package plugins import ( "context" "encoding/json" "sync" "github.com/muyue/muyue/internal/agent" ) type HookType string const ( BeforeToolCall HookType = "before_tool_call" AfterToolCall HookType = "after_tool_call" OnConversationStart HookType = "on_conversation_start" OnToolError HookType = "on_tool_error" ) type HookFunc func(ctx context.Context, payload HookPayload) error type HookPayload struct { ToolName string `json:"tool_name"` Arguments json.RawMessage `json:"arguments,omitempty"` Response *agent.ToolResponse `json:"response,omitempty"` Error string `json:"error,omitempty"` Metadata map[string]interface{} `json:"metadata,omitempty"` } type Hook struct { Type HookType Plugin string Priority int Fn HookFunc } type HookRegistry struct { mu sync.RWMutex hooks map[HookType][]Hook } func NewHookRegistry() *HookRegistry { return &HookRegistry{ hooks: make(map[HookType][]Hook), } } func (hr *HookRegistry) Register(hookType HookType, pluginName string, priority int, fn HookFunc) { hr.mu.Lock() defer hr.mu.Unlock() h := Hook{ Type: hookType, Plugin: pluginName, Priority: priority, Fn: fn, } hr.hooks[hookType] = append(hr.hooks[hookType], h) for i := len(hr.hooks[hookType]) - 1; i > 0; i-- { if hr.hooks[hookType][i].Priority < hr.hooks[hookType][i-1].Priority { hr.hooks[hookType][i], hr.hooks[hookType][i-1] = hr.hooks[hookType][i-1], hr.hooks[hookType][i] } } } func (hr *HookRegistry) Fire(ctx context.Context, hookType HookType, payload HookPayload) error { hr.mu.RLock() hooks := make([]Hook, len(hr.hooks[hookType])) copy(hooks, hr.hooks[hookType]) hr.mu.RUnlock() for _, h := range hooks { if err := h.Fn(ctx, payload); err != nil { return err } } return nil } func (hr *HookRegistry) RemoveByPlugin(pluginName string) { hr.mu.Lock() defer hr.mu.Unlock() for hookType := range hr.hooks { filtered := make([]Hook, 0, len(hr.hooks[hookType])) for _, h := range hr.hooks[hookType] { if h.Plugin != pluginName { filtered = append(filtered, h) } } hr.hooks[hookType] = filtered } }