use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum HookEvent {
PreToolUse,
PostToolUse,
PostToolUseFailure,
UserPromptSubmit,
Stop,
SubagentStop,
PreCompact,
Notification,
SubagentStart,
PermissionRequest,
}
#[non_exhaustive]
pub struct HookMatcher {
pub matcher: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BaseHookInput {
pub session_id: String,
pub transcript_path: String,
pub cwd: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub permission_mode: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PreToolUseHookInput {
#[serde(flatten)]
pub base: BaseHookInput,
pub hook_event_name: String, pub tool_name: String,
pub tool_input: serde_json::Value,
pub tool_use_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PostToolUseHookInput {
#[serde(flatten)]
pub base: BaseHookInput,
pub hook_event_name: String, pub tool_name: String,
pub tool_input: serde_json::Value,
pub tool_response: serde_json::Value,
pub tool_use_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PostToolUseFailureHookInput {
#[serde(flatten)]
pub base: BaseHookInput,
pub hook_event_name: String, pub tool_name: String,
pub tool_input: serde_json::Value,
pub tool_use_id: String,
pub error: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_interrupt: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserPromptSubmitHookInput {
#[serde(flatten)]
pub base: BaseHookInput,
pub hook_event_name: String, pub prompt: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StopHookInput {
#[serde(flatten)]
pub base: BaseHookInput,
pub hook_event_name: String, pub stop_hook_active: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubagentStopHookInput {
#[serde(flatten)]
pub base: BaseHookInput,
pub hook_event_name: String, pub stop_hook_active: bool,
pub agent_id: String,
pub agent_transcript_path: String,
pub agent_type: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PreCompactHookInput {
#[serde(flatten)]
pub base: BaseHookInput,
pub hook_event_name: String, pub trigger: String, #[serde(skip_serializing_if = "Option::is_none")]
pub custom_instructions: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NotificationHookInput {
#[serde(flatten)]
pub base: BaseHookInput,
pub hook_event_name: String, pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
pub notification_type: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubagentStartHookInput {
#[serde(flatten)]
pub base: BaseHookInput,
pub hook_event_name: String, pub agent_id: String,
pub agent_type: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PermissionRequestHookInput {
#[serde(flatten)]
pub base: BaseHookInput,
pub hook_event_name: String, pub tool_name: String,
pub tool_input: serde_json::Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub permission_suggestions: Option<Vec<serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum HookInput {
PreToolUse(PreToolUseHookInput),
PostToolUse(PostToolUseHookInput),
PostToolUseFailure(PostToolUseFailureHookInput),
UserPromptSubmit(UserPromptSubmitHookInput),
Stop(StopHookInput),
SubagentStop(SubagentStopHookInput),
PreCompact(PreCompactHookInput),
Notification(NotificationHookInput),
SubagentStart(SubagentStartHookInput),
PermissionRequest(PermissionRequestHookInput),
}
#[derive(Debug, Clone, Default)]
pub struct HookContext {
pub signal: Option<()>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncHookOutput {
#[serde(rename = "continue", skip_serializing_if = "Option::is_none")]
pub continue_: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub suppress_output: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub decision: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub system_message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hook_specific_output: Option<HookSpecificOutput>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum HookSpecificOutput {
PreToolUse(PreToolUseHookSpecificOutput),
PostToolUse(PostToolUseHookSpecificOutput),
PostToolUseFailure(PostToolUseFailureHookSpecificOutput),
UserPromptSubmit(UserPromptSubmitHookSpecificOutput),
Notification(NotificationHookSpecificOutput),
SubagentStart(SubagentStartHookSpecificOutput),
PermissionRequest(PermissionRequestHookSpecificOutput),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PreToolUseHookSpecificOutput {
pub hook_event_name: String, #[serde(skip_serializing_if = "Option::is_none")]
pub permission_decision: Option<String>, #[serde(skip_serializing_if = "Option::is_none")]
pub permission_decision_reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_input: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_context: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PostToolUseHookSpecificOutput {
pub hook_event_name: String, #[serde(skip_serializing_if = "Option::is_none")]
pub additional_context: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_mcp_tool_output: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PostToolUseFailureHookSpecificOutput {
pub hook_event_name: String, #[serde(skip_serializing_if = "Option::is_none")]
pub additional_context: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UserPromptSubmitHookSpecificOutput {
pub hook_event_name: String, #[serde(skip_serializing_if = "Option::is_none")]
pub additional_context: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NotificationHookSpecificOutput {
pub hook_event_name: String, #[serde(skip_serializing_if = "Option::is_none")]
pub additional_context: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubagentStartHookSpecificOutput {
pub hook_event_name: String, #[serde(skip_serializing_if = "Option::is_none")]
pub additional_context: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PermissionRequestHookSpecificOutput {
pub hook_event_name: String, pub decision: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AsyncHookOutput {
#[serde(rename = "async")]
pub async_: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub async_timeout: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum HookOutput {
Sync(Box<SyncHookOutput>),
Async(AsyncHookOutput),
}
impl Default for HookOutput {
fn default() -> Self {
HookOutput::Sync(Box::default())
}
}