Documentation
¶
Overview ¶
Package hooks provides an extensibility mechanism for agent lifecycle events. It allows external executables to observe and intercept tool calls, user messages, and agent lifecycle events for audit logging, security controls, and monitoring.
Index ¶
- Constants
- type AfterToolCallPayload
- type AfterToolCallResult
- type AgentStopPayload
- type AgentStopResult
- type BaseBuiltinHandler
- func (h *BaseBuiltinHandler) HandleAfterToolCall(_ context.Context, _ llmtypes.Thread, _ AfterToolCallPayload) (*AfterToolCallResult, error)
- func (h *BaseBuiltinHandler) HandleAgentStop(_ context.Context, _ llmtypes.Thread, _ AgentStopPayload) (*AgentStopResult, error)
- func (h *BaseBuiltinHandler) HandleBeforeToolCall(_ context.Context, _ llmtypes.Thread, _ BeforeToolCallPayload) (*BeforeToolCallResult, error)
- func (h *BaseBuiltinHandler) HandleTurnEnd(_ context.Context, _ llmtypes.Thread, _ TurnEndPayload) (*TurnEndResult, error)
- func (h *BaseBuiltinHandler) HandleUserMessageSend(_ context.Context, _ llmtypes.Thread, _ UserMessageSendPayload) (*UserMessageSendResult, error)
- type BasePayload
- type BeforeToolCallPayload
- type BeforeToolCallResult
- type BuiltinHandler
- type BuiltinRegistry
- type ContextSwapper
- type Discovery
- type DiscoveryOption
- type Hook
- type HookDirConfig
- type HookManager
- func (m HookManager) AllHooks() []*Hook
- func (m HookManager) Execute(ctx context.Context, hookType HookType, payload any) ([]byte, error)
- func (m HookManager) ExecuteAfterToolCall(ctx context.Context, payload AfterToolCallPayload) (*AfterToolCallResult, error)
- func (m HookManager) ExecuteAgentStop(ctx context.Context, payload AgentStopPayload) (*AgentStopResult, error)
- func (m HookManager) ExecuteBeforeToolCall(ctx context.Context, payload BeforeToolCallPayload) (*BeforeToolCallResult, error)
- func (m HookManager) ExecuteTurnEnd(ctx context.Context, payload TurnEndPayload) (*TurnEndResult, error)
- func (m HookManager) ExecuteUserMessageSend(ctx context.Context, payload UserMessageSendPayload) (*UserMessageSendResult, error)
- func (m HookManager) GetHookByName(name string) *Hook
- func (m HookManager) GetHooks(hookType HookType) []*Hook
- func (m HookManager) HasHooks(hookType HookType) bool
- func (m *HookManager) SetTimeout(timeout time.Duration)
- type HookType
- type InvokedBy
- type SwapContextHandler
- type ToolCallContext
- type Trigger
- func (t *Trigger) SetConversationID(id string)
- func (t Trigger) TriggerAfterToolCall(ctx context.Context, thread llmtypes.Thread, ...) *tooltypes.StructuredToolResult
- func (t Trigger) TriggerAgentStop(ctx context.Context, thread llmtypes.Thread, messages []llmtypes.Message, ...) []string
- func (t Trigger) TriggerBeforeToolCall(ctx context.Context, thread llmtypes.Thread, ...) (bool, string, string)
- func (t Trigger) TriggerTurnEnd(ctx context.Context, thread llmtypes.Thread, response string, turnNumber int, ...)
- func (t Trigger) TriggerUserMessageSend(ctx context.Context, thread llmtypes.Thread, message string, ...) (bool, string)
- type TurnEndPayload
- type TurnEndResult
- type UserMessageSendPayload
- type UserMessageSendResult
Constants ¶
const DefaultTimeout = 30 * time.Second
DefaultTimeout is the default execution timeout for hooks
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AfterToolCallPayload ¶
type AfterToolCallPayload struct {
BasePayload
ToolName string `json:"tool_name"`
ToolInput json.RawMessage `json:"tool_input"`
ToolOutput tooltypes.StructuredToolResult `json:"tool_output"`
ToolUserID string `json:"tool_user_id"`
}
AfterToolCallPayload is sent to after_tool_call hooks
type AfterToolCallResult ¶
type AfterToolCallResult struct {
Output *tooltypes.StructuredToolResult `json:"output,omitempty"`
}
AfterToolCallResult is returned by after_tool_call hooks
type AgentStopPayload ¶
type AgentStopPayload struct {
BasePayload
Messages []llmtypes.Message `json:"messages"`
}
AgentStopPayload is sent to agent_stop hooks
type AgentStopResult ¶
type AgentStopResult struct {
// FollowUpMessages contains optional messages to append to the conversation.
// This enables LLM-based hooks to provide feedback, ask clarifying questions,
// or request additional actions based on the agent's work.
FollowUpMessages []string `json:"follow_up_messages,omitempty"`
}
AgentStopResult is returned by agent_stop hooks
type BaseBuiltinHandler ¶
type BaseBuiltinHandler struct{}
BaseBuiltinHandler provides default nil implementations for all hook methods. Embed this in your handler to only override the hooks you care about. Returning (nil, nil) indicates the handler does not implement that hook type.
func (*BaseBuiltinHandler) HandleAfterToolCall ¶
func (h *BaseBuiltinHandler) HandleAfterToolCall(_ context.Context, _ llmtypes.Thread, _ AfterToolCallPayload) (*AfterToolCallResult, error)
HandleAfterToolCall returns nil to indicate this handler doesn't implement after_tool_call.
func (*BaseBuiltinHandler) HandleAgentStop ¶
func (h *BaseBuiltinHandler) HandleAgentStop(_ context.Context, _ llmtypes.Thread, _ AgentStopPayload) (*AgentStopResult, error)
HandleAgentStop returns nil to indicate this handler doesn't implement agent_stop.
func (*BaseBuiltinHandler) HandleBeforeToolCall ¶
func (h *BaseBuiltinHandler) HandleBeforeToolCall(_ context.Context, _ llmtypes.Thread, _ BeforeToolCallPayload) (*BeforeToolCallResult, error)
HandleBeforeToolCall returns nil to indicate this handler doesn't implement before_tool_call.
func (*BaseBuiltinHandler) HandleTurnEnd ¶
func (h *BaseBuiltinHandler) HandleTurnEnd(_ context.Context, _ llmtypes.Thread, _ TurnEndPayload) (*TurnEndResult, error)
HandleTurnEnd returns nil to indicate this handler doesn't implement turn_end.
func (*BaseBuiltinHandler) HandleUserMessageSend ¶
func (h *BaseBuiltinHandler) HandleUserMessageSend(_ context.Context, _ llmtypes.Thread, _ UserMessageSendPayload) (*UserMessageSendResult, error)
HandleUserMessageSend returns nil to indicate this handler doesn't implement user_message_send.
type BasePayload ¶
type BasePayload struct {
Event HookType `json:"event"`
ConvID string `json:"conv_id"`
CWD string `json:"cwd"`
InvokedBy InvokedBy `json:"invoked_by"`
RecipeName string `json:"recipe_name,omitempty"` // Active recipe name, empty if none
}
BasePayload contains fields common to all hook payloads
type BeforeToolCallPayload ¶
type BeforeToolCallPayload struct {
BasePayload
ToolName string `json:"tool_name"`
ToolInput json.RawMessage `json:"tool_input"`
ToolUserID string `json:"tool_user_id"`
}
BeforeToolCallPayload is sent to before_tool_call hooks
type BeforeToolCallResult ¶
type BeforeToolCallResult struct {
Blocked bool `json:"blocked"`
Reason string `json:"reason,omitempty"`
Input json.RawMessage `json:"input,omitempty"`
}
BeforeToolCallResult is returned by before_tool_call hooks
type BuiltinHandler ¶
type BuiltinHandler interface {
// Name returns the handler identifier used in recipe metadata
Name() string
// HandleBeforeToolCall is called when before_tool_call event fires.
// Returns nil if this handler doesn't implement before_tool_call.
HandleBeforeToolCall(ctx context.Context, thread llmtypes.Thread, payload BeforeToolCallPayload) (*BeforeToolCallResult, error)
// HandleAfterToolCall is called when after_tool_call event fires.
// Returns nil if this handler doesn't implement after_tool_call.
HandleAfterToolCall(ctx context.Context, thread llmtypes.Thread, payload AfterToolCallPayload) (*AfterToolCallResult, error)
// HandleUserMessageSend is called when user_message_send event fires.
// Returns nil if this handler doesn't implement user_message_send.
HandleUserMessageSend(ctx context.Context, thread llmtypes.Thread, payload UserMessageSendPayload) (*UserMessageSendResult, error)
// HandleAgentStop is called when agent_stop event fires.
// Returns nil if this handler doesn't implement agent_stop.
HandleAgentStop(ctx context.Context, thread llmtypes.Thread, payload AgentStopPayload) (*AgentStopResult, error)
// HandleTurnEnd is called when turn_end event fires.
// Returns nil if this handler doesn't implement turn_end.
HandleTurnEnd(ctx context.Context, thread llmtypes.Thread, payload TurnEndPayload) (*TurnEndResult, error)
}
BuiltinHandler defines the interface for built-in hook handlers. These are internal handlers that can be referenced by recipes. Handlers return nil for hook types they don't implement.
type BuiltinRegistry ¶
type BuiltinRegistry struct {
// contains filtered or unexported fields
}
BuiltinRegistry holds registered built-in handlers
func DefaultBuiltinRegistry ¶
func DefaultBuiltinRegistry() *BuiltinRegistry
DefaultBuiltinRegistry returns registry with default handlers
func (*BuiltinRegistry) Get ¶
func (r *BuiltinRegistry) Get(name string) (BuiltinHandler, bool)
Get retrieves a handler by name
func (*BuiltinRegistry) Register ¶
func (r *BuiltinRegistry) Register(h BuiltinHandler)
Register adds a handler to the registry
type ContextSwapper ¶
type ContextSwapper interface {
// SwapContext replaces the current message history with a summary
SwapContext(ctx context.Context, summary string) error
}
ContextSwapper is implemented by threads that support context replacement. This interface is defined in the hooks package to avoid circular dependencies, but is implemented by each provider's thread.
type Discovery ¶
type Discovery struct {
// contains filtered or unexported fields
}
Discovery handles hook discovery from configured directories
func NewDiscovery ¶
func NewDiscovery(opts ...DiscoveryOption) (*Discovery, error)
NewDiscovery creates a new hook discovery instance
func (*Discovery) DiscoverHooks ¶
DiscoverHooks finds all available hooks from configured directories. Hooks from plugin directories are prefixed with "org/repo/" format. Hooks are discovered in precedence order, with higher precedence hooks shadowing lower ones.
type DiscoveryOption ¶
DiscoveryOption is a function that configures a Discovery
func WithDefaultDirs ¶
func WithDefaultDirs() DiscoveryOption
WithDefaultDirs initializes with default hook directories including plugin directories. This provides the full precedence order: repo-local standalone > repo-local plugins > global standalone > global plugins
func WithHookDirConfigs ¶
func WithHookDirConfigs(configs ...HookDirConfig) DiscoveryOption
WithHookDirConfigs sets custom hook directories with prefix support (for testing).
func WithHookDirs ¶
func WithHookDirs(dirs ...string) DiscoveryOption
WithHookDirs sets custom hook directories (for testing). All directories are treated as standalone (no prefix).
type Hook ¶
type Hook struct {
Name string // Filename of the executable
Path string // Full path to the executable
HookType HookType // Type returned by "hook" command
}
Hook represents a discovered hook executable
type HookDirConfig ¶
type HookDirConfig struct {
Dir string // Directory path
Prefix string // Prefix for hook names (e.g., "org/repo/" for plugins)
}
HookDirConfig represents a directory to scan for hooks with optional prefix
type HookManager ¶
type HookManager struct {
// contains filtered or unexported fields
}
HookManager manages hook discovery and execution
func NewHookManager ¶
func NewHookManager(opts ...DiscoveryOption) (HookManager, error)
NewHookManager creates a new HookManager with discovered hooks Returns an empty manager (no-op) if discovery fails
func (HookManager) AllHooks ¶
func (m HookManager) AllHooks() []*Hook
AllHooks returns all discovered hooks regardless of type.
func (HookManager) Execute ¶
Execute runs all hooks of a given type with the provided payload. Returns the result bytes from the last successful hook execution.
func (HookManager) ExecuteAfterToolCall ¶
func (m HookManager) ExecuteAfterToolCall(ctx context.Context, payload AfterToolCallPayload) (*AfterToolCallResult, error)
ExecuteAfterToolCall runs after_tool_call hooks and returns typed result. Empty or nil output with exit code 0 is treated as "no modification".
func (HookManager) ExecuteAgentStop ¶
func (m HookManager) ExecuteAgentStop(ctx context.Context, payload AgentStopPayload) (*AgentStopResult, error)
ExecuteAgentStop runs agent_stop hooks and returns typed result. Empty or nil output with exit code 0 is treated as "no follow-up". Accumulates follow_up_messages from all hooks.
func (HookManager) ExecuteBeforeToolCall ¶
func (m HookManager) ExecuteBeforeToolCall(ctx context.Context, payload BeforeToolCallPayload) (*BeforeToolCallResult, error)
ExecuteBeforeToolCall runs before_tool_call hooks and returns typed result. Empty or nil output with exit code 0 is treated as "no action" (not blocked, no modification). Uses deny-fast semantics: if any hook blocks, execution stops immediately.
func (HookManager) ExecuteTurnEnd ¶
func (m HookManager) ExecuteTurnEnd(ctx context.Context, payload TurnEndPayload) (*TurnEndResult, error)
ExecuteTurnEnd runs turn_end hooks. Empty or nil output with exit code 0 is treated as "no action".
func (HookManager) ExecuteUserMessageSend ¶
func (m HookManager) ExecuteUserMessageSend(ctx context.Context, payload UserMessageSendPayload) (*UserMessageSendResult, error)
ExecuteUserMessageSend runs user_message_send hooks and returns typed result. Empty or nil output with exit code 0 is treated as "no action" (not blocked). Uses deny-fast semantics: if any hook blocks, execution stops immediately.
func (HookManager) GetHookByName ¶
func (m HookManager) GetHookByName(name string) *Hook
GetHookByName returns a hook by its name, searching across all hook types. Returns nil if the hook is not found.
func (HookManager) GetHooks ¶
func (m HookManager) GetHooks(hookType HookType) []*Hook
GetHooks returns all hooks registered for the given type
func (HookManager) HasHooks ¶
func (m HookManager) HasHooks(hookType HookType) bool
HasHooks returns true if there are any hooks registered for the given type
func (*HookManager) SetTimeout ¶
func (m *HookManager) SetTimeout(timeout time.Duration)
SetTimeout sets the execution timeout for hooks
type HookType ¶
type HookType string
HookType represents the type of lifecycle hook
const ( HookTypeBeforeToolCall HookType = "before_tool_call" HookTypeAfterToolCall HookType = "after_tool_call" HookTypeUserMessageSend HookType = "user_message_send" HookTypeAgentStop HookType = "agent_stop" HookTypeTurnEnd HookType = "turn_end" )
Hook type constants define the lifecycle events that can be hooked
type InvokedBy ¶
type InvokedBy string
InvokedBy indicates whether the hook was triggered by main agent or subagent
type SwapContextHandler ¶
type SwapContextHandler struct {
BaseBuiltinHandler
}
SwapContextHandler replaces thread messages with the provided summary. It embeds BaseBuiltinHandler to get nil implementations for unused hooks.
func (*SwapContextHandler) HandleTurnEnd ¶
func (h *SwapContextHandler) HandleTurnEnd(ctx context.Context, thread llmtypes.Thread, payload TurnEndPayload) (*TurnEndResult, error)
HandleTurnEnd is called when turn_end event fires to swap the conversation context
func (*SwapContextHandler) Name ¶
func (h *SwapContextHandler) Name() string
Name returns the handler identifier used in recipe metadata
type ToolCallContext ¶
type ToolCallContext struct {
ToolName string
ToolInput string
ToolUserID string
ToolOutput *tooltypes.StructuredToolResult // Only for after_tool_call
}
ToolCallContext provides context for tool call hooks
type Trigger ¶
type Trigger struct {
Manager HookManager
ConversationID string
IsSubAgent bool
WorkingDir string
RecipeName string // Active recipe name, empty if none
}
Trigger provides methods to invoke lifecycle hooks. It encapsulates the common hook triggering logic shared across LLM providers. A zero-value Trigger is safe to use and acts as a no-op.
func NewTrigger ¶
func NewTrigger(manager HookManager, conversationID string, isSubAgent bool, args ...string) Trigger
func (*Trigger) SetConversationID ¶
SetConversationID updates the conversation ID for the trigger. This is useful when the conversation ID is set after thread creation.
func (Trigger) TriggerAfterToolCall ¶
func (t Trigger) TriggerAfterToolCall(ctx context.Context, thread llmtypes.Thread, toolName, toolInput, toolUserID string, toolOutput tooltypes.StructuredToolResult, recipeHooks map[string]llmtypes.HookConfig) *tooltypes.StructuredToolResult
TriggerAfterToolCall invokes after_tool_call hooks including built-in handlers. The thread parameter is passed to built-in handlers that need to modify thread state. The recipeHooks parameter contains hook configurations from recipe metadata. Returns modified output or nil to use original. A zero-value Trigger returns nil.
func (Trigger) TriggerAgentStop ¶
func (t Trigger) TriggerAgentStop(ctx context.Context, thread llmtypes.Thread, messages []llmtypes.Message, recipeHooks map[string]llmtypes.HookConfig) []string
TriggerAgentStop invokes agent_stop hooks including built-in handlers. The thread parameter is passed to built-in handlers that need to modify thread state. The recipeHooks parameter contains hook configurations from recipe metadata. Returns follow-up messages that can be appended to the conversation. A zero-value Trigger returns nil.
func (Trigger) TriggerBeforeToolCall ¶
func (t Trigger) TriggerBeforeToolCall(ctx context.Context, thread llmtypes.Thread, toolName, toolInput, toolUserID string, recipeHooks map[string]llmtypes.HookConfig) (bool, string, string)
TriggerBeforeToolCall invokes before_tool_call hooks including built-in handlers. The thread parameter is passed to built-in handlers that need to modify thread state. The recipeHooks parameter contains hook configurations from recipe metadata. Returns (blocked, reason, input) - input is the potentially modified tool input. A zero-value Trigger returns (false, "", toolInput).
func (Trigger) TriggerTurnEnd ¶
func (t Trigger) TriggerTurnEnd(ctx context.Context, thread llmtypes.Thread, response string, turnNumber int, recipeHooks map[string]llmtypes.HookConfig)
TriggerTurnEnd invokes turn_end hooks including built-in handlers. The recipeHooks parameter contains hook configurations from recipe metadata. The thread parameter is passed to built-in handlers that need to modify thread state. A zero-value Trigger is a no-op.
func (Trigger) TriggerUserMessageSend ¶
func (t Trigger) TriggerUserMessageSend(ctx context.Context, thread llmtypes.Thread, message string, recipeHooks map[string]llmtypes.HookConfig) (bool, string)
TriggerUserMessageSend invokes user_message_send hooks including built-in handlers. The thread parameter is passed to built-in handlers that need to modify thread state. The recipeHooks parameter contains hook configurations from recipe metadata. Returns (blocked, reason). A zero-value Trigger returns (false, "").
type TurnEndPayload ¶
type TurnEndPayload struct {
BasePayload
Response string `json:"response"` // The assistant's response text
TurnNumber int `json:"turn_number"` // Which turn in the conversation (1-indexed)
}
TurnEndPayload is sent when an assistant turn completes
type UserMessageSendPayload ¶
type UserMessageSendPayload struct {
BasePayload
Message string `json:"message"`
}
UserMessageSendPayload is sent to user_message_send hooks
type UserMessageSendResult ¶
type UserMessageSendResult struct {
Blocked bool `json:"blocked"`
Reason string `json:"reason,omitempty"`
}
UserMessageSendResult is returned by user_message_send hooks