@bitkyc08/opencodex 2.40.0 → 2.41.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 +4 -0
- package/gui/dist/assets/index-B2YjLA-i.css +1 -0
- package/gui/dist/assets/{index-BHe2rl_C.js → index-aPup8CKb.js} +20 -20
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/meta.svg +1 -0
- package/package.json +4 -3
- package/src/adapters/cursor/catalog.ts +71 -29
- package/src/adapters/cursor/claude-id.ts +76 -0
- package/src/adapters/cursor/discovery.ts +16 -3
- package/src/adapters/cursor/effort-map.ts +27 -12
- package/src/adapters/google.ts +39 -2
- package/src/adapters/openai-responses.ts +14 -1
- package/src/cli/claude.ts +11 -2
- package/src/cli/connect.ts +7 -1
- package/src/cli/registry.ts +1 -1
- package/src/cli/status.ts +19 -4
- package/src/client/connect.ts +5 -1
- package/src/client/hub-client.ts +29 -5
- package/src/clients/config-export.ts +12 -2
- package/src/codex/catalog/aggregation.ts +8 -0
- package/src/codex/catalog/metadata.ts +5 -0
- package/src/codex/catalog/parsing.ts +2 -0
- package/src/codex/catalog/provider-fetch.ts +163 -26
- package/src/codex/catalog.ts +1 -1
- package/src/codex/convergence-types.ts +1 -0
- package/src/codex/desired-state.ts +18 -11
- package/src/combos/failover.ts +185 -6
- package/src/combos/index.ts +6 -0
- package/src/combos/resolve.ts +43 -6
- package/src/config.ts +5 -1
- package/src/generated/compatibility-version.json +85 -61
- package/src/generated/model-metadata.ts +1 -1
- package/src/grok/sync.ts +10 -2
- package/src/integrations/cursor-effort-table.ts +143 -0
- package/src/integrations/state.ts +1 -1
- package/src/integrations/writer.ts +2 -2
- package/src/lib/app-owned-memory-stores.ts +27 -8
- package/src/lib/bounded-body.ts +16 -1
- package/src/oauth/generic-account-failover.ts +2 -2
- package/src/oauth/index.ts +11 -0
- package/src/oauth/meta-muse.ts +235 -0
- package/src/providers/antigravity-models.ts +71 -13
- package/src/providers/command-code-efforts.ts +15 -0
- package/src/providers/free-directory.ts +4 -1
- package/src/providers/registry.ts +116 -8
- package/src/responses/code-mode-helper-compat.ts +4 -1
- package/src/responses/state.ts +5 -4
- package/src/server/auth-cors.ts +241 -56
- package/src/server/chat-completions.ts +11 -2
- package/src/server/chat-native.ts +30 -4
- package/src/server/claude-messages.ts +17 -3
- package/src/server/effort-row.ts +131 -0
- package/src/server/index.ts +67 -38
- package/src/server/management/api-key-rotation.ts +2 -1
- package/src/server/management/api-key-usage.ts +97 -43
- package/src/server/management/context.ts +3 -0
- package/src/server/management/cursor-integration-routes.ts +36 -7
- package/src/server/management/logs-usage-routes.ts +64 -87
- package/src/server/management/provider-routes.ts +218 -1
- package/src/server/management/route-registry.ts +1 -0
- package/src/server/management/usage-aggregate-cache.ts +464 -0
- package/src/server/management/usage-summary-cache.ts +4 -0
- package/src/server/models-capabilities.ts +60 -5
- package/src/server/responses/core.ts +61 -7
- package/src/types/config.ts +10 -1
- package/src/types/tools.ts +12 -9
- package/src/usage/expected-prices.ts +43 -7
- package/src/usage/ledger-scanner.ts +448 -0
- package/src/usage/log.ts +1 -1
- package/src/usage/summary.ts +915 -655
- package/src/web-search/index.ts +1 -1
- package/gui/dist/assets/index-CJSb3HPe.css +0 -1
|
@@ -13,14 +13,27 @@ import { isModelCacheGenerationCurrent } from "../codex/model-cache";
|
|
|
13
13
|
// ── Wire IDs (what CCA :fetchAvailableModels returns) ──
|
|
14
14
|
|
|
15
15
|
/** Current Antigravity Flash generation. */
|
|
16
|
-
const GEMINI_FLASH_CURRENT = "gemini-3.
|
|
16
|
+
const GEMINI_FLASH_CURRENT = "gemini-3.8-flash";
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* Previous Flash generation — still served, still picker-visible.
|
|
20
|
+
*
|
|
21
|
+
* 3.6 vanished from CCA the moment 3.7 shipped, which is why RETIRED_FLASH_TIERS exists. 3.8
|
|
22
|
+
* did not do that: Google documents 3.7 Flash as "remains fully supported", and a 2026-09-03
|
|
23
|
+
* :fetchAvailableModels call returns 3.8, 3.7 AND 3.6 wire ids together. Retiring 3.7 here
|
|
24
|
+
* would strand a model the backend is actively serving.
|
|
25
|
+
*/
|
|
26
|
+
const GEMINI_FLASH_PREVIOUS = "gemini-3.7-flash";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Wire ID that CCA accepts for the RETIRED-tier redirect target (currently 3.7).
|
|
30
|
+
*
|
|
31
|
+
* Google renamed 3.7 to carry a `-tiered` suffix; the picker-visible ID stays
|
|
32
|
+
* `gemini-3.7-flash` (stripped by `pickerModelIdForDiscoveredWireId`). This constant is named
|
|
33
|
+
* for its ROLE, not for the current generation: 3.8 is current and has no `-tiered` id, so a
|
|
34
|
+
* name like GEMINI_FLASH_WIRE_ID would now point readers at the wrong model.
|
|
22
35
|
*/
|
|
23
|
-
const
|
|
36
|
+
const GEMINI_RETIRED_FLASH_TARGET_WIRE_ID = "gemini-3.7-flash-tiered";
|
|
24
37
|
|
|
25
38
|
/**
|
|
26
39
|
* Retired Flash ids → the reasoning tier they used to encode.
|
|
@@ -60,6 +73,9 @@ const ANTIGRAVITY_WIRE_MODELS = [
|
|
|
60
73
|
];
|
|
61
74
|
|
|
62
75
|
const ANTIGRAVITY_PICKER_MODEL_BY_WIRE_ID: Record<string, string> = {
|
|
76
|
+
"gemini-3.8-flash-low": "gemini-3.8-flash",
|
|
77
|
+
"gemini-3.8-flash-medium": "gemini-3.8-flash",
|
|
78
|
+
"gemini-3.8-flash-high": "gemini-3.8-flash",
|
|
63
79
|
"gemini-3.1-pro-low": "gemini-3.1-pro",
|
|
64
80
|
"gemini-pro-agent": "gemini-3.1-pro",
|
|
65
81
|
};
|
|
@@ -143,6 +159,9 @@ function collapsesIntoKnownPickerModel(candidateId: string): boolean {
|
|
|
143
159
|
// Gemini models: effort → wire model suffix (official agy UI pattern).
|
|
144
160
|
// Claude Opus: effort → thinkingConfig.thinkingLevel (CLIProxyAPI proven pattern).
|
|
145
161
|
export const ANTIGRAVITY_MODEL_EFFORTS: Record<string, string[]> = {
|
|
162
|
+
// No `minimal`: Google documents it as an error for this generation, and CCA exposes only
|
|
163
|
+
// the three tiers.
|
|
164
|
+
"gemini-3.8-flash": ["low", "medium", "high"],
|
|
146
165
|
"gemini-3.7-flash": ["low", "medium", "high"],
|
|
147
166
|
"gemini-3.1-pro": ["low", "high"],
|
|
148
167
|
"claude-sonnet-4-6": ["low", "medium", "high", "max"],
|
|
@@ -151,12 +170,32 @@ export const ANTIGRAVITY_MODEL_EFFORTS: Record<string, string[]> = {
|
|
|
151
170
|
|
|
152
171
|
// ── Effort → wire model map for Gemini base models ──
|
|
153
172
|
const ANTIGRAVITY_EFFORT_WIRE_MAP: Record<string, Record<string, string>> = {
|
|
173
|
+
// 3.8 publishes one wire id per tier and no `-tiered` row, so its efforts ride the suffix.
|
|
174
|
+
// This is the 3.6 shape, not the 3.7 one.
|
|
175
|
+
"gemini-3.8-flash": {
|
|
176
|
+
low: "gemini-3.8-flash-low",
|
|
177
|
+
medium: "gemini-3.8-flash-medium",
|
|
178
|
+
high: "gemini-3.8-flash-high",
|
|
179
|
+
},
|
|
154
180
|
"gemini-3.1-pro": {
|
|
155
181
|
low: "gemini-3.1-pro-low",
|
|
156
182
|
high: "gemini-pro-agent",
|
|
157
183
|
},
|
|
158
184
|
};
|
|
159
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Base models whose every effort maps to a wire id that ALREADY encodes the tier.
|
|
188
|
+
*
|
|
189
|
+
* Sending `thinkingLevel` beside such a suffix states the effort twice, and CCA does not reject
|
|
190
|
+
* the contradiction — a `-low` wire id paired with `HIGH` returns 200, so the tier that actually
|
|
191
|
+
* ran becomes unknowable from the response. Membership also makes static resolution
|
|
192
|
+
* byte-identical to the discovery path, which never emits a thinking level.
|
|
193
|
+
*
|
|
194
|
+
* `gemini-3.1-pro` is deliberately absent: its `high` rung is `gemini-pro-agent`, which carries
|
|
195
|
+
* no tier suffix, so there the level is the only thing naming the effort.
|
|
196
|
+
*/
|
|
197
|
+
const ANTIGRAVITY_SUFFIX_TIER_MODELS = new Set(["gemini-3.8-flash"]);
|
|
198
|
+
|
|
160
199
|
function completeDiscoveredEffortWireModelIds(
|
|
161
200
|
pickerId: string,
|
|
162
201
|
available: ReadonlyMap<string, Record<string, unknown>>,
|
|
@@ -178,6 +217,8 @@ function completeDiscoveredEffortWireModelIds(
|
|
|
178
217
|
|
|
179
218
|
// ── Default effort per Gemini base model ──
|
|
180
219
|
const ANTIGRAVITY_DEFAULT_EFFORT: Record<string, string> = {
|
|
220
|
+
// Google's documented thinking_level default, and the tier CCA marks `recommended`.
|
|
221
|
+
"gemini-3.8-flash": "medium",
|
|
181
222
|
"gemini-3.1-pro": "high",
|
|
182
223
|
};
|
|
183
224
|
|
|
@@ -198,7 +239,7 @@ const ANTIGRAVITY_THINKING_LEVELS = new Set(["low", "medium", "high"]);
|
|
|
198
239
|
* Models not listed here use themselves as the wire ID.
|
|
199
240
|
*/
|
|
200
241
|
const ANTIGRAVITY_PICKER_TO_WIRE: Record<string, string> = {
|
|
201
|
-
"gemini-3.7-flash":
|
|
242
|
+
"gemini-3.7-flash": GEMINI_RETIRED_FLASH_TARGET_WIRE_ID,
|
|
202
243
|
};
|
|
203
244
|
|
|
204
245
|
/** Map a picker-visible base model to its CCA wire ID. Identity when no mapping exists. */
|
|
@@ -230,7 +271,7 @@ const ANTIGRAVITY_COMPATIBILITY_MODEL_ALIASES: Record<string, string> = {
|
|
|
230
271
|
// because `parseAntigravityAvailableModels` uses THIS map to keep a stale CCA
|
|
231
272
|
// payload from republishing a dead wire id as a picker row.
|
|
232
273
|
...Object.fromEntries(
|
|
233
|
-
Object.keys(RETIRED_FLASH_TIERS).map(retired => [retired,
|
|
274
|
+
Object.keys(RETIRED_FLASH_TIERS).map(retired => [retired, GEMINI_RETIRED_FLASH_TARGET_WIRE_ID]),
|
|
234
275
|
),
|
|
235
276
|
};
|
|
236
277
|
|
|
@@ -242,6 +283,7 @@ export const ANTIGRAVITY_MODEL_ALIASES: Record<string, string> = {
|
|
|
242
283
|
// Picker-visible: collapsed base models only.
|
|
243
284
|
export const ANTIGRAVITY_MODELS = [
|
|
244
285
|
GEMINI_FLASH_CURRENT,
|
|
286
|
+
GEMINI_FLASH_PREVIOUS,
|
|
245
287
|
"gemini-3.1-pro",
|
|
246
288
|
"gemini-3.1-flash-image",
|
|
247
289
|
"claude-sonnet-4-6",
|
|
@@ -255,6 +297,9 @@ function isKnownAntigravityPickerModelId(value: string): boolean {
|
|
|
255
297
|
|
|
256
298
|
// Context windows from the upstream `:fetchAvailableModels` maxTokens per model.
|
|
257
299
|
const ANTIGRAVITY_WIRE_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
300
|
+
"gemini-3.8-flash-low": 1_048_576,
|
|
301
|
+
"gemini-3.8-flash-medium": 1_048_576,
|
|
302
|
+
"gemini-3.8-flash-high": 1_048_576,
|
|
258
303
|
"gemini-3.7-flash-tiered": 1_048_576,
|
|
259
304
|
"gemini-3.1-pro-low": 1_048_576,
|
|
260
305
|
"gemini-pro-agent": 1_048_576,
|
|
@@ -266,6 +311,7 @@ const ANTIGRAVITY_WIRE_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
|
266
311
|
|
|
267
312
|
export const ANTIGRAVITY_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
|
|
268
313
|
// Collapsed base IDs — explicit entries for the picker.
|
|
314
|
+
"gemini-3.8-flash": 1_048_576,
|
|
269
315
|
"gemini-3.7-flash": 1_048_576,
|
|
270
316
|
"gemini-3.1-pro": 1_048_576,
|
|
271
317
|
// Wire IDs and aliases via derivation.
|
|
@@ -283,6 +329,7 @@ export const ANTIGRAVITY_MODEL_INPUT_MODALITIES: Record<string, string[]> = {
|
|
|
283
329
|
// carries only text and image parts (`OcxImageContent`, src/types.ts) and the Codex
|
|
284
330
|
// catalog normalizes `input_modalities` against a closed enum. Advertising a modality
|
|
285
331
|
// the wire cannot carry would be a promise we break at request time.
|
|
332
|
+
"gemini-3.8-flash": ["text", "image"],
|
|
286
333
|
"gemini-3.7-flash": ["text", "image"],
|
|
287
334
|
"gemini-3.1-pro": ["text", "image"],
|
|
288
335
|
"gemini-3.1-flash-image": ["text", "image"],
|
|
@@ -609,13 +656,15 @@ export function resolveAntigravityEffortWireModel(
|
|
|
609
656
|
};
|
|
610
657
|
}
|
|
611
658
|
|
|
612
|
-
// Rule 0: retired Flash id — Google has taken the wire id offline, so route to the
|
|
613
|
-
//
|
|
614
|
-
//
|
|
659
|
+
// Rule 0: retired Flash id — Google has taken the wire id offline, so route to the 3.7
|
|
660
|
+
// redirect target and carry the tier the retired id encoded. (3.7, not "the current
|
|
661
|
+
// generation": 3.8 is current but these ids were retired onto 3.7, which is still served.)
|
|
662
|
+
// This runs BEFORE the suffix check because those ids are aliases, and rule 1 would drop
|
|
663
|
+
// the tier.
|
|
615
664
|
const retiredTier = retiredAntigravityFlashTier(modelId);
|
|
616
665
|
if (retiredTier) {
|
|
617
666
|
return {
|
|
618
|
-
wireModelId:
|
|
667
|
+
wireModelId: GEMINI_RETIRED_FLASH_TARGET_WIRE_ID,
|
|
619
668
|
thinkingLevel: effort ? resolveAntigravityThinkingLevel(effort) ?? retiredTier : retiredTier,
|
|
620
669
|
};
|
|
621
670
|
}
|
|
@@ -638,8 +687,17 @@ export function resolveAntigravityEffortWireModel(
|
|
|
638
687
|
// Rule 2/3: mapped Gemini base model.
|
|
639
688
|
const effortMap = ANTIGRAVITY_EFFORT_WIRE_MAP[modelId];
|
|
640
689
|
if (effortMap) {
|
|
641
|
-
|
|
642
|
-
|
|
690
|
+
const suffixTiered = ANTIGRAVITY_SUFFIX_TIER_MODELS.has(modelId);
|
|
691
|
+
// Normalize FIRST for suffix-tiered models. The discovery path clamps max/xhigh/ultra to
|
|
692
|
+
// `high` before its own lookup, so a static path that skipped the clamp answered `medium`
|
|
693
|
+
// for the same request: one input, two tiers, decided by whether discovery happened to run.
|
|
694
|
+
const requested = suffixTiered && effort
|
|
695
|
+
? resolveAntigravityThinkingLevel(effort) ?? effort
|
|
696
|
+
: effort;
|
|
697
|
+
if (requested && requested in effortMap) {
|
|
698
|
+
const wireModelId = effortMap[requested]!;
|
|
699
|
+
// The suffix already names the tier; see ANTIGRAVITY_SUFFIX_TIER_MODELS.
|
|
700
|
+
return suffixTiered ? { wireModelId } : { wireModelId, thinkingLevel: requested };
|
|
643
701
|
}
|
|
644
702
|
const defaultEffort = ANTIGRAVITY_DEFAULT_EFFORT[modelId]!;
|
|
645
703
|
return { wireModelId: effortMap[defaultEffort]! };
|
|
@@ -103,6 +103,21 @@ const COMMAND_CODE_MODEL_EFFORTS = {
|
|
|
103
103
|
// 2026-08-13: direct upstream POST with low/medium/high/xhigh/max all 200,
|
|
104
104
|
// ultra 400; reasoningTokens differentiated 114..253; proxy previously stripped
|
|
105
105
|
// the field so effort changes had no effect).
|
|
106
|
+
//
|
|
107
|
+
// 1.3 shipped 2026-09-02 as the same-shaped successor to 1.2 (Command Code
|
|
108
|
+
// publishes meta/muse-spark-1.3 and meta/muse-spark-1.3-contributor alongside
|
|
109
|
+
// the 1.2 pair, and Zen serves muse-spark-1.3-contributor over the same
|
|
110
|
+
// /responses wire). It carries the 1.2 ladder because it IS the 1.2 spec: the
|
|
111
|
+
// upstream ladder statement is per-family, and a narrower guess here would
|
|
112
|
+
// strip an effort the gateway accepts. Additive — 1.2 and 1.1 stay live.
|
|
113
|
+
"meta/muse-spark-1.3": {
|
|
114
|
+
efforts: ["low", "medium", "high", "xhigh", "max"],
|
|
115
|
+
profileUrl: "https://commandcode.ai/models/meta-muse-spark-1.3",
|
|
116
|
+
},
|
|
117
|
+
"meta/muse-spark-1.3-contributor": {
|
|
118
|
+
efforts: ["low", "medium", "high", "xhigh", "max"],
|
|
119
|
+
profileUrl: "https://commandcode.ai/models/meta-muse-spark-1.3-contributor",
|
|
120
|
+
},
|
|
106
121
|
"meta/muse-spark-1.2": {
|
|
107
122
|
efforts: ["low", "medium", "high", "xhigh", "max"],
|
|
108
123
|
profileUrl: "https://commandcode.ai/models/meta-muse-spark-1.2",
|
|
@@ -82,7 +82,10 @@ const CONNECTABLE: Record<string, ConnectableOverride> = {
|
|
|
82
82
|
"cloudflare-ai": openAi("https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/v1", "https://dash.cloudflare.com/?to=/:account/ai/workers-ai", { supportLevel: "supported", verification: "official", documentationUrl: "https://developers.cloudflare.com/workers-ai/configuration/open-ai-compatibility/", discovery: "static", liveModels: false, models: ["@cf/meta/llama-3.3-70b-instruct-fp8-fast", "@cf/qwen/qwq-32b"] }),
|
|
83
83
|
cohere: openAi("https://api.cohere.com/compatibility/v1", "https://dashboard.cohere.com/api-keys", { supportLevel: "supported", verification: "official", documentationUrl: "https://docs.cohere.com/reference/list-models", modelsUrl: "https://api.cohere.com/compatibility/v1/models" }),
|
|
84
84
|
friendliai: openAi("https://api.friendli.ai/serverless/v1", "https://suite.friendli.ai", { modelsUrl: "https://api.friendli.ai/serverless/v1/models" }),
|
|
85
|
-
|
|
85
|
+
// `lastVerified` is row-specific here: the model list was re-checked against ai.google.dev
|
|
86
|
+
// on 2026-09-03 when 3.8 was added. Bumping the shared LAST_VERIFIED instead would stamp
|
|
87
|
+
// that date on every other provider row, none of which was re-checked.
|
|
88
|
+
gemini: { baseUrl: "https://generativelanguage.googleapis.com", dashboardUrl: "https://aistudio.google.com/apikey", adapter: "google", authKind: "key", supportLevel: "supported", verification: "official", documentationUrl: "https://ai.google.dev/api/models", lastVerified: "2026-09-03", discovery: "live", liveModels: true, googleMode: "ai-studio", models: ["gemini-3.8-flash", "gemini-3.7-flash", "gemini-3.6-flash", "gemini-3.5-flash", "gemini-3.5-flash-lite", "gemini-3.1-pro-preview"] },
|
|
86
89
|
"github-models": openAi("https://models.github.ai/inference", "https://github.com/settings/tokens", { supportLevel: "supported", verification: "official", documentationUrl: "https://docs.github.com/en/github-models/prototyping-with-ai-models", discovery: "static", liveModels: false, models: ["openai/gpt-4.1", "meta/llama-4-scout-17b-16e-instruct"] }),
|
|
87
90
|
groq: openAi("https://api.groq.com/openai/v1", "https://console.groq.com/keys", { supportLevel: "supported", verification: "official", documentationUrl: "https://console.groq.com/docs/api-reference#models" }),
|
|
88
91
|
hackclub: openAi("https://ai.hackclub.com/proxy/v1", "https://ai.hackclub.com", { modelsUrl: "https://ai.hackclub.com/proxy/v1/models" }),
|
|
@@ -435,6 +435,32 @@ const OPENAI_API_GPT56_VIRTUAL_MODELS: Record<string, { wireModelId: string; rea
|
|
|
435
435
|
"gpt-5.6-luna-pro": { wireModelId: "gpt-5.6-luna", reasoningMode: "pro" },
|
|
436
436
|
};
|
|
437
437
|
const OPENAI_API_GPT56_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
438
|
+
/*
|
|
439
|
+
* Meta Model API (https://api.meta.ai/v1) — published ladder, deliberately NOT the
|
|
440
|
+
* house set. dev.meta.ai/docs/reasoning lists "none", "minimal", "low", "medium",
|
|
441
|
+
* "high", "xhigh" and then excludes "none" for this family: "not supported by Muse
|
|
442
|
+
* Spark and returns HTTP 400". "max" and "ultra" are absent from the vendor's list
|
|
443
|
+
* entirely, so appending one by family resemblance would invent a wire value.
|
|
444
|
+
*
|
|
445
|
+
* Corroborated on a second surface: an unauthenticated OpenCode Zen probe of
|
|
446
|
+
* muse-spark-1.3-contributor-free (2026-09-03) accepted minimal..xhigh, rejected
|
|
447
|
+
* max/ultra with `unknown variant`, and rejected none with "does not support none
|
|
448
|
+
* with this model".
|
|
449
|
+
*/
|
|
450
|
+
const META_MUSE_REASONING_EFFORTS = ["minimal", "low", "medium", "high", "xhigh"];
|
|
451
|
+
/*
|
|
452
|
+
* Identity wire map. `requestToCodexEffort` (src/reasoning-effort.ts) rewrites
|
|
453
|
+
* `minimal` to `low` unless a model-scoped wire map says otherwise, so without this
|
|
454
|
+
* the picker would advertise an effort the wire never sends — and a registry-array
|
|
455
|
+
* assertion would pass while the request body was wrong. Identity because Meta's
|
|
456
|
+
* values ARE the Codex names.
|
|
457
|
+
*/
|
|
458
|
+
const META_MUSE_REASONING_EFFORT_MAP: Record<string, string> = Object.fromEntries(
|
|
459
|
+
META_MUSE_REASONING_EFFORTS.map(effort => [effort, effort]),
|
|
460
|
+
);
|
|
461
|
+
/** Both Muse Spark 1.3 tiers publish a 1,048,576-token window (dev.meta.ai/docs/models). */
|
|
462
|
+
const META_MUSE_CONTEXT_WINDOW = 1_048_576;
|
|
463
|
+
const META_MUSE_MODELS = ["muse-spark-1.3", "muse-spark-1.3-contributor"];
|
|
438
464
|
/**
|
|
439
465
|
* Daybreak program aliases. These `-latest` ids are the stable contract: OpenAI repoints
|
|
440
466
|
* them at newer snapshots over time (red -> gpt-5.6-cyber, blue -> gpt-5.6-sol as of
|
|
@@ -552,6 +578,8 @@ const COMMAND_CODE_IMAGE_MODELS = [
|
|
|
552
578
|
"gpt-5.6-sol",
|
|
553
579
|
"MiniMaxAI/MiniMax-M3",
|
|
554
580
|
"moonshotai/Kimi-K3",
|
|
581
|
+
"meta/muse-spark-1.3",
|
|
582
|
+
"meta/muse-spark-1.3-contributor",
|
|
555
583
|
"meta/muse-spark-1.2",
|
|
556
584
|
"meta/muse-spark-1.2-contributor",
|
|
557
585
|
] as const;
|
|
@@ -1446,6 +1474,74 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
1446
1474
|
},
|
|
1447
1475
|
virtualModels: OPENAI_API_GPT56_VIRTUAL_MODELS,
|
|
1448
1476
|
},
|
|
1477
|
+
/* [Decision Log]
|
|
1478
|
+
- 목적과 의도: Reach Meta's Muse Spark models directly on Meta's own Model API, instead of only through the Command Code and OpenCode Zen resellers already in this registry.
|
|
1479
|
+
- 기존 구현 및 제약 조건: Meta publishes both POST /v1/responses and POST /v1/chat/completions at https://api.meta.ai/v1, and no API key was issued for this change — every value here comes from the published spec (devlog/_plan/260903_muse_spark_plan_oauth/001).
|
|
1480
|
+
- 검토한 주요 대안: register as openai-chat; use provider id "meta"; enable live discovery; wire the Muse Code subscription credential as OAuth.
|
|
1481
|
+
- 선택한 방식: an openai-responses key provider under the id "meta-model", with a static two-model roster and no OAuth.
|
|
1482
|
+
- 다른 대안 대신 이 방식을 선택한 이유: Meta calls Responses "the recommended default for new work ... OpenAI-compatible and exposes the full feature set", carrying reasoning replay and native input_image that Chat would forfeit. The id is "meta-model" because "meta" would capture the LIVE Command Code selector meta/muse-spark-1.3 at router.ts's provider-prefix branch, and would derive META_API_KEY — the Muse Code CLI's variable, not this API's MODEL_API_KEY.
|
|
1483
|
+
- 장점, 단점 및 영향: users reach Muse Spark without a reseller; discovery stays off until an authenticated /v1/models payload is actually observed, so an unseen roster (Meta also serves image and voice families here) cannot leak into the picker.
|
|
1484
|
+
*/
|
|
1485
|
+
{
|
|
1486
|
+
id: "meta-model",
|
|
1487
|
+
label: "Meta Model API",
|
|
1488
|
+
adapter: "openai-responses",
|
|
1489
|
+
baseUrl: "https://api.meta.ai/v1",
|
|
1490
|
+
authKind: "key",
|
|
1491
|
+
dashboardUrl: "https://dev.meta.ai/docs/authentication",
|
|
1492
|
+
defaultModel: "muse-spark-1.3",
|
|
1493
|
+
models: META_MUSE_MODELS,
|
|
1494
|
+
// Static roster: no authenticated /v1/models payload was ever observed (the only
|
|
1495
|
+
// contact was an unauthenticated GET returning 401 invalid_api_key), and Meta serves
|
|
1496
|
+
// non-agent families on this same base URL. Turning discovery on would publish an
|
|
1497
|
+
// unseen roster into the picker.
|
|
1498
|
+
liveModels: false,
|
|
1499
|
+
// A user may already own a custom provider named "meta-model" pointing elsewhere;
|
|
1500
|
+
// without this, registry transport canonicalization would retarget it and send their
|
|
1501
|
+
// saved key to Meta.
|
|
1502
|
+
preserveCustomDestination: true,
|
|
1503
|
+
modelContextWindows: Object.fromEntries(META_MUSE_MODELS.map(id => [id, META_MUSE_CONTEXT_WINDOW])),
|
|
1504
|
+
// text+image only. Meta also documents video, audio (degraded on 1.3), and PDF, but
|
|
1505
|
+
// the catalog modality enum is text/image and over-advertising poisons the exported
|
|
1506
|
+
// client config (see tests/catalog-input-modality-enum.test.ts).
|
|
1507
|
+
modelInputModalities: Object.fromEntries(META_MUSE_MODELS.map(id => [id, ["text", "image"] as ["text", "image"]])),
|
|
1508
|
+
modelReasoningEfforts: Object.fromEntries(META_MUSE_MODELS.map(id => [id, META_MUSE_REASONING_EFFORTS])),
|
|
1509
|
+
modelReasoningEffortMap: Object.fromEntries(META_MUSE_MODELS.map(id => [id, META_MUSE_REASONING_EFFORT_MAP])),
|
|
1510
|
+
// No defaultMaxOutputTokens: Meta publishes none. The only number in its docs
|
|
1511
|
+
// (131072) appears inside a third-party config sample, and the protocol pages call
|
|
1512
|
+
// the real limit "model-dependent".
|
|
1513
|
+
// Meta names its variable MODEL_API_KEY, but the env var opencodex reads is derived
|
|
1514
|
+
// from the provider id (META_MODEL_API_KEY). Saying only Meta's name would send a
|
|
1515
|
+
// user to export a variable this proxy never reads.
|
|
1516
|
+
note: "Pay-as-you-go Meta Model API. Get a key at https://dev.meta.ai (Meta calls it MODEL_API_KEY; export it here as META_MODEL_API_KEY) — a Meta developer account needs a payment method before it can serve requests, and every call is metered per token. A Muse Code subscription does NOT work here: Meta scopes that credential to the Muse Code CLI and bills any other key pay-as-you-go (dev.meta.ai/docs/muse-code/subscriptions). The Contributor tier (muse-spark-1.3-contributor) is cheap because Meta trains on your prompts — about 92% off input, 95% off output, 99% off cached input; do not send confidential material through it. Muse Spark is also reachable through resellers: command-code carries both tiers, opencode-go serves only muse-spark-1.3-contributor.",
|
|
1517
|
+
},
|
|
1518
|
+
/* [Decision Log]
|
|
1519
|
+
- 목적과 의도: Let an operator who already signed the Muse Code CLI in reach Muse Spark with that credential, instead of provisioning a second key.
|
|
1520
|
+
- 기존 구현 및 제약 조건: The CLI stores a pointer at ~/.config/muse/auth.json and the secret in the macOS Keychain (ai.meta.dev.credentials/meta). Measured: the OAuth access_token 401s on /v1/models while the sibling api_key returns 200, so the usable artifact is a static key, not a refreshable token.
|
|
1521
|
+
- 검토한 주요 대안: spawn `muse login` and poll; reimplement Meta's device grant; treat it as a second key preset; ship nothing.
|
|
1522
|
+
- 선택한 방식: an import-only, macOS-only OAuth provider that reads the existing credential, validates it once, and never spawns or reimplements anything.
|
|
1523
|
+
- 다른 대안 대신 이 방식을 선택한 이유: `muse login` has no non-interactive mode, so a spawned child could outlive cancellation, and polling for the pointer file is satisfied instantly by the one already on disk — reimporting the OLD account on a force-login. Reimplementing the grant would mean guessing a client id the vendor does not publish.
|
|
1524
|
+
- 장점, 단점 및 영향: no new credential to provision, and the id is distinct from meta-model so neither pool contaminates the other. Meta scopes this credential to its own CLI, so the provider carries a HIGH_RISK ToS warning, a CLI-side warning before any read, and a note that says plainly what is unsupported.
|
|
1525
|
+
*/
|
|
1526
|
+
{
|
|
1527
|
+
id: "meta-muse",
|
|
1528
|
+
label: "Meta Muse Code (CLI credential)",
|
|
1529
|
+
adapter: "openai-responses",
|
|
1530
|
+
baseUrl: "https://api.meta.ai/v1",
|
|
1531
|
+
authKind: "oauth",
|
|
1532
|
+
oauthId: "meta-muse",
|
|
1533
|
+
dashboardUrl: "https://dev.meta.ai",
|
|
1534
|
+
defaultModel: "muse-spark-1.3",
|
|
1535
|
+
models: META_MUSE_MODELS,
|
|
1536
|
+
// Same reason as meta-model: the authenticated roster carries muse-image-1.0 and
|
|
1537
|
+
// muse-voice-transcribe-1.0, which this Responses-agent provider cannot drive.
|
|
1538
|
+
liveModels: false,
|
|
1539
|
+
modelContextWindows: Object.fromEntries(META_MUSE_MODELS.map(id => [id, META_MUSE_CONTEXT_WINDOW])),
|
|
1540
|
+
modelInputModalities: Object.fromEntries(META_MUSE_MODELS.map(id => [id, ["text", "image"] as ["text", "image"]])),
|
|
1541
|
+
modelReasoningEfforts: Object.fromEntries(META_MUSE_MODELS.map(id => [id, META_MUSE_REASONING_EFFORTS])),
|
|
1542
|
+
modelReasoningEffortMap: Object.fromEntries(META_MUSE_MODELS.map(id => [id, META_MUSE_REASONING_EFFORT_MAP])),
|
|
1543
|
+
note: "Reuses the API key the Muse Code CLI stores after `muse login` (macOS only; requires the CLI installed and signed in). Meta scopes that credential to the Muse Code CLI, so this is an UNSUPPORTED use: Meta does not authorize subscription coverage outside its own CLI, how these calls settle is not observable from the API, and you should treat every call as billable against your account. The imported key is copied into OpenCodex's auth store. Meta reports subscription window usage inside streaming responses, but OpenCodex does not yet read or display it, and there is no endpoint to query it on demand. Rate limits apply per team, not per key. For a supported path use the meta-model provider with your own key (export it as META_MODEL_API_KEY).",
|
|
1544
|
+
},
|
|
1449
1545
|
{
|
|
1450
1546
|
id: "umans",
|
|
1451
1547
|
label: "Umans AI Coding Plan",
|
|
@@ -1487,25 +1583,33 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
1487
1583
|
- 다른 대안 대신 이 방식을 선택한 이유: OpenCode Go documents sibling models on Chat or Anthropic endpoints, and an exact registry default preserves both those routes and explicit opt-out precedence.
|
|
1488
1584
|
- 장점, 단점 및 영향: Each listed model reaches `/responses` from every inbound surface without changing siblings; a future upstream endpoint change requires an evidence-backed registry update.
|
|
1489
1585
|
*/
|
|
1490
|
-
modelWireDefaults: {
|
|
1586
|
+
modelWireDefaults: {
|
|
1587
|
+
"gpt-5.6-luna": "openai-responses",
|
|
1588
|
+
"muse-spark-1.3-contributor": "openai-responses",
|
|
1589
|
+
"muse-spark-1.2-contributor": "openai-responses",
|
|
1590
|
+
},
|
|
1491
1591
|
modelContextWindows: {
|
|
1492
1592
|
"kimi-k3": KIMI_K3_STANDARD_CONTEXT_WINDOW,
|
|
1493
1593
|
// The DeepSeek vision preview id is metadata-only here: the Go roster is
|
|
1494
1594
|
// discovered live, so it applies the moment the gateway serves the id.
|
|
1495
1595
|
[DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576,
|
|
1496
|
-
// Muse Spark
|
|
1596
|
+
// Muse Spark Contributor serves a 1,048,576-token (1M) context window over
|
|
1497
1597
|
// /responses on Zen Go, matching its 1.1 sibling (Meta developer docs, verified 2026-08-28).
|
|
1498
1598
|
// Without this declaration the catalog falls back to 128k, capping real usable context.
|
|
1599
|
+
// 1.3 ships the same window as 1.2 and is served from the same Zen Go roster.
|
|
1600
|
+
"muse-spark-1.3-contributor": 1_048_576,
|
|
1499
1601
|
"muse-spark-1.2-contributor": 1_048_576,
|
|
1500
1602
|
},
|
|
1501
1603
|
modelInputModalities: {
|
|
1502
1604
|
"kimi-k3": ["text", "image"],
|
|
1503
1605
|
// Experimental DeepSeek vision preview — expected to merge into deepseek-v4-flash later.
|
|
1504
1606
|
[DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
|
|
1505
|
-
// Muse Spark
|
|
1607
|
+
// Muse Spark Contributor is natively multimodal on Zen Go: it accepts input_image
|
|
1506
1608
|
// parts over /responses (probed 2026-08-26). Without this declaration the catalog
|
|
1507
1609
|
// advertises it text-only and the Codex app blocks image attachments client-side with
|
|
1508
1610
|
// "This model does not support image inputs" before the request ever reaches the proxy.
|
|
1611
|
+
// 1.3 is the same-shaped successor and Command Code documents it as multimodal.
|
|
1612
|
+
"muse-spark-1.3-contributor": ["text", "image"],
|
|
1509
1613
|
"muse-spark-1.2-contributor": ["text", "image"],
|
|
1510
1614
|
},
|
|
1511
1615
|
modelReasoningEfforts: {
|
|
@@ -1736,13 +1840,17 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
1736
1840
|
// devlog/_plan/260710_provider_hardening/001_research_frontier.md.
|
|
1737
1841
|
{
|
|
1738
1842
|
id: "google", label: "Google Gemini", adapter: "google", baseUrl: "https://generativelanguage.googleapis.com", authKind: "key", featured: true,
|
|
1739
|
-
dashboardUrl: "https://aistudio.google.com/apikey", defaultModel: "gemini-3.5-flash", models: ["gemini-3.6-flash", "gemini-3.5-flash", "gemini-3.5-flash-lite", "gemini-3.1-pro-preview", "gemini-3.7-flash"],
|
|
1740
|
-
modelContextWindows: { "gemini-3.6-flash": 1_048_576, "gemini-3.5-flash": 1_000_000, "gemini-3.5-flash-lite": 1_048_576, "gemini-3.7-flash": 1_048_576 },
|
|
1741
|
-
modelInputModalities: { "gemini-3.6-flash": ["text", "image"], "gemini-3.5-flash-lite": ["text", "image"], "gemini-3.7-flash": ["text", "image"] },
|
|
1843
|
+
dashboardUrl: "https://aistudio.google.com/apikey", defaultModel: "gemini-3.5-flash", models: ["gemini-3.8-flash", "gemini-3.6-flash", "gemini-3.5-flash", "gemini-3.5-flash-lite", "gemini-3.1-pro-preview", "gemini-3.7-flash"],
|
|
1844
|
+
modelContextWindows: { "gemini-3.8-flash": 1_048_576, "gemini-3.6-flash": 1_048_576, "gemini-3.5-flash": 1_000_000, "gemini-3.5-flash-lite": 1_048_576, "gemini-3.7-flash": 1_048_576 },
|
|
1845
|
+
modelInputModalities: { "gemini-3.8-flash": ["text", "image"], "gemini-3.6-flash": ["text", "image"], "gemini-3.5-flash-lite": ["text", "image"], "gemini-3.7-flash": ["text", "image"] },
|
|
1742
1846
|
modelReasoningEfforts: {
|
|
1847
|
+
// 3.7 and 3.8 omit `minimal`: Google documents it as a validation error on both model
|
|
1848
|
+
// pages, so advertising it hands the user a rung the API rejects. 3.5/3.6 keep theirs —
|
|
1849
|
+
// their pages still list it, and this unit has no evidence to change them.
|
|
1850
|
+
"gemini-3.8-flash": ["low", "medium", "high"],
|
|
1851
|
+
"gemini-3.7-flash": ["low", "medium", "high"],
|
|
1743
1852
|
"gemini-3.6-flash": ["minimal", "low", "medium", "high"],
|
|
1744
1853
|
"gemini-3.5-flash": ["minimal", "low", "medium", "high"],
|
|
1745
|
-
"gemini-3.7-flash": ["minimal", "low", "medium", "high"],
|
|
1746
1854
|
"gemini-3.1-pro-preview": ["low", "medium", "high"],
|
|
1747
1855
|
},
|
|
1748
1856
|
jawcodeBundle: "google", extraMetadataAliases: ["gemini"],
|
|
@@ -1750,7 +1858,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
|
|
|
1750
1858
|
// 2026-07-10: defaultModel is frozen pending Vertex-specific Tier-2 evidence; Gemini API
|
|
1751
1859
|
// evidence from ai.google.dev does not establish Vertex publisher availability.
|
|
1752
1860
|
{ 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"] },
|
|
1753
|
-
{ 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.
|
|
1861
|
+
{ 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.8-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"] },
|
|
1754
1862
|
{ 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" },
|
|
1755
1863
|
{ 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" },
|
|
1756
1864
|
{ 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" },
|
|
@@ -21,7 +21,7 @@ function unwrapPatchInput(value: string): string {
|
|
|
21
21
|
* Convert a nested Code Mode helper call into unified-exec JavaScript.
|
|
22
22
|
*
|
|
23
23
|
* Parsed values are serialized as data, never interpolated as source, so command and patch text
|
|
24
|
-
* cannot escape the generated call. Invalid structured
|
|
24
|
+
* cannot escape the generated call. Invalid structured helper payloads are also passed as data so
|
|
25
25
|
* nested-tool validation can reject them without evaluating provider text as JavaScript.
|
|
26
26
|
*/
|
|
27
27
|
export function compileCodeModeHelperInput(argumentsText: unknown, toolName: string): string {
|
|
@@ -46,5 +46,8 @@ export function compileCodeModeHelperInput(argumentsText: unknown, toolName: str
|
|
|
46
46
|
args.cmd = args.command;
|
|
47
47
|
delete args.command;
|
|
48
48
|
}
|
|
49
|
+
if (toolName === "write_stdin") {
|
|
50
|
+
return `const result = await tools.write_stdin(${JSON.stringify(args)});\ntext(result);`;
|
|
51
|
+
}
|
|
49
52
|
return `const result = await tools.exec_command(${JSON.stringify(args)});\ntext(result);`;
|
|
50
53
|
}
|
package/src/responses/state.ts
CHANGED
|
@@ -1469,14 +1469,14 @@ function ensureLoaded(): void {
|
|
|
1469
1469
|
|
|
1470
1470
|
type SnapshotWriteOutcome = "stable" | "unstable" | "failed";
|
|
1471
1471
|
|
|
1472
|
-
async function writeBoundedSnapshot(path: string): Promise<SnapshotWriteOutcome> {
|
|
1472
|
+
async function writeBoundedSnapshot(path: string, attemptLimit: number): Promise<SnapshotWriteOutcome> {
|
|
1473
1473
|
// Serialize writers so concurrent flush + debounce cannot race on temps / ACL (#612).
|
|
1474
1474
|
const previous = persistGate;
|
|
1475
1475
|
let release!: () => void;
|
|
1476
1476
|
persistGate = new Promise<void>(resolve => { release = resolve; });
|
|
1477
1477
|
await previous;
|
|
1478
1478
|
try {
|
|
1479
|
-
for (let attempt = 0; attempt <
|
|
1479
|
+
for (let attempt = 0; attempt < attemptLimit; attempt += 1) {
|
|
1480
1480
|
const revision = stateRevision;
|
|
1481
1481
|
const entries: Array<[string, unknown]> = [];
|
|
1482
1482
|
let total = 0;
|
|
@@ -1579,12 +1579,13 @@ async function persistNow(path: string, awaitFollowUp = false): Promise<void> {
|
|
|
1579
1579
|
persistTimer = null;
|
|
1580
1580
|
}
|
|
1581
1581
|
pendingPersistPath = null;
|
|
1582
|
-
|
|
1582
|
+
const attemptLimit = awaitFollowUp ? MAX_SNAPSHOT_REWRITE_ATTEMPTS : 1;
|
|
1583
|
+
let outcome = await writeBoundedSnapshot(path, attemptLimit);
|
|
1583
1584
|
if (outcome === "unstable" && awaitFollowUp) {
|
|
1584
1585
|
if (persistTimer) clearTimeout(persistTimer);
|
|
1585
1586
|
persistTimer = null;
|
|
1586
1587
|
pendingPersistPath = null;
|
|
1587
|
-
outcome = await writeBoundedSnapshot(path);
|
|
1588
|
+
outcome = await writeBoundedSnapshot(path, attemptLimit);
|
|
1588
1589
|
}
|
|
1589
1590
|
if (outcome === "stable") drainPendingSpillUnlinks();
|
|
1590
1591
|
else if (outcome === "unstable" && !awaitFollowUp) schedulePersistAt(path, true);
|