@oh-my-pi/pi-ai 16.0.3 → 16.0.5
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 +36 -0
- package/dist/types/auth-broker/wire-schemas.d.ts +14 -0
- package/dist/types/auth-retry.d.ts +9 -0
- package/dist/types/auth-storage.d.ts +3 -0
- package/dist/types/dialect/anthropic.d.ts +7 -1
- package/dist/types/dialect/minimax.d.ts +3 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/providers/google-gemini-cli.d.ts +23 -2
- package/dist/types/providers/openai-completions.d.ts +2 -0
- package/dist/types/registry/alibaba-coding-plan.d.ts +4 -3
- package/dist/types/registry/oauth/github-copilot.d.ts +1 -1
- package/dist/types/registry/oauth/google-oauth-shared.d.ts +0 -6
- package/dist/types/registry/oauth/openai-codex.d.ts +2 -0
- package/dist/types/registry/oauth/types.d.ts +1 -0
- package/dist/types/registry/registry.d.ts +2 -1
- package/dist/types/types.d.ts +8 -0
- package/dist/types/usage.d.ts +1 -0
- package/dist/types/utils/google-validation.d.ts +2 -0
- package/dist/types/utils/thinking-loop.d.ts +45 -0
- package/package.json +3 -3
- package/src/auth-broker/wire-schemas.ts +1 -0
- package/src/auth-retry.ts +19 -0
- package/src/auth-storage.ts +16 -12
- package/src/dialect/anthropic.ts +19 -8
- package/src/dialect/factory.ts +2 -0
- package/src/dialect/minimax.md +31 -0
- package/src/dialect/minimax.ts +95 -0
- package/src/dialect/owned-stream.ts +1 -0
- package/src/index.ts +1 -0
- package/src/providers/github-copilot-headers.ts +2 -1
- package/src/providers/google-gemini-cli.ts +298 -106
- package/src/providers/openai-completions.ts +54 -12
- package/src/providers/openai-responses-shared.ts +2 -2
- package/src/registry/alibaba-coding-plan.ts +55 -10
- package/src/registry/github-copilot.ts +1 -1
- package/src/registry/litellm.ts +2 -1
- package/src/registry/oauth/github-copilot.ts +37 -5
- package/src/registry/oauth/google-oauth-shared.ts +9 -1
- package/src/registry/oauth/index.ts +5 -1
- package/src/registry/oauth/openai-codex.ts +59 -26
- package/src/registry/oauth/types.ts +1 -0
- package/src/stream.ts +19 -3
- package/src/types.ts +9 -0
- package/src/usage/gemini.ts +10 -2
- package/src/usage/google-antigravity.ts +38 -17
- package/src/usage.ts +1 -0
- package/src/utils/google-validation.ts +25 -0
- package/src/utils/thinking-loop.ts +354 -0
- package/src/utils/validation.ts +72 -30
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.0.5] - 2026-06-17
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `antigravityEndpointMode` stream option with `auto`, `production`, and `sandbox` values to control Antigravity endpoint routing
|
|
10
|
+
- Added `seedApiKeyResolver` for reusing a pre-resolved request key while preserving resolver-driven auth retry and credential rotation
|
|
11
|
+
- Added optional `contextSnapshot` property to `AssistantMessage` with token usage metadata via new `ContextSnapshot` interface (`promptTokens`, `nonMessageTokens`, and optional `lastMessageTimestamp`)
|
|
12
|
+
- Added `LITELLM_BASE_URL` guidance to the LiteLLM login prompt so non-default proxy endpoints are discoverable. ([#2726](https://github.com/can1357/oh-my-pi/issues/2726))
|
|
13
|
+
- Added a Gemini thinking-loop guard that watches streamed `thinking` deltas for degenerate reasoning loops — verbatim tail repetition and near-duplicate paragraph cycling — and terminates the stream with a retryable, empty-content `error` message (worded as a transient stream stall) so the turn is discarded and re-sampled instead of committing a runaway transcript. Gated to Gemini models across every transport (OpenRouter, direct Google, Vertex) and disarmed once visible answer text or a tool call starts; disable with `PI_NO_THINKING_LOOP_GUARD=1`.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Changed the Antigravity (`google-antigravity`) request builder to mirror the captured `antigravity/hub` client: gemini-3.x send `thinkingConfig.thinkingBudget` per tier, a fixed per-model `maxOutputTokens`, a default `functionCallingConfig.mode: "VALIDATED"` tool mode (auto/unset tool choice only), a `role: "user"` system instruction, a structured `requestId` (`agent/<id>/<ts>/<trajectoryId>/<step>`), and `labels` (`model_enum`, `trajectory_id`, `last_step_index`, `last_execution_id`, `used_claude*`) tracked across the conversation via provider session state.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Fixed Gemini usage-tier mapping so `gemini-3.5-flash` is treated as `Flash` and `gemini-3.1-pro` plus `gemini-pro-agent` are treated as `Pro` in usage accounting
|
|
22
|
+
- Fixed Antigravity stream state handling so a request’s `last_execution_id` is committed only after a successful completion and cleared between retry attempts
|
|
23
|
+
- Fixed `streamSimple()` Gemini streams to run through the thinking-loop guard for custom API and pi-native transports, so degenerate `thinking` loops now abort with the same retryable empty-content error path as other Gemini stream paths
|
|
24
|
+
- Fixed Antigravity model streaming and usage fetch paths to retry on transient `429`/`5xx` errors by failing over to the alternate endpoint before surfacing an error
|
|
25
|
+
- Fixed Antigravity endpoint tracking to prefer a previously successful endpoint in `auto` mode for subsequent requests
|
|
26
|
+
- Fixed Antigravity and Gemini CLI model requests failing with an opaque error when Google requires account verification. Cloud Code Assist `403 VALIDATION_REQUIRED` responses now surface the `validation_url` and the signed-in account email when available, so users see an actionable account-verification message instead of the raw API error body.
|
|
27
|
+
- Fixed MiniMax M3 in-band tool calls by adding a MiniMax dialect that parses `<minimax:tool_call>` wrappers instead of falling back to generic XML. ([#2759](https://github.com/can1357/oh-my-pi/issues/2759))
|
|
28
|
+
- Fixed GitHub Copilot OAuth for Business seats by storing the login-discovered API endpoint and routing model enablement plus chat requests to that endpoint. ([#2876](https://github.com/can1357/oh-my-pi/issues/2876))
|
|
29
|
+
|
|
30
|
+
## [16.0.4] - 2026-06-17
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Fixed tool argument coercion to parse double-encoded JSON strings, including quoted values like `"300"`, when schema expects a number
|
|
35
|
+
- Fixed object-array coercion to parse JSON object and array strings into proper array arguments instead of wrapping raw strings
|
|
36
|
+
- Fixed handling of malformed JSON container strings for array schema fields so validation now surfaces a top-level `expected array, received string` error rather than nested element errors
|
|
37
|
+
- Fixed ChatGPT/Codex browser login missing connector OAuth scopes and rendering object-shaped token endpoint errors as `[object Object]`. ([#2825](https://github.com/can1357/oh-my-pi/issues/2825))
|
|
38
|
+
- Fixed Zhipu/BigModel GLM-5.2 chat-completions requests so internal `xhigh` effort serializes as provider-native `reasoning_effort: "max"` and tool calls opt into `tool_stream`. ([#2833](https://github.com/can1357/oh-my-pi/issues/2833))
|
|
39
|
+
- Fixed Google Gemini CLI and Antigravity tool calls with `toolChoice: "auto"` serializing an explicit `toolConfig` AUTO mode, which can cause Gemini-3 models to leak raw planning JSON instead of executing tools. ([#2830](https://github.com/can1357/oh-my-pi/issues/2830))
|
|
40
|
+
|
|
5
41
|
## [16.0.3] - 2026-06-16
|
|
6
42
|
|
|
7
43
|
### Added
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import { z } from "zod/v4";
|
|
16
16
|
/** Real OAuth credential (broker-side) — refresh token is the actual upstream value. */
|
|
17
17
|
export declare const oauthCredentialSchema: z.ZodObject<{
|
|
18
|
+
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
18
19
|
type: z.ZodLiteral<"oauth">;
|
|
19
20
|
refresh: z.ZodString;
|
|
20
21
|
access: z.ZodString;
|
|
@@ -26,6 +27,7 @@ export declare const oauthCredentialSchema: z.ZodObject<{
|
|
|
26
27
|
}, z.core.$loose>;
|
|
27
28
|
/** OAuth credential as it appears in broker snapshots — refresh replaced with sentinel. */
|
|
28
29
|
export declare const remoteOauthCredentialSchema: z.ZodObject<{
|
|
30
|
+
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
29
31
|
type: z.ZodLiteral<"oauth">;
|
|
30
32
|
access: z.ZodString;
|
|
31
33
|
expires: z.ZodNumber;
|
|
@@ -41,6 +43,7 @@ export declare const apiKeyCredentialSchema: z.ZodObject<{
|
|
|
41
43
|
}, z.core.$strict>;
|
|
42
44
|
/** Discriminated union accepted on POST /v1/credential (writes). */
|
|
43
45
|
export declare const writableAuthCredentialSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
46
|
+
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
44
47
|
type: z.ZodLiteral<"oauth">;
|
|
45
48
|
refresh: z.ZodString;
|
|
46
49
|
access: z.ZodString;
|
|
@@ -55,6 +58,7 @@ export declare const writableAuthCredentialSchema: z.ZodDiscriminatedUnion<[z.Zo
|
|
|
55
58
|
}, z.core.$strict>], "type">;
|
|
56
59
|
/** Discriminated union returned in snapshots (refresh is sentinel for OAuth). */
|
|
57
60
|
export declare const snapshotCredentialSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
61
|
+
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
58
62
|
type: z.ZodLiteral<"oauth">;
|
|
59
63
|
access: z.ZodString;
|
|
60
64
|
expires: z.ZodNumber;
|
|
@@ -71,6 +75,7 @@ export declare const credentialSnapshotEntrySchema: z.ZodObject<{
|
|
|
71
75
|
id: z.ZodNumber;
|
|
72
76
|
provider: z.ZodString;
|
|
73
77
|
credential: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
78
|
+
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
74
79
|
type: z.ZodLiteral<"oauth">;
|
|
75
80
|
access: z.ZodString;
|
|
76
81
|
expires: z.ZodNumber;
|
|
@@ -89,6 +94,7 @@ export declare const snapshotEntrySchema: z.ZodObject<{
|
|
|
89
94
|
id: z.ZodNumber;
|
|
90
95
|
provider: z.ZodString;
|
|
91
96
|
credential: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
97
|
+
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
92
98
|
type: z.ZodLiteral<"oauth">;
|
|
93
99
|
access: z.ZodString;
|
|
94
100
|
expires: z.ZodNumber;
|
|
@@ -124,6 +130,7 @@ export declare const snapshotResponseSchema: z.ZodObject<{
|
|
|
124
130
|
id: z.ZodNumber;
|
|
125
131
|
provider: z.ZodString;
|
|
126
132
|
credential: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
133
|
+
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
127
134
|
type: z.ZodLiteral<"oauth">;
|
|
128
135
|
access: z.ZodString;
|
|
129
136
|
expires: z.ZodNumber;
|
|
@@ -155,6 +162,7 @@ export declare const snapshotStreamSnapshotEventSchema: z.ZodObject<{
|
|
|
155
162
|
id: z.ZodNumber;
|
|
156
163
|
provider: z.ZodString;
|
|
157
164
|
credential: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
165
|
+
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
158
166
|
type: z.ZodLiteral<"oauth">;
|
|
159
167
|
access: z.ZodString;
|
|
160
168
|
expires: z.ZodNumber;
|
|
@@ -187,6 +195,7 @@ export declare const snapshotStreamEntryEventSchema: z.ZodObject<{
|
|
|
187
195
|
id: z.ZodNumber;
|
|
188
196
|
provider: z.ZodString;
|
|
189
197
|
credential: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
198
|
+
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
190
199
|
type: z.ZodLiteral<"oauth">;
|
|
191
200
|
access: z.ZodString;
|
|
192
201
|
expires: z.ZodNumber;
|
|
@@ -231,6 +240,7 @@ export declare const snapshotStreamEventSchema: z.ZodDiscriminatedUnion<[z.ZodOb
|
|
|
231
240
|
id: z.ZodNumber;
|
|
232
241
|
provider: z.ZodString;
|
|
233
242
|
credential: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
243
|
+
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
234
244
|
type: z.ZodLiteral<"oauth">;
|
|
235
245
|
access: z.ZodString;
|
|
236
246
|
expires: z.ZodNumber;
|
|
@@ -261,6 +271,7 @@ export declare const snapshotStreamEventSchema: z.ZodDiscriminatedUnion<[z.ZodOb
|
|
|
261
271
|
id: z.ZodNumber;
|
|
262
272
|
provider: z.ZodString;
|
|
263
273
|
credential: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
274
|
+
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
264
275
|
type: z.ZodLiteral<"oauth">;
|
|
265
276
|
access: z.ZodString;
|
|
266
277
|
expires: z.ZodNumber;
|
|
@@ -358,6 +369,7 @@ export declare const credentialRefreshResponseSchema: z.ZodObject<{
|
|
|
358
369
|
id: z.ZodNumber;
|
|
359
370
|
provider: z.ZodString;
|
|
360
371
|
credential: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
372
|
+
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
361
373
|
type: z.ZodLiteral<"oauth">;
|
|
362
374
|
access: z.ZodString;
|
|
363
375
|
expires: z.ZodNumber;
|
|
@@ -382,6 +394,7 @@ export declare const credentialDisableResponseSchema: z.ZodObject<{
|
|
|
382
394
|
export declare const credentialUploadRequestSchema: z.ZodObject<{
|
|
383
395
|
provider: z.ZodString;
|
|
384
396
|
credential: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
397
|
+
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
385
398
|
type: z.ZodLiteral<"oauth">;
|
|
386
399
|
refresh: z.ZodString;
|
|
387
400
|
access: z.ZodString;
|
|
@@ -400,6 +413,7 @@ export declare const credentialUploadResponseSchema: z.ZodObject<{
|
|
|
400
413
|
id: z.ZodNumber;
|
|
401
414
|
provider: z.ZodString;
|
|
402
415
|
credential: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
416
|
+
apiEndpoint: z.ZodOptional<z.ZodString>;
|
|
403
417
|
type: z.ZodLiteral<"oauth">;
|
|
404
418
|
access: z.ZodString;
|
|
405
419
|
expires: z.ZodNumber;
|
|
@@ -37,6 +37,15 @@ export declare function isApiKeyResolver(key: ApiKey | undefined): key is ApiKey
|
|
|
37
37
|
* `lastChance: false`). Static keys pass through unchanged.
|
|
38
38
|
*/
|
|
39
39
|
export declare function resolveApiKeyOnce(key: ApiKey | undefined, signal?: AbortSignal): Promise<string | undefined>;
|
|
40
|
+
/**
|
|
41
|
+
* Wraps a resolver with a bearer that was already selected for this request.
|
|
42
|
+
*
|
|
43
|
+
* Callers that preflight credentials can pass the returned resolver to the
|
|
44
|
+
* auth-retry driver without making the driver know about that preflight: the
|
|
45
|
+
* first initial resolution reuses `seed`, and all later resolutions delegate to
|
|
46
|
+
* `resolver`.
|
|
47
|
+
*/
|
|
48
|
+
export declare function seedApiKeyResolver(seed: string | undefined, resolver: ApiKeyResolver): ApiKeyResolver;
|
|
40
49
|
/**
|
|
41
50
|
* Classifies whether an error should trigger a credential refresh/rotation
|
|
42
51
|
* retry: a hard `401`, or a rotatable usage-limit ("usage_limit_reached",
|
|
@@ -139,6 +139,7 @@ export type CompletionProbeCredential = {
|
|
|
139
139
|
projectId?: string;
|
|
140
140
|
email?: string;
|
|
141
141
|
enterpriseUrl?: string;
|
|
142
|
+
apiEndpoint?: string;
|
|
142
143
|
};
|
|
143
144
|
/**
|
|
144
145
|
* Caller-supplied bearer probe. Receives the post-refresh credential for a
|
|
@@ -435,6 +436,7 @@ export interface OAuthAccess {
|
|
|
435
436
|
email?: string;
|
|
436
437
|
projectId?: string;
|
|
437
438
|
enterpriseUrl?: string;
|
|
439
|
+
apiEndpoint?: string;
|
|
438
440
|
}
|
|
439
441
|
export interface OAuthAccessFailure {
|
|
440
442
|
credentialId?: number;
|
|
@@ -442,6 +444,7 @@ export interface OAuthAccessFailure {
|
|
|
442
444
|
email?: string;
|
|
443
445
|
projectId?: string;
|
|
444
446
|
enterpriseUrl?: string;
|
|
447
|
+
apiEndpoint?: string;
|
|
445
448
|
error: string;
|
|
446
449
|
}
|
|
447
450
|
/**
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { DialectDefinition, InbandScanEvent, InbandScanner, InbandScannerOptions } from "./types";
|
|
2
|
+
export declare const ANTHROPIC_THINKING_TAG_PREFIXES: readonly ["<thinking", "</thinking", "<think", "</think", "<scratchpad", "</scratchpad", "<antml:thinking", "</antml:thinking", "<antml:think", "</antml:think", "<antml:scratchpad", "</antml:scratchpad"];
|
|
3
|
+
export interface AnthropicInbandScannerConfig {
|
|
4
|
+
readonly wrapperTags?: Readonly<Record<string, true>>;
|
|
5
|
+
readonly baseTagPrefixes?: readonly string[];
|
|
6
|
+
readonly allTagPrefixes?: readonly string[];
|
|
7
|
+
}
|
|
2
8
|
export declare class AnthropicInbandScanner implements InbandScanner {
|
|
3
9
|
#private;
|
|
4
|
-
constructor(options?: InbandScannerOptions);
|
|
10
|
+
constructor(options?: InbandScannerOptions, config?: AnthropicInbandScannerConfig);
|
|
5
11
|
feed(text: string): InbandScanEvent[];
|
|
6
12
|
flush(): InbandScanEvent[];
|
|
7
13
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ProviderHttpError } from "../errors";
|
|
2
|
-
import type { Context, Model, StreamFunction, StreamOptions } from "../types";
|
|
2
|
+
import type { Context, Model, ProviderSessionState, StreamFunction, StreamOptions } from "../types";
|
|
3
3
|
import type { Content, FunctionCallingConfigMode, ThinkingConfig } from "./google-shared";
|
|
4
4
|
import { type GoogleThinkingLevel } from "./google-shared";
|
|
5
5
|
/**
|
|
@@ -52,13 +52,33 @@ export interface GoogleGeminiCliOptions extends StreamOptions {
|
|
|
52
52
|
*/
|
|
53
53
|
requestModelId?: string;
|
|
54
54
|
projectId?: string;
|
|
55
|
+
/** Antigravity endpoint routing mode: "auto" (default with failover), "production", "sandbox". */
|
|
56
|
+
antigravityEndpointMode?: "auto" | "production" | "sandbox";
|
|
57
|
+
providerSessionState?: Map<string, ProviderSessionState>;
|
|
55
58
|
}
|
|
56
|
-
export
|
|
59
|
+
export interface AntigravityProviderSessionState extends ProviderSessionState {
|
|
60
|
+
lastGoodEndpoint?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Per-conversation request-envelope identity that mirrors the real
|
|
63
|
+
* Antigravity client. `sessionId` is the signed-decimal session id;
|
|
64
|
+
* `agentId`/`trajectoryId` are UUIDs; `stepIndex` is the monotonic step
|
|
65
|
+
* counter; `lastExecutionId` is the prior response id echoed as
|
|
66
|
+
* `labels.last_execution_id`.
|
|
67
|
+
*/
|
|
68
|
+
agentId?: string;
|
|
69
|
+
trajectoryId?: string;
|
|
70
|
+
sessionId?: string;
|
|
71
|
+
stepIndex?: number;
|
|
72
|
+
lastExecutionId?: string;
|
|
73
|
+
}
|
|
74
|
+
export declare function getAntigravityProviderSessionState(providerSessionState: Map<string, ProviderSessionState> | undefined): AntigravityProviderSessionState | undefined;
|
|
75
|
+
export { ANTIGRAVITY_SYSTEM_INSTRUCTION, getAntigravityUserAgent, getGeminiCliHeaders, getGeminiCliUserAgent, } from "@oh-my-pi/pi-catalog/wire/gemini-headers";
|
|
57
76
|
interface ParsedGeminiCliCredentials {
|
|
58
77
|
accessToken: string;
|
|
59
78
|
projectId: string;
|
|
60
79
|
refreshToken?: string;
|
|
61
80
|
expiresAt?: number;
|
|
81
|
+
email?: string;
|
|
62
82
|
}
|
|
63
83
|
export declare function parseGeminiCliCredentials(apiKeyRaw: string): ParsedGeminiCliCredentials;
|
|
64
84
|
export declare function shouldRefreshGeminiCliCredentials(expiresAt: number | undefined, isAntigravity: boolean, nowMs?: number): boolean;
|
|
@@ -93,6 +113,7 @@ interface CloudCodeAssistRequest {
|
|
|
93
113
|
allowedFunctionNames?: string[];
|
|
94
114
|
};
|
|
95
115
|
};
|
|
116
|
+
labels?: Record<string, string>;
|
|
96
117
|
};
|
|
97
118
|
requestType?: string;
|
|
98
119
|
userAgent?: string;
|
|
@@ -21,6 +21,8 @@ export interface OpenAICompletionsOptions extends StreamOptions {
|
|
|
21
21
|
/** Force-disable reasoning where supported, or request the lowest effort on generic effort endpoints. */
|
|
22
22
|
disableReasoning?: boolean;
|
|
23
23
|
serviceTier?: ServiceTier;
|
|
24
|
+
/** @internal True when maxTokens came from the caller, not the model default. */
|
|
25
|
+
maxTokensExplicit?: boolean;
|
|
24
26
|
/**
|
|
25
27
|
* Routing-variant suffix appended to OpenRouter model IDs when none is
|
|
26
28
|
* already present (`anthropic/claude-haiku-latest` → `…:nitro`). Common
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { OAuthController, OAuthLoginCallbacks } from "./oauth/types";
|
|
2
|
-
export declare function loginAlibabaCodingPlan(options: OAuthController): Promise<
|
|
1
|
+
import type { OAuthController, OAuthCredentials, OAuthLoginCallbacks } from "./oauth/types";
|
|
2
|
+
export declare function loginAlibabaCodingPlan(options: OAuthController): Promise<OAuthCredentials>;
|
|
3
3
|
export declare const alibabaCodingPlanProvider: {
|
|
4
4
|
readonly id: "alibaba-coding-plan";
|
|
5
5
|
readonly name: "Alibaba Coding Plan";
|
|
6
|
-
readonly login: (cb: OAuthLoginCallbacks) => Promise<
|
|
6
|
+
readonly login: (cb: OAuthLoginCallbacks) => Promise<OAuthCredentials>;
|
|
7
|
+
readonly getApiKey: (credentials: OAuthCredentials) => string;
|
|
7
8
|
};
|
|
@@ -17,7 +17,7 @@ type GitHubCopilotLoginOptions = {
|
|
|
17
17
|
* Refresh GitHub Copilot token.
|
|
18
18
|
* With the opencode OAuth flow, the GitHub token is used directly — no JWT exchange needed.
|
|
19
19
|
*/
|
|
20
|
-
export declare function refreshGitHubCopilotToken(refreshToken: string, enterpriseDomain?: string): OAuthCredentials;
|
|
20
|
+
export declare function refreshGitHubCopilotToken(refreshToken: string, enterpriseDomain?: string, apiEndpoint?: string): OAuthCredentials;
|
|
21
21
|
/**
|
|
22
22
|
* Login with GitHub Copilot OAuth (device code flow)
|
|
23
23
|
*
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared OAuth flow for Google-style providers (Gemini CLI, Antigravity).
|
|
3
|
-
*
|
|
4
|
-
* Both providers use the same authorization-code flow shape; only the client
|
|
5
|
-
* credentials, scopes, endpoint constants, and project-discovery logic differ.
|
|
6
|
-
*/
|
|
7
1
|
import { OAuthCallbackFlow } from "./callback-server";
|
|
8
2
|
import type { OAuthController, OAuthCredentials } from "./types";
|
|
9
3
|
export interface GoogleOAuthFlowConfig {
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { OAuthController, OAuthCredentials } from "./types";
|
|
5
5
|
export declare function decodeJwt<T = Record<string, unknown>>(token: string): T | null;
|
|
6
|
+
/** Formats OpenAI Codex OAuth token endpoint errors for login and refresh failures. */
|
|
7
|
+
export declare function formatOpenAICodexTokenEndpointError(status: number, bodyText: string): string;
|
|
6
8
|
/** Builds the Codex browser OAuth URL used by browser login; exported for auth regression tests. */
|
|
7
9
|
export declare function createOpenAICodexAuthorizationUrl(args: {
|
|
8
10
|
state: string;
|
|
@@ -12,7 +12,8 @@ declare const ALL: ({
|
|
|
12
12
|
} | {
|
|
13
13
|
readonly id: "alibaba-coding-plan";
|
|
14
14
|
readonly name: "Alibaba Coding Plan";
|
|
15
|
-
readonly login: (cb: import("./oauth").OAuthLoginCallbacks) => Promise<
|
|
15
|
+
readonly login: (cb: import("./oauth").OAuthLoginCallbacks) => Promise<import("./oauth").OAuthCredentials>;
|
|
16
|
+
readonly getApiKey: (credentials: import("./oauth").OAuthCredentials) => string;
|
|
16
17
|
} | {
|
|
17
18
|
readonly id: "amazon-bedrock";
|
|
18
19
|
readonly name: "Amazon Bedrock";
|
package/dist/types/types.d.ts
CHANGED
|
@@ -304,6 +304,8 @@ export interface SimpleStreamOptions extends Omit<StreamOptions, "apiKey"> {
|
|
|
304
304
|
* or the catalog entry already names the variant).
|
|
305
305
|
*/
|
|
306
306
|
openrouterVariant?: string;
|
|
307
|
+
/** Antigravity endpoint routing mode: "auto" (default with failover), "production", "sandbox". */
|
|
308
|
+
antigravityEndpointMode?: "auto" | "production" | "sandbox";
|
|
307
309
|
}
|
|
308
310
|
export type StreamFunction<TApi extends Api> = (model: Model<TApi>, context: Context, options: OptionsForApi<TApi>) => AssistantMessageEventStream;
|
|
309
311
|
export interface TextSignatureV1 {
|
|
@@ -388,12 +390,18 @@ export interface DeveloperMessage {
|
|
|
388
390
|
providerPayload?: ProviderPayload;
|
|
389
391
|
timestamp: number;
|
|
390
392
|
}
|
|
393
|
+
export interface ContextSnapshot {
|
|
394
|
+
promptTokens: number;
|
|
395
|
+
nonMessageTokens: number;
|
|
396
|
+
lastMessageTimestamp?: number;
|
|
397
|
+
}
|
|
391
398
|
export interface AssistantMessage {
|
|
392
399
|
role: "assistant";
|
|
393
400
|
content: (TextContent | ThinkingContent | RedactedThinkingContent | ToolCall)[];
|
|
394
401
|
api: Api;
|
|
395
402
|
provider: Provider;
|
|
396
403
|
model: string;
|
|
404
|
+
contextSnapshot?: ContextSnapshot;
|
|
397
405
|
responseId?: string;
|
|
398
406
|
/**
|
|
399
407
|
* Name of the upstream provider an aggregator routed this request to, as
|
package/dist/types/usage.d.ts
CHANGED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Api, Model } from "../types";
|
|
2
|
+
import { AssistantMessageEventStream } from "./event-stream";
|
|
3
|
+
/** Stable lead phrase of the guard's error message; exported for tests. The
|
|
4
|
+
* message also carries "stream stall" so the session + transport retry
|
|
5
|
+
* classifiers treat it as a transient (retryable) stop without bespoke rules. */
|
|
6
|
+
export declare const THINKING_LOOP_ERROR_MARKER = "Thinking loop detected";
|
|
7
|
+
/**
|
|
8
|
+
* True when `model` should be guarded for thinking loops (Gemini only).
|
|
9
|
+
*
|
|
10
|
+
* OpenAI-compat transports can serve Gemini under an arbitrary provider/id, so
|
|
11
|
+
* for those we trust the family-derived (and user-overridable)
|
|
12
|
+
* `compat.enableGeminiThinkingLoopGuard` flag set by the catalog. Direct Google
|
|
13
|
+
* transports always carry a clearly gemini-shaped id/provider, so a string
|
|
14
|
+
* match is sufficient (and works for hand-built models without a compat record).
|
|
15
|
+
*/
|
|
16
|
+
export declare function isGeminiThinkingLoopModel(model: Model<Api>): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Stateful detector fed the streamed thinking deltas. `push` returns a
|
|
19
|
+
* human-readable reason the first time a loop shape is recognized; the caller
|
|
20
|
+
* is responsible for stopping after the first hit.
|
|
21
|
+
*/
|
|
22
|
+
export declare class ThinkingLoopDetector {
|
|
23
|
+
#private;
|
|
24
|
+
push(delta: string): string | null;
|
|
25
|
+
/** Process the buffered trailing paragraph (one with no blank-line / heading
|
|
26
|
+
* terminator). Called when the thinking block ends so the final segment —
|
|
27
|
+
* which may be the one that completes a duplicate cluster — is not dropped. */
|
|
28
|
+
flush(): string | null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Wrap a provider stream with the loop guard. `controller` is the guard's own
|
|
32
|
+
* abort handle: aborting it (after wiring it into the provider's signal via
|
|
33
|
+
* {@link withGeminiThinkingLoopGuard}) tears down the upstream once a loop
|
|
34
|
+
* trips.
|
|
35
|
+
*/
|
|
36
|
+
export declare function guardThinkingLoopStream(inner: AssistantMessageEventStream, model: Model<Api>, controller: AbortController): AssistantMessageEventStream;
|
|
37
|
+
/**
|
|
38
|
+
* Apply the Gemini loop guard around a provider dispatch. For non-Gemini models
|
|
39
|
+
* (or when disabled) this is a transparent pass-through. For Gemini it injects a
|
|
40
|
+
* guard abort signal into the provider call so a detected loop tears down the
|
|
41
|
+
* upstream, then wraps the returned stream.
|
|
42
|
+
*/
|
|
43
|
+
export declare function withGeminiThinkingLoopGuard<O extends {
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
}>(model: Model<Api>, options: O | undefined, dispatch: (options: O | undefined) => AssistantMessageEventStream): AssistantMessageEventStream;
|
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.0.
|
|
4
|
+
"version": "16.0.5",
|
|
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,8 +38,8 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@bufbuild/protobuf": "^2.12.0",
|
|
41
|
-
"@oh-my-pi/pi-catalog": "16.0.
|
|
42
|
-
"@oh-my-pi/pi-utils": "16.0.
|
|
41
|
+
"@oh-my-pi/pi-catalog": "16.0.5",
|
|
42
|
+
"@oh-my-pi/pi-utils": "16.0.5",
|
|
43
43
|
"partial-json": "^0.1.7",
|
|
44
44
|
"zod": "^4"
|
|
45
45
|
},
|
|
@@ -21,6 +21,7 @@ import { usageReportSchema } from "../usage";
|
|
|
21
21
|
/** Real OAuth credential (broker-side) — refresh token is the actual upstream value. */
|
|
22
22
|
export const oauthCredentialSchema = z
|
|
23
23
|
.object({
|
|
24
|
+
apiEndpoint: z.string().optional(),
|
|
24
25
|
type: z.literal("oauth"),
|
|
25
26
|
refresh: z
|
|
26
27
|
.string()
|
package/src/auth-retry.ts
CHANGED
|
@@ -51,6 +51,25 @@ export async function resolveApiKeyOnce(key: ApiKey | undefined, signal?: AbortS
|
|
|
51
51
|
return key;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Wraps a resolver with a bearer that was already selected for this request.
|
|
56
|
+
*
|
|
57
|
+
* Callers that preflight credentials can pass the returned resolver to the
|
|
58
|
+
* auth-retry driver without making the driver know about that preflight: the
|
|
59
|
+
* first initial resolution reuses `seed`, and all later resolutions delegate to
|
|
60
|
+
* `resolver`.
|
|
61
|
+
*/
|
|
62
|
+
export function seedApiKeyResolver(seed: string | undefined, resolver: ApiKeyResolver): ApiKeyResolver {
|
|
63
|
+
let seedPending = seed !== undefined;
|
|
64
|
+
return ctx => {
|
|
65
|
+
if (seedPending && ctx.error === undefined) {
|
|
66
|
+
seedPending = false;
|
|
67
|
+
return seed;
|
|
68
|
+
}
|
|
69
|
+
return resolver(ctx);
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
54
73
|
/**
|
|
55
74
|
* Classifies whether an error should trigger a credential refresh/rotation
|
|
56
75
|
* retry: a hard `401`, or a rotatable usage-limit ("usage_limit_reached",
|
package/src/auth-storage.ts
CHANGED
|
@@ -191,6 +191,7 @@ export type CompletionProbeCredential =
|
|
|
191
191
|
projectId?: string;
|
|
192
192
|
email?: string;
|
|
193
193
|
enterpriseUrl?: string;
|
|
194
|
+
apiEndpoint?: string;
|
|
194
195
|
};
|
|
195
196
|
|
|
196
197
|
/**
|
|
@@ -638,6 +639,7 @@ export interface OAuthAccess {
|
|
|
638
639
|
email?: string;
|
|
639
640
|
projectId?: string;
|
|
640
641
|
enterpriseUrl?: string;
|
|
642
|
+
apiEndpoint?: string;
|
|
641
643
|
}
|
|
642
644
|
|
|
643
645
|
export interface OAuthAccessFailure {
|
|
@@ -646,6 +648,7 @@ export interface OAuthAccessFailure {
|
|
|
646
648
|
email?: string;
|
|
647
649
|
projectId?: string;
|
|
648
650
|
enterpriseUrl?: string;
|
|
651
|
+
apiEndpoint?: string;
|
|
649
652
|
error: string;
|
|
650
653
|
}
|
|
651
654
|
|
|
@@ -1512,17 +1515,6 @@ export class AuthStorage {
|
|
|
1512
1515
|
this.#resetProviderAssignments(provider);
|
|
1513
1516
|
}
|
|
1514
1517
|
|
|
1515
|
-
async #upsertOAuthCredential(provider: string, credential: OAuthCredential): Promise<void> {
|
|
1516
|
-
const stored = this.#store.upsertAuthCredentialRemote
|
|
1517
|
-
? await this.#store.upsertAuthCredentialRemote(provider, credential)
|
|
1518
|
-
: this.#store.upsertAuthCredentialForProvider(provider, credential);
|
|
1519
|
-
this.#setStoredCredentials(
|
|
1520
|
-
provider,
|
|
1521
|
-
stored.map(record => ({ id: record.id, credential: record.credential })),
|
|
1522
|
-
);
|
|
1523
|
-
this.#resetProviderAssignments(provider);
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
1518
|
/**
|
|
1527
1519
|
* List stored credential rows, optionally filtered by provider.
|
|
1528
1520
|
*/
|
|
@@ -1786,7 +1778,10 @@ export class AuthStorage {
|
|
|
1786
1778
|
return;
|
|
1787
1779
|
}
|
|
1788
1780
|
const newCredential: OAuthCredential = { type: "oauth", ...result };
|
|
1789
|
-
|
|
1781
|
+
// Use set() instead of #upsertOAuthCredential to replace ALL existing credentials
|
|
1782
|
+
// (including legacy api_key rows from older versions) with the new OAuth credential.
|
|
1783
|
+
// This ensures getApiKey() doesn't match an old api_key row before the new OAuth row.
|
|
1784
|
+
await this.set(def.storeCredentialsAs ?? provider, newCredential);
|
|
1790
1785
|
}
|
|
1791
1786
|
|
|
1792
1787
|
/**
|
|
@@ -1811,6 +1806,7 @@ export class AuthStorage {
|
|
|
1811
1806
|
projectId: credential.projectId,
|
|
1812
1807
|
email: credential.email,
|
|
1813
1808
|
enterpriseUrl: credential.enterpriseUrl,
|
|
1809
|
+
apiEndpoint: credential.apiEndpoint,
|
|
1814
1810
|
};
|
|
1815
1811
|
}
|
|
1816
1812
|
|
|
@@ -1889,6 +1885,7 @@ export class AuthStorage {
|
|
|
1889
1885
|
projectId: credential.projectId,
|
|
1890
1886
|
email: credential.email,
|
|
1891
1887
|
enterpriseUrl: credential.enterpriseUrl,
|
|
1888
|
+
apiEndpoint: credential.apiEndpoint,
|
|
1892
1889
|
};
|
|
1893
1890
|
}
|
|
1894
1891
|
|
|
@@ -1912,6 +1909,7 @@ export class AuthStorage {
|
|
|
1912
1909
|
projectId: credential.projectId,
|
|
1913
1910
|
email: credential.email,
|
|
1914
1911
|
enterpriseUrl: credential.enterpriseUrl,
|
|
1912
|
+
apiEndpoint: credential.apiEndpoint,
|
|
1915
1913
|
};
|
|
1916
1914
|
}
|
|
1917
1915
|
|
|
@@ -1925,6 +1923,7 @@ export class AuthStorage {
|
|
|
1925
1923
|
projectId: refreshed.projectId ?? credential.projectId,
|
|
1926
1924
|
email: refreshed.email ?? credential.email,
|
|
1927
1925
|
enterpriseUrl: refreshed.enterpriseUrl ?? credential.enterpriseUrl,
|
|
1926
|
+
apiEndpoint: refreshed.apiEndpoint ?? credential.apiEndpoint,
|
|
1928
1927
|
};
|
|
1929
1928
|
}
|
|
1930
1929
|
|
|
@@ -1972,6 +1971,7 @@ export class AuthStorage {
|
|
|
1972
1971
|
projectId: next.projectId,
|
|
1973
1972
|
email: next.email,
|
|
1974
1973
|
enterpriseUrl: next.enterpriseUrl,
|
|
1974
|
+
apiEndpoint: next.apiEndpoint,
|
|
1975
1975
|
});
|
|
1976
1976
|
}
|
|
1977
1977
|
|
|
@@ -3392,6 +3392,7 @@ export class AuthStorage {
|
|
|
3392
3392
|
email: result.newCredentials.email ?? selection.credential.email,
|
|
3393
3393
|
projectId: result.newCredentials.projectId ?? selection.credential.projectId,
|
|
3394
3394
|
enterpriseUrl: result.newCredentials.enterpriseUrl ?? selection.credential.enterpriseUrl,
|
|
3395
|
+
apiEndpoint: result.newCredentials.apiEndpoint ?? selection.credential.apiEndpoint,
|
|
3395
3396
|
};
|
|
3396
3397
|
this.#replaceCredentialAt(provider, selection.index, updated);
|
|
3397
3398
|
if ((checkUsage && !allowBlocked) || requiresProModel) {
|
|
@@ -3518,6 +3519,7 @@ export class AuthStorage {
|
|
|
3518
3519
|
return JSON.stringify({
|
|
3519
3520
|
token: oauthSelection.credential.access,
|
|
3520
3521
|
enterpriseUrl: oauthSelection.credential.enterpriseUrl,
|
|
3522
|
+
apiEndpoint: oauthSelection.credential.apiEndpoint,
|
|
3521
3523
|
});
|
|
3522
3524
|
}
|
|
3523
3525
|
return oauthSelection.credential.access;
|
|
@@ -3615,6 +3617,7 @@ export class AuthStorage {
|
|
|
3615
3617
|
email: credential.email,
|
|
3616
3618
|
projectId: credential.projectId,
|
|
3617
3619
|
enterpriseUrl: credential.enterpriseUrl,
|
|
3620
|
+
apiEndpoint: credential.apiEndpoint,
|
|
3618
3621
|
};
|
|
3619
3622
|
}
|
|
3620
3623
|
|
|
@@ -4075,6 +4078,7 @@ export class AuthStorage {
|
|
|
4075
4078
|
email: refreshed.email ?? target.credential.email,
|
|
4076
4079
|
projectId: refreshed.projectId ?? target.credential.projectId,
|
|
4077
4080
|
enterpriseUrl: refreshed.enterpriseUrl ?? target.credential.enterpriseUrl,
|
|
4081
|
+
apiEndpoint: refreshed.apiEndpoint ?? target.credential.apiEndpoint,
|
|
4078
4082
|
};
|
|
4079
4083
|
this.#replaceCredentialAt(provider, index, updated);
|
|
4080
4084
|
return {
|