@tangle-network/traces 0.9.16 → 0.9.18
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 +27 -0
- package/dist/{chunk-I4W6DECY.js → chunk-F2OEMHVZ.js} +69 -5
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/docs/trace-analysts.md +2 -1
- package/package.json +4 -2
- package/skills/build-trace-analyst/SKILL.md +60 -0
- package/skills/build-trace-analyst/agents/openai.yaml +4 -0
- package/skills/inspect-agent-traces/SKILL.md +60 -0
- package/skills/inspect-agent-traces/agents/openai.yaml +4 -0
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ It reads the transcripts your harness leaves on disk, reconstructs the run as sp
|
|
|
23
23
|
- [Policy-mining evidence](#policy-mining-evidence)
|
|
24
24
|
- [Upload to the Intelligence Platform](#upload-to-the-intelligence-platform)
|
|
25
25
|
- [Trace analysts](#trace-analysts)
|
|
26
|
+
- [Agent skills](#agent-skills)
|
|
26
27
|
- [Library (SDK)](#library-sdk)
|
|
27
28
|
- [Examples](#examples)
|
|
28
29
|
- [Develop](#develop)
|
|
@@ -373,6 +374,32 @@ See [`examples/external-engines.ts`](./examples/external-engines.ts).
|
|
|
373
374
|
|
|
374
375
|
> For the built-in agentic analysts (`--llm`), set `OPENAI_API_KEY`, or point at any OpenAI-compatible gateway with `OPENAI_BASE_URL` (e.g. an internal router) to use a non-OpenAI key.
|
|
375
376
|
|
|
377
|
+
## Agent skills
|
|
378
|
+
|
|
379
|
+
The npm package ships two AgentProfile-ready skills:
|
|
380
|
+
|
|
381
|
+
| Skill | Use |
|
|
382
|
+
|---|---|
|
|
383
|
+
| [`inspect-agent-traces`](./skills/inspect-agent-traces/SKILL.md) | select workflows and export deterministic findings |
|
|
384
|
+
| [`build-trace-analyst`](./skills/build-trace-analyst/SKILL.md) | write and calibrate a custom analyst |
|
|
385
|
+
|
|
386
|
+
Load both from GitHub:
|
|
387
|
+
|
|
388
|
+
```ts
|
|
389
|
+
import { defineGitHubResource } from '@tangle-network/agent-interface'
|
|
390
|
+
|
|
391
|
+
const ref = process.env.TRACES_SKILLS_REF
|
|
392
|
+
if (!ref) throw new Error('TRACES_SKILLS_REF must be an immutable commit')
|
|
393
|
+
|
|
394
|
+
const names = ['inspect-agent-traces', 'build-trace-analyst']
|
|
395
|
+
const skills = names.map((name) => defineGitHubResource(`skills/${name}/SKILL.md`, {
|
|
396
|
+
repository: 'tangle-network/traces',
|
|
397
|
+
ref,
|
|
398
|
+
name,
|
|
399
|
+
}))
|
|
400
|
+
const profile = { name: 'trace-reviewer', resources: { skills } }
|
|
401
|
+
```
|
|
402
|
+
|
|
376
403
|
## Release automation
|
|
377
404
|
|
|
378
405
|
Merging to `main` publishes a patch release automatically:
|
|
@@ -8975,7 +8975,58 @@ function traceCoverageFinding(pipelines, adoption) {
|
|
|
8975
8975
|
}
|
|
8976
8976
|
});
|
|
8977
8977
|
}
|
|
8978
|
-
function
|
|
8978
|
+
function sortedToolErrorSpans(spans) {
|
|
8979
|
+
return spans.filter((item) => item.attributes["openinference.span.kind"] === "TOOL" && item.status.code === "ERROR").sort((left, right) => left.start_time.localeCompare(right.start_time) || left.trace_id.localeCompare(right.trace_id) || left.span_id.localeCompare(right.span_id));
|
|
8980
|
+
}
|
|
8981
|
+
function toolErrorEvidence(item) {
|
|
8982
|
+
return evidence(
|
|
8983
|
+
"span",
|
|
8984
|
+
spanEvidenceUri(item.trace_id, item.span_id),
|
|
8985
|
+
`${String(item.attributes["tool.name"] ?? item.name)}: ${item.status.message ?? "tool span ended with ERROR"}`.slice(0, 240)
|
|
8986
|
+
);
|
|
8987
|
+
}
|
|
8988
|
+
function toolErrorFinding(pipelines, spans) {
|
|
8989
|
+
const terminalFailureTraceIds = new Set(
|
|
8990
|
+
spans.filter((item) => item.parent_span_id === null && item.status.code === "ERROR").map((item) => item.trace_id)
|
|
8991
|
+
);
|
|
8992
|
+
const nonTerminalRuns = pipelines.toolUse.filter(
|
|
8993
|
+
(run) => !terminalFailureTraceIds.has(run.runId)
|
|
8994
|
+
);
|
|
8995
|
+
const totalCalls = nonTerminalRuns.reduce((total, run) => total + run.totalCalls, 0);
|
|
8996
|
+
const errorCount = nonTerminalRuns.reduce(
|
|
8997
|
+
(total, run) => total + Object.values(run.byTool).reduce((runTotal, tool) => runTotal + tool.errors, 0),
|
|
8998
|
+
0
|
|
8999
|
+
);
|
|
9000
|
+
if (errorCount === 0) return void 0;
|
|
9001
|
+
const errorSpans = sortedToolErrorSpans(spans).filter((item) => !terminalFailureTraceIds.has(item.trace_id));
|
|
9002
|
+
if (errorSpans.length === 0) return void 0;
|
|
9003
|
+
const affectedRuns = new Set(errorSpans.map((item) => item.trace_id)).size;
|
|
9004
|
+
const errorRate = errorCount / Math.max(1, totalCalls);
|
|
9005
|
+
const citedSpans = errorSpans.slice(0, 3);
|
|
9006
|
+
return makeFinding({
|
|
9007
|
+
analyst_id: "traces-deterministic",
|
|
9008
|
+
area: "tool-use",
|
|
9009
|
+
subject: "non-terminal-tool-errors",
|
|
9010
|
+
claim: `${errorCount}/${totalCalls} tool call(s) ended in errors across ${affectedRuns} run(s) without a terminal failure`,
|
|
9011
|
+
severity: errorRate >= 0.25 ? "high" : "medium",
|
|
9012
|
+
rationale: "Recovered tool errors still consume time and tokens, and repeated failures can hide brittle retry or recovery behavior.",
|
|
9013
|
+
evidence_refs: citedSpans.map(toolErrorEvidence),
|
|
9014
|
+
recommended_action: "Inspect the most frequent failing tool and its cited inputs, then change the retry or recovery policy so a failure produces new state before another call.",
|
|
9015
|
+
validation_plan: "Rerun comparable sessions and require non-terminal tool errors to decrease without increasing terminal failures.",
|
|
9016
|
+
confidence: 1,
|
|
9017
|
+
metadata: {
|
|
9018
|
+
source: "agent-eval.computeToolUseMetrics",
|
|
9019
|
+
errorCount,
|
|
9020
|
+
totalCalls,
|
|
9021
|
+
errorRate,
|
|
9022
|
+
affectedRuns,
|
|
9023
|
+
citedErrorSpans: citedSpans.length,
|
|
9024
|
+
omittedErrorSpans: Math.max(0, errorSpans.length - citedSpans.length)
|
|
9025
|
+
},
|
|
9026
|
+
id_basis: "non-terminal-tool-errors"
|
|
9027
|
+
});
|
|
9028
|
+
}
|
|
9029
|
+
function deterministicFindings(pipelines, reactions, adoption, spans) {
|
|
8979
9030
|
const findings = [];
|
|
8980
9031
|
const coverage = traceCoverageFinding(pipelines, adoption);
|
|
8981
9032
|
if (coverage) findings.push(coverage);
|
|
@@ -8998,10 +9049,16 @@ function deterministicFindings(pipelines, reactions, adoption) {
|
|
|
8998
9049
|
metadata: { source: "traces.pipeline.stuckLoopView", loopCount: loops.length }
|
|
8999
9050
|
}));
|
|
9000
9051
|
}
|
|
9052
|
+
const toolErrors = toolErrorFinding(pipelines, spans);
|
|
9053
|
+
if (toolErrors) findings.push(toolErrors);
|
|
9001
9054
|
const failedRuns = pipelines.failureClusters.totalFailures;
|
|
9002
9055
|
if (failedRuns > 0) {
|
|
9003
9056
|
const top = pipelines.failureClusters.clusters[0];
|
|
9004
9057
|
const failureRate = failedRuns / Math.max(1, pipelines.failureClusters.totalRuns);
|
|
9058
|
+
const exampleSpans = top?.exampleRunId ? spans.filter((item) => item.trace_id === top.exampleRunId) : [];
|
|
9059
|
+
const exampleRoot = exampleSpans.find((item) => item.parent_span_id === null && item.status.code === "ERROR");
|
|
9060
|
+
const exampleToolErrors = sortedToolErrorSpans(exampleSpans);
|
|
9061
|
+
const citedToolErrors = exampleToolErrors.slice(0, 3);
|
|
9005
9062
|
findings.push(makeFinding({
|
|
9006
9063
|
analyst_id: "traces-deterministic",
|
|
9007
9064
|
area: "reliability",
|
|
@@ -9009,12 +9066,17 @@ function deterministicFindings(pipelines, reactions, adoption) {
|
|
|
9009
9066
|
severity: failureRate >= 0.1 ? "high" : "medium",
|
|
9010
9067
|
rationale: "Execution errors consume time and tokens, and sparse error telemetry can hide whether the agent recovered or repeated the same failure.",
|
|
9011
9068
|
evidence_refs: [
|
|
9069
|
+
...exampleRoot ? [evidence(
|
|
9070
|
+
"span",
|
|
9071
|
+
spanEvidenceUri(exampleRoot.trace_id, exampleRoot.span_id),
|
|
9072
|
+
`${exampleRoot.name}: ${exampleRoot.status.message ?? "root span ended with ERROR"}`.slice(0, 240)
|
|
9073
|
+
)] : [],
|
|
9074
|
+
...citedToolErrors.map(toolErrorEvidence),
|
|
9012
9075
|
evidence(
|
|
9013
9076
|
"metric",
|
|
9014
9077
|
`pipelines.failure_cluster.${top?.failureClass ?? "unknown"}`,
|
|
9015
9078
|
top ? `${top.runCount} run(s); example: ${top.exampleError ?? "error details not captured"}` : `${failedRuns} failed run(s)`
|
|
9016
|
-
)
|
|
9017
|
-
...top?.exampleRunId ? [evidence("span", `trace:${top.exampleRunId}`, top.exampleError)] : []
|
|
9079
|
+
)
|
|
9018
9080
|
],
|
|
9019
9081
|
recommended_action: top?.failureClass === "unknown" ? "Instrument the failing operation name and arguments, then start with the highest-frequency example error and verify the agent changes state before retrying." : "Fix the highest-frequency failure cluster first, then require the agent to inspect the error and change state before retrying.",
|
|
9020
9082
|
validation_plan: "Rerun a comparable trace window and require failed runs and repeated failures in the top cluster to decrease.",
|
|
@@ -9024,7 +9086,9 @@ function deterministicFindings(pipelines, reactions, adoption) {
|
|
|
9024
9086
|
failedRuns,
|
|
9025
9087
|
totalRuns: pipelines.failureClusters.totalRuns,
|
|
9026
9088
|
topFailureClass: top?.failureClass,
|
|
9027
|
-
exampleRunId: top?.exampleRunId
|
|
9089
|
+
exampleRunId: top?.exampleRunId,
|
|
9090
|
+
citedToolErrorSpans: citedToolErrors.length,
|
|
9091
|
+
omittedToolErrorSpans: Math.max(0, exampleToolErrors.length - citedToolErrors.length)
|
|
9028
9092
|
}
|
|
9029
9093
|
}));
|
|
9030
9094
|
}
|
|
@@ -9212,7 +9276,7 @@ async function runTraceInvestigation(opts) {
|
|
|
9212
9276
|
analyzeAdoption(opts.spans, { cwds: opts.cwds })
|
|
9213
9277
|
]);
|
|
9214
9278
|
opts.signal?.throwIfAborted();
|
|
9215
|
-
const deterministic = deterministicFindings(pipelines, reactions, adoption);
|
|
9279
|
+
const deterministic = deterministicFindings(pipelines, reactions, adoption, opts.spans);
|
|
9216
9280
|
const agenticRoute = opts.ai && !opts.agenticRegistry ? planTraceAgenticRoute(pipelines, reactions) : void 0;
|
|
9217
9281
|
const analysis = await analyzeSpans(opts.spans, {
|
|
9218
9282
|
ai: opts.ai,
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
package/docs/trace-analysts.md
CHANGED
|
@@ -175,10 +175,11 @@ It must also avoid inventing issues on clean trajectories.
|
|
|
175
175
|
| Dataset | Labels used |
|
|
176
176
|
| --- | --- |
|
|
177
177
|
| AgentRx | Failure category, failed step, and root-cause step |
|
|
178
|
-
| CodeTraceBench | Incorrect and unhelpful action steps
|
|
178
|
+
| CodeTraceBench | Incorrect and unhelpful action steps; solved label-empty rows are clean controls |
|
|
179
179
|
|
|
180
180
|
Use `runAnalystBenchmark` to report recall, precision, F1, root-step accuracy, citation coverage, citation validity, clean-case false positives, repeat agreement, latency, calls, tokens, and known cost.
|
|
181
181
|
Use `compareAnalystRunners` for paired baseline and candidate comparisons.
|
|
182
|
+
Failed label-empty CodeTraceBench rows are unlabeled, not clean controls.
|
|
182
183
|
|
|
183
184
|
```ts
|
|
184
185
|
import {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/traces",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.18",
|
|
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",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"types": "./dist/index.d.ts",
|
|
30
30
|
"import": "./dist/index.js"
|
|
31
31
|
},
|
|
32
|
-
"./package.json": "./package.json"
|
|
32
|
+
"./package.json": "./package.json",
|
|
33
|
+
"./skills/*": "./skills/*"
|
|
33
34
|
},
|
|
34
35
|
"bin": {
|
|
35
36
|
"traces": "dist/cli.js"
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
"dist",
|
|
39
40
|
"docs",
|
|
40
41
|
"examples",
|
|
42
|
+
"skills",
|
|
41
43
|
"install.sh",
|
|
42
44
|
"README.md"
|
|
43
45
|
],
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: build-trace-analyst
|
|
3
|
+
description: Write and calibrate a custom agent-eval trace analyst on real and labeled workflows.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Build a trace analyst
|
|
7
|
+
|
|
8
|
+
Implement one measurable finding that existing analysts do not provide.
|
|
9
|
+
|
|
10
|
+
## Check coverage
|
|
11
|
+
|
|
12
|
+
1. Run maintained analysis on one positive and one clean case.
|
|
13
|
+
2. Call `buildDefaultAnalystRegistry().list()` for batch analysts.
|
|
14
|
+
Check live analysts separately only for `traces stream`.
|
|
15
|
+
3. Search source or public docs once for the target behavior, then decide.
|
|
16
|
+
|
|
17
|
+
Extend an existing analyst when it already emits the target with usable evidence.
|
|
18
|
+
Do not inspect bundled `dist` or `node_modules`.
|
|
19
|
+
|
|
20
|
+
## Implement
|
|
21
|
+
|
|
22
|
+
- Use `Analyst`, `AnalystRegistry`, `TraceAnalysisStore`, and `makeFinding` from `@tangle-network/traces`.
|
|
23
|
+
- Start with `getOverview`; narrow with `queryTraces`, `viewSpans`, or `searchTrace`.
|
|
24
|
+
- Pass `context.signal` through reads and model calls.
|
|
25
|
+
- Return stable `analyst_id`, `area`, `subject`, and `id_basis` values.
|
|
26
|
+
- Cite exact spans as `trace://<trace-id>/span/<span-id>`.
|
|
27
|
+
- Return no finding when evidence is absent.
|
|
28
|
+
- Use `cost.kind: 'deterministic'` only without model calls.
|
|
29
|
+
- For `cost.kind: 'llm'`, use the injected chat client, budget, cancellation, and usage recording.
|
|
30
|
+
- Extend `buildDefaultAnalystRegistry()` unless full replacement is intentional.
|
|
31
|
+
|
|
32
|
+
Use the [custom analyst](../../examples/custom-analyst.ts) and [config](../../examples/improvement-config.mjs) examples rather than adding a wrapper.
|
|
33
|
+
|
|
34
|
+
## Run
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pnpm tsx examples/custom-analyst.ts --fixture
|
|
38
|
+
pnpm tsx examples/custom-analyst.ts --good-fixture
|
|
39
|
+
traces improve --harness codex --current --latest-turn --workflow \
|
|
40
|
+
--config traces.config.mjs --dir .traces/custom-analyst
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Inspect every finding and cited span.
|
|
44
|
+
|
|
45
|
+
## Calibrate
|
|
46
|
+
|
|
47
|
+
Use `runAnalystBenchmark` with `registryBenchmarkRunner` from `@tangle-network/agent-eval/analyst`.
|
|
48
|
+
Use AgentRx or CodeTraceBench labels when they match the finding.
|
|
49
|
+
Compare the candidate with a no-findings baseline and relevant maintained or external analysts on identical cases and limits.
|
|
50
|
+
|
|
51
|
+
Use at least 20 independent cases before comparative statistics.
|
|
52
|
+
Retain every case row and report exact-step recall, precision, F1, root-step accuracy when labeled, citation coverage and validity, clean-case false positives, repeat agreement, failures, latency, calls, all token fields, and known or missing cost.
|
|
53
|
+
|
|
54
|
+
Do not claim quality from fixtures alone.
|
|
55
|
+
Require a fresh task comparison before using findings to change an agent or knowledge base.
|
|
56
|
+
|
|
57
|
+
## Then consider
|
|
58
|
+
|
|
59
|
+
- Use `inspect-agent-traces` to investigate a miss or false positive on its original workflow.
|
|
60
|
+
- Use `calibrate-before-measure` before trusting a new label set or scoring rule.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: inspect-agent-traces
|
|
3
|
+
description: Inspect real agent workflows with the published Traces CLI and export cited local findings.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Inspect agent traces
|
|
7
|
+
|
|
8
|
+
Use the deterministic CLI first.
|
|
9
|
+
Keep inspection local and read-only.
|
|
10
|
+
|
|
11
|
+
## Select the workflow
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
traces --version
|
|
15
|
+
traces list --harness codex --cwd "$PWD"
|
|
16
|
+
traces analyze --harness codex --current --latest-turn --workflow \
|
|
17
|
+
--out .traces/current.md
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- Use `--current` for the active Codex session.
|
|
21
|
+
- Use `--session <id-or-path>` to pin a listed session.
|
|
22
|
+
- Use `--latest-turn` for the current task in a resumed Codex or Claude Code session.
|
|
23
|
+
- Use `--workflow` to include workers linked by stable parent and child IDs.
|
|
24
|
+
- Use `--max-workflow-sessions <n>` only when the default 100-file bound is insufficient.
|
|
25
|
+
- For Claude Code, use `--harness claude-code --session <path> --latest-turn`; nested subagents are included.
|
|
26
|
+
|
|
27
|
+
Never join agents by display name or timestamp when Traces reports missing or conflicting IDs.
|
|
28
|
+
|
|
29
|
+
## Export
|
|
30
|
+
|
|
31
|
+
Write normalized OpenInference spans for another tool:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
traces convert --harness codex --current --latest-turn --workflow \
|
|
35
|
+
--otlp .traces/current.otlp.jsonl
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Write the complete deterministic review packet:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
traces improve --harness codex --current --latest-turn --workflow \
|
|
42
|
+
--dir .traces/improvement
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`improve` writes findings, evidence, a report, and spans.
|
|
46
|
+
It does not edit an agent, repository, memory store, or knowledge base.
|
|
47
|
+
|
|
48
|
+
## Report
|
|
49
|
+
|
|
50
|
+
- State the harness, selected task boundary, session and span counts, and integrity warnings.
|
|
51
|
+
- Cite each finding with its exact `trace://` reference.
|
|
52
|
+
- Mark missing outcome, cost, token, skill, or relationship data as unknown.
|
|
53
|
+
- Do not infer task success from a completion message.
|
|
54
|
+
- Do not infer skill use from reading a `SKILL.md`.
|
|
55
|
+
- Do not upload traces unless explicitly requested.
|
|
56
|
+
|
|
57
|
+
## Then consider
|
|
58
|
+
|
|
59
|
+
- Use `build-trace-analyst` when deterministic findings cannot answer a repeated trace question.
|
|
60
|
+
- Use `autopsy` when the result is empty, surprising, or suspiciously strong.
|