@tangle-network/traces 0.11.4 → 0.11.6

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/dist/cli.js CHANGED
@@ -1,17 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- ACTOR_ATTR,
4
- ATTR,
5
- CORRUPTION_RECEIPT_DISPLAY_LIMIT,
3
+ DEFAULT_SANDBOX_BASE_URL,
6
4
  appendAll,
5
+ assembleSessionBundle,
7
6
  buildPolicyEvidenceRecord,
8
7
  buildSessionIndexFromRows,
9
8
  cmdReplayVerify,
10
9
  cmdReplayVerifyBatch,
10
+ cmdVerifyFindings,
11
11
  collectSessionSelection,
12
12
  commandAnalyzer,
13
13
  commandRedactor,
14
- describeSessionRelationship,
15
14
  executeUpload,
16
15
  exportTraceEvidenceFile,
17
16
  fileRunContextSupervisorRunReader,
@@ -25,13 +24,17 @@ import {
25
24
  loadTracesConfig,
26
25
  locateSessions,
27
26
  mergeTracesConfig,
27
+ parseCorpusFlag,
28
28
  parseSession,
29
29
  parseSince,
30
30
  planUpload,
31
+ primeAnalyzer,
32
+ projectSessionBundle,
31
33
  readOtlpInput,
32
34
  readSessionIndexFile,
33
35
  renderInspectionReport,
34
36
  renderValidation,
37
+ renderVerifiedFindingsSection,
35
38
  resolveAdapter,
36
39
  resolveRunWatchTarget,
37
40
  runTraceImprovement,
@@ -42,11 +45,12 @@ import {
42
45
  serializeSessionIndex,
43
46
  serializeSpans,
44
47
  serializeTraceStreamEvent,
45
- sessionIdFromAttributes,
48
+ sessionReportSource,
46
49
  streamSessions,
47
50
  totalAgenticFailureMessage,
48
51
  traceStreamEventsFromSpans,
49
52
  validationExitCode,
53
+ verifyFindings,
50
54
  watchRunTarget,
51
55
  watchSessions,
52
56
  writeInspectionReportFile,
@@ -54,7 +58,7 @@ import {
54
58
  writePolicyEvidenceFile,
55
59
  writeSessionIndexFile,
56
60
  writeTraceEvidenceExportFile
57
- } from "./chunk-AGPOIWPQ.js";
61
+ } from "./chunk-MEAEXKUV.js";
58
62
 
59
63
  // src/cli.ts
60
64
  import { readFileSync } from "fs";
@@ -69,6 +73,71 @@ import {
69
73
  rollupSupervisorRuns
70
74
  } from "@tangle-network/agent-eval/supervisor-run";
71
75
  import { createDspyRlmTraceEngine } from "@tangle-network/agent-eval/analyst";
76
+
77
+ // src/analyst-model-call.ts
78
+ import {
79
+ callLlm,
80
+ costReceiptFromLlm,
81
+ costReceiptFromLlmError,
82
+ LlmCallError
83
+ } from "@tangle-network/agent-eval";
84
+ var PROVIDER_ERROR_BODY_LIMIT = 2e3;
85
+ function createAnalystModelCall(opts) {
86
+ const { apiKey, baseUrl } = opts;
87
+ return async ({ request, callId, signal }) => {
88
+ try {
89
+ const req = structuredClone(request);
90
+ const response = await callLlm(req, {
91
+ apiKey,
92
+ baseUrl,
93
+ signal,
94
+ idempotencyKey: callId
95
+ });
96
+ return {
97
+ succeeded: true,
98
+ response,
99
+ receipt: costReceiptFromLlm(response),
100
+ execution: {
101
+ callId,
102
+ baseUrl,
103
+ requestedModel: req.model,
104
+ servedModel: response.servedModel ?? null,
105
+ finishReason: response.finishReason ?? null,
106
+ durationMs: response.durationMs
107
+ }
108
+ };
109
+ } catch (error) {
110
+ const err = error instanceof Error ? error : new Error(String(error));
111
+ const aborted = signal.aborted;
112
+ const message = aborted ? `AbortError: ${err.message}` : err.message;
113
+ const detail = err instanceof LlmCallError ? { status: err.status, body: err.body.slice(0, PROVIDER_ERROR_BODY_LIMIT) } : {};
114
+ return {
115
+ succeeded: false,
116
+ error: message,
117
+ // costReceiptFromLlmError recovers the provider receipt when the
118
+ // response completed but violated the contract; otherwise usage and
119
+ // cost stay explicitly unknown.
120
+ receipt: costReceiptFromLlmError(err) ?? {
121
+ model: request.model,
122
+ inputTokens: 0,
123
+ outputTokens: 0,
124
+ costUnknown: true,
125
+ usageUnknown: true
126
+ },
127
+ execution: {
128
+ callId,
129
+ baseUrl,
130
+ requestedModel: request.model,
131
+ aborted,
132
+ error: message,
133
+ ...detail
134
+ }
135
+ };
136
+ }
137
+ };
138
+ }
139
+
140
+ // src/cli.ts
72
141
  var DEFAULT_ANALYST_MODEL = "gpt-5-mini";
73
142
  var TANGLE_ROUTER_BASE_URL = "https://router.tangle.tools/v1";
74
143
  function packageVersion() {
@@ -102,7 +171,9 @@ function parseArgs(argv) {
102
171
  noSpans: false,
103
172
  noFindings: false,
104
173
  attrs: [],
105
- concurrency: 4
174
+ concurrency: 4,
175
+ verifyFindings: false,
176
+ replayCorpora: []
106
177
  };
107
178
  for (let i = 1; i < argv.length; i++) {
108
179
  const arg = argv[i];
@@ -178,6 +249,9 @@ function parseArgs(argv) {
178
249
  case "--mode":
179
250
  a.mode = next();
180
251
  break;
252
+ case "--view":
253
+ a.view = next();
254
+ break;
181
255
  case "--metadata":
182
256
  a.metadata = next();
183
257
  break;
@@ -222,6 +296,17 @@ function parseArgs(argv) {
222
296
  if (v) a.analyzers.push(v);
223
297
  break;
224
298
  }
299
+ case "--verify-findings":
300
+ a.verifyFindings = true;
301
+ break;
302
+ case "--replay-corpus": {
303
+ const v = next();
304
+ if (v) a.replayCorpora.push(v);
305
+ break;
306
+ }
307
+ case "--verify-out":
308
+ a.verifyOut = next();
309
+ break;
225
310
  case "--analyzer-prompt":
226
311
  a.analyzerPrompt = next();
227
312
  break;
@@ -278,6 +363,7 @@ var CURRENT_SESSION_COMMANDS = /* @__PURE__ */ new Set([
278
363
  "convert",
279
364
  "index",
280
365
  "evidence",
366
+ "bundle",
281
367
  "stream"
282
368
  ]);
283
369
  var WORKFLOW_COMMANDS = /* @__PURE__ */ new Set([
@@ -306,6 +392,12 @@ function applyCurrentSessionSelection(args) {
306
392
  if (!sessionId) throw new Error("--current requires CODEX_THREAD_ID from the active Codex session");
307
393
  return { ...args, session: sessionId };
308
394
  }
395
+ function validateViewSelection(args) {
396
+ if (args.view === void 0 || args.command === "bundle-view") return args;
397
+ throw new Error(
398
+ `--view is not supported by ${args.command}. A view is a projection of an assembled bundle: run \`traces bundle ... --out <dir>\` first, then \`traces bundle-view <dir> --view evidence-only --out <view-dir>\`.`
399
+ );
400
+ }
309
401
  function validateWorkflowSelection(args) {
310
402
  if (!args.workflow && !args.latestTurn) return args;
311
403
  if (!WORKFLOW_COMMANDS.has(args.command)) {
@@ -404,15 +496,19 @@ function buildAnalysisEngine(model, budgetUsd) {
404
496
  const baseUrl = process.env.OPENAI_BASE_URL || (tangleKey ? TANGLE_ROUTER_BASE_URL : "https://api.openai.com/v1");
405
497
  const python = process.env.TRACES_PYTHON;
406
498
  return createDspyRlmTraceEngine({
407
- apiKey,
408
- baseUrl,
499
+ call: createAnalystModelCall({ apiKey, baseUrl }),
500
+ callRef: `traces-cli:${baseUrl}#${model}`,
501
+ recordExecution: (observation) => {
502
+ analystLog(
503
+ `[analyst] model call ${observation.sequence} ${observation.succeeded ? "ok" : "FAIL"} ${observation.model}`,
504
+ observation.succeeded ? void 0 : { error: observation.error }
505
+ );
506
+ },
409
507
  model,
410
- // agent-eval 0.139.3's engine defaults are tuned below what real runs
411
- // need. maxOutputTokens 4096 is under what current coding models emit for
412
- // one findings array: glm-5.2 counts reasoning tokens in
413
- // completion_tokens, the first oversized completion breaches its cost
414
- // reservation, and the fail-closed ledger then refuses every later call
415
- // in the run.
508
+ // Pinned, not defaulted: one findings array from a current coding model
509
+ // needs this cap, and glm-5.2 counts reasoning tokens in
510
+ // completion_tokens — a smaller cap breaches its cost reservation and the
511
+ // fail-closed ledger then refuses every later call in the run.
416
512
  maxOutputTokens: 16384,
417
513
  // maxCostUsd defaults to $1 per analyst — a proxy-side ceiling separate
418
514
  // from --budget. With the larger token cap the per-call reservation grows
@@ -448,47 +544,6 @@ ${adapter.harness}: ${refs.length} session(s)`);
448
544
  }
449
545
  }
450
546
  }
451
- function selectedSessionSource(ref, spans, sessionIdOverride) {
452
- const root = spans.find((item) => item.parent_span_id === null) ?? spans[0];
453
- const prompt = spans.find(
454
- (item) => item.name === "user.prompt" && item.attributes[ACTOR_ATTR] === "human"
455
- ) ?? spans.find((item) => item.name === "user.prompt") ?? spans.find(
456
- (item) => item.attributes["span.type"] === "interaction" && typeof item.attributes.content === "string"
457
- );
458
- const content = typeof prompt?.attributes.content === "string" ? prompt.attributes.content : "";
459
- const firstLine = content.split(/\r?\n/, 1)[0].trim();
460
- const subject = firstLine.length > 240 ? `${firstLine.slice(0, 240)}\u2026 [+${firstLine.length - 240} chars]` : firstLine;
461
- const role = root?.attributes["traces.session.role"];
462
- const parentSessionId = root?.attributes["traces.parent_session_id"];
463
- const relationship = describeSessionRelationship(ref, spans);
464
- const corruptionDigest = root?.attributes[ATTR.CORRUPTION_DIGEST];
465
- const sessionId = [
466
- sessionIdOverride,
467
- root ? sessionIdFromAttributes(root.attributes) : void 0,
468
- root?.trace_id,
469
- ref.sessionId
470
- ].find((value) => typeof value === "string" && value.length > 0);
471
- return {
472
- sessionId,
473
- path: ref.path,
474
- subject,
475
- role: role === "operator" || role === "child" ? role : "unknown",
476
- ...typeof parentSessionId === "string" ? { parentSessionId } : {},
477
- childSessionIds: relationship.childSessionIds,
478
- ...relationship.depth !== void 0 ? { depth: relationship.depth } : {},
479
- ...relationship.agentNickname ? { agentNickname: relationship.agentNickname } : {},
480
- ...relationship.agentRole ? { agentRole: relationship.agentRole } : {},
481
- ...relationship.agentPath ? { agentPath: relationship.agentPath } : {},
482
- ...relationship.taskScope ? { taskScope: relationship.taskScope } : {},
483
- ...relationship.turnId ? { turnId: relationship.turnId } : {},
484
- integrity: ref.integrity?.status ?? "complete",
485
- ...ref.integrity ? {
486
- corruptionCount: ref.integrity.corruptions.length,
487
- ...typeof corruptionDigest === "string" ? { corruptionDigest } : {},
488
- corruptions: ref.integrity.corruptions.slice(0, CORRUPTION_RECEIPT_DISPLAY_LIMIT)
489
- } : {}
490
- };
491
- }
492
547
  async function collectOtlpSpans(path) {
493
548
  const input = await readOtlpInput(path);
494
549
  if (input.spans.length === 0) {
@@ -510,7 +565,7 @@ async function collectOtlpSpans(path) {
510
565
  spans: [...input.spans],
511
566
  harness: "otlp",
512
567
  cwds: [],
513
- sources: [...byTrace].map(([traceId, spans]) => selectedSessionSource({
568
+ sources: [...byTrace].map(([traceId, spans]) => sessionReportSource({
514
569
  harness: "otlp",
515
570
  sessionId: traceId,
516
571
  path: fileByTrace.get(traceId) ?? path,
@@ -540,7 +595,7 @@ async function collectSpans(args) {
540
595
  for (const row of selection.rows) {
541
596
  harnesses.add(row.adapter.harness);
542
597
  appendAll(spans, row.spans);
543
- sources.push(selectedSessionSource(row.ref, row.spans));
598
+ sources.push(sessionReportSource(row.ref, row.spans));
544
599
  if (row.ref.cwd) cwds.push(row.ref.cwd);
545
600
  }
546
601
  return {
@@ -659,6 +714,46 @@ async function cmdIndex(args) {
659
714
  process.stdout.write(serializeSessionIndex(index));
660
715
  }
661
716
  }
717
+ async function cmdBundle(args) {
718
+ if (!args.session) {
719
+ throw new Error("bundle needs --session <id|path>; run `traces list` to pick a session ID");
720
+ }
721
+ if (!args.out) throw new Error("bundle needs --out <dir> \u2014 a new or empty directory for the bundle");
722
+ const { adapter, ref } = await resolveSelectedSession(args);
723
+ const result = await assembleSessionBundle({
724
+ adapter,
725
+ ref,
726
+ outDir: args.out,
727
+ minLoopOccurrences: args.minLoop,
728
+ log: analystLog
729
+ });
730
+ const { manifest } = result;
731
+ console.log(
732
+ `session bundle (${manifest.view} view) \u2192 ${result.directory} (${manifest.files.length} file(s), ${manifest.ledgerSlices.length} ledger slice(s), ${manifest.absent.length} recorded absent)`
733
+ );
734
+ }
735
+ var PROJECTABLE_VIEWS = ["evidence-only"];
736
+ async function cmdBundleView(args) {
737
+ if (!args.input) {
738
+ throw new Error("bundle-view needs the bundle directory to project; run `traces bundle --out <dir>` first");
739
+ }
740
+ if (!args.view) {
741
+ throw new Error(`bundle-view needs --view <${PROJECTABLE_VIEWS.join("|")}>`);
742
+ }
743
+ if (!PROJECTABLE_VIEWS.includes(args.view)) {
744
+ throw new Error(`unknown view "${args.view}"; projectable views: ${PROJECTABLE_VIEWS.join(", ")}`);
745
+ }
746
+ if (!args.out) throw new Error("bundle-view needs --out <dir> \u2014 a new or empty directory for the view");
747
+ const result = await projectSessionBundle({
748
+ bundleDir: args.input,
749
+ outDir: args.out,
750
+ view: args.view
751
+ });
752
+ const { manifest, leakCheck } = result;
753
+ console.log(
754
+ `${manifest.view} view \u2192 ${result.directory} (${manifest.files.length} file(s) carried, ${manifest.excluded.length} excluded, ${leakCheck.matches} session-text match(es) against ${leakCheck.sourceSignatures} signature(s) from ${leakCheck.comparedSources.length} excluded source(s))`
755
+ );
756
+ }
662
757
  async function cmdInspect(args) {
663
758
  if (!args.input) throw new Error("inspect needs an index file; run `traces index --out session-index.json` first");
664
759
  const index = await readSessionIndexFile(args.input);
@@ -765,16 +860,43 @@ async function loadExportAttributes(args) {
765
860
  async function cmdAnalyze(args) {
766
861
  if (args.supervisorRunDir) return cmdAnalyzeSupervisorRun(args.supervisorRunDir, args);
767
862
  const result = await investigate(args, { loadDefaultConfig: false });
863
+ let report = result.report;
864
+ let verifySummary = "";
865
+ if (args.verifyFindings) {
866
+ const run = await verifyAnalyzeFindings(args, result);
867
+ report = `${report}
868
+ ${renderVerifiedFindingsSection(run)}`;
869
+ verifySummary = `, verified: ${run.counts.reproduced} reproduced / ${run.counts["fix-flipped"]} fix-flipped / ${run.counts.divergent} divergent / ${run.counts["not-replayable"]} not-replayable \u2192 ${run.out}`;
870
+ }
768
871
  if (args.out) {
769
- await saveReport(args.out, result.report);
872
+ await saveReport(args.out, report);
770
873
  console.log(
771
- `report \u2192 ${args.out} (${result.findings.length} findings, ${result.pipelines.stuckLoops.findings.length} loops, OTLP: ${result.otlpPath})`
874
+ `report \u2192 ${args.out} (${result.findings.length} findings, ${result.pipelines.stuckLoops.findings.length} loops, OTLP: ${result.otlpPath}${verifySummary})`
772
875
  );
773
876
  } else {
774
- console.log(result.report);
877
+ console.log(report);
775
878
  }
776
879
  assertAgenticAnalystsRan(args, result.agenticPerAnalyst);
777
880
  }
881
+ async function verifyAnalyzeFindings(args, result) {
882
+ if (args.replayCorpora.length === 0) {
883
+ throw new Error(
884
+ "analyze --verify-findings needs the executable trajectory source: pass --replay-corpus name=<labels.json>::<preparedDir> (repeatable). Harness sessions carry no docker_config, so findings cannot be replayed from the session store alone."
885
+ );
886
+ }
887
+ if (result.findings.length === 0) {
888
+ throw new Error("analyze --verify-findings: the analysis produced no findings to verify");
889
+ }
890
+ const out = args.verifyOut ?? (args.out ? `${args.out}.verify` : "traces-verify-findings");
891
+ return verifyFindings(result.findings, {
892
+ source: { kind: "corpus", corpora: args.replayCorpora.map(parseCorpusFlag) },
893
+ out,
894
+ apiKey: process.env.SANDBOX_API_KEY,
895
+ baseUrl: process.env.SANDBOX_API_URL ?? DEFAULT_SANDBOX_BASE_URL,
896
+ onProgress: (message) => process.stderr.write(`${message}
897
+ `)
898
+ });
899
+ }
778
900
  async function cmdAnalyzeSupervisorRun(runDir, args) {
779
901
  const runContextDirs = await findFileRunContextDirs(runDir);
780
902
  const nested = runContextDirs.length > 0 ? [] : await findSupervisorRunDirs(runDir);
@@ -845,7 +967,7 @@ async function collectImportedSpans(args) {
845
967
  spans: result.spans,
846
968
  harness: result.format,
847
969
  cwds: [],
848
- sources: [...sessions].map(([sessionId, spans]) => selectedSessionSource({
970
+ sources: [...sessions].map(([sessionId, spans]) => sessionReportSource({
849
971
  harness: result.format,
850
972
  sessionId,
851
973
  path: args.input,
@@ -863,7 +985,7 @@ function analystLog(msg, fields) {
863
985
  }
864
986
  function externalAnalyzersFromArgs(args) {
865
987
  return args.analyzers.map(
866
- (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] })
988
+ (spec) => spec === "halo" ? haloAnalyzer({ defaultPrompt: args.analyzerPrompt, model: args.model }) : spec === "hodoscope" ? hodoscopeAnalyzer({ summarizeModel: args.model }) : spec === "prime" ? primeAnalyzer({ defaultPrompt: args.analyzerPrompt, ...args.model ? { model: args.model } : {} }) : commandAnalyzer({ name: spec, command: spec, args: (p, prompt) => prompt ? [p, prompt] : [p] })
867
989
  );
868
990
  }
869
991
  async function cmdInvestigate(args) {
@@ -1267,6 +1389,17 @@ Commands:
1267
1389
  improve Write findings, evidence, report, and canonical trace artifacts
1268
1390
  convert Emit OTLP-JSONL only, to --otlp-out (HALO: use analyze --analyzer halo)
1269
1391
  index Emit a reusable session index JSON for later investigation
1392
+ bundle Assemble one session's durable evidence directory: transcript +
1393
+ subagents, derived index/report/evidence/OTLP, the repo's .evolve
1394
+ ledger sliced to the session window, git log, and a sha256 manifest
1395
+ (needs --session <id|path> and --out <new-dir>). This is the FULL
1396
+ view: it holds the session's own words, for a reader who cites them
1397
+ bundle-view <bundle-dir>
1398
+ Project an assembled bundle into a narrower view for a consumer that
1399
+ must NOT read the session's own words. --view evidence-only carries
1400
+ the counted artifacts and structured ledger rows, excludes the
1401
+ transcript, the report, the spans and every prose file by name, and
1402
+ refuses to write a view whose text repeats an excluded source
1270
1403
  inspect Read a session index and print ranked improvement findings
1271
1404
  export Convert evidence/events files to OpenInference JSONL for HALO
1272
1405
  import-codetracebench
@@ -1277,6 +1410,10 @@ Commands:
1277
1410
  replay-verify-batch
1278
1411
  Measure replayability + fix-flip rates across gold-labeled corpora:
1279
1412
  arm A per replayable case, LLM-generated arm-B fixes (--help for flags)
1413
+ verify-findings
1414
+ Execute recorded analyst findings as sandbox replay proofs: each
1415
+ finding \u2192 reproduced | fix-flipped | divergent | not-replayable,
1416
+ with a receipt directory per finding (--help for flags)
1280
1417
  evidence Emit compact session-evidence JSONL for downstream policy miners
1281
1418
  stream Emit JSONL trace stream events for live visualizers or replay
1282
1419
  watch Online observer: tail active sessions, notify on loops + semantic findings
@@ -1316,6 +1453,9 @@ Options:
1316
1453
  --metadata <json> analyze/export file: attach JSON object fields as span attributes
1317
1454
  --attr <k=v> analyze/export file: attach one span attribute (repeatable)
1318
1455
  --mode <kind> stream: visualizer | findings | agent (default visualizer)
1456
+ --view <name> bundle-view: which projection to write. evidence-only is the
1457
+ writer's view; the full view is the bundle itself, so there is
1458
+ nothing to project into it
1319
1459
  --replay, --once stream: scan once and exit (default for positional input / --session)
1320
1460
  --once watch <target>: print ONE snapshot and exit, for scripts and agents
1321
1461
  --no-spans stream: omit per-span pulse events
@@ -1329,8 +1469,17 @@ Options:
1329
1469
  --model <id> Model for --llm, HALO, and Hodoscope (default for --llm: ${DEFAULT_ANALYST_MODEL})
1330
1470
  --config <path> investigate/improve/stream: JS config with analysts, liveAnalysts, or external analyzers
1331
1471
  --budget <usd> USD cap for agentic analysts
1332
- --analyzer <id> analyze: also run halo, hodoscope, or an installed command (repeatable)
1472
+ --analyzer <id> analyze: also run halo, hodoscope, prime, or an installed command (repeatable)
1473
+ prime posts the full span projection to an OpenAI-compatible bridge
1474
+ (TRACES_PRIME_BRIDGE_URL, default http://localhost:4181;
1475
+ TRACES_PRIME_MODEL, default prime/zai/glm-5.2; TRACES_PRIME_TIMEOUT_MS)
1333
1476
  --analyzer-prompt <p> analyze: prompt passed to external analyzers (default: diagnose)
1477
+ --verify-findings analyze: execute the findings as sandbox replay proofs and mark
1478
+ each VERIFIED (receipt path) or UNVERIFIABLE (reason). Needs
1479
+ --replay-corpus + a running sandbox (SANDBOX_API_KEY/_URL)
1480
+ --replay-corpus name=<labels.json>::<preparedDir>
1481
+ trajectory source for --verify-findings (repeatable)
1482
+ --verify-out <dir> receipt root for --verify-findings (default: <--out>.verify)
1334
1483
  --interval <s> watch/stream: poll interval seconds (sessions 5, run tree 2)
1335
1484
  --window <m> watch/stream: only sessions active in the last N minutes (default 30)
1336
1485
  --min-loop <n> Min identical repeated calls to flag a loop (default 3)
@@ -1365,6 +1514,10 @@ async function main() {
1365
1514
  await cmdReplayVerifyBatch(rawArgs.slice(1));
1366
1515
  return;
1367
1516
  }
1517
+ if (rawArgs[0] === "verify-findings") {
1518
+ await cmdVerifyFindings(rawArgs.slice(1));
1519
+ return;
1520
+ }
1368
1521
  const parsedArgs = parseArgs(rawArgs);
1369
1522
  if (parsedArgs.help) {
1370
1523
  if (parsedArgs.command === "import-codetracebench" || parsedArgs.command === "help" && parsedArgs.input === "import-codetracebench") usageImportCodeTraceBench();
@@ -1373,7 +1526,9 @@ async function main() {
1373
1526
  else usage();
1374
1527
  return;
1375
1528
  }
1376
- const args = validateOtlpSelection(validateWorkflowSelection(applyCurrentSessionSelection(parsedArgs)));
1529
+ const args = validateOtlpSelection(
1530
+ validateViewSelection(validateWorkflowSelection(applyCurrentSessionSelection(parsedArgs)))
1531
+ );
1377
1532
  switch (args.command) {
1378
1533
  case "help":
1379
1534
  if (args.input === "import-codetracebench") usageImportCodeTraceBench();
@@ -1402,6 +1557,12 @@ async function main() {
1402
1557
  case "index":
1403
1558
  await cmdIndex(args);
1404
1559
  break;
1560
+ case "bundle":
1561
+ await cmdBundle(args);
1562
+ break;
1563
+ case "bundle-view":
1564
+ await cmdBundleView(args);
1565
+ break;
1405
1566
  case "inspect":
1406
1567
  await cmdInspect(args);
1407
1568
  break;