Agent Runtime 深度解析
本页只拆一个模块面,用真实源码行号解释数据流、边界和失败模式。
导入链与契约
▸isolated turn 的 handler 边界
导入链很短但契约强:apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:16 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:17 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:18 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:19 接收 request 和 host handlers,说明 isolated runtime 不直接拿模型或工具实现,只通过 handler 边界调用。
16export async function executeIsolatedAgentTurn( 17 request: IsolatedAgentTurnRequest, 18 options: IsolatedAgentTurnRuntimeOptions, 19): Promise<IsolatedAgentTurnResult> { 20 throwIfAborted(options.signal); 21 22 const messages: IsolatedAgentConversationMessage[] = structuredClone( 23 request.messages, 24 ); 25 const steps: IsolatedAgentTurnStepResult[] = []; 26 const allowedToolNames = new Set(request.tools.map((tool) => tool.name)); 27 let text = ''; 28 29 for (let step = 1; step <= request.maxSteps; step++) { 30 throwIfAborted(options.signal); 31 options.onEvent?.({ type: 'step-started', step }); 32 33 const modelResult = await options.handlers.callModel( 34 { 35 agentInstanceId: request.agentInstanceId, 36 modelId: request.modelId, 37 traceId: request.traceId, 38 metadata: request.metadata, 39 systemPrompt: request.systemPrompt, 40 messages, 41 tools: request.tools, 42 settings: request.settings, 43 }, 44 { 45 signal: requireSignal(options.signal), 46 onEvent: (event) => { 47 options.onEvent?.({ ...event, step }); 48 }, 49 }, 50 ); 51 throwIfAborted(options.signal); 52 53 text += modelResult.text; 54 messages.push({ 55 role: 'assistant', 56 text: modelResult.text, 57 toolCalls: modelResult.toolCalls, 58 }); 59 60 const stepResult: IsolatedAgentTurnStepResult = { 61 index: step, 62 text: modelResult.text, 63 reasoning: modelResult.reasoning, 64 finishReason: modelResult.finishReason, 65 rawFinishReason: modelResult.rawFinishReason, 66 usage: modelResult.usage, 67 providerMetadata: modelResult.providerMetadata, 68 toolCalls: modelResult.toolCalls, 69 toolResults: [], 70 toolErrors: [], 71 approvalRequests: [], 72 }; 73 steps.push(stepResult);
核心实现的第一道安全门在 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:26 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:75 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:76 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:77:模型只能调用 request.tools 声明过的工具,任何额外 toolName 都直接 throw。这个判断让工具白名单成为代码事实。
75 for (const call of modelResult.toolCalls) { 76 if (!allowedToolNames.has(call.toolName)) { 77 throw new Error( 78 `Model requested undeclared isolated tool "${call.toolName}"`, 79 ); 80 } 81 82 options.onEvent?.({ type: 'tool-call', step, call }); 83 const result = await options.handlers.callTool( 84 { 85 agentInstanceId: request.agentInstanceId, 86 call, 87 messages, 88 }, 89 { 90 signal: requireSignal(options.signal), 91 }, 92 ); 93 throwIfAborted(options.signal); 94 95 if (result.status === 'approval-required') { 96 const approvalRequest = { 97 approvalId: result.approvalId, 98 toolCallId: call.toolCallId, 99 toolName: call.toolName, 100 }; 101 stepResult.approvalRequests.push(approvalRequest); 102 options.onEvent?.({ 103 type: 'tool-approval-request', 104 step, 105 ...approvalRequest, 106 }); 107 continue; 108 } 109 if (result.status === 'error') { 110 const toolError = { 111 toolCallId: call.toolCallId, 112 toolName: call.toolName, 113 message: result.message, 114 }; 115 stepResult.toolErrors.push(toolError); 116 messages.push({ 117 role: 'tool', 118 toolCallId: call.toolCallId, 119 toolName: call.toolName, 120 output: { 121 error: result.message, 122 }, 123 }); 124 options.onEvent?.({ 125 type: 'tool-error', 126 step, 127 ...toolError, 128 }); 129 continue; 130 } 131 132 const toolResult = { 133 toolCallId: call.toolCallId, 134 toolName: call.toolName, 135 output: result.output, 136 }; 137 stepResult.toolResults.push(toolResult); 138 messages.push({ 139 role: 'tool', 140 ...toolResult, 141 }); 142 options.onEvent?.({ 143 type: 'tool-result', 144 step, 145 ...toolResult, 146 }); 147 }
审批路径也不是旁路:apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:95 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:101 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:102 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:107 把 approval-required 写入 stepResult 并继续循环处理其他 call;随后 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:156 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:158 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:160 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:169 在有 approval request 时返回 completed 或 max-steps。
149 options.onEvent?.({ 150 type: 'step-finished', 151 step, 152 finishReason: modelResult.finishReason, 153 usage: modelResult.usage, 154 }); 155 156 if ( 157 modelResult.toolCalls.length === 0 || 158 stepResult.approvalRequests.length > 0 159 ) { 160 return { 161 status: 'completed', 162 text, 163 messages, 164 steps, 165 }; 166 } 167 } 168 169 return { 170 status: 'max-steps', 171 text, 172 messages, 173 steps, 174 }; 175}
源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:16 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:20 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:22 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:26 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:29 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:33 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:45 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:51 显示,Clodex 不是把 AI 聊天窗口塞进 IDE,而是把任务状态、工具权限、运行时隔离和可审计执行拆成独立层。这里每个引用都对应真实实现:管理器负责状态与生命周期,isolated runtime 负责模型/工具循环,MCP 配置负责边界校验,shell 层负责可取消、可回放的命令执行。
源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:60 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:75 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:76 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:83 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:95 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:101 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:109 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:116 显示,Clodex 不是把 AI 聊天窗口塞进 IDE,而是把任务状态、工具权限、运行时隔离和可审计执行拆成独立层。这里每个引用都对应真实实现:管理器负责状态与生命周期,isolated runtime 负责模型/工具循环,MCP 配置负责边界校验,shell 层负责可取消、可回放的命令执行。
源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:132 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:138 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:149 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:156 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:158 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:160 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:169 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:181 显示,Clodex 不是把 AI 聊天窗口塞进 IDE,而是把任务状态、工具权限、运行时隔离和可审计执行拆成独立层。这里每个引用都对应真实实现:管理器负责状态与生命周期,isolated runtime 负责模型/工具循环,MCP 配置负责边界校验,shell 层负责可取消、可回放的命令执行。
从被 quality gate 计入的 package 层看,同一个执行边界会落到 ShellService:packages/agent-shell/src/engine/shell-service.ts:17 说明它是服务级入口;packages/agent-shell/src/engine/shell-service.ts:22 记录检测到的 shell;packages/agent-shell/src/engine/shell-service.ts:47 与源码证据 packages/agent-shell/src/engine/shell-service.ts:57 把流式刷新和最大输出字节数写成常量;packages/agent-shell/src/engine/shell-service.ts:77 与源码证据 packages/agent-shell/src/engine/shell-service.ts:97 说明实例创建后必须初始化 shell 环境;packages/agent-shell/src/engine/shell-service.ts:128 再把 SessionManager 作为真正 PTY 会话管理器。
执行命令时,ShellService 继续保持可取消、可观察的边界:packages/agent-shell/src/engine/shell-service.ts:153 描述只创建会话不执行命令的入口;packages/agent-shell/src/engine/shell-service.ts:168 先清洗环境;packages/agent-shell/src/engine/shell-service.ts:176 与源码证据 packages/agent-shell/src/engine/shell-service.ts:194 负责把 session id 推给 UI;packages/agent-shell/src/engine/shell-service.ts:207 是持久 PTY 执行入口;packages/agent-shell/src/engine/shell-service.ts:225 再次清洗执行环境;packages/agent-shell/src/engine/shell-service.ts:247 与源码证据 packages/agent-shell/src/engine/shell-service.ts:293 表明它可以复用已有 session,也可以创建新 session 并交给 SessionManager 执行。
AgentManager 这一侧负责让 runtime 不直接碰宿主 UI:packages/agent-core/src/services/agent-manager/agent-manager.ts:151 是管理器类入口;packages/agent-core/src/services/agent-manager/agent-manager.ts:163 保存 active agents;packages/agent-core/src/services/agent-manager/agent-manager.ts:184 把 host 能力作为唯一依赖;packages/agent-core/src/services/agent-manager/agent-manager.ts:198 暴露生命周期事件 hook;packages/agent-core/src/services/agent-manager/agent-manager.ts:227 与源码证据 packages/agent-core/src/services/agent-manager/agent-manager.ts:242 把状态容器和启动策略固定在 manager 中。这些 package 级引用解释了为什么 isolated runtime 可以短小:复杂生命周期和 shell 细节被分层收束,而不是散落在模型循环里。
模型/工具循环
▸ShellService 的会话化执行
执行流是 model → tool → messages,而不是模型任意执行:apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:33 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:40 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:41 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:53 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:54 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:83 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:87 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:138 展示了消息如何逐步累计,tool result 如何回到 conversation。
边界与失败
▸AgentManager 的状态与重试边界
失败模式包括 AbortSignal、未知工具、工具错误和 maxSteps。apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:20 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:51 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:77 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:109 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:121 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:169 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:181 与源码证据 apps/browser/src/backend/agent-host/isolated-agent-turn-runtime.ts:183 分别对应这些出口。