braintrust-tracing by parcadei/continuous-claude-v3
npx skills add https://github.com/parcadei/continuous-claude-v3 --skill braintrust-tracingClaude Code 会话在 Braintrust 中追踪的完整指南,包括子代理关联。
PARENT SESSION
+---------------------+
| SessionStart |
| (creates root) |
+----------+----------+
|
+----------v----------+
| UserPromptSubmit |
| (creates Turn) |
+----------+----------+
|
+--------------------+--------------------+
| | |
+---------v--------+ +--------v--------+ +--------v--------+
| PostToolUse | | PostToolUse | | PreToolUse |
| (Read span) | | (Edit span) | | (Task - inject) |
+------------------+ +-----------------+ +--------+--------+
|
+----------v----------+
| SUB-AGENT |
| SessionStart |
| (NEW root_span_id)|
+----------+----------+
|
+----------v----------+
| SubagentStop |
| (has session_id) |
+---------------------+
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 钩子 | 触发时机 | 创建内容 | 关键字段 |
|---|---|---|---|
| SessionStart | 会话开始 | 根跨度 | session_id, root_span_id |
| UserPromptSubmit | 用户发送提示 | 轮次跨度 | prompt, turn_number |
| PreToolUse | 工具运行前 | (修改任务提示) | tool_input.prompt |
| PostToolUse | 工具运行后 | 工具跨度 | tool_name, input, output |
| Stop | 轮次完成 | LLM 跨度 | model, tokens, tool_calls |
| SubagentStop | 子代理完成 | (无跨度) | 子代理的 session_id |
| SessionEnd | 会话结束 | (完成根跨度) | turn_count, tool_count |
Session (task span) - root_span_id = session_id
|
+-- Turn 1 (task span)
| |
| +-- claude-sonnet (llm span) - model call with tool_use
| +-- Read (tool span)
| +-- Edit (tool span)
| +-- claude-sonnet (llm span) - response after tools
|
+-- Turn 2 (task span)
| |
| +-- claude-sonnet (llm span)
| +-- Task (tool span) -----> [Sub-agent session - SEPARATE trace]
| +-- claude-sonnet (llm span)
|
+-- Turn 3 ...
SessionStart 无法接收到任务提示。
我们尝试通过 PreToolUse 将追踪上下文注入到任务提示中:
# PreToolUse hook injects:
[BRAINTRUST_TRACE_CONTEXT]
{"root_span_id": "abc", "parent_span_id": "xyz", "project_id": "123"}
[/BRAINTRUST_TRACE_CONTEXT]
但 SessionStart 只接收会话元数据,而非修改后的提示。注入的上下文丢失了。
父会话中的任务跨度包含所有信息:
agentId - 子代理运行的标识符totalTokens, totalToolUseCount - 指标content - 完整的代理响应/摘要tool_input.prompt - 原始任务提示tool_input.subagent_type - 代理类型(例如,"oracle")SubagentStop 钩子接收子代理的 session_id:
root_span_id当前状态: 子代理创建孤立的追踪(新的 root_span_id)。
关联方法:
agentId 或时间与孤立追踪进行匹配session_id = 其追踪的 root_span_id未来解决方案(尚未实现):
SubagentStop fires -> writes session_id to temp file
PostToolUse (Task) -> reads temp file -> adds child_session_id to Task span metadata
这将链接:Task.agentId + Task.child_session_id -> 孤立追踪的 root_span_id
~/.claude/state/braintrust_sessions/
{session_id}.json # Per-session state
每个会话文件包含:
{
"root_span_id": "abc-123",
"project_id": "proj-456",
"turn_count": 5,
"tool_count": 23,
"current_turn_span_id": "turn-789",
"current_turn_start": 1703456789,
"started": "2025-12-24T10:00:00.000Z",
"is_subagent": false
}
~/.claude/state/braintrust_global.json # Cached project_id
~/.claude/state/braintrust_hook.log # Debug log
# View hook logs in real-time
tail -f ~/.claude/state/braintrust_hook.log
# Check if session has state
cat ~/.claude/state/braintrust_sessions/*.json | jq -s '.'
# Verify environment
echo "TRACE_TO_BRAINTRUST=$TRACE_TO_BRAINTRUST"
echo "BRAINTRUST_API_KEY=${BRAINTRUST_API_KEY:+set}"
# List recent sessions
uv run python -m runtime.harness scripts/braintrust_analyze.py --sessions 5
# Analyze last session
uv run python -m runtime.harness scripts/braintrust_analyze.py --last-session
# Replay specific session
uv run python -m runtime.harness scripts/braintrust_analyze.py --replay <session-id>
# Find sub-agent traces (orphaned roots)
uv run python -m runtime.harness scripts/braintrust_analyze.py --agent-stats
# Enable verbose logging
export BRAINTRUST_CC_DEBUG=true
# Test hooks manually
echo '{"session_id":"test-123","type":"resume"}' | \
bash "$CLAUDE_PROJECT_DIR/.claude/plugins/braintrust-tracing/hooks/session_start.sh"
# Test PreToolUse (Task injection)
echo '{"session_id":"test-123","tool_name":"Task","tool_input":{"prompt":"test"}}' | \
bash "$CLAUDE_PROJECT_DIR/.claude/plugins/braintrust-tracing/hooks/pre_tool_use.sh"
无追踪出现:
.claude/settings.local.json 中的 TRACE_TO_BRAINTRUST=trueecho $BRAINTRUST_API_KEYtail -20 ~/.claude/state/braintrust_hook.log子代理未链接:
--agent-stats 查找代理活动agentId 进行关联缺少跨度:
current_turn_span_id状态损坏:
rm ~/.claude/state/braintrust_sessions/*.jsonrm ~/.claude/state/braintrust_global.json| 文件 | 用途 |
|---|---|
.claude/plugins/braintrust-tracing/hooks/common.sh | 共享工具、API、状态管理 |
.claude/plugins/braintrust-tracing/hooks/session_start.sh | 创建根跨度,处理子代理上下文 |
.claude/plugins/braintrust-tracing/hooks/user_prompt_submit.sh | 为每条用户消息创建轮次跨度 |
.claude/plugins/braintrust-tracing/hooks/pre_tool_use.sh | 将追踪上下文注入到任务提示中 |
.claude/plugins/braintrust-tracing/hooks/post_tool_use.sh | 创建工具跨度,捕获代理/技能元数据 |
.claude/plugins/braintrust-tracing/hooks/stop_hook.sh | 创建 LLM 跨度,完成轮次 |
.claude/plugins/braintrust-tracing/hooks/session_end.sh | 完成会话,触发学习提取 |
scripts/braintrust_analyze.py | 查询和分析追踪的会话 |
~/.claude/state/braintrust_sessions/ | 每会话状态文件 |
~/.claude/state/braintrust_hook.log | 调试日志 |
| 变量 | 必需 | 默认值 | 描述 |
|---|---|---|---|
TRACE_TO_BRAINTRUST | 是 | - | 设置为 "true" 以启用 |
BRAINTRUST_API_KEY | 是 | - | Braintrust 的 API 密钥 |
BRAINTRUST_CC_PROJECT | 否 | claude-code | 项目名称 |
BRAINTRUST_CC_DEBUG | 否 | false | 详细日志记录 |
BRAINTRUST_API_URL | 否 | https://api.braintrust.dev | API 端点 |
尝试: 通过 PreToolUse 将追踪上下文注入到任务提示中。
结果: 失败 - SessionStart 只接收会话元数据,而非提示。
发现: 任务跨度已包含丰富的子代理数据:
metadata.agent_type - 来自 subagent_type 的代理类型metadata.skill_name - 来自 Skill 工具的技能tool_input - 发送给代理的完整提示tool_output - 代理响应当前关联路径:
agentId 和时间root_span_id = session_idsession_idsession_id 链接未来工作: 在 SubagentStop 之后,从 PostToolUse 将 child_session_id 写入任务跨度元数据。
agentId 的任务跨度,子代理具有独立的 session_id1. 通过 PreToolUse 注入提示
SessionStart 钩子只接收会话元数据(session_id、type、cwd),而非提示。注入的追踪上下文永远不会被看到。
钩子接收:
{
"session_id": "...",
"type": "start|resume|compact|clear",
"cwd": "...",
"env": {...}
}
不存在提示字段 - 在 SessionStart 时无法进行上下文注入。
2. SubagentStop → PostToolUse 文件交接
竞态条件。这些是独立的异步钩子,没有时间保证:
3. PreToolUse 关联文件
SessionStart 无法访问 task_span_id,因为它没有关于哪个 Task 生成它的上下文。PreToolUse 修改提示但不创建 SessionStart 可以可靠访问的状态文件。
用于数据集构建的事后匹配:
父会话任务跨度包含:
agentId - 子代理运行的标识符totalTokens, totalToolUseCount - 聚合指标content - 完整的代理响应/摘要tool_input.prompt - 原始任务提示tool_input.subagent_type - 代理类型(例如,"oracle")子代理会话包含:
session_id(等于孤立追踪的 root_span_id)关联策略:
root_span_id)subagent_typesession_id(可以捕获并记录)SessionStart 输入是故意最小化的 - 它不包含提示或工具上下文:
interface SessionStartInput {
session_id: string;
type: "start" | "resume" | "compact" | "clear";
cwd: string;
env: { [key: string]: string };
// NO: prompt, tool_context, task_span_id, parent_span_id
}
这种设计边界阻止了在钩子时间进行实时关联。
对于构建具有子代理关联的代理运行数据集:
session_id脚本模式示例:
# 1. Export parent session
braintrust_analyze.py --replay <parent-session-id> > parent_traces.json
# 2. Query for orphaned sub-agent traces (those created during parent's time window)
braintrust_analyze.py --agent-stats > all_agent_traces.json
# 3. Correlate in Python:
# - Parent Task spans -> agentId, timestamps, subagent_type
# - Orphaned traces -> root_span_id, timestamps
# - Match by timing and type
这种方法可靠、可测试,并且不需要钩子维护隐式状态。
每周安装
201
仓库
GitHub 星标
3.6K
首次出现
2026年1月22日
安全审计
安装于
opencode196
codex194
gemini-cli193
cursor191
github-copilot188
amp184
Comprehensive guide to tracing Claude Code sessions in Braintrust, including sub-agent correlation.
PARENT SESSION
+---------------------+
| SessionStart |
| (creates root) |
+----------+----------+
|
+----------v----------+
| UserPromptSubmit |
| (creates Turn) |
+----------+----------+
|
+--------------------+--------------------+
| | |
+---------v--------+ +--------v--------+ +--------v--------+
| PostToolUse | | PostToolUse | | PreToolUse |
| (Read span) | | (Edit span) | | (Task - inject) |
+------------------+ +-----------------+ +--------+--------+
|
+----------v----------+
| SUB-AGENT |
| SessionStart |
| (NEW root_span_id)|
+----------+----------+
|
+----------v----------+
| SubagentStop |
| (has session_id) |
+---------------------+
| Hook | Trigger | Creates | Key Fields |
|---|---|---|---|
| SessionStart | Session begins | Root span | session_id, root_span_id |
| UserPromptSubmit | User sends prompt | Turn span | prompt, turn_number |
| PreToolUse | Before tool runs | (modifies Task prompts) | tool_input.prompt |
Session (task span) - root_span_id = session_id
|
+-- Turn 1 (task span)
| |
| +-- claude-sonnet (llm span) - model call with tool_use
| +-- Read (tool span)
| +-- Edit (tool span)
| +-- claude-sonnet (llm span) - response after tools
|
+-- Turn 2 (task span)
| |
| +-- claude-sonnet (llm span)
| +-- Task (tool span) -----> [Sub-agent session - SEPARATE trace]
| +-- claude-sonnet (llm span)
|
+-- Turn 3 ...
SessionStart doesn't receive the Task prompt.
We tried injecting trace context into Task prompts via PreToolUse:
# PreToolUse hook injects:
[BRAINTRUST_TRACE_CONTEXT]
{"root_span_id": "abc", "parent_span_id": "xyz", "project_id": "123"}
[/BRAINTRUST_TRACE_CONTEXT]
But SessionStart only receives session metadata, not the modified prompt. The injected context is lost.
Task spans in parent session contain everything:
agentId - identifier for the sub-agent runtotalTokens, totalToolUseCount - metricscontent - full agent response/summarytool_input.prompt - original task prompttool_input.subagent_type - agent type (e.g., "oracle")SubagentStop hook receives the sub-agent'ssession_id:
root_span_idCurrent state: Sub-agents create orphaned traces (new root_span_id).
Correlation method:
agentId or timing with orphaned tracessession_id = its trace's root_span_idFuture solution (not yet implemented):
SubagentStop fires -> writes session_id to temp file
PostToolUse (Task) -> reads temp file -> adds child_session_id to Task span metadata
This would link: Task.agentId + Task.child_session_id -> orphaned trace root_span_id
~/.claude/state/braintrust_sessions/
{session_id}.json # Per-session state
Each session file contains:
{
"root_span_id": "abc-123",
"project_id": "proj-456",
"turn_count": 5,
"tool_count": 23,
"current_turn_span_id": "turn-789",
"current_turn_start": 1703456789,
"started": "2025-12-24T10:00:00.000Z",
"is_subagent": false
}
~/.claude/state/braintrust_global.json # Cached project_id
~/.claude/state/braintrust_hook.log # Debug log
# View hook logs in real-time
tail -f ~/.claude/state/braintrust_hook.log
# Check if session has state
cat ~/.claude/state/braintrust_sessions/*.json | jq -s '.'
# Verify environment
echo "TRACE_TO_BRAINTRUST=$TRACE_TO_BRAINTRUST"
echo "BRAINTRUST_API_KEY=${BRAINTRUST_API_KEY:+set}"
# List recent sessions
uv run python -m runtime.harness scripts/braintrust_analyze.py --sessions 5
# Analyze last session
uv run python -m runtime.harness scripts/braintrust_analyze.py --last-session
# Replay specific session
uv run python -m runtime.harness scripts/braintrust_analyze.py --replay <session-id>
# Find sub-agent traces (orphaned roots)
uv run python -m runtime.harness scripts/braintrust_analyze.py --agent-stats
# Enable verbose logging
export BRAINTRUST_CC_DEBUG=true
# Test hooks manually
echo '{"session_id":"test-123","type":"resume"}' | \
bash "$CLAUDE_PROJECT_DIR/.claude/plugins/braintrust-tracing/hooks/session_start.sh"
# Test PreToolUse (Task injection)
echo '{"session_id":"test-123","tool_name":"Task","tool_input":{"prompt":"test"}}' | \
bash "$CLAUDE_PROJECT_DIR/.claude/plugins/braintrust-tracing/hooks/pre_tool_use.sh"
No traces appearing:
TRACE_TO_BRAINTRUST=true in .claude/settings.local.jsonecho $BRAINTRUST_API_KEYtail -20 ~/.claude/state/braintrust_hook.logSub-agents not linking:
--agent-stats to find agent activityagentId in parent Task spanMissing spans:
current_turn_span_id in session state| File | Purpose |
|---|---|
.claude/plugins/braintrust-tracing/hooks/common.sh | Shared utilities, API, state management |
.claude/plugins/braintrust-tracing/hooks/session_start.sh | Creates root span, handles sub-agent context |
.claude/plugins/braintrust-tracing/hooks/user_prompt_submit.sh | Creates Turn spans per user message |
.claude/plugins/braintrust-tracing/hooks/pre_tool_use.sh | Injects trace context into Task prompts |
.claude/plugins/braintrust-tracing/hooks/post_tool_use.sh | Creates tool spans, captures agent/skill metadata |
| Variable | Required | Default | Description |
|---|---|---|---|
TRACE_TO_BRAINTRUST | Yes | - | Set to "true" to enable |
BRAINTRUST_API_KEY | Yes | - | API key for Braintrust |
BRAINTRUST_CC_PROJECT | No | claude-code | Project name |
BRAINTRUST_CC_DEBUG |
Attempted: Inject trace context via PreToolUse into Task prompts.
Result: Failed - SessionStart only receives session metadata, not the prompt.
Discovery: Task spans already contain rich sub-agent data:
metadata.agent_type - agent type from subagent_typemetadata.skill_name - skill from Skill tooltool_input - full prompt sent to agenttool_output - agent responseCurrent correlation path:
agentId and timingroot_span_id = session_idsession_idsession_id linkFuture work: Write child_session_id to Task span metadata from PostToolUse after SubagentStop.
agentId, sub-agent has separate session_id1. Prompt injection via PreToolUse
SessionStart hook only receives session metadata (session_id, type, cwd), NOT the prompt. Injected trace context is never seen.
The hook receives:
{
"session_id": "...",
"type": "start|resume|compact|clear",
"cwd": "...",
"env": {...}
}
No prompt field exists - context injection is impossible at SessionStart.
2. SubagentStop → PostToolUse file handoff
Race condition. These are independent async hooks with no timing guarantees:
3. PreToolUse correlation files
SessionStart can't access the task_span_id because it has no context about which Task spawned it. PreToolUse modifies prompts but doesn't create a reliably accessible state file that SessionStart can find.
Post-hoc matching for dataset building:
Parent session Task spans contain:
agentId - identifier for the sub-agent runtotalTokens, totalToolUseCount - aggregated metricscontent - full agent response/summarytool_input.prompt - original task prompttool_input.subagent_type - agent type (e.g., "oracle")Sub-agent sessions contain:
session_id (equals orphaned trace root_span_id)Correlation strategy:
root_span_id)subagent_type from Task promptsession_id (can be captured and logged)SessionStart input is intentionally minimal - it contains no prompt or tool context:
interface SessionStartInput {
session_id: string;
type: "start" | "resume" | "compact" | "clear";
cwd: string;
env: { [key: string]: string };
// NO: prompt, tool_context, task_span_id, parent_span_id
}
This design boundary prevents real-time correlation at hook time.
For building agent run datasets with sub-agent correlation:
session_id in logs or stateExample script pattern:
# 1. Export parent session
braintrust_analyze.py --replay <parent-session-id> > parent_traces.json
# 2. Query for orphaned sub-agent traces (those created during parent's time window)
braintrust_analyze.py --agent-stats > all_agent_traces.json
# 3. Correlate in Python:
# - Parent Task spans -> agentId, timestamps, subagent_type
# - Orphaned traces -> root_span_id, timestamps
# - Match by timing and type
This approach is reliable, testable, and doesn't require hooks to maintain implicit state.
Weekly Installs
201
Repository
GitHub Stars
3.6K
First Seen
Jan 22, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
opencode196
codex194
gemini-cli193
cursor191
github-copilot188
amp184
| PostToolUse | After tool runs | Tool span | tool_name, input, output |
| Stop | Turn completes | LLM spans | model, tokens, tool_calls |
| SubagentStop | Sub-agent finishes | (no span) | session_id of sub-agent |
| SessionEnd | Session ends | (finalizes root) | turn_count, tool_count |
State corruption:
rm ~/.claude/state/braintrust_sessions/*.jsonrm ~/.claude/state/braintrust_global.json.claude/plugins/braintrust-tracing/hooks/stop_hook.sh | Creates LLM spans, finalizes Turns |
.claude/plugins/braintrust-tracing/hooks/session_end.sh | Finalizes session, triggers learning extraction |
scripts/braintrust_analyze.py | Query and analyze traced sessions |
~/.claude/state/braintrust_sessions/ | Per-session state files |
~/.claude/state/braintrust_hook.log | Debug log |
| No |
false |
| Verbose logging |
BRAINTRUST_API_URL | No | https://api.braintrust.dev | API endpoint |