nexus-agents 8.13.0 → 8.14.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 (30) hide show
  1. package/dist/{chunk-74PMAQD6.js → chunk-357SIWOH.js} +2 -2
  2. package/dist/{chunk-22FTW2WH.js → chunk-DO63GVEY.js} +4 -4
  3. package/dist/{chunk-JEQF3ET2.js → chunk-DVYY5WN6.js} +34 -14
  4. package/dist/chunk-DVYY5WN6.js.map +1 -0
  5. package/dist/chunk-GLIEFVVU.js +7 -0
  6. package/dist/{chunk-5KXR5XZQ.js → chunk-GZTMRQCU.js} +2 -2
  7. package/dist/{chunk-6FGQB3AO.js → chunk-LMYHGHZN.js} +6 -6
  8. package/dist/{chunk-LN7ELNT3.js → chunk-MSOUT5RH.js} +4 -4
  9. package/dist/{chunk-JH2GRV3L.js → chunk-T22TMAIG.js} +2 -2
  10. package/dist/cli.js +53 -23
  11. package/dist/cli.js.map +1 -1
  12. package/dist/{consensus-vote-J662PG7U.js → consensus-vote-TMWNYIHD.js} +5 -5
  13. package/dist/{improvement-review-DRHKKXAE.js → improvement-review-SZTIX2IL.js} +4 -4
  14. package/dist/index.js +9 -9
  15. package/dist/{init-opencode-5WSAP46F.js → init-opencode-3NZVCAQX.js} +3 -3
  16. package/dist/{setup-data-dir-WMB4DSAI.js → setup-data-dir-PFCBRVZ5.js} +5 -5
  17. package/package.json +1 -1
  18. package/dist/chunk-JEQF3ET2.js.map +0 -1
  19. package/dist/chunk-ZUI3SW2Y.js +0 -7
  20. /package/dist/{chunk-74PMAQD6.js.map → chunk-357SIWOH.js.map} +0 -0
  21. /package/dist/{chunk-22FTW2WH.js.map → chunk-DO63GVEY.js.map} +0 -0
  22. /package/dist/{chunk-ZUI3SW2Y.js.map → chunk-GLIEFVVU.js.map} +0 -0
  23. /package/dist/{chunk-5KXR5XZQ.js.map → chunk-GZTMRQCU.js.map} +0 -0
  24. /package/dist/{chunk-6FGQB3AO.js.map → chunk-LMYHGHZN.js.map} +0 -0
  25. /package/dist/{chunk-LN7ELNT3.js.map → chunk-MSOUT5RH.js.map} +0 -0
  26. /package/dist/{chunk-JH2GRV3L.js.map → chunk-T22TMAIG.js.map} +0 -0
  27. /package/dist/{consensus-vote-J662PG7U.js.map → consensus-vote-TMWNYIHD.js.map} +0 -0
  28. /package/dist/{improvement-review-DRHKKXAE.js.map → improvement-review-SZTIX2IL.js.map} +0 -0
  29. /package/dist/{init-opencode-5WSAP46F.js.map → init-opencode-3NZVCAQX.js.map} +0 -0
  30. /package/dist/{setup-data-dir-WMB4DSAI.js.map → setup-data-dir-PFCBRVZ5.js.map} +0 -0
@@ -7,7 +7,7 @@ import {
7
7
  toolStructuredError,
8
8
  toolSuccessStructured,
9
9
  wrapToolWithTimeout
10
- } from "./chunk-5KXR5XZQ.js";
10
+ } from "./chunk-GZTMRQCU.js";
11
11
  import {
12
12
  JsonlStore,
13
13
  SECURITY_KEYWORDS,
@@ -2159,4 +2159,4 @@ export {
2159
2159
  runImprovementReview,
2160
2160
  registerImprovementReviewTool
2161
2161
  };
2162
- //# sourceMappingURL=chunk-74PMAQD6.js.map
2162
+ //# sourceMappingURL=chunk-357SIWOH.js.map
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  readOpenAICompatEnv
3
- } from "./chunk-JH2GRV3L.js";
3
+ } from "./chunk-T22TMAIG.js";
4
4
  import {
5
5
  allOf
6
6
  } from "./chunk-OQB3XC5O.js";
7
7
  import {
8
8
  VERSION
9
- } from "./chunk-ZUI3SW2Y.js";
9
+ } from "./chunk-GLIEFVVU.js";
10
10
  import {
11
11
  capitalize
12
12
  } from "./chunk-6T3EPABN.js";
@@ -1201,7 +1201,7 @@ async function runDoctorFix(result) {
1201
1201
  writeLine2("\u2500".repeat(40));
1202
1202
  let fixCount = 0;
1203
1203
  if (!result.dataDirectory.rootExists || result.dataDirectory.subdirectories.some((d) => !d.exists || !d.writable)) {
1204
- const { initDataDirectories: initDataDirectories2, describeDataDirFix: describeDataDirFix2 } = await import("./setup-data-dir-WMB4DSAI.js");
1204
+ const { initDataDirectories: initDataDirectories2, describeDataDirFix: describeDataDirFix2 } = await import("./setup-data-dir-PFCBRVZ5.js");
1205
1205
  const outcome = describeDataDirFix2(initDataDirectories2());
1206
1206
  writeLine2(outcome.line);
1207
1207
  if (outcome.counted) fixCount++;
@@ -1303,4 +1303,4 @@ export {
1303
1303
  startStdioServer,
1304
1304
  closeServer
1305
1305
  };
1306
- //# sourceMappingURL=chunk-22FTW2WH.js.map
1306
+ //# sourceMappingURL=chunk-DO63GVEY.js.map
@@ -42,12 +42,21 @@ function recordUsageEvent(event) {
42
42
  } catch {
43
43
  }
44
44
  }
45
+ function describeReadError(error) {
46
+ return error instanceof Error ? error.message : String(error);
47
+ }
45
48
  function listUsageFiles(dir) {
46
- if (!existsSync(dir)) return [];
49
+ if (!existsSync(dir)) return { files: [], readErrors: [] };
47
50
  try {
48
- return readdirSync(dir).filter((f) => f.startsWith("usage-") && f.endsWith(".jsonl"));
49
- } catch {
50
- return [];
51
+ const files = readdirSync(dir).filter(
52
+ (file) => file.startsWith("usage-") && file.endsWith(".jsonl")
53
+ );
54
+ return { files, readErrors: [] };
55
+ } catch (error) {
56
+ return {
57
+ files: [],
58
+ readErrors: [`usage ledger unreadable: failed to list ${dir}: ${describeReadError(error)}`]
59
+ };
51
60
  }
52
61
  }
53
62
  var logger = createLogger({ component: "usage-log" });
@@ -92,8 +101,8 @@ function parseFileLines(filePath, filter) {
92
101
  let content;
93
102
  try {
94
103
  content = readFileSync(filePath, "utf-8");
95
- } catch {
96
- return [];
104
+ } catch (error) {
105
+ return { events: [], readError: `${filePath}: ${describeReadError(error)}` };
97
106
  }
98
107
  const out = [];
99
108
  let rejected = 0;
@@ -117,12 +126,14 @@ function parseFileLines(filePath, filter) {
117
126
  if (rejected > 0) {
118
127
  logger.warn("Usage ledger lines rejected as unreadable", { filePath, rejected });
119
128
  }
120
- return out;
129
+ return { events: out };
121
130
  }
122
131
  function loadUsageEvents(opts = {}) {
123
132
  const dir = join(getNexusDataDir(), "usage");
124
- const files = listUsageFiles(dir);
125
- if (files.length === 0) return [];
133
+ const listed = listUsageFiles(dir);
134
+ if (listed.files.length === 0) {
135
+ return { events: [], complete: listed.readErrors.length === 0, readErrors: listed.readErrors };
136
+ }
126
137
  const filter = {
127
138
  sinceMs: opts.sinceIso !== void 0 ? Date.parse(opts.sinceIso) : Number.NEGATIVE_INFINITY,
128
139
  untilMs: opts.untilIso !== void 0 ? Date.parse(opts.untilIso) : Number.POSITIVE_INFINITY,
@@ -130,12 +141,19 @@ function loadUsageEvents(opts = {}) {
130
141
  category: opts.category
131
142
  };
132
143
  const events = [];
133
- for (const f of files) {
134
- events.push(...parseFileLines(join(dir, f), filter));
144
+ const readErrors = [];
145
+ for (const file of listed.files) {
146
+ const parsed = parseFileLines(join(dir, file), filter);
147
+ events.push(...parsed.events);
148
+ if (parsed.readError !== void 0) readErrors.push(parsed.readError);
149
+ }
150
+ if (readErrors.length === listed.files.length) {
151
+ readErrors[0] = `usage ledger unreadable: no files readable; ${readErrors[0] ?? ""}`;
135
152
  }
136
- return events;
153
+ return { events, complete: readErrors.length === 0, readErrors };
137
154
  }
138
155
  function rollupByModel(events) {
156
+ if (events.length === 0) return [];
139
157
  const groups = /* @__PURE__ */ new Map();
140
158
  for (const e of events) {
141
159
  const arr = groups.get(e.modelId);
@@ -149,10 +167,11 @@ function rollupByModel(events) {
149
167
  const totalInputTokens = group.reduce((s, e) => s + e.inputTokens, 0);
150
168
  const totalOutputTokens = group.reduce((s, e) => s + e.outputTokens, 0);
151
169
  const totalUsdCost = group.reduce((s, e) => s + e.usdCost, 0);
170
+ const unpricedCallCount = group.filter((event) => event.priced === false).length;
152
171
  const totalLatency = group.reduce((s, e) => s + e.latencyMs, 0);
153
172
  const successRate = callCount === 0 ? 0 : successCount / callCount;
154
173
  const avgLatencyMs = callCount === 0 ? 0 : totalLatency / callCount;
155
- const costPerSuccessUsd = successCount === 0 ? totalUsdCost : totalUsdCost / successCount;
174
+ const costPerSuccessUsd = successCount === 0 ? null : totalUsdCost / successCount;
156
175
  rollups.push({
157
176
  modelId,
158
177
  providerId: group[0]?.providerId ?? "unknown",
@@ -162,6 +181,7 @@ function rollupByModel(events) {
162
181
  totalInputTokens,
163
182
  totalOutputTokens,
164
183
  totalUsdCost,
184
+ unpricedCallCount,
165
185
  avgLatencyMs,
166
186
  costPerSuccessUsd
167
187
  });
@@ -176,4 +196,4 @@ export {
176
196
  loadUsageEvents,
177
197
  rollupByModel
178
198
  };
179
- //# sourceMappingURL=chunk-JEQF3ET2.js.map
199
+ //# sourceMappingURL=chunk-DVYY5WN6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/learning/usage-log.ts"],"sourcesContent":["/**\n * usage-log — append-only per-call usage events with cost.\n *\n * Source: Issue #2469 (epic #2467 child).\n *\n * For operators running against metered API gateways, per-call cost +\n * tokens + latency is the data they need to manage spend. This module\n * provides three things:\n *\n * 1. `recordUsageEvent(event)` — append a per-call record to a JSONL\n * log under <NEXUS_DATA_DIR>/usage/usage-YYYY-MM.jsonl.\n * 2. `loadUsageEvents({...})` — read events for a window, filtered\n * by model / category.\n * 3. `computeCostDetail(modelId, inputTokens, outputTokens)` (and its\n * thin wrapper `computeCostUSD`) — compute cost from the FULL model\n * registry pricing chain (manifest > in-tree > models.dev > generated\n * LiteLLM catalog), including the #4164 normalized/identity resolution\n * tier for decorated gateway model ids. A model with no pricing\n * anywhere in the chain is reported as `priced: false` so consumers\n * can treat the missing cost as UNMEASURED (#3855), never a real $0.\n *\n * The `usage` CLI command (cli/usage-command.ts) consumes this for the\n * operator dashboard. Existing OutcomeStore is intentionally untouched\n * — its schema is for routing/learning signals, not billing.\n */\n\nimport { z } from 'zod';\nimport { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync } from 'node:fs';\nimport { computeTokenCost, roundToMicroUsd } from './token-cost-core.js';\nimport { dirname, join } from 'node:path';\n\nimport { getNexusDataDir } from '../config/nexus-data-dir.js';\nimport { createLogger } from '../core/logger.js';\n// NOTE: only the FUNCTION is imported here — `getDefaultRegistry()` must never\n// be CALLED at module scope (#3185 bootstrap hazard: first construction reads\n// the manifest overlay/snapshot from disk). All calls happen at invocation\n// time inside computeCostDetail.\nimport { getDefaultRegistry, type MatchedVia } from '../config/model-registry.js';\nimport type { PriceBasis } from '../core/price-basis.js';\n\nexport interface UsageEvent {\n /** ISO 8601 timestamp of the call. */\n readonly timestamp: string;\n /** Model identifier (e.g., 'claude-sonnet-4', 'gpt-4o'). */\n readonly modelId: string;\n /** Provider/adapter (e.g., 'anthropic', 'openai', 'openai-compat'). */\n readonly providerId: string;\n /** Token counts. */\n readonly inputTokens: number;\n readonly outputTokens: number;\n /**\n * Cost in USD. Computed at write time from the full model-registry pricing\n * chain (see `computeCostDetail`). 0 with `priced: false` means the model\n * was UNPRICED (cost unknown), not a real $0 — check `priced` before\n * treating this as a measured figure.\n */\n readonly usdCost: number;\n /** Wall-clock latency in milliseconds. */\n readonly latencyMs: number;\n /** Whether the call succeeded. */\n readonly success: boolean;\n /**\n * Optional task category — populated when the call was made on behalf of\n * a routed task (so aggregation can roll up by category).\n */\n readonly category?: string;\n /** Optional failure code when success === false. */\n readonly errorCode?: string;\n /**\n * Whether pricing data existed for the model at write time (#4165), so\n * audit can distinguish a real $0 from an unpriced model. Absent on lines\n * written before this field existed.\n */\n readonly priced?: boolean;\n /**\n * Pricing provenance: the canonical registry id whose pricing was applied\n * (the entry's `resolvedFrom` when the #4164 fuzzy tier matched a decorated\n * id, else the caller's model id). Present only when `priced` is true.\n */\n readonly priceSource?: string;\n}\n\n/** Result of a pricing-aware cost computation (#4165). */\nexport interface CostDetail {\n /** Cost in USD. Always 0 when `priced` is false — an unknown, not a $0. */\n readonly costUsd: number;\n /** Whether the resolved registry entry carried pricing data. */\n readonly priced: boolean;\n /**\n * Canonical id the pricing/metadata came from: the entry's `resolvedFrom`\n * when the #4164 fuzzy tier matched a decorated id, else the caller's id.\n */\n readonly resolvedId: string;\n /** Fuzzy-resolution provenance (#4164), passed through from the entry. */\n readonly matchedVia?: MatchedVia;\n}\n\n/**\n * Compute cost in USD given a model and token counts, with pricing\n * provenance. Reads the FULL registry chain via\n * `getDefaultRegistry().getEntry()` — manifest > in-tree > models.dev >\n * generated LiteLLM catalog, plus the #4164 normalized/identity tier that\n * resolves decorated model names from OpenAI-compatible gateways to their\n * canonical entry's pricing.\n *\n * `priced: false` (with `costUsd: 0`) means NO pricing existed anywhere in\n * the chain — consumers must treat the cost as UNMEASURED (#3855), never a\n * real $0. Operators can add pricing for gateway-only models via the\n * models-manifest overlay.\n */\nexport function computeCostDetail(\n modelId: string,\n inputTokens: number,\n outputTokens: number\n): CostDetail {\n // Registry read happens at INVOCATION time, never module load — first\n // construction touches the filesystem (#3185 bootstrap hazard).\n const entry = getDefaultRegistry().getEntry(modelId);\n const resolvedId = entry.resolvedFrom ?? modelId;\n const provenance = entry.matchedVia !== undefined ? { matchedVia: entry.matchedVia } : {};\n if (entry.pricing === undefined) {\n return { costUsd: 0, priced: false, resolvedId, ...provenance };\n }\n // The arithmetic lives in the shared core (#5122); this function is the\n // REGISTRY-BACKED, LEDGER-ROUNDED wrapper over it. Rounding to micro-USD\n // stays here rather than in the core: it exists so this log's JSONL does not\n // drift into floating-point noise across many small calls, which is a ledger\n // requirement, not a property of cost.\n const { costUsd } = computeTokenCost(\n { input: inputTokens, output: outputTokens },\n { inputPer1M: entry.pricing.inputPer1M, outputPer1M: entry.pricing.outputPer1M }\n );\n return { costUsd: roundToMicroUsd(costUsd), priced: true, resolvedId, ...provenance };\n}\n\n/**\n * The {@link PriceBasis} the cost in a {@link CostDetail} rests on (#4406).\n *\n * DELIBERATELY derived rather than stored, because `CostDetail.priced` already\n * carries the only distinction the current two-member union can express: the\n * chain resolved a rate, or it did not. A `priceBasis` property beside `priced`\n * would be a second spelling of the same boolean.\n *\n * WHAT THE `'list'` VALUE ASSERTS. Only that a rate was resolved, and that it\n * should be read as an assumed published rate. It is NOT a guarantee that the\n * number is a vendor's advertised public rate — at least two paths put\n * something else behind the label, and one puts a real published rate behind\n * `'unknown'`:\n *\n * - The operator manifest overlay is the highest-precedence tier and passes\n * `pricing` through; overriding pricing is its purpose. A negotiated rate\n * entered there reports `'list'`, and the caveat then warns the reader their\n * contract may differ over the contract rate.\n * - The normalized/fuzzy identity tier lets a decorated gateway id inherit a\n * DIFFERENT canonical entry's rate.\n * - `config/models-generated-loader.ts` discards a published $0/$0 rate unless\n * the id ends `:free`, so `'unknown'` does not mean no price exists.\n *\n * The derivation is therefore an assumption about the chain, not a fact about\n * it — conservative in the right direction (it invites a caveat rather than\n * suppressing one), and honest only if described this way. Narrowing it needs a\n * per-entry label the registry does not carry today; see {@link PriceBasis}.\n *\n * `priceSource`, the other provenance field, answers a different question: WHICH\n * registry entry supplied the number, not what KIND of rate it is. It does not\n * encode the basis, which is why the basis has to travel separately into records\n * (the decision-cost store) that persist neither field.\n */\nexport function priceBasisOf(detail: CostDetail): PriceBasis {\n return detail.priced ? 'list' : 'unknown';\n}\n\n/**\n * Thin wrapper over {@link computeCostDetail} returning only the USD figure.\n * Returns 0 both for a genuinely free model and for a model with no pricing\n * data — callers that must tell those apart (unpriced ⇒ UNMEASURED, #3855)\n * should use `computeCostDetail` and check `priced`.\n */\nexport function computeCostUSD(modelId: string, inputTokens: number, outputTokens: number): number {\n return computeCostDetail(modelId, inputTokens, outputTokens).costUsd;\n}\n\n/** Resolve the active usage log path for the current month. */\nexport function getUsageLogPath(date: Date = new Date()): string {\n const year = date.getUTCFullYear();\n const month = String(date.getUTCMonth() + 1).padStart(2, '0');\n return join(getNexusDataDir(), 'usage', `usage-${String(year)}-${month}.jsonl`);\n}\n\n/**\n * Append a usage event to the current month's log. Best-effort — failures\n * are silent (we don't want to fail a successful model call because we\n * couldn't write a log line).\n */\nexport function recordUsageEvent(event: UsageEvent): void {\n try {\n const path = getUsageLogPath(new Date(event.timestamp));\n mkdirSync(dirname(path), { recursive: true });\n appendFileSync(path, `${JSON.stringify(event)}\\n`, 'utf-8');\n } catch {\n // Intentionally silent — telemetry must not break user calls.\n }\n}\n\nexport interface LoadUsageOptions {\n /** Restrict to events at or after this ISO timestamp. */\n readonly sinceIso?: string;\n /** Restrict to events before this ISO timestamp. */\n readonly untilIso?: string;\n /** Only events for this model. */\n readonly modelId?: string;\n /** Only events for this category. */\n readonly category?: string;\n}\n\ninterface UsageFileList {\n readonly files: readonly string[];\n readonly readErrors: readonly string[];\n}\n\nfunction describeReadError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction listUsageFiles(dir: string): UsageFileList {\n if (!existsSync(dir)) return { files: [], readErrors: [] };\n try {\n const files = readdirSync(dir).filter(\n (file) => file.startsWith('usage-') && file.endsWith('.jsonl')\n );\n return { files, readErrors: [] };\n } catch (error) {\n return {\n files: [],\n readErrors: [`usage ledger unreadable: failed to list ${dir}: ${describeReadError(error)}`],\n };\n }\n}\n\ninterface LoadFilter {\n readonly sinceMs: number;\n readonly untilMs: number;\n readonly modelId: string | undefined;\n readonly category: string | undefined;\n}\n\nconst logger = createLogger({ component: 'usage-log' });\n\n/**\n * Validate a ledger line before it is trusted (#5328).\n *\n * The read path did `JSON.parse(line) as UsageEvent` — a cast. `eventMatches`\n * only inspects `timestamp` / `modelId` / `category`, so a corrupt `usdCost`\n * reached `rollupByModel`'s `reduce((s, e) => s + e.usdCost, 0)`: a string\n * concatenates, a missing value yields `NaN`, and neither throws. Every sibling\n * JSONL reader here validates; this one did not, and it is the cost ledger.\n *\n * Optional fields stay optional ON PURPOSE. `category`, `errorCode`, `priced`\n * and `priceSource` all postdate the original format — `priced` is documented\n * as \"Absent on lines written before this field existed\" — so requiring them\n * would silently discard real spend history, which is worse than the corruption\n * being fixed.\n *\n * Not `.strict()`, for the same forward-compatibility reason: a line carrying a\n * field a newer version writes must still load in an older one.\n *\n * `z.number()` already rejects `NaN` and infinities in zod v4, which is the\n * property that matters here: a cost that is not a real number cannot be summed,\n * and JSON renders `NaN` as `null`, which this also rejects.\n */\nconst UsageEventSchema = z.object({\n timestamp: z.string(),\n modelId: z.string(),\n providerId: z.string(),\n inputTokens: z.number(),\n outputTokens: z.number(),\n usdCost: z.number(),\n latencyMs: z.number(),\n success: z.boolean(),\n category: z.string().optional(),\n errorCode: z.string().optional(),\n priced: z.boolean().optional(),\n priceSource: z.string().optional(),\n});\n\n/**\n * Narrow a validated line to {@link UsageEvent}.\n *\n * Zod's `.optional()` yields `T | undefined`, which `exactOptionalPropertyTypes`\n * will not assign to a `?: T` property — an absent key and a present-but-\n * undefined key are different things under that flag, and the distinction is\n * the right one for a ledger: a line that omits `priced` is legacy, whereas one\n * that sets it to `undefined` is malformed.\n */\nfunction toUsageEvent(v: z.infer<typeof UsageEventSchema>): UsageEvent {\n return {\n timestamp: v.timestamp,\n modelId: v.modelId,\n providerId: v.providerId,\n inputTokens: v.inputTokens,\n outputTokens: v.outputTokens,\n usdCost: v.usdCost,\n latencyMs: v.latencyMs,\n success: v.success,\n ...(v.category !== undefined && { category: v.category }),\n ...(v.errorCode !== undefined && { errorCode: v.errorCode }),\n ...(v.priced !== undefined && { priced: v.priced }),\n ...(v.priceSource !== undefined && { priceSource: v.priceSource }),\n };\n}\n\nfunction eventMatches(parsed: UsageEvent, f: LoadFilter): boolean {\n const ts = Date.parse(parsed.timestamp);\n if (ts < f.sinceMs || ts >= f.untilMs) return false;\n if (f.modelId !== undefined && parsed.modelId !== f.modelId) return false;\n if (f.category !== undefined && parsed.category !== f.category) return false;\n return true;\n}\n\ninterface ParsedUsageFile {\n readonly events: readonly UsageEvent[];\n readonly readError?: string;\n}\n\nfunction parseFileLines(filePath: string, filter: LoadFilter): ParsedUsageFile {\n let content: string;\n try {\n content = readFileSync(filePath, 'utf-8');\n } catch (error) {\n return { events: [], readError: `${filePath}: ${describeReadError(error)}` };\n }\n const out: UsageEvent[] = [];\n let rejected = 0;\n for (const line of content.split('\\n')) {\n if (line.trim() === '') continue;\n let raw: unknown;\n try {\n raw = JSON.parse(line);\n } catch {\n // Skip malformed line; keep reading.\n rejected++;\n continue;\n }\n const parsed = UsageEventSchema.safeParse(raw);\n if (!parsed.success) {\n rejected++;\n continue;\n }\n const event = toUsageEvent(parsed.data);\n if (eventMatches(event, filter)) out.push(event);\n }\n // Say so rather than quietly returning a shorter list: a ledger that drops\n // lines silently under-reports spend, and the operator has no way to tell.\n if (rejected > 0) {\n logger.warn('Usage ledger lines rejected as unreadable', { filePath, rejected });\n }\n return { events: out };\n}\n\ninterface UsageLedgerLoadResult {\n /** Valid events recovered from every readable ledger file. */\n readonly events: readonly UsageEvent[];\n /** True only when the ledger directory and every discovered file were readable. */\n readonly complete: boolean;\n /** Human-readable directory/file failures that made the result incomplete. */\n readonly readErrors: readonly string[];\n}\n\n/**\n * Load all usage events from disk that match the filter, preserving whether\n * every ledger file was readable. A missing usage directory is a complete,\n * empty ledger. For sub-second filtering at scale a future PR can index by\n * month, but linear scan is fine at the \"operator dashboard\" scale.\n */\nexport function loadUsageEvents(opts: LoadUsageOptions = {}): UsageLedgerLoadResult {\n const dir = join(getNexusDataDir(), 'usage');\n const listed = listUsageFiles(dir);\n if (listed.files.length === 0) {\n return { events: [], complete: listed.readErrors.length === 0, readErrors: listed.readErrors };\n }\n const filter: LoadFilter = {\n sinceMs: opts.sinceIso !== undefined ? Date.parse(opts.sinceIso) : Number.NEGATIVE_INFINITY,\n untilMs: opts.untilIso !== undefined ? Date.parse(opts.untilIso) : Number.POSITIVE_INFINITY,\n modelId: opts.modelId,\n category: opts.category,\n };\n const events: UsageEvent[] = [];\n const readErrors: string[] = [];\n for (const file of listed.files) {\n const parsed = parseFileLines(join(dir, file), filter);\n events.push(...parsed.events);\n if (parsed.readError !== undefined) readErrors.push(parsed.readError);\n }\n if (readErrors.length === listed.files.length) {\n readErrors[0] = `usage ledger unreadable: no files readable; ${readErrors[0] ?? ''}`;\n }\n return { events, complete: readErrors.length === 0, readErrors };\n}\n\nexport interface ModelRollup {\n readonly modelId: string;\n readonly providerId: string;\n readonly callCount: number;\n readonly successCount: number;\n readonly successRate: number;\n readonly totalInputTokens: number;\n readonly totalOutputTokens: number;\n readonly totalUsdCost: number;\n /** Calls whose cost is unknown; legacy events without `priced` do not count. */\n readonly unpricedCallCount: number;\n readonly avgLatencyMs: number;\n /** Null when there were no successful calls. */\n readonly costPerSuccessUsd: number | null;\n}\n\n/**\n * Aggregate events into per-model rollups. Sorted by total cost descending\n * — the model burning the most money at top. Useful for \"where is my spend\n * going?\" investigations.\n */\nexport function rollupByModel(events: readonly UsageEvent[]): readonly ModelRollup[] {\n if (events.length === 0) return [];\n const groups = new Map<string, UsageEvent[]>();\n for (const e of events) {\n const arr = groups.get(e.modelId);\n if (arr === undefined) groups.set(e.modelId, [e]);\n else arr.push(e);\n }\n const rollups: ModelRollup[] = [];\n for (const [modelId, group] of groups) {\n const callCount = group.length;\n const successCount = group.filter((e) => e.success).length;\n const totalInputTokens = group.reduce((s, e) => s + e.inputTokens, 0);\n const totalOutputTokens = group.reduce((s, e) => s + e.outputTokens, 0);\n const totalUsdCost = group.reduce((s, e) => s + e.usdCost, 0);\n // Legacy ledger lines predate `priced`; only an explicit false is unpriced.\n const unpricedCallCount = group.filter((event) => event.priced === false).length;\n const totalLatency = group.reduce((s, e) => s + e.latencyMs, 0);\n const successRate = callCount === 0 ? 0 : successCount / callCount;\n const avgLatencyMs = callCount === 0 ? 0 : totalLatency / callCount;\n const costPerSuccessUsd = successCount === 0 ? null : totalUsdCost / successCount;\n rollups.push({\n modelId,\n providerId: group[0]?.providerId ?? 'unknown',\n callCount,\n successCount,\n successRate,\n totalInputTokens,\n totalOutputTokens,\n totalUsdCost,\n unpricedCallCount,\n avgLatencyMs,\n costPerSuccessUsd,\n });\n }\n return rollups.sort((a, b) => b.totalUsdCost - a.totalUsdCost);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,7 @@
1
+ // src/version.ts
2
+ var VERSION = true ? "8.14.0" : "dev";
3
+
4
+ export {
5
+ VERSION
6
+ };
7
+ //# sourceMappingURL=chunk-GLIEFVVU.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-ZUI3SW2Y.js";
3
+ } from "./chunk-GLIEFVVU.js";
4
4
  import {
5
5
  emitClawGuardViolation
6
6
  } from "./chunk-KHJACO7N.js";
@@ -2246,4 +2246,4 @@ export {
2246
2246
  VOTING_THRESHOLDS,
2247
2247
  ConsensusMetricsSchema
2248
2248
  };
2249
- //# sourceMappingURL=chunk-5KXR5XZQ.js.map
2249
+ //# sourceMappingURL=chunk-GZTMRQCU.js.map
@@ -21,7 +21,7 @@ import {
21
21
  import {
22
22
  DEFAULT_TASK_TTL_MS,
23
23
  clampTaskTtl
24
- } from "./chunk-22FTW2WH.js";
24
+ } from "./chunk-DO63GVEY.js";
25
25
  import {
26
26
  executeExpert
27
27
  } from "./chunk-ZVBGJJHJ.js";
@@ -48,7 +48,7 @@ import {
48
48
  simulationDeniedResult,
49
49
  toJobSummary,
50
50
  writeJobCancelled
51
- } from "./chunk-LN7ELNT3.js";
51
+ } from "./chunk-MSOUT5RH.js";
52
52
  import {
53
53
  allOf
54
54
  } from "./chunk-OQB3XC5O.js";
@@ -67,7 +67,7 @@ import {
67
67
  } from "./chunk-EIEC22PA.js";
68
68
  import {
69
69
  withPrerequisite
70
- } from "./chunk-74PMAQD6.js";
70
+ } from "./chunk-357SIWOH.js";
71
71
  import {
72
72
  NOOP_NOTIFIER,
73
73
  RateLimiter,
@@ -92,7 +92,7 @@ import {
92
92
  withAuditTrail,
93
93
  withProgressHeartbeat,
94
94
  wrapToolWithTimeout
95
- } from "./chunk-5KXR5XZQ.js";
95
+ } from "./chunk-GZTMRQCU.js";
96
96
  import {
97
97
  createAuditTrail,
98
98
  createGraphAuditBridge,
@@ -47395,7 +47395,7 @@ ${contextBlock}`;
47395
47395
  const strategy = config.votingStrategy ?? "higher_order";
47396
47396
  await postProgress(config, "Vote", `Running consensus with ${strategy} strategy...`);
47397
47397
  try {
47398
- const { executeVoting } = await import("./consensus-vote-J662PG7U.js");
47398
+ const { executeVoting } = await import("./consensus-vote-TMWNYIHD.js");
47399
47399
  const votingResult = await executeVoting(
47400
47400
  {
47401
47401
  proposal: buildVoteProposal(plan, research),
@@ -56059,4 +56059,4 @@ export {
56059
56059
  shutdownFeedbackSubscriber,
56060
56060
  createEventBusBridge
56061
56061
  };
56062
- //# sourceMappingURL=chunk-6FGQB3AO.js.map
56062
+ //# sourceMappingURL=chunk-LMYHGHZN.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  computeCostDetail,
3
3
  priceBasisOf
4
- } from "./chunk-JEQF3ET2.js";
4
+ } from "./chunk-DVYY5WN6.js";
5
5
  import {
6
6
  allOf
7
7
  } from "./chunk-OQB3XC5O.js";
@@ -23,10 +23,10 @@ import {
23
23
  toolSuccessStructured,
24
24
  withProgressHeartbeat,
25
25
  wrapToolWithTimeout
26
- } from "./chunk-5KXR5XZQ.js";
26
+ } from "./chunk-GZTMRQCU.js";
27
27
  import {
28
28
  VERSION
29
- } from "./chunk-ZUI3SW2Y.js";
29
+ } from "./chunk-GLIEFVVU.js";
30
30
  import {
31
31
  assessPanelIndependence,
32
32
  countDistinctModels,
@@ -6972,4 +6972,4 @@ export {
6972
6972
  CONSENSUS_VOTE_TOOL_SCHEMA,
6973
6973
  registerConsensusVoteTool
6974
6974
  };
6975
- //# sourceMappingURL=chunk-LN7ELNT3.js.map
6975
+ //# sourceMappingURL=chunk-MSOUT5RH.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  computeCostDetail,
3
3
  recordUsageEvent
4
- } from "./chunk-JEQF3ET2.js";
4
+ } from "./chunk-DVYY5WN6.js";
5
5
  import {
6
6
  BaseAdapter,
7
7
  createStream,
@@ -833,4 +833,4 @@ export {
833
833
  discoverModels,
834
834
  buildOpenAICompatAdapters
835
835
  };
836
- //# sourceMappingURL=chunk-JH2GRV3L.js.map
836
+ //# sourceMappingURL=chunk-T22TMAIG.js.map
package/dist/cli.js CHANGED
@@ -154,7 +154,7 @@ import {
154
154
  validateNexusEnv,
155
155
  validateWorkflow,
156
156
  wrapInMarkdownFence
157
- } from "./chunk-6FGQB3AO.js";
157
+ } from "./chunk-LMYHGHZN.js";
158
158
  import "./chunk-FGEGUND2.js";
159
159
  import "./chunk-DGWT7DXX.js";
160
160
  import "./chunk-64GMIUM5.js";
@@ -174,11 +174,11 @@ import {
174
174
  doctorCommand,
175
175
  initDataDirectories,
176
176
  runDoctor
177
- } from "./chunk-22FTW2WH.js";
177
+ } from "./chunk-DO63GVEY.js";
178
178
  import {
179
179
  buildOpenAICompatAdapters,
180
180
  readOpenAICompatEnv
181
- } from "./chunk-JH2GRV3L.js";
181
+ } from "./chunk-T22TMAIG.js";
182
182
  import {
183
183
  shutdownExpertBridge
184
184
  } from "./chunk-ZVBGJJHJ.js";
@@ -193,11 +193,11 @@ import {
193
193
  recordAuthenticVote,
194
194
  registerConsensusVoteTool,
195
195
  toRecordDecision
196
- } from "./chunk-LN7ELNT3.js";
196
+ } from "./chunk-MSOUT5RH.js";
197
197
  import {
198
198
  loadUsageEvents,
199
199
  rollupByModel
200
- } from "./chunk-JEQF3ET2.js";
200
+ } from "./chunk-DVYY5WN6.js";
201
201
  import {
202
202
  allOf,
203
203
  anyOf
@@ -230,15 +230,15 @@ import {
230
230
  registerImprovementReviewTool,
231
231
  runImprovementReview,
232
232
  scanForSecrets
233
- } from "./chunk-74PMAQD6.js";
233
+ } from "./chunk-357SIWOH.js";
234
234
  import {
235
235
  createDefaultPolicyFirewall,
236
236
  setGlobalPolicyFirewall,
237
237
  stagePolicyFirewallForRollout
238
- } from "./chunk-5KXR5XZQ.js";
238
+ } from "./chunk-GZTMRQCU.js";
239
239
  import {
240
240
  VERSION
241
- } from "./chunk-ZUI3SW2Y.js";
241
+ } from "./chunk-GLIEFVVU.js";
242
242
  import "./chunk-KHJACO7N.js";
243
243
  import {
244
244
  createUnifiedRegistry,
@@ -19685,7 +19685,7 @@ function startImprovementReviewScheduler(options) {
19685
19685
  let running = false;
19686
19686
  const runOnce = async () => {
19687
19687
  try {
19688
- const { runImprovementReview: runImprovementReview2, ImprovementReviewInputSchema: ImprovementReviewInputSchema2 } = await import("./improvement-review-DRHKKXAE.js");
19688
+ const { runImprovementReview: runImprovementReview2, ImprovementReviewInputSchema: ImprovementReviewInputSchema2 } = await import("./improvement-review-SZTIX2IL.js");
19689
19689
  const input = ImprovementReviewInputSchema2.parse({ fileIssues });
19690
19690
  const result = await runImprovementReview2(input, { logger: logger22 });
19691
19691
  logger22.info("Scheduled improvement_review complete", {
@@ -23099,7 +23099,7 @@ async function runInitPortableFlow(args) {
23099
23099
  return cliExit(result.success ? EXIT_CODES.SUCCESS : EXIT_CODES.SERVER_START_FAILED);
23100
23100
  }
23101
23101
  async function runInitOpencodeFlow(args) {
23102
- const { runInitOpencode } = await import("./init-opencode-5WSAP46F.js");
23102
+ const { runInitOpencode } = await import("./init-opencode-3NZVCAQX.js");
23103
23103
  const opencodePath = args.options.opencode;
23104
23104
  if (opencodePath === void 0 || opencodePath === "") {
23105
23105
  process.stderr.write("Error: --opencode requires a path argument.\n");
@@ -23125,7 +23125,7 @@ async function runInitOpencodeFlow(args) {
23125
23125
  return cliExit(EXIT_CODES.SUCCESS);
23126
23126
  }
23127
23127
  async function renderOpencodeValidate(opencodePath) {
23128
- const { runOpencodeValidate } = await import("./init-opencode-5WSAP46F.js");
23128
+ const { runOpencodeValidate } = await import("./init-opencode-3NZVCAQX.js");
23129
23129
  const result = await runOpencodeValidate(opencodePath);
23130
23130
  if (!result.ok) {
23131
23131
  process.stderr.write(`init --opencode --validate: ${result.reason ?? "failed"}
@@ -25398,19 +25398,32 @@ async function handleUsageCommand(args) {
25398
25398
  if (opts.modelId !== void 0) {
25399
25399
  loadOpts.modelId = opts.modelId;
25400
25400
  }
25401
- const events = loadUsageEvents(loadOpts);
25402
- const rollups = rollupByModel(events);
25401
+ const ledger = loadUsageEvents(loadOpts);
25402
+ const rollups = rollupByModel(ledger.events);
25403
+ const exitCode = ledger.complete ? EXIT_CODES.SUCCESS : EXIT_CODES.SERVER_START_FAILED;
25403
25404
  if (opts.format === "json") {
25404
- process.stdout.write(`${JSON.stringify({ since: opts.sinceIso, rollups }, null, 2)}
25405
- `);
25405
+ process.stdout.write(
25406
+ `${JSON.stringify(
25407
+ {
25408
+ since: opts.sinceIso,
25409
+ complete: ledger.complete,
25410
+ readErrors: ledger.readErrors,
25411
+ rollups
25412
+ },
25413
+ null,
25414
+ 2
25415
+ )}
25416
+ `
25417
+ );
25406
25418
  await Promise.resolve();
25407
- return cliExit(EXIT_CODES.SUCCESS);
25419
+ return cliExit(exitCode);
25408
25420
  }
25409
- printTextReport(opts, rollups, events.length);
25421
+ printTextReport(opts, rollups, ledger.events.length, ledger.complete);
25422
+ printReadStatus(ledger.readErrors);
25410
25423
  await Promise.resolve();
25411
- return cliExit(EXIT_CODES.SUCCESS);
25424
+ return cliExit(exitCode);
25412
25425
  }
25413
- function printTextReport(opts, rollups, totalEvents) {
25426
+ function printTextReport(opts, rollups, totalEvents, complete) {
25414
25427
  console.log("Nexus Agents \u2014 Usage Report");
25415
25428
  console.log("===========================");
25416
25429
  console.log(`Window: ${opts.sinceIso} \u2192 ${opts.untilIso ?? "now"}`);
@@ -25420,6 +25433,7 @@ function printTextReport(opts, rollups, totalEvents) {
25420
25433
  console.log(`Events: ${String(totalEvents)}
25421
25434
  `);
25422
25435
  if (rollups.length === 0) {
25436
+ if (!complete) return;
25423
25437
  console.log("No usage events recorded for this window.");
25424
25438
  console.log("");
25425
25439
  console.log("To start recording, calls must reach a recordUsageEvent()-instrumented");
@@ -25434,14 +25448,30 @@ function printTextReport(opts, rollups, totalEvents) {
25434
25448
  console.log(
25435
25449
  ` tokens : ${String(r.totalInputTokens)} in / ${String(r.totalOutputTokens)} out`
25436
25450
  );
25437
- console.log(
25438
- ` cost : $${r.totalUsdCost.toFixed(4)} ($${r.costPerSuccessUsd.toFixed(4)} / success)`
25439
- );
25451
+ const costPerSuccess = r.costPerSuccessUsd === null ? "N/A (no successes)" : `${r.unpricedCallCount === 0 ? "" : "\u2265 "}$${r.costPerSuccessUsd.toFixed(4)} / success`;
25452
+ console.log(` cost : ${formatCost2(r.totalUsdCost, r.unpricedCallCount)}`);
25453
+ console.log(` cost / success : ${costPerSuccess}`);
25440
25454
  console.log(` avg latency : ${r.avgLatencyMs.toFixed(0)}ms`);
25441
25455
  console.log("");
25442
25456
  }
25443
25457
  const totalCost = rollups.reduce((s, r) => s + r.totalUsdCost, 0);
25444
- console.log(`Total cost: $${totalCost.toFixed(4)} across ${String(rollups.length)} model(s).`);
25458
+ const totalUnpriced = rollups.reduce((sum, rollup) => sum + rollup.unpricedCallCount, 0);
25459
+ console.log(
25460
+ `Total cost: ${formatCost2(totalCost, totalUnpriced)} across ${String(rollups.length)} model(s).`
25461
+ );
25462
+ }
25463
+ function formatCost2(costUsd, unpricedCallCount) {
25464
+ const measured = `$${costUsd.toFixed(4)}`;
25465
+ return unpricedCallCount === 0 ? measured : `\u2265 ${measured} (${String(unpricedCallCount)} unpriced)`;
25466
+ }
25467
+ function printReadStatus(readErrors) {
25468
+ if (readErrors.length === 0) return;
25469
+ if (readErrors[0]?.startsWith("usage ledger unreadable:") === true) {
25470
+ console.error(readErrors.join("; "));
25471
+ return;
25472
+ }
25473
+ console.error(`Usage ledger partial: ${String(readErrors.length)} file(s) unreadable.`);
25474
+ for (const error of readErrors) console.error(` ${error}`);
25445
25475
  }
25446
25476
 
25447
25477
  // src/cli/improvement-review-command.ts