Codex 系统手册

工具执行、审批与受控操作

stage-14294 个文件

这一阶段发生在模型说“我要动手做点事”之后,是主流程里的受控执行段。它先按统一合同看懂工具长什么样、参数是否干净,再把 MCP、插件、搜索、记忆、图片等外部工具翻译成系统能用的格式。接着安全总闸检查要不要审批,hook 可插手补查,沙箱规则限制文件、网络和权限。通过后,执行后端才真正跑命令、打补丁或调用远端工具。出错时也会分清是可告诉模型的小问题,还是必须停下来的严重故障。

子阶段

本阶段涉及的状态24
  • reg-effective-config系统最终采用的那份配置,把文件、云端规则、项目设置、命令行参数和临时覆盖合在一起,后面各模块都按它办事。
  • reg-feature-flags当前打开或关闭的实验功能和能力开关,决定界面、模型、工具、插件等功能能不能用。
  • reg-installation-environment程序知道自己装在哪里、家目录在哪里、运行在哪台机器和什么终端里的那组环境信息。
  • reg-secret-store本机保存的 API Key、ChatGPT 令牌、个人访问令牌、Bedrock Key 等秘密凭据。
  • reg-plugin-registry已安装、可用、来自市场或本地的插件清单,以及每个插件的能力和加载结果。
  • reg-mcp-registryMCP 服务器、连接、工具和资源的登记表,模型和前端都靠它知道能调用哪些外部能力。
  • reg-memory-store系统保存和检索长期记忆的地方,包含记忆文件、目录、搜索结果和后台整理状态。
  • reg-permissions-approvals用户已经允许、拒绝或需要确认的命令、文件、联网、MCP 调用等权限和审批状态。
  • reg-sandbox-execution-policy命令和工具运行时要遵守的沙箱、读写、联网、身份和执行策略。
  • reg-exec-environment-processesexec-server 和统一执行层里正在运行或已经结束的进程、退出码、输入输出和失败原因。
  • reg-turn-execution-state一轮对话正在排队、运行、取消、追加输入、压缩上下文或等待工具结果的临时但跨模块共享状态。
  • reg-tool-registry模型这一轮能看到和调用的工具集合,包括内置工具、动态工具、插件工具、MCP 工具和搜索工具。
  • reg-hook-lifecycle-state钩子在配置、回合、工具调用和审批过程里会触发什么、传什么上下文、返回什么结果的共享状态。
  • reg-observability-telemetry日志、指标、分析事件、请求耗时、工具结果和错误报告等观测数据的收集与发送状态。
  • reg-network-client-proxyHTTP 客户端、证书、Cookie、重试、流式连接和网络代理放行/拒绝规则的共享联网状态。
  • reg-code-mode-sessions代码模式运行器里的会话、正在执行的脚本、等待结果、定时器和宿主回调状态。
  • reg-workspace-trust-state当前工作区和目录的信任状态、授权根目录及同步到前端和沙箱的工作区权限信息。
  • reg-shell-environment-snapshot启动时捕获的用户 shell、PATH、环境变量和终端习惯快照,用于后续命令执行和上下文说明。
  • reg-runtime-environment-registry本地、云端或远程执行环境的可用清单、默认选择和当前线程选中的运行目标。
  • reg-connector-registry从后端同步的外部连接器及其可用性、授权和目录刷新状态。
  • reg-worktree-diff-state当前工作树快照、Git 状态、文件改动摘要和待展示或持久化的 diff 状态。
  • reg-parallel-tool-scheduler并行工具调用的活跃任务、资源占用、互斥限制、取消和收尾调度状态。
  • reg-active-workspace-context当前会话选中的工作目录、项目根和能力根等工作区上下文,用于提示词、工具执行、权限判断和结果展示。
  • reg-extension-lifecycle-state已加载扩展在账户、线程、回合和工具生命周期中注册的贡献、回调上下文和运行中结果状态。

本阶段的文件6

工具 crate 接口

这些文件建立共享工具 crate 的接口,以及该阶段其余部分使用的核心执行和错误约定。

tools/src/function_call_error.rs源码 ↗
data_modelrequest handling

模型有时会调用外部工具,比如查资料、读文件或执行某个动作。工具执行时可能失败,但失败也分轻重:一种是可以把原因原样回给模型,让模型自己决定下一步;另一种是系统级的大问题,不能假装正常继续。这个文件就像给错误贴标签的贴纸,定义了 FunctionCallError 这个错误类型。RespondToModel 表示“把这段错误信息告诉模型”;Fatal 表示“这是致命错误”,显示时会加上“Fatal error:”前缀。它还使用 thiserror 这个库来自动生成错误显示方式,避免每个地方手写重复代码。这个文件本身不执行工具,只规定工具失败时大家统一用什么格式说话。

tools/src/tool_executor.rs源码 ↗
data_modelcross-cutting:工具注册、工具发现、模型调用工具时都会用到

系统里会有很多工具,比如搜索、读文件、执行某种动作。这个文件不实现具体工具,而是规定工具必须提供哪些信息:工具名、给模型看的说明书、实际执行入口,以及它是否能被模型直接看到。ToolExposure 像一个“上架规则”,决定工具是放在模型一开始就能看到的货架上,还是先藏起来、之后再搜索发现,或者完全隐藏只给内部调度用。ToolExecutor 是每个工具都要遵守的接口,也就是一份合同:工具要说清自己叫什么、规格是什么,并能接收一次调用请求后返回结果。这里还给了一些默认行为,比如默认直接暴露、默认不支持并行调用。这样上层系统就能用同一种方式注册和调用不同工具,而不用关心每个工具内部怎么干活。

函数细节4
ToolExposure::is_direct39–41 ↗
fn is_direct(self) -> bool

作用:判断一个工具是不是会直接出现在模型一开始能看到的工具列表里。有人需要快速区分“马上可见”和“暂时不展示”的工具时会用它。

数据流:进去的是一个 ToolExposure 值,也就是这个工具的展示方式;函数检查它是不是 Direct 或 DirectModelOnly;出来的是 true 或 false,不改动任何数据。

调用关系:它是 ToolExposure 这个展示规则上的小判断工具。注册或整理工具列表的代码会用它来决定哪些工具应该先摆到模型面前;它内部只做匹配判断,不把活交给别的项目函数。

调用图:外部调用 1 个(matches!)。

ToolExecutor::exposure55–57 ↗
fn exposure(&self) -> ToolExposure

作用:给工具提供一个默认的展示方式:直接展示给模型。具体工具如果没有特殊要求,就不用自己写这段。

数据流:进去的是某个工具对象本身;函数不读取复杂状态,直接返回 ToolExposure::Direct;结果表示这个工具默认会在初始工具列表里露面,也不改动任何东西。

调用关系:这是 ToolExecutor 合同里的默认方法。上层注册工具时会问每个工具 exposure 是什么;如果具体工具没重写,就走这里的默认答案。

ToolExecutor::search_info59–62 ↗
fn search_info(&self) -> Option<ToolSearchInfo>

作用:为“延后发现”的工具生成搜索用的信息,让系统之后能按说明找到它。简单说,就是把工具说明书整理成可检索的简介。

数据流:进去的是工具对象;它先调用这个工具的 spec 拿到工具规格,也就是名称、参数、说明等面向模型的描述;再交给 ToolSearchInfo::from_tool_spec 转成搜索信息;出来的是可能存在的 ToolSearchInfo,不直接改动工具本身。

调用关系:这是 ToolExecutor 合同里的默认搜索信息生成器。工具注册或工具搜索阶段会用到它,尤其是 exposure 为 Deferred 的工具;它把主要工作交给 from_tool_spec,根据工具规格拼出搜索元数据。

调用图:调用 1 个内部函数(from_tool_spec)。

ToolExecutor::supports_parallel_tool_calls64–66 ↗
fn supports_parallel_tool_calls(&self) -> bool

作用:说明这个工具默认不支持同时被并行调用。这样可以避免两个调用同时操作同一份资源时互相打架。

数据流:进去的是工具对象;函数不检查额外数据,直接返回 false;出来的结果告诉调度器不要默认把这个工具的多次调用并排跑,也不会改动任何状态。

调用关系:这是 ToolExecutor 合同里的安全默认值。调度器准备安排工具调用时会参考它;如果某个工具确认自己能安全并行执行,可以重写这个方法返回 true。

tools/src/lib.rs源码 ↗
othercross-cutting

这个文件本身不做具体计算,也没有函数。它的作用更像一个商场导览台:真正的店铺在各个子文件里,比如工具定义、工具调用、工具输出、JSON schema(描述 JSON 数据长什么样的规则)、Responses API 工具转换、插件安装请求、MCP 工具等;而这里负责把这些东西集中导出。这样外部代码只要引用 tools 这个库,就能拿到需要的类型和函数,不必直接去找每个内部模块。它也把一部分工具能力从 codex-core 里独立出来,方便共享和复用。没有它,调用方就会到处依赖内部路径,代码更乱,也更容易在内部文件调整时坏掉。

Schema 与 MCP 适配

这些文件定义工具 schema 如何标准化,以及 MCP 工具元数据如何转换为内部工具定义格式。

tools/src/json_schema.rs源码 ↗
domain_logictool registration / schema parsing

JSON Schema 是一套用来说明 JSON 数据长什么样的规则,比如某个字段必须是字符串,某个参数是数组。这个文件先定义项目支持的 schema 数据结构,比如字符串、数字、对象、数组、枚举、引用等;再提供一组方便创建 schema 的小工具。更重要的是,它会处理外部传来的 schema:补上缺失的类型,去掉不支持或没用到的定义,把 const 改成 enum,必要时把过大的 schema 分几步压小。这样工具参数既尽量保留原意,又能符合当前系统和接口的限制。

函数细节47
JsonSchema::typed78–84 ↗
fn typed(schema_type: JsonSchemaPrimitiveType, description: Option<String>) -> Self

作用:创建一个最基础的 schema,表示“这个值是什么类型”,比如字符串、数字或对象。它是其他快捷构造函数的底层小零件。

数据流:进去一个基础类型和可选说明文字 → 它生成一个只填好 type 和 description 的 JsonSchema,其他字段保持默认空值 → 出来一个简单可用的 schema。

调用关系:JsonSchema::boolean、JsonSchema::string、JsonSchema::number 等函数都会借它干活,避免每种基础类型都重复写同一套创建代码。

调用图:外部调用 2 个(default, Single)。

JsonSchema::any_of86–92 ↗
fn any_of(variants: Vec<JsonSchema>, description: Option<String>) -> Self

作用:创建一个“多个方案里满足任意一个就行”的 schema。适合参数可以有几种不同形状的情况。

数据流:进去一组候选 schema 和可选说明 → 它把这些候选放进 anyOf 字段 → 出来一个表示“任选其一”的 JsonSchema。

调用关系:它是公开的构造工具,给上层定义复杂工具参数时使用;内部主要依赖默认值来填空白字段。

调用图:外部调用 1 个(default)。

JsonSchema::one_of94–100 ↗
fn one_of(variants: Vec<JsonSchema>, description: Option<String>) -> Self

作用:创建一个“只能符合其中一个方案”的 schema。它比 anyOf 更严格,强调只匹配一个分支。

数据流:进去一组候选 schema 和说明文字 → 它写入 oneOf 字段 → 出来一个表达“只选一个形状”的 JsonSchema。

调用关系:它给工具定义代码提供现成积木;和 any_of、all_of 一样,属于组合 schema 的创建入口。

调用图:外部调用 1 个(default)。

JsonSchema::all_of102–108 ↗
fn all_of(variants: Vec<JsonSchema>, description: Option<String>) -> Self

作用:创建一个“必须同时满足所有方案”的 schema。适合把几条规则叠加起来描述同一个值。

数据流:进去多条 schema 规则和可选说明 → 它把规则放入 allOf 字段 → 出来一个表示“全部都要满足”的 JsonSchema。

调用关系:它服务于上层工具 schema 的组合表达,和 any_of、one_of 构成三种常见组合方式。

调用图:外部调用 1 个(default)。

JsonSchema::boolean110–112 ↗
fn boolean(description: Option<String>) -> Self

作用:快速创建布尔值 schema,也就是只允许 true 或 false。上层定义开关类参数时会用它。

数据流:进去可选说明文字 → 它交给 JsonSchema::typed,指定类型为 Boolean → 出来一个布尔参数的 schema。

调用关系:多个创建工具参数的函数会调用它,比如等待、发送输入、命令执行和网络权限相关工具,用来声明某个字段是开关。

调用图:被 9 处调用(create_wait_tool, create_report_agent_job_result_tool, create_send_input_tool_v1, spawn_agent_common_properties_v1, create_exec_command_tool_with_environment_id, create_shell_command_tool, network_permissions_schema, exec_command_tool_matches_expected_spec, shell_command_tool_matches_expected_spec);外部调用 1 个(typed)。

JsonSchema::string114–116 ↗
fn string(description: Option<String>) -> Self

作用:快速创建字符串 schema,也就是文本参数。工具里很多名字、路径、命令、说明文字都会用到它。

数据流:进去可选说明文字 → 它交给 JsonSchema::typed,指定类型为 String → 出来一个文本参数的 schema。

调用关系:它被大量工具定义函数调用,是最常用的基础积木之一,用来让工具参数清楚标明“这里要填文字”。

调用图:被 38 处调用(create_wait_tool, create_report_agent_job_result_tool, create_spawn_agents_on_csv_tool, create_list_mcp_resource_templates_tool, create_list_mcp_resources_tool, create_read_mcp_resource_tool, create_close_agent_tool_v1, create_collab_input_items_schema, create_followup_task_tool, create_interrupt_agent_tool_v2 (+15 more));外部调用 1 个(typed)。

JsonSchema::with_encrypted118–121 ↗
fn with_encrypted(mut self) -> Self

作用:给已有 schema 打上 encrypted 标记,表示这个参数是经过审核的加密参数。它不会改变参数形状,只是多贴一个标签。

数据流:进去一个 JsonSchema → 它把 encrypted 字段设为 true → 出来同一个 schema,但带有加密提示。

调用关系:它通常接在别的构造函数后面使用,像给某个字段盖章;后续序列化时这个标记会一起发出去。

JsonSchema::number123–125 ↗
fn number(description: Option<String>) -> Self

作用:快速创建数字 schema,适合小数或普通数值。比如超时时间、比例、数量等参数可用它描述。

数据流:进去可选说明文字 → 它调用 JsonSchema::typed,指定类型为 Number → 出来一个数字参数的 schema。

调用关系:等待、请求用户输入、执行命令等工具参数定义会用它,确保调用方知道这个字段要给数字。

调用图:被 14 处调用(create_wait_tool, create_spawn_agents_on_csv_tool, wait_agent_tool_parameters_v1, wait_agent_tool_parameters_v2, create_request_user_input_tool, create_exec_command_tool_with_environment_id, create_shell_command_tool, create_write_stdin_tool, exec_command_tool_matches_expected_spec, shell_command_tool_matches_expected_spec (+4 more));外部调用 1 个(typed)。

JsonSchema::integer127–129 ↗
fn integer(description: Option<String>) -> Self

作用:快速创建整数 schema,也就是不能带小数的数字。适合次数、序号这类参数。

数据流:进去可选说明文字 → 它调用 JsonSchema::typed,指定类型为 Integer → 出来一个整数参数的 schema。

调用关系:它被创建目标等工具使用,是 number 的更严格版本。

调用图:被 1 处调用(create_create_goal_tool);外部调用 1 个(typed)。

JsonSchema::null131–133 ↗
fn null(description: Option<String>) -> Self

作用:创建 null 类型 schema,也就是只允许空值。这个能力存在于类型系统里,但顶层工具输入不能单独是 null。

数据流:进去可选说明文字 → 它调用 JsonSchema::typed,指定类型为 Null → 出来一个 null schema。

调用关系:如果外部 schema 顶层只有 null,deserialize_tool_input_schema 会拒绝它,避免工具参数没有实际内容。

调用图:外部调用 1 个(typed)。

JsonSchema::string_enum135–142 ↗
fn string_enum(values: Vec<JsonValue>, description: Option<String>) -> Self

作用:创建“只能从固定文本里选一个”的 schema。比如状态只能是 open、closed 这类选项。

数据流:进去一组允许的 JSON 值和说明 → 它把类型设为 string,并把可选值写入 enum → 出来一个字符串枚举 schema。

调用关系:更新计划、审批参数、查看图片、更新目标等工具会调用它,用来限制调用者只能传合法选项。

调用图:被 4 处调用(create_update_plan_tool, create_approval_parameters, create_view_image_tool, create_update_goal_tool);外部调用 2 个(default, Single)。

JsonSchema::array144–151 ↗
fn array(items: JsonSchema, description: Option<String>) -> Self

作用:创建数组 schema,也就是一串同类或同规则的值。它会说明数组里的每一项应该长什么样。

数据流:进去数组元素的 schema 和可选说明 → 它把整体类型设为 Array,并把元素规则放进 items → 出来一个数组参数 schema。

调用关系:协作输入、等待 agent、更新计划、权限列表等工具会用它;它把另一个 JsonSchema 包起来,组成更大的结构。

调用图:被 6 处调用(create_collab_input_items_schema, wait_agent_tool_parameters_v1, create_update_plan_tool, create_request_user_input_tool, create_approval_parameters, file_system_permissions_schema);外部调用 3 个(new, default, Single)。

JsonSchema::object153–165 ↗
fn object(
        properties: BTreeMap<String, JsonSchema>,
        required: Option<Vec<String>>,
        additional_properties: Option<AdditionalProperties>,
    ) -> Self

作用:创建对象 schema,也就是一组带名字的字段。它能说明有哪些字段、哪些必填、是否允许额外字段。

数据流:进去字段表、必填字段列表、额外字段规则 → 它把类型设为 Object,并保存这些规则 → 出来一个对象参数 schema。

调用关系:大量工具定义函数会调用它,因为工具输入通常就是一个对象;它是组织复杂参数的核心积木。

调用图:被 38 处调用(create_wait_tool, create_report_agent_job_result_tool, create_spawn_agents_on_csv_tool, described_object, create_get_context_remaining_tool, create_list_available_plugins_to_install_tool, create_list_mcp_resource_templates_tool, create_list_mcp_resources_tool, create_read_mcp_resource_tool, create_collab_input_items_schema (+15 more));外部调用 2 个(default, Single)。

AdditionalProperties::from183–185 ↗
fn from(value: JsonSchema) -> Self

作用:把“是否允许额外字段”或“额外字段必须符合什么 schema”包装成统一格式。这样调用方写起来更省事。

数据流:进去一个布尔值或一个 JsonSchema → 它分别变成 Boolean 或 Schema 变体 → 出来一个 AdditionalProperties 值。

调用关系:JsonSchema::object 接收 AdditionalProperties;这个转换函数让上层既能传 true/false,也能传具体规则。

调用图:外部调用 3 个(new, Boolean, Schema)。

parse_tool_input_schema189–193 ↗
fn parse_tool_input_schema(input_schema: &JsonValue) -> Result<JsonSchema, serde_json::Error>

作用:解析工具的 input_schema,并在解析前做清洗、裁剪和压缩。普通外部 schema 进入系统时应该走这条安全通道。

数据流:进去原始 JSON 值 → 先 prepare_tool_input_schema 清洗和去掉没用定义,再 compact_large_tool_schema 尝试压小,最后 deserialize_tool_input_schema 转成 JsonSchema → 成功返回结构化 schema,失败返回 JSON 解析错误。

调用关系:这是主入口。它把准备、压缩、反序列化三段串起来,保证后续工具注册拿到的是项目能处理的 schema。

调用图:调用 3 个内部函数(compact_large_tool_schema, deserialize_tool_input_schema, prepare_tool_input_schema)。

parse_tool_input_schema_without_compaction196–200 ↗
fn parse_tool_input_schema_without_compaction(
    input_schema: &JsonValue,
) -> Result<JsonSchema, serde_json::Error>

作用:解析可信的工具 input_schema,但不做大 schema 压缩。适合已经知道大小和内容可靠的内部 schema。

数据流:进去原始 JSON 值 → 调用 prepare_tool_input_schema 做基本清洗和裁剪 → 再调用 deserialize_tool_input_schema 转成 JsonSchema → 返回结果或错误。

调用关系:它和 parse_tool_input_schema 很像,但跳过 compact_large_tool_schema,给可信来源保留更多细节。

调用图:调用 2 个内部函数(deserialize_tool_input_schema, prepare_tool_input_schema)。

prepare_tool_input_schema202–207 ↗
fn prepare_tool_input_schema(input_schema: &JsonValue) -> JsonValue

作用:把原始 schema 复制一份并整理到可解析状态。它不直接改变调用者传进来的原件。

数据流:进去一个 JSON schema 引用 → 它先 clone 出副本,再 sanitize_json_schema 兼容化,接着 prune_unreachable_definitions 删除用不到的定义 → 出来整理后的 JSON 值。

调用关系:parse_tool_input_schema 和 parse_tool_input_schema_without_compaction 都先调用它,相当于解析前的统一预处理站。

调用图:调用 2 个内部函数(prune_unreachable_definitions, sanitize_json_schema);被 2 处调用(parse_tool_input_schema, parse_tool_input_schema_without_compaction);外部调用 1 个(clone)。

deserialize_tool_input_schema209–218 ↗
fn deserialize_tool_input_schema(input_schema: JsonValue) -> Result<JsonSchema, serde_json::Error>

作用:把整理后的 JSON 真正变成 Rust 里的 JsonSchema 结构,并拒绝顶层单独为 null 的 schema。

数据流:进去一个 JSON 值 → 用 serde_json 转成 JsonSchema → 如果发现顶层类型只是 null,就生成错误 → 否则返回 JsonSchema。

调用关系:两个 parse 函数最后都会调用它;它是从“松散 JSON”进入“强类型结构”的最后一道门。

调用图:调用 1 个内部函数(singleton_null_schema_error);被 2 处调用(parse_tool_input_schema, parse_tool_input_schema_without_compaction);外部调用 2 个(matches!, from_value)。

compact_large_tool_schema229–236 ↗
fn compact_large_tool_schema(value: &mut JsonValue)

作用:当 schema 太大时,分阶段把它缩小。它尽量保留顶层参数外观,但会逐步牺牲说明文字、定义和深层细节。

数据流:进去一个可修改的 JSON schema → 每轮先用 compact_schema_fits_budget 看是否够小;还太大就执行一个压缩步骤 → 最后原地留下较小的 schema。

调用关系:parse_tool_input_schema 会调用它;它通过一组压缩步骤依次使用 strip_schema_descriptions、drop_schema_definitions、collapse_deep_schema_objects_from_root、prune_schema_compositions。

调用图:调用 1 个内部函数(compact_schema_fits_budget);被 1 处调用(parse_tool_input_schema)。

collapse_deep_schema_objects_from_root247–249 ↗
fn collapse_deep_schema_objects_from_root(value: &mut JsonValue)

作用:从根节点开始,把太深的复杂 schema 对象折叠掉。可以理解成地图太详细时,把远处小巷先简化成空白。

数据流:进去一个可修改的 JSON schema → 它从深度 0 调用 collapse_deep_schema_objects → 原地把过深复杂部分替换成空对象。

调用关系:它作为大 schema 压缩步骤之一被 compact_large_tool_schema 间接使用,专门处理层级太深导致的体积问题。

调用图:调用 1 个内部函数(collapse_deep_schema_objects)。

compact_schema_fits_budget251–253 ↗
fn compact_schema_fits_budget(value: &JsonValue) -> bool

作用:判断当前 schema 是否已经小到预算以内。这里用紧凑 JSON 字节数粗略代表接口的 token 限制。

数据流:进去一个 JSON schema → 调用 compact_normalized_schema_len 算标准化后的长度 → 和最大字节数比较 → 返回 true 或 false。

调用关系:compact_large_tool_schema 每执行一个压缩步骤前都会问它:现在够小了吗?够小就停止。

调用图:调用 1 个内部函数(compact_normalized_schema_len);被 1 处调用(compact_large_tool_schema)。

compact_normalized_schema_len255–260 ↗
fn compact_normalized_schema_len(value: &JsonValue) -> usize

作用:估算 schema 标准化后有多大。它先按 JsonSchema 解析再重新序列化,得到更接近实际发送内容的长度。

数据流:进去一个 JSON 值 → 克隆后尝试转成 JsonSchema,再转成紧凑字节数组 → 成功返回字节长度,失败返回 0。

调用关系:compact_schema_fits_budget 依赖它判断大小;如果 schema 解析不了,这里返回 0 会让压缩判断认为它不超预算。

调用图:被 1 处调用(compact_schema_fits_budget);外部调用 1 个(clone)。

for_each_schema_child268–304 ↗
fn for_each_schema_child(
    map: &serde_json::Map<String, JsonValue>,
    definition_traversal: DefinitionTraversal,
    visitor: &mut impl FnMut(&JsonValue),
)

作用:遍历一个 schema 对象下面真正算“子 schema”的地方。它像导游,只带访问者去 properties、items、组合规则等关键入口。

数据流:进去一个 JSON 对象、是否进入定义表的选择、一个只读访问函数 → 它找到所有子 schema 并逐个交给访问函数 → 不返回新值,只让调用方读取。

调用关系:collect_refs_outside_definitions 用它查找定义表外的引用;是否跳过定义表由 DefinitionTraversal 控制。

调用图:被 1 处调用(collect_refs_outside_definitions);外部调用 2 个(get, matches!)。

strip_schema_descriptions306–321 ↗
fn strip_schema_descriptions(value: &mut JsonValue)

作用:删除 schema 里的 description 说明文字。说明文字对人友好,但对机器校验不是必须,删除后能明显减小体积。

数据流:进去一个可修改的 JSON 值 → 如果是数组就逐项处理,如果是对象就删掉 description 并递归处理子 schema → 原地得到没有说明文字的 schema。

调用关系:它是大 schema 压缩的第一步,通常最安全,因为只去掉解释文字,不改变参数结构。

调用图:调用 1 个内部函数(for_each_schema_child_mut)。

for_each_schema_child_mut323–359 ↗
fn for_each_schema_child_mut(
    map: &mut serde_json::Map<String, JsonValue>,
    definition_traversal: DefinitionTraversal,
    visitor: &mut impl FnMut(&mut JsonValue),
)

作用:遍历一个 schema 对象下面的子 schema,并允许调用方修改它们。它是很多递归清理函数共用的“找孩子”工具。

数据流:进去一个可修改 JSON 对象、是否进入定义表的选择、一个可修改访问函数 → 它找到 properties、items、组合规则、额外字段规则等子 schema 并交给访问函数修改 → 原对象被原地更新。

调用关系:strip_schema_descriptions、rewrite_definition_refs_to_empty_schemas、collapse_deep_schema_objects、prune_schema_compositions 都靠它递归走完整棵 schema 树。

调用图:被 4 处调用(collapse_deep_schema_objects, prune_schema_compositions, rewrite_definition_refs_to_empty_schemas, strip_schema_descriptions);外部调用 2 个(get_mut, matches!)。

drop_schema_definitions364–374 ↗
fn drop_schema_definitions(value: &mut JsonValue)

作用:删除根部的定义表,也就是 $defs 和 definitions。删除前会先把本地引用改成空 schema,避免留下指向不存在内容的引用。

数据流:进去一个可修改 JSON schema → 先调用 rewrite_definition_refs_to_empty_schemas 替换本地定义引用 → 如果根是对象,就移除 $defs 和 definitions → 原地缩小 schema。

调用关系:它是大 schema 压缩步骤之一;在去掉定义表前先处理引用,是为了让后续解析器不会因为“找不到引用目标”表现不一致。

调用图:调用 1 个内部函数(rewrite_definition_refs_to_empty_schemas)。

rewrite_definition_refs_to_empty_schemas376–400 ↗
fn rewrite_definition_refs_to_empty_schemas(value: &mut JsonValue)

作用:把指向本地定义的 $ref 替换成空 schema。空 schema 表示“这里不再详细限制”,比坏掉的引用更安全。

数据流:进去一个可修改 JSON 值 → 遇到数组就递归,遇到对象就检查 $ref 是否指向本地定义;是的话整个节点变成 {},否则继续处理子 schema → 原地改写引用。

调用关系:drop_schema_definitions 会先调用它;它用 parse_local_definition_ref 判断引用是不是本地定义,并用 for_each_schema_child_mut 递归。

调用图:调用 1 个内部函数(for_each_schema_child_mut);被 1 处调用(drop_schema_definitions);外部调用 1 个(json!)。

collapse_deep_schema_objects402–421 ↗
fn collapse_deep_schema_objects(value: &mut JsonValue, depth: usize)

作用:把超过指定深度的复杂 schema 对象折叠成空对象。这样可以防止很深的参数结构把 schema 撑得太大。

数据流:进去一个可修改 JSON 值和当前深度 → 如果走到太深且节点很复杂,就替换为 {};否则继续递归处理子 schema → 原地简化深层结构。

调用关系:collapse_deep_schema_objects_from_root 从根调用它;它靠 is_complex_schema_object 判断一个节点是否值得折叠,靠 for_each_schema_child_mut 往下走。

调用图:调用 2 个内部函数(for_each_schema_child_mut, is_complex_schema_object);被 1 处调用(collapse_deep_schema_objects_from_root);外部调用 1 个(json!)。

prune_schema_compositions423–442 ↗
fn prune_schema_compositions(value: &mut JsonValue)

作用:删除含有 anyOf、oneOf、allOf 的复杂组合节点。组合规则表达力强,但也容易很大,所以作为较激进的压缩手段。

数据流:进去一个可修改 JSON 值 → 数组逐项递归;对象如果有组合关键字就替换成 {},否则继续处理子 schema → 原地删减复杂组合。

调用关系:它是大 schema 压缩的后期步骤,只有前面更温和的压缩还不够小时才会被 compact_large_tool_schema 用到。

调用图:调用 2 个内部函数(for_each_schema_child_mut, has_composition_keyword);外部调用 1 个(json!)。

is_complex_schema_object444–449 ↗
fn is_complex_schema_object(map: &serde_json::Map<String, JsonValue>) -> bool

作用:判断一个 schema 对象是不是“复杂节点”。有子 schema、properties、additionalProperties 或 $ref 的,都算复杂。

数据流:进去一个 JSON 对象 → 检查是否包含 items、anyOf、oneOf、allOf、properties、additionalProperties、$ref 等键 → 返回 true 或 false。

调用关系:collapse_deep_schema_objects 用它决定深层节点是否要被折叠成空对象。

调用图:被 1 处调用(collapse_deep_schema_objects);外部调用 1 个(contains_key)。

has_composition_keyword451–455 ↗
fn has_composition_keyword(map: &serde_json::Map<String, JsonValue>) -> bool

作用:判断一个 schema 对象是否使用了组合关键字 anyOf、oneOf 或 allOf。也就是是否在表达“多套规则的组合”。

数据流:进去一个 JSON 对象 → 检查三个组合键是否存在 → 返回 true 或 false。

调用关系:prune_schema_compositions 用它决定是否剪掉节点;sanitize_json_schema 也用它判断没有 type 的组合 schema 是否可以保留。

调用图:被 2 处调用(prune_schema_compositions, sanitize_json_schema)。

sanitize_json_schema466–544 ↗
fn sanitize_json_schema(value: &mut JsonValue)

作用:把各种写法不统一、项目不完全支持的 JSON Schema 清洗成可接受版本。它是整个文件最关键的兼容处理函数。

数据流:进去一个可修改 JSON 值 → 递归清洗子 schema、定义表和组合项;把 const 变成单值 enum;推断缺失的 type;给对象补 properties、给数组补 items;无法识别的空壳对象会被清空 → 原地得到更规范的 schema。

调用关系:prepare_tool_input_schema 首先调用它;sanitize_schema_table 处理定义表时也会递归调用它。它还调用 normalized_schema_types、write_schema_types、ensure_default_children_for_schema_types 等小工具。

调用图:调用 5 个内部函数(ensure_default_children_for_schema_types, has_composition_keyword, normalized_schema_types, sanitize_schema_table, write_schema_types);被 2 处调用(prepare_tool_input_schema, sanitize_schema_table);外部调用 4 个(Array, json!, matches!, vec!)。

sanitize_schema_table552–567 ↗
fn sanitize_schema_table(map: &mut serde_json::Map<String, JsonValue>, key: &str)

作用:专门清洗 $defs 或 definitions 这种定义表。定义表必须是对象,不合格的表会被直接删除。

数据流:进去一个 schema 对象和定义表名字 → 如果对应值是对象,就逐个清洗里面的定义;如果不是对象,就移除这个表 → 原地更新 schema。

调用关系:sanitize_json_schema 在处理每个对象时会调用它,确保定义表里的 schema 和内联 schema 用同样规则整理。

调用图:调用 1 个内部函数(sanitize_json_schema);被 1 处调用(sanitize_json_schema);外部调用 2 个(get_mut, remove)。

ensure_default_children_for_schema_types569–583 ↗
fn ensure_default_children_for_schema_types(
    map: &mut serde_json::Map<String, JsonValue>,
    schema_types: &[JsonSchemaPrimitiveType],
)

作用:给对象和数组补上必要的子字段。对象没有 properties、数组没有 items 时,后续结构化解析可能不完整。

数据流:进去一个可修改 JSON 对象和已经确定的类型列表 → 如果类型包含 object 且没有 properties,就补一个空字段表;如果包含 array 且没有 items,就补一个默认字符串元素规则 → 原地补齐缺省内容。

调用关系:sanitize_json_schema 在写回类型后调用它,保证 object 和 array schema 至少有基本骨架。

调用图:被 1 处调用(sanitize_json_schema);外部调用 6 个(Object, contains_key, insert, json!, new, contains)。

prune_unreachable_definitions593–602 ↗
fn prune_unreachable_definitions(value: &mut JsonValue)

作用:删除根定义表里从来没有被引用的定义。这样不会把无用说明发出去,节省空间。

数据流:进去一个可修改 JSON schema → collect_reachable_definitions 找出真正会被 $ref 用到的定义 → 对 $defs 和 definitions 分别调用 prune_schema_table → 原地只保留可达定义。

调用关系:prepare_tool_input_schema 在清洗后调用它;它负责把 schema 里的“备用零件库”清理到只剩会用到的零件。

调用图:调用 2 个内部函数(collect_reachable_definitions, prune_schema_table);被 1 处调用(prepare_tool_input_schema)。

prune_schema_table604–623 ↗
fn prune_schema_table(
    map: &mut serde_json::Map<String, JsonValue>,
    table: &'static str,
    reachable: &BTreeSet<DefinitionPointer>,
)

作用:在某一个定义表里,只留下可达定义。表空了就把整个表删掉。

数据流:进去根对象、表名、可达定义集合 → 找到对应定义表后,逐项检查名字是否在集合里 → 删除不可达项,若为空则移除表。

调用关系:prune_unreachable_definitions 对 $defs 和 definitions 都会调用它,实际执行裁剪动作。

调用图:被 1 处调用(prune_unreachable_definitions);外部调用 2 个(get_mut, remove)。

collect_reachable_definitions625–642 ↗
fn collect_reachable_definitions(value: &JsonValue) -> BTreeSet<DefinitionPointer>

作用:找出哪些本地定义真的被 schema 引用了。它会顺着引用继续追踪引用里的引用。

数据流:进去完整 JSON schema → 先 collect_refs_outside_definitions 找到定义表外的引用作为待处理队列 → 逐个取出引用,加入可达集合,再用 definition_for_pointer 找到定义内容并 collect_refs 继续发现新引用 → 返回所有可达定义指针。

调用关系:prune_unreachable_definitions 靠它决定哪些定义能保留;它串起 collect_refs_outside_definitions、definition_for_pointer 和 collect_refs。

调用图:调用 3 个内部函数(collect_refs, collect_refs_outside_definitions, definition_for_pointer);被 1 处调用(prune_unreachable_definitions);外部调用 2 个(new, new)。

collect_refs_outside_definitions644–659 ↗
fn collect_refs_outside_definitions(value: &JsonValue, refs: &mut Vec<DefinitionPointer>)

作用:收集定义表外部出现的本地 $ref 引用。这样可以知道主 schema 实际从哪里开始用定义。

数据流:进去一个 JSON 值和引用列表 → 递归数组和对象;对象先检查自己的 $ref,再通过 for_each_schema_child 遍历子 schema,但跳过定义表 → 把发现的定义指针追加到列表。

调用关系:collect_reachable_definitions 首先调用它,用来找到追踪定义可达性的起点。

调用图:调用 2 个内部函数(collect_ref_from_map, for_each_schema_child);被 1 处调用(collect_reachable_definitions)。

collect_refs661–676 ↗
fn collect_refs(value: &JsonValue, refs: &mut Vec<DefinitionPointer>)

作用:在任意 schema 片段里收集所有本地 $ref。它会进入对象的所有值,不只看标准子 schema 位置。

数据流:进去一个 JSON 值和引用列表 → 数组逐项递归;对象检查自己的 $ref,然后遍历所有字段继续递归 → 把发现的定义指针加入列表。

调用关系:collect_reachable_definitions 在进入某个已知可达定义后调用它,用来发现这个定义又依赖了哪些定义。

调用图:调用 1 个内部函数(collect_ref_from_map);被 1 处调用(collect_reachable_definitions)。

collect_ref_from_map678–687 ↗
fn collect_ref_from_map(
    map: &serde_json::Map<String, JsonValue>,
    refs: &mut Vec<DefinitionPointer>,
)

作用:从单个 JSON 对象里查看是否有本地 $ref。只处理能解析成本地定义引用的情况。

数据流:进去一个 JSON 对象和引用列表 → 如果 $ref 是字符串,就交给 parse_local_definition_ref 解析 → 成功则把得到的定义指针加入列表。

调用关系:collect_refs 和 collect_refs_outside_definitions 都用它检查当前对象,避免重复写解析 $ref 的逻辑。

调用图:调用 1 个内部函数(parse_local_definition_ref);被 2 处调用(collect_refs, collect_refs_outside_definitions);外部调用 1 个(get)。

definition_for_pointer689–700 ↗
fn definition_for_pointer(
    value: &'a JsonValue,
    pointer: &DefinitionPointer,
) -> Option<&'a JsonValue>

作用:根据定义指针找到根 schema 里的具体定义内容。指针里包含定义表名和定义名。

数据流:进去完整 JSON schema 和 DefinitionPointer → 如果根是对象,就进入对应的 $defs 或 definitions 表,再按名字查找 → 找到返回定义的 JSON 引用,找不到返回 None。

调用关系:collect_reachable_definitions 用它顺着引用进入定义内容,继续追踪定义之间的依赖。

调用图:被 1 处调用(collect_reachable_definitions)。

parse_local_definition_ref702–720 ↗
fn parse_local_definition_ref(schema_ref: &str) -> Option<DefinitionPointer>

作用:解析形如 #/$defs/User 的本地定义引用。它还支持指到定义内部更深位置的引用,但只把顶层定义名记为可达。

数据流:进去一个 $ref 字符串 → 确认以 # 开头,做 URL 解码,再按 JSON Pointer(JSON 路径写法)解析 → 如果指向 $defs 或 definitions 下的某个名字,就返回 DefinitionPointer,否则返回 None。

调用关系:collect_ref_from_map 用它识别引用;rewrite_definition_refs_to_empty_schemas 也用它判断某个 $ref 是否该被替换为空 schema。

调用图:被 1 处调用(collect_ref_from_map);外部调用 2 个(parse, decode)。

normalized_schema_types722–738 ↗
fn normalized_schema_types(
    map: &serde_json::Map<String, JsonValue>,
) -> Vec<JsonSchemaPrimitiveType>

作用:读取并规范化 schema 的 type 字段。type 可能是一个字符串,也可能是一组字符串。

数据流:进去一个 JSON 对象 → 查找 type;如果是字符串就转成一个基础类型,如果是数组就逐个转成支持的基础类型,无法识别的跳过 → 返回类型列表。

调用关系:sanitize_json_schema 调用它来理解当前对象声明了什么类型,然后再决定是否补类型、重写类型或补子字段。

调用图:调用 1 个内部函数(schema_type_from_str);被 1 处调用(sanitize_json_schema);外部调用 2 个(get, new)。

write_schema_types740–768 ↗
fn write_schema_types(
    map: &mut serde_json::Map<String, JsonValue>,
    schema_types: &[JsonSchemaPrimitiveType],
)

作用:把规范化后的类型列表写回 JSON 对象。它会根据数量决定写成单个字符串、字符串数组,或删除 type。

数据流:进去一个可修改 JSON 对象和类型列表 → 空列表就移除 type;一个类型就写成字符串;多个类型就写成数组 → 原地更新 type 字段。

调用关系:sanitize_json_schema 在推断和清理完类型后调用它,确保 type 字段使用项目支持的写法。

调用图:调用 1 个内部函数(schema_type_name);被 1 处调用(sanitize_json_schema);外部调用 5 个(Array, String, insert, remove, iter)。

schema_type_from_str770–781 ↗
fn schema_type_from_str(schema_type: &str) -> Option<JsonSchemaPrimitiveType>

作用:把 JSON Schema 里的类型名字转成代码里的枚举值。只接受项目支持的七种基础类型。

数据流:进去一个字符串,比如 string 或 object → 匹配支持的类型名 → 成功返回对应 JsonSchemaPrimitiveType,不认识就返回 None。

调用关系:normalized_schema_types 用它过滤和转换 type 字段中的类型名字。

调用图:被 1 处调用(normalized_schema_types)。

schema_type_name783–793 ↗
fn schema_type_name(schema_type: JsonSchemaPrimitiveType) -> &'static str

作用:把代码里的基础类型枚举转回 JSON Schema 使用的字符串名字。比如 JsonSchemaPrimitiveType::String 变成 string。

数据流:进去一个 JsonSchemaPrimitiveType → 按固定对应关系选择字符串 → 返回静态字符串切片。

调用关系:write_schema_types 用它把整理后的类型重新写回 JSON 对象。

调用图:被 1 处调用(write_schema_types)。

singleton_null_schema_error795–800 ↗
fn singleton_null_schema_error() -> serde_json::Error

作用:生成一个专门的错误:工具输入 schema 不能只是单独的 null。因为这样的工具参数没有可用结构。

数据流:不接收业务输入 → 创建一个 InvalidInput 类型的 I/O 错误,再包装成 serde_json::Error → 返回这个错误对象。

调用关系:deserialize_tool_input_schema 在发现顶层 schema 是单独 null 时调用它,用统一错误说明拒绝原因。

调用图:被 1 处调用(deserialize_tool_input_schema);外部调用 2 个(io, new)。

tools/src/mcp_tool.rs源码 ↗
io_transporttool discovery / setup

MCP 可以理解成一种让程序把外部工具接进来的协议。外部 MCP 服务器会告诉我们:我有哪些工具、每个工具要什么输入、会返回什么输出。这个文件做的事,就是把这份外来的说明整理成本项目自己的 ToolDefinition。它还会补一个很关键的小洞:有些 MCP 工具没有写输入参数里的 properties 字段,但 OpenAI 模型要求这个字段必须存在,所以这里会自动塞一个空对象,像给表格补上缺失的列名。然后它解析输入格式,并把输出格式包装成一次 MCP 调用结果应有的样子,包括 content、structuredContent、isError 和 _meta。这样后面的代码不用关心 MCP 的各种小差异,只拿到统一、干净的工具定义。

函数细节2
parse_mcp_tool6–37 ↗
fn parse_mcp_tool(tool: &rmcp::model::Tool) -> Result<ToolDefinition, serde_json::Error>

作用:把一个 MCP 工具对象转换成本项目内部使用的 ToolDefinition。有人会在加载外部 MCP 工具时用它,确保工具名、说明、输入格式和输出格式都变成统一样子。

数据流:进去的是一个 MCP 提供的 tool,里面有名称、描述、输入 schema 和可能存在的输出 schema。它先把输入 schema 变成 JSON 对象,如果缺少 properties 就补一个空的;再交给 parse_tool_input_schema 做输入格式解析;接着把输出 schema 包成标准的 MCP 调用结果格式。出来的是一个 ToolDefinition;如果输入 schema 解析失败,就出来一个 serde_json::Error 错误。

调用关系:它是这个文件的主要入口,通常在发现或注册 MCP 工具时被调用。它自己负责整理外部工具信息,并把输入解析交给外部的 parse_tool_input_schema,把输出包装交给本文件里的 mcp_call_tool_result_output_schema。

调用图:调用 1 个内部函数(mcp_call_tool_result_output_schema);外部调用 3 个(parse_tool_input_schema, new, Object)。

mcp_call_tool_result_output_schema39–60 ↗
fn mcp_call_tool_result_output_schema(structured_content_schema: JsonValue) -> JsonValue

作用:生成 MCP 工具调用结果的标准输出格式说明。它把真正的结构化结果 schema 放进 structuredContent,同时固定外层必须长什么样。

数据流:进去的是 structured_content_schema,也就是工具真正想返回的结构化内容格式。它用 json! 组装出一个 JSON schema:外层是对象,包含 content、structuredContent、isError 和 _meta,其中 content 是必需字段,并且不允许额外字段。出来的是这个完整的输出 schema。

调用关系:它是 parse_mcp_tool 的帮手,只负责把输出格式包成统一外壳。parse_mcp_tool 在创建 ToolDefinition 时会调用它,这样所有 MCP 工具的返回值看起来都符合同一套规则。

调用图:被 1 处调用(parse_mcp_tool);外部调用 1 个(json!)。

处理器工具

此模块提供共享的处理器接线和辅助例程,供具体工具实现,在受保护执行期间使用。

core/src/tools/handlers/mod.rs源码 ↗
orchestrationrequest handling / cross-cutting

这个文件连接了很多具体工具,比如执行命令、打补丁、请求用户输入、查看图片、列插件等。它自己不主要做某一个工具的业务,而是提供一批共用的小零件:把模型传来的 JSON 字符串变成 Rust 里的结构;在需要时按某个工作目录解析路径;根据环境编号找到这次对话里该用哪个运行环境;还会处理“额外权限”。额外权限可以理解成临时通行证,比如让命令多访问某个目录或网络。这里会检查:功能开关是否允许、审批策略是否合适、请求内容是否为空、已经批准过的权限能不能直接沿用。这样做的重点是安全和一致性:工具可以更方便地执行,但不能绕过沙箱(一个限制程序能碰什么的安全盒子)随便扩大权限。

函数细节18
parse_arguments80–87 ↗
fn parse_arguments(arguments: &str) -> Result<T, FunctionCallError>

作用:把工具调用传进来的参数字符串解析成指定的数据结构。模型调用工具时参数通常是 JSON 文本,这个函数负责把它变成程序真正能用的对象,并在格式错时给模型一个能看懂的错误。

数据流:进去的是一段 JSON 字符串,以及调用方期望得到的目标类型 → 它调用 JSON 解析器读取这段文本 → 成功时出来一个对应类型的值;失败时出来一个 FunctionCallError,提示“函数参数解析失败”。它不修改外部状态。

调用关系:它是很多工具入口的第一步,多个 handle_call 会先用它读懂模型给的参数。parse_arguments_with_base_path、resolve_workdir_base_path、rewrite_function_arguments 也把具体解析工作交给它,所以它相当于整个工具参数入口的统一门卫。

调用图:被 12 处调用(handle_call, parse_arguments_with_base_path, handle_call, handle_call, handle_call, resolve_workdir_base_path, rewrite_function_arguments, handle, handle_call, handle_call (+2 more));外部调用 1 个(from_str)。

updated_hook_command89–98 ↗
fn updated_hook_command(updated_input: &Value) -> Result<&str, FunctionCallError>

作用:从 hook 返回的更新内容里取出新的 command 字符串。hook 可以理解成工具执行前后插入的一段小检查或改写逻辑,这里确保它改出来的命令确实是文本。

数据流:进去的是一个 JSON 值 updated_input → 它查找里面名叫 command 的字段,并确认这个字段是字符串 → 成功时出来这个字符串引用;如果没有这个字段或字段不是字符串,就返回给模型看的错误。它不改动输入。

调用关系:它被多个 with_updated_hook_input 使用。那些流程拿到 hook 改写后的输入后,会通过这个函数确认新命令可用,再继续把命令交给后面的工具执行逻辑。

调用图:被 3 处调用(with_updated_hook_input, with_updated_hook_input, with_updated_hook_input);外部调用 1 个(get)。

rewrite_function_arguments100–117 ↗
fn rewrite_function_arguments(
    arguments: &str,
    tool_name: &str,
    rewrite: impl FnOnce(&mut Map<String, Value>),
) -> Result<String, FunctionCallError>

作用:安全地改写某个工具调用的 JSON 参数。它先确认参数是一个 JSON 对象,再让调用方改其中的字段,最后重新变回字符串。

数据流:进去的是原始参数字符串、工具名,以及一个“怎么改”的小函数 → 它先用 parse_arguments 解析 JSON,再确认顶层是对象,随后把对象交给小函数修改 → 成功时出来改写后的 JSON 字符串;如果参数不是对象或重新序列化失败,就返回错误。

调用关系:它是更具体的 rewrite_function_string_argument 的底层帮手。hook 改写输入时如果只是要替换某个字符串字段,会先走 rewrite_function_string_argument,再由这里完成通用的解析、修改、重新打包。

调用图:调用 1 个内部函数(parse_arguments);被 1 处调用(rewrite_function_string_argument);外部调用 3 个(format!, to_string, RespondToModel)。

rewrite_function_string_argument119–128 ↗
fn rewrite_function_string_argument(
    arguments: &str,
    tool_name: &str,
    field_name: &str,
    value: &str,
) -> Result<String, FunctionCallError>

作用:把某个工具参数里的指定字段替换成新的字符串值。它适合用在只需要改一个文本参数的场景,比如把 command 改成 hook 修正后的命令。

数据流:进去的是原始参数字符串、工具名、字段名和新的字符串值 → 它调用 rewrite_function_arguments,在 JSON 对象里插入或覆盖这个字段 → 出来的是更新后的 JSON 参数字符串;错误会从底层解析或序列化过程传出来。

调用关系:它被多个 with_updated_hook_input 调用。那些流程先通过 hook 得到新输入,再用这个函数把新值写回工具调用参数,最后继续执行原工具。

调用图:调用 1 个内部函数(rewrite_function_arguments);被 2 处调用(with_updated_hook_input, with_updated_hook_input)。

parse_arguments_with_base_path130–139 ↗
fn parse_arguments_with_base_path(
    arguments: &str,
    base_path: &AbsolutePathBuf,
) -> Result<T, FunctionCallError>

作用:在指定基础路径的语境下解析工具参数。它主要服务于带路径的参数,让相对路径能按照正确的工作目录理解。

数据流:进去的是参数 JSON 字符串和一个 base_path 基础路径 → 它临时设置一个 AbsolutePathBufGuard,也就是路径解析时用的“当前基准” → 然后调用 parse_arguments 得到目标类型;结束时这个临时基准会自动撤销。

调用关系:它被多个 handle_call 使用,通常出现在工具参数里有文件路径、工作目录等内容的时候。真正解析 JSON 的活交给 parse_arguments,它自己负责先把路径上下文摆正。

调用图:调用 2 个内部函数(parse_arguments, new);被 3 处调用(handle_call, handle_call, handle_call)。

resolve_workdir_base_path141–151 ↗
fn resolve_workdir_base_path(
    arguments: &str,
    default_cwd: &AbsolutePathBuf,
) -> Result<AbsolutePathBuf, FunctionCallError>

作用:算出工具这次应该在哪个工作目录下运行。模型可以在参数里给 workdir;如果没给,就使用默认当前目录。

数据流:进去的是参数 JSON 字符串和默认工作目录 default_cwd → 它先解析参数,再读取 workdir 字段;如果 workdir 是非空字符串,就把它接到默认目录后面,否则直接用默认目录 → 出来一个绝对路径对象。

调用关系:它被某个 handle_call 使用,用来在真正执行工具前确定基础目录。它依赖 parse_arguments 读懂 JSON,之后的工具执行会用这个目录解析相对路径或启动命令。

调用图:调用 1 个内部函数(parse_arguments);被 1 处调用(handle_call)。

resolve_tool_environment153–172 ↗
fn resolve_tool_environment(
    turn: &'a TurnContext,
    environment_id: Option<&str>,
) -> Result<Option<&'a TurnEnvironment>, FunctionCallError>

作用:根据环境编号找到这次工具应该使用的运行环境。运行环境可以理解成“在哪个隔间里干活”,比如不同的工作区或执行上下文。

数据流:进去的是当前回合 TurnContext 和可选的 environment_id → 如果没有给编号,它返回主环境;如果给了编号,它在当前回合的环境列表里查找匹配项 → 找到就返回这个环境,找不到就返回“未知环境编号”的错误。

调用关系:它被多个 handle_call 调用。工具在执行前需要知道用哪个环境,尤其是有多环境支持时;这个函数负责选择环境,后续具体工具再在这个环境中执行自己的动作。

调用图:被 4 处调用(handle_call, handle_call, handle_call, handle_call)。

normalize_and_validate_additional_permissions176–229 ↗
fn normalize_and_validate_additional_permissions(
    additional_permissions_allowed: bool,
    approval_policy: AskForApproval,
    sandbox_permissions: SandboxPermissions,
    additional_permissions

作用:检查并整理工具请求的额外权限。它防止工具在没有开关、没有审批或参数不完整的情况下偷偷扩大自己能访问的范围。

数据流:进去的是功能是否允许额外权限、审批策略、沙箱权限模式、请求的额外权限、这些权限是否已预先批准、当前目录 → 它判断是否真的在请求额外权限,检查功能开关和审批策略,要求权限内容不能为空,并把路径类权限规范化 → 成功时出来整理后的额外权限或 None;失败时出来一段明确的错误文字。

调用关系:它在当前文件里主要被测试函数直接验证。实际设计上,它属于执行类工具申请额外权限前的安全闸门;底层会调用 normalize_additional_permissions 做权限内容的标准化。

调用图:调用 1 个内部函数(normalize_additional_permissions);被 2 处调用(fresh_additional_permissions_still_require_exec_permission_approvals_feature, preapproved_permissions_work_when_request_permissions_tool_is_enabled_without_exec_permission_approvals_feature);外部调用 2 个(format!, matches!)。

implicit_granted_permissions237–252 ↗
fn implicit_granted_permissions(
    sandbox_permissions: SandboxPermissions,
    additional_permissions: Option<&AdditionalPermissionProfile>,
    effective_additional_permissions: &EffectiveAddition

作用:判断是否可以自动沿用之前已经授予的权限。比如用户之前允许过某个目录访问,后续工具没有显式再申请时,可以把这份“粘住的授权”带上。

数据流:进去的是本次显式沙箱模式、本次显式额外权限、以及已经算好的有效额外权限 → 它检查本次是否没有主动申请额外权限、也不是强制提权模式 → 如果满足,就返回已经生效的授权副本;否则返回 None。

调用关系:它被 run_exec_like 和 handle_call 等执行流程使用,也被两个测试验证边界情况。它的位置是在权限合并之后、具体执行之前,决定是否走“隐式沿用已批准权限”的路径。

调用图:调用 1 个内部函数(uses_additional_permissions);被 4 处调用(run_exec_like, explicit_inline_permissions_do_not_use_implicit_sticky_grant_path, implicit_sticky_grants_bypass_inline_permission_validation, handle_call);外部调用 1 个(matches!)。

apply_granted_turn_permissions254–298 ↗
async fn apply_granted_turn_permissions(
    session: &Session,
    environment_id: &str,
    cwd: &Path,
    sandbox_permissions: SandboxPermissions,
    additional_permissions: Option<AdditionalPerm

作用:把会话级和当前回合级已经批准的权限合并进本次工具执行。它让已经批准过的权限真正生效,同时判断这些权限是不是已经足够覆盖本次请求。

数据流:进去的是 Session、环境编号、当前目录、原始沙箱权限模式、以及本次额外权限请求 → 如果是强制提权模式,它直接返回原样且标记未预批准;否则它从 session 读取会话授权和本回合授权,合并它们,再和本次请求合并 → 出来一个 EffectiveAdditionalPermissions,里面包含最终沙箱模式、最终额外权限和是否预批准。

调用关系:它被 effective_patch_permissions、to_extension_call、run_exec_like、handle_call 等流程调用。它会向 Session 查询已授权权限,调用 merge_permission_profiles 合并权限,并用 permissions_are_preapproved 判断是否已被之前授权覆盖。

调用图:调用 3 个内部函数(permissions_are_preapproved, uses_additional_permissions, merge_permission_profiles);被 4 处调用(effective_patch_permissions, to_extension_call, run_exec_like, handle_call);外部调用 3 个(granted_session_permissions, granted_turn_permissions, matches!)。

permissions_are_preapproved300–312 ↗
fn permissions_are_preapproved(
    effective_permissions: &AdditionalPermissionProfile,
    granted_permissions: AdditionalPermissionProfile,
    cwd: &Path,
) -> bool

作用:判断本次最终需要的权限,是否已经完全包含在之前批准过的权限里。换句话说,它检查“这张新通行证要开的门,以前是不是都已经批准过”。

数据流:进去的是本次有效权限、已批准权限和当前目录 → 它先把有效权限按当前目录展开成可比较的形式,再把有效权限和已批准权限求交集 → 如果交集等于有效权限本身,就说明全部已预批准,返回 true;否则返回 false。

调用关系:它只被 apply_granted_turn_permissions 调用。权限合并完成后,apply_granted_turn_permissions 用它来决定是否可以跳过新的审批要求。

调用图:调用 1 个内部函数(intersect_permission_profiles);被 1 处调用(apply_granted_turn_permissions);外部调用 1 个(clone)。

tests::network_permissions336–343 ↗
fn network_permissions() -> AdditionalPermissionProfile

作用:为测试快速造一个“允许网络”的额外权限对象。它避免每个测试都重复写同样的样板数据。

数据流:进去没有参数 → 它创建一个 AdditionalPermissionProfile,并把 network.enabled 设为 true,其它字段用默认值 → 出来一个表示网络权限请求的对象。

调用关系:它被多个权限测试调用,特别是验证预批准权限和新鲜权限请求的场景。它只是测试夹具,也就是测试用的准备材料。

调用图:外部调用 1 个(default)。

tests::file_system_permissions345–355 ↗
fn file_system_permissions(path: &std::path::Path) -> AdditionalPermissionProfile

作用:为测试快速造一个“允许写某个目录”的文件系统权限对象。文件系统权限就是控制程序能读写哪些文件和目录的规则。

数据流:进去的是一个路径 → 它把这个路径转成绝对路径,并放进读写根目录权限里,其它字段使用默认值 → 出来一个包含文件系统写权限的 AdditionalPermissionProfile。

调用关系:它被隐式授权相关测试调用。测试先用它造出一份已授予的目录权限,再检查主逻辑是否会正确沿用或拒绝沿用。

调用图:调用 1 个内部函数(from_read_write_roots);外部调用 2 个(default, vec!)。

tests::preapproved_permissions_work_when_request_permissions_tool_is_enabled_without_exec_permission_approvals_feature358–379 ↗
fn preapproved_permissions_work_when_request_permissions_tool_is_enabled_without_exec_permission_approvals_feature()

作用:验证一种重要例外:即使“执行时申请权限”的功能开关没开,只要权限已经预批准,也应该允许使用。这样之前合法拿到的授权不会被误挡。

数据流:进去没有外部参数 → 它创建临时目录和网络权限,调用 normalize_and_validate_additional_permissions,并把 permissions_preapproved 设为 true → 期望出来的是成功结果,并且权限保持为网络权限。

调用关系:它直接测试 normalize_and_validate_additional_permissions。这个测试说明:预批准权限和新申请权限的限制不同,安全检查不能一刀切。

调用图:调用 1 个内部函数(normalize_and_validate_additional_permissions);外部调用 4 个(Granular, assert_eq!, network_permissions, tempdir)。

tests::fresh_additional_permissions_still_require_exec_permission_approvals_feature382–399 ↗
fn fresh_additional_permissions_still_require_exec_permission_approvals_feature()

作用:验证新申请的额外权限仍然需要功能开关允许。这样可以防止工具在配置没开启时临时索要更多权限。

数据流:进去没有外部参数 → 它创建临时目录和网络权限,调用 normalize_and_validate_additional_permissions,并把 additional_permissions_allowed 和 permissions_preapproved 都设为 false → 期望出来的是错误,错误文字说明需要开启 exec_permission_approvals 功能。

调用关系:它直接测试 normalize_and_validate_additional_permissions。它和上一个测试形成对照:预批准可以过,新鲜申请不能绕过开关。

调用图:调用 1 个内部函数(normalize_and_validate_additional_permissions);外部调用 3 个(assert_eq!, network_permissions, tempdir)。

tests::implicit_sticky_grants_bypass_inline_permission_validation402–416 ↗
fn implicit_sticky_grants_bypass_inline_permission_validation()

作用:验证已经“粘住”的授权可以被隐式带入,而不必当成一次新的内联权限请求。粘住的授权就是之前批准过、后续还能继续用的权限。

数据流:进去没有外部参数 → 它创建临时目录和文件系统权限,把这份权限放进 EffectiveAdditionalPermissions,再调用 implicit_granted_permissions,且本次没有显式额外权限请求 → 期望出来的就是那份已授予权限。

调用关系:它测试 implicit_granted_permissions。这个测试保证执行流程在没有显式申请时,可以正确使用已经算好的有效权限。

调用图:调用 1 个内部函数(implicit_granted_permissions);外部调用 3 个(assert_eq!, file_system_permissions, tempdir)。

tests::explicit_inline_permissions_do_not_use_implicit_sticky_grant_path419–433 ↗
fn explicit_inline_permissions_do_not_use_implicit_sticky_grant_path()

作用:验证如果本次已经显式写了额外权限请求,就不要再走“隐式沿用旧授权”的捷径。这样可以避免把两种权限路径混在一起。

数据流:进去没有外部参数 → 它创建临时目录和文件系统权限,同时把 sandbox_permissions 设为 WithAdditionalPermissions,并把本次 additional_permissions 传进去 → 调用 implicit_granted_permissions 后,期望结果是 None。

调用关系:它测试 implicit_granted_permissions 的另一个分支。和上一个测试一起说明:只有没有显式申请时,才会考虑隐式沿用。

调用图:调用 1 个内部函数(implicit_granted_permissions);外部调用 3 个(assert_eq!, file_system_permissions, tempdir)。

tests::relative_deny_glob_grants_remain_preapproved_after_materialization436–472 ↗
fn relative_deny_glob_grants_remain_preapproved_after_materialization()

作用:验证带相对 glob 规则的拒绝权限,在按当前目录展开后,仍然能被正确识别为已预批准。glob 规则就是像 **/*.env 这种用通配符匹配一批文件的写法。

数据流:进去没有外部参数 → 它创建临时目录,构造一份权限:允许写项目根目录,但拒绝匹配 **/*.env 的文件;然后用 intersect_permission_profiles 把规则按目录展开成存储授权,再与原请求合并 → 最后断言 permissions_are_preapproved 判断为 true。

调用关系:它主要覆盖 permissions_are_preapproved 背后的路径展开比较逻辑,也直接用到 intersect_permission_profiles 和 merge_permission_profiles。这个测试保护一个细节:相对拒绝规则不能因为展开方式不同而被误判为未批准。

调用图:调用 2 个内部函数(intersect_permission_profiles, merge_permission_profiles);外部调用 4 个(default, assert!, tempdir, vec!)。