TurnEndReasonMap.error now carries a single `error: LlmFailure` field:
an LlmError keeps its structured facts, any other error flattens to
errorChain text under the UNKNOWN code. Consumers read message/code
directly instead of defending against an unknown union — this also fixes
errorChain() rendering structured failures as '[object Object]' in the
TUI and ACP error paths. Document the turn-stopping contract: a
concludesTurn result never short-circuits already-submitted next-step
work (same-step additionalContexts or racing steering still runs), data
decides.
A prompt now resolves only when the agent reaches quiescence: the
correlated turn/end arms an endReason instead of settling immediately,
so steering or injected work that runs further turns before idle no
longer splits one prompt across the automation wire. Token-limit turn
endings settle as end_turn per the README contract (they are not
prompt-level stop reasons); model errors still reject immediately, and
explicit ACP cancellation stays 'cancelled'.
next-step items now split by origin: user-origin messages keep the
'steering' placement, while agent.inject context (approval notices, task
completion notices, attached snapshots) carries a new 'context' placement
that no surface renders until it is claimed as a durable user/message
context card. Widen the placement unions on the wire and in the client
runtime, add a projection test case, and document the split in the
apiproxy/ui-conversation READMEs and the web-steer chrome agent note.
- agent/error: drop the stale "(plus the logger)" claim; the machine only
emits the event.
- agent/inbox/claimed: document that a rejected turn boundary ends the
claimed message without discard or re-emission, and that a later batch
may reuse the turn number.
- steer(): document that a rejected step leaves steering parked until the
next wake.
- Fix stray double-space in the inbox inserted/discarded callbacks.
- Sync core.md/core.zh.md mirrors and regenerate the cordis catalog.
Steer, inject, and followup now land as durable user/message events on the
session surface; the steering/message event type and its ConversationNode
kind are removed from the client projection. Update tests, docs, generated
catalogs, and agent notes to match, and align the steering e2e fixture and
prompt inventory assertions with the durable user/message landing.
Master's transactional loader made the invalid-provider PTY case regress:
the HMR main watcher's initial scan refreshed the include mid-initial-apply,
the concurrent group updates stranded the include fiber, and once serialized
the failing apply's rollback deadlocked on HMR's refresh drain — dsh exited
13 with no diagnostic and the terminal stranded, the exact symptom this
branch fixes. Serialize every include child-tree mutation through one queue
and pass ignoreInitial to the HMR main watcher; the failing boot now settles
through boot()'s labelled rejection with the tree disposed and exit 1. The
PTY case asserts the settled diagnostic; the fail-loud release remains the
guard for rejections boot cannot see.
Conflicts: apps/cli/src/tui.ts (keep the release install over master's comment
rewording), packages/ui/app-boot/README* (master's new installFailLoud row
wording plus this branch's release and timeout rows).
The trajectory host kept the composer as a fixed flex sibling, so the
ledger never reached the viewport bottom. Anchor the composer seat
absolutely over the ledger (reusing chat's fade treatment) and have the
internal scroll panes reserve the composer's live height plus a 16px gap
so end rows and detail bodies scroll clear of the overlay.
(cherry picked from commit 0058a1f4b6e8c2e23bfde7c827a84838cc5ebffc)
The hover affordance framed only the strip right of the crumbs. The bar itself
now carries the outline and the padding in both modes, so hovering previews
exactly the field the click produces and nothing resizes when the two swap.
One keystroke moved the view twice: deleting a separator first narrowed the
pane the draft had just walked away from, then replaced it with its landing.
The tail now filters only the LAST pane — the one whose level the path names —
so a pane on its way out holds still until its landing arrives.
Also from the review round: the walk waits both legs out instead of taking the
submitted-navigation bound (a speculative scan has nothing waiting on it, and
a tail keystroke aborting a slow parent leg would otherwise strand the
two-pane view); a level keeps answering the directory text that produced it,
so `..` segments and Windows forward slashes filter and stop rescanning; the
release-on-miss rule counts displayable rows, so it survives `hidden` ever
meaning more than dot-prefixed; and the editor's 250ms rest joins the other
two constants on the remote-recalibration list.
Skipping the draft-following scan whenever ANY pane happened to list the
directory was the cheaper rule and the wrong one: erasing a segment left the
level being typed on the LEFT, with its own child pane still standing to its
right, so the two panes stopped reading as "where I am, and where I came
from".
The pane arity is now the invariant the editor maintains — the last pane lists
the level the path names, its parent sits beside it, and only a display root
lists alone. Only that last pane's own tail costs no scan; every other
directory part re-lands.