@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.
- package/CHANGELOG.md +21 -0
- package/dist/types/auth-broker/remote-store.d.ts +2 -0
- package/dist/types/auth-broker/wire-schemas.d.ts +28 -0
- package/dist/types/auth-retry.d.ts +44 -20
- package/dist/types/auth-storage.d.ts +51 -10
- package/dist/types/error/flags.d.ts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/providers/openai-chat-server-schema.d.ts +3 -3
- package/dist/types/providers/openai-shared.d.ts +2 -0
- package/dist/types/registry/oauth/types.d.ts +8 -0
- package/dist/types/usage/cursor.d.ts +3 -0
- package/dist/types/usage/openai-codex-reset.d.ts +1 -1
- package/dist/types/usage/zai.d.ts +2 -1
- package/dist/types/usage.d.ts +4 -0
- package/package.json +4 -4
- package/src/auth-broker/discover.ts +22 -3
- package/src/auth-broker/remote-store.ts +103 -10
- package/src/auth-broker/wire-schemas.ts +4 -0
- package/src/auth-gateway/server.ts +3 -1
- package/src/auth-retry.ts +191 -53
- package/src/auth-storage.ts +614 -109
- package/src/error/auth-classify.ts +2 -1
- package/src/error/flags.ts +10 -0
- package/src/error/provider.ts +2 -2
- package/src/index.ts +1 -0
- package/src/providers/cursor.ts +10 -1
- package/src/providers/openai-chat-server-schema.ts +2 -1
- package/src/providers/openai-codex-responses.ts +54 -27
- package/src/providers/openai-shared.ts +6 -3
- package/src/registry/oauth/anthropic.ts +57 -24
- package/src/registry/oauth/oauth.html +1 -1
- package/src/registry/oauth/types.ts +8 -0
- package/src/stream.ts +19 -30
- package/src/usage/cursor.ts +192 -0
- package/src/usage/openai-codex-reset.ts +6 -2
- package/src/usage/zai.ts +49 -6
- 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
|
|
7
|
-
*
|
|
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
|
-
*
|
|
17
|
-
*
|
|
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
|
|
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
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
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
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
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
|
-
* -
|
|
118
|
-
* account
|
|
119
|
-
* -
|
|
137
|
+
* - 401/auth failure → one `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
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
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
|
|
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<
|
|
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
|
|
946
|
-
*
|
|
947
|
-
*
|
|
948
|
-
*
|
|
949
|
-
*
|
|
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
|
|
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
|
|
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;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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 & {});
|
|
@@ -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
|
package/dist/types/usage.d.ts
CHANGED
|
@@ -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.
|
|
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.
|
|
42
|
-
"@oh-my-pi/pi-utils": "16.5.
|
|
43
|
-
"@oh-my-pi/pi-wire": "16.5.
|
|
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 =
|
|
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;
|