@xhulz/nina 0.32.0 → 0.33.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 +5 -3
- package/package.json +1 -1
- package/releases/0.33.0/core/GAPS.md +125 -0
- package/releases/0.33.0/core/answered.json +29 -0
- package/releases/0.33.0/core/defaults/tree/.claude/agents/architect.md +1 -0
- package/releases/0.33.0/core/defaults/tree/.claude/agents/dba.md +3 -0
- package/releases/0.33.0/core/defaults/tree/.claude/agents/devops.md +1 -0
- package/releases/0.33.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
- package/releases/0.33.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.33.0/core/defaults/tree/.claude/agents/qa.md +2 -0
- package/releases/0.33.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
- package/releases/0.33.0/core/defaults/tree/.claude/agents/secops.md +3 -0
- package/releases/0.33.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
- package/releases/0.33.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
- package/releases/0.33.0/core/tree/.claude/agents/architect.md +127 -0
- package/releases/0.33.0/core/tree/.claude/agents/dba.md +110 -0
- package/releases/0.33.0/core/tree/.claude/agents/devops.md +114 -0
- package/releases/0.33.0/core/tree/.claude/agents/implementer.md +156 -0
- package/releases/0.33.0/core/tree/.claude/agents/integration-tester.md +168 -0
- package/releases/0.33.0/core/tree/.claude/agents/planner.md +132 -0
- package/releases/0.33.0/core/tree/.claude/agents/qa.md +148 -0
- package/releases/0.33.0/core/tree/.claude/agents/reviewer.md +183 -0
- package/releases/0.33.0/core/tree/.claude/agents/secops.md +121 -0
- package/releases/0.33.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
- package/releases/0.33.0/core/tree/.claude/agents/solidity-dev.md +123 -0
- package/releases/0.33.0/core/tree/.claude/agents-overview.md +64 -0
- package/releases/0.33.0/core/tree/.claude/frontend.md +10 -0
- package/releases/0.33.0/core/tree/.claude/graph.md +75 -0
- package/releases/0.33.0/core/tree/.claude/patterns.md +213 -0
- package/releases/0.33.0/core/tree/.claude/pills/README.md +146 -0
- package/releases/0.33.0/core/tree/.claude/pipeline.md +101 -0
- package/releases/0.33.0/core/tree/.claude/retrieval.md +103 -0
- package/releases/0.33.0/core/tree/.claude/router.md +198 -0
- package/releases/0.33.0/core/tree/.claude/templates/integration.md +45 -0
- package/releases/0.33.0/core/tree/CLAUDE.md +161 -0
- package/releases/0.33.0/core/tree/scripts/cost-watch.mjs +18 -0
- package/releases/0.33.0/core/tree/scripts/edit-guard.mjs +16 -0
- package/releases/0.33.0/core/tree/scripts/harness-check.mjs +71 -0
- package/releases/0.33.0/core/tree/scripts/loop-gate.mjs +19 -0
- package/releases/0.33.0/core/vocabulary.json +13 -0
- package/releases/0.33.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
- package/releases/0.33.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
- package/releases/0.33.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
- package/releases/0.33.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
- package/releases/0.33.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
- package/releases/0.33.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
- package/releases/0.33.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
- package/releases/0.33.0/surfaces/blockchain/tree/.claude/router.md +6 -0
- package/releases/0.33.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
- package/releases/0.33.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
- package/releases/0.33.0/surfaces/db/tree/.claude/agents/dba.md +50 -0
- package/releases/0.33.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
- package/releases/0.33.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.33.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
- package/releases/0.33.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
- package/releases/0.33.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
- package/releases/0.33.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
- package/releases/0.33.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
- package/releases/0.33.0/surfaces/db/tree/.claude/graph.md +8 -0
- package/releases/0.33.0/surfaces/db/tree/.claude/patterns.md +19 -0
- package/releases/0.33.0/surfaces/db/tree/.claude/pipeline.md +13 -0
- package/releases/0.33.0/surfaces/db/tree/.claude/retrieval.md +5 -0
- package/releases/0.33.0/surfaces/db/tree/.claude/router.md +4 -0
- package/releases/0.33.0/surfaces/db/tree/CLAUDE.md +17 -0
- package/releases/0.33.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
- package/releases/0.33.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
- package/releases/0.33.0/surfaces/edge-cf/tree/.claude/agents/devops.md +32 -0
- package/releases/0.33.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
- package/releases/0.33.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
- package/releases/0.33.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
- package/releases/0.33.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.33.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
- package/releases/0.33.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
- package/releases/0.33.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
- package/releases/0.33.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
- package/releases/0.33.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
- package/releases/0.33.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
- package/releases/0.33.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
- package/releases/0.33.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
- package/releases/0.33.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
- package/releases/0.33.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
- package/releases/0.33.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
- package/releases/0.33.0/surfaces/frontend/tree/.claude/router.md +14 -0
- package/releases/0.33.0/surfaces/frontend/tree/CLAUDE.md +5 -0
- package/releases/0.33.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
- package/releases/0.33.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
- package/releases/0.33.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
- package/releases/0.33.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
- package/releases/0.33.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
- package/releases/0.33.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
- package/releases/0.33.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
- package/releases/0.33.0/surfaces/integrations/tree/.claude/graph.md +10 -0
- package/releases/0.33.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
- package/releases/0.33.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
- package/releases/0.33.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
- package/releases/0.33.0/surfaces/integrations/tree/.claude/router.md +6 -0
- package/releases/0.33.0/surfaces/integrations/tree/CLAUDE.md +17 -0
- package/releases/0.33.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
- package/releases/0.33.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
- package/releases/0.33.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
- package/releases/0.33.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
- package/releases/0.33.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
- package/releases/0.33.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
- package/releases/0.33.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
- package/releases/0.33.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
- package/releases/0.33.0/surfaces/money/tree/.claude/graph.md +2 -0
- package/releases/0.33.0/surfaces/money/tree/.claude/patterns.md +49 -0
- package/releases/0.33.0/surfaces/money/tree/.claude/pipeline.md +2 -0
- package/releases/0.33.0/surfaces/money/tree/.claude/retrieval.md +2 -0
- package/releases/0.33.0/surfaces/money/tree/CLAUDE.md +11 -0
- package/releases/0.33.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
- package/releases/0.33.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
- package/releases/0.33.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
- package/releases/0.33.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
- package/releases/0.33.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
- package/releases/0.33.0/surfaces/pii/tree/CLAUDE.md +5 -0
- package/releases/0.33.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
- package/releases/0.33.0/surfaces/prisma/tree/.claude/agents/dba.md +39 -0
- package/releases/0.33.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
- package/releases/0.33.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
- package/releases/0.33.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
- package/releases/0.33.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
- package/releases/0.33.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
- package/releases/0.33.0/surfaces/prisma/tree/CLAUDE.md +5 -0
- package/src/commands/check.mjs +3 -1
- package/src/commands/runs.mjs +7 -2
- package/src/commands/stats.mjs +35 -0
- package/src/commands/upgrade.mjs +12 -3
- package/src/expected.mjs +4 -3
- package/src/transcripts.mjs +27 -1
package/src/commands/stats.mjs
CHANGED
|
@@ -223,6 +223,40 @@ function costReport(records) {
|
|
|
223
223
|
console.log(bold(` ${'all stages'.padEnd(20)}${String([...byRole.values()].flat().length).padStart(6)}${''.padStart(10)}${money(total).padStart(10)}`));
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
/**
|
|
227
|
+
* How each stage's rounds spent their context: turns, tool calls sent per turn, and the context a turn
|
|
228
|
+
* re-read when the round began and at its largest. Every turn is billed the whole context again, so the
|
|
229
|
+
* cost of a round is its turns times its context, and a round that starts where a long one ended pays for
|
|
230
|
+
* all of it on every turn. The first rounds of a run are set beside the later ones, which are resumes.
|
|
231
|
+
*
|
|
232
|
+
* @param {object[]} records - The rounds in the window.
|
|
233
|
+
*/
|
|
234
|
+
function contextReport(records) {
|
|
235
|
+
const shaped = records.filter((r) => typeof r.turns === 'number' && r.turns > 0);
|
|
236
|
+
if (shaped.length === 0) return;
|
|
237
|
+
const median = (xs) => [...xs].sort((a, b) => a - b)[Math.floor(xs.length / 2)];
|
|
238
|
+
const k = (n) => (typeof n === 'number' ? `${Math.round(n / 1000)}k` : '—');
|
|
239
|
+
console.log(heading('context', 'every turn re-reads the whole context: a round costs its turns times its size, and a resumed one starts where it ended'));
|
|
240
|
+
console.log(dim(` ${'stage'.padEnd(20)}${'rounds'.padEnd(8)}${'n'.padStart(4)}${'turns'.padStart(7)}${'calls/turn'.padStart(12)}${'starts at'.padStart(11)}${'peak'.padStart(8)}`));
|
|
241
|
+
const byRole = new Map();
|
|
242
|
+
for (const r of shaped) byRole.set(r.role, [...(byRole.get(r.role) ?? []), r]);
|
|
243
|
+
for (const [role, rounds] of [...byRole].sort((a, b) => b[1].reduce((x, r) => x + r.turns, 0) - a[1].reduce((x, r) => x + r.turns, 0))) {
|
|
244
|
+
[
|
|
245
|
+
['first', rounds.filter((r) => (r.round ?? 1) === 1)],
|
|
246
|
+
['resumed', rounds.filter((r) => (r.round ?? 1) > 1)],
|
|
247
|
+
].forEach(([label, group], i) => {
|
|
248
|
+
if (group.length === 0) return;
|
|
249
|
+
const turns = group.reduce((a, r) => a + r.turns, 0);
|
|
250
|
+
const calls = group.reduce((a, r) => a + (r.tool_calls ?? 0), 0);
|
|
251
|
+
const name = i === 0 || !rounds.some((r) => (r.round ?? 1) === 1) ? pink(role) + ' '.repeat(Math.max(20 - role.length, 1)) : ' '.repeat(20);
|
|
252
|
+
console.log(
|
|
253
|
+
` ${name}${dim(label.padEnd(8))}${String(group.length).padStart(4)}${String(median(group.map((r) => r.turns))).padStart(7)}` +
|
|
254
|
+
`${(calls / turns).toFixed(2).padStart(12)}${k(median(group.map((r) => r.context_start ?? 0))).padStart(11)}${k(median(group.map((r) => r.context_peak ?? 0))).padStart(8)}`,
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
226
260
|
/**
|
|
227
261
|
* Whether a model a run used is the one a spec declares: an alias (`opus`) names a family — a whole
|
|
228
262
|
* segment of the id, wherever it sits, so `claude-3-sonnet` is a sonnet — anything else names a model
|
|
@@ -722,6 +756,7 @@ export async function stats(argv, ctx) {
|
|
|
722
756
|
}
|
|
723
757
|
|
|
724
758
|
costReport(records.filter((r) => r.status !== 'denied'));
|
|
759
|
+
contextReport(records.filter((r) => r.status !== 'denied'));
|
|
725
760
|
modelReport(records.filter((r) => r.status !== 'denied'));
|
|
726
761
|
proportionReport(records.filter((r) => r.status !== 'denied'), stepLimitOf(ctx));
|
|
727
762
|
|
package/src/commands/upgrade.mjs
CHANGED
|
@@ -37,7 +37,8 @@ const BANNER = /^[\s█╗╔╝║═╚▄▀]*$|harness orchestration ·/;
|
|
|
37
37
|
* fault — everything else it finds still rolls the move back.
|
|
38
38
|
*
|
|
39
39
|
* @param {string} target - The project.
|
|
40
|
-
* @param {string[]} newSlots - Project slots this move introduces, as `<file> <slot id
|
|
40
|
+
* @param {string[]} newSlots - Project slots this move introduces, as `<file> <slot id>`, and the
|
|
41
|
+
* vocabulary names it introduces, as `{{NAME}}`.
|
|
41
42
|
* @returns {{label: string, args: string[]}[]}
|
|
42
43
|
*/
|
|
43
44
|
function validationsFor(target, newSlots = []) {
|
|
@@ -242,6 +243,10 @@ export async function upgrade(argv, ctx) {
|
|
|
242
243
|
// A slot the new release fills itself is not asked of the project: it composes as the release wrote it.
|
|
243
244
|
const defaultedAfter = await defaultedSlots(onto.dir);
|
|
244
245
|
const newSlots = [...slotsAfter].filter((s) => !slotsBefore.has(s) && !filled.has(s) && !defaultedAfter.has(s)).sort();
|
|
246
|
+
// A name only the new release uses is owed the way a new slot is: it cannot be filled with meaning
|
|
247
|
+
// before the core that asks for it is pinned, and rolling back for it left no order the move completes in.
|
|
248
|
+
const newVocab = needed.filter((v) => !vocabBefore.has(v));
|
|
249
|
+
const awaited = [...newSlots, ...newVocab.map((v) => `{{${v}}}`)];
|
|
245
250
|
const stranded = [...filled].filter((s) => !slotsAfter.has(s)).sort();
|
|
246
251
|
|
|
247
252
|
console.log(` ${profile.core} → ${to}\n`);
|
|
@@ -357,7 +362,7 @@ export async function upgrade(argv, ctx) {
|
|
|
357
362
|
// What was already failing is not this upgrade's doing, and rolling back for it would blame
|
|
358
363
|
// the move for a problem the project brought with it. Measured before anything is written,
|
|
359
364
|
// so the only rollback is for something that WAS passing and now is not.
|
|
360
|
-
const VALIDATIONS = validationsFor(target,
|
|
365
|
+
const VALIDATIONS = validationsFor(target, awaited);
|
|
361
366
|
const DETECTORS = "running the project's own detectors";
|
|
362
367
|
const hasDetectors = await declares(target, 'harness:check');
|
|
363
368
|
|
|
@@ -416,7 +421,7 @@ export async function upgrade(argv, ctx) {
|
|
|
416
421
|
// `harness:check` — an npm script whose detector list this command does not own and cannot
|
|
417
422
|
// pass an argument to. The environment is what crosses that boundary, and setting it once
|
|
418
423
|
// means a reporter added to the chain later is told without anyone remembering to tell it.
|
|
419
|
-
if (
|
|
424
|
+
if (awaited.length > 0) process.env[EXPECT_ENV] = awaited.join(',');
|
|
420
425
|
|
|
421
426
|
// Compose writes and never deletes. So the files the new version stops composing are removed right
|
|
422
427
|
// after it composes — before anything is verified, so what is verified is what stays — unless they
|
|
@@ -516,6 +521,10 @@ export async function upgrade(argv, ctx) {
|
|
|
516
521
|
console.log(`\n ${newSlots.length} project slot(s) still to fill — only this project can write them:`);
|
|
517
522
|
for (const s of newSlots) console.log(` ${s}`);
|
|
518
523
|
}
|
|
524
|
+
if (newVocab.length > 0) {
|
|
525
|
+
console.log(`\n ${newVocab.length} vocabulary name(s) ${to} asks for and this profile does not answer — fill each, or defer it with why:`);
|
|
526
|
+
console.log(` ${newVocab.join(', ')}`);
|
|
527
|
+
}
|
|
519
528
|
console.log('');
|
|
520
529
|
return 0;
|
|
521
530
|
}
|
package/src/expected.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The project slots a caller has declared it already knows are open.
|
|
2
|
+
* The project slots, and vocabulary names, a caller has declared it already knows are open.
|
|
3
3
|
*
|
|
4
4
|
* `nina upgrade --apply` creates slots: a new core can introduce one, and it cannot be filled
|
|
5
|
-
* before the core that introduces it is pinned.
|
|
5
|
+
* before the core that introduces it is pinned. A name only the new core uses is the same case,
|
|
6
|
+
* named as `{{NAME}}`: `check` rolled a move back for one the preview had just listed as needed. Every reporter that fails on an unfilled slot
|
|
6
7
|
* therefore has to be told, or the move deadlocks — there is no order in which it completes.
|
|
7
8
|
*
|
|
8
9
|
* Two reporters say this same thing, and the first fix taught only one: `check` counts a slot
|
|
@@ -21,7 +22,7 @@ export const EXPECT_ENV = 'NINA_EXPECT_UNFILLED';
|
|
|
21
22
|
/**
|
|
22
23
|
* @param {string[]} argv - The command's arguments; `--expect-unfilled <a,b>` names slots.
|
|
23
24
|
* @param {NodeJS.ProcessEnv} [env] - Where to read `NINA_EXPECT_UNFILLED` from.
|
|
24
|
-
* @returns {Set<string>} Slot ids as `<relative path> <slot id
|
|
25
|
+
* @returns {Set<string>} Slot ids as `<relative path> <slot id>`, names as `{{NAME}}`.
|
|
25
26
|
*/
|
|
26
27
|
export function expectedUnfilled(argv = [], env = process.env) {
|
|
27
28
|
const fromArgv = argv.includes('--expect-unfilled') ? argv[argv.indexOf('--expect-unfilled') + 1] ?? '' : '';
|
package/src/transcripts.mjs
CHANGED
|
@@ -626,9 +626,13 @@ async function attachAgentDetail(projectDir, dispatches) {
|
|
|
626
626
|
// 744 of one project's 826 in full on every turn, two seconds before the person saw an answer.
|
|
627
627
|
const size = (await stat(join(dir, file)).catch(() => null))?.size ?? null;
|
|
628
628
|
const settled = (r) => r.handed_back === false || String(r.verdict_source ?? '').startsWith('handback');
|
|
629
|
+
// `resumes` is written only by a reader from before rounds, which bills the whole run to its first
|
|
630
|
+
// record. A store both versions wrote to — one project on the old pin, a hook elsewhere on the new —
|
|
631
|
+
// had its runs marked read by the old one and skipped here, their first round billed twice.
|
|
629
632
|
if (
|
|
630
633
|
run.agent_read_bytes === size &&
|
|
631
|
-
|
|
634
|
+
!('resumes' in run) &&
|
|
635
|
+
[...records, ...later].every((r) => settled(r) && 'round' in r && 'lessons_read' in r && 'tokens' in r && 'files_touched' in r && 'effort' in r && 'turns' in r)
|
|
632
636
|
) {
|
|
633
637
|
continue;
|
|
634
638
|
}
|
|
@@ -656,6 +660,8 @@ async function attachAgentDetail(projectDir, dispatches) {
|
|
|
656
660
|
usage: new Map(),
|
|
657
661
|
/** The files the round wrote through the edit tools. Only their number is kept. */
|
|
658
662
|
written: new Set(),
|
|
663
|
+
/** The tool calls the round made, by id: over its turns, how many calls each turn sent at once. */
|
|
664
|
+
calls: new Set(),
|
|
659
665
|
});
|
|
660
666
|
const rounds = [fresh()];
|
|
661
667
|
const roundOf = roundsOf();
|
|
@@ -677,6 +683,7 @@ async function attachAgentDetail(projectDir, dispatches) {
|
|
|
677
683
|
const round = rounds[n - 1];
|
|
678
684
|
const hasSkill = line.includes('"Skill"');
|
|
679
685
|
const hasHandback = line.includes('SubagentHandback');
|
|
686
|
+
for (const m of line.matchAll(/"type":"tool_use","id":"([^"]+)"/g)) round.calls.add(m[1]);
|
|
680
687
|
if (/"name":"(Edit|Write|MultiEdit|NotebookEdit)"/.test(line)) {
|
|
681
688
|
try {
|
|
682
689
|
for (const block of JSON.parse(line)?.message?.content ?? []) {
|
|
@@ -755,9 +762,14 @@ async function attachAgentDetail(projectDir, dispatches) {
|
|
|
755
762
|
targets = [dispatches.get(id)];
|
|
756
763
|
}
|
|
757
764
|
const spent = tokensOf(round.usage);
|
|
765
|
+
const shape = contextOf(round.usage);
|
|
758
766
|
targets.forEach((record, j) => {
|
|
759
767
|
// One transcript, one bill: were two records ever to share an agent, the second would count it again.
|
|
760
768
|
record.tokens = j === 0 ? spent.tokens : null;
|
|
769
|
+
record.turns = shape.turns;
|
|
770
|
+
record.tool_calls = round.calls.size;
|
|
771
|
+
record.context_start = shape.start;
|
|
772
|
+
record.context_peak = shape.peak;
|
|
761
773
|
record.usage_model = spent.model;
|
|
762
774
|
record.effort = spent.effort;
|
|
763
775
|
record.files_touched = round.written.size;
|
|
@@ -790,6 +802,20 @@ async function attachAgentDetail(projectDir, dispatches) {
|
|
|
790
802
|
}
|
|
791
803
|
}
|
|
792
804
|
|
|
805
|
+
/**
|
|
806
|
+
* How a round spent its context: how many turns it took, and how large the context each turn re-read was
|
|
807
|
+
* when the round began and at its largest. Every turn is billed the whole context again, so a round that
|
|
808
|
+
* begins where a long one ended — a run resumed for a fix — pays for everything before it on each turn.
|
|
809
|
+
*
|
|
810
|
+
* @param {Map<string, {usage: object}>} usage - Message id → its final usage, in the order the round sent them.
|
|
811
|
+
* @returns {{turns: number, start: number|null, peak: number|null}} Context sizes in tokens.
|
|
812
|
+
*/
|
|
813
|
+
export function contextOf(usage) {
|
|
814
|
+
const n = (v) => (typeof v === 'number' ? v : 0);
|
|
815
|
+
const sizes = [...usage.values()].map(({ usage: u }) => n(u.input_tokens) + n(u.cache_read_input_tokens) + n(u.cache_creation_input_tokens));
|
|
816
|
+
return { turns: sizes.length, start: sizes[0] ?? null, peak: sizes.length > 0 ? Math.max(...sizes) : null };
|
|
817
|
+
}
|
|
818
|
+
|
|
793
819
|
/**
|
|
794
820
|
* What a run spent, from each of its messages' final usage: tokens by kind, and the model and effort
|
|
795
821
|
* level that most of its messages ran at. Metadata — counts, a model id and a level, never a message.
|