Event Schema
The event types the Trenchcoat plugin records and the fields each one carries.
Every event shares a common envelope; the event-specific payload lives in data.
Envelope
{
"ts": "2026-05-12T14:23:01.456Z",
"event": "tool_use",
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"seq": 42,
"data": { }
}| Field | Type | Description |
|---|---|---|
ts | string (ISO 8601 UTC) | When the event was recorded, millisecond precision. |
event | string | Event type. See the table below. |
session_id | string | Identifier for the Claude Code session. All events in a session share it. |
seq | integer | Monotonic counter used to order events recorded within the same millisecond. |
data | object | Event-specific payload. Shape varies by event. |
The same envelope is used for local JSONL and for POST /api/v1/events.
Two names for four events
The plugin writes every event to ~/.claude/trenchcoat/events-YYYY-MM-DD.jsonl under a local name, then translates four of them to a wire name before sending. The payload is identical either way — only event changes.
| Local name (JSONL) | Wire name (API, events table) |
|---|---|
tool_start | tool_use |
tool_end | tool_result |
prompt | prompt_submit |
stop | assistant_stop |
All other types use the same name in both places. This matters when you are reading raw JSONL and querying the API in the same breath — grep tool_use finds nothing on disk, and event=tool_start matches nothing over the API.
Below, headings use the wire name, with the local name noted where they differ.
Event types
| Wire name | Local name | Hook | Fires when |
|---|---|---|---|
session_start | same | SessionStart | A session begins |
session_end | same | SessionEnd | A session ends |
prompt_submit | prompt | UserPromptSubmit | You submit a prompt |
tool_use | tool_start | PreToolUse | Immediately before a tool runs |
tool_result | tool_end | PostToolUse | After a tool completes |
skill_use | same | PreToolUse | A Skill is invoked (instead of tool_use) |
assistant_stop | stop | Stop | The assistant finishes a turn |
subagent_start | same | SubagentStart | A subagent begins |
subagent_stop | same | SubagentStop | A subagent completes |
pre_compact | same | PreCompact | Before the context is compacted |
The API also accepts an error type, which the plugin does not currently emit.
Only these types are pushed. A local event type the ingest schema does not accept is written to JSONL but never queued — the API validates the whole batch, so one unrecognized type would reject every event alongside it.
Optional fields below are omitted entirely when they do not apply — they are not sent as null.
session_start
{ "cwd": "/Users/alice/projects/my-app" }| Field | Type | Description |
|---|---|---|
cwd | string | Working directory where Claude Code was launched. |
git_branch | string | Opt-in. Current branch. Requires privacy.capture_git_branch: true; omitted otherwise, and omitted silently for a non-repo directory, a detached HEAD, or a slow git call. |
eval_id | string | Optional. From TRENCHCOAT_EVAL_ID, truncated to 128 characters. |
eval_variant | string | Optional. From TRENCHCOAT_EVAL_VARIANT, truncated to 128 characters. |
session_end
{ "duration_ms": 5062100 }| Field | Type | Description |
|---|---|---|
duration_ms | integer | Session duration in milliseconds. |
Token totals and cost are not on this event — they are derived server-side from the assistant_stop events in the session.
prompt_submit
Local name: prompt
{ "prompt_length": 184, "word_count": 31 }| Field | Type | Description |
|---|---|---|
prompt_length | integer | Character count of the submitted prompt. |
word_count | integer | Whitespace-delimited word count. |
prompt | string | Opt-in. The prompt text itself. Present only when privacy.log_prompt_content: true. |
By default this event records the shape of your prompt, never its content.
tool_use
Local name: tool_start
{
"tool_name": "Bash",
"tool_use_id": "toolu_01ABC...",
"correlation_id": "9f86d081b8ac",
"input_preview": "ls -la /Users/alice/projects"
}| Field | Type | Description |
|---|---|---|
tool_name | string | Tool being invoked (Bash, Edit, Read, …). |
tool_use_id | string | Claude Code's own identifier for this tool call. |
correlation_id | string | Ties this event to its matching tool_result. |
input_preview | string | Truncated tool input, privacy.tool_input_preview_chars characters (default 100). Set to 0 to disable. |
origin_agent_id | string | Optional. The subagent that made this call, when it did not come from the main thread. |
origin_agent_type | string | Optional. That subagent's type. |
spawner_id | string | Optional. Activation ID of the Skill this call happened under. |
spawner_type | string | Optional. Always "skill" when present. |
agent_id | string | Agent tool only. ID minted for this spawn, carried onto the matching tool_result and the subagent's own events. |
edge_label | string | Agent tool only, optional. One of delegate, verify, critique. See Edge labels. |
tool_result
Local name: tool_end
{
"tool_name": "Bash",
"tool_use_id": "toolu_01ABC...",
"correlation_id": "9f86d081b8ac",
"duration_ms": 634,
"duration_source": "native",
"result_size": 1428,
"is_error": false,
"error_preview": null
}| Field | Type | Description |
|---|---|---|
tool_name | string | Tool that completed. |
tool_use_id | string | Matches the tool_use. |
correlation_id | string | null | Matches the originating tool_use, when correlation succeeded. |
duration_ms | number | null | Wall-clock duration. |
duration_source | string | null | How the duration was obtained: native (reported by Claude Code), computed (measured by the plugin), or reconciled (recovered at turn end for a call whose completion was never observed). |
result_size | integer | Size in characters of the serialized result. |
is_error | boolean | null | Whether the tool errored. null when the result shape is not conclusive. |
error_preview | string | null | Short excerpt of the error, only when is_error is true. |
origin_agent_id | string | Optional. Same meaning as on tool_use. |
origin_agent_type | string | Optional. Same meaning as on tool_use. |
spawner_id / spawner_type | string | Optional. Same meaning as on tool_use. |
agent_id / edge_label | string | Agent tool only. Same values as the matching tool_use. |
A duration_source of reconciled means the plugin never saw the tool finish and closed the record out at the end of the turn — treat those durations as upper bounds.
skill_use
Emitted instead of tool_use when the invoked tool is a Skill.
{
"skill_name": "superpowers:brainstorming",
"activation_id": "3c9a1e7f2b04",
"args_preview": "redesign the connect command"
}| Field | Type | Description |
|---|---|---|
skill_name | string | Fully qualified skill name. |
activation_id | string | Identifier for this activation. Tool calls made while the skill is active carry it as their spawner_id. |
args_preview | string | Truncated arguments, same budget as input_preview. |
spawner_id / spawner_type | string | Optional. Present when this skill was invoked inside another skill's activation. |
origin_agent_id / origin_agent_type | string | Optional. Present when a subagent invoked the skill. |
assistant_stop
Local name: stop
{
"reason": "end_turn",
"input_tokens": 12400,
"output_tokens": 2340,
"cache_creation_tokens": 1800,
"cache_read_tokens": 96000,
"model": "claude-sonnet-4-6"
}| Field | Type | Description |
|---|---|---|
reason | string | Why the turn ended. |
input_tokens | integer | Input tokens for the turn. |
output_tokens | integer | Output tokens for the turn. |
cache_creation_tokens | integer | null | Tokens written to the prompt cache. |
cache_read_tokens | integer | null | Tokens served from the prompt cache. |
model | string | null | Model that generated the turn, parsed from the transcript. |
Cache fields are separate from input_tokens because they are priced differently; session cost is derived from all four.
subagent_start
{ "agent_id": "3c9a1e7f2b04", "agent_type": "general-purpose", "agent_kind": "builtin" }| Field | Type | Description |
|---|---|---|
agent_id | string | Optional. Matches the agent_id on the spawning tool_use. |
agent_type | string | Optional. Subagent type. |
agent_kind | string | Optional. Classification derived from the type and working directory (e.g. a built-in agent versus a project-defined one). |
subagent_stop
{
"agent_id": "3c9a1e7f2b04",
"agent_type": "general-purpose",
"agent_kind": "builtin",
"tool_counts": { "Read": 4, "Bash": 2 },
"tool_count_total": 6,
"turns": 5,
"input_tokens": 8210,
"output_tokens": 1340,
"model": "claude-haiku-4-5",
"stop_hook_active": false
}| Field | Type | Description |
|---|---|---|
agent_id | string | Optional. Recovered from the pending stack when available. |
agent_type | string | Subagent type; defaults to general-purpose when the hook omits it. |
agent_kind | string | Same classification as on subagent_start. |
tool_counts | object | Tool name → invocation count, parsed from the subagent's transcript. |
tool_count_total | integer | Total tool calls made by the subagent. |
turns | integer | Assistant turns in the subagent's transcript. |
input_tokens / output_tokens | integer | Cumulative tokens across the subagent's turns. |
model | string | null | Model the subagent ran on. |
stop_hook_active | boolean | Whether a stop hook was active for this subagent. |
pre_compact
{}Carries no payload. Its value is the timestamp — it marks where context was compacted, which is often where a session's behavior changes.
Edge labels
When one agent spawns another via the Agent tool, the spawning agent can annotate why by embedding a marker anywhere in the Task prompt:
[tc:delegate]
[tc:verify]
[tc:critique]If the label matches one of those three (case-insensitive), it is:
- Recorded as
edge_labelon both thetool_useand matchingtool_resultfor thatAgentinvocation. - Stripped from the prompt before truncation into
input_preview, so the marker never appears in the stored preview and does not consume the truncation budget.
Unrecognized markers are left in place and ignored — no edge_label is emitted. The convention applies only to the Agent tool.
Caveat — parallel spawns. agent_id correlation and edge-label attachment both assume at most one Agent spawn in flight per session. With concurrent spawns, attribution may be ambiguous between siblings, and any latency or label derived from it inherits that ambiguity. Serial spawns are unaffected.
Schema stability
data is stored as jsonb and is not strictly validated at ingest, so the schema can evolve without breaking older clients. Fields documented here are stable; new fields may appear without a major version bump. Removals and renames are announced in the changelog.