@tangle-network/traces 0.12.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -3
- package/dist/{chunk-CV22VGCK.js → chunk-7DOBU7BJ.js} +16040 -12291
- package/dist/cli.js +221 -169
- package/dist/index.d.ts +933 -59
- package/dist/index.js +109 -1
- package/docs/trace-analysts.md +307 -0
- package/install.sh +0 -0
- package/package.json +23 -19
- package/skills/build-trace-analyst/SKILL.md +3 -0
- package/skills/inspect-agent-traces/SKILL.md +22 -20
package/README.md
CHANGED
|
@@ -30,6 +30,8 @@ Emitting the contract is the supported way to integrate a new system. The adapte
|
|
|
30
30
|
- [Live stream](#live-stream)
|
|
31
31
|
- [Watch a run tree](#watch-a-run-tree)
|
|
32
32
|
- [Improvement engine](#improvement-engine)
|
|
33
|
+
- [Ask questions](#ask-questions)
|
|
34
|
+
- [Session facts](#session-facts)
|
|
33
35
|
- [Session index](#session-index)
|
|
34
36
|
- [Session bundle](#session-bundle) · [Two views](#two-views-two-consumers)
|
|
35
37
|
- [Policy-mining evidence](#policy-mining-evidence)
|
|
@@ -38,6 +40,7 @@ Emitting the contract is the supported way to integrate a new system. The adapte
|
|
|
38
40
|
- [Agent skills](#agent-skills)
|
|
39
41
|
- [Library (SDK)](#library-sdk)
|
|
40
42
|
- [Examples](#examples)
|
|
43
|
+
- [Audit benchmark](#audit-benchmark)
|
|
41
44
|
- [Develop](#develop)
|
|
42
45
|
|
|
43
46
|
## Install
|
|
@@ -179,6 +182,10 @@ Two of those analyses are why the loop shape exists at all:
|
|
|
179
182
|
caused which subsequent round. A link whose target is missing is kept and marked,
|
|
180
183
|
because a shorter chain than the producer recorded is the opposite of the truth.
|
|
181
184
|
|
|
185
|
+
Parent and verdict lookups use both trace and span IDs.
|
|
186
|
+
Steering edges retain `causeTraceId` and `effectTraceId`, including unresolved targets.
|
|
187
|
+
Generic chat transcripts report unknown execution status because message text does not establish completion.
|
|
188
|
+
|
|
182
189
|
Nothing is repaired on the way in. A span with an unreadable timestamp is
|
|
183
190
|
reported and excluded, because a synthesized value would put invented work into a
|
|
184
191
|
total.
|
|
@@ -221,6 +228,8 @@ not the way to integrate a system you own — for that, [emit the contract](#int
|
|
|
221
228
|
|
|
222
229
|
Every adapter captures the conversation stored in one session file: the **user's prompt** and the **assistant's response** text, plus tool calls/results and token usage.
|
|
223
230
|
Claude Code's nested subagent files are folded into the parent trace.
|
|
231
|
+
Claude source UUIDs remain searchable in `traces.claude.source_*` attributes, while exported OTLP IDs use deterministic `deriveHexId` values.
|
|
232
|
+
The Claude transcript proves turns and tool calls, but does not prove loop iterations or causal links, so those fields remain absent.
|
|
224
233
|
Codex stores each worker in a separate session file; add `--workflow` to resolve the connected coordinator and worker files.
|
|
225
234
|
`github-copilot` is the one exception: its log format carries no user prompt.
|
|
226
235
|
Factory stores token totals in `.settings.json`, not per turn.
|
|
@@ -237,6 +246,8 @@ traces analyze --harness codex --current --latest-turn --workflow # current tur
|
|
|
237
246
|
traces analyze --harness claude-code --session <path> --latest-turn # latest task plus its subagents
|
|
238
247
|
traces investigate --all --last 10 --out report.md # explicit investigation alias
|
|
239
248
|
traces improve --all --last 10 --dir .traces/improvement
|
|
249
|
+
traces ask --harness codex --session <id> --question "Which commands failed?"
|
|
250
|
+
traces facts --harness codex --session <id> # the deterministic facts sheet, $0
|
|
240
251
|
traces analyze --all --since 2026-06-18 --out report.md
|
|
241
252
|
traces validate spans.otlp.jsonl # conformance; exit 1 only when it is not a trace
|
|
242
253
|
traces validate results/sessions --out conformance.md # a whole directory of exports
|
|
@@ -285,11 +296,16 @@ See [Replay verification](./docs/replay-verify.md) for setup, semantics, and hon
|
|
|
285
296
|
| `--cwd <dir>` | Filter by working directory |
|
|
286
297
|
| `--since <t>` | `upload`: window, `30m`/`2h`/`7d` or ISO (default 24h); `analyze`: ISO cutoff |
|
|
287
298
|
| `--out <path>` | Write the report to a file |
|
|
288
|
-
| `--dir <path>` | `improve`: write the full artifact pack to this directory |
|
|
289
|
-
| `--otlp <file\|dir>` | **READ** OTLP-JSONL from any system, skipping the adapters; a directory reads the OTLP files under it (only `otlp/` when the producer made one) and names the JSONL that is not OTLP. `validate`, `analyze`, `investigate`, `improve`, `stream` |
|
|
299
|
+
| `--dir <path>` | `improve`: write the full artifact pack to this directory; `ask`: write `answers.json` + `report.md` there |
|
|
300
|
+
| `--otlp <file\|dir>` | **READ** OTLP-JSONL from any system, skipping the adapters; a directory reads the OTLP files under it (only `otlp/` when the producer made one) and names the JSONL that is not OTLP. `validate`, `analyze`, `investigate`, `improve`, `ask`, `stream` |
|
|
290
301
|
| `--otlp-out <path>` | **WRITE** the OTLP artifact here (also evidence provenance / dry-run upload preview) |
|
|
291
302
|
| `--format <kind>` | File `analyze`, `export`, or `stream`: `auto`, `policy-evidence`, `sandbox-events`, `openinference`, `intelligence-spans`, or `chat-trajectory` |
|
|
303
|
+
| `--source-bundle <dir>` | `analyze` / `investigate` / `improve` / `ask`: read a retained full bundle and explicitly grant source-field reads |
|
|
292
304
|
| `--llm` / `--budget <usd>` | Enable agentic analysts (needs `TANGLE_API_KEY` + Python with `agent-eval-rpc[dspy]`) / cap their spend |
|
|
305
|
+
| `--question <text>` | `ask`: one question, repeatable. Kept short so the engine sees it whole |
|
|
306
|
+
| `--questions <file>` | `ask`: JSON array of questions — strings, or `{ id?, question, instructions?, answerSchema? }` |
|
|
307
|
+
| `--question-budget <usd>` | `ask`: provider ceiling for ONE question; `--budget` is the ceiling shared by all of them |
|
|
308
|
+
| `--concurrency <n>` | `ask`: questions running at once (default 4); `import-codetracebench`: trajectories imported at once |
|
|
293
309
|
| `--config <path>` | `analyze` / `investigate` / `improve` / `stream`: load BYO analysts, live analysts, and external analyzers |
|
|
294
310
|
| `--interval <s>` / `--window <m>` | `watch` / live `stream`: poll seconds (sessions 5, run tree 2) / active-session window minutes (default 30) |
|
|
295
311
|
| `--min-loop <n>` | Identical repeated calls before flagging a loop (default 3) |
|
|
@@ -400,7 +416,10 @@ RESULT no-winner · budget-exhausted
|
|
|
400
416
|
```
|
|
401
417
|
|
|
402
418
|
`traces analyze --supervisor-run-dir <dir>` writes the full report for the same run.
|
|
403
|
-
|
|
419
|
+
It also reads `failure.json` when Runtime fails before the first spawn.
|
|
420
|
+
Pass a parent directory to combine reports from its Runtime runs.
|
|
421
|
+
Eval owns discovery, normalization, and analysis; Traces adds recorded coordination requests and delivery receipts.
|
|
422
|
+
Old Loops directories are unsupported.
|
|
404
423
|
|
|
405
424
|
Replay and resume are a separate concern: they belong to the durable spawn journal, and nothing here routes them through telemetry.
|
|
406
425
|
|
|
@@ -437,6 +456,94 @@ The config can export:
|
|
|
437
456
|
Traces does not pretend that an action is a measured candidate.
|
|
438
457
|
Use `agent-eval` to propose and compare candidate changes, `agent-runtime` to package an approved improvement, and `agent-interface` to represent profile edits.
|
|
439
458
|
|
|
459
|
+
## Ask questions
|
|
460
|
+
|
|
461
|
+
`traces ask` answers free-form questions about the selected sessions.
|
|
462
|
+
Each question becomes its own recursive investigation over the same trace, and the questions run at the same time under one budget.
|
|
463
|
+
|
|
464
|
+
```bash
|
|
465
|
+
traces ask --harness codex --session <id> \
|
|
466
|
+
--question "Which shell commands exited non-zero, and what were they?" \
|
|
467
|
+
--question "What was the last thing the human asked for?" \
|
|
468
|
+
--dir .traces/ask
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Ask many questions from a file, and hold an answer to a shape a scorer can compare:
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
traces ask --all --last 3 --questions questions.json --concurrency 6 --budget 2 --dir .traces/ask
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
```json
|
|
478
|
+
[
|
|
479
|
+
"Which pull requests did this session open?",
|
|
480
|
+
{
|
|
481
|
+
"id": "merged-prs",
|
|
482
|
+
"question": "Which pull requests were merged?",
|
|
483
|
+
"instructions": "Count only merges the trace records, not merges the agent said it would do.",
|
|
484
|
+
"answerSchema": { "type": "array", "items": { "type": "integer" } }
|
|
485
|
+
}
|
|
486
|
+
]
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
An entry is a question string, or an object with `question` and an optional `id`, `instructions`, and `answerSchema`.
|
|
490
|
+
The schema accepts a small JSON Schema subset: `type`, `properties`, `required`, `additionalProperties`, `items`, `enum`, and `const`.
|
|
491
|
+
Any other keyword is rejected when the run starts, because a constraint that is silently ignored would let a wrong answer pass as checked.
|
|
492
|
+
|
|
493
|
+
The command writes two artifacts to `--dir`:
|
|
494
|
+
|
|
495
|
+
| File | Contents |
|
|
496
|
+
|---|---|
|
|
497
|
+
| `answers.json` | per question: the answer text, the parsed answer, every citation with whether it resolves, accepted findings, evidence-gate rejections by reason, model calls, tool calls, cost with provenance, and latency |
|
|
498
|
+
| `report.md` | the same run as readable Markdown: a summary line, a per-question table, then each answer with its citation and rejection notes |
|
|
499
|
+
|
|
500
|
+
What it checks, and what it costs:
|
|
501
|
+
|
|
502
|
+
- **Facts first.** Every question receives the deterministic [session-facts sheet](#session-facts) as prepared context before its first model call, at $0. The sheet is not citable; it names the span ids behind each fact.
|
|
503
|
+
- **Citations.** Every `trace://<trace_id>/span/<span_id>` URI in an answer is looked up in the trace. An answer that cites a span the trace does not hold fails.
|
|
504
|
+
- **Budget.** `--budget` is one ceiling shared by every question. `--question-budget` bounds one question. The run refuses to start when the budget cannot cover a single model call, and warns when the budget admits fewer concurrent calls than `--concurrency`.
|
|
505
|
+
- **Cost.** Each cost carries its provenance: `observed` from a provider receipt, `estimated` from token counts, or `uncaptured`. An uncaptured cost stays null; it never becomes zero.
|
|
506
|
+
- **Exit code.** `ask` writes both artifacts first, then exits 1 when any question failed, returned no answer, broke its schema, or cited a span that does not exist. A failed question never costs the other answers, and neither does Ctrl-C: the answers already bought are recorded, the questions the run never reached are recorded as `aborted`, and both artifacts are still written.
|
|
507
|
+
- **Wall time.** `totals.wallTimeMs` covers the whole run, including writing and indexing the trace file; `totals.setupTimeMs` says how much of it that setup was. `totals.peakConcurrency` is how many questions actually overlapped, which is at most `min(--concurrency, questions)`.
|
|
508
|
+
|
|
509
|
+
`ask` uses the same engine and credentials as `--llm`, so it needs `TANGLE_API_KEY` and a Python interpreter with `agent-eval-rpc[dspy]`.
|
|
510
|
+
Do not pass `--llm`; the command is model-backed by definition.
|
|
511
|
+
|
|
512
|
+
## Session facts
|
|
513
|
+
|
|
514
|
+
`traces facts` prints the deterministic session-facts sheet: the answers a session audit needs first, computed straight from the spans.
|
|
515
|
+
No model call, no engine, no budget — it costs $0 and always returns the same sheet for the same spans.
|
|
516
|
+
|
|
517
|
+
```bash
|
|
518
|
+
traces facts --harness codex --session <id> # JSON on stdout
|
|
519
|
+
traces facts --harness codex --last 5 --format text # the short readable form
|
|
520
|
+
traces facts --otlp spans.otlp.jsonl --out facts.json
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
| Fact | What it is |
|
|
524
|
+
|---|---|
|
|
525
|
+
| `toolCalls` | TOOL spans **this session's agent** actually invoked. Synthesized lifecycle spans are excluded and counted in `synthesizedToolSpans`, so the total is not high by the number of subagents; calls a spawned subagent made are excluded and counted in `subagentToolSpans` |
|
|
526
|
+
| `toolCallsByName` | the same calls by tool name, so a category decision is the reader's, not a guess |
|
|
527
|
+
| `subagentToolSpans` | TOOL spans excluded because a subagent, not this agent, made the call. Zero on a trace that carries no subagent records |
|
|
528
|
+
| `subagents` | every subagent-spawning call with the task name the adapter recorded |
|
|
529
|
+
| `pullRequests` | the pull requests the commands created and merged, each named by number or head branch, with the command span and how the identity was joined. Scanned the way a shell reads the script, so a `gh pr create` inside a heredoc body is not a command that ran |
|
|
530
|
+
| `humanTurns` | `user.prompt` turns a person typed into this session, in order, with the timestamp. Inherited fork or compaction history, harness-injected blocks, and a second record of the same turn are excluded — each listed in `excludedTurns` with its reason and span ids, never silently dropped. `turnsByActor` shows every turn by actor so the filter is checkable |
|
|
531
|
+
| `finalMessages` | the last message of the session's own agent, and of each subagent task, kept apart |
|
|
532
|
+
| `changedFiles` | paths the harness recorded for the changes it applied, plus paths named by patch headers and file-editing tool arguments, with the operation |
|
|
533
|
+
| `firstRecordAt` / `lastRecordAt` | the earliest span start and latest span end among this session's own records — a subagent that outlives the session does not stretch its window |
|
|
534
|
+
| `unreadRecords` | records the session reader could not parse, from the session's integrity receipt |
|
|
535
|
+
| `tokenTotal` | the harness's own cumulative token total, when a span carries `traces.session.total_tokens` |
|
|
536
|
+
|
|
537
|
+
Three rules hold for every field:
|
|
538
|
+
|
|
539
|
+
- **Every fact is about this session's own agent.** Some harnesses fold a spawned agent's transcript into the parent's trace, so a trace can carry both what this agent did and what a child it spawned did. The child's work is named in `subagents` and counted in `subagentToolSpans`; it is never added to the parent's totals.
|
|
540
|
+
- **Every fact names its span ids.** `spanIds` lists the spans the value was computed from, so any number here can be opened and checked. The sheet itself is not a span and cannot be cited.
|
|
541
|
+
- **A fact the spans cannot support is `null` with its reason.** It is never guessed, and never a silent zero. `partial` marks a measured value that is known to be incomplete — a truncated patch, or a list above the entry cap.
|
|
542
|
+
|
|
543
|
+
`facts` exits non-zero when a selected session cannot be read at all: a session that produced no record spans would otherwise print a sheet of zeros stating, in the sheet's own voice, that the session did nothing.
|
|
544
|
+
|
|
545
|
+
The same sheet reaches the model-backed analysts as prepared context, before their first model call — see [Trace analysts](docs/trace-analysts.md#session-facts-as-prepared-context).
|
|
546
|
+
|
|
440
547
|
## Session index
|
|
441
548
|
|
|
442
549
|
`traces index` writes one general JSON catalog over the selected sessions.
|
|
@@ -602,6 +709,8 @@ traces analyze --last 1 --analyzer prime
|
|
|
602
709
|
traces analyze --last 1 --analyzer my-installed-command
|
|
603
710
|
```
|
|
604
711
|
|
|
712
|
+
To ask your own question instead of the built-in kinds, use [`traces ask`](#ask-questions).
|
|
713
|
+
|
|
605
714
|
HALO returns a diagnosis report.
|
|
606
715
|
Hodoscope samples distinct behaviors and marks every sample `needs_review`.
|
|
607
716
|
Prime posts the full span projection to an OpenAI-compatible bridge (`TRACES_PRIME_BRIDGE_URL`, default `http://localhost:4181`) and returns validated findings with span evidence.
|
|
@@ -624,6 +733,7 @@ See [`examples/external-engines.ts`](./examples/external-engines.ts).
|
|
|
624
733
|
> `--llm` also needs a Python interpreter with `agent-eval-rpc[dspy]` installed, because agent-eval's model-backed analysts run through the DSPy RLM engine out of process; set `TRACES_PYTHON` to choose the interpreter.
|
|
625
734
|
> The bridge protocol is version-locked: install the exact version matching this package's `@tangle-network/agent-eval` dependency (`pip install "agent-eval-rpc[dspy]==$(npm view @tangle-network/traces dependencies.@tangle-network/agent-eval)"`) — a skewed bridge kills every agentic analyst at startup.
|
|
626
735
|
> When `--llm` was requested and every agentic analyst fails, `analyze`/`investigate`/`improve` still write the deterministic report, then exit 1 with each analyst's underlying error.
|
|
736
|
+
> A requested `--analyzer` behaves the same way: its error is written into the report, and `analyze` then exits 1 naming every external analyzer that failed.
|
|
627
737
|
> Every deterministic command — `list`, `analyze` without `--llm`, `convert`, `index`, `inspect`, `export`, `evidence`, `stream`, `watch`, `analyze --supervisor-run-dir` — needs neither a key nor Python.
|
|
628
738
|
|
|
629
739
|
## Agent skills
|
|
@@ -683,6 +793,7 @@ The CLI is a thin consumer of these exports.
|
|
|
683
793
|
| `analyzeSpans` | `(spans, { registry?, ai?, budgetUsd? }) → AnalyzeResult` | run built-in analysts, or **your own** via `registry` |
|
|
684
794
|
| `runTraceInvestigation` | `(TraceInvestigationOptions) → TraceInvestigationResult` | typed findings with actions/checks, execution facts, external analyzer output, and report |
|
|
685
795
|
| `runTraceImprovement` | `(TraceImprovementOptions) → TraceImprovementResult` | writes the full findings, evidence, report, and trace artifact pack |
|
|
796
|
+
| `runTraceQuestions` | `(TraceQuestionsOptions) → TraceQuestionsResult` | ask many free-form questions of one span list, concurrently, under one shared cost ledger; keeps each answer and checks its citations |
|
|
686
797
|
| `buildTraceFindingPacket` | `({ findings }) → TraceFindingPacket` | render any `AnalystFinding[]` without changing its schema |
|
|
687
798
|
| `runTraceStoreInvestigation` | `({ traceStore }) → TraceStoreInvestigationResult` | run the same packet layer over a hosted/custom `TraceAnalysisStore` |
|
|
688
799
|
| `loadTracesConfig` | `(path?) → TracesConfig \| undefined` | load BYO analysts and external analyzers |
|
|
@@ -750,6 +861,24 @@ Runnable, in [`examples/`](./examples):
|
|
|
750
861
|
| [`register-harness.ts`](./examples/register-harness.ts) | add a new harness by implementing `HarnessTraceAdapter` |
|
|
751
862
|
| [`external-engines.ts`](./examples/external-engines.ts) | drive HALO, Hodoscope, and an external PII scrubber |
|
|
752
863
|
|
|
864
|
+
## Audit benchmark
|
|
865
|
+
|
|
866
|
+
[`bench/audit/`](./bench/audit) asks nineteen questions about three synthetic agent
|
|
867
|
+
sessions and scores an arm's answers by exact match, with no model judging anything. It
|
|
868
|
+
measures whether reading a session with `traces` beats reading the raw files, on facts a
|
|
869
|
+
single read cannot cover: counts over hundreds of records, timestamps of specific records,
|
|
870
|
+
and facts linked across tool calls and sessions.
|
|
871
|
+
|
|
872
|
+
```bash
|
|
873
|
+
pnpm bench:audit fixtures --out /tmp/audit-bench # the tree an arm may read
|
|
874
|
+
pnpm bench:audit gold --out /tmp/audit-gold.json # the answer key, kept away from the arm
|
|
875
|
+
pnpm bench:audit score /tmp/arm-answers.json --fixtures /tmp/audit-bench
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
[`bench/audit/README.md`](./bench/audit/README.md) documents the questions, the scoring
|
|
879
|
+
rules, the answers-file format, and which answers are re-derived from the generated bytes.
|
|
880
|
+
The benchmark ships outside the published package; the model arms are run by hand.
|
|
881
|
+
|
|
753
882
|
## Develop
|
|
754
883
|
|
|
755
884
|
```bash
|