@oh-my-pi/pi-ai 15.11.3 → 15.11.6

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 (46) hide show
  1. package/CHANGELOG.md +49 -1
  2. package/dist/types/auth-broker/wire-schemas.d.ts +3 -0
  3. package/dist/types/auth-retry.d.ts +47 -0
  4. package/dist/types/auth-storage.d.ts +104 -1
  5. package/dist/types/errors.d.ts +24 -0
  6. package/dist/types/index.d.ts +2 -0
  7. package/dist/types/providers/amazon-bedrock.d.ts +5 -0
  8. package/dist/types/providers/anthropic-client.d.ts +2 -2
  9. package/dist/types/providers/google-gemini-cli.d.ts +5 -0
  10. package/dist/types/providers/google-shared.d.ts +5 -0
  11. package/dist/types/providers/ollama.d.ts +5 -0
  12. package/dist/types/providers/openai-codex/request-transformer.d.ts +8 -0
  13. package/dist/types/providers/openai-codex/response-handler.d.ts +9 -0
  14. package/dist/types/providers/openai-codex-responses.d.ts +36 -1
  15. package/dist/types/providers/openai-responses-shared.d.ts +21 -3
  16. package/dist/types/providers/openai-responses.d.ts +10 -0
  17. package/dist/types/providers/pi-native-client.d.ts +9 -0
  18. package/dist/types/usage/openai-codex-reset.d.ts +79 -0
  19. package/dist/types/usage/openai-codex.d.ts +1 -0
  20. package/dist/types/usage.d.ts +56 -0
  21. package/dist/types/utils/http-inspector.d.ts +0 -1
  22. package/dist/types/utils/request-debug.d.ts +3 -0
  23. package/package.json +3 -3
  24. package/src/auth-retry.ts +97 -0
  25. package/src/auth-storage.ts +447 -14
  26. package/src/errors.ts +32 -0
  27. package/src/index.ts +2 -0
  28. package/src/providers/amazon-bedrock.ts +14 -8
  29. package/src/providers/anthropic-client.ts +4 -6
  30. package/src/providers/aws-credentials.ts +0 -1
  31. package/src/providers/google-gemini-cli.ts +16 -8
  32. package/src/providers/google-shared.ts +13 -6
  33. package/src/providers/ollama.ts +10 -7
  34. package/src/providers/openai-codex/request-transformer.ts +45 -0
  35. package/src/providers/openai-codex/response-handler.ts +21 -0
  36. package/src/providers/openai-codex-responses.ts +267 -83
  37. package/src/providers/openai-completions.ts +10 -10
  38. package/src/providers/openai-responses-shared.ts +52 -6
  39. package/src/providers/openai-responses.ts +266 -49
  40. package/src/providers/pi-native-client.ts +21 -8
  41. package/src/stream.ts +4 -4
  42. package/src/usage/openai-codex-reset.ts +174 -0
  43. package/src/usage/openai-codex.ts +18 -1
  44. package/src/usage.ts +68 -0
  45. package/src/utils/http-inspector.ts +0 -10
  46. package/src/utils/request-debug.ts +55 -12
package/CHANGELOG.md CHANGED
@@ -2,7 +2,55 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [15.11.5] - 2026-06-12
6
+
7
+ ### Added
8
+
9
+ - Added `AuthStorage.listUsageHistory` to retrieve historical usage snapshots with optional `provider` and `sinceMs` filtering
10
+ - Added durable usage-history persistence in the sqlite auth store so successful usage reports are recorded as time-series snapshots of limit utilization for later trend inspection
11
+ - Added `AuthStorage.redeemResetCredit` to redeem stored OpenAI Codex saved rate-limit reset credits for a target account by `credentialId`, `accountId`, or `email`
12
+ - Added `listCodexResetCredits` and `consumeCodexResetCredit` exports for OpenAI Codex saved reset-credit listing and redemption
13
+ - Added `resetCredits` with `availableCount` to `UsageReport` so OpenAI Codex usage data now exposes redeemable rate-limit resets
14
+ - Added `openai-codex-reset` exports via package barrel for out-of-band tooling usage
15
+ - Added a one-shot request-debug target that writes the next provider HTTP request JSON to an explicit path.
16
+
17
+ ### Changed
18
+
19
+ - Changed `AuthStorage.redeemResetCredit` to invalidate cached usage data after a successful redemption so the next usage report reflects the reset immediately
20
+
21
+ ### Fixed
22
+
23
+ - Fixed temporary credential block state so redeemed reset credits immediately make the affected account selectable again after `redeemResetCredit` succeeds
24
+ - Fixed one-shot request-debug path handling so an explicit request log target is consumed after the next request and no longer affects subsequent calls
25
+ - Fixed explicit request-debug path mode to create missing parent directories before writing request logs
26
+ - Fixed explicit request-debug mode to overwrite existing `.res.log` files for the requested path instead of failing when they already exist
27
+ - Fixed OpenAI Responses `previous_response_id` chaining on Zero Data Retention orgs: the in-provider retry classifier missed the ZDR-specific 400 ("Previous response cannot be used for this organization due to Zero Data Retention"), so chained turns kept failing every other request after a brief recovery — the chain was reset but not disabled, so the next successful full-replay turn re-armed it. The ZDR phrasing is now classified categorically: one strike disables chaining for the session (skipping the three-strike circuit breaker) and the in-call retry drops `store: true`/`previous_response_id` and replays the full transcript instead ([#2341](https://github.com/can1357/oh-my-pi/issues/2341)).
28
+
29
+ ## [15.11.4] - 2026-06-12
30
+
31
+ ### Added
32
+
33
+ - Codex/Responses providers now map `end_turn: false` on the terminal stream event (Codex backend signal for "response ended, turn didn't" — commentary-only progress updates) to `stopDetails: { type: "pause_turn" }` with stopReason `"stop"`, so the agent loop can re-sample instead of ending the turn. Wired in `openai-codex-responses` and `processResponsesStream` (`openai-responses`/`azure-openai-responses`); inert for backends that never send the field.
34
+ - Added Codex upstream protocol features to `openai-codex-responses` (tracking codex-rs as of June 2026): `onModerationMetadata` callback surfacing `response.metadata` → `openai_chatgpt_moderation_metadata` on both transports; `reasoningContext` option emitting `reasoning.context` (`auto`/`current_turn`/`all_turns`); `clientMetadata` option emitting `client_metadata` in the request body (canonical `x-codex-turn-metadata` envelope) without breaking the websocket append fast-path; and an opt-in `responsesLite` mode mirroring codex-rs — lite header on HTTP requests and the websocket upgrade, `ws_request_header_*` marker in `response.create` client metadata, lite-keyed socket pooling, image-detail stripping, forced serial tool calls, and `reasoning.context: all_turns` default. Dormant until OpenAI flips `use_responses_lite` in the model catalog.
35
+ - Added `withOAuthAccess` — the `withAuth` counterpart for OAuth-access consumers: runs an operation through the central a/b/c auth-retry policy (resolve → force-refresh same account → rotate to a sibling) while handing the attempt the full `OAuthAccess` (bearer plus `accountId`/`projectId`/`enterpriseUrl` identity metadata). Use it instead of hand-rolled `getOAuthAccess` + fetch flows so 401s and usage-limits rotate credentials instead of failing the call.
36
+ - Added `ProviderHttpError` — a typed HTTP error carrying `status`, `headers`, and `code` — replacing the ad-hoc `as Error & { status?... }` / `Object.assign` hacks at provider throw sites, with per-provider subclasses `CodexApiError`, `AuthGatewayError`, `GoogleApiError`, `GeminiCliApiError`, `OllamaApiError`, and `BedrockApiError`; `AnthropicApiError` now extends it. Google, Gemini CLI, Ollama, and Bedrock HTTP errors now also carry response headers, so server-suggested `retry-after` delays are visible to retry classification on those paths. The internal `withHttpStatus` helper was removed.
37
+ - Added stateful SSE turn chaining for OpenAI Codex (on by default; disable with `PI_CODEX_STATEFUL=0` or `statefulResponses: false`): SSE requests now reuse `previous_response_id` with delta-only input instead of replaying the full transcript, mirroring the websocket fast-path via a shared transport-aware builder. Any history mutation or option change falls back to a full replay; a server-side `previous_response_not_found` (HTTP or in-stream) resets the chain and retries the turn with full context, and three consecutive stale failures disable chaining for the session.
38
+ - Added stateful `previous_response_id` chaining to the platform OpenAI Responses provider (`openai-responses`): on by default against the official api.openai.com endpoint (forces `store: true`, which chaining requires), off for other Responses endpoints; override with `statefulResponses` or `PI_OPENAI_STATEFUL`. Chain detection compares the wire form of the conversation arguments alone — per-turn trailing scaffolding such as the GPT-5 "Juice: 0" developer item is excluded from the append-baseline prefix check and re-appended to the delta — and a rejected/stale previous response falls back to a one-shot full replay with the same circuit breaker.
39
+ - Added `AuthStorage.getOAuthAccountIdentity()` and the `OAuthAccountIdentity` type — a read-only lookup returning the `accountId`/`email`/`projectId` of the OAuth credential a session is currently routed to, for display and metadata paths.
40
+
41
+ ### Changed
42
+
43
+ - The GPT-5 "Juice: 0" no-reasoning developer item in `applyResponsesReasoningParams` is now gated on the resolved `compat.requiresJuiceZeroHack` flag (auto-detected from GPT-5-family model names by `@oh-my-pi/pi-catalog`, overridable per model) instead of an inline model-name check.
44
+
45
+ ### Fixed
46
+
47
+ - Fixed websocket append fast-path to remain usable when only `client_metadata` changes between turns
48
+ - Fixed `onModerationMetadata` handling so exceptions thrown by callback observers no longer terminate the response stream
49
+ - Fixed local SQLite OAuth credential caches returning a stale Anthropic access token after another `omp` process refreshed and persisted the same row. `AuthStorage` now syncs the selected row from storage before returning or force-refreshing OAuth credentials, so concurrent sessions pick up peer-rotated tokens instead of surfacing a one-turn `401 Invalid authentication credentials`.
50
+ - Fixed forced OAuth preflight refresh failures being swallowed silently in credential selection; they now emit a debug log (`OAuth preflight refresh failed`) so stale-refresh-token replays from concurrent sessions are diagnosable.
51
+
5
52
  ## [15.11.3] - 2026-06-11
53
+
6
54
  ### Fixed
7
55
 
8
56
  - Fixed GitHub Copilot long-context model requests to use the upstream `requestModelId` when calling Anthropic, OpenAI Responses, and OpenAI Completions APIs
@@ -3274,4 +3322,4 @@ _Dedicated to Peter's shoulder ([@steipete](https://twitter.com/steipete))_
3274
3322
 
3275
3323
  ## [0.9.4] - 2025-11-26
3276
3324
 
3277
- Initial release with multi-provider LLM support.
3325
+ Initial release with multi-provider LLM support.
@@ -344,6 +344,9 @@ export declare const usageResponseSchema: z.ZodObject<{
344
344
  }>>;
345
345
  notes: z.ZodOptional<z.ZodArray<z.ZodString>>;
346
346
  }, z.core.$strip>>;
347
+ resetCredits: z.ZodOptional<z.ZodObject<{
348
+ availableCount: z.ZodNumber;
349
+ }, z.core.$strip>>;
347
350
  metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
348
351
  raw: z.ZodOptional<z.ZodUnknown>;
349
352
  }, z.core.$strip>>;
@@ -1,3 +1,4 @@
1
+ import type { OAuthAccess } from "./auth-storage";
1
2
  /**
2
3
  * Context passed to an {@link ApiKeyResolver} on each resolution attempt.
3
4
  *
@@ -70,3 +71,49 @@ export declare function withAuth<T>(key: ApiKey | undefined, attempt: (key: stri
70
71
  signal?: AbortSignal;
71
72
  missingKeyMessage?: string;
72
73
  }): Promise<T>;
74
+ /**
75
+ * Minimal structural slice of `AuthStorage` consumed by {@link withOAuthAccess}.
76
+ * Typed structurally (and importing only the `OAuthAccess` type) so this module
77
+ * never takes a runtime dependency on `./auth-storage`.
78
+ */
79
+ export interface OAuthAccessSource {
80
+ getOAuthAccess(provider: string, sessionId?: string, options?: {
81
+ forceRefresh?: boolean;
82
+ signal?: AbortSignal;
83
+ }): Promise<OAuthAccess | undefined>;
84
+ rotateSessionCredential(provider: string, sessionId: string | undefined, options?: {
85
+ error?: unknown;
86
+ signal?: AbortSignal;
87
+ }): Promise<boolean>;
88
+ }
89
+ export interface WithOAuthAccessOptions {
90
+ /** Session id for credential stickiness, threaded into every resolve. */
91
+ sessionId?: string;
92
+ signal?: AbortSignal;
93
+ /** Override the retryable-error classifier (default {@link isAuthRetryableError}). */
94
+ isAuthError?: (error: unknown) => boolean;
95
+ /**
96
+ * Pre-resolved access used for the initial attempt. Callers that already
97
+ * resolved access for an availability gate pass it here so the helper
98
+ * doesn't double-resolve (mirrors the gateway resolver's `initialKey`).
99
+ */
100
+ seed?: OAuthAccess;
101
+ missingAccessMessage?: string;
102
+ }
103
+ /**
104
+ * {@link withAuth} for OAuth-access consumers: runs an auth-protected
105
+ * operation through the central a/b/c retry policy, handing the attempt the
106
+ * full {@link OAuthAccess} (bearer + identity metadata: `accountId`,
107
+ * `projectId`, `enterpriseUrl`) instead of bare API-key bytes.
108
+ *
109
+ * - initial → `getOAuthAccess` (or `opts.seed`).
110
+ * - step (b) → `getOAuthAccess` with `forceRefresh: true` (re-mint the SAME
111
+ * account; picks up peer/broker rotations).
112
+ * - step (c) → `rotateSessionCredential` then re-resolve (switch to a sibling).
113
+ *
114
+ * A step is skipped when it yields no access or the same `accessToken` that
115
+ * just failed; non-auth errors propagate immediately. Use this instead of
116
+ * hand-rolled `getOAuthAccess` + fetch flows so 401s and usage-limits rotate
117
+ * credentials instead of failing the call.
118
+ */
119
+ export declare function withOAuthAccess<T>(storage: OAuthAccessSource, provider: string, attempt: (access: OAuthAccess) => Promise<T>, opts?: WithOAuthAccessOptions): Promise<T>;
@@ -11,7 +11,8 @@ import { Database } from "bun:sqlite";
11
11
  import type { ApiKeyResolver } from "./auth-retry";
12
12
  import type { OAuthController, OAuthCredentials, OAuthProviderId } from "./registry/oauth/types";
13
13
  import type { Provider } from "./types";
14
- import type { CredentialRankingStrategy, UsageLogger, UsageProvider, UsageReport } from "./usage";
14
+ import type { CredentialRankingStrategy, UsageHistoryEntry, UsageHistoryQuery, UsageLogger, UsageProvider, UsageReport } from "./usage";
15
+ import { type CodexResetConsumeCode, type CodexResetCredit } from "./usage/openai-codex-reset";
15
16
  export type ApiKeyCredential = {
16
17
  type: "api_key";
17
18
  key: string;
@@ -225,6 +226,14 @@ export interface AuthCredentialStore {
225
226
  }): string | null;
226
227
  setCache(key: string, value: string, expiresAtSec: number): void;
227
228
  cleanExpiredCache(): void;
229
+ /**
230
+ * Append usage-limit snapshots for trend history. Optional: stores without
231
+ * durable storage (e.g. the broker remote store) omit it and recording is
232
+ * skipped — the broker host records into its own database instead.
233
+ */
234
+ recordUsageSnapshots?(entries: UsageHistoryEntry[]): void;
235
+ /** Read recorded usage-limit snapshots, oldest first. */
236
+ listUsageHistory?(query?: UsageHistoryQuery): UsageHistoryEntry[];
228
237
  /**
229
238
  * Optional store-supplied OAuth refresh. When present, `AuthStorage` uses
230
239
  * it before the per-provider local refresh path. `RemoteAuthCredentialStore`
@@ -430,6 +439,17 @@ export interface OAuthAccessFailure {
430
439
  enterpriseUrl?: string;
431
440
  error: string;
432
441
  }
442
+ /**
443
+ * Identity of the OAuth credential a session is currently routed to. Read-only
444
+ * display/metadata shape: `accountId` is the provider's account UUID, `email`
445
+ * the user-facing login, `projectId` the GCP-style project for providers that
446
+ * key usage on it (Gemini CLI / Antigravity).
447
+ */
448
+ export interface OAuthAccountIdentity {
449
+ accountId?: string;
450
+ email?: string;
451
+ projectId?: string;
452
+ }
433
453
  export type OAuthAccessResolution = ({
434
454
  ok: true;
435
455
  } & OAuthAccess) | ({
@@ -439,6 +459,44 @@ export interface InvalidateCredentialMatchingOptions {
439
459
  signal?: AbortSignal;
440
460
  sessionId?: string;
441
461
  }
462
+ /**
463
+ * Identifies which stored account to redeem a saved rate-limit reset for.
464
+ * Any one field is enough; `credentialId` is the most precise.
465
+ */
466
+ export interface ResetCreditTarget {
467
+ credentialId?: number;
468
+ accountId?: string;
469
+ email?: string;
470
+ }
471
+ /** Outcome of {@link AuthStorage.redeemResetCredit}. */
472
+ export interface ResetCreditRedeemOutcome {
473
+ /** `true` only when a reset was actually applied (`code === "reset"`). */
474
+ ok: boolean;
475
+ /**
476
+ * Result code. Backend codes: `reset` (success), `already_redeemed`,
477
+ * `no_credit`, `nothing_to_reset`. Locally-synthesized: `no_account`
478
+ * (target not found), `account_unavailable` (token refresh failed),
479
+ * `http_<status>` (unexpected HTTP).
480
+ */
481
+ code: CodexResetConsumeCode;
482
+ accountId?: string;
483
+ email?: string;
484
+ /** The credit that was spent (when one was). */
485
+ creditId?: string;
486
+ }
487
+ /** One stored account's live saved-reset status, from {@link AuthStorage.listResetCredits}. */
488
+ export interface ResetCreditAccountStatus {
489
+ credentialId?: number;
490
+ accountId?: string;
491
+ email?: string;
492
+ /** Resets redeemable for this account right now (live, not cached). */
493
+ availableCount: number;
494
+ credits: CodexResetCredit[];
495
+ /** Whether this is the given session's active account. */
496
+ active: boolean;
497
+ /** Set when the account's token refresh or list call failed. */
498
+ error?: string;
499
+ }
442
500
  /**
443
501
  * Credential storage backed by an AuthCredentialStore.
444
502
  * Reads from storage on reload(), manages round-robin credential selection,
@@ -581,6 +639,12 @@ export declare class AuthStorage {
581
639
  * Returns `undefined` when no OAuth credential carries an `accountId`.
582
640
  */
583
641
  getOAuthAccountId(provider: string, sessionId?: string): string | undefined;
642
+ /**
643
+ * Get the OAuth account identity for a provider, preferring the credential that
644
+ * is session-sticky for `sessionId`. This is a read-only lookup for display and
645
+ * metadata paths; it does not refresh tokens, rank usage, or advance selection.
646
+ */
647
+ getOAuthAccountIdentity(provider: string, sessionId?: string): OAuthAccountIdentity | undefined;
584
648
  /**
585
649
  * Get all credentials.
586
650
  */
@@ -604,6 +668,11 @@ export declare class AuthStorage {
604
668
  * Logout from a provider.
605
669
  */
606
670
  logout(provider: string): Promise<void>;
671
+ /**
672
+ * Recorded usage-limit snapshots, oldest first. Empty when the underlying
673
+ * store has no durable history (e.g. a broker-backed remote store).
674
+ */
675
+ listUsageHistory(query?: UsageHistoryQuery): UsageHistoryEntry[];
607
676
  ingestUsageHeaders(provider: Provider, headers: Record<string, string>, options?: {
608
677
  sessionId?: string;
609
678
  baseUrl?: string;
@@ -696,6 +765,38 @@ export declare class AuthStorage {
696
765
  * exercise each stored account exactly once.
697
766
  */
698
767
  getOAuthAccesses(provider: string, options?: AuthApiKeyOptions): Promise<OAuthAccessResolution[]>;
768
+ /**
769
+ * List saved rate-limit resets for every stored OAuth account of `provider`
770
+ * (Codex), fetched LIVE from the dedicated `rate-limit-reset-credits` route.
771
+ *
772
+ * This deliberately bypasses the usage-report cache: `/wham/usage` is
773
+ * IP-rate-limited and may serve stale (or pre-feature) snapshots when many
774
+ * accounts are polled, which would hide redeemable credits. One entry per
775
+ * account, with the session's active account flagged and unreachable
776
+ * accounts carrying an `error`.
777
+ */
778
+ listResetCredits(options?: {
779
+ provider?: string;
780
+ sessionId?: string;
781
+ baseUrlResolver?: (provider: string) => string | undefined;
782
+ signal?: AbortSignal;
783
+ }): Promise<ResetCreditAccountStatus[]>;
784
+ /**
785
+ * Redeem one saved rate-limit reset (OpenAI Codex "saved resets") for a
786
+ * specific stored account.
787
+ *
788
+ * Resolves a fresh access token for the target account, picks an available
789
+ * credit (the given `creditId`, else the first redeemable one), spends it,
790
+ * and invalidates the cached usage report so the next `/usage` reflects the
791
+ * reset. Never throws for business outcomes — inspect the returned `code`.
792
+ */
793
+ redeemResetCredit(options: {
794
+ target: ResetCreditTarget;
795
+ provider?: string;
796
+ creditId?: string;
797
+ baseUrlResolver?: (provider: string) => string | undefined;
798
+ signal?: AbortSignal;
799
+ }): Promise<ResetCreditRedeemOutcome>;
699
800
  invalidateCredentialMatching(provider: string, apiKey: string, options?: InvalidateCredentialMatchingOptions): Promise<boolean>;
700
801
  invalidateCredentialMatching(provider: string, apiKey: string, signal?: AbortSignal): Promise<boolean>;
701
802
  /**
@@ -819,6 +920,8 @@ export declare class SqliteAuthCredentialStore implements AuthCredentialStore {
819
920
  }): string | null;
820
921
  setCache(key: string, value: string, expiresAtSec: number): void;
821
922
  cleanExpiredCache(): void;
923
+ recordUsageSnapshots(entries: UsageHistoryEntry[]): void;
924
+ listUsageHistory(query?: UsageHistoryQuery): UsageHistoryEntry[];
822
925
  /**
823
926
  * Save OAuth credentials for a provider.
824
927
  * Preserves unrelated identities and replaces only the matching credential.
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Structured HTTP errors thrown by provider clients.
3
+ *
4
+ * Downstream classification reads these fields structurally rather than via
5
+ * `instanceof`: `extractHttpStatusFromError` (pi-utils) reads `status`,
6
+ * `getHeadersFromError` (retry-after extraction) reads `headers`, and retry
7
+ * policies such as `isCopilotTransientModelError` read `code`. Per-provider
8
+ * subclasses exist so call sites can narrow with `instanceof` and logs carry
9
+ * a meaningful `error.name`.
10
+ */
11
+ export interface ProviderHttpErrorOptions {
12
+ /** Response headers; enables `retry-after`/rate-limit extraction downstream. */
13
+ headers?: Headers;
14
+ /** Machine-readable error code from the response body (`error.code` / `error.type`). */
15
+ code?: string;
16
+ cause?: unknown;
17
+ }
18
+ /** Non-2xx HTTP response from a provider endpoint. */
19
+ export declare class ProviderHttpError extends Error {
20
+ readonly status: number;
21
+ readonly headers: Headers | undefined;
22
+ readonly code: string | undefined;
23
+ constructor(message: string, status: number, options?: ProviderHttpErrorOptions);
24
+ }
@@ -5,6 +5,7 @@ export { type AuthGatewayBootOptions, type ModelResolver, startAuthGateway } fro
5
5
  export * from "./auth-gateway/types";
6
6
  export * from "./auth-retry";
7
7
  export * from "./auth-storage";
8
+ export * from "./errors";
8
9
  export * from "./provider-details";
9
10
  export * from "./providers/anthropic";
10
11
  export * from "./providers/anthropic-client";
@@ -33,6 +34,7 @@ export * from "./usage/google-antigravity";
33
34
  export * from "./usage/kimi";
34
35
  export * from "./usage/minimax-code";
35
36
  export * from "./usage/openai-codex";
37
+ export * from "./usage/openai-codex-reset";
36
38
  export * from "./usage/zai";
37
39
  export * from "./utils/anthropic-auth";
38
40
  export * from "./utils/event-stream";
@@ -7,7 +7,12 @@
7
7
  * Bun's native `HTTPS_PROXY` support.
8
8
  */
9
9
  import type { Effort } from "@oh-my-pi/pi-catalog/effort";
10
+ import { ProviderHttpError } from "../errors";
10
11
  import type { StreamFunction, StreamOptions, ThinkingBudgets } from "../types";
12
+ /** Non-2xx response (or in-stream exception event) from the Bedrock runtime API. */
13
+ export declare class BedrockApiError extends ProviderHttpError {
14
+ readonly name = "BedrockApiError";
15
+ }
11
16
  export type BedrockThinkingDisplay = "summarized" | "omitted";
12
17
  export interface BedrockOptions extends StreamOptions {
13
18
  region?: string;
@@ -1,3 +1,4 @@
1
+ import { ProviderHttpError } from "../errors";
1
2
  import type { FetchImpl } from "../types";
2
3
  import type { MessageCreateParamsStreaming } from "./anthropic-wire";
3
4
  /** Per-request options accepted by {@link AnthropicMessages.create}. */
@@ -39,8 +40,7 @@ export interface AnthropicClientOptions {
39
40
  fetchOptions?: AnthropicFetchOptions;
40
41
  }
41
42
  /** Non-2xx response from the Anthropic API. */
42
- export declare class AnthropicApiError extends Error {
43
- readonly status: number;
43
+ export declare class AnthropicApiError extends ProviderHttpError {
44
44
  readonly headers: Headers;
45
45
  readonly requestId: string | null;
46
46
  constructor(status: number, message: string, headers: Headers);
@@ -1,3 +1,4 @@
1
+ import { ProviderHttpError } from "../errors";
1
2
  import type { Context, Model, StreamFunction, StreamOptions } from "../types";
2
3
  import type { Content, FunctionCallingConfigMode, ThinkingConfig } from "./google-shared";
3
4
  import { type GoogleThinkingLevel } from "./google-shared";
@@ -6,6 +7,10 @@ import { type GoogleThinkingLevel } from "./google-shared";
6
7
  * `import { GoogleThinkingLevel } from "./google-gemini-cli"` callers keep working.
7
8
  */
8
9
  export type { GoogleThinkingLevel };
10
+ /** Non-2xx response (or in-stream error chunk) from the Cloud Code Assist API. */
11
+ export declare class GeminiCliApiError extends ProviderHttpError {
12
+ readonly name = "GeminiCliApiError";
13
+ }
9
14
  export interface GoogleGeminiCliOptions extends StreamOptions {
10
15
  /**
11
16
  * Tool selection mode. String forms map directly to Gemini
@@ -1,12 +1,17 @@
1
1
  /**
2
2
  * Shared utilities for Google Generative AI and Google Cloud Code Assist providers.
3
3
  */
4
+ import { ProviderHttpError } from "../errors";
4
5
  import type { AssistantMessage, Context, FetchImpl, Model, StopReason, StreamOptions, TextContent, ThinkingContent, Tool, ToolCall } from "../types";
5
6
  import { AssistantMessageEventStream } from "../utils/event-stream";
6
7
  import { normalizeSchemaForGoogle } from "../utils/schema";
7
8
  import type { Content, FinishReason, FunctionCallingConfigMode, GenerateContentParameters, GenerateContentResponse, Part } from "./google-types";
8
9
  export type { Content, FunctionCallingConfigMode, GenerateContentParameters, GenerateContentResponse, ThinkingConfig, } from "./google-types";
9
10
  export { normalizeSchemaForGoogle };
11
+ /** Non-2xx response (or in-stream error chunk) from the Google Generative Language / Vertex API. */
12
+ export declare class GoogleApiError extends ProviderHttpError {
13
+ readonly name = "GoogleApiError";
14
+ }
10
15
  type GoogleApiType = "google-generative-ai" | "google-gemini-cli" | "google-vertex";
11
16
  /**
12
17
  * Thinking level for Gemini 3 models. Mirrors Google's `ThinkingLevel` enum values.
@@ -1,4 +1,9 @@
1
+ import { ProviderHttpError } from "../errors";
1
2
  import type { StreamFunction, StreamOptions, ToolChoice } from "../types";
3
+ /** Non-2xx response from the Ollama `/api/chat` endpoint. */
4
+ export declare class OllamaApiError extends ProviderHttpError {
5
+ readonly name = "OllamaApiError";
6
+ }
2
7
  export interface OllamaChatOptions extends StreamOptions {
3
8
  reasoning?: "minimal" | "low" | "medium" | "high" | "xhigh";
4
9
  disableReasoning?: boolean;
@@ -1,13 +1,20 @@
1
1
  import type { Api, Model } from "../../types";
2
+ /** Reasoning replay scope for the Codex Responses API (`reasoning.context`). */
3
+ export type CodexReasoningContext = "auto" | "current_turn" | "all_turns";
2
4
  export interface ReasoningConfig {
3
5
  effort: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
4
6
  summary?: "auto" | "concise" | "detailed";
7
+ context?: CodexReasoningContext;
5
8
  }
6
9
  export interface CodexRequestOptions {
7
10
  reasoningEffort?: ReasoningConfig["effort"];
8
11
  reasoningSummary?: ReasoningConfig["summary"] | null;
12
+ /** Explicit `reasoning.context` override. Defaults to `all_turns` under {@link CodexRequestOptions.responsesLite}, otherwise omitted (server default is `current_turn`). */
13
+ reasoningContext?: CodexReasoningContext;
9
14
  textVerbosity?: "low" | "medium" | "high";
10
15
  include?: string[];
16
+ /** Responses Lite transport contract: strips image detail and defaults `reasoning.context` to `all_turns`, mirroring codex-rs. */
17
+ responsesLite?: boolean;
11
18
  }
12
19
  export interface InputItem {
13
20
  id?: string | null;
@@ -40,6 +47,7 @@ export interface RequestBody {
40
47
  include?: string[];
41
48
  prompt_cache_key?: string;
42
49
  prompt_cache_retention?: "in_memory" | "24h";
50
+ client_metadata?: Record<string, string>;
43
51
  max_output_tokens?: number;
44
52
  max_completion_tokens?: number;
45
53
  [key: string]: unknown;
@@ -1,3 +1,4 @@
1
+ import { ProviderHttpError } from "../../errors";
1
2
  export type CodexRateLimit = {
2
3
  used_percent?: number;
3
4
  window_minutes?: number;
@@ -10,8 +11,16 @@ export type CodexRateLimits = {
10
11
  export type CodexErrorInfo = {
11
12
  message: string;
12
13
  status: number;
14
+ /** Machine-readable error code (`error.code` or `error.type` from the response body), when present. */
15
+ code?: string;
13
16
  friendlyMessage?: string;
14
17
  rateLimits?: CodexRateLimits;
15
18
  raw?: string;
16
19
  };
20
+ /** Non-2xx response from the Codex backend, with the parsed body retained. */
21
+ export declare class CodexApiError extends ProviderHttpError {
22
+ readonly info: CodexErrorInfo;
23
+ constructor(info: CodexErrorInfo, headers?: Headers);
24
+ static fromResponse(response: Response): Promise<CodexApiError>;
25
+ }
17
26
  export declare function parseCodexError(response: Response): Promise<CodexErrorInfo>;
@@ -1,16 +1,51 @@
1
1
  import type { ResponseInput } from "openai/resources/responses/responses";
2
2
  import { type Context, type Model, type ProviderSessionState, type ServiceTier, type StreamFunction, type StreamOptions, type Tool, type ToolChoice } from "../types";
3
+ import { type CodexReasoningContext } from "./openai-codex/request-transformer";
3
4
  export interface OpenAICodexResponsesOptions extends StreamOptions {
4
5
  reasoning?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
5
6
  reasoningSummary?: "auto" | "concise" | "detailed" | null;
7
+ /** `reasoning.context` replay scope. Defaults to `all_turns` under {@link OpenAICodexResponsesOptions.responsesLite}, otherwise omitted (server default is `current_turn`). */
8
+ reasoningContext?: CodexReasoningContext;
6
9
  textVerbosity?: "low" | "medium" | "high";
7
10
  include?: string[];
8
11
  codexMode?: boolean;
9
12
  toolChoice?: ToolChoice;
10
13
  preferWebsockets?: boolean;
14
+ /**
15
+ * Enable stateful SSE turns: chain via `previous_response_id` + delta input
16
+ * instead of replaying the full transcript. Requires `sessionId` +
17
+ * `providerSessionState`. `false` vetoes the `PI_CODEX_STATEFUL` env flag.
18
+ */
19
+ statefulResponses?: boolean;
11
20
  serviceTier?: ServiceTier;
21
+ /**
22
+ * Opt into the Responses Lite transport contract. Sends
23
+ * `x-openai-internal-codex-responses-lite: true` on HTTP requests and on the
24
+ * WebSocket upgrade (the marker is connection-scoped there, so lite and
25
+ * non-lite turns never share a pooled socket), strips image detail from
26
+ * input, and defaults `reasoning.context` to `all_turns` — mirroring codex-rs.
27
+ */
28
+ responsesLite?: boolean;
29
+ /**
30
+ * Extra `client_metadata` to include in the request body on both transports.
31
+ * The canonical Codex envelope is `client_metadata["x-codex-turn-metadata"]`
32
+ * (JSON string of thread/turn identifiers); flat keys are also accepted.
33
+ */
34
+ clientMetadata?: Record<string, string>;
35
+ /**
36
+ * Invoked when the server streams a `response.metadata` event carrying
37
+ * ChatGPT moderation metadata (`metadata.openai_chatgpt_moderation_metadata`)
38
+ * for first-party presentation parity. Diagnostic observer: failures are
39
+ * swallowed and must not alter the stream.
40
+ */
41
+ onModerationMetadata?: (metadata: unknown) => void;
12
42
  }
13
43
  type CodexTransport = "sse" | "websocket";
44
+ /**
45
+ * Per-session request-shape counters. Despite the name, these cover both
46
+ * transports: once stateful SSE chaining is enabled, SSE requests are counted
47
+ * too (the shared chained-request builder records every request it shapes).
48
+ */
14
49
  export interface OpenAICodexWebSocketDebugStats {
15
50
  fullContextRequests: number;
16
51
  deltaRequests: number;
@@ -21,7 +56,7 @@ export interface OpenAICodexWebSocketDebugStats {
21
56
  /** @internal Exported for tests. */
22
57
  export declare function normalizeCodexToolChoice(choice: ToolChoice | undefined, tools?: Tool[], model?: Model<"openai-codex-responses">): string | Record<string, unknown> | undefined;
23
58
  export declare const streamOpenAICodexResponses: StreamFunction<"openai-codex-responses">;
24
- export declare function prewarmOpenAICodexResponses(model: Model<"openai-codex-responses">, options?: Pick<OpenAICodexResponsesOptions, "apiKey" | "headers" | "sessionId" | "signal" | "preferWebsockets" | "providerSessionState">): Promise<void>;
59
+ export declare function prewarmOpenAICodexResponses(model: Model<"openai-codex-responses">, options?: Pick<OpenAICodexResponsesOptions, "apiKey" | "headers" | "sessionId" | "signal" | "preferWebsockets" | "providerSessionState" | "responsesLite">): Promise<void>;
25
60
  export interface OpenAICodexTransportDetails {
26
61
  websocketPreferred: boolean;
27
62
  lastTransport?: CodexTransport;
@@ -100,8 +100,12 @@ type ReasoningOptions = {
100
100
  };
101
101
  /**
102
102
  * Apply reasoning-related Responses parameters: enable encrypted reasoning content for replay,
103
- * set effort/summary when requested, and otherwise inject the GPT-5 "Juice: 0" no-reasoning hack.
104
- * Mutates `params` and may push a developer message into `messages`.
103
+ * set effort/summary when requested, and otherwise inject the "Juice: 0" no-reasoning hack
104
+ * when `model.compat.requiresJuiceZeroHack` is set (GPT-5 family by default).
105
+ * Mutates `params` and may push a developer message into `messages`. Returns
106
+ * the number of per-turn trailing scaffolding items appended to `messages`
107
+ * (the "Juice: 0" developer item), so callers doing stateful
108
+ * `previous_response_id` chaining can exclude them from append-baseline math.
105
109
  *
106
110
  * @param omitReasoningEffort - When `true`, suppresses `params.reasoning.effort` from the wire
107
111
  * body. Set by `xai-responses.ts` via {@link OpenAIResponsesOptions.omitReasoningEffort} for
@@ -112,7 +116,7 @@ type ReasoningOptions = {
112
116
  * without needing explicit activation. Callers that pass `options.reasoning` for such models
113
117
  * should expect this documented downgrade: the model will reason, but at its default effort.
114
118
  */
115
- export declare function applyResponsesReasoningParams<P extends OpenAI.Responses.ResponseCreateParamsStreaming>(params: P, model: Model<Api>, options: ReasoningOptions | undefined, messages: ResponseInput, mapEffort?: (effort: string) => string, includeEncryptedReasoning?: boolean, omitReasoningEffort?: boolean): void;
119
+ export declare function applyResponsesReasoningParams<P extends OpenAI.Responses.ResponseCreateParamsStreaming>(params: P, model: Model<"openai-responses" | "azure-openai-responses" | "openai-codex-responses">, options: ReasoningOptions | undefined, messages: ResponseInput, mapEffort?: (effort: string) => string, includeEncryptedReasoning?: boolean, omitReasoningEffort?: boolean): number;
116
120
  /** Populate `output.usage` from a Responses-API `response.usage` payload. Does not invoke `calculateCost`. */
117
121
  export declare function populateResponsesUsageFromResponse(output: AssistantMessage, usage: {
118
122
  input_tokens?: number | null;
@@ -125,4 +129,18 @@ export declare function populateResponsesUsageFromResponse(output: AssistantMess
125
129
  reasoning_tokens?: number | null;
126
130
  } | null;
127
131
  } | null | undefined): void;
132
+ /**
133
+ * Strict-prefix delta for stateful `previous_response_id` chaining (used by the
134
+ * platform Responses provider and the Codex provider on both transports):
135
+ * returns the input items the current request appends beyond the previous
136
+ * request's input plus the previous response's output items, or null when the
137
+ * request options differ or history mutated (the chain must break). Per-turn
138
+ * `client_metadata` (e.g. rotating turn ids) is excluded from the option
139
+ * comparison; codex-rs excludes it from the same check.
140
+ */
141
+ export declare function buildResponsesDeltaInput<TItem>(previous: {
142
+ input?: unknown;
143
+ } | undefined, previousResponseItems: readonly TItem[] | undefined, current: {
144
+ input?: unknown;
145
+ }): TItem[] | null;
128
146
  export {};
@@ -7,6 +7,16 @@ export interface OpenAIResponsesOptions extends StreamOptions {
7
7
  reasoningSummary?: "auto" | "detailed" | "concise" | null;
8
8
  serviceTier?: ServiceTier;
9
9
  toolChoice?: ToolChoice;
10
+ /**
11
+ * Stateful turns: chain via `previous_response_id` + delta input instead of
12
+ * replaying the full transcript. Forces `store: true` (the platform only
13
+ * resolves stored responses). Defaults ON against the official OpenAI API
14
+ * and OFF for other Responses endpoints; `PI_OPENAI_STATEFUL` overrides the
15
+ * default, and `false` here vetoes everything. Requires `sessionId` +
16
+ * `providerSessionState`. Falls back to a full replay whenever history
17
+ * mutates or the server reports a stale id.
18
+ */
19
+ statefulResponses?: boolean;
10
20
  /**
11
21
  * Enforce strict tool call/result pairing when building Responses API inputs.
12
22
  * Azure OpenAI and GitHub Copilot Responses paths require tool results to match prior tool calls.
@@ -1,4 +1,13 @@
1
+ import { ProviderHttpError } from "../errors";
1
2
  import type { Api, AssistantMessageEventStream as AssistantMessageEventStreamType, Context, Model, SimpleStreamOptions } from "../types";
3
+ /**
4
+ * Non-2xx response from the auth-gateway `/v1/pi/stream` endpoint. `code`
5
+ * carries the gateway's error-type token (`authentication_error`,
6
+ * `rate_limit_error`, `upstream_error`, ...).
7
+ */
8
+ export declare class AuthGatewayError extends ProviderHttpError {
9
+ constructor(message: string, status: number, headers?: Headers, code?: string);
10
+ }
2
11
  /**
3
12
  * Stream a turn through an `omp auth-gateway` over the pi-native protocol.
4
13
  *