@oh-my-pi/pi-ai 18.12.2 → 18.12.4
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 +44 -78
- package/dist/types/auth/rank.d.ts +4 -0
- package/dist/types/auth/types.d.ts +2 -0
- package/dist/types/auth-broker/remote-store.d.ts +16 -6
- package/dist/types/providers/amazon-bedrock.d.ts +7 -5
- package/dist/types/providers/anthropic-compaction.d.ts +8 -0
- package/dist/types/providers/anthropic-state.d.ts +8 -2
- package/dist/types/providers/anthropic-wire.d.ts +3 -0
- package/dist/types/providers/anthropic.d.ts +19 -2
- package/dist/types/providers/factory-droid/tool-names.d.ts +21 -0
- package/dist/types/providers/google-shared.d.ts +17 -0
- package/dist/types/types.d.ts +2 -0
- package/dist/types/utils/http-inspector.d.ts +2 -0
- package/dist/types/utils/tool-call-loop-guard.d.ts +2 -0
- package/dist/types/utils/validation.d.ts +11 -1
- package/package.json +6 -6
- package/src/auth/policy.ts +26 -10
- package/src/auth/rank.ts +6 -0
- package/src/auth/select.ts +25 -6
- package/src/auth/types.ts +2 -0
- package/src/auth-broker/discover.ts +67 -17
- package/src/auth-broker/remote-store.ts +59 -16
- package/src/error/flags.ts +2 -2
- package/src/images/google-antigravity.ts +0 -1
- package/src/providers/amazon-bedrock.ts +421 -142
- package/src/providers/anthropic-compaction.ts +13 -0
- package/src/providers/anthropic-state.ts +16 -2
- package/src/providers/anthropic-wire.ts +9 -1
- package/src/providers/anthropic.ts +271 -74
- package/src/providers/cursor-pi-args.ts +3 -1
- package/src/providers/devin.ts +13 -43
- package/src/providers/factory-droid/gemini.ts +9 -30
- package/src/providers/factory-droid/tool-names.ts +44 -0
- package/src/providers/factory-droid.ts +91 -20
- package/src/providers/google-gemini-cli.ts +0 -1
- package/src/providers/google-shared.ts +61 -6
- package/src/types.ts +2 -0
- package/src/usage/devin.ts +3 -3
- package/src/utils/http-inspector.ts +3 -2
- package/src/utils/leaked-thinking-stream.ts +28 -4
- package/src/utils/request-debug.ts +3 -2
- package/src/utils/schema/CONSTRAINTS.md +12 -5
- package/src/utils/schema/allowlist.ts +144 -18
- package/src/utils/thinking-loop.ts +75 -14
- package/src/utils/tool-call-loop-guard.ts +14 -2
- package/src/utils/validation.ts +47 -7
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
} from "../auth-storage";
|
|
28
28
|
import * as AIError from "../error";
|
|
29
29
|
import { AuthBrokerClient, AuthBrokerError } from "./client";
|
|
30
|
-
import { type AuthBrokerAccountPool, RemoteAuthCredentialStore } from "./remote-store";
|
|
30
|
+
import { type AuthBrokerAccountPool, type AuthBrokerProviderPool, RemoteAuthCredentialStore } from "./remote-store";
|
|
31
31
|
import { readAuthBrokerSnapshotCache, scheduleAuthBrokerSnapshotCacheWrite } from "./snapshot-cache";
|
|
32
32
|
import { DEFAULT_SNAPSHOT_CACHE_TTL_MS, type SnapshotResponse } from "./types";
|
|
33
33
|
|
|
@@ -122,7 +122,12 @@ function parseAuthAccountPolicies(value: unknown): AuthAccountPolicies {
|
|
|
122
122
|
}
|
|
123
123
|
const policy = entry as Record<string, unknown>;
|
|
124
124
|
const unknownPolicyFields = Object.keys(policy).filter(
|
|
125
|
-
key =>
|
|
125
|
+
key =>
|
|
126
|
+
key !== "provider" &&
|
|
127
|
+
key !== "account" &&
|
|
128
|
+
key !== "priority" &&
|
|
129
|
+
key !== "reservePct" &&
|
|
130
|
+
key !== "reclaimAbovePct",
|
|
126
131
|
);
|
|
127
132
|
if (unknownPolicyFields.length > 0) {
|
|
128
133
|
throw new AIError.ConfigurationError(`${path} has unknown fields: ${unknownPolicyFields.join(", ")}`);
|
|
@@ -171,6 +176,12 @@ function parseAuthAccountPolicies(value: unknown): AuthAccountPolicies {
|
|
|
171
176
|
) {
|
|
172
177
|
throw new AIError.ConfigurationError(`${path}.reservePct must be between 0 and 100`);
|
|
173
178
|
}
|
|
179
|
+
if (
|
|
180
|
+
policy.reclaimAbovePct !== undefined &&
|
|
181
|
+
(typeof policy.reclaimAbovePct !== "number" || !Number.isFinite(policy.reclaimAbovePct))
|
|
182
|
+
) {
|
|
183
|
+
throw new AIError.ConfigurationError(`${path}.reclaimAbovePct must be a finite number`);
|
|
184
|
+
}
|
|
174
185
|
|
|
175
186
|
return {
|
|
176
187
|
provider,
|
|
@@ -182,6 +193,7 @@ function parseAuthAccountPolicies(value: unknown): AuthAccountPolicies {
|
|
|
182
193
|
},
|
|
183
194
|
...(typeof policy.priority === "number" ? { priority: policy.priority } : {}),
|
|
184
195
|
...(typeof policy.reservePct === "number" ? { reservePct: policy.reservePct } : {}),
|
|
196
|
+
...(typeof policy.reclaimAbovePct === "number" ? { reclaimAbovePct: policy.reclaimAbovePct } : {}),
|
|
185
197
|
};
|
|
186
198
|
});
|
|
187
199
|
}
|
|
@@ -272,7 +284,7 @@ export async function loadAuthBrokerAccountPool(): Promise<AuthBrokerAccountPool
|
|
|
272
284
|
throw new AIError.ConfigurationError("OMP_AUTH_BROKER_ACCOUNT_POOL_FILE must contain a JSON object");
|
|
273
285
|
}
|
|
274
286
|
|
|
275
|
-
const accountPool = new Map<string, ReadonlySet<string
|
|
287
|
+
const accountPool = new Map<string, ReadonlySet<string> | AuthBrokerProviderPool>();
|
|
276
288
|
for (const [provider, value] of Object.entries(parsed)) {
|
|
277
289
|
const normalizedProvider = provider.trim();
|
|
278
290
|
if (normalizedProvider.length === 0) {
|
|
@@ -283,28 +295,66 @@ export async function loadAuthBrokerAccountPool(): Promise<AuthBrokerAccountPool
|
|
|
283
295
|
"OMP_AUTH_BROKER_ACCOUNT_POOL_FILE contains a provider id with surrounding whitespace",
|
|
284
296
|
);
|
|
285
297
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
298
|
+
accountPool.set(provider, parseAccountPoolEntry(provider, value));
|
|
299
|
+
}
|
|
300
|
+
return accountPool;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* One pool-file entry: an identity array (visible OAuth accounts, API keys
|
|
305
|
+
* kept), `true` (everything visible), `false` (nothing visible), or a rule
|
|
306
|
+
* object `{ "accounts"?: [...], "apiKeys"?: boolean }`.
|
|
307
|
+
*/
|
|
308
|
+
function parseAccountPoolEntry(provider: string, value: unknown): ReadonlySet<string> | AuthBrokerProviderPool {
|
|
309
|
+
if (Array.isArray(value)) return parseAccountPoolIdentities(provider, value);
|
|
310
|
+
if (value === true) return {};
|
|
311
|
+
if (value === false) return { accounts: new Set(), apiKeys: false };
|
|
312
|
+
if (value === null || typeof value !== "object") {
|
|
313
|
+
throw new AIError.ConfigurationError(
|
|
314
|
+
`OMP_AUTH_BROKER_ACCOUNT_POOL_FILE entry for ${provider} must be an array of identity keys, a boolean, or an object with "accounts"/"apiKeys"`,
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
const rule: { accounts?: ReadonlySet<string>; apiKeys?: boolean } = {};
|
|
318
|
+
for (const [key, field] of Object.entries(value)) {
|
|
319
|
+
if (key === "accounts") {
|
|
320
|
+
if (!Array.isArray(field)) {
|
|
294
321
|
throw new AIError.ConfigurationError(
|
|
295
|
-
`OMP_AUTH_BROKER_ACCOUNT_POOL_FILE entry for ${provider}
|
|
322
|
+
`OMP_AUTH_BROKER_ACCOUNT_POOL_FILE entry for ${provider} has "accounts" that is not an array of identity keys`,
|
|
296
323
|
);
|
|
297
324
|
}
|
|
298
|
-
|
|
325
|
+
rule.accounts = parseAccountPoolIdentities(provider, field);
|
|
326
|
+
} else if (key === "apiKeys") {
|
|
327
|
+
if (typeof field !== "boolean") {
|
|
299
328
|
throw new AIError.ConfigurationError(
|
|
300
|
-
`OMP_AUTH_BROKER_ACCOUNT_POOL_FILE entry for ${provider}
|
|
329
|
+
`OMP_AUTH_BROKER_ACCOUNT_POOL_FILE entry for ${provider} has "apiKeys" that is not a boolean`,
|
|
301
330
|
);
|
|
302
331
|
}
|
|
303
|
-
|
|
332
|
+
rule.apiKeys = field;
|
|
333
|
+
} else {
|
|
334
|
+
throw new AIError.ConfigurationError(
|
|
335
|
+
`OMP_AUTH_BROKER_ACCOUNT_POOL_FILE entry for ${provider} has unknown field ${JSON.stringify(key)}`,
|
|
336
|
+
);
|
|
304
337
|
}
|
|
305
|
-
accountPool.set(provider, identities);
|
|
306
338
|
}
|
|
307
|
-
return
|
|
339
|
+
return rule;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function parseAccountPoolIdentities(provider: string, value: unknown[]): Set<string> {
|
|
343
|
+
const identities = new Set<string>();
|
|
344
|
+
for (const identity of value) {
|
|
345
|
+
if (typeof identity !== "string" || identity.length === 0) {
|
|
346
|
+
throw new AIError.ConfigurationError(
|
|
347
|
+
`OMP_AUTH_BROKER_ACCOUNT_POOL_FILE entry for ${provider} contains an invalid identity key`,
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
if (identity !== identity.trim()) {
|
|
351
|
+
throw new AIError.ConfigurationError(
|
|
352
|
+
`OMP_AUTH_BROKER_ACCOUNT_POOL_FILE entry for ${provider} contains an identity key with surrounding whitespace`,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
identities.add(identity);
|
|
356
|
+
}
|
|
357
|
+
return identities;
|
|
308
358
|
}
|
|
309
359
|
|
|
310
360
|
function resolveSnapshotTtlMs(): number {
|
|
@@ -41,21 +41,61 @@ import type {
|
|
|
41
41
|
const REMOTE_CACHE_MAX_ENTRIES = 4096;
|
|
42
42
|
const REMOTE_CACHE_MAX_CHARACTERS = 4 * 1024 * 1024;
|
|
43
43
|
|
|
44
|
+
/** Visibility rule for one provider's broker credentials. */
|
|
45
|
+
export interface AuthBrokerProviderPool {
|
|
46
|
+
/** OAuth `identityKey`s visible to this client; omitted = every OAuth account. */
|
|
47
|
+
readonly accounts?: ReadonlySet<string>;
|
|
48
|
+
/** Whether API-key credentials are visible. Default `true`. */
|
|
49
|
+
readonly apiKeys?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Pool key whose rule applies to every provider without its own entry. */
|
|
53
|
+
const AUTH_BROKER_ACCOUNT_POOL_DEFAULT_KEY = "*";
|
|
54
|
+
|
|
44
55
|
/**
|
|
45
|
-
* Per-provider
|
|
46
|
-
*
|
|
47
|
-
* OAuth
|
|
56
|
+
* Per-provider credential visibility for this trusted broker client. A set
|
|
57
|
+
* value lists the provider's visible OAuth identities and keeps its API keys
|
|
58
|
+
* (an empty set excludes every OAuth credential); a rule object can also hide
|
|
59
|
+
* API keys. Providers without an entry follow the `"*"` rule, which defaults
|
|
60
|
+
* to unrestricted.
|
|
48
61
|
*/
|
|
49
|
-
export type AuthBrokerAccountPool = ReadonlyMap<string, ReadonlySet<string
|
|
62
|
+
export type AuthBrokerAccountPool = ReadonlyMap<string, ReadonlySet<string> | AuthBrokerProviderPool>;
|
|
63
|
+
|
|
64
|
+
interface ResolvedProviderPool {
|
|
65
|
+
accounts: ReadonlySet<string> | undefined;
|
|
66
|
+
apiKeys: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Copies `pool` so later caller mutation cannot widen it, normalizing both value forms. */
|
|
70
|
+
function resolveAccountPool(pool: AuthBrokerAccountPool): ReadonlyMap<string, ResolvedProviderPool> {
|
|
71
|
+
const resolved = new Map<string, ResolvedProviderPool>();
|
|
72
|
+
for (const [provider, value] of pool) {
|
|
73
|
+
resolved.set(
|
|
74
|
+
provider,
|
|
75
|
+
"has" in value
|
|
76
|
+
? { accounts: new Set(value), apiKeys: true }
|
|
77
|
+
: { accounts: value.accounts && new Set(value.accounts), apiKeys: value.apiKeys ?? true },
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return resolved;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function providerPoolRule(
|
|
84
|
+
accountPool: ReadonlyMap<string, ResolvedProviderPool> | undefined,
|
|
85
|
+
provider: string,
|
|
86
|
+
): ResolvedProviderPool | undefined {
|
|
87
|
+
return accountPool?.get(provider) ?? accountPool?.get(AUTH_BROKER_ACCOUNT_POOL_DEFAULT_KEY);
|
|
88
|
+
}
|
|
50
89
|
|
|
51
90
|
function isCredentialInAccountPool(
|
|
52
91
|
entry: Pick<SnapshotEntry, "provider" | "credential" | "identityKey">,
|
|
53
|
-
accountPool:
|
|
92
|
+
accountPool: ReadonlyMap<string, ResolvedProviderPool> | undefined,
|
|
54
93
|
): boolean {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (
|
|
58
|
-
|
|
94
|
+
const rule = providerPoolRule(accountPool, entry.provider);
|
|
95
|
+
if (!rule) return true;
|
|
96
|
+
if (entry.credential.type !== "oauth") return rule.apiKeys;
|
|
97
|
+
if (!rule.accounts) return true;
|
|
98
|
+
return entry.identityKey !== null && rule.accounts.has(entry.identityKey);
|
|
59
99
|
}
|
|
60
100
|
|
|
61
101
|
/**
|
|
@@ -245,8 +285,9 @@ export interface RemoteAuthCredentialStoreOptions {
|
|
|
245
285
|
*/
|
|
246
286
|
onSnapshot?: (snapshot: SnapshotResponse, generation: number) => void;
|
|
247
287
|
/**
|
|
248
|
-
*
|
|
249
|
-
* routing policy, not broker
|
|
288
|
+
* Broker credentials visible through this store (OAuth identities and API
|
|
289
|
+
* keys per provider). This is a trusted-client routing policy, not broker
|
|
290
|
+
* authorization.
|
|
250
291
|
*/
|
|
251
292
|
accountPool?: AuthBrokerAccountPool;
|
|
252
293
|
/** Flush cadence for batched observed-usage reports. Default 10s. */
|
|
@@ -265,7 +306,7 @@ export class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
265
306
|
readonly #client: AuthBrokerClient;
|
|
266
307
|
readonly #streamSnapshots: boolean;
|
|
267
308
|
readonly #onSnapshot?: (snapshot: SnapshotResponse, generation: number) => void;
|
|
268
|
-
readonly #accountPool?:
|
|
309
|
+
readonly #accountPool?: ReadonlyMap<string, ResolvedProviderPool>;
|
|
269
310
|
#snapshot: SnapshotResponse = emptySnapshot();
|
|
270
311
|
#snapshotReceivedAt = Date.now();
|
|
271
312
|
#generation = 0;
|
|
@@ -336,9 +377,7 @@ export class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
336
377
|
this.#streamSnapshots = opts.streamSnapshots ?? true;
|
|
337
378
|
this.#observedUsageFlushMs = opts.observedUsageFlushMs ?? 10_000;
|
|
338
379
|
this.#backgroundIdleMs = opts.backgroundIdleMs ?? BACKGROUND_IDLE_MS;
|
|
339
|
-
this.#accountPool = opts.accountPool
|
|
340
|
-
? new Map([...opts.accountPool].map(([provider, identities]) => [provider, new Set(identities)]))
|
|
341
|
-
: undefined;
|
|
380
|
+
this.#accountPool = opts.accountPool ? resolveAccountPool(opts.accountPool) : undefined;
|
|
342
381
|
this.#applySnapshot(opts.initialSnapshot ?? emptySnapshot(), opts.initialSnapshot?.generation ?? 0);
|
|
343
382
|
this.#acknowledgedRevision = this.#credentialRevision;
|
|
344
383
|
this.#onSnapshot = opts.onSnapshot;
|
|
@@ -1372,7 +1411,11 @@ export class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
1372
1411
|
if (memo && memo.input === reports && memo.byProvider === lookup.byProvider) return memo.output;
|
|
1373
1412
|
const byProvider = lookup.byProvider;
|
|
1374
1413
|
const output = reports.filter(report => {
|
|
1375
|
-
|
|
1414
|
+
// Unrestricted for this provider: every report stays. Otherwise a
|
|
1415
|
+
// report must belong to a visible OAuth credential, so reports from
|
|
1416
|
+
// hidden accounts or API keys fail closed.
|
|
1417
|
+
const rule = providerPoolRule(accountPool, report.provider);
|
|
1418
|
+
if (!rule || (rule.accounts === undefined && rule.apiKeys)) return true;
|
|
1376
1419
|
const credentials = byProvider.get(report.provider);
|
|
1377
1420
|
if (!credentials) return false;
|
|
1378
1421
|
return credentials.some(credential => usageReportMatchesCredential(report, credential));
|
package/src/error/flags.ts
CHANGED
|
@@ -167,7 +167,7 @@ function matchesPayloadRejectionText(text: string): boolean {
|
|
|
167
167
|
|
|
168
168
|
const TIMEOUT_PATTERN = /\b(?:operation\s+)?timed?\s*out\b|\btimeout\b|\bstream stall\b/i;
|
|
169
169
|
const TRANSIENT_ENVELOPE_PATTERN = /anthropic stream envelope error:/i;
|
|
170
|
-
const TRANSIENT_ENVELOPE_TRUNCATION_PATTERN = /before message_(?:start|stop)/i;
|
|
170
|
+
const TRANSIENT_ENVELOPE_TRUNCATION_PATTERN = /before message_(?:start|delta|stop)/i;
|
|
171
171
|
export const STREAM_READ_ERROR_PATTERN = /stream[_ -]?read[_ -]?error/i;
|
|
172
172
|
/** Python h2/httpx diagnostics forwarded through provider or proxy error events. */
|
|
173
173
|
export const PYTHON_HTTP2_STREAM_RESET_PATTERN = /<StreamReset stream_id:\d+, error_code:(?:2|7), remote_reset:True>/;
|
|
@@ -1103,7 +1103,7 @@ export function isTransientStreamParseError(error: unknown): boolean {
|
|
|
1103
1103
|
* non-terminal status treat it as transient (#11805).
|
|
1104
1104
|
*/
|
|
1105
1105
|
const STREAM_DROP_PATTERN =
|
|
1106
|
-
/stream disconnected before completion|(?:stream|websocket) closed before response\.completed|stream was interrupted|stream ended before terminal (?:chunk|completion event)|socket disconnected before secure tls connection/i;
|
|
1106
|
+
/stream disconnected before completion|(?:stream|websocket) closed before response\.completed|stream was interrupted|stream ended before (?:terminal (?:chunk|completion event)|message_(?:start|delta|stop))|socket disconnected before secure tls connection/i;
|
|
1107
1107
|
|
|
1108
1108
|
/**
|
|
1109
1109
|
* Transport stream-drop diagnostic (see {@link STREAM_DROP_PATTERN}). Unlike
|
|
@@ -99,7 +99,6 @@ function buildRequest(request: ImageGenerationRequest, model: string, projectId:
|
|
|
99
99
|
{ category: "HARM_CATEGORY_CIVIC_INTEGRITY", threshold: "BLOCK_ONLY_HIGH" },
|
|
100
100
|
],
|
|
101
101
|
},
|
|
102
|
-
requestType: "agent",
|
|
103
102
|
requestId: `agent-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
|
|
104
103
|
userAgent: "antigravity",
|
|
105
104
|
};
|