@iowarp/clio-coder 0.3.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/CHANGELOG.md +407 -0
- package/CODE_OF_CONDUCT.md +21 -0
- package/CONTRIBUTING.md +224 -0
- package/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +798 -0
- package/SECURITY.md +72 -0
- package/assets/clio-coder-logo-128.webp +0 -0
- package/damage-control-rules.yaml +419 -0
- package/dist/acp-UMLFVA3F.js +92 -0
- package/dist/agents-Q4MYPMUW.js +91 -0
- package/dist/auth-O6HYIJ6J.js +521 -0
- package/dist/chunk-262G75JS.js +35 -0
- package/dist/chunk-26BZQOAD.js +1281 -0
- package/dist/chunk-2J63S4SF.js +508 -0
- package/dist/chunk-3DANZDGR.js +717 -0
- package/dist/chunk-4UQA7NCT.js +29 -0
- package/dist/chunk-527KG6XR.js +497 -0
- package/dist/chunk-5LDRNKX2.js +1063 -0
- package/dist/chunk-5N2FG33Q.js +25 -0
- package/dist/chunk-67MTHP2E.js +135 -0
- package/dist/chunk-6CWDTGUC.js +20 -0
- package/dist/chunk-7BHLZB3A.js +2115 -0
- package/dist/chunk-7RBKDI66.js +348 -0
- package/dist/chunk-AMFR5YA3.js +541 -0
- package/dist/chunk-BBUH4VAA.js +1224 -0
- package/dist/chunk-BYEU76JP.js +899 -0
- package/dist/chunk-CLJ5HLUD.js +458 -0
- package/dist/chunk-D5YD55AR.js +116 -0
- package/dist/chunk-DXQNI4PC.js +61 -0
- package/dist/chunk-E3NYWENM.js +1004 -0
- package/dist/chunk-GNGDQYDU.js +34688 -0
- package/dist/chunk-GOTUR54M.js +9 -0
- package/dist/chunk-HBU5MTAM.js +41 -0
- package/dist/chunk-HMYNFFY4.js +28 -0
- package/dist/chunk-JPOWPFCU.js +1010 -0
- package/dist/chunk-JWHCJDCI.js +1215 -0
- package/dist/chunk-KBR4MZZR.js +41 -0
- package/dist/chunk-KKKPTZLM.js +93 -0
- package/dist/chunk-ME6DNWIU.js +66 -0
- package/dist/chunk-NI4DEJMC.js +88 -0
- package/dist/chunk-O4EJEDHO.js +659 -0
- package/dist/chunk-PIDUD6M2.js +31 -0
- package/dist/chunk-PS4PFJQP.js +29459 -0
- package/dist/chunk-QV47YRF4.js +48 -0
- package/dist/chunk-RQDWMVRB.js +279 -0
- package/dist/chunk-TFSSEXL6.js +136 -0
- package/dist/chunk-TKHQ4DGZ.js +8290 -0
- package/dist/chunk-TPOCL34A.js +2876 -0
- package/dist/chunk-UGYAX5YI.js +565 -0
- package/dist/chunk-UHTSULZS.js +461 -0
- package/dist/chunk-UU3R62TT.js +128 -0
- package/dist/chunk-UWIJNAOB.js +3906 -0
- package/dist/chunk-VOO7NYPP.js +914 -0
- package/dist/chunk-VPAWTYLY.js +117 -0
- package/dist/chunk-WD6AJM35.js +1216 -0
- package/dist/chunk-X3BR7HWV.js +115 -0
- package/dist/chunk-X3NE4WVW.js +120 -0
- package/dist/chunk-XNISANGE.js +1395 -0
- package/dist/chunk-XV4ZJ6ZM.js +3177 -0
- package/dist/cli/index.js +236 -0
- package/dist/clio-KIQ5SNDS.js +53 -0
- package/dist/components-JVHMUBEB.js +653 -0
- package/dist/config-ZFCDBMDC.js +372 -0
- package/dist/configure-G4E3A2PG.js +27 -0
- package/dist/context-CDXTP2MP.js +293 -0
- package/dist/context-E3KIFVXI.js +185 -0
- package/dist/context-clear-3F4PLXOS.js +102 -0
- package/dist/context-index-Q7YSYTR3.js +106 -0
- package/dist/docs-YIETIWZI.js +280 -0
- package/dist/doctor-M5HJJZOL.js +61 -0
- package/dist/domains/agents/builtins/architect.md +33 -0
- package/dist/domains/agents/builtins/coder.md +31 -0
- package/dist/domains/agents/builtins/context-bootstrap.md +38 -0
- package/dist/domains/agents/builtins/debugger.md +30 -0
- package/dist/domains/agents/builtins/documenter.md +31 -0
- package/dist/domains/agents/builtins/git-master.md +30 -0
- package/dist/domains/agents/builtins/provenance.md +30 -0
- package/dist/domains/agents/builtins/researcher.md +71 -0
- package/dist/domains/agents/builtins/scout.md +42 -0
- package/dist/domains/agents/builtins/tester.md +31 -0
- package/dist/domains/agents/builtins/verifier.md +30 -0
- package/dist/domains/agents/builtins/wiki-writer.md +41 -0
- package/dist/eval-B3KZZESM.js +2674 -0
- package/dist/evidence-V67CHM35.js +233 -0
- package/dist/evolve-YDZSUQYA.js +518 -0
- package/dist/extensions-SRG7XCAH.js +207 -0
- package/dist/fleet-CA2CRTVG.js +760 -0
- package/dist/fleet-preflight-CLIAX7YR.js +21 -0
- package/dist/init-2OZDJE2D.js +227 -0
- package/dist/memory-3PIQQAKX.js +207 -0
- package/dist/models-DY35XI7Y.js +237 -0
- package/dist/paths-5OMXW7Z4.js +57 -0
- package/dist/preload-KZVHET2B.js +11 -0
- package/dist/reset-PIFYNOS3.js +216 -0
- package/dist/run-3VSPP24F.js +735 -0
- package/dist/share-D36RQCXM.js +241 -0
- package/dist/skills-F2MRLELY.js +445 -0
- package/dist/skills-eval-E2ZTW4PL.js +932 -0
- package/dist/targets-DZMEZAH4.js +977 -0
- package/dist/trace-7NYCUI2J.js +250 -0
- package/dist/uninstall-AD3JWHBB.js +322 -0
- package/dist/upgrade-WYYBKGDY.js +301 -0
- package/dist/usage-ULIDAGFF.js +755 -0
- package/dist/version-ROZ6CZKH.js +16 -0
- package/dist/wiki-generate-PKFIX6OB.js +377 -0
- package/dist/worker/entry.js +1739 -0
- package/docs/README.md +93 -0
- package/docs/acp.md +120 -0
- package/docs/alcf-provider.md +72 -0
- package/docs/architecture.md +172 -0
- package/docs/artifact-versions.md +54 -0
- package/docs/built-in-agents.md +265 -0
- package/docs/capacity-and-scheduling.md +97 -0
- package/docs/commands-and-modes.md +554 -0
- package/docs/config-knobs-audit.md +115 -0
- package/docs/configuration-and-targets.md +812 -0
- package/docs/context-engine.md +236 -0
- package/docs/dispatch-architecture-rationale.md +126 -0
- package/docs/documentation-coverage.md +46 -0
- package/docs/documentation-guide.md +166 -0
- package/docs/environment-variables.md +105 -0
- package/docs/eval-runner.md +205 -0
- package/docs/evals-internal.md +298 -0
- package/docs/evidence-and-memory.md +243 -0
- package/docs/evolution.md +143 -0
- package/docs/exit-codes-and-output.md +74 -0
- package/docs/extensions-and-sharing.md +306 -0
- package/docs/fleet-demo-runbook.md +179 -0
- package/docs/fleet-dispatch.md +591 -0
- package/docs/glossary.md +75 -0
- package/docs/html/agents_blueprint.html +936 -0
- package/docs/html/alcf_blueprint.html +324 -0
- package/docs/html/architecture_blueprint.html +850 -0
- package/docs/html/commands_blueprint.html +794 -0
- package/docs/html/config_knobs_audit_blueprint.html +178 -0
- package/docs/html/configuration_blueprint.html +1080 -0
- package/docs/html/context_blueprint.html +603 -0
- package/docs/html/documentation_blueprint.html +832 -0
- package/docs/html/environment_blueprint.html +404 -0
- package/docs/html/eval_blueprint.html +743 -0
- package/docs/html/evals_internal_blueprint.html +190 -0
- package/docs/html/evolution_blueprint.html +674 -0
- package/docs/html/extensions_blueprint.html +2065 -0
- package/docs/html/fleet_dispatch_blueprint.html +286 -0
- package/docs/html/index.html +919 -0
- package/docs/html/lifecycle_blueprint.html +723 -0
- package/docs/html/memory_blueprint.html +699 -0
- package/docs/html/middleware_blueprint.html +664 -0
- package/docs/html/models_blueprint.html +2366 -0
- package/docs/html/observability_blueprint.html +683 -0
- package/docs/html/provider_adapter_blueprint.html +245 -0
- package/docs/html/safety_blueprint.html +1386 -0
- package/docs/html/shared.css +571 -0
- package/docs/html/shared.js +143 -0
- package/docs/html/skills_blueprint.html +671 -0
- package/docs/html/soak_blueprint.html +182 -0
- package/docs/html/tool_usage_blueprint.html +350 -0
- package/docs/html/tools_blueprint.html +2249 -0
- package/docs/html/trace_blueprint.html +235 -0
- package/docs/html/tui_design_blueprint.html +314 -0
- package/docs/html/validation_blueprint.html +961 -0
- package/docs/html/worker_dispatch_blueprint.html +231 -0
- package/docs/installation-and-lifecycle.md +308 -0
- package/docs/middleware-and-components.md +148 -0
- package/docs/model-catalog.md +189 -0
- package/docs/observability.md +233 -0
- package/docs/proactive-memory.md +452 -0
- package/docs/prompt-envelope-and-tools.md +142 -0
- package/docs/provider-adapter-cookbook.md +148 -0
- package/docs/release-cut-checklist.md +138 -0
- package/docs/safety-model.md +357 -0
- package/docs/scientific-validation.md +105 -0
- package/docs/session-lifecycle.md +156 -0
- package/docs/skills-marketplace.md +46 -0
- package/docs/tool-usage.md +527 -0
- package/docs/trace-store.md +132 -0
- package/docs/troubleshooting.md +33 -0
- package/docs/tui-design.md +239 -0
- package/docs/worker-dispatch-mechanics.md +242 -0
- package/package.json +132 -0
- package/skills/README.md +408 -0
- package/skills/git/commit-crafting/SKILL.md +79 -0
- package/skills/git/commit-crafting/evals.md +92 -0
- package/skills/git/create-pr/SKILL.md +116 -0
- package/skills/git/create-pr/evals.md +114 -0
- package/skills/git/investigate-issue/SKILL.md +139 -0
- package/skills/git/investigate-issue/evals.md +94 -0
- package/skills/git/resolve-merge-conflicts/SKILL.md +96 -0
- package/skills/git/resolve-merge-conflicts/evals.md +58 -0
- package/skills/git/review-changes/SKILL.md +103 -0
- package/skills/git/review-changes/evals.md +85 -0
- package/skills/git/worktree-create/SKILL.md +92 -0
- package/skills/git/worktree-create/evals.md +97 -0
- package/skills/git/worktree-create/references/worktree-setup.md +66 -0
- package/skills/git/worktree-merge/SKILL.md +95 -0
- package/skills/git/worktree-merge/evals.md +114 -0
- package/skills/skill-marketplace.json +261 -0
- package/skills/workflow/cut-it/SKILL.md +86 -0
- package/skills/workflow/cut-it/evals.md +42 -0
- package/src/domains/agents/builtins/architect.md +33 -0
- package/src/domains/agents/builtins/coder.md +31 -0
- package/src/domains/agents/builtins/context-bootstrap.md +38 -0
- package/src/domains/agents/builtins/debugger.md +30 -0
- package/src/domains/agents/builtins/documenter.md +31 -0
- package/src/domains/agents/builtins/git-master.md +30 -0
- package/src/domains/agents/builtins/provenance.md +30 -0
- package/src/domains/agents/builtins/researcher.md +71 -0
- package/src/domains/agents/builtins/scout.md +42 -0
- package/src/domains/agents/builtins/tester.md +31 -0
- package/src/domains/agents/builtins/verifier.md +30 -0
- package/src/domains/agents/builtins/wiki-writer.md +41 -0
- package/src/domains/agents/fleets/build-review.md +34 -0
- package/src/domains/agents/fleets/build-test.md +35 -0
- package/src/domains/agents/fleets/sdlc.md +86 -0
- package/src/domains/prompts/fragments/identity/clio-worker.md +11 -0
- package/src/domains/prompts/fragments/identity/clio.md +26 -0
- package/src/domains/prompts/fragments/operating/contract.md +64 -0
- package/src/domains/prompts/fragments/safety/auto-edit.md +14 -0
- package/src/domains/prompts/fragments/safety/full-auto.md +14 -0
- package/src/domains/prompts/fragments/safety/read-only.md +13 -0
- package/src/domains/prompts/fragments/safety/suggest.md +13 -0
- package/src/domains/prompts/fragments/wiki/page.md +75 -0
- package/src/domains/prompts/fragments/wiki/plan.md +48 -0
- package/src/domains/providers/models/cloud-models/alcf.yaml +40 -0
- package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +993 -0
|
@@ -0,0 +1,755 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadMemoryRecordsSync
|
|
3
|
+
} from "./chunk-VOO7NYPP.js";
|
|
4
|
+
import {
|
|
5
|
+
ledgerUsageCalls,
|
|
6
|
+
listSessionLedgerRefs,
|
|
7
|
+
parseSessionEntries,
|
|
8
|
+
readAuditRows,
|
|
9
|
+
readEvidenceIndex
|
|
10
|
+
} from "./chunk-GNGDQYDU.js";
|
|
11
|
+
import "./chunk-2J63S4SF.js";
|
|
12
|
+
import "./chunk-RQDWMVRB.js";
|
|
13
|
+
import "./chunk-7BHLZB3A.js";
|
|
14
|
+
import "./chunk-TPOCL34A.js";
|
|
15
|
+
import "./chunk-E3NYWENM.js";
|
|
16
|
+
import "./chunk-XV4ZJ6ZM.js";
|
|
17
|
+
import {
|
|
18
|
+
loadSkills
|
|
19
|
+
} from "./chunk-26BZQOAD.js";
|
|
20
|
+
import "./chunk-WD6AJM35.js";
|
|
21
|
+
import "./chunk-QV47YRF4.js";
|
|
22
|
+
import "./chunk-CLJ5HLUD.js";
|
|
23
|
+
import "./chunk-BBUH4VAA.js";
|
|
24
|
+
import "./chunk-GOTUR54M.js";
|
|
25
|
+
import "./chunk-PIDUD6M2.js";
|
|
26
|
+
import {
|
|
27
|
+
formatColumns,
|
|
28
|
+
printError
|
|
29
|
+
} from "./chunk-67MTHP2E.js";
|
|
30
|
+
import "./chunk-TKHQ4DGZ.js";
|
|
31
|
+
import "./chunk-D5YD55AR.js";
|
|
32
|
+
import "./chunk-JWHCJDCI.js";
|
|
33
|
+
import "./chunk-UHTSULZS.js";
|
|
34
|
+
import "./chunk-5LDRNKX2.js";
|
|
35
|
+
import {
|
|
36
|
+
cwdHash
|
|
37
|
+
} from "./chunk-UGYAX5YI.js";
|
|
38
|
+
import "./chunk-5N2FG33Q.js";
|
|
39
|
+
import "./chunk-VPAWTYLY.js";
|
|
40
|
+
import {
|
|
41
|
+
clioDataDir,
|
|
42
|
+
clioStateDir
|
|
43
|
+
} from "./chunk-X3NE4WVW.js";
|
|
44
|
+
import "./chunk-DXQNI4PC.js";
|
|
45
|
+
|
|
46
|
+
// src/cli/usage.ts
|
|
47
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
48
|
+
import { join, resolve } from "node:path";
|
|
49
|
+
var HELP = `clio-coder usage report [--repo <path>] [--days <n>] [--json]
|
|
50
|
+
|
|
51
|
+
Cross-session usage analyzer (experimental, read-only). Reads the local usage
|
|
52
|
+
archive (receipts, session ledgers, audit rows, evidence index, memory store)
|
|
53
|
+
and reports facts plus improvement opportunities, every line citing ids and
|
|
54
|
+
counts. Nothing is written or stored; suggestions are printed only.
|
|
55
|
+
|
|
56
|
+
Flags:
|
|
57
|
+
--repo <path> restrict sessions (and runs via the run ledger) to one repo
|
|
58
|
+
--days <n> report window in days (default 30)
|
|
59
|
+
--json emit one JSONL row per fact and per opportunity
|
|
60
|
+
(schema: "experimental"); diagnostics stay on stderr
|
|
61
|
+
`;
|
|
62
|
+
var DEFAULT_WINDOW_DAYS = 30;
|
|
63
|
+
var RECEIPT_CAP = 1e3;
|
|
64
|
+
var USAGE_WINDOW_FUTURE_SKEW_MS = 5e3;
|
|
65
|
+
var NON_FAILURE_TAGS = /* @__PURE__ */ new Set([
|
|
66
|
+
"session-linked",
|
|
67
|
+
"audit-linked",
|
|
68
|
+
"best-effort-link",
|
|
69
|
+
"protected-artifact",
|
|
70
|
+
"session-missing",
|
|
71
|
+
"audit-missing"
|
|
72
|
+
]);
|
|
73
|
+
function emptyUsageTotals() {
|
|
74
|
+
return {
|
|
75
|
+
apiCalls: 0,
|
|
76
|
+
input: 0,
|
|
77
|
+
output: 0,
|
|
78
|
+
cacheRead: 0,
|
|
79
|
+
cacheWrite: 0,
|
|
80
|
+
reasoningTokens: 0,
|
|
81
|
+
totalTokens: 0,
|
|
82
|
+
costUsd: 0
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function addUsageCall(totals, call) {
|
|
86
|
+
totals.apiCalls += call.apiCalls ?? 1;
|
|
87
|
+
totals.input += call.input;
|
|
88
|
+
totals.output += call.output;
|
|
89
|
+
totals.cacheRead += call.cacheRead;
|
|
90
|
+
totals.cacheWrite += call.cacheWrite;
|
|
91
|
+
totals.reasoningTokens += call.reasoningTokens;
|
|
92
|
+
totals.totalTokens += call.totalTokens;
|
|
93
|
+
totals.costUsd += call.costUsd;
|
|
94
|
+
}
|
|
95
|
+
async function isDirectory(path) {
|
|
96
|
+
try {
|
|
97
|
+
return (await stat(path)).isDirectory();
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function readStorePresence(stateDir) {
|
|
103
|
+
const sessionsPath = join(stateDir, "sessions");
|
|
104
|
+
const receiptsPath = join(stateDir, "receipts");
|
|
105
|
+
return {
|
|
106
|
+
sessionsPath,
|
|
107
|
+
sessionsPresent: await isDirectory(sessionsPath),
|
|
108
|
+
receiptsPath,
|
|
109
|
+
receiptsPresent: await isDirectory(receiptsPath)
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function missingStoreNotes(presence) {
|
|
113
|
+
const notes = [];
|
|
114
|
+
if (!presence.sessionsPresent) notes.push(`session store missing at ${presence.sessionsPath}`);
|
|
115
|
+
if (!presence.receiptsPresent) notes.push(`receipt store missing at ${presence.receiptsPath}`);
|
|
116
|
+
return notes;
|
|
117
|
+
}
|
|
118
|
+
function parseArgs(argv) {
|
|
119
|
+
const out = { days: DEFAULT_WINDOW_DAYS, json: false, help: false };
|
|
120
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
121
|
+
const arg = argv[i];
|
|
122
|
+
if (arg === void 0) continue;
|
|
123
|
+
if (!out.command && !arg.startsWith("-")) {
|
|
124
|
+
out.command = arg;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
switch (arg) {
|
|
128
|
+
case "--json":
|
|
129
|
+
out.json = true;
|
|
130
|
+
break;
|
|
131
|
+
case "--help":
|
|
132
|
+
case "-h":
|
|
133
|
+
out.help = true;
|
|
134
|
+
break;
|
|
135
|
+
case "--repo": {
|
|
136
|
+
const value = argv[i + 1];
|
|
137
|
+
if (!value || value.startsWith("-")) throw new Error("--repo requires a path");
|
|
138
|
+
out.repo = value;
|
|
139
|
+
i += 1;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case "--days": {
|
|
143
|
+
const value = argv[i + 1];
|
|
144
|
+
const days = value === void 0 ? Number.NaN : Number.parseInt(value, 10);
|
|
145
|
+
if (!Number.isInteger(days) || days <= 0) throw new Error("--days requires a positive integer");
|
|
146
|
+
out.days = days;
|
|
147
|
+
i += 1;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
default:
|
|
151
|
+
throw new Error(`unknown flag: ${arg}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
156
|
+
async function runUsageCommand(argv) {
|
|
157
|
+
let parsed;
|
|
158
|
+
try {
|
|
159
|
+
parsed = parseArgs(argv);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
printError(error instanceof Error ? error.message : String(error));
|
|
162
|
+
process.stderr.write(HELP);
|
|
163
|
+
return 2;
|
|
164
|
+
}
|
|
165
|
+
if (parsed.help || parsed.command === void 0) {
|
|
166
|
+
process.stdout.write(HELP);
|
|
167
|
+
return parsed.help ? 0 : 2;
|
|
168
|
+
}
|
|
169
|
+
if (parsed.command !== "report") {
|
|
170
|
+
printError(`unknown usage command: ${parsed.command}`);
|
|
171
|
+
process.stderr.write(HELP);
|
|
172
|
+
return 2;
|
|
173
|
+
}
|
|
174
|
+
const stateDir = clioStateDir();
|
|
175
|
+
const now = Date.now();
|
|
176
|
+
const windowStart = now - parsed.days * 24 * 60 * 60 * 1e3;
|
|
177
|
+
const diagnostics = {
|
|
178
|
+
malformedReceipts: 0,
|
|
179
|
+
unreadableSessions: 0,
|
|
180
|
+
malformedSessionLines: 0,
|
|
181
|
+
malformedAuditRows: 0,
|
|
182
|
+
notes: []
|
|
183
|
+
};
|
|
184
|
+
const presence = await readStorePresence(stateDir);
|
|
185
|
+
const repoPath = parsed.repo === void 0 ? void 0 : resolve(parsed.repo);
|
|
186
|
+
const repoHash = repoPath === void 0 ? void 0 : cwdHash(repoPath);
|
|
187
|
+
const repoRunIds = repoPath === void 0 ? null : await runIdsForCwd(stateDir, repoPath, diagnostics);
|
|
188
|
+
const receipts = (await readReceipts(stateDir, windowStart, now, diagnostics)).filter(
|
|
189
|
+
(receipt) => repoRunIds === null || repoRunIds.has(receipt.runId)
|
|
190
|
+
);
|
|
191
|
+
const sessions = await readSessions(stateDir, windowStart, now, repoHash, diagnostics);
|
|
192
|
+
const auditRows = await readAuditRows(stateDir);
|
|
193
|
+
diagnostics.malformedAuditRows = auditRows.errors.length;
|
|
194
|
+
const auditInWindow = auditRows.rows.filter((row) => row.ts !== null && inWindow(row.ts, windowStart, now));
|
|
195
|
+
const auditToolCalls = auditInWindow.filter((row) => row.auditKind === "tool_call");
|
|
196
|
+
const auditBlocked = auditToolCalls.filter((row) => {
|
|
197
|
+
const decision = typeof row.row.decision === "string" ? row.row.decision : "";
|
|
198
|
+
return decision === "blocked" || decision === "denied";
|
|
199
|
+
});
|
|
200
|
+
const evidenceRows = readEvidenceIndex(stateDir).filter((row) => inWindow(row.generatedAt, windowStart, now));
|
|
201
|
+
let memoryRecords = [];
|
|
202
|
+
try {
|
|
203
|
+
memoryRecords = loadMemoryRecordsSync(clioDataDir());
|
|
204
|
+
} catch (error) {
|
|
205
|
+
diagnostics.notes.push(`memory store unreadable: ${error instanceof Error ? error.message : String(error)}`);
|
|
206
|
+
}
|
|
207
|
+
const installedSkills = loadSkills({ cwd: repoPath ?? process.cwd() }).items.map((skill) => skill.name);
|
|
208
|
+
const toolTotals = /* @__PURE__ */ new Map();
|
|
209
|
+
for (const receipt of receipts) {
|
|
210
|
+
for (const statRow of receipt.toolStats) {
|
|
211
|
+
const entry = toolTotals.get(statRow.tool) ?? { count: 0, ok: 0, errors: 0, blocked: 0 };
|
|
212
|
+
entry.count += statRow.count;
|
|
213
|
+
entry.ok += statRow.ok;
|
|
214
|
+
entry.errors += statRow.errors;
|
|
215
|
+
entry.blocked += statRow.blocked;
|
|
216
|
+
toolTotals.set(statRow.tool, entry);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const topTools = [...toolTotals.entries()].sort((a, b) => b[1].count - a[1].count).slice(0, 10);
|
|
220
|
+
const shapeSessions = /* @__PURE__ */ new Map();
|
|
221
|
+
const shapeCounts = /* @__PURE__ */ new Map();
|
|
222
|
+
for (const session of sessions) {
|
|
223
|
+
for (const [shape, count] of session.bashShapes) {
|
|
224
|
+
shapeCounts.set(shape, (shapeCounts.get(shape) ?? 0) + count);
|
|
225
|
+
const set = shapeSessions.get(shape) ?? /* @__PURE__ */ new Set();
|
|
226
|
+
set.add(session.sessionId);
|
|
227
|
+
shapeSessions.set(shape, set);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const topShapes = [...shapeCounts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, 10);
|
|
231
|
+
const activatedSkills = /* @__PURE__ */ new Map();
|
|
232
|
+
for (const session of sessions) {
|
|
233
|
+
for (const name of session.skillActivations) {
|
|
234
|
+
activatedSkills.set(name, (activatedSkills.get(name) ?? 0) + 1);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
for (const receipt of receipts) {
|
|
238
|
+
for (const name of receipt.skillActivations) {
|
|
239
|
+
activatedSkills.set(name, (activatedSkills.get(name) ?? 0) + 1);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const neverActivated = installedSkills.filter((name) => !activatedSkills.has(name)).sort();
|
|
243
|
+
const recipeRuns = /* @__PURE__ */ new Map();
|
|
244
|
+
for (const receipt of receipts) {
|
|
245
|
+
recipeRuns.set(receipt.agentId, (recipeRuns.get(receipt.agentId) ?? 0) + 1);
|
|
246
|
+
}
|
|
247
|
+
const tagCounts = /* @__PURE__ */ new Map();
|
|
248
|
+
for (const row of evidenceRows) {
|
|
249
|
+
for (const tag of row.tags) {
|
|
250
|
+
if (NON_FAILURE_TAGS.has(tag)) continue;
|
|
251
|
+
const entry = tagCounts.get(tag);
|
|
252
|
+
if (entry === void 0) {
|
|
253
|
+
tagCounts.set(tag, { count: 1, latestEvidenceId: row.evidenceId, latestAt: row.generatedAt });
|
|
254
|
+
} else {
|
|
255
|
+
entry.count += 1;
|
|
256
|
+
if (row.generatedAt > entry.latestAt) {
|
|
257
|
+
entry.latestAt = row.generatedAt;
|
|
258
|
+
entry.latestEvidenceId = row.evidenceId;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const usageByModel = /* @__PURE__ */ new Map();
|
|
264
|
+
const usageTotals = emptyUsageTotals();
|
|
265
|
+
for (const session of sessions) {
|
|
266
|
+
for (const call of session.usageCalls) {
|
|
267
|
+
addUsageCall(usageTotals, call);
|
|
268
|
+
const key = `${call.providerId}::${call.modelId}`;
|
|
269
|
+
const entry = usageByModel.get(key) ?? {
|
|
270
|
+
providerId: call.providerId,
|
|
271
|
+
modelId: call.modelId,
|
|
272
|
+
totals: emptyUsageTotals()
|
|
273
|
+
};
|
|
274
|
+
addUsageCall(entry.totals, call);
|
|
275
|
+
usageByModel.set(key, entry);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
const usageRows = [...usageByModel.values()].sort(
|
|
279
|
+
(a, b) => b.totals.totalTokens - a.totals.totalTokens || a.providerId.localeCompare(b.providerId)
|
|
280
|
+
);
|
|
281
|
+
const approvedMemory = memoryRecords.filter((record) => record.approved && record.rejectedAt === void 0);
|
|
282
|
+
const pendingMemory = memoryRecords.filter((record) => !record.approved && record.rejectedAt === void 0);
|
|
283
|
+
const opportunities = [];
|
|
284
|
+
const sessionsWithSkills = new Set(
|
|
285
|
+
sessions.filter((session) => session.skillActivations.size > 0).map((session) => session.sessionId)
|
|
286
|
+
);
|
|
287
|
+
for (const [shape, ids] of [...shapeSessions.entries()].sort((a, b) => b[1].size - a[1].size)) {
|
|
288
|
+
if (isTrivialShape(shape)) continue;
|
|
289
|
+
const bareIds = [...ids].filter((id) => !sessionsWithSkills.has(id));
|
|
290
|
+
if (bareIds.length < 3) continue;
|
|
291
|
+
opportunities.push({
|
|
292
|
+
kind: "workflow-distiller",
|
|
293
|
+
suggestion: `bash shape "${shape}" recurs with no skill activation; consider /skill:workflow-distiller to distill it`,
|
|
294
|
+
evidence: `${bareIds.length} sessions: ${bareIds.slice(0, 5).join(", ")}${bareIds.length > 5 ? ", ..." : ""}`
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
const taskPrefixGroups = /* @__PURE__ */ new Map();
|
|
298
|
+
for (const receipt of receipts) {
|
|
299
|
+
const prefix = receipt.task.toLowerCase().replace(/\s+/g, " ").trim().slice(0, 64);
|
|
300
|
+
if (prefix.length === 0) continue;
|
|
301
|
+
const group = taskPrefixGroups.get(prefix) ?? [];
|
|
302
|
+
group.push(receipt.runId);
|
|
303
|
+
taskPrefixGroups.set(prefix, group);
|
|
304
|
+
}
|
|
305
|
+
for (const [prefix, runIds] of [...taskPrefixGroups.entries()].sort((a, b) => b[1].length - a[1].length)) {
|
|
306
|
+
if (runIds.length < 2) continue;
|
|
307
|
+
opportunities.push({
|
|
308
|
+
kind: "recipe",
|
|
309
|
+
suggestion: `dispatch task prefix "${prefix}" repeats; consider a recipe on the agents surface`,
|
|
310
|
+
evidence: `${runIds.length} runs: ${runIds.slice(0, 5).join(", ")}${runIds.length > 5 ? ", ..." : ""}`
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
for (const [tag, info] of [...tagCounts.entries()].sort((a, b) => b[1].count - a[1].count)) {
|
|
314
|
+
if (info.count < 2) continue;
|
|
315
|
+
if (memoryMentions(memoryRecords, tag)) continue;
|
|
316
|
+
opportunities.push({
|
|
317
|
+
kind: "memory",
|
|
318
|
+
suggestion: `failure tag "${tag}" recurs with no memory record; consider: clio-coder memory propose --from-evidence ${info.latestEvidenceId}`,
|
|
319
|
+
evidence: `${info.count} evidence rows in window, latest ${info.latestEvidenceId} at ${info.latestAt}`
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
const diagParts = [];
|
|
323
|
+
if (diagnostics.malformedReceipts > 0) diagParts.push(`${diagnostics.malformedReceipts} malformed receipt(s) skipped`);
|
|
324
|
+
if (diagnostics.unreadableSessions > 0)
|
|
325
|
+
diagParts.push(`${diagnostics.unreadableSessions} unreadable session ledger(s)`);
|
|
326
|
+
if (diagnostics.malformedSessionLines > 0)
|
|
327
|
+
diagParts.push(`${diagnostics.malformedSessionLines} malformed session line(s) skipped`);
|
|
328
|
+
if (diagnostics.malformedAuditRows > 0)
|
|
329
|
+
diagParts.push(`${diagnostics.malformedAuditRows} malformed audit row(s) skipped`);
|
|
330
|
+
for (const note of diagnostics.notes) diagParts.push(note);
|
|
331
|
+
if (diagParts.length > 0) process.stderr.write(`clio-coder usage: ${diagParts.join("; ")}
|
|
332
|
+
`);
|
|
333
|
+
const windowFrom = new Date(windowStart).toISOString();
|
|
334
|
+
const windowTo = new Date(now).toISOString();
|
|
335
|
+
if (parsed.json) {
|
|
336
|
+
const emit = (row) => {
|
|
337
|
+
process.stdout.write(
|
|
338
|
+
`${JSON.stringify({ schema: "experimental", windowDays: parsed.days, from: windowFrom, to: windowTo, ...row })}
|
|
339
|
+
`
|
|
340
|
+
);
|
|
341
|
+
};
|
|
342
|
+
if (presence.sessionsPresent) emit({ kind: "fact", fact: "sessions", value: sessions.length });
|
|
343
|
+
else emit({ kind: "fact", fact: "session-store-missing", path: presence.sessionsPath });
|
|
344
|
+
if (presence.receiptsPresent) emit({ kind: "fact", fact: "dispatch-runs", value: receipts.length });
|
|
345
|
+
else emit({ kind: "fact", fact: "receipt-store-missing", path: presence.receiptsPath });
|
|
346
|
+
emit({ kind: "fact", fact: "audit-tool-calls", value: auditToolCalls.length, blocked: auditBlocked.length });
|
|
347
|
+
if (presence.sessionsPresent) {
|
|
348
|
+
emit({ kind: "fact", fact: "tokens", ...usageTotals });
|
|
349
|
+
for (const row of usageRows) {
|
|
350
|
+
emit({ kind: "fact", fact: "model-usage", providerId: row.providerId, modelId: row.modelId, ...row.totals });
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
for (const [tool, totals] of topTools) emit({ kind: "fact", fact: "top-tool", tool, ...totals });
|
|
354
|
+
for (const [shape, count] of topShapes) {
|
|
355
|
+
emit({ kind: "fact", fact: "bash-shape", shape, count, sessions: shapeSessions.get(shape)?.size ?? 0 });
|
|
356
|
+
}
|
|
357
|
+
for (const [name, count] of [...activatedSkills.entries()].sort((a, b) => b[1] - a[1])) {
|
|
358
|
+
emit({ kind: "fact", fact: "skill-activated", skill: name, activations: count });
|
|
359
|
+
}
|
|
360
|
+
for (const name of neverActivated) emit({ kind: "fact", fact: "skill-never-activated", skill: name });
|
|
361
|
+
for (const [agentId, count] of [...recipeRuns.entries()].sort((a, b) => b[1] - a[1])) {
|
|
362
|
+
emit({ kind: "fact", fact: "recipe-used", agentId, runs: count });
|
|
363
|
+
}
|
|
364
|
+
for (const [tag, info] of [...tagCounts.entries()].sort((a, b) => b[1].count - a[1].count)) {
|
|
365
|
+
emit({ kind: "fact", fact: "failure-tag", tag, count: info.count, latestEvidenceId: info.latestEvidenceId });
|
|
366
|
+
}
|
|
367
|
+
emit({ kind: "fact", fact: "memory", approved: approvedMemory.length, pending: pendingMemory.length });
|
|
368
|
+
for (const opportunity of opportunities) {
|
|
369
|
+
emit({
|
|
370
|
+
kind: "opportunity",
|
|
371
|
+
opportunity: opportunity.kind,
|
|
372
|
+
suggestion: opportunity.suggestion,
|
|
373
|
+
evidence: opportunity.evidence
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
const out = (text) => {
|
|
379
|
+
process.stdout.write(`${text}
|
|
380
|
+
`);
|
|
381
|
+
};
|
|
382
|
+
out(
|
|
383
|
+
`usage report: last ${parsed.days} days (${windowFrom} -> ${windowTo})${repoPath === void 0 ? "" : ` repo=${repoPath}`}`
|
|
384
|
+
);
|
|
385
|
+
out("");
|
|
386
|
+
out("facts");
|
|
387
|
+
if (presence.sessionsPresent) {
|
|
388
|
+
out(` sessions in window: ${sessions.length} (${sessionsWithSkills.size} with skill activations)`);
|
|
389
|
+
} else {
|
|
390
|
+
out(` session store missing at ${presence.sessionsPath}`);
|
|
391
|
+
}
|
|
392
|
+
if (presence.receiptsPresent) {
|
|
393
|
+
out(` dispatch runs (receipts) in window: ${receipts.length}`);
|
|
394
|
+
} else {
|
|
395
|
+
out(` receipt store missing at ${presence.receiptsPath}`);
|
|
396
|
+
}
|
|
397
|
+
out(` audited tool calls in window: ${auditToolCalls.length} (${auditBlocked.length} blocked/denied)`);
|
|
398
|
+
if (presence.sessionsPresent) {
|
|
399
|
+
out(
|
|
400
|
+
` tokens in window: ${usageTotals.totalTokens} over ${usageTotals.apiCalls} model calls (input ${usageTotals.input}, output ${usageTotals.output}, cache read ${usageTotals.cacheRead}, cache write ${usageTotals.cacheWrite}, reasoning ${usageTotals.reasoningTokens})`
|
|
401
|
+
);
|
|
402
|
+
out(` provider-reported cost in window: $${usageTotals.costUsd.toFixed(4)}`);
|
|
403
|
+
}
|
|
404
|
+
if (usageRows.length > 0) {
|
|
405
|
+
out("");
|
|
406
|
+
out(" tokens by model (from session ledgers, provider-reported)");
|
|
407
|
+
process.stdout.write(
|
|
408
|
+
indent(
|
|
409
|
+
formatColumns([
|
|
410
|
+
["provider", "model", "calls", "input", "output", "cache read", "reasoning", "tokens", "cost"],
|
|
411
|
+
...usageRows.map((row) => [
|
|
412
|
+
row.providerId,
|
|
413
|
+
row.modelId,
|
|
414
|
+
String(row.totals.apiCalls),
|
|
415
|
+
String(row.totals.input),
|
|
416
|
+
String(row.totals.output),
|
|
417
|
+
String(row.totals.cacheRead),
|
|
418
|
+
String(row.totals.reasoningTokens),
|
|
419
|
+
String(row.totals.totalTokens),
|
|
420
|
+
`$${row.totals.costUsd.toFixed(4)}`
|
|
421
|
+
])
|
|
422
|
+
])
|
|
423
|
+
)
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
if (topTools.length > 0) {
|
|
427
|
+
out("");
|
|
428
|
+
out(" top tools (from receipt toolStats)");
|
|
429
|
+
process.stdout.write(
|
|
430
|
+
indent(
|
|
431
|
+
formatColumns([
|
|
432
|
+
["tool", "calls", "ok", "errors", "blocked"],
|
|
433
|
+
...topTools.map(([tool, totals]) => [
|
|
434
|
+
tool,
|
|
435
|
+
String(totals.count),
|
|
436
|
+
String(totals.ok),
|
|
437
|
+
String(totals.errors),
|
|
438
|
+
String(totals.blocked)
|
|
439
|
+
])
|
|
440
|
+
])
|
|
441
|
+
)
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
if (topShapes.length > 0) {
|
|
445
|
+
out("");
|
|
446
|
+
out(" top bash shapes (from session ledgers, arguments stripped)");
|
|
447
|
+
process.stdout.write(
|
|
448
|
+
indent(
|
|
449
|
+
formatColumns([
|
|
450
|
+
["shape", "calls", "sessions"],
|
|
451
|
+
...topShapes.map(([shape, count]) => [shape, String(count), String(shapeSessions.get(shape)?.size ?? 0)])
|
|
452
|
+
])
|
|
453
|
+
)
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
out("");
|
|
457
|
+
if (activatedSkills.size > 0) {
|
|
458
|
+
out(" skills activated");
|
|
459
|
+
process.stdout.write(
|
|
460
|
+
indent(
|
|
461
|
+
formatColumns([
|
|
462
|
+
["skill", "activations"],
|
|
463
|
+
...[...activatedSkills.entries()].sort((a, b) => b[1] - a[1]).map(([name, count]) => [name, String(count)])
|
|
464
|
+
])
|
|
465
|
+
)
|
|
466
|
+
);
|
|
467
|
+
} else {
|
|
468
|
+
out(" skills activated: none");
|
|
469
|
+
}
|
|
470
|
+
out(
|
|
471
|
+
` installed but never activated (${neverActivated.length}): ${neverActivated.length > 0 ? neverActivated.join(", ") : "none"}`
|
|
472
|
+
);
|
|
473
|
+
if (recipeRuns.size > 0) {
|
|
474
|
+
out("");
|
|
475
|
+
out(" dispatch recipes used");
|
|
476
|
+
process.stdout.write(
|
|
477
|
+
indent(
|
|
478
|
+
formatColumns([
|
|
479
|
+
["recipe", "runs"],
|
|
480
|
+
...[...recipeRuns.entries()].sort((a, b) => b[1] - a[1]).map(([agentId, count]) => [agentId, String(count)])
|
|
481
|
+
])
|
|
482
|
+
)
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
if (tagCounts.size > 0) {
|
|
486
|
+
out("");
|
|
487
|
+
out(" failure tags (from evidence index)");
|
|
488
|
+
process.stdout.write(
|
|
489
|
+
indent(
|
|
490
|
+
formatColumns([
|
|
491
|
+
["tag", "count", "latest evidence"],
|
|
492
|
+
...[...tagCounts.entries()].sort((a, b) => b[1].count - a[1].count).map(([tag, info]) => [tag, String(info.count), info.latestEvidenceId])
|
|
493
|
+
])
|
|
494
|
+
)
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
out("");
|
|
498
|
+
out(` memory records: ${approvedMemory.length} approved, ${pendingMemory.length} pending`);
|
|
499
|
+
out("");
|
|
500
|
+
out("opportunities");
|
|
501
|
+
const absentInputs = missingStoreNotes(presence);
|
|
502
|
+
if (opportunities.length === 0 && absentInputs.length > 0) {
|
|
503
|
+
out(` not computed: the inputs are absent (${absentInputs.join("; ")})`);
|
|
504
|
+
} else if (opportunities.length === 0) {
|
|
505
|
+
out(" none: no recurring unskilled bash shapes, repeated dispatch tasks, or unmemorized failure tags in window");
|
|
506
|
+
} else {
|
|
507
|
+
for (const opportunity of opportunities) {
|
|
508
|
+
out(` [${opportunity.kind}] ${opportunity.suggestion}`);
|
|
509
|
+
out(` evidence: ${opportunity.evidence}`);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return 0;
|
|
513
|
+
}
|
|
514
|
+
function indent(text) {
|
|
515
|
+
return text.split("\n").map((line) => line.length > 0 ? ` ${line}` : line).join("\n");
|
|
516
|
+
}
|
|
517
|
+
function inWindow(iso, start, end) {
|
|
518
|
+
const value = Date.parse(iso);
|
|
519
|
+
return Number.isFinite(value) && value >= start && value <= end + USAGE_WINDOW_FUTURE_SKEW_MS;
|
|
520
|
+
}
|
|
521
|
+
async function runIdsForCwd(stateDir, repoPath, diagnostics) {
|
|
522
|
+
try {
|
|
523
|
+
const raw = await readFile(join(stateDir, "runs.json"), "utf8");
|
|
524
|
+
const parsed = JSON.parse(raw);
|
|
525
|
+
if (!Array.isArray(parsed)) throw new Error("expected array");
|
|
526
|
+
const ids = /* @__PURE__ */ new Set();
|
|
527
|
+
for (const entry of parsed) {
|
|
528
|
+
if (!isRecord(entry)) continue;
|
|
529
|
+
if (typeof entry.id === "string" && entry.cwd === repoPath) ids.add(entry.id);
|
|
530
|
+
}
|
|
531
|
+
return ids;
|
|
532
|
+
} catch (error) {
|
|
533
|
+
diagnostics.notes.push(
|
|
534
|
+
`run ledger unreadable, --repo run filter skipped: ${error instanceof Error ? error.message : String(error)}`
|
|
535
|
+
);
|
|
536
|
+
return /* @__PURE__ */ new Set();
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
async function readReceipts(stateDir, windowStart, windowEnd, diagnostics) {
|
|
540
|
+
const root = join(stateDir, "receipts");
|
|
541
|
+
let files;
|
|
542
|
+
try {
|
|
543
|
+
files = (await readdir(root)).filter((name) => name.endsWith(".json"));
|
|
544
|
+
} catch {
|
|
545
|
+
return [];
|
|
546
|
+
}
|
|
547
|
+
if (files.length > RECEIPT_CAP) {
|
|
548
|
+
const withTimes = [];
|
|
549
|
+
for (const name of files) {
|
|
550
|
+
try {
|
|
551
|
+
withTimes.push({ name, mtimeMs: (await stat(join(root, name))).mtimeMs });
|
|
552
|
+
} catch {
|
|
553
|
+
diagnostics.malformedReceipts += 1;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
withTimes.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
557
|
+
const kept = withTimes.slice(0, RECEIPT_CAP);
|
|
558
|
+
process.stderr.write(
|
|
559
|
+
`clio-coder usage: receipt cap: reading newest ${RECEIPT_CAP} of ${files.length} receipts; oldest ${files.length - RECEIPT_CAP} truncated
|
|
560
|
+
`
|
|
561
|
+
);
|
|
562
|
+
files = kept.map((entry) => entry.name);
|
|
563
|
+
}
|
|
564
|
+
const receipts = [];
|
|
565
|
+
for (const name of files) {
|
|
566
|
+
let parsed;
|
|
567
|
+
try {
|
|
568
|
+
parsed = JSON.parse(await readFile(join(root, name), "utf8"));
|
|
569
|
+
} catch {
|
|
570
|
+
diagnostics.malformedReceipts += 1;
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
if (!isRecord(parsed) || typeof parsed.runId !== "string" || typeof parsed.endedAt !== "string") {
|
|
574
|
+
diagnostics.malformedReceipts += 1;
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
if (!inWindow(parsed.endedAt, windowStart, windowEnd)) continue;
|
|
578
|
+
const toolStats = Array.isArray(parsed.toolStats) ? parsed.toolStats.flatMap((entry) => {
|
|
579
|
+
if (!isRecord(entry) || typeof entry.tool !== "string") return [];
|
|
580
|
+
return [
|
|
581
|
+
{
|
|
582
|
+
tool: entry.tool,
|
|
583
|
+
count: numberOr0(entry.count),
|
|
584
|
+
ok: numberOr0(entry.ok),
|
|
585
|
+
errors: numberOr0(entry.errors),
|
|
586
|
+
blocked: numberOr0(entry.blocked)
|
|
587
|
+
}
|
|
588
|
+
];
|
|
589
|
+
}) : [];
|
|
590
|
+
const skillActivations = Array.isArray(parsed.skillActivations) ? parsed.skillActivations.flatMap((entry) => isRecord(entry) && typeof entry.name === "string" ? [entry.name] : []) : [];
|
|
591
|
+
receipts.push({
|
|
592
|
+
runId: parsed.runId,
|
|
593
|
+
agentId: typeof parsed.agentId === "string" ? parsed.agentId : "unknown",
|
|
594
|
+
task: typeof parsed.task === "string" ? parsed.task : "",
|
|
595
|
+
startedAt: typeof parsed.startedAt === "string" ? parsed.startedAt : parsed.endedAt,
|
|
596
|
+
endedAt: parsed.endedAt,
|
|
597
|
+
sessionId: typeof parsed.sessionId === "string" ? parsed.sessionId : null,
|
|
598
|
+
toolStats,
|
|
599
|
+
skillActivations
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
return receipts;
|
|
603
|
+
}
|
|
604
|
+
async function readSessions(stateDir, windowStart, windowEnd, repoHash, diagnostics) {
|
|
605
|
+
const refs = await listSessionLedgerRefs(stateDir, repoHash);
|
|
606
|
+
const sessions = [];
|
|
607
|
+
for (const ref of refs) {
|
|
608
|
+
let mtimeMs;
|
|
609
|
+
try {
|
|
610
|
+
mtimeMs = (await stat(ref.path)).mtimeMs;
|
|
611
|
+
} catch {
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
if (mtimeMs < windowStart) continue;
|
|
615
|
+
let raw;
|
|
616
|
+
try {
|
|
617
|
+
raw = await readFile(ref.path, "utf8");
|
|
618
|
+
} catch {
|
|
619
|
+
diagnostics.unreadableSessions += 1;
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
const parsedEntries = parseSessionEntries(raw, ref.path);
|
|
623
|
+
diagnostics.malformedSessionLines += parsedEntries.errors.length;
|
|
624
|
+
const usage = {
|
|
625
|
+
sessionId: ref.sessionId,
|
|
626
|
+
cwdHash: ref.cwdHash,
|
|
627
|
+
bashShapes: /* @__PURE__ */ new Map(),
|
|
628
|
+
skillActivations: /* @__PURE__ */ new Set(),
|
|
629
|
+
entriesInWindow: 0,
|
|
630
|
+
// modelChange rows are kept regardless of the window: they carry no
|
|
631
|
+
// usage of their own, and dropping the ones that predate the window
|
|
632
|
+
// would attribute in-window calls to the wrong target.
|
|
633
|
+
usageCalls: ledgerUsageCalls(
|
|
634
|
+
parsedEntries.entries.filter(
|
|
635
|
+
(entry) => entry.kind === "modelChange" || inWindow(entry.timestamp, windowStart, windowEnd)
|
|
636
|
+
)
|
|
637
|
+
)
|
|
638
|
+
};
|
|
639
|
+
for (const entry of parsedEntries.entries) {
|
|
640
|
+
if (!inWindow(entry.timestamp, windowStart, windowEnd)) continue;
|
|
641
|
+
usage.entriesInWindow += 1;
|
|
642
|
+
if (entry.kind === "skillActivation") {
|
|
643
|
+
usage.skillActivations.add(entry.activation.name);
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
let command = null;
|
|
647
|
+
if (entry.kind === "bashExecution") {
|
|
648
|
+
command = entry.command;
|
|
649
|
+
} else if (entry.kind === "message" && entry.role === "tool_call") {
|
|
650
|
+
command = bashCommandFromToolCallPayload(entry.payload);
|
|
651
|
+
}
|
|
652
|
+
if (command !== null) {
|
|
653
|
+
const shape = bashShape(command);
|
|
654
|
+
if (shape.length > 0) usage.bashShapes.set(shape, (usage.bashShapes.get(shape) ?? 0) + 1);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
if (usage.entriesInWindow > 0) sessions.push(usage);
|
|
658
|
+
}
|
|
659
|
+
return sessions;
|
|
660
|
+
}
|
|
661
|
+
function bashCommandFromToolCallPayload(payload) {
|
|
662
|
+
if (!isRecord(payload)) return null;
|
|
663
|
+
const tool = firstString(payload.name, payload.toolName, payload.tool);
|
|
664
|
+
if (tool !== "bash") return null;
|
|
665
|
+
const args = payload.arguments ?? payload.args ?? payload.input;
|
|
666
|
+
const argRecord = isRecord(args) ? args : maybeJsonRecord(args);
|
|
667
|
+
const command = argRecord?.command;
|
|
668
|
+
return typeof command === "string" && command.length > 0 ? command : null;
|
|
669
|
+
}
|
|
670
|
+
function bashShape(command) {
|
|
671
|
+
const firstLine = command.split("\n").find((line) => {
|
|
672
|
+
const trimmed = line.trim();
|
|
673
|
+
return trimmed.length > 0 && !trimmed.startsWith("#");
|
|
674
|
+
});
|
|
675
|
+
if (firstLine === void 0) return "";
|
|
676
|
+
const firstSegment = firstLine.split(/&&|\|\||;|\|/)[0] ?? "";
|
|
677
|
+
const tokens = firstSegment.trim().split(/\s+/).filter((token) => token.length > 0);
|
|
678
|
+
const out = [];
|
|
679
|
+
let positionals = 0;
|
|
680
|
+
for (const token of tokens) {
|
|
681
|
+
if (positionals === 0 && /^[A-Za-z_][A-Za-z0-9_]*=/.test(token)) continue;
|
|
682
|
+
if (token.startsWith("-")) {
|
|
683
|
+
const flag = token.split("=")[0] ?? token;
|
|
684
|
+
out.push(flag);
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
if (positionals < 2) {
|
|
688
|
+
const clean = positionals === 0 ? token.split("/").at(-1) ?? token : token;
|
|
689
|
+
if (positionals === 1 && !/^[A-Za-z][A-Za-z0-9._-]*$/.test(clean)) {
|
|
690
|
+
positionals += 1;
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
out.push(clean);
|
|
694
|
+
positionals += 1;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return out.join(" ");
|
|
698
|
+
}
|
|
699
|
+
var TRIVIAL_SHAPE_VERBS = /* @__PURE__ */ new Set([
|
|
700
|
+
"cd",
|
|
701
|
+
"ls",
|
|
702
|
+
"pwd",
|
|
703
|
+
"cat",
|
|
704
|
+
"echo",
|
|
705
|
+
"wc",
|
|
706
|
+
"which",
|
|
707
|
+
"head",
|
|
708
|
+
"tail",
|
|
709
|
+
"mkdir",
|
|
710
|
+
"cp",
|
|
711
|
+
"mv",
|
|
712
|
+
"touch",
|
|
713
|
+
"true",
|
|
714
|
+
"env",
|
|
715
|
+
"date"
|
|
716
|
+
]);
|
|
717
|
+
function isTrivialShape(shape) {
|
|
718
|
+
const verb = shape.split(" ")[0] ?? "";
|
|
719
|
+
return TRIVIAL_SHAPE_VERBS.has(verb);
|
|
720
|
+
}
|
|
721
|
+
function memoryMentions(records, tag) {
|
|
722
|
+
const needle = tag.toLowerCase();
|
|
723
|
+
return records.some((record) => {
|
|
724
|
+
if (record.rejectedAt !== void 0) return false;
|
|
725
|
+
const haystacks = [record.key, record.lesson, ...record.appliesWhen, ...record.evidenceRefs];
|
|
726
|
+
return haystacks.some((text) => text.toLowerCase().includes(needle));
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
function firstString(...values) {
|
|
730
|
+
for (const value of values) {
|
|
731
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
732
|
+
}
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
function maybeJsonRecord(value) {
|
|
736
|
+
if (typeof value !== "string") return null;
|
|
737
|
+
try {
|
|
738
|
+
const parsed = JSON.parse(value);
|
|
739
|
+
return isRecord(parsed) ? parsed : null;
|
|
740
|
+
} catch {
|
|
741
|
+
return null;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
function numberOr0(value) {
|
|
745
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
746
|
+
}
|
|
747
|
+
function isRecord(value) {
|
|
748
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
749
|
+
}
|
|
750
|
+
export {
|
|
751
|
+
bashShape,
|
|
752
|
+
inWindow,
|
|
753
|
+
runUsageCommand
|
|
754
|
+
};
|
|
755
|
+
//# sourceMappingURL=usage-ULIDAGFF.js.map
|