Przeglądaj źródła

feat: add docs website

lintianle 3 miesięcy temu
rodzic
commit
87a1774fef
35 zmienionych plików z 3540 dodań i 0 usunięć
  1. 3 0
      website/.gitignore
  2. 17 0
      website/.vitepress/config/index.ts
  3. 99 0
      website/.vitepress/config/zh-CN.ts
  4. 15 0
      website/package.json
  5. 85 0
      website/zh-CN/api/cordis/context.md
  6. 120 0
      website/zh-CN/api/cordis/events.md
  7. 108 0
      website/zh-CN/api/cordis/fiber.md
  8. 87 0
      website/zh-CN/api/cordis/registry.md
  9. 97 0
      website/zh-CN/api/cordis/service.md
  10. 85 0
      website/zh-CN/api/harness/agent.md
  11. 81 0
      website/zh-CN/api/harness/bash.md
  12. 78 0
      website/zh-CN/api/harness/fs.md
  13. 124 0
      website/zh-CN/api/harness/llm.md
  14. 56 0
      website/zh-CN/api/harness/session.md
  15. 85 0
      website/zh-CN/api/harness/subagent.md
  16. 122 0
      website/zh-CN/api/harness/tools.md
  17. 25 0
      website/zh-CN/api/index.md
  18. 72 0
      website/zh-CN/design/composability.md
  19. 129 0
      website/zh-CN/design/context-model.md
  20. 69 0
      website/zh-CN/design/effects-coeffects.md
  21. 39 0
      website/zh-CN/design/index.md
  22. 90 0
      website/zh-CN/design/reactive-coeffects.md
  23. 128 0
      website/zh-CN/design/revertible-effects.md
  24. 108 0
      website/zh-CN/develop/basic/config.md
  25. 148 0
      website/zh-CN/develop/basic/index.md
  26. 199 0
      website/zh-CN/develop/basic/tool.md
  27. 152 0
      website/zh-CN/develop/framework/events.md
  28. 139 0
      website/zh-CN/develop/framework/index.md
  29. 147 0
      website/zh-CN/develop/framework/service.md
  30. 156 0
      website/zh-CN/develop/practice/index.md
  31. 169 0
      website/zh-CN/develop/practice/llm-adapter.md
  32. 342 0
      website/zh-CN/guide/config.md
  33. 47 0
      website/zh-CN/guide/index.md
  34. 98 0
      website/zh-CN/guide/quickstart.md
  35. 21 0
      website/zh-CN/index.md

+ 3 - 0
website/.gitignore

@@ -0,0 +1,3 @@
+node_modules/
+.vitepress/dist/
+.vitepress/cache/

+ 17 - 0
website/.vitepress/config/index.ts

@@ -0,0 +1,17 @@
+import { defineConfig } from 'vitepress'
+import { zhCN } from './zh-CN'
+
+export default defineConfig({
+  title: 'DeepSeek Harness',
+  description: '插件化 Agent 开发框架',
+
+  locales: {
+    'zh-CN': zhCN,
+  },
+
+  themeConfig: {
+    socialLinks: [
+      { icon: 'github', link: 'https://github.com/deepseek-harness/deepseek-harness' },
+    ],
+  },
+})

+ 99 - 0
website/.vitepress/config/zh-CN.ts

@@ -0,0 +1,99 @@
+import type { DefaultTheme, LocaleSpecificConfig } from 'vitepress'
+
+const guideSidebar: DefaultTheme.SidebarItem[] = [
+  {
+    text: '入门',
+    items: [
+      { text: '介绍', link: '/zh-CN/guide/' },
+      { text: '快速开始', link: '/zh-CN/guide/quickstart' },
+      { text: '配置文件', link: '/zh-CN/guide/config' },
+    ],
+  },
+]
+
+const developSidebar: DefaultTheme.SidebarItem[] = [
+  {
+    text: '基础',
+    items: [
+      { text: '第一个插件', link: '/zh-CN/develop/basic/' },
+      { text: '开发一个 Tool', link: '/zh-CN/develop/basic/tool' },
+      { text: '插件配置', link: '/zh-CN/develop/basic/config' },
+    ],
+  },
+  {
+    text: '框架能力',
+    items: [
+      { text: '插件与生命周期', link: '/zh-CN/develop/framework/' },
+      { text: '服务与依赖', link: '/zh-CN/develop/framework/service' },
+      { text: '事件系统', link: '/zh-CN/develop/framework/events' },
+    ],
+  },
+  {
+    text: '实战',
+    items: [
+      { text: '能力的三层拆分', link: '/zh-CN/develop/practice/' },
+      { text: 'LLM 适配器', link: '/zh-CN/develop/practice/llm-adapter' },
+    ],
+  },
+]
+
+const apiSidebar: DefaultTheme.SidebarItem[] = [
+  {
+    text: '框架 API',
+    items: [
+      { text: '总览', link: '/zh-CN/api/' },
+      { text: 'Context', link: '/zh-CN/api/cordis/context' },
+      { text: 'Events', link: '/zh-CN/api/cordis/events' },
+      { text: 'Fiber', link: '/zh-CN/api/cordis/fiber' },
+      { text: 'Registry', link: '/zh-CN/api/cordis/registry' },
+      { text: 'Service', link: '/zh-CN/api/cordis/service' },
+    ],
+  },
+  {
+    text: 'Harness API',
+    items: [
+      { text: 'Tools (dsh-tools)', link: '/zh-CN/api/harness/tools' },
+      { text: 'LLM (dsh-llm)', link: '/zh-CN/api/harness/llm' },
+      { text: 'Session (dsh-session)', link: '/zh-CN/api/harness/session' },
+      { text: 'Agent (dsh-agent)', link: '/zh-CN/api/harness/agent' },
+      { text: 'Bash (dsh-bash)', link: '/zh-CN/api/harness/bash' },
+      { text: 'Filesystem (dsh-fs)', link: '/zh-CN/api/harness/fs' },
+      { text: 'Subagent (dsh-subagent)', link: '/zh-CN/api/harness/subagent' },
+    ],
+  },
+]
+
+const designSidebar: DefaultTheme.SidebarItem[] = [
+  {
+    text: '系统设计',
+    items: [
+      { text: '概述', link: '/zh-CN/design/' },
+      { text: '可组合性与插件系统', link: '/zh-CN/design/composability' },
+      { text: '作用与余作用', link: '/zh-CN/design/effects-coeffects' },
+      { text: '可逆作用', link: '/zh-CN/design/revertible-effects' },
+      { text: '响应式余作用', link: '/zh-CN/design/reactive-coeffects' },
+      { text: '上下文模型', link: '/zh-CN/design/context-model' },
+    ],
+  },
+]
+
+export const zhCN: LocaleSpecificConfig<DefaultTheme.Config> = {
+  label: '简体中文',
+  lang: 'zh-CN',
+  themeConfig: {
+    nav: [
+      { text: '入门', link: '/zh-CN/guide/', activeMatch: '/zh-CN/guide/' },
+      { text: '开发', link: '/zh-CN/develop/basic/', activeMatch: '/zh-CN/develop/' },
+      { text: 'API', link: '/zh-CN/api/', activeMatch: '/zh-CN/api/' },
+      { text: '设计', link: '/zh-CN/design/', activeMatch: '/zh-CN/design/' },
+    ],
+    sidebar: {
+      '/zh-CN/guide/': guideSidebar,
+      '/zh-CN/develop/': developSidebar,
+      '/zh-CN/api/': apiSidebar,
+      '/zh-CN/design/': designSidebar,
+    },
+    outline: { label: '本页目录' },
+    docFooter: { prev: '上一篇', next: '下一篇' },
+  },
+}

+ 15 - 0
website/package.json

@@ -0,0 +1,15 @@
+{
+  "name": "@deepseek-ai/website",
+  "private": true,
+  "version": "0.0.1",
+  "type": "module",
+  "scripts": {
+    "dev": "vitepress dev . --port 5173 --open",
+    "build": "vitepress build .",
+    "preview": "vitepress preview ."
+  },
+  "devDependencies": {
+    "vitepress": "^1.6.3",
+    "vue": "^3.5.13"
+  }
+}

+ 85 - 0
website/zh-CN/api/cordis/context.md

@@ -0,0 +1,85 @@
+# Context
+
+上下文对象是 Cordis 的核心。所有服务、方法、属性都通过 `ctx` 访问。
+
+## 服务与混入
+
+Context 基于组合式 API 设计,大部分属性和方法挂载在服务上。以下是核心 API:
+
+- [`ctx.on`](./events#ctx-on) — 注册事件监听器
+- [`ctx.emit`](./events#ctx-emit) — 触发事件
+- [`ctx.bail`](./events#ctx-bail) — 短路事件
+- [`ctx.serial`](./events#ctx-serial) — 顺序异步事件
+- [`ctx.waterfall`](./events#ctx-waterfall) — 管道事件
+- [`ctx.effect`](./fiber#fiber-effect) — 注册可逆效果
+- [`ctx.plugin`](./registry#ctx-plugin) — 加载子插件
+- [`ctx.inject`](./registry#ctx-inject) — 获取依赖的插件
+- [`ctx.get`](#ctx-get) — 获取服务
+- [`ctx.set`](#ctx-set) — 设置服务
+- [`ctx.provide`](#ctx-provide) — 声明服务
+
+## 实例属性
+
+### ctx.fiber
+
+- **类型:** [`Fiber`](./fiber)
+
+当前上下文的作用域对象。
+
+## 实例方法
+
+### ctx.extend(meta)
+
+- **meta:** `object`
+- **返回值:** `Context`
+
+构造一个以当前上下文为原型的新上下文实例。
+
+### ctx.intercept(name, config)
+
+- **name:** `string` 服务名称
+- **config:** `object` 配置拦截
+- **返回值:** `Context`
+
+为指定服务添加一层配置拦截,返回新的上下文实例。
+
+### ctx.isolate(name, label?)
+
+- **name:** `string` 服务名称
+- **label:** `symbol` 隔离域符号(可选)
+- **返回值:** `Context`
+
+创建一个针对指定服务的隔离域,返回新的上下文实例。隔离域中的同名服务互不影响。
+
+### ctx.get(name)
+
+- **name:** `string` 服务名称
+- **返回值:** `Service | undefined`
+
+获取指定名称的服务实例。
+
+### ctx.set(name, value)
+
+- **name:** `string` 服务名称
+- **value:** `any` 服务值
+
+设置指定名称的服务。
+
+### ctx.provide(name, value?, options?)
+
+- **name:** `string` 服务名称
+- **value:** `any` 初始值(可选)
+- **options:** `object`
+- **返回值:** `void`
+
+声明一个服务。声明后其他插件可以通过 `inject` 依赖它。
+
+## 静态属性
+
+### Context.events
+
+内置事件服务的 symbol key。
+
+### Context.current
+
+当前活跃的 Context 实例(在异步链中通过 AsyncLocalStorage 追踪)。

+ 120 - 0
website/zh-CN/api/cordis/events.md

@@ -0,0 +1,120 @@
+# Events
+
+`ctx.events` 是内置服务,提供事件系统相关的全部 API。
+
+## 实例方法
+
+### ctx.on(event, listener, options?) {#ctx-on}
+
+- **event:** `string` 事件名称
+- **listener:** `Function` 事件监听器
+- **options:** `object`
+  - **prepend:** `boolean` 是否注册为前置(默认 `false`)
+  - **global:** `boolean` 是否注册为全局(默认 `false`)
+- **返回值:** `() => void` 取消注册函数
+
+注册一个事件监听器。返回的函数可用于手动取消注册,但通常不需要——插件卸载时会自动清理。
+
+```typescript
+ctx.on('agent/turn-end', (data) => {
+  console.log('turn ended:', data)
+})
+```
+
+### ctx.emit(thisArg?, event, ...args) {#ctx-emit}
+
+- **thisArg:** `any` 监听器的 `this` 参数(可选)
+- **event:** `string` 事件名称
+- **args:** `any[]` 事件参数
+- **返回值:** `void`
+
+同步触发所有匹配的监听器(并行,不等待异步完成)。
+
+### ctx.parallel(thisArg?, event, ...args)
+
+- 签名同 `emit`
+- **返回值:** `Promise<void>`
+
+异步触发所有匹配的监听器(并行等待)。
+
+### ctx.bail(thisArg?, event, ...args) {#ctx-bail}
+
+- **返回值:** `any`
+
+同步依次触发监听器。第一个返回非 `undefined`/`null`/`false` 值的监听器停止链并返回该值。
+
+### ctx.serial(thisArg?, event, ...args) {#ctx-serial}
+
+- **返回值:** `Promise<any>`
+
+异步依次触发监听器。语义同 `bail` 的异步版本。
+
+### ctx.waterfall(thisArg?, event, ...args) {#ctx-waterfall}
+
+- **返回值:** `Promise<any>`
+
+管道模式:每个监听器接收前一个的输出。监听器内部必须调用 `next()` 才会传递给下一个。
+
+```typescript
+// 注册
+ctx.on('llm/pre-request', async (messages, next) => {
+  messages.push(extraMsg)
+  return next(messages)  // 必须调用
+})
+
+// 触发
+const result = await ctx.waterfall('llm/pre-request', initialMessages)
+```
+
+::: warning
+不调用 `next()` 即为否决 (veto)——管道终止。这是设计行为,用于拦截/网关。
+:::
+
+## Harness 内置事件
+
+### agent/pre-step
+
+- **触发模式:** serial
+- **参数:** `{ agentId, turnIndex }`
+
+Agent 执行一步之前触发。
+
+### agent/post-step
+
+- **触发模式:** emit
+- **参数:** `{ agentId, turnIndex, blocks }`
+
+Agent 执行一步之后触发。
+
+### tool/call
+
+- **触发模式:** emit
+- **参数:** `{ name, args, callId }`
+
+Tool 被模型调用时触发。
+
+### tool/result
+
+- **触发模式:** emit
+- **参数:** `{ name, result, callId }`
+
+Tool 返回结果时触发。
+
+### session/event
+
+- **触发模式:** emit
+- **参数:** `SessionEvent`
+
+会话事件被记录时触发。
+
+### compact/start
+
+- **触发模式:** emit
+
+上下文压缩开始。
+
+### compact/end
+
+- **触发模式:** emit
+
+上下文压缩结束。

+ 108 - 0
website/zh-CN/api/cordis/fiber.md

@@ -0,0 +1,108 @@
+# Fiber
+
+Fiber(作用域)是插件实例的运行时容器,管理其生命周期和效果。
+
+## 状态机
+
+```
+PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED
+                 ↘ FAILED
+```
+
+| 状态 | 数值 | 含义 |
+|------|------|------|
+| PENDING | 0 | 依赖未就绪,等待中 |
+| LOADING | 1 | 正在执行 `apply` |
+| ACTIVE | 2 | 运行中 |
+| FAILED | 3 | `apply` 抛出异常 |
+| UNLOADING | 4 | 正在撤销效果 |
+| DISPOSED | 5 | 已完全卸载 |
+
+## 实例属性
+
+### fiber.uid
+
+- **类型:** `number`
+
+Fiber 的唯一标识符。
+
+### fiber.status
+
+- **类型:** `number`
+
+当前状态(见状态机)。
+
+### fiber.config
+
+- **类型:** `object`
+
+传递给插件的配置对象。
+
+### fiber.error
+
+- **类型:** `Error | undefined`
+
+如果状态是 FAILED,包含导致失败的异常。
+
+## 实例方法
+
+### fiber.effect(callback) {#fiber-effect}
+
+- **callback:** `() => (() => void) | void`
+- **返回值:** `() => void`
+
+注册一个效果。`callback` 在 Fiber 激活时执行;如果返回函数,该函数在 Fiber dispose 时执行。
+
+```typescript
+ctx.effect(() => {
+  const timer = setInterval(tick, 1000)
+  return () => clearInterval(timer)
+})
+```
+
+等价地可以通过 `ctx.effect()` 调用(ctx 代理到当前 fiber)。
+
+### fiber.dispose()
+
+- **返回值:** `Promise<void>`
+
+手动 dispose 该 Fiber。按注册逆序撤销所有效果,递归 dispose 所有子 Fiber。
+
+```typescript
+const child = ctx.plugin(somePlugin)
+// 之后:
+await child.dispose()
+```
+
+### fiber.update(config)
+
+- **config:** `object` 新配置
+- **返回值:** `void`
+
+热更新配置。如果新旧配置不同,触发 dispose + 重新 apply。
+
+### fiber.restart()
+
+- **返回值:** `void`
+
+强制重启:dispose 后重新加载。
+
+### fiber.then(resolve, reject?)
+
+- **返回值:** `Promise<void>`
+
+使 Fiber 可以被 `await`:等到状态进入 ACTIVE 或 FAILED。
+
+```typescript
+const fiber = ctx.plugin(myPlugin)
+await fiber  // 等待插件加载完成
+```
+
+## 访问当前 Fiber
+
+```typescript
+export function apply(ctx: Context) {
+  const fiber = ctx.fiber  // 当前插件的 Fiber
+  console.log(fiber.status)  // 1 (LOADING, 因为正在 apply 中)
+}
+```

+ 87 - 0
website/zh-CN/api/cordis/registry.md

@@ -0,0 +1,87 @@
+# Registry
+
+插件注册表,管理插件的加载和依赖解析。
+
+## 实例方法
+
+### ctx.plugin(plugin, config?) {#ctx-plugin}
+
+- **plugin:** `Plugin` 插件(函数、对象或类)
+- **config:** `object` 传递给插件的配置(可选)
+- **返回值:** `Fiber`
+
+加载一个子插件,返回其 Fiber。子 Fiber 的生命周期绑定到父上下文。
+
+```typescript
+// 函数插件
+ctx.plugin(myPlugin, { key: 'value' })
+
+// 类插件
+ctx.plugin(MyService)
+
+// 返回的 Fiber 可以 await 或 dispose
+const fiber = ctx.plugin(myPlugin)
+await fiber
+```
+
+### ctx.inject(names, callback) {#ctx-inject}
+
+- **names:** `string[]` 服务名列表
+- **callback:** `(ctx: Context) => void`
+- **返回值:** `() => void`
+
+等待指定服务全部就绪后执行 callback。如果服务消失,callback 的效果会自动撤销;服务恢复后重新执行。
+
+```typescript
+ctx.inject(['tools', 'llm'], (ctx) => {
+  // tools 和 llm 都就绪了
+  ctx.tools.register(/* ... */)
+})
+```
+
+这是 `export const inject = [...]` 声明的底层 API。大多数情况下直接使用声明式写法即可。
+
+## 插件形态
+
+`ctx.plugin()` 接受三种插件形态:
+
+### 函数插件
+
+```typescript
+function myPlugin(ctx: Context, config?: Config) {
+  // ...
+}
+myPlugin.name = 'my-plugin'
+myPlugin.inject = ['tools']
+```
+
+### 对象插件
+
+```typescript
+const myPlugin = {
+  name: 'my-plugin',
+  inject: ['tools'],
+  apply(ctx: Context, config?: Config) {
+    // ...
+  },
+}
+```
+
+### 类插件(Service)
+
+```typescript
+class MyService extends Service {
+  static inject = ['tools']
+  constructor(ctx: Context) {
+    super(ctx, 'myService')
+  }
+}
+```
+
+## 插件元信息
+
+| 属性 | 类型 | 说明 |
+|------|------|------|
+| `name` | `string` | 插件名称(日志用) |
+| `inject` | `string[] \| { required?: string[], optional?: string[] }` | 依赖声明 |
+| `Config` | `Schema \| object` | 配置 schema 或默认值 |

+ 97 - 0
website/zh-CN/api/cordis/service.md

@@ -0,0 +1,97 @@
+# Service
+
+Service 基类,用于创建对外暴露能力的插件。
+
+## 基本用法
+
+```typescript
+import { Service, type Context } from 'cordis'
+
+declare module 'cordis' {
+  interface Context {
+    myService: MyService
+  }
+}
+
+export default class MyService extends Service {
+  constructor(ctx: Context) {
+    super(ctx, 'myService')
+  }
+
+  // 公开方法
+  doSomething() {
+    // ...
+  }
+}
+```
+
+加载后,其他插件可通过 `ctx.myService` 访问。
+
+## 构造函数
+
+### new Service(ctx, name)
+
+- **ctx:** `Context` 上下文
+- **name:** `string` 服务名(注册到 `ctx[name]`)
+
+## 实例属性
+
+### service.ctx
+
+- **类型:** `Context`
+
+该服务绑定的上下文。
+
+### service\[Service.tracker\]
+
+- **类型:** `object`
+
+服务追踪信息(名称、绑定状态等)。
+
+## 生命周期
+
+Service 子类可以覆写以下方法:
+
+### start()
+
+服务激活时调用。在这里初始化资源。
+
+### stop()
+
+服务停用时调用。在这里释放资源。
+
+## 静态属性
+
+### Service.inject
+
+- **类型:** `string[] | { required?: string[], optional?: string[] }`
+
+声明本服务依赖的其他服务。
+
+## 与 inject 的关系
+
+当一个 Service 被加载:
+1. 框架为该服务名创建声明 (`ctx.provide`)
+2. 实例赋值到 `ctx[name]`
+3. 依赖该服务的所有 Fiber 从 PENDING 转为 LOADING
+
+当 Service 被卸载:
+1. `ctx[name]` 被置为 `undefined`
+2. 依赖它的 Fiber 被 dispose
+3. 当新的 provider 出现时,dependant Fiber 重新加载
+
+## 示例:Harness 中的 Service
+
+```typescript
+// dsh-tools 的 ToolRegistry 就是一个 Service
+export class ToolRegistry extends Service {
+  constructor(ctx: Context) {
+    super(ctx, 'tools')
+  }
+
+  register(tool: ToolDefinition): () => void {
+    // ...注册逻辑
+    return dispose
+  }
+}
+```

+ 85 - 0
website/zh-CN/api/harness/agent.md

@@ -0,0 +1,85 @@
+# Agent (dsh-agent)
+
+Agent 实例管理和生命周期。
+
+**包名:** `@deepseek-ai/dsh-agent`
+**服务名:** `ctx.agents`
+
+## Agent Service
+
+### ctx.agents.create(options)
+
+- **options:** `AgentOptions`
+- **返回值:** `Agent`
+
+创建一个新的 Agent 实例。
+
+### ctx.agents.get(id)
+
+- **id:** `AgentId`
+- **返回值:** `Agent | undefined`
+
+获取指定 ID 的 Agent 实例。
+
+## AgentOptions
+
+```typescript
+interface AgentOptions {
+  /** Agent ID(branded) */
+  id?: AgentId
+  /** 使用的模型名 */
+  model: string
+  /** 系统提示词(支持 {{model}} 变量) */
+  persona?: string
+  /** 关联的 session */
+  session?: Session
+}
+```
+
+## Agent 实例
+
+### agent.id
+
+- **类型:** `AgentId`
+
+Agent 的唯一标识符(branded string)。
+
+### agent.model
+
+- **类型:** `string`
+
+Agent 使用的模型名。
+
+### agent.step(input)
+
+- **input:** `ContentBlock[]`
+- **返回值:** `Promise<StepResult>`
+
+执行一步:将输入发送给模型,获取响应,执行 tool calls。这是 agent-loop 内部使用的核心方法。
+
+## Agent Loop
+
+Agent 的执行循环由 `dsh-agent-loop` 管理。它:
+
+1. 组装 system prompt + 历史消息 + 当前输入
+2. 调用 LLM(通过 `ctx.llm`)
+3. 解析响应中的 tool calls
+4. 执行 tools
+5. 将 tool results 追加到 session
+6. 如果 finish reason 是 `tool-calls`,回到步骤 2
+
+### 扩展点
+
+- `agent/pre-step` 事件 — 在每一步 LLM 调用前触发
+- `agent/post-step` 事件 — 在每一步完成后触发
+- `llm/pre-request` waterfall — 可修改发送给模型的消息
+
+## AgentId
+
+Opaque branded string:
+
+```typescript
+import { AgentId } from '@deepseek-ai/dsh-agent'
+
+const id = AgentId('main')
+```

+ 81 - 0
website/zh-CN/api/harness/bash.md

@@ -0,0 +1,81 @@
+# Bash (dsh-bash)
+
+Bash 命令执行接口。
+
+**接口包:** `@deepseek-ai/dsh-bash`
+**实现:** `@deepseek-ai/dsh-bash-local`
+**消费者:** `@deepseek-ai/dsh-tool-bash`(内置于 agent-core)
+
+## Bash Service
+
+### ctx.bash.execute(request)
+
+- **request:** `BashRequest`
+- **返回值:** `Promise<BashResult>`
+
+执行一个 bash 命令。
+
+## BashRequest
+
+```typescript
+interface BashRequest {
+  /** 要执行的命令 */
+  command: string
+  /** 工作目录 */
+  workdir?: string
+  /** 超时时间 (ms) */
+  timeoutMs?: number
+}
+```
+
+## BashResult
+
+```typescript
+interface BashResult {
+  /** 退出码 */
+  exitCode: number
+  /** stdout 输出 */
+  stdout: string
+  /** stderr 输出 */
+  stderr: string
+  /** 是否超时 */
+  timedOut: boolean
+}
+```
+
+## 配置 (dsh-bash-local)
+
+```typescript
+interface Config {
+  /** 命令超时时间,默认 120000 (2 分钟) */
+  timeoutMs: number
+}
+```
+
+在 `cordis.yml` 中:
+
+```yaml
+- name: '@deepseek-ai/dsh-bash-local'
+  config:
+    timeoutMs: 60000
+```
+
+## 模型可用的 Tools
+
+`dsh-tool-bash` 向模型暴露以下 tools(由 `agent-core` 捆绑):
+
+| Tool | 说明 |
+|------|------|
+| `bash` | 执行命令(同步,等待完成) |
+| `bash_output` | 获取后台命令的输出 |
+| `bash_kill` | 终止后台命令 |
+
+## 设计模式
+
+Bash 是 Harness 的"能力三件套"典型案例:
+
+- `dsh-bash`(接口):定义 `ctx.bash` 和 `BashRequest`/`BashResult` 类型
+- `dsh-bash-local`(实现):通过 `child_process.spawn` 在本地执行
+- `dsh-tool-bash`(消费者):将能力包装为模型可调用的 tool
+
+换一个沙箱执行器只需替换 `dsh-bash-local`,接口和 tool 不变。

+ 78 - 0
website/zh-CN/api/harness/fs.md

@@ -0,0 +1,78 @@
+# Filesystem (dsh-fs)
+
+文件系统操作接口。
+
+**接口包:** `@deepseek-ai/dsh-fs`
+**实现:** `@deepseek-ai/dsh-fs-local` + `@deepseek-ai/dsh-fs-policy`
+**消费者:** `@deepseek-ai/dsh-tool-fs`
+
+## FS Service
+
+### ctx.fs.read(path, options?)
+
+- **path:** `string`
+- **options:** `{ offset?: number; limit?: number }`
+- **返回值:** `Promise<string>`
+
+读取文件内容。
+
+### ctx.fs.write(path, content)
+
+- **path:** `string`
+- **content:** `string`
+- **返回值:** `Promise<void>`
+
+写入文件(覆盖)。
+
+### ctx.fs.edit(path, edits)
+
+- **path:** `string`
+- **edits:** `Edit[]`
+- **返回值:** `Promise<void>`
+
+对文件执行精确的字符串替换编辑。
+
+### ctx.fs.stat(path)
+
+- **path:** `string`
+- **返回值:** `Promise<FileStat>`
+
+获取文件/目录信息。
+
+## 配置 (dsh-fs-local)
+
+```typescript
+interface Config {
+  /** 工作目录(相对路径的基准) */
+  cwd: string
+}
+```
+
+## 策略门 (dsh-fs-policy)
+
+`dsh-fs-policy` 是一个可选的中间层插件,实现 read-before-write/edit 策略——模型必须先读取文件才能写入或编辑。这防止模型盲目覆盖文件。
+
+在 `cordis.yml` 中,它位于 `fs-local` 和 `tool-fs` 之间:
+
+```yaml
+- name: '@deepseek-ai/dsh-fs-local'
+  config:
+    cwd: !!js process.cwd()
+- name: '@deepseek-ai/dsh-fs-policy'
+- name: '@deepseek-ai/dsh-tool-fs'
+```
+
+## 模型可用的 Tools
+
+| Tool | 说明 |
+|------|------|
+| `read` | 读取文件内容(支持 offset/limit) |
+| `write` | 写入文件(需要先 read) |
+| `edit` | 精确字符串替换(需要先 read) |
+
+## 三件套结构
+
+- `dsh-fs`:接口定义
+- `dsh-fs-local`:本地文件系统实现
+- `dsh-fs-policy`:策略门(read-before-write 检查)
+- `dsh-tool-fs`:模型 tool 层

+ 124 - 0
website/zh-CN/api/harness/llm.md

@@ -0,0 +1,124 @@
+# LLM (dsh-llm)
+
+LLM 服务接口和适配器注册。
+
+**包名:** `@deepseek-ai/dsh-llm`
+**服务名:** `ctx.llm`
+
+## LLM Service
+
+### ctx.llm.registerAdapter(models, adapter)
+
+- **models:** `string[]` 该适配器支持的模型名列表
+- **adapter:** `LlmAdapter` 适配器实例
+- **返回值:** `() => void` disposer
+
+注册一个 LLM 适配器。当请求中指定的模型名在 `models` 列表中时,路由到该适配器。
+
+```typescript
+ctx.llm.registerAdapter(['deepseek-v4-flash', 'deepseek-v4-pro'], adapter)
+```
+
+## LlmAdapter
+
+适配器基类。子类必须实现 `stream()` 方法。
+
+### stream(options)
+
+- **options:** `GenerateOptions`
+- **返回值:** `AsyncIterable<StreamChunk>`
+
+将统一请求格式转换为具体 API 的流式调用。
+
+## GenerateOptions
+
+```typescript
+interface GenerateOptions {
+  model: string
+  messages: Message[]
+  tools?: ToolSpec[]
+  system?: string
+  maxTokens?: number
+  temperature?: number
+}
+```
+
+| 字段 | 说明 |
+|------|------|
+| `model` | 请求的模型名 |
+| `messages` | 对话历史 |
+| `tools` | 当前可用的 tool 列表(JSON Schema 格式) |
+| `system` | 系统提示词 |
+| `maxTokens` | 最大输出 token |
+| `temperature` | 采样温度 |
+
+## StreamChunk
+
+流式响应的增量 chunk 类型:
+
+```typescript
+type StreamChunk =
+  | { type: 'block-start'; index: number; blockType: 'text' | 'tool-call' }
+  | { type: 'text-delta'; index: number; text: string }
+  | { type: 'tool-call-delta'; index: number; id: CallId; name: string; argumentsDelta: string }
+  | { type: 'block-end'; index: number; block: ContentBlock }
+  | { type: 'usage'; usage: TokenUsage }
+  | { type: 'finish'; reason: FinishReason }
+```
+
+### 协议规则
+
+1. 每个内容块以 `block-start` 开始,以 `block-end` 结束
+2. `index` 从 0 递增
+3. `text-delta` 只在 `blockType: 'text'` 的块中
+4. `tool-call-delta` 只在 `blockType: 'tool-call'` 的块中
+5. `usage` 在 `finish` 之前
+6. `finish` 必须是最后一个 chunk
+
+## CallId
+
+Tool call 的 opaque branded ID:
+
+```typescript
+import { CallId } from '@deepseek-ai/dsh-llm'
+
+const id = CallId('call-abc123')
+```
+
+## TokenUsage
+
+```typescript
+interface TokenUsage {
+  inputTokens: number
+  outputTokens: number
+}
+```
+
+## FinishReason
+
+```typescript
+type FinishReason =
+  | { kind: 'stop' }
+  | { kind: 'tool-calls' }
+  | { kind: 'max-tokens' }
+```
+
+## Message
+
+对话消息类型:
+
+```typescript
+interface Message {
+  role: 'user' | 'assistant'
+  content: ContentBlock[]
+}
+```
+
+## ContentBlock
+
+```typescript
+type ContentBlock =
+  | { type: 'text'; text: string }
+  | { type: 'tool-call'; id: CallId; name: string; arguments: string }
+  | { type: 'tool-result'; callId: CallId; content: ContentBlock[]; isError?: boolean }
+```

+ 56 - 0
website/zh-CN/api/harness/session.md

@@ -0,0 +1,56 @@
+# Session (dsh-session)
+
+会话事件流管理。
+
+**包名:** `@deepseek-ai/dsh-session`
+**服务名:** `ctx.session`
+
+## 概述
+
+Session 是 Agent 的对话状态容器。所有模型可见的内容都必须经过 session 事件流记录——这是"model-visible = logged"原则的实现。
+
+## SessionSurface
+
+会话的外部接口,用于查询当前状态。
+
+### surface.messages
+
+- **类型:** `Message[]`
+
+当前会话的完整消息列表(经过 compaction 处理后的视图)。
+
+### surface.events
+
+- **类型:** `SessionEvent[]`
+
+原始事件流。
+
+## SessionEvent
+
+会话中所有变更以事件形式记录:
+
+```typescript
+type SessionEvent =
+  | { type: 'user/message'; content: ContentBlock[] }
+  | { type: 'assistant/message'; content: ContentBlock[] }
+  | { type: 'tool/call'; name: string; args: unknown; callId: CallId }
+  | { type: 'tool/result'; callId: CallId; content: ContentBlock[]; isError?: boolean }
+  | { type: 'compact/start'; range: [number, number] }
+  | { type: 'compact/end'; summary: string }
+  | { type: 'todo/write'; items: TodoItem[] }
+  // ... 更多事件类型
+```
+
+## 设计原则
+
+### Model-visible = Logged
+
+任何到达模型请求的内容都必须能从 session log 重建。如果你要引入新的模型可见输入,必须先定义对应的 session event。
+
+### 事件是 append-only
+
+Session 事件流是只追加的。修改历史(如 compaction)通过新事件(compact/start + compact/end)表达,而不是修改旧事件。
+
+### 持久化
+
+Session 事件流可以通过 `dsh-session-persistence` 持久化到磁盘(JSONL 或 SQLite),实现跨进程恢复。

+ 85 - 0
website/zh-CN/api/harness/subagent.md

@@ -0,0 +1,85 @@
+# Subagent (dsh-subagent)
+
+子代理委派接口。
+
+**接口包:** `@deepseek-ai/dsh-subagent`
+**实现:** `@deepseek-ai/dsh-subagent-spawn` / `@deepseek-ai/dsh-subagent-fork`
+**消费者:** `@deepseek-ai/dsh-tool-subagent`
+
+## Subagent Service
+
+### ctx.subagent.run(request)
+
+- **request:** `SubagentRequest`
+- **返回值:** `Promise<SubagentResult>`
+
+委派一个任务给子代理执行。
+
+## SubagentRequest
+
+```typescript
+interface SubagentRequest {
+  /** 使用的 provider 名称 */
+  provider: string
+  /** 委派给子代理的提示 */
+  prompt: string
+  /** 子代理使用的模型(可选,默认继承父) */
+  model?: string
+}
+```
+
+## SubagentResult
+
+```typescript
+interface SubagentResult {
+  /** 子代理的最终回复 */
+  response: string
+}
+```
+
+## Provider 模式
+
+Subagent 支持多种"后端"(provider),通过配置选择:
+
+### spawn
+
+创建一个全新的子代理实例,没有父级的对话历史:
+
+```yaml
+- name: '@deepseek-ai/dsh-subagent-spawn'
+  config:
+    providerName: spawn
+```
+
+### fork
+
+创建一个携带父级已完成 turn 前缀的子代理,子代理"知道"父级的对话上下文:
+
+```yaml
+- name: '@deepseek-ai/dsh-subagent-fork'
+  config:
+    providerName: fork
+```
+
+## 模型可用的 Tools
+
+通过 `dsh-tool-subagent` 暴露。可以加载多次,每次绑定不同 provider:
+
+```yaml
+# 暴露为 "subagent" tool,使用 spawn 后端
+- name: '@deepseek-ai/dsh-tool-subagent'
+  config:
+    provider: spawn
+    toolName: subagent
+
+# 暴露为 "subagent_fork" tool,使用 fork 后端
+- name: '@deepseek-ai/dsh-tool-subagent'
+  config:
+    provider: fork
+    toolName: subagent_fork
+```
+
+## 使用场景
+
+- **spawn** — 独立子任务(如"搜索这个问题"),子代理不需要知道父级上下文
+- **fork** — 需要上下文的子任务(如"基于我们刚才讨论的,去实现这个"),子代理继承父级的对话前缀

+ 122 - 0
website/zh-CN/api/harness/tools.md

@@ -0,0 +1,122 @@
+# Tools (dsh-tools)
+
+Tool 注册表和 `defineTool` DSL。
+
+**包名:** `@deepseek-ai/dsh-tools`
+**服务名:** `ctx.tools`
+
+## ToolRegistry
+
+### ctx.tools.register(tool)
+
+- **tool:** `ToolDefinition`
+- **返回值:** `() => void` disposer
+
+注册一个 tool。返回的 disposer 可手动撤销注册(通常不需要,插件卸载时自动撤销)。
+
+## defineTool\<S\>(options)
+
+类型安全的 tool 定义辅助函数。
+
+```typescript
+import { defineTool } from '@deepseek-ai/dsh-tools'
+
+const tool = defineTool({
+  name: 'read_file',
+  description: 'Read a file from disk.',
+  parameters: {
+    path: { type: 'string', required: true, description: 'Absolute file path' },
+    offset: { type: 'number' },
+    limit: { type: 'number', description: 'Max lines to read' },
+  },
+  async execute(args) {
+    // args: { path: string; offset?: number; limit?: number }
+  },
+})
+```
+
+### DefineToolOptions\<S\>
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `name` | `string` | Tool 名称(全局唯一) |
+| `description` | `string` | 发送给模型的描述 |
+| `parameters` | `SchemaSpec` | 参数 schema(见下文) |
+| `execute` | `(args: InferArgs<S>, exec: ToolExecution) => Promise<ToolExecuteReturn>` | 执行函数 |
+| `presentCall?` | `(args: InferArgs<S>) => ToolCallView \| undefined` | UI 展示(纯函数) |
+| `presentResult?` | `(args: InferArgs<S>, result: ToolResult) => ToolResultView \| undefined` | 结果 UI 展示(纯函数) |
+
+## SchemaSpec
+
+参数 schema DSL。每个属性是一个 `SchemaProp`:
+
+```typescript
+interface SchemaProp {
+  type: 'string' | 'number' | 'boolean' | 'object' | 'array'
+  required?: true
+  description?: string
+  enum?: string[]
+  properties?: SchemaSpec   // type: 'object' 时
+  items?: SchemaProp        // type: 'array' 时
+}
+```
+
+### 类型推导 (InferArgs)
+
+`InferArgs<S>` 自动从 `SchemaSpec` 推导 TypeScript 类型:
+
+- `required: true` → 必填字段
+- 无 `required` → 可选字段(`?`)
+- `type: 'object'` + `properties` → 递归推导嵌套对象
+- `type: 'array'` + `items` → 推导为数组
+
+## ToolDefinition
+
+运行时 tool 定义(`defineTool` 的返回值):
+
+```typescript
+interface ToolDefinition {
+  name: string
+  description: string
+  parameters: Record<string, unknown>  // JSON Schema
+  execute(args: unknown, exec: ToolExecution): Promise<ToolExecuteReturn>
+  presentCall?(args: unknown): ToolCallView | undefined
+  presentResult?(args: unknown, result: ToolResult): ToolResultView | undefined
+}
+```
+
+## ToolExecuteReturn
+
+```typescript
+type ToolExecuteReturn =
+  | ContentBlock[]                         // 仅内容
+  | { content: ContentBlock[]; meta?: unknown }  // 内容 + 元信息
+```
+
+## ToolArgsError
+
+当模型生成的参数不匹配 schema 时抛出:
+
+```typescript
+class ToolArgsError extends HarnessError {
+  code: 'INVALID_ARGS'
+  violations: string[]
+}
+```
+
+框架自动捕获并转换为 `isError` 结果返回给模型。
+
+## validateArgs(spec, args)
+
+- **spec:** `SchemaSpec`
+- **args:** `unknown`
+- **返回值:** `string[]` 违规信息列表(空 = 合法)
+
+手动校验参数。`defineTool` 内部使用,通常不需要直接调用。
+
+## schemaSpecToJsonSchema(spec)
+
+- **spec:** `SchemaSpec`
+- **返回值:** `JsonSchemaObject`
+
+将 SchemaSpec 转换为标准 JSON Schema。用于发送给模型的 wire format。

+ 25 - 0
website/zh-CN/api/index.md

@@ -0,0 +1,25 @@
+# API 参考
+
+本节提供 DeepSeek Harness 的完整 API 参考文档,分为两部分:
+
+## 框架 API
+
+Cordis 微内核提供的基础能力,所有插件开发都建立在这些 API 之上:
+
+- [Context](./cordis/context) — 上下文对象,所有服务和方法的入口
+- [Events](./cordis/events) — 事件系统 API(emit / on / bail / serial / waterfall)
+- [Fiber](./cordis/fiber) — 作用域生命周期(状态机、effect、dispose)
+- [Registry](./cordis/registry) — 插件注册(plugin / inject)
+- [Service](./cordis/service) — 服务基类
+
+## Harness API
+
+DeepSeek Harness SDK 提供的扩展 API,用于构建 Agent 能力:
+
+- [Tools (dsh-tools)](./harness/tools) — Tool 注册、defineTool DSL、Schema 类型系统
+- [LLM (dsh-llm)](./harness/llm) — LLM 服务、适配器注册、StreamChunk 协议
+- [Session (dsh-session)](./harness/session) — 会话事件流、消息类型
+- [Agent (dsh-agent)](./harness/agent) — Agent 实例管理、生命周期
+- [Bash (dsh-bash)](./harness/bash) — Bash 执行接口
+- [Filesystem (dsh-fs)](./harness/fs) — 文件系统接口
+- [Subagent (dsh-subagent)](./harness/subagent) — 子代理委派接口

+ 72 - 0
website/zh-CN/design/composability.md

@@ -0,0 +1,72 @@
+# 可组合性与插件系统
+
+## 组合
+
+编程的本质就是组合。将小的构建块拼装为更大的系统,再将大系统作为块继续拼装——这是从函数到模块到微服务一脉相承的思想。
+
+组合可以分为两种:
+
+- **静态组合**:编译期确定的组合,例如函数调用、模块导入。
+- **动态组合**:运行时确定的组合,例如热更新、插件加载/卸载。
+
+静态组合是逻辑的组合;动态组合为可组合性引入了时间和空间两个新维度。
+
+## 三种可组合性
+
+| 维度 | 定义 | 对应问题 |
+|------|------|----------|
+| **逻辑可组合性** (Logical) | 功能能否被任意拆分和组装 | 接口设计是否正交 |
+| **时间可组合性** (Temporal) | 能否灵活、安全地控制组合的运行时序 | 能否热加载/卸载而不泄漏 |
+| **空间可组合性** (Spatial) | 能否灵活、安全地管理组合的依赖关系 | 依赖缺失时行为是否确定 |
+
+一门编程语言或应用框架越多地使用组合范式,就称它的可组合性越好。
+
+## 传统插件系统的问题
+
+插件系统是动态组合的典型形式。浏览器扩展、IDE 插件、操作系统驱动,都是其实例。然而大多数插件系统并不可靠。
+
+### 不可逆的插件化
+
+以 VSCode 为例:
+
+- 卸载或更新插件时需要重启整个系统。
+- 无法在运行时追踪和回收副作用,导致内存泄漏和非预期的资源占用。
+- 即便提供了 `deactivate` 钩子,也无法强制开发者正确实现清理逻辑。
+
+**根本原因**:未做到时间可组合——系统不知道某个插件产生了哪些副作用、占用了哪些资源。
+
+### 不完全的插件化
+
+- 无法表达插件间的依赖关系,扩展能力受限。
+- 只有外围功能被下放给插件,核心功能依然通过修改主体代码来实现。
+
+**根本原因**:未做到空间可组合——系统缺乏对依赖关系的建模和管理。
+
+## Cordis 的解法
+
+Cordis 同时解决了上述两个问题:
+
+1. **可逆作用** (Revertible Effects) 实现时间可组合性——所有注册自动追踪、自动回收。
+2. **响应式余作用** (Reactive Coeffects) 实现空间可组合性——依赖声明驱动加载顺序。
+
+两者通过**上下文模型** (Context Model) 统一为单一的编程范式:开发者只需通过 `ctx` 调用框架 API,可逆性和依赖管理由框架保证。
+
+## 在 Harness 中的体现
+
+DeepSeek Harness 将 Cordis 的可组合性应用到 Agent 开发领域:
+
+```typescript
+// 一个 Harness 插件天然是可逆的
+export const inject = ['tools', 'llm'] // 空间可组合:声明依赖
+
+export function apply(ctx: Context) {
+  // 时间可组合:注册会被自动追踪和回收
+  ctx.tools.register(defineTool('my-tool', {
+    description: '...',
+    parameters: { /* ... */ },
+    async execute(args) { /* ... */ },
+  }))
+}
+```
+
+插件卸载时,tool 自动注销、事件监听自动移除——无需手动清理。依赖的服务(如 `llm`)消失时,插件自动挂起;恢复时自动重新加载。

+ 129 - 0
website/zh-CN/design/context-model.md

@@ -0,0 +1,129 @@
+# 上下文模型
+
+上下文 (Context) 是 Cordis 将作用与余作用统一的运行时模型。它提供了一种编程范式,允许开发者无心智负担地编写时间、空间可组合的程序。
+
+## 作用上下文 (Effect Context)
+
+当副作用被记录到全局环境时,$\mathcal{C}\times\left(\mathcal{C}\to\mathcal{C}\right)$ 也就变成了一个更大的 $\mathcal{C}$。
+
+递归地定义:
+
+$$
+\begin{matrix}
+\mathcal{C}_1=\mathcal{C}_0\times\left(\mathcal{C}_0\to\mathcal{C}_0\right)\\
+\mathcal{C}_2=\mathcal{C}_1\times\left(\mathcal{C}_1\to\mathcal{C}_1\right)\\
+\cdots\\
+\mathcal{C}_{n+1}=\mathcal{C}_n\times\left(\mathcal{C}_n\to\mathcal{C}_n\right)\\
+\end{matrix}
+$$
+
+每一层 $\mathcal{C}$ 包含上一层的状态,同时记录了上一层的副作用。
+
+利用递归类型得到真正的作用上下文:
+
+$$
+\mathcal{C}=\mathcal{C}\times\left(\mathcal{C}\to\mathcal{C}\right)
+$$
+
+这就是 Cordis Context 的理论根基:**上下文既是状态容器,又是副作用追踪器。**
+
+## 上下文的派生
+
+当一个插件被加载时,从当前上下文派生出新的上下文实例:
+
+```
+Root Context
+├── Plugin A Context   ← 管理 A 的副作用
+│   └── Sub-plugin Context
+└── Plugin B Context   ← 管理 B 的副作用
+```
+
+- 子级上下文管理插件内部的全部副作用
+- 插件整体作为一个副作用被父级上下文收集
+- 父级 dispose 时,子级先被 dispose(保证依赖逆序)
+
+## 余作用上下文 (Coeffect Context)
+
+余作用由作用产生:
+
+- **提供服务**本身是一种作用——它占用了服务命名空间资源
+- 因此服务的提供被记录在作用上下文中
+- 上下文将作用与余作用关联起来,提供了统一的时间、空间可组合性
+
+```typescript
+// 提供服务 = 一个 effect(占用 ctx.llm 这个 "资源")
+class LlmService extends Service {
+  // 当此插件卸载时,ctx.llm 被回收(effect 的逆操作)
+  // 所有依赖 llm 的插件因 coeffect 不满足而挂起
+}
+```
+
+## 基于上下文的开发范式
+
+上下文模型提供了两个关键优势:
+
+### 无感性 (Transparent)
+
+框架将领域中的所有方法都封装为 effect 版本。开发者只需调用 `ctx` 上的方法,就能自动获得时间/空间可组合性:
+
+```typescript
+export function apply(ctx: Context) {
+  // 以下每一行都是 effect——卸载时自动逆序回收
+  ctx.on('agent/step-result', validateResult)
+  ctx.tools.register(myTool)
+  ctx.llm.registerAdapter(['my-model'], adapter)
+
+  // 开发者无需知道"可逆作用"的存在
+  // 只需通过 ctx 调用,框架保证一切安全
+}
+```
+
+### 渐进性 (Incremental)
+
+可以逐步将现有框架中的 API 替换为可组合版本,无需一次性重写:
+
+```typescript
+// 第一步:用 ctx.effect 包装遗留 API
+ctx.effect(() => {
+  const legacy = legacySystem.register(handler)
+  return () => legacySystem.unregister(legacy)
+})
+
+// 第二步:在未来将遗留 API 原生改造为 effect
+// 两种方式可以并存
+```
+
+## 在 Harness 中的完整图景
+
+DeepSeek Harness 的运行时是一个 Context 树:
+
+```
+Root Context (Cordis 应用)
+├── dsh-session (提供 ctx.sessions)
+├── dsh-tools (提供 ctx.tools)
+├── dsh-llm (提供 ctx.llm)
+│   └── deepseek-adapter (注册模型适配器)
+├── dsh-agent-loop (提供 ctx.agentLoop)
+├── dsh-bash (提供 ctx.bash)
+│   └── bash-local (本地执行器实现)
+├── dsh-fs (提供 ctx.fs)
+│   └── fs-local (本地 FS 实现)
+├── dsh-system-prompt (提供 ctx.systemPrompt)
+└── Agent Context (由 agents.create() 派生)
+    ├── Agent 自己注册的 tools
+    ├── Agent 的 session
+    └── Subagent Context (进一步派生)
+```
+
+每个节点都是一个 Context 实例。插件加载/卸载、服务出现/消失、Agent 创建/销毁——这一切都在 Context 树上以统一的语义发生。
+
+## 总结
+
+| 概念 | 解决的问题 | Cordis 机制 |
+|------|-----------|-------------|
+| 作用上下文 | 副作用追踪与回收 | `ctx.effect()` / `fiber.dispose()` |
+| 上下文派生 | 副作用的层级隔离 | `ctx.plugin()` 创建子 Context |
+| 余作用上下文 | 依赖的动态管理 | `inject` 声明 + 服务生命周期 |
+| 统一范式 | 开发者无需关心底层机制 | 只需通过 `ctx` 调用 API |
+
+这就是为什么 Harness 能在保持「一切皆插件」的同时,不给插件开发者增加心智负担——**上下文模型把复杂性封装在了框架内部**。

+ 69 - 0
website/zh-CN/design/effects-coeffects.md

@@ -0,0 +1,69 @@
+# 作用与余作用
+
+## 作用 (Effects)
+
+Effects 是程序中对系统状态或外部环境产生影响的操作:I/O、状态修改、资源占用等。
+
+学术界对作用有两种主要建模方式:
+
+### 单子作用 (Monadic Effects)
+
+- 通过单子 (monad) 将副作用封装为类型安全的计算链。
+- 提供 `return`(纯值注入)和 `bind`(链式组合)两个基本操作。
+- 以纯函数式的方式处理带有副作用的计算。(Moggi 1991, Wadler 1992)
+- 代表语言:Haskell (IO Monad)、Rust (Result/Option)
+
+### 代数作用 (Algebraic Effects)
+
+- 允许在函数中"抛出"一个 effect,在调用栈的更高层次"捕获"并处理。
+- 类似异常处理,但更通用——处理后可以恢复执行。
+- 代表语言:Koka、Eff、OCaml 5+ (Kiselyov 2018, Kawahara 2020)
+
+## 余作用 (Coeffects)
+
+Coeffects 是程序执行时依赖的上下文信息:环境变量、系统资源、外部服务等。
+
+- Coeffects 是 effects 的对偶 (dual) 概念,通常通过余单子 (comonad) 建模。(Petricek 2013, 2014; Brünnler 2014)
+- 更前沿的理论将带有资源的上下文建模为 **graded algebra**(有序半环加最大元):
+  - 加法 = 并行组合;0 元 = 无资源
+  - 乘法 = 串行组合;1 元 = 单位资源
+  - 序 = 资源约束;最大元 = 无限资源
+  - (Breuvart 2015, Gaboardi 2016, Dal Lago 2022)
+
+## 现有理论的不足
+
+这些理论主要面向**静态分析**和**短时程序**:
+
+1. **缺乏运行时追踪**:类型系统能标记副作用的存在,但无法在运行时追踪和回收。对长时运行程序(服务端、Agent),这意味着资源泄漏不可避免。
+
+2. **缺乏动态性**:面向编译期分析,无法处理运行时的加载/卸载需求。
+
+3. **崩溃而非降级**:类型不满足时直接拒绝编译或运行时崩溃,而长时运行程序更希望安全降级——挂起不满足依赖的部分,而非停止整个系统。
+
+## Cordis 的突破
+
+Cordis 选择了不同的路径——在运行时层面解决可组合性问题:
+
+| 现有理论 | Cordis 方案 |
+|----------|-------------|
+| 类型标记副作用 | 运行时追踪并自动回收副作用 |
+| 编译期拒绝 | 运行时挂起/恢复 |
+| 面向短时程序 | 面向长时运行程序设计 |
+
+这由两个互补机制实现:
+
+- **[可逆作用](./revertible-effects)** — 将副作用形式化为可逆的群操作
+- **[响应式余作用](./reactive-coeffects)** — 将依赖建模为具有生命周期的服务
+
+## 在 Agent 开发中的意义
+
+对 DeepSeek Harness 而言,作用/余作用模型直接支撑了以下能力:
+
+| 作用 (Effect) | 余作用 (Coeffect) |
+|---------------|-------------------|
+| 注册一个 tool | 依赖 tool registry 服务 |
+| 注册一个 LLM adapter | 依赖 LLM 服务接口 |
+| 监听 session 事件 | 依赖 session 服务存在 |
+| 启动子进程 | 依赖 bash executor 实现 |
+
+每一个 effect 都可逆(tool 可注销、adapter 可移除);每一个 coeffect 都有生命周期(服务消失则依赖者挂起)。这就是 Agent 能被安全热替换的根本原因。

+ 39 - 0
website/zh-CN/design/index.md

@@ -0,0 +1,39 @@
+# 系统设计
+
+DeepSeek Harness 建立在 Cordis 微内核之上,采用「一切皆插件」的架构。本节阐述这套设计背后的理论基础和设计哲学。
+
+## 核心思想
+
+Harness 追求三种可组合性的统一:
+
+| 维度 | 含义 | Cordis 对应机制 |
+|------|------|----------------|
+| 逻辑可组合性 | 功能能否自由拆分和拼装 | 插件系统、事件系统 |
+| 时间可组合性 | 运行时能否安全地加载/卸载功能 | 可逆作用、自动清理 |
+| 空间可组合性 | 依赖关系能否被安全地声明和管理 | 服务生命周期、依赖注入 |
+
+这三种可组合性在上下文模型中统一为单一的编程范式。
+
+## 目录
+
+- [可组合性与插件系统](./composability) — 组合的本质,以及传统插件系统为什么不可靠
+- [作用与余作用](./effects-coeffects) — Cordis 效果系统的理论模型
+- [可逆作用](./revertible-effects) — 时间可组合性的形式化定义与证明
+- [响应式余作用](./reactive-coeffects) — 空间可组合性的服务语义
+- [上下文模型](./context-model) — Context 如何将作用与余作用统一
+
+## 设计如何映射到 Harness
+
+| 理论概念 | Harness 中的体现 |
+|----------|-----------------|
+| 可逆作用 | `ctx.tools.register()` 返回 disposer;插件卸载时工具自动注销 |
+| 响应式余作用 | `inject: ['llm']` 声明依赖;LLM 适配器不可用时插件自动挂起 |
+| 上下文派生 | 子 Agent 拥有独立 Context,继承父级服务但有独立生命周期 |
+| Waterfall 事件 | `agent/request` 链式拦截,任一监听器可决定最终请求参数 |
+| Capability seam | bash/fs/web 三层拆分:接口 → 实现 → 模型工具 |
+
+## 进一步阅读
+
+- [插件与生命周期](/zh-CN/develop/framework/) — 实践中的 Fiber 状态机
+- [服务与依赖](/zh-CN/develop/framework/service) — 服务声明与注入
+- [能力的三层拆分](/zh-CN/develop/practice/) — Capability seam 模式

+ 90 - 0
website/zh-CN/design/reactive-coeffects.md

@@ -0,0 +1,90 @@
+# 响应式余作用
+
+响应式余作用 (Reactive Coeffects) 是 Cordis 实现**空间可组合性**的核心机制。
+
+- 将代码中的资源依赖抽象为服务 (service) 的概念
+- 通过运行时生命周期语义,实现自动、安全、高效的资源管理
+
+## 依赖的本质是生命周期
+
+传统的依赖注入(如 Angular DI、Spring IoC)解决的是"怎么拿到依赖"的问题,但忽略了一个关键问题:**依赖是有生命周期的**。
+
+一个数据库连接池可能重启,一个 API 服务可能下线,一个 LLM adapter 可能被热替换。当依赖消失时,依赖者应当如何表现?
+
+- 崩溃?——对长时运行程序不可接受。
+- 继续运行?——可能产生不一致状态。
+- **自动挂起,等待恢复?**——Cordis 的选择。
+
+## 服务与生命周期
+
+Cordis 将程序中的资源依赖抽象为**服务** (service):
+
+- 任何插件都可以声明自己依赖的服务列表
+- 服务存在明确的生命周期(提供、撤销)
+- 运行时对依赖不满足的插件**等待**,而非拒绝
+- 服务生命周期结束前,依赖该服务的插件**先一步被回收**
+
+```typescript
+// LLM 适配器插件:提供 llm 服务
+export class LlmService extends Service {
+  static inject = ['http']  // 自身依赖 http
+  // 当 http 不可用时,LlmService 自动挂起
+  // 挂起导致 ctx.llm 不可用
+  // 所有 inject: ['llm'] 的插件级联挂起
+}
+```
+
+## 与现有理论的对比
+
+### 与 Comonad 余作用比较
+
+基于 Comonad 的余作用(Petricek 2013)将上下文建模为静态结构,侧重于编译期分析。Cordis 的响应式余作用额外引入了**时序语义**:
+
+- 服务可在运行时出现/消失
+- 依赖关系随之动态建立/解除
+- 效果的生命周期由依赖关系决定
+
+### 与 Grade Algebra 余作用比较
+
+基于 Grade Algebra 的余作用(Gaboardi 2016)用有序半环描述资源的组合规则。Cordis 的服务依赖可以建模为**交换半群**:
+
+- 服务名构成依赖集合
+- 集合并(∪)对应并行依赖
+- 交换律:依赖 A + B ≡ 依赖 B + A(声明顺序无关)
+- 结合律:依赖分组方式不影响语义
+
+但 Cordis 还增加了代数不具备的运行时行为:当集合中的某个服务不可用时,整个依赖集不满足,触发挂起。
+
+## 在 Cordis 中的实现
+
+```typescript
+// 声明依赖
+export const inject = ['tools', 'llm']
+
+export function apply(ctx: Context) {
+  // 到这里时,ctx.tools 和 ctx.llm 一定可用
+  // 如果任一服务消失,此插件自动卸载
+  // 服务恢复后,自动重新执行 apply
+}
+```
+
+服务生命周期变化时的行为:
+
+```
+llm service 可用 → 依赖 llm 的插件 PENDING → ACTIVE
+llm service 消失 → 依赖 llm 的插件 ACTIVE → DISPOSED
+llm service 恢复 → 依赖 llm 的插件重新 PENDING → ACTIVE
+```
+
+## 为什么 Agent 需要响应式余作用
+
+在 Harness 场景下,响应式余作用直接支撑:
+
+| 场景 | 行为 |
+|------|------|
+| LLM adapter 热替换 | 依赖 `llm` 的插件自动挂起/恢复,中间不丢状态 |
+| 按需加载 bash 执行器 | bash tool 只在 `bash` 服务就绪后注册 |
+| 子 Agent 独立服务空间 | 通过 `ctx.isolate()` 隔离服务实例,互不干扰 |
+| 可选能力降级 | `inject: { web: { required: false } }` 允许 web 不可用时继续运行 |
+
+这意味着 Harness 插件开发者无需编写防御性的 "if service exists" 检查——框架保证:当你的 `apply` 被调用时,声明的依赖一定已就绪。

+ 128 - 0
website/zh-CN/design/revertible-effects.md

@@ -0,0 +1,128 @@
+# 可逆作用
+
+可逆作用 (Revertible Effects) 是 Cordis 实现**时间可组合性**的核心机制。
+
+- 在单子作用的基础上增加可逆性约束
+- 提供面向长时运行程序的作用系统
+- 确保程序可以在插件粒度上回到任意状态
+
+## 副作用的封装
+
+现实中的程序需要与各种副作用打交道。假设一个不纯函数:
+
+$$
+f_\text{impure}: \text{X}\to\text{Y}
+$$
+
+我们将所有可能的副作用用类型 $\mathcal{C}$ 封装,函数变为:
+
+$$
+f: \mathcal{C}\times\text{X}\to\mathcal{C}\times\text{Y}
+$$
+
+对于长时运行程序,忽略函数本身的入参和出参,$f$ 属于函数空间 $\mathfrak{F}=\mathcal{C}\to\mathcal{C}$。
+
+## 从幺半群到群
+
+任何函数 $f: \mathcal{C}\to\mathcal{C}$ 都是状态空间到自身的变换。在组合 $\circ$ 下构成**幺半群**:
+
+1. 封闭性:$f\circ g$ 也是 $\mathcal{C}\to\mathcal{C}$
+2. 结合律:$(f\circ g)\circ h=f\circ (g\circ h)$
+3. 单位元:$\text{id}$,使得 $f\circ\text{id}=\text{id}\circ f=f$
+
+如果额外要求每个 $f$ 存在逆元 $f^{-1}$(即副作用可回收),$\mathfrak{F}$ 升级为**群**。
+
+## 副作用都可逆吗?
+
+观察计算机中的副作用模式:
+
+| 操作 | 占用资源 | 逆操作 |
+|------|----------|--------|
+| 打开文件 | 文件描述符 | 关闭文件 |
+| 创建子进程 | 进程号 | 杀死进程 |
+| 监听端口 | 端口 | 取消监听 |
+| 添加回调函数 | 事件槽位 | 删除回调 |
+| 分配内存 | 内存区块 | 回收内存 |
+
+**副作用就是对资源的占用。** 计算机的资源天然设计为可重复使用,因此这些副作用一定是可逆的。
+
+## 追踪和回收副作用
+
+Cordis 通过 $\text{effect}$ 和 $\text{restore}$ 函子追踪和回收逆函数。
+
+### effect 函子
+
+$$
+\begin{array}{}
+\text{effect}&:&
+\left(\mathcal{C}\to\mathcal{C}\right)&\to&
+\mathcal{C}\times\left(\mathcal{C}\to\mathcal{C}\right)&\to&
+\mathcal{C}\times\left(\mathcal{C}\to\mathcal{C}\right)\\
+\text{effect}&=&f&\mapsto&\left(c, h\right)&\mapsto&\left(f(c), h\circ f^{-1}\right)
+\end{array}
+$$
+
+直觉:执行 $f$ 产生的副作用记入状态 $c$,同时将逆操作 $f^{-1}$ 追加到回收链 $h$ 中。
+
+### 同态性证明
+
+$\text{effect}$ 是从 $\mathcal{C}\to\mathcal{C}$ 到 $\mathcal{C}\times(\mathcal{C}\to\mathcal{C})\to\mathcal{C}\times(\mathcal{C}\to\mathcal{C})$ 的同态:
+
+$$
+\begin{aligned}
+\text{effect}\ (f\circ g) \left(c, h\right)
+&=\left((f\circ g)(c), h\circ (f\circ g)^{-1}\right)\\
+&=\left(f(g(c)), h\circ g^{-1}\circ f^{-1}\right)\\
+&=\left(\text{effect}\ f\right)\left(g(c), h\circ g^{-1}\right)\\
+&=\left(\text{effect}\ f\right)\circ\left(\text{effect}\ g\right) \left(c, h\right)
+\end{aligned}
+$$
+
+这意味着:组合两个操作后再追踪 = 分别追踪后再组合。副作用追踪与执行顺序无关。
+
+### restore 函子
+
+$$
+\begin{array}{}
+\text{restore}&:&
+\mathcal{C}\times\left(\mathcal{C}\to\mathcal{C}\right)&\to&
+\mathcal{C}\times\left(\mathcal{C}\to\mathcal{C}\right)\\
+\text{restore}&=&\left(c, h\right)&\mapsto&\left(h(c),\text{id}\right)
+\end{array}
+$$
+
+直觉:将回收链 $h$ 应用到当前状态,一次性回收所有已追踪的副作用。
+
+## 在 Cordis 中的实现
+
+理论映射到 API:
+
+| 数学概念 | Cordis API | 说明 |
+|----------|-----------|------|
+| $\text{effect}(f)$ | `ctx.effect(() => { ...; return dispose })` | 注册副作用并返回清理函数 |
+| $\text{restore}$ | `fiber.dispose()` | 执行 Fiber 的整个回收链 |
+| $f^{-1}$ | dispose 返回值 / cleanup 函数 | 逆操作 |
+
+```typescript
+export function apply(ctx: Context) {
+  // effect: 创建资源,返回其逆操作
+  ctx.effect(() => {
+    const server = startServer(8080)     // f: 占用端口
+    return () => server.close()          // f⁻¹: 释放端口
+  })
+
+  // 框架 API 内部已封装 effect
+  ctx.on('event', handler)               // 内部: effect(addListener, removeListener)
+  ctx.tools.register(myTool)             // 内部: effect(addTool, removeTool)
+}
+// 当此插件被卸载时,restore 自动按逆序执行所有 f⁻¹
+```
+
+## 为什么 Agent 需要可逆作用
+
+在 Harness 场景下,可逆作用直接支撑:
+
+- **热替换 LLM 适配器**:卸载旧适配器(回收注册)、加载新适配器,无需重启
+- **动态 tool 管理**:根据对话上下文动态添加/移除 tool,不泄漏
+- **子 Agent 生命周期**:子 Agent 完成后,其注册的所有临时 tool 和监听器自动清理
+- **优雅关闭**:进程退出时所有插件按依赖逆序 dispose,确保资源完全释放

+ 108 - 0
website/zh-CN/develop/basic/config.md

@@ -0,0 +1,108 @@
+# 插件配置
+
+让你的插件接受用户在 `cordis.yml` 中传入的配置。
+
+## 定义 Config 类型
+
+在插件中导出一个 `Config` 类型和可选的默认值:
+
+```typescript
+import type { Context } from 'cordis'
+
+export const name = 'my-plugin'
+
+export interface Config {
+  greeting: string
+  maxRetries: number
+  verbose?: boolean
+}
+
+export const Config = {
+  greeting: 'Hello',
+  maxRetries: 3,
+  verbose: false,
+}
+
+export function apply(ctx: Context, config: Config) {
+  console.log(config.greeting)  // 用户配置或默认值
+}
+```
+
+用户在 `cordis.yml` 中这样使用:
+
+```yaml
+- name: './src/my-plugin.ts'
+  config:
+    greeting: 'Hi there'
+    maxRetries: 5
+```
+
+未提供的字段使用导出的 `Config` 对象中的默认值。
+
+## Schema 校验
+
+对于需要严格校验的场景,使用 Schemastery 定义 schema:
+
+```typescript
+import type { Context } from 'cordis'
+import Schema from 'schemastery'
+
+export const name = 'validated-plugin'
+
+export interface Config {
+  apiKey: string
+  timeout: number
+  mode: 'fast' | 'accurate'
+}
+
+export const Config = Schema.object({
+  apiKey: Schema.string().required(),
+  timeout: Schema.number().default(30000),
+  mode: Schema.union(['fast', 'accurate']).default('fast'),
+})
+
+export function apply(ctx: Context, config: Config) {
+  // config 已经过校验,类型安全
+}
+```
+
+Schema 在插件加载时执行校验。如果配置不合法,插件会加载失败并给出明确错误信息。
+
+## 设计原则
+
+### 无硬编码可调参数
+
+Harness 的约定:**任何两个部署可能想要不同值的东西,都应该是配置字段**。
+
+```typescript
+// 错误 — 硬编码超时时间
+const TIMEOUT = 30000
+
+// 正确 — 可配置
+export interface Config {
+  timeoutMs: number  // 默认 30000
+}
+```
+
+检验标准:能否在 `cordis.yml` 中改变这个值,而不需要修改代码?
+
+### 配置错误要响亮
+
+如果配置引用了不存在的东西(比如一个不存在的模型名),应该尽早报错,而不是静默跳过:
+
+```typescript
+export function apply(ctx: Context, config: Config) {
+  if (!ctx.llm.hasAdapter(config.model)) {
+    throw new Error(`Model "${config.model}" is not registered by any LLM adapter`)
+  }
+}
+```
+
+## 配合 HMR
+
+配置变更会触发插件热替换:修改 `cordis.yml` 中某个插件的 `config`,框架会卸载旧实例、加载新实例。由于注册都是效果(自动清理),这个过程是安全的。
+
+## 下一步
+
+- [插件与生命周期](../framework/) — 深入了解插件的完整生命周期
+- [服务与依赖](../framework/service) — 让你的插件对外提供服务

+ 148 - 0
website/zh-CN/develop/basic/index.md

@@ -0,0 +1,148 @@
+# 第一个插件
+
+本文带你编写一个最小的 Harness 插件并加载到 Agent 中。
+
+## 插件是什么
+
+在 Harness 中,插件是一个导出 `apply` 函数的 TypeScript 模块。框架在加载时调用 `apply`,传入一个 `ctx`(上下文对象),你通过 `ctx` 注册能力:
+
+```typescript
+import type { Context } from 'cordis'
+
+export const name = 'my-plugin'
+
+export function apply(ctx: Context) {
+  // 在这里注册能力
+}
+```
+
+就这么简单。
+
+## 创建插件文件
+
+在你的项目目录下创建 `src/my-plugin.ts`:
+
+```typescript
+import type { Context } from 'cordis'
+
+export const name = 'hello-plugin'
+
+export function apply(ctx: Context) {
+  // 监听 agent-loop 的 ready 事件
+  ctx.on('ready', () => {
+    console.log('[hello-plugin] 插件已加载!')
+  })
+}
+```
+
+## 注册到 cordis.yml
+
+在你的 `cordis.yml` 中添加一条:
+
+```yaml
+- id: hello
+  name: './src/my-plugin.ts'
+```
+
+启动后你会在控制台看到 `[hello-plugin] 插件已加载!`。
+
+## 自动清理
+
+通过 `ctx` 注册的任何东西——事件监听、tool、定时器——在插件卸载时都会被自动清理。你不需要手动 removeListener 或 clearInterval。
+
+如果你有需要手动清理的资源(比如一个网络连接),用 `ctx.effect()` 告诉框架怎么清理:
+
+```typescript
+export function apply(ctx: Context) {
+  ctx.effect(() => {
+    const timer = setInterval(() => {
+      console.log('heartbeat')
+    }, 5000)
+
+    // 返回的函数会在插件卸载时被调用
+    return () => clearInterval(timer)
+  })
+}
+```
+
+## 声明依赖
+
+如果你的插件需要使用其他服务(如 `tools`、`llm`),需要声明 `inject`:
+
+```typescript
+export const name = 'my-tool-plugin'
+export const inject = ['tools']
+
+export function apply(ctx: Context) {
+  // ctx.tools 现在可用
+  ctx.tools.register(/* ... */)
+}
+```
+
+框架会确保依赖的服务就绪后才加载你的插件。
+
+## 插件的三种形态
+
+除了函数形式,插件还支持对象形式和类形式:
+
+### 对象形式
+
+```typescript
+export default {
+  name: 'my-plugin',
+  inject: ['tools'],
+  apply(ctx: Context) {
+    // ...
+  },
+}
+```
+
+### 类形式
+
+```typescript
+import { Service } from 'cordis'
+
+export default class MyService extends Service {
+  static inject = ['tools']
+
+  constructor(ctx: Context) {
+    super(ctx, 'myService')
+  }
+
+  start() {
+    // 服务启动逻辑
+  }
+}
+```
+
+大多数情况下,函数形式足够了。类形式用于需要对外提供服务的插件(见 [服务与依赖](../framework/service))。
+
+## 完整示例
+
+参考仓库中的 `examples/echo-agent/src/echo-tool.ts`,这是一个注册 tool 的插件:
+
+```typescript
+import type { Context } from 'cordis'
+import { defineTool } from '@deepseek-ai/dsh-tools'
+
+export const name = 'echo-tool'
+export const inject = ['tools']
+
+export function apply(ctx: Context) {
+  ctx.tools.register(defineTool({
+    name: 'echo',
+    description: 'Echo the given text back, uppercased.',
+    parameters: {
+      text: { type: 'string', required: true },
+    },
+    async execute(args) {
+      return [{ type: 'text', text: `ECHO: ${args.text.toUpperCase()}` }]
+    },
+  }))
+}
+```
+
+## 下一步
+
+- [开发一个 Tool](./tool) — 详细了解 tool 定义 DSL
+- [插件配置](./config) — 让插件接受用户配置

+ 199 - 0
website/zh-CN/develop/basic/tool.md

@@ -0,0 +1,199 @@
+# 开发一个 Tool
+
+Tool 是模型可以调用的能力。本文介绍如何用 `defineTool` 编写一个 tool。
+
+## 最小示例
+
+```typescript
+import type { Context } from 'cordis'
+import { defineTool } from '@deepseek-ai/dsh-tools'
+
+export const name = 'my-tool'
+export const inject = ['tools']
+
+export function apply(ctx: Context) {
+  ctx.tools.register(defineTool({
+    name: 'greet',
+    description: 'Greet someone by name.',
+    parameters: {
+      name: { type: 'string', required: true, description: 'The name to greet' },
+    },
+    async execute(args) {
+      // args 自动推导为 { name: string }
+      return [{ type: 'text', text: `Hello, ${args.name}!` }]
+    },
+  }))
+}
+```
+
+## 参数定义
+
+`parameters` 用一种简洁的格式描述参数,框架会自动转换为模型需要的 JSON Schema。
+
+### 基本类型
+
+```typescript
+parameters: {
+  path: { type: 'string', required: true },
+  limit: { type: 'number' },
+  recursive: { type: 'boolean' },
+}
+// 推导类型: { path: string; limit?: number; recursive?: boolean }
+```
+
+### 枚举
+
+```typescript
+parameters: {
+  mode: { type: 'string', required: true, enum: ['read', 'write', 'append'] },
+}
+// 推导类型: { mode: string }  (运行时校验 enum 值)
+```
+
+### 嵌套对象
+
+```typescript
+parameters: {
+  options: {
+    type: 'object',
+    properties: {
+      timeout: { type: 'number' },
+      retries: { type: 'number' },
+    },
+  },
+}
+// 推导类型: { options?: { timeout?: number; retries?: number } }
+```
+
+### 数组
+
+```typescript
+parameters: {
+  tags: {
+    type: 'array',
+    items: { type: 'string' },
+  },
+}
+// 推导类型: { tags?: string[] }
+```
+
+### 每个属性的字段
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| `type` | `'string' \| 'number' \| 'boolean' \| 'object' \| 'array'` | 值类型 |
+| `required` | `true` | 标记为必填(影响类型推导) |
+| `description` | `string` | 发送给模型的描述 |
+| `enum` | `string[]` | 允许的枚举值 |
+| `properties` | `SchemaSpec` | 嵌套属性(type 为 object 时) |
+| `items` | `SchemaProp` | 数组元素 schema(type 为 array 时) |
+
+## execute 函数
+
+`execute` 接收经过校验的 `args`(类型自动推导)和一个 `exec` 上下文对象:
+
+```typescript
+async execute(args, exec) {
+  // args: 根据 parameters 自动推导的类型
+  // exec: ToolExecution 对象,提供执行上下文
+
+  // 返回 ContentBlock 数组
+  return [{ type: 'text', text: 'result here' }]
+}
+```
+
+### 返回值
+
+`execute` 必须返回一个 `ContentBlock[]`,告诉模型 tool 的执行结果:
+
+```typescript
+// 文本结果
+return [{ type: 'text', text: 'file content here...' }]
+
+// 多个 block
+return [
+  { type: 'text', text: 'Found 3 matches:' },
+  { type: 'text', text: matchResults.join('\n') },
+]
+```
+
+### 参数校验
+
+`defineTool` 在调用 `execute` 之前会自动校验模型生成的参数。如果参数不合法,会抛出 `ToolArgsError`,框架将其转换为 `isError` 结果返回给模型,让模型自行修正。
+
+你不需要在 `execute` 里手动校验参数类型。
+
+## 展示层 (Presentation)
+
+Tool 可以定义 UI 渲染方法,用于在终端或 ACP 客户端中展示 tool call 和 result:
+
+```typescript
+defineTool({
+  name: 'bash',
+  // ...
+  presentCall(args) {
+    return {
+      intent: 'terminal',
+      title: `bash(${JSON.stringify(args.command).slice(0, 60)})`,
+    }
+  },
+  presentResult(args, result) {
+    return {
+      intent: 'terminal',
+      body: result.content.map(b => b.type === 'text' ? b.text : '').join(''),
+    }
+  },
+})
+```
+
+`presentCall` 和 `presentResult` 是**纯函数**,不能有副作用——UI 可能在流式传输中和会话回放中多次调用它们。
+
+## 注册与卸载
+
+`ctx.tools.register()` 返回值就是 disposer。但由于你在 `ctx` 上调用,框架已经自动追踪了这个注册——插件卸载时会自动移除 tool。你不需要手动调用 disposer。
+
+```typescript
+// 这样就够了:
+ctx.tools.register(defineTool({ /* ... */ }))
+
+// 不需要:
+// const dispose = ctx.tools.register(...)
+// ctx.on('dispose', dispose)
+```
+
+## 完整实战示例
+
+一个文件计数 tool:
+
+```typescript
+import type { Context } from 'cordis'
+import { defineTool } from '@deepseek-ai/dsh-tools'
+import { readdir } from 'node:fs/promises'
+
+export const name = 'file-counter'
+export const inject = ['tools']
+
+export function apply(ctx: Context) {
+  ctx.tools.register(defineTool({
+    name: 'count_files',
+    description: 'Count files in a directory.',
+    parameters: {
+      path: { type: 'string', required: true, description: 'Directory path' },
+      extension: { type: 'string', description: 'Filter by extension (e.g. ".ts")' },
+    },
+    async execute(args) {
+      const entries = await readdir(args.path, { withFileTypes: true })
+      let files = entries.filter(e => e.isFile())
+      if (args.extension) {
+        files = files.filter(f => f.name.endsWith(args.extension!))
+      }
+      return [{ type: 'text', text: `Found ${files.length} files.` }]
+    },
+  }))
+}
+```
+
+## 下一步
+
+- [插件配置](./config) — 让你的 tool 可配置
+- [能力三件套](../practice/) — 了解 seam/impl/consumer 模式

+ 152 - 0
website/zh-CN/develop/framework/events.md

@@ -0,0 +1,152 @@
+# 事件系统
+
+事件是 Cordis 插件间通信的核心机制。Harness 大量使用事件来实现松耦合的扩展点。
+
+## 基本用法
+
+### 监听事件
+
+```typescript
+ctx.on('event-name', (payload) => {
+  // 处理事件
+})
+```
+
+### 触发事件
+
+```typescript
+ctx.emit('event-name', payload)
+```
+
+## 事件模式
+
+Cordis 提供多种事件触发模式,适用于不同场景:
+
+### emit — 广播
+
+所有监听器并行执行,不关心返回值:
+
+```typescript
+// 触发
+ctx.emit('agent/turn-end', { agentId, turnIndex })
+
+// 监听
+ctx.on('agent/turn-end', ({ agentId, turnIndex }) => {
+  console.log(`Turn ${turnIndex} ended`)
+})
+```
+
+### bail — 短路
+
+依次调用监听器,第一个返回非 `undefined` 值的结果作为最终值:
+
+```typescript
+// 触发
+const result = ctx.bail('some-check', input)
+
+// 监听(返回值阻止后续监听器)
+ctx.on('some-check', (input) => {
+  if (shouldBlock(input)) return 'blocked'
+  // 返回 undefined 继续传递给下一个监听器
+})
+```
+
+### serial — 顺序执行
+
+所有监听器按注册顺序依次执行(异步安全):
+
+```typescript
+await ctx.serial('setup-phase', context)
+```
+
+### waterfall — 管道
+
+每个监听器接收前一个的输出,形成数据管道。**必须调用 `next()` 传递给下游**,不调用即为否决:
+
+```typescript
+// 触发
+const finalMessages = await ctx.waterfall('llm/pre-request', messages)
+
+// 监听(必须调用 next)
+ctx.on('llm/pre-request', async (messages, next) => {
+  // 可以修改 messages
+  messages.push(extraMessage)
+  // 必须调用 next() 传递给下一个监听器
+  return next(messages)
+})
+```
+
+::: warning
+Waterfall 监听器**必须调用 `next()`**。不调用 `next` 等于否决整个管道,这是故意为之的设计——用于实现拦截/网关逻辑。
+:::
+
+## Typed Events
+
+Harness 使用 TypeScript 声明合并来为事件提供类型安全:
+
+```typescript
+declare module 'cordis' {
+  interface Events {
+    'my-plugin/ready': (payload: { id: string }) => void
+    'my-plugin/check': (input: string) => boolean | undefined
+  }
+}
+
+// 现在 ctx.on('my-plugin/ready', ...) 和 ctx.emit('my-plugin/ready', ...)
+// 都有正确的类型推导
+```
+
+## 命名约定
+
+Harness 事件遵循 `namespace/action` 命名:
+
+```
+agent/pre-step      — agent 执行一步之前
+agent/post-step     — agent 执行一步之后
+tool/call           — tool 被调用
+tool/result         — tool 返回结果
+llm/pre-request     — LLM 请求发送前
+session/event       — 会话事件被记录
+compact/start       — 压缩开始
+compact/end         — 压缩结束
+```
+
+## 事件也是效果
+
+通过 `ctx.on()` 注册的监听器会在插件卸载时自动移除:
+
+```typescript
+export function apply(ctx: Context) {
+  // 这个监听器在插件 dispose 时自动清理
+  ctx.on('agent/turn-end', handler)
+}
+```
+
+## 实战示例:日志插件
+
+一个记录所有 tool 调用的简单插件:
+
+```typescript
+import type { Context } from 'cordis'
+
+export const name = 'tool-logger'
+
+export function apply(ctx: Context) {
+  ctx.on('tool/call', ({ name, args }) => {
+    console.log(`[tool] ${name}(${JSON.stringify(args)})`)
+  })
+
+  ctx.on('tool/result', ({ name, result }) => {
+    const text = result.content
+      .filter(b => b.type === 'text')
+      .map(b => b.text)
+      .join('')
+    console.log(`[tool result] ${text.slice(0, 100)}`)
+  })
+}
+```
+
+## 下一步
+
+- [能力三件套](../practice/) — 事件在 capability seam 中的角色
+- [LLM 适配器](../practice/llm-adapter) — 实现一个完整的 LLM 后端

+ 139 - 0
website/zh-CN/develop/framework/index.md

@@ -0,0 +1,139 @@
+# 插件与生命周期
+
+深入了解 Cordis 插件模型和生命周期状态机。
+
+## Fiber 状态机
+
+每个被加载的插件对应一个 **Fiber**(作用域)。Fiber 有以下状态:
+
+```
+PENDING → LOADING → ACTIVE
+                 ↘ FAILED
+ACTIVE → UNLOADING → DISPOSED
+```
+
+| 状态 | 含义 |
+|------|------|
+| PENDING | 已声明但依赖未就绪 |
+| LOADING | 依赖就绪,正在执行 `apply` |
+| ACTIVE | 插件运行中 |
+| FAILED | `apply` 抛出异常 |
+| UNLOADING | 正在卸载,清理中 |
+| DISPOSED | 已完全卸载 |
+
+## 依赖驱动的加载
+
+声明了 `inject` 的插件不会立即加载,而是等待依赖的服务就绪:
+
+```typescript
+export const inject = ['tools', 'llm']
+
+export function apply(ctx: Context) {
+  // 到这里时,ctx.tools 和 ctx.llm 一定存在
+}
+```
+
+如果依赖的服务消失(比如提供者被热替换),插件会被自动卸载(ACTIVE → DISPOSED),待服务恢复后重新加载。
+
+## 自动清理机制
+
+通过 `ctx` 做的任何注册,在插件卸载时都会自动撤销:
+
+```typescript
+export function apply(ctx: Context) {
+  // 事件监听——卸载时自动移除
+  ctx.on('some-event', handler)
+
+  // 自定义资源——卸载时调用返回的函数
+  ctx.effect(() => {
+    const connection = createConnection()
+    return () => connection.close()
+  })
+}
+```
+
+以下操作都会被自动追踪和清理:
+- `ctx.on(event, handler)` — 事件监听
+- `ctx.tools.register(tool)` — tool 注册
+- `ctx.llm.registerAdapter(names, adapter)` — LLM 适配器注册
+- `ctx.effect(() => cleanup)` — 自定义资源
+
+插件卸载时,这些注册按倒序逐个撤销。
+
+## 嵌套上下文
+
+`ctx.plugin()` 创建子 Fiber,它继承父上下文但有独立的生命周期:
+
+```typescript
+export function apply(ctx: Context) {
+  // 注册一个子插件
+  ctx.plugin(childPlugin)
+
+  // 子插件有自己的 Fiber,父卸载时子也卸载
+}
+```
+
+## dispose 语义
+
+当你需要提前终止一个插件实例:
+
+```typescript
+const fiber = ctx.plugin(myPlugin)
+
+// 之后可以手动 dispose
+fiber.dispose()
+```
+
+`dispose` 保证:
+1. 该插件注册的所有东西被撤销
+2. 它的子插件也被递归卸载
+3. 所有异步清理完成后 Promise resolve
+
+## 热替换 (HMR)
+
+在开发环境中(`cordis.yml` 加载了 `@cordisjs/plugin-hmr`),修改插件源文件会自动触发:
+
+1. 卸载旧插件(清理所有注册)
+2. 重新加载新代码
+3. 执行新的 `apply`
+
+因为所有注册都会被自动清理,所以热替换天然安全——不会留下旧状态。
+
+## 实战:理解生命周期
+
+```typescript
+export function apply(ctx: Context) {
+  console.log('plugin loading')
+
+  ctx.on('ready', () => {
+    console.log('context ready')
+  })
+
+  ctx.on('dispose', () => {
+    console.log('plugin disposing')
+  })
+
+  ctx.effect(() => {
+    console.log('effect registered')
+    return () => console.log('effect cleaned up')
+  })
+}
+```
+
+加载时输出:
+```
+plugin loading
+effect registered
+context ready
+```
+
+卸载时输出(逆序):
+```
+plugin disposing
+effect cleaned up
+```
+
+## 下一步
+
+- [服务与依赖](./service) — 让你的插件对外提供能力
+- [事件系统](./events) — 插件间通信的核心机制

+ 147 - 0
website/zh-CN/develop/framework/service.md

@@ -0,0 +1,147 @@
+# 服务与依赖
+
+服务 (Service) 是插件对外暴露能力的方式。依赖 (inject) 是插件声明自己需要哪些服务。
+
+## 什么是服务
+
+在 Harness 中,`tools`、`llm`、`agents` 都是服务。服务是挂载在 `ctx` 上的命名能力:
+
+```typescript
+ctx.tools    // ToolRegistry 服务
+ctx.llm      // LLM 服务
+ctx.agents   // Agent 服务
+```
+
+任何插件都可以提供一个新服务,供其他插件使用。
+
+## 使用服务
+
+声明 `inject` 来使用已有服务:
+
+```typescript
+export const inject = ['tools']
+
+export function apply(ctx: Context) {
+  // ctx.tools 在这里一定存在且就绪
+  ctx.tools.register(/* ... */)
+}
+```
+
+框架保证:在 `apply` 执行时,`inject` 声明的服务已经全部就绪。如果服务还没准备好,你的插件会等着,不会执行。
+
+## 提供服务
+
+### 使用 Service 基类
+
+```typescript
+import { Service, type Context } from 'cordis'
+
+export default class MetricsService extends Service {
+  static inject = ['llm']  // 本服务也可以依赖其他服务
+
+  constructor(ctx: Context) {
+    super(ctx, 'metrics')  // 'metrics' 是服务名
+  }
+
+  // 服务的公开方法
+  record(event: string, value: number) {
+    // ...
+  }
+}
+```
+
+加载这个插件后,其他插件就可以通过 `ctx.metrics` 访问它:
+
+```typescript
+export const inject = ['metrics']
+
+export function apply(ctx: Context) {
+  ctx.metrics.record('tool_call', 1)
+}
+```
+
+### 类型声明
+
+使用 TypeScript 声明合并让 `ctx.metrics` 有正确类型:
+
+```typescript
+import { Service, type Context } from 'cordis'
+
+declare module 'cordis' {
+  interface Context {
+    metrics: MetricsService
+  }
+}
+
+export default class MetricsService extends Service {
+  constructor(ctx: Context) {
+    super(ctx, 'metrics')
+  }
+
+  record(event: string, value: number) { /* ... */ }
+}
+```
+
+## 依赖的行为
+
+### 必选依赖 vs 可选依赖
+
+```typescript
+// 必选:服务不存在时,插件不会加载
+export const inject = ['tools']
+
+// 可选:服务不存在时,插件仍然加载,但 ctx.xxx 可能是 undefined
+export const inject = { optional: ['metrics'] }
+```
+
+### 服务消失时的行为
+
+如果一个必选依赖的服务在运行时消失(比如提供者被卸载):
+
+1. 依赖它的插件自动 dispose
+2. 当服务重新出现时,插件自动重新加载
+
+这保证了不会出现"调用一个已不存在的服务"的情况。
+
+## 服务隔离
+
+`cordis.yml` 支持服务隔离——同一个服务可以有多个实例,不同插件组看到不同实例:
+
+```yaml
+- id: group-a
+  name: 'group:'
+  config:
+    - name: '@deepseek-ai/dsh-bash-local'
+      config:
+        timeoutMs: 5000
+    - name: './src/plugin-a.ts'
+
+- id: group-b
+  name: 'group:'
+  config:
+    - name: '@deepseek-ai/dsh-bash-local'
+      config:
+        timeoutMs: 60000
+    - name: './src/plugin-b.ts'
+```
+
+`plugin-a` 和 `plugin-b` 各自看到自己组内的 bash 实例,互不影响。
+
+## Harness 内置服务一览
+
+| 服务名 | 提供者 | 用途 |
+|--------|--------|------|
+| `tools` | dsh-tools | Tool 注册表 |
+| `llm` | dsh-llm | LLM 调用 + 适配器注册 |
+| `agents` | dsh-agent | Agent 实例管理 |
+| `session` | dsh-session | 会话事件流 |
+| `systemPrompt` | dsh-system-prompt | 系统提示词组装 |
+| `bash` | dsh-bash-local | Bash 命令执行 |
+| `fs` | dsh-fs-local | 文件系统操作 |
+| `subagent` | dsh-subagent | 子代理委派 |
+| `persistence` | dsh-session-persistence | 会话持久化 |
+
+## 下一步
+
+- [事件系统](./events) — 插件间松耦合通信
+- [能力三件套](../practice/) — 服务在 seam 模式中的应用

+ 156 - 0
website/zh-CN/develop/practice/index.md

@@ -0,0 +1,156 @@
+# 能力的三层拆分
+
+当一个能力(插件)足够通用(比如"执行 bash 命令"),Harness 会把它拆成三个包:**接口**、**实现**、**消费者**。这样可以独立替换其中任何一层。
+
+## 以 Bash 为例
+
+考虑 "Bash 执行" 这个能力:
+
+- **接口** (`dsh-bash`) — 定义"bash 执行"长什么样:输入是什么、输出是什么
+- **实现** (`dsh-bash-local`) — 真正在本地跑命令的代码
+- **消费者** (`dsh-tool-bash`) — 把这个能力包装成模型能调用的 tool
+
+```
+┌─────────────┐     ┌──────────────────┐     ┌──────────────┐
+│  dsh-bash   │────▶│  dsh-bash-local  │     │ dsh-tool-bash│
+│  (接口)     │     │  (实现)          │     │ (消费者/tool)│
+└─────────────┘     └──────────────────┘     └──────────────┘
+       ▲                                            │
+       └────────────────────────────────────────────┘
+                    inject: ['bash']
+```
+
+## 拆分的好处
+
+### 具体实现可替换
+
+同一个接口可以有多种实现。用户通过 `cordis.yml` 选择:
+
+```yaml
+# 本地执行
+- name: '@deepseek-ai/dsh-bash-local'
+
+# 或:远程沙箱执行(未来)
+# - name: '@deepseek-ai/dsh-bash-remote'
+#   config:
+#     endpoint: 'https://sandbox.example.com'
+```
+
+接口不变、tool 不变,只换实现。
+
+### 独立演进
+
+- 接口定义稳定后很少改动
+- 实现可以独立优化(性能、安全)
+- 消费者(tool)可以调整对模型的呈现方式
+
+### 依赖解耦
+
+- 实现 depend on 接口
+- 消费者 depend on 接口
+- 实现和消费者**互不依赖**
+
+## Harness 中内置的三件套
+
+| 能力 | 接口 (seam) | 实现 | 消费者 (tool) |
+|------|-------------|------|---------------|
+| Bash | `dsh-bash` | `dsh-bash-local` | `dsh-tool-bash` |
+| 文件系统 | `dsh-fs` | `dsh-fs-local` + `dsh-fs-policy` | `dsh-tool-fs` |
+| Web | `dsh-web` | `dsh-web-fetch-local` / `dsh-web-search-*` | `dsh-tool-web` |
+| 子代理 | `dsh-subagent` | `dsh-subagent-spawn` / `dsh-subagent-fork` | `dsh-tool-subagent` |
+| 压缩 | `dsh-compact` | `dsh-compact-basic` | (内置于 agent-loop) |
+
+## 开发你自己的三件套
+
+### 第一步:定义接口
+
+```typescript
+// packages/my-cap/my-cap/src/index.ts
+import { Service, type Context } from 'cordis'
+
+declare module 'cordis' {
+  interface Context {
+    myCap: MyCapService
+  }
+}
+
+export abstract class MyCapService extends Service {
+  constructor(ctx: Context) {
+    super(ctx, 'myCap')
+  }
+
+  /** 执行能力的核心方法 */
+  abstract execute(request: MyCapRequest): Promise<MyCapResult>
+}
+
+export interface MyCapRequest {
+  input: string
+}
+
+export interface MyCapResult {
+  output: string
+}
+```
+
+### 第二步:编写实现
+
+```typescript
+// packages/my-cap/my-cap-local/src/index.ts
+import type { Context } from 'cordis'
+import { MyCapService, type MyCapRequest, type MyCapResult } from '@deepseek-ai/dsh-my-cap'
+
+class MyCapLocal extends MyCapService {
+  async execute(request: MyCapRequest): Promise<MyCapResult> {
+    // 具体实现
+    return { output: request.input.toUpperCase() }
+  }
+}
+
+export const name = 'my-cap-local'
+
+export function apply(ctx: Context) {
+  ctx.plugin(MyCapLocal)
+}
+```
+
+### 第三步:编写消费者 (tool)
+
+```typescript
+// packages/my-cap/tool-my-cap/src/index.ts
+import type { Context } from 'cordis'
+import { defineTool } from '@deepseek-ai/dsh-tools'
+
+export const name = 'tool-my-cap'
+export const inject = ['tools', 'myCap']
+
+export function apply(ctx: Context) {
+  ctx.tools.register(defineTool({
+    name: 'my_cap',
+    description: 'Execute my capability.',
+    parameters: {
+      input: { type: 'string', required: true },
+    },
+    async execute(args) {
+      const result = await ctx.myCap.execute({ input: args.input })
+      return [{ type: 'text', text: result.output }]
+    },
+  }))
+}
+```
+
+### 在 cordis.yml 中组合
+
+```yaml
+- name: '@deepseek-ai/dsh-my-cap-local'
+- name: '@deepseek-ai/dsh-tool-my-cap'
+```
+
+## 设计要点
+
+- **不要预防性拆分** — 只有当你确实需要可替换实现时才拆三件套。一个简单的 tool 插件不需要拆分。
+- **接口定义 Request/Result 类型** — 实现和消费者只依赖接口包。
+- **Explicit > Implicit** — 实现中的默认值处理应该是显式的 `resolve(request): Spec` 步骤,不是隐藏在 `run()` 中的 `?? default`。
+
+## 下一步
+
+- [LLM 适配器](./llm-adapter) — 实现一个 LLM 后端(最常见的 seam 扩展)

+ 169 - 0
website/zh-CN/develop/practice/llm-adapter.md

@@ -0,0 +1,169 @@
+# LLM 适配器
+
+本文介绍如何为 Harness 接入一个新的 LLM 提供方。
+
+## 概述
+
+LLM 适配器是一个继承 `LlmAdapter` 的类,实现 `stream()` 方法,将 Harness 的统一请求格式转换为具体 API 的调用。
+
+## 最小实现
+
+```typescript
+import type { Context } from 'cordis'
+import { LlmAdapter, type GenerateOptions, type StreamChunk } from '@deepseek-ai/dsh-llm'
+
+class MyAdapter extends LlmAdapter {
+  private apiKey: string
+
+  constructor(apiKey: string) {
+    super()
+    this.apiKey = apiKey
+  }
+
+  async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
+    // 1. 将 options.messages 转换为你的 API 格式
+    // 2. 调用 API(流式)
+    // 3. 将 API 响应转换为 StreamChunk 序列
+  }
+}
+
+export interface Config {
+  apiKey: string
+  models: string[]
+}
+
+export const name = 'my-llm-adapter'
+export const inject = ['llm']
+
+export function apply(ctx: Context, config: Config) {
+  const adapter = new MyAdapter(config.apiKey)
+  ctx.llm.registerAdapter(config.models, adapter)
+}
+```
+
+## StreamChunk 协议
+
+`stream()` 必须按以下协议 yield chunk:
+
+```typescript
+// 1. 每个内容块以 block-start 开始
+yield { type: 'block-start', index: 0, blockType: 'text' }
+
+// 2. 文本块使用 text-delta
+yield { type: 'text-delta', index: 0, text: 'Hello' }
+yield { type: 'text-delta', index: 0, text: ' world' }
+
+// 3. 每个内容块以 block-end 结束(携带完整 block)
+yield {
+  type: 'block-end',
+  index: 0,
+  block: { type: 'text', text: 'Hello world' },
+}
+
+// 4. Tool call 块
+yield { type: 'block-start', index: 1, blockType: 'tool-call' }
+yield {
+  type: 'tool-call-delta',
+  index: 1,
+  id: CallId('call-123'),
+  name: 'bash',
+  argumentsDelta: '{"command":"ls"}',
+}
+yield {
+  type: 'block-end',
+  index: 1,
+  block: {
+    type: 'tool-call',
+    id: CallId('call-123'),
+    name: 'bash',
+    arguments: '{"command":"ls"}',
+  },
+}
+
+// 5. Token 用量
+yield { type: 'usage', usage: { inputTokens: 100, outputTokens: 50 } }
+
+// 6. 结束原因
+yield { type: 'finish', reason: { kind: 'stop' } }
+// 或: { kind: 'tool-calls' } 表示模型想调用 tool
+```
+
+### 关键规则
+
+- 每个 `block-start` 必须有对应的 `block-end`
+- `index` 从 0 递增,标识内容块顺序
+- `tool-call-delta` 的 `argumentsDelta` 是 JSON 字符串的增量(可以一次 yield 全部,也可以分多次)
+- `finish` 必须是最后一个 chunk
+- `usage` 在 `finish` 之前 yield
+
+## GenerateOptions
+
+`stream()` 接收的请求包含:
+
+```typescript
+interface GenerateOptions {
+  /** 模型名 */
+  model: string
+  /** 对话历史 */
+  messages: Message[]
+  /** 可用的 tool 列表 */
+  tools?: ToolSpec[]
+  /** 系统提示词 */
+  system?: string
+  /** 最大输出 token */
+  maxTokens?: number
+  /** 温度 */
+  temperature?: number
+}
+```
+
+你的适配器需要将这些映射到具体 API 的参数。
+
+## 注册适配器
+
+```typescript
+ctx.llm.registerAdapter(['model-name-1', 'model-name-2'], adapter)
+```
+
+第一个参数是该适配器支持的模型名列表。当用户在 `cordis.yml` 中配置 `model: model-name-1` 时,框架会路由到这个适配器。
+
+## 在 cordis.yml 中使用
+
+```yaml
+- id: my-llm
+  name: './src/my-llm-adapter.ts'
+  config:
+    apiKey: !!js process.env.MY_API_KEY
+    models:
+      - my-model-v1
+      - my-model-v2
+
+- id: stdio-agent
+  name: '@deepseek-ai/dsh-stdio-agent'
+  config:
+    model: my-model-v1  # 引用上面注册的模型名
+```
+
+## 实战参考
+
+仓库中有两个完整实现可供参考:
+
+- `packages/llm/llm-deepseek/` — DeepSeek API 适配器(OpenAI 兼容格式)
+- `packages/llm/llm-pi-ai/` — Pi AI 适配器(不同的 API 格式)
+- `examples/echo-agent/src/mock-llm.ts` — 最简 mock 适配器(教学用)
+
+mock 适配器是学习 StreamChunk 协议的最佳起点——它用纯本地逻辑演示了完整的 chunk 序列。
+
+## 错误处理
+
+适配器中的异常会被 agent-loop 捕获并转化为 `LlmError`,告知上层。不需要在 `stream()` 内部做错误恢复——让异常冒泡即可。
+
+```typescript
+async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
+  const response = await fetch(this.endpoint, { /* ... */ })
+  if (!response.ok) {
+    throw new Error(`API error: ${response.status}`)
+  }
+  // ... 正常流式处理
+}
+```

+ 342 - 0
website/zh-CN/guide/config.md

@@ -0,0 +1,342 @@
+# 配置文件
+
+Harness 使用 `cordis.yml` 描述一个 Agent 加载哪些插件、以什么参数运行。
+
+## 从例子开始
+
+### echo-agent 的配置
+
+这是一开始的第一个 Agent 的完整配置:
+
+```yaml
+# 热替换:修改代码后自动重载,不用手动重启
+- id: hmr
+  name: '@cordisjs/plugin-hmr'
+  config:
+    root: ['.']
+
+# Mock 模型:从本地 `.ts` 文件加载,注册一个名为 `mock-llm` 的工具
+# 本地模拟 LLM 响应,不联网
+- id: mock-llm
+  name: './src/mock-llm.ts'
+
+# Echo 工具:收到文本后转大写返回
+- id: echo-tool
+  name: './src/echo-tool.ts'
+
+# Bash 执行器:从 npm 包 `@deepseek-ai/dsh-bash-local`加载,提供 bash 命令执行能力
+- id: bash
+  name: '@deepseek-ai/dsh-bash-local'
+
+# 应用主体:把 session 管理、tool 调度、agent loop 等组装成一个可交互的终端 Agent
+# 只需告诉它用哪个模型 (`model`)、什么人设 (`persona`)
+- id: stdio-agent
+  name: '@deepseek-ai/dsh-stdio-agent'
+  config:
+    model: mock-echo
+    persona: 'You are echo-agent, a demo agent.'
+    welcome: 'echo-agent ready. Type a message ("echo <text>" triggers the tool).'
+    persistenceRoot: './.sessions'
+```
+
+### coding-agent 的配置
+
+真实场景——接入 DeepSeek API,带完整工具链:
+
+```yaml
+# 热替换:同上,开发时自动重载
+- id: hmr
+  name: '@cordisjs/plugin-hmr'
+  config:
+    root: ['.']
+
+# LLM 后端:从 npm 包加载,具备接入 DeepSeek API 能力
+# `!!js` 从环境变量读取密钥,不会写进配置文件
+# `models` 声明该适配器能处理哪些模型名
+- id: llm-deepseek
+  name: '@deepseek-ai/dsh-llm-deepseek'
+  config:
+    apiKey: !!js process.env.DEEPSEEK_API_KEY
+    baseURL: !!js process.env.DEEPSEEK_BASE_URL
+    models:
+      - deepseek-v4-pro
+      - deepseek-v4-flash
+
+# Bash 执行器:让 Agent 能跑 shell 命令
+# timeoutMs 设置单条命令的超时时间
+- id: bash
+  name: '@deepseek-ai/dsh-bash-local'
+  config:
+    timeoutMs: 60000
+
+# 应用主体:和 echo-agent 一样的框架,只是配置不同
+# `model` 指定默认使用哪个模型(要和上面 models 列表里的名字对应)
+# `persona` 是系统提示词,{{model}} 会被替换为实际模型名
+# `resumeSessionId` 设了就恢复旧对话,没设就每次新建
+- id: stdio-agent
+  name: '@deepseek-ai/dsh-stdio-agent'
+  config:
+    model: deepseek-v4-flash
+    resumeSessionId: !!js process.env.RESUME_SESSION_ID
+    persistenceRoot: './.sessions'
+    welcome: 'agent REPL ready. Give it a coding task.'
+    persona: |
+      You are coding-agent, a coding assistant powered by the {{model}} model.
+      Verify your work by running the code or tests. Keep answers brief and factual.
+
+# 自动压缩:对话太长时自动总结旧内容,腾出上下文空间
+# contextWindow 是模型能看到的 token 上限
+# thresholdRatio 超过这个比例就触发压缩
+- id: compact-basic
+  name: '@deepseek-ai/dsh-compact-basic'
+  config:
+    contextWindow: 128000
+    thresholdRatio: 0.8
+    retainTokens: 20480
+    maxTokens: 8192
+
+# 子代理:把子任务分配给独立的 Agent 去做
+# subagent 是服务注册,spawn/fork 是两种委派方式:
+#   spawn — 全新子代理,不知道父级在聊什么
+#   fork  — 继承父级对话上下文的子代理
+# tool-subagent 把委派能力暴露给模型,toolName 是模型看到的工具名
+- id: subagent
+  name: '@deepseek-ai/dsh-subagent'
+
+- id: subagent-spawn
+  name: '@deepseek-ai/dsh-subagent-spawn'
+  config:
+    providerName: spawn
+
+- id: subagent-fork
+  name: '@deepseek-ai/dsh-subagent-fork'
+  config:
+    providerName: fork
+
+- id: tool-subagent
+  name: '@deepseek-ai/dsh-tool-subagent'
+  config:
+    provider: spawn
+    toolName: subagent
+
+- id: tool-subagent-fork
+  name: '@deepseek-ai/dsh-tool-subagent'
+  config:
+    provider: fork
+    toolName: subagent_fork
+
+# 任务追踪:模型可以用 todo_write 记录和更新任务清单
+- id: tool-todo
+  name: '@deepseek-ai/dsh-tool-todo'
+
+# 文件系统:让 Agent 能读写编辑文件
+# fs-local 提供本地文件操作能力,cwd 是工作目录
+# fs-policy 是安全策略——必须先读才能写,防止模型盲写
+# tool-fs 把能力暴露给模型(read / write / edit 三个工具)
+- id: fs-local
+  name: '@deepseek-ai/dsh-fs-local'
+  config:
+    cwd: !!js process.cwd()
+
+- id: fs-policy
+  name: '@deepseek-ai/dsh-fs-policy'
+
+- id: tool-fs
+  name: '@deepseek-ai/dsh-tool-fs'
+```
+
+和 echo-agent 对比:同一个 `dsh-stdio-agent` 应用主体,只是把 mock 换成了真实 API,加上了更多工具插件。
+
+## 语法详解
+
+### 插件声明字段
+
+每个插件条目支持以下字段:
+
+| 字段 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| `name` | string | 是 | 插件来源(npm 包名或相对路径) |
+| `id` | string | 否 | 实例标识符,用于日志和调试 |
+| `config` | object | 否 | 传递给插件的配置 |
+| `disabled` | boolean | 否 | 设为 `true` 临时禁用该插件 |
+
+### 插件来源 (`name`)
+
+**npm 包** — 已安装的 `@deepseek-ai/dsh-*` 包或第三方包:
+
+```yaml
+- name: '@deepseek-ai/dsh-llm-deepseek'
+```
+
+**相对路径** — 本地 TypeScript 文件(相对于 `cordis.yml` 所在目录):
+
+```yaml
+- name: './src/my-tool.ts'
+```
+
+### 环境变量 (`!!js`)
+
+用 `!!js` 标签在配置中引用运行时表达式:
+
+```yaml
+config:
+  apiKey: !!js process.env.DEEPSEEK_API_KEY
+  cwd: !!js process.cwd()
+```
+
+::: warning
+是 `!!js`(两个感叹号),不是 `!js`。写错了会静默失败。
+:::
+
+环境变量从仓库根目录的 `.env` 文件自动加载(已被 gitignore)。
+
+### 禁用插件
+
+不想删配置但暂时不加载?加一行 `disabled`:
+
+```yaml
+- id: compact-basic
+  name: '@deepseek-ai/dsh-compact-basic'
+  disabled: true
+  config:
+    contextWindow: 128000
+```
+
+## 各插件配置参考
+
+### stdio-agent(标准应用主体)
+
+**包名:** `@deepseek-ai/dsh-stdio-agent`
+
+| 字段 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| `model` | string | **必填** | 使用的模型名,需与 LLM 适配器注册的名字一致 |
+| `persona` | string | `''` | 系统提示词。支持 `{{model}}` 等模板变量 |
+| `toolOrder` | string[] | — | 模型看到的工具顺序。省略则按字母排序 |
+| `persistenceRoot` | string | `'./.sessions'` | 会话日志存储目录 |
+| `welcome` | string | `'ready.'` | 启动时显示的欢迎信息 |
+| `resumeSessionId` | string | — | 恢复指定会话 ID。留空则每次新建 |
+
+### llm-deepseek(DeepSeek 适配器)
+
+**包名:** `@deepseek-ai/dsh-llm-deepseek`
+
+| 字段 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| `apiKey` | string | `$DEEPSEEK_API_KEY` | API 密钥。省略则从环境变量读取 |
+| `baseURL` | string | `$DEEPSEEK_BASE_URL` 或官方地址 | API 端点 |
+| `models` | string[] | `['deepseek-v4-flash', 'deepseek-v4-pro']` | 注册的模型名列表 |
+| `thinking` | `'enabled'` \| `'disabled'` | `'enabled'` | 是否开启思维链 |
+| `reasoningEffort` | `'high'` \| `'max'` | — | 思维链深度(仅 thinking 开启时有效) |
+
+### bash-local(Bash 执行器)
+
+**包名:** `@deepseek-ai/dsh-bash-local`
+
+| 字段 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| `cwd` | string | `process.cwd()` | 命令执行的工作目录 |
+| `timeoutMs` | number | `120000` | 单条命令的超时时间(毫秒) |
+| `maxTimeoutMs` | number | `600000` | 单条命令超时的上限(模型不能请求更久) |
+| `maxOutputBytes` | number | `64000` | 单次输出的内存上限(超出后溢出到临时文件) |
+| `graceMs` | number | `3000` | kill 时从 SIGTERM 到 SIGKILL 的等待时间 |
+
+### compact-basic(自动压缩)
+
+**包名:** `@deepseek-ai/dsh-compact-basic`
+
+| 字段 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| `contextWindow` | number | **必填** | 模型的上下文窗口大小(token) |
+| `thresholdRatio` | number | **必填** | token 占用超过此比例时触发压缩(0-1) |
+| `retainTokens` | number | **必填** | 压缩后至少保留多少 token 的近期内容 |
+| `maxTokens` | number | **必填** | 总结时的最大输出 token |
+| `summarizationModel` | string | `''`(用当前模型) | 专门用于总结的模型名 |
+| `compactionRetries` | number | **必填** | 首次压缩后仍超标时的额外重试次数 |
+| `auto` | boolean | `true` | 是否自动在每步前检查并触发压缩 |
+| `charsPerToken` | number | `4` | 每 token 估算字符数。中文应设 1-2 |
+
+### fs-local(文件系统)
+
+**包名:** `@deepseek-ai/dsh-fs-local`
+
+| 字段 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| `cwd` | string | `process.cwd()` | 工作目录,相对路径以此为基准 |
+
+### fs-policy(文件系统策略)
+
+**包名:** `@deepseek-ai/dsh-fs-policy`
+
+无配置项。加载即启用"必须先读才能写"的安全策略。
+
+### tool-fs(文件系统工具)
+
+**包名:** `@deepseek-ai/dsh-tool-fs`
+
+无配置项。加载后向模型暴露 `read`、`write`、`edit` 三个工具。
+
+### tool-web(Web 工具)
+
+**包名:** `@deepseek-ai/dsh-tool-web`
+
+| 字段 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| `search` | boolean | `true` | 是否注册 `web_search` 工具 |
+| `fetch` | boolean | `true` | 是否注册 `web_fetch` 工具 |
+| `searchMaxResults` | number | `8` | 单次搜索返回的最大结果数 |
+
+### subagent-spawn / subagent-fork(子代理后端)
+
+**包名:** `@deepseek-ai/dsh-subagent-spawn` / `@deepseek-ai/dsh-subagent-fork`
+
+| 字段 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| `providerName` | string | `'spawn'` / `'fork'` | 注册到子代理服务的 provider 名称 |
+
+### tool-subagent(子代理工具)
+
+**包名:** `@deepseek-ai/dsh-tool-subagent`
+
+| 字段 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| `provider` | string | **必填** | 使用哪个 provider(如 `spawn`、`fork`) |
+| `toolName` | string | `'subagent'` | 暴露给模型的工具名。多次加载时必须不同 |
+| `agentOptions.model` | string | — | 子代理使用的模型名(省略则继承父代理) |
+
+### tool-todo(任务清单)
+
+**包名:** `@deepseek-ai/dsh-tool-todo`
+
+无配置项。加载后向模型暴露 `todo_write` 工具。
+
+### hmr(热替换)
+
+**包名:** `@cordisjs/plugin-hmr`
+
+| 字段 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| `root` | string[] | **必填** | 监听文件变更的目录列表 |
+
+::: tip
+hmr 仅用于开发环境。它需要 `node --expose-internals` 启动参数,`demo:*` 脚本已自动添加。
+:::
+
+---
+
+## 加载顺序
+
+`cordis.yml` 的顺序就是加载顺序。推荐:
+
+1. **hmr** — 热替换(仅开发时需要)
+2. **LLM 适配器** — 模型后端
+3. **执行器** — bash、fs 等能力提供者
+4. **应用主体** — `dsh-stdio-agent` 或 `dsh-acp-agent`
+5. **附加插件** — compact、subagent、todo 等
+
+应用主体内部已经捆绑了核心能力(session、tools、agent-loop),不需要手动加载。
+
+## 下一步
+
+- [开发插件](../develop/basic/) — 编写自己的插件
+- [API 参考](../api/) — 查看各插件完整接口

+ 47 - 0
website/zh-CN/guide/index.md

@@ -0,0 +1,47 @@
+# 介绍
+
+DeepSeek Harness 是一个**插件化的 Agent 开发框架**,基于 [Cordis](https://github.com/cordiverse/cordis) 微内核构建。它的核心理念是:**一切皆插件**。
+
+## 它是什么
+
+Harness 将一个 AI Agent(智能体) 所需要的所有能力——LLM 调用、工具执行、会话管理、子任务分配——全部构建为可组合的插件。你通过一个 `cordis.yml` 配置文件来声明加载哪些插件、使用什么参数,就能组装出一个完整的 Agent。
+
+```yaml
+# 选择 LLM 后端
+- name: '@deepseek-ai/dsh-llm-deepseek'
+  config:
+    apiKey: !!js process.env.DEEPSEEK_API_KEY
+
+# 选择应用模板
+- name: '@deepseek-ai/dsh-stdio-agent'
+  config:
+    model: deepseek-v4-flash
+```
+
+## 适合谁
+
+### 应用使用者
+
+如果你只是想用一个现成的 Agent 应用(如编程助手、对话代理),你需要的全部操作就是:
+
+1. 复制一个 example 模板
+2. 填写 API key
+3. 运行
+
+不需要写任何代码。详见 [快速开始](./quickstart)。
+
+### 插件开发者
+
+如果你想为 Agent 添加新能力——一个自定义 tool、一个新的 LLM 适配器、一个新的执行后端——你需要编写一个插件。Harness 提供了清晰的扩展接口和类型安全的开发体验。详见 [开发](../develop/basic/)。
+
+## 核心特性
+
+- **只需要配置** — `cordis.yml` 决定能力集合,换模型、加工具只需改一行
+- **随时替换 (HMR)** — 开发时修改插件代码,无需重启进程
+
+## 技术栈
+
+- **运行时**: Node.js >= 24
+- **语言**: TypeScript (ESM)
+- **框架**: Cordis
+- **包管理**: pnpm workspaces

+ 98 - 0
website/zh-CN/guide/quickstart.md

@@ -0,0 +1,98 @@
+# 快速开始
+
+本指南带你在 5 分钟内跑起一个 Agent。
+
+## 环境准备
+
+- [Node.js](https://nodejs.org/) >= 24
+- [pnpm](https://pnpm.io/) >= 9
+
+```sh
+# 确认版本
+node -v   # v24.x 或更高
+pnpm -v   # 9.x 或更高
+```
+
+## 第一步:运行 echo-agent
+
+echo-agent 不需要 API key,装好依赖就能跑。
+
+```sh
+# 克隆仓库
+git clone https://github.com/deepseek-harness/deepseek-harness.git
+cd deepseek-harness
+
+# 安装依赖
+pnpm install
+# 如果看到 ERR_PNPM_IGNORED_BUILDS,可以忽略——安装已经成功了。
+# 想消除这个提示可以跑一次: pnpm approve-builds
+
+# 启动 echo-agent
+pnpm run demo:echo
+```
+
+启动后你会看到:
+
+```
+echo-agent ready. Type a message ("echo <text>" triggers the tool).
+>
+```
+
+试着输入:
+
+```
+> echo hello world
+```
+
+你会看到模型发起了一次 tool call(工具调用),echo 工具将文本转为大写并返回:
+
+```
+[tool call] echo({"text":"hello world"})
+[tool result] ECHO: HELLO WORLD
+```
+
+恭喜!环境没问题。
+
+## 第二步:使用真实模型调用
+
+接下来接入真实的 DeepSeek 模型,跑一个完整的命令行 Agent。
+
+### 获取 API Key
+
+前往 [DeepSeek Platform](https://platform.deepseek.com/) 获取你的 API key。
+
+### 配置环境变量
+
+在仓库根目录创建 `.env` 文件(已被 gitignore):
+
+```sh
+DEEPSEEK_API_KEY=sk-your-key-here
+```
+
+### 启动 coding-agent
+
+```sh
+pnpm run demo:repl
+```
+
+```
+agent REPL ready. Give it a coding task.
+>
+```
+
+这就是一个完整的编程助手,它能读写文件、跑命令、拆分子任务。
+
+试着给它一个任务:
+
+```
+> 在当前目录创建一个 hello.js,内容是打印 "Hello from Harness!",然后运行它
+```
+
+## 回头看
+
+echo-agent 和 coding-agent 用的是同一个应用框架(`@deepseek-ai/dsh-stdio-agent`),区别只在 `cordis.yml`——换了哪些插件、填了什么配置。你以后定制自己的 Agent 也是同样的方式。
+
+## 下一步
+
+- [配置文件](./config) — 了解 `cordis.yml` 的完整语法
+- [开发插件](../develop/basic/) — 编写你自己的 tool 或后端

+ 21 - 0
website/zh-CN/index.md

@@ -0,0 +1,21 @@
+---
+layout: home
+hero:
+  name: DeepSeek Harness
+  text: 插件化 Agent 开发框架
+  tagline: 基于 Cordis 微内核,一切皆插件
+  actions:
+    - theme: brand
+      text: 快速开始
+      link: /zh-CN/guide/quickstart
+    - theme: alt
+      text: 开发插件
+      link: /zh-CN/develop/basic/
+features:
+  - title: 插件化架构
+    details: 基于 Cordis 效果系统,所有能力通过插件注册,加载即生效、卸载即还原。
+  - title: 配置即组合
+    details: 一个 cordis.yml 决定整个 Agent 的能力组合——换模型、加工具,只需改一行配置。
+  - title: 开箱即用
+    details: 内置 LLM 调用、文件读写、Bash 执行、子代理委派等完整工具链,复制模板即可运行。
+---