@yansigit/opencodex 2.31.3 → 2.33.0
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/README.md +2 -2
- package/bin/ocx.mjs +99 -70
- package/gui/dist/assets/index-DKLr4LTE.js +102 -0
- package/gui/dist/assets/index-DrSQdTRd.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +6 -5
- package/src/adapters/anthropic.ts +25 -13
- package/src/adapters/azure.ts +20 -4
- package/src/adapters/base.ts +5 -1
- package/src/adapters/command-code.ts +42 -10
- package/src/adapters/cursor/live-models.ts +8 -0
- package/src/adapters/cursor/live-transport.ts +1 -1
- package/src/adapters/cursor/native-exec-desktop.ts +16 -0
- package/src/adapters/cursor/protobuf-events.ts +158 -7
- package/src/adapters/cursor/protobuf-request.ts +33 -15
- package/src/adapters/cursor/request-builder.ts +4 -3
- package/src/adapters/cursor/tool-definitions.ts +27 -1
- package/src/adapters/cursor/types.ts +4 -3
- package/src/adapters/cursor.ts +9 -0
- package/src/adapters/google-antigravity-replay.ts +2 -2
- package/src/adapters/google-antigravity-wire.ts +7 -0
- package/src/adapters/google-errors.ts +6 -2
- package/src/adapters/google-http.ts +30 -7
- package/src/adapters/google-truncation.ts +5 -0
- package/src/adapters/google-wire-compiler.ts +38 -6
- package/src/adapters/google.ts +154 -33
- package/src/adapters/kiro-tools.ts +20 -9
- package/src/adapters/kiro.ts +0 -3
- package/src/adapters/openai-chat.ts +9 -0
- package/src/adapters/openai-responses.ts +4 -1
- package/src/adapters/tool-catalog-nudge.ts +1 -1
- package/src/adapters/xai-web-search.ts +7 -2
- package/src/bridge.ts +133 -24
- package/src/claude/context-windows.ts +16 -9
- package/src/cli/dispatch.ts +50 -2
- package/src/cli/doctor.ts +26 -13
- package/src/cli/help.ts +4 -3
- package/src/cli/index.ts +20 -6
- package/src/cli/models.ts +13 -3
- package/src/cli/observe.ts +20 -5
- package/src/cli/provider.ts +8 -1
- package/src/cli/registry.ts +7 -5
- package/src/cli/status.ts +25 -1
- package/src/cli/system-restart-client.ts +1 -1
- package/src/cli/usage-report.ts +134 -0
- package/src/codex/app-server-processes.ts +3 -1
- package/src/codex/auth-api.ts +4 -2
- package/src/codex/autostart-health.ts +16 -0
- package/src/codex/catalog/aggregation.ts +12 -0
- package/src/codex/catalog/effort.ts +42 -12
- package/src/codex/catalog/metadata.ts +27 -1
- package/src/codex/catalog/model-metadata.ts +593 -0
- package/src/codex/catalog/parsing.ts +71 -27
- package/src/codex/catalog/provider-fetch.ts +189 -33
- package/src/codex/catalog/sync.ts +6 -5
- package/src/codex/convergence.ts +5 -0
- package/src/codex/desktop-app-restart.ts +342 -0
- package/src/codex/history-job.ts +32 -3
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +5 -5
- package/src/codex/history-provider.ts +825 -247
- package/src/codex/history-worker.ts +8 -5
- package/src/codex/inject.ts +49 -21
- package/src/codex/injected-marker.ts +1 -1
- package/src/codex/internal/history-writer.ts +4 -3
- package/src/codex/native-profile-startup.ts +157 -27
- package/src/codex/native-residue.ts +26 -33
- package/src/codex/shim.ts +56 -3
- package/src/combos/failover.ts +27 -0
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +81 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +308 -0
- package/src/config/provider-validation.ts +214 -0
- package/src/config.ts +153 -814
- package/src/generated/compatibility-version.json +232 -148
- package/src/images/loop.ts +37 -6
- package/src/images/plan.ts +5 -4
- package/src/integrations/ownership-policy.ts +141 -0
- package/src/integrations/ownership.ts +10 -0
- package/src/integrations/state.ts +44 -5
- package/src/integrations/writer.ts +6 -0
- package/src/lib/azure-identity.ts +154 -0
- package/src/lib/bounded-body.ts +14 -2
- package/src/lib/debug.ts +42 -0
- package/src/lib/errors.ts +14 -0
- package/src/lib/process-control.ts +2 -1
- package/src/lib/provider-outbound.ts +45 -33
- package/src/lib/provider-tls-profile.ts +309 -0
- package/src/lib/proxy-env.ts +49 -0
- package/src/lib/redact.ts +10 -1
- package/src/lib/state-store-registrations.ts +2 -0
- package/src/lib/tool-argument-integers.ts +56 -5
- package/src/oauth/antigravity-routing.ts +282 -236
- package/src/oauth/callback-server.ts +22 -2
- package/src/oauth/command-code.ts +5 -16
- package/src/oauth/google-antigravity.ts +42 -5
- package/src/oauth/health.ts +1 -1
- package/src/oauth/index.ts +15 -3
- package/src/oauth/kimi.ts +9 -1
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +6 -0
- package/src/providers/antigravity-quota.ts +3 -1
- package/src/providers/api-keys.ts +2 -1
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/derive.ts +4 -0
- package/src/providers/key-failover.ts +5 -1
- package/src/providers/openai-tiers.ts +5 -0
- package/src/providers/provider-id-rewrite.ts +1 -0
- package/src/providers/quota.ts +59 -13
- package/src/providers/registry.ts +4 -2
- package/src/providers/request-pacing.ts +33 -6
- package/src/providers/xai-transport.ts +21 -0
- package/src/reasoning-effort.ts +19 -2
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/custom-tool-compat.ts +132 -38
- package/src/responses/google-provider-options.ts +36 -0
- package/src/responses/namespace-tool-compat.ts +84 -4
- package/src/responses/parser.ts +14 -2
- package/src/responses/provider-opaque-metadata.ts +3 -3
- package/src/responses/reasoning-replay-cache.ts +81 -3
- package/src/responses/schema.ts +37 -0
- package/src/responses/state.ts +94 -4
- package/src/router.ts +8 -2
- package/src/server/auth-cors.ts +37 -7
- package/src/server/images.ts +19 -35
- package/src/server/index.ts +102 -21
- package/src/server/local-management-read-client.ts +1 -1
- package/src/server/local-provider-reload-client.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +206 -16
- package/src/server/management/combo-routes.ts +6 -0
- package/src/server/management/config-routes.ts +35 -6
- package/src/server/management/context.ts +1 -1
- package/src/server/management/logs-usage-routes.ts +27 -6
- package/src/server/management/model-routes.ts +8 -4
- package/src/server/management/model-rows.ts +4 -0
- package/src/server/management/native-integration-routes.ts +2 -1
- package/src/server/management/oauth-account-routes.ts +25 -4
- package/src/server/management/provider-capability-config.ts +1 -1
- package/src/server/management/provider-routes.ts +113 -15
- package/src/server/management/routing-profile-routes.ts +3 -0
- package/src/server/management/system-restart.ts +1 -1
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/proxy-liveness.ts +2 -1
- package/src/server/request-log-conversation.ts +30 -0
- package/src/server/request-log.ts +21 -0
- package/src/server/responses/agent-task-recovery.ts +1 -1
- package/src/server/responses/codex-auth-error.ts +55 -0
- package/src/server/responses/combo-stream-preflight.ts +171 -0
- package/src/server/responses/compact.ts +36 -22
- package/src/server/responses/core.ts +584 -247
- package/src/server/responses/empty-completion-guard.ts +35 -6
- package/src/server/responses/fetch-helpers.ts +20 -102
- package/src/server/responses/v2-native-parent-override.ts +59 -0
- package/src/server/responses/ws-upstream.ts +75 -2
- package/src/server/responses-custom-tool-repair.ts +41 -5
- package/src/server/responses-undeclared-tool-guard.ts +241 -18
- package/src/service.ts +9 -5
- package/src/types/config.ts +16 -1
- package/src/types/provider.ts +16 -0
- package/src/types/request.ts +34 -1
- package/src/types/tools.ts +114 -11
- package/src/types.ts +7 -1
- package/src/update/index.ts +5 -4
- package/src/update/job.ts +3 -1
- package/src/update/transactional-install.mjs +8 -1
- package/src/usage/log.ts +16 -8
- package/src/usage/summary.ts +201 -8
- package/src/vision/describe.ts +18 -13
- package/src/web-search/executor.ts +10 -3
- package/src/web-search/gemini-executor.ts +6 -4
- package/src/web-search/loop.ts +42 -6
- package/gui/dist/assets/index-CGoDO3uO.css +0 -1
- package/gui/dist/assets/index-Cxt5fZMP.js +0 -102
|
@@ -51,6 +51,28 @@ interface GoogleTokenPayload {
|
|
|
51
51
|
id_token?: unknown;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
const TERMINAL_OAUTH_ERRORS = new Set([
|
|
55
|
+
"access_denied",
|
|
56
|
+
"expired_token",
|
|
57
|
+
"invalid_grant",
|
|
58
|
+
"refresh_token_reused",
|
|
59
|
+
"refresh_token_revoked",
|
|
60
|
+
"revoked",
|
|
61
|
+
"revoked_token",
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
export class AntigravityTokenRequestError extends Error {
|
|
65
|
+
readonly httpStatus: number;
|
|
66
|
+
readonly oauthError?: string;
|
|
67
|
+
|
|
68
|
+
constructor(httpStatus: number, oauthError?: string) {
|
|
69
|
+
super(`Antigravity token request failed: ${httpStatus}`);
|
|
70
|
+
this.name = "AntigravityTokenRequestError";
|
|
71
|
+
this.httpStatus = httpStatus;
|
|
72
|
+
if (oauthError) this.oauthError = oauthError;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
54
76
|
function decodeJwtPayload(token: string): Record<string, unknown> | undefined {
|
|
55
77
|
const part = token.split(".")[1];
|
|
56
78
|
if (!part) return undefined;
|
|
@@ -75,8 +97,17 @@ async function postToken(body: Record<string, string>, signal?: AbortSignal): Pr
|
|
|
75
97
|
signal: requestSignal(signal),
|
|
76
98
|
});
|
|
77
99
|
if (!response.ok) {
|
|
78
|
-
|
|
79
|
-
|
|
100
|
+
let oauthError: string | undefined;
|
|
101
|
+
if (response.status === 400 || response.status === 401) {
|
|
102
|
+
try {
|
|
103
|
+
const body = await response.json() as { error?: unknown };
|
|
104
|
+
const candidate = typeof body.error === "string" ? body.error.trim().toLowerCase() : "";
|
|
105
|
+
if (TERMINAL_OAUTH_ERRORS.has(candidate)) oauthError = candidate;
|
|
106
|
+
} catch {
|
|
107
|
+
/* Keep status-only errors for malformed or non-JSON responses. */
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
throw new AntigravityTokenRequestError(response.status, oauthError);
|
|
80
111
|
}
|
|
81
112
|
return (await response.json()) as GoogleTokenPayload;
|
|
82
113
|
}
|
|
@@ -221,7 +252,11 @@ export async function loginAntigravity(ctrl: OAuthController, opts?: { forceAcco
|
|
|
221
252
|
return new AntigravityOAuthFlow(ctrl, opts).login();
|
|
222
253
|
}
|
|
223
254
|
|
|
224
|
-
export async function refreshAntigravityToken(
|
|
255
|
+
export async function refreshAntigravityToken(
|
|
256
|
+
refreshToken: string,
|
|
257
|
+
signal?: AbortSignal,
|
|
258
|
+
previousCredential?: OAuthCredentials,
|
|
259
|
+
): Promise<OAuthCredentials> {
|
|
225
260
|
if (!refreshToken) throw new Error("Antigravity credentials are expired and do not include a refresh token");
|
|
226
261
|
const payload = await postToken({
|
|
227
262
|
grant_type: "refresh_token",
|
|
@@ -230,8 +265,10 @@ export async function refreshAntigravityToken(refreshToken: string, signal?: Abo
|
|
|
230
265
|
refresh_token: refreshToken,
|
|
231
266
|
}, signal);
|
|
232
267
|
const creds = credentialsFromPayload(payload, refreshToken);
|
|
233
|
-
//
|
|
234
|
-
|
|
268
|
+
// Project discovery is onboarding work, not ordinary token renewal. Preserve the existing
|
|
269
|
+
// account-scoped project id and avoid an extra authenticated CCA call when it is present.
|
|
270
|
+
const projectId = previousCredential?.projectId
|
|
271
|
+
?? await discoverAntigravityProject(creds.access, signal).catch(() => undefined);
|
|
235
272
|
return projectId ? { ...creds, projectId } : creds;
|
|
236
273
|
}
|
|
237
274
|
|
package/src/oauth/health.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { getAnthropicAccountHealthSnapshot } from "./anthropic-routing";
|
|
|
3
3
|
import { isAccountNeedsReauth } from "../codex/account-runtime-state";
|
|
4
4
|
import { getCodexAccountCredential, listCodexAccountIds } from "../codex/account-store";
|
|
5
5
|
import { MAIN_CODEX_ACCOUNT_ID } from "../codex/main-account";
|
|
6
|
-
import { readRuntimePort } from "../config";
|
|
6
|
+
import { readRuntimePort } from "../config/process-state";
|
|
7
7
|
import { LOCAL_MANAGEMENT_READ_PATHS } from "../lib/local-management-capability";
|
|
8
8
|
import { maskAccountId } from "../lib/privacy";
|
|
9
9
|
import { findLiveProxy } from "../server/proxy-liveness";
|
package/src/oauth/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { ANTHROPIC_OAUTH_BETA, AnthropicTokenError, loginAnthropic, refreshAnthr
|
|
|
10
10
|
import { loginKimi, refreshKimiToken } from "./kimi";
|
|
11
11
|
import { loginNous, NousTokenError, refreshNousToken, clearNousRefreshIntent, RefreshIntentIOError } from "./nous";
|
|
12
12
|
import { loginChatGPT, refreshChatGPTToken } from "./chatgpt";
|
|
13
|
-
import { loginAntigravity, refreshAntigravityToken } from "./google-antigravity";
|
|
13
|
+
import { AntigravityTokenRequestError, loginAntigravity, refreshAntigravityToken } from "./google-antigravity";
|
|
14
14
|
import { loginCursor, refreshCursorToken } from "./cursor";
|
|
15
15
|
import { loginGithubCopilot, refreshGithubCopilotToken, validateCopilotApiBaseUrl } from "./github-copilot";
|
|
16
16
|
import { loginCommandCode, refreshCommandCodeToken } from "./command-code";
|
|
@@ -222,6 +222,7 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderDef> = {
|
|
|
222
222
|
refresh: refreshAntigravityToken,
|
|
223
223
|
providerConfig: oauthConfig("google-antigravity"),
|
|
224
224
|
defaultModel: oauthDefaultModel("google-antigravity"),
|
|
225
|
+
defaultRefreshPolicy: "lazy-only",
|
|
225
226
|
},
|
|
226
227
|
cursor: {
|
|
227
228
|
login: (ctrl, opts) => loginCursor(ctrl, undefined, { forceLogin: opts?.forceLogin }),
|
|
@@ -466,8 +467,16 @@ export async function getValidAccessTokenSnapshot(provider: string): Promise<OAu
|
|
|
466
467
|
return resolveAccessSnapshotForAccount(provider, set.activeAccountId);
|
|
467
468
|
}
|
|
468
469
|
|
|
470
|
+
/** Resolve a specific stored account without consulting or changing the active account. */
|
|
471
|
+
export async function getValidAccessTokenSnapshotForAccount(
|
|
472
|
+
provider: string,
|
|
473
|
+
accountId: string,
|
|
474
|
+
): Promise<OAuthAccessSnapshot> {
|
|
475
|
+
return resolveAccessSnapshotForAccount(provider, accountId);
|
|
476
|
+
}
|
|
477
|
+
|
|
469
478
|
/** Providers whose upstream-401 replay path may force a snapshot refresh. */
|
|
470
|
-
const FORCE_REFRESH_PROVIDERS = new Set(["xai", "github-copilot", "kiro"]);
|
|
479
|
+
const FORCE_REFRESH_PROVIDERS = new Set(["xai", "github-copilot", "kiro", "google-antigravity"]);
|
|
471
480
|
|
|
472
481
|
export async function forceRefreshOAuthAccessSnapshot(
|
|
473
482
|
rejected: OAuthAccessSnapshot,
|
|
@@ -501,6 +510,9 @@ function isTerminalRefreshError(err: unknown): boolean {
|
|
|
501
510
|
|| msg.includes("expired_token");
|
|
502
511
|
}
|
|
503
512
|
function terminal(error:unknown):boolean{
|
|
513
|
+
if (error instanceof AntigravityTokenRequestError) {
|
|
514
|
+
return (error.httpStatus === 400 || error.httpStatus === 401) && error.oauthError !== undefined;
|
|
515
|
+
}
|
|
504
516
|
if(error instanceof XaiTokenRequestError)return ["invalid_grant","refresh_token_reused","revoked_token"].includes(error.oauthError??"");
|
|
505
517
|
if(error instanceof AnthropicTokenError)return (error.httpStatus===400||error.httpStatus===401)&&["invalid_grant","refresh_token_reused","revoked","revoked_token","refresh_token_revoked"].includes(error.oauthError??"");
|
|
506
518
|
if(error instanceof KiroTokenRefreshError)return (error.httpStatus===400||error.httpStatus===401)&&error.oauthError!==undefined;
|
|
@@ -630,7 +642,7 @@ export async function refreshAnthropicAccountWithLock(
|
|
|
630
642
|
writeOAuthRefreshIntent(provider, accountId, generation, now(), deps.flight?.flightId);
|
|
631
643
|
if (deps.signal?.aborted) throw deps.signal.reason;
|
|
632
644
|
if (deps.flight) deps.flight.dispatched = true;
|
|
633
|
-
const fresh = merged(await def.refresh(stored.refresh, deps.signal), stored);
|
|
645
|
+
const fresh = merged(await def.refresh(stored.refresh, deps.signal, stored), stored);
|
|
634
646
|
const outcome = await mergeAccountCredential(provider, accountId, fresh, {
|
|
635
647
|
expectedGeneration: generation,
|
|
636
648
|
afterPrePersistRead: deps.afterPrePersistRead,
|
package/src/oauth/kimi.ts
CHANGED
|
@@ -209,7 +209,15 @@ async function pollForToken(deviceCode: string, intervalMs: number, expiresInMs:
|
|
|
209
209
|
|
|
210
210
|
export async function loginKimi(ctrl: OAuthController): Promise<OAuthCredentials> {
|
|
211
211
|
const device = await requestDeviceAuthorization();
|
|
212
|
-
|
|
212
|
+
// `deviceCode` carries the human-typed user code, matching the other device
|
|
213
|
+
// providers. It is also what tells the management login route this is a device
|
|
214
|
+
// flow, so a provider-supplied verification URI is no longer handed to a local
|
|
215
|
+
// browser spawn — the URL stays visible and copyable in every login surface.
|
|
216
|
+
ctrl.onAuth?.({
|
|
217
|
+
url: device.verificationUriComplete,
|
|
218
|
+
instructions: `Enter code: ${device.userCode}`,
|
|
219
|
+
deviceCode: device.userCode,
|
|
220
|
+
});
|
|
213
221
|
return pollForToken(device.deviceCode, device.intervalMs, device.expiresInMs, ctrl.signal);
|
|
214
222
|
}
|
|
215
223
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { OcxConfig } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Whether a login should open a browser on the machine running the proxy.
|
|
5
|
+
*
|
|
6
|
+
* Both login routes share this so the two surfaces cannot drift: a per-request
|
|
7
|
+
* `openBrowser` beats the persisted setting, and the persisted setting beats
|
|
8
|
+
* the historical default. It lives in `src/oauth/` rather than beside either
|
|
9
|
+
* route because `src/codex/auth-api.ts` and
|
|
10
|
+
* `src/server/management/oauth-account-routes.ts` both already import from
|
|
11
|
+
* here, so no new edge is added to the import graph.
|
|
12
|
+
*
|
|
13
|
+
* The default is the important part. An operator who upgrades and configures
|
|
14
|
+
* nothing must see exactly what they saw before, so only an explicit `false`
|
|
15
|
+
* declines — `undefined`, `true`, and a malformed value all open.
|
|
16
|
+
*
|
|
17
|
+
* A malformed request field is ignored rather than rejected on purpose: this is
|
|
18
|
+
* a display preference, and no login should fail because of one.
|
|
19
|
+
*/
|
|
20
|
+
export function shouldOpenBrowserForLogin(
|
|
21
|
+
requested: unknown,
|
|
22
|
+
config: Pick<OcxConfig, "oauthOpenBrowser">,
|
|
23
|
+
): boolean {
|
|
24
|
+
if (typeof requested === "boolean") return requested;
|
|
25
|
+
return config.oauthOpenBrowser !== false;
|
|
26
|
+
}
|
package/src/oauth/store.ts
CHANGED
|
@@ -593,9 +593,11 @@ export async function upsertCredentialByIdentity(
|
|
|
593
593
|
|
|
594
594
|
/** Remove the ACTIVE account; remaining accounts promote the first one. */
|
|
595
595
|
export async function removeCredential(provider: string): Promise<void> {
|
|
596
|
+
let removedAccountId: string | undefined;
|
|
596
597
|
await mutateStore(store => {
|
|
597
598
|
const set = store[provider];
|
|
598
599
|
if (!set) return;
|
|
600
|
+
removedAccountId = set.activeAccountId;
|
|
599
601
|
set.accounts = set.accounts.filter(a => a.id !== set.activeAccountId);
|
|
600
602
|
if (set.accounts.length === 0) {
|
|
601
603
|
delete store[provider];
|
|
@@ -603,6 +605,10 @@ export async function removeCredential(provider: string): Promise<void> {
|
|
|
603
605
|
}
|
|
604
606
|
set.activeAccountId = set.accounts[0]!.id;
|
|
605
607
|
}, [provider]);
|
|
608
|
+
if (provider === "google-antigravity" && removedAccountId) {
|
|
609
|
+
const { clearAntigravityRoutingStateForAccount } = await import("./antigravity-routing");
|
|
610
|
+
clearAntigravityRoutingStateForAccount(removedAccountId);
|
|
611
|
+
}
|
|
606
612
|
}
|
|
607
613
|
|
|
608
614
|
// ---------------------------------------------------------------------------
|
|
@@ -90,7 +90,9 @@ function candidateModelName(record: Record<string, unknown>): string {
|
|
|
90
90
|
?? record.modelName
|
|
91
91
|
?? record.model
|
|
92
92
|
?? record.name
|
|
93
|
-
?? record.displayName
|
|
93
|
+
?? record.displayName
|
|
94
|
+
?? record.modelFamily
|
|
95
|
+
?? record.model_family;
|
|
94
96
|
return typeof explicit === "string" ? explicit.toLowerCase() : "";
|
|
95
97
|
}
|
|
96
98
|
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
10
10
|
import { saveConfigPreservingClaudeCode } from "../config";
|
|
11
|
+
import { isAzureIdentityProvider } from "../config/provider-validation";
|
|
11
12
|
import type { OcxConfig, OcxProviderConfig } from "../types";
|
|
12
13
|
|
|
13
14
|
export interface ProviderApiKeyInfo {
|
|
@@ -36,7 +37,7 @@ export function apiKeyPoolEntryId(key: string): string {
|
|
|
36
37
|
|
|
37
38
|
/** True for providers whose upstream auth is a configured API key (not oauth/forward). */
|
|
38
39
|
export function isKeyAuthProvider(provider: OcxProviderConfig): boolean {
|
|
39
|
-
return provider.authMode !== "oauth" && provider.authMode !== "forward";
|
|
40
|
+
return !isAzureIdentityProvider(provider) && provider.authMode !== "oauth" && provider.authMode !== "forward";
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
/** Trim and reject blank / CRLF-bearing secrets. Shared by pool writes and OAuth upsert. */
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { SUPPORTED_NATIVE_OPENAI_SLUGS } from "../codex/catalog/native-models";
|
|
2
|
+
import { redactSecretString } from "../lib/redact";
|
|
3
|
+
|
|
4
|
+
const RESERVED_OBJECT_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
5
|
+
|
|
6
|
+
function positiveSafeInteger(value: unknown): value is number {
|
|
7
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolve a client-facing soft compaction budget without changing any hard
|
|
12
|
+
* model limit. Configuration and measured input ceilings may only lower the
|
|
13
|
+
* default 90% envelope.
|
|
14
|
+
*/
|
|
15
|
+
export function clampAutoCompactTokenLimit(
|
|
16
|
+
contextWindow: number,
|
|
17
|
+
maxInputTokens?: number,
|
|
18
|
+
configuredLimit?: number,
|
|
19
|
+
): number {
|
|
20
|
+
const candidates = [Math.floor(contextWindow * 0.9), contextWindow];
|
|
21
|
+
if (positiveSafeInteger(maxInputTokens)) candidates.push(maxInputTokens);
|
|
22
|
+
if (positiveSafeInteger(configuredLimit)) candidates.push(configuredLimit);
|
|
23
|
+
return Math.min(...candidates);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type AutoCompactBudgetValidationOptions = Readonly<{
|
|
27
|
+
/** PATCH accepts null for whole-map and per-key deletion. */
|
|
28
|
+
allowTombstones?: boolean;
|
|
29
|
+
/** The canonical ChatGPT provider accepts only exact supported native ids. */
|
|
30
|
+
requireNativeIds?: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
|
|
33
|
+
/** Shared config/load/management boundary for per-model soft budgets. */
|
|
34
|
+
export function modelAutoCompactTokenLimitsConfigError(
|
|
35
|
+
value: unknown,
|
|
36
|
+
options: AutoCompactBudgetValidationOptions = {},
|
|
37
|
+
): string | null {
|
|
38
|
+
const field = "modelAutoCompactTokenLimits";
|
|
39
|
+
if (value === undefined || (options.allowTombstones && value === null)) return null;
|
|
40
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
41
|
+
return `${field} must be a plain object${options.allowTombstones ? " or null" : ""}`;
|
|
42
|
+
}
|
|
43
|
+
const prototype = Object.getPrototypeOf(value);
|
|
44
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
45
|
+
return `${field} must be a plain object with own properties`;
|
|
46
|
+
}
|
|
47
|
+
for (const [modelId, entry] of Object.entries(value as Record<string, unknown>)) {
|
|
48
|
+
const safeModelId = JSON.stringify(redactSecretString(modelId));
|
|
49
|
+
if (!modelId.trim()) return `${field} keys must be nonblank model ids`;
|
|
50
|
+
if (RESERVED_OBJECT_KEYS.has(modelId)) {
|
|
51
|
+
return `${field} key ${safeModelId} is reserved`;
|
|
52
|
+
}
|
|
53
|
+
if (options.requireNativeIds
|
|
54
|
+
&& (modelId.includes("/") || !SUPPORTED_NATIVE_OPENAI_SLUGS.has(modelId))) {
|
|
55
|
+
return `${field} key ${safeModelId} must be an exact supported native model id`;
|
|
56
|
+
}
|
|
57
|
+
if (options.allowTombstones && entry === null) continue;
|
|
58
|
+
if (!positiveSafeInteger(entry)) {
|
|
59
|
+
return `${field}[${safeModelId}] must be a positive safe integer${
|
|
60
|
+
options.allowTombstones ? " or null" : ""
|
|
61
|
+
}`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
package/src/providers/derive.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CodexAccountMode, OcxProviderConfig } from "../types";
|
|
2
|
+
import { isAzureIdentityProvider } from "../config/provider-validation";
|
|
2
3
|
import { cloneFastWire } from "./fastwire";
|
|
3
4
|
import {
|
|
4
5
|
PROVIDER_REGISTRY,
|
|
@@ -226,6 +227,7 @@ export function providerConfigSeed(entry: ProviderRegistryEntry): OcxProviderCon
|
|
|
226
227
|
...(entry.modelSuffixBracketStrip !== undefined ? { modelSuffixBracketStrip: entry.modelSuffixBracketStrip } : {}),
|
|
227
228
|
...(entry.staticHeaders ? { headers: { ...entry.staticHeaders } } : {}),
|
|
228
229
|
...(entry.defaultModel ? { defaultModel: entry.defaultModel } : {}),
|
|
230
|
+
...(entry.requestPacing ? { requestPacing: structuredClone(entry.requestPacing) } : {}),
|
|
229
231
|
...(entry.models ? { models: [...entry.models] } : {}),
|
|
230
232
|
...(liveModels !== undefined ? { liveModels } : {}),
|
|
231
233
|
...(entry.contextWindow !== undefined ? { contextWindow: entry.contextWindow } : {}),
|
|
@@ -416,6 +418,7 @@ export function hasLegacyClinePassReasoningEfforts(name: string, prov: OcxProvid
|
|
|
416
418
|
}
|
|
417
419
|
|
|
418
420
|
export function enrichProviderFromRegistry(name: string, prov: OcxProviderConfig): void {
|
|
421
|
+
if (isAzureIdentityProvider(prov)) prov.liveModels = false;
|
|
419
422
|
const entry = PROVIDER_REGISTRY.find(row => row.id === name);
|
|
420
423
|
if (!entry || !providerMatchesRegistryTransportWithStaticGuards(name, prov)) {
|
|
421
424
|
// Name lookup failed, but the row may still point at a vendor route we know. #1100 was
|
|
@@ -438,6 +441,7 @@ export function enrichProviderFromRegistry(name: string, prov: OcxProviderConfig
|
|
|
438
441
|
repairStaticModelCatalogProvider(name, prov);
|
|
439
442
|
if (prov.apiKeyTransport === undefined && seed.apiKeyTransport !== undefined) prov.apiKeyTransport = seed.apiKeyTransport;
|
|
440
443
|
if (!prov.defaultModel && seed.defaultModel) prov.defaultModel = seed.defaultModel;
|
|
444
|
+
if (prov.requestPacing === undefined && seed.requestPacing) prov.requestPacing = structuredClone(seed.requestPacing);
|
|
441
445
|
if (prov.responsesPath === undefined && seed.responsesPath !== undefined) prov.responsesPath = seed.responsesPath;
|
|
442
446
|
// Fill mode only when absent: an explicit persisted `direct` must never be overwritten.
|
|
443
447
|
if (prov.codexAccountMode === undefined && seed.codexAccountMode !== undefined) prov.codexAccountMode = seed.codexAccountMode;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* Modelled after src/codex/routing.ts cooldown logic but scoped to plain API-key pools.
|
|
10
10
|
*/
|
|
11
11
|
import { saveConfigPreservingClaudeCode } from "../config";
|
|
12
|
+
import { isAzureIdentityProvider } from "../config/provider-validation";
|
|
12
13
|
import type { OcxConfig, OcxProviderConfig, RateLimitRetryPolicy } from "../types";
|
|
13
14
|
import { resolveProviderTransport, type OcxProviderTransport } from "./xai-transport";
|
|
14
15
|
import { sweepExpiredOnWrite } from "../lib/state-store-sweeper";
|
|
@@ -84,6 +85,7 @@ function isKeyInCooldown(providerName: string, keyId: string, now = Date.now()):
|
|
|
84
85
|
* Returns true only for key-auth providers with 2+ pool entries.
|
|
85
86
|
*/
|
|
86
87
|
export function hasKeyPoolFailover(provider: OcxProviderConfig): boolean {
|
|
88
|
+
if (isAzureIdentityProvider(provider)) return false;
|
|
87
89
|
if (provider.authMode === "oauth" || provider.authMode === "forward") return false;
|
|
88
90
|
return (provider.apiKeyPool?.length ?? 0) >= 2;
|
|
89
91
|
}
|
|
@@ -96,7 +98,7 @@ export function hasKeyPoolFailover(provider: OcxProviderConfig): boolean {
|
|
|
96
98
|
* callers never re-check fields.
|
|
97
99
|
*/
|
|
98
100
|
export function rateLimitRetryPolicyFor(
|
|
99
|
-
provider: Pick<OcxProviderConfig, "retryOn429" | "authMode">,
|
|
101
|
+
provider: Pick<OcxProviderConfig, "retryOn429" | "authMode" | "adapter" | "azureCredential">,
|
|
100
102
|
): Required<RateLimitRetryPolicy> | null {
|
|
101
103
|
const policy = provider.retryOn429;
|
|
102
104
|
if (!policy || policy.enabled === false) return null;
|
|
@@ -105,6 +107,7 @@ export function rateLimitRetryPolicyFor(
|
|
|
105
107
|
// same token, local runtimes have no remote key to preserve, and unknown/custom values are
|
|
106
108
|
// rejected rather than guessed at.
|
|
107
109
|
if (provider.authMode !== undefined && provider.authMode !== "key") return null;
|
|
110
|
+
if (isAzureIdentityProvider(provider)) return null;
|
|
108
111
|
return {
|
|
109
112
|
enabled: policy.enabled ?? DEFAULT_RATE_LIMIT_RETRY.enabled,
|
|
110
113
|
attempts: policy.attempts ?? DEFAULT_RATE_LIMIT_RETRY.attempts,
|
|
@@ -153,6 +156,7 @@ export function rotateKeyOn429(
|
|
|
153
156
|
): OcxProviderConfig | null {
|
|
154
157
|
const provider = config.providers[providerName];
|
|
155
158
|
if (!provider) return null;
|
|
159
|
+
if (isAzureIdentityProvider(provider)) return null;
|
|
156
160
|
if (provider.authMode === "oauth" || provider.authMode === "forward") return null;
|
|
157
161
|
|
|
158
162
|
const pool = provider.apiKeyPool;
|
|
@@ -237,6 +237,7 @@ function hasKnownLegacyOpenAiReference(config: OcxConfig): boolean {
|
|
|
237
237
|
|| matchesList(config.subagentModels)
|
|
238
238
|
|| (config.subagentRoles ?? []).some(role => matches(role.model))
|
|
239
239
|
|| matches(config.injectionModel)
|
|
240
|
+
|| matches(config.v2NativeParentOverride?.model)
|
|
240
241
|
|| matches(config.shadowCallIntercept?.model)
|
|
241
242
|
|| matches(config.webSearchSidecar?.model)
|
|
242
243
|
|| matches(config.visionSidecar?.model)
|
|
@@ -260,6 +261,9 @@ function rewriteLegacyOpenAiReferences(config: OcxConfig, warnings: string[]): v
|
|
|
260
261
|
}
|
|
261
262
|
}
|
|
262
263
|
if (config.injectionModel) config.injectionModel = rewriteLegacyOpenAiSelectedId(config.injectionModel);
|
|
264
|
+
if (config.v2NativeParentOverride?.model) {
|
|
265
|
+
config.v2NativeParentOverride.model = rewriteLegacyOpenAiSelectedId(config.v2NativeParentOverride.model);
|
|
266
|
+
}
|
|
263
267
|
if (config.shadowCallIntercept?.model) {
|
|
264
268
|
config.shadowCallIntercept.model = rewriteLegacyOpenAiSelectedId(config.shadowCallIntercept.model);
|
|
265
269
|
}
|
|
@@ -305,6 +309,7 @@ function isKnownLegacyValuePath(path: readonly string[]): boolean {
|
|
|
305
309
|
if (/^providers\.(openai|openai-multi)\.selectedModels\.\d+$/.test(joined)) return true;
|
|
306
310
|
return new Set([
|
|
307
311
|
"injectionModel",
|
|
312
|
+
"v2NativeParentOverride.model",
|
|
308
313
|
"shadowCallIntercept.model",
|
|
309
314
|
"webSearchSidecar.model",
|
|
310
315
|
"visionSidecar.model",
|
|
@@ -82,6 +82,7 @@ export function rewriteProviderReferences(config: OcxConfig, from: string, to: s
|
|
|
82
82
|
|
|
83
83
|
const scalarOwners: Array<[Record<string, unknown> | undefined, string]> = [
|
|
84
84
|
[config as unknown as Record<string, unknown>, "injectionModel"],
|
|
85
|
+
[config.v2NativeParentOverride as Record<string, unknown> | undefined, "model"],
|
|
85
86
|
[config.shadowCallIntercept as Record<string, unknown> | undefined, "model"],
|
|
86
87
|
[config.webSearchSidecar as Record<string, unknown> | undefined, "model"],
|
|
87
88
|
[config.visionSidecar as Record<string, unknown> | undefined, "model"],
|
package/src/providers/quota.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { isMainAccountIdentityGenerationLive } from "../codex/main-account-cache
|
|
|
8
8
|
import { MAIN_CODEX_ACCOUNT_ID } from "../codex/main-account";
|
|
9
9
|
import { codexPlanKey } from "../codex/plan";
|
|
10
10
|
import { resolveEnvValue } from "../config";
|
|
11
|
-
import { getValidAccessToken, getValidAccessTokenForAccount } from "../oauth";
|
|
11
|
+
import { getValidAccessToken, getValidAccessTokenForAccount, getValidAccessTokenSnapshot, getValidAccessTokenSnapshotForAccount, type OAuthAccessSnapshot } from "../oauth";
|
|
12
12
|
import { getAccountCredential, getAccountSet, getCredential } from "../oauth/store";
|
|
13
13
|
import { antigravityUserAgent } from "../adapters/client-fingerprint";
|
|
14
14
|
import { apiKeyPoolEntryId } from "./api-keys";
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
isTerminalAntigravityQuotaStatus,
|
|
35
35
|
} from "./antigravity-quota";
|
|
36
36
|
import { antigravityHostCandidates, isAntigravityHttpsHost } from "../adapters/google-antigravity-hosts";
|
|
37
|
+
import { providerTlsFetch } from "../lib/provider-tls-profile";
|
|
37
38
|
|
|
38
39
|
/** Match oauth/index REFRESH_SKEW_MS — use stored access without refresh when still fresh. */
|
|
39
40
|
const ACCOUNT_TOKEN_SKEW_MS = 60_000;
|
|
@@ -1400,7 +1401,7 @@ export interface ProviderAccountQuota {
|
|
|
1400
1401
|
|
|
1401
1402
|
/** Providers whose per-account quota can be probed. Extend as other OAuth APIs are covered. */
|
|
1402
1403
|
export function supportsPerAccountQuota(provider: string): boolean {
|
|
1403
|
-
return provider === "anthropic";
|
|
1404
|
+
return provider === "anthropic" || provider === "google-antigravity";
|
|
1404
1405
|
}
|
|
1405
1406
|
|
|
1406
1407
|
function accountCacheKey(provider: string, accountId: string): string {
|
|
@@ -1519,8 +1520,13 @@ async function fetchAccountQuota(
|
|
|
1519
1520
|
|
|
1520
1521
|
const probe = (async (): Promise<AccountQuotaCacheEntry> => {
|
|
1521
1522
|
try {
|
|
1522
|
-
|
|
1523
|
-
|
|
1523
|
+
let quota: ProviderQuota | null = null;
|
|
1524
|
+
if (provider === "anthropic") {
|
|
1525
|
+
const token = await getTokenForAccountQuotaProbe(provider, accountId);
|
|
1526
|
+
quota = await fetchAnthropicUsageQuota(token);
|
|
1527
|
+
} else if (provider === "google-antigravity") {
|
|
1528
|
+
quota = await fetchAntigravityAccountQuota(accountId);
|
|
1529
|
+
}
|
|
1524
1530
|
if (!quota) {
|
|
1525
1531
|
// Preserve last-good bars and mark unavailable; advance TTL so failures
|
|
1526
1532
|
// negative-cache instead of re-probing on every GUI poll.
|
|
@@ -2077,23 +2083,63 @@ function antigravityUsedPercent(quotaInfo: Record<string, unknown>): number | un
|
|
|
2077
2083
|
return normalizePercent(100 - remaining);
|
|
2078
2084
|
}
|
|
2079
2085
|
|
|
2086
|
+
async function fetchAntigravityAccountQuota(accountId: string): Promise<ProviderQuota | null> {
|
|
2087
|
+
let snapshot: OAuthAccessSnapshot;
|
|
2088
|
+
try {
|
|
2089
|
+
snapshot = await getValidAccessTokenSnapshotForAccount("google-antigravity", accountId);
|
|
2090
|
+
} catch {
|
|
2091
|
+
return null;
|
|
2092
|
+
}
|
|
2093
|
+
if (!snapshot.projectId) return null;
|
|
2094
|
+
const entry = getProviderRegistryEntry("google-antigravity");
|
|
2095
|
+
const baseUrl = (entry?.baseUrl || "https://daily-cloudcode-pa.googleapis.com").replace(/\/+$/, "");
|
|
2096
|
+
const providerConfig: Pick<OcxProviderConfig, "adapter" | "authMode" | "baseUrl" | "googleMode" | "tlsProfile"> = {
|
|
2097
|
+
adapter: "google",
|
|
2098
|
+
authMode: "oauth",
|
|
2099
|
+
googleMode: "cloud-code-assist",
|
|
2100
|
+
baseUrl,
|
|
2101
|
+
};
|
|
2102
|
+
const fetchImpl = providerTlsFetch("google-antigravity", providerConfig, globalThis.fetch);
|
|
2103
|
+
let liveQuota: ProviderQuota | null;
|
|
2104
|
+
try {
|
|
2105
|
+
liveQuota = await fetchAntigravityLiveQuota({
|
|
2106
|
+
accessToken: snapshot.accessToken,
|
|
2107
|
+
projectId: snapshot.projectId,
|
|
2108
|
+
baseUrl,
|
|
2109
|
+
timeoutMs: REQUEST_TIMEOUT_MS,
|
|
2110
|
+
fetchImpl,
|
|
2111
|
+
});
|
|
2112
|
+
} catch (error) {
|
|
2113
|
+
if (error instanceof AntigravityQuotaRpcError && isTerminalAntigravityQuotaStatus(error.status)) {
|
|
2114
|
+
return null;
|
|
2115
|
+
}
|
|
2116
|
+
liveQuota = null;
|
|
2117
|
+
}
|
|
2118
|
+
if (!liveQuota) return null;
|
|
2119
|
+
return {
|
|
2120
|
+
...liveQuota,
|
|
2121
|
+
updatedAt: Date.now(),
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2080
2125
|
async function fetchAntigravityQuota(provider: string, config: OcxProviderConfig): Promise<ProviderQuotaProbeResult> {
|
|
2081
|
-
|
|
2082
|
-
if (!credential?.projectId) return null;
|
|
2083
|
-
let accessToken: string;
|
|
2126
|
+
let snapshot: OAuthAccessSnapshot;
|
|
2084
2127
|
try {
|
|
2085
|
-
|
|
2128
|
+
snapshot = await getValidAccessTokenSnapshot("google-antigravity");
|
|
2086
2129
|
} catch {
|
|
2087
2130
|
return null;
|
|
2088
2131
|
}
|
|
2132
|
+
if (!snapshot.projectId) return null;
|
|
2089
2133
|
const baseUrl = (config.baseUrl || "https://daily-cloudcode-pa.googleapis.com").replace(/\/+$/, "");
|
|
2134
|
+
const fetchImpl = providerTlsFetch(provider, config, globalThis.fetch);
|
|
2090
2135
|
let liveQuota: ProviderQuota | null;
|
|
2091
2136
|
try {
|
|
2092
2137
|
liveQuota = await fetchAntigravityLiveQuota({
|
|
2093
|
-
accessToken,
|
|
2094
|
-
projectId:
|
|
2138
|
+
accessToken: snapshot.accessToken,
|
|
2139
|
+
projectId: snapshot.projectId,
|
|
2095
2140
|
baseUrl,
|
|
2096
2141
|
timeoutMs: REQUEST_TIMEOUT_MS,
|
|
2142
|
+
fetchImpl,
|
|
2097
2143
|
});
|
|
2098
2144
|
} catch (error) {
|
|
2099
2145
|
if (error instanceof AntigravityQuotaRpcError && isTerminalAntigravityQuotaStatus(error.status)) {
|
|
@@ -2106,15 +2152,15 @@ async function fetchAntigravityQuota(provider: string, config: OcxProviderConfig
|
|
|
2106
2152
|
for (const [index, host] of antigravityHostCandidates(baseUrl).entries()) {
|
|
2107
2153
|
if (!isAntigravityHttpsHost(host)) continue;
|
|
2108
2154
|
try {
|
|
2109
|
-
const response = await
|
|
2155
|
+
const response = await fetchImpl(`${host}/v1internal:fetchAvailableModels`, {
|
|
2110
2156
|
method: "POST",
|
|
2111
2157
|
headers: {
|
|
2112
2158
|
Accept: "application/json",
|
|
2113
2159
|
"Content-Type": "application/json",
|
|
2114
2160
|
"User-Agent": antigravityUserAgent(),
|
|
2115
|
-
Authorization: `Bearer ${accessToken}`,
|
|
2161
|
+
Authorization: `Bearer ${snapshot.accessToken}`,
|
|
2116
2162
|
},
|
|
2117
|
-
body: JSON.stringify({ project:
|
|
2163
|
+
body: JSON.stringify({ project: snapshot.projectId }),
|
|
2118
2164
|
redirect: "error",
|
|
2119
2165
|
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
2120
2166
|
});
|
|
@@ -161,6 +161,7 @@ export interface ProviderRegistryEntry {
|
|
|
161
161
|
note?: string;
|
|
162
162
|
dashboardUrl?: string;
|
|
163
163
|
defaultModel?: string;
|
|
164
|
+
requestPacing?: OcxProviderConfig["requestPacing"];
|
|
164
165
|
models?: string[];
|
|
165
166
|
liveModels?: boolean;
|
|
166
167
|
/**
|
|
@@ -442,7 +443,7 @@ const THINKING_TOGGLE_MAP: Record<string, string> = {
|
|
|
442
443
|
max: "enabled",
|
|
443
444
|
};
|
|
444
445
|
const OPENCODE_GO_THINKING_TOGGLE_MODELS = [
|
|
445
|
-
"mimo-v2.5", "mimo-v2.5-pro", "
|
|
446
|
+
"mimo-v2.5", "mimo-v2.5-pro", "glm-5", "glm-5.1",
|
|
446
447
|
];
|
|
447
448
|
/**
|
|
448
449
|
* Zhipu's domestic BigModel platform. Text families first, then the vision member: modalities are
|
|
@@ -1597,7 +1598,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
1597
1598
|
// 2026-07-10: defaultModel is frozen pending Vertex-specific Tier-2 evidence; Gemini API
|
|
1598
1599
|
// evidence from ai.google.dev does not establish Vertex publisher availability.
|
|
1599
1600
|
{ id: "google-vertex", label: "Google Vertex AI", adapter: "google", baseUrl: "https://aiplatform.googleapis.com", authKind: "key", dashboardUrl: "https://console.cloud.google.com/vertex-ai", defaultModel: "gemini-3-pro", googleMode: "vertex", jawcodeBundle: "google", extraMetadataAliases: ["gemini-vertex"] },
|
|
1600
|
-
{ id: "google-antigravity", label: "Google Antigravity", adapter: "google", baseUrl: "https://daily-cloudcode-pa.googleapis.com", authKind: "oauth", allowBaseUrlOverride: true, dashboardUrl: "https://antigravity.google", models: ANTIGRAVITY_MODELS, liveModels: true, defaultModel: "gemini-3.7-flash", modelContextWindows: ANTIGRAVITY_MODEL_CONTEXT_WINDOWS, modelInputModalities: ANTIGRAVITY_MODEL_INPUT_MODALITIES, modelReasoningEfforts: ANTIGRAVITY_MODEL_EFFORTS, googleMode: "cloud-code-assist", jawcodeBundle: "google", extraMetadataAliases: ["antigravity", "gemini-antigravity"] },
|
|
1601
|
+
{ id: "google-antigravity", label: "Google Antigravity", adapter: "google", baseUrl: "https://daily-cloudcode-pa.googleapis.com", authKind: "oauth", allowBaseUrlOverride: true, dashboardUrl: "https://antigravity.google", models: ANTIGRAVITY_MODELS, liveModels: true, defaultModel: "gemini-3.7-flash", requestPacing: { enabled: true, requestsPerMinute: 30, minIntervalMs: 2_000, jitterMs: 500 }, modelContextWindows: ANTIGRAVITY_MODEL_CONTEXT_WINDOWS, modelInputModalities: ANTIGRAVITY_MODEL_INPUT_MODALITIES, modelReasoningEfforts: ANTIGRAVITY_MODEL_EFFORTS, googleMode: "cloud-code-assist", jawcodeBundle: "google", extraMetadataAliases: ["antigravity", "gemini-antigravity"] },
|
|
1601
1602
|
{ id: "azure-openai", label: "Azure OpenAI", adapter: "azure-openai", baseUrl: "https://{resource}.openai.azure.com/openai", authKind: "key", featured: true, dashboardUrl: "https://portal.azure.com" },
|
|
1602
1603
|
{ id: "ollama", label: "Ollama (local)", adapter: "openai-chat", baseUrl: "http://localhost:11434/v1", authKind: "local", allowPrivateNetworkByDefault: true, allowBaseUrlOverride: true, featured: true, note: "Local — key usually blank" },
|
|
1603
1604
|
{ id: "vllm", label: "vLLM (local)", adapter: "openai-chat", baseUrl: "http://localhost:8000/v1", authKind: "local", allowPrivateNetworkByDefault: true, allowBaseUrlOverride: true, featured: true, note: "Local — key usually blank" },
|
|
@@ -1867,6 +1868,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
1867
1868
|
liveModels: true,
|
|
1868
1869
|
preserveCustomDestination: true,
|
|
1869
1870
|
defaultModel: "deepseek/deepseek-v4-flash",
|
|
1871
|
+
promptCacheKey: true,
|
|
1870
1872
|
// The default is also the cold-start seed: live discovery failure must not empty the catalog
|
|
1871
1873
|
// for a freshly configured provider with no stale cache (issue #308 pattern).
|
|
1872
1874
|
models: ["deepseek/deepseek-v4-flash"],
|