DSHarness Systems Dissection Pinned baseline 47f943859b · 36 verified / 0 in progress / 36 chapters
中文
Orchestration·Chapter 26

Inbox, Steering, and Continuation

How mid-run input is queued, claimed, injected, or routed to child Agents

VerifiedUpstream 47f943859bScope: Analyze message classes, wake and next-step behavior, claims, steer, follow-up, activation, and turn boundaries.

Conclusion: the Inbox Is a Replayable Scheduling Protocol, Not a Temporary Chat-Input Array

DeepSeek Harness expresses input intent through two ordered Inbox lists: next-turn holds work that should each open a later Turn, while next-step holds steering or context that should enter together at the nearest available Step boundary. followup, steer, and inject are only three fixed combinations of target list and wake behavior; claim, pre-step admission, Turn-closing checks, and cancellation convergence determine when a message actually becomes visible.

The important capability is not merely “send another message while work is running.” Pending input itself enters the Session Event Log, allowing stable MessageIds, insertion, editing, cancellation, claiming, recovery, and UI classification to share one fact. The cost is equally explicit: accepted means the Inbox admitted work, not that it ran, produced a reply, or was necessarily fsynced; running describes whole-driver activity rather than a completion handle for one message.

1. First Separate Six Mechanisms with Similar Names

NameDestinationWakes?Boundary semantics
followupnext-turnYesOrdinary later input; each item gets its own later Turn on the success path
steernext-stepYesTargets the current Turn's next Step when possible; opens a Turn when idle
injectnext-stepNoQuiet model context; remains parked while idle until another wake arrives
Tool continuationTool results are logged; additional context enters next-stepThe current driver continuesA later Step in the same Turn, not a new follow-up
Continuable-child follow-upThe child Agent's next-turnYesAlways a later FIFO Turn for the child; it cannot redirect the current Turn
Child report / settlement noticeThe parent Agent's next-turn or next-stepPolicy-dependentChild-to-parent traffic, entirely separate from parent-to-child follow-up

2. Queue Class Is Not Message Role: Identity, Source, and Scheduling Placement Are Orthogonal

An Inbox item is still the shared immutable UserMessage: it has a stable id, role: user, exact content blocks, and a required source. Plugins may extend MessageSourceMap; freezeMessage structured-clones and deep-freezes, while creation assigns a fresh UUID. INBOX-MESSAGE-IDENTITY

DimensionQuestion it answersExamples
Message roleHow it enters the provider-neutral conversationuser
SourceWho produced it and how the UI should interpret ituser, plugin notice, coordinator relay, subagent report
Inbox targetAt which scheduling boundary it is claimednext-turn / next-step
Web placementWhere pending state rendersqueued / steering / context

A “steering message” is therefore not a third message structure. It is the historical fact that a user-source message was claimed from next-step; a non-user source with the same role is instead interpreted as context by the UI.

3. One Durable Splice Rebuilds Both Lists; Commit Precedes Live Mutation

agent/inbox/spliced {
  target: next-turn | next-step,
  start,
  removedCount?,
  inserted: full UserMessage[],
  outcome?: canceled
}
One event vocabulary

Insertion, editing, removal, claim, and clear all normalize to the same splice. The event carries every full inserted message. Deletion receives outcome: canceled only on user/API/cancellation paths; the loop's claim is a pure deletion without that outcome. INBOX-EVENT-SCHEMA

Commit first

Inbox.mutate() validates coordinates and MessageId uniqueness across both lists, calls session.append, and mutates the in-memory array plus live notifications only after append succeeds. An append failure leaves the Inbox unchanged. A synchronous Session observer sees the pre-splice lists and can recover removed objects from the event coordinates. INBOX-COMMIT-IDENTITY

This is also why the Web projection needs no separate command queue: it reads the Agent's current lists and applies the event being emitted once to obtain authoritative post-state.

4. Claim Has a Strict Priority: Every next-step Item, Then at Most One next-turn Item

claim(target) for each proposed Step
  1. Remove and take every next-step item (FIFO)
  2. If target = next-turn, remove the head next-turn item
  3. Emit claimed(message, turn) in that order

First Step of a Turn: target = next-turn
Later Steps in that Turn: target = next-step
Priority is not preemption

The constructor replays only Inbox splices after seedLength. Claim forms one batch from all current next-step items and appends only one ordinary queued message. It never interrupts a model request already in flight; it can act only at the next pre-step boundary. INBOX-CLAIM-REPLAY

This creates an often-missed composition: a Turn's first request may include long-parked injected context, steering, and that Turn's ordinary prompt at once, ordered as the next-step batch followed by the next-turn head.

5. Follow-up Preserves the FIFO Turn Mapping, but Status Does Not Flicker Per Turn

followup(message) is send(message, next-turn, true). When idle, wake synchronously reserves the running phase and then starts the driver asynchronously. While running, enqueueing is enough: after the current Turn closes, the live driver checks hasPending, installs a fresh AbortController, and continues with another Turn. INBOX-SEND-MAP

Property-tested

Synchronous bursts, sequential sends, and mixed scheduling all verify that N follow-ups produce N strictly increasing Turns on the failure-free path, with exactly one ordinary message per Turn and no reordering. The entire burst still has only a running → idle status trace. INBOX-FIFO-PROPERTY

6. Steer Means “Nearest Step Plus Wake,” Not an In-Stream Append to the In-Flight Request

steer(message) writes to next-step and requests a wake. If the Agent is normally running, wakeDriver() does not start another driver: the current one should claim it at its next Step boundary. If the Agent is idle, steer opens a Turn itself. The core API therefore does not require running; an idle steer becomes the first input of a new Turn while replay still classifies it as steering. INBOX-DRIVER-WAKE

Semantic strength

This is boundary steering, not mutation of the provider request while it is generating tokens. It changes the log prefix seen by the next model call, but cannot retract an emitted chunk and does not automatically cancel the current tool call.

The Web composer offers steer only while an ordinary session reports running, reducing the surprise that idle steer actually opens a Turn. Direct Host session.prompt(mode: steer) still exposes the underlying semantics.

7. Inject Means “Nearest Step Without Wake”; Its Turn Depends Entirely on Timing

Injection timeOutcome
Agent idlePark in next-step until follow-up, steer, or another waking delivery
Current pre-step has not claimedJoin this batch
Pre-step already claimed; assembly/listener/model/tool is activeMiss this request and enter the next Step
Inside an agent/turn-stopping listenerThe post-listener reread can open another Step in the same Turn
An aborted driver is convergingNo wake latch; remain parked for a future wake
Claim cutoff

A test pauses the first pre-step listener and calls inject then steer. Both remain in next-step, are absent from the first request, and appear in insertion order in the second. If the current batch is rejected, later next-step input survives but still needs a fresh wake. INBOX-PRESTEP-CUTOFF-TEST

8. Pre-step Consumes Before Admission: Reject Does Not Automatically Put the Original Message Back

turn/start
  → inbox.claim(target)                 // durable pure deletion
  → assemble system prompt
  → agent/pre-step waterfall
       ├─ enter(messages) → step/start → user/message* → model
       └─ reject          → turn/end(blocked), no Step
One-way claim

The loop has already removed the complete batch before listeners run. A listener may preserve, replace, or empty messages. Reject records a blocked Turn; the original batch is not marked canceled, does not become user/message, and is not restored to the queue. An empty first enter instead produces a completed Turn with no Step. INBOX-TURN-MACHINE

If assembly or a listener throws, the Turn closes with error. next-turn work not yet claimed and next-step work arriving later remain queued, but the driver stops. The design makes input claim the commit point rather than successful model dispatch.

9. Tool Continuation and turn-stopping Reuse next-step but Have Different Producers

After the model emits tool calls, results enter the log in model order. Each finalized result's additionalContexts is then spliced into next-step in order. Without concludesTurn, the loop opens another Step in the same Turn. A conclusion still cannot skip next-step input that already exists. INBOX-TOOL-CONTEXTINBOX-TOOL-CONTINUATION

Last objection point

When the model appears ready to end and next-step is empty, the loop awaits agent/turn-stopping and rereads next-step. A regression test proves that steer from this listener forces Step 2; if a listener-injected batch is rewritten to empty at pre-step, no extra model call occurs. INBOX-TURN-STOPPING-TEST

10. running Belongs to the Whole Driver; Maintenance Still Appears Idle

ObservationWhat it meansWhat it does not guarantee
status: runningA driver is reserved and may be in pre-step, model, tools, Turn close, or between drained TurnsOne particular message is executing; the current Turn certainly still accepts steer
status: idleNo turn driver; a maintenance task may still be activeThe Inbox is empty
whenIdle()Wait for current activity and replacement activity started before retirement to stabilizeA result for one MessageId
runMaintenance()Exclusively own non-Turn activity from true idle; latch waking inputExpose maintenance itself to the model
Activity ownership

The public types define running as the complete drain interval and keep maintenance externally idle. whenIdle follows replacement work by comparing activity Promises rather than binding to a send. INBOX-ACTIVITY-CONTRACT

11. Read Cancel Across Three Layers: Signal, Pending Input, and Already-Claimed Input

CallCurrent activityUnclaimed InboxClaimed batch
cancel(cause)Abort when non-idleClear next-step then next-turn and write canceled splices; also clears quiet parked input while idleNever restored
cancel(cause,{keepInbox:true})Abort when non-idlePreserve, but pre-abort work is not automatically wokenNever restored
Web session.cancelCalls the keepInbox formPreserves ordinary queue and steeringNever restored
Subagent interruptCalls the keepInbox form and returns immediatelyPreserves and parks until a later waking sendNever restored
Implementation contract

Default cancel always invokes Inbox.clear and aborts only a non-idle phase; keepInbox skips clear. Tests prove that a waking send may already be claimed before cancel is called, leaving nothing for keepInbox to preserve. Default cancel drops a mid-step queued tail and steering. INBOX-CANCEL-CODEINBOX-CANCEL-BEHAVIOR-TESTINBOX-CANCEL-DROP-TEST

12. The Abort-to-Idle Wake Latch Repairs Cancellation Convergence but Leaves a Narrower Retirement Gap

Active Turn is aborted but not yet idle
  ├─ already queued + keepInbox → preserved but parked (no latch)
  ├─ new waking send afterward → force next-turn + wakeRequested=true
  ├─ default cancel            → clear Inbox + clear latch
  └─ disposed                  → never latch

Old driver finally: idle → if latched and pending remains → new driver
Why reclassification matters

Send samples abort state before insertion. Even a post-abort steer is redirected to next-turn so it cannot join a Turn already committed to end. Replay after convergence orders the old turn/end before the new turn/start; removing the latched message suppresses an empty replay. Regression tests cover same-tick and slow convergence, maintenance, default cancel, and disposal. INBOX-CANCEL-LATCH-TEST

13. Turn Records Alone Cannot Answer “What Happened to This Work?”

A no-Step Turn may mean an empty claim, a listener rewrite to empty, pre-step rejection, cancellation, or error. Reading turn/end alone cannot distinguish “nothing ran” from “input was consumed before reaching the model.” foldConsumedWork therefore joins steps, pure-deletion claims, and canceled splices. INBOX-CONSUMED-WORK

History shapeAccounting
Entered a StepThe Turn consumed work
Claim followed by blocked / aborted / errorThe Turn consumed work
Claim followed by completed with no StepTreat as listener-emptying, not a completed work Turn
Inbox canceled without replacementdroppedUnrun: true
One splice replaces an old message with a new oneStill pending, not dropped
Regression coverage

Dedicated tests cover failed claims, stopped claims, blocked claims, empty rewrites, claims outside a Turn, unrun drops, replacements, and a later Turn absorbing an older drop. INBOX-CONSUMED-WORK-TEST

14. “Logged,” “Pushed to Web,” and “Fsynced” Are Three Different Commit Points

Host prompt accepted
  → Inbox splice synchronously appended to live Session
  → API may return accepted
  → JSONL write-behind batch (fixed window by default)

First pre-step checkpoint
  → flush enqueue + turn/start + claim
Before model-adapter dispatch
  → flush step/start + user/message + request prefix
Fail closed before side effects

The checkpoint policy flushes at pre-step and the llm/stream boundary. The latter delays downstream-stream construction, so a flush failure prevents model dispatch. Top-level tool dispatch uses the same posture. INBOX-CHECKPOINT-POLICY

Hard-crash experiment

An E2E child process is killed at model dispatch. Recovery still contains insertion, turn/start, claim, step, user message, and request prefix, plus synthesized interrupted closers. This delivery reran that file's two tests and both passed. INBOX-CRASH-RECOVERY

The JSONL backend batches live events behind a fixed window; flush and teardown drain immediately. Every batch append is fsynced, and failure rolls the file back to its prior byte length. A synchronous prompt accepted is therefore Session-level admission, not an independent fsync receipt. A product promising power-loss safety before acknowledgement would need to await an explicit checkpoint. INBOX-JSONL-DURABILITY

15. Resume Rebuilds Pending Work but Does Not Run Merely Because It Rebuilt; Fork Seeds Do Not Copy Ancestor To-Dos

The resume path prepares a Session from persistence and then rejoins the same prepare/setup/publish path as creation. A new ReactLoopAgent constructor restores the last Turn number and Inbox from the log. INBOX-AGENT-RESUME

Seed ownership boundary

Inbox replays only session.events.slice(seedLength). Historical splices copied from a parent Session into a fork seed are child context history, not newly pending child work. Messages accepted by the child after its seed but never claimed do recover. INBOX-SEED-BOUNDARY

Construction and publication do not call wake. An ordinary Host cold resume is normally followed immediately by a waking prompt; a continuable-child cold resume likewise calls followup next, so older pending work proceeds with the new wake under the two-list claim rule. Resuming an Agent alone does not promise to consume parked input.

16. The Ordinary Host API Returns Admission, Not a Turn or Reply

session.prompt accepts mode: queue | steer. After time-zone handling, durable attachment conversion, and message-source construction, it calls agent.followup or agent.steer and returns {accepted:true} once synchronous enqueueing succeeds. It does not await pre-step, model output, or idle. INBOX-HOST-PROMPT

Wire contract

The public contract maps queue/steer directly to the two Agent operations. updateQueue supports edit/remove/steer, while session.cancel explicitly preserves pending work. INBOX-HOST-CONTRACT

The Host resolves or resumes a cold ordinary Session first. Direct mode: steer adds no running check, so it retains the core behavior where idle steer opens a Turn. That is a different layer from the UI's conservative submission policy.

17. Queue Edit, Remove, and Strict Steer Address MessageId; Claim Wins the Race

ActionAdmissionImplementation
EditStill pending; text blocks onlyDeep-freeze a replacement while preserving MessageId
RemoveStill pendingDelete with a canceled splice
Strict steerItem remains in next-turn and Agent currently reports runningRemove, then call steer with the same message
Authority is checked at mutation time

The Host relocates the message across both lists. If the loop claimed it first, the operation returns queue-item-not-found. Strict steer's running check is stronger than free-form mode: steer, but it is not a capability token bound to one Turn and remains subject to the final retirement window. Subagent-owned Sessions reject these ordinary queue operations. INBOX-HOST-QUEUE-MUTATION

18. Web Uses Whole Snapshots for the Live Mirror and Durable Splices to Recover Historical Identity

Session splice (observer sees pre-state)
  → Host applies this splice to current lists
  → session/queue { complete items[] }
       next-turn              → queued
       next-step + user       → steering
       next-step + non-user   → context
Post-state projection

The Host explicitly projects both lists and splits next-step into steering/context by source.kind. Its event observer applies the emitted splice to pre-state rather than waiting for live Inbox mutation. INBOX-HOST-PROJECTION

Reconnect baseline

The mux sends session/subscribed first and emits a queue baseline only when pending work exists. The client clears the old generation on subscribed, so “no later queue frame” correctly means an empty queue rather than retaining disconnected phantom rows. INBOX-RECONNECT-HOSTINBOX-RECONNECT-CLIENT

The Queue mirror is a whole-snapshot last-wins value. Pending steering retires when a durable user/message with the same MessageId appears. Historical replay instead reads a non-canceled removal from next-step and then checks user source to reconstruct the same user/message as steering. INBOX-CLIENT-MIRRORINBOX-HISTORY-CLASSIFICATION

19. The UI Places Queued and Steering Input Separately, and Treats “Accelerated Send” as Policy, Not Truth

QueueDock shows only placement=queued and provides edit, remove, and per-row steer. The steer button is enabled only while running. Pending steering instead renders as a user-style bubble at the conversation tail and hands off after the durable message enters history. INBOX-QUEUE-DOCKINBOX-PENDING-STEERING-UI

Enter policy

A non-running or non-steer-capable transport always queues. A busy ordinary session follows its saved Queue/Steer preference, while Cmd/Ctrl+Enter chooses the opposite. Accelerated Enter on an empty draft can strict-steer every queued row. Held Enter is suppressed to prevent repeated sends. INBOX-SUBMISSION-POLICYINBOX-INPUT-GESTURES

20. A Continuable Child's Activation Is a Residency Epoch, Not a Request or Result

startContinuable establishes a durable child. Later followup hides the resident-versus-cold-resume distinction and routes every item through the child's single Inbox. The success boundary is acceptance of a message id by that Inbox; it does not await Turn start or return a child reply. INBOX-CONTINUATION-SEAM

Activation stateDerived condition
runningChild Agent is running, or the manager recorded a waking message before synchronous send completed
waitingChild Agent is quiescent but still owns undisposed descendant Activations
settledAgent is quiescent with no owned children, so its handle can be disposed

One Activation may execute many FIFO Turns and may remain resident after its own Agent becomes idle because descendants still run. The manager owns only residency and ownership; the Agent loop and Inbox remain the sole owners of Turn ordering. INBOX-ACTIVATION-MODELINBOX-ACTIVATION-STATE

21. Parent-to-Child Delivery Is Always Follow-up; a Lock Linearizes Delivery Against Disposal

For a resident child, the manager submits under a child-id lock. If disposal has begun, delivery waits for release and then cold-resumes. For an absent child, it inspects persistence, authorizes against durable parent lineage before folding, reads only the post-seed continuable descriptor, and reconstructs an Activation. INBOX-CONTINUATION-DELIVERYINBOX-COLD-RESUME

Acceptance fence

Final admission is a no-await synchronous span checking caller signal, manager drain, Activation disposal, and direct-parent authority. It adds MessageId to the accepted set before invoking child Agent.followup, then marks the Activation announced. Every parent-to-child message therefore enters next-turn; it never steers the child's current Turn. INBOX-CONTINUATION-ADMISSION

Interrupt is not settlement

Interrupt authorizes only against a live Activation, calls cancel(...,{keepInbox:true}), and returns immediately. Pending work and descendants remain, claimed work is not restored, and an absent target is an accepted no-op. A later waking follow-up restarts the parked queue. INBOX-CHILD-INTERRUPT

22. Child-to-Parent Has Two Independent Messages: Explicit Report and Automatic Settlement Account

MessageSourceParent schedulingEnds child?
Explicit reportsubagent-report relaywakeup → followup; quiet → injectNo
Settlement noticesubagent-settled noticeparent closing → inject; idle → followup; busy → steerIt accounts for ended residency; it is not words authored by the child
Explicit reporting

Only the exact resident child Agent may report. The recipient is derived from durable direct-parent lineage rather than caller choice. wakeup versus quiet is a parent scheduling policy; reporting does not alter the child's Turn or Activation. INBOX-CHILD-REPORT

Settlement notification

The manager delivers settlement after the child handle is released but before parent ownership is released, preventing the parent watcher from disposing first. A busy parent receives steer, letting several simultaneous child completions coalesce into one next-step batch. Notice failure is logged and dropped, and final flush is best-effort. INBOX-SETTLEMENT-NOTICE

23. Child Web and Model Control Surfaces Are Intentionally Narrower Than Ordinary Sessions

SurfaceFollow-upSteerStop
Web continuable childRequires live direct parent; text only; returns MessageIdNone; client ignores mode and uses subagent.promptMay interrupt by durable address while parent is offline
Web one-shot childRead-onlyNoneNone
Model send_messageThe exact live direct parent sends a later FIFO Turn to its childNoneA live ancestor can use interrupt_agent on a descendant

The wire contract defines prompt receipt as an Inbox MessageId and interrupt receipt as signal acceptance rather than target quiescence. Host child prompt requires a live parent, while interrupt deliberately performs no parent, catalog, or persistence lookup. INBOX-SUBAGENT-WIREINBOX-SUBAGENT-HOST

The client makes one-shot children read-only. A running continuable child keeps Send and exposes Stop separately, so new input becomes a later Turn. With the parent offline, input is disabled but a live child can still be stopped. The model tool likewise states that send_message returns no reply and cannot redirect current work. INBOX-CHILD-CLIENTINBOX-CHILD-COMPOSERINBOX-CHILD-OFFLINE-UIINBOX-CONTROL-TOOLS

24. Two Reproducible Documentation Drifts Exist at the Pinned Baseline

Adjacent documentation/commentWhat it saysCurrent production code
api/events.tsA pending message becomes durable only when claimed; pending work has no durable Session eventagent/inbox/spliced is appended before live mutation and insertion carries the complete message
ApiProxy READMEPending next-step steering stays outside the Web projectionqueueItems() projects both lists, labels user-origin next-step as steering, and ChatView renders its pending bubble
Not an interpretive difference

The wire comments directly contradict the current durable event schema and Inbox implementation. The README directly contradicts the current Host projection and UI. These are comparisons within the same pinned commit, not runtime speculation. INBOX-WIRE-DOC-DRIFTINBOX-README-DRIFT

25. Guarantee Matrix and Design Assessment

ClaimGuaranteedNot guaranteed
Ordinary follow-upFIFO in one Inbox; one Turn per item during a successful drainAn independent result Promise per item; automatic continuation after rejection/error
SteerThe nearest Step boundary not already crossed; can wake from idleMutation of an in-flight request; absolute freedom from retirement races
InjectModel-facing context at the next claimSelf-wake; guaranteed membership in the current Turn
Cancel keepInboxPreservation of work not yet claimedRestoration of claimed work; automatic wake for pre-abort queued work
Prompt acceptanceSynchronous Inbox/Session admission and a MessageId for child deliveryModel dispatch, a produced reply, or completed physical fsync
Continuable childCold resume across residency; parent-to-child always a later FIFO TurnA synchronous child result from send_message; current-Turn steering
Final assessment

The strongest design choice is avoiding separate pending-work queues for UI, plugins, and subagents. Two lists, one splice vocabulary, stable identities, and one claim algorithm span the kernel, recovery, and product surface. The expensive complexity accumulates around “acceptance is not execution”: pre-step claims first, cancel does not roll back, driver status spans Turns, and Web snapshots plus physical persistence each add their own commit point. The system has filled most observability gaps with consumed-work folding, checkpoints, and a wake latch. Public documentation still needs correction; stronger no-stranding or crash-safe acknowledgement would require a new explicit protocol, not more optimistic wording.

Verification status

Beyond branch-by-branch tracing of production source, wire contracts, READMEs, and tests at the pinned commit, this delivery executed 13 targeted Agent/Inbox/Host/client/subagent Vitest files (379 tests passed) and separately ran the hard-crash checkpoint file under the E2E configuration (2 tests passed): 14 files and 381 tests passed in total.

My Learning Notes

Autosaved only in this browser. Nothing is uploaded or committed. Export Markdown whenever you want to keep a copy.