II.
Page JSON
Structured · livepage:docs-agent-stack-hooks-missing-capabilities
Missing Hook Capabilities — Agent Stack Gaps json
Inspect the normalized record payload exactly as the atlas UI reads it.
{
"id": "page:docs-agent-stack-hooks-missing-capabilities",
"_kind": "Page",
"_file": "wiki/docs/agent-stack/hooks/missing-capabilities.md",
"_cluster": "wiki",
"attributes": {
"nodeKind": "Page",
"sourcePath": "docs/agent-stack/hooks/missing-capabilities.md",
"sourceKind": "repo-docs",
"title": "Missing Hook Capabilities — Agent Stack Gaps",
"displayName": "Missing Hook Capabilities — Agent Stack Gaps",
"slug": "docs/agent-stack/hooks/missing-capabilities",
"articlePath": "wiki/docs/agent-stack/hooks/missing-capabilities.md",
"article": "\n# Missing Hook Capabilities — Agent Stack Gaps\n\nBeyond the 13 missing events, the agent stack lacks several hook capabilities that Claude Code supports.\n\n## 1. Handler Types (core support implemented; platform executors pending)\n\nClaude Code supports 5 hook handler types. hooks-adapter core now models and dispatches all 5 handler types. `command` remains the legacy default when `type` is omitted.\n\n| Handler Type | Claude Code | Agent Stack | Gap |\n|-------------|------------|-------------|-----|\n| `command` | ✅ Shell subprocess | ✅ hooks-adapter runner.ts | — |\n| `http` | ✅ POST to webhook URL | ✅ hooks-adapter core | Platform policy may still restrict private URLs; local/private endpoints require explicit opt-in |\n| `mcp_tool` | ✅ Call MCP server tool | Partial | hooks-adapter has the typed handler and injectable executor seam; live tools-adapter/MCP registry wiring remains tied to #576 |\n| `prompt` | ✅ LLM evaluates prompt | Partial | hooks-adapter has the typed handler and bounded executor seam; host model invocation must be supplied by agent-platform/adapters |\n| `agent` | ✅ Spawn subagent to evaluate | Partial | hooks-adapter has the typed handler and bounded executor seam; host agent spawning must be supplied by agent-platform/adapters |\n\n### Implemented\n\n**hooks-adapter/core:**\n- `src/normalizer/runner.ts` — Dispatches by handler type and preserves command fail-open/fail-closed semantics\n- `src/types/plan.ts` — `HandlerRef` is a backward-compatible typed union; omitted `type` behaves as `command`\n- `src/handlers/http.ts` — HTTP POST handler with allowed-env header interpolation, URL validation, timeout, and JSON result parsing\n- `src/handlers/mcp-tool.ts` — MCP tool handler through an injected executor seam\n- `src/handlers/prompt.ts` — Prompt handler through an injected, timeout/depth-bounded executor seam\n- `src/handlers/agent.ts` — Agent handler through an injected, timeout/depth/max-turn bounded executor seam\n\n### Remaining integration work\n\n**agent-platform:**\n- `src/harness/adapters/adapterBridge.ts` already forwards opaque hook config to adapters. Live `mcp_tool`, `prompt`, and `agent` execution still needs platform wiring that supplies hooks-adapter executor seams from the unified tool registry/model/agent invocation surfaces.\n\n## 2. Decision Types (3 missing)\n\n| Decision | Claude Code | Agent Stack | Gap |\n|----------|------------|-------------|-----|\n| `allow` | ✅ PreToolUse | ✅ hooks-adapter | — |\n| `deny` | ✅ PreToolUse | ✅ hooks-adapter | — |\n| `ask` | ✅ PreToolUse | ✅ hooks-adapter | — |\n| `defer` | ✅ Let normal flow decide | ✅ hooks-adapter | First-class decision; renders as normal flow where native adapters have no explicit field |\n| `block` | ✅ Block action with reason | ✅ hooks-adapter + agent-platform bridge | Precedence and audit bridge implemented; Claude PreToolUse renders as `deny` |\n| `retry` | ✅ PermissionDenied recovery | Partial | First-class decision with bounded/audited agent-platform metadata; native Claude retry rendering is deferred until a stable event contract exists |\n\n### Changes needed\n\n**hooks-adapter/core:**\n- `src/types/result.ts` — `defer`, `block`, `retry` are in the decision union\n- `src/merge-engine/merge.ts` and `src/sdk-interface/parser.ts` — Handle precedence and validation for the decision types\n\n**hooks-adapter/adapter-claude:**\n- `src/renderer.ts` — Renders `block` as PreToolUse `deny`; `defer` and unsupported `retry` omit native decisions explicitly\n\n**agent-platform:**\n- `src/harness/internal/createRun/orchestration/effects.ts` — Handles hook decision metadata for block/retry/session abort\n- `src/governance/` — Existing permission events audit hook block/retry bridge decisions\n\n## 3. Matcher Patterns (regex, negation, OR missing)\n\n| Capability | Claude Code | Agent Stack | Gap |\n|-----------|------------|-------------|-----|\n| Exact match | ✅ `\"Bash\"` | ✅ dot-path equality | — |\n| Pipe-separated OR | ✅ `\"Edit\\|Write\"` | ❌ AND-only | Need OR logic |\n| Regex patterns | ✅ `\"mcp__.*\"` | ❌ exact only | Need regex support |\n| Negation | ✅ (via regex `^(?!rm)`) | ❌ | Need negation operator |\n| `if` conditional | ✅ `\"Bash(rm *)\"` permission syntax | ❌ | Need `if` field parsing |\n\n### Changes needed\n\n**hooks-adapter/core:**\n- `src/normalizer/plan-resolver.ts` — Extend `evaluateWhen()` with regex, OR, negation\n- `src/types/plan.ts` — Add `if` field to HookPlanEntry\n\n## 4. Async Execution (fully missing)\n\n| Capability | Claude Code | Agent Stack | Gap |\n|-----------|------------|-------------|-----|\n| `async: true` | ✅ Background, non-blocking | ❌ | Need async handler spawn |\n| `asyncRewake: true` | ✅ Background + rewake on exit 2 | ❌ | Need rewake mechanism |\n| `once: true` | ✅ Run only once per session | ❌ | Need per-session dedup |\n\n### Changes needed\n\n**hooks-adapter/core:**\n- `src/normalizer/runner.ts` — Add async spawn mode (don't await)\n- `src/normalizer/runner.ts` — Add rewake: monitor exit code, inject stderr as system reminder\n- New: `src/state/hook-execution-tracker.ts` — Track `once` per session\n\n**agent-platform:**\n- Background hook results need to feed back into orchestration context\n\n## 5. Environment Variables (2 missing)\n\n| Variable | Claude Code | Agent Stack | Gap |\n|----------|------------|-------------|-----|\n| `CLAUDE_PROJECT_DIR` | ✅ | ❌ | Map to `AGENT_WORKSPACE_ROOT` or add |\n| `CLAUDE_ENV_FILE` | ✅ Persist env vars | ❌ | Need env file mechanism |\n| `CLAUDE_PLUGIN_ROOT` | ✅ | ✅ via `PI_PLUGIN_ROOT` | Naming mismatch |\n| `CLAUDE_PLUGIN_DATA` | ✅ | ❌ | Need plugin data dir |\n| `CLAUDE_EFFORT` | ✅ | ❌ | Need effort level propagation |\n| `CLAUDE_CODE_REMOTE` | ✅ | ❌ | Need remote detection |\n\n### Changes needed\n\n**hooks-adapter/core:**\n- `src/propagation/materialize.ts` — Add missing env vars to injection\n- New: env file mechanism for SessionStart/Setup/CwdChanged/FileChanged hooks\n\n**agent-platform:**\n- Propagate `CLAUDE_EFFORT` from session options\n\n## 6. Hook Output Processing (partial)\n\n| Capability | Claude Code | Agent Stack | Gap |\n|-----------|------------|-------------|-----|\n| `continue: false` | ✅ Stop entire session | Partial | Core merge and Claude Stop rendering exist; agent-platform hook metadata bridge treats it as an abort signal |\n| `stopReason` | ✅ Message on stop | ✅ hooks-adapter + Claude renderer | Core merge and Claude Stop reason rendering exist |\n| `suppressOutput` | ✅ Hide hook output | ✅ hooks-adapter + Claude renderer | MessageDisplay can render empty display content |\n| `systemMessage` | ✅ Warning to user | Partial | Exists but not all events |\n| `terminalSequence` | ✅ Terminal escape codes | ❌ | Need terminal sequence injection |\n| `additionalContext` | ✅ Context for Claude | Partial | Some events only |\n| `updatedInput` | ✅ Modify tool input | ✅ hooks-adapter + Claude PreToolUse | Adapter/parser alias over canonical `toolMutation`; platform mutation only applies where a live tool invocation can be proven |\n| `sessionTitle` | ✅ Set session name | ✅ Claude SessionStart renderer | Existing hook-specific output propagates `sessionTitle` |\n| `watchPaths` | ✅ Add file watchers | Deferred | Current daemon watchers have static trigger config and expose no safe dynamic registration API |\n\n### Changes needed\n\n**hooks-adapter/core:**\n- `src/types/result.ts` — Continue to keep supported output fields as typed top-level fields\n- `src/sdk-interface/parser.ts` and `src/merge-engine/merge.ts` — Normalize `updatedInput` to canonical `toolMutation`\n\n**agent-platform:**\n- `src/harness/internal/createRun/orchestration/` — Handles hook metadata `continueSession:false` as session abort\n- `src/session/` — No new API required for current Claude `sessionTitle`; it is emitted through SessionStart hook-specific output\n- `src/harness/` — `updatedInput` remains canonicalized to `toolMutation`; live tool input mutation requires a provable pending tool invocation boundary\n\n**agent-runtime:**\n- `src/daemon/` — Dynamic file watcher registration for `watchPaths` remains deferred until watcher handles support safe runtime registration\n\n## 7. Hook Configuration (partial)\n\n| Capability | Claude Code | Agent Stack | Gap |\n|-----------|------------|-------------|-----|\n| `disableAllHooks` | ✅ Global kill switch | ❌ | Need in hooks-adapter config |\n| `statusMessage` | ✅ Custom spinner text | ❌ | Need in hook UI |\n| `timeout` per hook | ✅ Configurable | ❌ | Need per-hook timeout in runner |\n| `shell` selection | ✅ bash/powershell | ❌ | Need in hook command config |\n| Header env interpolation | ✅ `$VAR` in headers | ❌ | Need for HTTP handler |\n| `allowedEnvVars` | ✅ Restrict env leaks | ❌ | Need for HTTP handler |\n\n### Changes needed\n\n**hooks-adapter/core:**\n- `src/config.ts` — Add `disableAllHooks` support\n- `src/normalizer/runner.ts` — Per-hook timeout, shell selection\n- New HTTP handler: header interpolation with allowedEnvVars\n",
"documents": []
},
"outgoingEdges": [],
"incomingEdges": [
{
"from": "page:docs-agent-stack-hooks",
"to": "page:docs-agent-stack-hooks-missing-capabilities",
"kind": "contains_page"
}
]
}