@tangle-network/traces 0.8.18 → 0.8.19

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 CHANGED
@@ -104,7 +104,8 @@ traces inspect session-index.json --out inspection-report.md
104
104
  traces evidence --harness codex --last 20 --out policy-evidence.jsonl
105
105
  traces export policy-evidence.jsonl --out spans.openinference.jsonl
106
106
  traces watch --all # live observer; loops + semantic findings
107
- traces stream --all --no-spans # live JSONL events for dashboards/visualizers
107
+ traces stream --all --mode findings # low-volume semantic feed
108
+ traces stream --all --mode agent # findings + deterministic report events
108
109
  traces stream spans.openinference.jsonl --format openinference --no-spans
109
110
  traces upload --since 1h --dry-run # redact + dedup + preview, no network
110
111
  traces upload --since 24h # upload last day to the Intelligence Platform
@@ -123,9 +124,10 @@ traces upload --since 24h # upload last day to the Inte
123
124
  | `--otlp <path>` | OTLP artifact path (also evidence provenance / dry-run upload preview) |
124
125
  | `--format <kind>` | `export` / file `stream`: `auto`, `policy-evidence`, `sandbox-events`, or `openinference` |
125
126
  | `--llm` / `--budget <usd>` | Enable agentic analysts (needs `OPENAI_API_KEY`) / cap their spend |
126
- | `--config <path>` | `investigate` / `improve`: load BYO analysts, external analyzers, and proposal adapters |
127
+ | `--config <path>` | `investigate` / `improve` / `stream`: load BYO analysts, live analysts, external analyzers, and proposal adapters |
127
128
  | `--interval <s>` / `--window <m>` | `watch` / live `stream`: poll seconds (default 5) / active-session window minutes (default 30) |
128
129
  | `--min-loop <n>` | Identical repeated calls before flagging a loop (default 3) |
130
+ | `--mode <kind>` | `stream`: `visualizer` (spans + findings), `findings` (low-volume), or `agent` (findings + reports) |
129
131
  | `--replay` / `--once` | `stream`: scan once, then exit |
130
132
  | `--no-spans` / `--no-findings` | `stream`: suppress raw span rows / finding rows |
131
133
  | `--no-content` | `upload`: send metadata only — strip all prompt/response text |
@@ -141,13 +143,15 @@ It emits newline-delimited JSON events that a dashboard, art visualizer, local w
141
143
 
142
144
  ```bash
143
145
  traces stream --all
144
- traces stream --all --no-spans
146
+ traces stream --all --mode findings
147
+ traces stream --all --mode agent --config traces.config.mjs
145
148
  traces stream spans.openinference.jsonl --format openinference --no-spans
146
149
  ```
147
150
 
148
- The stream emits `session`, `span`, `analysis_batch`, `finding`, and `tick` events.
151
+ Live streaming emits `session`, `span`, `analysis_batch`, `finding`, and `tick` events; `--mode agent` also emits `report` events.
149
152
  The semantic findings currently cover repeated failing commands, verification churn without code/config changes, completion claims without later verification, and high tool-error rates.
150
- Use `--no-spans` when you want the low-volume meaning layer; keep spans on for real-time visualizers that need motion, timing, and tool-call texture.
153
+ Use `--mode findings` when you want the low-volume meaning layer; keep `visualizer` for real-time views that need motion, timing, and tool-call texture.
154
+ Use `--mode agent` when another agent needs deterministic loop/tool-use reports alongside the findings.
151
155
 
152
156
  ## Improvement engine
153
157
 
@@ -177,6 +181,7 @@ traces improve --last 5 --config examples/improvement-config.mjs --dir .traces/i
177
181
  The config can export:
178
182
 
179
183
  - `analysts`: deterministic or LLM analysts that implement the `agent-eval` `Analyst` contract
184
+ - `liveAnalysts`: deterministic online analysts that implement the `TraceLiveAnalyst` contract for `traces stream`
180
185
  - `registry`: a prebuilt `AnalystRegistry`
181
186
  - `externalAnalyzers`: HALO or any command/model adapter that reads the OTLP artifact
182
187
  - `improvementAdapter`: a proposal writer that maps recommendations to patches, profile edits, prompts, or validation commands
@@ -317,6 +322,8 @@ The CLI is a thin consumer of these exports.
317
322
  | `streamSessions` | `(TraceStreamOptions) → Promise<void>` | live JSONL-ready event stream over active sessions |
318
323
  | `traceStreamEventsFromSpans` | `(spans, opts?) → TraceStreamEvent[]` | replay an existing span list as stream events |
319
324
  | `analyzeLiveBatch` | `(spans, opts?) → TraceLiveBatch` | compute semantic online findings for one batch |
325
+ | `classifyLiveActions` | `(spans) → TraceLiveAction[]` | classify spans once as read/edit/verify/claim/tool/other |
326
+ | `defaultTraceLiveAnalysts` | `TraceLiveAnalyst[]` | the built-in online analysts; extend or replace them |
320
327
  | `collectSessionIndex` | `(ScanOptions) → TraceSessionIndex` | scan sessions and return a reusable JSON-ready catalog |
321
328
  | `inspectSessionIndex` | `(TraceSessionIndex) → TraceInspectionReport` | rank improvement findings from an index without rescanning sessions |
322
329
  | `buildPolicyEvidenceRecord` | `(ref, spans, opts?) → PolicyEvidenceRecord` | summarize one session for downstream policy mining |
@@ -4297,6 +4297,36 @@ function isChange(span2) {
4297
4297
  if (!isTool(span2)) return false;
4298
4298
  return CHANGE_RE.test(`${toolName2(span2)} ${span2.name} ${spanContent(span2)}`);
4299
4299
  }
4300
+ function isRead(span2) {
4301
+ if (!isTool(span2)) return false;
4302
+ return /\b(read|cat|less|grep|rg|sed -n|nl|ls|find|git show|git diff)\b/i.test(`${toolName2(span2)} ${span2.name} ${spanContent(span2)}`);
4303
+ }
4304
+ function actionKind(span2) {
4305
+ if (isVerification(span2)) return "verify";
4306
+ if (isChange(span2)) return "edit";
4307
+ if (isRead(span2)) return "read";
4308
+ if (isTool(span2)) return "tool";
4309
+ if (isTextSpan(span2) && CLAIM_DONE_RE.test(spanContent(span2))) return "claim";
4310
+ return "other";
4311
+ }
4312
+ function classifyLiveActions(spans) {
4313
+ return orderSpans(spans).map((span2) => {
4314
+ const tool = isTool(span2) ? toolName2(span2) : void 0;
4315
+ return {
4316
+ kind: actionKind(span2),
4317
+ spanId: span2.span_id,
4318
+ span: span2,
4319
+ ...tool ? { toolName: tool } : {},
4320
+ signature: toolSignature(span2),
4321
+ errored: span2.status.code === "ERROR"
4322
+ };
4323
+ });
4324
+ }
4325
+ function actionCounts(actions) {
4326
+ const counts = { tool: 0, read: 0, edit: 0, verify: 0, claim: 0, other: 0 };
4327
+ for (const action of actions) counts[action.kind] += 1;
4328
+ return counts;
4329
+ }
4300
4330
  function timeOf(span2) {
4301
4331
  const parsed = Date.parse(span2.start_time);
4302
4332
  return Number.isFinite(parsed) ? parsed : 0;
@@ -4339,54 +4369,52 @@ function finding(input) {
4339
4369
  observedAt: input.observedAt
4340
4370
  };
4341
4371
  }
4342
- function analyzeLiveBatch(spans, opts = {}) {
4343
- const ordered2 = orderSpans(spans);
4344
- const generatedAt = opts.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
4345
- const session = sessionFrom(ordered2, opts);
4346
- const tools = ordered2.filter(isTool);
4347
- const erroredTools = tools.filter((span2) => span2.status.code === "ERROR");
4348
- const verificationCalls = tools.filter(isVerification);
4349
- const changeCalls = tools.filter(isChange);
4350
- const findings = [];
4351
- const failedBySignature = /* @__PURE__ */ new Map();
4352
- for (const span2 of erroredTools) {
4353
- const signature = toolSignature(span2);
4354
- failedBySignature.set(signature, [...failedBySignature.get(signature) ?? [], span2]);
4355
- }
4356
- for (const [signature, rows] of failedBySignature) {
4357
- if (rows.length < 2) continue;
4358
- findings.push(finding({
4359
- ruleId: "same-failing-command",
4360
- severity: rows.length >= 3 ? "high" : "medium",
4361
- title: "Same failing command is repeating",
4362
- claim: `${rows.length} failed tool call(s) repeated the same command or arguments.`,
4363
- action: "Stop rerunning it; inspect the first failure, change state, or choose a different diagnostic path before another retry.",
4364
- check: "The next action should read the failing file, inspect the stack/output, or edit code before this command runs again.",
4365
- evidence: [
4366
- evidence2("pattern", "command", signature, rows.map((span2) => span2.span_id)),
4367
- evidence2("metric", "failed_repeats", String(rows.length))
4368
- ],
4369
- session,
4370
- observedAt: generatedAt,
4371
- signature: [signature, String(rows.length)]
4372
- }));
4373
- }
4374
- let lastChangeIndex = -1;
4375
- let verificationSinceChange = 0;
4376
- const verificationSpanIds = [];
4377
- for (let i = 0; i < ordered2.length; i++) {
4378
- const span2 = ordered2[i];
4379
- if (isChange(span2)) {
4380
- lastChangeIndex = i;
4381
- verificationSinceChange = 0;
4382
- verificationSpanIds.length = 0;
4383
- } else if (isVerification(span2)) {
4384
- verificationSinceChange += 1;
4385
- verificationSpanIds.push(span2.span_id);
4372
+ var sameFailingCommandAnalyst = {
4373
+ id: "same-failing-command",
4374
+ analyze(context) {
4375
+ const findings = [];
4376
+ const failedBySignature = /* @__PURE__ */ new Map();
4377
+ for (const action of context.actions) {
4378
+ if (!action.toolName || !action.errored) continue;
4379
+ failedBySignature.set(action.signature, [...failedBySignature.get(action.signature) ?? [], action.span]);
4380
+ }
4381
+ for (const [signature, rows] of failedBySignature) {
4382
+ if (rows.length < 2) continue;
4383
+ findings.push(finding({
4384
+ ruleId: "same-failing-command",
4385
+ severity: rows.length >= 3 ? "high" : "medium",
4386
+ title: "Same failing command is repeating",
4387
+ claim: `${rows.length} failed tool call(s) repeated the same command or arguments.`,
4388
+ action: "Stop rerunning it; inspect the first failure, change state, or choose a different diagnostic path before another retry.",
4389
+ check: "The next action should read the failing file, inspect the stack/output, or edit code before this command runs again.",
4390
+ evidence: [
4391
+ evidence2("pattern", "command", signature, rows.map((span2) => span2.span_id)),
4392
+ evidence2("metric", "failed_repeats", String(rows.length))
4393
+ ],
4394
+ session: context.session,
4395
+ observedAt: context.generatedAt,
4396
+ signature: [signature, String(rows.length)]
4397
+ }));
4386
4398
  }
4399
+ return findings;
4387
4400
  }
4388
- if (verificationSinceChange >= 2) {
4389
- findings.push(finding({
4401
+ };
4402
+ var verificationWithoutChangeAnalyst = {
4403
+ id: "verification-without-change",
4404
+ analyze(context) {
4405
+ let verificationSinceChange = 0;
4406
+ const verificationSpanIds = [];
4407
+ for (const action of context.actions) {
4408
+ if (action.kind === "edit") {
4409
+ verificationSinceChange = 0;
4410
+ verificationSpanIds.length = 0;
4411
+ } else if (action.kind === "verify") {
4412
+ verificationSinceChange += 1;
4413
+ verificationSpanIds.push(action.spanId);
4414
+ }
4415
+ }
4416
+ if (verificationSinceChange < 2) return [];
4417
+ return [finding({
4390
4418
  ruleId: "verification-without-change",
4391
4419
  severity: verificationSinceChange >= 3 ? "high" : "medium",
4392
4420
  title: "Verification is repeating without a state change",
@@ -4395,51 +4423,89 @@ function analyzeLiveBatch(spans, opts = {}) {
4395
4423
  check: "The next action should be a code/config edit, targeted file read, or a different narrow diagnostic command.",
4396
4424
  evidence: [
4397
4425
  evidence2("metric", "verification_calls_since_change", String(verificationSinceChange), verificationSpanIds),
4398
- evidence2("metric", "change_calls", String(changeCalls.length))
4426
+ evidence2("metric", "change_calls", String(context.changeActions.length))
4399
4427
  ],
4400
- session,
4401
- observedAt: generatedAt,
4428
+ session: context.session,
4429
+ observedAt: context.generatedAt,
4402
4430
  signature: [String(verificationSinceChange), verificationSpanIds.join(",")]
4403
- }));
4431
+ })];
4404
4432
  }
4405
- const lastClaim = [...ordered2].reverse().find((span2) => isTextSpan(span2) && CLAIM_DONE_RE.test(spanContent(span2)));
4406
- if (lastClaim) {
4407
- const claimTime = timeOf(lastClaim);
4408
- const verificationAfterClaim = verificationCalls.some((span2) => timeOf(span2) > claimTime);
4409
- if (!verificationAfterClaim) {
4410
- findings.push(finding({
4411
- ruleId: "completion-claim-without-verification",
4412
- severity: "medium",
4413
- title: "Completion claim has no later verification",
4414
- claim: "The agent claimed completion, correctness, or verification without a later verification tool call in the observed trace.",
4415
- action: "Require a concrete check after the claim, or downgrade the claim to an unverified hypothesis.",
4416
- check: "A later test/build/typecheck/lint/smoke command should appear after the claim.",
4417
- evidence: [
4418
- evidence2("span", "claim_span", spanContent(lastClaim).slice(0, 240), [lastClaim.span_id]),
4419
- evidence2("metric", "verification_after_claim", "0")
4420
- ],
4421
- session,
4422
- observedAt: generatedAt,
4423
- signature: [lastClaim.span_id, spanContent(lastClaim).slice(0, 120)]
4424
- }));
4425
- }
4433
+ };
4434
+ var completionClaimWithoutVerificationAnalyst = {
4435
+ id: "completion-claim-without-verification",
4436
+ analyze(context) {
4437
+ const lastClaim = [...context.actions].reverse().find((action) => action.kind === "claim");
4438
+ if (!lastClaim) return [];
4439
+ const claimTime = timeOf(lastClaim.span);
4440
+ const verificationAfterClaim = context.verificationActions.some((action) => timeOf(action.span) > claimTime);
4441
+ if (verificationAfterClaim) return [];
4442
+ return [finding({
4443
+ ruleId: "completion-claim-without-verification",
4444
+ severity: "medium",
4445
+ title: "Completion claim has no later verification",
4446
+ claim: "The agent claimed completion, correctness, or verification without a later verification tool call in the observed trace.",
4447
+ action: "Require a concrete check after the claim, or downgrade the claim to an unverified hypothesis.",
4448
+ check: "A later test/build/typecheck/lint/smoke command should appear after the claim.",
4449
+ evidence: [
4450
+ evidence2("span", "claim_span", spanContent(lastClaim.span).slice(0, 240), [lastClaim.spanId]),
4451
+ evidence2("metric", "verification_after_claim", "0")
4452
+ ],
4453
+ session: context.session,
4454
+ observedAt: context.generatedAt,
4455
+ signature: [lastClaim.spanId, spanContent(lastClaim.span).slice(0, 120)]
4456
+ })];
4426
4457
  }
4427
- if (tools.length >= 4 && erroredTools.length / tools.length >= 0.5) {
4428
- findings.push(finding({
4458
+ };
4459
+ var highToolErrorRateAnalyst = {
4460
+ id: "high-tool-error-rate",
4461
+ analyze(context) {
4462
+ if (context.tools.length < 4 || context.erroredTools.length / context.tools.length < 0.5) return [];
4463
+ return [finding({
4429
4464
  ruleId: "high-tool-error-rate",
4430
- severity: erroredTools.length / tools.length >= 0.75 ? "high" : "medium",
4465
+ severity: context.erroredTools.length / context.tools.length >= 0.75 ? "high" : "medium",
4431
4466
  title: "Tool calls are mostly failing",
4432
- claim: `${erroredTools.length}/${tools.length} tool call(s) in this live batch ended with ERROR.`,
4467
+ claim: `${context.erroredTools.length}/${context.tools.length} tool call(s) in this live batch ended with ERROR.`,
4433
4468
  action: "Switch from broad execution to diagnosis: classify the dominant error, then run one targeted command to validate the fix path.",
4434
4469
  check: "The next batch should reduce the error ratio or show a new targeted diagnostic result.",
4435
4470
  evidence: [
4436
- evidence2("metric", "errored_tool_calls", `${erroredTools.length}/${tools.length}`, erroredTools.map((span2) => span2.span_id))
4471
+ evidence2("metric", "errored_tool_calls", `${context.erroredTools.length}/${context.tools.length}`, context.erroredTools.map((span2) => span2.span_id))
4437
4472
  ],
4438
- session,
4439
- observedAt: generatedAt,
4440
- signature: [String(erroredTools.length), String(tools.length)]
4441
- }));
4473
+ session: context.session,
4474
+ observedAt: context.generatedAt,
4475
+ signature: [String(context.erroredTools.length), String(context.tools.length)]
4476
+ })];
4442
4477
  }
4478
+ };
4479
+ var defaultTraceLiveAnalysts = [
4480
+ sameFailingCommandAnalyst,
4481
+ verificationWithoutChangeAnalyst,
4482
+ completionClaimWithoutVerificationAnalyst,
4483
+ highToolErrorRateAnalyst
4484
+ ];
4485
+ function liveAnalysts(opts) {
4486
+ return [...opts.analysts ?? defaultTraceLiveAnalysts, ...opts.extraAnalysts ?? []];
4487
+ }
4488
+ function analyzeLiveBatch(spans, opts = {}) {
4489
+ const ordered2 = orderSpans(spans);
4490
+ const generatedAt = opts.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
4491
+ const session = sessionFrom(ordered2, opts);
4492
+ const actions = classifyLiveActions(ordered2);
4493
+ const tools = ordered2.filter(isTool);
4494
+ const erroredTools = tools.filter((span2) => span2.status.code === "ERROR");
4495
+ const verificationActions = actions.filter((action) => action.kind === "verify");
4496
+ const changeActions = actions.filter((action) => action.kind === "edit");
4497
+ const context = {
4498
+ spans: ordered2,
4499
+ actions,
4500
+ tools,
4501
+ erroredTools,
4502
+ verificationActions,
4503
+ changeActions,
4504
+ session,
4505
+ generatedAt,
4506
+ newSpanCount: opts.newSpanCount ?? ordered2.length
4507
+ };
4508
+ const findings = liveAnalysts(opts).flatMap((analyst) => [...analyst.analyze(context)]);
4443
4509
  return {
4444
4510
  schemaVersion: 1,
4445
4511
  kind: "traces.live_batch",
@@ -4449,8 +4515,9 @@ function analyzeLiveBatch(spans, opts = {}) {
4449
4515
  newSpanCount: opts.newSpanCount ?? ordered2.length,
4450
4516
  toolCallCount: tools.length,
4451
4517
  erroredToolCallCount: erroredTools.length,
4452
- verificationCallCount: verificationCalls.length,
4453
- changeCallCount: changeCalls.length,
4518
+ verificationCallCount: verificationActions.length,
4519
+ changeCallCount: changeActions.length,
4520
+ actionCounts: actionCounts(actions),
4454
4521
  findingCount: findings.length,
4455
4522
  findings
4456
4523
  };
@@ -4475,14 +4542,20 @@ function traceStreamEventsFromSpans(spans, opts = {}) {
4475
4542
  session: opts.ref ? { harness: opts.ref.harness, sessionId: opts.ref.sessionId, cwd: opts.ref.cwd, path: opts.ref.path } : opts.session
4476
4543
  });
4477
4544
  const ordered2 = orderSpans(spans);
4478
- const batch = analyzeLiveBatch(ordered2, { generatedAt, session, newSpanCount: ordered2.length });
4545
+ const batch = analyzeLiveBatch(ordered2, {
4546
+ generatedAt,
4547
+ session,
4548
+ newSpanCount: ordered2.length,
4549
+ analysts: opts.analysts,
4550
+ extraAnalysts: opts.extraAnalysts
4551
+ });
4479
4552
  const events = [sessionEvent(session, ordered2, generatedAt)];
4480
4553
  if (opts.includeSpans !== false) {
4481
4554
  for (const span2 of ordered2) {
4482
4555
  events.push({ schemaVersion: 1, kind: "traces.stream.span", event: "span", generatedAt, session, span: span2 });
4483
4556
  }
4484
4557
  }
4485
- events.push({ schemaVersion: 1, kind: "traces.stream.batch", event: "analysis_batch", generatedAt, batch });
4558
+ events.push({ schemaVersion: 1, kind: "traces.stream.batch", event: "analysis_batch", generatedAt, ref: opts.ref, batch });
4486
4559
  if (opts.includeFindings !== false) {
4487
4560
  for (const liveFinding of batch.findings) {
4488
4561
  events.push({ schemaVersion: 1, kind: "traces.stream.finding", event: "finding", generatedAt, finding: liveFinding });
@@ -4494,12 +4567,24 @@ function serializeTraceStreamEvent(event) {
4494
4567
  return `${JSON.stringify(event)}
4495
4568
  `;
4496
4569
  }
4570
+ function loopsFromReport(session, report) {
4571
+ return report.stuckLoops.findings.map((loop) => ({
4572
+ harness: session.harness,
4573
+ sessionId: session.sessionId,
4574
+ cwd: session.cwd,
4575
+ toolName: loop.toolName,
4576
+ argHash: loop.argHash,
4577
+ occurrences: loop.occurrences,
4578
+ windowMs: loop.windowMs
4579
+ }));
4580
+ }
4497
4581
  async function streamSessions(opts) {
4498
4582
  const intervalMs = Math.max(250, opts.intervalMs ?? 5e3);
4499
4583
  const windowMs = opts.windowMs ?? 30 * 6e4;
4500
4584
  const includeSpans = opts.includeSpans !== false;
4501
4585
  const includeFindings = opts.includeFindings !== false;
4502
4586
  const includeBatches = opts.includeBatches !== false;
4587
+ const includeReports = opts.includeReports === true;
4503
4588
  const seenSessions = /* @__PURE__ */ new Set();
4504
4589
  const seenSpans = /* @__PURE__ */ new Set();
4505
4590
  const seenFindings = /* @__PURE__ */ new Set();
@@ -4533,9 +4618,31 @@ async function streamSessions(opts) {
4533
4618
  await opts.onEvent({ schemaVersion: 1, kind: "traces.stream.span", event: "span", generatedAt, session, span: span2 });
4534
4619
  }
4535
4620
  }
4536
- const batch = analyzeLiveBatch(ordered2, { generatedAt, session, newSpanCount: unseen.length });
4621
+ const batch = analyzeLiveBatch(ordered2, {
4622
+ generatedAt,
4623
+ session,
4624
+ newSpanCount: unseen.length,
4625
+ extraAnalysts: opts.extraAnalysts
4626
+ });
4537
4627
  if (includeBatches) {
4538
- await opts.onEvent({ schemaVersion: 1, kind: "traces.stream.batch", event: "analysis_batch", generatedAt, batch });
4628
+ await opts.onEvent({ schemaVersion: 1, kind: "traces.stream.batch", event: "analysis_batch", generatedAt, ref, batch });
4629
+ }
4630
+ if (includeReports) {
4631
+ try {
4632
+ const report = await runPipelines(ordered2, { minLoopOccurrences: opts.minLoopOccurrences });
4633
+ await opts.onEvent({
4634
+ schemaVersion: 1,
4635
+ kind: "traces.stream.report",
4636
+ event: "report",
4637
+ generatedAt,
4638
+ session,
4639
+ ref,
4640
+ report,
4641
+ loops: loopsFromReport(session, report)
4642
+ });
4643
+ } catch (err) {
4644
+ opts.onError?.(err, ref);
4645
+ }
4539
4646
  }
4540
4647
  if (includeFindings) {
4541
4648
  for (const liveFinding of batch.findings) {
@@ -4561,107 +4668,52 @@ async function streamSessions(opts) {
4561
4668
  }
4562
4669
 
4563
4670
  // src/observer.ts
4564
- var sleep2 = (ms, signal) => new Promise((resolve2) => {
4565
- if (signal?.aborted) {
4566
- resolve2();
4567
- return;
4671
+ function loopKey(loop) {
4672
+ return `${loop.sessionId}:${loop.toolName}:${loop.argHash}`;
4673
+ }
4674
+ async function callSafely(ref, onError, fn) {
4675
+ try {
4676
+ await fn();
4677
+ } catch (err) {
4678
+ onError?.(err, ref);
4568
4679
  }
4569
- const onAbort = () => {
4570
- clearTimeout(t);
4571
- resolve2();
4572
- };
4573
- const t = setTimeout(() => {
4574
- signal?.removeEventListener("abort", onAbort);
4575
- resolve2();
4576
- }, ms);
4577
- signal?.addEventListener("abort", onAbort, { once: true });
4578
- });
4579
- function loopKey(sessionId, toolName3, argHash) {
4580
- return `${sessionId}:${toolName3}:${argHash}`;
4581
4680
  }
4582
4681
  async function watchSessions(opts) {
4583
- const windowMs = opts.windowMs ?? 30 * 6e4;
4584
- const intervalMs = Math.max(250, opts.intervalMs ?? 5e3);
4585
- const minLoop = opts.minLoopOccurrences ?? 3;
4586
- const seen = /* @__PURE__ */ new Map();
4587
- const seenFindings = /* @__PURE__ */ new Set();
4588
- while (!opts.signal?.aborted) {
4589
- let sessionCount = 0;
4590
- for await (const { adapter, ref, spans } of scanSessions({
4591
- adapters: opts.adapters,
4592
- all: opts.all,
4593
- harnesses: opts.harnesses,
4594
- cwd: opts.cwd,
4595
- sinceMs: Date.now() - windowMs,
4596
- signal: opts.signal,
4597
- onError: opts.onError
4598
- })) {
4599
- let report;
4600
- try {
4601
- report = await runPipelines(spans, { minLoopOccurrences: minLoop });
4602
- } catch (err) {
4603
- opts.onError?.(err, ref);
4604
- continue;
4605
- }
4606
- sessionCount += 1;
4607
- if (opts.onReport) {
4608
- try {
4609
- await opts.onReport(ref, report);
4610
- } catch (err) {
4611
- opts.onError?.(err, ref);
4682
+ const seenLoops = /* @__PURE__ */ new Map();
4683
+ await streamSessions({
4684
+ adapters: opts.adapters,
4685
+ all: opts.all,
4686
+ harnesses: opts.harnesses,
4687
+ cwd: opts.cwd,
4688
+ windowMs: opts.windowMs,
4689
+ intervalMs: opts.intervalMs,
4690
+ minLoopOccurrences: opts.minLoopOccurrences,
4691
+ signal: opts.signal,
4692
+ includeSpans: false,
4693
+ includeBatches: Boolean(opts.onBatch),
4694
+ includeFindings: Boolean(opts.onFinding),
4695
+ includeReports: Boolean(opts.onReport || opts.onLoop),
4696
+ onError: opts.onError,
4697
+ onEvent: async (event) => {
4698
+ if (event.event === "analysis_batch") {
4699
+ if (event.ref) {
4700
+ await callSafely(event.ref, opts.onError, () => opts.onBatch?.(event.ref, event.batch));
4612
4701
  }
4613
- }
4614
- const batch = analyzeLiveBatch(spans, {
4615
- session: { harness: adapter.harness, sessionId: ref.sessionId, cwd: ref.cwd, path: ref.path }
4616
- });
4617
- if (opts.onBatch) {
4618
- try {
4619
- await opts.onBatch(ref, batch);
4620
- } catch (err) {
4621
- opts.onError?.(err, ref);
4622
- }
4623
- }
4624
- if (opts.onFinding) {
4625
- for (const finding2 of batch.findings) {
4626
- if (seenFindings.has(finding2.id)) continue;
4627
- seenFindings.add(finding2.id);
4628
- try {
4629
- await opts.onFinding(finding2);
4630
- } catch (err) {
4631
- opts.onError?.(err, ref);
4632
- }
4633
- }
4634
- }
4635
- for (const f of report.stuckLoops.findings) {
4636
- const key = loopKey(ref.sessionId, f.toolName, f.argHash);
4637
- if (f.occurrences > (seen.get(key) ?? 0)) {
4638
- seen.set(key, f.occurrences);
4639
- if (opts.onLoop) {
4640
- try {
4641
- await opts.onLoop({
4642
- harness: adapter.harness,
4643
- sessionId: ref.sessionId,
4644
- cwd: ref.cwd,
4645
- toolName: f.toolName,
4646
- argHash: f.argHash,
4647
- occurrences: f.occurrences,
4648
- windowMs: f.windowMs
4649
- });
4650
- } catch (err) {
4651
- opts.onError?.(err, ref);
4652
- }
4653
- }
4702
+ } else if (event.event === "finding") {
4703
+ await callSafely(void 0, opts.onError, () => opts.onFinding?.(event.finding));
4704
+ } else if (event.event === "report") {
4705
+ await callSafely(event.ref, opts.onError, () => opts.onReport?.(event.ref, event.report));
4706
+ for (const loop of event.loops) {
4707
+ const key = loopKey(loop);
4708
+ if (loop.occurrences <= (seenLoops.get(key) ?? 0)) continue;
4709
+ seenLoops.set(key, loop.occurrences);
4710
+ await callSafely(event.ref, opts.onError, () => opts.onLoop?.(loop));
4654
4711
  }
4712
+ } else if (event.event === "tick") {
4713
+ await callSafely(void 0, opts.onError, () => opts.onTick?.({ sessions: event.sessions }));
4655
4714
  }
4656
4715
  }
4657
- try {
4658
- opts.onTick?.({ sessions: sessionCount });
4659
- } catch (err) {
4660
- opts.onError?.(err);
4661
- }
4662
- if (opts.signal?.aborted) break;
4663
- await sleep2(intervalMs, opts.signal);
4664
- }
4716
+ });
4665
4717
  }
4666
4718
 
4667
4719
  // src/session-index.ts
@@ -5140,6 +5192,8 @@ export {
5140
5192
  writeTraceImprovementArtifacts,
5141
5193
  runTraceImprovementLoop,
5142
5194
  saveReport,
5195
+ classifyLiveActions,
5196
+ defaultTraceLiveAnalysts,
5143
5197
  analyzeLiveBatch,
5144
5198
  traceStreamEventsFromSpans,
5145
5199
  serializeTraceStreamEvent,
package/dist/cli.js CHANGED
@@ -43,7 +43,7 @@ import {
43
43
  writePolicyEvidenceFile,
44
44
  writeSessionIndexFile,
45
45
  writeTraceEvidenceExportFile
46
- } from "./chunk-G3OBZSWY.js";
46
+ } from "./chunk-FCTZLWP7.js";
47
47
 
48
48
  // src/cli.ts
49
49
  import { readFileSync } from "fs";
@@ -119,6 +119,9 @@ function parseArgs(argv) {
119
119
  case "--config":
120
120
  a.config = next();
121
121
  break;
122
+ case "--mode":
123
+ a.mode = next();
124
+ break;
122
125
  case "--metadata":
123
126
  a.metadata = next();
124
127
  break;
@@ -519,10 +522,26 @@ function formatLiveFinding(finding) {
519
522
  ` check: ${finding.check}`
520
523
  ].join("\n");
521
524
  }
522
- async function streamExplicitSession(args) {
525
+ function streamMode(raw) {
526
+ if (raw === void 0) return "visualizer";
527
+ if (raw === "visualizer" || raw === "findings" || raw === "agent") return raw;
528
+ throw new Error(`unknown stream mode "${raw}" (expected visualizer, findings, or agent)`);
529
+ }
530
+ function streamPreset(args) {
531
+ const mode = streamMode(args.mode);
532
+ return {
533
+ mode,
534
+ includeSpans: !args.noSpans && mode === "visualizer",
535
+ includeFindings: !args.noFindings,
536
+ includeBatches: true,
537
+ includeReports: mode === "agent"
538
+ };
539
+ }
540
+ async function streamExplicitSession(args, extraAnalysts) {
523
541
  if (!args.session) throw new Error("streamExplicitSession needs --session");
524
542
  const adapter = resolveAdapter(args.harness);
525
543
  if (!adapter) throw new Error(`unknown harness "${args.harness}"`);
544
+ const preset = streamPreset(args);
526
545
  const st = await stat(args.session);
527
546
  const ref = {
528
547
  harness: adapter.harness,
@@ -534,13 +553,16 @@ async function streamExplicitSession(args) {
534
553
  const spans = await parseSession(adapter, ref);
535
554
  for (const event of traceStreamEventsFromSpans(spans, {
536
555
  ref,
537
- includeSpans: !args.noSpans,
538
- includeFindings: !args.noFindings
556
+ includeSpans: preset.includeSpans,
557
+ includeFindings: preset.includeFindings,
558
+ extraAnalysts
539
559
  })) {
540
560
  process.stdout.write(serializeTraceStreamEvent(event));
541
561
  }
542
562
  }
543
563
  async function cmdStream(args) {
564
+ const config = await loadTracesConfig(args.config);
565
+ const preset = streamPreset(args);
544
566
  if (args.input) {
545
567
  const { spans, harness } = await collectImportedSpans(args);
546
568
  const ref = {
@@ -552,15 +574,16 @@ async function cmdStream(args) {
552
574
  };
553
575
  for (const event of traceStreamEventsFromSpans(spans, {
554
576
  ref,
555
- includeSpans: !args.noSpans,
556
- includeFindings: !args.noFindings
577
+ includeSpans: preset.includeSpans,
578
+ includeFindings: preset.includeFindings,
579
+ extraAnalysts: config?.liveAnalysts
557
580
  })) {
558
581
  process.stdout.write(serializeTraceStreamEvent(event));
559
582
  }
560
583
  return;
561
584
  }
562
585
  if (args.session) {
563
- await streamExplicitSession(args);
586
+ await streamExplicitSession(args, config?.liveAnalysts);
564
587
  return;
565
588
  }
566
589
  const all = args.all || !args.harnessExplicit && !args.cwd;
@@ -568,7 +591,7 @@ async function cmdStream(args) {
568
591
  process.once("SIGINT", () => controller.abort());
569
592
  if (!args.replay) {
570
593
  process.stderr.write(
571
- `traces stream \u2014 JSONL live feed for ${all ? "all harnesses" : args.harness}, last ${args.window}m, every ${args.interval}s. Ctrl-C to stop.
594
+ `traces stream \u2014 ${preset.mode} JSONL feed for ${all ? "all harnesses" : args.harness}, last ${args.window}m, every ${args.interval}s. Ctrl-C to stop.
572
595
  `
573
596
  );
574
597
  }
@@ -580,8 +603,11 @@ async function cmdStream(args) {
580
603
  windowMs: args.window * 6e4,
581
604
  intervalMs: args.interval * 1e3,
582
605
  once: args.replay,
583
- includeSpans: !args.noSpans,
584
- includeFindings: !args.noFindings,
606
+ includeSpans: preset.includeSpans,
607
+ includeFindings: preset.includeFindings,
608
+ includeBatches: preset.includeBatches,
609
+ includeReports: preset.includeReports,
610
+ extraAnalysts: config?.liveAnalysts,
585
611
  signal: controller.signal,
586
612
  onEvent: (event) => {
587
613
  process.stdout.write(serializeTraceStreamEvent(event));
@@ -735,12 +761,13 @@ Options:
735
761
  --format <kind> analyze/export file: auto | policy-evidence | sandbox-events | openinference | intelligence-spans
736
762
  --metadata <json> analyze/export file: attach JSON object fields as span attributes
737
763
  --attr <k=v> analyze/export file: attach one span attribute (repeatable)
764
+ --mode <kind> stream: visualizer | findings | agent (default visualizer)
738
765
  --replay, --once stream: scan once and exit (default for positional input / --session)
739
766
  --no-spans stream: omit per-span pulse events
740
767
  --no-findings stream: omit semantic live-finding events
741
768
  --llm Enable agentic RLM analysts (needs OPENAI_API_KEY / OPENAI_BASE_URL)
742
769
  --model <id> --llm model id (e.g. a router model like glm-5.2); default is agent-eval's
743
- --config <path> investigate/improve: JS config with analysts, external analyzers, or proposal adapter
770
+ --config <path> investigate/improve/stream: JS config with analysts, liveAnalysts, external analyzers, or proposal adapter
744
771
  --budget <usd> USD cap for agentic analysts
745
772
  --analyzer <cmd> analyze: also run an external engine over the OTLP (repeatable; "halo" or any command)
746
773
  --analyzer-prompt <p> analyze: prompt passed to external analyzers (default: diagnose)
package/dist/index.d.ts CHANGED
@@ -1106,9 +1106,175 @@ declare function commandRedactor(spec: {
1106
1106
  timeoutMs?: number;
1107
1107
  }): Redactor;
1108
1108
 
1109
+ type TraceLiveSeverity = 'info' | 'low' | 'medium' | 'high';
1110
+ interface TraceLiveEvidence {
1111
+ readonly kind: 'metric' | 'span' | 'pattern';
1112
+ readonly label: string;
1113
+ readonly value: string;
1114
+ readonly spanIds?: readonly string[];
1115
+ }
1116
+ interface TraceLiveFinding {
1117
+ readonly schemaVersion: 1;
1118
+ readonly kind: 'traces.live_finding';
1119
+ readonly id: string;
1120
+ readonly ruleId: string;
1121
+ readonly fingerprint: string;
1122
+ readonly severity: TraceLiveSeverity;
1123
+ readonly title: string;
1124
+ readonly claim: string;
1125
+ readonly action: string;
1126
+ readonly check: string;
1127
+ readonly evidence: readonly TraceLiveEvidence[];
1128
+ readonly session: TraceStreamSession;
1129
+ readonly observedAt: string;
1130
+ }
1131
+ interface TraceStreamSession {
1132
+ readonly harness: string;
1133
+ readonly sessionId: string;
1134
+ readonly cwd: string | null;
1135
+ readonly path?: string;
1136
+ }
1137
+ type TraceLiveActionKind = 'tool' | 'read' | 'edit' | 'verify' | 'claim' | 'other';
1138
+ interface TraceLiveAction {
1139
+ readonly kind: TraceLiveActionKind;
1140
+ readonly spanId: string;
1141
+ readonly span: OtlpSpan;
1142
+ readonly toolName?: string;
1143
+ readonly signature: string;
1144
+ readonly errored: boolean;
1145
+ }
1146
+ interface TraceLiveLoop {
1147
+ readonly harness: string;
1148
+ readonly sessionId: string;
1149
+ readonly cwd: string | null;
1150
+ readonly toolName: string;
1151
+ readonly argHash: string;
1152
+ readonly occurrences: number;
1153
+ readonly windowMs: number;
1154
+ }
1155
+ interface TraceLiveAnalysisContext {
1156
+ readonly spans: readonly OtlpSpan[];
1157
+ readonly actions: readonly TraceLiveAction[];
1158
+ readonly tools: readonly OtlpSpan[];
1159
+ readonly erroredTools: readonly OtlpSpan[];
1160
+ readonly verificationActions: readonly TraceLiveAction[];
1161
+ readonly changeActions: readonly TraceLiveAction[];
1162
+ readonly session: TraceStreamSession;
1163
+ readonly generatedAt: string;
1164
+ readonly newSpanCount: number;
1165
+ }
1166
+ interface TraceLiveAnalyst {
1167
+ readonly id: string;
1168
+ analyze(context: TraceLiveAnalysisContext): readonly TraceLiveFinding[];
1169
+ }
1170
+ interface TraceLiveBatch {
1171
+ readonly schemaVersion: 1;
1172
+ readonly kind: 'traces.live_batch';
1173
+ readonly generatedAt: string;
1174
+ readonly session: TraceStreamSession;
1175
+ readonly spanCount: number;
1176
+ readonly newSpanCount: number;
1177
+ readonly toolCallCount: number;
1178
+ readonly erroredToolCallCount: number;
1179
+ readonly verificationCallCount: number;
1180
+ readonly changeCallCount: number;
1181
+ readonly actionCounts: Readonly<Record<TraceLiveActionKind, number>>;
1182
+ readonly findingCount: number;
1183
+ readonly findings: readonly TraceLiveFinding[];
1184
+ }
1185
+ type TraceStreamEvent = {
1186
+ readonly schemaVersion: 1;
1187
+ readonly kind: 'traces.stream.session';
1188
+ readonly event: 'session';
1189
+ readonly generatedAt: string;
1190
+ readonly session: TraceStreamSession;
1191
+ readonly spanCount: number;
1192
+ readonly startedAt: string | null;
1193
+ readonly endedAt: string | null;
1194
+ } | {
1195
+ readonly schemaVersion: 1;
1196
+ readonly kind: 'traces.stream.span';
1197
+ readonly event: 'span';
1198
+ readonly generatedAt: string;
1199
+ readonly session: TraceStreamSession;
1200
+ readonly span: OtlpSpan;
1201
+ } | {
1202
+ readonly schemaVersion: 1;
1203
+ readonly kind: 'traces.stream.batch';
1204
+ readonly event: 'analysis_batch';
1205
+ readonly generatedAt: string;
1206
+ readonly ref?: SessionRef;
1207
+ readonly batch: TraceLiveBatch;
1208
+ } | {
1209
+ readonly schemaVersion: 1;
1210
+ readonly kind: 'traces.stream.report';
1211
+ readonly event: 'report';
1212
+ readonly generatedAt: string;
1213
+ readonly session: TraceStreamSession;
1214
+ readonly ref: SessionRef;
1215
+ readonly report: PipelineReport;
1216
+ readonly loops: readonly TraceLiveLoop[];
1217
+ } | {
1218
+ readonly schemaVersion: 1;
1219
+ readonly kind: 'traces.stream.finding';
1220
+ readonly event: 'finding';
1221
+ readonly generatedAt: string;
1222
+ readonly finding: TraceLiveFinding;
1223
+ } | {
1224
+ readonly schemaVersion: 1;
1225
+ readonly kind: 'traces.stream.tick';
1226
+ readonly event: 'tick';
1227
+ readonly generatedAt: string;
1228
+ readonly sessions: number;
1229
+ readonly newSpans: number;
1230
+ readonly findings: number;
1231
+ };
1232
+ interface LiveBatchOptions {
1233
+ readonly generatedAt?: string;
1234
+ readonly session?: Partial<TraceStreamSession>;
1235
+ readonly newSpanCount?: number;
1236
+ /** Replaces the built-in deterministic live analysts. */
1237
+ readonly analysts?: readonly TraceLiveAnalyst[];
1238
+ /** Adds custom live analysts after the built-ins. */
1239
+ readonly extraAnalysts?: readonly TraceLiveAnalyst[];
1240
+ }
1241
+ interface TraceStreamReplayOptions extends LiveBatchOptions {
1242
+ readonly ref?: SessionRef;
1243
+ readonly includeSpans?: boolean;
1244
+ readonly includeFindings?: boolean;
1245
+ }
1246
+ interface TraceStreamOptions extends ScanOptions {
1247
+ /** Observe sessions active within this window. Default 30 min. */
1248
+ readonly windowMs?: number;
1249
+ /** Poll interval. Default 5s. */
1250
+ readonly intervalMs?: number;
1251
+ /** Stop after one scan, useful for replay or tests. */
1252
+ readonly once?: boolean;
1253
+ /** Emit one JSON event per new span. Default true. */
1254
+ readonly includeSpans?: boolean;
1255
+ /** Emit live semantic findings. Default true. */
1256
+ readonly includeFindings?: boolean;
1257
+ /** Emit session and analysis-batch events. Default true. */
1258
+ readonly includeBatches?: boolean;
1259
+ /** Emit deterministic pipeline report events. Default false. */
1260
+ readonly includeReports?: boolean;
1261
+ /** Minimum repeated identical calls for report loop detection. Default 3. */
1262
+ readonly minLoopOccurrences?: number;
1263
+ /** Adds custom live analysts after the built-ins. */
1264
+ readonly extraAnalysts?: readonly TraceLiveAnalyst[];
1265
+ readonly onEvent: (event: TraceStreamEvent) => void | Promise<void>;
1266
+ }
1267
+ declare function classifyLiveActions(spans: readonly OtlpSpan[]): TraceLiveAction[];
1268
+ declare const defaultTraceLiveAnalysts: readonly TraceLiveAnalyst[];
1269
+ declare function analyzeLiveBatch(spans: readonly OtlpSpan[], opts?: LiveBatchOptions): TraceLiveBatch;
1270
+ declare function traceStreamEventsFromSpans(spans: readonly OtlpSpan[], opts?: TraceStreamReplayOptions): TraceStreamEvent[];
1271
+ declare function serializeTraceStreamEvent(event: TraceStreamEvent): string;
1272
+ declare function streamSessions(opts: TraceStreamOptions): Promise<void>;
1273
+
1109
1274
  interface TracesConfig {
1110
1275
  readonly registry?: AnalystRegistry;
1111
1276
  readonly analysts?: readonly Analyst[];
1277
+ readonly liveAnalysts?: readonly TraceLiveAnalyst[];
1112
1278
  readonly externalAnalyzers?: readonly ExternalAnalyzer[];
1113
1279
  readonly improvementAdapter?: ImprovementAdapter;
1114
1280
  }
@@ -1276,139 +1442,15 @@ declare function writeTraceImprovementArtifacts(result: Pick<TraceImprovementRes
1276
1442
  declare function runTraceImprovementLoop(opts: TraceImprovementOptions): Promise<TraceImprovementResult>;
1277
1443
  declare function saveReport(path: string, report: string): Promise<void>;
1278
1444
 
1279
- type TraceLiveSeverity = 'info' | 'low' | 'medium' | 'high';
1280
- interface TraceLiveEvidence {
1281
- readonly kind: 'metric' | 'span' | 'pattern';
1282
- readonly label: string;
1283
- readonly value: string;
1284
- readonly spanIds?: readonly string[];
1285
- }
1286
- interface TraceLiveFinding {
1287
- readonly schemaVersion: 1;
1288
- readonly kind: 'traces.live_finding';
1289
- readonly id: string;
1290
- readonly ruleId: string;
1291
- readonly fingerprint: string;
1292
- readonly severity: TraceLiveSeverity;
1293
- readonly title: string;
1294
- readonly claim: string;
1295
- readonly action: string;
1296
- readonly check: string;
1297
- readonly evidence: readonly TraceLiveEvidence[];
1298
- readonly session: TraceStreamSession;
1299
- readonly observedAt: string;
1300
- }
1301
- interface TraceStreamSession {
1302
- readonly harness: string;
1303
- readonly sessionId: string;
1304
- readonly cwd: string | null;
1305
- readonly path?: string;
1306
- }
1307
- interface TraceLiveBatch {
1308
- readonly schemaVersion: 1;
1309
- readonly kind: 'traces.live_batch';
1310
- readonly generatedAt: string;
1311
- readonly session: TraceStreamSession;
1312
- readonly spanCount: number;
1313
- readonly newSpanCount: number;
1314
- readonly toolCallCount: number;
1315
- readonly erroredToolCallCount: number;
1316
- readonly verificationCallCount: number;
1317
- readonly changeCallCount: number;
1318
- readonly findingCount: number;
1319
- readonly findings: readonly TraceLiveFinding[];
1320
- }
1321
- type TraceStreamEvent = {
1322
- readonly schemaVersion: 1;
1323
- readonly kind: 'traces.stream.session';
1324
- readonly event: 'session';
1325
- readonly generatedAt: string;
1326
- readonly session: TraceStreamSession;
1327
- readonly spanCount: number;
1328
- readonly startedAt: string | null;
1329
- readonly endedAt: string | null;
1330
- } | {
1331
- readonly schemaVersion: 1;
1332
- readonly kind: 'traces.stream.span';
1333
- readonly event: 'span';
1334
- readonly generatedAt: string;
1335
- readonly session: TraceStreamSession;
1336
- readonly span: OtlpSpan;
1337
- } | {
1338
- readonly schemaVersion: 1;
1339
- readonly kind: 'traces.stream.batch';
1340
- readonly event: 'analysis_batch';
1341
- readonly generatedAt: string;
1342
- readonly batch: TraceLiveBatch;
1343
- } | {
1344
- readonly schemaVersion: 1;
1345
- readonly kind: 'traces.stream.finding';
1346
- readonly event: 'finding';
1347
- readonly generatedAt: string;
1348
- readonly finding: TraceLiveFinding;
1349
- } | {
1350
- readonly schemaVersion: 1;
1351
- readonly kind: 'traces.stream.tick';
1352
- readonly event: 'tick';
1353
- readonly generatedAt: string;
1354
- readonly sessions: number;
1355
- readonly newSpans: number;
1356
- readonly findings: number;
1357
- };
1358
- interface LiveBatchOptions {
1359
- readonly generatedAt?: string;
1360
- readonly session?: Partial<TraceStreamSession>;
1361
- readonly newSpanCount?: number;
1362
- }
1363
- interface TraceStreamReplayOptions extends LiveBatchOptions {
1364
- readonly ref?: SessionRef;
1365
- readonly includeSpans?: boolean;
1366
- readonly includeFindings?: boolean;
1367
- }
1368
- interface TraceStreamOptions extends ScanOptions {
1369
- /** Observe sessions active within this window. Default 30 min. */
1370
- readonly windowMs?: number;
1371
- /** Poll interval. Default 5s. */
1372
- readonly intervalMs?: number;
1373
- /** Stop after one scan, useful for replay or tests. */
1374
- readonly once?: boolean;
1375
- /** Emit one JSON event per new span. Default true. */
1376
- readonly includeSpans?: boolean;
1377
- /** Emit live semantic findings. Default true. */
1378
- readonly includeFindings?: boolean;
1379
- /** Emit session and analysis-batch events. Default true. */
1380
- readonly includeBatches?: boolean;
1381
- readonly onEvent: (event: TraceStreamEvent) => void | Promise<void>;
1382
- }
1383
- declare function analyzeLiveBatch(spans: readonly OtlpSpan[], opts?: LiveBatchOptions): TraceLiveBatch;
1384
- declare function traceStreamEventsFromSpans(spans: readonly OtlpSpan[], opts?: TraceStreamReplayOptions): TraceStreamEvent[];
1385
- declare function serializeTraceStreamEvent(event: TraceStreamEvent): string;
1386
- declare function streamSessions(opts: TraceStreamOptions): Promise<void>;
1387
-
1388
1445
  /**
1389
- * Live trace observer — the reusable, event-driven core behind `traces watch`.
1446
+ * Live trace observer — compatibility wrapper behind `traces watch`.
1390
1447
  *
1391
- * `watchSessions` tails active coding-agent sessions across harnesses and emits
1392
- * structured events as it goes: every session's pipeline report each tick
1393
- * (`onReport`), and a deduped signal whenever a stuck loop first appears or
1394
- * grows (`onLoop`). It's read-only (never touches the agent or its harness) and
1395
- * cancellable via an `AbortSignal`, so a third party can subscribe to the live
1396
- * findings stream and feed it into any system — a dashboard, an alerter, a
1397
- * steering layer, or another agent — without shelling out to the CLI.
1448
+ * `streamSessions` is the single live scanner. `watchSessions` keeps the
1449
+ * callback-oriented SDK surface for existing users while consuming stream
1450
+ * events instead of running a second polling loop.
1398
1451
  */
1399
1452
 
1400
- /** A newly-observed (or grown) stuck loop in a live session. */
1401
- interface ObservedLoop {
1402
- harness: string;
1403
- sessionId: string;
1404
- cwd: string | null;
1405
- toolName: string;
1406
- argHash: string;
1407
- /** Identical repeated calls observed so far. */
1408
- occurrences: number;
1409
- /** Milliseconds between the first and last call in the loop. */
1410
- windowMs: number;
1411
- }
1453
+ type ObservedLoop = TraceLiveLoop;
1412
1454
  interface ObserverOptions {
1413
1455
  /** Harness ids/aliases to observe. Omit (or pass `all: true`) → every harness. */
1414
1456
  harnesses?: string[];
@@ -1443,11 +1485,6 @@ interface ObserverOptions {
1443
1485
  sessions: number;
1444
1486
  }) => void;
1445
1487
  }
1446
- /**
1447
- * Observe active sessions until `opts.signal` aborts. Returns a promise that
1448
- * resolves on abort. Errors in a single session are routed to `onError` and
1449
- * never stop the loop.
1450
- */
1451
1488
  declare function watchSessions(opts: ObserverOptions): Promise<void>;
1452
1489
 
1453
1490
  /**
@@ -1602,4 +1639,4 @@ interface ExecuteOptions {
1602
1639
  /** Send the NEW items (or, on dryRun, write the redacted OTLP that would send). */
1603
1640
  declare function executeUpload(plan: UploadPlan, opts?: ExecuteOptions): Promise<UploadResult>;
1604
1641
 
1605
- export { ACTOR_ATTR, ATTR, type Actor, type AdoptionOptions, type AdoptionReport, AmpAdapter, type AnalyzeOptions, type AnalyzeResult, type BuildPolicyEvidenceOptions, type BuildSessionIndexOptions, type BuildTraceFindingPacketOptions, CODING_REDACTION_RULES, CONTENT_CAP, CORRECTIVE_REACTIONS, ClaudeAdapter, CodexAdapter, type CollectOptions, type CollectPolicyEvidenceOptions, type CollectSessionIndexOptions, CopilotAdapter, DEFAULT_HARNESS, type ExecuteOptions, type ExternalAnalysisResult, type ExternalAnalyzer, FactoryAdapter, type FindingStore, ForgeAdapter, GeminiAdapter, type HarnessTraceAdapter, INGEST_SOURCE_CLI, type ImprovementAdapter, type ImprovementAdapterInput, type ImprovementProposal, type InspectSessionIndexOptions, type LiveBatchOptions, type LocateOptions, type ObservedLoop, type ObserverOptions, OpencodeAdapter, type OtlpSpan, type OtlpSpanKind, type OtlpStatusCode, PiAdapter, type PipelineOptions, type PipelineReport, type PlanOptions, type PolicyEvidenceLoopSummary, type PolicyEvidenceRecord, type PolicyEvidenceToolSummary, QwenAdapter, REACTIONS, type Reaction, type ReactionReport, type ReactionSessionReport, type RecommendationWriter, type Redactor, type RepoAttrs, type RepoResolution, type RepoResolutionSource, type RunResult, type RuntimeTrace, type ScanOptions, type ScannedSession, type SessionBatch, type SessionRef, type SpanInput, type SpanRedaction, TRACES_REDACTION_RULES, type TraceClaim, type TraceContextIndex, type TraceContextRoot, type TraceEvidenceExportOptions, type TraceEvidenceExportResult, type TraceEvidenceFormatOption, type TraceEvidenceInputFormat, type TraceEvidenceRow, type TraceFindingPacket, type TraceImprovementArtifacts, type TraceImprovementOptions, type TraceImprovementResult, type TraceIndexedFile, type TraceInspectionArea, type TraceInspectionFinding, type TraceInspectionReport, type TraceInspectionSeverity, type TraceInvestigationOptions, type TraceInvestigationResult, type TraceLiveBatch, type TraceLiveEvidence, type TraceLiveFinding, type TraceLiveSeverity, type TraceRecommendation, type TraceReplayProof, type TraceSessionIndex, type TraceSessionIndexRow, type TraceStoreInvestigationOptions, type TraceStoreInvestigationResult, type TraceStreamEvent, type TraceStreamOptions, type TraceStreamReplayOptions, type TraceStreamSession, type TracesConfig, type TriggerPair, type UploadBackend, type UploadItem, type UploadPlan, type UploadRecord, type UploadResult, type UploadState, type UserPromptInput, alreadyUploaded, analyzeAdoption, analyzeLiveBatch, analyzeReactions, analyzeSpans, applyRedactor, buildPolicyEvidenceRecord, buildSessionIndex, buildSessionIndexFromRows, buildTraceFindingPacket, capText, classifyReaction, claudeActor, codexActor, collectPolicyEvidence, collectSessionIndex, collectSessions, commandAnalyzer, commandRedactor, countSkillRunsJsonl, executeUpload, exportTraceEvidenceFile, exportTraceEvidenceRows, exportTraceEvidenceText, haloAnalyzer, inspectSessionIndex, knownHarnesses, listAdapters, loadState, loadTracesConfig, looksLikeAgentPrompt, mergeTracesConfig, normalizeRemote, parseIsoToEpochMs, parseSession, parseSince, planUpload, readSessionIndexFile, redactSpans, renderInspectionReport, resolveAdapter, resolveRepoAttrs, resolveSessionRepoAttrs, runCommand, runExternalAnalyzers, runPipelines, runTraceImprovementLoop, runTraceInvestigation, runTraceStoreInvestigation, saveReport, saveState, scanSessions, selectAdapters, serializeInspectionReport, serializePolicyEvidence, serializeSessionIndex, serializeSpans, serializeTraceStreamEvent, sessionHash, span, stampRepoAttrs, streamSessions, textIsSynthetic, toOpenInferenceSpan, toRuntimeStore, toTraceSpanEvents, traceStreamEventsFromSpans, uploadKey, userPromptSpan, watchSessions, writeInspectionReportFile, writeOtlpFile, writePolicyEvidenceFile, writeSessionIndexFile, writeTraceEvidenceExportFile, writeTraceImprovementArtifacts };
1642
+ export { ACTOR_ATTR, ATTR, type Actor, type AdoptionOptions, type AdoptionReport, AmpAdapter, type AnalyzeOptions, type AnalyzeResult, type BuildPolicyEvidenceOptions, type BuildSessionIndexOptions, type BuildTraceFindingPacketOptions, CODING_REDACTION_RULES, CONTENT_CAP, CORRECTIVE_REACTIONS, ClaudeAdapter, CodexAdapter, type CollectOptions, type CollectPolicyEvidenceOptions, type CollectSessionIndexOptions, CopilotAdapter, DEFAULT_HARNESS, type ExecuteOptions, type ExternalAnalysisResult, type ExternalAnalyzer, FactoryAdapter, type FindingStore, ForgeAdapter, GeminiAdapter, type HarnessTraceAdapter, INGEST_SOURCE_CLI, type ImprovementAdapter, type ImprovementAdapterInput, type ImprovementProposal, type InspectSessionIndexOptions, type LiveBatchOptions, type LocateOptions, type ObservedLoop, type ObserverOptions, OpencodeAdapter, type OtlpSpan, type OtlpSpanKind, type OtlpStatusCode, PiAdapter, type PipelineOptions, type PipelineReport, type PlanOptions, type PolicyEvidenceLoopSummary, type PolicyEvidenceRecord, type PolicyEvidenceToolSummary, QwenAdapter, REACTIONS, type Reaction, type ReactionReport, type ReactionSessionReport, type RecommendationWriter, type Redactor, type RepoAttrs, type RepoResolution, type RepoResolutionSource, type RunResult, type RuntimeTrace, type ScanOptions, type ScannedSession, type SessionBatch, type SessionRef, type SpanInput, type SpanRedaction, TRACES_REDACTION_RULES, type TraceClaim, type TraceContextIndex, type TraceContextRoot, type TraceEvidenceExportOptions, type TraceEvidenceExportResult, type TraceEvidenceFormatOption, type TraceEvidenceInputFormat, type TraceEvidenceRow, type TraceFindingPacket, type TraceImprovementArtifacts, type TraceImprovementOptions, type TraceImprovementResult, type TraceIndexedFile, type TraceInspectionArea, type TraceInspectionFinding, type TraceInspectionReport, type TraceInspectionSeverity, type TraceInvestigationOptions, type TraceInvestigationResult, type TraceLiveAction, type TraceLiveActionKind, type TraceLiveAnalysisContext, type TraceLiveAnalyst, type TraceLiveBatch, type TraceLiveEvidence, type TraceLiveFinding, type TraceLiveLoop, type TraceLiveSeverity, type TraceRecommendation, type TraceReplayProof, type TraceSessionIndex, type TraceSessionIndexRow, type TraceStoreInvestigationOptions, type TraceStoreInvestigationResult, type TraceStreamEvent, type TraceStreamOptions, type TraceStreamReplayOptions, type TraceStreamSession, type TracesConfig, type TriggerPair, type UploadBackend, type UploadItem, type UploadPlan, type UploadRecord, type UploadResult, type UploadState, type UserPromptInput, alreadyUploaded, analyzeAdoption, analyzeLiveBatch, analyzeReactions, analyzeSpans, applyRedactor, buildPolicyEvidenceRecord, buildSessionIndex, buildSessionIndexFromRows, buildTraceFindingPacket, capText, classifyLiveActions, classifyReaction, claudeActor, codexActor, collectPolicyEvidence, collectSessionIndex, collectSessions, commandAnalyzer, commandRedactor, countSkillRunsJsonl, defaultTraceLiveAnalysts, executeUpload, exportTraceEvidenceFile, exportTraceEvidenceRows, exportTraceEvidenceText, haloAnalyzer, inspectSessionIndex, knownHarnesses, listAdapters, loadState, loadTracesConfig, looksLikeAgentPrompt, mergeTracesConfig, normalizeRemote, parseIsoToEpochMs, parseSession, parseSince, planUpload, readSessionIndexFile, redactSpans, renderInspectionReport, resolveAdapter, resolveRepoAttrs, resolveSessionRepoAttrs, runCommand, runExternalAnalyzers, runPipelines, runTraceImprovementLoop, runTraceInvestigation, runTraceStoreInvestigation, saveReport, saveState, scanSessions, selectAdapters, serializeInspectionReport, serializePolicyEvidence, serializeSessionIndex, serializeSpans, serializeTraceStreamEvent, sessionHash, span, stampRepoAttrs, streamSessions, textIsSynthetic, toOpenInferenceSpan, toRuntimeStore, toTraceSpanEvents, traceStreamEventsFromSpans, uploadKey, userPromptSpan, watchSessions, writeInspectionReportFile, writeOtlpFile, writePolicyEvidenceFile, writeSessionIndexFile, writeTraceEvidenceExportFile, writeTraceImprovementArtifacts };
package/dist/index.js CHANGED
@@ -29,6 +29,7 @@ import {
29
29
  buildSessionIndexFromRows,
30
30
  buildTraceFindingPacket,
31
31
  capText,
32
+ classifyLiveActions,
32
33
  classifyReaction,
33
34
  claudeActor,
34
35
  codexActor,
@@ -37,6 +38,7 @@ import {
37
38
  commandAnalyzer,
38
39
  commandRedactor,
39
40
  countSkillRunsJsonl,
41
+ defaultTraceLiveAnalysts,
40
42
  executeUpload,
41
43
  exportTraceEvidenceFile,
42
44
  exportTraceEvidenceRows,
@@ -93,7 +95,7 @@ import {
93
95
  writeSessionIndexFile,
94
96
  writeTraceEvidenceExportFile,
95
97
  writeTraceImprovementArtifacts
96
- } from "./chunk-G3OBZSWY.js";
98
+ } from "./chunk-FCTZLWP7.js";
97
99
 
98
100
  // src/collect.ts
99
101
  async function collectSessions(opts = {}) {
@@ -151,6 +153,7 @@ export {
151
153
  buildSessionIndexFromRows,
152
154
  buildTraceFindingPacket,
153
155
  capText,
156
+ classifyLiveActions,
154
157
  classifyReaction,
155
158
  claudeActor,
156
159
  codexActor,
@@ -161,6 +164,7 @@ export {
161
164
  commandRedactor,
162
165
  countSkillRunsJsonl,
163
166
  createHostedClient,
167
+ defaultTraceLiveAnalysts,
164
168
  executeUpload,
165
169
  exportTraceEvidenceFile,
166
170
  exportTraceEvidenceRows,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/traces",
3
- "version": "0.8.18",
3
+ "version": "0.8.19",
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",