ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

VS Code 源码级解读:Agent Host 中的 `ClaudeAgent` 骨架如何落地(Phase 4 实现计划)

VS Code 源码级解读:Agent Host 中的 `ClaudeAgent` 骨架如何落地(Phase 4 实现计划) VS Code 源码级解读Agent Host 中的ClaudeAgent骨架如何落地Phase 4 实现计划【免费下载链接】vscodeVisual Studio Code项目地址: https://gitcode.com/GitHub_Trending/vscode6/vscode导读本文基于仓库内src/vs/platform/agentHost/node/claude/phase4-plan.md这一份“交接式实现计划”完整拆解 Visual Studio CodeVS Code如何在 agent hostAgent 宿主进程中落地一个名为ClaudeAgent的IAgent提供方骨架它如何与已有的CopilotAgent并列注册、如何被一处chat.agentHost.claudeAgent.enabled开关默认遮蔽、如何复用 GitHub Copilot 的认证与模型目录以及其余方法如何以TODO: Phase N的形式为后续阶段预留位置。读完本文你将掌握该实现的类骨架设计、注册门控链路、模型过滤谓词、stub 方法分阶段映射表、单元测试范式与 live-system smoke 流程并能用仓库源码逐一印证。本文所依据的计划文档是一份真实被执行的“Phase 4 契约”其相邻的 roadmap.md 已标记 Phase 4✅ DONEPR landedcommit7211c0f3746live-system smoke 完成于 2026-05-01。因此文中既有“计划如何设计”也有“当前仓库实现到何种程度”的对照读者可顺藤摸瓜阅读 phase5-plan.md、smoke.md 等后续文档。1. 背景为什么需要一个ClaudeAgent骨架在 agent host 的架构里IAgent是统一抽象CopilotAgent是第一个也是当时唯一的树内参考实现。计划的目标是在 agent host 中新增第二个 providerClaudeAgent让它向IAgentService注册自己与CopilotAgent并列出现在根状态root state中通过ICopilotApiService.models()拉取并筛选出 Anthropic 家族的模型对外广告复用与CopilotAgent完全相同的 GitHub 资源认证https://api.github.com/https://github.com/login/oauth用户只登录一次 GitHub Copilot 即可选用 Claude 模型全程不直连 Anthropic。而 Phase 4 明确不含SDK、子进程或sendMessage——这些属于 Phase 6。绝大多数IAgent方法在此阶段以throw new Error(TODO: Phase N)占位。1.1 为什么默认关闭因为骨架是“绝大多数用户可见方法都会抛TODO”的桩件如果把注册默认打开任何选择 Claude 模型的用户都会在第一个提示词上撞到TODO: Phase 5错误。因此计划在原始 brief 之外新增了注册门控默认关闭、需要显式开启直到用户可见方法不再抛错自然的里程碑是 Phase 6sendMessage落地才打开默认值。这一设计演进值得注意当前仓库中该设置项的默认值已经翻转为true见下文 5.2 节说明代码已越过 Phase 6进入了默认可见的状态。1.2 退出标准exit criteria设置chat.agentHost.claudeAgent.enabled: true后连接 agent host 的 workbench 客户端能在根状态看到claudeprovider能挑选 Claude 模型调用sendMessage()抛出TODO: Phase 6设置关闭默认时根状态中只有copilotcli。2. 文件改动地图计划针对以下文件创建/修改目标实现规模约 200–300 行动作文件用途新建src/vs/platform/agentHost/node/claude/claudeAgent.tsClaudeAgent类本体当前仓库中该文件已长成 2672 行的完整实现新建src/vs/platform/agentHost/test/node/claudeAgent.test.ts单元测试当前仓库已存在 claudeAgent.test.ts另有claudeAgent.integrationTest.ts修改src/vs/platform/agentHost/common/agentService.ts导出设置项 ID 与环境变量常量修改src/vs/workbench/contrib/chat/browser/chat.contribution.ts注册chat.agentHost.claudeAgent.enabled修改src/vs/platform/agentHost/electron-main/electronAgentHostStarter.ts读取设置、以 env var 转发进 utility process修改src/vs/platform/agentHost/node/nodeAgentHostStarter.tsNode 子进程兜底路径同样转发修改src/vs/platform/agentHost/node/agentHostMain.ts依据 env var 条件注册ClaudeAgent修改src/vs/platform/agentHost/node/agentHostServerMain.ts补注册ICopilotApiService、IClaudeProxyService、ClaudeAgent当时三者皆缺修改scripts/launch-smoke.sh相关辅助脚本导出 env var避免 smoke 触碰用户设置路径演化提示计划书写于早期部分落地位置在后来的实现中有调整。例如设置 schema 当前实际注册于 agentHostStarter.config.contribution.ts而非计划当时指定的chat.contribution.ts阅读时以源码为准。3.ClaudeAgent类骨架规范3.1 参考实现地图计划明确给出了“照谁抄、抄哪段”Phase 4 关注点参考来源类外壳、Disposable、Emitter、observable树内IAgent参考实现 copilotAgent.tsgetDescriptor/getProtectedResources/authenticate只参考CopilotAgent扩展侧没有IAgent对应物模型过滤extensions/copilot/src/extension/chatSessions/claude/node/claudeCodeModels.ts:165–179同时计划给出了一条重要纪律不要整段照抄扩展侧ClaudeCodeSessionclaudeCodeAgent.ts:122——那个类已累积约 20 层关注点MCP gateway、plugins、edit tracker、settings tracker、OTel、hooks、debug logger、ripgrep PATH、runtime data、folder MRU。每个关注点应等到真正需要它的那个 phase 再进入树内实现。3.2 类外壳与 provider idexport class ClaudeAgent extends Disposable implements IAgent { readonly id claude as const; private readonly _onDidSessionProgress this._register(new EmitterAgentSignal()); readonly onDidSessionProgress this._onDidSessionProgress.event; private readonly _models observableValuereadonly IAgentModelInfo[](this, []); readonly models: IObservablereadonly IAgentModelInfo[] this._models; private _githubToken: string | undefined; private _proxyHandle: IClaudeProxyHandle | undefined; constructor( ILogService private readonly _logService: ILogService, ICopilotApiService private readonly _copilotApiService: ICopilotApiService, IClaudeProxyService private readonly _claudeProxyService: IClaudeProxyService, ) { super(); } // ... }关键决策点Provider id 是claude不是claude-code。因为该 id 会经由AgentSession.uri()成为 session URI 的 schemeclaude:/uuid品牌化信息放在displayName里。AgentProvider在 agentService.ts 中实际是一个string类型别名而非枚举对应实现位于common/agent.tsCLAUDE_AGENT_PROVIDER_ID claude as const。三个构造依赖缺一不可日志、Copilot CAPI 服务、Claude 代理服务。_models用observableValue承载作为可观测模型列表对外暴露。3.3getDescriptor()与getProtectedResources()镜像CopilotAgent对应实现用户可见字符串一律走localize()getDescriptor(): IAgentDescriptor { return { provider: claude, displayName: localize(claudeAgent.displayName, Claude), description: localize(claudeAgent.description, Claude agent backed by the Anthropic Claude Agent SDK), }; } getProtectedResources(): ProtectedResourceMetadata[] { return [{ resource: https://api.github.com, resource_name: GitHub Copilot, authorization_servers: [https://github.com/login/oauth], scopes_supported: [read:user, user:email], required: true, }]; }这保证了 Claude 与 Copilot 共享同一 GitHub 保护资源——认证机制天然复用客户端无需新增登录流程。3.4authenticate()本阶段唯一的真实逻辑计划强调不要在构造函数里启动代理IClaudeProxyService.start()要求非空 GitHub tokenclaudeProxyService.ts 中有此约束因此急切构造不可行。代理启动必须推迟到 token 到达的authenticate()async authenticate(resource: string, token: string): Promiseboolean { if (resource ! https://api.github.com) { return false; } const tokenChanged this._githubToken ! token; this._githubToken token; this._logService.info([Claude] Auth token ${tokenChanged ? updated : unchanged}); if (tokenChanged) { // Restart proxy with new token. Old handles dispose() decrements // refcount; ClaudeProxyService applies most-recent-token-wins. const oldHandle this._proxyHandle; this._proxyHandle await this._claudeProxyService.start(token); oldHandle?.dispose(); void this._refreshModels(); } return true; }顺序是关键先拿到新 handle、再释放旧 handle使两次调用之间代理服务不会整体拆除引用计数始终 ≥ 1。3.5_refreshModels()token 变化驱动 过期写保护private async _refreshModels(): Promisevoid { const tokenAtStart this._githubToken; if (!tokenAtStart) { this._models.set([], undefined); return; } try { const all await this._copilotApiService.models(tokenAtStart); const filtered all.filter(m isClaudeModel(m)).map(m toAgentModelInfo(m, this.id)); if (this._githubToken tokenAtStart) { this._models.set(filtered, undefined); } } catch (err) { this._logService.error(err, [Claude] Failed to refresh models); if (this._githubToken tokenAtStart) { this._models.set([], undefined); } } }关键点在await之后写入前重新比对 token。若慢速的models(tokA)在 token 轮换后才返回其迟到结果必须被丢弃——这正是文档 §5 测试用例 13“stale-write guard”要固化下来的不变式。3.6 模型过滤谓词与IAgentModelInfo投影模块级辅助函数两个检查合一CCAModel 的表面过滤 Claude id 解析器后者能优雅排除auto这类合成 idfunction isClaudeModel(m: CCAModel): boolean { return ( m.vendor Anthropic !!m.supported_endpoints?.includes(/v1/messages) !!m.model_picker_enabled !!m.capabilities?.supports?.tool_calls tryParseClaudeModelId(m.id) ! undefined // from claudeModelId.ts ); } function toAgentModelInfo(m: CCAModel, provider: AgentProvider): IAgentModelInfo { return { provider, id: m.id, name: m.name, maxContextWindow: m.capabilities?.limits?.max_context_window_tokens, supportsVision: !!m.capabilities?.supports?.vision, }; }对应的字段类型引用自 copilot-api.d.tsvendor、可选的supported_endpoints必须用?.includes因为它是string[] | undefined、model_picker_enabled、capabilities.supports.tool_calls、capabilities.limits.max_context_window_tokens。编写时必须对照IAgentModelInfo真实接口形状agentService.ts 中的定义删减不存在的字段。现状对照这一谓词已完整落在当前 claudeAgent.ts 中isClaudeModel位于文件头部附近且后续还扩展了 reasoning effort 等字段——证明计划的过滤器设计经受住了后续阶段的演进。3.7 stub 映射表未实现的所有必需IAgent方法统一throw new Error(TODO: Phase N)phase 编号必须与 roadmap 一致方法Phase N备注createSession5disposeSession5getSessionMessages5完整转录重建属 Phase 13listSessions5resolveSessionConfig5sessionConfigCompletions5shutdown5sendMessage6respondToPermissionRequest7respondToUserInputRequest7abortSession9changeModel9setClientTools10onClientToolCallComplete10setClientCustomizations11setCustomizationEnabled11可省略的可选方法接口允许不实现truncateSession?、setPendingMessages?、getCustomizations?、getSessionCustomizations?、onArchivedChanged?、onDidCustomizationsChange?。由于 roadmap 的执行顺序并不按编号线性推进实际为 1 → 1.5 → 2 → 3 → 4 → 5 → 6 → 9 → 13 → 7 → …提交每个Phase N前必须交叉核对 roadmap避免编号漂移。3.8dispose()必须真实实现不能是 TODOAgentService.dispose()会无条件调用provider.dispose()。_proxyHandle是引用计数的不释放就会泄漏代理服务生命周期override dispose(): void { // Phase 6 INVARIANT: SDK subprocess(es) MUST be killed before disposing // the proxy handle. In Phase 4 there are no subprocesses, so this is safe. this._proxyHandle?.dispose(); this._proxyHandle undefined; this._githubToken undefined; this._models.set([], undefined); super.dispose(); }注释是强制的——Phase 6 引入 SDK 子进程后释放顺序必须满足IClaudeProxyHandle的约定。4. 注册与门控链路4.1 契约总览门控的目的默认安装的用户永远看不到一个“桩 provider”。其契约是一条完整链路[workbench] [agent host process] chat.agentHost.claudeAgent.enabled --- env var1 --- registerProvider(ClaudeAgent)设置键名与 env var 名都集中在 agentService.ts计划中的常量为AgentHostClaudeAgentEnabledSettingId与AgentHostEnableClaudeEnvVar保证两端引用同一个字符串。env var 兼作开发者覆盖开关在父进程如 smoke 脚本里设置它即可绕过 workbench 设置。变更需要重启 agent host——env var 在进程 spawn 时捕获运行中翻转设置不会生效设置描述必须写清楚这一点。4.2 设置 schema[AgentHostClaudeAgentEnabledSettingId]: { type: boolean, description: nls.localize(chat.agentHost.claudeAgent.enabled, When enabled, the Claude agent provider is registered inside the agent host. The agent host process must be restarted for changes to this setting to take effect.), default: false, tags: [experimental, advanced], included: product.quality ! stable, },included: product.quality ! stable意味着 stable 构建中该设置项根本不出现只有 Insiders 等非稳定构建可见——这正服务于“不让 Insiders 用户以外的任何人在生产里撞到桩件错误”。现状对照当前 agentHostStarter.config.contribution.ts 中该设置已注册并带有企业策略绑定policy nameClaude3PIntegration且default已变为true——这正是计划中“等用户可见方法不再抛错后翻转默认值”的落实结果。4.3 两个 starter设置 → env varElectron utility-process 路径electronAgentHostStarter.ts在start()构造 env 前计算const claudeEnabled this._configurationService.getValueboolean(AgentHostClaudeAgentEnabledSettingId) || process.env[AgentHostEnableClaudeEnvVar] 1; this.utilityProcess.start({ // ... env: { ...deepClone(process.env), ...shellEnv, VSCODE_ESM_ENTRYPOINT: vs/platform/agentHost/node/agentHostMain, VSCODE_PIPE_LOGGING: true, VSCODE_VERBOSE_LOGGING: true, ...(claudeEnabled ? { [AgentHostEnableClaudeEnvVar]: 1 } : {}), } });Node 子进程兜底nodeAgentHostStarter.ts优先级逻辑相同设置 OR 继承的 env var写入env后再构造IIPCOptions。当前仓库中两处 starter 均确实读取该设置并向下游传递。4.4agentHostMain.tsDI 已具备前置条件ICopilotApiService与IClaudeProxyService已在此注册因此在CopilotAgent之后追加一个受 env var 门控的注册agentService.registerProvider(instantiationService.createInstance(CopilotAgent)); if (process.env[AgentHostEnableClaudeEnvVar] 1) { agentService.registerProvider(instantiationService.createInstance(ClaudeAgent)); }导入注意树内 import 一律带.js后缀即便源文件是.ts例如ClaudeAgent从./claude/claudeAgent.js导入。4.5agentHostServerMain.tsDI 裸奔需补三样再门控当时该文件既不注册ICopilotApiService也不注册IClaudeProxyService独立服务器模式下直接注册ClaudeAgent会崩溃。计划要求在if (!options.quiet)块内、createInstance(CopilotAgent)之前补齐const copilotApiService instantiationService.createInstance(CopilotApiService, undefined); diServices.set(ICopilotApiService, copilotApiService); const claudeProxyService disposables.add(instantiationService.createInstance(ClaudeProxyService)); diServices.set(IClaudeProxyService, claudeProxyService);随后条件注册 Claude并新增 CLI 旗标--enable-claude-agent镜像已有的--enable-mock-agent与 env var 取或// parseServerOptions(): const enableClaudeAgent argv.includes(--enable-claude-agent) || process.env[AgentHostEnableClaudeEnvVar] 1; // !options.quiet 块内CopilotAgent 注册之后 if (options.enableClaudeAgent) { const claudeAgent disposables.add(instantiationService.createInstance(ClaudeAgent)); agentService.registerProvider(claudeAgent); log(ClaudeAgent registered); }注册顺序敏感IClaudeProxyService的构造函数依赖ICopilotApiService因此必须先注册ICopilotApiService否则服务器启动即崩溃。CopilotApiService的undefined第二参fetchFn槽位是有意为之与agentHostMain.ts行为一致。5. 测试文件规范新文件src/vs/platform/agentHost/test/node/claudeAgent.test.ts当前已存在镜像 copilotAgent.test.ts 的createTestAgentContext装配风格。Mock 策略是“最小替身”而非繁重模拟IClaudeProxyServicemockstart(token)返回{ baseUrl: http://127.0.0.1:0, nonce: test-nonce, dispose: () disposeCount }追踪调用次数与最后一次 token。ICopilotApiServicemockmodels(token)返回混合了 Anthropic / 非 Anthropic / 无 tool_calls / 未进 picker 的预置CCAModel[]。13 个测试用例快照断言遵循assert.deepStrictEqualgetDescriptor()返回预期 descriptor。getProtectedResources()匹配 GitHub 资源形状。authenticate之前 models observable 为空。对 GitHub 资源authenticate返回true调用start(token)models 只含 Claude 族条目且形状正确。对无关资源authenticate返回false随后对 GitHub 资源再次authenticate仍可用代理start总共只调一次——用于捕获“早期 return false 时已污染状态”的实现错误。同一 token 二次authenticate不再次start()。不同 token新start(tokenB)被调用且旧 handle 被 dispose。过滤器排除矩阵非 Anthropic vendor含合成auto模型、缺/v1/messages端点、model_picker_enabled: false、tool_calls: false、无法解析的 Claude id。AgentSession.uri(claude, abc)往返一致schemeclaude、idabc、providerclaude。dispose()释放代理 handle二次dispose()幂等。抽样 3–4 个 stub 方法抛出消息含TODO: Phase及正确编号的Error。注册 smoke实例化AgentService 注册ClaudeAgent经公共服务面断言其出现在根状态。stale-write guard慢models(tokA)在authenticate(tokB)已发布[B]之后才返回时迟到的[A]结果必须被丢弃。测试用例 13 是文档着力强调的一个不变式——它把CopilotAgent已依赖但从未测试的“过期写保护”固化为测试。控制解析顺序用 async.ts 的DeferredPromiseconst tokAModels new DeferredPromiseCCAModel[](); mockApi.models (token: string) token tokA ? tokAModels.p : Promise.resolve([CLAUDE_MODEL_B]); void agent.authenticate(https://api.github.com, tokA); // refresh-A starts, hangs on tokAModels.p await agent.authenticate(https://api.github.com, tokB); // refresh-B runs to completion, models [B] tokAModels.complete([CLAUDE_MODEL_A]); // refresh-A unblocks; guard must drop the write await new Promise(r setImmediate(r)); assert.deepStrictEqual(agent.models.get().map(m m.id), [CLAUDE_MODEL_B.id]);测试套件顶部还要调用ensureNoDisposablesAreLeakedInTestSuite()做泄漏检测且 mock 的IClaudeProxyHandle不应注册进测试 store用普通对象字面量而非Disposable子类。6. 风险与应对gotchas风险缓解旧 roadmap 说“构造函数里启动代理”——错误start()需要 token以本计划 claudeProxyService.ts为准推迟到authenticate()agentHostServerMain.ts缺ICopilotApiService/IClaudeProxyServiceDI服务器模式会崩按 §4.5 补齐两处注册CCAModel.supported_endpoints是string[] \| undefined直接.includes()会抛一律用?.includes()_refreshModels在await后写回token 可能已轮换开头快照 token、写回前比对镜像 CopilotAgent不实现真实dispose()会泄漏代理引用计数本阶段就实现真dispose()并注释 Phase 6 顺序不变式IClaudeProxyService构造函数依赖ICopilotApiServiceagentHostServerMain.ts中先注册前者异步断言时机敏感模型拉取是 promise用await new Promise(r setImmediate(r))排空微任务需要控制时序时用DeferredPromise避免计时器等待忘记门控——默认开启会把TODO: Phase N暴露给每个 Insiders 用户两个 main 文件都必须以 env var 门控未设置 env var 时 smoke 应只见copilotcli运行中翻转设置不生效env var 在 spawn 时捕获重载窗口或杀宿主进程后生效设置描述写清smoke 报告“ClaudeAgent 没注册”但实为 env var 丢失launch-smoke.sh导出 env varElectron starter 通过展开process.env后再合并显式 env var 转发Node starter 显式写入 spawn env7. 验收清单摘要计划把 PR “完成”定义为一组按序可勾选的验收项代码结构claudeAgent.ts存在且export class ClaudeAgent extends Disposable implements IAgentid claude构造函数恰好三个依赖16 个 stub 方法全部TODO: Phase N不出现可选方法dispose()真实实现并释放_proxyHandlePhase 6 子进程所有权不变式写入注释每个新文件带微软版权头。注册与门控常量从 agentService.ts 导出设置以default: false、tags: [experimental, advanced]、非 stable 才可见注册描述注明需重启两个 starter 都转发 env var两个 main 文件都在env var 1时才注册。编译与静态检查VS Code - Build或npm run typecheck-client零 TypeScript 错误npm run eslint -- 两个新文件通过npm run valid-layers-check通过仅允许导入vs/base、vs/platform、vs/typings误引vs/workbench/vs/sessions会失败npm run hygiene通过。测试全部 13 个用例存在ensureNoDisposablesAreLeakedInTestSuite()在套件顶部scripts/test.sh --grep ClaudeAgent全绿无as any/as unknown as Foo无真实网络/子进程依赖。行为退出标准默认关闭时只见copilotcli开启后claude与copilotcli并列认证后模型选择器出现 Anthropic 模型对 Claude session 调sendMessage抛TODO: Phase 6。失败排查表节选IAgentModelInfo编译错 → 加了不存在的字段如family重读 agentService.ts 中接口形状valid-layers-check失败 → 误从高层导入Test 13 flake → 用DeferredPromisesetImmediate代替计时器dispose()测试泄漏 → mock handle 用普通对象字面量agentHostServerMain.ts启动崩溃 → DI 注册顺序Cannot find module ./claude/claudeAgent.js→ import 缺.js后缀。8. live-system smoke真实“能开机、能注册、能出现在 UI”单元测试证明类在隔离下接线正确而 live smoke 证明它真的能启动、注册并在已认证用户的 Agents 应用中浮现。完整流程见 smoke.md核心动作Launch用launch-smoke.sh port拉起应用处理unset ELECTRON_RUN_AS_NODE、--user-data-dir、CDP 端口等脚本内导出 env var绕开用户设置。Verify agent host wiring无需 UI跑verify-claude-logs.sh [--phaseN]断言五条日志不变式copilotcli与claude两个 provider 都注册[Claude] Auth token updated出现证明agentService.authenticate向拥有该资源的所有 provider 扇出[ClaudeProxyService] listening on http://127.0.0.1:port根状态 IPC 日志含provider: claude块≥ 1 个 Claude 家族模型 idclaude-opus-*、claude-sonnet-*等浮现——同时验证模型过滤谓词与tryParseClaudeModelId。Verify picker UIplaywright 连 CDP快照中应同时出现option Copilot CLI与option Claude。文档记录了一个陷阱——下拉浮层有context-view-pointerBlock拦截点击须用ArrowDownEnter键盘导航而非直接 click。Drive a promptPhase 4 发消息命中createSessionstub快照出现TODO: Phase 5Phase 5 则是TODO: Phase 6Phase 6 才是真实模型输出。Phase 4 的实际 smoke 已把证据落盘到/tmp/claude-phase4-smoke/timestamp/包括registration.logRegistering agent provider: copilotcli…: claude两行、一条捕获的claude:/e32d3567-9da7-41c4-a71a-57daa0a6cf46session URI、46 个 Claude 模型条目、以及首个提示词触发TODO: Phase 5的截图其中四份必需工件要随 PR 附上。最终状态为除“disabled-gate 实跑”一项有意推迟交给 Phase 5 的单元级门控测试覆盖外Phase 4 全部必须不变式均验证通过。9. 已决决策resolved decisionsdispose()是否等待在途_refreshModels不等待。dispose()在super.dispose()之前清空_githubToken因此任何被挂起的_refreshModels恢复后都会发现 token 不匹配而放弃写入——在途刷新自动变成 no-op测试用例 13 固化该不变式。fire-and-forget与CopilotAgent一致。规范只说了“注册”为什么还要加设置门控Phase 4 是桩件用户可见方法全抛TODO。默认开启意味着任何 Insiders 用户在 picker 里选中 Claude 都会在首个提示词上撞TODO: Phase 5。默认关闭 设置 开发者 env var 使 Phase 4 可测试而不广泛暴露。设置included: product.quality ! stable使其在稳定构建完全隐藏。只有用户可见方法不再抛错时才翻转默认或移除门控——自然里程碑是 Phase 6sendMessage落地。workbench 设置 vs. agent-host 根配置IAgentConfigurationService选 workbench 设置。根配置服务于经 IPC 协议流动的运行时/每会话旋钮而这是“provider 是否存在”的功能开关应在进程 spawn 时决定。10. 从 Phase 4 骨架到完整实现当前仓库的落点把计划与当前源码对照可以直观看到这条演进线已被完整走完claudeAgent.ts 已从计划的 ~300 行骨架长成 2672 行完整实现isClaudeModel过滤谓词、toAgentModelInfo投影函数均按计划落地并扩展文件头还保留了ClaudeSessionMetadataStore、fork anchor、subagent 等 Phase 5–19 引入的能力引用。roadmap.md 显示 Phase 4 之后的 session 生命周期5、sendMessage6、fork6.5、resumeSessionAt截断6.7、tool calls permission7、transcript 重建13等均已 ✅ DONE且 phase 编号作为稳定标识被TODO: Phase N抛错与注释持续引用。设置默认值已翻转、注册门控从“默认遮蔽”演化为“默认可见”正是计划 §9 中“Phase 6 落地后打开默认”的兑现。对后续维护者最有价值的是 Phase 4 确立的两条贯穿性纪律“每个关注点只在真正需要的 phase 进入实现”拒绝照抄扩展侧 ~20 层关注点的ClaudeCodeSession以及“先 acquire 新 handle 再 dispose 旧 handle、await 后写回前比对 token、dispose 必须真实”这三条与生命周期安全相关的编码不变式。它们共同保证了一个多 provider agent host 在并排注册、共享认证与共享代理引用计数时不会互相踩踏。延伸阅读roadmap.md阶段总图与执行顺序、各 phase 落点与经验总结phase5-plan.mdsession 生命周期create/dispose/list/shutdown的实现契约smoke.mdlive-system smoke 完整可重复流程与常见失败对照表agentService.tsprovider 注册、设置项 ID、env var 常量与启用解析逻辑copilotAgent.tsIAgent的树内参考实现ClaudeAgent各方法的行为镜像来源claudeAgent.test.ts上述 13 个用例的落地形态【免费下载链接】vscodeVisual Studio Code项目地址: https://gitcode.com/GitHub_Trending/vscode6/vscode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表