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.
--capture decides how much text leaves the developer's machine. evalkit coding status and doctor check an install without opening the app, and uninstall removes it cleanly.
Minimal and full capture
The default, minimal, sends tool calls, timings, tokens and cost, but not the prompt, the reply or tool text. --capture full turns on the vendor's content export:
| 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 a larger body cap (below) |
codex | log_user_prompt = true, and with the Python CLI the Stop and SessionEnd hooks that send the reply, which Codex never exports |
gemini-cli | telemetry.logPrompts: true (otherwise the installer sets it to false) |
cursor, windsurf, opencode | prompt, completion, tool.arguments, tool.result and user.id in the spans the hook or plugin posts. Without it the turn root is named turn. |
Claude Code cuts each body at 60 KB by default, which is smaller than its own system prompt. The installer raises the cap to 512 K characters with CLAUDE_CODE_OTEL_CONTENT_MAX_LENGTH=524288. A body cut short is marked gen_ai.request.body_truncated: true, with its length.
The receiver marks an llm_call that arrived without prompt and completion, or a tool_call without arguments and result, with evalkit.content_captured=false. It removes the marker when the content arrives later in the batch or in an enrichment record. On a marked span, the trace page says what did not arrive, such as "No request text arrived for this call.", and how to turn capture on if every call is 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 you pass --no-subagents, the installer also merges two hooks into ~/.claude/settings.json. Both run evalkit coding hook --vendor claude-code with a 5-second timeout, always exit 0 and never block the agent.
PostToolUseposts aclaude_code.tool_outputrecord with the tool name, duration, and subagent id and type, plus the output itself with--capture full. It is the only way non-shell tool output reaches SyntropyLabs.SubagentStoppostsclaude_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, not as nested groups.
status
evalkit coding status reads the local config and state files and does not contact the server. It prints the endpoint, the masked key, the capture level and the last doctor result. For each installed vendor it adds the scope, capture level, whether traces are on, the hooks, and the files written, marking a deleted file "(missing)".
doctor
evalkit coding doctor posts one synthetic turn and reads it back with the same key. The turn is an agent root with a prompt, one llm_call child with tokens and cost and one tool_call child, plus an enrichment record carrying the completion. It reads up to four times, 0.75 seconds apart, and passes when the merged tree has at least 3 spans and the prompt.
The synthetic trace is tagged agent.vendor = evalkit-doctor and evalkit.doctor = true. It is left out of the session, user, agent and agent-version rollups and stays readable by trace id. The result is stored in state.json, and status shows it as the last doctor result.
--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 a tenant id only against a receiver older than that alias. install --tenant <id> stores it in config.env, and doctor --tenant <id> uses it for that run only.
An older receiver 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, and 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. It restores any value it replaced, even one that happened to match ours. With --purge, once no vendor is left installed, it also removes config.env and state.json.
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.