- Updated the hashline mismatch error to describe anchor mismatches against the current file.
- Rewrote hashline tool instructions to clarify insert payload rules, anchor usage, and avoidance of fabricated hashes.
- Expanded stale-edit detection and tests to recognize the revised anchor-mismatch rejection wording.
- Added an internal accounting-state guard and used it to skip goal usage flushing when accounting was inactive.
- Updated goal abort handling to return early unless accounting or pause logic was required, then paused only a cloned active goal state before committing.
- Aligned related tests/types by tightening OpenAI helper typing and using Tool typings for the goal tool registry.
- Discouraged coreutils in favor of dedicated read/search/find/edit/write tools.
- Prohibited pipe truncation via head/tail since artifacts handle full output.
- Prohibited stderr redirection since stdout and stderr are already merged.
- Added an HTML comment state tracker to prompt formatting.
- Updated ASCII symbol replacement to skip substitutions inside `<!-- ... -->` comment blocks across lines.
- Added tests that preserved comment text while converting symbols outside comments.
- Added GoalRuntime with wall-clock and token accounting, budget steering, and lifecycle operations (create, pause, resume, drop, complete).
- Exposed goal tool as a hidden agent tool, activated only when goal mode is enabled.
- Integrated goal continuation loop in InteractiveMode with auto-submit between turns.
- Added status line segment and theme icons for goal mode state.
- Hardened context usage accounting to tolerate missing session fields by defaulting skills and tools to empty arrays.
- Guarded message and system-prompt token counting with presence checks to avoid access errors on partial session objects.
- Added getOpenAIReasoningModel to wrap the bundled gpt-5-mini model with a non-gpt-5 name in tests.
- Replaced all OpenAI responses test model constructions using gpt-5-mini with the new helper to bypass reasoning-model branching during payload assertions.
- Updated the Exa tool factory to emit details using a raw field rather than response.
- Updated the MCP wrapped Exa tool to emit details using a raw field rather than response.
- Added sync truncation helpers to recursively prepare session entries and externalize image data.
- Reworked session persistence to use synchronous preparation plus `writeSync` with close-state checks.
- Added synchronous session-storage APIs and rerouted write paths to `writeLineSync`/`readTextSync`.
- Added `BlobStore.putSync`, migrated hashing to `Bun.SHA256`, and updated hash tests accordingly.
- Expanded the hashline tool rules to state that patch payloads should only contain newly added lines and must not duplicate existing content.
- Clarified that insertion and replacement operations affect only their specified targets, with adjacent lines remaining unchanged.
- Reinforced widening edits to self-contained syntactic units before selecting the smallest patch operation.
- Extended selector regex and parser to accept ranges like `:5-16,960-973`.
- Ranges are sorted and merged automatically before reading.
- Out-of-bounds ranges surface as inline notices instead of errors.
- Added `#readLocalFileMultiRange` and `#buildInMemoryMultiRangeResult` for file, archive, notebook, and internal URL targets.
- Added formatGenericResponse to handle objects, arrays, MCP content blocks, and primitives.
- Applied formatted output to MCPWrappedTool, createExaTool, and propose-changelog tool.
- Replaced raw response in details field with structured response object.
- Fixed root `cli --help` startup by preventing the config/model-registry initialization cycle.
- Extracted config validation, migration, and loading logic from config.ts into config/config-file.ts.
- Added ConfigFile helpers for migration, validated JSON/JSONC/YAML loading, status caching, and reset.
- Added a regression test that runs `cli.ts --help` with temp HOME/XDG env paths and expects exit code 0.
- Removed ExitPlanModeTool and deleted exit-plan-mode docs/tests, dropping the old approval contract outputs.
- Replaced plan-mode approval flow from exit_plan_mode to resolve across session, SDK, controllers, and discovery.
- Added standing resolve handler accessors and updated resolve routing for queued or standing approval handlers.
- Added PlanApprovalDetails and enforced normalized, validated approval titles with readable plan-file requirements.
- Extended resolve schema and invocation signatures with optional extra metadata and reason trimming behavior updates.
- Updated plan and resolve prompts and changelog guidance to require resolve action, reason, and extra.title for apply/discard.
The earlier exports/cleanup refactor removed @napi-rs/cli from
packages/natives devDeps, breaking every native job. It also left two
test files calling private symbols and a stale KeyId literal:
- packages/natives: re-add @napi-rs/cli (catalog) so node_modules/.bin/napi
exists for build-native.ts.
- test/acp-agent.test.ts: import ACP_BOOTSTRAP_RACE_GUARD_MS from
modes/acp/acp-agent (previously implicit via mass-export refactor).
- test/silent-abort-overlay-render.test.ts: lowercase 'Ctrl+S' -> 'ctrl+s'
to match the KeyId union.
- Updated parseHashlineInputPreviewHeader to strip all leading "@" markers before resolving the preview path, matching existing parser behavior.
- Added a regression test in edit renderer tests confirming both canonical "@@" and longer "@" runs render as clean file paths without extra "@" characters in titles.
- Updated conflict URI parsing to accept `path:conflict://N` and record the removed prefix in `recoveredPrefix`.
- Updated write conflict handling to resolve single or wildcard IDs through shared helpers and append a recovery note when a malformed prefix was stripped.
- Added regression tests for recovered prefixes and end-to-end write-path recovery and documented the change in the changelog.
- Drop duplicated op listing from the reminder; <ops> already defines
the four shapes one block above.
- Replace hardcoded `~` with the {{hsep}} helper so the reminder
tracks PI_HL_SEP / HL_EDIT_SEP overrides, matching the rest of
hashline.md and addressing Codex review on #1063.
- Shrink the WRONG/RIGHT example (smaller anchor, no padding runs)
since the reminder's only job is contrasting on-line vs next-line
payload placement.
- Added optional `hide` metadata to skill capabilities so `SKILL.md` frontmatter intent is preserved when skills are loaded.
- Filtered system prompt skill rendering to exclude `hide: true` skills from the `<skills>` listing while keeping them loadable.
- Removed export leakage by demoting many helper and const symbols to module-local scope.
- Renamed underscore-prefixed internals and cache fields, then updated related references and `satisfies never` checks.
- Deleted obsolete logic branches and helpers, including harmony-stream interruption flow and unused benchmark runtime helpers.
- Updated Biome config and manifests by broadening lint coverage and removing an unused `@napi-rs/cli` dev dependency.
- Adjusted tests and utilities to use renamed test helpers and remove redundant private test-only helpers/locals.
Models frequently write '+ ANCHOR| CONTENT' instead of '+ ANCHOR'
followed by '~CONTENT' on the next line. Add a compact FORMAT block
between </ops> and <rules> with a WRONG/RIGHT example that directly
addresses this failure mode.
Codex review flagged that the silent-abort sentinel
("__omp.silent_abort__") persists into AssistantMessage.errorMessage
but three downstream consumers render errorMessage verbatim:
- session-observer-overlay.ts: renders "✗ Error: __omp.silent_abort__"
when content is empty (confirmed user-visible today)
- print-mode.ts: writes marker to stderr and exits non-zero (latent;
plan-mode→compact not reachable from print mode today, but unguarded)
- acp-agent.ts: emits marker as agent_message_chunk text to ACP
clients when message has no other notifications (latent)
Add isSilentAbort() guard at each site. Extend the SILENT_ABORT_MARKER
consumer list in messages.ts doc comment to include all six consumers.
Add regression tests: overlay (2 tests), print-mode (2 tests), ACP
replay (1 test).
Op: correct
Restores: spec:silent-abort-marker-never-surfaces
When an MCP server uses OAuth Dynamic Client Registration (RFC 7591) and
no client_id is pre-configured, MCPOAuthFlow registers a fresh public
PKCE client on each authorize, captures the issued client_id into a
private field, then discards it once the flow object goes out of scope.
At refresh time, MCPManager#resolveAuthConfig calls refreshMCPOAuthToken
with auth.clientId from mcp.json — which is empty for these servers —
so providers that require client_id on the refresh grant (e.g. Linear at
mcp.linear.app/token) reject with HTTP 401 invalid_client. The user is
forced to /mcp reauth manually every time the access token expires.
This change threads the resolved/registered client credentials back out
of the OAuth flow and persists them into mcp.json so refresh has what
it needs indefinitely:
- MCPOAuthFlow exposes resolvedClientId / registeredClientSecret getters.
- MCPCommandController#handleOAuthFlow returns OAuthFlowResult with
credentialId + clientId + clientSecret, populated from the flow's
post-login state.
- The initial-connect non-wizard path and /mcp reauth path persist the
returned client credentials into both auth.{clientId,clientSecret}
(used at refresh) and oauth.{clientId,clientSecret} (used by future
/mcp reauth to skip re-registration).
- The wizard's onOAuth callback signature now returns the same shape;
#launchOAuthFlow folds the registered credentials into wizard state so
the final mcp.json entry built by #buildServerConfigWithAuth includes
them under auth.{clientId,clientSecret}.
Servers that configure a static oauth.clientId in mcp.json (Notion,
Slack, Datadog) are unaffected: #tryRegisterClient short-circuits, the
returned clientId equals the configured one, and the write-back is a
no-op.
Adds two MCPOAuthFlow unit tests covering both paths.
/simplify pass on 4882d1e38. Three small cleanups, no behavior change.
* Extracted the inline 50ms bootstrap-race guard into an exported
ACP_BOOTSTRAP_RACE_GUARD_MS constant at the top of acp-agent.ts.
Source uses it in the #scheduleBootstrapUpdates setTimeout. Tests import
it and call a new waitForBootstrapGuard() helper (constant + 30ms slack
for setTimeout drift) instead of three hardcoded Bun.sleep(80) sites —
tests now bind to the source-of-truth instead of dueling magic numbers.
* Consolidated four block comments that all narrated the same race story
into one canonical explanation at the install site (#scheduleBootstrapUpdates).
Field declaration, #registerPreparedSession, and the setSessionConfigOption
handler keep brief one/two-line pointers. Net change is roughly 30 lines
of comments removed without losing the diagnosis.
* Trimmed the handler-site thinkingHandledBySubscription comment from six
lines to three; the local-variable name carries the intent.
Verified:
* bun test test/acp-agent.test.ts: 11/11 pass
* biome check on touched files: clean
* No behavior change (no test had to be updated)
Co-Authored-By: omp <noreply@oh-my-pi.dev>
Addresses codex review on #1060: an extension session_start handler that
calls setThinkingLevel via the exposed extension action (line 1541) would
have run BEFORE #registerPreparedSession set the record into #sessions and
BEFORE the session/new response was delivered to the client, causing
config_option_update to be pushed for a session id the client did not yet
know about. This is the exact race that #scheduleBootstrapUpdates already
documents and guards for available_commands_update / session_info_update
(Zed's 'Received session notification for unknown session' drop).
Moved the session.subscribe(...) installation out of #registerPreparedSession
and into #scheduleBootstrapUpdates's 50ms timer callback so the lifetime
subscription shares the same response-delivery guard as the existing
bootstrap notifications. The pre-bootstrap thinking level is still
communicated to the client through the response payload's configOptions
(newSession / loadSession / resumeSession / unstable_forkSession all return
it), so no state is lost; it is only the notification that is deferred.
For client-driven setSessionConfigOption({thinking}) the handler now only
skips its own push when the lifetime subscription is already installed.
Pre-bootstrap the handler keeps pushing (the client knows the session id
because they passed it in), post-bootstrap the subscription pushes
exactly once. No double-push, no missing pre-bootstrap notification.
Tests:
- updated existing pushes-config-option-update test to await past the 50ms
bootstrap timer before driving the internal setThinkingLevel
- updated the single-config_option_update-per-setSessionConfigOption test
the same way
- added 'suppresses lifetime config_option_update during the bootstrap
window' regression that drives setThinkingLevel synchronously after
newSession and asserts zero notifications, then asserts notifications
resume after the bootstrap timer fires
- bun test test/acp-agent.test.ts: 11/11 pass
Co-Authored-By: omp <noreply@oh-my-pi.dev>
ACP clients (Zed, etc.) only received `config_option_update` notifications
when they themselves drove the change via `session/set_session_config_option`.
Internal thinking-level updates (slash commands, automatic model-driven
adjustments, extension UI) bypassed the notification path, so client config
panels went stale until the next user-initiated change.
AgentSession now emits a `thinking_level_changed` event from
`setThinkingLevel`, and AcpAgent installs a session-lifetime subscription on
each managed session that pushes a fresh `config_option_update` whenever the
event fires — independent of prompt-turn lifecycle. The
`session/set_session_config_option` handler no longer pushes its own
notification for the `thinking` config (lifetime subscription covers it);
the response still returns fresh `configOptions` so callers see the new
state synchronously. Subscriptions are released in `#disposeSessionRecord`.
Also consolidated four duplicate `config_option_update` send sites into a
new `#pushConfigOptionUpdate(record)` helper.
Tests: added two cases to `test/acp-agent.test.ts` — one verifying internal
`setThinkingLevel` calls produce a `config_option_update` and a no-op
re-set produces none, and one verifying client-driven
`setSessionConfigOption(thinking, …)` produces exactly one notification.
Co-Authored-By: omp <noreply@oh-my-pi.dev>
- Restored formatDimensionNote bracket form '[Image: original WxH, displayed at WxH. Multiply coordinates by S to map to original image.]' that tests assert. The Bun 1.3.14 refactor regressed it to a less informative 'Image resized from …' line.
- Broadened bash-sixel-render multi-line styling assertion to accept both truecolor (38;2;) and 256-color (38;5;) SGR runs so CI runners with TERM=dumb don't fail. The contract being tested — every line carries its own SGR — is independent of color depth.
The async job completion path copied durationMs, tokens, and extractedToolData
from SingleResult back into the AgentProgress object, but missed cost.
Add progress.cost = singleResult?.usage?.cost.total ?? 0 to the same block.
Eliminates the three-way duplication of toolCount/tokens/cost stat
appending across renderAgentProgress (running), renderAgentProgress
(completed), and renderAgentResult.
renderAgentResult (rendered after the task tool resolves) was missing the
cost display present in renderAgentProgress. Read cost directly from
result.usage?.cost.total which is already available on SingleResult.
Token counter (token_total status-line segment, subagent progress tree,
session-observer stats line) previously included cacheRead in its cumulative
sum. With Anthropic prompt caching, cacheRead per turn equals the full cached
context, so summing across N turns gives N*context_size -- a session with a 1M
context and 5 turns showed ~5M tokens despite no compaction occurring.
Fix: display shows input + output + cacheWrite per turn. cacheWrite is kept
because each byte is written once; cacheRead re-reads the same context every
turn. Dedicated cache_read/cache_write status-line segments still show cache
activity; billing cost is unaffected.
Also adds per-subagent cost display (dollar amount, statusLineCost color)
accumulated incrementally from message_end events. Hidden when cost is zero
(subscription/OAuth providers). Brings token and cost display in line with
what Claude Code shows per-agent.
- Updated BashTool's leading `cd` regex to stop matching newline characters so cwd extraction only applies to a single-line `cd ... &&` prefix.
- Added a regression test for multiline commands with a later-line `&&` to ensure each line of the script executes normally.
Keep the active page stealth setup synchronous, but make the broader CDP target UA override sweep selective and best-effort. Non-page or ephemeral Chrome targets can otherwise block worker initialization long enough for browser.open to hit the tool timeout before the tab worker sends ready.
Fixes#1053
Forward worker error and messageerror events while acquireTab waits for the initial ready/init-failed response. This prevents async worker module-load or early startup failures from being reported only as a generic tab worker init timeout.
- Added a new formatBashCommandLines helper that syntax-highlighted each command line and applied the dim prefix only to the first line.
- Updated the shell renderer to emit command output as line-based entries instead of a single dimmed string.
- Extended the bash renderer test to verify multi-line commands keep ANSI styling on every rendered line.