Loaded cached startup models for special built-in providers alongside standard provider descriptors so boot-time model resolution can see cached Google Antigravity, Gemini CLI, and OpenAI Codex discoveries before refresh.\n\nFixes #1721
Kept Ctrl+V as a default clipboard-image paste shortcut on Windows while preserving Alt+V as the Windows Terminal-safe fallback.
Updated keybinding docs and regression coverage for the platform-specific default.
Fixes#1708
- Defined a 20-second timeout constant for randomized streaming-edit patch tests and passed it to the success and failure cases.
- Restructured per-seed cleanup to await session disposal inside a nested finally block before closing auth storage.
- Preserved existing assertions for abort behavior while running each random chunk stream through the updated paths.
- Updated listClaudePluginRoots test assertions to expect unprefixed skill identifiers.
- Adjusted both manifest and outside-skill discovery expectations to match the updated naming.
- Ensured the tests now look for plain skill names instead of namespaced values.
- Expanded path parsing to split top-level comma, semicolon, and whitespace entries.
- Updated find/search and scope resolution to apply delimiter expansion before path-spec validation.
- Updated read tool fallback to try split path parts before raising missing-path errors.
- Coerced bare string arguments into singleton arrays for array-typed schemas.
- Added `parseClaudeRateLimitHeaders` to extract 5h/7d utilization from `anthropic-ratelimit-unified-*` response headers.
- Added `AuthStorage.ingestUsageHeaders` to warm the per-credential usage cache from headers, throttled to 60s per key.
- Merges header-derived limits onto the last full usage report, preserving per-tier data not present in headers.
- Wires ingestion into `AgentSession` on each Anthropic response to reduce direct OAuth `/usage` probes.
- Added local CONNECT proxy with TLS interception to capture Claude API traffic.
- Drives Claude Code via headless PTY/xterm and extracts the first /v1/messages exchange.
- Added `claude:trace` npm script and CLI with JSON/text output modes.
- Added integration test using a fake Claude script against a local TLS server.
- Derived Anthropic and session metadata `device_id` from `getInstallId()` deterministically.
- Added CLAUDE bootstrap identity lookup and merged fallback into token exchange/refresh.
- Recovered `accountId` and `email` during token exchange/refresh when token payloads lacked them.
- Enforced bootstrap failures for non-OK responses and invalid JSON payloads.
ExaProvider.isAvailable() and searchExa() now consult AuthStorage so Exa credentials configured through the broker/credential store work alongside EXA_API_KEY, matching the other API-key search providers.
Refs #1695
- Passed ANTHROPIC_SEARCH_BASE_URL through to Anthropic web search calls that use authStorage fallback credentials instead of only applying it with ANTHROPIC_SEARCH_API_KEY.
- Added regression coverage asserting fallback Anthropic credentials use the search-specific base URL.
- Updated the environment and web_search docs to reflect the actual credential and base URL resolution order.
Fixes#1694
The Anthropic web search path built request headers via buildAnthropicSearchHeaders, which never threaded model headers through buildAnthropicHeaders, so ANTHROPIC_CUSTOM_HEADERS was dropped from every web-search request regardless of mode. The streaming path's resolveAnthropicCustomHeaders also gated on isFoundryEnabled(), so users with a corporate ANTHROPIC_BASE_URL + ANTHROPIC_CUSTOM_HEADERS (e.g. X-Gateway-Key) got 401s on web_search unless they set CLAUDE_CODE_USE_FOUNDRY=true.
Loosen the resolver to also apply when ANTHROPIC_BASE_URL points to a non-Anthropic host, export the baseUrl-keyed variant, and have buildAnthropicSearchHeaders pass the resolved custom headers as modelHeaders so search and streaming paths behave identically. Stock api.anthropic.com (no Foundry) still omits the headers.
Fixes#1693
- Expanded the existing entries in docs/environment-variables.md so the override-semantics ('search-only, isolates from main ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL / FOUNDRY_BASE_URL') are spelled out, and added a usage note for enterprise-gateway split routing.
- Surfaced the search-only env vars (ANTHROPIC_SEARCH_API_KEY / ANTHROPIC_SEARCH_BASE_URL / ANTHROPIC_SEARCH_MODEL) in the Anthropic provider section of docs/tools/web_search.md, where users were already looking.
- Added ANTHROPIC_SEARCH_BASE_URL alongside ANTHROPIC_SEARCH_API_KEY in 'omp --help' so the pair shows up together in the CLI env-var summary.
Fixes#1694
The plugin:name prefix (e.g. hyperpiemia:whistle-rules) broke skill://
URL parsing because colons are ambiguous with port separators. Skills
from marketplace plugins are now registered under their plain names.
Name collisions are handled by the existing capability-layer dedup
based on provider priority ordering.
Post-extension session-model retry now covers the case where the initial restore failed entirely (e.g. saved default unavailable, last active role supplied by an extension) and the settings default filled in the active model. Also recomputes thinking-level from full precedence against the reclaimed model so a fallback model's defaultLevel does not become sticky.\n\nFixes #1649
Initial startup resume runs before extension providers register, so a role model supplied by an extension fell back to the saved default. Retry the preferred session-model candidates once provider registrations are processed and re-resolve thinking level for the new model.\n\nFixes #1649
Treat temporary model_change roles as non-restorable when resuming sessions so context-promotion and retry-fallback models do not override the saved default.\n\nFixes #1649
Try the last active role model first, then the saved default model when the role model cannot be restored during session switching or startup resume.\n\nFixes #1649
Use the last model_change role when resuming an existing session instead of always restoring models.default. Covered both /resume switchSession and startup continue paths.\n\nFixes #1649
Address review of the in-place loader: the directory-subtree filter had two
regressions vs the old mirror.
- It only rewrote files under the entry's package root, so a `dist/`
entry importing `../../shared/helper.ts` (or a symlink-escaping sibling)
left that module's legacy `@(scope)/pi-*` / `@sinclair/typebox` imports
un-rewritten.
- `findExtensionRoot` walked up to the nearest package.json, which for an
ad-hoc extension under a project (e.g. `/repo/.omp/extensions/foo.ts`)
resolved to the project root — so the permanent onLoad hook would then
rewrite unrelated project/host source imported later.
Replace the directory filter with a precise scope: pre-walk the entry's
relative-import graph (static + dynamic `./`/`../` specifiers), collect each
module's realpath, and build the onLoad filter as an exact-path alternation
of just those modules. This matches exactly the set the old mirror tracked
(minus the copy): it covers `../src`/symlinked siblings and never touches
the host, other extensions, node_modules deps, or unrelated project files.
Adds a regression test that a non-imported sibling stays outside the rewrite
scope, and renames the ../src test to reflect graph-following.
Legacy Pi extensions were mirrored module-by-module into a flat temp dir
(`omp-legacy-pi-file/entry-<hash>/`) with imports rewritten to absolute
URLs. Running from that temp root made `import.meta.url`/`__dirname`
resolve to the mirror, so `readFileSync(join(__dirname, "ui.html"))`-style
asset loads ENOENT'd — e.g. @plannotator/pi-extension's HTML never loaded
and it auto-approved plans (#1674). The standing remedy (#1675) copied
~30MB of .html/.css per startup with a fragile extension whitelist.
Bun's runtime plugins don't fire onResolve for transitive imports, which
is why the mirror pre-resolved everything. But onLoad does fire
transitively for file-namespace modules matching its filter, and a real
file import keeps import.meta.url pointing at the source. So:
- Load the extension entry in place via `import(pathToFileURL(real))`;
realpath first so the path matches what Bun hands onLoad (macOS
/var->/private/var, bun link/pnpm symlinks).
- Register one Bun.plugin() onLoad per extension *package root* (nearest
package.json), filtered to that root's .js/.ts but excluding any
node_modules segment, that rewrites only `@(scope)/pi-*` and the bare
`@sinclair/typebox` specifier to absolute bundled/shim URLs.
- Everything else — relative siblings (incl. ../src), the extension's own
node_modules deps (CJS/ESM), and bundled assets — resolves natively.
Removes the flat mirror, the temp-dir writes, the asset-copy problem, and
the now-dead `omp-legacy-pi-file:` namespace machinery. import.meta.url is
the real source file, so assets resolve exactly as under the original Pi
runtime. Adds an in-place load regression test covering asset reads,
submodule .css siblings, node_modules-excluded native deps, and
package-root-scoped ../src rewrites.
Fixes#1674.
- Added `expectSleepNear` to allow ±100ms variance on sleep duration checks.
- Updated `--thinking` test value from `"extended"` to `Effort.XHigh` enum constant.
- Converted existing local paths to Windows paths via `wslpath -w` before opening.
- Used `wslview` directly in WSL environments, bypassing `xdg-open`'s broken file-handler translation.
- Fell back to `xdg-open` for URLs or when `wslview` is unavailable.
- Added unit tests covering WSL file, URL, and fallback scenarios.
Replace the sunset V0 Search API with V1 (POST /api/v1/search) under the
existing `kagi` provider id instead of shipping a parallel `kagi-v1`
provider. Credentials still resolve through the shared AuthStorage broker
(Bearer token, KAGI_API_KEY, /login kagi), and recency now maps to a
UTC-deterministic filters.after date.
- Merge V1 client into src/web/kagi.ts (categorized result buckets, direct
answer, related/adjacent questions)
- Keep classifyProviderHttpError mapping for auth/quota signals
- Drop the kagi-v1 entries from the provider registry, order, type union,
and settings schema
- Consolidate tests into web-search-kagi.test.ts
omp resolves the update target by querying https://registry.npmjs.org/ directly, but `bun install -g pkg@<version>` would then consult bun's on-disk manifest snapshot AND honour the user's npm-mirror configuration (corporate proxy, Taobao, …). Either source can lag the upstream registry by minutes-to-hours, in which case bun rejects the version with `No version matching "X" found for specifier "@oh-my-pi/pi-coding-agent" (but package exists)` even though the registry omp just queried is serving it.
The bun install step now runs with both `--no-cache` (skip the manifest snapshot) and `--registry=https://registry.npmjs.org/` (pin the official catalog regardless of bunfig/.npmrc) so the install observes the same registry state the version check used. The registry URL is centralised in an `NPM_REGISTRY` constant shared by `getLatestRelease` and `buildBunInstallArgs`.
Fixes#1686