@bitkyc08/opencodex 2.76.0 → 2.77.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/gui/dist/assets/App-COdEeqSj.css +1 -0
- package/gui/dist/assets/App-DEpCJEP_.js +54 -0
- package/gui/dist/assets/{Tray-DjNwwrWS.js → Tray-gloIYNaW.js} +1 -1
- package/gui/dist/assets/index-Cd7ittMv.css +1 -0
- package/gui/dist/assets/index-DwJKFJm5.js +94 -0
- package/gui/dist/assets/quota-summary-DGcEJU3h.js +1 -0
- package/gui/dist/index.html +4 -3
- package/package.json +1 -1
- package/src/adapters/antigravity-refusal.ts +28 -0
- package/src/adapters/cursor/protobuf-request.ts +6 -0
- package/src/adapters/cursor/request-builder.ts +3 -0
- package/src/adapters/cursor/structured-output.ts +13 -0
- package/src/adapters/cursor/types.ts +2 -0
- package/src/adapters/ollama-native.ts +29 -2
- package/src/adapters/openai-responses/passthrough.ts +133 -103
- package/src/adapters/openai-responses/request-strips.ts +10 -4
- package/src/adapters/zed.ts +26 -4
- package/src/bridge/response-json.ts +14 -14
- package/src/bridge/sse.ts +15 -5
- package/src/chatgpt/app-server-shim/bundle-trust.ts +40 -10
- package/src/chatgpt/app-server-shim/gate-rewrite.ts +3 -0
- package/src/claude/agents-inject.ts +4 -43
- package/src/claude/gateway-cache.ts +16 -13
- package/src/claude/inbound-model-options.ts +9 -2
- package/src/claude/intercept/cli-catalog.ts +27 -16
- package/src/claude/intercept/picker-bootstrap.ts +37 -16
- package/src/claude/intercept/picker-budget.ts +41 -0
- package/src/claude/outbound.ts +2 -0
- package/src/claude/subagent-force-status.ts +53 -0
- package/src/claude/subagent-model.ts +93 -0
- package/src/cli/account-api.ts +3 -0
- package/src/cli/account.ts +3 -1
- package/src/cli/agent.ts +10 -0
- package/src/cli/capabilities.ts +9 -0
- package/src/cli/chatgpt-command.ts +10 -7
- package/src/cli/claude.ts +22 -6
- package/src/cli/codex-shim-readiness.ts +89 -0
- package/src/cli/connect.ts +19 -0
- package/src/cli/dispatch.ts +3 -3
- package/src/cli/export-command.ts +20 -9
- package/src/cli/gui.ts +8 -3
- package/src/cli/help-catalog.ts +84 -0
- package/src/cli/help-models-context.ts +17 -0
- package/src/cli/help-navigation.ts +50 -0
- package/src/cli/help-recovery.ts +66 -0
- package/src/cli/help.ts +52 -8
- package/src/cli/models-runtime.ts +2 -1
- package/src/cli/opencode.ts +54 -10
- package/src/cli/provider.ts +3 -29
- package/src/cli/registry.ts +29 -5
- package/src/cli/root.ts +30 -9
- package/src/cli/runtime-api.ts +1 -1
- package/src/cli/tray-proxy.ts +3 -2
- package/src/clients/config-export/contracts.ts +100 -0
- package/src/clients/config-export/droid.ts +54 -2
- package/src/clients/config-export/kilo.ts +12 -2
- package/src/clients/config-export/model-metadata.ts +10 -4
- package/src/clients/config-export/reasoning-metadata.ts +47 -0
- package/src/clients/config-export.ts +46 -66
- package/src/clients/omo-role-models.ts +17 -3
- package/src/codex/account-credit-use.ts +4 -2
- package/src/codex/account-runtime-state.ts +21 -0
- package/src/codex/account-store.ts +11 -47
- package/src/codex/account-usability.ts +11 -8
- package/src/codex/auth-api/account-list.ts +73 -63
- package/src/codex/auth-api/main-account-probe.ts +25 -12
- package/src/codex/auth-api/pool-mode-gate.ts +6 -9
- package/src/codex/auth-api/pool-quota-probe.ts +2 -4
- package/src/codex/auth-api/reset-credit-service.ts +3 -2
- package/src/codex/auth-context.ts +30 -11
- package/src/codex/catalog/aggregation.ts +3 -0
- package/src/codex/catalog/model-hints.ts +2 -1
- package/src/codex/catalog/model-visibility.ts +6 -3
- package/src/codex/catalog/parsing.ts +2 -0
- package/src/codex/catalog/provider-models.ts +13 -4
- package/src/codex/catalog/retained-sync.ts +2 -1
- package/src/codex/chatgpt-refresh-failure.ts +56 -0
- package/src/codex/convergence.ts +2 -1
- package/src/codex/main-account.ts +144 -11
- package/src/codex/quota-refresh-outcome.ts +17 -2
- package/src/codex/quota-types.ts +33 -1
- package/src/codex/quota.ts +56 -15
- package/src/codex/routing/cooldown-math.ts +10 -4
- package/src/codex/routing/selection.ts +11 -4
- package/src/codex/routing.ts +9 -9
- package/src/codex/shim-path-resolution.ts +133 -0
- package/src/codex/shim.ts +39 -39
- package/src/codex/subagent-model-fallback.ts +2 -1
- package/src/combos/failover.ts +82 -1
- package/src/combos/jev.ts +13 -3
- package/src/config/diagnostics.ts +4 -0
- package/src/config/load-degrade.ts +4 -0
- package/src/config/provider-validation.ts +3 -0
- package/src/config/schema/compaction-triggers.ts +8 -0
- package/src/config/schema/config-schema.ts +11 -0
- package/src/config/schema/leaf-validators.ts +5 -1
- package/src/config/subagent-models.ts +12 -0
- package/src/generated/compatibility-version.json +245 -153
- package/src/grok/session-identity.ts +33 -0
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/mutation-plan.ts +4 -2
- package/src/integrations/state.ts +68 -6
- package/src/integrations/writer.ts +18 -4
- package/src/lib/gui-pair-capability.ts +17 -0
- package/src/lib/provider-client-headers.ts +62 -0
- package/src/oauth/anthropic-continuity.ts +65 -0
- package/src/oauth/anthropic-identity.ts +75 -0
- package/src/oauth/anthropic-routing.ts +3 -3
- package/src/oauth/anthropic.ts +7 -1
- package/src/oauth/generic-account-failover.ts +21 -0
- package/src/oauth/health.ts +32 -5
- package/src/oauth/index.ts +36 -27
- package/src/oauth/local-token-detect.ts +7 -0
- package/src/oauth/refresh-policy.ts +2 -0
- package/src/oauth/store.ts +55 -6
- package/src/oauth/types.ts +9 -0
- package/src/protocols/encoders/messages.ts +4 -3
- package/src/providers/antigravity-effort-families.ts +75 -0
- package/src/providers/antigravity-models.ts +54 -16
- package/src/providers/antigravity-wire-snapshot.ts +66 -0
- package/src/providers/derive.ts +2 -0
- package/src/providers/model-rename-fields.ts +3 -0
- package/src/providers/new-model-policy.ts +48 -10
- package/src/providers/registry/entries-extended.ts +3 -0
- package/src/providers/registry/model-ids.ts +2 -0
- package/src/providers/registry/model-seeds.ts +5 -0
- package/src/providers/registry/types.ts +4 -0
- package/src/responses/compaction.ts +19 -0
- package/src/router.ts +6 -0
- package/src/server/auth-cors.ts +9 -0
- package/src/server/chat-completions.ts +10 -0
- package/src/server/claude-messages.ts +18 -11
- package/src/server/droid-reasoning-default.ts +50 -0
- package/src/server/github-copilot-responses-repair.ts +3 -6
- package/src/server/grok-responses-control-frame.ts +2 -2
- package/src/server/grok-responses-snapshot-repair.ts +4 -5
- package/src/server/gui-session.ts +33 -11
- package/src/server/index/claude-intercept-lifecycle.ts +4 -0
- package/src/server/index/serve-options.ts +2 -1
- package/src/server/management/agent-settings-routes.ts +6 -129
- package/src/server/management/config-routes.ts +1 -1
- package/src/server/management/integration-routes.ts +64 -6
- package/src/server/management/link-routes.ts +20 -13
- package/src/server/management/model-row-export-metadata.ts +150 -0
- package/src/server/management/model-rows.ts +148 -9
- package/src/server/management/route-registry.ts +4 -3
- package/src/server/management/shared.ts +19 -5
- package/src/server/management/subagent-model-routes.ts +214 -0
- package/src/server/relay.ts +14 -44
- package/src/server/request-log-generation-window.ts +38 -0
- package/src/server/request-log.ts +13 -0
- package/src/server/responses/adapter-dispatch.ts +100 -1
- package/src/server/responses/codex-auth-error.ts +10 -2
- package/src/server/responses/combo-stream-preflight.ts +29 -3
- package/src/server/responses/compaction-routing.ts +10 -1
- package/src/server/responses/core-auth.ts +57 -26
- package/src/server/responses/core-codex-account.ts +6 -3
- package/src/server/responses/core-combo-failure.ts +5 -1
- package/src/server/responses/core-combo-native.ts +7 -1
- package/src/server/responses/core-combo.ts +18 -1
- package/src/server/responses/core-normalize.ts +7 -0
- package/src/server/responses/core-options.ts +5 -0
- package/src/server/responses/fetch-helpers.ts +9 -0
- package/src/server/responses/passthrough-delivery.ts +1 -1
- package/src/server/responses/policy-fallback.ts +1 -1
- package/src/server/responses/request-prepare.ts +5 -0
- package/src/server/responses/request-spend.ts +30 -30
- package/src/server/responses-custom-tool-repair.ts +2 -2
- package/src/server/responses-hosted-image-display.ts +70 -9
- package/src/server/responses-terminal-repair.ts +15 -2
- package/src/server/sse-frame-buffer.ts +38 -36
- package/src/server/sse-payload-rewrite.ts +78 -49
- package/src/server/ws-bridge.ts +1 -11
- package/src/service/windows-taskxml.ts +1 -2
- package/src/service-manager-probe.ts +36 -2
- package/src/types/config.ts +4 -0
- package/src/types/provider.ts +18 -0
- package/src/update/npm-invocation.mjs +35 -3
- package/src/update/proxy-liveness-probe.mjs +38 -8
- package/src/usage/expected-prices.ts +9 -0
- package/src/usage/log.ts +7 -0
- package/src/usage/summary.ts +82 -1
- package/src/web-search/loop.ts +11 -8
- package/src/web-search/run-turn-loop.ts +10 -7
- package/gui/dist/assets/App-CddorHdA.js +0 -51
- package/gui/dist/assets/App-DMudtISW.css +0 -1
- package/gui/dist/assets/index-Btze1I3T.css +0 -1
- package/gui/dist/assets/index-CvnY7E6i.js +0 -86
- package/gui/dist/assets/quota-summary-B_AWpHfP.js +0 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { isValidModelDiscoveryModelId, MODEL_DISCOVERY_MAX_MODELS } from "./model-discovery-limits";
|
|
2
|
-
import { isModelCacheGenerationCurrent } from "../codex/model-cache";
|
|
2
|
+
import { captureModelCacheGeneration, isModelCacheGenerationCurrent } from "../codex/model-cache";
|
|
3
|
+
import { getConfigDir } from "../config/paths";
|
|
4
|
+
import { readAntigravityWireSnapshot, writeAntigravityWireSnapshot, type AntigravitySuffixMap } from "./antigravity-wire-snapshot";
|
|
3
5
|
|
|
4
6
|
// Google Antigravity (Cloud Code Assist) bundled model list.
|
|
5
7
|
//
|
|
@@ -7,8 +9,8 @@ import { isModelCacheGenerationCurrent } from "../codex/model-cache";
|
|
|
7
9
|
// CLI resolves labels against. The ids below separate CCA wire ids, collapsed picker entries,
|
|
8
10
|
// and hidden compatibility aliases for saved selections. The CCA envelope's `model` field must
|
|
9
11
|
// receive the wire id (for example "Gemini 3.1 Pro (High)" => gemini-pro-agent), while the
|
|
10
|
-
// picker
|
|
11
|
-
//
|
|
12
|
+
// picker collapses complete low/medium/high families discovered by CCA, including new versions.
|
|
13
|
+
// Partial families remain wire-addressable rather than advertising an invented effort ladder.
|
|
12
14
|
|
|
13
15
|
// ── Wire IDs (what CCA :fetchAvailableModels returns) ──
|
|
14
16
|
|
|
@@ -121,10 +123,12 @@ function pickerModelIdForDiscoveredWireId(
|
|
|
121
123
|
const effortMatch = /^(.*)-(low|medium|high)$/.exec(wireId);
|
|
122
124
|
if (effortMatch) {
|
|
123
125
|
const baseId = effortMatch[1]!;
|
|
124
|
-
if (
|
|
126
|
+
if (isValidModelDiscoveryModelId(baseId)
|
|
125
127
|
&& ANTIGRAVITY_DISCOVERY_EFFORTS.every(effort => available.has(`${baseId}-${effort}`))) {
|
|
126
128
|
return baseId;
|
|
127
129
|
}
|
|
130
|
+
// A shared display label must not collapse an incomplete family or rename its wire tier.
|
|
131
|
+
return wireId;
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
// Display labels are a LAST resort, never a first one. CCA labels a tier row
|
|
@@ -200,12 +204,13 @@ function completeDiscoveredEffortWireModelIds(
|
|
|
200
204
|
pickerId: string,
|
|
201
205
|
available: ReadonlyMap<string, Record<string, unknown>>,
|
|
202
206
|
): AntigravityEffortWireModelIds | undefined {
|
|
203
|
-
const explicitEffortMap = ANTIGRAVITY_EFFORT_WIRE_MAP
|
|
207
|
+
const explicitEffortMap = Object.hasOwn(ANTIGRAVITY_EFFORT_WIRE_MAP, pickerId)
|
|
208
|
+
? ANTIGRAVITY_EFFORT_WIRE_MAP[pickerId] : undefined;
|
|
204
209
|
if (explicitEffortMap && Object.values(explicitEffortMap).every(wireId => available.has(wireId))) {
|
|
205
210
|
return { ...explicitEffortMap };
|
|
206
211
|
}
|
|
207
212
|
|
|
208
|
-
if (!
|
|
213
|
+
if (!isValidModelDiscoveryModelId(pickerId)) return undefined;
|
|
209
214
|
const suffixEffortMap: AntigravityEffortWireModelIds = {};
|
|
210
215
|
for (const effort of ANTIGRAVITY_DISCOVERY_EFFORTS) {
|
|
211
216
|
const wireId = `${pickerId}-${effort}`;
|
|
@@ -364,7 +369,7 @@ interface DiscoveredWireModelMapping {
|
|
|
364
369
|
readonly generation?: { provider: string; cacheGeneration: string };
|
|
365
370
|
}
|
|
366
371
|
|
|
367
|
-
const discoveredWireModelsByBaseUrl = new Map<string, DiscoveredWireModelMapping>();
|
|
372
|
+
const discoveredWireModelsByBaseUrl = new Map<string, DiscoveredWireModelMapping | null>();
|
|
368
373
|
|
|
369
374
|
/**
|
|
370
375
|
* Strip trailing slashes without a backtracking regex.
|
|
@@ -387,9 +392,9 @@ function antigravityBaseUrlKey(baseUrl: string | undefined): string | undefined
|
|
|
387
392
|
const url = new URL(trimmed);
|
|
388
393
|
url.hash = "";
|
|
389
394
|
url.search = "";
|
|
390
|
-
return stripTrailingSlashes(url.toString())
|
|
395
|
+
return stripTrailingSlashes(url.toString());
|
|
391
396
|
} catch {
|
|
392
|
-
return trimmed
|
|
397
|
+
return trimmed;
|
|
393
398
|
}
|
|
394
399
|
}
|
|
395
400
|
|
|
@@ -401,13 +406,23 @@ export function registerAntigravityDiscoveredWireModels(
|
|
|
401
406
|
): void {
|
|
402
407
|
const key = antigravityBaseUrlKey(baseUrl);
|
|
403
408
|
if (!key) return;
|
|
409
|
+
if (generation) {
|
|
410
|
+
if (!isModelCacheGenerationCurrent(generation.provider, generation.cacheGeneration)) return;
|
|
411
|
+
const families: Record<string, AntigravitySuffixMap> = Object.create(null);
|
|
412
|
+
for (const model of models) {
|
|
413
|
+
if (ANTIGRAVITY_DISCOVERY_EFFORTS.every(effort => model.effortWireModelIds?.[effort] === `${model.id}-${effort}`)) {
|
|
414
|
+
families[model.id] = { low: `${model.id}-low`, medium: `${model.id}-medium`, high: `${model.id}-high` };
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
writeAntigravityWireSnapshot(key, { version: 1, provider: generation.provider, families });
|
|
418
|
+
}
|
|
404
419
|
const wireModels = new Map<string, string>();
|
|
405
420
|
const effortModels = new Map<string, AntigravityEffortWireModelIds>();
|
|
406
421
|
for (const model of models) {
|
|
407
422
|
wireModels.set(model.id, model.wireModelId);
|
|
408
423
|
if (model.effortWireModelIds) effortModels.set(model.id, { ...model.effortWireModelIds });
|
|
409
424
|
}
|
|
410
|
-
discoveredWireModelsByBaseUrl.set(key
|
|
425
|
+
discoveredWireModelsByBaseUrl.set(`${getConfigDir()}\0${key}`, {
|
|
411
426
|
models: wireModels,
|
|
412
427
|
effortModels,
|
|
413
428
|
...(generation ? { generation } : {}),
|
|
@@ -419,11 +434,21 @@ function discoveredAntigravityMapping(
|
|
|
419
434
|
): DiscoveredWireModelMapping | undefined {
|
|
420
435
|
const key = antigravityBaseUrlKey(baseUrl);
|
|
421
436
|
if (!key) return undefined;
|
|
422
|
-
const
|
|
437
|
+
const scopedKey = `${getConfigDir()}\0${key}`;
|
|
438
|
+
if (!discoveredWireModelsByBaseUrl.has(scopedKey)) {
|
|
439
|
+
const saved = readAntigravityWireSnapshot(key);
|
|
440
|
+
discoveredWireModelsByBaseUrl.set(scopedKey, saved ? {
|
|
441
|
+
models: new Map(Object.entries(saved.families).map(([id, map]) => [id, map.medium])),
|
|
442
|
+
effortModels: new Map(Object.entries(saved.families)),
|
|
443
|
+
generation: { provider: saved.provider, cacheGeneration: captureModelCacheGeneration(saved.provider) },
|
|
444
|
+
} : null);
|
|
445
|
+
}
|
|
446
|
+
const mapping = discoveredWireModelsByBaseUrl.get(scopedKey);
|
|
423
447
|
if (!mapping) return undefined;
|
|
424
448
|
if (mapping.generation
|
|
425
449
|
&& !isModelCacheGenerationCurrent(mapping.generation.provider, mapping.generation.cacheGeneration)) {
|
|
426
|
-
|
|
450
|
+
// Keep a tombstone: clearing account/cache authority must not reload old disk evidence.
|
|
451
|
+
discoveredWireModelsByBaseUrl.set(scopedKey, null);
|
|
427
452
|
return undefined;
|
|
428
453
|
}
|
|
429
454
|
return mapping;
|
|
@@ -450,7 +475,8 @@ function discoveredAntigravityEffortWireModelId(
|
|
|
450
475
|
}
|
|
451
476
|
|
|
452
477
|
const defaultEffort = ANTIGRAVITY_DEFAULT_EFFORT[modelId]
|
|
453
|
-
?? ANTIGRAVITY_THINKING_LEVEL_MODELS[modelId]
|
|
478
|
+
?? ANTIGRAVITY_THINKING_LEVEL_MODELS[modelId]
|
|
479
|
+
?? "medium";
|
|
454
480
|
if (defaultEffort && isAntigravityDiscoveryEffort(defaultEffort) && effortMap[defaultEffort]) {
|
|
455
481
|
return effortMap[defaultEffort];
|
|
456
482
|
}
|
|
@@ -573,20 +599,27 @@ export function parseAntigravityAvailableModels(
|
|
|
573
599
|
if (seen.has(id)) continue;
|
|
574
600
|
seen.add(id);
|
|
575
601
|
const effortWireModelIds = completeDiscoveredEffortWireModelIds(id, available);
|
|
602
|
+
const tierInfo = effortWireModelIds
|
|
603
|
+
? Object.values(effortWireModelIds).map(wire => available.get(wire)!) : [info];
|
|
604
|
+
const windows = tierInfo.map(tier => antigravityPositiveInteger(tier.maxTokens));
|
|
605
|
+
const contextWindow = windows.every((window): window is number => window !== undefined)
|
|
606
|
+
? Math.min(...windows) : undefined;
|
|
607
|
+
const supportsImages = tierInfo.every(tier => tier.supportsImages === true) ? true
|
|
608
|
+
: tierInfo.some(tier => tier.supportsImages === false) ? false : undefined;
|
|
576
609
|
out.push({
|
|
577
610
|
id,
|
|
578
611
|
wireModelId: wireId,
|
|
579
612
|
...(effortWireModelIds ? { effortWireModelIds } : {}),
|
|
580
|
-
...(
|
|
613
|
+
...(contextWindow ? { contextWindow } : {}),
|
|
581
614
|
// Tri-state, deliberately not a ternary: `true` asserts image support,
|
|
582
615
|
// `false` asserts against it, and ABSENT is unknown. Collapsing absent into
|
|
583
616
|
// `["text"]` let routing read it as a confident `image: false` (#1796). The
|
|
584
617
|
// strict catalog still receives its `["text"]` compatibility default
|
|
585
618
|
// downstream via ensureStrictCatalogFields; only the routing-evidence
|
|
586
619
|
// channel stays honest about what was never asserted.
|
|
587
|
-
...(
|
|
620
|
+
...(supportsImages === true
|
|
588
621
|
? { inputModalities: ["text", "image"] as string[] }
|
|
589
|
-
:
|
|
622
|
+
: supportsImages === false
|
|
590
623
|
? { inputModalities: ["text"] as string[] }
|
|
591
624
|
: {}),
|
|
592
625
|
});
|
|
@@ -737,6 +770,11 @@ const ANTIGRAVITY_USAGE_BASE_BY_ID: Record<string, string> = (() => {
|
|
|
737
770
|
// onto a model that did not exist then, and away from the 3.6 price row that still
|
|
738
771
|
// prices it correctly. Retirement changes what we CALL, not what we RECORD.
|
|
739
772
|
for (const retired of Object.keys(RETIRED_FLASH_TIERS)) rev[retired] = retired;
|
|
773
|
+
// Usage identity is stable even before discovery; routing keeps its own live evidence.
|
|
774
|
+
for (const base of ["claude-sonnet-5-5", "claude-opus-5-5"]) {
|
|
775
|
+
rev[base] = base;
|
|
776
|
+
for (const effort of ANTIGRAVITY_DISCOVERY_EFFORTS) rev[`${base}-${effort}`] = base;
|
|
777
|
+
}
|
|
740
778
|
// Visible aliases that only appear in ANTIGRAVITY_VISIBLE_MODEL_ALIASES are already
|
|
741
779
|
// included via ANTIGRAVITY_MODEL_ALIASES. Identity bases without effort maps remain.
|
|
742
780
|
return rev;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { closeSync, fstatSync, openSync, readSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { atomicWriteFileNoFollow } from "../config/atomic-write";
|
|
5
|
+
import { getConfigDir } from "../config/paths";
|
|
6
|
+
import { isValidProviderName } from "../config/provider-name";
|
|
7
|
+
import { isValidModelDiscoveryModelId, MODEL_DISCOVERY_MAX_MODELS, MODEL_DISCOVERY_MAX_RESPONSE_BYTES } from "./model-discovery-limits";
|
|
8
|
+
|
|
9
|
+
export type AntigravitySuffixMap = Record<"low" | "medium" | "high", string>;
|
|
10
|
+
export interface AntigravityWireSnapshot {
|
|
11
|
+
version: 1;
|
|
12
|
+
provider: string;
|
|
13
|
+
families: Record<string, AntigravitySuffixMap>;
|
|
14
|
+
}
|
|
15
|
+
const EFFORTS = ["low", "medium", "high"] as const;
|
|
16
|
+
|
|
17
|
+
function snapshotPath(destination: string): string {
|
|
18
|
+
const key = createHash("sha256").update(destination).digest("hex");
|
|
19
|
+
return join(getConfigDir(), `antigravity-wire-${key}.json`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function validatedSnapshot(value: unknown): AntigravityWireSnapshot | undefined {
|
|
23
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
24
|
+
const row = value as Record<string, unknown>;
|
|
25
|
+
if (row.version !== 1 || typeof row.provider !== "string" || !isValidProviderName(row.provider)
|
|
26
|
+
|| !row.families || typeof row.families !== "object" || Array.isArray(row.families)) return undefined;
|
|
27
|
+
const entries = Object.entries(row.families);
|
|
28
|
+
if (entries.length > MODEL_DISCOVERY_MAX_MODELS) return undefined;
|
|
29
|
+
const families: Record<string, AntigravitySuffixMap> = Object.create(null);
|
|
30
|
+
for (const [base, candidate] of entries) {
|
|
31
|
+
if (!isValidModelDiscoveryModelId(base) || !candidate || typeof candidate !== "object" || Array.isArray(candidate)) return undefined;
|
|
32
|
+
const map = candidate as Record<string, unknown>;
|
|
33
|
+
if (!EFFORTS.every(effort => map[effort] === `${base}-${effort}` && isValidModelDiscoveryModelId(map[effort]))) return undefined;
|
|
34
|
+
families[base] = { low: `${base}-low`, medium: `${base}-medium`, high: `${base}-high` };
|
|
35
|
+
}
|
|
36
|
+
return { version: 1, provider: row.provider, families };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Routing evidence only, never an account's current availability or entitlement. */
|
|
40
|
+
export function readAntigravityWireSnapshot(destination: string): AntigravityWireSnapshot | undefined {
|
|
41
|
+
let fd: number | undefined;
|
|
42
|
+
try {
|
|
43
|
+
fd = openSync(snapshotPath(destination), "r");
|
|
44
|
+
const stat = fstatSync(fd);
|
|
45
|
+
if (!stat.isFile() || stat.size > MODEL_DISCOVERY_MAX_RESPONSE_BYTES) return undefined;
|
|
46
|
+
const bytes = Buffer.alloc(Math.min(stat.size + 1, MODEL_DISCOVERY_MAX_RESPONSE_BYTES + 1));
|
|
47
|
+
let length = 0;
|
|
48
|
+
while (length < bytes.byteLength) {
|
|
49
|
+
const read = readSync(fd, bytes, length, bytes.byteLength - length, null);
|
|
50
|
+
if (read === 0) break;
|
|
51
|
+
length += read;
|
|
52
|
+
}
|
|
53
|
+
if (length > stat.size || length > MODEL_DISCOVERY_MAX_RESPONSE_BYTES) return undefined;
|
|
54
|
+
return validatedSnapshot(JSON.parse(bytes.subarray(0, length).toString("utf8")));
|
|
55
|
+
} catch { return undefined; }
|
|
56
|
+
finally { if (fd !== undefined) closeSync(fd); }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Throw before publishing new synthetic catalog IDs if their routing evidence cannot be saved. */
|
|
60
|
+
export function writeAntigravityWireSnapshot(destination: string, snapshot: AntigravityWireSnapshot): void {
|
|
61
|
+
const validated = validatedSnapshot(snapshot);
|
|
62
|
+
if (!validated) throw new Error("invalid Antigravity wire snapshot");
|
|
63
|
+
const text = JSON.stringify(validated);
|
|
64
|
+
if (Buffer.byteLength(text) > MODEL_DISCOVERY_MAX_RESPONSE_BYTES) throw new Error("Antigravity wire snapshot exceeds size limit");
|
|
65
|
+
atomicWriteFileNoFollow(snapshotPath(destination), text);
|
|
66
|
+
}
|
package/src/providers/derive.ts
CHANGED
|
@@ -590,6 +590,8 @@ export function enrichProviderFromRegistry(name: string, prov: OcxProviderConfig
|
|
|
590
590
|
prov.supportsResponsesCustomTools = entry.supportsResponsesCustomTools;
|
|
591
591
|
}
|
|
592
592
|
if (prov.preserveResponsesReasoningContent === undefined && entry.preserveResponsesReasoningContent !== undefined) prov.preserveResponsesReasoningContent = entry.preserveResponsesReasoningContent;
|
|
593
|
+
if (prov.preserveResponsesInputItemIds === undefined && entry.preserveResponsesInputItemIds !== undefined) prov.preserveResponsesInputItemIds = entry.preserveResponsesInputItemIds;
|
|
594
|
+
if (prov.preserveResponsesMessageMetadata === undefined && entry.preserveResponsesMessageMetadata !== undefined) prov.preserveResponsesMessageMetadata = entry.preserveResponsesMessageMetadata;
|
|
593
595
|
if (prov.dropResponsesReasoningItems === undefined && entry.dropResponsesReasoningItems !== undefined) prov.dropResponsesReasoningItems = entry.dropResponsesReasoningItems;
|
|
594
596
|
applyReasoningSummaryDefaults(prov, entry.modelSupportsReasoningSummaries);
|
|
595
597
|
applyServiceTierModelDefaults(prov, serviceTierModelDefaultsFor(entry, prov));
|
|
@@ -30,6 +30,8 @@ export const PROVIDER_MODEL_RENAME_ROLES = {
|
|
|
30
30
|
fastEnabled: "none",
|
|
31
31
|
modelSupportsServiceTier: "record",
|
|
32
32
|
preserveResponsesReasoningContent: "none",
|
|
33
|
+
preserveResponsesInputItemIds: "none",
|
|
34
|
+
preserveResponsesMessageMetadata: "none",
|
|
33
35
|
modelReasoningEffortsAuthoritative: "none",
|
|
34
36
|
dropResponsesReasoningItems: "none",
|
|
35
37
|
decodesNativeCompactionBlobs: "none",
|
|
@@ -69,6 +71,7 @@ export const PROVIDER_MODEL_RENAME_ROLES = {
|
|
|
69
71
|
autoReviewModel: "none",
|
|
70
72
|
autoReviewModelOverrides: "record",
|
|
71
73
|
headers: "none",
|
|
74
|
+
forwardClientHeaders: "none",
|
|
72
75
|
openRouterRouting: "none",
|
|
73
76
|
modelOpenRouterRouting: "record",
|
|
74
77
|
vercelGatewayRouting: "none",
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { OcxConfig } from "../types";
|
|
2
|
-
import { routedSlug } from "./slug-codec";
|
|
2
|
+
import { routedSlug, slugEquals } from "./slug-codec";
|
|
3
|
+
import {
|
|
4
|
+
antigravityEffortFamilyIds, antigravityFamilyDisabled,
|
|
5
|
+
type AntigravityEffortFamilyRow,
|
|
6
|
+
} from "./antigravity-effort-families";
|
|
3
7
|
|
|
4
8
|
export const MODEL_REMOVAL_GRACE_FETCHES = 3;
|
|
5
9
|
export const MAX_KNOWN_MODELS_PER_PROVIDER = 2_000;
|
|
@@ -115,30 +119,63 @@ export function effectiveNewModelPolicy(config: OcxConfig, provider: string): Ne
|
|
|
115
119
|
return config.modelDiscovery?.newModelPolicy ?? "on";
|
|
116
120
|
}
|
|
117
121
|
|
|
122
|
+
/** Normalize policy identities only; the original rows and saved wire selections stay intact. */
|
|
123
|
+
function normalizeEffortFamilies(config: OcxConfig, provider: string, rows: AntigravityEffortFamilyRow[]) {
|
|
124
|
+
const aliases = new Map<string, string>();
|
|
125
|
+
const inheritedDisables: string[] = [];
|
|
126
|
+
const bases = new Set(rows.filter(row => antigravityEffortFamilyIds(row)).map(row => row.id));
|
|
127
|
+
for (const row of rows) {
|
|
128
|
+
const ids = antigravityEffortFamilyIds(row);
|
|
129
|
+
if (!ids) continue;
|
|
130
|
+
for (const id of ids) if (!bases.has(id)) aliases.set(id, row.id);
|
|
131
|
+
if (antigravityFamilyDisabled(config, row, rows)
|
|
132
|
+
&& !config.disabledModels?.some(slug => slugEquals(slug, provider, row.id))) {
|
|
133
|
+
inheritedDisables.push(routedSlug(provider, row.id));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const normalize = (id: string) => aliases.get(id) ?? id;
|
|
137
|
+
const normalizeIds = (ids: string[]) => [...new Set(ids.map(normalize))].sort();
|
|
138
|
+
const prior = config.modelDiscovery?.knownModels?.[provider];
|
|
139
|
+
let baseline = prior;
|
|
140
|
+
if (prior && aliases.size) {
|
|
141
|
+
const missing: Record<string, number> = {};
|
|
142
|
+
for (const [id, count] of Object.entries(prior.missing ?? {})) {
|
|
143
|
+
const key = normalize(id);
|
|
144
|
+
// A family cannot disappear sooner than its least-missing known tier.
|
|
145
|
+
missing[key] = Math.min(missing[key] ?? count, count);
|
|
146
|
+
}
|
|
147
|
+
baseline = { ...prior, ids: normalizeIds(prior.ids), removed: normalizeIds(prior.removed),
|
|
148
|
+
...(Object.keys(missing).length ? { missing } : {}) };
|
|
149
|
+
}
|
|
150
|
+
return { discoveredIds: normalizeIds(rows.map(row => row.id)), baseline, inheritedDisables };
|
|
151
|
+
}
|
|
152
|
+
|
|
118
153
|
/** Apply authoritative provider rows to a mutable convergence copy; degraded providers are omitted. */
|
|
119
154
|
export function reconcileSuccessfulModelDiscoveries(options: {
|
|
120
155
|
config: OcxConfig;
|
|
121
|
-
models: Iterable<
|
|
156
|
+
models: Iterable<AntigravityEffortFamilyRow>;
|
|
122
157
|
authoritativeProviders: Iterable<string>;
|
|
123
158
|
now: string;
|
|
124
159
|
/** See {@link NewModelPolicyMode}; defaults to `converge`. */
|
|
125
160
|
mode?: NewModelPolicyMode;
|
|
126
161
|
}): boolean {
|
|
127
|
-
const byProvider = new Map<string,
|
|
162
|
+
const byProvider = new Map<string, AntigravityEffortFamilyRow[]>();
|
|
128
163
|
for (const model of options.models) {
|
|
129
|
-
if (model.custom) continue;
|
|
130
|
-
const
|
|
131
|
-
|
|
164
|
+
if (model.custom || model.catalogKind === "custom-model-v1") continue;
|
|
165
|
+
const rows = byProvider.get(model.provider) ?? [];
|
|
166
|
+
rows.push(model); byProvider.set(model.provider, rows);
|
|
132
167
|
}
|
|
133
168
|
let changed = false;
|
|
134
169
|
for (const provider of options.authoritativeProviders) {
|
|
135
170
|
const configured = options.config.providers[provider];
|
|
136
171
|
if (!configured || configured.liveModels === false) continue;
|
|
137
|
-
const discoveredIds =
|
|
172
|
+
const { discoveredIds, baseline, inheritedDisables } = normalizeEffortFamilies(
|
|
173
|
+
options.config, provider, byProvider.get(provider) ?? [],
|
|
174
|
+
);
|
|
138
175
|
const discovery = options.config.modelDiscovery ??= {};
|
|
139
176
|
const known = discovery.knownModels ??= {};
|
|
140
177
|
const result = applyNewModelPolicy({
|
|
141
|
-
provider, discoveredIds, baseline
|
|
178
|
+
provider, discoveredIds, baseline,
|
|
142
179
|
policy: effectiveNewModelPolicy(options.config, provider),
|
|
143
180
|
hasSelectedModels: (configured.selectedModels?.length ?? 0) > 0,
|
|
144
181
|
now: options.now,
|
|
@@ -151,9 +188,10 @@ export function reconcileSuccessfulModelDiscoveries(options: {
|
|
|
151
188
|
// Keep the previous timestamp when nothing else moved, so a steady-state roster does not
|
|
152
189
|
// make the baseline look dirty on the next comparison either.
|
|
153
190
|
if (!baselineChanged && priorBaseline) known[provider] = priorBaseline;
|
|
154
|
-
|
|
191
|
+
const slugsToDisable = [...result.slugsToDisable, ...inheritedDisables];
|
|
192
|
+
if (slugsToDisable.length) {
|
|
155
193
|
const disabled = options.config.disabledModels ??= [];
|
|
156
|
-
for (const slug of
|
|
194
|
+
for (const slug of slugsToDisable) {
|
|
157
195
|
if (disabled.includes(slug)) continue;
|
|
158
196
|
disabled.push(slug);
|
|
159
197
|
changed = true;
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
ZAI_GLM_5X_REASONING_EFFORTS,
|
|
36
36
|
MINIMAX_MODELS,
|
|
37
37
|
MINIMAX_MODEL_CONTEXT_WINDOWS,
|
|
38
|
+
MINIMAX_MODEL_INPUT_MODALITIES,
|
|
38
39
|
MINIMAX_M3_REASONING_EFFORTS,
|
|
39
40
|
MINIMAX_M3_REASONING_EFFORT_MAP,
|
|
40
41
|
MINIMAX_M31_FLASH_PREVIEW,
|
|
@@ -1024,6 +1025,7 @@ export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
|
|
|
1024
1025
|
id: "minimax", label: "MiniMax — Coding Plan", baseUrl: "https://api.minimax.io/v1", adapter: "openai-chat", authKind: "key",
|
|
1025
1026
|
dashboardUrl: "https://platform.minimax.io", defaultModel: "MiniMax-M3", models: MINIMAX_MODELS,
|
|
1026
1027
|
modelContextWindows: MINIMAX_MODEL_CONTEXT_WINDOWS,
|
|
1028
|
+
modelInputModalities: MINIMAX_MODEL_INPUT_MODALITIES,
|
|
1027
1029
|
modelReasoningEfforts: { "MiniMax-M3": MINIMAX_M3_REASONING_EFFORTS, [MINIMAX_M31_FLASH_PREVIEW]: MINIMAX_M31_REASONING_EFFORTS },
|
|
1028
1030
|
modelDefaultReasoningEfforts: { "MiniMax-M3": "medium", [MINIMAX_M31_FLASH_PREVIEW]: MINIMAX_M31_DEFAULT_REASONING_EFFORT },
|
|
1029
1031
|
modelReasoningEffortMap: { "MiniMax-M3": MINIMAX_M3_REASONING_EFFORT_MAP },
|
|
@@ -1049,6 +1051,7 @@ export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
|
|
|
1049
1051
|
id: "minimax-cn", label: "MiniMax — Coding Plan (CN)", baseUrl: "https://api.minimaxi.com/v1", adapter: "openai-chat", authKind: "key",
|
|
1050
1052
|
dashboardUrl: "https://platform.minimaxi.com", defaultModel: "MiniMax-M3", models: MINIMAX_MODELS,
|
|
1051
1053
|
modelContextWindows: MINIMAX_MODEL_CONTEXT_WINDOWS,
|
|
1054
|
+
modelInputModalities: MINIMAX_MODEL_INPUT_MODALITIES,
|
|
1052
1055
|
modelReasoningEfforts: { "MiniMax-M3": MINIMAX_M3_REASONING_EFFORTS, [MINIMAX_M31_FLASH_PREVIEW]: MINIMAX_M31_REASONING_EFFORTS },
|
|
1053
1056
|
modelDefaultReasoningEfforts: { "MiniMax-M3": "medium", [MINIMAX_M31_FLASH_PREVIEW]: MINIMAX_M31_DEFAULT_REASONING_EFFORT },
|
|
1054
1057
|
modelReasoningEffortMap: { "MiniMax-M3": MINIMAX_M3_REASONING_EFFORT_MAP },
|
|
@@ -87,6 +87,8 @@ export const REGISTRY_FIELD_MODEL_ID_ROLES = {
|
|
|
87
87
|
fastOptIn: NONE,
|
|
88
88
|
modelServiceTierCapabilityBaseUrlGuard: NONE,
|
|
89
89
|
preserveResponsesReasoningContent: NONE,
|
|
90
|
+
preserveResponsesInputItemIds: NONE,
|
|
91
|
+
preserveResponsesMessageMetadata: NONE,
|
|
90
92
|
dropResponsesReasoningItems: NONE,
|
|
91
93
|
modelSupportsReasoningSummaries: RECORD_KEYS,
|
|
92
94
|
modelSupportsVerbosity: RECORD_KEYS,
|
|
@@ -172,6 +172,11 @@ export const MINIMAX_REASONING_SPLIT_MODELS = MINIMAX_MODELS_BEFORE_M31;
|
|
|
172
172
|
export const MINIMAX_MODEL_CONTEXT_WINDOWS: Record<string, number> = Object.fromEntries(
|
|
173
173
|
MINIMAX_MODELS.map(id => [id, id === "MiniMax-M3" || id === MINIMAX_M31_FLASH_PREVIEW ? 1_000_000 : 204_800]),
|
|
174
174
|
);
|
|
175
|
+
/** MiniMax's OpenAI-compatible M3 endpoints accept image_url; keep video off Codex's input enum. */
|
|
176
|
+
export const MINIMAX_MODEL_INPUT_MODALITIES: Record<string, string[]> = {
|
|
177
|
+
"MiniMax-M3": ["text", "image"],
|
|
178
|
+
[MINIMAX_M31_FLASH_PREVIEW]: ["text", "image"],
|
|
179
|
+
};
|
|
175
180
|
export const MINIMAX_M3_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
176
181
|
/** Identity efforts on the wire; no map, so none omits the field instead of disabling thinking. */
|
|
177
182
|
export const MINIMAX_M31_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
@@ -278,6 +278,10 @@ export interface ProviderRegistryEntry {
|
|
|
278
278
|
modelServiceTierCapabilityBaseUrlGuard?: (baseUrl: string) => boolean;
|
|
279
279
|
/** Registry default for plaintext reasoning replay; see `OcxProviderConfig.preserveResponsesReasoningContent`. Registry-only like `supportsServiceTier`. */
|
|
280
280
|
preserveResponsesReasoningContent?: boolean;
|
|
281
|
+
/** Registry default for preserving input item IDs; see `OcxProviderConfig.preserveResponsesInputItemIds`. */
|
|
282
|
+
preserveResponsesInputItemIds?: boolean;
|
|
283
|
+
/** Registry default for preserving per-item chat metadata; see `OcxProviderConfig.preserveResponsesMessageMetadata`. */
|
|
284
|
+
preserveResponsesMessageMetadata?: boolean;
|
|
281
285
|
/** Registry default for dropping replayed reasoning items for Responses upstreams that reject them. */
|
|
282
286
|
dropResponsesReasoningItems?: boolean;
|
|
283
287
|
/** Registry defaults for per-model Codex reasoning propagation; explicit user keys win during enrichment. */
|
|
@@ -15,6 +15,25 @@
|
|
|
15
15
|
* routed models get a short "history was compacted" note instead.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
import type { TranslatorBudget } from "../lib/translator-budget";
|
|
19
|
+
|
|
20
|
+
const ciphertextLeases = new WeakMap<object, { budget: TranslatorBudget; bytes: number }>();
|
|
21
|
+
|
|
22
|
+
/** Attach an already-charged parser ciphertext lease to its exact terminal event. */
|
|
23
|
+
export function transferCompactionCiphertextLease(event: object, budget: TranslatorBudget, bytes: number): void {
|
|
24
|
+
if (bytes === 0) return;
|
|
25
|
+
if (ciphertextLeases.has(event)) throw new Error("compaction ciphertext lease is already transferred");
|
|
26
|
+
ciphertextLeases.set(event, { budget, bytes });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Release only this event's transferred ciphertext lease, once and for its owning budget. */
|
|
30
|
+
export function releaseCompactionCiphertextLease(event: object, budget: TranslatorBudget): void {
|
|
31
|
+
const lease = ciphertextLeases.get(event);
|
|
32
|
+
if (!lease || lease.budget !== budget) return;
|
|
33
|
+
ciphertextLeases.delete(event);
|
|
34
|
+
budget.releaseRetained(lease.bytes, { kind: "retained_collectors" });
|
|
35
|
+
}
|
|
36
|
+
|
|
18
37
|
export const OCX_COMPACTION_PREFIX = "ocx1:";
|
|
19
38
|
|
|
20
39
|
export const OCX_NATIVE_REPLAY_RECOVERY_NOTE =
|
package/src/router.ts
CHANGED
|
@@ -426,6 +426,12 @@ export function routedProviderConfig(providerName: string, provider: OcxProvider
|
|
|
426
426
|
...(provider.preserveResponsesReasoningContent === undefined && registryEntry.preserveResponsesReasoningContent !== undefined
|
|
427
427
|
? { preserveResponsesReasoningContent: registryEntry.preserveResponsesReasoningContent }
|
|
428
428
|
: {}),
|
|
429
|
+
...(provider.preserveResponsesInputItemIds === undefined && registryEntry.preserveResponsesInputItemIds !== undefined
|
|
430
|
+
? { preserveResponsesInputItemIds: registryEntry.preserveResponsesInputItemIds }
|
|
431
|
+
: {}),
|
|
432
|
+
...(provider.preserveResponsesMessageMetadata === undefined && registryEntry.preserveResponsesMessageMetadata !== undefined
|
|
433
|
+
? { preserveResponsesMessageMetadata: registryEntry.preserveResponsesMessageMetadata }
|
|
434
|
+
: {}),
|
|
429
435
|
...(provider.dropResponsesReasoningItems === undefined && registryEntry.dropResponsesReasoningItems !== undefined
|
|
430
436
|
? { dropResponsesReasoningItems: registryEntry.dropResponsesReasoningItems }
|
|
431
437
|
: {}),
|
package/src/server/auth-cors.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
positiveIntegerRecordConfigError,
|
|
28
28
|
providerBaseUrlConfigError,
|
|
29
29
|
providerHeadersConfigError,
|
|
30
|
+
providerForwardClientHeadersConfigError,
|
|
30
31
|
reasoningSummaryDeliveryRecordConfigError,
|
|
31
32
|
upstreamHttpVersionConfigError,
|
|
32
33
|
} from "../config/provider-validation";
|
|
@@ -765,6 +766,9 @@ export function providerManagementConfigError(
|
|
|
765
766
|
// validation and then rejected by the seed comparison, so canonical OpenAI could never
|
|
766
767
|
// set OR clear it — the value was admitted and then refused in the same request.
|
|
767
768
|
delete canonicalCandidate.annotateEmptyToolOutputs;
|
|
769
|
+
// forwardClientHeaders is an editor-managed request-metadata overlay. It is validated
|
|
770
|
+
// separately below and must not make an otherwise canonical OpenAI provider fail the seed check.
|
|
771
|
+
delete canonicalCandidate.forwardClientHeaders;
|
|
768
772
|
// Canonical ChatGPT keeps WebSocket as the default, but an operator may
|
|
769
773
|
// select the existing HTTP/SSE path without changing its auth or endpoint.
|
|
770
774
|
if (raw.upstreamWebsocket !== undefined) {
|
|
@@ -800,6 +804,8 @@ export function providerManagementConfigError(
|
|
|
800
804
|
}
|
|
801
805
|
const headersError = providerHeadersConfigError(typed.headers);
|
|
802
806
|
if (headersError) return `provider ${name} ${headersError}`;
|
|
807
|
+
const forwardClientHeadersError = providerForwardClientHeadersConfigError(raw.forwardClientHeaders);
|
|
808
|
+
if (forwardClientHeadersError) return `provider ${name} ${forwardClientHeadersError}`;
|
|
803
809
|
const retryOn429Error = retryOn429PolicyConfigError(raw.retryOn429);
|
|
804
810
|
if (retryOn429Error) {
|
|
805
811
|
// The provider name is caller-controlled and can be token-shaped; redact and JSON-escape
|
|
@@ -998,6 +1004,8 @@ const PROVIDER_CONFIG_FIELD_POLICY = {
|
|
|
998
1004
|
supportsServiceTier: "editor",
|
|
999
1005
|
modelSupportsServiceTier: "editor",
|
|
1000
1006
|
preserveResponsesReasoningContent: "editor",
|
|
1007
|
+
preserveResponsesInputItemIds: "editor",
|
|
1008
|
+
preserveResponsesMessageMetadata: "editor",
|
|
1001
1009
|
dropResponsesReasoningItems: "editor",
|
|
1002
1010
|
modelReasoningEffortsAuthoritative: "editor",
|
|
1003
1011
|
decodesNativeCompactionBlobs: "editor",
|
|
@@ -1042,6 +1050,7 @@ const PROVIDER_CONFIG_FIELD_POLICY = {
|
|
|
1042
1050
|
modelMaxOutputTokens: "editor",
|
|
1043
1051
|
modelCosts: "editor",
|
|
1044
1052
|
headers: "redacted",
|
|
1053
|
+
forwardClientHeaders: "editor",
|
|
1045
1054
|
openRouterRouting: "editor",
|
|
1046
1055
|
modelOpenRouterRouting: "editor",
|
|
1047
1056
|
vercelGatewayRouting: "editor",
|
|
@@ -86,6 +86,7 @@ import { parseRequestEffortRowId } from "./effort-row";
|
|
|
86
86
|
import { parseSyntheticRowId } from "./fast-row";
|
|
87
87
|
import { isCanonicalOpenAiForwardProvider } from "../providers/openai-tiers";
|
|
88
88
|
import { CODEX_RESERVE_HELPER_UNSUPPORTED_MESSAGE, isCodexReserveHelperUnsupported } from "../codex/loopback-target";
|
|
89
|
+
import { applyDroidReasoningDefault, droidReasoningDefault, DROID_DEFAULT_EFFORT_HEADER } from "./droid-reasoning-default";
|
|
89
90
|
|
|
90
91
|
type Rec = Record<string, unknown>;
|
|
91
92
|
|
|
@@ -157,6 +158,9 @@ async function handleChatCompletionsWithBudget(
|
|
|
157
158
|
|
|
158
159
|
const requestedModel = chatBody.model as string;
|
|
159
160
|
const { fastRow, effortRow } = parseSyntheticRowId(requestedModel, config);
|
|
161
|
+
const droidDefaultEffort = effortRow
|
|
162
|
+
? undefined
|
|
163
|
+
: droidReasoningDefault(req.headers.get(DROID_DEFAULT_EFFORT_HEADER), chatBody);
|
|
160
164
|
if (effortRow) chatBody.model = effortRow.baseId;
|
|
161
165
|
if (fastRow) {
|
|
162
166
|
chatBody.model = fastRow.baseId;
|
|
@@ -198,6 +202,9 @@ async function handleChatCompletionsWithBudget(
|
|
|
198
202
|
getOrAllocateRequestSessionLane(req),
|
|
199
203
|
routedProvider,
|
|
200
204
|
);
|
|
205
|
+
if (!route.combo && route.routeKind !== "policy") {
|
|
206
|
+
applyDroidReasoningDefault(chatBody, droidDefaultEffort, { provider: route.provider, modelId: route.modelId });
|
|
207
|
+
}
|
|
201
208
|
logCtx.model = route.modelId;
|
|
202
209
|
logCtx.providerAdapter = route.provider.adapter;
|
|
203
210
|
logCtx.requestedModel = requestedModel;
|
|
@@ -442,6 +449,9 @@ async function handleChatCompletionsWithBudget(
|
|
|
442
449
|
abortSignal: req.signal,
|
|
443
450
|
// Body is Responses-shaped by now, but the client spoke Chat Completions.
|
|
444
451
|
inboundWire: "chat",
|
|
452
|
+
...((settledRoute?.combo || settledRoute?.routeKind === "policy") && droidDefaultEffort
|
|
453
|
+
? { droidDefaultEffort }
|
|
454
|
+
: {}),
|
|
445
455
|
// PF-07: the combo sends eligible candidates natively from this envelope.
|
|
446
456
|
...(envelope && nativeChatCombos ? {
|
|
447
457
|
protocolSource: createNativeChatComboSource({
|