核心立场

  • 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)生成。自下而上:

  1. runtime-diagnostics:invariants 是最底层依赖,几乎所有包都依赖它(ctx.invariants 是 package-owned invariant registry)。
  2. util:atomic-write、brand、home-paths、launch-environment、native-command、output-retention、timeout。
  3. llm:llm(消息/流词汇表 + adapter seam)、llm-deepseek、llm-pi-ai、llm-retry、token-meter。
  4. core(产品 API 脊柱):scope → session → system-prompt → agent → tools → agent-loop:
包拥有ctx key
core/sessionappend-only SessionEvent log + 内存存储ctx.sessions
core/system-promptprompt-section 与 tool-schema 组装ctx.systemPrompt
core/toolsscoped tool registry + 受保护执行管线ctx.tools
core/agentAgent 接口、live registry、agent/* 事件ctx.agents
core/agent-loop实现 Agent 接口的默认 driverctx.agentLoop
core/scopeper-agent scoped-registration 原语(库,无 key)
llm/llm消息/流词汇表 + adapter seamctx.llm
  1. 能力域组: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。
  2. 接入/应用组:acp、sdk、api、host、boot(app-boot/cmdline)、bundle(base/web-app/headless)、typert。
  3. 顶层:client(ui-* 系列)、extensions、examples、test-support。

完整包清单见 monorepo。

Capability Seams(可替换能力接缝)

一个 seam 是一个可替换能力,含三种角色:

  • Service Definition:声明接口,拥有 ctx.<key> 与词汇类型;是抽象类(如 ShellExecutor)或具体 registry(如 WebRuntime),绝不是 TypeScript interface。
  • 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)

  1. Model-visible means logged:任何到达 model request 的内容必须可从 session log 重构(运行时 invariant 断言)。新的 model-visible 输入必须新增 session event。
  2. 无特权核心:注册是 effect,unload 即 unwind。
  3. Seam 三角色完备性;Service Definition 禁用 TS interface。
  4. 唯一具体 loop:ctx.agentLoop 是唯一 concrete loop plugin;扩展包依赖 dsh-agent 的事件与 service,不依赖 agent-loop 包。
  5. 共享 execution world:ctx.e2b 持单一 SDK handle + 远程工作目录,fs-e2b 与 subprocess-e2b 同处一个 Linux runtime。
  6. 单一 sandbox policy home:ctx.sandboxPolicy 是部署默认 mode + workspace root 的唯一归属,bash 与 fs 两族执行者都读它(见 security-sandbox)。
  7. Fail-closed:ctx.approval 的一次性许可经 approval/request waterfall 分发,无人应答时 fail closed 为 unavailable。
  8. credentials 引用/值分离:配置只带 secret 引用,provider 拥有值;每次操作时解析,凭证轮换立即生效。
  9. Scope 扁平两级 + lineage 是数据:scoped 注册不向 subagent 继承;父子关系只作为数据(parentSession 等),永不影响可见性。
  10. goal activation 刻意不持久化:resume/fork 后自动工作必须先有人工授权。
  11. pre-release 立场:优先正确基础而非兼容 shim;SQLite 单调 SCHEMA_VERSION,SESSION_FORMAT_VERSION=0 无兼容承诺。
  12. 信任类型边界:同进程 typed 边界信任 TypeScript;校验只放在 parser/config、队列、model/tool JSON、durable/file、worker、进程与 wire 边界。

术语定义见 glossary。