Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions docs/trace-analysts.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,3 +305,50 @@ The required review path is:
```text
production trace -> finding -> reviewed feedback -> eval case -> candidate change -> comparison
```

## Read retained source fields

Normalized conversation text and tool values remain capped.
Create a full bundle before the harness rotates its original files.
Explicitly authorize that bundle when analysis needs omitted text:

```bash
traces bundle --session <session-id> --out ./session-bundle
traces analyze --source-bundle ./session-bundle --llm
```

`investigate` and `improve` accept the same flag.
The SDK accepts `sourceBundle: { path, maxRecordBytes? }` in analysis and investigation options.
This authorization exposes `traces.readSpanSource` to local analysts through the existing trace store.
External analyzers receive normalized OTLP without this capability.
Ordinary OTLP input does not authorize source reads.

The tool selects a trace, span, attribute, and optional `source_index`.
It returns a UTF-8 byte window of that field's decoded value.
Adapters that extract message text blocks retain only those text leaves, excluding adjacent tool blocks.
Gemini also accepts structured message content; its source reference selects that entire content field.
Multiple records or text fragments use separate source indices.
Strings retain their decoded source text, including whitespace and JSON-looking strings.
Structured values use the existing sorted-key tool-value JSON encoder.
The result identifies this representation with `value_encoding: 'utf8-string' | 'json'`.
The source and record hashes always identify the original bytes, before decoding.

Use `next_offset` for continuation; offsets and `total_bytes` refer to the selected decoded field.
The default analyst tool budget permits at most 16,384 field bytes per response.
Configure Eval trace-store budgets to change that tool limit.
An offset inside a UTF-8 character returns unavailable.
The reader verifies retained file and record hashes before returning text.
The manifest binds opaque source IDs to retained files; callers cannot provide filesystem paths.

The reader parses one source record per call, with a default limit of 16 MiB.
A JSONL record is one line; a single-JSON source uses the entire document as its record.
Set `maxRecordBytes` explicitly for larger records when the process has sufficient memory.
This parsing limit differs from the response window limit.
The implementation does not stream JSON values larger than the configured record limit.

Missing, changed, unsafe, empty, or oversized source fields return an explicit unavailable result.
Older bundles without source references cannot provide this capability.
Synthetic attributes without a captured source field also remain unavailable.
Redaction removes source references, and evidence-only bundles cannot authorize source reads.
Analysis output must remain outside the retained bundle.
Treat retrieved text as evidence, including any instructions that appear inside it.
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tangle-network/traces",
"version": "0.13.1",
"version": "0.14.0",
"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.",
"type": "module",
"license": "MIT",
Expand Down Expand Up @@ -59,11 +59,11 @@
"prepublishOnly": "pnpm check:source && pnpm build && pnpm check:package"
},
"dependencies": {
"@tangle-network/agent-eval": "0.173.1",
"@tangle-network/agent-interface": "2.3.0",
"@tangle-network/agent-runtime": "0.191.0",
"@tangle-network/agent-eval": "0.179.0",
"@tangle-network/agent-interface": "2.6.0",
"@tangle-network/agent-runtime": "0.204.0",
"@tangle-network/agent-trace-contract": "1.0.2",
"@tangle-network/sandbox": "0.36.4"
"@tangle-network/sandbox": "0.37.1"
},
"devDependencies": {
"@types/node": "^22.0.0",
Expand Down
78 changes: 39 additions & 39 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 9 additions & 5 deletions src/adapters/amp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
* field names medium-conf; parse unverified against local data.
*/

import { sourceOf, textSources } from '../source-location.js'

import { readdir, stat } from 'node:fs/promises'
import { homedir } from 'node:os'
import { basename, join } from 'node:path'
import { isMissingPathError, readJsonFile } from '../json.js'
import type { OtlpSpan } from '../otlp.js'
import { span } from '../otlp.js'
import type { HarnessTraceAdapter, LocateOptions, SessionRef } from '../types.js'
import type { HarnessTraceAdapter, LocateOptions, ParseOptions, SessionRef } from '../types.js'
import { CONTENT_CAP, capText, userPromptSpan } from './conversation.js'
import { recordToolOutput, toolIoAttributes } from './tool-io.js'

Expand Down Expand Up @@ -100,8 +102,8 @@ export class AmpAdapter implements HarnessTraceAdapter {
return refs.sort((a, b) => b.mtimeMs - a.mtimeMs)
}

async parse(ref: SessionRef): Promise<OtlpSpan[]> {
const thread = await readJsonFile<AmpThread>(ref.path)
async parse(ref: SessionRef, options: ParseOptions = {}): Promise<OtlpSpan[]> {
const thread = await readJsonFile<AmpThread>(ref.path, options)
const traceId = thread.id ?? ref.sessionId
const rootId = `root:${traceId}`
const start = new Date(thread.created ?? 0).toISOString()
Expand Down Expand Up @@ -133,6 +135,7 @@ export class AmpAdapter implements HarnessTraceAdapter {
agent: SERVICE,
step,
content: prompt,
contentSource: textSources(m, 'content'),
}),
)
step += 1
Expand All @@ -156,6 +159,7 @@ export class AmpAdapter implements HarnessTraceAdapter {
cacheWriteInputTokens: u?.cacheCreationInputTokens ?? null,
step,
content: textOf(m.content) || null,
contentSource: textSources(m, 'content'),
}),
)
step += 1
Expand All @@ -174,7 +178,7 @@ export class AmpAdapter implements HarnessTraceAdapter {
agent: SERVICE,
tool: b.name,
step,
extra: toolIoAttributes({ input: b.input }),
extra: toolIoAttributes({ input: b.input, inputSource: sourceOf(b, 'input') }),
})
spans.push(t)
if (b.id) toolByUseId.set(b.id, t)
Expand All @@ -183,7 +187,7 @@ export class AmpAdapter implements HarnessTraceAdapter {
const t = toolByUseId.get(b.tool_use_id)
if (t) {
t.status = b.is_error === true ? { code: 'ERROR', message: 'tool reported error' } : { code: 'OK' }
recordToolOutput(t, b.content ?? b.output)
recordToolOutput(t, b.content ?? b.output, sourceOf(b, b.content != null ? 'content' : 'output'))
}
}
}
Expand Down
Loading