@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
@@ -100,6 +100,11 @@ export type ParsedInvocationFile = {
100
100
  assistantTurns: number;
101
101
  contextSignal: SessionContextSignal;
102
102
  };
103
+ /** Private-index proof used to narrow one aggregated Codex window exactly. */
104
+ export type ParsedInvocationWindowProof = {
105
+ earliestCountedAt?: string;
106
+ allCountedEventsTimestamped: boolean;
107
+ };
103
108
  export type ToolInvocationOptions = {
104
109
  /** default: join(homedir(), ".claude", "projects") */
105
110
  claudeProjectsDir?: string;
@@ -118,13 +123,47 @@ export type ToolInvocationOptions = {
118
123
  export declare function parseClaudeCodeInvocations(content: string, sinceMs?: number): ParsedInvocationFile;
119
124
  /** Parse ONE Codex rollout's tool/skill/subagent invocations. */
120
125
  export declare function parseCodexInvocations(content: string, sinceMs?: number): ParsedInvocationFile;
126
+ /**
127
+ * Serializable snapshot of one Codex invocation collector, used by the
128
+ * checkpointed streaming path to carry aggregation across bounded runs. It
129
+ * contains only what the finished summary itself persists — tool/skill/
130
+ * command names, file basenames, opaque session ids, counters and window
131
+ * proof timestamps — never raw transcript text or absolute paths.
132
+ */
133
+ export type CodexInvocationCollectorSnapshot = {
134
+ counts: Array<[string, number]>;
135
+ mcpTools: string[];
136
+ skills: string[];
137
+ subagents: string[];
138
+ commands: string[];
139
+ fileReads: Array<[string, number]>;
140
+ assistantTurns: number;
141
+ sessionId?: string;
142
+ rootSessionMetaSeen: boolean;
143
+ rootStartedAtMs?: number;
144
+ rootTaskStarted: boolean;
145
+ lastActivityAt?: string;
146
+ compactionEvents: number;
147
+ isSubagent: boolean;
148
+ parentSessionId?: string;
149
+ nestedSessions: Array<[string, NestedSessionMetadata]>;
150
+ earliestCountedMs?: number;
151
+ allCountedEventsTimestamped: boolean;
152
+ };
153
+ /** Fail-closed structural check for a restored collector snapshot. */
154
+ export declare function isCodexInvocationCollectorSnapshot(value: unknown): value is CodexInvocationCollectorSnapshot;
121
155
  /**
122
156
  * Stateful Codex invocation parser used to share localAgentLogs' JSONL pass.
123
157
  * One collector is created per rollout file and discarded after `finish()`.
158
+ * A checkpointed stream restores a prior run's snapshot; the window (sinceMs)
159
+ * must be the one the snapshot was created under — the caller pins it in the
160
+ * checkpoint envelope.
124
161
  */
125
- export declare function createCodexInvocationCollector(sinceMs?: number): {
162
+ export declare function createCodexInvocationCollector(sinceMs?: number, restored?: CodexInvocationCollectorSnapshot): {
126
163
  consume: (entry: Record<string, unknown>) => void;
127
164
  finish: () => ParsedInvocationFile;
165
+ windowProof: () => ParsedInvocationWindowProof;
166
+ snapshot: () => CodexInvocationCollectorSnapshot;
128
167
  };
129
168
  /** Scan this machine's Claude Code + Codex transcripts and aggregate invocations. */
130
169
  export declare function loadToolInvocations(options?: ToolInvocationOptions): Promise<InvocationSummary>;
@@ -132,27 +132,64 @@ export function parseCodexInvocations(content, sinceMs) {
132
132
  }
133
133
  return collector.finish();
134
134
  }
135
+ /** Fail-closed structural check for a restored collector snapshot. */
136
+ export function isCodexInvocationCollectorSnapshot(value) {
137
+ if (typeof value !== "object" || value === null || Array.isArray(value))
138
+ return false;
139
+ const record = value;
140
+ const stringNumberPairs = (input) => (Array.isArray(input) && input.every((pair) => (Array.isArray(pair) && pair.length === 2 &&
141
+ typeof pair[0] === "string" &&
142
+ Number.isSafeInteger(pair[1]) && Number(pair[1]) >= 0)));
143
+ const stringArray = (input) => (Array.isArray(input) && input.every((item) => typeof item === "string"));
144
+ const optionalString = (input) => (input === undefined || typeof input === "string");
145
+ const optionalFinite = (input) => (input === undefined || typeof input === "number" && Number.isFinite(input));
146
+ return stringNumberPairs(record.counts) &&
147
+ stringArray(record.mcpTools) &&
148
+ stringArray(record.skills) &&
149
+ stringArray(record.subagents) &&
150
+ stringArray(record.commands) &&
151
+ stringNumberPairs(record.fileReads) &&
152
+ Number.isSafeInteger(record.assistantTurns) && Number(record.assistantTurns) >= 0 &&
153
+ optionalString(record.sessionId) &&
154
+ typeof record.rootSessionMetaSeen === "boolean" &&
155
+ optionalFinite(record.rootStartedAtMs) &&
156
+ typeof record.rootTaskStarted === "boolean" &&
157
+ optionalString(record.lastActivityAt) &&
158
+ Number.isSafeInteger(record.compactionEvents) && Number(record.compactionEvents) >= 0 &&
159
+ typeof record.isSubagent === "boolean" &&
160
+ optionalString(record.parentSessionId) &&
161
+ Array.isArray(record.nestedSessions) && record.nestedSessions.every((pair) => (Array.isArray(pair) && pair.length === 2 && typeof pair[0] === "string" &&
162
+ typeof pair[1] === "object" && pair[1] !== null &&
163
+ typeof pair[1].isSubagent === "boolean")) &&
164
+ optionalFinite(record.earliestCountedMs) &&
165
+ typeof record.allCountedEventsTimestamped === "boolean";
166
+ }
135
167
  /**
136
168
  * Stateful Codex invocation parser used to share localAgentLogs' JSONL pass.
137
169
  * One collector is created per rollout file and discarded after `finish()`.
170
+ * A checkpointed stream restores a prior run's snapshot; the window (sinceMs)
171
+ * must be the one the snapshot was created under — the caller pins it in the
172
+ * checkpoint envelope.
138
173
  */
139
- export function createCodexInvocationCollector(sinceMs) {
140
- const counts = new Map();
141
- const mcpTools = new Set();
142
- const skills = new Set();
143
- const subagents = new Set();
144
- const commands = new Set();
145
- const fileReads = new Map();
146
- let assistantTurns = 0;
147
- let sessionId;
148
- let rootSessionMetaSeen = false;
149
- let rootStartedAtMs;
150
- let rootTaskStarted = false;
151
- let lastActivityAt;
152
- let compactionEvents = 0;
153
- let isSubagent = false;
154
- let parentSessionId;
155
- const nestedSessions = new Map();
174
+ export function createCodexInvocationCollector(sinceMs, restored) {
175
+ const counts = new Map(restored?.counts ?? []);
176
+ const mcpTools = new Set(restored?.mcpTools ?? []);
177
+ const skills = new Set(restored?.skills ?? []);
178
+ const subagents = new Set(restored?.subagents ?? []);
179
+ const commands = new Set(restored?.commands ?? []);
180
+ const fileReads = new Map(restored?.fileReads ?? []);
181
+ let assistantTurns = restored?.assistantTurns ?? 0;
182
+ let sessionId = restored?.sessionId;
183
+ let rootSessionMetaSeen = restored?.rootSessionMetaSeen ?? false;
184
+ let rootStartedAtMs = restored?.rootStartedAtMs;
185
+ let rootTaskStarted = restored?.rootTaskStarted ?? false;
186
+ let lastActivityAt = restored?.lastActivityAt;
187
+ let compactionEvents = restored?.compactionEvents ?? 0;
188
+ let isSubagent = restored?.isSubagent ?? false;
189
+ let parentSessionId = restored?.parentSessionId;
190
+ const nestedSessions = new Map(restored?.nestedSessions ?? []);
191
+ let earliestCountedMs = restored?.earliestCountedMs;
192
+ let allCountedEventsTimestamped = restored?.allCountedEventsTimestamped ?? true;
156
193
  const resetObservedEvidence = () => {
157
194
  counts.clear();
158
195
  mcpTools.clear();
@@ -162,6 +199,18 @@ export function createCodexInvocationCollector(sinceMs) {
162
199
  fileReads.clear();
163
200
  assistantTurns = 0;
164
201
  compactionEvents = 0;
202
+ earliestCountedMs = undefined;
203
+ allCountedEventsTimestamped = true;
204
+ };
205
+ const recordCountedTimestamp = (entry) => {
206
+ const timestamp = Date.parse(stringOf(entry.timestamp) ?? "");
207
+ if (!Number.isFinite(timestamp)) {
208
+ allCountedEventsTimestamped = false;
209
+ return;
210
+ }
211
+ earliestCountedMs = earliestCountedMs === undefined
212
+ ? timestamp
213
+ : Math.min(earliestCountedMs, timestamp);
165
214
  };
166
215
  const consume = (entry) => {
167
216
  const payload = isRecord(entry.payload) ? entry.payload : undefined;
@@ -211,18 +260,23 @@ export function createCodexInvocationCollector(sinceMs) {
211
260
  return;
212
261
  // Codex writes both a top-level `compacted` entry and a separate
213
262
  // `context_compacted` event for one compaction. Count only the former.
214
- if (entry.type === "compacted")
263
+ if (entry.type === "compacted") {
215
264
  compactionEvents += 1;
265
+ recordCountedTimestamp(entry);
266
+ }
216
267
  // Codex emits one turn_context per model turn.
217
268
  if (entry.type === "turn_context") {
218
269
  assistantTurns += 1;
270
+ recordCountedTimestamp(entry);
219
271
  return;
220
272
  }
221
273
  if (payload.type === "message" && payload.role === "user") {
222
274
  for (const text of codexTextValues(payload.content)) {
223
275
  const command = /^\s*\/([A-Za-z0-9:_-]+)/.exec(text)?.[1];
224
- if (command)
276
+ if (command) {
225
277
  commands.add(command);
278
+ recordCountedTimestamp(entry);
279
+ }
226
280
  }
227
281
  return;
228
282
  }
@@ -233,6 +287,7 @@ export function createCodexInvocationCollector(sinceMs) {
233
287
  if (!name)
234
288
  return;
235
289
  counts.set(name, (counts.get(name) ?? 0) + 1);
290
+ recordCountedTimestamp(entry);
236
291
  const input = codexToolInput(payload);
237
292
  if (name.startsWith("mcp__")) {
238
293
  mcpTools.add(name);
@@ -280,7 +335,33 @@ export function createCodexInvocationCollector(sinceMs) {
280
335
  })
281
336
  };
282
337
  };
283
- return { consume, finish };
338
+ const windowProof = () => ({
339
+ ...(earliestCountedMs === undefined
340
+ ? {}
341
+ : { earliestCountedAt: new Date(earliestCountedMs).toISOString() }),
342
+ allCountedEventsTimestamped
343
+ });
344
+ const snapshot = () => ({
345
+ counts: [...counts.entries()],
346
+ mcpTools: [...mcpTools],
347
+ skills: [...skills],
348
+ subagents: [...subagents],
349
+ commands: [...commands],
350
+ fileReads: [...fileReads.entries()],
351
+ assistantTurns,
352
+ ...(sessionId !== undefined ? { sessionId } : {}),
353
+ rootSessionMetaSeen,
354
+ ...(rootStartedAtMs !== undefined ? { rootStartedAtMs } : {}),
355
+ rootTaskStarted,
356
+ ...(lastActivityAt !== undefined ? { lastActivityAt } : {}),
357
+ compactionEvents,
358
+ isSubagent,
359
+ ...(parentSessionId !== undefined ? { parentSessionId } : {}),
360
+ nestedSessions: [...nestedSessions.entries()],
361
+ ...(earliestCountedMs !== undefined ? { earliestCountedMs } : {}),
362
+ allCountedEventsTimestamped
363
+ });
364
+ return { consume, finish, windowProof, snapshot };
284
365
  }
285
366
  /** Scan this machine's Claude Code + Codex transcripts and aggregate invocations. */
286
367
  export async function loadToolInvocations(options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-finops/core",
3
- "version": "0.8.1",
3
+ "version": "0.9.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",