@tangle-network/traces 0.9.12 → 0.9.13
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 +58 -17
- package/dist/{chunk-YDUN3M5J.js → chunk-H47X6QWV.js} +3907 -798
- package/dist/cli.js +126 -65
- package/dist/index.d.ts +319 -24
- package/dist/index.js +23 -1
- package/docs/demo.gif +0 -0
- package/docs/trace-analysts.md +222 -0
- package/examples/custom-analyst.ts +179 -0
- package/examples/custom-backend.ts +21 -0
- package/examples/external-engines.ts +49 -0
- package/examples/improvement-config.mjs +63 -0
- package/examples/observe-and-alert.ts +19 -0
- package/examples/register-harness.ts +26 -0
- package/package.json +9 -8
package/dist/cli.js
CHANGED
|
@@ -6,11 +6,14 @@ import {
|
|
|
6
6
|
appendAll,
|
|
7
7
|
buildPolicyEvidenceRecord,
|
|
8
8
|
buildSessionIndexFromRows,
|
|
9
|
+
collectSessionSelection,
|
|
9
10
|
commandAnalyzer,
|
|
10
11
|
commandRedactor,
|
|
12
|
+
describeSessionRelationship,
|
|
11
13
|
executeUpload,
|
|
12
14
|
exportTraceEvidenceFile,
|
|
13
15
|
haloAnalyzer,
|
|
16
|
+
hodoscopeAnalyzer,
|
|
14
17
|
indexSessionIdsByTrace,
|
|
15
18
|
inspectSessionIndex,
|
|
16
19
|
knownHarnesses,
|
|
@@ -40,11 +43,10 @@ import {
|
|
|
40
43
|
writePolicyEvidenceFile,
|
|
41
44
|
writeSessionIndexFile,
|
|
42
45
|
writeTraceEvidenceExportFile
|
|
43
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-H47X6QWV.js";
|
|
44
47
|
|
|
45
48
|
// src/cli.ts
|
|
46
|
-
import {
|
|
47
|
-
import { createReadStream, readFileSync } from "fs";
|
|
49
|
+
import { readFileSync } from "fs";
|
|
48
50
|
import { readFile, stat } from "fs/promises";
|
|
49
51
|
import { basename, resolve } from "path";
|
|
50
52
|
import {
|
|
@@ -55,6 +57,8 @@ import {
|
|
|
55
57
|
renderSupervisorRunMarkdown,
|
|
56
58
|
rollupSupervisorRuns
|
|
57
59
|
} from "@tangle-network/agent-eval/supervisor-run";
|
|
60
|
+
import { createAnalystAi } from "@tangle-network/agent-eval/analyst";
|
|
61
|
+
var DEFAULT_ANALYST_MODEL = "gpt-5-mini";
|
|
58
62
|
function packageVersion() {
|
|
59
63
|
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
60
64
|
if (typeof pkg.version !== "string" || !pkg.version) throw new Error("package.json is missing version");
|
|
@@ -69,6 +73,9 @@ function parseArgs(argv) {
|
|
|
69
73
|
all: false,
|
|
70
74
|
last: 0,
|
|
71
75
|
current: false,
|
|
76
|
+
latestTurn: false,
|
|
77
|
+
workflow: false,
|
|
78
|
+
maxWorkflowSessions: 100,
|
|
72
79
|
llm: false,
|
|
73
80
|
interval: 5,
|
|
74
81
|
window: 30,
|
|
@@ -99,6 +106,15 @@ function parseArgs(argv) {
|
|
|
99
106
|
case "--current":
|
|
100
107
|
a.current = true;
|
|
101
108
|
break;
|
|
109
|
+
case "--latest-turn":
|
|
110
|
+
a.latestTurn = true;
|
|
111
|
+
break;
|
|
112
|
+
case "--workflow":
|
|
113
|
+
a.workflow = true;
|
|
114
|
+
break;
|
|
115
|
+
case "--max-workflow-sessions":
|
|
116
|
+
a.maxWorkflowSessions = Number(next());
|
|
117
|
+
break;
|
|
102
118
|
case "--session":
|
|
103
119
|
a.session = next();
|
|
104
120
|
break;
|
|
@@ -207,6 +223,14 @@ var CURRENT_SESSION_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
207
223
|
"evidence",
|
|
208
224
|
"stream"
|
|
209
225
|
]);
|
|
226
|
+
var WORKFLOW_COMMANDS = /* @__PURE__ */ new Set([
|
|
227
|
+
"analyze",
|
|
228
|
+
"investigate",
|
|
229
|
+
"improve",
|
|
230
|
+
"convert",
|
|
231
|
+
"index",
|
|
232
|
+
"evidence"
|
|
233
|
+
]);
|
|
210
234
|
function applyCurrentSessionSelection(args) {
|
|
211
235
|
if (!args.current) return args;
|
|
212
236
|
if (!CURRENT_SESSION_COMMANDS.has(args.command)) {
|
|
@@ -225,6 +249,22 @@ function applyCurrentSessionSelection(args) {
|
|
|
225
249
|
if (!sessionId) throw new Error("--current requires CODEX_THREAD_ID from the active Codex session");
|
|
226
250
|
return { ...args, session: sessionId };
|
|
227
251
|
}
|
|
252
|
+
function validateWorkflowSelection(args) {
|
|
253
|
+
if (!args.workflow && !args.latestTurn) return args;
|
|
254
|
+
if (!WORKFLOW_COMMANDS.has(args.command)) {
|
|
255
|
+
throw new Error(`${args.workflow ? "--workflow" : "--latest-turn"} is not supported by ${args.command}`);
|
|
256
|
+
}
|
|
257
|
+
if (args.input) throw new Error("--workflow and --latest-turn cannot be combined with an input file");
|
|
258
|
+
if (args.supervisorRunDir) {
|
|
259
|
+
throw new Error("--workflow and --latest-turn cannot be combined with --supervisor-run-dir");
|
|
260
|
+
}
|
|
261
|
+
const selected = resolveAdapter(args.harness);
|
|
262
|
+
const supportsTaskTurns = selected?.harness === "codex" || selected?.harness === "claude-code";
|
|
263
|
+
if (args.latestTurn && (args.all || !supportsTaskTurns)) {
|
|
264
|
+
throw new Error("--latest-turn requires --harness codex or claude-code");
|
|
265
|
+
}
|
|
266
|
+
return args;
|
|
267
|
+
}
|
|
228
268
|
async function confirm(question) {
|
|
229
269
|
if (!process.stdin.isTTY) return false;
|
|
230
270
|
const rl = (await import("readline/promises")).createInterface({
|
|
@@ -295,7 +335,7 @@ async function resolveSelectedSession(args) {
|
|
|
295
335
|
`session ID "${args.session}" is ambiguous across ${matches.length} files; select one harness or pass its explicit session path`
|
|
296
336
|
);
|
|
297
337
|
}
|
|
298
|
-
async function buildAxService() {
|
|
338
|
+
async function buildAxService(model) {
|
|
299
339
|
const apiKey = process.env.OPENAI_API_KEY;
|
|
300
340
|
if (!apiKey) {
|
|
301
341
|
throw new Error(
|
|
@@ -303,8 +343,7 @@ async function buildAxService() {
|
|
|
303
343
|
);
|
|
304
344
|
}
|
|
305
345
|
const apiURL = process.env.OPENAI_BASE_URL || void 0;
|
|
306
|
-
|
|
307
|
-
return AxAI.create({ name: "openai", apiKey, ...apiURL ? { apiURL } : {} });
|
|
346
|
+
return createAnalystAi({ apiKey, baseUrl: apiURL, model });
|
|
308
347
|
}
|
|
309
348
|
async function cmdList(args) {
|
|
310
349
|
const groups = await discover({ ...args, last: args.last || 20 });
|
|
@@ -328,6 +367,7 @@ function selectedSessionSource(ref, spans, sessionIdOverride) {
|
|
|
328
367
|
const subject = firstLine.length > 240 ? `${firstLine.slice(0, 240)}\u2026 [+${firstLine.length - 240} chars]` : firstLine;
|
|
329
368
|
const role = root?.attributes["traces.session.role"];
|
|
330
369
|
const parentSessionId = root?.attributes["traces.parent_session_id"];
|
|
370
|
+
const relationship = describeSessionRelationship(ref, spans);
|
|
331
371
|
const corruptionDigest = root?.attributes[ATTR.CORRUPTION_DIGEST];
|
|
332
372
|
const sessionId = [
|
|
333
373
|
sessionIdOverride,
|
|
@@ -341,6 +381,13 @@ function selectedSessionSource(ref, spans, sessionIdOverride) {
|
|
|
341
381
|
subject,
|
|
342
382
|
role: role === "operator" || role === "child" ? role : "unknown",
|
|
343
383
|
...typeof parentSessionId === "string" ? { parentSessionId } : {},
|
|
384
|
+
childSessionIds: relationship.childSessionIds,
|
|
385
|
+
...relationship.depth !== void 0 ? { depth: relationship.depth } : {},
|
|
386
|
+
...relationship.agentNickname ? { agentNickname: relationship.agentNickname } : {},
|
|
387
|
+
...relationship.agentRole ? { agentRole: relationship.agentRole } : {},
|
|
388
|
+
...relationship.agentPath ? { agentPath: relationship.agentPath } : {},
|
|
389
|
+
...relationship.taskScope ? { taskScope: relationship.taskScope } : {},
|
|
390
|
+
...relationship.turnId ? { turnId: relationship.turnId } : {},
|
|
344
391
|
integrity: ref.integrity?.status ?? "complete",
|
|
345
392
|
...ref.integrity ? {
|
|
346
393
|
corruptionCount: ref.integrity.corruptions.length,
|
|
@@ -350,66 +397,67 @@ function selectedSessionSource(ref, spans, sessionIdOverride) {
|
|
|
350
397
|
};
|
|
351
398
|
}
|
|
352
399
|
async function collectSpans(args) {
|
|
353
|
-
if (args.input)
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const spans2 = await parseSession(adapter, ref);
|
|
357
|
-
return {
|
|
358
|
-
spans: spans2,
|
|
359
|
-
harness: adapter.harness,
|
|
360
|
-
cwds: ref.cwd ? [ref.cwd] : [],
|
|
361
|
-
sources: [selectedSessionSource(ref, spans2)]
|
|
362
|
-
};
|
|
400
|
+
if (args.input) {
|
|
401
|
+
if (args.workflow) throw new Error("--workflow reads discovered sessions and cannot be combined with an input file");
|
|
402
|
+
return collectImportedSpans(args);
|
|
363
403
|
}
|
|
364
|
-
const
|
|
404
|
+
const selection = await collectSessionRows(args, false, args.last || 1);
|
|
365
405
|
const spans = [];
|
|
366
|
-
const harnesses =
|
|
406
|
+
const harnesses = /* @__PURE__ */ new Set();
|
|
367
407
|
const cwds = [];
|
|
368
408
|
const sources = [];
|
|
369
|
-
for (const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
sources.push(selectedSessionSource(ref, parsed));
|
|
375
|
-
if (ref.cwd) cwds.push(ref.cwd);
|
|
376
|
-
}
|
|
409
|
+
for (const row of selection.rows) {
|
|
410
|
+
harnesses.add(row.adapter.harness);
|
|
411
|
+
appendAll(spans, row.spans);
|
|
412
|
+
sources.push(selectedSessionSource(row.ref, row.spans));
|
|
413
|
+
if (row.ref.cwd) cwds.push(row.ref.cwd);
|
|
377
414
|
}
|
|
378
|
-
return {
|
|
415
|
+
return {
|
|
416
|
+
spans,
|
|
417
|
+
harness: [...harnesses].join("+") || args.harness,
|
|
418
|
+
cwds,
|
|
419
|
+
sources,
|
|
420
|
+
...selection.workflow ? { workflow: selection.workflow } : {}
|
|
421
|
+
};
|
|
379
422
|
}
|
|
380
423
|
async function cmdConvert(args) {
|
|
381
|
-
const { spans } = await collectSpans(args);
|
|
424
|
+
const { spans, workflow } = await collectSpans(args);
|
|
382
425
|
if (spans.length === 0) throw new Error("no spans found for the given selection");
|
|
426
|
+
warnIncompleteWorkflow(workflow);
|
|
383
427
|
const path = await writeOtlpFile(spans, args.otlp);
|
|
384
428
|
console.log(`wrote ${spans.length} spans \u2192 ${path}`);
|
|
385
429
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
}
|
|
391
|
-
async function collectSessionRows(args, bindExplicitSource = false) {
|
|
392
|
-
if (args.session) {
|
|
393
|
-
const { adapter, ref } = await resolveSelectedSession(args);
|
|
394
|
-
if (!bindExplicitSource) return [{ ref, spans: await parseSession(adapter, ref) }];
|
|
395
|
-
const before = await sourceSha256(ref.path);
|
|
396
|
-
const spans = await parseSession(adapter, ref);
|
|
397
|
-
const after = await sourceSha256(ref.path);
|
|
398
|
-
if (before !== after) {
|
|
399
|
-
throw new Error(`session source changed while parsing; refusing unbound evidence: ${ref.path}`);
|
|
400
|
-
}
|
|
401
|
-
return [{ ref, spans, sourceSha256: after }];
|
|
430
|
+
function workflowIssueText(issue) {
|
|
431
|
+
if (issue.kind === "cycle") return `cycle: ${issue.sessionIds.join(" -> ")}`;
|
|
432
|
+
if (issue.kind === "unresolved-parent-task") {
|
|
433
|
+
return `cannot resolve parent task for child ${issue.childSessionId} in ${issue.parentSessionId}: ${issue.reason}`;
|
|
402
434
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
for (const { adapter, refs } of groups) {
|
|
406
|
-
for (const ref of refs) rows.push({ ref, spans: await parseSession(adapter, ref) });
|
|
435
|
+
if (issue.kind === "parent-conflict") {
|
|
436
|
+
return `parent conflict for ${issue.sessionId}: declared ${issue.declaredParentSessionId ?? "none"}, referenced by ${issue.referencedParentSessionIds.join(", ") || "none"}`;
|
|
407
437
|
}
|
|
408
|
-
return
|
|
438
|
+
return `${issue.kind} ${issue.sessionId}, referenced as ${issue.relation} by ${issue.referencedBySessionId}`;
|
|
439
|
+
}
|
|
440
|
+
function warnIncompleteWorkflow(workflow) {
|
|
441
|
+
if (!workflow || workflow.complete) return;
|
|
442
|
+
process.stderr.write(
|
|
443
|
+
`warning: session workflow is incomplete (${workflow.issues.length} issue(s)): ${workflow.issues.map(workflowIssueText).join("; ")}
|
|
444
|
+
`
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
async function collectSessionRows(args, bindExplicitSource = false, defaultLast = 20) {
|
|
448
|
+
const groups = args.session ? [await resolveSelectedSession(args).then(({ adapter, ref }) => ({ adapter, refs: [ref] }))] : await discover({ ...args, last: args.last || defaultLast });
|
|
449
|
+
return collectSessionSelection(groups, {
|
|
450
|
+
workflow: args.workflow,
|
|
451
|
+
maxWorkflowSessions: args.maxWorkflowSessions,
|
|
452
|
+
taskScope: args.latestTurn ? "latest" : "all",
|
|
453
|
+
bindSources: bindExplicitSource && Boolean(args.session)
|
|
454
|
+
});
|
|
409
455
|
}
|
|
410
456
|
async function cmdEvidence(args) {
|
|
411
|
-
const
|
|
457
|
+
const selection = await collectSessionRows(args, true);
|
|
458
|
+
const rows = selection.rows.filter((row) => row.spans.length > 0);
|
|
412
459
|
if (rows.length === 0) throw new Error("no spans found for the given selection");
|
|
460
|
+
warnIncompleteWorkflow(selection.workflow);
|
|
413
461
|
const otlpPath = args.otlp ? await writeOtlpFile(rows.flatMap((row) => row.spans), args.otlp) : void 0;
|
|
414
462
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
415
463
|
const records = await Promise.all(rows.map(
|
|
@@ -429,8 +477,10 @@ async function cmdEvidence(args) {
|
|
|
429
477
|
}
|
|
430
478
|
}
|
|
431
479
|
async function cmdIndex(args) {
|
|
432
|
-
const
|
|
480
|
+
const selection = await collectSessionRows(args);
|
|
481
|
+
const rows = selection.rows.filter((row) => row.spans.length > 0);
|
|
433
482
|
if (rows.length === 0) throw new Error("no spans found for the given selection");
|
|
483
|
+
warnIncompleteWorkflow(selection.workflow);
|
|
434
484
|
const index = await buildSessionIndexFromRows(rows, {
|
|
435
485
|
minLoopOccurrences: args.minLoop,
|
|
436
486
|
selection: {
|
|
@@ -440,7 +490,9 @@ async function cmdIndex(args) {
|
|
|
440
490
|
last: args.last || void 0,
|
|
441
491
|
session: args.session,
|
|
442
492
|
cwd: args.cwd,
|
|
443
|
-
since: args.since
|
|
493
|
+
since: args.since,
|
|
494
|
+
latestTurn: args.latestTurn || void 0,
|
|
495
|
+
...selection.workflow ? { workflow: selection.workflow } : {}
|
|
444
496
|
}
|
|
445
497
|
});
|
|
446
498
|
if (args.out) {
|
|
@@ -478,8 +530,8 @@ async function cmdExport(args) {
|
|
|
478
530
|
}
|
|
479
531
|
function traceEvidenceFormat(raw) {
|
|
480
532
|
const format = raw ?? "auto";
|
|
481
|
-
if (format !== "auto" && format !== "policy-evidence" && format !== "sandbox-events" && format !== "openinference" && format !== "intelligence-spans") {
|
|
482
|
-
throw new Error("--format must be auto, policy-evidence, sandbox-events, openinference, or
|
|
533
|
+
if (format !== "auto" && format !== "policy-evidence" && format !== "sandbox-events" && format !== "openinference" && format !== "intelligence-spans" && format !== "chat-trajectory") {
|
|
534
|
+
throw new Error("--format must be auto, policy-evidence, sandbox-events, openinference, intelligence-spans, or chat-trajectory");
|
|
483
535
|
}
|
|
484
536
|
return format;
|
|
485
537
|
}
|
|
@@ -577,7 +629,7 @@ async function collectImportedSpans(args) {
|
|
|
577
629
|
}
|
|
578
630
|
function externalAnalyzersFromArgs(args) {
|
|
579
631
|
return args.analyzers.map(
|
|
580
|
-
(spec) => spec === "halo" ? haloAnalyzer({ defaultPrompt: args.analyzerPrompt, model: args.model }) : commandAnalyzer({ name: spec, command: spec, args: (p, prompt) => prompt ? [p, prompt] : [p] })
|
|
632
|
+
(spec) => spec === "halo" ? haloAnalyzer({ defaultPrompt: args.analyzerPrompt, model: args.model }) : spec === "hodoscope" ? hodoscopeAnalyzer({ summarizeModel: args.model }) : commandAnalyzer({ name: spec, command: spec, args: (p, prompt) => prompt ? [p, prompt] : [p] })
|
|
581
633
|
);
|
|
582
634
|
}
|
|
583
635
|
async function cmdInvestigate(args) {
|
|
@@ -590,18 +642,20 @@ async function cmdInvestigate(args) {
|
|
|
590
642
|
}
|
|
591
643
|
}
|
|
592
644
|
async function investigate(args, options = {}) {
|
|
593
|
-
const { spans, harness, cwds, sources } = await collectSpans(args);
|
|
645
|
+
const { spans, harness, cwds, sources, workflow } = await collectSpans(args);
|
|
594
646
|
if (spans.length === 0) throw new Error("no spans found for the given selection");
|
|
595
647
|
const config = args.config !== void 0 || options.loadDefaultConfig !== false ? await loadTracesConfig(args.config) : void 0;
|
|
596
|
-
const
|
|
648
|
+
const analystModel = args.model ?? process.env.TRACES_ANALYST_MODEL ?? DEFAULT_ANALYST_MODEL;
|
|
649
|
+
const ai = args.llm ? await buildAxService(analystModel) : void 0;
|
|
597
650
|
return runTraceInvestigation(mergeTracesConfig({
|
|
598
651
|
spans,
|
|
599
652
|
harness,
|
|
600
653
|
sources,
|
|
654
|
+
workflow,
|
|
601
655
|
cwds,
|
|
602
656
|
minLoopOccurrences: args.minLoop,
|
|
603
657
|
ai,
|
|
604
|
-
model: args.model,
|
|
658
|
+
model: args.llm ? analystModel : args.model,
|
|
605
659
|
budgetUsd: args.budget,
|
|
606
660
|
otlpOutPath: args.otlp,
|
|
607
661
|
externalAnalyzers: externalAnalyzersFromArgs(args),
|
|
@@ -611,19 +665,21 @@ async function investigate(args, options = {}) {
|
|
|
611
665
|
}, config));
|
|
612
666
|
}
|
|
613
667
|
async function cmdImprove(args) {
|
|
614
|
-
const { spans, harness, cwds, sources } = await collectSpans(args);
|
|
668
|
+
const { spans, harness, cwds, sources, workflow } = await collectSpans(args);
|
|
615
669
|
if (spans.length === 0) throw new Error("no spans found for the given selection");
|
|
616
670
|
const config = await loadTracesConfig(args.config);
|
|
617
|
-
const
|
|
671
|
+
const analystModel = args.model ?? process.env.TRACES_ANALYST_MODEL ?? DEFAULT_ANALYST_MODEL;
|
|
672
|
+
const ai = args.llm ? await buildAxService(analystModel) : void 0;
|
|
618
673
|
const result = await runTraceImprovement({
|
|
619
674
|
...mergeTracesConfig({
|
|
620
675
|
spans,
|
|
621
676
|
harness,
|
|
622
677
|
sources,
|
|
678
|
+
workflow,
|
|
623
679
|
cwds,
|
|
624
680
|
minLoopOccurrences: args.minLoop,
|
|
625
681
|
ai,
|
|
626
|
-
model: args.model,
|
|
682
|
+
model: args.llm ? analystModel : args.model,
|
|
627
683
|
budgetUsd: args.budget,
|
|
628
684
|
otlpOutPath: args.otlp,
|
|
629
685
|
externalAnalyzers: externalAnalyzersFromArgs(args),
|
|
@@ -850,6 +906,7 @@ Input formats:
|
|
|
850
906
|
sandbox-events JSON arrays with start/raw/result/done/error events
|
|
851
907
|
openinference Existing OpenInference JSONL; rewrites through traces redaction
|
|
852
908
|
intelligence-spans JSONL rows exported from Tangle Intelligence trace spans
|
|
909
|
+
chat-trajectory A message array or objects with a messages array
|
|
853
910
|
auto Detect the format from the file contents (default)
|
|
854
911
|
|
|
855
912
|
Examples:
|
|
@@ -887,7 +944,11 @@ Options:
|
|
|
887
944
|
--all Sweep every known harness
|
|
888
945
|
--last <n> Most-recent N sessions
|
|
889
946
|
--current Analyze the active Codex session named by CODEX_THREAD_ID
|
|
947
|
+
--latest-turn Limit a resumed Codex or Claude Code session to its latest task turn
|
|
890
948
|
--session <id|path> Analyze one listed session ID or one explicit harness session file
|
|
949
|
+
--workflow Expand selected sessions through stable parent/child session IDs
|
|
950
|
+
--max-workflow-sessions <n>
|
|
951
|
+
Stop workflow expansion before parsing more than N files (default 100)
|
|
891
952
|
--cwd <dir> Filter sessions by working directory
|
|
892
953
|
--supervisor-run-dir <dir>
|
|
893
954
|
analyze: report a SUPERVISION TREE instead of harness sessions \u2014
|
|
@@ -897,7 +958,7 @@ Options:
|
|
|
897
958
|
--out <path> Write report to a file
|
|
898
959
|
--dir <path> improve: write artifacts to this directory
|
|
899
960
|
--otlp <path> OTLP artifact path (also evidence provenance / dry-run upload preview)
|
|
900
|
-
--format <kind> analyze/export
|
|
961
|
+
--format <kind> analyze/export: auto | policy-evidence | sandbox-events | openinference | intelligence-spans | chat-trajectory
|
|
901
962
|
--metadata <json> analyze/export file: attach JSON object fields as span attributes
|
|
902
963
|
--attr <k=v> analyze/export file: attach one span attribute (repeatable)
|
|
903
964
|
--mode <kind> stream: visualizer | findings | agent (default visualizer)
|
|
@@ -905,10 +966,10 @@ Options:
|
|
|
905
966
|
--no-spans stream: omit per-span pulse events
|
|
906
967
|
--no-findings stream: omit semantic live-finding events
|
|
907
968
|
--llm Enable agentic RLM analysts (needs OPENAI_API_KEY / OPENAI_BASE_URL)
|
|
908
|
-
--model <id> --llm
|
|
969
|
+
--model <id> Model for --llm, HALO, and Hodoscope (default for --llm: ${DEFAULT_ANALYST_MODEL})
|
|
909
970
|
--config <path> investigate/improve/stream: JS config with analysts, liveAnalysts, or external analyzers
|
|
910
971
|
--budget <usd> USD cap for agentic analysts
|
|
911
|
-
--analyzer <
|
|
972
|
+
--analyzer <id> analyze: also run halo, hodoscope, or an installed command (repeatable)
|
|
912
973
|
--analyzer-prompt <p> analyze: prompt passed to external analyzers (default: diagnose)
|
|
913
974
|
--interval <s> watch/stream: poll interval seconds (default 5)
|
|
914
975
|
--window <m> watch/stream: only sessions active in the last N minutes (default 30)
|
|
@@ -934,7 +995,7 @@ async function main() {
|
|
|
934
995
|
else usage();
|
|
935
996
|
return;
|
|
936
997
|
}
|
|
937
|
-
const args = applyCurrentSessionSelection(parsedArgs);
|
|
998
|
+
const args = validateWorkflowSelection(applyCurrentSessionSelection(parsedArgs));
|
|
938
999
|
switch (args.command) {
|
|
939
1000
|
case "help":
|
|
940
1001
|
if (args.input === "export") usageExport();
|