-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Open
Labels
domain: agentstatus: discussionUnder discussion, not ready for implementationUnder discussion, not ready for implementationtype: enhancementNew feature or requestNew feature or request
Description
Feature: Event-driven Hooks System
Background
OpenClaw has a powerful Hooks system that automatically triggers user-defined code when specific events occur (e.g., message sent, command executed).
Problem: PicoClaw currently only supports active invocation (Skills) and scheduled triggers (Cron), but not event-driven automation.
Design Goals
- Decouple core code from extension functionality
- Support user-defined hooks
- Lightweight, following PicoClaw's simplicity principle
- Pluggable (enable/disable)
Minimal Design
1. Core Interface
// pkg/hooks/hooks.go
package hooks
import "time"
// Event represents a hook event
type Event struct {
Type string // Event type, e.g., "message:sent"
Action string // Specific action, e.g., "sent"
Timestamp time.Time // When the event occurred
Context map[string]interface{} // Event context
}
// Handler is the hook handler function
type Handler func(event Event) error
// Manager manages hooks
type Manager struct {
handlers map[string][]Handler
enabled map[string]bool
}
// NewManager creates a hook manager
func NewManager() *Manager {
return &Manager{
handlers: make(map[string][]Handler),
enabled: make(map[string]bool),
}
}
// On registers a hook handler
func (m *Manager) On(eventType string, handler Handler) {
m.handlers[eventType] = append(m.handlers[eventType], handler)
}
// Emit triggers an event
func (m *Manager) Emit(event Event) {
handlers, ok := m.handlers[event.Type]
if !ok {
return
}
for _, h := range handlers {
go func(handler Handler) {
// Execute asynchronously, don't block main flow
if err := handler(event); err != nil {
log.Printf("[hooks] handler error: %v", err)
}
}(h)
}
}
// Enable enables a hook
func (m *Manager) Enable(eventType string) {
m.enabled[eventType] = true
}
// Disable disables a hook
func (m *Manager) Disable(eventType string) {
m.enabled[eventType] = false
}2. Predefined Event Types
// pkg/hooks/events.go
package hooks
const (
// Message events
EventMessageReceived = "message:received" // Message received
EventMessageSent = "message:sent" // Message sent
// Command events
EventCommandNew = "command:new" // /new command
EventCommandReset = "command:reset" // /reset command
EventCommandStop = "command:stop" // /stop command
// Task events
EventTaskCreated = "task:created" // Task created
EventTaskCompleted = "task:completed" // Task completed
// Skill events
EventSkillLoaded = "skill:loaded" // Skill loaded
EventSkillExecuted = "skill:executed" // Skill executed
// Lifecycle events
EventGatewayStartup = "gateway:startup" // Gateway started
EventSessionStart = "session:start" // Session started
)3. Integration Example
// Trigger hook when Agent sends a message
func (a *Agent) SendMessage(content string) error {
// 1. Send message
err := a.channel.Send(content)
if err != nil {
return err
}
// 2. Trigger post-response hook
a.hooks.Emit(hooks.Event{
Type: hooks.EventMessageSent,
Action: "sent",
Timestamp: time.Now(),
Context: map[string]interface{}{
"content": content,
"channel": a.channel.Name(),
"recipient": a.recipient,
},
})
return nil
}4. User-defined Hook
// hooks/log-messages/hook.go
package main
import (
"encoding/json"
"os"
"github.com/sipeed/picoclaw/pkg/hooks"
)
func init() {
// Auto-register hook
hooks.Register("message-logger", hooks.EventMessageSent, logMessage)
}
func logMessage(event hooks.Event) error {
entry := map[string]interface{}{
"timestamp": event.Timestamp,
"content": event.Context["content"],
"channel": event.Context["channel"],
}
data, _ := json.Marshal(entry)
f, _ := os.OpenFile("messages.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
defer f.Close()
f.WriteString(string(data) + "\n")
return nil
}Configuration
{
"hooks": {
"enabled": true,
"entries": {
"message-logger": {
"enabled": true,
"events": ["message:sent"]
},
"auto-backup": {
"enabled": false,
"events": ["task:completed"]
}
}
}
}Implementation Priority
| Phase | Content | Effort |
|---|---|---|
| P0 | Core interface + 2-3 events | 1-2 days |
| P1 | Configuration file support | 1 day |
| P2 | CLI commands (list/enable/disable) | 1 day |
| P3 | Hook package discovery and loading | 2-3 days |
Discussion
Question 1: Do we need Hooks?
PicoClaw is positioned as a lightweight assistant. Hooks add complexity.
Pros:
- Decouple core code from extensions
- Support user-defined automation
- Competitive with OpenClaw, etc.
Cons:
- Increases code complexity
- Might not be needed (YAGNI principle)
- Skills system might be sufficient
Question 2: If implemented, what scope?
Minimal: Only support 3-5 core events, configuration file management
Full: Reference OpenClaw's complete implementation (auto-discovery, hook packages, CLI management)
Question 3: Relationship with Skills?
- Skills: Active invocation, user explicitly triggers
- Hooks: Passive response, system automatically triggers
Is this needed? Or is Skills + Cron enough?
References
- OpenClaw Hooks docs: https://docs.openclaw.ai/automation/hooks
- Git Hooks design
- React Hooks concept
Please vote:
- 👍 Support implementing Hooks
- 👎 Not needed for now
- 🤔 Need more discussion
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
domain: agentstatus: discussionUnder discussion, not ready for implementationUnder discussion, not ready for implementationtype: enhancementNew feature or requestNew feature or request