@kontextmind/kxm 0.7.12 → 0.7.13

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.
@@ -11,7 +11,7 @@
11
11
  "name": "kxm",
12
12
  "source": "./plugins/kxm",
13
13
  "description": "Durable workflows, peer agents, and kxm tui",
14
- "version": "0.7.12",
14
+ "version": "0.7.13",
15
15
  "category": "development",
16
16
  "tags": ["kxm", "multi-agent", "workflows", "mcp"]
17
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextmind/kxm",
3
- "version": "0.7.12",
3
+ "version": "0.7.13",
4
4
  "description": "KXM local-first multi-agent orchestration and operator dashboard",
5
5
  "type": "module",
6
6
  "author": "KontextMind",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "kxm",
4
4
  "displayName": "KXM",
5
- "version": "0.7.12",
5
+ "version": "0.7.13",
6
6
  "description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
7
7
  "author": {
8
8
  "name": "KontextMind",
@@ -24813,6 +24813,18 @@ function loadPriceCatalog(rootOrPath) {
24813
24813
  const content = readFileSync7(candidatePath, "utf8");
24814
24814
  return parsePriceCatalog(content);
24815
24815
  }
24816
+ function loadPriceCatalogForEstimate(options) {
24817
+ let catalog;
24818
+ try {
24819
+ catalog = options?.priceCatalog ? parsePriceCatalog(JSON.stringify(options.priceCatalog)) : loadPriceCatalog(options?.projectRoot ?? process.cwd());
24820
+ } catch {
24821
+ return { catalog: void 0, unavailable: true, stale: false };
24822
+ }
24823
+ if (catalog && catalog.date !== (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)) {
24824
+ return { catalog: void 0, unavailable: false, stale: true };
24825
+ }
24826
+ return { catalog, unavailable: false, stale: false };
24827
+ }
24816
24828
 
24817
24829
  // plugins/kxm/src/vnext-engine.ts
24818
24830
  var import_yaml5 = __toESM(require_dist(), 1);
@@ -46205,6 +46217,41 @@ function resolveActiveMode(config, majorModeName = "coder", domainNames = []) {
46205
46217
  model: major.model || "grok/grok-4.6"
46206
46218
  };
46207
46219
  }
46220
+ var CATALOG_DATE_RE = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/;
46221
+ function boundedCatalogDate(value) {
46222
+ return typeof value === "string" && CATALOG_DATE_RE.test(value) ? value : void 0;
46223
+ }
46224
+ function catalogDateForStatus(loaded, injected, projectRoot) {
46225
+ const fromLoaded = boundedCatalogDate(loaded.catalog?.date);
46226
+ if (fromLoaded) return fromLoaded;
46227
+ const fromInjected = boundedCatalogDate(injected?.date);
46228
+ if (fromInjected) return fromInjected;
46229
+ if (!loaded.stale) return void 0;
46230
+ try {
46231
+ return boundedCatalogDate(loadPriceCatalog(projectRoot)?.date);
46232
+ } catch {
46233
+ return void 0;
46234
+ }
46235
+ }
46236
+ function describePriceCatalogStatus(loaded, catalogDate) {
46237
+ if (loaded.stale) {
46238
+ return {
46239
+ catalogStatus: "stale",
46240
+ catalogReason: catalogDate ? `price catalog stale (dated ${catalogDate})` : "price catalog stale"
46241
+ };
46242
+ }
46243
+ if (loaded.unavailable) {
46244
+ return { catalogStatus: "corrupt", catalogReason: "price catalog corrupt" };
46245
+ }
46246
+ if (!loaded.catalog) {
46247
+ return { catalogStatus: "missing", catalogReason: "price catalog missing" };
46248
+ }
46249
+ return { catalogStatus: "verified", catalogReason: "price catalog verified" };
46250
+ }
46251
+ function formatExplainCost(value, footprint, suffix = "") {
46252
+ if (value !== null) return `$${value.toFixed(4)}${suffix}`;
46253
+ return footprint.catalogStatus === "verified" ? "unmetered/unknown" : footprint.catalogReason;
46254
+ }
46208
46255
  function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog) {
46209
46256
  const breakdown = [];
46210
46257
  const baseSystemPromptChars = 3200;
@@ -46250,7 +46297,14 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
46250
46297
  const totalTokens = breakdown.reduce((sum, item) => sum + item.estimatedTokens, 0);
46251
46298
  const maxContextWindow = 2e5;
46252
46299
  const contextWindowRatio = Math.round(totalTokens / maxContextWindow * 1e3) / 10;
46253
- const cat = catalog || loadPriceCatalog(projectRoot);
46300
+ const loaded = loadPriceCatalogForEstimate(
46301
+ catalog ? { priceCatalog: catalog } : { projectRoot }
46302
+ );
46303
+ const catalogInfo = describePriceCatalogStatus(
46304
+ loaded,
46305
+ catalogDateForStatus(loaded, catalog, projectRoot)
46306
+ );
46307
+ const cat = loaded.catalog;
46254
46308
  const rawModel = resolved.model || "grok/grok-4.6";
46255
46309
  const [prov, mod] = rawModel.includes("/") ? rawModel.split("/", 2) : [void 0, rawModel];
46256
46310
  const inputCost = cat ? calculateModelCost(cat, {
@@ -46289,7 +46343,9 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
46289
46343
  inputCostUsd: inputCost?.costUsd ?? null,
46290
46344
  cacheReadCostUsd: cacheReadCost?.costUsd ?? null,
46291
46345
  outputCostEstimateUsd: outputEstimateCost?.costUsd ?? null
46292
- }
46346
+ },
46347
+ catalogStatus: catalogInfo.catalogStatus,
46348
+ catalogReason: catalogInfo.catalogReason
46293
46349
  };
46294
46350
  }
46295
46351
  function formatModesExplainReport(footprint) {
@@ -46307,6 +46363,8 @@ ${divider}
46307
46363
  out += `Enabled Domains: ${footprint.enabledDomains.length > 0 ? footprint.enabledDomains.join(", ") : "(none)"}
46308
46364
  `;
46309
46365
  out += `Target Model: ${footprint.model}
46366
+ `;
46367
+ out += `Catalog status: ${footprint.catalogReason}
46310
46368
 
46311
46369
  `;
46312
46370
  out += `CONTEXT BREAKDOWN:
@@ -46326,11 +46384,11 @@ ${divider}
46326
46384
  `;
46327
46385
  out += `PROJECTED COSTS (per turn):
46328
46386
  `;
46329
- out += ` \u2022 Initial Turn Input Cost: ${footprint.projectedCost.inputCostUsd !== null ? `$${footprint.projectedCost.inputCostUsd.toFixed(4)}` : "unmetered/unknown"}
46387
+ out += ` \u2022 Initial Turn Input Cost: ${formatExplainCost(footprint.projectedCost.inputCostUsd, footprint)}
46330
46388
  `;
46331
- out += ` \u2022 Subsequent Cache-Read Cost: ${footprint.projectedCost.cacheReadCostUsd !== null ? `$${footprint.projectedCost.cacheReadCostUsd.toFixed(4)} (approx 90% savings)` : "unmetered/unknown"}
46389
+ out += ` \u2022 Subsequent Cache-Read Cost: ${formatExplainCost(footprint.projectedCost.cacheReadCostUsd, footprint, " (approx 90% savings)")}
46332
46390
  `;
46333
- out += ` \u2022 Output Estimate (1k tokens): ${footprint.projectedCost.outputCostEstimateUsd !== null ? `$${footprint.projectedCost.outputCostEstimateUsd.toFixed(4)}` : "unmetered/unknown"}
46391
+ out += ` \u2022 Output Estimate (1k tokens): ${formatExplainCost(footprint.projectedCost.outputCostEstimateUsd, footprint)}
46334
46392
  `;
46335
46393
  out += `${divider}
46336
46394
  `;
@@ -17036,7 +17036,7 @@ async function deliverInboxNotification(messageId, delivered, notify) {
17036
17036
  }
17037
17037
 
17038
17038
  // plugins/kxm/src/mcp-server.ts
17039
- var VERSION = "0.7.12";
17039
+ var VERSION = "0.7.13";
17040
17040
  var inbox = /* @__PURE__ */ new Map();
17041
17041
  var notifiedInbox = /* @__PURE__ */ new Set();
17042
17042
  var meshClient;
@@ -22674,6 +22674,18 @@ function loadPriceCatalog(rootOrPath) {
22674
22674
  const content = readFileSync5(candidatePath, "utf8");
22675
22675
  return parsePriceCatalog(content);
22676
22676
  }
22677
+ function loadPriceCatalogForEstimate(options) {
22678
+ let catalog;
22679
+ try {
22680
+ catalog = options?.priceCatalog ? parsePriceCatalog(JSON.stringify(options.priceCatalog)) : loadPriceCatalog(options?.projectRoot ?? process.cwd());
22681
+ } catch {
22682
+ return { catalog: void 0, unavailable: true, stale: false };
22683
+ }
22684
+ if (catalog && catalog.date !== (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)) {
22685
+ return { catalog: void 0, unavailable: false, stale: true };
22686
+ }
22687
+ return { catalog, unavailable: false, stale: false };
22688
+ }
22677
22689
 
22678
22690
  // plugins/kxm/src/vnext-engine.ts
22679
22691
  var import_yaml5 = __toESM(require_dist(), 1);
@@ -27286,10 +27298,16 @@ function createVnextPiProducer(options = {}) {
27286
27298
  const cacheReadTokens = typeof usage?.cacheRead === "number" ? usage.cacheRead : null;
27287
27299
  const cacheWriteTokens = typeof usage?.cacheWrite === "number" ? usage.cacheWrite : null;
27288
27300
  const contextTokens = typeof usage?.contextUsage?.tokens === "number" ? usage.contextUsage.tokens : tokensIn;
27289
- const catalog = options.priceCatalog ?? loadPriceCatalog(options.projectRoot ?? process.cwd());
27290
- let costBasis = "unknown";
27291
- let costUsd = null;
27292
- let priceRef;
27301
+ const providerMetadata = {};
27302
+ const loaded = loadPriceCatalogForEstimate({
27303
+ priceCatalog: options.priceCatalog,
27304
+ projectRoot: options.projectRoot
27305
+ });
27306
+ if (loaded.unavailable) providerMetadata.priceCatalogUnavailable = true;
27307
+ if (loaded.stale) providerMetadata.priceCatalogStale = true;
27308
+ const catalog = loaded.catalog;
27309
+ const costBasis = "unknown";
27310
+ const costUsd = null;
27293
27311
  if (catalog) {
27294
27312
  const calculated = calculateModelCost(catalog, {
27295
27313
  model: resolved.model,
@@ -27300,10 +27318,10 @@ function createVnextPiProducer(options = {}) {
27300
27318
  cacheWriteTokens,
27301
27319
  contextTokens
27302
27320
  });
27303
- if (calculated) {
27304
- costBasis = "metered";
27305
- costUsd = calculated.costUsd;
27306
- priceRef = calculated.priceRef;
27321
+ if (calculated && Number.isFinite(calculated.costUsd)) {
27322
+ providerMetadata.listCostUsd = calculated.costUsd;
27323
+ providerMetadata.listPriceRef = calculated.priceRef;
27324
+ providerMetadata.listPriceSha256 = catalog.sha256;
27307
27325
  }
27308
27326
  }
27309
27327
  return {
@@ -27320,9 +27338,9 @@ function createVnextPiProducer(options = {}) {
27320
27338
  provider: resolved.provider,
27321
27339
  requestedModel: resolved.model,
27322
27340
  effectiveModel: resolved.model,
27341
+ providerMetadata,
27323
27342
  ...resolved.thinking !== void 0 ? { thinking: resolved.thinking } : {},
27324
- agentRole: request.agentRole ?? request.agentId,
27325
- ...priceRef !== void 0 ? { priceRef } : {}
27343
+ agentRole: request.agentRole ?? request.agentId
27326
27344
  };
27327
27345
  },
27328
27346
  async closeRun(runId) {
@@ -28371,6 +28389,41 @@ function resolveActiveMode(config, majorModeName = "coder", domainNames = []) {
28371
28389
  model: major.model || "grok/grok-4.6"
28372
28390
  };
28373
28391
  }
28392
+ var CATALOG_DATE_RE = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/;
28393
+ function boundedCatalogDate(value) {
28394
+ return typeof value === "string" && CATALOG_DATE_RE.test(value) ? value : void 0;
28395
+ }
28396
+ function catalogDateForStatus(loaded, injected, projectRoot) {
28397
+ const fromLoaded = boundedCatalogDate(loaded.catalog?.date);
28398
+ if (fromLoaded) return fromLoaded;
28399
+ const fromInjected = boundedCatalogDate(injected?.date);
28400
+ if (fromInjected) return fromInjected;
28401
+ if (!loaded.stale) return void 0;
28402
+ try {
28403
+ return boundedCatalogDate(loadPriceCatalog(projectRoot)?.date);
28404
+ } catch {
28405
+ return void 0;
28406
+ }
28407
+ }
28408
+ function describePriceCatalogStatus(loaded, catalogDate) {
28409
+ if (loaded.stale) {
28410
+ return {
28411
+ catalogStatus: "stale",
28412
+ catalogReason: catalogDate ? `price catalog stale (dated ${catalogDate})` : "price catalog stale"
28413
+ };
28414
+ }
28415
+ if (loaded.unavailable) {
28416
+ return { catalogStatus: "corrupt", catalogReason: "price catalog corrupt" };
28417
+ }
28418
+ if (!loaded.catalog) {
28419
+ return { catalogStatus: "missing", catalogReason: "price catalog missing" };
28420
+ }
28421
+ return { catalogStatus: "verified", catalogReason: "price catalog verified" };
28422
+ }
28423
+ function formatExplainCost(value, footprint, suffix = "") {
28424
+ if (value !== null) return `$${value.toFixed(4)}${suffix}`;
28425
+ return footprint.catalogStatus === "verified" ? "unmetered/unknown" : footprint.catalogReason;
28426
+ }
28374
28427
  function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog) {
28375
28428
  const breakdown = [];
28376
28429
  const baseSystemPromptChars = 3200;
@@ -28416,7 +28469,14 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
28416
28469
  const totalTokens = breakdown.reduce((sum, item) => sum + item.estimatedTokens, 0);
28417
28470
  const maxContextWindow = 2e5;
28418
28471
  const contextWindowRatio = Math.round(totalTokens / maxContextWindow * 1e3) / 10;
28419
- const cat = catalog || loadPriceCatalog(projectRoot);
28472
+ const loaded = loadPriceCatalogForEstimate(
28473
+ catalog ? { priceCatalog: catalog } : { projectRoot }
28474
+ );
28475
+ const catalogInfo = describePriceCatalogStatus(
28476
+ loaded,
28477
+ catalogDateForStatus(loaded, catalog, projectRoot)
28478
+ );
28479
+ const cat = loaded.catalog;
28420
28480
  const rawModel = resolved.model || "grok/grok-4.6";
28421
28481
  const [prov, mod] = rawModel.includes("/") ? rawModel.split("/", 2) : [void 0, rawModel];
28422
28482
  const inputCost = cat ? calculateModelCost(cat, {
@@ -28455,7 +28515,9 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
28455
28515
  inputCostUsd: inputCost?.costUsd ?? null,
28456
28516
  cacheReadCostUsd: cacheReadCost?.costUsd ?? null,
28457
28517
  outputCostEstimateUsd: outputEstimateCost?.costUsd ?? null
28458
- }
28518
+ },
28519
+ catalogStatus: catalogInfo.catalogStatus,
28520
+ catalogReason: catalogInfo.catalogReason
28459
28521
  };
28460
28522
  }
28461
28523
  function formatModesExplainReport(footprint) {
@@ -28473,6 +28535,8 @@ ${divider}
28473
28535
  out += `Enabled Domains: ${footprint.enabledDomains.length > 0 ? footprint.enabledDomains.join(", ") : "(none)"}
28474
28536
  `;
28475
28537
  out += `Target Model: ${footprint.model}
28538
+ `;
28539
+ out += `Catalog status: ${footprint.catalogReason}
28476
28540
 
28477
28541
  `;
28478
28542
  out += `CONTEXT BREAKDOWN:
@@ -28492,11 +28556,11 @@ ${divider}
28492
28556
  `;
28493
28557
  out += `PROJECTED COSTS (per turn):
28494
28558
  `;
28495
- out += ` \u2022 Initial Turn Input Cost: ${footprint.projectedCost.inputCostUsd !== null ? `$${footprint.projectedCost.inputCostUsd.toFixed(4)}` : "unmetered/unknown"}
28559
+ out += ` \u2022 Initial Turn Input Cost: ${formatExplainCost(footprint.projectedCost.inputCostUsd, footprint)}
28496
28560
  `;
28497
- out += ` \u2022 Subsequent Cache-Read Cost: ${footprint.projectedCost.cacheReadCostUsd !== null ? `$${footprint.projectedCost.cacheReadCostUsd.toFixed(4)} (approx 90% savings)` : "unmetered/unknown"}
28561
+ out += ` \u2022 Subsequent Cache-Read Cost: ${formatExplainCost(footprint.projectedCost.cacheReadCostUsd, footprint, " (approx 90% savings)")}
28498
28562
  `;
28499
- out += ` \u2022 Output Estimate (1k tokens): ${footprint.projectedCost.outputCostEstimateUsd !== null ? `$${footprint.projectedCost.outputCostEstimateUsd.toFixed(4)}` : "unmetered/unknown"}
28563
+ out += ` \u2022 Output Estimate (1k tokens): ${formatExplainCost(footprint.projectedCost.outputCostEstimateUsd, footprint)}
28500
28564
  `;
28501
28565
  out += `${divider}
28502
28566
  `;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kxm-claude-plugin",
3
- "version": "0.7.12",
3
+ "version": "0.7.13",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
@@ -7,7 +7,7 @@ import { AGENT_COMMANDS_MAP, enforceToolPolicy, getMcpTools, reconcileInbox } fr
7
7
  import { deliverInboxNotification } from "./inbox.ts";
8
8
  import type { HubEvent, MessageRecord } from "./protocol.ts";
9
9
 
10
- const VERSION = "0.7.12";
10
+ const VERSION = "0.7.13";
11
11
  const inbox = new Map<string, MessageRecord>();
12
12
  const notifiedInbox = new Set<string>();
13
13
  let meshClient: HubClient | undefined;
@@ -8,7 +8,15 @@
8
8
  import { existsSync, readFileSync } from "node:fs";
9
9
  import { join } from "node:path";
10
10
  import { parse } from "yaml";
11
- import { calculateModelCost, type PriceCatalog, loadPriceCatalog } from "./prices.ts";
11
+ import {
12
+ calculateModelCost,
13
+ loadPriceCatalog,
14
+ loadPriceCatalogForEstimate,
15
+ type PriceCatalog,
16
+ type PriceCatalogEstimateSource,
17
+ } from "./prices.ts";
18
+
19
+ export type PriceCatalogStatus = "verified" | "stale" | "corrupt" | "missing";
12
20
 
13
21
  export interface MajorMode {
14
22
  description?: string | undefined;
@@ -60,6 +68,8 @@ export interface PromptFootprint {
60
68
  cacheReadCostUsd: number | null;
61
69
  outputCostEstimateUsd: number | null;
62
70
  };
71
+ catalogStatus: PriceCatalogStatus;
72
+ catalogReason: string;
63
73
  }
64
74
 
65
75
  export const DEFAULT_MODES_CONFIG: ModesConfig = Object.freeze({
@@ -195,6 +205,59 @@ export function resolveActiveMode(
195
205
  };
196
206
  }
197
207
 
208
+ const CATALOG_DATE_RE = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/;
209
+
210
+ function boundedCatalogDate(value: unknown): string | undefined {
211
+ return typeof value === "string" && CATALOG_DATE_RE.test(value) ? value : undefined;
212
+ }
213
+
214
+ function catalogDateForStatus(
215
+ loaded: PriceCatalogEstimateSource,
216
+ injected: PriceCatalog | undefined,
217
+ projectRoot: string,
218
+ ): string | undefined {
219
+ const fromLoaded = boundedCatalogDate(loaded.catalog?.date);
220
+ if (fromLoaded) return fromLoaded;
221
+ const fromInjected = boundedCatalogDate(injected?.date);
222
+ if (fromInjected) return fromInjected;
223
+ if (!loaded.stale) return undefined;
224
+ try {
225
+ return boundedCatalogDate(loadPriceCatalog(projectRoot)?.date);
226
+ } catch {
227
+ return undefined;
228
+ }
229
+ }
230
+
231
+ function describePriceCatalogStatus(
232
+ loaded: PriceCatalogEstimateSource,
233
+ catalogDate?: string,
234
+ ): { catalogStatus: PriceCatalogStatus; catalogReason: string } {
235
+ if (loaded.stale) {
236
+ return {
237
+ catalogStatus: "stale",
238
+ catalogReason: catalogDate
239
+ ? `price catalog stale (dated ${catalogDate})`
240
+ : "price catalog stale",
241
+ };
242
+ }
243
+ if (loaded.unavailable) {
244
+ return { catalogStatus: "corrupt", catalogReason: "price catalog corrupt" };
245
+ }
246
+ if (!loaded.catalog) {
247
+ return { catalogStatus: "missing", catalogReason: "price catalog missing" };
248
+ }
249
+ return { catalogStatus: "verified", catalogReason: "price catalog verified" };
250
+ }
251
+
252
+ function formatExplainCost(
253
+ value: number | null,
254
+ footprint: PromptFootprint,
255
+ suffix = "",
256
+ ): string {
257
+ if (value !== null) return `$${value.toFixed(4)}${suffix}`;
258
+ return footprint.catalogStatus === "verified" ? "unmetered/unknown" : footprint.catalogReason;
259
+ }
260
+
198
261
  /**
199
262
  * Calculate the exact prompt token footprint and projected costs for a mode configuration.
200
263
  */
@@ -258,8 +321,15 @@ export function calculatePromptFootprint(
258
321
  const maxContextWindow = 200000;
259
322
  const contextWindowRatio = Math.round((totalTokens / maxContextWindow) * 1000) / 10;
260
323
 
261
- // 5. Projected Costs
262
- const cat = catalog || loadPriceCatalog(projectRoot);
324
+ // 5. Projected Costs — unverified or stale catalogs stay unknown.
325
+ const loaded = loadPriceCatalogForEstimate(
326
+ catalog ? { priceCatalog: catalog } : { projectRoot },
327
+ );
328
+ const catalogInfo = describePriceCatalogStatus(
329
+ loaded,
330
+ catalogDateForStatus(loaded, catalog, projectRoot),
331
+ );
332
+ const cat = loaded.catalog;
263
333
  const rawModel = resolved.model || "grok/grok-4.6";
264
334
  const [prov, mod] = rawModel.includes("/") ? rawModel.split("/", 2) : [undefined, rawModel];
265
335
 
@@ -309,6 +379,8 @@ export function calculatePromptFootprint(
309
379
  cacheReadCostUsd: cacheReadCost?.costUsd ?? null,
310
380
  outputCostEstimateUsd: outputEstimateCost?.costUsd ?? null,
311
381
  },
382
+ catalogStatus: catalogInfo.catalogStatus,
383
+ catalogReason: catalogInfo.catalogReason,
312
384
  };
313
385
  }
314
386
 
@@ -324,7 +396,8 @@ export function formatModesExplainReport(footprint: PromptFootprint): string {
324
396
  out += `${divider}\n`;
325
397
  out += `Major Mode: ${footprint.majorMode}\n`;
326
398
  out += `Enabled Domains: ${footprint.enabledDomains.length > 0 ? footprint.enabledDomains.join(", ") : "(none)"}\n`;
327
- out += `Target Model: ${footprint.model}\n\n`;
399
+ out += `Target Model: ${footprint.model}\n`;
400
+ out += `Catalog status: ${footprint.catalogReason}\n\n`;
328
401
 
329
402
  out += `CONTEXT BREAKDOWN:\n`;
330
403
  for (const item of footprint.breakdown) {
@@ -339,9 +412,9 @@ export function formatModesExplainReport(footprint: PromptFootprint): string {
339
412
  out += ` ${totalPad} ${totalStr} (${footprint.contextWindowRatio}% of 200k window)\n\n`;
340
413
 
341
414
  out += `PROJECTED COSTS (per turn):\n`;
342
- out += ` • Initial Turn Input Cost: ${footprint.projectedCost.inputCostUsd !== null ? `$${footprint.projectedCost.inputCostUsd.toFixed(4)}` : "unmetered/unknown"}\n`;
343
- out += ` • Subsequent Cache-Read Cost: ${footprint.projectedCost.cacheReadCostUsd !== null ? `$${footprint.projectedCost.cacheReadCostUsd.toFixed(4)} (approx 90% savings)` : "unmetered/unknown"}\n`;
344
- out += ` • Output Estimate (1k tokens): ${footprint.projectedCost.outputCostEstimateUsd !== null ? `$${footprint.projectedCost.outputCostEstimateUsd.toFixed(4)}` : "unmetered/unknown"}\n`;
415
+ out += ` • Initial Turn Input Cost: ${formatExplainCost(footprint.projectedCost.inputCostUsd, footprint)}\n`;
416
+ out += ` • Subsequent Cache-Read Cost: ${formatExplainCost(footprint.projectedCost.cacheReadCostUsd, footprint, " (approx 90% savings)")}\n`;
417
+ out += ` • Output Estimate (1k tokens): ${formatExplainCost(footprint.projectedCost.outputCostEstimateUsd, footprint)}\n`;
345
418
  out += `${divider}\n`;
346
419
 
347
420
  return out;
@@ -135,3 +135,35 @@ export function loadPriceCatalog(rootOrPath: string): PriceCatalog | undefined {
135
135
  return parsePriceCatalog(content);
136
136
  }
137
137
 
138
+ /** Result of a hash-verified, freshness-gated catalog load used for list estimates. */
139
+ export interface PriceCatalogEstimateSource {
140
+ readonly catalog: PriceCatalog | undefined;
141
+ readonly unavailable: boolean;
142
+ readonly stale: boolean;
143
+ }
144
+
145
+ /**
146
+ * Load a price catalog the way one-shot settlement does: re-parse injected
147
+ * catalogs so the digest is checked, treat missing files as unknown, and drop
148
+ * any snapshot whose date is not today. Callers must not throw after spend.
149
+ */
150
+ export function loadPriceCatalogForEstimate(options?: {
151
+ priceCatalog?: PriceCatalog | undefined;
152
+ projectRoot?: string | undefined;
153
+ }): PriceCatalogEstimateSource {
154
+ let catalog: PriceCatalog | undefined;
155
+ try {
156
+ catalog = options?.priceCatalog
157
+ ? parsePriceCatalog(JSON.stringify(options.priceCatalog))
158
+ : loadPriceCatalog(options?.projectRoot ?? process.cwd());
159
+ } catch {
160
+ return { catalog: undefined, unavailable: true, stale: false };
161
+ }
162
+ // Without a freshness-aware vendor feed, don't silently quote an older
163
+ // local snapshot. Even today's hash-verified snapshot is only an estimate.
164
+ if (catalog && catalog.date !== new Date().toISOString().slice(0, 10)) {
165
+ return { catalog: undefined, unavailable: false, stale: true };
166
+ }
167
+ return { catalog, unavailable: false, stale: false };
168
+ }
169
+
@@ -15,7 +15,7 @@ import {
15
15
  } from "./vnext-harness.ts";
16
16
  import {
17
17
  calculateModelCost,
18
- loadPriceCatalog,
18
+ loadPriceCatalogForEstimate,
19
19
  type PriceCatalog,
20
20
  } from "./prices.ts";
21
21
 
@@ -566,12 +566,17 @@ export function createVnextPiProducer(options: VnextPiProducerOptions = {}): Vne
566
566
  const cacheWriteTokens = typeof usage?.cacheWrite === "number" ? usage.cacheWrite : null;
567
567
  const contextTokens = typeof usage?.contextUsage?.tokens === "number" ? usage.contextUsage.tokens : tokensIn;
568
568
 
569
- // Price calculation
570
- const catalog = options.priceCatalog ?? loadPriceCatalog(options.projectRoot ?? process.cwd());
571
- let costBasis: "metered" | "unknown" = "unknown";
572
- let costUsd: number | null = null;
573
- let priceRef: string | undefined;
574
-
569
+ // Catalog errors must not discard observed usage or crash settlement.
570
+ const providerMetadata: Record<string, string | number | boolean> = {};
571
+ const loaded = loadPriceCatalogForEstimate({
572
+ priceCatalog: options.priceCatalog,
573
+ projectRoot: options.projectRoot,
574
+ });
575
+ if (loaded.unavailable) providerMetadata.priceCatalogUnavailable = true;
576
+ if (loaded.stale) providerMetadata.priceCatalogStale = true;
577
+ const catalog = loaded.catalog;
578
+ const costBasis = "unknown" as const;
579
+ const costUsd = null;
575
580
  if (catalog) {
576
581
  const calculated = calculateModelCost(catalog, {
577
582
  model: resolved.model,
@@ -582,10 +587,10 @@ export function createVnextPiProducer(options: VnextPiProducerOptions = {}): Vne
582
587
  cacheWriteTokens,
583
588
  contextTokens,
584
589
  });
585
- if (calculated) {
586
- costBasis = "metered";
587
- costUsd = calculated.costUsd;
588
- priceRef = calculated.priceRef;
590
+ if (calculated && Number.isFinite(calculated.costUsd)) {
591
+ providerMetadata.listCostUsd = calculated.costUsd;
592
+ providerMetadata.listPriceRef = calculated.priceRef;
593
+ providerMetadata.listPriceSha256 = catalog.sha256;
589
594
  }
590
595
  }
591
596
 
@@ -603,9 +608,9 @@ export function createVnextPiProducer(options: VnextPiProducerOptions = {}): Vne
603
608
  provider: resolved.provider,
604
609
  requestedModel: resolved.model,
605
610
  effectiveModel: resolved.model,
611
+ providerMetadata,
606
612
  ...(resolved.thinking !== undefined ? { thinking: resolved.thinking } : {}),
607
613
  agentRole: request.agentRole ?? request.agentId,
608
- ...(priceRef !== undefined ? { priceRef } : {}),
609
614
  };
610
615
  },
611
616