feat(voice,host,panel): calls are on agents
PR 5b of the Unified agents rollout (STAG-322). It builds on 5a (#4246) and 2c (#4260), both merged.
This is the PR that changes what the developer sees: a call is about an agent, not a task. Any agent's tab can take a call, and "+ → New voice chat" starts a blank agent and calls it. The fixed Call tab and the client's task-keyed call path are gone. There is no backwards compatibility: kept tab ids, Call pins, and /call links from before are dropped.
What changed
Voice
LiveVoiceOrchestratorkeys its one call by{ taskId, agentId }(VoiceCallAbout).talkAboutTaskbecomestalkTo(about). Calling another agent hangs up the call that stands first.VoiceView.callTaskIdbecomesVoiceView.call, andisVoiceViewreads it.- The renderer's
createSessionpasses the agent on. The session holder and source putagentIdon the service'ssession.createframe.
Host and main
voice.createLiveSessionnow requiresagentIdand forwards it.- Before creating a session, the host reads the agent once. It refuses an agent on another task, or one that is archived or out of reach. This adds one read to call-open latency, and the service checks again.
TASKS_TALKandPUSH.TASKS_TALKcarry the agent.- New
codingAgents.callsact, which reads 5a'scalls(agentId). call.showTaskbecomescall.showAgent.
The panel
- Call button. An agent's composer has a Call button beside Dictate, on desktop only (
HOST_CAPABILITY.VOICE). - During a call. The tab's foot draws the avatar video, the captions, and the
CallBar, and the tab's glyph wears the live mark. - Calls in the transcript. Each call's divider and end line sit around that call's spoken rows (
callPlaces, from 5a's calls read). Lines heard on the live call that aren't on record yet grow at the bottom. - "+" menu. It offers New voice chat, New agent, then each agent. Start's menu offers New voice chat at its foot.
- No Call tab. Deleted:
MAIN_TAB.CALL,TaskCall,LINK_PLACE.CALL,TAB_WORDS.CALL, Call pins,transcriptStreaming, and the Call revision sources. - A task with no agent tab opens on its plan. The side panel opens on Plan, and the column shows an empty pane offering New voice chat and New agent.
- Header. Start and the task's ⋯ stand in the task's header on every tab.
- Following an ended agent (the #4246 review point). Main's pushed-transcript follow (
codingAgents.transcript) already keeps an ended agent followed while its tab is shown, so a call's spoken rows reach it. A tab shown again reads from the start. A test now covers this. - Settings. Settings › Coding agents is now Settings › Agents.
Decisions from Dean, passed on by the orchestrator
-
The global keys are removed. The talk key (⌥Space) and the stop key (⌥S) are deleted, along with:
- their registrar and the
mac-talk-keyhelper; - the Settings › Voice "Keys" section and their stored settings (the retired ids are kept);
- the hotkey pushes and the app-state slice;
setShortcutCapturing,LUKE_STOP, and the orchestrator'sbeginTalk/endTalk.
The in-call mute and speaker chords and Escape are unchanged.
RunMode.registersGlobalKeysgives way totakesFocusfor the voice window. Video calls lose the avatar barge-in flush, which only the held talk key fed. Re-keying it on mic activity is a possible follow-up. - their registrar and the
-
Plan mode is removed from New task. Every start sends
planning: false, and starting a task never opens a call.
Docs: docs/TASKS.md (calls, agent tabs, transcript, call bar, New task), docs/DESIGN.md (keys section removed), PRIVACY.md, README.md, and CONTRIBUTING.md.
Not in this PR
- Off-call
show_codein the Code tab. An agent'sshow_codereaches the Code tab only during a call, through the call'stask.codeframes. Drawing it from the agent's record, coloured by the host's highlighter, will be its own PR. The Code feed now heads each code by the turn that showed it, rather than grouping code under calls. - Typing into a live call is PR 6.
- Deleting the per-task Luke and the service's task call path is PR 7.
Tests
- Orchestrator: with two agents, calling the second hangs up the first, and the view names the agent.
- Host: an offer into another task's agent, or into an archived agent, is refused and opens no session. The frame names the agent.
- Tab model: there is no Call tab, and a task opens on its plan. Closing the last tab goes back to the plan, and a kept
"call"tab is dropped. - New voice chat: starts a blank agent, opens its tab, and calls it. The web offers none.
- Second call: pressing Call while another agent's call stands asks first.
- Call button: it stands beside Dictate on desktop and is absent on the web. Mid-call, the call bar and live mark show.
- Transcript follow: an ended agent stays followed while its tab is shown, and spoken rows pushed after it ended join the transcript.
callPlaces: places the dividers and the heard tail.
Locally:
tscis clean for voice, host, ui, panel, settings, features, gateway, service, desktop, and web.biomeis clean at error level, and the discarded-effect check passes.- vitest passes for voice, host, ui, settings, features, gateway, desktop, and panel (543 files, 4786 tests).
- knip reports only
ChatGptSaveandChatGptSignInRequestinpackages/credentials, which come from main's #4243 and aren't touched here.
Evidence
Screenshots follow in a comment. They come from a throwaway harness that draws DesktopTasks over the panel's test fixtures in Chromium. The red "No act port" line in the side panel is the harness's own.
Left as is: the changelog-screenshots skill's staging shot for the old Call tab release (.agents/skills/changelog-screenshots/staging/shots/call.ts), which is applied only to that release's own build.
Made with Luke