@tangle-network/traces 0.9.0 → 0.9.2
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 +8 -4
- package/dist/{chunk-5SBXL62V.js → chunk-RUG26PIS.js} +716 -574
- package/dist/cli.js +67 -87
- package/dist/index.d.ts +15 -15
- package/dist/index.js +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -3,9 +3,7 @@ import {
|
|
|
3
3
|
ACTOR_ATTR,
|
|
4
4
|
ATTR,
|
|
5
5
|
CORRUPTION_RECEIPT_DISPLAY_LIMIT,
|
|
6
|
-
|
|
7
|
-
analyzeReactions,
|
|
8
|
-
analyzeSpans,
|
|
6
|
+
appendAll,
|
|
9
7
|
buildPolicyEvidenceRecord,
|
|
10
8
|
buildSessionIndexFromRows,
|
|
11
9
|
commandAnalyzer,
|
|
@@ -23,14 +21,8 @@ import {
|
|
|
23
21
|
parseSince,
|
|
24
22
|
planUpload,
|
|
25
23
|
readSessionIndexFile,
|
|
26
|
-
renderAdoption,
|
|
27
24
|
renderInspectionReport,
|
|
28
|
-
renderPipelines,
|
|
29
|
-
renderReactions,
|
|
30
|
-
renderReport,
|
|
31
25
|
resolveAdapter,
|
|
32
|
-
runExternalAnalyzers,
|
|
33
|
-
runPipelines,
|
|
34
26
|
runTraceImprovement,
|
|
35
27
|
runTraceInvestigation,
|
|
36
28
|
saveReport,
|
|
@@ -41,7 +33,6 @@ import {
|
|
|
41
33
|
serializeTraceStreamEvent,
|
|
42
34
|
sessionIdFromAttributes,
|
|
43
35
|
streamSessions,
|
|
44
|
-
summarizeDeterministicSignals,
|
|
45
36
|
traceStreamEventsFromSpans,
|
|
46
37
|
watchSessions,
|
|
47
38
|
writeInspectionReportFile,
|
|
@@ -49,11 +40,11 @@ import {
|
|
|
49
40
|
writePolicyEvidenceFile,
|
|
50
41
|
writeSessionIndexFile,
|
|
51
42
|
writeTraceEvidenceExportFile
|
|
52
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-RUG26PIS.js";
|
|
53
44
|
|
|
54
45
|
// src/cli.ts
|
|
55
46
|
import { readFileSync } from "fs";
|
|
56
|
-
import { readFile, stat
|
|
47
|
+
import { readFile, stat } from "fs/promises";
|
|
57
48
|
import { basename, resolve } from "path";
|
|
58
49
|
function packageVersion() {
|
|
59
50
|
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
@@ -217,6 +208,50 @@ async function discover(args) {
|
|
|
217
208
|
}
|
|
218
209
|
return out;
|
|
219
210
|
}
|
|
211
|
+
function missingPath(error) {
|
|
212
|
+
return typeof error === "object" && error !== null && error.code === "ENOENT";
|
|
213
|
+
}
|
|
214
|
+
async function resolveSelectedSession(args) {
|
|
215
|
+
if (!args.session) throw new Error("resolveSelectedSession requires --session");
|
|
216
|
+
const adapter = resolveAdapter(args.harness);
|
|
217
|
+
if (!adapter && !args.all) throw new Error(`unknown harness "${args.harness}"`);
|
|
218
|
+
try {
|
|
219
|
+
const st = await stat(args.session);
|
|
220
|
+
if (!adapter) {
|
|
221
|
+
throw new Error("--session <path> requires --harness when --all is set");
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
adapter,
|
|
225
|
+
ref: {
|
|
226
|
+
harness: adapter.harness,
|
|
227
|
+
sessionId: args.session,
|
|
228
|
+
path: args.session,
|
|
229
|
+
// --session is an explicit file; honor --cwd so adoption can find the
|
|
230
|
+
// project's .evolve/skill-runs.jsonl (locate() infers cwd in the scan path).
|
|
231
|
+
cwd: args.cwd ?? null,
|
|
232
|
+
mtimeMs: st.mtimeMs
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
} catch (error) {
|
|
236
|
+
if (!missingPath(error)) throw error;
|
|
237
|
+
}
|
|
238
|
+
const matches = [];
|
|
239
|
+
for (const candidate of args.all ? adaptersFor(args) : [adapter]) {
|
|
240
|
+
const refs = await locateSessions(candidate, { cwd: args.cwd });
|
|
241
|
+
for (const ref of refs) {
|
|
242
|
+
if (ref.sessionId === args.session) matches.push({ adapter: candidate, ref });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (matches.length === 1) return matches[0];
|
|
246
|
+
if (matches.length === 0) {
|
|
247
|
+
throw new Error(
|
|
248
|
+
`no ${args.all ? "selected" : adapter.harness} session with ID "${args.session}"; run \`traces list\` to select an ID, or pass an explicit session path`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
throw new Error(
|
|
252
|
+
`session ID "${args.session}" is ambiguous across ${matches.length} files; select one harness or pass its explicit session path`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
220
255
|
async function buildAxService() {
|
|
221
256
|
const apiKey = process.env.OPENAI_API_KEY;
|
|
222
257
|
if (!apiKey) {
|
|
@@ -226,7 +261,7 @@ async function buildAxService() {
|
|
|
226
261
|
}
|
|
227
262
|
const apiURL = process.env.OPENAI_BASE_URL || void 0;
|
|
228
263
|
const { AxAI } = await import("@ax-llm/ax");
|
|
229
|
-
return
|
|
264
|
+
return AxAI.create({ name: "openai", apiKey, ...apiURL ? { apiURL } : {} });
|
|
230
265
|
}
|
|
231
266
|
async function cmdList(args) {
|
|
232
267
|
const groups = await discover({ ...args, last: args.last || 20 });
|
|
@@ -274,18 +309,7 @@ function selectedSessionSource(ref, spans, sessionIdOverride) {
|
|
|
274
309
|
async function collectSpans(args) {
|
|
275
310
|
if (args.input) return collectImportedSpans(args);
|
|
276
311
|
if (args.session) {
|
|
277
|
-
const adapter =
|
|
278
|
-
if (!adapter) throw new Error(`unknown harness "${args.harness}"`);
|
|
279
|
-
const st = await stat(args.session);
|
|
280
|
-
const ref = {
|
|
281
|
-
harness: adapter.harness,
|
|
282
|
-
sessionId: args.session,
|
|
283
|
-
path: args.session,
|
|
284
|
-
// --session is an explicit file; honor --cwd so adoption can find the
|
|
285
|
-
// project's .evolve/skill-runs.jsonl (locate() infers cwd in the scan path).
|
|
286
|
-
cwd: args.cwd ?? null,
|
|
287
|
-
mtimeMs: st.mtimeMs
|
|
288
|
-
};
|
|
312
|
+
const { adapter, ref } = await resolveSelectedSession(args);
|
|
289
313
|
const spans2 = await parseSession(adapter, ref);
|
|
290
314
|
return {
|
|
291
315
|
spans: spans2,
|
|
@@ -303,7 +327,7 @@ async function collectSpans(args) {
|
|
|
303
327
|
if (refs.length > 0) harnesses.push(adapter.harness);
|
|
304
328
|
for (const ref of refs) {
|
|
305
329
|
const parsed = await parseSession(adapter, ref);
|
|
306
|
-
spans
|
|
330
|
+
appendAll(spans, parsed);
|
|
307
331
|
sources.push(selectedSessionSource(ref, parsed));
|
|
308
332
|
if (ref.cwd) cwds.push(ref.cwd);
|
|
309
333
|
}
|
|
@@ -318,16 +342,7 @@ async function cmdConvert(args) {
|
|
|
318
342
|
}
|
|
319
343
|
async function collectSessionRows(args) {
|
|
320
344
|
if (args.session) {
|
|
321
|
-
const adapter =
|
|
322
|
-
if (!adapter) throw new Error(`unknown harness "${args.harness}"`);
|
|
323
|
-
const st = await stat(args.session);
|
|
324
|
-
const ref = {
|
|
325
|
-
harness: adapter.harness,
|
|
326
|
-
sessionId: args.session,
|
|
327
|
-
path: args.session,
|
|
328
|
-
cwd: args.cwd ?? null,
|
|
329
|
-
mtimeMs: st.mtimeMs
|
|
330
|
-
};
|
|
345
|
+
const { adapter, ref } = await resolveSelectedSession(args);
|
|
331
346
|
return [{ ref, spans: await parseSession(adapter, ref) }];
|
|
332
347
|
}
|
|
333
348
|
const groups = await discover({ ...args, last: args.last || 20 });
|
|
@@ -438,52 +453,14 @@ async function loadExportAttributes(args) {
|
|
|
438
453
|
return attributes;
|
|
439
454
|
}
|
|
440
455
|
async function cmdAnalyze(args) {
|
|
441
|
-
const
|
|
442
|
-
if (spans.length === 0) throw new Error("no spans found for the given selection");
|
|
443
|
-
const ai = args.llm ? await buildAxService() : void 0;
|
|
444
|
-
const { otlpPath, execution, result } = await analyzeSpans(spans, {
|
|
445
|
-
ai,
|
|
446
|
-
model: args.model,
|
|
447
|
-
budgetUsd: args.budget,
|
|
448
|
-
otlpOutPath: args.otlp,
|
|
449
|
-
log: (msg) => process.stderr.write(`${msg}
|
|
450
|
-
`)
|
|
451
|
-
});
|
|
452
|
-
const pipelines = await runPipelines(spans, { minLoopOccurrences: args.minLoop });
|
|
453
|
-
const reactions = analyzeReactions(spans);
|
|
454
|
-
const adoption = await analyzeAdoption(spans, { cwds });
|
|
455
|
-
const deterministic = summarizeDeterministicSignals(pipelines, reactions);
|
|
456
|
-
let report = `${renderReport(result, {
|
|
457
|
-
harness,
|
|
458
|
-
sessionCount: adoption.identifiedSessionCount,
|
|
459
|
-
unassignedTraceCount: adoption.unassignedTraceCount,
|
|
460
|
-
spanCount: spans.length,
|
|
461
|
-
otlpPath,
|
|
462
|
-
execution,
|
|
463
|
-
deterministic,
|
|
464
|
-
sources
|
|
465
|
-
})}
|
|
466
|
-
${renderPipelines(pipelines)}
|
|
467
|
-
${renderReactions(reactions)}
|
|
468
|
-
${renderAdoption(adoption)}`;
|
|
469
|
-
if (args.analyzers.length > 0 && otlpPath) {
|
|
470
|
-
const engines = externalAnalyzersFromArgs(args);
|
|
471
|
-
const results = await runExternalAnalyzers(otlpPath, engines, { prompt: args.analyzerPrompt });
|
|
472
|
-
for (const r of results) {
|
|
473
|
-
report += `
|
|
474
|
-
|
|
475
|
-
## ${r.analyzer} (external analyzer)
|
|
476
|
-
|
|
477
|
-
${r.ok ? r.output || "(no output)" : `failed: ${r.error}`}`;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
456
|
+
const result = await investigate(args, { loadDefaultConfig: false });
|
|
480
457
|
if (args.out) {
|
|
481
|
-
await
|
|
458
|
+
await saveReport(args.out, result.report);
|
|
482
459
|
console.log(
|
|
483
|
-
`report \u2192 ${args.out} (${result.findings.length} findings, ${pipelines.stuckLoops.findings.length} loops, OTLP: ${otlpPath})`
|
|
460
|
+
`report \u2192 ${args.out} (${result.findings.length} findings, ${result.pipelines.stuckLoops.findings.length} loops, OTLP: ${result.otlpPath})`
|
|
484
461
|
);
|
|
485
462
|
} else {
|
|
486
|
-
console.log(report);
|
|
463
|
+
console.log(result.report);
|
|
487
464
|
}
|
|
488
465
|
}
|
|
489
466
|
async function collectImportedSpans(args) {
|
|
@@ -521,11 +498,20 @@ function externalAnalyzersFromArgs(args) {
|
|
|
521
498
|
);
|
|
522
499
|
}
|
|
523
500
|
async function cmdInvestigate(args) {
|
|
501
|
+
const result = await investigate(args);
|
|
502
|
+
if (args.out) {
|
|
503
|
+
await saveReport(args.out, result.report);
|
|
504
|
+
console.log(`investigation report \u2192 ${args.out} (${result.findings.length} findings with actions, OTLP: ${result.otlpPath})`);
|
|
505
|
+
} else {
|
|
506
|
+
console.log(result.report);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
async function investigate(args, options = {}) {
|
|
524
510
|
const { spans, harness, cwds, sources } = await collectSpans(args);
|
|
525
511
|
if (spans.length === 0) throw new Error("no spans found for the given selection");
|
|
526
|
-
const config = await loadTracesConfig(args.config);
|
|
512
|
+
const config = args.config !== void 0 || options.loadDefaultConfig !== false ? await loadTracesConfig(args.config) : void 0;
|
|
527
513
|
const ai = args.llm ? await buildAxService() : void 0;
|
|
528
|
-
|
|
514
|
+
return runTraceInvestigation(mergeTracesConfig({
|
|
529
515
|
spans,
|
|
530
516
|
harness,
|
|
531
517
|
sources,
|
|
@@ -540,12 +526,6 @@ async function cmdInvestigate(args) {
|
|
|
540
526
|
log: (msg) => process.stderr.write(`${msg}
|
|
541
527
|
`)
|
|
542
528
|
}, config));
|
|
543
|
-
if (args.out) {
|
|
544
|
-
await saveReport(args.out, result.report);
|
|
545
|
-
console.log(`investigation report \u2192 ${args.out} (${result.findings.length} findings with actions, OTLP: ${result.otlpPath})`);
|
|
546
|
-
} else {
|
|
547
|
-
console.log(result.report);
|
|
548
|
-
}
|
|
549
529
|
}
|
|
550
530
|
async function cmdImprove(args) {
|
|
551
531
|
const { spans, harness, cwds, sources } = await collectSpans(args);
|
|
@@ -822,7 +802,7 @@ Options:
|
|
|
822
802
|
--harness <id> Harness or alias (default: claude-code). Known: ${knownHarnesses().join(", ")}
|
|
823
803
|
--all Sweep every known harness
|
|
824
804
|
--last <n> Most-recent N sessions
|
|
825
|
-
--session <path> Analyze
|
|
805
|
+
--session <id|path> Analyze one listed session ID or one explicit harness session file
|
|
826
806
|
--cwd <dir> Filter sessions by working directory
|
|
827
807
|
--since <t> upload: window, 30m / 2h / 7d or an ISO date (default 24h); analyze: ISO cutoff
|
|
828
808
|
--out <path> Write report to a file
|
package/dist/index.d.ts
CHANGED
|
@@ -815,13 +815,9 @@ declare function analyzeReactions(spans: readonly OtlpSpan[]): ReactionReport;
|
|
|
815
815
|
* 2. per-skill invocation frequency
|
|
816
816
|
* 3. subagent (Task/Agent) spawn frequency
|
|
817
817
|
*
|
|
818
|
-
*
|
|
819
|
-
*
|
|
820
|
-
*
|
|
821
|
-
* `.evolve/skill-runs.jsonl` under the session's cwd and never surface as a
|
|
822
|
-
* `Skill` tool span — historically ~370× more runs than the trace count. So we
|
|
823
|
-
* read those files too and report "explicit invocations" and "loop-dispatched
|
|
824
|
-
* runs" SEPARATELY, so neither silently reads as zero.
|
|
818
|
+
* A repository's `.evolve/skill-runs.jsonl` is historical context, not session
|
|
819
|
+
* evidence, unless a row carries the selected session ID. This keeps old runs
|
|
820
|
+
* from being attributed to the current trace.
|
|
825
821
|
*/
|
|
826
822
|
|
|
827
823
|
interface AdoptionReport {
|
|
@@ -843,6 +839,11 @@ interface AdoptionReport {
|
|
|
843
839
|
sessionsWithMaterializedSkills: number;
|
|
844
840
|
/** Sessions with tool inputs that reference a SKILL.md path. */
|
|
845
841
|
sessionsWithSkillFileReference: number;
|
|
842
|
+
/** Skill documents opened by a successful captured shell-read command.
|
|
843
|
+
* Inspection is not evidence that the skill changed the outcome. */
|
|
844
|
+
skillDocumentReads: Record<string, number>;
|
|
845
|
+
/** Total observed skill-document reads. */
|
|
846
|
+
totalSkillDocumentReads: number;
|
|
846
847
|
/** Explicit `Skill` tool invocations per skill name, corpus-wide. */
|
|
847
848
|
skillInvocations: Record<string, number>;
|
|
848
849
|
/** Total explicit `Skill` tool invocations. */
|
|
@@ -853,19 +854,18 @@ interface AdoptionReport {
|
|
|
853
854
|
totalSubagentSpawns: number;
|
|
854
855
|
/** Sessions that spawned ≥1 subagent. */
|
|
855
856
|
sessionsWithSubagent: number;
|
|
856
|
-
/** Loop-dispatched skill runs
|
|
857
|
-
* (NOT visible at trace level — counted separately to avoid silent zeros). */
|
|
857
|
+
/** Loop-dispatched skill runs whose row names a selected session ID. */
|
|
858
858
|
loopDispatchedRuns: Record<string, number>;
|
|
859
|
-
/** Total loop-dispatched skill runs. */
|
|
859
|
+
/** Total session-linked loop-dispatched skill runs. */
|
|
860
860
|
totalLoopDispatchedRuns: number;
|
|
861
861
|
/** `.evolve/skill-runs.jsonl` files that were read. */
|
|
862
862
|
skillRunFilesRead: number;
|
|
863
|
+
/** Same-repository loop history without a selected session ID. Never treat
|
|
864
|
+
* this as evidence that a selected session used a skill. */
|
|
865
|
+
unlinkedLoopDispatchedRuns: Record<string, number>;
|
|
866
|
+
/** Total unlinked same-repository loop history rows. */
|
|
867
|
+
totalUnlinkedLoopDispatchedRuns: number;
|
|
863
868
|
}
|
|
864
|
-
/**
|
|
865
|
-
* Count loop-dispatched skill runs from a `.evolve/skill-runs.jsonl` file's raw
|
|
866
|
-
* contents. Each line records one or more skills under `skills` (array) or
|
|
867
|
-
* `skill` (string) — the schema varies across producers, so accept both.
|
|
868
|
-
*/
|
|
869
869
|
declare function countSkillRunsJsonl(raw: string): Record<string, number>;
|
|
870
870
|
interface AdoptionOptions {
|
|
871
871
|
/** Session cwds to probe for `.evolve/skill-runs.jsonl`. The CLI passes the
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/traces",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "Point it at your coding-agent session traces (Claude Code, Codex, OpenCode, Gemini, Pi, …) and get failure-mode + efficiency findings. CLI + SDK over the @tangle-network/agent-eval analyst suite — observe live sessions, run your own analysts, redact, and upload to the Tangle Intelligence Platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"node": ">=22"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@ax-llm/ax": "^
|
|
48
|
-
"@tangle-network/agent-eval": "^0.
|
|
47
|
+
"@ax-llm/ax": "^23.0.1",
|
|
48
|
+
"@tangle-network/agent-eval": "^0.122.8"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/node": "^22.0.0",
|