核心立场
- dsh 构建在 vendored 的 cordis 之上:plugin 向共享
Context贡献 service、typed events 和可逆 effects。 - 产品的每一部分都是 plugin——model adapter、tool registry、session log、乃至 agent loop 本身——因此每一部分都可以从配置中替换。
- 没有特权核心(no privileged core):扩展方式是把 plugin mount 到其他 plugin 旁边;注册本身是 effect,plugin unload 时自动 unwind。
- 仓库纪律:改
packages/前必读 architecture.md;新行为必须挂到已文档化的 extension point;改 loop 本身必须同步更新扩展点地图。
Profile / Bundle:启动时的插件树组合
运行中的 dsh 是启动时按有序 layer 组合出的 plugin tree:
- profile:存放在 Harness home 的具名组合,声明堆叠哪些 bundle、持有 out-of-tree plugin、保留用户自己的
cordis.patch.yml。自带模板:web与headless。 - bundle:Cordis 配置行 + 其 mount 代码的发行格式;插入的内容仍可被上层 patch。
- 每个包在
package.json的dsh字段自声明:dsh.profile列出 bundle,dsh.bundle指向 patch 文件。
Layer 应用顺序(4 级):
profile 中各 bundle(按列出顺序)→ profile 的 cordis.patch.yml → home 级 patch → --patch overlay
patch 按 row id 整体替换配置或插入新行。三个 bundle:dsh-base(所有 profile 的第一层:model adapter、tools、persistence、sandbox/approval policy、settings、credentials、telemetry)、dsh-web-app(+浏览器应用)、dsh-headless(无 server 的一次性 runner)。查看实际启动树:dsh --profile web --dump-config。
Host / Client 双侧
仓库以 tsconfig.host.json / tsconfig.client.json 区分两侧(双面构建,见 engineering):
- Host 面(Node 进程中的 Cordis 树,
packages/host):ctx.webServer:纯node:http载体(named-route registry + index transform taps + 静态 dist fallback)。ctx.apiProxy:transport-agnostic 的 host gateway face,dispatch 浏览器 API 调用——每条打开的 host stream 订阅它所转发的事件,而不是被 broadcast 推送。
- Client 面(浏览器侧约 40 个包,
packages/client,大量client-ui-*):client-runtime依赖api-remotes(Remote BFF assembly);client-modules通过增量dsh.client扫描组合__DSH_BOOT__entry graph、serve plugin bundle(配client-hmr)。
- RPC 桥:
ctx.typertGateway(api-gateway)把生成的 Remote descriptor 关联到活的 Cordis service,通过共享 Connection RPC carrier 暴露 unary 调用;typert(generator/loader/registry/protocol)是跨 host/client 的类型图机制。 - “everything is a plugin” 贯穿两侧:
cordis-host-runner/cordis-client-runner/tool-cordis/client-ui-cordis支持动态 Cordis 包在 host 与浏览器两半运行。
Module graph 分层
module-graph.md 由脚本从各包的 peerDependencies(canonical runtime-dependency signal)生成。自下而上:
- runtime-diagnostics:
invariants是最底层依赖,几乎所有包都依赖它(ctx.invariants是 package-owned invariant registry)。 - util:atomic-write、brand、home-paths、launch-environment、native-command、output-retention、timeout。
- llm:
llm(消息/流词汇表 + adapter seam)、llm-deepseek、llm-pi-ai、llm-retry、token-meter。 - core(产品 API 脊柱):scope → session → system-prompt → agent → tools → agent-loop:
| 包 | 拥有 | ctx key |
|---|---|---|
| core/session | append-only SessionEvent log + 内存存储 | ctx.sessions |
| core/system-prompt | prompt-section 与 tool-schema 组装 | ctx.systemPrompt |
| core/tools | scoped tool registry + 受保护执行管线 | ctx.tools |
| core/agent | Agent 接口、live registry、agent/* 事件 | ctx.agents |
| core/agent-loop | 实现 Agent 接口的默认 driver | ctx.agentLoop |
| core/scope | per-agent scoped-registration 原语 | (库,无 key) |
| llm/llm | 消息/流词汇表 + adapter seam | ctx.llm |
- 能力域组:fs、shell、subprocess、terminal、sandbox、web、skill、subagent、jobs、lsp、mcp、compaction、spill、storage、session(persistence/projection/title/telemetry)、session-query、credentials、settings、interaction、goal、plan、todo、workflow、hooks、context、preset、guard、feedback、workspace、identity、schedule、e2b。
- 接入/应用组:acp、sdk、api、host、boot(app-boot/cmdline)、bundle(base/web-app/headless)、typert。
- 顶层:client(ui-* 系列)、extensions、examples、test-support。
完整包清单见 monorepo。
Capability Seams(可替换能力接缝)
一个 seam 是一个可替换能力,含三种角色:
- Service Definition:声明接口,拥有
ctx.<key>与词汇类型;是抽象类(如ShellExecutor)或具体 registry(如WebRuntime),绝不是 TypeScriptinterface。 - Service Provider:实现。
- Consumer:使用者,常为 model-facing tool。
一个包可兼多角色,但单一角色不构成 seam;新增能力 = 同时设计三个角色。Seam 的价值:fs 与 subprocess provider 共享同一 execution world——把它们指向远程 sandbox(如 e2b)会带着 Bash、PTY、LSP 一起整体迁移,无需 provider 分叉;subagent seam 在同一接口下从”新建 child agent”到”委托另一产品的一个 turn”都可变。
规模:26 个 seam + 29 个 core service + 1 个 bundle service(ctx.agentLoop),共 56 个 ctx key。26 个 seam:
attachments llm sessionPersistence settings credentials sessionTelemetry
storage sessionQuery sessionTitle userQuestions skills subprocess
shell terminals sandbox approval codeRuntime fs compaction subagents
jobs web spillStore directoryPicker workflowEngine lsp
Turn 流程与事件三域
step = 一次 model request + 它调用的工具;turn = 零或多个 step。详见 agent-lifecycle。
turn/start → claim 输入 → 组装 prompt sections + tool schemas
→ agent/pre-step(waterfall,可 reject/重写)→ step/start → append user/message
→ 从 log 派生历史 → agent/request → llm/stream → assistant/chunk* → assistant/message
→ tool/call* → tools/pre-execute → tools/execute → tools/post-execute → tool/result*
→ step/end →(欠款则下一 step)→ agent/turn-stopping → turn/end
事件三大域:
- Session events:持久事实,append 到 log,经
session/event广播,须在 reload 后存活。 - Agent events(
agent/*):携带 live Agent,观察/拦截进行中的工作。 - Capability events(
fs/*、tools/*、telemetry/*):不 import loop 即可挂策略/adapter。
agent/pre-step、agent/request、llm/stream、三个 tools/* 是 waterfall(listener 必须调 next());agent/turn-stopping 是 serial。全库 56 个 harness-owned 声明事件;session/event 监听者最多(约 22 个包),agent/pre-step waterfall 有 13 个 listener。
关键不变量(invariants)
- Model-visible means logged:任何到达 model request 的内容必须可从 session log 重构(运行时 invariant 断言)。新的 model-visible 输入必须新增 session event。
- 无特权核心:注册是 effect,unload 即 unwind。
- Seam 三角色完备性;Service Definition 禁用 TS interface。
- 唯一具体 loop:
ctx.agentLoop是唯一 concrete loop plugin;扩展包依赖 dsh-agent 的事件与 service,不依赖 agent-loop 包。 - 共享 execution world:
ctx.e2b持单一 SDK handle + 远程工作目录,fs-e2b 与 subprocess-e2b 同处一个 Linux runtime。 - 单一 sandbox policy home:
ctx.sandboxPolicy是部署默认 mode + workspace root 的唯一归属,bash 与 fs 两族执行者都读它(见 security-sandbox)。 - Fail-closed:
ctx.approval的一次性许可经approval/requestwaterfall 分发,无人应答时 fail closed 为unavailable。 - credentials 引用/值分离:配置只带 secret 引用,provider 拥有值;每次操作时解析,凭证轮换立即生效。
- Scope 扁平两级 + lineage 是数据:scoped 注册不向 subagent 继承;父子关系只作为数据(
parentSession等),永不影响可见性。 - goal activation 刻意不持久化:resume/fork 后自动工作必须先有人工授权。
- pre-release 立场:优先正确基础而非兼容 shim;SQLite 单调
SCHEMA_VERSION,SESSION_FORMAT_VERSION=0无兼容承诺。 - 信任类型边界:同进程 typed 边界信任 TypeScript;校验只放在 parser/config、队列、model/tool JSON、durable/file、worker、进程与 wire 边界。
术语定义见 glossary。