@tangle-network/traces 0.8.27 → 0.9.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 +14 -16
- package/dist/{chunk-EZDMNNW4.js → chunk-5SBXL62V.js} +1970 -854
- package/dist/cli.js +104 -35
- package/dist/index.d.ts +221 -131
- package/dist/index.js +35 -3
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
ACTOR_ATTR,
|
|
4
|
+
ATTR,
|
|
5
|
+
CORRUPTION_RECEIPT_DISPLAY_LIMIT,
|
|
3
6
|
analyzeAdoption,
|
|
4
7
|
analyzeReactions,
|
|
5
8
|
analyzeSpans,
|
|
@@ -10,6 +13,7 @@ import {
|
|
|
10
13
|
executeUpload,
|
|
11
14
|
exportTraceEvidenceFile,
|
|
12
15
|
haloAnalyzer,
|
|
16
|
+
indexSessionIdsByTrace,
|
|
13
17
|
inspectSessionIndex,
|
|
14
18
|
knownHarnesses,
|
|
15
19
|
loadTracesConfig,
|
|
@@ -27,7 +31,7 @@ import {
|
|
|
27
31
|
resolveAdapter,
|
|
28
32
|
runExternalAnalyzers,
|
|
29
33
|
runPipelines,
|
|
30
|
-
|
|
34
|
+
runTraceImprovement,
|
|
31
35
|
runTraceInvestigation,
|
|
32
36
|
saveReport,
|
|
33
37
|
selectAdapters,
|
|
@@ -35,6 +39,7 @@ import {
|
|
|
35
39
|
serializeSessionIndex,
|
|
36
40
|
serializeSpans,
|
|
37
41
|
serializeTraceStreamEvent,
|
|
42
|
+
sessionIdFromAttributes,
|
|
38
43
|
streamSessions,
|
|
39
44
|
summarizeDeterministicSignals,
|
|
40
45
|
traceStreamEventsFromSpans,
|
|
@@ -44,7 +49,7 @@ import {
|
|
|
44
49
|
writePolicyEvidenceFile,
|
|
45
50
|
writeSessionIndexFile,
|
|
46
51
|
writeTraceEvidenceExportFile
|
|
47
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-5SBXL62V.js";
|
|
48
53
|
|
|
49
54
|
// src/cli.ts
|
|
50
55
|
import { readFileSync } from "fs";
|
|
@@ -233,7 +238,41 @@ ${adapter.harness}: ${refs.length} session(s)`);
|
|
|
233
238
|
}
|
|
234
239
|
}
|
|
235
240
|
}
|
|
241
|
+
function selectedSessionSource(ref, spans, sessionIdOverride) {
|
|
242
|
+
const root = spans.find((item) => item.parent_span_id === null) ?? spans[0];
|
|
243
|
+
const prompt = spans.find(
|
|
244
|
+
(item) => item.name === "user.prompt" && item.attributes[ACTOR_ATTR] === "human"
|
|
245
|
+
) ?? spans.find((item) => item.name === "user.prompt") ?? spans.find(
|
|
246
|
+
(item) => item.attributes["span.type"] === "interaction" && typeof item.attributes.content === "string"
|
|
247
|
+
);
|
|
248
|
+
const content = typeof prompt?.attributes.content === "string" ? prompt.attributes.content : "";
|
|
249
|
+
const firstLine = content.split(/\r?\n/, 1)[0].trim();
|
|
250
|
+
const subject = firstLine.length > 240 ? `${firstLine.slice(0, 240)}\u2026 [+${firstLine.length - 240} chars]` : firstLine;
|
|
251
|
+
const role = root?.attributes["traces.session.role"];
|
|
252
|
+
const parentSessionId = root?.attributes["traces.parent_session_id"];
|
|
253
|
+
const corruptionDigest = root?.attributes[ATTR.CORRUPTION_DIGEST];
|
|
254
|
+
const sessionId = [
|
|
255
|
+
sessionIdOverride,
|
|
256
|
+
root ? sessionIdFromAttributes(root.attributes) : void 0,
|
|
257
|
+
root?.trace_id,
|
|
258
|
+
ref.sessionId
|
|
259
|
+
].find((value) => typeof value === "string" && value.length > 0);
|
|
260
|
+
return {
|
|
261
|
+
sessionId,
|
|
262
|
+
path: ref.path,
|
|
263
|
+
subject,
|
|
264
|
+
role: role === "operator" || role === "child" ? role : "unknown",
|
|
265
|
+
...typeof parentSessionId === "string" ? { parentSessionId } : {},
|
|
266
|
+
integrity: ref.integrity?.status ?? "complete",
|
|
267
|
+
...ref.integrity ? {
|
|
268
|
+
corruptionCount: ref.integrity.corruptions.length,
|
|
269
|
+
...typeof corruptionDigest === "string" ? { corruptionDigest } : {},
|
|
270
|
+
corruptions: ref.integrity.corruptions.slice(0, CORRUPTION_RECEIPT_DISPLAY_LIMIT)
|
|
271
|
+
} : {}
|
|
272
|
+
};
|
|
273
|
+
}
|
|
236
274
|
async function collectSpans(args) {
|
|
275
|
+
if (args.input) return collectImportedSpans(args);
|
|
237
276
|
if (args.session) {
|
|
238
277
|
const adapter = resolveAdapter(args.harness);
|
|
239
278
|
if (!adapter) throw new Error(`unknown harness "${args.harness}"`);
|
|
@@ -248,22 +287,28 @@ async function collectSpans(args) {
|
|
|
248
287
|
mtimeMs: st.mtimeMs
|
|
249
288
|
};
|
|
250
289
|
const spans2 = await parseSession(adapter, ref);
|
|
251
|
-
return {
|
|
290
|
+
return {
|
|
291
|
+
spans: spans2,
|
|
292
|
+
harness: adapter.harness,
|
|
293
|
+
cwds: ref.cwd ? [ref.cwd] : [],
|
|
294
|
+
sources: [selectedSessionSource(ref, spans2)]
|
|
295
|
+
};
|
|
252
296
|
}
|
|
253
297
|
const groups = await discover({ ...args, last: args.last || 1 });
|
|
254
298
|
const spans = [];
|
|
255
|
-
let sessionCount = 0;
|
|
256
299
|
const harnesses = [];
|
|
257
300
|
const cwds = [];
|
|
301
|
+
const sources = [];
|
|
258
302
|
for (const { adapter, refs } of groups) {
|
|
259
303
|
if (refs.length > 0) harnesses.push(adapter.harness);
|
|
260
304
|
for (const ref of refs) {
|
|
261
|
-
|
|
305
|
+
const parsed = await parseSession(adapter, ref);
|
|
306
|
+
spans.push(...parsed);
|
|
307
|
+
sources.push(selectedSessionSource(ref, parsed));
|
|
262
308
|
if (ref.cwd) cwds.push(ref.cwd);
|
|
263
|
-
sessionCount += 1;
|
|
264
309
|
}
|
|
265
310
|
}
|
|
266
|
-
return { spans, harness: harnesses.join("+") || args.harness,
|
|
311
|
+
return { spans, harness: harnesses.join("+") || args.harness, cwds, sources };
|
|
267
312
|
}
|
|
268
313
|
async function cmdConvert(args) {
|
|
269
314
|
const { spans } = await collectSpans(args);
|
|
@@ -393,10 +438,10 @@ async function loadExportAttributes(args) {
|
|
|
393
438
|
return attributes;
|
|
394
439
|
}
|
|
395
440
|
async function cmdAnalyze(args) {
|
|
396
|
-
const { spans, harness,
|
|
441
|
+
const { spans, harness, cwds, sources } = await collectSpans(args);
|
|
397
442
|
if (spans.length === 0) throw new Error("no spans found for the given selection");
|
|
398
443
|
const ai = args.llm ? await buildAxService() : void 0;
|
|
399
|
-
const { otlpPath, result } = await analyzeSpans(spans, {
|
|
444
|
+
const { otlpPath, execution, result } = await analyzeSpans(spans, {
|
|
400
445
|
ai,
|
|
401
446
|
model: args.model,
|
|
402
447
|
budgetUsd: args.budget,
|
|
@@ -408,7 +453,16 @@ async function cmdAnalyze(args) {
|
|
|
408
453
|
const reactions = analyzeReactions(spans);
|
|
409
454
|
const adoption = await analyzeAdoption(spans, { cwds });
|
|
410
455
|
const deterministic = summarizeDeterministicSignals(pipelines, reactions);
|
|
411
|
-
let report = `${renderReport(result, {
|
|
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
|
+
})}
|
|
412
466
|
${renderPipelines(pipelines)}
|
|
413
467
|
${renderReactions(reactions)}
|
|
414
468
|
${renderAdoption(adoption)}`;
|
|
@@ -425,24 +479,40 @@ ${r.ok ? r.output || "(no output)" : `failed: ${r.error}`}`;
|
|
|
425
479
|
}
|
|
426
480
|
if (args.out) {
|
|
427
481
|
await writeFile(args.out, report, "utf8");
|
|
428
|
-
console.log(
|
|
482
|
+
console.log(
|
|
483
|
+
`report \u2192 ${args.out} (${result.findings.length} findings, ${pipelines.stuckLoops.findings.length} loops, OTLP: ${otlpPath})`
|
|
484
|
+
);
|
|
429
485
|
} else {
|
|
430
486
|
console.log(report);
|
|
431
487
|
}
|
|
432
488
|
}
|
|
433
489
|
async function collectImportedSpans(args) {
|
|
434
|
-
if (!args.input) throw new Error("
|
|
490
|
+
if (!args.input) throw new Error("input file missing");
|
|
435
491
|
const attributes = await loadExportAttributes(args);
|
|
436
492
|
const result = await exportTraceEvidenceFile(args.input, {
|
|
437
493
|
format: traceEvidenceFormat(args.format),
|
|
438
494
|
attributes
|
|
439
495
|
});
|
|
440
|
-
const
|
|
496
|
+
const { sessionByTrace } = indexSessionIdsByTrace(result.spans);
|
|
497
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
498
|
+
for (const span of result.spans) {
|
|
499
|
+
const sessionId = sessionByTrace.get(span.trace_id);
|
|
500
|
+
if (!sessionId) continue;
|
|
501
|
+
const grouped = sessions.get(sessionId) ?? [];
|
|
502
|
+
grouped.push(span);
|
|
503
|
+
sessions.set(sessionId, grouped);
|
|
504
|
+
}
|
|
441
505
|
return {
|
|
442
506
|
spans: result.spans,
|
|
443
507
|
harness: result.format,
|
|
444
|
-
|
|
445
|
-
|
|
508
|
+
cwds: [],
|
|
509
|
+
sources: [...sessions].map(([sessionId, spans]) => selectedSessionSource({
|
|
510
|
+
harness: result.format,
|
|
511
|
+
sessionId,
|
|
512
|
+
path: args.input,
|
|
513
|
+
cwd: null,
|
|
514
|
+
mtimeMs: 0
|
|
515
|
+
}, spans, sessionId))
|
|
446
516
|
};
|
|
447
517
|
}
|
|
448
518
|
function externalAnalyzersFromArgs(args) {
|
|
@@ -451,14 +521,14 @@ function externalAnalyzersFromArgs(args) {
|
|
|
451
521
|
);
|
|
452
522
|
}
|
|
453
523
|
async function cmdInvestigate(args) {
|
|
454
|
-
const { spans, harness,
|
|
524
|
+
const { spans, harness, cwds, sources } = await collectSpans(args);
|
|
455
525
|
if (spans.length === 0) throw new Error("no spans found for the given selection");
|
|
456
526
|
const config = await loadTracesConfig(args.config);
|
|
457
527
|
const ai = args.llm ? await buildAxService() : void 0;
|
|
458
528
|
const result = await runTraceInvestigation(mergeTracesConfig({
|
|
459
529
|
spans,
|
|
460
530
|
harness,
|
|
461
|
-
|
|
531
|
+
sources,
|
|
462
532
|
cwds,
|
|
463
533
|
minLoopOccurrences: args.minLoop,
|
|
464
534
|
ai,
|
|
@@ -472,21 +542,21 @@ async function cmdInvestigate(args) {
|
|
|
472
542
|
}, config));
|
|
473
543
|
if (args.out) {
|
|
474
544
|
await saveReport(args.out, result.report);
|
|
475
|
-
console.log(`investigation report \u2192 ${args.out} (${result.findings.length} findings
|
|
545
|
+
console.log(`investigation report \u2192 ${args.out} (${result.findings.length} findings with actions, OTLP: ${result.otlpPath})`);
|
|
476
546
|
} else {
|
|
477
547
|
console.log(result.report);
|
|
478
548
|
}
|
|
479
549
|
}
|
|
480
550
|
async function cmdImprove(args) {
|
|
481
|
-
const { spans, harness,
|
|
551
|
+
const { spans, harness, cwds, sources } = await collectSpans(args);
|
|
482
552
|
if (spans.length === 0) throw new Error("no spans found for the given selection");
|
|
483
553
|
const config = await loadTracesConfig(args.config);
|
|
484
554
|
const ai = args.llm ? await buildAxService() : void 0;
|
|
485
|
-
const result = await
|
|
555
|
+
const result = await runTraceImprovement({
|
|
486
556
|
...mergeTracesConfig({
|
|
487
557
|
spans,
|
|
488
558
|
harness,
|
|
489
|
-
|
|
559
|
+
sources,
|
|
490
560
|
cwds,
|
|
491
561
|
minLoopOccurrences: args.minLoop,
|
|
492
562
|
ai,
|
|
@@ -498,13 +568,12 @@ async function cmdImprove(args) {
|
|
|
498
568
|
log: (msg) => process.stderr.write(`${msg}
|
|
499
569
|
`)
|
|
500
570
|
}, config),
|
|
501
|
-
adapter: config?.improvementAdapter,
|
|
502
571
|
outDir: args.dir ?? args.out
|
|
503
572
|
});
|
|
504
573
|
const dir = result.artifacts?.directory;
|
|
505
574
|
if (!dir) throw new Error("improve did not produce an artifact directory");
|
|
506
575
|
console.log(
|
|
507
|
-
`improvement artifacts \u2192 ${dir} (${result.findings.length} findings
|
|
576
|
+
`improvement artifacts \u2192 ${dir} (${result.findings.length} findings with actions and checks, OTLP: ${result.otlpPath})`
|
|
508
577
|
);
|
|
509
578
|
}
|
|
510
579
|
function summarizeFindingEvidence(finding) {
|
|
@@ -660,10 +729,10 @@ async function cmdUpload(args) {
|
|
|
660
729
|
});
|
|
661
730
|
const redactorParts = args.redactorCmd?.split(/\s+/).filter(Boolean) ?? [];
|
|
662
731
|
const redactor = redactorParts[0] ? commandRedactor({ command: redactorParts[0], args: redactorParts.slice(1) }) : void 0;
|
|
663
|
-
const
|
|
732
|
+
const candidates = plan.items;
|
|
664
733
|
const byRule = {};
|
|
665
734
|
let totalRedactions = 0;
|
|
666
|
-
for (const i of
|
|
735
|
+
for (const i of candidates) {
|
|
667
736
|
totalRedactions += i.redaction.redactionCount;
|
|
668
737
|
for (const [r, n] of Object.entries(i.redaction.byRule)) byRule[r] = (byRule[r] ?? 0) + n;
|
|
669
738
|
}
|
|
@@ -671,28 +740,28 @@ async function cmdUpload(args) {
|
|
|
671
740
|
w(`
|
|
672
741
|
Window: since ${new Date(sinceMs).toISOString()}
|
|
673
742
|
`);
|
|
674
|
-
w(`Sessions found: ${plan.items.length} \xB7
|
|
743
|
+
w(`Sessions found: ${plan.items.length} \xB7 final privacy-mode dedup runs before upload
|
|
675
744
|
`);
|
|
676
745
|
w(`PII/secrets redacted: ${totalRedactions}${Object.keys(byRule).length ? ` (${Object.entries(byRule).map(([r, n]) => `${r}:${n}`).join(", ")})` : ""}
|
|
677
746
|
`);
|
|
678
|
-
for (const i of
|
|
747
|
+
for (const i of candidates.slice(0, 25)) {
|
|
679
748
|
w(` + [${i.ref.harness}] ${i.ref.sessionId.slice(0, 8)} ${i.spans.length} spans ${i.redaction.redactionCount} redacted ${i.ref.cwd ?? ""}
|
|
680
749
|
`);
|
|
681
750
|
}
|
|
682
|
-
if (
|
|
751
|
+
if (candidates.length > 25) w(` \u2026 and ${candidates.length - 25} more
|
|
683
752
|
`);
|
|
684
|
-
if (
|
|
685
|
-
console.log("
|
|
753
|
+
if (candidates.length === 0) {
|
|
754
|
+
console.log("No sessions found to upload.");
|
|
686
755
|
return;
|
|
687
756
|
}
|
|
688
757
|
if (args.dryRun) {
|
|
689
758
|
const res2 = await executeUpload(plan, { dryRun: true, otlpOut: args.otlp, stripContent: args.noContent, redactor });
|
|
690
|
-
console.log(`dry run: ${
|
|
759
|
+
console.log(`dry run: ${candidates.length - res2.skippedSessions} session(s), ${totalRedactions} redaction(s). Redacted OTLP -> ${res2.otlpPath}`);
|
|
691
760
|
console.log("No upload performed. Set TANGLE_INGEST_URL / TANGLE_INGEST_API_KEY / TANGLE_TENANT_ID and drop --dry-run to send.");
|
|
692
761
|
return;
|
|
693
762
|
}
|
|
694
763
|
if (!args.yes) {
|
|
695
|
-
const ok = await confirm(`
|
|
764
|
+
const ok = await confirm(`Process ${candidates.length} redacted session(s) for upload to the Tangle Intelligence Platform?`);
|
|
696
765
|
if (!ok) {
|
|
697
766
|
console.log("Aborted (use --yes to skip the prompt, or --dry-run to preview).");
|
|
698
767
|
return;
|
|
@@ -738,8 +807,8 @@ function usage() {
|
|
|
738
807
|
Commands:
|
|
739
808
|
list List discovered sessions
|
|
740
809
|
analyze Run analyst suite + loop/waste pipelines over sessions or an input file
|
|
741
|
-
investigate Run typed investigation flow, including BYO config +
|
|
742
|
-
improve Write
|
|
810
|
+
investigate Run typed investigation flow, including BYO config + evidence-backed actions
|
|
811
|
+
improve Write findings, evidence, report, and canonical trace artifacts
|
|
743
812
|
convert Emit OTLP-JSONL only (HALO: use analyze --analyzer halo)
|
|
744
813
|
index Emit a reusable session index JSON for later investigation
|
|
745
814
|
inspect Read a session index and print ranked improvement findings
|
|
@@ -768,7 +837,7 @@ Options:
|
|
|
768
837
|
--no-findings stream: omit semantic live-finding events
|
|
769
838
|
--llm Enable agentic RLM analysts (needs OPENAI_API_KEY / OPENAI_BASE_URL)
|
|
770
839
|
--model <id> --llm model id (e.g. a router model like glm-5.2); default is agent-eval's
|
|
771
|
-
--config <path> investigate/improve/stream: JS config with analysts, liveAnalysts, external analyzers
|
|
840
|
+
--config <path> investigate/improve/stream: JS config with analysts, liveAnalysts, or external analyzers
|
|
772
841
|
--budget <usd> USD cap for agentic analysts
|
|
773
842
|
--analyzer <cmd> analyze: also run an external engine over the OTLP (repeatable; "halo" or any command)
|
|
774
843
|
--analyzer-prompt <p> analyze: prompt passed to external analyzers (default: diagnose)
|