@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.
Files changed (72) hide show
  1. package/README.md +4 -0
  2. package/gui/dist/assets/index-B2YjLA-i.css +1 -0
  3. package/gui/dist/assets/{index-BHe2rl_C.js → index-aPup8CKb.js} +20 -20
  4. package/gui/dist/index.html +2 -2
  5. package/gui/dist/provider-icons/meta.svg +1 -0
  6. package/package.json +4 -3
  7. package/src/adapters/cursor/catalog.ts +71 -29
  8. package/src/adapters/cursor/claude-id.ts +76 -0
  9. package/src/adapters/cursor/discovery.ts +16 -3
  10. package/src/adapters/cursor/effort-map.ts +27 -12
  11. package/src/adapters/google.ts +39 -2
  12. package/src/adapters/openai-responses.ts +14 -1
  13. package/src/cli/claude.ts +11 -2
  14. package/src/cli/connect.ts +7 -1
  15. package/src/cli/registry.ts +1 -1
  16. package/src/cli/status.ts +19 -4
  17. package/src/client/connect.ts +5 -1
  18. package/src/client/hub-client.ts +29 -5
  19. package/src/clients/config-export.ts +12 -2
  20. package/src/codex/catalog/aggregation.ts +8 -0
  21. package/src/codex/catalog/metadata.ts +5 -0
  22. package/src/codex/catalog/parsing.ts +2 -0
  23. package/src/codex/catalog/provider-fetch.ts +163 -26
  24. package/src/codex/catalog.ts +1 -1
  25. package/src/codex/convergence-types.ts +1 -0
  26. package/src/codex/desired-state.ts +18 -11
  27. package/src/combos/failover.ts +185 -6
  28. package/src/combos/index.ts +6 -0
  29. package/src/combos/resolve.ts +43 -6
  30. package/src/config.ts +5 -1
  31. package/src/generated/compatibility-version.json +85 -61
  32. package/src/generated/model-metadata.ts +1 -1
  33. package/src/grok/sync.ts +10 -2
  34. package/src/integrations/cursor-effort-table.ts +143 -0
  35. package/src/integrations/state.ts +1 -1
  36. package/src/integrations/writer.ts +2 -2
  37. package/src/lib/app-owned-memory-stores.ts +27 -8
  38. package/src/lib/bounded-body.ts +16 -1
  39. package/src/oauth/generic-account-failover.ts +2 -2
  40. package/src/oauth/index.ts +11 -0
  41. package/src/oauth/meta-muse.ts +235 -0
  42. package/src/providers/antigravity-models.ts +71 -13
  43. package/src/providers/command-code-efforts.ts +15 -0
  44. package/src/providers/free-directory.ts +4 -1
  45. package/src/providers/registry.ts +116 -8
  46. package/src/responses/code-mode-helper-compat.ts +4 -1
  47. package/src/responses/state.ts +5 -4
  48. package/src/server/auth-cors.ts +241 -56
  49. package/src/server/chat-completions.ts +11 -2
  50. package/src/server/chat-native.ts +30 -4
  51. package/src/server/claude-messages.ts +17 -3
  52. package/src/server/effort-row.ts +131 -0
  53. package/src/server/index.ts +67 -38
  54. package/src/server/management/api-key-rotation.ts +2 -1
  55. package/src/server/management/api-key-usage.ts +97 -43
  56. package/src/server/management/context.ts +3 -0
  57. package/src/server/management/cursor-integration-routes.ts +36 -7
  58. package/src/server/management/logs-usage-routes.ts +64 -87
  59. package/src/server/management/provider-routes.ts +218 -1
  60. package/src/server/management/route-registry.ts +1 -0
  61. package/src/server/management/usage-aggregate-cache.ts +464 -0
  62. package/src/server/management/usage-summary-cache.ts +4 -0
  63. package/src/server/models-capabilities.ts +60 -5
  64. package/src/server/responses/core.ts +61 -7
  65. package/src/types/config.ts +10 -1
  66. package/src/types/tools.ts +12 -9
  67. package/src/usage/expected-prices.ts +43 -7
  68. package/src/usage/ledger-scanner.ts +448 -0
  69. package/src/usage/log.ts +1 -1
  70. package/src/usage/summary.ts +915 -655
  71. package/src/web-search/index.ts +1 -1
  72. package/gui/dist/assets/index-CJSb3HPe.css +0 -1
@@ -68,12 +68,14 @@ import { resolvePassiveRouteSubjectId } from "../passive-route-linker";
68
68
  import {
69
69
  advanceComboAfterFailure,
70
70
  comboDefaultEffort,
71
+ comboFailureCooldownScope,
71
72
  comboFailureDecision,
72
73
  comboIdFromRawBody,
73
74
  comboRequestHasImageInput,
74
75
  concreteComboRequestBody,
75
76
  getCombo,
76
77
  isComboTargetInCooldown,
78
+ comboCooldownRetryAfterSeconds,
77
79
  NoAvailableComboTargetsError,
78
80
  noteComboSuccess,
79
81
  parseRetryAfterMs,
@@ -326,6 +328,7 @@ import {
326
328
  restoreImageGenCallsInJson,
327
329
  } from "../responses-image-gen-repair";
328
330
  import { createResponsesModelPayloadRewrite, rewriteResponsesModelJson } from "../responses-model-rewrite";
331
+ import { parseRequestEffortRowId } from "../effort-row";
329
332
  import {
330
333
  collectSelfNamedNamespaceScrubAuthorization,
331
334
  createSelfNamedToolCallNamespaceScrubRewrite,
@@ -1434,15 +1437,29 @@ export function decodeRequestErrorResponse(err: unknown, label: string): Respons
1434
1437
 
1435
1438
 
1436
1439
 
1437
- export function comboUnavailableResponse(message: string): Response {
1440
+ export function comboUnavailableResponse(
1441
+ message: string,
1442
+ options?: { retryAfter?: string | null },
1443
+ ): Response {
1444
+ const headers = new Headers({ "Content-Type": "application/json" });
1445
+ const retryAfter = options?.retryAfter?.trim();
1446
+ if (retryAfter && retryAfter.length > 0 && retryAfter.length <= 128) {
1447
+ headers.set("Retry-After", retryAfter);
1448
+ }
1438
1449
  return new Response(
1439
1450
  JSON.stringify({
1440
1451
  error: { message, type: "server_error", code: "combo_unavailable" },
1441
1452
  }),
1442
- { status: 503, headers: { "Content-Type": "application/json" } },
1453
+ { status: 503, headers },
1443
1454
  );
1444
1455
  }
1445
1456
 
1457
+ function comboUnavailable(comboId: string, now = Date.now()): Response {
1458
+ return comboUnavailableResponse(`No available targets for combo: ${comboId}`, {
1459
+ retryAfter: comboCooldownRetryAfterSeconds(comboId, now),
1460
+ });
1461
+ }
1462
+
1446
1463
 
1447
1464
 
1448
1465
  export interface ConsumedComboFailure {
@@ -2297,7 +2314,7 @@ export async function handleComboResponses(
2297
2314
  config,
2298
2315
  { parentThreadId: inboundClientThreadId },
2299
2316
  );
2300
- return comboUnavailableResponse(`No available targets for combo: ${comboId}`);
2317
+ return comboUnavailable(comboId);
2301
2318
  }
2302
2319
  let recovered = false;
2303
2320
  try {
@@ -2334,7 +2351,7 @@ export async function handleComboResponses(
2334
2351
  }
2335
2352
 
2336
2353
  if (!pick) {
2337
- return comboUnavailableResponse(`No available targets for combo: ${comboId}`);
2354
+ return comboUnavailable(comboId);
2338
2355
  }
2339
2356
  // One immutable combo selection trace, before any child dispatch; child
2340
2357
  // adoption below must never replace it with a concrete child route trace.
@@ -2541,7 +2558,13 @@ export async function handleComboResponses(
2541
2558
  const nextPick = advanceComboAfterFailure(config, pick, {
2542
2559
  retryAfter: failure.retryAfter,
2543
2560
  now: Date.now(),
2561
+ cooldownScope: comboFailureCooldownScope(failure.response.status, failure.classificationText, {
2562
+ code: failure.upstreamCode,
2563
+ }),
2544
2564
  eligible: payloadEligible,
2565
+ status: failure.response.status,
2566
+ code: failure.upstreamCode,
2567
+ message: failure.classificationText,
2545
2568
  });
2546
2569
  if (!nextPick) adoptFailedChildLog(childLog);
2547
2570
  pick = nextPick;
@@ -2697,6 +2720,23 @@ async function handleResponsesInner(
2697
2720
  }
2698
2721
  return decodeRequestErrorResponse(err, "responses");
2699
2722
  }
2723
+ // An effort row naming a table-less combo (`combo/x--high`) must reach the combo dispatcher
2724
+ // as its base id, so the selector is normalized here, before comboIdFromRawBody reads model.
2725
+ const comboEffortRow = !options.comboAttempt && body && typeof body === "object" && !Array.isArray(body)
2726
+ && typeof (body as { model?: unknown }).model === "string"
2727
+ ? parseRequestEffortRowId((body as { model: string }).model, config)
2728
+ : null;
2729
+ if (comboEffortRow) {
2730
+ const raw = body as Record<string, unknown>;
2731
+ raw.model = comboEffortRow.baseId;
2732
+ const rawReasoning = raw.reasoning;
2733
+ raw.reasoning = {
2734
+ ...(rawReasoning && typeof rawReasoning === "object" && !Array.isArray(rawReasoning)
2735
+ ? rawReasoning as Record<string, unknown>
2736
+ : {}),
2737
+ effort: comboEffortRow.effort,
2738
+ };
2739
+ }
2700
2740
  const comboId = !options.comboAttempt ? comboIdFromRawBody(body, config) : null;
2701
2741
  if (comboId && Object.hasOwn(config.combos ?? {}, comboId)) {
2702
2742
  options.onRequestBodyRead?.();
@@ -2751,6 +2791,20 @@ async function handleResponsesInner(
2751
2791
  let toolBridgeMaps: ReturnType<typeof buildToolBridgeMaps>;
2752
2792
  try {
2753
2793
  parsed = parseRequest(body);
2794
+ const effortRow = parseRequestEffortRowId(parsed.modelId, config);
2795
+ if (effortRow) {
2796
+ parsed.modelId = effortRow.baseId;
2797
+ parsed.options.reasoning = effortRow.effort;
2798
+ const raw = parsed._rawBody as Record<string, unknown>;
2799
+ const rawReasoning = raw.reasoning;
2800
+ raw.model = effortRow.baseId;
2801
+ raw.reasoning = {
2802
+ ...(rawReasoning && typeof rawReasoning === "object" && !Array.isArray(rawReasoning)
2803
+ ? rawReasoning as Record<string, unknown>
2804
+ : {}),
2805
+ effort: effortRow.effort,
2806
+ };
2807
+ }
2754
2808
  if (options.comboReplaySnapshot?.recoveredPlaintext) {
2755
2809
  markBodyNonPersistable(parsed._rawBody);
2756
2810
  }
@@ -2881,7 +2935,7 @@ async function handleResponsesInner(
2881
2935
  logCtx.routeDecision = route.routeDecision;
2882
2936
  } catch (err) {
2883
2937
  if (err instanceof NoAvailableComboTargetsError) {
2884
- return comboUnavailableResponse(err.message);
2938
+ return comboUnavailable(err.comboId);
2885
2939
  }
2886
2940
  if (err instanceof NoEligiblePolicyCandidateError) {
2887
2941
  // Persist the evaluation trace (per-candidate exclusions + the
@@ -2991,7 +3045,7 @@ async function handleResponsesInner(
2991
3045
  logCtx.routeDecision = route.routeDecision;
2992
3046
  } catch (err) {
2993
3047
  if (err instanceof NoAvailableComboTargetsError) {
2994
- return comboUnavailableResponse(err.message);
3048
+ return comboUnavailable(err.comboId);
2995
3049
  }
2996
3050
  if (err instanceof NoEligiblePolicyCandidateError) {
2997
3051
  logCtx.routeDecision = err.trace;
@@ -3115,7 +3169,7 @@ async function handleResponsesInner(
3115
3169
  logCtx.routeDecision = route.routeDecision;
3116
3170
  } catch (err) {
3117
3171
  if (err instanceof NoAvailableComboTargetsError) {
3118
- return comboUnavailableResponse(err.message);
3172
+ return comboUnavailable(err.comboId);
3119
3173
  }
3120
3174
  if (err instanceof NoEligiblePolicyCandidateError) {
3121
3175
  logCtx.routeDecision = err.trace;
@@ -348,7 +348,10 @@ export interface OcxConfig {
348
348
  * the guess is wrong.
349
349
  */
350
350
  oauthOpenBrowser?: boolean;
351
- /** Maximum usage-log bytes read for one management snapshot. */
351
+ /**
352
+ * @deprecated Compatibility-only limit for bounded legacy usage readers.
353
+ * `GET /api/usage` always aggregates the complete ledger.
354
+ */
352
355
  managementUsageMaxReadBytes?: number;
353
356
  providers: Record<string, OcxProviderConfig>;
354
357
  defaultProvider: string;
@@ -366,6 +369,12 @@ export interface OcxConfig {
366
369
  };
367
370
  /** Enable the shipped model alias patterns for providers without an override. */
368
371
  defaultModelAliases?: boolean;
372
+ /**
373
+ * Opt-in Cursor Private Inference compatibility rows. When true, `/v1/models`
374
+ * adds `<base-id>--<effort>` selectors for reasoning-capable model ids absent
375
+ * from Cursor's built-in effort table. Omitted/false preserves discovery output.
376
+ */
377
+ cursorEffortRows?: boolean;
369
378
  /** Explicit top-level deletion intent used by stale whole-config rebases. */
370
379
  configRebaseProvenance?: OcxConfigRebaseProvenance | Record<string, unknown>;
371
380
  /** OpenAI provider-contract migration marker (v2 = single `openai` provider with account mode). */
@@ -35,20 +35,23 @@ export function namespacedToolName(namespace: string | undefined, name: string):
35
35
  * Codex unified-exec name normalization.
36
36
  *
37
37
  * Codex's code-mode shell tool is declared as `exec` (a freeform custom tool whose own
38
- * description mentions the nested `await tools.exec_command(...)` helper). Routed models
39
- * DeepSeek in particular — sometimes echo that helper name as the tool-call name, emitting
40
- * `exec_command` or `apply_patch` instead of the declared `exec`. Accept these nested helper
41
- * names only when the request catalog actually declares `exec` and does not itself declare the
42
- * emitted name (an MCP server may legitimately advertise one under its own namespace).
38
+ * description mentions the nested `await tools.exec_command(...)` helper). Some routed providers
39
+ * echo that helper name as the tool-call name, emitting `exec_command`, `write_stdin`, or
40
+ * `apply_patch` instead of the declared `exec`. Accept these nested helper names only when the
41
+ * request catalog actually declares `exec` and does not itself declare the emitted name (an MCP
42
+ * server may legitimately advertise one under its own namespace).
43
43
  */
44
44
  const LEGACY_SHELL_BRIDGE_TOOL_NAMES = ["exec_command", "shell_command"] as const;
45
- const CODE_MODE_HELPER_TOOL_NAMES = [...LEGACY_SHELL_BRIDGE_TOOL_NAMES, "apply_patch"] as const;
45
+ const CODE_MODE_HELPER_TOOL_NAMES = [
46
+ ...LEGACY_SHELL_BRIDGE_TOOL_NAMES,
47
+ "write_stdin",
48
+ "apply_patch",
49
+ ] as const;
46
50
 
47
51
  /**
48
52
  * The one declared name that turns nested-helper normalization on. Declaring it is not just a
49
- * name: it also decides whether an emitted `exec_command`/`shell_command`/`apply_patch` is
50
- * accepted as that shell tool, so callers that build declared-name sets must add it only for a
51
- * genuine bare declaration.
53
+ * name: it also decides whether an emitted helper name is accepted as that shell tool, so callers
54
+ * that build declared-name sets must add it only for a genuine bare declaration.
52
55
  */
53
56
  export const CODE_MODE_EXEC_TOOL_NAME = "exec";
54
57
 
@@ -1,3 +1,5 @@
1
+ import { normalizeCursorClaudeId } from "../adapters/cursor/claude-id";
2
+
1
3
  /**
2
4
  * Expected-price overlay for models whose jawcode cost rows are missing or all-zero
3
5
  * (subscription/OAuth surfaces). Sourced from official pricing pages only
@@ -56,6 +58,10 @@ const GEMINI_36_FLASH: Cost4 = { input: 1.5, output: 7.5, cacheRead: 0.15, cache
56
58
  // through 2026-12-31, stepping up to $1.50 / $7.50 on 2027-01-01. Revisit this row
57
59
  // then — the promotional rate is dated on the pricing page, not open-ended.
58
60
  const GEMINI_37_FLASH: Cost4 = { input: 0.75, output: 3.75, cacheRead: 0.075, cacheWrite: 0 };
61
+ // Gemini 3.8 Flash carries the same published promotional shape as 3.7 through 2026-12-31,
62
+ // rising to $1.50 / $7.50 on 2027-01-01. A SEPARATE constant on purpose: equal today, but
63
+ // aliasing them would silently drag 3.8 along if 3.7's row is ever re-verified differently.
64
+ const GEMINI_38_FLASH: Cost4 = { input: 0.75, output: 3.75, cacheRead: 0.075, cacheWrite: 0 };
59
65
  const MINIMAX_M21_HIGHSPEED: Cost4 = { input: 0.6, output: 2.4, cacheRead: 0.03, cacheWrite: 0.375 };
60
66
  const KIMI_K3: Cost4 = { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3 };
61
67
  const KIMI_K27_CODE: Cost4 = { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite: 0.95 };
@@ -79,8 +85,19 @@ const ANTHROPIC_PRICING = "https://platform.claude.com/docs/en/about-claude/pric
79
85
 
80
86
  const GEMINI_PRICING = "https://ai.google.dev/gemini-api/docs/pricing (2026-07-22); cacheWrite=0: storage is billed per-hour, not per-token";
81
87
  const GEMINI_37_PRICING = "https://ai.google.dev/gemini-api/docs/pricing (2026-08-14); promotional rate through 2026-12-31, rises to 1.50/7.50 on 2027-01-01; cacheWrite=0: storage is billed per-hour, not per-token";
88
+ const GEMINI_38_PRICING = "https://ai.google.dev/gemini-api/docs/pricing (2026-09-03); promotional rate through 2026-12-31, rises to 1.50/7.50 on 2027-01-01; cacheWrite=0: storage is billed per-hour, not per-token";
82
89
  const MINIMAX_PRICING = "https://platform.minimax.io/docs/guides/pricing-paygo";
83
90
  const OPENAI_GPT56_PRICING = "https://developers.openai.com/api/docs/pricing";
91
+ const META_MODEL_PRICING = "https://dev.meta.ai/docs/pricing-rate-limits";
92
+ /*
93
+ * Shared by both Meta providers. Overlays resolve by EXACT provider id, so `meta-muse`
94
+ * cannot inherit `meta-model`'s rows — and an unpriced provider whose whole warning is
95
+ * "treat every call as billable" would report no cost at all.
96
+ */
97
+ const META_MUSE_SPARK_13: Cost4 = { input: 1.25, output: 4.25, cacheRead: 0.15, cacheWrite: 0 };
98
+ const META_MUSE_SPARK_13_CONTRIBUTOR: Cost4 = { input: 0.1, output: 0.2, cacheRead: 0.002, cacheWrite: 0 };
99
+ const META_SPARK_SOURCE = `Meta Model API published price ${META_MODEL_PRICING}`;
100
+ const META_SPARK_CONTRIBUTOR_SOURCE = `Meta Model API published Contributor-tier price ${META_MODEL_PRICING}; data-sharing discount tier`;
84
101
  const DEEPSEEK_PRICING = "https://api-docs.deepseek.com/quick_start/pricing-details-usd; V4 Flash alias transition scheduled 2026-07-24 — re-verify after";
85
102
  // Kimi official tables publish input/output/cache-hit only; cacheWrite is mapped to the
86
103
  // cache-miss input price (Kimi auto-caches with no separate write billing). 2026-07-20 re-verified.
@@ -100,13 +117,8 @@ export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [
100
117
  // bundle collapses anthropic-apikey onto anthropic).
101
118
  { provider: "anthropic", modelId: "claude-fable-5-1", cost4: CLAUDE_FABLE_51, source: `anthropic official Claude Fable 5.1 ${ANTHROPIC_PRICING}; cache hit = 0.025x base input`, verifiedAt: "2026-09-02", status: "verified" },
102
119
  { provider: "anthropic-apikey", modelId: "claude-fable-5-1", cost4: CLAUDE_FABLE_51, source: `anthropic official Claude Fable 5.1 ${ANTHROPIC_PRICING}; cache hit = 0.025x base input`, verifiedAt: "2026-09-02", status: "verified" },
103
- // Cursor seeds Fable 5.1 preemptively under three spellings (adapters/cursor/catalog.ts);
104
- // the model-level vendor fallback only searches jawcode metadata, which has no Fable 5.1
105
- // row yet, so each Cursor spelling needs its own overlay. Vendor list price, like the
106
- // cursor/claude-opus-5 row.
120
+ // Cursor canonicalizes every Fable 5.1 spelling onto this sole overlay row.
107
121
  { provider: "cursor", modelId: "claude-fable-5-1", cost4: CLAUDE_FABLE_51, source: `anthropic official Claude Fable 5.1 ${ANTHROPIC_PRICING}; cache hit = 0.025x base input; vendor list price applied to the Cursor surface`, verifiedAt: "2026-09-02", status: "verified-derived" },
108
- { provider: "cursor", modelId: "claude-fable-5.1", cost4: CLAUDE_FABLE_51, source: `anthropic official Claude Fable 5.1 ${ANTHROPIC_PRICING}; cache hit = 0.025x base input; vendor list price applied to the Cursor surface`, verifiedAt: "2026-09-02", status: "verified-derived" },
109
- { provider: "cursor", modelId: "claude-5.1-fable", cost4: CLAUDE_FABLE_51, source: `anthropic official Claude Fable 5.1 ${ANTHROPIC_PRICING}; cache hit = 0.025x base input; vendor list price applied to the Cursor surface`, verifiedAt: "2026-09-02", status: "verified-derived" },
110
122
  // claude-opus-5 is exposed by three providers but absent from the jawcode bundle, so
111
123
  // cost resolution returned null and the Logs `~$` column rendered an em dash. The
112
124
  // model-level vendor fallback only searches jawcode metadata, never overlays, so one
@@ -126,6 +138,10 @@ export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [
126
138
  // 3.7 Flash rides CCA, whose billing equivalence to the Developer API list price is
127
139
  // not published, so this is `verified-derived` rather than `verified`: the number is
128
140
  // proven, the claim that Antigravity charges it is inferred.
141
+ { provider: "google-antigravity", modelId: "gemini-3.8-flash", cost4: GEMINI_38_FLASH, source: `derived: Gemini 3.8 Flash promotional rate through 2026-12-31 ${GEMINI_38_PRICING}`, verifiedAt: "2026-09-03", status: "verified-derived" },
142
+ { provider: "google-antigravity", modelId: "gemini-3.8-flash-low", cost4: GEMINI_38_FLASH, source: `derived: gemini-3.8-flash ${GEMINI_38_PRICING}`, verifiedAt: "2026-09-03", status: "verified-derived" },
143
+ { provider: "google-antigravity", modelId: "gemini-3.8-flash-medium", cost4: GEMINI_38_FLASH, source: `derived: gemini-3.8-flash ${GEMINI_38_PRICING}`, verifiedAt: "2026-09-03", status: "verified-derived" },
144
+ { provider: "google-antigravity", modelId: "gemini-3.8-flash-high", cost4: GEMINI_38_FLASH, source: `derived: gemini-3.8-flash ${GEMINI_38_PRICING}`, verifiedAt: "2026-09-03", status: "verified-derived" },
129
145
  { provider: "google-antigravity", modelId: "gemini-3.7-flash", cost4: GEMINI_37_FLASH, source: `derived: Gemini 3.7 Flash promotional rate through 2026-12-31 ${GEMINI_37_PRICING}`, verifiedAt: "2026-08-14", status: "verified-derived" },
130
146
  // Retained after the 3.6 retirement: historical usage.jsonl rows still carry these
131
147
  // ids, and dropping the row would silently zero the cost of requests already made.
@@ -139,6 +155,19 @@ export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [
139
155
  { provider: "openai-apikey", modelId: "gpt-5.6-sol-pro", cost4: GPT56_SOL, source: `collapsed base ID ${OPENAI_GPT56_PRICING}`, verifiedAt: "2026-08-03", status: "verified-derived" },
140
156
  { provider: "openai-apikey", modelId: "gpt-5.6-terra-pro", cost4: GPT56_TERRA, source: `collapsed base ID ${OPENAI_GPT56_PRICING}`, verifiedAt: "2026-08-03", status: "verified-derived" },
141
157
  { provider: "openai-apikey", modelId: "gpt-5.6-luna-pro", cost4: GPT56_LUNA, source: `collapsed base ID ${OPENAI_GPT56_PRICING}`, verifiedAt: "2026-08-03", status: "verified-derived" },
158
+ // Meta Model API direct provider. `meta-model` has no jawcode metadata alias, so an
159
+ // unpriced row falls through the whole resolution chain and the Logs cost column
160
+ // renders nothing — these exact overlays are the only source. Both are Meta's own
161
+ // published list prices for Meta's own endpoint (hence "verified", not derived), and
162
+ // they match the figures Command Code republishes for the same two models.
163
+ // cacheWrite=0: Meta publishes a cached-input price but no cache-write charge.
164
+ { provider: "meta-model", modelId: "muse-spark-1.3", cost4: META_MUSE_SPARK_13, source: META_SPARK_SOURCE, verifiedAt: "2026-09-03", status: "verified" },
165
+ { provider: "meta-model", modelId: "muse-spark-1.3-contributor", cost4: META_MUSE_SPARK_13_CONTRIBUTOR, source: META_SPARK_CONTRIBUTOR_SOURCE, verifiedAt: "2026-09-03", status: "verified" },
166
+ // Same endpoint, same list price, different credential. Meta does not authorize this
167
+ // reuse and settlement is not observable, so these are the public Model API rates as a
168
+ // conservative estimate — not evidence of how the call is actually billed.
169
+ { provider: "meta-muse", modelId: "muse-spark-1.3", cost4: META_MUSE_SPARK_13, source: META_SPARK_SOURCE, verifiedAt: "2026-09-03", status: "verified-derived" },
170
+ { provider: "meta-muse", modelId: "muse-spark-1.3-contributor", cost4: META_MUSE_SPARK_13_CONTRIBUTOR, source: META_SPARK_CONTRIBUTOR_SOURCE, verifiedAt: "2026-09-03", status: "verified-derived" },
142
171
  // Daybreak aliases: priced as their current snapshots (red -> gpt-5.6-cyber,
143
172
  // blue -> gpt-5.6-sol). The alias ids carry no rows of their own upstream, hence
144
173
  // verified-derived. Blue deliberately reuses GPT56_SOL rather than duplicating the tuple.
@@ -163,6 +192,7 @@ export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [
163
192
  { provider: "google", modelId: "gemini-3.6-flash", cost4: GEMINI_36_FLASH, source: GEMINI_PRICING, verifiedAt: "2026-07-22", status: "verified" },
164
193
  // Developer API row: the price IS published for this surface, so `verified`.
165
194
  { provider: "google", modelId: "gemini-3.7-flash", cost4: GEMINI_37_FLASH, source: GEMINI_37_PRICING, verifiedAt: "2026-08-14", status: "verified" },
195
+ { provider: "google", modelId: "gemini-3.8-flash", cost4: GEMINI_38_FLASH, source: GEMINI_38_PRICING, verifiedAt: "2026-09-03", status: "verified" },
166
196
  { provider: "google-antigravity", modelId: "gemini-3.1-pro-preview", cost4: GEMINI_31_PRO, source: GEMINI_PRICING, verifiedAt: "2026-07-20", status: "verified" },
167
197
  // Antigravity-bundled third-party models — derived from the underlying vendor's
168
198
  // official API price (Antigravity itself bills via subscription quota).
@@ -236,8 +266,14 @@ export function findExpectedPriceOverlay(
236
266
  overlays: readonly ExpectedPriceOverlay[] = EXPECTED_PRICE_OVERLAYS,
237
267
  ): ExpectedPriceOverlay | undefined {
238
268
  const exact = overlays.filter(row => row.provider === provider && row.modelId === modelId);
239
- return exact.find(row => row.status === "verified")
269
+ const match = exact.find(row => row.status === "verified")
240
270
  ?? exact.find(row => row.status === "verified-derived");
271
+ if (match || provider !== "cursor") return match;
272
+ const canonicalBaseId = normalizeCursorClaudeId(modelId)?.canonicalBaseId;
273
+ if (!canonicalBaseId) return undefined;
274
+ const canonical = overlays.filter(row => row.provider === provider && row.modelId === canonicalBaseId);
275
+ return canonical.find(row => row.status === "verified")
276
+ ?? canonical.find(row => row.status === "verified-derived");
241
277
  }
242
278
 
243
279
  /** OpenAI Fast price multipliers retained as a compatibility export. */