@agent-finops/core 0.5.9 → 0.6.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.
- package/README.md +6 -0
- package/dist/discovery.d.ts +6 -2
- package/dist/discovery.js +36 -14
- package/dist/glance.d.ts +6 -2
- package/dist/glance.js +62 -13
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/localAgentLogs.d.ts +38 -2
- package/dist/localAgentLogs.js +184 -20
- package/dist/modelPricing.js +0 -1
- package/dist/providerConnectors.d.ts +3 -2
- package/dist/providerConnectors.js +665 -89
- package/dist/sampleData.js +4 -3
- package/dist/schema.d.ts +31 -29
- package/dist/schema.js +27 -3
- package/dist/sourceRegistry.d.ts +30 -5
- package/dist/sourceRegistry.js +250 -21
- package/dist/sourceStatus.d.ts +65 -0
- package/dist/sourceStatus.js +147 -0
- package/dist/stateTrust.d.ts +37 -0
- package/dist/stateTrust.js +277 -0
- package/package.json +1 -1
- package/samples/openai-usage.csv +2 -2
package/README.md
CHANGED
|
@@ -14,6 +14,12 @@ invoice reconciliation, or ROI.
|
|
|
14
14
|
|
|
15
15
|
Local API-equivalent estimates, subscription context, and official
|
|
16
16
|
provider-reported cost are separate concepts and must not be added together.
|
|
17
|
+
Reader/connector validation (`live_verified`, `fixture_verified`, `untested`,
|
|
18
|
+
or `failed`) is also separate from each number's financial evidence
|
|
19
|
+
(`verified`, `estimated`, `detected_unverified`, or `missing`). A live-tested
|
|
20
|
+
local reader still emits estimates or missing cost—not billed spend.
|
|
21
|
+
Source-boundary approval is a third, permission-only field: approving a folder
|
|
22
|
+
for read-only scanning never verifies any financial number inside it.
|
|
17
23
|
|
|
18
24
|
MIT licensed. See the repository
|
|
19
25
|
[README](https://github.com/futurastudio/ai-spend-agent#readme) and
|
package/dist/discovery.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export type UsageSignalKind = "dependency" | "config" | "environment" | "source_
|
|
|
2
2
|
export type UsageSignal = {
|
|
3
3
|
provider: string;
|
|
4
4
|
kind: UsageSignalKind;
|
|
5
|
+
/** Deterministic opaque reference; never a repository-controlled filename. */
|
|
5
6
|
filePath: string;
|
|
6
7
|
/** Stable rule identity; present on scanner-produced signals. */
|
|
7
8
|
ruleId?: string;
|
|
@@ -12,6 +13,7 @@ export type UsageSignal = {
|
|
|
12
13
|
confidence: number;
|
|
13
14
|
};
|
|
14
15
|
export type UsageSignalEvidence = {
|
|
16
|
+
/** Same deterministic opaque reference as UsageSignal.filePath. */
|
|
15
17
|
file: string;
|
|
16
18
|
provider: string;
|
|
17
19
|
signal: UsageSignalKind;
|
|
@@ -20,12 +22,14 @@ export type UsageSignalEvidence = {
|
|
|
20
22
|
export type LocalDiscoveryResult = {
|
|
21
23
|
rootPath: string;
|
|
22
24
|
scannedFiles: number;
|
|
25
|
+
/** Deterministic opaque references for denied/heavy descendant directories. */
|
|
23
26
|
skippedDirectories: string[];
|
|
24
|
-
/**
|
|
27
|
+
/** Opaque references for symbolic links below the approved root. They are never followed. */
|
|
25
28
|
skippedSymlinks: string[];
|
|
26
|
-
/**
|
|
29
|
+
/** Opaque references for unreadable descendants — skipped, never fatal. */
|
|
27
30
|
unreadablePaths: string[];
|
|
28
31
|
signals: UsageSignal[];
|
|
32
|
+
/** Deterministic opaque references for detected secret assignments. */
|
|
29
33
|
secretsDetected: string[];
|
|
30
34
|
redactedEvidence: string[];
|
|
31
35
|
};
|
package/dist/discovery.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { lstat, readdir, readFile } from "node:fs/promises";
|
|
2
|
-
import {
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { basename, join, relative, sep } from "node:path";
|
|
3
4
|
import { resolveSafeScanRoot } from "./scanGuard.js";
|
|
4
5
|
const skippedDirectoryNames = new Set([
|
|
5
6
|
".git",
|
|
@@ -93,13 +94,15 @@ export async function scanLocalUsageSignals(rootPath) {
|
|
|
93
94
|
return;
|
|
94
95
|
}
|
|
95
96
|
const redacted = redactSecrets(raw);
|
|
96
|
-
const relativePath = relative(canonicalRoot, path) ||
|
|
97
|
+
const relativePath = relative(canonicalRoot, path) || ".";
|
|
98
|
+
const pathReference = opaquePathReference(relativePath);
|
|
97
99
|
result.scannedFiles += 1;
|
|
98
100
|
for (const name of detectSecretNames(raw)) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
const secretReference = opaqueSecretReference(name);
|
|
102
|
+
secrets.add(secretReference);
|
|
103
|
+
result.redactedEvidence.push(`${pathReference}: ${secretReference}=[REDACTED]`);
|
|
101
104
|
}
|
|
102
|
-
for (const signal of detectExportSignals(relativePath, redacted)) {
|
|
105
|
+
for (const signal of detectExportSignals(relativePath, redacted, pathReference)) {
|
|
103
106
|
result.signals.push(signal);
|
|
104
107
|
}
|
|
105
108
|
for (const rule of providerRules) {
|
|
@@ -108,11 +111,11 @@ export async function scanLocalUsageSignals(rootPath) {
|
|
|
108
111
|
continue;
|
|
109
112
|
}
|
|
110
113
|
const kind = inferKind(path, rule.kind);
|
|
111
|
-
const evidenceMeta = buildEvidence(
|
|
114
|
+
const evidenceMeta = buildEvidence(pathReference, rule.provider, kind, rule.id);
|
|
112
115
|
result.signals.push({
|
|
113
116
|
provider: rule.provider,
|
|
114
117
|
kind,
|
|
115
|
-
filePath:
|
|
118
|
+
filePath: pathReference,
|
|
116
119
|
ruleId: rule.id,
|
|
117
120
|
evidenceMeta,
|
|
118
121
|
evidence: encodeEvidence(evidenceMeta),
|
|
@@ -120,12 +123,14 @@ export async function scanLocalUsageSignals(rootPath) {
|
|
|
120
123
|
});
|
|
121
124
|
}
|
|
122
125
|
}, skipped, symlinks, unreadable);
|
|
123
|
-
result.skippedDirectories = Array.from(skipped)
|
|
126
|
+
result.skippedDirectories = Array.from(skipped)
|
|
127
|
+
.map((path) => opaquePathReference(relative(canonicalRoot, path) || "."))
|
|
128
|
+
.sort();
|
|
124
129
|
result.skippedSymlinks = Array.from(symlinks)
|
|
125
|
-
.map((path) => relative(canonicalRoot, path) ||
|
|
130
|
+
.map((path) => opaquePathReference(relative(canonicalRoot, path) || "."))
|
|
126
131
|
.sort();
|
|
127
132
|
result.unreadablePaths = Array.from(unreadable)
|
|
128
|
-
.map((path) => relative(canonicalRoot, path) ||
|
|
133
|
+
.map((path) => opaquePathReference(relative(canonicalRoot, path) || "."))
|
|
129
134
|
.sort();
|
|
130
135
|
result.secretsDetected = Array.from(secrets).sort();
|
|
131
136
|
result.signals = dedupeSignals(result.signals).sort((left, right) => {
|
|
@@ -169,7 +174,7 @@ async function walk(rootPath, visit, skipped, symlinks, unreadable) {
|
|
|
169
174
|
}
|
|
170
175
|
if (entry.isDirectory()) {
|
|
171
176
|
if (skippedDirectoryNames.has(entry.name)) {
|
|
172
|
-
skipped.add(
|
|
177
|
+
skipped.add(path);
|
|
173
178
|
continue;
|
|
174
179
|
}
|
|
175
180
|
await walk(path, visit, skipped, symlinks, unreadable);
|
|
@@ -203,7 +208,7 @@ function inferKind(path, fallback) {
|
|
|
203
208
|
}
|
|
204
209
|
return fallback;
|
|
205
210
|
}
|
|
206
|
-
function detectExportSignals(filePath, redacted) {
|
|
211
|
+
function detectExportSignals(filePath, redacted, pathReference) {
|
|
207
212
|
const lowerPath = filePath.toLowerCase();
|
|
208
213
|
const lowerText = redacted.toLowerCase();
|
|
209
214
|
const providers = ["openai", "anthropic", "cursor", "helicone", "langfuse", "gemini", "google", "replit"];
|
|
@@ -219,11 +224,11 @@ function detectExportSignals(filePath, redacted) {
|
|
|
219
224
|
const normalizedProvider = provider === "google" ? "gemini" : provider;
|
|
220
225
|
const kind = isInvoice ? "invoice" : "provider_export";
|
|
221
226
|
const ruleId = `export.${normalizedProvider}.${kind}`;
|
|
222
|
-
const evidenceMeta = buildEvidence(
|
|
227
|
+
const evidenceMeta = buildEvidence(pathReference, normalizedProvider, kind, ruleId);
|
|
223
228
|
return [{
|
|
224
229
|
provider: normalizedProvider,
|
|
225
230
|
kind,
|
|
226
|
-
filePath,
|
|
231
|
+
filePath: pathReference,
|
|
227
232
|
ruleId,
|
|
228
233
|
evidenceMeta,
|
|
229
234
|
evidence: encodeEvidence(evidenceMeta),
|
|
@@ -236,6 +241,23 @@ function buildEvidence(file, provider, signal, ruleId) {
|
|
|
236
241
|
function encodeEvidence(evidence) {
|
|
237
242
|
return JSON.stringify(evidence);
|
|
238
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Repository-controlled descendant names are untrusted metadata. Discovery may
|
|
246
|
+
* use the real relative path internally for classification, but persisted and
|
|
247
|
+
* agent-facing output receives only this stable, non-semantic reference.
|
|
248
|
+
*/
|
|
249
|
+
function opaquePathReference(relativePath) {
|
|
250
|
+
// Normalize only the current platform's separator. A literal backslash is
|
|
251
|
+
// a valid POSIX filename character and must not alias a nested POSIX path.
|
|
252
|
+
const normalized = (relativePath || ".").split(sep).join("/");
|
|
253
|
+
const digest = createHash("sha256").update(normalized, "utf8").digest("hex").slice(0, 16);
|
|
254
|
+
return `path-${digest}`;
|
|
255
|
+
}
|
|
256
|
+
/** Repository-controlled environment names are untrusted metadata too. */
|
|
257
|
+
function opaqueSecretReference(name) {
|
|
258
|
+
const digest = createHash("sha256").update(name, "utf8").digest("hex").slice(0, 16);
|
|
259
|
+
return `secret-${digest}`;
|
|
260
|
+
}
|
|
239
261
|
function dedupeSignals(signals) {
|
|
240
262
|
const byKey = new Map();
|
|
241
263
|
for (const signal of signals) {
|
package/dist/glance.d.ts
CHANGED
|
@@ -11,8 +11,12 @@ export type GlanceSession = {
|
|
|
11
11
|
durationMinutes: number;
|
|
12
12
|
apiEquivalentUsd: number | null;
|
|
13
13
|
costConfidence: "estimated" | "missing";
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
/** Null when the transcript reports only a total and no priceable breakdown. */
|
|
15
|
+
inputTokens: number | null;
|
|
16
|
+
/** Null when the transcript reports only a total and no priceable breakdown. */
|
|
17
|
+
outputTokens: number | null;
|
|
18
|
+
/** Provider-reported total retained without inventing input/output components. */
|
|
19
|
+
reportedTotalTokens?: number;
|
|
16
20
|
};
|
|
17
21
|
export type GlanceLimit = {
|
|
18
22
|
agent: LocalAgentCall["agent"];
|
package/dist/glance.js
CHANGED
|
@@ -193,6 +193,10 @@ function groupSessions(calls) {
|
|
|
193
193
|
const last = ordered[ordered.length - 1];
|
|
194
194
|
const costs = ordered.map(callCost);
|
|
195
195
|
const costComplete = costs.every((cost) => typeof cost === "number");
|
|
196
|
+
const tokenComponentsComplete = ordered.every((call) => call.usageSupport !== "unsupported_token_shape");
|
|
197
|
+
const reportedTotalTokens = tokenComponentsComplete
|
|
198
|
+
? undefined
|
|
199
|
+
: sessionReportedTotalTokens(ordered);
|
|
196
200
|
const startedAt = ordered
|
|
197
201
|
.map((call) => call.startedAt ?? call.timestamp)
|
|
198
202
|
.sort()[0];
|
|
@@ -205,16 +209,13 @@ function groupSessions(calls) {
|
|
|
205
209
|
startedAt,
|
|
206
210
|
lastActivityAt: last.timestamp,
|
|
207
211
|
apiEquivalentUsd: costComplete ? costs.reduce((total, cost) => total + cost, 0) : null,
|
|
208
|
-
inputTokens:
|
|
209
|
-
(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
(call.usage.cacheReadTokens ?? 0) +
|
|
216
|
-
(call.usage.cacheWrite5mTokens ?? 0) +
|
|
217
|
-
(call.usage.cacheWrite1hTokens ?? 0))),
|
|
212
|
+
inputTokens: tokenComponentsComplete
|
|
213
|
+
? sum(ordered, inputSideTokens)
|
|
214
|
+
: null,
|
|
215
|
+
outputTokens: tokenComponentsComplete
|
|
216
|
+
? sum(ordered, (call) => call.usage.outputTokens)
|
|
217
|
+
: null,
|
|
218
|
+
...(reportedTotalTokens !== undefined ? { reportedTotalTokens } : {}),
|
|
218
219
|
activity: ordered
|
|
219
220
|
.slice()
|
|
220
221
|
.reverse()
|
|
@@ -237,7 +238,10 @@ function toGlanceSession(session, now, activeWithinMinutes) {
|
|
|
237
238
|
apiEquivalentUsd: roundUsd(session.apiEquivalentUsd),
|
|
238
239
|
costConfidence: session.apiEquivalentUsd === null ? "missing" : "estimated",
|
|
239
240
|
inputTokens: session.inputTokens,
|
|
240
|
-
outputTokens: session.outputTokens
|
|
241
|
+
outputTokens: session.outputTokens,
|
|
242
|
+
...(session.reportedTotalTokens !== undefined
|
|
243
|
+
? { reportedTotalTokens: session.reportedTotalTokens }
|
|
244
|
+
: {})
|
|
241
245
|
};
|
|
242
246
|
}
|
|
243
247
|
function latestLimits(calls, now) {
|
|
@@ -516,8 +520,11 @@ function buildPrimaryAction(input) {
|
|
|
516
520
|
: input.limits.length > 0
|
|
517
521
|
? "No transcript-reported plan window is currently projected to exhaust before reset."
|
|
518
522
|
: "Not available; no plan window was reported in the local transcript.";
|
|
523
|
+
const reportedTotalEvidence = input.currentSession?.reportedTotalTokens === undefined
|
|
524
|
+
? ""
|
|
525
|
+
: `; provider-reported total tokens=${input.currentSession.reportedTotalTokens.toLocaleString("en-US")}; input/output breakdown unavailable`;
|
|
519
526
|
const sessionEvidence = input.currentSession
|
|
520
|
-
? `${input.currentSession.agent}; model=${input.currentSession.model}; status=${input.currentSession.status}; API-equivalent value=${input.currentSession.apiEquivalentUsd
|
|
527
|
+
? `${input.currentSession.agent}; model=${input.currentSession.model}; status=${input.currentSession.status}; API-equivalent value=${formatGlanceUsd(input.currentSession.apiEquivalentUsd)} (${input.currentSession.costConfidence}, not billed spend)${reportedTotalEvidence}`
|
|
521
528
|
: "not available";
|
|
522
529
|
const promptLines = [
|
|
523
530
|
"Use this aibill Glance evidence to prepare a bounded session handoff.",
|
|
@@ -603,8 +610,37 @@ function limitActionName(limit) {
|
|
|
603
610
|
: limit.name;
|
|
604
611
|
}
|
|
605
612
|
function callCost(call) {
|
|
613
|
+
if (call.usageSupport === "unsupported_token_shape")
|
|
614
|
+
return undefined;
|
|
606
615
|
return estimateTokenCostUsd(call.model, call.usage);
|
|
607
616
|
}
|
|
617
|
+
function inputSideTokens(call) {
|
|
618
|
+
return call.usage.inputTokens +
|
|
619
|
+
(call.usage.cacheReadTokens ?? 0) +
|
|
620
|
+
(call.usage.cacheWrite5mTokens ?? 0) +
|
|
621
|
+
(call.usage.cacheWrite1hTokens ?? 0);
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Preserve a provider-reported total when a session contains a total-only
|
|
625
|
+
* snapshot. Complete calls can be added from their real components; an
|
|
626
|
+
* unsupported call without a trustworthy total makes the aggregate unknown.
|
|
627
|
+
*/
|
|
628
|
+
function sessionReportedTotalTokens(calls) {
|
|
629
|
+
let total = 0;
|
|
630
|
+
for (const call of calls) {
|
|
631
|
+
if (call.usageSupport === "unsupported_token_shape") {
|
|
632
|
+
if (typeof call.reportedTotalTokens !== "number" ||
|
|
633
|
+
!Number.isFinite(call.reportedTotalTokens) ||
|
|
634
|
+
call.reportedTotalTokens < 0) {
|
|
635
|
+
return undefined;
|
|
636
|
+
}
|
|
637
|
+
total += call.reportedTotalTokens;
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
total += inputSideTokens(call) + call.usage.outputTokens;
|
|
641
|
+
}
|
|
642
|
+
return total;
|
|
643
|
+
}
|
|
608
644
|
function uniqueAgents(calls) {
|
|
609
645
|
return [...new Set(calls.map((call) => call.agent))].sort();
|
|
610
646
|
}
|
|
@@ -612,7 +648,20 @@ function sum(calls, pick) {
|
|
|
612
648
|
return calls.reduce((total, call) => total + pick(call), 0);
|
|
613
649
|
}
|
|
614
650
|
function roundUsd(value) {
|
|
615
|
-
|
|
651
|
+
if (value === null)
|
|
652
|
+
return null;
|
|
653
|
+
if (value > 0 && value < 0.01) {
|
|
654
|
+
const precise = Math.round(value * 1_000_000) / 1_000_000;
|
|
655
|
+
return precise === 0 ? value : precise;
|
|
656
|
+
}
|
|
657
|
+
return Math.round(value * 100) / 100;
|
|
658
|
+
}
|
|
659
|
+
function formatGlanceUsd(value) {
|
|
660
|
+
if (value === null)
|
|
661
|
+
return "unpriced";
|
|
662
|
+
if (value > 0 && value < 0.01)
|
|
663
|
+
return "<$0.01";
|
|
664
|
+
return `$${value.toFixed(2)}`;
|
|
616
665
|
}
|
|
617
666
|
function roundPercent(value) {
|
|
618
667
|
return Math.round(value * 10) / 10;
|
package/dist/index.d.ts
CHANGED
|
@@ -17,5 +17,7 @@ export * from "./sampleData.js";
|
|
|
17
17
|
export * from "./scanGuard.js";
|
|
18
18
|
export * from "./schema.js";
|
|
19
19
|
export * from "./sourceRegistry.js";
|
|
20
|
+
export * from "./sourceStatus.js";
|
|
21
|
+
export * from "./stateTrust.js";
|
|
20
22
|
export * from "./providerConnectors.js";
|
|
21
23
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -17,5 +17,7 @@ export * from "./sampleData.js";
|
|
|
17
17
|
export * from "./scanGuard.js";
|
|
18
18
|
export * from "./schema.js";
|
|
19
19
|
export * from "./sourceRegistry.js";
|
|
20
|
+
export * from "./sourceStatus.js";
|
|
21
|
+
export * from "./stateTrust.js";
|
|
20
22
|
export * from "./providerConnectors.js";
|
|
21
23
|
//# sourceMappingURL=index.js.map
|
package/dist/localAgentLogs.d.ts
CHANGED
|
@@ -41,6 +41,14 @@ export type LocalAgentCall = {
|
|
|
41
41
|
latestTurnUsage?: LocalAgentTurnUsage;
|
|
42
42
|
/** Whether `usage` is one model turn or the session's cumulative financial total. */
|
|
43
43
|
usageScope?: "turn" | "session_cumulative";
|
|
44
|
+
/**
|
|
45
|
+
* Whether the transcript exposed the input/output components required for
|
|
46
|
+
* pricing. A total-only snapshot is still usage evidence, but pricing it as
|
|
47
|
+
* zero would be false precision.
|
|
48
|
+
*/
|
|
49
|
+
usageSupport?: "complete" | "unsupported_token_shape";
|
|
50
|
+
/** Provider-reported total retained when component fields are unavailable. */
|
|
51
|
+
reportedTotalTokens?: number;
|
|
44
52
|
usage: TokenUsage;
|
|
45
53
|
sessionId?: string;
|
|
46
54
|
/** Provider-reported plan windows embedded in the transcript, when present. */
|
|
@@ -101,6 +109,24 @@ export type LocalAgentLogOptions = {
|
|
|
101
109
|
/** Collect privacy-safe Codex invocation summaries during the same JSON pass. */
|
|
102
110
|
collectCodexInvocationEvidence?: boolean;
|
|
103
111
|
};
|
|
112
|
+
export type LocalAgentLogDiagnosticCode = "directory_missing" | "directory_unreadable" | "file_unreadable" | "malformed_jsonl" | "unsupported_token_shape";
|
|
113
|
+
export type LocalAgentLogDiagnostic = {
|
|
114
|
+
agent: LocalAgentCall["agent"];
|
|
115
|
+
code: LocalAgentLogDiagnosticCode;
|
|
116
|
+
severity: "info" | "warning" | "error";
|
|
117
|
+
/** Privacy-safe summary; absolute local paths and transcript text are omitted. */
|
|
118
|
+
message: string;
|
|
119
|
+
count: number;
|
|
120
|
+
};
|
|
121
|
+
export type LocalAgentSourceScan = {
|
|
122
|
+
agent: LocalAgentCall["agent"];
|
|
123
|
+
directoryStatus: "readable" | "missing" | "unreadable";
|
|
124
|
+
filesDiscovered: number;
|
|
125
|
+
filesParsed: number;
|
|
126
|
+
malformedLines: number;
|
|
127
|
+
unreadableFiles: number;
|
|
128
|
+
unsupportedUsageSnapshots: number;
|
|
129
|
+
};
|
|
104
130
|
export type LocalAgentLogResult = {
|
|
105
131
|
records: UsageRecord[];
|
|
106
132
|
/** Per-call entries before aggregation (for drill-down/debugging). */
|
|
@@ -108,9 +134,18 @@ export type LocalAgentLogResult = {
|
|
|
108
134
|
filesParsed: number;
|
|
109
135
|
/** Which agents actually had data on this machine. */
|
|
110
136
|
agentsDetected: Array<LocalAgentCall["agent"]>;
|
|
137
|
+
/** Per-source scan outcome, including honest empty and unsupported states. */
|
|
138
|
+
sourceScans: LocalAgentSourceScan[];
|
|
139
|
+
/** Structured, privacy-safe failures/warnings encountered during the scan. */
|
|
140
|
+
diagnostics: LocalAgentLogDiagnostic[];
|
|
111
141
|
/** Present only when requested; contains counts/basenames, never raw text. */
|
|
112
142
|
codexInvocationFiles?: ParsedInvocationFile[];
|
|
113
143
|
};
|
|
144
|
+
type TranscriptParseDiagnostic = {
|
|
145
|
+
code: "malformed_jsonl" | "unsupported_token_shape";
|
|
146
|
+
count: number;
|
|
147
|
+
};
|
|
148
|
+
type TranscriptParseDiagnosticHandler = (diagnostic: TranscriptParseDiagnostic) => void;
|
|
114
149
|
/**
|
|
115
150
|
* Codex rollout/compaction files can repeat the same session's cumulative
|
|
116
151
|
* token counter. Keep only the latest snapshot per session so financial value,
|
|
@@ -119,9 +154,9 @@ export type LocalAgentLogResult = {
|
|
|
119
154
|
*/
|
|
120
155
|
export declare function dedupeCumulativeSessionCalls(calls: LocalAgentCall[]): LocalAgentCall[];
|
|
121
156
|
/** Parse one Claude Code transcript (JSONL). Exported for tests. */
|
|
122
|
-
export declare function parseClaudeCodeTranscript(content: string, filePath?: string, sinceMs?: number): LocalAgentCall[];
|
|
157
|
+
export declare function parseClaudeCodeTranscript(content: string, filePath?: string, sinceMs?: number, onDiagnostic?: TranscriptParseDiagnosticHandler): LocalAgentCall[];
|
|
123
158
|
/** Parse one Codex rollout file (JSONL event stream). Exported for tests. */
|
|
124
|
-
export declare function parseCodexRollout(content: string, onEntry?: (entry: Record<string, unknown>) => void): LocalAgentCall[];
|
|
159
|
+
export declare function parseCodexRollout(content: string, onEntry?: (entry: Record<string, unknown>) => void, onDiagnostic?: TranscriptParseDiagnosticHandler): LocalAgentCall[];
|
|
125
160
|
/** Scan this machine's agent logs and return aggregated UsageRecords. */
|
|
126
161
|
export declare function loadLocalAgentUsage(options?: LocalAgentLogOptions): Promise<LocalAgentLogResult>;
|
|
127
162
|
/** Aggregate per-call usage into one UsageRecord per day+agent+model+project. */
|
|
@@ -132,4 +167,5 @@ export declare function aggregateCalls(calls: LocalAgentCall[]): UsageRecord[];
|
|
|
132
167
|
* This intentionally favors dropping a suspicious token over displaying it.
|
|
133
168
|
*/
|
|
134
169
|
export declare function sanitizeLocalActivityText(value: string): string;
|
|
170
|
+
export {};
|
|
135
171
|
//# sourceMappingURL=localAgentLogs.d.ts.map
|