- Added a `resolveSearchRepoScope` helper that uses an explicit `repo` when provided, skips defaulting when a query already contains a repo/org/user/owner scope qualifier, and otherwise resolves the current checkout via `resolveDefaultRepoMemoized`.
- Updated `search_issues`, `search_prs`, `search_code`, and `search_commits` to use the resolver before composing API queries, defaulting `repo` when omitted but silently falling back to an unscoped search on resolution failure.
- Documented the new search-repo defaulting rules in tool prompts, user docs, and the package changelog.
The agentic commit pipeline (`omp commit`) wrote its commit and then sat
spinning on Ctrl+C because nothing in `Commit.run()` drained the lingering
event-loop handles: `installH2Fetch()` keeps idle HTTP/2 sockets warm to
the provider, the Settings autosave timer can still be armed, and the
AgentSession's extension/runner machinery holds onto async-job and OAuth
refresh state even after `session.dispose()` releases what it knows how
to. Mirror the `runPrintMode` exit path from `main.ts` by calling
`postmortem.quit(0)` once `runCommitCommand` resolves so the CLI returns
to the shell, runs registered cleanup callbacks, then exits — same model
the non-interactive launch flow already uses.
Also widens the private bash-tool helpers' `notices` parameter to
`readonly string[]` so `bun check:types` keeps passing — the public outer
arm already accepted `readonly string[]` and an upstream commit had only
partially propagated the change.
Fixes#1041
The previous examples used " • " in the source file and inserted "·"
as the new content. Some agents were copying the middle-dot literally
into real edits as if it were format scaffolding, since the demo
inserts were near-twins of the existing string.
The new example uses TITLE = "Mr" → "Mrs" with "Dr" inserts:
ASCII-only, clearly distinct from any payload separator, and obviously
domain content rather than punctuation the model could confuse for
syntax. Every original op shape is preserved (single-line replace,
multiline replace, insert AFTER/BEFORE, append, delete, blank, both
anti-patterns). Pure prompt change; parser/schema/runtime untouched.
The ClaudePluginManifest interface was missing the `commands` field
(the standard Claude plugin format), and resolvePluginDir only checked
the legacy `slash-commands` key. Plugins declaring their command path
via `"commands": "..."` silently fell back to the hardcoded
`<plugin-root>/commands/` directory, which doesn't exist for
Claude-format plugins, so no slash commands were ever loaded.
Changes:
- Added `commands?: string` to ClaudePluginManifest.
- Changed resolvePluginDir to accept ReadonlyArray<keyof
ClaudePluginManifest> and iterate in priority order; the first
non-empty match wins.
- loadSlashCommands now passes ["commands", "slash-commands"] so
the canonical Claude plugin key takes precedence over the legacy one.
- Added two regression tests: one covering the `commands` key in
isolation, one verifying its precedence over `slash-commands` when
both fields are present.
Fixes#1076
- Updated `collectPayload` to treat blank lines inside a payload run as empty entries when subsequent payload text is present.
- Added lookahead-based end-of-run detection so blank lines before a non-payload operation remain section separators.
- Added tests verifying blank-line recovery inside payloads and non-consumption of trailing blanks between hashline sections.
- anthropic: add signal to AnthropicSearchParams, callSearch(), and
AnthropicProvider.search()
- exa: add signal to ExaSearchParams, callExaSearch(), and ExaProvider.search()
- jina: add signal to JinaSearchParams, callJinaSearch(), and JinaProvider.search()
- zai: add signal to ZaiSearchParams, callZaiTool(), and ZaiProvider.search()
- gemini: add signal to GeminiSearchParams, callGeminiSearch() and
buildInit() so both fetchWithRetry calls (initial + auth-refresh retry)
carry the signal
The five providers listed above never forwarded SearchParams.signal to the
underlying HTTP layer, so pressing Esc during a web_search call had no effect
and the session froze until the request resolved or Ctrl+C was pressed.
brave, kimi, perplexity, searxng, tavily, synthetic, codex, kagi, and parallel
already thread the signal correctly and are unchanged.
Fixes#1044
- Added stripOutputNotice to output-meta to remove appended truncation notices when output metadata is available.
- Updated bash, eval, browser, read, and ssh renderers to strip the notice before display so the styled warning line is not duplicated.
- Left fallback behavior unchanged so outputs without a notice continue through unchanged.
TypeScript 6 tightens mutability checks; the notices parameter was typed
as string[] but the caller held a readonly string[] (from the options
object). Widening both private method overloads to readonly string[]
satisfies the type checker without any runtime change — filter(Boolean)
works on readonly arrays.
discoverAgents() called listClaudePluginRoots() unconditionally, so agents
from Claude Code marketplace plugins appeared in /agents and the Agent
Control Center even when claude-plugins was listed in disabledProviders.
Guard the listClaudePluginRoots() call with isProviderEnabled("claude-plugins"),
returning an empty roots array when the provider is disabled — matching how
filterProviders() handles every other capability's provider set.
Added regression test that verifies both the enabled path (agents visible)
and the disabled path (agents absent) using a real temp-directory plugin
registry fixture.
Fixes#1075
- Updated `formatBashFixupNotice` to wrap the stripped-pattern warning in a `<system-warning>` wrapper.
- Reworded the notice to clarify output is already truncated and stderr is merged into stdout.
- Extended the Bash interceptor test to verify the warning tag appears for head/tail stripping.
- Added a persistent asyncio event loop and coroutine-aware compiled-code execution for runner cells.
- Enabled compiling notebook cells with top-level await flags and awaiting coroutine results before rendering.
- Added an integration test confirming top-level await works across kernel cells with preserved state.
- Added top-level parsing and segment splitting to apply bash fixups only on safe command chunks.
- Replaced `stripTrailingHeadTail` usage with `applyBashFixups` and array-based notice formatting.
- Fixed terminal `| head`/`| tail` and redundant `2>&1` stripping while preserving command semantics.
- Updated fixup tests for cross-command cases and removed superseded head-tail-only test coverage.
- Adjusted the test server in test_host_uris.py to re-emit host_uri_result frames as uri_echo notifications.
- Updated test client setup to attach an unknown-notification capture hook for uri_echo frames.
- Changed _await_echo to read captured uri_echo frames from that hook and fail fast if capture is missing.
Drop trailing `| head [args]` / `| tail [args]` pipes that exist purely
to limit output — the harness already truncates bash output and exposes
the full result via the bash-original artifact, so these pipes only
hide content from the agent.
Conservative gates (any failing leaves the command verbatim):
- single-line only; multi-line scripts may legitimately end pipelines
with head/tail to bound a generator or loop body
- whitelisted limit-only args (-nN, -n N, -cN, -N, -q, -v, --lines[=N],
--bytes[=N], --quiet, --verbose); rejects -f/-F/+N/filenames so
`tail -f`, `tail -n +2` etc. stay intact
- regex anchored at end of command; any downstream operator (`&&`,
`||`, `;`, `&`, `>`, `|`, `` ` ``, `$(…)`, `)`) blocks the match, so
`just build 2>&1 | tail -3 && just up && …` is untouched
- refuses to reduce the command to an empty string
- pipe boundary uses `[ \t]*`, not `\s*`, so a `|` on a continuation
line cannot be swallowed
Consolidates the existing `timeoutClampNotice` and the new strip notice
into a single `pendingNotices: string[]` array threaded through every
execute branch (async, auto-background, ACP terminal, local exec).
New setting `bash.stripTrailingHeadTail` (default `true`).
- Updated the status-line path segment to detect project directories under OS scratch roots and strip the scratch root when rendering.
- Switched scratch-root paths to use the new icon.scratchFolder glyph and registered that symbol across theme variants.
- Added tests covering scratch-root trimming, nested scratch subpaths, and non-scratch fallback icon behavior.
- Fixed legacy `pi-*` scope alias remapping to canonical packages, including `pi-ai/oauth` rewrites.
- Fixed restoration of `Key` on `@oh-my-pi/pi-tui` with canonical key strings and typed modifier helpers.
- Updated both package changelogs with unreleased notes for compatibility and `Key` restoration.
- Added `pi-scope-aliases.test.ts` coverage for alias remaps using fixture plugins and `loadExtensions`.
- Updated job label rendering to split labels on newlines and cap visible lines by collapsed or expanded view state.
- Truncated each visible label line to the existing max width and appended an ellipsis when extra lines were hidden.
- Printed additional visible label lines as indented follow-on lines beneath the job header.
- Updated shell minimizer line truncation to append `...[+N]` with the count of dropped Unicode scalars when truncation occurs.
- Updated read summary rendering to track `elidedLines`, include them in tool details, and append a recovery footer for `:raw` or line-range access whenever elided spans are present.
- Updated read-tool prompts/docs/tests to cover the new elision-footers and recovery guidance.
Fixes#1046
`CreateElicitationRequest` is a discriminated union — even after
narrowing on `mode === "form"`, both `ElicitationRequestScope` (no
`sessionId`) and `ElicitationSessionScope` (with `sessionId`)
remain in the union. The new live-getter regression test was reading
`calls[N]!.sessionId` directly, which CI tsgo rejected with TS2339.
Per-element `if (!call || call.mode !== "form" || !("sessionId"
in call))` narrows to the session-scoped variant. Spelled three times
because loop-style narrows don't propagate to the assertions below.
Matches the discriminator pattern used in the older 'translates select'
test at line ~927.
Co-Authored-By: omp <noreply@oh-my-pi.dev>
`biome check` enforces print-width on the two test-only call-sites that
`ast_edit` collapsed onto a single line during the sessionId-getter
rewrite. Auto-formatter wrap, no behavior change.
Co-Authored-By: omp <noreply@oh-my-pi.dev>
`AgentSession.sessionId` is a getter that reads through to
`sessionManager.getSessionId()` and mutates when an extension command
calls `ctx.newSession` or `ctx.switchSession` (both exposed in the
same #configureExtensions block). Snapshotting the id once at factory
time routed later elicitations to the pre-switch id — diverging from
every other sessionUpdate call in this file, which already reads
record.session.sessionId live.
createAcpExtensionUiContext now takes `getSessionId: () => string` and
calls it per elicitation. Caller passes `() => record.session.sessionId`
so each select / confirm / input picks up the current id.
Also simplifies elicitFromAcpClient: `onAbort` and `finish` had
identical settlement bodies differing only by the resolve value, with a
hand-rolled `removeEventListener` symmetry comment to keep them in
sync. Collapsed to a single settle path — `onAbort = () =>
finish(undefined)` — so future changes to settlement apply to both
paths automatically. Doc-comment tightened to call out that late SDK
`accept` responses (not just rejections) after abort/timeout are also
dropped silently.
New regression test asserts that mutating the captured sessionId
between elicitations is reflected in the next request.
Co-Authored-By: omp <noreply@oh-my-pi.dev>
Promotes the stub acpExtensionUiContext to a createAcpExtensionUiContext
factory invoked per session inside #configureExtensions. select / confirm
/ input each map to a single-property `value` schema and round-trip
through a shared elicitFromAcpClient helper that mirrors
RpcExtensionUIContext.#createDialogPromise:
- capability gating on clientCapabilities.elicitation.form
- runtime typeof narrowing on accept payloads (wrong-type / missing
key / no content all fall back to the stub return values)
- dialogOptions.signal: pre-aborted short-circuits before any SDK
call; mid-flight abort races the in-flight elicitation. Symmetric
removeEventListener on both onAbort and finish paths.
- dialogOptions.timeout: setTimeout(.unref()) settles the promise via
onTimeout + stub fallback. A throwing onTimeout is caught and
logged so the elicitation promise still settles.
- late SDK rejections after abort/timeout are dropped silently;
transport failures log via logger.warn with { sessionId, method,
error }.
Empty/whitespace-only placeholders on `input` and empty/whitespace-only
messages on `confirm` are treated as absent (trim-aware), matching the
behavior documented in the CHANGELOG bullet.
15 new tests cover request shape, decline/cancel, missing capability,
transport failure, pre-abort, mid-flight abort, wrong-typed accept,
missing `value` key, no content, timeout, whitespace placeholder,
empty-message join, and throwing onTimeout.
Co-Authored-By: omp <noreply@oh-my-pi.dev>
- 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.