hooks

package
v0.3.29-beta Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 8, 2026 License: MIT Imports: 13 Imported by: 0

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

View Source
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

HandleAfterToolCall returns nil to indicate this handler doesn't implement after_tool_call.

func (*BaseBuiltinHandler) HandleAgentStop

HandleAgentStop returns nil to indicate this handler doesn't implement agent_stop.

func (*BaseBuiltinHandler) HandleBeforeToolCall

HandleBeforeToolCall returns nil to indicate this handler doesn't implement before_tool_call.

func (*BaseBuiltinHandler) HandleTurnEnd

HandleTurnEnd returns nil to indicate this handler doesn't implement turn_end.

func (*BaseBuiltinHandler) HandleUserMessageSend

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

func (d *Discovery) DiscoverHooks() (map[HookType][]*Hook, error)

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

type DiscoveryOption func(*Discovery) error

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

func (m HookManager) Execute(ctx context.Context, hookType HookType, payload any) ([]byte, error)

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

const (
	InvokedByMain     InvokedBy = "main"
	InvokedBySubagent InvokedBy = "subagent"
)

InvokedBy constants indicate the source of hook invocation

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

func (t *Trigger) SetConversationID(id string)

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 TurnEndResult

type TurnEndResult struct {
}

TurnEndResult is returned by turn_end hooks

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL