@oh-my-pi/pi-ai 18.12.6 → 18.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -48
- package/dist/types/auth/types.d.ts +1 -0
- package/dist/types/auth-retry.d.ts +3 -0
- package/dist/types/error/classes.d.ts +7 -4
- package/dist/types/error/flags.d.ts +7 -0
- package/dist/types/images/factory-droid.d.ts +21 -0
- package/dist/types/images/google-generative-ai.d.ts +33 -0
- package/dist/types/images/index.d.ts +1 -0
- package/dist/types/images/shared.d.ts +1 -0
- package/dist/types/providers/anthropic.d.ts +10 -0
- package/dist/types/providers/factory-droid.d.ts +24 -1
- package/dist/types/providers/google-gemini-cli.d.ts +10 -4
- package/dist/types/providers/inference-headers.d.ts +32 -0
- package/dist/types/providers/native-finish-reason.d.ts +2 -0
- package/dist/types/providers/openai-shared.d.ts +19 -2
- package/dist/types/providers/openrouter-generation.d.ts +16 -0
- package/dist/types/registry/oauth/cursor.d.ts +5 -3
- package/dist/types/registry/oauth/xiaomi.d.ts +2 -2
- package/dist/types/usage/cursor.d.ts +3 -2
- package/dist/types/utils/schema/json-schema-validator.d.ts +19 -1
- package/dist/types/utils/schema/normalize.d.ts +13 -4
- package/package.json +6 -6
- package/src/auth/refresh.ts +199 -78
- package/src/auth/types.ts +1 -0
- package/src/auth-gateway/routes/images.ts +2 -1
- package/src/auth-gateway/server.ts +37 -4
- package/src/auth-retry.ts +16 -1
- package/src/error/classes.ts +7 -4
- package/src/error/flags.ts +13 -2
- package/src/images/factory-droid-gemini-system.md +1 -0
- package/src/images/factory-droid.ts +211 -0
- package/src/images/google-antigravity.ts +3 -3
- package/src/images/google-generative-ai.ts +45 -28
- package/src/images/index.ts +4 -0
- package/src/images/shared.ts +1 -1
- package/src/providers/anthropic-user-profiles.ts +2 -1
- package/src/providers/anthropic.ts +373 -53
- package/src/providers/factory-droid.ts +58 -0
- package/src/providers/gitlab-duo-workflow.ts +82 -1
- package/src/providers/google-gemini-cli.ts +79 -20
- package/src/providers/google-shared.ts +31 -10
- package/src/providers/google.ts +1 -0
- package/src/providers/inference-headers.ts +65 -9
- package/src/providers/native-finish-reason.ts +13 -0
- package/src/providers/openai-codex-responses.ts +86 -7
- package/src/providers/openai-completions.ts +28 -4
- package/src/providers/openai-responses.ts +39 -0
- package/src/providers/openai-shared.ts +41 -2
- package/src/providers/openrouter-generation.ts +55 -0
- package/src/registry/api-key-validation.ts +2 -1
- package/src/registry/oauth/cursor.ts +39 -14
- package/src/registry/oauth/factory-droid.ts +122 -46
- package/src/registry/oauth/google-antigravity.ts +2 -2
- package/src/registry/oauth/xiaomi.ts +43 -69
- package/src/stream.ts +83 -6
- package/src/usage/cline-pass.ts +6 -0
- package/src/usage/cursor.ts +148 -4
- package/src/usage/google-antigravity.ts +10 -3
- package/src/usage/kimi.ts +5 -0
- package/src/usage/openai-codex.ts +132 -7
- package/src/usage/zai.ts +6 -0
- package/src/utils/schema/json-schema-validator.ts +40 -1
- package/src/utils/schema/normalize.ts +92 -17
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,58 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.13.0] - 2026-10-26
|
|
6
|
+
|
|
7
|
+
### Breaking Changes
|
|
8
|
+
|
|
9
|
+
- `OpenAIRequestSetupModel` now requires the catalog-policy fields (`api`, `reasoning`), so callers building a reduced model for `resolveOpenAIRequestSetup` must pass them ([#16988](https://github.com/can1357/oh-my-pi/pull/16988) by [@rafalopilowski1](https://github.com/rafalopilowski1))
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Added `PI_ANTHROPIC_API_VERSION=2026-10-21` to send Anthropic API version 2026-10-21 to the official Anthropic API; sampling parameters are omitted under that version, which rejects them ([#16706](https://github.com/can1357/oh-my-pi/pull/16706) by [@H4vC](https://github.com/H4vC))
|
|
14
|
+
- OpenCode Go requests on lanes that opt into shared prompt caching send an explicit prompt-cache key as `x-opencode-session`, so Task siblings, forks and their parent reuse one gateway cache; custom providers declaring `providerType: "opencode-go"` get the same OpenCode headers ([#16988](https://github.com/can1357/oh-my-pi/pull/16988) by [@rafalopilowski1](https://github.com/rafalopilowski1))
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Reduced the CPU spent preparing tool schemas on every model call, so requests with many tools start faster ([#16780](https://github.com/can1357/oh-my-pi/pull/16780) by [@yuzu-octopus](https://github.com/yuzu-octopus))
|
|
19
|
+
- Fixed tool schema normalization modifying the caller's schema in place, which added the folded `const` value to a tool's own `enum` array ([#16780](https://github.com/can1357/oh-my-pi/pull/16780) by [@yuzu-octopus](https://github.com/yuzu-octopus))
|
|
20
|
+
- Google Antigravity requests now carry the same request labels as the official 2.26.0 client (`cascade_id`, `root_cascade_id`, `request_id`, `used_non_gemini_model`), and image model lookup sends the account's project ([#17012](https://github.com/can1357/oh-my-pi/pull/17012) by [@apoc](https://github.com/apoc))
|
|
21
|
+
- Google Antigravity requests now send each model tier's own `model_enum` label, output cap and thinking setting from the serving account's model list, as the official client does: Claude 5.5 and Gemini 4 Argon tiers send a thinking level, Gemini 3.8 Flash tiers the tier's thinking budget ([#17012](https://github.com/can1357/oh-my-pi/pull/17012) by [@apoc](https://github.com/apoc))
|
|
22
|
+
- Google Antigravity requests now number conversation steps like the official client (the first request is step 1, and each model turn and tool result adds one), send `Accept-Encoding: gzip` without an explicit `Accept`, and no longer send the Claude thinking beta header to Claude 5.5 ([#17012](https://github.com/can1357/oh-my-pi/pull/17012) by [@apoc](https://github.com/apoc))
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Anthropic-compatible endpoints that require a `thinking` block on every turn of the current turn no longer replay a 400 when the history holds a turn an upstream answered without reasoning; the serializer pads those turns ([#16739](https://github.com/can1357/oh-my-pi/pull/16739) by [@lz37](https://github.com/lz37))
|
|
27
|
+
- Fixed an Anthropic stream that a gateway or proxy cuts after `message_start`, substituting an in-band `error` frame, being reported as a completed-turn provider failure instead of a retryable stream truncation ([#16990](https://github.com/can1357/oh-my-pi/pull/16990) by [@Maziar123](https://github.com/Maziar123))
|
|
28
|
+
- Fixed Kimi Code, Z.AI, and ClinePass usage polls logging a warning every poll for accounts without a metered plan ([#17057](https://github.com/can1357/oh-my-pi/pull/17057) by [@trukhinyuri](https://github.com/trukhinyuri))
|
|
29
|
+
- Fixed Codex usage showing no limits for credit-billed ChatGPT Business/Enterprise seats: `/wham/usage` returns `rate_limit: null` for them, and their per-user credit cap (`spend_control.individual_limit`) is now reported as a `Spend cap (credits)` limit that also gates chat requests when no plan windows exist ([#16973](https://github.com/can1357/oh-my-pi/pull/16973) by [@DusKing1](https://github.com/DusKing1))
|
|
30
|
+
- Fixed overlapping OpenAI Codex requests in the same session (e.g. concurrent auth-gateway requests) closing the WebSocket another request was still streaming on; the later request now goes over SSE ([#16812](https://github.com/can1357/oh-my-pi/pull/16812) by [@Abelo9996](https://github.com/Abelo9996))
|
|
31
|
+
- Fixed Codex WebSocket turns dying with `unsupported_native_inflight_message` when the native turn lane was still busy; the turn is now retried as an independent full-context request instead of surfacing the error ([#17065](https://github.com/can1357/oh-my-pi/pull/17065) by [@AgileInnov8tor](https://github.com/AgileInnov8tor))
|
|
32
|
+
- Fixed OpenRouter native failures appearing as successful empty stops and malformed Responses being replayed before corrective recovery; metadata permission/rate-limit failures no longer trigger malformed-response recovery ([#17063](https://github.com/can1357/oh-my-pi/pull/17063) by [@martijnberger](https://github.com/martijnberger))
|
|
33
|
+
- Fixed Xiaomi MiMo API-key login failing with `Unsupported model mimo-v2.5` after the V2.5 retirement ([#16727](https://github.com/can1357/oh-my-pi/pull/16727) by [@LiPu-jpg](https://github.com/LiPu-jpg))
|
|
34
|
+
- Fixed Xiaomi MiMo login rejecting `ttp-` Token Plan keys with `401 Invalid API Key`; they now validate against the regional Token Plan hosts like `tp-` keys ([#17045](https://github.com/can1357/oh-my-pi/issues/17045))
|
|
35
|
+
- Fixed Xiaomi MiMo Token Plan login reporting `401 Invalid API Key` when the key's own region rejected the probe for another reason; that region's actual error is now shown ([#17045](https://github.com/can1357/oh-my-pi/issues/17045))
|
|
36
|
+
- Fixed non-streaming requests reporting a missing API key when the credential resolver actually failed, hiding auth broker, OAuth and network outages behind "No API key" ([#16655](https://github.com/can1357/oh-my-pi/pull/16655) by [@adi-IL](https://github.com/adi-IL))
|
|
37
|
+
- Fixed Claude subagents with large structured output schemas failing with `Schemas contains too many optional parameters` ([#16887](https://github.com/can1357/oh-my-pi/pull/16887) by [@alphastorm](https://github.com/alphastorm))
|
|
38
|
+
- Fixed auth-gateway requests failing when a client requests an unsupported thinking effort; the gateway now uses the nearest supported tier ([#16671](https://github.com/can1357/oh-my-pi/pull/16671) by [@codyw912](https://github.com/codyw912))
|
|
39
|
+
- Fixed Codex requests failing with `array schema must define items` when a tool or a task's `outputSchema` declares an array without `items` ([#16886](https://github.com/can1357/oh-my-pi/pull/16886) by [@alphastorm](https://github.com/alphastorm))
|
|
40
|
+
- Fixed Anthropic tools whose parameters are a root `anyOf`/`oneOf`/`allOf` of objects being sent as an empty closed object that only accepts `{}`; variant properties now fold into the root `input_schema` ([#16630](https://github.com/can1357/oh-my-pi/issues/16630))
|
|
41
|
+
- Fixed OAuth logins failing with `invalid_grant` after a token refresh took longer than 10 seconds: the refresh now finishes in the background and saves the provider's new refresh token ([#16291](https://github.com/can1357/oh-my-pi/pull/16291) by [@will-bogusz](https://github.com/will-bogusz))
|
|
42
|
+
- Fixed a tool call from a Claude model that declined mid-response still running after its server-side fallback model took over ([#16250](https://github.com/can1357/oh-my-pi/pull/16250) by [@will-bogusz](https://github.com/will-bogusz))
|
|
43
|
+
- Added 1h prompt caching on Factory Droid's Claude models (Anthropic, Vertex, Bedrock and Azure upstreams) when you set cache retention to long, and cache warming plus cache-timed idle compaction on those models ([#16296](https://github.com/can1357/oh-my-pi/pull/16296) by [@will-bogusz](https://github.com/will-bogusz))
|
|
44
|
+
- Fixed Factory Droid accounts whose login token lacks an organization getting 401 "User not affiliated with an organization" on every request: omp now resolves the account's organization at login and refresh ([#14061](https://github.com/can1357/oh-my-pi/issues/14061)) ([#16257](https://github.com/can1357/oh-my-pi/pull/16257) by [@will-bogusz](https://github.com/will-bogusz))
|
|
45
|
+
- Added image generation and editing through a Factory Droid login: `generate_image` runs Factory's GPT Image 2.5, GPT Image 2.5 Flare and Gemini 3 Pro Image, with up to 16 reference images for GPT Image edits ([#16316](https://github.com/can1357/oh-my-pi/pull/16316) by [@will-bogusz](https://github.com/will-bogusz))
|
|
46
|
+
- Fixed OpenAI-compatible chat completions with Anthropic-style prompt caching (`cacheControlFormat: "anthropic"`, e.g. OpenRouter Claude) re-sending a turn's tool calls and results uncached on every tool-loop request; the cache breakpoint now advances onto the latest tool result ([#16845](https://github.com/can1357/oh-my-pi/issues/16845))
|
|
47
|
+
- Fixed native Google (`google-generative-ai`, `google-vertex`) requests failing with `The operation timed out.` after ~5 minutes of slow prefill even when `providers.streamFirstEventTimeoutSeconds` allowed longer ([#16991](https://github.com/can1357/oh-my-pi/issues/16991))
|
|
48
|
+
- Fixed `model.loopGuard` settings being ignored for providers routed through the shared option mapping, such as `openai-responses` models from `models.yml` ([#11129](https://github.com/can1357/oh-my-pi/issues/11129), [#16963](https://github.com/can1357/oh-my-pi/pull/16963) by [@SipgateSteffenPaul](https://github.com/SipgateSteffenPaul))
|
|
49
|
+
- Fixed Google Antigravity's Gemini 4 Argon weekly bucket sharing the generic "Gemini" label with the shared Gemini weekly bucket, leaving usage rows indistinguishable; the Argon bucket now reads "Gemini (Argon)" ([#16790](https://github.com/can1357/oh-my-pi/issues/16790))
|
|
50
|
+
- Fixed Anthropic API-key requests with thinking off failing with 400 "context_management: Extra inputs are not permitted" on Claude Opus 5.5 and Fable 5.1, and on Claude Haiku 5.5 after an effort change (for example a retry fallback from a model without reasoning) ([#17047](https://github.com/can1357/oh-my-pi/pull/17047) by [@sjawhar](https://github.com/sjawhar))
|
|
51
|
+
- Fixed every GitLab Duo Agent request failing with 4400 "Flow configuration failed security validation" when a context file (such as an `AGENTS.md`) contains an HTML comment ([#17044](https://github.com/can1357/oh-my-pi/pull/17044) by [@trukhinyuri](https://github.com/trukhinyuri))
|
|
52
|
+
- Fixed a GitLab Duo Agent session that GitLab rejects on close (WebSocket 4000–4999) ending as an empty answer that was silently retried three times; it now ends with an error naming GitLab's reason and HTTP status ([#17044](https://github.com/can1357/oh-my-pi/pull/17044) by [@trukhinyuri](https://github.com/trukhinyuri))
|
|
53
|
+
- Fixed Cursor GitHub and Google logins never getting an account email ([#17048](https://github.com/can1357/oh-my-pi/pull/17048) by [@trukhinyuri](https://github.com/trukhinyuri))
|
|
54
|
+
- Fixed Cursor X Subscription usage treating the weekly Grok/Composer pool as third-party budget; third-party models are now metered by prepaid credit ([#17048](https://github.com/can1357/oh-my-pi/pull/17048) by [@trukhinyuri](https://github.com/trukhinyuri))
|
|
55
|
+
- Fixed Google models (`google-generative-ai`, `google-vertex`, `google-gemini-cli`/Antigravity) running out of output on small `maxTokens` requests with reasoning on, ending with thinking only and no answer; the requested `maxTokens` now leaves room for thinking on top, as on other providers ([#16943](https://github.com/can1357/oh-my-pi/pull/16943) by [@Mouriya-Emma](https://github.com/Mouriya-Emma))
|
|
56
|
+
|
|
5
57
|
## [18.12.5] - 2026-10-21
|
|
6
58
|
|
|
7
59
|
### Added
|
|
@@ -2266,51 +2318,4 @@
|
|
|
2266
2318
|
|
|
2267
2319
|
- Fixed local llama.cpp (and any local OpenAI-compatible server rendering the Qwen3.6+ chat template) re-processing the full prompt every new user message even with `replayReasoningContent` enabled (#3541 follow-up to #3528). Sending `reasoning_content` alone wasn't enough: Qwen3's chat template strips `<think>...</think>` from any assistant turn whose index is `<= last_query_index`, so the moment a new user message (the user's next prompt, or the auto-learn capture-at-stop nudge) lands, every prior assistant turn becomes "older" and is re-rendered without the `<think>` block — diverging from the generation tokens still in the slot's KV cache. The chat-completions encoder now emits `preserve_thinking: true` for Qwen thinking dialects on local servers, route-split the same way the existing `enable_thinking` emission is: the `qwen` dialect rides the top-level field (llama.cpp's `--jinja` hook and Alibaba Cloud Model Studio's compatible-mode), the `qwen-chat-template` dialect (NVIDIA NIM, vLLM/SGLang's chat-template-kwargs path) rides only `chat_template_kwargs.preserve_thinking` because NIM's request schema is `additionalProperties: false` and rejects unknown top-level fields (#2299). The emission is hoisted above the `reasoning.enabled` gate so it fires for THREE cases the original gating missed: (1) runtime-discovered local Qwen models that ship with `reasoning: false` because the upstream `/v1/models` doesn't advertise the capability (same gotcha #3532 fixed for `replayReasoningContent`), (2) caller-disabled reasoning (`/think off`) — the kwarg is a history-rendering knob, not a per-turn thinking switch, and the slot still holds `<think>` tokens from earlier turns, and (3) forced-tool-choice / DeepSeek-style auto-disable. Qwen3.6+ then renders `<think>...</think>` for every assistant turn regardless of position, and the next-turn render matches the cached generation tokens. ([#3541](https://github.com/can1357/oh-my-pi/issues/3541))
|
|
2268
2320
|
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
### Fixed
|
|
2272
|
-
|
|
2273
|
-
- Fixed llama.cpp / LM Studio / vLLM (and any local OpenAI-compatible server on a loopback or RFC1918 baseUrl) re-processing the full prompt on every assistant continuation when the prior turn produced `reasoning_content`: the `openai-completions` encoder dropped the preserved `thinking` block on re-serialization for compat profiles without `requiresReasoningContentForToolCalls` / `thinkingFormat: "zai"`, so the chat template re-rendered the assistant turn without `<think>…</think>` and the rendered tokens diverged from the slot's KV cache state. The auto-learn capture-at-stop nudge made it reproduce on every turn. The encoder now replays preserved thinking as `reasoning_content` (honoring the streamed signature when it identifies a recognized wire field — `reasoning_content` / `reasoning` / `reasoning_text` — and falling back to the configured `reasoningContentField` for opaque signatures) whenever the new `compat.replayReasoningContent` flag is set, and the cross-API `transformMessages` predicate (`openAICompletionsReplaysUnsignedThinking`) honors the same flag ahead of the `model.reasoning` gate so a switch into a discovered local target (where the spec carries `reasoning: false` because the upstream `/models` endpoints don't advertise the capability) still preserves the prior turn's thinking block as signature-stripped reasoning instead of demoting it to conversation text. The chat-template-rendered prefix stays byte-stable across turns and llama.cpp's prefix KV cache survives. ([#3528](https://github.com/can1357/oh-my-pi/issues/3528))
|
|
2274
|
-
|
|
2275
|
-
## [16.1.21] - 2026-06-26
|
|
2276
|
-
|
|
2277
|
-
### Fixed
|
|
2278
|
-
|
|
2279
|
-
- Restored the `pollOAuthDeviceCodeFlow` export from `@oh-my-pi/pi-ai/oauth` so legacy provider extensions can reuse the host OAuth device-code poller. ([#3508](https://github.com/can1357/oh-my-pi/issues/3508))
|
|
2280
|
-
|
|
2281
|
-
## [16.1.20] - 2026-06-25
|
|
2282
|
-
|
|
2283
|
-
### Fixed
|
|
2284
|
-
|
|
2285
|
-
- Fixed Ollama/Ollama Cloud native chat responses that finish with `done_reason: "length"` and no assistant content surfacing as a normal empty stop; they now become a context-window error instead of entering empty-stop retry recovery. ([#3464](https://github.com/can1357/oh-my-pi/issues/3464))
|
|
2286
|
-
- Fixed direct Anthropic Claude Sonnet/Haiku 4.5 requests serializing `output_config.effort`. The catalog classification (`packages/catalog/src/model-thinking.ts`) drove the `anthropic-budget-effort` branch in `buildParams`, which Anthropic's first-party Messages API rejects on Sonnet/Haiku 4.5 with HTTP 400 `This model does not support the effort parameter.` Sonnet/Haiku 4.5 now use plain `thinking.budget_tokens`; Opus 4.5 still emits `output_config.effort` because Anthropic supports it there. ([#3497](https://github.com/can1357/oh-my-pi/issues/3497))
|
|
2287
|
-
|
|
2288
|
-
## [16.1.19] - 2026-06-25
|
|
2289
|
-
|
|
2290
|
-
### Fixed
|
|
2291
|
-
|
|
2292
|
-
- Fixed Ollama/llama.cpp chat payloads serializing user-attributed mid-conversation developer messages (auto-learn capture nudge, advisor cards, file-mention companions) as `system` turns; they now serialize as `user` so llama.cpp can reuse the warm prompt prefix instead of forcing full re-processing. Agent-owned developer reminders (`attribution: "agent"` — empty/unexpected-stop retries, checkpoint rewind warning, todo reminders) keep their `system` priority. ([#3456](https://github.com/can1357/oh-my-pi/issues/3456))
|
|
2293
|
-
- Fixed prior-turn reasoning being lost on cross-API provider switches: when a session moved from an Anthropic-compatible 3p endpoint to an OpenAI-compatible one (Z.AI Anthropic → Z.AI OpenAI, Kimi Anthropic → Kimi OpenAI, DeepSeek, OpenCode-hosted reasoning models, or any custom `models.yaml` switch that crosses API types), the cross-API path of `transformMessages` text-demoted every prior `thinking` block, so the next request shipped the reasoning chain as plain conversation `content` instead of structured `reasoning_content` — losing it as reasoning context and re-billing it. `convertMessages` now threads the request-time resolved compat into `transformMessages`, which preserves the prior reasoning as a native, signature-stripped `thinking` block whenever that resolved target accepts `reasoning_content` as a continuation hint (`requiresReasoningContentForToolCalls` — including the `whenThinking` policy OpenCode reactivates for thinking-on requests, #1071/#1484 — or `thinkingFormat: "zai"`); the `openai-completions` encoder surfaces those blocks via `reasoningContentField`, with a new branch for Z.AI-format hosts (Z.AI, Zhipu, Moonshot Kimi, Xiaomi MiMo) that accept but don't require the field. Targets that can't replay unsigned reasoning (encrypted reasoning blobs, signed thought parts, non-reasoning models, thinking-disabled OpenCode) still text-demote so the reasoning survives as conversation context. ([#3437](https://github.com/can1357/oh-my-pi/pull/3437), [#3439](https://github.com/can1357/oh-my-pi/pull/3439) by [@roboomp](https://github.com/roboomp); [#3433](https://github.com/can1357/oh-my-pi/issues/3433), [#3434](https://github.com/can1357/oh-my-pi/issues/3434))
|
|
2294
|
-
- Fixed Bedrock cross-region inference profiles routing to `us-east-1` regardless of their geo prefix: a profile such as `eu.anthropic.claude-…` (or `apac.`/`au.`/`jp.`) sent to the hardcoded `us-east-1` endpoint returned HTTP 400 `The provided model identifier is invalid`. `streamBedrock` now derives the runtime region from the profile's geo prefix — honoring an ambient `AWS_REGION`/`AWS_DEFAULT_REGION` only when it can serve that geo and falling back to the geo's default region otherwise — while explicit per-request and ARN-embedded regions still win and region-agnostic `global.` profiles stay unchanged.
|
|
2295
|
-
- Fixed malformed tool calls (empty `name`) wedging entire sessions in HTTP 400 loops: when a model occasionally emits `{ "name": "", "arguments": "{}" }` (observed: GLM-5.2 + thinking on long turns), the agent rejected the call at execution time with `Tool not found`, but the malformed block plus its error `toolResult` stayed in conversation history and every subsequent request 400'd on `tool_use.name`/`tool_calls[i].function.name` validation until the user ran `/clear`. `transformMessages` — the canonical sanitize boundary every provider passes through — now drops `toolCall` blocks with empty/whitespace `name`, pairs them with their `toolResult` messages only inside the same assistant→tool-result window (per-id FIFO queue cleared at non-result boundaries, so stale malformed calls without a result cannot consume later valid duplicate-id outputs), and drops the assistant turn when it has no replayable content left. Defensive (provider-agnostic, fires regardless of model), idempotent (no-op on a clean history), and self-healing (one round-trip after the fix lands sanitizes an already-poisoned session). ([#3458](https://github.com/can1357/oh-my-pi/issues/3458))
|
|
2296
|
-
|
|
2297
|
-
## [16.1.18] - 2026-06-25
|
|
2298
|
-
|
|
2299
|
-
### Added
|
|
2300
|
-
|
|
2301
|
-
- Added `listOAuthAccounts` for retrieving a read-only list of stored OAuth account identities
|
|
2302
|
-
- Added `getOAuthAccessAt` to resolve an OAuth token exclusively for a specific account position
|
|
2303
|
-
|
|
2304
|
-
### Changed
|
|
2305
|
-
|
|
2306
|
-
- Refactored OAuth token persistence and disable logic to use stable credential IDs instead of positional indices to prevent race conditions during concurrent updates
|
|
2307
|
-
- Updated OAuth failure classification to treat 403 status codes, rate limits, and network errors as transient, preventing unnecessary credential invalidation
|
|
2308
|
-
|
|
2309
|
-
### Fixed
|
|
2310
|
-
|
|
2311
|
-
- Fixed Codex Responses Lite staying enabled for image prompts, which caused GPT/Codex image turns to be rejected as `Invalid value: 'input_image'`; image-bearing Codex requests now fall back to the full Responses transport. ([#3421](https://github.com/can1357/oh-my-pi/issues/3421))
|
|
2312
|
-
- Fixed the auth-broker background refresher disabling OAuth credentials unconditionally (`disableCredentialById`) on a definitive refresh failure, so a credential another process or a fresh login rotated mid-refresh could be torn down even though the stored row already held a valid token. The definitive-failure teardown now happens inside `AuthStorage.refreshCredentialById` via the same compare-and-set the in-stream and usage-probe paths use — it disables only when the persisted row still matches the credential the refresh actually attempted, and reloads on a CAS loss; the refresher now only logs.
|
|
2313
|
-
- Fixed OAuth refresh persisting the rotated token by a positional index captured before the refresh `await`. A concurrent disable could reorder or shrink a provider's credential array while the refresh was in flight, landing the new token on the wrong row (or silently dropping it) and leaving accounts with a stale refresh token that failed — and was then disabled — on the next cycle. Refresh persistence, selection-index resync, and CAS-disable now address the row by id across `forceRefreshCredentialById`, candidate preflight, and in-stream selection (`#replaceCredentialById` / `#disableCredentialByIdIfMatches`).
|
|
2314
|
-
- Fixed `isDefinitiveOAuthFailure` treating a bare HTTP 403 (and generic `unauthorized` / access-token-expired wording) as a definitive credential failure, which permanently disabled healthy OAuth accounts on WAF, egress rate-limit, permission, and account-verification responses. Bare 403, rate limits (429), gateway/5xx, and more network errors (`ECONNRESET`, `ETIMEDOUT`, `EAI_AGAIN`, …) are now classified transient; only explicit dead-grant errors (`invalid_grant`, `invalid_token`, `unauthorized_client`, revoked, `refresh token … expired`) or a bare 401 tear the credential down.
|
|
2315
|
-
|
|
2316
|
-
Older entries are archived in [packages/ai/CHANGELOG.md@7a5f1425b9bd](https://github.com/can1357/oh-my-pi/blob/7a5f1425b9bd79c3c74ae1d8946782b0494c8aa9/packages/ai/CHANGELOG.md).
|
|
2321
|
+
Older entries are archived in [packages/ai/CHANGELOG.md@917a36589039](https://github.com/can1357/oh-my-pi/blob/917a365890396071a959dfd2865a3538518ef717/packages/ai/CHANGELOG.md).
|
|
@@ -560,6 +560,7 @@ export interface StoredOAuthRefreshOptions<T extends OAuthCredential = OAuthCred
|
|
|
560
560
|
signal?: AbortSignal;
|
|
561
561
|
keepCredentialOnRefreshFailure?: boolean | ((error: unknown) => boolean);
|
|
562
562
|
onRefreshFailure?: (error: unknown) => void;
|
|
563
|
+
/** How long the caller waits (for the lease and the exchange); the exchange itself runs on until its backstop. */
|
|
563
564
|
refreshTimeoutMs?: number;
|
|
564
565
|
refresh: (credential: T, signal?: AbortSignal) => Promise<OAuthCredentials>;
|
|
565
566
|
mergeRefreshedCredential?: (credential: T, refreshed: OAuthCredentials) => T;
|
|
@@ -111,6 +111,9 @@ export declare function resolveNextAuthRetryKey(state: AuthRetryKeyState, resolv
|
|
|
111
111
|
* enters sibling rotation. Ordinary 401/auth failures get one refresh-same,
|
|
112
112
|
* then rotate through distinct siblings; 403/usage-limit failures skip the
|
|
113
113
|
* refresh and rotate directly.
|
|
114
|
+
* - A resolver that throws on the initial resolve surfaces its cause (a
|
|
115
|
+
* broker/OAuth/network failure) instead of reading as a missing key; only a
|
|
116
|
+
* resolver that resolves without a credential is a "No API key" case.
|
|
114
117
|
*
|
|
115
118
|
* Used by non-streaming consumers (image generation, web search, completion
|
|
116
119
|
* helpers). The streaming driver in `stream.ts` implements the same policy with
|
|
@@ -54,10 +54,13 @@ export declare class AnthropicConnectionTimeoutError extends Error {
|
|
|
54
54
|
constructor();
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
|
-
* A malformed Anthropic SSE stream envelope — events arriving out
|
|
58
|
-
* (before `message_start`)
|
|
59
|
-
*
|
|
60
|
-
*
|
|
57
|
+
* A malformed or truncated Anthropic SSE stream envelope — events arriving out
|
|
58
|
+
* of order (before `message_start`), a retryable in-band `error` frame that ends
|
|
59
|
+
* the message before its terminal frame, or otherwise a stream that never
|
|
60
|
+
* reached that frame. A deterministic provider rejection keeps its
|
|
61
|
+
* completed-turn error instead. The message is prefixed with
|
|
62
|
+
* {@link STREAM_ENVELOPE_ERROR_PREFIX} so the shared envelope predicates
|
|
63
|
+
* classify it.
|
|
61
64
|
*/
|
|
62
65
|
export declare class AnthropicStreamEnvelopeError extends Error {
|
|
63
66
|
constructor(detail: string);
|
|
@@ -108,6 +108,13 @@ export declare function isStreamReadErrorText(text: string): boolean;
|
|
|
108
108
|
* `stopDetails.explanation` so loop-level salvage can classify it after the
|
|
109
109
|
* original `Error` instance is gone. */
|
|
110
110
|
export declare function isStreamEnvelopeErrorText(text: string): boolean;
|
|
111
|
+
/** Persisted-text form of a truncated stream envelope: an
|
|
112
|
+
* `Anthropic stream envelope error:` diagnostic naming the frame the stream
|
|
113
|
+
* never reached (`before message_start|delta|stop`). Covers both a connection
|
|
114
|
+
* that dies inside an open envelope and an in-band `error` frame that a
|
|
115
|
+
* gateway or proxy substitutes for the cut, so turn recovery can classify the
|
|
116
|
+
* family by its envelope marker instead of per-gateway wording. */
|
|
117
|
+
export declare function isStreamEnvelopeTruncationText(text: string): boolean;
|
|
111
118
|
export declare function classify(error: unknown, api?: Api): number;
|
|
112
119
|
/**
|
|
113
120
|
* Whether an error (or message string) classifies as an account usage/quota
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Model } from "@oh-my-pi/pi-catalog/types";
|
|
2
|
+
import type { ImageGenerationOptions, ImageGenerationRequest, ImageGenerationResult } from "./types.js";
|
|
3
|
+
/** Reference images Factory's JSON edits contract accepts per request. */
|
|
4
|
+
export declare const FACTORY_DROID_MAX_EDIT_IMAGES = 16;
|
|
5
|
+
/** Decoded bytes the CLI accepts per reference image. */
|
|
6
|
+
export declare const FACTORY_DROID_MAX_REFERENCE_BYTES: number;
|
|
7
|
+
/**
|
|
8
|
+
* Factory Droid `generate_image` transport, mirroring the CLI: GPT Image
|
|
9
|
+
* models call the proxy's OpenAI Images routes, a prompt alone on
|
|
10
|
+
* `/images/generations` and reference images inline on `/images/edits`
|
|
11
|
+
* (Factory converts `input_images` to upstream multipart); Gemini image
|
|
12
|
+
* models call the native Gemini route asking for TEXT and IMAGE output. Both
|
|
13
|
+
* carry chat's client identity. The Droid identity rides the proxy-only
|
|
14
|
+
* `instructions` field, or opens the Gemini system instruction ahead of the
|
|
15
|
+
* CLI's generate or edit directive.
|
|
16
|
+
*
|
|
17
|
+
* A 200 without an image (a Gemini prompt block, an empty data array) throws
|
|
18
|
+
* a response error, which ends the tool's chain like other malformed
|
|
19
|
+
* responses; HTTP rejections throw provider HTTP errors, which advance it.
|
|
20
|
+
*/
|
|
21
|
+
export declare function generateFactoryDroidImage(model: Model<"factory-droid-agent">, request: ImageGenerationRequest, options: ImageGenerationOptions): Promise<ImageGenerationResult>;
|
|
@@ -1,3 +1,36 @@
|
|
|
1
1
|
import type { Model } from "@oh-my-pi/pi-catalog/types";
|
|
2
2
|
import type { ImageGenerationOptions, ImageGenerationRequest, ImageGenerationResult } from "./types.js";
|
|
3
|
+
interface GeminiPart {
|
|
4
|
+
text?: string;
|
|
5
|
+
inlineData?: {
|
|
6
|
+
data?: string;
|
|
7
|
+
mimeType?: string;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/** One `generateContent` response, or one chunk of its SSE stream. */
|
|
11
|
+
export interface GeminiImageResponse {
|
|
12
|
+
candidates?: Array<{
|
|
13
|
+
content?: {
|
|
14
|
+
parts?: GeminiPart[];
|
|
15
|
+
};
|
|
16
|
+
finishReason?: string;
|
|
17
|
+
}>;
|
|
18
|
+
usageMetadata?: {
|
|
19
|
+
promptTokenCount?: number;
|
|
20
|
+
candidatesTokenCount?: number;
|
|
21
|
+
totalTokenCount?: number;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** The user turn of a Gemini image request: reference images first, then the prompt. */
|
|
25
|
+
export declare function geminiImageContents(request: ImageGenerationRequest): Array<{
|
|
26
|
+
role: "user";
|
|
27
|
+
parts: GeminiPart[];
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Collect images, text and usage from a response or its stream chunks: parts
|
|
31
|
+
* within a response are separate paragraphs, chunks continue the same text,
|
|
32
|
+
* and the last usage report stands.
|
|
33
|
+
*/
|
|
34
|
+
export declare function geminiImageResult(responses: Iterable<GeminiImageResponse>): ImageGenerationResult;
|
|
3
35
|
export declare function generateGoogleImage(model: Model, request: ImageGenerationRequest, options: ImageGenerationOptions): Promise<ImageGenerationResult>;
|
|
36
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Api, type ImageGenerationApi, type Model } from "@oh-my-pi/pi-catalog/types";
|
|
2
2
|
import type { ImageGenerationOptions, ImageGenerationRequest, ImageGenerationResult } from "./types.js";
|
|
3
|
+
export * from "./factory-droid.js";
|
|
3
4
|
export * from "./google-antigravity.js";
|
|
4
5
|
export * from "./google-generative-ai.js";
|
|
5
6
|
export * from "./openai-hosted.js";
|
|
@@ -11,6 +11,7 @@ export declare function usageFromWire(value: unknown): Usage;
|
|
|
11
11
|
export declare function imageBaseUrl(model: Model): string;
|
|
12
12
|
export declare function modelHeaders(model: Model, signal?: AbortSignal): Promise<Record<string, string>>;
|
|
13
13
|
export declare function errorMessage(rawText: string): string;
|
|
14
|
+
export declare function parseImageApiResponse(model: Model, response: Response): Promise<unknown>;
|
|
14
15
|
/** Request URL, or a builder for routes that depend on the bearer (xAI's `XAI_BASE_URL` rule). */
|
|
15
16
|
type ImageRequestUrl = string | ((bearer: string) => string);
|
|
16
17
|
export declare function postJson(options: {
|
|
@@ -326,15 +326,25 @@ export type AnthropicMessageParam = MessageParam;
|
|
|
326
326
|
* before the compaction block, so the assistant turn carrying it may open
|
|
327
327
|
* the conversation; the request must send the compaction beta (the stream
|
|
328
328
|
* entry point adds it whenever such a payload is present).
|
|
329
|
+
*
|
|
330
|
+
* `opts.thinkingEnabled` — the CURRENT request enables thinking. Combined with
|
|
331
|
+
* `model.compat.requiresThinkingPresence` this pads assistant turns that carry
|
|
332
|
+
* no thinking block at all (see the guard below).
|
|
329
333
|
*/
|
|
330
334
|
export declare function convertAnthropicMessages(messages: Message[], model: Model<"anthropic-messages">, isOAuthToken: boolean, opts?: {
|
|
331
335
|
serverSideFallbackEnabled?: boolean;
|
|
332
336
|
replayCompaction?: boolean;
|
|
333
337
|
replayLegacyCompaction?: boolean;
|
|
334
338
|
dropAllThinking?: boolean;
|
|
339
|
+
thinkingEnabled?: boolean;
|
|
335
340
|
droppedThinkingBlocks?: ReadonlySet<string>;
|
|
336
341
|
credentialId?: number;
|
|
337
342
|
/** On-demand compaction request: it ends inside the conversation, so it gets nothing live requests lack there. */
|
|
338
343
|
compactionRequest?: AnthropicCompactionRequest;
|
|
339
344
|
}): AnthropicMessageParam[];
|
|
345
|
+
/**
|
|
346
|
+
* Normalize a tool parameter schema into an Anthropic `input_schema`: root
|
|
347
|
+
* combinators fold into the root object ({@link foldAnthropicRootCombinators}),
|
|
348
|
+
* then {@link normalizeAnthropicToolSchemaNode} applies the keyword whitelist.
|
|
349
|
+
*/
|
|
340
350
|
export declare function normalizeAnthropicToolSchema(schema: unknown): unknown;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Effort } from "@oh-my-pi/pi-catalog/effort";
|
|
2
|
-
import
|
|
2
|
+
import { type FactoryDroidWire } from "@oh-my-pi/pi-catalog/wire/factory-droid";
|
|
3
|
+
import type { OAuthRequestIdentity } from "../auth/types.js";
|
|
4
|
+
import type { Api, Model, ServiceTier, StreamFunction, StreamOptions, ToolChoice } from "../types.js";
|
|
3
5
|
export interface FactoryDroidOptions extends StreamOptions {
|
|
4
6
|
reasoning?: Effort;
|
|
5
7
|
disableReasoning?: boolean;
|
|
@@ -10,4 +12,25 @@ export interface FactoryDroidOptions extends StreamOptions {
|
|
|
10
12
|
/** OMP-native response verbosity (responses wire `text.verbosity`). */
|
|
11
13
|
textVerbosity?: "low" | "medium" | "high";
|
|
12
14
|
}
|
|
15
|
+
/** Wires that carry Factory's `generate_image` models. */
|
|
16
|
+
export type FactoryDroidImageWire = Extract<FactoryDroidWire, "openai-responses" | "google-generate">;
|
|
17
|
+
/** Endpoint, identity headers and system identity for one Factory image request. */
|
|
18
|
+
export interface FactoryDroidImageRoute {
|
|
19
|
+
/** `openai-responses` rides the Images API beside Responses; `google-generate` the native Gemini route. */
|
|
20
|
+
wire: FactoryDroidImageWire;
|
|
21
|
+
baseUrl: string;
|
|
22
|
+
headers: Record<string, string>;
|
|
23
|
+
/** Droid identity the proxy requires: the Images `instructions` field or the Gemini system instruction. */
|
|
24
|
+
identity: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Bind an image request to the same client identity chat sends: the CLI's
|
|
28
|
+
* Images and Gemini image calls reuse its chat request headers for the
|
|
29
|
+
* model's upstream, and the proxy runs the same Droid identity check.
|
|
30
|
+
*/
|
|
31
|
+
export declare function factoryDroidImageRoute(model: Model<"factory-droid-agent">, token: string, sessionId: string | undefined,
|
|
32
|
+
/** Account identity of the credential serving this attempt; host, org and upstream follow it. */
|
|
33
|
+
identity: OAuthRequestIdentity | undefined): FactoryDroidImageRoute;
|
|
34
|
+
/** Whether a catalog model runs on the Factory Droid transport. */
|
|
35
|
+
export declare function isFactoryDroidModel(model: Model<Api>): model is Model<"factory-droid-agent">;
|
|
13
36
|
export declare const streamFactoryDroid: StreamFunction<"factory-droid-agent">;
|
|
@@ -58,14 +58,17 @@ export interface AntigravityProviderSessionState extends ProviderSessionState {
|
|
|
58
58
|
/**
|
|
59
59
|
* Per-conversation request-envelope identity that mirrors the real
|
|
60
60
|
* Antigravity client. `sessionId` is the signed-decimal session id;
|
|
61
|
-
* `agentId`/`trajectoryId` are UUIDs
|
|
62
|
-
*
|
|
63
|
-
* `
|
|
61
|
+
* `agentId`/`trajectoryId` are UUIDs (`agentId` doubles as
|
|
62
|
+
* `labels.cascade_id`/`root_cascade_id`); `stepIndex` is the last
|
|
63
|
+
* requestId step (monotonic); `requestCount` counts envelopes already built for the trajectory
|
|
64
|
+
* (`labels.request_id` suffix); `lastExecutionId` is the prior response id
|
|
65
|
+
* echoed as `labels.last_execution_id`.
|
|
64
66
|
*/
|
|
65
67
|
agentId?: string;
|
|
66
68
|
trajectoryId?: string;
|
|
67
69
|
sessionId?: string;
|
|
68
70
|
stepIndex?: number;
|
|
71
|
+
requestCount?: number;
|
|
69
72
|
lastExecutionId?: string;
|
|
70
73
|
}
|
|
71
74
|
export declare function getAntigravityProviderSessionState(providerSessionState: Map<string, ProviderSessionState> | undefined): AntigravityProviderSessionState | undefined;
|
|
@@ -75,6 +78,7 @@ interface ParsedGeminiCliCredentials {
|
|
|
75
78
|
refreshToken?: string;
|
|
76
79
|
expiresAt?: number;
|
|
77
80
|
email?: string;
|
|
81
|
+
accountId?: string;
|
|
78
82
|
}
|
|
79
83
|
export declare function parseGeminiCliCredentials(apiKeyRaw: string): ParsedGeminiCliCredentials;
|
|
80
84
|
export declare function shouldRefreshGeminiCliCredentials(expiresAt: number | undefined, isAntigravity: boolean, nowMs?: number): boolean;
|
|
@@ -114,4 +118,6 @@ interface CloudCodeAssistRequest {
|
|
|
114
118
|
requestId?: string;
|
|
115
119
|
}
|
|
116
120
|
export declare const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli">;
|
|
117
|
-
export declare function buildRequest(model: Model<"google-gemini-cli">, context: Context, projectId: string, options?: GoogleGeminiCliOptions, isAntigravity?: boolean
|
|
121
|
+
export declare function buildRequest(model: Model<"google-gemini-cli">, context: Context, projectId: string, options?: GoogleGeminiCliOptions, isAntigravity?: boolean,
|
|
122
|
+
/** Serving credential's account key; selects its discovered Antigravity request constants. */
|
|
123
|
+
accountKey?: string): CloudCodeAssistRequest;
|
|
@@ -1,10 +1,42 @@
|
|
|
1
1
|
/** Shared inference request identity headers. */
|
|
2
|
+
import type { CatalogPolicyInputs } from "@oh-my-pi/pi-catalog/types";
|
|
2
3
|
/** Options controlling provider and protocol inference headers. */
|
|
3
4
|
export interface InferenceHeaderOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Credential-bearing provider id. Wire decisions (protocol session
|
|
7
|
+
* headers, OpenCode attribution) resolve against the declared backend
|
|
8
|
+
* instead — see {@link providerType}.
|
|
9
|
+
*/
|
|
4
10
|
provider: string;
|
|
11
|
+
/**
|
|
12
|
+
* Declared backend identity for a custom provider (`providerType ?? provider`,
|
|
13
|
+
* the same target the catalog policy cascade resolves). Carried through so
|
|
14
|
+
* a credential-bearing custom provider declared as an OpenCode backend
|
|
15
|
+
* gets that backend's wire headers while auth and registry keep using
|
|
16
|
+
* {@link provider}.
|
|
17
|
+
*/
|
|
18
|
+
providerType?: string;
|
|
5
19
|
protocol: "anthropic" | "google" | "openai";
|
|
6
20
|
sessionId?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Shared prompt-cache affinity key (see {@link resolveOpenCodeGoCacheAffinity}).
|
|
23
|
+
* Carried only by `x-opencode-session` on the `opencode-go` backend;
|
|
24
|
+
* protocol-native session headers and every other backend keep `sessionId`.
|
|
25
|
+
*/
|
|
26
|
+
cacheAffinity?: string;
|
|
7
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Shared prompt-cache affinity for the OpenCode Go backend: on lanes opted
|
|
30
|
+
* into KDL `shared-prompt-cache`, related requests (Task siblings, forks,
|
|
31
|
+
* explicit-key callers) warm and hit the gateway's prefix cache under one
|
|
32
|
+
* shared key, so Go's session header carries that key instead of the
|
|
33
|
+
* conversation id.
|
|
34
|
+
*
|
|
35
|
+
* `promptCacheKey` MUST be an explicit, already retention-gated cache key —
|
|
36
|
+
* never the sessionId-derived fallback. The backend identity is
|
|
37
|
+
* `providerType ?? provider`; catalog policy is the only opt-in authority.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveOpenCodeGoCacheAffinity(model: CatalogPolicyInputs, promptCacheKey: string | undefined): string | undefined;
|
|
8
40
|
/** Set a header unless the map already contains that field under any casing. */
|
|
9
41
|
export declare function setHeaderIfAbsent(headers: Record<string, string>, name: string, value: string): void;
|
|
10
42
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Effort } from "@oh-my-pi/pi-catalog/effort";
|
|
2
|
-
import type { OpenAICompat, OpenAIReasoningDisableMode, OpenAIStreamMarkupHealingPattern, OpenRouterRouting, ResolvedOpenAICompat, ResolvedOpenAIResponsesCompat, ResolvedOpenAISharedCompat, VercelGatewayRouting } from "@oh-my-pi/pi-catalog/types";
|
|
2
|
+
import type { CatalogPolicyInputs, OpenAICompat, OpenAIReasoningDisableMode, OpenAIStreamMarkupHealingPattern, OpenRouterRouting, ResolvedOpenAICompat, ResolvedOpenAIResponsesCompat, ResolvedOpenAISharedCompat, VercelGatewayRouting } from "@oh-my-pi/pi-catalog/types";
|
|
3
3
|
import { type Api, type AssistantMessage, type CacheRetention, type ComputerToolCallMetadata, type Context, type ImageContent, type Message, type MessageAttribution, type Model, type ServiceTier, type StopReason, type StreamOptions, type TextContent, type TextSignatureV1, type ThinkingContent, type Tool, type ToolCall, type ToolResultMessage, type Usage } from "../types.js";
|
|
4
4
|
export type { OpenAIPromptCacheOptions } from "../types.js";
|
|
5
5
|
import { kStreamingLastParseLen, kStreamingPartialJson } from "../utils/block-symbols.js";
|
|
@@ -24,7 +24,15 @@ export interface OpenAIStrictToolsState {
|
|
|
24
24
|
disabledModelScopes: Set<string>;
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
/**
|
|
28
|
+
* The model a request setup may be built from: the transport facts it reads
|
|
29
|
+
* plus every fact the catalog policy cascade reads ({@link CatalogPolicyInputs}).
|
|
30
|
+
* `api` is required — the Go sibling cache-affinity rules are all `on-api`
|
|
31
|
+
* scoped, so a reduced model that omits it used to typecheck and silently
|
|
32
|
+
* leave the family header off. Callers handing a reduced model object must
|
|
33
|
+
* pass the real policy inputs; there is no default, inference, or fallback.
|
|
34
|
+
*/
|
|
35
|
+
export interface OpenAIRequestSetupModel extends OpenAIModelIdentity, CatalogPolicyInputs {
|
|
28
36
|
headers?: Record<string, string>;
|
|
29
37
|
premiumMultiplier?: number;
|
|
30
38
|
compat?: Pick<ResolvedOpenAISharedCompat, "promptCacheSessionHeader" | "sessionHeader">;
|
|
@@ -49,6 +57,15 @@ export interface OpenAIRequestSetupOptions {
|
|
|
49
57
|
};
|
|
50
58
|
sessionId?: string;
|
|
51
59
|
promptCacheSessionId?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Explicit prompt-cache identity only, never the sessionId fallback:
|
|
62
|
+
* callers set it from the already-resolved `promptCacheSessionId` when an
|
|
63
|
+
* explicit `promptCacheKey` was supplied, and leave it unset otherwise.
|
|
64
|
+
* Feeds the Go sibling cache-affinity header so an ordinary conversation
|
|
65
|
+
* id (especially one long enough to normalize to a `pc_` hash) can never
|
|
66
|
+
* flip `x-opencode-session` on opted-in lanes.
|
|
67
|
+
*/
|
|
68
|
+
promptCacheAffinityKey?: string;
|
|
52
69
|
}
|
|
53
70
|
export interface OpenAIRequestSetup {
|
|
54
71
|
copilotPremiumRequests: number | undefined;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FetchImpl } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Responses can normalize upstream failures to completed without exposing the
|
|
4
|
+
* native reason. Only a metadata-lag 404 with non-reasoning output usage triggers
|
|
5
|
+
* inferred malformed-response recovery; usage does not confirm the native reason.
|
|
6
|
+
* Other diagnostic failures leave the original empty-stop recovery unchanged.
|
|
7
|
+
*/
|
|
8
|
+
export declare function readOpenRouterGenerationError(args: {
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
headers: Record<string, string>;
|
|
11
|
+
responseId: string;
|
|
12
|
+
outputTokens?: number;
|
|
13
|
+
reasoningTokens?: number;
|
|
14
|
+
fetch?: FetchImpl;
|
|
15
|
+
signal: AbortSignal;
|
|
16
|
+
}): Promise<string | undefined>;
|
|
@@ -13,13 +13,15 @@ export declare function pollCursorAuth(uuid: string, verifier: string): Promise<
|
|
|
13
13
|
}>;
|
|
14
14
|
export declare function loginCursor(onAuthUrl: (url: string) => void, onPollStart?: () => void): Promise<OAuthCredentials>;
|
|
15
15
|
export declare function loginCursorHook(callbacks: OAuthController): Promise<OAuthCredentials>;
|
|
16
|
-
export declare function refreshCursorToken(refreshToken: string): Promise<OAuthCredentials>;
|
|
16
|
+
export declare function refreshCursorToken(refreshToken: string, signal?: AbortSignal): Promise<OAuthCredentials>;
|
|
17
17
|
export declare function refreshCursorHook(credentials: OAuthCredentials, signal?: AbortSignal): Promise<OAuthCredentials>;
|
|
18
18
|
/** Request headers that present `accessToken` to cursor.com as its user's web session. */
|
|
19
19
|
export declare function cursorSessionHeaders(userId: string, accessToken: string): Record<string, string>;
|
|
20
|
+
/** Unary Connect RPC call with an empty message, authenticated by `accessToken` as a bearer token. */
|
|
21
|
+
export declare function cursorConnectRequest(accessToken: string, signal?: AbortSignal): RequestInit;
|
|
20
22
|
/**
|
|
21
|
-
* Email of the Cursor account that owns `accessToken`, read from
|
|
22
|
-
*
|
|
23
|
+
* Email of the Cursor account that owns `accessToken`, read from Cursor's account profile.
|
|
24
|
+
* Undefined when the token names no subject or the profile names another.
|
|
23
25
|
*/
|
|
24
26
|
export declare function fetchCursorAccountEmail(accessToken: string, fetchImpl?: FetchImpl, signal?: AbortSignal): Promise<string | undefined>;
|
|
25
27
|
export declare function extractCursorAccessTokenUserId(accessToken: string): string | undefined;
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Xiaomi MiMo provides OpenAI-compatible models via
|
|
5
5
|
* https://api.xiaomimimo.com/v1.
|
|
6
6
|
*
|
|
7
|
-
* Standard Xiaomi login accepts both pay-as-you-go keys and regional
|
|
8
|
-
* keys, probing the regional Token Plan clusters in fallback order.
|
|
7
|
+
* Standard Xiaomi login accepts both pay-as-you-go keys and regional Token Plan
|
|
8
|
+
* (`tp-...`/`ttp-...`) keys, probing the regional Token Plan clusters in fallback order.
|
|
9
9
|
*/
|
|
10
10
|
import type { OAuthController } from "./types.js";
|
|
11
11
|
/**
|
|
@@ -8,9 +8,10 @@ import type { CredentialRankingStrategy, UsageProvider, UsageReport } from "../u
|
|
|
8
8
|
* Prefer a *usable* overall bucket; if overall is absent/disabled/malformed,
|
|
9
9
|
* fall through to plan rails (`autoPercentUsed` / `apiPercentUsed`). Always
|
|
10
10
|
* consider on-demand afterward so a valid on-demand meter is not dropped when
|
|
11
|
-
* the included plan bucket is empty.
|
|
11
|
+
* the included plan bucket is empty. X Subscription payloads take
|
|
12
|
+
* {@link parseUnifiedUsage}; `weeklyResetsAt` is its pool's next reset.
|
|
12
13
|
*/
|
|
13
|
-
export declare function parseCursorIndividualUsage(payload: unknown, fetchedAt?: number): UsageReport | null;
|
|
14
|
+
export declare function parseCursorIndividualUsage(payload: unknown, fetchedAt?: number, weeklyResetsAt?: number): UsageReport | null;
|
|
14
15
|
export declare function parseCursorUsage(payload: unknown, fetchedAt?: number): UsageReport | null;
|
|
15
16
|
/** Routes Cursor credential ranking and reserve checks through the requested model's billing pool. */
|
|
16
17
|
export declare const cursorRankingStrategy: CredentialRankingStrategy;
|
|
@@ -18,7 +18,25 @@ export interface JsonSchemaValidationResult {
|
|
|
18
18
|
}
|
|
19
19
|
/** The unique discriminator-selected branch, or undefined when selection is ambiguous or absent. */
|
|
20
20
|
export declare function getTagSelectedUnionBranch(branches: readonly unknown[], value: unknown): unknown;
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Whether any same-instance schema declaration or constraint owns this property name.
|
|
23
|
+
*
|
|
24
|
+
* The answer depends on exactly two things: the property name, and the content
|
|
25
|
+
* of the JSON-Schema graph reachable from the root. The cache is keyed by BOTH
|
|
26
|
+
* — the root's object identity (through {@link stamp}, the repo's
|
|
27
|
+
* identity-keyed side table) and the property name within it — so two
|
|
28
|
+
* schemas asking about the same name keep separate answers, and one schema
|
|
29
|
+
* asked about two names keeps two answers.
|
|
30
|
+
*
|
|
31
|
+
* The root identity covers the graph because the schema graphs reaching this
|
|
32
|
+
* function are already treated as immutable for the lifetime of their
|
|
33
|
+
* identity: `toolWireSchema` memoizes the wire schema against the tool's
|
|
34
|
+
* parameters, and `stripSchemaDescriptions`, `tryEnforceStrictSchema` and
|
|
35
|
+
* `memoizedInjectIntentIntoSchema` already memoize derived values against
|
|
36
|
+
* that same wire-schema object. A graph mutated in place after production
|
|
37
|
+
* would already hand those three a stale answer; this cache adds no new
|
|
38
|
+
* assumption beyond the ones the schema pipeline already relies on.
|
|
39
|
+
*/
|
|
22
40
|
export declare function schemaDefinesProperty(schema: unknown, key: string): boolean;
|
|
23
41
|
export declare function validateJsonSchemaValue(schema: unknown, value: unknown, root?: unknown): JsonSchemaValidationResult;
|
|
24
42
|
/** Validate a subschema using its complete schema's local-reference context. */
|
|
@@ -113,8 +113,15 @@ export declare function sanitizeSchemaForGrammar(schema: JsonObject): JsonObject
|
|
|
113
113
|
/**
|
|
114
114
|
* OpenAI Responses rejects `oneOf` in tool schemas even when strict mode is
|
|
115
115
|
* disabled, and rejects every schema node with `type: "object"` unless it has
|
|
116
|
-
* a `properties` member.
|
|
117
|
-
*
|
|
116
|
+
* a `properties` member. Codex opts into repairing arrays without `items` with
|
|
117
|
+
* explicit open `items: true`, including the default open tail after
|
|
118
|
+
* `prefixItems`, which makes strict adaptation fall back to non-strict.
|
|
119
|
+
* The repair is off by default: other Responses routes include grammar
|
|
120
|
+
* backends that cannot compile boolean subschemas. If `unevaluatedItems`
|
|
121
|
+
* appears in any schema position, repair is disabled for the whole tool:
|
|
122
|
+
* references and composition can carry evaluated-item effects across subtrees.
|
|
123
|
+
* Normalize only schema-valued positions so literal payloads under `enum`,
|
|
124
|
+
* `const`, `default`, and `examples` remain unchanged.
|
|
118
125
|
*
|
|
119
126
|
* Identity-preserving: returns the input reference unchanged when no rewrite
|
|
120
127
|
* occurred so callers can dedupe via reference equality (and the strict-mode
|
|
@@ -122,12 +129,14 @@ export declare function sanitizeSchemaForGrammar(schema: JsonObject): JsonObject
|
|
|
122
129
|
* concatenated (the wire payload accepts a single union; preserving both
|
|
123
130
|
* would not survive).
|
|
124
131
|
*/
|
|
125
|
-
export declare function sanitizeSchemaForOpenAIResponses(schema: JsonObject
|
|
132
|
+
export declare function sanitizeSchemaForOpenAIResponses(schema: JsonObject, options?: {
|
|
133
|
+
repairArrayItems?: boolean;
|
|
134
|
+
}): JsonObject;
|
|
126
135
|
/**
|
|
127
136
|
* Alias for {@link sanitizeSchemaForOpenAIResponses} matching the
|
|
128
137
|
* `normalizeSchemaFor*` dispatcher naming used elsewhere in this module.
|
|
129
138
|
*/
|
|
130
|
-
export declare const normalizeSchemaForOpenAIResponses:
|
|
139
|
+
export declare const normalizeSchemaForOpenAIResponses: typeof sanitizeSchemaForOpenAIResponses;
|
|
131
140
|
/**
|
|
132
141
|
* First pass of strict-mode preparation.
|
|
133
142
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-ai",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.13.0",
|
|
4
4
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -155,11 +155,11 @@
|
|
|
155
155
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
156
156
|
},
|
|
157
157
|
"dependencies": {
|
|
158
|
-
"@oh-my-pi/omptype": "18.
|
|
159
|
-
"@oh-my-pi/pi-catalog": "18.
|
|
160
|
-
"@oh-my-pi/pi-natives": "18.
|
|
161
|
-
"@oh-my-pi/pi-utils": "18.
|
|
162
|
-
"@oh-my-pi/pi-wire": "18.
|
|
158
|
+
"@oh-my-pi/omptype": "18.13.0",
|
|
159
|
+
"@oh-my-pi/pi-catalog": "18.13.0",
|
|
160
|
+
"@oh-my-pi/pi-natives": "18.13.0",
|
|
161
|
+
"@oh-my-pi/pi-utils": "18.13.0",
|
|
162
|
+
"@oh-my-pi/pi-wire": "18.13.0"
|
|
163
163
|
},
|
|
164
164
|
"devDependencies": {
|
|
165
165
|
"@types/bun": "^1.3.14"
|