@oh-my-pi/pi-ai 16.5.0 → 16.5.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 (37) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/types/auth-broker/remote-store.d.ts +2 -0
  3. package/dist/types/auth-broker/wire-schemas.d.ts +28 -0
  4. package/dist/types/auth-retry.d.ts +44 -20
  5. package/dist/types/auth-storage.d.ts +51 -10
  6. package/dist/types/error/flags.d.ts +1 -0
  7. package/dist/types/index.d.ts +1 -0
  8. package/dist/types/providers/openai-chat-server-schema.d.ts +3 -3
  9. package/dist/types/providers/openai-shared.d.ts +2 -0
  10. package/dist/types/registry/oauth/types.d.ts +8 -0
  11. package/dist/types/usage/cursor.d.ts +3 -0
  12. package/dist/types/usage/openai-codex-reset.d.ts +1 -1
  13. package/dist/types/usage/zai.d.ts +2 -1
  14. package/dist/types/usage.d.ts +4 -0
  15. package/package.json +4 -4
  16. package/src/auth-broker/discover.ts +22 -3
  17. package/src/auth-broker/remote-store.ts +103 -10
  18. package/src/auth-broker/wire-schemas.ts +4 -0
  19. package/src/auth-gateway/server.ts +3 -1
  20. package/src/auth-retry.ts +191 -53
  21. package/src/auth-storage.ts +614 -109
  22. package/src/error/auth-classify.ts +2 -1
  23. package/src/error/flags.ts +10 -0
  24. package/src/error/provider.ts +2 -2
  25. package/src/index.ts +1 -0
  26. package/src/providers/cursor.ts +10 -1
  27. package/src/providers/openai-chat-server-schema.ts +2 -1
  28. package/src/providers/openai-codex-responses.ts +54 -27
  29. package/src/providers/openai-shared.ts +6 -3
  30. package/src/registry/oauth/anthropic.ts +57 -24
  31. package/src/registry/oauth/oauth.html +1 -1
  32. package/src/registry/oauth/types.ts +8 -0
  33. package/src/stream.ts +19 -30
  34. package/src/usage/cursor.ts +192 -0
  35. package/src/usage/openai-codex-reset.ts +6 -2
  36. package/src/usage/zai.ts +49 -6
  37. package/src/usage.ts +4 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.5.1] - 2026-07-14
6
+
7
+ ### Added
8
+
9
+ - Added Cursor OAuth and access-token usage reporting to `omp usage` via Cursor's account usage endpoint.
10
+
11
+ ### Fixed
12
+
13
+ - Fixed OpenAI Responses `content_filter` terminal events being auto-retried as provider finish errors, ensuring content-filtered turns remain hard failures without triggering a retry loop.
14
+ - Improved credential rotation on usage and account-quota failures to cycle through all eligible credentials instead of stopping early, while maintaining rate-limit backoffs and safety guards.
15
+ - Fixed GLM tool call parsing to correctly handle and recover from missing or mistyped argument closers, preventing subsequent arguments from being swallowed.
16
+ - Fixed Anthropic credential management and usage routing for users with multiple organizations under a single email. Credentials, OAuth refreshes, usage reports, and active sessions are now correctly partitioned and isolated by organization, preventing subscriptions from overwriting or merging with each other.
17
+ - Fixed OpenAI and Codex response finalization to preserve streamed text when receiving empty content on completion. ([#5146])
18
+ - Fixed OpenAI Chat Completions request parsing to correctly accept assistant tool-call replay messages with null content. ([#5121])
19
+ - Fixed session-sticky OAuth credential mappings remaining active after credential changes, ensuring sessions correctly reselect accounts after login or logout. ([#4982])
20
+ - Fixed concurrent reasoning summaries to ignore legacy streaming events under cutoff contracts.
21
+ - Fixed Codex saved-reset redemption to apply to the selected OpenAI account in multi-account configurations. ([#5054])
22
+ - Updated the OAuth completion page to instruct users to close the tab manually when the browser blocks automatic window closing. ([#4855])
23
+ - Fixed Cursor `max_mode` requests to correctly send max-mode metadata on both model payload fields. ([#4797])
24
+ - Fixed configuration discovery to support both nested and flat YAML formats for `auth.broker.url` and `auth.broker.token` keys. ([#4734])
25
+
5
26
  ## [16.5.0] - 2026-07-13
6
27
 
7
28
  ### Added
@@ -79,6 +79,8 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
79
79
  deleteAuthCredentialsRemote(provider: string, disabledCause: string): Promise<void>;
80
80
  getCache(key: string): string | null;
81
81
  setCache(key: string, value: string, expiresAtSec: number): void;
82
+ /** Drop all cache rows whose keys start with the supplied prefix. */
83
+ deleteCachePrefix(prefix: string): void;
82
84
  cleanExpiredCache(): void;
83
85
  invalidateUsageCache(signal?: AbortSignal): Promise<void>;
84
86
  /**
@@ -9,6 +9,8 @@ export declare const oauthCredentialSchema: import("arktype/internal/variants/ob
9
9
  projectId?: string | undefined;
10
10
  email?: string | undefined;
11
11
  accountId?: string | undefined;
12
+ orgId?: string | undefined;
13
+ orgName?: string | undefined;
12
14
  }, {}>;
13
15
  /** OAuth credential as it appears in broker snapshots — refresh replaced with sentinel. */
14
16
  export declare const remoteOauthCredentialSchema: import("arktype/internal/variants/object.ts").ObjectType<{
@@ -21,6 +23,8 @@ export declare const remoteOauthCredentialSchema: import("arktype/internal/varia
21
23
  projectId?: string | undefined;
22
24
  email?: string | undefined;
23
25
  accountId?: string | undefined;
26
+ orgId?: string | undefined;
27
+ orgName?: string | undefined;
24
28
  }, {}>;
25
29
  export declare const apiKeyCredentialSchema: import("arktype/internal/variants/object.ts").ObjectType<{
26
30
  type: "api_key";
@@ -37,6 +41,8 @@ export declare const writableAuthCredentialSchema: import("arktype/internal/vari
37
41
  projectId?: string | undefined;
38
42
  email?: string | undefined;
39
43
  accountId?: string | undefined;
44
+ orgId?: string | undefined;
45
+ orgName?: string | undefined;
40
46
  } | {
41
47
  type: "api_key";
42
48
  key: string;
@@ -52,6 +58,8 @@ export declare const snapshotCredentialSchema: import("arktype/internal/variants
52
58
  projectId?: string | undefined;
53
59
  email?: string | undefined;
54
60
  accountId?: string | undefined;
61
+ orgId?: string | undefined;
62
+ orgName?: string | undefined;
55
63
  } | {
56
64
  type: "api_key";
57
65
  key: string;
@@ -69,6 +77,8 @@ export declare const credentialSnapshotEntrySchema: import("arktype/internal/var
69
77
  projectId?: string | undefined;
70
78
  email?: string | undefined;
71
79
  accountId?: string | undefined;
80
+ orgId?: string | undefined;
81
+ orgName?: string | undefined;
72
82
  } | {
73
83
  type: "api_key";
74
84
  key: string;
@@ -94,6 +104,8 @@ export declare const snapshotEntrySchema: import("arktype/internal/variants/obje
94
104
  projectId?: string | undefined;
95
105
  email?: string | undefined;
96
106
  accountId?: string | undefined;
107
+ orgId?: string | undefined;
108
+ orgName?: string | undefined;
97
109
  } | {
98
110
  type: "api_key";
99
111
  key: string;
@@ -136,6 +148,8 @@ export declare const snapshotResponseSchema: import("arktype/internal/variants/o
136
148
  projectId?: string | undefined;
137
149
  email?: string | undefined;
138
150
  accountId?: string | undefined;
151
+ orgId?: string | undefined;
152
+ orgName?: string | undefined;
139
153
  } | {
140
154
  type: "api_key";
141
155
  key: string;
@@ -174,6 +188,8 @@ export declare const snapshotStreamSnapshotEventSchema: import("arktype/internal
174
188
  projectId?: string | undefined;
175
189
  email?: string | undefined;
176
190
  accountId?: string | undefined;
191
+ orgId?: string | undefined;
192
+ orgName?: string | undefined;
177
193
  } | {
178
194
  type: "api_key";
179
195
  key: string;
@@ -213,6 +229,8 @@ export declare const snapshotStreamEntryEventSchema: import("arktype/internal/va
213
229
  projectId?: string | undefined;
214
230
  email?: string | undefined;
215
231
  accountId?: string | undefined;
232
+ orgId?: string | undefined;
233
+ orgName?: string | undefined;
216
234
  } | {
217
235
  type: "api_key";
218
236
  key: string;
@@ -264,6 +282,8 @@ export declare const snapshotStreamEventSchema: import("arktype/internal/variant
264
282
  projectId?: string | undefined;
265
283
  email?: string | undefined;
266
284
  accountId?: string | undefined;
285
+ orgId?: string | undefined;
286
+ orgName?: string | undefined;
267
287
  } | {
268
288
  type: "api_key";
269
289
  key: string;
@@ -301,6 +321,8 @@ export declare const snapshotStreamEventSchema: import("arktype/internal/variant
301
321
  projectId?: string | undefined;
302
322
  email?: string | undefined;
303
323
  accountId?: string | undefined;
324
+ orgId?: string | undefined;
325
+ orgName?: string | undefined;
304
326
  } | {
305
327
  type: "api_key";
306
328
  key: string;
@@ -400,6 +422,8 @@ export declare const credentialRefreshResponseSchema: import("arktype/internal/v
400
422
  projectId?: string | undefined;
401
423
  email?: string | undefined;
402
424
  accountId?: string | undefined;
425
+ orgId?: string | undefined;
426
+ orgName?: string | undefined;
403
427
  } | {
404
428
  type: "api_key";
405
429
  key: string;
@@ -440,6 +464,8 @@ export declare const credentialUploadRequestSchema: import("arktype/internal/var
440
464
  projectId?: string | undefined;
441
465
  email?: string | undefined;
442
466
  accountId?: string | undefined;
467
+ orgId?: string | undefined;
468
+ orgName?: string | undefined;
443
469
  } | {
444
470
  type: "api_key";
445
471
  key: string;
@@ -459,6 +485,8 @@ export declare const credentialUploadResponseSchema: import("arktype/internal/va
459
485
  projectId?: string | undefined;
460
486
  email?: string | undefined;
461
487
  accountId?: string | undefined;
488
+ orgId?: string | undefined;
489
+ orgName?: string | undefined;
462
490
  } | {
463
491
  type: "api_key";
464
492
  key: string;
@@ -3,8 +3,8 @@ import { isAuthRetryableError } from "./error/auth-classify.js";
3
3
  /**
4
4
  * Context passed to an {@link ApiKeyResolver} on each resolution attempt.
5
5
  *
6
- * The `error`/`lastChance` pair drives the central a/b/c retry policy shared by
7
- * the streaming ({@link streamSimple}) and non-streaming ({@link withAuth})
6
+ * The `error`/`lastChance` pair preserves the legacy a/b/c resolver contract
7
+ * shared by streaming ({@link streamSimple}) and non-streaming ({@link withAuth})
8
8
  * drivers:
9
9
  * - `error === undefined` → **initial resolve** (no force-refresh; cheap, may
10
10
  * return a locally-cached not-yet-expired token).
@@ -13,11 +13,13 @@ import { isAuthRetryableError } from "./error/auth-classify.js";
13
13
  * - `error !== undefined && lastChance` → **step (c): switch account**
14
14
  * (invalidate/usage-limit the current credential and rotate to a sibling).
15
15
  *
16
- * The resolver returns the bearer to send, or `undefined` to stop retrying and
17
- * surface the last error to the caller.
16
+ * Current drivers preserve that bounded a/b/c sequence for ordinary 401/auth
17
+ * failures. Usage/account-limit failures skip refresh and may repeat step (c)
18
+ * until the resolver returns `undefined`, cycles, or hits
19
+ * {@link AUTH_RETRY_MAX_ATTEMPTS}.
18
20
  */
19
21
  export interface ApiKeyResolveContext {
20
- /** True on the final retry step — the resolver should rotate to a sibling credential. */
22
+ /** True when the resolver should rotate to a sibling credential. */
21
23
  lastChance: boolean;
22
24
  /** The auth error that triggered this re-resolution, or `undefined` on the initial resolve. */
23
25
  error: unknown;
@@ -51,23 +53,39 @@ export declare function resolveApiKeyOnce(key: ApiKey | undefined, signal?: Abor
51
53
  export declare function seedApiKeyResolver(seed: string | undefined, resolver: ApiKeyResolver): ApiKeyResolver;
52
54
  export { isAuthRetryableError };
53
55
  /**
54
- * The ordered `lastChance` values for the retry steps after the initial
55
- * attempt fails: `false` → step (b) refresh-same, `true` → step (c) switch.
56
- * Shared by {@link withAuth} and the streaming retry driver so both run the
57
- * same policy.
56
+ * Legacy bounded a/b/c retry sequence retained for public compatibility:
57
+ * `false` → refresh-same, `true` → rotate/switch. Current drivers consume it
58
+ * once for ordinary 401/auth failures; usage/account-limit failures may repeat
59
+ * sibling rotation until a termination guard fires.
58
60
  */
59
61
  export declare const AUTH_RETRY_STEPS: readonly boolean[];
62
+ export declare const AUTH_RETRY_MAX_ATTEMPTS = 64;
60
63
  /** Resolve a single retry step, swallowing resolver failures into `undefined`. */
61
64
  export declare function resolveRetryKey(resolver: ApiKeyResolver, lastChance: boolean, error: unknown, signal?: AbortSignal, previousKey?: string): Promise<string | undefined>;
65
+ export interface AuthRetryKeyState {
66
+ /** Bearer strings already sent during this logical operation. */
67
+ attemptedKeys: Set<string>;
68
+ /** Bearer used by the most recent failed attempt. */
69
+ lastKey: string;
70
+ /** Whether the current credential already consumed its 401 refresh-same retry. */
71
+ refreshedCurrent: boolean;
72
+ /** Whether the legacy non-usage auth path already switched to one sibling. */
73
+ legacyAuthSwitchUsed: boolean;
74
+ /** Total outbound attempts accepted for this logical operation, including the initial request. */
75
+ attempts: number;
76
+ }
77
+ export declare function createAuthRetryKeyState(initialKey: string): AuthRetryKeyState;
78
+ export declare function resolveNextAuthRetryKey(state: AuthRetryKeyState, resolver: ApiKeyResolver, error: unknown, signal?: AbortSignal): Promise<string | undefined>;
62
79
  /**
63
80
  * Runs an auth-protected operation through the central a/b/c retry policy.
64
81
  *
65
82
  * - A static string key (or any non-resolver) → a single `attempt` with no
66
83
  * retry (identical to the legacy static-key path).
67
- * - A resolver → initial `attempt`, then on a retryable auth error up to two
68
- * more attempts (refresh-same, then switch). A step is skipped when the
69
- * resolver returns the same key it just tried or `undefined`; non-auth errors
70
- * propagate immediately.
84
+ * - A resolver → initial `attempt`, then resolver-driven retries until the
85
+ * applicable policy is exhausted, the resolver declines or cycles, or the
86
+ * operation reaches {@link AUTH_RETRY_MAX_ATTEMPTS}. Ordinary 401/auth
87
+ * failures retain one refresh-same plus one sibling switch; usage/account
88
+ * limits rotate directly through distinct siblings.
71
89
  *
72
90
  * Used by non-streaming consumers (image generation, web search, completion
73
91
  * helpers). The streaming driver in `stream.ts` implements the same policy with
@@ -91,6 +109,8 @@ export interface OAuthAccessSource {
91
109
  rotateSessionCredential(provider: string, sessionId: string | undefined, options?: {
92
110
  error?: unknown;
93
111
  signal?: AbortSignal;
112
+ apiKey?: string;
113
+ credentialId?: number;
94
114
  }): Promise<boolean>;
95
115
  }
96
116
  export interface WithOAuthAccessOptions {
@@ -114,13 +134,17 @@ export interface WithOAuthAccessOptions {
114
134
  * `projectId`, `enterpriseUrl`) instead of bare API-key bytes.
115
135
  *
116
136
  * - initial → `getOAuthAccess` (or `opts.seed`).
117
- * - step (b) → `getOAuthAccess` with `forceRefresh: true` (re-mint the SAME
118
- * account; picks up peer/broker rotations).
119
- * - step (c) → `rotateSessionCredential` then re-resolve (switch to a sibling).
137
+ * - 401/auth failureone `getOAuthAccess` with `forceRefresh: true` for the
138
+ * current account, then sibling rotation.
139
+ * - usage-limit failure → `rotateSessionCredential` directly, without a
140
+ * force-refresh detour.
120
141
  *
121
- * A step is skipped when it yields no access or the same `accessToken` that
122
- * just failed; non-auth errors propagate immediately. Use this instead of
123
- * hand-rolled `getOAuthAccess` + fetch flows so 401s and usage-limits rotate
124
- * credentials instead of failing the call.
142
+ * A refresh-same step may retry a new bearer for the same credential identity;
143
+ * sibling rotation stops when it yields a credential identity
144
+ * (`credentialId ?? accessToken`) or bearer already attempted in this turn.
145
+ * All OAuth attempts share the {@link AUTH_RETRY_MAX_ATTEMPTS} ceiling.
146
+ * Non-auth errors propagate immediately. Use this instead of hand-rolled
147
+ * `getOAuthAccess` + fetch flows so 401s and usage-limits rotate credentials
148
+ * instead of failing the call.
125
149
  */
126
150
  export declare function withOAuthAccess<T>(storage: OAuthAccessSource, provider: string, attempt: (access: OAuthAccess) => Promise<T>, opts?: WithOAuthAccessOptions): Promise<T>;
@@ -97,8 +97,11 @@ export interface CredentialHealthResult {
97
97
  type: AuthCredential["type"];
98
98
  /** OAuth email if known on the stored credential or surfaced by the probe. */
99
99
  email?: string;
100
- /** OAuth account id / org id if known. */
100
+ /** OAuth account id if known. */
101
101
  accountId?: string;
102
+ /** Organization/workspace the credential is scoped to (Anthropic multi-subscription). */
103
+ orgId?: string;
104
+ orgName?: string;
102
105
  /** `true` when the refresh token lives on a remote broker (sentinel was present). */
103
106
  remoteRefresh?: true;
104
107
  ok: boolean | null;
@@ -247,6 +250,8 @@ export interface AuthCredentialStore {
247
250
  includeExpired?: boolean;
248
251
  }): string | null;
249
252
  setCache(key: string, value: string, expiresAtSec: number): void;
253
+ /** Drop all cache rows whose keys start with the supplied prefix. */
254
+ deleteCachePrefix?(prefix: string): void;
250
255
  cleanExpiredCache(): void;
251
256
  /** Non-expired block for one (credential, providerKey, scope) key, or undefined. */
252
257
  getCredentialBlock?(credentialId: number, providerKey: string, blockScope: string): number | undefined;
@@ -485,6 +490,21 @@ export interface OAuthAccess {
485
490
  projectId?: string;
486
491
  enterpriseUrl?: string;
487
492
  apiEndpoint?: string;
493
+ /** Organization/workspace the credential is scoped to (Anthropic multi-subscription). */
494
+ orgId?: string;
495
+ orgName?: string;
496
+ }
497
+ /**
498
+ * Identity slice of the credential a successful {@link AuthStorage.login}
499
+ * stored — lets callers confirm WHICH account (and for Anthropic, which
500
+ * organization/subscription) was added, without exposing tokens.
501
+ */
502
+ export interface OAuthLoginIdentity {
503
+ type: "oauth" | "api_key";
504
+ email?: string;
505
+ accountId?: string;
506
+ orgId?: string;
507
+ orgName?: string;
488
508
  }
489
509
  export interface OAuthAccessFailure {
490
510
  credentialId?: number;
@@ -493,6 +513,9 @@ export interface OAuthAccessFailure {
493
513
  projectId?: string;
494
514
  enterpriseUrl?: string;
495
515
  apiEndpoint?: string;
516
+ /** Organization/workspace the credential is scoped to (Anthropic multi-subscription). */
517
+ orgId?: string;
518
+ orgName?: string;
496
519
  error: string;
497
520
  }
498
521
  /**
@@ -505,6 +528,9 @@ export interface OAuthAccountIdentity {
505
528
  accountId?: string;
506
529
  email?: string;
507
530
  projectId?: string;
531
+ /** Organization/workspace the credential is scoped to (Anthropic multi-subscription). */
532
+ orgId?: string;
533
+ orgName?: string;
508
534
  }
509
535
  export type OAuthAccessResolution = ({
510
536
  ok: true;
@@ -523,6 +549,9 @@ export interface OAuthAccountSummary {
523
549
  email?: string;
524
550
  projectId?: string;
525
551
  enterpriseUrl?: string;
552
+ /** Organization/workspace the credential is scoped to (Anthropic multi-subscription). */
553
+ orgId?: string;
554
+ orgName?: string;
526
555
  }
527
556
  export interface InvalidateCredentialMatchingOptions {
528
557
  signal?: AbortSignal;
@@ -753,7 +782,10 @@ export declare class AuthStorage {
753
782
  */
754
783
  getAll(): AuthStorageData;
755
784
  /**
756
- * Login to an OAuth provider.
785
+ * Login to an OAuth provider. Resolves with the stored credential's
786
+ * identity slice (or `undefined` when nothing was stored) so callers can
787
+ * surface which account — and for Anthropic, which organization — the
788
+ * login registered.
757
789
  */
758
790
  login(provider: OAuthProviderId, ctrl: OAuthController & {
759
791
  /** onAuth is required by auth-storage but optional in OAuthController */
@@ -763,7 +795,7 @@ export declare class AuthStorage {
763
795
  message: string;
764
796
  placeholder?: string;
765
797
  }) => Promise<string>;
766
- }): Promise<void>;
798
+ }): Promise<OAuthLoginIdentity | undefined>;
767
799
  /**
768
800
  * Logout from a provider.
769
801
  */
@@ -835,6 +867,7 @@ export declare class AuthStorage {
835
867
  baseUrl?: string;
836
868
  modelId?: string;
837
869
  apiKey?: string;
870
+ credentialId?: number;
838
871
  signal?: AbortSignal;
839
872
  }): Promise<UsageLimitMarkResult>;
840
873
  /**
@@ -942,17 +975,22 @@ export declare class AuthStorage {
942
975
  invalidateCredentialMatching(provider: string, apiKey: string, options?: InvalidateCredentialMatchingOptions): Promise<boolean>;
943
976
  invalidateCredentialMatching(provider: string, apiKey: string, signal?: AbortSignal): Promise<boolean>;
944
977
  /**
945
- * Rotate away from the session's current credential after a retryable auth
946
- * error — step (c) of the auth-retry policy. Stateless: looks up the
947
- * session-sticky credential (no API-key matching needed), applies the
948
- * storage action for the error class, then clears the sticky so the next
949
- * {@link AuthStorage.getApiKey} for this session picks a sibling.
978
+ * Rotate away from the credential that failed after a retryable auth error —
979
+ * step (c) of the auth-retry policy. Prefer the failed stored row id supplied
980
+ * in `options.credentialId`, then the failed bearer supplied in
981
+ * `options.apiKey`, so overlapping requests cannot redirect rotation through
982
+ * stale session stickiness. Fall back to the session-sticky credential only
983
+ * when neither explicit target is available. For hard-auth errors, an explicit
984
+ * target that no longer matches storage returns `false` without mutation.
985
+ * Delayed usage-limit errors may instead recover the durable OAuth row from
986
+ * the bearer fingerprint recorded when the request resolved.
950
987
  *
951
988
  * - usage-limit / account-rate-limit error → {@link AuthStorage.markUsageLimitReached}
952
989
  * (temporary block via its own backoff — default plus server usage-report
953
990
  * reset; sticky left intact so the next resolve re-ranks around the block).
954
991
  * - otherwise (hard 401 / auth failure) → mark the credential suspect (or
955
- * reload when no broker hook is wired) and block it, then drop the sticky.
992
+ * reload when no broker hook is wired) and block it, then drop matching
993
+ * sticky state.
956
994
  *
957
995
  * Returns whether another usable credential of the same type remains.
958
996
  */
@@ -960,6 +998,7 @@ export declare class AuthStorage {
960
998
  error?: unknown;
961
999
  modelId?: string;
962
1000
  apiKey?: string;
1001
+ credentialId?: number;
963
1002
  signal?: AbortSignal;
964
1003
  }): Promise<boolean>;
965
1004
  /**
@@ -968,7 +1007,7 @@ export declare class AuthStorage {
968
1007
  *
969
1008
  * - initial (`error: undefined`) → resolve the session credential.
970
1009
  * - step (b) `!lastChance` → force-refresh the SAME session-sticky credential.
971
- * - step (c) `lastChance` → rotate to a sibling credential, then re-resolve.
1010
+ * - step (c) `lastChance` → rotate to a sibling and re-resolve, unless quota exhaustion has no sibling.
972
1011
  *
973
1012
  * Used by web-search providers and other consumers that hold an AuthStorage
974
1013
  * directly (no ModelRegistry in scope).
@@ -1083,6 +1122,8 @@ export declare class SqliteAuthCredentialStore implements AuthCredentialStore {
1083
1122
  includeExpired?: boolean;
1084
1123
  }): string | null;
1085
1124
  setCache(key: string, value: string, expiresAtSec: number): void;
1125
+ /** Drop all cache rows whose keys start with the supplied prefix. */
1126
+ deleteCachePrefix(prefix: string): void;
1086
1127
  cleanExpiredCache(): void;
1087
1128
  getCredentialBlock(credentialId: number, providerKey: string, blockScope: string): number | undefined;
1088
1129
  getCredentialBlockReconcileAfter(credentialId: number, providerKey: string, blockScope: string): number | undefined;
@@ -8,6 +8,7 @@ export declare const Flag: {
8
8
  readonly StaleResponsesItem: 1048576;
9
9
  readonly MalformedFunctionCall: 2097152;
10
10
  readonly ProviderFinishError: 4194304;
11
+ readonly ContentBlocked: 32768;
11
12
  readonly ContextOverflow: 8388608;
12
13
  readonly AuthFailed: 16777216;
13
14
  readonly SilentAbort: 33554432;
@@ -29,6 +29,7 @@ export * from "./stream.js";
29
29
  export * from "./types.js";
30
30
  export * from "./usage.js";
31
31
  export * from "./usage/claude.js";
32
+ export * from "./usage/cursor.js";
32
33
  export * from "./usage/gemini.js";
33
34
  export * from "./usage/github-copilot.js";
34
35
  export * from "./usage/google-antigravity.js";
@@ -232,7 +232,7 @@ export declare const assistantMessageSchema: import("arktype/internal/variants/o
232
232
  refusal: string;
233
233
  } | {
234
234
  type: string;
235
- })[] | undefined;
235
+ })[] | null | undefined;
236
236
  tool_calls?: {
237
237
  id: string;
238
238
  type?: "function" | undefined;
@@ -404,7 +404,7 @@ export declare const messageSchema: import("arktype/internal/variants/object.ts"
404
404
  refusal: string;
405
405
  } | {
406
406
  type: string;
407
- })[] | undefined;
407
+ })[] | null | undefined;
408
408
  tool_calls?: {
409
409
  id: string;
410
410
  type?: "function" | undefined;
@@ -581,7 +581,7 @@ export declare const openaiChatRequestSchema: import("arktype/internal/variants/
581
581
  refusal: string;
582
582
  } | {
583
583
  type: string;
584
- })[] | undefined;
584
+ })[] | null | undefined;
585
585
  tool_calls?: {
586
586
  id: string;
587
587
  type?: "function" | undefined;
@@ -428,6 +428,8 @@ export declare function appendReasoningSummaryPartDone(item: ResponseReasoningIt
428
428
  export declare function applyReasoningSummaryDone(state: SequentialCutoffSummaryState, block: ThinkingContent, text: string, summaryIndex: number, stream: AssistantMessageEventStream, output: AssistantMessage, contentIndex: number): void;
429
429
  export declare function appendMessageContentPart(item: ResponseOutputMessage, part: ResponseContentPartAddedEvent["part"] | undefined): void;
430
430
  export declare function appendMessageTextDelta(item: ResponseOutputMessage, block: TextContent, delta: string, stream: AssistantMessageEventStream, output: AssistantMessage, contentIndex: number, partType: "output_text" | "refusal"): void;
431
+ /** Chooses final message text while treating non-empty terminal content as authoritative. */
432
+ export declare function finalizeMessageText(item: ResponseOutputMessage, streamedText: string): string;
431
433
  export declare function accumulateToolCallArgumentsDelta(block: ResponsesToolCallBlock, delta: string, stream: AssistantMessageEventStream, output: AssistantMessage, contentIndex: number): void;
432
434
  /**
433
435
  * Finalize streamed function-call arguments from the authoritative `.done`
@@ -9,6 +9,14 @@ export type OAuthCredentials = {
9
9
  email?: string;
10
10
  accountId?: string;
11
11
  apiEndpoint?: string;
12
+ /**
13
+ * Organization/workspace the token is scoped to (e.g. an Anthropic org
14
+ * UUID). Captured once at login; token refreshes never rewrite it. Lets
15
+ * one account email hold credentials for multiple subscriptions.
16
+ */
17
+ orgId?: string;
18
+ /** Human-readable organization name for display (may embed the email). */
19
+ orgName?: string;
12
20
  };
13
21
  export type OAuthProvider = OAuthProviderUnion;
14
22
  export type OAuthProviderId = OAuthProvider | (string & {});
@@ -0,0 +1,3 @@
1
+ import type { UsageProvider, UsageReport } from "../usage.js";
2
+ export declare function parseCursorUsage(payload: unknown, fetchedAt?: number): UsageReport | null;
3
+ export declare const cursorUsageProvider: UsageProvider;
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * GET /wham/rate-limit-reset-credits → list redeemable credits
10
10
  * POST /wham/rate-limit-reset-credits/consume → spend one credit
11
- * body: { credit_id, redeem_request_id }
11
+ * body: { credit_id, redeem_request_id, account_id? }
12
12
  *
13
13
  * `redeem_request_id` is a client-generated idempotency key (UUID). The consume
14
14
  * response carries a `code`: `"reset"` on success, otherwise a business reason
@@ -1,2 +1,3 @@
1
- import type { UsageProvider } from "../usage.js";
1
+ import type { CredentialRankingStrategy, UsageProvider } from "../usage.js";
2
2
  export declare const zaiUsageProvider: UsageProvider;
3
+ export declare const zaiRankingStrategy: CredentialRankingStrategy;
@@ -281,6 +281,10 @@ export interface UsageCredential {
281
281
  accountId?: string;
282
282
  projectId?: string;
283
283
  email?: string;
284
+ /** Organization/workspace the credential is scoped to (see OAuthCredentials.orgId). */
285
+ orgId?: string;
286
+ /** Human-readable organization name for display. */
287
+ orgName?: string;
284
288
  enterpriseUrl?: string;
285
289
  metadata?: Record<string, unknown>;
286
290
  apiEndpoint?: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-ai",
4
- "version": "16.5.0",
4
+ "version": "16.5.1",
5
5
  "description": "Unified LLM API with automatic model discovery and provider configuration",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -38,9 +38,9 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@bufbuild/protobuf": "^2.12.1",
41
- "@oh-my-pi/pi-catalog": "16.5.0",
42
- "@oh-my-pi/pi-utils": "16.5.0",
43
- "@oh-my-pi/pi-wire": "16.5.0",
41
+ "@oh-my-pi/pi-catalog": "16.5.1",
42
+ "@oh-my-pi/pi-utils": "16.5.1",
43
+ "@oh-my-pi/pi-wire": "16.5.1",
44
44
  "arktype": "2.2.3",
45
45
  "zod": "^4"
46
46
  },
@@ -72,6 +72,26 @@ interface ConfigSnapshot {
72
72
  token?: string;
73
73
  }
74
74
 
75
+ /**
76
+ * Resolve a dotted config key (e.g. `auth.broker.url`) against a parsed YAML
77
+ * record, accepting both nested form (`auth: { broker: { url } }`) and the
78
+ * legacy flat literal-dot key (`"auth.broker.url": ...`). Nested wins when both
79
+ * are present. Returns the value only when it is a string.
80
+ */
81
+ function readDottedString(record: Record<string, unknown>, dottedKey: string): string | undefined {
82
+ let current: unknown = record;
83
+ for (const segment of dottedKey.split(".")) {
84
+ if (current === null || typeof current !== "object" || Array.isArray(current)) {
85
+ current = undefined;
86
+ break;
87
+ }
88
+ current = (current as Record<string, unknown>)[segment];
89
+ }
90
+ if (typeof current === "string") return current;
91
+ const flat = record[dottedKey];
92
+ return typeof flat === "string" ? flat : undefined;
93
+ }
94
+
75
95
  async function readConfigYaml(agentDir: string): Promise<ConfigSnapshot> {
76
96
  for (const filename of MAIN_CONFIG_FILENAMES) {
77
97
  const configPath = path.join(agentDir, filename);
@@ -80,9 +100,8 @@ async function readConfigYaml(agentDir: string): Promise<ConfigSnapshot> {
80
100
  const parsed = YAML.parse(raw);
81
101
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
82
102
  const record = parsed as Record<string, unknown>;
83
- const url = typeof record["auth.broker.url"] === "string" ? (record["auth.broker.url"] as string) : undefined;
84
- const token =
85
- typeof record["auth.broker.token"] === "string" ? (record["auth.broker.token"] as string) : undefined;
103
+ const url = readDottedString(record, "auth.broker.url");
104
+ const token = readDottedString(record, "auth.broker.token");
86
105
  return { url, token };
87
106
  } catch (err) {
88
107
  if (isEnoent(err)) continue;