Commit Graph

3535 Commits

Author SHA1 Message Date
roboomp 4d108c1951 fix(plugins): enumerate linked plugins for sub-discovery
PluginManager.link symlinks the package into <plugins>/node_modules
and records it in omp-plugins.lock.json, but never writes to
<plugins>/package.json#dependencies. getEnabledPlugins iterated only
the dependency map, so the documented `omp install ./local-extension`
workflow (delegated to plugin link) succeeded but its sibling skills/,
hooks/, tools/, etc. stayed invisible after install.

Iterate the union of package.json#dependencies and
omp-plugins.lock.json#plugins so symlinked-only packages surface
alongside npm/marketplace installs. Lockfile entries whose
node_modules tree has since been deleted (stale link) are skipped
silently. Linked-only setups with no <plugins>/package.json at all
now work too.

Per-PR review feedback: https://github.com/can1357/oh-my-pi/pull/1498
2026-05-29 06:34:10 +00:00
roboomp bdce9cf068 feat(discovery): scan installed plugin packages for sub-discovery
Marketplace and `omp plugin link` installs write to
`<plugins>/node_modules/` rather than to `extensions:` in settings,
so the original PR still missed their sibling skills/, hooks/,
tools/, commands/, rules/, prompts/, .mcp.json sub-trees. Wire
listOmpExtensionRoots to enumerate getEnabledPlugins(cwd, { home })
in addition to CLI-injected and settings-driven roots.

Adds an optional { home } parameter to getEnabledPlugins so the
discovery loader can pass through LoadContext.home for tempdir-rooted
tests. The getPluginsNodeModules/getPluginsPackageJson/
getPluginsLockfile helpers gain the same optional home overload so
they mirror getPluginsDir.

Per-PR review feedback: https://github.com/can1357/oh-my-pi/pull/1498
2026-05-29 06:28:33 +00:00
roboomp db525316ab fix(discovery): wire extension package sub-dirs into discovery + add top-level install command
Bug 1: capability loaders in src/discovery/builtin.ts only walked
.omp/ and ~/.omp/agent/, so extension packages registered via
extensions: in settings or --extension on the CLI shipped their
skills/, hooks/pre|post/, tools/, commands/, rules/, prompts/, and
.mcp.json silently — the docs at omp.sh/docs/extension-authoring
advertise the opposite. Add a new omp-plugins discovery provider that
scans every configured extension package directory for those
sub-trees, plus a small omp-extension-roots helper that resolves the
union of settings-driven and CLI-injected roots. main.ts injects CLI
extension paths via injectOmpExtensionCliRoots before any capability
load.

Bug 2: install was never registered as a top-level subcommand, so
`omp install ./my-extension` was rewritten to `launch install
./my-extension` and forwarded to the LLM as an initial prompt. Add a
top-level install command that routes local paths to plugin link and
remote specs to plugin install. Extract the command table into
src/cli-commands.ts so tests can introspect registered subcommands
without triggering cli.ts's top-level await.

Fixes #1496
2026-05-29 06:16:32 +00:00
can1357 f874837d9c feat(coding-agent): added ultrathink keyword detection and prompt notice injection
- Added a new ultrathink mode module with standalone, case-insensitive detection, rainbow editor highlighting, and a hidden notice payload.
- Updated `CustomEditor` and the shared TUI `Editor` to support optional zero-width text decoration and apply the `ultrathink` styling during input rendering.
- Extended `AgentSession` prompt handling to append the hidden ultrathink notice after user turns in both streaming and non-streaming message flows, excluding synthetic messages.
2026-05-29 07:12:43 +02:00
can1357 625c8b1992 test(ai): updated tests for model metadata and auth token fallback
- Mocked the Vertex stream E2E test to override the home directory and clear GOOGLE_APPLICATION_CREDENTIALS so token resolution uses metadata credentials instead of local ADC files.
- Updated wafer and model-registry test expectations to match current model metadata values (Qwen3.7 Max and claude-opus-4-8).
2026-05-29 06:56:29 +02:00
roboomp e33a39d766 style: bun run fix 2026-05-29 06:42:47 +02:00
roboomp 82f5db2fbc fix(coding-agent): clamped memory phase1/phase2 reasoning effort to the model's supported range
The memory pipeline hardcoded `Effort.Low` (stage1) and `Effort.Medium` (phase2
consolidation) when calling `completeSimple`. On models whose supported efforts
exclude those levels (e.g. `deepseek/deepseek-v4-pro` → [high, xhigh]),
`completeSimple → mapOptionsForApi → resolveOpenAiReasoningEffort →
requireSupportedEffort` threw "Thinking effort low is not supported by
<provider>/<model>" and every stage1 job was recorded as failed, blocking phase2
and producing no memory artifacts.

Route both call sites through `clampThinkingLevelForModel(model, requested)` —
the same helper already used by compaction (#1182). For `[high, xhigh]` both
`low` and `medium` lift to `high`; non-reasoning models continue to receive
`undefined`, preserving prior behaviour.

Fixes #1480
2026-05-29 06:42:47 +02:00
roboomp 437b6524cd fix(cli): restored package resolver for non-compiled pi root remaps
Limited bunfs package-root overrides to compiled-binary mode so non-compiled installs (monorepo, source-link, node_modules) keep resolving legacy pi roots through Bun's package resolver instead of a hardcoded source-tree path.

Refs #1474
2026-05-29 06:42:47 +02:00
roboomp 349fb51ddb fix(cli): preserved legacy peer fallback for subpaths
Retried original legacy specifiers after canonical peer fallback fails so direct plugin imports with only legacy-scoped peer dependencies continue to load.
2026-05-29 06:42:46 +02:00
roboomp 564b6d0f22 fix(cli): routed legacy pi-coding-agent imports through a sibling shim
Listing the coding-agent's own ./src/index.ts as a bun --compile extra entrypoint silently breaks the CLI binary startup. Added a dedicated legacy-pi-coding-agent-shim.ts that re-exports the canonical barrel, registered the shim instead of the package index, and updated the compat resolver to point pi-coding-agent at the shim path.
2026-05-29 06:42:46 +02:00
roboomp 82008e4f38 fix(cli): restored legacy pi package root remaps
Added bundled root overrides for legacy pi package imports in compiled binaries and corrected fallback resolution to use canonical @oh-my-pi specifiers.

Fixes #1474
2026-05-29 06:42:46 +02:00
can1357 11da79a6bf fix(read): fixed column truncation mutating snapshot with display content
- Column truncation is now applied to a cloned array so `collectedLines` retains on-disk content for snapshot recording.
- Snapshots bound to hashline TAGs now hold the original file text, preventing hash-mismatch failures on subsequent edits to files with long lines.
- Added regression tests covering full-file, range, multi-range reads and a live edit-after-read scenario.
2026-05-29 06:42:46 +02:00
can1357 31f3fbda61 feat(write): added snapshot header to write tool output in hashline mode
- Prepended `¶path#TAG` hashline header to plain file, ACP-bridge, and conflict resolution write results.
- Bulk conflict resolutions emit a trailing `Snapshots:` block with one header per written file.
- Suppressed when hashline display mode is disabled or for archive/SQLite/internal-URL targets.
- Added tests covering header presence, patcher usability, and disabled-mode suppression.
2026-05-29 06:42:46 +02:00
can1357 cacf996f90 feat(session): added sql session storage with queue-backed async writes
- Added `SqlSessionStorage` with SQL row persistence, optional schema bootstrap, and async queue-backed writes.
- Added adapter inference with postgres/mysql/sqlite query generation and in-memory `#mirror` with monotonic mtime sync.
- Added `SqlSessionStorage` export in package entry and Unreleased changelog notes for SQL session persistence.
- Added SQLite/session-manager tests covering helper creation, write/read/list/opening, and delete/rename error paths.
2026-05-29 06:42:46 +02:00
can1357 47bd4fd766 feat(session): added RedisSessionStorage with redis-backed session persistence
- Added RedisSessionStorage with Redis-backed session JSONL persistence and storage options.
- Added create/refresh/list/read behavior with in-memory mirror, SCAN hydration, and monotonic mtime tracking.
- Added package exports for SessionStorage backends and a Redis session SDK example with usage guidance.
- Added in-memory fake Redis tests covering persistence restore, session listing, rename, and error-injection paths.
2026-05-29 06:42:45 +02:00
can1357 5bed80785a feat(coding-agent): added /drop-images support to strip images
- Added `/drop-images` slash command handling for runtime and TUI to drop images and report results.
- Added `stripImagesFromMessage` utilities to remove image blocks from message content and return removal counts.
- Added `AgentSession.dropImages()` to prune image blocks, rewrite history when needed, and rebuild session context.
- Added tests for user, toolResult, fileMention, and assistant image-stripping, placeholders, and zero-removal cases.
2026-05-28 13:03:45 +02:00
can1357 c4f93eca20 fix(agent): patched compaction 401/403 fallback to copy error status
- Centralized compaction stop-reason error throws via createSummarizationError().
- Set compaction thrown errors to copy response.errorStatus into Error.status.
- Expanded compaction auth detection to treat HTTP 401/403 as auth failures with regex fallback preserved.
- Added regression tests for 401/403 status propagation and compaction fallback auth behavior.
- Documented both package fixes in Unreleased Fixed changelog entries.
2026-05-28 12:50:52 +02:00
can1357 509963bd63 feat(coding-agent/internal-urls): enabled vault:// protocol behind vault.enabled gate
- Added a `vault.enabled` setting and `isVaultEnabled` guard, and vault resolve, write, and path resolution now threw a disabled error when the feature was off.
- Improved CLI handling by parsing active vault path output and treating `Error:` lines from stdout/stderr as command failures.
- Updated tests to validate the disabled gate, cached active-vault path resolution, and CLI error surfacing on successful exit codes.
2026-05-28 10:17:56 +02:00
can1357 5053a6a4d3 fix(coding-agent): corrected coding-agent incomplete stop recovery logic
- Handled "incomplete" stop reasons in session recovery and auto-compaction workflows.
- Dropped the prior assistant turn before attempting recovery on incomplete-length stops.
- Expanded auto-compaction reason types and triggers to include "incomplete".
- Updated internal URLs parsing internals, export order, tests, and Obsidian URI prompt docs.
2026-05-28 10:10:34 +02:00
can1357 1709172bfe feat(coding-agent): added obsidian integration
- Added vault:// URL parsing, typed variants, and path resolution with vault-root validation.
- Added VaultProtocolHandler with fs and Obsidian CLI-backed resolve/read/write/list support plus caching.
- Added vault scheme integration in router, path utils, and plan-mode guard using resolveVaultUrlToPath.
- Documented vault:// read/edit and `?op`-scoped URI formats in system prompts when Obsidian is available.
- Secured vault:// operations by rejecting traversal, absolute, and symlink-escape path cases.
- Fixed response.incomplete recovery by dropping truncated turns and promoting context.
- Added internal tests for vault protocol parsing, caching, CLI behavior, and invalid-path defenses.
2026-05-28 10:10:34 +02:00
can1357 c1fa0e9f50 refactor(agent): replaced keepalive utility with disposable EventLoopKeepalive
- Replaced the `keepaliveWhile` Promise wrapper with a new `EventLoopKeepalive` class that registers and disposes an interval timer through `Symbol.dispose`.
- Updated `Agent` to instantiate `EventLoopKeepalive` via `using` during prompt execution instead of manually managing an interval.
- Wrapped interactive mode's await path with the new helper and removed redundant `keepaliveWhile` usage from the CLI entrypoint.
2026-05-28 09:51:57 +02:00
Can Bölük 20584149ea Merge pull request #1461 from can1357/farm/c2d56198/http-mcp-get-sse-timeout
fix(mcp): bound optional HTTP SSE startup
2026-05-28 10:46:17 +03:00
Can Bölük 8eb4ff13d6 Merge pull request #1471 from daandden/fix/codex-web-search-gpt55
fix(codex): prefer gpt-5.5 for web search
2026-05-28 10:46:09 +03:00
Can Bölük 8e22eb6473 Merge pull request #1468 from oldschoola/feat/wafer-provider
feat(ai): add Wafer Pass and Wafer Serverless providers
2026-05-28 10:45:55 +03:00
Vu Anh Nguyen 674d9b00a2 fix(codex): prefer gpt-5.5 for web search 2026-05-28 14:02:52 +07:00
bench-local f6ca76728b feat(ai): add Wafer Pass and Wafer Serverless providers
Wafer (https://wafer.ai) exposes a single OpenAI-compatible endpoint
(`https://pass.wafer.ai/v1`) for two SKUs whose entitlement differs
server-side, so we model them as two parallel providers — mirroring the
firepass/fireworks split so a user with both subscriptions can switch
without re-pasting:

- `wafer-pass` — flat-rate. `/v1/models` is filtered to entries whose
  `wafer.tier === "pass_included"`.
- `wafer-serverless` — pay-as-you-go superset of Pass.

Both issue `wfr_…` keys. `/login wafer-pass` and `/login wafer-serverless`
paste-and-validate via `/v1/models`. `WAFER_PASS_API_KEY` and
`WAFER_SERVERLESS_API_KEY` are wired through `getEnvApiKey`.

Bundled catalog:
- `wafer-pass`: GLM-5.1, Qwen3.5-397B-A17B.
- `wafer-serverless`: GLM-5.1, Qwen3.5-397B-A17B, Kimi-K2.6, Qwen3.6-35B-A3B.

Dynamic discovery via `/v1/models` overlays additional models at runtime
and folds the `wafer` envelope (tier, capabilities, cents/M pricing) into
the canonical `Model<"openai-completions">` shape. GLM-family entries
carry the zai-style thinking compat (`thinkingFormat: "zai"`,
`reasoningContentField: "reasoning_content"`) so reasoning tokens land in
the right field. Cents-per-million → dollars-per-million via /100.

Tests (`packages/ai/test/wafer.test.ts`, 5 cases): bundled catalog
contract for both providers and wire-id pass-through (case-sensitive,
no rewrite — `GLM-5.1` must round-trip verbatim or upstream 404s).
Optional `packages/ai/test/wafer.live.ts` exercises a real round-trip
against `pass.wafer.ai` when `WAFER_PASS_API_KEY` is set.
2026-05-27 20:45:33 -07:00
Scott Hyndman e46ee155a8 fix(coding-agent): shared Python kernels between eval and user shortcut
- Namespaced `AgentSession.executePython()` session IDs before invoking the Python executor.
- Added a regression test proving eval state is visible to the user shortcut path.
2026-05-27 22:16:23 -04:00
can1357 7dd00c015b feat: hashline improvements for spark
- Redesigned hashline patch syntax from anchor-based (`A-B:`) to hunk-header format (`@@ A..B @@`) with unified-diff compatibility.
- Removed `autoDropPureInsertDuplicates` option and simplified apply behavior to preserve duplicated boundary and context lines.
- Changed repeat operator from `^A-B` to `&A..B` and range separator from `-` to `..` for consistency with hunk-header syntax.
- Added image resizing and dimension notes to eval tool output; improved write tool hashline header sanitation for legacy formats.
- Removed 521 lines of boundary-duplicate absorption code and simplified parser to auto-convert bare body rows and unified-diff contamination.
2026-05-28 03:06:52 +02:00
can1357 b4238b10d3 fix: resolved auth-gateway handling of 429 usage-limit responses
- Classified usage-limit gateway responses as `429 rate_limit_error` in auth handling paths.
- Aligned auth-gateway and pi-native key retrieval with derived `sessionId` for `getApiKey` lookups.
- Handled usage-limit auth failures by rotating credentials with retry hints and returning undefined when none available.
- Replaced stream auth checks with retryable-upstream logic for 401 and usage-limit errors before content.
- Expanded `extractRetryHint` parsing for `~`, `sec`, `ms`, and minute/hour units.
- Added coverage for classifyGatewayError, retry-hint parsing variants, and stream-auth retry edge cases.
2026-05-28 02:56:54 +02:00
roboomp 2266fdae82 fix(mcp): honor disabled mcp timeouts for sse startup
When the operator disables MCP client-side timeouts via timeout: 0 or OMP_MCP_TIMEOUT_MS=0, do not impose a 1s startup deadline on the optional HTTP GET SSE listener — let the listener wait as long as the server takes so server-to-client messages are not lost.

Refs #1460
2026-05-27 23:37:10 +00:00
roboomp c0c9049cca fix(mcp): bounded optional http sse startup
Abort the optional Streamable HTTP GET SSE listener attempt after a short bounded startup window so POST-only request/response servers can finish initialization.

Fixes #1460
2026-05-27 23:32:38 +00:00
can1357 7c64576524 feat(hashline): replaced file-hash anchors with opaque snapshot-store tags
- Replaced 4-hex content-derived file hashes with 3-hex opaque tags minted by InMemorySnapshotStore, making tags session-bound pointers rather than content fingerprints.
- Removed lru-cache dependency; replaced LRU-bounded per-path rings with a flat 4096-slot global ring using a scrambled permutation to prevent LLM tag extrapolation.
- Made SnapshotStore required in Patcher (was optional); tag resolution now drives stale-anchor detection instead of recomputing hashes at apply time.
- Changed literal payload sigil from `|` to `+` and accepted `^A` shorthand for `^A-A`; added lenient recovery for bare bodies, lone `-` rows, and overlapping bare/concrete block pairs.
2026-05-28 01:00:23 +02:00
can1357 3d5f0d8868 refactor(coding-agent/cli): switched auth-broker serve to dedicated logger transport setter
- Updated the auth-broker CLI to import the transport setter from the logger module.
- Replaced the logger.setTransports call in runServe with the dedicated setTransports helper.
2026-05-28 00:51:35 +02:00
can1357 6491fff8f6 feat(ai): added strict auth-gateway mode with completion-probe checks
- Added strict `auth-gateway` check mode, propagated `--strict`, and updated strict output/exit rules.
- Added `checkCredentials` completion-probe support with timeout and provider-aware payload helpers.
- Changed OAuth credential checks to refresh first, preserve usage results, and skip completion on refresh failures.
- Added tests for completion-probe execution, OAuth refresh rejection, and `completion.reason`/sentinel behavior.
2026-05-28 00:35:15 +02:00
can1357 9474e95cb5 feat(coding-agent/tools): enabled shebang files to be auto-marked executable
- Added a `madeExecutable` result field to `WriteToolDetails` to surface executable changes.
- Implemented `maybeMarkExecutableForShebang` to chmod shebang files executable while preserving existing mode bits and swallowing chmod errors.
- Updated write flow and renderer output to return and display when a file was auto-marked executable.
2026-05-28 00:34:11 +02:00
can1357 7fa55750f9 feat(hashline): introduced explicit range syntax and repeat edit kind for hashline
- Replaced anchor shorthand syntax with explicit range format (1: -> 1-1:) and removed ^/v sigils in favor of ^A-B repeat and A-B:- delete operations.
- Added repeat edit kind to support ^A-B syntax for copying lines A through B, and inline delete syntax A-B:- for range deletions.
- Removed after_anchor cursor kind and standalone delete rows; empty anchor blocks now produce blank-line replacements instead of deletions.
- Updated parser, tokenizer, and type system to discriminate literal and repeat payloads, and refactored apply/recovery logic to expand repeat edits into individual inserts.
- Updated coding-agent test fixtures and settings documentation to reflect new hashline syntax and behavior.
2026-05-27 22:51:21 +02:00
can1357 1dbd2a0659 fix(coding-agent): pin streaming diff preview to tail of the diff 2026-05-27 19:57:54 +02:00
roboomp efba782fa7 fix(tools): isolate read URL reader-mode fallback chain from remote stalls
A stalled Jina reader request shared the overall reader-mode AbortSignal
with the downstream trafilatura/lynx/native fallbacks. When Jina hung
until the budget timer fired, the shared signal aborted and the catch
handler's signal?.throwIfAborted() re-threw before any local fallback
ran.

- Bound Jina and Parallel extract to their own per-attempt sub-budget
  (REMOTE_READER_MAX_MS, capped at 10s) so a remote stall cannot consume
  the whole overall reader-mode budget.
- Catch handlers now rethrow only on real userSignal cancellation, not
  on remote sub-budget or overall budget expiry.
- Wrap trafilatura/lynx in their own try/catch so a subprocess failure
  or abort does not skip the in-process native renderer.
- Always attempt the native renderer last: it works on already-loaded
  HTML with no network or subprocess, so even an exhausted overall
  budget still yields a result.

Fixes #1449
2026-05-27 19:00:49 +02:00
Can Bölük 8fa6652eec Merge pull request #1418 from can1357/farm/76c380e7/provider-models-deprecation-cache
fix(providers): prune stale synthetic model cache entries
2026-05-27 19:53:12 +03:00
Can Bölük f9c5484892 Merge pull request #1425 from oldschoola/fix/search-regex-error-prefix
fix(search): wrap native regex-build errors in ToolError
2026-05-27 19:53:02 +03:00
can1357 9f1a442a06 fix(coding-agent): fixed xAI base URL resolution and pass resolved model to credentials
- Added check to avoid returning DEFAULT_BASE_URL when a custom provider base URL is configured.
- Passed resolvedModel argument to resolveXAIHttpCredentials call in image generation tool.
2026-05-27 18:46:03 +02:00
can1357 c5055d6623 feat(ai): added OpenRouter routing-variant suffix support
- Added `openrouterVariant` option to `SimpleStreamOptions` and `OpenAICompletionsOptions` to append routing suffixes (`:nitro`, `:floor`, `:online`, `:exacto`) to OpenRouter model IDs at request time.
- Skips appending when the model ID already carries an explicit colon-suffix.
- Exposed `providers.openrouterVariant` setting in the coding-agent UI under Settings → Providers.
- Plumbed through `pi-native-server` forwarder and `AgentSession` options preparation.
2026-05-27 18:41:47 +02:00
Can Bölük 77c2af46e7 Merge pull request #1444 from OutlineDriven/fix/compaction-reasoning-effort-fallback
fix(agent): compaction reasoning effort — fallback to off when reasoning unsupported
2026-05-27 19:41:40 +03:00
Can Bölük dc1eb8d96f Merge pull request #1446 from OutlineDriven/fix/xai-grok-oauth-stabilize
fix(ai,coding-agent): stabilize xAI Grok OAuth
2026-05-27 19:41:20 +03:00
metaphorics b76f39d3a6 fix(coding-agent): reopen approved plan on plan-mode reentry
Patch axis: extend

Displacement: net-zero; reuses existing plan reference state instead of adding persistence or overwriting approved artifacts

Rule violations averted: no approved-plan overwrite, no transcript format migration, no public CLI/API expansion

PASS/FAIL: PASS after plan-mode focused tests and package check. Note: system-prompt-templates has an unrelated HOME=/tmp path-shortening expectation failure.
2026-05-27 16:32:40 +00:00
metaphorics 2a7f716386 fix(coding-agent): route xAI image edits to /v1/images/edits; honor image_size
The xAI image branch on origin/main always posts to /v1/images/generations
and hard-codes `resolution: "1k"`, contradicting two advertised contracts:

  P1: generate_image schema declares `input: z.array(inputImageSchema)`
  globally; resolvedImages was populated for every provider but the xAI
  branch POST body only forwarded text fields. Image-edit and
  multi-reference prompts silently degraded to text-only.

  P2: user-supplied image_size was ignored on the xAI path even though
  every other provider honors it via resolveOpenAIImageSize /
  imageConfig.imageSize.

Fix:

  * Add XAIImageReference and XAIImageRequestBase typed interfaces;
    combine into a discriminated XAIImageRequestBody union with mutually-
    exclusive image / images fields (text-only branch carries
    `image?: never; images?: never`).
  * Route to POST /v1/images/edits when resolvedImages.length > 0; map
    1 source -> `image: {url, type}`, 2-3 sources -> `images: [{url,
    type}, ...]` per docs.x.ai. Cap at 3 with a tool-level error; xAI
    documents that limit.
  * Reuse the existing toDataUrl(InlineImageData) helper for the `url`
    field (data: URIs are accepted alongside public URLs per docs.x.ai).
    `type: "image_url"` is the OpenAI-compat discriminator every official
    xAI code example sends.
  * Add resolveXAIResolution(image_size): map OpenAI-style pixel size to
    xAI's discrete "1k" | "2k" tier. 1024x1024 -> 1k; anything wider ->
    2k. Absent image_size still defaults to "1k", matching hermes-agent
    DEFAULT_RESOLUTION (plugins/image_gen/xai/__init__.py:71).
  * buildXAIEditPayload uses tuple destructure + explicit guard rather
    than `resolvedImages[0]`, staying safe under future
    noUncheckedIndexedAccess: true.

No effect on the OpenAI / OpenAI-codex / antigravity / gemini / openrouter
branches.

Op: correct
Restores: ref:feat/xai-grok-oauth@ecedf7c7e
2026-05-27 15:49:23 +00:00
metaphorics 91ac3496d4 fix(coding-agent): respect per-model xAI baseUrl overrides for tool traffic
resolveXAIHttpCredentials honored only \$env.XAI_BASE_URL — every
per-model baseUrl pin (models.yml model.baseUrl) and every provider-
level override (providers.xai-oauth.baseUrl) was silently bypassed for
image and TTS HTTP requests, even when the chat path went through the
override correctly via Model.baseUrl on the Responses request.

Add resolveXAIBaseURL: (1) per-model override when merged.baseUrl
diverges from the bundled default, scoped to (provider, id) so xai and
xai-oauth entries with the same id don't cross-route, (2) provider-level
baseUrl from ModelRegistry.getProviderBaseUrl, (3) XAI_BASE_URL env,
(4) DEFAULT_BASE_URL. resolveXAIHttpCredentials takes an optional
modelId; probes pass undefined and fall through to env/default.

Op: correct
Restores: ref:feat/xai-grok-oauth@2c1abd7fa
2026-05-27 15:26:25 +00:00
metaphorics 6ef2a4f3f1 fix(ai,coding-agent): gate xai-oauth on dedicated credential source
The cross-provider env fallback (stream.ts: "xai-oauth" → XAI_OAUTH_TOKEN
|| XAI_API_KEY) lets an XAI_API_KEY-only setup silently satisfy the
xai-oauth credential branch in resolveXAIHttpCredentials. Once the
helper enters that branch it resolves baseURL under xai-oauth instead of
xai, bypassing providers.xai.baseUrl overrides for image/TTS traffic.

Add AuthStorage.hasNonEnvCredential — hasAuth minus the env-fallback
leg — and gate the xai-oauth branch on (dedicated credential source ||
$env.XAI_OAUTH_TOKEN). The XAI_API_KEY borrow now falls through to the
xai branch, preserving back-compat while restoring provider-level
baseUrl precedence for users with a dedicated xai-oauth source.

Op: correct
Restores: ref:feat/xai-grok-oauth@015437534
2026-05-27 15:25:09 +00:00
cognitive 25c6794cd5 fix(agent): compaction honors session thinking level and silent-clamps unsupported-effort models
Triple-stacked failure on the same axis (thinking effort) produced the
user-visible

    Error: Compaction failed: Thinking effort high is not supported by
           xai-oauth/grok-build.
    Supported efforts:

(empty list after the colon) whenever the active model was a curated
xAI catalog entry with compat.supportsReasoningEffort: false.

Three defects lined up. (1) Behavior: compaction at four call sites
in packages/agent/src/compaction/compaction.ts hardcoded
reasoning: Effort.High and never threaded session.thinkingLevel —
the user's /model :off selection (and any explicit low/medium) was
silently overridden. On every other model this was invisible.
(2) Validation: requireSupportedEffort threw at the openai-flavored
mapper layer before the wire-side omitReasoningEffort gate in
providers/xai-responses.ts ever ran; two contradictory guards on the
same wire param. (3) Message: when getSupportedEfforts returned [],
the rendered error tail was 'Supported efforts: ' with nothing after
the colon — disappears as a side-effect of fix #2.

Fix #1 — thread ThinkingLevel | undefined end-to-end. Add
SummaryOptions.thinkingLevel and HandoffOptions.thinkingLevel.
Convert via a single exhaustive switch (effortFromThinkingLevel) in
the new resolveCompactionEffort helper:
  - Off            → undefined  (omit reasoning entirely)
  - undefined/Inherit → Effort.High → clamp per model (preserves the
                                       historical default for users
                                       who never touched the dial)
  - explicit Effort → respect user → clamp per model

resolveCompactionEffort lives in compaction.ts; all four call sites
(generateSummary, generateHandoff, generateShortSummary,
generateTurnPrefixSummary) route through it. agent-session.ts threads
this.thinkingLevel into all three production compaction entry points
(manual /compact at L6201, auto-compaction at L6458 — the most-fired
path, originally missed in plan review — and direct generateHandoff
at L5465). The audit-gate test
(test/agent-session-compaction-thinking-threading.test.ts) scans the
file with a brace-balanced extractor and refuses any unthreaded site.

Fix #2 — silent-clamp at the openai-flavored mapper layer. Extract
exported modelOmitsReasoningEffort(model) in model-thinking.ts as the
single source of truth for compat.supportsReasoningEffort: false on
openai-responses* APIs. getSupportedEfforts now calls it instead of
inlining the check (pure refactor — observable behavior preserved).
resolveOpenAiReasoningEffort in stream.ts early-returns undefined
when the predicate is true, so the wire-side omitReasoningEffort
gate (providers/xai-responses.ts:78) becomes the single source of
truth for the actual strip — no redundant throw.

Three regression tests pin the contract:
  - packages/ai/test/xai-oauth-effort-strip.test.ts (5 tests):
    modelOmitsReasoningEffort returns true for grok-build and
    grok-4.20-0309-reasoning, false for grok-4.3 / Anthropic /
    openai-completions.
  - packages/agent/test/compaction-thinking-level.test.ts (5 tests):
    every ThinkingLevel outcome through generateHandoff — Off stays
    undefined (not coerced to High), Low stays Low, Inherit / undefined
    default to High, grok-build clamps to undefined regardless of
    requested level. Covers the Codex-caught Off-vs-not-provided
    distinction.
  - packages/coding-agent/test/agent-session-compaction-thinking-threading.test.ts
    (2 tests): brace-balanced source scan asserts every direct
    compact() / generateHandoff() in agent-session.ts threads
    'thinkingLevel: this.thinkingLevel'; floor of 3 threaded sites.

TDD red-green verified for fix #1: temporarily reverted the handoff
call-site back to hardcoded Effort.High → compaction-thinking-level
went 2 pass / 3 fail (Off coerced, Low overridden, grok-build throws);
restored → 5 pass / 0 fail.

Verified:
  - packages/agent:  127 pass / 0 fail
  - packages/ai:     1061 pass / 337 skip / 0 fail
  - packages/coding-agent (focused): 179 pass / 5 skip / 0 fail
  - biome + tsgo --noEmit clean across all three packages

Out of scope (follow-ups):
  - branch-summarization.ts:307 already passes no reasoning — no edit.
  - The empty-list error message at model-thinking.ts:296 is now
    structurally unreachable from the openai-responses path.
  - modelOmitsReasoningEffort and grokSupportsReasoningEffort
    (xai-responses.ts:22) overlap; collapse into a single predicate
    in a future commit.

Op: correct
Restores: spec:compaction-honors-session-thinking-level
Restores: spec:xai-oauth-grok-build-compaction-no-throw
(cherry picked from commit e07b47ee46769053c658819437e2478389a4cee0)
2026-05-27 15:01:20 +00:00
can1357 ff94f91104 feat: added extraBody support, xAI fixes, and image provider updates
- Added `extraBody` merging into OpenAI Responses request params.
- Fixed xAI OAuth redirect URI to fail fast on port conflicts.
- Exposed `antigravity` and `xai` as explicit `providers.image` options.
- Added `isImageProviderPreference` guard, replacing inline string checks.
- Fixed TTS tool to resolve output path relative to cwd and require write approval.
2026-05-27 15:20:55 +02:00