The scenario inspects the tools service API; the round-2 content-contract
JSDoc change shifted its rendered output. Keyless DSH_SNAPSHOT=refresh —
the resulting fixture is byte-identical to the one the shiki branch
already carries (the downstream trees were green for this reason).
The re-records for the SDK-prompt change had harvested live v4-pro
headers while every replay overlay pins v4-flash, so keyless replay
diverged on provenance; keyless refresh reconciles the affected
scenarios (dispatch-start pairs preserved).
The scripted (non-recorded) fixtures' run_code calls predate the required
description parameter, so replay rejected them at validation before any
dispatch: patch the scripted programs' args (tool/call, message blocks,
and chunk deltas together) and refresh goldens keylessly. Also picks up
the v4-pro re-records of the code-mode scenario pair whose live model
drifted from the overlay pin, and drops tmp-path churn.
The bridge replaces its serialization queue with a pool that reuses the
native concurrency contract: submissions classify through
registry.executionMode (fail-closed isConcurrencySafe), start strictly in
submission order, overlap up to the validated maxParallelSubCalls config
(default 10; 1 restores serial), and exclusive calls drain the pool, run
alone, and bar later calls. Each started sub-call logs a
tool/code-dispatch-start event at pool entry; the existing
tool/code-dispatch settles the pair (started ⇔ settles exactly once;
abandoned queued calls log neither). SDK prompt guidance now states the
true Promise.all contract — re-recorded across every code/both-mode
snapshot (plus the stale cordis-dynamic-toolchain fixture gaining the
required description arg).
Client: CodeSubCall widens to RunningToolCall | ToolResultNode — starts
land the running shape (rows wear the native running ring), settles
replace in place preserving start order, callTime pairs to the start
time. Fixture emits start/settle pairs; jsdom pins the running sub-row;
runtime specs pin in-place settlement and out-of-order completion.
run_code gains a required bash-style description parameter: presentCall
titles the card with it and moves the program to rawInput, so every
surface gets a readable label. tool/code-dispatch now logs each
sub-call's complete content/isError (the tool/result vocabulary),
replacing the bounded resultSummary and deleting the summarize/cwd
machinery — a UI renders sub-calls through the identical path as native
results. The dsh config tree mounts the worker code runtime and reads
DSH_TOOLS_MODE (temporary seam until per-session mode selection lands).
Session format stays v0 (pre-release churn). Code-mode ACP/TUI fixtures
re-recorded; TUI presenter pin refreshed; catalogs regenerated. Keyless
web smoke pins the code-mode wire contract (tools=[run_code] + SDK
prompt section).
A well-formed provider stream that ends with finish_reason stop and zero
content blocks previously became a successful empty assistant message: the
turn completed silently, and drivers like goal-session counted the no-op
round. Both adapters now map that degenerate completion to a finish
{kind:'error'} with the new canonical EMPTY_RESPONSE code from dsh-llm, and
dsh-llm-retry adds the code to its default retryable set, so the existing
closed-step recovery path retries it and fails loud once the budget is
exhausted.
Covered by adapter unit tests, an llm-retry default-policy test, and a new
authored keyless ACP snapshot (empty-response-retry) with a deterministic
1 ms zero-jitter retry overlay.
Address a second fresh-eye review of the review fixes:
- MAJOR: late steering that lands after runTurn returns terminally
stopped (e.g. during the post-turn flush) was drained by runLoop and
dropped without a discard, leaving a dangling outstanding id the
negative-only invariant can't catch. Emit agent/inbox/discard for it,
symmetric with the in-turn terminal-stop drop.
- remove the dead cancel() idle-settle branch: whenIdle's fast path
already resolves for a lone quiet item, so no waiter is ever left for
it to settle. Document why.
- gen-cordis-api classShape now drops private/protected/#private members
and strips getter/setter bodies, so Session no longer leaks private
fields and getter bodies into the model catalog.
- document that AgentMessage intentionally omits meta (durable-only).
Adds a regression test for the late-steering discard.
Resolve six review findings on the unified-send change:
- quiet (wakeup:false) queued items no longer un-park the driver; the
inbox distinguishes hasWakingQueued (drives the loop, idle/quiescence)
from hasQueued (anything to dequeue), so a lone quiet item parks at idle
and rides the next waking send. whenIdle/cancel settle off the waking
signal, so cancelling a parked quiet item no longer hangs whenIdle.
- SendOptions.meta on queued/steering sends now reaches the durable
user/message and steering/message (was dropped except on injection).
- a terminal agent/turn-stop that drops pending steering emits
agent/inbox/discard so the enqueue-dequeue-or-discard ledger balances.
- the loop-authored continuation reason is snapshotted and frozen like a
public send.
- gen-cordis-api collects exported classes (body-stripped) so the now-
abstract-class Agent and its transitive shapes reappear in the API
catalog.
Adds regression tests for each and re-records the affected snapshot.
Merge took master's snapshot fixtures; re-record the two keyless
scenarios so they reflect this branch's user/message coalescing and the
abstract Agent + AgentMessageId type dump.
Replace send/steer/inject with one Agent.send primitive over the
(target × wakeup) matrix; followup/steer/inject become fixed-preset
alias methods on the now-abstract Agent class. Coalesce context/message
into user/message (injected context is a non-user source). Replace
agent/queued with agent/inbox/enqueue/dequeue/discard, add cancel
keepInbox, and add a FIFO-conservation invariant.