research phase 4.5 hooks - sinrimin/harness-nexus GitHub Wiki
Status: research complete. PRD:
prd-phase-4-web-ui.md(Β§4.5). Design: to be written intodesign-phase-4-web-ui.mdbefore implementation.
This answers the open question gating the Hook management sub-phase: what is the
canonical hook event set, and which events does each target tool (Claude Code,
ZCode, Hermes) support? The result is a fixed event Γ target matrix that
lands in packages/shared and drives both the 4.5 hook editor and the Phase 3.2
install writer.
- Claude Code has a large event set (~30 events across session/turn/tool/ notification/lifecycle categories).
- ZCode supports exactly 7 events (a strict subset of CC's tool/session events). Any other event name is unsupported.
-
Hermes does not use the CC
hooks.jsoneventβcommand model at all β its hooks are Python plugins (plugin.yaml+ entry points) registered with aPluginManager. There is no event-name interop; a Hermes hook is code, not a declarative event binding. -
Conclusion: model our hook resource as a
hooks.json-shape document (the CC/ZCode interop format), validate events against a per-target matrix, and treat Hermes as unsupported for declarative hooks (a Hermes hook = a Python plugin, which is a different resource shape entirely β out of scope for 4.5).
Source of truth: hooks reference. Verified by reading the official doc (July 2026). Events fall into cadences:
-
once per session:
SessionStart,SessionEnd -
once per turn:
UserPromptSubmit,UserPromptExpansion,Stop,StopFailure,PreCompact,PostCompact -
on every tool call:
PreToolUse,PostToolUse,PostToolUseFailure,PostToolBatch,PermissionRequest,PermissionDenied -
setup/lifecycle:
Setup,InstructionsLoaded -
subagents/teams:
SubagentStart,SubagentStop,TaskCreated,TaskCompleted,TeammateIdle -
environment/files:
ConfigChange,CwdChanged,FileChanged,WorktreeCreate,WorktreeRemove -
display/IO:
MessageDisplay,Notification,Elicitation,ElicitationResult
Hook types: command, http, mcp_tool, prompt, agent. Matchers are
regex/exact against tool names (for tool events) or other fields.
Source of truth: the built-in zcode-guide plugin's diagnosing-hooks/SKILL.md
on this machine (Z.ai-authored, authoritative). Verified locally.
The seven supported events: SessionStart, UserPromptSubmit, PreToolUse,
PermissionRequest, PostToolUse, PostToolUseFailure, Stop.
"Events such as
Notification,SubagentStop, andPreCompactare not supported." Matcher is a case-sensitive regex. Two hook types:command(shell) andprocess(exec vector). Plugin hooks run with no extra trust gate. This matches Phase 3 research (research-phase-3-plugin-targets.md).
Source: ~/.hermes/hermes-agent/ (read-only). Hermes hooks are not the CC
hooks.json model. They are:
-
Python plugins under
~/.hermes/plugins/<name>/withplugin.yaml+ Python entry points, registered via aPluginManager. A "hook" is code that subscribes to lifecycle events in-process. - (config-layer shell hooks exist but are a separate, simpler mechanism.)
There is no event-name interop with CC/ZCode. A Hermes hook can't be
expressed as a hooks.json eventβcommand binding, and vice versa. For 4.5,
Hermes is out of scope for declarative hooks β a Hermes hook is a Python
plugin (a different resource shape). If/when we support it, it's a separate
kind or a Phase 7+ concern.
Canonical events (the union we validate against), with per-target support. β
= the event fires in that tool; β = unsupported (the writer drops it for that
target with a reason).
| Event | Claude Code | ZCode | Hermes |
|---|---|---|---|
SessionStart |
β | β | β (n/a) |
UserPromptSubmit |
β | β | β |
PreToolUse |
β | β | β |
PostToolUse |
β | β | β |
PostToolUseFailure |
β | β | β |
PermissionRequest |
β | β | β |
Stop |
β | β | β |
SessionEnd |
β | β | β |
PreCompact |
β | β | β |
PostCompact |
β | β | β |
Notification |
β | β | β |
SubagentStart |
β | β | β |
SubagentStop |
β | β | β |
UserPromptExpansion |
β | β | β |
PostToolBatch |
β | β | β |
PermissionDenied |
β | β | β |
| (other CC events) | β | β | β |
Hermes column is uniformly "β" because its hook model is structurally different (Python plugins, not declarative event bindings). Don't model it as "unsupported events"; model it as "different resource kind."
A map Record<AgentTarget, ReadonlySet<HookEvent> | null> where null means
"this target doesn't use the declarative hooks.json model at all" (Hermes).
Lives in packages/shared/src/hooks.ts:
export const HOOK_EVENTS = [
'SessionStart',
'UserPromptSubmit',
'PreToolUse',
'PostToolUse',
'PostToolUseFailure',
'PermissionRequest',
'Stop',
'SessionEnd',
'PreCompact',
'PostCompact',
'Notification',
'SubagentStart',
'SubagentStop',
'UserPromptExpansion',
'PostToolBatch',
'PermissionDenied',
] as const;
export type HookEvent = (typeof HOOK_EVENTS)[number];
// null = target uses a different hook model (not declarative hooks.json).
export const HOOK_SUPPORT: Record<AgentTarget, ReadonlySet<HookEvent> | null> = {
'claude-code': new Set(HOOK_EVENTS),
zcode: new Set([
'SessionStart',
'UserPromptSubmit',
'PreToolUse',
'PostToolUse',
'PostToolUseFailure',
'PermissionRequest',
'Stop',
]),
hermes: null,
generic: new Set(HOOK_EVENTS), // assume permissive for 'generic'
};A hook resource stores a hooks.json document (the CC/ZCode interop
format) as source: { type: 'inline', content: <JSON string> }. The content is
the { hooks: { <Event>: [{ matcher?, hooks: [...] }] } } shape both CC and
ZCode consume. The editor is a structured eventβhandler form that emits valid
hooks.json, not a freeform textarea (too easy to produce invalid JSON).
Validation at save time: parse the JSON, and for each event key, check it
against HOOK_SUPPORT for each of the resource's targets. An event unsupported
by all declared targets is a hard error (409 HOOK_EVENT_UNSUPPORTED). An
event unsupported by some targets is a soft warning surfaced in the UI ("this
hook will be dropped for ZCode") but still saved (the install writer filters per
target).
-
Hook handler types. CC supports 5 (
command/http/mcp_tool/prompt/agent); ZCode supports 2 (command/process). 4.5's editor should offercommand(universal) first;process(ZCode) and the CC-only types are later refinements. Don't over-build the handler editor initially. -
processvscommand. ZCode'sprocesstype (exec vector) has no CC equivalent; CC's shell form iscommand. The editor maps a "command" entry tocommandfor CC and eithercommandorprocessfor ZCode. Keep it simple: emitcommand(shell) for both in 4.5;processis a ZCode nicety for later. - Matcher semantics differ subtly (CC v2.1.195+ hyphen rules; ZCode is case-sensitive regex always). The editor shouldn't try to abstract this β surface "matcher is a regex against the tool name" and let the author own it.
-
Hermes. Out of scope for 4.5 (different model). A Hermes target on a hook
resource should be rejected at save (
409 TARGET_NO_DECLARATIVE_HOOKS) rather than silently ignored.
- Claude Code: hooks reference (read in full, July 2026 β the authoritative event list + schemas).
- ZCode: built-in
zcode-guideplugin,diagnosing-hooks/SKILL.md(on-disk, this machine) β the 7-event list + matcher/handler semantics. - Hermes:
~/.hermes/hermes-agent/(read-only) β PythonPluginManagermodel, nohooks.jsonevent interop. - Phase 3 research:
research-phase-3-plugin-targets.md(ZCode hook narrowing, plugin hook formats).