Field Manual
Tool Calls
A tool call is an agent-mediated request to do something outside the model. It can read, write, execute, query, browse, or transform evidence.
Why it matters
Tool calls bridge intent and effect. They often show what changed, what was attempted, and what external systems were touched.
Evidence to look for
- Tool name and capability
- Input arguments
- Output or error
- Approval state
- Files or APIs affected
- Timestamp and session ID
Common pitfalls
- Recording the command but not the output
- Missing denied or failed tool calls
- Ignoring implicit side effects
How different agents record tool calls
Each coding agent stores tool call evidence differently. The examples below are synthetic shapes, not complete live-agent behavior. Current sections are formats Telltale still acquires. Legacy labels distinguish older accepted formats from retired registrations. The Codex discriminator remains accepted, and OpenCode legacy SQLite tables remain readable when neither v2 table exists; retired source identities are not acquired.
Claude Code
Claude Code stores sessions as JSONL files under ~/.claude/projects. Tool calls and results are nested as content blocks inside message objects. A tool_use block appears in the assistant message content array, and the matching tool_result appears in the next user message content array, linked by tool_use_id.
{"type":"assistant","sessionId":"claude-tool-use","timestamp":"2026-04-27T12:10:01Z","message":{"role":"assistant","model":"claude-fixture-model","content":[{"type":"text","text":"I will read the public README only."},{"type":"tool_use","id":"toolu_fixture_read","name":"Read","input":{"file_path":"README.md"}}]}}
{"type":"user","sessionId":"claude-tool-use","timestamp":"2026-04-27T12:10:02Z","message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_fixture_read","content":"# ADR\nSynthetic README excerpt for parser coverage.","is_error":false}]}}Tool calls live inside assistant message.content as tool_use blocks. Results come back in the next user message as tool_result blocks linked by tool_use_id.
OpenAI Codex CLI
Codex stores JSONL under $CODEX_HOME or ~/.codex, in sessions, archived_sessions, and headless. Current rollouts use completed items and response items. A completed command keeps its command array, aggregated output, and exit code. Call ID is conditional.
{"type":"session_meta","payload":{"id":"thread-fixture","session_id":"root-fixture"}}
{"type":"event_msg","payload":{"type":"item_completed","thread_id":"thread-fixture","turn_id":"turn-fixture","item":{"type":"CommandExecution","id":"call-fixture","command":["printf","synthetic"],"status":"completed","aggregated_output":"synthetic","exit_code":0}}}Current shape. The same completed-item contract is used for codex.sessions, codex.archived_sessions, and codex.headless_sessions. Completed McpToolCall, FileChange, and Extension items are quarantined, not mapped.
{"type":"event_msg","timestamp":"2026-04-03T06:00:02Z","payload":{"type":"tool_call","tool_name":"mcp_inspector","arguments":{"server":"filesystem"},"message":"Run a synthetic probe."}}Legacy discriminator. The parser still accepts this synthetic shape. It is not the current rollout envelope, and it is not a maintained live format.
Qwen Code
Qwen Code stores sessions as JSONL files under ~/.qwen/projects. Tool calls and results are direct top-level objects in the JSONL stream. Each record carries agent, provider, and model fields at the root level alongside the event data.
{"type":"user","sessionId":"qwen-uc001-tool-result","timestamp":"2026-04-27T12:40:00Z","agent":"qwen","provider":"qwen","model":"qwen3-coder-plus","content":"Use the synthetic repo_status MCP fixture only."}
{"type":"tool_call","sessionId":"qwen-uc001-tool-result","timestamp":"2026-04-27T12:40:01Z","agent":"qwen","provider":"qwen","model":"qwen3-coder-plus","name":"repo_status","input":{"format":"json"}}
{"type":"tool_result","sessionId":"qwen-uc001-tool-result","timestamp":"2026-04-27T12:40:02Z","agent":"qwen","provider":"qwen","model":"qwen3-coder-plus","tool":"repo_status","content":"MCP tool result for repo_status: status=ok, repository=fixture-repo"}Each line is a standalone record. Tool calls use name and input fields. Tool results use tool and content fields. Both carry session metadata (agent, provider, model) on every record.
OpenCode
The retained source is opencode.sqlite, typically ~/.local/share/opencode/opencode.db on Linux. When session_v2 and session_message exist, only those tables are read, even if legacy message and part tables remain. A tool entry lives in the assistant message data, not in a separate part row. Reported execution and settled agent `shell` metadata can emit execution stages, but v2 tool-entry ToolExecution remains Unknown; v2 user shell commands report Supported.
{
"id": "msg-fixture",
"session_id": "ses-fixture",
"type": "assistant",
"seq": 2,
"time_updated": 1775000002000,
"data": {
"content": [
{
"type": "tool",
"id": "call-fixture",
"name": "bash",
"executed": false,
"state": {
"status": "completed",
"input": { "command": "printf synthetic" },
"content": [{ "type": "text", "text": "synthetic" }]
}
}
]
}
}Current shape when v2 tables are present. One session_message row is one native unit. Identity is the message ID plus the content index. Oversized rows are quarantined.
{
"id": "part-fixture",
"session_id": "ses-fixture",
"data": {
"type": "tool",
"tool": "bash",
"state": {
"status": "completed",
"input": { "command": "printf synthetic" },
"output": "synthetic"
}
}
}Legacy. The part and message tables are still read only when neither v2 table exists. Filesystem JSON under storage/message, and opencode.legacy_json, are retired: support is no longer maintained.
Legacy — Gemini, Roo Code, and Kilo Code
Legacy. Support is no longer maintained. gemini.tmp, roocode.tasks, and kilocode.tasks are not registered, discovered, or parsed. Older pages showed synthetic ui_messages.json arrays and single-file Gemini sessions. Those examples are not verified current product schemas. Preserve whatever the installed version actually wrote, and do not expect Telltale to acquire it.
OpenClaw
OpenClaw is a fixture-backed JSONL source under ~/.openclaw/agents. Discovery matches filenames that contain .jsonl, including archived and reset suffixes. Tool calls and results are parsed when the file reports them. ToolExecution is unknown. Workspace, process ID, and exit code are absent. This is not live-validated.
GitHub Copilot
GitHub Copilot process logs are project-local logs/copilot/*.log, not a home session store. Tool calls appear as function_call items inside JSON arrays on Accumulated output items lines. UserContext is unsupported, so a missing user message is a visibility gap, not proof that no prompt existed. Logs can still contain message items. Tool results are conditional. ToolExecution is unknown.
2026-04-27T16:16:57.841Z [INFO] Workspace initialized: copilot-uc001-tool-result (checkpoints: 0)
2026-04-27T16:16:57.847Z [INFO] Starting Copilot CLI: 1.0.36
2026-04-27T16:17:17.990Z [INFO] Accumulated output items (2): [{"arguments":"{\"format\":\"json\"}","call_id":"call_fixture_001","id":"c1","name":"repo_status","status":"completed","type":"function_call"}]
2026-04-27T16:17:30.100Z [INFO] Accumulated output items (1): [{"arguments":"{\"format\":\"json\"}","call_id":"call_fixture_002","id":"c2","name":"repo_status","status":"completed","type":"function_call","message":"MCP tool result: status=ok, repository=fixture-repo"}]
2026-04-27T16:17:45.200Z [INFO] Session completed.Copilot CLI writes timestamped log lines. Tool calls appear as function_call objects inside JSON arrays embedded in Accumulated output items log lines. The arguments field is a JSON-encoded string, not a structured object.
Format comparison
Use the source identity, not the client family, when deciding what a tool call can prove.
- Current formats: JSONL for Claude, Codex, Qwen, and OpenClaw; SQLite for OpenCode; project-local process logs for Copilot.
- Legacy, support no longer maintained: gemini.tmp, roocode.tasks, kilocode.tasks, opencode.legacy_json, opencode.project_json, and codex.project_sessions. OpenCode part and message tables are a legacy schema still read only when v2 tables are absent.
- Correlation: prefer tool_use_id or call_id when the source reports one. Ordering alone does not prove a call/result relationship.
- Call ID, error state, workspace, and content parts can be conditional. Copilot user context and content parts are absent. ToolExecution is unknown for Copilot, Qwen, and OpenClaw.
- OpenClaw is fixture-backed only. Files under ~/.openclaw/agents match when the name contains .jsonl, including archived and reset suffixes.
What to capture
When preserving tool call evidence from any agent, capture these fields at minimum:
- Tool name — what capability was invoked
- Input arguments — what parameters were passed, as structured data
- Output or error — what the tool returned, including error states
- Timestamps — when the call was made and when the result arrived
- Session ID — to correlate across multiple tool calls in the same session
- Correlation ID — tool_use_id, call_id, or sequential position to link calls with results
- Approval state — whether the call was auto-approved, user-approved, or denied
- Agent and model — which agent and model version made the call
- Raw format — preserve the original file alongside any canonical or telemetry view
