Add a typed lifecycle hook system inspired by OpenClaw, designed for PicoClaw's ultra-lightweight philosophy. Provides 8 interception points around the agent loop for observability, content filtering, and guardrails. Two execution patterns: - Void hooks (concurrent): message_received, after_tool_call, llm_input, llm_output, session_start, session_end - Modifying hooks (sequential by priority, with cancel): message_sending, before_tool_call Key design choices: - Zero-cost when unused: all triggers check len==0 and return immediately - Copy-on-write registration: insertSorted allocates new backing array so concurrent readers never race with writers - Panic recovery in all handler dispatch paths - sendOutbound wrapper returns cancel status to callers - MessageTool callback rewired via SetHooks for content filtering 15 tests covering execution, priority ordering, cancel semantics, concurrency (barrier-based), panic recovery, and error swallowing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
82 lines
1.9 KiB
Go
82 lines
1.9 KiB
Go
// PicoClaw - Ultra-lightweight personal AI agent
|
|
// Inspired by and based on nanobot: https://github.com/HKUDS/nanobot
|
|
// License: MIT
|
|
//
|
|
// Copyright (c) 2026 PicoClaw contributors
|
|
|
|
package hooks
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/sipeed/picoclaw/pkg/providers"
|
|
"github.com/sipeed/picoclaw/pkg/tools"
|
|
)
|
|
|
|
// MessageReceivedEvent is fired when an inbound message is consumed from the bus.
|
|
type MessageReceivedEvent struct {
|
|
Channel string
|
|
SenderID string
|
|
ChatID string
|
|
Content string
|
|
Media []string
|
|
Metadata map[string]string
|
|
}
|
|
|
|
// MessageSendingEvent is fired before an outbound message is published.
|
|
// Handlers can modify Content or set Cancel to block delivery.
|
|
type MessageSendingEvent struct {
|
|
Channel string
|
|
ChatID string
|
|
Content string // Modifiable
|
|
Cancel bool
|
|
CancelReason string
|
|
}
|
|
|
|
// BeforeToolCallEvent is fired before a tool is executed.
|
|
// Handlers can modify Args, or set Cancel to block execution.
|
|
type BeforeToolCallEvent struct {
|
|
ToolName string
|
|
Args map[string]any // Modifiable
|
|
Channel string
|
|
ChatID string
|
|
Cancel bool
|
|
CancelReason string // Message returned to LLM when canceled
|
|
}
|
|
|
|
// AfterToolCallEvent is fired after a tool completes execution.
|
|
type AfterToolCallEvent struct {
|
|
ToolName string
|
|
Args map[string]any
|
|
Channel string
|
|
ChatID string
|
|
Duration time.Duration
|
|
Result *tools.ToolResult
|
|
}
|
|
|
|
// LLMInputEvent is fired before the LLM provider is called.
|
|
type LLMInputEvent struct {
|
|
AgentID string
|
|
Model string
|
|
Messages []providers.Message
|
|
Tools []providers.ToolDefinition
|
|
Iteration int
|
|
}
|
|
|
|
// LLMOutputEvent is fired after the LLM provider responds.
|
|
type LLMOutputEvent struct {
|
|
AgentID string
|
|
Model string
|
|
Content string
|
|
ToolCalls []providers.ToolCall
|
|
Iteration int
|
|
Duration time.Duration
|
|
}
|
|
|
|
// SessionEvent is fired at session start and end.
|
|
type SessionEvent struct {
|
|
AgentID string
|
|
SessionKey string
|
|
Channel string
|
|
ChatID string
|
|
}
|