外观
02 · 一次工具调用的完整生命周期
One tool call, end to end
模型每次想干一件事,都会经过五段钩子。下面的时序即一次中风险 bash 调用的标准旅程(各步骤数字均可在代码定位):
- ① 模型 → DSH模型发出工具调用(如 `bash`、`write`、`apply_patch`、`web_fetch`)。
- ② 同步硬拒闸门 ·
ctx.tools.guard()index.ts:L1868-1874不是事件,而是 tools 服务的**同步注册守卫**:`isAutoExecution` 后先 `hardDenyReason`(凭据物质 / 受保护路径 / shell 熔断),再 `symlinkEscapeReason`(realpath 逃逸工作区)。命中即返回原因字符串 → 直接拒,**不弹窗**。这一层只做硬拒,永不挂类别分类。 - ③ 静态评估 + 类别收紧 ·
tools/pre-executeindex.ts:L1876-1937`assessTool` → `deny`(硬拒 `[auto-mode hard deny]`)/ `allow`(直接放行)/ `ask`。中间还有一层**类别收紧**(index.ts:L1896-1913):三态开关配成 `deny` 的类别在这里终端拒绝、配成 `ask` 的无条件跳过 classifier 快径直接转人工(详见 [§17](./17-category-switches))。之后若 `classifierEligible`,交给 LLM 预分类器(`classifier.classify`)再定 `allow | deny | ask`;分类器不可用 → 一律向人工(`classifier unavailable`)。 - ④ 终局裁决 ·
approval/requestindex.ts:L2407-2822(prepend+global)本插件的**核心决策管线**(详见 [§04](./04-adjudicator-pipeline)):声明规则 → 名单 → 类别开关 → 评审模式 → 熔断 → 策略硬拒 → 学习放行 → 风险分档 → LLM 复审 + 人工倒计时。产出 `allowed-once` 或 `rejected`,或委托官方面板走人工竞速。 - ⑤ 执行与产物登记 ·
tools/resultindex.ts:L1939-1942`artifacts.settle`:把本会话**成功创建**的文件登记进 `ArtifactRegistry`(后续删除该文件时可豁免)——入参 `plannedCreates` 来自评估阶段的 `artifacts.plan`。 - ⑥ 喂回模型 ·
tools/post-executeindex.ts:L1963-1993(global)若这次工具结果 `isError` 且有超时/决策标记(`timeoutFeedback` / `decisionFeedback`),注入 `{kind:'block', feedback}` —— 让模型知道「不是因为命令错,而是被审批挡下(超时 / 规则 / 模型拒绝)」。另有独立一段**结果脱敏**(index.ts:L1974-1986):`redactResults` 开启时,成功结果同样过一遍脱敏器并记审计——喂回模型的文本不夹带秘密。 - ⑦ 通知冲刷 ·
step/endindex.ts:L669-683(watchNotices 内 L676-678 分流)没有独立的 `on('step/end')` 注册:`watchNotices` 监听 `session/event`,在事件流里分流出 `step/end` 分支调 `flushNotices`——把「✅ Model approved」这类通知写进会话日志(`notifyUser` 开启时);已看到 tool/result 的才写(否则在 tool-calls 序列中间插入用户消息会破坏 OpenAI 消息序列 → 只打控制台)。
注意
②③ 在 approval/request 之前就把「无争议放行 / 明显拒绝」消化掉,只有真正的模糊区(ask)才进入审批面板 + LLM 复审,吞吐与安全兼得。