# Agent API Endpoints

本文档以当前后端实现为准，描述 `/api/v1/agent` 的 HTTP 接口契约。

Base URL: `/api/v1/agent`

---

## 端点清单

| 方法 | 路径 | 说明 |
|---|---|---|
| POST | `/runs` | 创建一次 agent run（异步入队） |
| GET | `/runs/{thread_id}/events` | 订阅 SSE 事件流 |
| GET | `/history` | 获取历史快照（按天分页） |
| POST | `/attachments` | 上传用户图片附件 |
| GET | `/attachments/signed-url` | 获取附件临时签名链接 |
| POST | `/transcribe` | WAV 音频转写 |

---

## 1) POST `/runs`

发起一次运行请求，后端会先持久化用户消息，再将命令放入异步队列。

### Request

- Body: `RunAgentInput`
- 详细结构见 `docs/protocols/agent/run-agent-input.md`

### Response

`202 Accepted`

```ts
{
  taskId: string;
  threadId: string;
  runId: string;
  created: boolean; // 是否新建了会话
}
```

### 错误码

- `401` 未认证
- `422` 请求结构校验失败

---

## 2) GET `/runs/{thread_id}/events`

订阅指定 thread 的实时事件流。

### Path

| 参数 | 类型 | 说明 |
|---|---|---|
| `thread_id` | string | 会话 ID |

### Query

| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
| `idle_limit` | integer | `300` | 最大空闲轮询次数（1-3600） |

### Headers

| Header | 必填 | 说明 |
|---|---|---|
| `Accept: text/event-stream` | 否 | 建议显式设置 |
| `Last-Event-ID` | 否 | 从指定游标断点续流 |

### Response

- `200 OK`
- `Content-Type: text/event-stream`
- 事件类型与字段见 `docs/protocols/agent/sse-events.md`
- usage 审计与成本回退策略见 `docs/protocols/agent/sse-events.md`（5) Usage 审计协议）
- 空闲时会发送 keep-alive 注释行 `: keep-alive`

### 错误码

- `401` 未认证
- `403` 非会话所有者
- `422` `Last-Event-ID` 非法
- `429` 超过 SSE 连接数限制

---

## 3) GET `/history`

返回历史快照（`HistorySnapshotResponse`），不是 SSE 包装事件。

### Query

| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `threadId` | string | 否 | 指定会话，不传则取当前用户最近会话 |
| `before` | `YYYY-MM-DD` | 否 | 返回该日期之前最近一天的快照 |

### Response

```ts
{
  scope: "history_day";
  threadId: string | null;
  day: string | null;      // YYYY-MM-DD
  hasMore: boolean;
  messages: Array<{
    id: string;
    seq: number;
    role: "user" | "assistant";
    content: string;
    attachments?: Array<{       // user 附件签名链接列表
      mimeType: string;
      url: string;
    }>;
    ui_schema?: object | null; // assistant 的编译后 UI
    timestamp: string;         // ISO-8601
  }>;
}
```

tool 消息在存储层用于运行时上下文续接，不在 `/history` 对外返回。续接时以 `metadata.tool_agent_output` 作为主信源（`content` 为轻量摘要）。

### 说明

- 若用户没有任何会话：返回
  - `threadId = null`
  - `day = null`
  - `hasMore = false`
  - `messages = []`

---

## 4) POST `/attachments`

上传图片附件，返回可直接用于 `RunAgentInput.messages[].content[].url` 的签名链接。

### Request

- `multipart/form-data`

| 字段 | 类型 | 说明 |
|---|---|---|
| `threadId` | string | 会话 ID |
| `file` | file | 附件文件 |

### Response

```ts
{
  attachment: {
    bucket: string;
    path: string;
    mimeType: string;
    url: string;
  };
}
```

### 错误码

- `401` 未认证
- `403` 非会话所有者
- `413` 附件超过大小限制
- `422` 文件类型不支持/空文件等
- `503` 存储服务不可用

---

## 5) GET `/attachments/signed-url`

对已有存储对象重新签名。

### Query

| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `bucket` | string | 是 | 存储桶 |
| `path` | string | 是 | 对象路径 |

### Response

```ts
{
  bucket: string;
  path: string;
  url: string;
}
```

---

## 6) POST `/transcribe`

WAV 音频转写。

### Request

- `multipart/form-data`

| 字段 | 类型 | 说明 |
|---|---|---|
| `audio` | file | WAV 文件 |

### Response

```ts
{
  transcript: string;
}
```

### 限制

- 内容类型：`audio/wav` / `audio/x-wav` / `audio/wave`
- 文件大小：最大 10MB

---

## 通用错误

当前实现的错误主体为 FastAPI `detail` 字段：

```json
{
  "detail": "..."
}
```
