- 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.
- Extended GitHub URL parsing to recognize commit paths and extract commit refs.
- Implemented a commit renderer that fetches commit metadata, stats, and file patches from the GitHub API.
- Added a handler branch to render commit URLs to markdown with `github-commit` results metadata.
- Added a generation-aware early-exit check in `#waitForPostPromptRecovery` to return when the prompt turn is superseded.
- Passed the current prompt generation into the post-prompt recovery wait after prompting.
- Ensured aborted prompts no longer block while later queued retry/follow-up turns drain.
- Updated JS eval helper option parsing to accept positional optional args or a trailing plain-object options argument, while rejecting mixed or invalid forms.
- Updated `read` to route non-`local://` URI paths through the `read` tool with line selectors so offset/limit slicing works for artifact-style resources.
- Expanded JS executor tests for positional reads, nullable slot skipping, and delegated URI read slicing.
- Removed the `task` embedded agent's explicit `thinkingLevel` assignment.
- Updated the `quick_task` agent to use `Effort.Medium` instead of `Effort.Minimal`.
- Updated OpenAI context promotion linking to resolve target models by parsed version and provider/API match instead of fixed bare ids.
- Scanned available siblings to select the plainest matching gpt-5.4 fallback so namespaced, dotted, and dated 5.5 variants promote correctly.
- Adjusted the TUI render stress shadow writer to ignore alternate-screen regions and replay only normal-screen bytes after exits.
- Added 8on22-bw and 11on16-bw variants with spacing-tuned defaults for snapcompact.
- Changed provider defaults/mappings to use 11on16-bw for Anthropic and 8on22-bw for OpenAI/Google.
- Updated model frame rules so claude/fable/opus and gemini/gpt families map to new shapes and 2048 billing behavior.
- Fixed non-multiplexer resize dragging by using alternate screen during transient resize frames.
- 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 OpenAI-compatible compat metadata for endpoints that allow tools but reject forced tool_choice. OpenCode Go kimi-k2.7-code now downgrades resolve-gate forcing to auto tool selection while preserving thinking-mode request state.\n\nFixes #2546
- Updated the resize fast-path repaint to remove the terminal full-screen clear and instead overwrite the viewport in place at home, rewriting each row via `#lineRewriteSequence`.
- Passed the viewport width into `#emitResizeViewport` so each row self-clears only its tail with CSI K while resize drag remains active.
- Added coverage in `resize-viewport-defer.test.ts` confirming drag-time writes omit ED2/ED3 clears and that ED3 is emitted when the resize settles.
- 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 a smart poll-wait mode to AsyncJobManager with per-owner escalation ladder logic and a reset timer for idle pauses.
- Updated job polling to use the adaptive wait when `async.pollWaitDuration` is `smart` and to record poll completion timing for subsequent waits.
- Expanded async job settings and tests so `smart` is the default option and escalation, reset, and owner isolation behavior are covered.
- 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.
- Routed `customType: "handoff"` messages to the compact divider path in Agent Hub and UI helpers.
- Added handoff summary expansion that extracts context text and strips `<handoff-context>` wrappers.
- Refactored shared divider rendering into `SummaryDividerComponent` used by compaction and handoff messages.
- Added a `snapcompact-savings.jsonl` append-only journal for per-tool savings records.
- Extended `SnapcompactInlineTransformer` to compute `savedTokens` and emit savings to an optional sink.
- Integrated `createSnapcompactSavingsRecorder` into `createAgentSession` for session-aware tool-result metrics.
- Skipped journaling entries without a session and deduped duplicate `toolCallId`s per recorder lifecycle.
- Added tests for savings-sink emissions and savings-journal read/write 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.
The continue() mock used mockResolvedValue(), which never consumed the
queued follow-up. After threshold compaction, #endInFlight ->
#drainStrandedQueuedMessages -> #scheduleQueuedMessageDrain reschedules a
zero-delay continue whenever messages remain queued, so the no-op mock spun
the drain into an unbounded microtask loop that allocated until the test
worker OOM'd (~101GB) and segfaulted, taking sibling tests down with it.
Mirror real continue() semantics (it polls and consumes the queue) by
clearing the queues in the mock, matching the sibling steer-idle-drain
tests. The drain now settles after one resume.
`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.
Address PR review: the makeCtx session stub typed clearQueue() as
returning string[], forcing an `as unknown as` cast when the ordering
test overrode it with message objects. Type the stub to the real
RestoredQueuedMessage[] shape so the override needs no cast and shape
drift is caught at compile time.
runGuidedGoalTurn sent the rendered interview transcript to the plan/slow
provider as raw text, so a secret typed into the rough goal or an answer
bypassed the session's redaction contract. Route the transcript through the
session obfuscator before the request and deobfuscate the echoed question /
objective before it is displayed or the goal starts (no-op when no secrets
are configured).
Add a display-only `activity` field to AgentRef plus `setActivity`, fed
from the subagent progress chokepoint with a short gist of the agent's
latest intent (or current tool). Render it in the `irc list` output, the
subagent peer roster, and the TUI peer card, beside the role-derived
display name. setActivity emits no event — the roster reads on demand —
so the per-tool-call rate stays off the registry listener path. Peers
with no activity render without a dangling clause.
Refs #2470
Op: extend
When a spawner with remaining depth capacity spawns generic role-less
workers (a task/quick_task spawn without a `role`, or the same agent
cloned >=2x all without roles), TaskTool.execute appends a non-blocking
advisory steering it toward tailored specialists. Gated on DepthCapacity
so a leaf at max recursion is never nudged; the task-tool depth gate is
extracted into a shared `canSpawnAtDepth` helper reused by both the tool
gate and the advisory.
Refs #2469
Op: extend
Document the `role` parameter in the task-tool description (both the
batch and single-spawn shapes) and make tailored specialists the default
rule, not the exception. Direct a recursing worker to pass a `role` for
each sub-specialist. Activates the role field from #2467 for the model.
Refs #2468
Op: extend
Add an optional `role` field to the task spawn contract, threaded end to
end through resolveSpawnItems/spawnParamsFor into the executor. A role
injects a specialization preamble into the subagent system prompt and
becomes the subagent's display name and telemetry identity (label
normalized, length capped), so delegated trees stop being clones of one
generic worker. Empty/absent roles fall back to the agent type name.
Refs #2467
Op: extend
Addresses Copilot review on #2522: the nested ternary building the /fast
status label was duplicated across the ACP and TUI handlers. Extracted
formatFastModeStatus(session) (a switch over serviceTier) so the wording
stays consistent and new tiers/scopes are added in one place. No behavior
change.
Addresses Copilot + Codex review on #2521:
- computeEditorMaxHeight returned 1 on terminals too small to host both the
editor and the chrome reserve, but the bordered editor never renders fewer
than 3 rows (2 border + 1 content). The cap now floors at that real minimum
(EDITOR_MIN_RENDERED_ROWS) so it no longer misreports the rows the editor
occupies; rendering is unchanged, and the contract is documented honestly
(reserve holds once terminalRows >= 7).
- #resolveOverlayLayout now always resolves maxHeight (?? availHeight), so the
maxHeight !== undefined branch in effectiveHeight, the composite slice guard,
and the number | undefined return type were dead. Tightened all three.
- Mirrored the maxHeight-default contract in the render stress oracle
(resolveExpectedOverlayLayout + compositeExpectedOverlays) so the randomized
sweep validates the real clipped geometry instead of the obsolete unclipped
one; updated the oracle helper test expectation accordingly.
Editor-height tests rewritten to assert the real contract (reserve when the
terminal can host both; pinned to the bordered minimum below that).
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.