delegate_task)Voicebox supports delegating long-running engineering tasks to external coding agents (delegate_task, task_status, cancel_task) with cryptographic owner binding, root pinning, and private audit persistence (core/tasks.ts, lib/tasks.mjs, lib/task-placement.mjs).
Tasks can be delegated from a local authenticated session (carrying the host token, loopback session cookie, or same-origin local browser entitlement) or across paired environments via POST /api/call → POST /api/execute.
// Delegate a new task
{ "agent": "pi", "task": "Refactor the parser and run unit tests", "context": [] }
// Check task status or cancel
{ "address": "<sealed-task-address>" }
x-voicebox-call-id): Reusing the same x-voicebox-call-id for the same owner and workspace root returns the existing task record without re-dispatching. Supplying different task parameters under an existing call ID is refused with task-call-id-conflict.context arrays are refused with task-context-unavailable unless supported by the target host.task-owner-unverified.Every admitted task is pinned to four immutable attributes in lib/tasks.mjs:
SELF_ENVIRONMENT): Bound to the host’s .environment-key identity.task-owner-mismatch).core/root.ts), or to portable opfs / handle roots in browser placement (lib/task-placement.mjs, 16-zero-server-delegation.md). Switching or replacing the workspace directory later refuses readback with task-root-replaced or task-root-unavailable..host-token invalidates previously minted addresses.<root>/.audit/*.jsonl)Task events (which include the task prompt) are persisted in the active root’s .audit/*.jsonl file with 0600 permissions. Task records are filtered out of public GET /api/audit responses and are inaccessible to workspace file tools (protected-audit).
task-input-over-budget), and verifies that a task executor is installed via installTaskExecutor() (executor-unavailable if no harness is active).queued event is appended and flushed to disk before the task handle is returned. Execution starts asynchronously on a subsequent event-loop turn only after a running event is persisted.task-capacity-exhausted, task-output-over-budget).queued or running, subsequent readback checks whether the original PID is still alive (ESRCH). Once confirmed absent, the task transitions to interrupted (lib/task-interrupted.mjs). Unfinished prompts are never automatically re-executed after a crash.task_view includes:
closurePolicy: { onVoiceDisconnect: "continue", onEnvironmentClose: "continue-until-host-exit" | "interrupt", reconciliation }delivery: { mode: "surface-notification", surfaceUpdated: true, modelReceived: false } — task updates push { type: "task", task, delivery } frames to update the browser task card in real time without injecting unsolicited turns into the live voice stream.Terminal task records carry an explicit outcome classification (tests/task-outcome.test.mjs) that distinguishes executor claims from host-observed terminations:
| Terminal State | Outcome Class | Basis | Meaning |
|---|---|---|---|
completed |
claimed-complete |
executor-claimed |
The coding agent returned a response within its time and output bounds. |
failed |
observed-failure |
host-observed |
The adapter or subprocess exited with an error. |
interrupted |
observed-interruption |
host-observed |
Execution hit its deadline or the host process terminated before completion. |
cancelled |
observed-cancellation |
host-observed |
The task was cancelled via cancel_task and confirmed stopped by the adapter. |
(Non-terminal states such as running or cancel_unconfirmed carry no outcome classification.)
When a task fails or is interrupted after partial execution output has been returned by the executor or attached to a thrown failure (err.partial), the partial output is recorded in partial rather than discarded (lib/tasks.mjs). Terminal partial text is scrubbed through redactSecrets() at the durable settle() boundary before being written to .audit or served in task views.
node --test --test-concurrency=1 tests/tasks.test.mjs tests/tasks-http.test.mjs tests/tasks-browser.test.mjs tests/task-outcome.test.mjs