feat(voice): Luke speaks a subagent's result at a pause
Merged
CH
11d ago+436+436+012345678901234567890123456789-10-10-01234567890123456789
Stacked on #1823.
CI couldn't verify the voice behavior. This PR changes what the developer hears, and no CI job runs a live call. It needs a ./scripts/verify.sh pass and a live call on a Mac: ask Luke to research something, keep talking, then check that he answers right away and brings the findings back after his current answer.
What changes
- Brain (
apps/web/server/voice/live-brain.ts). Each socket watches its conversation (store.turns.wokenIn, polled every second) forchild_completionturns from that socket's start onward. It tells each one once as a newLIVE_BRAIN_RUN_EVENT.WOKEN, then follows it under the turn's own id. It reusesprojectTurnEvents,runEventOf, and the follow's cadence and bound. - Service (
live-session-service.ts). A woken run gets an exchange with no delegation, so it speaks session-wide (delegation_id: null).- Its reply waits while any asked run of the session is under way, and is said once that run has ended. The developer's answer is never cut off.
- The developer's next ask silences it, like any older revision, following the GPT Live guide's advice to drop stale results.
- A woken run that fails says nothing, because nobody asked for it.
- Eval (
brain-host.eval.ts). The scripted model learnsResearch: ā¦and hands it to the worker. The eval checks that the planning session is woken in a settledchild_completionturn, whose first line is the brain's.
Known limits
- A re-attached connection doesn't take a woken turn up again. No ask row holds how far it was told.
- The watch reads from the socket's start, so a result that lands while no voice socket is attached is recorded but not spoken.
Tests
live-session-service.test.ts: a woken reply waits for the asked run and is said after it, withdelegation_idnull. The developer's next ask silences a waiting woken reply, and a failed woken run says nothing. The first test fails without the change.voice-live-brain.test.ts: a child-completion turn played through the real relay is told as woken, then its sentences and end, all under the turn's id. It fails without the change.brain-host.eval.ts: needs Postgres, so it wasn't run locally. CI runs it../scripts/check.shpasses.
š¤ Generated with Claude Code