# pi-starter 参考手册

> **本文档由 `scripts/visualization/generate_reference.mjs` 从源码生成，请勿手改。**
>
> 事实源：`src/protocol.ts`（协议）、`src/http/*` + `src/app.ts` + `src/server.ts`（REST）、
> `src/tools/*`（工具）、`.env.example`（环境变量）、`package.json`（脚本）。
> `npm run docs:reference:check` 在 CI 里挡住漂移；生成过程本身还带一致性断言，
> 新增协议命令/工具却忘了归类会直接让门禁红，而不是让这份文档悄悄少一条。

## 1. WS 协议（真正的对外接口）

协议版本 `PROTOCOL_VERSION = 1`。客户端命令 **34** 条，服务端帧 **21** 条。

说明覆盖率：客户端 4/34，服务端 7/21（说明取自 src/protocol.ts 里分支上方的 JSDoc；没有 JSDoc 就是空。补说明是持续动作，刻意不设成门禁——逼出来的空说明比没有说明更糟）。

### 1.1 客户端 → 服务端

| 命令 | 说明 |
|---|---|
| **连接与状态** | |
| `hello` |  |
| `get_state` |  |
| `ping` |  |
| **对话运行** | |
| `prompt` | 发一轮 |
| `abort` |  |
| `compact_context` | 主动压缩上下文 |
| `draft_update` |  |
| **会话编排** | |
| `new_conversation` |  |
| `open_conversation` |  |
| `switch_conversation` |  |
| `close_conversation` |  |
| `delete_conversation` | 真删除一条对话：内存卸掉 + 索引去掉 + 磁盘会话文件删掉 |
| `list_conversations` |  |
| `rename_conversation` |  |
| `rollback_conversation` |  |
| `edit_message` |  |
| `fork_conversation` |  |
| `set_label` | 给会话条目打/清官方标签（label 为空串或未传即清除） |
| **模型与思考** | |
| `list_models` |  |
| `set_model` |  |
| `set_thinking` |  |
| `cycle_model` |  |
| `cycle_thinking` |  |
| **能力目录** | |
| `get_capabilities` |  |
| `set_tool_enabled` |  |
| `search_knowledge` |  |
| **审批** | |
| `approval_response` |  |
| **HITL 反问应答（对 extension_ui_request 的回复，按 id 匹配）** | |
| `extension_ui_response` |  |
| **对话进行中的干预（idle 时会被拒绝，改用 prompt）** | |
| `steer` |  |
| `follow_up` |  |
| `abort_compaction` |  |
| **运行模式** | |
| `set_plan_mode` |  |
| **设置** | |
| `get_settings` |  |
| `set_settings` |  |

### 1.2 服务端 → 客户端

| 帧 | 说明 |
|---|---|
| `ready` | — |
| `snapshot` | — |
| `snapshot_delta` | — |
| `message_delta` | — |
| `tool_status` | — |
| `run_start` | 一轮开始（agent_start） |
| `run_end` | 一轮结束（agent_end）——**本轮唯一的权威结束信号** |
| `tool_delta` | 工具的流式中间输出（tool_execution_update） |
| `turn_start` | 一次 ReAct 迭代开始（SDK `turn_start`） |
| `turn_end` | 一次 ReAct 迭代结束（SDK `turn_end`） |
| `conversations` | — |
| `edit_ready` | 编辑用户消息之后，原文交回输入框 |
| `models` | — |
| `capabilities` | — |
| `settings_state` | — |
| `knowledge_hits` | — |
| `approval_request` | — |
| `extension_ui_request` | HITL 反问：把官方 extension_ui_request 推到 WS，阻塞等 extension_ui_response |
| `notice` | — |
| `pong` | — |
| `error` | — |

## 2. REST 接口

共 **47** 个路由处理器。

### `src/app.ts`

- `GET /info`
- `GET /providers`
- `POST /chat`
- `POST /model`
- `POST /model/cycle`

### `src/http/approval-routes.ts`

- `DELETE /approval/rules/:id`
- `GET /approval/rules`
- `POST /approval/rules`
- `PUT /approval/rules`

### `src/http/file-routes.ts`

- `GET /files/list`
- `GET /files/raw`
- `GET /files/read`
- `POST /files/copy`
- `POST /files/create`
- `POST /files/delete`
- `POST /files/rename`
- `POST /files/upload`
- `POST /files/write`

### `src/http/log-routes.ts`

- `GET /logs`
- `GET /logs/stats`

### `src/http/provider-key-routes.ts`

- `GET /provider-keys`
- `POST /provider-keys`
- `POST /provider-keys/activate`
- `POST /provider-keys/remove`

### `src/http/routes.ts`

- `DELETE /memory/:id`
- `GET /capabilities`
- `GET /db`
- `GET /db/notes`
- `GET /db/notes/:id`
- `GET /health`
- `GET /health/ready`
- `GET /knowledge`
- `GET /knowledge/:name`
- `GET /knowledge/search`
- `GET /memory`
- `GET /metrics`
- `GET /prompt-templates`
- `GET /prompt-templates/:name`
- `GET /settings`
- `GET /skills`
- `GET /skills/:name`
- `PATCH /settings`
- `POST /context/compact`
- `POST /db/query`
- `POST /memory`
- `POST /sessions/import`
- `POST /tools/:name/enabled`

## 3. 工具

### 3.1 脚手架自带工具（按装配档位分组）

| 工具 | 能力标签 | 风险 | 默认可用 |
|---|---|---|---|
| `ask_user_question` | human.input | low | 是 |
| `current_time` | custom | low | 是 |
| `read_knowledge` | custom | low | 否（知识库（扫描到 `src/knowledge/*.md` 时）） |
| `search_knowledge` | custom | low | 否（知识库（扫描到 `src/knowledge/*.md` 时）） |
| `db_query` | custom | low | 否（数据库（连接成功时）） |
| `db_status` | custom | low | 否（数据库（连接成功时）） |
| `exec` | shell | high | 否（`PI_BUILTIN_TOOLS=coding`） |
| `exec_jobs` | shell.observe | low | 否（`PI_BUILTIN_TOOLS=coding`） |
| `exec_stop` | shell | high | 否（`PI_BUILTIN_TOOLS=coding`） |
| `web_fetch` | net | medium | 否（`PI_WEB=on`） |
| `web_search` | net | medium | 否（`PI_WEB=on`） |
| `recall` | memory | low | 否（记忆（`PI_MEMORY`，默认开）） |
| `remember` | memory | low | 否（记忆（`PI_MEMORY`，默认开）） |

> 装配档位说明：
> - 始终可用 → `ask_user_question`, `current_time`
> - 知识库（扫描到 `src/knowledge/*.md` 时） → `read_knowledge`, `search_knowledge`
> - 数据库（连接成功时） → `db_query`, `db_status`
> - `PI_BUILTIN_TOOLS=coding` → `exec`, `exec_jobs`, `exec_stop`
> - `PI_WEB=on` → `web_fetch`, `web_search`
> - 记忆（`PI_MEMORY`，默认开） → `recall`, `remember`

### 3.2 SDK 内置工具（按 `PI_BUILTIN_TOOLS` 档位）

| 档位 | 工具 |
|---|---|
| `off` | `read` |
| `readonly` | `read`, `grep`, `find`, `ls` |
| `coding` | `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls` |

> 另：`PI_WEB=on` 时额外注册 `web_fetch`, `web_search`（注入的后端没有 `search()` 时不含 `web_search`）。MCP 工具由外部 server 提供，不在此表。

## 4. 环境变量

`.env.example` 登记了 **58** 个（按变量名去重）；下表的「默认」列里，未注释的即为默认启用的写法。

| 变量 | 默认 | 说明 |
|---|---|---|
| `PI_MODEL` | 启用 | 默认模型。两种写法都行： |
| `PI_PROVIDER` | 启用 | 命令行 --model / --provider 优先级更高。 模型 id 本身带斜杠（如 Qwen/Qwen3-...）时，用第一种写法，斜杠会原样保留。 |
| `PI_MODELS` | 注释（示例） | 每条：provider|baseUrl|api|modelId[:显示名],modelId[:显示名] 多条用分号隔开。换行请保持在同一行，.env 不支持续行。 |
| `PI_BASE_URL` | 启用 | 只在没写 PI_MODELS 时生效 |
| `PI_API` | 注释（示例） | — |
| `PI_SCOPED_MODELS` | 注释（示例） | 模型轮换列表（官方 scopedModels + cycleModel / WS cycle_model / REST POST /model/cycle / CLI /cycle）。 逗号分隔，每项 provider/modelId[:thinkingLevel]。不填则默认用所有已配 Key 的模型派生一份。 |
| `PI_KNOWLEDGE_RETRIEVAL` | 注释（示例） | 知识检索后端。默认 keyword（进程内关键词，零依赖，行为与以前一致）。 设 vector 则走官方 RAG 入口的可插拔向量检索：启动时把知识库切段→ embedding → cosine。 |
| `PI_EMBEDDINGS_PROVIDER` | 注释（示例） | embedding 源 PI_EMBEDDINGS_PROVIDER：openai（默认）| ollama | transformers（进程内，不需外部服务） openai / ollama 需 base+model（Ollama 的 /v1/embeddings 也走 openai 兼容）： transformers：首次用自动从 HF Hub 下载 ONNX 权重到 cacheDir、进程内跑（需 npm i @huggingface/transformers） |
| `PI_EMBEDDINGS_BASE_URL` | 注释（示例） | — |
| `PI_EMBEDDINGS_MODEL` | 注释（示例） | — |
| `PI_EMBEDDINGS_KEY` | 注释（示例） | — |
| `PI_EMBEDDINGS_CACHE_DIR` | 注释（示例） | — |
| `PI_EMBEDDINGS_HF_ENDPOINT` | 注释（示例） | 下载主机（默认 huggingface.co；直连不通时可用镜像）： |
| `PI_KNOWLEDGE_VECTOR_STORE` | 注释（示例） | 向量库：memory（默认）| sqlite（重启不重算，持久化到文件） |
| `PI_KNOWLEDGE_VECTOR_DB_PATH` | 注释（示例） | — |
| `PI_API_KEY` | 启用 | 默认 provider（PI_PROVIDER）用 PI_API_KEY；其余 provider 用 PI_API_KEY_<PROVIDER>（大写）。 已有密钥默认保留，覆盖才加 --force。切勿把填好的 .env 提交进 git。 |
| `PI_API_KEY_ZHIPU` | 注释（示例） | — |
| `PI_WEB` | 注释（示例） | （best-effort HTML 解析，对方改版即抽不到 —— 生产请注入自己的后端，见 lib.ts 的 WebClient）。 换后端不改工具契约：buildAgent({ webClient }) 传一个实现即可（换搜索源 / 加缓存 / 加审计）。 |
| `PI_WEB_MAX_BYTES` | 注释（示例） | — |
| `PI_WEB_TIMEOUT_MS` | 注释（示例） | — |
| `PI_MEMORY` | 注释（示例） | 存储是 JSONL（默认 ~/.pi/agent/pi-starter-memory.jsonl），原子写；单条 4KB、总量 2000 条封顶。 只有明确的 off / false / 0 才算关；不写 = 开。 |
| `PI_MEMORY_PATH` | 注释（示例） | — |
| `PI_BUILTIN_TOOLS` | 启用 | readonly = 再打开 grep / find / ls coding = 再打开 bash / edit / write（能改磁盘、能跑 shell） |
| `PI_DATABASE_PATH` | 注释（示例） | 数据库路径。不填 = 内存 SQLite（每次启动都是示例 notes） |
| `PI_COMPACTION_ENABLED` | 注释（示例） | SDK 设置透传（交给官方 SettingsManager，驱动自动压缩/重试/图像/模型白名单）。全部留空则不建 SettingsManager，行为与以往一致。 自动压缩：阈值相关 token 预算（reserveTokens 预留、keepRecentTokens 保留最近）、总开关 enabled |
| `PI_COMPACTION_RESERVE_TOKENS` | 注释（示例） | — |
| `PI_COMPACTION_KEEP_RECENT_TOKENS` | 注释（示例） | — |
| `PI_RETRY_ENABLED` | 注释（示例） | LLM 自动重试：enabled / 最大重试次数 / 基础退避毫秒 |
| `PI_RETRY_MAX_RETRIES` | 注释（示例） | — |
| `PI_RETRY_BASE_DELAY_MS` | 注释（示例） | — |
| `PI_IMAGES_AUTO_RESIZE` | 注释（示例） | 图像：autoResize 自动降采、block 屏蔽图片输入 |
| `PI_IMAGES_BLOCK` | 注释（示例） | — |
| `PI_ENABLED_MODELS` | 注释（示例） | 模型白名单（官方 enabledModels，逗号分隔 provider/modelId）；不设则所有已配 Key 模型可用 |
| `PI_HTTP_IDLE_TIMEOUT_MS` | 注释（示例） | 出站 provider HTTP 空闲超时 / WebSocket 连接超时（毫秒）——区别于入站服务器超时 |
| `PI_WS_CONNECT_TIMEOUT_MS` | 注释（示例） | — |
| `PI_STEERING_MODE` | 注释（示例） | 队列消费策略（all=一次性全部消费 / one-at-a-time=逐条） |
| `PI_FOLLOW_UP_MODE` | 注释（示例） | — |
| `PI_THINKING_BUDGET_MINIMAL` | 注释（示例） | 各思考档 token 预算 |
| `PI_THINKING_BUDGET_LOW` | 注释（示例） | — |
| `PI_THINKING_BUDGET_MEDIUM` | 注释（示例） | — |
| `PI_THINKING_BUDGET_HIGH` | 注释（示例） | — |
| `PI_BRANCH_SUMMARY_RESERVE_TOKENS` | 注释（示例） | 分支摘要：预留 token / 是否跳过写提示（true=不弹确认直接摘） |
| `PI_BRANCH_SUMMARY_SKIP` | 注释（示例） | — |
| `PI_EXTENSION_PATHS` | 注释（示例） | 官方扩展文件路径装载（DefaultResourceLoader.additionalExtensionPaths，逗号分隔 .ts/.js）。 与 extraExtensions 内联工厂并列；noExtensions 只关 ~/.pi 扫描，不影响这里。 |
| `PI_LOG_LEVEL` | 注释（示例） | 日志。级别 debug/info/warn/error/silent（默认 info；开发想全开设 debug）。 |
| `PI_LOG_TO_FILE` | 注释（示例） | 是否把结构化日志落盘到文件（默认 true；关则只 stdout）。JSONL 每行一条。 |
| `PI_LOG_DIR` | 注释（示例） | 落盘目录（相对路径按进程 cwd 解析，默认 ./logs；不进版本库/发布包）。 |
| `PI_LOG_MAX_SIZE_MB` | 注释（示例） | 单文件轮转上限 MB（超过即 gzip 归档并重开，默认 50；0 = 不按大小轮转）。 |
| `PI_LOG_RETENTION_DAYS` | 注释（示例） | 归档保留天数（超期清理 .log 与 .log.gz，默认 14；0 = 不清理）。 |
| `PI_HOST` | 注释（示例） | 监听地址。默认 127.0.0.1（仅本机）；设成非回环地址会启动告警，而且**没有内置鉴权**—— 暴露前请自行加反向代理 + 鉴权（见 SECURITY.md）。 |
| `PI_WS_PATH` | 注释（示例） | WebSocket 路径（没写前导斜杠会自动补上）。 |
| `PI_PROTOCOL_VERSION` | 注释（示例） | 对外宣告的协议版本。默认取 PROTOCOL_VERSION 单一真源，只在刻意跑版本不匹配的构建时才覆盖。 |
| `PI_SNAPSHOT_INTERVAL_MS` | 注释（示例） | 空闲时的快照间隔（毫秒）。默认 60ms：客户端靠 rev 链自愈，丢帧不影响正确性。 |
| `PI_STREAMING_SNAPSHOT_INTERVAL_MS` | 注释（示例） | 流式中的快照间隔（毫秒）。默认 2000ms：流式期间增量帧负责实时性，全量投影刻意放慢。 |
| `PI_WS_HEARTBEAT_MS` | 注释（示例） | WS 心跳间隔（毫秒）。 |
| `PI_SNAPSHOT_RETRY_MS` | 注释（示例） | 背压丢快照后重建的重试间隔（毫秒）。 |
| `PI_WS_BACKPRESSURE_BYTES` | 注释（示例） | 发送缓冲超过该字节数时可丢弃快照（增量帧绕过背压，永远可达）。 |
| `PI_WS_MAX_CONSECUTIVE_DROPS` | 注释（示例） | 连续丢弃多少次快照后判定客户端太慢并断开（0 = 永不断开）。 |

## 5. npm 脚本

| 脚本 | 命令 |
|---|---|
| `npm run build` | `npm run clean && tsc -p tsconfig.build.json && node scripts/dist-assets.cjs copy` |
| `npm run clean` | `node scripts/dist-assets.cjs clean` |
| `npm run dev` | `tsx src/index.ts` |
| `npm run docs:check` | `npm run docs:numbers:check && npm run docs:reference:check && npm run docs:svg:check && npm run docs:overview:check` |
| `npm run docs:numbers` | `node scripts/visualization/generate_readme_numbers.mjs` |
| `npm run docs:numbers:check` | `node scripts/visualization/generate_readme_numbers.mjs --check` |
| `npm run docs:overview` | `node scripts/visualization/generate_overview.mjs` |
| `npm run docs:overview:check` | `node scripts/visualization/generate_overview.mjs --check` |
| `npm run docs:reference` | `tsx scripts/visualization/generate_reference.mjs` |
| `npm run docs:reference:check` | `tsx scripts/visualization/generate_reference.mjs --check` |
| `npm run docs:svg` | `node scripts/visualization/generate_architecture.mjs && node scripts/visualization/generate_request_flow.mjs && node scripts/visualization/generate_retrieval.mjs` |
| `npm run docs:svg:check` | `node scripts/visualization/generate_architecture.mjs --check && node scripts/visualization/generate_request_flow.mjs --check && node scripts/visualization/generate_retrieval.mjs --check` |
| `npm run e2e` | `tsx scripts/e2e-restart.mjs` |
| `npm run lint:unused` | `tsc --noEmit --noUnusedLocals --noUnusedParameters` |
| `npm run probe:providers` | `node scripts/probe-providers.mjs` |
| `npm run publish:local` | `node scripts/publish-local.mjs` |
| `npm run rag:smoke` | `tsx scripts/rag-smoke.mjs` |
| `npm run release` | `node scripts/release.mjs` |
| `npm run setup` | `tsx src/setup.ts` |
| `npm run smoke` | `tsx scripts/smoke-ws.mjs` |
| `npm run test` | `tsx --test src/client-session.test.ts src/config.test.ts src/models.test.ts src/sdk-adapter.test.ts src/extension-ui-bridge.test.ts src/sse.test.ts src/app.test.ts src/backend.test.ts src/infra.test.ts src/integration.test.ts src/extensions.test.ts src/files/service.test.ts src/store.test.ts src/approval/watchdog.test.ts src/errors.test.ts src/http/rate-limit.test.ts src/extensions/guard.test.ts src/extensions/hooks.example.test.ts src/setup.test.ts src/skills/index.test.ts src/prompt-templates/index.test.ts src/knowledge/index.test.ts src/knowledge/retrieval.test.ts src/knowledge/embeddings.test.ts src/knowledge/vector-store-sqlite.test.ts src/prompt-images.test.ts src/extensions/audit.test.ts src/tools/ask-user-question.test.ts src/tools/exec.test.ts src/tools/current-time.test.ts src/tools/resource.test.ts src/tools/memory.test.ts src/db/index.test.ts src/sessions/store.test.ts src/sessions/resume.test.ts src/sessions/edit.test.ts src/sessions/cap.test.ts src/http/file-routes.test.ts src/http/approval-routes.test.ts src/mcp/mcp.test.ts src/modes/plan-mode.test.ts src/subagents/subagents.test.ts src/provider-keys.test.ts src/memory/store.test.ts src/tools/registry.test.ts src/tools/web.test.ts src/exec/runner.test.ts src/context-compact.test.ts src/test-tmp.test.ts src/settings.test.ts src/graceful.test.ts src/log-sink-file.test.ts src/http/log.test.ts` |
| `npm run test:coverage` | `node scripts/coverage.mjs` |
| `npm run test:web` | `tsx --test --tsconfig web/tsconfig.json web/src/pi/client.test.ts web/src/pi/logClient.test.ts` |
| `npm run typecheck` | `tsc --noEmit` |
| `npm run ui:build` | `npm --prefix web run build` |
| `npm run ui:dev` | `npm --prefix web run dev` |
| `npm run verify` | `npm run typecheck && npm run lint:unused && npm test && npm run test:web && npm run docs:check && npm run verify:audit && npm run smoke && npm run build && npm run verify:embed` |
| `npm run verify:all` | `npm run verify && npm run e2e` |
| `npm run verify:audit` | `node scripts/verify-audit.mjs` |
| `npm run verify:embed` | `node scripts/verify-embed.mjs` |
| `npm run web` | `tsx src/server.ts` |

## 6. 模块地图（按代码量降序）

| 模块 | 文件数 | 行数 |
|---|---|---|
| `src/http/` | 9 | 1753 |
| `src/tools/` | 9 | 1456 |
| `src/conversation/` | 2 | 1298 |
| `src/approval/` | 4 | 1091 |
| `src/transport/` | 1 | 911 |
| `src/agent.ts` | 1 | 845 |
| `src/knowledge/` | 5 | 837 |
| `src/mcp/` | 2 | 754 |
| `src/config.ts` | 1 | 720 |
| `src/extensions/` | 10 | 687 |
| `src/client-session.ts` | 1 | 541 |
| `src/protocol.ts` | 1 | 535 |
| `src/exec/` | 1 | 531 |
| `src/lib.ts` | 1 | 504 |
| `src/session-hub.ts` | 1 | 492 |
| `src/app.ts` | 1 | 470 |
| `src/server.ts` | 1 | 465 |
| `src/sessions/` | 2 | 465 |
| `src/files/` | 1 | 451 |
| `src/settings.ts` | 1 | 445 |
| `src/db/` | 1 | 365 |
| `src/setup.ts` | 1 | 322 |
| `src/memory/` | 1 | 318 |
| `src/log-sink-file.ts` | 1 | 313 |
| `src/subagents/` | 1 | 300 |
| `src/log.ts` | 1 | 269 |
| `src/provider-keys.ts` | 1 | 239 |
| `src/modes/` | 1 | 222 |
| `src/errors.ts` | 1 | 211 |
| `src/skills/` | 1 | 208 |
| `src/models.ts` | 1 | 184 |
| `src/metrics.ts` | 1 | 182 |
| `src/context/` | 1 | 168 |
| `src/extension-ui-bridge.ts` | 1 | 166 |
| `src/index.ts` | 1 | 143 |
| `src/prompt-templates/` | 1 | 142 |
| `src/snapshot.ts` | 1 | 127 |
| `src/test-server.ts` | 1 | 127 |
| `src/sdk-adapter.ts` | 1 | 111 |
| `src/prompts/` | 1 | 110 |
| `src/graceful.ts` | 1 | 97 |
| `src/sse.ts` | 1 | 89 |
| `src/secret-files.ts` | 1 | 61 |
| `src/test-tmp.ts` | 1 | 52 |
| `src/rpc.ts` | 1 | 46 |
| `src/capabilities.ts` | 1 | 45 |
| `src/prompt-images.ts` | 1 | 45 |
| `src/cli-args.ts` | 1 | 36 |
| `src/child-env.ts` | 1 | 34 |

---

*生成于 2026-10-10 的工作区源码。重新生成：`npm run docs:reference`。*
