@tangle-network/traces 0.15.4 → 0.16.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 +130 -13
- package/dist/{chunk-SKOW5V5U.js → chunk-G5KKIQZ6.js} +578 -283
- package/dist/cli.js +606 -42
- package/dist/index.d.ts +140 -32
- package/dist/index.js +27 -9
- package/docs/trace-analysts.md +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ Emitting the contract is the supported way to integrate a new system. The adapte
|
|
|
23
23
|
|
|
24
24
|
- [Install](#install)
|
|
25
25
|
- [Quick start](#quick-start)
|
|
26
|
+
- [Gate CI on a trace contract](#gate-ci-on-a-trace-contract)
|
|
26
27
|
- [Integrate your own system](#integrate-your-own-system)
|
|
27
28
|
- [What it finds](#what-it-finds)
|
|
28
29
|
- [Supported harnesses](#supported-harnesses)
|
|
@@ -32,10 +33,12 @@ Emitting the contract is the supported way to integrate a new system. The adapte
|
|
|
32
33
|
- [Improvement engine](#improvement-engine)
|
|
33
34
|
- [Ask questions](#ask-questions)
|
|
34
35
|
- [Session facts](#session-facts)
|
|
36
|
+
- [Diff two runs](#diff-two-runs)
|
|
37
|
+
- [MCP server](#mcp-server)
|
|
35
38
|
- [Session index](#session-index)
|
|
36
39
|
- [Session bundle](#session-bundle) · [Two views](#two-views-two-consumers)
|
|
37
40
|
- [Policy-mining evidence](#policy-mining-evidence)
|
|
38
|
-
- [Upload to the Intelligence Platform](#upload-to-the-intelligence-platform)
|
|
41
|
+
- [Upload to the Intelligence Platform](#upload-to-the-intelligence-platform) · [Check before sharing](#check-before-sharing)
|
|
39
42
|
- [Trace analysts](#trace-analysts)
|
|
40
43
|
- [Agent skills](#agent-skills)
|
|
41
44
|
- [Library (SDK)](#library-sdk)
|
|
@@ -62,6 +65,7 @@ Requires Node ≥ 22.
|
|
|
62
65
|
traces validate spans.otlp.jsonl # what can this trace answer?
|
|
63
66
|
traces analyze --otlp spans.otlp.jsonl # analyse it, no adapter involved
|
|
64
67
|
traces analyze --harness claude-code --last 1 # or read a coding agent's own log
|
|
68
|
+
traces check session.jsonl --contract contract.json # gate CI on the path the run took
|
|
65
69
|
traces improve --harness claude-code --last 5 --dir .traces/improvement
|
|
66
70
|
traces watch --all
|
|
67
71
|
traces stream --all --mode findings
|
|
@@ -82,6 +86,48 @@ See [Trace analysts](#trace-analysts).
|
|
|
82
86
|
It writes one typed result, one report, flattened evidence rows, and the canonical OTLP trace.
|
|
83
87
|
Each finding already contains the claim, evidence, recommended action, confidence, and validation plan.
|
|
84
88
|
|
|
89
|
+
## Gate CI on a trace contract
|
|
90
|
+
|
|
91
|
+
Two runs can give the same answer while one of them calls a forbidden tool.
|
|
92
|
+
An output check passes both; a trace contract fails the wrong one.
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"name": "refund-desk",
|
|
97
|
+
"run": { "requireCompleted": true, "maxDurationMs": 300000 },
|
|
98
|
+
"tools": { "required": ["Read"], "forbidden": ["Bash"], "allowed": ["Read", "Grep"], "maxCalls": 10, "enforced": true },
|
|
99
|
+
"retries": { "reads": ["Read", "Grep"] },
|
|
100
|
+
"llm": { "maxCalls": 10 }
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
claude -p "$TASK" --tools Read,Grep --strict-mcp-config --output-format stream-json --verbose > run.jsonl
|
|
106
|
+
traces check run.jsonl --contract refund-desk.json --junit contract.xml
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`tools.enforced` fails when the harness offered the model a tool the contract does not allow, which a flag such as `--tools` does not prove.
|
|
110
|
+
Without `--strict-mcp-config`, the run above is also offered every MCP server the account or project configures.
|
|
111
|
+
`retries` fails when a tool repeats a call with the same arguments and nothing proves the repeat is safe.
|
|
112
|
+
|
|
113
|
+
The trace is `claude -p --output-format stream-json` output, a Claude Code or Codex session file, OTLP spans (a file or a directory), or trace evidence; `--format` names it when the file could be read as more than one.
|
|
114
|
+
Only stream-json output records the offered tools and the run's own result.
|
|
115
|
+
**A saved interactive session file carries no run status at all** (the `result` record above only exists in `-p` mode), so `run.requireCompleted`/`run.allowedStatuses` always fail on one — write a contract for a session file with no `run` key, like the `refund-desk` example above running against `run.jsonl`.
|
|
116
|
+
Every rule prints `pass`, `fail`, `error`, or `skipped` (a rule of an alternative path that did not decide the result).
|
|
117
|
+
|
|
118
|
+
| Exit | Meaning |
|
|
119
|
+
|---|---|
|
|
120
|
+
| 0 | Every rule passed. |
|
|
121
|
+
| 1 | A rule failed. |
|
|
122
|
+
| 2 | The contract is malformed or contradicts itself, or a rule could not be evaluated. |
|
|
123
|
+
| 3 | The trace could not be read. |
|
|
124
|
+
| 4 | The trace reference is ambiguous. |
|
|
125
|
+
|
|
126
|
+
`--junit` writes one test case per rule, and GitHub annotations print under `GITHUB_ACTIONS=true` or `--annotations`.
|
|
127
|
+
`traces check --contract <file> --explain` prints what each compiled rule checks and reads no trace.
|
|
128
|
+
For a trace that does not pass, the verdict, the contract, its plain-language statement, and the redacted spans the violations cite go to `--evidence` (default `.traces/check`).
|
|
129
|
+
The contract keys and rule semantics are in [agent-eval's trace-contract guide](https://github.com/tangle-network/agent-eval/blob/main/docs/trace-contracts.md).
|
|
130
|
+
|
|
85
131
|
## Integrate your own system
|
|
86
132
|
|
|
87
133
|
No adapter, no plugin. Emit the span contract and point `--otlp` at the file.
|
|
@@ -125,9 +171,7 @@ files are read — every other JSONL is listed in the report with what it actual
|
|
|
125
171
|
holds, rather than counted as several hundred broken spans. Put the exports in an
|
|
126
172
|
`otlp/` subdirectory and that decides it outright.
|
|
127
173
|
|
|
128
|
-
|
|
129
|
-
DEPRECATED spelling of `--otlp-out`. It still works, with a warning, and is
|
|
130
|
-
removed in 0.12.
|
|
174
|
+
Writing commands (`convert`, `export`, `evidence`, `upload`) take `--otlp-out`; they refuse `--otlp`.
|
|
131
175
|
|
|
132
176
|
`parent_span_id` is CONTAINMENT — this span happened inside that one. `links` is
|
|
133
177
|
CAUSALITY — that verdict caused this retry. Encoding causality as a parent claims
|
|
@@ -168,9 +212,9 @@ trace through `traces` cannot produce a clean bill of health.
|
|
|
168
212
|
`analyze` prints the same section, plus an **analyses skipped, and why** list —
|
|
169
213
|
and, crucially, carries that verdict INTO the report. A section whose inputs are
|
|
170
214
|
incomplete says so in its own heading and again directly above its table, so a
|
|
171
|
-
number and the reason it is wrong can never end up forty lines apart.
|
|
172
|
-
|
|
173
|
-
|
|
215
|
+
number and the reason it is wrong can never end up forty lines apart. When the
|
|
216
|
+
trace supports `tree-comparison`, the table names the command that compares its
|
|
217
|
+
arms: `traces diff <file>#branch=<a> <file>#branch=<b>`.
|
|
174
218
|
|
|
175
219
|
Two of those analyses are why the loop shape exists at all:
|
|
176
220
|
|
|
@@ -250,7 +294,10 @@ traces investigate --all --last 10 --out report.md # explicit investigation ali
|
|
|
250
294
|
traces improve --all --last 10 --dir .traces/improvement
|
|
251
295
|
traces ask --harness codex --session <id> --question "Which commands failed?"
|
|
252
296
|
traces facts --harness codex --session <id> # the deterministic facts sheet, $0
|
|
297
|
+
traces diff run-a.otlp.jsonl run-b.otlp.jsonl # where two runs of one task first diverge
|
|
298
|
+
traces mcp --otlp spans.otlp.jsonl # read-only trace tools for an MCP client
|
|
253
299
|
traces analyze --all --since 2026-06-18 --out report.md
|
|
300
|
+
traces check spans.otlp.jsonl --contract c.json --junit c.xml # exit 0 pass, 1 fail, 2 bad contract, 3 unreadable, 4 ambiguous
|
|
254
301
|
traces validate spans.otlp.jsonl # conformance; exit 1 only when it is not a trace
|
|
255
302
|
traces validate results/sessions --out conformance.md # a whole directory of exports
|
|
256
303
|
traces analyze --otlp spans.otlp.jsonl # analyse foreign OTLP, no adapter
|
|
@@ -275,6 +322,7 @@ traces stream --all --mode agent # findings + deterministic re
|
|
|
275
322
|
traces stream spans.openinference.jsonl --format openinference --no-spans
|
|
276
323
|
traces upload --since 1h --dry-run # redact + dedup + preview, no network
|
|
277
324
|
traces upload --since 24h # upload last day to the Intelligence Platform
|
|
325
|
+
traces verify-safe bundle-dir # SAFE / SAFE_WITH_WARNINGS / UNSAFE (exit 1) / UNKNOWN (exit 2)
|
|
278
326
|
traces replay-verify --steps steps.json --image <docker-image> --at 37 --cwd /home --out ./replay-out \
|
|
279
327
|
--fix-command "<corrected step>" # executed proof: replay prefix, reproduce failure, show fix
|
|
280
328
|
traces verify-findings --findings findings.json --out ./receipts \
|
|
@@ -300,7 +348,11 @@ See [Replay verification](./docs/replay-verify.md) for setup, semantics, and hon
|
|
|
300
348
|
| `--since <t>` | `upload`: window, `30m`/`2h`/`7d` or ISO (default 24h); `analyze`: ISO cutoff |
|
|
301
349
|
| `--out <path>` | Write the report to a file |
|
|
302
350
|
| `--dir <path>` | `improve`: write the full artifact pack to this directory; `ask`: write `answers.json` + `report.md` there |
|
|
303
|
-
| `--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` |
|
|
351
|
+
| `--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`, `check`, `analyze`, `investigate`, `improve`, `ask`, `stream` |
|
|
352
|
+
| `--contract <file>` | `check`: the declarative trace contract (JSON) |
|
|
353
|
+
| `--junit <file>` | `check`: write JUnit XML, one test case per rule |
|
|
354
|
+
| `--evidence <dir>` | `check`: where evidence for a trace that did not pass goes (default `.traces/check`) |
|
|
355
|
+
| `--annotations` | `check`: print GitHub workflow annotations (on by default under `GITHUB_ACTIONS=true`) |
|
|
304
356
|
| `--otlp-out <path>` | **WRITE** the OTLP artifact here (also evidence provenance / dry-run upload preview) |
|
|
305
357
|
| `--format <kind>` | File `analyze`, `export`, or `stream`: `auto`, `policy-evidence`, `sandbox-events`, `openinference`, `intelligence-spans`, or `chat-trajectory` |
|
|
306
358
|
| `--source-bundle <dir>` | `analyze` / `investigate` / `improve` / `ask`: read a retained full bundle and explicitly grant source-field reads |
|
|
@@ -536,6 +588,7 @@ traces facts --otlp spans.otlp.jsonl --out facts.json
|
|
|
536
588
|
| `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 |
|
|
537
589
|
| `unreadRecords` | records the session reader could not parse, from the session's integrity receipt |
|
|
538
590
|
| `tokenTotal` | the harness's own cumulative token total, when a span carries `traces.session.total_tokens` |
|
|
591
|
+
| `firstFailure` | the first failure among this session's own records, ranked by agent-eval's fixed precedence: the innermost ERROR span that ended first, else a span whose `agent.outcome` is `fail`, else `none` — spans were observed and none of them failed. When every span is UNSET (a capture that failed before any status was written), or a session has no records of its own at all (every span is `traces.span.subagent` — spawned work folded into this trace, with nothing this agent recorded itself), the status is `unknown`, not `none`: the record does not say whether the run failed. Failures that ended at the same instant are `ambiguous` and list the candidates instead of picking one. `blame` separates machine and provider failures from the agent's own, and is `unknown` rather than `agent` when no evidence names the agent |
|
|
539
592
|
|
|
540
593
|
Three rules hold for every field:
|
|
541
594
|
|
|
@@ -547,6 +600,43 @@ Three rules hold for every field:
|
|
|
547
600
|
|
|
548
601
|
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).
|
|
549
602
|
|
|
603
|
+
## Diff two runs
|
|
604
|
+
|
|
605
|
+
`traces diff` pairs the steps of two runs of the same task and marks where they first stop agreeing.
|
|
606
|
+
|
|
607
|
+
```bash
|
|
608
|
+
traces diff runs/a/otlp runs/b/otlp # two OTLP exports
|
|
609
|
+
traces diff a.sdk-events.jsonl b.sdk-events.jsonl --kind TOOL # any file convert reads; tool calls only
|
|
610
|
+
traces diff spans.otlp.jsonl#<trace id> other.jsonl --format json
|
|
611
|
+
traces diff run.otlp.jsonl#branch=arm-a run.otlp.jsonl#branch=arm-b # two arms of one run
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
A side is a file or directory that holds one run, `file#<trace id>` for one run of several, or `file#branch=<id>` for one arm: the spans whose `agent.branch.id` or `agent.branch.arm` is `<id>` and have no matched ancestor (a root), and every span below them. Sibling arms can share a branch id or label; when `<id>` names more than one such root subtree, the command refuses (exit 2) and lists each root's span id, rather than silently comparing the union of both.
|
|
615
|
+
Steps pair by span id first, then by position with the same name and kind, then by name and kind anywhere.
|
|
616
|
+
One inserted step therefore does not mark every later step as changed.
|
|
617
|
+
Paired steps compare status, tool and model.
|
|
618
|
+
The first divergence is `changed`, `replaced`, `only-in-a`, `only-in-b` or `reordered`, with its reason, followed by every changed and unpaired step.
|
|
619
|
+
`--kind` keeps only steps of that span kind, so event noise in a raw stream does not decide the first divergence.
|
|
620
|
+
The diff is agent-eval's `diffSteps`; this command reads the runs and prints it.
|
|
621
|
+
It exits 0 when the runs agree on outcome, 1 when they diverge, 2 when a side cannot be read (including an ambiguous `#branch=` selector), and 3 when the steps line up with no divergence but there is no recorded outcome to call agreement: neither side has a step with a definitive OK/ERROR status (both are entirely UNSET, the common shape for a capture that failed before any status was written), or neither side has a TOOL step. That case's report and text output carry a `caveat` naming which reason applies; a CI or agent gate should treat exit 3 as unknown, not as a pass.
|
|
622
|
+
|
|
623
|
+
## MCP server
|
|
624
|
+
|
|
625
|
+
`traces mcp` serves the selected traces to an MCP client over stdio.
|
|
626
|
+
|
|
627
|
+
```bash
|
|
628
|
+
claude mcp add traces -- traces mcp --otlp ./spans.otlp.jsonl
|
|
629
|
+
claude mcp add traces -- traces mcp --harness claude-code --last 5
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
It serves agent-eval's seven trace tools: `getDatasetOverview`, `queryTraces`, `countTraces`, `viewTrace`, `viewSpans`, `searchTrace` and `searchSpan`.
|
|
633
|
+
Each tool declares `readOnlyHint` and `idempotentHint` in its MCP annotations, and its description says that returned trace text is untrusted.
|
|
634
|
+
|
|
635
|
+
- The spans are redacted before the store is built — every span's name, attributes, status message and causal links' attributes, every field the store reads — and each result is redacted again at the boundary. The server then reassesses the redacted spans and refuses to start when they are still UNSAFE or UNKNOWN, the same gate `upload` applies to what it sends.
|
|
636
|
+
- Each result carries an `untrusted` notice beside it.
|
|
637
|
+
- A result above 512 KiB is refused with an error rather than truncated.
|
|
638
|
+
- `readSpanSource` is not served. It reads original source bytes in windows the caller picks, and a secret split across two windows matches no redaction rule.
|
|
639
|
+
|
|
550
640
|
## Session index
|
|
551
641
|
|
|
552
642
|
`traces index` writes one general JSON catalog over the selected sessions.
|
|
@@ -689,6 +779,8 @@ Run `traces export --help` for the full command reference.
|
|
|
689
779
|
## Upload to the Intelligence Platform
|
|
690
780
|
|
|
691
781
|
`upload` **redacts locally before anything leaves the machine**, dedups against already-uploaded sessions, and tags each with metadata (harness, cwd, git branch, host).
|
|
782
|
+
It then checks the final events of each session, exactly as they would be sent, with the share-safety verdict.
|
|
783
|
+
A session that is UNSAFE or UNKNOWN is refused: it is not sent, not written to the dry-run preview, and not marked uploaded, and the command exits 1.
|
|
692
784
|
|
|
693
785
|
```bash
|
|
694
786
|
traces upload --since 24h --dry-run # see exactly what would be sent; no network
|
|
@@ -700,16 +792,40 @@ It needs `TANGLE_INGEST_URL` (or `TANGLE_ORCHESTRATOR_URL`), `TANGLE_INGEST_API_
|
|
|
700
792
|
|
|
701
793
|
### Redaction scope: read this before uploading prose
|
|
702
794
|
|
|
703
|
-
Redaction is
|
|
795
|
+
Redaction is agent-eval's redaction core ([docs/redaction.md](https://github.com/tangle-network/agent-eval/blob/main/docs/redaction.md)), `default` profile.
|
|
796
|
+
It finds credentials by field name (`apiKey`, `x-api-key`, `client.secret`, `refreshToken`) and by value shape (provider keys, GitHub, Slack, AWS and Google tokens, JWTs, bearer headers, private keys, `KEY=secret` assignments, credentials in URLs), and replaces the whole string.
|
|
797
|
+
It replaces email, card, SSN and phone values in place.
|
|
798
|
+
Token counts such as `input_tokens` and `max_tokens` are kept.
|
|
799
|
+
It runs over every span attribute, including the captured prompt/response text.
|
|
704
800
|
|
|
705
|
-
It does **not** catch
|
|
801
|
+
It does **not** catch names, postal addresses or account numbers written in prose. Those need a context-aware model. Three postures, strongest first:
|
|
706
802
|
|
|
707
803
|
1. **`--no-content`**: upload metadata only (tool calls, tokens, timing, loop signal); no prose leaves the machine.
|
|
708
|
-
2.
|
|
709
|
-
3. Default:
|
|
804
|
+
2. **`--redactor <cmd>`**: run an ML PII scrubber (e.g. [`openai/privacy-filter`](https://github.com/openai/privacy-filter)) after the redaction core.
|
|
805
|
+
3. Default: the redaction core.
|
|
710
806
|
|
|
711
807
|
Always `--dry-run` first to see exactly what would be sent.
|
|
712
808
|
|
|
809
|
+
### Check before sharing
|
|
810
|
+
|
|
811
|
+
`verify-safe` reads files and says whether they are safe to share. It never changes them.
|
|
812
|
+
|
|
813
|
+
```bash
|
|
814
|
+
traces verify-safe bundle-dir # share profile (default)
|
|
815
|
+
traces verify-safe session.jsonl --profile strict --format json
|
|
816
|
+
traces verify-safe out/ --known-secret-env TANGLE_API_KEY # also look for this exact key, in any encoding
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
| Status | Meaning | Exit |
|
|
820
|
+
|---|---|---|
|
|
821
|
+
| `SAFE` | Nothing found | 0 |
|
|
822
|
+
| `SAFE_WITH_WARNINGS` | Only warnings: raw content, identifiers, embedded media | 0 |
|
|
823
|
+
| `UNSAFE` | A credential remains, or personal data under `share` or `strict` | 1 |
|
|
824
|
+
| `UNKNOWN` | A file, line or byte range could not be read, so its safety is unknown | 2 |
|
|
825
|
+
|
|
826
|
+
`.json` files are parsed whole and `.jsonl` or `.ndjson` files line by line. Any other file is scanned as text.
|
|
827
|
+
Each finding names the file, line and JSON Pointer, never the matched value.
|
|
828
|
+
|
|
713
829
|
## Trace analysts
|
|
714
830
|
|
|
715
831
|
Use the local checks by default.
|
|
@@ -827,7 +943,8 @@ The CLI is a thin consumer of these exports.
|
|
|
827
943
|
| `exportTraceEvidenceFile` | `(path, opts?) → { format, spans, redactionCount }` | convert compact evidence/events/OpenInference files to redacted OpenInference spans |
|
|
828
944
|
| `scanSessions` | `(ScanOptions) → AsyncIterable<ScannedSession>` | the shared locate→parse iterator |
|
|
829
945
|
| `collectSessions` | `(CollectOptions) → SessionBatch[]` | redacted per-session batches for your own pipeline |
|
|
830
|
-
| `redactSpans` | `(spans,
|
|
946
|
+
| `redactSpans` | `(spans, { profile?, knownSecrets? }) → { spans, report }` | redact spans with agent-eval's redaction core |
|
|
947
|
+
| `assessSpans` / `verifySafe` | `(spans, profile?)` / `(path, { profile?, knownSecrets? })` → verdict | share-safety verdict for spans or files |
|
|
831
948
|
| `planUpload` / `executeUpload` | `(…, { backend? }) → …` | redact + dedup + send to any sink |
|
|
832
949
|
| `selectAdapters` / `listAdapters` / `resolveAdapter` | adapter selection + the harness registry |
|
|
833
950
|
| `PiAdapter` | `new PiAdapter({ sessionsRoot? })` | parse Pi's default session tree or an exact `PI_CODING_AGENT_SESSION_DIR` |
|