从零构建 · 第七套 · 编程 Agent 引擎

从 PRD 到
可嵌入引擎

跟着「用户在聊天框里输入一段提示词,按下回车」这个动作,从零走到尾。17 个功能模块按开发顺序罗列,每个模块讲清楚目的、实现形式、技术栈、功能定义取舍、优劣分析、如何互相配合。第 20 章详解提示词从打包到渲染的完整链路。

17
功能模块
16
章详解
22
总章数
完整
工作流详解

教程定位:这是一份以 Cline(github.com/cline/cline,Apache-2.0,main 分支 2026-08 检出;CLI v3.0.49 / VS Code 扩展 4.1.3 / SDK 包 0.0.69) 为蓝本的从零构建教程。你将扮演一个能胜任产品经理的开发者:从 PRD 出发,把 Cline 拆成可执行的开发计划——每个功能模块按开发顺序罗列,讲清楚目的、实现形式、技术栈、功能定义取舍、优劣分析、如何互相配合。不放过大大小小的技术细节,哪怕是最基础的。 核心叙事:跟着「用户在聊天框里输入一段提示词,按下回车」这个动作,从零走到尾——它如何被打包、如何上传/传给引擎、如何在主循环里反复运转(模型↔工具),直到流式结果返回聊天窗口渲染。这条链就是整个项目的骨架。 阅读建议:先读第 1 章(PRD)和第 2 章(总览与里程碑),然后按顺序推进。第 20 章(完整工作流)是全书的高潮,建议在读完模块 1-9 后精读。


Part 2

第 1 章 PRD:我们要做什么、不做什么

1.1 一句话产品定义

Cline 是一个开源编程 Agent:住在你的 IDE 里(VS Code 侧边栏),也能跑在终端(CLI)、跑在 CI/CD(headless),用任意主流模型(179 个可注册 Provider ID / 4118 个模型),通过 Plan/Act 双模式和 28 个工具,帮你跨文件改代码、跑命令、浏览网页,并且所有有后果的操作都先问你。

1.2 用户与场景

用户 场景 核心诉求
开发者(IDE 用户) 在 VS Code 里让 Agent 改代码 侧边栏可见、diff 可审、checkpoint 可回滚
开发者(终端用户) 命令行下 Agent 快、可脚本化、可 headless
CI/CD 工程师 流水线里跑 Agent --json NDJSON 事件流、退出码、无交互
企业/集成方 把 Agent 嵌进自己产品 SDK(@cline/sdk)、可编程、插件

1.3 功能需求(FR)与优先级

P0(MVP 必须有): - FR-1 多 Provider 聊天(Anthropic/OpenAI/Gemini + 自定义端点) - FR-2 核心 Agent 循环:模型→工具→再模型 - FR-3 基础工具:读文件、写文件(editor/apply_patch)、跑命令、搜索代码库 - FR-4 Plan/Act 双模式 - FR-5 工具审批(有后果操作先问)+ auto-approve - FR-6 会话持久化(重启后能继续) - FR-7 流式输出渲染(思考/文字/工具分开展示)

P1(高优): - FR-8 VS Code 扩展(WebView 界面) - FR-9 CLI(TUI + headless --json) - FR-10 上下文压缩(长会话不爆) - FR-11 checkpoint 回滚 - FR-12 MCP 接入 - FR-13 .clinerules 规则

P2(差异化): - FR-14 多 agent 团队 - FR-15 cron 定时任务 - FR-16 IM 连接器(Slack/Telegram…) - FR-17 hub 协作中心 - FR-18 插件系统

1.4 非功能需求(NFR)

  • NFR-1 缓存友好:系统提示与消息组装尽量少破坏 provider 前缀缓存(省 token)
  • NFR-2 可嵌入:引擎核心(循环)必须无状态,宿主可插拔
  • NFR-3 安全:默认审批、死循环检测、错误上限
  • NFR-4 成本可控:压缩策略、token 估算、模型路由

1.5 明确不做什么(非目标)

  • ❌ 不做云端平台(本地优先,hub 只是本机协作)
  • ❌ 不做记忆库/向量检索(靠压缩 + checkpoint)
  • ❌ 不做 GUI 框架(VS Code WebView 用 React 自己画)
  • ❌ 不做通用任务 Agent(聚焦编程)

1.6 PRD 验收口径

用户输入「帮我把 src/parser.ts 里所有 parseInt(x, 10) 改成 Number(x),并跑一下测试」,期望:Plan 模式先给方案 → 切 Act 模式 → 审批通过后 editor/apply_patch 改文件 → run_commands 跑测试 → 结果显示在聊天窗口 → 可回滚。全流程 < 5 分钟,P99 工具调用 < 30s。


Part 3

第 2 章 总览:架构五层、里程碑、团队

2.1 目标架构(对齐 Cline v3)

flowchart TB
    subgraph APP["宿主应用层"]
        CLI["CLI(OpenTUI + commander)"]
        VSC["VS Code 扩展(WebView React)"]
        HUB["hub daemon(WebSocket 协作中心)"]
    end
    subgraph CORE["core 包 · 有状态编排"]
        CC["ClineCore 门面"]
        LH["RuntimeHost(local/hub/remote)"]
        SR["SessionRuntime 会话编排"]
        MB["MessageBuilder 消息整形"]
        CP["compaction 压缩策略"]
        ST["存储(JSON + SQLite)"]
    end
    subgraph AGENTS["agents 包 · 无状态主循环"]
        AR["AgentRuntime while loop"]
    end
    subgraph LLMS["llms 包 · Provider 层"]
        GATE["gateway + factory"]
        CAT["模型目录(4118 模型)"]
        VENDORS["vendors(AI SDK 适配)"]
    end
    subgraph SHARED["shared 包 · 契约层"]
        PATH["路径/事件/工具类型/token 估算"]
    end
    CLI --> CORE
    VSC --> CORE
    HUB --> CORE
    CC --> LH --> SR
    SR --> MB & CP & ST
    SR --> AR
    AR --> LLMS
    LLMS --> SHARED
    CORE --> SHARED

2.2 里程碑(12 个月,6 人团队)

里程碑 时间 交付
M0 地基 第 1-2 月 monorepo 脚手架 + shared + llms 最小 Provider + 单文件循环 Demo
M1 引擎 第 3-4 月 AgentRuntime + 4 个核心工具 + 审批 + 会话存储
M2 桌面 第 5-6 月 VS Code 扩展 WebView 完整界面
M3 终端 第 7-8 月 CLI(TUI + headless --json)+ 压缩 + checkpoint
M4 生态 第 9-10 月 MCP/插件/hooks + 多 agent 团队 + cron + connectors
M5 打磨 第 11-12 月 hub + 测试加固 + 发布/CI/CD

2.3 团队分工(6 人)

  • TL/架构(1):模块边界、接口冻结、代码评审
  • 引擎工程师(2):agents/core/llms
  • 前端工程师(1):VS Code WebView / CLI TUI
  • 全栈(1):cron/connectors/hub/插件
  • 测试/QA(1):e2e 黄金场景、回归

2.4 开发纪律(三条红线)

  1. 依赖方向单向shared ← llms ← agents ← core ← apps,谁违反谁重构(用 bun workspace + package.json 依赖强制)。
  2. agents 包无状态:不许在 agents 里写持久化、不许碰宿主生命周期——它是纯循环库。
  3. 事件契约冻结AgentRuntimeEvent 枚举先定,改形状必须全链路同步。

Part 4

第 3 章 模块 0:Monorepo 脚手架与包边界

目的

一个仓库装 6 个包 + 2 个应用,依赖单向,构建/测试/发布统一。

实现形式(对齐 Cline 的 package.json)

  • 包管理器:bun(workspaces + bun -F <package> 过滤命令)。Cline 用 bun 1.3.13,node >= 22。取舍:bun 快、内置 test runner、TS 原生;但生态不如 npm 稳。如果你团队不接受 bun,可用 pnpm workspace,概念等价。
  • 根 package.json"workspaces": ["sdk/packages/*", "apps/*", ...],scripts 用 bun -F '@cline/xxx' 定向。
  • 包清单
路径 职责 依赖
@cline/shared sdk/packages/shared 契约、路径、事件、工具类型
@cline/llms sdk/packages/llms Provider 适配、模型目录 shared
@cline/agents sdk/packages/agents 无状态主循环 shared, llms
@cline/core sdk/packages/core 有状态编排 agents, llms, shared
@cline/sdk sdk/packages/sdk 门面(re-export core) core
@cline/ui sdk/packages/ui 共享 UI 组件
cline(CLI) apps/cli 终端应用 core, ui
@cline/code(扩展) apps/vscode VS Code 扩展 core, ui

技术栈明细

  • 语言:TypeScript 5.9(strict)
  • Lint/Format:Biome(Cline 用 2.4.5)
  • 测试:vitest 4(vitest.config.ts per package)
  • 提交:husky + lint-staged
  • 版本:changesets(.changeset/

功能定义取舍

选择 理由 代价
bun 而非 npm/pnpm 快、TS 原生、内置 bundler 团队学习成本
独立包而非单包 强制边界、独立发版、SDK 可裁剪 调试/发布复杂度
Biome 而非 ESLint+Prettier 一个工具干两件事、快 生态插件少

优劣分析

:边界清晰(编译期就能抓到依赖方向违规)、@cline/sdk 天然可发布、测试隔离。 :6 包联动改一处要全量 build;bun workspace 的 bun install 偶尔坑。

如何互相配合

所有上层包 import 下层包,@cline/sdk 是唯一对外出口。先建包骨架(每个包一个空 index.ts + tsconfig),再填内容——边界先于实现。


Part 5

第 4 章 模块 1:事件契约与共享层(shared)

目的

全项目的"宪法":事件枚举、工具类型、路径解析、token 估算、hook 事件。下层包无依赖,上层全依赖它。

实现形式(对齐 @cline/shared,15417 行 / 88 文件)

关键子模块:

  1. storage/(644 行 paths.ts)resolveClineDataDir()~/.cline/data(可用 CLINE_DATA_DIR 覆盖);各子路径 resolveCronDbPath / resolveProviderSettingsPath / resolveGlobalSettingsPath / resolveSessionDataDir
  2. llms/tokens.tsestimateRequestInputTokens —— 3 chars/token 保守估算(不做精确 tokenizer,因为上百个模型的 tokenizer 各不相同;估算只需"够准",用于压缩触发判断)。
  3. llms/tools.tsToolPolicy = {enabled?, autoApprove?}
  4. hooks/events.ts:10 个外部 hook 事件:agent_start / agent_resume / agent_abort / agent_end / agent_error / tool_call / tool_result / prompt_submit / pre_compact / session_shutdown
  5. tools/create.tscreateTool({name, description, inputSchema, execute, timeoutMs, retryable, maxRetries, lifecycle}) 工具工厂(L81)。

功能定义取舍

  • 为什么 token 估算用字符数而非 tokenizer:上百个 provider 各自 tokenizer 不同,精确估算不现实且没必要——压缩触发的阈值容忍 ±20% 误差。
  • 为什么路径集中在 shared:CLI 和 VS Code 扩展要共享同一份数据(exportVSCodeStorageToSharedFiles 就是为了统一),路径必须单一来源。

优劣分析

:契约集中、跨包类型安全、路径/事件不重复实现。 :shared 膨胀风险(什么都往里塞)——需要纪律:只放"跨包共享"的,单包私有的放各包。

如何互相配合

  • 事件枚举被 agents(发)、core(翻译)、apps(消费)三方引用。
  • ToolPolicy 被 core 审批、apps 配置引用。
  • token 估算被 core 压缩触发引用。

验收

import { estimateRequestInputTokens } from "@cline/shared" 在三个包都能用;事件枚举改动会在编译期爆出所有引用方。


Part 6

第 5 章 模块 2:Provider 抽象与模型目录(llms)

目的

让主循环只认"一个能流式吐字的模型接口",不关心背后是 Anthropic 还是 Ollama 还是自建端点。同时给 179 个 provider ID / 4118 个模型一个可查询的目录。

实现形式(对齐 @cline/llms,118772 行)

  1. 依赖 Vercel AI SDKai@7 + @ai-sdk/anthropic/openai/google/google-vertex/amazon-bedrock/mistral/openai-compatible + ai-sdk-ollama)。取舍:自研 provider 层(如 OpenWorker)vs 用 AI SDK——AI SDK 省 80% 适配工作量,代价是受其 API 约束。
  2. 双注册 Factory(factory-registry.ts):registerHandler(fn) / registerAsyncHandler(fn) + createHandler(config) 门面:先查注册表,否则落到 gateway.tscreateGatewayApiHandler(OpenAI 兼容兜底)。
  3. vendors/:每个 provider 的 AI SDK 适配(anthropic/bedrock/google/vertex/mistral/ollama/openai-compatible)。
  4. 模型目录(catalog/catalog.generated.ts,87274 行):由脚本生成scripts/generate-models.ts 从 models.dev catalog-live 抓取),含 4118 模型的 contextWindow/pricing/capabilities。根脚本 bun run build:models = bun -F @cline/llms generate:models
  5. usage 归一化(ai-sdk.ts):GatewayNormalizedUsage(inputTokens/outputTokens/cacheReadTokens/cacheWriteTokens/reasoningTokenCount/totalCost)——所有 provider 都归一成这一个形状,上层只认它。

功能定义取舍

选择 理由 代价
AI SDK 而非自研 快、稳、更新跟得上 API 演进依赖上游
模型目录自动生成 4118 个模型手写不可能 需要联网抓取 + 脚本维护
OpenAI 兼容兜底 任何自建端点都能用 兼容性参差(tool call 格式)

优劣分析

:广度无敌(179 个 provider ID);新增模型只是跑脚本;usage 归一化让成本统计统一。 :generated 文件 8.7 万行进 git(churn 大);AI SDK 抽象偶尔漏底(某 provider 特殊能力要 hack)。

如何互相配合

  • agentscreateHandler(config) 拿 provider,喂给 model.stream()
  • corelocal-provider-service.ts(963 行)管自定义 provider 的 CRUD 与写入 models 文件注册。
  • core 的 compaction 用 GatewayNormalizedUsage 的 inputTokens 判断触发。

验收

一个请求能从 Anthropic、Ollama、OpenAI 兼容端点三家各流式返回内容,usage 归一化一致;换模型只改 config 字段。


Part 7

第 6 章 模块 3:无状态主循环(agents)——AgentRuntime

目的

这是全项目的"心脏":一个可反复重入的 while loop,模型↔工具反复运转,直到完成。它必须无状态(不持有持久化、不感知宿主),这样 CLI/VS Code/hub 都能驱动它。

实现形式(对齐 @cline/agents/agent-runtime.ts,1969 行)

核心 execute()(L641,循环体 L677-789):

flowchart TD
    START["execute()
emit run-started"] --> TURN["while 循环
emit turn-started"] TURN --> GEN["generateAssistantMessageWithOverflowRecovery()
L876 for await model.stream"] GEN --> TOOL{"模型要工具?"} TOOL -->|否| STEER{"steering 打断?"} STEER -->|有| CONSUME["consumePendingUserMessage
iteration>1 时 L968"] --> TURN STEER -->|没有| DONE["finishRun(completed)"] TOOL -->|要| EXEC["executeToolCalls()
L1455 sequential/parallel"] EXEC --> WRITE["写回 tool_result"] --> TURN TOOL -->|terminal 工具 submit_and_exit| FINISH["finishRun(completed)"]

关键机制:

  1. 每次 run 是独立实例:会话层 new AgentRuntime({...}),用 initialMessages 全量转录播种,runtime.run("")(空输入防重复)。多轮 = 反复重入。
  2. 流式消费generateAssistantMessageWithOverflowRecovery 内部 for await (const event of model.stream(request)),把 stream 事件转成 AgentRuntimeEvent 发出。
  3. overflow recovery:provider 拒绝请求(上下文超限)时自动触发恢复路径(切压缩)。
  4. steering 消费:iteration > 1 时检查 pending prompt 队列,有打断就注入。
  5. config 判别联合WithModel | WithProvider——要么传模型实例,要么传 provider 配置。
  6. prepare-turn 投影 seam:core 注入 compaction prepareTurn——agents 保持无状态但允许"每轮前被投影"。

功能定义取舍

选择 理由 代价
无状态类 + 反复重入 宿主可插拔、会话可多开 每次 run 重建开销
事件全外发 引擎不知道谁在听 事件协议要稳定
同步循环 + 外部事件化 逻辑直白、易测试 阻塞性 loop(靠宿主放 worker)

优劣分析

:引擎零耦合、可测(喂假 provider 就能单测循环逻辑)、可嵌入(@cline/sdk 的核心)。 :反复重入 = 每条用户消息都要重建 loop 上下文;run("") 空输入模式对新手难理解。

如何互相配合

  • coreSessionRuntime.executeRunInternal(orchestrator L737)创建 AgentRuntime(L856)并 run("")(L878)。
  • llms 提供 model(createAgentModelFromConfig)。
  • core 注入 prepareTurn(compaction)。

验收

单测:假 provider 依次返回 text→tool_call→tool_result→text,循环恰好走 3 轮结束;interrupt 能在任意点停;steering 能在 iteration 2 注入。


Part 8

第 7 章 模块 4:消息整形器(MessageBuilder)

目的

把"会话消息数组"变成"provider API 请求"——同时做缓存保护、截断、补孤儿、媒体预算。这是省钱(token)和防炸(超限)的关键

实现形式(对齐 core/src/session/services/message-builder.ts,1727 行)

buildForApi(messages)(L166)六步:

  1. reindex(L278):增量索引——tail 引用比对,命中只索引新增,否则全量重建。缓存友好
  2. commitOutdatedRewrites(L336):读过的文件后来被改,旧内容批量替换为 [outdated - see the latest file content];超过 minOutdatedRewriteBytes(64KB)才一次性提交——避免频繁破坏 provider 前缀缓存。
  3. addMissingToolResults(L502):中断场景给孤儿 tool_call 补 is_error: true 伪结果。
  4. 逐 block 截断:assistant 文本 12K(重复工具 markup)/200K(普通)、file 块 50K、tool_result 8K(保留首尾)、嵌套深截断。
  5. applyMediaBudget(L1332):图片按字节预算限流,超限换 IMAGE_OMITTED_PLACEHOLDER
  6. truncateToTotalTextBudget(L1162):全局 6MB 兜底,候选按大小降序截断,tool_use 参数最后才动。

功能定义取舍

  • 为什么"少动中部":provider 前缀缓存(prompt cache)只在消息前缀不变时命中。所有设计(增量、批量重写、截断顺序)都为了保住前缀。
  • 为什么 6MB 兜底:各家 provider 硬上限不同,6MB 是安全的全局下限。

优劣分析

:省 token(缓存命中率高)、防 provider 拒绝(超限兜底)、容错(补孤儿)。 :逻辑复杂(6 步流水线 + 一堆阈值常量)、难调试(环境变量 CLINE_MESSAGE_BUILDER_* 可覆盖)。

如何互相配合

  • core 的 orchestrator prepareProviderMessagesForApi(L1049)先跑插件注册的 messageBuilder 再调 buildForApi。
  • agents 拿到组装好的 messages 发请求。
  • 系统提示由 composeSystemPrompt(L679)单独拼,工具定义单独传,不塞消息数组

验收

长会话测试:10 轮工具调用后,前缀缓存仍命中(可观察 provider 返回的 cacheReadTokens > 0);图片超限被替换占位符;中断产生的孤儿 tool_call 不导致 provider 报错。


Part 9

第 8 章 模块 5:工具系统与审批(28 个工具 + preset)

目的

给模型一套"手",并且所有有后果的操作过审批。工具分两类:内置(9 默认 + spawn_agent + 18 team)与外部(MCP/插件)。

实现形式(对齐 core/src/extensions/tools/

工具目录(runtime.ts):BASE_TOOL_CATALOG(9 条:工具 id/描述/headlessToolNames)→ preset(act/plan/search/minimal/yolo)+ model-tool-routing(per-model 禁用规则)+ disabledToolIdsresolveCoreSelectedToolIds(206-245 行)→ createDefaultTools 按"开关 && 有 executor"实例化。

9 个默认工具(definitions.ts):

工具 工厂行号 干什么
read_files 247 读文件(窗口化)
search_codebase 343 搜索代码库(替代 glob)
run_commands 457 跑终端命令
fetch_web_content 518 抓网页(web 搜索的退化版)
apply_patch 611 aider 风格批量补丁
editor 660 单文件精确编辑
skills 723 技能调用
ask_question 780 向用户提问
submit_and_exit 801 无头结束运行(lifecycle.completesRun)

审批链路(tool-approval.ts,102 行):

模型工具调用 → SessionRuntime 合并 toolPolicies(全局 * + 工具级,orchestrator:117-133)
→ enabled? → beforeTool 钩子(含循环检测)
→ autoApprove? → 是:执行;否:宿主 requestToolApproval
→ requestDesktopToolApproval(L29):写 <sessionId>.request.<id>.json,200ms 轮询 decision 文件,5 分钟超时
→ 执行 → afterTool

preset 分级(presets.ts:137):default | yolo——yolo 对 * 及全部默认工具 autoApprove: true(其余 preset:act/plan/search/minimal 是工具集合预设,不是权限预设)。

功能定义取舍

选择 理由 代价
9 个精简默认工具 少而精,focus 编程 web 搜索/glob 能力退化
editor + apply_patch 双编辑 editor 精确单文件 / apply_patch 批量多文件 两个工具互斥启用,模型要会选
文件轮询审批 IPC 简单可靠、跨进程解耦 5 分钟超时硬编码、无推送
per-model 路由禁用工具 替代 Claude Code 的连续前缀排序,适配上千个模型 路由规则要维护

优劣分析

:工具少、目录扁平、per-model 精确控制;yolo 模式一键全放行(适合 CI)。 :审批是轮询不是推送(延迟最高 200ms + 文件 IO);无 OpenWorker 那种精确 target 级 standing rule。

如何互相配合

  • agentsexecuteToolCalls(L1455)调用 executor。
  • core 的 orchestrator 管 toolPolicies 合并与审批。
  • UI(apps)渲染审批卡片(packages/ui/components/agent-approval-card.tsx)。
  • MCP/插件工具经 createTool 包装进同一目录。

验收

无头模式(--json + 非 TTY)下:未 auto-approve 的命令直接拒绝(不挂起);auto-approve 模式全执行;yolo 全放行。


Part 10

第 9 章 模块 6:安全护栏(loop-detection / mistake-tracker / sandbox)

目的

Agent 会"卡死"(死循环)、会"连错"(连续失败)、会"被利用"(恶意代码)。三道护栏兜底。

实现形式(对齐 core/src/runtime/safety/ + tools/subprocess-sandbox.ts

① 死循环检测(loop-detection.ts,162 行): - toolCallSignature(L50):对 input 做排序键 JSON 序列化(键按序排列,保证语义相同的调用签名一致)。 - checkRepeatedToolCall(L113):连续相同"工具名+签名"次数,soft=3 / hard=5。 - LoopDetectionTracker.inspect(L136)返回 ok/soft/hard;SessionRuntime:soft→对话注入"换种方式"提示;hard→forceAtLimit 喂给 MistakeTracker 并中止(orchestrator:1244-1273)。

② 错误追踪(mistake-tracker.ts,229 行): - 三种原因:api_error / invalid_tool_call / tool_execution_failed。 - record() 累加 consecutiveMistakes;达上限 onLimitReached 决策(默认 stop);continue 清零并附恢复指引;stop 写停止信息 + activeRuntime.abort()。 - 所有 record 经 activeTrackerWork Promise 链串行化(保序)。

③ 子进程沙箱(subprocess-sandbox.ts,346 行): - spawn node/bun 子进程走 IPC:父发 {type:"call"}、子回 {type:"response"|"event"}。 - call 超时 SIGTERM→SIGKILL 强杀(210-274 行)。 - resolveSubprocessRuntimeExecutable(L73)解析 node/bun。 - 用途:插件/浏览器沙箱代码在主进程跑不安全——放子进程隔离崩溃。

功能定义取舍

选择 理由 代价
soft/hard 双阈值 soft 给机会、hard 硬停 阈值(3/5)拍脑袋,需调参
排序键签名 语义相同的调用(键序不同)也识别 JSON 序列化开销
子进程沙箱 崩溃隔离 + 超时强杀 IPC 复杂度

优劣分析

:三道护栏覆盖"卡死/连错/被利用";沙箱强杀机制可靠。 :无 OpenWorker 的 shell 防注入(命令白名单精确 token 匹配);护栏是"事后",不是"事前结构安全"。

如何互相配合

  • 循环检测挂在 beforeTool 钩子(orchestrator 接线)。
  • MistakeTracker 的 stop 调 activeRuntime.abort()——agents 的中断入口。
  • 沙箱服务 插件系统(插件沙箱)与浏览器工具。

验收

e2e:模型反复调同一工具 6 次 → hard 触发中止;连续 3 次 API 错误 → mistake limit 停止;插件崩溃不影响主进程。


Part 11

第 10 章 模块 7:会话编排(SessionRuntime)与存储

目的

"记得住状态"的一层:跨轮状态(消息、usage、mistake、loop 追踪)、持久化、事件翻译。这是 core 包的心脏。

实现形式(对齐 core/src/runtime/orchestration/session-runtime-orchestrator.ts,1429 行)

SessionRuntime(L278)持有:ConversationStoreMistakeTracker(L405)、LoopDetectionTracker(L443)、MessageBuildercontributionRegistryRuntimeEventAdapter(L331)。

executeRun 链路run()(L647)→ executeRun()(L690)→ executeRunWithAuthRetry()(L712,OAuth 失败重试)→ executeRunInternal()(L737): 1. append 用户消息到 ConversationStore(L783) 2. 组装 config(L836) 3. 新建 AgentRuntime(L856)→ runtime.run("")(L878) 4. 完成后 runResult.messages 写回会话(L905)

事件翻译handleRuntimeEvent()(L1061)同步维护工具记录/usage/错误追踪,经 RuntimeEventAdapter.translate()(runtime-event-adapter.ts:183)把 14 种 AgentRuntimeEvent → 9 种 legacy AgentEvent(有状态:usage 增量用差值、tool durationMs)。

存储(对齐 core/src/services/storage/ + session/stores/): - 会话索引:sessions.index.json(单文件全量 JSON + atomicWrite + statusLock CAS) - manifest:sessions/<id>/session.json - 消息:sessions/<id>/<id>.json(整体重写,{version, messages[], system_prompt}) - SQLite:db/sessions.db(sqlite-session-store.ts 292 行)、db/cron.dbdb/connectors.db - 密钥:secrets.json(chmod 0o600) - SessionPersistenceAdapter 抽象,默认文件实现,hub/remote 可换

功能定义取舍

选择 理由 代价
消息 JSON 整体重写 会话要能整体读进内存重建 大会话写盘慢
索引 + manifest + 消息三文件 读索引快、改 manifest 不碰消息 文件数多
新事件→legacy 事件 adapter 重构期兼容旧消费者 翻译层要维护

优劣分析

:跨轮状态集中、事件翻译隔离、存储可插拔(未来换 SQLite 全量)。 :orchestrator 太胖(1429 行单文件);JSON 重写对大会话是性能隐患。

如何互相配合

  • host(LocalRuntimeHost)每会话创建一个 SessionRuntime 并驱动 run。
  • MessageBuilder / compaction / tools 都是 orchestrator 的组成部分。
  • ClineCore(649 行)是门面:create()(L203)建 host,start()(L280)→ host.startSession,其余方法透传 host。

验收

重启进程后 readMessages 能恢复完整会话;并发两条消息不互相覆盖(statusLock);事件翻译后的 legacy 事件能被 UI 正常渲染。


Part 12

第 11 章 模块 8:宿主抽象(RuntimeHost)与 ClineCore 门面

目的

引擎(agents)和会话(core)之上,还需要一层"谁在驱动它"的抽象——CLI 进程内跑、VS Code 进程内跑、hub 跨进程跑。RuntimeHost 就是这层。

实现形式(对齐 core/src/runtime/host/

RuntimeHost 接口(runtime-host.ts:315):startSession / runTurn / abort / stopSession / dispose / subscribe三种实现: - LocalRuntimeHost(local-runtime-host.ts,2396 行):进程内。startSession()(L334)→ startResolvedSession()(L366)构建 manifest/provider config → DefaultRuntimeBuilder.build()(runtime-builder.ts:348)组装工具/team/extensions → createAgentInstance 建 SessionRuntime(L748)→ ActiveSession(L785)入 sessions: MaprunTurn()(L946)按 delivery 直跑或入队;executeTurn()(L1551)→ executeAgentTurn()(L1664,按 session.started 决定 run/continue,含 team 自动续跑循环 L1582);shutdownSession(L1989)状态落盘+清理。事件经 AgentEventBridge(local/agent-event-bridge)转 CoreSessionEvent。 - HubRuntimeHost(hub-runtime-host.ts,2156 行):连本地 hub 服务器,hooks/tools/checkpoint/compaction 按 capability prefix 协商。 - RemoteRuntimeHost:远程。

工厂(host.ts:137):createRuntimeHost(),auto 模式自动发现 hub 并降级 local。

ClineCore 门面(ClineCore.ts,649 行):ClineCore.create()(L203)创建 host + FeatureFlags + cron;start()(L280)→ host.startSessionsend / abort / stop / get / list / delete / update / readMessages / readLiveMessages / restore / compareCheckpoint 全是透传 host。automation / settings / pendingPrompts 是对外 API。

功能定义取舍

选择 理由 代价
三宿主实现 同引擎支持进程内/hub/远程 三种实现要同步维护
auto 模式降级 无 hub 也能跑 隐式行为

优劣分析

:宿主可插拔;hub 模式让 CLI/connectors/cron 跨进程共享会话。 :LocalRuntimeHost 2396 行偏大;三宿主能力差异(capability 协商)要小心。

如何互相配合

  • CLI 默认连 hub(backendMode: hub),forceLocalBackend 进程内。
  • VS Code 进程内直接 LocalRuntimeHost。
  • ClineCore 是所有应用的唯一入口。

验收

同一份会话,在 CLI 里开着、VS Code 里也能看到(共享存储);hub 模式下 CLI 退出后会话仍在跑(zen 模式)。


Part 13

第 12 章 模块 9:CLI——OpenTUI 交互 + headless

目的

终端的门面:交互式聊天(TUI)+ 无交互自动化(headless --json)+ 一票管理命令(auth/config/mcp/schedule/connect/team…)。

实现形式(对齐 apps/cli/,71070 行)

入口(index.ts,94 行):#!/usr/bin/env bun,处理 worker/hub daemon/connector 进程分支、SIGINT 转发到 active-runtime,动态 import("./main")runCli()(main.ts,1197 行)。

框架:commander v14(参数)+ @clack/prompts(wizard)+ OpenTUI@opentui/core + @opentui/react + react-reconciler + react 19)。TUI 是自研 React 终端渲染器(tui/index.tsx 的 renderOpenTuicreateCliRenderercreateRootRoot 组件树)。

命令auth/config/plugin/skill/connect/mcp/doctor/hook/schedule/hub/dashboard/history/team + 默认 chat。根选项(commands/program.ts):--plan/--json/--auto-approve/-c --cwd/-P --provider/-k --key/-z --zen/--config

headless(main.ts ~944 行判定 --json || (!stdin.isTTY && !interactive)):runtime/run-agent.ts(425 行)单发执行;--json 输出 NDJSONemitJsonLine,utils/output.ts):run_start / run_abort_requested / message / error 等,逐行 stdout,CI 可逐步消费。--zen:任务丢给后台 hub daemon,CLI 立即退出。

审批三档(utils/approval.ts):CLINE_TOOL_APPROVAL_MODE=desktop 走 core 桌面审批 IPC;TTY 下 readline [y/N];非 TTY 直接拒绝。

功能定义取舍

选择 理由 代价
自研 OpenTUI 而非 Ink Cline 自己维护、原生终端渲染 生态要自己养
NDJSON 而非单 JSON 流式事件逐行可消费 消费者要逐行 parse
headless 判定 --json || !isTTY 管道即 headless 隐式行为难发现

优劣分析

:一条命令三模式(交互/headless/zen);NDJSON 对 CI 友好;命令面广(14 个)。 :TUI 自研渲染器维护成本;--zen 依赖 hub daemon 在跑。

如何互相配合

  • createCliCore()(session/session.ts)→ ClineCore.create()
  • headless 的 run-agent 直接用 AgentRuntime 级 API。
  • 审批三档复用了 core 的 requestToolApproval 能力。

验收

echo "fix tests" | cline --json --auto-approve | jq 能在 CI 里跑通;cline --zen "task" 立即返回且任务在后台执行。


Part 14

第 13 章 模块 10:VS Code 扩展与 WebView

目的

IDE 的门面:侧边栏聊天、diff 审阅、checkpoint 回滚按钮、审批卡片——全部在一个 WebView 里。

实现形式(对齐 apps/vscode/,127726 行)

入口(extension.ts,753 行):activate(L66):① setupHostProvider(必须最先)→ ② legacy 存储迁移 → ③ exportVSCodeStorageToSharedFiles 统一到 ~/.cline/data(与 CLI/JetBrains 共享)→ ④ initialize 创建 VscodeWebviewProvider → ⑤ 注册 commands。

与 core 连接进程内直接 import——extension.tsSdkController.ts(2110 行)直接 import @cline/corehosts/vscode/hostbridge/(gRPC)只是宿主能力桥(diff/terminal/workspace),不承载 core

WebView:单个 WebviewViewclaude-dev.SidebarProvider)→ 内部 React 应用(webview-ui)。消息渲染:thinking/文字/工具调用分块展示;审批卡片、diff 预览(VscodeEditPreview)、checkpoint 按钮都是 React 组件。通信协议:webview ↔ 扩展宿主 postMessage 往返。

功能定义取舍

选择 理由 代价
进程内直跑 core 简单、无 IPC 复杂度 扩展崩溃拖垮引擎(好在有 sandbox)
单 WebView + React 一个界面全搞定 大型单页 React 应用复杂度
存储统一 ~/.cline/data CLI/扩展/IDE 共享会话 迁移逻辑(legacy → shared)要写

优劣分析

:IDE 原生体验(diff/terminal/workspace 都是 VS Code 能力);存储共享让 CLI 和 IDE 无缝切换。 :扩展代码量大(12.7 万行);webview-ui 未开源(Cline 自己)——你要自己写 React UI。

如何互相配合

  • 扩展通过 SdkController.interactions.handleRequestToolApproval 拦截工具请求 → webview 渲染审批卡 → 用户点击 → 返回决策。
  • diff/terminal/workspace 能力经 hostbridge 暴露给 core 的工具 executor。

验收

在 VS Code 打开项目 → 侧边栏聊天 → 让它改文件 → diff 可见 → 审批通过 → 改完 → 点 checkpoint 回滚到之前状态。


Part 15

第 14 章 模块 11:上下文压缩(basic / agentic)

目的

长会话必然超窗口。压缩策略决定"何时压、怎么压、压完怎么续"。

实现形式(对齐 core/src/extensions/context/compaction.ts,710 行)

触发createContextCompactionPrepareTurn(L256)每次模型请求前估算 requestInputTokens(system+message+tools),trigger = maxInputTokens * 0.9(90% 阈值)。三种 mode:auto / manual(/compact)/ overflow_recovery

basic(basic-compaction.ts,711 行,L452):不调 LLM。typed 用户提示必留;最新回合留"最新消息 + 预算后缀"(对齐 assistant 边界);旧回合留各自最终 assistant 答复(最近 3 条原文);其余丢弃折叠进 <SYSTEM_NOTICE> dropped-work 摘要;冻结上次压缩产物不重复折叠。确定性、零成本

agentic(agentic-compaction.ts,283 行,L97):调 summarize 模型findCutIndexpreserveRecentTokens(20K)切分,旧段序列化发 LLM 生成 "continuation note",替换为 metadata.kind=compaction_summary 的用户消息(userRunSpan=被折叠回合数);已有 summary 只折叠其后新消息(增量)。失败回退 basic。

侧车缓存createCompactionStateAwarePrepareTurn,L643):压缩后把 {compacted messages, source_prefix_hash(sha256), source_message_count, system_prompt}session-compaction.ts 侧车;下一轮 projectSessionCompactionState(L161)校验前缀哈希匹配 → 压缩产物 + 新消息尾部,避免每轮全量重建

功能定义取舍

选择 理由 代价
90% 高阈值 尽量晚压,保信息 临界时可能一次压很多
basic/agentic 双策略 basic 零成本兜底 / agentic 保信息 两套实现
侧车 + 前缀哈希 压缩状态跨轮复用 sha256 计算

优劣分析

:agentic 信息保留好(LLM 摘要);basic 确定性可测;侧车避免重复压缩。 :agentic 每次完整摘要请求成本高;90% 阈值比 Claude Code 的 microcompact(0.5-0.6×)激进,临界抖动可能大。

如何互相配合

  • 通过 prepareTurn seam 注入 AgentRuntime(每轮前检查)。
  • shared 的 token 估算判断触发。
  • 压缩产物经 MessageBuilder 组装进请求。

验收

长会话 e2e:消息逼近 90% 窗口 → 触发 agentic(或 manual 触发 basic)→ 后续轮次正常;重启后侧车哈希命中,不重复压缩。


Part 16

第 15 章 模块 12:checkpoint 与版本化回滚

目的

让用户敢让 Agent 改代码——每个关键点的工作区快照 + 一键回滚

实现形式(对齐 core/src/session/checkpoint-*.ts,直接复用 git)

快照:工作区完整状态存成 git commit/stash。含 untracked 时用三父提交<ref>^3 捕获 untracked 文件)。会话 metadata 记 checkpoint.history: [{ref, createdAt, runCount, kind}]

diff(checkpoint-diff.ts,150 行):compareCheckpointToWorkspace(L142)→ listChangedPaths(L76)用 git diff --name-only -z <ref> + git ls-files --others 找变更文件,逐个 git show <ref>:<path> vs 工作区对比 → CheckpointContentDiff[]。有路径逃逸防护(L44)。

回滚事务(checkpoint-restore.ts,413 行): 1. readSessionCheckpointHistory(L156)解析历史。 2. findCheckpointForRun(L202)找 runCount 前最近 checkpoint。 3. trimMessagesToCheckpoint(L245)用 getUserRunSpan 定位第 runCount 次用户回合,截断消息。 4. beginWorktreeRestoreTransaction(L50):先 git stash push --include-untracked 快照当前工作区,对象转私有 ref refs/cline/restore-transactions/<uuid>——回滚也要可回滚。 5. applyCheckpointToWorktree(L357):stash 用 reset --hard <ref>^1 + stash apply <ref>;普通 commit 直接 reset;仅当含 ^3 父才 clean -fd(避免不可恢复删除)。 6. 以截断消息为 initialMessages 启动新会话

编排SessionVersioningService.restoreCheckpoint(session-versioning-service.ts:134):验证 → 恢复计划 → 事务快照 → apply → 新会话 → commit 事务;失败 rollback + 清理新会话。

功能定义取舍

选择 理由 代价
复用 git 而非自建快照 零成本、diff 免费 依赖项目是 git 仓库
回滚可回滚(事务) 防止误操作毁工作区 复杂度高(私有 ref 管理)
回滚 = 开新会话 消息和历史天然隔离 旧会话留档

优劣分析

:快照免费(git 对象)、diff 精确、回滚安全(事务)。这是 Cline 最优雅的工程之一。 :非 git 项目无法 checkpoint;clean -fd 谨慎逻辑复杂。

如何互相配合

  • LocalRuntimeHost.restoreSession(host L913)接线。
  • 每次 run 结束(或关键节点)自动打 checkpoint(kind 区分 auto/manual)。
  • 消息截断用 userRunSpan(user-run-messages.ts:133)与压缩机制对接。

验收

让 Agent 改坏一个文件 → 点回滚 → 工作区恢复 + 会话从 checkpoint 前的消息继续;回滚过程中中断 → 工作区仍完整(事务 rollback)。


Part 17

第 16 章 模块 13:多 agent 团队

目的

一个 Agent 干不过来的任务,coordinator 拆给 specialist——多 agent 并行协作。

实现形式(对齐 core/src/extensions/tools/team/multi-agent.ts,1852 行)

AgentTeamsRuntime(协调器核心)内存态:members / tasks / missionLog / mailbox(信箱)/ runs(队列+租约)/ outcomes / outcomeFragments

18 个 team 工具(team-tools.ts:916):create_team_task / spawn_teammate / route_to_teammate / team_outcome 等——lead 通过这些工具拆任务、委派、回收结果。

任务状态机pending → in_progress → blocked(blockedBy 依赖链)→ completed。运行队列 maxConcurrentRuns 限流 + 心跳 heartbeat + buildRecoveredRunMessage 中断自动恢复。

持久化exportState()/hydrateState() 全量快照 → session/team/team-session-coordinator.ts(240 行)转发 TeamEvent(onTeamTaskStart/Progress/End)→ 落 sqlite-team-store.ts(537 行)/ file-team-store.tswaitForTeamRunUpdates 把子代理异步结果拼进 lead 下一轮 prompt 继续协调。

功能定义取舍

选择 理由 代价
内存态 + 快照持久化 快、跨会话恢复 大团队内存占用
任务依赖链 blocked 支持 DAG 编排 死锁检测缺失
spawn 就地子 SessionRuntime 复用同一引擎 上下文不隔离(共享会话)

优劣分析

:与 Claude Code 的 Task 子 agent 相比,Cline 是真正的"团队"(lead 持续协调、依赖链、结果回收);状态可恢复。 team_* 工具 18 个占工具空间;协调逻辑复杂(1852 行单文件)。

如何互相配合

  • AgentTeamsRuntimeruntime-builder 组装进 BuiltRuntime。
  • 子代理是 SessionRuntime 实例(复用 模块 7)。
  • spawn_agent 工具是单发委派;team_* 是持续协调。

验收

cline --team-name auth-sprint "实现带测试的用户认证":coordinator 拆 3 个子任务 → 并行委派 → 汇总结果 → 产出最终报告;中断后 --team-name 恢复。


Part 18

第 17 章 模块 14:cron 定时与 connectors

目的

让 Agent 能"自己醒来干活":定时任务(cron)+ 从 IM 平台接进来(connectors)。

实现形式(对齐 core/src/cron/ + apps/cli/src/connectors/

cron(31 个文件): - 规范文件 .mdcron-watcher 监听 → cron-spec-parser 解析 → cron-reconciler 同步。 - sqlite-cron-store.ts(1719 行):独立 {data}/db/cron.db,表 cron_specs / cron_runs / cron_event_logclaim_token + claim_until_at 租约机制防多进程重复执行(和 OpenWorker 的 skip-on-overlap 异曲同工)。 - 触发:one_off / schedule(cron 表达式)/ eventschedule-service.ts(443 行)轮询 + 并发限流。

connectors(IM):core 只管生命周期(active-connectors.ts);实际实现 apps/cli/src/connectors/:slack / discord / telegram / gchat / whatsapp / linear 六平台。每平台独立 adapter 进程 → 注册到 hub → 经 HubRuntimeHost 执行会话。凭据存 db/connectors.db + connectors/ 目录。

功能定义取舍

选择 理由 代价
cron 规范用 .md 文件 人类可读、可版本化 解析器要维护
SQLite 租约防重 多进程安全 租约过期要处理
connector 独立 adapter 进程 崩溃隔离 进程管理

优劣分析

:租约机制扎实;cron 规范文件对用户友好(改 .md 即改调度)。 :六平台 adapter 每个都要维护;IM 权限控制弱于 OpenWorker(无精确 target standing rule)。

如何互相配合

  • cron 触发 → 经 ClineCore 起自动化会话。
  • connectors 经 hub → HubRuntimeHost → SessionRuntime。
  • 都在 ~/.cline/data/db/*.db 共享存储。

验收

cline schedule create "PR summary" --cron "0 9 * * MON-FRI" --prompt ... 周一到周五 9 点跑;两个进程同时跑同一 cron 只有一次执行(租约)。


Part 19

第 18 章 模块 15:插件、MCP、hooks、hub

目的

把引擎变成平台:第三方能加工具(插件)、接外部服务(MCP)、挂生命周期钩子(hooks)、跨进程协作(hub)。

实现形式(对齐 core/src/extensions/ + hub/

插件系统(extensions/plugin/):插件经 SubprocessSandbox 子进程加载,JSON IPC(initialize / executeTool / invokeHook / buildMessages)。插件可贡献:tools、commands、rules、messageBuilders、providers、automationEventTypes、mcpServers、shortcuts、flags。安装来源 npm / git / local / remote(plugin-install.ts,1218 行,官方 slug 校验)。

MCP(extensions/mcp/):MCP manager + OAuth + policies;任何 MCP 工具经 createTool 包装进工具目录。

hooks:外部 hook 事件 10 个(模块 1 定义);运行时钩子 AgentHooksbeforeRun / afterRun / beforeModel / afterModel / beforeTool / afterTool / onEvent。实现:hook-file-hooks.ts(1010 行,读 .hooks/ 配置)+ subprocess-runner.ts(转发到 agent hook 子进程)。插件经 createSandboxRuntimeHooks 注册同名钩子,跨沙箱 RPC。

hub(46 个文件):本地 WebSocket 服务器(hub-websocket-server.ts,协议版本协商),handlers:session/run/capability/approval/connector。HubRuntimeHost(2156 行)连远端 hub 驱动会话,hooks/tools/checkpoint/compaction 按 capability prefix 协商。

auth/account:WorkOS Device Flow + OAuth(auth/cline.ts),本地 OAuth server 端口 48801-48811;account/cline-account-service.ts 用户信息 RPC。

功能定义取舍

选择 理由 代价
插件跑子进程沙箱 安全(崩溃隔离 + 权限) IPC 往返延迟
hub 本地而非云端 数据不出机器 多机协作不可用
10 个外部 hook + 7 个运行时钩子 覆盖面广 两套钩子要同步

优劣分析

:插件贡献面大(9 类能力);hub 让 CLI/connectors/cron 共享;hooks 可用于审计/策略。 :沙箱 IPC 复杂;hub 无云端 = 无远程协作。

如何互相配合

  • 插件工具经 plugin-tools.ts(259 行)列出 → 进工具目录。
  • 插件 messageBuilder 经 orchestrator registerMessageBuilder 参与组装。
  • hub 是 CLI --zen、connectors、cron 的公共后端。

验收

官方插件示例(SDK 注册 createTool)装上后新工具出现在目录;.hooks/ 里的 shell 脚本在 tool_call 时被调用;两个 CLI 进程通过 hub 共享同一会话。


Part 20

第 19 章 模块 16:规则系统与技能(.clinerules / skills)

目的

让项目约定(架构规范、测试要求)自动进系统提示,让技能(可加载能力包)按需加载。

实现形式(对齐 core/src/runtime/safety/rules.ts + extensions/

.clinerules(rules.ts,49 行):UserInstructionConfigWatcher 取快照 → 过滤 disabled → 按名字排序 → 渲染 ## 名称\n指令 → 拼进 system prompt 的 # Rules 段(composeSystemPrompt 里)。

skills:Anthropic 格式的可加载能力。工具 skills(definitions.ts:723)让模型按需加载技能全文——渐进式披露(目录给清单,加载给全文)。

功能定义取舍

选择 理由 代价
.clinerules 自动进提示 项目约定零配置生效 规则多时占 token
技能渐进披露 省 token、聚焦 模型要会"想起来加载"

优劣分析

:与 Claude Code 的 CLAUDE.md 机制同源;技能生态兼容 Anthropic 格式。 :规则没有优先级/冲突解决;技能加载依赖模型自觉。

如何互相配合

  • rules 进 composeSystemPrompt(模块 4)。
  • skills 工具进工具目录(模块 5)。

验收

项目放 .clinerules 后,Agent 生成的代码遵守其中约定;skills 工具能列出并加载技能全文。


Part 21

第 20 章 「用户输入提示词之后」:完整工作流详解

本章是全书的核心。从用户在聊天框输入、按下回车,到流式结果回到窗口——把之前 19 章的模块串成一条完整链路。以 VS Code 扩展(进程内 LocalRuntimeHost) 场景为例。

20.1 完整时序图

sequenceDiagram
    participant U as 用户
    participant WV as WebView(React)
    participant EXT as 扩展宿主(extension.ts)
    participant CC as ClineCore
    participant LH as LocalRuntimeHost
    participant SR as SessionRuntime
    participant MB as MessageBuilder
    participant AR as AgentRuntime
    participant PR as Provider(llms)
    participant TOOL as 工具executor
    participant ST as 存储(SQLite/JSON)

    U->>WV: 输入提示词,回车
    WV->>EXT: postMessage({type:'user_prompt'})
    EXT->>CC: core.send(sessionId, prompt)
    CC->>LH: host.runTurn(sessionId, prompt)
    LH->>SR: sessionRuntime.run(prompt)
    SR->>ST: append 用户消息(持久化)
    SR->>SR: 组装 config(模型/工具/policies)
    SR->>AR: new AgentRuntime(config) → runtime.run("")
    loop 主循环(while)
        AR->>PR: model.stream(request)  ← 这里调用真实模型
        PR-->>AR: 流式事件(thinking/text)
        AR-->>SR: AgentRuntimeEvent
        SR-->>EXT: translate→legacy AgentEvent
        EXT-->>WV: postMessage 渲染 thinking/text
        AR-->>AR: 模型要工具?
        alt 要工具
            AR->>SR: 工具调用请求
            SR->>SR: beforeTool + 审批判断
            alt 需审批
                SR-->>EXT: requestToolApproval
                EXT-->>WV: 审批卡片
                U-->>WV: 批准/拒绝
                WV-->>SR: 决策
            end
            SR->>TOOL: 执行 executor
            TOOL-->>AR: tool_result
            AR->>ST: 记录 usage/审计
        end
    end
    AR-->>SR: runResult.messages
    SR->>ST: 写回会话(持久化)
    SR-->>EXT: TURN_END / COMPLETED
    EXT-->>WV: 渲染最终结果
    WV-->>U: 流式结果在聊天窗口

20.2 每一跳的细节

① 输入打包(WebView → 扩展宿主) - 用户在 React 输入框打字 → onSubmitpostMessage({type: "user_prompt", text}) 给扩展宿主。 - 扩展宿主 SdkController 收到 → 调 core.send(sessionId, text)

② 会话定位(ClineCore → LocalRuntimeHost) - ClineCore.send(ClineCore.ts)→ host.runTurn(sessionId, ...)(LocalRuntimeHost L946)。 - runTurn 按 delivery 决定直跑还是入队(pending prompt 队列,模块 4)。

③ 消息落库 + 组装(SessionRuntime) - SessionRuntime.run(prompt)(orchestrator L647)→ executeRunInternal(L737): - 先 append 用户消息到 ConversationStore(L783)——重启后消息不丢。 - 组装 config(L836):模型(createAgentModelFromConfig)、工具(runtime-builder 的 BuiltRuntime)、toolPolicies。

④ 主循环启动(AgentRuntime) - new AgentRuntime(config)(L856)→ runtime.run("")(L878,空输入因为用户消息已在消息数组里)。 - while loop 开始(agent-runtime.ts:677)。

⑤ 模型调用与流式(AgentRuntime → Provider) - generateAssistantMessageWithOverflowRecovery(L876)→ model.stream(request)(L1220)。 - request = system prompt(composeSystemPrompt)+ messages(MessageBuilder.buildForApi)+ tools(工具定义数组)。 - Provider(llms gateway)流式吐事件 → for await 消费 → 转 AgentRuntimeEvent 外发。

⑥ 事件上屏(SR → EXT → WV) - SessionRuntime.handleRuntimeEvent(L1061)→ RuntimeEventAdapter.translate(L183)→ legacy AgentEvent → AgentEventBridgeCoreSessionEvent → 扩展 postMessage → WebView React 渲染(thinking 折叠块 / 文字流式 / 工具卡片)。

⑦ 工具执行与审批(回到 SR) - 模型要求工具 → executeToolCalls(L1455)→ 每个工具过 beforeTool → 审批判断(模块 5)→ executor 执行 → tool_result 写回消息。 - 桌面审批:requestDesktopToolApproval(tool-approval.ts:29)写请求文件 → 扩展侧 200ms 轮询 decision 文件 → 结果返回。

⑧ 反复运转(loop 继续) - tool_result 作为新 assistant 消息进入消息数组 → 下一轮迭代 → 模型看到结果 → 决定继续还是收尾。 - 每轮前 compaction prepareTurn 检查(90% 阈值,模块 11)。 - loop 结束条件:无 tool-call、submit_and_exit、interrupt、错误上限(模块 6)。

⑨ 结果返回(AR → SR → EXT → WV) - runResult.messages 写回会话(L905)→ 持久化(模块 7 存储)。 - TURN_END 事件上屏 → WebView 渲染最终状态(usage、成本、checkpoint 按钮)。

20.3 "打包上传接收"的精确含义(答疑)

很多人问"提示词是怎么上传的"——Cline 是本地引擎,没有"上传到服务器"这一步。精确流程是:

  1. 打包:提示词 + 系统提示 + 历史消息 + 工具定义 = 一个 model.stream(request) 请求体(内存组装,MessageBuilder 整形)。
  2. 发送:请求发给本地配置的模型端点(Anthropic API / OpenAI / Ollama 本地…)——只有这一步是网络 IO,而且只发"上下文"不发"会话文件"。
  3. 接收:流式事件逐块回来(思考/文字/工具调用)。
  4. 反复:工具结果写回 → 再请求 → 再接收 → 直到完成。
  5. 渲染:每块事件经事件链翻译 → postMessage → React 渲染到聊天窗口。

已有功能模块/接口清单(整个项目依赖的外部件): - 模型端点:Anthropic / OpenAI / Gemini / OpenRouter / Ollama 等(经 @cline/llms + Vercel AI SDK) - 文件系统:读/写/搜索(VS Code workspace API + Node fs + ripgrep 类实现) - 终端:VS Code terminal API / Node child_process(run_commands) - git:checkpoint(复用 git 对象库) - MCP 服务器:第三方(经 MCP manager) - 插件:npm/git 安装的 Cline 插件 - 浏览器(fetch_web_content):HTTP 抓取


Part 22

第 21 章 测试策略与评测

21.1 分层测试

测什么 工具
单测(agents) 循环逻辑(假 provider 喂事件) vitest
单测(core) MessageBuilder 截断/补孤儿、loop-detection、mistake-tracker vitest
集成(host) LocalRuntimeHost 会话生命周期、持久化 vitest + temp dir
e2e 黄金场景(改文件→跑测试→回滚) vitest.e2e(bun test:e2e
协议测试 RuntimeEventAdapter 13→9 映射 vitest

21.2 黄金场景(回归保护)

  1. 改代码闭环:输入 → Plan → Act → 审批 → 改 → 测试 → 完成。
  2. 长会话:50 轮 → 压缩触发 → 继续正常。
  3. 中断:流式中 Stop → 无孤儿 tool_call → 恢复。
  4. 回滚:改坏 → checkpoint 回滚 → 工作区恢复。
  5. headless--json 事件流完整、退出码正确。
  6. 多模型:同一场景在 Anthropic/Ollama/OpenAI 兼容端点都过。

21.3 评测建议(对齐本仓库《Agent评测与基准测试》)

  • SWE-bench Verified:测真实工程能力(Cline 官方做)。
  • Aider Polyglot:测多语言编辑(apply_patch 是重点)。
  • Terminal-Bench:测命令执行。
  • 注意:评测结果高度依赖模型选择——Cline 是"4118 个模型的中立引擎",评测要按模型分档报告。

Part 23

第 22 章 发布、打包与 CI/CD

22.1 发布物

打包方式 分发
@cline/sdk bun build → npm 发布 npm
CLI bun build → bin npm i -g cline / brew
VS Code 扩展 vsce/vsix VS Marketplace
hub daemon 随 CLI

22.2 CI/CD 管线

bun install → bun check(biome lint + typecheck)→ bun test(vitest)
→ build:sdk → build:apps → package(vsix/npm)→ e2e(黄金场景)→ release(changesets)

22.3 发布纪律

  • 版本:changesets 自动版本 + CHANGELOG。
  • 兼容:事件协议向后兼容(adapter 层兜底);SDK 语义化版本。
  • 遥测:可选(PostHog/OTel,telemetryOptOut 开关——Cline 有,你的产品要合规)。

Part 24

附:开发顺序速查

顺序 模块 为什么这个顺序
0 Monorepo 脚手架 边界先于实现
1 shared 契约 全项目依赖它
2 llms Provider 引擎要有模型可调
3 agents 主循环 心脏
4 MessageBuilder 引擎要喂安全消息
5 工具 + 审批 引擎要干活
6 安全护栏 引擎不会闯祸
7 会话编排 + 存储 记得住状态
8 host + ClineCore 宿主可插拔
9 CLI 第一个可用界面
10 VS Code 扩展 第二个界面
11 压缩 长会话可用
12 checkpoint 敢让它改代码
13-15 团队/cron/插件 变成平台
16 规则/技能 打磨
完整工作流(第 20 章) 验收主线

本教程以 cline/cline main 分支 2026-08 实际源码为蓝本(CLI v3.0.49 / VS Code 4.1.3 / SDK 0.0.69),所有模块描述标注 文件:行号 可回源核对。