@agent-finops/core 0.5.8 → 0.5.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agentInventory.d.ts +11 -14
- package/dist/agentInventory.js +180 -46
- package/dist/analyze.js +165 -113
- package/dist/contextHealth.d.ts +10 -1
- package/dist/contextHealth.js +216 -44
- package/dist/cutList.d.ts +9 -3
- package/dist/cutList.js +99 -41
- package/dist/deadContext.d.ts +8 -4
- package/dist/deadContext.js +83 -26
- package/dist/glance.d.ts +3 -0
- package/dist/glance.js +46 -12
- package/dist/insights.js +53 -30
- package/dist/localAgentLogs.d.ts +44 -2
- package/dist/localAgentLogs.js +305 -77
- package/dist/providerConnectors.js +8 -0
- package/dist/sampleData.js +28 -1
- package/dist/schema.d.ts +83 -7
- package/dist/schema.js +85 -1
- package/dist/toolInvocations.d.ts +47 -18
- package/dist/toolInvocations.js +200 -48
- package/package.json +1 -1
- package/samples/anthropic-usage.csv +4 -4
- package/samples/openai-usage.csv +7 -7
package/dist/schema.d.ts
CHANGED
|
@@ -20,6 +20,37 @@ export declare const spendSourceSchema: z.ZodObject<{
|
|
|
20
20
|
observedFrom: z.ZodString;
|
|
21
21
|
}, z.core.$strip>;
|
|
22
22
|
export type SpendSource = z.infer<typeof spendSourceSchema>;
|
|
23
|
+
/**
|
|
24
|
+
* What one normalized usage record represents.
|
|
25
|
+
*
|
|
26
|
+
* Only `call` and `invocation` are precise enough to support per-workload
|
|
27
|
+
* counterfactuals such as model routing, result caching, Batch API moves, or
|
|
28
|
+
* prompt trimming. The remaining values are still useful financial evidence,
|
|
29
|
+
* but must never be silently treated as individual calls.
|
|
30
|
+
*/
|
|
31
|
+
export declare const usageGranularityValues: readonly ["call", "invocation", "session", "daily_aggregate", "usage_bucket", "billing_bucket", "seat", "user_aggregate"];
|
|
32
|
+
export declare const usageGranularitySchema: z.ZodEnum<{
|
|
33
|
+
call: "call";
|
|
34
|
+
invocation: "invocation";
|
|
35
|
+
session: "session";
|
|
36
|
+
daily_aggregate: "daily_aggregate";
|
|
37
|
+
usage_bucket: "usage_bucket";
|
|
38
|
+
billing_bucket: "billing_bucket";
|
|
39
|
+
seat: "seat";
|
|
40
|
+
user_aggregate: "user_aggregate";
|
|
41
|
+
}>;
|
|
42
|
+
export type UsageGranularity = z.infer<typeof usageGranularitySchema>;
|
|
43
|
+
/**
|
|
44
|
+
* Explicit adapter attestations for workload-specific optimization advice.
|
|
45
|
+
* These fields are intentionally absent by default: an operation label alone
|
|
46
|
+
* does not prove identical inputs, latency tolerance, or downgrade safety.
|
|
47
|
+
*/
|
|
48
|
+
export declare const workloadSemanticsSchema: z.ZodObject<{
|
|
49
|
+
stableInputFingerprint: z.ZodOptional<z.ZodString>;
|
|
50
|
+
batchEligible: z.ZodOptional<z.ZodBoolean>;
|
|
51
|
+
downgradeSafe: z.ZodOptional<z.ZodBoolean>;
|
|
52
|
+
}, z.core.$strict>;
|
|
53
|
+
export type WorkloadSemantics = z.infer<typeof workloadSemanticsSchema>;
|
|
23
54
|
export declare const usageRecordSchema: z.ZodObject<{
|
|
24
55
|
id: z.ZodString;
|
|
25
56
|
timestamp: z.ZodString;
|
|
@@ -54,14 +85,57 @@ export declare const usageRecordSchema: z.ZodObject<{
|
|
|
54
85
|
quantity: z.ZodOptional<z.ZodNumber>;
|
|
55
86
|
agentId: z.ZodOptional<z.ZodString>;
|
|
56
87
|
operation: z.ZodOptional<z.ZodString>;
|
|
88
|
+
usageGranularity: z.ZodOptional<z.ZodEnum<{
|
|
89
|
+
call: "call";
|
|
90
|
+
invocation: "invocation";
|
|
91
|
+
session: "session";
|
|
92
|
+
daily_aggregate: "daily_aggregate";
|
|
93
|
+
usage_bucket: "usage_bucket";
|
|
94
|
+
billing_bucket: "billing_bucket";
|
|
95
|
+
seat: "seat";
|
|
96
|
+
user_aggregate: "user_aggregate";
|
|
97
|
+
}>>;
|
|
98
|
+
workloadSemantics: z.ZodOptional<z.ZodObject<{
|
|
99
|
+
stableInputFingerprint: z.ZodOptional<z.ZodString>;
|
|
100
|
+
batchEligible: z.ZodOptional<z.ZodBoolean>;
|
|
101
|
+
downgradeSafe: z.ZodOptional<z.ZodBoolean>;
|
|
102
|
+
}, z.core.$strict>>;
|
|
57
103
|
}, z.core.$strip>;
|
|
58
104
|
export type UsageRecord = z.infer<typeof usageRecordSchema>;
|
|
105
|
+
/**
|
|
106
|
+
* True only when a record can honestly ground a modeled workload change.
|
|
107
|
+
*
|
|
108
|
+
* A provider billing row, usage bucket, seat, user total, or unlabelled legacy
|
|
109
|
+
* row may describe real spend, but it does not prove that one row was one
|
|
110
|
+
* optimizable call. Requiring both explicit call/invocation provenance and a
|
|
111
|
+
* named operation keeps aggregate connector data in accounting views without
|
|
112
|
+
* manufacturing per-call savings advice from it.
|
|
113
|
+
*/
|
|
114
|
+
export declare function hasModeledWorkloadEvidence(record: UsageRecord): boolean;
|
|
115
|
+
/** Explicit one-call / one-model-invocation provenance, never inferred. */
|
|
116
|
+
export declare function hasCallLevelProvenance(record: UsageRecord): boolean;
|
|
117
|
+
/** A positive priced observation from which a dollar counterfactual can be modeled. */
|
|
118
|
+
export declare function hasPricedEvidence(record: UsageRecord): boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Recognize the bundled demo records written by releases that predate the
|
|
121
|
+
* persisted `mode` field. The marker is deliberately narrow: every record
|
|
122
|
+
* must come from the shipped sample CSV and use a sample source identifier.
|
|
123
|
+
* This lets newer clients recover the demo boundary without guessing that an
|
|
124
|
+
* arbitrary unlabeled state is real local or connected evidence.
|
|
125
|
+
*/
|
|
126
|
+
export declare function isBundledSampleUsage(records: UsageRecord[]): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Stable financial cohort for period-over-period comparisons. Missing shape
|
|
129
|
+
* provenance returns `undefined`; unknown rows must not be pooled into a fake
|
|
130
|
+
* comparable series.
|
|
131
|
+
*/
|
|
132
|
+
export declare function spendComparisonKey(record: UsageRecord): string | undefined;
|
|
59
133
|
export declare const attributionCandidateSchema: z.ZodObject<{
|
|
60
134
|
entityType: z.ZodEnum<{
|
|
61
|
-
user: "user";
|
|
62
|
-
project: "project";
|
|
63
135
|
client: "client";
|
|
136
|
+
project: "project";
|
|
64
137
|
agent: "agent";
|
|
138
|
+
user: "user";
|
|
65
139
|
workspace: "workspace";
|
|
66
140
|
api_key: "api_key";
|
|
67
141
|
}>;
|
|
@@ -74,10 +148,10 @@ export declare const attributionMappingSchema: z.ZodObject<{
|
|
|
74
148
|
usageRecordId: z.ZodString;
|
|
75
149
|
candidates: z.ZodArray<z.ZodObject<{
|
|
76
150
|
entityType: z.ZodEnum<{
|
|
77
|
-
user: "user";
|
|
78
|
-
project: "project";
|
|
79
151
|
client: "client";
|
|
152
|
+
project: "project";
|
|
80
153
|
agent: "agent";
|
|
154
|
+
user: "user";
|
|
81
155
|
workspace: "workspace";
|
|
82
156
|
api_key: "api_key";
|
|
83
157
|
}>;
|
|
@@ -87,10 +161,10 @@ export declare const attributionMappingSchema: z.ZodObject<{
|
|
|
87
161
|
}, z.core.$strip>>;
|
|
88
162
|
selected: z.ZodOptional<z.ZodObject<{
|
|
89
163
|
entityType: z.ZodEnum<{
|
|
90
|
-
user: "user";
|
|
91
|
-
project: "project";
|
|
92
164
|
client: "client";
|
|
165
|
+
project: "project";
|
|
93
166
|
agent: "agent";
|
|
167
|
+
user: "user";
|
|
94
168
|
workspace: "workspace";
|
|
95
169
|
api_key: "api_key";
|
|
96
170
|
}>;
|
|
@@ -99,10 +173,10 @@ export declare const attributionMappingSchema: z.ZodObject<{
|
|
|
99
173
|
evidence: z.ZodArray<z.ZodString>;
|
|
100
174
|
}, z.core.$strip>>;
|
|
101
175
|
status: z.ZodEnum<{
|
|
102
|
-
unmapped: "unmapped";
|
|
103
176
|
auto_mapped: "auto_mapped";
|
|
104
177
|
needs_confirmation: "needs_confirmation";
|
|
105
178
|
needs_question: "needs_question";
|
|
179
|
+
unmapped: "unmapped";
|
|
106
180
|
}>;
|
|
107
181
|
evidence: z.ZodArray<z.ZodString>;
|
|
108
182
|
}, z.core.$strip>;
|
|
@@ -125,6 +199,7 @@ export declare const spendAnomalySchema: z.ZodObject<{
|
|
|
125
199
|
week_over_week_spike: "week_over_week_spike";
|
|
126
200
|
}>;
|
|
127
201
|
key: z.ZodString;
|
|
202
|
+
comparisonKey: z.ZodOptional<z.ZodString>;
|
|
128
203
|
previousAmountUsd: z.ZodNumber;
|
|
129
204
|
currentAmountUsd: z.ZodNumber;
|
|
130
205
|
multiplier: z.ZodNumber;
|
|
@@ -355,6 +430,7 @@ export declare const spendSummarySchema: z.ZodObject<{
|
|
|
355
430
|
week_over_week_spike: "week_over_week_spike";
|
|
356
431
|
}>;
|
|
357
432
|
key: z.ZodString;
|
|
433
|
+
comparisonKey: z.ZodOptional<z.ZodString>;
|
|
358
434
|
previousAmountUsd: z.ZodNumber;
|
|
359
435
|
currentAmountUsd: z.ZodNumber;
|
|
360
436
|
multiplier: z.ZodNumber;
|
package/dist/schema.js
CHANGED
|
@@ -13,6 +13,35 @@ export const spendSourceSchema = z.object({
|
|
|
13
13
|
confidence: costConfidenceSchema,
|
|
14
14
|
observedFrom: z.string().min(1)
|
|
15
15
|
});
|
|
16
|
+
/**
|
|
17
|
+
* What one normalized usage record represents.
|
|
18
|
+
*
|
|
19
|
+
* Only `call` and `invocation` are precise enough to support per-workload
|
|
20
|
+
* counterfactuals such as model routing, result caching, Batch API moves, or
|
|
21
|
+
* prompt trimming. The remaining values are still useful financial evidence,
|
|
22
|
+
* but must never be silently treated as individual calls.
|
|
23
|
+
*/
|
|
24
|
+
export const usageGranularityValues = [
|
|
25
|
+
"call",
|
|
26
|
+
"invocation",
|
|
27
|
+
"session",
|
|
28
|
+
"daily_aggregate",
|
|
29
|
+
"usage_bucket",
|
|
30
|
+
"billing_bucket",
|
|
31
|
+
"seat",
|
|
32
|
+
"user_aggregate"
|
|
33
|
+
];
|
|
34
|
+
export const usageGranularitySchema = z.enum(usageGranularityValues);
|
|
35
|
+
/**
|
|
36
|
+
* Explicit adapter attestations for workload-specific optimization advice.
|
|
37
|
+
* These fields are intentionally absent by default: an operation label alone
|
|
38
|
+
* does not prove identical inputs, latency tolerance, or downgrade safety.
|
|
39
|
+
*/
|
|
40
|
+
export const workloadSemanticsSchema = z.object({
|
|
41
|
+
stableInputFingerprint: z.string().min(8).max(128).regex(/^[a-zA-Z0-9:_-]+$/).optional(),
|
|
42
|
+
batchEligible: z.boolean().optional(),
|
|
43
|
+
downgradeSafe: z.boolean().optional()
|
|
44
|
+
}).strict();
|
|
16
45
|
export const usageRecordSchema = z.object({
|
|
17
46
|
id: z.string().min(1),
|
|
18
47
|
timestamp: z.string().datetime({ offset: true }),
|
|
@@ -30,7 +59,9 @@ export const usageRecordSchema = z.object({
|
|
|
30
59
|
providerCostType: z.string().min(1).optional(),
|
|
31
60
|
quantity: z.number().nonnegative().optional(),
|
|
32
61
|
agentId: z.string().min(1).optional(),
|
|
33
|
-
operation: z.string().min(1).optional()
|
|
62
|
+
operation: z.string().min(1).optional(),
|
|
63
|
+
usageGranularity: usageGranularitySchema.optional(),
|
|
64
|
+
workloadSemantics: workloadSemanticsSchema.optional()
|
|
34
65
|
}).superRefine((record, context) => {
|
|
35
66
|
if (record.costConfidence === "missing" && record.amountUsd !== null) {
|
|
36
67
|
context.addIssue({
|
|
@@ -47,6 +78,58 @@ export const usageRecordSchema = z.object({
|
|
|
47
78
|
});
|
|
48
79
|
}
|
|
49
80
|
});
|
|
81
|
+
/**
|
|
82
|
+
* True only when a record can honestly ground a modeled workload change.
|
|
83
|
+
*
|
|
84
|
+
* A provider billing row, usage bucket, seat, user total, or unlabelled legacy
|
|
85
|
+
* row may describe real spend, but it does not prove that one row was one
|
|
86
|
+
* optimizable call. Requiring both explicit call/invocation provenance and a
|
|
87
|
+
* named operation keeps aggregate connector data in accounting views without
|
|
88
|
+
* manufacturing per-call savings advice from it.
|
|
89
|
+
*/
|
|
90
|
+
export function hasModeledWorkloadEvidence(record) {
|
|
91
|
+
return (hasCallLevelProvenance(record) &&
|
|
92
|
+
typeof record.operation === "string" &&
|
|
93
|
+
record.operation.trim().length > 0 &&
|
|
94
|
+
hasPricedEvidence(record));
|
|
95
|
+
}
|
|
96
|
+
/** Explicit one-call / one-model-invocation provenance, never inferred. */
|
|
97
|
+
export function hasCallLevelProvenance(record) {
|
|
98
|
+
return record.usageGranularity === "call" || record.usageGranularity === "invocation";
|
|
99
|
+
}
|
|
100
|
+
/** A positive priced observation from which a dollar counterfactual can be modeled. */
|
|
101
|
+
export function hasPricedEvidence(record) {
|
|
102
|
+
return (typeof record.amountUsd === "number" &&
|
|
103
|
+
record.amountUsd > 0 &&
|
|
104
|
+
record.costConfidence !== "missing");
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Recognize the bundled demo records written by releases that predate the
|
|
108
|
+
* persisted `mode` field. The marker is deliberately narrow: every record
|
|
109
|
+
* must come from the shipped sample CSV and use a sample source identifier.
|
|
110
|
+
* This lets newer clients recover the demo boundary without guessing that an
|
|
111
|
+
* arbitrary unlabeled state is real local or connected evidence.
|
|
112
|
+
*/
|
|
113
|
+
export function isBundledSampleUsage(records) {
|
|
114
|
+
return records.length > 0 && records.every((record) => record.source.observedFrom === "sample_csv" &&
|
|
115
|
+
/(?:^|-)sample$/i.test(record.source.id));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Stable financial cohort for period-over-period comparisons. Missing shape
|
|
119
|
+
* provenance returns `undefined`; unknown rows must not be pooled into a fake
|
|
120
|
+
* comparable series.
|
|
121
|
+
*/
|
|
122
|
+
export function spendComparisonKey(record) {
|
|
123
|
+
if (!record.providerCostType || !record.usageGranularity) {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
return [
|
|
127
|
+
record.source.id,
|
|
128
|
+
record.source.provider,
|
|
129
|
+
record.providerCostType,
|
|
130
|
+
record.usageGranularity
|
|
131
|
+
].map((part) => encodeURIComponent(part)).join("|");
|
|
132
|
+
}
|
|
50
133
|
export const attributionCandidateSchema = z.object({
|
|
51
134
|
entityType: z.enum(["client", "project", "agent", "user", "workspace", "api_key"]),
|
|
52
135
|
entityId: z.string().min(1),
|
|
@@ -69,6 +152,7 @@ export const spendBreakdownEntrySchema = z.object({
|
|
|
69
152
|
export const spendAnomalySchema = z.object({
|
|
70
153
|
kind: z.enum(["day_over_day_spike", "week_over_week_spike"]),
|
|
71
154
|
key: z.string().min(1),
|
|
155
|
+
comparisonKey: z.string().min(1).optional(),
|
|
72
156
|
previousAmountUsd: z.number().nonnegative(),
|
|
73
157
|
currentAmountUsd: z.number().nonnegative(),
|
|
74
158
|
multiplier: z.number().nonnegative(),
|
|
@@ -23,6 +23,20 @@ export type ToolInvocationCount = {
|
|
|
23
23
|
name: string;
|
|
24
24
|
count: number;
|
|
25
25
|
};
|
|
26
|
+
export type HostInvocationEvidence = {
|
|
27
|
+
sessions: number;
|
|
28
|
+
totalAssistantTurns: number;
|
|
29
|
+
sessionTurnCounts: number[];
|
|
30
|
+
invokedMcpTools: string[];
|
|
31
|
+
invokedSkills: string[];
|
|
32
|
+
invokedSubagents: string[];
|
|
33
|
+
invokedCommands: string[];
|
|
34
|
+
};
|
|
35
|
+
export type NestedSessionMetadata = {
|
|
36
|
+
sessionId?: string;
|
|
37
|
+
isSubagent: boolean;
|
|
38
|
+
parentSessionId?: string;
|
|
39
|
+
};
|
|
26
40
|
export type SessionContextSignal = {
|
|
27
41
|
agent: "claude-code" | "codex";
|
|
28
42
|
sessionId?: string;
|
|
@@ -38,6 +52,8 @@ export type SessionContextSignal = {
|
|
|
38
52
|
/** Whether this transcript is a Claude sidechain/subagent transcript. */
|
|
39
53
|
isSubagent: boolean;
|
|
40
54
|
parentSessionId?: string;
|
|
55
|
+
/** Embedded/fork-history metadata, kept separate from this file's root identity. */
|
|
56
|
+
nestedSessions?: NestedSessionMetadata[];
|
|
41
57
|
/**
|
|
42
58
|
* Coverage is intentionally narrow. Shell commands are not parsed as file
|
|
43
59
|
* reads because doing so would turn arbitrary command text into a heuristic.
|
|
@@ -55,7 +71,7 @@ export type InvocationSummary = {
|
|
|
55
71
|
invokedSubagents: string[];
|
|
56
72
|
/** distinct slash-command names invoked, if detectable; else [] */
|
|
57
73
|
invokedCommands: string[];
|
|
58
|
-
/**
|
|
74
|
+
/** transcript files with at least one assistant turn in the selected window */
|
|
59
75
|
sessions: number;
|
|
60
76
|
/** total assistant turns across all sessions (post-dedupe) */
|
|
61
77
|
totalAssistantTurns: number;
|
|
@@ -66,9 +82,24 @@ export type InvocationSummary = {
|
|
|
66
82
|
claudeCode: number;
|
|
67
83
|
codex: number;
|
|
68
84
|
};
|
|
85
|
+
/** Host-isolated coverage and matchable invocation evidence. */
|
|
86
|
+
byHost?: {
|
|
87
|
+
"claude-code": HostInvocationEvidence;
|
|
88
|
+
codex: HostInvocationEvidence;
|
|
89
|
+
};
|
|
69
90
|
/** Per-transcript compaction/read evidence used by Context Health. */
|
|
70
91
|
sessionSignals?: SessionContextSignal[];
|
|
71
92
|
};
|
|
93
|
+
/** Privacy-safe per-file result; it never contains prompt text or local paths. */
|
|
94
|
+
export type ParsedInvocationFile = {
|
|
95
|
+
invocations: ToolInvocationCount[];
|
|
96
|
+
invokedMcpTools: string[];
|
|
97
|
+
invokedSkills: string[];
|
|
98
|
+
invokedSubagents: string[];
|
|
99
|
+
invokedCommands: string[];
|
|
100
|
+
assistantTurns: number;
|
|
101
|
+
contextSignal: SessionContextSignal;
|
|
102
|
+
};
|
|
72
103
|
export type ToolInvocationOptions = {
|
|
73
104
|
/** default: join(homedir(), ".claude", "projects") */
|
|
74
105
|
claudeProjectsDir?: string;
|
|
@@ -76,26 +107,24 @@ export type ToolInvocationOptions = {
|
|
|
76
107
|
codexSessionsDir?: string;
|
|
77
108
|
/** optional: only count turns at/after this time */
|
|
78
109
|
sinceIso?: string;
|
|
110
|
+
/**
|
|
111
|
+
* Fresh Codex summaries collected while usage parsed the same files. Supplying
|
|
112
|
+
* these skips a second rollout read/JSON parse in the same command. The
|
|
113
|
+
* summaries contain counts and basenames only, never raw transcript text.
|
|
114
|
+
*/
|
|
115
|
+
codexInvocationFiles?: ParsedInvocationFile[];
|
|
79
116
|
};
|
|
80
117
|
/** Parse ONE transcript's content. Exported for tests. Returns the per-file pieces the aggregator needs. */
|
|
81
|
-
export declare function parseClaudeCodeInvocations(content: string, sinceMs?: number):
|
|
82
|
-
invocations: ToolInvocationCount[];
|
|
83
|
-
invokedMcpTools: string[];
|
|
84
|
-
invokedSkills: string[];
|
|
85
|
-
invokedSubagents: string[];
|
|
86
|
-
invokedCommands: string[];
|
|
87
|
-
assistantTurns: number;
|
|
88
|
-
contextSignal: SessionContextSignal;
|
|
89
|
-
};
|
|
118
|
+
export declare function parseClaudeCodeInvocations(content: string, sinceMs?: number): ParsedInvocationFile;
|
|
90
119
|
/** Parse ONE Codex rollout's tool/skill/subagent invocations. */
|
|
91
|
-
export declare function parseCodexInvocations(content: string, sinceMs?: number):
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
120
|
+
export declare function parseCodexInvocations(content: string, sinceMs?: number): ParsedInvocationFile;
|
|
121
|
+
/**
|
|
122
|
+
* Stateful Codex invocation parser used to share localAgentLogs' JSONL pass.
|
|
123
|
+
* One collector is created per rollout file and discarded after `finish()`.
|
|
124
|
+
*/
|
|
125
|
+
export declare function createCodexInvocationCollector(sinceMs?: number): {
|
|
126
|
+
consume: (entry: Record<string, unknown>) => void;
|
|
127
|
+
finish: () => ParsedInvocationFile;
|
|
99
128
|
};
|
|
100
129
|
/** Scan this machine's Claude Code + Codex transcripts and aggregate invocations. */
|
|
101
130
|
export declare function loadToolInvocations(options?: ToolInvocationOptions): Promise<InvocationSummary>;
|