Files
amc/docs/claude-jsonl-reference/03-tool-lifecycle.md
teernisse 781e74cda2 docs(jsonl): add comprehensive Claude JSONL session log reference
Create authoritative documentation suite for Claude Code JSONL session
log processing, synthesized from codebase analysis, official Anthropic
documentation, and community tooling research.

Documentation structure (docs/claude-jsonl-reference/):

01-format-specification.md (214 lines):
- Complete message envelope structure with all fields
- Content block types (text, thinking, tool_use, tool_result)
- Usage object for token reporting
- Model identifiers and version history
- Conversation DAG structure via parentUuid

02-message-types.md (346 lines):
- Every message type with concrete JSON examples
- User messages (string content vs array for tool results)
- Assistant messages with all content block variants
- Progress events (hooks, bash, MCP)
- System, summary, and file-history-snapshot types
- Codex format differences (response_item, function_call)

03-tool-lifecycle.md (341 lines):
- Complete tool invocation to result flow
- Hook input/output formats (PreToolUse, PostToolUse)
- Parallel tool call handling
- Tool-to-result pairing algorithm
- Missing result edge cases
- Codex tool format differences

04-subagent-teams.md (363 lines):
- Task tool invocation and input fields
- Subagent transcript locations and format
- Team coordination (TeamCreate, SendMessage)
- Hook events (SubagentStart, SubagentStop)
- AMC spawn tracking with pending spawn registry
- Worktree isolation for subagents

05-edge-cases.md (475 lines):
- Parsing edge cases (invalid JSON, type ambiguity)
- Type coercion gotchas (bool vs int in Python)
- Session state edge cases (orphans, dead detection)
- Tool call edge cases (missing results, parallel ordering)
- Codex-specific quirks (content injection, buffering)
- File system safety (path traversal, permissions)
- Cache invalidation strategies

06-quick-reference.md (238 lines):
- File locations cheat sheet
- jq recipes for common queries
- Python parsing snippets
- Common gotchas table
- Useful constants
- Debugging commands

Also adds CLAUDE.md at project root linking to documentation and
providing project overview for agents working on AMC.

Sources include Claude Code hooks.md, headless.md, Anthropic Messages
API reference, and community tools (claude-code-log, claude-JSONL-browser).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-28 00:48:55 -05:00

7.8 KiB

Tool Call Lifecycle

Complete documentation of how tool invocations flow through Claude JSONL logs.

Lifecycle Overview

1. Assistant message with tool_use block
   ↓
2. PreToolUse hook fires (optional)
   ↓
3. Tool executes
   ↓
4. PostToolUse hook fires (optional)
   ↓
5. User message with tool_result block
   ↓
6. Assistant processes result

Phase 1: Tool Invocation

Claude requests a tool via tool_use content block:

{
  "type": "assistant",
  "message": {
    "role": "assistant",
    "content": [
      {
        "type": "text",
        "text": "I'll read that file for you."
      },
      {
        "type": "tool_use",
        "id": "toolu_01ReadFile123",
        "name": "Read",
        "input": {
          "file_path": "/src/auth/login.ts",
          "limit": 200
        }
      }
    ],
    "stop_reason": null
  },
  "uuid": "msg-001"
}

Tool Use Block Structure

Field Type Required Description
type "tool_use" Yes Block type identifier
id string Yes Unique tool call ID (format: toolu_*)
name string Yes Tool name
input object Yes Tool parameters

Common Tool Names

Tool Purpose Key Input Fields
Read Read file file_path, offset, limit
Edit Edit file file_path, old_string, new_string
Write Create file file_path, content
Bash Run command command, timeout
Glob Find files pattern, path
Grep Search content pattern, path, type
WebFetch Fetch URL url, prompt
WebSearch Search web query
Task Spawn subagent prompt, subagent_type
AskUserQuestion Ask user questions

Phase 2: Hook Execution (Optional)

If hooks are configured, progress events are logged:

PreToolUse Hook Input

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../session.jsonl",
  "cwd": "/Users/dev/myproject",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Read",
  "tool_input": {
    "file_path": "/src/auth/login.ts"
  }
}

Hook Progress Event

{
  "type": "progress",
  "data": {
    "type": "hook_progress",
    "hookEvent": "PreToolUse",
    "hookName": "security_check",
    "status": "running"
  },
  "parentToolUseID": "toolu_01ReadFile123",
  "toolUseID": "toolu_01ReadFile123",
  "uuid": "prog-001"
}

Hook Output (Decision)

{
  "decision": "allow",
  "reason": "File read permitted",
  "additionalContext": "Note: This file was recently modified"
}

Phase 3: Tool Result

Tool output is wrapped in a user message:

{
  "type": "user",
  "message": {
    "role": "user",
    "content": [
      {
        "type": "tool_result",
        "tool_use_id": "toolu_01ReadFile123",
        "content": "1\texport async function login(email: string, password: string) {\n2\t  const user = await db.users.findByEmail(email);\n..."
      }
    ]
  },
  "uuid": "msg-002",
  "parentUuid": "msg-001"
}

Tool Result Block Structure

Field Type Required Description
type "tool_result" Yes Block type identifier
tool_use_id string Yes Matches tool_use.id
content string Yes Tool output
is_error boolean No True if tool failed

Error Results

{
  "type": "tool_result",
  "tool_use_id": "toolu_01ReadFile123",
  "content": "Error: File not found: /src/auth/login.ts",
  "is_error": true
}

Phase 4: Result Processing

Claude processes the result and continues:

{
  "type": "assistant",
  "message": {
    "content": [
      {
        "type": "thinking",
        "thinking": "The login function looks correct. The issue might be in the middleware..."
      },
      {
        "type": "text",
        "text": "I see the login function. Let me check the middleware next."
      },
      {
        "type": "tool_use",
        "id": "toolu_01ReadMiddleware",
        "name": "Read",
        "input": {"file_path": "/src/auth/middleware.ts"}
      }
    ]
  },
  "uuid": "msg-003",
  "parentUuid": "msg-002"
}

Parallel Tool Calls

Multiple tools can be invoked in a single message:

{
  "type": "assistant",
  "message": {
    "content": [
      {"type": "tool_use", "id": "toolu_01A", "name": "Grep", "input": {"pattern": "TODO"}},
      {"type": "tool_use", "id": "toolu_01B", "name": "Grep", "input": {"pattern": "FIXME"}},
      {"type": "tool_use", "id": "toolu_01C", "name": "Glob", "input": {"pattern": "**/*.test.ts"}}
    ]
  }
}

Results come back in the same user message:

{
  "type": "user",
  "message": {
    "content": [
      {"type": "tool_result", "tool_use_id": "toolu_01A", "content": "Found 15 TODOs"},
      {"type": "tool_result", "tool_use_id": "toolu_01B", "content": "Found 3 FIXMEs"},
      {"type": "tool_result", "tool_use_id": "toolu_01C", "content": "tests/auth.test.ts\ntests/api.test.ts"}
    ]
  }
}

Codex Tool Format

Codex uses a different structure:

Function Call

{
  "type": "response_item",
  "payload": {
    "type": "function_call",
    "call_id": "call_abc123",
    "name": "Read",
    "arguments": "{\"file_path\": \"/src/auth/login.ts\"}"
  }
}

Note: arguments is a JSON string that needs parsing.

Function Result

{
  "type": "response_item",
  "payload": {
    "type": "function_call_result",
    "call_id": "call_abc123",
    "result": "File contents..."
  }
}

Tool Call Pairing

To reconstruct tool call history:

  1. Find tool_use blocks in assistant messages
  2. Match by ID to tool_result blocks in following user messages
  3. Handle parallel calls — multiple tool_use can have multiple tool_result
# Example: Pairing tool calls with results
tool_calls = {}

for line in jsonl_file:
    event = json.loads(line)

    if event['type'] == 'assistant':
        for block in event['message']['content']:
            if block['type'] == 'tool_use':
                tool_calls[block['id']] = {
                    'name': block['name'],
                    'input': block['input'],
                    'timestamp': event['timestamp']
                }

    elif event['type'] == 'user':
        content = event['message']['content']
        if isinstance(content, list):
            for block in content:
                if block['type'] == 'tool_result':
                    call_id = block['tool_use_id']
                    if call_id in tool_calls:
                        tool_calls[call_id]['result'] = block['content']
                        tool_calls[call_id]['is_error'] = block.get('is_error', False)

Missing Tool Results

Edge cases where tool results may be absent:

  1. Session interrupted — User closed session mid-tool
  2. Tool timeout — Long-running tool exceeded limits
  3. Hook blocked — PreToolUse hook returned block
  4. Permission denied — User denied tool permission

Handle by checking if tool_use has matching tool_result before assuming completion.

Tool-Specific Formats

Bash Tool

{
  "type": "tool_use",
  "name": "Bash",
  "input": {
    "command": "npm test -- --coverage",
    "timeout": 120000,
    "description": "Run tests with coverage"
  }
}

Result includes exit code context:

{
  "type": "tool_result",
  "content": "PASS src/auth.test.ts\n...\nCoverage: 85%\n\n[Exit code: 0]"
}

Task Tool (Subagent)

{
  "type": "tool_use",
  "name": "Task",
  "input": {
    "description": "Research auth patterns",
    "prompt": "Explore authentication implementations...",
    "subagent_type": "Explore"
  }
}

Result returns subagent output:

{
  "type": "tool_result",
  "content": "## Research Findings\n\n1. JWT patterns...\n\nagentId: agent-abc123"
}