DESIGN.md@agent/robot Β· git:20260127.3ae3f54 Β· 2026-01-27 Β· sha256 b81b11aa40d45925

DESIGN.md@agent/robot git:20260127.3ae3f54A

Immutable. This exact content is served forever at /api/v1/blob/b81b11aa40d45925.

# 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).

```mermaid
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

```mermaid
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:

```yaml
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):

```go
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:

```go
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).

```go
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).

```go
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:

```go
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.

```go
// 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:**

```go
// 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:

```go
// 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:**

```json
{
  "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:**

```go
// 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**:

```yaml
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

```go
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

```go
// 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:

```json
{
  "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

```mermaid
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
5. Soft delete record

### 6.4 Execution Flow

Single execution flow, depends on trigger type:

```mermaid
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
```

```mermaid
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.

```go
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).

```go
// 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:**

```go
// 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:**

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

**Query examples:**

```go
// 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

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

### Clock

```yaml
# 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

```yaml
# 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

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

### Executor

```yaml
# 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:**

```javascript
// 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.

```json
// 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.

```json
// 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.

```json
// 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.

```json
// 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.

```json
// 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
```