@agent-finops/core 0.8.1 → 0.9.1

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 (51) hide show
  1. package/README.md +5 -3
  2. package/dist/actionPlanner.d.ts +140 -0
  3. package/dist/actionPlanner.js +938 -0
  4. package/dist/actionVerification.d.ts +1240 -0
  5. package/dist/actionVerification.js +1028 -0
  6. package/dist/activitySnapshot.d.ts +142 -50
  7. package/dist/activitySnapshot.js +145 -6
  8. package/dist/activitySnapshotCache.d.ts +8 -1
  9. package/dist/activitySnapshotCache.js +103 -7
  10. package/dist/agentDraftToken.d.ts +80 -0
  11. package/dist/agentDraftToken.js +188 -0
  12. package/dist/agentEconomicsReceipt.d.ts +74 -74
  13. package/dist/agentLoopContract.d.ts +27 -0
  14. package/dist/agentLoopContract.js +36 -0
  15. package/dist/glance.d.ts +27 -1
  16. package/dist/glance.js +151 -12
  17. package/dist/guidedAnswer.d.ts +51 -0
  18. package/dist/guidedAnswer.js +352 -0
  19. package/dist/index.d.ts +14 -2
  20. package/dist/index.js +13 -1
  21. package/dist/localAgentFormats/gemini.js +2 -2
  22. package/dist/localAgentFormats/registry.js +6 -2
  23. package/dist/localAgentFormats/runtimeRegistry.js +5 -2
  24. package/dist/localAgentFormats/types.d.ts +2 -1
  25. package/dist/localAgentLogs.d.ts +362 -3
  26. package/dist/localAgentLogs.js +1964 -165
  27. package/dist/modelPricing.d.ts +1 -1
  28. package/dist/modelPricing.js +1 -1
  29. package/dist/projectEconomics.d.ts +617 -0
  30. package/dist/projectEconomics.js +620 -0
  31. package/dist/projectEconomicsBuilder.d.ts +89 -0
  32. package/dist/projectEconomicsBuilder.js +473 -0
  33. package/dist/projectIndexStore.d.ts +545 -0
  34. package/dist/projectIndexStore.js +606 -0
  35. package/dist/providerConnectors.d.ts +161 -1
  36. package/dist/providerConnectors.js +406 -11
  37. package/dist/qualitativeIndexCache.d.ts +494 -0
  38. package/dist/qualitativeIndexCache.js +930 -0
  39. package/dist/resultCard.d.ts +350 -0
  40. package/dist/resultCard.js +604 -0
  41. package/dist/runtimeCommands.d.ts +36 -0
  42. package/dist/runtimeCommands.js +50 -0
  43. package/dist/scanGuard.d.ts +3 -1
  44. package/dist/scanGuard.js +164 -4
  45. package/dist/schema.d.ts +33 -31
  46. package/dist/schema.js +9 -1
  47. package/dist/sessionVitals.d.ts +145 -0
  48. package/dist/sessionVitals.js +521 -0
  49. package/dist/toolInvocations.d.ts +40 -1
  50. package/dist/toolInvocations.js +101 -20
  51. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from "./analyze.js";
2
+ export * from "./actionPlanner.js";
3
+ export * from "./actionVerification.js";
2
4
  export * from "./agentInventory.js";
3
5
  export * from "./agentEconomicsReceipt.js";
4
6
  export * from "./activitySnapshot.js";
@@ -12,14 +14,24 @@ export * from "./discovery.js";
12
14
  export * from "./glance.js";
13
15
  export * from "./toolInvocations.js";
14
16
  export * from "./insights.js";
15
- export { aggregateCalls, dedupeCumulativeSessionCalls, latestObservedWorkingDirectory, loadLocalAgentFinancialUsage, loadLocalAgentUsage, parseClaudeCodeTranscript, parseCodexRollout, sanitizeLocalActivityText } from "./localAgentLogs.js";
17
+ export { aggregateCalls, codexHeaderProbesPerScan, dedupeCumulativeSessionCalls, defaultStreamedBytesPerRun, hasCompleteQualitativeCoverage, hasExactSelectedQualitativeEvidence, latestObservedWorkingDirectory, loadLocalAgentActionEvidence, loadLocalAgentFinancialUsage, loadLocalAgentUsage, localAgentQualitativeParserVersion, parseClaudeCodeTranscript, parseCodexRollout, SAFE_QUALITATIVE_SCAN_POLICY, sanitizeLocalActivityText } from "./localAgentLogs.js";
16
18
  export * from "./localAgentFormats/registry.js";
17
19
  export * from "./modelPricing.js";
18
20
  export * from "./planDetection.js";
19
21
  export * from "./planMath.js";
22
+ export * from "./projectEconomics.js";
23
+ export * from "./resultCard.js";
24
+ export * from "./projectEconomicsBuilder.js";
25
+ export * from "./qualitativeIndexCache.js";
26
+ export * from "./projectIndexStore.js";
27
+ export * from "./runtimeCommands.js";
28
+ export * from "./guidedAnswer.js";
29
+ export * from "./agentDraftToken.js";
30
+ export * from "./agentLoopContract.js";
20
31
  export * from "./sampleData.js";
21
32
  export * from "./scanGuard.js";
22
33
  export * from "./schema.js";
34
+ export * from "./sessionVitals.js";
23
35
  export * from "./sourceRegistry.js";
24
36
  export * from "./sourceStatus.js";
25
37
  export * from "./stateTrust.js";
@@ -176,8 +176,8 @@ function processMessage(message, state) {
176
176
  state.diagnostics.add("missing_timestamp");
177
177
  return;
178
178
  }
179
- if (state.sinceMs !== undefined && Date.parse(timestamp) < state.sinceMs)
180
- return;
179
+ // Window-blind on purpose (financial cache correctness): the loader's
180
+ // final timestamp filter performs all narrowing.
181
181
  const parsedTokens = parseTokens(message.tokens);
182
182
  const metadata = mergeMetadata(state.metadata, message);
183
183
  const attribution = explicitAttribution(message, metadata) ??
@@ -46,6 +46,7 @@ const descriptors = [
46
46
  fieldsRead: [
47
47
  "timestamp, model, and token-usage components",
48
48
  "session and working-directory metadata for local deduplication and attribution",
49
+ "explicit system/turn_duration work-unit completion markers and transcript version strings when present",
49
50
  "human-prompt and tool metadata for privacy-reduced local activity summaries"
50
51
  ],
51
52
  verified: [
@@ -65,7 +66,8 @@ const descriptors = [
65
66
  ],
66
67
  limitations: [
67
68
  "Malformed lines are skipped and reported.",
68
- "Incomplete token shapes remain unpriced with missing financial evidence instead of becoming $0."
69
+ "Incomplete token shapes remain unpriced with missing financial evidence instead of becoming $0.",
70
+ "A system/turn_duration marker proves only that the latest observed turn completed; it does not prove permanent transcript closure, and missing or inconsistent completion evidence stays ineligible for automatic before/after cohorts."
69
71
  ]
70
72
  },
71
73
  fixtures: ["claude-code-v1"]
@@ -115,6 +117,7 @@ const descriptors = [
115
117
  ],
116
118
  fieldsRead: [
117
119
  "session metadata, timestamps, model, and cumulative/last-turn token usage",
120
+ "explicit event_msg/task_complete work-unit completion markers and session_meta cli_version strings when present",
118
121
  "transcript-reported rate-limit windows when present",
119
122
  "tool-call metadata for local attribution and optional privacy-safe invocation counts"
120
123
  ],
@@ -135,7 +138,8 @@ const descriptors = [
135
138
  ],
136
139
  limitations: [
137
140
  "Only rollout-*.jsonl files are parsed as Codex sessions.",
138
- "Incomplete, regressing, or total-only token shapes remain unpriced with missing financial evidence."
141
+ "Incomplete, regressing, or total-only token shapes remain unpriced with missing financial evidence.",
142
+ "An event_msg/task_complete marker proves only that the latest observed task completed; it does not prove permanent transcript closure, and missing or inconsistent completion evidence stays ineligible for automatic before/after cohorts."
139
143
  ]
140
144
  },
141
145
  fixtures: ["codex-v1"]
@@ -24,9 +24,12 @@ const runtimes = [
24
24
  const collector = collectInvocationEvidence
25
25
  ? createCodexInvocationCollector(sinceMs)
26
26
  : undefined;
27
+ const calls = parseCodexRollout(content, collector?.consume, onDiagnostic);
28
+ const invocationFile = collector?.finish();
27
29
  return {
28
- calls: parseCodexRollout(content, collector?.consume, onDiagnostic),
29
- ...(collector ? { invocationFile: collector.finish() } : {})
30
+ calls,
31
+ ...(invocationFile ? { invocationFile } : {}),
32
+ ...(collector ? { invocationWindowProof: collector.windowProof() } : {})
30
33
  };
31
34
  },
32
35
  parseFinancialFile: readCodexFinancialFileForRegistry
@@ -1,5 +1,5 @@
1
1
  import type { LocalAgentCall, LocalAgentLogDiagnostic, LocalAgentSourceScan } from "../localAgentLogs.js";
2
- import type { ParsedInvocationFile } from "../toolInvocations.js";
2
+ import type { ParsedInvocationFile, ParsedInvocationWindowProof } from "../toolInvocations.js";
3
3
  /**
4
4
  * Public format identity contract. Add future parser IDs here as part of the
5
5
  * registry-owned change so existing exhaustive consumers do not see an
@@ -78,6 +78,7 @@ export type LocalAgentFormatParseContext = {
78
78
  export type LocalAgentFormatParseResult = {
79
79
  calls: LocalAgentCall[];
80
80
  invocationFile?: ParsedInvocationFile;
81
+ invocationWindowProof?: ParsedInvocationWindowProof;
81
82
  };
82
83
  export type LocalAgentFormatFinancialFileContext = {
83
84
  filePath: string;
@@ -1,6 +1,6 @@
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, type ParsedInvocationWindowProof } from "./toolInvocations.js";
4
4
  import type { LocalAgentFormatDescriptor, LocalAgentFormatFinancialFileContext, LocalAgentFormatId, LocalAgentFormatRuntime } from "./localAgentFormats/types.js";
5
5
  /**
6
6
  * Local agent-session log ingestion: turns the transcript files that coding
@@ -35,6 +35,11 @@ export type LocalAgentCall = {
35
35
  * reads to the same repository as the active session.
36
36
  */
37
37
  workingDirectory?: string;
38
+ /**
39
+ * Stable privacy-safe identity for `workingDirectory`. Warm indexes retain
40
+ * this opaque reference while deliberately omitting the absolute path.
41
+ */
42
+ workingDirectoryRef?: string;
38
43
  /**
39
44
  * Numeric-only usage for the latest observed model turn. Codex reports this
40
45
  * separately from cumulative `total_token_usage`; Claude assistant-message
@@ -52,8 +57,21 @@ export type LocalAgentCall = {
52
57
  usageSupport?: "complete" | "unsupported_token_shape";
53
58
  /** Provider-reported total retained when component fields are unavailable. */
54
59
  reportedTotalTokens?: number;
60
+ /**
61
+ * Parser evidence for whether each disjoint token component was actually
62
+ * present in the source. Numeric zeroes alone cannot distinguish a reported
63
+ * zero from a field that the parser had to omit/default.
64
+ */
65
+ tokenComponentEvidence?: LocalAgentTokenComponentEvidence;
55
66
  /** Optional parser/source version when the evolving session format reports it. */
56
67
  sourceVersion?: string;
68
+ /**
69
+ * Explicit host completion evidence for the current session snapshot. This proves only that the
70
+ * latest observed Claude turn or Codex task reached its host completion
71
+ * marker; it does not claim that a resumable transcript is permanently
72
+ * closed.
73
+ */
74
+ completion?: LocalAgentCompletionEvidence;
57
75
  /** Raw Gemini token split retained for evidence/debugging, never prompt content. */
58
76
  geminiTokenEvidence?: {
59
77
  input?: number;
@@ -66,6 +84,24 @@ export type LocalAgentCall = {
66
84
  };
67
85
  usage: TokenUsage;
68
86
  sessionId?: string;
87
+ /**
88
+ * Distinct subagent-run identity when the host shares one `sessionId`
89
+ * across transcript files. Claude Code stores each subagent transcript
90
+ * under `<sessionId>/subagents/agent-<agentId>.jsonl` with the parent's
91
+ * `sessionId` on every line; without this identity a parent session and
92
+ * all of its subagent runs would collapse into one untruthful session row.
93
+ * Codex subagent rollouts carry their own sessionId and never set this.
94
+ */
95
+ subagentId?: string;
96
+ /**
97
+ * Host-recorded completions of subagent runs owned by this session, taken
98
+ * from the parent transcript's Task tool results (`toolUseResult.agentId`
99
+ * with `status: "completed"`). Subagent transcript files carry no
100
+ * completion marker of their own, so this is the only explicit completion
101
+ * evidence for a subagent run. Attached to one call per session; the
102
+ * session-vitals join reads it across transcript files.
103
+ */
104
+ subagentCompletions?: LocalAgentSubagentCompletion[];
69
105
  /** Provider-reported plan windows embedded in the transcript, when present. */
70
106
  rateLimits?: LocalAgentRateLimitSnapshot;
71
107
  /**
@@ -74,6 +110,27 @@ export type LocalAgentCall = {
74
110
  */
75
111
  activity?: LocalAgentActivity;
76
112
  };
113
+ export type LocalAgentTokenComponentEvidence = {
114
+ inputTokens: "observed";
115
+ outputTokens: "observed";
116
+ cacheReadTokens: "observed" | "not_separately_reported";
117
+ cacheWriteTokens: "observed" | "partial" | "not_separately_reported";
118
+ thoughtTokens: "observed" | "not_separately_reported";
119
+ toolTokens: "observed" | "not_separately_reported";
120
+ /** Complete means the parser can form a disjoint source-faithful total. */
121
+ calculatedTotalTokens: "calculated_complete" | "calculated_partial";
122
+ reportedTotalTokens: "provider_reported" | "not_reported";
123
+ };
124
+ export type LocalAgentCompletionEvidence = {
125
+ status: "completed";
126
+ evidence: "claude_turn_duration" | "codex_task_complete";
127
+ observedAt: string;
128
+ };
129
+ /** One host-recorded subagent completion (see LocalAgentCall.subagentCompletions). */
130
+ export type LocalAgentSubagentCompletion = {
131
+ subagentId: string;
132
+ observedAt: string;
133
+ };
77
134
  /**
78
135
  * Resolve the repository root most recently observed in transcript metadata.
79
136
  *
@@ -127,14 +184,224 @@ export type LocalAgentLogOptions = {
127
184
  sinceIso?: string;
128
185
  /** Collect privacy-safe Codex invocation summaries during the same JSON pass. */
129
186
  collectCodexInvocationEvidence?: boolean;
187
+ /**
188
+ * Optional fail-closed limits for qualitative/action reads. The financial
189
+ * loader has its own proof-based fast path; these limits protect the richer
190
+ * prompt/tool/activity pass from multi-gigabyte transcript histories.
191
+ *
192
+ * Files are considered newest-first within each source. A file is parsed
193
+ * only when it fits both limits in full. Skipped files never contribute
194
+ * calls, findings, invocation evidence, or zero-valued placeholders, and
195
+ * the returned source scan is explicitly marked partial.
196
+ */
197
+ qualitativeScan?: LocalAgentQualitativeScanPolicy;
198
+ /**
199
+ * Optional trusted private index backing warm qualitative scans. The adapter
200
+ * owns storage permissions/validation; keys contain a path hash and file
201
+ * identity, never a raw local path or transcript content.
202
+ */
203
+ qualitativeIndex?: LocalAgentQualitativeIndexAdapter;
204
+ /**
205
+ * Optional per-file cache for financial parse results. Unchanged files skip
206
+ * their full financial re-read; the newest file per source always parses
207
+ * fresh so the active session's evidence (including its raw working
208
+ * directory for context inference) is never served stale from a cache.
209
+ */
210
+ financialIndex?: LocalAgentFinancialIndexAdapter;
211
+ /**
212
+ * Optional ownership section of the private project index. When present,
213
+ * budget-skipped Codex files receive a bounded first-line header probe whose
214
+ * proven/unknown attribution is persisted and feeds the per-project coverage
215
+ * ledger. Claude transcripts get no header shortcut: their cwd is per-entry
216
+ * and can change mid-file.
217
+ */
218
+ ownershipIndex?: LocalAgentOwnershipIndexAdapter;
219
+ /**
220
+ * Optional checkpoint section of the private project index. When present
221
+ * together with `qualitativeIndex`, oversized Codex rollouts are parsed in
222
+ * bounded resumable slices instead of being skipped outright: each run
223
+ * advances at least one slice, and the file joins the index (and the
224
+ * per-project ledger) once its stream completes. Absent, oversized files
225
+ * keep today's skip-plus-header-pass behavior.
226
+ */
227
+ streamCheckpoints?: LocalAgentStreamCheckpointAdapter;
228
+ /**
229
+ * Requested project ref (`avref_…`) for the per-project coverage ledger.
230
+ * Only files proven to belong to a DIFFERENT ref are excluded from the
231
+ * blocking set; a file proven to belong to this exact ref keeps blocking
232
+ * until indexed — it is the oversized relevant transcript. Absent or
233
+ * malformed refs disable proven-foreign exclusion entirely (fail closed).
234
+ */
235
+ coverageProjectRef?: string;
236
+ };
237
+ export type LocalAgentQualitativeScanPolicy = {
238
+ /** Maximum UTF-8 bytes allowed for one qualitative transcript file. */
239
+ maxFileBytes: number;
240
+ /** Maximum UTF-8 bytes parsed per registered source in one scan. */
241
+ maxSourceBytes: number;
242
+ /**
243
+ * Byte allowance for the checkpointed streaming pass over oversized Codex
244
+ * rollouts in one run (default 512 MiB, roughly 4-5 s at measured
245
+ * throughput). Deliberately byte-metered rather than wall-clock so runs
246
+ * and tests are deterministic; the first scheduled file always advances by
247
+ * at least one complete line even at a smaller remaining allowance, which
248
+ * guarantees convergence.
249
+ */
250
+ maxStreamedBytesPerRun?: number;
251
+ };
252
+ export type LocalAgentQualitativeIndexKey = {
253
+ schemaVersion: 1;
254
+ parserVersion: typeof localAgentQualitativeParserVersion;
255
+ agent: LocalAgentFormatId;
256
+ /** SHA-256 of the normalized private path; the path itself is never stored. */
257
+ pathHash: string;
258
+ /** Opaque stat identity including ctime so in-place edits invalidate a hit. */
259
+ fileIdentity: string;
260
+ sinceIso: string | null;
261
+ collectInvocationEvidence: boolean;
262
+ };
263
+ export type LocalAgentQualitativeIndexValue = {
264
+ calls: LocalAgentCall[];
265
+ invocationFile?: ParsedInvocationFile;
266
+ /** Exact narrowing proof for cached aggregated invocation evidence. */
267
+ invocationWindowProof?: ParsedInvocationWindowProof;
268
+ diagnostics: Array<{
269
+ code: "malformed_jsonl" | "malformed_session_file" | "unsupported_token_shape";
270
+ count: number;
271
+ }>;
272
+ };
273
+ export type LocalAgentQualitativeIndexAdapter = {
274
+ read: (key: Readonly<LocalAgentQualitativeIndexKey>) => Promise<LocalAgentQualitativeIndexValue | undefined>;
275
+ write: (key: Readonly<LocalAgentQualitativeIndexKey>, value: Readonly<LocalAgentQualitativeIndexValue>) => Promise<void>;
276
+ };
277
+ export type LocalAgentFinancialIndexKey = {
278
+ schemaVersion: 2;
279
+ section: "financial";
280
+ agent: LocalAgentFormatId;
281
+ pathHash: string;
282
+ fileIdentity: string;
283
+ financialParserVersion: number;
284
+ };
285
+ /**
286
+ * Per-file cache for financial parse results. The stored value reuses the
287
+ * privacy-reduced qualitative value contract (calls + parse diagnostics; no
288
+ * invocation evidence). An entry's existence means the file had financial
289
+ * content under this exact identity and parser version.
290
+ */
291
+ export type LocalAgentFinancialIndexAdapter = {
292
+ read: (key: Readonly<LocalAgentFinancialIndexKey>) => Promise<LocalAgentQualitativeIndexValue | undefined>;
293
+ write: (key: Readonly<LocalAgentFinancialIndexKey>, value: Readonly<LocalAgentQualitativeIndexValue>) => Promise<void>;
294
+ };
295
+ /**
296
+ * Ownership evidence for one transcript file in the private project index.
297
+ * "unknown" is a first-class state: ownership is never guessed from hashes,
298
+ * basenames, or absence. Structurally identical to the project-index store's
299
+ * ownership document (the loader cannot import the store without a cycle).
300
+ */
301
+ export type LocalAgentOwnershipRecord = {
302
+ /** Body-derived ownership; header-only records stay "unknown". */
303
+ status: "resolved" | "no_calls" | "unknown";
304
+ /** Ownership binds to one exact file identity; rotation supersedes it. */
305
+ fileIdentity: string;
306
+ /** Distinct `avref_…` refs observed in parsed calls (empty until parsed). */
307
+ projectRefs: string[];
308
+ /**
309
+ * Bounded Codex header-pass attribution. "proven" is asserted only when the
310
+ * header cwd would short-circuit `dominantCodexCwd` exactly as a full parse
311
+ * would; every other header stays "unknown" and is never assumed foreign.
312
+ */
313
+ headerAttribution?: {
314
+ status: "proven" | "unknown";
315
+ projectRef?: string;
316
+ /**
317
+ * Subagent marker from the same bounded header read — reused to order
318
+ * the streaming schedule without re-probing. A scheduling hint only,
319
+ * never ownership evidence.
320
+ */
321
+ isSubagent?: boolean;
322
+ };
130
323
  };
324
+ /**
325
+ * Ownership section of the private project index. `createProjectIndexAdapters`
326
+ * satisfies this structurally; the adapter owns storage validation and
327
+ * rotation-superseding semantics.
328
+ */
329
+ export type LocalAgentOwnershipIndexAdapter = {
330
+ readOwnership: (agent: LocalAgentFormatId, pathHash: string) => Promise<LocalAgentOwnershipRecord | undefined>;
331
+ writeOwnership: (agent: LocalAgentFormatId, pathHash: string, ownership: Readonly<LocalAgentOwnershipRecord>) => Promise<void>;
332
+ };
333
+ /**
334
+ * Resumable stream checkpoint for one oversized transcript (design section
335
+ * e). The identity pin is deliberately append-tolerant (dev/ino/birthtime
336
+ * survive appends); truncation, rotation, edits inside the 64 KiB prefix
337
+ * probe window, and parser contract changes all fail the resume proof and
338
+ * force a restart. Documented residual (QA-confirmed): an in-place edit of
339
+ * bytes BEFORE the probe window on the same inode is indistinguishable from
340
+ * untouched history and resumes — the same local trust extended to the
341
+ * source files themselves (design section e). The reducer/collector payloads
342
+ * are opaque to the store and re-validated by the loader on every resume
343
+ * (fail closed: invalid state is discarded, never partially reused).
344
+ */
345
+ export type LocalAgentStreamCheckpointRecord = {
346
+ /** Append-tolerant identity pin for the checkpointed inode. */
347
+ pin: {
348
+ dev: number;
349
+ ino: number;
350
+ birthtimeMs: number;
351
+ };
352
+ parserVersion: number;
353
+ collectInvocationEvidence: boolean;
354
+ /** Window the stream's invocation collector was created with. */
355
+ sinceIso: string | null;
356
+ /** Bytes consumed through the end of the last complete line. */
357
+ offset: number;
358
+ /** Content proof over the up-to-64 KiB immediately before `offset`. */
359
+ prefixProbe: {
360
+ bytes: number;
361
+ sha256: string;
362
+ };
363
+ /** Privacy-reduced Codex reducer state; raw paths and prompt text never appear. */
364
+ reducerState: unknown;
365
+ /** Invocation collector snapshot when evidence collection was requested. */
366
+ collectorState?: unknown;
367
+ };
368
+ /**
369
+ * Checkpoint section of the private project index; structurally satisfied by
370
+ * `createProjectIndexAdapters`.
371
+ */
372
+ export type LocalAgentStreamCheckpointAdapter = {
373
+ readStreamCheckpoint: (agent: LocalAgentFormatId, pathHash: string) => Promise<LocalAgentStreamCheckpointRecord | undefined>;
374
+ writeStreamCheckpoint: (agent: LocalAgentFormatId, pathHash: string, checkpoint: Readonly<LocalAgentStreamCheckpointRecord>) => Promise<void>;
375
+ deleteStreamCheckpoint: (agent: LocalAgentFormatId, pathHash: string) => Promise<void>;
376
+ };
377
+ export declare const localAgentFinancialParserVersion = 1;
378
+ /**
379
+ * Qualitative parser contract version. Bumped to 2 with the checkpointed
380
+ * streaming path (A4b): entries and checkpoints written by the pre-streaming
381
+ * parser are never reinterpreted — a version mismatch is a miss (entries) or
382
+ * a discard (checkpoints), and the store schema pins this exact literal so
383
+ * both sides fail closed together. Bumped to 4 when Claude Code subagent
384
+ * transcripts gained their own session identity (`subagentId`) and
385
+ * cross-file completion evidence (`subagentCompletions`, from both Task tool
386
+ * results and background task-notifications): entries persisted by the
387
+ * collapsing parser must re-parse rather than silently keep merging subagent
388
+ * runs into their parent session.
389
+ */
390
+ export declare const localAgentQualitativeParserVersion = 4;
391
+ /**
392
+ * Conservative launch defaults for action-capable qualitative evidence.
393
+ * Callers must still inspect `qualitativeCoverage` before deriving a finding:
394
+ * the limits protect responsiveness; they do not turn a partial scan into a
395
+ * representative sample.
396
+ */
397
+ export declare const SAFE_QUALITATIVE_SCAN_POLICY: Readonly<LocalAgentQualitativeScanPolicy>;
131
398
  /**
132
399
  * Options accepted by the financial-only loader. Invocation collection is
133
400
  * intentionally unavailable: this path reads only the evidence needed for a
134
401
  * financial snapshot and transcript-reported plan limits.
135
402
  */
136
- export type LocalAgentFinancialLogOptions = Omit<LocalAgentLogOptions, "collectCodexInvocationEvidence">;
137
- export type LocalAgentLogDiagnosticCode = "directory_missing" | "directory_unreadable" | "file_unreadable" | "malformed_jsonl" | "malformed_session_file" | "unsupported_token_shape";
403
+ export type LocalAgentFinancialLogOptions = Omit<LocalAgentLogOptions, "collectCodexInvocationEvidence" | "qualitativeScan" | "qualitativeIndex" | "ownershipIndex" | "coverageProjectRef">;
404
+ export type LocalAgentLogDiagnosticCode = "directory_missing" | "directory_unreadable" | "file_unreadable" | "malformed_jsonl" | "malformed_session_file" | "unsupported_token_shape" | "qualitative_scan_incomplete" | "qualitative_index_error";
138
405
  export type LocalAgentLogDiagnostic = {
139
406
  agent: LocalAgentCall["agent"];
140
407
  code: LocalAgentLogDiagnosticCode;
@@ -166,6 +433,62 @@ export type LocalAgentSourceScan = {
166
433
  nonFinancialBytesPrefiltered?: number;
167
434
  /** Whether JSON syntax was checked for every line or financial events only. */
168
435
  jsonlValidationCoverage?: "complete" | "financial_events_only";
436
+ /**
437
+ * Coverage for an explicitly bounded qualitative/action scan. Omitted for
438
+ * the legacy unbounded loader and for the financial-only loader.
439
+ */
440
+ qualitativeCoverage?: "complete" | "partial";
441
+ /** Files inside the requested time window considered by the bounded scan. */
442
+ qualitativeFilesEligible?: number;
443
+ /** Eligible files omitted because a configured byte limit would be crossed. */
444
+ qualitativeFilesSkippedForBudget?: number;
445
+ /** Eligible files selected for a complete bounded read. */
446
+ qualitativeFilesSelected?: number;
447
+ /** Selected files that were read and parsed completely. */
448
+ qualitativeFilesReadCompletely?: number;
449
+ /** Eligible files whose evidence is present in the private index (hit or fresh parse). */
450
+ qualitativeFilesIndexed?: number;
451
+ /**
452
+ * Eligible files proven by a bounded header pass to belong to a project
453
+ * other than the requested `coverageProjectRef`. Always zero when no ref
454
+ * was requested: proven ownership never unblocks an unnamed project.
455
+ */
456
+ qualitativeFilesForeignProven?: number;
457
+ /**
458
+ * Eligible files that still block the requested project: not indexed and
459
+ * not proven foreign. A file proven to belong to the requested project
460
+ * itself stays in this count until indexed — it is exactly the oversized
461
+ * relevant transcript that must be parsed, never excluded.
462
+ */
463
+ qualitativeFilesOwnershipUnknown?: number;
464
+ /**
465
+ * Per-project qualitative coverage for the requested project. "indexing"
466
+ * whenever any eligible file's ownership is unknown, any scan-level failure
467
+ * occurred, or a file proven to belong to the requested project is not yet
468
+ * indexed — the honest no-claim state while the index converges.
469
+ */
470
+ qualitativeProjectCoverage?: "complete" | "indexing";
471
+ /** Sum of eligible regular-file sizes observed before reading. */
472
+ qualitativeBytesEligible?: number;
473
+ /** Sum of metadata sizes reserved for selected complete-file reads. */
474
+ qualitativeBytesSelected?: number;
475
+ /** Bytes actually accepted into full-file qualitative parsing. */
476
+ qualitativeBytesRead?: number;
477
+ /** Selected bytes reused from a trusted warm index instead of reread. */
478
+ qualitativeBytesReused?: number;
479
+ /** Bytes consumed by the checkpointed streaming pass this run. */
480
+ qualitativeBytesStreamed?: number;
481
+ /** Oversized files with an in-progress (unconverged) stream this run. */
482
+ qualitativeFilesStreaming?: number;
483
+ qualitativeIndexHits?: number;
484
+ /** Index read/write failures; source parsing falls back to disk. */
485
+ qualitativeIndexErrors?: number;
486
+ /**
487
+ * Emitted calls always come from complete files, even when global source
488
+ * coverage is partial. This lets cohort experiments use exact selected
489
+ * evidence while global/main-driver claims remain gated on coverage.
490
+ */
491
+ qualitativeSelectedEvidence?: "complete_files_only";
169
492
  };
170
493
  export type LocalAgentLogResult = {
171
494
  records: UsageRecord[];
@@ -181,6 +504,20 @@ export type LocalAgentLogResult = {
181
504
  /** Present only when requested; contains counts/basenames, never raw text. */
182
505
  codexInvocationFiles?: ParsedInvocationFile[];
183
506
  };
507
+ /**
508
+ * True only when every requested source was scanned under an explicit bounded
509
+ * policy without omitting an eligible file. Legacy unbounded results return
510
+ * false so an action caller cannot accidentally treat unknown coverage as a
511
+ * complete launch-safe scan.
512
+ */
513
+ export declare function hasCompleteQualitativeCoverage(result: Pick<LocalAgentLogResult, "sourceScans">, agents?: readonly LocalAgentFormatId[]): boolean;
514
+ /**
515
+ * Whether a bounded result contains exact calls from at least one completely
516
+ * parsed selected file for every requested source. This is deliberately
517
+ * weaker than global coverage: it can support a clearly scoped experiment,
518
+ * never a claim about the source's overall/main driver.
519
+ */
520
+ export declare function hasExactSelectedQualitativeEvidence(result: Pick<LocalAgentLogResult, "calls" | "sourceScans">, agents?: readonly LocalAgentFormatId[]): boolean;
184
521
  type TranscriptParseDiagnostic = {
185
522
  code: "malformed_jsonl" | "malformed_session_file" | "unsupported_token_shape";
186
523
  count: number;
@@ -200,6 +537,15 @@ export declare function parseClaudeCodeTranscript(content: string, filePath?: st
200
537
  export declare function parseCodexRollout(content: string, onEntry?: (entry: Record<string, unknown>) => void, onDiagnostic?: TranscriptParseDiagnosticHandler): LocalAgentCall[];
201
538
  /** Scan this machine's agent logs and return aggregated UsageRecords. */
202
539
  export declare function loadLocalAgentUsage(options?: LocalAgentLogOptions): Promise<LocalAgentLogResult>;
540
+ /**
541
+ * Read only formats whose registry contract supports action planning.
542
+ *
543
+ * The launch action loop currently has truthful session/completion semantics
544
+ * for Claude Code and Codex. Keeping this entrypoint registry-driven avoids an
545
+ * unnecessary Gemini history walk and prevents presence-only/experimental
546
+ * formats from silently entering a matched token experiment.
547
+ */
548
+ export declare function loadLocalAgentActionEvidence(options?: LocalAgentLogOptions): Promise<LocalAgentLogResult>;
203
549
  /**
204
550
  * Registry-driven ingestion engine. Exported from this module for registry
205
551
  * contract tests and format modules, but intentionally omitted from the
@@ -225,6 +571,19 @@ export declare function readClaudeCodeFinancialFileForRegistry(context: LocalAge
225
571
  export declare function readCodexFinancialFileForRegistry(context: LocalAgentFormatFinancialFileContext): Promise<LocalAgentCall[]>;
226
572
  /** @internal Runtime hook owned by the Gemini CLI registry entry. */
227
573
  export declare function readGeminiFinancialFileForRegistry(context: LocalAgentFormatFinancialFileContext): Promise<LocalAgentCall[]>;
574
+ /**
575
+ * Fresh bounded header reads allowed per scan. Files beyond the cap simply
576
+ * stay ownership-unknown ("indexing") this run — never claimed either way —
577
+ * and are reached on a later run once earlier probes persist their results.
578
+ */
579
+ export declare const codexHeaderProbesPerScan = 64;
580
+ /**
581
+ * Default per-run byte allowance for the streaming pass. Sized from measured
582
+ * end-to-end throughput (~99 MB/s on the reference machine, QA probe8): 512
583
+ * MiB keeps one slice at roughly 4-5 seconds of wall clock inside the cold
584
+ * budget. Callers with different budgets tune `maxStreamedBytesPerRun`.
585
+ */
586
+ export declare const defaultStreamedBytesPerRun: number;
228
587
  /** Aggregate per-call usage into one UsageRecord per day+agent+model+project. */
229
588
  export declare function aggregateCalls(calls: LocalAgentCall[]): UsageRecord[];
230
589
  /** @internal Registry-aware aggregation used by the extensible ingestion engine. */