@oh-my-pi/pi-ai 18.13.3 → 18.14.1

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.
Files changed (83) hide show
  1. package/CHANGELOG.md +54 -59
  2. package/dist/types/auth/abort.d.ts +3 -1
  3. package/dist/types/auth/cascade.d.ts +2 -0
  4. package/dist/types/auth/rotation.d.ts +2 -1
  5. package/dist/types/auth/select.d.ts +2 -0
  6. package/dist/types/auth/store.d.ts +1 -1
  7. package/dist/types/auth/types.d.ts +12 -3
  8. package/dist/types/auth/usage.d.ts +1 -0
  9. package/dist/types/auth-broker/client.d.ts +3 -2
  10. package/dist/types/auth-broker/remote-store.d.ts +3 -3
  11. package/dist/types/auth-gateway/dispatch.d.ts +17 -1
  12. package/dist/types/error/abort.d.ts +5 -0
  13. package/dist/types/error/finalize.d.ts +5 -4
  14. package/dist/types/index.d.ts +3 -0
  15. package/dist/types/providers/anthropic-headers.d.ts +84 -0
  16. package/dist/types/providers/anthropic-http-options.d.ts +15 -0
  17. package/dist/types/providers/anthropic.d.ts +1 -60
  18. package/dist/types/providers/cursor.d.ts +35 -5
  19. package/dist/types/providers/factory-droid/gemini.d.ts +24 -1
  20. package/dist/types/providers/openai-codex/access-programs.d.ts +11 -3
  21. package/dist/types/providers/openai-codex/live-steering.d.ts +2 -0
  22. package/dist/types/providers/openai-codex-responses.d.ts +110 -37
  23. package/dist/types/providers/openai-codex-state.d.ts +43 -0
  24. package/dist/types/providers/openai-completions.d.ts +4 -1
  25. package/dist/types/providers/openai-responses-state.d.ts +48 -0
  26. package/dist/types/providers/openai-responses.d.ts +2 -34
  27. package/dist/types/providers/pi-native-server.d.ts +2 -1
  28. package/dist/types/providers/register-builtins.d.ts +3 -3
  29. package/dist/types/types.d.ts +8 -1
  30. package/dist/types/utils/abort.d.ts +4 -1
  31. package/package.json +7 -7
  32. package/src/auth/abort.ts +7 -2
  33. package/src/auth/cascade.ts +7 -0
  34. package/src/auth/rank.ts +6 -4
  35. package/src/auth/refresh.ts +15 -10
  36. package/src/auth/rotation.ts +10 -5
  37. package/src/auth/select.ts +98 -52
  38. package/src/auth/store.ts +1 -1
  39. package/src/auth/types.ts +12 -3
  40. package/src/auth/usage.ts +14 -4
  41. package/src/auth-broker/client.ts +7 -4
  42. package/src/auth-broker/remote-store.ts +47 -26
  43. package/src/auth-broker/server.ts +4 -1
  44. package/src/auth-gateway/dispatch.ts +22 -2
  45. package/src/auth-gateway/server.ts +18 -18
  46. package/src/error/abort.ts +12 -0
  47. package/src/error/finalize.ts +10 -5
  48. package/src/index.ts +3 -0
  49. package/src/judgment/native.ts +15 -6
  50. package/src/judgment/text-judge-retry.md +1 -1
  51. package/src/judgment/text-judge-state.md +1 -1
  52. package/src/judgment/text-judge.md +0 -3
  53. package/src/provider-details.ts +1 -1
  54. package/src/provider-session-state.ts +1 -1
  55. package/src/providers/anthropic-headers.ts +412 -0
  56. package/src/providers/anthropic-http-options.ts +148 -0
  57. package/src/providers/anthropic.ts +57 -553
  58. package/src/providers/connection-prewarm.ts +1 -1
  59. package/src/providers/cursor/proto/agent.proto +5 -0
  60. package/src/providers/cursor.ts +610 -67
  61. package/src/providers/factory-droid/droid-identity.md +1 -1
  62. package/src/providers/factory-droid/gemini.ts +2 -2
  63. package/src/providers/factory-droid.ts +2 -1
  64. package/src/providers/gitlab-duo-workflow-chatml-note.md +1 -1
  65. package/src/providers/gitlab-duo.ts +1 -0
  66. package/src/providers/openai-anthropic-shim.ts +1 -0
  67. package/src/providers/openai-codex/access-programs.ts +21 -4
  68. package/src/providers/openai-codex/live-steering.ts +10 -1
  69. package/src/providers/openai-codex-responses.ts +54 -197
  70. package/src/providers/openai-codex-state.ts +194 -0
  71. package/src/providers/openai-completions.ts +14 -4
  72. package/src/providers/openai-responses-state.ts +100 -0
  73. package/src/providers/openai-responses.ts +7 -93
  74. package/src/providers/openai-shared.ts +6 -1
  75. package/src/providers/pi-native-client.ts +60 -9
  76. package/src/providers/pi-native-server.ts +36 -4
  77. package/src/providers/register-builtins.ts +68 -52
  78. package/src/registry/engine/api-key.ts +3 -1
  79. package/src/stream.ts +2 -0
  80. package/src/types.ts +8 -1
  81. package/src/utils/abort.ts +11 -2
  82. package/src/utils/anthropic-auth.ts +5 -1
  83. package/THIRD-PARTY-NOTICES.txt +0 -22901
package/CHANGELOG.md CHANGED
@@ -2,6 +2,59 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.14.1] - 2026-10-31
6
+
7
+ ### Added
8
+
9
+ - Added the `codexAccessPrograms` stream option and `configureCodexAccessPrograms()` process default to turn off the automatic Daybreak Blue `access_programs` request on Codex ([#17508](https://github.com/can1357/oh-my-pi/pull/17508) by [@H4vC](https://github.com/H4vC))
10
+ - Added `waitForSibling` to `RotateCredentialOptions`; set it to `false` to skip the short sibling-unblock wait in `limits.rotate()` while still blocking the failed credential ([#17439](https://github.com/can1357/oh-my-pi/pull/17439) by [@wolfiesch](https://github.com/wolfiesch))
11
+
12
+ ### Changed
13
+
14
+ - Cursor runs served through `omp auth-gateway` are limited to the client's own tools, all advertised as MCP; Cursor's built-in tools (file, shell, edit, todo, web fetch, SCM) are no longer offered on that path ([#17382](https://github.com/can1357/oh-my-pi/pull/17382) by [@eggpeat](https://github.com/eggpeat))
15
+
16
+ ### Fixed
17
+
18
+ - Fixed native judgment requests (TypeSafe, OpenRouter Decisions, OpenAI Decisions, Workers AI) sending credentials in their state unredacted while credential redaction was on ([#17443](https://github.com/can1357/oh-my-pi/pull/17443))
19
+ - Fixed every new Codex session, including each subagent, losing a response and retrying when the native turn lane refuses live steering; once refused, later sessions on the same account, endpoint, and model queue follow-ups for four hours ([#14158](https://github.com/can1357/oh-my-pi/issues/14158), [#17438](https://github.com/can1357/oh-my-pi/pull/17438) by [@wolfiesch](https://github.com/wolfiesch))
20
+ - Fixed Cursor models served through `omp auth-gateway` being told their file and shell tools were unavailable while the client ran the calls anyway, in Cursor's own argument shape (edits failed validation); each call now reaches the client as one of its own tools and the turn stops with `toolUse` until the result comes back ([#17382](https://github.com/can1357/oh-my-pi/pull/17382) by [@eggpeat](https://github.com/eggpeat))
21
+ - Fixed Cursor models served through `omp auth-gateway` being told each tool call was handed off, which left Composer issuing calls blind and answering their results with empty stops; the gateway now keeps the Cursor run open while the client runs a step's calls and answers them in-band with the client's results on its next request (up to 30 minutes; otherwise the turn is rebuilt from history as before) ([#17382](https://github.com/can1357/oh-my-pi/pull/17382) by [@eggpeat](https://github.com/eggpeat))
22
+ - Fixed Anthropic-compatible endpoints behind a relay opening a new connection for every turn: the response body is now drained for up to 2.5 s after `message_stop` instead of being cancelled, so the kept-alive connection is reused ([#17432](https://github.com/can1357/oh-my-pi/pull/17432) by [@flosrn](https://github.com/flosrn))
23
+
24
+ ## [18.14.0] - 2026-10-29
25
+
26
+ ### Breaking Changes
27
+
28
+ - Moved Anthropic header and endpoint helpers, along with Codex session/transport inspection and OpenAI Responses replay/storage helpers, to dedicated provider modules. Update imports from the previous locations.
29
+ - Updated auth-gateway model resolvers to receive `ModelResolveOptions`, including the route's native API wire and the scoped client's accepted grants. Scoped client IDs no longer fall back to scanning all models; resolvers now select the best permitted candidate.
30
+
31
+ ### Added
32
+
33
+ - Added mTLS support for Anthropic connections through the `CLAUDE_CODE_CLIENT_CERT` and `CLAUDE_CODE_CLIENT_KEY` environment variables.
34
+
35
+ ### Changed
36
+
37
+ - Provider implementations are now loaded on demand, reducing startup and memory overhead while preserving existing stream timeouts, cancellation, and host overrides.
38
+ - Account policy `priority` now takes precedence when usage information is temporarily unavailable, keeping new sessions on the preferred account.
39
+ - Improved provider-specific credential usage lookups so they no longer wait for unrelated providers, while preserving cache freshness, cancellation isolation, and complete aggregate reports. The corresponding server-side latency improvement requires an updated auth broker.
40
+ - Credential usage is now prefetched during session setup without selecting, pinning, or advancing the OAuth account rotation.
41
+
42
+ ### Fixed
43
+
44
+ - Fixed Mistral thinking-message replay requests that could be rejected by LiteLLM with a `422 extra_forbidden` error.
45
+ - Fixed native Judge API requests missing the `omp/<version>` User-Agent, so omp usage is correctly identified in upstream statistics.
46
+ - Fixed Codex capacity-error retries over WebSocket and SSE ignoring provider-supplied `Retry-After` delays and retrying too early.
47
+ - Fixed cancelled OAuth refresh requests aborting the entire session, and ensured requests joining an existing refresh can still use the refreshed credential when the original request is cancelled.
48
+ - Fixed Cursor models losing omp system-prompt context, including context files, skills, and rules, on subsequent turns or when earlier messages precede the active turn.
49
+ - Fixed short-lived commands that refresh OAuth credentials, such as `omp token --force-refresh`, remaining open for several seconds after completing.
50
+ - Fixed TypeSafe authentication when `TYPESAFE_BASE_URL` is configured by validating keys against the correct `/v1/models` endpoint.
51
+ - Fixed forced tool calls on non-reasoning models causing an unhandled reasoning-effort error.
52
+ - Fixed interrupted turns reporting a transport error instead of the caller-provided cancellation reason.
53
+ - Fixed long-running pi-native turns being terminated before their first event by fetch or proxy timeouts by supporting keepalives and the configured first-event timeout.
54
+ - Fixed pi-native clients crashing when the auth gateway reports an upstream failure during streaming.
55
+ - Fixed Cursor turns failing after a mid-turn stream interruption when an automatic retry receives a zero-token provider error; retries now continue on the rotated conversation.
56
+ - Corrected auth-gateway debug logs so they no longer claim a retry occurred when a usage-limit error was returned without retrying.
57
+
5
58
  ## [18.13.3] - 2026-10-28
6
59
 
7
60
  ### Changed
@@ -2266,62 +2319,4 @@
2266
2319
  - Enabled freeform tool patch support for Azure OpenAI and Codex models.
2267
2320
  - Fixed an issue where the `/usage show` command returned "No usage data available" when using a custom proxy base URL for Codex.
2268
2321
 
2269
- ## [16.2.2] - 2026-06-27
2270
-
2271
- ### Added
2272
-
2273
- - Added a comprehensive, public-facing error module exported via the "./error" path, featuring structured error classification, provider-specific HTTP error classes (e.g., Anthropic, OpenAI, Gemini), OAuth/Auth-specific errors, rate-limit utilities, and retryability predicates.
2274
-
2275
- ### Changed
2276
-
2277
- - Updated OpenAI Codex defaults to increase default text verbosity to medium, enable detailed reasoning summaries by default, and include all turns in the reasoning context by default.
2278
- - Updated the OpenAI Codex WebSocket transport to resolve its configuration (via PI_CODEX_WEBSOCKET_* environment variables) once at startup rather than re-parsing on every request.
2279
- - Enhanced cross-model reasoning recovery and preservation to render demoted reasoning in the target model's canonical inline thinking dialect (such as Gemini's thinking fence or standard think tags) to prevent leaking inert context or control tokens into history.
2280
- - Broadened the leaked-thinking stream healer to recover reasoning emitted in any dialect's canonical idiom (including Gemini, Gemma, Harmony, and scratchpads) and route them to thinking events instead of raw markup.
2281
- - Implemented automatic retry logic for detected thinking-loop stalls to improve response reliability.
2282
- - Hardened stateful delta chaining to ignore transient streaming bookkeeping symbols during structural equality checks, preventing unnecessary full-transcript replays.
2283
-
2284
- ### Fixed
2285
-
2286
- - Fixed preservation of OpenAI Responses assistant message phase values across auth-gateway parsing, streaming, and history replay, ensuring GPT-5.4/GPT-5.5 intermediate updates and final answers retain their original phase labels.
2287
-
2288
- ### Removed
2289
-
2290
- - Removed Pi dialect support and related serialization/parsing logic.
2291
-
2292
- ## [16.2.0] - 2026-06-27
2293
-
2294
- ### Breaking Changes
2295
-
2296
- - Removed the `@oh-my-pi/pi-ai/utils/json-parse` module. The JSON repair and parsing helpers (`repairJson`, `parseJsonWithRepair`, `parseStreamingJson`, `parseStreamingJsonThrottled`) have been moved to `@oh-my-pi/pi-utils` to be shared across utilities.
2297
-
2298
- ### Added
2299
-
2300
- - Added the GitLab Duo Agent provider (`gitlab-duo-agent`) and built-in implementation, renaming the existing AI Gateway proxy provider to "GitLab Duo Non-Agentic" (`gitlab-duo`).
2301
- - Added GitLab Duo Workflow provider support, featuring OAuth login via the official VS Code OAuth application, automatic project discovery, and automatic session-time namespace Duo settings enablement.
2302
- - Added runaway detection for Gemini models to interrupt streams stuck in excessive planning steps.
2303
- - Added a per-provider in-flight request limiter for LLM streams, shared across local OMP processes and configurable via `maxInFlightRequests`.
2304
- - Added a `credits` field to `UsageResetCredits` to display when banked rate-limit resets expire, with support for OpenAI Codex usage details.
2305
-
2306
- ### Changed
2307
-
2308
- - Optimized GitLab Duo Agent and Workflow providers to use an inline custom "ambient" flow with MCP-only agent privileges, registering MCP tools under their bare names.
2309
- - Improved GitLab Duo Agent context management and auto-compaction by lowering the soft overflow threshold to 1 MB and stripping redundant bytes (such as tool-call UUIDs and escaped JSON) from the goal transcript.
2310
- - Enhanced GitLab Duo Agent prompt engineering to render replayed tool calls as past-tense records, reducing model confusion and preventing the model from mimicking historical markers.
2311
- - Added caching for discovered GitLab Duo Agent root namespaces per account to avoid redundant discovery requests.
2312
-
2313
- ### Fixed
2314
-
2315
- - Fixed various GitLab Duo Agent and Workflow stability issues, including infinite tool-call loops, connection hangs on half-open WebSockets, and unhandled step-limit or generic server-side failures.
2316
- - Improved GitLab Duo Workflow routing, namespace resolution, and project-path handling, ensuring correct numeric ID resolution and support for self-managed GitLab relative install base paths.
2317
- - Fixed GitLab Duo Workflow checkpoint streaming to correctly map reasoning entries to thinking blocks, preserve tool boundaries, and accurately report token usage.
2318
- - Fixed `AuthStorage.login` to only synthesize manual-code paste prompts for paste-code providers, preventing terminal-blocking races on loopback OAuth flows.
2319
- - Fixed llama.cpp compatibility by downgrading named forced `tool_choice` objects to the string `"required"` in the chat-completions encoder.
2320
- - Fixed `omp usage` omitting Ollama and Ollama Cloud accounts by registering placeholder usage providers.
2321
- - Fixed Gemini reasoning-runaway detection to expose a dedicated thought-summary header guard to interrupt streams stuck in planning loops.
2322
-
2323
- ### Removed
2324
-
2325
- - Removed legacy GitLab Duo Workflow `chat` and `software_development` flow paths and the non-MCP action bridge in favor of the inline custom `ambient` flow.
2326
-
2327
- Older entries are archived in [packages/ai/CHANGELOG.md@a36a1f252e23](https://github.com/can1357/oh-my-pi/blob/a36a1f252e23031accb25bacf2b9894605ba524f/packages/ai/CHANGELOG.md).
2322
+ Older entries are archived in [packages/ai/CHANGELOG.md@cef4a452836c](https://github.com/can1357/oh-my-pi/blob/cef4a452836c7a8d547dd14470c34c9ed2dbb5f8/packages/ai/CHANGELOG.md).
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Race `promise` against `signal`, rejecting only this caller when the signal
3
3
  * fires. The underlying promise keeps running so other awaiters on the same
4
- * single-flight operation aren't punished by a peer's cancel.
4
+ * single-flight operation aren't punished by a peer's cancel. It stays
5
+ * observed either way: a caller that leaves must not turn the work's later
6
+ * rejection into an unhandled one.
5
7
  */
6
8
  export declare function raceSignal<T>(promise: Promise<T>, signal: AbortSignal | undefined, message: string): Promise<T>;
@@ -140,6 +140,8 @@ export declare class KeyCascade implements KeysApi {
140
140
  * {@link KeyCascade.getWithCredential}, which surfaces it.
141
141
  */
142
142
  get(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<string | undefined>;
143
+ /** Warm selection metadata during session startup without choosing or pinning an account. */
144
+ prefetchUsage(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<void>;
143
145
  /**
144
146
  * Build an {@link ApiKeyResolver} backed by this storage, implementing the
145
147
  * central a/b/c auth-retry policy:
@@ -63,7 +63,8 @@ export declare class RateLimits implements LimitsApi {
63
63
  * For usage-limit and account-policy failures with no free sibling, sleeps
64
64
  * until the earliest sibling unblocks when that is at most
65
65
  * {@link SIBLING_UNBLOCK_WAIT_MAX_MS} away, then reports `afterSiblingWait`.
66
- * Aborting `options.signal` during that wait rejects.
66
+ * Setting `options.waitForSibling` to false skips that wait without skipping
67
+ * the failed credential's block. Aborting `options.signal` during the wait rejects.
67
68
  */
68
69
  rotate(provider: string, sessionId: string | undefined, options?: RotateCredentialOptions): Promise<CredentialRotation>;
69
70
  }
@@ -68,6 +68,8 @@ export declare class CredentialSelector {
68
68
  index: number;
69
69
  } | undefined;
70
70
  selectApiKey(provider: string, sessionId: string | undefined, options: AuthApiKeyOptions | undefined, filter?: (credential: ApiKeyCredential) => boolean): Promise<ApiKeySelection | undefined>;
71
+ /** Warm exactly the usage fetches a fresh, unpinned OAuth ranking pass needs. */
72
+ prefetchUsage(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<void>;
71
73
  /**
72
74
  * Resolves an OAuth credential, trying credentials in priority order.
73
75
  *
@@ -170,7 +170,7 @@ export interface CredentialUpstream {
170
170
  *
171
171
  * `signal` propagates the agent's cancel down to the broker fetch.
172
172
  */
173
- fetchUsageReports(signal?: AbortSignal): Promise<UsageReport[] | null>;
173
+ fetchUsageReports(signal?: AbortSignal, provider?: Provider): Promise<UsageReport[] | null>;
174
174
  /**
175
175
  * Optional store-supplied per-credential usage report lookup. When present,
176
176
  * `AuthStorage` consults this before its own per-credential upstream fetch
@@ -32,7 +32,7 @@ export interface AuthAccountSelector {
32
32
  export interface AuthAccountPolicy {
33
33
  readonly provider: string;
34
34
  readonly account: AuthAccountSelector;
35
- /** Higher values win after hard, plan, reserve, hot-window, and measured-usage safety checks. */
35
+ /** Higher values win after hard, plan, reserve and hot-window safety checks, and ahead of a missing usage report. */
36
36
  readonly priority?: number;
37
37
  /** Protected remaining quota percentage for this account. */
38
38
  readonly reservePct?: number;
@@ -698,6 +698,8 @@ export type RotateCredentialOptions = {
698
698
  apiKey?: string;
699
699
  credentialId?: number;
700
700
  signal?: AbortSignal;
701
+ /** Whether to wait for a briefly blocked sibling; defaults to true. The failed credential is blocked either way. */
702
+ waitForSibling?: boolean;
701
703
  };
702
704
  /**
703
705
  * Outcome of {@link LimitsApi.rotate}.
@@ -863,6 +865,12 @@ export interface KeysApi {
863
865
  * {@link KeysApi.getWithCredential}, which surfaces that failure.
864
866
  */
865
867
  get(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<string | undefined>;
868
+ /**
869
+ * Warm usage needed by an unpinned OAuth selection without choosing a key,
870
+ * advancing round-robin state, or pinning an account. Existing overrides,
871
+ * pinned sessions, and pools that do not rank usage do no work.
872
+ */
873
+ prefetchUsage(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<void>;
866
874
  /**
867
875
  * Resolve a bearer together with its durable stored credential row id, when known.
868
876
  * Same precedence as {@link KeysApi.get}, but rejects with
@@ -1136,8 +1144,9 @@ export interface SessionsApi {
1136
1144
  }
1137
1145
  /** Usage reporting, observation, and provider configuration. */
1138
1146
  export interface UsageApi {
1139
- /** Fetch aggregate usage reports for configured providers. */
1147
+ /** Fetch usage reports for configured providers, optionally restricted to one provider. */
1140
1148
  reports(options?: {
1149
+ provider?: Provider;
1141
1150
  baseUrlResolver?: (provider: Provider) => string | undefined;
1142
1151
  signal?: AbortSignal;
1143
1152
  }): Promise<UsageReport[] | null>;
@@ -1268,7 +1277,7 @@ export interface LimitsApi {
1268
1277
  *
1269
1278
  * For usage-limit and account-policy failures with no free sibling, waits
1270
1279
  * (abortable via `options.signal`) when a sibling's block expires within a
1271
- * few seconds, then reports `afterSiblingWait`.
1280
+ * few seconds, then reports `afterSiblingWait`, unless `options.waitForSibling` is false.
1272
1281
  */
1273
1282
  rotate(provider: string, sessionId: string | undefined, options?: RotateCredentialOptions): Promise<CredentialRotation>;
1274
1283
  /** Invalidate a credential matching an API key after authentication failure. */
@@ -30,6 +30,7 @@ export interface UsageServiceDeps {
30
30
  logger: UsageLogger;
31
31
  }
32
32
  type UsageReportsOptions = {
33
+ provider?: Provider;
33
34
  baseUrlResolver?: (provider: Provider) => string | undefined;
34
35
  signal?: AbortSignal;
35
36
  };
@@ -67,12 +67,13 @@ export declare class AuthBrokerClient {
67
67
  signal?: AbortSignal;
68
68
  }): AsyncGenerator<SnapshotStreamEvent>;
69
69
  /**
70
- * Fetch aggregate broker usage with a timeout sized for serialized
71
- * same-provider account probes.
70
+ * Fetch broker usage, optionally for one provider, with a timeout sized
71
+ * for serialized same-provider account probes.
72
72
  */
73
73
  fetchUsage(options?: {
74
74
  signal?: AbortSignal;
75
75
  maxAccountsPerProvider?: number;
76
+ provider?: string;
76
77
  }): Promise<UsageResponse>;
77
78
  /** Recorded usage-limit snapshots from the broker host, oldest first. */
78
79
  fetchUsageHistory(query?: {
@@ -145,11 +145,11 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
145
145
  * to the broker's `/v1/usage` endpoint. Shared per-credential caches and
146
146
  * cooldowns keep separate clients from multiplying provider probes.
147
147
  */
148
- fetchUsageReports(signal?: AbortSignal): Promise<UsageReport[] | null>;
148
+ fetchUsageReports(signal?: AbortSignal, provider?: Provider): Promise<UsageReport[] | null>;
149
149
  /**
150
150
  * Per-credential usage hook consumed by `UsageService.report`. Pulls
151
- * the aggregate broker `/v1/usage` once and serves all callers from the
152
- * same response (coalesced + cached), then overlays any client-observed
151
+ * provider-scoped broker `/v1/usage` once and serves that provider's callers
152
+ * from the same response (coalesced + cached), then overlays any client-observed
153
153
  * header hints for the matching credential.
154
154
  *
155
155
  * The broker caches each credential independently; the short client TTL
@@ -5,7 +5,23 @@ import type { Api, FetchImpl, Model, Usage } from "../types.js";
5
5
  import type { ClientUsageIdentity } from "../usage.js";
6
6
  import type { AuthGatewayClientScope } from "./clients.js";
7
7
  import type { AuthGatewayServerOptions } from "./types.js";
8
- export type ModelResolver = (modelId: string) => Model<Api> | undefined;
8
+ /** What a route knows about a request beyond the model id it names. */
9
+ export interface ModelResolveOptions {
10
+ /**
11
+ * The api the route's wire format speaks natively (`anthropic-messages` for
12
+ * `/v1/messages`). A bare id served by several equally ranked providers
13
+ * prefers a model on this api.
14
+ */
15
+ api?: Api;
16
+ /**
17
+ * Models the caller may use. A bare id resolves to its best candidate this
18
+ * admits; a refused `provider/id` resolves to nothing, never to another
19
+ * provider's bare id spelled the same way.
20
+ */
21
+ accepts?: (model: Model<Api>) => boolean;
22
+ }
23
+ /** Resolves a client-requested model id, `provider/id` or bare `id`, to the model a route dispatches to. */
24
+ export type ModelResolver = (modelId: string, options?: ModelResolveOptions) => Model<Api> | undefined;
9
25
  /** What the gateway's routes need, whatever transport carries the requests. */
10
26
  export interface AuthGatewayRouteOptions {
11
27
  /** Source of credentials: broker-backed for `serve`, the CLI's own for `stdio`. */
@@ -12,3 +12,8 @@ export declare class AbortError extends Error {
12
12
  cause?: unknown;
13
13
  });
14
14
  }
15
+ /**
16
+ * The text a caller gave `AbortController.abort(reason)`, a string or an `Error` other than an abort, such as the coding
17
+ * agent's user-interrupt label; `undefined` for a bare `abort()`, whose reason is the default `AbortError`.
18
+ */
19
+ export declare function callerAbortReason(signal: AbortSignal | undefined): string | undefined;
@@ -26,7 +26,7 @@ export interface FinalizeResult {
26
26
  status: number | undefined;
27
27
  /** `"aborted"` when the caller cancelled, otherwise `"error"`. */
28
28
  stopReason: "aborted" | "error";
29
- /** User-facing message from {@link formatMessage}, or a local abort reason. */
29
+ /** User-facing message: a local abort reason, else the caller's abort reason, else from {@link formatMessage}. */
30
30
  message: string;
31
31
  }
32
32
  /**
@@ -34,8 +34,9 @@ export interface FinalizeResult {
34
34
  * `stopReason` / `errorStatus` / `errorId` / `errorMessage` boilerplate.
35
35
  *
36
36
  * `stopReason` comes from the abort tracker (caller intent dominates) or, when
37
- * no tracker is supplied, the raw `signal.aborted`. A local abort reason (e.g. a
38
- * first-event timeout) supersedes the formatted message. Message formatting is
39
- * wrapped so a formatter throw can never skip the caller's `stream.end()`.
37
+ * no tracker is supplied, the raw `signal.aborted`. The message is, in order, a
38
+ * local abort reason (e.g. a first-event timeout), the reason the caller passed
39
+ * to `abort()` (e.g. "Interrupted by user"), or the formatted error. Message
40
+ * formatting is wrapped so a formatter throw can never skip the caller's `stream.end()`.
40
41
  */
41
42
  export declare function finalize(error: unknown, opts?: FinalizeOptions): Promise<FinalizeResult>;
@@ -17,6 +17,8 @@ export * from "./oneshot-retry.js";
17
17
  export * from "./provider-details.js";
18
18
  export * from "./provider-session-state.js";
19
19
  export type * from "./providers/anthropic.js";
20
+ export type * from "./providers/anthropic-headers.js";
21
+ export type * from "./providers/anthropic-http-options.js";
20
22
  export * from "./providers/anthropic-identity.js";
21
23
  export * from "./providers/anthropic-state.js";
22
24
  export type * from "./providers/anthropic-client.js";
@@ -34,6 +36,7 @@ export type * from "./providers/kimi.js";
34
36
  export type * from "./providers/mock.js";
35
37
  export type * from "./providers/ollama.js";
36
38
  export type * from "./providers/openai-codex-responses.js";
39
+ export type * from "./providers/openai-codex-state.js";
37
40
  export type * from "./providers/openai-completions.js";
38
41
  export type * from "./providers/openai-responses.js";
39
42
  export type * from "./providers/synthetic.js";
@@ -0,0 +1,84 @@
1
+ export type AnthropicHeaderOptions = {
2
+ apiKey: string;
3
+ baseUrl?: string;
4
+ isOAuth?: boolean;
5
+ extraBetas?: string[];
6
+ stream?: boolean;
7
+ modelHeaders?: Record<string, string>;
8
+ isCloudflareAiGateway?: boolean;
9
+ claudeCodeSessionId?: string;
10
+ claudeCodeBetas?: readonly string[];
11
+ /** Allow explicit fingerprint headers to replace OAuth defaults on non-official endpoints. */
12
+ allowAnthropicHeaderOverrides?: boolean;
13
+ };
14
+ export declare function buildBetaHeader(baseBetas: readonly string[], extraBetas: readonly string[]): string;
15
+ /**
16
+ * Merge an extra Anthropic beta into a caller-provided `anthropic-beta` header,
17
+ * preserving the caller's key casing and deduping the tokens. Returns a
18
+ * single-entry header record for a per-request `headers` override — used to
19
+ * attach a required beta to injected SDK clients that bypass the client-level
20
+ * beta construction.
21
+ */
22
+ export declare function mergeAnthropicBetaHeader(callerHeaders: Record<string, string>, beta: string): Record<string, string>;
23
+ export declare const midConversationSystemClearAtBeta = "mid-conversation-system-clear-at-2026-08-21";
24
+ export declare const midConversationToolChangesBeta = "mid-conversation-tool-changes-2026-07-01";
25
+ export declare const midConversationOutputConfigBeta = "mid-conversation-output-config-2026-07-01";
26
+ export declare const contextManagementBeta = "context-management-2025-06-27";
27
+ export declare const structuredOutputsBeta = "structured-outputs-2025-12-15";
28
+ export declare const fallbackCreditBeta = "fallback-credit-2026-06-01";
29
+ export declare const promptCachingScopeBeta = "prompt-caching-scope-2026-01-05";
30
+ export declare const extendedCacheTtlBeta = "extended-cache-ttl-2025-04-11";
31
+ export declare const fineGrainedToolStreamingBeta = "fine-grained-tool-streaming-2025-05-14";
32
+ export declare const interleavedThinkingBeta = "interleaved-thinking-2025-05-14";
33
+ export declare const fastModeBeta = "fast-mode-2026-02-01";
34
+ export declare const taskBudgetBeta = "task-budgets-2026-03-13";
35
+ export declare const effortBeta = "effort-2025-11-24";
36
+ export declare const serverSideFallbackBeta = "server-side-fallback-2026-06-01";
37
+ export declare function buildClaudeCodeBetas({ agentRequest, thinkingRequest, disableStrictTools, supportsContextManagement, dropPromptCacheBetas, }: {
38
+ agentRequest: boolean;
39
+ thinkingRequest: boolean;
40
+ disableStrictTools?: boolean;
41
+ supportsContextManagement?: boolean;
42
+ /**
43
+ * Withhold the prompt-cache betas: this endpoint rejected `cache_control`,
44
+ * so a request that carries no breakpoint must not advertise caching it
45
+ * cannot use. This deviates from Claude Code's header fingerprint, which is
46
+ * acceptable here precisely because an endpoint that refuses the field is
47
+ * not the first-party API the fingerprint exists to match.
48
+ */
49
+ dropPromptCacheBetas?: boolean;
50
+ }): readonly string[];
51
+ export declare function buildAnthropicHeaders(options: AnthropicHeaderOptions): Record<string, string>;
52
+ /** Maps Node's platform identifier to the Stainless wire value. */
53
+ export declare function mapStainlessOs(platform: string): "MacOS" | "Windows" | "Linux" | "FreeBSD" | `Other::${string}`;
54
+ /** Maps Node's architecture identifier to the Stainless wire value. */
55
+ export declare function mapStainlessArch(arch: string): "x64" | "arm64" | "x86" | `other::${string}`;
56
+ /** Static headers emitted by Claude Code's CLI runtime. */
57
+ export declare const claudeCodeHeaders: {
58
+ "X-Stainless-Arch": "arm64" | "x64" | "x86" | `other::${string}`;
59
+ "X-Stainless-Lang": string;
60
+ "X-Stainless-OS": "FreeBSD" | "Linux" | "MacOS" | "Windows" | `Other::${string}`;
61
+ "X-Stainless-Package-Version": string;
62
+ "X-Stainless-Retry-Count": string;
63
+ "X-Stainless-Runtime": string;
64
+ "X-Stainless-Runtime-Version": string;
65
+ "X-Stainless-Timeout": string;
66
+ };
67
+ /**
68
+ * Returns env-supplied custom headers (`ANTHROPIC_CUSTOM_HEADERS`) when they
69
+ * should be forwarded to the upstream endpoint.
70
+ *
71
+ * Foundry mode forwards them unconditionally. Outside Foundry, they're applied
72
+ * only when the configured base URL is a non-Anthropic host — i.e. an
73
+ * enterprise/corporate gateway that may require its own proprietary auth
74
+ * header. Stock `api.anthropic.com` would reject unknown headers, so they're
75
+ * omitted there.
76
+ */
77
+ export declare function resolveAnthropicCustomHeadersForBaseUrl(baseUrl: string | undefined): Record<string, string> | undefined;
78
+ /**
79
+ * Merge headers case-insensitively; later sources win and preserve their key casing.
80
+ * Undefined sources are ignored. Differently cased duplicates are removed so the
81
+ * Headers constructor cannot join competing values comma-separated on the wire.
82
+ */
83
+ export declare function mergeHeaders(...headerSources: (Record<string, string> | undefined)[]): Record<string, string>;
84
+ export declare function normalizeExtraBetas(betas?: string[] | string): string[];
@@ -0,0 +1,15 @@
1
+ import type { Api, Model } from "../types.js";
2
+ import type { AnthropicFetchOptions } from "./anthropic-client.js";
3
+ export declare function resolveAnthropicBaseUrl(model: Model<"anthropic-messages">, apiKey?: string): string | undefined;
4
+ export declare function buildClaudeCodeTlsFetchOptions(model: Model<"anthropic-messages">, baseUrl: string | undefined): AnthropicFetchOptions | undefined;
5
+ /**
6
+ * Warm-up target for a first-party `anthropic` model: the effective official
7
+ * API URL plus the TLS options real requests use (sockets pool per TLS
8
+ * configuration, so a warm-up made with other options is never reused).
9
+ * `undefined` when requests resolve elsewhere — `ANTHROPIC_BASE_URL` or
10
+ * Foundry gateways never receive unsolicited probes.
11
+ */
12
+ export declare function resolveAnthropicPrewarmTarget(model: Model<Api>): {
13
+ url: string;
14
+ init: RequestInit;
15
+ } | undefined;
@@ -1,5 +1,5 @@
1
1
  import type { Effort } from "@oh-my-pi/pi-catalog/effort";
2
- import type { AnthropicCompactionRequest, AnthropicOutputEffort, Api, Context, FetchImpl, Message, Model, ServiceTier, SimpleStreamOptions, StreamFunction, StreamOptions, Usage } from "../types.js";
2
+ import type { AnthropicCompactionRequest, AnthropicOutputEffort, Context, FetchImpl, Message, Model, ServiceTier, SimpleStreamOptions, StreamFunction, StreamOptions, Usage } from "../types.js";
3
3
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
4
4
  import { type RawHttpRequestDump } from "../utils/http-inspector.js";
5
5
  import { type AnthropicFetchOptions, type AnthropicMessagesClientLike } from "./anthropic-client.js";
@@ -9,38 +9,8 @@ import { applyClaudeToolPrefix, deriveClaudeDeviceId, generateClaudeCloakingUser
9
9
  import { clearAnthropicFastModeFallback, isAnthropicFastModeFallbackDisabled, normalizeAnthropicBaseUrl } from "./anthropic-state.js";
10
10
  export { applyClaudeToolPrefix, clearAnthropicFastModeFallback, deriveClaudeDeviceId, generateClaudeCloakingUserId, isAnthropicFastModeFallbackDisabled, isClaudeCloakingUserId, normalizeAnthropicBaseUrl, resolveAnthropicMetadataUserId, stripClaudeToolPrefix, };
11
11
  export { resolvesToOfficialAnthropicEndpoint, supportsAnthropicCompaction, supportsAnthropicCompactionOnClient };
12
- export type AnthropicHeaderOptions = {
13
- apiKey: string;
14
- baseUrl?: string;
15
- isOAuth?: boolean;
16
- extraBetas?: string[];
17
- stream?: boolean;
18
- modelHeaders?: Record<string, string>;
19
- isCloudflareAiGateway?: boolean;
20
- claudeCodeSessionId?: string;
21
- claudeCodeBetas?: readonly string[];
22
- /** Allow explicit fingerprint headers to replace OAuth defaults on non-official endpoints. */
23
- allowAnthropicHeaderOverrides?: boolean;
24
- };
25
- export declare function buildBetaHeader(baseBetas: readonly string[], extraBetas: readonly string[]): string;
26
- export declare function buildAnthropicHeaders(options: AnthropicHeaderOptions): Record<string, string>;
27
12
  type AnthropicCacheControl = NonNullable<TextBlockParam["cache_control"]>;
28
13
  export * from "./claude-code-fingerprint.js";
29
- /** Maps Node's platform identifier to the Stainless wire value. */
30
- export declare function mapStainlessOs(platform: string): "MacOS" | "Windows" | "Linux" | "FreeBSD" | `Other::${string}`;
31
- /** Maps Node's architecture identifier to the Stainless wire value. */
32
- export declare function mapStainlessArch(arch: string): "x64" | "arm64" | "x86" | `other::${string}`;
33
- /** Static headers emitted by Claude Code's CLI runtime. */
34
- export declare const claudeCodeHeaders: {
35
- "X-Stainless-Arch": "arm64" | "x64" | "x86" | `other::${string}`;
36
- "X-Stainless-Lang": string;
37
- "X-Stainless-OS": "FreeBSD" | "Linux" | "MacOS" | "Windows" | `Other::${string}`;
38
- "X-Stainless-Package-Version": string;
39
- "X-Stainless-Retry-Count": string;
40
- "X-Stainless-Runtime": string;
41
- "X-Stainless-Runtime-Version": string;
42
- "X-Stainless-Timeout": string;
43
- };
44
14
  /**
45
15
  * Wraps a fetch implementation to patch the Claude Code billing-header `cch`
46
16
  * attestation into outgoing request bodies. Bodies without the placeholder
@@ -170,23 +140,6 @@ export type AnthropicClientOptionsResult = {
170
140
  fetch?: FetchImpl;
171
141
  fetchOptions?: AnthropicFetchOptions;
172
142
  };
173
- /**
174
- * Returns env-supplied custom headers (`ANTHROPIC_CUSTOM_HEADERS`) when they
175
- * should be forwarded to the upstream endpoint.
176
- *
177
- * Foundry mode forwards them unconditionally. Outside Foundry, they're applied
178
- * only when the configured base URL is a non-Anthropic host — i.e. an
179
- * enterprise/corporate gateway that may require its own proprietary auth
180
- * header. Stock `api.anthropic.com` would reject unknown headers, so they're
181
- * omitted there.
182
- */
183
- export declare function resolveAnthropicCustomHeadersForBaseUrl(baseUrl: string | undefined): Record<string, string> | undefined;
184
- /**
185
- * Merge headers case-insensitively; later sources win and preserve their key casing.
186
- * Undefined sources are ignored. Differently cased duplicates are removed so the
187
- * Headers constructor cannot join competing values comma-separated on the wire.
188
- */
189
- export declare function mergeHeaders(...headerSources: (Record<string, string> | undefined)[]): Record<string, string>;
190
143
  export type AnthropicUsageLike = {
191
144
  cache_creation?: {
192
145
  ephemeral_5m_input_tokens?: number | null;
@@ -250,19 +203,7 @@ type SystemBlockOptions = {
250
203
  dropCacheControl?: boolean;
251
204
  };
252
205
  export declare function buildAnthropicSystemBlocks(systemPrompt: readonly string[] | undefined, options?: SystemBlockOptions): AnthropicSystemBlock[] | undefined;
253
- export declare function normalizeExtraBetas(betas?: string[] | string): string[];
254
206
  export declare function buildAnthropicClientOptions(args: AnthropicClientOptionsArgs): AnthropicClientOptionsResult;
255
- /**
256
- * Warm-up target for a first-party `anthropic` model: the effective official
257
- * API URL plus the TLS options real requests use (sockets pool per TLS
258
- * configuration, so a warm-up made with other options is never reused).
259
- * `undefined` when requests resolve elsewhere — `ANTHROPIC_BASE_URL` or
260
- * Foundry gateways never receive unsolicited probes.
261
- */
262
- export declare function resolveAnthropicPrewarmTarget(model: Model<Api>): {
263
- url: string;
264
- init: RequestInit;
265
- } | undefined;
266
207
  /**
267
208
  * True when enabled thinking on `model` is budget thinking
268
209
  * (`thinking.type: "enabled"` with `budget_tokens`) rather than adaptive.
@@ -1,8 +1,8 @@
1
1
  import type { CursorRule, McpToolDefinition } from "@oh-my-pi/pi-catalog/discovery/cursor-proto";
2
2
  import { CURSOR_CLIENT_VERSION } from "@oh-my-pi/pi-catalog/wire/cursor";
3
- import { type AgentServerMessage, type ConversationStateStructure } from "@oh-my-pi/pi-catalog/discovery/cursor-proto";
3
+ import { type AgentServerMessage, type ConversationStateStructure, type ExecServerMessage } from "@oh-my-pi/pi-catalog/discovery/cursor-proto";
4
4
  import { type JsonValue } from "@oh-my-pi/pi-catalog/discovery/protobuf";
5
- import type { AssistantMessage, Context, CursorExecHandlerResult, CursorExecHandlers, CursorExecPairing, CursorTodoSyncHandler, CursorToolResultHandler, Message, Model, StreamFunction, StreamOptions, TextContent, ThinkingContent, Tool, ToolCall, ToolResultMessage } from "../types.js";
5
+ import type { AssistantMessage, Context, CursorExecHandlerResult, CursorExecHandlers, CursorExecPairing, CursorMcpCall, CursorTodoSyncHandler, CursorToolResultHandler, Message, Model, StreamFunction, StreamOptions, TextContent, ThinkingContent, Tool, ToolCall, ToolResultMessage } from "../types.js";
6
6
  import { kCursorExecResolved, kStreamingBlockIndex, kStreamingBlockKind, kStreamingEnvelopeId, kStreamingLastParseLen, kStreamingPartialJson } from "../utils/block-symbols.js";
7
7
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
8
8
  export declare const CURSOR_API_URL = "https://api2.cursor.sh";
@@ -12,7 +12,14 @@ export interface CursorOptions extends StreamOptions {
12
12
  conversationId?: string;
13
13
  execHandlers?: CursorExecHandlers;
14
14
  onToolResult?: CursorToolResultHandler;
15
- /** Treat unhandled MCP calls as accepted handoffs to an external executor. */
15
+ /**
16
+ * The caller's client runs every tool (auth-gateway). Cursor is limited to
17
+ * MCP calls, every context tool is advertised as one, and a turn that hands
18
+ * the client a call stops with `toolUse`. With a `providerSessionState` to
19
+ * park in, the run stays open for the next request to answer its calls
20
+ * in-band (`CursorParkedRun`); without one, each call is handed off and
21
+ * Cursor told its result comes with the next request.
22
+ */
16
23
  externalToolExecutor?: boolean;
17
24
  /** Wire model id selected after thinking-effort routing (`resolveWireModelId`). */
18
25
  wireModelId?: string;
@@ -76,6 +83,22 @@ export interface BlockState {
76
83
  openToolCalls: Map<string, ToolCallState>;
77
84
  /** MCP call IDs synthesized from exec frames before their redundant streamed block arrives. */
78
85
  resolvedMcpToolCallIds: Set<string>;
86
+ /**
87
+ * Calls a relayed run handed its client in an earlier response
88
+ * (`CursorParkedRun`). Each response fills its own message, so a streamed
89
+ * announcement that trails the handoff must not open the call again there.
90
+ * Optional so existing test harnesses stay valid.
91
+ */
92
+ handedOffToolCallIds?: Set<string>;
93
+ /**
94
+ * MCP calls a relayed run (`CursorParkedRun`) has seen announced but holds
95
+ * no exec frame for, by call id. A streaming client runs every call it is
96
+ * sent and a streamed event cannot be taken back, while a response may hand
97
+ * the client only held calls; so a call stays off the stream and out of the
98
+ * message until its exec frame is held (`streamUnheldExternalCall`).
99
+ * Set only for a relayed run.
100
+ */
101
+ unheldExternalCalls?: Map<string, ToolCallState>;
79
102
  /**
80
103
  * Native `editToolCall` (StrReplace) ids whose materialization `readArgs` /
81
104
  * `writeArgs` must stay raw and must not synthesize extra transcript blocks.
@@ -104,6 +127,12 @@ export interface BlockState {
104
127
  * matching result and every transcript rebuild strips it as dangling.
105
128
  */
106
129
  onToolResult?: CursorToolResultHandler;
130
+ /**
131
+ * Keep an external executor's MCP exec frame open instead of handing the
132
+ * call off: a relayed run answers it in-band once the client's next request
133
+ * brings the result (`CursorParkedRun`).
134
+ */
135
+ holdExternalCall?: (execMsg: ExecServerMessage, call: CursorMcpCall) => void;
107
136
  }
108
137
  export interface UsageState {
109
138
  sawTokenDelta: boolean;
@@ -236,10 +265,11 @@ export declare function processInteractionUpdate(update: any, output: AssistantM
236
265
  * Cursor AgentService reconstructs the model prompt from `requestContext.rules`,
237
266
  * not from the client-supplied `rootPromptMessagesJson` system blobs. Map each
238
267
  * OMP system-prompt entry to a global CursorRule so always-apply rules survive
239
- * that reconstruction.
268
+ * that reconstruction. The server only does this while the root carries no
269
+ * history; later turns rely on {@link buildCursorRulesEntryJson}.
240
270
  */
241
271
  export declare function buildCursorRequestContextRules(systemPrompt: readonly string[] | undefined): CursorRule[];
242
- export declare function buildMcpToolDefinitions(tools: Tool[] | undefined, requiresCursorToolSchemaProjection?: boolean): McpToolDefinition[];
272
+ export declare function buildMcpToolDefinitions(tools: Tool[] | undefined, requiresCursorToolSchemaProjection?: boolean, externalToolExecutor?: boolean): McpToolDefinition[];
243
273
  /**
244
274
  * Build `ConversationStateStructure.rootPromptMessagesJson` blob IDs for the
245
275
  * system prompt plus prior conversation history, as JSON blobs matching