Originally created by: kumaakh
When a member hits a verify checkpoint or becomes blocked, there is no way for the PM to communicate across sessions. The PM must manually poll `progress.json`.
Don't try to inject into a running session. Instead, have the orchestrator write updates to a known file (`TASK_UPDATES.md`) and include in the original dispatch prompt: "Before each major step, check TASK_UPDATES.md for any new instructions."
The agent reads that file as part of its agentic loop since it was instructed to. Low-tech but surprisingly effective — poll by instruction rather than by infrastructure. Works equally for Claude and Gemini members.
PM writes to `TASK_UPDATES.md` via `send_files` or `execute_command` — no session interrupt needed.
A cleaner solution is to add an `update` flag to `execute_prompt`:
This gives PM a single call to "reach" a member regardless of whether they're mid-task or at a checkpoint — no polling, no separate resume logic.
The file-polling pattern above serves as the immediate solution and remains a useful low-overhead fallback once native interject lands.
Backlog item [#14] from docs/MCP-BACKLOG.md. High priority.
Originally posted by: kumaakh
From my research:
Given your PM → doer/reviewer architecture, the cleanest pattern might be: don't try to inject into a running session. Instead, have your orchestrator write updates to a known file (like TASK_UPDATES.md) and include in the original prompt: "Before each major step, check TASK_UPDATES.md for any new instructions." Claude will cat that file as part of its agentic loop since you told it to. This is low-tech but surprisingly effective — you're essentially making the agent poll by instruction rather than by infrastructure.
Originally posted by: kumaakh
PM Skill Integration Pattern
Given the PM → doer/reviewer architecture, the cleanest pattern might be: don't try to inject into a running session. Instead, have the orchestrator write updates to a known file (e.g.
TASK_UPDATES.md) and include in the original dispatch prompt: "Before each major step, check TASK_UPDATES.md for any new instructions."Claude will read that file as part of its agentic loop since it was instructed to. This is low-tech but surprisingly effective — you're essentially making the agent poll by instruction rather than by infrastructure.
How this fits the PM skill
TASK_UPDATES.mdin the member's work folder viasend_filesorexecute_commandSuggested addition to doer context file template
Originally posted by: kumaakh
Technical direction: The file-polling pattern described in the comments is already the recommended immediate approach and requires no infrastructure changes. The native interject path is the right next step:
Add an update flag to execute_prompt (src/tools/execute-prompt.ts):
esume: true on the existing session ID — continuity preserved.
The key design question is whether Claude CLI supports injecting into a currently running session (mid-turn). If not, the file-polling approach remains the only real-time path. Worth checking claude --help for any --inject or --append flag before implementing.
Originally posted by: kumaakh
Research: Inter-session attention mechanism — Feasibility findings
1. Claude Code CLI capability: Can you inject input into a running
claude -psession?No.
claude -p(print mode) is a fire-and-forget invocation. It reads a prompt, runs to completion (up to--max-turns), and exits. There is no stdin channel for injecting additional user turns mid-session. Key observations:claude -pcloses stdin immediately after reading the prompt. The fleet codebase confirms this:LocalStrategy.execCommandcallschild.stdin?.end()right after spawn (src/services/strategy.ts:96).--resume/-cresumes a completed session — it starts a new process that loads the prior conversation as context, then appends a new user turn. It does not attach to a running process.--session-id <uuid>sets the session ID for a new invocation; it doesn't connect to a live one.--input-format stream-jsonallows streaming input, but only before the agent starts running — it's for piping structured input at invocation time, not for mid-turn injection.Verdict: Claude Code CLI has no mechanism for injecting a message into a running
-psession from an external process.2. Current
execute_promptresume path in apra-fleetReading
src/tools/execute-prompt.tsandsrc/providers/claude.ts:sessionIdon each agent after a successful prompt execution (touchAgent(agent.id, parsed.sessionId)at line 165).execute_promptcall withresume: true, it appends-cto theclaude -pcommand (Claude provider'sresumeFlag()). This resumes the prior conversation context in a new process..fleet-task.mdin the work folder, then cleaned up after execution.Key insight: Resume today is strictly sequential — it only works after the previous session has exited. There is no "resume into a running session" path.
Could "resume with a NEW prompt" serve as the inject mechanism? Only if the current session has finished. You cannot resume a session that is still running — the CLI would either fail or start a parallel conversation with the same history. So resume is the right mechanism for follow-up messages, but not for mid-flight steering.
3. File polling viability (TASK_UPDATES.md approach)
How it would work:
TASK_UPDATES.md) in the member's work folder.Readtool call (or the prompt could instruct periodic checking).Risks and limitations:
Mitigation: Use a sentinel file (e.g.,
.fleet-attention) that the member's prompt says to check after every tool call. But this is still best-effort.4. Recommendation
Approach B (resume with new prompt) is simpler and more reliable than file polling — but with a critical caveat.
The realistic interaction model is:
-cto resume with new context → member runs again.This is already how fleet works today. The "attention mechanism" reduces to: can the PM interrupt a running session to redirect it?
For true mid-flight injection, neither approach works without upstream Claude Code changes. But there's a pragmatic middle ground:
Recommended minimal API change:
cancel_and_redirectoption toexecute_promptthat:-cand the new steering promptEffort: Small — the PID tracking and kill infrastructure already exists. The resume path already works. The main work is a new tool parameter and the orchestration logic.
File polling (Approach A) should be a Phase 2 enhancement for cases where the PM wants to augment a running session without interrupting it (e.g., "also check the tests when you're done"). It's lower priority because the timing guarantees are weak.
Originally posted by: kumaakh
Research: Inter-session attention —
stream-jsonas a potential injection pathBuilding on the prior feasibility research, I investigated one under-explored avenue: Claude Code's
--input-format stream-jsonmode.Key finding:
--input-format stream-jsonmay support mid-session user-turn injectionFrom
claude --help:The description says "realtime streaming input" — not "initial structured input." And
--replay-user-messagesexplicitly references "user messages from stdin," implying multiple messages can be sent over the lifetime of the process.This suggests that
claude -p --input-format stream-json --output-format stream-jsonkeeps stdin open and accepts additional user messages while the agent is running. If confirmed, this is exactly the injection mechanism fleet needs — no polling, no kill-and-resume, no upstream changes required.How fleet currently blocks this path
Both execution strategies close stdin immediately:
src/services/strategy.ts:95):child.stdin?.end()right after spawnsrc/services/ssh.ts:143-144):stream.end()immediately, with the comment "Close stdin so commands that read from it (e.g. claude -p) get EOF"If we kept stdin open and switched to
stream-jsoninput/output format, we could potentially write additional user turns to the running process.What an
updateflag implementation could look like.fleet-task.md, invokeclaude -p, etc.)--input-format stream-json:-cand the new prompt (existing path)Architecture changes needed
child.stdin?.end()orstream.end()at spawn time. Instead, store a reference to the writable stream.--input-format stream-json --output-format stream-jsoninstead of--output-format json.Map<agentId, { stdin: Writable, sessionId: string }>soexecute_promptwithupdate: truecan write to the correct stdin.parseResponsewould need a streaming variant.Caveats and risks
stream-jsoninput behavior needs testing. It's possible "realtime streaming input" only means streaming the initial prompt in chunks, not injecting new turns during autonomous execution. The--replay-user-messagesflag is strong evidence for multi-turn support, but testing is essential.ChildProcess.stdinreference). Remote members via SSH require more careful engineering.Updated recommendation
kill → -c)Recommended next step: Spike a manual test of
--input-format stream-jsonto confirm multi-turn stdin injection works:If confirmed, implement stream-json injection for local members first (Phase 1), then extend to SSH (Phase 2). The cancel-and-resume approach remains the reliable fallback regardless.