- Minted the guided-goal Codex side session id once per interview in handleGuidedGoalCommand and threaded it through every turn via GuidedGoalTurnOptions.sideSessionId, so a multi-question interview shares a single websocket-only Codex socket instead of opening a fresh one each turn (which could trip websocket_connection_limit_reached and fall back to the rejected SSE path).
- Exported newGuidedGoalSessionId; runGuidedGoalTurn mints its own id only when no side session id is supplied (one-shot callers, tests).
- Added regression coverage asserting the supplied side session id is reused across turns.
Fixes#5304
The empty-editor left-left gesture opens the Agent Hub whenever persisted
or parked subagents exist (intended since f3e372e7b), but the hub's own
close detector starts fresh at 0 with no handoff from the editor's
double-tap detector. The two taps that opened the hub were consumed by the
editor, so a single subsequent left did nothing and the user had to press
left-left again to escape while input and hotkeys stayed disabled.
Thread an armCloseTap option from the gesture through showAgentHub to the
new AgentHubOverlayComponent.armCloseTap(), which seeds the table's
#lastLeftTap so one more left (within the tap window) dismisses the hub.
Fixes#4780
- Added `tui.scrollbackRebuild` configuration with interactive startup/controller wiring to apply `setScrollbackRebuild`.
- Exposed prewalk session state in `SegmentContext` and rendered a dedicated prewalk segment/icon in the status line.
- Added divergence-aware TUI full-paint logic that enables scrollback erase-and-replay rebuilds for non-multiplexer divergence cases.
- Updated rendering and streaming tests to verify rebuild behavior (`3J`) and eliminate stale marker expectations under drift scenarios.
- Introduced conditional scrollback clearing during UI renders when transcript compaction is enabled.
- Updated `CommandController` and `EventController` to respect the `display.collapseCompacted` setting.
- Configured `SelectorController` to trigger a chat rebuild and UI reset when the compaction setting changes.
- Updated `InteractiveMode` to dynamically toggle between collapsed and full inline history based on user settings.
- Implemented `/queue` command and `->`/`=>` shorthands to support deferred, sequential message processing.
- Added a robust parsing utility to handle various list-based queue inputs and automate yield management.
- Integrated visual decorations and state tracking to provide real-time feedback on queueing status.
- Enabled non-cursor line text decoration in the TUI to support dynamic queue header rendering and list numbering.
- Included the current session name in the fullscreen pause UI.
- Updated `renderPauseScreen` and `InteractiveModeContext` to propagate and display the session title.
- Added test coverage for both full and compact pause screen layouts.
- Transitioned vibe tools to an ephemeral registration model where they are installed only when entering `/vibe` mode and removed upon exit.
- Added `activateVibeTools` and `deactivateVibeTools` methods to `AgentSession` to manage these transient tool registrations.
- Removed vibe tools from the default global tool registry, preventing unnecessary background exposure.
- Improved real-time screen rendering by implementing dynamic builder functions for vibe mode components.
- Optimized cursor and spinner rendering to re-derive state from shared mutable options on every paint.
- Added session snapshotting and improved wait logic to accurately detect settled turns and running sessions.
- Ensured reliable toolset restoration during mode transitions through updated logic and new contract tests.
- Implemented Vibe mode to enable worker session management and director-role context injection.
- Added a `/vibe` slash command and integrated status line UI to display mode activity.
- Configured restricted toolsets and guards to prevent concurrent conflicts with existing Goal or Plan modes.
- Provided system prompts and tool templates to support specialized agent communication and task orchestration.
Extensions calling ctx.ui.addAutocompleteProvider (e.g. @ff-labs/pi-fff)
crashed at load with 'TypeError: ... is not a function' because omp's
ExtensionAPI.ui omitted pi's autocomplete-provider API; the throw also
aborted the rest of a try/catch-guarded session_start init.
ExtensionUIContext now declares addAutocompleteProvider(factory).
Interactive mode stacks each factory on the built-in editor provider in
registration order, re-applies the stack on every slash-command refresh,
and skips throwing/malformed factories; RPC, ACP, and headless contexts
accept the factory as a no-op, matching upstream pi's RPC behavior.
Fixes#4919
Stopped new-session and session-switch UI paths from detaching active loader/render components without running their disposal hooks.
Added container and loader coverage for disposing children before destructive transcript/status replacement.
Fixes#4686
- Added throttling and debouncing to HUD data rendering and observer UI synchronization to coalesce update bursts.
- Constrained the subagent HUD display to a maximum of 8 rows with a truncation notice for hidden sessions.
- Enhanced the session observer registry to categorize update types, enabling more granular UI reconciliation.
- Verified render coalescing and display truncation behavior with comprehensive integration tests using fake timers.
- Added comprehensive unit tests for `TranscriptContainer` to verify uncommitted block tracking.
- Created integration tests ensuring `AssistantMessageComponent` correctly streams thinking and answer content into scrollback.
- Added tests verifying that expanded tool evaluation output records rows correctly without duplication after settling.
- Updated `AssistantMessageComponent` test suite to cover table streaming scenarios in the unsettled tail.
- Moved terminal title update logic to a single listener onSessionNameChanged.
- Removed redundant setSessionTerminalTitle calls from ExtensionUiController, InputController, and InteractiveMode.
- Ensured consistent side-effect execution for terminal titles and editor accents across all session name change triggers.
Timer-driven reveal and spinner ticks (streaming reveal, tool-args reveal,
tool-execution spinner, todo strike animation) now hand the changed
component to `TUI.requestComponentRender(component)` instead of forcing a
full-tree render at 30fps. Every other root subtree reuses its previous
frame rows, cutting the Box/Container tree walk out of the compose
pipeline while the transcript grows.
Shimmer:
- Intern the working-message palette per accent (WeakMap-keyed) so the
Symbol-slot compiled-ANSI cache in `shimmerSegments.compile` actually
hits between frames — the fresh palette literal in `renderWorkingMessage`
guaranteed a per-tick miss.
- Add an `activeBand` fast-path: outside the sweep window the intensity
is guaranteed zero, so those code points coalesce into a single low-tier
run without running `intensityFn` or `tierFor`. On the typical ~60-char
working message the classic band is 12 cells wide, so ~80% of the per-char
loop disappears.
Widen `ToolExecutionHandle` to extend `Component` (matches every
concrete impl — `ToolExecutionComponent`, `ReadToolGroupComponent` —
which already extend `Container`) so the reveal controller callback
sites are type-checked.
Fixes#4377
Plan-approval's 'Approve and compact context' used to pass the rendered
plan-mode-compact-instructions prompt as the first positional argument
to handleCompactCommand -> session.compact(), which landed on the
session_before_compact extension hook as customInstructions. Extensions
treating that field as user focus (e.g. to bias a query-focused summary)
would then see plan-mode boilerplate instead of operator intent and
produce query-biased compactions.
Add CompactOptions.internalGuidance: a private summarizer-only channel.
session.compact() reads it into the fallback-model summarizer while the
session_before_compact hook payload still only carries the public
customInstructions arg (undefined for the plan-compact path). The
snapcompact-disable predicate and the /compact rejectsFocus guard cover
both fields so a directed summary is never silently downgraded.
Extend the interactive-mode handleCompactCommand facade + command
controller with a fourth internalGuidance parameter, and switch the
plan-approval callsite in interactive-mode.ts to route the plan prompt
through it.
Fixes#4359
Selecting "Approve and compact context" while a user turn was typed during
compaction surfaced `Failed to finalize approved plan: Agent is already
processing` and silently discarded the operator's queued turn.
`flushCompactionQueue` fires the queued user turn (fire-and-forget) before
`handleCompactCommand` returns, so by the time `#approvePlan` resumed, the
session was streaming. The previous shape aborted the queued turn and still
raced into `AgentBusyError` when `session.prompt()` ran before abort settled.
The finalize path now queues the plan-approved directive as a synthetic
follow-up when the session is streaming, and catches a racing `AgentBusyError`
from `prompt()` with the same fallback. `AgentSession.followUp()` gained a
`{ synthetic, expandPromptTemplates, attribution }` option so the hidden
execution directive lands as an agent-attributed developer message on the
follow-up queue, without flipping advisor auto-resume the user-follow-up path
does.
Fixes#4358
Main removed the floating todoReminderContainer in 112317bc8 (todo
reminders are now anchored inside the scrollback transcript and reset
by renderInitialMessages({clearTerminalHistory: true})), so the added
this.todoReminderContainer.clear() references a property that no longer
exists post-merge, failing typecheck and throwing on every /btw branch.
Revert the interactive-mode hunk and its test, and reword the changelog
entry to cover only the goal-mode todo context fixes.
- Refactored `renderSubagentHudLines` to use `renderTreeList` with dim connectors and a single-space indentation shift.
- Adjusted budget limits to account for the new layout wrapping and tree-list padding.
- Consolidated duplicated inline thinking level comparisons into a unified `concreteThinkingLevel` helper.
- Enhanced legacy tool shims to respect isolated session settings and support legacy options.
- Cleaned up redundant UI render requests and extra status-line updates.
- Refactored `grep` tool shim to configure context dynamically via isolated settings.
- Disabled platform-incompatible shell shim tests on Windows environments.
- Ensures in-memory overlay edits are durably written to the plan file before proceeding with approval.
- Avoids asynchronous write races by awaiting the final plan file serialization.
- Aligns synthetic approved-plan prompts with reference-only expectations.
- Thread the postmortem reason through the session teardown pipeline to the session dispose process.
- Prevent generic "dispose" logs from overwriting real triggers like SIGTERM, SIGHUP, or uncaught exceptions.
- Ensure the first teardown trigger's reason is preserved when concurrent disposal calls occur.
- Add comprehensive test coverage verifying signal-specific reason mapping inside exit diagnostics.
- Fix a minor unhandled-exception test utility expectation in input controller tests.
/quit and /exit hung for many seconds because AgentSession.dispose()
awaited MnemopiSessionState.dispose() unconditionally, and that path
runs consolidate() (state.ts:421) which fires a fresh LLM fact
extraction for the just-retained transcript and then awaits
flushExtractions() per owned bank. One LLM round-trip per shutdown,
no upper bound, no visible status.
- Add a timeoutMs option to MnemopiSessionState.dispose. When the cap
fires the in-flight consolidate is detached to the background and the
SQLite handles close once it settles, so writes never race a closed
handle.
- AgentSession.dispose passes SHUTDOWN_CONSOLIDATE_BUDGET_MS = 1_500 on
the user-visible shutdown path. Per-turn maybeRetainOnAgentEnd has
already retained earlier turns, so the worst case is losing episodic
promotion for the last few turns. State-replacement disposes
(mnemopiBackend.start) stay unbounded.
- InteractiveMode.shutdown surfaces a 'Closing session…' status before
dispose runs so the brief pause is explained rather than mysterious.
Two regression tests in memory-tools.test.ts cover (1) dispose returns
within the budget when flushExtractions stalls and the deferred close
still runs once consolidate settles, and (2) unbounded dispose still
runs the full #2320 consolidate-then-close pipeline.
Fixes#3641
EventController.handleEvent rebuilt the editor's status-line top border
synchronously on every session event via updateEditorTopBorder(). During
a long-running eval that fires 5-10 events/s, each rebuild ran
StatusLine.getTopBorder → #buildSegmentContext → getCachedContextBreakdown
→ session.getContextUsage → estimateTokens (with JSON.stringify per
toolCall block) — the render pipeline is throttled to ~30 fps, so most
rebuilds were dropped before painting. Combined with a scheduler that
collapsed cadenceDelay to zero whenever a frame overran the 33ms budget,
the TUI busy-looped at ~40-50% CPU.
Fix:
- Editor gains setTopBorderProvider(): a lazy builder invoked once per
editor render. InteractiveMode installs it in the constructor and on
setEditorComponent, so the rebuild coalesces to the render tempo
regardless of event rate.
- Delete updateEditorTopBorder wrapper (now equivalent to
ui.requestRender) and inline every call site.
- Add adaptive render backpressure: a frame that exceeds
MIN_RENDER_INTERVAL_MS inflates the next scheduling delay to
2 * last_frame_cost, capped at 200 ms, targeting a 50% render duty
cycle instead of pinning the CPU at t=0.
New regression tests:
- editor-top-border-provider.test.ts: provider fires exactly once per
render, wins over eager setTopBorder, falls back when cleared, gets
the correct availableWidth.
- adaptive-render-backpressure.test.ts: cheap frames keep the 33 ms
cadence, a slow frame idles proportionally, pathological frames are
capped at 200 ms.
Verified with bun test packages/tui/test (all 246 relevant tests pass)
and bun test packages/coding-agent/test/modes (455 tests pass). Three
pre-existing agent-session-handoff snapcompact failures on main are
unrelated (snapcompactSupportedChars binding).
Fixes#4145
The postmortem SIGTERM/SIGHUP/uncaughtException handlers only ran the registered
cleanup callback list before process.exit, and the only session-related callback
was session-manager-flush. So a real kernel signal (terminal close, process
manager killing omp, IDE stop) skipped saveDraft, session.dispose (session_shutdown
emit, owned async job disposal, kernel disposal, MCP disconnect, browser tab
release), and violated the SessionShutdownEvent docstring contract that promises
delivery on SIGINT/SIGTERM. The LSP client also owned its own SIGINT/SIGTERM
handlers that called shutdownAll then process.exit(0), which could race postmortem's
async runCleanup and short-circuit the session teardown.
- Extracted a promise-memoized createSessionTeardown helper (modes/session-teardown.ts)
that snapshots the editor draft, persists it via sessionManager.saveDraft, then
invokes session.dispose. A saveDraft failure is logged but never aborts disposal.
- Memoized AgentSession.dispose so the keypress path and the signal path share one
settled promise and cannot double-emit session_shutdown or double-drain the owned
AsyncJobManager.
- Registered the teardown on postmortem as "session-teardown" in InteractiveMode.init,
replacing the narrower session-manager-flush callback. InteractiveMode.shutdown
now delegates the draft+dispose steps to the same helper.
- Replaced the LSP client's SIGINT/SIGTERM handlers with a "lsp-shutdown" postmortem
callback so LSP cleanup runs alongside every other session teardown instead of
racing them via process.exit(0). beforeExit is unchanged.
- Added session-teardown.test.ts covering: draft-then-dispose ordering, disposal
after saveDraft rejects, empty-string clears stale sidecar, promise memoization
under concurrent invocation, and snapshot-at-first-call semantics.
Fixes#4080
- Anchors the incomplete-todo reminder block inside the scrollback transcript instead of a floating live container.
- Eliminates duplicate reminder copies piling up in terminal scrollback during terminal reflows.
- Removes the dedicated `todoReminderContainer` and simplifies state synchronization on todo reload.
- Updates tests to verify sequential reminders commit as separate blocks and are left intact when tools succeed.
Captured the configured thinking selector when entering plan mode so approving a plan restores auto instead of the provisional concrete effort. Reloaded DEFAULT(auto) badges from defaultThinkingLevel and covered the plan-approval handoff plus /model display.
Fixes#3901
User-invoked skills (typed /skill:, steered, follow-up, interrupted/
resumed via compaction, ACP, RPC) only appended a bare "Skill: <path>"
line, so the model neither learned the user had invoked that specific
skill nor where the skill directory was. Relative paths in skill bodies
(scripts/, templates/) could not be resolved.
Route all user-invoked paths through a self-identifying, baseDir-aware
prompt template; keep hidden autoload skills on the minimal non-user
format. Interactive skillCommands now carries the loaded Skill object
instead of a bare path so baseDir flows through without reconstruction.
The invocation kind defaults to "user" to keep buildSkillPromptMessage
source-compatible.
Op: correct
Restores: spec:user-invoked-skill-prompt-self-identifies-and-exposes-skill-directory