核心概念地图:一篇讲透全部术语
本章导读 :这是全系列的"字典章"。dsh 的文档和源码里有大量约定俗成的术语------plugin、bundle、profile、patch、seam、inbox、turn、step、surface......它们在
docs/glossary.zh.md里有严格定义,但官方术语表是按字母域组织的,不适合第一次系统学习。本章把这些词汇按"七组"重新组织:框架层、组装层、agent 层、会话层、工具层、能力 seam 层、界面层。读完你不必记住每一条,但需要的是遇到陌生词时知道它属于哪一层、去哪里查。后续每一章都会回查本章。
摘要 :dsh 是一个构建在 Cordis 元框架之上的智能体运行时,其核心思想是"一切皆插件"------没有特权内核。本文按七组术语(框架层、组装层、Agent 层、会话层、工具层、能力 seam 层、界面层)系统梳理了 dsh 的完整架构:启动时由 profile 按序叠放 bundle 并应用 patch 组装出插件树;运行时由 Agent 驱动会话,经 inbox 的next-turn/next-step两档接收输入;会话层以仅追加的 SessionEvent 日志作为唯一真源,模型历史由deriveMessages()现场投影;工具调用经过守卫流水线(pre-execute → ToolGuard → execute → post-execute → result),PTC 模式让模型写程序经run_code调工具。事件分三域:会话事件(持久)、agent/*(观察/拦截)、能力事件(挂策略)。循环分三层:step ⊂ turn ⊂ Round。三十多个ctx.*键按 seam 三角色组织,替换一个提供方即可改变整个产品。
文章目录
- 核心概念地图:一篇讲透全部术语
-
- 学习目标
- 正文
-
- 一张全景图:一次对话流经了谁
- [第一组:框架层 ------ Cordis 给了 dsh 什么](#第一组:框架层 —— Cordis 给了 dsh 什么)
- [第二组:组装层 ------ 一棵插件树是怎么拼出来的](#第二组:组装层 —— 一棵插件树是怎么拼出来的)
- [第三组:Agent 层 ------ 真正在干活的行为者](#第三组:Agent 层 —— 真正在干活的行为者)
- [第四组:会话层 ------ 唯一真源](#第四组:会话层 —— 唯一真源)
- [第五组:工具层 ------ 模型的手](#第五组:工具层 —— 模型的手)
- [第六组:能力 seam 层 ------ 三十多个 ctx 键总表](#第六组:能力 seam 层 —— 三十多个 ctx 键总表)
- [第七组:界面层 ------ 从浏览器看这台机器](#第七组:界面层 —— 从浏览器看这台机器)
- 三大事件域:决定你的代码该写在哪
- [turn / step / Round:三层循环的精确关系](#turn / step / Round:三层循环的精确关系)
- 动手实验
-
- [实验 1:打印你机器上的组装树(约 5 分钟)](#实验 1:打印你机器上的组装树(约 5 分钟))
- [实验 2:术语溯源------从本章回到源码(约 5 分钟)](#实验 2:术语溯源——从本章回到源码(约 5 分钟))
- 常见坑
- 小结
- 术语速查表
- 参考资料
学习目标
- 能照着本章全景图,向别人讲清"一次对话在 dsh 里流经了哪些组件"。
- 能用一句话说清任意一条核心术语(如 bundle、inbox、seam、surface、ToolGuard)。
- 能区分三大事件域(会话事件 / agent 事件 / 能力事件),并为每个域举出两个真实事件名。
- 能说清 turn、step、Round 三层的包含关系,以及 Ralph 循环和 Goal Round 处在哪一层。
- 拿到本章没覆盖的术语,知道去仓库哪个文档、哪个源码文件回查。
正文
一张全景图:一次对话流经了谁
- 先看图。这张图里的每一个名词,本章都会逐条定义,你现在不用全看懂,读完再回来看,应该每个词都认识。
bash
你(人类)
│ 提示词 / 斜杠命令 / 审批点击
▼
┌─ Client(浏览器 Web UI / CLI / 编辑器 ACP / Python SDK)──────────────────
│ Typert Remote 方法 · $events 事件流 · Conversation 会话视图 · Slots 组件
└────────────────────────────┬─────────────────────────────────────────────
│ Connection(RPC + 事件流)
▼
┌─ Host:一棵 Cordis 插件树(没有特权内核)────────────────────────────────
│ (这棵树由 bundle/profile/patch 在启动时组合而成 ------ 第二组组装层)
│
│ ctx.commands ──┐ Agent(agent.ctx 作用域世界)
│ ctx.agents ────┼─ 创建/恢复 ─▶ id · session · inbox · status
│ │ │ send / followup / steer / inject
│ ctx.agentLoop ─┴─ driver ───┘
│ │
│ │ turn/start
│ │ step 循环(一次模型请求 + 它引发的工具执行)
│ │ ├ ctx.systemPrompt 组装提示词片段 + 工具 schema
│ │ ├ ctx.llm 流式请求模型 → assistant/chunk*
│ │ ├ ctx.tools 守卫流水线 → tool/call → tool/result
│ │ │ ├ ctx.fs / ctx.web 文件、搜索抓取
│ │ │ ├ ctx.shell ─▶ ctx.subprocess(可被 ctx.sandbox 包装)
│ │ │ └ ctx.subagents 派生子代理
│ │ └ 每个模型可见的事实都追加进日志
│ ▼
│ ctx.sessions(Session 日志,唯一真源)
│ │ session/event 广播
│ ├─▶ ctx.sessionPersistence(JSONL / SQLite 落盘)
│ └─▶ Client:deriveMessages 投影历史,渲染回复
│ turn/end
└──────────────────────────────────────────────────────────────────────────
图里的词可以分成七组:框架层(Cordis、插件、Context、Service)是地基;组装层(bundle、profile、patch)决定启动时往树上挂什么;agent 层(Agent、inbox、driver)是干活的行为者;会话层(Session、SessionEvent)是唯一真源;工具层(Tool、ToolGuard、PTC)是模型的手;能力 seam 层(三十多个 ctx.* 键)是可替换的能力插座;界面层(Typert、slots、conversation)是浏览器那一侧。下面逐组讲。
- ⚠️ 图是运行时快照:这张图只画"对话进行时"流经的组件,并不承载"启动之前"和"抽象原语"两件事。因此组装层(bundle、profile、patch)发生在启动时、在对话之前,不在这张图里,第二组会给它专门的全景;框架层的抽象词汇 Context、Service,以及工具层的 Tool、ToolGuard、PTC,图上也只以它们的实例出现(ctx.* 键、守卫流水线),不会逐一标名。别因为图里没看到某组就觉得它被跳过了,下面七组都会逐条讲到。
- 先把七组在结构上摆出来,谁垫在谁下面、一次对话又从哪进哪出,先有个空间感------细节全部在后面逐组展开:
#mermaid-svg-SoLv5mv6S4cOQtCx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SoLv5mv6S4cOQtCx .error-icon{fill:#552222;}#mermaid-svg-SoLv5mv6S4cOQtCx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SoLv5mv6S4cOQtCx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SoLv5mv6S4cOQtCx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SoLv5mv6S4cOQtCx .marker.cross{stroke:#333333;}#mermaid-svg-SoLv5mv6S4cOQtCx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SoLv5mv6S4cOQtCx p{margin:0;}#mermaid-svg-SoLv5mv6S4cOQtCx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SoLv5mv6S4cOQtCx .cluster-label text{fill:#333;}#mermaid-svg-SoLv5mv6S4cOQtCx .cluster-label span{color:#333;}#mermaid-svg-SoLv5mv6S4cOQtCx .cluster-label span p{background-color:transparent;}#mermaid-svg-SoLv5mv6S4cOQtCx .label text,#mermaid-svg-SoLv5mv6S4cOQtCx span{fill:#333;color:#333;}#mermaid-svg-SoLv5mv6S4cOQtCx .node rect,#mermaid-svg-SoLv5mv6S4cOQtCx .node circle,#mermaid-svg-SoLv5mv6S4cOQtCx .node ellipse,#mermaid-svg-SoLv5mv6S4cOQtCx .node polygon,#mermaid-svg-SoLv5mv6S4cOQtCx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SoLv5mv6S4cOQtCx .rough-node .label text,#mermaid-svg-SoLv5mv6S4cOQtCx .node .label text,#mermaid-svg-SoLv5mv6S4cOQtCx .image-shape .label,#mermaid-svg-SoLv5mv6S4cOQtCx .icon-shape .label{text-anchor:middle;}#mermaid-svg-SoLv5mv6S4cOQtCx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SoLv5mv6S4cOQtCx .rough-node .label,#mermaid-svg-SoLv5mv6S4cOQtCx .node .label,#mermaid-svg-SoLv5mv6S4cOQtCx .image-shape .label,#mermaid-svg-SoLv5mv6S4cOQtCx .icon-shape .label{text-align:center;}#mermaid-svg-SoLv5mv6S4cOQtCx .node.clickable{cursor:pointer;}#mermaid-svg-SoLv5mv6S4cOQtCx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SoLv5mv6S4cOQtCx .arrowheadPath{fill:#333333;}#mermaid-svg-SoLv5mv6S4cOQtCx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SoLv5mv6S4cOQtCx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SoLv5mv6S4cOQtCx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SoLv5mv6S4cOQtCx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SoLv5mv6S4cOQtCx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SoLv5mv6S4cOQtCx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SoLv5mv6S4cOQtCx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SoLv5mv6S4cOQtCx .cluster text{fill:#333;}#mermaid-svg-SoLv5mv6S4cOQtCx .cluster span{color:#333;}#mermaid-svg-SoLv5mv6S4cOQtCx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SoLv5mv6S4cOQtCx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SoLv5mv6S4cOQtCx rect.text{fill:none;stroke-width:0;}#mermaid-svg-SoLv5mv6S4cOQtCx .icon-shape,#mermaid-svg-SoLv5mv6S4cOQtCx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SoLv5mv6S4cOQtCx .icon-shape p,#mermaid-svg-SoLv5mv6S4cOQtCx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SoLv5mv6S4cOQtCx .icon-shape .label rect,#mermaid-svg-SoLv5mv6S4cOQtCx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SoLv5mv6S4cOQtCx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SoLv5mv6S4cOQtCx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SoLv5mv6S4cOQtCx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-SoLv5mv6S4cOQtCx .root>*{fill:#444441!important;stroke:#B4B2A9!important;color:#D3D1C7!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .root span{fill:#444441!important;stroke:#B4B2A9!important;color:#D3D1C7!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .root tspan{fill:#D3D1C7!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .ui>*{fill:#712B13!important;stroke:#F0997B!important;color:#F5C4B3!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .ui span{fill:#712B13!important;stroke:#F0997B!important;color:#F5C4B3!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .ui tspan{fill:#F5C4B3!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .core>*{fill:#085041!important;stroke:#5DCAA5!important;color:#9FE1CB!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .core span{fill:#085041!important;stroke:#5DCAA5!important;color:#9FE1CB!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .core tspan{fill:#9FE1CB!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .seam>*{fill:#633806!important;stroke:#FAC775!important;color:#FAC775!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .seam span{fill:#633806!important;stroke:#FAC775!important;color:#FAC775!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .seam tspan{fill:#FAC775!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .assembly>*{fill:#0C447C!important;stroke:#85B7EB!important;color:#B5D4F4!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .assembly span{fill:#0C447C!important;stroke:#85B7EB!important;color:#B5D4F4!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .assembly tspan{fill:#B5D4F4!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .foundation>*{fill:#0C447C!important;stroke:#85B7EB!important;color:#B5D4F4!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .foundation span{fill:#0C447C!important;stroke:#85B7EB!important;color:#B5D4F4!important;}#mermaid-svg-SoLv5mv6S4cOQtCx .foundation tspan{fill:#B5D4F4!important;} 你(人类)
⑦ 界面层 · 浏览器那一侧
Typert · slots · conversation
Connection
RPC + 事件流
③ agent 层 · 干活的行为者
Agent · inbox · driver
④ 会话层 · 唯一真源
Session · SessionEvent
⑤ 工具层 · 模型的手
Tool · ToolGuard · PTC
⑥ 能力 seam 层 · 可替换插座
30+ 个 ctx.* 键(llm · fs · shell · sandbox · approval · subagents ...)
② 组装层 · 启动时挂树
bundle · profile · patch / cordis.yml / overlay
① 框架层 · 地基
Cordis · plugin · Context · Service
- 颜色图例:蓝 = 装配 (框架层、组装层------启动时搭起来的结构);绿 = 运行核心 (agent / 会话 / 工具------对话进行时真正在动的三样);橙 = 可替换插座 (能力 seam 层------挂在树上的
ctx.*键);红 = 界面 (浏览器那一侧);灰 = 入口连接(你与 Connection)。 - 虚线关系省略:框架层是①到⑥全部的地基,组装层②把整棵树挂好,⑥的接线板就承载③④⑤三种运行服务,而⑦在对话进行时经 Connection 与③联通。
第一组:框架层 ------ Cordis 给了 dsh 什么
这层是干什么的:dsh 没有自己发明插件系统。它站在 Cordis 这个元框架之上,所有上层词汇(服务、事件、生命周期)的语义都来自这里。理解这层,你就理解了为什么 dsh 敢说"一切皆插件"。
- Cordis --- dsh 底层的元框架:插件向共享上下文贡献服务、类型化事件和可逆的副作用。(
docs/architecture.zh.md第 1 节);系统讲解见docs/cordis-primer.zh.md与docs/cordis-tutorial/。dsh 以 vendored 方式内置它并更名为@deepseek-ai/cordis。例:你写的第一个插件,本质就是"向 Cordis 树挂载"。 - 插件(plugin) --- 产品的最小组成单元。在 dsh 里,模型适配器、工具注册表、会话日志、agent loop(智能体循环)本身全都是插件,因此每一个都可以从配置替换;不存在需要打补丁的特权内核.(
docs/architecture.zh.md第 1 节原话)。例:把bash-local换成bash-sandbox提供方,整个产品的命令执行行为就变了。 - Context(上下文) --- 插件共享的服务容器与事件总线,
ctx是它的惯用变量名。出处:docs/cordis-primer.zh.md。dsh 里每个 Agent 还有一个自己的agent.ctx(见第三组)。例:ctx.tools就是挂在 Context 上的一个服务键。 - Service(服务) --- 挂在 Context 上、以
ctx.<key>为键的可调用对象。声明服务的包是"服务定义方",实现它的包是"提供方"。出处:docs/capability-seams.zh.md总表(该表由scripts/gen-doc-graphs.ts从源码生成)。例:ctx.shell是packages/shell/shell声明的服务。 - inject(注入) --- 插件声明"我依赖哪些服务"的方式,框架据此决定装载顺序,并在被依赖插件卸载时先撤走依赖方。(
docs/cordis-tutorial/)。例:从能力图能直接看到tool-bash直接消费ctx.shell、ctx.approval、ctx.jobs、ctx.shellEnv。 - effect(副作用)与 Fiber --- effect 是插件声明"我贡献了什么"的可逆副作用(注册服务、监听事件),插件卸载时按登记顺序自动撤销;fiber 是承载插件生命周期与初始化异步流的执行单元。出处:
docs/cordis-primer.zh.md;源码见packages/core/agent/src/index.ts中register的 JSDoc------"配置创建的 agent 归 loop fiber 所有"、setFactory返回"精确的 Cordis effect disposer"。例:插件里ctx.effect(() => { 注册服务; return 卸载函数 }),卸载函数在插件移除时被自动调用。 - HMR(hot module reload,热重载) --- 运行中的进程不重启就应用配置变更、替换插件的机制。dsh 里它具体表现为 patch 实时重载:自定义 profile 默认实时重载 patch,随附的
webprofile 开启,而headless、sdk、sdk-minimal、acp只在启动时应用一次(一次性或 stdio 应用替换依赖会破坏生命周期)。(docs/architecture.zh.md"Profile 与组合包"节)。例:改一行cordis.patch.yml保存,运行中的 web 服务随即更新。
⚠️ 常见误区:把 Cordis 理解成"一个普通的依赖注入库"。它对组件有硬约束------副作用必须可逆、组件移除时其效果必须被完全回退(时间维可组合性)、依赖必须结构化声明(空间维可组合性)。dsh 的每个子系统都按这套约束设计,这才是"替换一个提供方就能改变整个产品"能成立的前提。理论出处是设计论文《A Programming Paradigm for Spatiotemporal Composability》。
第二组:组装层 ------ 一棵插件树是怎么拼出来的
这层是干什么的:回答"启动一个 dsh 进程时,到底有哪些插件、按什么顺序、带什么配置被挂进树里"。你以后想替换任何内置能力,都是在这层动手。
- bundle(组合包) --- Cordis 配置项及其挂载代码的分发格式;一个 bundle 在自己
package.json的dsh.bundle字段里指向自己的 patch 文件,因此它插入的内容始终可被其上各层 patch。例:dsh-base就是一个 bundle。 - profile --- 存放在 Harness home 中的具名组装:它列出自己叠放哪些 bundle、安装了哪些树外插件、并保存用户自己的
cordis.patch.yml。随发行版交付五个:web、headless、sdk、sdk-minimal、acp。各 bundle 详见packages/bundle/*/README.zh.md。例:dsh --profile headless启动的是不带服务器的一次性运行器。 - dsh-base ---
web、headless、sdk、acp四个 profile 共享的第一层组合包:模型适配器、工具、持久化、沙箱与审批策略、设置、凭据、遥。例:sdkprofile = dsh-base +dsh-sdk-app(一个 JSON-RPC 服务器),所以 SDK 形态也有完整的工具与审批能力。 - dsh-sdk-minimal --- 刻意保留的例外:一个拥有完整显式 SDK 配置树的独立组合包,不应用 dsh-base,适合想把依赖面压到最小的嵌入场景。例:Python SDK 的极简示例默认选它。
- patch(
cordis.patch.yml) --- 覆盖文件:按 id 定位某个配置条目并替换其整个 config,或插入新条目。各层按顺序应用:profile 列出的每个 bundle → profile 自己的 patch → home 级那份 → 任意--patchoverlay。例:想替换内置某个工具的配置,不改源码,写一份 patch 即可。 - cordis.yml --- Cordis 的声明式配置文件,bundle 与 profile 用它表达插件树;agent preset 本质上也是一份 cordis.yml------挂载到 agent 作用域之下。
docs/capability-seams.zh.md中ctx.agentPresets一行:"把一份 preset cordis.yml 挂载到 agent 作用域之下"。例:你给某个团队写的 agent preset,就是"一个目录 + 一份 cordis.yml"。 - loader(装载器)与应用启动器 --- 把 bundle/profile/patch 解析成运行时插件树的机制。dsh 规定所有受支持的 Node 应用都从
dshCLI 与具名 profile 启动:dsh web是--profile web的刻意别名,其余是dsh --profile headless等。例:Python SDK 的运行时 wheel 打包的就是这个普通dshCLI,客户端默认以dsh --profile sdk启动。 - overlay --- 通过
--patch命令行参数传入的最后一层覆盖,排在 home 级 patch 之后应用。出处:docs/architecture.zh.md。例:临时实验一个树外插件时用--patch指过去,不落盘、不污染 profile。
💡 深挖 :
dsh --profile web --dump-config会打印机器启动时的完整组装树。官方原话是:"它打印出的任何条目,都可以由你自己的 patch 替换。"这条命令是组装层最好的教具。
第三组:Agent 层 ------ 真正在干活的行为者
这层是干什么的 :定义"谁在替你跑这个循环",输入怎么排队,以及你如何在它跑动时观察、拦截、纠偏。权威文档是 docs/subsystems/core.zh.md(agent / agent-loop 两个包的专属页)。
- Agent --- 面向编程的活跃智能体句柄。接口定义在
packages/core/agent/src/types.ts:id(与其会话共享的SessionId)、options(provider/model/reasoningEffort/maxTokens)、session(它驱动的会话)、inbox、status、ctx(agent 作用域上下文),方法有send、cancel、whenIdle、runMaintenance、followup、steer、inject。例:Web UI 里每一次对话背后,都是一个 Agent 在驱动。 - AgentHandle --- 编程式创建/恢复 Agent 时返回的"所有者凭证":
{ agent, dispose() }。dispose 是一种能力(capability):在所有消费方中,只有持有者能拆掉这个 agent。例:进程内 subagent 驱动器创建子 agent 后,就持有它的 handle。 ctx.agents(AgentRegistry) --- 活跃 Agent 注册表:create()(新建会话 + agent)、resume()(从持久会话恢复)、get(id)、list()、roots();真正负责"造"的工厂由循环经setFactory()注册,所以消费方不依赖具体循环包。例:编辑器集成(ACP)经ctx.agents驱动 agent 并从session/event渲染(架构文档"新行为的归属位置"表原话)。- agent loop(智能体循环)/ driver ---
dsh-agent-loop是公开Agent约定的唯一具体实现包:driver 认领一条排队的提示词,在会话日志上开启轮次,经 system-prompt 组装请求前缀并从日志派生历史,经 LLM seam 流式获取响应,经工具注册表分发工具调用,把每个模型可见的事实追加回日志。例:扩展插件依赖dsh-agent而绝不直接依赖dsh-agent-loop------因此循环保持可替换。 - inbox(收件箱) --- agent 以持久投影形式拥有的两条 有序待处理消息列表。投递目标只有两档:
type InboxTarget = 'next-turn' | 'next-step'(packages/core/agent/src/types.ts第 29 行)。认领(claim)时取全部next-step输入,外加轮次边界上的一条next-turn消息。持久变更事件有agent/inbox/inserted、agent/inbox/claimed、agent/inbox/discarded与整体 splice 记录。 - followup(后续轮次) ---
agent.followup(message):排队一个普通后续轮次并唤醒 driver,该消息成为它自己那一轮的唯一普通消息;不返回句柄,其MessageId标识的是持久的 inbox 插入事实。例:你在输入框发出一条新消息、agent 正忙时,走的正是这种排队语义。 - steer(中途引导) ---
agent.steer(message):为最近的步骤提交引导。空闲 driver 会开一个新轮次;运行中的 driver 在下一个 step 边界消费它。被拒绝的步骤会让 steering 留在 inbox 里等下一次唤醒。例:任务跑偏时插话"先别动 tests 目录"。 - inject(注入上下文) ---
agent.inject(message):把模型可见上下文排队到下一次 pre-step,不唤醒 driver;空闲 driver 会留着它,直到 followup 或 steering 把 driver 弄醒。例:agent/session-start钩子里用它种入项目背景。 - AgentStatus 与 cancel --- 生命周期状态只有两个值:
type AgentStatus = 'idle' | 'running';running描述整个 driver 的排空区间,可能横跨连续多个轮次,不代表某个轮次还开着。cancel(cause, options?)的原因有四种:user/parent/hook/disposed(AgentCancelCause);keepInbox选项可保留待办工作。例:Web UI 上的"停止"按钮就是user取消。 agent/pre-step--- 请求推导前唯一的 waterfall(瀑布式)监听链:每个监听器可以拒绝 本步骤(reject)或改写 进入步骤的消息批次(enter+messages,还可设startsRequestSeries开启独立的模型消息序列)。首次领取被拒绝或改写为空时,仍会关闭一个不含步骤的持久轮次------日志会记录这次尝试。例:计划模式(plan mode)的"只规划不动手"就是靠 pre-step 拦截改写实现的。agent/turn-stopping--- 轮次即将关闭时运行的 serial 事件(没有next());监听器想续命就调用agent.steer(...),机器会重读 inbox:有新鲜 steering 就再来一步,没有才关轮。数据说了算,监听器顺序无法改变结果。例:goal 领域在轮次末尾判断是否续跑,走的就是这个点。- scope 与
agent.ctx--- 按 agent 划分的注册单位:一项贡献(工具、提示词段、变量、监听器)要么全局、要么归属恰好一个 scope;只有两层,扁平结构,不向下继承给 subagent。经agent.ctx注册的内容生命周期绑定该 agent,卸载即回滚。同名的带作用域注册会遮蔽(shadowing)全局项------最具体者胜出。例:agent preset 把一份 cordis.yml 挂到agent.ctx之下,让这个 agent 拥有专属工具集。
⚠️ 常见误区 :
agent/status是running≠ "当前轮次还开着"。status 描述的是整个 driver 的排空区间;要知道轮次边界,看持久会话事件turn/start/turn/end,它们是会话事件,不是 agent emit(docs/subsystems/core.zh.md原话:"轮次和步骤边界是持久会话事件,而不是 agent emit")。
第四组:会话层 ------ 唯一真源
这层是干什么的 :定义"事实记在哪里、以什么形状记"。dsh 的一切可回放、可恢复、可审计,都建立在这层之上。权威文档是 docs/subsystems/session.zh.md。
- Session --- 一份类型化
SessionEvent的仅追加日志 (append-only log),模型所见上下文的唯一真源;服务键ctx.sessions(packages/core/session)。模型历史不是单独存储的------它从日志派生。例:fork、恢复、transcript(文本记录)、遥测和持久化全部派生自这条事件流。 - SessionEvent(信封) --- 日志条目的形状:单调递增的
seq、时间time、按type判别的datapayload;surface 变体还带两个条件字段------sourceEventSeqs(本事件引用了哪些较早事件)与surfaceOp。例:一条assistant/message的sourceEventSeqs指向它所聚合的每条assistant/chunk;sourceEventSeqs: []则表示"提供方流已知且完整地为空"。 - 事件溯源(event sourcing) --- 一条运行时不变量:"模型可见即已记录 "------抵达模型请求的一切都必须能从日志重建;因此新增一项模型可见输入,就必须新增一个会话事件(扩展
SessionEventMap并从日志渲染)。例:压缩(compaction)子系统加的正是compaction/start/compaction/summary/compaction/end三个新事件类型,而不是往消息里塞私货。 deriveMessages()--- 从日志投影出模型历史的函数。原始assistant/chunk事件则保证回放和 UI 保真。例:恢复一个旧会话时,发给模型的历史是现场从 JSONL 日志投影出来的,没有第二份存储。- surface 事件 --- 面向"表层"(用户可见表面)的事件变体,携带
surfaceOp并用sourceEventSeqs引用源事件。三大 surface 事件:user/message、assistant/message、tool/result。例:UI 渲染一条完整回复读的是assistant/message,而不是逐个 chunk 拼。 - 十二种核心会话事件 ---
turn/start、turn/end、step/start、step/end、user/message、assistant/chunk、assistant/message、tool/call、tool/result、request/header、request/context、session/end-seed。例:每次成功的模型调用都会写一条request/header;agent/pre-step的 enter 决策设置startsRequestSeries开启独立消息序列时,loop 记录的新 header 原因为series,若封装同时变化则为携带startsSeries: true的change。 session/event与持久化 --- 日志经session/event广播给所有消费方;持久化插件订阅它,并在session/flush检查点或 dispose 时落盘。后端二选一:session-persistence-jsonl或session-persistence-sqlite(ctx.sessionPersistenceseam),两者持久化同一套 SessionEvent 词汇。例:rc.8 版本把存储换成 SQLite 时发生过不兼容变更------所以升级前备份$DSH_HOME。- fork(分叉) ---
ctx.sessions.fork(source, boundary?, childSessionId?):从源会话的某个边界派生一个子会话;创建 agent 时可携带可选的seed回放前缀。例:从第 10 轮分叉出去试另一个方案,原会话一个字都不动。 - lineage(谱系) --- 以数据形式携带的父子关系事实:
parentSession、持久的delegationDepth(委派深度)、运行时的subagentDepth;它从不影响可见性------可见性由 scope 决定。例:子代理会话的元数据里记录着它的父会话与委派深度。 - 品牌化 ID(Branded ID) --- 在包之间传递的 ID(
SessionId、ToolCallId、JobId......)结构上是字符串,但类型层面不可互换------不能把SessionId传给需要ToolCallId的位置。原语Branded<B>在独立纯类型包packages/util/brand/src/index.ts。例:这是全仓通用的两个类型模式之一(另一个是...Map → derived-union声明合并模式,见docs/subsystems/core.zh.md末节)。
💡 深挖 :
SessionEventMap的扩展方式是 TypeScript 声明合并(declaration merging)------插件不改dsh-session的源码就能添加新事件类型。docs/subsystems/core.zh.md给出的五个规范 map:ContentBlockMap、MessageSourceMap、FinishReasonMap(属 dsh-llm)、TurnEndReasonMap、SessionEventMap(属 dsh-session)。这五个 map 是插件作者最常扩展的类型面(第 14 章起会反复用到)。
第五组:工具层 ------ 模型的手
这层是干什么的:定义模型能"做"什么、每一次"做"要过哪些关卡。
- 权威文档:
docs/subsystems/tools.zh.md与docs/tool-execution-pipeline.zh.md;真实工具清单见docs/tool-catalog.zh.md。
- Tool / ToolDefinition --- 每个已注册工具"是什么":一个面向模型的
ToolSchema+ 一个execute函数 + 可选的最终内容回调与 UI 回调。工具作者很少手动构造它------defineToolDSL 会用类型化参数构建。例:dsh-tool-fs注册的read/write/edit/read_image四个工具(docs/tool-catalog.zh.md)。
- 出处:
docs/subsystems/core.zh.mdToolDefinition 节 +docs/subsystems/tools.zh.md。
- 工具注册表(
ctx.tools) --- 作用域化的工具注册表 + 带把关的执行流水线。一次调用依次经过:策略前处理(tools/pre-execute)→ 单调守卫(ToolGuard)→ 环绕分派(tools/execute)→ 策略后处理(tools/post-execute)→ 最终结果观测(tools/result)。
- 出处:
docs/capability-seams.zh.mdctx.tools行 +docs/tool-execution-pipeline.zh.md。
- restriction(限制) ---
tools.restrict为单个 scope 过滤全局工具集合,多个 restriction 取交集组合;被过滤掉的全局工具既不出现在提示词中、也拒绝执行,与不存在的工具无法区分。例:给子代理加 restriction,只留搜索与读取两类工具。
出处:
docs/glossary.zh.mdagent-scope 节。
- approval(审批) ---
ctx.approval(审批 seam):一次性权限决策通过approval/requestwaterfall 事件分派,回答方是监听器(Web UI、ACP 桥接等);没有回答方时以unavailable失败关闭。出处:docs/capability-seams.zh.mdctx.approval行。例:你在 Web UI 看到的"允许执行这条命令?"弹窗,就是一个回答方在应答 waterfall。 - ToolGuard(单调守卫) --- 感知作用域的最终预分派策略,在每次
tools/pre-execute之后求值:type ToolGuard = (execution: Readonly<ToolExecution>) => string | undefined。返回类型故意不含"allow"------undefined表示保留前面的决策,返回 reason 则只能缩减权限,后续监听器无法撤销。例:给某 agent 挂一个 guard,工作区之外的路径一律返回拒绝 reason。
- 出处:
docs/subsystems/tools.zh.md(源码packages/core/tools/src/index.ts)。
- sandbox / sandboxPolicy ---
ctx.sandbox(进程沙箱 seam):消费方交出即将 spawn 的确切 argv,与宿主共享文件系统和内核的后端按每次调用的策略包装该 argv 并报告强制执行情况。ctx.sandboxPolicy统一保存部署默认模式与工作区根目录------bash 与 fs 读的是同一份策略,不会限制到不同的根目录 。例:开启沙箱后,bash 走bash-sandbox、文件变更走fs-sandbox,共享同一模式与根目录(第 26 章专讲)。
- 出处:
docs/capability-seams.zh.md两行。
- PTC(programmatic tool calls,程序化工具调用) --- 工具的三种呈现模式:
export type ToolPresentationMode = 'native' | 'ptc' | 'both'(docs/config-catalog.zh.md)。native(默认)把每个可见工具的 schema 都发给模型;ptc只发保留工具run_code加一段生成的 SDK 提示词,模型写一段程序、经 binding 调用工具,子调用按并发约定调度并重新进入完整且受守卫保护的工具流水线 ;both两者都发。例:工具表很大时,ptc 让模型每轮只看"一个工具 + 一份 SDK 文档"。
- 出处:
docs/tool-catalog.zh.mdrun_code行。
- run_code --- PTC 模式下的保留传输工具:由工具注册表所有、处于可过滤能力层之外(你不能用 restriction 把它过滤掉再指望 PTC 还能用);执行时消费
ctx.codeRuntime;每个桥接子调用记录一对tool/code-dispatch-start+tool/code-dispatch事件,另有tools/ptc-dispatch-logwaterfall 可改写持久事件所存副本。例:模型在一段run_code程序里循环调用 grep 二十次------一次模型请求完成,二十个子调用全走守卫流水线。
- 出处:
docs/tool-catalog.zh.md+docs/subsystems/tools.zh.md。
⚠️ 常见误区 :PTC 不是"绕过安全"的后门。子调用会重入守卫流水线、记录持久事件、受maxParallelSubCalls并发上限约束------以上全部出自docs/tool-catalog.zh.mdrun_code行的原文。改名史:v0.1.2 之前这套机制叫 Code Mode(第 01 章提过更名)。
第六组:能力 seam 层 ------ 三十多个 ctx 键总表
-
这层是干什么的 :这是 dsh 的"能力插座板"。每一项能力(执行命令、读写文件、接模型、派子代理)都被拆成三角色的 seam(接缝):Service Definition (服务定义,拥有自己的
ctx.<key>)+ 一个或多个 Service Provider (提供方实现)+ 一个或多个 Consumer(消费方)。seam 是完整能力,绝不单指其中一个角色。 -
规范范例是
packages/shell:dsh-shell是 Service Definition;dsh-bash-local/dsh-bash-sandbox是提供方;dsh-tool-bash是消费方。角色需要独立演进时通常放在不同包;同一关注点也可以合并进一个包(dsh-user-approval在同一个包里既当 approval seam 的定义又当实现)。
- 出处:
docs/glossary.zh.mdcapability-seam 节 +docs/capability-seams.zh.md。
先看主干五个"非 seam"核心服务(角色列标注为 core),再上总表:
| 服务键 | 一句话定义 | 所在包 | 典型消费方 |
|---|---|---|---|
ctx.sessions |
仅追加的 Session 日志与内存 store,唯一真源 | core/session |
agent-loop、agent、持久化、查询 |
ctx.systemPrompt |
每步骤收集提示词片段与工具 schema 的组装注册表 | core/system-prompt |
agent-loop、各 tool-* |
ctx.tools |
作用域化工具注册表 + 守卫执行流水线 | core/tools |
agent-loop、各 tool-* |
ctx.agents |
活跃 Agent 注册表 + 创建/恢复工厂 | core/agent |
agent-loop、acp、subagent 驱动器 |
ctx.agentLoop |
唯一的具体循环驱动器(角色标注为 bundle) | core/agent-loop |
示例包 agent-spine-demo |
下面是按字母序排列的常用服务总表(角色:seam 表示可替换接缝)。每条的权威出处都是 docs/capability-seams.zh.md 的总表(由脚本从源码生成,含完整提供方/消费方清单);包路径均相对 packages/:
| 服务键 | 一句话定义 | 提供方(实现包) | 典型消费方 |
|---|---|---|---|
ctx.llm |
LLM 适配器注册表:注册提供方,循环与压缩调用提供方无关的流服务 | llm/llm-deepseek、llm/llm-pi-ai、llm-replay |
agent-loop、compaction-basic |
ctx.fs |
文件系统提供方 seam:读写编辑都经它 | fs/fs-local、fs/fs-sandbox、fs/fs-e2b |
tool-fs(配套 fs-observation-policy 事件门禁) |
ctx.shell |
Bash 执行器 seam:面向模型的 shell 工具背后的执行世界 | shell/bash-local、shell/bash-sandbox、shell/pwsh-local |
tool-bash、tool-pwsh、hooks 桥接 |
ctx.subprocess |
子进程 spawn seam:进程坐标、进程树生命周期、kill 升级 | subprocess/subprocess-local、subprocess-e2b |
bash 执行器、PTY、LSP、进程外 subagent 后端 |
ctx.sandbox |
进程沙箱 seam:按每次调用的策略包装即将 spawn 的 argv | sandbox/sandbox-local |
bash-sandbox、terminal-bash |
ctx.sandboxPolicy |
沙箱策略 home:部署默认模式 + 工作区根目录 | sandbox/sandbox-policy |
bash-sandbox、fs-sandbox、terminal-bash |
ctx.approval |
审批 seam:一次性权限决策经 approval/request waterfall 分派 |
interaction/user-approval |
tools、tool-bash、acp |
ctx.codeRuntime |
代码执行 seam:用 Host 提供的绑定运行模型写的程序 | code-runtime/code-runtime-worker-thread |
tools(PTC mode 下消费) |
ctx.compaction |
压缩 seam:上下文压力过大时摘要折叠历史 | compaction/compaction-basic |
---(无面向模型的压缩工具) |
ctx.subagents |
子代理提供方与延续服务:同接口后差异极大的委派传输 | subagent/spawn-in-process、fork-in-process、acp、codex、claude-code、dsh-sdk |
tool-subagent、tool-subagent-control、tool-ralph |
ctx.skills |
Skill(技能)目录注册表:合并各提供方的技能目录 | skill/skill-filesystem、skill-badge |
tool-skill |
ctx.commands |
人类命令注册表:注册直接面向人的命令,不发送给模型 | interaction/commands |
各 UI 适配器与命令插件 |
ctx.goals |
同会话目标领域:从日志折叠带修订版本的目标状态 | goal/goal |
/goal 命令、goal 工具 |
ctx.jobs |
后台任务注册表:登记正在运行的后台工作 | jobs/jobs-local |
tool-bash、tool-terminal、tool-jobs 等 |
ctx.web |
Web 访问提供方注册表:搜索与抓取注册到同一 seam | web/web-search-exa、web-search-perplexity、web-search-deepseek、web-fetch-http |
tool-web |
ctx.spillStore |
溢出存储 seam:保存过大的工具文本,返回定位与取回提示 | spill/spill-local |
spill-policy(tools/post-execute 消费方) |
ctx.terminals |
持久 PTY 会话注册表:精确到 Agent 的会话身份与清理 | terminal/terminal-bash |
tool-terminal |
ctx.lsp |
语言服务器导航 seam:恰好四种标准化操作 | lsp/lsp-stdio |
tool-lsp |
ctx.storage |
非会话存储枢纽:领域数据挂载为类型化持久状态 | storage/storage-json、storage-sqlite |
storage-domain |
ctx.settings |
用户设置 seam:插件注册命名空间 schema,提供方存原始文档 | settings/settings-file |
settings 控制器、LLM 适配器 |
ctx.credentials |
凭据 seam:配置持引用,提供方持实际值,按操作解析 | credentials/credentials-local |
settings 控制器、LLM 适配器 |
ctx.workspaceRegistry |
工作区实体注册表(带 WorkspaceId 品牌类型) | workspace/workspace |
workspace/session 控制器 |
ctx.sessionPersistence |
会话持久化 seam:各后端持久化同一套 SessionEvent 词汇 | session/session-persistence-jsonl、session-persistence-sqlite |
agent-loop、session-query、tool-bash 等 |
ctx.sessionQuery |
会话读取 seam:精确读取、过滤、追踪、搜索 | session-query/session-query-sqlite |
tool-session-query、session-reference |
ctx.typert |
运行时类型注册表:插件注册实时 zod 贡献 | typert/typert-registry |
typert-loader、api-gateway |
ctx.typertGateway |
Typert Host 调用网关:Remote 描述符 ↔ Cordis 服务 | api/api-gateway |
Client 调用 |
ctx.webServer |
HTTP 路由注册:普通 node:http 载体 + 静态回退 | host/host-webserver |
client-connection、client-modules、client-hmr |
ctx.planMode |
计划协作状态:折叠计划/模式状态,注册 /plan |
plan/plan-mode |
UI 与 pre-step 策略 |
ctx.userQuestions |
人类问答 seam:ask() promise 上暂停工具调用等人回答 | interaction/user-questions |
tool-ask-user |
ctx.permissionPresets |
权限预设表:workspace-write / danger-full-access 组合沙箱与审批选项 |
interaction/permission-presets |
写 permission/preset 事件 |
ctx.agentDefaultModel |
默认模型选择:经 settings 分层默认 ModelSelection |
core/agent-default-model |
session 控制器、headless |
ctx.webhookRuntime |
Webhook 规则运行时:分派已认证交付并转为 Workspace Session | webhook/webhook |
webhook-github |
三个精讲条目,帮你把"三角色"落到具体:
ctx.shell是 seam 的教科书 。定义在packages/shell/shell;bash-local与bash-sandbox都实现它;tool-bash只消费它。所以把bash-local换成bash-sandbox,tool-bash一行不用改------这正是docs/architecture.zh.md"seam 正是替换一个提供方就能改变整个产品的原因"这句话的出处场景。ctx.sandbox+ctx.sandboxPolicy是成对工作的。sandbox 是"怎么做"(包装 argv),sandboxPolicy 是"按什么规则做"(模式与根目录)。两者都只被沙箱执行器和提供方读取,所以 bash 与 fs 永远限制到同一个根目录。ctx.approval的回答方是监听器 。审批不是"调用一个函数",而是发一个approval/requestwaterfall 事件等监听器应答;ACP 集成为自己的 agent 提供桥接回答方,没有回答方时以unavailable失败关闭------这是理解第ACP 集成的钥匙。
第七组:界面层 ------ 从浏览器看这台机器
这层是干什么的 :Web Client 是一个由独立加载插件组装而成的浏览器侧 Cordis 应用(docs/subsystems/web-client.zh.md 原话)。它有四个可复用底座:Client Modules、API Gateway、Slots、Conversation。
- Typert --- dsh 的运行时类型体系:
ctx.typert(typert/typert-registry)是运行时类型注册表,插件直接或经dsh-typert-loader注册实时 zod 贡献;构建期则生成 Host/Client 双侧的严格约定。例:Client 调用的参数类型不是手写的,是构建期从 Host 源码生成的。
- 出处:
docs/capability-seams.zh.md+docs/api-gateway.zh.md
- API Gateway(
ctx.typertGateway) --- 把生成的 Remote 描述符与实时 Cordis 服务关联、解析已注册身份、并经共享 Connection RPC 载体提供一元调用的网关(api/api-gateway)。例:浏览器里的一次ctx.remote.goals.create(...)调用,最终落到 Host 上真正的 Cordis 服务方法。
- 出处:
docs/api-gateway.zh.md。
@Remote/@RemoteScope--- 标记对 Client 开放的方法的装饰器:@Remote表示调用根 Host Context 中注册的 Cordis 服务;@RemoteScope(key)表示先解析到一个作用域 Context 再取服务调用。未标记的方法不会进入生成的 Client 类型,也不能通过ctx.remote调用 。例:GoalService类上的@Remote('create')方法。
- 出处:
docs/api-gateway.zh.md。
ctx.remote.<namespace>--- Client 侧的调用入口:直接调用落在ctx.remote.<namespace>,作用域调用落在agentCtx.remote.<namespace>;ctx.remote.$on()接收事件,ctx.remote.$mount()挂载贡献。例:文档原例await ctx.remote.goals.create(agentId, { objective: 'ship it' })。
- 出处:
docs/api-gateway.zh.md。
- client-modules(
ctx.clientModules) --- Client 插件图宿主:通过增量dsh.client扫描组合出__DSH_BOOT__入口图,提供插件组合包,并通知重建/图变更订阅方(client/client-modules)。例:你的插件带了浏览器侧代码,就是经它进入 Client 的。
- 出处:
docs/capability-seams.zh.md。
window.__DSH_BOOT__--- Host 把组合后的WebBootGraph写入这个浏览器全局变量,并在 parser-preloaded script 执行前安装浏览器 module-loader facade;模块系统是一张 lazy CommonJS 表------加载 bundle 只注册 factory,materialize entry 时才同步require运行。例:第 28 章你会在 DevTools 里亲眼看这个启动图对象。
- 出处:
docs/subsystems/web-client.zh.md。
- slots --- Web Client 的 UI 组合系统:
ui-slots提供类型化 registry 与 lifecycle ledger,声明扩展位置、推导组件 props、把 observable 绑定成 React hook、挂载最终组件树。例:插件往设置页加一张卡片、往会话流加一种消息渲染,走的都是 slot。
- 出处:
docs/subsystems/web-client.zh.md+docs/subsystems/slots.zh.md。
- conversation --- 把 Session 历史窗口变成各 target 自有视图的子系统:event registry 把标准会话事件与 Client-only 的
chunkrow/*历史 event 关联成稳定的业务 Context,view registry 再 materialize target snapshot;Chat Assistant、Trajectory Assistant 和 Turn Tail 是解释 packed run 的三个内建 Definition。例:你在界面上看到的"一条完整助手回复",其实是 conversation 把几十条 chunk 事件打包解释后的视图。
- 出处:
docs/subsystems/web-client.zh.md+docs/subsystems/conversation.zh.md。
$events--- API Remotes 的内部逻辑事件流,也是 Connection 的 generation source:开场的readyframe 携带 Host home 并建立 generation。allowlist 内的普通事件交付给根 Client Context,scoped waterfall 事件交付给已解析的 Session Context(经ctx.remote.$on();waterfall 监听器可返回结果、调用next()或拒绝)。例:Host 上的审批 waterfall 事件一路送到浏览器弹窗,走的就是这条通道------"Host Cordis waterfall → API Remotes$events→ Session Context 上的ctx.remote.$on()"。
- 出处:
docs/subsystems/web-client.zh.md。
三大事件域:决定你的代码该写在哪
docs/architecture.zh.md 有一句重要论断:"事件就是扩展点,而选对事件域是大多数改动的第一个决定。"三大事件域各管一件事:
| 事件域 | 是什么 | 用在什么时候 | 真实事件名举例 |
|---|---|---|---|
| 会话事件 | 追加到日志、经 session/event 广播的持久事实 |
当某个事实必须在重新加载后仍然存在 | user/message、tool/result(另有 turn/end、assistant/chunk 等) |
Agent 事件(agent/*) |
携带活跃 Agent 的实时扩展点:inbox、步骤、状态、请求、续跑 |
要观察或拦截进行中的工作 | agent/pre-step(waterfall,可拒绝/改写步骤)、agent/turn-stopping(serial,轮次收口前) |
| 能力事件 | 向某个 seam 附加策略与适配器的事件,无需导入循环 | 给文件系统、工具执行、遥测挂策略 | fs/write-intent(waterfall,写前意图)、tools/pre-execute(waterfall);遥测域如 session-telemetry/record |
两个判别技巧:
- 要不要"重启后还在"? 要,就是会话事件;不要,才是后两个域。把该持久的东西挂在 agent 事件上,进程一重启就没了。
- waterfall 还是 serial? waterfall 事件的监听器必须调用
next()把控制权交给下一个监听器(可以改写 payload 再交出去);serial 事件没有next(),只是依次等待。agent/pre-step、agent/request、llm/stream和三个tools/*事件是 waterfall;agent/turn-stopping是 serial。完整语义(emit/waterfall/parallel/serial/bail 五种分发模式)在第 08 章展开。
turn / step / Round:三层循环的精确关系
- 这三个词定义了 dsh 的"节奏感",出自
docs/glossary.zh.md"循环层级"节,务必按官方定义理解:
bash
Round(外层策略迭代:Goal Round / Ralph Round)
│ 计数器归策略所有,不统计会话中的每个轮次
▼
turn(轮次:一次对已接纳输入的排空过程)
│ 在模型及其工具停止工作或终止策略介入后结束;包含 0..n 个 step
▼
step(步骤:一次模型请求 + 由模型响应引发的工具执行)
- 配合 inbox 的两档 target,边界就很清楚了:一条投到
next-turn的消息要等轮次边界 才被认领;投到next-step的消息在步骤边界 就能被认领。turn/end的原因、工具结果携带concludesTurn提前收轮等细节。
在这三层之上,还有一组容易望文生义的 Ralph 词汇,同样以 docs/glossary.zh.md 为准:
- Ralph 循环(Ralph loop) --- 一次面向不可变目标 的前台全新 agent 工作流运行。它是由工作流和 subagent 原语组合而成的面向模型的工具策略------不是同会话目标、不是 agent loop 模式、不是调度器。
docs/tool-catalog.zh.md里能看到它的模型入口:dsh-tool-ralph提供的ralph工具,每个 Round 启动一个全新的结构化子级。 - Ralph Round --- Ralph 循环中的一个全新子会话。子会话不接收父会话或此前子会话的对话种子;跨 Round 的状态只靠两样东西:共享工作区,和一份有界的 Ralph 交接。
- Ralph 交接(Ralph handoff) --- 从一个仍需继续的 Ralph Round 传给下一个 Round 的规范化、有界结构化报告:状态、摘要、证据、后续步骤、阻塞说明。它补充共享工作区,而不取代工作区的权威地位。
- Goal Round(目标轮) --- 与 Ralph 不同层:同会话的目标(
ctx.goals)为当前目标接纳的一次续行周期,具体化为一个由目标触发的轮次,可包含零个或多个步骤。目标有active/paused/blocked/complete四个阶段;"目标激活"(续行权限)是进程本地的armed/disarmed状态,恢复或 fork 后必须重新经人类授权才能自动续跑。
一句话总结这组关系:step 是模型的一口呼吸,turn 是把一件事干完的一段排空,Round 是策略层的一次"再来一轮"------Goal Round 和 Ralph Round 都属于最外层,但前者在同一会话内续行,后者每次都开全新子会话。
动手实验
实验 1:打印你机器上的组装树(约 5 分钟)
- 第 02 章你已经跑起来了 dsh。现在看它的"装配清单":
sh
# 从源码仓库(REPO):
pnpm dsh --profile web --dump-config
# 若你按第 02 章用 npx 安装过:
npx @deepseek-ai/dsh --profile web --dump-config
- 预期输出是一份条目列表:每个条目带 id 和 config,覆盖 bundle 各层(
dsh-base及其上的 web app 层)与你 home 目录里的 patch。对照第二组术语逐段解读:哪些条目来自哪个 bundle?你的cordis.patch.yml改了哪些 id?官方原话是"它打印出的任何条目,都可以由你自己的 patch 替换"------挑一条你认识的(比如某个工具的配置),试着写一份 patch 替换它,再用同一条命令确认变化。
实验 2:术语溯源------从本章回到源码(约 5 分钟)
- 本章每条术语都能回源码验证。挑三条走一遍:
sh
# 1. inbox 的两档 target(第三组):
grep -n "InboxTarget" REPO/packages/core/agent/src/types.ts
# 预期:export type InboxTarget = 'next-turn' | 'next-step'
# 2. PTC 的三种模式(第五组):
grep -n "ToolPresentationMode" REPO/docs/config-catalog.zh.md
# 预期:export type ToolPresentationMode = 'native' | 'ptc' | 'both'
# 3. 单调守卫的类型(第五组):
grep -n "type ToolGuard" REPO/docs/subsystems/tools.zh.md
# 预期:type ToolGuard = (execution: Readonly<ToolExecution>) => string | undefined
- 这就是本系列反复强调的"给出源码路径"的意义:术语不是背下来的,是查出来的。
常见坑
| 症状 | 原因 | 解法 |
|---|---|---|
| 以为"Agent 和 Session 是一回事" | 一个活跃 Agent 与其会话共享同一个 SessionId,但 Agent 是运行中的行为者,Session 是日志 |
记住:Agent 会 dispose,日志永远在;resume 是"同一日志上再挂一个新 Agent 实例" |
| 想在插件里"修改历史消息" | 事件溯源:日志仅追加,历史不可改 | 改模型看到的内容用 agent/pre-step 改写已领取的批次;改不了已经发生的日志 |
把 ctx.shell 当成"启动进程的原语" |
shell 是面向模型的 Bash 执行器 seam;真正 spawn 进程的是 ctx.subprocess |
本地 shell 后端(bash-local)才通过 ctx.subprocess spawn;glob/grep 工具也直接走 ctx.subprocess,不经过 shell 层 |
waterfall 监听器忘了调用 next() |
waterfall 语义要求显式委托 | 拦截型监听器才不调 next();只想"观察"的监听器必须调,否则链条断掉 |
| 把 agent 事件当成持久事实来记 | agent/* 是实时扩展点,重载后消失 |
需要持久化的事实,扩展 SessionEventMap 走会话事件 |
| 混淆 bundle 与 profile | 两者都是"配置载体",但层级不同 | bundle 是分发格式 ,profile 是具名组装(列 bundle 清单 + 自己的 patch);一个 profile 叠多个 bundle |
以为 sdk-minimal 也有 dsh-base 的全部能力 |
dsh-sdk-minimal 刻意不应用 dsh-base |
需要完整能力用 sdk profile;要最小面用 sdk-minimal,自己显式列出配置树 |
| 分不清 turn 和 step | 名字相近 | step = 一次模型请求 + 它引发的工具执行;turn = 0...n 个 step 的排空过程;Round 是最外层策略迭代 |
小结
- dsh = Cordis 框架(可逆副作用 + 结构化依赖)之上的一棵插件树;没有特权内核,模型适配器、工具注册表、会话日志、agent loop 全是插件。
- 启动 = profile(具名组装)按序叠放 bundle,再依次应用 profile patch、home patch、
--patchoverlay;dsh --profile web --dump-config能看到全部可被 patch 的条目。 - Agent 是活跃行为者(
ctx.agents注册、AgentHandle 归所有者),input 经 inbox 的next-turn/next-step两档进入;followup/steer/inject是三种投递语义。 - Session 是唯一真源:仅追加的 SessionEvent 日志(信封
{type, seq, time, data},surface 变体带sourceEventSeqs/surfaceOp),模型历史由deriveMessages()从日志派生------"模型可见即已记录"。 - 工具调用走守卫流水线(pre-execute → 单调 ToolGuard → execute → post-execute → result);PTC 模式(
'native' | 'ptc' | 'both')让模型写程序经保留工具run_code调工具,子调用重入完整流水线。 - 三十多个
ctx.*键按 seam(定义/提供方/消费方三角色)或 core 服务组织;权威总表在docs/capability-seams.zh.md,替换一个提供方就能改变整个产品。 - 事件分三域:会话事件(持久)、
agent/*(观察/拦截)、能力事件(fs/*、tools/*、telemetry/*,挂策略);waterfall 监听器必须next()。 - 循环三层:step(一次模型请求+工具)⊂ turn(排空过程)⊂ Round(策略迭代,如 Goal Round / Ralph Round);Ralph 循环 = 面向不可变目标的前台全新 agent 工作流,跨 Round 只靠共享工作区 + Ralph 交接。
- 界面层五件套:Typert 类型体系 +
@Remote/ctx.remote.<namespace>调用、client-modules 与window.__DSH_BOOT__启动图、slots 组合 UI、conversation 会话视图、$events事件流。
术语速查表
- 按七组排列。"详见"列的章号对应总目录(02 环境 / 04 使用指南 / 05 模型 / 07-08 Cordis / 09 架构 / 10 会话 / 11 Agent 生命周期 / 12 工具流水线 / 13 LLM 适配层 / 14-19 插件 / 23 源码 / 24 agent-loop / 25 PTC / 26 沙箱 / 27 子代理 / 28 Web / 29 SDK / 30 集成)。
框架层
| 术语 | 一句话 | 详见 |
|---|---|---|
| Cordis | dsh 底层元框架:插件向共享上下文贡献服务、事件与可逆副作用 | 第 07 章 |
| plugin(插件) | 产品的最小组成单元------dsh 里一切皆插件,无特权内核 | 第 07、09 章 |
| Context | 插件共享的服务容器与事件总线(ctx) |
第 07 章 |
| Service | 挂在 Context 上、以 ctx.<key> 为键的可调用对象 |
第 07、17 章 |
| inject | 插件声明"我依赖哪些服务"的方式 | 第 07、14 章 |
| effect / Fiber | 可逆副作用的注册/撤销机制与插件生命周期载体 | 第 07 章 |
| HMR | 运行中实时应用 patch、替换插件而无需重启 | 第 08 章 |
组装层
| 术语 | 一句话 | 详见 |
|---|---|---|
| bundle(组合包) | Cordis 配置项及其挂载代码的分发格式 | 第 09 章 |
| profile | Harness home 中的具名组装:web / headless / sdk / sdk-minimal / acp | 第 09 章(sdk 两个形态另见第 29 章) |
| dsh-base | web/headless/sdk/acp 四个 profile 共享的第一层组合包 | 第 09 章 |
patch(cordis.patch.yml) |
按 id 定位并整体替换配置条目、或插入新条目的覆盖文件 | 第 08、09 章 |
| cordis.yml | Cordis 声明式配置文件(agent preset 也是一份 cordis.yml) | 第 08 章 |
| loader / 应用启动器 | 把组装解析成运行时插件树;一切经 dsh CLI + profile 启动 |
第 09 章 |
| overlay | --patch 命令行传入的最后一层覆盖 |
第 09、14 章 |
Agent 层
| 术语 | 一句话 | 详见 |
|---|---|---|
| Agent | 活跃智能体句柄:id / options / session / inbox / status / ctx | 第 11 章 |
| AgentHandle | 创建者持有的所有者凭证 { agent, dispose() } |
第 11 章 |
ctx.agents |
活跃 Agent 注册表 + 创建/恢复工厂(工厂由循环注册) | 第 11 章 |
| agent loop / driver | 具体循环驱动器(dsh-agent-loop,唯一实现) | 第 24 章 |
| inbox | agent 拥有的两条有序待处理列表,投递目标 next-turn / next-step |
第 11 章 |
| followup | 排队一个普通后续轮次并唤醒 driver | 第 11 章(用法见第 04 章) |
| steer | 为最近的步骤投递引导消息;运行中 driver 在 step 边界消费 | 第 11 章(实战见第 04 章) |
| inject(agent 方法) | 排队模型可见上下文到下一次 pre-step,不唤醒 driver | 第 11 章 |
agent/pre-step |
waterfall:拒绝或改写进入本步骤的消息批次(计划模式的机关) | 第 11 章 |
agent/turn-stopping |
serial:轮次收口前的最后干预点,续命靠 steer | 第 11、24 章 |
scope / agent.ctx |
按 agent 划分的注册单位;agent 卸载即回滚其注册 | 第 11 章(subagent 场景见第 27 章) |
| shadowing | 最具体者胜出:带作用域的注册遮蔽同名全局项 | 第 17 章 |
会话层
| 术语 | 一句话 | 详见 |
|---|---|---|
| Session | 仅追加的 SessionEvent 日志,模型所见上下文的唯一真源 | 第 10 章 |
| SessionEvent | 日志条目信封:{type, seq, time, data}(+条件 sourceEventSeqs/surfaceOp) |
第 10 章 |
| 事件溯源 | "模型可见即已记录"------抵达模型请求的一切必须能从日志重建 | 第 10 章 |
deriveMessages() |
从日志投影模型历史(历史不单独存储) | 第 10 章 |
| surface 事件 | 三大表层事件:user/message、assistant/message、tool/result |
第 10 章(浏览器消费见第 28 章) |
session/event |
会话日志的广播通道,持久化插件订阅它落盘 | 第 10 章 |
| fork | ctx.sessions.fork(...):从源会话某边界派生子会话 |
第 10 章(用法见第 04 章) |
| lineage | 父子事实数据:parentSession、delegationDepth、subagentDepth |
第 27 章 |
| SessionEventMap | 经声明合并扩展的会话事件类型 map(插件加事件的地方) | 第 10、14 章 |
工具层
| 术语 | 一句话 | 详见 |
|---|---|---|
| Tool / ToolDefinition | 面向模型的 ToolSchema + execute 函数的注册约定 | 第 12 章 |
| defineTool | 构建工具的类型化 DSL | 第 15 章 |
ctx.tools |
作用域化工具注册表 + 带把关执行流水线 | 第 12 章 |
| restriction | 按 scope 过滤全局工具集合(取交集),被滤工具与不存在无法区分 | 第 27 章 |
| approval | 一次性权限决策 seam,经 approval/request waterfall 分派给回答方 |
第 12 章(体验见第 04 章) |
| ToolGuard | 单调预分派守卫:返回 reason 只能缩减权限、不可撤销 | 第 12 章 |
| sandbox / sandboxPolicy | 包装 argv 的进程沙箱 seam 与统一模式/根目录的策略 home | 第 26 章 |
| PTC | 程序化工具调用,模式 `'native' | 'ptc' |
| run_code | PTC 保留传输工具;子调用重入完整守卫流水线 | 第 25 章 |
能力 seam 层
| 术语 | 一句话 | 详见 |
|---|---|---|
| seam | Service Definition + Provider + Consumer 三角色构成的可替换能力 | 第 09 章 |
ctx.llm |
LLM 适配器注册表(llm-deepseek 等注册,循环与压缩消费) | 第 13 章 |
ctx.fs / ctx.shell / ctx.subprocess / ctx.sandbox |
文件、Bash 执行器、spawn、argv 包装四个执行世界 seam | 第 26 章 |
ctx.approval / ctx.permissionPresets / ctx.userQuestions |
审批、权限预设、人类问答三个交互 seam | 第 04 章 |
ctx.subagents |
子代理提供方与延续服务(进程内/ACP/Codex/Claude Code/SDK) | 第 27 章 |
ctx.skills / ctx.commands |
技能目录注册表 / 人类命令注册表 | 第 04 章(写法见第 15 章) |
ctx.goals / ctx.jobs / ctx.planMode |
同会话目标 / 后台任务 / 计划协作状态 | 第 04 章(goal 深入见第 27 章) |
ctx.compaction |
上下文压缩 seam(无面向模型的压缩工具) | 第 10 章 |
ctx.settings / ctx.credentials / ctx.agentDefaultModel |
用户设置、凭据引用、默认模型选择 | 第 05 章 |
ctx.sessionPersistence / ctx.sessionQuery |
会话落盘 seam(JSONL/SQLite)/ 会话读取搜索 seam | 第 10 章 |
ctx.webhookRuntime |
webhook 规则运行时:交付转 Workspace Session | 第 30 章 |
ctx.web / ctx.spillStore / ctx.terminals / ctx.lsp / ctx.storage |
搜索抓取 / 溢出存储 / PTY / 语言服务器 / 非会话存储 | 第 23 章(逐包地图) |
界面层
| 术语 | 一句话 | 详见 |
|---|---|---|
Typert / ctx.typertGateway |
运行时类型注册表 + Host 调用网关 | 第 28 章 |
@Remote / @RemoteScope |
标记对 Client 开放的方法(未标记不进生成类型) | 第 28 章 |
ctx.remote.<namespace> |
Client 侧调用入口($on() 收事件、$mount() 挂贡献) |
第 28 章 |
client-modules / window.__DSH_BOOT__ |
Client 插件图宿主与其组装出的浏览器启动图 | 第 28 章 |
| slots | Web UI 的类型化扩展位置与组件组合系统 | 第 28 章(实战见第 22 章) |
| conversation | 把 Session 历史窗口变成各 target 自有视图的子系统 | 第 28 章 |
$events |
API Remotes 内部逻辑事件流 / Connection generation source | 第 28 章 |
循环与流程
| 术语 | 一句话 | 详见 |
|---|---|---|
| turn / step / Round | 排空过程 / 一次模型请求+工具 / 外层策略迭代 | 第 11 章(状态机见第 24 章) |
| 三大事件域 | 会话事件(持久)/ agent 事件(观察拦截)/ 能力事件(挂策略) | 第 09 章(实战见第 18 章) |
| waterfall / serial | 必须调 next() 委托的瀑布事件 / 无 next() 的串行事件 |
第 08 章 |
| Ralph 循环 / Ralph Round / Ralph 交接 | 不可变目标的前台全新 agent 工作流 / 全新子会话 / 有界结构化报告 | 第 27 章 |
| Goal Round / 目标激活 | 同会话目标的续行周期 / 进程本地 armed/disarmed 续行权限 | 第 27 章(用法见第 04 章) |
参考资料
官方文档(仓库路径):
docs/glossary.zh.md--- 官方术语表,本章第五、三组多条定义的直接出处(capability-seam、agent-scope、目标、循环层级、Ralph 五节)docs/architecture.zh.md--- 事件三域、核心包表、Profile 与组合包、轮次流程、会话日志docs/capability-seams.zh.md--- 全部ctx.*服务总表(含提供方/消费方),由scripts/gen-doc-graphs.ts生成docs/subsystems/core.zh.md--- Agent/AgentHandle/inbox/拦截决策/类型模式,含agent/*事件完整目录docs/subsystems/session.zh.md--- SessionEvent 信封、十二种核心事件、deriveMessages()投影docs/subsystems/tools.zh.md---ToolGuard、tools/*事件、PTC 桥接docs/tool-catalog.zh.md--- 真实工具清单(bash、run_code、subagent、ralph等)docs/tool-execution-pipeline.zh.md--- 工具流水线全解docs/api-gateway.zh.md--- Typert API Gateway、@Remote、ctx.remote.<namespace>docs/subsystems/web-client.zh.md--- Web Client 四底座、window.__DSH_BOOT__、$eventsdocs/config-catalog.zh.md---ToolPresentationMode等配置目录- 官方文档站:https://deepseek-harness.github.io/deepseek-harness/
源码:
packages/core/agent/src/types.ts---Agent、InboxTarget、AgentOptions、CancelOptionspackages/core/agent/src/runtime-types.ts---agent/*事件与PreStepDecisionpackages/core/agent/src/index.ts---AgentRegistry、AgentHandlepackages/core/tools/src/index.ts--- 工具注册表与ToolGuardpackages/core/agent-loop/src/index.ts--- 具体循环驱动器packages/util/brand/src/index.ts---Branded<B>品牌化 ID 原语
外部资料:
- 设计论文:A Programming Paradigm for Spatiotemporal Composability(arXiv:2608.25512)--- Cordis 可组合性理论的形式化出处