sentinelayer-cli 0.6.2 → 0.8.0
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 +996 -996
- package/bin/create-sentinelayer.js +5 -5
- package/bin/sentinelayer-cli.js +4 -4
- package/bin/sl.js +5 -5
- package/package.json +64 -63
- package/src/agents/jules/config/definition.js +160 -160
- package/src/agents/jules/config/system-prompt.js +182 -182
- package/src/agents/jules/error-intake.js +51 -51
- package/src/agents/jules/fix-cycle.js +17 -17
- package/src/agents/jules/loop.js +457 -450
- package/src/agents/jules/pulse.js +10 -10
- package/src/agents/jules/stream.js +187 -186
- package/src/agents/jules/swarm/file-scanner.js +74 -74
- package/src/agents/jules/swarm/index.js +11 -11
- package/src/agents/jules/swarm/orchestrator.js +362 -362
- package/src/agents/jules/swarm/pattern-hunter.js +123 -123
- package/src/agents/jules/swarm/sub-agent.js +311 -309
- package/src/agents/jules/tools/aidenid-email.js +189 -189
- package/src/agents/jules/tools/auth-audit.js +1699 -1691
- package/src/agents/jules/tools/dispatch.js +340 -335
- package/src/agents/jules/tools/file-edit.js +2 -2
- package/src/agents/jules/tools/file-read.js +2 -2
- package/src/agents/jules/tools/frontend-analyze.js +570 -570
- package/src/agents/jules/tools/glob.js +2 -2
- package/src/agents/jules/tools/grep.js +2 -2
- package/src/agents/jules/tools/index.js +29 -29
- package/src/agents/jules/tools/path-guards.js +2 -2
- package/src/agents/jules/tools/runtime-audit.js +507 -507
- package/src/agents/jules/tools/shell.js +2 -2
- package/src/agents/jules/tools/url-policy.js +100 -100
- package/src/agents/persona-visuals.js +64 -61
- package/src/agents/shared-tools/dispatch-core.js +320 -315
- package/src/agents/shared-tools/file-edit.js +180 -180
- package/src/agents/shared-tools/file-read.js +100 -100
- package/src/agents/shared-tools/glob.js +168 -168
- package/src/agents/shared-tools/grep.js +228 -228
- package/src/agents/shared-tools/index.js +46 -46
- package/src/agents/shared-tools/path-guards.js +161 -161
- package/src/agents/shared-tools/shell.js +383 -383
- package/src/ai/aidenid.js +1021 -1009
- package/src/ai/client.js +553 -553
- package/src/ai/domain-target-store.js +268 -268
- package/src/ai/identity-store.js +270 -270
- package/src/ai/proxy.js +137 -137
- package/src/ai/site-store.js +145 -145
- package/src/audit/agents/architecture.js +180 -180
- package/src/audit/agents/compliance.js +179 -179
- package/src/audit/agents/documentation.js +165 -165
- package/src/audit/agents/performance.js +145 -145
- package/src/audit/agents/security.js +215 -215
- package/src/audit/agents/testing.js +172 -172
- package/src/audit/orchestrator.js +557 -557
- package/src/audit/package.js +204 -204
- package/src/audit/registry.js +284 -284
- package/src/audit/replay.js +103 -103
- package/src/auth/gate.js +400 -371
- package/src/auth/http.js +681 -611
- package/src/auth/service.js +1106 -1106
- package/src/auth/session-store.js +813 -813
- package/src/cli.js +257 -252
- package/src/commands/ai/identity-lifecycle.js +1338 -1338
- package/src/commands/ai/provision-governance.js +1272 -1272
- package/src/commands/ai/shared.js +147 -147
- package/src/commands/ai.js +11 -11
- package/src/commands/apply.js +12 -12
- package/src/commands/audit.js +1171 -1166
- package/src/commands/auth.js +419 -419
- package/src/commands/chat.js +191 -191
- package/src/commands/config.js +184 -184
- package/src/commands/cost.js +311 -311
- package/src/commands/daemon/core.js +850 -850
- package/src/commands/daemon/extended.js +1048 -1048
- package/src/commands/daemon/shared.js +213 -213
- package/src/commands/daemon.js +11 -11
- package/src/commands/guide.js +174 -174
- package/src/commands/ingest.js +58 -58
- package/src/commands/init.js +55 -55
- package/src/commands/legacy-args.js +10 -10
- package/src/commands/mcp.js +461 -461
- package/src/commands/omargate.js +29 -29
- package/src/commands/persona.js +20 -20
- package/src/commands/plugin.js +260 -260
- package/src/commands/policy.js +132 -132
- package/src/commands/prompt.js +238 -238
- package/src/commands/review.js +704 -704
- package/src/commands/scan.js +872 -872
- package/src/commands/session.js +590 -0
- package/src/commands/spec.js +778 -716
- package/src/commands/swarm.js +651 -651
- package/src/commands/telemetry.js +202 -202
- package/src/commands/watch.js +511 -511
- package/src/config/agent-dictionary.js +182 -182
- package/src/config/io.js +56 -56
- package/src/config/paths.js +18 -18
- package/src/config/schema.js +55 -55
- package/src/config/service.js +184 -184
- package/src/cost/budget.js +235 -235
- package/src/cost/history.js +188 -188
- package/src/cost/tracker.js +171 -171
- package/src/daemon/artifact-lineage.js +534 -534
- package/src/daemon/assignment-ledger.js +966 -770
- package/src/daemon/ast-parser-layer.js +258 -258
- package/src/daemon/budget-governor.js +633 -633
- package/src/daemon/callgraph-overlay.js +646 -646
- package/src/daemon/error-worker.js +1209 -626
- package/src/daemon/fix-cycle.js +384 -377
- package/src/daemon/hybrid-mapper.js +929 -929
- package/src/daemon/ingest-refresh.js +10 -9
- package/src/daemon/jira-lifecycle.js +767 -632
- package/src/daemon/operator-control.js +657 -657
- package/src/daemon/pulse.js +327 -327
- package/src/daemon/reliability-lane.js +471 -471
- package/src/daemon/scope-engine.js +1068 -0
- package/src/daemon/watchdog.js +971 -971
- package/src/events/schema.js +190 -0
- package/src/guide/generator.js +316 -316
- package/src/ingest/engine.js +918 -918
- package/src/interactive/index.js +97 -97
- package/src/legacy-cli.js +3161 -2994
- package/src/mcp/registry.js +695 -695
- package/src/memory/blackboard.js +301 -301
- package/src/memory/retrieval.js +581 -581
- package/src/plugin/manifest.js +553 -553
- package/src/policy/packs.js +144 -144
- package/src/prompt/generator.js +136 -118
- package/src/review/ai-review.js +679 -679
- package/src/review/local-review.js +1351 -1305
- package/src/review/omargate-interactive.js +68 -68
- package/src/review/omargate-orchestrator.js +404 -300
- package/src/review/persona-prompts.js +296 -296
- package/src/review/replay.js +235 -235
- package/src/review/report.js +664 -664
- package/src/review/scan-modes.js +48 -42
- package/src/review/spec-binding.js +487 -487
- package/src/scaffold/generator.js +67 -67
- package/src/scaffold/templates.js +150 -150
- package/src/scan/generator.js +418 -418
- package/src/scan/gh-secrets.js +107 -107
- package/src/session/agent-registry.js +352 -0
- package/src/session/daemon.js +801 -0
- package/src/session/paths.js +33 -0
- package/src/session/runtime-bridge.js +739 -0
- package/src/session/store.js +388 -0
- package/src/session/stream.js +325 -0
- package/src/spec/generator.js +619 -519
- package/src/spec/regenerate.js +237 -237
- package/src/spec/templates.js +91 -91
- package/src/swarm/dashboard.js +247 -247
- package/src/swarm/factory.js +363 -363
- package/src/swarm/pentest.js +934 -934
- package/src/swarm/registry.js +419 -419
- package/src/swarm/report.js +158 -158
- package/src/swarm/runtime.js +576 -576
- package/src/swarm/scenario-dsl.js +272 -272
- package/src/telemetry/ledger.js +302 -302
- package/src/telemetry/session-tracker.js +234 -234
- package/src/telemetry/sync.js +203 -203
- package/src/ui/command-hints.js +13 -13
- package/src/ui/markdown.js +220 -220
package/src/cost/history.js
CHANGED
|
@@ -1,188 +1,188 @@
|
|
|
1
|
-
import fsp from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { randomUUID } from "node:crypto";
|
|
4
|
-
|
|
5
|
-
import { resolveOutputRoot } from "../config/service.js";
|
|
6
|
-
import { rollupUsage } from "./tracker.js";
|
|
7
|
-
|
|
8
|
-
const HISTORY_VERSION = 1;
|
|
9
|
-
const HISTORY_FILE_NAME = "cost-history.json";
|
|
10
|
-
|
|
11
|
-
function normalizeNumber(value, field) {
|
|
12
|
-
const normalized = Number(value || 0);
|
|
13
|
-
if (!Number.isFinite(normalized) || normalized < 0) {
|
|
14
|
-
throw new Error(`${field} must be a non-negative number.`);
|
|
15
|
-
}
|
|
16
|
-
return normalized;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function normalizeProgressScore(value) {
|
|
20
|
-
const normalized = Number(value || 0);
|
|
21
|
-
if (!Number.isFinite(normalized)) {
|
|
22
|
-
return 0;
|
|
23
|
-
}
|
|
24
|
-
return normalized;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function normalizeEntry(entry) {
|
|
28
|
-
if (!entry || typeof entry !== "object") {
|
|
29
|
-
throw new Error("Cost entry must be an object.");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const provider = String(entry.provider || "").trim().toLowerCase();
|
|
33
|
-
const model = String(entry.model || "").trim();
|
|
34
|
-
if (!provider) {
|
|
35
|
-
throw new Error("Cost entry provider is required.");
|
|
36
|
-
}
|
|
37
|
-
if (!model) {
|
|
38
|
-
throw new Error("Cost entry model is required.");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const invocationId = String(entry.invocationId || "").trim() || randomUUID();
|
|
42
|
-
const sessionId = String(entry.sessionId || "").trim() || "default";
|
|
43
|
-
const timestamp = String(entry.timestamp || "").trim() || new Date().toISOString();
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
invocationId,
|
|
47
|
-
sessionId,
|
|
48
|
-
timestamp,
|
|
49
|
-
provider,
|
|
50
|
-
model,
|
|
51
|
-
inputTokens: normalizeNumber(entry.inputTokens, "entry.inputTokens"),
|
|
52
|
-
outputTokens: normalizeNumber(entry.outputTokens, "entry.outputTokens"),
|
|
53
|
-
cacheReadTokens: normalizeNumber(entry.cacheReadTokens, "entry.cacheReadTokens"),
|
|
54
|
-
cacheWriteTokens: normalizeNumber(entry.cacheWriteTokens, "entry.cacheWriteTokens"),
|
|
55
|
-
durationMs: normalizeNumber(entry.durationMs, "entry.durationMs"),
|
|
56
|
-
toolCalls: normalizeNumber(entry.toolCalls, "entry.toolCalls"),
|
|
57
|
-
costUsd: normalizeNumber(entry.costUsd, "entry.costUsd"),
|
|
58
|
-
progressScore: normalizeProgressScore(entry.progressScore),
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export async function resolveCostHistoryPath({
|
|
63
|
-
targetPath = ".",
|
|
64
|
-
outputDirOverride = "",
|
|
65
|
-
env,
|
|
66
|
-
homeDir,
|
|
67
|
-
} = {}) {
|
|
68
|
-
const outputRoot = await resolveOutputRoot({
|
|
69
|
-
cwd: path.resolve(targetPath),
|
|
70
|
-
outputDirOverride,
|
|
71
|
-
env,
|
|
72
|
-
homeDir,
|
|
73
|
-
});
|
|
74
|
-
return path.join(outputRoot, HISTORY_FILE_NAME);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export async function loadCostHistory(options = {}) {
|
|
78
|
-
const filePath = await resolveCostHistoryPath(options);
|
|
79
|
-
try {
|
|
80
|
-
const raw = await fsp.readFile(filePath, "utf-8");
|
|
81
|
-
const parsed = JSON.parse(raw);
|
|
82
|
-
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.entries)) {
|
|
83
|
-
throw new Error("Invalid cost history payload.");
|
|
84
|
-
}
|
|
85
|
-
return {
|
|
86
|
-
filePath,
|
|
87
|
-
history: {
|
|
88
|
-
version: Number(parsed.version || HISTORY_VERSION),
|
|
89
|
-
entries: parsed.entries,
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
} catch (error) {
|
|
93
|
-
if (error && typeof error === "object" && error.code === "ENOENT") {
|
|
94
|
-
return {
|
|
95
|
-
filePath,
|
|
96
|
-
history: {
|
|
97
|
-
version: HISTORY_VERSION,
|
|
98
|
-
entries: [],
|
|
99
|
-
},
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
throw error;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export async function saveCostHistory({ filePath, history }) {
|
|
107
|
-
const payload = {
|
|
108
|
-
version: HISTORY_VERSION,
|
|
109
|
-
entries: Array.isArray(history?.entries) ? history.entries : [],
|
|
110
|
-
};
|
|
111
|
-
await fsp.mkdir(path.dirname(filePath), { recursive: true });
|
|
112
|
-
await fsp.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export async function appendCostEntry(options = {}, entry = {}) {
|
|
116
|
-
const normalizedEntry = normalizeEntry(entry);
|
|
117
|
-
const { filePath, history } = await loadCostHistory(options);
|
|
118
|
-
const nextHistory = {
|
|
119
|
-
version: HISTORY_VERSION,
|
|
120
|
-
entries: [...history.entries, normalizedEntry],
|
|
121
|
-
};
|
|
122
|
-
await saveCostHistory({ filePath, history: nextHistory });
|
|
123
|
-
return {
|
|
124
|
-
filePath,
|
|
125
|
-
entry: normalizedEntry,
|
|
126
|
-
history: nextHistory,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function summarizeSessionEntries(entries) {
|
|
131
|
-
const usageEntries = entries.map((item) => ({
|
|
132
|
-
inputTokens: item.inputTokens,
|
|
133
|
-
outputTokens: item.outputTokens,
|
|
134
|
-
costUsd: item.costUsd,
|
|
135
|
-
}));
|
|
136
|
-
const usage = rollupUsage(usageEntries);
|
|
137
|
-
const cacheReadTokens = entries.reduce((sum, item) => sum + Number(item.cacheReadTokens || 0), 0);
|
|
138
|
-
const cacheWriteTokens = entries.reduce((sum, item) => sum + Number(item.cacheWriteTokens || 0), 0);
|
|
139
|
-
const durationMs = entries.reduce((sum, item) => sum + Number(item.durationMs || 0), 0);
|
|
140
|
-
const toolCalls = entries.reduce((sum, item) => sum + Number(item.toolCalls || 0), 0);
|
|
141
|
-
|
|
142
|
-
let noProgressStreak = 0;
|
|
143
|
-
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
144
|
-
if (Number(entries[index].progressScore || 0) > 0) {
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
|
-
noProgressStreak += 1;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return {
|
|
151
|
-
invocationCount: entries.length,
|
|
152
|
-
inputTokens: usage.inputTokens,
|
|
153
|
-
outputTokens: usage.outputTokens,
|
|
154
|
-
cacheReadTokens,
|
|
155
|
-
cacheWriteTokens,
|
|
156
|
-
durationMs,
|
|
157
|
-
toolCalls,
|
|
158
|
-
costUsd: usage.costUsd,
|
|
159
|
-
noProgressStreak,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export function summarizeCostHistory(history = {}) {
|
|
164
|
-
const entries = Array.isArray(history.entries) ? history.entries : [];
|
|
165
|
-
const sessionMap = new Map();
|
|
166
|
-
for (const entry of entries) {
|
|
167
|
-
const key = String(entry.sessionId || "default");
|
|
168
|
-
const existing = sessionMap.get(key) || [];
|
|
169
|
-
existing.push(entry);
|
|
170
|
-
sessionMap.set(key, existing);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const sessions = [...sessionMap.entries()]
|
|
174
|
-
.map(([sessionId, sessionEntries]) => ({
|
|
175
|
-
sessionId,
|
|
176
|
-
...summarizeSessionEntries(sessionEntries),
|
|
177
|
-
}))
|
|
178
|
-
.sort((a, b) => a.sessionId.localeCompare(b.sessionId));
|
|
179
|
-
|
|
180
|
-
const totals = summarizeSessionEntries(entries);
|
|
181
|
-
|
|
182
|
-
return {
|
|
183
|
-
sessionCount: sessions.length,
|
|
184
|
-
...totals,
|
|
185
|
-
sessions,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
1
|
+
import fsp from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
|
|
5
|
+
import { resolveOutputRoot } from "../config/service.js";
|
|
6
|
+
import { rollupUsage } from "./tracker.js";
|
|
7
|
+
|
|
8
|
+
const HISTORY_VERSION = 1;
|
|
9
|
+
const HISTORY_FILE_NAME = "cost-history.json";
|
|
10
|
+
|
|
11
|
+
function normalizeNumber(value, field) {
|
|
12
|
+
const normalized = Number(value || 0);
|
|
13
|
+
if (!Number.isFinite(normalized) || normalized < 0) {
|
|
14
|
+
throw new Error(`${field} must be a non-negative number.`);
|
|
15
|
+
}
|
|
16
|
+
return normalized;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function normalizeProgressScore(value) {
|
|
20
|
+
const normalized = Number(value || 0);
|
|
21
|
+
if (!Number.isFinite(normalized)) {
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
return normalized;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function normalizeEntry(entry) {
|
|
28
|
+
if (!entry || typeof entry !== "object") {
|
|
29
|
+
throw new Error("Cost entry must be an object.");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const provider = String(entry.provider || "").trim().toLowerCase();
|
|
33
|
+
const model = String(entry.model || "").trim();
|
|
34
|
+
if (!provider) {
|
|
35
|
+
throw new Error("Cost entry provider is required.");
|
|
36
|
+
}
|
|
37
|
+
if (!model) {
|
|
38
|
+
throw new Error("Cost entry model is required.");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const invocationId = String(entry.invocationId || "").trim() || randomUUID();
|
|
42
|
+
const sessionId = String(entry.sessionId || "").trim() || "default";
|
|
43
|
+
const timestamp = String(entry.timestamp || "").trim() || new Date().toISOString();
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
invocationId,
|
|
47
|
+
sessionId,
|
|
48
|
+
timestamp,
|
|
49
|
+
provider,
|
|
50
|
+
model,
|
|
51
|
+
inputTokens: normalizeNumber(entry.inputTokens, "entry.inputTokens"),
|
|
52
|
+
outputTokens: normalizeNumber(entry.outputTokens, "entry.outputTokens"),
|
|
53
|
+
cacheReadTokens: normalizeNumber(entry.cacheReadTokens, "entry.cacheReadTokens"),
|
|
54
|
+
cacheWriteTokens: normalizeNumber(entry.cacheWriteTokens, "entry.cacheWriteTokens"),
|
|
55
|
+
durationMs: normalizeNumber(entry.durationMs, "entry.durationMs"),
|
|
56
|
+
toolCalls: normalizeNumber(entry.toolCalls, "entry.toolCalls"),
|
|
57
|
+
costUsd: normalizeNumber(entry.costUsd, "entry.costUsd"),
|
|
58
|
+
progressScore: normalizeProgressScore(entry.progressScore),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function resolveCostHistoryPath({
|
|
63
|
+
targetPath = ".",
|
|
64
|
+
outputDirOverride = "",
|
|
65
|
+
env,
|
|
66
|
+
homeDir,
|
|
67
|
+
} = {}) {
|
|
68
|
+
const outputRoot = await resolveOutputRoot({
|
|
69
|
+
cwd: path.resolve(targetPath),
|
|
70
|
+
outputDirOverride,
|
|
71
|
+
env,
|
|
72
|
+
homeDir,
|
|
73
|
+
});
|
|
74
|
+
return path.join(outputRoot, HISTORY_FILE_NAME);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function loadCostHistory(options = {}) {
|
|
78
|
+
const filePath = await resolveCostHistoryPath(options);
|
|
79
|
+
try {
|
|
80
|
+
const raw = await fsp.readFile(filePath, "utf-8");
|
|
81
|
+
const parsed = JSON.parse(raw);
|
|
82
|
+
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.entries)) {
|
|
83
|
+
throw new Error("Invalid cost history payload.");
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
filePath,
|
|
87
|
+
history: {
|
|
88
|
+
version: Number(parsed.version || HISTORY_VERSION),
|
|
89
|
+
entries: parsed.entries,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (error && typeof error === "object" && error.code === "ENOENT") {
|
|
94
|
+
return {
|
|
95
|
+
filePath,
|
|
96
|
+
history: {
|
|
97
|
+
version: HISTORY_VERSION,
|
|
98
|
+
entries: [],
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function saveCostHistory({ filePath, history }) {
|
|
107
|
+
const payload = {
|
|
108
|
+
version: HISTORY_VERSION,
|
|
109
|
+
entries: Array.isArray(history?.entries) ? history.entries : [],
|
|
110
|
+
};
|
|
111
|
+
await fsp.mkdir(path.dirname(filePath), { recursive: true });
|
|
112
|
+
await fsp.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function appendCostEntry(options = {}, entry = {}) {
|
|
116
|
+
const normalizedEntry = normalizeEntry(entry);
|
|
117
|
+
const { filePath, history } = await loadCostHistory(options);
|
|
118
|
+
const nextHistory = {
|
|
119
|
+
version: HISTORY_VERSION,
|
|
120
|
+
entries: [...history.entries, normalizedEntry],
|
|
121
|
+
};
|
|
122
|
+
await saveCostHistory({ filePath, history: nextHistory });
|
|
123
|
+
return {
|
|
124
|
+
filePath,
|
|
125
|
+
entry: normalizedEntry,
|
|
126
|
+
history: nextHistory,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function summarizeSessionEntries(entries) {
|
|
131
|
+
const usageEntries = entries.map((item) => ({
|
|
132
|
+
inputTokens: item.inputTokens,
|
|
133
|
+
outputTokens: item.outputTokens,
|
|
134
|
+
costUsd: item.costUsd,
|
|
135
|
+
}));
|
|
136
|
+
const usage = rollupUsage(usageEntries);
|
|
137
|
+
const cacheReadTokens = entries.reduce((sum, item) => sum + Number(item.cacheReadTokens || 0), 0);
|
|
138
|
+
const cacheWriteTokens = entries.reduce((sum, item) => sum + Number(item.cacheWriteTokens || 0), 0);
|
|
139
|
+
const durationMs = entries.reduce((sum, item) => sum + Number(item.durationMs || 0), 0);
|
|
140
|
+
const toolCalls = entries.reduce((sum, item) => sum + Number(item.toolCalls || 0), 0);
|
|
141
|
+
|
|
142
|
+
let noProgressStreak = 0;
|
|
143
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
144
|
+
if (Number(entries[index].progressScore || 0) > 0) {
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
noProgressStreak += 1;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
invocationCount: entries.length,
|
|
152
|
+
inputTokens: usage.inputTokens,
|
|
153
|
+
outputTokens: usage.outputTokens,
|
|
154
|
+
cacheReadTokens,
|
|
155
|
+
cacheWriteTokens,
|
|
156
|
+
durationMs,
|
|
157
|
+
toolCalls,
|
|
158
|
+
costUsd: usage.costUsd,
|
|
159
|
+
noProgressStreak,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function summarizeCostHistory(history = {}) {
|
|
164
|
+
const entries = Array.isArray(history.entries) ? history.entries : [];
|
|
165
|
+
const sessionMap = new Map();
|
|
166
|
+
for (const entry of entries) {
|
|
167
|
+
const key = String(entry.sessionId || "default");
|
|
168
|
+
const existing = sessionMap.get(key) || [];
|
|
169
|
+
existing.push(entry);
|
|
170
|
+
sessionMap.set(key, existing);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const sessions = [...sessionMap.entries()]
|
|
174
|
+
.map(([sessionId, sessionEntries]) => ({
|
|
175
|
+
sessionId,
|
|
176
|
+
...summarizeSessionEntries(sessionEntries),
|
|
177
|
+
}))
|
|
178
|
+
.sort((a, b) => a.sessionId.localeCompare(b.sessionId));
|
|
179
|
+
|
|
180
|
+
const totals = summarizeSessionEntries(entries);
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
sessionCount: sessions.length,
|
|
184
|
+
...totals,
|
|
185
|
+
sessions,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|