Coding agents
Content capture, status and doctor
What --capture full turns on per vendor, how subagent hooks work for Claude Code, and what evalkit coding status, doctor and uninstall do.
After this page you can decide how much text leaves the developer’s machine, confirm an install end to end without opening the app, and remove an install cleanly.
--capture minimal and full
The default, minimal, sends tool calls, timings, tokens and cost but not the prompt, the reply or tool text. --capture full turns the vendor’s content export on:
| Vendor | What `--capture full` adds |
|---|---|
claude-code | OTEL_LOG_USER_PROMPTS, OTEL_LOG_ASSISTANT_RESPONSES, OTEL_LOG_TOOL_DETAILS, OTEL_LOG_TOOL_CONTENT and OTEL_LOG_RAW_API_BODIES, plus CLAUDE_CODE_OTEL_CONTENT_MAX_LENGTH=524288. Claude Code cuts each body at 60 KB by default, which is smaller than its own system prompt, so the installer raises the cap to 512 K characters. A body cut short is marked gen_ai.request.body_truncated: true with its length. |
codex | log_user_prompt = true |
gemini-cli | telemetry.logPrompts: true (the installer sets it false otherwise) |
cursor, windsurf, opencode | the hook or plugin includes prompt, completion, tool.arguments, tool.result and user.id in the spans it posts; without it the turn root is named turn |
The receiver marks an llm_call that arrived without prompt and completion, and a tool_call without arguments and result, with evalkit.content_captured=false, and removes the marker when the content arrives later in the batch or through an enrichment record. The trace page prints “not captured” in those fields rather than leaving them empty.
Subagents in Claude Code
In span mode the nesting is real: a subagent’s calls already have the Agent tool’s execution span as their parent, so the receiver turns that span into the subagent’s agent span. Unless --no-subagents, the installer also merges two hooks into ~/.claude/settings.json, both running evalkit coding hook --vendor claude-code with a 5-second timeout that always exits 0 and never blocks the agent: PostToolUse posts a claude_code.tool_output record (tool name, duration, subagent id and type; the output itself only with --capture full) — the only way non-shell tool output reaches the platform — and SubagentStop posts claude_code.subagent_stop with the subagent’s last message under --capture full. With --no-traces (log events only) subagents appear as subagent:<type> bands built from Claude Code’s own subagent_completed events instead of nested groups.
status
evalkit coding status prints the endpoint, the masked key, the capture level, the last doctor result and, per vendor, the scope, capture level, whether traces are on and which hooks are installed. It reads the local config and state files; it does not ask the server.
doctor
evalkit coding doctor posts one synthetic turn — an agent root with a prompt, one llm_call child with tokens and cost, one tool_call child — plus one enrichment record carrying the completion, then reads the trace back with the same key and asserts the merged tree came back (at least 3 spans, prompt present), retrying four times over about two seconds. The synthetic trace is tagged agent.vendor = evalkit-doctor and evalkit.doctor = true; it is excluded from the session, user and agent-version rollups and stays readable by trace id. The result is stored in state.json and shown by status as last doctor.
--tenant is optional. Without it, doctor reads /v1/traces/me/<traceId>, and a current receiver resolves me to the tenant the key belongs to. Pass --tenant <id> (remembered in config.env) only against a receiver older than that alias: it answers the me read with 403 and doctor reports read back forbidden at <url>: this receiver predates the 'me' alias; pass --tenant <your tenant id> or update the receiver. A --tenant the key does not belong to reports the key belongs to a different tenant than '<tenant>'. The URL in the message is the one that was read; the → exporter <endpoint> after it is the ingest endpoint.
uninstall
evalkit coding uninstall --vendor <vendor> [--purge] [--dry-run] removes only what state.json says was written — including files an earlier install wrote under the other --scope — and restores any value it replaced, even one whose value happened to match ours. A reinstall with different flags removes the keys and hook entries the previous install owned and no longer needs. Vendor configuration the installer did not touch is left exactly as it was.