@tangle-network/traces 0.8.27 → 0.9.1
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 +16 -18
- package/dist/{chunk-EZDMNNW4.js → chunk-RUG26PIS.js} +2312 -1054
- package/dist/cli.js +108 -83
- package/dist/index.d.ts +236 -146
- package/dist/index.js +35 -3
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
ACTOR_ATTR,
|
|
4
|
+
ATTR,
|
|
5
|
+
CORRUPTION_RECEIPT_DISPLAY_LIMIT,
|
|
6
|
+
appendAll,
|
|
6
7
|
buildPolicyEvidenceRecord,
|
|
7
8
|
buildSessionIndexFromRows,
|
|
8
9
|
commandAnalyzer,
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
executeUpload,
|
|
11
12
|
exportTraceEvidenceFile,
|
|
12
13
|
haloAnalyzer,
|
|
14
|
+
indexSessionIdsByTrace,
|
|
13
15
|
inspectSessionIndex,
|
|
14
16
|
knownHarnesses,
|
|
15
17
|
loadTracesConfig,
|
|
@@ -19,15 +21,9 @@ import {
|
|
|
19
21
|
parseSince,
|
|
20
22
|
planUpload,
|
|
21
23
|
readSessionIndexFile,
|
|
22
|
-
renderAdoption,
|
|
23
24
|
renderInspectionReport,
|
|
24
|
-
renderPipelines,
|
|
25
|
-
renderReactions,
|
|
26
|
-
renderReport,
|
|
27
25
|
resolveAdapter,
|
|
28
|
-
|
|
29
|
-
runPipelines,
|
|
30
|
-
runTraceImprovementLoop,
|
|
26
|
+
runTraceImprovement,
|
|
31
27
|
runTraceInvestigation,
|
|
32
28
|
saveReport,
|
|
33
29
|
selectAdapters,
|
|
@@ -35,8 +31,8 @@ import {
|
|
|
35
31
|
serializeSessionIndex,
|
|
36
32
|
serializeSpans,
|
|
37
33
|
serializeTraceStreamEvent,
|
|
34
|
+
sessionIdFromAttributes,
|
|
38
35
|
streamSessions,
|
|
39
|
-
summarizeDeterministicSignals,
|
|
40
36
|
traceStreamEventsFromSpans,
|
|
41
37
|
watchSessions,
|
|
42
38
|
writeInspectionReportFile,
|
|
@@ -44,11 +40,11 @@ import {
|
|
|
44
40
|
writePolicyEvidenceFile,
|
|
45
41
|
writeSessionIndexFile,
|
|
46
42
|
writeTraceEvidenceExportFile
|
|
47
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-RUG26PIS.js";
|
|
48
44
|
|
|
49
45
|
// src/cli.ts
|
|
50
46
|
import { readFileSync } from "fs";
|
|
51
|
-
import { readFile, stat
|
|
47
|
+
import { readFile, stat } from "fs/promises";
|
|
52
48
|
import { basename, resolve } from "path";
|
|
53
49
|
function packageVersion() {
|
|
54
50
|
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
@@ -221,7 +217,7 @@ async function buildAxService() {
|
|
|
221
217
|
}
|
|
222
218
|
const apiURL = process.env.OPENAI_BASE_URL || void 0;
|
|
223
219
|
const { AxAI } = await import("@ax-llm/ax");
|
|
224
|
-
return
|
|
220
|
+
return AxAI.create({ name: "openai", apiKey, ...apiURL ? { apiURL } : {} });
|
|
225
221
|
}
|
|
226
222
|
async function cmdList(args) {
|
|
227
223
|
const groups = await discover({ ...args, last: args.last || 20 });
|
|
@@ -233,7 +229,41 @@ ${adapter.harness}: ${refs.length} session(s)`);
|
|
|
233
229
|
}
|
|
234
230
|
}
|
|
235
231
|
}
|
|
232
|
+
function selectedSessionSource(ref, spans, sessionIdOverride) {
|
|
233
|
+
const root = spans.find((item) => item.parent_span_id === null) ?? spans[0];
|
|
234
|
+
const prompt = spans.find(
|
|
235
|
+
(item) => item.name === "user.prompt" && item.attributes[ACTOR_ATTR] === "human"
|
|
236
|
+
) ?? spans.find((item) => item.name === "user.prompt") ?? spans.find(
|
|
237
|
+
(item) => item.attributes["span.type"] === "interaction" && typeof item.attributes.content === "string"
|
|
238
|
+
);
|
|
239
|
+
const content = typeof prompt?.attributes.content === "string" ? prompt.attributes.content : "";
|
|
240
|
+
const firstLine = content.split(/\r?\n/, 1)[0].trim();
|
|
241
|
+
const subject = firstLine.length > 240 ? `${firstLine.slice(0, 240)}\u2026 [+${firstLine.length - 240} chars]` : firstLine;
|
|
242
|
+
const role = root?.attributes["traces.session.role"];
|
|
243
|
+
const parentSessionId = root?.attributes["traces.parent_session_id"];
|
|
244
|
+
const corruptionDigest = root?.attributes[ATTR.CORRUPTION_DIGEST];
|
|
245
|
+
const sessionId = [
|
|
246
|
+
sessionIdOverride,
|
|
247
|
+
root ? sessionIdFromAttributes(root.attributes) : void 0,
|
|
248
|
+
root?.trace_id,
|
|
249
|
+
ref.sessionId
|
|
250
|
+
].find((value) => typeof value === "string" && value.length > 0);
|
|
251
|
+
return {
|
|
252
|
+
sessionId,
|
|
253
|
+
path: ref.path,
|
|
254
|
+
subject,
|
|
255
|
+
role: role === "operator" || role === "child" ? role : "unknown",
|
|
256
|
+
...typeof parentSessionId === "string" ? { parentSessionId } : {},
|
|
257
|
+
integrity: ref.integrity?.status ?? "complete",
|
|
258
|
+
...ref.integrity ? {
|
|
259
|
+
corruptionCount: ref.integrity.corruptions.length,
|
|
260
|
+
...typeof corruptionDigest === "string" ? { corruptionDigest } : {},
|
|
261
|
+
corruptions: ref.integrity.corruptions.slice(0, CORRUPTION_RECEIPT_DISPLAY_LIMIT)
|
|
262
|
+
} : {}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
236
265
|
async function collectSpans(args) {
|
|
266
|
+
if (args.input) return collectImportedSpans(args);
|
|
237
267
|
if (args.session) {
|
|
238
268
|
const adapter = resolveAdapter(args.harness);
|
|
239
269
|
if (!adapter) throw new Error(`unknown harness "${args.harness}"`);
|
|
@@ -248,22 +278,28 @@ async function collectSpans(args) {
|
|
|
248
278
|
mtimeMs: st.mtimeMs
|
|
249
279
|
};
|
|
250
280
|
const spans2 = await parseSession(adapter, ref);
|
|
251
|
-
return {
|
|
281
|
+
return {
|
|
282
|
+
spans: spans2,
|
|
283
|
+
harness: adapter.harness,
|
|
284
|
+
cwds: ref.cwd ? [ref.cwd] : [],
|
|
285
|
+
sources: [selectedSessionSource(ref, spans2)]
|
|
286
|
+
};
|
|
252
287
|
}
|
|
253
288
|
const groups = await discover({ ...args, last: args.last || 1 });
|
|
254
289
|
const spans = [];
|
|
255
|
-
let sessionCount = 0;
|
|
256
290
|
const harnesses = [];
|
|
257
291
|
const cwds = [];
|
|
292
|
+
const sources = [];
|
|
258
293
|
for (const { adapter, refs } of groups) {
|
|
259
294
|
if (refs.length > 0) harnesses.push(adapter.harness);
|
|
260
295
|
for (const ref of refs) {
|
|
261
|
-
|
|
296
|
+
const parsed = await parseSession(adapter, ref);
|
|
297
|
+
appendAll(spans, parsed);
|
|
298
|
+
sources.push(selectedSessionSource(ref, parsed));
|
|
262
299
|
if (ref.cwd) cwds.push(ref.cwd);
|
|
263
|
-
sessionCount += 1;
|
|
264
300
|
}
|
|
265
301
|
}
|
|
266
|
-
return { spans, harness: harnesses.join("+") || args.harness,
|
|
302
|
+
return { spans, harness: harnesses.join("+") || args.harness, cwds, sources };
|
|
267
303
|
}
|
|
268
304
|
async function cmdConvert(args) {
|
|
269
305
|
const { spans } = await collectSpans(args);
|
|
@@ -393,56 +429,43 @@ async function loadExportAttributes(args) {
|
|
|
393
429
|
return attributes;
|
|
394
430
|
}
|
|
395
431
|
async function cmdAnalyze(args) {
|
|
396
|
-
const
|
|
397
|
-
if (spans.length === 0) throw new Error("no spans found for the given selection");
|
|
398
|
-
const ai = args.llm ? await buildAxService() : void 0;
|
|
399
|
-
const { otlpPath, result } = await analyzeSpans(spans, {
|
|
400
|
-
ai,
|
|
401
|
-
model: args.model,
|
|
402
|
-
budgetUsd: args.budget,
|
|
403
|
-
otlpOutPath: args.otlp,
|
|
404
|
-
log: (msg) => process.stderr.write(`${msg}
|
|
405
|
-
`)
|
|
406
|
-
});
|
|
407
|
-
const pipelines = await runPipelines(spans, { minLoopOccurrences: args.minLoop });
|
|
408
|
-
const reactions = analyzeReactions(spans);
|
|
409
|
-
const adoption = await analyzeAdoption(spans, { cwds });
|
|
410
|
-
const deterministic = summarizeDeterministicSignals(pipelines, reactions);
|
|
411
|
-
let report = `${renderReport(result, { harness, sessionCount, spanCount: spans.length, otlpPath, deterministic })}
|
|
412
|
-
${renderPipelines(pipelines)}
|
|
413
|
-
${renderReactions(reactions)}
|
|
414
|
-
${renderAdoption(adoption)}`;
|
|
415
|
-
if (args.analyzers.length > 0 && otlpPath) {
|
|
416
|
-
const engines = externalAnalyzersFromArgs(args);
|
|
417
|
-
const results = await runExternalAnalyzers(otlpPath, engines, { prompt: args.analyzerPrompt });
|
|
418
|
-
for (const r of results) {
|
|
419
|
-
report += `
|
|
420
|
-
|
|
421
|
-
## ${r.analyzer} (external analyzer)
|
|
422
|
-
|
|
423
|
-
${r.ok ? r.output || "(no output)" : `failed: ${r.error}`}`;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
432
|
+
const result = await investigate(args, { loadDefaultConfig: false });
|
|
426
433
|
if (args.out) {
|
|
427
|
-
await
|
|
428
|
-
console.log(
|
|
434
|
+
await saveReport(args.out, result.report);
|
|
435
|
+
console.log(
|
|
436
|
+
`report \u2192 ${args.out} (${result.findings.length} findings, ${result.pipelines.stuckLoops.findings.length} loops, OTLP: ${result.otlpPath})`
|
|
437
|
+
);
|
|
429
438
|
} else {
|
|
430
|
-
console.log(report);
|
|
439
|
+
console.log(result.report);
|
|
431
440
|
}
|
|
432
441
|
}
|
|
433
442
|
async function collectImportedSpans(args) {
|
|
434
|
-
if (!args.input) throw new Error("
|
|
443
|
+
if (!args.input) throw new Error("input file missing");
|
|
435
444
|
const attributes = await loadExportAttributes(args);
|
|
436
445
|
const result = await exportTraceEvidenceFile(args.input, {
|
|
437
446
|
format: traceEvidenceFormat(args.format),
|
|
438
447
|
attributes
|
|
439
448
|
});
|
|
440
|
-
const
|
|
449
|
+
const { sessionByTrace } = indexSessionIdsByTrace(result.spans);
|
|
450
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
451
|
+
for (const span of result.spans) {
|
|
452
|
+
const sessionId = sessionByTrace.get(span.trace_id);
|
|
453
|
+
if (!sessionId) continue;
|
|
454
|
+
const grouped = sessions.get(sessionId) ?? [];
|
|
455
|
+
grouped.push(span);
|
|
456
|
+
sessions.set(sessionId, grouped);
|
|
457
|
+
}
|
|
441
458
|
return {
|
|
442
459
|
spans: result.spans,
|
|
443
460
|
harness: result.format,
|
|
444
|
-
|
|
445
|
-
|
|
461
|
+
cwds: [],
|
|
462
|
+
sources: [...sessions].map(([sessionId, spans]) => selectedSessionSource({
|
|
463
|
+
harness: result.format,
|
|
464
|
+
sessionId,
|
|
465
|
+
path: args.input,
|
|
466
|
+
cwd: null,
|
|
467
|
+
mtimeMs: 0
|
|
468
|
+
}, spans, sessionId))
|
|
446
469
|
};
|
|
447
470
|
}
|
|
448
471
|
function externalAnalyzersFromArgs(args) {
|
|
@@ -451,14 +474,23 @@ function externalAnalyzersFromArgs(args) {
|
|
|
451
474
|
);
|
|
452
475
|
}
|
|
453
476
|
async function cmdInvestigate(args) {
|
|
454
|
-
const
|
|
477
|
+
const result = await investigate(args);
|
|
478
|
+
if (args.out) {
|
|
479
|
+
await saveReport(args.out, result.report);
|
|
480
|
+
console.log(`investigation report \u2192 ${args.out} (${result.findings.length} findings with actions, OTLP: ${result.otlpPath})`);
|
|
481
|
+
} else {
|
|
482
|
+
console.log(result.report);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
async function investigate(args, options = {}) {
|
|
486
|
+
const { spans, harness, cwds, sources } = await collectSpans(args);
|
|
455
487
|
if (spans.length === 0) throw new Error("no spans found for the given selection");
|
|
456
|
-
const config = await loadTracesConfig(args.config);
|
|
488
|
+
const config = args.config !== void 0 || options.loadDefaultConfig !== false ? await loadTracesConfig(args.config) : void 0;
|
|
457
489
|
const ai = args.llm ? await buildAxService() : void 0;
|
|
458
|
-
|
|
490
|
+
return runTraceInvestigation(mergeTracesConfig({
|
|
459
491
|
spans,
|
|
460
492
|
harness,
|
|
461
|
-
|
|
493
|
+
sources,
|
|
462
494
|
cwds,
|
|
463
495
|
minLoopOccurrences: args.minLoop,
|
|
464
496
|
ai,
|
|
@@ -470,23 +502,17 @@ async function cmdInvestigate(args) {
|
|
|
470
502
|
log: (msg) => process.stderr.write(`${msg}
|
|
471
503
|
`)
|
|
472
504
|
}, config));
|
|
473
|
-
if (args.out) {
|
|
474
|
-
await saveReport(args.out, result.report);
|
|
475
|
-
console.log(`investigation report \u2192 ${args.out} (${result.findings.length} findings, ${result.recommendations.length} recommendations, OTLP: ${result.otlpPath})`);
|
|
476
|
-
} else {
|
|
477
|
-
console.log(result.report);
|
|
478
|
-
}
|
|
479
505
|
}
|
|
480
506
|
async function cmdImprove(args) {
|
|
481
|
-
const { spans, harness,
|
|
507
|
+
const { spans, harness, cwds, sources } = await collectSpans(args);
|
|
482
508
|
if (spans.length === 0) throw new Error("no spans found for the given selection");
|
|
483
509
|
const config = await loadTracesConfig(args.config);
|
|
484
510
|
const ai = args.llm ? await buildAxService() : void 0;
|
|
485
|
-
const result = await
|
|
511
|
+
const result = await runTraceImprovement({
|
|
486
512
|
...mergeTracesConfig({
|
|
487
513
|
spans,
|
|
488
514
|
harness,
|
|
489
|
-
|
|
515
|
+
sources,
|
|
490
516
|
cwds,
|
|
491
517
|
minLoopOccurrences: args.minLoop,
|
|
492
518
|
ai,
|
|
@@ -498,13 +524,12 @@ async function cmdImprove(args) {
|
|
|
498
524
|
log: (msg) => process.stderr.write(`${msg}
|
|
499
525
|
`)
|
|
500
526
|
}, config),
|
|
501
|
-
adapter: config?.improvementAdapter,
|
|
502
527
|
outDir: args.dir ?? args.out
|
|
503
528
|
});
|
|
504
529
|
const dir = result.artifacts?.directory;
|
|
505
530
|
if (!dir) throw new Error("improve did not produce an artifact directory");
|
|
506
531
|
console.log(
|
|
507
|
-
`improvement artifacts \u2192 ${dir} (${result.findings.length} findings
|
|
532
|
+
`improvement artifacts \u2192 ${dir} (${result.findings.length} findings with actions and checks, OTLP: ${result.otlpPath})`
|
|
508
533
|
);
|
|
509
534
|
}
|
|
510
535
|
function summarizeFindingEvidence(finding) {
|
|
@@ -660,10 +685,10 @@ async function cmdUpload(args) {
|
|
|
660
685
|
});
|
|
661
686
|
const redactorParts = args.redactorCmd?.split(/\s+/).filter(Boolean) ?? [];
|
|
662
687
|
const redactor = redactorParts[0] ? commandRedactor({ command: redactorParts[0], args: redactorParts.slice(1) }) : void 0;
|
|
663
|
-
const
|
|
688
|
+
const candidates = plan.items;
|
|
664
689
|
const byRule = {};
|
|
665
690
|
let totalRedactions = 0;
|
|
666
|
-
for (const i of
|
|
691
|
+
for (const i of candidates) {
|
|
667
692
|
totalRedactions += i.redaction.redactionCount;
|
|
668
693
|
for (const [r, n] of Object.entries(i.redaction.byRule)) byRule[r] = (byRule[r] ?? 0) + n;
|
|
669
694
|
}
|
|
@@ -671,28 +696,28 @@ async function cmdUpload(args) {
|
|
|
671
696
|
w(`
|
|
672
697
|
Window: since ${new Date(sinceMs).toISOString()}
|
|
673
698
|
`);
|
|
674
|
-
w(`Sessions found: ${plan.items.length} \xB7
|
|
699
|
+
w(`Sessions found: ${plan.items.length} \xB7 final privacy-mode dedup runs before upload
|
|
675
700
|
`);
|
|
676
701
|
w(`PII/secrets redacted: ${totalRedactions}${Object.keys(byRule).length ? ` (${Object.entries(byRule).map(([r, n]) => `${r}:${n}`).join(", ")})` : ""}
|
|
677
702
|
`);
|
|
678
|
-
for (const i of
|
|
703
|
+
for (const i of candidates.slice(0, 25)) {
|
|
679
704
|
w(` + [${i.ref.harness}] ${i.ref.sessionId.slice(0, 8)} ${i.spans.length} spans ${i.redaction.redactionCount} redacted ${i.ref.cwd ?? ""}
|
|
680
705
|
`);
|
|
681
706
|
}
|
|
682
|
-
if (
|
|
707
|
+
if (candidates.length > 25) w(` \u2026 and ${candidates.length - 25} more
|
|
683
708
|
`);
|
|
684
|
-
if (
|
|
685
|
-
console.log("
|
|
709
|
+
if (candidates.length === 0) {
|
|
710
|
+
console.log("No sessions found to upload.");
|
|
686
711
|
return;
|
|
687
712
|
}
|
|
688
713
|
if (args.dryRun) {
|
|
689
714
|
const res2 = await executeUpload(plan, { dryRun: true, otlpOut: args.otlp, stripContent: args.noContent, redactor });
|
|
690
|
-
console.log(`dry run: ${
|
|
715
|
+
console.log(`dry run: ${candidates.length - res2.skippedSessions} session(s), ${totalRedactions} redaction(s). Redacted OTLP -> ${res2.otlpPath}`);
|
|
691
716
|
console.log("No upload performed. Set TANGLE_INGEST_URL / TANGLE_INGEST_API_KEY / TANGLE_TENANT_ID and drop --dry-run to send.");
|
|
692
717
|
return;
|
|
693
718
|
}
|
|
694
719
|
if (!args.yes) {
|
|
695
|
-
const ok = await confirm(`
|
|
720
|
+
const ok = await confirm(`Process ${candidates.length} redacted session(s) for upload to the Tangle Intelligence Platform?`);
|
|
696
721
|
if (!ok) {
|
|
697
722
|
console.log("Aborted (use --yes to skip the prompt, or --dry-run to preview).");
|
|
698
723
|
return;
|
|
@@ -738,8 +763,8 @@ function usage() {
|
|
|
738
763
|
Commands:
|
|
739
764
|
list List discovered sessions
|
|
740
765
|
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
|
|
766
|
+
investigate Run typed investigation flow, including BYO config + evidence-backed actions
|
|
767
|
+
improve Write findings, evidence, report, and canonical trace artifacts
|
|
743
768
|
convert Emit OTLP-JSONL only (HALO: use analyze --analyzer halo)
|
|
744
769
|
index Emit a reusable session index JSON for later investigation
|
|
745
770
|
inspect Read a session index and print ranked improvement findings
|
|
@@ -768,7 +793,7 @@ Options:
|
|
|
768
793
|
--no-findings stream: omit semantic live-finding events
|
|
769
794
|
--llm Enable agentic RLM analysts (needs OPENAI_API_KEY / OPENAI_BASE_URL)
|
|
770
795
|
--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
|
|
796
|
+
--config <path> investigate/improve/stream: JS config with analysts, liveAnalysts, or external analyzers
|
|
772
797
|
--budget <usd> USD cap for agentic analysts
|
|
773
798
|
--analyzer <cmd> analyze: also run an external engine over the OTLP (repeatable; "halo" or any command)
|
|
774
799
|
--analyzer-prompt <p> analyze: prompt passed to external analyzers (default: diagnose)
|