You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(widget+voice): carry, run & persist chat session-state across CHAT↔VOICE
Three related correctness fixes so a widget CHAT↔VOICE flip keeps the session's
reducer-built state (cart_id, checkout_id, client-pushed facts) instead of
silently dropping it. Before this, voice had ZERO state-reducer /
tool_arg_injection code, so a template's state config worked in chat and
silently no-op'd on the actual voice call.
1. CARRY (CHAT→VOICE seed). The widget voice resume seed carried prior_history
+ start_node but NOT agent_session_state. Read it into the seed
(widget/handlers.py) and merge into the voice runtime's template_vars
(only_if_missing) so chat-built identifiers thread through voice's existing
{placeholder} resolution. The voice agent now holds self.agent_state, seeded
from the resume.
2. RUN (SessionStatePolicy on voice). inject_tool_args runs before each voice
tool and apply_state_reducers after it, reading/writing bot.agent_state —
the same pure engines chat runs in _cycle_loop — at BOTH voice tool seams:
global functions (_make_global_wrapper) and MCP tools (the mcp loader's
_state_wrap_mcp_handler, applied outside the approval gate). Gated by a new
handles_state_externally flag: chat sets it (wrapper skips, chat does it
itself — no double-application); voice doesn't. The two voice-state helpers
live in session_state.py next to the pure engines so both seams share them.
No-op for templates with no state config.
3. PERSIST (VOICE→CHAT drain). end_conversation writes the voice-accumulated
agent_state back to the chat_session, so a later chat turn sees what voice
changed — completing the round-trip.
The principled completion of the agent_state seed-fix: voice now READS and
UPDATES the shared state via the chat engines, for global functions AND MCP
tools, with no new state machinery.
Tests: tests/test_voice_session_state.py (global-function hook + gating) and
tests/test_mcp_approval.py (MCP state wrap). pyrefly 0 errors; full suite 452
passed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0 commit comments