Commit 4546a8

2026-03-19 23:18:24 Claude (MCP): [mcp] Revamp agent lifecycle section with concrete SDK patterns and code sketches
Minsky/Agent_IRC_Architecture.md ..
@@ 154,37 154,65 @@
Two-layer approach:
- **Layer 1 — Supervisor push (primary).** The supervisor watches IRC directly for `TASK:` prefixes and immediately injects a "check your channels" message into the target agent's SDK session. Near-zero dispatch latency.
+ **Layer 1 — Supervisor push (primary).** The supervisor watches IRC directly for `TASK:` prefixes and immediately calls `client.query()` on the target agent's session with a "check your channels" prompt. Near-zero dispatch latency.
- **Layer 2 — Polling (fallback/health).** The supervisor periodically injects heartbeat prompts into idle agents. This catches messages the supervisor missed (e.g., during its own restart) and proves the agent is still alive.
+ **Layer 2 — Polling (fallback/health).** A flat 30-second heartbeat: the supervisor calls `client.query()` on idle agents with a brief check-in prompt. This catches messages the supervisor missed (e.g., during its own restart) and confirms the agent process is alive. Add backoff (30s → 60s → 120s cap) later if idle token costs warrant it.
- MVP polling strategy: flat 30s interval when idle, no polling when active. Add backoff (30s → 60s → 120s cap) later if idle token costs warrant it.
+ #### Response iteration
+
+ Supervisor code that drives an agent turn:
+
+ ```python
+ await agent.client.query(prompt)
+ async for message in agent.client.receive_response():
+ if isinstance(message, AssistantMessage):
+ agent.last_active = datetime.utcnow()
+ elif isinstance(message, ResultMessage):
+ agent.session_id = message.session_id
+ agent.input_tokens_total += message.usage.input_tokens
+ agent.cost_usd_total += message.cost_usd
+ break
+ ```
+
+ Multiple agents run concurrently via `asyncio.gather()` or by scheduling each agent's loop as a separate task.
### Idle detection
- A Haiku-class classifier determines whether an agent is idle. No conversation state needed — just a single SDK `create_message` call:
+ A Haiku-class `ClaudeSDKClient` call (single turn, no session state needed):
- > "Here's the last 5 minutes of this agent's IRC activity. Is it idle? yes/no"
+ ```python
+ async with ClaudeSDKClient(options=haiku_options) as checker:
+ await checker.query(
+ f"Here is the last 5 minutes of IRC activity for agent '{nick}'. "
+ f"Is it idle? Respond with only: yes or no.\n\n{activity}"
+ )
+ async for msg in checker.receive_response():
+ if isinstance(msg, ResultMessage):
+ idle = msg.result.strip().lower() == "yes"
+ ```
- Pennies per evaluation. This keeps the supervisor dumb — it doesn't need to understand task semantics, just whether to send a heartbeat or let the agent work.
+ Pennies per evaluation. The supervisor doesn't need to understand task semantics — just whether to send a heartbeat or let the agent work.
### Context monitoring
- The Claude Code SDK does not expose a "% context used" metric. Each response includes per-turn `usage.input_tokens`. The supervisor accumulates these across turns and compares against the known context window size (200K for Sonnet, 1M for Opus) to estimate fullness.
+ `ResultMessage` provides `usage.input_tokens` per turn and `cost_usd` for budget tracking. The supervisor accumulates `input_tokens` across turns and compares against the known window size (200K for Sonnet, 1M for Opus) to estimate fullness. There is no built-in "% context used" metric — the accumulated token count is the proxy.
- The `result` message also provides `cost_usd`, useful for per-agent budget tracking.
+ Thresholds:
+ - **80% of context window** — warn the agent, suggest wrapping up current subtask before the next one starts.
+ - **90% of context window** — trigger shift-change sequence immediately.
### Context exhaustion and shift-changes
- When an agent's estimated context usage crosses a threshold (e.g., 80% of window):
+ When the accumulated token estimate crosses the shift-change threshold:
- 1. Supervisor tells the agent to produce a handoff summary.
- 2. Agent writes the summary to the project wiki.
- 3. Agent posts a notice to its task channel and `#standup-{slug}` that it's handing off.
- 4. Supervisor kills the session.
- 5. Supervisor spawns a replacement with a new name from the names file and the wiki handoff page as initial context.
+ 1. Supervisor calls `agent.client.query()` with a handoff prompt: "Write a handoff summary to the wiki and post a notice to your channels."
+ 2. Agent writes the summary to the project wiki (via wiki MCP) and posts a shift-change notice to its task channel and `#standup-{slug}`.
+ 3. Supervisor drains `receive_response()` to completion and records the final `session_id` from the `ResultMessage`.
+ 4. Supervisor closes the `ClaudeSDKClient` context (`async with` exits).
+ 5. Supervisor pops a new name from `names.txt`, spawns a fresh `ClaudeSDKClient` with `--append-system-prompt` containing the agent's role, new name, channel assignments, and a pointer to the wiki handoff page.
+ 6. New agent reads the handoff page on its first turn and posts an introduction to its channels.
- This is the "shift change" pattern — natural for an org metaphor. When `Ramona` leaves and `Jules` arrives, everyone in the channel can see the transition.
+ Session resume (`session_id`) is **not** used for shift-changes — a new session with a clean context is the point. Session resume is instead useful for **supervisor restarts**: if the supervisor crashes and restarts, it can reconnect to existing agent sessions by replaying the stored `session_id` values rather than forcing a shift-change on every live agent.
## Git strategy
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9