Subagent yield handoff

The subagent registry owns completion across sessions_yield. A yielded execution ends; the delegated task and its completion audience remain. The registry's requester settlement batch starts a successor turn after the children settle. Gateway admission attaches that successor to a paused subagent when necessary, preserving its original requester.

This design applies equally to an orchestrator spawned from an interactive session and one spawned from an isolated cron run. Cron owns delivery of the scheduled result, while the registry owns the nested orchestrator's continuation.

Ownership through the handoff

Phase Owner Required handoff
Executing requester Admitted agent turn Children identify the spawning turn with requesterTurnRunId. Progress callbacks use this turn's live authority.
Explicit yield Registry requester-yield settlement Persist yield intent, freeze the child run IDs, advance the batch generation, and clear the old requester-turn binding.
Waiting for children Registry lifecycle and requesterSettleWake Retain captured completion results and schedule the owed batch. Individual announcements must not start a competing continuation.
Settlement dispatch Requester-settle wake delivery Validate the current batch and Gateway owner, dispatch an idempotent internal continuation for a nested requester, and record its outcome.
Successor admission Gateway task tracking and paused-run adoption Continue the paused task under the newly admitted run ID, preserving requester lineage and its outstanding settlement obligation.
Successor completion Registry completion delivery Deliver the orchestrator's result to its original requester. Cron's existing continuation and delivery policy own the scheduled output.

The implementation owners are subagent-registry-requester-yield.ts, subagent-announce.requester-settle-wake.ts, and agent-task-tracking.ts. adoptPausedSubagentRunForFollowUp uses the existing registry replacement operation; it does not create a second delegated task. Adoption clears a child-only pause notice instead of carrying it into completion. Actual requester completion batches keep their frozen membership and generation.

Before admitting another turn in a yielded requester, the recovery owner transfers its ended execution's fence to the current durable child batch. The batch is revalidated before the write commits; absent or adopted batches, unrelated run fences, and outstanding recovery or delivery work remain fenced. The successor then receives its own execution fence, so completing it leaves later user input admissible without discarding pending child results.

An explicit waitFor: "message" counts as continuation evidence after the registry accepts the wait. The attempt carries that fact into terminal reply presentation, so a registered message wait does not produce a missing-continuation warning. Refused or unregistered waits do not provide that evidence.

Private child results wait for their spawning turn to settle before individual announcement admission. Normal settlement resumes each finished private child, even while siblings are still running. Explicit yield assigns the frozen batch first, then resumes child cleanup under that owner. Late announcement failures cannot replace the batch's delivery state; already committed delivery evidence remains valid. Restart activation reconciles retained requester-turn bindings before resuming child completion.

For a nested requester, settlement persists its paused run together with the child wake batch before scheduling the continuation. This also covers a child that finishes before the requester yields: successor admission must not depend on the later lifecycle-end notification. The successor keeps the same task, and a delayed notification from the predecessor cannot reopen it.

While the requester executes a settle-wake continuation, its own completion batch can still be marked dispatching. The yield tool excludes that exact batch from older pending children: it must not promise another wake for the results already being processed. Unrelated pending batches and newly spawned work keep their existing completion ownership.

Settlement dispatch uses subagent_settle input provenance. Individual announcements and the older descendant-wake path retain subagent_announce: the latter already owns its run replacement after dispatch and must not trigger paused-run adoption at admission. Provenance classifies the handoff; live Gateway admission and registry ownership still authorize it.

An explicit yield batch must be eligible at any requester depth. The ordinary nested-wave exclusion remains: nested runs without a yielded batch use the existing descendant-settle path. The top-level cron exclusion also remains; starting an independent requester-settle turn for the cron session would compete with its scheduler-owned continuation.

Invariants

The registry persistence owner serializes mutations per run, admitting multi-run batches in sorted order. A synchronous plan reads the current immutable published rows after admission and returns replacement rows or deletions. Asynchronous preparation happens first; the plan rechecks execution, cancellation, requester, and cohort ownership before committing. Callbacks retain their observed ownership and wake progress across waits. They advance those observations only from their own acknowledged publications.

The SQLite worker compares row-version digests before writing. A foreign change refreshes the affected rows through the read worker and reruns the plan, up to three attempts. Only acknowledged commits publish resident rows and notify readers. An unknown write outcome fences those rows until canonical restoration. Terminal rows and their eligible session-state events commit together; equivalent duplicate terminal callbacks preserve in-flight cleanup authority. This changes no schema or update format.

Process-local callback ownership survives metadata publication. Recovery under a replacement Gateway acquires a fresh runtime incarnation through the same row owner, so callbacks from the closed Gateway cannot settle the recovered wake.

Progress after yield

Yield closes the old execution, not the delegated work. When the turn would otherwise be silent, the shared reply pipeline can send a waiting acknowledgment. The native subagent registry retains the completion obligation and wakes the requester through its accepted completion path; progress text is not proof that a child finished or that its result was delivered.

When settlement resumes a top-level parent with automatic channel delivery, Apus keeps the channel's typing indicator active while that continuation executes. The indicator starts after execution begins, not while admission is queued, and stops when the call settles, is cancelled, or loses its owner. It respects typingMode: "never", uses the agents.defaults.typingIntervalSeconds refresh cadence, and requires channel typing support. Private and nested continuations do not send activity to an external channel. This activity signal does not change the configured message queue mode or restore individual tool-progress messages.

On Telegram and Discord, a confirmed progress draft can stay with the yielding turn's announcing children instead of the waiting acknowledgment. The channel keeps rendering, throttling and deleting it; the native registry only forwards child status and prepared operation names (never child prose, commands, arguments or results). Public task state remains visible with the detailed tool log disabled; streaming.progress.toolProgress controls the rolling diagnostic rows. A resumed parent that yields again keeps the same draft for its new children. The draft is deleted when the settle wake completes the last tracked cohort (final, NO_REPLY or terminal failure) or when stop or reset cancels the children. It is process-local: a Gateway restart does not revive it. Other channels keep the waiting acknowledgment. A yielded turn still does not start a separate task or flow projection.

Progress cards remain durable session state. The parent updates its own card as work advances and when child results return. Inspect native child status or retained messages through sub-agent commands and session history. Neither a saved card nor a stored message ID authorizes a new execution or delivery.

Cron observes the registry's descendant settlement boundary before starting its bounded synthesis grace period. A yielded task remains pending between the last worker ending and successor admission; the successor and its completion delivery must settle before cron selects the final result. Execution waits, settlement observation, and synthesis share the existing follow-up deadline and stop on cron cancellation. Suspended or permanently failed child delivery retains the registry's terminal semantics, allowing cron's existing fallback policy to resolve the scheduled result without retrying that delivery.

See Subagents for tool behavior and Progress drafts for channel presentation.