# LarkPal

LarkPal connects Lark/Feishu conversations to agent runtimes. The first setup path is now organized around Feishu's Agent integration layers:

1. **凭据层**：用飞书官方扫码创建应用能力获取 App ID 和 App Secret。
2. **交互层**：LarkPal 的 Feishu channel 接收消息、评论、卡片交互并流式回复。
3. **执行层**：飞书 CLI 作为 Agent 操作文档、日历、表格、邮件、任务等业务对象的工具来源。

> Agent 协作入口：先读 [AGENTS.md](./AGENTS.md)，再按 [agent-workspace/INDEX.md](./agent-workspace/INDEX.md) 选择相关领域文档。

## 快速开始

### 1. 安装依赖

```bash
pnpm install
pnpm -s build
```

运行服务时仍需要一个 agent runtime：

```bash
npm install -g @anthropic-ai/claude-code
```

如果使用 Codex app-server runtime，请安装并配置 Codex CLI，并设置 `LARKPAL_RUNTIME=codex-app-server`。

### 2. 连接飞书

```bash
pnpm start -- init
```

初始化向导默认使用飞书官方一键创建应用能力。用户打开或扫码向导中的链接后，LarkPal 会收到并保存 App ID 和 App Secret；不需要去飞书开发者后台手动复制 App Secret。

也可以选择手动输入 App ID/App Secret，或使用旧的 `lark-cli config init` 兼容流程。

### 3. 按需授权执行层

基础飞书 bot 会话只需要应用凭据。只有当 Agent 需要以用户身份访问个人日历、私信、云文档、邮箱等数据时，才需要飞书 CLI 用户授权：

```bash
npx @larksuite/cli@latest install
lark-cli auth login
```

这一步属于执行层，不是应用凭据配置的一部分。

---

# LarkPal 外部接入规范

本文档描述外部业务方接入 LarkPal 平台时需遵循的协议和配置规范。

> Agent 协作入口：先读 [AGENTS.md](./AGENTS.md)，再按 [agent-workspace/INDEX.md](./agent-workspace/INDEX.md) 选择相关领域文档。

---

## 目录

1. [环境变量配置](#环境变量配置)
2. [Agent Completion API（作为 AI 能力消费方）](#agent-completion-api)
3. [Chat 流式对话 API（多端 Agent Chat）](#chat-流式对话-api)
4. [RuntimeAdapter 接口（自定义 AI 引擎）](#runtimeadapter-接口)
5. [MCP 扩展配置（仅 CC 模式）](#mcp-扩展配置)
6. [网关路由总览](#网关路由总览)

---

## 环境变量配置

### 必填

| 变量名 | 用途 | 示例 |
|--------|------|------|
| `LARK_APP_ID` | 飞书应用 App ID | `cli_xxx` |
| `LARK_APP_SECRET` | 飞书应用 App Secret | - |
| `ANTHROPIC_API_KEY` | Anthropic API Key（CC Runtime 需要） | `sk-ant-xxx` |

### 可选

| 变量名 | 用途 | 默认值 |
|--------|------|--------|
| `LARKPAL_RUNTIME` | 切换 RuntimeAdapter 实现 | `claude-code` |
| `CLAUDE_MODEL` | 指定 LLM 模型名 | CC 默认模型 |
| `LARKPAL_WORKSPACE` | 工作目录根路径 | `~/.larkpal/workspace` |
| `LARKPAL_GATEWAY_HOST` | 网关监听地址 | `localhost` |
| `LARKPAL_EXTERNAL_URL` | 外部可访问的网关 URL | - |
| `LARKPAL_PUBLIC_URL` | OAuth 回调公开 URL | `http://localhost:3000` |
| `LARKPAL_API_SECRET` | Agent Completion API 鉴权密钥 | `dev-internal-secret` |
| `LARKPAL_COMPLETION_CWD` | Agent Completion 工作目录 | `/tmp/larkpal-completion` |
| `LARKPAL_CC_IDLE_TIMEOUT` | CC 进程空闲超时（秒） | `0`（永不超时） |
| `LARKPAL_ENCRYPTION_KEY` | 用户凭证加密密钥（32字节 hex） | 未设置（明文，仅限开发） |
| `LARKPAL_PLUGINS` | 声明式加载的插件列表 | 空 |
| `LARKPAL_CHAT_JWT_SECRET` | Chat API JWT 签名密钥 | fallback 到 `LARKPAL_API_SECRET` |

会话工作目录默认写入 `LARKPAL_WORKSPACE` 下的隐藏目录：飞书会话为 `.chats/`，通用 Agent 会话为 `.sessions/`，Chat API 历史为 `.chat-history/`。

### larkpal-agent Runtime 专用（LARKPAL_RUNTIME=larkpal-agent 时必填）

| 变量名 | 用途 | 示例 |
|--------|------|------|
| `LARKPAL_AGENT_LLM_BASE_URL` | 火山方舟 LLM 端点 | `https://ark.cn-beijing.volces.com/api/v3` |
| `LARKPAL_AGENT_LLM_API_KEY` | 火山方舟 API Key | - |
| `LARKPAL_AGENT_LLM_MODEL` | 模型 endpoint ID | `ep-20250327140538-xxxxx` |
| `LARKPAL_AGENT_MAX_TURNS` | Agent 最大对话轮数（可选） | `25` |
| `LARKPAL_AGENT_MCP_SERVER_URL` | larkpal-agent HTTP MCP Server 地址（可选） | `https://.../api/mcp` |
| `LARKPAL_AGENT_SCENARIO_PROVIDER_TYPE` | Scenario Provider 类型（可选） | `local` / `http` / `mcp` |
| `LARKPAL_AGENT_SCENARIO_PROVIDER_BASE_URL` | HTTP Scenario Provider base URL（type=http 时必填） | `https://...` |
| `LARKPAL_AGENT_SCENARIO_PROVIDER_CONFIG` | Scenario Provider JSON 配置（优先级最高，可选） | `{"type":"http","baseURL":"https://..."}` |
| `LARKPAL_AGENT_SCENARIO_DIRS` | local scenario manifest 目录，多个目录用 `:` 分隔（可选） | `/opt/larkpal/scenarios` |

---

## Agent Completion API

外部业务方作为 AI 能力消费方，调用 LarkPal 的通用 AI 补全接口。

### 请求

```http
POST /api/agent/completion
Content-Type: application/json
X-Internal-Secret: {LARKPAL_API_SECRET}
X-Callback-Url: https://your-server/callback  (可选，有则为异步模式)

{
  "taskId": "your-unique-task-id",
  "task": "extract",                          // 可选，仅日志用
  "prompt": "请分析以下文档...",
  "files": [                                  // 可选，附件
    { "name": "doc.txt", "contentBase64": "..." }
  ],
  "options": {
    "maxTurns": 10,                           // 可选
    "resultFileName": "output.json"           // 可选，优先读取 CC 写入的此文件
  }
}
```

### 同步模式响应

无 `X-Callback-Url` 时，等待执行完成后返回：

```json
{ "code": 0, "mode": "sync", "result": "分析结果..." }
```

### 异步模式

有 `X-Callback-Url` 时，立即返回：

```json
{ "code": 0, "mode": "async", "message": "accepted" }
```

完成后 POST 回调到你的 URL：

```json
{ "taskId": "xxx", "status": "success", "result": "..." }
// 或
{ "taskId": "xxx", "status": "failed", "error": "timeout" }
```

---

## Chat 流式对话 API

为外部客户端（Web、移动端、桌面端等）提供流式 Agent Chat 能力，支持多会话管理和多端消息同步。

### 鉴权流程

1. 客户端通过飞书 OAuth 获取用户身份
2. 调用 LarkPal 签发 JWT：

```http
POST /api/chat/auth
X-Internal-Secret: {LARKPAL_API_SECRET}
Content-Type: application/json

{ "tenantKey": "xxx", "userId": "user_xxx", "openId": "ou_xxx" }
```

响应：
```json
{ "token": "eyJ...", "expiresAt": 1234567890 }
```

3. 后续所有 Chat API 请求携带：`Authorization: Bearer {token}`

### 会话管理

**列出会话**
```http
GET /api/chat/sessions
Authorization: Bearer {token}
```

**创建会话**
```http
POST /api/chat/sessions
Authorization: Bearer {token}
Content-Type: application/json

{ "title": "知识库诊断" }
```

**删除会话**
```http
DELETE /api/chat/sessions/{sessionId}
Authorization: Bearer {token}
```

### 流式对话（SSE）

```http
POST /api/chat/stream
Authorization: Bearer {token}
Content-Type: application/json
Accept: text/event-stream

{
  "session_id": "可选，不传则自动创建",
  "prompt": "帮我诊断知识库质量",
  "options": { "maxTurns": 10 }
}
```

**SSE 事件流**：

| 事件 | 说明 | data 示例 |
|------|------|-----------|
| `text-delta` | AI 回复文本增量 | `{"text": "好的"}` |
| `thinking-delta` | AI 思考过程增量 | `{"text": "我需要先..."}` |
| `tool-use-start` | 开始调用工具 | `{"toolName": "list-spaces", "input": {...}}` |
| `tool-result` | 工具调用结果 | `{"toolUseId": "xxx"}` |
| `tool-progress` | 工具执行进度 | `{"toolName": "xxx", "elapsedSeconds": 5}` |
| `turn-end` | 单轮结束 | `{"stopReason": "end_turn"}` |
| `done` | 对话完成 | `{"sessionId": "xxx", "durationMs": 3200}` |
| `error` | 错误 | `{"message": "..."}` |

### 历史消息

```http
GET /api/chat/history?session_id={id}&cursor={msgId}&limit=50
Authorization: Bearer {token}
```

返回该会话的完整消息历史（含飞书端和客户端两侧的消息）。

### 多端同步说明

- 消息 `channel` 字段标识来源通道：如 `'feishu-bot'`（飞书机器人端）、`'web'`（Web 客户端）、`'mobile'`（移动端）等
- 客户端始终能看到完整对话（含飞书端触发的消息）
- 飞书端仅显示飞书端的消息（受限于飞书 API，无法向 bot 会话注入外部消息）
- 用户通过 `openId` 关联，同一用户在不同端共享会话列表

---

## RuntimeAdapter 接口

自定义 AI 引擎需实现以下接口：

```typescript
interface RuntimeAdapter {
  readonly name: string;

  // 执行 prompt，通过 callbacks 流式返回结果
  executePrompt(config: RuntimeStartConfig, callbacks: RuntimeStreamCallbacks): Promise<void>;

  // 停止指定会话
  stopProcess(sessionId: string): Promise<void>;

  // 停止所有会话
  stopAll(): Promise<void>;

  // 查询会话进程信息
  getProcessInfo(sessionId: string): RuntimeProcessInfo | undefined;
  getAllProcessInfo(): RuntimeProcessInfo[];

  // 可选能力契约：上层通过能力判断是否展示交互入口
  getCapabilities?(): RuntimeCapabilities;
  getLastUserMessageId?(sessionId: string): string | undefined;
  previewRewind?(params: RuntimeRewindParams): Promise<RuntimeRewindPreview>;
  confirmRewind?(params: RuntimeRewindParams): Promise<RuntimeRewindResult>;
}
```

### RuntimeStartConfig

```typescript
interface RuntimeStartConfig {
  sessionId: string;
  cwd: string;
  prompt: string | CCContentBlock[];
  maxTurns?: number;
  maxBudgetUsd?: number;
  model?: string;
  userContext?: UserContext;
}
```

### RuntimeStreamCallbacks

```typescript
interface RuntimeStreamCallbacks {
  onTextDelta?: (text: string) => void;
  onThinkingDelta?: (text: string) => void;
  onToolUseStart?: (toolName: string, toolInput: unknown) => void;
  onToolResult?: (toolUseId: string) => void;
  onToolProgress?: (toolName: string, elapsedSeconds: number) => void;
  onTurnEnd?: (stopReason: string) => void;
  onResult?: (result: RuntimeResultMessage) => void;
  onError?: (error: Error) => void;
}
```

### 切换 Runtime

通过环境变量切换：

```bash
LARKPAL_RUNTIME=claude-code      # 默认，使用 Claude Code 子进程
LARKPAL_RUNTIME=codex-app-server # 使用 OpenAI Codex app-server
LARKPAL_RUNTIME=larkpal-agent    # 使用 larkpal-agent 引擎（火山方舟 LLM）
```

Runtime 能力矩阵：

| Runtime | 流式回复 | 多模态输入 | 停止当前任务 | 交互卡片续跑 | 文件撤销 |
|---------|----------|------------|--------------|--------------|----------|
| `claude-code` | 支持 | 支持 | 支持 | 支持 | 支持 |
| `codex-app-server` | 支持 | 支持 | 支持 | 支持 | 支持 |
| `larkpal-agent` | 支持 | 由 agent/provider 决定 | 支持 | 支持 | 暂不支持 |

使用 `larkpal-agent` 时的完整配置示例：

```bash
LARKPAL_RUNTIME=larkpal-agent
LARKPAL_AGENT_LLM_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
LARKPAL_AGENT_LLM_API_KEY=your-api-key
LARKPAL_AGENT_LLM_MODEL=ep-20250327140538-xxxxx
LARKPAL_AGENT_MAX_TURNS=25
```

**工具发现**：由 larkpal-agent 自身的 MCP Client 负责。larkpal-agent 启动时自动连接配置的 MCP Server，发现并注册外部工具。larkpal 平台层不涉及工具层。

**人设 / system instructions 来源**：LarkPal 以飞书应用信息和人设文档为唯一来源，同步到 runtime 各自能读取的指令文件。Claude Code 读取 `~/.claude/CLAUDE.md` 和会话目录 `CLAUDE.md`；Codex 读取 workspace / 会话目录中的 `AGENTS.md`，内容由 LarkPal 从同一份人设自动镜像生成。

---

## MCP 扩展配置（仅 CC 模式）

LarkPal 在 CC 模式下作为 MCP Client 代理层，支持双层 MCP Server 配置合并。larkpal-agent 模式下工具发现由 Agent 自身 MCP Client 负责。

| 层级 | 路径 | 说明 |
|------|------|------|
| 全局 | `~/.claude/mcp-servers.json` | 所有用户共享的 MCP Server |
| 用户私有 | `{workspace}/users/{userId}/mcp-servers.json` | 用户自定义（优先级更高） |

配置格式：

```json
{
  "mcpServers": {
    "my-server": {
      "command": "npx",
      "args": ["-y", "my-mcp-server"],
      "env": { "API_KEY": "xxx" }
    },
    "remote-server": {
      "url": "http://localhost:8080/mcp/sse"
    }
  }
}
```

合并规则：用户配置同名 key 覆盖全局配置。

---

## 网关路由总览

| 路由 | 方法 | 功能 |
|------|------|------|
| `/api/agent/completion` | POST | 通用 AI 补全（外部主接入点） |
| `/api/chat/auth` | POST | 签发 Chat JWT |
| `/api/chat/stream` | POST | SSE 流式对话 |
| `/api/chat/sessions` | GET/POST | 会话列表 / 创建会话 |
| `/api/chat/sessions/:id` | DELETE | 删除会话 |
| `/api/chat/history` | GET | 会话历史消息 |
| `/api/execute` | POST | 单次执行（飞书会话绑定） |
| `/api/execute/batch` | POST | 批量执行 |
| `/api/execute/:taskId` | GET | 查询任务状态 |
| `/api/execute/:taskId/cancel` | POST | 取消任务 |
| `/api/skills/*` | - | 技能管理 |
| `/api/scheduled-tasks` | - | 定时任务 CRUD |
| `/hooks/*` | POST | 飞书事件 Hook 接收 |

---

## 安全注意事项

1. `LARK_APP_SECRET` 启动后从 `process.env` 中删除，防止子进程继承
2. `ANTHROPIC_API_KEY` 保留（CC 子进程需要），但通过 `settings.json` deny 规则禁止 CC 通过 shell 读取
3. `LARKPAL_API_SECRET` 用于 Agent Completion API 鉴权，生产环境务必设置强密钥
4. 用户凭证加密需设置 `LARKPAL_ENCRYPTION_KEY`（32 字节 hex/base64），否则明文存储
