diff --git a/agents/s02_tool_use.py b/agents/s02_tool_use.py
index a05ac4bf9..93abd39c9 100644
--- a/agents/s02_tool_use.py
+++ b/agents/s02_tool_use.py
@@ -49,20 +49,28 @@ def run_bash(command: str) -> str:
if any(d in command for d in dangerous):
return "Error: Dangerous command blocked"
try:
- r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ r = subprocess.run(
+ command,
+ shell=True,
+ cwd=WORKDIR,
+ capture_output=True,
+ text=True,
+ timeout=120,
+ )
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
return "Error: Timeout (120s)"
-def run_read(path: str, limit: int = None) -> str:
+def run_read(path: str, limit: int | None = None) -> str:
try:
text = safe_path(path).read_text()
lines = text.splitlines()
- if limit and limit < len(lines):
- lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
+ if limit is not None:
+ limit = max(0, int(limit))
+ if limit < len(lines):
+ lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)[:50000]
except Exception as e:
return f"Error: {e}"
@@ -92,29 +100,64 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
# -- The dispatch map: {tool_name: handler} --
TOOL_HANDLERS = {
- "bash": lambda **kw: run_bash(kw["command"]),
- "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
+ "bash": lambda **kw: run_bash(kw["command"]),
+ "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
"write_file": lambda **kw: run_write(kw["path"], kw["content"]),
- "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
+ "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
}
TOOLS = [
- {"name": "bash", "description": "Run a shell command.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file contents.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write content to file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Replace exact text in file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
+ {
+ "name": "bash",
+ "description": "Run a shell command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file contents.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write content to file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "content": {"type": "string"}},
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Replace exact text in file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
]
def agent_loop(messages: list):
while True:
response = client.messages.create(
- model=MODEL, system=SYSTEM, messages=messages,
- tools=TOOLS, max_tokens=8000,
+ model=MODEL,
+ system=SYSTEM,
+ messages=messages,
+ tools=TOOLS,
+ max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
@@ -123,9 +166,13 @@ def agent_loop(messages: list):
for block in response.content:
if block.type == "tool_use":
handler = TOOL_HANDLERS.get(block.name)
- output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
+ output = (
+ handler(**block.input) if handler else f"Unknown tool: {block.name}"
+ )
print(f"> {block.name}: {output[:200]}")
- results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})
+ results.append(
+ {"type": "tool_result", "tool_use_id": block.id, "content": output}
+ )
messages.append({"role": "user", "content": results})
diff --git a/agents/s03_todo_write.py b/agents/s03_todo_write.py
index 9ca805c05..9bbb6f083 100644
--- a/agents/s03_todo_write.py
+++ b/agents/s03_todo_write.py
@@ -78,7 +78,9 @@ def render(self) -> str:
return "No todos."
lines = []
for item in self.items:
- marker = {"pending": "[ ]", "in_progress": "[>]", "completed": "[x]"}[item["status"]]
+ marker = {"pending": "[ ]", "in_progress": "[>]", "completed": "[x]"}[
+ item["status"]
+ ]
lines.append(f"{marker} #{item['id']}: {item['text']}")
done = sum(1 for t in self.items if t["status"] == "completed")
lines.append(f"\n({done}/{len(self.items)} completed)")
@@ -95,27 +97,38 @@ def safe_path(p: str) -> Path:
raise ValueError(f"Path escapes workspace: {p}")
return path
+
def run_bash(command: str) -> str:
dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
if any(d in command for d in dangerous):
return "Error: Dangerous command blocked"
try:
- r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ r = subprocess.run(
+ command,
+ shell=True,
+ cwd=WORKDIR,
+ capture_output=True,
+ text=True,
+ timeout=120,
+ )
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
return "Error: Timeout (120s)"
-def run_read(path: str, limit: int = None) -> str:
+
+def run_read(path: str, limit: int | None = None) -> str:
try:
lines = safe_path(path).read_text().splitlines()
- if limit and limit < len(lines):
- lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
+ if limit is not None:
+ limit = max(0, int(limit))
+ if limit < len(lines):
+ lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
except Exception as e:
return f"Error: {e}"
+
def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
@@ -125,6 +138,7 @@ def run_write(path: str, content: str) -> str:
except Exception as e:
return f"Error: {e}"
+
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
@@ -138,24 +152,79 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
TOOL_HANDLERS = {
- "bash": lambda **kw: run_bash(kw["command"]),
- "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
+ "bash": lambda **kw: run_bash(kw["command"]),
+ "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
"write_file": lambda **kw: run_write(kw["path"], kw["content"]),
- "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
- "todo": lambda **kw: TODO.update(kw["items"]),
+ "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
+ "todo": lambda **kw: TODO.update(kw["items"]),
}
TOOLS = [
- {"name": "bash", "description": "Run a shell command.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file contents.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write content to file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Replace exact text in file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
- {"name": "todo", "description": "Update task list. Track progress on multi-step tasks.",
- "input_schema": {"type": "object", "properties": {"items": {"type": "array", "items": {"type": "object", "properties": {"id": {"type": "string"}, "text": {"type": "string"}, "status": {"type": "string", "enum": ["pending", "in_progress", "completed"]}}, "required": ["id", "text", "status"]}}}, "required": ["items"]}},
+ {
+ "name": "bash",
+ "description": "Run a shell command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file contents.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write content to file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "content": {"type": "string"}},
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Replace exact text in file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
+ {
+ "name": "todo",
+ "description": "Update task list. Track progress on multi-step tasks.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {"type": "string"},
+ "text": {"type": "string"},
+ "status": {
+ "type": "string",
+ "enum": ["pending", "in_progress", "completed"],
+ },
+ },
+ "required": ["id", "text", "status"],
+ },
+ }
+ },
+ "required": ["items"],
+ },
+ },
]
@@ -165,8 +234,11 @@ def agent_loop(messages: list):
while True:
# Nag reminder is injected below, alongside tool results
response = client.messages.create(
- model=MODEL, system=SYSTEM, messages=messages,
- tools=TOOLS, max_tokens=8000,
+ model=MODEL,
+ system=SYSTEM,
+ messages=messages,
+ tools=TOOLS,
+ max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
@@ -177,16 +249,28 @@ def agent_loop(messages: list):
if block.type == "tool_use":
handler = TOOL_HANDLERS.get(block.name)
try:
- output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
+ output = (
+ handler(**block.input)
+ if handler
+ else f"Unknown tool: {block.name}"
+ )
except Exception as e:
output = f"Error: {e}"
print(f"> {block.name}: {str(output)[:200]}")
- results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(output)})
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output),
+ }
+ )
if block.name == "todo":
used_todo = True
rounds_since_todo = 0 if used_todo else rounds_since_todo + 1
if rounds_since_todo >= 3:
- results.insert(0, {"type": "text", "text": "Update your todos."})
+ results.insert(
+ 0, {"type": "text", "text": "Update your todos."}
+ )
messages.append({"role": "user", "content": results})
diff --git a/agents/s04_subagent.py b/agents/s04_subagent.py
index 9de087b1f..e1a8d69f8 100644
--- a/agents/s04_subagent.py
+++ b/agents/s04_subagent.py
@@ -49,27 +49,38 @@ def safe_path(p: str) -> Path:
raise ValueError(f"Path escapes workspace: {p}")
return path
+
def run_bash(command: str) -> str:
dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
if any(d in command for d in dangerous):
return "Error: Dangerous command blocked"
try:
- r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ r = subprocess.run(
+ command,
+ shell=True,
+ cwd=WORKDIR,
+ capture_output=True,
+ text=True,
+ timeout=120,
+ )
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
return "Error: Timeout (120s)"
-def run_read(path: str, limit: int = None) -> str:
+
+def run_read(path: str, limit: int | None = None) -> str:
try:
lines = safe_path(path).read_text().splitlines()
- if limit and limit < len(lines):
- lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
+ if limit is not None:
+ limit = max(0, int(limit))
+ if limit < len(lines):
+ lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
except Exception as e:
return f"Error: {e}"
+
def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
@@ -79,6 +90,7 @@ def run_write(path: str, content: str) -> str:
except Exception as e:
return f"Error: {e}"
+
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
@@ -92,22 +104,54 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
TOOL_HANDLERS = {
- "bash": lambda **kw: run_bash(kw["command"]),
- "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
+ "bash": lambda **kw: run_bash(kw["command"]),
+ "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
"write_file": lambda **kw: run_write(kw["path"], kw["content"]),
- "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
+ "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
}
# Child gets all base tools except task (no recursive spawning)
CHILD_TOOLS = [
- {"name": "bash", "description": "Run a shell command.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file contents.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write content to file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Replace exact text in file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
+ {
+ "name": "bash",
+ "description": "Run a shell command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file contents.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write content to file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "content": {"type": "string"}},
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Replace exact text in file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
]
@@ -116,8 +160,11 @@ def run_subagent(prompt: str) -> str:
sub_messages = [{"role": "user", "content": prompt}] # fresh context
for _ in range(30): # safety limit
response = client.messages.create(
- model=MODEL, system=SUBAGENT_SYSTEM, messages=sub_messages,
- tools=CHILD_TOOLS, max_tokens=8000,
+ model=MODEL,
+ system=SUBAGENT_SYSTEM,
+ messages=sub_messages,
+ tools=CHILD_TOOLS,
+ max_tokens=8000,
)
sub_messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
@@ -126,25 +173,52 @@ def run_subagent(prompt: str) -> str:
for block in response.content:
if block.type == "tool_use":
handler = TOOL_HANDLERS.get(block.name)
- output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
- results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(output)[:50000]})
+ output = (
+ handler(**block.input) if handler else f"Unknown tool: {block.name}"
+ )
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output)[:50000],
+ }
+ )
sub_messages.append({"role": "user", "content": results})
# Only the final text returns to the parent -- child context is discarded
- return "".join(b.text for b in response.content if hasattr(b, "text")) or "(no summary)"
+ return (
+ "".join(b.text for b in response.content if hasattr(b, "text"))
+ or "(no summary)"
+ )
# -- Parent tools: base tools + task dispatcher --
PARENT_TOOLS = CHILD_TOOLS + [
- {"name": "task", "description": "Spawn a subagent with fresh context. It shares the filesystem but not conversation history.",
- "input_schema": {"type": "object", "properties": {"prompt": {"type": "string"}, "description": {"type": "string", "description": "Short description of the task"}}, "required": ["prompt"]}},
+ {
+ "name": "task",
+ "description": "Spawn a subagent with fresh context. It shares the filesystem but not conversation history.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "prompt": {"type": "string"},
+ "description": {
+ "type": "string",
+ "description": "Short description of the task",
+ },
+ },
+ "required": ["prompt"],
+ },
+ },
]
def agent_loop(messages: list):
while True:
response = client.messages.create(
- model=MODEL, system=SYSTEM, messages=messages,
- tools=PARENT_TOOLS, max_tokens=8000,
+ model=MODEL,
+ system=SYSTEM,
+ messages=messages,
+ tools=PARENT_TOOLS,
+ max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
@@ -158,9 +232,19 @@ def agent_loop(messages: list):
output = run_subagent(block.input["prompt"])
else:
handler = TOOL_HANDLERS.get(block.name)
- output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
+ output = (
+ handler(**block.input)
+ if handler
+ else f"Unknown tool: {block.name}"
+ )
print(f" {str(output)[:200]}")
- results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(output)})
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output),
+ }
+ )
messages.append({"role": "user", "content": results})
diff --git a/agents/s05_skill_loading.py b/agents/s05_skill_loading.py
index ee8ffc157..c21e5f321 100644
--- a/agents/s05_skill_loading.py
+++ b/agents/s05_skill_loading.py
@@ -100,7 +100,7 @@ def get_content(self, name: str) -> str:
skill = self.skills.get(name)
if not skill:
return f"Error: Unknown skill '{name}'. Available: {', '.join(self.skills.keys())}"
- return f"\n{skill['body']}\n"
+ return f'\n{skill["body"]}\n'
SKILL_LOADER = SkillLoader(SKILLS_DIR)
@@ -120,27 +120,38 @@ def safe_path(p: str) -> Path:
raise ValueError(f"Path escapes workspace: {p}")
return path
+
def run_bash(command: str) -> str:
dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
if any(d in command for d in dangerous):
return "Error: Dangerous command blocked"
try:
- r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ r = subprocess.run(
+ command,
+ shell=True,
+ cwd=WORKDIR,
+ capture_output=True,
+ text=True,
+ timeout=120,
+ )
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
return "Error: Timeout (120s)"
-def run_read(path: str, limit: int = None) -> str:
+
+def run_read(path: str, limit: int | None = None) -> str:
try:
lines = safe_path(path).read_text().splitlines()
- if limit and limit < len(lines):
- lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
+ if limit is not None:
+ limit = max(0, int(limit))
+ if limit < len(lines):
+ lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
except Exception as e:
return f"Error: {e}"
+
def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
@@ -150,6 +161,7 @@ def run_write(path: str, content: str) -> str:
except Exception as e:
return f"Error: {e}"
+
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
@@ -163,32 +175,76 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
TOOL_HANDLERS = {
- "bash": lambda **kw: run_bash(kw["command"]),
- "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
+ "bash": lambda **kw: run_bash(kw["command"]),
+ "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
"write_file": lambda **kw: run_write(kw["path"], kw["content"]),
- "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
+ "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
"load_skill": lambda **kw: SKILL_LOADER.get_content(kw["name"]),
}
TOOLS = [
- {"name": "bash", "description": "Run a shell command.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file contents.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write content to file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Replace exact text in file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
- {"name": "load_skill", "description": "Load specialized knowledge by name.",
- "input_schema": {"type": "object", "properties": {"name": {"type": "string", "description": "Skill name to load"}}, "required": ["name"]}},
+ {
+ "name": "bash",
+ "description": "Run a shell command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file contents.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write content to file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "content": {"type": "string"}},
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Replace exact text in file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
+ {
+ "name": "load_skill",
+ "description": "Load specialized knowledge by name.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string", "description": "Skill name to load"}
+ },
+ "required": ["name"],
+ },
+ },
]
def agent_loop(messages: list):
while True:
response = client.messages.create(
- model=MODEL, system=SYSTEM, messages=messages,
- tools=TOOLS, max_tokens=8000,
+ model=MODEL,
+ system=SYSTEM,
+ messages=messages,
+ tools=TOOLS,
+ max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
@@ -198,11 +254,21 @@ def agent_loop(messages: list):
if block.type == "tool_use":
handler = TOOL_HANDLERS.get(block.name)
try:
- output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
+ output = (
+ handler(**block.input)
+ if handler
+ else f"Unknown tool: {block.name}"
+ )
except Exception as e:
output = f"Error: {e}"
print(f"> {block.name}: {str(output)[:200]}")
- results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(output)})
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output),
+ }
+ )
messages.append({"role": "user", "content": results})
diff --git a/agents/s06_context_compact.py b/agents/s06_context_compact.py
index b9c6aa8d2..0ac5631b9 100644
--- a/agents/s06_context_compact.py
+++ b/agents/s06_context_compact.py
@@ -106,17 +106,27 @@ def auto_compact(messages: list) -> list:
conversation_text = json.dumps(messages, default=str)[:80000]
response = client.messages.create(
model=MODEL,
- messages=[{"role": "user", "content":
- "Summarize this conversation for continuity. Include: "
- "1) What was accomplished, 2) Current state, 3) Key decisions made. "
- "Be concise but preserve critical details.\n\n" + conversation_text}],
+ messages=[
+ {
+ "role": "user",
+ "content": "Summarize this conversation for continuity. Include: "
+ "1) What was accomplished, 2) Current state, 3) Key decisions made. "
+ "Be concise but preserve critical details.\n\n" + conversation_text,
+ }
+ ],
max_tokens=2000,
)
summary = response.content[0].text
# Replace all messages with compressed summary
return [
- {"role": "user", "content": f"[Conversation compressed. Transcript: {transcript_path}]\n\n{summary}"},
- {"role": "assistant", "content": "Understood. I have the context from the summary. Continuing."},
+ {
+ "role": "user",
+ "content": f"[Conversation compressed. Transcript: {transcript_path}]\n\n{summary}",
+ },
+ {
+ "role": "assistant",
+ "content": "Understood. I have the context from the summary. Continuing.",
+ },
]
@@ -127,27 +137,38 @@ def safe_path(p: str) -> Path:
raise ValueError(f"Path escapes workspace: {p}")
return path
+
def run_bash(command: str) -> str:
dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
if any(d in command for d in dangerous):
return "Error: Dangerous command blocked"
try:
- r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ r = subprocess.run(
+ command,
+ shell=True,
+ cwd=WORKDIR,
+ capture_output=True,
+ text=True,
+ timeout=120,
+ )
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
return "Error: Timeout (120s)"
-def run_read(path: str, limit: int = None) -> str:
+
+def run_read(path: str, limit: int | None = None) -> str:
try:
lines = safe_path(path).read_text().splitlines()
- if limit and limit < len(lines):
- lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
+ if limit is not None:
+ limit = max(0, int(limit))
+ if limit < len(lines):
+ lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
except Exception as e:
return f"Error: {e}"
+
def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
@@ -157,6 +178,7 @@ def run_write(path: str, content: str) -> str:
except Exception as e:
return f"Error: {e}"
+
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
@@ -170,24 +192,67 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
TOOL_HANDLERS = {
- "bash": lambda **kw: run_bash(kw["command"]),
- "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
+ "bash": lambda **kw: run_bash(kw["command"]),
+ "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
"write_file": lambda **kw: run_write(kw["path"], kw["content"]),
- "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
- "compact": lambda **kw: "Manual compression requested.",
+ "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
+ "compact": lambda **kw: "Manual compression requested.",
}
TOOLS = [
- {"name": "bash", "description": "Run a shell command.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file contents.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write content to file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Replace exact text in file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
- {"name": "compact", "description": "Trigger manual conversation compression.",
- "input_schema": {"type": "object", "properties": {"focus": {"type": "string", "description": "What to preserve in the summary"}}}},
+ {
+ "name": "bash",
+ "description": "Run a shell command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file contents.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write content to file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "content": {"type": "string"}},
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Replace exact text in file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
+ {
+ "name": "compact",
+ "description": "Trigger manual conversation compression.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "focus": {
+ "type": "string",
+ "description": "What to preserve in the summary",
+ }
+ },
+ },
+ },
]
@@ -200,8 +265,11 @@ def agent_loop(messages: list):
print("[auto_compact triggered]")
messages[:] = auto_compact(messages)
response = client.messages.create(
- model=MODEL, system=SYSTEM, messages=messages,
- tools=TOOLS, max_tokens=8000,
+ model=MODEL,
+ system=SYSTEM,
+ messages=messages,
+ tools=TOOLS,
+ max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
@@ -216,11 +284,21 @@ def agent_loop(messages: list):
else:
handler = TOOL_HANDLERS.get(block.name)
try:
- output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
+ output = (
+ handler(**block.input)
+ if handler
+ else f"Unknown tool: {block.name}"
+ )
except Exception as e:
output = f"Error: {e}"
print(f"> {block.name}: {str(output)[:200]}")
- results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(output)})
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output),
+ }
+ )
messages.append({"role": "user", "content": results})
# Layer 3: manual compact triggered by the compact tool
if manual_compact:
diff --git a/agents/s07_task_system.py b/agents/s07_task_system.py
index 82b16af62..5c459dcdb 100644
--- a/agents/s07_task_system.py
+++ b/agents/s07_task_system.py
@@ -65,8 +65,13 @@ def _save(self, task: dict):
def create(self, subject: str, description: str = "") -> str:
task = {
- "id": self._next_id, "subject": subject, "description": description,
- "status": "pending", "blockedBy": [], "blocks": [], "owner": "",
+ "id": self._next_id,
+ "subject": subject,
+ "description": description,
+ "status": "pending",
+ "blockedBy": [],
+ "blocks": [],
+ "owner": "",
}
self._save(task)
self._next_id += 1
@@ -75,8 +80,13 @@ def create(self, subject: str, description: str = "") -> str:
def get(self, task_id: int) -> str:
return json.dumps(self._load(task_id), indent=2)
- def update(self, task_id: int, status: str = None,
- add_blocked_by: list = None, add_blocks: list = None) -> str:
+ def update(
+ self,
+ task_id: int,
+ status: str = None,
+ add_blocked_by: list = None,
+ add_blocks: list = None,
+ ) -> str:
task = self._load(task_id)
if status:
if status not in ("pending", "in_progress", "completed"):
@@ -117,7 +127,9 @@ def list_all(self) -> str:
return "No tasks."
lines = []
for t in tasks:
- marker = {"pending": "[ ]", "in_progress": "[>]", "completed": "[x]"}.get(t["status"], "[?]")
+ marker = {"pending": "[ ]", "in_progress": "[>]", "completed": "[x]"}.get(
+ t["status"], "[?]"
+ )
blocked = f" (blocked by: {t['blockedBy']})" if t.get("blockedBy") else ""
lines.append(f"{marker} #{t['id']}: {t['subject']}{blocked}")
return "\n".join(lines)
@@ -133,27 +145,38 @@ def safe_path(p: str) -> Path:
raise ValueError(f"Path escapes workspace: {p}")
return path
+
def run_bash(command: str) -> str:
dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
if any(d in command for d in dangerous):
return "Error: Dangerous command blocked"
try:
- r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ r = subprocess.run(
+ command,
+ shell=True,
+ cwd=WORKDIR,
+ capture_output=True,
+ text=True,
+ timeout=120,
+ )
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
return "Error: Timeout (120s)"
-def run_read(path: str, limit: int = None) -> str:
+
+def run_read(path: str, limit: int | None = None) -> str:
try:
lines = safe_path(path).read_text().splitlines()
- if limit and limit < len(lines):
- lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
+ if limit is not None:
+ limit = max(0, int(limit))
+ if limit < len(lines):
+ lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
except Exception as e:
return f"Error: {e}"
+
def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
@@ -163,6 +186,7 @@ def run_write(path: str, content: str) -> str:
except Exception as e:
return f"Error: {e}"
+
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
@@ -176,41 +200,113 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
TOOL_HANDLERS = {
- "bash": lambda **kw: run_bash(kw["command"]),
- "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
- "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
- "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
+ "bash": lambda **kw: run_bash(kw["command"]),
+ "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
+ "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
+ "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
"task_create": lambda **kw: TASKS.create(kw["subject"], kw.get("description", "")),
- "task_update": lambda **kw: TASKS.update(kw["task_id"], kw.get("status"), kw.get("addBlockedBy"), kw.get("addBlocks")),
- "task_list": lambda **kw: TASKS.list_all(),
- "task_get": lambda **kw: TASKS.get(kw["task_id"]),
+ "task_update": lambda **kw: TASKS.update(
+ kw["task_id"], kw.get("status"), kw.get("addBlockedBy"), kw.get("addBlocks")
+ ),
+ "task_list": lambda **kw: TASKS.list_all(),
+ "task_get": lambda **kw: TASKS.get(kw["task_id"]),
}
TOOLS = [
- {"name": "bash", "description": "Run a shell command.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file contents.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write content to file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Replace exact text in file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
- {"name": "task_create", "description": "Create a new task.",
- "input_schema": {"type": "object", "properties": {"subject": {"type": "string"}, "description": {"type": "string"}}, "required": ["subject"]}},
- {"name": "task_update", "description": "Update a task's status or dependencies.",
- "input_schema": {"type": "object", "properties": {"task_id": {"type": "integer"}, "status": {"type": "string", "enum": ["pending", "in_progress", "completed"]}, "addBlockedBy": {"type": "array", "items": {"type": "integer"}}, "addBlocks": {"type": "array", "items": {"type": "integer"}}}, "required": ["task_id"]}},
- {"name": "task_list", "description": "List all tasks with status summary.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "task_get", "description": "Get full details of a task by ID.",
- "input_schema": {"type": "object", "properties": {"task_id": {"type": "integer"}}, "required": ["task_id"]}},
+ {
+ "name": "bash",
+ "description": "Run a shell command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file contents.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write content to file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "content": {"type": "string"}},
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Replace exact text in file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
+ {
+ "name": "task_create",
+ "description": "Create a new task.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "subject": {"type": "string"},
+ "description": {"type": "string"},
+ },
+ "required": ["subject"],
+ },
+ },
+ {
+ "name": "task_update",
+ "description": "Update a task's status or dependencies.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "task_id": {"type": "integer"},
+ "status": {
+ "type": "string",
+ "enum": ["pending", "in_progress", "completed"],
+ },
+ "addBlockedBy": {"type": "array", "items": {"type": "integer"}},
+ "addBlocks": {"type": "array", "items": {"type": "integer"}},
+ },
+ "required": ["task_id"],
+ },
+ },
+ {
+ "name": "task_list",
+ "description": "List all tasks with status summary.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "task_get",
+ "description": "Get full details of a task by ID.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"task_id": {"type": "integer"}},
+ "required": ["task_id"],
+ },
+ },
]
def agent_loop(messages: list):
while True:
response = client.messages.create(
- model=MODEL, system=SYSTEM, messages=messages,
- tools=TOOLS, max_tokens=8000,
+ model=MODEL,
+ system=SYSTEM,
+ messages=messages,
+ tools=TOOLS,
+ max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
@@ -220,11 +316,21 @@ def agent_loop(messages: list):
if block.type == "tool_use":
handler = TOOL_HANDLERS.get(block.name)
try:
- output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
+ output = (
+ handler(**block.input)
+ if handler
+ else f"Unknown tool: {block.name}"
+ )
except Exception as e:
output = f"Error: {e}"
print(f"> {block.name}: {str(output)[:200]}")
- results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(output)})
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output),
+ }
+ )
messages.append({"role": "user", "content": results})
diff --git a/agents/s08_background_tasks.py b/agents/s08_background_tasks.py
index 77a992eaf..b80db0230 100644
--- a/agents/s08_background_tasks.py
+++ b/agents/s08_background_tasks.py
@@ -66,8 +66,12 @@ def _execute(self, task_id: str, command: str):
"""Thread target: run subprocess, capture output, push to queue."""
try:
r = subprocess.run(
- command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=300
+ command,
+ shell=True,
+ cwd=WORKDIR,
+ capture_output=True,
+ text=True,
+ timeout=300,
)
output = (r.stdout + r.stderr).strip()[:50000]
status = "completed"
@@ -80,12 +84,14 @@ def _execute(self, task_id: str, command: str):
self.tasks[task_id]["status"] = status
self.tasks[task_id]["result"] = output or "(no output)"
with self._lock:
- self._notification_queue.append({
- "task_id": task_id,
- "status": status,
- "command": command[:80],
- "result": (output or "(no output)")[:500],
- })
+ self._notification_queue.append(
+ {
+ "task_id": task_id,
+ "status": status,
+ "command": command[:80],
+ "result": (output or "(no output)")[:500],
+ }
+ )
def check(self, task_id: str = None) -> str:
"""Check status of one task or list all."""
@@ -93,7 +99,9 @@ def check(self, task_id: str = None) -> str:
t = self.tasks.get(task_id)
if not t:
return f"Error: Unknown task {task_id}"
- return f"[{t['status']}] {t['command'][:60]}\n{t.get('result') or '(running)'}"
+ return (
+ f"[{t['status']}] {t['command'][:60]}\n{t.get('result') or '(running)'}"
+ )
lines = []
for tid, t in self.tasks.items():
lines.append(f"{tid}: [{t['status']}] {t['command'][:60]}")
@@ -117,27 +125,38 @@ def safe_path(p: str) -> Path:
raise ValueError(f"Path escapes workspace: {p}")
return path
+
def run_bash(command: str) -> str:
dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
if any(d in command for d in dangerous):
return "Error: Dangerous command blocked"
try:
- r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ r = subprocess.run(
+ command,
+ shell=True,
+ cwd=WORKDIR,
+ capture_output=True,
+ text=True,
+ timeout=120,
+ )
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
return "Error: Timeout (120s)"
-def run_read(path: str, limit: int = None) -> str:
+
+def run_read(path: str, limit: int | None = None) -> str:
try:
lines = safe_path(path).read_text().splitlines()
- if limit and limit < len(lines):
- lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
+ if limit is not None:
+ limit = max(0, int(limit))
+ if limit < len(lines):
+ lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
except Exception as e:
return f"Error: {e}"
+
def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
@@ -147,6 +166,7 @@ def run_write(path: str, content: str) -> str:
except Exception as e:
return f"Error: {e}"
+
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
@@ -160,27 +180,72 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
TOOL_HANDLERS = {
- "bash": lambda **kw: run_bash(kw["command"]),
- "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
- "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
- "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
- "background_run": lambda **kw: BG.run(kw["command"]),
+ "bash": lambda **kw: run_bash(kw["command"]),
+ "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
+ "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
+ "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
+ "background_run": lambda **kw: BG.run(kw["command"]),
"check_background": lambda **kw: BG.check(kw.get("task_id")),
}
TOOLS = [
- {"name": "bash", "description": "Run a shell command (blocking).",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file contents.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write content to file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Replace exact text in file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
- {"name": "background_run", "description": "Run command in background thread. Returns task_id immediately.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "check_background", "description": "Check background task status. Omit task_id to list all.",
- "input_schema": {"type": "object", "properties": {"task_id": {"type": "string"}}}},
+ {
+ "name": "bash",
+ "description": "Run a shell command (blocking).",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file contents.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write content to file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "content": {"type": "string"}},
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Replace exact text in file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
+ {
+ "name": "background_run",
+ "description": "Run command in background thread. Returns task_id immediately.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "check_background",
+ "description": "Check background task status. Omit task_id to list all.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"task_id": {"type": "string"}},
+ },
+ },
]
@@ -192,11 +257,21 @@ def agent_loop(messages: list):
notif_text = "\n".join(
f"[bg:{n['task_id']}] {n['status']}: {n['result']}" for n in notifs
)
- messages.append({"role": "user", "content": f"\n{notif_text}\n"})
- messages.append({"role": "assistant", "content": "Noted background results."})
+ messages.append(
+ {
+ "role": "user",
+ "content": f"\n{notif_text}\n",
+ }
+ )
+ messages.append(
+ {"role": "assistant", "content": "Noted background results."}
+ )
response = client.messages.create(
- model=MODEL, system=SYSTEM, messages=messages,
- tools=TOOLS, max_tokens=8000,
+ model=MODEL,
+ system=SYSTEM,
+ messages=messages,
+ tools=TOOLS,
+ max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
@@ -206,11 +281,21 @@ def agent_loop(messages: list):
if block.type == "tool_use":
handler = TOOL_HANDLERS.get(block.name)
try:
- output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
+ output = (
+ handler(**block.input)
+ if handler
+ else f"Unknown tool: {block.name}"
+ )
except Exception as e:
output = f"Error: {e}"
print(f"> {block.name}: {str(output)[:200]}")
- results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(output)})
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output),
+ }
+ )
messages.append({"role": "user", "content": results})
diff --git a/agents/s09_agent_teams.py b/agents/s09_agent_teams.py
index 284a1ac19..b6df2b4ed 100644
--- a/agents/s09_agent_teams.py
+++ b/agents/s09_agent_teams.py
@@ -62,7 +62,9 @@
TEAM_DIR = WORKDIR / ".team"
INBOX_DIR = TEAM_DIR / "inbox"
-SYSTEM = f"You are a team lead at {WORKDIR}. Spawn teammates and communicate via inboxes."
+SYSTEM = (
+ f"You are a team lead at {WORKDIR}. Spawn teammates and communicate via inboxes."
+)
VALID_MSG_TYPES = {
"message",
@@ -79,8 +81,14 @@ def __init__(self, inbox_dir: Path):
self.dir = inbox_dir
self.dir.mkdir(parents=True, exist_ok=True)
- def send(self, sender: str, to: str, content: str,
- msg_type: str = "message", extra: dict = None) -> str:
+ def send(
+ self,
+ sender: str,
+ to: str,
+ content: str,
+ msg_type: str = "message",
+ extra: dict = None,
+ ) -> str:
if msg_type not in VALID_MSG_TYPES:
return f"Error: Invalid type '{msg_type}'. Valid: {VALID_MSG_TYPES}"
msg = {
@@ -191,11 +199,13 @@ def _teammate_loop(self, name: str, role: str, prompt: str):
if block.type == "tool_use":
output = self._exec(name, block.name, block.input)
print(f" [{name}] {block.name}: {str(output)[:120]}")
- results.append({
- "type": "tool_result",
- "tool_use_id": block.id,
- "content": str(output),
- })
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output),
+ }
+ )
messages.append({"role": "user", "content": results})
member = self._find_member(name)
if member and member["status"] != "shutdown":
@@ -213,7 +223,9 @@ def _exec(self, sender: str, tool_name: str, args: dict) -> str:
if tool_name == "edit_file":
return _run_edit(args["path"], args["old_text"], args["new_text"])
if tool_name == "send_message":
- return BUS.send(sender, args["to"], args["content"], args.get("msg_type", "message"))
+ return BUS.send(
+ sender, args["to"], args["content"], args.get("msg_type", "message")
+ )
if tool_name == "read_inbox":
return json.dumps(BUS.read_inbox(sender), indent=2)
return f"Unknown tool: {tool_name}"
@@ -221,18 +233,67 @@ def _exec(self, sender: str, tool_name: str, args: dict) -> str:
def _teammate_tools(self) -> list:
# these base tools are unchanged from s02
return [
- {"name": "bash", "description": "Run a shell command.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file contents.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write content to file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Replace exact text in file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
- {"name": "send_message", "description": "Send message to a teammate.",
- "input_schema": {"type": "object", "properties": {"to": {"type": "string"}, "content": {"type": "string"}, "msg_type": {"type": "string", "enum": list(VALID_MSG_TYPES)}}, "required": ["to", "content"]}},
- {"name": "read_inbox", "description": "Read and drain your inbox.",
- "input_schema": {"type": "object", "properties": {}}},
+ {
+ "name": "bash",
+ "description": "Run a shell command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file contents.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write content to file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "content": {"type": "string"},
+ },
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Replace exact text in file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
+ {
+ "name": "send_message",
+ "description": "Send message to a teammate.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "to": {"type": "string"},
+ "content": {"type": "string"},
+ "msg_type": {"type": "string", "enum": list(VALID_MSG_TYPES)},
+ },
+ "required": ["to", "content"],
+ },
+ },
+ {
+ "name": "read_inbox",
+ "description": "Read and drain your inbox.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
]
def list_all(self) -> str:
@@ -264,8 +325,12 @@ def _run_bash(command: str) -> str:
return "Error: Dangerous command blocked"
try:
r = subprocess.run(
- command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120,
+ command,
+ shell=True,
+ cwd=WORKDIR,
+ capture_output=True,
+ text=True,
+ timeout=120,
)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
@@ -273,11 +338,13 @@ def _run_bash(command: str) -> str:
return "Error: Timeout (120s)"
-def _run_read(path: str, limit: int = None) -> str:
+def _run_read(path: str, limit: int | None = None) -> str:
try:
lines = _safe_path(path).read_text().splitlines()
- if limit and limit < len(lines):
- lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
+ if limit is not None:
+ limit = max(0, int(limit))
+ if limit < len(lines):
+ lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
except Exception as e:
return f"Error: {e}"
@@ -307,37 +374,106 @@ def _run_edit(path: str, old_text: str, new_text: str) -> str:
# -- Lead tool dispatch (9 tools) --
TOOL_HANDLERS = {
- "bash": lambda **kw: _run_bash(kw["command"]),
- "read_file": lambda **kw: _run_read(kw["path"], kw.get("limit")),
- "write_file": lambda **kw: _run_write(kw["path"], kw["content"]),
- "edit_file": lambda **kw: _run_edit(kw["path"], kw["old_text"], kw["new_text"]),
- "spawn_teammate": lambda **kw: TEAM.spawn(kw["name"], kw["role"], kw["prompt"]),
- "list_teammates": lambda **kw: TEAM.list_all(),
- "send_message": lambda **kw: BUS.send("lead", kw["to"], kw["content"], kw.get("msg_type", "message")),
- "read_inbox": lambda **kw: json.dumps(BUS.read_inbox("lead"), indent=2),
- "broadcast": lambda **kw: BUS.broadcast("lead", kw["content"], TEAM.member_names()),
+ "bash": lambda **kw: _run_bash(kw["command"]),
+ "read_file": lambda **kw: _run_read(kw["path"], kw.get("limit")),
+ "write_file": lambda **kw: _run_write(kw["path"], kw["content"]),
+ "edit_file": lambda **kw: _run_edit(kw["path"], kw["old_text"], kw["new_text"]),
+ "spawn_teammate": lambda **kw: TEAM.spawn(kw["name"], kw["role"], kw["prompt"]),
+ "list_teammates": lambda **kw: TEAM.list_all(),
+ "send_message": lambda **kw: BUS.send(
+ "lead", kw["to"], kw["content"], kw.get("msg_type", "message")
+ ),
+ "read_inbox": lambda **kw: json.dumps(BUS.read_inbox("lead"), indent=2),
+ "broadcast": lambda **kw: BUS.broadcast("lead", kw["content"], TEAM.member_names()),
}
# these base tools are unchanged from s02
TOOLS = [
- {"name": "bash", "description": "Run a shell command.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file contents.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write content to file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Replace exact text in file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
- {"name": "spawn_teammate", "description": "Spawn a persistent teammate that runs in its own thread.",
- "input_schema": {"type": "object", "properties": {"name": {"type": "string"}, "role": {"type": "string"}, "prompt": {"type": "string"}}, "required": ["name", "role", "prompt"]}},
- {"name": "list_teammates", "description": "List all teammates with name, role, status.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "send_message", "description": "Send a message to a teammate's inbox.",
- "input_schema": {"type": "object", "properties": {"to": {"type": "string"}, "content": {"type": "string"}, "msg_type": {"type": "string", "enum": list(VALID_MSG_TYPES)}}, "required": ["to", "content"]}},
- {"name": "read_inbox", "description": "Read and drain the lead's inbox.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "broadcast", "description": "Send a message to all teammates.",
- "input_schema": {"type": "object", "properties": {"content": {"type": "string"}}, "required": ["content"]}},
+ {
+ "name": "bash",
+ "description": "Run a shell command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file contents.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write content to file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "content": {"type": "string"}},
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Replace exact text in file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
+ {
+ "name": "spawn_teammate",
+ "description": "Spawn a persistent teammate that runs in its own thread.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "role": {"type": "string"},
+ "prompt": {"type": "string"},
+ },
+ "required": ["name", "role", "prompt"],
+ },
+ },
+ {
+ "name": "list_teammates",
+ "description": "List all teammates with name, role, status.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "send_message",
+ "description": "Send a message to a teammate's inbox.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "to": {"type": "string"},
+ "content": {"type": "string"},
+ "msg_type": {"type": "string", "enum": list(VALID_MSG_TYPES)},
+ },
+ "required": ["to", "content"],
+ },
+ },
+ {
+ "name": "read_inbox",
+ "description": "Read and drain the lead's inbox.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "broadcast",
+ "description": "Send a message to all teammates.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"content": {"type": "string"}},
+ "required": ["content"],
+ },
+ },
]
@@ -345,14 +481,18 @@ def agent_loop(messages: list):
while True:
inbox = BUS.read_inbox("lead")
if inbox:
- messages.append({
- "role": "user",
- "content": f"{json.dumps(inbox, indent=2)}",
- })
- messages.append({
- "role": "assistant",
- "content": "Noted inbox messages.",
- })
+ messages.append(
+ {
+ "role": "user",
+ "content": f"{json.dumps(inbox, indent=2)}",
+ }
+ )
+ messages.append(
+ {
+ "role": "assistant",
+ "content": "Noted inbox messages.",
+ }
+ )
response = client.messages.create(
model=MODEL,
system=SYSTEM,
@@ -368,15 +508,21 @@ def agent_loop(messages: list):
if block.type == "tool_use":
handler = TOOL_HANDLERS.get(block.name)
try:
- output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
+ output = (
+ handler(**block.input)
+ if handler
+ else f"Unknown tool: {block.name}"
+ )
except Exception as e:
output = f"Error: {e}"
print(f"> {block.name}: {str(output)[:200]}")
- results.append({
- "type": "tool_result",
- "tool_use_id": block.id,
- "content": str(output),
- })
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output),
+ }
+ )
messages.append({"role": "user", "content": results})
diff --git a/agents/s10_team_protocols.py b/agents/s10_team_protocols.py
index 21f936df3..b4b696102 100644
--- a/agents/s10_team_protocols.py
+++ b/agents/s10_team_protocols.py
@@ -89,8 +89,14 @@ def __init__(self, inbox_dir: Path):
self.dir = inbox_dir
self.dir.mkdir(parents=True, exist_ok=True)
- def send(self, sender: str, to: str, content: str,
- msg_type: str = "message", extra: dict = None) -> str:
+ def send(
+ self,
+ sender: str,
+ to: str,
+ content: str,
+ msg_type: str = "message",
+ extra: dict = None,
+ ) -> str:
if msg_type not in VALID_MSG_TYPES:
return f"Error: Invalid type '{msg_type}'. Valid: {VALID_MSG_TYPES}"
msg = {
@@ -205,11 +211,13 @@ def _teammate_loop(self, name: str, role: str, prompt: str):
if block.type == "tool_use":
output = self._exec(name, block.name, block.input)
print(f" [{name}] {block.name}: {str(output)[:120]}")
- results.append({
- "type": "tool_result",
- "tool_use_id": block.id,
- "content": str(output),
- })
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output),
+ }
+ )
if block.name == "shutdown_response" and block.input.get("approve"):
should_exit = True
messages.append({"role": "user", "content": results})
@@ -229,7 +237,9 @@ def _exec(self, sender: str, tool_name: str, args: dict) -> str:
if tool_name == "edit_file":
return _run_edit(args["path"], args["old_text"], args["new_text"])
if tool_name == "send_message":
- return BUS.send(sender, args["to"], args["content"], args.get("msg_type", "message"))
+ return BUS.send(
+ sender, args["to"], args["content"], args.get("msg_type", "message")
+ )
if tool_name == "read_inbox":
return json.dumps(BUS.read_inbox(sender), indent=2)
if tool_name == "shutdown_response":
@@ -237,19 +247,31 @@ def _exec(self, sender: str, tool_name: str, args: dict) -> str:
approve = args["approve"]
with _tracker_lock:
if req_id in shutdown_requests:
- shutdown_requests[req_id]["status"] = "approved" if approve else "rejected"
+ shutdown_requests[req_id]["status"] = (
+ "approved" if approve else "rejected"
+ )
BUS.send(
- sender, "lead", args.get("reason", ""),
- "shutdown_response", {"request_id": req_id, "approve": approve},
+ sender,
+ "lead",
+ args.get("reason", ""),
+ "shutdown_response",
+ {"request_id": req_id, "approve": approve},
)
return f"Shutdown {'approved' if approve else 'rejected'}"
if tool_name == "plan_approval":
plan_text = args.get("plan", "")
req_id = str(uuid.uuid4())[:8]
with _tracker_lock:
- plan_requests[req_id] = {"from": sender, "plan": plan_text, "status": "pending"}
+ plan_requests[req_id] = {
+ "from": sender,
+ "plan": plan_text,
+ "status": "pending",
+ }
BUS.send(
- sender, "lead", plan_text, "plan_approval_response",
+ sender,
+ "lead",
+ plan_text,
+ "plan_approval_response",
{"request_id": req_id, "plan": plan_text},
)
return f"Plan submitted (request_id={req_id}). Waiting for lead approval."
@@ -258,22 +280,89 @@ def _exec(self, sender: str, tool_name: str, args: dict) -> str:
def _teammate_tools(self) -> list:
# these base tools are unchanged from s02
return [
- {"name": "bash", "description": "Run a shell command.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file contents.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write content to file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Replace exact text in file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
- {"name": "send_message", "description": "Send message to a teammate.",
- "input_schema": {"type": "object", "properties": {"to": {"type": "string"}, "content": {"type": "string"}, "msg_type": {"type": "string", "enum": list(VALID_MSG_TYPES)}}, "required": ["to", "content"]}},
- {"name": "read_inbox", "description": "Read and drain your inbox.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "shutdown_response", "description": "Respond to a shutdown request. Approve to shut down, reject to keep working.",
- "input_schema": {"type": "object", "properties": {"request_id": {"type": "string"}, "approve": {"type": "boolean"}, "reason": {"type": "string"}}, "required": ["request_id", "approve"]}},
- {"name": "plan_approval", "description": "Submit a plan for lead approval. Provide plan text.",
- "input_schema": {"type": "object", "properties": {"plan": {"type": "string"}}, "required": ["plan"]}},
+ {
+ "name": "bash",
+ "description": "Run a shell command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file contents.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write content to file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "content": {"type": "string"},
+ },
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Replace exact text in file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
+ {
+ "name": "send_message",
+ "description": "Send message to a teammate.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "to": {"type": "string"},
+ "content": {"type": "string"},
+ "msg_type": {"type": "string", "enum": list(VALID_MSG_TYPES)},
+ },
+ "required": ["to", "content"],
+ },
+ },
+ {
+ "name": "read_inbox",
+ "description": "Read and drain your inbox.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "shutdown_response",
+ "description": "Respond to a shutdown request. Approve to shut down, reject to keep working.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "request_id": {"type": "string"},
+ "approve": {"type": "boolean"},
+ "reason": {"type": "string"},
+ },
+ "required": ["request_id", "approve"],
+ },
+ },
+ {
+ "name": "plan_approval",
+ "description": "Submit a plan for lead approval. Provide plan text.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"plan": {"type": "string"}},
+ "required": ["plan"],
+ },
+ },
]
def list_all(self) -> str:
@@ -305,8 +394,12 @@ def _run_bash(command: str) -> str:
return "Error: Dangerous command blocked"
try:
r = subprocess.run(
- command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120,
+ command,
+ shell=True,
+ cwd=WORKDIR,
+ capture_output=True,
+ text=True,
+ timeout=120,
)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
@@ -314,11 +407,13 @@ def _run_bash(command: str) -> str:
return "Error: Timeout (120s)"
-def _run_read(path: str, limit: int = None) -> str:
+def _run_read(path: str, limit: int | None = None) -> str:
try:
lines = _safe_path(path).read_text().splitlines()
- if limit and limit < len(lines):
- lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
+ if limit is not None:
+ limit = max(0, int(limit))
+ if limit < len(lines):
+ lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
except Exception as e:
return f"Error: {e}"
@@ -352,8 +447,11 @@ def handle_shutdown_request(teammate: str) -> str:
with _tracker_lock:
shutdown_requests[req_id] = {"target": teammate, "status": "pending"}
BUS.send(
- "lead", teammate, "Please shut down gracefully.",
- "shutdown_request", {"request_id": req_id},
+ "lead",
+ teammate,
+ "Please shut down gracefully.",
+ "shutdown_request",
+ {"request_id": req_id},
)
return f"Shutdown request {req_id} sent to '{teammate}' (status: pending)"
@@ -366,7 +464,10 @@ def handle_plan_review(request_id: str, approve: bool, feedback: str = "") -> st
with _tracker_lock:
req["status"] = "approved" if approve else "rejected"
BUS.send(
- "lead", req["from"], feedback, "plan_approval_response",
+ "lead",
+ req["from"],
+ feedback,
+ "plan_approval_response",
{"request_id": request_id, "approve": approve, "feedback": feedback},
)
return f"Plan {req['status']} for '{req['from']}'"
@@ -379,46 +480,142 @@ def _check_shutdown_status(request_id: str) -> str:
# -- Lead tool dispatch (12 tools) --
TOOL_HANDLERS = {
- "bash": lambda **kw: _run_bash(kw["command"]),
- "read_file": lambda **kw: _run_read(kw["path"], kw.get("limit")),
- "write_file": lambda **kw: _run_write(kw["path"], kw["content"]),
- "edit_file": lambda **kw: _run_edit(kw["path"], kw["old_text"], kw["new_text"]),
- "spawn_teammate": lambda **kw: TEAM.spawn(kw["name"], kw["role"], kw["prompt"]),
- "list_teammates": lambda **kw: TEAM.list_all(),
- "send_message": lambda **kw: BUS.send("lead", kw["to"], kw["content"], kw.get("msg_type", "message")),
- "read_inbox": lambda **kw: json.dumps(BUS.read_inbox("lead"), indent=2),
- "broadcast": lambda **kw: BUS.broadcast("lead", kw["content"], TEAM.member_names()),
- "shutdown_request": lambda **kw: handle_shutdown_request(kw["teammate"]),
+ "bash": lambda **kw: _run_bash(kw["command"]),
+ "read_file": lambda **kw: _run_read(kw["path"], kw.get("limit")),
+ "write_file": lambda **kw: _run_write(kw["path"], kw["content"]),
+ "edit_file": lambda **kw: _run_edit(kw["path"], kw["old_text"], kw["new_text"]),
+ "spawn_teammate": lambda **kw: TEAM.spawn(kw["name"], kw["role"], kw["prompt"]),
+ "list_teammates": lambda **kw: TEAM.list_all(),
+ "send_message": lambda **kw: BUS.send(
+ "lead", kw["to"], kw["content"], kw.get("msg_type", "message")
+ ),
+ "read_inbox": lambda **kw: json.dumps(BUS.read_inbox("lead"), indent=2),
+ "broadcast": lambda **kw: BUS.broadcast("lead", kw["content"], TEAM.member_names()),
+ "shutdown_request": lambda **kw: handle_shutdown_request(kw["teammate"]),
"shutdown_response": lambda **kw: _check_shutdown_status(kw.get("request_id", "")),
- "plan_approval": lambda **kw: handle_plan_review(kw["request_id"], kw["approve"], kw.get("feedback", "")),
+ "plan_approval": lambda **kw: handle_plan_review(
+ kw["request_id"], kw["approve"], kw.get("feedback", "")
+ ),
}
# these base tools are unchanged from s02
TOOLS = [
- {"name": "bash", "description": "Run a shell command.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file contents.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write content to file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Replace exact text in file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
- {"name": "spawn_teammate", "description": "Spawn a persistent teammate.",
- "input_schema": {"type": "object", "properties": {"name": {"type": "string"}, "role": {"type": "string"}, "prompt": {"type": "string"}}, "required": ["name", "role", "prompt"]}},
- {"name": "list_teammates", "description": "List all teammates.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "send_message", "description": "Send a message to a teammate.",
- "input_schema": {"type": "object", "properties": {"to": {"type": "string"}, "content": {"type": "string"}, "msg_type": {"type": "string", "enum": list(VALID_MSG_TYPES)}}, "required": ["to", "content"]}},
- {"name": "read_inbox", "description": "Read and drain the lead's inbox.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "broadcast", "description": "Send a message to all teammates.",
- "input_schema": {"type": "object", "properties": {"content": {"type": "string"}}, "required": ["content"]}},
- {"name": "shutdown_request", "description": "Request a teammate to shut down gracefully. Returns a request_id for tracking.",
- "input_schema": {"type": "object", "properties": {"teammate": {"type": "string"}}, "required": ["teammate"]}},
- {"name": "shutdown_response", "description": "Check the status of a shutdown request by request_id.",
- "input_schema": {"type": "object", "properties": {"request_id": {"type": "string"}}, "required": ["request_id"]}},
- {"name": "plan_approval", "description": "Approve or reject a teammate's plan. Provide request_id + approve + optional feedback.",
- "input_schema": {"type": "object", "properties": {"request_id": {"type": "string"}, "approve": {"type": "boolean"}, "feedback": {"type": "string"}}, "required": ["request_id", "approve"]}},
+ {
+ "name": "bash",
+ "description": "Run a shell command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file contents.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write content to file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "content": {"type": "string"}},
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Replace exact text in file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
+ {
+ "name": "spawn_teammate",
+ "description": "Spawn a persistent teammate.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "role": {"type": "string"},
+ "prompt": {"type": "string"},
+ },
+ "required": ["name", "role", "prompt"],
+ },
+ },
+ {
+ "name": "list_teammates",
+ "description": "List all teammates.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "send_message",
+ "description": "Send a message to a teammate.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "to": {"type": "string"},
+ "content": {"type": "string"},
+ "msg_type": {"type": "string", "enum": list(VALID_MSG_TYPES)},
+ },
+ "required": ["to", "content"],
+ },
+ },
+ {
+ "name": "read_inbox",
+ "description": "Read and drain the lead's inbox.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "broadcast",
+ "description": "Send a message to all teammates.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"content": {"type": "string"}},
+ "required": ["content"],
+ },
+ },
+ {
+ "name": "shutdown_request",
+ "description": "Request a teammate to shut down gracefully. Returns a request_id for tracking.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"teammate": {"type": "string"}},
+ "required": ["teammate"],
+ },
+ },
+ {
+ "name": "shutdown_response",
+ "description": "Check the status of a shutdown request by request_id.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"request_id": {"type": "string"}},
+ "required": ["request_id"],
+ },
+ },
+ {
+ "name": "plan_approval",
+ "description": "Approve or reject a teammate's plan. Provide request_id + approve + optional feedback.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "request_id": {"type": "string"},
+ "approve": {"type": "boolean"},
+ "feedback": {"type": "string"},
+ },
+ "required": ["request_id", "approve"],
+ },
+ },
]
@@ -426,14 +623,18 @@ def agent_loop(messages: list):
while True:
inbox = BUS.read_inbox("lead")
if inbox:
- messages.append({
- "role": "user",
- "content": f"{json.dumps(inbox, indent=2)}",
- })
- messages.append({
- "role": "assistant",
- "content": "Noted inbox messages.",
- })
+ messages.append(
+ {
+ "role": "user",
+ "content": f"{json.dumps(inbox, indent=2)}",
+ }
+ )
+ messages.append(
+ {
+ "role": "assistant",
+ "content": "Noted inbox messages.",
+ }
+ )
response = client.messages.create(
model=MODEL,
system=SYSTEM,
@@ -449,15 +650,21 @@ def agent_loop(messages: list):
if block.type == "tool_use":
handler = TOOL_HANDLERS.get(block.name)
try:
- output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
+ output = (
+ handler(**block.input)
+ if handler
+ else f"Unknown tool: {block.name}"
+ )
except Exception as e:
output = f"Error: {e}"
print(f"> {block.name}: {str(output)[:200]}")
- results.append({
- "type": "tool_result",
- "tool_use_id": block.id,
- "content": str(output),
- })
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output),
+ }
+ )
messages.append({"role": "user", "content": results})
diff --git a/agents/s11_autonomous_agents.py b/agents/s11_autonomous_agents.py
index 856bc92c3..f4d57d95f 100644
--- a/agents/s11_autonomous_agents.py
+++ b/agents/s11_autonomous_agents.py
@@ -82,8 +82,14 @@ def __init__(self, inbox_dir: Path):
self.dir = inbox_dir
self.dir.mkdir(parents=True, exist_ok=True)
- def send(self, sender: str, to: str, content: str,
- msg_type: str = "message", extra: dict = None) -> str:
+ def send(
+ self,
+ sender: str,
+ to: str,
+ content: str,
+ msg_type: str = "message",
+ extra: dict = None,
+ ) -> str:
if msg_type not in VALID_MSG_TYPES:
return f"Error: Invalid type '{msg_type}'. Valid: {VALID_MSG_TYPES}"
msg = {
@@ -128,9 +134,11 @@ def scan_unclaimed_tasks() -> list:
unclaimed = []
for f in sorted(TASKS_DIR.glob("task_*.json")):
task = json.loads(f.read_text())
- if (task.get("status") == "pending"
- and not task.get("owner")
- and not task.get("blockedBy")):
+ if (
+ task.get("status") == "pending"
+ and not task.get("owner")
+ and not task.get("blockedBy")
+ ):
unclaimed.append(task)
return unclaimed
@@ -246,11 +254,13 @@ def _loop(self, name: str, role: str, prompt: str):
else:
output = self._exec(name, block.name, block.input)
print(f" [{name}] {block.name}: {str(output)[:120]}")
- results.append({
- "type": "tool_result",
- "tool_use_id": block.id,
- "content": str(output),
- })
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output),
+ }
+ )
messages.append({"role": "user", "content": results})
if idle_requested:
break
@@ -280,9 +290,20 @@ def _loop(self, name: str, role: str, prompt: str):
)
if len(messages) <= 3:
messages.insert(0, make_identity_block(name, role, team_name))
- messages.insert(1, {"role": "assistant", "content": f"I am {name}. Continuing."})
+ messages.insert(
+ 1,
+ {
+ "role": "assistant",
+ "content": f"I am {name}. Continuing.",
+ },
+ )
messages.append({"role": "user", "content": task_prompt})
- messages.append({"role": "assistant", "content": f"Claimed task #{task['id']}. Working on it."})
+ messages.append(
+ {
+ "role": "assistant",
+ "content": f"Claimed task #{task['id']}. Working on it.",
+ }
+ )
resume = True
break
@@ -302,26 +323,40 @@ def _exec(self, sender: str, tool_name: str, args: dict) -> str:
if tool_name == "edit_file":
return _run_edit(args["path"], args["old_text"], args["new_text"])
if tool_name == "send_message":
- return BUS.send(sender, args["to"], args["content"], args.get("msg_type", "message"))
+ return BUS.send(
+ sender, args["to"], args["content"], args.get("msg_type", "message")
+ )
if tool_name == "read_inbox":
return json.dumps(BUS.read_inbox(sender), indent=2)
if tool_name == "shutdown_response":
req_id = args["request_id"]
with _tracker_lock:
if req_id in shutdown_requests:
- shutdown_requests[req_id]["status"] = "approved" if args["approve"] else "rejected"
+ shutdown_requests[req_id]["status"] = (
+ "approved" if args["approve"] else "rejected"
+ )
BUS.send(
- sender, "lead", args.get("reason", ""),
- "shutdown_response", {"request_id": req_id, "approve": args["approve"]},
+ sender,
+ "lead",
+ args.get("reason", ""),
+ "shutdown_response",
+ {"request_id": req_id, "approve": args["approve"]},
)
return f"Shutdown {'approved' if args['approve'] else 'rejected'}"
if tool_name == "plan_approval":
plan_text = args.get("plan", "")
req_id = str(uuid.uuid4())[:8]
with _tracker_lock:
- plan_requests[req_id] = {"from": sender, "plan": plan_text, "status": "pending"}
+ plan_requests[req_id] = {
+ "from": sender,
+ "plan": plan_text,
+ "status": "pending",
+ }
BUS.send(
- sender, "lead", plan_text, "plan_approval_response",
+ sender,
+ "lead",
+ plan_text,
+ "plan_approval_response",
{"request_id": req_id, "plan": plan_text},
)
return f"Plan submitted (request_id={req_id}). Waiting for approval."
@@ -332,26 +367,103 @@ def _exec(self, sender: str, tool_name: str, args: dict) -> str:
def _teammate_tools(self) -> list:
# these base tools are unchanged from s02
return [
- {"name": "bash", "description": "Run a shell command.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file contents.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write content to file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Replace exact text in file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
- {"name": "send_message", "description": "Send message to a teammate.",
- "input_schema": {"type": "object", "properties": {"to": {"type": "string"}, "content": {"type": "string"}, "msg_type": {"type": "string", "enum": list(VALID_MSG_TYPES)}}, "required": ["to", "content"]}},
- {"name": "read_inbox", "description": "Read and drain your inbox.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "shutdown_response", "description": "Respond to a shutdown request.",
- "input_schema": {"type": "object", "properties": {"request_id": {"type": "string"}, "approve": {"type": "boolean"}, "reason": {"type": "string"}}, "required": ["request_id", "approve"]}},
- {"name": "plan_approval", "description": "Submit a plan for lead approval.",
- "input_schema": {"type": "object", "properties": {"plan": {"type": "string"}}, "required": ["plan"]}},
- {"name": "idle", "description": "Signal that you have no more work. Enters idle polling phase.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "claim_task", "description": "Claim a task from the task board by ID.",
- "input_schema": {"type": "object", "properties": {"task_id": {"type": "integer"}}, "required": ["task_id"]}},
+ {
+ "name": "bash",
+ "description": "Run a shell command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file contents.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write content to file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "content": {"type": "string"},
+ },
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Replace exact text in file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
+ {
+ "name": "send_message",
+ "description": "Send message to a teammate.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "to": {"type": "string"},
+ "content": {"type": "string"},
+ "msg_type": {"type": "string", "enum": list(VALID_MSG_TYPES)},
+ },
+ "required": ["to", "content"],
+ },
+ },
+ {
+ "name": "read_inbox",
+ "description": "Read and drain your inbox.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "shutdown_response",
+ "description": "Respond to a shutdown request.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "request_id": {"type": "string"},
+ "approve": {"type": "boolean"},
+ "reason": {"type": "string"},
+ },
+ "required": ["request_id", "approve"],
+ },
+ },
+ {
+ "name": "plan_approval",
+ "description": "Submit a plan for lead approval.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"plan": {"type": "string"}},
+ "required": ["plan"],
+ },
+ },
+ {
+ "name": "idle",
+ "description": "Signal that you have no more work. Enters idle polling phase.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "claim_task",
+ "description": "Claim a task from the task board by ID.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"task_id": {"type": "integer"}},
+ "required": ["task_id"],
+ },
+ },
]
def list_all(self) -> str:
@@ -383,8 +495,12 @@ def _run_bash(command: str) -> str:
return "Error: Dangerous command blocked"
try:
r = subprocess.run(
- command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120,
+ command,
+ shell=True,
+ cwd=WORKDIR,
+ capture_output=True,
+ text=True,
+ timeout=120,
)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
@@ -392,11 +508,13 @@ def _run_bash(command: str) -> str:
return "Error: Timeout (120s)"
-def _run_read(path: str, limit: int = None) -> str:
+def _run_read(path: str, limit: int | None = None) -> str:
try:
lines = _safe_path(path).read_text().splitlines()
- if limit and limit < len(lines):
- lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
+ if limit is not None:
+ limit = max(0, int(limit))
+ if limit < len(lines):
+ lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
except Exception as e:
return f"Error: {e}"
@@ -430,8 +548,11 @@ def handle_shutdown_request(teammate: str) -> str:
with _tracker_lock:
shutdown_requests[req_id] = {"target": teammate, "status": "pending"}
BUS.send(
- "lead", teammate, "Please shut down gracefully.",
- "shutdown_request", {"request_id": req_id},
+ "lead",
+ teammate,
+ "Please shut down gracefully.",
+ "shutdown_request",
+ {"request_id": req_id},
)
return f"Shutdown request {req_id} sent to '{teammate}'"
@@ -444,7 +565,10 @@ def handle_plan_review(request_id: str, approve: bool, feedback: str = "") -> st
with _tracker_lock:
req["status"] = "approved" if approve else "rejected"
BUS.send(
- "lead", req["from"], feedback, "plan_approval_response",
+ "lead",
+ req["from"],
+ feedback,
+ "plan_approval_response",
{"request_id": request_id, "approve": approve, "feedback": feedback},
)
return f"Plan {req['status']} for '{req['from']}'"
@@ -457,52 +581,158 @@ def _check_shutdown_status(request_id: str) -> str:
# -- Lead tool dispatch (14 tools) --
TOOL_HANDLERS = {
- "bash": lambda **kw: _run_bash(kw["command"]),
- "read_file": lambda **kw: _run_read(kw["path"], kw.get("limit")),
- "write_file": lambda **kw: _run_write(kw["path"], kw["content"]),
- "edit_file": lambda **kw: _run_edit(kw["path"], kw["old_text"], kw["new_text"]),
- "spawn_teammate": lambda **kw: TEAM.spawn(kw["name"], kw["role"], kw["prompt"]),
- "list_teammates": lambda **kw: TEAM.list_all(),
- "send_message": lambda **kw: BUS.send("lead", kw["to"], kw["content"], kw.get("msg_type", "message")),
- "read_inbox": lambda **kw: json.dumps(BUS.read_inbox("lead"), indent=2),
- "broadcast": lambda **kw: BUS.broadcast("lead", kw["content"], TEAM.member_names()),
- "shutdown_request": lambda **kw: handle_shutdown_request(kw["teammate"]),
+ "bash": lambda **kw: _run_bash(kw["command"]),
+ "read_file": lambda **kw: _run_read(kw["path"], kw.get("limit")),
+ "write_file": lambda **kw: _run_write(kw["path"], kw["content"]),
+ "edit_file": lambda **kw: _run_edit(kw["path"], kw["old_text"], kw["new_text"]),
+ "spawn_teammate": lambda **kw: TEAM.spawn(kw["name"], kw["role"], kw["prompt"]),
+ "list_teammates": lambda **kw: TEAM.list_all(),
+ "send_message": lambda **kw: BUS.send(
+ "lead", kw["to"], kw["content"], kw.get("msg_type", "message")
+ ),
+ "read_inbox": lambda **kw: json.dumps(BUS.read_inbox("lead"), indent=2),
+ "broadcast": lambda **kw: BUS.broadcast("lead", kw["content"], TEAM.member_names()),
+ "shutdown_request": lambda **kw: handle_shutdown_request(kw["teammate"]),
"shutdown_response": lambda **kw: _check_shutdown_status(kw.get("request_id", "")),
- "plan_approval": lambda **kw: handle_plan_review(kw["request_id"], kw["approve"], kw.get("feedback", "")),
- "idle": lambda **kw: "Lead does not idle.",
- "claim_task": lambda **kw: claim_task(kw["task_id"], "lead"),
+ "plan_approval": lambda **kw: handle_plan_review(
+ kw["request_id"], kw["approve"], kw.get("feedback", "")
+ ),
+ "idle": lambda **kw: "Lead does not idle.",
+ "claim_task": lambda **kw: claim_task(kw["task_id"], "lead"),
}
# these base tools are unchanged from s02
TOOLS = [
- {"name": "bash", "description": "Run a shell command.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file contents.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write content to file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Replace exact text in file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
- {"name": "spawn_teammate", "description": "Spawn an autonomous teammate.",
- "input_schema": {"type": "object", "properties": {"name": {"type": "string"}, "role": {"type": "string"}, "prompt": {"type": "string"}}, "required": ["name", "role", "prompt"]}},
- {"name": "list_teammates", "description": "List all teammates.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "send_message", "description": "Send a message to a teammate.",
- "input_schema": {"type": "object", "properties": {"to": {"type": "string"}, "content": {"type": "string"}, "msg_type": {"type": "string", "enum": list(VALID_MSG_TYPES)}}, "required": ["to", "content"]}},
- {"name": "read_inbox", "description": "Read and drain the lead's inbox.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "broadcast", "description": "Send a message to all teammates.",
- "input_schema": {"type": "object", "properties": {"content": {"type": "string"}}, "required": ["content"]}},
- {"name": "shutdown_request", "description": "Request a teammate to shut down.",
- "input_schema": {"type": "object", "properties": {"teammate": {"type": "string"}}, "required": ["teammate"]}},
- {"name": "shutdown_response", "description": "Check shutdown request status.",
- "input_schema": {"type": "object", "properties": {"request_id": {"type": "string"}}, "required": ["request_id"]}},
- {"name": "plan_approval", "description": "Approve or reject a teammate's plan.",
- "input_schema": {"type": "object", "properties": {"request_id": {"type": "string"}, "approve": {"type": "boolean"}, "feedback": {"type": "string"}}, "required": ["request_id", "approve"]}},
- {"name": "idle", "description": "Enter idle state (for lead -- rarely used).",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "claim_task", "description": "Claim a task from the board by ID.",
- "input_schema": {"type": "object", "properties": {"task_id": {"type": "integer"}}, "required": ["task_id"]}},
+ {
+ "name": "bash",
+ "description": "Run a shell command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file contents.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write content to file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "content": {"type": "string"}},
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Replace exact text in file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
+ {
+ "name": "spawn_teammate",
+ "description": "Spawn an autonomous teammate.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "role": {"type": "string"},
+ "prompt": {"type": "string"},
+ },
+ "required": ["name", "role", "prompt"],
+ },
+ },
+ {
+ "name": "list_teammates",
+ "description": "List all teammates.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "send_message",
+ "description": "Send a message to a teammate.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "to": {"type": "string"},
+ "content": {"type": "string"},
+ "msg_type": {"type": "string", "enum": list(VALID_MSG_TYPES)},
+ },
+ "required": ["to", "content"],
+ },
+ },
+ {
+ "name": "read_inbox",
+ "description": "Read and drain the lead's inbox.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "broadcast",
+ "description": "Send a message to all teammates.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"content": {"type": "string"}},
+ "required": ["content"],
+ },
+ },
+ {
+ "name": "shutdown_request",
+ "description": "Request a teammate to shut down.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"teammate": {"type": "string"}},
+ "required": ["teammate"],
+ },
+ },
+ {
+ "name": "shutdown_response",
+ "description": "Check shutdown request status.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"request_id": {"type": "string"}},
+ "required": ["request_id"],
+ },
+ },
+ {
+ "name": "plan_approval",
+ "description": "Approve or reject a teammate's plan.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "request_id": {"type": "string"},
+ "approve": {"type": "boolean"},
+ "feedback": {"type": "string"},
+ },
+ "required": ["request_id", "approve"],
+ },
+ },
+ {
+ "name": "idle",
+ "description": "Enter idle state (for lead -- rarely used).",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "claim_task",
+ "description": "Claim a task from the board by ID.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"task_id": {"type": "integer"}},
+ "required": ["task_id"],
+ },
+ },
]
@@ -510,14 +740,18 @@ def agent_loop(messages: list):
while True:
inbox = BUS.read_inbox("lead")
if inbox:
- messages.append({
- "role": "user",
- "content": f"{json.dumps(inbox, indent=2)}",
- })
- messages.append({
- "role": "assistant",
- "content": "Noted inbox messages.",
- })
+ messages.append(
+ {
+ "role": "user",
+ "content": f"{json.dumps(inbox, indent=2)}",
+ }
+ )
+ messages.append(
+ {
+ "role": "assistant",
+ "content": "Noted inbox messages.",
+ }
+ )
response = client.messages.create(
model=MODEL,
system=SYSTEM,
@@ -533,15 +767,21 @@ def agent_loop(messages: list):
if block.type == "tool_use":
handler = TOOL_HANDLERS.get(block.name)
try:
- output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
+ output = (
+ handler(**block.input)
+ if handler
+ else f"Unknown tool: {block.name}"
+ )
except Exception as e:
output = f"Error: {e}"
print(f"> {block.name}: {str(output)[:200]}")
- results.append({
- "type": "tool_result",
- "tool_use_id": block.id,
- "content": str(output),
- })
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output),
+ }
+ )
messages.append({"role": "user", "content": results})
@@ -564,7 +804,11 @@ def agent_loop(messages: list):
TASKS_DIR.mkdir(exist_ok=True)
for f in sorted(TASKS_DIR.glob("task_*.json")):
t = json.loads(f.read_text())
- marker = {"pending": "[ ]", "in_progress": "[>]", "completed": "[x]"}.get(t["status"], "[?]")
+ marker = {
+ "pending": "[ ]",
+ "in_progress": "[>]",
+ "completed": "[x]",
+ }.get(t["status"], "[?]")
owner = f" @{t['owner']}" if t.get("owner") else ""
print(f" {marker} #{t['id']}: {t['subject']}{owner}")
continue
diff --git a/agents/s12_worktree_task_isolation.py b/agents/s12_worktree_task_isolation.py
index 0162de58f..05fc56f77 100644
--- a/agents/s12_worktree_task_isolation.py
+++ b/agents/s12_worktree_task_isolation.py
@@ -390,7 +390,9 @@ def run(self, name: str, command: str) -> str:
except subprocess.TimeoutExpired:
return "Error: Timeout (300s)"
- def remove(self, name: str, force: bool = False, complete_task: bool = False) -> str:
+ def remove(
+ self, name: str, force: bool = False, complete_task: bool = False
+ ) -> str:
wt = self._find(name)
if not wt:
return f"Error: Unknown worktree '{name}'"
@@ -467,7 +469,9 @@ def keep(self, name: str) -> str:
"status": "kept",
},
)
- return json.dumps(kept, indent=2) if kept else f"Error: Unknown worktree '{name}'"
+ return (
+ json.dumps(kept, indent=2) if kept else f"Error: Unknown worktree '{name}'"
+ )
WORKTREES = WorktreeManager(REPO_ROOT, TASKS, EVENTS)
@@ -500,11 +504,13 @@ def run_bash(command: str) -> str:
return "Error: Timeout (120s)"
-def run_read(path: str, limit: int = None) -> str:
+def run_read(path: str, limit: int | None = None) -> str:
try:
lines = safe_path(path).read_text().splitlines()
- if limit and limit < len(lines):
- lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
+ if limit is not None:
+ limit = max(0, int(limit))
+ if limit < len(lines):
+ lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
except Exception as e:
return f"Error: {e}"
@@ -540,14 +546,22 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
"task_create": lambda **kw: TASKS.create(kw["subject"], kw.get("description", "")),
"task_list": lambda **kw: TASKS.list_all(),
"task_get": lambda **kw: TASKS.get(kw["task_id"]),
- "task_update": lambda **kw: TASKS.update(kw["task_id"], kw.get("status"), kw.get("owner")),
- "task_bind_worktree": lambda **kw: TASKS.bind_worktree(kw["task_id"], kw["worktree"], kw.get("owner", "")),
- "worktree_create": lambda **kw: WORKTREES.create(kw["name"], kw.get("task_id"), kw.get("base_ref", "HEAD")),
+ "task_update": lambda **kw: TASKS.update(
+ kw["task_id"], kw.get("status"), kw.get("owner")
+ ),
+ "task_bind_worktree": lambda **kw: TASKS.bind_worktree(
+ kw["task_id"], kw["worktree"], kw.get("owner", "")
+ ),
+ "worktree_create": lambda **kw: WORKTREES.create(
+ kw["name"], kw.get("task_id"), kw.get("base_ref", "HEAD")
+ ),
"worktree_list": lambda **kw: WORKTREES.list_all(),
"worktree_status": lambda **kw: WORKTREES.status(kw["name"]),
"worktree_run": lambda **kw: WORKTREES.run(kw["name"], kw["command"]),
"worktree_keep": lambda **kw: WORKTREES.keep(kw["name"]),
- "worktree_remove": lambda **kw: WORKTREES.remove(kw["name"], kw.get("force", False), kw.get("complete_task", False)),
+ "worktree_remove": lambda **kw: WORKTREES.remove(
+ kw["name"], kw.get("force", False), kw.get("complete_task", False)
+ ),
"worktree_events": lambda **kw: EVENTS.list_recent(kw.get("limit", 20)),
}
@@ -743,7 +757,11 @@ def agent_loop(messages: list):
if block.type == "tool_use":
handler = TOOL_HANDLERS.get(block.name)
try:
- output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
+ output = (
+ handler(**block.input)
+ if handler
+ else f"Unknown tool: {block.name}"
+ )
except Exception as e:
output = f"Error: {e}"
print(f"> {block.name}: {str(output)[:200]}")
diff --git a/agents/s_full.py b/agents/s_full.py
index d4dcfd3c6..d3910da4d 100644
--- a/agents/s_full.py
+++ b/agents/s_full.py
@@ -65,8 +65,13 @@
POLL_INTERVAL = 5
IDLE_TIMEOUT = 60
-VALID_MSG_TYPES = {"message", "broadcast", "shutdown_request",
- "shutdown_response", "plan_approval_response"}
+VALID_MSG_TYPES = {
+ "message",
+ "broadcast",
+ "shutdown_request",
+ "shutdown_response",
+ "plan_approval_response",
+}
# === SECTION: base_tools ===
@@ -76,27 +81,38 @@ def safe_path(p: str) -> Path:
raise ValueError(f"Path escapes workspace: {p}")
return path
+
def run_bash(command: str) -> str:
dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
if any(d in command for d in dangerous):
return "Error: Dangerous command blocked"
try:
- r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ r = subprocess.run(
+ command,
+ shell=True,
+ cwd=WORKDIR,
+ capture_output=True,
+ text=True,
+ timeout=120,
+ )
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
return "Error: Timeout (120s)"
-def run_read(path: str, limit: int = None) -> str:
+
+def run_read(path: str, limit: int | None = None) -> str:
try:
lines = safe_path(path).read_text().splitlines()
- if limit and limit < len(lines):
- lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
+ if limit is not None:
+ limit = max(0, int(limit))
+ if limit < len(lines):
+ lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
except Exception as e:
return f"Error: {e}"
+
def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
@@ -106,6 +122,7 @@ def run_write(path: str, content: str) -> str:
except Exception as e:
return f"Error: {e}"
+
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
@@ -129,23 +146,33 @@ def update(self, items: list) -> str:
content = str(item.get("content", "")).strip()
status = str(item.get("status", "pending")).lower()
af = str(item.get("activeForm", "")).strip()
- if not content: raise ValueError(f"Item {i}: content required")
+ if not content:
+ raise ValueError(f"Item {i}: content required")
if status not in ("pending", "in_progress", "completed"):
raise ValueError(f"Item {i}: invalid status '{status}'")
- if not af: raise ValueError(f"Item {i}: activeForm required")
- if status == "in_progress": ip += 1
+ if not af:
+ raise ValueError(f"Item {i}: activeForm required")
+ if status == "in_progress":
+ ip += 1
validated.append({"content": content, "status": status, "activeForm": af})
- if len(validated) > 20: raise ValueError("Max 20 todos")
- if ip > 1: raise ValueError("Only one in_progress allowed")
+ if len(validated) > 20:
+ raise ValueError("Max 20 todos")
+ if ip > 1:
+ raise ValueError("Only one in_progress allowed")
self.items = validated
return self.render()
def render(self) -> str:
- if not self.items: return "No todos."
+ if not self.items:
+ return "No todos."
lines = []
for item in self.items:
- m = {"completed": "[x]", "in_progress": "[>]", "pending": "[ ]"}.get(item["status"], "[?]")
- suffix = f" <- {item['activeForm']}" if item["status"] == "in_progress" else ""
+ m = {"completed": "[x]", "in_progress": "[>]", "pending": "[ ]"}.get(
+ item["status"], "[?]"
+ )
+ suffix = (
+ f" <- {item['activeForm']}" if item["status"] == "in_progress" else ""
+ )
lines.append(f"{m} {item['content']}{suffix}")
done = sum(1 for t in self.items if t["status"] == "completed")
lines.append(f"\n({done}/{len(self.items)} completed)")
@@ -158,17 +185,52 @@ def has_open_items(self) -> bool:
# === SECTION: subagent (s04) ===
def run_subagent(prompt: str, agent_type: str = "Explore") -> str:
sub_tools = [
- {"name": "bash", "description": "Run command.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]}},
+ {
+ "name": "bash",
+ "description": "Run command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}},
+ "required": ["path"],
+ },
+ },
]
if agent_type != "Explore":
sub_tools += [
- {"name": "write_file", "description": "Write file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Edit file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
+ {
+ "name": "write_file",
+ "description": "Write file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "content": {"type": "string"},
+ },
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Edit file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
]
sub_handlers = {
"bash": lambda **kw: run_bash(kw["command"]),
@@ -179,7 +241,9 @@ def run_subagent(prompt: str, agent_type: str = "Explore") -> str:
sub_msgs = [{"role": "user", "content": prompt}]
resp = None
for _ in range(30):
- resp = client.messages.create(model=MODEL, messages=sub_msgs, tools=sub_tools, max_tokens=8000)
+ resp = client.messages.create(
+ model=MODEL, messages=sub_msgs, tools=sub_tools, max_tokens=8000
+ )
sub_msgs.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use":
break
@@ -187,10 +251,19 @@ def run_subagent(prompt: str, agent_type: str = "Explore") -> str:
for b in resp.content:
if b.type == "tool_use":
h = sub_handlers.get(b.name, lambda **kw: "Unknown tool")
- results.append({"type": "tool_result", "tool_use_id": b.id, "content": str(h(**b.input))[:50000]})
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": b.id,
+ "content": str(h(**b.input))[:50000],
+ }
+ )
sub_msgs.append({"role": "user", "content": results})
if resp:
- return "".join(b.text for b in resp.content if hasattr(b, "text")) or "(no summary)"
+ return (
+ "".join(b.text for b in resp.content if hasattr(b, "text"))
+ or "(no summary)"
+ )
return "(subagent failed)"
@@ -213,19 +286,25 @@ def __init__(self, skills_dir: Path):
self.skills[name] = {"meta": meta, "body": body}
def descriptions(self) -> str:
- if not self.skills: return "(no skills)"
- return "\n".join(f" - {n}: {s['meta'].get('description', '-')}" for n, s in self.skills.items())
+ if not self.skills:
+ return "(no skills)"
+ return "\n".join(
+ f" - {n}: {s['meta'].get('description', '-')}"
+ for n, s in self.skills.items()
+ )
def load(self, name: str) -> str:
s = self.skills.get(name)
- if not s: return f"Error: Unknown skill '{name}'. Available: {', '.join(self.skills.keys())}"
- return f"\n{s['body']}\n"
+ if not s:
+ return f"Error: Unknown skill '{name}'. Available: {', '.join(self.skills.keys())}"
+ return f'\n{s["body"]}\n'
# === SECTION: compression (s06) ===
def estimate_tokens(messages: list) -> int:
return len(json.dumps(messages, default=str)) // 4
+
def microcompact(messages: list):
indices = []
for i, msg in enumerate(messages):
@@ -239,6 +318,7 @@ def microcompact(messages: list):
if isinstance(part.get("content"), str) and len(part["content"]) > 100:
part["content"] = "[cleared]"
+
def auto_compact(messages: list) -> list:
TRANSCRIPT_DIR.mkdir(exist_ok=True)
path = TRANSCRIPT_DIR / f"transcript_{int(time.time())}.jsonl"
@@ -248,13 +328,18 @@ def auto_compact(messages: list) -> list:
conv_text = json.dumps(messages, default=str)[:80000]
resp = client.messages.create(
model=MODEL,
- messages=[{"role": "user", "content": f"Summarize for continuity:\n{conv_text}"}],
+ messages=[
+ {"role": "user", "content": f"Summarize for continuity:\n{conv_text}"}
+ ],
max_tokens=2000,
)
summary = resp.content[0].text
return [
{"role": "user", "content": f"[Compressed. Transcript: {path}]\n{summary}"},
- {"role": "assistant", "content": "Understood. Continuing with summary context."},
+ {
+ "role": "assistant",
+ "content": "Understood. Continuing with summary context.",
+ },
]
@@ -269,23 +354,36 @@ def _next_id(self) -> int:
def _load(self, tid: int) -> dict:
p = TASKS_DIR / f"task_{tid}.json"
- if not p.exists(): raise ValueError(f"Task {tid} not found")
+ if not p.exists():
+ raise ValueError(f"Task {tid} not found")
return json.loads(p.read_text())
def _save(self, task: dict):
(TASKS_DIR / f"task_{task['id']}.json").write_text(json.dumps(task, indent=2))
def create(self, subject: str, description: str = "") -> str:
- task = {"id": self._next_id(), "subject": subject, "description": description,
- "status": "pending", "owner": None, "blockedBy": [], "blocks": []}
+ task = {
+ "id": self._next_id(),
+ "subject": subject,
+ "description": description,
+ "status": "pending",
+ "owner": None,
+ "blockedBy": [],
+ "blocks": [],
+ }
self._save(task)
return json.dumps(task, indent=2)
def get(self, tid: int) -> str:
return json.dumps(self._load(tid), indent=2)
- def update(self, tid: int, status: str = None,
- add_blocked_by: list = None, add_blocks: list = None) -> str:
+ def update(
+ self,
+ tid: int,
+ status: str = None,
+ add_blocked_by: list = None,
+ add_blocks: list = None,
+ ) -> str:
task = self._load(tid)
if status:
task["status"] = status
@@ -306,11 +404,16 @@ def update(self, tid: int, status: str = None,
return json.dumps(task, indent=2)
def list_all(self) -> str:
- tasks = [json.loads(f.read_text()) for f in sorted(TASKS_DIR.glob("task_*.json"))]
- if not tasks: return "No tasks."
+ tasks = [
+ json.loads(f.read_text()) for f in sorted(TASKS_DIR.glob("task_*.json"))
+ ]
+ if not tasks:
+ return "No tasks."
lines = []
for t in tasks:
- m = {"pending": "[ ]", "in_progress": "[>]", "completed": "[x]"}.get(t["status"], "[?]")
+ m = {"pending": "[ ]", "in_progress": "[>]", "completed": "[x]"}.get(
+ t["status"], "[?]"
+ )
owner = f" @{t['owner']}" if t.get("owner") else ""
blocked = f" (blocked by: {t['blockedBy']})" if t.get("blockedBy") else ""
lines.append(f"{m} #{t['id']}: {t['subject']}{owner}{blocked}")
@@ -333,25 +436,50 @@ def __init__(self):
def run(self, command: str, timeout: int = 120) -> str:
tid = str(uuid.uuid4())[:8]
self.tasks[tid] = {"status": "running", "command": command, "result": None}
- threading.Thread(target=self._exec, args=(tid, command, timeout), daemon=True).start()
+ threading.Thread(
+ target=self._exec, args=(tid, command, timeout), daemon=True
+ ).start()
return f"Background task {tid} started: {command[:80]}"
def _exec(self, tid: str, command: str, timeout: int):
try:
- r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=timeout)
+ r = subprocess.run(
+ command,
+ shell=True,
+ cwd=WORKDIR,
+ capture_output=True,
+ text=True,
+ timeout=timeout,
+ )
output = (r.stdout + r.stderr).strip()[:50000]
- self.tasks[tid].update({"status": "completed", "result": output or "(no output)"})
+ self.tasks[tid].update(
+ {"status": "completed", "result": output or "(no output)"}
+ )
except Exception as e:
self.tasks[tid].update({"status": "error", "result": str(e)})
- self.notifications.put({"task_id": tid, "status": self.tasks[tid]["status"],
- "result": self.tasks[tid]["result"][:500]})
+ self.notifications.put(
+ {
+ "task_id": tid,
+ "status": self.tasks[tid]["status"],
+ "result": self.tasks[tid]["result"][:500],
+ }
+ )
def check(self, tid: str = None) -> str:
if tid:
t = self.tasks.get(tid)
- return f"[{t['status']}] {t.get('result', '(running)')}" if t else f"Unknown: {tid}"
- return "\n".join(f"{k}: [{v['status']}] {v['command'][:60]}" for k, v in self.tasks.items()) or "No bg tasks."
+ return (
+ f"[{t['status']}] {t.get('result', '(running)')}"
+ if t
+ else f"Unknown: {tid}"
+ )
+ return (
+ "\n".join(
+ f"{k}: [{v['status']}] {v['command'][:60]}"
+ for k, v in self.tasks.items()
+ )
+ or "No bg tasks."
+ )
def drain(self) -> list:
notifs = []
@@ -365,18 +493,30 @@ class MessageBus:
def __init__(self):
INBOX_DIR.mkdir(parents=True, exist_ok=True)
- def send(self, sender: str, to: str, content: str,
- msg_type: str = "message", extra: dict = None) -> str:
- msg = {"type": msg_type, "from": sender, "content": content,
- "timestamp": time.time()}
- if extra: msg.update(extra)
+ def send(
+ self,
+ sender: str,
+ to: str,
+ content: str,
+ msg_type: str = "message",
+ extra: dict = None,
+ ) -> str:
+ msg = {
+ "type": msg_type,
+ "from": sender,
+ "content": content,
+ "timestamp": time.time(),
+ }
+ if extra:
+ msg.update(extra)
with open(INBOX_DIR / f"{to}.jsonl", "a") as f:
f.write(json.dumps(msg) + "\n")
return f"Sent {msg_type} to {to}"
def read_inbox(self, name: str) -> list:
path = INBOX_DIR / f"{name}.jsonl"
- if not path.exists(): return []
+ if not path.exists():
+ return []
msgs = [json.loads(l) for l in path.read_text().strip().splitlines() if l]
path.write_text("")
return msgs
@@ -415,7 +555,8 @@ def _save(self):
def _find(self, name: str) -> dict:
for m in self.config["members"]:
- if m["name"] == name: return m
+ if m["name"] == name:
+ return m
return None
def spawn(self, name: str, role: str, prompt: str) -> str:
@@ -429,7 +570,9 @@ def spawn(self, name: str, role: str, prompt: str) -> str:
member = {"name": name, "role": role, "status": "working"}
self.config["members"].append(member)
self._save()
- threading.Thread(target=self._loop, args=(name, role, prompt), daemon=True).start()
+ threading.Thread(
+ target=self._loop, args=(name, role, prompt), daemon=True
+ ).start()
return f"Spawned '{name}' (role: {role})"
def _set_status(self, name: str, status: str):
@@ -440,17 +583,81 @@ def _set_status(self, name: str, status: str):
def _loop(self, name: str, role: str, prompt: str):
team_name = self.config["team_name"]
- sys_prompt = (f"You are '{name}', role: {role}, team: {team_name}, at {WORKDIR}. "
- f"Use idle when done with current work. You may auto-claim tasks.")
+ sys_prompt = (
+ f"You are '{name}', role: {role}, team: {team_name}, at {WORKDIR}. "
+ f"Use idle when done with current work. You may auto-claim tasks."
+ )
messages = [{"role": "user", "content": prompt}]
tools = [
- {"name": "bash", "description": "Run command.", "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file.", "input_schema": {"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write file.", "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Edit file.", "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
- {"name": "send_message", "description": "Send message.", "input_schema": {"type": "object", "properties": {"to": {"type": "string"}, "content": {"type": "string"}}, "required": ["to", "content"]}},
- {"name": "idle", "description": "Signal no more work.", "input_schema": {"type": "object", "properties": {}}},
- {"name": "claim_task", "description": "Claim task by ID.", "input_schema": {"type": "object", "properties": {"task_id": {"type": "integer"}}, "required": ["task_id"]}},
+ {
+ "name": "bash",
+ "description": "Run command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "content": {"type": "string"},
+ },
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Edit file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
+ {
+ "name": "send_message",
+ "description": "Send message.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "to": {"type": "string"},
+ "content": {"type": "string"},
+ },
+ "required": ["to", "content"],
+ },
+ },
+ {
+ "name": "idle",
+ "description": "Signal no more work.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "claim_task",
+ "description": "Claim task by ID.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"task_id": {"type": "integer"}},
+ "required": ["task_id"],
+ },
+ },
]
while True:
# -- WORK PHASE --
@@ -463,8 +670,12 @@ def _loop(self, name: str, role: str, prompt: str):
messages.append({"role": "user", "content": json.dumps(msg)})
try:
response = client.messages.create(
- model=MODEL, system=sys_prompt, messages=messages,
- tools=tools, max_tokens=8000)
+ model=MODEL,
+ system=sys_prompt,
+ messages=messages,
+ tools=tools,
+ max_tokens=8000,
+ )
except Exception:
self._set_status(name, "shutdown")
return
@@ -481,15 +692,31 @@ def _loop(self, name: str, role: str, prompt: str):
elif block.name == "claim_task":
output = self.task_mgr.claim(block.input["task_id"], name)
elif block.name == "send_message":
- output = self.bus.send(name, block.input["to"], block.input["content"])
+ output = self.bus.send(
+ name, block.input["to"], block.input["content"]
+ )
else:
- dispatch = {"bash": lambda **kw: run_bash(kw["command"]),
- "read_file": lambda **kw: run_read(kw["path"]),
- "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
- "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"])}
- output = dispatch.get(block.name, lambda **kw: "Unknown")(**block.input)
+ dispatch = {
+ "bash": lambda **kw: run_bash(kw["command"]),
+ "read_file": lambda **kw: run_read(kw["path"]),
+ "write_file": lambda **kw: run_write(
+ kw["path"], kw["content"]
+ ),
+ "edit_file": lambda **kw: run_edit(
+ kw["path"], kw["old_text"], kw["new_text"]
+ ),
+ }
+ output = dispatch.get(block.name, lambda **kw: "Unknown")(
+ **block.input
+ )
print(f" [{name}] {block.name}: {str(output)[:120]}")
- results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(output)})
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output),
+ }
+ )
messages.append({"role": "user", "content": results})
if idle_requested:
break
@@ -510,19 +737,43 @@ def _loop(self, name: str, role: str, prompt: str):
unclaimed = []
for f in sorted(TASKS_DIR.glob("task_*.json")):
t = json.loads(f.read_text())
- if t.get("status") == "pending" and not t.get("owner") and not t.get("blockedBy"):
+ if (
+ t.get("status") == "pending"
+ and not t.get("owner")
+ and not t.get("blockedBy")
+ ):
unclaimed.append(t)
if unclaimed:
task = unclaimed[0]
self.task_mgr.claim(task["id"], name)
# Identity re-injection for compressed contexts
if len(messages) <= 3:
- messages.insert(0, {"role": "user", "content":
- f"You are '{name}', role: {role}, team: {team_name}."})
- messages.insert(1, {"role": "assistant", "content": f"I am {name}. Continuing."})
- messages.append({"role": "user", "content":
- f"Task #{task['id']}: {task['subject']}\n{task.get('description', '')}"})
- messages.append({"role": "assistant", "content": f"Claimed task #{task['id']}. Working on it."})
+ messages.insert(
+ 0,
+ {
+ "role": "user",
+ "content": f"You are '{name}', role: {role}, team: {team_name}.",
+ },
+ )
+ messages.insert(
+ 1,
+ {
+ "role": "assistant",
+ "content": f"I am {name}. Continuing.",
+ },
+ )
+ messages.append(
+ {
+ "role": "user",
+ "content": f"Task #{task['id']}: {task['subject']}\n{task.get('description', '')}",
+ }
+ )
+ messages.append(
+ {
+ "role": "assistant",
+ "content": f"Claimed task #{task['id']}. Working on it.",
+ }
+ )
resume = True
break
if not resume:
@@ -531,7 +782,8 @@ def _loop(self, name: str, role: str, prompt: str):
self._set_status(name, "working")
def list_all(self) -> str:
- if not self.config["members"]: return "No teammates."
+ if not self.config["members"]:
+ return "No teammates."
lines = [f"Team: {self.config['team_name']}"]
for m in self.config["members"]:
lines.append(f" {m['name']} ({m['role']}): {m['status']}")
@@ -560,93 +812,306 @@ def member_names(self) -> list:
def handle_shutdown_request(teammate: str) -> str:
req_id = str(uuid.uuid4())[:8]
shutdown_requests[req_id] = {"target": teammate, "status": "pending"}
- BUS.send("lead", teammate, "Please shut down.", "shutdown_request", {"request_id": req_id})
+ BUS.send(
+ "lead",
+ teammate,
+ "Please shut down.",
+ "shutdown_request",
+ {"request_id": req_id},
+ )
return f"Shutdown request {req_id} sent to '{teammate}'"
+
# === SECTION: plan_approval (s10) ===
def handle_plan_review(request_id: str, approve: bool, feedback: str = "") -> str:
req = plan_requests.get(request_id)
- if not req: return f"Error: Unknown plan request_id '{request_id}'"
+ if not req:
+ return f"Error: Unknown plan request_id '{request_id}'"
req["status"] = "approved" if approve else "rejected"
- BUS.send("lead", req["from"], feedback, "plan_approval_response",
- {"request_id": request_id, "approve": approve, "feedback": feedback})
+ BUS.send(
+ "lead",
+ req["from"],
+ feedback,
+ "plan_approval_response",
+ {"request_id": request_id, "approve": approve, "feedback": feedback},
+ )
return f"Plan {req['status']} for '{req['from']}'"
# === SECTION: tool_dispatch (s02) ===
TOOL_HANDLERS = {
- "bash": lambda **kw: run_bash(kw["command"]),
- "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
- "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
- "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
- "TodoWrite": lambda **kw: TODO.update(kw["items"]),
- "task": lambda **kw: run_subagent(kw["prompt"], kw.get("agent_type", "Explore")),
- "load_skill": lambda **kw: SKILLS.load(kw["name"]),
- "compress": lambda **kw: "Compressing...",
- "background_run": lambda **kw: BG.run(kw["command"], kw.get("timeout", 120)),
+ "bash": lambda **kw: run_bash(kw["command"]),
+ "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
+ "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
+ "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
+ "TodoWrite": lambda **kw: TODO.update(kw["items"]),
+ "task": lambda **kw: run_subagent(kw["prompt"], kw.get("agent_type", "Explore")),
+ "load_skill": lambda **kw: SKILLS.load(kw["name"]),
+ "compress": lambda **kw: "Compressing...",
+ "background_run": lambda **kw: BG.run(kw["command"], kw.get("timeout", 120)),
"check_background": lambda **kw: BG.check(kw.get("task_id")),
- "task_create": lambda **kw: TASK_MGR.create(kw["subject"], kw.get("description", "")),
- "task_get": lambda **kw: TASK_MGR.get(kw["task_id"]),
- "task_update": lambda **kw: TASK_MGR.update(kw["task_id"], kw.get("status"), kw.get("add_blocked_by"), kw.get("add_blocks")),
- "task_list": lambda **kw: TASK_MGR.list_all(),
- "spawn_teammate": lambda **kw: TEAM.spawn(kw["name"], kw["role"], kw["prompt"]),
- "list_teammates": lambda **kw: TEAM.list_all(),
- "send_message": lambda **kw: BUS.send("lead", kw["to"], kw["content"], kw.get("msg_type", "message")),
- "read_inbox": lambda **kw: json.dumps(BUS.read_inbox("lead"), indent=2),
- "broadcast": lambda **kw: BUS.broadcast("lead", kw["content"], TEAM.member_names()),
+ "task_create": lambda **kw: TASK_MGR.create(
+ kw["subject"], kw.get("description", "")
+ ),
+ "task_get": lambda **kw: TASK_MGR.get(kw["task_id"]),
+ "task_update": lambda **kw: TASK_MGR.update(
+ kw["task_id"], kw.get("status"), kw.get("add_blocked_by"), kw.get("add_blocks")
+ ),
+ "task_list": lambda **kw: TASK_MGR.list_all(),
+ "spawn_teammate": lambda **kw: TEAM.spawn(kw["name"], kw["role"], kw["prompt"]),
+ "list_teammates": lambda **kw: TEAM.list_all(),
+ "send_message": lambda **kw: BUS.send(
+ "lead", kw["to"], kw["content"], kw.get("msg_type", "message")
+ ),
+ "read_inbox": lambda **kw: json.dumps(BUS.read_inbox("lead"), indent=2),
+ "broadcast": lambda **kw: BUS.broadcast("lead", kw["content"], TEAM.member_names()),
"shutdown_request": lambda **kw: handle_shutdown_request(kw["teammate"]),
- "plan_approval": lambda **kw: handle_plan_review(kw["request_id"], kw["approve"], kw.get("feedback", "")),
- "idle": lambda **kw: "Lead does not idle.",
- "claim_task": lambda **kw: TASK_MGR.claim(kw["task_id"], "lead"),
+ "plan_approval": lambda **kw: handle_plan_review(
+ kw["request_id"], kw["approve"], kw.get("feedback", "")
+ ),
+ "idle": lambda **kw: "Lead does not idle.",
+ "claim_task": lambda **kw: TASK_MGR.claim(kw["task_id"], "lead"),
}
TOOLS = [
- {"name": "bash", "description": "Run a shell command.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
- {"name": "read_file", "description": "Read file contents.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}}, "required": ["path"]}},
- {"name": "write_file", "description": "Write content to file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
- {"name": "edit_file", "description": "Replace exact text in file.",
- "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
- {"name": "TodoWrite", "description": "Update task tracking list.",
- "input_schema": {"type": "object", "properties": {"items": {"type": "array", "items": {"type": "object", "properties": {"content": {"type": "string"}, "status": {"type": "string", "enum": ["pending", "in_progress", "completed"]}, "activeForm": {"type": "string"}}, "required": ["content", "status", "activeForm"]}}}, "required": ["items"]}},
- {"name": "task", "description": "Spawn a subagent for isolated exploration or work.",
- "input_schema": {"type": "object", "properties": {"prompt": {"type": "string"}, "agent_type": {"type": "string", "enum": ["Explore", "general-purpose"]}}, "required": ["prompt"]}},
- {"name": "load_skill", "description": "Load specialized knowledge by name.",
- "input_schema": {"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}},
- {"name": "compress", "description": "Manually compress conversation context.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "background_run", "description": "Run command in background thread.",
- "input_schema": {"type": "object", "properties": {"command": {"type": "string"}, "timeout": {"type": "integer"}}, "required": ["command"]}},
- {"name": "check_background", "description": "Check background task status.",
- "input_schema": {"type": "object", "properties": {"task_id": {"type": "string"}}}},
- {"name": "task_create", "description": "Create a persistent file task.",
- "input_schema": {"type": "object", "properties": {"subject": {"type": "string"}, "description": {"type": "string"}}, "required": ["subject"]}},
- {"name": "task_get", "description": "Get task details by ID.",
- "input_schema": {"type": "object", "properties": {"task_id": {"type": "integer"}}, "required": ["task_id"]}},
- {"name": "task_update", "description": "Update task status or dependencies.",
- "input_schema": {"type": "object", "properties": {"task_id": {"type": "integer"}, "status": {"type": "string", "enum": ["pending", "in_progress", "completed", "deleted"]}, "add_blocked_by": {"type": "array", "items": {"type": "integer"}}, "add_blocks": {"type": "array", "items": {"type": "integer"}}}, "required": ["task_id"]}},
- {"name": "task_list", "description": "List all tasks.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "spawn_teammate", "description": "Spawn a persistent autonomous teammate.",
- "input_schema": {"type": "object", "properties": {"name": {"type": "string"}, "role": {"type": "string"}, "prompt": {"type": "string"}}, "required": ["name", "role", "prompt"]}},
- {"name": "list_teammates", "description": "List all teammates.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "send_message", "description": "Send a message to a teammate.",
- "input_schema": {"type": "object", "properties": {"to": {"type": "string"}, "content": {"type": "string"}, "msg_type": {"type": "string", "enum": list(VALID_MSG_TYPES)}}, "required": ["to", "content"]}},
- {"name": "read_inbox", "description": "Read and drain the lead's inbox.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "broadcast", "description": "Send message to all teammates.",
- "input_schema": {"type": "object", "properties": {"content": {"type": "string"}}, "required": ["content"]}},
- {"name": "shutdown_request", "description": "Request a teammate to shut down.",
- "input_schema": {"type": "object", "properties": {"teammate": {"type": "string"}}, "required": ["teammate"]}},
- {"name": "plan_approval", "description": "Approve or reject a teammate's plan.",
- "input_schema": {"type": "object", "properties": {"request_id": {"type": "string"}, "approve": {"type": "boolean"}, "feedback": {"type": "string"}}, "required": ["request_id", "approve"]}},
- {"name": "idle", "description": "Enter idle state.",
- "input_schema": {"type": "object", "properties": {}}},
- {"name": "claim_task", "description": "Claim a task from the board.",
- "input_schema": {"type": "object", "properties": {"task_id": {"type": "integer"}}, "required": ["task_id"]}},
+ {
+ "name": "bash",
+ "description": "Run a shell command.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"command": {"type": "string"}},
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "read_file",
+ "description": "Read file contents.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}},
+ "required": ["path"],
+ },
+ },
+ {
+ "name": "write_file",
+ "description": "Write content to file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"path": {"type": "string"}, "content": {"type": "string"}},
+ "required": ["path", "content"],
+ },
+ },
+ {
+ "name": "edit_file",
+ "description": "Replace exact text in file.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "path": {"type": "string"},
+ "old_text": {"type": "string"},
+ "new_text": {"type": "string"},
+ },
+ "required": ["path", "old_text", "new_text"],
+ },
+ },
+ {
+ "name": "TodoWrite",
+ "description": "Update task tracking list.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "content": {"type": "string"},
+ "status": {
+ "type": "string",
+ "enum": ["pending", "in_progress", "completed"],
+ },
+ "activeForm": {"type": "string"},
+ },
+ "required": ["content", "status", "activeForm"],
+ },
+ }
+ },
+ "required": ["items"],
+ },
+ },
+ {
+ "name": "task",
+ "description": "Spawn a subagent for isolated exploration or work.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "prompt": {"type": "string"},
+ "agent_type": {
+ "type": "string",
+ "enum": ["Explore", "general-purpose"],
+ },
+ },
+ "required": ["prompt"],
+ },
+ },
+ {
+ "name": "load_skill",
+ "description": "Load specialized knowledge by name.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"name": {"type": "string"}},
+ "required": ["name"],
+ },
+ },
+ {
+ "name": "compress",
+ "description": "Manually compress conversation context.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "background_run",
+ "description": "Run command in background thread.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "command": {"type": "string"},
+ "timeout": {"type": "integer"},
+ },
+ "required": ["command"],
+ },
+ },
+ {
+ "name": "check_background",
+ "description": "Check background task status.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"task_id": {"type": "string"}},
+ },
+ },
+ {
+ "name": "task_create",
+ "description": "Create a persistent file task.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "subject": {"type": "string"},
+ "description": {"type": "string"},
+ },
+ "required": ["subject"],
+ },
+ },
+ {
+ "name": "task_get",
+ "description": "Get task details by ID.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"task_id": {"type": "integer"}},
+ "required": ["task_id"],
+ },
+ },
+ {
+ "name": "task_update",
+ "description": "Update task status or dependencies.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "task_id": {"type": "integer"},
+ "status": {
+ "type": "string",
+ "enum": ["pending", "in_progress", "completed", "deleted"],
+ },
+ "add_blocked_by": {"type": "array", "items": {"type": "integer"}},
+ "add_blocks": {"type": "array", "items": {"type": "integer"}},
+ },
+ "required": ["task_id"],
+ },
+ },
+ {
+ "name": "task_list",
+ "description": "List all tasks.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "spawn_teammate",
+ "description": "Spawn a persistent autonomous teammate.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "role": {"type": "string"},
+ "prompt": {"type": "string"},
+ },
+ "required": ["name", "role", "prompt"],
+ },
+ },
+ {
+ "name": "list_teammates",
+ "description": "List all teammates.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "send_message",
+ "description": "Send a message to a teammate.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "to": {"type": "string"},
+ "content": {"type": "string"},
+ "msg_type": {"type": "string", "enum": list(VALID_MSG_TYPES)},
+ },
+ "required": ["to", "content"],
+ },
+ },
+ {
+ "name": "read_inbox",
+ "description": "Read and drain the lead's inbox.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "broadcast",
+ "description": "Send message to all teammates.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"content": {"type": "string"}},
+ "required": ["content"],
+ },
+ },
+ {
+ "name": "shutdown_request",
+ "description": "Request a teammate to shut down.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"teammate": {"type": "string"}},
+ "required": ["teammate"],
+ },
+ },
+ {
+ "name": "plan_approval",
+ "description": "Approve or reject a teammate's plan.",
+ "input_schema": {
+ "type": "object",
+ "properties": {
+ "request_id": {"type": "string"},
+ "approve": {"type": "boolean"},
+ "feedback": {"type": "string"},
+ },
+ "required": ["request_id", "approve"],
+ },
+ },
+ {
+ "name": "idle",
+ "description": "Enter idle state.",
+ "input_schema": {"type": "object", "properties": {}},
+ },
+ {
+ "name": "claim_task",
+ "description": "Claim a task from the board.",
+ "input_schema": {
+ "type": "object",
+ "properties": {"task_id": {"type": "integer"}},
+ "required": ["task_id"],
+ },
+ },
]
@@ -662,18 +1127,35 @@ def agent_loop(messages: list):
# s08: drain background notifications
notifs = BG.drain()
if notifs:
- txt = "\n".join(f"[bg:{n['task_id']}] {n['status']}: {n['result']}" for n in notifs)
- messages.append({"role": "user", "content": f"\n{txt}\n"})
- messages.append({"role": "assistant", "content": "Noted background results."})
+ txt = "\n".join(
+ f"[bg:{n['task_id']}] {n['status']}: {n['result']}" for n in notifs
+ )
+ messages.append(
+ {
+ "role": "user",
+ "content": f"\n{txt}\n",
+ }
+ )
+ messages.append(
+ {"role": "assistant", "content": "Noted background results."}
+ )
# s10: check lead inbox
inbox = BUS.read_inbox("lead")
if inbox:
- messages.append({"role": "user", "content": f"{json.dumps(inbox, indent=2)}"})
+ messages.append(
+ {
+ "role": "user",
+ "content": f"{json.dumps(inbox, indent=2)}",
+ }
+ )
messages.append({"role": "assistant", "content": "Noted inbox messages."})
# LLM call
response = client.messages.create(
- model=MODEL, system=SYSTEM, messages=messages,
- tools=TOOLS, max_tokens=8000,
+ model=MODEL,
+ system=SYSTEM,
+ messages=messages,
+ tools=TOOLS,
+ max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
@@ -688,17 +1170,29 @@ def agent_loop(messages: list):
manual_compress = True
handler = TOOL_HANDLERS.get(block.name)
try:
- output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
+ output = (
+ handler(**block.input)
+ if handler
+ else f"Unknown tool: {block.name}"
+ )
except Exception as e:
output = f"Error: {e}"
print(f"> {block.name}: {str(output)[:200]}")
- results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(output)})
+ results.append(
+ {
+ "type": "tool_result",
+ "tool_use_id": block.id,
+ "content": str(output),
+ }
+ )
if block.name == "TodoWrite":
used_todo = True
# s03: nag reminder (only when todo workflow is active)
rounds_without_todo = 0 if used_todo else rounds_without_todo + 1
if TODO.has_open_items() and rounds_without_todo >= 3:
- results.insert(0, {"type": "text", "text": "Update your todos."})
+ results.insert(
+ 0, {"type": "text", "text": "Update your todos."}
+ )
messages.append({"role": "user", "content": results})
# s06: manual compress
if manual_compress: