Codex 系统手册

RPC 请求路由

stage-10.237 个文件

这一阶段像系统的总机和分诊台,是主干活儿时一直运转的部分。客户端、模型或外部工具发来 JSON-RPC 消息(带编号的远程请求)后,app-server 先做初始化、鉴权和排队,再分给对话、线程、文件、插件、MCP、反馈等接待员。core 找到并执行模型要用的工具。exec-server 和 MCP server 把执行命令、读写文件、调用 Codex 等请求送到真正模块。界面分拣消息,代理、监听等小件负责把关,避免串线。

本阶段的文件37

应用服务器分发核心

这些文件定义应用服务器的顶层路由层、共享处理器工具,以及支撑大多数连接范围消息处理的主要请求类别。

app-server/src/message_processor.rs源码 ↗
orchestrationstartup, request handling, connection teardown, cross-cutting

这个文件处理客户端和 app-server 之间的消息流。客户端发来的内容可能是 JSON-RPC(一种把“请求、响应、错误”包成统一格式的通信规矩),也可能是进程内直接传来的已解析请求。MessageProcessor 会先把消息变成内部认识的 ClientRequest,检查这条连接是否已经 initialize(初始化,类似进门登记),再看是否允许实验性功能,然后按请求类型转给配置、文件、线程、登录、插件、搜索、远程控制等专门处理器。它还保存每条连接的会话状态,比如客户端名字、版本、是否要安全证明。另一个重要点是排队:有些请求不能同时跑,它会按范围串行执行,避免两个操作同时改同一份东西。文件里还接上了外部登录刷新桥,让服务端缺 token 时可以反过来请求客户端刷新登录信息。

函数细节36
deserialize_client_request98–107 ↗
fn deserialize_client_request(
    request: &JSONRPCRequest,
) -> Result<ClientRequest, JSONRPCErrorError>

作用:把收到的通用 JSON-RPC 请求,转换成项目内部更具体的 ClientRequest。这样后面的代码就不用猜这条请求到底想干什么。

数据流:进去的是一条 JSONRPCRequest → 先转成 JSON 值,再按 ClientRequest 的格式解析 → 成功就得到类型明确的请求,失败就产出“无效请求”的错误。

调用关系:MessageProcessor::process_request 收到网络 JSON 请求后会先调用它。它只负责“翻译格式”,翻译完才交给 MessageProcessor::handle_client_request 去真正处理。

调用图:被 1 处调用(process_request);外部调用 1 个(to_value)。

ExternalAuthRefreshBridge::map_reason115–119 ↗
fn map_reason(reason: ExternalAuthRefreshReason) -> ChatgptAuthTokensRefreshReason

作用:把登录模块内部的“为什么要刷新认证”的原因,翻译成协议里发给客户端的原因。现在只处理一种情况:未授权,需要刷新。

数据流:进去的是 ExternalAuthRefreshReason → 根据枚举值做对应转换 → 出来的是 ChatgptAuthTokensRefreshReason,供发给客户端使用。

调用关系:ExternalAuthRefreshBridge::refresh 在向客户端请求新 token 前会用它做一次小翻译,保证登录模块和 app-server 协议说的是同一种语言。

ExternalAuthRefreshBridge::auth_mode171–173 ↗
fn auth_mode(&self) -> LoginAuthMode

作用:告诉登录系统,这个外部刷新桥服务的是 ChatGPT 登录模式。登录系统据此知道该把哪类刷新请求交给它。

数据流:不需要输入额外数据 → 直接返回 LoginAuthMode::Chatgpt → 不改动任何状态。

调用关系:它是 ExternalAuth 这个接口的一部分。AuthManager 需要判断外部认证类型时会通过这个方法识别这座桥。

ExternalAuthRefreshBridge::refresh175–180 ↗
fn refresh(
        &self,
        context: ExternalAuthRefreshContext,
    ) -> codex_login::ExternalAuthFuture<'_, ExternalAuthTokens>

作用:当服务端发现 ChatGPT 登录 token 失效时,通过现有连接请求客户端刷新 token。它像是服务端向客户端递一张“请重新取票”的纸条。

数据流:进去的是刷新上下文,比如刷新原因和之前的账号 ID → 组装成 ChatgptAuthTokensRefresh 请求发给客户端,并最多等 10 秒 → 成功时把客户端返回的 access token、账号 ID、套餐类型包装成 ExternalAuthTokens;超时、取消或客户端报错时返回输入输出错误。

调用关系:MessageProcessor::new 会把 ExternalAuthRefreshBridge 注册进 AuthManager。之后登录系统需要刷新外部认证时,会通过 ExternalAuth 接口调用这里;这里再借 OutgoingMessageSender 把请求发回客户端。

调用图:调用 1 个内部函数(chatgpt);外部调用 7 个(pin, map_reason, ChatgptAuthTokensRefresh, other, format!, from_value, timeout)。

ConnectionSessionState::default226–228 ↗
fn default() -> Self

作用:提供创建默认连接会话状态的快捷方式。它等同于创建一个全新的、还没初始化的连接状态。

数据流:没有输入 → 调用 ConnectionSessionState::new → 返回带有 RPC 闸门、但尚未初始化的会话状态。

调用关系:这是 Rust 的 Default 约定入口,外部代码需要默认值时会走这里;真正建对象的活交给 ConnectionSessionState::new。

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

ConnectionSessionState::new232–237 ↗
fn new() -> Self

作用:创建一条新连接的会话状态。这里会准备一扇 RPC 闸门,用来在连接关闭时等待正在跑的请求收尾。

数据流:没有输入 → 新建 ConnectionRpcGate,并创建一个空的 OnceLock(只能成功写入一次的格子)保存初始化信息 → 返回新的 ConnectionSessionState。

调用关系:启动未初始化连接、默认构造、测试或连接建立流程都会用它。后续 Initialize 请求会把客户端信息填进这个状态。

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

ConnectionSessionState::initialized239–241 ↗
fn initialized(&self) -> bool

作用:判断这条连接是否已经完成初始化。大多数业务请求必须先初始化,不能一连上就乱发。

数据流:读取内部 OnceLock → 如果里面已有初始化信息就返回 true,否则返回 false → 不修改状态。

调用关系:Initialize 之后的请求分发会用它拦截未登记的连接;如果没初始化,MessageProcessor::dispatch_initialized_client_request 会返回错误。

调用图:被 1 处调用(initialize);外部调用 1 个(get)。

ConnectionSessionState::experimental_api_enabled243–247 ↗
fn experimental_api_enabled(&self) -> bool

作用:判断这条连接是否声明允许使用实验性 API。实验性 API 就是还不稳定、需要客户端明确同意的功能。

数据流:读取初始化信息 → 如果已初始化并且 experimental_api_enabled 为 true,就返回 true;否则返回 false → 不修改状态。

调用关系:初始化后的请求分发会用它检查某些实验性请求。没开权限却调用实验功能时,请求会被拒绝。

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

ConnectionSessionState::opted_out_notification_methods249–254 ↗
fn opted_out_notification_methods(&self) -> HashSet<String>

作用:取出客户端不想接收的通知方法列表。这样服务端可以少发客户端明确不要的通知。

数据流:读取初始化信息 → 如果有,就复制其中的 opted_out_notification_methods;如果没有,就返回空集合 → 不修改状态。

调用关系:它给需要决定“某条通知该不该发”的流程使用。这里不做发送,只提供连接自己的偏好。

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

ConnectionSessionState::app_server_client_name256–260 ↗
fn app_server_client_name(&self) -> Option<&str>

作用:返回这条连接登记过的客户端名字,比如某个编辑器或嵌入端名称。服务端可用它做日志、追踪或细分行为。

数据流:读取初始化信息 → 有客户端名就返回字符串引用,没有初始化就返回 None → 不修改状态。

调用关系:追踪 span、远程控制和初始化后请求处理会读取它,用来知道当前请求来自哪类客户端。

调用图:被 2 处调用(client_name, typed_request_span);外部调用 1 个(get)。

ConnectionSessionState::client_version262–266 ↗
fn client_version(&self) -> Option<&str>

作用:返回客户端版本号。服务端可以据此记录问题、兼容不同版本客户端。

数据流:读取初始化信息 → 有版本就返回字符串引用,没初始化就返回 None → 不修改状态。

调用关系:请求追踪和线程/回合处理会用到它,方便把某次操作和客户端版本联系起来。

调用图:被 2 处调用(client_version, typed_request_span);外部调用 1 个(get)。

ConnectionSessionState::request_attestation268–272 ↗
fn request_attestation(&self) -> bool

作用:判断客户端是否要求请求安全证明。可以理解为客户端问:“这次服务端操作能不能带上可信身份证明?”

数据流:读取初始化信息 → 如果已初始化且 request_attestation 为 true,就返回 true;否则返回 false → 不修改状态。

调用关系:连接初始化成功后,MessageProcessor::connection_initialized 和相关线程处理会用这个值设置连接能力。

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

ConnectionSessionState::initialize274–276 ↗
fn initialize(&self, session: InitializedConnectionSessionState) -> Result<(), ()>

作用:把初始化信息写入连接状态,而且只允许写一次。这样一条连接不会被反复改身份。

数据流:进去的是 InitializedConnectionSessionState → 尝试放进 OnceLock → 成功返回 Ok,若之前已经初始化过则返回 Err。

调用关系:InitializeRequestProcessor 处理客户端 Initialize 请求时会调用它。之后其他请求再通过各种读取方法拿到这些会话信息。

调用图:被 1 处调用(initialize);外部调用 1 个(set)。

MessageProcessor::new301–564 ↗
fn new(args: MessageProcessorArgs) -> Self

作用:搭建整个请求处理器,把所有子处理器、线程管理器、插件、技能、认证刷新、文件监听等零件装配好。它是 app-server 请求系统开工前的总组装步骤。

数据流:进去的是 MessageProcessorArgs,里面有配置、认证、数据库、输出通道、环境管理器等共享资源 → 创建 ThreadManager、SkillsWatcher、各种 RequestProcessor,并把外部认证刷新桥注册到 AuthManager → 返回一个可处理各类客户端请求的 MessageProcessor。

调用关系:app-server 启动、测试构造和主运行流程会调用它。它自己不处理具体请求,而是把后面 MessageProcessor::handle_initialized_client_request 要用到的各个专门处理器先准备好。

调用图:调用 27 个内部函数(new, new, new, new, new, new, new, new, new, new (+15 more));被 4 处调用(start_uninitialized, build_test_processor, run_main_with_transport_options, run_main);外部调用 11 个(clone, new, new_cyclic, new, new, new, new, default, default, thread_store_from_config (+1 more))。

MessageProcessor::clear_runtime_references566–570 ↗
fn clear_runtime_references(&self)

作用:清掉运行期的一些外部引用,帮助关闭或重载时断开长生命周期资源。比如取消外部认证桥、停止应用列表和技能监听。

数据流:读取自身持有的 account_processor、apps_processor、skills_watcher → 分别调用清理或 shutdown → 不返回数据,但会让这些后台引用停止继续工作。

调用关系:通常在进程收尾或运行环境重置时调用。它把清理动作交给对应子模块,而不是自己逐个处理细节。

调用图:调用 2 个内部函数(clear_external_auth, shutdown)。

MessageProcessor::process_request572–624 ↗
async fn process_request(
        self: &Arc<Self>,
        connection_id: ConnectionId,
        request: JSONRPCRequest,
        transport: &AppServerTransport,
        session: Arc<ConnectionSession

作用:处理从传输层收到的一条 JSON-RPC 请求。这是普通网络/套接字客户端请求进入业务系统的入口。

数据流:进去的是连接 ID、原始 JSONRPCRequest、传输信息和连接会话 → 创建请求 ID、追踪上下文,把 JSON 请求解析成 ClientRequest → 调用 MessageProcessor::handle_client_request;如果出错,就通过 outgoing 发 JSON-RPC 错误响应。

调用关系:传输层收到客户端请求时会调用它。它先用 deserialize_client_request 翻译格式,再用 MessageProcessor::run_request_with_context 包上追踪上下文,最后交给共享的请求处理路径。

调用图:调用 4 个内部函数(request_span, handle_client_request, deserialize_client_request, new);外部调用 3 个(clone, run_request_with_context, trace!)。

MessageProcessor::process_client_request630–672 ↗
async fn process_client_request(
        self: &Arc<Self>,
        connection_id: ConnectionId,
        request: ClientRequest,
        session: Arc<ConnectionSessionState>,
        outbound_initializ

作用:处理进程内客户端传来的已解析请求。它跳过 JSON 反序列化,但仍然走和网络请求一样的业务规则。

数据流:进去的是连接 ID、ClientRequest、会话状态和 outbound_initialized 标记 → 生成请求上下文和追踪信息 → 调用 MessageProcessor::handle_client_request;失败时发送错误。

调用关系:嵌入式或同进程调用者会用它。它和 MessageProcessor::process_request 最终都会汇合到 MessageProcessor::handle_client_request,保证行为一致。

调用图:调用 3 个内部函数(typed_request_span, handle_client_request, new);外部调用 4 个(clone, id, run_request_with_context, trace!)。

MessageProcessor::process_notification674–678 ↗
async fn process_notification(&self, notification: JSONRPCNotification)

作用:处理客户端发来的 JSON-RPC 通知。当前服务端并不期待客户端通知,所以这里只记录日志。

数据流:进去的是 JSONRPCNotification → 写一条 info 日志 → 不返回业务结果,也不改动状态。

调用关系:传输层收到通知而不是请求时会调用它。因为现在没有通知业务,它不会再分发给其他处理器。

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

MessageProcessor::process_client_notification681–685 ↗
async fn process_client_notification(&self, notification: ClientNotification)

作用:处理进程内客户端发来的已解析通知。当前也只是记录下来,不做进一步动作。

数据流:进去的是 ClientNotification → 写一条 info 日志 → 没有响应,也不改变系统状态。

调用关系:这是进程内通知路径,对应 MessageProcessor::process_notification 的 typed 版本。现在两者都只是观察和记录。

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

MessageProcessor::run_request_with_context687–698 ↗
async fn run_request_with_context(
        outgoing: Arc<OutgoingMessageSender>,
        request_context: RequestContext,
        request_fut: F,
    )

作用:给一次请求挂上上下文和追踪信息再运行。可以把它理解成给包裹贴上快递单,后面日志和响应都知道这是谁的请求。

数据流:进去的是输出通道、RequestContext 和一个未来要执行的异步任务 → 先把上下文登记到 outgoing,再在追踪 span 里执行这个任务 → 任务完成后没有额外返回值。

调用关系:MessageProcessor::process_request 和 MessageProcessor::process_client_request 都用它包住真正处理逻辑。它不决定业务,只负责上下文登记和 tracing(追踪日志链路)。

调用图:调用 1 个内部函数(span);外部调用 2 个(instrument, clone)。

MessageProcessor::thread_created_receiver700–702 ↗
fn thread_created_receiver(&self) -> broadcast::Receiver<ThreadId>

作用:提供一个订阅入口,让别的地方能收到“新线程被创建了”的通知。这里的线程是对话/工作线程,不是操作系统线程。

数据流:读取 thread_processor → 向它要一个 broadcast Receiver(广播接收器,多个订阅者都能收到消息)→ 返回给调用者。

调用关系:需要监听新线程事件的组件会调用它。真正维护事件源的是 ThreadRequestProcessor。

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

MessageProcessor::send_initialize_notifications_to_connection704–711 ↗
async fn send_initialize_notifications_to_connection(
        &self,
        connection_id: ConnectionId,
    )

作用:给某一条连接单独发送初始化后的通知。比如新客户端刚登记完,需要把当前状态补发给它。

数据流:进去的是 connection_id → 转交 initialize_processor 发送只针对这条连接的初始化通知 → 不直接返回业务数据。

调用关系:连接初始化流程或 websocket 特定流程会用它。实际通知内容由 InitializeRequestProcessor 负责。

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

MessageProcessor::connection_initialized713–726 ↗
async fn connection_initialized(
        &self,
        connection_id: ConnectionId,
        request_attestation: bool,
    )

作用:告诉线程系统:某条连接已经初始化好了,并说明它有什么能力。比如是否要求安全证明。

数据流:进去的是连接 ID 和 request_attestation 标记 → 包装成 ConnectionCapabilities → 调用 thread_processor.connection_initialized 更新连接能力。

调用关系:外部连接流程确认初始化完成时会调用它。MessageProcessor::handle_client_request 在处理 Initialize 时也会做类似动作。

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

MessageProcessor::send_initialize_notifications728–732 ↗
async fn send_initialize_notifications(&self)

作用:向所有相关连接发送初始化通知。用于整体初始化后同步当前服务端状态。

数据流:没有额外输入 → 调用 initialize_processor.send_initialize_notifications → 通知被排入输出通道。

调用关系:启动或初始化阶段会用它。它只做转发,通知怎么组织由 InitializeRequestProcessor 决定。

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

MessageProcessor::try_attach_thread_listener734–742 ↗
async fn try_attach_thread_listener(
        &self,
        thread_id: ThreadId,
        connection_ids: Vec<ConnectionId>,
    )

作用:尝试把一些连接挂到某个线程的事件监听上。这样这些客户端能继续收到该线程的后续消息。

数据流:进去的是 thread_id 和一组 connection_id → 转交 thread_processor 尝试建立监听关系 → 不直接产出响应数据。

调用关系:恢复连接、重新订阅线程或多连接同步时会用到它。具体判断线程是否存在、能否监听由 ThreadRequestProcessor 做。

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

MessageProcessor::drain_background_tasks744–746 ↗
async fn drain_background_tasks(&self)

作用:等待线程相关后台任务尽量跑完。关闭或测试时常需要这个动作,避免任务还没收尾进程就退出。

数据流:没有输入 → 调用 thread_processor.drain_background_tasks → 等待后台任务排空或完成。

调用关系:收尾流程会调用它。MessageProcessor 不直接知道有哪些后台任务,交给 ThreadRequestProcessor 管。

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

MessageProcessor::cancel_active_login748–750 ↗
async fn cancel_active_login(&self)

作用:取消当前正在进行的登录流程。用户关闭窗口或退出时,不应该让登录流程继续悬着。

数据流:没有输入 → 调用 account_processor.cancel_active_login → 正在进行的登录尝试会被取消。

调用关系:关闭或用户主动取消登录时会走这里。真正知道登录状态的是 AccountRequestProcessor。

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

MessageProcessor::clear_all_thread_listeners752–754 ↗
async fn clear_all_thread_listeners(&self)

作用:清掉所有线程监听关系。通常用于断开连接或整体清理,避免已经不存在的客户端继续被当成监听者。

数据流:没有输入 → 调用 thread_processor.clear_all_thread_listeners → 线程监听表被清空。

调用关系:清理阶段使用。它把实际监听状态维护交给 ThreadRequestProcessor。

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

MessageProcessor::shutdown_threads756–758 ↗
async fn shutdown_threads(&self)

作用:关闭所有线程相关活动。用于服务端退出时让对话、后台终端等线程资源停止工作。

数据流:没有输入 → 调用 thread_processor.shutdown_threads → 线程系统进入关闭流程。

调用关系:进程收尾时调用。MessageProcessor 只是统一出口,具体关闭顺序由 ThreadRequestProcessor 负责。

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

MessageProcessor::connection_closed760–787 ↗
async fn connection_closed(
        &self,
        connection_id: ConnectionId,
        session_state: &ConnectionSessionState,
    )

作用:处理一条客户端连接断开后的清理工作。它会等待这条连接上的 RPC 请求收尾,然后通知各个子系统把与这条连接有关的东西删掉。

数据流:进去的是 connection_id 和该连接的会话状态 → 最多等 30 秒让 rpc_gate 里的请求结束;超时就记警告 → 依次通知 outgoing、文件、命令执行、进程执行、线程处理器清理该连接。

调用关系:传输层发现连接关闭时会调用它。它把连接关闭事件广播给所有会持有连接资源的处理器,避免文件监听、命令进程或线程订阅泄漏。

调用图:调用 4 个内部函数(connection_closed, connection_closed, connection_closed, connection_closed);外部调用 2 个(timeout, warn!)。

MessageProcessor::subscribe_running_assistant_turn_count789–792 ↗
fn subscribe_running_assistant_turn_count(&self) -> watch::Receiver<usize>

作用:订阅当前正在运行的助手回合数量。外部可以用它显示“现在有几个 AI 回答正在跑”。

数据流:没有输入 → 向 thread_processor 要一个 watch Receiver(只保存最新值的观察通道)→ 返回这个接收器。

调用关系:状态展示或监控组件会调用它。计数的维护在 ThreadRequestProcessor 内部。

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

MessageProcessor::process_response795–799 ↗
async fn process_response(&self, response: JSONRPCResponse)

作用:处理客户端发回来的独立响应。常见场景是服务端之前反向向客户端发了请求,现在客户端把结果回来了。

数据流:进去的是 JSONRPCResponse → 记录日志,取出 id 和 result → 调用 outgoing.notify_client_response 唤醒等待这次结果的服务端任务。

调用关系:例如 ExternalAuthRefreshBridge::refresh 发起 token 刷新请求后,就需要这类响应回来。这里把响应交还给 OutgoingMessageSender 管的等待者。

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

MessageProcessor::process_error802–805 ↗
async fn process_error(&self, err: JSONRPCError)

作用:处理客户端发回来的 JSON-RPC 错误。也就是服务端向客户端提问,客户端回答“失败了”。

数据流:进去的是 JSONRPCError → 记录错误日志 → 用错误里的 id 和 error 通知 outgoing 中等待结果的任务。

调用关系:和 MessageProcessor::process_response 是一对。它不自己修复错误,只把错误送回原先等待客户端回复的那段逻辑。

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

MessageProcessor::handle_client_request807–850 ↗
async fn handle_client_request(
        self: &Arc<Self>,
        connection_request_id: ConnectionRequestId,
        codex_request: ClientRequest,
        session: Arc<ConnectionSessionState>,

作用:处理一条已经变成 ClientRequest 的请求,并专门把 Initialize 请求拎出来先办。初始化像登记入住,没登记前不能使用大多数服务。

数据流:进去的是连接请求 ID、ClientRequest、会话状态、可选的 outbound 初始化标记和请求上下文 → 如果是 Initialize,就调用 initialize_processor.initialize,并在成功后通知 thread_processor 连接能力;如果不是 Initialize,就转给 MessageProcessor::dispatch_initialized_client_request → 返回成功或 JSON-RPC 错误。

调用关系:MessageProcessor::process_request 和 MessageProcessor::process_client_request 都调用它。它是“初始化请求”和“普通已初始化请求”的分岔口。

调用图:调用 3 个内部函数(dispatch_initialized_client_request, initialize, connection_initialized);被 2 处调用(process_client_request, process_request)。

MessageProcessor::dispatch_initialized_client_request852–913 ↗
async fn dispatch_initialized_client_request(
        self: &Arc<Self>,
        connection_request_id: ConnectionRequestId,
        codex_request: ClientRequest,
        session: Arc<ConnectionSession

作用:把已经初始化后的请求排队并安排执行。它会先检查连接是否初始化、实验功能是否允许,再决定请求是否需要串行排队。

数据流:进去的是连接请求 ID、ClientRequest、会话状态和请求上下文 → 未初始化就报错;实验 API 未开启也报错;通过后记录 initialized request,并把真正处理动作包装成 QueuedInitializedRequest → 如果请求有序列化范围,就放进对应队列;否则直接 spawn 成异步任务。

调用关系:MessageProcessor::handle_client_request 处理非 Initialize 请求时调用它。它最终会安排 MessageProcessor::handle_initialized_client_request 执行业务,并在出错时通过 outgoing 发错误。

调用图:调用 6 个内部函数(invalid_request, span, track_initialized_request, new, from_scope, enqueue);被 1 处调用(handle_client_request);外部调用 6 个(clone, experimental_reason, serialization_scope, clone, experimental_required_message, spawn)。

MessageProcessor::handle_initialized_client_request915–1485 ↗
async fn handle_initialized_client_request(
        self: Arc<Self>,
        connection_request_id: ConnectionRequestId,
        codex_request: ClientRequest,
        request_context: RequestContext,

作用:这是最大的请求分发开关:根据 ClientRequest 的具体种类,把活转交给配置、文件、线程、插件、登录、搜索、命令执行等专门处理器。它像服务大厅的总服务台,看号码牌后把人带到对应窗口。

数据流:进去的是连接请求 ID、具体 ClientRequest、请求上下文、客户端名和版本 → 按请求枚举逐项匹配,调用对应 processor 的方法 → 如果子处理器返回响应,就用 outgoing.send_response_as 发回客户端;如果返回 None,表示这类请求会异步通知或无需立即响应;如果出错,就发送错误响应。

调用关系:它只会在连接已初始化并通过 MessageProcessor::dispatch_initialized_client_request 安排后运行。它本身不做文件读写、登录、线程执行等细活,而是把每类请求交给 FsRequestProcessor、AccountRequestProcessor、ThreadRequestProcessor、TurnRequestProcessor 等对应模块。

调用图:调用 120 个内部函数(cancel_login_account, get_account, get_account_rate_limits, get_account_token_usage, get_auth_status, login_account, logout_account, send_add_credits_nudge_email, apps_list, collaboration_mode_list (+15 more));外部调用 4 个(id, consume_account_rate_limit_reset_credit, thread_delete, panic!)。

app-server/src/request_processors.rs源码 ↗
orchestrationrequest handling / cross-cutting

这个文件本身不直接完成某一个大功能,而是把账号、线程、插件、Git、MCP、沙盒、命令执行等很多“请求处理器”集中声明和导出。可以把它想成服务台的总目录:用户来办不同的事,目录告诉系统该去哪个窗口。它还放了几个共享的小函数,用来把用户传来的工作目录变成安全的绝对路径、检查一次对话要使用哪些运行环境、去掉重复的工作区目录,以及把底层保存的对话流水整理成接口要返回的“回合”。这些看起来小,但很重要:路径不先规范化,可能找错目录;环境不先校验,任务可能跑到不存在的环境;历史记录不过滤,界面可能显示不该显示的内部内容。

函数细节4
resolve_request_cwd536–542 ↗
fn resolve_request_cwd(cwd: Option<PathBuf>) -> Result<Option<AbsolutePathBuf>, JSONRPCErrorError>

作用:把请求里可选的当前工作目录,也就是命令应该在哪个文件夹里运行,转换成系统内部认可的绝对路径。如果路径不合法,它会给出 JSON-RPC 错误;JSON-RPC 可以理解成前后端用 JSON 传请求和回复的一套规则。

数据流:输入是一个可能为空的路径。它先把路径按本机系统规则做规范化,再尝试转成绝对路径;如果没有传路径,就保持为空;如果转换失败,就产出“invalid cwd”这样的请求错误。输出是一个可选的 AbsolutePathBuf,也就是已经确认过的绝对路径,不会改动外部状态。

调用关系:它是各类请求处理器的前置清洗步骤。后面的线程、命令或环境相关流程需要知道“在哪个目录办事”,就可以先用它把用户输入变成可靠路径;它不再把工作交给别的本文件函数,只调用底层路径工具和错误包装函数。

resolve_turn_environment_selections544–573 ↗
fn resolve_turn_environment_selections(
    thread_manager: &ThreadManager,
    environments: Option<Vec<TurnEnvironmentParams>>,
) -> Result<Option<Vec<TurnEnvironmentSelection>>, JSONRPCErrorError>

作用:把一次对话回合里指定的运行环境列表整理成核心系统能理解的形式,并确认这些环境真的可用。运行环境可以理解成“任务要在哪台机器或哪个沙盒、哪个目录里执行”。

数据流:输入是线程管理器和一个可选的环境参数列表。没有传环境时,它直接返回空;传了环境时,它逐个读取 environment_id 和 cwd,把 cwd 检查成绝对的 POSIX 或 Windows 路径 URI,然后组装成 TurnEnvironmentSelection 列表。最后它调用 thread_manager.validate_environment_selections 做统一校验;校验通过就返回整理好的列表,失败就返回请求错误。

调用关系:它位于“用户准备开始或控制一个回合”之前。请求处理器先用它把前端传来的环境选择翻译成核心层认识的结构,再交给 ThreadManager 校验;其中 validate_environment_selections 是真正判断环境组合是否允许的关口,with_capacity 只是提前按数量准备列表空间,减少重复分配。

调用图:调用 1 个内部函数(validate_environment_selections);外部调用 1 个(with_capacity)。

resolve_runtime_workspace_roots575–583 ↗
fn resolve_runtime_workspace_roots(workspace_roots: Vec<AbsolutePathBuf>) -> Vec<AbsolutePathBuf>

作用:清理运行时工作区根目录列表,把重复的目录去掉,同时保留原来的顺序。工作区根目录可以理解成这次任务允许关注的文件夹边界。

数据流:输入是一串已经是绝对路径的工作区目录。它从空列表开始,按顺序检查每个目录:如果前面没出现过,就加入结果;如果已经有了,就跳过。输出是一串去重后的目录列表,不修改原输入之外的任何状态。

调用关系:它通常作为请求进入核心逻辑前的整理步骤。后续配置、线程启动或运行环境设置可以拿到一份更干净的工作区列表,避免同一个目录被重复处理;它只依赖标准列表创建动作,不调用项目里的复杂模块。

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

build_api_turns_from_rollout_items609–617 ↗
fn build_api_turns_from_rollout_items(items: &[RolloutItem]) -> Vec<Turn>

作用:把底层保存的对话流水记录,转换成接口要返回给客户端的“回合”列表。这里的 rollout item 可以理解成系统保存的一条条历史事件,turn 则是界面更容易展示的一轮对话。

数据流:输入是一组 RolloutItem 历史项。它先创建 ThreadHistoryBuilder,也就是专门拼装对话历史的小工具;然后逐条检查历史项,只把 is_persisted_rollout_item 判断为需要长期保存的项交给 builder 处理;最后 builder.finish 产出 Vec<Turn>,也就是整理好的回合列表。它不改动原始历史,只生成一份面向 API 的结果。

调用关系:它连接“内部存档格式”和“外部接口格式”。线程读取、恢复或摘要相关流程需要把保存的历史展示给客户端时,会用这种转换;它把过滤判断交给外部的 is_persisted_rollout_item,把具体拼装工作交给 ThreadHistoryBuilder。

调用图:调用 1 个内部函数(new);外部调用 1 个(is_persisted_rollout_item)。

app-server/src/request_processors/request_errors.rs源码 ↗
domain_logicrequest handling

这个文件很小,但位置很关键:它像一个“翻译员”。服务器在处理请求时,可能需要检查用户选的运行环境是否有效。如果检查失败,内部会得到一个 CodexErr 错误;但客户端通常不懂这种内部错误格式,它需要的是 JSON-RPC 错误。JSON-RPC 是一种客户端和服务器用 JSON 互相发请求、回结果的通信格式。这里的 environment_selection_error 会看错误属于哪一类:如果是 InvalidRequest,也就是用户发来的请求本身不合法,就把原始提示包装成“无效请求”错误;如果是别的错误,就统一包装成“服务器内部错误”,并附上一句说明,表示验证环境选择失败。这样做既能给用户合理反馈,也避免内部细节泄漏太多。

函数细节1
environment_selection_error3–8 ↗
fn environment_selection_error(err: CodexErr) -> JSONRPCErrorError

作用:这个函数专门把环境选择验证失败的错误,转换成 JSON-RPC 返回格式。调用它的人不用关心内部错误该怎么对外表达,只要把 CodexErr 交进来就行。

数据流:进去的是一个 CodexErr,也就是服务器内部表示错误的值。函数先判断它是不是 InvalidRequest:如果是,就把里面的提示文字变成“请求无效”的 JSON-RPC 错误;如果不是,就用 format! 拼出一段“验证环境选择失败”的说明,再变成“内部错误”的 JSON-RPC 错误。出来的是一个 JSONRPCErrorError,供服务器直接返回给客户端。

调用关系:它位于请求处理流程的错误出口附近:前面的代码负责验证环境选择,一旦得到 CodexErr,就会交给这个函数翻译成客户端看得懂的 JSON-RPC 错误。函数内部只额外使用 format! 来拼接错误说明,不再把工作交给更复杂的流程。

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

app-server/src/request_processors/catalog_processor.rs源码 ↗
orchestrationrequest handling

这个文件里的 CatalogRequestProcessor 像一个前台接待员。客户端发来 JSON-RPC 请求(可以理解成一种“用 JSON 说话的远程调用格式”)后,它不会自己做所有事,而是去问配置管理器、认证管理器、技能管理器、插件管理器、线程管理器等部件,然后把结果包装成客户端能看懂的响应。比如列技能时,它会先确认当前目录、重新读取最新配置、判断工作区是否允许 Codex 插件,再把技能、错误、启用状态整理出来。列钩子时,它会把配置里的钩子和插件带来的钩子合在一起。列模型、实验功能、权限配置时,它还支持分页,避免一次返回太多。这里也负责一些会改变状态的请求,比如写入技能开关、设置额外技能目录,并在变化后通知客户端“技能变了”。

函数细节25
skills_to_info18–62 ↗
fn skills_to_info(
    skills: &[codex_core::skills::SkillMetadata],
    disabled_paths: &HashSet<AbsolutePathBuf>,
) -> Vec<codex_app_server_protocol::SkillMetadata>

作用:把内部使用的技能资料,转换成客户端协议里要返回的技能资料。它还会顺手标出这个技能现在是启用还是禁用。

数据流:输入是一组内部技能元数据,以及一组被禁用的技能文件路径。函数逐个查看技能,把名称、描述、界面展示信息、依赖工具、路径、作用范围等字段搬到协议对象里;如果技能路径出现在禁用列表里,就把 enabled 标成 false。输出是一组客户端可以直接收到的技能信息。

调用关系:它是技能列表返回前的“翻译工”。上游拿到的是核心层的技能对象,发给客户端前需要经过它改成 app-server 协议格式;它内部主要只是遍历列表,不再把工作交给复杂组件。

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

hooks_to_info64–85 ↗
fn hooks_to_info(hooks: &[codex_hooks::HookListEntry]) -> Vec<HookMetadata>

作用:把内部的钩子条目转换成客户端能读懂的钩子信息。钩子可以理解成“某个事件发生时自动执行的动作”。

数据流:输入是一组内部钩子条目。函数逐个复制关键字段,比如事件名、处理方式、匹配条件、命令、超时时间、来源、插件编号、是否启用、是否受管理、信任状态等。输出是一组协议层的 HookMetadata。

调用关系:CatalogRequestProcessor::hooks_list_response 在列出钩子后会调用它。它处在“核心钩子系统已经算完结果,准备发给客户端”这一步。

调用图:被 1 处调用(hooks_list_response);外部调用 1 个(iter)。

errors_to_info87–97 ↗
fn errors_to_info(
    errors: &[codex_core::skills::SkillError],
) -> Vec<codex_app_server_protocol::SkillErrorInfo>

作用:把技能加载时产生的内部错误,转换成客户端能显示的错误信息。

数据流:输入是一组技能错误,每个错误包含出错路径和错误文字。函数逐个提取路径和消息,生成协议里的 SkillErrorInfo。输出是一组可以随技能列表一起返回的错误说明。

调用关系:它是技能列表流程里的错误翻译器。技能管理器发现问题后,响应层用它把错误整理成客户端格式。

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

CatalogRequestProcessor::new100–118 ↗
fn new(
        outgoing: Arc<OutgoingMessageSender>,
        skills_watcher: Arc<SkillsWatcher>,
        auth_manager: Arc<AuthManager>,
        thread_manager: Arc<ThreadManager>,
        config: Ar

作用:创建一个 CatalogRequestProcessor,并把它后面处理请求需要用到的各个工具都装进去。

数据流:输入包括发消息用的 outgoing、监控技能目录的 skills_watcher、认证管理器、线程管理器、配置对象、配置管理器、工作区设置缓存。函数只是把这些东西保存到结构体字段里。输出是一个可以处理目录类请求的新处理器。

调用关系:它在系统组装请求处理器时被调用。后续所有列表、写配置、通知客户端的函数,都会使用这里保存下来的这些共享组件。

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

CatalogRequestProcessor::skills_list120–127 ↗
async fn skills_list(
        &self,
        params: SkillsListParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理客户端的“列出技能”请求。它是对外入口,负责把内部响应包成通用客户端响应。

数据流:输入是 SkillsListParams,里面可能有要查询的目录和是否强制刷新。函数把实际工作交给 CatalogRequestProcessor::skills_list_response,拿到技能列表响应后转成 ClientResponsePayload,并包在 Some 里返回;如果出错就返回 JSON-RPC 错误。

调用关系:它由 handle_initialized_client_request 在收到对应请求时调用。它本身不算技能,而是把活儿交给 CatalogRequestProcessor::skills_list_response。

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

CatalogRequestProcessor::hooks_list129–136 ↗
async fn hooks_list(
        &self,
        params: HooksListParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理客户端的“列出钩子”请求,把项目里会自动触发的动作列出来。

数据流:输入是 HooksListParams,通常包含要查看的目录。函数调用 CatalogRequestProcessor::hooks_list_response 获取结果,再转换成统一的客户端响应格式返回。

调用关系:它由 handle_initialized_client_request 分发调用。真正解析配置、插件和钩子的工作在 CatalogRequestProcessor::hooks_list_response 里完成。

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

CatalogRequestProcessor::skills_config_write138–145 ↗
async fn skills_config_write(
        &self,
        params: SkillsConfigWriteParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理客户端修改技能启用状态的请求,比如打开或关闭某个技能。

数据流:输入是 SkillsConfigWriteParams,包含技能路径或技能名,以及目标启用状态。函数调用 CatalogRequestProcessor::skills_config_write_response_inner 写入配置,成功后把结果转成通用响应。

调用关系:它由 handle_initialized_client_request 在收到技能配置写入请求时调用。它是外层包装,具体校验参数、写配置、清缓存都交给内部函数。

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

CatalogRequestProcessor::skills_extra_roots_set147–154 ↗
async fn skills_extra_roots_set(
        &self,
        params: SkillsExtraRootsSetParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理客户端设置“额外技能目录”的请求。额外技能目录就是除了默认位置以外,也去这些地方找技能。

数据流:输入是 SkillsExtraRootsSetParams,里面是额外目录列表。函数把它交给 CatalogRequestProcessor::skills_extra_roots_set_response,成功后返回一个空的成功响应。

调用关系:它由 handle_initialized_client_request 调用。真正更新 watcher、技能管理器并通知客户端的动作在内部响应函数里。

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

CatalogRequestProcessor::model_list156–163 ↗
async fn model_list(
        &self,
        params: ModelListParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理客户端的“列出可用模型”请求。模型就是系统可以调用的 AI 模型选项。

数据流:输入是 ModelListParams,可能包含分页大小、游标和是否包含隐藏模型。函数把线程管理器和参数传给 CatalogRequestProcessor::list_models,拿到分页后的模型列表,再转成客户端响应。

调用关系:它由 handle_initialized_client_request 分发调用。它不自己分页,而是把核心工作交给 CatalogRequestProcessor::list_models。

调用图:被 1 处调用(handle_initialized_client_request);外部调用 1 个(list_models)。

CatalogRequestProcessor::experimental_feature_list165–172 ↗
async fn experimental_feature_list(
        &self,
        params: ExperimentalFeatureListParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理客户端的“列出实验功能”请求,让客户端知道哪些新功能存在、处于什么阶段、当前是否启用。

数据流:输入是 ExperimentalFeatureListParams,可能包含分页和线程编号。函数调用 CatalogRequestProcessor::experimental_feature_list_response 生成列表,再包装成统一响应。

调用关系:它由 handle_initialized_client_request 调用。配置读取、线程配置选择、工作区插件限制判断,都在内部响应函数里完成。

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

CatalogRequestProcessor::permission_profile_list174–181 ↗
async fn permission_profile_list(
        &self,
        params: PermissionProfileListParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理客户端的“列出权限方案”请求。权限方案可以理解成预设的安全规则,比如只读、工作区内可写、完全访问。

数据流:输入是 PermissionProfileListParams,可能指定目录、分页大小和游标。函数调用 CatalogRequestProcessor::permission_profile_list_response 拿到列表,再转成客户端响应。

调用关系:它由 handle_initialized_client_request 调用。它只是外层接口,具体从配置层合成权限方案列表的工作由内部函数完成。

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

CatalogRequestProcessor::collaboration_mode_list183–190 ↗
async fn collaboration_mode_list(
        &self,
        params: CollaborationModeListParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理客户端的“列出协作模式”请求。协作模式表示线程或会话可以用哪些合作方式运行。

数据流:输入是 CollaborationModeListParams。函数把线程管理器和参数交给 CatalogRequestProcessor::list_collaboration_modes,拿到模式列表后包装成客户端响应。

调用关系:它由 handle_initialized_client_request 调用。实际读取线程管理器里可用协作模式的是 CatalogRequestProcessor::list_collaboration_modes。

调用图:被 1 处调用(handle_initialized_client_request);外部调用 1 个(list_collaboration_modes)。

CatalogRequestProcessor::mock_experimental_method192–199 ↗
async fn mock_experimental_method(
        &self,
        params: MockExperimentalMethodParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理一个测试性质的实验接口。它主要用来验证实验接口链路是否能正常收发数据。

数据流:输入是 MockExperimentalMethodParams,里面有一个 value。函数调用 CatalogRequestProcessor::mock_experimental_method_inner,让内部函数原样回传这个值,再包装成客户端响应。

调用关系:它由 handle_initialized_client_request 调用。它像一条“回声测试”通道,实际回声逻辑在内部函数里。

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

CatalogRequestProcessor::resolve_cwd_config201–214 ↗
async fn resolve_cwd_config(
        &self,
        cwd: &Path,
    ) -> Result<(AbsolutePathBuf, ConfigLayerStack), String>

作用:把一个目录变成绝对路径,并加载这个目录对应的配置层。配置层可以理解成多张叠在一起的设置表,比如全局设置、项目设置、目录设置。

数据流:输入是 cwd 路径。函数先用 relative_to_current_dir 把它转成绝对路径,再调用 load_config_layers_for_cwd 读取这个目录适用的配置层栈。输出是绝对目录和配置层栈;如果路径或配置加载失败,就把错误变成字符串返回。

调用关系:CatalogRequestProcessor::permission_profile_list_response 会用它按指定目录读取权限配置;技能列表流程也需要类似能力来按目录算配置。它是“目录到配置”的小关卡。

调用图:调用 2 个内部函数(load_config_layers_for_cwd, relative_to_current_dir);被 1 处调用(permission_profile_list_response)。

CatalogRequestProcessor::load_latest_config216–224 ↗
async fn load_latest_config(
        &self,
        fallback_cwd: Option<PathBuf>,
    ) -> Result<Config, JSONRPCErrorError>

作用:重新读取最新配置,避免用旧设置回答客户端。比如用户刚改了配置文件,这里会尽量拿到新版本。

数据流:输入是可选的备用工作目录 fallback_cwd。函数调用配置管理器的 load_latest_config;成功就返回新的 Config,失败就包装成 JSON-RPC 内部错误,告诉客户端服务器这边刷新配置失败。

调用关系:CatalogRequestProcessor::experimental_feature_list_response 和 CatalogRequestProcessor::skills_list_response 会调用它。它承担“先刷新配置,再回答问题”的步骤。

调用图:调用 1 个内部函数(load_latest_config);被 2 处调用(experimental_feature_list_response, skills_list_response)。

CatalogRequestProcessor::workspace_codex_plugins_enabled226–246 ↗
async fn workspace_codex_plugins_enabled(
        &self,
        config: &Config,
        auth: Option<&CodexAuth>,
    ) -> bool

作用:判断当前工作区是否允许使用 Codex 插件。插件可能带来额外技能和钩子,所以这个开关会影响很多列表结果。

数据流:输入是当前配置和可选认证信息。函数调用 workspace_settings::codex_plugins_enabled_for_workspace,并带上工作区设置缓存;如果读取成功,就返回真实开关。如果读取失败,它会记录一条警告,并默认允许插件,避免因为设置服务出错就把功能关掉。

调用关系:CatalogRequestProcessor::experimental_feature_list_response、CatalogRequestProcessor::hooks_list_response 和 CatalogRequestProcessor::skills_list_response 都会调用它。它是这些流程里判断“插件相关内容能不能出现”的共同门卫。

调用图:调用 1 个内部函数(codex_plugins_enabled_for_workspace);被 3 处调用(experimental_feature_list_response, hooks_list_response, skills_list_response);外部调用 1 个(warn!)。

CatalogRequestProcessor::list_models248–293 ↗
async fn list_models(
        thread_manager: Arc<ThreadManager>,
        params: ModelListParams,
    ) -> Result<ModelListResponse, JSONRPCErrorError>

作用:生成可用模型列表,并按客户端要求分页返回。分页就是一次只给一段,客户端下次用游标继续拿。

数据流:输入是线程管理器和 ModelListParams。函数先取得支持的模型列表,按 include_hidden 决定是否包含隐藏模型;然后根据 limit 和 cursor 算出起止位置,切出当前页;如果游标不是数字或超过总数,就返回请求错误。输出是当前页模型和下一个游标。

调用关系:CatalogRequestProcessor::model_list 会调用它。它位于模型查询流程的核心位置,负责把完整模型清单整理成客户端可逐页读取的结果。

调用图:外部调用 2 个(new, format!)。

CatalogRequestProcessor::list_collaboration_modes295–307 ↗
async fn list_collaboration_modes(
        thread_manager: Arc<ThreadManager>,
        params: CollaborationModeListParams,
    ) -> Result<CollaborationModeListResponse, JSONRPCErrorError>

作用:从线程管理器里取出系统支持的协作模式,并转换成客户端协议格式。

数据流:输入是线程管理器和空参数 CollaborationModeListParams。函数调用线程管理器列出协作模式,把每个模式转换成协议对象,最后返回 CollaborationModeListResponse。

调用关系:CatalogRequestProcessor::collaboration_mode_list 会调用它。它是协作模式列表请求里的实际读取步骤。

CatalogRequestProcessor::experimental_feature_list_response309–415 ↗
async fn experimental_feature_list_response(
        &self,
        params: ExperimentalFeatureListParams,
    ) -> Result<ExperimentalFeatureListResponse, JSONRPCErrorError>

作用:生成实验功能列表,告诉客户端每个功能叫什么、处于什么阶段、默认是否开启、现在是否真正开启。

数据流:输入包含分页信息和可选线程编号。如果给了线程编号,函数先把字符串转成 ThreadId,找到线程,并按该线程配置刷新最新配置;否则加载全局最新配置。然后读取认证信息,判断工作区是否允许 Codex 插件。接着遍历内置功能清单 FEATURES,把每个功能变成 API 对象,并把插件开关限制也算进去。最后按 cursor 和 limit 分页输出;游标非法或越界会返回请求错误。

调用关系:CatalogRequestProcessor::experimental_feature_list 会调用它。它会用到 CatalogRequestProcessor::load_latest_config、CatalogRequestProcessor::workspace_codex_plugins_enabled,也可能通过配置管理器按线程加载配置。

调用图:调用 4 个内部函数(load_latest_config_for_thread, load_latest_config, workspace_codex_plugins_enabled, from_string);被 1 处调用(experimental_feature_list);外部调用 2 个(new, format!)。

CatalogRequestProcessor::permission_profile_list_response417–487 ↗
async fn permission_profile_list_response(
        &self,
        params: PermissionProfileListParams,
    ) -> Result<PermissionProfileListResponse, JSONRPCErrorError>

作用:生成权限方案列表,把内置方案和用户在配置里写的自定义方案合在一起返回。

数据流:输入包含可选目录 cwd、分页游标和分页大小。如果指定了目录,它先通过 CatalogRequestProcessor::resolve_cwd_config 读取该目录的配置层;否则加载默认配置层。然后把最终生效配置转成 ConfigToml,从 permissions 里取出用户自定义方案,按 id 排序,再接到三个内置方案后面。最后按分页参数切出一页返回;游标非法或越界会报错。

调用关系:CatalogRequestProcessor::permission_profile_list 会调用它。它会把按目录解析配置的工作交给 CatalogRequestProcessor::resolve_cwd_config,自己负责合并内置和配置里的权限方案。

调用图:调用 2 个内部函数(load_config_layers, resolve_cwd_config);被 1 处调用(permission_profile_list);外部调用 3 个(from, format!, vec!)。

CatalogRequestProcessor::mock_experimental_method_inner489–496 ↗
async fn mock_experimental_method_inner(
        &self,
        params: MockExperimentalMethodParams,
    ) -> Result<MockExperimentalMethodResponse, JSONRPCErrorError>

作用:实现实验接口的简单回声行为:收到什么值,就把什么值放进响应里。

数据流:输入是 MockExperimentalMethodParams,里面有 value。函数取出 value,放到 MockExperimentalMethodResponse 的 echoed 字段里。输出就是这个响应,不改动其他状态。

调用关系:CatalogRequestProcessor::mock_experimental_method 会调用它。它通常用于测试请求链路,而不是完成真实业务。

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

CatalogRequestProcessor::skills_list_response498–583 ↗
async fn skills_list_response(
        &self,
        params: SkillsListParams,
    ) -> Result<SkillsListResponse, JSONRPCErrorError>

作用:真正生成技能列表响应。它会按一个或多个目录分别计算可用技能、禁用状态和加载错误。

数据流:输入包含目录列表 cwds 和 force_reload。目录为空时使用当前配置里的默认目录。函数先刷新最新配置,读取认证信息,判断工作区是否允许插件;再拿到技能管理器、插件管理器和默认文件系统。随后它并发处理多个目录,但最多同时处理 5 个:每个目录先解析配置,如果失败就为该目录返回错误;成功后根据插件开关算出额外技能根目录,构造 SkillsLoadInput,让技能管理器加载技能。最后把错误和技能分别转成协议格式,按原目录顺序排好后返回。

调用关系:CatalogRequestProcessor::skills_list 会调用它。它会用 CatalogRequestProcessor::load_latest_config、CatalogRequestProcessor::workspace_codex_plugins_enabled,并在末尾使用技能和错误转换函数,把核心层结果变成客户端响应。

调用图:调用 2 个内部函数(load_latest_config, workspace_codex_plugins_enabled);被 1 处调用(skills_list);外部调用 2 个(iter, vec!)。

CatalogRequestProcessor::skills_extra_roots_set_response585–601 ↗
async fn skills_extra_roots_set_response(
        &self,
        params: SkillsExtraRootsSetParams,
    ) -> Result<SkillsExtraRootsSetResponse, JSONRPCErrorError>

作用:设置运行时额外技能目录,并通知客户端技能列表已经变化。

数据流:输入是 extra_roots。函数先让 skills_watcher 记录这些运行时额外目录,再让线程管理器里的技能管理器使用这些目录;随后通过 outgoing 发送 SkillsChanged 服务器通知。输出是一个空的成功响应。

调用关系:CatalogRequestProcessor::skills_extra_roots_set 会调用它。它是一个会改变服务器状态的请求处理函数,最后还会主动通知客户端刷新技能相关界面。

调用图:被 1 处调用(skills_extra_roots_set);外部调用 1 个(SkillsChanged)。

CatalogRequestProcessor::hooks_list_response604–674 ↗
async fn hooks_list_response(
        &self,
        params: HooksListParams,
    ) -> Result<HooksListResponse, JSONRPCErrorError>

作用:真正生成钩子列表响应,按目录列出配置和插件共同带来的自动动作。

数据流:输入是 HooksListParams。目录为空时使用默认当前目录。函数先取认证信息和插件管理器,然后逐个目录处理:先加载该目录配置;如果失败,就返回该目录的错误项。配置成功后,判断工作区和功能开关是否允许插件;允许时加载插件钩子来源和警告,不允许时使用空插件结果。最后调用 codex_hooks::list_hooks 按配置、信任设置、插件来源等算出钩子列表,再用 hooks_to_info 转成客户端格式,连同警告一起返回。

调用关系:CatalogRequestProcessor::hooks_list 会调用它。它会调用 CatalogRequestProcessor::workspace_codex_plugins_enabled 判断插件门槛,并把最终钩子条目交给 hooks_to_info 做格式转换。

调用图:调用 3 个内部函数(load_for_cwd, workspace_codex_plugins_enabled, hooks_to_info);被 1 处调用(hooks_list);外部调用 6 个(default, new, list_hooks, default, default, vec!)。

CatalogRequestProcessor::skills_config_write_response_inner676–712 ↗
async fn skills_config_write_response_inner(
        &self,
        params: SkillsConfigWriteParams,
    ) -> Result<SkillsConfigWriteResponse, JSONRPCErrorError>

作用:真正写入技能开关配置。客户端可以按技能路径或技能名来启用、禁用某个技能,但二者必须只给一个。

数据流:输入包含 path、name 和 enabled。函数先检查参数:如果给了路径,就生成按路径设置技能的配置编辑;如果给了非空名称,就生成按名称设置技能的配置编辑;其他情况返回参数错误。然后用 ConfigEditsBuilder 把编辑写进 codex_home 下的配置。写成功后清掉插件和技能缓存,返回 effective_enabled;写失败则返回内部错误。

调用关系:CatalogRequestProcessor::skills_config_write 会调用它。它在写配置后会让插件管理器和技能管理器清缓存,这样后续列表请求不会继续看到旧状态。

调用图:调用 1 个内部函数(new);被 1 处调用(skills_config_write);外部调用 1 个(vec!)。

app-server/src/request_processors/environment_processor.rs源码 ↗
domain_logicrequest handling

这个文件像一个前台接待员,只负责一种事:客户端说“我这里有一个环境,请登记一下”。这里的“环境”可以理解成某个任务要运行的地方或配置入口,“执行服务器地址”就是实际干活的服务位置。文件里的 EnvironmentRequestProcessor 保存了一个 EnvironmentManager,也就是后面真正登记环境的组件。收到 environment_add 请求后,它会把请求参数里的 environment_id 和 exec_server_url 交给 EnvironmentManager 的 upsert_environment。upsert 的意思是“有就更新,没有就新增”。如果登记失败,它会把内部错误改成 JSON-RPC 的 invalid_request 错误;JSON-RPC 是一种用 JSON 格式发请求和收响应的协议。成功时,它返回一个空的添加成功响应,告诉客户端这件事办完了。

函数细节2
EnvironmentRequestProcessor::new9–13 ↗
fn new(environment_manager: Arc<EnvironmentManager>) -> Self

作用:创建一个环境请求处理器,并把真正保存环境信息的 EnvironmentManager 放进去。别人之后就可以用这个处理器来处理环境相关请求。

数据流:进去的是一个共享的 EnvironmentManager(Arc 是一种安全共享同一个对象的包装)→ 函数把它存进新的 EnvironmentRequestProcessor 里 → 出来的是一个可以复用的请求处理器实例,没有额外改动外部状态。

调用关系:它在上层初始化请求处理模块时被调用,也就是 called by 里的 new。它不自己处理请求,只是先把后面要用的工具准备好。

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

EnvironmentRequestProcessor::environment_add15–23 ↗
async fn environment_add(
        &self,
        params: EnvironmentAddParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理客户端发来的“添加环境”请求。它把环境编号和执行服务器地址登记到环境管理器里,成功后给客户端一个成功响应。

数据流:进去的是 EnvironmentAddParams,里面有 environment_id 和 exec_server_url,同时函数读取自己保存的 environment_manager → 它调用 upsert_environment,把这个环境新增或覆盖更新;如果出错,就把错误转换成 JSON-RPC 的 invalid_request → 出来的是成功响应 EnvironmentAddResponse,或者一个会返回给客户端的错误。

调用关系:它在客户端已经初始化之后处理请求时被 handle_initialized_client_request 调用。它自己不直接保存数据,而是把真正的登记工作交给 EnvironmentManager;做完后再把结果包装成客户端能看懂的响应。

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

app-server/src/request_processors/external_agent_config_processor.rs源码 ↗
orchestrationrequest handling 和后台导入任务

可以把这个文件想成“搬家调度员”。外部代理工具里可能有配置、插件、技能、会话记录、命令、钩子等东西,用户想迁到 Codex 里。这个文件不亲自搬所有箱子,而是接收客户端请求,调用底层的迁移服务去扫描和导入,再把结果翻译成客户端认识的格式。导入时它会先生成一个导入编号,快速回一个“我开始了”,然后持续发进度通知。普通配置导入可以马上完成;插件和会话可能比较慢,所以会放到后台继续跑。它还会在需要时刷新运行中的配置缓存,避免“文件已经改了,但程序还用旧配置”。最后,它会把成功和失败记录写进状态数据库,方便以后查看导入历史。

函数细节21
ExternalAgentConfigRequestProcessor::new74–100 ↗
fn new(args: ExternalAgentConfigRequestProcessorArgs) -> Self

作用:创建一个外部代理配置请求处理器,把它后面要用的发消息工具、迁移服务、会话导入器、配置处理器等零件装好。

数据流:输入是一包启动参数,里面有消息发送器、线程管理器、配置管理器、状态数据库、Codex 主目录等信息。它用这些信息新建会话导入器和配置迁移服务,然后把所有依赖保存到结构体里。输出是一个可以处理检测、导入、读取历史请求的处理器对象。

调用关系:它通常在上层请求处理器初始化时被调用。之后客户端来的检测、导入、历史查询请求,都会落到这个对象的方法上。它内部调用其他组件的 new,并复制共享指针,目的是让后续异步任务也能安全使用这些组件。

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

ExternalAgentConfigRequestProcessor::detect102–194 ↗
async fn detect(
        &self,
        params: ExternalAgentConfigDetectParams,
    ) -> Result<ExternalAgentConfigDetectResponse, JSONRPCErrorError>

作用:扫描外部代理工具里有哪些东西可以迁移,比如配置、插件、会话、命令等,并把扫描结果返回给客户端。

数据流:输入是检测参数,例如是否包含用户主目录、要检查哪些当前工作目录。它把这些参数交给底层迁移服务检测。拿到内部格式的迁移项后,它逐个翻译成协议格式,也就是客户端能读懂的数据结构。输出是一份可迁移项目列表;如果扫描失败,就返回 JSON-RPC 错误。

调用关系:客户端发起“检测可导入内容”请求时,上层的 handle_initialized_client_request 会调用它。它把真正的扫描工作交给 migration_service.detect,自己主要负责参数传递、错误包装,以及把内部类型翻译成网络协议类型。

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

ExternalAgentConfigRequestProcessor::import196–334 ↗
async fn import(
        &self,
        request_id: ConnectionRequestId,
        params: ExternalAgentConfigImportParams,
    ) -> Result<(), JSONRPCErrorError>

作用:执行一次外部配置导入,并持续告诉客户端导入进展。它还会处理慢任务,比如会话和插件导入,把它们放到后台继续完成。

数据流:输入是请求编号和导入参数,里面包含用户选择要迁移的项目。它先生成 import_id,判断是否需要刷新运行时配置,校验会话导入项,再调用 import_external_agent_config 导入除会话外的大部分内容。随后它向客户端发送响应和进度通知。如果还有插件或会话这种后台任务,它会启动异步任务继续导入,完成后汇总所有成功失败结果,必要时清缓存,并发送完成通知,还可能写入历史数据库。

调用关系:这是导入流程的总调度点,由 handle_initialized_client_request 在收到导入请求时调用。它会调用 validate_pending_session_imports、import_external_agent_config、send_import_progress、send_completed_import_notification 等函数;插件导入完成后会用 apply_plugin_outcome_to_item_result 整理结果;如果配置被改动,还会让 config_processor.handle_config_mutation 刷新运行状态。

调用图:调用 8 个内部函数(record_import_error, handle_config_mutation, import_external_agent_config, validate_pending_session_imports, apply_plugin_outcome_to_item_result, migration_items_need_runtime_refresh, send_completed_import_notification, send_import_progress);被 1 处调用(handle_initialized_client_request);外部调用 7 个(clone, new, new_v4, new, clone, join!, spawn)。

ExternalAgentConfigRequestProcessor::read_import_histories336–353 ↗
async fn read_import_histories(
        &self,
    ) -> Result<ExternalAgentConfigImportHistoriesReadResponse, JSONRPCErrorError>

作用:读取以前外部配置导入的历史记录,让客户端能展示“以前导入过什么、哪些成功、哪些失败”。

数据流:输入不需要额外参数,但它会读取处理器里保存的状态数据库句柄。它从数据库取出历史记录,再把数据库里的记录格式转换成协议格式。输出是一组历史记录;如果状态数据库不可用或读取失败,就返回内部错误。

调用关系:客户端请求查看导入历史时,由 handle_initialized_client_request 调用它。它依赖 state_db 保存过的记录,并使用 protocol_import_history 把数据库记录转换成客户端可用的响应数据。

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

ExternalAgentConfigRequestProcessor::validate_pending_session_imports355–419 ↗
fn validate_pending_session_imports(
        &self,
        params: &ExternalAgentConfigImportParams,
    ) -> (Vec<CoreSessionMigration>, Option<CoreImportItemResult>)

作用:在真正导入会话前先检查用户选择的会话是否还存在,并去掉重复项,避免后台导入时踩空或重复搬同一个会话。

数据流:输入是导入参数。它从迁移项里挑出 Sessions 类型,把里面的会话路径、工作目录、标题取出来。然后逐个问迁移服务:这个会话源文件是否能找到真实路径。找不到或出错就把失败原因记进结果;找到且没重复,就加入待导入列表。输出是待导入会话列表,以及一份“会话校验”的结果记录。

调用关系:它只在 import 的前半段使用。import 先靠它筛出靠谱的会话,马上把校验结果作为进度发给客户端;真正导入会话的工作稍后交给 ExternalAgentSessionImporter 在后台完成。

调用图:调用 2 个内部函数(external_agent_session_source_path, record_import_error);被 1 处调用(import);外部调用 4 个(new, new, new, format!)。

ExternalAgentConfigRequestProcessor::import_external_agent_config421–520 ↗
async fn import_external_agent_config(
        &self,
        params: ExternalAgentConfigImportParams,
    ) -> Result<CoreImportOutcome, JSONRPCErrorError>

作用:把客户端提交的迁移项目转换成底层迁移服务认识的格式,并执行配置类导入;会话项会被排除,因为会话有单独的后台导入流程。

数据流:输入是客户端协议里的导入参数。它过滤掉 Sessions 类型,把剩下每个迁移项的类型、描述、目录和详细信息都翻译成核心迁移服务的内部格式。然后调用 migration_service.import。输出是核心导入结果,里面包含已完成项目的成功失败信息,以及可能还要后台处理的插件导入任务;失败时输出 JSON-RPC 内部错误。

调用关系:它由 import 调用,是主导入流程里真正把配置、技能、插件清单、服务器配置等交给核心服务的桥梁。它不发送消息,也不写历史,只负责格式转换和调用底层 import。

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

ExternalAgentConfigRequestProcessor::complete_pending_plugin_import522–533 ↗
async fn complete_pending_plugin_import(
        &self,
        pending_plugin_import: PendingPluginImport,
    ) -> Result<PluginImportOutcome, JSONRPCErrorError>

作用:完成一个还没真正装完的插件导入任务。插件可能需要额外步骤,所以主导入结束后会单独补做。

数据流:输入是一个待处理插件导入任务,里面有工作目录和插件详情。它把这些信息交给 migration_service.import_plugins。输出是插件导入结果,包括哪些插件成功、哪些产生错误;如果失败,就把错误包装成 JSON-RPC 错误。

调用关系:它在 import 启动的后台任务里被调用。每个 PendingPluginImport 都会经过它处理,结果再交给 apply_plugin_outcome_to_item_result 写进本次导入的项目结果里。

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

send_import_progress536–549 ↗
async fn send_import_progress(
    outgoing: &OutgoingMessageSender,
    import_id: &str,
    item_result: &CoreImportItemResult,
)

作用:给客户端发送一次“导入进度更新”,告诉它某一类项目现在成功了哪些、失败了哪些。

数据流:输入是消息发送器、导入编号和一个内部项目结果。它先把内部结果转换成协议里的 item_type_results,再包成 ExternalAgentConfigImportProgress 通知。输出不是普通返回值,而是通过 outgoing 向客户端发出服务器通知。

调用关系:import 在校验会话、完成普通导入项、完成后台会话或插件导入后都会调用它。它依赖 protocol_import_type_result 做格式转换,再调用 send_server_notification 把消息发出去。

调用图:调用 1 个内部函数(send_server_notification);被 1 处调用(import);外部调用 2 个(ExternalAgentConfigImportProgress, vec!)。

send_completed_import_notification551–572 ↗
async fn send_completed_import_notification(
    outgoing: &OutgoingMessageSender,
    state_db: Option<&StateDbHandle>,
    import_id: String,
    item_results: &[CoreImportItemResult],
)

作用:发送“这次导入全部结束”的通知,并尽量把这次导入的结果写进历史数据库。

数据流:输入是消息发送器、可选的状态数据库、导入编号和所有项目结果。它先调用 completed_notification 汇总成最终通知;如果有状态数据库,就调用 record_completed_import_notification 写历史。写历史失败不会让导入失败,只会打警告日志。最后它把完成通知发给客户端。

调用关系:import 在没有后台任务时会直接调用它;有后台任务时,会在所有后台会话和插件导入结束后调用它。它位于整个导入流程的收尾位置,负责“存档”和“宣布完成”。

调用图:调用 3 个内部函数(send_server_notification, completed_notification, record_completed_import_notification);被 1 处调用(import);外部调用 2 个(ExternalAgentConfigImportCompleted, warn!)。

record_completed_import_notification574–613 ↗
async fn record_completed_import_notification(
    state_db: &StateDbHandle,
    notification: &ExternalAgentConfigImportCompletedNotification,
) -> anyhow::Result<()>

作用:把一次导入完成通知里的成功和失败明细保存到状态数据库里,方便以后查历史。

数据流:输入是状态数据库句柄和完成通知。它从通知里拆出所有成功记录和失败记录,把协议里的字段转成数据库记录结构。然后调用数据库的 record_external_agent_config_import_completed 保存导入编号、成功列表和失败列表。输出是成功或失败的结果;失败会被上层记录成警告。

调用关系:它只由 send_completed_import_notification 调用。它不负责发消息,只负责把已经整理好的完成通知落库,让 read_import_histories 以后可以读出来。

调用图:被 1 处调用(send_completed_import_notification);外部调用 1 个(record_external_agent_config_import_completed)。

protocol_import_history615–635 ↗
fn protocol_import_history(
    record: codex_state::ExternalAgentConfigImportHistoryRecord,
) -> Result<ExternalAgentConfigImportHistory, JSONRPCErrorError>

作用:把数据库里的导入历史记录转换成客户端协议里的历史记录格式。

数据流:输入是一条数据库历史记录,里面有导入编号、完成时间、成功列表和失败列表。它分别转换成功记录和失败记录,再组合成 ExternalAgentConfigImportHistory。输出是客户端可返回的历史对象;如果某个项目类型无法解析,就返回错误。

调用关系:read_import_histories 读取数据库后会对每条记录使用它。它进一步依赖 protocol_import_success_record 和 protocol_import_failure_record 转换明细。

protocol_import_success_record637–646 ↗
fn protocol_import_success_record(
    record: ExternalAgentConfigImportSuccessRecord,
) -> Result<ProtocolImportSuccess, JSONRPCErrorError>

作用:把一条数据库里的成功记录转换成客户端能理解的成功记录。

数据流:输入是数据库保存的成功记录,包括项目类型字符串、工作目录、来源和目标。它先把项目类型字符串解析成协议枚举,再原样带上目录、来源、目标。输出是 ProtocolImportSuccess;如果类型字符串不合法,就返回错误。

调用关系:它被 protocol_import_history 用来转换历史里的成功明细。项目类型解析工作交给 protocol_import_record_item_type。

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

protocol_import_failure_record648–659 ↗
fn protocol_import_failure_record(
    record: ExternalAgentConfigImportFailureRecord,
) -> Result<ProtocolImportFailure, JSONRPCErrorError>

作用:把一条数据库里的失败记录转换成客户端能理解的失败记录。

数据流:输入是数据库保存的失败记录,包括项目类型、错误类型、失败阶段、错误消息、工作目录和来源。它解析项目类型,并保留其他失败说明字段。输出是 ProtocolImportFailure;如果项目类型解析失败,就返回错误。

调用关系:它被 protocol_import_history 用来转换历史里的失败明细。和成功记录一样,它把项目类型解析交给 protocol_import_record_item_type。

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

protocol_import_record_item_type661–669 ↗
fn protocol_import_record_item_type(
    item_type: String,
) -> Result<ExternalAgentConfigMigrationItemType, JSONRPCErrorError>

作用:把数据库里保存的项目类型字符串,重新变成协议里的项目类型枚举。枚举可以理解成一组固定选项,比如 Config、Plugins、Sessions。

数据流:输入是一个字符串形式的项目类型。它用 JSON 反序列化方式尝试把字符串转成 ExternalAgentConfigMigrationItemType。输出是项目类型枚举;如果字符串不是合法选项,就返回内部错误,并说明哪个类型解码失败。

调用关系:protocol_import_success_record 和 protocol_import_failure_record 都会调用它。它是历史记录从数据库格式回到协议格式时的“小翻译器”。

调用图:被 2 处调用(protocol_import_failure_record, protocol_import_success_record);外部调用 2 个(String, from_value)。

completed_notification671–718 ↗
fn completed_notification(
    import_id: String,
    item_results: &[CoreImportItemResult],
) -> ExternalAgentConfigImportCompletedNotification

作用:把多个零散的导入结果合并成一条最终完成通知,按项目类型归类,并按固定顺序排列。

数据流:输入是导入编号和一组内部项目结果。它逐个取出成功和失败记录,转换成协议格式;如果同一种项目类型出现多次,就把它们合并到同一个结果组里。最后按 Config、Skills、AgentsMd、Plugins 等固定顺序排序。输出是一条 ExternalAgentConfigImportCompletedNotification。

调用关系:send_completed_import_notification 在发完成通知和写历史前会调用它。它内部使用 protocol_migration_item_type、protocol_import_success、protocol_import_raw_error 来完成格式转换。

调用图:调用 1 个内部函数(protocol_migration_item_type);被 1 处调用(send_completed_import_notification);外部调用 1 个(new)。

protocol_import_type_result720–734 ↗
fn protocol_import_type_result(item_result: &CoreImportItemResult) -> ProtocolImportTypeResult

作用:把一个内部导入项目结果转换成一次进度通知里使用的协议结果。

数据流:输入是 CoreImportItemResult,里面有项目类型、成功列表和原始错误列表。它把项目类型转换成协议项目类型,把成功和失败分别转换成协议记录。输出是 ProtocolImportTypeResult。

调用关系:send_import_progress 调用它来生成进度通知。它负责单个项目结果的转换,而 completed_notification 负责多个项目结果的最终汇总。

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

protocol_import_success736–745 ↗
fn protocol_import_success(
    success: &crate::config::external_agent_config::ExternalAgentConfigImportSuccess,
) -> ProtocolImportSuccess

作用:把内部的成功导入记录转换成客户端协议里的成功记录。

数据流:输入是一条内部成功记录,包括项目类型、工作目录、来源和目标。它转换项目类型,并复制其他展示用字段。输出是 ProtocolImportSuccess。

调用关系:protocol_import_type_result 和 completed_notification 都会间接使用它。它和 protocol_import_raw_error 是一对:一个处理成功,一个处理失败。

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

protocol_import_raw_error747–756 ↗
fn protocol_import_raw_error(raw_error: &CoreImportRawError) -> ProtocolImportFailure

作用:把内部的导入错误记录转换成客户端协议里的失败记录,让前端能展示失败原因。

数据流:输入是一条内部原始错误,包含项目类型、错误类型、失败阶段、消息、目录和来源。它转换项目类型,并保留错误说明。输出是 ProtocolImportFailure。

调用关系:protocol_import_type_result 和 completed_notification 都会用它处理失败项。它依赖 protocol_migration_item_type 做项目类型转换。

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

protocol_migration_item_type758–774 ↗
fn protocol_migration_item_type(
    item_type: CoreMigrationItemType,
) -> ExternalAgentConfigMigrationItemType

作用:把核心迁移服务使用的项目类型转换成客户端协议使用的项目类型。

数据流:输入是 CoreMigrationItemType,比如 Config、Plugins、Sessions。它用一一对应的 match 映射成 ExternalAgentConfigMigrationItemType。输出是协议项目类型。

调用关系:它是很多结果转换函数的公共小工具,被 completed_notification、protocol_import_type_result、protocol_import_success、protocol_import_raw_error 调用,保证内部类型和客户端类型始终对应。

调用图:被 4 处调用(completed_notification, protocol_import_raw_error, protocol_import_success, protocol_import_type_result)。

apply_plugin_outcome_to_item_result776–786 ↗
fn apply_plugin_outcome_to_item_result(
    item_result: &mut CoreImportItemResult,
    plugin_outcome: PluginImportOutcome,
)

作用:把插件导入的结果写进某个导入项目结果里,成功的记成功,失败的记错误。

数据流:输入是一个可修改的项目结果,以及插件导入结果。它遍历成功插件 ID,把每个插件记成一次成功;再遍历插件导入产生的原始错误,把它们记入失败列表。输出没有新对象,但会修改传入的 item_result。

调用关系:import 的后台插件导入流程会调用它。complete_pending_plugin_import 先拿到插件结果,然后它把结果整理进统一的 CoreImportItemResult,之后再由 send_import_progress 和完成通知发给客户端。

调用图:被 1 处调用(import);外部调用 2 个(record_error, record_success)。

migration_items_need_runtime_refresh788–800 ↗
fn migration_items_need_runtime_refresh(items: &[ExternalAgentConfigMigrationItem]) -> bool

作用:判断这次导入是否会影响正在运行的配置缓存。如果会,就需要刷新,避免程序继续使用旧配置。

数据流:输入是用户选择的迁移项目列表。它检查里面是否包含配置、技能、MCP 服务器配置、钩子、命令或插件这几类会影响运行状态的东西。输出是布尔值:true 表示需要刷新,false 表示不需要。

调用关系:import 一开始会调用它。若结果为 true,导入完成主要配置后会调用 config_processor.handle_config_mutation,让运行中的系统重新感知配置变化。

调用图:被 1 处调用(import);外部调用 1 个(iter)。

app-server/src/request_processors/mcp_processor.rs源码 ↗
orchestrationrequest handling

这个文件里的 McpRequestProcessor 像一个“前台分诊员”。客户端通过 JSON-RPC(一种用 JSON 发请求和收回复的通信格式)发来 MCP 请求后,它先判断要办哪件事:OAuth 登录、刷新服务器、列状态、读资源,还是调用工具。它不会把所有重活都堵在当前请求里,而是常常把耗时操作丢给后台任务,然后通过 outgoing 把结果再发回去,这样主请求通道不会卡住。它还会在需要时重新读取最新配置,检查线程 ID 是否有效,拿到当前登录信息,并根据有没有指定线程走不同路径。一个重要细节是:调用 MCP 工具时,它会把 threadId 塞进元数据里,方便后面的工具知道这次调用属于哪个会话线程。

函数细节17
McpRequestProcessor::new14–26 ↗
fn new(
        auth_manager: Arc<AuthManager>,
        thread_manager: Arc<ThreadManager>,
        outgoing: Arc<OutgoingMessageSender>,
        config_manager: ConfigManager,
    ) -> Self

作用:创建一个 MCP 请求处理器,把它以后办事需要用到的几个“助手”保存起来,比如认证、线程、发消息和配置。

数据流:进去的是认证管理器、线程管理器、消息发送器和配置管理器 → 函数把它们装进 McpRequestProcessor 这个结构里 → 出来的是一个可以处理 MCP 请求的新对象,不额外做网络或磁盘操作。

调用关系:它在上层创建请求处理器时被调用。后面的登录、刷新、读资源、调工具等方法,都会依赖这里保存下来的这些组件。

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

McpRequestProcessor::mcp_server_oauth_login28–35 ↗
async fn mcp_server_oauth_login(
        &self,
        params: McpServerOauthLoginParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“给某个 MCP 服务器做 OAuth 登录”的客户端请求。OAuth 可以理解成跳转到网页授权,让这个应用拿到访问某个服务的许可。

数据流:进去的是登录参数,比如服务器名字、权限范围和超时时间 → 它把真正的工作交给 mcp_server_oauth_login_response → 拿到登录响应后包装成客户端能收的响应格式返回。

调用关系:它由 handle_initialized_client_request 在收到对应客户端请求时调用。它本身是外层入口,里面转给 mcp_server_oauth_login_response 做实际登录准备。

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

McpRequestProcessor::mcp_server_refresh37–44 ↗
async fn mcp_server_refresh(
        &self,
        params: Option<()>,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“刷新 MCP 服务器信息”的请求,让系统重新检查配置里的 MCP 服务。

数据流:进去的是一个可选但实际不用的参数 → 它调用 mcp_server_refresh_response 去排队执行刷新 → 成功后把空的刷新响应包装给客户端。

调用关系:它由 handle_initialized_client_request 调用,是刷新请求的门面;真正排队刷新由 mcp_server_refresh_response 完成。

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

McpRequestProcessor::mcp_server_status_list46–54 ↗
async fn mcp_server_status_list(
        &self,
        request_id: &ConnectionRequestId,
        params: ListMcpServerStatusParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“列出 MCP 服务器状态”的请求,比如有哪些服务器、工具、资源、认证状态等。

数据流:进去的是请求 ID 和查询参数 → 它把任务交给 list_mcp_server_status → 这个操作之后会异步发结果,所以这里正常返回时没有直接带数据。

调用关系:它由 handle_initialized_client_request 调用。它只是接住客户端请求,随后让 list_mcp_server_status 安排后台任务去收集状态并回传。

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

McpRequestProcessor::mcp_resource_read56–64 ↗
async fn mcp_resource_read(
        &self,
        request_id: &ConnectionRequestId,
        params: McpResourceReadParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“读取 MCP 资源”的请求。资源可以理解成 MCP 服务器暴露出来的一份内容,比如文件、文档或某个服务数据。

数据流:进去的是请求 ID、服务器名、资源 URI,以及可选线程 ID → 它调用 read_mcp_resource 安排读取 → 结果会稍后通过消息发送器返回,所以这里不直接返回资源内容。

调用关系:它由 handle_initialized_client_request 在客户端要求读资源时调用。真正区分“有线程”和“无线程”两种读取方式的是 read_mcp_resource。

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

McpRequestProcessor::mcp_server_tool_call66–74 ↗
async fn mcp_server_tool_call(
        &self,
        request_id: &ConnectionRequestId,
        params: McpServerToolCallParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“调用某个 MCP 工具”的请求。工具可以理解成外部服务提供的一个可执行动作,比如搜索、查询或生成内容。

数据流:进去的是请求 ID、线程 ID、服务器名、工具名、参数和元数据 → 它把实际调用交给 call_mcp_server_tool → 调用结果稍后异步发回客户端。

调用关系:它由 handle_initialized_client_request 调用,是客户端工具调用请求的外层入口;后续由 call_mcp_server_tool 找到线程并执行工具。

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

McpRequestProcessor::mcp_server_refresh_response76–84 ↗
async fn mcp_server_refresh_response(
        &self,
        _params: Option<()>,
    ) -> Result<McpServerRefreshResponse, JSONRPCErrorError>

作用:真正安排 MCP 服务器刷新。它的重点不是立刻刷新完,而是把严格刷新任务放进队列。

数据流:进去的是一个未使用的参数 → 它调用 queue_strict_refresh,用线程管理器和配置管理器安排刷新 → 成功时返回一个空的刷新响应,失败时变成 JSON-RPC 错误。

调用关系:它被 mcp_server_refresh 调用。它把刷新这件事交给 crate::mcp_refresh::queue_strict_refresh,自己负责把底层错误翻译成客户端能理解的错误。

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

McpRequestProcessor::load_latest_config86–94 ↗
async fn load_latest_config(
        &self,
        fallback_cwd: Option<PathBuf>,
    ) -> Result<Config, JSONRPCErrorError>

作用:读取最新配置,并把读取失败统一变成客户端请求能收到的错误。

数据流:进去的是一个可选的备用工作目录 → 它让 config_manager 加载最新配置 → 出来的是 Config;如果失败,就返回“重新加载配置失败”的内部错误。

调用关系:它被 mcp_server_oauth_login_response、list_mcp_server_status 和 read_mcp_resource 使用。凡是 MCP 操作需要知道最新服务器配置时,都会先经过它。

调用图:调用 1 个内部函数(load_latest_config);被 3 处调用(list_mcp_server_status, mcp_server_oauth_login_response, read_mcp_resource)。

McpRequestProcessor::load_thread96–110 ↗
async fn load_thread(
        &self,
        thread_id: &str,
    ) -> Result<(ThreadId, Arc<CodexThread>), JSONRPCErrorError>

作用:根据字符串形式的线程 ID 找到真正的会话线程。这里的线程不是操作系统线程,更像一次对话或任务的上下文。

数据流:进去的是 thread_id 字符串 → 它先用 from_string 检查并转换成内部 ThreadId,再向 thread_manager 查找对应线程 → 出来的是规范化后的 ThreadId 和线程对象;ID 不合法或找不到都会变成无效请求错误。

调用关系:它被 list_mcp_server_status、read_mcp_resource 和 call_mcp_server_tool 调用。凡是请求明确绑定某个会话线程时,都要先通过它确认这个线程真的存在。

调用图:调用 1 个内部函数(from_string);被 3 处调用(call_mcp_server_tool, list_mcp_server_status, read_mcp_resource)。

McpRequestProcessor::mcp_server_oauth_login_response112–197 ↗
async fn mcp_server_oauth_login_response(
        &self,
        params: McpServerOauthLoginParams,
    ) -> Result<McpServerOauthLoginResponse, JSONRPCErrorError>

作用:准备并启动某个 MCP 服务器的 OAuth 登录流程,然后把用户需要打开的授权网址返回给客户端。

数据流:进去的是服务器名、权限范围和超时时间 → 它加载最新配置,找到对应 MCP 服务器,确认它是支持 OAuth 的 HTTP 类型服务器,算出要申请的权限范围,然后启动登录流程 → 出来的是 authorization_url;同时它开一个后台任务等待登录完成,并发送“登录完成或失败”的通知。

调用关系:它被 mcp_server_oauth_login 调用。它会用 load_latest_config 取配置,还会在后台用 tokio::spawn 等待授权结果,最后通过 outgoing 发送 McpServerOauthLoginCompleted 通知。

调用图:调用 1 个内部函数(load_latest_config);被 1 处调用(mcp_server_oauth_login);外部调用 4 个(clone, McpServerOauthLoginCompleted, format!, spawn)。

McpRequestProcessor::list_mcp_server_status199–249 ↗
async fn list_mcp_server_status(
        &self,
        request_id: &ConnectionRequestId,
        params: ListMcpServerStatusParams,
    ) -> Result<(), JSONRPCErrorError>

作用:安排一次 MCP 服务器状态查询,并尽快把当前请求放行,避免客户端连接被长时间占住。

数据流:进去的是请求 ID 和状态查询参数 → 如果参数里有线程 ID,就先 load_thread 并按该线程的配置加载最新配置;否则加载全局最新配置;然后生成运行时 MCP 配置、认证信息和运行环境上下文 → 它启动后台任务 list_mcp_server_status_task,自己返回成功但不直接带状态数据。

调用关系:它被 mcp_server_status_list 调用。它负责把状态查询所需的配置、认证和环境准备好,再交给 list_mcp_server_status_task 异步执行并发送结果。

调用图:调用 4 个内部函数(load_latest_config_for_thread, load_latest_config, load_thread, new);被 1 处调用(mcp_server_status_list);外部调用 4 个(clone, list_mcp_server_status_task, clone, spawn)。

McpRequestProcessor::list_mcp_server_status_task251–268 ↗
async fn list_mcp_server_status_task(
        outgoing: Arc<OutgoingMessageSender>,
        request_id: ConnectionRequestId,
        params: ListMcpServerStatusParams,
        mcp_config: codex_mcp::M

作用:在后台真正执行状态查询,并把结果按原请求 ID 发回客户端。

数据流:进去的是消息发送器、请求 ID、查询参数、MCP 配置、认证信息和运行环境 → 它调用 list_mcp_server_status_response 生成状态响应 → 最后用 outgoing.send_result 把成功结果或错误发回去。

调用关系:它由 list_mcp_server_status 启动为后台任务。它是“后台工人”,上接准备好的查询材料,下接 list_mcp_server_status_response 生成具体内容。

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

McpRequestProcessor::list_mcp_server_status_response270–351 ↗
async fn list_mcp_server_status_response(
        request_id: String,
        params: ListMcpServerStatusParams,
        mcp_config: codex_mcp::McpConfig,
        auth: Option<CodexAuth>,
        runt

作用:把 MCP 服务器的原始状态快照整理成客户端要看的分页列表。

数据流:进去的是请求 ID、查询参数、MCP 配置、认证信息和运行环境 → 它按请求的详细程度收集状态快照,把服务器信息、工具、资源、资源模板和认证状态合并到同一份名单里,再按 cursor 和 limit 做分页 → 出来的是 ListMcpServerStatusResponse,包含本页数据和下一页游标;游标不合法会返回无效请求错误。

调用关系:它被 list_mcp_server_status_task 调用。它是状态查询里最核心的整理步骤,把底层收集到的多张“表”拼成客户端容易消费的一页结果。

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

McpRequestProcessor::read_mcp_resource353–404 ↗
async fn read_mcp_resource(
        &self,
        request_id: &ConnectionRequestId,
        params: McpResourceReadParams,
    ) -> Result<(), JSONRPCErrorError>

作用:安排读取某个 MCP 资源,并根据请求是否带线程 ID 选择合适的读取方式。

数据流:进去的是请求 ID、可选线程 ID、服务器名和资源 URI → 如果有线程 ID,它先 load_thread,然后让该线程读取资源;如果没有线程 ID,它加载全局配置,构造 MCP 运行配置和环境上下文,再无线程读取资源 → 两种路径都会启动后台任务,最后通过 send_mcp_resource_read_response 发回结果。

调用关系:它被 mcp_resource_read 调用。它会用 load_thread 或 load_latest_config 准备读取条件,并把最终回包交给 send_mcp_resource_read_response。

调用图:调用 3 个内部函数(load_latest_config, load_thread, new);被 1 处调用(mcp_resource_read);外部调用 4 个(clone, send_mcp_resource_read_response, clone, spawn)。

McpRequestProcessor::send_mcp_resource_read_response406–421 ↗
async fn send_mcp_resource_read_response(
        outgoing: Arc<OutgoingMessageSender>,
        request_id: ConnectionRequestId,
        result: anyhow::Result<serde_json::Value>,
    )

作用:把读取 MCP 资源得到的原始结果整理成客户端响应,并发送出去。

数据流:进去的是消息发送器、请求 ID 和一个可能成功也可能失败的 JSON 值结果 → 它先把底层错误转换成 JSON-RPC 错误,再把 JSON 值反序列化成 McpResourceReadResponse → 最后用 outgoing.send_result 发给对应请求。

调用关系:它被 read_mcp_resource 启动的后台读取流程使用。它是资源读取链路的最后一步,专门负责把“读到的东西或错误”变成正式回包。

McpRequestProcessor::call_mcp_server_tool423–443 ↗
async fn call_mcp_server_tool(
        &self,
        request_id: &ConnectionRequestId,
        params: McpServerToolCallParams,
    ) -> Result<(), JSONRPCErrorError>

作用:在指定会话线程里调用某个 MCP 工具,并把结果异步发回客户端。

数据流:进去的是请求 ID、线程 ID、服务器名、工具名、工具参数和元数据 → 它先 load_thread 确认线程存在,再用 with_mcp_tool_call_thread_id_meta 把线程 ID 写进元数据,然后启动后台任务调用 thread.call_mcp_tool → 出来时当前函数只表示任务已安排;真正结果会通过 outgoing.send_result 发回。

调用关系:它被 mcp_server_tool_call 调用。它依赖 load_thread 找到会话上下文,依赖 with_mcp_tool_call_thread_id_meta 补充元数据,然后把实际工具执行交给线程对象。

调用图:调用 2 个内部函数(load_thread, with_mcp_tool_call_thread_id_meta);被 1 处调用(mcp_server_tool_call);外部调用 3 个(clone, clone, spawn)。

with_mcp_tool_call_thread_id_meta446–468 ↗
fn with_mcp_tool_call_thread_id_meta(
    meta: Option<serde_json::Value>,
    thread_id: &str,
) -> Option<serde_json::Value>

作用:给 MCP 工具调用的元数据补上 threadId,方便下游知道这次工具调用属于哪个会话线程。

数据流:进去的是可选的 JSON 元数据和线程 ID 字符串 → 如果元数据本来是一个 JSON 对象,它就在里面加上 threadId;如果没有元数据,它新建一个只含 threadId 的对象;如果元数据是数组、字符串等非对象,就保持原样 → 出来的是补充后的可选 JSON 元数据。

调用关系:它被 call_mcp_server_tool 调用。它不负责执行工具,只是在调用前给请求贴一张“来自哪个线程”的标签。

调用图:被 1 处调用(call_mcp_server_tool);外部调用 3 个(new, Object, String)。

app-server/src/request_processors/turn_processor.rs源码 ↗
orchestrationrequest handling

可以把这个文件想成餐厅前台:客人说“我要点菜”“我要加菜”“这桌先停一下”“开个语音聊天”“帮我做代码审查”,前台先检查桌号对不对、请求是否合法、输入会不会太长、当前桌子能不能接这种请求,然后把整理好的单子交给后厨。这里的“线程”不是普通人说的聊天线索,而是一段 Codex 会话;“回合”就是一次用户输入到模型回应的过程。文件里的 TurnRequestProcessor 保存了很多外部零件,比如线程管理器、认证、配置、消息发送器、统计上报器。它不会自己生成模型答案,而是把请求翻译成核心层能懂的 Op(操作命令),并负责在出错时返回清楚的 JSON-RPC 错误(JSON-RPC 是一种用 JSON 传请求和回应的通信格式)。它还处理一些特殊场景:多智能体子线程不允许直接输入、实时语音会话要先挂监听器、审查可以在原线程里跑也可以另开一个线程跑。

函数细节45
map_additional_context27–48 ↗
fn map_additional_context(
    additional_context: Option<HashMap<String, AdditionalContextEntry>>,
) -> BTreeMap<String, CoreAdditionalContextEntry>

作用:把客户端附带的额外上下文,转换成核心对话引擎能识别的格式。这样用户输入之外的补充信息不会丢,也能标明它是可信的应用信息还是不可信来源。

数据流:进去的是一个可选的键值表,里面每项有内容和值的来源类型;函数把空值当成空表,把每个条目逐个改成核心协议里的条目类型;出来的是按 key 排好序的 BTreeMap,不改动外部状态。

调用关系:开始新回合和引导当前回合时都会调用它,也就是 turn_start_inner 和 turn_steer_inner 会先把客户端上下文翻译好,再交给核心线程。

调用图:被 2 处调用(turn_start_inner, turn_steer_inner)。

TurnRequestProcessor::new68–96 ↗
fn new(
        auth_manager: Arc<AuthManager>,
        thread_manager: Arc<ThreadManager>,
        outgoing: Arc<OutgoingMessageSender>,
        analytics_events_client: AnalyticsEventsClient,

作用:创建一个 TurnRequestProcessor,把它需要用到的所有外部工具一次性装进去。之后每个回合请求都靠这个对象来协调。

数据流:进去的是认证、线程管理、消息发送、配置、监听、技能监控等共享对象;函数只是把这些对象保存到结构体字段里;出来的是一个可被复用的请求处理器。

调用关系:它在上层创建处理器时被调用,相当于把前台需要的电话、登记本、规则手册都摆好,后面的请求入口函数才能使用这些资源。

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

TurnRequestProcessor::turn_start98–113 ↗
async fn turn_start(
        &self,
        request_id: ConnectionRequestId,
        params: TurnStartParams,
        app_server_client_name: Option<String>,
        app_server_client_version: Option<

作用:处理客户端发来的“开始一个新回合”请求。它是公开入口,真正的检查和提交工作交给 turn_start_inner。

数据流:进去的是请求编号、开始回合参数、客户端名称和版本;它调用内部函数拿到 TurnStartResponse,再包装成通用的客户端响应;出来的是可发回客户端的响应或 JSON-RPC 错误。

调用关系:handle_initialized_client_request 收到 turn/start 后会调用它;它自己不做复杂活,只把请求转给 turn_start_inner,像门口接单后交给后台处理。

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

TurnRequestProcessor::thread_inject_items115–122 ↗
async fn thread_inject_items(
        &self,
        params: ThreadInjectItemsParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“往线程里塞入响应项”的请求。这个入口主要用于把外部准备好的响应内容注入到某个会话线程中。

数据流:进去的是线程 ID 和要注入的项目;它交给 thread_inject_items_response_inner 校验并写入;出来的是空成功响应或错误。

调用关系:handle_initialized_client_request 收到对应请求时调用它;它只是统一响应包装,实际解析和注入由内部函数完成。

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

TurnRequestProcessor::thread_settings_update124–132 ↗
async fn thread_settings_update(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadSettingsUpdateParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理客户端修改线程设置的请求,比如模型、权限、沙盒策略、协作模式等。它让设置变更走统一校验流程,避免把非法配置送进核心线程。

数据流:进去的是请求编号和设置更新参数;它调用 thread_settings_update_inner 生成并提交设置操作;出来的是通用客户端响应或错误。

调用关系:handle_initialized_client_request 收到 thread/settings/update 时调用它;它把细节交给内部函数,自己负责把内部响应转成外层协议响应。

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

TurnRequestProcessor::turn_steer134–142 ↗
async fn turn_steer(
        &self,
        request_id: &ConnectionRequestId,
        params: TurnSteerParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“引导正在进行的回合”的请求。比如模型正在工作时,用户追加一句指示,这个入口会把追加内容送到当前活动回合。

数据流:进去的是请求编号和引导参数;它调用 turn_steer_inner 做线程检查、输入校验和提交;出来的是包含回合 ID 的响应或错误。

调用关系:handle_initialized_client_request 收到 turn/steer 时调用它;真正判断当前回合能不能被引导的是 turn_steer_inner。

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

TurnRequestProcessor::turn_interrupt144–152 ↗
async fn turn_interrupt(
        &self,
        request_id: &ConnectionRequestId,
        params: TurnInterruptParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“打断当前回合”或“取消启动中任务”的请求。它让客户端可以停止正在运行的模型工作。

数据流:进去的是请求编号和要打断的线程、回合信息;它调用 turn_interrupt_inner;出来可能是立即确认,也可能等后续回合中止事件再回应,或者返回错误。

调用关系:handle_initialized_client_request 收到打断请求时调用它;它把复杂的状态判断和核心中断提交交给 turn_interrupt_inner。

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

TurnRequestProcessor::thread_realtime_start154–162 ↗
async fn thread_realtime_start(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadRealtimeStartParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理开启实时对话的请求。实时对话可以理解为更像语音通话或流式交互的一种会话模式。

数据流:进去的是请求编号和实时会话启动参数;它调用 thread_realtime_start_inner;出来是启动成功响应、无响应或错误。

调用关系:handle_initialized_client_request 收到实时启动请求时调用它;内部函数会先确保线程能被监听,再向核心提交启动实时会话的命令。

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

TurnRequestProcessor::thread_realtime_append_audio164–172 ↗
async fn thread_realtime_append_audio(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadRealtimeAppendAudioParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCE

作用:处理向实时对话追加一段音频的请求。它把客户端传来的音频帧送进正在进行的实时会话。

数据流:进去的是请求编号、线程 ID 和音频数据;它调用 thread_realtime_append_audio_inner;出来是追加成功响应、无响应或错误。

调用关系:handle_initialized_client_request 收到音频追加请求时调用它;内部函数负责确认实时会话可用并提交音频操作。

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

TurnRequestProcessor::thread_realtime_append_text174–182 ↗
async fn thread_realtime_append_text(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadRealtimeAppendTextParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErr

作用:处理向实时对话追加文字的请求。它让实时会话不只收声音,也能接收文本消息。

数据流:进去的是请求编号、线程 ID、文本和角色信息;它交给 thread_realtime_append_text_inner;出来是成功响应、无响应或错误。

调用关系:handle_initialized_client_request 收到实时文本追加请求时调用它;后续由内部函数把文字包装成核心操作。

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

TurnRequestProcessor::thread_realtime_append_speech184–192 ↗
async fn thread_realtime_append_speech(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadRealtimeAppendSpeechParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRP

作用:处理向实时对话追加“要说出来的文字”的请求。它通常用于让系统把一段文本作为语音内容继续送入实时流程。

数据流:进去的是请求编号、线程 ID 和文本;它调用 thread_realtime_append_speech_inner;出来是成功响应、无响应或错误。

调用关系:handle_initialized_client_request 收到实时 speech 追加请求时调用它;内部函数会把文本变成核心层的实时语音操作。

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

TurnRequestProcessor::thread_realtime_stop194–202 ↗
async fn thread_realtime_stop(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadRealtimeStopParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理停止实时对话的请求。它把“结束通话”的命令发给核心线程。

数据流:进去的是请求编号和线程 ID;它调用 thread_realtime_stop_inner;出来是停止成功响应、无响应或错误。

调用关系:handle_initialized_client_request 收到实时停止请求时调用它;内部函数负责准备线程并提交关闭操作。

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

TurnRequestProcessor::thread_realtime_list_voices204–213 ↗
async fn thread_realtime_list_voices(
        &self,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:返回系统内置的实时语音声音列表。客户端可以用它知道有哪些 voice(声音)可选。

数据流:没有复杂输入;它读取内置声音清单 RealtimeVoicesList::builtin;出来的是包含 voices 的客户端响应。

调用关系:handle_initialized_client_request 收到列出声音请求时直接调用它;它不碰线程,也不提交核心操作,只是回一个固定能力列表。

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

TurnRequestProcessor::review_start215–223 ↗
async fn review_start(
        &self,
        request_id: &ConnectionRequestId,
        params: ReviewStartParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理开始代码审查的请求。它是审查功能的外层入口,内部会决定是在当前线程审查还是另开一个审查线程。

数据流:进去的是请求编号和审查参数;它调用 review_start_inner,成功时返回空响应;出错时返回 JSON-RPC 错误。

调用关系:handle_initialized_client_request 收到 review/start 时调用它;真正准备审查目标和启动方式的是 review_start_inner。

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

TurnRequestProcessor::track_error_response225–237 ↗
fn track_error_response(
        &self,
        request_id: &ConnectionRequestId,
        error: &JSONRPCErrorError,
        error_type: Option<AnalyticsJsonRpcError>,
    )

作用:把一次请求失败的信息记到统计系统里。这样团队可以知道哪些请求常失败、失败原因是什么。

数据流:进去的是请求编号、错误对象和可选的错误分类;它从请求编号里取连接 ID 和请求 ID,复制错误并交给 analytics_events_client;出来没有返回值,只产生统计记录。

调用关系:ensure_direct_input_allowed、turn_start_inner、turn_steer_inner 在发现错误时会调用它;它不修复错误,只负责留下可分析的记录。

调用图:调用 1 个内部函数(track_error_response);被 3 处调用(ensure_direct_input_allowed, turn_start_inner, turn_steer_inner);外部调用 1 个(clone)。

TurnRequestProcessor::load_thread239–254 ↗
async fn load_thread(
        &self,
        thread_id: &str,
    ) -> Result<(ThreadId, Arc<CodexThread>), JSONRPCErrorError>

作用:根据客户端传来的线程 ID 找到真正的会话线程。没有这一步,后面所有操作都不知道要作用在哪个会话上。

数据流:进去的是字符串形式的 thread_id;函数先把它解析成内部 ThreadId,再向 thread_manager 查询线程;出来是内部线程 ID 和线程对象,或者返回“ID 无效/线程不存在”的错误。

调用关系:很多内部流程都会先调用它,包括开始回合、引导、打断、实时对话、审查和注入项目;它是大多数请求进入线程世界的第一道门。

调用图:调用 1 个内部函数(from_string);被 7 处调用(prepare_realtime_conversation_thread, review_start_inner, thread_inject_items_response_inner, thread_settings_update_inner, turn_interrupt_inner, turn_start_inner, turn_steer_inner)。

TurnRequestProcessor::ensure_direct_input_allowed256–273 ↗
async fn ensure_direct_input_allowed(
        &self,
        request_id: &ConnectionRequestId,
        thread: &CodexThread,
    ) -> Result<(), JSONRPCErrorError>

作用:检查某些多智能体子线程是否禁止直接接收 app-server 输入。这样可以防止外部请求绕过多智能体系统自己的调度规则。

数据流:进去的是请求编号和线程;它读取线程的多智能体版本和会话来源,如果发现是 v2 子智能体线程,就生成错误并上报;出来是成功的空结果或错误。

调用关系:turn_start_inner 和 turn_steer_inner 在接受用户输入前调用它;如果不允许,它会调用 track_error_response 记录错误并阻止请求继续。

调用图:调用 2 个内部函数(track_error_response, multi_agent_version);被 2 处调用(turn_start_inner, turn_steer_inner);外部调用 1 个(matches!)。

TurnRequestProcessor::normalize_collaboration_mode275–290 ↗
fn normalize_collaboration_mode(
        &self,
        mut collaboration_mode: CollaborationMode,
    ) -> CollaborationMode

作用:补全协作模式里的默认开发者指令。客户端只传了模式但没传具体说明时,它会从内置预设里找一份合适的说明补上。

数据流:进去的是一个 collaboration_mode;函数检查其中 developer_instructions 是否为空,若为空就查内置预设并填入非空指令;出来是可能被补全过的协作模式。

调用关系:它服务于线程设置构建流程,虽然调用图里没有列出调用者,但在 build_thread_settings_overrides 中会通过 map 使用它来规范客户端传入的协作模式。

TurnRequestProcessor::review_request_from_target292–341 ↗
fn review_request_from_target(
        target: ApiReviewTarget,
    ) -> Result<(ReviewRequest, String), JSONRPCErrorError>

作用:把客户端说的“审查什么”整理成核心审查请求。它还会生成一段给用户看的提示文字,说明这次审查的目标。

数据流:进去的是审查目标,比如未提交改动、某个分支、某个提交或自定义说明;函数会去掉首尾空白并拒绝空分支、空 sha、空说明,再转换成核心目标;出来是 ReviewRequest 和展示提示文字。

调用关系:review_start_inner 在启动审查前会用它清洗目标;它还调用 user_facing_hint 生成用户能看懂的提示。

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

TurnRequestProcessor::request_trace_context343–348 ↗
async fn request_trace_context(
        &self,
        request_id: &ConnectionRequestId,
    ) -> Option<codex_protocol::protocol::W3cTraceContext>

作用:取出当前请求的追踪上下文。追踪上下文可以理解为给一次请求贴的快递单号,方便跨组件追查它一路发生了什么。

数据流:进去的是请求编号;它向 outgoing 查询这个请求对应的 W3C trace context;出来是可选的追踪信息,不改动线程内容。

调用关系:submit_core_op、turn_start_inner 和 start_detached_review 会用它;这样提交给核心线程的操作可以带上同一条追踪链路。

调用图:被 3 处调用(start_detached_review, submit_core_op, turn_start_inner)。

TurnRequestProcessor::submit_core_op350–359 ↗
async fn submit_core_op(
        &self,
        request_id: &ConnectionRequestId,
        thread: &CodexThread,
        op: Op,
    ) -> CodexResult<String>

作用:把整理好的核心操作提交给某个 CodexThread。它是很多请求最终“发命令给核心引擎”的统一通道。

数据流:进去的是请求编号、线程和 Op 操作;它先取请求追踪上下文,再调用线程的 submit_with_trace;出来是核心返回的提交 ID,或者核心错误。

调用关系:线程设置、实时对话、审查、打断等很多内部函数都会调用它;它把上层请求处理器和底层线程执行连接起来。

调用图:调用 2 个内部函数(request_trace_context, submit_with_trace);被 9 处调用(start_detached_review, start_inline_review, thread_realtime_append_audio_inner, thread_realtime_append_speech_inner, thread_realtime_append_text_inner, thread_realtime_start_inner, thread_realtime_stop_inner, thread_settings_update_inner, turn_interrupt_inner)。

TurnRequestProcessor::input_too_large_error361–371 ↗
fn input_too_large_error(actual_chars: usize) -> JSONRPCErrorError

作用:生成“输入太长”的标准错误。这样客户端不仅看到错误文字,还能知道最大允许长度和实际长度。

数据流:进去的是实际字符数;函数创建 invalid_params 错误,并把 input_error_code、max_chars、actual_chars 放进错误 data;出来是完整的 JSON-RPC 错误对象。

调用关系:validate_v2_input_limit 在发现输入超过限制时会用它;它本身不检查文本,只负责把错误做成统一格式。

调用图:外部调用 2 个(format!, json!)。

TurnRequestProcessor::validate_v2_input_limit373–379 ↗
fn validate_v2_input_limit(items: &[V2UserInput]) -> Result<(), JSONRPCErrorError>

作用:检查 v2 用户输入的总字符数是否超过上限。这样可以避免一次请求塞入过大的文本,拖垮处理或超过模型能力。

数据流:进去的是一组 V2UserInput;它把每项文本字符数加起来,与 MAX_USER_INPUT_TEXT_CHARS 比较;出来是成功空结果,或者由 input_too_large_error 生成的错误。

调用关系:turn_start_inner 和 turn_steer_inner 在接受用户输入前都会调用它;如果失败,外层会记录统计并拒绝继续提交。

调用图:外部调用 2 个(input_too_large_error, iter)。

TurnRequestProcessor::turn_start_inner381–499 ↗
async fn turn_start_inner(
        &self,
        request_id: ConnectionRequestId,
        params: TurnStartParams,
        app_server_client_name: Option<String>,
        app_server_client_version: O

作用:真正执行“开始新回合”的完整流程。它负责从找线程、校验输入、套用临时设置,到把用户输入提交给核心线程。

数据流:进去的是请求编号、回合参数和客户端信息;它加载线程、检查是否允许直接输入、检查文本长度、记录客户端信息、解析环境和线程设置、把输入和额外上下文转成核心格式,然后提交 UserInput;出来是带有新 turn 的 TurnStartResponse,同时记录请求和 turn_id 的对应关系,必要时还启动记忆写入任务。

调用关系:turn_start 只负责包装,真正调用的是它;它会调用 load_thread、ensure_direct_input_allowed、build_environment_override、build_thread_settings_overrides、map_additional_context、request_trace_context,并在错误时调用 track_error_response。

调用图:调用 7 个内部函数(build_environment_override, build_thread_settings_overrides, ensure_direct_input_allowed, load_thread, request_trace_context, track_error_response, map_additional_context);被 1 处调用(turn_start);外部调用 6 个(clone, set_app_server_client_info, validate_v2_input_limit, Input, start_memories_startup_task, vec!)。

TurnRequestProcessor::build_environment_override501–532 ↗
async fn build_environment_override(
        &self,
        thread: &CodexThread,
        cwd: Option<AbsolutePathBuf>,
        environment_selections: Option<Vec<TurnEnvironmentSelection>>,
    ) ->

作用:根据请求里的工作目录和环境选择,生成本次回合临时使用的运行环境设置。它解决的是“这次操作到底在哪个目录、哪个环境里跑”的问题。

数据流:进去的是线程、可选 cwd(当前工作目录)和可选环境选择;如果都没有就返回 None,如果只有 cwd 就用线程管理器给出默认环境,如果有环境选择但缺 cwd 就从本地环境或线程快照里找兜底目录;出来是可选的 TurnEnvironmentSelections。

调用关系:turn_start_inner 和 thread_settings_update_inner 会调用它;它生成的环境覆盖项随后会进入 build_thread_settings_overrides 或核心 UserInput 操作。

调用图:调用 2 个内部函数(config_snapshot, new);被 2 处调用(thread_settings_update_inner, turn_start_inner)。

TurnRequestProcessor::build_thread_settings_overrides534–686 ↗
async fn build_thread_settings_overrides(
        &self,
        thread: &CodexThread,
        params: ThreadSettingsBuildParams,
    ) -> Result<codex_protocol::protocol::ThreadSettingsOverrides, JSO

作用:把客户端传来的临时线程设置整理、校验并转换成核心协议格式。它是防止权限、沙盒、模型等设置乱传的关键关卡。

数据流:进去的是线程和一包设置参数;它先拒绝互相冲突的 permissions 与 sandboxPolicy,补全协作模式,必要时重新加载配置来解析权限配置,再让线程预览这些覆盖设置是否合法;出来是 ThreadSettingsOverrides,包含环境、工作区根目录、审批策略、沙盒、权限、模型、推理强度等字段。

调用关系:turn_start_inner 和 thread_settings_update_inner 都会调用它;它会用 config_manager.load_for_cwd 解析权限,并用 preview_thread_settings_overrides 让核心线程提前检查。

调用图:调用 3 个内部函数(load_for_cwd, config_snapshot, preview_thread_settings_overrides);被 2 处调用(thread_settings_update_inner, turn_start_inner);外部调用 2 个(default, format!)。

TurnRequestProcessor::thread_settings_update_inner688–730 ↗
async fn thread_settings_update_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadSettingsUpdateParams,
    ) -> Result<ThreadSettingsUpdateResponse, JSONRPCErrorEr

作用:真正处理线程设置更新。它只在有实际改动时才向核心线程提交设置操作。

数据流:进去的是请求编号和设置参数;它加载线程,解析 cwd,构建环境覆盖和线程设置覆盖;如果设置不是默认空值,就提交 Op::ThreadSettings;出来是空的 ThreadSettingsUpdateResponse 或错误。

调用关系:thread_settings_update 会调用它;它依赖 load_thread、build_environment_override、build_thread_settings_overrides 和 submit_core_op,把设置请求排进核心线程。

调用图:调用 4 个内部函数(build_environment_override, build_thread_settings_overrides, load_thread, submit_core_op);被 1 处调用(thread_settings_update);外部调用 1 个(default)。

TurnRequestProcessor::thread_inject_items_response_inner732–757 ↗
async fn thread_inject_items_response_inner(
        &self,
        params: ThreadInjectItemsParams,
    ) -> Result<ThreadInjectItemsResponse, JSONRPCErrorError>

作用:真正把响应项注入到指定线程。它会先确认每个 JSON 项目确实能解析成系统认识的响应项。

数据流:进去的是线程 ID 和一组 JSON 值;函数加载线程,把每项 JSON 反序列化成 ResponseItem,任何一项不合法都会指出位置;成功后调用线程的 inject_response_items;出来是空成功响应或错误。

调用关系:thread_inject_items 调用它;它用 load_thread 找到目标线程,然后把注入动作交给 CodexThread。

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

TurnRequestProcessor::set_app_server_client_info759–776 ↗
async fn set_app_server_client_info(
        thread: &CodexThread,
        app_server_client_name: Option<String>,
        app_server_client_version: Option<String>,
    ) -> Result<(), JSONRPCErrorEr

作用:把当前 app-server 客户端的名称和版本写入线程。这样核心层可以根据客户端差异做兼容处理。

数据流:进去的是线程、客户端名称和版本;它先用 xcode_26_4_mcp_elicitations_auto_deny 判断是否需要特殊兼容开关,再调用线程保存这些信息;出来是成功空结果或内部错误。

调用关系:turn_start_inner 在开始回合前调用它;它又调用 xcode_26_4_mcp_elicitations_auto_deny 来处理 Xcode 26.4 的兼容规则。

调用图:调用 2 个内部函数(xcode_26_4_mcp_elicitations_auto_deny, set_app_server_client_info)。

TurnRequestProcessor::turn_steer_inner778–885 ↗
async fn turn_steer_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: TurnSteerParams,
    ) -> Result<TurnSteerResponse, JSONRPCErrorError>

作用:真正处理“给正在运行的回合追加指示”。它确保追加内容是发给正确的活动回合,而不是误塞到别的回合里。

数据流:进去的是请求编号和引导参数;它加载线程、检查直接输入权限、要求 expected_turn_id 非空、记录请求对应的回合、检查输入长度、转换输入和额外上下文,然后调用线程 steer_input;出来是新的或确认的 turn_id,或者带明确原因的错误。

调用关系:turn_steer 调用它;它用 load_thread、ensure_direct_input_allowed、validate_v2_input_limit 和 map_additional_context 做准备,出错时用 track_error_response 记录分类。

调用图:调用 4 个内部函数(ensure_direct_input_allowed, load_thread, track_error_response, map_additional_context);被 1 处调用(turn_steer);外部调用 2 个(validate_v2_input_limit, Input)。

TurnRequestProcessor::prepare_realtime_conversation_thread887–916 ↗
async fn prepare_realtime_conversation_thread(
        &self,
        request_id: &ConnectionRequestId,
        thread_id: &str,
    ) -> Result<Option<(ThreadId, Arc<CodexThread>)>, JSONRPCErrorError

作用:为实时对话请求准备线程。它确保连接已经挂上会话监听器,并且这个线程真的支持实时对话功能。

数据流:进去的是请求编号和线程 ID 字符串;它加载线程,调用 ensure_conversation_listener 让当前连接能收到线程事件,如果连接已关闭就返回 None,再检查 Feature::RealtimeConversation;出来是可选的线程 ID 和线程对象。

调用关系:所有实时对话内部函数都会先调用它;它像进入语音房间前的门禁,没监听、连接关了或线程不支持都不能继续。

调用图:调用 2 个内部函数(ensure_conversation_listener, load_thread);被 5 处调用(thread_realtime_append_audio_inner, thread_realtime_append_speech_inner, thread_realtime_append_text_inner, thread_realtime_start_inner, thread_realtime_stop_inner);外部调用 1 个(format!)。

TurnRequestProcessor::thread_realtime_start_inner918–956 ↗
async fn thread_realtime_start_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadRealtimeStartParams,
    ) -> Result<Option<ThreadRealtimeStartResponse>, JSONRPCEr

作用:真正向核心线程提交“启动实时对话”的命令。它把客户端传来的模型、声音、传输方式等参数整理成核心会话启动参数。

数据流:进去的是请求编号和实时启动参数;它先准备线程,然后构造 ConversationStartParams,包括 websocket 或 webrtc 传输、声音、模型、输出形式等;出来是默认启动响应、无响应或错误。

调用关系:thread_realtime_start 调用它;它依赖 prepare_realtime_conversation_thread 做门禁,再用 submit_core_op 提交 RealtimeConversationStart。

调用图:调用 2 个内部函数(prepare_realtime_conversation_thread, submit_core_op);被 1 处调用(thread_realtime_start);外部调用 2 个(default, RealtimeConversationStart)。

TurnRequestProcessor::thread_realtime_append_audio_inner958–983 ↗
async fn thread_realtime_append_audio_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadRealtimeAppendAudioParams,
    ) -> Result<Option<ThreadRealtimeAppendAudioR

作用:真正向实时对话追加音频帧。它把客户端发来的音频包装成核心层认识的音频参数。

数据流:进去的是请求编号、线程 ID 和音频;它准备线程,把 audio 转成 frame,提交 Op::RealtimeConversationAudio;出来是默认成功响应、无响应或错误。

调用关系:thread_realtime_append_audio 调用它;它先走 prepare_realtime_conversation_thread,再通过 submit_core_op 把音频送入核心线程。

调用图:调用 2 个内部函数(prepare_realtime_conversation_thread, submit_core_op);被 1 处调用(thread_realtime_append_audio);外部调用 2 个(default, RealtimeConversationAudio)。

TurnRequestProcessor::thread_realtime_append_text_inner985–1011 ↗
async fn thread_realtime_append_text_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadRealtimeAppendTextParams,
    ) -> Result<Option<ThreadRealtimeAppendTextResp

作用:真正向实时对话追加文本消息。它让实时会话可以接收用户或系统角色的文字输入。

数据流:进去的是请求编号、线程 ID、文本和角色;它准备线程,构造 ConversationTextParams,然后提交 RealtimeConversationText;出来是默认成功响应、无响应或错误。

调用关系:thread_realtime_append_text 调用它;它和其他实时追加函数一样,先确保线程可用,再用 submit_core_op 发给核心。

调用图:调用 2 个内部函数(prepare_realtime_conversation_thread, submit_core_op);被 1 处调用(thread_realtime_append_text);外部调用 2 个(default, RealtimeConversationText)。

TurnRequestProcessor::thread_realtime_append_speech_inner1013–1036 ↗
async fn thread_realtime_append_speech_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadRealtimeAppendSpeechParams,
    ) -> Result<Option<ThreadRealtimeAppendSpee

作用:真正向实时对话追加要作为语音处理的文本。它把一段文字送进实时语音流程。

数据流:进去的是请求编号、线程 ID 和文本;它准备线程,把文本放进 ConversationSpeechParams,并提交 RealtimeConversationSpeech;出来是默认成功响应、无响应或错误。

调用关系:thread_realtime_append_speech 调用它;它依赖 prepare_realtime_conversation_thread 和 submit_core_op 完成从请求到核心操作的转交。

调用图:调用 2 个内部函数(prepare_realtime_conversation_thread, submit_core_op);被 1 处调用(thread_realtime_append_speech);外部调用 2 个(default, RealtimeConversationSpeech)。

TurnRequestProcessor::thread_realtime_stop_inner1038–1055 ↗
async fn thread_realtime_stop_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadRealtimeStopParams,
    ) -> Result<Option<ThreadRealtimeStopResponse>, JSONRPCError

作用:真正停止实时对话。它向核心线程发送关闭实时会话的命令。

数据流:进去的是请求编号和线程 ID;它准备线程后提交 Op::RealtimeConversationClose;出来是默认停止响应、无响应或错误。

调用关系:thread_realtime_stop 调用它;它和启动、追加音频文本一样,先通过 prepare_realtime_conversation_thread 校验,再通过 submit_core_op 下发关闭操作。

调用图:调用 2 个内部函数(prepare_realtime_conversation_thread, submit_core_op);被 1 处调用(thread_realtime_stop);外部调用 1 个(default)。

TurnRequestProcessor::build_review_turn1057–1082 ↗
fn build_review_turn(turn_id: String, display_text: &str) -> Turn

作用:为代码审查创建一个展示用的 Turn 对象。这样客户端能像显示普通回合一样显示审查已经开始。

数据流:进去的是 turn_id 和展示文字;如果展示文字为空,就生成没有项目的回合,否则生成一条用户消息项目;出来是状态为 InProgress 的 Turn。

调用关系:start_inline_review 和 start_detached_review 在提交审查操作后会用它;它只造展示数据,不负责真正审查。

调用图:外部调用 2 个(new, vec!)。

TurnRequestProcessor::emit_review_started1084–1097 ↗
async fn emit_review_started(
        &self,
        request_id: &ConnectionRequestId,
        turn: Turn,
        review_thread_id: String,
    )

作用:向客户端发送“审查已开始”的响应。它把审查回合和审查线程 ID 一起告诉客户端。

数据流:进去的是请求编号、Turn 和 review_thread_id;它包装成 ReviewStartResponse,通过 outgoing 发回响应;出来没有业务返回值。

调用关系:start_inline_review 和 start_detached_review 都会在审查启动后调用它;它是审查流程最后通知客户端的一步。

调用图:被 2 处调用(start_detached_review, start_inline_review);外部调用 1 个(clone)。

TurnRequestProcessor::start_inline_review1099–1119 ↗
async fn start_inline_review(
        &self,
        request_id: &ConnectionRequestId,
        parent_thread: Arc<CodexThread>,
        review_request: ReviewRequest,
        display_text: &str,

作用:在当前线程里启动代码审查。也就是说审查作为父会话里的一个普通回合出现。

数据流:进去的是请求编号、父线程、审查请求、展示文字和父线程 ID;它向父线程提交 Op::Review,拿到 turn_id,构造展示 Turn,再发送审查开始响应;出来是成功空结果或错误。

调用关系:review_start_inner 判断 delivery 是 Inline 时调用它;它依赖 submit_core_op、build_review_turn 和 emit_review_started。

调用图:调用 2 个内部函数(emit_review_started, submit_core_op);被 1 处调用(review_start_inner);外部调用 1 个(build_review_turn)。

TurnRequestProcessor::start_detached_review1121–1230 ↗
async fn start_detached_review(
        &self,
        request_id: &ConnectionRequestId,
        parent_thread_id: ThreadId,
        parent_thread: Arc<CodexThread>,
        review_request: ReviewRequ

作用:另开一个线程来做代码审查。这样审查不会直接混在原来的会话回合里,但会继承父线程历史作为背景。

数据流:进去的是请求编号、父线程 ID、父线程、审查请求和展示文字;它先确保父线程历史落盘,读取历史,复制配置并可能切换到 review_model,然后 fork 出审查线程,挂监听器,更新线程列表通知客户端,再在新线程提交审查操作;出来是成功空结果或错误。

调用关系:review_start_inner 判断 delivery 是 Detached 时调用它;它会调用 ensure_conversation_listener、request_trace_context、submit_core_op、emit_review_started,并通过线程 watch 管理器把新审查线程悄悄加入列表再通知客户端。

调用图:调用 6 个内部函数(emit_review_started, ensure_conversation_listener, request_trace_context, submit_core_op, loaded_status_for_thread, upsert_thread_silently);被 1 处调用(review_start_inner);外部调用 4 个(build_review_turn, ThreadStarted, Resumed, warn!)。

TurnRequestProcessor::review_start_inner1232–1268 ↗
async fn review_start_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: ReviewStartParams,
    ) -> Result<(), JSONRPCErrorError>

作用:真正处理开始审查的总流程。它负责找父线程、清洗审查目标,并按客户端要求选择内联审查还是独立审查。

数据流:进去的是请求编号和 ReviewStartParams;它拆出线程 ID、目标和交付方式,加载父线程,把目标转成 ReviewRequest 和展示文本,然后分支调用 start_inline_review 或 start_detached_review;出来是成功空结果或错误。

调用关系:review_start 调用它;它是审查功能的调度点,向下连接 review_request_from_target、start_inline_review 和 start_detached_review。

调用图:调用 3 个内部函数(load_thread, start_detached_review, start_inline_review);被 1 处调用(review_start);外部调用 1 个(review_request_from_target)。

TurnRequestProcessor::turn_interrupt_inner1270–1333 ↗
async fn turn_interrupt_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: TurnInterruptParams,
    ) -> Result<Option<TurnInterruptResponse>, JSONRPCErrorError>

作用:真正处理打断请求。它既要确认要打断的回合确实还在运行,也要把等待响应的请求登记好。

数据流:进去的是请求编号、线程 ID 和 turn_id;如果 turn_id 为空就当作启动阶段打断,否则加载线程状态,确认活动回合 ID 匹配且还在运行,把请求加入 pending_interrupts,然后提交 Op::Interrupt;出来可能立即返回打断成功,也可能返回 None 等待 TurnAborted 事件,失败时会清理已登记的 pending interrupt。

调用关系:turn_interrupt 调用它;它会用 load_thread 找线程、用 thread_state_manager 查状态、用 submit_core_op 提交中断,是“用户点停止按钮”背后的主要流程。

调用图:调用 3 个内部函数(load_thread, submit_core_op, thread_state);被 1 处调用(turn_interrupt);外部调用 3 个(clone, format!, matches!)。

TurnRequestProcessor::listener_task_context1335–1347 ↗
fn listener_task_context(&self) -> ListenerTaskContext

作用:组装会话监听任务需要的一包上下文。监听任务要知道怎么取线程、怎么发消息、怎么更新线程状态等。

数据流:进去的是 self 中已经保存的各种管理器和配置;函数复制或克隆这些共享句柄,放进 ListenerTaskContext;出来是一个上下文对象,不启动任务。

调用关系:ensure_conversation_listener 会调用它;它相当于把监听器开工前需要的工具箱打包好。

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

TurnRequestProcessor::ensure_conversation_listener1349–1362 ↗
async fn ensure_conversation_listener(
        &self,
        conversation_id: ThreadId,
        connection_id: ConnectionId,
        raw_events_enabled: bool,
    ) -> Result<EnsureConversationListen

作用:确保某个连接正在监听某个会话线程的事件。没有监听器,客户端可能收不到回合进度、审查线程变化或实时会话事件。

数据流:进去的是会话 ID、连接 ID 和是否启用原始事件;它先生成 listener_task_context,再调用 thread_lifecycle 里的 ensure_conversation_listener;出来是监听已附着、连接已关闭或错误。

调用关系:prepare_realtime_conversation_thread 和 start_detached_review 会调用它;它把具体监听创建工作交给 thread_lifecycle 模块。

调用图:调用 2 个内部函数(ensure_conversation_listener, listener_task_context);被 2 处调用(prepare_realtime_conversation_thread, start_detached_review)。

xcode_26_4_mcp_elicitations_auto_deny1365–1374 ↗
fn xcode_26_4_mcp_elicitations_auto_deny(
    client_name: Option<&str>,
    client_version: Option<&str>,
) -> bool

作用:判断是否要对 Xcode 26.4 客户端自动拒绝 MCP elicitation 请求。这里是一个兼容旧客户端的临时补丁,因为那个版本还不能显示这类请求。

数据流:进去的是客户端名称和版本;函数检查名称是否为 Xcode,版本是否以 26.4 开头;出来是布尔值,true 表示启用自动拒绝。

调用关系:set_app_server_client_info 会调用它;它不接触线程本身,只给保存客户端信息时提供一个兼容开关。

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

app-server/src/request_processors/windows_sandbox_processor.rs源码 ↗
orchestrationrequest handling

Windows 沙盒可以理解成给程序套一个“隔离房间”,让它在更受控的环境里运行。这个文件就是服务器端接待这类请求的窗口。客户端问准备状态时,它会根据当前系统、配置和已有安装状态,回答“没配置”“已可用”或“需要更新”。客户端要求开始设置时,它不会盲目开工,而是先按当前工作目录重新加载配置,确认管理员规则允许这个沙盒模式;如果不允许,就直接报错,避免客户端误以为已经开始。确认没问题后,它先回一个“已开始”,再开一个后台任务去做真正的 Windows 沙盒设置。后台任务完成后,会给同一个连接发通知,说明成功还是失败。这样设计的好处是:请求响应快,错误又能及时告诉用户,长时间安装也不会堵住主服务。

函数细节12
WindowsSandboxRequestProcessor::new11–21 ↗
fn new(
        outgoing: Arc<OutgoingMessageSender>,
        config: Arc<Config>,
        config_manager: ConfigManager,
    ) -> Self

作用:创建一个 Windows 沙盒请求处理器,把之后办事需要的三样东西收好:发消息的通道、当前配置、以及能重新加载配置的工具。

数据流:进去的是 outgoing、config 和 config_manager → 函数把它们放进 WindowsSandboxRequestProcessor 这个结构里 → 出来的是一个可以处理 Windows 沙盒请求的新对象,本身不做网络发送,也不改配置。

调用关系:它在上层创建请求处理器时被调用,相当于把服务台窗口搭好。后面的 readiness 查询和 setup 启动都会使用这里保存的消息通道和配置工具。

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

WindowsSandboxRequestProcessor::windows_sandbox_readiness23–27 ↗
async fn windows_sandbox_readiness(
        &self,
    ) -> Result<WindowsSandboxReadinessResponse, JSONRPCErrorError>

作用:回答客户端一句话:当前 Windows 沙盒是否已经准备好。它把复杂判断包装成一个异步请求接口,方便请求分发器直接调用。

数据流:进去的是处理器自己保存的配置 → 它调用 determine_windows_sandbox_readiness 做实际判断 → 出来的是 WindowsSandboxReadinessResponse,里面写着状态,比如未配置、可用或需要更新;不会改动任何状态。

调用关系:当 handle_initialized_client_request 收到已初始化客户端的 readiness 请求时会调用它。它自己不判断细节,而是把活交给 determine_windows_sandbox_readiness。

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

WindowsSandboxRequestProcessor::windows_sandbox_setup_start29–37 ↗
async fn windows_sandbox_setup_start(
        &self,
        request_id: &ConnectionRequestId,
        params: WindowsSandboxSetupStartParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErr

作用:处理“开始设置 Windows 沙盒”的客户端请求。它的公开职责是启动流程,并把成功启动后的直接响应整理成协议需要的形式。

数据流:进去的是请求编号和客户端传来的设置参数 → 它调用 windows_sandbox_setup_start_inner 做真正的检查和启动 → 如果启动步骤成功,出来的是 None,表示正式的“已开始”响应已经在内部单独发过了;如果失败,就返回 JSON-RPC 错误。

调用关系:handle_initialized_client_request 收到 setup start 请求时会到这里。它像外层包装纸,把核心工作交给 windows_sandbox_setup_start_inner,并把返回值调整成客户端请求处理框架能理解的样子。

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

WindowsSandboxRequestProcessor::windows_sandbox_setup_start_inner39–104 ↗
async fn windows_sandbox_setup_start_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: WindowsSandboxSetupStartParams,
    ) -> Result<(), JSONRPCErrorError>

作用:这是启动 Windows 沙盒设置的核心流程:先验证配置和权限,再告诉客户端“已经开始”,最后把真正耗时的设置任务丢到后台执行。

数据流:进去的是请求编号和参数,参数里可能带有工作目录和想要的沙盒模式 → 它先确定工作目录,没有传就用当前配置里的目录;再用 config_manager.load_for_cwd 按这个目录重新加载配置;接着用 resolve_allowed_windows_sandbox_setup_mode 检查这个模式是否被配置规则允许;通过后,先向客户端发送 started: true;随后复制发送通道和连接编号,启动一个后台任务。后台任务会收集权限配置、工作区目录、环境变量、codex_home 等信息,调用 run_windows_sandbox_setup 真正设置沙盒,最后发 WindowsSandboxSetupCompleted 通知,告诉客户端成功、失败以及错误文字。

调用关系:它由 windows_sandbox_setup_start 调用,是整个设置请求的发动机。它把配置加载交给 load_for_cwd,把模式合法性检查交给 resolve_allowed_windows_sandbox_setup_mode,把真正的系统级设置交给 codex_core::windows_sandbox::run_windows_sandbox_setup,最后通过 outgoing 把结果通知发回原连接。

调用图:调用 3 个内部函数(load_for_cwd, resolve_allowed_windows_sandbox_setup_mode, run_windows_sandbox_setup);被 1 处调用(windows_sandbox_setup_start);外部调用 6 个(clone, default, WindowsSandboxSetupCompleted, clone, vars, spawn)。

resolve_allowed_windows_sandbox_setup_mode108–127 ↗
fn resolve_allowed_windows_sandbox_setup_mode(
    requirements: &codex_config::ConfigRequirements,
    requested_mode: WindowsSandboxSetupMode,
) -> Result<CoreWindowsSandboxSetupMode, JSONRPCErrorEr

作用:把客户端说的沙盒模式翻译成核心代码能使用的模式,同时检查管理员或配置文件是否允许这么设置。它防止用户绕过受管配置,启动不被允许的沙盒模式。

数据流:进去的是配置要求 requirements 和客户端请求的 WindowsSandboxSetupMode → 它把请求模式对应到两种表示:一种给核心沙盒代码用,一种给配置规则检查用;然后询问 requirements.windows_sandbox_mode.can_set 是否允许设置这个值 → 如果允许,出来的是 CoreWindowsSandboxSetupMode;如果不允许,出来的是 invalid request 错误。

调用关系:windows_sandbox_setup_start_inner 在真正开始设置前调用它。测试函数 tests::resolve_allowed_windows_sandbox_setup_mode_rejects_disallowed_mode 也会调用它,确认不允许的模式会被挡住。

调用图:被 2 处调用(windows_sandbox_setup_start_inner, resolve_allowed_windows_sandbox_setup_mode_rejects_disallowed_mode)。

determine_windows_sandbox_readiness129–140 ↗
fn determine_windows_sandbox_readiness(config: &Config) -> WindowsSandboxReadinessResponse

作用:根据当前运行平台和配置,判断 Windows 沙盒现在处于什么准备状态。它是 readiness 查询的实际判断入口。

数据流:进去的是 Config 配置 → 它先看程序是不是运行在 Windows 上;如果不是 Windows,直接认为未配置;如果是 Windows,就从配置推导沙盒级别,并检查 codex_home 里的沙盒设置是否已经完成 → 最后交给 determine_windows_sandbox_readiness_from_state 生成响应。

调用关系:WindowsSandboxRequestProcessor::windows_sandbox_readiness 调用它来回答客户端。它自己负责收集真实环境信息,然后把纯粹的状态判断交给 determine_windows_sandbox_readiness_from_state。

调用图:调用 1 个内部函数(determine_windows_sandbox_readiness_from_state);被 1 处调用(windows_sandbox_readiness);外部调用 2 个(cfg!, from_config)。

determine_windows_sandbox_readiness_from_state142–159 ↗
fn determine_windows_sandbox_readiness_from_state(
    windows_sandbox_level: WindowsSandboxLevel,
    sandbox_setup_is_complete: bool,
) -> WindowsSandboxReadinessResponse

作用:把两个简单事实翻译成用户能看懂的准备状态:沙盒级别是什么、需要的设置是否已经完成。它把判断规则集中在一个容易测试的小函数里。

数据流:进去的是 windows_sandbox_level 和 sandbox_setup_is_complete → 如果沙盒被禁用,返回 NotConfigured;如果是受限令牌模式,返回 Ready;如果是提升权限模式,就看设置是否完成,完成返回 Ready,没完成返回 UpdateRequired → 出来的是 WindowsSandboxReadinessResponse。

调用关系:determine_windows_sandbox_readiness 用它做最终判断。多个测试也直接调用它,用固定输入检查各种状态是否会得到正确答案。

调用图:被 5 处调用(determine_windows_sandbox_readiness, determine_windows_sandbox_readiness_reports_not_configured_when_disabled, determine_windows_sandbox_readiness_reports_ready_for_complete_elevated_mode, determine_windows_sandbox_readiness_reports_ready_for_unelevated_mode, determine_windows_sandbox_readiness_reports_update_required_when_elevated_setup_is_stale)。

tests::resolve_allowed_windows_sandbox_setup_mode_rejects_disallowed_mode171–191 ↗
fn resolve_allowed_windows_sandbox_setup_mode_rejects_disallowed_mode()

作用:这个测试确认:当配置只允许 Elevated 模式时,用户请求 Unelevated 模式会被拒绝。它是在保护“受管配置不能被请求绕过”这条规则。

数据流:进去的是测试里临时造出的配置要求:只允许 Elevated → 它调用 resolve_allowed_windows_sandbox_setup_mode,并故意传 Unelevated → 出来应该是一个错误;测试再检查错误码是 INVALID_REQUEST_ERROR_CODE,错误消息里也提到 Windows 沙盒模式无效。

调用关系:它直接测试 resolve_allowed_windows_sandbox_setup_mode。这个测试不参与运行时流程,只在测试阶段防止以后有人改代码时把限制检查弄坏。

调用图:调用 3 个内部函数(resolve_allowed_windows_sandbox_setup_mode, new, allow_only);外部调用 3 个(default, assert!, assert_eq!)。

tests::determine_windows_sandbox_readiness_reports_not_configured_when_disabled194–201 ↗
fn determine_windows_sandbox_readiness_reports_not_configured_when_disabled()

作用:这个测试确认:如果沙盒级别是 Disabled,也就是关闭状态,那么 readiness 应该报告未配置。

数据流:进去的是 Disabled 和 setup 未完成这个测试输入 → 它调用 determine_windows_sandbox_readiness_from_state → 出来应该是 NotConfigured,测试用 assert_eq 检查结果。

调用关系:它直接覆盖 determine_windows_sandbox_readiness_from_state 的禁用分支。作用是在测试阶段锁住这条最基本的状态规则。

调用图:调用 1 个内部函数(determine_windows_sandbox_readiness_from_state);外部调用 1 个(assert_eq!)。

tests::determine_windows_sandbox_readiness_reports_ready_for_unelevated_mode204–211 ↗
fn determine_windows_sandbox_readiness_reports_ready_for_unelevated_mode()

作用:这个测试确认:受限令牌模式不需要额外的提升权限设置,即使 setup 标记没完成,也应该算可用。

数据流:进去的是 RestrictedToken 和 setup 未完成 → 它调用 determine_windows_sandbox_readiness_from_state → 出来应该是 Ready,测试检查这个状态是否正确。

调用关系:它直接测试 determine_windows_sandbox_readiness_from_state 的受限令牌分支。它说明 Unelevated 这类模式和 Elevated 模式的准备条件不一样。

调用图:调用 1 个内部函数(determine_windows_sandbox_readiness_from_state);外部调用 1 个(assert_eq!)。

tests::determine_windows_sandbox_readiness_reports_ready_for_complete_elevated_mode214–221 ↗
fn determine_windows_sandbox_readiness_reports_ready_for_complete_elevated_mode()

作用:这个测试确认:提升权限模式下,如果所需设置已经完成,就应该报告可用。

数据流:进去的是 Elevated 和 setup 已完成 → 它调用 determine_windows_sandbox_readiness_from_state → 出来应该是 Ready,测试用断言确认。

调用关系:它覆盖 determine_windows_sandbox_readiness_from_state 的 Elevated 成功路径。这样能保证提升权限沙盒在安装完成后不会被误报成需要更新。

调用图:调用 1 个内部函数(determine_windows_sandbox_readiness_from_state);外部调用 1 个(assert_eq!)。

tests::determine_windows_sandbox_readiness_reports_update_required_when_elevated_setup_is_stale224–231 ↗
fn determine_windows_sandbox_readiness_reports_update_required_when_elevated_setup_is_stale()

作用:这个测试确认:提升权限模式下,如果沙盒设置没有完成或已经过期,就应该报告需要更新,而不是假装已经可用。

数据流:进去的是 Elevated 和 setup 未完成 → 它调用 determine_windows_sandbox_readiness_from_state → 出来应该是 UpdateRequired,测试检查这个结果。

调用关系:它覆盖 determine_windows_sandbox_readiness_from_state 的 Elevated 待更新路径。这个测试保护的是用户体验和安全性:该补设置时必须明确提示。

调用图:调用 1 个内部函数(determine_windows_sandbox_readiness_from_state);外部调用 1 个(assert_eq!)。

线程与对话路由

本组跟踪应用服务器以线程为中心的请求路径,涵盖线程生命周期操作、目标、删除、动态工具响应,以及面向模型的工具分发。

app-server/src/request_processors/thread_processor.rs源码 ↗
orchestrationrequest handling

这里的“thread”可以理解成一条可继续的 AI 对话,不是操作系统线程。这个文件像服务台前台:收到客户端的 JSON-RPC 请求(一种用 JSON 发命令和收结果的协议)后,先检查参数,再找真正干活的部件,比如线程管理器、存储层、状态监听器和消息发送器。它还处理很多容易出错的边界:恢复一个已经在跑的对话时不能重复启动;归档前要先把活跃对话关停;列表分页要防止无限翻页;读取历史时要把磁盘里的记录和内存里的最新状态合起来。没有它,客户端会直接面对一堆底层零件,容易出现重复会话、状态不同步、历史读不全、错误信息混乱等问题。

函数细节131
collect_resume_override_mismatches21–140 ↗
fn collect_resume_override_mismatches(
    request: &ThreadResumeParams,
    config_snapshot: &ThreadConfigSnapshot,
) -> Vec<String>

作用:检查“恢复已有对话”时,用户新给的模型、目录、权限等设置,和正在运行的对话实际设置是不是不一致。

数据流:输入恢复请求和当前对话配置快照 → 逐项比较模型、模型供应商、工作目录、沙盒、权限、人格等 → 输出一组可读的差异说明,不改动外部状态。

调用关系:resume_running_thread 在发现对话已经运行时会用它判断新参数是否只能被忽略,并把差异写进警告。

调用图:调用 2 个内部函数(cwd, sandbox_policy);被 1 处调用(resume_running_thread);外部调用 4 个(new, format!, matches!, from)。

merge_persisted_resume_metadata142–160 ↗
fn merge_persisted_resume_metadata(
    request_overrides: &mut Option<HashMap<String, serde_json::Value>>,
    typesafe_overrides: &mut ConfigOverrides,
    persisted_metadata: &ThreadMetadata,
)

作用:恢复旧对话时,把之前保存的模型信息补回配置里,避免换成当前默认模型。

数据流:输入请求里的自由配置、类型化配置和已保存元数据 → 如果用户没有显式指定模型,就填入旧模型、旧供应商和推理强度 → 修改传入的配置对象,无返回值。

调用关系:load_and_apply_persisted_resume_metadata 读到数据库里的旧元数据后调用它。

调用图:调用 1 个内部函数(has_model_resume_override);被 1 处调用(load_and_apply_persisted_resume_metadata);外部调用 1 个(String)。

normalize_thread_list_cwd_filters162–184 ↗
fn normalize_thread_list_cwd_filters(
    cwd: Option<ThreadListCwdFilter>,
) -> Result<Option<Vec<PathBuf>>, JSONRPCErrorError>

作用:把对话列表里的工作目录筛选条件统一成绝对路径,方便准确比较。

数据流:输入可能为空、单个或多个目录字符串 → 逐个按当前目录解析成规范路径 → 输出路径列表;路径不合法就返回参数错误。

调用关系:thread_list_response_inner 在真正查询列表前调用它,避免后面用模糊路径筛选。

调用图:调用 1 个内部函数(relative_to_current_dir);被 1 处调用(thread_list_response_inner);外部调用 2 个(with_capacity, vec!)。

has_model_resume_override186–195 ↗
fn has_model_resume_override(
    request_overrides: Option<&HashMap<String, serde_json::Value>>,
    typesafe_overrides: &ConfigOverrides,
) -> bool

作用:判断恢复请求里是否已经明确指定了模型相关设置。

数据流:输入两类配置覆盖项 → 查看是否包含模型、模型供应商或推理强度 → 输出 true 或 false,不改状态。

调用关系:merge_persisted_resume_metadata 用它决定是否可以安全地用旧元数据补模型。

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

validate_dynamic_tools197–342 ↗
fn validate_dynamic_tools(tools: &[DynamicToolSpec]) -> Result<(), String>

作用:检查客户端临时提供的工具定义是否合法,防止把坏名字、重复工具或不支持的输入格式交给模型。

数据流:输入动态工具列表 → 检查名字长度、字符、保留命名空间、重复项、输入 schema(工具参数格式说明)等 → 全部通过返回成功,否则返回具体错误文字。

调用关系:thread_start_task 在新建对话前调用它;只有通过检查的工具才会随对话启动。

调用图:被 1 处调用(thread_start_task);外部调用 2 个(new, format!)。

ThreadRequestProcessor::new377–412 ↗
fn new(
        auth_manager: Arc<AuthManager>,
        thread_manager: Arc<ThreadManager>,
        outgoing: Arc<OutgoingMessageSender>,
        arg0_paths: Arg0DispatchPaths,
        config: Arc<Con

作用:创建 ThreadRequestProcessor,把它需要用的管理器、存储、配置和发送器都装进去。

数据流:输入认证、线程管理、消息发送、配置、数据库、监听器等共享对象 → 保存到结构体字段里 → 输出一个可处理请求的处理器实例。

调用关系:服务启动组装请求处理链时会调用它,之后各种 thread/* 请求都依赖这个实例。

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

ThreadRequestProcessor::thread_start414–431 ↗
async fn thread_start(
        &self,
        request_id: ConnectionRequestId,
        params: ThreadStartParams,
        app_server_client_name: Option<String>,
        app_server_client_version: Opt

作用:处理客户端的新建对话请求。

数据流:输入请求编号、启动参数、客户端信息和请求上下文 → 转交 thread_start_inner 异步启动 → 成功时不直接返回载荷,因为响应会由后台任务发送。

调用关系:handle_initialized_client_request 收到 thread/start 后调用它,它把重活交给 thread_start_inner。

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

ThreadRequestProcessor::thread_unsubscribe433–441 ↗
async fn thread_unsubscribe(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadUnsubscribeParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:让某个连接取消订阅一个对话的实时事件。

数据流:输入请求编号和退订参数 → 调用内部退订逻辑 → 输出退订状态响应。

调用关系:客户端不想再收某个对话更新时由 handle_initialized_client_request 调用。

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

ThreadRequestProcessor::thread_resume443–458 ↗
async fn thread_resume(
        &self,
        request_id: ConnectionRequestId,
        params: ThreadResumeParams,
        app_server_client_name: Option<String>,
        app_server_client_version: O

作用:处理恢复旧对话请求,让客户端重新接上历史会话。

数据流:输入请求、恢复参数和客户端信息 → 交给 thread_resume_inner 查历史、建配置、接监听 → 成功响应由内部发送。

调用关系:handle_initialized_client_request 收到 thread/resume 后调用它。

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

ThreadRequestProcessor::thread_fork460–475 ↗
async fn thread_fork(
        &self,
        request_id: ConnectionRequestId,
        params: ThreadForkParams,
        app_server_client_name: Option<String>,
        app_server_client_version: Optio

作用:处理从旧对话复制出新分支的请求。

数据流:输入源对话、覆盖配置和客户端信息 → 转给 thread_fork_inner 读取历史并创建新对话 → 成功响应由内部发送。

调用关系:客户端想从某个历史点另开一条路时,由 handle_initialized_client_request 调用。

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

ThreadRequestProcessor::thread_archive477–498 ↗
async fn thread_archive(
        &self,
        request_id: ConnectionRequestId,
        params: ThreadArchiveParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:把对话归档,让它从普通列表里隐藏,同时通知客户端状态变化。

数据流:输入请求编号和归档参数 → 调用 thread_archive_inner 归档目标及可能的子对话 → 发送响应和 ThreadArchived 通知。

调用关系:handle_initialized_client_request 收到归档请求时调用;内部依赖 thread_archive_inner。

调用图:调用 1 个内部函数(thread_archive_inner);被 1 处调用(handle_initialized_client_request);外部调用 2 个(ThreadArchived, clone)。

ThreadRequestProcessor::thread_increment_elicitation500–507 ↗
async fn thread_increment_elicitation(
        &self,
        params: ThreadIncrementElicitationParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:增加某个对话的“外部等待确认”计数,让对话知道自己该暂停。

数据流:输入线程 ID → 内部找到运行中对话并把计数加一 → 输出新计数和是否暂停。

调用关系:handle_initialized_client_request 调用它,实际修改由 thread_increment_elicitation_inner 完成。

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

ThreadRequestProcessor::thread_decrement_elicitation509–516 ↗
async fn thread_decrement_elicitation(
        &self,
        params: ThreadDecrementElicitationParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:减少“外部等待确认”计数,可能让暂停的对话恢复。

数据流:输入线程 ID → 找到对话并把计数减一 → 输出新计数和是否仍暂停。

调用关系:handle_initialized_client_request 调用它,实际工作交给 thread_decrement_elicitation_inner。

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

ThreadRequestProcessor::thread_set_name518–539 ↗
async fn thread_set_name(
        &self,
        request_id: ConnectionRequestId,
        params: ThreadSetNameParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:给对话设置一个显示名称,并把变化通知其他客户端。

数据流:输入请求编号、线程 ID 和名称 → 内部清洗并保存名称 → 发送响应,必要时广播名称更新通知。

调用关系:handle_initialized_client_request 收到改名请求时调用。

调用图:调用 1 个内部函数(thread_set_name_response_inner);被 1 处调用(handle_initialized_client_request);外部调用 2 个(ThreadNameUpdated, clone)。

ThreadRequestProcessor::thread_metadata_update541–548 ↗
async fn thread_metadata_update(
        &self,
        params: ThreadMetadataUpdateParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:更新对话的额外信息,比如 Git 提交、分支和远端地址。

数据流:输入元数据更新参数 → 内部校验并写入存储 → 输出更新后的对话视图。

调用关系:由 handle_initialized_client_request 调用,细节在 thread_metadata_update_response_inner。

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

ThreadRequestProcessor::thread_memory_mode_set550–557 ↗
async fn thread_memory_mode_set(
        &self,
        params: ThreadMemoryModeSetParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:修改某个对话使用记忆的方式。

数据流:输入线程 ID 和记忆模式 → 转换成核心系统认识的值并写入元数据 → 输出空成功响应。

调用关系:handle_initialized_client_request 调用它,内部使用 thread_memory_mode_set_response_inner。

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

ThreadRequestProcessor::memory_reset559–565 ↗
async fn memory_reset(
        &self,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:清空系统记忆数据,相当于把记忆本重置。

数据流:无业务输入 → 内部清数据库记忆记录和磁盘记忆目录 → 输出成功响应或错误。

调用关系:handle_initialized_client_request 收到 memory/reset 时调用。

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

ThreadRequestProcessor::thread_unarchive567–584 ↗
async fn thread_unarchive(
        &self,
        request_id: ConnectionRequestId,
        params: ThreadUnarchiveParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:把已归档对话恢复到普通可见状态,并通知客户端。

数据流:输入请求编号和线程 ID → 内部取消归档并构造对话信息 → 发送响应和 ThreadUnarchived 通知。

调用关系:handle_initialized_client_request 调用,实际读写由 thread_unarchive_inner 完成。

调用图:调用 1 个内部函数(thread_unarchive_inner);被 1 处调用(handle_initialized_client_request);外部调用 2 个(ThreadUnarchived, clone)。

ThreadRequestProcessor::thread_compact_start586–594 ↗
async fn thread_compact_start(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadCompactStartParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:启动对话压缩,把长历史整理得更省上下文。

数据流:输入线程 ID → 找到运行中对话并提交压缩命令 → 输出启动成功响应。

调用关系:客户端发起压缩时由 handle_initialized_client_request 调用。

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

ThreadRequestProcessor::thread_background_terminals_clean596–604 ↗
async fn thread_background_terminals_clean(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadBackgroundTerminalsCleanParams,
    ) -> Result<Option<ClientResponsePayload>

作用:要求某个对话清理后台终端进程记录。

数据流:输入线程 ID → 找到对话并提交清理后台终端命令 → 输出成功响应。

调用关系:handle_initialized_client_request 调用,内部交给核心线程执行。

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

ThreadRequestProcessor::thread_background_terminals_list606–613 ↗
async fn thread_background_terminals_list(
        &self,
        params: ThreadBackgroundTerminalsListParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:列出某个对话里还在记录的后台终端。

数据流:输入线程 ID、游标和数量限制 → 内部读取并分页 → 输出终端列表和下一页游标。

调用关系:handle_initialized_client_request 收到列表请求时调用。

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

ThreadRequestProcessor::thread_background_terminals_terminate615–622 ↗
async fn thread_background_terminals_terminate(
        &self,
        params: ThreadBackgroundTerminalsTerminateParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:终止某个对话里的一个后台终端进程。

数据流:输入线程 ID 和进程 ID 字符串 → 解析进程 ID 并请求线程终止它 → 输出是否真的终止。

调用关系:handle_initialized_client_request 调用,实际动作在运行中的 CodexThread 上完成。

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

ThreadRequestProcessor::thread_rollback624–632 ↗
async fn thread_rollback(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadRollbackParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:让对话回滚若干轮,撤掉最近的交互。

数据流:输入线程 ID 和回滚轮数 → 内部校验并提交回滚命令 → 成功时等待后续事件回复。

调用关系:handle_initialized_client_request 调用,thread_rollback_inner 继续处理。

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

ThreadRequestProcessor::thread_list634–641 ↗
async fn thread_list(
        &self,
        params: ThreadListParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:返回保存过的对话列表。

数据流:输入分页、排序和筛选条件 → 内部查询存储并补上加载状态 → 输出列表响应。

调用关系:客户端打开历史列表时由 handle_initialized_client_request 调用。

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

ThreadRequestProcessor::thread_loaded_list652–659 ↗
async fn thread_loaded_list(
        &self,
        params: ThreadLoadedListParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:列出当前内存里已经加载的对话 ID。

数据流:输入分页参数 → 内部取运行中线程 ID、排序和截页 → 输出 ID 列表。

调用关系:handle_initialized_client_request 调用,用于诊断或客户端同步。

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

ThreadRequestProcessor::thread_read661–668 ↗
async fn thread_read(
        &self,
        params: ThreadReadParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:读取一个对话的完整视图,可选择带上各轮内容。

数据流:输入线程 ID 和是否包含轮次 → 内部从存储和内存合成视图 → 输出 Thread 对象。

调用关系:handle_initialized_client_request 收到 thread/read 时调用。

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

ThreadRequestProcessor::thread_turns_list670–677 ↗
async fn thread_turns_list(
        &self,
        params: ThreadTurnsListParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:分页读取一个对话的轮次列表。

数据流:输入线程 ID、游标、数量、排序和内容详细程度 → 内部重建轮次并分页 → 输出一页轮次。

调用关系:handle_initialized_client_request 调用,适合客户端逐页加载长对话。

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

ThreadRequestProcessor::thread_turns_items_list679–686 ↗
async fn thread_turns_items_list(
        &self,
        _params: ThreadTurnsItemsListParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:占位函数,明确告诉客户端“逐项列出轮次内容”这个接口还没实现。

数据流:输入参数被忽略 → 直接构造 method_not_found 错误 → 不读取也不修改状态。

调用关系:handle_initialized_client_request 如果收到该方法会调用它,但它只返回不支持。

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

ThreadRequestProcessor::thread_shell_command688–696 ↗
async fn thread_shell_command(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadShellCommandParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:在本机环境中为某个对话启动一条用户 shell 命令。

数据流:输入线程 ID 和命令字符串 → 内部校验非空、本机环境可用,再提交命令 → 输出启动成功响应。

调用关系:handle_initialized_client_request 调用,实际提交由 thread_shell_command_inner 完成。

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

ThreadRequestProcessor::thread_approve_guardian_denied_action698–706 ↗
async fn thread_approve_guardian_denied_action(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadApproveGuardianDeniedActionParams,
    ) -> Result<Option<ClientResponseP

作用:允许用户批准一个被 Guardian 拦下的动作。

数据流:输入线程 ID 和被拒事件 JSON → 内部解析事件并提交批准命令 → 输出成功响应。

调用关系:handle_initialized_client_request 调用,交给运行中的对话继续处理。

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

ThreadRequestProcessor::conversation_summary708–715 ↗
async fn conversation_summary(
        &self,
        params: GetConversationSummaryParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:读取某个对话的简短摘要,不加载完整历史。

数据流:输入对话 ID 或 rollout 文件路径 → 内部读取存储里的概要字段 → 输出 ConversationSummary。

调用关系:handle_initialized_client_request 调用,内部走 get_thread_summary_response_inner。

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

ThreadRequestProcessor::load_thread717–732 ↗
async fn load_thread(
        &self,
        thread_id: &str,
    ) -> Result<(ThreadId, Arc<CodexThread>), JSONRPCErrorError>

作用:按字符串形式的线程 ID 找到正在内存里运行的对话。

数据流:输入线程 ID 字符串 → 解析成内部 ID 并向线程管理器查找 → 输出内部 ID 和线程对象;找不到则报错。

调用关系:多个需要操作活跃对话的内部函数都会先调用它,比如压缩、回滚、后台终端和 shell 命令。

调用图:调用 1 个内部函数(from_string);被 9 处调用(thread_approve_guardian_denied_action_inner, thread_background_terminals_clean_inner, thread_background_terminals_list_inner, thread_background_terminals_terminate_inner, thread_compact_start_inner, thread_decrement_elicitation_inner, thread_increment_elicitation_inner, thread_rollback_start, thread_shell_command_inner)。

ThreadRequestProcessor::acquire_thread_list_state_permit733–742 ↗
async fn acquire_thread_list_state_permit(
        &self,
    ) -> Result<SemaphorePermit<'_>, JSONRPCErrorError>

作用:拿一张“列表状态修改通行证”,防止多个任务同时改影响列表的状态。

数据流:无业务输入 → 等待信号量许可(信号量就是限流牌) → 输出许可;拿不到则返回内部错误。

调用关系:归档、恢复、改名、更新元数据等会影响列表的流程会先调用它。

调用图:被 5 处调用(thread_archive_inner, thread_metadata_update_response_inner, thread_resume_inner, thread_set_name_response_inner, thread_unarchive_inner)。

ThreadRequestProcessor::set_app_server_client_info744–761 ↗
async fn set_app_server_client_info(
        thread: &CodexThread,
        app_server_client_name: Option<String>,
        app_server_client_version: Option<String>,
    ) -> Result<(), JSONRPCErrorEr

作用:把客户端名称和版本记录到对话里,并处理 Xcode 26.4 的兼容行为。

数据流:输入线程对象、客户端名和版本 → 判断是否需要自动拒绝某类 MCP 询问 → 写入线程内部信息 → 输出成功或错误。

调用关系:新建、恢复、分叉和重连运行中对话时都会调用它。

调用图:调用 2 个内部函数(xcode_26_4_mcp_elicitations_auto_deny, set_app_server_client_info)。

ThreadRequestProcessor::finalize_thread_teardown763–774 ↗
async fn finalize_thread_teardown(&self, thread_id: ThreadId)

作用:彻底清理一个对话在 app-server 里的残留记录。

数据流:输入线程 ID → 从待卸载集合移除、取消相关请求、删除线程状态和监听登记 → 无返回值。

调用关系:退订、归档移除、连接关闭和恢复时替换旧线程都会用它收尾。

调用图:调用 2 个内部函数(remove_thread_state, remove_thread);被 4 处调用(connection_closed, prepare_thread_for_removal, resume_running_thread, thread_unsubscribe_response_inner);外部调用 1 个(to_string)。

ThreadRequestProcessor::thread_unsubscribe_response_inner776–802 ↗
async fn thread_unsubscribe_response_inner(
        &self,
        params: ThreadUnsubscribeParams,
        connection_id: ConnectionId,
    ) -> Result<ThreadUnsubscribeResponse, JSONRPCErrorError>

作用:执行退订,并告诉客户端结果是已退订、未订阅还是线程未加载。

数据流:输入退订参数和连接 ID → 解析线程 ID,检查线程是否存在,移除连接订阅 → 输出退订状态。

调用关系:thread_unsubscribe 的核心逻辑;线程已不在内存时会顺手调用 finalize_thread_teardown 清理。

调用图:调用 3 个内部函数(finalize_thread_teardown, unsubscribe_connection_from_thread, from_string);被 1 处调用(thread_unsubscribe)。

ThreadRequestProcessor::prepare_thread_for_archive804–806 ↗
async fn prepare_thread_for_archive(&self, thread_id: ThreadId)

作用:归档前把活跃对话停下来并清理现场。

数据流:输入线程 ID → 调用通用移除准备逻辑,操作名标记为 archive → 无直接输出。

调用关系:thread_archive_response 在真正写归档标记前调用它。

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

ThreadRequestProcessor::prepare_thread_for_removal808–825 ↗
async fn prepare_thread_for_removal(&self, thread_id: ThreadId, operation: &str)

作用:从线程管理器移除一个对话,并尽量等它正常关停。

数据流:输入线程 ID 和操作名 → 如果对话活跃就发送关闭并等待,超时或失败会记录日志 → 最后清理 app-server 状态。

调用关系:目前由 prepare_thread_for_archive 调用,也可作为其他删除类操作的通用收尾。

调用图:调用 1 个内部函数(finalize_thread_teardown);被 1 处调用(prepare_thread_for_archive);外部调用 3 个(error!, info!, warn!)。

ThreadRequestProcessor::listener_task_context827–839 ↗
fn listener_task_context(&self) -> ListenerTaskContext

作用:打包启动监听任务所需的一组共享零件。

数据流:读取处理器里的线程管理器、状态管理器、消息发送器、配置等 → 克隆共享引用 → 输出 ListenerTaskContext。

调用关系:ensure_conversation_listener 和 ensure_listener_task_running 都靠它把上下文交给 thread_lifecycle 模块。

调用图:被 2 处调用(ensure_conversation_listener, ensure_listener_task_running);外部调用 3 个(clone, clone, clone)。

ThreadRequestProcessor::ensure_conversation_listener841–854 ↗
async fn ensure_conversation_listener(
        &self,
        conversation_id: ThreadId,
        connection_id: ConnectionId,
        raw_events_enabled: bool,
    ) -> Result<EnsureConversationListen

作用:确保某个连接正在监听某个对话的事件。

数据流:输入线程 ID、连接 ID 和是否启用原始事件 → 构造监听上下文并调用生命周期模块 → 输出监听结果或错误。

调用关系:新建、恢复、分叉和手动补挂监听时会调用它。

调用图:调用 2 个内部函数(ensure_conversation_listener, listener_task_context);被 3 处调用(thread_fork_inner, thread_resume_inner, try_attach_thread_listener)。

ThreadRequestProcessor::ensure_listener_task_running856–869 ↗
async fn ensure_listener_task_running(
        &self,
        conversation_id: ThreadId,
        conversation: Arc<CodexThread>,
        thread_state: Arc<Mutex<ThreadState>>,
    ) -> Result<(), JSON

作用:确保某个对话的后台监听任务还活着。

数据流:输入线程 ID、线程对象和线程状态 → 交给 thread_lifecycle 模块检查或启动任务 → 输出成功或错误。

调用关系:resume_running_thread 处理已经运行的对话重连时会调用它。

调用图:调用 2 个内部函数(ensure_listener_task_running, listener_task_context);被 1 处调用(resume_running_thread)。

ThreadRequestProcessor::thread_start_inner871–967 ↗
async fn thread_start_inner(
        &self,
        request_id: ConnectionRequestId,
        params: ThreadStartParams,
        app_server_client_name: Option<String>,
        app_server_client_versio

作用:准备新建对话所需的参数,并把耗时启动放到后台任务里。

数据流:输入启动请求的所有字段 → 校验权限参数、解析环境、构造配置覆盖项和监听上下文 → 启动 thread_start_task,立即返回成功。

调用关系:thread_start 调用它;真正创建对话的工作由后台的 thread_start_task 完成,失败会主动发错误响应。

调用图:调用 3 个内部函数(request_trace, span, build_thread_config_overrides);被 1 处调用(thread_start);外部调用 7 个(clone, thread_start_task, spawn, clone, clone, clone, clone)。

ThreadRequestProcessor::drain_background_tasks969–977 ↗
async fn drain_background_tasks(&self)

作用:关闭时等待本处理器启动的后台任务结束。

数据流:无业务输入 → 关闭任务追踪器并最多等 10 秒 → 超时只记录警告。

调用关系:服务器 teardown 阶段调用,避免新建对话后台任务半路被丢。

调用图:被 1 处调用(drain_background_tasks);外部调用 5 个(from_secs, close, wait, timeout, warn!)。

ThreadRequestProcessor::clear_all_thread_listeners979–981 ↗
async fn clear_all_thread_listeners(&self)

作用:清空所有对话监听记录。

数据流:无输入 → 调用线程状态管理器清除监听者 → 无返回业务数据。

调用关系:连接或服务整体清理时由外层清理流程调用。

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

ThreadRequestProcessor::shutdown_threads983–994 ↗
async fn shutdown_threads(&self)

作用:服务关闭时尽量关掉所有正在运行的对话。

数据流:无输入 → 要求线程管理器在 10 秒内关闭全部线程 → 对提交失败或超时的线程写警告日志。

调用关系:服务器退出流程调用它做善后。

调用图:被 1 处调用(shutdown_threads);外部调用 2 个(from_secs, warn!)。

ThreadRequestProcessor::request_trace_context996–1001 ↗
async fn request_trace_context(
        &self,
        request_id: &ConnectionRequestId,
    ) -> Option<codex_protocol::protocol::W3cTraceContext>

作用:取出某个请求关联的追踪上下文,方便跨模块串起日志和性能追踪。

数据流:输入请求 ID → 从 outgoing 查询保存的 W3C trace context(标准追踪信息) → 输出可选追踪上下文。

调用关系:submit_core_op、恢复和分叉线程时会调用它,把追踪信息传给核心层。

调用图:被 3 处调用(submit_core_op, thread_fork_inner, thread_resume_inner)。

ThreadRequestProcessor::submit_core_op1003–1012 ↗
async fn submit_core_op(
        &self,
        request_id: &ConnectionRequestId,
        thread: &CodexThread,
        op: Op,
    ) -> CodexResult<String>

作用:把一个操作命令提交给核心对话,并带上追踪信息。

数据流:输入请求 ID、线程对象和 Op 命令 → 读取追踪上下文并调用 submit_with_trace → 输出核心层返回的操作 ID 或错误。

调用关系:回滚、压缩、清理终端、shell 命令和批准 Guardian 动作都通过它提交。

调用图:调用 2 个内部函数(request_trace_context, submit_with_trace);被 5 处调用(thread_approve_guardian_denied_action_inner, thread_background_terminals_clean_inner, thread_compact_start_inner, thread_rollback_start, thread_shell_command_inner)。

ThreadRequestProcessor::thread_start_task1015–1275 ↗
async fn thread_start_task(
        listener_task_context: ListenerTaskContext,
        config_manager: ConfigManager,
        request_id: ConnectionRequestId,
        app_server_client_name: Option<S

作用:真正创建新对话、装配配置、工具、监听器,并给客户端发启动响应。

数据流:输入监听上下文、配置覆盖、动态工具、客户端信息、来源和追踪信息 → 加载配置、处理项目信任、校验工具、启动核心线程、挂监听、生成 API 响应和通知 → 发送 ThreadStartResponse 和 ThreadStarted 通知。

调用关系:thread_start_inner 把它作为后台任务启动;它调用 validate_dynamic_tools、build_thread_from_snapshot、ensure_conversation_listener 等完成完整启动流程。

调用图:调用 10 个内部函数(current_cli_overrides, load_with_cli_overrides, load_with_overrides, ensure_conversation_listener, build_thread_from_snapshot, permission_profile_trusts_project, requested_permissions_trust_project, validate_dynamic_tools, set_project_trust_level, new);外部调用 12 个(set_app_server_client_info, ThreadStarted, String, Table, clone, initialize_executor_plugin_thread_data, clone, once, now, new (+2 more))。

ThreadRequestProcessor::build_thread_config_overrides1278–1312 ↗
fn build_thread_config_overrides(
        &self,
        model: Option<String>,
        model_provider: Option<String>,
        service_tier: Option<Option<String>>,
        cwd: Option<String>,

作用:把请求里的模型、目录、权限、说明文字等转换成核心配置覆盖对象。

数据流:输入一组可选请求字段 → 转换枚举和值,并补上沙盒可执行文件路径 → 输出 ConfigOverrides。

调用关系:新建、恢复和分叉对话都会先用它整理配置。

调用图:被 3 处调用(thread_fork_inner, thread_resume_inner, thread_start_inner);外部调用 1 个(default)。

ThreadRequestProcessor::thread_archive_inner1314–1320 ↗
async fn thread_archive_inner(
        &self,
        params: ThreadArchiveParams,
    ) -> Result<(ThreadArchiveResponse, Vec<String>), JSONRPCErrorError>

作用:给归档操作加上列表状态锁,再执行归档。

数据流:输入归档参数 → 先获取列表状态许可 → 调用 thread_archive_response → 输出归档响应和被归档 ID 列表。

调用关系:thread_archive 调用它,防止归档和列表/恢复等操作互相打架。

调用图:调用 2 个内部函数(acquire_thread_list_state_permit, thread_archive_response);被 1 处调用(thread_archive)。

ThreadRequestProcessor::thread_archive_response1322–1412 ↗
async fn thread_archive_response(
        &self,
        params: ThreadArchiveParams,
    ) -> Result<(ThreadArchiveResponse, Vec<String>), JSONRPCErrorError>

作用:执行真正的归档,包括一起处理派生出来的子对话。

数据流:输入线程 ID → 查找该线程及其 spawn 子树,跳过已归档项,逐个停掉活跃线程并写归档标记 → 输出响应和成功归档的 ID。

调用关系:thread_archive_inner 调用它;它会用 state_db_spawn_subtree_thread_ids 找子对话,用 prepare_thread_for_archive 停线程。

调用图:调用 4 个内部函数(prepare_thread_for_archive, state_db_spawn_subtree_thread_ids, thread_store_archive_error, from_string);被 1 处调用(thread_archive_inner);外部调用 2 个(new, warn!)。

ThreadRequestProcessor::state_db_spawn_subtree_thread_ids1414–1437 ↗
async fn state_db_spawn_subtree_thread_ids(
        &self,
        thread_id: ThreadId,
    ) -> Result<Vec<ThreadId>, JSONRPCErrorError>

作用:找出某个对话通过 spawn 派生出的后代对话 ID。

数据流:输入根线程 ID → 如果有状态数据库就查询后代并去重,否则只返回根 ID → 输出 ID 列表。

调用关系:thread_archive_response 用它决定归档一个父对话时还要考虑哪些子对话。

调用图:被 1 处调用(thread_archive_response);外部调用 2 个(from, vec!)。

ThreadRequestProcessor::thread_increment_elicitation_inner1439–1456 ↗
async fn thread_increment_elicitation_inner(
        &self,
        params: ThreadIncrementElicitationParams,
    ) -> Result<ThreadIncrementElicitationResponse, JSONRPCErrorError>

作用:给运行中的对话增加一个外部询问挂起计数。

数据流:输入线程 ID → load_thread 找到对话,调用线程计数加一 → 输出新计数和 paused 状态。

调用关系:thread_increment_elicitation 调用它。

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

ThreadRequestProcessor::thread_decrement_elicitation_inner1458–1476 ↗
async fn thread_decrement_elicitation_inner(
        &self,
        params: ThreadDecrementElicitationParams,
    ) -> Result<ThreadDecrementElicitationResponse, JSONRPCErrorError>

作用:给运行中的对话减少一个外部询问挂起计数。

数据流:输入线程 ID → load_thread 找到对话,调用线程计数减一 → 输出新计数和 paused 状态;非法减少会变成客户端错误。

调用关系:thread_decrement_elicitation 调用它。

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

ThreadRequestProcessor::thread_set_name_response_inner1478–1510 ↗
async fn thread_set_name_response_inner(
        &self,
        params: ThreadSetNameParams,
    ) -> Result<(ThreadSetNameResponse, Option<ThreadNameUpdatedNotification>), JSONRPCErrorError>

作用:校验并保存对话名称。

数据流:输入线程 ID 和名称 → 解析 ID、规范化名称、拿列表状态许可、写元数据补丁 → 输出空响应和名称更新通知内容。

调用关系:thread_set_name 调用它,并负责把通知发出去。

调用图:调用 3 个内部函数(acquire_thread_list_state_permit, normalize_thread_name, from_string);被 1 处调用(thread_set_name);外部调用 1 个(default)。

ThreadRequestProcessor::thread_memory_mode_set_response_inner1512–1533 ↗
async fn thread_memory_mode_set_response_inner(
        &self,
        params: ThreadMemoryModeSetParams,
    ) -> Result<ThreadMemoryModeSetResponse, JSONRPCErrorError>

作用:把对话记忆模式写入元数据。

数据流:输入线程 ID 和 API 层记忆模式 → 转成核心模式后更新线程元数据 → 输出空成功响应。

调用关系:thread_memory_mode_set 的内部实现。

调用图:调用 1 个内部函数(from_string);被 1 处调用(thread_memory_mode_set);外部调用 1 个(default)。

ThreadRequestProcessor::memory_reset_response_inner1535–1559 ↗
async fn memory_reset_response_inner(&self) -> Result<MemoryResetResponse, JSONRPCErrorError>

作用:执行记忆重置的实际清理动作。

数据流:读取状态数据库句柄和 codex_home 路径 → 清空数据库里的 memory 行,再删除记忆目录内容 → 输出空响应。

调用关系:memory_reset 调用它;如果状态库不可用或磁盘清理失败会返回内部错误。

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

ThreadRequestProcessor::thread_metadata_update_response_inner1561–1624 ↗
async fn thread_metadata_update_response_inner(
        &self,
        params: ThreadMetadataUpdateParams,
    ) -> Result<ThreadMetadataUpdateResponse, JSONRPCErrorError>

作用:更新对话的 Git 信息并返回最新对话视图。

数据流:输入线程 ID 和 gitInfo → 校验至少有字段、清洗空白值、写入元数据 → 重新构造 Thread,补名称和加载状态 → 输出更新后的 Thread。

调用关系:thread_metadata_update 调用它;它使用 normalize_thread_metadata_git_field、thread_from_stored_thread 和 attach_thread_name。

调用图:调用 5 个内部函数(acquire_thread_list_state_permit, attach_thread_name, thread_from_stored_thread, loaded_status_for_thread, from_string);被 1 处调用(thread_metadata_update);外部调用 2 个(default, normalize_thread_metadata_git_field)。

ThreadRequestProcessor::normalize_thread_metadata_git_field1626–1641 ↗
fn normalize_thread_metadata_git_field(
        value: Option<Option<String>>,
        name: &str,
    ) -> Result<Option<Option<String>>, JSONRPCErrorError>

作用:清洗一个 Git 元数据字段,避免保存空字符串。

数据流:输入可选的可清空字符串和字段名 → 有字符串就 trim,空则报错;显式 null 表示清空 → 输出补丁格式的字段值。

调用关系:thread_metadata_update_response_inner 用它处理 sha、branch、originUrl 三个字段。

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

ThreadRequestProcessor::thread_unarchive_inner1643–1650 ↗
async fn thread_unarchive_inner(
        &self,
        params: ThreadUnarchiveParams,
    ) -> Result<(ThreadUnarchiveResponse, ThreadUnarchivedNotification), JSONRPCErrorError>

作用:给取消归档操作加锁,并包装通知内容。

数据流:输入取消归档参数 → 获取列表状态许可,调用 thread_unarchive_response → 输出响应和 ThreadUnarchived 通知。

调用关系:thread_unarchive 调用它并负责发送响应和通知。

调用图:调用 2 个内部函数(acquire_thread_list_state_permit, thread_unarchive_response);被 1 处调用(thread_unarchive)。

ThreadRequestProcessor::thread_unarchive_response1652–1677 ↗
async fn thread_unarchive_response(
        &self,
        params: ThreadUnarchiveParams,
    ) -> Result<(ThreadUnarchiveResponse, String), JSONRPCErrorError>

作用:把一个归档对话恢复成未归档,并构造客户端可看的对话对象。

数据流:输入线程 ID → 存储层取消归档,转换为 API Thread,补加载状态和名称 → 输出响应和线程 ID 字符串。

调用关系:thread_unarchive_inner 调用它。

调用图:调用 4 个内部函数(attach_thread_name, thread_from_stored_thread, loaded_status_for_thread, from_string);被 1 处调用(thread_unarchive_inner)。

ThreadRequestProcessor::thread_rollback_inner1679–1685 ↗
async fn thread_rollback_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadRollbackParams,
    ) -> Result<(), JSONRPCErrorError>

作用:回滚请求的薄包装。

数据流:输入请求 ID 和回滚参数 → 直接转给 thread_rollback_start → 输出成功或错误。

调用关系:thread_rollback 调用它。

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

ThreadRequestProcessor::thread_rollback_start1687–1737 ↗
async fn thread_rollback_start(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadRollbackParams,
    ) -> Result<(), JSONRPCErrorError>

作用:启动一次对话回滚,并防止同一对话同时回滚两次。

数据流:输入线程 ID 和回滚轮数 → 校验轮数、找到线程、在状态里登记待处理请求、提交 ThreadRollback 命令 → 成功无立即响应载荷;失败会清掉登记。

调用关系:thread_rollback_inner 调用它;后续回滚结果会由监听事件流程完成响应。

调用图:调用 3 个内部函数(load_thread, submit_core_op, thread_state);被 1 处调用(thread_rollback_inner);外部调用 2 个(clone, format!)。

ThreadRequestProcessor::thread_compact_start_inner1739–1751 ↗
async fn thread_compact_start_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadCompactStartParams,
    ) -> Result<ThreadCompactStartResponse, JSONRPCErrorError>

作用:向运行中对话提交压缩命令。

数据流:输入线程 ID → load_thread 找到线程,submit_core_op 提交 Compact → 输出空成功响应。

调用关系:thread_compact_start 调用它。

调用图:调用 2 个内部函数(load_thread, submit_core_op);被 1 处调用(thread_compact_start)。

ThreadRequestProcessor::thread_background_terminals_clean_inner1753–1767 ↗
async fn thread_background_terminals_clean_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadBackgroundTerminalsCleanParams,
    ) -> Result<ThreadBackgroundTermina

作用:向运行中对话提交清理后台终端命令。

数据流:输入线程 ID → 找到线程并提交 CleanBackgroundTerminals → 输出空成功响应。

调用关系:thread_background_terminals_clean 调用它。

调用图:调用 2 个内部函数(load_thread, submit_core_op);被 1 处调用(thread_background_terminals_clean)。

ThreadRequestProcessor::thread_background_terminals_list_inner1769–1798 ↗
async fn thread_background_terminals_list_inner(
        &self,
        params: ThreadBackgroundTerminalsListParams,
    ) -> Result<ThreadBackgroundTerminalsListResponse, JSONRPCErrorError>

作用:读取并分页返回后台终端列表。

数据流:输入线程 ID、游标和限制 → 找到线程,读取后台终端,转换成 API 结构,再分页 → 输出数据和下一页游标。

调用关系:thread_background_terminals_list 调用它,分页由 paginate_background_terminals 完成。

调用图:调用 2 个内部函数(load_thread, paginate_background_terminals);被 1 处调用(thread_background_terminals_list)。

ThreadRequestProcessor::thread_background_terminals_terminate_inner1800–1815 ↗
async fn thread_background_terminals_terminate_inner(
        &self,
        params: ThreadBackgroundTerminalsTerminateParams,
    ) -> Result<ThreadBackgroundTerminalsTerminateResponse, JSONRPCErrorE

作用:终止指定后台终端。

数据流:输入线程 ID 和进程 ID 字符串 → 解析成数字,找到线程,调用终止方法 → 输出 terminated 布尔值。

调用关系:thread_background_terminals_terminate 调用它。

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

ThreadRequestProcessor::thread_shell_command_inner1817–1847 ↗
async fn thread_shell_command_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadShellCommandParams,
    ) -> Result<ThreadShellCommandResponse, JSONRPCErrorError>

作用:校验并提交本机 shell 命令。

数据流:输入线程 ID 和命令 → 去掉首尾空白,检查非空和本机环境可用 → 提交 RunUserShellCommand → 输出空响应。

调用关系:thread_shell_command 调用它;它通过 submit_core_op 把命令交给核心线程。

调用图:调用 2 个内部函数(load_thread, submit_core_op);被 1 处调用(thread_shell_command)。

ThreadRequestProcessor::thread_approve_guardian_denied_action_inner1849–1867 ↗
async fn thread_approve_guardian_denied_action_inner(
        &self,
        request_id: &ConnectionRequestId,
        params: ThreadApproveGuardianDeniedActionParams,
    ) -> Result<ThreadApproveGua

作用:把用户批准的 Guardian 拦截事件交回核心对话。

数据流:输入线程 ID 和事件 JSON → 反序列化事件,找到线程,提交 ApproveGuardianDeniedAction → 输出空响应。

调用关系:thread_approve_guardian_denied_action 调用它。

调用图:调用 2 个内部函数(load_thread, submit_core_op);被 1 处调用(thread_approve_guardian_denied_action);外部调用 1 个(from_value)。

ThreadRequestProcessor::thread_list_response_inner1869–1955 ↗
async fn thread_list_response_inner(
        &self,
        params: ThreadListParams,
    ) -> Result<ThreadListResponse, JSONRPCErrorError>

作用:构造对话列表响应。

数据流:输入分页、排序、模型供应商、来源、目录、搜索词等筛选 → 规范化参数,调用 list_threads_common,转换存储对象并补加载状态 → 输出 ThreadListResponse。

调用关系:thread_list 调用它;公共分页查询由 list_threads_common 完成。

调用图:调用 4 个内部函数(list_threads_common, normalize_thread_list_cwd_filters, thread_from_stored_thread, loaded_statuses_for_threads);被 1 处调用(thread_list);外部调用 1 个(with_capacity)。

ThreadRequestProcessor::thread_search_response_inner1957–2080 ↗
async fn thread_search_response_inner(
        &self,
        params: ThreadSearchParams,
    ) -> Result<ThreadSearchResponse, JSONRPCErrorError>

作用:执行关键词搜索并返回带片段的对话结果。

数据流:输入搜索参数 → 校验搜索词非空,循环查询存储直到凑够一页或没数据,按来源再过滤 → 转成 API 结果并补加载状态 → 输出 ThreadSearchResponse。

调用关系:thread_search 调用它;与列表逻辑类似,但用 search_threads 并保留 snippet。

调用图:调用 2 个内部函数(thread_from_stored_thread, loaded_statuses_for_threads);被 1 处调用(thread_search);外部调用 1 个(with_capacity)。

ThreadRequestProcessor::thread_loaded_list_response_inner2082–2127 ↗
async fn thread_loaded_list_response_inner(
        &self,
        params: ThreadLoadedListParams,
    ) -> Result<ThreadLoadedListResponse, JSONRPCErrorError>

作用:分页列出当前已加载线程 ID。

数据流:输入游标和限制 → 从线程管理器拿 ID,排序,按游标截取一页 → 输出 ID 列表和下一页游标。

调用关系:thread_loaded_list 调用它。

调用图:调用 1 个内部函数(from_string);被 1 处调用(thread_loaded_list);外部调用 1 个(format!)。

ThreadRequestProcessor::thread_read_response_inner2129–2146 ↗
async fn thread_read_response_inner(
        &self,
        params: ThreadReadParams,
    ) -> Result<ThreadReadResponse, JSONRPCErrorError>

作用:处理 thread/read 的参数解析和错误转换。

数据流:输入线程 ID 和 include_turns → 解析 ID,调用 read_thread_view → 输出 ThreadReadResponse。

调用关系:thread_read 调用它;真正合成视图在 read_thread_view。

调用图:调用 2 个内部函数(read_thread_view, from_string);被 1 处调用(thread_read)。

ThreadRequestProcessor::read_thread_view2149–2220 ↗
async fn read_thread_view(
        &self,
        thread_id: ThreadId,
        include_turns: bool,
    ) -> Result<Thread, ThreadReadViewError>

作用:把磁盘保存的对话和内存里的活跃状态合成一个客户端视图。

数据流:输入线程 ID 和是否包含轮次 → 根据线程是否加载、是否需要轮次,选择读存储或读活线程;最后设置状态并中断过期的进行中轮次 → 输出 Thread。

调用关系:thread_read_response_inner 调用它;它再调用 load_persisted_thread_for_read 或 load_live_thread_view。

调用图:调用 3 个内部函数(load_live_thread_view, load_persisted_thread_for_read, loaded_status_for_thread);被 1 处调用(thread_read_response_inner);外部调用 3 个(InvalidRequest, format!, matches!)。

ThreadRequestProcessor::load_persisted_thread_for_read2222–2260 ↗
async fn load_persisted_thread_for_read(
        &self,
        thread_id: ThreadId,
        include_turns: bool,
    ) -> Result<Option<Thread>, ThreadReadViewError>

作用:从持久化存储读取对话,必要时带历史轮次。

数据流:输入线程 ID 和 include_turns → 调 thread_store 读取,转换成 API Thread;带历史时重建 turns → 输出 Some(Thread)、None 或读取错误。

调用关系:read_thread_view 用它优先读取已经落盘的对话。

调用图:调用 1 个内部函数(thread_from_stored_thread);被 1 处调用(read_thread_view);外部调用 3 个(Internal, InvalidRequest, format!)。

ThreadRequestProcessor::load_live_thread_view2263–2291 ↗
async fn load_live_thread_view(
        &self,
        thread_id: ThreadId,
        include_turns: bool,
        loaded_thread: &CodexThread,
        persisted_thread: Option<Thread>,
    ) -> Result<

作用:从正在运行的对话构造 thread/read 视图。

数据流:输入线程 ID、是否包含轮次、活线程和可选持久化元数据 → 读取配置快照,处理 ephemeral 不能读历史的限制,合并持久化字段和活状态 → 输出 Thread。

调用关系:read_thread_view 在对话还没完整落盘或需要最新状态时调用它。

调用图:调用 3 个内部函数(apply_thread_read_store_fields, build_thread_from_loaded_snapshot, config_snapshot);被 1 处调用(read_thread_view);外部调用 1 个(InvalidRequest)。

ThreadRequestProcessor::apply_thread_read_store_fields2293–2311 ↗
async fn apply_thread_read_store_fields(
        &self,
        thread_id: ThreadId,
        thread: &mut Thread,
        include_turns: bool,
        loaded_thread: &CodexThread,
    ) -> Result<(),

作用:给 thread/read 的结果补上存储层字段,比如名称和历史轮次。

数据流:输入线程 ID、可修改 Thread、是否包含轮次和活线程 → 补名称;如果要轮次,就从活线程加载历史并构建 turns → 修改传入 Thread。

调用关系:load_live_thread_view 调用它做最后补全。

调用图:调用 2 个内部函数(attach_thread_name, load_history);被 1 处调用(load_live_thread_view)。

ThreadRequestProcessor::thread_turns_list_response_inner2313–2365 ↗
async fn thread_turns_list_response_inner(
        &self,
        params: ThreadTurnsListParams,
    ) -> Result<ThreadTurnsListResponse, JSONRPCErrorError>

作用:分页返回某个对话的轮次,并合并当前正在进行的轮次快照。

数据流:输入线程 ID、分页和显示选项 → 加载历史,查看是否有活线程和 active turn,构建分页响应 → 输出 ThreadTurnsListResponse。

调用关系:thread_turns_list 调用它;最终分页由 build_thread_turns_page_response 完成。

调用图:调用 5 个内部函数(load_thread_turns_list_history, build_thread_turns_page_response, thread_state, loaded_status_for_thread, from_string);被 1 处调用(thread_turns_list);外部调用 1 个(matches!)。

ThreadRequestProcessor::load_thread_turns_list_history2367–2422 ↗
async fn load_thread_turns_list_history(
        &self,
        thread_id: ThreadId,
    ) -> Result<Vec<RolloutItem>, ThreadReadViewError>

作用:为 thread/turns/list 读取原始历史条目。

数据流:输入线程 ID → 优先从存储读取 rollout 历史;如果没有但线程在运行,则从活线程加载;ephemeral 对话拒绝读取 → 输出 RolloutItem 列表。

调用关系:thread_turns_list_response_inner 调用它。

调用图:被 1 处调用(thread_turns_list_response_inner);外部调用 3 个(Internal, InvalidRequest, format!)。

ThreadRequestProcessor::thread_created_receiver2424–2426 ↗
fn thread_created_receiver(&self) -> broadcast::Receiver<ThreadId>

作用:提供一个接收“新线程创建”广播的通道。

数据流:无输入 → 向线程管理器订阅创建事件 → 输出 broadcast receiver。

调用关系:外层需要监听新对话创建时调用。

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

ThreadRequestProcessor::connection_initialized2428–2436 ↗
async fn connection_initialized(
        &self,
        connection_id: ConnectionId,
        capabilities: ConnectionCapabilities,
    )

作用:记录一个客户端连接已经初始化,以及它支持哪些能力。

数据流:输入连接 ID 和能力列表 → 写入线程状态管理器 → 无响应载荷。

调用关系:连接初始化流程和 handle_client_request 会调用它。

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

ThreadRequestProcessor::connection_closed2438–2451 ↗
async fn connection_closed(&self, connection_id: ConnectionId)

作用:客户端连接断开时,清理它订阅过的对话。

数据流:输入连接 ID → 从状态管理器移除该连接,拿到受影响线程 → 如果线程已经不存在,就 finalize 清理残留状态。

调用关系:连接关闭流程调用它。

调用图:调用 2 个内部函数(finalize_thread_teardown, remove_connection);被 1 处调用(connection_closed)。

ThreadRequestProcessor::subscribe_running_assistant_turn_count2453–2455 ↗
fn subscribe_running_assistant_turn_count(&self) -> watch::Receiver<usize>

作用:订阅当前正在运行的助手轮次数量。

数据流:无输入 → 从 thread_watch_manager 获取 watch receiver(会推送最新值的通道) → 输出接收器。

调用关系:外层状态栏或监控模块会调用它。

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

ThreadRequestProcessor::try_attach_thread_listener2458–2493 ↗
async fn try_attach_thread_listener(
        &self,
        thread_id: ThreadId,
        connection_ids: Vec<ConnectionId>,
    )

作用:尽力把一批已初始化连接挂到某个对话监听上。

数据流:输入线程 ID 和连接 ID 列表 → 如果线程已加载就更新观察管理器并继承父线程 raw events 设置 → 逐个连接尝试挂监听并记录结果。

调用关系:外部需要补订阅线程事件时调用;内部用 ensure_conversation_listener。

调用图:调用 4 个内部函数(ensure_conversation_listener, build_thread_from_snapshot, thread_state, upsert_thread);被 1 处调用(try_attach_thread_listener)。

ThreadRequestProcessor::thread_resume_inner2495–2805 ↗
async fn thread_resume_inner(
        &self,
        request_id: ConnectionRequestId,
        params: ThreadResumeParams,
        app_server_client_name: Option<String>,
        app_server_client_vers

作用:恢复对话的主流程,既处理已运行对话重连,也处理冷启动恢复。

数据流:输入请求、恢复参数和客户端信息 → 检查卸载中和权限冲突,尝试接回运行中线程;否则读历史、合并旧配置、加载配置、恢复核心线程、挂监听、构造响应和可选初始轮次页 → 发送响应、用量更新和目标状态。

调用关系:thread_resume 调用它;它串起 resume_running_thread、resume_thread_from_rollout、load_and_apply_persisted_resume_metadata、load_thread_from_resume_source_or_send_internal 等多个步骤。

调用图:调用 16 个内部函数(load_for_cwd, emit_resume_goal_snapshot_and_continue, acquire_thread_list_state_permit, build_thread_config_overrides, ensure_conversation_listener, load_and_apply_persisted_resume_metadata, load_thread_from_resume_source_or_send_internal, request_trace_context, resume_running_thread, resume_thread_from_history (+6 more));被 1 处调用(thread_resume);外部调用 2 个(set_app_server_client_info, format!)。

ThreadRequestProcessor::load_and_apply_persisted_resume_metadata2807–2824 ↗
async fn load_and_apply_persisted_resume_metadata(
        &self,
        thread_history: &InitialHistory,
        request_overrides: &mut Option<HashMap<String, serde_json::Value>>,
        typesafe_

作用:恢复旧对话时读取数据库里的旧模型元数据并应用到配置。

数据流:输入 InitialHistory、请求配置覆盖和类型化覆盖 → 如果是 Resumed 历史且数据库有元数据,就调用 merge_persisted_resume_metadata → 返回读到的元数据或 None。

调用关系:thread_resume_inner 在加载配置前调用它,避免恢复时意外换模型。

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

ThreadRequestProcessor::resume_running_thread2827–3011 ↗
async fn resume_running_thread(
        &self,
        request_id: &ConnectionRequestId,
        params: &ThreadResumeParams,
        app_server_client_name: Option<String>,
        app_server_client_

作用:处理“要恢复的对话其实已经在内存里运行”的情况。

数据流:输入请求 ID、恢复参数和客户端信息 → 查运行中线程与存储历史,检查路径和配置差异;能直接重连就让监听器发送恢复响应,必要时关闭无订阅的空闲缓存线程 → 输出已处理或未运行结果。

调用关系:thread_resume_inner 首先调用它;它用 collect_resume_override_mismatches、ensure_listener_task_running、stored_thread_to_api_thread 等辅助。

调用图:调用 10 个内部函数(pending_resume_goal_state, ensure_listener_task_running, finalize_thread_teardown, read_stored_thread_for_resume, stored_thread_to_api_thread, collect_resume_override_mismatches, subscribed_connection_ids, thread_state, loaded_status_for_thread, from_string);被 1 处调用(thread_resume_inner);外部调用 10 个(new, set_app_server_client_info, clone, NotRunning, SendThreadResumeResponse, format!, matches!, paths_match_after_normalization, warn!, warn!)。

ThreadRequestProcessor::resume_thread_from_history3014–3028 ↗
async fn resume_thread_from_history(
        &self,
        history: &[ResponseItem],
    ) -> Result<InitialHistory, JSONRPCErrorError>

作用:把客户端直接传来的历史数组转换成可恢复的初始历史。

数据流:输入 ResponseItem 列表 → 校验非空,把每项包成 RolloutItem → 输出 InitialHistory::Forked。

调用关系:thread_resume_inner 在请求自带 history 时调用它。

调用图:被 1 处调用(thread_resume_inner);外部调用 3 个(is_empty, iter, Forked)。

ThreadRequestProcessor::resume_thread_from_rollout3031–3043 ↗
async fn resume_thread_from_rollout(
        &self,
        thread_id: &str,
        path: Option<&PathBuf>,
    ) -> Result<(InitialHistory, StoredThread), JSONRPCErrorError>

作用:从已保存的 rollout 历史文件恢复对话。

数据流:输入线程 ID 字符串和可选路径 → 读取 StoredThread,再转换成 InitialHistory → 输出历史和源 StoredThread。

调用关系:thread_resume_inner 冷恢复且没有直接 history 时调用它。

调用图:调用 2 个内部函数(read_stored_thread_for_resume, stored_thread_to_initial_history);被 1 处调用(thread_resume_inner)。

ThreadRequestProcessor::read_stored_thread_for_resume3045–3083 ↗
async fn read_stored_thread_for_resume(
        &self,
        thread_id: &str,
        path: Option<&PathBuf>,
        include_history: bool,
    ) -> Result<StoredThread, JSONRPCErrorError>

作用:按线程 ID 或 rollout 路径读取用于恢复/分叉的已保存对话。

数据流:输入线程 ID、可选路径和是否带历史 → 从 thread_store 读取,拒绝已归档对话 → 输出 StoredThread。

调用关系:resume_running_thread、resume_thread_from_rollout 和 thread_fork_inner 都用它。

调用图:调用 1 个内部函数(from_string);被 3 处调用(resume_running_thread, resume_thread_from_rollout, thread_fork_inner);外部调用 1 个(format!)。

ThreadRequestProcessor::stored_thread_to_initial_history3086–3105 ↗
async fn stored_thread_to_initial_history(
        &self,
        stored_thread: &StoredThread,
    ) -> Result<InitialHistory, JSONRPCErrorError>

作用:把已保存对话里的历史包装成核心层可恢复的 InitialHistory。

数据流:输入 StoredThread → 取出 history items、线程 ID 和 rollout 路径 → 输出 InitialHistory::Resumed;没有历史则报内部错误。

调用关系:resume_thread_from_rollout 和 thread_resume_inner 用它复用存储历史。

调用图:被 2 处调用(resume_thread_from_rollout, thread_resume_inner);外部调用 1 个(Resumed)。

ThreadRequestProcessor::stored_thread_to_api_thread3107–3123 ↗
fn stored_thread_to_api_thread(
        &self,
        stored_thread: StoredThread,
        fallback_provider: &str,
        include_turns: bool,
    ) -> Thread

作用:把存储层的 StoredThread 转成客户端 API 里的 Thread。

数据流:输入 StoredThread、备用模型供应商和是否包含轮次 → 调 thread_from_stored_thread 转基础字段,必要时从历史填 turns → 输出 Thread。

调用关系:resume_running_thread 和 thread_fork_inner 用它构造响应里的对话摘要。

调用图:调用 1 个内部函数(thread_from_stored_thread);被 2 处调用(resume_running_thread, thread_fork_inner)。

ThreadRequestProcessor::read_stored_thread_for_new_fork3125–3138 ↗
async fn read_stored_thread_for_new_fork(
        &self,
        thread_id: ThreadId,
        include_history: bool,
    ) -> Result<StoredThread, JSONRPCErrorError>

作用:读取刚创建出来的分叉对话的存储记录。

数据流:输入新线程 ID 和是否带历史 → 从 thread_store 读取,包括已归档也允许 → 输出 StoredThread 或恢复读取错误。

调用关系:thread_fork_inner 在持久化分叉创建后调用它来构造响应。

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

ThreadRequestProcessor::load_thread_from_resume_source_or_send_internal3140–3237 ↗
async fn load_thread_from_resume_source_or_send_internal(
        &self,
        thread_id: ThreadId,
        thread: &CodexThread,
        thread_history: &InitialHistory,
        rollout_path: &Path

作用:恢复成功后,构造要发给客户端的 Thread 对象。

数据流:输入新线程 ID、活线程、初始历史、rollout 路径、可选源存储线程和是否带轮次 → 根据是 Resumed 还是 Forked 读取或生成基础 Thread,修正 ID、session、路径,填 turns 和名称 → 输出 Thread 或错误文字。

调用关系:thread_resume_inner 恢复核心线程成功后调用它。

调用图:调用 7 个内部函数(attach_thread_name, build_thread_from_snapshot, preview_from_rollout_items, thread_from_stored_thread, config_snapshot, session_configured, get_rollout_items);被 1 处调用(thread_resume_inner);外部调用 4 个(into, to_path_buf, format!, to_string)。

ThreadRequestProcessor::attach_thread_name3239–3254 ↗
async fn attach_thread_name(&self, thread_id: ThreadId, thread: &mut Thread)

作用:从存储里补上用户给对话设置的名称。

数据流:输入线程 ID 和可修改 Thread → 读取存储记录,若 name 非空且不同于预览,就写入 Thread.name → 无返回值。

调用关系:读取、恢复、取消归档和元数据更新构造 Thread 时会调用它。

调用图:调用 1 个内部函数(set_thread_name_from_title);被 4 处调用(apply_thread_read_store_fields, load_thread_from_resume_source_or_send_internal, thread_metadata_update_response_inner, thread_unarchive_response)。

ThreadRequestProcessor::thread_fork_inner3256–3519 ↗
async fn thread_fork_inner(
        &self,
        request_id: ConnectionRequestId,
        params: ThreadForkParams,
        app_server_client_name: Option<String>,
        app_server_client_version:

作用:从已有对话历史创建一个新分支对话。

数据流:输入源线程、配置覆盖和客户端信息 → 读取源历史,加载新配置,调用核心分叉,继承名称,挂监听,构造响应和通知,可选发送历史用量更新 → 发送 ThreadForkResponse 和 ThreadStarted 通知。

调用关系:thread_fork 调用它;它复用 read_stored_thread_for_resume、build_thread_config_overrides、stored_thread_to_api_thread 等。

调用图:调用 12 个内部函数(load_for_cwd, build_thread_config_overrides, ensure_conversation_listener, read_stored_thread_for_new_fork, read_stored_thread_for_resume, request_trace_context, stored_thread_to_api_thread, build_thread_from_snapshot, preview_from_rollout_items, set_thread_name_from_title (+2 more));被 1 处调用(thread_fork);外部调用 7 个(default, set_app_server_client_info, ThreadStarted, cfg!, from_config, Resumed, json!)。

ThreadRequestProcessor::get_thread_summary_response_inner3521–3561 ↗
async fn get_thread_summary_response_inner(
        &self,
        params: GetConversationSummaryParams,
    ) -> Result<GetConversationSummaryResponse, JSONRPCErrorError>

作用:读取对话摘要的内部实现。

数据流:输入按 ID 或路径查询的参数 → 从 thread_store 或本地存储读取不含历史的 StoredThread → 转成 ConversationSummary → 输出摘要响应。

调用关系:conversation_summary 调用它。

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

ThreadRequestProcessor::list_threads_common3563–3673 ↗
async fn list_threads_common(
        &self,
        requested_page_size: usize,
        cursor: Option<String>,
        sort_key: StoreThreadSortKey,
        sort_direction: SortDirection,
        fi

作用:对话列表查询的公共分页和过滤逻辑。

数据流:输入页大小、游标、排序和过滤器 → 循环调用 thread_store.list_threads,按来源和目录做二次过滤,防止游标重复导致死循环 → 输出 StoredThread 列表和下一页游标。

调用关系:thread_list_response_inner 调用它;搜索接口有自己的相似逻辑。

调用图:被 1 处调用(thread_list_response_inner);外部调用 3 个(new, with_capacity, vec!)。

xcode_26_4_mcp_elicitations_auto_deny3676–3685 ↗
fn xcode_26_4_mcp_elicitations_auto_deny(
    client_name: Option<&str>,
    client_version: Option<&str>,
) -> bool

作用:判断是否要为 Xcode 26.4 自动拒绝 MCP elicitation 请求。

数据流:输入客户端名和版本 → 检查是否正好是 Xcode 且版本以 26.4 开头 → 输出布尔值。

调用关系:set_app_server_client_info 调用它,这是一个兼容旧客户端的临时判断。

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

thread_backwards_cursor_for_sort_key3690–3706 ↗
fn thread_backwards_cursor_for_sort_key(
    thread: &StoredThread,
    sort_key: StoreThreadSortKey,
    sort_direction: SortDirection,
) -> Option<String>

作用:为列表反向翻页生成游标。

数据流:输入存储线程、排序字段和方向 → 取创建或更新时间,并按方向偏移 1 毫秒 → 输出 RFC3339 时间字符串游标。

调用关系:列表和搜索响应用它给客户端提供 backwards_cursor。

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

paginate_thread_turns3721–3798 ↗
fn paginate_thread_turns(
    turns: Vec<Turn>,
    cursor: Option<&str>,
    limit: Option<u32>,
    sort_direction: SortDirection,
) -> Result<ThreadTurnsPage, JSONRPCErrorError>

作用:把一堆对话轮次按游标、限制和排序方向切成一页。

数据流:输入 turns、游标、limit 和排序方向 → 解析锚点,按升序或降序过滤,截取一页,生成前后游标 → 输出 ThreadTurnsPage。

调用关系:build_thread_turns_page_response 调用它。

调用图:被 1 处调用(build_thread_turns_page_response);外部调用 1 个(new)。

serialize_thread_turns_cursor3800–3809 ↗
fn serialize_thread_turns_cursor(
    turn_id: &str,
    include_anchor: bool,
) -> Result<String, JSONRPCErrorError>

作用:把轮次分页游标编码成字符串。

数据流:输入 turn_id 和是否包含锚点 → 序列化成 JSON 字符串 → 输出游标字符串或内部错误。

调用关系:paginate_thread_turns 用它生成 next_cursor 和 backwards_cursor。

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

parse_thread_turns_cursor3811–3813 ↗
fn parse_thread_turns_cursor(cursor: &str) -> Result<ThreadTurnsCursor, JSONRPCErrorError>

作用:把客户端传回的轮次分页游标解析回来。

数据流:输入游标字符串 → 按 JSON 解析成 ThreadTurnsCursor → 输出游标对象;格式错则返回参数错误。

调用关系:paginate_thread_turns 在开始分页时调用它。

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

build_thread_turns_page_response3822–3842 ↗
fn build_thread_turns_page_response(
    items: &[RolloutItem],
    loaded_status: ThreadStatus,
    has_live_running_thread: bool,
    active_turn: Option<Turn>,
    options: ThreadTurnsPageOptions<'

作用:从原始历史构造一页 thread/turns/list 响应。

数据流:输入 rollout items、加载状态、是否有活跃运行、当前活跃轮次和分页选项 → 重建 turns、应用显示粒度、分页 → 输出 ThreadTurnsListResponse。

调用关系:thread_turns_list_response_inner 和 build_thread_resume_initial_turns_page 都用它。

调用图:调用 3 个内部函数(apply_thread_turns_items_view, paginate_thread_turns, reconstruct_thread_turns_for_turns_list);被 2 处调用(thread_turns_list_response_inner, build_thread_resume_initial_turns_page)。

build_thread_resume_initial_turns_page3844–3864 ↗
fn build_thread_resume_initial_turns_page(
    items: &[RolloutItem],
    loaded_status: ThreadStatus,
    has_live_running_thread: bool,
    active_turn: Option<Turn>,
    params: &ThreadResumeInitia

作用:恢复对话时顺手构造首屏轮次页。

数据流:输入历史条目、状态、活跃轮次和客户端请求的分页参数 → 调 build_thread_turns_page_response 从第一页开始分页 → 输出协议里的 TurnsPage。

调用关系:thread_resume_inner 和处理挂起恢复响应的流程会调用它。

调用图:调用 1 个内部函数(build_thread_turns_page_response);被 2 处调用(handle_pending_thread_resume_request, thread_resume_inner)。

apply_thread_turns_items_view3866–3902 ↗
fn apply_thread_turns_items_view(turns: &mut [Turn], items_view: TurnItemsView)

作用:按客户端要求裁剪每轮里的消息内容。

数据流:输入可修改 turns 和 items_view → NotLoaded 清空内容,Summary 只保留首个用户消息和最后助手消息,Full 保留全部 → 原地修改 turns。

调用关系:build_thread_turns_page_response 在分页前调用它,减少网络传输。

调用图:被 1 处调用(build_thread_turns_page_response);外部调用 2 个(new, vec!)。

reconstruct_thread_turns_for_turns_list3904–3920 ↗
fn reconstruct_thread_turns_for_turns_list(
    items: &[RolloutItem],
    loaded_status: ThreadStatus,
    has_live_running_thread: bool,
    active_turn: Option<Turn>,
) -> Vec<Turn>

作用:从原始历史重建轮次,并合并内存里的当前轮次。

数据流:输入历史条目、线程状态、是否运行和可选 active_turn → 生成 turns,修正状态,把活跃轮次合并进去 → 输出完整 turns。

调用关系:build_thread_turns_page_response 调用它。

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

normalize_thread_turns_status3922–3936 ↗
fn normalize_thread_turns_status(
    turns: &mut [Turn],
    loaded_status: ThreadStatus,
    has_live_in_progress_turn: bool,
)

作用:如果线程其实不活跃,把历史里还标着进行中的轮次改成已中断。

数据流:输入可修改 turns、加载状态和是否有实时进行中轮次 → 解析线程最终状态;若不是活跃,就把 InProgress 轮次改为 Interrupted → 原地修改。

调用关系:reconstruct_thread_turns_for_turns_list 调用它,避免客户端看到假“进行中”。

调用图:被 1 处调用(reconstruct_thread_turns_for_turns_list);外部调用 1 个(matches!)。

thread_read_view_error3944–3952 ↗
fn thread_read_view_error(err: ThreadReadViewError) -> JSONRPCErrorError

作用:把 thread/read 内部错误转换成 JSON-RPC 错误。

数据流:输入 ThreadReadViewError → 按无效请求、不支持、内部错误三类映射 → 输出 JSONRPCErrorError。

调用关系:thread_read_response_inner 和 turns 历史读取相关流程用它统一报错口径。

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

unsupported_thread_store_operation3954–3956 ↗
fn unsupported_thread_store_operation(operation: &'static str) -> JSONRPCErrorError

作用:生成“这个存储操作还不支持”的标准错误。

数据流:输入操作名 → 包装成 method_not_found 错误 → 输出 JSON-RPC 错误。

调用关系:多种存储错误转换函数都会调用它。

调用图:调用 1 个内部函数(method_not_found);被 7 处调用(thread_store_delete_error, conversation_summary_rollout_path_read_error, conversation_summary_thread_id_read_error, thread_read_view_error, thread_store_archive_error, thread_store_list_error, thread_store_resume_read_error);外部调用 1 个(format!)。

thread_store_list_error3958–3966 ↗
fn thread_store_list_error(err: ThreadStoreError) -> JSONRPCErrorError

作用:把列对话列表时的存储错误翻译成客户端能懂的错误。

数据流:输入 ThreadStoreError → 无效请求原样给客户端,不支持转 method_not_found,其他转内部错误 → 输出 JSON-RPC 错误。

调用关系:list_threads_common 和搜索列表查询失败时使用。

调用图:调用 1 个内部函数(unsupported_thread_store_operation);外部调用 1 个(format!)。

thread_store_resume_read_error3968–3979 ↗
fn thread_store_resume_read_error(err: ThreadStoreError) -> JSONRPCErrorError

作用:把恢复读取存储时的错误翻译成合适的 JSON-RPC 错误。

数据流:输入 ThreadStoreError → 区分参数错、不支持、线程不存在和其他错误 → 输出客户端错误或内部错误。

调用关系:read_stored_thread_for_resume 和 read_stored_thread_for_new_fork 使用。

调用图:调用 1 个内部函数(unsupported_thread_store_operation);外部调用 1 个(format!)。

thread_turns_list_history_load_error3981–4001 ↗
fn thread_turns_list_history_load_error(
    thread_id: ThreadId,
    err: ThreadStoreError,
) -> ThreadReadViewError

作用:把 thread/turns/list 加载历史失败的原因改写成更友好的说明。

数据流:输入线程 ID 和存储错误 → 对尚未物化的 rollout 给出“第一条用户消息前不可用”的提示,其余按类型转换 → 输出 ThreadReadViewError。

调用关系:load_thread_turns_list_history 从活线程加载历史失败时使用。

调用图:外部调用 4 个(Internal, InvalidRequest, Unsupported, format!)。

thread_read_history_load_error4003–4028 ↗
fn thread_read_history_load_error(
    thread_id: ThreadId,
    err: ThreadStoreError,
) -> ThreadReadViewError

作用:把 thread/read includeTurns 加载历史失败转换成友好错误。

数据流:输入线程 ID 和存储错误 → 对未物化或找不到历史给出 includeTurns 不可用提示,其余映射为内部/无效/不支持 → 输出 ThreadReadViewError。

调用关系:apply_thread_read_store_fields 加载历史失败时调用。

调用图:外部调用 4 个(Internal, InvalidRequest, Unsupported, format!)。

conversation_summary_thread_id_read_error4030–4050 ↗
fn conversation_summary_thread_id_read_error(
    conversation_id: ThreadId,
    err: ThreadStoreError,
) -> JSONRPCErrorError

作用:把按对话 ID 读取摘要失败转换成 JSON-RPC 错误。

数据流:输入对话 ID 和存储错误 → 没有 rollout 或找不到时返回统一 not found,无效请求和不支持分别处理,其他为内部错误 → 输出错误。

调用关系:get_thread_summary_response_inner 按 ID 读摘要失败时使用。

调用图:调用 2 个内部函数(conversation_summary_not_found_error, unsupported_thread_store_operation);外部调用 1 个(format!)。

conversation_summary_not_found_error4052–4056 ↗
fn conversation_summary_not_found_error(conversation_id: ThreadId) -> JSONRPCErrorError

作用:生成“找不到这个对话摘要”的标准错误。

数据流:输入对话 ID → 组成固定错误消息 → 输出 invalid_request 错误。

调用关系:conversation_summary_thread_id_read_error 调用它统一 not found 文案。

调用图:被 1 处调用(conversation_summary_thread_id_read_error);外部调用 1 个(format!)。

conversation_summary_rollout_path_read_error4058–4073 ↗
fn conversation_summary_rollout_path_read_error(
    path: &Path,
    err: ThreadStoreError,
) -> JSONRPCErrorError

作用:把按 rollout 路径读取摘要失败转换成 JSON-RPC 错误。

数据流:输入路径和存储错误 → 参数错直接返回,不支持转 method_not_found,其他带路径包装成内部错误 → 输出错误。

调用关系:get_thread_summary_response_inner 按路径读摘要失败时使用。

调用图:调用 1 个内部函数(unsupported_thread_store_operation);外部调用 1 个(format!)。

core_thread_write_error4075–4084 ↗
fn core_thread_write_error(operation: &str, err: CodexErr) -> JSONRPCErrorError

作用:把核心层写线程元数据失败转换成 API 错误。

数据流:输入操作名和 CodexErr → 线程不存在、无效请求、不支持分别映射,其他包装为内部错误 → 输出 JSON-RPC 错误。

调用关系:改名、设置记忆模式、更新元数据和其他写线程信息的流程会用它。

调用图:调用 1 个内部函数(method_not_found);被 1 处调用(thread_delete_response);外部调用 1 个(format!)。

thread_store_archive_error4086–4094 ↗
fn thread_store_archive_error(operation: &str, err: ThreadStoreError) -> JSONRPCErrorError

作用:把归档/取消归档时的存储错误转换成 API 错误。

数据流:输入操作名和存储错误 → 参数错直接返回,不支持转标准不支持错误,其他转内部错误 → 输出 JSON-RPC 错误。

调用关系:thread_archive_response 和 thread_unarchive_response 使用。

调用图:调用 1 个内部函数(unsupported_thread_store_operation);被 1 处调用(thread_archive_response);外部调用 1 个(format!)。

set_thread_name_from_title4096–4101 ↗
fn set_thread_name_from_title(thread: &mut Thread, title: String)

作用:把一个标题写成 Thread 的显示名称,但避免空名和与预览重复。

数据流:输入可修改 Thread 和标题 → trim 判断;空或等于 preview 就不动,否则设置 name → 原地修改 Thread。

调用关系:attach_thread_name 和 thread_fork_inner 用它补显示名。

调用图:被 2 处调用(attach_thread_name, thread_fork_inner)。

thread_from_stored_thread4103–4155 ↗
fn thread_from_stored_thread(
    thread: StoredThread,
    fallback_provider: &str,
    fallback_cwd: &AbsolutePathBuf,
) -> (Thread, Option<codex_thread_store::StoredThreadHistory>)

作用:把存储层 StoredThread 转成客户端 API 的 Thread 基础对象。

数据流:输入 StoredThread、备用模型供应商和备用 cwd → 规范化路径、转换 Git 信息、来源、时间、父子关系等字段 → 输出 Thread 和可选历史。

调用关系:列表、搜索、读取、恢复、取消归档、分叉等很多地方都用它做统一转换。

调用图:调用 1 个内部函数(relative_to_current_dir);被 7 处调用(load_persisted_thread_for_read, load_thread_from_resume_source_or_send_internal, stored_thread_to_api_thread, thread_list_response_inner, thread_metadata_update_response_inner, thread_search_response_inner, thread_unarchive_response);外部调用 2 个(new, normalize_for_native_workdir)。

summary_from_stored_thread4157–4198 ↗
fn summary_from_stored_thread(
    thread: StoredThread,
    fallback_provider: &str,
) -> ConversationSummary

作用:把存储层对话转成轻量 ConversationSummary。

数据流:输入 StoredThread 和备用模型供应商 → 提取路径、预览、时间、模型供应商、目录、来源和 Git 信息 → 输出摘要对象。

调用关系:get_thread_summary_response_inner 调用它。

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

summary_from_state_db_metadata4202–4246 ↗
fn summary_from_state_db_metadata(
    conversation_id: ThreadId,
    path: PathBuf,
    first_user_message: Option<String>,
    preview: Option<String>,
    timestamp: String,
    updated_at: String,

作用:测试用:把状态数据库元数据拼成 ConversationSummary。

数据流:输入一组拆开的元数据字段 → 解析来源、组合预览和 Git 信息 → 输出摘要对象。

调用关系:只在测试配置下编译,由 summary_from_thread_metadata 调用。

调用图:被 1 处调用(summary_from_thread_metadata);外部调用 1 个(from_str)。

summary_from_thread_metadata4249–4272 ↗
fn summary_from_thread_metadata(metadata: &ThreadMetadata) -> ConversationSummary

作用:测试用:把 ThreadMetadata 转成 ConversationSummary。

数据流:输入 ThreadMetadata → 拆出各字段并传给 summary_from_state_db_metadata → 输出摘要。

调用关系:只在测试里使用,用来验证旧状态库元数据转换。

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

preview_from_rollout_items4274–4289 ↗
fn preview_from_rollout_items(items: &[RolloutItem]) -> String

作用:从历史条目里提取第一条用户消息,作为对话预览。

数据流:输入 RolloutItem 列表 → 找到第一个用户消息,去掉特殊开头标记 → 输出预览字符串;找不到则空字符串。

调用关系:恢复从 history 分叉、创建 ephemeral 分叉时用它生成 preview。

调用图:被 2 处调用(load_thread_from_resume_source_or_send_internal, thread_fork_inner);外部调用 1 个(iter)。

requested_permissions_trust_project4291–4315 ↗
fn requested_permissions_trust_project(overrides: &ConfigOverrides, cwd: &Path) -> bool

作用:判断用户请求的权限是否意味着当前项目应被信任。

数据流:输入配置覆盖和工作目录 → 查看沙盒模式、内置权限档案或自定义权限档案是否允许写项目 → 输出布尔值。

调用关系:thread_start_task 新建对话加载配置后用它决定是否要标记项目可信。

调用图:被 1 处调用(thread_start_task);外部调用 1 个(matches!)。

permission_profile_trusts_project4317–4328 ↗
fn permission_profile_trusts_project(
    profile: &codex_protocol::models::PermissionProfile,
    cwd: &Path,
) -> bool

作用:判断一个权限档案是否信任当前项目目录。

数据流:输入权限档案和 cwd → Disabled/External 视为信任;Managed 则检查文件系统沙盒是否允许写 cwd → 输出布尔值。

调用关系:thread_start_task 和 requested_permissions_trust_project 用它判断项目信任。

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

build_thread_from_snapshot4330–4359 ↗
fn build_thread_from_snapshot(
    thread_id: ThreadId,
    session_id: String,
    config_snapshot: &ThreadConfigSnapshot,
    path: Option<PathBuf>,
) -> Thread

作用:从运行中线程的配置快照创建一个 API Thread 骨架。

数据流:输入线程 ID、session ID、配置快照和可选路径 → 填入模型供应商、cwd、来源、父线程、时间、ephemeral 等基础字段 → 输出没有 turns 的 Thread。

调用关系:新建、恢复、分叉、补监听和 build_thread_from_loaded_snapshot 都用它。

调用图:调用 1 个内部函数(cwd);被 5 处调用(load_thread_from_resume_source_or_send_internal, thread_fork_inner, thread_start_task, try_attach_thread_listener, build_thread_from_loaded_snapshot);外部调用 5 个(new, new, env!, to_string, now_utc)。

paginate_background_terminals4361–4387 ↗
fn paginate_background_terminals(
    terminals: &[ThreadBackgroundTerminal],
    cursor: Option<String>,
    limit: Option<u32>,
) -> Result<(Vec<ThreadBackgroundTerminal>, Option<String>), JSONRPCEr

作用:给后台终端列表做简单分页。

数据流:输入终端切片、游标和限制 → 游标按进程 ID 解析,找到起点,截取一页 → 输出终端子列表和下一页游标。

调用关系:thread_background_terminals_list_inner 调用它。

调用图:被 1 处调用(thread_background_terminals_list_inner);外部调用 2 个(iter, len)。

build_thread_from_loaded_snapshot4389–4400 ↗
fn build_thread_from_loaded_snapshot(
    thread_id: ThreadId,
    config_snapshot: &ThreadConfigSnapshot,
    loaded_thread: &CodexThread,
) -> Thread

作用:从已加载线程直接构造 API Thread 骨架。

数据流:输入线程 ID、配置快照和 loaded_thread → 取 loaded_thread 的 session ID 和 rollout 路径,再调用 build_thread_from_snapshot → 输出 Thread。

调用关系:load_live_thread_view 用它作为活线程视图的 fallback。

调用图:调用 3 个内部函数(build_thread_from_snapshot, rollout_path, session_configured);被 1 处调用(load_live_thread_view)。

app-server/src/request_processors/thread_goal_processor.rs源码 ↗
orchestrationrequest handling, resume

可以把“线程目标”理解成给某个对话线程贴的一张任务卡:要做什么、当前是进行中还是暂停、最多能花多少 token。这个文件就是任务卡的前台办事窗口。客户端发来设置、读取、清除目标的请求后,它先检查功能开关有没有打开,再确认线程编号合法、线程不是临时线程,并找到对应的状态数据库。设置或清除前,它还会把磁盘上的线程记录和数据库对齐,防止旧记录漏进来。真正保存目标的活交给 GoalService 做;保存完后,它会把结果回复给客户端,并尽量通过线程自己的监听通道按顺序发通知。如果通道已经关了,就直接向客户端广播通知,保证外面仍能知道目标变了。

函数细节18
ThreadGoalRequestProcessor::new19–35 ↗
fn new(
        thread_manager: Arc<ThreadManager>,
        outgoing: Arc<OutgoingMessageSender>,
        config: Arc<Config>,
        thread_state_manager: ThreadStateManager,
        state_db: Optio

作用:创建一个处理“线程目标请求”的处理器。它把后面办事需要用到的线程管理器、消息发送器、配置、状态数据库和目标服务都装进同一个对象里。

数据流:进去的是一组共享组件,比如线程管理器、对外发消息的发送器、配置、可选的状态数据库、目标服务。函数不做复杂计算,只是把这些东西保存到结构体字段里。出来的是一个可以处理目标相关请求的 ThreadGoalRequestProcessor。

调用关系:它在上层请求处理器初始化时被调用。之后客户端关于目标的设置、读取、清除请求,都会通过这个对象继续往下分发。

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

ThreadGoalRequestProcessor::thread_goal_set37–45 ↗
async fn thread_goal_set(
        &self,
        request_id: ConnectionRequestId,
        params: ThreadGoalSetParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:这是“设置线程目标”对外暴露的入口。它把真正的工作交给内部函数,成功时告诉上层这里已经自己发送过响应,不需要再包装额外返回值。

数据流:进去的是请求编号和设置目标的参数。它调用 thread_goal_set_inner 完成校验、保存和通知;如果成功,把内部的空结果转换成 None;如果失败,原样把 JSON-RPC 错误交回去。

调用关系:客户端请求进入 handle_initialized_client_request 后会走到这里。它是薄薄的一层门面,实际活由 thread_goal_set_inner 做。

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

ThreadGoalRequestProcessor::thread_goal_get47–54 ↗
async fn thread_goal_get(
        &self,
        params: ThreadGoalGetParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:这是“读取线程目标”对外暴露的入口。它负责把内部拿到的目标结果包装成客户端能收到的响应格式。

数据流:进去的是包含线程编号的查询参数。它调用 thread_goal_get_inner 查数据库;成功后把响应转换成 ClientResponsePayload;失败时返回 JSON-RPC 错误。

调用关系:客户端请求进入 handle_initialized_client_request 后会走到这里。它把查询工作交给 thread_goal_get_inner,并把结果交回上层统一返回。

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

ThreadGoalRequestProcessor::thread_goal_clear56–64 ↗
async fn thread_goal_clear(
        &self,
        request_id: ConnectionRequestId,
        params: ThreadGoalClearParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:这是“清除线程目标”对外暴露的入口。它负责接收请求,并让内部函数完成删除目标、回复客户端、发通知这些事。

数据流:进去的是请求编号和清除参数。它调用 thread_goal_clear_inner;成功后返回 None,表示响应已经在内部发送;失败则返回对应的 JSON-RPC 错误。

调用关系:客户端请求进入 handle_initialized_client_request 后会走到这里。它本身不直接删数据,而是把活交给 thread_goal_clear_inner。

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

ThreadGoalRequestProcessor::emit_resume_goal_snapshot_and_continue66–78 ↗
async fn emit_resume_goal_snapshot_and_continue(
        &self,
        thread_id: ThreadId,
        thread: &CodexThread,
    )

作用:在线程恢复运行时,先把当前目标状态发给客户端,再让线程继续发“我空闲了”之类的生命周期通知。这样客户端看到的顺序不会乱。

数据流:进去的是线程编号和运行中的线程对象。它先看配置里 Goals 功能是否开启;没开启就什么都不做。开启时先调用 emit_thread_goal_snapshot 发一份目标快照,再调用线程的 emit_thread_idle_lifecycle_if_idle 继续后续通知。

调用关系:thread_resume_inner 在线程恢复时调用它。它把目标快照放在恢复流程的正确位置,之后才让线程自己的空闲通知继续发出。

调用图:调用 2 个内部函数(emit_thread_goal_snapshot, emit_thread_idle_lifecycle_if_idle);被 1 处调用(thread_resume_inner)。

ThreadGoalRequestProcessor::pending_resume_goal_state80–95 ↗
async fn pending_resume_goal_state(
        &self,
        thread: &CodexThread,
    ) -> (bool, Option<StateDbHandle>)

作用:在线程恢复前,判断是否需要准备目标状态数据库。它像是提前问一句:这次恢复要不要带上目标信息?要的话去哪儿读?

数据流:进去的是线程对象。它读取配置里的 Goals 功能开关;如果关闭,返回“不需要”和空数据库。若开启,优先使用线程自己带的状态数据库;没有的话使用处理器保存的全局状态数据库。出来的是一个布尔值和一个可选数据库句柄。

调用关系:resume_running_thread 在恢复运行中线程时调用它。它为恢复流程提前准备目标状态信息,后续如果需要发目标快照就有数据库可用。

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

ThreadGoalRequestProcessor::thread_goal_set_inner97–148 ↗
async fn thread_goal_set_inner(
        &self,
        request_id: ConnectionRequestId,
        params: ThreadGoalSetParams,
    ) -> Result<(), JSONRPCErrorError>

作用:真正执行“设置线程目标”的核心流程。它会检查功能开关、确认线程存在、同步旧记录、保存新目标,并通知客户端目标已经更新。

数据流:进去的是请求编号和目标参数,比如线程编号、目标文字、状态、token 预算。它先拒绝未开启的功能,再把线程编号字符串转成内部编号,找到状态数据库,并把线程 rollout 文件和数据库对齐。然后取出线程监听通道,把参数转换成 GoalService 认识的更新请求,交给 goal_service 保存。出来时它已发送设置成功响应,发出目标更新通知,并执行目标服务返回的运行时影响;函数本身返回空成功或错误。

调用关系:thread_goal_set 调用它。它向下依赖 parse_thread_id_for_request、state_db_for_materialized_thread、reconcile_thread_goal_rollout 和 goal_service,保存完后交给 emit_thread_goal_updated_ordered 保证通知顺序。

调用图:调用 6 个内部函数(from, emit_thread_goal_updated_ordered, reconcile_thread_goal_rollout, state_db_for_materialized_thread, parse_thread_id_for_request, thread_state);被 1 处调用(thread_goal_set);外部调用 2 个(clone, Set)。

ThreadGoalRequestProcessor::thread_goal_get_inner150–167 ↗
async fn thread_goal_get_inner(
        &self,
        params: ThreadGoalGetParams,
    ) -> Result<ThreadGoalGetResponse, JSONRPCErrorError>

作用:真正执行“读取线程目标”的查询流程。它负责从状态数据库里拿出某个线程当前的目标记录。

数据流:进去的是查询参数,主要是线程编号字符串。它先检查 Goals 功能是否开启,再解析线程编号,找到对应状态数据库,然后调用 goal_service 读取目标。出来的是 ThreadGoalGetResponse,里面可能有目标,也可能是空,表示这个线程还没设置目标。

调用关系:thread_goal_get 调用它。它使用 parse_thread_id_for_request 和 state_db_for_materialized_thread 做准备,真正的数据读取交给 goal_service。

调用图:调用 2 个内部函数(state_db_for_materialized_thread, parse_thread_id_for_request);被 1 处调用(thread_goal_get)。

ThreadGoalRequestProcessor::thread_goal_clear_inner169–202 ↗
async fn thread_goal_clear_inner(
        &self,
        request_id: ConnectionRequestId,
        params: ThreadGoalClearParams,
    ) -> Result<(), JSONRPCErrorError>

作用:真正执行“清除线程目标”的核心流程。它会删除数据库里的目标记录,并在确实删掉了东西时通知外部。

数据流:进去的是请求编号和清除参数。它先检查功能开关,解析线程编号,找到状态数据库,并同步线程 rollout 记录。然后拿到线程监听通道,调用 goal_service 清除目标。出来时它已给客户端发送清除结果;如果确实清除了目标,还会发出目标已清除通知。

调用关系:thread_goal_clear 调用它。它前面依赖 parse_thread_id_for_request、state_db_for_materialized_thread、reconcile_thread_goal_rollout,后面把通知交给 emit_thread_goal_cleared_ordered。

调用图:调用 5 个内部函数(emit_thread_goal_cleared_ordered, reconcile_thread_goal_rollout, state_db_for_materialized_thread, parse_thread_id_for_request, thread_state);被 1 处调用(thread_goal_clear)。

ThreadGoalRequestProcessor::state_db_for_materialized_thread204–233 ↗
async fn state_db_for_materialized_thread(
        &self,
        thread_id: ThreadId,
    ) -> Result<StateDbHandle, JSONRPCErrorError>

作用:为一个线程找到可用的状态数据库。它还会挡住临时线程,因为临时线程没有落盘记录,不能可靠保存目标。

数据流:进去的是内部线程编号。它先问线程管理器这个线程是否正在运行;如果在运行但没有 rollout 路径,就返回“临时线程不支持目标”的错误;如果线程自己带状态数据库,就直接返回。若线程不在内存中,它会去磁盘目录里查线程路径,找不到就报“线程不存在”。最后如果只能用全局数据库,就返回它;没有全局数据库则报内部错误。

调用关系:设置、读取、清除目标以及恢复时发快照都会调用它。它是所有目标操作进入数据库前的安全检查口。

调用图:被 4 处调用(emit_thread_goal_snapshot, thread_goal_clear_inner, thread_goal_get_inner, thread_goal_set_inner);外部调用 3 个(find_thread_path_by_id_str, format!, to_string)。

ThreadGoalRequestProcessor::reconcile_thread_goal_rollout235–269 ↗
async fn reconcile_thread_goal_rollout(
        &self,
        thread_id: ThreadId,
        state_db: &StateDbHandle,
    ) -> Result<(), JSONRPCErrorError>

作用:在修改目标前,把线程的磁盘记录和状态数据库对齐。这样目标服务看到的是最新、完整的线程资料。

数据流:进去的是线程编号和状态数据库。它先找到线程的 rollout 路径:运行中的线程直接取,没运行的线程就按编号去磁盘查。找不到或是临时线程就报错。找到后调用 reconcile_rollout,把 rollout 文件内容同步进状态数据库。出来时没有新数据返回,但数据库可能已被补齐或更新。

调用关系:thread_goal_set_inner 和 thread_goal_clear_inner 在改目标前调用它。它把“旧的文件记录”和“新的数据库状态”接上,避免后面的 goal_service 基于过期信息操作。

调用图:被 2 处调用(thread_goal_clear_inner, thread_goal_set_inner);外部调用 2 个(find_thread_path_by_id_str, to_string)。

ThreadGoalRequestProcessor::emit_thread_goal_snapshot271–299 ↗
async fn emit_thread_goal_snapshot(&self, thread_id: ThreadId)

作用:给客户端发送某个线程当前目标状态的快照。快照就是“现在这张任务卡长什么样”的一次完整通知。

数据流:进去的是线程编号。它先尝试找到状态数据库;失败就记警告并停止。然后拿线程监听通道,如果通道存在且还能发送,就把“发目标快照”的命令塞给线程监听器。若通道没有或已经关了,它就直接通过 outgoing 发送目标快照通知。

调用关系:emit_resume_goal_snapshot_and_continue 在线程恢复时调用它。它优先借线程监听器发消息,以保持和其他线程事件的顺序;不行时才走直接发送的兜底路线。

调用图:调用 2 个内部函数(state_db_for_materialized_thread, thread_state);被 1 处调用(emit_resume_goal_snapshot_and_continue);外部调用 1 个(warn!)。

ThreadGoalRequestProcessor::emit_thread_goal_updated_ordered301–328 ↗
async fn emit_thread_goal_updated_ordered(
        &self,
        thread_id: ThreadId,
        goal: ThreadGoal,
        listener_command_tx: Option<tokio::sync::mpsc::UnboundedSender<ThreadListenerCo

作用:发送“线程目标已更新”的通知,并尽量保证它和线程的其他事件按正确顺序出现。

数据流:进去的是线程编号、更新后的目标、以及可选的线程监听通道。若通道可用,它把更新命令交给监听器;如果发送失败,会写警告。没有可用通道时,它直接通过 outgoing 广播 ThreadGoalUpdated 通知。出来没有返回值,但外部客户端会收到更新消息。

调用关系:thread_goal_set_inner 保存目标成功后调用它。它负责通知层面的收尾,让客户端知道目标已经变成新内容。

调用图:被 1 处调用(thread_goal_set_inner);外部调用 4 个(ThreadGoalUpdated, clone, to_string, warn!)。

ThreadGoalRequestProcessor::emit_thread_goal_cleared_ordered330–351 ↗
async fn emit_thread_goal_cleared_ordered(
        &self,
        thread_id: ThreadId,
        listener_command_tx: Option<tokio::sync::mpsc::UnboundedSender<ThreadListenerCommand>>,
    )

作用:发送“线程目标已清除”的通知,并尽量让这条通知排在正确的事件顺序里。

数据流:进去的是线程编号和可选的线程监听通道。若通道可用,它把清除命令交给监听器;如果通道关闭,会写警告。没有通道时,它直接通过 outgoing 广播 ThreadGoalCleared 通知。出来没有返回值,但客户端会知道这条线程的目标已被清掉。

调用关系:thread_goal_clear_inner 在确认目标真的被清除后调用它。它和 emit_thread_goal_updated_ordered 类似,都是先走监听器保顺序,失败再直接发通知。

调用图:被 1 处调用(thread_goal_clear_inner);外部调用 3 个(ThreadGoalCleared, to_string, warn!)。

api_thread_goal_from_state354–365 ↗
fn api_thread_goal_from_state(goal: codex_state::ThreadGoal) -> ThreadGoal

作用:把数据库里的目标记录转换成 API 返回给客户端的目标格式。它相当于翻译员,把内部说法翻成外部能看懂的说法。

数据流:进去的是 codex_state::ThreadGoal,也就是状态层保存的目标。它复制线程编号、目标内容、预算、用量和时间戳,并调用 api_thread_goal_status_from_state 翻译状态枚举。出来的是 ThreadGoal,适合放进 API 响应或通知里。

调用关系:它被目标数据需要暴露给客户端时使用。状态字段的具体翻译交给 api_thread_goal_status_from_state。

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

api_thread_goal_status_from_state367–376 ↗
fn api_thread_goal_status_from_state(status: codex_state::ThreadGoalStatus) -> ThreadGoalStatus

作用:把数据库层的目标状态转换成 API 层的目标状态。比如进行中、暂停、受限、完成这些状态,都一一对应过去。

数据流:进去的是 codex_state::ThreadGoalStatus。它根据具体状态做匹配,不改含义,只换成 API 类型。出来的是 ThreadGoalStatus。

调用关系:api_thread_goal_from_state 在转换整条目标记录时调用它。它只负责状态这一个小字段的翻译。

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

goal_service_error378–383 ↗
fn goal_service_error(err: GoalServiceError) -> JSONRPCErrorError

作用:把 GoalService 返回的错误转换成 JSON-RPC 错误。JSON-RPC 是客户端和服务器通话的一种格式,错误也要按它的格式返回。

数据流:进去的是 GoalServiceError。若是 InvalidRequest,就变成客户端请求有问题的错误;若是 Internal,就变成服务器内部错误。出来的是 JSONRPCErrorError,能直接返回给客户端。

调用关系:目标服务调用失败时会用它做错误翻译。这样 thread_goal_set_inner、thread_goal_get_inner、thread_goal_clear_inner 不需要关心每种服务错误该怎么包装。

parse_thread_id_for_request385–388 ↗
fn parse_thread_id_for_request(thread_id: &str) -> Result<ThreadId, JSONRPCErrorError>

作用:把客户端传来的线程编号字符串解析成程序内部使用的 ThreadId。它会把格式不对的编号变成清楚的请求错误。

数据流:进去的是线程编号文本。它调用 ThreadId::from_string 尝试解析;成功就输出 ThreadId;失败就输出 invalid_request 错误,并说明线程编号无效。

调用关系:设置、读取、清除目标的内部流程都会先调用它。它是防止乱七八糟线程编号继续进入数据库操作的第一道关。

调用图:调用 1 个内部函数(from_string);被 3 处调用(thread_goal_clear_inner, thread_goal_get_inner, thread_goal_set_inner)。

app-server/src/request_processors/thread_delete.rs源码 ↗
orchestrationrequest handling

这里的“线程”可以理解成一段会话或任务树里的一个节点。删除它时,不能只把根节点拿掉,否则下面的子节点、运行中的任务、服务器自己的状态记录可能会留下“孤儿”。这个文件的做法像拆一棵树:先找出要删的线程以及它的所有后代,既查持久存储里的记录,也问当前还活着的线程管理器;再检查这个根线程能不能删,比如临时的、没有保存过的线程不能按这种方式删除;然后逐个让线程停下来或准备移除;最后按“先删孩子、再删父亲”的顺序从存储里删除,并清理 app-server 自己的状态库。删除成功后,它先回客户端一个成功响应,再逐个发“线程已删除”的通知。这样客户端界面和服务器内部状态不会互相打架。

函数细节6
ThreadRequestProcessor::thread_delete8–30 ↗
async fn thread_delete(
        &self,
        request_id: ConnectionRequestId,
        params: ThreadDeleteParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:这是删除线程请求的入口。它负责把一次客户端请求包装成完整流程:加锁、执行删除、回响应、发通知。

数据流:输入是请求编号和删除参数,参数里包含要删除的线程 ID。它先拿到线程列表状态的许可,相当于临时占住“线程清单”这张表,防止别人同时改;然后调用 ThreadRequestProcessor::thread_delete_response 真正删除。成功后,它用请求编号把结果发回客户端,再把删掉的线程 ID 交给 ThreadRequestProcessor::send_thread_deleted_notifications 发通知;如果中间失败,就把错误原样返回。

调用关系:它是外部 thread/delete 请求进入本文件的第一站。它把具体删除工作交给 ThreadRequestProcessor::thread_delete_response,把通知工作交给 ThreadRequestProcessor::send_thread_deleted_notifications;自己主要负责控制顺序,确保客户端先收到请求响应,再收到删除通知。

调用图:调用 2 个内部函数(send_thread_deleted_notifications, thread_delete_response);外部调用 2 个(new, clone)。

ThreadRequestProcessor::thread_delete_response32–104 ↗
async fn thread_delete_response(
        &self,
        params: ThreadDeleteParams,
        deleted_thread_ids: &mut Vec<String>,
    ) -> Result<ThreadDeleteResponse, JSONRPCErrorError>

作用:这个函数是真正执行删除的核心步骤。它负责找全要删的线程、确认能不能删、做删除前准备、从各个存储里清掉记录,并整理出最终要通知客户端的删除列表。

数据流:输入是删除参数和一个用来收集已删除线程 ID 的列表。它先把字符串形式的线程 ID 转成内部 ID,失败就报“请求不合法”。然后它从状态库找这个线程的子树,又从运行中的线程管理器补上还活着的子线程,合并去重。接着调用 ThreadRequestProcessor::validate_root_thread_delete 检查根线程是否允许删除,再对每个线程调用 ThreadRequestProcessor::prepare_thread_for_delete 做删除前收尾。之后它按孩子在前、父亲在后的顺序调用线程存储删除记录;如果某个线程已经不在了,会记警告但继续;其他错误会用 thread_store_delete_error 翻译成客户端能懂的错误。最后它还会清理 app-server 自己的状态库,把删除顺序里的 ID 转成字符串放进输出列表,并返回一个空的成功响应。

调用关系:它由 ThreadRequestProcessor::thread_delete 调用,是整条删除链路的主干。遇到运行中线程管理器的写入类错误时,它会交给 core_thread_write_error 包装;遇到线程存储删除错误时,会交给 thread_store_delete_error 变成 JSON-RPC 错误,也就是接口层能返回给客户端的标准错误。

调用图:调用 5 个内部函数(prepare_thread_for_delete, validate_root_thread_delete, thread_store_delete_error, core_thread_write_error, from_string);被 1 处调用(thread_delete);外部调用 1 个(warn!)。

ThreadRequestProcessor::send_thread_deleted_notifications106–114 ↗
async fn send_thread_deleted_notifications(&self, deleted_thread_ids: Vec<String>)

作用:这个函数负责告诉客户端:哪些线程已经被删除了。它把删除结果变成一条条服务器通知发出去。

数据流:输入是一组已经删除的线程 ID 字符串。它逐个取出 ID,包装成 ThreadDeletedNotification,再放进 ServerNotification::ThreadDeleted 这种通知消息里,通过 outgoing 发送出去。它不返回业务数据,只完成发送动作。

调用关系:它只在 ThreadRequestProcessor::thread_delete 删除成功并已发送请求响应之后被调用。这样客户端先知道“这次删除请求成功了”,再收到每个线程消失的事件,界面可以据此移除对应项。

调用图:被 1 处调用(thread_delete);外部调用 1 个(ThreadDeleted)。

ThreadRequestProcessor::validate_root_thread_delete116–167 ↗
async fn validate_root_thread_delete(
        &self,
        thread_id: ThreadId,
        has_descendants: bool,
    ) -> Result<(), JSONRPCErrorError>

作用:这个函数检查“要删除的根线程”是否合法。它避免把不该删的线程删掉,也区分“真的不存在”和“主记录没了但还有子节点或状态残留”的情况。

数据流:输入是根线程 ID,以及它是否有后代。它先问运行中的线程管理器有没有这个线程;如果有,并且这个线程不是临时线程,就允许删除;如果是临时线程,就返回“不能删除”的请求错误。若运行中没有,它再去线程存储里读归档也包含在内的记录;读得到就允许。读不到时,如果还有后代,说明仍有一棵子树需要清理,也允许;如果没有后代,它会再查 app-server 自己的状态库,那里还存在就允许清理,否则就把“线程不存在”作为错误返回。其他存储错误会通过 thread_store_delete_error 转成标准错误。

调用关系:它由 ThreadRequestProcessor::thread_delete_response 在真正删除前调用,相当于拆房子前先确认这栋房子是不是能拆。它依赖 thread_store_delete_error 把底层存储错误翻译成客户端接口能理解的说法。

调用图:调用 1 个内部函数(thread_store_delete_error);被 1 处调用(thread_delete_response);外部调用 1 个(format!)。

ThreadRequestProcessor::prepare_thread_for_delete169–174 ↗
async fn prepare_thread_for_delete(&self, thread_id: ThreadId)

作用:这个函数做删除前的收尾工作。它让指定线程进入可移除状态,并尽量把日志写稳,减少删除时留下半截记录的风险。

数据流:输入是一个线程 ID。它先调用通用的“准备移除线程”步骤,并标明这次原因是 delete;如果有日志数据库,就执行 flush,也就是把缓冲里的日志尽快刷到实际存储里。它不产出新数据,主要改变线程和日志系统的状态。

调用关系:它由 ThreadRequestProcessor::thread_delete_response 对每个将要删除的线程调用。它位于“确认可以删”和“真正从存储删除”之间,作用像搬家前先关水关电,避免后面硬删除时还有后台活动。

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

thread_store_delete_error177–188 ↗
fn thread_store_delete_error(err: ThreadStoreError) -> JSONRPCErrorError

作用:这个小函数把线程存储层的错误翻译成 JSON-RPC 错误,也就是客户端接口能读懂的错误格式。它让底层错误不会原封不动、含糊地漏到外面。

数据流:输入是一个 ThreadStoreError,也就是线程存储报告的失败原因。它会按情况改写:线程不存在变成“thread not found”,请求本身不合法就保留对应消息,不支持的存储操作交给 unsupported_thread_store_operation 处理,其他意外错误统一变成“删除线程失败”的内部错误。输出是 JSONRPCErrorError,用来返回给请求处理层。

调用关系:它被 ThreadRequestProcessor::thread_delete_response 和 ThreadRequestProcessor::validate_root_thread_delete 调用。前者在真正删除失败时用它,后者在删除前检查读存储失败时用它;它是本文件里把底层存储语言翻译成接口语言的关口。

调用图:调用 1 个内部函数(unsupported_thread_store_operation);被 2 处调用(thread_delete_response, validate_root_thread_delete);外部调用 1 个(format!)。

app-server/src/dynamic_tools.rs源码 ↗
orchestrationrequest handling

可以把这里看成一个“工具结果收件员”。核心系统让客户端去执行某个动态工具,之后会等一个一次性回信通道。on_call_response 收到回信后,先判断是正常结果、客户端报错,还是通道断了。如果只是因为对话轮次切换导致的旧请求失效,它会安静退出;如果是真失败,就记日志,并造一个失败结果。正常结果还要经过 decode_response,把 JSON(一种常见的数据文本格式)变成程序里能用的 DynamicToolCallResponse。最后,它把 app-server 使用的返回格式转换成 core 使用的返回格式,并通过 conversation.submit 交回 CodexThread,也就是核心对话线程。fallback_response 是安全网:无论哪里出错,都能生成一条“工具调用失败”的文本结果,告诉核心系统这次没有成功,而不是让系统拿到空白或崩掉。

函数细节3
on_call_response14–53 ↗
async fn on_call_response(
    call_id: String,
    receiver: oneshot::Receiver<ClientRequestResult>,
    conversation: Arc<CodexThread>,
)

作用:这是动态工具回信流程的主入口。它等待客户端返回工具执行结果,判断成败,必要时做兜底,然后把结果交回核心对话。

数据流:进去的是一个工具调用编号 call_id、一个一次性接收器 receiver,以及核心对话线程 conversation。它先等 receiver 给结果;如果收到正常 JSON,就交给 decode_response 解析;如果收到客户端错误或通道错误,就根据情况忽略或生成 fallback_response。之后它把 app-server 的响应内容转换成核心协议里的 DynamicToolResponse,并提交成 Op::DynamicToolResponse。出来没有普通返回值,但它会改动系统状态:把工具结果送回正在运行的对话线程;如果提交失败,会写错误日志。

调用关系:它由 apply_bespoke_event_handling 在处理特殊事件时调用,是客户端工具结果回到核心系统的桥。它把解析工作交给 decode_response,把失败兜底交给 fallback_response,还会调用 is_turn_transition_server_request_error 判断某些错误是不是因为对话轮次切换造成的旧请求失效。遇到问题时,它用 error! 写日志,方便排查。

调用图:调用 3 个内部函数(decode_response, fallback_response, is_turn_transition_server_request_error);被 1 处调用(apply_bespoke_event_handling);外部调用 1 个(error!)。

decode_response55–63 ↗
fn decode_response(value: serde_json::Value) -> (DynamicToolCallResponse, Option<String>)

作用:这个函数把客户端传回来的 JSON 数据翻译成真正的 DynamicToolCallResponse。它的作用是确认“回信长得对不对”,避免坏格式直接进入核心流程。

数据流:进去的是一份 serde_json::Value,也就是还没确认结构的 JSON 数据。它尝试按 DynamicToolCallResponse 的格式读取;成功就原样产出这个响应,并带上空的错误信息;失败就记录日志,然后调用 fallback_response 生成一份失败响应。出来的是一对数据:可用的工具响应,以及可选的错误文字。

调用关系:它只被 on_call_response 使用,位置在“收到客户端回信之后、提交给核心对话之前”。如果解析成功,它让流程继续走正常路线;如果解析失败,它把补救工作交给 fallback_response,保证 on_call_response 后面仍然有东西可以提交。

调用图:调用 1 个内部函数(fallback_response);被 1 处调用(on_call_response);外部调用 1 个(error!)。

fallback_response65–75 ↗
fn fallback_response(message: &str) -> (DynamicToolCallResponse, Option<String>)

作用:这是统一的失败兜底生成器。只要动态工具请求失败、回信断了、或者返回格式不对,它就造一份明确标记为失败的响应。

数据流:进去的是一段给用户或系统看的错误文字 message。它把这段文字放进一个文本内容项里,并把 success 设为 false。出来的是一份 DynamicToolCallResponse,加上一份同样的错误文字,表示这次工具调用没有成功。

调用关系:它被 on_call_response 和 decode_response 调用,是整个文件的安全网。on_call_response 在请求失败或通道断开时直接用它;decode_response 在 JSON 解析失败时用它。它不再调用项目里的其他业务函数,只负责快速拼出一个可靠的失败结果。

调用图:被 2 处调用(decode_response, on_call_response);外部调用 1 个(vec!)。

app-server/src/server_request_error.rs源码 ↗
domain_logicrequest handling

在这个系统里,客户端和服务器会用 JSON-RPC(一种用 JSON 格式来表达请求和响应的通信规则)来互相说话。有些请求可能还没处理完,当前对话轮次就变了,这时服务器会返回一个错误对象,但这个错误其实表示“旧请求该停了”,不是程序坏了。这个文件就像一个专门看错误单据的小标签机:它检查错误里的 data.reason 字段是不是固定的 "turnTransition"。如果是,就告诉调用者“这是轮次切换导致的中断”;如果不是,就当普通错误处理。文件里还带了两个测试,一个确认这种特殊错误能被认出来,另一个确认普通错误不会被误认。

函数细节3
is_turn_transition_server_request_error5–12 ↗
fn is_turn_transition_server_request_error(error: &JSONRPCErrorError) -> bool

作用:判断一个 JSON-RPC 错误是不是“对话轮次切换”导致的特殊中断。别人会用它来决定这个错误要不要当成真正失败来处理。

数据流:进去的是一个 JSONRPCErrorError 错误对象;函数读取它的 data 字段,再找里面的 reason 字段,并确认这个字段是不是字符串 "turnTransition";出来的是 true 或 false,不修改原来的错误对象。

调用关系:它是请求处理流程里的一个小判定器。像 mcp_server_elicitation_response_from_client_result、on_command_execution_request_approval_response、on_file_change_request_approval_response、on_request_user_input_response、request_permissions_response_from_client_result、on_call_response 这些处理客户端回复或调用结果的地方,会在遇到错误时叫它来分辨:这是轮次切换造成的可预期中断,还是需要继续当作异常处理的别的问题。

调用图:被 6 处调用(mcp_server_elicitation_response_from_client_result, on_command_execution_request_approval_response, on_file_change_request_approval_response, on_request_user_input_response, request_permissions_response_from_client_result, on_call_response)。

tests::turn_transition_error_is_detected22–30 ↗
fn turn_transition_error_is_detected()

作用:测试“轮次切换”这种特殊错误确实能被识别出来。它防止以后有人改代码时,不小心把这个判断弄坏。

数据流:进去的是测试里临时造出的一个错误对象,里面的 data.reason 是 "turnTransition";测试把它交给 is_turn_transition_server_request_error;期望出来的结果是 true,并用断言检查这一点。

调用关系:它只在运行测试时执行。它调用 json! 来快速拼出错误里的 JSON 数据,再用 assert_eq! 检查判断结果是否符合预期,是 is_turn_transition_server_request_error 的正向样例。

调用图:外部调用 2 个(assert_eq!, json!)。

tests::unrelated_error_is_not_detected33–41 ↗
fn unrelated_error_is_not_detected()

作用:测试普通错误不会被误认为“轮次切换”错误。它保证这个判断不会太宽松,避免把真正的问题悄悄放过去。

数据流:进去的是测试里临时造出的一个错误对象,里面的 data.reason 是 "other";测试把它交给 is_turn_transition_server_request_error;期望出来的结果是 false,并用断言确认。

调用关系:它只在运行测试时执行。它同样用 json! 造测试数据,用 assert_eq! 检查结果,是 is_turn_transition_server_request_error 的反向样例,用来和正向测试配成一组。

调用图:外部调用 2 个(assert_eq!, json!)。

app-server/src/models.rs源码 ↗
domain_logicrequest handling

这个文件像一个“菜单整理员”。系统内部的模型信息来自 ThreadManager,可以把它理解成管着后台资源和模型列表的管家。但这些原始信息不一定适合直接给界面用,所以这里会先去拿模型列表,再按需过滤掉隐藏模型,最后把每个内部的 ModelPreset 转成对外协议里的 Model。转换时不只是改名字,还会带上显示名、描述、是否默认、升级说明、输入能力、服务档位等信息。推理强度也会从内部格式转成客户端能展示的选项。这样一来,界面拿到的就是一份干净、完整、适合展示的模型菜单。

函数细节3
supported_models12–23 ↗
async fn supported_models(
    thread_manager: Arc<ThreadManager>,
    include_hidden: bool,
) -> Vec<Model>

作用:这个函数生成“客户端可以看到的模型列表”。别人调用它时,通常是想在界面上展示模型选择器,或者让客户端知道当前有哪些模型可用。

数据流:进去的是一个 ThreadManager(一位能查询模型清单的后台管家)和 include_hidden(是否连隐藏模型也一起给)。它先在线获取或使用缓存里的模型清单,然后如果不需要隐藏模型,就把不该出现在选择器里的模型过滤掉。接着它把每个内部模型资料交给 model_from_preset 转成对外格式,最后出来的是一组 Model。

调用关系:它是这个文件对外最主要的入口。上层在需要模型清单时会调用它;它自己负责拿列表和过滤,具体“怎么把一个内部模型变成客户端模型”则交给 model_from_preset 完成。

model_from_preset25–59 ↗
fn model_from_preset(preset: ModelPreset) -> Model

作用:这个函数把一个内部模型配置,翻译成客户端协议里的模型对象。它的用处是保证前端看到的字段完整、名字统一,而且不用理解后台内部格式。

数据流:进去的是一个 ModelPreset,也就是系统内部保存的一份模型说明。它逐项取出模型编号、真实模型名、显示名、描述、升级信息、是否隐藏、默认推理强度、输入能力、服务档位等内容,并按客户端需要的结构重新包装。遇到推理强度列表时,它会交给 reasoning_efforts_from_preset 再转换。最后出来的是一个 Model。

调用关系:supported_models 会对每个查到的模型调用它。它是中间的翻译层:上面接收内部模型清单,下面调用 reasoning_efforts_from_preset 处理推理强度这种子列表,最后把完整结果交还给调用方。

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

reasoning_efforts_from_preset61–71 ↗
fn reasoning_efforts_from_preset(
    efforts: Vec<ReasoningEffortPreset>,
) -> Vec<ReasoningEffortOption>

作用:这个函数专门转换“推理强度”选项。推理强度可以理解成模型思考得多深入、可能也会影响速度和成本的选项。

数据流:进去的是一组内部格式的 ReasoningEffortPreset。它逐个取出实际的强度值和说明文字,包装成客户端协议里的 ReasoningEffortOption。最后出来的是一组客户端能直接展示或使用的推理强度选项。

调用关系:它不直接面向最上层调用者,而是被 model_from_preset 在转换单个模型时使用。这样 model_from_preset 不用把所有细节都写在一起,推理强度这一小块由它单独完成。

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

core/src/tools/router.rs源码 ↗
orchestration每轮对话开始准备工具时,以及模型返回工具调用后处理该调用时

这份文件解决的是一个很实际的问题:模型会用不同格式说“调用工具”,比如普通函数工具、工具搜索、扩展工具等;系统不能让每个地方都自己猜这些格式。这里的 ToolRouter 就像前台分诊员:先知道有哪些工具、哪些工具能给模型看;再把模型返回的 ResponseItem 解析成统一的 ToolCall;最后交给 ToolRegistry(工具登记册,记录工具名字和执行方式)去真正执行。它还会回答一些调度问题,比如某个工具能不能并行跑、取消时要不要等工具自己收尾。文件末尾的 extension_tool_executors 会从当前会话里收集扩展插件提供的工具执行器,让外部扩展的工具也能进入同一套调度流程。

函数细节13
ToolRouter::from_turn_context49–55 ↗
fn from_turn_context(
        turn_context: &TurnContext,
        params: ToolRouterParams<'_>,
        tool_search_handler_cache: &ToolSearchHandlerCache,
    ) -> Self

作用:根据当前这一轮对话的上下文,创建一个可用的工具路由器。有人要开始一轮模型交互、需要知道本轮有哪些工具可用时,会用它。

数据流:输入是一轮对话的上下文、各种工具来源参数,以及工具搜索处理器缓存 → 它把这些信息交给 build_tool_router 统一组装 → 输出一个 ToolRouter,里面已经放好了工具登记册和模型能看到的工具说明。

调用关系:它是创建路由器的常用入口,测试和正常流程都会用到。它自己不细拆工具来源,而是把组装工作交给 build_tool_router,这样本文件只负责“路由器长什么样、怎么用”。

调用图:调用 1 个内部函数(build_tool_router);被 11 处调用(fatal_tool_error_stops_turn_and_reports_error, test_tool_runtime, built_tools, handle_output_item_done_returns_contributed_last_agent_message, extension_tool_executors_are_model_visible_and_dispatchable, mcp_parallel_support_uses_handler_data, parallel_support_does_not_match_namespaced_local_tool_names, specs_filter_deferred_dynamic_tools, tools_without_handlers_do_not_support_parallel, probe_with (+1 more))。

ToolRouter::from_parts57–62 ↗
fn from_parts(registry: ToolRegistry, model_visible_specs: Vec<ToolSpec>) -> Self

作用:用已经准备好的工具登记册和工具说明,直接拼出一个 ToolRouter。适合底层构建流程或测试已经把零件准备好的情况。

数据流:输入是 ToolRegistry 和一组模型可见的 ToolSpec → 它原样保存到 ToolRouter 里 → 输出一个可以查询工具说明、派发工具调用的路由器。

调用关系:它是更底层的构造方式。build_tool_router 组装好所有工具后会用它生成最终路由器,一些测试也会绕过复杂准备步骤直接用它。

调用图:被 3 处调用(cancellation_after_handler_finishes_preserves_completed_lifecycle, cancellation_waiting_for_runtime_cleanup_emits_only_aborted_lifecycle, build_tool_router)。

ToolRouter::model_visible_specs64–66 ↗
fn model_visible_specs(&self) -> Vec<ToolSpec>

作用:取出“模型能看到的工具说明”。这些说明会告诉模型:现在有哪些工具、每个工具该怎么传参数。

数据流:输入是当前 ToolRouter 自己保存的工具说明列表 → 它复制一份列表 → 输出给调用方,不让外部直接改动路由器内部保存的原件。

调用关系:提示词构建流程会用它把工具说明塞进给模型的上下文里。也就是说,它连接了“系统有什么工具”和“模型知道自己能用什么工具”这两件事。

调用图:被 2 处调用(build_prompt, from_router)。

ToolRouter::registered_tool_names_for_test69–71 ↗
fn registered_tool_names_for_test(&self) -> Vec<ToolName>

作用:测试专用:拿到当前已经登记的工具名字。它帮助测试确认工具路由器是不是真的装进了预期的工具。

数据流:输入是当前 ToolRouter → 它向内部的 ToolRegistry 询问测试用工具名列表 → 输出这些工具名。

调用关系:它只在测试编译时存在。测试代码通过它检查 build_tool_router 或其他构建流程的结果是否正确,真正运行时不会依赖它。

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

ToolRouter::tool_exposure_for_test74–79 ↗
fn tool_exposure_for_test(
        &self,
        name: &ToolName,
    ) -> Option<crate::tools::registry::ToolExposure>

作用:测试专用:查看某个工具是怎样暴露给外界的,比如是否给模型可见。它用来验证工具的可见性策略是否符合预期。

数据流:输入是一个工具名 → 它去 ToolRegistry 里查这个工具的暴露信息 → 输出可能存在的 ToolExposure;如果没这个工具,就输出空。

调用关系:它服务于测试,帮助测试从路由器外面观察登记册里的状态。实际工具调用流程不会靠它执行工具。

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

ToolRouter::create_diff_consumer81–86 ↗
fn create_diff_consumer(
        &self,
        tool_name: &ToolName,
    ) -> Option<Box<dyn ToolArgumentDiffConsumer>>

作用:为某个工具创建“参数变化接收器”。简单说,当模型一点点流式吐出工具参数时,这个接收器可以边看边处理参数的增量变化。

数据流:输入是工具名 → 它询问 ToolRegistry 这个工具有没有对应的参数增量接收器 → 输出一个可用的接收器,或者在不支持时输出空。

调用关系:它把“边生成边处理工具参数”的需求转交给工具登记册。调用方不需要知道每个工具具体怎么处理参数变化,只要通过路由器来要接收器即可。

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

ToolRouter::tool_supports_parallel88–92 ↗
fn tool_supports_parallel(&self, call: &ToolCall) -> bool

作用:判断某个工具调用能不能和其他工具调用并行执行。这样系统可以避免把不能同时跑的工具硬塞到并发流程里。

数据流:输入是一个 ToolCall → 它拿出工具名,去 ToolRegistry 查询该工具是否支持并行调用 → 输出 true 或 false;如果查不到,就保守地返回 false。

调用关系:它在调度工具调用前提供安全判断。真正的信息来自 ToolRegistry,ToolRouter 在这里负责把调用对象里的工具名拿出来并给出默认策略。

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

ToolRouter::tool_waits_for_runtime_cancellation94–98 ↗
fn tool_waits_for_runtime_cancellation(&self, call: &ToolCall) -> bool

作用:判断取消工具时,系统是否应该等待工具运行环境完成清理。比如有些工具被取消后还要关进程、收尾日志,不能立刻当作结束。

数据流:输入是一个 ToolCall → 它用工具名询问 ToolRegistry 是否需要等待运行时取消清理 → 输出 true 或 false;查不到时默认认为不需要等待。

调用关系:它帮助取消流程做正确决定。路由器不自己清理工具,只负责告诉上层:这个工具取消后要不要给它时间收尾。

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

ToolRouter::build_tool_call101–148 ↗
fn build_tool_call(item: ResponseItem) -> Result<Option<ToolCall>, FunctionCallError>

作用:把模型返回的一条消息,翻译成系统内部统一使用的 ToolCall。没有这一步,后面的执行器就得面对各种不同形状的模型输出,很容易出错。

数据流:输入是一个 ResponseItem,也就是模型输出中的一项 → 它判断这项是不是工具调用:普通函数调用会带上命名空间和名字,工具搜索调用会解析 JSON 参数,自定义工具调用会保存原始输入 → 输出 Some(ToolCall);如果这项不是需要客户端执行的工具调用,就输出 None;如果工具搜索参数解析失败,就输出一个可反馈给模型的错误。

调用关系:它是模型输出进入工具系统的翻译关口。工具执行前,调用方会先用它把 ResponseItem 变成 ToolCall;后续再由派发函数把 ToolCall 送进 ToolRegistry。

调用图:调用 2 个内部函数(new, plain);被 5 处调用(fatal_tool_error_stops_turn_and_reports_error, shell_tool_cancellation_waits_for_runtime_cleanup, handle_output_item_done, build_tool_call_uses_namespace_for_registry_name, extension_tool_executors_are_model_visible_and_dispatchable);外部调用 1 个(from_value)。

ToolRouter::dispatch_tool_call_with_code_mode_result152–171 ↗
async fn dispatch_tool_call_with_code_mode_result(
        &self,
        session: Arc<Session>,
        turn: Arc<TurnContext>,
        cancellation_token: CancellationToken,
        tracker: SharedT

作用:执行一个工具调用,并拿回统一的工具结果。它适合普通工具派发场景,不额外传入“终局结果是否已出现”的标记。

数据流:输入包括会话、当前轮上下文、取消令牌、差异跟踪器、工具调用和调用来源 → 它把这些原样交给内部派发函数,并说明没有额外的终局标记 → 输出工具执行结果,或者输出函数调用错误。

调用关系:它是公开一些的便捷包装函数。真正组装 ToolInvocation 和调用登记册的工作在 dispatch_tool_call_with_code_mode_result_inner 里完成。

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

ToolRouter::dispatch_tool_call_with_terminal_outcome175–195 ↗
async fn dispatch_tool_call_with_terminal_outcome(
        &self,
        session: Arc<Session>,
        turn: Arc<TurnContext>,
        cancellation_token: CancellationToken,
        tracker: SharedT

作用:执行工具调用,同时带上一个“终局结果是否已经达到”的共享标记。这个标记可以帮助系统在复杂流程里知道是否已经不需要继续等某些结果。

数据流:输入比普通派发多一个 AtomicBool(原子布尔值,可以被多个任务安全共享的一盏开关灯) → 它把所有信息交给内部派发函数,并把这盏开关灯一起传进去 → 输出工具执行结果或错误。

调用关系:它服务于需要关注终局状态的调度流程。它本身不执行工具,而是把额外状态包装好后交给 dispatch_tool_call_with_code_mode_result_inner。

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

ToolRouter::dispatch_tool_call_with_code_mode_result_inner198–228 ↗
async fn dispatch_tool_call_with_code_mode_result_inner(
        &self,
        session: Arc<Session>,
        turn: Arc<TurnContext>,
        cancellation_token: CancellationToken,
        tracker: S

作用:这是实际派发工具调用的核心小管道。它把零散的会话、工具名、参数、取消控制等信息打包成一次完整的工具执行请求。

数据流:输入是会话、轮次上下文、取消令牌、差异跟踪器、ToolCall、来源,以及可选的终局标记 → 它拆开 ToolCall,组装成 ToolInvocation(一次工具调用所需的完整包裹) → 然后交给 ToolRegistry 执行 → 输出任意工具结果 AnyToolResult,或执行中产生的错误。

调用关系:两个外层派发函数都会把活儿交给它。它是 ToolRouter 和 ToolRegistry 的交接点:路由器负责把信息整理成标准包裹,登记册负责找到对应工具并执行。

调用图:调用 1 个内部函数(dispatch_any_with_terminal_outcome);被 2 处调用(dispatch_tool_call_with_code_mode_result, dispatch_tool_call_with_terminal_outcome)。

extension_tool_executors231–246 ↗
fn extension_tool_executors(
    session: &Session,
) -> Vec<Arc<dyn ToolExecutor<ExtensionToolCall>>>

作用:从当前会话里收集扩展插件提供的工具执行器。这样插件贡献的工具也能被纳入系统,而不只是内置工具能用。

数据流:输入是 Session,也就是当前会话及其服务集合 → 它找到所有扩展工具贡献者,并把会话级、线程级的扩展数据传给它们 → 收集每个贡献者返回的工具执行器 → 输出一组可执行扩展工具的对象。

调用关系:构建工具列表时会调用它。它把扩展系统和工具路由系统接起来:扩展负责提供工具,后续的工具构建和路由流程负责让这些工具对模型可见并能被派发执行。

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

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

可以把这个文件想成一家修理店的前台加调度员。模型说“我要用某个工具”,这里先查登记表,看这个工具是否存在、输入类型对不对;然后通知生命周期系统“工具开始了”,再运行“前置钩子”(钩子就是执行前后插入的一段检查或改写逻辑),允许它拦截或修改输入。真正执行时,它还会记录遥测日志(给运行情况打点)、沙箱信息(工具能做什么、不能做什么)、内存污染标记等。工具成功后,它会准备“后置钩子”能看懂的输入和结果,让后置钩子决定是否放行、追加上下文,或者把给模型看的结果替换成反馈信息。这里还处理隐藏工具、并行调用能力、取消时是否等待清理、流式参数差异消费等边角问题。核心类型是 ToolRegistry,像一本工具通讯录;CoreToolRuntime 是每个工具必须遵守的运行合同;AnyToolResult 则把任意工具的输出统一包装起来。

函数细节45
CoreToolRuntime::matches_kind48–53 ↗
fn matches_kind(&self, payload: &ToolPayload) -> bool

作用:判断某个工具是否能处理这次传进来的调用内容。默认只接受普通函数工具和工具搜索请求,避免拿错工具干错活。

数据流:输入是一份工具调用载荷 → 它检查载荷是不是函数调用或工具搜索 → 输出 true 或 false,告诉调度器能不能继续交给这个工具。

调用关系:ToolRegistry::dispatch_any_with_terminal_outcome 在真正执行前会问它一句;如果回答不能处理,调度器会立刻报错,而不会把不合适的请求塞给工具。

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

CoreToolRuntime::waits_for_runtime_cancellation57–59 ↗
fn waits_for_runtime_cancellation(&self) -> bool

作用:说明工具被取消时,系统是否应该等工具自己做完收尾再返回“已中止”。默认不等。

数据流:没有输入 → 返回一个布尔值 → 这个值影响取消流程是马上结束,还是给工具留时间清理现场。

调用关系:ToolRegistry::waits_for_runtime_cancellation 会查询它;外层取消逻辑据此决定是否等待运行时清理。

CoreToolRuntime::telemetry_tags61–66 ↗
fn telemetry_tags(
        &'a self,
        _invocation: &'a ToolInvocation,
    ) -> BoxFuture<'a, ToolTelemetryTags>

作用:给这次工具调用补充遥测标签。遥测标签就是日志里的分类小纸条,方便之后统计“哪个服务器、哪个来源、哪类工具”出了什么情况。

数据流:输入是一次工具调用 → 默认不添加任何标签 → 输出一个空列表;具体工具可以重写它,返回自己的标签。

调用关系:ToolRegistry::dispatch_any_with_terminal_outcome 在记录工具执行日志前会调用它,把这些标签合并进日志和追踪信息。

调用图:外部调用 2 个(pin, new)。

CoreToolRuntime::post_tool_use_payload68–100 ↗
fn post_tool_use_payload(
        &self,
        invocation: &ToolInvocation,
        result: &dyn ToolOutput,
    ) -> Option<PostToolUsePayload>

作用:把工具执行后的结果整理成后置钩子能看懂的格式。后置钩子可以据此检查结果、追加上下文,甚至阻止结果给模型看。

数据流:输入是工具调用和工具输出 → 它提取工具名、调用编号、输入和响应内容 → 输出 PostToolUsePayload;如果不是函数类工具,输出空。

调用关系:handle_any_tool 在工具执行成功后会调用它,提前准备后置钩子的材料;之后 ToolRegistry::dispatch_any_with_terminal_outcome 会拿这些材料去运行后置钩子。

调用图:调用 4 个内部函数(function_hook_tool_name, post_tool_use_id, post_tool_use_input, post_tool_use_response);被 1 处调用(handle_any_tool)。

CoreToolRuntime::pre_tool_use_payload102–111 ↗
fn pre_tool_use_payload(&self, invocation: &ToolInvocation) -> Option<PreToolUsePayload>

作用:把工具执行前的输入整理成前置钩子能看懂的格式。前置钩子可以用它来审查或改写即将执行的工具输入。

数据流:输入是工具调用 → 如果是函数工具,就把参数转成 JSON 值并配上钩子用的工具名 → 输出 PreToolUsePayload;否则输出空。

调用关系:ToolRegistry::dispatch_any_with_terminal_outcome 在执行工具前会调用它,然后把结果交给 run_pre_tool_use_hooks。

调用图:调用 2 个内部函数(function_hook_tool_input, function_hook_tool_name)。

CoreToolRuntime::with_updated_hook_input117–138 ↗
fn with_updated_hook_input(
        &self,
        invocation: ToolInvocation,
        updated_input: Value,
    ) -> Result<ToolInvocation, FunctionCallError>

作用:当前置钩子改写了工具输入时,用改写后的内容重新生成一次工具调用。默认做法是把新的 JSON 输入重新变成函数字符串参数。

数据流:输入是原工具调用和钩子改过的 JSON 输入 → 它确认这是函数工具,再把 JSON 序列化成参数字符串 → 输出新的 ToolInvocation;失败时返回给模型看的错误。

调用关系:ToolRegistry::dispatch_any_with_terminal_outcome 在前置钩子返回 updated_input 时会调用它;有些工具可以重写它,以支持更稳定的自定义输入格式。

调用图:外部调用 2 个(to_string, RespondToModel)。

CoreToolRuntime::create_diff_consumer141–143 ↗
fn create_diff_consumer(&self) -> Option<Box<dyn ToolArgumentDiffConsumer>>

作用:为支持“参数一边生成一边消费”的工具创建消费者。默认没有这种能力。

数据流:没有输入 → 默认返回空 → 表示这个工具不处理流式参数差异。

调用关系:ToolRegistry::create_diff_consumer 会转发到这里;如果某个工具支持流式参数预览,就会提供自己的实现。

ToolArgumentDiffConsumer::finish154–156 ↗
fn finish(&mut self) -> Result<Option<EventMsg>, FunctionCallError>

作用:告诉参数差异消费者:这次流式输入已经结束,可以做最后收尾。默认什么事件都不发。

数据流:没有额外输入 → 默认返回成功且没有事件 → 不改动外部状态。

调用关系:实现 ToolArgumentDiffConsumer 的工具可以重写它;调用方会在工具参数流结束前后用它收尾。

AnyToolResult::into_response167–175 ↗
fn into_response(self) -> ResponseInputItem

作用:把统一包装的工具结果转换成协议里要返回给模型的消息。

数据流:输入是 AnyToolResult 本身 → 取出调用编号、原始载荷和工具输出 → 输出 ResponseInputItem,也就是模型能接收的结果项。

调用关系:工具调度完成后,外层流程需要把结果喂回模型时会用它;真正的转换工作交给具体 ToolOutput。

AnyToolResult::code_mode_result177–182 ↗
fn code_mode_result(self) -> serde_json::Value

作用:把工具结果转换成“代码模式”需要的 JSON 结果,而不是普通对话响应。

数据流:输入是 AnyToolResult 本身 → 取出载荷和工具输出 → 输出 serde_json::Value 形式的结果。

调用关系:代码模式分支会用它拿到更适合程序继续处理的结构化结果;具体格式由 ToolOutput 决定。

PostToolUseFeedbackOutput::log_preview191–193 ↗
fn log_preview(&self) -> String

作用:保留原始工具输出的日志预览。即使给模型看的内容被后置钩子替换,日志里仍能看到原工具结果的摘要。

数据流:输入是这个包装输出对象 → 它转问 original 原始输出 → 返回原始日志预览字符串。

调用关系:ToolRegistry::dispatch_any_with_terminal_outcome 在后置钩子给出反馈消息时会用 PostToolUseFeedbackOutput 包住结果;日志预览仍走这里回到原输出。

PostToolUseFeedbackOutput::success_for_logging195–197 ↗
fn success_for_logging(&self) -> bool

作用:保留原始工具执行是否成功的判断。反馈信息只改变给模型看的话,不改变工具本身成功与否。

数据流:输入是包装输出对象 → 它询问 original 原始输出的成功状态 → 返回 true 或 false。

调用关系:执行日志和生命周期记录会读取这个成功标记;它确保后置反馈不会篡改真实执行结果。

PostToolUseFeedbackOutput::to_response_item199–201 ↗
fn to_response_item(&self, call_id: &str, payload: &ToolPayload) -> ResponseInputItem

作用:把后置钩子提供的反馈消息转换成模型能看到的响应,替代原始工具输出。

数据流:输入是调用编号和工具载荷 → 它使用 model_visible 这份替代输出生成响应项 → 输出 ResponseInputItem。

调用关系:当后置钩子不阻止结果、但要求给模型显示一段反馈时,AnyToolResult::into_response 最终会走到这里。

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

PostToolUseFeedbackOutput::code_mode_result203–205 ↗
fn code_mode_result(&self, payload: &ToolPayload) -> Value

作用:代码模式下仍返回原始工具结果,不让后置钩子的模型反馈覆盖结构化数据。

数据流:输入是工具载荷 → 它把请求交给 original 原始输出 → 返回原本的 JSON 结果。

调用关系:这和 to_response_item 形成区别:普通模型可见内容可能被替换,但代码模式需要保留真实工具输出。

override_tool_exposure237–246 ↗
fn override_tool_exposure(
    handler: Arc<dyn CoreToolRuntime>,
    exposure: ToolExposure,
) -> Arc<dyn CoreToolRuntime>

作用:临时改写一个工具的可见性,比如把工具隐藏起来,或改变它是否展示给模型。它不改工具本体,只是在外面套一层外壳。

数据流:输入是原工具处理器和目标可见性 → 如果原本就一样,直接返回原处理器;否则创建 ExposureOverride 包装器 → 输出新的工具处理器引用。

调用关系:add_with_exposure 和 add_collaboration_tools 会用它在注册工具时调整曝光策略;后续调度仍然像使用普通工具一样使用它。

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

ExposureOverride::tool_name254–256 ↗
fn tool_name(&self) -> ToolName

作用:让改写可见性的包装器继续报告原工具的名字。

数据流:没有额外输入 → 转问内部 handler 的 tool_name → 输出原工具名。

调用关系:ToolRegistry 注册和查找工具时会依赖工具名;这个包装器只改可见性,不改身份。

ExposureOverride::spec258–260 ↗
fn spec(&self) -> ToolSpec

作用:让包装器继续使用原工具的规格说明。规格说明就是告诉模型这个工具叫什么、需要什么参数、能做什么。

数据流:没有额外输入 → 转问内部 handler 的 spec → 输出原工具规格。

调用关系:构建模型可见工具列表时会读取规格;这里保证包装后工具能力描述不丢失。

ExposureOverride::exposure262–264 ↗
fn exposure(&self) -> ToolExposure

作用:返回被强制指定的新可见性,而不是原工具自己的可见性。

数据流:没有额外输入 → 读取包装器保存的 exposure 字段 → 输出 ToolExposure。

调用关系:工具列表展示、隐藏工具判断、并行调用能力判断都会间接受它影响。

ExposureOverride::supports_parallel_tool_calls266–268 ↗
fn supports_parallel_tool_calls(&self) -> bool

作用:判断包装后的工具是否还能并行调用。隐藏工具会被直接视为不支持并行,其他情况再看原工具能力。

数据流:没有额外输入 → 先检查新可见性是否 Hidden,再询问原工具 → 输出是否支持并行。

调用关系:外层调度或模型工具声明会用这个结果;它避免隐藏工具还被当成可并行公开工具使用。

ExposureOverride::search_info270–272 ↗
fn search_info(&self) -> Option<ToolSearchInfo>

作用:把原工具的搜索信息原样透传出去。搜索信息用于工具搜索功能判断这个工具能否被检索到。

数据流:没有额外输入 → 转问内部 handler 的 search_info → 输出可选搜索信息。

调用关系:工具搜索相关流程读取它;包装器只影响曝光,不重新定义搜索元数据。

ExposureOverride::handle274–276 ↗
fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_>

作用:真正执行工具时,包装器不插手,直接把调用交给原工具。

数据流:输入是一次工具调用 → 转交给内部 handler.handle → 输出原工具的异步执行结果。

调用关系:ToolRegistry 调度到这个包装器时,最终仍会通过这里执行原工具逻辑。

ExposureOverride::matches_kind280–282 ↗
fn matches_kind(&self, payload: &ToolPayload) -> bool

作用:判断载荷类型是否匹配时,包装器直接沿用原工具的判断。

数据流:输入是工具调用载荷 → 转问内部 handler.matches_kind → 输出 true 或 false。

调用关系:ToolRegistry::dispatch_any_with_terminal_outcome 执行前会检查它;包装器不改变工具能处理的调用类型。

ExposureOverride::waits_for_runtime_cancellation284–286 ↗
fn waits_for_runtime_cancellation(&self) -> bool

作用:取消等待策略沿用原工具。包装器不会改变工具清理现场的需要。

数据流:没有额外输入 → 转问内部 handler → 输出是否等待取消清理。

调用关系:ToolRegistry::waits_for_runtime_cancellation 查询包装工具时会得到原工具的真实策略。

ExposureOverride::pre_tool_use_payload288–290 ↗
fn pre_tool_use_payload(&self, invocation: &ToolInvocation) -> Option<PreToolUsePayload>

作用:执行前给钩子的材料沿用原工具的生成方式。

数据流:输入是工具调用 → 转问内部 handler.pre_tool_use_payload → 输出可选的前置钩子载荷。

调用关系:ToolRegistry 执行前置钩子时会用到它;包装器不改变钩子看到的工具输入。

ExposureOverride::post_tool_use_payload292–298 ↗
fn post_tool_use_payload(
        &self,
        invocation: &ToolInvocation,
        result: &dyn ToolOutput,
    ) -> Option<PostToolUsePayload>

作用:执行后给钩子的材料也沿用原工具的生成方式。

数据流:输入是工具调用和工具输出 → 转问内部 handler.post_tool_use_payload → 输出可选的后置钩子载荷。

调用关系:handle_any_tool 调用它时,即使工具被包装过,后置钩子仍看到原工具定义的稳定格式。

ExposureOverride::with_updated_hook_input300–307 ↗
fn with_updated_hook_input(
        &self,
        invocation: ToolInvocation,
        updated_input: Value,
    ) -> Result<ToolInvocation, FunctionCallError>

作用:当前置钩子改写输入时,仍由原工具决定怎么把新输入变回工具调用。

数据流:输入是原调用和改写后的 JSON 输入 → 转交内部 handler.with_updated_hook_input → 输出新调用或错误。

调用关系:ToolRegistry::dispatch_any_with_terminal_outcome 在前置钩子改写输入后会走到这里;包装器不破坏工具自己的改写规则。

ExposureOverride::telemetry_tags309–314 ↗
fn telemetry_tags(
        &'a self,
        invocation: &'a ToolInvocation,
    ) -> BoxFuture<'a, ToolTelemetryTags>

作用:遥测标签沿用原工具提供的标签。

数据流:输入是工具调用 → 转问内部 handler.telemetry_tags → 输出标签列表。

调用关系:ToolRegistry 记录工具日志时会合并这些标签;包装可见性不会让工具的统计分类丢失。

ExposureOverride::create_diff_consumer316–318 ↗
fn create_diff_consumer(&self) -> Option<Box<dyn ToolArgumentDiffConsumer>>

作用:流式参数差异消费者也沿用原工具的实现。

数据流:没有额外输入 → 转问内部 handler.create_diff_consumer → 输出可选消费者。

调用关系:ToolRegistry::create_diff_consumer 查询包装工具时会通过这里拿到原工具的消费者。

ToolRegistry::new326–328 ↗
fn new(tools: HashMap<ToolName, Arc<dyn CoreToolRuntime>>) -> Self

作用:用一张已经整理好的工具表创建注册表。它是最基础的构造函数。

数据流:输入是“工具名到工具处理器”的映射表 → 保存到 ToolRegistry 里 → 输出一个可查询、可调度的注册表。

调用关系:ToolRegistry::from_tools、测试辅助函数以及部分测试会调用它;正常业务更常通过 from_tools 创建。

调用图:被 2 处调用(dispatch_notifies_tool_lifecycle_contributors, handler_looks_up_namespaced_aliases_explicitly)。

ToolRegistry::from_tools330–341 ↗
fn from_tools(tools: impl IntoIterator<Item = Arc<dyn CoreToolRuntime>>) -> Self

作用:把一串工具处理器整理成按名字查找的注册表,并检查有没有重名工具。

数据流:输入是一批工具 → 逐个读取工具名,放入 HashMap;如果发现重名,就报错或触发 panic → 输出 ToolRegistry。

调用关系:build_model_visible_specs_and_registry 等注册流程会用它建立正式工具表;多项测试也用它验证取消和生命周期行为。

调用图:调用 1 个内部函数(error_or_panic);被 3 处调用(cancellation_after_handler_finishes_preserves_completed_lifecycle, cancellation_waiting_for_runtime_cleanup_emits_only_aborted_lifecycle, build_model_visible_specs_and_registry);外部调用 3 个(new, new, format!)。

ToolRegistry::empty_for_test344–346 ↗
fn empty_for_test() -> Self

作用:创建一个空工具注册表,专门给测试用。

数据流:没有输入 → 创建空 HashMap → 输出没有任何工具的 ToolRegistry。

调用关系:测试 unsupported tool 这类场景时会用它,因为需要模拟“系统里没有这个工具”。

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

ToolRegistry::with_handler_for_test349–355 ↗
fn with_handler_for_test(handler: Arc<T>) -> Self

作用:创建一个只包含单个工具处理器的测试注册表,方便测试某个工具的调度行为。

数据流:输入是一个工具处理器 → 读取它的工具名,放进只有一项的映射表 → 输出 ToolRegistry。

调用关系:多个调度和追踪测试会用它快速搭建环境,不必注册完整工具集合。

调用图:被 3 处调用(dispatch_lifecycle_trace_records_direct_and_code_mode_requesters, dispatch_lifecycle_trace_records_incompatible_payload_failures, missing_code_mode_wait_traces_only_the_wait_tool_call);外部调用 2 个(from, new)。

ToolRegistry::tool357–359 ↗
fn tool(&self, name: &ToolName) -> Option<Arc<dyn CoreToolRuntime>>

作用:按工具名从注册表里取出工具处理器。返回的是共享引用,方便异步流程安全使用同一个工具。

数据流:输入是工具名 → 在内部 HashMap 查找并克隆 Arc 引用 → 输出工具处理器,找不到则输出空。

调用关系:create_diff_consumer、supports_parallel_tool_calls、waits_for_runtime_cancellation 和 dispatch_any_with_terminal_outcome 都通过它找工具。

调用图:被 4 处调用(create_diff_consumer, dispatch_any_with_terminal_outcome, supports_parallel_tool_calls, waits_for_runtime_cancellation)。

ToolRegistry::tool_names_for_test362–366 ↗
fn tool_names_for_test(&self) -> Vec<ToolName>

作用:列出测试注册表里的所有工具名,并排序,方便测试稳定比较。

数据流:没有额外输入 → 收集 HashMap 里的所有名字并排序 → 输出工具名列表。

调用关系:registered_tool_names_for_test 这类测试辅助会调用它,确认注册结果符合预期。

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

ToolRegistry::tool_exposure369–371 ↗
fn tool_exposure(&self, name: &ToolName) -> Option<ToolExposure>

作用:查询某个工具当前对模型的可见性,主要给测试验证用。

数据流:输入是工具名 → 找到工具后读取 exposure → 输出可见性;找不到则输出空。

调用关系:tool_exposure_for_test 会调用它,检查工具隐藏、公开或其他曝光设置是否正确。

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

ToolRegistry::create_diff_consumer373–378 ↗
fn create_diff_consumer(
        &self,
        name: &ToolName,
    ) -> Option<Box<dyn ToolArgumentDiffConsumer>>

作用:按工具名创建这个工具的流式参数差异消费者。如果工具不存在或不支持,就返回空。

数据流:输入是工具名 → 先查工具,再调用工具自己的 create_diff_consumer → 输出可选消费者。

调用关系:外层流式工具调用流程会通过它拿消费者;它本身只是注册表到具体工具之间的转发站。

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

ToolRegistry::supports_parallel_tool_calls380–383 ↗
fn supports_parallel_tool_calls(&self, name: &ToolName) -> Option<bool>

作用:查询某个工具是否支持并行调用。并行调用就是多个工具请求同时跑,能提速但要求工具自己安全。

数据流:输入是工具名 → 找到工具后询问 supports_parallel_tool_calls → 输出可选布尔值。

调用关系:tool_supports_parallel 等外层逻辑会调用它,决定是否允许这个工具和其他工具一起跑。

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

ToolRegistry::waits_for_runtime_cancellation385–388 ↗
fn waits_for_runtime_cancellation(&self, name: &ToolName) -> Option<bool>

作用:查询某个工具取消时是否需要等待运行时清理。

数据流:输入是工具名 → 找到工具后询问 waits_for_runtime_cancellation → 输出可选布尔值。

调用关系:tool_waits_for_runtime_cancellation 等取消控制逻辑会用它,决定取消工具时的等待策略。

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

ToolRegistry::dispatch_any391–397 ↗
async fn dispatch_any(
        &self,
        invocation: ToolInvocation,
    ) -> Result<AnyToolResult, FunctionCallError>

作用:用默认方式调度一次工具调用。它是较简单的入口,不关心外部是否已经记录过终态。

数据流:输入是一次 ToolInvocation → 调用 dispatch_any_with_terminal_outcome,并传入空的终态标记 → 输出统一工具结果或错误。

调用关系:它把真正复杂的调度工作交给 dispatch_any_with_terminal_outcome;适合不需要特殊取消终态协调的调用方。

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

ToolRegistry::dispatch_any_with_terminal_outcome403–668 ↗
async fn dispatch_any_with_terminal_outcome(
        &self,
        mut invocation: ToolInvocation,
        terminal_outcome_reached: Option<Arc<AtomicBool>>,
    ) -> Result<AnyToolResult, FunctionCa

作用:这是整个文件的核心调度流程:从接到工具调用,到检查、跑前置钩子、执行工具、跑后置钩子、记录日志和生命周期,全部在这里串起来。

数据流:输入是工具调用,以及一个可选的“终态是否已通知”标记 → 它读取会话、权限、工具表和遥测信息;检查工具是否存在、输入是否匹配;运行前置钩子,可能拦截或改写输入;执行工具并记录日志;成功后运行后置钩子,可能阻止结果或替换给模型看的内容;最后通知工具结束 → 输出 AnyToolResult,或返回给模型/系统的错误。

调用关系:dispatch_any 和 dispatch_tool_call_with_code_mode_result_inner 会调用它。它向下调用 run_pre_tool_use_hooks、handle_any_tool、run_post_tool_use_hooks、notify_tool_finish_if_unclaimed 等,是工具调用链路里的总指挥。

调用图:调用 13 个内部函数(record_additional_contexts, run_post_tool_use_hooks, run_pre_tool_use_hooks, emit_metric_for_tool_read, permission_profile_policy_tag, permission_profile_sandbox_tag, from_text, flat_tool_name, notify_tool_start, tool (+3 more));被 2 处调用(dispatch_any, dispatch_tool_call_with_code_mode_result_inner);外部调用 9 个(new, new, with_capacity, clone, format!, matches!, new, Fatal, RespondToModel)。

notify_tool_finish_if_unclaimed671–682 ↗
async fn notify_tool_finish_if_unclaimed(
    invocation: &ToolInvocation,
    terminal_outcome_reached: Option<&AtomicBool>,
    outcome: ToolCallOutcome,
) -> bool

作用:安全地通知“工具结束了”,并避免同一次工具调用被重复通知终态。

数据流:输入是工具调用、可选原子布尔标记和结果状态 → 如果标记显示别人已经通知过,就什么也不做并返回 false;否则设置标记并发送结束通知 → 返回 true。

调用关系:ToolRegistry::dispatch_any_with_terminal_outcome 在工具被前置钩子阻止、执行失败、执行成功等路径都会调用它,确保生命周期事件只发一次。

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

handle_any_tool684–709 ↗
async fn handle_any_tool(
    tool: &dyn CoreToolRuntime,
    invocation: ToolInvocation,
) -> Result<AnyToolResult, FunctionCallError>

作用:真正调用某个工具处理器,并把它的输出包装成统一结果。它还会处理“外部上下文污染内存模式”这类安全标记。

数据流:输入是工具处理器和工具调用 → 克隆调用编号和载荷,执行 tool.handle;如果输出含外部上下文且配置要求禁用相关记忆,就在状态库标记线程记忆模式被污染;然后生成后置钩子载荷 → 输出 AnyToolResult。

调用关系:ToolRegistry::dispatch_any_with_terminal_outcome 在日志包裹器里面调用它;它上接调度流程,下接具体工具的 handle 实现。

调用图:调用 2 个内部函数(post_tool_use_payload, mark_thread_memory_mode_polluted);外部调用 2 个(clone, handle)。

function_hook_tool_name711–722 ↗
fn function_hook_tool_name(invocation: &ToolInvocation) -> HookToolName

作用:把内部工具名转换成钩子系统使用的工具名。特殊地,spawn_agent 会被映射成钩子认识的标准名字。

数据流:输入是工具调用 → 如果是指定命名空间下的 spawn_agent,就返回专门的 spawn_agent 钩子名;否则把工具名压平成字符串 → 输出 HookToolName。

调用关系:CoreToolRuntime::pre_tool_use_payload 和 CoreToolRuntime::post_tool_use_payload 都用它,让前置/后置钩子看到一致的工具名。

调用图:调用 3 个内部函数(flat_tool_name, new, spawn_agent);被 2 处调用(post_tool_use_payload, pre_tool_use_payload);外部调用 1 个(matches!)。

function_hook_tool_input724–730 ↗
fn function_hook_tool_input(arguments: &str) -> Value

作用:把函数字符串参数转换成钩子更容易处理的 JSON 输入。

数据流:输入是一段参数字符串 → 如果是空白,就返回空 JSON 对象;如果能解析成 JSON,就返回解析后的值;解析失败则把原字符串当普通文本返回 → 输出 serde_json::Value。

调用关系:CoreToolRuntime::pre_tool_use_payload 会调用它,准备给前置钩子的 tool_input。

调用图:被 1 处调用(pre_tool_use_payload);外部调用 3 个(Object, new, from_str)。

unsupported_tool_call_message732–737 ↗
fn unsupported_tool_call_message(payload: &ToolPayload, tool_name: &ToolName) -> String

作用:为“不支持的工具调用”生成一条清楚的错误消息。

数据流:输入是工具载荷和工具名 → 如果是自定义工具载荷,就生成“unsupported custom tool call”;否则生成普通“unsupported call” → 输出错误字符串。

调用关系:ToolRegistry::dispatch_any_with_terminal_outcome 找不到工具时会调用它,然后把这条消息记录到遥测并返回给模型。

调用图:被 1 处调用(dispatch_any_with_terminal_outcome);外部调用 1 个(format!)。

tui/src/app/app_server_event_targets.rs源码 ↗
domain_logicrequest handling / cross-cutting

这个文件解决的是“这条服务器消息该给谁看”的问题。app server 会发来很多请求和通知,有些明确属于某个聊天线程,有些是整个应用都要知道的,还有些线程编号写坏了。这里用 ThreadId(线程编号,可以理解成每个聊天会话的门牌号)来判断归属。server_request_thread_id 从请求里尝试取出线程编号;server_notification_thread_target 则更细,把通知分成某个线程、线程编号无效、应用范围、全局四类。下面的测试专门覆盖几种容易混淆的情况,比如警告没有线程时算全局,MCP 服务器状态没有线程时算应用范围。

函数细节9
server_request_thread_id7–32 ↗
fn server_request_thread_id(request: &ServerRequest) -> Option<ThreadId>

作用:从一个服务器请求里找出它属于哪个聊天线程。有人处理请求时会用它来决定把审批、输入请求等送到哪个线程界面。

数据流:输入是一个 ServerRequest(服务器请求)。函数查看请求的具体种类:如果这种请求带有 thread_id,就把字符串形式的编号转换成 ThreadId;转换成功就输出 Some(ThreadId),转换失败或这种请求本来不属于线程就输出 None。它不改动请求本身。

调用关系:它在处理服务器请求时被 handle_server_request_event 调用。它只做一件事:借助 from_string 把文本编号变成可靠的 ThreadId,然后把结果交回给上层,让上层继续决定消息该路由到哪里。

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

server_notification_thread_target42–186 ↗
fn server_notification_thread_target(
    notification: &ServerNotification,
) -> ServerNotificationThreadTarget

作用:判断一条服务器通知应该投递到哪里:某个线程、整个应用、全局区域,还是标记为坏的线程编号。它是通知分发前的“地址识别器”。

数据流:输入是一条 ServerNotification(服务器通知)。函数先按通知类型取出可能存在的 thread_id;如果拿到线程编号,就用 from_string 检查并转换,成功输出 Thread(ThreadId),失败输出 InvalidThreadId;如果通知明确是应用级的 MCP 状态,就输出 AppScoped;如果没有线程归属,就输出 Global。它不修改通知,只返回目标分类。

调用关系:正式运行时,handle_server_notification_event 会调用它来决定通知发到哪里。测试函数也会直接调用它,验证警告、守护警告、MCP 启动状态、线程设置更新这些场景是否被分到正确目标。它内部把最终结果包装成 Thread 或 InvalidThreadId 等枚举值。

调用图:调用 1 个内部函数(from_string);被 7 处调用(guardian_warning_notifications_route_to_threads, mcp_startup_notifications_route_to_threads, mcp_startup_notifications_without_threads_are_app_scoped, thread_settings_updated_notifications_route_to_threads, warning_notifications_route_to_threads_when_thread_id_is_present, warning_notifications_without_threads_are_global, handle_server_notification_event);外部调用 2 个(InvalidThreadId, Thread)。

tests::test_thread_settings208–232 ↗
fn test_thread_settings() -> ThreadSettings

作用:构造一份测试用的线程设置。测试线程设置更新通知时,需要一份像真的一样的数据,但不想每个测试都重复写一大坨字段。

数据流:输入没有显式参数。函数用 test_path_buf 生成一个测试路径,再填好模型、沙盒、审批策略、协作模式等字段,最后输出一个 ThreadSettings 对象。它只生成测试数据,不影响真实配置。

调用关系:它被 tests::thread_settings_updated_notifications_route_to_threads 使用,作为构造 ThreadSettingsUpdated 通知的配套材料。它属于测试辅助函数,不参与真实运行流程。

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

tests::warning_notifications_without_threads_are_global235–244 ↗
fn warning_notifications_without_threads_are_global()

作用:验证没有线程编号的普通警告会被当成全局通知。这样警告不会被错误塞进某个聊天线程里。

数据流:测试先造一条 Warning 通知,其中 thread_id 是 None。然后把它传给 server_notification_thread_target,得到目标分类。最后用 assert_eq! 检查结果是不是 Global。

调用关系:这个测试直接覆盖 server_notification_thread_target 的一个边界情况:警告可以没有线程编号。它帮助保证通知分发逻辑以后改动时不会把这种消息误分到线程。

调用图:调用 1 个内部函数(server_notification_thread_target);外部调用 2 个(Warning, assert_eq!)。

tests::warning_notifications_route_to_threads_when_thread_id_is_present247–257 ↗
fn warning_notifications_route_to_threads_when_thread_id_is_present()

作用:验证普通警告如果带了线程编号,就会被送到对应线程。这样用户能在正确的聊天上下文里看到警告。

数据流:测试先用 new 生成一个新的 ThreadId,把它转成字符串放进 Warning 通知。然后调用 server_notification_thread_target。函数返回后,测试确认结果是 Thread,并且里面的编号就是刚才生成的那个。

调用关系:这个测试检查 server_notification_thread_target 对 Warning 通知的另一种情况。它和“没有线程编号时是全局”的测试一起,说明 Warning 的路由取决于有没有 thread_id。

调用图:调用 2 个内部函数(new, server_notification_thread_target);外部调用 2 个(Warning, assert_eq!)。

tests::guardian_warning_notifications_route_to_threads260–270 ↗
fn guardian_warning_notifications_route_to_threads()

作用:验证 GuardianWarning(守护/安全检查类警告)一定会按线程编号投递到对应线程。这样安全提醒不会跑错地方。

数据流:测试生成一个 ThreadId,把它写进 GuardianWarning 通知。然后调用 server_notification_thread_target,最后断言结果是 Thread,并且线程编号一致。

调用关系:这个测试直接调用 server_notification_thread_target,专门保护 GuardianWarning 的分发规则。它说明这类警告不是全局消息,而是跟某个线程绑定。

调用图:调用 2 个内部函数(new, server_notification_thread_target);外部调用 2 个(GuardianWarning, assert_eq!)。

tests::mcp_startup_notifications_route_to_threads273–286 ↗
fn mcp_startup_notifications_route_to_threads()

作用:验证 MCP 服务器状态通知如果带线程编号,就会送到该线程。MCP 可以粗略理解成外部工具服务,这里是在测试它启动状态的消息归属。

数据流:测试生成一个 ThreadId,构造一条 McpServerStatusUpdated 通知,并把 thread_id 填进去。之后调用 server_notification_thread_target,最后检查返回的是 Thread(thread_id)。

调用关系:这个测试覆盖 server_notification_thread_target 对 MCP 状态通知的线程级处理。它和无线程编号的 MCP 测试成对存在,确保两种情况分得清。

调用图:调用 2 个内部函数(new, server_notification_thread_target);外部调用 2 个(McpServerStatusUpdated, assert_eq!)。

tests::mcp_startup_notifications_without_threads_are_app_scoped289–301 ↗
fn mcp_startup_notifications_without_threads_are_app_scoped()

作用:验证 MCP 服务器状态通知如果没有线程编号,就算作应用范围通知,而不是普通全局通知。这样界面可以把它放在更合适的应用级位置。

数据流:测试构造一条 McpServerStatusUpdated 通知,其中 thread_id 是 None。然后调用 server_notification_thread_target,最后断言结果是 AppScoped。

调用关系:这个测试保护 server_notification_thread_target 里一个特殊规则:MCP 状态没有线程时返回 AppScoped。它说明这个文件不只是简单地“有线程就线程、没线程就全局”,还有少数应用级例外。

调用图:调用 1 个内部函数(server_notification_thread_target);外部调用 2 个(McpServerStatusUpdated, assert_eq!)。

tests::thread_settings_updated_notifications_route_to_threads304–315 ↗
fn thread_settings_updated_notifications_route_to_threads()

作用:验证线程设置更新通知会被送回对应线程。这样某个聊天线程的模型、权限、沙盒等设置变化,不会影响或显示到别的线程。

数据流:测试先生成 ThreadId,再用 test_thread_settings 做一份线程设置,构造 ThreadSettingsUpdated 通知。随后调用 server_notification_thread_target,并检查结果是同一个 ThreadId 对应的 Thread。

调用关系:这个测试调用 server_notification_thread_target,并依赖 tests::test_thread_settings 提供完整测试数据。它确保线程设置变化这种通知仍然按照线程编号正确分发。

调用图:调用 2 个内部函数(new, server_notification_thread_target);外部调用 3 个(ThreadSettingsUpdated, assert_eq!, test_thread_settings)。

应用服务器功能适配器

这些文件涵盖应用服务器中更窄的特定功能 RPC 适配器,用于文件系统、插件、市场、搜索、反馈、git、远程控制和认证相关操作。

app-server/src/attestation.rs源码 ↗
orchestrationrequest handling

这个文件解决的是一个很实际的问题:服务器自己不能凭空生成某些“可信证明”,需要去问当前会话里支持证明功能的客户端。如果没有这层代码,后续发出的请求可能缺少证明头,或者遇到客户端超时、返回坏数据时不知道怎么表达失败。它像一个中转柜台:先从线程状态里找出哪个连接能办理证明;再通过 outgoing 消息通道发一个“请生成证明”的请求;最多等 100 毫秒,避免整个请求被卡住;拿到结果后,把 token 和状态码装进一个小 JSON 信封里。即使失败,它也尽量返回一个带失败原因的头值,比如超时、请求失败、请求被取消、返回格式不对。这样下游系统不仅知道“有没有 token”,也知道为什么没有。

函数细节8
app_server_attestation_provider21–29 ↗
fn app_server_attestation_provider(
    outgoing: Arc<OutgoingMessageSender>,
    thread_state_manager: ThreadStateManager,
) -> Arc<dyn AttestationProvider>

作用:创建一个给核心系统使用的证明提供者。调用方把发送消息的通道和线程状态交进来,它返回一个统一接口,之后核心系统就能通过这个接口要证明头。

数据流:进去的是 outgoing 消息发送器和 thread_state_manager 线程状态管理器 → 它把 outgoing 转成弱引用,也就是“不强行让发送器一直活着”的引用,再和线程状态一起放进 AppServerAttestationProvider → 出来的是一个可共享的 AttestationProvider 对象,供别处按接口调用。

调用关系:这是本文件对外搭桥的入口。它内部用 Arc::downgrade 保存 outgoing,避免这个证明提供者反过来延长 outgoing 的生命周期;之后真正生成请求头的工作,会在 AppServerAttestationProvider::header_for_request 里发生。

调用图:外部调用 2 个(downgrade, new)。

AppServerAttestationProvider::fmt37–41 ↗
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result

作用:给 AppServerAttestationProvider 提供调试打印的样子。它不会把内部复杂字段展开,只打印结构名,方便日志或调试时识别对象类型。

数据流:进去的是一个格式化器 formatter → 它告诉 formatter 打印一个名叫 AppServerAttestationProvider 的调试结构 → 出来的是格式化是否成功的结果,不改变证明流程里的业务数据。

调用关系:这是 Rust 调试输出机制会自动用到的小配件。它不参与证明请求本身,只在有人打印或记录这个对象时被调用,并把具体格式化工作交给标准库的 debug_struct。

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

AppServerAttestationProvider::header_for_request45–60 ↗
fn header_for_request(&self, context: AttestationContext) -> GenerateAttestationFuture<'_>

作用:在需要发请求时,异步生成一个可以放进 HTTP 头里的证明值。它会先确认消息通道还活着,再去找客户端生成证明。

数据流:进去的是 AttestationContext,上下文里包含 thread_id,也就是这次请求属于哪个线程 → 它先把弱引用升级成可用的 outgoing;如果通道已经没了,就直接返回 None;如果还在,就复制线程状态管理器,并启动异步流程去请求证明 → 出来的是一个未来会完成的结果:可能是合法的 HeaderValue,也可能是 None。

调用关系:这是核心系统通过 AttestationProvider 接口真正会调用的方法。它自己不直接处理所有细节,而是把找连接、发请求、等结果、包装 JSON 的工作交给 request_attestation_header_value_with_timeout;最后再把字符串转成 HTTP 头专用的 HeaderValue。

调用图:调用 1 个内部函数(request_attestation_header_value_with_timeout);外部调用 3 个(pin, upgrade, clone)。

request_attestation_header_value_with_timeout63–128 ↗
async fn request_attestation_header_value_with_timeout(
    outgoing: Arc<OutgoingMessageSender>,
    thread_state_manager: ThreadStateManager,
    thread_id: codex_protocol::ThreadId,
    timeout_dur

作用:向某个支持证明功能的连接发送“生成证明”的请求,并在限定时间内等结果。它负责把成功和各种失败都转换成统一的头部字符串。

数据流:进去的是 outgoing 消息发送器、线程状态管理器、thread_id 和超时时长 → 它先问线程状态管理器:这个线程里第一个能生成证明的连接是谁;找不到就返回 None。找到后,它只向这个连接发送 AttestationGenerate 请求,然后等待回应。成功时,它把回应解析成 AttestationGenerateResponse,取出 token;失败、取消、超时或返回格式错误时,它写日志并选择对应状态码 → 出来的是一个 JSON 字符串,比如成功时带 token,失败时只带版本号和状态码;超时时还会主动取消刚才发出的请求。

调用关系:它是 AppServerAttestationProvider::header_for_request 背后的主流程。它会调用 first_attestation_capable_connection_for_thread 找可用连接,调用 outgoing 的发送能力把请求交给客户端,再调用 app_server_attestation_header_value 把最终状态包装成头值。

调用图:调用 2 个内部函数(app_server_attestation_header_value, first_attestation_capable_connection_for_thread);被 1 处调用(header_for_request);外部调用 3 个(AttestationGenerate, timeout, warn!)。

AppServerAttestationStatus::code140–148 ↗
fn code(self) -> u8

作用:把证明生成的结果状态变成一个小数字。这样放进 HTTP 头里的 JSON 更短,也方便接收方按固定编号理解发生了什么。

数据流:进去的是一个状态,比如 Ok、Timeout、RequestFailed → 它按固定映射转成数字:成功是 0,超时是 1,请求失败是 2,请求取消是 3,返回格式错误是 4 → 出来的是这个状态对应的 u8 数字,不改动任何外部数据。

调用关系:它只被 app_server_attestation_header_value 使用。后者在组装 JSON 信封时,需要把人能读懂的枚举状态转换成机器更容易传输的状态码。

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

app_server_attestation_header_value159–170 ↗
fn app_server_attestation_header_value(
    status: AppServerAttestationStatus,
    token: Option<&str>,
) -> Option<String>

作用:把证明结果包装成一个短小的 JSON 字符串,作为 HTTP 头的内容。成功时会带 token,失败时会带失败状态码但不带 token。

数据流:进去的是状态和可选 token → 它创建一个信封对象,里面有版本号 v、状态码 s,以及可选的 token 字段 t;如果 token 是 None,就不写 t 字段;然后把这个信封序列化成 JSON 字符串 → 出来的是 Some(JSON 字符串),如果序列化意外失败就记录警告并返回 None。

调用关系:这是证明流程最后的打包步骤。request_attestation_header_value_with_timeout 在成功、超时、请求失败、请求取消、返回格式错误等分支都会调用它;它内部又调用 AppServerAttestationStatus::code 来拿状态数字。

调用图:调用 1 个内部函数(code);被 1 处调用(request_attestation_header_value_with_timeout);外部调用 1 个(to_string)。

tests::app_server_attestation_header_value_wraps_opaque_client_payloads179–187 ↗
fn app_server_attestation_header_value_wraps_opaque_client_payloads()

作用:测试成功场景下,客户端给的 token 会被原样放进 JSON 信封里。这里的 token 被当成不透明内容,也就是本文件不理解、不拆开,只负责转交。

数据流:进去的是一个成功状态 Ok 和字符串 v1.opaque-client-payload → 测试调用 app_server_attestation_header_value 生成 JSON → 出来后用断言检查结果必须正好是包含版本号、成功状态码和 token 的字符串。

调用关系:这是给 app_server_attestation_header_value 的保护性测试。它确保以后有人改包装格式时,不会意外破坏成功响应中 token 的传递方式。

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

tests::app_server_attestation_header_value_reports_app_server_failures190–219 ↗
fn app_server_attestation_header_value_reports_app_server_failures()

作用:测试失败场景下,头部 JSON 会正确报告失败原因,而且不会带 token。这样接收方能区分是超时、请求失败、取消,还是返回格式坏了。

数据流:进去的是四种失败状态:Timeout、RequestFailed、RequestCanceled、MalformedResponse,并且 token 都是 None → 测试分别调用 app_server_attestation_header_value → 出来后逐个断言 JSON 字符串里的状态码分别是 1、2、3、4,并且没有 t 字段。

调用关系:这是 app_server_attestation_header_value 的另一组保护性测试。它对应 request_attestation_header_value_with_timeout 里的多个失败分支,确保这些分支最后写进头里的状态码不会乱。

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

app-server/src/fs_watch.rs源码 ↗
domain_logicrequest handling / cross-cutting

这个文件可以理解成服务器里的“文件门卫”。客户端说“帮我看一下这个文件”,这里就登记一条监听记录,并启动一个后台小任务等文件变化。文件真的变了以后,它不会每次细小抖动都立刻通知,而是用 200 毫秒的防抖,也就是把短时间内连续发生的变化合并一下,再发一条通知,避免消息刷屏。每条监听用“连接编号 + watch_id”一起当钥匙,所以不同客户端可以用同样的 watch_id,但同一个客户端不能重复用。取消监听时,它会等后台任务确认停下,确保“取消成功”的回复发出后,不会再冒出旧通知。连接断开时,它会清掉这个连接名下的监听记录,防止留下没人用的登记。

函数细节11
FsWatchManager::new54–63 ↗
fn new(outgoing: Arc<OutgoingMessageSender>) -> Self

作用:创建一个文件监听管理器,给服务器处理“监听文件变化”的请求用。它会优先使用真正的文件监听器;如果系统不支持或创建失败,就退回到一个什么也不做的空监听器,避免整个服务器启动失败。

数据流:进去的是一个 outgoing,也就是服务器给客户端发消息的通道。函数尝试创建 FileWatcher(文件监听器);成功就保存它,失败就记录一条警告并换成 noop(空实现,意思是不真正监听)。最后把消息通道、监听器和空的登记表组合成一个 FsWatchManager 返回。

调用关系:这是正常创建 FsWatchManager 的入口。它自己不直接组装所有字段,而是把选好的 FileWatcher 交给 FsWatchManager::new_with_file_watcher;测试里则会绕过它,直接用 new_with_file_watcher 塞入空监听器,方便验证登记逻辑。

调用图:调用 2 个内部函数(new, noop);被 1 处调用(new);外部调用 3 个(new, new_with_file_watcher, warn!)。

FsWatchManager::new_with_file_watcher65–74 ↗
fn new_with_file_watcher(
        outgoing: Arc<OutgoingMessageSender>,
        file_watcher: Arc<FileWatcher>,
    ) -> Self

作用:用指定的文件监听器创建管理器。这个函数主要给测试或特殊场景用,因为它允许外面传入一个假的或空的监听器。

数据流:进去的是发消息用的 outgoing 和已经准备好的 file_watcher。函数新建一个共享状态 state,里面一开始没有任何监听记录,然后把三样东西包进 FsWatchManager 返回。

调用关系:FsWatchManager::new 会在挑好真实或空的 FileWatcher 后调用它。测试辅助函数 tests::manager_with_noop_watcher 也会调用它,用空监听器做出一个不会碰真实文件系统事件的管理器。

调用图:被 1 处调用(manager_with_noop_watcher);外部调用 3 个(new, new, default)。

FsWatchManager::watch76–144 ↗
async fn watch(
        &self,
        connection_id: ConnectionId,
        params: FsWatchParams,
    ) -> Result<FsWatchResponse, JSONRPCErrorError>

作用:处理客户端的“开始监听这个文件”请求。它会登记这个监听,并在后台等文件变化,变化后把通知发回发起请求的那个连接。

数据流:进去的是连接编号 connection_id 和监听参数 params,里面有 watch_id 和文件路径。函数先用“连接编号 + watch_id”组成唯一钥匙;如果同一个连接已经用过这个 watch_id,就返回一个 JSON-RPC 错误(JSON-RPC 是一种客户端和服务器互发请求/回复的格式)。如果没重复,它会向底层 FileWatcher 订阅这个路径,保存取消用的通道和注册信息,然后启动一个 tokio 后台任务(tokio 是 Rust 常用的异步运行器)。后台任务收到文件变化后,先防抖合并,再把变化路径排序,最后通过 outgoing 发 FsChanged 通知。函数本身马上返回 FsWatchResponse,表示监听登记成功。

调用关系:这是客户端请求开始监听时的核心流程。它会调用 invalid_request 生成重复 watch_id 的错误,会用底层 FileWatcher 注册路径,还会把真正等待变化和发通知的工作交给 tokio::spawn 创建的后台任务。之后 FsWatchManager::unwatch 可以通过保存的 terminate_tx 叫这个后台任务停下。

调用图:调用 2 个内部函数(invalid_request, new);被 1 处调用(watch);外部调用 7 个(FsChanged, format!, channel, pin!, select!, spawn, vec!)。

FsWatchManager::unwatch146–164 ↗
async fn unwatch(
        &self,
        connection_id: ConnectionId,
        params: FsUnwatchParams,
    ) -> Result<FsUnwatchResponse, JSONRPCErrorError>

作用:处理客户端的“不要再监听了”请求。它只会取消同一个连接自己创建的监听,不会让别的连接代删。

数据流:进去的是连接编号和要取消的 watch_id。函数用这两个值拼出钥匙,到登记表里删除对应记录;如果找到了,就通过一次性通道 oneshot(只发送一次消息的小管道)通知后台任务退出,并等待后台任务确认结束。最后返回一个空的 FsUnwatchResponse,表示取消流程结束;如果本来就没有这条监听,也会当作成功。

调用关系:它和 FsWatchManager::watch 是一对:watch 登记并启动后台任务,unwatch 删除登记并叫任务停下。这里等待 done_rx 的目的很重要:保证取消响应之后,不会再发出这条监听的旧文件变化通知。

调用图:被 1 处调用(unwatch);外部调用 1 个(channel)。

FsWatchManager::connection_closed166–172 ↗
async fn connection_closed(&self, connection_id: ConnectionId)

作用:当某个客户端连接断开时,清理这个连接名下的所有监听记录。这样服务器不会一直记着已经没人接收的监听。

数据流:进去的是断开的 connection_id。函数锁住共享状态,从登记表里筛掉所有属于这个连接的 WatchKey。出来没有返回值,但内部状态会少掉这些监听记录。

调用关系:这是连接生命周期结束时会用到的清理动作。它不处理某个具体 watch_id,而是按连接编号批量清理,和 FsWatchManager::watch 保存的“连接编号 + watch_id”钥匙设计配合使用。

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

tests::absolute_path184–191 ↗
fn absolute_path(path: PathBuf) -> AbsolutePathBuf

作用:测试用的小工具,把普通路径转成项目要求的绝对路径类型。它顺便检查测试没有误传相对路径。

数据流:进去的是 PathBuf,也就是一段文件路径。函数先断言它必须是绝对路径;如果不是,测试直接失败。然后把它转换成 AbsolutePathBuf 并返回。

调用关系:多个测试在调用 FsWatchManager::watch 前都会用它准备路径。这样测试关注监听逻辑,不用在每个测试里重复写路径校验和转换代码。

调用图:调用 1 个内部函数(try_from);外部调用 1 个(assert!)。

tests::manager_with_noop_watcher193–203 ↗
fn manager_with_noop_watcher() -> FsWatchManager

作用:测试用的小工厂,做出一个不会真正监听文件系统的 FsWatchManager。这样测试可以稳定地检查登记、取消和去重逻辑,而不受操作系统文件事件影响。

数据流:函数没有外部输入。它创建一个很小的消息通道,创建一个关闭 analytics(统计上报)的 OutgoingMessageSender,再配上 FileWatcher::noop 这个空监听器,最后调用 FsWatchManager::new_with_file_watcher 返回测试管理器。

调用关系:所有测试基本都从这里拿管理器。它把真实文件监听替换成空实现,让测试只验证本文件自己的状态变化,比如 entries 里有没有正确的 WatchKey。

调用图:调用 4 个内部函数(disabled, new_with_file_watcher, new, noop);外部调用 2 个(new, channel)。

tests::watch_uses_client_id_and_tracks_the_owner_scoped_entry206–235 ↗
async fn watch_uses_client_id_and_tracks_the_owner_scoped_entry()

作用:这个测试确认:开始监听后,管理器登记的是“连接编号 + watch_id”,而不是只记 watch_id。这样同名 watch_id 在不同连接之间不会混淆。

数据流:测试先创建临时目录和一个 HEAD 文件,再用空监听器管理器发起 watch 请求。然后它检查返回的路径就是传入路径,并读取内部 state,确认 entries 里正好有一条属于 ConnectionId(1) 和 watch-head 的记录。

调用关系:它调用 tests::manager_with_noop_watcher 准备管理器,调用 tests::absolute_path 准备路径,再调用 FsWatchManager::watch。这个测试覆盖了 watch 的成功登记路径。

调用图:外部调用 6 个(new, new, absolute_path, manager_with_noop_watcher, assert_eq!, write)。

tests::unwatch_is_scoped_to_the_connection_that_created_the_watch238–280 ↗
async fn unwatch_is_scoped_to_the_connection_that_created_the_watch()

作用:这个测试确认:一个连接不能取消另一个连接创建的监听。它防止客户端之间互相干扰。

数据流:测试先让 ConnectionId(1) 创建 watch-head 监听。然后 ConnectionId(2) 尝试取消同名 watch_id,内部登记应该还在。接着 ConnectionId(1) 自己取消,登记才应该消失。

调用关系:它先通过 FsWatchManager::watch 建立记录,再两次调用 FsWatchManager::unwatch。这个测试专门验证 unwatch 使用“连接编号 + watch_id”作为范围,而不是只看 watch_id。

调用图:外部调用 6 个(new, new, absolute_path, manager_with_noop_watcher, assert!, write)。

tests::watch_rejects_duplicate_id_for_the_same_connection283–315 ↗
async fn watch_rejects_duplicate_id_for_the_same_connection()

作用:这个测试确认:同一个连接不能重复使用同一个 watch_id。否则后面取消或通知时就不知道该对应哪一条监听。

数据流:测试创建两个不同文件。ConnectionId(1) 先用 watch-head 监听第一个文件,应该成功;再用同一个 watch_id 监听第二个文件,应该失败。最后检查错误消息是“watchId already exists: watch-head”,并确认登记表里仍然只有第一条记录。

调用关系:它调用 FsWatchManager::watch 两次,第二次触发 watch 里的重复检查,也就是 Entry::Occupied 分支和 invalid_request 错误。这个测试保护 watch_id 去重规则不被改坏。

调用图:外部调用 6 个(new, new, absolute_path, manager_with_noop_watcher, assert_eq!, write)。

tests::connection_closed_removes_only_that_connections_watches318–376 ↗
async fn connection_closed_removes_only_that_connections_watches()

作用:这个测试确认:连接断开时,只清理这个连接自己的监听,不会误删其他连接的监听。

数据流:测试先创建三个文件,让 ConnectionId(1) 建两个监听,让 ConnectionId(2) 建一个监听。然后调用 connection_closed(ConnectionId(1))。最后检查内部登记表只剩 ConnectionId(2) 的那条,并顺便确认第一次 watch 返回的路径没变。

调用关系:它通过多次 FsWatchManager::watch 准备状态,再调用 FsWatchManager::connection_closed 执行批量清理。这个测试覆盖连接关闭时的收尾流程,确保多客户端同时使用时不会互相影响。

调用图:外部调用 6 个(new, new, absolute_path, manager_with_noop_watcher, assert_eq!, write)。

app-server/src/request_processors/fs_processor.rs源码 ↗
orchestrationrequest handling / connection teardown

这个文件解决的是“远端客户端怎么安全、统一地操作服务器文件”的问题。客户端发来的请求里只有路径、内容、选项等信息,这里先把普通路径变成系统内部使用的 PathUri(可以理解成更规范的文件地址),再从 EnvironmentManager 里拿到本地文件系统。如果没有配置本地文件系统,就直接报内部错误,避免后面乱操作。读文件时,它把读到的二进制内容转成 base64(一种把任意字节变成普通文本的编码),方便通过 JSON 传回去;写文件时则反过来先解码 base64。目录、元数据、删除、复制这些操作都只是把请求参数整理好后交给底层文件系统。文件监听不直接操作磁盘,而是交给 FsWatchManager 记住哪个连接在看哪个路径;连接断开时也会通知它清理。最后,map_fs_error 统一把底层文件错误翻译成“请求有问题”或“服务器出问题”。

函数细节13
FsRequestProcessor::new43–51 ↗
fn new(
        environment_manager: Arc<EnvironmentManager>,
        fs_watch_manager: FsWatchManager,
    ) -> Self

作用:创建一个文件请求处理器,把“怎么找到文件系统”和“怎么管理文件监听”这两样工具装进去。后面所有文件请求都靠这个对象来转发和协调。

数据流:进去的是一个 EnvironmentManager(环境管理器,用来找到当前可用的本地文件系统)和一个 FsWatchManager(文件变化监听管理器)→ 函数把它们保存到 FsRequestProcessor 里 → 出来的是一个可以处理文件相关请求的新对象,不会立刻读写任何文件。

调用关系:它通常在上层初始化处理器时被调用,作为文件请求这条通道的组装步骤。之后 handle_initialized_client_request 这类请求分发逻辑会通过这个对象调用读写、删除、监听等具体方法。

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

FsRequestProcessor::file_system53–58 ↗
fn file_system(&self) -> Result<Arc<dyn ExecutorFileSystem>, JSONRPCErrorError>

作用:取出当前可用的本地文件系统。它像开工前先确认“工具箱在不在”,没有文件系统就马上返回错误。

数据流:进去的是处理器自己保存的 EnvironmentManager → 它尝试取得本地环境,再从环境里拿文件系统对象 → 成功时出来一个可共享的文件系统接口,失败时出来一个 JSON-RPC 内部错误,说明本地文件系统没有配置。

调用关系:几乎所有真正碰文件的方法都会先调用它,比如读文件、写文件、建目录、删文件、复制、监听和取消监听。它把“有没有本地文件系统”这个检查集中到一处,避免每个方法各写一遍。

调用图:被 9 处调用(copy, create_directory, get_metadata, read_directory, read_file, remove, unwatch, watch, write_file)。

FsRequestProcessor::connection_closed60–62 ↗
async fn connection_closed(&self, connection_id: ConnectionId)

作用:当某个客户端连接断开时,通知文件监听管理器清理这个连接留下的监听任务。否则就像人已经走了,系统还一直替他盯着文件,浪费资源。

数据流:进去的是断开的 ConnectionId(连接编号)→ 函数把这个编号交给 FsWatchManager → 出来没有业务数据,但监听管理器会把这个连接相关的监听记录清掉。

调用关系:它在连接关闭流程里被上层调用,自己把清理工作交给 fs_watch_manager.connection_closed。它不处理普通文件读写,只负责断线后的收尾。

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

FsRequestProcessor::read_file64–77 ↗
async fn read_file(
        &self,
        params: FsReadFileParams,
    ) -> Result<FsReadFileResponse, JSONRPCErrorError>

作用:读取客户端指定的文件,并把文件内容用 base64 文本返回。这样即使文件里是图片、压缩包这类二进制内容,也能安全塞进 JSON 响应。

数据流:进去的是 FsReadFileParams,里面有文件路径 → 它把路径转成 PathUri,拿到文件系统,异步读取文件字节,并把底层错误交给 map_fs_error 翻译 → 出来的是 FsReadFileResponse,里面的 data_base64 是文件内容的 base64 字符串。

调用关系:客户端发起读文件请求后,handle_initialized_client_request 会调用它。它先调用 file_system 确认有文件系统,再用 from_abs_path 规范化路径,最后交给底层文件系统读取。

调用图:调用 2 个内部函数(file_system, from_abs_path);被 1 处调用(handle_initialized_client_request)。

FsRequestProcessor::write_file79–94 ↗
async fn write_file(
        &self,
        params: FsWriteFileParams,
    ) -> Result<FsWriteFileResponse, JSONRPCErrorError>

作用:把客户端传来的 base64 文件内容写到指定路径。它先检查内容是不是合法 base64,避免把乱码当成文件写进去。

数据流:进去的是 FsWriteFileParams,包含路径和 data_base64 → 它先把 base64 解成原始字节,解码失败就返回“请求无效”;然后把路径转成 PathUri,拿到文件系统并写入字节 → 成功时出来一个空的 FsWriteFileResponse,表示写完了;失败时返回整理过的错误。

调用关系:客户端发起写文件请求时由 handle_initialized_client_request 调用。它自己负责 base64 解码和路径转换,再通过 file_system 找到底层文件系统执行写入。

调用图:调用 2 个内部函数(file_system, from_abs_path);被 1 处调用(handle_initialized_client_request)。

FsRequestProcessor::create_directory96–112 ↗
async fn create_directory(
        &self,
        params: FsCreateDirectoryParams,
    ) -> Result<FsCreateDirectoryResponse, JSONRPCErrorError>

作用:创建客户端指定的目录。默认会递归创建,也就是父目录不存在时一起建好,像执行“mkdir -p”。

数据流:进去的是 FsCreateDirectoryParams,包含路径和可选的 recursive 选项 → 它把路径转成 PathUri,拿到文件系统,并构造 CreateDirectoryOptions;如果客户端没说 recursive,就默认 true → 成功时出来空响应,失败时返回翻译后的错误。

调用关系:目录创建请求由 handle_initialized_client_request 分发到这里。它先用 file_system 拿执行工具,再把实际创建工作交给底层文件系统。

调用图:调用 2 个内部函数(file_system, from_abs_path);被 1 处调用(handle_initialized_client_request)。

FsRequestProcessor::get_metadata114–131 ↗
async fn get_metadata(
        &self,
        params: FsGetMetadataParams,
    ) -> Result<FsGetMetadataResponse, JSONRPCErrorError>

作用:查询一个路径的基本信息,比如它是不是文件、是不是目录、是不是符号链接,以及创建和修改时间。它不会读取文件内容,只看“身份证信息”。

数据流:进去的是 FsGetMetadataParams,主要是路径 → 它把路径转成 PathUri,拿文件系统查询元数据 → 出来的是 FsGetMetadataResponse,包含 is_directory、is_file、is_symlink、created_at_ms、modified_at_ms 等字段。

调用关系:客户端想知道某个路径是什么类型时,handle_initialized_client_request 会调用它。它依赖 file_system 和 from_abs_path 做准备,然后把查询交给底层文件系统。

调用图:调用 2 个内部函数(file_system, from_abs_path);被 1 处调用(handle_initialized_client_request)。

FsRequestProcessor::read_directory133–153 ↗
async fn read_directory(
        &self,
        params: FsReadDirectoryParams,
    ) -> Result<FsReadDirectoryResponse, JSONRPCErrorError>

作用:列出一个目录里的条目,并告诉客户端每个条目叫什么、是文件还是目录。它相当于给客户端做一次“打开文件夹看看里面有什么”。

数据流:进去的是 FsReadDirectoryParams,包含目录路径 → 它把路径转成 PathUri,拿文件系统读取目录列表 → 再把底层条目转换成协议里的 FsReadDirectoryEntry → 出来的是 FsReadDirectoryResponse,里面有 entries 列表。

调用关系:目录读取请求由 handle_initialized_client_request 交给它。它负责把底层文件系统返回的目录项改成客户端协议需要的形状。

调用图:调用 2 个内部函数(file_system, from_abs_path);被 1 处调用(handle_initialized_client_request)。

FsRequestProcessor::remove155–172 ↗
async fn remove(
        &self,
        params: FsRemoveParams,
    ) -> Result<FsRemoveResponse, JSONRPCErrorError>

作用:删除指定文件或目录。默认 recursive 和 force 都是 true,意思是目录可以连里面一起删,目标不存在或类似情况也尽量不让操作失败。

数据流:进去的是 FsRemoveParams,包含路径,以及可选的 recursive、force → 它把路径转成 PathUri,拿文件系统,并用默认值补齐删除选项 → 成功时出来空的 FsRemoveResponse;如果底层删除失败,就返回经过 map_fs_error 转换的错误。

调用关系:删除请求由 handle_initialized_client_request 调用这里。它不自己删除文件,而是整理好 RemoveOptions 后交给底层文件系统执行。

调用图:调用 2 个内部函数(file_system, from_abs_path);被 1 处调用(handle_initialized_client_request)。

FsRequestProcessor::copy174–192 ↗
async fn copy(
        &self,
        params: FsCopyParams,
    ) -> Result<FsCopyResponse, JSONRPCErrorError>

作用:把一个文件或目录从源路径复制到目标路径。是否递归复制由请求里的 recursive 选项决定。

数据流:进去的是 FsCopyParams,包含 source_path、destination_path 和 recursive → 它分别把源路径和目标路径转成 PathUri,拿到文件系统,再构造 CopyOptions → 成功时出来空的 FsCopyResponse,失败时返回整理后的错误。

调用关系:复制请求由 handle_initialized_client_request 分发到它。它负责把协议参数变成底层文件系统能理解的参数,然后调用文件系统的复制能力。

调用图:调用 2 个内部函数(file_system, from_abs_path);被 1 处调用(handle_initialized_client_request)。

FsRequestProcessor::watch194–201 ↗
async fn watch(
        &self,
        connection_id: ConnectionId,
        params: FsWatchParams,
    ) -> Result<FsWatchResponse, JSONRPCErrorError>

作用:让某个客户端连接开始监听文件或目录变化。它先确认本地文件系统存在,再把真正的监听登记交给 FsWatchManager。

数据流:进去的是 ConnectionId 和 FsWatchParams → 它先调用 file_system 做可用性检查;检查通过后,把连接编号和监听参数交给 fs_watch_manager.watch → 出来的是 FsWatchResponse,表示监听登记结果,或者返回错误。

调用关系:客户端请求开始监听时,handle_initialized_client_request 会调用它。它自己不实现监听细节,而是像前台登记员一样确认条件后,把活交给 FsWatchManager。

调用图:调用 2 个内部函数(watch, file_system);被 1 处调用(handle_initialized_client_request)。

FsRequestProcessor::unwatch203–210 ↗
async fn unwatch(
        &self,
        connection_id: ConnectionId,
        params: FsUnwatchParams,
    ) -> Result<FsUnwatchResponse, JSONRPCErrorError>

作用:取消某个客户端连接之前登记的文件监听。它确保文件系统环境可用后,让 FsWatchManager 删除对应的监听记录。

数据流:进去的是 ConnectionId 和 FsUnwatchParams → 它先调用 file_system 检查环境,再把连接编号和取消监听参数交给 fs_watch_manager.unwatch → 出来的是 FsUnwatchResponse,表示取消完成或失败。

调用关系:客户端请求停止监听时由 handle_initialized_client_request 调用。它负责入口检查,真正的监听状态修改由 FsWatchManager 完成。

调用图:调用 2 个内部函数(unwatch, file_system);被 1 处调用(handle_initialized_client_request)。

map_fs_error213–219 ↗
fn map_fs_error(err: io::Error) -> JSONRPCErrorError

作用:把底层文件系统抛出的 io::Error 翻译成 JSON-RPC 错误。这样客户端不用面对 Rust 或操作系统风格的杂乱错误,而是收到统一格式的错误。

数据流:进去的是一个 io::Error → 它查看错误类型:如果是 InvalidInput,说明请求参数本身不合适,就转成 invalid_request;其他情况通常算服务器内部问题,就转成 internal_error → 出来的是 JSONRPCErrorError,错误文字来自原始错误的 to_string。

调用关系:读、写、建目录、查元数据、读目录、删除、复制等方法在底层文件系统失败时都会用它收口。它调用 invalid_request 或 internal_error,把技术性错误变成协议层能返回给客户端的错误。

调用图:调用 2 个内部函数(internal_error, invalid_request);外部调用 2 个(kind, to_string)。

app-server/src/request_processors/feedback_processor.rs源码 ↗
orchestrationrequest handling

用户点“提交反馈”时,光发一句“有问题”通常不够,开发者还需要知道是哪次对话、当时有哪些日志、系统环境怎么样。这个文件就像一个打包员:先确认配置允许发送反馈,再检查用户给的线程 ID 是否合法;如果用户同意带日志,它会尽量找出这次对话以及相关子对话的记录文件,还会从数据库里补日志。为了避免上传太大,它最多保留一棵反馈对话树里的 8 个线程,并优先保留最近的。它还会去重,避免同一个附件传两次;在 Windows 上会额外带上沙盒日志;如果能生成“医生报告”(诊断报告),也一起加进去。最后,因为真正上传可能比较慢,它把上传放到后台阻塞任务里做,避免卡住主服务的异步请求处理。

函数细节7
FeedbackRequestProcessor::new18–34 ↗
fn new(
        auth_manager: Arc<AuthManager>,
        thread_manager: Arc<ThreadManager>,
        config: Arc<Config>,
        feedback: CodexFeedback,
        log_db: Option<LogDbLayer>,
        st

作用:创建一个反馈请求处理器,把后面上传反馈会用到的几样东西先装进去,比如登录信息、线程管理器、配置、反馈上传器和数据库句柄。

数据流:进去的是认证管理器、线程管理器、配置、反馈对象、日志数据库和状态数据库;函数只是把它们保存到结构体里;出来的是一个可以处理反馈上传请求的 FeedbackRequestProcessor。

调用关系:它在服务初始化时被上层的 new 调用,相当于先把工具箱准备好。之后真正收到客户端请求时,handle_initialized_client_request 会通过这个处理器走反馈上传流程。

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

FeedbackRequestProcessor::feedback_upload36–43 ↗
async fn feedback_upload(
        &self,
        params: FeedbackUploadParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:这是客户端请求进来时调用的公开入口,用来处理“上传反馈”这件事。它主要负责把内部上传结果包装成客户端能理解的响应格式。

数据流:进去的是 FeedbackUploadParams,也就是用户提交的反馈分类、原因、是否带日志、额外文件等;它把这些交给 upload_feedback_response;成功后把反馈响应转换成 ClientResponsePayload 并包成 Some,失败就返回 JSON-RPC 错误。

调用关系:它由 handle_initialized_client_request 在收到已初始化客户端的反馈请求时调用。它自己不做复杂打包,而是把主要工作交给 upload_feedback_response。

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

FeedbackRequestProcessor::upload_feedback_response45–271 ↗
async fn upload_feedback_response(
        &self,
        params: FeedbackUploadParams,
    ) -> Result<FeedbackUploadResponse, JSONRPCErrorError>

作用:这是整个文件的核心流程,真正决定反馈能不能上传、要带哪些对话和日志、最后怎么调用上传接口。

数据流:进去的是用户的反馈参数,以及处理器里保存的配置、认证信息、线程管理器、数据库和反馈上传器;它先检查反馈功能是否开启,再解析线程 ID,记录一些账号相关标签,截取当前反馈快照。如果要求带日志,它会刷新日志数据库、找出相关线程、必要时从状态数据库兜底、限制最多上传 8 个线程、查询数据库日志、收集 rollout 日志文件、自动评审日志、Windows 沙盒日志和用户额外指定的文件,并去掉重复附件。随后它可能生成诊断报告并补充标签。最后它在后台阻塞任务里调用 snapshot.upload_feedback 上传;成功返回 FeedbackUploadResponse,里面带线程 ID,失败返回适合 JSON-RPC 的错误。

调用关系:它由 feedback_upload 调用,是反馈请求的主干。过程中它会调用 resolve_rollout_path 找日志路径,调用 auto_review_rollout_filename 给自动评审附件取名,调用 windows_sandbox_log_attachment 找 Windows 沙盒日志,还会调用 doctor_feedback_report 生成诊断附件。真正的网络上传交给反馈快照对象的 upload_feedback 完成。

调用图:调用 6 个内部函数(doctor_feedback_report, resolve_rollout_path, auto_review_rollout_filename, windows_sandbox_log_attachment, snapshot, from_string);被 1 处调用(feedback_upload);外部调用 8 个(new, new, with_capacity, format!, spawn_blocking, info!, vec!, warn!)。

FeedbackRequestProcessor::resolve_rollout_path273–292 ↗
async fn resolve_rollout_path(
        &self,
        conversation_id: ThreadId,
        state_db_ctx: Option<&StateDbHandle>,
    ) -> Option<PathBuf>

作用:根据一个对话线程 ID 找到它对应的 rollout 日志文件路径。rollout 可以理解成这次对话运行过程的流水账文件。

数据流:进去的是一个 ThreadId 和可选的状态数据库句柄;它先问当前线程管理器,看这个线程是否还在内存里并且能直接给出日志路径。如果找不到,就去状态数据库里按 ID 查历史记录。成功返回文件路径,查不到或出错就返回 None,并在出错时写警告日志。

调用关系:它只被 upload_feedback_response 调用,用在收集反馈附件的阶段。主流程不直接关心路径从哪里来,只要它能尽量把当前或归档线程的日志文件找出来。

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

auto_review_rollout_filename295–297 ↗
fn auto_review_rollout_filename(thread_id: ThreadId) -> String

作用:给“自动评审”相关的 rollout 附件生成一个清楚的文件名,避免上传后看不出它属于哪个线程。

数据流:进去的是线程 ID;它把 ID 拼进固定格式 auto-review-rollout-<thread_id>.jsonl;出来的是这个附件在反馈包里显示用的文件名字符串。

调用关系:它被 upload_feedback_response 在添加 guardian trunk,也就是自动评审相关日志附件时调用。它只负责命名,不负责找文件或上传文件。

调用图:被 1 处调用(upload_feedback_response);外部调用 1 个(format!)。

windows_sandbox_log_attachment311–313 ↗
fn windows_sandbox_log_attachment(_codex_home: &Path) -> Option<FeedbackAttachmentPath>

作用:在 Windows 系统上,检查当前 Codex 目录里有没有沙盒日志;如果有,就把它做成一个反馈附件。非 Windows 系统上它直接表示没有这个附件。

数据流:进去的是 codex_home 路径,也就是 Codex 的主目录;Windows 版本会根据这个目录算出当前沙盒日志文件位置,确认文件真的存在后,返回带路径和固定附件名的 FeedbackAttachmentPath。非 Windows 版本不读取文件,直接返回 None。

调用关系:它被 upload_feedback_response 在收集日志附件时调用,用来补上 Windows 特有的沙盒运行记录。Windows 测试 tests::windows_sandbox_log_attachment_uses_current_log 也会调用它,确认它拿到的是当前日志文件。

调用图:被 2 处调用(upload_feedback_response, windows_sandbox_log_attachment_uses_current_log);外部调用 1 个(current_log_file_path_for_codex_home)。

tests::windows_sandbox_log_attachment_uses_current_log321–339 ↗
fn windows_sandbox_log_attachment_uses_current_log()

作用:这是一个只在 Windows 测试环境运行的测试,确认 Windows 沙盒日志附件函数会选择当前日志文件,并使用正确的附件文件名。

数据流:它先创建一个临时 Codex 目录,再创建沙盒目录和一份假的当前沙盒日志;然后调用 windows_sandbox_log_attachment;最后用断言检查返回的路径和文件名是否正好等于预期。

调用关系:它是 windows_sandbox_log_attachment 的安全网。平时服务运行不会调用它,只有跑测试时才会执行,用来防止以后改代码时不小心把 Windows 沙盒日志上传路径弄错。

调用图:调用 1 个内部函数(windows_sandbox_log_attachment);外部调用 6 个(assert_eq!, current_log_file_path_for_codex_home, sandbox_dir, create_dir_all, write, tempdir)。

app-server/src/request_processors/git_processor.rs源码 ↗
domain_logicrequest handling

这个文件像一个“Git 问询窗口”。客户端想知道某个项目目录里,本地代码相对于远端代码有哪些改动,就会把请求送到这里。这里的 GitRequestProcessor 本身不保存状态,只是负责接单、调用真正做 Git 对比的函数,然后把结果整理成统一的 JSON-RPC 响应。JSON-RPC 可以理解成一种“用 JSON 格式传请求和结果”的通信规矩。流程很短:外部请求先进入 git_diff_to_remote,它再调用内部的 git_diff_to_origin;后者拿到工作目录 cwd,交给底层 git_diff_to_remote 函数去算远端提交 sha 和 diff 内容。如果算出来了,就包装成 GitDiffToRemoteResponse;如果没算出来,就返回 invalid_request 错误,明确告诉调用方这个目录的 Git diff 计算失败。这个文件重要在于它把“底层 Git 操作”和“客户端接口格式”隔开,避免外部直接碰复杂的 Git 细节。

函数细节3
GitRequestProcessor::new7–9 ↗
fn new() -> Self

作用:创建一个 GitRequestProcessor,也就是准备好一个处理 Git 请求的小对象。因为它不需要保存配置或状态,所以创建过程非常简单。

数据流:进去没有额外输入 → 它直接构造一个空的 GitRequestProcessor → 出来一个可以被其他请求处理流程使用的处理器实例,不改动任何外部数据。

调用关系:它会在上层的 new 流程里被调用,用来把 Git 请求处理能力装配进整个服务器的请求处理系统。之后真正收到 Git diff 请求时,才会用这个实例去调用其他方法。

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

GitRequestProcessor::git_diff_to_remote11–18 ↗
async fn git_diff_to_remote(
        &self,
        params: GitDiffToRemoteParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理客户端发来的“查看本地和远端 Git 差异”的请求。它是对外使用的入口,把请求参数转成内部计算,再把结果包装成客户端响应。

数据流:进去的是 GitDiffToRemoteParams,其中主要有 cwd,也就是要检查的项目目录 → 它把 cwd 交给 git_diff_to_origin 去计算差异 → 如果成功,出来的是一个可发给客户端的 ClientResponsePayload;如果失败,出来的是 JSON-RPC 错误。

调用关系:它由 handle_initialized_client_request 在收到已初始化客户端的对应请求时调用。它自己不直接算 Git 差异,而是把核心工作交给 GitRequestProcessor::git_diff_to_origin,然后只负责把内部结果变成客户端响应格式。

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

GitRequestProcessor::git_diff_to_origin20–35 ↗
async fn git_diff_to_origin(
        &self,
        cwd: PathBuf,
    ) -> Result<GitDiffToRemoteResponse, JSONRPCErrorError>

作用:真正把某个目录拿去和远端 Git 版本做对比,并整理出提交编号和差异内容。它是这个文件里的内部帮手,负责把底层结果变成服务端自己的响应结构。

数据流:进去的是 cwd,也就是本地项目目录路径 → 它调用底层 git_diff_to_remote(&cwd) 来获取远端提交 sha 和 diff 文本 → 如果有结果,就输出 GitDiffToRemoteResponse;如果没有结果,就生成 invalid_request 错误,并在错误里带上失败的目录。

调用关系:它只被 GitRequestProcessor::git_diff_to_remote 调用,属于对外请求背后的实际执行步骤。它把繁重的 Git 差异计算交给底层 git_diff_to_remote 函数,自己负责检查结果是否存在,以及失败时把问题翻译成 JSON-RPC 能返回的错误。

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

app-server/src/request_processors/marketplace_processor.rs源码 ↗
orchestrationrequest handling

这个文件像一个前台服务员:客户端说“帮我加一个市场”“删掉这个市场”“升级市场”,它不会自己做所有苦力,而是检查和整理参数,然后把活交给更底层的安装、删除、插件管理代码。它保存了三样重要东西:当前配置、能重新读取配置的配置管理器、以及线程管理器。添加和删除 marketplace 时,它会用配置里的 codex_home,也就是程序自己的主目录,去决定文件该放哪或从哪删。升级时比较特别:它先重新加载最新配置,避免拿旧设置办事;然后把可能很耗时的升级工作丢到 blocking 线程里跑,防止卡住异步服务器。所有对外方法都会把内部结果包装成统一的客户端响应;如果出错,也会把错误分成“请求本身不对”和“服务器内部失败”,让调用方更容易判断问题在哪。

函数细节8
MarketplaceRequestProcessor::new11–21 ↗
fn new(
        config: Arc<Config>,
        config_manager: ConfigManager,
        thread_manager: Arc<ThreadManager>,
    ) -> Self

作用:创建一个 marketplace 请求处理器,把它之后办事需要的配置、配置管理器和线程管理器装进去。没有这一步,后面的添加、删除、升级请求就不知道该用哪个主目录、怎么重读配置、怎么找插件管理器。

数据流:进去的是共享配置、配置管理器、共享线程管理器 → 函数把这三样保存到新的 MarketplaceRequestProcessor 里 → 出来的是一个可以处理 marketplace 请求的处理器对象,不会立刻执行添加或删除。

调用关系:它在更外层的 new 流程里被调用,用来组装请求处理系统的一块零件。后续客户端请求到来时,marketplace_add、marketplace_remove、marketplace_upgrade 都会依赖这里保存下来的这些东西。

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

MarketplaceRequestProcessor::marketplace_add23–30 ↗
async fn marketplace_add(
        &self,
        params: MarketplaceAddParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:这是客户端“添加 marketplace”请求的对外入口。它负责调用真正干活的内部函数,并把内部回答包装成统一的客户端响应格式。

数据流:进去的是 MarketplaceAddParams,里面有来源、分支或版本名、可选的稀疏路径等信息 → 它交给 marketplace_add_inner 去实际添加 → 出来的是一个可选的 ClientResponsePayload;成功时里面装着添加结果,失败时返回 JSON-RPC 错误。

调用关系:它由 handle_initialized_client_request 在收到已初始化客户端的添加请求时调用。它自己不直接碰文件安装细节,而是把活交给 marketplace_add_inner,最后负责把结果转换成客户端要的格式。

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

MarketplaceRequestProcessor::marketplace_remove32–39 ↗
async fn marketplace_remove(
        &self,
        params: MarketplaceRemoveParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:这是客户端“删除 marketplace”请求的对外入口。它把请求转给内部删除流程,再把删除结果包装成客户端能收到的标准响应。

数据流:进去的是 MarketplaceRemoveParams,主要是要删除的 marketplace 名字 → 它调用 marketplace_remove_inner 做实际删除 → 出来的是可选的 ClientResponsePayload;成功时包含被删的名称和原安装位置,失败时是 JSON-RPC 错误。

调用关系:它由 handle_initialized_client_request 在收到删除请求时调用。它在整个流程里像外壳,负责接请求和包装结果,实际删除动作交给 marketplace_remove_inner。

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

MarketplaceRequestProcessor::marketplace_upgrade41–48 ↗
async fn marketplace_upgrade(
        &self,
        params: MarketplaceUpgradeParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:这是客户端“升级 marketplace”请求的对外入口。它会触发内部升级流程,并把升级后的市场列表、路径和错误信息整理成客户端响应。

数据流:进去的是 MarketplaceUpgradeParams,可能指定某个 marketplace,也可能表示升级所有已配置的 marketplace → 它调用 marketplace_upgrade_response_inner → 出来的是可选的 ClientResponsePayload,里面说明哪些市场被选中、哪些目录被升级、哪些失败了。

调用关系:它由 handle_initialized_client_request 在收到升级请求时调用。它把实际升级交给 marketplace_upgrade_response_inner,自己主要负责把内部响应转换成统一的客户端返回格式。

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

MarketplaceRequestProcessor::marketplace_remove_inner50–69 ↗
async fn marketplace_remove_inner(
        &self,
        params: MarketplaceRemoveParams,
    ) -> Result<MarketplaceRemoveResponse, JSONRPCErrorError>

作用:真正执行“删除 marketplace”的内部步骤。它把客户端参数变成核心删除请求,并把核心层的成功或失败结果翻译成服务器对外使用的格式。

数据流:进去的是要删除的 marketplace 名称,以及处理器里保存的 codex_home 主目录 → 它调用底层删除函数,从程序主目录下移除对应 marketplace → 成功时产出 MarketplaceRemoveResponse,包含名称和被删除的安装根目录;如果请求不合法就变成 invalid_request,如果内部出问题就变成 internal_error。

调用关系:它只被 marketplace_remove 调用,是对外删除入口背后的实际执行者。它连接了请求处理层和更底层的 marketplace 删除能力,让上层不用关心删除时的文件路径和错误类型细节。

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

MarketplaceRequestProcessor::marketplace_upgrade_response_inner71–102 ↗
async fn marketplace_upgrade_response_inner(
        &self,
        params: MarketplaceUpgradeParams,
    ) -> Result<MarketplaceUpgradeResponse, JSONRPCErrorError>

作用:真正执行“升级 marketplace”的内部步骤。它会先拿到最新配置,再让插件管理器去升级已配置的 marketplace,避免用过期配置做决定。

数据流:进去的是升级参数,里面可能有指定的 marketplace 名称 → 它先通过 load_latest_config 重读配置,再取出插件配置输入,然后用 spawn_blocking 把耗时升级放到专门线程执行 → 出来的是 MarketplaceUpgradeResponse,包含被选中的市场、升级过的目录,以及每个失败项的名字和错误消息;加载失败、线程失败或参数问题都会转成 JSON-RPC 错误。

调用关系:它被 marketplace_upgrade 调用,是升级请求的核心流程。它会向 load_latest_config 要最新配置,并把真正耗时的升级工作交给 spawn_blocking 里的插件管理器执行,这样主异步请求处理不会被长时间阻塞。

调用图:调用 1 个内部函数(load_latest_config);被 1 处调用(marketplace_upgrade);外部调用 1 个(spawn_blocking)。

MarketplaceRequestProcessor::marketplace_add_inner104–126 ↗
async fn marketplace_add_inner(
        &self,
        params: MarketplaceAddParams,
    ) -> Result<MarketplaceAddResponse, JSONRPCErrorError>

作用:真正执行“添加 marketplace”的内部步骤。它把客户端给的来源、版本名和路径筛选信息交给底层安装逻辑,并整理安装结果。

数据流:进去的是 MarketplaceAddParams,以及处理器保存的 codex_home 主目录 → 它把参数转成 MarketplaceAddRequest;如果 sparse_paths 没给,就用空列表 → 底层添加完成后,出来的是 MarketplaceAddResponse,说明 marketplace 名称、安装位置,以及它是不是早就已经添加过;错误会被翻译成请求错误或内部错误。

调用关系:它只被 marketplace_add 调用,是添加请求的实际执行者。marketplace_add 负责对外接待,它负责把请求变成底层添加动作,并把底层结果变回对外响应。

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

MarketplaceRequestProcessor::load_latest_config128–136 ↗
async fn load_latest_config(
        &self,
        fallback_cwd: Option<PathBuf>,
    ) -> Result<Config, JSONRPCErrorError>

作用:重新读取最新配置,给需要新鲜配置的流程使用。这里主要服务升级 marketplace,因为升级前必须知道当前到底配置了哪些插件市场。

数据流:进去的是一个可选的 fallback_cwd,也就是找配置时可以备用的当前目录 → 它调用配置管理器的 load_latest_config 去读取最新配置 → 成功时出来的是 Config;失败时把读取错误包装成 internal_error,告诉客户端这是服务器重载配置失败。

调用关系:它被 marketplace_upgrade_response_inner 调用,位于升级流程开头。它把“怎么重新加载配置”的细节藏起来,让升级流程只需要拿到最新 Config 后继续工作。

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

app-server/src/request_processors/plugins.rs源码 ↗
orchestrationrequest handling

这个文件把前端发来的插件请求,翻译成后端各个模块能听懂的动作。它像一个服务台:先检查配置、登录状态和工作区是否允许用插件;再决定去本地插件市场、远程插件市场,还是分享服务里取数据;最后把结果整理成客户端协议里的格式返回。它还会处理一些容易出错的边界,比如远程插件编号是否合法、插件分享目标能不能用、远程和本地同名已安装插件要不要去重。安装或卸载后,它会清缓存,并触发 MCP(模型上下文协议,一种让插件提供外部工具的接口)刷新;如果插件里的 MCP 服务需要 OAuth 登录(一种网页登录授权方式),它还会在后台尝试静默登录并通知客户端结果。

函数细节65
plugin_skills_to_info36–60 ↗
fn plugin_skills_to_info(
    skills: &[codex_core::skills::SkillMetadata],
    disabled_skill_paths: &HashSet<AbsolutePathBuf>,
) -> Vec<SkillSummary>

作用:把插件内部记录的“技能”信息,整理成客户端能展示的技能摘要。它还会标出哪些技能被禁用了。

数据流:输入是一组技能元数据和一组被禁用的技能文件路径;它逐个复制名称、描述、界面信息和路径,并检查路径是否在禁用名单里;输出是一组给客户端看的 SkillSummary。

调用关系:plugin_read_response 读取本地插件详情时会调用它,把插件里的技能清单变成最终响应的一部分。

调用图:被 1 处调用(plugin_read_response);外部调用 1 个(iter)。

local_plugin_interface_to_info62–82 ↗
fn local_plugin_interface_to_info(interface: PluginManifestInterface) -> PluginInterface

作用:把本地插件清单里的界面展示信息,改成应用服务器协议里的界面信息。这样客户端不用认识插件内部的原始格式。

数据流:输入是本地插件的界面字段;它逐项搬运显示名、描述、图标、颜色、网址等内容,并把远程图片地址留空;输出是 PluginInterface。

调用关系:它是本地插件摘要和详情转换时的小翻译器,帮助上层返回统一格式。

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

marketplace_plugin_source_to_info84–99 ↗
fn marketplace_plugin_source_to_info(source: MarketplacePluginSource) -> PluginSource

作用:说明一个插件是从哪里来的:本地目录,还是 Git 仓库。客户端需要这个来源来展示或后续操作。

数据流:输入是内部的插件来源;如果是本地就保留路径,如果是 Git 就保留网址、路径、分支名和提交号;输出是协议里的 PluginSource。

调用关系:plugin_read_response 和 convert_configured_marketplace_plugin_to_plugin_summary 会用它,把内部来源转换给客户端看。

调用图:被 2 处调用(plugin_read_response, convert_configured_marketplace_plugin_to_plugin_summary)。

load_shared_plugin_ids_by_local_path101–112 ↗
fn load_shared_plugin_ids_by_local_path(
    config: &Config,
) -> Result<std::collections::BTreeMap<AbsolutePathBuf, String>, JSONRPCErrorError>

作用:读取“本地插件路径对应哪个远程分享插件编号”的记录。没有它,本地已分享的插件就无法显示分享状态。

数据流:输入是配置,主要用到 codex_home 目录;它从磁盘里的分享映射文件读取数据,失败时转成 JSON-RPC 错误;输出是按本地路径查远程插件编号的表。

调用关系:plugin_list_response、plugin_read_response 和 load_local_installed_and_suggested_plugins 在展示本地插件时会调用它,用来补上分享上下文。

调用图:被 3 处调用(load_local_installed_and_suggested_plugins, plugin_list_response, plugin_read_response);外部调用 1 个(load_plugin_share_remote_ids_by_local_path)。

share_context_for_source114–133 ↗
fn share_context_for_source(
    source: &MarketplacePluginSource,
    shared_plugin_ids_by_local_path: &std::collections::BTreeMap<AbsolutePathBuf, String>,
) -> Option<PluginShareContext>

作用:根据插件来源判断它有没有本地分享记录。只有本地路径插件才可能直接对应一个远程分享编号。

数据流:输入是插件来源和本地路径到远程编号的映射;如果来源是本地且路径命中,就生成一个基础 PluginShareContext;如果是 Git 来源则返回空。

调用关系:convert_configured_marketplace_plugin_to_plugin_summary 和 plugin_read_response 会用它,为本地插件摘要或详情加上分享信息。

调用图:被 2 处调用(plugin_read_response, convert_configured_marketplace_plugin_to_plugin_summary)。

convert_configured_marketplace_plugin_to_plugin_summary135–155 ↗
fn convert_configured_marketplace_plugin_to_plugin_summary(
    plugin: codex_core_plugins::ConfiguredMarketplacePlugin,
    shared_plugin_ids_by_local_path: &std::collections::BTreeMap<AbsolutePathBu

作用:把内部读到的本地市场插件,压缩成客户端列表里的一条插件摘要。

数据流:输入是内部插件对象和分享映射;它补分享上下文、转换来源和界面信息、复制安装状态和策略;输出是 PluginSummary。

调用关系:本地插件列表和已安装插件列表在组装响应时会用它;它内部把来源转换交给 marketplace_plugin_source_to_info,把分享判断交给 share_context_for_source。

调用图:调用 2 个内部函数(marketplace_plugin_source_to_info, share_context_for_source)。

remote_installed_plugin_visible_marketplaces157–170 ↗
fn remote_installed_plugin_visible_marketplaces(config: &Config) -> Vec<&'static str>

作用:决定“已安装远程插件”应该从哪些远程市场里显示出来。不同功能开关会让用户看到不同范围。

数据流:输入是配置;它根据 RemotePlugin 和 PluginSharing 等功能开关加入全局、我创建的、工作区、分享给我的市场名;输出是一组市场名称。

调用关系:plugin_installed_response 会先调用它,再按这些市场去加载远程已安装插件。

调用图:被 1 处调用(plugin_installed_response);外部调用 1 个(new)。

filter_openai_curated_installed_conflicts172–207 ↗
fn filter_openai_curated_installed_conflicts(
    marketplaces: &mut Vec<PluginMarketplaceEntry>,
    prefer_remote_curated_conflicts: bool,
)

作用:处理 OpenAI 精选本地市场和远程全局市场里同名已安装插件重复显示的问题。

数据流:输入是市场列表和“冲突时更偏向远程还是本地”的选择;它找出本地精选和远程全局都已安装的插件名,并从不优先的一边删掉冲突项;输出是被原地整理后的市场列表。

调用关系:plugin_installed_response 合并本地和远程已安装插件后会调用它,避免客户端看到两份同名插件。

调用图:被 1 处调用(plugin_installed_response);外部调用 1 个(is_openai_curated_marketplace_name)。

installed_plugin_names209–215 ↗
fn installed_plugin_names(plugins: &[PluginSummary]) -> HashSet<String>

作用:从一组插件摘要里取出已安装插件的名字。它是做冲突判断时的小工具。

数据流:输入是插件摘要列表;它过滤出 installed 为真的插件,并收集它们的 name;输出是不重复的名字集合。

调用关系:filter_openai_curated_installed_conflicts 用它分别拿到本地和远程已安装名字,再比较重叠部分。

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

remote_plugin_share_discoverability217–231 ↗
fn remote_plugin_share_discoverability(
    discoverability: PluginShareDiscoverability,
) -> codex_core_plugins::remote::RemotePluginShareDiscoverability

作用:把客户端说的分享可见性,翻译成远程服务认识的可见性。可见性就是“别人怎么找到这个分享”。

数据流:输入是 Listed、Unlisted 或 Private;它逐一映射到远程模块对应的枚举;输出是远程分享可见性值。

调用关系:插件分享保存时会用这个转换,保证发给远程服务的参数类型正确。

remote_plugin_share_update_discoverability233–244 ↗
fn remote_plugin_share_update_discoverability(
    discoverability: PluginShareUpdateDiscoverability,
) -> codex_core_plugins::remote::RemotePluginShareUpdateDiscoverability

作用:把“更新分享设置”里的可见性选项,翻译成远程服务的格式。

数据流:输入是更新时允许的 Unlisted 或 Private;它转换成远程模块的对应值;输出给更新接口使用。

调用关系:plugin_share_update_targets_response 在调用远程更新分享目标前会用它。

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

validate_client_plugin_share_targets246–258 ↗
fn validate_client_plugin_share_targets(
    targets: &[PluginShareTarget],
) -> Result<(), JSONRPCErrorError>

作用:检查客户端传来的分享对象是否合法。这里特别禁止把 workspace 当成普通分享目标。

数据流:输入是分享目标列表;它查看有没有 Workspace 类型,如果有就返回 invalid_request;如果没有就返回成功。

调用关系:plugin_share_save_response 和 plugin_share_update_targets_response 都会先调用它,防止把远程服务不接受的分享规则发出去。

调用图:调用 1 个内部函数(invalid_request);被 2 处调用(plugin_share_save_response, plugin_share_update_targets_response);外部调用 1 个(iter)。

remote_plugin_share_target_role260–271 ↗
fn remote_plugin_share_target_role(
    role: PluginShareTargetRole,
) -> codex_core_plugins::remote::RemotePluginShareTargetRole

作用:把客户端分享目标的权限角色,翻译成远程服务的权限角色。角色比如只读者、编辑者。

数据流:输入是 Reader 或 Editor;它转换成远程模块的 Reader 或 Editor;输出用于远程分享目标。

调用关系:remote_plugin_share_targets 在转换整批分享目标时会用它。

plugin_share_principal_role_from_remote273–287 ↗
fn plugin_share_principal_role_from_remote(
    role: codex_core_plugins::remote::RemotePluginSharePrincipalRole,
) -> PluginSharePrincipalRole

作用:把远程服务返回的分享参与者角色,翻译成客户端认识的角色。

数据流:输入是远程角色 Reader、Editor 或 Owner;它映射成协议里的对应角色;输出用于展示分享成员。

调用关系:plugin_share_principal_from_remote 调用它来完成单个分享成员的转换。

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

remote_plugin_share_targets289–312 ↗
fn remote_plugin_share_targets(
    targets: Vec<PluginShareTarget>,
) -> Vec<codex_core_plugins::remote::RemotePluginShareTarget>

作用:把客户端提交的一批分享目标,整体翻译成远程服务要的格式。

数据流:输入是一组 PluginShareTarget;它逐个转换目标类型、目标编号和角色;输出是一组 RemotePluginShareTarget。

调用关系:plugin_share_update_targets_response 会把用户的新分享名单交给它,再发给远程服务。

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

plugin_share_principal_from_remote314–333 ↗
fn plugin_share_principal_from_remote(
    principal: codex_core_plugins::remote::RemotePluginSharePrincipal,
) -> PluginSharePrincipal

作用:把远程服务返回的一个分享成员,整理成客户端能显示的成员信息。

数据流:输入是远程成员对象;它转换成员类型和角色,并保留编号、名称;输出是 PluginSharePrincipal。

调用关系:分享上下文和更新分享目标返回结果里都会用它;角色转换交给 plugin_share_principal_role_from_remote。

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

PluginRequestProcessor::new336–352 ↗
fn new(
        auth_manager: Arc<AuthManager>,
        thread_manager: Arc<ThreadManager>,
        outgoing: Arc<OutgoingMessageSender>,
        analytics_events_client: AnalyticsEventsClient,

作用:创建一个插件请求处理器,把它后面办事需要的各种工具都装进去。

数据流:输入是认证管理器、线程管理器、消息发送器、统计客户端、配置管理器和工作区设置缓存;它保存这些引用;输出是 PluginRequestProcessor 实例。

调用关系:服务器初始化处理器时会调用它,之后所有插件请求都通过这个实例处理。

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

PluginRequestProcessor::plugin_list354–361 ↗
async fn plugin_list(
        &self,
        params: PluginListParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理客户端的 plugin/list 请求,也就是“给我插件市场和插件列表”。

数据流:输入是列表参数;它把真正工作交给 plugin_list_response,再把响应包装成通用客户端响应;输出可发送给客户端的载荷。

调用关系:handle_initialized_client_request 收到对应请求时调用它;它是 plugin_list_response 的薄外壳。

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

PluginRequestProcessor::plugin_installed363–370 ↗
async fn plugin_installed(
        &self,
        params: PluginInstalledParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“查看已安装插件”的请求。

数据流:输入是已安装查询参数;它调用 plugin_installed_response 取得结果,再包装成通用响应;输出给客户端。

调用关系:handle_initialized_client_request 会调用它,它再把业务交给 plugin_installed_response。

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

PluginRequestProcessor::plugin_read372–379 ↗
async fn plugin_read(
        &self,
        params: PluginReadParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“读取某个插件详细信息”的请求。

数据流:输入是插件所在市场和插件名;它调用 plugin_read_response 读取详情,再包装成客户端响应;输出插件详情。

调用关系:handle_initialized_client_request 分发到这里;实际读取本地或远程详情由 plugin_read_response 完成。

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

PluginRequestProcessor::plugin_skill_read381–388 ↗
async fn plugin_skill_read(
        &self,
        params: PluginSkillReadParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“读取远程插件某个技能内容”的请求。

数据流:输入是远程市场名、远程插件编号和技能名;它调用 plugin_skill_read_response;输出技能文件内容。

调用关系:handle_initialized_client_request 调用它,它只是统一包装返回值。

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

PluginRequestProcessor::plugin_share_save390–397 ↗
async fn plugin_share_save(
        &self,
        params: PluginShareSaveParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“保存或创建插件分享”的请求。

数据流:输入是本地插件路径、可选远程插件编号、可见性和分享对象;它调用 plugin_share_save_response;输出远程插件编号和分享链接。

调用关系:handle_initialized_client_request 调用它,真正的校验和远程保存由 plugin_share_save_response 做。

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

PluginRequestProcessor::plugin_share_update_targets399–406 ↗
async fn plugin_share_update_targets(
        &self,
        params: PluginShareUpdateTargetsParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“修改插件分享对象和可见性”的请求。

数据流:输入是远程插件编号、新可见性和分享目标;它调用 plugin_share_update_targets_response;输出更新后的成员和可见性。

调用关系:handle_initialized_client_request 调用它,细节交给 plugin_share_update_targets_response。

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

PluginRequestProcessor::plugin_share_list408–415 ↗
async fn plugin_share_list(
        &self,
        params: PluginShareListParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“列出我分享过的插件”的请求。

数据流:输入是列表参数;它调用 plugin_share_list_response;输出分享条目列表。

调用关系:handle_initialized_client_request 调用它,远程查询由 plugin_share_list_response 完成。

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

PluginRequestProcessor::plugin_share_checkout417–424 ↗
async fn plugin_share_checkout(
        &self,
        params: PluginShareCheckoutParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“把别人分享的远程插件取到本地”的请求。

数据流:输入是远程插件编号;它调用 plugin_share_checkout_response;输出本地插件路径、市场信息和版本等结果。

调用关系:handle_initialized_client_request 调用它,下载和缓存清理由 plugin_share_checkout_response 完成。

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

PluginRequestProcessor::plugin_share_delete426–433 ↗
async fn plugin_share_delete(
        &self,
        params: PluginShareDeleteParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理“删除一个插件分享”的请求。

数据流:输入是远程插件编号;它调用 plugin_share_delete_response;输出空响应,表示删除成功。

调用关系:handle_initialized_client_request 调用它,远程删除和清缓存由 plugin_share_delete_response 完成。

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

PluginRequestProcessor::plugin_install435–442 ↗
async fn plugin_install(
        &self,
        params: PluginInstallParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理安装插件请求。安装来源可能是本地市场,也可能是远程市场。

数据流:输入是市场路径或远程市场名以及插件名;它调用 plugin_install_response;输出安装后的认证策略和需要用户授权的应用。

调用关系:handle_initialized_client_request 调用它,安装流程由 plugin_install_response 决定走本地还是远程。

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

PluginRequestProcessor::plugin_uninstall444–451 ↗
async fn plugin_uninstall(
        &self,
        params: PluginUninstallParams,
    ) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError>

作用:处理卸载插件请求。

数据流:输入是插件编号;它调用 plugin_uninstall_response;输出空响应表示卸载完成。

调用关系:handle_initialized_client_request 调用它,实际卸载和刷新交给 plugin_uninstall_response。

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

PluginRequestProcessor::effective_plugins_changed_callback453–462 ↗
fn effective_plugins_changed_callback(&self) -> Arc<dyn Fn() + Send + Sync>

作用:生成一个回调函数,用来在插件实际生效集合变化时触发刷新。

数据流:输入是当前处理器里的线程管理器和配置管理器;它克隆必要引用并做成一个可传出去的函数;输出是线程安全的回调。

调用关系:插件列表后台任务、远程已安装同步、远程安装和卸载后刷新都会拿这个回调,回调内部启动 spawn_effective_plugins_changed_task。

调用图:被 5 处调用(load_remote_installed_plugins, plugin_installed_response, plugin_list_response, remote_plugin_install_response, remote_plugin_uninstall_response);外部调用 3 个(clone, new, clone)。

PluginRequestProcessor::on_effective_plugins_changed464–469 ↗
fn on_effective_plugins_changed(&self)

作用:在当前流程里直接宣布“插件生效情况变了”。

数据流:输入来自处理器自身保存的管理器;它启动后台任务清缓存并刷新 MCP;没有直接返回数据,只触发副作用。

调用关系:本地安装、本地卸载和远程卸载成功后会调用它;真正异步工作交给 spawn_effective_plugins_changed_task。

调用图:被 3 处调用(plugin_install_response, plugin_uninstall_response, remote_plugin_uninstall_response);外部调用 3 个(clone, spawn_effective_plugins_changed_task, clone)。

PluginRequestProcessor::spawn_effective_plugins_changed_task471–483 ↗
fn spawn_effective_plugins_changed_task(
        thread_manager: Arc<ThreadManager>,
        config_manager: ConfigManager,
    )

作用:后台执行插件变更后的收尾工作:清缓存,必要时刷新 MCP。

数据流:输入是线程管理器和配置管理器;它开一个 tokio 异步任务,清插件和技能缓存,如果当前有线程会话就排队刷新 MCP;输出是后台副作用。

调用关系:effective_plugins_changed_callback 和 on_effective_plugins_changed 都会把工作交给它。

调用图:调用 1 个内部函数(queue_best_effort_refresh);外部调用 1 个(spawn)。

PluginRequestProcessor::load_latest_config490–498 ↗
async fn load_latest_config(
        &self,
        fallback_cwd: Option<PathBuf>,
    ) -> Result<Config, JSONRPCErrorError>

作用:重新加载最新配置,并把失败统一包装成 JSON-RPC 错误。

数据流:输入是可选的备用当前目录;它调用配置管理器读取最新配置;成功输出 Config,失败输出 internal_error。

调用关系:大多数插件请求都会先调用它,确保后续判断使用的是最新功能开关和路径。

调用图:调用 1 个内部函数(load_latest_config);被 9 处调用(load_plugin_share_config_and_auth, plugin_install_response, plugin_installed_response, plugin_list_response, plugin_read_response, plugin_skill_read_response, plugin_uninstall_response, remote_plugin_install_response, remote_plugin_uninstall_response)。

PluginRequestProcessor::workspace_codex_plugins_enabled500–520 ↗
async fn workspace_codex_plugins_enabled(
        &self,
        config: &Config,
        auth: Option<&CodexAuth>,
    ) -> bool

作用:检查当前工作区是否允许使用 Codex 插件。工作区设置就像公司门禁,不允许时请求要被挡住。

数据流:输入是配置和可选登录信息;它查询工作区设置缓存;成功输出是否启用,查询失败时记录警告并默认允许。

调用关系:plugin_list_response、plugin_installed_response 和 plugin_install_response 会用它做前置检查。

调用图:调用 1 个内部函数(codex_plugins_enabled_for_workspace);被 3 处调用(plugin_install_response, plugin_installed_response, plugin_list_response);外部调用 1 个(warn!)。

PluginRequestProcessor::plugin_list_response522–773 ↗
async fn plugin_list_response(
        &self,
        params: PluginListParams,
    ) -> Result<PluginListResponse, JSONRPCErrorError>

作用:真正组装插件市场列表响应。它会把本地市场、远程市场、精选插件和加载错误合在一起。

数据流:输入是列表参数,包括目录和市场类型;它加载配置、检查功能和工作区开关、读取本地市场、按需拉远程市场、启动后台刷新,并整理 featured 插件编号;输出 PluginListResponse。

调用关系:plugin_list 调用它;它会用 load_latest_config、workspace_codex_plugins_enabled、load_shared_plugin_ids_by_local_path、remote_marketplace_to_info 和 remote_plugin_catalog_error_to_jsonrpc 等工具完成整条列表流程。

调用图:调用 10 个内部函数(internal_error, effective_plugins_changed_callback, load_latest_config, workspace_codex_plugins_enabled, load_shared_plugin_ids_by_local_path, remote_marketplace_to_info, remote_plugin_catalog_error_to_jsonrpc, fetch_openai_curated_remote_collection_marketplace, fetch_remote_marketplaces, has_cached_global_remote_plugin_catalog);被 1 处调用(plugin_list);外部调用 5 个(marketplace_error, new, format!, spawn_blocking, warn!)。

PluginRequestProcessor::plugin_installed_response775–844 ↗
async fn plugin_installed_response(
        &self,
        params: PluginInstalledParams,
    ) -> Result<PluginInstalledResponse, JSONRPCErrorError>

作用:真正组装“已安装插件”响应。它同时考虑本地已安装、安装建议和远程已安装。

数据流:输入是目录和建议安装的插件名;它加载配置、检查开关、启动远程同步、加载本地匹配项和远程已安装项,再去掉冲突;输出 PluginInstalledResponse。

调用关系:plugin_installed 调用它;它把本地部分交给 load_local_installed_and_suggested_plugins,把远程部分交给 load_remote_installed_plugins。

调用图:调用 7 个内部函数(effective_plugins_changed_callback, load_latest_config, load_local_installed_and_suggested_plugins, load_remote_installed_plugins, workspace_codex_plugins_enabled, filter_openai_curated_installed_conflicts, remote_installed_plugin_visible_marketplaces);被 1 处调用(plugin_installed)。

PluginRequestProcessor::load_local_installed_and_suggested_plugins846–927 ↗
async fn load_local_installed_and_suggested_plugins(
        &self,
        plugins_manager: Arc<codex_core_plugins::PluginsManager>,
        config: &Config,
        plugins_input: &codex_core_plugin

作用:加载本地市场里已经安装或被建议安装的插件。

数据流:输入是插件管理器、配置、插件配置输入、根目录和建议插件名集合;它在阻塞线程里扫描本地市场,过滤出已安装或建议项,并转换成客户端摘要;输出市场列表和加载错误。

调用关系:plugin_installed_response 调用它来拿本地那一半数据;它会读取分享映射并在出错时用 marketplace_error 转换错误。

调用图:调用 2 个内部函数(internal_error, load_shared_plugin_ids_by_local_path);被 1 处调用(plugin_installed_response);外部调用 4 个(marketplace_error, clone, format!, spawn_blocking)。

PluginRequestProcessor::load_remote_installed_plugins929–968 ↗
async fn load_remote_installed_plugins(
        &self,
        plugins_manager: Arc<codex_core_plugins::PluginsManager>,
        plugins_input: &codex_core_plugins::PluginsConfigInput,
        visible

作用:加载远程市场里当前账号已安装的插件,并尽量用缓存提速。

数据流:输入是插件管理器、插件配置、可见市场名单和登录信息;它先尝试从缓存组装,缓存没有就联网构建并缓存;输出远程市场条目列表,认证不合适或失败时通常返回空并记录警告。

调用关系:plugin_installed_response 调用它;远程刷新时会传入 effective_plugins_changed_callback。

调用图:调用 1 个内部函数(effective_plugins_changed_callback);被 1 处调用(plugin_installed_response);外部调用 2 个(new, warn!)。

PluginRequestProcessor::plugin_read_response970–1154 ↗
async fn plugin_read_response(
        &self,
        params: PluginReadParams,
    ) -> Result<PluginReadResponse, JSONRPCErrorError>

作用:读取一个插件的完整详情。它能读本地市场插件,也能读远程市场插件。

数据流:输入是本地市场路径或远程市场名,以及插件名;它校验来源只能二选一,加载配置和认证,本地时读取插件文件并补分享、技能、应用信息,远程时调用远程详情接口;输出 PluginReadResponse。

调用关系:plugin_read 调用它;它会使用 plugin_skills_to_info、load_plugin_app_summaries、remote_plugin_detail_to_info 等转换工具。

调用图:调用 12 个内部函数(invalid_request, load_latest_config, load_plugin_app_summaries, load_shared_plugin_ids_by_local_path, marketplace_plugin_source_to_info, plugin_skills_to_info, remote_plugin_detail_to_info, remote_plugin_share_context_to_info, share_context_for_source, fetch_remote_plugin_detail (+2 more));被 1 处调用(plugin_read);外部调用 3 个(new, format!, warn!)。

PluginRequestProcessor::plugin_skill_read_response1156–1198 ↗
async fn plugin_skill_read_response(
        &self,
        params: PluginSkillReadParams,
    ) -> Result<PluginSkillReadResponse, JSONRPCErrorError>

作用:读取远程插件里某个技能的正文内容。

数据流:输入是远程市场名、插件编号和技能名;它检查插件功能开关、校验远程编号和技能名,然后请求远程服务;输出技能 contents。

调用关系:plugin_skill_read 调用它;远程错误会转成 JSON-RPC 错误返回给客户端。

调用图:调用 4 个内部函数(invalid_request, load_latest_config, fetch_remote_plugin_skill_detail, validate_remote_plugin_id);被 1 处调用(plugin_skill_read);外部调用 1 个(format!)。

PluginRequestProcessor::plugin_share_save_response1200–1256 ↗
async fn plugin_share_save_response(
        &self,
        params: PluginShareSaveParams,
    ) -> Result<PluginShareSaveResponse, JSONRPCErrorError>

作用:创建新的插件分享,或把本地插件保存到已有远程分享上。

数据流:输入是插件路径、可选远程编号、可见性和分享目标;它检查功能开关、编号、参数组合和分享目标,然后调用远程保存接口,成功后清缓存;输出远程插件编号和分享链接。

调用关系:plugin_share_save 调用它;它会用 load_plugin_share_config_and_auth、validate_client_plugin_share_targets 和 clear_plugin_related_caches。

调用图:调用 5 个内部函数(invalid_request, clear_plugin_related_caches, load_plugin_share_config_and_auth, validate_client_plugin_share_targets, is_valid_remote_plugin_id);被 1 处调用(plugin_share_save);外部调用 1 个(save_remote_plugin_share)。

PluginRequestProcessor::plugin_share_update_targets_response1258–1299 ↗
async fn plugin_share_update_targets_response(
        &self,
        params: PluginShareUpdateTargetsParams,
    ) -> Result<PluginShareUpdateTargetsResponse, JSONRPCErrorError>

作用:更新一个远程插件分享的分享对象和可见性。

数据流:输入是远程插件编号、可见性和目标列表;它校验编号和目标,把目标与可见性翻译成远程格式,调用远程更新接口,清缓存;输出更新后的成员列表和可见性。

调用关系:plugin_share_update_targets 调用它;它依赖 remote_plugin_share_targets、remote_plugin_share_update_discoverability 和 remote_plugin_share_discoverability_to_info 做格式转换。

调用图:调用 8 个内部函数(invalid_request, clear_plugin_related_caches, load_plugin_share_config_and_auth, remote_plugin_share_discoverability_to_info, remote_plugin_share_targets, remote_plugin_share_update_discoverability, validate_client_plugin_share_targets, is_valid_remote_plugin_id);被 1 处调用(plugin_share_update_targets);外部调用 1 个(update_remote_plugin_share_targets)。

PluginRequestProcessor::plugin_share_list_response1301–1330 ↗
async fn plugin_share_list_response(
        &self,
        _params: PluginShareListParams,
    ) -> Result<PluginShareListResponse, JSONRPCErrorError>

作用:列出当前用户的远程插件分享,以及这些分享在本地有没有对应路径。

数据流:输入是列表参数但当前不使用;它加载配置和认证,调用远程分享列表接口,把每条远程摘要转成插件摘要并保留本地路径;输出 PluginShareListResponse。

调用关系:plugin_share_list 调用它;准备配置和认证的工作交给 load_plugin_share_config_and_auth。

调用图:调用 1 个内部函数(load_plugin_share_config_and_auth);被 1 处调用(plugin_share_list);外部调用 1 个(list_remote_plugin_shares)。

PluginRequestProcessor::plugin_share_checkout_response1332–1366 ↗
async fn plugin_share_checkout_response(
        &self,
        params: PluginShareCheckoutParams,
    ) -> Result<PluginShareCheckoutResponse, JSONRPCErrorError>

作用:把一个远程分享插件签出到本地,也就是下载成可用的本地插件。

数据流:输入是远程插件编号;它检查分享功能和编号,调用远程 checkout 接口,成功后清缓存;输出本地插件编号、名称、路径、市场和版本信息。

调用关系:plugin_share_checkout 调用它;完成后调用 clear_plugin_related_caches,避免列表继续显示旧状态。

调用图:调用 4 个内部函数(invalid_request, clear_plugin_related_caches, load_plugin_share_config_and_auth, is_valid_remote_plugin_id);被 1 处调用(plugin_share_checkout);外部调用 1 个(checkout_remote_plugin_share)。

PluginRequestProcessor::plugin_share_delete_response1368–1391 ↗
async fn plugin_share_delete_response(
        &self,
        params: PluginShareDeleteParams,
    ) -> Result<PluginShareDeleteResponse, JSONRPCErrorError>

作用:删除一个远程插件分享记录。

数据流:输入是远程插件编号;它校验编号,调用远程删除接口,成功后清缓存;输出空响应。

调用关系:plugin_share_delete 调用它;配置和登录由 load_plugin_share_config_and_auth 提供。

调用图:调用 4 个内部函数(invalid_request, clear_plugin_related_caches, load_plugin_share_config_and_auth, is_valid_remote_plugin_id);被 1 处调用(plugin_share_delete);外部调用 1 个(delete_remote_plugin_share)。

PluginRequestProcessor::load_plugin_share_config_and_auth1393–1402 ↗
async fn load_plugin_share_config_and_auth(
        &self,
    ) -> Result<(Config, Option<CodexAuth>), JSONRPCErrorError>

作用:为插件分享相关请求准备配置和登录信息,并确认插件总功能是打开的。

数据流:输入是处理器自身;它加载最新配置,检查 Plugins 功能开关,然后读取当前认证;输出 Config 和可选 CodexAuth。

调用关系:所有 plugin_share_*_response 基本都会先调用它,避免每个函数重复写同样的准备代码。

调用图:调用 2 个内部函数(invalid_request, load_latest_config);被 5 处调用(plugin_share_checkout_response, plugin_share_delete_response, plugin_share_list_response, plugin_share_save_response, plugin_share_update_targets_response)。

PluginRequestProcessor::plugin_install_response1404–1487 ↗
async fn plugin_install_response(
        &self,
        params: PluginInstallParams,
    ) -> Result<PluginInstallResponse, JSONRPCErrorError>

作用:安装插件的主流程入口,负责判断是本地安装还是远程安装。

数据流:输入是安装参数;如果给的是远程市场名就转给 remote_plugin_install_response,本地路径则加载配置、检查工作区开关、调用插件管理器安装、触发刷新、启动 MCP OAuth 登录,并找出需要额外授权的应用;输出 PluginInstallResponse。

调用关系:plugin_install 调用它;远程分支交给 remote_plugin_install_response,本地安装后的应用授权检查交给 plugin_apps_needing_auth_for_install。

调用图:调用 7 个内部函数(invalid_request, load_latest_config, on_effective_plugins_changed, plugin_apps_needing_auth_for_install, remote_plugin_install_response, start_plugin_mcp_oauth_logins, workspace_codex_plugins_enabled);被 1 处调用(plugin_install);外部调用 2 个(app_connector_ids_from_declarations, warn!)。

PluginRequestProcessor::remote_plugin_install_response1489–1647 ↗
async fn remote_plugin_install_response(
        &self,
        remote_marketplace_name: String,
        remote_plugin_id: String,
    ) -> Result<PluginInstallResponse, JSONRPCErrorError>

作用:安装远程插件的完整流程。它不仅下载插件,还会通知后端这个插件已安装。

数据流:输入是远程市场名和远程插件编号;它加载配置、校验编号、拉取带下载地址的详情、检查可安装性、验证并下载插件包、调用远程安装接口、刷新缓存、记录统计、启动 MCP 登录并计算需授权应用;输出 PluginInstallResponse。

调用关系:plugin_install_response 在发现请求是远程安装时调用它;它会用 effective_plugins_changed_callback、start_plugin_mcp_oauth_logins 和 plugin_apps_needing_auth_for_install 串起后续动作。

调用图:调用 13 个内部函数(track_plugin_installed, invalid_request, effective_plugins_changed_callback, load_latest_config, plugin_apps_needing_auth_for_install, start_plugin_mcp_oauth_logins, connectors_for_plugin_apps, list_cached_all_connectors, fetch_remote_plugin_detail_with_download_urls, install_remote_plugin (+3 more));被 1 处调用(plugin_install_response);外部调用 4 个(new, mark_remote_plugin_cache_mutation_in_flight, app_connector_ids_from_declarations, format!)。

PluginRequestProcessor::plugin_apps_needing_auth_for_install1649–1712 ↗
async fn plugin_apps_needing_auth_for_install(
        &self,
        config: &Config,
        is_chatgpt_auth: bool,
        plugin_id: &str,
        plugin_apps: &[codex_plugin::AppConnectorId],

作用:安装插件后,找出插件声明的哪些应用连接器还需要用户授权。

数据流:输入是配置、登录类型、插件编号和插件应用列表;它同时拉取所有连接器和当前可访问连接器,失败时尝试用缓存,最后比较差异;输出需要授权的 AppSummary 列表。

调用关系:本地和远程安装完成后都会调用它;最后的差异判断交给 plugin_apps_needing_auth。

调用图:调用 4 个内部函数(plugin_apps_needing_auth, connectors_for_plugin_apps, list_cached_all_connectors, list_cached_accessible_connectors_from_mcp_tools);被 2 处调用(plugin_install_response, remote_plugin_install_response);外部调用 4 个(new, is_empty, join!, warn!)。

PluginRequestProcessor::start_plugin_mcp_oauth_logins1714–1796 ↗
async fn start_plugin_mcp_oauth_logins(
        &self,
        config: &Config,
        plugin_mcp_servers: HashMap<String, McpServerConfig>,
    )

作用:安装插件后,给插件里的 MCP 服务尝试后台 OAuth 登录。OAuth 是网页登录授权,能让服务拿到访问权限。

数据流:输入是配置和一组 MCP 服务配置;它逐个检查服务是否支持 OAuth,解析权限范围,启动后台任务静默登录,必要时不带 scope 重试,最后发送登录成功或失败通知;输出是异步通知副作用。

调用关系:plugin_install_response 和 remote_plugin_install_response 在发现插件带 MCP 服务时调用它;登录动作交给 perform_oauth_login_silent。

调用图:被 2 处调用(plugin_install_response, remote_plugin_install_response);外部调用 8 个(clone, McpServerOauthLoginCompleted, auth_keyring_backend_kind, oauth_login_support, should_retry_without_scopes, perform_oauth_login_silent, spawn, warn!)。

PluginRequestProcessor::plugin_uninstall_response1798–1827 ↗
async fn plugin_uninstall_response(
        &self,
        params: PluginUninstallParams,
    ) -> Result<PluginUninstallResponse, JSONRPCErrorError>

作用:卸载插件的主流程,能处理本地插件编号,也能处理远程插件编号。

数据流:输入是插件编号;它先判断编号是否合法,如果是远程编号就转给 remote_plugin_uninstall_response,否则调用本地插件管理器卸载,随后刷新配置或清缓存;输出空响应。

调用关系:plugin_uninstall 调用它;本地卸载成功后会触发 on_effective_plugins_changed。

调用图:调用 7 个内部函数(invalid_request, clear_plugin_related_caches, load_latest_config, on_effective_plugins_changed, remote_plugin_uninstall_response, is_valid_remote_plugin_id, parse);被 1 处调用(plugin_uninstall);外部调用 1 个(warn!)。

PluginRequestProcessor::plugin_install_error1829–1851 ↗
fn plugin_install_error(err: CorePluginInstallError) -> JSONRPCErrorError

作用:把核心插件安装模块的错误,翻译成客户端协议里的错误。

数据流:输入是 CorePluginInstallError;它先看是不是用户请求错误,是就返回 invalid_request,否则按配置、远程、线程、存储等类型包装成 internal_error;输出 JSONRPCErrorError。

调用关系:plugin_install_response 在本地安装失败时用它统一报错口径。

调用图:调用 2 个内部函数(internal_error, invalid_request);外部调用 4 个(is_invalid_request, to_string, marketplace_error, format!)。

PluginRequestProcessor::plugin_uninstall_error1853–1875 ↗
fn plugin_uninstall_error(err: CorePluginUninstallError) -> JSONRPCErrorError

作用:把核心插件卸载模块的错误,翻译成客户端能理解的错误。

数据流:输入是 CorePluginUninstallError;它把无效请求变成 invalid_request,把配置、远程、线程、存储失败变成 internal_error;输出 JSONRPCErrorError。

调用关系:plugin_uninstall_response 在本地卸载失败时用它。

调用图:调用 2 个内部函数(internal_error, invalid_request);外部调用 4 个(is_invalid_request, to_string, format!, unreachable!)。

PluginRequestProcessor::marketplace_error1877–1887 ↗
fn marketplace_error(err: MarketplaceError, action: &str) -> JSONRPCErrorError

作用:把插件市场相关错误转换成 JSON-RPC 错误,并区分“用户给错了”和“服务器内部失败”。

数据流:输入是 MarketplaceError 和动作说明;找不到市场、插件不可用、配置格式不对等返回 invalid_request,磁盘 IO 失败返回 internal_error;输出 JSONRPCErrorError。

调用关系:安装、读取、列表等本地市场操作失败时都会通过它统一翻译错误。

调用图:调用 2 个内部函数(internal_error, invalid_request);外部调用 2 个(to_string, format!)。

PluginRequestProcessor::remote_plugin_uninstall_response1889–1930 ↗
async fn remote_plugin_uninstall_response(
        &self,
        plugin_id: String,
    ) -> Result<PluginUninstallResponse, JSONRPCErrorError>

作用:卸载远程插件,并处理本地远程插件缓存的刷新。

数据流:输入是远程插件编号;它加载配置、检查功能、校验编号、调用远程卸载接口,成功或只剩缓存删除问题时清远程已安装缓存并安排刷新;输出空响应或错误。

调用关系:plugin_uninstall_response 识别出远程编号后调用它;它会用 effective_plugins_changed_callback 和 on_effective_plugins_changed 触发后续刷新。

调用图:调用 6 个内部函数(invalid_request, effective_plugins_changed_callback, load_latest_config, on_effective_plugins_changed, uninstall_remote_plugin, validate_remote_plugin_id);被 1 处调用(plugin_uninstall_response);外部调用 1 个(matches!)。

load_plugin_app_summaries1933–1976 ↗
async fn load_plugin_app_summaries(
    config: &Config,
    plugin_apps: &[codex_plugin::AppConnectorId],
    app_category_by_id: &HashMap<String, String>,
) -> Vec<AppSummary>

作用:把插件声明的应用连接器编号,转换成客户端能显示的应用卡片信息。

数据流:输入是配置、插件应用编号列表和可选分类映射;它加载全部连接器元数据,失败时用缓存,再筛出插件用到的连接器并补分类;输出 AppSummary 列表。

调用关系:plugin_read_response 在展示插件详情时调用它,用来显示插件关联的应用。

调用图:调用 3 个内部函数(connectors_for_plugin_apps, list_all_connectors_with_options, list_cached_all_connectors);被 1 处调用(plugin_read_response);外部调用 3 个(new, is_empty, warn!)。

plugin_app_category_by_id_from_value1978–1983 ↗
fn plugin_app_category_by_id_from_value(value: &serde_json::Value) -> HashMap<String, String>

作用:从远程插件的应用清单 JSON 里提取每个应用的分类。

数据流:输入是一段 JSON 值;它解析成插件应用声明,挑出带 category 的项;输出从连接器编号到分类名的表。

调用关系:远程插件详情和远程安装流程需要按应用编号补分类时会用它。

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

plugin_apps_needing_auth1985–2022 ↗
fn plugin_apps_needing_auth(
    all_connectors: &[AppInfo],
    accessible_connectors: &[AppInfo],
    plugin_apps: &[codex_plugin::AppConnectorId],
    codex_apps_ready: bool,
) -> Vec<AppSummary>

作用:比较“插件需要的应用”和“用户已经可访问的应用”,找出还要授权的那些。

数据流:输入是全部连接器、可访问连接器、插件应用编号和 codex_apps 是否准备好;如果服务没准备好直接返回空,否则用集合比较并输出未授权应用摘要。

调用关系:plugin_apps_needing_auth_for_install 在收集完连接器状态后调用它做最后判断。

调用图:被 1 处调用(plugin_apps_needing_auth_for_install);外部调用 3 个(new, iter, iter)。

remote_marketplace_to_info2024–2037 ↗
fn remote_marketplace_to_info(marketplace: RemoteMarketplace) -> PluginMarketplaceEntry

作用:把远程市场对象转换成客户端市场条目。

数据流:输入是 RemoteMarketplace;它复制市场名和显示名,把路径设为空,并把每个远程插件摘要转换成 PluginSummary;输出 PluginMarketplaceEntry。

调用关系:plugin_list_response 和远程已安装加载流程会用它把远程结果塞进统一市场列表。

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

remote_plugin_summary_to_info2039–2057 ↗
fn remote_plugin_summary_to_info(summary: RemoteCatalogPluginSummary) -> PluginSummary

作用:把远程插件摘要转换成客户端统一的插件摘要。

数据流:输入是 RemoteCatalogPluginSummary;它保留远程编号、名称、安装状态、策略、可用性、界面和关键词,并把来源标成 Remote;输出 PluginSummary。

调用关系:remote_marketplace_to_info 和 remote_plugin_detail_to_info 都会用它。

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

remote_plugin_share_context_to_info2059–2078 ↗
fn remote_plugin_share_context_to_info(
    context: RemoteCatalogPluginShareContext,
) -> PluginShareContext

作用:把远程返回的插件分享上下文,转换成客户端能展示的分享信息。

数据流:输入是 RemoteCatalogPluginShareContext;它复制远程编号、版本、分享链接、创建者和成员,并转换可见性;输出 PluginShareContext。

调用关系:plugin_read_response 和 remote_plugin_summary_to_info 相关转换中会用它;可见性转换交给 remote_plugin_share_discoverability_to_info。

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

remote_plugin_share_discoverability_to_info2080–2094 ↗
fn remote_plugin_share_discoverability_to_info(
    discoverability: codex_core_plugins::remote::RemotePluginShareDiscoverability,
) -> PluginShareDiscoverability

作用:把远程服务的分享可见性,翻译回客户端协议里的可见性。

数据流:输入是远程 Listed、Unlisted 或 Private;它映射成客户端的同名枚举;输出 PluginShareDiscoverability。

调用关系:plugin_share_update_targets_response 和 remote_plugin_share_context_to_info 会调用它。

调用图:被 2 处调用(plugin_share_update_targets_response, remote_plugin_share_context_to_info)。

remote_plugin_detail_to_info2096–2146 ↗
fn remote_plugin_detail_to_info(
    detail: RemoteCatalogPluginDetail,
    apps: Vec<AppSummary>,
) -> PluginDetail

作用:把远程插件完整详情转换成客户端的插件详情。

数据流:输入是远程详情和已经整理好的应用摘要;它转换摘要、描述、技能、应用模板、MCP 服务等字段,并把本地路径留空;输出 PluginDetail。

调用关系:plugin_read_response 在读取远程插件详情后调用它;插件摘要部分交给 remote_plugin_summary_to_info。

调用图:调用 1 个内部函数(remote_plugin_summary_to_info);被 1 处调用(plugin_read_response);外部调用 1 个(new)。

remote_plugin_catalog_error_to_jsonrpc2148–2179 ↗
fn remote_plugin_catalog_error_to_jsonrpc(
    err: RemotePluginCatalogError,
    context: &str,
) -> JSONRPCErrorError

作用:把远程插件目录服务的各种错误,翻译成 JSON-RPC 错误。

数据流:输入是 RemotePluginCatalogError 和上下文说明;它把需要登录、认证模式不支持、404、路径无效等归为 invalid_request,把网络、解析、缓存、归档等问题归为 internal_error;输出 JSONRPCErrorError。

调用关系:plugin_list_response 等远程目录请求失败时会调用它,保证客户端收到一致的错误格式。

调用图:调用 2 个内部函数(internal_error, invalid_request);被 1 处调用(plugin_list_response);外部调用 1 个(format!)。

remote_plugin_bundle_install_error_to_jsonrpc2181–2185 ↗
fn remote_plugin_bundle_install_error_to_jsonrpc(
    err: codex_core_plugins::remote_bundle::RemotePluginBundleInstallError,
) -> JSONRPCErrorError

作用:把远程插件包安装失败包装成服务器内部错误。

数据流:输入是远程插件包安装错误;它加上“install remote plugin bundle”的上下文文字;输出 internal_error。

调用关系:remote_plugin_install_response 在验证、下载或安装远程插件包失败时会用它。

调用图:调用 1 个内部函数(internal_error);外部调用 1 个(format!)。

app-server/src/request_processors/remote_control_processor.rs源码 ↗
orchestrationrequest handling

远程控制就像给这个应用开一扇“别人也能连进来操作”的门,所以不能随便开、随便配对、随便删客户端。这个文件把这些入口集中在 RemoteControlRequestProcessor 里:每次请求进来,它先拿到 RemoteControlHandle,也就是通往远程控制系统的把手;如果这个服务器根本没启用远程控制,或者当前环境不允许,就直接返回 JSON-RPC 错误。JSON-RPC 是一种用 JSON 传请求和响应的通信格式。然后它把具体事情交给 handle,比如启用、停用、开始配对、查询配对状态、列出或撤销客户端。文件里还专门把底层的 io::Error(读写文件、权限等错误)翻译成外部能理解的错误:用户填错或没权限算 invalid request,真正的系统故障算 internal error。

函数细节15
RemoteControlRequestProcessor::new26–30 ↗
fn new(remote_control_handle: Option<RemoteControlHandle>) -> Self

作用:创建一个远程控制请求处理器,把可用的远程控制把手保存起来。有人后面要处理远程控制请求时,会先用它组装出这个接待员对象。

数据流:进去的是一个可能存在、也可能不存在的 RemoteControlHandle;函数不做额外检查,只把它放进 RemoteControlRequestProcessor;出来的是一个新的处理器实例,后续所有远程控制请求都会从这里开始。

调用关系:它是在初始化处理器时被调用的,也会被相关测试直接使用。它本身不处理请求,只为 enable、disable、pairing_start 等方法准备好共同使用的内部把手。

调用图:被 3 处调用(new, pairing_start_returns_internal_error_when_remote_control_is_unavailable, pairing_status_returns_internal_error_when_remote_control_is_unavailable)。

RemoteControlRequestProcessor::enable32–47 ↗
async fn enable(
        &self,
        ephemeral: bool,
        app_server_client_name: Option<&str>,
    ) -> Result<RemoteControlEnableResponse, JSONRPCErrorError>

作用:处理“开启远程控制”的请求。它会区分临时开启和正常开启,并把开启后的状态包装成对外返回的响应。

数据流:进去的是 ephemeral 标记和可选的客户端名字;它先通过 handle 拿到可用的远程控制把手,再按标记调用临时开启或正常开启;成功后把内部状态转换成 RemoteControlEnableResponse,失败时把错误翻译成 JSON-RPC 错误。

调用关系:它由 handle_initialized_client_request 在收到对应客户端请求时调用。它先依赖 RemoteControlRequestProcessor::handle 做总检查,再把真正的开启动作交给 RemoteControlHandle,最后用 from 转成协议层响应。

调用图:调用 2 个内部函数(from, handle);被 1 处调用(handle_initialized_client_request)。

RemoteControlRequestProcessor::disable49–64 ↗
async fn disable(
        &self,
        ephemeral: bool,
        app_server_client_name: Option<&str>,
    ) -> Result<RemoteControlDisableResponse, JSONRPCErrorError>

作用:处理“关闭远程控制”的请求。它同样支持临时模式和正常模式,保证关闭结果用统一格式返回给调用方。

数据流:进去的是 ephemeral 标记和可选客户端名字;它先确认远程控制把手存在且允许使用,然后选择调用临时关闭或正常关闭;出来的是 RemoteControlDisableResponse,或者是翻译后的 JSON-RPC 错误。

调用关系:它由 handle_initialized_client_request 在收到关闭请求时调用。它复用 handle 做安全门槛检查,再调用底层关闭方法,并把底层更新错误通过 map_update_error 转成适合外部看到的错误。

调用图:调用 2 个内部函数(from, handle);被 1 处调用(handle_initialized_client_request)。

RemoteControlRequestProcessor::status_read66–74 ↗
fn status_read(&self) -> Result<RemoteControlStatusReadResponse, JSONRPCErrorError>

作用:读取当前远程控制状态,比如是否开启、服务器名字、安装编号和环境编号。它用于让客户端知道现在这扇“远程控制门”处在什么状态。

数据流:进去时不需要额外参数;它通过 handle 取得可用把手,再读取 status;出来的是 RemoteControlStatusReadResponse,里面带上状态和几个身份信息,失败则返回 JSON-RPC 错误。

调用关系:它由 handle_initialized_client_request 在收到状态查询时调用。它不改变系统,只通过 handle 经过统一检查后读取当前状态。

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

RemoteControlRequestProcessor::pairing_start76–85 ↗
async fn pairing_start(
        &self,
        params: RemoteControlPairingStartParams,
        app_server_client_name: Option<&str>,
    ) -> Result<RemoteControlPairingStartResponse, JSONRPCErrorErr

作用:开始一次远程控制配对。配对可以理解成给新设备发一张“临时入场券”,让它后面能被确认并连接。

数据流:进去的是配对启动参数和可选的客户端名字;它先拿到远程控制把手,再把参数交给 start_pairing;出来的是配对启动响应,或者把输入错误、系统错误翻译成 JSON-RPC 错误。

调用关系:它由 handle_initialized_client_request 在收到配对启动请求时调用。它自己不生成配对码,而是做入口检查后交给 RemoteControlHandle,并用 map_pairing_start_error 统一处理失败情况。

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

RemoteControlRequestProcessor::pairing_status87–97 ↗
async fn pairing_status(
        &self,
        params: RemoteControlPairingStatusParams,
    ) -> Result<RemoteControlPairingStatusResponse, JSONRPCErrorError>

作用:查询一次配对的进度或结果。它会先确认调用方只给了一种配对码,避免请求含糊不清。

数据流:进去的是配对状态查询参数;它先用 validate_pairing_status_params 检查 pairing_code 和 manual_pairing_code 是否二选一,再通过 handle 拿到底层把手并查询状态;出来的是配对状态响应或 JSON-RPC 错误。

调用关系:它由 handle_initialized_client_request 在收到配对状态请求时调用。它先把参数规则挡在门口,再把真正查询交给 RemoteControlHandle,错误走 map_pairing_start_error。

调用图:调用 2 个内部函数(handle, validate_pairing_status_params);被 1 处调用(handle_initialized_client_request)。

RemoteControlRequestProcessor::clients_list99–107 ↗
async fn clients_list(
        &self,
        params: RemoteControlClientsListParams,
    ) -> Result<RemoteControlClientsListResponse, JSONRPCErrorError>

作用:列出已经配对或登记过的远程控制客户端。它让管理者知道哪些设备目前有资格连接。

数据流:进去的是客户端列表查询参数;它先确认远程控制功能可用,再调用 list_clients;出来的是客户端列表响应,或者把权限、找不到、输入不对等问题转成 JSON-RPC 错误。

调用关系:它由 handle_initialized_client_request 在收到客户端列表请求时调用。它负责入口检查和错误翻译,具体读取客户端名单的工作交给 RemoteControlHandle。

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

RemoteControlRequestProcessor::clients_revoke109–117 ↗
async fn clients_revoke(
        &self,
        params: RemoteControlClientsRevokeParams,
    ) -> Result<RemoteControlClientsRevokeResponse, JSONRPCErrorError>

作用:撤销某个远程控制客户端的资格。通俗地说,就是把某台已授权设备从名单里踢出去。

数据流:进去的是要撤销哪个客户端的参数;它先通过 handle 确认能操作远程控制,再调用 revoke_client;出来的是撤销结果响应,或者是适合返回给请求方的 JSON-RPC 错误。

调用关系:它由 handle_initialized_client_request 在收到撤销请求时调用。它不直接改名单,而是把操作交给 RemoteControlHandle,并用 map_client_management_error 处理常见失败。

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

RemoteControlRequestProcessor::handle119–128 ↗
fn handle(&self) -> Result<&RemoteControlHandle, JSONRPCErrorError>

作用:这是所有远程控制操作共用的“门卫”。它检查远程控制把手是否存在,并确认当前环境允许使用远程控制。

数据流:进去的是处理器自身保存的 remote_control_handle;它先看有没有把手,没有就返回 internal error,再调用 ensure_remote_control_allowed 检查是否被规则禁止;出来的是可安全使用的 RemoteControlHandle 引用,或一个 JSON-RPC 错误。

调用关系:enable、disable、status_read、pairing_start、pairing_status、clients_list、clients_revoke 都先调用它。这样每个入口不用重复写同样的可用性和权限检查。

调用图:被 7 处调用(clients_list, clients_revoke, disable, enable, pairing_start, pairing_status, status_read)。

map_enable_error131–136 ↗
fn map_enable_error(err: RemoteControlEnableError) -> JSONRPCErrorError

作用:把“开启远程控制”时出现的内部错误,翻译成对外的 JSON-RPC 错误。这样外部调用方不会看到杂乱的内部错误类型。

数据流:进去的是 RemoteControlEnableError;如果是远程控制不可用,就交给 map_unavailable;如果是因为运行要求不满足而被禁用,就转成 invalid request;出来的是 JSONRPCErrorError。

调用关系:它被 enable 在临时开启失败时使用。它还会把不可用这种情况继续交给 map_unavailable,让错误分类保持一致。

调用图:调用 2 个内部函数(invalid_request, map_unavailable);外部调用 1 个(to_string)。

map_unavailable138–140 ↗
fn map_unavailable(err: RemoteControlUnavailable) -> JSONRPCErrorError

作用:把“远程控制不可用”这类问题转成 invalid request。意思是:请求本身在当前环境下办不了,而不是服务器代码崩了。

数据流:进去的是 RemoteControlUnavailable;它取出错误文字,包装成 invalid request;出来的是 JSON-RPC 错误对象。

调用关系:它由 map_enable_error 调用,专门处理开启远程控制时遇到的不可用情况。它是一个很小的翻译器。

调用图:调用 1 个内部函数(invalid_request);被 1 处调用(map_enable_error);外部调用 1 个(to_string)。

map_update_error142–151 ↗
fn map_update_error(err: io::Error) -> JSONRPCErrorError

作用:把开启或关闭时涉及保存状态的读写错误,分成“用户可理解的问题”和“服务器内部问题”。比如文件不存在或没权限,更像当前请求无法完成。

数据流:进去的是 io::Error,也就是系统读写或权限相关错误;它查看错误种类,如果是 NotFound 或 PermissionDenied,就转成 invalid request;其他未知错误转成 internal error;出来的是 JSON-RPC 错误。

调用关系:它被 enable 的正常开启路径和 disable 的正常关闭路径间接使用。它让状态更新失败时,对外返回的错误更准确。

调用图:调用 2 个内部函数(internal_error, invalid_request);外部调用 2 个(to_string, matches!)。

map_pairing_start_error153–159 ↗
fn map_pairing_start_error(err: io::Error) -> JSONRPCErrorError

作用:把配对相关操作的读写错误翻译成 JSON-RPC 错误。特别是参数不合法时,会明确告诉调用方这是请求有问题。

数据流:进去的是 io::Error;它检查错误种类,如果是 InvalidInput,就转成 invalid request;其他错误都按 internal error 处理;出来的是 JSON-RPC 错误。

调用关系:pairing_start 和 pairing_status 在调用底层配对功能失败时会用它。它让配对流程的错误口径统一。

调用图:调用 2 个内部函数(internal_error, invalid_request);外部调用 2 个(kind, to_string)。

validate_pairing_status_params161–173 ↗
fn validate_pairing_status_params(
    params: &RemoteControlPairingStatusParams,
) -> Result<(), JSONRPCErrorError>

作用:检查查询配对状态时的参数是否清楚。规则是 pairingCode 和 manualPairingCode 必须二选一,不能都给,也不能都不给。

数据流:进去的是 RemoteControlPairingStatusParams;它查看两个可选配对码的组合;如果正好有一个,就返回成功,否则返回 invalid request,并说明哪里填错了。

调用关系:它只被 pairing_status 调用,而且是在真正查询底层状态之前调用。它像表单提交前的检查,避免把含糊或矛盾的请求传进系统深处。

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

map_client_management_error175–183 ↗
fn map_client_management_error(err: io::Error) -> JSONRPCErrorError

作用:把管理客户端名单时的系统错误翻译成外部能懂的错误。它覆盖列名单、撤销客户端这类操作常见的失败情况。

数据流:进去的是 io::Error;它看错误种类,如果是输入不对、找不到、没权限或暂时无法操作,就转成 invalid request;其他异常转成 internal error;出来的是 JSON-RPC 错误。

调用关系:clients_list 和 clients_revoke 在底层客户端管理失败时会用它。它把名单管理相关操作的错误返回方式统一起来。

调用图:调用 2 个内部函数(internal_error, invalid_request);外部调用 2 个(kind, to_string)。

app-server/src/request_processors/search.rs源码 ↗
orchestrationrequest handling

用户在界面里输入几个字母找文件时,搜索可能会很频繁:刚输入“a”,马上又输入“ab”。这个文件就像搜索请求的调度员,负责把客户端来的请求交给真正的文件搜索模块,同时记住哪些搜索还在跑。对于一次性搜索,它会用 cancellation token(取消令牌,可以理解成一张“如果有新任务就停掉旧任务”的票)找到旧任务并通知它停止,避免慢的旧结果盖过新的结果。对于持续搜索会话,它会保存 session_id(会话编号)对应的搜索会话,后续输入变化时只更新查询内容,结束时再移除会话。这里还会检查一些明显不对的请求,比如空的会话编号,并把错误包装成客户端能看懂的 JSON-RPC 错误。

函数细节5
SearchRequestProcessor::new31–37 ↗
fn new(outgoing: Arc<OutgoingMessageSender>) -> Self

作用:创建一个搜索请求处理器。它准备好发送结果用的通道,以及两本“登记簿”:一本记正在跑的一次性搜索,另一本记正在开的搜索会话。

数据流:进去的是 outgoing,也就是用来往客户端发消息的发送器 → 函数把它保存起来,并新建两个空的共享表:pending_fuzzy_searches 记录可取消的搜索,fuzzy_search_sessions 记录长期搜索会话 → 出来的是一个可以被复制使用的 SearchRequestProcessor。

调用关系:它通常在服务初始化或上层处理器创建时被调用。后面的搜索请求都会通过这个对象进入,再由它去调用具体的搜索功能或操作已保存的会话。

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

SearchRequestProcessor::fuzzy_file_search_session_start_response81–100 ↗
async fn fuzzy_file_search_session_start_response(
        &self,
        params: FuzzyFileSearchSessionStartParams,
    ) -> Result<FuzzyFileSearchSessionStartResponse, JSONRPCErrorError>

作用:开启一个持续的模糊文件搜索会话。适合用户边输入边搜索的场景,因为会话开好后可以不断更新关键词,而不是每次都从零开始。

数据流:进去的是 session_id 和 roots:会话编号以及要搜索的目录 → 它先检查 session_id 不能为空;如果为空,就返回 invalid_request 这种“请求本身不合格”的错误;如果没问题,就调用 start_fuzzy_file_search_session 创建会话,并把 outgoing 发送器交给它,让会话以后能主动发搜索结果 → 创建成功后,把 session_id 和会话对象存进 fuzzy_search_sessions;出来的是一个空的成功响应,如果启动失败则返回内部错误。

调用关系:它由 handle_initialized_client_request 在客户端请求开始搜索会话时调用。它把真正创建会话的工作交给 start_fuzzy_file_search_session,自己负责参数检查、错误转换,以及把新会话登记起来,方便后续更新和停止。

调用图:调用 2 个内部函数(invalid_request, start_fuzzy_file_search_session);被 1 处调用(handle_initialized_client_request)。

SearchRequestProcessor::fuzzy_file_search_session_update_response102–123 ↗
async fn fuzzy_file_search_session_update_response(
        &self,
        params: FuzzyFileSearchSessionUpdateParams,
    ) -> Result<FuzzyFileSearchSessionUpdateResponse, JSONRPCErrorError>

作用:更新某个已存在搜索会话的关键词。用户继续打字时,客户端会用它告诉服务器:同一个搜索框,现在要搜这个新词。

数据流:进去的是 session_id 和新的 query → 它到 fuzzy_search_sessions 这本登记簿里找对应会话;找到了,就调用会话的 update_query,把新关键词交给会话;没找到,就返回 invalid_request,说明客户端给了一个不存在的会话编号 → 成功时返回一个空的更新成功响应。

调用关系:它由 handle_initialized_client_request 在客户端更新搜索会话时调用。它不自己搜索文件,而是找到已经运行的 FuzzyFileSearchSession,再把新 query 交给那个会话处理。

调用图:调用 1 个内部函数(invalid_request);被 1 处调用(handle_initialized_client_request);外部调用 1 个(format!)。

SearchRequestProcessor::fuzzy_file_search_session_stop125–133 ↗
async fn fuzzy_file_search_session_stop(
        &self,
        params: FuzzyFileSearchSessionStopParams,
    ) -> Result<FuzzyFileSearchSessionStopResponse, JSONRPCErrorError>

作用:停止并忘掉一个搜索会话。用户关闭搜索框、离开页面,或者客户端不再需要持续搜索时,会调用它做收尾。

数据流:进去的是 session_id → 它从 fuzzy_search_sessions 这本登记簿里删除对应会话;如果本来就没有这个会话,也不会报错 → 出来的是一个空的停止成功响应,表示服务器这边已经不再保留该会话。

调用关系:它由 handle_initialized_client_request 在客户端请求结束搜索会话时调用。它是会话生命周期的收尾步骤,和开始会话、更新会话配成一组,负责释放这份会话记录。

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

exec 服务器 RPC 处理

本组涵盖 exec 服务器的 JSON-RPC 传输、方法注册,以及将传入方法转换为本地进程和文件系统操作的各功能处理器。

exec-server/src/server/handler.rs源码 ↗
orchestrationrequest handling, shutdown

这个文件定义了 ExecServerHandler,可以把它理解成服务器门口的前台加调度员。客户端连接上来后,必须先调用 initialize 绑定一个会话,再发 initialized 表示准备好了;之后才能执行进程命令、读写进程输入输出、访问本地文件系统,或者代发 HTTP 请求。它自己不真正执行所有重活,而是把任务转交给会话里的进程处理器、FileSystemHandler 文件处理器、ReqwestHttpRequestRunner HTTP 请求器。它还保存一些“状态灯”:是否已经请求初始化、是否已经完成初始化、当前会话是否仍属于这个连接。HTTP 响应体如果要一段段传回来,它会记录正在活动的流,防止同一个 requestId 重复占用,并用后台任务继续推送数据。关闭时,它会取消后台任务、等待它们结束、关闭文件系统资源,并从会话上解绑,避免留下悬挂任务或占着会话不放。

函数细节29
ExecServerHandler::new74–90 ↗
fn new(
        session_registry: Arc<SessionRegistry>,
        notifications: RpcNotificationSender,
        runtime_paths: ExecServerRuntimePaths,
    ) -> Self

作用:创建一个新的请求处理器,准备好会话登记簿、通知通道、文件系统处理器和各种状态开关。服务器每开一条连接时通常会用它来搭好这条连接的“控制台”。

数据流:进去的是共享的会话登记簿、发送通知用的对象、运行时路径信息 → 它把这些装进 ExecServerHandler,并新建空会话、空的 HTTP 流记录、取消令牌和后台任务跟踪器 → 出来的是一个可以开始接收请求的处理器。

调用关系:它是整个处理器的起点。run_connection 这类连接运行代码会创建它,测试也会创建它来模拟真实连接;之后 initialize、exec、文件和 HTTP 方法都靠这个对象工作。

调用图:调用 1 个内部函数(new);被 5 处调用(active_session_resume_is_rejected, initialized_handler, long_poll_read_fails_after_session_resume, output_and_exit_are_retained_after_notification_receiver_closes, run_connection);外部调用 6 个(new, new, new, new, new, new)。

ExecServerHandler::shutdown92–100 ↗
async fn shutdown(&self)

作用:关闭这条连接相关的所有东西,防止后台任务、文件句柄或会话还留着。它像下班前的关灯锁门流程。

数据流:进去的是当前处理器自身保存的状态 → 它发出取消信号,关闭后台任务入口,等待已启动任务结束,再关闭文件系统处理器;如果当前有会话,就把这条连接从会话上 detach 掉 → 出来没有业务结果,但资源被清理,连接不再占用会话。

调用关系:它通常在连接结束或服务器收尾时被调用。它会先通过 session 取当前会话,再把清理工作交给后台任务跟踪器、文件系统处理器和会话对象。

调用图:调用 2 个内部函数(shutdown, session);外部调用 3 个(cancel, close, wait)。

ExecServerHandler::is_session_attached102–105 ↗
fn is_session_attached(&self) -> bool

作用:检查这条连接手里的会话是不是还有效。会话可能被另一个连接恢复走,所以这里用来判断当前连接还能不能继续代表它做事。

数据流:进去没有额外参数,只读取处理器里保存的会话 → 如果没有会话,按当前逻辑认为没有发现“已脱离”的问题;如果有会话,就询问会话是否仍然附着在本连接 → 出来是 true 或 false。

调用关系:它依赖 session 读取当前会话。外层连接管理或测试可以用它判断会话状态,而真正执行请求时更严格的检查由 require_session_attached 和 require_initialized_for 做。

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

ExecServerHandler::initialize107–139 ↗
async fn initialize(
        &self,
        params: InitializeParams,
    ) -> Result<InitializeResponse, JSONRPCErrorError>

作用:处理客户端的初始化请求,把这条连接绑定到一个新会话或恢复的旧会话。没有这一步,后面的执行命令、文件访问和 HTTP 操作都不能开始。

数据流:进去的是 InitializeParams,里面可能带着要恢复的 session id → 它先用原子开关检查 initialize 是否已经发过;如果重复发送就报错;否则向 SessionRegistry 申请 attach,会话绑定成功后记录 session id 和会话句柄 → 出来是 InitializeResponse,告诉客户端当前 session_id;如果绑定失败,会把初始化请求标记退回去。

调用关系:这是客户端连接开始后的第一道正式流程。它把活儿交给 SessionRegistry 来创建或恢复会话,并保存 SessionHandle;initialized 和后续所有请求都依赖它先成功。

调用图:调用 1 个内部函数(invalid_request);外部调用 5 个(store, swap, lock, clone, debug!)。

ExecServerHandler::initialized141–149 ↗
fn initialized(&self) -> Result<(), String>

作用:处理客户端的 initialized 通知,表示“我已经完成初始化握手,可以开始干活了”。它是 initialize 之后、真正执行操作之前的第二道门。

数据流:进去没有业务参数,只读取两个状态:是否发过 initialize、会话是否仍附着 → 如果 initialize 还没发,或者会话已无效,就返回错误;否则把 initialized 状态设为 true → 出来是成功或一条说明原因的错误字符串。

调用关系:它在 initialize 成功后被调用。它会使用 require_session_attached 确认会话还在;之后 exec、HTTP、文件系统等方法都会通过 require_initialized_for 检查这个标记。

调用图:调用 1 个内部函数(require_session_attached);外部调用 2 个(load, store)。

ExecServerHandler::exec151–154 ↗
async fn exec(&self, params: ExecParams) -> Result<ExecResponse, JSONRPCErrorError>

作用:启动一个要执行的进程或命令。它先确认连接已经完成初始化,再把真正的执行工作交给会话里的进程处理器。

数据流:进去的是 ExecParams,描述要执行什么 → 它通过 require_initialized_for 检查 initialize 和 initialized 都已完成,并拿到有效会话 → 把参数交给 session.process().exec → 出来是 ExecResponse,通常包含进程启动后的信息。

调用关系:它是执行命令这一组功能的入口之一。自己只做把关和转发,实际工作由会话中的 process 组件完成。

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

ExecServerHandler::environment_info156–159 ↗
fn environment_info(&self) -> Result<EnvironmentInfo, JSONRPCErrorError>

作用:返回服务器本机的环境信息,比如让客户端知道自己正在和什么样的运行环境打交道。它同样要求客户端已经完成初始化。

数据流:进去没有业务参数 → 它先用 require_initialized_for 检查当前连接是否有资格查询环境 → 然后调用 EnvironmentInfo::local 收集本地环境信息 → 出来是 EnvironmentInfo。

调用关系:它属于查询类请求。它不依赖进程会话做重活,但仍然使用 require_initialized_for 保证调用顺序正确。

调用图:调用 1 个内部函数(require_initialized_for);外部调用 1 个(local)。

ExecServerHandler::exec_read161–169 ↗
async fn exec_read(
        &self,
        params: ReadParams,
    ) -> Result<ReadResponse, JSONRPCErrorError>

作用:从已经启动的进程读取输出。它适合客户端持续拿标准输出、标准错误这类进程产生的数据。

数据流:进去的是 ReadParams,说明要读哪个进程、读多少或怎么读 → 它先检查初始化状态,交给 session.process().exec_read 读取数据;读完后再次确认会话没有被别人恢复走 → 出来是 ReadResponse,包含读到的内容或结束状态。

调用关系:它是进程读输出流程的一环,测试 read_process_until_closed 会用到它。它比普通转发多了一次会话检查,避免长轮询读取期间会话被别的连接接管后还继续返回旧连接的数据。

调用图:调用 2 个内部函数(require_initialized_for, require_session_attached);被 1 处调用(read_process_until_closed)。

ExecServerHandler::exec_write171–177 ↗
async fn exec_write(
        &self,
        params: WriteParams,
    ) -> Result<WriteResponse, JSONRPCErrorError>

作用:向正在运行的进程写入输入,比如给命令行程序喂一段文本。使用前必须完成初始化。

数据流:进去的是 WriteParams,包含目标进程和要写的数据 → 它通过 require_initialized_for 拿到有效会话 → 调用 session.process().exec_write 写入 → 出来是 WriteResponse,表示写入结果。

调用关系:它是进程交互流程的一部分。它只负责检查权限和转交,真正把字节写进进程输入的是会话里的 process 组件。

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

ExecServerHandler::signal179–185 ↗
async fn signal(
        &self,
        params: SignalParams,
    ) -> Result<SignalResponse, JSONRPCErrorError>

作用:给正在运行的进程发送信号,比如让它中断或按约定做某种动作。它提供了比直接杀掉进程更细的控制方式。

数据流:进去的是 SignalParams,说明给哪个进程发什么信号 → 它先检查初始化和会话状态 → 把请求交给 session.process().signal → 出来是 SignalResponse,说明发送是否成功。

调用关系:它属于进程控制功能。前置检查由 require_initialized_for 负责,实际信号发送由 process 组件完成。

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

ExecServerHandler::terminate187–193 ↗
async fn terminate(
        &self,
        params: TerminateParams,
    ) -> Result<TerminateResponse, JSONRPCErrorError>

作用:结束一个正在运行的进程。它用于客户端明确要求停止某个执行任务的时候。

数据流:进去的是 TerminateParams,指定要终止的进程 → 它确认连接已初始化且会话有效 → 调用 session.process().terminate → 出来是 TerminateResponse,表示终止请求的结果。

调用关系:它和 exec、signal 同属进程生命周期控制。这个函数只是守门和转发,底层终止动作在 process 组件里完成。

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

ExecServerHandler::http_request195–234 ↗
async fn http_request(
        self: &Arc<Self>,
        request_id: RequestId,
        params: HttpRequestParams,
    ) -> Result<(), JSONRPCErrorError>

作用:替客户端发起一次 HTTP 请求(HTTP 是网页和服务之间常用的请求协议)。如果响应体很大,它还能先返回响应头,再在后台一段段推送正文。

数据流:进去的是 JSON-RPC 请求 id 和 HttpRequestParams → 它先检查初始化;如果客户端要求流式响应,就预留 request_id,防止重复;然后用 ReqwestHttpRequestRunner 发请求;把响应对象转成 JSON 值并通过 notifications.response 回给客户端;如果还有待传的响应体,就启动后台任务继续传 → 出来通常是空结果,因为正式响应已经通过通知通道发走;出错时会释放已预留的流 id。

调用关系:这是 HTTP 子流程的总调度。它会调用 reserve_http_body_stream 占位,失败时或结束时调用 release_http_body_stream,必要时调用 start_http_body_stream 把后续正文交给后台任务。

调用图:调用 7 个内部函数(new, response, internal_error, release_http_body_stream, require_initialized_for, reserve_http_body_stream, start_http_body_stream);外部调用 1 个(to_value)。

ExecServerHandler::fs_read_file236–242 ↗
async fn fs_read_file(
        &self,
        params: FsReadFileParams,
    ) -> Result<FsReadFileResponse, JSONRPCErrorError>

作用:读取一个文件的全部内容。它给客户端提供受控的本地文件读取入口。

数据流:进去的是 FsReadFileParams,说明要读哪个文件 → 它先确认客户端已完成初始化 → 把请求交给 FileSystemHandler.read_file → 出来是 FsReadFileResponse,包含文件内容或错误。

调用关系:它是文件系统功能的一个薄入口。所有安全顺序检查在这里做,具体读文件由 file_system 组件处理。

调用图:调用 2 个内部函数(read_file, require_initialized_for)。

ExecServerHandler::fs_open244–250 ↗
async fn fs_open(
        &self,
        params: FsOpenParams,
    ) -> Result<FsOpenResponse, JSONRPCErrorError>

作用:打开一个文件,通常用于后续分块读取。这样不必一次把大文件全读进来。

数据流:进去的是 FsOpenParams,描述要打开的文件 → 它先用 require_initialized_for 检查资格 → 调用 FileSystemHandler.open → 出来是 FsOpenResponse,通常包含后续读取用的句柄或标识。

调用关系:它开启分块文件读取流程。后续 fs_read_block 会基于这里打开的文件继续读,fs_close 会负责关闭。

调用图:调用 2 个内部函数(open, require_initialized_for)。

ExecServerHandler::fs_read_block252–258 ↗
async fn fs_read_block(
        &self,
        params: FsReadBlockParams,
    ) -> Result<FsReadBlockResponse, JSONRPCErrorError>

作用:从已打开的文件里读取一块内容。它适合读取大文件时分段取数据。

数据流:进去的是 FsReadBlockParams,说明哪个打开的文件、从哪里读、读多少 → 它先检查初始化 → 调用 FileSystemHandler.read_block → 出来是 FsReadBlockResponse,包含这一块数据。

调用关系:它通常跟在 fs_open 后面使用,最后配合 fs_close 收尾。处理器只转发,文件句柄和读取细节由 file_system 组件掌握。

调用图:调用 2 个内部函数(read_block, require_initialized_for)。

ExecServerHandler::fs_close260–266 ↗
async fn fs_close(
        &self,
        params: FsCloseParams,
    ) -> Result<FsCloseResponse, JSONRPCErrorError>

作用:关闭之前打开的文件。它用于释放文件句柄,避免像借书不还一样一直占着系统资源。

数据流:进去的是 FsCloseParams,指定要关闭的文件句柄 → 它确认初始化状态 → 调用 FileSystemHandler.close → 出来是 FsCloseResponse,表示关闭是否成功。

调用关系:它是 fs_open / fs_read_block 这条分块读取流程的收尾步骤。资源释放的具体动作由 file_system 组件执行。

调用图:调用 2 个内部函数(close, require_initialized_for)。

ExecServerHandler::fs_write_file268–274 ↗
async fn fs_write_file(
        &self,
        params: FsWriteFileParams,
    ) -> Result<FsWriteFileResponse, JSONRPCErrorError>

作用:把内容写入文件。它让客户端可以创建或覆盖本地文件,前提是连接已经完成初始化。

数据流:进去的是 FsWriteFileParams,包含目标路径和要写入的内容 → 它先做初始化检查 → 调用 FileSystemHandler.write_file → 出来是 FsWriteFileResponse,说明写入结果。

调用关系:它属于文件修改类入口。Handler 负责守门,FileSystemHandler 负责真正碰磁盘。

调用图:调用 2 个内部函数(write_file, require_initialized_for)。

ExecServerHandler::fs_create_directory276–282 ↗
async fn fs_create_directory(
        &self,
        params: FsCreateDirectoryParams,
    ) -> Result<FsCreateDirectoryResponse, JSONRPCErrorError>

作用:创建目录,也就是新建文件夹。客户端需要准备工作目录时会用到它。

数据流:进去的是 FsCreateDirectoryParams,说明要创建哪个目录以及可能的选项 → 它先确认客户端已初始化 → 调用 FileSystemHandler.create_directory → 出来是 FsCreateDirectoryResponse。

调用关系:它是文件系统写操作的一种。它和写文件、删除、复制一样,都先经过 require_initialized_for,再交给 file_system。

调用图:调用 2 个内部函数(create_directory, require_initialized_for)。

ExecServerHandler::fs_get_metadata284–290 ↗
async fn fs_get_metadata(
        &self,
        params: FsGetMetadataParams,
    ) -> Result<FsGetMetadataResponse, JSONRPCErrorError>

作用:查看文件或目录的基本信息,比如它是否存在、是文件还是目录、大小等。它不读取文件正文。

数据流:进去的是 FsGetMetadataParams,指定路径 → 它先检查初始化状态 → 调用 FileSystemHandler.get_metadata → 出来是 FsGetMetadataResponse,包含这些属性信息。

调用关系:它是文件系统查询入口。常用于客户端决定下一步要读、写、复制还是删除之前。

调用图:调用 2 个内部函数(get_metadata, require_initialized_for)。

ExecServerHandler::fs_canonicalize292–298 ↗
async fn fs_canonicalize(
        &self,
        params: FsCanonicalizeParams,
    ) -> Result<FsCanonicalizeResponse, JSONRPCErrorError>

作用:把路径整理成系统认可的标准绝对路径。比如把带有“..”或相对路径的写法变成清楚明确的位置。

数据流:进去的是 FsCanonicalizeParams,包含待整理路径 → 它先确认初始化 → 调用 FileSystemHandler.canonicalize → 出来是 FsCanonicalizeResponse,包含规范化后的路径。

调用关系:它帮助客户端和服务器对同一个文件位置达成一致。具体路径解析由 file_system 完成,Handler 只负责调用顺序检查。

调用图:调用 2 个内部函数(canonicalize, require_initialized_for)。

ExecServerHandler::fs_read_directory300–306 ↗
async fn fs_read_directory(
        &self,
        params: FsReadDirectoryParams,
    ) -> Result<FsReadDirectoryResponse, JSONRPCErrorError>

作用:列出一个目录里的内容。它相当于让客户端查看某个文件夹下面有哪些文件和子文件夹。

数据流:进去的是 FsReadDirectoryParams,指定目录路径 → 它先做初始化检查 → 调用 FileSystemHandler.read_directory → 出来是 FsReadDirectoryResponse,包含目录项列表。

调用关系:它是文件浏览流程的一部分。客户端可能先用它找文件,再调用读文件、获取元数据等函数。

调用图:调用 2 个内部函数(read_directory, require_initialized_for)。

ExecServerHandler::fs_remove308–314 ↗
async fn fs_remove(
        &self,
        params: FsRemoveParams,
    ) -> Result<FsRemoveResponse, JSONRPCErrorError>

作用:删除文件或目录。因为这是破坏性操作,所以同样必须先经过初始化检查。

数据流:进去的是 FsRemoveParams,说明要删除的路径和删除方式 → 它确认客户端已初始化且会话有效 → 调用 FileSystemHandler.remove → 出来是 FsRemoveResponse,说明删除结果。

调用关系:它是文件系统修改类操作。Handler 不直接删磁盘内容,而是把请求交给 FileSystemHandler。

调用图:调用 2 个内部函数(remove, require_initialized_for)。

ExecServerHandler::fs_copy316–322 ↗
async fn fs_copy(
        &self,
        params: FsCopyParams,
    ) -> Result<FsCopyResponse, JSONRPCErrorError>

作用:复制文件或目录。它让客户端能在服务器本地把一份内容拷贝到另一个位置。

数据流:进去的是 FsCopyParams,包含来源和目标 → 它先检查初始化 → 调用 FileSystemHandler.copy → 出来是 FsCopyResponse,说明复制是否成功。

调用关系:它属于文件系统操作入口之一。它和其他 fs_* 函数保持同样模式:先守门,再转交给 file_system。

调用图:调用 2 个内部函数(copy, require_initialized_for)。

ExecServerHandler::require_initialized_for324–340 ↗
fn require_initialized_for(
        &self,
        method_family: &str,
    ) -> Result<SessionHandle, JSONRPCErrorError>

作用:这是很多请求共用的“门禁检查”。它确认客户端已经先发 initialize,又发 initialized,并且当前会话还没有被别的连接接走。

数据流:进去的是 method_family,也就是出错提示里用的功能类别名称 → 它读取 initialize_requested 和 initialized 两个原子状态,并调用 require_session_attached 拿有效会话;任何一步不满足就生成 JSON-RPC 错误 → 出来是可用的 SessionHandle,或者说明调用顺序不对的错误。

调用关系:exec、environment_info、HTTP 和所有文件系统方法都会先经过它。它把会话有效性检查交给 require_session_attached,是本文件最核心的安全顺序守门函数。

调用图:调用 2 个内部函数(invalid_request, require_session_attached);被 18 处调用(environment_info, exec, exec_read, exec_write, fs_canonicalize, fs_close, fs_copy, fs_create_directory, fs_get_metadata, fs_open (+8 more));外部调用 2 个(load, format!)。

ExecServerHandler::require_session_attached342–355 ↗
fn require_session_attached(&self) -> Result<SessionHandle, JSONRPCErrorError>

作用:确认当前处理器确实有会话,而且这个会话还属于当前连接。这样可以防止一个旧连接在会话被新连接恢复后继续操作。

数据流:进去没有额外参数,只读取当前保存的 session → 如果没有 session,就返回“必须先 initialize”的错误;如果 session 仍 attached,就返回 SessionHandle;如果已被别的连接恢复,就返回“会话已被另一个连接接管”的错误 → 出来是有效会话或错误。

调用关系:initialized、exec_read 和 require_initialized_for 都会调用它。它内部通过 session 取得会话快照,是会话归属检查的集中位置。

调用图:调用 2 个内部函数(invalid_request, session);被 3 处调用(exec_read, initialized, require_initialized_for)。

ExecServerHandler::session357–362 ↗
fn session(&self) -> Option<SessionHandle>

作用:安全地取出当前保存的会话句柄。这里用互斥锁(一把锁,防止两个线程同时改同一份数据)保护会话字段。

数据流:进去没有参数 → 它锁住 session 字段,处理锁被污染的情况,然后克隆一份 Option<SessionHandle> → 出来是当前会话的副本,可能有,也可能没有。

调用关系:shutdown、is_session_attached 和 require_session_attached 都靠它读取会话。它把加锁细节藏起来,让其他函数不用重复写这些保护代码。

调用图:被 3 处调用(is_session_attached, require_session_attached, shutdown);外部调用 1 个(lock)。

ExecServerHandler::start_http_body_stream364–384 ↗
async fn start_http_body_stream(
        self: &Arc<Self>,
        pending_stream: PendingReqwestHttpBodyStream,
    )

作用:启动一个后台任务,把 HTTP 响应体继续一段段发送给客户端。它用于响应内容不能或不想一次性返回的情况。

数据流:进去的是 PendingReqwestHttpBodyStream,里面保存待传的响应体和 request_id → 如果处理器已经在关闭,就直接释放这个 request_id;否则复制所需的 handler、通知通道和关闭信号,启动后台任务;任务会在收到关闭信号或传输完成后结束 → 最后释放该 request_id 的占用。

调用关系:它只由 http_request 在发现有待传响应体时调用。真正传输正文交给 ReqwestHttpRequestRunner::stream_body;任务结束后调用 release_http_body_stream 做清理。

调用图:调用 1 个内部函数(release_http_body_stream);被 1 处调用(http_request);外部调用 6 个(clone, clone, is_cancelled, spawn, clone, select!)。

ExecServerHandler::release_http_body_stream386–389 ↗
async fn release_http_body_stream(&self, request_id: &str)

作用:释放一个 HTTP 流式响应的 request_id。也就是告诉处理器:这个响应体已经不再占用这个编号了。

数据流:进去的是 request_id 字符串 → 它锁住 active_body_stream_ids 集合,把这个 id 从集合里移除 → 出来没有返回值,但内部记录被更新,之后同名 id 可以再次被使用。

调用关系:http_request 在请求失败、序列化失败或通知发送失败时会调用它;start_http_body_stream 在后台传输结束时也会调用它。它负责防止流式请求记录泄漏。

调用图:被 2 处调用(http_request, start_http_body_stream)。

ExecServerHandler::reserve_http_body_stream391–400 ↗
async fn reserve_http_body_stream(&self, request_id: &str) -> Result<(), JSONRPCErrorError>

作用:预定一个 HTTP 流式响应的 request_id,防止两个正在传的响应体使用同一个编号,导致客户端分不清数据属于谁。

数据流:进去的是 request_id 字符串 → 它锁住 active_body_stream_ids 集合,检查这个 id 是否已存在;如果存在就返回参数错误;如果不存在就插入集合 → 出来是成功,或者“这个 requestId 已经在用”的错误。

调用关系:http_request 在发起带 streamResponse 的请求前调用它。之后如果请求失败或流传完,会由 release_http_body_stream 把这个预定释放掉。

调用图:调用 1 个内部函数(invalid_params);被 1 处调用(http_request);外部调用 1 个(format!)。

exec-server/src/rpc.rs源码 ↗
io_transportcross-cutting / request handling / teardown

JSON-RPC 可以理解成“用 JSON 写信办事”:一边发请求,另一边按方法名处理并回信。这个文件就是这套收发信规则的中间层。客户端发请求时,它会给每封信编号码,记住谁在等回信,等服务器回来了再按号码送回正确的人;就算回复顺序乱了,也不会搞错。服务端这边,RpcRouter 像前台分诊台,根据方法名把请求或通知交给对应处理函数,并把参数从 JSON 翻译成 Rust 能用的数据。RpcNotificationSender 则专门把服务端的回复或主动通知送出去。文件还统一生成 JSON-RPC 的常见错误码,比如参数错、方法不存在、内部错误。最重要的是,它很小心处理连接断开:一旦通道关了,所有还在等回复的请求都会立刻失败,避免程序永远卡住。

函数细节30
RpcNotificationSender::new71–73 ↗
fn new(outgoing_tx: mpsc::Sender<RpcServerOutboundMessage>) -> Self

作用:创建一个服务端用的通知发送器。它把“往外发消息”的通道包起来,后面代码只要拿着这个对象,就能发回复或通知。

数据流:进去的是一个消息发送通道 → 函数把它放进 RpcNotificationSender 结构里 → 出来的是一个可复制、可传给其他模块使用的发送器。

调用关系:在连接或会话建立时会被创建,然后交给会话、初始化、长轮询等流程。后续 http_request 会用它回请求,send_body_delta 会用它发通知。

调用图:被 6 处调用(default, active_session_resume_is_rejected, initialized_handler, long_poll_read_fails_after_session_resume, output_and_exit_are_retained_after_notification_receiver_closes, run_connection)。

RpcNotificationSender::response75–84 ↗
async fn response(
        &self,
        request_id: RequestId,
        result: Value,
    ) -> Result<(), JSONRPCErrorError>

作用:把某个请求的结果发回给对方。它保证回复里带着原请求的编号,这样对方知道这封回信对应哪件事。

数据流:进去的是请求编号和结果数据 → 函数把它包装成服务端出站消息 → 通过内部通道发出去;如果通道已经关了,就返回一个 JSON-RPC 内部错误。

调用关系:http_request 处理完实际工作后会调用它。它不直接写网络,而是把消息交给后面的连接发送流程。

调用图:被 1 处调用(http_request);外部调用 1 个(send)。

RpcNotificationSender::notify86–101 ↗
async fn notify(
        &self,
        method: &str,
        params: &P,
    ) -> Result<(), JSONRPCErrorError>

作用:让服务端主动给客户端发一条通知。通知和请求不同,不需要对方回信,适合报告进度、输出增量这类消息。

数据流:进去的是方法名和任意可序列化参数;序列化就是把程序里的数据变成 JSON → 函数组装成 JSON-RPC 通知 → 放进出站通道;如果参数转 JSON 失败或连接关了,就返回错误。

调用关系:send_body_delta 这类需要持续推送内容的地方会调用它。它把消息交给连接层发送,不等待客户端响应。

调用图:被 1 处调用(send_body_delta);外部调用 3 个(send, Notification, to_value)。

RpcRouter::default110–115 ↗
fn default() -> Self

作用:创建一个空的路由表。路由表可以理解成“方法名到处理函数的通讯录”。

数据流:没有业务输入 → 函数新建两个空表,一个放请求处理器,一个放通知处理器 → 返回还没注册任何方法的 RpcRouter。

调用关系:RpcRouter::new 会借它完成初始化。之后构建路由的代码会往里面登记具体方法。

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

RpcRouter::new122–124 ↗
fn new() -> Self

作用:创建一个新的 RPC 路由器。调用者用它来登记服务器能听懂哪些方法。

数据流:没有输入 → 调用默认构造逻辑 → 得到一个空路由器,等待后续注册请求或通知处理函数。

调用关系:build_router 会在启动服务端处理能力时调用它,然后继续调用 request、request_with_id、notification 把各个方法挂上去。

调用图:被 1 处调用(build_router);外部调用 1 个(default)。

RpcRouter::request126–160 ↗
fn request(&mut self, method: &'static str, handler: F)

作用:登记一个“有请求就必须回结果”的 RPC 方法。它把外部传来的 JSON 参数转成处理函数需要的类型,再把处理结果转回 JSON。

数据流:进去的是方法名和处理函数 → 函数保存一段包装逻辑:收到请求时先解析参数,再调用处理函数,最后把成功结果或错误包装成出站回复 → 路由表因此多了一个可处理的方法。

调用关系:路由搭建阶段会用它注册普通请求。运行时 run_connection 找到对应路由后,会间接执行这里保存的包装逻辑。

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

RpcRouter::request_with_id162–188 ↗
fn request_with_id(&mut self, method: &'static str, handler: F)

作用:登记一种特殊请求:处理函数需要知道请求编号,并且成功时不自动回普通结果。适合处理函数自己决定何时或如何回信的场景。

数据流:进去的是方法名和能接收请求编号的处理函数 → 函数保存包装逻辑,收到请求时解析参数并把编号一起交给处理函数 → 如果处理失败就生成错误回复,成功则不产生默认回复。

调用关系:路由搭建阶段使用。它让某些流程可以拿着 RequestId 延后回复,例如需要流式或异步协调的请求。

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

RpcRouter::notification190–210 ↗
fn notification(&mut self, method: &'static str, handler: F)

作用:登记一个“只接收、不回信”的通知方法。通知像广播消息,处理完就结束。

数据流:进去的是通知方法名和处理函数 → 函数保存包装逻辑:收到通知后解析参数,再调用处理函数 → 成功只表示处理完,失败返回一段错误文字供上层记录或断开连接。

调用关系:路由搭建阶段用它注册客户端发来的通知。运行时连接处理代码按方法名找到它,然后执行对应处理函数。

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

RpcRouter::request_route212–214 ↗
fn request_route(&self, method: &str) -> Option<&RequestRoute<S>>

作用:按方法名查找请求处理器。就像前台根据业务名称查应该找哪个窗口。

数据流:进去的是一个方法名字符串 → 函数在请求路由表里查找 → 找到就返回对应处理器,找不到就返回空。

调用关系:连接收到 JSON-RPC 请求时会用它找处理函数。找不到时,上层通常会返回“方法不存在”的错误。

RpcRouter::notification_route216–218 ↗
fn notification_route(&self, method: &str) -> Option<&NotificationRoute<S>>

作用:按方法名查找通知处理器。它用于判断某条通知服务器是否认识。

数据流:进去的是通知方法名 → 函数在通知路由表里查找 → 返回对应处理器或空结果。

调用关系:连接收到 JSON-RPC 通知时会用它分发消息。找到后交给登记时包装好的通知处理逻辑。

RpcClient::new235–293 ↗
fn new(connection: JsonRpcConnection) -> (Self, mpsc::Receiver<RpcClientEvent>)

作用:把底层 JSON-RPC 连接包装成一个好用的客户端。它还启动一个后台读消息任务,专门接收回复、通知和断开事件。

数据流:进去的是已经建立好的 JsonRpcConnection → 函数拆出读写通道、断开状态和传输任务,建立等待回复表,并启动 reader_task → 出来的是 RpcClient 和一个事件接收器,外部可以用客户端发请求,用事件接收器收通知或断开消息。

调用关系:connect 和测试会调用它。它启动的后台任务会持续调用 handle_server_message;一旦连接结束,会发送断开事件、调用 drain_pending 清空等待中的请求,并终止传输。

调用图:调用 2 个内部函数(drain_pending, handle_server_message);被 2 处调用(connect, rpc_client_matches_out_of_order_responses_by_request_id);外部调用 7 个(clone, new, new, new, new, channel, spawn)。

RpcClient::notify295–313 ↗
async fn notify(
        &self,
        method: &str,
        params: &P,
    ) -> Result<(), serde_json::Error>

作用:客户端主动给服务器发一条通知,不等待回复。适合告诉服务器某个状态变化或事件发生了。

数据流:进去的是方法名和参数 → 参数被转成 JSON,再包装成 JSON-RPC 通知 → 通过写通道发出;如果参数不能转 JSON 或通道断了,就返回序列化错误。

调用关系:这是客户端对外的发送通知入口。它直接把消息交给 JsonRpcConnection 的写通道,由连接层负责真正传输。

调用图:外部调用 3 个(send, Notification, to_value)。

RpcClient::is_disconnected315–317 ↗
fn is_disconnected(&self) -> bool

作用:快速查看客户端底层连接是否已经断开。调用者可以用它避免继续发无意义的请求。

数据流:没有业务输入 → 函数读取共享的断开状态标记 → 返回 true 或 false。

调用关系:它是一个状态查询工具。RpcClient::call 内部也会检查类似的断开状态,防止请求注册后没人回应。

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

RpcClient::call319–372 ↗
async fn call(&self, method: &str, params: &P) -> Result<T, RpcCallError>

作用:向服务器发一个请求,并等待对应回复。它会处理编号、等待、错误和结果类型转换,是客户端最核心的调用函数。

数据流:进去的是方法名和请求参数 → 函数生成新的请求编号,登记一个等待回复的小通道,把参数转成 JSON 并发出请求 → 后台读任务收到同编号回复后唤醒它;最后它把 JSON 结果转成调用者想要的类型并返回。若连接断开、服务器报错或 JSON 类型不对,就返回对应错误。

调用关系:外部业务代码会用它调用远端能力。它依赖 RpcClient::new 启动的 reader_task 来接收回复;reader_task 通过 handle_server_message 按请求编号把结果送回这里。

调用图:外部调用 9 个(fetch_add, send, Request, Integer, Json, borrow, channel, from_value, to_value)。

RpcClient::pending_request_count375–377 ↗
async fn pending_request_count(&self) -> usize

作用:返回当前还有多少请求在等回复。这个函数只在测试里启用,用来确认请求没有被遗漏。

数据流:没有输入 → 函数锁住等待回复表并读取长度 → 返回等待中的请求数量,不改变业务状态。

调用关系:测试 rpc_client_matches_out_of_order_responses_by_request_id 用它检查两次请求都正确完成,等待表已经清空。

RpcClient::drop381–387 ↗
fn drop(&mut self)

作用:当 RpcClient 被销毁时,立刻收拾后台资源。没有它,读写任务可能还在后台跑,造成泄漏或僵尸任务。

数据流:进去的是即将被释放的客户端对象 → 函数终止底层传输,并中止所有传输任务和读消息任务 → 客户端相关后台活动停止。

调用关系:Rust 在 RpcClient 生命周期结束时自动调用它。它是 RpcClient::new 启动那些后台任务的收尾动作。

调用图:调用 1 个内部函数(terminate);外部调用 1 个(abort)。

encode_server_message390–410 ↗
fn encode_server_message(
    message: RpcServerOutboundMessage,
) -> Result<JSONRPCMessage, serde_json::Error>

作用:把服务端内部使用的出站消息,转换成标准 JSON-RPC 消息。它像出门前把内部便条装进正式信封。

数据流:进去的是响应、错误或通知三种内部消息之一 → 函数按类型填入 JSON-RPC 的 Response、Error 或 Notification 结构 → 返回可交给连接层发送的 JSONRPCMessage。

调用关系:run_connection 在真正往外发服务器消息前会调用它。RpcNotificationSender 和路由处理器产生的内部消息,都要经过它变成协议消息。

调用图:被 1 处调用(run_connection);外部调用 3 个(Error, Notification, Response)。

invalid_request412–418 ↗
fn invalid_request(message: String) -> JSONRPCErrorError

作用:生成“请求格式不合法”的 JSON-RPC 错误。比如请求本身缺了必要字段或结构不对,就用这种错误告诉对方。

数据流:进去的是一段错误说明文字 → 函数填入标准错误码 -32600,并不附带额外数据 → 返回 JSONRPCErrorError。

调用关系:很多命令和初始化检查发现请求本身不合规时会调用它,上层再把这个错误包装成 RPC 错误回复。

调用图:被 12 处调用(map_fs_error, sandbox_cwd, spawn_command, exec_read, start_process, map_fs_error, validate_file_read_handle_id, initialize, require_initialized_for, require_session_attached (+2 more))。

method_not_found420–426 ↗
fn method_not_found(message: String) -> JSONRPCErrorError

作用:生成“这个方法不存在”的 JSON-RPC 错误。也就是客户端喊了一个服务器不认识的名字。

数据流:进去的是说明文字 → 函数填入标准错误码 -32601 → 返回可发送给客户端的错误对象。

调用关系:run_connection 在路由表里找不到方法时会用它,让客户端明确知道不是服务器崩了,而是方法名不对。

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

invalid_params428–434 ↗
fn invalid_params(message: String) -> JSONRPCErrorError

作用:生成“参数不对”的 JSON-RPC 错误。比如类型错、缺字段、路径格式不符合要求,都可以用它表达。

数据流:进去的是错误说明 → 函数填入标准错误码 -32602 → 返回统一格式的错误对象。

调用关系:参数解析、启动进程、预留 HTTP body 流等流程发现输入不合要求时会调用它。decode_request_params 也会间接产生这种错误。

调用图:被 3 处调用(run, start_process_rejects_non_native_cwd_before_launch, reserve_http_body_stream)。

not_found436–442 ↗
fn not_found(message: String) -> JSONRPCErrorError

作用:生成“找不到目标”的错误。它通常表示某个文件、资源或句柄不存在。

数据流:进去的是找不到什么的说明 → 函数填入项目使用的错误码 -32004 → 返回 JSON-RPC 错误对象。

调用关系:文件系统错误映射函数会调用它,把底层“没有这个东西”的错误翻译成远端能理解的 RPC 错误。

调用图:被 2 处调用(map_fs_error, map_fs_error)。

internal_error444–450 ↗
fn internal_error(message: String) -> JSONRPCErrorError

作用:生成“服务器内部出错”的 JSON-RPC 错误。它用于那些不是客户端输入直接造成、而是执行过程中失败的情况。

数据流:进去的是内部错误说明 → 函数填入标准错误码 -32603 → 返回统一错误对象。

调用关系:许多底层失败都会转成它,比如 IO 错误、JSON 转换失败、命令运行异常。发送回复或通知时连接已关闭,也会用它表达。

调用图:被 9 处调用(run, map_fs_error, unexpected_response, io_error, json_error, run_command, start_process, map_fs_error, http_request)。

decode_request_params452–457 ↗
fn decode_request_params(params: Option<Value>) -> Result<P, JSONRPCErrorError>

作用:把请求里的 JSON 参数解析成处理函数真正需要的数据类型。解析失败时,它会把问题包装成“参数不对”的 RPC 错误。

数据流:进去的是可有可无的 JSON 参数 → 函数调用 decode_params 做实际转换 → 成功返回目标类型,失败返回 JSON-RPC 的 invalid_params 错误。

调用关系:RpcRouter::request 和 RpcRouter::request_with_id 登记的包装逻辑会用它,确保处理函数拿到的是干净、类型正确的数据。

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

decode_notification_params459–464 ↗
fn decode_notification_params(params: Option<Value>) -> Result<P, String>

作用:把通知里的 JSON 参数解析成处理函数需要的数据。通知没有标准错误回复,所以失败时只返回一段文字。

数据流:进去的是通知参数 → 函数调用 decode_params 转换 → 成功返回目标类型,失败返回错误字符串。

调用关系:RpcRouter::notification 保存的包装逻辑会用它。解析失败后,上层可以记录错误或决定断开连接。

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

decode_params466–481 ↗
fn decode_params(params: Option<Value>) -> Result<P, serde_json::Error>

作用:执行真正的 JSON 参数解析。它还有一个宽容处理:空对象 {} 在某些情况下会当成没有参数来试一次。

数据流:进去的是可选 JSON 值;没有参数就当作 JSON null → 函数尝试把它转成目标类型 → 如果失败且原参数是空对象,就再尝试用 null 转换;最终返回解析出的数据或 serde_json 错误。

调用关系:decode_request_params 和 decode_notification_params 都依赖它。它把通用解析规则集中在一处,避免请求和通知各写一套。

调用图:被 2 处调用(decode_notification_params, decode_request_params);外部调用 2 个(matches!, from_value)。

handle_server_message483–513 ↗
async fn handle_server_message(
    pending: &Mutex<HashMap<RequestId, PendingRequest>>,
    event_tx: &mpsc::Sender<RpcClientEvent>,
    message: JSONRPCMessage,
) -> Result<(), String>

作用:处理客户端从服务器收到的一条 JSON-RPC 消息。它负责把回复送给正在等待的 call,把通知转成客户端事件。

数据流:进去的是等待回复表、事件发送通道和一条消息 → 如果是成功回复,就按请求编号找到等待者并送结果;如果是错误回复,就送服务器错误;如果是通知,就发到事件队列;如果服务器意外发来请求,就返回错误 → 处理过程中会移除已完成的等待项。

调用关系:RpcClient::new 启动的后台读任务反复调用它。它是远端消息进入 RpcClient 后的分拣员,直接决定 RpcClient::call 何时被唤醒。

调用图:被 1 处调用(new);外部调用 4 个(send, Server, Notification, format!)。

drain_pending515–526 ↗
async fn drain_pending(pending: &Mutex<HashMap<RequestId, PendingRequest>>)

作用:连接断开时,把所有还在等回复的请求一次性结束掉。这样调用方不会一直挂在那里。

数据流:进去的是等待回复表 → 函数把表里的等待者全部取出并清空表 → 给每个等待者发送 Closed 错误。

调用关系:RpcClient::new 的后台读任务在发现连接结束后调用它。它配合 RpcClient::call,保证断线后所有正在等待的调用都会收到失败结果。

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

tests::read_jsonrpc_line543–564 ↗
async fn read_jsonrpc_line(lines: &mut tokio::io::Lines<BufReader<R>>) -> JSONRPCMessage

作用:测试用的小工具:从模拟连接里读一行 JSON-RPC 消息。它带超时,避免测试因为等不到数据而永久卡住。

数据流:进去的是按行读取器 → 函数最多等 1 秒读取下一行,再把这一行 JSON 字符串解析成 JSONRPCMessage → 成功返回消息,失败或超时就让测试直接报错。

调用关系:rpc_client_matches_out_of_order_responses_by_request_id 用它读取客户端发出的请求,确认测试服务器收到了什么。

调用图:外部调用 4 个(from_secs, next_line, panic!, timeout)。

tests::write_jsonrpc_line566–577 ↗
async fn write_jsonrpc_line(writer: &mut W, message: JSONRPCMessage)

作用:测试用的小工具:把一条 JSON-RPC 消息写成一行,发进模拟连接。它模拟服务器给客户端回消息。

数据流:进去的是写入器和 JSON-RPC 消息 → 函数把消息转成 JSON 字符串,加上换行符 → 写入连接;编码或写入失败就让测试报错。

调用关系:rpc_client_matches_out_of_order_responses_by_request_id 用它按指定顺序回两个响应,测试客户端是否能按请求编号匹配。

调用图:外部调用 4 个(write_all, format!, panic!, to_string)。

tests::rpc_client_matches_out_of_order_responses_by_request_id580–643 ↗
async fn rpc_client_matches_out_of_order_responses_by_request_id()

作用:验证客户端不会被乱序回复搞混。也就是说,先发出的请求即使后收到回复,也应该拿到自己的结果。

数据流:测试先建立一对内存里的假连接 → 客户端同时发 slow 和 fast 两个请求 → 假服务器先回 fast、再回 slow → 测试检查两个调用各自拿到正确结果,并确认等待表清空。

调用关系:这个测试覆盖 RpcClient::new、RpcClient::call、handle_server_message 以及测试读写辅助函数。它证明请求编号匹配机制是可靠的。

调用图:调用 2 个内部函数(from_stdio, new);外部调用 10 个(new, Response, assert_eq!, read_jsonrpc_line, write_jsonrpc_line, panic!, json!, duplex, join!, spawn)。

exec-server/src/server/file_system_handler.rs源码 ↗
io_transportrequest handling, teardown

这个文件解决的是一个很实际的问题:服务器不能让外部请求直接碰硬盘,必须有一层统一入口来检查参数、套用沙箱限制、转换错误格式。可以把 FileSystemHandler 想成文件柜前的柜台人员:别人递来“打开文件”“读一段”“复制文件”的单子,它先看单子合不合规,再交给 LocalFileSystem 去真正动磁盘。大文件读取不是一次性全塞回去,而是先 open 得到一个读取把手,再 read_block 分块读,最后 close 关掉,避免占太多内存。普通整文件读写会用 base64(一种把二进制数据变成普通文本的编码)传输,方便放进 JSON。它还把底层 io::Error 转成 JSON-RPC 错误,比如文件不存在、请求不合法、服务器内部错误,让调用方收到的错误更稳定、更好懂。

函数细节16
FileSystemHandler::new51–56 ↗
fn new(runtime_paths: ExecServerRuntimePaths) -> Self

作用:创建一个新的文件系统处理器。服务器启动或搭建处理链时会用它,把“真实文件系统”和“分块读取记录本”准备好。

数据流:进去的是运行时路径配置,比如当前可执行文件位置、沙箱辅助程序位置 → 它用这些路径创建 LocalFileSystem,并准备一个空的 FileReadHandleManager → 出来的是一个可以接收文件请求的 FileSystemHandler。

调用关系:它是这套文件操作入口的装配步骤。上层创建服务器组件时会调用它,测试里也会调用它;后面的 open、read_file、write_file 等请求都依赖它提前放好的 file_system 和 file_reads。

调用图:调用 1 个内部函数(with_runtime_paths);被 2 处调用(no_platform_sandbox_policies_do_not_require_configured_sandbox_helper, new);外部调用 1 个(default)。

FileSystemHandler::shutdown58–60 ↗
async fn shutdown(&self)

作用:服务器要收工时,把还没关闭的文件读取把手全部关掉。这样可以避免文件一直被占着,像借了钥匙最后要归还。

数据流:进去的是当前处理器自身保存的所有读取把手 → 它让 FileReadHandleManager 逐个关闭 → 出来没有业务数据,但内部打开的文件资源会被清理掉。

调用关系:它在服务关闭流程里被调用,不处理单个请求,而是做收尾。具体关闭动作交给 file_reads.close_all 完成。

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

FileSystemHandler::open62–78 ↗
async fn open(
        &self,
        params: FsOpenParams,
    ) -> Result<FsOpenResponse, JSONRPCErrorError>

作用:为后续分块读取打开一个文件,并登记一个读取把手。适合读大文件,因为调用方可以一块一块取,而不是一次拿完整文件。

数据流:进去的是文件路径、沙箱信息和调用方想用的 handle_id → 先检查 handle_id 不要太长,再让 LocalFileSystem 按沙箱规则打开文件,接着把打开的文件登记到 FileReadHandleManager → 出来的是确认后的 handle_id;如果路径或权限有问题,会变成 JSON-RPC 错误。

调用关系:它由 fs_open 这个协议入口调用。它先用 validate_file_read_handle_id 把门,再把真正开文件的活交给 open_file_for_read,最后把文件交给 file_reads.open 保存,供 read_block 和 close 后续使用。

调用图:调用 3 个内部函数(open, open_file_for_read, validate_file_read_handle_id);被 1 处调用(fs_open)。

FileSystemHandler::read_block80–94 ↗
async fn read_block(
        &self,
        params: FsReadBlockParams,
    ) -> Result<FsReadBlockResponse, JSONRPCErrorError>

作用:从已经打开的文件里读取指定位置和长度的一小段。它让大文件传输变得可控,不需要一次把整份文件塞进内存。

数据流:进去的是 handle_id、起始位置 offset 和要读的长度 len → 它先检查 handle_id,再到 FileReadHandleManager 找到对应文件并读取那一段 → 出来的是字节块 chunk 和 eof 标记,eof 表示是不是已经读到文件末尾。

调用关系:它由 fs_read_block 调用,通常发生在 open 之后、close 之前。它不直接碰路径,而是根据 open 时登记的 handle_id 找文件。

调用图:调用 2 个内部函数(read_block, validate_file_read_handle_id);被 1 处调用(fs_read_block)。

FileSystemHandler::close96–103 ↗
async fn close(
        &self,
        params: FsCloseParams,
    ) -> Result<FsCloseResponse, JSONRPCErrorError>

作用:关闭一个分块读取用的把手。调用方读完文件后应当调用它,释放服务器这边保存的文件资源。

数据流:进去的是 handle_id → 它先确认这个编号长度合规,再让 FileReadHandleManager 删除并关闭对应记录 → 出来是一个空响应,表示关闭完成。

调用关系:它由 fs_close 调用,是 open/read_block 这条分块读取流程的最后一步。它把具体清理工作交给 file_reads.close。

调用图:调用 2 个内部函数(close, validate_file_read_handle_id);被 1 处调用(fs_close)。

FileSystemHandler::read_file105–117 ↗
async fn read_file(
        &self,
        params: FsReadFileParams,
    ) -> Result<FsReadFileResponse, JSONRPCErrorError>

作用:一次性读取整个文件,并把内容作为 base64 文本返回。适合小文件或调用方明确想直接拿完整内容的场景。

数据流:进去的是文件路径和可选沙箱信息 → 它让 LocalFileSystem 按限制读取文件字节 → 再把字节编码成 base64 字符串 → 出来的是带 data_base64 的响应。

调用关系:它由 fs_read_file 调用。它把磁盘读取交给 file_system.read_file,自己主要负责把二进制文件内容变成协议能安全传输的文本。

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

FileSystemHandler::write_file119–133 ↗
async fn write_file(
        &self,
        params: FsWriteFileParams,
    ) -> Result<FsWriteFileResponse, JSONRPCErrorError>

作用:把调用方发来的 base64 文件内容写到指定路径。这样 JSON 请求也能安全传二进制文件,比如图片或压缩包。

数据流:进去的是路径、base64 文本和沙箱信息 → 它先尝试把 base64 解码成原始字节;如果解码失败,就返回“请求不合法” → 解码成功后交给 LocalFileSystem 写入磁盘 → 出来是空响应,表示写入完成。

调用关系:它由 fs_write_file 调用。它先处理协议层的编码问题,再把真正写文件的动作交给 file_system.write_file。

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

FileSystemHandler::create_directory135–149 ↗
async fn create_directory(
        &self,
        params: FsCreateDirectoryParams,
    ) -> Result<FsCreateDirectoryResponse, JSONRPCErrorError>

作用:创建目录。默认会递归创建,也就是父目录不存在时一起建好,像 mkdir -p 那样省事。

数据流:进去的是目录路径、是否递归创建的选项和沙箱信息 → 如果调用方没说 recursive,就默认 true → 然后把路径和选项交给 LocalFileSystem → 出来是空响应;失败时返回统一的 JSON-RPC 错误。

调用关系:它由 fs_create_directory 调用。它负责把协议里的可选参数补成明确选项,再让 file_system.create_directory 执行。

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

FileSystemHandler::get_metadata151–168 ↗
async fn get_metadata(
        &self,
        params: FsGetMetadataParams,
    ) -> Result<FsGetMetadataResponse, JSONRPCErrorError>

作用:查询一个路径的基本信息,比如它是不是文件、是不是目录、大小是多少、创建和修改时间是什么。

数据流:进去的是路径和沙箱信息 → 它向 LocalFileSystem 查询元数据 → 再挑出协议需要的字段,组装成响应 → 出来的是 FsGetMetadataResponse。

调用关系:它由 fs_get_metadata 调用。底层文件系统返回的是内部格式,它在这里翻译成对外协议格式。

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

FileSystemHandler::canonicalize170–180 ↗
async fn canonicalize(
        &self,
        params: FsCanonicalizeParams,
    ) -> Result<FsCanonicalizeResponse, JSONRPCErrorError>

作用:把一个路径整理成系统认可的标准路径。比如把相对路径、符号链接等解析成更明确的路径。

数据流:进去的是原始路径和沙箱信息 → 它让 LocalFileSystem 做路径规范化,并遵守沙箱限制 → 出来的是规范化后的路径。

调用关系:它由 fs_canonicalize 调用。测试里会验证在不需要平台沙箱辅助程序的策略下,这个操作也能正常工作。

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

FileSystemHandler::read_directory182–199 ↗
async fn read_directory(
        &self,
        params: FsReadDirectoryParams,
    ) -> Result<FsReadDirectoryResponse, JSONRPCErrorError>

作用:读取一个目录下面有哪些条目,并告诉调用方每个条目是文件还是目录。

数据流:进去的是目录路径和沙箱信息 → 它让 LocalFileSystem 列出目录内容 → 再把每个内部目录项转换成协议里的 FsReadDirectoryEntry → 出来的是 entries 列表。

调用关系:它由 fs_read_directory 调用。它不直接解释复杂文件属性,只保留调用方最常用的名字、是否目录、是否文件。

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

FileSystemHandler::remove201–216 ↗
async fn remove(
        &self,
        params: FsRemoveParams,
    ) -> Result<FsRemoveResponse, JSONRPCErrorError>

作用:删除文件或目录。默认 recursive 和 force 都是 true,也就是默认允许递归删除目录,并且尽量忽略一些不存在之类的情况。

数据流:进去的是路径、recursive、force 和沙箱信息 → 没传的选项会补成默认 true → 然后交给 LocalFileSystem 按这些选项删除 → 出来是空响应。

调用关系:它由 fs_remove 调用。它负责把协议里的删除意图变成 RemoveOptions,再由 file_system.remove 真正执行。

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

FileSystemHandler::copy218–234 ↗
async fn copy(
        &self,
        params: FsCopyParams,
    ) -> Result<FsCopyResponse, JSONRPCErrorError>

作用:把一个文件或目录复制到另一个位置。调用方可以指定是否递归复制目录。

数据流:进去的是源路径、目标路径、recursive 选项和沙箱信息 → 它把这些整理成 CopyOptions → 交给 LocalFileSystem 做复制 → 出来是空响应。

调用关系:它由 fs_copy 调用。它是协议请求和底层复制动作之间的翻译层。

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

validate_file_read_handle_id237–244 ↗
fn validate_file_read_handle_id(handle_id: &str) -> Result<(), JSONRPCErrorError>

作用:检查分块读取用的 handle_id 不能太长。这样可以避免调用方传一个离谱的大字符串,占用资源或制造麻烦。

数据流:进去的是 handle_id 字符串 → 它看这个字符串的字节长度是否超过 32 → 合规则返回成功;太长就返回“请求不合法”的 JSON-RPC 错误。

调用关系:open、read_block、close 在使用 handle_id 前都会先调用它。它像门口量尺,确保后面的读取把手管理器不用处理异常大的编号。

调用图:调用 1 个内部函数(invalid_request);被 3 处调用(close, open, read_block);外部调用 1 个(format!)。

map_fs_error246–254 ↗
fn map_fs_error(err: io::Error) -> JSONRPCErrorError

作用:把底层文件系统错误翻译成对外的 JSON-RPC 错误。这样调用方不用理解操作系统的各种错误细节,只看统一的错误类型。

数据流:进去的是 io::Error,也就是 Rust 标准库里的输入输出错误 → 它查看错误种类:找不到文件变成 not_found,参数不对或没权限变成 invalid_request,其他情况变成 internal_error → 出来的是 JSONRPCErrorError。

调用关系:这个函数被多个文件操作通过 map_err 使用。LocalFileSystem 或读取管理器出错后,错误会先经过它“翻译”,再返回给 fs_open、fs_read_file、fs_copy 等协议入口。

调用图:调用 3 个内部函数(internal_error, invalid_request, not_found);外部调用 2 个(kind, to_string)。

tests::no_platform_sandbox_policies_do_not_require_configured_sandbox_helper269–331 ↗
async fn no_platform_sandbox_policies_do_not_require_configured_sandbox_helper()

作用:这是一个自动测试,确认某些沙箱策略不需要额外配置平台沙箱辅助程序,也能正常写文件、解析路径、读文件。

数据流:进去的是测试临时创建的目录和两种沙箱策略 → 它创建 FileSystemHandler,写入内容为 ok 的文件,再规范化路径,最后读回文件内容并比较 → 如果结果都符合预期,测试通过;否则测试失败。

调用关系:它只在测试运行时活跃。它会调用 FileSystemHandler::new,并间接覆盖 write_file、canonicalize、read_file 这些行为,用来防止以后改代码时破坏这类沙箱场景。

调用图:调用 3 个内部函数(new, new, from_path);外部调用 3 个(assert_eq!, current_exe, tempdir)。

exec-server/src/server/registry.rs源码 ↗
orchestrationconnection setup

这个文件只做一件很关键的事:搭好 RPC 路由表。RPC 可以理解成“隔着一层通信,让别人调用这里的功能”。外部请求会带一个方法名,比如初始化、执行程序、读写进程输入输出、查看环境信息、读写文件、创建目录、复制删除文件等。这里的 build_router 会先新建一个 RpcRouter,然后逐条登记:看到某个方法名时,就把参数按对应类型读出来,再调用 ExecServerHandler 上的对应方法。它本身不真正执行命令,也不真正读写文件;它更像总机接线员,负责把电话转到正确部门。一个特别点是 HTTP 请求使用 request_with_id,因为处理时还需要知道这次请求自己的编号,方便后续对应回应。

函数细节1
build_router44–163 ↗
fn build_router() -> RpcRouter<ExecServerHandler>

作用:创建并填好服务器的 RPC 路由表,让服务器知道每种外部消息应该调用哪个处理函数。有人建立连接、准备开始收消息时,会用它来装配这张“方法名到处理动作”的对照表。

数据流:进去时不需要外部参数;它先调用 RpcRouter::new 新建一张空路由表,然后把初始化、执行命令、读写进程、发送信号、终止进程、HTTP 请求、文件读写和目录操作等方法逐个登记进去。出来的是一个已经配置好的 RpcRouter<ExecServerHandler>;之后收到消息时,这个路由器会拿到共享的 ExecServerHandler 和解析好的参数,再把请求转给对应的 handler 方法。

调用关系:run_connection 会在一条连接开始工作时调用 build_router,拿到这张路由表来分发后续消息。build_router 自己只调用 new 来创建路由器,后面登记的每一项都是把实际工作交给 ExecServerHandler,比如 initialize、exec、fs_read_file、fs_copy 等;这样连接层只管收发消息,具体功能由 handler 完成。

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

exec-server/src/server/process_handler.rs源码 ↗
orchestrationrequest handling

这个文件定义了一个叫 ProcessHandler 的小包装器。可以把它想成服务台窗口:外面的人只需要对窗口说“启动命令”“读输出”“写输入”“停掉它”,窗口再把活交给真正干活的 LocalProcess。LocalProcess 才是实际管理本地进程的部件,而 ProcessHandler 负责给服务器层提供一组干净的方法。这里的方法大多很薄,基本都是把请求参数原样传下去,再把结果或 JSON-RPC 错误返回回来。JSON-RPC 是一种用 JSON 格式远程调用函数的协议;这里的错误类型表示请求失败时要回给客户端的标准错误。这个文件还处理通知发送器,也就是进程有输出或状态变化时,用来主动告诉客户端的通道。整体上,它的重要性不在复杂算法,而在隔离:服务器只认识 ProcessHandler,不需要知道 LocalProcess 内部怎么启动和控制进程。

函数细节8
ProcessHandler::new22–26 ↗
fn new(notifications: RpcNotificationSender) -> Self

作用:创建一个新的进程处理器,并把通知通道交给它。服务器刚接上客户端、准备处理执行命令时会用它。

数据流:进去的是一个 RpcNotificationSender,也就是“以后有消息要推给客户端时走的通道” → 它用这个通道创建内部的 LocalProcess → 出来的是一个 ProcessHandler,里面已经带好真正负责本地进程的对象。

调用关系:调用图显示它会调用 LocalProcess 的 new 来完成真正初始化,并且会被 attach 使用;也就是说,当连接或会话挂接进来时,外层先建这个处理器,再通过它处理后续进程请求。

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

ProcessHandler::shutdown28–30 ↗
async fn shutdown(&self)

作用:让内部正在管理的进程系统收尾,避免连接结束后还留下孤儿进程或后台任务。

数据流:进去的是当前 ProcessHandler 自己 → 它把“请关闭”的指令交给内部 LocalProcess → 出来没有业务数据,但内部资源会被清理,相关进程或后台状态会进入关闭流程。

调用关系:它直接调用 LocalProcess 的 shutdown。它通常位于会话结束或服务器清理阶段,自己不做复杂判断,只负责把关闭动作传到真正管理进程的地方。

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

ProcessHandler::set_notification_sender32–34 ↗
fn set_notification_sender(&self, notifications: Option<RpcNotificationSender>)

作用:更换或移除通知发送通道。这样进程输出、状态变化等消息可以发给新的客户端,或者在不需要时停止发送。

数据流:进去的是一个可选的 RpcNotificationSender;有值表示换成这个通知通道,没有值表示清掉通知通道 → 它把这个设置交给内部 LocalProcess → 出来没有返回数据,但后续通知会按新的设置发送或不再发送。

调用关系:它直接调用 LocalProcess 的 set_notification_sender。它夹在服务器连接层和本地进程层之间,用来在连接变化时同步通知出口。

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

ProcessHandler::exec36–38 ↗
async fn exec(&self, params: ExecParams) -> Result<ExecResponse, JSONRPCErrorError>

作用:启动一个新的本地命令或进程,并把启动结果返回给请求方。

数据流:进去的是 ExecParams,里面包含要执行什么命令、参数等执行请求信息 → 它把这些信息交给内部 LocalProcess 的 exec → 出来是 ExecResponse,表示启动结果;如果失败,就出来一个 JSON-RPC 标准错误。

调用关系:它直接调用 LocalProcess 的 exec。服务器收到“执行命令”的请求时,会走到这个方法;这个方法不亲自启动进程,而是把任务交给 LocalProcess。

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

ProcessHandler::exec_read40–45 ↗
async fn exec_read(
        &self,
        params: ReadParams,
    ) -> Result<ReadResponse, JSONRPCErrorError>

作用:读取某个已启动进程的输出,比如命令打印到屏幕上的内容。

数据流:进去的是 ReadParams,通常会说明要读哪个进程、读多少或从哪里读 → 它把读取请求交给内部 LocalProcess 的 exec_read → 出来是 ReadResponse,包含读到的内容或状态;失败时返回 JSON-RPC 错误。

调用关系:它直接调用 LocalProcess 的 exec_read。它是外部读进程输出请求和底层进程缓冲区之间的中转站,让服务器层不用关心输出具体怎么保存和读取。

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

ProcessHandler::exec_write47–52 ↗
async fn exec_write(
        &self,
        params: WriteParams,
    ) -> Result<WriteResponse, JSONRPCErrorError>

作用:向某个正在运行的进程写入内容,比如给交互式命令输入一行文字。

数据流:进去的是 WriteParams,里面说明要写给哪个进程以及写什么内容 → 它把写入请求交给内部 LocalProcess 的 exec_write → 出来是 WriteResponse,表示写入是否成功;失败时返回 JSON-RPC 错误。

调用关系:它直接调用 LocalProcess 的 exec_write。当客户端想给进程标准输入发送数据时,这个方法负责把请求从协议层送到真正的本地进程层。

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

ProcessHandler::signal54–59 ↗
async fn signal(
        &self,
        params: SignalParams,
    ) -> Result<SignalResponse, JSONRPCErrorError>

作用:给正在运行的进程发送一个信号。信号可以理解成操作系统给进程的一种指令,比如暂停、继续或中断。

数据流:进去的是 SignalParams,里面说明目标进程和要发送的信号 → 它把这件事交给内部 LocalProcess 的 signal_process → 出来是 SignalResponse,表示信号发送结果;如果目标不存在或发送失败,就返回 JSON-RPC 错误。

调用关系:它直接调用 LocalProcess 的 signal_process。它位于客户端控制请求和操作系统进程控制之间,负责把外部请求转成底层进程对象能执行的动作。

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

ProcessHandler::terminate61–66 ↗
async fn terminate(
        &self,
        params: TerminateParams,
    ) -> Result<TerminateResponse, JSONRPCErrorError>

作用:请求结束某个进程。相比普通读写,这是明确告诉进程“该停了”。

数据流:进去的是 TerminateParams,里面说明要终止哪个进程以及可能的终止方式 → 它调用内部 LocalProcess 的 terminate_process → 出来是 TerminateResponse,表示终止请求是否成功;失败时返回 JSON-RPC 错误。

调用关系:它直接调用 LocalProcess 的 terminate_process。当客户端或服务器需要清掉某个执行中的命令时,会通过这个方法把结束请求交给真正管理进程生命周期的 LocalProcess。

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

MCP 请求与客户端路由

这些文件实现 MCP 侧分发和工具执行,以及 rmcp 客户端适配器;这些适配器接收通知,并将引导式请求流程序列化回 UI。

mcp-server/src/message_processor.rs源码 ↗
orchestrationrequest handling

这个文件定义了 MessageProcessor。可以把它想成一个服务台:客户端每递一张单子,它先看单子是“初始化”“查工具”“调用工具”还是“取消”,然后决定是立刻回话、记录日志,还是启动一个后台 Codex 会话。它还保存一些关键状态,比如是否已经初始化、正在运行的请求 ID 对应哪个 Codex 线程。真正耗时的 Codex 对话不会堵住服务台,而是丢给异步任务(一种后台小工)去跑,这样服务器还能继续接收新消息。文件里也包含不少“目前只记日志”的处理器,比如资源、提示词、进度通知等,表示协议里有这些入口,但这里暂时不提供实际内容。最重要的两条工具路由是 codex 和 codex-reply:前者开启新会话,后者给已有会话继续发送用户输入。

函数细节25
MessageProcessor::new52–89 ↗
async fn new(
        outgoing: OutgoingMessageSender,
        arg0_paths: Arg0DispatchPaths,
        config: Arc<Config>,
        environment_manager: Arc<EnvironmentManager>,
        state_db: Optio

作用:创建一个新的消息处理器,并把它需要用到的外部零件准备好,比如发消息的通道、登录信息、Codex 线程管理器。服务器启动后需要先建好它,后面才能处理客户端请求。

数据流:输入包括发往客户端的发送器、配置、运行环境管理器、状态数据库和安装 ID → 它根据配置创建认证管理器、用户说明读取器和 ThreadManager(Codex 会话的管家)→ 输出一个 MessageProcessor,里面带着初始状态、工具路径、线程管理器和一张空的“请求 ID 到线程 ID”对照表。

调用关系:这是整个处理器的装配步骤。后面的 process_request、工具调用和取消通知都会依赖这里创建的 outgoing、thread_manager 和 running_requests_id_to_codex_uuid。

调用图:调用 3 个内部函数(new, new, shared_from_config);外部调用 5 个(new, new, new, thread_store_from_config, empty_extension_registry)。

MessageProcessor::process_request91–165 ↗
async fn process_request(&mut self, request: JsonRpcRequest<ClientRequest>)

作用:处理客户端发来的普通请求。它像总机一样,根据请求类型把电话转给正确的处理函数。

数据流:输入是一条带 ID 的 JSON-RPC 请求 → 它取出请求 ID 和具体请求内容,匹配请求种类 → 对初始化、ping、列工具、调用工具等请求调用对应函数;对不支持的任务类请求或未知自定义请求返回“找不到方法”的错误。

调用关系:这是请求进入本文件的主入口。它会把工作分给 handle_initialize、handle_list_tools、handle_call_tool、handle_unsupported_request 等具体处理器。

调用图:调用 14 个内部函数(handle_call_tool, handle_complete, handle_get_prompt, handle_initialize, handle_list_prompts, handle_list_resource_templates, handle_list_resources, handle_list_tools, handle_ping, handle_read_resource (+4 more));外部调用 3 个(new, format!, json!)。

MessageProcessor::process_response167–171 ↗
async fn process_response(&mut self, response: JsonRpcResponse<serde_json::Value>)

作用:处理客户端回传给服务器的响应。这里主要是把这个响应转交给负责等待结果的发送器机制。

数据流:输入是一条 JSON-RPC 响应 → 它记录日志,取出响应 ID 和结果内容 → 通过 outgoing.notify_client_response 通知内部等待方这个客户端响应已经到了。

调用关系:当服务器曾向客户端发出请求、客户端再回响应时会走这里。它不做业务判断,只把结果交还给 outgoing 里维护的响应等待流程。

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

MessageProcessor::process_notification173–194 ↗
async fn process_notification(
        &mut self,
        notification: JsonRpcNotification<ClientNotification>,
    )

作用:处理客户端发来的通知。通知和请求不同,通常不要求服务器回一个正式结果。

数据流:输入是一条 JSON-RPC 通知 → 它查看通知类型 → 取消通知会尝试中断 Codex 会话,进度、根目录变化、初始化完成等通知主要记录日志,自定义通知会被忽略并写警告。

调用关系:这是通知进入本文件的主入口。它会把取消通知交给 handle_cancelled_notification,把其他通知交给对应的小处理函数。

调用图:调用 4 个内部函数(handle_cancelled_notification, handle_initialized_notification, handle_progress_notification, handle_roots_list_changed);外部调用 1 个(warn!)。

MessageProcessor::process_error196–198 ↗
fn process_error(&mut self, err: JsonRpcError)

作用:处理客户端发来的 JSON-RPC 错误消息。它目前只把错误记到日志里,方便排查问题。

数据流:输入是一条错误对象 → 它不修改状态,也不回消息 → 只把错误内容写入错误日志。

调用关系:这是错误消息的入口。它独立于请求和通知流程,不再继续分发给其他函数。

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

MessageProcessor::handle_initialize200–277 ↗
async fn handle_initialize(
        &mut self,
        id: RequestId,
        params: rmcp::model::InitializeRequestParams,
    )

作用:处理客户端第一次连接时的初始化握手。它告诉客户端:这个服务器是谁、支持哪些能力,以及该用哪个协议版本。

数据流:输入是请求 ID 和初始化参数,里面有客户端名称、版本、协议版本 → 它检查是否已经初始化过,设置用户代理信息,组装服务器信息和能力列表 → 成功时标记 initialized 为 true 并发送初始化响应;重复初始化或序列化失败时发送错误。

调用关系:process_request 收到 InitializeRequest 时调用它。初始化成功后,客户端才知道这里支持工具调用,尤其是后面 handle_list_tools 和 handle_call_tool 暴露的 Codex 工具。

调用图:被 1 处调用(process_request);外部调用 10 个(internal_error, invalid_request, new, new, builder, env!, format!, json!, to_value, info!)。

MessageProcessor::handle_ping279–282 ↗
async fn handle_ping(&self, id: RequestId)

作用:回应客户端的 ping,也就是“你还活着吗”的探测。它用一个空结果表示服务器在线。

数据流:输入是请求 ID → 它记录一条 ping 日志 → 通过 outgoing 给同一个请求 ID 返回一个空 JSON 对象。

调用关系:process_request 收到 PingRequest 时调用它。它不依赖其他业务模块,只用于保持连接健康检查。

调用图:被 1 处调用(process_request);外部调用 2 个(json!, info!)。

MessageProcessor::handle_list_resources284–286 ↗
fn handle_list_resources(&self, params: Option<rmcp::model::PaginatedRequestParams>)

作用:接收“列出资源”的请求,但这个文件里目前没有真正返回资源列表,只记录请求参数。

数据流:输入是可选的分页参数 → 它把参数写进日志 → 不发送响应、不改变内部状态。

调用关系:process_request 收到 ListResourcesRequest 时调用它。它像一个预留接口,说明协议入口存在,但这里还没有实现资源服务。

调用图:被 1 处调用(process_request);外部调用 1 个(info!)。

MessageProcessor::handle_list_resource_templates288–290 ↗
fn handle_list_resource_templates(&self, params: Option<rmcp::model::PaginatedRequestParams>)

作用:接收“列出资源模板”的请求,目前只做日志记录。资源模板可以理解为资源地址的填写格式。

数据流:输入是可选的分页参数 → 它记录请求内容 → 没有产出实际列表,也不修改状态。

调用关系:process_request 收到 ListResourceTemplatesRequest 时调用它。它没有再把工作交给别的模块。

调用图:被 1 处调用(process_request);外部调用 1 个(info!)。

MessageProcessor::handle_read_resource292–294 ↗
fn handle_read_resource(&self, params: rmcp::model::ReadResourceRequestParams)

作用:接收“读取某个资源”的请求,目前只把要读的资源参数记下来。

数据流:输入是读取资源所需的参数 → 它写日志 → 不返回资源内容,也不改变 MessageProcessor 的状态。

调用关系:process_request 收到 ReadResourceRequest 时调用它。当前实现中它没有连接到实际资源读取系统。

调用图:被 1 处调用(process_request);外部调用 1 个(info!)。

MessageProcessor::handle_subscribe296–298 ↗
fn handle_subscribe(&self, params: rmcp::model::SubscribeRequestParams)

作用:接收“订阅资源变化”的请求,目前只记录客户端想订阅什么。

数据流:输入是订阅参数 → 它记录日志 → 不建立真正的订阅,也不发送响应。

调用关系:process_request 收到 SubscribeRequest 时调用它。它是协议占位式处理,没有调用其他业务函数。

调用图:被 1 处调用(process_request);外部调用 1 个(info!)。

MessageProcessor::handle_unsubscribe300–302 ↗
fn handle_unsubscribe(&self, params: rmcp::model::UnsubscribeRequestParams)

作用:接收“取消订阅资源变化”的请求,目前只记录取消订阅的参数。

数据流:输入是取消订阅参数 → 它写入日志 → 不实际移除订阅,也不改内部表。

调用关系:process_request 收到 UnsubscribeRequest 时调用它。它和 handle_subscribe 对应,但当前都只是日志入口。

调用图:被 1 处调用(process_request);外部调用 1 个(info!)。

MessageProcessor::handle_list_prompts304–306 ↗
fn handle_list_prompts(&self, params: Option<rmcp::model::PaginatedRequestParams>)

作用:接收“列出提示词”的请求,目前只记录请求参数。提示词可以理解为预设好的提问模板。

数据流:输入是可选分页参数 → 它把参数记到日志 → 不返回提示词列表。

调用关系:process_request 收到 ListPromptsRequest 时调用它。当前没有继续调用提示词仓库或生成响应。

调用图:被 1 处调用(process_request);外部调用 1 个(info!)。

MessageProcessor::handle_get_prompt308–310 ↗
fn handle_get_prompt(&self, params: rmcp::model::GetPromptRequestParams)

作用:接收“获取某个提示词”的请求,目前只记录客户端想取哪个提示词。

数据流:输入是获取提示词的参数 → 它写日志 → 不返回提示词内容,也不改状态。

调用关系:process_request 收到 GetPromptRequest 时调用它。它是提示词协议入口的占位实现。

调用图:被 1 处调用(process_request);外部调用 1 个(info!)。

MessageProcessor::handle_list_tools312–328 ↗
async fn handle_list_tools(
        &self,
        id: RequestId,
        params: Option<rmcp::model::PaginatedRequestParams>,
    )

作用:告诉客户端这个服务器提供哪些工具。这里会暴露两个工具:开启 Codex 会话的 codex,以及给已有会话继续回复的 codex-reply。

数据流:输入是请求 ID 和可选分页参数 → 它创建两个工具说明对象 → 通过 outgoing 返回工具列表,且没有下一页。

调用关系:process_request 收到 ListToolsRequest 时调用它。它使用 codex_tool_config 里的工具描述创建函数,让客户端知道之后可以怎样调用 handle_call_tool。

调用图:被 1 处调用(process_request);外部调用 2 个(trace!, vec!)。

MessageProcessor::handle_call_tool330–349 ↗
async fn handle_call_tool(&self, id: RequestId, params: CallToolRequestParams)

作用:处理客户端真正调用工具的请求。它看工具名,把 codex 交给新会话处理,把 codex-reply 交给继续会话处理。

数据流:输入是请求 ID 和工具调用参数,里面有工具名和参数 → 它拆出 name 和 arguments → 如果是 codex,就调用 handle_tool_call_codex;如果是 codex-reply,就调用 handle_tool_call_codex_session_reply;其他名字会返回“未知工具”的错误结果。

调用关系:process_request 收到 CallToolRequest 时调用它。它是工具调用的分岔口,后面真正耗时的 Codex 工作由两个专门函数继续安排。

调用图:调用 2 个内部函数(handle_tool_call_codex, handle_tool_call_codex_session_reply);被 1 处调用(process_request);外部调用 3 个(error, info!, vec!)。

MessageProcessor::handle_tool_call_codex351–405 ↗
async fn handle_tool_call_codex(
        &self,
        id: RequestId,
        arguments: Option<rmcp::model::JsonObject>,
    )

作用:处理 codex 工具调用,也就是开启一个新的 Codex 对话任务。它会校验参数、加载配置,然后把长时间运行的会话放到后台跑。

数据流:输入是请求 ID 和可选 JSON 参数 → 它把参数解析成 CodexToolCallParam,并转换成初始提示词和 Config 配置;如果缺参数、解析失败或配置加载失败,就返回错误 → 成功时克隆需要的共享对象,并启动异步任务运行 run_codex_tool_session。

调用关系:handle_call_tool 遇到工具名 codex 时调用它。它不直接执行完整会话,而是把活交给 codex_tool_runner::run_codex_tool_session,这样主消息处理循环不会被一次长对话卡住。

调用图:调用 1 个内部函数(run_codex_tool_session);被 1 处调用(handle_call_tool);外部调用 4 个(clone, error, spawn, vec!)。

MessageProcessor::handle_tool_call_codex_session_reply407–488 ↗
async fn handle_tool_call_codex_session_reply(
        &self,
        request_id: RequestId,
        arguments: Option<rmcp::model::JsonObject>,
    )

作用:处理 codex-reply 工具调用,也就是给已经存在的 Codex 会话继续发一段话。它会找到对应线程,并把回复任务放到后台执行。

数据流:输入是请求 ID 和可选 JSON 参数 → 它解析出 thread_id 和 prompt;解析失败或缺参数会返回错误;如果找不到对应会话,会返回带 thread_id 的“会话不存在”结果 → 找到会话后启动异步任务 run_codex_tool_session_reply,把新提示词送进该会话。

调用关系:handle_call_tool 遇到工具名 codex-reply 时调用它。它依赖 thread_manager 找回之前的 Codex 线程,并把实际续聊工作交给 codex_tool_runner。

调用图:调用 2 个内部函数(create_call_tool_result_with_thread_id, run_codex_tool_session_reply);被 1 处调用(handle_call_tool);外部调用 7 个(format!, error, spawn, error!, info!, warn!, vec!)。

MessageProcessor::handle_set_level490–492 ↗
fn handle_set_level(&self, params: rmcp::model::SetLevelRequestParams)

作用:接收客户端设置日志级别的请求,目前只记录参数。日志级别就是决定记录多少运行信息的开关。

数据流:输入是日志级别设置参数 → 它写日志 → 不实际调整日志系统,也不发送结果。

调用关系:process_request 收到 SetLevelRequest 时调用它。当前实现没有把设置传给日志框架。

调用图:被 1 处调用(process_request);外部调用 1 个(info!)。

MessageProcessor::handle_complete494–496 ↗
fn handle_complete(&self, params: rmcp::model::CompleteRequestParams)

作用:接收补全请求,目前只记录参数。补全可以理解为客户端请求服务器帮忙补齐输入内容。

数据流:输入是补全参数 → 它把参数写入日志 → 不返回补全候选,也不改变状态。

调用关系:process_request 收到 CompleteRequest 时调用它。它是协议入口,但当前没有接入实际补全功能。

调用图:被 1 处调用(process_request);外部调用 1 个(info!)。

MessageProcessor::handle_unsupported_request498–509 ↗
async fn handle_unsupported_request(&self, id: RequestId, method: &str)

作用:统一回应那些协议里有、但这个服务器不支持的请求。它会明确告诉客户端“这个方法找不到”。

数据流:输入是请求 ID 和方法名 → 它创建 METHOD_NOT_FOUND 错误,错误里带上方法名 → 通过 outgoing 把错误发回客户端。

调用关系:process_request 遇到 tasks/get_info、tasks/list、tasks/get_result、tasks/cancel 等不支持的任务请求时调用它,避免每个分支重复写同样的错误回复。

调用图:被 1 处调用(process_request);外部调用 3 个(new, format!, json!)。

MessageProcessor::handle_cancelled_notification515–560 ↗
async fn handle_cancelled_notification(&self, params: rmcp::model::CancelledNotificationParam)

作用:处理客户端发来的取消通知,用来中断一个正在运行的 Codex 请求。它相当于把“停一下”的信号送进对应会话。

数据流:输入是取消通知参数,里面有要取消的 request_id → 它先在“请求 ID 到线程 ID”的表里查出对应 Codex 线程,再从 thread_manager 取出会话 → 成功后向会话提交 Interrupt(中断)操作,并从对照表里移除这个请求;找不到请求或会话时只记录警告。

调用关系:process_notification 收到 CancelledNotification 时调用它。它依赖 run_codex_tool_session 或回复任务曾经登记过 request_id 与 thread_id 的关系,最后通过 Codex 会话的 submit_with_id 把中断命令送进去。

调用图:被 1 处调用(process_notification);外部调用 3 个(error!, info!, warn!)。

MessageProcessor::handle_progress_notification562–564 ↗
fn handle_progress_notification(&self, params: rmcp::model::ProgressNotificationParam)

作用:接收客户端发来的进度通知,目前只记录内容。进度通知通常表示某个操作进行到哪里了。

数据流:输入是进度通知参数 → 它记录日志 → 不改变状态,也不触发后续动作。

调用关系:process_notification 收到 ProgressNotification 时调用它。当前没有把进度转发给 Codex 或其他模块。

调用图:被 1 处调用(process_notification);外部调用 1 个(info!)。

MessageProcessor::handle_roots_list_changed566–568 ↗
fn handle_roots_list_changed(&self)

作用:接收客户端说“根目录列表变了”的通知,目前只写日志。根目录可以理解为客户端允许服务器看的工作区入口。

数据流:没有额外输入参数 → 它记录一条 roots/list_changed 日志 → 不重新读取目录,也不更新配置。

调用关系:process_notification 收到 RootsListChangedNotification 时调用它。当前实现只是留痕,没有触发工作区刷新。

调用图:被 1 处调用(process_notification);外部调用 1 个(info!)。

MessageProcessor::handle_initialized_notification570–572 ↗
fn handle_initialized_notification(&self)

作用:接收客户端说“我这边初始化完成了”的通知,目前只记录日志。

数据流:没有使用额外数据 → 它写一条 initialized 通知日志 → 不修改 initialized 字段,也不发送响应。

调用关系:process_notification 收到 InitializedNotification 时调用它。真正设置服务器 initialized 状态的是 handle_initialize,而不是这个通知处理器。

调用图:被 1 处调用(process_notification);外部调用 1 个(info!)。

mcp-server/src/codex_tool_runner.rs源码 ↗
orchestrationrequest handling

这个文件像一个“现场调度员”。MCP(让外部客户端按统一格式调用工具的一套协议)客户端发来一句提示词后,它会启动或继续一个 Codex 线程,也就是一段可持续的代码助手会话。然后它把用户输入交给 Codex,守在旁边等事件:普通进度就转成通知发回客户端;如果 Codex 想执行命令或改文件,就交给专门的审批处理器;如果出错,就按 MCP 要求返回错误;如果本轮完成,就把最后一句回答包装成工具调用结果返回。它还维护“请求 ID 到线程 ID”的对应表,方便后续回复能接上同一段会话。一个细节很重要:返回结果里会同时放文本和 threadId,这样客户端即使只看结构化内容,也知道该继续哪条会话。

函数细节5
create_call_tool_result_with_thread_id36–51 ↗
fn create_call_tool_result_with_thread_id(
    thread_id: ThreadId,
    text: String,
    is_error: Option<bool>,
) -> CallToolResult

作用:把 Codex 的回答包装成 MCP 工具调用需要的返回格式,并额外带上 threadId,也就是这段会话的编号。这样客户端以后可以用这个编号继续同一场对话。

数据流:进去的是线程编号、要返回的文字,以及是否算错误的标记。它把文字放进普通 content,也把同样的文字和 threadId 放进 structured_content(结构化内容,方便机器读取)。出来的是一个 CallToolResult,可以直接作为 tools/call 的响应发回客户端。

调用关系:这是本文件里统一“打包结果”的小工具。新开会话、继续会话、会话内部遇到错误或完成时都会用它;测试也会检查它确实把 threadId 放进结构化内容里。外部的 handle_tool_call_codex_session_reply 也会借它生成一致格式的回复。

调用图:被 5 处调用(run_codex_tool_session, run_codex_tool_session_inner, run_codex_tool_session_reply, call_tool_result_includes_thread_id_in_structured_content, handle_tool_call_codex_session_reply);外部调用 3 个(json!, success, vec!)。

run_codex_tool_session57–141 ↗
async fn run_codex_tool_session(
    id: RequestId,
    initial_prompt: String,
    config: CodexConfig,
    outgoing: Arc<OutgoingMessageSender>,
    thread_manager: Arc<ThreadManager>,
    running_r

作用:启动一场新的 Codex 工具会话,把用户最开始的提示词提交进去,并安排后续事件处理。它是“新对话”的入口。

数据流:进去的是 MCP 请求编号、用户提示词、Codex 配置、发消息用的通道、线程管理器,以及一张正在运行请求的对应表。它先让线程管理器创建新线程;失败就立即回错误。成功后,它把会话已配置的事件通知客户端,把请求编号和线程编号记到表里,再把初始提示词提交给 Codex。提交失败会返回带 threadId 的错误并清理表;提交成功后,把后面的等待和转发交给 run_codex_tool_session_inner。

调用关系:当上层 handle_tool_call_codex 收到一次新的 Codex 工具调用时,会调用它。它自己不长期守事件,而是完成“开场准备”后,把主循环交给 run_codex_tool_session_inner;需要返回结果时会用 create_call_tool_result_with_thread_id。

调用图:调用 2 个内部函数(create_call_tool_result_with_thread_id, run_codex_tool_session_inner);被 1 处调用(handle_tool_call_codex);外部调用 9 个(clone, default, clone, to_string, format!, error, SessionConfigured, error!, vec!)。

run_codex_tool_session_reply143–192 ↗
async fn run_codex_tool_session_reply(
    thread_id: ThreadId,
    thread: Arc<CodexThread>,
    outgoing: Arc<OutgoingMessageSender>,
    request_id: RequestId,
    prompt: String,
    running_reque

作用:继续一场已经存在的 Codex 会话,把用户的新输入接到原来的线程里。它用于“接着刚才聊”的场景。

数据流:进去的是已有线程编号、已有 Codex 线程、发消息通道、这次 MCP 请求编号、新提示词,以及请求到线程的对应表。它先记录这次请求对应哪个线程,然后把新提示词提交给这个线程。提交失败就返回带 threadId 的错误并清理记录;提交成功后,继续进入 run_codex_tool_session_inner 等待事件和最终结果。

调用关系:上层 handle_tool_call_codex_session_reply 在客户端要求继续某个 Codex 会话时调用它。它和 run_codex_tool_session 很像,但跳过创建线程这一步;之后同样依赖 run_codex_tool_session_inner 处理后续事件,并用 create_call_tool_result_with_thread_id 打包错误响应。

调用图:调用 2 个内部函数(create_call_tool_result_with_thread_id, run_codex_tool_session_inner);被 1 处调用(handle_tool_call_codex_session_reply);外部调用 5 个(default, clone, format!, error!, vec!)。

run_codex_tool_session_inner194–411 ↗
async fn run_codex_tool_session_inner(
    thread_id: ThreadId,
    thread: Arc<CodexThread>,
    outgoing: Arc<OutgoingMessageSender>,
    request_id: RequestId,
    running_requests_id_to_codex_uuid

作用:这是会话运行时的主循环:一直听 Codex 发出的事件,并决定哪些要通知客户端、哪些要触发审批、什么时候该返回最终结果。

数据流:进去的是线程编号、Codex 线程、发消息通道、MCP 请求编号,以及正在运行请求的对应表。它反复从线程里取下一个事件;每个事件都会先转成通知发给客户端。遇到执行命令审批,就把命令、目录、调用编号等交给 handle_exec_approval_request;遇到补丁审批,就把改动内容交给 handle_patch_approval_request;遇到错误,就返回带 threadId 的错误;遇到本轮完成,就取最后的助手消息作为最终文本返回,并从对应表里移除这次请求。取事件本身失败时,也会返回运行时错误。

调用关系:它由 run_codex_tool_session 和 run_codex_tool_session_reply 调用,是新会话和续聊会话共用的“守夜人”。它不会自己处理所有复杂决定,而是在需要人工或客户端批准执行命令、改文件时,把工作分给审批模块;最终响应则统一交给 create_call_tool_result_with_thread_id 包装。

调用图:调用 3 个内部函数(create_call_tool_result_with_thread_id, handle_exec_approval_request, handle_patch_approval_request);被 2 处调用(run_codex_tool_session, run_codex_tool_session_reply);外部调用 4 个(clone, to_string, format!, error!)。

tests::call_tool_result_includes_thread_id_in_structured_content419–433 ↗
fn call_tool_result_includes_thread_id_in_structured_content()

作用:确认工具调用返回值里真的包含 threadId。这个测试防止以后改代码时不小心删掉这个关键信息,导致客户端无法继续会话。

数据流:进去的是测试里新建的一个线程编号和文字“done”。测试调用 create_call_tool_result_with_thread_id 生成结果,然后检查 structured_content 是否正好包含这个 threadId 和同样的文字。出来的是测试通过或失败;它不改动运行时状态。

调用关系:它只在测试时运行,专门盯住 create_call_tool_result_with_thread_id 的行为。因为运行会话和继续会话都依赖这个包装函数,所以这个小测试间接保护了整条 MCP 返回链路的兼容性。

调用图:调用 2 个内部函数(create_call_tool_result_with_thread_id, new);外部调用 1 个(assert_eq!)。

rmcp-client/src/elicitation_client_service.rs源码 ↗
orchestrationrequest handling

这个文件像一个前台接待员。服务器有时会发来 elicitation 请求,也就是“请客户端去问用户一个问题,或让用户填一段表单”。这里的 ElicitationClientService 会认出这种请求,先把被 RMCP 框架挪到上下文里的 _meta 信息放回请求本身,再进入暂停状态,调用外部传进来的 send_elicitation 函数,把问题交给真正的界面或业务代码处理。用户回答后,它把回答包装成协议能发送的结果。普通请求和通知它不自己处理,而是转交给 LoggingClientHandler,这样日志、默认行为还能照常工作。一个重要细节是:progressToken 是进度跟踪用的内部标记,不应该混进业务请求的 _meta,所以会被剔除。

函数细节12
ElicitationClientService::new34–48 ↗
fn new(
        client_info: ClientInfo,
        send_elicitation: SendElicitation,
        pause_state: ElicitationPauseState,
    ) -> Self

作用:创建一个能处理 elicitation 请求的客户端服务。调用方把客户端信息、真正发送询问的函数、以及暂停状态传进来,它把这些零件装成一个服务对象。

数据流:进去的是客户端身份信息、send_elicitation 回调函数和 pause_state → 它把回调函数放进 Arc(一种可安全共享同一份东西的引用计数指针),同时创建一个带日志能力的默认处理器 → 出来的是 ElicitationClientService,后续可以接收服务器请求。

调用关系:它由 initialize 在客户端启动准备阶段调用。它会使用 clone_send_elicitation 做一份可交给 LoggingClientHandler 的回调包装,并创建 LoggingClientHandler,让本服务只专门接管 elicitation,其余事情仍能交给日志处理器。

调用图:调用 2 个内部函数(clone_send_elicitation, new);被 1 处调用(initialize);外部调用 2 个(clone, new)。

ElicitationClientService::create_elicitation50–61 ↗
async fn create_elicitation(
        &self,
        request: Elicitation,
        context: RequestContext<RoleClient>,
    ) -> Result<ElicitationResponse, rmcp::ErrorData>

作用:真正处理服务器发来的“请问用户”请求。它会恢复请求里的附加信息,暂停当前流程,然后把请求交给外部的用户交互代码。

数据流:进去的是一个 Elicitation 请求和请求上下文,里面有请求 id 和被框架提出来的 meta → 它先用 restore_context_meta 把 meta 放回请求,再进入 pause_state 的暂停区域,然后调用 send_elicitation(id, request) → 出来的是 ElicitationResponse;如果外部处理失败,就转换成 RMCP 能理解的内部错误。

调用关系:它只在 ElicitationClientService::handle_request 认出 CreateElicitationRequest 时被调用。它自己不决定用户界面怎么显示,而是把活交给 send_elicitation 回调;恢复 meta 的细节交给 restore_context_meta。

调用图:调用 2 个内部函数(restore_context_meta, enter);被 1 处调用(handle_request)。

clone_send_elicitation64–66 ↗
fn clone_send_elicitation(send_elicitation: Arc<SendElicitation>) -> SendElicitation

作用:把共享的 send_elicitation 回调再包装成一个新的回调。这样同一套发送询问的能力可以同时给本服务和日志处理器使用。

数据流:进去的是 Arc 包着的 send_elicitation → 它创建一个闭包,也就是一小段记住原回调的函数 → 出来的是新的 SendElicitation,调用它时仍然会转到同一个真实回调上。

调用关系:它在 ElicitationClientService::new 里使用,专门给 LoggingClientHandler 准备一份可调用的包装。这样初始化时不用复制真实逻辑,只共享同一个发送入口。

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

ElicitationClientService::handle_request69–90 ↗
async fn handle_request(
        &self,
        request: ServerRequest,
        context: RequestContext<RoleClient>,
    ) -> Result<ClientResult, rmcp::ErrorData>

作用:这是服务器请求进来时的分流口。它会专门拦截 elicitation 请求,其它请求则原样交给日志处理器。

数据流:进去的是 ServerRequest 和请求上下文 → 它检查请求类型:如果是 CreateElicitationRequest,就调用 create_elicitation 得到用户回答,再用 elicitation_response_result 转成可发送的自定义结果;如果不是,就转给 LoggingClientHandler → 出来的是 ClientResult,或一个 RMCP 错误。

调用关系:它是 RMCP Service 接口的一部分,通常由 RMCP 框架在收到服务器请求时调用。它把 elicitation 的主流程交给 create_elicitation,把结果格式转换交给 elicitation_response_result,把非 elicitation 请求交给 LoggingClientHandler 的 handle_request。

调用图:调用 2 个内部函数(create_elicitation, elicitation_response_result);外部调用 2 个(handle_request, CustomResult)。

ElicitationClientService::handle_notification92–103 ↗
async fn handle_notification(
        &self,
        notification: ServerNotification,
        context: NotificationContext<RoleClient>,
    ) -> Result<(), rmcp::ErrorData>

作用:处理服务器发来的通知。这里不做特别逻辑,直接交给带日志的默认处理器。

数据流:进去的是 ServerNotification 和通知上下文 → 它调用 LoggingClientHandler 的 handle_notification → 出来的是成功的空结果,或处理通知时产生的错误;本服务本身不改动通知内容。

调用关系:它也是 RMCP Service 接口的一部分,由框架在收到服务器通知时调用。因为 elicitation 主要是请求-回应式的,这里的通知通道由 LoggingClientHandler 接手。

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

ElicitationClientService::get_info105–107 ↗
fn get_info(&self) -> ClientInfo

作用:返回这个客户端对外声明的基本信息。比如服务器想知道客户端是谁、有什么信息,就会通过这个接口拿。

数据流:没有额外输入,只读取内部的 LoggingClientHandler → 它调用 LoggingClientHandler 的 get_info → 返回 ClientInfo,不修改任何状态。

调用关系:它实现 RMCP Service 接口,由框架在需要客户端信息时调用。信息实际保存在 LoggingClientHandler 里,所以这里相当于转交查询。

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

restore_context_meta110–122 ↗
fn restore_context_meta(mut request: Elicitation, mut context_meta: Meta) -> Elicitation

作用:把被 RMCP 框架提前拿走的 _meta 附加信息放回 elicitation 请求里,同时去掉进度用的 progressToken。这样业务代码看到的请求更完整,也不会误把进度标记当成业务数据。

数据流:进去的是一个 Elicitation 请求和一份 context_meta → 它先删除 progressToken;如果剩下的 meta 为空,就直接返回原请求;如果还有内容,就把这些内容合并进请求自己的 meta 字段 → 出来的是 meta 被修正后的请求。

调用关系:它在 create_elicitation 里被调用,是把框架上下文还原成业务请求的关键小步骤。测试 restore_context_meta_adds_elicitation_meta_and_removes_progress_token 会验证它确实添加了普通 meta,并移除了 progressToken。

调用图:被 2 处调用(create_elicitation, restore_context_meta_adds_elicitation_meta_and_removes_progress_token);外部调用 3 个(meta_mut, is_empty, remove)。

elicitation_response_result134–151 ↗
fn elicitation_response_result(
    response: ElicitationResponse,
) -> Result<CustomResult, rmcp::ErrorData>

作用:把用户对 elicitation 的回答转成 RMCP 能发回服务器的自定义结果。这样可以带上标准结果里没有建模的 _meta 字段。

数据流:进去的是 ElicitationResponse,包含 action、content 和 meta → 它装进 CreateElicitationResultWithMeta 这个可序列化结构,再用 serde_json 转成 JSON 值,最后包成 CustomResult → 出来的是可返回给服务器的 CustomResult;如果 JSON 转换失败,就变成内部错误。

调用关系:它在 handle_request 得到用户回答后调用,负责最后的协议格式包装。测试 elicitation_response_result_serializes_response_meta 会确认 _meta 能被正确序列化出去。

调用图:被 2 处调用(handle_request, elicitation_response_result_serializes_response_meta);外部调用 1 个(to_value)。

tests::restore_context_meta_adds_elicitation_meta_and_removes_progress_token166–181 ↗
fn restore_context_meta_adds_elicitation_meta_and_removes_progress_token()

作用:测试 restore_context_meta 的关键行为:普通 meta 要保留,progressToken 要删掉。它防止以后改代码时把这条协议细节弄坏。

数据流:进去的是测试构造出的表单请求和一份包含 progressToken、persist 的 meta → 它调用 restore_context_meta → 用断言比较结果,确认只剩 persist 被放进请求 meta 里。

调用关系:这是 restore_context_meta 的单元测试。它用 tests::form_request 构造假请求,用 tests::meta 构造 meta 数据,然后直接验证函数输出。

调用图:调用 1 个内部函数(restore_context_meta);外部调用 4 个(assert_eq!, json!, form_request, meta)。

tests::elicitation_response_result_serializes_response_meta184–202 ↗
fn elicitation_response_result_serializes_response_meta()

作用:测试 elicitation 回答在变成返回结果时,_meta 没有丢。它保证服务器能收到客户端想附带的额外信息。

数据流:进去的是测试构造的 ElicitationResponse,里面有 accept 动作、content 和 meta → 它调用 elicitation_response_result,再把 ClientResult 序列化成 JSON → 用断言确认 JSON 里有 action、content 和 _meta

调用关系:这是 elicitation_response_result 的单元测试。它模拟 handle_request 后半段会做的包装和序列化过程,确保结果格式符合预期。

调用图:调用 1 个内部函数(elicitation_response_result);外部调用 3 个(assert_eq!, json!, CustomResult)。

tests::form_request204–213 ↗
fn form_request(meta: Option<Meta>) -> CreateElicitationRequestParams

作用:给测试快速造一个表单式 elicitation 请求。这样测试不用每次手写一大段表单结构。

数据流:进去的是可选的 Meta → 它创建一个问题为“Confirm?”的表单请求,并构造一个要求填写 confirmed 布尔值的 schema;schema 可以理解为“表单字段规则” → 出来的是 CreateElicitationRequestParams。

调用关系:它只在测试里使用,主要服务于 tests::restore_context_meta_adds_elicitation_meta_and_removes_progress_token。它把测试输入准备好,让测试重点放在 meta 恢复逻辑上。

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

tests::meta215–220 ↗
fn meta(value: Value) -> Meta

作用:把测试里的 JSON 对象转换成 RMCP 使用的 Meta 类型。它让测试写起来更像普通 JSON,更直观。

数据流:进去的是 serde_json::Value → 如果这个值是 JSON 对象,就取出里面的键值表并包成 Meta;如果不是对象,就直接 panic,也就是让测试失败 → 出来的是 Meta。

调用关系:它只在测试里作为小工具使用,主要配合 tests::restore_context_meta_adds_elicitation_meta_and_removes_progress_token 构造输入和预期结果。

调用图:外部调用 2 个(panic!, Meta)。

rmcp-client/src/logging_client_handler.rs源码 ↗
orchestrationrequest handling / cross-cutting

MCP 可以理解成客户端和服务器之间的一套对话协议。服务器不只是回答问题,还可能主动通知客户端:某个请求取消了、任务有进度了、资源列表变了,或者发来一条日志。这个文件里的 LoggingClientHandler 就像前台接线员:所有这些从服务器来的消息先到它这里。普通通知会被写进日志,方便人排查问题;服务器发来的日志会按严重程度分到 error、warn、info、debug 等不同日志级别;如果服务器要求“向用户再问点信息”,它会调用创建时传进来的 send_elicitation 回调,把问题交给真正能和用户交互的地方。它还保存一份 ClientInfo,用来告诉 MCP 服务器“我这个客户端是谁”。

函数细节10
LoggingClientHandler::new29–34 ↗
fn new(client_info: ClientInfo, send_elicitation: SendElicitation) -> Self

作用:创建一个新的客户端处理器。调用者把客户端身份信息和“如何向用户追问信息”的函数交给它,它就把这些保存起来,之后收到服务器消息时使用。

数据流:进去的是 ClientInfo 和 SendElicitation。函数把 SendElicitation 放进 Arc(一种可安全共享同一份东西的引用计数包装),再和 ClientInfo 一起装进 LoggingClientHandler。出来的是一个可被 MCP 客户端注册使用的处理器。

调用关系:它是这个处理器的组装入口,外层创建客户端时会调用它。它内部用 Arc::new 包好追问用户的函数,方便后续异步回调里共享使用。

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

LoggingClientHandler::create_elicitation38–47 ↗
async fn create_elicitation(
        &self,
        request: CreateElicitationRequestParams,
        context: RequestContext<RoleClient>,
    ) -> Result<CreateElicitationResult, rmcp::ErrorData>

作用:处理服务器发来的“请向用户追问信息”的请求。比如服务器需要用户确认、选择或补充内容时,会走到这里。

数据流:进去的是服务器的追问请求和这次请求的上下文,其中上下文里有请求编号。函数把请求编号和请求内容交给创建时保存的 send_elicitation。成功时,把外部返回的结果转换成 MCP 需要的 CreateElicitationResult;失败时,把错误包装成 MCP 的内部错误返回。

调用关系:它是 ClientHandler 接口的一部分,由 rmcp 框架在服务器发起 elicitation 请求时调用。它自己不直接和用户交流,而是把活儿交给 send_elicitation,这样用户界面或上层业务可以决定怎么问、怎么答。

LoggingClientHandler::on_cancelled49–58 ↗
async fn on_cancelled(
        &self,
        params: CancelledNotificationParam,
        _context: NotificationContext<RoleClient>,
    )

作用:记录服务器通知“某个请求被取消了”。这能让人知道不是客户端无缘无故没结果,而是服务器明确取消了请求。

数据流:进去的是取消通知,里面有 request_id 和可选原因。函数把这些信息写成一条 info 级别日志。它不返回业务结果,也不修改内部状态。

调用关系:当 rmcp 框架收到服务器的取消通知时会调用它。它把这类协议事件交给日志系统的 info! 宏记录下来,供运行人员或开发者查看。

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

LoggingClientHandler::on_progress60–69 ↗
async fn on_progress(
        &self,
        params: ProgressNotificationParam,
        _context: NotificationContext<RoleClient>,
    )

作用:记录服务器发来的任务进度。比如一个长任务完成了多少、总量是多少、有没有附带说明,都在这里写进日志。

数据流:进去的是进度通知,包含进度 token、当前进度、总量和消息。函数把这些字段整理成一条 info 级别日志。出来没有新数据,只是在日志里留下进度记录。

调用关系:rmcp 框架在收到服务器进度通知时调用它。它不参与计算进度,只负责把服务器说的进度用 info! 写出来。

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

LoggingClientHandler::on_resource_updated71–77 ↗
async fn on_resource_updated(
        &self,
        params: ResourceUpdatedNotificationParam,
        _context: NotificationContext<RoleClient>,
    )

作用:记录服务器通知“某个资源更新了”。资源可以理解成服务器提供的一份文件、数据或可读取内容。

数据流:进去的是资源更新通知,主要有资源的 uri(资源地址)。函数把这个 uri 写入 info 级别日志。它不重新拉取资源,也不改缓存。

调用关系:当服务器通过 MCP 告诉客户端某个资源有变化时,rmcp 框架会调用它。它的职责很窄:把变化消息交给日志系统保存。

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

LoggingClientHandler::on_resource_list_changed79–81 ↗
async fn on_resource_list_changed(&self, _context: NotificationContext<RoleClient>)

作用:记录服务器通知“资源列表变了”。这表示服务器能提供的资源集合可能增加、减少或调整了。

数据流:进去的是通知上下文,但这里不需要读取具体内容。函数只写一条 info 级别日志,说明资源列表发生变化。没有返回值,也不主动刷新列表。

调用关系:rmcp 框架收到资源列表变化通知时调用它。它把这个事件通过 info! 写入日志,真正要不要重新获取资源列表由别的部分决定。

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

LoggingClientHandler::on_tool_list_changed83–85 ↗
async fn on_tool_list_changed(&self, _context: NotificationContext<RoleClient>)

作用:记录服务器通知“工具列表变了”。工具可以理解成服务器提供给客户端调用的一组能力。

数据流:进去的是通知上下文,但函数不需要使用里面的数据。它写一条 info 级别日志,说明工具列表已变化。它不会自己重新加载工具列表。

调用关系:当 rmcp 框架收到工具列表变化通知时会调用它。它通过 info! 留痕,方便后续排查为什么可用工具发生变化。

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

LoggingClientHandler::on_prompt_list_changed87–89 ↗
async fn on_prompt_list_changed(&self, _context: NotificationContext<RoleClient>)

作用:记录服务器通知“提示词列表变了”。提示词可以理解成服务器提供的一些预设问题模板或对话模板。

数据流:进去的是通知上下文,但这里没有用到。函数写一条 info 级别日志,说明提示词列表发生变化。它不负责重新读取提示词内容。

调用关系:rmcp 框架在收到提示词列表变化通知时调用它。它只做记录,把事件交给日志系统。

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

LoggingClientHandler::get_info91–93 ↗
fn get_info(&self) -> ClientInfo

作用:把这个客户端的身份信息交给 MCP 框架。服务器需要知道客户端叫什么、版本等信息时,会用到它。

数据流:进去的是当前处理器自身。函数读取里面保存的 client_info,并 clone(一份拷贝)出来返回。这样外部拿到信息时,不会直接改到处理器内部保存的那份。

调用关系:这是 ClientHandler 接口要求提供的函数,rmcp 框架会在需要客户端信息时调用。它只调用 clone 复制数据,不触发网络或日志操作。

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

LoggingClientHandler::on_logging_message95–135 ↗
async fn on_logging_message(
        &self,
        params: LoggingMessageNotificationParam,
        _context: NotificationContext<RoleClient>,
    )

作用:处理服务器主动发来的日志消息,并按严重程度写到本应用的日志系统。这样服务器端发生的事情也能出现在客户端日志里,排查问题更完整。

数据流:进去的是一条服务器日志,里面有级别、可选的 logger 名字和日志内容。函数先拆开这些字段,再根据级别选择记录方式:严重错误走 error,警告走 warn,普通信息走 info,调试信息走 debug。出来没有返回值,但日志系统里会多一条对应级别的记录。

调用关系:rmcp 框架收到服务器日志通知时调用它。它是服务器日志和本地 tracing 日志系统之间的翻译层,会把不同 LoggingLevel 分别交给 error!、warn!、info! 或 debug!。

调用图:外部调用 4 个(debug!, error!, info!, warn!)。

HTTP 代理请求处理

这个独立传输路由器处理传入的 HTTP 和 CONNECT 流量,应用代理策略,并据此转发或阻止请求。

network-proxy/src/http_proxy.rs源码 ↗
io_transportstartup, main loop, request handling

这个文件解决的是“程序能不能访问某个网络地址、该怎么转发”的问题。没有它,代理可能会直接把请求放出去,绕过项目里的网络策略;也可能在 HTTPS、Unix socket、本机代理转发这些特殊场景里行为不一致。它启动一个 HTTP/1 代理监听端口,收到 CONNECT 时先检查目标主机和策略,必要时要求 MITM(中间人解密检查,也就是代理临时查看 HTTPS 里面的真实请求)才能继续;普通 HTTP 请求则检查 Host 是否可信、代理是否启用、方法是否允许、域名是否允许。被拦截的请求会返回清楚的错误响应,并记录审计事件和 blocked 记录。真正放行时,它会清理只适合单段连接的 HTTP 头,再把请求转给上游网站、上游代理,或 macOS 上允许的 Unix socket。

函数细节31
run_http_proxy86–103 ↗
async fn run_http_proxy(
    state: Arc<NetworkProxyState>,
    addr: SocketAddr,
    policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
) -> Result<()>

作用:按给定地址启动 HTTP 代理监听。别人只要给它共享状态、监听地址和可选的策略判断器,它就负责把代理服务跑起来。

数据流:进去的是代理状态、要绑定的 IP 和端口、可选策略判断器 → 它创建 TCP 监听器,绑定到这个地址,并给绑定失败补上易懂的错误上下文 → 成功后把监听器交给真正搭服务的 run_http_proxy_with_listener,最后返回运行结果。

调用关系:这是上层 run 启动代理时会调用的入口之一。它自己不处理请求,只负责准备监听器,然后把后续工作交给 run_http_proxy_with_listener。

调用图:调用 1 个内部函数(run_http_proxy_with_listener);被 1 处调用(run);外部调用 1 个(build)。

run_http_proxy_with_std_listener105–113 ↗
async fn run_http_proxy_with_std_listener(
    state: Arc<NetworkProxyState>,
    listener: StdTcpListener,
    policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
) -> Result<()>

作用:用一个已经创建好的标准库监听器来启动代理。这样测试或外部代码可以先自己占好端口,再交给代理使用。

数据流:进去的是代理状态、标准 TCP 监听器和可选策略判断器 → 它把标准监听器转换成 Rama 框架使用的监听器 → 转换成功后继续调用 run_http_proxy_with_listener,转换失败就返回错误。

调用关系:上层 run 和监听器相关测试会用它。它是 run_http_proxy_with_listener 的包装层,方便复用同一套代理启动逻辑。

调用图:调用 1 个内部函数(run_http_proxy_with_listener);被 2 处调用(http_proxy_listener_accepts_plain_http1_connect_requests, run);外部调用 1 个(try_from)。

run_http_proxy_with_listener115–154 ↗
async fn run_http_proxy_with_listener(
    state: Arc<NetworkProxyState>,
    listener: TcpListener,
    policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
) -> Result<()>

作用:真正把 HTTP 代理服务装配到监听器上并开始接客。它决定 CONNECT 请求走隧道流程,普通 HTTP 请求走普通转发流程。

数据流:进去的是共享状态、已绑定监听器、可选策略判断器 → 它初始化 TLS 加密库,读取本地监听地址,搭出 HTTP/1 服务链:CONNECT 先由 http_connect_accept 审核,再由 http_connect_proxy 转发;普通请求交给 http_plain_proxy;响应里还会移除不该透传的逐跳头 → 最后把共享状态挂进每个连接并开始 serve。

调用关系:run_http_proxy 和 run_http_proxy_with_std_listener 都把最终工作交给它。它是本文件的装配台,把请求按类型分派给 http_connect_accept、http_connect_proxy 和 http_plain_proxy。

调用图:被 2 处调用(run_http_proxy, run_http_proxy_with_std_listener);外部调用 9 个(new, http1, hop_by_hop, local_addr, serve, new, ensure_rustls_crypto_provider, info!, service_fn)。

http_connect_accept156–329 ↗
async fn http_connect_accept(
    policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
    mut req: Request,
) -> Result<(Response, Request), Response>

作用:审核 HTTPS CONNECT 请求能不能建立隧道。CONNECT 是浏览器让代理连接某个 HTTPS 站点的方式,必须先过策略门禁。

数据流:进去的是可选策略判断器和一条 CONNECT 请求 → 它取出全局状态、解析目标主机和端口、规范化主机名、读取客户端地址,检查代理是否启用、域名策略是否允许、当前模式或主机 hook 是否需要 MITM,以及 MITM 状态是否可用 → 允许时在请求扩展里塞入目标、模式和 MITM 标记,并返回 200;拒绝时返回带原因的响应,同时记录 blocked 和审计信息。

调用关系:它由 CONNECT 升级层在建立隧道前调用。它会用 client_addr、evaluate_host_policy、proxy_disabled_response、blocked_text_with_details 和 emit_http_block_decision_audit_event;如果它放行,后续 http_connect_proxy 才会真正转发或 MITM。多个测试直接调用它验证各种放行和拦截场景。

调用图:调用 9 个内部函数(blocked_text_with_details, client_addr, emit_http_block_decision_audit_event, proxy_disabled_response, text_response, new, evaluate_host_policy, normalize_host, new);被 4 处调用(http_connect_accept_allows_allowlisted_host_in_full_mode, http_connect_accept_blocks_hooked_host_in_full_mode_without_mitm_state, http_connect_accept_blocks_in_limited_mode, http_connect_accept_denies_denylisted_host);外部调用 9 个(try_from, extensions, extensions_mut, builder, error!, empty, info!, ProxyTarget, warn!)。

http_connect_proxy331–405 ↗
async fn http_connect_proxy(upgraded: Upgraded) -> Result<(), Infallible>

作用:在 CONNECT 已被允许后,真正处理这条隧道。它要么交给 MITM 检查 HTTPS 内部请求,要么直接把客户端和目标服务器接通。

数据流:进去的是已经升级后的连接 → 它从扩展里读出网络模式、目标地址、是否启用 MITM、共享状态和上游代理设置 → 如果需要且有 MITM 状态,就调用 mitm_tunnel;否则决定是否走环境里的上游代理,并调用 forward_connect_tunnel → 函数本身总是结束为 Ok,不把隧道错误抛给外层。

调用关系:它跟在 http_connect_accept 后面运行。它会在 MITM 场景把活交给 mitm::mitm_tunnel,在普通隧道场景把活交给 forward_connect_tunnel,并用 proxy_for_connect 选择上游代理。

调用图:调用 4 个内部函数(forward_connect_tunnel, mitm_tunnel, normalize_host, proxy_for_connect);外部调用 4 个(extensions, error!, info!, warn!)。

forward_connect_tunnel407–477 ↗
async fn forward_connect_tunnel(
    upgraded: Upgraded,
    proxy: Option<ProxyAddress>,
    app_state: Arc<NetworkProxyState>,
) -> Result<(), BoxError>

作用:把 CONNECT 隧道的两头真正接起来:一头是客户端,一头是目标服务器或上游代理后的目标服务器。

数据流:进去的是升级后的客户端连接、可选上游代理地址、共享状态 → 它读出目标地址,把代理信息放进扩展,创建 TCP 请求并使用带目标检查的连接器拨号,外层再套 TLS 隧道连接器 → 连上后把客户端流和目标流交给 StreamForwardService 双向搬运字节 → 成功时完成转发,失败时返回带上下文的错误。

调用关系:只由 http_connect_proxy 调用。它是 CONNECT 普通转发路线的底层执行者,前面的策略判断已经完成,它负责安全地拨号并搬运数据。

调用图:调用 1 个内部函数(new);被 1 处调用(http_connect_proxy);外部调用 10 个(optional, now, from_boxed, default, new_with_extensions, new, tunnel, extensions, info!, warn!)。

http_plain_proxy479–803 ↗
async fn http_plain_proxy(
    policy_decider: Option<Arc<dyn NetworkPolicyDecider>>,
    mut req: Request,
) -> Result<Response, Infallible>

作用:处理非 CONNECT 的普通 HTTP 代理请求。它会先检查各种安全规则,再把请求转发到目标网站、上游代理或允许的 Unix socket。

数据流:进去的是可选策略判断器和一条 HTTP 请求 → 它取出共享状态和客户端地址,先看请求方法是否允许;如果有 x-unix-socket 头,就走 Unix socket 的专门检查:代理开关、方法限制、平台支持、路径白名单;否则解析 Host,检查绝对 URI 和 Host 头是否一致,检查代理是否启用、域名策略是否允许、方法是否允许 → 被拒绝就返回 JSON 或文本错误并记录;被允许就按配置选择 UpstreamClient,清理逐跳头后转发请求 → 输出上游响应或 502 错误。

调用关系:普通 HTTP 请求都会从 run_http_proxy_with_listener 装配的服务链进入这里。它会调用 client_addr、validate_absolute_form_host_header、evaluate_host_policy、json_blocked、proxy_disabled_response、proxy_via_unix_socket、remove_hop_by_hop_request_headers 等辅助函数;多项测试直接调用它检查拦截行为。

调用图:调用 16 个内部函数(client_addr, emit_http_allow_decision_audit_event, emit_http_block_decision_audit_event, json_blocked, proxy_disabled_response, proxy_via_unix_socket, remove_hop_by_hop_request_headers, text_response, validate_absolute_form_host_header, new (+6 more));被 4 处调用(http_plain_proxy_attempts_allowed_unix_socket_proxy, http_plain_proxy_blocks_unix_socket_when_method_not_allowed, http_plain_proxy_rejects_absolute_uri_host_header_mismatch, http_plain_proxy_rejects_unix_socket_when_not_allowlisted);外部调用 8 个(try_from, extensions, headers, headers_mut, method, error!, info!, warn!)。

proxy_via_unix_socket805–831 ↗
async fn proxy_via_unix_socket(req: Request, socket_path: &str) -> Result<Response>

作用:把 HTTP 请求转发到本机 Unix socket。Unix socket 可以理解成本机进程之间用的“文件形状的插口”,这里只在支持的平台和白名单通过后使用。

数据流:进去的是 HTTP 请求和 socket 路径 → 在 macOS 上,它创建 Unix socket 上游客户端,把请求拆成头部和正文,把 URI 改成只含路径,移除 x-unix-socket 和逐跳头,再重新组装并发送 → 输出上游响应;非 macOS 上直接返回“不支持”的错误。

调用关系:只由 http_plain_proxy 在 x-unix-socket 路径被允许后调用。它还会调用 remove_hop_by_hop_request_headers,确保发给本地服务的请求头是干净的。

调用图:调用 2 个内部函数(remove_hop_by_hop_request_headers, unix_socket);被 1 处调用(http_plain_proxy);外部调用 3 个(anyhow!, from_parts, into_parts)。

client_addr833–838 ↗
fn client_addr(input: &T) -> Option<String>

作用:从请求或连接的扩展信息里取出客户端地址。这个地址用于日志、审计和 blocked 记录,方便知道是谁发起的请求。

数据流:进去的是任何能提供扩展信息的对象 → 它查找 SocketInfo,并把对端地址转成字符串 → 找得到就返回地址,找不到就返回 None,不会报错。

调用关系:http_connect_accept 和 http_plain_proxy 在做策略判断和记录日志前会调用它。它是很小的取信息工具,不参与放行或拦截判断。

调用图:被 2 处调用(http_connect_accept, http_plain_proxy);外部调用 1 个(extensions)。

validate_absolute_form_host_header840–872 ↗
fn validate_absolute_form_host_header(
    req: &Request,
    request_ctx: &RequestContext,
) -> Result<(), &'static str>

作用:检查代理请求里的完整网址和 Host 头是否指向同一个目标。这样可以防止请求行说去 A 网站、Host 头却伪装成 B 网站的混乱或攻击。

数据流:进去的是请求和已经解析出的请求上下文 → 如果 URI 不是完整形式,就直接认为没问题;如果是完整网址,就读取 Host 头,比较主机名和端口,端口缺失时还会判断是否是默认端口 → 一致返回 Ok,不一致返回固定错误文字。

调用关系:http_plain_proxy 在普通 HTTP 转发前调用它。相关测试覆盖了默认端口匹配、主机不匹配和非默认端口缺失这几种情况。

调用图:被 1 处调用(http_plain_proxy);外部调用 3 个(authority_has_default_port, headers, uri)。

remove_hop_by_hop_request_headers873–906 ↗
fn remove_hop_by_hop_request_headers(headers: &mut HeaderMap)

作用:删除不应该被代理继续转发的 HTTP 头。逐跳头就是只对当前这一段连接有意义的头,像快递面单上的“交给下一站处理”,不能原样贴到最终收件人那里。

数据流:进去的是可修改的请求头集合 → 它先处理 Connection 头里列出的临时头名并删除它们,再删除 keep-alive、proxy-authorization、transfer-encoding、upgrade、TE 等常见逐跳头 → 输出没有返回值,但原来的头集合会被就地清理。

调用关系:http_plain_proxy 在转发到上游前调用它,proxy_via_unix_socket 在发给本机 socket 前也调用它。测试 remove_hop_by_hop_request_headers_keeps_forwarding_headers 验证它不会误删转发类头。

调用图:被 3 处调用(http_plain_proxy, proxy_via_unix_socket, remove_hop_by_hop_request_headers_keeps_forwarding_headers);外部调用 3 个(get, remove, from_bytes)。

json_blocked908–937 ↗
fn json_blocked(host: &str, reason: &str, details: Option<&PolicyDecisionDetails<'_>>) -> Response

作用:生成一个 JSON 格式的“请求被拦截”响应。客户端可以用它清楚知道被拦的是哪个主机、原因是什么,以及策略细节。

数据流:进去的是主机、拦截原因和可选策略详情 → 它组装 BlockedResponse,必要时加入 decision、source、protocol、port 和可读消息 → 输出 HTTP 403 响应,并设置 x-proxy-error 头说明错误类别。

调用关系:http_plain_proxy 在域名、方法、Unix socket 白名单等检查失败时会调用它。它内部用 json_response 做序列化,用 blocked_header_value 生成统一错误头。

调用图:调用 2 个内部函数(blocked_header_value, json_response);被 1 处调用(http_plain_proxy);外部调用 1 个(from_static)。

blocked_text_with_details939–941 ↗
fn blocked_text_with_details(reason: &str, details: &PolicyDecisionDetails<'_>) -> Response

作用:生成文本版的策略拦截响应。它主要给 CONNECT 这类不适合返回 JSON 正文的场景使用。

数据流:进去的是拦截原因和策略详情 → 它直接调用 blocked_text_response_with_policy 生成带策略说明的文本响应 → 输出一个 HTTP 响应。

调用关系:http_connect_accept 在 CONNECT 被策略拒绝或缺少 MITM 时调用它。它是对 responses 模块函数的薄包装,让本文件里的 CONNECT 代码更好读。

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

proxy_disabled_response943–994 ↗
async fn proxy_disabled_response(
    app_state: &NetworkProxyState,
    host: String,
    port: u16,
    client: Option<String>,
    method: Option<String>,
    protocol: NetworkProtocol,
    audit_e

作用:生成“代理当前被关闭”的响应,并把这次拒绝记录下来。这样用户看到的是明确的服务不可用,而不是模糊的网络失败。

数据流:进去的是代理状态、目标主机端口、客户端地址、方法、协议和可选审计地址覆盖 → 它先发出拦截审计事件,再把 blocked 记录写入状态,接着构造策略详情和文本消息 → 输出 HTTP 503 响应。

调用关系:http_connect_accept 和 http_plain_proxy 在发现代理开关关闭时都会调用它。它会借助 emit_http_block_decision_audit_event、record_blocked、blocked_message_with_policy 和 text_response 完成记录与回复。

调用图:调用 6 个内部函数(emit_http_block_decision_audit_event, text_response, as_policy_protocol, blocked_message_with_policy, new, record_blocked);被 2 处调用(http_connect_accept, http_plain_proxy)。

internal_error996–999 ↗
fn internal_error(context: &str, err: impl std::fmt::Display) -> Response

作用:把内部读取状态或配置失败这类问题统一变成 500 响应。它同时写日志,方便排查后台到底哪里坏了。

数据流:进去的是错误场景说明和具体错误 → 它把两者写入错误日志 → 输出正文为 error 的 HTTP 500 文本响应。

调用关系:它被 http_connect_accept、http_plain_proxy 等流程通过 map_err 使用。后续响应生成交给 text_response。

调用图:调用 1 个内部函数(text_response);外部调用 1 个(error!)。

text_response1001–1007 ↗
fn text_response(status: StatusCode, body: &str) -> Response

作用:快速构造一个纯文本 HTTP 响应。很多错误场景不需要复杂 JSON,只需要状态码和一句话。

数据流:进去的是 HTTP 状态码和文本内容 → 它用响应构造器设置状态码、content-type 为 text/plain,并把字符串放进响应体 → 输出 Response;如果构造器意外失败,就退回一个简单响应。

调用关系:http_connect_accept、http_plain_proxy、internal_error 和 proxy_disabled_response 都用它返回简短错误。它是本文件最基础的响应工具。

调用图:被 4 处调用(http_connect_accept, http_plain_proxy, internal_error, proxy_disabled_response);外部调用 2 个(builder, from)。

emit_http_block_decision_audit_event1009–1014 ↗
fn emit_http_block_decision_audit_event(
    app_state: &NetworkProxyState,
    args: BlockDecisionAuditEventArgs<'_>,
)

作用:发出 HTTP 代理里的“拦截决定”审计事件。审计事件就是给系统留下可追踪记录,说明为什么挡住了某次访问。

数据流:进去的是代理状态和审计参数 → 它不改写参数,直接转交给通用的 emit_block_decision_audit_event → 没有返回值,效果是系统记录一条拒绝审计。

调用关系:http_connect_accept、http_plain_proxy 和 proxy_disabled_response 在各种拒绝路径里调用它。它把本文件和通用网络策略审计函数连接起来。

调用图:调用 1 个内部函数(emit_block_decision_audit_event);被 3 处调用(http_connect_accept, http_plain_proxy, proxy_disabled_response)。

emit_http_allow_decision_audit_event1016–1021 ↗
fn emit_http_allow_decision_audit_event(
    app_state: &NetworkProxyState,
    args: BlockDecisionAuditEventArgs<'_>,
)

作用:发出 HTTP 代理里的“允许通过”审计事件。它用于少数需要明确记录放行的场景,比如允许访问某个 Unix socket。

数据流:进去的是代理状态和审计参数 → 它把信息交给 emit_allow_decision_audit_event → 没有返回值,效果是系统记录一条允许审计。

调用关系:http_plain_proxy 在 Unix socket 白名单通过后调用它。它是 HTTP 代理层到通用允许审计函数的转接点。

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

tests::http_connect_accept_blocks_in_limited_mode1060–1085 ↗
async fn http_connect_accept_blocks_in_limited_mode()

作用:验证受限模式下,如果 CONNECT 需要 MITM 但没有 MITM 状态,就会被拦住。这样能防止 HTTPS 请求绕过方法级检查。

数据流:进去的是测试构造的策略、状态和 CONNECT 请求 → 测试把网络模式设为 Limited,然后调用 http_connect_accept → 期望输出是 403,并且 x-proxy-error 表示 blocked-by-mitm-required。

调用关系:这个测试直接覆盖 http_connect_accept 的 MITM 必需拦截路径。它用 network_proxy_state_for_policy 准备状态。

调用图:调用 3 个内部函数(default, http_connect_accept, network_proxy_state_for_policy);外部调用 5 个(new, assert_eq!, builder, empty, vec!)。

tests::http_connect_accept_allows_allowlisted_host_in_full_mode1088–1111 ↗
async fn http_connect_accept_allows_allowlisted_host_in_full_mode()

作用:验证完整模式下,白名单里的 CONNECT 主机可以通过。它确保正常 HTTPS 隧道不会被误伤。

数据流:进去的是允许 example.com 的测试策略和 CONNECT 请求 → 调用 http_connect_accept → 期望拿到 200 OK,而不是错误响应。

调用关系:这个测试直接调用 http_connect_accept,覆盖允许路径。它和拦截测试一起说明 CONNECT 审核既会挡也会放。

调用图:调用 3 个内部函数(default, http_connect_accept, network_proxy_state_for_policy);外部调用 5 个(new, assert_eq!, builder, empty, vec!)。

tests::http_connect_accept_blocks_hooked_host_in_full_mode_without_mitm_state1114–1147 ↗
async fn http_connect_accept_blocks_hooked_host_in_full_mode_without_mitm_state()

作用:验证即使在完整模式下,只要某个主机配置了 MITM hook,但实际没有 MITM 状态,也必须拦截。hook 需要看 HTTPS 里面的请求,不具备条件就不能假装放行。

数据流:进去的是带 api.github.com MITM hook 的测试策略和 CONNECT 请求 → 调用 http_connect_accept → 期望返回 403,并带 blocked-by-mitm-required 错误头。

调用关系:这个测试覆盖 http_connect_accept 中“主机有 MITM hook 因而需要 MITM”的分支。

调用图:调用 2 个内部函数(http_connect_accept, network_proxy_state_for_policy);外部调用 6 个(new, default, assert_eq!, builder, empty, vec!)。

tests::http_proxy_listener_accepts_plain_http1_connect_requests1150–1209 ↗
async fn http_proxy_listener_accepts_plain_http1_connect_requests()

作用:验证代理监听器能接受普通 HTTP/1 CONNECT 请求。它防止底层服务器配置变动导致客户端在 CONNECT 前就卡住。

数据流:进去的是测试临时启动的目标监听器、代理监听器和允许本地地址的状态 → 测试启动 run_http_proxy_with_std_listener,手写一段 CONNECT HTTP/1.1 请求发给代理 → 期望读到以 HTTP/1.1 200 OK 开头的响应。

调用关系:这个测试走得比单元测试更完整:它调用 run_http_proxy_with_std_listener,间接覆盖 run_http_proxy_with_listener 的服务装配和 CONNECT 路径。

调用图:调用 3 个内部函数(default, run_http_proxy_with_std_listener, network_proxy_state_for_policy);外部调用 11 个(new, from_secs, bind, from_utf8_lossy, bind, assert!, format!, connect, spawn, timeout (+1 more))。

tests::http_plain_proxy_blocks_unix_socket_when_method_not_allowed1212–1238 ↗
async fn http_plain_proxy_blocks_unix_socket_when_method_not_allowed()

作用:验证 Unix socket 代理也会遵守请求方法限制。即使目标是本机 socket,受限模式下的 POST 也不能绕过去。

数据流:进去的是受限模式状态和带 x-unix-socket 的 POST 请求 → 调用 http_plain_proxy → 期望返回 403,并带 blocked-by-method-policy 错误头。

调用关系:这个测试直接覆盖 http_plain_proxy 的 Unix socket 方法拦截分支。

调用图:调用 3 个内部函数(default, http_plain_proxy, network_proxy_state_for_policy);外部调用 4 个(new, assert_eq!, builder, empty)。

tests::http_plain_proxy_rejects_unix_socket_when_not_allowlisted1241–1267 ↗
async fn http_plain_proxy_rejects_unix_socket_when_not_allowlisted()

作用:验证没有加入白名单的 Unix socket 不会被代理访问。这样避免代理变成本机权限提升的后门。

数据流:进去的是默认策略状态和带 x-unix-socket 的 GET 请求 → 调用 http_plain_proxy → 在 macOS 上期望 403 和 blocked-by-allowlist;其他平台因为不支持该能力,期望 NOT_IMPLEMENTED。

调用关系:这个测试覆盖 http_plain_proxy 对 Unix socket 平台支持和白名单检查的行为。

调用图:调用 3 个内部函数(default, http_plain_proxy, network_proxy_state_for_policy);外部调用 5 个(new, assert_eq!, cfg!, builder, empty)。

tests::http_plain_proxy_attempts_allowed_unix_socket_proxy1271–1290 ↗
async fn http_plain_proxy_attempts_allowed_unix_socket_proxy()

作用:验证白名单允许的 Unix socket 会尝试转发。测试不要求真的有服务在 socket 后面,所以预期是转发失败的网关错误。

数据流:进去的是允许 /tmp/test.sock 的策略和带该 socket 头的 GET 请求 → 调用 http_plain_proxy → 在 macOS 上会进入 proxy_via_unix_socket,因没有真实服务而返回 502。

调用关系:这个测试只在 macOS 编译运行,覆盖 http_plain_proxy 到 proxy_via_unix_socket 的允许路径。

调用图:调用 3 个内部函数(default, http_plain_proxy, network_proxy_state_for_policy);外部调用 5 个(new, assert_eq!, builder, empty, vec!)。

tests::http_connect_accept_denies_denylisted_host1293–1318 ↗
async fn http_connect_accept_denies_denylisted_host()

作用:验证黑名单优先生效:即使域名匹配了允许规则,被明确拒绝的主机也不能 CONNECT。

数据流:进去的是允许 **.openai.com 但拒绝 api.openai.com 的策略和 CONNECT 请求 → 调用 http_connect_accept → 期望返回 403,并带 blocked-by-denylist 错误头。

调用关系:这个测试直接覆盖 http_connect_accept 通过 evaluate_host_policy 得到拒绝决定的路径。

调用图:调用 3 个内部函数(default, http_connect_accept, network_proxy_state_for_policy);外部调用 5 个(new, assert_eq!, builder, empty, vec!)。

tests::http_plain_proxy_rejects_absolute_uri_host_header_mismatch1321–1335 ↗
async fn http_plain_proxy_rejects_absolute_uri_host_header_mismatch()

作用:验证普通 HTTP 代理会拒绝“请求网址”和 Host 头不一致的请求。这样避免目标地址被伪装或混淆。

数据流:进去的是一个 URI 指向 raw.githubusercontent.com、Host 头却写 api.github.com 的请求 → 调用 http_plain_proxy → 期望返回 400 Bad Request。

调用关系:这个测试覆盖 http_plain_proxy 调用 validate_absolute_form_host_header 后拒绝的路径。

调用图:调用 3 个内部函数(default, http_plain_proxy, network_proxy_state_for_policy);外部调用 4 个(new, assert_eq!, builder, empty)。

tests::validate_absolute_form_host_header_allows_matching_default_port1338–1350 ↗
fn validate_absolute_form_host_header_allows_matching_default_port()

作用:验证完整 HTTP URL 使用默认端口时,Host 头不写端口也算匹配。比如 http://example.com 默认就是 80。

数据流:进去的是 URI 为 http://example.com/、Host 为 example.com 的请求 → 调用 validate_absolute_form_host_header → 期望返回 Ok。

调用关系:这个测试直接检查 validate_absolute_form_host_header 的默认端口逻辑。

调用图:外部调用 3 个(assert_eq!, builder, empty)。

tests::validate_absolute_form_host_header_rejects_mismatched_host1353–1365 ↗
fn validate_absolute_form_host_header_rejects_mismatched_host()

作用:验证 Host 头主机名和请求目标主机名不一样时会被拒绝。

数据流:进去的是 URI 指向 raw.githubusercontent.com、Host 为 api.github.com 的请求 → 调用 validate_absolute_form_host_header → 期望返回 Host header does not match request target。

调用关系:这个测试直接检查 validate_absolute_form_host_header 的主机名比较逻辑。

调用图:外部调用 3 个(assert_eq!, builder, empty)。

tests::validate_absolute_form_host_header_rejects_missing_non_default_port1368–1380 ↗
fn validate_absolute_form_host_header_rejects_missing_non_default_port()

作用:验证目标 URL 使用非默认端口时,Host 头如果漏掉端口会被拒绝。这样端口不会被偷偷改掉。

数据流:进去的是 URI 为 http://example.com:8080/、Host 为 example.com 的请求 → 调用 validate_absolute_form_host_header → 期望返回不匹配错误。

调用关系:这个测试直接检查 validate_absolute_form_host_header 的端口比较逻辑。

调用图:外部调用 3 个(assert_eq!, builder, empty)。

tests::remove_hop_by_hop_request_headers_keeps_forwarding_headers1383–1413 ↗
fn remove_hop_by_hop_request_headers_keeps_forwarding_headers()

作用:验证清理逐跳头时,不会误删真正需要继续转发的头。比如 X-Forwarded-For 和 Host 应该留下。

数据流:进去的是一组包含 Connection、临时头、Proxy-Authorization、X-Forwarded-For 和 Host 的请求头 → 调用 remove_hop_by_hop_request_headers → 期望逐跳头被删掉,而转发相关头仍然存在。

调用关系:这个测试直接覆盖 remove_hop_by_hop_request_headers,保护 HTTP 转发前的头部清理行为不被改坏。

调用图:调用 1 个内部函数(remove_hop_by_hop_request_headers);外部调用 3 个(new, from_static, assert_eq!)。