DESIGN.md@openapi/agent/robot ยท git:20260122.cf2a98c ยท 2026-01-22 ยท sha256 208664f6568ec194

DESIGN.md@openapi/agent/robot git:20260122.cf2a98cA

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

# Robot OpenAPI - Design Document

> Based on: `yao/agent/robot/` (Backend), `cui/packages/cui/pages/mission-control/` (Frontend)
> Gap Analysis: `yao/openapi/agent/robot/GAPS.md`

## 1. Overview

### 1.1 Purpose

Provide HTTP REST API endpoints for Robot Agent management, designed to support the Mission Control frontend UI.

### 1.2 Implementation Strategy

> **Low-risk phases first. Medium-risk features (Chat API, SSE Event Bus) can be deferred.**

| Phase | Risk | Features | Frontend Fallback |
|-------|------|----------|-------------------|
| 1. Core CRUD | ๐ŸŸข Low | List, Get, Create, Update, Delete | - |
| 2. Execution Management | ๐ŸŸข Low | List, Get, Control executions | - |
| 3. Results & Activities | ๐ŸŸข Low | Deliverables, Activity feed | - |
| 4. i18n | ๐ŸŸข Low | Locale parameter support | - |
| 5. Chat API | ๐ŸŸก Medium (Deferred) | Multi-turn conversation | Single-submit mode |
| 6. SSE Event Bus | ๐ŸŸก Medium (Deferred) | Real-time status streams | Polling every 3-5s |

### 1.3 Route Decision: `/v1/agent/robots`

**Analysis of existing `openapi/` route structure:**

| Package | Route | Description |
|---------|-------|-------------|
| `agent/` | `/v1/agent/assistants` | Assistant CRUD, info |
| `chat/` | `/v1/chat/completions` | Chat completions |
| `kb/` | `/v1/kb/collections` | Knowledge base |
| `job/` | `/v1/job/jobs` | Job management |
| `file/` | `/v1/file/*` | File operations |
| `user/` | `/v1/user/*` | User management |
| `team/` | `/v1/team/*` | Team management |

**Decision:** Put Robot routes under `/v1/agent/robots` because:

1. **Semantic Alignment**: Robot is a type of Agent (Autonomous Robot Agent), just like Assistant is a type of Agent
2. **Existing Pattern**: `openapi/agent/` already handles `/v1/agent/assistants`
3. **Logical Grouping**: Agent-related APIs grouped together
4. **Consistent Hierarchy**: `/v1/agent/{type}` pattern

**Route Comparison:**

| Option | Path | Verdict |
|--------|------|---------|
| โŒ `/v1/robots` | New top-level namespace | Inconsistent with agent grouping |
| โœ… `/v1/agent/robots` | Under agent namespace | Follows existing pattern |
| โŒ `/v1/members?type=robot` | Reuse members | Less intuitive for operations |

### 1.4 Architecture

```
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      Frontend (Mission Control)                          โ”‚
โ”‚  cui/packages/cui/pages/mission-control/                                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚ HTTP REST / SSE
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      OpenAPI Layer                                       โ”‚
โ”‚  yao/openapi/agent/                                                      โ”‚
โ”‚  - Routes: /v1/agent/assistants/* (existing)                             โ”‚
โ”‚  - Routes: /v1/agent/robots/* (NEW)                                      โ”‚
โ”‚  - Auth: OAuth2 via Guard middleware                                     โ”‚
โ”‚  - SSE: Real-time updates                                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      Robot API Layer                                     โ”‚
โ”‚  yao/agent/robot/api/                                                    โ”‚
โ”‚  - Go functions: Get(), List(), Trigger(), etc.                          โ”‚
โ”‚  - Business logic                                                        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      Robot Core                                          โ”‚
โ”‚  yao/agent/robot/                                                        โ”‚
โ”‚  - Manager, Executor, Cache, Pool, Store                                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```

### 1.5 Design Principles

1. **Layered Architecture**: OpenAPI layer only handles HTTP concerns (routing, request parsing, response formatting). Business logic stays in `robot/api/`.
2. **Consistent with Existing Patterns**: Follow `yao/openapi/agent/` conventions, extend existing agent package
3. **Incremental Implementation**: Start with core CRUD, then add real-time features
4. **Frontend-Backend Balance**: API design considers both frontend needs and backend capabilities

---

## 2. Differences Analysis

### 2.1 Frontend Expectations vs Backend Reality

| Feature | Frontend (API.md) | Backend (robot/api/) | Gap | Solution |
|---------|-------------------|----------------------|-----|----------|
| Robot List | `GET /v1/robots` with `name`, `description` | `List()` returns `types.Robot` | Field mapping needed | Map in OpenAPI layer |
| Robot Detail | `GET /v1/robots/:id` with full `config` | `Get()` returns Robot + Config | Need format conversion | Map to frontend format |
| Create Robot | POST with `work_mode` | Not implemented | New feature | Add `Create()` |
| Update Robot | PUT with partial update | Not implemented | New feature | Add `Update()` |
| Delete Robot | DELETE | Not implemented | New feature | Add `Remove()` |
| Trigger | Immediate execution | `Trigger()` returns sync result | Works | Wrap with SSE events |
| Intervene | Immediate intervention | `Intervene()` returns sync result | Works | Wrap with SSE events |
| Multi-turn Chat | Chat before execute | Not implemented | **Deferred** | Frontend uses single-submit |
| Results List | `/results` endpoint | No separate results API | New feature | Derive from executions |
| Activities | `/activities` endpoint | No activities tracking | New feature | Derive from executions |
| Real-time Stream | SSE `/stream` endpoints | No SSE support | **Deferred** | Frontend uses polling |
| i18n | `?locale=` query param | No i18n support | New feature | Add locale handling |

### 2.2 Field Mapping (Backend โ†’ Frontend API)

The `__yao.member` model already has the necessary fields, with different names:

| Frontend API | Backend DB (`__yao.member`) | Backend Go (`types.Robot`) | Mapping |
|--------------|----------------------------|---------------------------|---------|
| `member_id` | `member_id` | `MemberID` | Direct |
| `name` | `member_id` | `MemberID` | **Reuse** (slug-like identifier) |
| `display_name` | `display_name` | `DisplayName` | Direct |
| `description` | `bio` | Need to add `Bio` field | Map in OpenAPI layer |
| `email` | `robot_email` | `RobotEmail` | Direct |

**Required Backend Changes:**
1. Add `Bio` field to `types.Robot` struct
2. Add `bio` to `cache/load.go` memberFields

### 2.3 Type Differences

| Frontend Type | Backend Type | Solution |
|---------------|--------------|----------|
| `RobotState.name` | `Robot.MemberID` | Map `member_id` to `name` |
| `RobotState.description` | `Robot.Bio` (new) | Add field, map to `description` |
| `Execution.name` | Not in `types.Execution` | Derive from goals or input in OpenAPI layer |
| `Execution.current_task_name` | Not in `types.Execution` | Derive from current task in OpenAPI layer |
| `ResultFile` | No equivalent | New type in OpenAPI layer (derive from delivery) |
| `Activity` | No equivalent | New type in OpenAPI layer (derive from executions) |

---

## 3. API Endpoints

> **Base Path:** `/v1/agent/robots`

### 3.1 Robot Management

| Method | Path | Handler | Description |
|--------|------|---------|-------------|
| GET | /v1/agent/robots | `ListRobots` | List all robots |
| GET | /v1/agent/robots/:id | `GetRobot` | Get robot details |
| POST | /v1/agent/robots | `CreateRobot` | Create robot |
| PUT | /v1/agent/robots/:id | `UpdateRobot` | Update robot |
| DELETE | /v1/agent/robots/:id | `DeleteRobot` | Delete robot |

### 3.2 Execution Management

| Method | Path | Handler | Description |
|--------|------|---------|-------------|
| GET | /v1/agent/robots/:id/executions | `ListExecutions` | List executions |
| GET | /v1/agent/robots/:id/executions/:exec_id | `GetExecution` | Get execution detail |
| POST | /v1/agent/robots/:id/trigger | `TriggerRobot` | Trigger execution (SSE) |
| POST | /v1/agent/robots/:id/intervene | `InterveneRobot` | Intervene execution (SSE) |
| POST | /v1/agent/robots/:id/executions/:exec_id/pause | `PauseExecution` | Pause execution |
| POST | /v1/agent/robots/:id/executions/:exec_id/resume | `ResumeExecution` | Resume execution |
| POST | /v1/agent/robots/:id/executions/:exec_id/cancel | `CancelExecution` | Cancel execution |
| POST | /v1/agent/robots/:id/executions/:exec_id/retry | `RetryExecution` | Retry execution |

### 3.3 Results Management

| Method | Path | Handler | Description |
|--------|------|---------|-------------|
| GET | /v1/agent/robots/:id/results | `ListResults` | List deliverables |
| GET | /v1/agent/robots/:id/results/:result_id | `GetResult` | Get deliverable detail |

### 3.4 Activities & Real-time

| Method | Path | Handler | Description |
|--------|------|---------|-------------|
| GET | /v1/agent/robots/activities | `ListActivities` | List recent activities |
| GET | /v1/agent/robots/stream | `StreamRobots` | Robot status SSE |
| GET | /v1/agent/robots/:id/executions/:exec_id/stream | `StreamExecution` | Execution progress SSE |

---

## 4. Response Types

### 4.1 RobotResponse (for list and detail)

```go
// RobotResponse - formatted robot for API response
// Maps backend fields to frontend expected format
type RobotResponse struct {
    MemberID    string          `json:"member_id"`
    TeamID      string          `json:"team_id"`
    Name        string          `json:"name"`         // From Robot.MemberID (slug-like identifier)
    DisplayName string          `json:"display_name"` // From Robot.DisplayName
    Description string          `json:"description,omitempty"` // From Robot.Bio
    Status      string          `json:"status"`       // idle | working | paused | error | maintenance
    Running     int             `json:"running"`      // Current running count
    MaxRunning  int             `json:"max_running"`  // From Config.Quota.Max
    LastRun     *string         `json:"last_run,omitempty"`     // ISO timestamp
    NextRun     *string         `json:"next_run,omitempty"`     // ISO timestamp
    RunningIDs  []string        `json:"running_ids,omitempty"`  // Execution IDs
    Config      *ConfigResponse `json:"config,omitempty"`       // Full config (for detail)
}

// NewRobotResponse converts backend Robot to API response
func NewRobotResponse(robot *types.Robot) *RobotResponse {
    return &RobotResponse{
        MemberID:    robot.MemberID,
        TeamID:      robot.TeamID,
        Name:        robot.MemberID,    // Use MemberID as unique identifier
        DisplayName: robot.DisplayName,
        Description: robot.Bio,          // Map Bio to Description
        Status:      string(robot.Status),
        // ... other fields
    }
}
```

### 4.2 ConfigResponse (robot config)

```go
// ConfigResponse - formatted config for API response
type ConfigResponse struct {
    Identity  *IdentityConfig  `json:"identity,omitempty"`
    Clock     *ClockConfig     `json:"clock,omitempty"`
    Events    []EventConfig    `json:"events,omitempty"`
    Quota     *QuotaConfig     `json:"quota,omitempty"`
    Resources *ResourcesConfig `json:"resources,omitempty"`
    Delivery  *DeliveryConfig  `json:"delivery,omitempty"`
    Triggers  *TriggersConfig  `json:"triggers,omitempty"`
    Learn     *LearnConfig     `json:"learn,omitempty"`
    Executor  *ExecutorConfig  `json:"executor,omitempty"`
}
```

### 4.3 ExecutionResponse

```go
// ExecutionResponse - formatted execution for API response
type ExecutionResponse struct {
    ID              string           `json:"id"`
    MemberID        string           `json:"member_id"`
    TeamID          string           `json:"team_id"`
    TriggerType     string           `json:"trigger_type"`
    StartTime       string           `json:"start_time"`
    EndTime         *string          `json:"end_time,omitempty"`
    Status          string           `json:"status"`
    Phase           string           `json:"phase"`
    Error           *string          `json:"error,omitempty"`
    JobID           string           `json:"job_id"`
    Name            string           `json:"name,omitempty"`             // Localized execution name
    CurrentTaskName string           `json:"current_task_name,omitempty"` // Localized current task
    Goals           *GoalsResponse   `json:"goals,omitempty"`
    Tasks           []TaskResponse   `json:"tasks,omitempty"`
    Current         *CurrentState    `json:"current,omitempty"`
    Delivery        *DeliveryResult  `json:"delivery,omitempty"`
}
```

### 4.4 ResultResponse

```go
// ResultResponse - deliverable file for Results tab
type ResultResponse struct {
    ID            string `json:"id"`
    MemberID      string `json:"member_id"`
    ExecutionID   string `json:"execution_id"`
    Name          string `json:"name"`
    Type          string `json:"type"`  // pdf, xlsx, csv, json, md
    Size          int64  `json:"size"`  // bytes
    CreatedAt     string `json:"created_at"`
    TriggerType   string `json:"trigger_type,omitempty"`
    ExecutionName string `json:"execution_name,omitempty"`
}
```

### 4.5 ActivityResponse

```go
// ActivityResponse - activity item
type ActivityResponse struct {
    ID          string `json:"id"`
    Type        string `json:"type"`  // completed | file | error | started | paused
    MemberID    string `json:"member_id"`
    RobotName   string `json:"robot_name"`   // Localized
    Title       string `json:"title"`        // Localized
    Description string `json:"description,omitempty"`  // Localized
    FileID      string `json:"file_id,omitempty"`
    Timestamp   string `json:"timestamp"`
}
```

---

## 5. Request Types

### 5.1 CreateRobotRequest

```go
// CreateRobotRequest - create robot request
type CreateRobotRequest struct {
    Locale      string          `json:"locale,omitempty"` // zh-CN | en-US
    Name        string          `json:"name"`             // Unique identifier
    DisplayName string          `json:"display_name"`     // Display name
    Email       string          `json:"email,omitempty"`  // Robot email
    ManagerID   string          `json:"manager_id,omitempty"`  // Manager user ID
    WorkMode    string          `json:"work_mode"`        // autonomous | on-demand
    Identity    *IdentityConfig `json:"identity"`
    Resources   *ResourcesConfig `json:"resources,omitempty"`
}
```

### 5.2 UpdateRobotRequest

```go
// UpdateRobotRequest - update robot request
type UpdateRobotRequest struct {
    Locale      string          `json:"locale,omitempty"`
    DisplayName *string         `json:"display_name,omitempty"`
    Config      *ConfigResponse `json:"config,omitempty"` // Partial update supported
}
```

### 5.3 TriggerRequest (SSE)

```go
// TriggerRequest - trigger robot execution
type TriggerRequest struct {
    Locale      string    `json:"locale,omitempty"`
    Messages    []Message `json:"messages"`
    Attachments []Attachment `json:"attachments,omitempty"`
}

// Message - chat message
type Message struct {
    Role    string `json:"role"`    // user | assistant
    Content string `json:"content"`
}

// Attachment - file attachment
type Attachment struct {
    File string `json:"file"` // __yao.attachment://fileID
    Name string `json:"name,omitempty"`
}
```

### 5.4 InterveneRequest (SSE)

```go
// InterveneRequest - intervene during execution
type InterveneRequest struct {
    Locale      string    `json:"locale,omitempty"`
    ExecutionID string    `json:"execution_id"`
    Action      string    `json:"action"`   // task.add | goal.adjust | instruct
    Messages    []Message `json:"messages"`
    Priority    string    `json:"priority,omitempty"` // high | normal | low
    Position    string    `json:"position,omitempty"` // first | last | next | at
}
```

---

## 6. Deferred Features

### 6.1 Multi-turn Chat API (Phase 5 - Deferred)

> **Risk Level:** ๐ŸŸก Medium - Requires new stateful component
> **Frontend Fallback:** Single-submit mode (user input โ†’ immediate execution)

The frontend `ChatDrawer` component expects multi-turn conversation before execution:

```
User: "Help me analyze competitor pricing"
       โ†“
Robot: "Got it. Which competitors?"
       โ†“
User: "Focus on Company A and B"
       โ†“
Robot: "Understood. Ready to start?"
       โ†“
User clicks [Confirm] โ†’ Execution starts
```

**Current backend behavior:** `Trigger()` immediately submits to execution pool.

**Deferred implementation:**
```
POST /v1/agent/robots/:id/chat
{
  "conversation_id": "conv_001",  // For continuing conversation
  "messages": [{ "role": "user", "content": "..." }]
}

Response (SSE):
event: message
data: {"role": "assistant", "content": "..."}

event: state
data: {"conversation_id": "conv_001", "ready_to_execute": false}
```

**For now:** Frontend can skip chat flow, directly call `/trigger` with user message.

### 6.2 SSE Event Bus (Phase 6 - Deferred)

> **Risk Level:** ๐ŸŸก Medium - Requires modification of executor/manager
> **Frontend Fallback:** Polling (GET /executions every 3-5 seconds)

Real-time status updates via SSE require an event bus integrated with:
- Manager (robot status changes)
- Executor (execution progress)

**For now:** Frontend uses polling to refresh status.

---

## 7. SSE Events

### 7.1 Trigger/Intervene SSE Events

```
event: received
data: {"message": "Task received, creating execution..."}

event: execution
data: {"execution_id": "exec_002", "status": "pending"}

event: message
data: {"role": "assistant", "content": "ๅฅฝ็š„๏ผŒๆˆ‘ๅผ€ๅง‹ๅค„็†..."}

event: phase
data: {"phase": "goals", "message": "ๆญฃๅœจ็”Ÿๆˆ็›ฎๆ ‡..."}

event: complete
data: {"execution_id": "exec_002", "status": "running"}

event: error
data: {"error": "Something went wrong"}
```

### 7.2 Robot Stream SSE Events (Phase 6 - Deferred)

```
event: robot_status
data: {"member_id": "robot_001", "status": "working", "running": 1}

event: execution_start
data: {"member_id": "robot_001", "execution_id": "exec_001", "name": "ๆฏๆ—ฅๆŠฅ่กจ็”Ÿๆˆ"}

event: execution_complete
data: {"member_id": "robot_001", "execution_id": "exec_001", "status": "completed"}

event: activity
data: {"id": "act_001", "type": "completed", "member_id": "robot_001", ...}
```

### 7.3 Execution Stream SSE Events (Phase 6 - Deferred)

```
event: phase
data: {"phase": "tasks", "progress": "2/5 tasks"}

event: task_start
data: {"task_id": "task_002", "order": 2}

event: task_complete
data: {"task_id": "task_002", "status": "completed"}

event: message
data: {"role": "assistant", "content": "ๆญฃๅœจๅˆ†ๆžๆ•ฐๆฎ..."}

event: delivery
data: {"summary": "...", "attachments": [...]}

event: complete
data: {"status": "completed"}

event: error
data: {"error": "Something went wrong", "phase": "run"}
```

---

## 8. i18n Support

### 8.1 Locale Detection

Priority order:
1. Query parameter: `?locale=zh-CN`
2. Request body field: `locale: "zh-CN"`
3. Accept-Language header
4. Default: `en-US`

### 8.2 Localized Fields

| Response Type | Localized Fields |
|---------------|------------------|
| RobotResponse | display_name, description |
| ExecutionResponse | name, current_task_name |
| TaskResponse | (none - tasks use executor_id) |
| ResultResponse | name, execution_name |
| ActivityResponse | robot_name, title, description |

---

## 9. Authentication & Authorization

### 9.1 Guard Middleware

All endpoints require OAuth2 authentication via `oauth.Guard` middleware.

```go
// In router registration
router.Use(oauth.Guard())
```

### 9.2 Permission Checks

| Endpoint | Required Scope |
|----------|----------------|
| GET /robots | `robots:read` |
| POST /robots | `robots:write` |
| PUT/DELETE /robots/:id | `robots:write` + ownership check |
| Trigger/Intervene | `robots:execute` |
| Stream endpoints | `robots:read` |

### 9.3 Team Isolation

Robots are team-scoped. Users can only access robots in their team.

```go
func checkTeamAccess(ctx context.Context, memberID string) error {
    auth := oauth.GetAuthorized(ctx)
    robot, _ := robotapi.Get(memberID)
    if robot.TeamID != auth.TeamID {
        return errors.New("access denied")
    }
    return nil
}
```

---

## 10. File Structure

### 10.1 Backend Store + API Layers

```
yao/agent/robot/
โ”œโ”€โ”€ store/                      # Store Layer (Core CRUD)
โ”‚   โ”œโ”€โ”€ store.go               # Common interfaces
โ”‚   โ”œโ”€โ”€ execution.go           # ExecutionStore (EXISTS)
โ”‚   โ””โ”€โ”€ robot.go               # RobotStore (NEW)
โ”‚
โ”œโ”€โ”€ api/                        # API Layer (Thin wrappers)
โ”‚   โ”œโ”€โ”€ robot.go               # Get, List, Create, Update, Remove
โ”‚   โ”œโ”€โ”€ execution.go           # Execution management
โ”‚   โ”œโ”€โ”€ trigger.go             # Trigger, Intervene
โ”‚   โ”œโ”€โ”€ results.go             # ListResults, GetResult (NEW)
โ”‚   โ””โ”€โ”€ activities.go          # ListActivities (NEW)
โ”‚
โ”œโ”€โ”€ types/                      # Type definitions
โ”‚   โ””โ”€โ”€ robot.go               # Add Bio field
โ”‚
โ””โ”€โ”€ cache/                      # Cache Layer
    โ””โ”€โ”€ load.go                # Add bio to memberFields
```

### 10.2 OpenAPI Layer

**Decision: Sub-package under `openapi/agent/`**

Robot logic is complex enough to warrant its own package. This keeps code organized and follows the pattern used by other complex modules.

```
yao/openapi/agent/
โ”œโ”€โ”€ agent.go            # Main route registration (MODIFY: add robot.Attach)
โ”œโ”€โ”€ assistant.go        # Assistant handlers (existing)
โ”œโ”€โ”€ filter.go           # Query filtering (existing)
โ”œโ”€โ”€ models.go           # LLM models (existing)
โ”œโ”€โ”€ types.go            # Types (existing)
โ”‚
โ””โ”€โ”€ robot/              # Robot sub-package (NEW)
    โ”œโ”€โ”€ DESIGN.md       # This document โœ…
    โ”œโ”€โ”€ TODO.md         # Implementation plan โœ…
    โ”œโ”€โ”€ GAPS.md         # Gap analysis โœ…
    โ”‚
    โ”œโ”€โ”€ robot.go        # Route registration (Attach function)
    โ”œโ”€โ”€ types.go        # Request/Response types
    โ”‚
    โ”œโ”€โ”€ list.go         # GET /v1/agent/robots
    โ”œโ”€โ”€ detail.go       # GET/POST/PUT/DELETE /v1/agent/robots/:id
    โ”‚
    โ”œโ”€โ”€ execution.go    # Execution list/detail/control handlers
    โ”œโ”€โ”€ trigger.go      # POST /trigger, POST /intervene (SSE)
    โ”‚
    โ”œโ”€โ”€ results.go      # GET /results, GET /results/:id
    โ”œโ”€โ”€ activities.go   # GET /activities
    โ”‚
    โ”œโ”€โ”€ stream.go       # GET /stream, GET /executions/:id/stream (SSE)
    โ”‚
    โ”œโ”€โ”€ filter.go       # Query param parsing helpers
    โ””โ”€โ”€ utils.go        # Locale, time formatting utilities
```

**Route Registration (in `openapi/agent/agent.go`):**

```go
import "github.com/yaoapp/yao/openapi/agent/robot"

func Attach(group *gin.RouterGroup, oauth types.OAuth) {
    group.Use(oauth.Guard)
    
    // Assistant routes (existing)
    group.GET("/assistants", ListAssistants)
    group.POST("/assistants", CreateAssistant)
    // ...
    
    // Robot routes (NEW)
    robot.Attach(group.Group("/robots"), oauth)
}
```

**Robot Route Registration (`robot/robot.go`):**

```go
package robot

func Attach(group *gin.RouterGroup, oauth types.OAuth) {
    // Robot CRUD
    group.GET("", ListRobots)
    group.POST("", CreateRobot)
    group.GET("/:id", GetRobot)
    group.PUT("/:id", UpdateRobot)
    group.DELETE("/:id", DeleteRobot)
    
    // Activities (before :id to avoid conflict)
    group.GET("/activities", ListActivities)
    group.GET("/stream", StreamRobots)
    
    // Execution management
    group.GET("/:id/executions", ListExecutions)
    group.GET("/:id/executions/:exec_id", GetExecution)
    group.GET("/:id/executions/:exec_id/stream", StreamExecution)
    group.POST("/:id/executions/:exec_id/pause", PauseExecution)
    group.POST("/:id/executions/:exec_id/resume", ResumeExecution)
    group.POST("/:id/executions/:exec_id/cancel", CancelExecution)
    group.POST("/:id/executions/:exec_id/retry", RetryExecution)
    
    // Trigger & Intervene (SSE)
    group.POST("/:id/trigger", TriggerRobot)
    group.POST("/:id/intervene", InterveneRobot)
    
    // Results
    group.GET("/:id/results", ListResults)
    group.GET("/:id/results/:result_id", GetResult)
}
```

---

## 11. Error Handling

### 11.1 Error Response Format

```json
{
  "error": {
    "code": "ROBOT_NOT_FOUND",
    "message": "Robot not found",
    "details": {
      "member_id": "robot_001"
    }
  }
}
```

### 11.2 Error Codes

| Code | HTTP Status | Description |
|------|-------------|-------------|
| ROBOT_NOT_FOUND | 404 | Robot does not exist |
| EXECUTION_NOT_FOUND | 404 | Execution does not exist |
| ROBOT_BUSY | 409 | Robot at max capacity |
| TRIGGER_DISABLED | 403 | Trigger type disabled |
| EXECUTION_NOT_RUNNING | 400 | Cannot pause/resume non-running execution |
| INVALID_REQUEST | 400 | Request validation failed |
| UNAUTHORIZED | 401 | Not authenticated |
| FORBIDDEN | 403 | No permission |

---

## 12. Implementation Notes

### 12.1 Backend Architecture: Store + API Layers

> **Principle:** Store layer handles database CRUD, API layer handles business logic.
> This enables reuse across Golang API, JSAPI, and Yao Process.

```
Consumers (Golang API / JSAPI / Yao Process)
              โ”‚
              โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚         API Layer (robot/api/)          โ”‚
โ”‚  Thin wrappers: validation, cache ops   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
              โ”‚
              โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚       Store Layer (robot/store/)        โ”‚
โ”‚  Core CRUD: RobotStore, ExecutionStore  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
              โ”‚
              โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚       Model Layer (__yao.member)        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```

### 12.2 Store Layer Extensions

**File: `store/robot.go` (NEW)** - Core Robot CRUD

```go
type RobotStore struct {
    modelID string  // "__yao.member"
}

func (s *RobotStore) Save(ctx context.Context, record *RobotRecord) error
func (s *RobotStore) Get(ctx context.Context, memberID string) (*RobotRecord, error)
func (s *RobotStore) List(ctx context.Context, opts *ListOptions) ([]*RobotRecord, error)
func (s *RobotStore) Delete(ctx context.Context, memberID string) error
func (s *RobotStore) UpdateConfig(ctx context.Context, memberID string, config map[string]interface{}) error
```

**File: `store/execution.go` (extend)**

```go
func (s *ExecutionStore) ListResults(ctx context.Context, memberID string, opts *ResultsQuery) ([]*ResultRecord, error)
func (s *ExecutionStore) GetResult(ctx context.Context, resultID string) (*ResultRecord, error)
func (s *ExecutionStore) ListActivities(ctx context.Context, opts *ActivityQuery) ([]*ActivityRecord, error)
```

### 12.3 API Layer Extensions

**File: `api/robot.go` (extend)** - Thin wrappers

```go
// Create - calls store.RobotStore.Save() + cache refresh
func Create(ctx *types.Context, teamID string, req *CreateRobotRequest) (*types.Robot, error)

// Update - calls store.RobotStore.UpdateConfig() + cache refresh
func Update(ctx *types.Context, memberID string, req *UpdateRobotRequest) (*types.Robot, error)

// Remove - calls store.RobotStore.Delete() + cache invalidate
func Remove(ctx *types.Context, memberID string) error
```

**File: `api/results.go` (NEW)**

```go
func ListResults(ctx *types.Context, memberID string, query *ResultQuery) (*ResultsResult, error)
func GetResult(ctx *types.Context, resultID string) (*ResultFile, error)
```

**File: `api/activities.go` (NEW)**

```go
func ListActivities(ctx *types.Context, query *ActivityQuery) (*ActivitiesResult, error)
```

### 12.4 Localization

Add `Locale` parameter support for localized responses.

### 12.5 SSE Implementation

Use standard Go SSE pattern:

```go
func streamHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")
    
    flusher, _ := w.(http.Flusher)
    
    for event := range events {
        fmt.Fprintf(w, "event: %s\ndata: %s\n\n", event.Type, event.Data)
        flusher.Flush()
    }
}
```

### 12.6 Localization Strategy

- Store display names in `__yao.member.display_name` (single language) initially
- Future: Add `display_name_cn`, `display_name_en` or use JSON `{"en": "...", "cn": "..."}`
- Execution names derived from goals or input message
- Activities derive titles from execution data

---

## 13. API Base Path Decision

Based on analysis of existing `openapi/` structure:

| Option | Path | Pros | Cons |
|--------|------|------|------|
| โŒ A | `/v1/robots` | Shorter path | New namespace, inconsistent |
| โœ… B | `/v1/agent/robots` | Groups with agent APIs, consistent | Longer path |
| โŒ C | `/v1/members?type=robot` | Uses existing members | Less intuitive |

**Decision**: Use `/v1/agent/robots` as base path.

**Rationale:**
1. `openapi/agent/` already exists with `/v1/agent/assistants`
2. Robot is conceptually an Agent type (Autonomous Robot Agent)
3. Follows the established pattern: `/v1/agent/{agent-type}`
4. Keeps agent-related APIs logically grouped

**Frontend Impact:**
- Update `cui/packages/cui/pages/mission-control/API.md` base path from `/v1/robots` to `/v1/agent/robots`
- Minimal code change (just update base URL constant)

---

## 14. References

- Frontend API Requirements: `cui/packages/cui/pages/mission-control/API.md`
- Backend Robot Design: `yao/agent/robot/DESIGN.md`
- Backend Technical Spec: `yao/agent/robot/TECHNICAL.md`
- Existing OpenAPI Patterns: `yao/openapi/kb/`, `yao/openapi/chat/`