Skip to content

add mid-turn message injection and subagent done notifications#200

Closed
erikswedberg wants to merge 1 commit into
boldsoftware:mainfrom
erikswedberg:mid-turn-messages
Closed

add mid-turn message injection and subagent done notifications#200
erikswedberg wants to merge 1 commit into
boldsoftware:mainfrom
erikswedberg:mid-turn-messages

Conversation

@erikswedberg
Copy link
Copy Markdown
Contributor

  • send messages to agent while it's working (injected between tool calls)
  • parent conversation notified with summary when subagent finishes
  • always use wait:false for subagents (system prompt guideline)

Mid-turn message injection and subagent done notifications

The problem

When the agent is working (executing tool calls), user messages are queued and only delivered after the entire turn ends. This means:

  1. You can't course-correct mid-task. If you realize you forgot to mention something, or want to change direction, your message sits in a queue until the agent finishes its current chain of tool calls — which can be minutes. By then it may have gone down the wrong path.

  2. Subagents force a bad choice: block or forget. With wait: true (the default), the parent sits there stuck until the subagent finishes — serial execution, no parallelism. With wait: false, the parent keeps working, but it never finds out the subagent finished. The results just sit there until the user manually prompts "check on that subagent." Neither option is good: you either waste time blocking, or you waste time chasing down results.

Shelley is async — messages get queued and the UI shows them immediately. But the agent doesn't actually see them until its turn is over. It's async from the user's perspective but synchronous from the agent's.

What this changes

Mid-turn injection: When the user sends a message while the agent is working, instead of appending to pendingMessages for post-turn draining, we inject directly into the loop's messageQueue via QueueUserMessage. The loop already checks this queue between tool calls (in executeToolCalls), so the message gets picked up at the next natural pause — between two tool executions, not after the entire turn. The DB flags (excluded_from_context, queued user_data) are cleared asynchronously so the message appears normally in history.

The fallback path is preserved: if the loop doesn't exist yet or distillation is running, messages still queue the old way.

Subagent done notifications: When a subagent's agentWorking transitions to false, an onDone callback fires. This looks up the parent conversation, grabs the subagent's last assistant response (truncated to 500 chars), and injects a [Subagent 'slug' finished]\n<summary> user message into the parent's loop. The parent sees it immediately — between tool calls — and can act on the results, catch errors, or adjust its plan without waiting for its own turn to end and without the user having to prompt it.

Done notifications make wait: false actually usable. The parent keeps working while subagents run, and gets notified with a summary when each one finishes — so it can react, catch errors, and incorporate results within the same turn. No blocking, no forgetting.

What it feels like

Before: "I'll type this correction... ok it's queued... waiting... waiting... ok the agent finished, now it's reading my message, now it's redoing work."

And for subagents: you either block with wait: true and watch the parent do nothing for minutes, or use wait: false and then spend time prompting "go check on that subagent" and discovering one of them went sideways after the fact.

After: The agent reads your message within seconds (at the next tool boundary) and adjusts course. Subagent results flow into the parent automatically — the parent reacts to a subagent finishing, checks if the output makes sense, and incorporates it into its work, all within the same turn.

Changes

  • server/convo.go: QueueMessage rewritten to inject into active loop when available; onDone callback field added to ConversationManager
  • server/server.go: notifyParentSubagentDone method; onDone wired in getOrCreateSubagentConversationManager
  • AGENTS.md: guideline about preferring wait: false for subagents

What it doesn't change

  • loop.go is untouched — QueueUserMessage and the between-tool-calls check already existed
  • drainPendingMessages is untouched — still used for the distillation/no-loop fallback
  • No UI changes, no DB migrations, no new tools

How to test

Mid-turn injection:

  1. Start a conversation and ask the agent to do something that takes a while (e.g. "read every file in this directory and summarize each one")
  2. While it's working (you'll see "Agent working..."), type a follow-up message like "also ignore any test files"
  3. The message should be picked up between tool calls — the agent adjusts its behavior within seconds, not after the full turn ends

Subagent done notifications:

  1. Ask the agent to spawn subagents with wait: false — e.g. "launch two subagents: one to count the Go files in this repo, one to count the TypeScript files"
  2. Watch the parent keep working after spawning them
  3. When each subagent finishes, a [Subagent 'slug' finished] message appears in the parent's conversation with a summary of the result
  4. The parent reacts to the notification and incorporates the results — no manual "go check on that subagent" prompting needed

Predictable model (no API key needed):

  1. Build and run with --predictable-only
  2. Mid-turn injection can be verified by sending a message while a predictable conversation is "working" — check server logs for Injected user message into active loop
  3. Subagent notifications can be verified by watching logs for Notified parent of subagent completion

- send messages to agent while it's working (injected between tool calls)
- parent conversation notified with summary when subagent finishes
- always use wait:false for subagents (system prompt guideline)

Co-authored-by: Shelley <shelley@exe.dev>
@cla-bot cla-bot Bot added the cla-signed label May 4, 2026
@erikswedberg
Copy link
Copy Markdown
Contributor Author

turns out shelley already has mid turn message injection

@erikswedberg erikswedberg deleted the mid-turn-messages branch May 18, 2026 10:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant