feat: 接入 Pi 内置 codemode,并修复全面审查发现的问题

- codemode:Tool Search 常驻(alwaysEnabled)、权限 allow(嵌套调用仍逐个过权限门)、
  tools.jsonc 登记使用规则;RTK 跳过 parentToolCallId 嵌套调用,避免丢失 bash
  structuredContent;Tool Search 目录过滤 hidden / codemode(script-only)exposure;
  扩展加载测试加载 builtin:codemode 并断言其常驻激活
- 搜索 key 不再经命令行参数传递:search-auth.mjs 改为 --set-stdin(先读校验再加锁),
  configure.sh 交互输入经管道、非交互用 --<provider> -
- CodeGraph 客户端监听子进程 stdin error,避免 EPIPE 未处理导致崩溃
- 移除 PI_TOOL_SEARCH/HERMES_MEMORY/RTK_CONFIG_PATH 环境变量入口,测试改用工厂 configPath 注入
- pi-subagents 固定 vitest / coverage-istanbul 4.1.10,修复 npm ls 版本冲突
- 补 pi-web-search / pi-worktree UPSTREAM.md 与 pi-minimal-footer AGENTS.md,
  files 白名单补齐并加测试;.codegraph/.gitignore 移出跟踪
- detect_kitty 优先识别官方安装目录

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
云服务部-叶林立
2026-10-21 23:18:34 -07:00
co-authored by Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent 56cbc83296
commit f9a7f743dd
46 changed files with 585 additions and 198 deletions
+1 -1
View File
@@ -5,4 +5,4 @@
- `web_fetch` 固定走 Exa `web_fetch_exa`,失败时静默降级 Keenable → Tavily;Exa 搜索统一走 `web_search_advanced_exa` 并显式约束 `numResults` 与 `textMaxCharacters`;Tavily 默认 `search_depth: basic` 且必须在 `auth.json` 存在 `tavily` api_key 凭据才参与路由;Keenable keyless 可用,`keenable` 凭据仅提高速率限制。
- `clients/` 为纯 `fetch` 直连,不使用 `pi-mcp-adapter` 或任何 MCP SDK:Tavily 走 `api.tavily.com`(必须有 key);Keenable 复刻上游 client,保留 keyless `/public` 端点与 SSRF 防护;Exa 直接对 `https://mcp.exa.ai/mcp` 发 JSON-RPC——`initialize` 一次并缓存 `mcp-session-id`,随后 `tools/call`,HTTP 404 视为会话失效自动重建并重试一次;`exa` 凭据通过 `x-api-key` 请求头发送,不得放入 URL、仓库文件或日志。
- 搜索 key 统一保存在 Pi 的 `auth.json`(`tavily`/`exa`/`keenable` 各一条 `api_key` 凭据,文件权限 `600` 且不得提交);key 支持 `!command` 执行、`$VAR`/`${VAR}` 插值间接寻址,`$$`/`$!` 转义。credential `env` 优先于进程 env;显式进程环境变量不再直接读取,env 注入通过 `"key": "$VAR"` 表达。
- `configure.sh search` 与 `scripts/search-auth.mjs` 以交互或参数方式写入凭据:加锁后原子写、权限 `600`、保留全部其他条目,支持 `--migrate` 从旧 `search.env` 一次性导入;只输出配置状态,禁止回显 key。`auth.ts` 每次调用读取 `auth.json`。
- `configure.sh search` 与 `scripts/search-auth.mjs` 以交互或标准输入方式写入凭据,key 不得出现在命令参数中:加锁后原子写、权限 `600`、保留全部其他条目,支持 `--migrate` 从旧 `search.env` 一次性导入;只输出配置状态,禁止回显 key。`auth.ts` 每次调用读取 `auth.json`。
+4 -1
View File
@@ -30,7 +30,10 @@ Keys live in Pi's `auth.json` as one `api_key` credential per provider
(`tavily`, `exa`, `keenable`), read by `auth.ts` with upstream resolution
semantics — literal values, `$VAR`/`${VAR}` interpolation, and `!command`
indirection (e.g. `"!op read op://Vault/Tavily/key"`). `configure.sh search`
writes them (atomically, mode 600, preserving all other entries); process
writes them via its hidden interactive prompt or `scripts/search-auth.mjs
--set-stdin` reading the key from stdin — a key is never accepted as a
command-line argument (atomically, mode 600, preserving all other entries);
process
environment variables are not read directly — use `"key": "$TAVILY_API_KEY"`
to indirect through env. `KEENABLE_API_URL` stays env-only, or can be stored
in the keenable credential's `env` map.
+9
View File
@@ -0,0 +1,9 @@
# 上游来源
- 本项目完全自维护,无上游;未从任何外部仓库或 npm 包导入源码。
- `clients/` 下 Tavily、Exa、Keenable client 是按上游语义复刻的纯 `fetch` 直连实现,遵循各自 upstream 的接口语义,未导入上游源码。
- 实现仅依赖 Pi 扩展宿主 API(`@earendil-works/pi-coding-agent`)与 `typebox`,两者均为组合包 peer 依赖。
## 本地改动记录
- 初始实现:`index.ts`(`web_search` / `web_fetch` 统一入口与工具注册)、`router.ts`(当前主模型按内置规则选择 provider)、`clients/`(Tavily / Exa / Keenable 直连)、`auth.ts`(auth.json 凭据解析)。