@agent-finops/core 0.5.7 → 0.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/analyze.js +1 -1
- package/dist/attribution.js +9 -6
- package/dist/localAgentLogs.js +28 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @agent-finops/core
|
|
2
2
|
|
|
3
|
-
The canonical local-first
|
|
3
|
+
The canonical local-first evidence and decision engine for
|
|
4
4
|
[aibill](https://github.com/futurastudio/ai-spend-agent): Claude Code/Codex
|
|
5
5
|
activity ingestion, provider cost semantics, attribution, provenance, runway,
|
|
6
6
|
Context Health, and the shared Glance contract.
|
|
@@ -8,6 +8,10 @@ Context Health, and the shared Glance contract.
|
|
|
8
8
|
Most users should run `npx aibill`. This package is for integrations that
|
|
9
9
|
need the same evidence-labeled calculations as the CLI and MCP server.
|
|
10
10
|
|
|
11
|
+
This is the open foundation for aibill's financial-accountability mission. It
|
|
12
|
+
does not yet implement company-wide ownership, accepted outcomes, approvals,
|
|
13
|
+
invoice reconciliation, or ROI.
|
|
14
|
+
|
|
11
15
|
Local API-equivalent estimates, subscription context, and official
|
|
12
16
|
provider-reported cost are separate concepts and must not be added together.
|
|
13
17
|
|
package/dist/analyze.js
CHANGED
|
@@ -147,7 +147,7 @@ export function generateRecommendations(records) {
|
|
|
147
147
|
id: "model-downgrade",
|
|
148
148
|
title: "Review expensive model workloads for downgrade candidates",
|
|
149
149
|
rationale: `${topModel.key} is the largest cost driver in the current local sample.`,
|
|
150
|
-
whyItMatters: "Premium model usage tends to become invisible once agents are running in the background.
|
|
150
|
+
whyItMatters: "Premium model usage tends to become invisible once agents are running in the background. Spend owners need a clear rule for which jobs deserve the expensive model.",
|
|
151
151
|
nextAction: `Audit the top ${topModel.key} operations and move low-risk summarization, extraction, and draft work to a cheaper model tier first.`,
|
|
152
152
|
priority: "high",
|
|
153
153
|
estimatedImpactUsd: roundMoney(topModel.amountUsd * impactRatios.modelDowngrade),
|
package/dist/attribution.js
CHANGED
|
@@ -26,7 +26,7 @@ export function attributeUsageRecords(records) {
|
|
|
26
26
|
}
|
|
27
27
|
function buildCandidates(record) {
|
|
28
28
|
const candidates = [];
|
|
29
|
-
if (record.projectId) {
|
|
29
|
+
if (record.projectId && !isPlaceholderEntityId(record.projectId)) {
|
|
30
30
|
candidates.push({
|
|
31
31
|
entityType: "project",
|
|
32
32
|
entityId: record.projectId,
|
|
@@ -34,7 +34,7 @@ function buildCandidates(record) {
|
|
|
34
34
|
evidence: [`usage record includes projectId ${record.projectId}`]
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
|
-
if (record.clientId) {
|
|
37
|
+
if (record.clientId && !isPlaceholderEntityId(record.clientId)) {
|
|
38
38
|
candidates.push({
|
|
39
39
|
entityType: "client",
|
|
40
40
|
entityId: record.clientId,
|
|
@@ -42,7 +42,7 @@ function buildCandidates(record) {
|
|
|
42
42
|
evidence: [`usage record includes clientId ${record.clientId}`]
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
-
if (record.agentId) {
|
|
45
|
+
if (record.agentId && !isPlaceholderEntityId(record.agentId)) {
|
|
46
46
|
candidates.push({
|
|
47
47
|
entityType: "agent",
|
|
48
48
|
entityId: record.agentId,
|
|
@@ -50,7 +50,7 @@ function buildCandidates(record) {
|
|
|
50
50
|
evidence: [`usage record includes agentId ${record.agentId}`]
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
|
-
if (record.userId) {
|
|
53
|
+
if (record.userId && !isPlaceholderEntityId(record.userId)) {
|
|
54
54
|
candidates.push({
|
|
55
55
|
entityType: "user",
|
|
56
56
|
entityId: record.userId,
|
|
@@ -58,7 +58,7 @@ function buildCandidates(record) {
|
|
|
58
58
|
evidence: [`usage record includes userId ${record.userId}`]
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
|
-
if (record.workspaceId) {
|
|
61
|
+
if (record.workspaceId && !isPlaceholderEntityId(record.workspaceId)) {
|
|
62
62
|
candidates.push({
|
|
63
63
|
entityType: "workspace",
|
|
64
64
|
entityId: record.workspaceId,
|
|
@@ -66,7 +66,7 @@ function buildCandidates(record) {
|
|
|
66
66
|
evidence: [`usage record includes workspaceId ${record.workspaceId}`]
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
|
-
if (record.apiKeyId) {
|
|
69
|
+
if (record.apiKeyId && !isPlaceholderEntityId(record.apiKeyId)) {
|
|
70
70
|
candidates.push({
|
|
71
71
|
entityType: "api_key",
|
|
72
72
|
entityId: record.apiKeyId,
|
|
@@ -103,6 +103,9 @@ function buildCandidates(record) {
|
|
|
103
103
|
}
|
|
104
104
|
return dedupeCandidates(candidates);
|
|
105
105
|
}
|
|
106
|
+
function isPlaceholderEntityId(value) {
|
|
107
|
+
return ["(home)", "home", "unknown", "unattributed", "unmapped", "(unmapped)"].includes(value.trim().toLowerCase());
|
|
108
|
+
}
|
|
106
109
|
function dedupeCandidates(candidates) {
|
|
107
110
|
const byKey = new Map();
|
|
108
111
|
for (const candidate of candidates) {
|
package/dist/localAgentLogs.js
CHANGED
|
@@ -146,6 +146,12 @@ export function parseCodexRollout(content) {
|
|
|
146
146
|
const normalized = resolve(workdir);
|
|
147
147
|
toolWorkdirs.set(normalized, (toolWorkdirs.get(normalized) ?? 0) + 1);
|
|
148
148
|
}
|
|
149
|
+
// Current Codex Desktop records the orchestration wrapper as a custom
|
|
150
|
+
// `exec` call whose input is JavaScript containing nested tool calls.
|
|
151
|
+
// Extract only quoted absolute workdir/cwd values; never evaluate it.
|
|
152
|
+
if (payload.type === "custom_tool_call" && stringOf(payload.name) === "exec") {
|
|
153
|
+
collectEmbeddedToolWorkdirs(stringOf(payload.input), toolWorkdirs);
|
|
154
|
+
}
|
|
149
155
|
collectToolFiles(args, fileCounts);
|
|
150
156
|
collectPatchFiles(stringOf(args?.patch) ?? stringOf(args?.input) ?? stringOf(payload.input), fileCounts);
|
|
151
157
|
}
|
|
@@ -199,6 +205,24 @@ export function parseCodexRollout(content) {
|
|
|
199
205
|
}
|
|
200
206
|
}];
|
|
201
207
|
}
|
|
208
|
+
function collectEmbeddedToolWorkdirs(input, toolWorkdirs) {
|
|
209
|
+
if (!input)
|
|
210
|
+
return;
|
|
211
|
+
const fieldPattern = /(?:^|[^A-Za-z0-9_])["']?(?:workdir|cwd)["']?\s*:\s*("(?:\\.|[^"\\])*")/g;
|
|
212
|
+
for (const match of input.matchAll(fieldPattern)) {
|
|
213
|
+
let value;
|
|
214
|
+
try {
|
|
215
|
+
value = JSON.parse(match[1]);
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (typeof value !== "string" || !isAbsolute(value))
|
|
221
|
+
continue;
|
|
222
|
+
const normalized = resolve(value);
|
|
223
|
+
toolWorkdirs.set(normalized, (toolWorkdirs.get(normalized) ?? 0) + 1);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
202
226
|
function parseCodexRateLimits(value, observedAt) {
|
|
203
227
|
if (!isRecord(value) || !observedAt)
|
|
204
228
|
return undefined;
|
|
@@ -314,7 +338,10 @@ export function aggregateCalls(calls) {
|
|
|
314
338
|
outputTokens: usage.outputTokens,
|
|
315
339
|
amountUsd: priced ? amountUsd : null,
|
|
316
340
|
costConfidence: priced ? "estimated" : "missing",
|
|
317
|
-
|
|
341
|
+
// `(home)` is an attribution fallback, not a real project. Keep it on
|
|
342
|
+
// LocalAgentCall for Glance/session context, but do not promote it to a
|
|
343
|
+
// high-confidence project id in receipts or the attribution engine.
|
|
344
|
+
projectId: project === "unattributed" || project === "(home)" ? undefined : project,
|
|
318
345
|
agentId: agent,
|
|
319
346
|
providerCostType: "local_agent_logs",
|
|
320
347
|
quantity: groupCalls.length,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-finops/core",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"!dist/**/*.tsbuildinfo"
|
|
21
21
|
],
|
|
22
22
|
"license": "MIT",
|
|
23
|
-
"description": "Canonical aibill engine for
|
|
23
|
+
"description": "Canonical aibill evidence engine for agent work, billing semantics, attribution, provenance, runway, and Context Health.",
|
|
24
24
|
"keywords": [
|
|
25
25
|
"ai",
|
|
26
26
|
"finops",
|