---
name: session-logs
description: Schema reference for session JSONL transcripts. Use when reading prior conversations, debugging tool timing, or searching session history.
---

# Session Logs

Session transcripts are stored as JSONL files at:

```
~/.shortcut/agent/sessions/<cwd-slug>/<timestamp>_<uuid>.jsonl
```

- `<cwd-slug>` is the launch directory with `/` replaced by `-` and wrapped in `--` (e.g. `--Users-alice-project--`)
- Files are named `<ISO-timestamp>_<session-uuid>.jsonl`, most recent = latest by filename sort

## JSONL Entry Types

Every line is a JSON object with `type`, `id`, `parentId`, and `timestamp` (ISO 8601 UTC).

### Metadata entries (appear once at session start)

| type                    | key fields                            |
| ----------------------- | ------------------------------------- |
| `session`               | `version`, `id`, `cwd`                |
| `model_change`          | `provider`, `modelId`                 |
| `thinking_level_change` | `thinkingLevel`                       |
| `system_prompt`         | `content` (full system prompt string) |
| `tool_definitions`      | `tools[]` (array of tool schemas)     |

### Conversation entries

**`turn_start`** / **`turn_end`** — bracket each agent turn.

- `turn_end` has `durationMs` (wall-clock time for the entire turn)

**`message`** — the core entry. Shape depends on `message.role`:

#### `message.role: "user"`

```jsonc
{
  "type": "message",
  "timestamp": "...",
  "message": {
    "role": "user",
    "content": [{ "type": "text", "text": "user's message" }],
    "timestamp": 1776020052931 // unix ms
  }
}
```

#### `message.role: "assistant"`

`message.content` is an array of blocks. Block types:

| block.type | fields                    | description                                                                                                       |
| ---------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `text`     | `text`                    | Assistant's visible response text                                                                                 |
| `thinking` | `thinking`                | Chain-of-thought (when thinking is enabled)                                                                       |
| `toolCall` | `id`, `name`, `arguments` | Tool invocation. `name` = tool name (e.g. `"bash"`, `"mcp"`, `"execute_code"`). `arguments` = dict of tool params |

A single assistant message can contain multiple blocks (e.g. thinking + text + toolCall).

#### `message.role: "toolResult"`

```jsonc
{
  "type": "message",
  "timestamp": "...",
  "message": {
    "role": "toolResult",
    "toolCallId": "toolu_...", // matches toolCall.id
    "toolName": "bash", // tool that was called
    "content": [{ "type": "text", "text": "stdout/result" }],
    "isError": false,
    "timestamp": 1776020062071 // unix ms
  },
  "durationMs": 18 // ⬅ wall-clock ms for this tool execution
}
```

## Key Lookup Patterns

**Find tool calls by tool name:**

```python
for entry in lines:
    if entry.get('type') != 'message': continue
    msg = entry['message']
    if msg['role'] == 'assistant':
        for block in msg.get('content', []):
            if block.get('type') == 'toolCall' and block.get('name') == 'mcp':
                print(block['id'], block['arguments'])
```

**Get tool execution duration:**
`durationMs` is on the `toolResult` message entry (top-level, not inside `message`). No need to diff timestamps.

```python
for entry in lines:
    if entry.get('type') != 'message': continue
    msg = entry['message']
    if msg['role'] == 'toolResult' and msg.get('toolName') == 'mcp':
        print(f"{msg['toolCallId']} took {entry['durationMs']}ms")
```

**Match a toolCall to its result:** join on `toolCall.id == toolResult.toolCallId`.

**Find sessions by date:** filenames start with ISO timestamps — use `ls -t` or sort by name.

**Search across sessions for content:**

```bash
grep -rl "search term" ~/.shortcut/agent/sessions/ --include="*.jsonl"
```
