Plugin SDK & Hooks

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": { }
}
FieldTypeDescription
tsstring (ISO 8601 UTC)When the event was recorded, millisecond precision.
eventstringEvent type. See the table below.
session_idstringIdentifier for the Claude Code session. All events in a session share it.
seqintegerMonotonic counter used to order events recorded within the same millisecond.
dataobjectEvent-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_starttool_use
tool_endtool_result
promptprompt_submit
stopassistant_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 nameLocal nameHookFires when
session_startsameSessionStartA session begins
session_endsameSessionEndA session ends
prompt_submitpromptUserPromptSubmitYou submit a prompt
tool_usetool_startPreToolUseImmediately before a tool runs
tool_resulttool_endPostToolUseAfter a tool completes
skill_usesamePreToolUseA Skill is invoked (instead of tool_use)
assistant_stopstopStopThe assistant finishes a turn
subagent_startsameSubagentStartA subagent begins
subagent_stopsameSubagentStopA subagent completes
pre_compactsamePreCompactBefore 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" }
FieldTypeDescription
cwdstringWorking directory where Claude Code was launched.
git_branchstringOpt-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_idstringOptional. From TRENCHCOAT_EVAL_ID, truncated to 128 characters.
eval_variantstringOptional. From TRENCHCOAT_EVAL_VARIANT, truncated to 128 characters.

session_end

{ "duration_ms": 5062100 }
FieldTypeDescription
duration_msintegerSession 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 }
FieldTypeDescription
prompt_lengthintegerCharacter count of the submitted prompt.
word_countintegerWhitespace-delimited word count.
promptstringOpt-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"
}
FieldTypeDescription
tool_namestringTool being invoked (Bash, Edit, Read, …).
tool_use_idstringClaude Code's own identifier for this tool call.
correlation_idstringTies this event to its matching tool_result.
input_previewstringTruncated tool input, privacy.tool_input_preview_chars characters (default 100). Set to 0 to disable.
origin_agent_idstringOptional. The subagent that made this call, when it did not come from the main thread.
origin_agent_typestringOptional. That subagent's type.
spawner_idstringOptional. Activation ID of the Skill this call happened under.
spawner_typestringOptional. Always "skill" when present.
agent_idstringAgent tool only. ID minted for this spawn, carried onto the matching tool_result and the subagent's own events.
edge_labelstringAgent 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
}
FieldTypeDescription
tool_namestringTool that completed.
tool_use_idstringMatches the tool_use.
correlation_idstring | nullMatches the originating tool_use, when correlation succeeded.
duration_msnumber | nullWall-clock duration.
duration_sourcestring | nullHow 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_sizeintegerSize in characters of the serialized result.
is_errorboolean | nullWhether the tool errored. null when the result shape is not conclusive.
error_previewstring | nullShort excerpt of the error, only when is_error is true.
origin_agent_idstringOptional. Same meaning as on tool_use.
origin_agent_typestringOptional. Same meaning as on tool_use.
spawner_id / spawner_typestringOptional. Same meaning as on tool_use.
agent_id / edge_labelstringAgent 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"
}
FieldTypeDescription
skill_namestringFully qualified skill name.
activation_idstringIdentifier for this activation. Tool calls made while the skill is active carry it as their spawner_id.
args_previewstringTruncated arguments, same budget as input_preview.
spawner_id / spawner_typestringOptional. Present when this skill was invoked inside another skill's activation.
origin_agent_id / origin_agent_typestringOptional. 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"
}
FieldTypeDescription
reasonstringWhy the turn ended.
input_tokensintegerInput tokens for the turn.
output_tokensintegerOutput tokens for the turn.
cache_creation_tokensinteger | nullTokens written to the prompt cache.
cache_read_tokensinteger | nullTokens served from the prompt cache.
modelstring | nullModel 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" }
FieldTypeDescription
agent_idstringOptional. Matches the agent_id on the spawning tool_use.
agent_typestringOptional. Subagent type.
agent_kindstringOptional. 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
}
FieldTypeDescription
agent_idstringOptional. Recovered from the pending stack when available.
agent_typestringSubagent type; defaults to general-purpose when the hook omits it.
agent_kindstringSame classification as on subagent_start.
tool_countsobjectTool name → invocation count, parsed from the subagent's transcript.
tool_count_totalintegerTotal tool calls made by the subagent.
turnsintegerAssistant turns in the subagent's transcript.
input_tokens / output_tokensintegerCumulative tokens across the subagent's turns.
modelstring | nullModel the subagent ran on.
stop_hook_activebooleanWhether 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:

  1. Recorded as edge_label on both the tool_use and matching tool_result for that Agent invocation.
  2. 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.

On this page