ImmutablePrefix caches system prompt + tool specs after first build()
so subsequent turns reuse identical byte sequences. AppendOnlyLog
converts messages once via syncMessages() and only appends deltas
on further turns — prior-turn bytes stay stable.
- New module: packages/agent/src/append-only-context.ts
StablePrefix, AppendOnlyLog, AppendOnlyContextManager
- AppendOnlyContextManager added to AgentLoopConfig
- Wired into streamAssistantResponse in agent-loop.ts
- Toggleable via provider.appendOnlyContext setting (auto/on/off)
- Default auto enables for deepseek provider
- 38 tests covering prefix, log, sync, compaction handling
- /session info surfaces current active state
WSLg exposes WAYLAND_DISPLAY, so readImageFromClipboard() took the
native arboard path on WSL2. arboard::Clipboard::get_image() returns
ContentNotAvailable on WSLg because the Wayland clipboard does not
carry image payloads from the Windows clipboard, and that surfaced as
silent 'No image in clipboard' on Ctrl+V.
Detect WSL via WSL_DISTRO_NAME / WSL_INTEROP and read the image with a
PowerShell one-liner that emits base64-encoded PNG bytes from
[System.Windows.Forms.Clipboard]::GetImage(). Fall back to the native
bridge when PowerShell returns nothing, exits non-zero, or is missing,
so non-WSLg Wayland setups continue working unchanged.
Fixes#1280
- Changed hashline format to canonical `§` section headers and `"`/`"`/`≔` operations across grammar, parser, and docs.
- Reworked range and op parsing so single anchors are valid, legacy `-`/`-=` ops error, and empty `≔` payloads now delete ranges.
- Removed legacy `HL_EDIT_SEP`, `$HSEP$`, and `hsep` plumbing, adopting raw payload lines.
- Updated execution, input, renderer, and streaming flows to use `HL_FILE_PREFIX` and `HL_OP_CHARS` helpers.
- Updated session-stats parsing to `§`/`≔`/`"`/`"` format, patch envelopes, and bumped parser versions.
- Removed the hashline-separator benchmark script and its PI_HL_SEP job orchestration.
- Added a shared `interruptHint()` utility in the modes shared module to generate the interrupt suffix with themed bracket glyphs.
- Replaced hardcoded working-message interrupt text in interactive and event controllers with calls to `interruptHint()`.
- Updated working-message rendering to recognize and strip the new themed hint when applying shimmer styling.
- Updated OpenAI completions parameterization to honor `disableReasoning` on effort-based compatible models by sending the minimum supported effort.
- Expanded commit and title generation token budgets so reasoning models can return output after internal thinking while non-reasoning calls keep existing limits.
- Switched title generation to request a `set_title` tool call, added extraction from tool-call arguments, and updated tests for the new behavior.
- Updated interactive mode to render the loader spinner with the current working message accent when available.
- Added a fallback to theme accent and a reset color code when no working-message accent is provided.
- Loading message rendering now derived session-specific accent colors and applied them to shimmer output when a session name was available.
- Shimmer palettes were updated to accept raw ANSI color values as well as theme color names during compilation.
- A unit test was added to confirm shimmer text rendered with a supplied ANSI crest color.
- Added SettingsList#setItems to replace items and clamp selection to a valid index after updates.
- Updated SettingsSelector to rebuild active memory items on backend changes and skip refresh when appropriate.
- Switched MCP wizard and command spinners to theme frames with themed initial frame and 80ms updates.
- Reworked welcome intro animation for a 3-second eased sweep with optional shine blending.
- Added memory backend refresh tests and aligned package changelogs with the updated behavior.
- Added optional `onBeforeYield` configuration and `setOnBeforeYield` in Agent, executed before follow-up checks.
- Added `YieldQueue` to `AgentSession`, with setup/teardown and streaming/idle flush via `setOnBeforeYield`.
- Replaced immediate async-result follow-up dispatch with queued batch entries, including stale-state suppression.
- Added MCP follow-up queueing in SDK, deduplicating updates by `serverName` and `uri`.
- Added changelog entries for `onBeforeYield`, async-result batching, MCP dedupe, and `display.shimmer` modes.
- Added yield queue unit tests for streaming emission, debounced idle batches, stale filtering, and error isolation.
- Added `display.shimmer` setting (`classic`, `kitt`, `disabled`) with default `classic` and UI metadata.
- Added shimmer mode resolution with defaulting plus classic/KITT intensity tier profiles and thresholds.
- Added `getFgAnsi` handling across shimmer compilation and progress-bar themes, with fallback ANSI output defaults.
- Reworked shimmer rendering to coalesce same-tier segments, cache palettes by symbol, and map disabled mode to mid-tier.
- Tuned loader timing to a 16ms render interval and 80ms spinner stepping for smoother ~60fps updates.
Appended the stealth iframe to documentElement when document.head is not available during new-document evaluation.
Added regression coverage for the null-head bootstrap path.
Fixes#1267
The native rule discovery provider only scanned the rules/ subdirectory under .omp/ and ~/.omp/agent/. The documented top-level RULES.md file (per https://omp.sh/docs/context-files) was silently ignored — neither ~/.omp/agent/RULES.md nor <repo>/.omp/RULES.md was ever read.
Load both as synthetic rules with alwaysApply forced to true (the whole point of RULES.md is to be reattached every turn). Project scope walks up from cwd to repoRoot using the same nearest-.omp/ search AGENTS.md uses.
Fixes#1266
- Added a shallow string-record sanitizer to normalize unknown model role values before applying updates.
- Updated setModelRole and overrideModelRoles to base persistence on global roles while retaining matching runtime overrides.
- Added model role override tests covering non-persistent temporary overrides, override clearing, and consistency after role updates over overrides.
- Added the active session model to compaction candidate selection before role-based candidates.
- Updated compaction routing so role-based models are only considered after the current chat model.
- Added a regression test proving an Anthropic session prefers its active model over `modelRoles.default` on OpenAI.
- Added configurable shimmer palettes by introducing ShimmerPalette and ShimmerSegment types.
- Added a new shimmerSegments helper to render a single sweep across multiple text segments with optional per-segment palettes.
- Updated working-message rendering to style the interrupt hint using a separate borderAccent palette.
- Added a new shimmerText helper that computes a moving accent shimmer band across characters.
- Updated the interactive mode loader and slash-command ASCII bar renderer to use shimmer styling, with interrupt hints kept dim.
- Added tests for shimmer-enabled progress rendering and verified the visible bar output remains correct.
Cleanup tail of 2817c582a — the search archive commit (78841798f) had
inadvertently included the redaction.ts import and wiring in
search.ts. That ad-hoc redactor was already reverted; this drops the
matching call sites so the file no longer references the deleted
module. SecretObfuscator (gated on `secrets.enabled`) is the supported
path for redaction.
Adds a focused redaction utility that targets well-known token shapes
(AWS, GitHub PATs/tokens, Slack, OpenAI-style sk-, JWT) plus a
key/value heuristic for env-style lines whose key contains SECRET /
TOKEN / PASSWORD / API_KEY / PRIVATE_KEY, plus Bearer/Basic Authorization
header values. Replacements are tagged `#REDACTED:<hint>#` so callers
can tell why each value was scrubbed.
Wired into read, search and ssh tool outputs. Each call site appends a
`[redacted N secret-like values]` footer when at least one value was
scrubbed so the model knows the output was modified.
Gated behind a new `tools.redactSecrets` boolean setting (default true).
Sandboxed tests that intentionally surface secret-shaped fixtures can
disable it via Settings.isolated({ "tools.redactSecrets": false }).
A per-cell timeout used to kill the persistent Python kernel, losing
all session state. The kernel.ts timeout path now sends SIGINT first
(letting the cell raise KeyboardInterrupt) and only escalates to
shutdown after a 5s grace window if the interrupt is ignored.
KernelExecuteResult gains an optional kernelKilled flag (defaulting to
false, propagated by the escalation timer and the unexpected-exit
handler). executor.ts formats two distinct timeout annotations: one
that says the kernel is still alive and reset:true would clear state,
one that says the kernel was killed and will be recreated.
#scheduleTodoAutoClear / #runTodoAutoClear used to splice completed and
abandoned tasks out of #todoPhases on a 60s (later 30min) timer. The
mutation made earlier completions vanish from phase counts ("5 tasks"
dropped to 4) and contradicted the model's own claim of progress.
The autoclear path is removed entirely. Canonical #todoPhases is only
mutated by explicit todo_write calls. formatSummary's denominator
(`current.tasks.length`) now stays stable across tool calls, so phase
counts include completed tasks until the model explicitly removes them.
Leaves the `tasks.todoClearDelay` setting in place (inert) to avoid
changing the schema in this patch.
`read` accepts `archive.zip:member` selectors but `search` previously
ignored them, returning zero matches even when the member's text
contained the query. resolveArchiveSearchPaths now detects archive
selectors, opens the archive via the shared archive-reader, decodes
UTF-8 members into a scratch tmpdir, and rewrites match paths back to
the original selector before returning. Binary, non-UTF-8, missing
members and unreadable archives surface as a structured error or a
per-archive footer note. Scratch dir is cleaned up in finally.
The read renderer was emitting `LINE+HASH|content` for lines whose
content had been column-truncated for display, but `computeLineHash`
is content-only and recomputes against the disk line. The model copied
the displayed anchor, edit rejected it as mismatched, even though the
underlying file had not changed.
formatTextWithMode now accepts an optional truncatedLines set; in
hashLines mode those lines emit as `LINE|content` (no hash) so the
verifier never tries to recompute against truncated text. Multi-range
and single-range read paths both populate the set when truncateLine
flips wasTruncated.
buildOutputValidator already exists in task/executor.ts but only ran on
the fallback JSON parse path. Subagents that called yield directly with
a data payload skipped validation entirely, so a schema-conforming-
empty-object (e.g. `{}` against a schema requiring `findings`) was
returned as a successful task.
finalizeSubprocessOutput now invokes the validator on every yield path
and on the fallback completion path. On failure the result carries
error="schema_violation", a typed message, the missing required field
list, and a truncated preview of the offending data. exitCode=1 and
isError=true so existing consumers in task/index.ts surface it as a
failed task without code changes.
Adds a shared classifyProviderHttpError helper that maps the well-known
failure shapes (status 401/402/403 and bodies matching credits / quota
/ insufficient) into compact SearchProviderError messages so the
orchestrator advances to the next provider instead of bailing.
Wired into every HTTP-talking provider (codex, exa, gemini, anthropic,
brave, jina, kimi, perplexity, searxng, synthetic, tavily, zai) and the
two wrapped-error providers (kagi via KagiApiError, parallel via
ParallelApiError). The orchestrator now collects per-provider failures
and emits a joined summary ("exa: 403 forbidden; codex: credits
exhausted; ...") when the whole chain fails.
Test updates: web-search-{exa,tavily,kagi} message expectations now
assert the compact "<id>: <status> <reason>" form.
- get op now returns paused goals (was returning null when enabled=false)
- complete op now works on paused goals; previously required enabled=true
which always failed after an interrupt set enabled=false
- create op now allowed after previous goal status is 'complete'; was
incorrectly blocked by the same guard as 'dropped' check
- goal tool is re-added to the active tool set on session reload when a
paused/active goal is persisted to disk; sdk.ts:1599 excludes 'goal'
from initial active tools unconditionally, so restoreModeFromSession
now re-adds it and saves #goalModePreviousTools for later cleanup
- goal_updated event for 'dropped' status now triggers #exitGoalMode
before clearing goalModeEnabled, ensuring the previous tool set is
restored when the agent drops a goal via the tool
- added 'resume' and 'drop' ops to goal tool schema and execute path
- updated goal.md prompt to document new ops and the paused-goal workflow
Fixes#1249
- Extended hashline parsing APIs to accept an optional target path so padding checks can be path-sensitive.
- Refined separator-padding heuristics to warn only on uniform single-space-before-content payloads while skipping those checks for indent-sensitive extensions.
- Threaded section paths through execute/diff callers and added tests covering warning behavior for both exempt and non-exempt file types.
Rolled back binary updater replacements when post-install version verification fails instead of deleting the previous working binary first.
Added a release workflow gate that downloads the published macOS arm64 asset and verifies codesign plus --version before npm publishing.
Fixes#1240
Route /force through a named Ollama tool choice and scope the Ollama request tools to that selected name so local models cannot pick a different tool.\n\nFixes #1236
ACP clients own MCP server configuration via session/new.mcpServers and AcpAgent#configureMcpServers. The ACP session factory previously left enableMCP at its default (true), so createAgentSession ran discoverAndLoadMCPTools on every session/new and the resulting host MCP tools landed in the session tool registry alongside the client-supplied ones. search_tool_bm25 then surfaced only the host tools.
Force enableMCP: false on every session created through createAcpSessionFactory so on-disk discovery is bypassed in ACP mode. Non-ACP modes (omp interactive, print, RPC) keep auto-discovery.
Fixes#1234
Prevent disabled providers from being registered for implicit local discovery and from creating built-in model discovery managers. Added regression coverage for disabled local providers during model registry refresh.
Fixes#1232
- Updated the oracle agent description to present it as a senior engineer who can either consult or execute when delegated.
- Removed the read-only mandate and added requirements to implement changes, run checks, and report completed work in delegation mode.
- Adjusted the procedure and decision guidance to distinguish consult versus implement paths while keeping the scope limited to requested tasks.
Bun's WinHTTP backend can ignore AbortSignal once a TCP/TLS connection
stalls (oven-sh/bun#15275, oven-sh/bun#18536), so Esc never reached the
in-flight `web_search` fetch on Windows and the session froze until
Ctrl+C. Only kimi shipped any timeout at all (server-side); every other
provider passed `signal` to `fetch` with no client-side bound.
Introduced `withHardTimeout(signal, ms=60_000)` in providers/utils and
wired it into every web-search provider's outbound fetch — anthropic,
brave, codex, exa, gemini, jina, kagi, kimi, parallel, perplexity
(api-key and oauth), searxng, synthetic, tavily, z.ai. 60s tolerates
legitimate slow LLM-mediated responses while still guaranteeing the
request settles within a minute when Bun's abort fails to propagate.
Independently, `executeSearch`'s provider-fallback loop swallowed every
`AbortError` as a regular provider error and returned
"All web search providers failed", masking cancellation on every
platform. The catch block now calls `throwIfAborted(signal)` first so a
caller-initiated cancel propagates as `ToolAbortError`.
Fixes#1221
- Updated Perplexity JWT parsing in the ai OAuth utilities to return a far-future sentinel when `exp` is missing and use that when computing token expiry.
- Updated `getOAuthApiKey` to prefer the JWT expiry and normalized legacy one-hour `expires` values to a non-expiring value.
- Updated coding-agent web search token lookup to verify Perplexity credentials against the JWT `exp` claim and treat missing claims as non-expiring.