yao/agent/robot/DESIGN.md
Max 3ae3f5425d Refine MCP Task Implementation and Documentation
- Updated MCP task executor ID format to use "mcp_server.mcp_tool" for better clarity and consistency.
- Added required MCP-specific fields (`mcp_server` and `mcp_tool`) to the Task struct and validation logic.
- Enhanced documentation in DESIGN.md and TECHNICAL.md to reflect changes in MCP task structure and requirements.
- Improved error handling in ExecuteMCPTask to ensure proper validation of MCP task fields before execution.
2026-01-27 18:44:25 +08:00

59 KiB

Robot Agent

1. What is it?

A Robot Agent is an AI team member. It works on its own, makes decisions, and runs tasks without waiting for user input.

Key points:

  • Belongs to a Team, managed like human members
  • Has clear job duties (e.g., "Sales Manager: track KPIs, make reports")
  • Created and deleted via Team API
  • Runs on schedule, or when triggered by humans or events
  • Learns from each run, stores knowledge in private KB

2. Architecture

2.1 System Flow

Architecture Note: All trigger types flow through Manager.

  • Clock: Manager.Tick() (internal ticker)
  • Human: Manager.Intervene() (API call)
  • Event: Manager.HandleEvent() (webhook/db trigger)

The trigger/ package provides utilities only (validation, clock matching, execution control).

flowchart TB
    subgraph Triggers["Triggers"]
        WC[/"⏰ Clock"/]
        HI[/"👤 Human"/]
        EV[/"📡 Event"/]
    end

    subgraph Manager["Manager (Central Orchestrator)"]
        TC{"Enabled?"}
        Cache[("Cache")]
        Dedup{"Dedup?"}
        Queue["Queue"]
    end

    subgraph Pool["Workers"]
        W1["Worker"]
        W2["Worker"]
        W3["Worker"]
    end

    subgraph Executor["Executor"]
        TT{"Trigger?"}
        P0["P0: Inspiration"]
        P1["P1: Goals"]
        P2["P2: Tasks"]
        P3["P3: Run"]
        P4["P4: Deliver"]
        P5["P5: Learn"]
    end

    subgraph Storage["Storage"]
        KB[("KB")]
        DB[("DB")]
    end

    WC --> TC
    HI & EV --> TC
    TC -->|Yes| Cache
    TC -->|No| X[/Skip/]
    Cache --> Dedup
    Dedup -->|OK| Queue
    Dedup -->|Dup| Cache
    Queue --> W1 & W2 & W3
    W1 & W2 & W3 --> TT
    TT -->|Clock| P0
    TT -->|Human/Event| P1
    P0 --> P1 --> P2 --> P3 --> P4 --> P5
    P5 --> KB & DB
    KB -.->|History| P0

2.2 Executor Modes

Executor supports multiple execution modes for different use cases:

Mode Use Case Status
Standard Production with real Agent calls Implemented
DryRun Tests, demos, preview without LLM calls Implemented
Sandbox Container-isolated for untrusted code Not Implemented

Standard Mode: Real execution with LLM calls, full phase execution, logging via kun/log.

DryRun Mode: Simulated execution without LLM calls. Used for:

  • Unit tests and integration tests
  • Demo and preview modes
  • Scheduling and concurrency testing

Sandbox Mode (Future): Container-level isolation (Docker/gVisor/Firecracker) for:

  • Untrusted robot configurations
  • Multi-tenant environments
  • Resource-limited execution

⚠️ Sandbox requires infrastructure support. Current placeholder behaves like DryRun.

2.3 Team Structure

Uses existing __yao.member model (yao/models/member.mod.yao):

┌─────────────────────────────────────────────────────────────────┐
│                            Team                                  │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                   Robot Members                          │    │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐        │    │
│  │  │Sales Manager│ │Data Analyst │ │CS Specialist│        │    │
│  │  │ • Track KPIs│ │ • Analyze   │ │ • Tickets   │        │    │
│  │  │ • Reports   │ │ • Reports   │ │ • Inquiries │        │    │
│  │  └─────────────┘ └─────────────┘ └─────────────┘        │    │
│  └─────────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                   User Members                           │    │
│  │  ┌─────────────┐ ┌─────────────┐                        │    │
│  │  │ John (Owner)│ │ Jane (Admin)│                        │    │
│  │  └─────────────┘ └─────────────┘                        │    │
│  └─────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────┘

Key fields in __yao.member for robot agents:

Field Type Description
member_type enum user | robot
autonomous_mode bool Enable robot execution
robot_config JSON Agent configuration (see section 5)
robot_status enum idle | working | paused | error | maintenance
system_prompt text Identity & role prompt
robot_email string Robot's email address for sending emails (From address)
agents JSON Accessible agents list
mcp_servers JSON Accessible MCP servers
manager_id string Direct manager user ID

3. How It Works

3.1 Flow: Trigger → Schedule → Run

sequenceDiagram
    autonumber
    participant T as Trigger
    participant M as Manager
    participant S as Scheduler
    participant W as Worker
    participant E as Executor
    participant A as Phase Agents
    participant KB as KB

    T->>M: Event
    M->>M: Check enabled
    M->>M: Get from cache
    M->>M: Check dedup
    M->>S: Submit

    S->>S: Check quota
    S->>S: Sort by priority
    S->>W: Dispatch

    W->>E: Run

    alt Clock trigger
        E->>A: P0: Inspiration (with clock context)
        A-->>E: Report
    end

    loop P1 to P5
        E->>A: Call agent
        A-->>E: Result
    end

    E->>KB: Save learning
    E-->>W: Done

3.2 Triggers

Type What Config Handler
Clock Timer (times/interval/daemon) triggers.clock Manager.Tick()
Human Manual action triggers.intervene Manager.Intervene()
Event Webhook, DB change triggers.event Manager.HandleEvent()

All on by default. Turn off per agent:

triggers:
  clock: { enabled: true }
  intervene: { enabled: true, actions: ["task.add", "goal.adjust"] }
  event: { enabled: false }

3.3 Concurrency

Two levels to prevent one agent from using all resources:

┌─────────────────────────────────────────────────────────────────┐
│                    Global Pool (10 workers)                      │
└─────────────────────────────────────────────────────────────────┘
          │                   │                   │
          ▼                   ▼                   ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Sales Manager   │ │ Data Analyst    │ │ CS Specialist   │
│ Limit: 3        │ │ Limit: 2        │ │ Limit: 3        │
│ Now: 2 ✓        │ │ Now: 2 (full)   │ │ Now: 1 ✓        │
└─────────────────┘ └─────────────────┘ └─────────────────┘

3.4 Dedup

Fast check (in memory):

key := memberID + ":" + triggerType + ":" + window
if has(key) { skip }

Smart check (for goals/tasks):

  • Dedup Agent looks at history
  • Returns: skip | merge | proceed

3.5 Cache

Keeps agents in memory. No DB query on each tick:

type AgentCache struct {
    agents map[string]*Agent   // member_id -> agent
    byTeam map[string][]string // team_id -> member_ids
}
// Refresh: on start, on change, every hour

4. Phases

4.1 Overview

Clock:        P0 → P1 → P2 → P3 → P4 → P5
Human/Event:       P1 → P2 → P3 → P4 → P5
Phase Agent In Out When
P0 Inspiration Clock + Data + News Report Clock only
P1 Goal Gen Report + history Goals Always
P2 Task Plan Goals + tools Tasks Always
P3 Run + Valid Tasks + Experts TaskResults Always
P4 Delivery All results Email/Webhook/Process Always
P5 Learning Summary KB entries Always

4.2 P0: Inspiration (Clock only)

Skipped for Human/Event triggers. They already have clear intent.

Gathers info to help make good goals. Clock context is key input - Agent knows what time it is and can decide what to do (e.g., 5pm Friday → write weekly report).

type InspirationReport struct {
    Clock   *ClockContext `json:"clock"`   // time context
    Content string        `json:"content"` // markdown text for LLM
}
// Content is markdown like:
// ## Summary
// ...
// ## Highlights
// - [High] Sales up 50%
// ## Opportunities / Risks / World News / Pending
// ...

type ClockContext struct {
    Now          time.Time // Current time
    Hour         int       // 0-23
    DayOfWeek    string    // Monday, Tuesday...
    DayOfMonth   int       // 1-31
    IsWeekend    bool
    IsMonthStart bool      // 1st-3rd
    IsMonthEnd   bool      // last 3 days
    IsQuarterEnd bool
    // Agent uses this to decide: "It's 5pm Friday, time for weekly report"
}

Sources:

  • Clock: Current time, day of week, month end, etc.
  • Internal: Data changes, events, feedback, pending work
  • External: Web search (news, competitors)

4.3 P1: Goals

For Clock: Uses inspiration report (with clock context) to make goals. Agent decides based on time what's important now.

For Human/Event: Uses the input directly as goals (or to generate goals).

type Goals struct {
    Content  string          // markdown text (for LLM)
    Delivery *DeliveryTarget // where to send results (for P4)
}

type DeliveryTarget struct {
    Type       DeliveryType // Preferred delivery type (P4 will use Delivery Center)
    Recipients []string     // email addresses, webhook URLs, user IDs
    Format     string       // markdown | html | json | text
    Template   string       // template name
    Options    map[string]interface{}
}

Example prompt:

You are [Sales Manager]. Your job: [track KPIs, make reports].

## Report
### Key Items
- [High] Data: 15 new sales (+50%)
- [High] Deadline: Friday report due
- [High] News: Competitor launched product

### Chances
- Sales up 20% vs last week
- Market growing

Make today's goals.

Note: Validation criteria (ExpectedOutput, ValidationRules) are defined at the Task level (P2), not Goals level. This allows each task to have specific validation rules for P3.

4.4 P2: Tasks

P2 Agent reads Goals markdown and breaks into executable tasks:

type Task struct {
    ID              string            // unique task ID
    Description     string            // human-readable task description (for UI display)
    Messages        []context.Message // original input (text, images, files, audio)
    GoalRef         string            // reference to goal (e.g., "Goal 1")
    Source          TaskSource        // auto | human | event
    ExecutorType    ExecutorType      // assistant | mcp | process
    ExecutorID      string            // agent ID or mcp tool name
    Args            []any             // arguments for executor
    Order           int               // execution order

    // Validation criteria (used in P3)
    ExpectedOutput  string   // what the task should produce
    ValidationRules []string // specific checks to perform
}

4.5 P3: Run

Architecture: P3 uses a modular design with three components:

┌─────────────────────────────────────────────────────────────┐
│                      run.go (P3 Entry)                       │
│  - RunConfig: ContinueOnFailure, ValidationThreshold,        │
│               MaxTurnsPerTask                                │
│  - RunExecution: main loop with task dependency passing      │
└─────────────────────┬───────────────────────────────────────┘
                      │
         ┌────────────┴────────────┐
         ▼                         ▼
┌─────────────────┐      ┌─────────────────┐
│   runner.go     │      │  validator.go   │
│  - Runner       │      │  - Validator    │
│  - Multi-turn   │      │  - Two-layer    │
│    conversation │      │  - Rule+Semantic│
│  - Task context │      │  - NeedReply    │
│    building     │      │  - ReplyContent │
└────────┬────────┘      └────────┬────────┘
         │                        │
         │                        ▼
         │               ┌─────────────────┐
         │               │  yao/assert     │
         │               │  - Asserter     │
         │               │  - 8 types      │
         │               │  - Extensible   │
         │               └─────────────────┘
         ▼
┌─────────────────────────────────────────┐
│  Executor Types                          │
│  - ExecutorAssistant → Multi-turn AI     │
│  - ExecutorMCP → Single-call MCP tool    │
│  - ExecutorProcess → Single-call Process │
└─────────────────────────────────────────┘

Execution Flow:

For each task:

  1. Build Context: Include previous task results as context
  2. Execute: Call appropriate executor (Assistant/MCP/Process)
  3. Validate: Use two-layer validation (rule-based + semantic)
  4. Continue or Complete:
    • For Assistant tasks: If NeedReply, continue conversation with ReplyContent
    • For MCP/Process tasks: Single-call execution, no multi-turn
  5. Update: Set task status and store result

Task Dependency: Previous task results are automatically passed as context to subsequent tasks via Runner.BuildTaskContext() and formatted using FormatPreviousResultsAsContext().

Two-Layer Validation:

Layer Method Speed Use Case
1. Rule-based yao/assert Fast Type check, contains, regex, json_path
2. Semantic Validation Agent Slow ExpectedOutput, complex criteria

Executor Types:

Type ExecutorID Format Example
assistant Agent ID experts.text-writer
mcp mcp_server.mcp_tool ark.image.text2img.generate
process Process name models.user.Find

MCP Task Fields:

For MCP tasks, three fields are required:

  • executor_id: Combined format mcp_server.mcp_tool
  • mcp_server: MCP server/client ID (e.g., ark.image.text2img)
  • mcp_tool: Tool name within the server (e.g., generate)

Multi-Turn Conversation Flow:

For assistant tasks, P3 uses a multi-turn conversation approach:

  1. Call: Call assistant and get result
  2. Validate: Validate result (determines: passed, complete, needReply, replyContent)
  3. Reply: If needReply, continue conversation with replyContent
  4. Repeat: Until complete or max turns exceeded

The Validator.ValidateWithContext() method determines:

  • Complete: Whether the expected result is obtained
  • NeedReply: Whether to continue conversation
  • ReplyContent: What to send in the next turn (validation feedback, clarification request, etc.)

This replaces the traditional retry mechanism with intelligent conversation continuation.

// RunConfig configures P3 execution behavior
type RunConfig struct {
    ContinueOnFailure   bool    // continue to next task even if current fails (default: false)
    ValidationThreshold float64 // minimum score to pass validation (default: 0.6)
    MaxTurnsPerTask     int     // max conversation turns per task (default: 10)
}

// ValidationResult with multi-turn conversation support
type ValidationResult struct {
    // Basic validation result
    Passed      bool     // overall validation passed
    Score       float64  // 0-1 confidence score
    Issues      []string // what failed
    Suggestions []string // how to improve
    Details     string   // detailed report (markdown)

    // Execution state (for multi-turn conversation control)
    Complete     bool   // whether expected result is obtained
    NeedReply    bool   // whether to continue conversation
    ReplyContent string // content for next turn (if NeedReply)
}

yao/assert Package:

Universal assertion library supporting 8 types:

Type Description Example
equals Exact match {"type": "equals", "value": "success"}
contains Substring check {"type": "contains", "value": "total"}
not_contains Negative check {"type": "not_contains", "value": "error"}
json_path JSON path extraction {"type": "json_path", "path": "data.count", "value": 10}
regex Pattern matching {"type": "regex", "value": "^[A-Z].*"}
type Type checking {"type": "type", "value": "array"}
script Custom script {"type": "script", "script": "scripts.validate"}
agent AI validation {"type": "agent", "use": "validator"}

4.6 P4: Deliver

P4 generates delivery content and pushes to Delivery Center. Agent only generates content, Delivery Center decides channels.

Architecture:

┌─────────────────────────────────────────────────────────────┐
│                      P4 Delivery Agent                       │
│  Role: Generate content only (Summary, Body, Attachments)    │
│  NOT responsible for: Channel selection                      │
└─────────────────────┬───────────────────────────────────────┘
                      │
                      ▼
┌─────────────────────────────────────────────────────────────┐
│              DeliveryRequest                                 │
│  - Content: Summary, Body, Attachments                       │
│  - Context: member_id, execution_id, trigger, team           │
│  (No Channels - Delivery Center decides)                     │
└─────────────────────┬───────────────────────────────────────┘
                      │
                      ▼
┌─────────────────────────────────────────────────────────────┐
│              Delivery Center                                 │
│  Role:                                                       │
│  1. Read Robot/User delivery preferences                     │
│  2. Decide which channels to use                             │
│  3. Execute delivery (email, webhook, process)               │
│  4. Future: auto-notify based on user subscriptions          │
│                                                              │
│  (Current: internal, future: yao/delivery)                   │
└─────────────────────────────────────────────────────────────┘

Key Design:

  • Separation of concerns: Agent generates content, Delivery Center handles channels
  • User preferences: Channels decided by Robot/User configuration, not Agent
  • Automatic delivery: If webhook configured, every execution pushes automatically
  • Future-ready: Delivery Center can be extracted to yao/delivery package

Delivery Request Structure:

// DeliveryRequest - pushed to Delivery Center
// No Channels field - Delivery Center decides based on preferences
type DeliveryRequest struct {
    Content *DeliveryContent `json:"content"` // Agent-generated content
    Context *DeliveryContext `json:"context"` // Tracking info
}

// DeliveryContent - content generated by Delivery Agent
type DeliveryContent struct {
    Summary     string               `json:"summary"`               // Brief 1-2 sentence summary
    Body        string               `json:"body"`                  // Full markdown report
    Attachments []DeliveryAttachment `json:"attachments,omitempty"` // Output artifacts
}

// DeliveryAttachment - task output attachment with metadata
type DeliveryAttachment struct {
    Title       string `json:"title"`                 // Human-readable title
    Description string `json:"description,omitempty"` // What this artifact is
    TaskID      string `json:"task_id,omitempty"`     // Which task produced this
    File        string `json:"file"`                  // Wrapper: __<uploader>://<fileID>
}

// DeliveryContext - tracking and audit info
type DeliveryContext struct {
    MemberID    string      `json:"member_id"`    // Robot member ID (globally unique)
    ExecutionID string      `json:"execution_id"`
    TriggerType TriggerType `json:"trigger_type"`
    TeamID      string      `json:"team_id"`
}

File Wrapper Format:

Attachments use the standard yao/attachment wrapper format:

  • Format: __<uploader>://<fileID>
  • Example: __yao.attachment://ccd472d11feb96e03a3fc468f494045c
  • Parse: attachment.Parse(value)(uploader, fileID, isWrapper)
  • Read: attachment.Base64(ctx, value) → base64 content

Delivery Channels (Delivery Center decides):

Channel Description Multiple Targets
email Send via yao/messenger Multiple recipients/emails
webhook POST to external URL Multiple URLs
process Yao Process call Multiple processes
notify In-app notification Future (auto by subscriptions)

Delivery Agent:

The Delivery Agent only generates content, does NOT decide channels:

// Delivery Agent Input
type DeliveryAgentInput struct {
    Robot       *Robot             `json:"robot"`
    TriggerType TriggerType        `json:"trigger"`
    Inspiration *InspirationReport `json:"inspiration"` // P0
    Goals       *Goals             `json:"goals"`       // P1
    Tasks       []Task             `json:"tasks"`       // P2
    Results     []TaskResult       `json:"results"`     // P3
}

// Delivery Agent Output - only content, no channels
type DeliveryAgentOutput struct {
    Content *DeliveryContent `json:"content"`
}

Example Agent Output:

{
  "content": {
    "summary": "Sales report completed: 15 new leads processed",
    "body": "## Weekly Sales Report\n\n### Summary\n...",
    "attachments": [
      {"title": "Sales Report.pdf", "file": "__yao.attachment://abc123"},
      {"title": "Lead Analysis.xlsx", "file": "__yao.attachment://def456"}
    ]
  }
}

Delivery Result:

// DeliveryResult - returned by Delivery Center
type DeliveryResult struct {
    RequestID string           `json:"request_id"`          // Delivery request ID
    Content   *DeliveryContent `json:"content"`             // Agent-generated content
    Results   []ChannelResult  `json:"results,omitempty"`   // Results per channel
    Success   bool             `json:"success"`             // Overall success
    Error     string           `json:"error,omitempty"`     // Error if failed
    SentAt    *time.Time       `json:"sent_at,omitempty"`   // When delivery completed
}

// ChannelResult - result for a single delivery target
type ChannelResult struct {
    Type       DeliveryType `json:"type"`                 // email | webhook | process
    Target     string       `json:"target"`               // Target identifier (email, URL, process name)
    Success    bool         `json:"success"`              // Whether delivery succeeded
    Recipients []string     `json:"recipients,omitempty"` // Who received (for email)
    Details    interface{}  `json:"details,omitempty"`    // Channel-specific response
    Error      string       `json:"error,omitempty"`      // Error message if failed
    SentAt     *time.Time   `json:"sent_at,omitempty"`    // When this target was delivered
}

Config (Delivery Preferences):

Robot config defines delivery preferences (Delivery Center reads and executes). Each channel supports multiple targets:

delivery:
  preferences:
    email:
      enabled: true
      targets:  # Multiple email targets
        - to: ["manager@company.com"]
          cc: ["team@company.com"]
        - to: ["ceo@company.com"]
          subject_template: "Executive Summary"
    
    webhook:
      enabled: true
      targets:  # Multiple webhook URLs
        - url: "https://slack.com/webhook/sales"
        - url: "https://feishu.cn/webhook/reports"
          headers: {"X-Custom": "value"}
    
    process:
      enabled: true
      targets:  # Multiple Yao Process calls
        - name: "orders.UpdateStatus"
          args: ["completed"]
        - name: "audit.LogDelivery"

# Note: notify handled by Delivery Center based on user subscriptions (future)

Use Cases:

Scenario Channels Description
Event callback process DB change → Robot → Update data via Process
Multi-channel notify email + webhook Send to multiple emails and Slack/飞书
Data pipeline process Robot result → Save to DB → Update dashboard

4.7 P5: Learn

Save to KB:

Type Examples
execution What worked, what failed
feedback Errors, fixes
insight Patterns, tips

5. Config

5.1 Structure

type Config struct {
    Triggers      *Triggers            `json:"triggers,omitempty"`
    Clock         *Clock               `json:"clock,omitempty"`
    Identity      *Identity            `json:"identity"`
    Quota         *Quota               `json:"quota"`
    KB            *KB                  `json:"kb,omitempty"`        // shared KB (same as assistant)
    DB            *DB                  `json:"db,omitempty"`        // shared DB (same as assistant)
    Learn         *Learn               `json:"learn,omitempty"`     // learning for private KB
    Resources     *Resources           `json:"resources"`
    Delivery      *DeliveryPreferences `json:"delivery,omitempty"`
    Events        []Event              `json:"events,omitempty"`
    Executor      *Executor            `json:"executor,omitempty"`  // executor mode settings
    DefaultLocale string               `json:"default_locale,omitempty"` // default language for clock/event triggers ("en-US", "zh-CN")
}

5.2 Types

// Phase - execution phase enum
type Phase string

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

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

// ClockMode - clock trigger mode enum
type ClockMode string

const (
    ClockModeTimes    ClockMode = "times"    // run at specific times
    ClockModeInterval ClockMode = "interval" // run every X duration
    ClockModeDaemon   ClockMode = "daemon"   // run continuously
)

// DeliveryType - output delivery type enum
type DeliveryType string

const (
    DeliveryEmail   DeliveryType = "email"   // Email via yao/messenger
    DeliveryWebhook DeliveryType = "webhook" // POST to external URL
    DeliveryProcess DeliveryType = "process" // Yao Process call
    DeliveryNotify  DeliveryType = "notify"  // In-app notification (future)
)

// ExecStatus - execution status enum
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 enum
type RobotStatus string

const (
    RobotIdle        RobotStatus = "idle"        // ready to run
    RobotWorking     RobotStatus = "working"     // currently executing
    RobotPaused      RobotStatus = "paused"      // manually paused
    RobotError       RobotStatus = "error"       // encountered error
    RobotMaintenance RobotStatus = "maintenance" // under maintenance
)

// Triggers - all on by default
type Triggers struct {
    Clock     *Trigger `json:"clock,omitempty"`
    Intervene *Trigger `json:"intervene,omitempty"`
    Event     *Trigger `json:"event,omitempty"`
}

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

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

// Identity
type Identity struct {
    Role   string   `json:"role"`
    Duties []string `json:"duties"`
    Rules  []string `json:"rules"`
}

// Quota
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)
}

// KB
// KB - shared knowledge base (same as assistant)
type KB struct {
    Collections []string               `json:"collections,omitempty"` // KB collection IDs
    Options     map[string]interface{} `json:"options,omitempty"`
}

// DB - shared database (same as assistant)
type DB struct {
    Models  []string               `json:"models,omitempty"` // database model names
    Options map[string]interface{} `json:"options,omitempty"`
}

// Learn - learning config for robot's private KB
// Private KB auto-created: robot_{team_id}_{member_id}_kb
type Learn struct {
    On    bool     `json:"on"`
    Types []string `json:"types"` // execution, feedback, insight
    Keep  int      `json:"keep"`  // days, 0 = forever
}

// Resources
type Resources struct {
    Phases map[Phase]string `json:"phases,omitempty"` // optional, defaults to __yao.{phase}
    Agents []string         `json:"agents"`
    MCP    []MCP            `json:"mcp"`
}

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

// DeliveryPreferences - Robot delivery preferences (read by Delivery Center)
// Each channel supports multiple targets
type DeliveryPreferences struct {
    Email   *EmailPreference   `json:"email,omitempty"`
    Webhook *WebhookPreference `json:"webhook,omitempty"`
    Process *ProcessPreference `json:"process,omitempty"`
    // notify is handled automatically based on user subscriptions
}

type EmailPreference struct {
    Enabled bool          `json:"enabled"`
    Targets []EmailTarget `json:"targets"`
}

type EmailTarget struct {
    To       []string `json:"to"`                 // Recipient addresses
    Template string   `json:"template,omitempty"` // Email template ID
    Subject  string   `json:"subject,omitempty"`  // Subject template
}

type WebhookPreference struct {
    Enabled bool            `json:"enabled"`
    Targets []WebhookTarget `json:"targets"`
}

type WebhookTarget struct {
    URL     string            `json:"url"`               // Webhook URL
    Method  string            `json:"method,omitempty"`  // HTTP method (default: POST)
    Headers map[string]string `json:"headers,omitempty"` // Custom headers
    Secret  string            `json:"secret,omitempty"`  // Signing secret
}

type ProcessPreference struct {
    Enabled bool            `json:"enabled"`
    Targets []ProcessTarget `json:"targets"`
}

type ProcessTarget struct {
    Process string `json:"process"`        // Yao Process name, e.g., "orders.UpdateStatus"
    Args    []any  `json:"args,omitempty"` // Additional arguments
}

// ExecutorMode - executor mode enum
type ExecutorMode string

const (
    ExecutorStandard ExecutorMode = "standard" // real Agent calls (default)
    ExecutorDryRun   ExecutorMode = "dryrun"   // simulated, no LLM calls
    ExecutorSandbox  ExecutorMode = "sandbox"  // container-isolated (NOT IMPLEMENTED)
)

// Executor - executor settings
type Executor struct {
    Mode        ExecutorMode  `json:"mode,omitempty"`         // standard | dryrun | sandbox
    MaxDuration string        `json:"max_duration,omitempty"` // max execution time (e.g., "30m")
}
// Note: Sandbox mode requires container infrastructure (Docker/gVisor).
// Current implementation falls back to DryRun behavior.

// Monitor

5.3 Example

Example record in __yao.member table:

{
  "member_id": "mem_abc123",
  "team_id": "team_xyz",
  "member_type": "robot",
  "display_name": "Sales Bot",
  "autonomous_mode": true,
  "robot_status": "idle",
  "system_prompt": "You are a sales analyst...",
  "robot_config": {
    "triggers": {
      "clock": { "enabled": true },
      "intervene": { "enabled": true },
      "event": { "enabled": false }
    },
    "clock": {
      "mode": "times",
      "times": ["09:00", "14:00", "17:00"],
      "days": ["Mon", "Tue", "Wed", "Thu", "Fri"],
      "tz": "Asia/Shanghai",
      "timeout": "30m"
    },
    "identity": {
      "role": "Sales Analyst",
      "duties": ["Analyze sales", "Make weekly reports"],
      "rules": ["Only access sales data"]
    },
    "quota": { "max": 2, "queue": 10, "priority": 5 },
    "kb": { "collections": ["sales-policies", "products"] },
    "db": { "models": ["sales", "customers"] },
    "learn": {
      "on": true,
      "types": ["execution", "feedback", "insight"],
      "keep": 90
    },
    "resources": {
      "phases": {
        "inspiration": "__yao.inspiration",
        "goals": "__yao.goals",
        "tasks": "__yao.tasks",
        "validation": "__yao.validation",
        "delivery": "__yao.delivery",
        "learning": "__yao.learning"
      },
      "agents": ["data-analyst", "chart-gen"],
      "mcp": [{ "id": "database", "tools": ["query"] }]
    },
    "delivery": {
      "type": "email",
      "opts": { "to": ["manager@company.com"] }
    },
    "executor": {
      "mode": "standard",
      "max_duration": "30m"
    }
  },
  "agents": ["data-analyst", "chart-gen"],
  "mcp_servers": ["database"]
}

6. Lifecycle

6.1 Agent States

stateDiagram-v2
    [*] --> Idle: POST create
    Idle --> Working: trigger
    Working --> Idle: done
    Idle --> Paused: PATCH pause
    Working --> Paused: PATCH pause
    Paused --> Idle: PATCH resume
    Idle --> Error: error
    Working --> Error: error
    Error --> Idle: PATCH reset
    Idle --> [*]: DELETE
    Paused --> [*]: DELETE
From To How
- idle POST create
idle working trigger (clock/human/event)
working idle execution done
idle paused PATCH robot_status="paused"
paused idle PATCH robot_status="idle"
any error execution error
error idle PATCH robot_status="idle"
any deleted DELETE

6.2 On Create

  1. Check config
  2. Generate member_id if missing
  3. Create KB: robot_{team_id}_{member_id}_kb
  4. Add to cache
  5. Set active

6.3 On Delete

  1. Stop running executions
  2. Remove from cache
  3. Delete or archive KB
  4. Soft delete record

6.4 Execution Flow

Single execution flow, depends on trigger type:

flowchart LR
    subgraph Trigger
        T{Trigger}
    end

    subgraph Schedule Path
        P0[P0: Inspiration]
    end

    subgraph Common Path
        P1[P1: Goals]
        P2[P2: Tasks]
        P3[P3: Run]
        P4[P4: Deliver]
        P5[P5: Learn]
    end

    T -->|Clock| P0
    T -->|Human/Event| P1
    P0 --> P1
    P1 --> P2 --> P3 --> P4 --> P5
stateDiagram-v2
    [*] --> Triggered
    Triggered --> P0_Inspiration: Clock
    Triggered --> P1_Goals: Human/Event
    P0_Inspiration --> P1_Goals
    P1_Goals --> P2_Tasks
    P2_Tasks --> P3_Run
    P3_Run --> P4_Deliver
    P4_Deliver --> P5_Learn
    P5_Learn --> [*]

7. Integrations

7.1 Execution Storage

Relationship: 1 Robot : N Executions (concurrent)

Each trigger creates a new Execution, stored in ExecutionStore (__yao.agent_execution table).

Execution data includes:

  • Status and phase tracking
  • All phase outputs (Inspiration, Goals, Tasks, Results, Delivery, Learning)
  • Error information
  • Timestamps and progress

Logging is handled by kun/log package for standard application logging. | List Execs | job.ListExecutions(param, page, pagesize) | | Get Exec | job.GetExecution(execID, param) | | Save Exec | job.SaveExecution(exec) | | List Logs | job.ListLogs(param, page, pagesize) | | Save Log | job.SaveLog(log) | | Push (start) | j.Push() | | Stop | j.Stop() | | Destroy | j.Destroy() | | Active Jobs | job.GetActiveJobs() | | Query by Cat | job.ListJobs({Wheres: [{Column: "category_id", ...}]}) |

7.2 Private KB

Made on robot member create: robot_{team_id}_{member_id}_kb

What it stores:

  • execution: What worked, what failed
  • feedback: Errors, fixes
  • insight: Patterns, tips

When:

  • Create: On robot member create
  • Update: After P5
  • Clean: Based on keep days
  • Delete: On robot member delete

7.3 External Input

Types:

  • clock: Timer (with time context)
  • intervene: Human action
  • event: Webhook, DB change
  • callback: Async result

Human actions (InterventionAction):

  • task.add: Add a new task
  • task.cancel: Cancel a task
  • task.update: Update task details
  • goal.adjust: Modify current goal
  • goal.add: Add a new goal
  • goal.complete: Mark goal as complete
  • goal.cancel: Cancel a goal
  • plan.add: Schedule for later
  • plan.remove: Remove from plan queue
  • plan.update: Update planned item
  • instruct: Direct instruction to robot

Plan Queue:

  • Holds tasks for later
  • Runs at next cycle start

8. API

8.1 Manager (Internal)

Note: Manager is the central orchestrator, handling all trigger types.

type Manager interface {
    // Lifecycle
    Start() error
    Stop() error

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

    // Manual trigger (for testing/API)
    TriggerManual(ctx *Context, memberID string, trigger TriggerType, data interface{}) (string, error)

    // Human intervention (called by API)
    Intervene(ctx *Context, req *InterveneRequest) (*ExecutionResult, error)

    // Event trigger (called by webhook/db trigger)
    HandleEvent(ctx *Context, req *EventRequest) (*ExecutionResult, error)

    // Execution control
    PauseExecution(ctx *Context, execID string) error
    ResumeExecution(ctx *Context, execID string) error
    StopExecution(ctx *Context, execID string) error

    // Cache access
    Cache() Cache
}

8.2 Trigger (Integrated into Manager)

Note: Trigger logic is integrated into Manager, not a separate interface. The trigger/ package provides utilities (validation, clock matching, execution control).

// TriggerType enum
type TriggerType string

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

// Manager handles all trigger types:
// - Clock: Manager.Tick() called by internal ticker
// - Human: Manager.Intervene() called by API
// - Event: Manager.HandleEvent() called by webhook/db trigger

// trigger/ package provides utilities:
// - trigger.ValidateIntervention(req) - validate human intervention request
// - trigger.ValidateEvent(req) - validate event request
// - trigger.BuildEventInput(req) - build TriggerInput from event
// - trigger.ClockMatcher - reusable clock matching logic
// - trigger.ExecutionController - pause/resume/stop execution

type InterveneRequest struct {
    TeamID       string
    MemberID     string
    Action       InterventionAction // task.add | goal.adjust | task.cancel | plan.add | instruct
    Messages     []context.Message  // user input (text, images, files)
    PlanTime     *time.Time         // for action=plan.add
    ExecutorMode ExecutorMode       // optional: standard | dryrun (override robot config)
}

type EventRequest struct {
    MemberID     string
    Source       string // webhook path or table name
    EventType    string // lead.created, etc.
    Data         map[string]interface{}
    ExecutorMode ExecutorMode // optional: standard | dryrun (override robot config)
}

type ExecutionResult struct {
    ExecutionID string     // Job execution ID
    Status      ExecStatus // pending | running | completed | failed
    Message     string     // status message
}

type RobotState struct {
    MemberID   string      // member_id from __yao.member
    Status     RobotStatus // idle | working | paused | error | maintenance
    LastRun    time.Time
    NextRun    time.Time
    Running    int         // current running execution count
    MaxRunning int         // max concurrent executions (from Quota.Max)
    RunningIDs []string    // list of running execution IDs
}

8.3 Execution (Uses ExecutionStore)

Uses dedicated __yao.agent_execution table via ExecutionStore.

Each trigger creates a new Execution:

// On each trigger (clock/human/event), create a new Execution
exec := &types.Execution{
    ID:          utils.NewID(),
    MemberID:    memberID,
    TeamID:      teamID,
    TriggerType: triggerType,
    Status:      types.ExecStatusRunning,
    Phase:       types.PhaseP0Init,
    StartedAt:   time.Now(),
}

// Save to ExecutionStore
execStore.Save(exec)

Query executions for a robot:

// List all executions for a robot member
executions, err := execStore.List(memberID, 1, 10)

Query examples:

// Get execution by ID
exec, err := execStore.Get(executionID)

// List executions for a robot
executions, err := execStore.List(memberID, page, pageSize)

// Update execution status
execStore.UpdateStatus(executionID, types.ExecStatusCompleted)

// Logging via kun/log
log.With(log.F{"execution_id": exec.ID, "phase": "P1"}).Info("Phase started")

9. Security

  1. Team only: Agent sees only its team's data
  2. Role rules: Uses role_id permissions
  3. Limited tools: Only what's in resources
  4. Timeout: Stops if runs too long
  5. Logs: All runs saved

10. Quick Ref

Triggers

triggers:
  clock: { enabled: true }
  intervene: { enabled: true, actions: [...] }
  event: { enabled: false }

Clock

# Mode 1: Specific times
clock:
  mode: times
  times: ["09:00", "14:00", "17:00"]
  days: ["Mon", "Tue", "Wed", "Thu", "Fri"]
  tz: Asia/Shanghai
  timeout: 30m

# Mode 2: Interval
clock:
  mode: interval
  every: 30m  # run every 30 minutes
  timeout: 10m

# Mode 3: Daemon (continuous thinking/analysis)
clock:
  mode: daemon  # restart immediately after each run
  timeout: 10m  # max time per run
  # Use case: Research analyst, market monitor

Phase Agents

# Optional - defaults to __yao.{phase} if not specified
resources:
  phases:
    inspiration: "__yao.inspiration" # Clock only
    goals: "__yao.goals"
    tasks: "__yao.tasks"
    validation: "__yao.validation"
    delivery: "__yao.delivery"
    learning: "__yao.learning"

Quota

quota:
  max: 2 # max running
  queue: 10 # queue size
  priority: 5 # 1-10

Executor

# Standard mode (default) - real Agent calls
executor:
  mode: standard
  max_duration: 30m

# DryRun mode - simulated execution (for testing/demos)
executor:
  mode: dryrun

# Sandbox mode (NOT IMPLEMENTED) - container-isolated
# Requires Docker/gVisor infrastructure
# executor:
#   mode: sandbox
#   max_duration: 10m

API Override:

// Override executor mode per trigger
const result = Process("robot.Trigger", "mem_abc123", {
  type: "human",
  action: "task.add",
  messages: [{ role: "user", content: "Test task" }],
  executor_mode: "dryrun", // override robot config
});

11. Examples

Each example shows a different trigger mode:

Example Trigger Mode Scenario
11.1 Clock times SEO/GEO Content - daily content optimization
11.2 Clock interval Competitor Monitor - check every 2 hours
11.3 Clock daemon Research Analyst - continuous insight mining
11.4 Human intervene Sales Assistant - manager assigns tasks
11.5 Event webhook Lead Processor - qualify and route new leads

11.1 SEO/GEO Content Agent (Clock: times)

Trigger: Clock - specific times daily

Role: AI Marketing - auto-generate and optimize SEO/GEO content.

// robot_config for SEO Content Agent
{
  "triggers": {
    "clock": { "enabled": true },
    "intervene": { "enabled": true }
  },
  "clock": {
    "mode": "times",
    "times": ["06:00", "18:00"],
    "days": ["Mon", "Tue", "Wed", "Thu", "Fri"],
    "tz": "Asia/Shanghai"
  },
  "identity": {
    "role": "SEO/GEO Content Specialist",
    "duties": [
      "Research trending keywords in our industry",
      "Generate SEO-optimized articles (2-3 per day)",
      "Optimize existing content for GEO (AI search)",
      "Track keyword rankings and adjust strategy",
      "A/B test titles and meta descriptions"
    ]
  },
  "resources": {
    "agents": ["keyword-researcher", "content-writer", "seo-optimizer"],
    "mcp": [
      { "id": "google-search", "tools": ["trends", "rankings"] },
      { "id": "cms", "tools": ["create", "update", "publish"] }
    ]
  },
  "delivery": {
    "type": "notify",
    "opts": { "channel": "marketing-team" }
  }
}

Example run at 06:00 Monday:

P0 Inspiration:
  Clock: Monday 06:00, start of week
  Data:
    - Keyword "AI app development" trending (+45% this week)
    - Our article ranks #8, competitor #2
    - 3 articles need GEO optimization
  World: New AI regulation announced last Friday

P1 Goals:
  1. Write new article targeting "AI app development"
  2. Optimize 3 old articles for GEO
  3. Update meta descriptions for top 5 pages

P2 Tasks:
  1. Research "AI app development" keywords → keyword-researcher
  2. Write article with SEO structure → content-writer
  3. Add FAQ schema for GEO → seo-optimizer
  4. Publish to CMS → cms.publish

P3 Execute:
  - Keywords: "AI app development", "build AI apps", "AI dev guide" (12 total)
  - Article: 2500 words, 8 sections, FAQ schema added
  - Published to CMS, indexed by Google

P4 Delivery:
  → Notify: "Published: 'Complete Guide to AI App Development' - targeting 12 keywords"

P5 Learn:
  - "AI app development" articles perform well on Monday morning
  - FAQ schema improves GEO visibility by 30%

11.2 Competitor Monitor (Clock: interval)

Trigger: Clock - every 2 hours

Role: Monitor competitors, track market changes, alert on important updates.

// robot_config for Competitor Monitor
{
  "triggers": {
    "clock": { "enabled": true }
  },
  "clock": {
    "mode": "interval",
    "every": "2h"
  },
  "identity": {
    "role": "Competitor Intelligence Analyst",
    "duties": [
      "Monitor competitor websites for changes",
      "Track competitor pricing updates",
      "Watch for new product launches",
      "Analyze competitor content strategy",
      "Alert team on significant changes"
    ]
  },
  "resources": {
    "agents": ["web-scraper", "diff-analyzer", "report-writer"],
    "mcp": [{ "id": "web-search", "tools": ["search", "news"] }]
  },
  "delivery": {
    "type": "webhook",
    "opts": { "url": "https://slack.com/webhook/competitor-alerts" }
  }
}

Example run detecting competitor change:

P0 Inspiration:
  Clock: Tuesday 14:00
  Data:
    - Competitor A: pricing page changed
    - Competitor B: new blog post about "AI agents"
    - Competitor C: no changes

P1 Goals:
  1. Analyze Competitor A pricing change
  2. Summarize Competitor B's new content
  3. Assess impact on our positioning

P2 Tasks:
  1. Scrape old vs new pricing → web-scraper
  2. Compare pricing tiers → diff-analyzer
  3. Generate competitive analysis → report-writer

P3 Execute:
  - Competitor A: dropped price 20% on enterprise tier
  - Competitor B: targeting same keywords as us

P4 Delivery:
  → Slack: "🚨 Competitor A cut enterprise price 20% - review needed"

P5 Learn:
  - Competitor A tends to change pricing on Tuesdays
  - Price changes often precede feature launches

11.3 Industry Research Analyst (Clock: daemon)

Trigger: Clock - continuous daemon mode

Role: Continuously read industry news, papers, social media; extract insights; build knowledge.

// robot_config for Research Analyst
{
  "triggers": {
    "clock": { "enabled": true }
  },
  "clock": {
    "mode": "daemon",
    "timeout": "10m"
  },
  "identity": {
    "role": "Industry Research Analyst",
    "duties": [
      "Continuously scan industry news and papers",
      "Analyze trends and extract key insights",
      "Identify emerging technologies and competitors",
      "Build and maintain industry knowledge base",
      "Alert team on significant developments"
    ]
  },
  "resources": {
    "agents": ["content-reader", "insight-extractor", "report-writer"],
    "mcp": [
      { "id": "web-search", "tools": ["search", "news"] },
      { "id": "arxiv", "tools": ["search", "fetch"] },
      { "id": "twitter", "tools": ["search", "trends"] }
    ]
  },
  "delivery": {
    "type": "notify",
    "opts": { "channel": "research-insights" }
  }
}

Example continuous run:

Run #1 (09:00):
  P0: Scan sources
      - 15 new AI news articles
      - 3 new papers on arXiv
      - Twitter: "AI Agent" trending
  P1: Goals:
      1. Read and analyze new content
      2. Extract insights relevant to our business
      3. Update knowledge base
  P2: Tasks:
      1. Read articles → content-reader
      2. Analyze papers → content-reader
      3. Extract insights → insight-extractor
  P3: Execute:
      - Article: "OpenAI releases new agent framework"
        Insight: Validates our direction, watch for API changes
      - Paper: "Multi-agent collaboration patterns"
        Insight: Useful for our agent design, save to KB
      - Twitter: Sentiment positive on AI agents
  P4: Notify: "📚 3 new insights added to KB"
  P5: Learn: OpenAI news = high relevance, prioritize
  → Restart immediately

Run #2 (09:12):
  P0: Scan sources
      - 2 new articles (low relevance)
      - No new papers
      - Twitter: Normal activity
  P1: Low-value content, skip deep analysis
  P5: Learn: Mid-morning usually quiet
  → Restart immediately

Run #3 (09:25):
  P0: Scan sources
      - Breaking: "Competitor X raises $100M for AI platform"
  P1: Goals:
      1. Deep analyze competitor news
      2. Assess impact on our market
      3. Alert team immediately
  P2: Tasks:
      1. Gather all competitor X info → web-search
      2. Analyze their positioning → insight-extractor
      3. Write competitive brief → report-writer
  P3: Execute:
      - Competitor X: Focus on enterprise, similar target market
      - Funding: Will likely expand sales team
      - Threat level: Medium-High
  P4: Notify: "🚨 Competitor X raised $100M - brief attached"
  P5: Learn: Funding news = always high priority
  → Restart immediately

11.4 Sales Assistant (Human: intervene)

Trigger: Human intervention - sales manager assigns tasks

Role: Help sales team with research, proposals, follow-ups when manager assigns work.

// robot_config for Sales Assistant
{
  "triggers": {
    "clock": { "enabled": false },
    "intervene": {
      "enabled": true,
      "actions": ["task.add", "goal.adjust", "instruct"]
    }
  },
  "identity": {
    "role": "Sales Assistant",
    "duties": [
      "Research assigned prospects and companies",
      "Prepare customized proposals and presentations",
      "Draft follow-up emails",
      "Analyze deal history and suggest strategies",
      "Prepare meeting briefs"
    ]
  },
  "resources": {
    "agents": ["company-researcher", "proposal-writer", "email-drafter"],
    "mcp": [
      { "id": "crm", "tools": ["query", "update"] },
      { "id": "linkedin", "tools": ["search", "profile"] },
      { "id": "email", "tools": ["draft", "send"] }
    ]
  },
  "delivery": {
    "type": "email",
    "opts": { "to": ["sales-manager@company.com"] }
  }
}

Example: Sales manager assigns task:

Sales Manager Input:
  Action: task.add
  Messages: [{ role: "user", content: "Meeting with BigCorp CTO tomorrow. Prepare materials.
               They do smart manufacturing, $150M revenue, digital transformation." }]

Agent Execution (no P0 for human trigger):
  P1 Goals (from human input):
    1. Research BigCorp and their CTO
    2. Prepare meeting brief
    3. Draft customized proposal

  P2 Tasks:
    1. Research BigCorp → company-researcher
       - Company background, recent news
       - Digital transformation status
       - Potential pain points
    2. Research CTO profile → linkedin.profile
       - Background, interests
       - Recent posts/articles
    3. Prepare meeting brief → proposal-writer
    4. Draft proposal → proposal-writer

  P3 Execute:
    - BigCorp: Leading smart manufacturing, 3 factories, implementing MES
    - CTO John: Ex-Google, focused on AI+Manufacturing, recent post on "AI QC"
    - Pain point: High QC labor cost, 2% defect miss rate
    - Opportunity: Our AI QC solution can reduce miss rate to 0.1%

  P4 Delivery:
    → Email to sales manager:
      - Attachment 1: BigCorp Research Report (PDF)
      - Attachment 2: CTO Profile Brief
      - Attachment 3: Custom Proposal - AI QC Solution
      - Attachment 4: Meeting Agenda Suggestion

Sales Manager Follow-up:
  Action: task.add
  Messages: [{ role: "user", content: "Also prepare some similar case studies, manufacturing preferred" }]

Agent Continues:
  P1: Find similar manufacturing case studies
  P2: Search CRM for manufacturing wins
  P3: Found 3 cases: Auto parts factory, Electronics plant, Food processing
  P4: Email: "3 manufacturing case studies attached"
  P5: Learn: Manufacturing prospects often need QC case studies

11.5 Lead Processor (Event: webhook)

Trigger: Event - new lead from website/CRM

Role: Instantly process and qualify new leads, route to sales.

// robot_config for Lead Processor
{
  "triggers": {
    "clock": { "enabled": false },
    "event": { "enabled": true }
  },
  "events": [
    {
      "type": "webhook",
      "source": "/webhook/leads",
      "filter": { "event_types": ["lead.created"] }
    },
    {
      "type": "database",
      "source": "crm_leads",
      "filter": { "trigger": "insert" }
    }
  ],
  "identity": {
    "role": "Lead Qualification Specialist",
    "duties": [
      "Instantly process new leads",
      "Enrich lead data (company info, LinkedIn)",
      "Score lead quality (1-100)",
      "Route hot leads to sales immediately",
      "Add cold leads to nurture sequence"
    ]
  },
  "resources": {
    "agents": ["data-enricher", "lead-scorer"],
    "mcp": [
      { "id": "clearbit", "tools": ["enrich"] },
      { "id": "crm", "tools": ["update", "assign"] },
      { "id": "email", "tools": ["send"] }
    ]
  },
  "delivery": {
    "type": "webhook",
    "opts": { "url": "https://slack.com/webhook/sales-leads" }
  }
}

Example: New lead event:

Event Received:
  Type: lead.created
  Data: {
    name: "John Smith",
    email: "john@bigcorp.com",
    company: "BigCorp",
    message: "Interested in Enterprise pricing, team of 50"
  }

Agent Execution (no P0 for events):
  P1 Goals:
    1. Enrich lead data
    2. Score lead quality
    3. Route appropriately

  P2 Tasks:
    1. Lookup company info → clearbit.enrich
    2. Calculate lead score → lead-scorer
    3. Update CRM → crm.update
    4. Notify sales → slack webhook

  P3 Execute:
    - Company: BigCorp, 500 employees, Series C
    - LinkedIn: VP of Engineering
    - Lead Score: 85/100 (HOT)
    - Reason: Enterprise inquiry, decision maker, funded company

  P4 Delivery:
    → Slack: "🔥 HOT LEAD (85/100): John Smith @ BigCorp
              - 500 employees, Series C
              - Interested in Enterprise (50 seats)
              - Assigned to: Sales Rep A"
    → CRM: Lead updated, assigned to Sales Rep A
    → Email to lead: "Thanks for your inquiry. Our sales rep will contact you within 1 hour."

  P5 Learn:
    - BigCorp profile saved for future reference
    - VP-level leads from funded companies = high conversion