首页
/ Cline SDK 架构深度剖析:分层包设计、运行时流程与自动化编排

Cline SDK 架构深度剖析:分层包设计、运行时流程与自动化编排

2026-09-06 18:10:39作者:余洋婵Anita

本文是 Cline SDK(Cline 本体以 SDK、IDE 扩展、CLI 助手三种形态存在的自主编码智能体)架构源码指南的中文精读。文档主线围绕五个问题展开:各包边界与职责如何划分、依赖方向与分层规则如何约束演进、本地 / Hub 托管 / 远端配置托管三种运行时如何流转、设计接缝(重复出现的扩展模式)在哪里,以及这些架构约束为什么存在。读完本文,你将能够看懂 sdk/packages/ 下的包间依赖关系,理清一次 agent 会话从启动、执行到落盘、上报完成的完整链路,掌握如何正确新增工具、新增运行时特性(hook/扩展)、理解会话持久化与设置变更的归属边界,并了解文件化 / 事件驱动自动化的分层实现。

前置说明:本文所引用文件路径均以当前仓库根目录为起点。ARCHITECTURE.md 原文中的 packages/... 相对路径在仓库中实际对应 sdk/packages/...(例如原文的 packages/core/src/runtime/host.ts 实际位于 host.ts)。

文档定位:架构的“事实来源”而非 API 手册

该文档是 Cline SDK 仓库的架构事实来源(architecture source of truth)。它的目标读者有三类:

  • 跨多个包工作的 SDK 贡献者;
  • 使用 @cline/core 构建集成或宿主应用的开发者;
  • 需要理解运行时与扩展系统的插件作者。

它覆盖的内容包括:包边界与职责、依赖方向与分层规则、运行时流程(本地、Hub 托管、远端配置托管)、设计接缝(反复出现的模式而非一次性集成)、以及架构约束及其存在理由。

同时它明确声明自己不是什么:不是新贡献者的上手指南(参见 README.mdCONTRIBUTING.md)、不是详尽的 API 参考(参见各包 README 与内联 JSDoc)、也不是面向终端用户的指南。理解这一边界,能避免把架构文档当成教程或 API 字典来读。

分层模型:一份单向依赖的运行时栈

工作区整体被组织成一个分层的运行时栈,依赖方向自下而上单向流动:

flowchart LR
  shared["@cline/shared"]
  llms["@cline/llms"]
  agents["@cline/agents"]
  core["@cline/core"]
  apps["Host Apps"]

  llms --> shared
  agents --> llms
  agents --> shared
  core --> agents
  core --> llms
  core --> shared
  apps --> core

即:apps(宿主应用,如 CLI)→ @cline/core@cline/agents@cline/llms@cline/shared。底层包不反向依赖高层包,可选的高层集成可以依赖底层,而底层不得依赖可选的特性包(“One-Way Optional Layers”约束)。

包职责:每个包只做一件事

@cline/shared:可复用的底层契约与基础设施

shared 持有可复用的底层契约,包括共享类型与 schema、路径解析、hook 契约/引擎、扩展注册契约、提示词与解析辅助、存储路径辅助,以及远端配置 schema、受管指令物化(managed instruction materialization)、遥测归一化与 blob 上传原语。其设计铁律是:shared 不得依赖任何更高层的运行时包。

从仓库源码看,这些能力散落在 sdk/packages/shared/srchooks/contracts.ts)、cron/cron-spec-types.ts,让其他包无需引入 YAML 解析器即可消费 cron 类型)、plugin/db/logging/remote-config/ 等目录下。

@cline/llms:模型与供应商运行时

llms 负责模型/供应商相关运行时:供应商设置与配置解析、模型目录与清单(manifests)、共享的 gateway 风格供应商契约、通过内部 gateway 注册表创建 handler、以及基于 AI SDK 的供应商执行代码。设计铁律是:供应商特有行为必须隔离在这里,而不是散落到 core 或各应用层。

@cline/agents:无状态运行时循环

agents 持有无状态的运行时循环:agent 迭代循环、工具编排、运行时事件发射、hook/扩展执行、provider 调用前的 turn 准备(turn preparation)、内存中的团队/运行时原语。设计铁律是:agents 不得拥有持久化存储或宿主生命周期相关职责——会话持久化、供应商设置存储、RPC 生命周期、宿主特定审批、远端配置策略缓存都不该进入 agents

在当前仓库中,这一无状态循环的实现位于 agent-runtime.ts(其下还配有 agent-runtime.provider-form.test.tsagent-runtime.test.ts 两个测试文件,分别覆盖 provider 形态与常规运行时行为)。

@cline/core:有状态编排层

core 是面向应用的编排层,拥有:运行时组合、会话生命周期、存储与持久化、配置监听/加载与 watcher 投影、设置列举与变更编排、默认宿主工具装配、插件发现/加载、默认上下文压缩策略、遥测集成。Hub 能力也归属 core:src/hub/ 下的 Hub server 与调度运行时服务、Hub 发现、分离式 Hub 守护进程(daemon)、@cline/core/hub/daemon-entry 子路径,以及从 @cline/core/hub 导出的宿主侧 Hub 客户端适配器(NodeHubClientHubSessionClientHubUIClientconnectToHub)。

仓库中对这些职责的实体位置如下:

  • Hub 相关模块集中在 hub/,按服务分组:client/(面向宿主的 Hub 客户端与浏览器连接辅助)、daemon/(分离式守护进程启动、入口点与本地运行时 handler 接线)、discovery/(端点默认值、发现记录与工作区属主解析)、server/(WebSocket server 启动、native/浏览器 socket 适配器、server 传输、server 辅助与按命令分发的 handlers/)。
  • 设置归属 coresettings/(其核心是 settings-service.ts)。设置变更必须落在 core 服务与 Hub 命令里,而不是由宿主编写特定文件。宿主应调用 core 的设置门面或 settings.* Hub 命令族,并响应 settings.changed 事件。

运行时流程:三种部署形态与完整链路

本地进程内运行时(Local In-Process Runtime)

本地执行的七步链路如下:

  1. 宿主通过 @cline/core 构造 RuntimeHost
  2. @cline/core 选择 LocalRuntimeHost(实体位于 local-runtime-host.ts,选择逻辑在 host.ts);
  3. 宿主把宽泛的本地配置归一化为 RuntimeSessionConfig 加上 localRuntime 覆盖项,再调用 RuntimeHost.start(...)
  4. @cline/core 依据 localRuntime 准备本地引导产物(bootstrap artifact),再据其构建运行时;
  5. @cline/core@cline/agents 创建 Agent
  6. @cline/agents@cline/llms 的 handler 运行循环;
  7. @cline/core 负责状态、产物与元数据的持久化。

完成遥测锚定在助手的显式完成声明上,而非会话关闭时。每轮 agent turn 结束后,本地运行时会检查 AgentResult.toolCalls,一旦观察到成功的 submit_and_exit(即原版 Cline 的 attempt_completion 在 SDK 中的对应物)就立刻发射 task.completed。此外存在一个统一的 teardown 收口点 emitTaskCompletedOnTeardown(...),用于兜底那些最后一轮干净结束却未被观测到显式完成工具的会话(例如未使用 yolo preset 的非交互运行,或宿主禁用了 submit_and_exit)。这个收口点会从每条会话退出路径被调用——既包括 shutdownSession(...) 也包括 releaseSessionRuntime(...)——从而保证发射不依赖 stop 走了哪条 teardown 分支。每个会话至多发射一次 task.completed

Hub 托管的运行时(Hub-Backed Runtime)

Hub 托管模式完整流程:

  1. 宿主通过 @cline/core 构造 RuntimeHost
  2. @cline/corehost.ts 中选择 HubRuntimeHostRemoteRuntimeHost
  3. 当没有发现兼容的本地 Hub 时,@cline/core 可以拉起一个分离式 Hub 守护进程,再通过发现机制重连;
  4. 宿主可以附加/脱离共享会话而无需停止权威运行时,因此另一个客户端可以在之后继续流式接收或恢复同一会话;
  5. Hub 托管的运行时用 @cline/agents@cline/llms 执行 agent 循环;
  6. @cline/core 的 Hub 服务代理会话、事件、审批、调度以及客户端持有的运行时能力(如 session-local 工具执行器);
  7. Hub 事件转发保持结构化的流式生命周期边界:text/reasoning 增量、最终 text/reasoning 完成、工具 start/update/finish、agent done 事件都会被跨 Hub 传输转换,使宿主 UI 能可靠地关闭 loading/流式状态。run.started 只在目标会话被解析之后发射,并携带发起命令的 requestIdclientId,让多客户端宿主可以关联投递确认;
  8. @cline/core/hub 导出的 Hub 客户端适配器(NodeHubClientHubSessionClientHubUIClientconnectToHub)把 command/reply 与事件流翻译成宿主可用的 API;
  9. Hub 的 session.get 记录同时包含根会话的规范用量与 Hub 自持 RuntimeHost 的显式聚合用量,使附加客户端能自主渲染“仅根会话成本”或“根会话加队友总成本”,而无需重放事件流。

会话状态是“报告”出来的,从不被“编造”。会话初始状态如实反映 start(...) 里是否真的有一个 turn 在运行:带 prompt 的启动(one-shot 或交互式)初始为 running;不带 prompt 的交互式启动在首个 turn 之前保持 idle;恢复的会话则上报其持久化状态。每个 turn 独占自己的 running → idle 转换。在客户端侧,HubRuntimeHost 只在 Hub 会话记录或会话快照确实携带状态时才投影状态事件;仅快照型的 session.updated 事件(异步持久化更新,可能滞后于某 turn 最终的 idle 更新)上报的是快照的真实状态。那些在“忙碌会话”上把关全局操作的宿主(例如桌面的 checkpoint-restore 门禁)依赖这条语义——一个被默认置为 running 却无归属 turn 的会话,会让这类门禁永久阻塞而无人能清除。

命令进度遵循与其他 agent 输出相同的运行时事件边界。Shell 执行器通过 AgentToolContext.emitUpdate 发射结构化的 stdout/stderr 分块;agent 运行时把它们投影为工具的 content_update 事件;Hub 以 tool.updated 发布(保留 session、tool-call 与 tool 标识);Hub 客户端再为宿主事件流重建该 tool 更新。客户端贡献的执行器必须走这条相同路径转发能力进度,而不是开一条宿主专属的旁路通道。内置 shell 执行器会按短间隔合并输出,并在输出进入事件管道前限制每个流的待处理尾部;消费者各自合并并限制其渲染的滚动缓冲区大小。

Proceed-while-running 是一个显式的命令生命周期,与客户端脱离/会话脱离是两个概念。shell 进程只有在已 spawn 并向宿主作用域的命令执行控制器注册后,才宣告自己可脱离。客户端发送携带属主 sessionId(有则带 toolCallId)的 run.proceed_while_running;Hub 委托给权威的 RuntimeHost,后者释放该工具调用关联的每个已注册进程。执行器随即移除自己的 abort 与超时属权,以当前的有界输出加一个临时日志路径来结算该工具调用,并继续把进程输出排入该日志。脱离日志有大小上限,命令退出后保留一段有界的检查窗口,随后其临时目录被删除。每个构造 LocalRuntimeHost 的进程都会启动一次脱离日志对账(reconciliation):回收保留窗口之外的已完成日志、重排保留日志、跟踪活跃脱离命令身份直至其退出——清理不依赖启动该命令的进程里的计时器。因此 Hub 守护进程与直接内嵌者共享同一套脱离/重启生命周期,而不是依赖一个守护进程专属的入口点。活跃命令标记把 PID 与“进程代次启动令牌”配对,防止之后复用该 PID 的不相关进程延长日志生命周期;完成标记区分“仍在运行但可能静默的命令”与“已完成日志”;进程探测区分“进程不存在”与“身份提供方不可用”。瞬时探测失败会保留活跃标记,绝不当作命令完成证据。对账会保留已通告的日志并持续重试,直到提供方能证明原始进程仍存在、其 PID 属于替代进程、或进程已不存在。宿主退出本身不会为仍在运行的命令启动保留窗口:接替宿主继续轮询进程身份,仅在命令结束后才开始保留。当提供方持续不可用时,因“保留可能存活命令的通告路径”优先于“猜测其已退出”,受上限约束的日志可能超出常规保留窗口。仅靠脱离的客户端连接永远不改变进程属权或命令执行。

生成媒体:操作与事件流(Generated Media Operation and Event Flow)

模型模态(modalities)与供应商操作(operations)是两个独立事实。模态描述模型能接受或产出的值类型;显式操作才选择供应商传输。语言模型即便能产出媒体也保持普通 agent 循环,而 operation: "image-generation" 选择 generateImageoperation: "transcription" 选择声明的语音转写传输。录音/实时转写等操作专属的执行变体放在 operationModes,不进通用能力清单。专用操作采用“失败关闭”(fail closed)策略:只有供应商 manifest 与适配器都实现了,才会启用——一个 OpenAI 兼容的聊天端点绝不意味着有图像、音频、转写或视频端点。

生成媒体跨包流动的链路是:

  1. @cline/llms 对供应商输出只校验一次,生成规范的 GeneratedMedia 值。该契约携带稳定 ID、模态、MIME 类型与一个可判别的 base64 / HTTP(S) / artifact 来源。当前产出者发射图像;音频、视频与大型 artifact 支撑文件复用同一契约。
  2. 供应商模型工具是适配器而非裸 AI SDK 工具。适配器负责把其原生结果投影成规范媒体。通用流层合并初步或重复结果、执行每 turn 媒体预算,且只持久化紧凑的活动摘要,而不是在模型工具元数据里复制 base64。
  3. @cline/agents 在 assistant 消息的精确流位置追加媒体事件。该消息是规范的重放与持久化来源;观察性的供应商工具活动只是纯展示元数据。
  4. @cline/core 把实时媒体投影为 content_end(media)。Hub 以 assistant.media 发布并保留同一媒体 ID,客户端按 ID 对实时内容与回填内容去重。
  5. Web 客户端从 @cline/ui 共享 GeneratedMediaContent,用于 image/audio/video/file/不可用来源的渲染。内联字节只通过浏览器持有的短生命周期对象 URL 暴露;远端与 artifact 来源需要客户端持有的受信解析器。CLI 与 ACP 客户端提供适配各自传输的物化或回退输出,而不改动规范消息。

图片编辑推理刻意保持局部性:当专用图像模型接受图像输入且当前用户消息没有显式图像时,只复用紧邻的前一条 assistant 消息上的图像;更早的图像不会跨中间 turn 被隐式附加。

会话历史溯源(provenance)把客户端表面与发起模式分开StartSessionInput.source 标识客户端(vscodedesktopclicore 等);顶层 StartSessionInput.mode 标识会话如何开始(userautomationsubagentteam)。持久化的消息信封会同时记录这两个值,外加客户端版本与子会话谱系。缺失的发起模式默认 user;自动化运行时适配器必须显式传 mode: "automation"

根会话持久化是惰性的。启动运行时只分配会话 ID,并把配置或播种的历史留在内存——不创建数据库行、manifest 或消息产物。首个被接受的用户 turn 才持久化同一 ID 及其产物。因此在用户 turn 之前关闭运行时不会留下空历史条目,持久化代码也绝不会为未知会话分配替代 ID。

工作区引导(bootstrap)由执行该会话的运行时拥有。Hub 客户端跨传输保留省略的 cwdworkspaceRoot,让 Hub 侧执行宿主把会话放到其自己文件系统上的共享聊天工作区 <cline-data-dir>/workspaces/chat(默认 ~/.cline/data/workspaces/chat)。该聊天工作区播种了一个 AGENTS.md 规则文件,指示 agent 把会话当作聊天,仅在用户要求时才创建命名项目文件夹。解析后的路径返回在会话快照里,是客户端侧 manifest 的事实来源;传输客户端不得为远端运行时臆造本地路径。

分离式守护进程启动会对瞬时 ETXTBSY spawn 失败重试,再轮询发现。这覆盖了包管理器在命令重启共享 Hub 前恰好替换 CLI 二进制的场景。

本地 Hub 发现还携带共享守护进程的认证契约。启动时 Hub server 生成一个密码学随机的每进程 auth token,存入属主发现记录,并以仅属主可读的文件权限写入该记录。本地客户端在连接时从发现文件解析 token,而不是把它内嵌到端点 URL。server 在接受 /hub WebSocket 升级或 /shutdown 请求前用常数时间比较校验 token;WebSocket 客户端经 Sec-WebSocket-Protocol 头发送,shutdown 请求用 Authorization: Bearer 头。未认证的本地进程仍可探测公共健康/构建元数据,但无法附加会话、发命令或停止守护进程。

本地 Hub 重新发现被限定在受管共享守护端点(来自发现或 ensure*HubServer(...) 启动路径)。受管本地 Hub 必须同时匹配受支持的线上协议与当前 Hub 构建身份;另一个构建的协议兼容守护进程会在其替代者启动前被退役,保证升级不会继续执行陈旧运行时代码。SDK 构建内嵌运行时源码、包 manifest、构建配置与依赖锁的确定性指纹,因此即便包版本尚未提升,可执行 Hub 代码一变身份就变。构建还内嵌一个构建纪元用于时间排序:指纹不同时,晚于客户端自身构建产生的受管 Hub 会经兼容线上协议被复用而非退役(替换会造成守护进程降级),同时客户端的构建失配 watcher 会提示用户更新并重启。更旧、无法排序或缺失构建元数据的 Hub 仍照旧退役并替换,因此两个并发运行的安装会收敛到最新构建,而不是反复替换彼此的守护进程。显式端点(包括 ws://127.0.0.1:<port>/hub 这类 loopback URL)则是粘性精确目标、仅做协议匹配:重连可以重试同一 socket URL,但命令恢复与启动死锁恢复不得把它们替换成工作区发现到的 Hub,从而保证自定义本地 Hub 与远端 Hub 不会悄悄漂移到别的进程。

交互式 CLI 启动(Interactive CLI Startup)

CLI/TUI 的启动规则体现了“响应式优先”原则:

  1. apps/cli 拥有 OpenTUI 启动,且必须不等分离式 Hub 启动就能渲染首帧
  2. 交互式会话使用 backendMode: "auto":已兼容的 Hub 立即可复用;缺失的 Hub 只在后台预热,TUI 先回退到本地运行时保证响应;
  3. 必须依赖 Hub 的流程(cline hub、schedules、connectors、--zen)仍可走显式 ensure 路径,因为这些命令在继续前必须有一个活的 Hub;
  4. resume 水合延迟到 renderOpenTui() 之后,避免加载历史消息阻塞首次 TUI 绘制;
  5. 未来所有 CLI/TUI 启动工作都应遵守同一条规则:守护进程启动、发现轮询、供应商目录刷新、文件索引与 resume 读取都必须后台化或用户操作门控——除非某命令在产出输出前明确需要其结果。

连接器持久化与恢复(Connector Persistence and Recovery)

  1. @cline/shared/db 拥有底层 SQLite 连接器存储与一次性遗留 JSON 导入;
  2. Dashboard 配置与 CLI 连接状态分开记录。配置编辑只替换 dashboard 持有的连接器与安全 flag(存于重连参数中),保留 CLI 专属的运行时选项,且只刷新那些此前成功启动过的连接器的参数;
  3. @cline/core 拥有连接器自动启动持久化与重连编排。分离式 Hub 守护进程是唯一的启动期重连属主,防止 dashboard 启动与之竞争、重复拉起进程;
  4. 分离式连接器启动只在子进程被创建后持久化。内部分离子进程退出时保留该状态,而一次干净的用户交互式退出则禁用自动启动;
  5. CLI 与 dashboard 宿主经分离进程环境传递其连接器 CLI 启动规范。包属主的守护入口点据此启动连接器重连包装器,无需 import 应用代码;
  6. 分离式 Hub 入口点暴露 hubDaemonReady,只在 WebSocket server 开始监听后才 resolve;就绪信号之后才开始重连尝试,且重连失败保持 best-effort,不会拖垮 Hub。

远端配置托管运行时(Remote-Config Managed Runtime)

  1. 宿主或 core 包装器拉取归一化的 RemoteConfigBundle
  2. @cline/shared/remote-config 在配置启用时缓存该 bundle;
  3. shared remote-config 把受管规则/工作流/技能物化到工作区本地 .cline/<plugin>/ 下;
  4. shared remote-config 从 bundle 派生出通用 OpenTelemetry 配置与会话 blob 上传元数据;
  5. @cline/core 暴露面向应用的集成包装器,把扩展、遥测与会话元数据应用到 StartSessionInput
  6. @cline/core 在本地引导时消费准备好的本地覆盖项。

这条分工的核心是:可复用的 remote-config 行为留在 shared,而会话专属的桥接留在 core。这也呼应了“core 保持通用”的约束——@cline/core 不应组织或供应商专属化,可复用的 remote-config 解析、物化与上传原语一律归 @cline/shared/remote-config

设计接缝:十个反复出现的扩展模式

代码库依赖少量反复出现的接缝(seams)而非一次性集成路径。理解这些接缝,就知道“新能力应该从哪里长出来”。

1. 配置 Watcher

Core 对规则、工作流、技能、agents、hooks、插件均使用基于文件的发现与 watcher。设计含义:新的指令来源通常应先物化成文件,再复用基于 watcher 的加载,而不是发明并行的内存执行路径。在 @cline/core 中,面向配置的发现、解析、监听与斜杠命令投影位于 extensions/config,例如 unified-config-file-watcher.ts

2. 运行时构造器输入(Runtime Builder Inputs)

DefaultRuntimeBuilder 从通用输入组合运行时:工具、hooks、扩展、用户指令 watcher、遥测。设计含义:更上层的集成应优先投喂这些接缝,而不是直接打补丁 agent 内部。本地运行时引导位于 local-runtime-bootstrap.ts,它投喂构造器而不是绕过它。

3. 运行时宿主边界(Runtime Host Boundary)

Core 只暴露一个共享执行边界:RuntimeHost。三个具体实现:LocalRuntimeHost(进程内执行)、HubRuntimeHost(共享本地 Hub 执行)、RemoteRuntimeHost(显式远端 Hub 端点)。设计含义:

  • 宿主选择发生在 runtime/host/host.ts(其抽象契约见 runtime-host.ts);
  • ClineCore 统一委托 RuntimeHost,不在本地 vs Hub 行为间分支;
  • 传输专属翻译属于具体宿主内部,而非顶层编排;
  • RuntimeHost 输入保持传输安全,ClineCore.start(...) 是面向应用的、在委托前归一化宽泛本地配置的门面;
  • RuntimeSessionConfig 在本地、共享 Hub 与远端 Hub 三种模式间传输中立;宿主本地引导关注点留在 localRuntime 下;
  • 客户端本地运行时行为(如 defaultToolExecutors)必须在 Hub 模式存活,因此在会话启动时附加并经 Hub 能力请求代理,而不是改变宿主选择;
  • 挂起 prompt 的 list/update/delete 通过分组的 ClineCore.pendingPrompts 服务暴露;用量摘要查询与活跃会话模型切换同样是服务式能力,在具体传输实现它们时经 ClineCore 暴露。这些服务 API 刻意落在最小 RuntimeHost 原语词汇表之外;
  • session.abort 在本地与 Hub 托管两种执行中都是根会话的取消边界。属主 LocalRuntimeHost 中止主导 agent,只要求该会话的团队运行时取消活跃的同步队友工作及运行中/排队中的异步 run。队友定义与对话状态仍可用于后续 turn;空闲与无关的团队运行时不被停止。一次性的 spawn_agent 委托通过其 SessionRuntime 观察父 turn 的 abort 信号。团队运行时会将会话侧“意图性中止”的任务结束事件标记为已取消,使持久化不会把它们记为失败;
  • 用量服务的 getAccumulatedUsage(sessionId) 返回两个显式桶的摘要:usage 指根/主导 agent,aggregateUsage 指根 agent 加队友/子 agent。本地执行把根用量与队友用量分成两桶再派生聚合,而遥测始终限定在主导/根 agent。

4. 设置变更边界(Settings Mutation Boundary)

Core 通过 settings/ 持有设置快照与变更。Hub 通过 settings.listsettings.toggle 暴露同一条路径。设计含义:

  • 宿主不得直接变更 skill、tool、MCP、provider 等设置文件;
  • 领域专属的持久化辅助(如 skill 的 Markdown frontmatter 写入)保留在属主的设置 provider/服务内部;
  • 成功的 Hub 托管变更返回更新后的设置快照,并以变更的设置类型发布 settings.changed
  • CLI 设置表面可以为启动响应性保留本地快照渲染,但变更流程必须在重新加载 UI 数据前刷新相关 watcher。

5. 会话启动引导(Session Startup Bootstrap)

ClineCore.create(...) 暴露一个通用 prepare(input) hook。设计含义:更高层包可以在会话开始前准备工作区作用域的运行时状态;core 保持对企业专属契约的无知;清理停留在宿主边界而不是 agent 循环内部。

6. 日志(Logging)

跨包日志使用从 @cline/shared 导出的小型注入接口:

  • BasicLogger——必需的 debuglog,可选的 error。宿主把它映射到自家后端(Pino、VS Code OutputChannel 等)。许多运行时选项接收 logger?: BasicLogger;省略时组件跳过日志,或在需要完整对象处使用 noopBasicLogger
  • BasicLogMetadata——可选结构化字段(sessionIdrunIdproviderIdtoolNamedurationMs…)以及 log 上的 severity,用于单方法同时表达信息型与警告型消息(例如 CLI 的 Pino 桥把 severity: "warn" 映射为 Pino 的 warn)。

命名上需要澄清两个容易混淆的对象:

  • CliLoggerAdapter(CLI 侧) 是一个宿主 bundle:持有原始 pino logger(负责文件路径、轮转与 CLI 专属关注点),并对任何消费 SDK 契约的部分暴露 .core: BasicLogger。它不是 ITelemetryAdapter
  • TelemetryLoggerSink@cline/core 侧) 是一个 ITelemetryAdapter:把遥测事件与指标镜像进 BasicLogger。它是遥测 sink,不是宿主日志实现。

agent 与其他调用点把原先的 info/warn 语义都路由到 log(警告在元数据里带 severity: "warn")。错误优先走 error;未实现时退回 log + severity: "error"

设计含义:日志可注入且传输无关,允许 CLI、VS Code、浏览器等宿主环境接入自己的后端;不要硬编码日志调用,改为接收 logger?: BasicLogger 参数。

7. 存储适配器(Storage Adapters)

有状态持久化应隔离在 adapter/service 层之后。文件型、SQLite 型、RPC 型与企业专属持久化应在可能处共享服务逻辑,把后端差异隔离在适配器中。

8. 扩展与 Hook 系统(Extension and Hook System)

可扩展性被刻意拆成两种机制:扩展(extensions)注册运行时贡献hooks 拦截生命周期阶段。设计含义:增量的运行时行为通常应通过这些扩展点进入,而不是写死特化的宿主代码。

9. 上下文压缩(Context Compaction)

上下文压缩由 core 所有:

  • @cline/agents 拥有通用的 turn 准备接缝:先跑常规生命周期 hooks;允许宿主在 provider 调用前投影消息历史或系统提示;当有投影返回时,保持其规范运行时抄本只追加(append-only);
  • @cline/core 拥有压缩策略:为根会话注入 prepare-turn 管道;通过注册表映射选择内置策略;把最新压缩的工作上下文持久化为会话压缩产物;把压缩逻辑挡在底层 agent 消息构造器之外。

设计含义:

  • 压缩是 core 拥有的上下文管道关注点。相关实现位于 extensions/contextcompaction.ts 与 agentic/basic 策略),仓库也配套了 compaction.test.ts 等测试;
  • 规范会话历史以全保真保存在会话消息产物中;压缩状态单独存放在 ${sessionId}.compaction.json
  • resume 时加载规范抄本用于历史/调试,并只在校验了该压缩状态所覆盖规范前缀的哈希后复用最新压缩状态;有效状态通过追加压缩边界之后写入的规范消息来投影;
  • 在此模型之前就已用压缩消息持久化的会话只能 best-effort 处理,因为被省略的原始抄本无法从压缩产物恢复;
  • agents 聚焦于无状态循环与 provider/工具编排;
  • 委托/子 agent 流程应通过 core 会话配置继承压缩行为,而不是走独立的 agent 级压缩 hook 面。

10. Core 内部的扩展分层

packages/core/src/extensions 按关注点拆分:

  • extensions/config:配置加载器、解析器、watcher 与 watcher 投影(如运行时斜杠命令展开);
  • extensions/plugin:运行时插件发现、加载与沙箱化(见 plugin-loader.tsplugin-sandbox.ts);
  • extensions/context:core 拥有的上下文/消息管道关注点,如压缩。

设计含义:不要把配置发现代码混入运行时/插件代码;当某 helper 本质是在投影 watcher 状态时,不要制造薄薄的运行时包装文件。

沙箱化插件子进程是会话局部的,但可惰性重建。Core 在无进行中 RPC 调用 30 分钟后回收沙箱(可通过 PluginSandboxOptions.idleTimeoutMsCLINE_PLUGIN_IDLE_TIMEOUT_MS 配置),下一次插件调用会透明地启动并重新初始化它。挂起请求与拥有它的子代次关联,因此旧进程退出不能拒绝发给其替代者的工作。bootstrap 也会在其父进程 IPC 通道断开时退出;父进程是空闲关闭的唯一权威,避免竞争性 deadline 在父进程正分发新工作时终止子进程。

沙箱生命周期带来的含义:

  • 沙箱进程数随近期活跃的会话伸缩,而不是随 Hub 启动以来见过的每个会话;
  • 驱逐从不打断进行中的插件调用;
  • 进程内插件状态在空闲驱逐后是临时的;持久插件状态属于持久存储;
  • 沙箱绝不能活得比其属主 Hub 进程更久。

架构约束:三条红线

保持 agents 无状态:不要把这些关注点移入 @cline/agents——会话持久化、供应商设置存储、RPC 生命周期、宿主专属审批、远端配置策略缓存。

保持 core 通用:不要使 @cline/core 组织化或供应商专属化。若某能力确实通用且面向应用,就添加通用 core 接缝。可复用的 remote-config 解析、物化与上传原语属于 @cline/shared/remote-config

使用单向可选分层:可选的高层集成可依赖底层;底层不应依赖可选特性包。就 remote-config 而言,shared 持有可复用的 bundle/物化/blob 原语,core 只持有导出给应用的会话导向包装器。

Hub 主导的 Agenda 任务队列

Agenda 任务是面向未来工作的持久化提案,与 cron 规格、既有会话内的排队 prompt、以及 agent 团队任务看板刻意区分。共享且浏览器安全的契约使用 AgendaTaskRecordAgendaTaskRunRecord;编排与持久化留在 @cline/core(仓库实体见 tasks/,含 agenda-task-manager.tsagenda-task-api.ts)。

状态说明:面向 agent 的 tasks 工具的 kind: "todo" 半边与桌面 Agenda UI 在 Agenda UX 重构期间暂时禁用(开关分别在 hub-server-transport.tsAGENDA_TODO_TOOL_ENABLED 与桌面 webview 的 AGENDA_UI_ENABLED)。flag 关闭期间 Hub 也跳过 agenda 规格文件 watcher——没有消费者消费 watcher 驱动的任务事件,task.* 命令按需对规格文件做对账。下面描述的完整后端——manager、存储、task.* Hub 命令与桌面管道——保持全程接线,schedule 种类保持活跃。

权威性与持久化

  • 一个 Hub 进程拥有一个 Agenda 任务管理器。其 AgendaTaskManagerApi 边界是唯一允许改变任务生命周期、审批、run 或会话链接状态的地方。Hub 命令、文件导入与 agent 工具的全部变更都路由经过该边界;
  • 用户可编辑的意图以“Markdown + YAML frontmatter”形式表示:全局任务在 ~/.cline/tasks/*.task.md,工作区任务在 <workspace>/.cline/tasks/*.task.md。状态、revision、审批、会话 ID 等运行字段不允许写进 spec。AgendaTaskSpecFileStore 把路径限制在所选任务目录内,并以原子方式写 spec;
  • SqliteAgendaTaskStore 拥有 tasks.db(由 resolveTasksDbPath() 解析)。SQLite 是任务状态、revision、运行尝试、会话链接与自动化策略的运行事实来源;Markdown 文件是规范的可编辑任务描述,不是追加式队列,也不能替代并发控制;
  • 任务文件解析与对账必须喂给 manager 而不是直接写 SQLite,从而无论变更来自编辑器、桌面客户端、SDK 客户端还是 agent,都只保留一个校验与审计边界;
  • Manager 启动时扫描全局任务 spec、恢复持久化的任务/运行状态、重新附加每个目录仍存在的已知工作区。缺失的历史工作区保留在 SQLite 中而不重建项目目录。select/listing 工作区会注册它,因此即便是其首个手写任务文件也会被发现并监听。文件系统变更同样经 manager 对账,而不是由 watcher 回调直接应用;
  • 原始文件的创建与编辑归属 system:file_reconciler,且改动的意图永远停留在等待人工任务审批。文件编辑从不重新打开已完成、已取消或已过期的任务;终态记录保持终态,并保留其最后已知良好的运行状态。

审批与会话执行

  • 每个新任务从 pending_approval 开始。审批通过 approvedRevision 绑定到精确的任务 revision;影响执行的编辑会使 revision 递增并撤销旧审批。因此已获批的 revision 在被认领执行的那一刻是不可变的;
  • 审批与执行会同步对账支撑的 Markdown 文件以关闭 watcher 去抖窗口。若该规范 spec 缺失、格式错误或与 SQLite revision 语义不匹配,两者都“失败关闭”——陈旧的 last-known-good 意图永远不会被批准或执行;
  • Hub 的 task.createtask.update 载荷省略 actor 字段;命令服务从调用它的 Hub 客户端派生用户 actor。Update、审批、取消与运行请求携带调用者展示的 expectedRevision;具体而言 task.approvetask.canceltask.run 会拒绝缺失或陈旧的 revision;
  • P0 最紧急、P5 最不紧急。expiresAt 是必填的“最晚开始”边界:过期只阻止新 run,不中止已开始的会话;
  • 启动任务会创建 AgendaTaskRunRecord 与一个普通 Hub 会话。run 持有任务/revision/会话关联,而任务保留 currentRunIdlastRunIdlastSessionId 投影供 UI 查询。全局任务保持全局作用域,只在会话启动时解析到 Hub 的共享聊天工作区;
  • Manager 把 Hub 会话的完成、失败与取消关联到链接的 run,并同时更新 run 与任务。启动恢复遵循同一边界,绝不制造第二个 run。已完成的任务不拥有也不删除其会话;链接会话保持为可被用户重新打开的普通会话历史。

Hub、agent 与桌面表面

  • Hub 命令族为 task.createtask.listtask.gettask.updatetask.approvetask.canceltask.runtask.automation.gettask.automation.set;注册的事件为 task.createdtask.updatedtask.deletedtask.run.startedtask.run.completedtask.run.failedtask.automation.updated事件只是失效与生命周期信号;客户端重新读取当前任务或策略投影,而不是从事件流重建权威状态;
  • Hub 托管的 agent 会话收到一个 snake_case 的 tasks 工具,带必填 kind 判别字段。kind: "todo" 在当前会话的工作区(聊天会话则为全局作用域)内创建、更新、列出与读取持久化 Agenda 项,但不能审批、取消或启动 Todo——agent 不能自我授权或终止队列工作;
  • kind: "scheduled" 路由到 HubScheduleService,管理桌面 Routine 视图展示的同一批记录;统一工具不合并两种持久化或生命周期领域。调度操作继承当前会话的工作区、cwd 与模型,对读写都按工作区过滤,且只允许交互式会话变更。一次性调度接受精确的未来 ISO 时间戳;循环调度接受五字段 cron 表达式与可选 IANA 时区;
  • Hub 为 tasks 贡献一条工具条件化的系统提示规则:区分需人工评审的 Todo 工作与自主的调度执行;声明 Todo 的 available_at 不是计时器;对含混的“提醒我”请求要求澄清;除非用户显式要求两种,否则禁止同时创建两类记录;
  • AgendaAutomationPolicy 是用户持有的 Hub 状态。manual 保留按任务评审的门禁;auto_startunattended 是显式 opt-in。manager 的自动化泵强制该策略的并发、链深度与每小时启动护栏。auto_start 等待一个持有工具审批能力的已连接客户端,并启动一个对每个工具都保守要求审批的交互式任务会话;显式 unattended 模式可无头运行并对已启用工具自动审批;
  • 自动化绝不从一次裸任务文件变更推断用户同意。对于 manager 背书的意图,applyToAgentCreated 管辖“agent 最初创建的任务”与“最近一次 manager 背书编辑来自 agent 的任务”;关闭它会让那些 revision 停留待人工审批;
  • Cline Code 把同一 Hub 状态投影为桌面侧边栏的 Agenda 区与欢迎 composer 下方按工作区过滤的 suggestion/reminder 快捷动作。侧边栏支持评审、启动、取消、链接会话导航与自动化开关;它不维护第二个任务存储。

文件化与事件驱动的自动化(ClineCore / CronService

@cline/corecron/ 下内置了一套文件化自动化子系统。运维者可以把循环与一次性任务写作 Markdown 文件(默认位于全局 ~/.cline/cron/),把事件驱动任务写作 events/*.event.md 规格。所有触发种类都经过同一套持久队列与运行时 handlers。ClineCore 暴露 SDK 面向的 cline.automation.* 入口点;CronService 是 core 与 hub 层使用的内部编排器。

九层结构

  1. 规格解析器specs/cron-spec-parser.ts):把 YAML frontmatter + 正文解析为 CronSpec 判别联合(one_off | schedule | event)。类型放在 @cline/sharedcron/cron-spec-types.ts,使其他包无需引入 YAML 解析器即可消费。调度表达式与时区在 spec 可运行前就被校验;
  2. 存储store/sqlite-cron-store.ts):拥有 cron.db(位于 resolveCronDbPath(),默认 .cline/data/db/cron.db)。schema 从 store/cron-schema.ts 引导——会话与 cron 使用各自独立的 DB,使二者的生命周期解耦
  3. 对账器specs/cron-reconciler.ts):扫描配置的 cron 规格目录(默认全局 ~/.cline/cron/,配置时可为工作区作用域),独立解析每个文件并 upsert 规格状态。非法规格以 parse_status='invalid' 记录,保证状态持久而不是被静默丢弃。扫描间消失的文件被标记 removed=1,其排队 run 被取消;
  4. Watcherspecs/cron-watcher.ts):node:fs watch({ recursive: true }),带约 250ms 的每路径去抖。watcher 事件总是触发重新对账——reconciler 永远是事实来源,而不是 watcher 流
  5. 物化器runner/cron-materializer.ts):把文件触发的规格变成排队的 cron_runs。一次性任务:每个 (spec_id, revision) 至多一个 run 记录(含失败 run),从而避免规格被意外重试。调度任务:启动时“补一次逾期,然后前进”,用带时区的 getNextCronTime 推进;
  6. 事件入口events/cron-event-ingress.ts):接收已经归一化的 AutomationEventEnvelope 值,持久化进 cron_event_log,按 event_type 加声明式过滤器匹配启用的事件规格,应用 dedupe/debounce/cooldown 策略,并以 trigger_kind='event' 入队 cron_runs。它从不直接执行 agent。插件可声明 automationEvents 并经 ctx.automation.ingestEvent(...) 提交归一化事件;沙箱插件经 core 插件事件桥转发这些事件;
  7. Runnerrunner/cron-runner.ts):轮询 cron.db、原子认领排队 run、经既有 HubScheduleRuntimeHandlersstartSessionsendSessionstopSession / abortSession)执行、执行期间续签 run 认领、每个 run 写一份 markdown 报告、事务化更新状态。文件规格可约束工具可用性、配置扩展加载(rulesskillsplugins)、触发来源,以及被注入系统提示的 notes 目录。自动化运行时适配器为每个 run 显式持久化 mode: "automation",并把规格定义的触发来源记为会话元数据里的 sessionHistoryOrigin.trigger。事件 run 会在 prompt 中包含归一化触发事件上下文;
  8. 报告reports/cron-report-writer.ts):把 .cline/cron/reports/<run-id>.md 写成带 run frontmatter 及 ## Summary## Usage## Tool Calls(事件 run 另有 ## Trigger Event)的报告;
  9. 服务service/cron-service.ts):编排以上全部。ClineCore.create({ automation }) 拥有 SDK 面向的生命周期并暴露 cline.automation.* 方法;Hub 侧调用方可通过 cron.event.ingest 命令提交归一化事件。

分离式 Hub 守护进程会把自己的工作区根作为 cronOptions 传入,因此常规 CLI/Hub 启动无需宿主显式 opt-in 就会监听 ${workspaceRoot}/.cline/cron/

编程式 Hub 调度cron_specs(source 为 hub-schedule)存储,并走与文件型一次性/循环/事件规格相同的 cron_runs 认领/重排/报告流程。Hub 的 schedule 命令表面只是薄适配层:不存在独立的 schedules 表、schedule 存储或 schedule runner。仓库源码对 sdk/packages/core/src/cron 下 events/reports/runner/schedule/service/specs/store 七个目录的划分与文档描述完全一致(如 cron/service/cron-service.ts)。

代码库导航

按任务给出阅读起点

理解 agent 循环与工具执行:从 agents/src(无状态运行时循环,当前实现见 agent-runtime.ts)开始;再看 extensions/plugin 了解插件发现与沙箱化。

理解会话持久化与状态:从 local-runtime-host.ts(本地会话生命周期)开始,再进入 runtime/orchestration(会话编排,如 session-runtime-orchestrator.tsruntime-builder.ts);设置见 settings

理解 Hub 系统:起点 hub/server(WebSocket server 与 Hub 命令 handlers);客户端见 hub/client;Hub 托管运行时宿主见 hub/runtime-host

新增一个工具:内置工具定义见 extensions/tools;插件工具见 extensions/plugin

理解设置与配置:watcher 系统见 extensions/config;provider 配置见 runtime/config;设置服务见 settings

新增运行时特性(hook/扩展):hook 契约见 shared/src/hooks;插件系统见 extensions/plugin;运行时构造见 services/local-runtime-bootstrap.ts

文件命名约定

  • *.ts——TypeScript 源码;
  • *.test.ts——单元测试(Vitest);
  • *.e2e.test.ts——需要完整集成的端到端测试;
  • examples 里的 *.ts——可运行的示例文件(插件、hooks);
  • apps/examples/ 下的 *.md——文档与基于 Markdown 的规格(cron、events)。

关键类型位置

可发布性约束:发布面与内部面隔离

本仓库同时含有可发布的 SDK 包与内部工作区包。架构后果:内部包不得意外成为可发布 SDK 表面的一部分;发布自动化只能瞄准预期发布的包;内部代码可以与已发布包组合,但已发布包不应硬依赖仅限内部的工作区层——除非你确实打算发布那个集成。

已发布到 npm 的包@cline/shared(共享类型、契约与底层工具)、@cline/llms(供应商集成与模型清单)、@cline/agents(agent 循环与工具编排)、@cline/core(含会话管理、Hub 与配置的主 SDK)。

内部工作区应用(不作为 SDK 包发布)apps/cli(CLI 实现)、apps/webview(VS Code webview)、apps/examples(示例插件与集成)。

结语:从架构到工程决策

回看整份架构,可以提炼出一套一致的决策哲学:状态与策略越高层越靠右(core 拥有会话、设置、压缩与自动化;agents 保持无状态;shared 只提供契约);可扩展性统一从接缝进入(watcher、runtime builder、RuntimeHost、扩展/hook、存储适配器),而不是各处打一次性补丁;Hub 既是共享执行层又是权威属主(认证、会话事实、Agenda 权威、设置门面、唯一启动期重连属主);自动化(Agenda 与 CronService)一律“文件表达意图、SQLite 承载运行事实、命令/事件只作失效信号、reconciler/manager 是唯一写入边界”。当你在 sdk/packages/coresdk/packages/agents 中贡献代码时,先回答三个问题:这个状态该由哪一层拥有?这个变化该从哪个接缝进入?这个入口是否绕过了既有的唯一权威边界?——答案与本文的约束一致时,改动通常就会落在正确的位置上。

登录后查看全文
热门项目推荐
相关项目推荐