feat(voice): Background Sessions — Async Background integrations for AgentSession#6517
feat(voice): Background Sessions — Async Background integrations for AgentSession#6517toubatbrian wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fb30127f68
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ) | ||
| chat_ctx = target_agent.chat_ctx.copy() | ||
| chat_ctx.insert(items_to_insert) | ||
| await target_agent.update_chat_ctx(chat_ctx) |
There was a problem hiding this comment.
Recheck idle after retargeting the reply context
When a pending background/tool update is retargeted after wait_for_idle() has already returned, this awaited update_chat_ctx() can take a realtime-session round trip; if the user starts talking during that await, _deliver_reply() still proceeds to session.generate_reply() without checking idleness again. In realtime handoff cases this can schedule the deferred update over an active user turn, defeating the scheduler's idle-wait contract; re-wait or hold the idle state after any awaited retarget sync before generating.
Useful? React with 👍 / 👎.
Adds examples/voice_agents/deep-research/ showing the new background session API end to end: a voice agent overlay (agent.py) fronting a long-lived, multi-round deep-research pipeline (deep_research.py) that asks clarifying questions, accepts mid-run steering, and writes a dated markdown report per round. The pipeline is a Python port of the bundled deep-research workflow script (deep-research-workflow.js), with all LLM calls using llm.chat() structured tool-call outputs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds background_claude_code.py: a Claude Agent SDK session wrapped in a @background definition — one long-lived interactive Claude Code session per voice call, with user tasks forwarded as follow-up queries and streamed assistant text relayed as voice updates. Also removes the bundled deep-research workflow reference script. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
814730d to
b3161c9
Compare
Overview
Adds background sessions: a reusable abstraction for long-lived background third-party integrations (coding agents, research pipelines, monitors, ...) that communicate asynchronously and bidirectionally with a voice
AgentSession. The voice agent stays the human-facing orchestrator/voice overlay; background work runs session-scoped, survives Agent handoffs, and can never outlive itsAgentSession.Background API
@background(name=..., description=...)returns an immutable, reusableBackgroundDefinition; the description falls back to the docstring like@function_tool.lk_background_send(background_session_id, content), lets the voice LLM route messages by ID; its description deterministically lists every configured session. The name is reserved — conflicting session/Agent tools fail fast.BackgroundContext.send()publishes a user-role update into the conversation, using the same eager context insertion, idle waiting, per-source coalescing, handoff retargeting, and deferredgenerate_reply(tool_choice="none")behavior as deferred async-tool updates. Templates are configurable per definition or per session viaBackgroundHandlingOptions.background_message_updatedevents (BackgroundMessageReceived,BackgroundReplyUpdatedwith scheduled/completed/interrupted/skipped) expose the message lifecycle.AgentSession.start()after the current Agent exists, keep running across handoffs, and are cancelled onaclose().Internal refactor
The deferred-reply machinery (pending buffering, idle wait, handoff retargeting until the target Agent is stable, tail detection, reply scheduling) is extracted from
_ToolExecutorinto a shared_ReplyScheduler, used by both async tools and background runtimes. Existing async-tool behavior, events, and public APIs are unchanged.Examples
examples/voice_agents/background_claude_code.py— the simplest end-to-end usage: a Claude Code (Claude Agent SDK) session as a background session. One long-lived interactiveClaudeSDKClientper voice call; every relayed user message becomes a follow-upclient.query()with full session context retained, and streamed assistant text is forwarded to the voice conversation viactx.send().examples/voice_agents/deep-research/— a fuller voice-fronted deep-research pipeline:agent.py: thin voice overlay that relays clarifying questions/progress and forwards user answers and mid-run steering vialk_background_send.deep_research.py: long-lived, multi-round background session (clarify -> scope -> parallel search -> fetch/extract falsifiable claims -> 3-vote adversarial verify -> synthesize). Writes a dated markdown report per round, then keeps listening for follow-ups with the previous report as context. All LLM calls usellm.chat()with forced tool calls for structured output.Verified end to end over live voice: clarify Q&A round-trips, mid-run steering, report delivery, and follow-up rounds re-using prior context.
Testing
tests/test_background_session.py(new): API validation, routing, FIFO/isolation, template resolution, deferred delivery, handoff retargeting, lifecycle/close races, events, reserved tool names.tests/test_tools.py: scheduler-extraction regressions — existing async-tool semantics (first-update inline, coalescing, call-ID stability, cancellation/drain) are unchanged.