feat(web): one delegation: delegate is a resumable task in both roots, the coder choosing to wait and the planner always in the background (one runtime 5b/n)
Merged
DA
6d ago+510+510+012345678901234567890123456789-103-103-012345678901234567890123456789
One-agent-runtime migration, PR 5b (docs/proposals/one-agent-runtime.md §8, Decision 3, option A as Dean picked it). Builds on 5a (#2387, merged), whose worker rows read the worker's own session.
What
One delegation mechanism for both roots: delegate, defined as an eve resumable task (serve, the shape eve gives every agent call).
Coding agent (coder/tools/delegate.ts):
- Input is
{task, model?, effort?, background?}. - The call answers with eve's receipt at once, and the worker's
{model, effort, result}reaches the agent as a<task_result>. - Foreground by default: unless the agent sets
background, the tool's words and the coder prompt have it calltask_waitright after delegating. Settingbackground: truekeeps it working while the worker runs. - A developer's steer no longer kills a running worker: a task is never interrupted by a new message, where the old blocking
executeaborted on one. - A call with the task's id sends the worker more, into the same session, whose child conversation stays the starting call's.
- The pair is still checked and recorded in a step as the task starts.
Planning model (eve/tools/delegate.ts, PLANNER_DELEGATE_TOOL):
- Input is
{task}only. It always runs in the background, because a foreground wait would hold the spoken turn silent. - Its worker is declared hidden (
tool: false), as the coder's is, so the model reaches it only throughdelegate. - The prompt now names
delegate, and "call delegate again with its taskId" to redirect a worker.
Wiring:
- Registry:
delegateserves[PLANNER, CODER]. - Planning stored set: registers the planner's
delegate. eve'sworker,task_waitandtask_cancelstay registered for earlier transcripts. - Old names keep reading:
PLANNER_DELEGATION_TOOLSnames bothdelegateand the legacyworker. The voice's follow of a parked turn (delegatedOf), the steered-worker journal reads (plannerDelegationJournalsOf, shared by the Work store and the voice) and the Work tab projection all read both. - Work tab projection: shows a
delegatecall's{task}as the worker'smessage, so every desktop reads it unchanged.
Tests
tests/voice-live-brain.test.ts, new: a turn parked on a task itsdelegatestarted is followed past the plain bound, and the findings that land after it are spoken. It fails without this PR, where the follow ends at the plain bound.tests/plan-work.test.ts, new: the planner'sdelegatereaches the Work tab as a worker, its task carried as the message and its task named off eve's receipt.- Offline evals (
test:agent, real eve runtime, local Postgres):- The coder delegates, waits with
task_wait, and answers with the worker's result from the task result. The call's output is eve's receipt. - The planner delegates in the background and its turn waits for the task's result before it ends.
- The coder delegates, waits with
- Updated:
hosted-brain-host-agent.test.ts(the stored set names the planner'sdelegate), and the goldens (codingAgentDelegationInputSchemagains optionalbackground, re-recorded withLUKE_UPDATE_FIXTURES=1). - Suites: the web suite (157 files) passes;
eve-layoutbuilds both roots. Lint and repository checks pass.
This PR touches no renderer file. 5a already reads a worker row's state from its session, so rows are right once the call settles at the receipt.
🤖 Generated with Claude Code