yao/agent/autonomous/TECHNICAL.md
Max f6b56ac6cc Refactor Technical Documentation to Remove Redundant Sections and Streamline Content
- Removed extensive sections on Manager and Executor implementations to focus on high-level concepts and integration points.
- Consolidated error handling and global singleton sections for clarity and brevity.
- Updated the structure of the document to enhance readability and ensure a more cohesive presentation of the autonomous agent's functionality.
2026-01-14 09:46:56 +08:00

33 KiB

Autonomous Agent - Technical Design

1. Code Structure

yao/agent/autonomous/
├── DESIGN.md                 # Product design doc
├── TECHNICAL.md              # This file
│
├── autonomous.go             # Package entry, Init(), Shutdown()
│
├── api/                      # All API forms
│   ├── api.go                # Go API (facade)
│   ├── process.go            # Yao Process: autonomous.*
│   └── jsapi.go              # JS API for scripts
│
├── types/                    # Types only (no logic, no external deps)
│   ├── enums.go              # Phase, ClockMode, TriggerType, etc.
│   ├── config.go             # Config, Clock, Identity, Quota, etc.
│   ├── robot.go              # Robot, Execution
│   ├── task.go               # Goal, Task, TaskResult
│   ├── request.go            # InterveneRequest, EventRequest, etc.
│   ├── inspiration.go        # ClockContext, InspirationReport
│   ├── interfaces.go         # All interfaces (Manager, Trigger, etc.)
│   └── errors.go             # Error definitions
│
├── manager/                  # Manager package (orchestration)
│   ├── manager.go            # Manager struct, Start/Stop, ticker loop
│   └── lifecycle.go          # OnRobotCreate/Delete/Update
│
├── pool/                     # Worker pool & task dispatch
│   ├── pool.go               # Pool struct, Submit
│   ├── queue.go              # Priority queue
│   └── worker.go             # Worker goroutines
│
├── executor/                 # Executor package
│   ├── executor.go           # Executor struct, Execute
│   ├── phase.go              # RunPhase dispatcher
│   ├── inspiration.go        # P0: Inspiration (clock only)
│   ├── goals.go              # P1: Goal generation
│   ├── tasks.go              # P2: Task planning
│   ├── run.go                # P3: Task execution
│   ├── delivery.go           # P4: Delivery
│   ├── learning.go           # P5: Learning
│   ├── agent.go              # Call assistant/agent unified method
│   └── prompt.go             # Prompt building helpers
│
├── utils/                    # Utility functions
│   ├── convert.go            # Type conversions (JSON, map, struct)
│   ├── time.go               # Time parsing, formatting, timezone
│   ├── id.go                 # ID generation (nanoid, uuid)
│   └── validate.go           # Validation helpers
│
├── trigger/                  # All trigger sources
│   ├── trigger.go            # Trigger interface & dispatcher
│   ├── clock.go              # Clock trigger (tick, schedule matching)
│   ├── intervene.go          # Human intervention trigger
│   ├── event.go              # Event trigger (webhook, db change)
│   └── control.go            # Pause/Resume/Cancel
│
├── cache/                    # Cache package
│   ├── cache.go              # Cache struct, Get/List
│   ├── load.go               # LoadAll, LoadOne
│   └── refresh.go            # Refresh logic
│
├── dedup/                    # Deduplication package
│   ├── dedup.go              # Dedup struct
│   ├── fast.go               # Fast in-memory check
│   └── semantic.go           # Semantic check via agent
│
├── store/                    # Data store package (KB, FS, DB access)
│   ├── store.go              # Store struct, interface
│   ├── kb.go                 # Knowledge base operations
│   ├── fs.go                 # File system operations
│   ├── db.go                 # Database queries
│   └── learning.go           # Learning entry save (to KB)
│
├── job/                      # Job system integration
│   ├── job.go                # Create/Get job for robot
│   ├── execution.go          # Create/Update execution
│   └── log.go                # Write execution logs
│
└── plan/                     # Plan queue (deferred tasks)
    ├── plan.go               # Plan queue struct
    └── schedule.go           # Schedule for later

Dependency Graph (No Cycles)

                              ┌──────────┐
                              │  types/  │  (pure types, no deps)
                              └────┬─────┘
                                   │
    ┌───────┬───────┬───────┬──────┼──────┬───────┬───────┬───────┐
    │       │       │       │      │      │       │       │       │
    ▼       ▼       ▼       ▼      ▼      ▼       ▼       ▼       ▼
┌───────┐┌───────┐┌───────┐┌──────┐┌────┐┌──────┐┌───────┐
│ cache ││ dedup ││ store ││ pool ││job ││ plan ││ utils │
└───┬───┘└───┬───┘└───┬───┘└──┬───┘└──┬─┘└──────┘└───────┘
    │        │        │       │       │
    └────────┴────────┴───────┴───────┘
                      │
       ┌──────────────┴──────────────┐
       │                             │
       ▼                             ▼
┌────────────┐                ┌────────────┐
│  trigger/  │                │  executor/ │
└──────┬─────┘                └──────┬─────┘
       │                             │
       └──────────────┬──────────────┘
                      │
                      ▼
               ┌────────────┐
               │  manager/  │
               └──────┬─────┘
                      │
       ┌──────────────┴──────────────┐
       │                             │
       ▼                             ▼
┌─────────────┐              ┌─────────────┐
│autonomous.go│              │    api/     │
└─────────────┘              └─────────────┘

Package Dependencies

Package Imports
types/ stdlib only
utils/ stdlib only
cache/ types/
dedup/ types/
store/ types/
pool/ types/
trigger/ types/, cache/
job/ types/, yao/job
plan/ types/
executor/ types/, cache/, dedup/, store/, pool/, job/
manager/ types/, cache/, pool/, trigger/, executor/
api/ types/, manager/, trigger/
root all packages

Public API (api/)

三种 API 形态,统一放在 api/ 目录下:

Go API (api/api.go)

package api

// Robot Management
func GetRobot(memberID string) (*types.Robot, error)
func ListRobots(teamID string) ([]*types.Robot, error)
func RefreshRobot(memberID string) error

// Triggers
func Intervene(req *types.InterveneRequest) (*types.ExecutionResult, error)
func HandleEvent(req *types.EventRequest) (*types.ExecutionResult, error)

// Control
func GetStatus(memberID string) (*types.RobotState, error)
func Pause(memberID string) error
func Resume(memberID string) error
func Cancel(memberID, executionID string) error

// Query
func GetExecution(executionID string) (*types.Execution, error)
func ListExecutions(memberID string, limit int) ([]*types.Execution, error)

Yao Process (api/process.go)

// autonomous.GetRobot(memberID) -> Robot
// autonomous.ListRobots(teamID) -> []Robot
// autonomous.Intervene(teamID, memberID, action, description, priority) -> ExecutionResult
// autonomous.HandleEvent(memberID, source, eventType, data) -> ExecutionResult
// autonomous.GetStatus(memberID) -> RobotState
// autonomous.Pause(memberID) -> bool
// autonomous.Resume(memberID) -> bool
// autonomous.Cancel(memberID, executionID) -> bool
// autonomous.GetExecution(executionID) -> Execution
// autonomous.ListExecutions(memberID, limit) -> []Execution

JS API (api/jsapi.go)

// In Yao scripts
const robot = $autonomous.GetRobot("member_123");
const result = $autonomous.Intervene({
  member_id: "member_123",
  action: "add_task",
  description: "Prepare report",
});
const status = $autonomous.GetStatus("member_123");

2. Type Definitions

All types are in autonomous/types/ package. Other files import as:

import "github.com/yaoapp/yao/agent/autonomous/types"

2.1 Enums

// types/enums.go
package types

// Phase - execution phase
type Phase string

const (
    PhaseInspiration Phase = "inspiration" // P0: Clock only
    PhaseGoals       Phase = "goals"       // P1
    PhaseTasks       Phase = "tasks"       // P2
    PhaseRun         Phase = "run"         // P3
    PhaseDelivery    Phase = "delivery"    // P4
    PhaseLearning    Phase = "learning"    // P5
)

// AllPhases for iteration
var AllPhases = []Phase{
    PhaseInspiration, PhaseGoals, PhaseTasks,
    PhaseRun, PhaseDelivery, PhaseLearning,
}

// ClockMode - clock trigger mode
type ClockMode string

const (
    ClockTimes    ClockMode = "times"    // run at specific times
    ClockInterval ClockMode = "interval" // run every X duration
    ClockDaemon   ClockMode = "daemon"   // run continuously
)

// TriggerType - trigger source
type TriggerType string

const (
    TriggerClock TriggerType = "clock"
    TriggerHuman TriggerType = "human"
    TriggerEvent TriggerType = "event"
)

// ExecStatus - execution status
type ExecStatus string

const (
    ExecPending   ExecStatus = "pending"
    ExecRunning   ExecStatus = "running"
    ExecCompleted ExecStatus = "completed"
    ExecFailed    ExecStatus = "failed"
    ExecCancelled ExecStatus = "cancelled"
)

// RobotStatus - matches __yao.member.robot_status
type RobotStatus string

const (
    RobotIdle        RobotStatus = "idle"
    RobotWorking     RobotStatus = "working"
    RobotPaused      RobotStatus = "paused"
    RobotError       RobotStatus = "error"
    RobotMaintenance RobotStatus = "maintenance"
)

// DeliveryType - output delivery type
type DeliveryType string

const (
    DeliveryEmail   DeliveryType = "email"
    DeliveryFile    DeliveryType = "file"
    DeliveryWebhook DeliveryType = "webhook"
    DeliveryNotify  DeliveryType = "notify"
)

// DedupResult - deduplication result
type DedupResult string

const (
    DedupSkip    DedupResult = "skip"    // skip execution
    DedupMerge   DedupResult = "merge"   // merge with existing
    DedupProceed DedupResult = "proceed" // proceed normally
)

// InterventionAction - human intervention actions
type InterventionAction string

const (
    ActionAddTask    InterventionAction = "add_task"
    ActionAdjustGoal InterventionAction = "adjust_goal"
    ActionCancelTask InterventionAction = "cancel_task"
    ActionPause      InterventionAction = "pause"
    ActionResume     InterventionAction = "resume"
    ActionAbort      InterventionAction = "abort"
    ActionPlan       InterventionAction = "plan"
)

// Priority levels
type Priority string

const (
    PriorityHigh   Priority = "high"
    PriorityNormal Priority = "normal"
    PriorityLow    Priority = "low"
)

2.2 Config Types

// types/config.go
package types

import "time"

// Config - robot_config in __yao.member
type Config struct {
    Triggers  *Triggers  `json:"triggers,omitempty"`
    Clock     *Clock     `json:"clock,omitempty"`
    Identity  *Identity  `json:"identity"`
    Quota     *Quota     `json:"quota,omitempty"`
    PrivateKB *KBConfig  `json:"private_kb,omitempty"`
    SharedKB  *KBConfig  `json:"shared_kb,omitempty"`
    Resources *Resources `json:"resources,omitempty"`
    Delivery  *Delivery  `json:"delivery,omitempty"`
    Events    []Event    `json:"events,omitempty"`
    Monitor   *Monitor   `json:"monitor,omitempty"`
}

// Validate validates the config
func (c *Config) Validate() error {
    if c.Identity == nil || c.Identity.Role == "" {
        return ErrMissingIdentity
    }
    if c.Clock != nil {
        if err := c.Clock.Validate(); err != nil {
            return err
        }
    }
    return nil
}

// Triggers - trigger enable/disable
type Triggers struct {
    Clock     *TriggerSwitch `json:"clock,omitempty"`
    Intervene *TriggerSwitch `json:"intervene,omitempty"`
    Event     *TriggerSwitch `json:"event,omitempty"`
}

type TriggerSwitch struct {
    Enabled bool     `json:"enabled"`
    Actions []string `json:"actions,omitempty"` // for intervene
}

// IsEnabled checks if trigger is enabled (default: true)
func (t *Triggers) IsEnabled(typ TriggerType) bool {
    if t == nil {
        return true
    }
    switch typ {
    case TriggerClock:
        return t.Clock == nil || t.Clock.Enabled
    case TriggerHuman:
        return t.Intervene == nil || t.Intervene.Enabled
    case TriggerEvent:
        return t.Event == nil || t.Event.Enabled
    }
    return false
}

// Clock - when to wake up
type Clock struct {
    Mode    ClockMode `json:"mode"`              // times | interval | daemon
    Times   []string  `json:"times,omitempty"`   // ["09:00", "14:00"]
    Days    []string  `json:"days,omitempty"`    // ["Mon", "Tue"] or ["*"]
    Every   string    `json:"every,omitempty"`   // "30m", "1h"
    TZ      string    `json:"tz,omitempty"`      // "Asia/Shanghai"
    Timeout string    `json:"timeout,omitempty"` // "30m"
}

// Validate validates clock config
func (c *Clock) Validate() error {
    switch c.Mode {
    case ClockTimes:
        if len(c.Times) == 0 {
            return ErrClockTimesEmpty
        }
    case ClockInterval:
        if c.Every == "" {
            return ErrClockIntervalEmpty
        }
    case ClockDaemon:
        // no extra validation
    default:
        return ErrClockModeInvalid
    }
    return nil
}

// GetTimeout returns parsed timeout duration
func (c *Clock) GetTimeout() time.Duration {
    if c.Timeout == "" {
        return 30 * time.Minute // default
    }
    d, err := time.ParseDuration(c.Timeout)
    if err != nil {
        return 30 * time.Minute
    }
    return d
}

// GetLocation returns timezone location
func (c *Clock) GetLocation() *time.Location {
    if c.TZ == "" {
        return time.Local
    }
    loc, err := time.LoadLocation(c.TZ)
    if err != nil {
        return time.Local
    }
    return loc
}

// Identity - who is this robot
type Identity struct {
    Role   string   `json:"role"`
    Duties []string `json:"duties,omitempty"`
    Rules  []string `json:"rules,omitempty"`
}

// Quota - concurrency limits
type Quota struct {
    Max      int `json:"max"`      // max running (default: 2)
    Queue    int `json:"queue"`    // queue size (default: 10)
    Priority int `json:"priority"` // 1-10 (default: 5)
}

// GetMax returns max with default
func (q *Quota) GetMax() int {
    if q == nil || q.Max <= 0 {
        return 2
    }
    return q.Max
}

// GetQueue returns queue size with default
func (q *Quota) GetQueue() int {
    if q == nil || q.Queue <= 0 {
        return 10
    }
    return q.Queue
}

// GetPriority returns priority with default
func (q *Quota) GetPriority() int {
    if q == nil || q.Priority <= 0 {
        return 5
    }
    return q.Priority
}

// KBConfig - knowledge base config
type KBConfig struct {
    ID    string   `json:"id,omitempty"`
    Refs  []string `json:"refs,omitempty"`
    Learn *Learn   `json:"learn,omitempty"`
}

type Learn struct {
    On    bool     `json:"on"`
    Types []string `json:"types,omitempty"` // execution, feedback, insight
    Keep  int      `json:"keep,omitempty"`  // days, 0 = forever
}

// Resources - available agents and tools
type Resources struct {
    Phases map[Phase]string `json:"phases,omitempty"` // phase -> agent ID
    Agents []string         `json:"agents,omitempty"`
    MCP    []MCPConfig      `json:"mcp,omitempty"`
}

// GetPhaseAgent returns agent ID for phase (default: __yao.{phase})
func (r *Resources) GetPhaseAgent(phase Phase) string {
    if r != nil && r.Phases != nil {
        if id, ok := r.Phases[phase]; ok && id != "" {
            return id
        }
    }
    return "__yao." + string(phase)
}

type MCPConfig struct {
    ID    string   `json:"id"`
    Tools []string `json:"tools,omitempty"` // empty = all
}

// Delivery - output delivery
type Delivery struct {
    Type DeliveryType           `json:"type"`
    Opts map[string]interface{} `json:"opts,omitempty"`
}

// Event - event trigger config
type Event struct {
    Type   string                 `json:"type"`   // webhook | database
    Source string                 `json:"source"` // path or table
    Filter map[string]interface{} `json:"filter,omitempty"`
}

// Monitor - monitoring config
type Monitor struct {
    On     bool    `json:"on"`
    Alerts []Alert `json:"alerts,omitempty"`
}

type Alert struct {
    Name     string   `json:"name"`
    When     string   `json:"when"`  // failed | timeout | error_rate
    Value    float64  `json:"value,omitempty"`
    Window   string   `json:"window,omitempty"` // 1h | 24h
    Do       []Action `json:"do"`
    Cooldown string   `json:"cooldown,omitempty"`
}

type Action struct {
    Type string                 `json:"type"` // email | webhook | notify
    Opts map[string]interface{} `json:"opts,omitempty"`
}

2.3 Core Types

// types/robot.go
package types

import (
    "context"
    "sync"
    "time"
)

// Robot - runtime representation of an autonomous robot
type Robot struct {
    // From __yao.member
    MemberID       string      `json:"member_id"`
    TeamID         string      `json:"team_id"`
    DisplayName    string      `json:"display_name"`
    SystemPrompt   string      `json:"system_prompt"`
    Status         RobotStatus `json:"robot_status"`
    AutonomousMode bool        `json:"autonomous_mode"`

    // Parsed config
    Config *Config `json:"-"`

    // Runtime state (job.Job stored as interface{} to avoid import cycle)
    Job           interface{} `json:"-"` // *job.Job, set by manager
    JobID         string      `json:"-"` // job_id for quick access
    LastExecution time.Time   `json:"-"`
    NextExecution time.Time   `json:"-"`

    // Concurrency control
    running   int
    runningMu sync.Mutex
}

// CanRun checks if robot can accept new execution
func (r *Robot) CanRun() bool {
    r.runningMu.Lock()
    defer r.runningMu.Unlock()
    return r.running < r.Config.Quota.GetMax()
}

// IncrRunning increments running count
func (r *Robot) IncrRunning() {
    r.runningMu.Lock()
    defer r.runningMu.Unlock()
    r.running++
}

// DecrRunning decrements running count
func (r *Robot) DecrRunning() {
    r.runningMu.Lock()
    defer r.runningMu.Unlock()
    if r.running > 0 {
        r.running--
    }
}

// Execution - single execution context
type Execution struct {
    ID          string      `json:"id"`
    MemberID    string      `json:"member_id"`
    TeamID      string      `json:"team_id"`
    TriggerType TriggerType `json:"trigger_type"`
    TriggerData interface{} `json:"trigger_data,omitempty"`
    StartTime   time.Time   `json:"start_time"`
    EndTime     *time.Time  `json:"end_time,omitempty"`
    Status      ExecStatus  `json:"status"`
    Phase       Phase       `json:"phase"`
    Error       string      `json:"error,omitempty"`

    // Phase outputs
    Inspiration *InspirationReport `json:"inspiration,omitempty"`
    Goals       []Goal             `json:"goals,omitempty"`
    Tasks       []Task             `json:"tasks,omitempty"`
    Results     []TaskResult       `json:"results,omitempty"`
    Delivery    *DeliveryResult    `json:"delivery,omitempty"`
    Learning    []LearningEntry    `json:"learning,omitempty"`

    // Context
    ctx    context.Context
    cancel context.CancelFunc
    robot  *Robot
}

// Goal - generated goal
type Goal struct {
    ID          string   `json:"id"`
    Description string   `json:"description"`
    Priority    Priority `json:"priority"`
    Rationale   string   `json:"rationale,omitempty"`
    Tags        []string `json:"tags,omitempty"`
}

// Task - planned task
type Task struct {
    ID           string     `json:"id"`
    GoalID       string     `json:"goal_id"`
    Description  string     `json:"description"`
    ExecutorType string     `json:"executor_type"` // "assistant" | "mcp"
    ExecutorID   string     `json:"executor_id"`
    Args         []any      `json:"args,omitempty"`
    Status       ExecStatus `json:"status"`
    Order        int        `json:"order"`
}

// TaskResult - task execution result
type TaskResult struct {
    TaskID    string      `json:"task_id"`
    Success   bool        `json:"success"`
    Output    interface{} `json:"output,omitempty"`
    Error     string      `json:"error,omitempty"`
    Duration  int64       `json:"duration_ms"`
    Validated bool        `json:"validated"`
}

// DeliveryResult - delivery output
type DeliveryResult struct {
    Type    DeliveryType `json:"type"`
    Success bool         `json:"success"`
    Details interface{}  `json:"details,omitempty"`
    Error   string       `json:"error,omitempty"`
}

// LearningEntry - knowledge to save
type LearningEntry struct {
    Type    string      `json:"type"` // execution | feedback | insight
    Content string      `json:"content"`
    Tags    []string    `json:"tags,omitempty"`
    Meta    interface{} `json:"meta,omitempty"`
}

2.4 Clock Context

// types/clock.go
package types

import "time"

// ClockContext - time context for P0 inspiration
type ClockContext struct {
    Now          time.Time `json:"now"`
    Hour         int       `json:"hour"`          // 0-23
    DayOfWeek    string    `json:"day_of_week"`   // Monday, Tuesday...
    DayOfMonth   int       `json:"day_of_month"`  // 1-31
    WeekOfYear   int       `json:"week_of_year"`  // 1-52
    Month        int       `json:"month"`         // 1-12
    Year         int       `json:"year"`
    IsWeekend    bool      `json:"is_weekend"`
    IsMonthStart bool      `json:"is_month_start"` // 1st-3rd
    IsMonthEnd   bool      `json:"is_month_end"`   // last 3 days
    IsQuarterEnd bool      `json:"is_quarter_end"`
    IsYearEnd    bool      `json:"is_year_end"`
    TZ           string    `json:"tz"`
}

// NewClockContext creates clock context from time
func NewClockContext(t time.Time, tz string) *ClockContext {
    loc := time.Local
    if tz != "" {
        if l, err := time.LoadLocation(tz); err == nil {
            loc = l
        }
    }
    t = t.In(loc)

    _, week := t.ISOWeek()
    dayOfMonth := t.Day()
    lastDay := time.Date(t.Year(), t.Month()+1, 0, 0, 0, 0, 0, loc).Day()

    return &ClockContext{
        Now:          t,
        Hour:         t.Hour(),
        DayOfWeek:    t.Weekday().String(),
        DayOfMonth:   dayOfMonth,
        WeekOfYear:   week,
        Month:        int(t.Month()),
        Year:         t.Year(),
        IsWeekend:    t.Weekday() == time.Saturday || t.Weekday() == time.Sunday,
        IsMonthStart: dayOfMonth <= 3,
        IsMonthEnd:   dayOfMonth >= lastDay-2,
        IsQuarterEnd: (t.Month()%3 == 0) && dayOfMonth >= lastDay-2,
        IsYearEnd:    t.Month() == 12 && dayOfMonth >= 29,
        TZ:           loc.String(),
    }
}

2.5 Inspiration Report

// types/inspiration.go
package types

// InspirationReport - P0 output
type InspirationReport struct {
    Clock         *ClockContext `json:"clock"`
    Summary       string                   `json:"summary"`
    Highlights    []Highlight              `json:"highlights,omitempty"`
    Opportunities []Opportunity            `json:"opportunities,omitempty"`
    Risks         []Risk                   `json:"risks,omitempty"`
    WorldInsights []WorldInsight           `json:"world_insights,omitempty"`
    Suggestions   []string                 `json:"suggestions,omitempty"`
    PendingItems  []PendingItem            `json:"pending_items,omitempty"`
}

type Highlight struct {
    Source   string `json:"source"`   // data | event | feedback
    Priority string `json:"priority"` // high | medium | low
    Content  string `json:"content"`
    Change   string `json:"change,omitempty"` // +50%, -20%, etc.
}

type Opportunity struct {
    Description string `json:"description"`
    Impact      string `json:"impact"` // high | medium | low
    Urgency     string `json:"urgency"`
}

type Risk struct {
    Description string `json:"description"`
    Severity    string `json:"severity"` // high | medium | low
    Mitigation  string `json:"mitigation,omitempty"`
}

type WorldInsight struct {
    Source  string `json:"source"` // news | competitor | industry
    Title   string `json:"title"`
    Summary string `json:"summary"`
    Impact  string `json:"impact,omitempty"`
    URL     string `json:"url,omitempty"`
}

type PendingItem struct {
    Type        string `json:"type"` // goal | task | plan
    ID          string `json:"id"`
    Description string `json:"description"`
    DueDate     string `json:"due_date,omitempty"`
}

2.6 Request/Response Types

// types/request.go
package types

import (
    "context"
    "time"
)

// InterveneRequest - human intervention request
type InterveneRequest struct {
    TeamID      string             `json:"team_id"`
    MemberID    string             `json:"member_id"`
    Action      InterventionAction `json:"action"`
    Description string             `json:"description"`
    Priority    Priority           `json:"priority,omitempty"`
    PlanTime    *time.Time         `json:"plan_time,omitempty"` // for action=plan
}

// EventRequest - event trigger request
type EventRequest struct {
    MemberID  string                 `json:"member_id"`
    Source    string                 `json:"source"`     // webhook path or table name
    EventType string                 `json:"event_type"` // lead.created, etc.
    Data      map[string]interface{} `json:"data"`
}

// ExecutionResult - trigger result
type ExecutionResult struct {
    ExecutionID string     `json:"execution_id"`
    Status      ExecStatus `json:"status"`
    Message     string     `json:"message,omitempty"`
}

// RobotState - robot status query result
type RobotState struct {
    MemberID    string      `json:"member_id"`
    TeamID      string      `json:"team_id"`
    DisplayName string      `json:"display_name"`
    Status      RobotStatus `json:"status"`
    LastRun     *time.Time  `json:"last_run,omitempty"`
    NextRun     *time.Time  `json:"next_run,omitempty"`
    RunningID   string      `json:"running_id,omitempty"` // current execution ID
    RunningCnt  int         `json:"running_cnt"`          // current running count
}

3. Interfaces

Interfaces are also in types/ package to avoid cycles.

3.1 Manager Interface

// types/interfaces.go
package types

import (
    "context"
    "time"
)

// Manager - manages all robots
type Manager interface {
    // Lifecycle
    Start() error
    Stop() error

    // Cache operations
    LoadActiveRobots(ctx context.Context) error
    GetRobot(teamID, memberID string) *Robot
    ListRobots(teamID string) []*Robot
    RefreshRobot(teamID, memberID string) error

    // Clock trigger (called by internal ticker)
    Tick(ctx context.Context, now time.Time) error

    // Robot lifecycle (called when member created/deleted)
    OnRobotCreate(ctx context.Context, teamID, memberID string) error
    OnRobotDelete(ctx context.Context, teamID, memberID string) error
    OnRobotUpdate(ctx context.Context, teamID, memberID string) error
}

3.2 Trigger Interface

// types/interfaces.go (continued)
package types

import "context"

// Trigger - called by openapi layer
type Trigger interface {
    // Human intervention
    Intervene(ctx context.Context, req *InterveneRequest) (*ExecutionResult, error)

    // Event trigger
    HandleEvent(ctx context.Context, req *EventRequest) (*ExecutionResult, error)

    // Query & control
    GetStatus(ctx context.Context, teamID, memberID string) (*RobotState, error)
    Pause(ctx context.Context, teamID, memberID string) error
    Resume(ctx context.Context, teamID, memberID string) error
    Cancel(ctx context.Context, teamID, memberID, executionID string) error
}

3.3 Executor Interface

// types/interfaces.go (continued)
package types

import "context"

// Executor - executes robot phases
type Executor interface {
    // Execute runs all phases for a trigger
    Execute(ctx context.Context, robot *Robot, triggerType TriggerType, triggerData interface{}) (*Execution, error)

    // Individual phase execution (for testing/debugging)
    RunPhase(ctx context.Context, exec *Execution, phase Phase) error
}

3.4 Phase Interface

// types/interfaces.go (continued)
package types

// PhaseExecutor - phase executor interface
type PhaseExecutor interface {
    // Name returns phase name
    Name() Phase

    // Execute runs the phase
    Execute(ctx context.Context, exec *Execution) error
}

3.5 Cache Interface

// types/interfaces.go (continued)
package types

import "context"

// Cache - robot cache interface
type Cache interface {
    // Load all active robots
    LoadAll(ctx context.Context) error

    // Get robot by ID
    Get(teamID, memberID string) *Robot

    // List robots by team
    List(teamID string) []*Robot

    // Add/Update/Remove
    Add(robot *Robot)
    Update(robot *Robot)
    Remove(teamID, memberID string)

    // Stats
    Count() int
    CountByTeam(teamID string) int
}

3.6 Scheduler Interface

// types/interfaces.go (continued)
package types

import "context"

// Scheduler - worker pool and queue
type Scheduler interface {
    // Start/Stop
    Start() error
    Stop() error

    // Submit execution
    Submit(ctx context.Context, robot *Robot, triggerType TriggerType, triggerData interface{}) error

    // Queue status
    QueueSize() int
    WorkerCount() int
    ActiveCount() int
}

// SchedulerConfig - scheduler configuration
type SchedulerConfig struct {
    Workers    int // global worker count (default: 10)
    QueueSize  int // global queue size (default: 1000)
    MaxPerTeam int // max concurrent per team (default: 20)
}

3.7 Dedup Interface

// types/interfaces.go (continued)
package types

import (
    "context"
    "time"
)

// Dedup - deduplication service
type Dedup interface {
    // CheckExecution - fast check for duplicate execution
    CheckExecution(ctx context.Context, memberID string, triggerType TriggerType) (DedupResult, error)

    // CheckGoal - semantic check for duplicate goal
    CheckGoal(ctx context.Context, memberID string, goal *Goal) (DedupResult, error)

    // CheckTask - semantic check for duplicate task
    CheckTask(ctx context.Context, memberID string, task *Task) (DedupResult, error)

    // MarkExecuted - mark execution as done
    MarkExecuted(ctx context.Context, memberID string, triggerType TriggerType, window time.Duration)
}

4. Errors

// types/errors.go
package types

import "errors"

var (
    // Config errors
    ErrMissingIdentity    = errors.New("identity.role is required")
    ErrClockTimesEmpty    = errors.New("clock.times is required for times mode")
    ErrClockIntervalEmpty = errors.New("clock.every is required for interval mode")
    ErrClockModeInvalid   = errors.New("clock.mode must be times, interval, or daemon")

    // Runtime errors
    ErrRobotNotFound      = errors.New("robot not found")
    ErrRobotPaused        = errors.New("robot is paused")
    ErrRobotBusy          = errors.New("robot has reached max concurrent executions")
    ErrTriggerDisabled    = errors.New("trigger type is disabled for this robot")
    ErrExecutionCancelled = errors.New("execution was cancelled")
    ErrExecutionTimeout   = errors.New("execution timed out")

    // Phase errors
    ErrPhaseAgentNotFound = errors.New("phase agent not found")
    ErrGoalGenFailed      = errors.New("goal generation failed")
    ErrTaskPlanFailed     = errors.New("task planning failed")
    ErrDeliveryFailed     = errors.New("delivery failed")
)