@oh-my-pi/pi-ai 17.2.7 → 17.2.9
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 +11 -0
- package/dist/types/auth/sqlite-credential-store.d.ts +117 -0
- package/dist/types/auth-storage.d.ts +1 -108
- package/dist/types/error/flags.d.ts +4 -2
- package/dist/types/error/index.d.ts +1 -0
- package/dist/types/error/rate-limit.d.ts +21 -5
- package/dist/types/error/retryable.d.ts +1 -1
- package/dist/types/providers/anthropic.d.ts +2 -2
- package/dist/types/providers/cursor/exec-modern.d.ts +1 -1
- package/dist/types/providers/cursor-pi-args.d.ts +6 -0
- package/dist/types/providers/mock.d.ts +1 -0
- package/dist/types/providers/openai-codex/request-transformer.d.ts +1 -1
- package/dist/types/providers/openai-codex-responses.d.ts +1 -1
- package/dist/types/providers/pi-native-server.d.ts +1 -1
- package/dist/types/utils/http-inspector.d.ts +5 -4
- package/dist/types/utils/retry.d.ts +3 -2
- package/package.json +5 -5
- package/src/auth/sqlite-credential-store.ts +2049 -0
- package/src/auth-retry.ts +8 -5
- package/src/auth-storage.ts +30 -2023
- package/src/error/auth-classify.ts +5 -4
- package/src/error/flags.ts +74 -18
- package/src/error/index.ts +1 -0
- package/src/error/rate-limit.ts +64 -5
- package/src/error/retryable.ts +1 -1
- package/src/providers/anthropic.ts +15 -3
- package/src/providers/cursor/exec-modern.ts +1 -0
- package/src/providers/cursor-pi-args.ts +30 -0
- package/src/providers/cursor.ts +16 -9
- package/src/providers/mock.ts +1 -1
- package/src/providers/openai-codex/request-transformer.ts +21 -27
- package/src/providers/openai-codex-responses.ts +2 -2
- package/src/providers/pi-native-client.ts +1 -1
- package/src/providers/pi-native-server.ts +1 -1
- package/src/registry/oauth/devin.ts +1 -1
- package/src/stream.ts +3 -3
- package/src/usage/openai-codex.ts +30 -19
- package/src/utils/http-inspector.ts +6 -5
- package/src/utils/retry.ts +24 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [17.2.9] - 2026-08-05
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed GitHub Copilot requests failing with a raw `HTTP 400 model_not_available_for_integrator` on roughly half of all turns for recently rolled-out models. Copilot's fleet is not uniform — part of it rejects models that `/models` advertises on the same host — and the transient classifier matched only the older `model_not_supported` code at a fixed envelope depth, so these rejections surfaced as terminal errors instead of entering the existing retry path. Model-availability 400s are now recognized at any envelope depth and rerolled on a flat delay with a dedicated 8-attempt budget on the OpenAI transports; every other retryable failure keeps its previous backoff and attempt count.
|
|
10
|
+
- Fixed Cursor reads with inline OMP range selectors reporting the returned slice length as the source file's `totalLines`, which made sequential reads of an unchanged file appear inconsistent ([#7590](https://github.com/can1357/oh-my-pi/issues/7590)).
|
|
11
|
+
- Made model-scoped usage health ignore Codex accounts that cannot use the requested plan-gated model while retaining conservative unknown-state handling and independent usage-window resets.
|
|
12
|
+
- Fixed OpenAI Codex usage telemetry blocking explicitly allowed ChatGPT Team credentials when a weekly `used_percent` rounded to 100, which could route multi-account sessions to an actually exhausted sibling instead ([#7617](https://github.com/can1357/oh-my-pi/issues/7617)).
|
|
13
|
+
- Fixed OpenAI Codex GPT-5.x requests sending optional `reasoning.summary`, `reasoning.context`, and `text.verbosity` controls by default, reducing Codex `server_error` disconnects from unsupported request shapes. ([#4949](https://github.com/can1357/oh-my-pi/issues/4949))
|
|
14
|
+
- Classified concurrent-request caps separately from quota exhaustion so they use a short retry backoff without burning a credential, and rotate credentials for account-scoped 403 caps such as Devin's overall message limit.
|
|
15
|
+
|
|
5
16
|
## [17.2.7] - 2026-08-03
|
|
6
17
|
|
|
7
18
|
### Changed
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite-backed credential persistence for AuthStorage.
|
|
3
|
+
*
|
|
4
|
+
* The public AuthCredentialStore interface remains in ../auth-storage so local
|
|
5
|
+
* and remote stores share the same contract.
|
|
6
|
+
*/
|
|
7
|
+
import { Database } from "bun:sqlite";
|
|
8
|
+
import type { AuthCredential, AuthCredentialStore, CredentialRefreshLeaseFence, DisabledCredentialSummary, StoredAuthCredential, StoredCredentialBlock } from "../auth-storage.js";
|
|
9
|
+
import type { OAuthCredentials } from "../registry/oauth/types.js";
|
|
10
|
+
import type { ClientUsageReport, ClientUsageSummary, UsageCostHistoryEntry, UsageCostHistoryQuery, UsageHistoryEntry, UsageHistoryQuery } from "../usage.js";
|
|
11
|
+
export declare const USAGE_REPORT_TTL_MS: number;
|
|
12
|
+
type SerializedCredentialRecord = {
|
|
13
|
+
credentialType: AuthCredential["type"];
|
|
14
|
+
data: string;
|
|
15
|
+
identityKey: string | null;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* SQLite's busy result code family — base `SQLITE_BUSY` plus the extended
|
|
19
|
+
* variants `SQLITE_BUSY_RECOVERY` (concurrent WAL recovery), `SQLITE_BUSY_SNAPSHOT`,
|
|
20
|
+
* and `SQLITE_BUSY_TIMEOUT`. All warrant the same backoff-and-retry treatment.
|
|
21
|
+
*/
|
|
22
|
+
export declare function isSqliteBusyError(err: unknown): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* SQLite's unrecoverable-corruption result codes — the `SQLITE_CORRUPT` family
|
|
25
|
+
* (base plus extended variants like `SQLITE_CORRUPT_VTAB` / `SQLITE_CORRUPT_INDEX`)
|
|
26
|
+
* and `SQLITE_NOTADB` (the file header is not a database). Unlike
|
|
27
|
+
* {@link isSqliteBusyError}, these never clear by retrying: the store must be
|
|
28
|
+
* repaired or replaced, so callers latch and stop touching it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isSqliteCorruptionError(err: unknown): boolean;
|
|
31
|
+
export declare function serializeCredential(provider: string, credential: AuthCredential): SerializedCredentialRecord | null;
|
|
32
|
+
export declare function resolveCredentialIdentityKey(provider: string, credential: AuthCredential): string | null;
|
|
33
|
+
/**
|
|
34
|
+
* Default SQLite-backed implementation of {@link AuthCredentialStore}.
|
|
35
|
+
*
|
|
36
|
+
* Used by the pi-ai CLI and as the default store for `AuthStorage.create()`.
|
|
37
|
+
* Also exposes convenience methods (`saveOAuth`, `getOAuth`, `saveApiKey`,
|
|
38
|
+
* `getApiKey`, `listProviders`, `deleteProvider`) that callers can use directly
|
|
39
|
+
* without going through `AuthStorage`.
|
|
40
|
+
*/
|
|
41
|
+
export declare class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
42
|
+
#private;
|
|
43
|
+
constructor(db: Database);
|
|
44
|
+
static open(dbPath?: string): Promise<SqliteAuthCredentialStore>;
|
|
45
|
+
listAuthCredentials(provider?: string): StoredAuthCredential[];
|
|
46
|
+
listDisabledCredentials(provider?: string): Promise<DisabledCredentialSummary[]>;
|
|
47
|
+
replaceAuthCredentialsForProvider(provider: string, credentials: AuthCredential[]): StoredAuthCredential[];
|
|
48
|
+
upsertAuthCredentialForProvider(provider: string, credential: AuthCredential): StoredAuthCredential[];
|
|
49
|
+
updateAuthCredential(id: number, credential: AuthCredential): void;
|
|
50
|
+
tryUpdateAuthCredentialIfMatches(id: number, expectedData: string, credential: AuthCredential, lease?: CredentialRefreshLeaseFence): boolean;
|
|
51
|
+
deleteAuthCredential(id: number, disabledCause: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* CAS-style disable: only soft-deletes the row when its `data` column still
|
|
54
|
+
* matches `expectedData` and the row has not already been disabled. Used by
|
|
55
|
+
* the OAuth refresh-failure path to avoid clobbering a peer that rotated the
|
|
56
|
+
* row between our pre-check and the disable.
|
|
57
|
+
*/
|
|
58
|
+
tryDisableAuthCredentialIfMatches(id: number, expectedData: string, disabledCause: string, lease?: CredentialRefreshLeaseFence): boolean;
|
|
59
|
+
deleteAuthCredentialsForProvider(provider: string, disabledCause: string): void;
|
|
60
|
+
getCache(key: string, options?: {
|
|
61
|
+
includeExpired?: boolean;
|
|
62
|
+
}): string | null;
|
|
63
|
+
setCache(key: string, value: string, expiresAtSec: number): void;
|
|
64
|
+
/** Drop all cache rows whose keys start with the supplied prefix. */
|
|
65
|
+
deleteCachePrefix(prefix: string): void;
|
|
66
|
+
cleanExpiredCache(): void;
|
|
67
|
+
getCredentialBlock(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
68
|
+
getCredentialBlockReconcileAfter(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
69
|
+
upsertCredentialBlock(block: StoredCredentialBlock): void;
|
|
70
|
+
deleteCredentialBlock(credentialId: number, providerKey: string, blockScope: string): void;
|
|
71
|
+
deleteCredentialBlocks(credentialId: number): void;
|
|
72
|
+
cleanExpiredCredentialBlocks(nowMs: number): void;
|
|
73
|
+
listCredentialBlocks(credentialIds: readonly number[]): StoredCredentialBlock[];
|
|
74
|
+
tryAcquireCredentialRefreshLease(credentialId: number, owner: string, expiresAtMs: number): boolean;
|
|
75
|
+
getCredentialRefreshLeaseExpiresAt(credentialId: number): number | undefined;
|
|
76
|
+
renewCredentialRefreshLease(credentialId: number, owner: string, expiresAtMs: number): boolean;
|
|
77
|
+
releaseCredentialRefreshLease(credentialId: number, owner: string): void;
|
|
78
|
+
recordUsageSnapshots(entries: UsageHistoryEntry[]): void;
|
|
79
|
+
listUsageHistory(query?: UsageHistoryQuery): UsageHistoryEntry[];
|
|
80
|
+
recordUsageCosts(entries: UsageCostHistoryEntry[]): void;
|
|
81
|
+
listUsageCosts(query?: UsageCostHistoryQuery): UsageCostHistoryEntry[];
|
|
82
|
+
recordClientUsage(report: ClientUsageReport): void;
|
|
83
|
+
getClientUsageSummary(sinceMs: number): ClientUsageSummary;
|
|
84
|
+
/**
|
|
85
|
+
* Save OAuth credentials for a provider.
|
|
86
|
+
* Preserves unrelated identities and replaces only the matching credential.
|
|
87
|
+
*/
|
|
88
|
+
saveOAuth(provider: string, credentials: OAuthCredentials): void;
|
|
89
|
+
/**
|
|
90
|
+
* Get OAuth credentials for a provider.
|
|
91
|
+
*/
|
|
92
|
+
getOAuth(provider: string): OAuthCredentials | null;
|
|
93
|
+
/**
|
|
94
|
+
* Save API key for a provider (replaces existing).
|
|
95
|
+
*/
|
|
96
|
+
saveApiKey(provider: string, apiKey: string): void;
|
|
97
|
+
/**
|
|
98
|
+
* Get API key for a provider.
|
|
99
|
+
*/
|
|
100
|
+
getApiKey(provider: string): string | null;
|
|
101
|
+
/**
|
|
102
|
+
* List all providers with credentials.
|
|
103
|
+
*/
|
|
104
|
+
listProviders(): string[];
|
|
105
|
+
/**
|
|
106
|
+
* Delete all credentials for a provider.
|
|
107
|
+
*/
|
|
108
|
+
deleteProvider(provider: string): void;
|
|
109
|
+
/**
|
|
110
|
+
* SQLite increments `data_version` when another connection commits. Own
|
|
111
|
+
* writes leave it unchanged and already notify AuthStorage directly.
|
|
112
|
+
*/
|
|
113
|
+
pollExternalChanges(): boolean;
|
|
114
|
+
acknowledgeLocalChanges(): void;
|
|
115
|
+
close(): void;
|
|
116
|
+
}
|
|
117
|
+
export {};
|
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Credential storage for API keys and OAuth tokens.
|
|
3
|
-
* Handles loading, saving, refreshing credentials, and usage tracking.
|
|
4
|
-
*
|
|
5
|
-
* This module defines:
|
|
6
|
-
* - `AuthCredentialStore` interface: persistence abstraction (SQLite, remote vault, …)
|
|
7
|
-
* - `AuthStorage` class: credential management with round-robin, usage limits, OAuth refresh
|
|
8
|
-
* - `SqliteAuthCredentialStore`: concrete SQLite-backed implementation
|
|
9
|
-
*/
|
|
10
|
-
import { Database } from "bun:sqlite";
|
|
11
1
|
import type { ApiKeyResolver } from "./auth-retry.js";
|
|
12
2
|
import type { OAuthAuthInfo, OAuthController, OAuthCredentials, OAuthProviderId } from "./registry/oauth/types.js";
|
|
13
3
|
import type { Provider } from "./types.js";
|
|
14
4
|
import type { ClientUsageReport, ClientUsageSummary, CredentialRankingStrategy, ObservedUsageEntry, UsageCostHistoryEntry, UsageCostHistoryQuery, UsageHistoryEntry, UsageHistoryQuery, UsageLogger, UsageProvider, UsageReport } from "./usage.js";
|
|
15
5
|
import { type CodexResetConsumeCode, type CodexResetCredit } from "./usage/openai-codex-reset.js";
|
|
6
|
+
export { isSqliteBusyError, isSqliteCorruptionError, SqliteAuthCredentialStore, } from "./auth/sqlite-credential-store.js";
|
|
16
7
|
export type ApiKeyCredential = {
|
|
17
8
|
type: "api_key";
|
|
18
9
|
key: string;
|
|
@@ -1265,101 +1256,3 @@ export declare class AuthStorage {
|
|
|
1265
1256
|
*/
|
|
1266
1257
|
describeCredentialSource(provider: string, sessionId?: string): string | undefined;
|
|
1267
1258
|
}
|
|
1268
|
-
/**
|
|
1269
|
-
* SQLite's busy result code family — base `SQLITE_BUSY` plus the extended
|
|
1270
|
-
* variants `SQLITE_BUSY_RECOVERY` (concurrent WAL recovery), `SQLITE_BUSY_SNAPSHOT`,
|
|
1271
|
-
* and `SQLITE_BUSY_TIMEOUT`. All warrant the same backoff-and-retry treatment.
|
|
1272
|
-
*/
|
|
1273
|
-
export declare function isSqliteBusyError(err: unknown): boolean;
|
|
1274
|
-
/**
|
|
1275
|
-
* SQLite's unrecoverable-corruption result codes — the `SQLITE_CORRUPT` family
|
|
1276
|
-
* (base plus extended variants like `SQLITE_CORRUPT_VTAB` / `SQLITE_CORRUPT_INDEX`)
|
|
1277
|
-
* and `SQLITE_NOTADB` (the file header is not a database). Unlike
|
|
1278
|
-
* {@link isSqliteBusyError}, these never clear by retrying: the store must be
|
|
1279
|
-
* repaired or replaced, so callers latch and stop touching it.
|
|
1280
|
-
*/
|
|
1281
|
-
export declare function isSqliteCorruptionError(err: unknown): boolean;
|
|
1282
|
-
/**
|
|
1283
|
-
* Default SQLite-backed implementation of {@link AuthCredentialStore}.
|
|
1284
|
-
*
|
|
1285
|
-
* Used by the pi-ai CLI and as the default store for `AuthStorage.create()`.
|
|
1286
|
-
* Also exposes convenience methods (`saveOAuth`, `getOAuth`, `saveApiKey`,
|
|
1287
|
-
* `getApiKey`, `listProviders`, `deleteProvider`) that callers can use directly
|
|
1288
|
-
* without going through `AuthStorage`.
|
|
1289
|
-
*/
|
|
1290
|
-
export declare class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
1291
|
-
#private;
|
|
1292
|
-
constructor(db: Database);
|
|
1293
|
-
static open(dbPath?: string): Promise<SqliteAuthCredentialStore>;
|
|
1294
|
-
listAuthCredentials(provider?: string): StoredAuthCredential[];
|
|
1295
|
-
listDisabledCredentials(provider?: string): Promise<DisabledCredentialSummary[]>;
|
|
1296
|
-
replaceAuthCredentialsForProvider(provider: string, credentials: AuthCredential[]): StoredAuthCredential[];
|
|
1297
|
-
upsertAuthCredentialForProvider(provider: string, credential: AuthCredential): StoredAuthCredential[];
|
|
1298
|
-
updateAuthCredential(id: number, credential: AuthCredential): void;
|
|
1299
|
-
tryUpdateAuthCredentialIfMatches(id: number, expectedData: string, credential: AuthCredential, lease?: CredentialRefreshLeaseFence): boolean;
|
|
1300
|
-
deleteAuthCredential(id: number, disabledCause: string): void;
|
|
1301
|
-
/**
|
|
1302
|
-
* CAS-style disable: only soft-deletes the row when its `data` column still
|
|
1303
|
-
* matches `expectedData` and the row has not already been disabled. Used by
|
|
1304
|
-
* the OAuth refresh-failure path to avoid clobbering a peer that rotated the
|
|
1305
|
-
* row between our pre-check and the disable.
|
|
1306
|
-
*/
|
|
1307
|
-
tryDisableAuthCredentialIfMatches(id: number, expectedData: string, disabledCause: string, lease?: CredentialRefreshLeaseFence): boolean;
|
|
1308
|
-
deleteAuthCredentialsForProvider(provider: string, disabledCause: string): void;
|
|
1309
|
-
getCache(key: string, options?: {
|
|
1310
|
-
includeExpired?: boolean;
|
|
1311
|
-
}): string | null;
|
|
1312
|
-
setCache(key: string, value: string, expiresAtSec: number): void;
|
|
1313
|
-
/** Drop all cache rows whose keys start with the supplied prefix. */
|
|
1314
|
-
deleteCachePrefix(prefix: string): void;
|
|
1315
|
-
cleanExpiredCache(): void;
|
|
1316
|
-
getCredentialBlock(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
1317
|
-
getCredentialBlockReconcileAfter(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
1318
|
-
upsertCredentialBlock(block: StoredCredentialBlock): void;
|
|
1319
|
-
deleteCredentialBlock(credentialId: number, providerKey: string, blockScope: string): void;
|
|
1320
|
-
deleteCredentialBlocks(credentialId: number): void;
|
|
1321
|
-
cleanExpiredCredentialBlocks(nowMs: number): void;
|
|
1322
|
-
listCredentialBlocks(credentialIds: readonly number[]): StoredCredentialBlock[];
|
|
1323
|
-
tryAcquireCredentialRefreshLease(credentialId: number, owner: string, expiresAtMs: number): boolean;
|
|
1324
|
-
getCredentialRefreshLeaseExpiresAt(credentialId: number): number | undefined;
|
|
1325
|
-
renewCredentialRefreshLease(credentialId: number, owner: string, expiresAtMs: number): boolean;
|
|
1326
|
-
releaseCredentialRefreshLease(credentialId: number, owner: string): void;
|
|
1327
|
-
recordUsageSnapshots(entries: UsageHistoryEntry[]): void;
|
|
1328
|
-
listUsageHistory(query?: UsageHistoryQuery): UsageHistoryEntry[];
|
|
1329
|
-
recordUsageCosts(entries: UsageCostHistoryEntry[]): void;
|
|
1330
|
-
listUsageCosts(query?: UsageCostHistoryQuery): UsageCostHistoryEntry[];
|
|
1331
|
-
recordClientUsage(report: ClientUsageReport): void;
|
|
1332
|
-
getClientUsageSummary(sinceMs: number): ClientUsageSummary;
|
|
1333
|
-
/**
|
|
1334
|
-
* Save OAuth credentials for a provider.
|
|
1335
|
-
* Preserves unrelated identities and replaces only the matching credential.
|
|
1336
|
-
*/
|
|
1337
|
-
saveOAuth(provider: string, credentials: OAuthCredentials): void;
|
|
1338
|
-
/**
|
|
1339
|
-
* Get OAuth credentials for a provider.
|
|
1340
|
-
*/
|
|
1341
|
-
getOAuth(provider: string): OAuthCredentials | null;
|
|
1342
|
-
/**
|
|
1343
|
-
* Save API key for a provider (replaces existing).
|
|
1344
|
-
*/
|
|
1345
|
-
saveApiKey(provider: string, apiKey: string): void;
|
|
1346
|
-
/**
|
|
1347
|
-
* Get API key for a provider.
|
|
1348
|
-
*/
|
|
1349
|
-
getApiKey(provider: string): string | null;
|
|
1350
|
-
/**
|
|
1351
|
-
* List all providers with credentials.
|
|
1352
|
-
*/
|
|
1353
|
-
listProviders(): string[];
|
|
1354
|
-
/**
|
|
1355
|
-
* Delete all credentials for a provider.
|
|
1356
|
-
*/
|
|
1357
|
-
deleteProvider(provider: string): void;
|
|
1358
|
-
/**
|
|
1359
|
-
* SQLite increments `data_version` when another connection commits. Own
|
|
1360
|
-
* writes leave it unchanged and already notify AuthStorage directly.
|
|
1361
|
-
*/
|
|
1362
|
-
pollExternalChanges(): boolean;
|
|
1363
|
-
acknowledgeLocalChanges(): void;
|
|
1364
|
-
close(): void;
|
|
1365
|
-
}
|
|
@@ -65,8 +65,10 @@ export declare function isGrammarError(error: unknown): boolean;
|
|
|
65
65
|
*/
|
|
66
66
|
export declare function isFastModeUnsupported(error: unknown): boolean;
|
|
67
67
|
/**
|
|
68
|
-
* GitHub Copilot 400
|
|
69
|
-
*
|
|
68
|
+
* GitHub Copilot 400 rejecting a model its own `/models` catalog advertises —
|
|
69
|
+
* transient fleet skew, not a malformed request. Reads the structural `code`
|
|
70
|
+
* through the SDK/body envelopes, then falls back to the stringified body both
|
|
71
|
+
* SDK families put in `message` (shapes drift; the wire text does not).
|
|
70
72
|
*/
|
|
71
73
|
export declare function isCopilotTransientModelError(error: unknown): boolean;
|
|
72
74
|
export declare function classifyMessage(message: {
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Rate limit reason classification and backoff calculation utilities.
|
|
3
3
|
* Ported from opencode-antigravity-auth plugin for consistency.
|
|
4
4
|
*/
|
|
5
|
-
export type RateLimitReason = "QUOTA_EXHAUSTED" | "RATE_LIMIT_EXCEEDED" | "MODEL_CAPACITY_EXHAUSTED" | "SERVER_ERROR" | "UNKNOWN";
|
|
5
|
+
export type RateLimitReason = "QUOTA_EXHAUSTED" | "RATE_LIMIT_EXCEEDED" | "CONCURRENT_LIMIT" | "MODEL_CAPACITY_EXHAUSTED" | "SERVER_ERROR" | "UNKNOWN";
|
|
6
6
|
/**
|
|
7
7
|
* Classify a rate-limit error message into a reason category.
|
|
8
8
|
* Priority order: explicit details in a resource-exhausted error > QUOTA
|
|
9
|
-
* (Antigravity "quota will reset") >
|
|
10
|
-
* RATE_LIMIT > QUOTA (generic) > SERVER_ERROR > bare resource-exhausted > UNKNOWN.
|
|
9
|
+
* (Antigravity "quota will reset") > CONCURRENT_LIMIT > MODEL_CAPACITY >
|
|
10
|
+
* QUOTA (account) > RATE_LIMIT > QUOTA (generic) > SERVER_ERROR > bare resource-exhausted > UNKNOWN.
|
|
11
11
|
*
|
|
12
12
|
* Bare "resource exhausted" / "resource_exhausted" maps to MODEL_CAPACITY (transient, short wait).
|
|
13
13
|
* Explicit details such as "quota exceeded" retain their normal classification.
|
|
@@ -39,8 +39,10 @@ export declare function isUsageLimitStatus(status: number | undefined): boolean;
|
|
|
39
39
|
* 3. Body is absent or {@link isOpaqueStatusBody opaque} (just the status,
|
|
40
40
|
* empty JSON, HTTP framing only) → rotate conservatively: the server
|
|
41
41
|
* gave us nothing else to go on.
|
|
42
|
-
* 4. Body has content → defer to {@link parseRateLimitReason}.
|
|
43
|
-
*
|
|
42
|
+
* 4. Body has content → defer to {@link parseRateLimitReason}. `QUOTA_EXHAUSTED`
|
|
43
|
+
* rotates; for the categorical 402 billing cap a `CONCURRENT_LIMIT` body
|
|
44
|
+
* also rotates (the cap is concurrent-worded but the status is still an
|
|
45
|
+
* exhausted billing cap). `RATE_LIMIT_EXCEEDED` (`Too many requests`,
|
|
44
46
|
* per-minute caps), `MODEL_CAPACITY_EXHAUSTED` (`Service overloaded`),
|
|
45
47
|
* `SERVER_ERROR`, and `UNKNOWN` (`Please retry in 5s`) stay in the
|
|
46
48
|
* provider's own backoff layer so transient 429s don't burn sibling
|
|
@@ -62,3 +64,17 @@ export declare function isOpaqueStatusBody(message: string): boolean;
|
|
|
62
64
|
* {@link isUsageLimitOutcome} uses it for the account-rotation decision.
|
|
63
65
|
*/
|
|
64
66
|
export declare function matchesUsageLimitText(errorMessage: string): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Account-scoped cap phrasing delivered on a 403 (or a statusless Connect
|
|
69
|
+
* trailer): "Reached overall message rate limit", "Your limit will reset in …".
|
|
70
|
+
* Kept separate from {@link matchesUsageLimitText} because the bare wording is
|
|
71
|
+
* ambiguous without the 403 / statusless-account context; consumed by both
|
|
72
|
+
* {@link isUsageLimitOutcome} (rotation decision) and `flags.ts` (Flag.UsageLimit).
|
|
73
|
+
*/
|
|
74
|
+
export declare function isAccountScopedCapText(message: string): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* A concurrency cap on a non-billing status is shed-and-backoff, not
|
|
77
|
+
* credential-rotatable. This mirrors the exclusion in {@link isUsageLimitOutcome}
|
|
78
|
+
* for the 403 auth-retry entry points. A 402 remains a categorical billing cap.
|
|
79
|
+
*/
|
|
80
|
+
export declare function isConcurrencyCapExclusion(status: number | undefined, message: string | undefined): boolean;
|
|
@@ -11,7 +11,7 @@ export declare function isTransientStatus(status: number | undefined): boolean;
|
|
|
11
11
|
export interface ProviderRetryableHooks {
|
|
12
12
|
/** Provider id of the failing request, used to gate provider-specific checks. */
|
|
13
13
|
provider?: string;
|
|
14
|
-
/** Provider-specific transient predicate (e.g. Copilot
|
|
14
|
+
/** Provider-specific transient predicate (e.g. Copilot model-availability 400s). */
|
|
15
15
|
isProviderTransient?: (error: Error) => boolean;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
@@ -188,8 +188,8 @@ export declare function resolveAnthropicCustomHeadersForBaseUrl(baseUrl: string
|
|
|
188
188
|
/**
|
|
189
189
|
* Whether an Anthropic (or Copilot-over-Anthropic) stream error should be
|
|
190
190
|
* retried. The classification lives in {@link AIError.isProviderRetryableError};
|
|
191
|
-
* this wrapper injects the Copilot-specific
|
|
192
|
-
*
|
|
191
|
+
* this wrapper injects the Copilot-specific model-availability transient check,
|
|
192
|
+
* which the error module must not import directly.
|
|
193
193
|
*/
|
|
194
194
|
export declare function isProviderRetryableError(error: unknown, provider?: string): boolean;
|
|
195
195
|
export type AnthropicUsageLike = {
|
|
@@ -17,7 +17,7 @@ import type { ToolResultMessage } from "../../types.js";
|
|
|
17
17
|
* virtual registry. Re-exported here because this is where the frame builders
|
|
18
18
|
* and their translation are consumed together.
|
|
19
19
|
*/
|
|
20
|
-
export { piEscapeRegexLiteral, piGrepSkip, piJoinPath, piLimit, piLsPath, piReadDisplayPath, piReadPath, piTimeout, } from "../cursor-pi-args.js";
|
|
20
|
+
export { piEscapeRegexLiteral, piGrepSkip, piJoinPath, piLimit, piLsPath, piReadDisplayPath, piReadPath, piReadPathHasRange, piTimeout, } from "../cursor-pi-args.js";
|
|
21
21
|
/** Flatten a tool result's content into the single `output` string the Pi frames carry. */
|
|
22
22
|
export declare function piOutputText(toolResult: ToolResultMessage): string;
|
|
23
23
|
/**
|
|
@@ -36,6 +36,12 @@
|
|
|
36
36
|
* A range-free read keeps the ordinary form — whole-file reads want them.
|
|
37
37
|
*/
|
|
38
38
|
export declare function piReadPath(readPath: string, offset?: number, limit?: number): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Whether a read path ends in an OMP line selector, including compound `raw`
|
|
41
|
+
* forms. Cursor uses this only to describe the operation already executed by
|
|
42
|
+
* the coding-agent read tool; the selector remains embedded in the path.
|
|
43
|
+
*/
|
|
44
|
+
export declare function piReadPathHasRange(readPath: string): boolean;
|
|
39
45
|
/**
|
|
40
46
|
* The same range as {@link piReadPath}, rendered for a transcript block rather
|
|
41
47
|
* than for execution.
|
|
@@ -14,7 +14,7 @@ export interface CodexRequestOptions {
|
|
|
14
14
|
/** User-facing effort; maps 1:1 onto the wire tier of the same name. */
|
|
15
15
|
reasoningEffort?: CodexCallerEffort | "none";
|
|
16
16
|
reasoningSummary?: ReasoningConfig["summary"] | null;
|
|
17
|
-
/** Explicit `reasoning.context` override
|
|
17
|
+
/** Explicit `reasoning.context` override. Omitted by default; Responses Lite forces `all_turns` as required by that transport. */
|
|
18
18
|
reasoningContext?: CodexReasoningContext;
|
|
19
19
|
textVerbosity?: "low" | "medium" | "high";
|
|
20
20
|
include?: string[];
|
|
@@ -4,7 +4,7 @@ import type { ResponseInput } from "./openai-responses-wire.js";
|
|
|
4
4
|
export interface OpenAICodexResponsesOptions extends StreamOptions {
|
|
5
5
|
reasoning?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
6
6
|
reasoningSummary?: "auto" | "concise" | "detailed" | null;
|
|
7
|
-
/** `reasoning.context` replay scope
|
|
7
|
+
/** Explicit `reasoning.context` replay scope. Omitted by default so Codex applies its native request policy. */
|
|
8
8
|
reasoningContext?: CodexReasoningContext;
|
|
9
9
|
textVerbosity?: "low" | "medium" | "high";
|
|
10
10
|
codexMode?: boolean;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Where the OpenAI / Anthropic / Responses route modules translate foreign
|
|
5
5
|
* wire shapes through pi-ai's canonical {@link Context}, this module accepts
|
|
6
6
|
* the canonical shape *directly* — for clients that already speak pi-ai
|
|
7
|
-
* (containerized omp,
|
|
7
|
+
* (containerized omp, robomp's sidecar auth-gateway).
|
|
8
8
|
* Skipping the wire-format → Context → wire-format round-trip cuts
|
|
9
9
|
* per-request CPU but, more importantly, avoids the quantization that those
|
|
10
10
|
* translations impose on first-class pi-ai fields (service tier, cache
|
|
@@ -38,10 +38,11 @@ export declare function finalizeErrorMessage(error: unknown, rawRequestDump: Raw
|
|
|
38
38
|
* Rewrite error message for GitHub Copilot request failures.
|
|
39
39
|
* Must run AFTER finalizeErrorMessage since it replaces the message entirely.
|
|
40
40
|
*
|
|
41
|
-
* 400
|
|
42
|
-
*
|
|
43
|
-
* 400 because only
|
|
44
|
-
* in
|
|
41
|
+
* 400 model-unavailable = Copilot fleet skew. A model that `/models` advertises
|
|
42
|
+
* (claude-sonnet-4.6, claude-opus-4.6, gpt-5.4, gpt-5.3-codex, ...)
|
|
43
|
+
* flaps between 200 and 400 because only part of Copilot's fleet has it
|
|
44
|
+
* in the integrator allowlist. After the in-request retry exhausts,
|
|
45
|
+
* surface guidance rather than the raw error.
|
|
45
46
|
* 401 = token invalid/expired → credential removal is safe, prompt re-login.
|
|
46
47
|
* 403 = token valid but access denied (plan, model policy, org restriction) →
|
|
47
48
|
* do NOT reuse the auth-failed string (which triggers credential removal).
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { isCopilotTransientModelError } from "../error/flags.js";
|
|
2
2
|
export { isCopilotTransientModelError };
|
|
3
3
|
/**
|
|
4
|
-
* Wrap an initial Copilot request so transient
|
|
5
|
-
*
|
|
4
|
+
* Wrap an initial Copilot request so transient model-availability 400s
|
|
5
|
+
* (`model_not_supported`, `model_not_available_for_integrator`) are retried a
|
|
6
|
+
* small number of times. No-op for non-Copilot providers.
|
|
6
7
|
*
|
|
7
8
|
* The callback **MUST** create a fresh in-flight request each invocation — a
|
|
8
9
|
* once-consumed AsyncIterable cannot be re-iterated.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-ai",
|
|
4
|
-
"version": "17.2.
|
|
4
|
+
"version": "17.2.9",
|
|
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,10 +38,10 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@bufbuild/protobuf": "^2.12.1",
|
|
41
|
-
"@oh-my-pi/omptype": "17.2.
|
|
42
|
-
"@oh-my-pi/pi-catalog": "17.2.
|
|
43
|
-
"@oh-my-pi/pi-utils": "17.2.
|
|
44
|
-
"@oh-my-pi/pi-wire": "17.2.
|
|
41
|
+
"@oh-my-pi/omptype": "17.2.9",
|
|
42
|
+
"@oh-my-pi/pi-catalog": "17.2.9",
|
|
43
|
+
"@oh-my-pi/pi-utils": "17.2.9",
|
|
44
|
+
"@oh-my-pi/pi-wire": "17.2.9",
|
|
45
45
|
"zod": "^4"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|