// doc.go - Flyto plugin 架构与语言模型说明 (package-level 文档). // // 本文件不含代码, 只提供给下一棒维护者 (或未来的自己) 快速建立 mental model: // Flyto plugin 究竟是什么, 为什么这样设计, 和其他 agent 生态的关键差异在哪. // // # Flyto plugin 的核心定位: 语言无关 + subprocess 隔离 // // Plugin 本身是一个本地目录, 内容 100% 是**数据/配置**: // // - plugin.json (JSON manifest) // - skills/*.md (markdown 指令) // - hooks.json (JSON 钩子配置) // - plugin.checksum (可选, SHA-256 完整性 sidecar, 见 integrity.go) // // **Flyto 从不加载、编译、或执行 plugin 作者写的代码**. 所有"可执行能力" // 来自**外部进程**, 通过三条路径注入: // // 1. MCP server (plugin.json 的 mcpServers 字段 → 启动子进程 → MCP JSON-RPC) // 2. Shell hook (hooks.json 的 command 字段 → 生命周期事件触发时执行) // 3. Declarative tool (plugin.json 的 tools 字段 → 见 plugin_tool.go) // // 这些外部进程可以用**任何语言**编写: Python / Node / Rust / Go / bash / // Ruby / C / PowerShell / 编译好的静态二进制. Plugin 作者永远不需要知道 // Flyto 是 Go 写的. // // # 与其他 agent 生态的对比 // // | 引擎 | Plugin 语言约束 | 隔离级别 | // | ---------------- | -------------------------------- | --------- | // | OpenClaw (小龙虾)| 必须 JS/TypeScript (npm 包) | in-process (弱) | // | LangChain | 必须 Python (pip 包) | in-process (弱) | // | Claude Desktop | DXT/MCPB zip 含 JS server | mixed | // | VSCode | 必须 JS/TypeScript (VSIX) | in-process (弱) | // | **Flyto** | **任意语言 (通过 MCP/shell)** | **subprocess (强)** | // // # 为什么这样设计 (反向思考过的取舍) // // 1. 零外部 runtime 依赖: Flyto 是 Go 静态二进制, 不嵌入 V8/QuickJS/CPython, // 也就不能执行其他语言的 in-process plugin. 反而逼出了更干净的 subprocess // 架构. // // 2. 强隔离: plugin 进程崩溃不影响 Flyto 主进程. JS 生态的 in-process plugin // (OpenClaw / VSCode) 一个内存泄漏能拖死宿主. // // 3. MCP 协议生态红利: 2025 年 MCP 协议成为 Anthropic / Microsoft / Google / // OpenAI / AWS 共同推的跨厂商标准. 押注 MCP 等于免费继承全部已有 MCP server. // // 4. Plugin 作者零 Go 知识要求: plugin.json 是纯声明, Flyto loader 负责翻译成 // Go 运行时对象. 降低生态进入门槛. // // 替代方案 (ELEVATED 注释, 记录被否决的思路): // - <嵌入 JS runtime (V8 / QuickJS) 让 plugin 可以 in-process 跑> - 否决: // 引入巨型依赖 + 违反零外部依赖原则 + 削弱隔离 // - <内嵌 WASM runtime (wazero) 提供 Tier 3 沙盒 plugin> - 暂不做, 未来可加. // 当前 MCP subprocess 已覆盖 99% 需求, WASM 是性能优化不是能力补全 // - <照抄 OpenClaw 的 npm 包格式以继承生态> - 否决: 锁死 JS 生态, 失去 // Python/Rust 等 MCP server 社区, 得不偿失 // // # Plugin 扩展 tool 的两条路径 // // 一个 plugin 给 agent 增加 tool 的两种方式**可以共存**: // // 1. MCP server (适合多 tool / 复杂协议 / 需要状态的场景) // plugin.json 的 mcpServers 字段声明如何启动 server, Flyto 运行时通过 // MCP JSON-RPC 协议和 server 通信, 从 server 发现的 tool 被注册进 // 全局 tools.Registry. // // 2. Declarative shell tool (适合单 tool / 无状态 / 快速扩展的场景) // plugin.json 的 tools 字段直接声明一个 tool, Flyto loader 包装成一个 // 实现 tools.Tool interface 的 pluginShellTool, 每次调用时 exec 对应的 // command + args, stdin 传 JSON 参数, stdout 收结果. 详见 plugin_tool.go. // // # Plugin 完整性校验 // // Plugin 目录的内容完整性通过可选的 plugin.checksum sidecar 文件校验 (SHA-256). // 详细实现与威胁模型见 integrity.go. // // - 默认: NoopVerifier (宽松) - 无 plugin.checksum 也放行 // - 宽松模式: SHA256IntegrityVerifier - 有 plugin.checksum 才校验, 否则放行 // - 严格模式: RejectUnsignedVerifier{Inner: ...} - 必须有 plugin.checksum // // 注意: SHA-256 完整性校验只防**本地篡改**, 不防**远程伪造**. 真正的密码学 // 签名 (Ed25519 / PKCS#7 / Sigstore) 留给未来 marketplace 场景再加实现, // SignatureVerifier interface 已经为此预留了扩展点. // // # 子进程 env 隔离 (所有外部进程共享策略) // // plugin 派生的所有子进程 (declarative shell tool + MCP server subprocess + // plugin-owned hook 的 shell 执行) 都走**统一的零信任 env 策略**, 实现位于 // pkg/execenv 包: // // 1. 白名单继承: 只透传 PATH / HOME / LANG / LC_ALL 四个 OS env. 其他变量 // (包括 ANTHROPIC_API_KEY / OPENAI_API_KEY / AWS_SECRET_ACCESS_KEY 等 // Flyto 主进程敏感 env) **永远不会**泄漏. // // 2. 显式声明: plugin 或 user 想让子进程看到某个变量, 必须在配置里写 // plugin.json 的 tools[].env / mcpServers[].env, 或 settings.json 的 // mcpServers[].env. // // 3. ${VAR} 展开: 配置值支持 ${HOST_VAR} / $HOST_VAR 语法引用宿主 env, // 未 set 的引用直接启动失败 (显式错误 > 静默降级). $$ 转义为字面 $. // 与 Claude Code .mcp.json / Docker Compose / Kubernetes envFrom 对齐. // // 这一策略对 plugin-owned MCP server 和 user-configured (settings.json) MCP // server **一视同仁**, 不按来源分叉. 反向讨论结论: Flyto 目标场景 (B2B SaaS, // 飞驼云仓等) 下"受信的 end user 手配 settings.json"是假想客户, 双轨制的 // 宽松轨是结构性死代码. 详见 pkg/execenv/env.go 的设计说明. // // 和 engine.go 的 parsePluginMCPServerKey 的关系: 前缀区分 (plugin..) // 只服务 **lifecycle 隔离** (禁用 plugin 时不能误伤 user-configured 的 server), // 和 env 策略是**正交**的两个机制, 未来修改时不要让它们产生耦合. // // # Hook executor 的特例: 按 PluginDir 分流 (2026-04-15 L511 衍生) // // pkg/hooks/executor.go 是此零信任策略的**唯一例外**, 按 HookDef.PluginDir // 是否为空分两条路径: // // - plugin-owned hook (PluginDir != "") → 走 execenv.MinimalEnv 白名单, // 和 MCP / shell tool 策略一致. 威胁模型: 第三方 plugin 作者的 hook 脚本 // 不应隐式读宿主进程的 ANTHROPIC_API_KEY. // // - user-configured hook (PluginDir == "", 来自 settings.json 或 SDK 注册) // → 全量继承 os.Environ(). 威胁模型上这是"用户在自己机器上写 shell 脚 // 本读自己的 env", 不是特权提升; 对齐 Git hooks / systemd Exec= / npm // pre-scripts 的默认语义. 强行套白名单会让存量 git push / curl -H // "Authorization: $TOKEN" 类用户 hook 全部崩溃, 爆炸半径远大于威胁模型 // 收益. // // 这是**唯一**合理的双轨分叉: hook 场景下用户 hook 是主流路径不是死代码, // 和 MCP server 场景的分布恰好相反. 注意分叉依据是 PluginDir 而非 Source, // 因为 PluginDir 是更强的"来自插件"信号 (引擎内部从不设 PluginDir, 而 // Source 未来可能被 SaaS 多租户前缀污染). // // Follow-up: 给 plugin-owned hook 加 HookDef.Env 字段作为显式逃生口, 让 // plugin 在 plugin.json 里声明需要的 env (走 execenv.ExpandEnvMap 做 ${VAR} // 展开), 对齐 tools[].env / mcp_servers[].env. 这是朝"手机应用式权限清单" // 产品愿景 (安装时显式授权) 迈进的一步. // // # Evolve 工具的例外: 刻意继承 os.Environ() (2026-04-15 审计闭合) // // pkg/evolve/tool_builder.go 的 executeScript / executeCommand (L325, L360) // **刻意**不走 execenv 白名单, 全量继承宿主 env. 这不是漏网的 L511 衍生漏 // 洞, 也不是忘记修 — 是 2026-04-15 审计后产品经理 + 工程明确决策保持现状. // 任何未来审计请先读完本小节再动手, 不要"顺手"把它改成 execenv.MinimalEnv. // // 决策依据: // // 1. 信任模型不同. evolve 脚本由 LLM 在**用户当前 session** 里生成, 经 // ApprovalFunc 人类实时审批, 等价于"用户在自己机器上手敲 bash". 它 // 不是"第三方 plugin 作者代码在宿主进程里运行"(这才是 L511 威胁模型), // 不应套同一策略. // // 2. 产品价值硬性依赖 env. evolve 的典型工具 (GitHub PR 列表需要 // $GITHUB_TOKEN, GCP 部署需要 $GOOGLE_APPLICATION_CREDENTIALS, 内网 // Docker push 需要 $DOCKER_PASSWORD) 本质上需要读环境变量. 切白名单 // 会让 Agent 发明工具时反复卡在"我没法读你的 token", 直接打碎"Agent // 自主造工具"的核心卖点. // // 3. env 白名单在此威胁模型下是安全剧场. 本地 Flyto **没有进程沙盒**, // 子进程能 cat ~/.ssh/id_rsa / 读 ~/.aws/credentials / curl 外发 / // 读 /proc/self/environ 绕过白名单. 能骗过 ApprovalFunc 的恶意脚本 // 同样能骗过审批一个 cat 凭证脚本, env 白名单只堵最显眼的 1/10 个洞, // 换取显著的 UX 破坏, 性价比不成立. // // 4. 主防线是 ApprovalFunc (人类审批) + SecretGuard (持久化前扫 API key // 硬编码落盘) + MaxPerSession (速率限制). 这是团队在设计 evolve 时 // 明确选择的防御栈, env 策略是正交层, 加了不补强主防线. // // 行业对齐: // // 本地 AI 开发工具 (Claude Code / Copilot / Cursor / Aider / Jupyter / // VS Code tasks) 全都全量继承 env, 信任模型都是"用户对自己机器负责". // 唯一例外是 OpenAI Code Interpreter, 但它是**云服务** — 容器跑在云端 // 服务器, 和本地 CLI 信任模型根本不同. // // 未来路线 (见 memory: project_sandbox_local_vs_cloud): // // - **本地 CLI**: 保持现状, 不引入沙盒. 不要在 core/ 层为本地场景加 // bwrap / seccomp / Docker 抽象, 会破坏跨平台 (Linux/Mac/Windows) // 一致性且与行业默认脱节. // // - **云端 SaaS (飞驼云仓等平台化产品)**: **必须**上真实进程沙盒 (Docker / // gVisor / Firecracker 容器). 云端没有"用户当下审批"的概念 — 租户 A // 的 Agent 跑时租户 B 不在场, ApprovalFunc 的信任链断裂, 只能靠沙盒 // 物理隔离. 这个沙盒封装属于 platform/ 层, 不是 core/ 层. 云端沙盒 // 建成后, evolve 的 os.Environ() 继承策略在云端场景下自然收紧 (沙盒 // 里根本没有敏感 env 可继承), 不需要改 core 代码. // // - **如果本地 CLI 的 evolve 从"实验性 + 每次审批"升级为"默认开启 + // 自动执行"**, 应同步补沙盒, 而不是补 env 白名单. 半吊子 env 策略 // 在那时依然不顶用. // // 禁止修改方向: // // ❌ 不要把 tool_builder.go:325/360 的 os.Environ() 改成 // execenv.MinimalEnv — 那会破坏 evolve 的产品价值且没换到安全收益. // ❌ 不要在 core/ 层为本地 evolve 引入沙盒抽象 — 跨平台地狱 + 与行业 // 同类工具脱节. // ✅ 可以在 platform/ 层为云端场景加沙盒封装, 通过依赖注入替换 core // 的 exec.Cmd 行为, 本地保持零成本. // // # Bash 工具 env 策略: L513 审计删除黑名单 (2026-04-15) // // pkg/tools/builtin/bash.go ExecuteBash + bash_background.go runBackground 原 // 使用 `filterSensitiveEnv()` 黑名单策略 (拷贝 os.Environ() 后过滤 8 条 // sensitiveEnvKeys). 2026-04-15 L513 审计后**删除**, 改为直接 os.Environ() // 全量继承, 并入类 B. 任何未来审计请先读完本小节再动手, 不要"顺手"把它 // 改回黑名单. // // 删除原因 (4 个结构性缺陷): // // 1. 枚举严重不全. 原 8 条只覆盖 ANTHROPIC/OPENAI/AWS_SECRET/GITHUB/GH/ // API/SECRET/PRIVATE_KEY, 漏了 GOOGLE_API_KEY / AZURE_CLIENT_SECRET / // AWS_ACCESS_KEY_ID / AWS_SESSION_TOKEN / NPM_TOKEN / KUBECONFIG / // DATABASE_URL / HF_TOKEN / SLACK_BOT_TOKEN / MINIMAX_TOKEN_PLAN_KEY // (本仓库自己在用的 key) 等 20+ 条常见敏感 env. 过去一年多无人维护, // 说明"加新敏感 env 时同步枚举"的隐式纪律从未建立. // // 2. GH_TOKEN 是实锤 bug. 早期实现明确 // 保留 GITHUB_TOKEN / GH_TOKEN 不过滤, 理由是 wrapper scripts (gh.sh) // need them to call the GitHub API, that token is job-scoped and expires. // Flyto 的黑名单反向把它过滤掉, 一旦 CLI 二进制落地并让 Agent 用 gh CLI, // 会直接挂. // // 3. 职责越界. env 隔离应由消费层负责 — 引擎假设 os.Environ() 就是 // 该继承的, 消费层 (platform/ 层云端沙盒 / 未来 CLI 二进制) 在调用 // engine.New 之前自行决定进程 env 内容. 引擎内部做 env 过滤是替 // 消费层干活, 而且干得不全不对, 还引入维护税. // // 4. 防错了层. 原黑名单的威胁模型是"用户把 API key 放在 shell env 里, // 被 Agent 跑的命令泄漏到 LLM". 这是**产品 UX 问题**: 用户应该用 // SecretStore (engine.SetSecret / WithSecret) 管理秘密, 自动享受 // env 注入 + 输出 Redact() 的完整防御. 产品该做的是把 SecretStore // 的数据入口做易用 (CLI `--secret` / OS keychain 集成), 而不是在 // 引擎里打补丁防御"用户没用 SecretStore"的场景. // // 反向讨论 (必做): 失去什么? // // 删除后的唯一失去场景: 用户把 ANTHROPIC_API_KEY 放在 ~/.zshrc 里 // (没用 SecretStore), 被 prompt injection 诱导执行 `env` 或 // `printenv | grep KEY`, 输出回到 LLM 可能泄漏到模型商 / 对话历史. // // 为什么可接受: // (a) SecretStore 是产品明确的秘密管理路径, 不走 SecretStore 等价于 // 用户主动放弃保护. 类似 git 不保护 .git/config 明文密码, bash // 不保护 .bash_history. // (b) Permission 层是主防线, `env` / `printenv` / `curl attacker.com` // 类命令会被 Permission gate, 用户拒绝就没事. env 过滤从来只是 // 纵深防御第二层, 不是主防线. // (c) 零生产影响: 本仓库目前无 CLI 二进制, 也无正在运行的 Bash 工具 // 用户, 删除没有 migration 成本. // (d) 行业共识: Claude Code 默认模式 / Cursor / Aider / Jupyter 全 // 都全量继承 env, 没有一个因为"这是 bug"要加过滤 — 是刻意选择. // // 跨行业对齐: // // Claude Code 早期方案: // 默认 return process.env (完全不过滤). 仅当 CLAUDE_CODE_SUBPROCESS_ENV_SCRUB // 被设置时才启用 20 条黑名单 (ANTHROPIC/OTEL headers/AWS/GCP/Azure/GHA // OIDC 等). 该开关由 claude-code-action 在 allowed_non_write_users // 场景自动打开, 威胁模型是"外部 PR 作者 prompt injection 窃取 Anthropic // 自家 Action 的 API key". **本地 CLI 模式下从不过滤**. // // Cursor / Aider / Jupyter !shell / VS Code tasks: 全都全量继承. // // Flyto 架构比早期方案**更干净**: 我们把"云端 scrub 开关"职责完全推给 // platform/ 层. 云端 SaaS 场景下, platform 层启动引擎前 os.Unsetenv // 想清的 env 即可, 一次 unset 保护所有子进程入口 (Bash / hooks / // evolve / memory git sync), 不需要引擎内部再做一层. 本地 CLI 场景 // 保持最简, 和 Claude Code 默认模式对齐. // // 禁止修改方向: // // ❌ 不要恢复 filterSensitiveEnv 或任何形式的 env 黑名单. 如果未来 // 有人担心"shell env 秘密泄漏", 正确解法是 (a) 在 CLI scaffolding // 里把 SecretStore 做易用, (b) 在 platform/ 层做沙盒封装, 不是在 // core/ 引擎里加过滤. // ❌ 不要在 Bash 工具之外的入口 (hooks / evolve / memory git sync) // 加 env 黑名单. 这些已在类 B 审计过, 各有刻意决策. // ✅ 可以在消费层 (platform/ 层或未来 CLI 二进制) 于 engine.New 之前 // 调 os.Unsetenv 控制引擎看到的进程 env. 这是正确的职责切法. // // Follow-up (非阻塞, 独立任务): // // 未来 core/cmd/flyto/main.go 第一天必须内建 SecretStore 数据流: // - --secret KEY=VAL 命令行参数 // - --secret-file /path/to/secrets.json // - OS keychain 集成 (macOS Keychain / libsecret) // 让用户有**比 shell env 更易用**的正确路径. 这是比本任务更紧迫的 // 根本断层 — 当前"有 SecretStore 机制但没 CLI 入口"才是用户会把 // 秘密放 shell env 的真正原因, 不是引擎缺过滤. // // # M1 Executor 门框 (as of 2026-04-15 M1 迁移进行中) // // 2026-04-15 方案 β 推进: 本清单的所有子进程入口统一经 execenv.Executor.Command // 启动, 不再直接调 os/exec. 门框接口 + Spec + Class + Process 定义在 // core/pkg/execenv/executor.go, DefaultExecutor (本地零开销 os/exec // 包装) 在 default_executor.go, 云端 sandbox.Backend 在 platform/common/internal/sandbox/. // 详见 platform/common/internal/sandbox/DESIGN.md section 7 / 7.1. // // Spec.Class 是 8 值枚举 (ClassPluginMCP / ClassPluginTool / ClassPluginHook / // ClassUserHook / ClassEvolve / ClassBash / ClassMemoryGit / ClassWorkspaceTool), // 本清单下方每一行都明确标注 Class. Env 策略从 Class 派生 (类 A 白名单 = // ClassPluginMCP + ClassPluginTool + ClassPluginHook, 类 B 全继承 = 其余). // Class -> 隔离策略的完整映射在 platform sandbox 后端, core 只透传 Class. // // 迁移进度 (21 commit 计划, 已完成 21/21 = 100%): // // ✅ commit 1-3 execenv 接口 + DefaultExecutor + 零开销 bench // ✅ commit 3.5 caller-driven audit 扩 Process interface (11 方法) // ✅ commit 4a MinimalEnvMap / FullInheritMap helper // ✅ commit 4b-1 engine.Config 加 Executor 必填字段 // ✅ commit 4b-2 mcp.NewManager 加 executor 参数 // ✅ commit 4b-3 stdio_transport 走 Executor.Command // ✅ commit 4d+5 hooks.executor 走 Executor.Command // ✅ commit 6 evolve tool_builder 走 Executor.Command // ✅ commit 8 memory sync_git 走 Executor.Command // ✅ commit 9 memory external scorer 走 Executor.Command // ✅ commit 10 builtin exec_tool 走 Executor.Command // ✅ commit 11 builtin grep_engine (RipgrepEngine) 走 Executor.Command // ✅ commit 12 builtin glob_engine (GitGlobEngine 3 点) 走 Executor.Command // ✅ commit 13 engine worktree.go (6 点) 走 Executor.Command // ✅ commit 14 本次 doc.go 清单同步 // ✅ commit 7a Process.SignalGroup interface 扩容 (pgid-level 信号) // ✅ commit 7b+4c plugin NewHost + NewPluginShellTool 走 Executor.Command // (ClassPluginTool, IsolateProcessGroup 完整生命周期接管 // 原 configureProcessGroup/killProcessGroup, 见下) // ✅ commit 7c+7d builtin bash + bash_background 走 Executor.Command // (ClassBash + IsolateProcessGroup, gracefulKill/waitForExit // 重写接受 Process handle, SIGTERM/SIGKILL 走 SignalGroup, // BackgroundBashTask.Pid 保持 int 字段由 Atoi(proc.ID()) 填充) // // # M1 完成 (21/21, 100%) // // 全部入口已迁移. 下一步由 platform/common/internal/sandbox 替换 DefaultExecutor // 为云端 E2B Firecracker backend, 本地零感知. // // # M2 sandbox.Backend 进展 (as of 2026-04-15) // // M2 commit 1: execenv 从 core/internal/ 提升到 core/pkg/, 解除 Go internal // 可见性限制, 让 flyto-platform-common 模块可 import + 实现 Executor interface. // (M1 遗留 gap: internal 包只能被同模块代码导入, platform 跨模块不可达.) // // M2 commit 2: platform/common/internal/sandbox/backend.go + class_policy.go 落地. // Backend struct 是 Executor 第二实现 (stub, 内部委托 DefaultExecutor). // ClassPolicy struct 6 字段对齐 E2B REST API (2026-04-15 调研确认): // TemplateID / RunAsUser / DefaultEnvs / TimeoutMs / ReuseKey / Metadata. // E2B 无官方 Go SDK, 未来走 REST API 自封装. // // M2 commit 3: parity test 12 case 全绿 (echo / exit code / env / workdir / // stderr / combined output / writer / ID / 进程组隔离 / SignalGroup / policy). // parityExecutors() 对 DefaultExecutor 和 Backend 跑同一批 Spec fixture, // M3 换真 E2B 时只改一行即自动变回归防线. // // 下一步: M3 接真 E2B infra (Terraform self-host on GCP), Backend.Command // 翻译 Spec + ClassPolicy 为 E2B Sandbox.create + commands.run 调用. // // # 子进程 env 继承入口完整清单 (as of 2026-04-15 M1 commit 14 同步) // // 本仓库中所有 "exec.Cmd 子进程 + env 构造" 的入口已完整扫描归档, 从 L513 // 审计时的 8 个点扩展到 **13 个点** (M1 审计补齐了 5 个 grep/glob/worktree/ // exec_tool/memory_scorer 入口, 均为 L513 audit 漏点). 分 2 类策略 (L513 前 // 是 3 类, Bash 工具从原类 C 黑名单**删除**上升为类 B, 类 C 消失). 此清单 // 是 Flyto 子进程 env 隔离的**单一事实来源**, 未来任何新增 exec.Cmd / // Executor.Command 子进程都应该在本清单里新增一行并说明 Class + 策略选择. // // 类 A — 白名单 (execenv.MinimalEnvMap): 零信任, 防第三方代码隐式泄漏秘密. // // 1. internal/mcp/stdio_transport.go — MCP stdio subprocess (L511) // Class=ClassPluginMCP. M1 commit 4b-3 迁移. // 2. pkg/plugin/plugin_tool.go — Plugin shell tool // Class=ClassPluginTool. M1 commit 7b+4c 迁移. Spec.IsolateProcessGroup // 完整接管原 configureProcessGroup/killProcessGroup/WaitDelay 三件套 // (DefaultExecutor 在 IsolateProcessGroup=true 路径下自动设 cmd.Cancel // 为 kill -pgid SIGKILL + WaitDelay 200ms). plugin_tool_unix.go / // plugin_tool_windows.go 已删除. // 3. pkg/hooks/executor.go (PluginDir!="") — Plugin-owned hook (L513) // Class=ClassPluginHook. M1 commit 4d+5 迁移. // // 类 B — 全继承 (execenv.FullInheritMap): 引擎/用户自己的代码 + Agent 主执行 // 通道 + 引擎对租户 workspace 的系统工具调用, 刻意决策. // // 4. pkg/hooks/executor.go (PluginDir=="") — User-configured hook // Class=ClassUserHook. 对齐 Git hooks / systemd Exec= 默认语义, 用户 // 对自己机器负责. M1 commit 4d+5 迁移. // // 5. pkg/evolve/tool_builder.go executeScript — Evolve 脚本 (L514) // 6. pkg/evolve/tool_builder.go executeCommand — Evolve 命令 (L514) // Class=ClassEvolve. LLM 生成 + ApprovalFunc 人类审批, 等价于"用户 // 手敲 bash". 见上方"Evolve 工具的例外"小节. M1 commit 6 迁移. // // 7. pkg/memory/sync_git.go runGitOutputErr — Memory git sync // Class=ClassMemoryGit. 引擎内部代码调用 git binary 做 memory 持久化, // 需要 SSH_AUTH_SOCK / HOME / GIT_AUTHOR_* 才能正常 git push. 不是 // plugin/LLM/用户代码, 是引擎自己. 信任边界内, 不应套白名单. // M1 commit 8 迁移. // // 8. pkg/tools/builtin/bash.go ExecuteBash + bash_background.go runBackground // — 内建 Bash 工具 (L513 审计决策 2026-04-15, 从原类 C 黑名单**删除** // 上升为全继承). Class=ClassBash. Agent 在用户当下授权下的主执行通道, // 对齐 Claude Code 默认模式 / Cursor / Aider / Jupyter. 秘密管理的单一 // 正确路径是 engine.SetSecret() / WithSecret() + SecretStore.Redact(), // 引擎不在 Bash 内部做 env 黑名单. 云端 SaaS 场景由 platform/ 层在 // engine.New 之前处理进程 env. 详见上方"Bash 工具 env 策略"小节. // M1 commit 7c+7d 迁移 (2026-04-15), 走 execenv.Spec.IsolateProcessGroup // 完整生命周期 + gracefulKill/waitForExit 重写走 Process.SignalGroup / // Signal(0). BackgroundBashTask.Pid 保持 int JSON 字段, 由 // strconv.Atoi(proc.ID()) 填充, 云端 backend 返回非 int ID 时回落 0 // (字段仅用于展示, 不参与 kill 路径). // // 9. pkg/tools/builtin/exec_tool.go Execute — 声明式 exec tool (M1 补齐) // Class=ClassUserHook. 用户静态声明的 exec tool, 信任模型同 // settings.json hook (ClassUserHook 共享). L513 audit 时漏点, M1 commit // 10 补齐归档. 2026-04-15 迁移. // // 10. pkg/memory/external_scorer.go Score — Memory external scorer (M1 补齐) // Class=ClassMemoryGit. StdinPipe + StdoutPipe 长驻进程模式, 和 sync_git // 共享 ClassMemoryGit (都是引擎自用 memory 基础设施). L513 audit 时漏点, // M1 commit 9 补齐归档. 2026-04-15 迁移. // // 11. pkg/tools/builtin/grep_engine.go RipgrepEngine.Search — rg (M1 补齐) // Class=ClassWorkspaceTool. "引擎对租户 workspace 的系统工具调用"新类别, // 和 ClassBash 共享 microVM 实例但只 bind /workspace 无网络. 本地全继承 // 需要 PATH/HOME/LANG 等, 云端 microVM 天然收紧. L513 audit 时漏点 // (ripgrep 是引擎工具, 非 env 泄漏路径被漏扫), M1 commit 11 补齐归档. // 2026-04-15 迁移. **ClassWorkspaceTool 首次使用**, 同时是 Spec.WorkDir // 字段首次使用 (取代 cmd.Dir). // // 12. pkg/tools/builtin/glob_engine.go GitGlobEngine — git ls-files (3 点, M1 补齐) // Class=ClassWorkspaceTool. 3 个 callsite: findRepoRoot (git rev-parse // --show-toplevel), listFiles tracked (git ls-files -z), listFiles // untracked (git ls-files --others -z). 同 ClassWorkspaceTool 策略. // L513 audit 时漏点, M1 commit 12 补齐归档. 2026-04-15 迁移. // // 13. pkg/engine/worktree.go — git worktree (6 点, M1 补齐) // Class=ClassWorkspaceTool. 6 个 callsite: CreateWorktree / DetectGitRepo / // isGitRepo / getHeadRef / removeWorktree / deleteBranch. 同 // ClassWorkspaceTool 策略. L513 audit 时漏点, M1 commit 13 补齐归档. // 2026-04-15 迁移. // // 审计规则: // // - 新增任何 Executor.Command 子进程入口时, 必须在本清单里新增一行 // - 新增入口必须明确标注 Class + 归类 A/B 并说明选择理由 // - 选择类 A (白名单) 是默认, 除非有明确的产品/信任模型理由选 B (全继承) // - 类 C (黑名单) 已于 2026-04-15 L513 审计后废弃, 不应再新增 // - 修改本清单任一条目策略时, 必须同步更新对应代码注释和本文档 // - 每次安全审计必须重跑 // rg 'exec\.Command|executor\.Command' --include='*.go' core/ // 对照本清单, 发现新入口立即走上述流程归类. 注意 M1 后权威 grep pattern // 是 `executor\.Command`, 原 `os\.Environ` 只能找到 env 相关入口, 漏掉 // grep/glob/worktree 这类"无 env 但有 exec"的系统工具调用 (L513 就是 // 这么漏掉 5 个点的). package plugin