feat: add docs website

This commit is contained in:
lintianle
2026-07-09 16:07:58 +08:00
parent dabc5e6225
commit 87a1774fef
35 changed files with 3540 additions and 0 deletions

View File

@@ -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 追踪)。

View File

@@ -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
上下文压缩结束。

View File

@@ -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 中)
}
```

View File

@@ -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 或默认值 |

View File

@@ -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
}
}
```

View File

@@ -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')
```

View File

@@ -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 不变。

View File

@@ -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 层

View File

@@ -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 }
```

View File

@@ -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),实现跨进程恢复。

View File

@@ -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** — 需要上下文的子任务(如"基于我们刚才讨论的,去实现这个"),子代理继承父级的对话前缀

View File

@@ -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。

View File

@@ -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) — 子代理委派接口