- Stage transcript initialization inside a detached TranscriptContainer to keep existing messages visible during incremental rendering.
- Add fallback state restoration in InteractiveMode.renderInitialMessages when chat rendering is aborted or fails.
- Update render-initial-messages tests to assert that old transcripts remain visible until replacements are fully committed.
The HerdR flicker fix routed direct HerdR panes onto the in-place
multiplexer resize path, but the randomized render-stress oracle still
modeled them as ED3 replaying direct terminals. The width-epoch ledger
now applies to any in-place-resize scenario (multiplexer + direct HerdR)
via a dedicated resizeRepaintsInPlace trait, keeping HerdR's direct
scrollback semantics intact. Adds a deterministic replay regression
(seed 0xcafed00d, 24 iterations) that fails without the oracle update.
Kept initialChatRendered false until the incremental cold-start replay settles, preventing extension display callbacks from launching a synchronous rebuild into a partially replayed transcript. If session entries arrive while the replay yields, discard the stale partial tree and replay the current context before publishing the initialized state.
Fixes#8133
Moved the per-message rebuild yield to the top of the loop so it runs regardless of which early continue path (preserved-live, image-only, or grouped read result) an iteration takes. A large parallel read-result batch replays entirely through the isReadGroupResult continue, which previously skipped the trailing yield and rebuilt the whole batch in one event-loop turn.
Fixes#8133
Replayed idle transcripts in bounded message and time chunks, painting cleared scrollback between macrotasks so terminal input remains responsive during restore and tree navigation. Kept streaming rebuilds atomic and migrated every rebuild caller to await completion.
Fixes#8133
- A provider-supplied retry-after now bypasses the transient rate/concurrency
heuristic window instead of being overridden by it (regression from the
subscription-cap retry change).
- Updated event-controller/ui-helpers test doubles for provenance-gated
renderer selection (hasBuiltInTool), aggregated retryErrors on
auto_retry_end, and Bedrock override compat gaining streamIdleTimeoutMs.
- The 'N tool calls elided' replay placeholder leaked tool activity while
display.hideToolActivity was on; it is now a visibility-aware component
wired into both the hotkey and /settings toggle paths.
- Added replay + live-reveal regression coverage.
- Added transcriptMessageComponents to every InteractiveMode ctx test literal; #6033's reuse cache made the field required and addMessageToChat populates it unconditionally.
- Wired setTodoPhases into the eager-todo ToolSession fixture to mirror sdk.ts; the test relied on the stale message_end todo replay #6148 removed.
- Added the `xd://` virtual device protocol (`internal-urls/xd-protocol.ts`, `tools/xdev.ts`): tools declaring `loadMode: "discoverable"` are unmounted from the request tools array and driven via `read xd://` (list/docs+schema) and `write xd://<tool>` (execute), gated by the `tools.xdev` setting (default on) and inlined into the system prompt.
- Merged the `irc`, `job`, and `launch` tools into a single `hub` tool (`tools/hub/`, `async/job-manager.ts`): messaging keeps `send`/`inbox`/`list`, job control maps to `wait`/`cancel`/`jobs`, process supervision keeps `start`/`logs`/`stop`/`restart`/`describe` with `ps`, and the unified `wait` races background jobs against peer messages; SDK `IrcTool`/`JobTool`/`LaunchTool` are replaced by `HubTool`.
- Removed the hidden `resolve` tool in favor of the `xd://resolve`/`xd://reject`/`xd://propose` resolution devices, auto-including `write` whenever a deferrable tool or plan mode is present.
- Removed the BM25 tool-discovery system: the `search_tool_bm25` tool, the `tool-discovery` module, the `tools.discoveryMode`/`mcp.discoveryMode`/`mcp.discoveryDefaultServers`/`tools.essentialOverride` settings, per-tool MCP selection, and the `mcp_tool_selection` message type.
- Unified tool presentation on `ToolLoadMode` (`essential`|`discoverable`), replacing the custom-tool `xdev?: boolean` opt-out; custom, extension, MCP, RPC host, image-generation, and TTS tools now default to `discoverable`, and added a `satisfies` predicate to `SoftToolRequirement`.
- Removed the standalone `ssh` command tool and `ssh/ssh-executor` (the `ssh://` read/write/search protocol stays), and made `--tools` address hidden built-ins.
- Updated collab-web to render `xd://` dispatches and `hub` op families, dropped the `search_tool_bm25`/`ssh`/`report-finding` renderers, refreshed tool docs and prompts, and migrated the affected tests and changelogs.
UiHelpers.renderInitialMessages() reads the module-level settings
singleton (display.collapseCompacted) added upstream. Tests exercising
it directly must call Settings.init() first or they fail with
"Settings not initialized" whenever no earlier test in the same
process happened to initialize it, which is bucket-order-dependent and
flaky.
- Bounded expanded partial edit diff rendering in `formatStreamingDiff` to `previewWindowRows()` instead of an unbounded budget, preventing runaway preview growth during live updates.
- Updated streaming diff tests to simulate terminal height and verify expanded previews stay full only within the viewport, then switch to a truncated tail with the `more lines above` marker when too tall.
- Reinitialized in-memory `Settings` before each initial-messages test since the test suite reads global display configuration and needs isolation.
- container stubs gained disposeChildren for the stale-renderer teardown paths
- login-stored API key assertions include the new source provenance field
- bash timeout test covers the zero-disable contract alongside the clamp
- skill keyword steering activates a task tool for the gated workflow notice
- Extracted decodeStreamedToolArgs into tool-args-reveal.ts and used it from both the live event path and transcript rebuilds, so mid-write theme/settings/focus replays no longer show stale streamed write/edit/eval content.
- Fixed the smoothing-off live path returning stale provider-parsed args.
- Documented the mandatory shared decode in the AGENTS.md streaming-preview hazard note; added changelog entries for this batch.
- Transitioned the eval tool from batch multi-cell execution to a single-step input structure with flat parameters.
- Updated core agent logic, UI components, and documentation to support state persistence across incremental eval calls.
- Restricted bash tool capabilities by requiring explicit use of `read` or `find` instead of `ls` or `find`.
- Added support for Ruby and Julia language runtimes to the eval tool and associated web renderers.
Tail appended transcript JSONL instead of rebuilding rendered history on every poll, collapse compacted history for live chat rendering, and replace synchronous session rewrites so tailers detect historical changes.
Fixes#3258
- Added cursor save/restore escape handling around direct image emission.
- Replaced the previous up-and-down cursor movement sequence with restore logic to keep renderer output positioned correctly.
- Ensured direct image replay path no longer depends on moving back down after placement to align the cursor.
- Added Matplotlib figure PNG rendering and display tracking in Python runner to emit PNG output immediately when figures are displayed via display(fig).
- Extended session persistence to externalize oversized image payloads in both content and details.images, enabling tool result images to survive session reload.
- Enhanced session loader to resolve image data payloads and blob references across content and details.images during session reconstruction.
- Added image cache invalidation in TUI image component when image protocol, cell dimensions, or Kitty Unicode placeholder mode changes.
- Added comprehensive test coverage for Matplotlib display, image persistence across reload, and TUI image rendering with protocol and dimension changes.
- Added session-domain modules and exports for session-entries, context, listing, loader, and migrations.
- Changed persistence to async append writes plus writeTextAtomic, removing sync line APIs.
- Added compaction-aware session context rebuild with dangling tool-call cleanup.
- Added resumable session resolution with status inference, id/stem/suffix matching, and backup recovery.
- Added a SessionFocusController to switch transcript and input context between main and subagent sessions.
- Added agent-hub Enter activation and double-left return behavior for focused local agents.
- Added view-session-based event and render logic to avoid stale focus-session state.
- Added status-line focused agent display with ghost icon and focused-mode border dimming.
The session-observer overlay is gone. The Agent Hub (ctrl+s, alt+a, or double-tap left arrow on an empty editor) presents one overlay with two views: a live registry table (status, unread irc count, current task, last activity; j/k to navigate, r to revive, x to abort/release) and per-agent chat (transcript + input line) — submitting revives a parked agent and steers it via the normal prompt path. Main is the ambient chat and stays out of the table.\n\nrenderInitialMessages no longer takes a prebuilt context: every redraw now reaches for AgentSession.buildTranscriptSessionContext() (full-history transcript with each compaction emitted inline at the point it fired, snapcompact frames re-attached on rebuild). UiHelpers drops the deferred-compaction render and the IRC autoreply branch that the new mailbox bus deprecated. CompactionSummaryMessage renders as a slim divider (── 📷 compacted · ctrl+o ──), expanding to the summary + snapcompact frame count. session-manager.buildSessionContext gains a { transcript: true } mode; an exported buildSessionContextFromFile() reads a session file without taking the writer lock so the hub chat view can tail any agent (parked or live). Theme picks up icon.camera + tool.irc symbol entries.
- Added ChatBlock and ChatBlockHost with mount, finish, and dispose lifecycle callbacks.
- Added InteractiveModeContext.present and resetTranscript APIs and used them to mount/repaint blocks.
- Reworked controller rendering paths to emit command, event, extension, and selector outputs via ctx.present.
- Implemented component and container dispose hooks in tui so loaders and child blocks cleanup timers/effects.
- Replaced regex code-block parser with line scanner masking fences.
- Exposed `>`-quoted runs as drillable copy targets per message.
- Added combined "all quotes" target when multiple quotes exist.
- Emitted per-command cmd:N targets after the assistant message that issued them.
- Carried commands from text-less messages to the next visible message.
- Replaced the single trailing "last command" leaf.
- Added a fullscreen /copy tree of recent assistant messages with nested code blocks and a live preview pane.
- Removed the /copy last|code|all|cmd subcommands in favor of tree selection.
- Extracted copy-target assembly into a testable util.
- Cleared native scrollback on `omp`/`omp -c` so the resumed transcript no longer stacks on the prior run's welcome screen.
- Tracked assistant block finalization so aborted streaming messages stay repaintable when status rows land beneath them on ED3-risk terminals.
AGENTS.md L51: NEVER use ReturnType<> — use the actual type name.
bun:test exports Mock<T> directly; use that instead.
Note: framework is bun:test, not vitest as Codex stated.
Before this change, every navigateTree → renderInitialMessages call path
performed two independent O(N) session-tree walks:
1. agent-session.ts:6586 buildDisplaySessionContext() [inside navigateTree]
2. ui-helpers.ts:402 sessionManager.buildSessionContext() [inside renderInitialMessages]
Changes:
- agent-session.ts: navigateTree() now calls sessionManager.buildSessionContext()
once, derives the display (deobfuscated) context from the raw result, and
returns the raw SessionContext in the result object.
- ui-helpers.ts: renderInitialMessages() accepts an optional prebuiltContext
parameter; reuses it when provided, falls back to buildSessionContext() otherwise.
- interactive-mode.ts: forwards prebuiltContext through the wrapper.
- modes/types.ts: updates InteractiveModeContext interface to match.
- selector-controller.ts: passes result.sessionContext from navigateTree into
renderInitialMessages(), closing the deduplication loop.
Bench (100-msg session, 200 iterations):
two walks [BEFORE]: 0.0702ms/op
one walk [AFTER]: 0.0298ms/op
Saved: 0.0404ms/navigation (57.5% reduction per navigate)
Tests: render-initial-messages-dedupe.test.ts asserts buildSessionContext is
called 0 times when a prebuilt context is passed, 1 time as fallback.