- Added unified `omp setup speech` flow with JSON/check modes and model picker.
- Added local STT pipeline with sherpa workers, recorder/download flow, and streaming inference.
- Added local TTS pipeline with `omp say`, backend selection, and streaming vocalization.
- Replaced legacy speech settings with unified `speech`/`speechgen` configuration keys.
- Added a space-hold gesture state machine in CustomEditor, tracking repeated spaces, detecting holds beyond SPACE_HOLD_THRESHOLD, and firing start/end callbacks via a release timer.
- Hooked editor space-hold callbacks in InputController so STT toggles on hold start and again on release when STT is enabled.
- Added tests for space-hold start/stop behavior and updated keybinding docs to describe the hold-to-record STT workflow.
- Stopped and cleared the working loader before auto-compaction and auto-retry.
- Ensured stale loadingAnimation is removed so agent_start recreates the Working loader.
- Set catalog model input/output costs to 0.09/0.18 and reduced maxTokens to 65536.
- Added `paste.largeMenuThreshold` setting with default 100 and values 0/100/250/500/1000.
- Added `Editor.onLargePaste` and `Editor.insertPaste` to intercept oversized pastes and expand markers.
- Added threshold-based large-paste routing to show the menu and wrap oversized content for code/XML or attachment fallback.
- Added tests for large-paste interception, fallback behavior, short-paste handling, and marker expansion.
- Added an `isEmpty` getter on `AgentHubOverlayComponent` to report whether no subagent rows were loaded.
- Extended `showAgentHub` with an optional `requireContent` flag so the overlay is disposed early when empty under the double-<- path.
- Added tests for double-<- gating behavior with no subagents, with subagents, and explicit hub-open behavior.
- Moved the ctrl+p model-role cycle rendering from `showStatus` to a dedicated anchored cycle container above the editor.
- Updated InteractiveMode to rebuild the cycle container in place and auto-clear the track after 4 seconds.
- Added tests that validate no scrollback stacking, in-place replacement, and timer-based clearing behavior.
- Hardened left-arrow double-tap handling with a new detector that only fires on a second tap in a 40--500ms window.
- Reused that detector for both Agent Hub opening and focused-subagent return so terminal-synthesized burst taps no longer trigger navigation.
- Added gesture tests for deliberate doubles, burst suppression, minimum-gap filtering, and focused-subagent unfocus behavior.
`restoreQueuedMessagesToEditor` prepended queued text but appended queued
images to `pendingImages`. Positional `[Image #N]` lookup at submit time
therefore broke whenever the editor draft already held pending image(s):
queued markers (numbered 1..K against their own image list) collided with
draft markers (1..M) and resolved to the wrong images; queued images
landing past slot M were orphaned.
Add `shiftImageMarkers(text, offset)` to `image-references.ts` and have
`restoreQueuedMessagesToEditor` walk each queued message in order,
shifting its markers by the running pending-image count (existing draft
images plus images already pulled in from earlier queued messages). Draft
markers stay untouched because draft images keep their original slots.
Paste markers are left alone — those are owned by the editor's paste store,
not the pending-image buffer, and queued message text never carries
unmaterialized `[Paste #N]` because the editor expands paste markers in
`getExpandedText()` before `onSubmit` fires.
Regression test seeds a draft image + a queued image-message in
`input-controller-compaction-image.test.ts` (per acceptance) and locks the
marker -> image mapping after restore. Unit tests for
`shiftImageMarkers` cover the WxH tail, Paste-marker passthrough, and
the zero-offset no-op.
Fixes#2531
- 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.
restoreQueuedMessagesToEditor only drained the agent steering/follow-up
queue via session.clearQueue(), but the "Alt+Up to edit" pending-bar
hint is rendered for both that queue and ctx.compactionQueuedMessages.
Messages typed while the session was compacting -- including /skill:*
follow-ups, which the follow-up path routes to the compaction queue
before its skill check -- were advertised by the hint yet unreachable,
so Alt+Up reported "No queued messages to restore".
Drain compactionQueuedMessages alongside the agent queue, merged in the
same order the pending bar renders (session-steer, compaction-steer,
session-follow-up, compaction-follow-up). The existing text-join, image
hand-back, and abort paths operate on the merged list unchanged.
Addresses Codex review on #2520:
- Cancel path: `#approvePlan` returned on `compactOutcome === "cancelled"`
without restoring the deferred pre-plan model, stranding the next turn on
the plan model and leaking `#planModePreviousModelState`. The model
transition now runs for the cancelled outcome too (the operator aborted
only the compaction, not the approval) before the early return.
- Queue-flush ordering: `executeCompaction` flushes input queued during
compaction before returning, so the post-return model switch landed after
the queued turn began streaming (deferred one turn via #pendingModelSwitch).
Added a `beforeFlush(outcome)` hook to `executeCompaction`/`handleCompactCommand`;
`#approvePlan` runs the transition through it (and idempotently re-runs it
afterward to cover the message-count short-circuit).
Tests cover the cancel restore and the before-flush ordering.
executeCompaction added a transcript Spacer(1) the handoff path never adds; it leaked as an orphan blank line on cancelled/failed compaction (only the OK branch cleared it via rebuildChatFromMessages). Removed it, and on the OK branch the loader is stopped + statusContainer cleared before the rebuild so the live loader no longer composites over the reconciled transcript. The finally block stays as the idempotent cancel/fail safety net.
With display.showTokenUsage on, the usage row was rendered inside the
assistant block above the turn's tool blocks. Finalizing the assistant
block was therefore deferred, and the late append recommitted the
already-committed tool rows, duplicating them in scrollback (worst with
parallel tool calls).
The assistant block now always finalizes as soon as a tool-call appears,
and the usage row is emitted as a standalone finalized block below the
turn's tool blocks across all three render paths (live event-controller,
transcript rebuild, agent-hub). The now-dead setUsageInfo/#usageInfo path
on AssistantMessageComponent is removed.
- Replaced queued-message interrupt flow with session abort calls on empty submit and escape.
- Removed interrupting state and notifyInterrupting teardown paths from abort handling.
- Updated AgentSession queue operations to use shared steering and follow-up queue views.
- Propagated isAborting through session state and collab payloads to suppress late updates.
- Added a credential-picker `/logout` flow for selecting one OAuth account.
- Added optional `/logout` provider argument and unknown-provider error handling.
- Changed logout handling to delete only the chosen credential and keep others.
- Added AuthStorage APIs to list and remove credentials by id, with remote deletion hook.
- Filtered dot-only or blank thinking blocks so they no longer render as assistant thought.
- Adjusted assistant-message and streaming-reveal logic to use visible-thinking helpers for consistency.
- Coalesced repeated interrupt-and-flush calls into one queued-steer resume flow.
- Retried queued continues on AgentBusyError after waitForIdle up to a 30s timeout.
- Handled queue-flush failures in input controllers with warning logs and TUI error display.
- InputController now acknowledged empty-submit interrupts before flushing queued steering or queued-abort paths to preserve interrupting feedback.
- EventController now recreated the loading animation on interrupt acknowledgement and deferred end-of-turn teardown while queued resume recovery was still arming.
- AgentSession exposed an isResumingQueuedMessages phase around interruptAndFlushQueuedMessages so stale agent_end events no longer tear down the loader prematurely.
- Short-circuited agent-end handling when a session was still streaming.
- Skipped turn-teardown steps for superseded agent_end events that arrived out of order.
- InputController now dispatched Esc to active viewSession operations, aborting compaction, handoff, and retry directly.
- Removed competing onEscape handler swaps across command and event controllers so overlapping auto/manual flow events no longer overwrote cancellation callbacks.
- Compaction now propagated fetch options and rethrew aborted signals so cancellations were not treated as remote failures.
- InputController now clears typed editor text and triggers a render when Esc is pressed with content, then resets the double-Esc timer.
- The double-Esc selector path was updated to reset the terminal display before opening tree or branch message selectors.
- Escape handling tests were expanded with settings setup/teardown to verify default, branch, and draft-clearing Esc scenarios.
- Added a focused-agent left-key input listener that consumes double left taps when the input is empty.
- Routed focused session left-tap behavior through #handleFocusedLeftTap so it matches the Esc-style unfocus timing.
- Updated tests/fixtures for the new listener flow and refreshed a hashline block-edit error assertion.
- 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.
- Added `src/export/share.ts`: `/share` now snapshots the session JSON, gzips and seals it with a fresh AES-256-GCM key, and pushes the blob to a secret gist or the share server (1 MB cap with image/string/entry truncation via `sealToFit`); links are `<serverUrl>/<id>#<key>` with the key only in the fragment.
- Added `share-loader.js` and `scripts/generate-share-viewer.ts` building the static viewer the relay serves at `GET /s/<id>`: it fetches the sealed blob, decrypts in-browser, and hands the JSON to the export template via `window.__OMP_SESSION_DATA__`.
- Reworked the `/share` command in `command-controller.ts`/`builtin-registry.ts` off the plaintext-gist HTML upload, exported `LoadedCustomShare`, and exposed the session `SecretObfuscator` getter on `AgentSession` for redaction.
- Added `share.serverUrl` and `share.redactSecrets` settings backed by `DEFAULT_SHARE_URL` from pi-wire.
- HTML exports now embed subagent transcripts: `collectSubSessions` walks `<session>/<AgentId>.jsonl` recursively into `SessionData.subSessions`, with `includeSubSessions` opt-out and the exported `buildSessionData` reused by share snapshots.
- Added `share.test.ts` (snapshot/seal/server-url contracts) and `export-subsessions.test.ts`.
- Added write-token generation and validation to distinguish writable and read-only guests.
- Added deep-link support using `https://<relay>/#<link>` with 32/48-byte collab secrets.
- Added full-link and key-only semantics where full links grant writes and key-only links are view-only.
- Added /collab view/status/stop command updates with read-only participant status and join hints.
Reviewer flagged that the bracketed-paste path is untrusted terminal input —
ANSI escapes, control chars, newlines/tabs, or a multi-hundred-char path
would corrupt the status line (per AGENTS.md TUI sanitization rules) and
leak the absolute home-dir path.
The new ENOENT diagnostic now feeds the path through sanitizeText (strip
ANSI/C0/C1 controls), collapses CR/LF/TAB to single spaces, runs it
through shortenPath (collapse home → '~'), and truncateToWidth-clamps it
to TRUNCATE_LENGTHS.CONTENT (80) before interpolating into either the SSH
or local status string. Added a third assertion to the repro test
defending the contract: ANSI/control bytes never reach the status, and
the displayed path is bounded below the input length.
Refs PR #2376
When a local terminal forwards a bracketed-paste containing an image-file
path and the omp process is itself running on the remote end of an SSH
session, the path is on the user's local machine and unreachable from the
remote filesystem. The path-as-text fallback in handleImagePathPaste then
made it look like the image was attached when in fact only a useless
absolute path entered the editor and the bytes never crossed.
Distinguish ENOENT from other read failures: drop the misleading
path-as-text degrade, and surface a clear status that names the file and
— when SSH_CONNECTION/SSH_TTY/SSH_CLIENT indicate the remote end of an
SSH session — points the user at the clipboard image-paste shortcut so
the bytes actually cross. The ImageInputTooLargeError and unknown-error
branches keep their existing fallback so genuine type issues still
yield the user's input back to them.
Fixes#2375
Dynamically assigns segment colors by sweeping across the full HSV hue spectrum based on their track position. This ensures each segment has a distinct, predictable hue and removes the need for explicit `color` assignments.
Updated thinking visibility toggles to refresh assistant blocks in place instead of rebuilding the transcript, preserving pending user submissions and loaders before streaming starts. Added a regression test for the Ctrl+T pre-stream gap.\n\nFixes #2370
- Updated snapcompact selectors and previews to pass `ShapeTarget` into `resolveShape` so `auto` is model-tuned.
- Applied `providerFrameBudget` in session compaction so generated archives stay within provider image caps.
- Replaced inline hard-coded image limits with `providerImageBudget` and skipped rasterization at cap.
- Added OpenRouter inline-transformer tests for cap exhaustion and existing-image budget exhaustion behavior.
- Added `SnapcompactShapePreview` (`snapcompact-shape-preview.ts`), rendering the highlighted shape variant over the sample text in `snapcompact-shape-preview-doc.md`; `auto` resolves to the active model's winning shape.
- Wired the preview as a footer component below the interactive rows in `settings-selector.ts`.
- Passed `modelApi`, `imageBudget`, and `requestRender` through `SelectorController` so the preview can rasterize against the session model and request repaints.
- Added `handleUsageResetCommand` support to list and redeem usage reset credits.
- Refactored `/usage` into `show` and `reset` subcommands and removed `/reset-usage`.
- Handled ACP/TUI `/usage` flows so `show` reports usage and `reset` redeems credits.
- Kept selected theme setting values dirty-colored while selected in the UI list.
- Added AuthStorage.listResetCredits to query each stored Codex account from the reset-credits endpoint and return live availability plus active or error state.
- Exposed the new status fetch through AgentSession and switched reset-usage selectors and commands to consume it via toResetUsageAccounts.
- Updated reset-usage UI and slash-command output to show per-account errors and updated empty-state messaging when resets could not be loaded.