Skip to main content

Hook System

The cosh-core hook system allows injecting external logic at key points in the Agent execution flow. Hooks are implemented by executing external commands and support interception, auditing, and context injection.

Event Points

EventTrigger TimingInterceptable
PreToolUseBefore tool executionYes (block/allow/ask)
PostToolUseAfter tool executionYes (block/allow)
PostToolUseFailureAfter tool execution failureNo
UserPromptSubmitWhen user message is submittedYes (block/allow)
SessionStartAfter session initializationNo
StopWhen Agent decides to stopYes (block/allow)
BeforeModelBefore LLM request is sentNo
AfterModelAfter LLM response is receivedNo

Configuration

Define hooks in ~/.copilot-shell/config.toml:

[hooks]
enabled = true

[[hooks.PreToolUse]]
name = "security-check"
command = "/usr/local/bin/my-security-hook"
timeout = 5000

[[hooks.SessionStart]]
name = "context-loader"
command = "/usr/local/bin/load-context"
timeout = 3000

Hooks can also be registered via an extension's cosh-extension.json.

Protocol

Input (stdin → hook process)

Core writes event data in JSON format to the hook process stdin:

{
"session_id": "abc-123",
"cwd": "/home/user/project",
"hook_event_name": "PreToolUse",
"timestamp": "2026-07-01T10:00:00Z",
"transcript_path": "/home/user/.copilot-shell/sessions/abc-123",
"tool_name": "shell",
"tool_input": { "command": "rm -rf /tmp/old" }
}

Output (hook process → stdout)

Hooks return decisions in JSON format:

{
"decision": "block",
"reason": "Dangerous rm -rf command",
"systemMessage": "Command blocked by security policy"
}

Decision Values

decisionMeaning
allowAllow to proceed
block / denyIntercept, abort the operation
askRequire user confirmation
None / emptyPass-through (no intervention)

Additional Fields

FieldDescription
reasonDecision reason (embedded in block/deny decisions, also serves as notification message fallback)
systemMessageNotification message (displayed to user with priority over reason)
hookSpecificOutputCustom JSON data (additional_context within it is injected into conversation context)

Execution Model

  1. Multiple hooks can be registered for the same event point, executed sequentially in configuration order
  2. If any hook returns block, the final decision is block (short-circuit)
  3. Hook timeout (default 5000ms) is treated as pass-through
  4. Non-zero exit code from hook process is treated as error, does not affect main flow
  5. Hook definitions without a name field are skipped

Notifications

Notifications generated after hook execution are delivered to Shell via JSONL output:

{"type":"stream_event","event":{"subtype":"hook_notification","hook_name":"security-check","message":"Command blocked by security policy","decision":"block"}}

Shell is responsible for rendering notification cards.

Extension Hooks

Hooks registered via cosh-extension.json are merged with configuration file hooks and use the same execution protocol. See extensions.md.