agent-nuvira 3.5.2 → 3.5.6

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 (155) hide show
  1. package/README.md +66 -0
  2. package/dist/cli/chat.d.ts.map +1 -1
  3. package/dist/cli/chat.js +72 -19
  4. package/dist/cli/chat.js.map +1 -1
  5. package/dist/cli/config.d.ts.map +1 -1
  6. package/dist/cli/config.js +17 -3
  7. package/dist/cli/config.js.map +1 -1
  8. package/dist/cli/execute.d.ts.map +1 -1
  9. package/dist/cli/execute.js +63 -22
  10. package/dist/cli/execute.js.map +1 -1
  11. package/dist/cli/failover-runner.js +4 -4
  12. package/dist/cli/failover-runner.js.map +1 -1
  13. package/dist/cli/gateway.js +13 -13
  14. package/dist/cli/gateway.js.map +1 -1
  15. package/dist/cli/loop-executor.d.ts +22 -0
  16. package/dist/cli/loop-executor.d.ts.map +1 -1
  17. package/dist/cli/loop-executor.js +56 -12
  18. package/dist/cli/loop-executor.js.map +1 -1
  19. package/dist/cli/stats.d.ts +62 -4
  20. package/dist/cli/stats.d.ts.map +1 -1
  21. package/dist/cli/stats.js +282 -5
  22. package/dist/cli/stats.js.map +1 -1
  23. package/dist/cli/ui/index.d.ts +2 -2
  24. package/dist/cli/ui/index.d.ts.map +1 -1
  25. package/dist/cli/ui/index.js +1 -1
  26. package/dist/cli/ui/index.js.map +1 -1
  27. package/dist/cli/ui/presenter.d.ts +43 -0
  28. package/dist/cli/ui/presenter.d.ts.map +1 -1
  29. package/dist/cli/ui/presenter.js +88 -1
  30. package/dist/cli/ui/presenter.js.map +1 -1
  31. package/dist/config/manager.d.ts +29 -1
  32. package/dist/config/manager.d.ts.map +1 -1
  33. package/dist/config/manager.js +178 -143
  34. package/dist/config/manager.js.map +1 -1
  35. package/dist/config/paths.d.ts +48 -1
  36. package/dist/config/paths.d.ts.map +1 -1
  37. package/dist/config/paths.js +59 -9
  38. package/dist/config/paths.js.map +1 -1
  39. package/dist/config/service-catalog.js +1 -1
  40. package/dist/config/service-catalog.js.map +1 -1
  41. package/dist/config/service-probe.js +1 -1
  42. package/dist/config/service-probe.js.map +1 -1
  43. package/dist/config/types.d.ts +22 -0
  44. package/dist/config/types.d.ts.map +1 -1
  45. package/dist/gateway/adapters.js +8 -8
  46. package/dist/gateway/adapters.js.map +1 -1
  47. package/dist/gateway/registry.js +1 -1
  48. package/dist/gateway/registry.js.map +1 -1
  49. package/dist/learning/auto-router.d.ts +30 -4
  50. package/dist/learning/auto-router.d.ts.map +1 -1
  51. package/dist/learning/auto-router.js +54 -38
  52. package/dist/learning/auto-router.js.map +1 -1
  53. package/dist/learning/cost-classification.d.ts +188 -0
  54. package/dist/learning/cost-classification.d.ts.map +1 -0
  55. package/dist/learning/cost-classification.js +294 -0
  56. package/dist/learning/cost-classification.js.map +1 -0
  57. package/dist/learning/cost-ledger-maintenance.d.ts +122 -0
  58. package/dist/learning/cost-ledger-maintenance.d.ts.map +1 -0
  59. package/dist/learning/cost-ledger-maintenance.js +289 -0
  60. package/dist/learning/cost-ledger-maintenance.js.map +1 -0
  61. package/dist/learning/cost-reconcile.d.ts +160 -0
  62. package/dist/learning/cost-reconcile.d.ts.map +1 -0
  63. package/dist/learning/cost-reconcile.js +238 -0
  64. package/dist/learning/cost-reconcile.js.map +1 -0
  65. package/dist/learning/cost-tracker.d.ts +134 -13
  66. package/dist/learning/cost-tracker.d.ts.map +1 -1
  67. package/dist/learning/cost-tracker.js +190 -68
  68. package/dist/learning/cost-tracker.js.map +1 -1
  69. package/dist/learning/model-pricing.d.ts +240 -0
  70. package/dist/learning/model-pricing.d.ts.map +1 -0
  71. package/dist/learning/model-pricing.js +309 -0
  72. package/dist/learning/model-pricing.js.map +1 -0
  73. package/dist/learning/model-selection.d.ts +15 -0
  74. package/dist/learning/model-selection.d.ts.map +1 -1
  75. package/dist/learning/model-selection.js +22 -2
  76. package/dist/learning/model-selection.js.map +1 -1
  77. package/dist/learning/model-warmup.d.ts.map +1 -1
  78. package/dist/learning/model-warmup.js +4 -1
  79. package/dist/learning/model-warmup.js.map +1 -1
  80. package/dist/learning/session-store.d.ts +1 -5
  81. package/dist/learning/session-store.d.ts.map +1 -1
  82. package/dist/learning/session-store.js +10 -1
  83. package/dist/learning/session-store.js.map +1 -1
  84. package/dist/learning/step-checkpoint.d.ts +32 -3
  85. package/dist/learning/step-checkpoint.d.ts.map +1 -1
  86. package/dist/learning/step-checkpoint.js +33 -4
  87. package/dist/learning/step-checkpoint.js.map +1 -1
  88. package/dist/mcp/settings.d.ts +15 -3
  89. package/dist/mcp/settings.d.ts.map +1 -1
  90. package/dist/mcp/settings.js +30 -15
  91. package/dist/mcp/settings.js.map +1 -1
  92. package/dist/parity/drivers.d.ts.map +1 -1
  93. package/dist/parity/drivers.js +31 -5
  94. package/dist/parity/drivers.js.map +1 -1
  95. package/dist/tools/child-agent-runtime.d.ts.map +1 -1
  96. package/dist/tools/child-agent-runtime.js +13 -7
  97. package/dist/tools/child-agent-runtime.js.map +1 -1
  98. package/dist/tools/followup-utils.d.ts +36 -0
  99. package/dist/tools/followup-utils.d.ts.map +1 -1
  100. package/dist/tools/followup-utils.js +66 -0
  101. package/dist/tools/followup-utils.js.map +1 -1
  102. package/dist/tools/modality/browser.js +2 -2
  103. package/dist/tools/modality/browser.js.map +1 -1
  104. package/dist/tools/modality/image-gen.d.ts +1 -1
  105. package/dist/tools/modality/image-gen.js +1 -1
  106. package/dist/tools/modality/image-providers.js +1 -1
  107. package/dist/tools/modality/image-providers.js.map +1 -1
  108. package/dist/tools/modality/vision.d.ts +1 -1
  109. package/dist/tools/modality/vision.js +1 -1
  110. package/dist/tools/modality/voice.js +2 -2
  111. package/dist/tools/modality/voice.js.map +1 -1
  112. package/dist/tools/neutts-synth.d.ts +3 -3
  113. package/dist/tools/neutts-synth.js +3 -3
  114. package/dist/tools/neutts-synth.js.map +1 -1
  115. package/dist/tools/process-liveness.d.ts +50 -0
  116. package/dist/tools/process-liveness.d.ts.map +1 -1
  117. package/dist/tools/process-liveness.js +82 -9
  118. package/dist/tools/process-liveness.js.map +1 -1
  119. package/dist/tools/registry.d.ts +1 -1
  120. package/dist/tools/registry.d.ts.map +1 -1
  121. package/dist/tools/registry.js +27 -4
  122. package/dist/tools/registry.js.map +1 -1
  123. package/dist/tools/run-registry.d.ts.map +1 -1
  124. package/dist/tools/run-registry.js +14 -1
  125. package/dist/tools/run-registry.js.map +1 -1
  126. package/dist/tools/run-terminal.d.ts.map +1 -1
  127. package/dist/tools/run-terminal.js +18 -1
  128. package/dist/tools/run-terminal.js.map +1 -1
  129. package/dist/tools/tool-loop.d.ts.map +1 -1
  130. package/dist/tools/tool-loop.js +5 -3
  131. package/dist/tools/tool-loop.js.map +1 -1
  132. package/dist/tools/web-research.d.ts +1 -1
  133. package/dist/tools/web-research.js +3 -3
  134. package/dist/tools/web-research.js.map +1 -1
  135. package/dist/web-dashboard/chat-console.d.ts +56 -0
  136. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  137. package/dist/web-dashboard/chat-console.js +57 -1
  138. package/dist/web-dashboard/chat-console.js.map +1 -1
  139. package/dist/web-dashboard/server.d.ts.map +1 -1
  140. package/dist/web-dashboard/server.js +122 -2
  141. package/dist/web-dashboard/server.js.map +1 -1
  142. package/dist/web-dashboard/src/types.d.ts +63 -0
  143. package/dist/web-dashboard/src/types.d.ts.map +1 -1
  144. package/dist/web-dashboard/task-runner.d.ts +17 -1
  145. package/dist/web-dashboard/task-runner.d.ts.map +1 -1
  146. package/dist/web-dashboard/task-runner.js +84 -9
  147. package/dist/web-dashboard/task-runner.js.map +1 -1
  148. package/package.json +1 -1
  149. package/src/web-dashboard/public/assets/index-DeSsNU94.css +1 -0
  150. package/src/web-dashboard/public/assets/index-Do_O67Kv.js +236 -0
  151. package/src/web-dashboard/public/assets/index-Do_O67Kv.js.map +1 -0
  152. package/src/web-dashboard/public/index.html +2 -2
  153. package/src/web-dashboard/public/assets/index-DycGBWwy.js +0 -236
  154. package/src/web-dashboard/public/assets/index-DycGBWwy.js.map +0 -1
  155. package/src/web-dashboard/public/assets/index-aO3aG7ex.css +0 -1
@@ -0,0 +1,289 @@
1
+ /**
2
+ * Cost-ledger maintenance — the EXPLICIT, dry-run-default way to repair the rows
3
+ * already on disk. Nothing here runs on a read: the ledger is repaired only when
4
+ * an operator asks for it, and every figure it changes is reported before and
5
+ * after.
6
+ *
7
+ * WHY IT EXISTS. Two classes of row in a real profile are not what they look like,
8
+ * and neither can be fixed by a read:
9
+ *
10
+ * 1. LEGACY PRICES. Rows written before `costSource` existed carry a `costUsd`
11
+ * computed from the retired hardcoded table (`cost-tracker.ts`'s five-entry
12
+ * `DEFAULT_PRICING` plus a `$0.00010/1K` generic fallback). Read-side
13
+ * classification already re-derives them for DISPLAY (`cost-classification.ts`
14
+ * counts them in `recomputedCalls`), and deliberately never rewrites the file
15
+ * — but `recordedTotalUsd` ("the ledger total as recorded") keeps showing the
16
+ * old number, and a reprice is what turns the file itself honest.
17
+ * 2. TEST-ORIGIN ROWS. A row a test run wrote into a real profile. Rows written
18
+ * since `origin` was added say so (`origin: 'test'`); rows from before have no
19
+ * field at all, and the only sound evidence left in them is the model id — a
20
+ * fixture id (`wire-stub-model`: MEASURED 747 rows in a real
21
+ * `~/.nuvira/memory/cost-tracker.json`) could not have come from a real call.
22
+ *
23
+ * ─── WHAT IT REFUSES TO DO (read before changing either rule) ───────────────
24
+ *
25
+ * - It does NOT re-derive a row that already carries `costSource`. That row was
26
+ * written by today's rules under the config in force at the time; re-pricing it
27
+ * with a config that has changed since would rewrite history to match the
28
+ * present — the exact dishonesty this whole change removes.
29
+ * - It does NOT pass a legacy row's stored `costUsd` back in as though the
30
+ * provider had reported it. The stored number is the old estimate; feeding it
31
+ * into `resolveCallCost` as `reportedCostUsd` would LAUNDER an estimate into an
32
+ * exact-looking figure. Only a row already flagged `costReported: true` keeps
33
+ * its number verbatim, because that flag was written by the old code and means
34
+ * the same thing.
35
+ * - It does NOT guess `origin`. A legacy row gets no origin field: we do not know
36
+ * which process wrote it, and stamping `live` would make it unprunable forever.
37
+ * - It NEVER writes without `apply`, and a write always leaves a `.bak` beside the
38
+ * file (the ledger is the user's billing history; a reprice is reversible).
39
+ */
40
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
41
+ import { dirname } from 'node:path';
42
+ import { costLedgerPath } from './cost-tracker.js';
43
+ import { classifyEntries, getPricingContext, } from './cost-classification.js';
44
+ import { resolveCallCost } from './model-pricing.js';
45
+ import { isFixtureModelId } from './model-selection.js';
46
+ // ─── Row-level rules ────────────────────────────────────────────────────────
47
+ /** True when the row is test-origin by evidence we can actually read. */
48
+ export function testRowVerdict(entry) {
49
+ if (entry.origin === 'test')
50
+ return 'test-origin';
51
+ // An EXPLICIT `live` tag outranks the id heuristic — the same doctrine
52
+ // `telemetryOrigin` applies (a stated fact beats an inferred one). A row the
53
+ // recorder itself marked as real usage is not deleted for looking odd.
54
+ if (entry.origin === 'live')
55
+ return null;
56
+ // A fixture id is evidence from the row itself, and it is the ONLY evidence a
57
+ // legacy row can carry (no `origin` field existed when it was written).
58
+ if (isFixtureModelId(entry.model ?? ''))
59
+ return 'fixture-id';
60
+ return null;
61
+ }
62
+ /**
63
+ * Re-derive ONE legacy row. Returns the repaired row, or the SAME object when the
64
+ * row already carries `costSource` (never re-derived — see the header).
65
+ */
66
+ function repriceRow(entry, ctx) {
67
+ if (entry.costSource !== undefined)
68
+ return entry;
69
+ const reported = entry.costReported === true ? entry.costUsd : undefined;
70
+ const resolved = resolveCallCost({
71
+ provider: entry.provider,
72
+ model: entry.model,
73
+ inputTokens: entry.inputTokens ?? 0,
74
+ outputTokens: entry.outputTokens ?? 0,
75
+ // Only a row ALREADY flagged as provider-reported keeps its figure verbatim.
76
+ ...(reported !== undefined ? { reportedCostUsd: reported } : {}),
77
+ billingTier: ctx.billingTierFor(entry.provider),
78
+ configPricing: ctx.configPricingFor(entry.provider),
79
+ catalogRow: ctx.catalogRowFor?.(entry.provider, entry.model),
80
+ });
81
+ return {
82
+ ...entry,
83
+ costUsd: resolved.billedUsd,
84
+ costSource: resolved.costSource,
85
+ ...(resolved.listUsd !== undefined ? { listPriceUsd: resolved.listUsd } : {}),
86
+ billingTier: resolved.billingTier,
87
+ pricingBasis: resolved.basis,
88
+ // `origin` is deliberately NOT set: unknown is not `live`.
89
+ };
90
+ }
91
+ // ─── The pass ───────────────────────────────────────────────────────────────
92
+ /**
93
+ * Re-price a set of ledger rows and (optionally) drop the test rows among them.
94
+ * Pure: takes rows, returns rows — the file is written by `maintainCostLedger`.
95
+ */
96
+ export function repriceLedgerEntries(entries, opts = {}) {
97
+ const ctx = opts.ctx ?? getPricingContext();
98
+ const prune = opts.pruneTestRows !== false;
99
+ const kept = [];
100
+ const removedCounts = new Map();
101
+ let repriced = 0;
102
+ let alreadyClassified = 0;
103
+ let testOriginRemoved = 0;
104
+ let fixtureRemoved = 0;
105
+ for (const entry of entries) {
106
+ const verdict = prune ? testRowVerdict(entry) : null;
107
+ if (verdict) {
108
+ if (verdict === 'test-origin')
109
+ testOriginRemoved += 1;
110
+ else
111
+ fixtureRemoved += 1;
112
+ const key = `${entry.provider}\u0000${entry.model}`;
113
+ const existing = removedCounts.get(key);
114
+ if (existing)
115
+ existing.count += 1;
116
+ else
117
+ removedCounts.set(key, {
118
+ provider: entry.provider,
119
+ model: entry.model,
120
+ count: 1,
121
+ fixtureId: verdict === 'fixture-id',
122
+ });
123
+ continue;
124
+ }
125
+ const repaired = repriceRow(entry, ctx);
126
+ if (repaired !== entry)
127
+ repriced += 1;
128
+ else
129
+ alreadyClassified += 1;
130
+ kept.push(repaired);
131
+ }
132
+ const beforeTotals = classifyEntries(entries, { ctx, excludeTestOrigin: false });
133
+ const afterTotals = classifyEntries(kept, { ctx, excludeTestOrigin: false });
134
+ const sumRecorded = (rows) => Math.round(rows.reduce((s, r) => s + (r.costUsd || 0), 0) * 100000) / 100000;
135
+ const sumTokens = (rows) => rows.reduce((s, r) => s + (r.totalTokens || 0), 0);
136
+ const unpricedCalls = (rows) => classifyEntries(rows, { ctx, excludeTestOrigin: false }).unpricedCalls;
137
+ const removed = [...removedCounts.values()].sort((a, b) => b.count - a.count);
138
+ const notes = [];
139
+ if (repriced > 0) {
140
+ notes.push(`${repriced} legacy row(s) re-priced from the ONE price source (provider catalog + declared model prices + your overrides) — no hardcoded table, no generic fallback.`);
141
+ }
142
+ if (alreadyClassified > 0) {
143
+ notes.push(`${alreadyClassified} row(s) already carried a cost classification and were left exactly as written.`);
144
+ }
145
+ if (testOriginRemoved + fixtureRemoved > 0) {
146
+ notes.push(`${testOriginRemoved} row(s) tagged \`origin: test\` and ${fixtureRemoved} row(s) with a test-fixture model id were removed — a test run is not your usage.`);
147
+ }
148
+ if (afterTotals.unverifiedListedUsd > 0) {
149
+ notes.push(`$${afterTotals.unverifiedListedUsd.toFixed(6)} of list-priced calls are still NOT counted as spend because the billing tier is undeclared: nuvira config set providers.<id>.billingTier paid|free|subscription`);
150
+ }
151
+ if (afterTotals.unpricedCalls > 0) {
152
+ notes.push(`${afterTotals.unpricedCalls} row(s) have no price evidence (an unknown provider, a pass-through gateway, or a model with no price source) — they contribute no dollars, which is honest but incomplete: set pricing.<provider>.inputPer1K/outputPer1K to price them.`);
153
+ }
154
+ if (prune && testOriginRemoved + fixtureRemoved === 0) {
155
+ notes.push('No test-origin rows found — new writes are tagged at record time, so this should stay at zero.');
156
+ }
157
+ return {
158
+ entries: kept,
159
+ report: {
160
+ rows: {
161
+ total: entries.length,
162
+ repriced,
163
+ alreadyClassified,
164
+ testOriginRemoved,
165
+ fixtureRemoved,
166
+ kept: kept.length,
167
+ },
168
+ before: {
169
+ recordedUsd: sumRecorded(entries),
170
+ tokens: sumTokens(entries),
171
+ unpricedCalls: unpricedCalls(entries),
172
+ },
173
+ after: {
174
+ recordedUsd: sumRecorded(kept),
175
+ tokens: sumTokens(kept),
176
+ billedUsd: afterTotals.billedUsd,
177
+ billedReportedUsd: afterTotals.billedReportedUsd,
178
+ billedListedUsd: afterTotals.billedListedUsd,
179
+ unverifiedListedUsd: afterTotals.unverifiedListedUsd,
180
+ notBilledValueUsd: afterTotals.notBilledValueUsd,
181
+ unpricedCalls: afterTotals.unpricedCalls,
182
+ },
183
+ removed,
184
+ notes,
185
+ },
186
+ };
187
+ }
188
+ // ─── File-level pass ────────────────────────────────────────────────────────
189
+ /** Read the ledger file, tolerating absence and corruption the way the tracker does. */
190
+ function readLedger(file) {
191
+ try {
192
+ if (!existsSync(file))
193
+ return { entries: [], version: 1 };
194
+ const parsed = JSON.parse(readFileSync(file, 'utf-8'));
195
+ return { entries: Array.isArray(parsed?.entries) ? parsed.entries : [], version: parsed?.version ?? 1 };
196
+ }
197
+ catch {
198
+ return { entries: [], version: 1 };
199
+ }
200
+ }
201
+ /**
202
+ * The whole pass against a FILE. `apply: false` (the default) reads and reports
203
+ * only — the dry run is the default because the ledger is billing history.
204
+ *
205
+ * On `apply` the previous file is copied to `<file>.bak` first (a fixed name, so
206
+ * the backup is the state immediately before THIS run and cannot itself grow a
207
+ * chain), and the rewrite goes through a temp-free single write.
208
+ */
209
+ export function maintainCostLedger(opts = {}) {
210
+ const file = opts.file ?? costLedgerPath();
211
+ const ledger = readLedger(file);
212
+ const { entries, report } = repriceLedgerEntries(ledger.entries, {
213
+ ctx: opts.ctx,
214
+ pruneTestRows: opts.pruneTestRows,
215
+ });
216
+ let backupPath;
217
+ if (opts.apply) {
218
+ try {
219
+ const dir = dirname(file);
220
+ if (!existsSync(dir))
221
+ mkdirSync(dir, { recursive: true });
222
+ if (existsSync(file)) {
223
+ backupPath = `${file}.bak`;
224
+ copyFileSync(file, backupPath);
225
+ }
226
+ writeFileSync(file, JSON.stringify({ version: ledger.version, entries }, null, 2), 'utf-8');
227
+ }
228
+ catch (err) {
229
+ // A failed write is reported, never silently swallowed: the operator asked
230
+ // for a repair and must know it did not happen.
231
+ return {
232
+ file,
233
+ applied: false,
234
+ ...report,
235
+ notes: [...report.notes, `✗ write failed: ${err.message} — the ledger is unchanged.`],
236
+ };
237
+ }
238
+ }
239
+ return { file, applied: opts.apply === true, ...(backupPath ? { backupPath } : {}), ...report };
240
+ }
241
+ /** The report as text — shared by the CLI and the scripts wrapper. */
242
+ export function formatMaintenanceReport(rep) {
243
+ const usd = (n) => `$${n.toFixed(6)}`;
244
+ const lines = [
245
+ rep.applied ? '🛠️ Cost Ledger Repriced (written)' : '🛠️ Cost Ledger Reprice — DRY RUN (nothing written)',
246
+ '',
247
+ ` File: ${rep.file}${rep.backupPath ? `\n Backup: ${rep.backupPath}` : ''}`,
248
+ '',
249
+ '── Rows ──',
250
+ ` total: ${rep.rows.total}`,
251
+ ` re-priced: ${rep.rows.repriced} (legacy rows re-derived by today's rules)`,
252
+ ` already classified: ${rep.rows.alreadyClassified} (left exactly as written)`,
253
+ ` removed (origin): ${rep.rows.testOriginRemoved} (tagged origin: test)`,
254
+ ` removed (fixture): ${rep.rows.fixtureRemoved} (model id could only be a fixture)`,
255
+ ` kept: ${rep.rows.kept}`,
256
+ '',
257
+ '── Recorded total ──',
258
+ ` before: ${usd(rep.before.recordedUsd)} (${rep.before.tokens} tokens, ${rep.before.unpricedCalls} unpriced calls)`,
259
+ ` after: ${usd(rep.after.recordedUsd)} (${rep.after.tokens} tokens, ${rep.after.unpricedCalls} unpriced calls)`,
260
+ '',
261
+ '── After re-pricing, by evidence ──',
262
+ ` 🧾 provider-reported (exact): ${usd(rep.after.billedReportedUsd)}`,
263
+ ` 📋 list-priced, account paid: ${usd(rep.after.billedListedUsd)}`,
264
+ ` = billed: ${usd(rep.after.billedUsd)}`,
265
+ ` ❓ list-priced, tier undeclared: ${usd(rep.after.unverifiedListedUsd)} (not counted as spend)`,
266
+ ` 🎁 free/subscription value at list: ${usd(rep.after.notBilledValueUsd)}`,
267
+ '',
268
+ ];
269
+ if (rep.removed.length > 0) {
270
+ lines.push('── Rows that would be removed ──');
271
+ for (const r of rep.removed.slice(0, 20)) {
272
+ lines.push(` ${String(r.count).padStart(6)} × ${(r.provider + '/' + r.model).slice(0, 60)}${r.fixtureId ? ' (fixture id)' : ' (origin: test)'}`);
273
+ }
274
+ if (rep.removed.length > 20)
275
+ lines.push(` … and ${rep.removed.length - 20} more pair(s)`);
276
+ lines.push('');
277
+ }
278
+ if (rep.notes.length > 0) {
279
+ lines.push('── Notes ──');
280
+ for (const n of rep.notes)
281
+ lines.push(` • ${n}`);
282
+ lines.push('');
283
+ }
284
+ if (!rep.applied) {
285
+ lines.push(' Nothing was written. Re-run with --apply to write (a .bak is kept).', '');
286
+ }
287
+ return lines.join('\n');
288
+ }
289
+ //# sourceMappingURL=cost-ledger-maintenance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-ledger-maintenance.js","sourceRoot":"","sources":["../../src/learning/cost-ledger-maintenance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Cost reconciliation — the ONLY way the word "actual" can be earned.
3
+ *
4
+ * WHAT THIS REPLACES. Every figure the ledger reports is, at best, an ESTIMATE of
5
+ * money owed: a provider-reported per-call cost is exact about the call, and a
6
+ * list price on a declared-`paid` account is a guess at the bill. Neither is the
7
+ * bill. The ledger cannot see one, so the honest headline is "billed by evidence
8
+ * — $X" and never "you paid $X". This module closes that gap the only way it can
9
+ * be closed: the operator reads the provider's OWN number off the invoice and
10
+ * enters it, and everything else here is arithmetic on two figures that came from
11
+ * outside this machine.
12
+ *
13
+ * WHAT IT IS NOT. It never guesses an invoice, never scales one, and never
14
+ * "adjusts" the ledger to match. It reports the difference and what the
15
+ * difference most likely consists of — unpriced calls, calls whose list price was
16
+ * never claimed as spend (an undeclared billing tier), or rows the ledger is
17
+ * missing.
18
+ *
19
+ * ─── THE BILLING FACT, EARNED (read before changing tierEvidence) ───────────
20
+ *
21
+ * `providers.<id>.billingTier` decides whether a list-priced call is money owed
22
+ * (`paid`) or value absorbed (`free`/`subscription`), and it is the single fact
23
+ * that moves the headline the most. Being user-declared, it is an assumption —
24
+ * which is exactly what this change is here to remove. An invoice is evidence,
25
+ * so this module states what the invoice PROVES, and the CLI refuses to record a
26
+ * tier the invoice contradicts:
27
+ *
28
+ * - $0 charged across N calls in the window, and nothing billed by evidence
29
+ * → the key is not being charged per call: `free` is EVIDENCED.
30
+ * - A charge that matches the per-call costs the provider itself reported
31
+ * → the key is metered and billed: `paid` is EVIDENCED.
32
+ * - Anything else (a flat fee, a discounted charge, a mismatch) proves only
33
+ * what it says: the key IS charged. `subscription` and `paid` cannot be told
34
+ * apart from one invoice line, so no tier is claimed — the operator picks,
35
+ * and the CLI says why it is asking rather than picking for them.
36
+ *
37
+ * A period that has not closed yet must not be reconciled against (`--days` on a
38
+ * running month reads a partial invoice). The window is always printed with the
39
+ * result so the comparison can be judged, and `--until` exists to pin it.
40
+ */
41
+ import { type ClassifiableEntry, type PricingContext } from './cost-classification.js';
42
+ import type { BillingTier } from './model-pricing.js';
43
+ /**
44
+ * What the operator read off the provider's bill. Every field is REQUIRED except
45
+ * `note`: a window is not optional, because comparing an all-time ledger to a
46
+ * monthly invoice is a meaningless subtraction dressed up as a number.
47
+ */
48
+ export interface InvoiceClaim {
49
+ /** The provider id the invoice line belongs to (e.g. `deepseek`). */
50
+ provider: string;
51
+ /** The provider's own figure for the window, USD. */
52
+ invoiceUsd: number;
53
+ /** Inclusive window start (epoch ms) — normally the billing period's start. */
54
+ since: number;
55
+ /**
56
+ * EXCLUSIVE window end (epoch ms) — normally the billing period's end. Half-open
57
+ * so consecutive periods cannot count a boundary row twice.
58
+ *
59
+ * A caller whose end is a LIVE "now" must pass `now + 1`, not `now`: a ledger
60
+ * row's timestamp is `Date.now()` and so is the caller's, in the same
61
+ * millisecond resolution, so an exclusive `now` silently drops whatever was
62
+ * recorded in the current millisecond (MEASURED — the CLI's default window did
63
+ * exactly this and reported "no ledger rows for <provider> in this window").
64
+ */
65
+ until: number;
66
+ /** Where the number came from, printed verbatim (e.g. "dashboard, Oct 2026"). */
67
+ note?: string;
68
+ }
69
+ /**
70
+ * How the invoice and the ledger relate. Three values, because "off by $0.40"
71
+ * and "off by $6.30 in the same direction" are different claims with different
72
+ * remedies, and a boolean would flatten them.
73
+ */
74
+ export type ReconciliationVerdict = 'match' | 'under-evidenced' | 'over-recorded';
75
+ export interface CostReconciliation {
76
+ provider: string;
77
+ since: number;
78
+ until: number;
79
+ /** The provider's own figure — the ONE number here that is not ours. */
80
+ invoiceUsd: number;
81
+ note?: string;
82
+ /** Ledger rows for this provider inside the window (test-origin excluded). */
83
+ requests: number;
84
+ tokens: number;
85
+ /** Evidence-backed bill: provider-reported + list-priced with a declared paid account. */
86
+ billedUsd: number;
87
+ /** The exact part (provider stated each call's charge). */
88
+ billedReportedUsd: number;
89
+ /** The estimated part (list price, account declared `paid`). */
90
+ billedListedUsd: number;
91
+ /** List-priced but the tier is undeclared: real tokens, no claim of spend. */
92
+ unverifiedListedUsd: number;
93
+ /** List price of calls a free/subscription tier absorbed — never spend. */
94
+ notBilledValueUsd: number;
95
+ /** Calls with no price evidence at all (unknown provider or pass-through gateway). */
96
+ unpricedCalls: number;
97
+ /** Test-origin rows in the window — excluded from every figure above. */
98
+ testOriginCalls: number;
99
+ /** `invoiceUsd - billedUsd`. Positive = the provider charged more than we can evidence. */
100
+ deltaUsd: number;
101
+ /** The band inside which the two are called equal (see `toleranceFor`). */
102
+ toleranceUsd: number;
103
+ verdict: ReconciliationVerdict;
104
+ /** One line naming exactly what was compared. Always printed. */
105
+ basis: string;
106
+ /** What the invoice PROVES about the key's billing tier, when it proves it. */
107
+ tierEvidence?: {
108
+ tier: BillingTier;
109
+ basis: string;
110
+ };
111
+ /** What could account for a non-zero delta — the actionable part. */
112
+ hints: string[];
113
+ }
114
+ /**
115
+ * Reconcile a provider's invoice against the ledger rows in the same window.
116
+ *
117
+ * Pure with respect to the ledger: it reads rows and returns a report; nothing is
118
+ * written and no row is modified. `provider` matching is exact — a per-provider
119
+ * invoice must never be compared against a whole-profile total.
120
+ *
121
+ * Test-origin rows are excluded from EVERY figure (a fixture call is not a charge)
122
+ * and counted in `testOriginCalls`, so a window can be checked for the rows that
123
+ * were hidden rather than silently shrinking.
124
+ */
125
+ export declare function reconcileEntries(claim: InvoiceClaim, entries: readonly ClassifiableEntry[], ctx?: PricingContext): CostReconciliation;
126
+ /**
127
+ * A reconciliation as stored. `recordedAt` is when the operator entered the
128
+ * invoice figure — a reconciliation is a statement about a moment, and a stored
129
+ * one without a date invites being read as current.
130
+ */
131
+ export interface StoredReconciliation extends CostReconciliation {
132
+ recordedAt: number;
133
+ }
134
+ /** Where the reconciliation records live, resolved lazily (see cost-tracker). */
135
+ export declare function reconciliationLedgerPath(): string;
136
+ /**
137
+ * Every recorded reconciliation, newest first. Never throws — a corrupt file
138
+ * reads as empty, because this is display/audit data and a broken history must
139
+ * not take down the cost page.
140
+ */
141
+ export declare function readReconciliations(opts?: {
142
+ file?: string;
143
+ }): StoredReconciliation[];
144
+ /**
145
+ * Persist a reconciliation. Appending rather than replacing on purpose: the
146
+ * HISTORY is the audit trail ("this was checked against the October invoice, and
147
+ * the delta was 3 cents"), and a later check must not erase what an earlier one
148
+ * established.
149
+ */
150
+ export declare function recordReconciliation(rec: CostReconciliation, opts?: {
151
+ file?: string;
152
+ at?: number;
153
+ }): StoredReconciliation;
154
+ /** The most recent record for a provider, or undefined. */
155
+ export declare function lastReconciliationFor(provider: string, opts?: {
156
+ file?: string;
157
+ }): StoredReconciliation | undefined;
158
+ /** The reconciliation, as text — shared by the CLI and any surface that prints it. */
159
+ export declare function formatReconciliation(rec: StoredReconciliation | CostReconciliation): string;
160
+ //# sourceMappingURL=cost-reconcile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-reconcile.d.ts","sourceRoot":"","sources":["../../src/learning/cost-reconcile.ts"],"names":[],"mappings":"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"}