ai-spend-agent 0.7.1 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { mkdir, readFile, rm, stat } from "node:fs/promises";
4
4
  import { homedir } from "node:os";
5
5
  import { basename, dirname, extname, join, resolve } from "node:path";
6
6
  import { fileURLToPath, pathToFileURL } from "node:url";
7
- import { analyzeSpend, attributeUsageRecords, buildUsageGlance, buildActivitySnapshot, loadContextHealth, detectLocalCredentials, detectLocalPlans, redactSecrets, readSafeStateText, invalidateConnectedSpendTrustReceipt, resolveSafeScanRoot, resolveSafeStateDirectory, subscriptionPlans, unsafeScanRootReason, selectProviderFinancialHeadlineRecords, writeSafeStateText, verifyConnectedSpendTrustReceipt, verifyConnectedSourceRegistryTrustReceipt, writeConnectedSpendTrustReceipt, loadDeadContext, sampleDeadContext, latestObservedWorkingDirectory, downgradeSampleUsageEvidence, isBundledSampleUsage, loadLocalAgentUsage, loadLocalAgentFinancialUsage, loadSampleUsageData, parseUsageRecord, scanLocalUsageSignals, buildMissingSourcePrompts, confirmMapping, createProviderConnectorStub, createLocalFolderSourceRegistry, createScanAuditLog, fetchProviderUsageRecords, addApprovedSource, normalizeSourceRegistry, downgradeUntrustedSourceRegistryClaims, buildSourceStatuses, slugifySourceId, financialEvidenceForRecords, formatSourceStatuses, readActivitySnapshot, recordActivitySnapshotRefreshFailure, sourceStatusDefinitions, writeActivitySnapshot } from "@agent-finops/core";
7
+ import { analyzeSpend, attributeUsageRecords, buildUsageGlance, buildActivitySnapshot, loadContextHealth, detectLocalCredentials, detectLocalPlans, redactSecrets, readSafeStateText, invalidateConnectedSpendTrustReceipt, resolveSafeScanRoot, resolveSafeStateDirectory, subscriptionPlans, unsafeScanRootReason, selectProviderFinancialHeadlineRecords, writeSafeStateText, verifyConnectedSpendTrustReceipt, verifyConnectedSourceRegistryTrustReceipt, writeConnectedSpendTrustReceipt, loadDeadContext, sampleDeadContext, latestObservedWorkingDirectory, downgradeSampleUsageEvidence, isBundledSampleUsage, loadLocalAgentUsage, loadLocalAgentFinancialUsage, localAgentFormatDescriptors, localAgentFormatLabel, loadSampleUsageData, parseUsageRecord, scanLocalUsageSignals, buildMissingSourcePrompts, confirmMapping, createProviderConnectorStub, createLocalFolderSourceRegistry, createScanAuditLog, fetchProviderUsageRecords, addApprovedSource, normalizeSourceRegistry, downgradeUntrustedSourceRegistryClaims, buildSourceStatuses, slugifySourceId, financialEvidenceForRecords, formatSourceStatuses, readActivitySnapshot, recordActivitySnapshotRefreshFailure, sourceStatusDefinitions, writeActivitySnapshot } from "@agent-finops/core";
8
8
  import { StatuslineInstallerError, installClaudeStatusline, uninstallClaudeStatusline } from "./statuslineInstaller.js";
9
9
  import { readStatuslineCache, renderStatusline } from "./statuslineRuntime.js";
10
10
  import { generateActionPlanMarkdown, generateApplyArtifactMarkdown, generateDemoPackageMarkdown, generateHtmlReport, generateMarkdownReport, generatePlainEnglishSummary, generatePolicyConfigDraftMarkdown, generateReportCardCaption, generateReportCardSvg, generateVerificationPlanMarkdown, groupByDimensions } from "@agent-finops/report";
@@ -556,7 +556,7 @@ async function doctorSourcesCommand(args) {
556
556
  localError = sanitizeSecretishError(error instanceof Error ? error.message : String(error));
557
557
  }
558
558
  const checkedAt = now.toISOString();
559
- for (const id of ["claude-code", "codex"]) {
559
+ for (const { id } of localAgentFormatDescriptors) {
560
560
  const records = (localLogs?.records ?? []).filter((record) => record.agentId === id);
561
561
  const evidence = financialEvidenceForRecords(records);
562
562
  const scan = localLogs?.sourceScans.find((entry) => entry.agent === id);
@@ -704,12 +704,12 @@ function localAgentDiagnosticSummary(diagnostics) {
704
704
  const unsupportedCount = unsupported
705
705
  .reduce((total, diagnostic) => total + diagnostic.count, 0);
706
706
  if (unsupportedCount > 0) {
707
- messages.push(`${unsupportedCount} ${unsupported[0].agent === "codex" ? "Codex" : "Claude Code"} token snapshot(s) lacked the input/output components required for pricing.`);
707
+ messages.push(`${unsupportedCount} ${localAgentFormatLabel(unsupported[0].agent)} token snapshot(s) lacked the input/output components required for pricing.`);
708
708
  }
709
709
  const malformedCount = malformed
710
710
  .reduce((total, diagnostic) => total + diagnostic.count, 0);
711
711
  if (malformedCount > 0) {
712
- messages.push(`${malformedCount} malformed JSONL line(s) were skipped in ${malformed[0].agent === "codex" ? "Codex" : "Claude Code"} transcripts.`);
712
+ messages.push(`${malformedCount} malformed JSONL line(s) were skipped in ${localAgentFormatLabel(malformed[0].agent)} transcripts.`);
713
713
  }
714
714
  return messages.length > 0 ? messages.join(" ") : undefined;
715
715
  }
@@ -1529,10 +1529,7 @@ function formatInitReceipt(input) {
1529
1529
  return `${plan.agent}: ${known} (${plan.billing})`;
1530
1530
  }).join("; ")
1531
1531
  : "none detected (billing mode remains unresolved)";
1532
- const sourceLines = (input.logs?.sourceScans ?? [
1533
- emptyInitSourceScan("claude-code"),
1534
- emptyInitSourceScan("codex")
1535
- ]).map((scan) => {
1532
+ const sourceLines = (input.logs?.sourceScans ?? localAgentFormatDescriptors.map((descriptor) => (emptyInitSourceScan(descriptor.id)))).map((scan) => {
1536
1533
  const agentRecords = records.filter((record) => record.agentId === scan.agent);
1537
1534
  const priced = agentRecords.filter((record) => typeof record.amountUsd === "number").length;
1538
1535
  const skipped = scan.filesSkippedBeforeWindow ?? 0;
@@ -155,9 +155,18 @@ export function renderStatusline(result, options = {}) {
155
155
  if (overage) {
156
156
  // Billed overage is the sole compact paid-alert bridge and must survive
157
157
  // narrow layouts. It never derives from plan pressure.
158
+ if (hasMultipleSubscribedAgents(snapshot)) {
159
+ return assembleMultiSubscriptionOverageLine(snapshot, segments, overage, freshness, tier, columns, now);
160
+ }
158
161
  return assembleOverageLine(segments, overage, freshness, columns);
159
162
  }
163
+ if (snapshot.mode === "subscription" && hasMultipleSubscribedAgents(snapshot)) {
164
+ return assembleMultiSubscriptionLine(snapshot, freshness, tier, columns, now, options.timeZone);
165
+ }
160
166
  if (snapshot.mode === "mixed") {
167
+ if (hasMultipleSubscribedAgents(snapshot)) {
168
+ return assembleMultiSubscriptionMixedLine(snapshot, segments, freshness, tier, columns, now, options.timeZone);
169
+ }
161
170
  return assembleMixedLine(snapshot, segments, freshness, tier, columns, now, options.timeZone);
162
171
  }
163
172
  return assembleLine(segments, freshness, columns, overage);
@@ -256,6 +265,9 @@ function renderMetered(snapshot, tier) {
256
265
  return rendered;
257
266
  }
258
267
  function renderSubscription(snapshot, tier, now, timeZone) {
268
+ if (hasMultipleSubscribedAgents(snapshot)) {
269
+ return renderMultiSubscriptionSegments(snapshot, tier, now, timeZone);
270
+ }
259
271
  const agent = selectSubscriptionAgent(snapshot, now);
260
272
  if (!agent)
261
273
  return ["subscription detected", "runway not reported"];
@@ -276,6 +288,11 @@ function renderSubscription(snapshot, tier, now, timeZone) {
276
288
  return segments.length > 0 ? segments : ["subscription detected"];
277
289
  }
278
290
  function renderMixed(snapshot, tier, now, timeZone) {
291
+ if (hasMultipleSubscribedAgents(snapshot)) {
292
+ const segments = renderMultiSubscriptionSegments(snapshot, tier, now, timeZone);
293
+ appendMeteredSevenDaySegment(snapshot, segments);
294
+ return segments.length > 0 ? segments : ["mixed billing", "amounts unavailable"];
295
+ }
279
296
  const agent = selectSubscriptionAgent(snapshot, now);
280
297
  const limits = agent ? activeLimits(agent, now) : [];
281
298
  const selectedLimits = tier === "full"
@@ -284,6 +301,42 @@ function renderMixed(snapshot, tier, now, timeZone) {
284
301
  const segments = selectedLimits.map((limit) => formatLimit(limit, now, timeZone));
285
302
  if (limits.length === 0 && tier !== "minimal")
286
303
  segments.push("runway not reported");
304
+ appendMeteredSevenDaySegment(snapshot, segments);
305
+ const value = agent?.apiEquivalent.sevenDays;
306
+ if (value?.amountUsd !== null && value?.amountUsd !== undefined &&
307
+ value.financialEvidence === "estimated") {
308
+ segments.push(`sub ~${formatUsd(value.amountUsd)} 7d value`);
309
+ }
310
+ if (agent?.pressure === "extra_usage_credits_exhausted" && tier !== "minimal") {
311
+ segments.push("plan pressure");
312
+ }
313
+ return segments.length > 0 ? segments : ["mixed billing", "amounts unavailable"];
314
+ }
315
+ function renderMultiSubscriptionSegments(snapshot, tier, now, timeZone) {
316
+ const entries = orderedSubscriptionLimits(snapshot, now);
317
+ const selected = tier === "full" ? entries : entries.slice(0, 1);
318
+ const segments = selected.map(({ agent, limit }) => tier === "full"
319
+ ? formatAttributedLimit(agent, limit, now, timeZone)
320
+ : formatAttributedLimitCompact(agent, limit));
321
+ if (entries.length === 0 && tier !== "minimal") {
322
+ segments.push("subscription detected", "runway not reported");
323
+ }
324
+ const agents = orderedSubscriptionAgents(snapshot, now);
325
+ const valueAgents = tier === "full" ? agents : agents.slice(0, 1);
326
+ for (const agent of valueAgents) {
327
+ const value = agent.apiEquivalent.sevenDays;
328
+ if (value.amountUsd !== null && value.financialEvidence === "estimated") {
329
+ segments.push(`${subscriptionAgentLabel(agent)} ~${formatUsd(value.amountUsd)} 7d value`);
330
+ }
331
+ }
332
+ if (tier !== "minimal") {
333
+ for (const agent of agents.filter(({ pressure }) => pressure === "extra_usage_credits_exhausted")) {
334
+ segments.push(`${subscriptionAgentLabel(agent)} plan pressure`);
335
+ }
336
+ }
337
+ return segments.length > 0 ? segments : ["subscription detected"];
338
+ }
339
+ function appendMeteredSevenDaySegment(snapshot, segments) {
287
340
  const billed = snapshot.metered?.providerBilled.sevenDays;
288
341
  const estimated = snapshot.metered?.apiEquivalent.sevenDays;
289
342
  if (billed?.amountUsd !== null && billed?.amountUsd !== undefined &&
@@ -294,15 +347,6 @@ function renderMixed(snapshot, tier, now, timeZone) {
294
347
  estimated.financialEvidence === "estimated") {
295
348
  segments.push(`metered ~${formatUsd(estimated.amountUsd)} 7d`);
296
349
  }
297
- const value = agent?.apiEquivalent.sevenDays;
298
- if (value?.amountUsd !== null && value?.amountUsd !== undefined &&
299
- value.financialEvidence === "estimated") {
300
- segments.push(`sub ~${formatUsd(value.amountUsd)} 7d value`);
301
- }
302
- if (agent?.pressure === "extra_usage_credits_exhausted" && tier !== "minimal") {
303
- segments.push("plan pressure");
304
- }
305
- return segments.length > 0 ? segments : ["mixed billing", "amounts unavailable"];
306
350
  }
307
351
  function renderUnresolved(snapshot, tier) {
308
352
  const value = snapshot.unresolved?.apiEquivalent.sevenDays;
@@ -324,6 +368,56 @@ function selectSubscriptionAgent(snapshot, now) {
324
368
  return left.agent.localeCompare(right.agent);
325
369
  })[0];
326
370
  }
371
+ function hasMultipleSubscribedAgents(snapshot) {
372
+ return (snapshot.subscription?.agents.length ?? 0) > 1;
373
+ }
374
+ function orderedSubscriptionLimits(snapshot, now) {
375
+ return (snapshot.subscription?.agents ?? [])
376
+ .flatMap((agent) => activeLimits(agent, now).map((limit) => ({ agent, limit })))
377
+ .sort(compareSubscriptionLimitEntries);
378
+ }
379
+ function primarySubscriptionLimits(snapshot, now) {
380
+ return (snapshot.subscription?.agents ?? [])
381
+ .flatMap((agent) => {
382
+ const limit = [...activeLimits(agent, now)].sort(compareUrgency)[0];
383
+ return limit ? [{ agent, limit }] : [];
384
+ })
385
+ .sort(compareSubscriptionLimitEntries);
386
+ }
387
+ function compareSubscriptionLimitEntries(left, right) {
388
+ const urgency = compareUrgency(left.limit, right.limit);
389
+ if (urgency !== 0)
390
+ return urgency;
391
+ const observationDifference = Date.parse(right.limit.observedAt) - Date.parse(left.limit.observedAt);
392
+ if (observationDifference !== 0)
393
+ return observationDifference;
394
+ return left.agent.agent.localeCompare(right.agent.agent);
395
+ }
396
+ function orderedSubscriptionAgents(snapshot, now) {
397
+ return [...(snapshot.subscription?.agents ?? [])].sort((left, right) => {
398
+ const leftUrgent = [...activeLimits(left, now)].sort(compareUrgency)[0];
399
+ const rightUrgent = [...activeLimits(right, now)].sort(compareUrgency)[0];
400
+ if (leftUrgent && rightUrgent) {
401
+ const urgency = compareUrgency(leftUrgent, rightUrgent);
402
+ if (urgency !== 0)
403
+ return urgency;
404
+ }
405
+ else if (leftUrgent) {
406
+ return -1;
407
+ }
408
+ else if (rightUrgent) {
409
+ return 1;
410
+ }
411
+ const latestDifference = latestLimitObservation(right, now) - latestLimitObservation(left, now);
412
+ if (latestDifference !== 0)
413
+ return latestDifference;
414
+ const recordsDifference = right.apiEquivalent.sevenDays.recordCount -
415
+ left.apiEquivalent.sevenDays.recordCount;
416
+ if (recordsDifference !== 0)
417
+ return recordsDifference;
418
+ return left.agent.localeCompare(right.agent);
419
+ });
420
+ }
327
421
  function latestLimitObservation(agent, now) {
328
422
  return Math.max(0, ...activeLimits(agent, now).map((limit) => Date.parse(limit.observedAt)));
329
423
  }
@@ -347,6 +441,24 @@ function formatLimit(limit, now, timeZone) {
347
441
  const reset = formatReset(limit, now, timeZone);
348
442
  return `${label} ${formatPercent(limit.remainingPercent)}% left ↻${reset}`;
349
443
  }
444
+ function subscriptionAgentLabel(agent) {
445
+ return agent.agent === "claude-code" ? "claude" : "codex";
446
+ }
447
+ function formatAttributedLimit(agent, limit, now, timeZone) {
448
+ const label = limit.kind === "five-hour" ? "5h" : "week";
449
+ return `${subscriptionAgentLabel(agent)} ${label} ${formatPercent(limit.remainingPercent)}% ↻${formatReset(limit, now, timeZone)}`;
450
+ }
451
+ function formatAttributedLimitCompact(agent, limit) {
452
+ const label = limit.kind === "five-hour" ? "5h" : "wk";
453
+ return `${subscriptionAgentLabel(agent)} ${label} ${formatPercent(limit.remainingPercent)}%`;
454
+ }
455
+ function formatAttributedValue(agent, compact = false) {
456
+ const value = agent.apiEquivalent.sevenDays;
457
+ if (value.amountUsd === null || value.financialEvidence !== "estimated")
458
+ return undefined;
459
+ const window = compact ? "/7d" : " 7d";
460
+ return `${subscriptionAgentLabel(agent)} ~${formatUsd(value.amountUsd)}${window} value`;
461
+ }
350
462
  function formatReset(limit, now, timeZone) {
351
463
  const reset = new Date(limit.resetsAt);
352
464
  const zone = validTimeZone(timeZone) ? timeZone : undefined;
@@ -444,6 +556,21 @@ function assembleOverageLine(segments, overage, freshness, columns) {
444
556
  ["billed"]
445
557
  ], columns);
446
558
  }
559
+ function assembleMultiSubscriptionOverageLine(snapshot, fullSegments, overage, freshness, tier, columns, now) {
560
+ const primary = primarySubscriptionLimits(snapshot, now);
561
+ const compactPrimary = primary.map(({ agent, limit }) => formatAttributedLimitCompact(agent, limit));
562
+ const urgent = compactPrimary[0];
563
+ const candidates = [];
564
+ if (tier === "full")
565
+ candidates.push(["aibill", ...fullSegments, overage, freshness]);
566
+ if (tier !== "minimal") {
567
+ candidates.push(["aibill", ...compactPrimary, overage, freshness]);
568
+ }
569
+ if (urgent)
570
+ candidates.push(["aibill", urgent, overage, freshness]);
571
+ candidates.push(["aibill", overage, freshness], ["aibill", overage], [overage], ["OVERAGE billed"], ["billed"]);
572
+ return firstFittingLine(candidates, columns);
573
+ }
447
574
  function assembleMixedLine(snapshot, fullSegments, freshness, tier, columns, now, timeZone) {
448
575
  const agent = selectSubscriptionAgent(snapshot, now);
449
576
  const urgent = agent ? [...activeLimits(agent, now)].sort(compareUrgency)[0] : undefined;
@@ -486,6 +613,75 @@ function assembleMixedLine(snapshot, fullSegments, freshness, tier, columns, now
486
613
  candidates.push(["mix", runway, meteredClear, shortFreshness], ["mix", runway.replace("↻", ""), meteredClear], ["mix", meteredClear], ["mix", metered], ["mix"]);
487
614
  return firstFittingLine(candidates, columns);
488
615
  }
616
+ function assembleMultiSubscriptionLine(snapshot, freshness, tier, columns, now, timeZone) {
617
+ const entries = orderedSubscriptionLimits(snapshot, now);
618
+ const primary = primarySubscriptionLimits(snapshot, now);
619
+ const agents = orderedSubscriptionAgents(snapshot, now);
620
+ const fullLimits = entries.map(({ agent, limit }) => formatAttributedLimit(agent, limit, now, timeZone));
621
+ const compactLimits = entries.map(({ agent, limit }) => formatAttributedLimitCompact(agent, limit));
622
+ const compactPrimary = primary.map(({ agent, limit }) => formatAttributedLimitCompact(agent, limit));
623
+ const fullValues = agents.flatMap((agent) => formatAttributedValue(agent) ?? []);
624
+ const compactValues = agents.flatMap((agent) => formatAttributedValue(agent, true) ?? []);
625
+ const pressure = agents
626
+ .filter((agent) => agent.pressure === "extra_usage_credits_exhausted")
627
+ .map((agent) => `${subscriptionAgentLabel(agent)} plan pressure`);
628
+ const noRunway = entries.length === 0 ? ["subscription detected", "runway not reported"] : [];
629
+ const urgent = compactPrimary[0];
630
+ const urgentValue = agents[0] ? formatAttributedValue(agents[0], true) : undefined;
631
+ const candidates = [];
632
+ if (tier === "full") {
633
+ candidates.push(["aibill", ...noRunway, ...fullLimits, ...fullValues, ...pressure, freshness]);
634
+ }
635
+ if (tier !== "minimal") {
636
+ candidates.push(["aibill", ...noRunway, ...compactLimits, ...compactValues, ...pressure, freshness], ["aibill", ...noRunway, ...compactLimits, freshness], ["aibill", ...noRunway, ...compactPrimary, ...compactValues, freshness], ["aibill", ...noRunway, ...compactPrimary, freshness]);
637
+ }
638
+ if (urgent) {
639
+ if (urgentValue)
640
+ candidates.push(["aibill", urgent, urgentValue, freshness]);
641
+ candidates.push(["aibill", urgent, freshness], ["aibill", urgent], [urgent]);
642
+ }
643
+ else {
644
+ candidates.push(["aibill", "runway n/r", freshness], ["runway n/r", freshness], ["aibill", ...noRunway, ...compactValues, freshness], ["aibill", ...compactValues, freshness], ["aibill", "subscription detected", freshness], ["subscription detected"]);
645
+ }
646
+ return firstFittingLine(candidates, columns);
647
+ }
648
+ function assembleMultiSubscriptionMixedLine(snapshot, fullSegments, freshness, tier, columns, now, timeZone) {
649
+ const entries = orderedSubscriptionLimits(snapshot, now);
650
+ const primary = primarySubscriptionLimits(snapshot, now);
651
+ const agents = orderedSubscriptionAgents(snapshot, now);
652
+ const compactLimits = entries.map(({ agent, limit }) => formatAttributedLimitCompact(agent, limit));
653
+ const compactPrimary = primary.map(({ agent, limit }) => formatAttributedLimitCompact(agent, limit));
654
+ const compactValues = agents.flatMap((agent) => formatAttributedValue(agent, true) ?? []);
655
+ const noRunway = entries.length === 0 ? ["subscription detected", "runway not reported"] : [];
656
+ const urgent = compactPrimary[0];
657
+ const urgentValue = agents[0] ? formatAttributedValue(agents[0], true) : undefined;
658
+ const meteredBilled = snapshot.metered?.providerBilled.sevenDays;
659
+ const meteredEstimated = snapshot.metered?.apiEquivalent.sevenDays;
660
+ const metered = meteredBilled?.amountUsd !== null && meteredBilled?.amountUsd !== undefined &&
661
+ meteredBilled.financialEvidence === "verified"
662
+ ? `metered ${formatBilledUsd(meteredBilled.amountUsd)}/7d billed`
663
+ : meteredEstimated?.amountUsd !== null && meteredEstimated?.amountUsd !== undefined &&
664
+ meteredEstimated.financialEvidence === "estimated"
665
+ ? `metered ~${formatUsd(meteredEstimated.amountUsd)}/7d`
666
+ : "metered n/r";
667
+ const shortFreshness = compactFreshness(freshness);
668
+ const candidates = [];
669
+ if (tier === "full")
670
+ candidates.push(["aibill", ...fullSegments, freshness]);
671
+ if (tier !== "minimal") {
672
+ candidates.push(["aibill", "mix", ...noRunway, ...compactLimits, metered, ...compactValues, freshness], ["aibill", "mix", ...noRunway, ...compactPrimary, metered, freshness]);
673
+ }
674
+ if (urgent) {
675
+ if (urgentValue)
676
+ candidates.push(["aibill", "mix", urgent, metered, urgentValue, shortFreshness]);
677
+ candidates.push(["aibill", "mix", urgent, metered, shortFreshness], ["mix", urgent, metered], ["aibill", urgent, shortFreshness], ["aibill", urgent], [urgent]);
678
+ }
679
+ else {
680
+ candidates.push(["aibill", "mix", ...noRunway, metered, freshness], ["aibill", ...noRunway, freshness], ["aibill", "mix", "runway n/r", metered, shortFreshness], ["mix", "runway n/r", metered, shortFreshness], ["mix", "runway n/r", shortFreshness]);
681
+ }
682
+ candidates.push(["mix", metered, shortFreshness], ["mix", metered], ["mix"]);
683
+ return firstFittingLine(candidates, columns);
684
+ }
489
685
  function formatLimitCompact(limit) {
490
686
  const label = limit.kind === "five-hour" ? "5h" : "wk";
491
687
  return `${label} ${formatPercent(limit.remainingPercent)}% left`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-spend-agent",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Local-first financial accountability CLI for Claude Code and Codex work, cost evidence, attribution, runway, provenance, and Context Health.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -54,8 +54,8 @@
54
54
  "prepack": "npm run build"
55
55
  },
56
56
  "dependencies": {
57
- "@agent-finops/core": "0.7.1",
58
- "@agent-finops/report": "0.7.1",
57
+ "@agent-finops/core": "0.7.3",
58
+ "@agent-finops/report": "0.7.3",
59
59
  "yocto-spinner": "^1.2.0"
60
60
  }
61
61
  }