@agent-finops/core 0.7.0 → 0.7.2

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.
@@ -1,5 +1,6 @@
1
1
  import { loadAgentInventory } from "./agentInventory.js";
2
2
  import { computeDeadContext } from "./deadContext.js";
3
+ import { localAgentFormatSupports } from "./localAgentFormats/registry.js";
3
4
  import { loadToolInvocations } from "./toolInvocations.js";
4
5
  const DAY_MS = 24 * 60 * 60 * 1_000;
5
6
  const DEFAULT_WINDOW_DAYS = 30;
@@ -30,7 +31,8 @@ export async function loadContextHealth(calls, options = {}) {
30
31
  /** Pure Context Health contract builder for deterministic tests and adapters. */
31
32
  export function buildContextHealth(input = {}) {
32
33
  const now = input.now ?? new Date();
33
- const calls = input.calls ?? [];
34
+ const calls = (input.calls ?? [])
35
+ .filter((call) => localAgentFormatSupports(call.agent, "contextHealth"));
34
36
  const items = input.inventory?.items ?? [];
35
37
  const invocations = input.invocations ?? emptyInvocations();
36
38
  const windowDays = input.windowDays ?? input.deadContext?.windowDays ?? DEFAULT_WINDOW_DAYS;
package/dist/glance.js CHANGED
@@ -2,6 +2,7 @@ import { dedupeCumulativeSessionCalls, sanitizeLocalActivityText } from "./local
2
2
  import { estimateTokenCostUsd, PRICING_TABLE_AS_OF } from "./modelPricing.js";
3
3
  import { subscriptionPlans } from "./planMath.js";
4
4
  import { buildContextHealth } from "./contextHealth.js";
5
+ import { localAgentFormatDescriptors, localAgentFormatSupports } from "./localAgentFormats/registry.js";
5
6
  const HOUR_MS = 60 * 60 * 1_000;
6
7
  const DAY_MS = 24 * HOUR_MS;
7
8
  /**
@@ -16,7 +17,9 @@ export function buildUsageGlance(calls, options = {}) {
16
17
  // defense-in-depth to every string-bearing transcript/context field before
17
18
  // any calculation so secrets cannot survive in a nested session-health or
18
19
  // provenance field even if an upstream parser missed them.
19
- const safeCalls = dedupeCumulativeSessionCalls(sanitizeStringMetadata(calls));
20
+ const supportedFormats = localAgentFormatDescriptors.filter((descriptor) => (descriptor.capabilities.glance));
21
+ const safeCalls = dedupeCumulativeSessionCalls(sanitizeStringMetadata(calls))
22
+ .filter((call) => localAgentFormatSupports(call.agent, "glance"));
20
23
  const suppliedContextHealth = options.contextHealth
21
24
  ? sanitizeStringMetadata(options.contextHealth)
22
25
  : undefined;
@@ -40,7 +43,8 @@ export function buildUsageGlance(calls, options = {}) {
40
43
  const plan = currentSession
41
44
  ? toGlancePlan(currentSession.agent, safeDetectedPlans)
42
45
  : null;
43
- const limitCalls = sanitizeStringMetadata(options.limitCalls ?? safeCalls);
46
+ const limitCalls = sanitizeStringMetadata(options.limitCalls ?? safeCalls)
47
+ .filter((call) => localAgentFormatSupports(call.agent, "rateLimits"));
44
48
  const limits = latestLimits(limitCalls, now).map(({ agent, window, observedAt }) => toGlanceLimit(agent, window, observedAt));
45
49
  const windowStart = now.getTime() - focusWindowDays * DAY_MS;
46
50
  const windowCalls = safeCalls.filter((call) => Date.parse(call.timestamp) >= windowStart);
@@ -62,7 +66,8 @@ export function buildUsageGlance(calls, options = {}) {
62
66
  generatedAt: now.toISOString(),
63
67
  filesParsed: options.filesParsed ?? 0
64
68
  });
65
- const detectedAgents = options.detectedAgents ?? uniqueAgents(safeCalls);
69
+ const detectedAgents = (options.detectedAgents ?? uniqueAgents(safeCalls))
70
+ .filter((agent) => localAgentFormatSupports(agent, "glance"));
66
71
  const agentsWithLimits = new Set(limits.map((limit) => limit.agent));
67
72
  const limitAgents = uniqueAgents(limitCalls.filter((call) => call.rateLimits));
68
73
  const reportedWindows = (agent) => ([...new Set(limits
@@ -82,20 +87,15 @@ export function buildUsageGlance(calls, options = {}) {
82
87
  generatedAt: now.toISOString(),
83
88
  coverage: {
84
89
  filesParsed: options.filesParsed ?? 0,
85
- supportedTranscriptAgents: ["claude-code", "codex"],
90
+ supportedTranscriptAgents: supportedFormats.map((descriptor) => descriptor.id),
86
91
  detectedAgents,
87
- rateLimitMetadata: [
88
- {
89
- agent: "claude-code",
90
- status: "not_reported_by_transcript",
91
- windowsReported: reportedWindows("claude-code")
92
- },
93
- {
94
- agent: "codex",
95
- status: agentsWithLimits.has("codex") ? "reported" : "not_seen",
96
- windowsReported: reportedWindows("codex")
97
- }
98
- ],
92
+ rateLimitMetadata: supportedFormats.map((descriptor) => ({
93
+ agent: descriptor.id,
94
+ status: descriptor.capabilities.rateLimits
95
+ ? agentsWithLimits.has(descriptor.id) ? "reported" : "not_seen"
96
+ : "not_reported_by_transcript",
97
+ windowsReported: reportedWindows(descriptor.id)
98
+ })),
99
99
  providerConnectionRequired: ["cursor", "github-copilot"]
100
100
  },
101
101
  provenance: {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./analyze.js";
2
2
  export * from "./agentInventory.js";
3
+ export * from "./agentEconomicsReceipt.js";
3
4
  export * from "./activitySnapshot.js";
4
5
  export * from "./activitySnapshotCache.js";
5
6
  export * from "./attribution.js";
@@ -11,7 +12,10 @@ export * from "./discovery.js";
11
12
  export * from "./glance.js";
12
13
  export * from "./toolInvocations.js";
13
14
  export * from "./insights.js";
14
- export * from "./localAgentLogs.js";
15
+ export { aggregateCalls, dedupeCumulativeSessionCalls, latestObservedWorkingDirectory, loadLocalAgentFinancialUsage, loadLocalAgentUsage, parseClaudeCodeTranscript, parseCodexRollout, sanitizeLocalActivityText } from "./localAgentLogs.js";
16
+ export type { LocalAgentActivity, LocalAgentCall, LocalAgentFinancialLogOptions, LocalAgentLogDiagnostic, LocalAgentLogDiagnosticCode, LocalAgentLogOptions, LocalAgentLogResult, LocalAgentRateLimitSnapshot, LocalAgentRateLimitWindow, LocalAgentSourceScan, LocalAgentTurnUsage } from "./localAgentLogs.js";
17
+ export * from "./localAgentFormats/registry.js";
18
+ export type * from "./localAgentFormats/types.js";
15
19
  export * from "./modelPricing.js";
16
20
  export * from "./planDetection.js";
17
21
  export * from "./planMath.js";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./analyze.js";
2
2
  export * from "./agentInventory.js";
3
+ export * from "./agentEconomicsReceipt.js";
3
4
  export * from "./activitySnapshot.js";
4
5
  export * from "./activitySnapshotCache.js";
5
6
  export * from "./attribution.js";
@@ -11,7 +12,8 @@ export * from "./discovery.js";
11
12
  export * from "./glance.js";
12
13
  export * from "./toolInvocations.js";
13
14
  export * from "./insights.js";
14
- export * from "./localAgentLogs.js";
15
+ export { aggregateCalls, dedupeCumulativeSessionCalls, latestObservedWorkingDirectory, loadLocalAgentFinancialUsage, loadLocalAgentUsage, parseClaudeCodeTranscript, parseCodexRollout, sanitizeLocalActivityText } from "./localAgentLogs.js";
16
+ export * from "./localAgentFormats/registry.js";
15
17
  export * from "./modelPricing.js";
16
18
  export * from "./planDetection.js";
17
19
  export * from "./planMath.js";
@@ -0,0 +1,8 @@
1
+ import type { LocalAgentFormatDescriptor, LocalAgentFormatId } from "./types.js";
2
+ export declare const localAgentFormatDescriptors: readonly LocalAgentFormatDescriptor[];
3
+ export declare function localAgentFormatDescriptor(id: LocalAgentFormatId): LocalAgentFormatDescriptor | undefined;
4
+ export declare function localAgentFormatLabel(id: LocalAgentFormatId): string;
5
+ export declare function localAgentFormatSupports(id: LocalAgentFormatId, capability: keyof LocalAgentFormatDescriptor["capabilities"]): boolean;
6
+ export declare function matchesLocalAgentFormatFile(descriptor: LocalAgentFormatDescriptor, filePath: string): boolean;
7
+ export declare function validateLocalAgentFormatDescriptors(registry?: readonly LocalAgentFormatDescriptor[]): void;
8
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1,219 @@
1
+ import { basename } from "node:path";
2
+ const descriptors = [
3
+ {
4
+ schemaVersion: 1,
5
+ id: "claude-code",
6
+ order: 10,
7
+ label: "Claude Code",
8
+ provider: "anthropic",
9
+ defaultHomeRelative: [".claude", "projects"],
10
+ legacyDirectoryOption: "claudeProjectsDir",
11
+ discovery: { extension: ".jsonl" },
12
+ confidenceDefaults: {
13
+ validationCoverage: "live_verified",
14
+ pricedFinancialEvidence: "estimated",
15
+ unpricedFinancialEvidence: "missing",
16
+ sourceConfidence: "estimated"
17
+ },
18
+ sourceRecord: {
19
+ id: "local-agent-logs",
20
+ name: "Local agent session logs",
21
+ observedFrom: "claude-code transcript JSONL (this machine)",
22
+ providerCostType: "local_agent_logs",
23
+ usageGranularity: "daily_aggregate",
24
+ operation: "claude-code sessions"
25
+ },
26
+ capabilities: {
27
+ activity: true,
28
+ contextHealth: true,
29
+ financialFastPath: true,
30
+ glance: true,
31
+ invocationEvidence: false,
32
+ planContext: true,
33
+ rateLimits: false
34
+ },
35
+ financialRead: "full_jsonl",
36
+ validationNote: "Local transcript parsing is exercised against live logs; dollar values remain API-rate estimates.",
37
+ docs: {
38
+ format: "JSON Lines under ~/.claude/projects/**/*.jsonl",
39
+ howRead: [
40
+ "Read one JSON object per line and keep assistant records with message.usage.",
41
+ "Ignore <synthetic> placeholder models and deduplicate streaming/retry rewrites by message and request identity.",
42
+ "Treat each retained assistant usage record as turn-scoped evidence."
43
+ ],
44
+ fieldsRead: [
45
+ "timestamp, model, and token-usage components",
46
+ "session and working-directory metadata for local deduplication and attribution",
47
+ "human-prompt and tool metadata for privacy-reduced local activity summaries"
48
+ ],
49
+ verified: [
50
+ "The reader and its failure paths have been exercised against live local Claude Code logs.",
51
+ "Synthetic recorded fixtures lock the supported JSONL shapes and normalized output."
52
+ ],
53
+ estimated: [
54
+ "Supported token components are priced at published API rates as API-equivalent value."
55
+ ],
56
+ notVerified: [
57
+ "API-equivalent value is not billed spend, subscription cost, savings, or ROI.",
58
+ "Claude Code transcripts do not provide account-plan headroom; missing limits are not inferred."
59
+ ],
60
+ privacy: [
61
+ "Parsing and aggregation run locally; raw prompts and responses are not returned by the parser registry.",
62
+ "aibill never sits in the inference path and never stores, prints, or proxies provider credentials."
63
+ ],
64
+ limitations: [
65
+ "Malformed lines are skipped and reported.",
66
+ "Incomplete token shapes remain unpriced with missing financial evidence instead of becoming $0."
67
+ ]
68
+ },
69
+ fixtures: ["claude-code-v1"]
70
+ },
71
+ {
72
+ schemaVersion: 1,
73
+ id: "codex",
74
+ order: 20,
75
+ label: "Codex",
76
+ provider: "openai",
77
+ defaultHomeRelative: [".codex", "sessions"],
78
+ legacyDirectoryOption: "codexSessionsDir",
79
+ discovery: { extension: ".jsonl", basenamePrefix: "rollout-" },
80
+ confidenceDefaults: {
81
+ validationCoverage: "live_verified",
82
+ pricedFinancialEvidence: "estimated",
83
+ unpricedFinancialEvidence: "missing",
84
+ sourceConfidence: "estimated"
85
+ },
86
+ sourceRecord: {
87
+ id: "local-agent-logs",
88
+ name: "Local agent session logs",
89
+ observedFrom: "codex transcript JSONL (this machine)",
90
+ providerCostType: "local_agent_logs",
91
+ usageGranularity: "daily_aggregate",
92
+ operation: "codex sessions"
93
+ },
94
+ capabilities: {
95
+ activity: true,
96
+ contextHealth: true,
97
+ financialFastPath: true,
98
+ glance: true,
99
+ invocationEvidence: true,
100
+ planContext: true,
101
+ rateLimits: true
102
+ },
103
+ financialRead: "bounded_event_jsonl",
104
+ validationNote: "Local parsing was replayed against live logs; total-only token shapes and unknown aliases remain missing rather than becoming estimated $0.",
105
+ docs: {
106
+ format: "JSON Lines under ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl",
107
+ howRead: [
108
+ "Read the Codex event stream and retain the root session identity, model, and latest cumulative event_msg/token_count evidence.",
109
+ "Never sum earlier running token counters; forked sessions subtract a supported inherited cumulative baseline.",
110
+ "Use a bounded proof-based financial reader for init/cache while the full reader retains privacy-reduced activity and optional invocation evidence."
111
+ ],
112
+ fieldsRead: [
113
+ "session metadata, timestamps, model, and cumulative/last-turn token usage",
114
+ "transcript-reported rate-limit windows when present",
115
+ "tool-call metadata for local attribution and optional privacy-safe invocation counts"
116
+ ],
117
+ verified: [
118
+ "The full and optimized financial readers have been replayed against live local Codex logs.",
119
+ "Synthetic recorded fixtures lock cumulative, last-turn, and rate-limit normalization."
120
+ ],
121
+ estimated: [
122
+ "Supported token components are priced at published API rates as API-equivalent value."
123
+ ],
124
+ notVerified: [
125
+ "API-equivalent value is not billed spend, subscription cost, savings, or ROI.",
126
+ "A transcript-reported limit is plan-capacity evidence, not a provider invoice."
127
+ ],
128
+ privacy: [
129
+ "Parsing and aggregation run locally; raw prompts and responses are not returned by the parser registry.",
130
+ "aibill never sits in the inference path and never stores, prints, or proxies provider credentials."
131
+ ],
132
+ limitations: [
133
+ "Only rollout-*.jsonl files are parsed as Codex sessions.",
134
+ "Incomplete, regressing, or total-only token shapes remain unpriced with missing financial evidence."
135
+ ]
136
+ },
137
+ fixtures: ["codex-v1"]
138
+ }
139
+ ];
140
+ export const localAgentFormatDescriptors = Object.freeze([...descriptors]
141
+ .sort((left, right) => left.order - right.order)
142
+ .map(freezeDescriptor));
143
+ export function localAgentFormatDescriptor(id) {
144
+ return localAgentFormatDescriptors.find((descriptor) => descriptor.id === id);
145
+ }
146
+ export function localAgentFormatLabel(id) {
147
+ return localAgentFormatDescriptor(id)?.label ?? id;
148
+ }
149
+ export function localAgentFormatSupports(id, capability) {
150
+ return localAgentFormatDescriptor(id)?.capabilities[capability] === true;
151
+ }
152
+ export function matchesLocalAgentFormatFile(descriptor, filePath) {
153
+ const name = basename(filePath);
154
+ if (descriptor.discovery.extension && !name.endsWith(descriptor.discovery.extension))
155
+ return false;
156
+ if (descriptor.discovery.basename && name !== descriptor.discovery.basename)
157
+ return false;
158
+ if (descriptor.discovery.basenamePrefix && !name.startsWith(descriptor.discovery.basenamePrefix))
159
+ return false;
160
+ return true;
161
+ }
162
+ export function validateLocalAgentFormatDescriptors(registry = localAgentFormatDescriptors) {
163
+ const ids = new Set();
164
+ const orders = new Set();
165
+ for (const descriptor of registry) {
166
+ if (descriptor.schemaVersion !== 1)
167
+ throw new Error(`Unsupported local-agent format schema for ${descriptor.id}.`);
168
+ if (!descriptor.id || !descriptor.label || !descriptor.provider)
169
+ throw new Error("Local-agent format identity is incomplete.");
170
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(descriptor.id) ||
171
+ !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(descriptor.provider)) {
172
+ throw new Error(`Unsafe local-agent format identity for ${descriptor.id}.`);
173
+ }
174
+ if (ids.has(descriptor.id))
175
+ throw new Error(`Duplicate local-agent format id: ${descriptor.id}.`);
176
+ if (orders.has(descriptor.order))
177
+ throw new Error(`Duplicate local-agent format order: ${descriptor.order}.`);
178
+ if (descriptor.defaultHomeRelative.length === 0 || descriptor.defaultHomeRelative.some((part) => (!part || part === "." || part === ".." || /[\\/\u0000]/.test(part)))) {
179
+ throw new Error(`Unsafe default local-agent root for ${descriptor.id}.`);
180
+ }
181
+ const discovery = descriptor.discovery;
182
+ if (!discovery.extension && !discovery.basename && !discovery.basenamePrefix) {
183
+ throw new Error(`Local-agent format ${descriptor.id} must declare a bounded file rule.`);
184
+ }
185
+ if (discovery.extension && !/^\.[A-Za-z0-9]+$/.test(discovery.extension)) {
186
+ throw new Error(`Unsafe discovery extension for ${descriptor.id}.`);
187
+ }
188
+ for (const value of [discovery.basename, discovery.basenamePrefix]) {
189
+ if (value && (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value) || /[\\/\u0000]/.test(value))) {
190
+ throw new Error(`Unsafe discovery filename rule for ${descriptor.id}.`);
191
+ }
192
+ }
193
+ if (descriptor.confidenceDefaults.pricedFinancialEvidence !== "estimated" ||
194
+ descriptor.confidenceDefaults.unpricedFinancialEvidence !== "missing" ||
195
+ descriptor.confidenceDefaults.sourceConfidence !== "estimated") {
196
+ throw new Error(`Local transcript format ${descriptor.id} must default to estimated/missing financial evidence.`);
197
+ }
198
+ if (descriptor.fixtures.length === 0 || descriptor.fixtures.some((fixture) => (!/^[a-z0-9]+(?:-[a-z0-9]+)*-v[1-9][0-9]*$/.test(fixture)))) {
199
+ throw new Error(`Local-agent format ${descriptor.id} has an unsafe or missing recorded fixture.`);
200
+ }
201
+ ids.add(descriptor.id);
202
+ orders.add(descriptor.order);
203
+ }
204
+ }
205
+ function freezeDescriptor(descriptor) {
206
+ Object.freeze(descriptor.defaultHomeRelative);
207
+ Object.freeze(descriptor.discovery);
208
+ Object.freeze(descriptor.confidenceDefaults);
209
+ Object.freeze(descriptor.sourceRecord);
210
+ Object.freeze(descriptor.capabilities);
211
+ for (const values of Object.values(descriptor.docs)) {
212
+ if (Array.isArray(values))
213
+ Object.freeze(values);
214
+ }
215
+ Object.freeze(descriptor.docs);
216
+ Object.freeze(descriptor.fixtures);
217
+ return Object.freeze(descriptor);
218
+ }
219
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1,4 @@
1
+ import type { LocalAgentFormatRuntime } from "./types.js";
2
+ export declare const localAgentFormatRuntimeRegistry: readonly LocalAgentFormatRuntime[];
3
+ export declare function validateLocalAgentFormatRuntimeRegistry(registry?: readonly LocalAgentFormatRuntime[], descriptors?: readonly import("./types.js").LocalAgentFormatDescriptor[]): void;
4
+ //# sourceMappingURL=runtimeRegistry.d.ts.map
@@ -0,0 +1,55 @@
1
+ import { parseClaudeCodeTranscript, parseCodexRollout, readClaudeCodeFinancialFileForRegistry, readCodexFinancialFileForRegistry } from "../localAgentLogs.js";
2
+ import { createCodexInvocationCollector } from "../toolInvocations.js";
3
+ import { localAgentFormatDescriptors, validateLocalAgentFormatDescriptors } from "./registry.js";
4
+ const byId = new Map(localAgentFormatDescriptors.map((descriptor) => [descriptor.id, descriptor]));
5
+ const claudeCode = byId.get("claude-code");
6
+ const codex = byId.get("codex");
7
+ if (!claudeCode || !codex) {
8
+ throw new Error("Built-in local-agent format descriptors are incomplete.");
9
+ }
10
+ validateLocalAgentFormatDescriptors();
11
+ const runtimes = [
12
+ {
13
+ descriptor: claudeCode,
14
+ parseFull: ({ content, filePath, sinceMs, onDiagnostic }) => ({
15
+ calls: parseClaudeCodeTranscript(content, filePath, sinceMs, onDiagnostic)
16
+ }),
17
+ parseFinancialFile: readClaudeCodeFinancialFileForRegistry
18
+ },
19
+ {
20
+ descriptor: codex,
21
+ parseFull: ({ content, sinceMs, collectInvocationEvidence, onDiagnostic }) => {
22
+ const collector = collectInvocationEvidence
23
+ ? createCodexInvocationCollector(sinceMs)
24
+ : undefined;
25
+ return {
26
+ calls: parseCodexRollout(content, collector?.consume, onDiagnostic),
27
+ ...(collector ? { invocationFile: collector.finish() } : {})
28
+ };
29
+ },
30
+ parseFinancialFile: readCodexFinancialFileForRegistry
31
+ }
32
+ ];
33
+ export const localAgentFormatRuntimeRegistry = Object.freeze(runtimes.map((runtime) => Object.freeze(runtime)));
34
+ export function validateLocalAgentFormatRuntimeRegistry(registry = localAgentFormatRuntimeRegistry, descriptors = localAgentFormatDescriptors) {
35
+ validateLocalAgentFormatDescriptors(registry.map((entry) => entry.descriptor));
36
+ const expected = [...registry].sort((left, right) => (left.descriptor.order - right.descriptor.order));
37
+ if (expected.some((entry, index) => entry !== registry[index])) {
38
+ throw new Error("Local-agent runtime registry must be ordered by descriptor order.");
39
+ }
40
+ for (const entry of registry) {
41
+ if (typeof entry.parseFull !== "function" || typeof entry.parseFinancialFile !== "function") {
42
+ throw new Error(`Local-agent format ${entry.descriptor.id} is missing a runtime parser.`);
43
+ }
44
+ }
45
+ const runtimeIds = registry.map((entry) => entry.descriptor.id);
46
+ const descriptorIds = descriptors.map((descriptor) => descriptor.id);
47
+ if (JSON.stringify(runtimeIds) !== JSON.stringify(descriptorIds)) {
48
+ throw new Error(`Local-agent runtime registry must exactly match descriptor order: ${descriptorIds.join(", ")}.`);
49
+ }
50
+ if (registry.some((entry, index) => entry.descriptor !== descriptors[index])) {
51
+ throw new Error("Local-agent runtime entries must use the canonical descriptor objects.");
52
+ }
53
+ }
54
+ validateLocalAgentFormatRuntimeRegistry();
55
+ //# sourceMappingURL=runtimeRegistry.js.map
@@ -0,0 +1,84 @@
1
+ import type { LocalAgentCall, LocalAgentLogDiagnostic, LocalAgentSourceScan } from "../localAgentLogs.js";
2
+ import type { ParsedInvocationFile } from "../toolInvocations.js";
3
+ /**
4
+ * Public format identity contract. Add future parser IDs here as part of the
5
+ * registry-owned change so existing exhaustive consumers do not see an
6
+ * unbounded `string` in a patch release.
7
+ */
8
+ export type LocalAgentFormatId = "claude-code" | "codex";
9
+ export type LocalAgentFormatDescriptor = {
10
+ readonly schemaVersion: 1;
11
+ readonly id: LocalAgentFormatId;
12
+ readonly order: number;
13
+ readonly label: string;
14
+ readonly provider: string;
15
+ readonly defaultHomeRelative: readonly string[];
16
+ readonly legacyDirectoryOption?: "claudeProjectsDir" | "codexSessionsDir";
17
+ readonly discovery: {
18
+ readonly extension?: string;
19
+ readonly basename?: string;
20
+ readonly basenamePrefix?: string;
21
+ };
22
+ readonly confidenceDefaults: {
23
+ readonly validationCoverage: "live_verified" | "fixture_verified" | "untested" | "failed";
24
+ readonly pricedFinancialEvidence: "estimated";
25
+ readonly unpricedFinancialEvidence: "missing";
26
+ readonly sourceConfidence: "estimated";
27
+ };
28
+ readonly sourceRecord: {
29
+ readonly id: "local-agent-logs";
30
+ readonly name: "Local agent session logs";
31
+ readonly observedFrom: string;
32
+ readonly providerCostType: "local_agent_logs";
33
+ readonly usageGranularity: "daily_aggregate";
34
+ readonly operation: string;
35
+ };
36
+ readonly capabilities: {
37
+ readonly activity: boolean;
38
+ readonly contextHealth: boolean;
39
+ readonly financialFastPath: boolean;
40
+ readonly glance: boolean;
41
+ readonly invocationEvidence: boolean;
42
+ readonly planContext: boolean;
43
+ readonly rateLimits: boolean;
44
+ };
45
+ readonly financialRead: "full_jsonl" | "bounded_event_jsonl";
46
+ readonly validationNote: string;
47
+ readonly docs: {
48
+ readonly format: string;
49
+ readonly howRead: readonly string[];
50
+ readonly fieldsRead: readonly string[];
51
+ readonly verified: readonly string[];
52
+ readonly estimated: readonly string[];
53
+ readonly notVerified: readonly string[];
54
+ readonly privacy: readonly string[];
55
+ readonly limitations: readonly string[];
56
+ };
57
+ readonly fixtures: readonly string[];
58
+ };
59
+ export type LocalAgentFormatParseContext = {
60
+ content: string;
61
+ filePath: string;
62
+ sinceMs?: number;
63
+ collectInvocationEvidence: boolean;
64
+ onDiagnostic: (diagnostic: {
65
+ code: "malformed_jsonl" | "unsupported_token_shape";
66
+ count: number;
67
+ }) => void;
68
+ };
69
+ export type LocalAgentFormatParseResult = {
70
+ calls: LocalAgentCall[];
71
+ invocationFile?: ParsedInvocationFile;
72
+ };
73
+ export type LocalAgentFormatFinancialFileContext = {
74
+ filePath: string;
75
+ sinceMs?: number;
76
+ scan: LocalAgentSourceScan;
77
+ diagnostics: LocalAgentLogDiagnostic[];
78
+ };
79
+ export type LocalAgentFormatRuntime = {
80
+ descriptor: LocalAgentFormatDescriptor;
81
+ parseFull: (context: LocalAgentFormatParseContext) => LocalAgentFormatParseResult;
82
+ parseFinancialFile: (context: LocalAgentFormatFinancialFileContext) => Promise<LocalAgentCall[]>;
83
+ };
84
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -1,6 +1,7 @@
1
1
  import { type TokenUsage } from "./modelPricing.js";
2
2
  import type { UsageRecord } from "./schema.js";
3
- import { type ParsedInvocationFile } from "./toolInvocations.js";
3
+ import type { ParsedInvocationFile } from "./toolInvocations.js";
4
+ import type { LocalAgentFormatDescriptor, LocalAgentFormatFinancialFileContext, LocalAgentFormatId, LocalAgentFormatRuntime } from "./localAgentFormats/types.js";
4
5
  /**
5
6
  * Local agent-session log ingestion: turns the transcript files that coding
6
7
  * agents already write on this machine into UsageRecords, priced at
@@ -18,7 +19,7 @@ import { type ParsedInvocationFile } from "./toolInvocations.js";
18
19
  * total_token_usage (earlier ones are running updates — never summed).
19
20
  */
20
21
  export type LocalAgentCall = {
21
- agent: "claude-code" | "codex";
22
+ agent: LocalAgentFormatId;
22
23
  model: string;
23
24
  /** ISO timestamp of this call, or the latest cumulative usage event. */
24
25
  timestamp: string;
@@ -104,6 +105,8 @@ export type LocalAgentLogOptions = {
104
105
  claudeProjectsDir?: string;
105
106
  /** Default: ~/.codex/sessions */
106
107
  codexSessionsDir?: string;
108
+ /** Registry-native source-root overrides, keyed by format id. */
109
+ sourceDirectories?: Readonly<Partial<Record<LocalAgentFormatId, string>>>;
107
110
  /** Only include calls at/after this ISO timestamp. */
108
111
  sinceIso?: string;
109
112
  /** Collect privacy-safe Codex invocation summaries during the same JSON pass. */
@@ -178,6 +181,12 @@ export declare function parseClaudeCodeTranscript(content: string, filePath?: st
178
181
  export declare function parseCodexRollout(content: string, onEntry?: (entry: Record<string, unknown>) => void, onDiagnostic?: TranscriptParseDiagnosticHandler): LocalAgentCall[];
179
182
  /** Scan this machine's agent logs and return aggregated UsageRecords. */
180
183
  export declare function loadLocalAgentUsage(options?: LocalAgentLogOptions): Promise<LocalAgentLogResult>;
184
+ /**
185
+ * Registry-driven ingestion engine. Exported from this module for registry
186
+ * contract tests and format modules, but intentionally omitted from the
187
+ * package-root API.
188
+ */
189
+ export declare function loadLocalAgentUsageWithFormats(registry: readonly LocalAgentFormatRuntime[], options?: LocalAgentLogOptions): Promise<LocalAgentLogResult>;
181
190
  /**
182
191
  * Stream only the financial evidence required by init/status snapshots.
183
192
  *
@@ -189,8 +198,16 @@ export declare function loadLocalAgentUsage(options?: LocalAgentLogOptions): Pro
189
198
  * tool-workdir inference remains exclusive to the full qualitative loader.
190
199
  */
191
200
  export declare function loadLocalAgentFinancialUsage(options?: LocalAgentFinancialLogOptions): Promise<LocalAgentLogResult>;
201
+ /** Registry-driven financial-only engine; package-root exports stay unchanged. */
202
+ export declare function loadLocalAgentFinancialUsageWithFormats(registry: readonly LocalAgentFormatRuntime[], options?: LocalAgentFinancialLogOptions): Promise<LocalAgentLogResult>;
203
+ /** @internal Runtime hook owned by the Claude Code registry entry. */
204
+ export declare function readClaudeCodeFinancialFileForRegistry(context: LocalAgentFormatFinancialFileContext): Promise<LocalAgentCall[]>;
205
+ /** @internal Runtime hook owned by the Codex registry entry. */
206
+ export declare function readCodexFinancialFileForRegistry(context: LocalAgentFormatFinancialFileContext): Promise<LocalAgentCall[]>;
192
207
  /** Aggregate per-call usage into one UsageRecord per day+agent+model+project. */
193
208
  export declare function aggregateCalls(calls: LocalAgentCall[]): UsageRecord[];
209
+ /** @internal Registry-aware aggregation used by the extensible ingestion engine. */
210
+ export declare function aggregateCallsForFormats(calls: LocalAgentCall[], descriptors: readonly LocalAgentFormatDescriptor[]): UsageRecord[];
194
211
  /**
195
212
  * Remove known and assignment-shaped credentials from metadata before it can
196
213
  * become a topic, title, Glance field, MCP result, or copy-ready handoff.