Merge branch 'codex/invariant-package-registration-gate' into codex/package-invariant-checks

This commit is contained in:
Tianyi Cui
2026-07-20 19:55:46 +08:00
398 changed files with 15485 additions and 8603 deletions

5
website/.gitignore vendored
View File

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

View File

@@ -0,0 +1,191 @@
/** VitePress configuration for the locally projected documentation site. */
import type { DefaultTheme, PageData } from 'vitepress'
import type { ViteDevServer } from 'vite'
import { withMermaid } from 'vitepress-plugin-mermaid'
import { docsPages, type DocsPage } from '../docs.ts'
import { docsSourceFiles, projectDocs } from '../../scripts/project-doc-site.ts'
projectDocs()
const sectionOrder = [
'入门',
'基础',
'框架能力',
'实战',
'概念',
'生成参考',
'Cordis API',
'数据结构',
'开发手册',
'Guide',
'Basics',
'Framework',
'Practice',
'Concepts',
'Generated reference',
'Cordis Core API',
'Data structures',
'Cookbook',
]
function sidebar(collection: DocsPage['sidebar']): DefaultTheme.SidebarItem[] {
const pages = docsPages.filter(page => page.sidebar === collection)
const sections = new Map<string, DocsPage[]>()
for (const page of pages) {
const entries = sections.get(page.section) ?? []
entries.push(page)
sections.set(page.section, entries)
}
return [...sections.entries()]
.sort(([left], [right]) => sectionOrder.indexOf(left) - sectionOrder.indexOf(right))
.map(([text, entries]) => ({
text,
items: entries
.sort((left, right) => left.order - right.order)
.map(page => ({ text: page.label, link: `/${page.route.replace(/(?:index)?\.md$/, '')}` })),
}))
}
function watchCanonicalDocs(server: ViteDevServer): void {
const sources = docsSourceFiles()
server.watcher.add(sources)
server.watcher.on('change', (changed) => {
if (!sources.includes(changed)) return
projectDocs()
})
}
function escapeVueInterpolation(html: string): string {
return html.replaceAll('{{', '&#123;&#123;').replaceAll('}}', '&#125;&#125;')
}
const sharedTheme: Pick<DefaultTheme.Config, 'search' | 'socialLinks' | 'editLink'> = {
search: {
provider: 'local',
options: {
locales: {
root: {
translations: {
button: {
buttonText: '搜索文档',
buttonAriaLabel: '搜索文档',
},
modal: {
displayDetails: '显示详细列表',
resetButtonTitle: '清除搜索',
backButtonTitle: '关闭搜索',
noResultsText: '未找到相关结果',
footer: {
selectText: '选择',
selectKeyAriaLabel: '回车键',
navigateText: '切换',
navigateUpKeyAriaLabel: '上方向键',
navigateDownKeyAriaLabel: '下方向键',
closeText: '关闭',
closeKeyAriaLabel: 'Esc 键',
},
},
},
},
},
},
},
socialLinks: [
{ icon: 'github', link: 'https://github.com/deepseek-harness/deepseek-harness' },
],
editLink: {
pattern: ({ frontmatter }: PageData) => {
const data: unknown = frontmatter
const editSource: unknown = typeof data === 'object' && data !== null ? Reflect.get(data, 'editSource') : undefined
if (typeof editSource !== 'string') throw new Error('Projected documentation page has no editSource frontmatter.')
return `https://github.com/deepseek-harness/deepseek-harness/edit/master/${editSource}`
},
text: '在 GitHub 上编辑此页',
},
}
export default withMermaid({
title: 'DeepSeek Harness',
description: '用于构建 Agent Harness 的插件化 SDK',
cleanUrls: true,
srcDir: '.generated',
cacheDir: '.cache',
outDir: '.dist',
locales: {
root: {
label: '简体中文',
lang: 'zh-CN',
themeConfig: {
nav: [
{ text: '入门', link: '/guide/', activeMatch: '^/guide/' },
{ text: '开发', link: '/develop/basic/', activeMatch: '^/develop/' },
{ text: '参考', link: '/reference/', activeMatch: '^/reference/' },
],
sidebar: {
'/guide/': sidebar('zh-guide'),
'/develop/': sidebar('zh-develop'),
'/reference/': sidebar('zh-reference'),
},
outline: { label: '本页目录' },
docFooter: { prev: '上一篇', next: '下一篇' },
darkModeSwitchLabel: '外观',
lightModeSwitchTitle: '切换到浅色主题',
darkModeSwitchTitle: '切换到深色主题',
sidebarMenuLabel: '菜单',
returnToTopLabel: '返回顶部',
langMenuLabel: '切换语言',
skipToContentLabel: '跳至内容',
},
},
en: {
label: 'English',
lang: 'en-US',
link: '/en/',
themeConfig: {
nav: [
{ text: 'Guide', link: '/en/guide/', activeMatch: '^/en/guide/' },
{ text: 'Develop', link: '/en/develop/basic/', activeMatch: '^/en/develop/' },
{ text: 'Reference', link: '/en/reference/', activeMatch: '^/en/reference/' },
],
sidebar: {
'/en/guide/': sidebar('en-guide'),
'/en/develop/': sidebar('en-develop'),
'/en/reference/': sidebar('en-reference'),
},
editLink: {
pattern: ({ frontmatter }: PageData) => {
const data: unknown = frontmatter
const editSource: unknown = typeof data === 'object' && data !== null ? Reflect.get(data, 'editSource') : undefined
if (typeof editSource !== 'string') throw new Error('Projected documentation page has no editSource frontmatter.')
return `https://github.com/deepseek-harness/deepseek-harness/edit/master/${editSource}`
},
text: 'Edit this page on GitHub',
},
outline: { label: 'On this page' },
docFooter: { prev: 'Previous', next: 'Next' },
},
},
},
vite: {
plugins: [
{
name: 'deepseek-harness-doc-projector',
configureServer: watchCanonicalDocs,
},
],
},
markdown: {
config(md) {
const renderText = md.renderer.rules.text
const renderCode = md.renderer.rules.code_inline
if (renderText === undefined || renderCode === undefined) {
throw new Error('VitePress Markdown renderer is missing its text or inline-code rule.')
}
md.renderer.rules.text = (...args) => escapeVueInterpolation(renderText(...args))
md.renderer.rules.code_inline = (...args) => escapeVueInterpolation(renderCode(...args))
},
},
mermaid: {},
themeConfig: sharedTheme,
})

View File

@@ -1,130 +0,0 @@
{
"cordis": [
{
"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"
}
],
"harness": [
{
"text": "ctx.agentLoop",
"link": "/zh-CN/api/harness/agent-loop"
},
{
"text": "ctx.agents",
"link": "/zh-CN/api/harness/agents"
},
{
"text": "ctx.approval",
"link": "/zh-CN/api/harness/approval"
},
{
"text": "ctx.bash",
"link": "/zh-CN/api/harness/bash"
},
{
"text": "ctx.bashEnv",
"link": "/zh-CN/api/harness/bash-env"
},
{
"text": "ctx.codeRuntime",
"link": "/zh-CN/api/harness/code-runtime"
},
{
"text": "ctx.compact",
"link": "/zh-CN/api/harness/compact"
},
{
"text": "ctx.fs",
"link": "/zh-CN/api/harness/fs"
},
{
"text": "ctx.invariants",
"link": "/zh-CN/api/harness/invariants"
},
{
"text": "ctx.llm",
"link": "/zh-CN/api/harness/llm"
},
{
"text": "ctx.permission",
"link": "/zh-CN/api/harness/permission"
},
{
"text": "ctx.sandbox",
"link": "/zh-CN/api/harness/sandbox"
},
{
"text": "ctx.sessionPersistence",
"link": "/zh-CN/api/harness/session-persistence"
},
{
"text": "ctx.sessionQuery",
"link": "/zh-CN/api/harness/session-query"
},
{
"text": "ctx.sessions",
"link": "/zh-CN/api/harness/sessions"
},
{
"text": "ctx.skills",
"link": "/zh-CN/api/harness/skills"
},
{
"text": "ctx.spillStore",
"link": "/zh-CN/api/harness/spill-store"
},
{
"text": "ctx.subagents",
"link": "/zh-CN/api/harness/subagents"
},
{
"text": "ctx.systemPrompt",
"link": "/zh-CN/api/harness/system-prompt"
},
{
"text": "ctx.tasks",
"link": "/zh-CN/api/harness/tasks"
},
{
"text": "ctx.tokenMeter",
"link": "/zh-CN/api/harness/token-meter"
},
{
"text": "ctx.tools",
"link": "/zh-CN/api/harness/tools"
},
{
"text": "ctx.userInteraction",
"link": "/zh-CN/api/harness/user-interaction"
},
{
"text": "ctx.web",
"link": "/zh-CN/api/harness/web"
},
{
"text": "ctx.workflows",
"link": "/zh-CN/api/harness/workflows"
},
{
"text": "Events",
"link": "/zh-CN/api/harness/events"
}
]
}

View File

@@ -1,24 +0,0 @@
import { defineConfig } from 'vitepress'
import { zhCN } from './zh-CN'
export default defineConfig({
title: 'DeepSeek Harness',
description: '插件化 Agent 开发框架',
// The design essays (design/revertible-effects, design/context-model) carry
// real TeX; math: true wires markdown-it-mathjax3 into the pipeline.
// markdown-it-mathjax3 is pinned to ^4 (NOT 5.x): v5 injects a <style> tag
// per formula, which Vue's template compiler rejects ("Tags with side
// effect … are ignored in client component templates"); v4 emits pure SVG.
markdown: { math: true },
locales: {
'zh-CN': zhCN,
},
themeConfig: {
socialLinks: [
{ icon: 'github', link: 'https://github.com/deepseek-harness/deepseek-harness' },
],
},
})

View File

@@ -1,93 +0,0 @@
import type { DefaultTheme, LocaleSpecificConfig } from 'vitepress'
import apiSidebarData from './api-sidebar.json'
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' },
],
},
]
// The API section sidebar is GENERATED (scripts/gen-website-api.ts writes
// api-sidebar.json alongside the pages), so navigation can never drift from
// the generated page set. Only the hand-written hub link lives here.
const apiSidebar: DefaultTheme.SidebarItem[] = [
{
text: '框架 API',
items: [
{ text: '总览', link: '/zh-CN/api/' },
...apiSidebarData.cordis,
],
},
{
text: 'Harness API',
items: apiSidebarData.harness,
},
]
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,
},
// level [2,3]: the generated API pages put each member at h3 (### ctx.foo)
// under an h2 scope/statics group — both belong in the page outline.
outline: { label: '本页目录', level: [2, 3] },
docFooter: { prev: '上一篇', next: '下一篇' },
},
}

305
website/docs.ts Normal file
View File

@@ -0,0 +1,305 @@
/**
* Canonical publication manifest for the documentation website.
*
* Markdown stays in its owning repository tier. This manifest maps each
* canonical source into matching route trees for both site locales; when a
* translation is absent, both routes intentionally project the available
* source instead of copying Markdown.
*/
/** Locale key used by the VitePress site. */
export type DocsLocale = 'root' | 'en'
/** Sidebar collection rendered for one locale and top-level module. */
type DocsSidebar =
| 'zh-guide'
| 'zh-develop'
| 'zh-reference'
| 'en-guide'
| 'en-develop'
| 'en-reference'
/** A page projected into the VitePress source tree. */
export interface DocsPage {
/** VitePress locale whose route tree owns this projection. */
locale: DocsLocale
/** Language of the canonical source currently projected at this route. */
contentLocale: 'zh-CN' | 'en-US'
/** Repository-relative canonical Markdown source. */
source: string
/** VitePress route, including the `.md` suffix. */
route: string
/** Navigation label shown in the sidebar. */
label: string
/** Sidebar collection that owns the page, or null for a locale home page. */
sidebar: DocsSidebar | null
/** Section label within the sidebar. */
section: string
/** Stable order within the section. */
order: number
/** Additional repository paths that resolve to this page. */
sourceAliases?: string[]
}
interface MirroredPage {
source: string | Record<DocsLocale, string>
route: string
contentLocale: DocsPage['contentLocale'] | Record<DocsLocale, DocsPage['contentLocale']>
label: Record<DocsLocale, string>
sidebar: Record<DocsLocale, DocsSidebar | null>
section: Record<DocsLocale, string>
order: number
sourceAliases?: string[] | Partial<Record<DocsLocale, string[]>>
}
type PairedPage = Omit<MirroredPage, 'source' | 'contentLocale' | 'sourceAliases'> & {
/** English side of a sibling `foo.md` / `foo.zh.md` pair. */
source: string
/** Language-neutral repository aliases, such as the directory of an index page. */
sourceAliases?: string[]
}
function localized<T>(value: T | Record<DocsLocale, T>, locale: DocsLocale): T {
return typeof value === 'object' && value !== null && !Array.isArray(value)
? (value as Record<DocsLocale, T>)[locale]
: value
}
function mirroredPages(pages: MirroredPage[]): DocsPage[] {
return pages.flatMap(page => (['root', 'en'] as const).map((locale) => {
const aliases = page.sourceAliases === undefined
? undefined
: Array.isArray(page.sourceAliases) ? page.sourceAliases : page.sourceAliases[locale]
return {
locale,
contentLocale: localized(page.contentLocale, locale),
source: localized(page.source, locale),
route: locale === 'root' ? page.route : `en/${page.route}`,
label: page.label[locale],
sidebar: page.sidebar[locale],
section: page.section[locale],
order: page.order,
...(aliases === undefined ? {} : { sourceAliases: aliases }),
}
}))
}
function pairedPages(pages: PairedPage[]): DocsPage[] {
return mirroredPages(pages.map((page) => {
const chineseSource = page.source.replace(/\.md$/, '.zh.md')
const sharedAliases = page.sourceAliases ?? []
return {
...page,
source: { root: chineseSource, en: page.source },
contentLocale: { root: 'zh-CN', en: 'en-US' },
sourceAliases: {
root: [...sharedAliases, page.source],
en: [...sharedAliases, chineseSource],
},
}
}))
}
const homeAndGuide = pairedPages([
{
source: 'docs/user/index.md',
route: 'index.md',
label: { root: 'DeepSeek Harness', en: 'DeepSeek Harness' },
sidebar: { root: null, en: null },
section: { root: '首页', en: 'Home' },
order: 0,
},
{
source: 'docs/user/guide/index.md',
route: 'guide/index.md',
label: { root: '介绍', en: 'Introduction' },
sidebar: { root: 'zh-guide', en: 'en-guide' },
section: { root: '入门', en: 'Guide' },
order: 1,
sourceAliases: ['docs/user/guide'],
},
{
source: 'docs/user/guide/quickstart.md',
route: 'guide/quickstart.md',
label: { root: '快速开始', en: 'Quick start' },
sidebar: { root: 'zh-guide', en: 'en-guide' },
section: { root: '入门', en: 'Guide' },
order: 2,
},
{
source: 'docs/user/guide/config.md',
route: 'guide/config.md',
label: { root: '配置文件', en: 'Configuration' },
sidebar: { root: 'zh-guide', en: 'en-guide' },
section: { root: '入门', en: 'Guide' },
order: 3,
},
])
const develop = pairedPages([
{
source: 'docs/user/develop/basic/index.md',
route: 'develop/basic/index.md',
label: { root: '第一个插件', en: 'First plugin' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '基础', en: 'Basics' },
order: 1,
sourceAliases: ['docs/user/develop/basic'],
},
{
source: 'docs/user/develop/basic/tool.md',
route: 'develop/basic/tool.md',
label: { root: '开发一个 Tool', en: 'Build a tool' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '基础', en: 'Basics' },
order: 2,
},
{
source: 'docs/user/develop/basic/config.md',
route: 'develop/basic/config.md',
label: { root: '插件配置', en: 'Plugin configuration' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '基础', en: 'Basics' },
order: 3,
},
{
source: 'docs/user/develop/framework/index.md',
route: 'develop/framework/index.md',
label: { root: '插件与生命周期', en: 'Plugin lifecycle' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '框架能力', en: 'Framework' },
order: 1,
sourceAliases: ['docs/user/develop/framework'],
},
{
source: 'docs/user/develop/framework/service.md',
route: 'develop/framework/service.md',
label: { root: '服务与依赖', en: 'Services and dependencies' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '框架能力', en: 'Framework' },
order: 2,
},
{
source: 'docs/user/develop/framework/events.md',
route: 'develop/framework/events.md',
label: { root: '事件系统', en: 'Event system' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '框架能力', en: 'Framework' },
order: 3,
},
{
source: 'docs/user/develop/practice/index.md',
route: 'develop/practice/index.md',
label: { root: '能力的三层拆分', en: 'Capability layering' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '实战', en: 'Practice' },
order: 1,
sourceAliases: ['docs/user/develop/practice'],
},
{
source: 'docs/user/develop/practice/llm-adapter.md',
route: 'develop/practice/llm-adapter.md',
label: { root: 'LLM 适配器', en: 'LLM adapter' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '实战', en: 'Practice' },
order: 2,
},
])
const reference = mirroredPages([
...([
['docs/architecture.md', 'reference/index.md', '架构', 'Architecture'],
['docs/cordis-primer.md', 'reference/cordis-primer.md', 'Cordis 入门', 'Cordis primer'],
['docs/capability-seams.md', 'reference/capability-seams.md', '能力服务', 'Capability services'],
['docs/agent-lifecycle.md', 'reference/agent-lifecycle.md', 'Agent 生命周期', 'Agent lifecycle'],
['docs/tool-execution-pipeline.md', 'reference/tool-execution-pipeline.md', 'Tool 执行', 'Tool execution'],
] as const).map(([source, route, rootLabel, enLabel], order): MirroredPage => ({
source,
route,
contentLocale: 'en-US',
label: { root: rootLabel, en: enLabel },
sidebar: { root: 'zh-reference', en: 'en-reference' },
section: { root: '概念', en: 'Concepts' },
order,
})),
...([
['docs/config-catalog.md', 'reference/config-catalog.md', '插件配置', 'Plugin configuration'],
['docs/tool-catalog.md', 'reference/tool-catalog.md', 'Tool Schema', 'Tool schemas'],
['docs/cordis-catalog/services.md', 'reference/cordis-catalog/services.md', '服务', 'Services'],
['docs/cordis-catalog/events.md', 'reference/cordis-catalog/events.md', '事件', 'Events'],
['docs/persistence-catalog.md', 'reference/persistence-catalog.md', '持久化事件', 'Persistence events'],
] as const).map(([source, route, rootLabel, enLabel], order): MirroredPage => ({
source,
route,
contentLocale: 'en-US',
label: { root: rootLabel, en: enLabel },
sidebar: { root: 'zh-reference', en: 'en-reference' },
section: { root: '生成参考', en: 'Generated reference' },
order,
})),
...([
['context.md', 'Context', 'Context'],
['events.md', 'Events', 'Events'],
['fiber.md', 'Fiber', 'Fiber'],
['registry.md', 'Plugin Registry', 'Plugin Registry'],
['service.md', 'Service', 'Service'],
] as const).map(([file, rootLabel, enLabel], order): MirroredPage => ({
source: `docs/cordis-catalog/core/${file}`,
route: `reference/cordis-api/${file}`,
contentLocale: 'en-US',
label: { root: rootLabel, en: enLabel },
sidebar: { root: 'zh-reference', en: 'en-reference' },
section: { root: 'Cordis API', en: 'Cordis Core API' },
order,
})),
...([
['core.md', '核心数据结构', 'Core data structures'],
['scope.md', '作用域', 'Scopes'],
['session.md', '会话', 'Sessions'],
['system-prompt.md', '系统提示词', 'System prompts'],
['tools.md', '工具', 'Tools'],
['llm-streaming.md', 'LLM 流式响应', 'LLM streaming'],
['bash.md', 'Bash 执行', 'Bash execution'],
['filesystem.md', '文件系统', 'Filesystem'],
['code-runtime.md', '代码运行时', 'Code runtime'],
['compaction.md', '上下文压缩', 'Compaction'],
['subagent.md', '子代理', 'Subagents'],
['workflow.md', '工作流', 'Workflows'],
['skills.md', '技能', 'Skills'],
['approval.md', '审批', 'Approvals'],
['user-interaction.md', '用户交互', 'User interaction'],
['sandbox.md', '沙箱', 'Sandboxing'],
['web.md', 'Web 访问', 'Web access'],
['persistence.md', '会话持久化', 'Session persistence'],
] as const).map(([file, rootLabel, enLabel], order): MirroredPage => ({
source: `docs/core-data-structures/${file}`,
route: `reference/core-data-structures/${file}`,
contentLocale: 'en-US',
label: { root: rootLabel, en: enLabel },
sidebar: { root: 'zh-reference', en: 'en-reference' },
section: { root: '数据结构', en: 'Data structures' },
order,
...(file === 'core.md' ? { sourceAliases: ['docs/core-data-structures'] } : {}),
})),
...([
['adding-a-package.md', '新增 Package', 'Adding a package'],
['adding-a-tool.md', '新增 Tool', 'Adding a tool'],
['adding-an-llm-adapter.md', '新增 LLM Adapter', 'Adding an LLM adapter'],
['extension-cookbook.md', '扩展模式', 'Extension patterns'],
] as const).map(([file, rootLabel, enLabel], order): MirroredPage => ({
source: `docs/cookbook/${file}`,
route: `reference/cookbook/${file}`,
contentLocale: 'en-US',
label: { root: rootLabel, en: enLabel },
sidebar: { root: 'zh-reference', en: 'en-reference' },
section: { root: '开发手册', en: 'Cookbook' },
order,
})),
])
/** Every canonical page published by the documentation website. */
export const docsPages: DocsPage[] = [
...homeAndGuide,
...develop,
...reference,
]

View File

@@ -4,13 +4,19 @@
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vitepress dev . --port 5173 --open",
"dev": "vitepress dev . --host 127.0.0.1 --port 5173",
"build": "vitepress build .",
"preview": "vitepress preview ."
"preview": "vitepress preview . --host 127.0.0.1 --port 4173"
},
"devDependencies": {
"markdown-it-mathjax3": "^4.3.2",
"vitepress": "^1.6.3",
"vue": "^3.5.13"
"@braintree/sanitize-url": "7.1.2",
"cytoscape": "3.34.0",
"cytoscape-cose-bilkent": "4.1.0",
"dayjs": "1.11.21",
"debug": "4.4.3",
"mermaid": "11.16.0",
"vite": "^5.4.14",
"vitepress": "^1.6.4",
"vitepress-plugin-mermaid": "^2.0.17"
}
}

View File

@@ -1,354 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# Context
The context is the core cordis object: every service, event, and lifecycle API is reached through `ctx`. Event methods (`ctx.on`, `ctx.emit`, …) are documented on [Events](./events.md); `ctx.effect` and `ctx.fiber` on [Fiber](./fiber.md); `ctx.plugin` and `ctx.inject` on [Registry](./registry.md).
Root and child dependency containers for Cordis plugins.
A context is a proxy: normal property reads go through the service resolver, while `extend()`, `isolate()`, and `intercept()` create scoped child contexts without mutating their parent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L42)
### ctx.extend(meta?)
```ts website-api
/**
* Create a child context with extra metadata on top of the current scope.
*
* The child prototypally inherits every property of this context; own
* properties of `meta` shadow the inherited ones. The parent is not mutated.
*
* @param meta — own properties (including symbol keys) to define on the child.
* @returns a child context inheriting from this one.
*/
extend(meta = {}): this
```
Create a child context with extra metadata on top of the current scope.
The child prototypally inherits every property of this context; own properties of `meta` shadow the inherited ones. The parent is not mutated.
- `meta` — own properties (including symbol keys) to define on the child.
**Returns** a child context inheriting from this one.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L99)
### ctx.isolate(name, label?)
```ts website-api
/**
* Create a child context with an independent service scope for `name`.
*
* Below the returned context, reads and writes of the service `name`
* resolve against the new label instead of the parent's, so a different
* implementation can be provided without affecting the parent scope.
* Passing the same `label` to two `isolate()` calls joins their scopes.
*
* @param name — the service name to isolate.
* @param label — scope label to join; defaults to a fresh unique symbol.
* @returns a child context whose `name` service resolves in the new scope.
*/
isolate(name: string, label?: symbol)
```
Create a child context with an independent service scope for `name`.
Below the returned context, reads and writes of the service `name` resolve against the new label instead of the parent's, so a different implementation can be provided without affecting the parent scope. Passing the same `label` to two `isolate()` calls joins their scopes.
- `name` — the service name to isolate.
- `label` — scope label to join; defaults to a fresh unique symbol.
**Returns** a child context whose `name` service resolves in the new scope.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L121)
### ctx.intercept(name, config)
```ts website-api
/**
* Add service-specific intercept config for plugins started below this
* context.
*
* Plugins loaded under the returned context see `config` merged into the
* service's resolved config (ancestor entries first; see
* `Service[symbols.resolveConfig]`). The parent context is not affected.
*
* @param name — the service name whose config to intercept.
* @param config — the intercept config to merge for that service.
* @returns a child context carrying the additional intercept entry.
*/
intercept<K extends InjectKey>(name: K, config: Context[K] extends { [symbols.config]: infer T } ? T : never): this
intercept(name: string, config: any): this
```
Add service-specific intercept config for plugins started below this context.
Plugins loaded under the returned context see `config` merged into the service's resolved config (ancestor entries first; see `Service[symbols.resolveConfig]`). The parent context is not affected.
- `name` — the service name whose config to intercept.
- `config` — the intercept config to merge for that service.
**Returns** a child context carrying the additional intercept entry.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L139)
### ctx.root
```ts website-api
/** The root context of the application (every child context shares it). @experimental */
root: this
```
The root context of the application (every child context shares it). @experimental
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L22)
### ctx.baseUrl
```ts website-api
/** Base URL used to resolve relative plugin/module specifiers, if the runtime sets one. */
baseUrl?: string
```
Base URL used to resolve relative plugin/module specifiers, if the runtime sets one.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L24)
### ctx.events
```ts website-api
/** The event bus. Its methods are also mixed onto `ctx` (`ctx.on`, `ctx.emit`, ...). */
events: EventsService
```
The event bus. Its methods are also mixed onto `ctx` (`ctx.on`, `ctx.emit`, ...).
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L26)
### ctx.logger
```ts website-api
/** The logging service. Call `ctx.logger(name)` for a named logger. */
logger: LoggerService
```
The logging service. Call `ctx.logger(name)` for a named logger.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L28)
### ctx.reflect
```ts website-api
/** The reflection layer backing the context proxy (`ctx.get`, `ctx.provide`, ...). */
reflect: ReflectService
```
The reflection layer backing the context proxy (`ctx.get`, `ctx.provide`, ...).
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L30)
### ctx.registry
```ts website-api
/** The plugin registry. Its methods are mixed onto `ctx` (`ctx.plugin`, `ctx.inject`). */
registry: RegistryService
```
The plugin registry. Its methods are mixed onto `ctx` (`ctx.plugin`, `ctx.inject`).
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L32)
## Static members
### Context.effect
```ts website-api
/** Symbol key under which a disposer exposes its {@link EffectMeta} diagnostics tree. */
static readonly effect: unique symbol
```
Symbol key under which a disposer exposes its EffectMeta diagnostics tree.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L44)
### Context.filter
```ts website-api
/** Symbol key for a context's listener filter, consulted on every event dispatch. */
static readonly filter: unique symbol
```
Symbol key for a context's listener filter, consulted on every event dispatch.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L46)
### Context.isolate
```ts website-api
/** Symbol key of the isolation map (see the `Context[symbols.isolate]` property). */
static readonly isolate: unique symbol
```
Symbol key of the isolation map (see the `Context[symbols.isolate]` property).
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L48)
### Context.intercept
```ts website-api
/** Symbol key of the intercept map (see the `Context[symbols.intercept]` property). */
static readonly intercept: unique symbol
```
Symbol key of the intercept map (see the `Context[symbols.intercept]` property).
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L50)
### Context.is(value)
```ts website-api
/**
* Returns true for Cordis context proxies and context prototypes.
*
* Works across realms and across multiple copies of cordis, because the
* brand is keyed by a global symbol rather than by `instanceof`.
*
* @param value — the value to test.
* @returns `true` if `value` is a Cordis context, narrowing its type.
*/
static is(value: any): value is Context
```
Returns true for Cordis context proxies and context prototypes.
Works across realms and across multiple copies of cordis, because the brand is keyed by a global symbol rather than by `instanceof`.
- `value` — the value to test.
**Returns** `true` if `value` is a Cordis context, narrowing its type.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/context.ts#L61)
## Service store and mixins
### ctx.get(name, strict?)
```ts website-api
/**
* Read a service from the store without the inject requirement.
*
* @param name — the service name.
* @param strict — when `true` (default), only return implementations
* whose providing fiber is currently active.
* @returns the service value, or `undefined` when not (yet) provided.
*/
get<K extends string & keyof this>(name: K, strict?: boolean): undefined | this[K]
get(name: string, strict?: boolean): any
```
Read a service from the store without the inject requirement.
- `name` — the service name.
- `strict` — when `true` (default), only return implementations whose providing fiber is currently active.
**Returns** the service value, or `undefined` when not (yet) provided.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/reflect.ts#L16)
### ctx.set(name, value)
```ts website-api
/**
* Overwrite a provided service's value.
*
* Only the fiber that provided the service may set it; setting an
* unprovided name throws.
*
* @param name — the service name.
* @param value — the new service value.
*/
set<K extends string & keyof this>(name: K, value: undefined | this[K]): void
set(name: string, value: any): void
```
Overwrite a provided service's value.
Only the fiber that provided the service may set it; setting an unprovided name throws.
- `name` — the service name.
- `value` — the new service value.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/reflect.ts#L28)
### ctx.provide(name, value)
```ts website-api
/**
* Register a service implementation owned by the current fiber.
*
* The service becomes visible to dependents in the same isolation scope
* once the fiber is active; it is unregistered (waking dependents) when
* the returned disposer runs or the fiber unloads. Throws if the name is
* already provided in this scope or declared as an accessor.
*
* @param name — the service name.
* @param value — the service value.
* @returns a disposer that unregisters the service.
*/
provide<K extends string & keyof this>(name: K, value: undefined | this[K]): () => void
provide(name: string, value?: any): () => void
```
Register a service implementation owned by the current fiber.
The service becomes visible to dependents in the same isolation scope once the fiber is active; it is unregistered (waking dependents) when the returned disposer runs or the fiber unloads. Throws if the name is already provided in this scope or declared as an accessor.
- `name` — the service name.
- `value` — the service value.
**Returns** a disposer that unregisters the service.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/reflect.ts#L43)
### ctx.accessor(name, options)
```ts website-api
/**
* Define a computed context property backed by get/set hooks.
*
* The accessor is removed when the current fiber unloads. Throws if the
* name is already declared.
*
* @param name — the context property name.
* @param options — the `get` hook and optional `set` hook.
*/
accessor(name: string, options: Omit<Property.Accessor, 'type'>): void
```
Define a computed context property backed by get/set hooks.
The accessor is removed when the current fiber unloads. Throws if the name is already declared.
- `name` — the context property name.
- `options` — the `get` hook and optional `set` hook.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/reflect.ts#L55)
### ctx.mixin(name, mixins)
```ts website-api
/**
* Expose selected members of a service directly on `ctx`.
*
* Each mixed-in key becomes an accessor that forwards to the service
* (binding methods to it), so e.g. `ctx.on` forwards to `ctx.events.on`.
* Mixins are removed when the current fiber unloads.
*
* @param name — the context property holding the source service.
* @param mixins — keys to forward, or a source-key → ctx-key map.
*/
mixin<K extends string & keyof this>(name: K, mixins: (keyof this & keyof this[K])[] | Dict<string>): void
mixin<T extends {}>(source: T, mixins: (keyof this & keyof T)[] | Dict<string>): void
```
Expose selected members of a service directly on `ctx`.
Each mixed-in key becomes an accessor that forwards to the service (binding methods to it), so e.g. `ctx.on` forwards to `ctx.events.on`. Mixins are removed when the current fiber unloads.
- `name` — the context property holding the source service.
- `mixins` — keys to forward, or a source-key → ctx-key map.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/reflect.ts#L66)

View File

@@ -1,204 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# Events
The event system mixed into every context. Harness-defined events are cataloged on [Harness events](../harness/events.md).
### ctx.parallel(name, ...args)
```ts website-api
/**
* Dispatch an event, running all listeners concurrently.
*
* @param name — the event name.
* @param args — arguments passed to every listener.
* @returns a promise resolving once every listener has settled.
*/
parallel<K extends keyof Events>(name: K, ...args: Parameters<Events[K]>): Promise<void>
parallel<K extends keyof Events>(thisArg: NoInfer<ThisType<Events[K]>>, name: K, ...args: Parameters<Events[K]>): Promise<void>
```
Dispatch an event, running all listeners concurrently.
- `name` — the event name.
- `args` — arguments passed to every listener.
**Returns** a promise resolving once every listener has settled.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/events.ts#L43)
### ctx.emit(name, ...args)
```ts website-api
/**
* Dispatch an event synchronously, ignoring listener return values.
*
* @param name — the event name.
* @param args — arguments passed to every listener.
*/
emit<K extends keyof Events>(name: K, ...args: Parameters<Events[K]>): void
emit<K extends keyof Events>(thisArg: NoInfer<ThisType<Events[K]>>, name: K, ...args: Parameters<Events[K]>): void
```
Dispatch an event synchronously, ignoring listener return values.
- `name` — the event name.
- `args` — arguments passed to every listener.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/events.ts#L52)
### ctx.serial(name, ...args)
```ts website-api
/**
* Dispatch an event, awaiting listeners in order until one bails.
*
* @param name — the event name.
* @param args — arguments passed to each listener.
* @returns the first bail value (non-null, non-false, non-undefined), if any.
*/
serial<K extends keyof Events>(name: K, ...args: Parameters<Events[K]>): Promisify<ReturnType<Events[K]>>
serial<K extends keyof Events>(thisArg: NoInfer<ThisType<Events[K]>>, name: K, ...args: Parameters<Events[K]>): Promisify<ReturnType<Events[K]>>
```
Dispatch an event, awaiting listeners in order until one bails.
- `name` — the event name.
- `args` — arguments passed to each listener.
**Returns** the first bail value (non-null, non-false, non-undefined), if any.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/events.ts#L62)
### ctx.bail(name, ...args)
```ts website-api
/**
* Dispatch an event, calling listeners in order until one bails.
*
* @param name — the event name.
* @param args — arguments passed to each listener.
* @returns the first bail value (non-null, non-false, non-undefined), if any.
*/
bail<K extends keyof Events>(name: K, ...args: Parameters<Events[K]>): ReturnType<Events[K]>
bail<K extends keyof Events>(thisArg: NoInfer<ThisType<Events[K]>>, name: K, ...args: Parameters<Events[K]>): ReturnType<Events[K]>
```
Dispatch an event, calling listeners in order until one bails.
- `name` — the event name.
- `args` — arguments passed to each listener.
**Returns** the first bail value (non-null, non-false, non-undefined), if any.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/events.ts#L72)
### ctx.waterfall(name, ...args)
```ts website-api
/**
* Dispatch an event whose last argument is a `next` continuation.
*
* Each listener wraps the rest of the chain: calling `next()` invokes the
* next listener (finally the built-in behavior); not calling it vetoes.
*
* @param name — the event name.
* @param args — listener arguments; the final one is the innermost `next`.
* @returns the outermost listener's return value.
*/
waterfall<K extends keyof Events>(name: K, ...args: Parameters<Events[K]>): ReturnType<Events[K]>
waterfall<K extends keyof Events>(thisArg: NoInfer<ThisType<Events[K]>>, name: K, ...args: Parameters<Events[K]>): ReturnType<Events[K]>
```
Dispatch an event whose last argument is a `next` continuation.
Each listener wraps the rest of the chain: calling `next()` invokes the next listener (finally the built-in behavior); not calling it vetoes.
- `name` — the event name.
- `args` — listener arguments; the final one is the innermost `next`.
**Returns** the outermost listener's return value.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/events.ts#L85)
### ctx.on(name, listener, options?)
```ts website-api
/**
* Register an event listener owned by the current fiber.
*
* @param name — the event name to listen for.
* @param listener — called with the dispatch arguments.
* @param options — listener options; a boolean is shorthand for `prepend`.
* @returns a disposer removing the listener; `true` if it was still registered.
*/
on<K extends keyof Events>(name: K, listener: Events[K], options?: boolean | EventOptions): () => boolean
```
Register an event listener owned by the current fiber.
- `name` — the event name to listen for.
- `listener` — called with the dispatch arguments.
- `options` — listener options; a boolean is shorthand for `prepend`.
**Returns** a disposer removing the listener; `true` if it was still registered.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/events.ts#L96)
### ctx.once(name, listener, options?)
```ts website-api
/**
* Same as `on()`, but the listener disposes itself after its first call.
*
* @param name — the event name to listen for.
* @param listener — called at most once with the dispatch arguments.
* @param options — listener options; a boolean is shorthand for `prepend`.
* @returns a disposer removing the listener; `true` if it was still registered.
*/
once<K extends keyof Events>(name: K, listener: Events[K], options?: boolean | EventOptions): () => boolean
```
Same as `on()`, but the listener disposes itself after its first call.
- `name` — the event name to listen for.
- `listener` — called at most once with the dispatch arguments.
- `options` — listener options; a boolean is shorthand for `prepend`.
**Returns** a disposer removing the listener; `true` if it was still registered.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/events.ts#L105)
## EventOptions
Options accepted by `ctx.on()` and `ctx.once()`.
```ts website-api
/** Options accepted by `ctx.on()` and `ctx.once()`. */
interface EventOptions {
/** Add the listener before existing listeners for the same event. */
prepend?: boolean
/** Receive the event regardless of context filter checks. */
global?: boolean
}
```
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/events.ts#L111)
## DispatchMode
Event dispatch strategy used by the event service.
`emit` runs synchronous listeners without awaiting them, `parallel` awaits all listeners together, `serial` awaits them in order until one bails, `bail` stops on the first synchronous bail value, and `waterfall` composes listeners around a final `next` callback.
```ts website-api
/**
* Event dispatch strategy used by the event service.
*
* `emit` runs synchronous listeners without awaiting them, `parallel` awaits
* all listeners together, `serial` awaits them in order until one bails,
* `bail` stops on the first synchronous bail value, and `waterfall` composes
* listeners around a final `next` callback.
*/
type DispatchMode = 'emit' | 'parallel' | 'serial' | 'bail' | 'waterfall'
```
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/events.ts#L31)

View File

@@ -1,368 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# Fiber
A fiber is one loaded plugin instance: its lifecycle state, validated config, and registered effects. `ctx.fiber` is the current fiber; `ctx.effect()` delegates to it.
### ctx.effect(execute, label?)
```ts website-api
/**
* Register a cleanup-aware effect on this fiber.
*
* `execute` runs immediately; the disposers it produces are collected and
* run (in reverse order) either when the returned disposer is called or
* when the fiber unloads, whichever comes first. Calling the disposer twice
* is a no-op. Throws `CordisError('INACTIVE_EFFECT')` if the fiber is
* already disposed, and `TypeError` if `execute` returns an invalid shape.
*
* @param execute — the effect body; see {@link Effect} for accepted shapes.
* @param label — effect label shown in `getEffects()` diagnostics.
* @returns a disposer that tears the effect down and settles once done.
*/
effect(execute: () => SyncEffect, label?: string): Disposable<Promise<void>>
effect(execute: () => Effect, label?: string): AsyncDisposable<Promise<void>>
```
Register a cleanup-aware effect on this fiber.
`execute` runs immediately; the disposers it produces are collected and run (in reverse order) either when the returned disposer is called or when the fiber unloads, whichever comes first. Calling the disposer twice is a no-op. Throws `CordisError('INACTIVE_EFFECT')` if the fiber is already disposed, and `TypeError` if `execute` returns an invalid shape.
- `execute` — the effect body; see `Effect` for accepted shapes.
- `label` — effect label shown in `getEffects()` diagnostics.
**Returns** a disposer that tears the effect down and settles once done.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L419)
### ctx.fiber
```ts website-api
/** The fiber (plugin runtime instance) that owns this context. */
fiber: Fiber
```
The fiber (plugin runtime instance) that owns this context.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L11)
## The Fiber class
Runtime instance of one plugin application.
A fiber tracks dependency state, validated config, lifecycle effects, and cleanup for the plugin context returned by `ctx.plugin()`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L183)
### fiber.uid
```ts website-api
/** Unique id within the registry; 0 for the root fiber, `null` once disposed. */
public uid: number | null
```
Unique id within the registry; 0 for the root fiber, `null` once disposed.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L185)
### fiber.ctx
```ts website-api
/** The context this fiber's plugin runs in (extends the parent context). */
public readonly ctx: Context
```
The context this fiber's plugin runs in (extends the parent context).
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L187)
### fiber.config
```ts website-api
/** The validated plugin config (updated by `update()`). */
public config: any
```
The validated plugin config (updated by `update()`).
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L189)
### fiber.state
```ts website-api
/** Current lifecycle state; transitions emit `internal/status`. */
public state
```
Current lifecycle state; transitions emit `internal/status`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L191)
### fiber.dispose
```ts website-api
/** Dispose this fiber: unload the plugin, then settle once cleanup finished. */
public readonly dispose: () => Promise<void>
```
Dispose this fiber: unload the plugin, then settle once cleanup finished.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L193)
### fiber.store
```ts website-api
/** Snapshot of required service implementations while loaded; `undefined` otherwise. */
public store: Dict<Impl> | undefined
```
Snapshot of required service implementations while loaded; `undefined` otherwise.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L195)
### fiber.inertia
```ts website-api
/** The in-flight load/unload transition, if one is currently running. */
public inertia: Promise<void> | undefined
```
The in-flight load/unload transition, if one is currently running.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L197)
### fiber.name
```ts website-api
/** The plugin's display name, inherited from the nearest named ancestor, else `'root'`. */
get name()
```
The plugin's display name, inherited from the nearest named ancestor, else `'root'`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L340)
### fiber.assertActive()
```ts website-api
/**
* Throw if the fiber has already been disposed.
*
* @returns nothing when the fiber is still active.
* @throws {CordisError} `INACTIVE_EFFECT` when the fiber's uid has been cleared.
*/
assertActive()
```
Throw if the fiber has already been disposed.
**Returns** nothing when the fiber is still active.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L355)
### fiber.effect(execute, label?)
```ts website-api
/**
* Register a cleanup-aware effect on this fiber.
*
* `execute` runs immediately; the disposers it produces are collected and
* run (in reverse order) either when the returned disposer is called or
* when the fiber unloads, whichever comes first. Calling the disposer twice
* is a no-op. Throws `CordisError('INACTIVE_EFFECT')` if the fiber is
* already disposed, and `TypeError` if `execute` returns an invalid shape.
*
* @param execute — the effect body; see {@link Effect} for accepted shapes.
* @param label — effect label shown in `getEffects()` diagnostics.
* @returns a disposer that tears the effect down and settles once done.
*/
effect(execute: () => SyncEffect, label?: string): Disposable<Promise<void>>
effect(execute: () => Effect, label?: string): AsyncDisposable<Promise<void>>
```
Register a cleanup-aware effect on this fiber.
`execute` runs immediately; the disposers it produces are collected and run (in reverse order) either when the returned disposer is called or when the fiber unloads, whichever comes first. Calling the disposer twice is a no-op. Throws `CordisError('INACTIVE_EFFECT')` if the fiber is already disposed, and `TypeError` if `execute` returns an invalid shape.
- `execute` — the effect body; see `Effect` for accepted shapes.
- `label` — effect label shown in `getEffects()` diagnostics.
**Returns** a disposer that tears the effect down and settles once done.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L419)
### fiber.getEffects()
```ts website-api
/**
* Return metadata for currently registered effects.
*
* @returns one {@link EffectMeta} tree per labeled live effect.
*/
getEffects()
```
Return metadata for currently registered effects.
**Returns** one `EffectMeta` tree per labeled live effect.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L572)
### fiber.await()
```ts website-api
/**
* Wait for current lifecycle work and rethrow startup errors.
*
* @returns this fiber, once it has settled into a stable state.
* @throws the config-validation or plugin-startup error, if any.
*/
async await()
```
Wait for current lifecycle work and rethrow startup errors.
**Returns** this fiber, once it has settled into a stable state.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L701)
### fiber.restart()
```ts website-api
/**
* Dispose and immediately reload this plugin with its current config.
*
* @returns a promise resolving once the reload settled.
* @throws {CordisError} `INACTIVE_EFFECT` when the fiber is already disposed.
*/
async restart()
```
Dispose and immediately reload this plugin with its current config.
**Returns** a promise resolving once the reload settled.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L715)
### fiber.update(config, noSave?)
```ts website-api
/**
* Validate and apply new config, then restart the plugin.
*
* Runs the `internal/update` waterfall first, so update hooks (and HMR)
* can veto or replace the restart.
*
* @param config — the new raw config; validated before anything restarts.
* @param noSave — hint for persistence hooks not to write the change back.
* @returns nothing; the restart runs behind the `internal/update` waterfall.
* @throws {ValidationError} when the new config fails validation.
*/
update(config: any, noSave = false)
```
Validate and apply new config, then restart the plugin.
Runs the `internal/update` waterfall first, so update hooks (and HMR) can veto or replace the restart.
- `config` — the new raw config; validated before anything restarts.
- `noSave` — hint for persistence hooks not to write the change back.
**Returns** nothing; the restart runs behind the `internal/update` waterfall.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L733)
## Effect
Effect body result accepted by `ctx.effect()` and plugin startup.
Either a single disposer, a promise of one, or a (possibly async) iterable yielding several — generator effects register each yielded disposer as it is produced.
```ts website-api
/**
* Effect body result accepted by `ctx.effect()` and plugin startup.
*
* Either a single disposer, a promise of one, or a (possibly async) iterable
* yielding several — generator effects register each yielded disposer as it
* is produced.
*/
type Effect<T = any> =
| SyncEffect<T>
| AsyncEffect<T>
```
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L82)
## Disposable
Function returned by an effect to release resources during disposal.
Disposers run in reverse registration order when the owning fiber unloads; they may be async, in which case unloading awaits them.
```ts website-api
/**
* Function returned by an effect to release resources during disposal.
*
* Disposers run in reverse registration order when the owning fiber unloads;
* they may be async, in which case unloading awaits them.
*/
type Disposable<T = any> = () => T
```
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L73)
## EffectMeta
Tree node used to expose nested effect labels for diagnostics.
```ts website-api
/** Tree node used to expose nested effect labels for diagnostics. */
interface EffectMeta {
/** Human-readable effect label, e.g. `ctx.on("event")` or `ctx.provide("name")`. */
label: string
/** Metadata of nested effects registered while this effect ran. */
children: EffectMeta[]
}
```
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L95)
## CordisError
Framework error with a stable machine-readable code.
```ts website-api
/** Framework error with a stable machine-readable code. */
class CordisError extends Error {
/**
* @param code — the stable error code; also the default message.
* @param message — optional human-readable override.
*/
constructor(public code: CordisError.Code, message?: string)
}
/** Cordis error code definitions. */
namespace CordisError {
export type Code = keyof typeof Code
export const Code = {
INACTIVE_EFFECT: 'cannot create effect on inactive context',
} as const
}
```
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L156)
## ValidationError
Error raised when plugin configuration fails standard-schema validation.
```ts website-api
/** Error raised when plugin configuration fails standard-schema validation. */
class ValidationError extends TypeError {
name = 'ValidationError'
/**
* Build the aggregated message from schema issues.
*
* @param issues — the standard-schema issues, one message line each.
*/
constructor(issues: readonly StandardSchemaV1.Issue[])
}
```
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/fiber.ts#L18)

View File

@@ -1,149 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# Registry
Plugin loading and dependency injection.
### ctx.inject(deps, callback)
```ts website-api
/**
* Run a callback once the requested services are available.
*
* Shorthand for `ctx.plugin({ inject, apply: callback })`: the callback
* is unloaded and re-run whenever a required service changes.
*
* @param deps — required services, as an array or a name → config map.
* @param callback — plugin body called with `(ctx, config)`.
* @returns the fiber; awaiting it settles once loading finished.
*/
inject(deps: Inject, callback: Plugin.Function<void>): Fiber & PromiseLike<Fiber>
```
Run a callback once the requested services are available.
Shorthand for `ctx.plugin({ inject, apply: callback })`: the callback is unloaded and re-run whenever a required service changes.
- `deps` — required services, as an array or a name → config map.
- `callback` — plugin body called with `(ctx, config)`.
**Returns** the fiber; awaiting it settles once loading finished.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/registry.ts#L175)
### ctx.plugin(plugin, ...args)
```ts website-api
/**
* Load a plugin in the current context.
*
* @param plugin — a function, class, or `{ apply }` object plugin.
* @param args — the plugin config, validated against its `Config` schema.
* @returns the fiber; awaiting it settles once loading finished
* (rejecting on config or startup errors).
*/
plugin<P extends Plugin>(plugin: P, ...args: Spread<GetPluginConfig<P>>): Fiber & PromiseLike<Fiber>
```
Load a plugin in the current context.
- `plugin` — a function, class, or `{ apply }` object plugin.
- `args` — the plugin config, validated against its `Config` schema.
**Returns** the fiber; awaiting it settles once loading finished (rejecting on config or startup errors).
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/registry.ts#L184)
## Plugin
Supported plugin entrypoint shapes.
```ts website-api
/** Supported plugin entrypoint shapes. */
type Plugin<T = any> =
| Plugin.Function<T>
| Plugin.Constructor<T>
| Plugin.Object<T>
/** Types associated with plugin entrypoints and runtime records. */
namespace Plugin {
/** Shared metadata understood by the plugin registry and related tooling. */
export interface Base<T = any> {
/** Display name used for fiber diagnostics and logger names. */
name?: string
/** Standard-schema validator applied to config before the plugin starts. */
Config?: StandardSchemaV1<any, T>
/** Services the plugin requires; it only loads while all are available. */
inject?: Inject
/** Service name(s) the plugin provides (read by `Service` and by loaders). */
provide?: string | string[]
/** Service names whose intercept config the plugin declares it consumes. */
intercept?: Dict<boolean>
}
export interface Transform<S, T> {
/** Marks the transform object as a schema/config transform. */
schema?: true
/** Convert user-facing config to runtime config. */
Config: (config: S) => T
}
/** Function plugin called with `(ctx, config)`. */
export interface Function<T = any> extends Base<T> {
(ctx: Context, config: T): any
}
/** Class plugin constructed with `(ctx, config)`. */
export interface Constructor<T = any> extends Base<T> {
new (ctx: Context, config: T): any
}
/** Object plugin with an `apply(ctx, config)` method. */
export interface Object<T = any> extends Base<T> {
apply(ctx: Context, config: T): any
}
/** Mutable registry record shared by all fibers of one plugin callback. */
export interface Runtime {
/** Display name copied from the first registered plugin shape. */
name?: string
/** Every live fiber of this plugin (one per `ctx.plugin()` call). */
fibers: DisposableList<Fiber>
/** The executable entrypoint all fibers share (registry identity key). */
callback: globalThis.Function
/** Standard-schema validator applied to each fiber's config. */
Config?: StandardSchemaV1
}
}
```
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/registry.ts#L91)
## Inject
Service dependency declaration accepted by plugins and the `@Inject` decorator.
Array form requests services without intercept config. Object form maps each service name to optional intercept config for the plugin context.
```ts website-api
/**
* Service dependency declaration accepted by plugins and the `@Inject`
* decorator.
*
* Array form requests services without intercept config. Object form maps each
* service name to optional intercept config for the plugin context.
*/
type Inject<M = Dict> = (keyof M)[] | { [K in keyof M]?: M[K] }
/** Utilities for normalizing plugin dependency declarations. */
namespace Inject {
/**
* Convert array/object/class-inherited inject metadata into a plain map.
*
* @param inject — the declaration to normalize; `null`/`undefined` add nothing.
* @param result — the map to fill (service name → intercept config or `null`).
* @returns `result`.
*/
export function resolve(inject: Inject | null | undefined, result: Dict = Object.create(null))
}
```
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/registry.ts#L18)

View File

@@ -1,100 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# Service
Base class for context services: subclass it and load the subclass as a plugin to register `ctx.<name>`.
Base class for services that expose a named API on `ctx`.
Subclasses call `super(ctx, name)` from their constructor. The service is registered immediately and is automatically removed with the owning fiber.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/service.ts#L11)
### service.name
```ts website-api
/** The service name this instance is registered under. */
public name!: string
```
The service name this instance is registered under.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/service.ts#L30)
## Static members
### Service.init
```ts website-api
/** Symbol key of an instance method run after construction (class plugins). */
static readonly init: unique symbol
```
Symbol key of an instance method run after construction (class plugins).
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/service.ts#L13)
### Service.check
```ts website-api
/** Symbol key of the availability predicate passed to `ctx.provide()`. */
static readonly check: unique symbol
```
Symbol key of the availability predicate passed to `ctx.provide()`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/service.ts#L15)
### Service.config
```ts website-api
/** Symbol key of the phantom intercept-config type parameter. */
static readonly config: unique symbol
```
Symbol key of the phantom intercept-config type parameter.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/service.ts#L17)
### Service.invoke
```ts website-api
/** Symbol key of the call body making a service callable (e.g. `ctx.logger()`). */
static readonly invoke: unique symbol
```
Symbol key of the call body making a service callable (e.g. `ctx.logger()`).
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/service.ts#L19)
### Service.extend
```ts website-api
/** Symbol key of the helper deriving an extended service instance. */
static readonly extend: unique symbol
```
Symbol key of the helper deriving an extended service instance.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/service.ts#L21)
### Service.tracker
```ts website-api
/** Symbol key of the tracker metadata used for context tracing. */
static readonly tracker: unique symbol
```
Symbol key of the tracker metadata used for context tracing.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/service.ts#L23)
### Service.resolveConfig
```ts website-api
/** Symbol key of the intercept-config resolution helper below. */
static readonly resolveConfig: unique symbol
```
Symbol key of the intercept-config resolution helper below.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/vendor/cordis/src/service.ts#L25)

View File

@@ -1,76 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.agentLoop
`AgentLoop` — provided by `@deepseek-ai/dsh-agent-loop`.
Concrete agent factory and driver service.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent-loop/src/index.ts#L407)
### ctx.agentLoop.create(id, options?, meta?)
```ts website-api
/**
* Create an agent and session under one caller-supplied identity, owned by
* the accessing fiber. Constructor-driven config calls mint a fresh combined
* id before entering this boundary.
* @param id - shared agent/session identity.
* @param options - concrete loop options.
* @param meta - optional fresh-session workspace metadata.
* @returns the published running agent.
*/
create(id: SessionId, options: AgentOptions = {}, meta: Pick<SessionHeader, 'cwd'> = {}): Agent
```
Create an agent and session under one caller-supplied identity, owned by the accessing fiber. Constructor-driven config calls mint a fresh combined id before entering this boundary.
- `id` — shared agent/session identity.
- `options` — concrete loop options.
- `meta` — optional fresh-session workspace metadata.
**Returns** the published running agent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent-loop/src/index.ts#L542)
### ctx.agentLoop.createAgent(ownerCtx, options)
```ts website-api
/**
* Create an owned agent on a caller-supplied session id.
* @param ownerCtx - caller context that structurally owns the transaction.
* @param options - identities, session seed/metadata, loop options, setup, and cancellation.
* @returns the published handle.
*/
async createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise<AgentHandle>
```
Create an owned agent on a caller-supplied session id.
- `ownerCtx` — caller context that structurally owns the transaction.
- `options` — identities, session seed/metadata, loop options, setup, and cancellation.
**Returns** the published handle.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent-loop/src/index.ts#L564)
### ctx.agentLoop.resume(ownerCtx, options)
```ts website-api
/**
* Resume an owned agent from the configured persistence service.
* @param ownerCtx - caller context that owns load, setup, and the live lifecycle.
* @param options - persisted identity, loop options, setup, and cancellation.
* @returns the published handle.
*/
async resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandle>
```
Resume an owned agent from the configured persistence service.
- `ownerCtx` — caller context that owns load, setup, and the live lifecycle.
- `options` — persisted identity, loop options, setup, and cancellation.
**Returns** the published handle.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent-loop/src/index.ts#L596)

View File

@@ -1,331 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.agents
`AgentRegistry` — provided by `@deepseek-ai/dsh-agent`.
Agent service (`ctx.agents`): tracks live agents and carries the initiating Agent through one process-local asynchronous driver chain. Agent *creation* is provided by whichever plugin implements the AgentFactory (`@deepseek-ai/dsh-agent-loop`), registered via setFactory.
Initiator methods provide same-process causal attribution only. Ambient presence is neither liveness proof nor authorization; subjects and owners remain explicit, as does identity at worker, process, persistence, and wire boundaries. Returned Promise boundaries drain during teardown, except a nested lineage that starts an owning-fiber unload is excluded from its own drain.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L217)
### ctx.agents.currentInitiator()
```ts website-api
/**
* Read the Agent that initiated the inherited asynchronous driver chain.
* Use this optional form for logging, tracing, metrics, or host attribution
* that also supports agentless calls. When a parent creates a child, setup
* reports the causal parent while `agentCtx.agent` identifies the child.
* @returns the inherited Agent, or `undefined` outside an initiator boundary
* and inside an explicit clearing boundary.
* @throws when this service instance has been disposed.
*/
currentInitiator(): Agent | undefined
```
Read the Agent that initiated the inherited asynchronous driver chain. Use this optional form for logging, tracing, metrics, or host attribution that also supports agentless calls. When a parent creates a child, setup reports the causal parent while `agentCtx.agent` identifies the child.
**Returns** the inherited Agent, or `undefined` outside an initiator boundary and inside an explicit clearing boundary.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L256)
### ctx.agents.requireInitiator()
```ts website-api
/**
* Read the initiating Agent and fail when no initiator boundary is active.
* Use this for private helpers contractually below a driver, or for a
* deployment-owned outbound request whose contract forbids agentless calls.
* Generic or direct-call seams use optional lookup or explicit request fields.
* @returns the inherited Agent.
* @throws when no initiator is active or this service instance has been disposed.
*/
requireInitiator(): Agent
```
Read the initiating Agent and fail when no initiator boundary is active. Use this for private helpers contractually below a driver, or for a deployment-owned outbound request whose contract forbids agentless calls. Generic or direct-call seams use optional lookup or explicit request fields.
**Returns** the inherited Agent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L269)
### ctx.agents.withInitiator(agent, operation)
```ts website-api
/**
* Run an operation with one exact Agent as its process-local initiator. The
* exact synchronous value or Promise returned by the operation is preserved.
* Custom drivers and test harnesses wrap their complete returned foreground
* lifetime.
* A queue or wire receiver may establish this boundary only after validating
* explicit identity and resolving the exact live Agent; this method does neither.
* Detached work remains owned by the subsystem that starts it.
* @param agent - initiating Agent to inherit; presence is neither liveness proof nor authorization.
* @param operation - synchronous or asynchronous operation to invoke.
* @returns the exact value returned by `operation`.
* @throws when the initiator scope is closing/disposed, or when `operation` throws.
*/
withInitiator<T>(agent: Agent, operation: () => T): T
```
Run an operation with one exact Agent as its process-local initiator. The exact synchronous value or Promise returned by the operation is preserved. Custom drivers and test harnesses wrap their complete returned foreground lifetime. A queue or wire receiver may establish this boundary only after validating explicit identity and resolving the exact live Agent; this method does neither. Detached work remains owned by the subsystem that starts it.
- `agent` — initiating Agent to inherit; presence is neither liveness proof nor authorization.
- `operation` — synchronous or asynchronous operation to invoke.
**Returns** the exact value returned by `operation`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L288)
### ctx.agents.withoutInitiator(operation)
```ts website-api
/**
* Run an operation inside a boundary that hides any inherited initiating
* Agent. The exact synchronous value or Promise is preserved.
* Use this while creating lazy shared timers, queue pumps, pool maintenance,
* watchers, or exporters so they do not inherit the first Agent that happens
* to initialize them. It clears only initiator attribution, not explicit
* fields, and does not own or drain detached resources.
* @param operation - synchronous or asynchronous operation to invoke without an initiator.
* @returns the exact value returned by `operation`.
* @throws when the initiator scope is closing/disposed, or when `operation` throws.
*/
withoutInitiator<T>(operation: () => T): T
```
Run an operation inside a boundary that hides any inherited initiating Agent. The exact synchronous value or Promise is preserved. Use this while creating lazy shared timers, queue pumps, pool maintenance, watchers, or exporters so they do not inherit the first Agent that happens to initialize them. It clears only initiator attribution, not explicit fields, and does not own or drain detached resources.
- `operation` — synchronous or asynchronous operation to invoke without an initiator.
**Returns** the exact value returned by `operation`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L303)
### ctx.agents.setFactory(factory)
```ts website-api
/**
* Register the agent-creation factory (the loop calls this on construction,
* effect-scoped). A traced Cordis service is canonicalized to its concrete
* target; each create/resume call is then traced through that caller's
* context so ownership follows the caller without stacking proxy layers.
* Throws if a factory is already registered. Returns the disposer; on
* dispose the factory slot is cleared.
* @param factory - the loop-owned factory {@link create}/{@link resume} delegate to.
* @returns the disposer that clears the factory slot. The exact
* Cordis effect disposer (single-shot): composite (generator) effects may
* yield it directly — exact identity nests the teardown in order.
*/
setFactory(factory: AgentFactory): () => void
```
Register the agent-creation factory (the loop calls this on construction, effect-scoped). A traced Cordis service is canonicalized to its concrete target; each create/resume call is then traced through that caller's context so ownership follows the caller without stacking proxy layers. Throws if a factory is already registered. Returns the disposer; on dispose the factory slot is cleared.
- `factory` — the loop-owned factory `create`/`resume` delegate to.
**Returns** the disposer that clears the factory slot. The exact Cordis effect disposer (single-shot): composite (generator) effects may yield it directly — exact identity nests the teardown in order.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L319)
### ctx.agents.create(options)
```ts website-api
/**
* Create and publish a new agent through the registered factory.
* Distinct from {@link register} (which records an already-constructed
* agent): this constructs the agent and its session. Rejects if no factory is
* registered or creation/setup fails. The resolved {@link AgentHandle} lets
* the owner tear down exactly this agent.
* @param options - shared identity, session seed/metadata, and agent options.
* @returns the handle after setup, rollback-covered publication, and loop start complete.
*/
async create(options: CreateAgentOptions): Promise<AgentHandle>
```
Create and publish a new agent through the registered factory. Distinct from register (which records an already-constructed agent): this constructs the agent and its session. Rejects if no factory is registered or creation/setup fails. The resolved AgentHandle lets the owner tear down exactly this agent.
- `options` — shared identity, session seed/metadata, and agent options.
**Returns** the handle after setup, rollback-covered publication, and loop start complete.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L352)
### ctx.agents.resume(options)
```ts website-api
/**
* Load a persisted session and resume an agent on it through the registered
* factory. Rejects if no factory is registered; the factory rejects if
* session persistence is not configured or persistence/setup fails.
* @param options - persisted identity, configuration, and optional setup.
* @returns the handle after setup, rollback-covered publication, and loop start complete.
*/
async resume(options: ResumeAgentOptions): Promise<AgentHandle>
```
Load a persisted session and resume an agent on it through the registered factory. Rejects if no factory is registered; the factory rejects if session persistence is not configured or persistence/setup fails.
- `options` — persisted identity, configuration, and optional setup.
**Returns** the handle after setup, rollback-covered publication, and loop start complete.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L371)
### ctx.agents.register(agent)
```ts website-api
/**
* Register a live agent. Throws if an agent with the same id is already
* registered. Emits `agent/created` on registration and `agent/disposed`
* when the calling fiber is disposed — both with the agent's scope carrier
* (`scopeTarget(agent, agent)`): the subject is the agent in hand, so the
* emits are scope-filtered regardless of which context invoked `register`
* (calling through `agent.ctx` scopes EFFECTS; dispatch scoping always
* requires passing the carrier). Returns the disposer.
* @param agent - the already-constructed agent to record in the store.
* @returns the EXACT Cordis effect disposer (single-shot; a repeat call
* returns undefined without awaiting an in-flight teardown). Exact
* identity is load-bearing: a composite (generator) effect that owns a
* teardown ORDER — the agent factory's lifecycle chain — must yield THIS
* function so Cordis nests the unregistration at that yield position;
* yielding a wrapper would leave it disposing as a concurrent sibling on
* owner unload, unregistering the agent (and emitting `agent/disposed`)
* while its final turn is still draining.
*/
register(agent: Agent): () => void
```
Register a live agent. Throws if an agent with the same id is already registered. Emits `agent/created` on registration and `agent/disposed` when the calling fiber is disposed — both with the agent's scope carrier (`scopeTarget(agent, agent)`): the subject is the agent in hand, so the emits are scope-filtered regardless of which context invoked `register` (calling through `agent.ctx` scopes EFFECTS; dispatch scoping always requires passing the carrier). Returns the disposer.
- `agent` — the already-constructed agent to record in the store.
**Returns** the EXACT Cordis effect disposer (single-shot; a repeat call returns undefined without awaiting an in-flight teardown). Exact identity is load-bearing: a composite (generator) effect that owns a teardown ORDER — the agent factory's lifecycle chain — must yield THIS function so Cordis nests the unregistration at that yield position; yielding a wrapper would leave it disposing as a concurrent sibling on owner unload, unregistering the agent (and emitting `agent/disposed`) while its final turn is still draining.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L397)
### ctx.agents.enter(agent, owner)
```ts website-api
/**
* Insert an already-constructed agent without announcing it. This is the
* advanced ordered-lifecycle primitive used by the async agent factory: it
* first completes setup while the agent is unpublished, then assigns the
* returned detach closure into its pre-installed composite teardown before
* calling {@link announce}. Ordinary callers use {@link register}.
* @param agent - the prepared, unpublished agent.
* @param owner - live agent whose scoped context created this agent, or
* undefined for a top-level runtime root. This is runtime ownership, not
* the resumed session's durable parent lineage.
* @returns an idempotent closure that removes this exact entry and emits
* `agent/disposed` with listener failures contained. When called from a
* synchronous `agent/created` listener, removal and disposal wait until
* that creation dispatch unwinds.
*/
enter(agent: Agent, owner: Agent | undefined): () => void
```
Insert an already-constructed agent without announcing it. This is the advanced ordered-lifecycle primitive used by the async agent factory: it first completes setup while the agent is unpublished, then assigns the returned detach closure into its pre-installed composite teardown before calling announce. Ordinary callers use register.
- `agent` — the prepared, unpublished agent.
- `owner` — live agent whose scoped context created this agent, or undefined for a top-level runtime root. This is runtime ownership, not the resumed session's durable parent lineage.
**Returns** an idempotent closure that removes this exact entry and emits `agent/disposed` with listener failures contained. When called from a synchronous `agent/created` listener, removal and disposal wait until that creation dispatch unwinds.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L421)
### ctx.agents.announce(agent)
```ts website-api
/**
* Announce an agent previously inserted with {@link enter}.
* @param agent - the live inserted agent to announce.
* @throws if `agent` is not the exact live registry entry for its id, or its
* creation announcement already began (including a reentrant call from a
* creation listener).
*/
announce(agent: Agent): void
```
Announce an agent previously inserted with enter.
- `agent` — the live inserted agent to announce.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L496)
### ctx.agents.get(id)
```ts website-api
/**
* Look up a live agent.
* @param id - the shared agent/session id to look up.
* @returns the agent, or undefined when no live agent has that id.
*/
get(id: SessionId): Agent | undefined
```
Look up a live agent.
- `id` — the shared agent/session id to look up.
**Returns** the agent, or undefined when no live agent has that id.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L530)
### ctx.agents.isOwnedBy(id, owner)
```ts website-api
/**
* Test whether a live agent was created through one exact parent agent's
* scoped context. Runtime ownership is independent of durable session
* lineage and remains unambiguous when unrelated providers reuse an id.
* @param id - the candidate child agent's shared agent/session id.
* @param owner - the expected runtime creator agent.
* @returns true only while the exact child entry is live under that owner.
*/
isOwnedBy(id: SessionId, owner: Agent): boolean
```
Test whether a live agent was created through one exact parent agent's scoped context. Runtime ownership is independent of durable session lineage and remains unambiguous when unrelated providers reuse an id.
- `id` — the candidate child agent's shared agent/session id.
- `owner` — the expected runtime creator agent.
**Returns** true only while the exact child entry is live under that owner.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L542)
### ctx.agents.list()
```ts website-api
/**
* All live agents, in registration order.
* @returns a fresh array; mutating it does not affect the registry.
*/
list(): Agent[]
```
All live agents, in registration order.
**Returns** a fresh array; mutating it does not affect the registry.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L550)
### ctx.agents.roots()
```ts website-api
/**
* All live top-level agents in registration order. A top-level agent was
* created without an owning agent context; durable session lineage does not
* affect this runtime relation, so a resumed fork may still be a root.
* @returns a fresh array; mutating it does not affect the registry.
*/
roots(): Agent[]
```
All live top-level agents in registration order. A top-level agent was created without an owning agent context; durable session lineage does not affect this runtime relation, so a resumed fork may still be a root.
**Returns** a fresh array; mutating it does not affect the registry.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/index.ts#L560)

View File

@@ -1,41 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.approval
`ApprovalService` — provided by `@deepseek-ai/dsh-user-approval`.
Approval service that applies session policy before answerers and logs every ask/outcome pair to the requesting session. It exposes deterministic policy changes to the model through prompt and pre-step notices.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/user-approval/src/index.ts#L229)
### ctx.approval.request(req)
```ts website-api
/**
* Ask the composed answerers to decide one readonly same-process request.
* The service borrows the request, agent, session, and live signal directly.
* The request requires an open turn because the audit pair must be enclosed
* by the durable log's commit/replay boundary; an idle ask rejects before
* appending anything. The answerer phase always produces an outcome: an
* aborted signal yields `'cancelled'`, a missing or throwing answerer yields
* `'unavailable'` (fail closed), and a rogue non-vocabulary return value is
* normalized to `'unavailable'`. A failure that prevents either audit append
* from committing still rejects because returning an unlogged decision would
* violate the pair. Session contains post-commit observer failures, so an
* authoritative append cannot reject the request or suppress its matching
* audit event.
* @param req - the pending decision (agent, tool identity, reason, signal).
* @returns the closed outcome; `'allowed-once'` is the only grant.
* @throws when no turn is open or either audit event fails before the session
* append commit point.
*/
async request(req: ApprovalRequest): Promise<ApprovalOutcome>
```
Ask the composed answerers to decide one readonly same-process request. The service borrows the request, agent, session, and live signal directly. The request requires an open turn because the audit pair must be enclosed by the durable log's commit/replay boundary; an idle ask rejects before appending anything. The answerer phase always produces an outcome: an aborted signal yields `'cancelled'`, a missing or throwing answerer yields `'unavailable'` (fail closed), and a rogue non-vocabulary return value is normalized to `'unavailable'`. A failure that prevents either audit append from committing still rejects because returning an unlogged decision would violate the pair. Session contains post-commit observer failures, so an authoritative append cannot reject the request or suppress its matching audit event.
- `req` — the pending decision (agent, tool identity, reason, signal).
**Returns** the closed outcome; `'allowed-once'` is the only grant.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/user-approval/src/index.ts#L313)

View File

@@ -1,64 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.bashEnv
`BashEnvRegistry` — provided by `@deepseek-ai/dsh-tool-bash`.
Registry (`ctx.bashEnv`) for trusted, per-execution `DSH_*` variables. The namespace is rebuilt for every model bash call: ambient `DSH_*` values are discarded by the executor, then the registry's current snapshot is injected. Built-in shell facts remain owned by the registry itself while plugins can register additional, enumerable facts with effect-scoped disposal.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/bash/tool-bash/src/index.ts#L102)
### ctx.bashEnv.register(contributor)
```ts website-api
/**
* Register one environment contributor. Names and keys are unique; built-in
* keys are reserved. Registration is disposed with the calling plugin fiber.
* @param contributor - declared key ownership and per-execution resolver.
* @returns the disposer that unregisters the contribution.
*/
register(contributor: BashEnvContributor): () => void
```
Register one environment contributor. Names and keys are unique; built-in keys are reserved. Registration is disposed with the calling plugin fiber.
- `contributor` — declared key ownership and per-execution resolver.
**Returns** the disposer that unregisters the contribution.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/bash/tool-bash/src/index.ts#L123)
### ctx.bashEnv.collect(execution)
```ts website-api
/**
* Build the trusted `DSH_*` snapshot for one bash tool execution.
* @param execution - the current tool execution.
* @returns an immutable environment overlay containing built-ins and current contributions.
*/
collect(execution: ToolExecution): DshEnvironment
```
Build the trusted `DSH_*` snapshot for one bash tool execution.
- `execution` — the current tool execution.
**Returns** an immutable environment overlay containing built-ins and current contributions.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/bash/tool-bash/src/index.ts#L165)
### ctx.bashEnv.list()
```ts website-api
/**
* Enumerate plugin-contributed variables without executing their resolvers.
* @returns declarations sorted by environment variable name.
*/
list(): BashEnvVariableInfo[]
```
Enumerate plugin-contributed variables without executing their resolvers.
**Returns** declarations sorted by environment variable name.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/bash/tool-bash/src/index.ts#L197)

View File

@@ -1,88 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.bash
`BashExecutor` (abstract seam) — provided by `@deepseek-ai/dsh-bash`.
Abstract bash execution service. Subclass, implement the abstract methods, and load the subclass as a plugin — it registers as `ctx.bash` (one implementation per context; loading a second throws, which is cordis' standard duplicate-service behavior).
Implementations must honor these semantics:
- run rejects only for infrastructure failures. Nonzero exits, timeout kills, and abort kills resolve with a BashRunResult.
- start returns immediately; no timeout applies to background processes. `done` settles at process close and never rejects; spawn failures settle as `killed` with the error on stderr.
- BashProcess.readOutput is incremental: consecutive reads never repeat output. Lossy reads report truncation and available spill files.
- Disposal kills all running background processes and awaits their exit.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/bash/bash/src/index.ts#L49)
### ctx.bash.sandboxMode
```ts website-api
/**
* The sandbox mode this executor applies by default, or `undefined` when it
* does not sandbox commands.
* @returns the configured default sandbox mode, when supported.
*/
get sandboxMode(): SandboxMode | undefined
```
The sandbox mode this executor applies by default, or `undefined` when it does not sandbox commands.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/bash/bash/src/index.ts#L59)
### ctx.bash.resolve(request)
```ts website-api
/**
* Apply implementation-owned defaults and caps to a request before execution.
* @param request - the caller's request; omitted fields get this
* implementation's defaults, capped fields are clamped.
* @returns the fully-specified spec to hand to {@link run}/{@link start}.
*/
abstract resolve(request: BashExecRequest): BashExecSpec
```
Apply implementation-owned defaults and caps to a request before execution.
- `request` — the caller's request; omitted fields get this implementation's defaults, capped fields are clamped.
**Returns** the fully-specified spec to hand to `run`/`start`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/bash/bash/src/index.ts#L69)
### ctx.bash.run(spec)
```ts website-api
/**
* Run a command in the foreground; resolves when it finishes.
* @param spec - a resolved spec from {@link resolve}, never a raw request.
* @returns the outcome; nonzero exits, timeout kills, and abort kills
* resolve with a descriptive result rather than reject.
*/
abstract run(spec: BashExecSpec): Promise<BashRunResult>
```
Run a command in the foreground; resolves when it finishes.
- `spec` — a resolved spec from `resolve`, never a raw request.
**Returns** the outcome; nonzero exits, timeout kills, and abort kills resolve with a descriptive result rather than reject.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/bash/bash/src/index.ts#L77)
### ctx.bash.start(spec)
```ts website-api
/**
* Start a background process and return its handle immediately.
* @param spec - a resolved spec from {@link resolve}, never a raw request.
* @returns the live process handle (reads, kill, quiescence promise).
*/
abstract start(spec: BashExecSpec): BashProcess
```
Start a background process and return its handle immediately.
- `spec` — a resolved spec from `resolve`, never a raw request.
**Returns** the live process handle (reads, kill, quiescence promise).
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/bash/bash/src/index.ts#L84)

View File

@@ -1,65 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.codeRuntime
`CodeRuntime` (abstract seam) — provided by `@deepseek-ai/dsh-code-runtime`.
Registers one `ctx.codeRuntime` implementation. Program, budget, abort, and substrate failures resolve in CodeRunResult; only seam misuse rejects. Implementations bridge structured-cloneable bindings while treating programs as hostile peers, isolate runs from one another, and terminate and await in-flight runs during disposal.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/code-runtime/code-runtime/src/index.ts#L30)
### ctx.codeRuntime.language
```ts website-api
/**
* The source language {@link run} expects `program` to be written in, as a
* lowercase identifier. Informational, not gating — a consumer that
* generates language-specific presentation (typed SDK stubs, usage
* instructions) switches on it and fails loud on a language it cannot
* present. Well-known value: `'typescript'`.
*/
abstract readonly language: string
```
The source language run expects `program` to be written in, as a lowercase identifier. Informational, not gating — a consumer that generates language-specific presentation (typed SDK stubs, usage instructions) switches on it and fails loud on a language it cannot present. Well-known value: `'typescript'`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/code-runtime/code-runtime/src/index.ts#L38)
### ctx.codeRuntime.isolation
```ts website-api
/**
* The execution substrate, as a lowercase identifier. Informational, not
* gating — a descriptor so deployments and diagnostics can tell backends
* apart, not a security claim. Well-known values: `'worker-thread'`,
* `'process'`, `'container'`.
*/
abstract readonly isolation: string
```
The execution substrate, as a lowercase identifier. Informational, not gating — a descriptor so deployments and diagnostics can tell backends apart, not a security claim. Well-known values: `'worker-thread'`, `'process'`, `'container'`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/code-runtime/code-runtime/src/index.ts#L46)
### ctx.codeRuntime.run(request)
```ts website-api
/**
* Execute one program against the request's bindings and capture what it
* emitted. See the class doc for the resolution contract (error is a result
* field; rejection means seam misuse only).
* @param request - the program, its bindings, and the abort signal; the
* request carries everything the runtime acts on, with no hidden defaults.
* @returns the run's outcome: completion value (when transferable), the
* ordered log capture, and the failure (if any).
*/
abstract run(request: CodeRunRequest): Promise<CodeRunResult>
```
Execute one program against the request's bindings and capture what it emitted. See the class doc for the resolution contract (error is a result field; rejection means seam misuse only).
- `request` — the program, its bindings, and the abort signal; the request carries everything the runtime acts on, with no hidden defaults.
**Returns** the run's outcome: completion value (when transferable), the ordered log capture, and the failure (if any).
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/code-runtime/code-runtime/src/index.ts#L61)

View File

@@ -1,71 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.compact
`CompactService` (abstract seam) — provided by `@deepseek-ai/dsh-compact`.
Abstract compaction service. Implementations own trigger policy, retention, and summarization, and may consume a separate measurement service. A successful run replaces the selected surface span with one summary node and prevents concurrent compaction of the same session. Load one implementation per context as `ctx.compact`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/compact/compact/src/index.ts#L40)
### ctx.compact.compactIfNeeded(agent, trigger, signal)
```ts website-api
/**
* Consider automatic compaction for one explicit trigger. Pressure policy
* uses the latest durable routed request, while context-overflow policy may
* force a useful balanced reduction even below the normal threshold. Return
* `null` when no safe range can be compacted. A single oversized retained
* unit or request envelope cannot be repaired through surface compaction.
*
* @param agent - agent context owning the session surface and routing options.
* @param trigger - normal pressure or provider-confirmed context overflow.
* @param signal - cancellation signal; model-backed implementations must forward it.
* @returns the compaction result, or `null` if no compaction was needed.
*/
abstract compactIfNeeded( agent: CompactAgentContext, trigger: CompactionTrigger, signal: AbortSignal, ): Promise<CompactionResult | null>
```
Consider automatic compaction for one explicit trigger. Pressure policy uses the latest durable routed request, while context-overflow policy may force a useful balanced reduction even below the normal threshold. Return `null` when no safe range can be compacted. A single oversized retained unit or request envelope cannot be repaired through surface compaction.
- `agent` — agent context owning the session surface and routing options.
- `trigger` — normal pressure or provider-confirmed context overflow.
- `signal` — cancellation signal; model-backed implementations must forward it.
**Returns** the compaction result, or `null` if no compaction was needed.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/compact/compact/src/index.ts#L57)
### ctx.compact.compactRegion(start, end, agent, signal?)
```ts website-api
/**
* Forcibly compact a range of surface nodes into a single summary node.
* `start` and `end` name an inclusive span by surface position, not numeric seq
* order; replacements can make visible seqs non-monotonic. Both edges must be
* balanced so assistant tool calls remain paired with their results. A model-
* backed implementation forwards cancellation and rejects active, missing,
* reversed, or unbalanced ranges. The target session is `agent.session`.
* Use {@link toolPairingBalancedBefore} and {@link toolPairingBalancedAfter}
* for the edge checks.
*
* @param start - first surface seq, inclusive.
* @param end - last surface seq, inclusive.
* @param agent - context whose session is mutated and whose routing options guide summarization.
* @param signal - optional cancellation; model-backed implementations must forward it.
* @throws when compaction is active or the range is missing, reversed, or unbalanced.
* @returns the appended event seqs, summary, replaced range, and token accounting.
*/
abstract compactRegion( start: number, end: number, agent: CompactAgentContext, signal?: AbortSignal, ): Promise<CompactionResult>
```
Forcibly compact a range of surface nodes into a single summary node. `start` and `end` name an inclusive span by surface position, not numeric seq order; replacements can make visible seqs non-monotonic. Both edges must be balanced so assistant tool calls remain paired with their results. A model- backed implementation forwards cancellation and rejects active, missing, reversed, or unbalanced ranges. The target session is `agent.session`. Use toolPairingBalancedBefore and toolPairingBalancedAfter for the edge checks.
- `start` — first surface seq, inclusive.
- `end` — last surface seq, inclusive.
- `agent` — context whose session is mutated and whose routing options guide summarization.
- `signal` — optional cancellation; model-backed implementations must forward it.
**Returns** the appended event seqs, summary, replaced range, and token accounting.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/compact/compact/src/index.ts#L80)

File diff suppressed because it is too large Load Diff

View File

@@ -1,205 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.fs
`FileSystem` (abstract seam) — provided by `@deepseek-ai/dsh-fs`.
Abstract filesystem provider. Targets must preserve identity across aliases; reads expose regular UTF-8 text or typed errors, listings are stable and content-free, and mutations are atomic. Optional guards add stale protection without changing the unguarded provider contract.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/fs/fs/src/index.ts#L80)
### ctx.fs.resolve(path, opts?)
```ts website-api
/**
* Resolve a model/plugin-supplied path into a stable {@link FsTarget}. May perform I/O (a
* remote/sandboxed backend may need a round-trip to map a path to a stable identity), hence
* async even though the local backend only normalizes + realpaths.
*
* @param path - the path to resolve; relative paths resolve against `opts.cwd`.
* @param opts - optional cwd override and cancellation signal.
* @returns the stable target; the same file yields the same `targetKey`.
*/
abstract resolve(path: string, opts?: { cwd?: string; signal?: AbortSignal }): Promise<FsTarget>
```
Resolve a model/plugin-supplied path into a stable FsTarget. May perform I/O (a remote/sandboxed backend may need a round-trip to map a path to a stable identity), hence async even though the local backend only normalizes + realpaths.
- `path` — the path to resolve; relative paths resolve against `opts.cwd`.
- `opts` — optional cwd override and cancellation signal.
**Returns** the stable target; the same file yields the same `targetKey`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/fs/fs/src/index.ts#L94)
### ctx.fs.stat(target, signal?)
```ts website-api
/**
* Return target metadata, or `undefined` when the target does not exist.
* @param target - the resolved target to stat.
* @param signal - aborts the metadata round-trip.
* @returns metadata only, never content; undefined for an absent target.
*/
abstract stat(target: FsTarget, signal?: AbortSignal): Promise<FsInfo | undefined>
```
Return target metadata, or `undefined` when the target does not exist.
- `target` — the resolved target to stat.
- `signal` — aborts the metadata round-trip.
**Returns** metadata only, never content; undefined for an absent target.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/fs/fs/src/index.ts#L102)
### ctx.fs.lstat(path, opts?, signal?)
```ts website-api
/**
* Return path metadata without following the final path component when it is a
* symbolic link. This is intentionally path-shaped, not target-shaped:
* {@link resolve} follows symlinks to produce the stable identity used by
* normal reads/writes, while `lstat` lets a consumer reject the path itself
* before that follow happens.
*
* `opts.cwd` follows {@link resolve}'s cwd rules. `undefined` means the path is
* absent.
* @param path - the path to inspect; relative paths resolve against `opts.cwd`.
* @param opts - `cwd` overrides the backend's default base for relative paths.
* @param signal - aborts the metadata round-trip.
* @returns metadata only, never content; undefined for an absent path.
*/
abstract lstat(path: string, opts?: { cwd?: string }, signal?: AbortSignal): Promise<FsPathInfo | undefined>
```
Return path metadata without following the final path component when it is a symbolic link. This is intentionally path-shaped, not target-shaped: resolve follows symlinks to produce the stable identity used by normal reads/writes, while `lstat` lets a consumer reject the path itself before that follow happens.
`opts.cwd` follows resolve's cwd rules. `undefined` means the path is absent.
- `path` — the path to inspect; relative paths resolve against `opts.cwd`.
- `opts` — `cwd` overrides the backend's default base for relative paths.
- `signal` — aborts the metadata round-trip.
**Returns** metadata only, never content; undefined for an absent path.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/fs/fs/src/index.ts#L118)
### ctx.fs.readText(target, signal?)
```ts website-api
/**
* Read the whole regular text file as a single decoded string.
* @param target - the resolved target to read.
* @param signal - aborts the read.
* @returns the full decoded UTF-8 content.
*/
abstract readText(target: FsTarget, signal?: AbortSignal): Promise<string>
```
Read the whole regular text file as a single decoded string.
- `target` — the resolved target to read.
- `signal` — aborts the read.
**Returns** the full decoded UTF-8 content.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/fs/fs/src/index.ts#L126)
### ctx.fs.streamText(target, signal?)
```ts website-api
/**
* Stream the whole regular text file as decoded text chunks (same text
* semantics as {@link readText}, for large files). The backend owns
* cross-chunk UTF-8 decoding and binary rejection so the policy layer never
* touches raw bytes.
* @param target - the resolved target to read.
* @param signal - aborts the stream, including between chunks.
* @returns the chunk iterable, decoded and validated like {@link readText}.
*/
abstract streamText(target: FsTarget, signal?: AbortSignal): Promise<AsyncIterable<string>>
```
Stream the whole regular text file as decoded text chunks (same text semantics as readText, for large files). The backend owns cross-chunk UTF-8 decoding and binary rejection so the policy layer never touches raw bytes.
- `target` — the resolved target to read.
- `signal` — aborts the stream, including between chunks.
**Returns** the chunk iterable, decoded and validated like `readText`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/fs/fs/src/index.ts#L137)
### ctx.fs.listDir(target, signal?)
```ts website-api
/**
* List direct children of a directory in stable name order. Returns resolved
* child targets plus cheap metadata only; never reads file contents.
* @param target - the resolved directory target.
* @param signal - aborts the listing.
* @returns one entry per direct child, in stable name order.
*/
abstract listDir(target: FsTarget, signal?: AbortSignal): Promise<FsDirEntry[]>
```
List direct children of a directory in stable name order. Returns resolved child targets plus cheap metadata only; never reads file contents.
- `target` — the resolved directory target.
- `signal` — aborts the listing.
**Returns** one entry per direct child, in stable name order.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/fs/fs/src/index.ts#L146)
### ctx.fs.writeText(target, content, expected?, signal?)
```ts website-api
/**
* Atomically create or replace UTF-8 text. `expected` guards intent and
* staleness; omission allows unconditional overwrite.
* @param target - the resolved target to write.
* @param content - the full new file content.
* @param expected - the write intent guarding the write; omit for unconditional.
* @param signal - aborts before the atomic rename takes effect.
* @returns the outcome, including the version the write produced.
*/
abstract writeText(target: FsTarget, content: string, expected?: FsWriteIntent, signal?: AbortSignal): Promise<FsWriteOutcome>
```
Atomically create or replace UTF-8 text. `expected` guards intent and staleness; omission allows unconditional overwrite.
- `target` — the resolved target to write.
- `content` — the full new file content.
- `expected` — the write intent guarding the write; omit for unconditional.
- `signal` — aborts before the atomic rename takes effect.
**Returns** the outcome, including the version the write produced.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/fs/fs/src/index.ts#L157)
### ctx.fs.editText(target, edit, expected?, signal?)
```ts website-api
/**
* Atomically edit literal text. When supplied, the version guard is checked
* before matching so stale content reports `FS_STALE_VERSION`; omission edits
* the current content without a freshness precondition.
* @param target - the resolved target to edit.
* @param edit - the literal search/replace request.
* @param expected - the version guard; omit for an unconditional edit.
* @param signal - aborts before the atomic rename takes effect.
* @returns the outcome, including the version the edit produced.
*/
abstract editText(target: FsTarget, edit: FsEditRequest, expected?: { version: FsVersion }, signal?: AbortSignal): Promise<FsEditOutcome>
```
Atomically edit literal text. When supplied, the version guard is checked before matching so stale content reports `FS_STALE_VERSION`; omission edits the current content without a freshness precondition.
- `target` — the resolved target to edit.
- `edit` — the literal search/replace request.
- `expected` — the version guard; omit for an unconditional edit.
- `signal` — aborts before the atomic rename takes effect.
**Returns** the outcome, including the version the edit produced.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/fs/fs/src/index.ts#L169)

View File

@@ -1,94 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.llm
`LlmService` — provided by `@deepseek-ai/dsh-llm`.
The abstract `llm` service: an adapter registry plus a streaming model-call surface, interceptable via the `llm/stream` waterfall.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/llm/llm/src/index.ts#L97)
### ctx.llm.registerAdapter(providers, adapter)
```ts website-api
/**
* Register an adapter for the given provider routes. Throws `LlmError` with code
* `DUPLICATE_ADAPTER` if any provider already has an adapter (all-or-nothing).
* Disposed with the fiber.
* @param providers - every provider route this adapter should serve.
* @param adapter - the adapter that streams calls for those providers.
* @returns the disposer that unregisters all of them.
*/
registerAdapter(providers: string[], adapter: LlmAdapter): () => void
```
Register an adapter for the given provider routes. Throws `LlmError` with code `DUPLICATE_ADAPTER` if any provider already has an adapter (all-or-nothing). Disposed with the fiber.
- `providers` — every provider route this adapter should serve.
- `adapter` — the adapter that streams calls for those providers.
**Returns** the disposer that unregisters all of them.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/llm/llm/src/index.ts#L112)
### ctx.llm.listProviders()
```ts website-api
/**
* Describe provider routes with a registered adapter.
* @returns detached provider metadata in registration order.
*/
listProviders(): LlmProviderInfo[]
```
Describe provider routes with a registered adapter.
**Returns** detached provider metadata in registration order.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/llm/llm/src/index.ts#L143)
### ctx.llm.listModels(provider)
```ts website-api
/**
* Discover models advertised by one registered provider. Catalog membership
* is advisory and never changes routing or request validation.
* @param provider - registered provider route to inspect.
* @returns detached model metadata in adapter-preferred order.
*/
async listModels(provider: string): Promise<LlmModelInfo[]>
```
Discover models advertised by one registered provider. Catalog membership is advisory and never changes routing or request validation.
- `provider` — registered provider route to inspect.
**Returns** detached model metadata in adapter-preferred order.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/llm/llm/src/index.ts#L153)
### ctx.llm.stream(options)
```ts website-api
/**
* Stream one model call as raw chunks (token-level deltas). Throws
* `LlmError` with code `NO_ADAPTER` if no adapter is registered for
* `options.provider`. Replay state is retained only when the same adapter
* instance owns its historical provider and the target provider. Final
* adapter selection, dispatch, and iteration failures retain their original
* Error identity and are tagged in a call-local scope for narrow agent-loop
* request recovery; middleware and nested-call failures remain untagged for
* the outer call.
* @param options - the full request; `options.provider` selects the adapter.
* @returns the chunk stream, possibly wrapped by `llm/stream` listeners.
*/
stream(options: GenerateOptions): AsyncIterable<StreamChunk>
```
Stream one model call as raw chunks (token-level deltas). Throws `LlmError` with code `NO_ADAPTER` if no adapter is registered for `options.provider`. Replay state is retained only when the same adapter instance owns its historical provider and the target provider. Final adapter selection, dispatch, and iteration failures retain their original Error identity and are tagged in a call-local scope for narrow agent-loop request recovery; middleware and nested-call failures remain untagged for the outer call.
- `options` — the full request; `options.provider` selects the adapter.
**Returns** the chunk stream, possibly wrapped by `llm/stream` listeners.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/llm/llm/src/index.ts#L264)

View File

@@ -1,104 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.permission
`PermissionService` — provided by `@deepseek-ai/dsh-permission`.
Owns the deployment's permission presets and their write path. Requires a confining `ctx.bash` executor and `ctx.approval`; unmatched knob values are reported as CUSTOM_PRESET, not an error.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/permission/src/index.ts#L94)
### ctx.permission.names
```ts website-api
/**
* The advertised preset names, in the preset table's declaration order.
* @returns every switchable preset name.
*/
get names(): readonly string[]
```
The advertised preset names, in the preset table's declaration order.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/permission/src/index.ts#L134)
### ctx.permission.current(events)
```ts website-api
/**
* Resolve the preset matching the effective knob values. A still-matching
* last selection wins shared-bundle ties; otherwise the first table match
* wins, or {@link CUSTOM_PRESET} when no entry matches.
* @param events - the session's events in log order.
* @returns the effective preset name, or `custom` when nothing matches.
*/
current(events: readonly SessionEvent[]): string
```
Resolve the preset matching the effective knob values. A still-matching last selection wins shared-bundle ties; otherwise the first table match wins, or CUSTOM_PRESET when no entry matches.
- `events` — the session's events in log order.
**Returns** the effective preset name, or `custom` when nothing matches.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/permission/src/index.ts#L145)
### ctx.permission.resolve(name)
```ts website-api
/**
* Resolve a preset's knob bundle.
* @param name - the preset name to resolve.
* @returns the configured bundle.
* @throws when `name` is not in the table.
*/
resolve(name: string): PresetSpec
```
Resolve a preset's knob bundle.
- `name` — the preset name to resolve.
**Returns** the configured bundle.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/permission/src/index.ts#L166)
### ctx.permission.optionOf(name)
```ts website-api
/**
* Build the client option for a table entry or {@link CUSTOM_PRESET}. A
* missing label falls back to the table key.
* @param name - a table key, or `custom`.
* @returns the option a client renders.
* @throws when `name` is neither a table key nor `custom`.
*/
optionOf(name: string): PresetOption
```
Build the client option for a table entry or CUSTOM_PRESET. A missing label falls back to the table key.
- `name` — a table key, or `custom`.
**Returns** the option a client renders.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/permission/src/index.ts#L181)
### ctx.permission.set(session, name)
```ts website-api
/**
* Record a changed preset, then update each changed knob through its own
* setter. Selecting the effective preset again appends nothing.
* @param session - the session the switch belongs to.
* @param name - the preset to switch to; unknown names throw.
*/
set(session: Session, name: string): void
```
Record a changed preset, then update each changed knob through its own setter. Selecting the effective preset again appends nothing.
- `session` — the session the switch belongs to.
- `name` — the preset to switch to; unknown names throw.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/permission/src/index.ts#L195)

View File

@@ -1,35 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.sandbox
`SandboxProvider` (abstract seam) — provided by `@deepseek-ai/dsh-sandbox`.
Abstract process-sandbox service. confine must return enforcing argv or fail closed at wrap or runner-execution time; silent unconfined passthrough is forbidden. Functional probes arbitrate multi-runner chains and may be skipped for a sole candidate, whose own refusal remains the fail-closed end.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/sandbox/sandbox/src/index.ts#L111)
### ctx.sandbox.confine(argv, policy)
```ts website-api
/**
* Wrap `argv` so it executes confined under `policy` on this host; the
* caller spawns the returned argv in place of its own.
* @param argv - the exact argv the caller is about to spawn (program plus
* arguments), NOT a shell string — a shell-shaped consumer passes
* `['bash', '-c', command]`.
* @param policy - the file-effect policy this execution runs under,
* carried per call (see {@link SandboxPolicy}).
* @returns the argv to spawn instead, plus the enforcement completeness
* the selected backend achieves for it.
*/
abstract confine(argv: readonly string[], policy: SandboxPolicy): ConfinedArgv
```
Wrap `argv` so it executes confined under `policy` on this host; the caller spawns the returned argv in place of its own.
- `argv` — the exact argv the caller is about to spawn (program plus arguments), NOT a shell string — a shell-shaped consumer passes `['bash', '-c', command]`.
- `policy` — the file-effect policy this execution runs under, carried per call (see `SandboxPolicy`).
**Returns** the argv to spawn instead, plus the enforcement completeness the selected backend achieves for it.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/sandbox/sandbox/src/index.ts#L127)

View File

@@ -1,109 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.sessionPersistence
`SessionPersistence` (abstract seam) — provided by `@deepseek-ai/dsh-session-persistence`.
Durable append-only session storage. Implementations preserve contiguous, losslessly JSON-serializable events; append resolves only after durability, and load balances a complete interrupted tail without rewriting committed events.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/session-persistence/session-persistence/src/index.ts#L42)
### ctx.sessionPersistence.locate(meta)
```ts website-api
/**
* Resolve this backend's independent local artifact for a session without
* reading, creating, flushing, or otherwise materializing it. Backends such
* as SQLite that do not own one artifact per session return `undefined`.
* @param meta - the immutable session header whose artifact is requested.
* @returns the backend-specific absolute location, when one exists.
*/
abstract locate(meta: SessionHeader): SessionLocation | undefined
```
Resolve this backend's independent local artifact for a session without reading, creating, flushing, or otherwise materializing it. Backends such as SQLite that do not own one artifact per session return `undefined`.
- `meta` — the immutable session header whose artifact is requested.
**Returns** the backend-specific absolute location, when one exists.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/session-persistence/session-persistence/src/index.ts#L54)
### ctx.sessionPersistence.create(meta)
```ts website-api
/**
* Register a new session's metadata. A backend MAY defer the physical write
* until the first {@link append} (lazy materialization), in which case a
* created-but-never-appended session is absent from {@link list}
* — abandoned sessions leave nothing behind.
* @param meta - the immutable header (id, version, cwd, lineage) to record.
*/
abstract create(meta: SessionHeader): Promise<void>
```
Register a new session's metadata. A backend MAY defer the physical write until the first append (lazy materialization), in which case a created-but-never-appended session is absent from list — abandoned sessions leave nothing behind.
- `meta` — the immutable header (id, version, cwd, lineage) to record.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/session-persistence/session-persistence/src/index.ts#L63)
### ctx.sessionPersistence.append(id, events)
```ts website-api
/**
* Durably persist a batch of events (called from the write-behind drain at
* the `session/flush` checkpoint). Honors the append-only and contiguous-seq
* contracts: the first event's `seq` MUST equal the stored next-seq (after
* `load` has durably closed any interrupted turn). Rejects non-JSON-
* serializable `event.data` with an error naming the offending event type.
* @param id - the session the batch belongs to.
* @param events - the contiguous batch to persist, in seq order.
*/
abstract append(id: SessionId, events: readonly SessionEvent[]): Promise<void>
```
Durably persist a batch of events (called from the write-behind drain at the `session/flush` checkpoint). Honors the append-only and contiguous-seq contracts: the first event's `seq` MUST equal the stored next-seq (after `load` has durably closed any interrupted turn). Rejects non-JSON- serializable `event.data` with an error naming the offending event type.
- `id` — the session the batch belongs to.
- `events` — the contiguous batch to persist, in seq order.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/session-persistence/session-persistence/src/index.ts#L74)
### ctx.sessionPersistence.load(id)
```ts website-api
/**
* Load a header and balanced contiguous log. A complete interrupted final
* turn is preserved and durably closed with missing tool errors plus any open
* step and turn boundaries; only a torn final record is discarded. Unknown
* versions and corruption in the committed prefix reject.
* @param id - the persisted session to reload.
* @returns the header and a log ending on a balanced `turn/end`.
*/
abstract load(id: SessionId): Promise<{ meta: SessionHeader; events: SessionEvent[] }>
```
Load a header and balanced contiguous log. A complete interrupted final turn is preserved and durably closed with missing tool errors plus any open step and turn boundaries; only a torn final record is discarded. Unknown versions and corruption in the committed prefix reject.
- `id` — the persisted session to reload.
**Returns** the header and a log ending on a balanced `turn/end`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/session-persistence/session-persistence/src/index.ts#L84)
### ctx.sessionPersistence.list()
```ts website-api
/**
* Lightweight listing from metadata, without a full-log parse.
* @returns one header per materialized session.
*/
abstract list(): Promise<SessionHeader[]>
```
Lightweight listing from metadata, without a full-log parse.
**Returns** one header per materialized session.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/session-persistence/session-persistence/src/index.ts#L90)

View File

@@ -1,103 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.sessionQuery
`SessionQueryService` — provided by `@deepseek-ai/dsh-session-query`.
Live-preferred logical-corpus exact-read and relationship-tracing service.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/session-query/session-query/src/index.ts#L38)
### ctx.sessionQuery.listSessions()
```ts website-api
/**
* List the complete logical corpus using live-preferred records.
* @returns deterministic newest-first cloned session records.
*/
listSessions(): Promise<SessionRecord[]>
```
List the complete logical corpus using live-preferred records.
**Returns** deterministic newest-first cloned session records.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/session-query/session-query/src/index.ts#L63)
### ctx.sessionQuery.listEvents(sessionId)
```ts website-api
/**
* List lightweight raw-log event records for one logical session.
* @param sessionId - live-preferred session id to read.
* @returns event records in ascending seq order.
*/
async listEvents(sessionId: SessionId): Promise<SessionEventRecord[]>
```
List lightweight raw-log event records for one logical session.
- `sessionId` — live-preferred session id to read.
**Returns** event records in ascending seq order.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/session-query/session-query/src/index.ts#L72)
### ctx.sessionQuery.traceSession(sessionId)
```ts website-api
/**
* Trace known ancestry and descendants from one corpus observation.
* @param sessionId - logical session id to trace.
* @returns a complete lineage or an explicit unresolved parent boundary.
* @throws when corpus resolution fails, the target is absent, or its known ancestry cycles.
*/
async traceSession(sessionId: SessionId): Promise<SessionLineageTrace>
```
Trace known ancestry and descendants from one corpus observation.
- `sessionId` — logical session id to trace.
**Returns** a complete lineage or an explicit unresolved parent boundary.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/session-query/session-query/src/index.ts#L83)
### ctx.sessionQuery.traceEvent(request)
```ts website-api
/**
* Trace one event's direct positional and provenance relationships.
* @param request - target session id and event seq.
* @returns direct links plus the target's positional replacement chain.
* @throws when source resolution fails, the target is absent, or surface/provenance validation fails.
*/
async traceEvent(request: SessionEventTraceRequest): Promise<SessionEventTrace>
```
Trace one event's direct positional and provenance relationships.
- `request` — target session id and event seq.
**Returns** direct links plus the target's positional replacement chain.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/session-query/session-query/src/index.ts#L94)
### ctx.sessionQuery.readEvent(request)
```ts website-api
/**
* Read one full event plus a bounded raw-log context window.
* @param request - target session/seq and context sizes.
* @returns cloned target and neighboring events.
*/
async readEvent(request: SessionEventReadRequest): Promise<SessionEventWindow>
```
Read one full event plus a bounded raw-log context window.
- `request` — target session/seq and context sizes.
**Returns** cloned target and neighboring events.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/session-query/session-query/src/index.ts#L104)

View File

@@ -1,223 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.sessions
`SessionStore` — provided by `@deepseek-ai/dsh-session`.
In-memory session store (`ctx.sessions`).
Persistence is intentionally not implemented here — persistence plugins subscribe to `session/event` and flush on `session/flush` / dispose.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L577)
### ctx.sessions.create(id?, options?)
```ts website-api
/**
* Create a session owned by the calling fiber: disposing that fiber stops
* event notification and removes the session from the store. `options.seed`
* populates the session with a copy of those events (replay/fork);
* `options.meta` attaches creation metadata (validated absolute `cwd`,
* `parentSession` lineage) as the immutable {@link SessionHeader} (the store
* fills `version`/`id`/`createdAt`).
*
* For an agent whose session must be torn down IN ORDER with its loop (so the
* loop's final flush is captured before the store attachment ends), do NOT use this
* — fold the session lifecycle into the agent's own effect via
* {@link prepare} + {@link enter} + {@link announce} (see
* `dsh-agent-loop`'s creation transaction).
*
* @param id - the session id; omitted, the store mints `session-<n>`.
* @param options - seed events and/or creation metadata for the header.
* @returns the live session, already entered and announced.
* @throws if a session with `id` already exists, metadata is not a plain
* lossless-JSON record with valid scalar fields, or `meta.cwd` is a
* non-absolute path (storage backends key directories off it).
*/
create(id?: SessionId, options?: CreateSessionOptions): Session
```
Create a session owned by the calling fiber: disposing that fiber stops event notification and removes the session from the store. `options.seed` populates the session with a copy of those events (replay/fork); `options.meta` attaches creation metadata (validated absolute `cwd`, `parentSession` lineage) as the immutable SessionHeader (the store fills `version`/`id`/`createdAt`).
For an agent whose session must be torn down IN ORDER with its loop (so the loop's final flush is captured before the store attachment ends), do NOT use this — fold the session lifecycle into the agent's own effect via prepare + enter + announce (see `dsh-agent-loop`'s creation transaction).
- `id` — the session id; omitted, the store mints `session-<n>`.
- `options` — seed events and/or creation metadata for the header.
**Returns** the live session, already entered and announced.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L606)
### ctx.sessions.prepare(id?, options?)
```ts website-api
/**
* Build a session WITHOUT entering it into the store — validate the id/cwd and
* construct the {@link Session} (with its immutable {@link SessionHeader}).
* Pairs with {@link enter} + {@link announce}: a caller that owns a composite
* `ctx.effect` (the agent factory) folds the session lifecycle into that ONE
* effect so a fiber unload tears the session + agent down as a single ORDERED
* chain rather than as racing sibling effects — which would remove the publication hooks
* before the loop's closing `session/flush`, dropping the closing events.
*
* @param id - the session id; omitted, the store mints `session-<n>`.
* @param options - seed events and/or creation metadata for the header.
* @returns the constructed session, NOT yet in the store.
* @throws if a session with `id` already exists, metadata is not a plain
* lossless-JSON record with valid scalar fields, or `meta.cwd` is a
* non-absolute path.
*/
prepare(id?: SessionId, options?: CreateSessionOptions): Session
```
Build a session WITHOUT entering it into the store — validate the id/cwd and construct the Session (with its immutable SessionHeader). Pairs with enter + announce: a caller that owns a composite `ctx.effect` (the agent factory) folds the session lifecycle into that ONE effect so a fiber unload tears the session + agent down as a single ORDERED chain rather than as racing sibling effects — which would remove the publication hooks before the loop's closing `session/flush`, dropping the closing events.
- `id` — the session id; omitted, the store mints `session-<n>`.
- `options` — seed events and/or creation metadata for the header.
**Returns** the constructed session, NOT yet in the store.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L635)
### ctx.sessions.enter(session)
```ts website-api
/**
* Enter a {@link prepare}d session into the store: install the module-private
* append publication hooks and add it to the store. Returns the DETACH
* disposer (hooks + store removal). Does NOT emit `session/created` —
* the caller yields this disposer inside its effect and THEN calls
* {@link announce}, so a throwing `session/created` listener rolls the attach
* back instead of leaking it.
*
* Re-checks the id for a duplicate: `prepare` and `enter` are public
* cross-package primitives and a caller may interleave arbitrary work (or
* another create) between them, so a stale prepared session must NOT overwrite
* a live store entry of the same id — its detach disposer would later delete
* the REAL session. The {@link create} convenience and the agent factory call
* the two back-to-back so they never trip this, but the public seam cannot
* assume that.
*
* @param session - a {@link prepare}d session not yet in the store.
* @returns the detach disposer (publication hooks + store removal). When called from
* a synchronous `session/created` listener, removal and disposal wait until
* that creation dispatch unwinds.
* @throws if a session with this id is already in the store.
*/
enter(session: Session): () => void
```
Enter a prepared session into the store: install the module-private append publication hooks and add it to the store. Returns the DETACH disposer (hooks + store removal). Does NOT emit `session/created` — the caller yields this disposer inside its effect and THEN calls announce, so a throwing `session/created` listener rolls the attach back instead of leaking it.
Re-checks the id for a duplicate: `prepare` and `enter` are public cross-package primitives and a caller may interleave arbitrary work (or another create) between them, so a stale prepared session must NOT overwrite a live store entry of the same id — its detach disposer would later delete the REAL session. The create convenience and the agent factory call the two back-to-back so they never trip this, but the public seam cannot assume that.
- `session` — a `prepare`d session not yet in the store.
**Returns** the detach disposer (publication hooks + store removal). When called from a synchronous `session/created` listener, removal and disposal wait until that creation dispatch unwinds.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L679)
### ctx.sessions.announce(session)
```ts website-api
/** Emit `session/created` exactly once for an {@link enter}ed session (with
* the carrier {@link enter} captured). Separate from {@link enter} so the
* caller can yield the detach disposer first (rollback safety — see
* {@link enter}).
* @param session - the entered session to announce to listeners.
* @throws if the session is not live or its announcement already began,
* including a reentrant call from a creation listener. */
announce(session: Session): void
```
Emit `session/created` exactly once for an entered session (with the carrier enter captured). Separate from enter so the caller can yield the detach disposer first (rollback safety — see enter).
- `session` — the entered session to announce to listeners.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L734)
### ctx.sessions.flush(session)
```ts website-api
/**
* Dispatch the awaited `session/flush` durability checkpoint for `session`,
* with the carrier captured at {@link enter}. THE flush entry point: the
* store owns the carrier, so callers (the loop's turn-end checkpoint, idle
* injection, teardown drains) must come through here rather than dispatch a
* raw `ctx.parallel('session/flush', …)` — one owner, one spelling, and the
* scoped-dispatch invariant can pin it.
* @param session - the session whose buffered events must reach durable storage.
* @returns resolves when every flush listener has settled; after all settle,
* rejects with the first registered listener failure if any listener failed.
*/
async flush(session: Session): Promise<void>
```
Dispatch the awaited `session/flush` durability checkpoint for `session`, with the carrier captured at enter. THE flush entry point: the store owns the carrier, so callers (the loop's turn-end checkpoint, idle injection, teardown drains) must come through here rather than dispatch a raw `ctx.parallel('session/flush', …)` — one owner, one spelling, and the scoped-dispatch invariant can pin it.
- `session` — the session whose buffered events must reach durable storage.
**Returns** resolves when every flush listener has settled; after all settle, rejects with the first registered listener failure if any listener failed.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L786)
### ctx.sessions.get(id)
```ts website-api
/**
* Look up a live session.
* @param id - the session id to look up.
* @returns the session, or undefined when no live session has that id.
*/
get(id: SessionId): Session | undefined
```
Look up a live session.
- `id` — the session id to look up.
**Returns** the session, or undefined when no live session has that id.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L818)
### ctx.sessions.list()
```ts website-api
/**
* All live sessions, in creation order.
* @returns a fresh array; mutating it does not affect the store.
*/
list(): Session[]
```
All live sessions, in creation order.
**Returns** a fresh array; mutating it does not affect the store.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L826)
### ctx.sessions.fork(source, boundary?, childSessionId?)
```ts website-api
/**
* Create a live child session from a turn-enclosed prefix of a live source.
* `boundary` is an inclusive source event seq; omitted means the source's
* current last event. A non-empty selected slice must end at `turn/end`.
*
* @param source - Live source session object or id.
* @param boundary - Inclusive source event seq to fork through; omitted means
* the source's current last event, and omitted on an empty source forks an
* empty child.
* @param childSessionId - Optional child session id; omitted delegates to
* `SessionStore`'s id policy.
* @returns The created live child session.
*/
fork(source: SessionForkSource, boundary?: number, childSessionId?: SessionId): Session
```
Create a live child session from a turn-enclosed prefix of a live source. `boundary` is an inclusive source event seq; omitted means the source's current last event. A non-empty selected slice must end at `turn/end`.
- `source` — Live source session object or id.
- `boundary` — Inclusive source event seq to fork through; omitted means the source's current last event, and omitted on an empty source forks an empty child.
- `childSessionId` — Optional child session id; omitted delegates to `SessionStore`'s id policy.
**Returns** The created live child session.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L843)

View File

@@ -1,96 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.skills
`SkillService` — provided by `@deepseek-ai/dsh-skill`.
Registry of skill providers. It merges provider catalogs with stable first-wins duplicate handling, exposes sorted model-visible summaries, and loads full skill bodies on demand.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/skill/skill/src/index.ts#L141)
### ctx.skills.registerProvider(provider)
```ts website-api
/**
* Register a borrowed same-process provider synchronously during plugin apply. Duplicate and
* reserved names throw; remote initialization belongs in `list()`. Fiber disposal unregisters
* the provider and invalidates catalog caches.
* @param provider - the provider to register by `provider.name`.
* @returns the exact Cordis effect disposer that unregisters this provider;
* composite effects may yield it directly to preserve teardown ordering.
*/
registerProvider(provider: SkillProvider): () => void
```
Register a borrowed same-process provider synchronously during plugin apply. Duplicate and reserved names throw; remote initialization belongs in `list()`. Fiber disposal unregisters the provider and invalidates catalog caches.
- `provider` — the provider to register by `provider.name`.
**Returns** the exact Cordis effect disposer that unregisters this provider; composite effects may yield it directly to preserve teardown ordering.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/skill/skill/src/index.ts#L168)
### ctx.skills.register(skill)
```ts website-api
/**
* Register a borrowed readonly runtime skill. Project entries outrank runtime entries, which
* outrank user entries. Same-name runtime entries are first-wins; a duplicate logs a warning and
* receives a no-op disposer so it cannot remove the winner.
* @param skill - the complete skill definition to expose for discovery.
* @returns the exact Cordis effect disposer, preserving composite teardown order and invalidating caches.
*/
register(skill: SkillRegistration): () => void
```
Register a borrowed readonly runtime skill. Project entries outrank runtime entries, which outrank user entries. Same-name runtime entries are first-wins; a duplicate logs a warning and receives a no-op disposer so it cannot remove the winner.
- `skill` — the complete skill definition to expose for discovery.
**Returns** the exact Cordis effect disposer, preserving composite teardown order and invalidating caches.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/skill/skill/src/index.ts#L199)
### ctx.skills.list(options?)
```ts website-api
/**
* List model-invocable skill summaries for a workspace. Lookup options and
* provider candidates are readonly same-process values borrowed throughout
* discovery.
* @param options - lookup options; `cwd` selects project roots and `signal` cancels discovery.
* @returns sorted summaries, excluding skills disabled for model invocation.
*/
async list(options: SkillLookupOptions = {}): Promise<SkillSummary[]>
```
List model-invocable skill summaries for a workspace. Lookup options and provider candidates are readonly same-process values borrowed throughout discovery.
- `options` — lookup options; `cwd` selects project roots and `signal` cancels discovery.
**Returns** sorted summaries, excluding skills disabled for model invocation.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/skill/skill/src/index.ts#L230)
### ctx.skills.get(name, options?)
```ts website-api
/**
* Load and validate the winning candidate, passing its opaque discovery locator back to the
* provider. Cancellation is rechecked after selection, including cache hits, and raced against
* loading so an uncooperative provider cannot hang the caller.
* @param name - kebab-case skill name.
* @param options - lookup options; `cwd` selects workspace-sensitive skills and `signal` cancels work.
* @returns the full skill, including body content, or `undefined`.
*/
async get(name: string, options: SkillLookupOptions = {}): Promise<SkillDefinition | undefined>
```
Load and validate the winning candidate, passing its opaque discovery locator back to the provider. Cancellation is rechecked after selection, including cache hits, and raced against loading so an uncooperative provider cannot hang the caller.
- `name` — kebab-case skill name.
- `options` — lookup options; `cwd` selects workspace-sensitive skills and `signal` cancels work.
**Returns** the full skill, including body content, or `undefined`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/skill/skill/src/index.ts#L246)

View File

@@ -1,32 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.spillStore
`SpillStore` (abstract seam) — provided by `@deepseek-ai/dsh-spill`.
Abstract spill storage service. Subclass, implement saveText, and load the subclass as a plugin — it registers as `ctx.spillStore` (one implementation per context; loading a second throws, cordis' standard duplicate-service behavior).
Semantics every implementation must honor:
- saveText persists the FULL `content` verbatim and returns an opaque locator, exact byte length, and model-facing retrieval guidance.
- Storage is scoped by the request's SaveTextSpill.owner session; the backend chooses a private (not world-readable) location and a collision-free name derived from — never equal to — the caller's `suggestedName`.
- `saveText` REJECTS on a real storage failure (permissions, ENOSPC, backend unavailable); the caller decides how to degrade (the spill policy treats a rejection as best-effort and keeps the inline result).
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/spill/spill/src/index.ts#L45)
### ctx.spillStore.saveText(input)
```ts website-api
/**
* Persist `input.content` to a session-scoped spill artifact.
* @param input - the owner, provenance, suggested name, and full text to save.
* @returns the saved artifact's {@link SpillRef}; rejects on a storage failure.
*/
abstract saveText(input: SaveTextSpill): Promise<SpillRef>
```
Persist `input.content` to a session-scoped spill artifact.
- `input` — the owner, provenance, suggested name, and full text to save.
**Returns** the saved artifact's `SpillRef`; rejects on a storage failure.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/spill/spill/src/index.ts#L55)

View File

@@ -1,89 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.subagents
`SubagentService` — provided by `@deepseek-ai/dsh-subagent`.
Named provider registry and capability-checked start surface.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/subagent/subagent/src/index.ts#L153)
### ctx.subagents.registerProvider(provider)
```ts website-api
/**
* Register a provider under its name. Registration is effect-scoped and HMR
* safe; removing a provider blocks new starts but does not revoke runs that
* were already returned to their holders.
* @param provider - the trusted provider implementation.
* @returns the exact Cordis effect disposer.
*/
registerProvider(provider: SubagentProvider): () => void
```
Register a provider under its name. Registration is effect-scoped and HMR safe; removing a provider blocks new starts but does not revoke runs that were already returned to their holders.
- `provider` — the trusted provider implementation.
**Returns** the exact Cordis effect disposer.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/subagent/subagent/src/index.ts#L167)
### ctx.subagents.getProvider(name)
```ts website-api
/**
* Look up a provider by name.
* @param name - the provider name.
* @returns the provider, or undefined when absent.
*/
getProvider(name: string): SubagentProvider | undefined
```
Look up a provider by name.
- `name` — the provider name.
**Returns** the provider, or undefined when absent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/subagent/subagent/src/index.ts#L190)
### ctx.subagents.list()
```ts website-api
/**
* List registered provider names in insertion order.
* @returns the registered names.
*/
list(): string[]
```
List registered provider names in insertion order.
**Returns** the registered names.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/subagent/subagent/src/index.ts#L198)
### ctx.subagents.start(name, request)
```ts website-api
/**
* Establish a ready child on the named provider. Capability and semantic
* checks run before delegation. Provider ownership lasts until its promise
* fulfills; a rejection therefore has no run for the caller to dispose and
* emits no run lifecycle events.
* @param name - the provider to use.
* @param request - child prompt, parent, signal, and optional capabilities.
* @returns the ready holder-owned run.
*/
async start(name: string, request: SubagentStartRequest): Promise<SubagentRun>
```
Establish a ready child on the named provider. Capability and semantic checks run before delegation. Provider ownership lasts until its promise fulfills; a rejection therefore has no run for the caller to dispose and emits no run lifecycle events.
- `name` — the provider to use.
- `request` — child prompt, parent, signal, and optional capabilities.
**Returns** the ready holder-owned run.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/subagent/subagent/src/index.ts#L211)

View File

@@ -1,96 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.systemPrompt
`SystemPrompt` — provided by `@deepseek-ai/dsh-system-prompt`.
Registry service for the prompt inputs assembled before each model step.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/system-prompt/src/index.ts#L209)
### ctx.systemPrompt.section(section)
```ts website-api
/**
* Register an ordered prompt section in the calling context's scope. A scoped
* section shadows a global section with the same name; duplicates within one
* layer and non-finite orders throw. Registration and disposal emit
* `system-prompt/change`.
* @param section - the section to register.
* @returns the exact Cordis effect disposer.
*/
section(section: PromptSection): () => void
```
Register an ordered prompt section in the calling context's scope. A scoped section shadows a global section with the same name; duplicates within one layer and non-finite orders throw. Registration and disposal emit `system-prompt/change`.
- `section` — the section to register.
**Returns** the exact Cordis effect disposer.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/system-prompt/src/index.ts#L250)
### ctx.systemPrompt.tools(provider)
```ts website-api
/**
* Register a tool-schema provider in the calling context's scope. Global and
* matching scoped providers both contribute; returning the reserved
* {@link TOOL_ORDER_REST} name makes assembly fail.
* @param provider - evaluated for each assembly with its context.
* @returns the exact Cordis effect disposer.
*/
tools(provider: (context: AssembleContext) => ToolProviderResult): () => void
```
Register a tool-schema provider in the calling context's scope. Global and matching scoped providers both contribute; returning the reserved TOOL_ORDER_REST name makes assembly fail.
- `provider` — evaluated for each assembly with its context.
**Returns** the exact Cordis effect disposer.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/system-prompt/src/index.ts#L291)
### ctx.systemPrompt.variable(name, provider)
```ts website-api
/**
* Register a prompt variable in the calling context's scope. Scoped values
* shadow globals; invalid or duplicate names throw. A provider may return
* `undefined`, but rendering a section that references that value then fails.
* @param name - the `[a-z][a-z0-9_]*` reference name.
* @param provider - evaluated for each assembly.
* @returns the exact Cordis effect disposer.
*/
variable(name: string, provider: (context: AssembleContext) => string | undefined): () => void
```
Register a prompt variable in the calling context's scope. Scoped values shadow globals; invalid or duplicate names throw. A provider may return `undefined`, but rendering a section that references that value then fails.
- `name` — the `[a-z][a-z0-9_]*` reference name.
- `provider` — evaluated for each assembly.
**Returns** the exact Cordis effect disposer.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/system-prompt/src/index.ts#L325)
### ctx.systemPrompt.assemble(context?)
```ts website-api
/**
* Assemble global and scoped providers, detach tool parameters, apply
* canonical ordering, then run the assembly waterfall. Scoped sections and
* variables shadow globals; the returned waterfall value is authoritative.
* @param context - the optional scope and plugin-defined assembly fields.
* @returns the authoritative post-waterfall assembly.
*/
async assemble(context: AssembleContext = {}): Promise<PromptAssembly>
```
Assemble global and scoped providers, detach tool parameters, apply canonical ordering, then run the assembly waterfall. Scoped sections and variables shadow globals; the returned waterfall value is authoritative.
- `context` — the optional scope and plugin-defined assembly fields.
**Returns** the authoritative post-waterfall assembly.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/system-prompt/src/index.ts#L365)

View File

@@ -1,191 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.tasks
`TaskService` — provided by `@deepseek-ai/dsh-tasks`.
The `tasks` service: the runtime-global background task registry. See the module doc for the ownership, isolation, and lifecycle contracts.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/tasks/tasks/src/index.ts#L76)
### ctx.tasks.start(spec)
```ts website-api
/**
* Preflight access, validation, and owner cleanup before starting and
* atomically registering work. A throwing starter leaves nothing registered;
* after it returns, registration cannot fail. Settlement records the outcome,
* notifies listeners, and releases waiters.
* @param spec - task identity, owner, and synchronous starter.
* @returns the registry-issued `<kind>-N` id.
*/
start(spec: TaskStart): TaskId
```
Preflight access, validation, and owner cleanup before starting and atomically registering work. A throwing starter leaves nothing registered; after it returns, registration cannot fail. Settlement records the outcome, notifies listeners, and releases waiters.
- `spec` — task identity, owner, and synchronous starter.
**Returns** the registry-issued `<kind>-N` id.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/tasks/tasks/src/index.ts#L101)
### ctx.tasks.list(caller?)
```ts website-api
/**
* List caller-owned and unowned tasks in registration order without exposing
* another session's labels.
* @param caller - reading agent; a non-agent caller sees only unowned tasks.
* @returns fresh snapshots.
*/
list(caller?: Agent): TaskSnapshot[]
```
List caller-owned and unowned tasks in registration order without exposing another session's labels.
- `caller` — reading agent; a non-agent caller sees only unowned tasks.
**Returns** fresh snapshots.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/tasks/tasks/src/index.ts#L153)
### ctx.tasks.get(id, caller?)
```ts website-api
/**
* Return a non-consuming snapshot without changing its read cursor or notice
* state. Throws for an unknown or foreign task.
* @param id - task to look up.
* @param caller - reading agent checked against the owner.
* @returns a fresh snapshot.
*/
get(id: TaskId, caller?: Agent): TaskSnapshot
```
Return a non-consuming snapshot without changing its read cursor or notice state. Throws for an unknown or foreign task.
- `id` — task to look up.
- `caller` — reading agent checked against the owner.
**Returns** a fresh snapshot.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/tasks/tasks/src/index.ts#L167)
### ctx.tasks.read(id, caller?)
```ts website-api
/**
* Read the next stream delta, or the idempotent final output after settlement.
* A terminal read marks the task reported. Throws for an unknown or foreign
* task.
* @param id - task to read.
* @param caller - reading agent checked against the owner.
* @returns output text and the post-read snapshot.
*/
read(id: TaskId, caller?: Agent): TaskRead
```
Read the next stream delta, or the idempotent final output after settlement. A terminal read marks the task reported. Throws for an unknown or foreign task.
- `id` — task to read.
- `caller` — reading agent checked against the owner.
**Returns** output text and the post-read snapshot.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/tasks/tasks/src/index.ts#L181)
### ctx.tasks.kill(id, caller?, reason?)
```ts website-api
/**
* Request cancellation, then mark the task stopping and reported. A producer
* throw propagates without changing task state. Throws for an unknown or
* foreign task.
* @param id - task to cancel.
* @param caller - killing agent checked against the owner.
* @param reason - logged reason forwarded to the producer.
* @returns `requested` for live work, otherwise `already-finished`.
*/
kill(id: TaskId, caller?: Agent, reason?: string): 'requested' | 'already-finished'
```
Request cancellation, then mark the task stopping and reported. A producer throw propagates without changing task state. Throws for an unknown or foreign task.
- `id` — task to cancel.
- `caller` — killing agent checked against the owner.
- `reason` — logged reason forwarded to the producer.
**Returns** `requested` for live work, otherwise `already-finished`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/tasks/tasks/src/index.ts#L200)
### ctx.tasks.wait(id, timeoutMs, caller?, signal?)
```ts website-api
/**
* Wait for settlement or timeout without cancelling the task. Caller abort
* rejects only while the task is live; after settlement it returns the
* terminal snapshot so a notice suppressed for this waiter is still delivered.
* Timed-out and aborted waits detach their resolvers. Throws for invalid,
* unknown, or foreign input.
* @param id - task to wait for.
* @param timeoutMs - positive finite wait bound in milliseconds.
* @param caller - waiting agent checked against the owner.
* @param signal - optional cancellation of the wait itself.
* @returns snapshot at settlement or timeout.
*/
async wait(id: TaskId, timeoutMs: number, caller?: Agent, signal?: AbortSignal): Promise<TaskSnapshot>
```
Wait for settlement or timeout without cancelling the task. Caller abort rejects only while the task is live; after settlement it returns the terminal snapshot so a notice suppressed for this waiter is still delivered. Timed-out and aborted waits detach their resolvers. Throws for invalid, unknown, or foreign input.
- `id` — task to wait for.
- `timeoutMs` — positive finite wait bound in milliseconds.
- `caller` — waiting agent checked against the owner.
- `signal` — optional cancellation of the wait itself.
**Returns** snapshot at settlement or timeout.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/tasks/tasks/src/index.ts#L226)
### ctx.tasks.onTaskDone(listener)
```ts website-api
/**
* Register an effect-scoped completion listener. Each listener is contained;
* returned promises are observed but not awaited. No listener runs after
* service disposal.
* @param listener - receives each terminal snapshot and its exact owner.
* @returns disposer that unregisters the listener.
*/
onTaskDone(listener: TaskDoneListener): () => void
```
Register an effect-scoped completion listener. Each listener is contained; returned promises are observed but not awaited. No listener runs after service disposal.
- `listener` — receives each terminal snapshot and its exact owner.
**Returns** disposer that unregisters the listener.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/tasks/tasks/src/index.ts#L283)
### ctx.tasks.attachSurface(name)
```ts website-api
/**
* Attach an effect-scoped surface that can read and stop tasks. {@link start}
* refuses work while none is attached.
* @param name - diagnostic label; duplicate names remain independent.
* @returns disposer that detaches this surface.
*/
attachSurface(name: string): () => void
```
Attach an effect-scoped surface that can read and stop tasks. start refuses work while none is attached.
- `name` — diagnostic label; duplicate names remain independent.
**Returns** disposer that detaches this surface.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/tasks/tasks/src/index.ts#L297)

View File

@@ -1,72 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.tokenMeter
`TokenMeterService` — provided by `@deepseek-ai/dsh-token-meter`.
Replay owner for one service-wide estimator and isolated per-session folds.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/llm/token-meter/src/index.ts#L106)
### ctx.tokenMeter.contextWindow
```ts website-api
/** Provider context-window capacity used by pressure consumers. */
readonly contextWindow: number
```
Provider context-window capacity used by pressure consumers.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/llm/token-meter/src/index.ts#L112)
### ctx.tokenMeter.measure(session, requestHeader?)
```ts website-api
/**
* Measure current request pressure and surface through the durable tail.
*
* Provider usage is reused only when the latest successful call's canonical
* request envelope matches `requestHeader` and its total is no lower than
* that call's full heuristic anchor; otherwise the complete envelope and
* surface are heuristically repriced.
*
* `requestHeader` affects request pressure only; surface fields always
* describe the current session surface. Every call clones those positional
* nodes, so measurement is O(surface).
*
* @param session - session to replay through its current durable tail.
* @param requestHeader - optional effective request envelope replacing the latest logged header.
* @returns a detached deeply immutable pressure and surface measurement.
*/
measure(session: Session, requestHeader?: EpochHeader): TokenMeasurement
```
Measure current request pressure and surface through the durable tail.
Provider usage is reused only when the latest successful call's canonical request envelope matches `requestHeader` and its total is no lower than that call's full heuristic anchor; otherwise the complete envelope and surface are heuristically repriced.
`requestHeader` affects request pressure only; surface fields always describe the current session surface. Every call clones those positional nodes, so measurement is O(surface).
- `session` — session to replay through its current durable tail.
- `requestHeader` — optional effective request envelope replacing the latest logged header.
**Returns** a detached deeply immutable pressure and surface measurement.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/llm/token-meter/src/index.ts#L143)
### ctx.tokenMeter.estimateMessage(message)
```ts website-api
/**
* Heuristically price one model-visible message.
* @param message - message to price without mutation.
* @returns content and role-framing tokens under the fixed service heuristic.
*/
estimateMessage(message: Message): number
```
Heuristically price one model-visible message.
- `message` — message to price without mutation.
**Returns** content and role-framing tokens under the fixed service heuristic.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/llm/token-meter/src/index.ts#L181)

View File

@@ -1,162 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.tools
`ToolRegistry` — provided by `@deepseek-ai/dsh-tools`.
Tool registry and execution pipeline. Scoped registrations shadow globals; one visibility resolver feeds presentation, lookup, and dispatch.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/tools/src/index.ts#L438)
### ctx.tools.register(definition)
```ts website-api
/**
* Register globally or in the calling agent scope. Scoped tools shadow
* globals; duplicates within one layer and the reserved `run_code` name fail.
* @param definition - the tool schema, execution, and optional presentation functions.
* @returns the exact disposer that unregisters the tool.
*/
register(definition: ToolDefinition): () => void
```
Register globally or in the calling agent scope. Scoped tools shadow globals; duplicates within one layer and the reserved `run_code` name fail.
- `definition` — the tool schema, execution, and optional presentation functions.
**Returns** the exact disposer that unregisters the tool.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/tools/src/index.ts#L538)
### ctx.tools.restrict(filter)
```ts website-api
/**
* Restrict global tools for the calling agent scope. Empty filters, unknown
* names, scope-local names, and reserved transport names fail. Restrictions
* intersect; scoped registrations remain visible.
* @param filter - global-surface mask: `allow` (keep only) and/or `deny` (remove).
* @returns the exact disposer that lifts this restriction.
*/
restrict(filter: ToolRestriction): () => void
```
Restrict global tools for the calling agent scope. Empty filters, unknown names, scope-local names, and reserved transport names fail. Restrictions intersect; scoped registrations remain visible.
- `filter` — global-surface mask: `allow` (keep only) and/or `deny` (remove).
**Returns** the exact disposer that lifts this restriction.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/tools/src/index.ts#L578)
### ctx.tools.guard(guard)
```ts website-api
/**
* Register a monotonic guard after the extensible `tools/pre-execute`
* waterfall. A plain-context guard applies globally; one registered through
* `agent.ctx` applies only to that agent. Any matching guard may deny by
* returning a reason, while no guard can force-allow a call another guard
* denied. The exact effect disposer is returned for ordered ownership and
* HMR cleanup.
* @param guard - synchronous check; a returned string denies the execution.
* @returns the exact disposer that unregisters the guard.
*/
guard(guard: ToolGuard): () => void
```
Register a monotonic guard after the extensible `tools/pre-execute` waterfall. A plain-context guard applies globally; one registered through `agent.ctx` applies only to that agent. Any matching guard may deny by returning a reason, while no guard can force-allow a call another guard denied. The exact effect disposer is returned for ordered ownership and HMR cleanup.
- `guard` — synchronous check; a returned string denies the execution.
**Returns** the exact disposer that unregisters the guard.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/tools/src/index.ts#L629)
### ctx.tools.get(name, scope?)
```ts website-api
/**
* Look up a tool as one scope sees it (scoped
* shadows global; a restricted-away global reads as absent). Presenters pass
* the calling agent so the rendered card matches the definition that
* actually executed.
* @param name - the tool name as registered.
* @param scope - the viewing scope (the agent); omitted = the global view.
* @returns the definition the scope resolves, or undefined when none is visible.
*/
get(name: string, scope?: ScopeKey): ToolDefinition | undefined
```
Look up a tool as one scope sees it (scoped shadows global; a restricted-away global reads as absent). Presenters pass the calling agent so the rendered card matches the definition that actually executed.
- `name` — the tool name as registered.
- `scope` — the viewing scope (the agent); omitted = the global view.
**Returns** the definition the scope resolves, or undefined when none is visible.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/tools/src/index.ts#L731)
### ctx.tools.schemas(scope?)
```ts website-api
/**
* Project visible definitions onto the allowlisted model-facing schema fields,
* excluding execution and presentation callbacks.
* @param scope - the viewing scope (the agent); omitted = the global view.
* @returns one deep-cloned schema per visible tool.
*/
schemas(scope?: ScopeKey): ToolSchema[]
```
Project visible definitions onto the allowlisted model-facing schema fields, excluding execution and presentation callbacks.
- `scope` — the viewing scope (the agent); omitted = the global view.
**Returns** one deep-cloned schema per visible tool.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/tools/src/index.ts#L741)
### ctx.tools.executionMode(exec)
```ts website-api
/**
* Classify a pending call through the caller's visible tool definition. Only
* an exact `true` is parallel; unknown, hidden, undeclared, invalid, or
* throwing classifiers are exclusive.
* @param exec - call name, parsed arguments, and optional agent scope.
* @returns the fail-closed scheduling mode.
*/
executionMode(exec: ToolExecutionInput): ToolExecutionMode
```
Classify a pending call through the caller's visible tool definition. Only an exact `true` is parallel; unknown, hidden, undeclared, invalid, or throwing classifiers are exclusive.
- `exec` — call name, parsed arguments, and optional agent scope.
**Returns** the fail-closed scheduling mode.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/tools/src/index.ts#L762)
### ctx.tools.execute(exec)
```ts website-api
/**
* Execute through pre-policy, guards, around-dispatch, post-policy, and final
* notification. Tool and listener failures resolve as materialized error
* results; an invisible tool reports `UNKNOWN_TOOL`. The returned outcome is
* the same lossless, frozen snapshot final observers receive.
* @param exec - the typed same-process call input. The registry assigns its
* correlation token before policy begins.
* @returns the materialized final result.
*/
async execute(exec: ToolExecutionInput): Promise<ToolExecutionResult>
```
Execute through pre-policy, guards, around-dispatch, post-policy, and final notification. Tool and listener failures resolve as materialized error results; an invisible tool reports `UNKNOWN_TOOL`. The returned outcome is the same lossless, frozen snapshot final observers receive.
- `exec` — the typed same-process call input. The registry assigns its correlation token before policy begins.
**Returns** the materialized final result.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/tools/src/index.ts#L782)

View File

@@ -1,49 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.userInteraction
`UserInteractionService` — provided by `@deepseek-ai/dsh-user-interaction`.
`ctx.userInteraction`: one active UI provider plus an `ask()` surface.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/user-interaction/src/index.ts#L82)
### ctx.userInteraction.registerProvider(provider)
```ts website-api
/**
* Register the UI provider. Only one provider may be active in a context.
*
* @param provider UI-side implementation that collects answers.
* @returns Disposer that unregisters this provider.
*/
registerProvider(provider: UserInteractionProvider): () => void
```
Register the UI provider. Only one provider may be active in a context.
- `provider` — UI-side implementation that collects answers.
**Returns** Disposer that unregisters this provider.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/user-interaction/src/index.ts#L95)
### ctx.userInteraction.ask(request)
```ts website-api
/**
* Ask the active UI provider and wait for the user's answer.
*
* @param request Questions, owner agent, and abort signal.
* @returns The answer chosen or typed by the human.
*/
async ask(request: AskUserQuestionRequest): Promise<AskUserQuestionAnswer>
```
Ask the active UI provider and wait for the user's answer.
- `request` — Questions, owner agent, and abort signal.
**Returns** The answer chosen or typed by the human.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/ui/user-interaction/src/index.ts#L114)

View File

@@ -1,105 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.web
`WebService` — provided by `@deepseek-ai/dsh-web`.
The web access service. Registered as `ctx.web` (one instance per context).
Selection semantics (resolved at execution time, never order-dependent):
- A configured id that is registered and `available()` → that provider.
- A configured id not registered → `WEB_PROVIDER_CONFIGURED_MISSING`.
- A configured id registered but unavailable → `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`.
- No id configured, exactly one registered usable provider → that provider.
- No id configured, multiple usable providers → `WEB_PROVIDER_AMBIGUOUS`.
- No id configured, no usable provider → `WEB_PROVIDER_UNAVAILABLE`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/web/web/src/index.ts#L74)
### ctx.web.registerSearchProvider(provider)
```ts website-api
/**
* Register a search provider. Throws {@link WebError} `WEB_DUPLICATE_PROVIDER`
* if its id is already registered for search. Returns a disposer; disposed
* with the calling fiber.
* @param provider - the provider; its `id` is the registry key.
* @returns the disposer that unregisters the provider.
*/
registerSearchProvider(provider: WebSearchProvider): () => void
```
Register a search provider. Throws WebError `WEB_DUPLICATE_PROVIDER` if its id is already registered for search. Returns a disposer; disposed with the calling fiber.
- `provider` — the provider; its `id` is the registry key.
**Returns** the disposer that unregisters the provider.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/web/web/src/index.ts#L103)
### ctx.web.registerFetchProvider(provider)
```ts website-api
/**
* Register a fetch provider. Throws {@link WebError} `WEB_DUPLICATE_PROVIDER`
* if its id is already registered for fetch. Returns a disposer; disposed
* with the calling fiber.
* @param provider - the provider; its `id` is the registry key.
* @returns the disposer that unregisters the provider.
*/
registerFetchProvider(provider: WebFetchProvider): () => void
```
Register a fetch provider. Throws WebError `WEB_DUPLICATE_PROVIDER` if its id is already registered for fetch. Returns a disposer; disposed with the calling fiber.
- `provider` — the provider; its `id` is the registry key.
**Returns** the disposer that unregisters the provider.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/web/web/src/index.ts#L114)
### ctx.web.search(request, signal?)
```ts website-api
/**
* Run one search through the selected provider. Resolves the provider at call
* time with the selection rules above; throws {@link WebError} when the
* capability cannot run. The seam enforces `request.maxResults` on the result:
* if the provider over-returns, `sources[]` is truncated and `truncated` set.
* @param request - the query plus result-shaping options.
* @param signal - optional cancellation signal forwarded to the provider.
* @returns the provider's results, capped to `request.maxResults`.
*/
async search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>
```
Run one search through the selected provider. Resolves the provider at call time with the selection rules above; throws WebError when the capability cannot run. The seam enforces `request.maxResults` on the result: if the provider over-returns, `sources[]` is truncated and `truncated` set.
- `request` — the query plus result-shaping options.
- `signal` — optional cancellation signal forwarded to the provider.
**Returns** the provider's results, capped to `request.maxResults`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/web/web/src/index.ts#L140)
### ctx.web.fetch(request, signal?)
```ts website-api
/**
* Retrieve one URL through the selected provider. Resolves the provider at
* call time with the selection rules above; throws {@link WebError} when the
* capability cannot run. A non-2xx response is a result, not a throw.
* @param request - the URL plus retrieval options.
* @param signal - optional cancellation signal forwarded to the provider.
* @returns the retrieval outcome; non-2xx responses resolve descriptively.
*/
async fetch(request: WebFetchRequest, signal?: AbortSignal): Promise<WebFetchResult>
```
Retrieve one URL through the selected provider. Resolves the provider at call time with the selection rules above; throws WebError when the capability cannot run. A non-2xx response is a result, not a throw.
- `request` — the URL plus retrieval options.
- `signal` — optional cancellation signal forwarded to the provider.
**Returns** the retrieval outcome; non-2xx responses resolve descriptively.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/web/web/src/index.ts#L157)

View File

@@ -1,29 +0,0 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.workflows
`WorkflowService` (abstract seam) — provided by `@deepseek-ai/dsh-workflow`.
Workflow execution seam. Invalid requests throw before publication; a live run is holder-owned, its result never rejects, cancellation and disposal are bounded, and disposal waits for child cleanup within that bound. Lifecycle listener failures are contained, and `workflow/end` fires exactly once as the result settles.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/workflow/workflow/src/index.ts#L159)
### ctx.workflows.start(request)
```ts website-api
/**
* Parse and execute a workflow script.
* @param request - the script, its `args`, the parent agent, and an
* optional cancel signal.
* @returns the live run; its `result` resolves when the script settles.
*/
abstract start(request: WorkflowStartRequest): WorkflowRun
```
Parse and execute a workflow script.
- `request` — the script, its `args`, the parent agent, and an optional cancel signal.
**Returns** the live run; its `result` resolves when the script settles.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/workflow/workflow/src/index.ts#L170)

View File

@@ -1,43 +0,0 @@
# API 参考
本节是 DeepSeek Harness 的 API 参考。除本页外,`cordis/``harness/` 下的所有页面**由脚本从源码生成**`pnpm run gen-website-api`CI 校验新鲜度);签名代码块保留源码的原始 JSDoc签名与说明永远与代码一致。生成页目前为英文中文版将随统一翻译流程提供。
## 框架 API
Cordis 微内核提供的基础能力,所有插件开发都建立在这些 API 之上:
- [Context](cordis/context) — 上下文对象,所有服务和方法的入口
- [Events](cordis/events) — 事件系统 APIon / emit / bail / serial / waterfall
- [Fiber](cordis/fiber) — 插件生命周期状态机、effect、dispose
- [Registry](cordis/registry) — 插件注册plugin / inject
- [Service](cordis/service) — 服务基类
## Harness API
每个 `ctx.*` 服务一页,按服务名索引:
- [ctx.agentLoop](harness/agent-loop) — ReAct 循环的创建与恢复
- [ctx.agents](harness/agents) — Agent 注册表与工厂
- [ctx.approval](harness/approval) — 用户审批
- [ctx.bash](harness/bash) — Bash 执行接口(抽象缝)
- [ctx.codeRuntime](harness/code-runtime) — 代码执行接口(抽象缝)
- [ctx.compact](harness/compact) — 上下文压缩接口(抽象缝)
- [ctx.fs](harness/fs) — 文件系统接口(抽象缝)
- [ctx.llm](harness/llm) — LLM 服务与适配器注册
- [ctx.permission](harness/permission) — 权限策略
- [ctx.sandbox](harness/sandbox) — 沙箱执行接口(抽象缝)
- [ctx.sessionPersistence](harness/session-persistence) — 会话持久化接口(抽象缝)
- [ctx.sessionQuery](harness/session-query) — 会话检索
- [ctx.sessions](harness/sessions) — 会话存储
- [ctx.skills](harness/skills) — 技能加载
- [ctx.subagents](harness/subagents) — 子代理委派
- [ctx.systemPrompt](harness/system-prompt) — 系统提示词组装
- [ctx.tasks](harness/tasks) — 后台任务
- [ctx.tools](harness/tools) — Tool 注册表
- [ctx.userInteraction](harness/user-interaction) — 用户交互接口
- [ctx.web](harness/web) — Web 搜索与抓取
- [ctx.workflows](harness/workflows) — 动态工作流引擎(抽象缝)
事件总表:[Harness events](harness/events) — 全部事件按作用域分组,含触发模式与载荷签名。
想学"怎么写一个 tool / 插件"?教程在[开发指南](../develop/basic/);本节只做精确的接口参考。

View File

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

View File

@@ -1,152 +0,0 @@
# 上下文模型
上下文 (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)
余作用由作用产生:
- **提供服务**本身是一种作用——它占用了服务命名空间资源
- 因此服务的提供被记录在作用上下文中
- 上下文将作用与余作用关联起来,提供了统一的时间、空间可组合性
```ts
import { Service, type Context } from 'cordis'
// 提供服务 = 一个 effect占用 ctx.llm 这个 "资源"
class LlmService extends Service {
constructor(ctx: Context) {
super(ctx, 'llm')
}
// 当此插件卸载时ctx.llm 被回收effect 的逆操作)
// 所有依赖 llm 的插件因 coeffect 不满足而挂起
}
```
## 基于上下文的开发范式
上下文模型提供了两个关键优势:
### 无感性 (Transparent)
框架将领域中的所有方法都封装为 effect 版本。开发者只需调用 `ctx` 上的方法,就能自动获得时间/空间可组合性:
```ts
import type { Context } from 'cordis'
import type { ToolDefinition } from '@deepseek-ai/dsh-tools'
import type { LlmAdapter, Message } from '@deepseek-ai/dsh-llm'
import type { Agent } from '@deepseek-ai/dsh-agent'
declare function validateResult(agent: Agent, turn: number, step: number, message: Message, next: () => Promise<Message>): Promise<Message>
declare const myTool: ToolDefinition
declare const adapter: LlmAdapter
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 替换为可组合版本,无需一次性重写:
```ts
import type { Context } from 'cordis'
declare const ctx: Context
declare function handler(): void
declare const legacySystem: {
register(handler: () => void): object
unregister(token: object): void
}
// 第一步:用 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 能在保持「一切皆插件」的同时,不给插件开发者增加心智负担——**上下文模型把复杂性封装在了框架内部**。

View File

@@ -1,69 +0,0 @@
# 作用与余作用
## 作用 (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 能被安全热替换的根本原因。

View File

@@ -1,39 +0,0 @@
# 系统设计
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 模式

View File

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

View File

@@ -1,141 +0,0 @@
# 可逆作用
可逆作用 (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 函数 | 逆操作 |
```ts
import type { Context } from 'cordis'
import type { ToolDefinition } from '@deepseek-ai/dsh-tools'
declare module 'cordis' {
interface Events {
'my-plugin/event'(): void
}
}
declare function startServer(port: number): { close(): void }
declare function handler(): void
declare const myTool: ToolDefinition
export function apply(ctx: Context) {
// effect: 创建资源,返回其逆操作
ctx.effect(() => {
const server = startServer(8080) // f: 占用端口
return () => server.close() // f⁻¹: 释放端口
})
// 框架 API 内部已封装 effect
ctx.on('my-plugin/event', handler) // 内部: effect(addListener, removeListener)
ctx.tools.register(myTool) // 内部: effect(addTool, removeTool)
}
// 当此插件被卸载时restore 自动按逆序执行所有 f⁻¹
```
## 为什么 Agent 需要可逆作用
在 Harness 场景下,可逆作用直接支撑:
- **热替换 LLM 适配器**:卸载旧适配器(回收注册)、加载新适配器,无需重启
- **动态 tool 管理**:根据对话上下文动态添加/移除 tool不泄漏
- **子 Agent 生命周期**:子 Agent 完成后,其注册的所有临时 tool 和监听器自动清理
- **优雅关闭**:进程退出时所有插件按依赖逆序 dispose确保资源完全释放

View File

@@ -1,113 +0,0 @@
# 插件配置
让你的插件接受用户在 `cordis.yml` 中传入的配置。
## 定义 Config 类型
在插件中导出一个 `Config` 类型,`apply` 的第二个参数就是用户配置:
```ts
import type { Context } from 'cordis'
export const name = 'my-plugin'
export interface Config {
greeting?: string
maxRetries?: number
verbose?: boolean
}
export function apply(ctx: Context, config: Config) {
console.log(config.greeting ?? 'Hello') // 用户配置或默认值
}
```
用户在 `cordis.yml` 中这样使用:
```yaml
- name: './src/my-plugin.ts'
config:
greeting: 'Hi there'
maxRetries: 5
```
只导出类型时,配置原样传入,默认值由代码自己兜底(如上面的 `??`)。想让框架代管默认值和校验,导出一个 schema见下节
## Schema 校验
对于需要默认值和严格校验的场景,额外导出一个 Schemastery schema仓库约定以 `z` 引入)。加载时框架先用它校验并填充默认值,再把结果传给 `apply`
```ts
import type { Context } from 'cordis'
import z from 'schemastery'
export const name = 'validated-plugin'
export interface Config {
apiKey: string
timeout?: number
mode?: 'fast' | 'accurate'
}
export const Config: z<Config> = z.object({
apiKey: z.string().required(),
timeout: z.number().default(30000),
mode: z.union(['fast', 'accurate'] as const).default('fast'),
})
export function apply(ctx: Context, config: Config) {
// config 已经过校验,类型安全,默认值已填充
}
```
Schema 在插件加载时执行校验。如果配置不合法,插件会加载失败并给出明确错误信息。
## 设计原则
### 无硬编码可调参数
Harness 的约定:**任何两个部署可能想要不同值的东西,都应该是配置字段**。
```ts
// 错误 — 硬编码超时时间
const TIMEOUT = 30000
// 正确 — 可配置
export interface Config {
/** 默认 30000 */
timeoutMs?: number
}
```
检验标准:能否在 `cordis.yml` 中改变这个值,而不需要修改代码?
### 配置错误要响亮
如果配置引用了不存在的东西(比如一个未注册的 LLM 提供方路由),应该尽早报错,而不是静默跳过:
```ts
import type { Context } from 'cordis'
import type {} from '@deepseek-ai/dsh-llm'
export interface Config {
provider: string
model: string
}
export function apply(ctx: Context, config: Config) {
if (!ctx.llm.listProviders().some(provider => provider.id === config.provider)) {
throw new Error(`LLM provider "${config.provider}" is not registered`)
}
}
```
模型目录只用于发现;适配器可能接受目录之外的模型 ID因此不能把 `listModels()` 当作请求白名单。
## 配合 HMR
配置变更会触发插件热替换:修改 `cordis.yml` 中某个插件的 `config`,框架会卸载旧实例、加载新实例。由于注册都是效果(自动清理),这个过程是安全的。
## 下一步
- [插件与生命周期](../framework/) — 深入了解插件的完整生命周期
- [服务与依赖](../framework/service) — 让你的插件对外提供服务

View File

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

View File

@@ -1,242 +0,0 @@
# 开发一个 Tool
Tool 是模型可以调用的能力。本文介绍如何用 `defineTool` 编写一个 tool。
## 最小示例
```ts
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。
### 基本类型
```ts
import type { SchemaSpec } from '@deepseek-ai/dsh-tools'
const parameters = {
path: { type: 'string', required: true },
limit: { type: 'number' },
recursive: { type: 'boolean' },
} satisfies SchemaSpec
// 推导类型: { path: string; limit?: number; recursive?: boolean }
```
### 枚举
```ts
import type { SchemaSpec } from '@deepseek-ai/dsh-tools'
const parameters = {
mode: { type: 'string', required: true, enum: ['read', 'write', 'append'] },
} satisfies SchemaSpec
// 推导类型: { mode: string } (运行时校验 enum 值)
```
### 嵌套对象
```ts
import type { SchemaSpec } from '@deepseek-ai/dsh-tools'
const parameters = {
options: {
type: 'object',
properties: {
timeout: { type: 'number' },
retries: { type: 'number' },
},
},
} satisfies SchemaSpec
// 推导类型: { options?: { timeout?: number; retries?: number } }
```
### 数组
```ts
import type { SchemaSpec } from '@deepseek-ai/dsh-tools'
const parameters = {
tags: {
type: 'array',
items: { type: 'string' },
},
} satisfies SchemaSpec
// 推导类型: { tags?: string[] }
```
### 每个属性的字段
| 字段 | 类型 | 说明 |
|------|------|------|
| `type` | `'string' \| 'number' \| 'boolean' \| 'object' \| 'array'` | 值类型 |
| `required` | `true` | 标记为必填(影响类型推导) |
| `description` | `string` | 发送给模型的描述 |
| `enum` | `string[]` | 允许的枚举值 |
| `properties` | `SchemaSpec` | 嵌套属性type 为 object 时) |
| `items` | `SchemaProp` | 数组元素 schematype 为 array 时) |
## execute 函数
`execute` 接收经过校验的 `args`(类型自动推导)和一个 `exec` 上下文对象:
```ts
import { defineTool } from '@deepseek-ai/dsh-tools'
defineTool({
name: 'demo',
description: 'Demo tool.',
parameters: {},
async execute(args, exec) {
// args: 根据 parameters 自动推导的类型
// exec: ToolExecution 对象,提供执行上下文
// 返回 ContentBlock 数组
return [{ type: 'text', text: 'result here' }]
},
})
```
### 返回值
`execute` 必须返回一个 `ContentBlock[]`,告诉模型 tool 的执行结果:
```ts
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
declare const matchResults: string[]
// 文本结果
function textResult(): ContentBlock[] {
return [{ type: 'text', text: 'file content here...' }]
}
// 多个 block
function multiBlockResult(): ContentBlock[] {
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
```ts
import { defineTool } from '@deepseek-ai/dsh-tools'
defineTool({
name: 'bash',
description: 'Run a shell command.',
parameters: {
command: { type: 'string', required: true },
},
async execute(args) {
return [{ type: 'text', text: `ran: ${args.command}` }]
},
presentCall(args) {
return {
card: 'terminal',
title: args.command.slice(0, 60),
}
},
presentResult(args, result) {
return {
card: 'terminal',
output: result.content.map(b => b.type === 'text' ? b.text : '').join(''),
}
},
})
```
`presentCall``presentResult` 是**纯函数**不能有副作用——UI 可能在流式传输中和会话回放中多次调用它们。
## 注册与卸载
`ctx.tools.register()` 返回值就是 disposer。但由于你在 `ctx` 上调用,框架已经自动追踪了这个注册——插件卸载时会自动移除 tool。你不需要手动调用 disposer。
```ts
import type { Context } from 'cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
declare const ctx: Context
// 这样就够了:
ctx.tools.register(defineTool({
name: 'noop',
description: 'Do nothing.',
parameters: {},
async execute() {
return []
},
}))
// 不需要:
// const dispose = ctx.tools.register(...)
// ctx.effect(() => dispose)
```
## 完整实战示例
一个文件计数 tool
```ts
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 模式

View File

@@ -1,228 +0,0 @@
# 事件系统
事件是 Cordis 插件间通信的核心机制。Harness 大量使用事件来实现松耦合的扩展点。
## 基本用法
### 监听事件
```ts
import type { Context } from 'cordis'
declare module 'cordis' {
interface Events {
'event-name'(payload: string): void
}
}
declare const ctx: Context
ctx.on('event-name', (payload) => {
// 处理事件
})
```
### 触发事件
```ts
import type { Context } from 'cordis'
declare module 'cordis' {
interface Events {
'event-name'(payload: string): void
}
}
declare const ctx: Context
declare const payload: string
ctx.emit('event-name', payload)
```
## 事件模式
Cordis 提供多种事件触发模式,适用于不同场景:
### emit — 广播
同步依次调用所有监听器,不等待、不关心返回值(监听器如果是 async其 Promise 被忽略):
```ts
import type { Context } from 'cordis'
declare module 'cordis' {
interface Events {
'my-plugin/turn-end'(agentId: string, turnIndex: number): void
}
}
declare const ctx: Context
declare const agentId: string
declare const turnIndex: number
// 触发
ctx.emit('my-plugin/turn-end', agentId, turnIndex)
// 监听
ctx.on('my-plugin/turn-end', (agentId, turnIndex) => {
console.log(`Turn ${turnIndex} ended`)
})
```
### bail — 短路
同步依次调用监听器,第一个返回**非 `undefined`/`null`/`false`** 值的监听器终止链并作为最终值(返回 `undefined`/`null`/`false` 则继续下一个):
```ts
import type { Context } from 'cordis'
declare module 'cordis' {
interface Events {
'some-check'(input: string): string | undefined
}
}
declare const ctx: Context
declare const input: string
declare function shouldBlock(input: string): boolean
// 触发
const result = ctx.bail('some-check', input)
// 监听(返回值阻止后续监听器)
ctx.on('some-check', (input) => {
if (shouldBlock(input)) return 'blocked'
// 返回 undefined 继续传递给下一个监听器
return undefined
})
```
### serial — 顺序执行
按注册顺序逐个 `await` 监听器,遇到第一个 bail 值(非 `undefined`/`null`/`false`)即停止并返回它;全部返回空值则执行到底。相当于 `bail` 的异步版:
```ts
import type { Context } from 'cordis'
declare module 'cordis' {
interface Events {
'setup-phase'(context: object): Promise<void> | void
}
}
declare const ctx: Context
declare const context: object
await ctx.serial('setup-phase', context)
```
### waterfall — 管道
监听器围绕默认实现层层包裹,形成数据管道。**必须调用 `next()` 委托给下游**,不调用即为否决:
```ts
import type { Context } from 'cordis'
import type { Message } from '@deepseek-ai/dsh-llm'
declare module 'cordis' {
interface Events {
'my-plugin/messages'(messages: Message[], next: () => Promise<Message[]>): Promise<Message[]>
}
}
declare const ctx: Context
declare const messages: Message[]
declare const extraMessage: Message
// 触发:最后一个参数是默认实现(所有监听器都调用 next 时的最终值)
const finalMessages = await ctx.waterfall('my-plugin/messages', messages, async () => messages)
// 监听(必须调用 next
ctx.on('my-plugin/messages', async (messages, next) => {
// next() 委托给下游监听器(最终到达默认实现),返回值可以被加工
const result = await next()
return [...result, extraMessage]
})
```
::: warning
Waterfall 监听器**必须调用 `next()`**。不调用 `next` 等于否决整个管道,这是故意为之的设计——用于实现拦截/网关逻辑。
:::
## Typed Events
Harness 使用 TypeScript 声明合并来为事件提供类型安全:
```ts
import type {} from 'cordis'
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 — 每个 step 开始前的检查点serial
agent/step-result — step 的 assistant 消息组装完成waterfall
tools/pre-execute — tool 执行前的允许/拒绝门waterfall
tools/post-execute — tool 执行后的检查/改写缝waterfall
llm/stream — 每次流式模型调用的环绕点waterfall
session/event — 会话事件被记录emit
session/flush — 会话持久化检查点parallel
```
完整的事件列表(含每个事件的签名与派发模式)见仓库中的 `docs/cordis-catalog/events.md`
## 事件也是效果
通过 `ctx.on()` 注册的监听器会在插件卸载时自动移除:
```ts
import type { Context } from 'cordis'
import type { Agent, AgentStatus } from '@deepseek-ai/dsh-agent'
declare function handler(agent: Agent, status: AgentStatus): void
export function apply(ctx: Context) {
// 这个监听器在插件 dispose 时自动清理
ctx.on('agent/status', handler)
}
```
## 实战示例:日志插件
一个记录所有 tool 调用的简单插件:
```ts
import type { Context } from 'cordis'
import type {} from '@deepseek-ai/dsh-tools'
export const name = 'tool-logger'
export function apply(ctx: Context) {
ctx.on('tools/execute', async (exec, next) => {
console.log(`[tool] ${exec.name}(${JSON.stringify(exec.arguments)})`)
const result = await next()
const text = result.content
.map(b => b.type === 'text' ? b.text : '')
.join('')
console.log(`[tool result] ${text.slice(0, 100)}`)
return result
})
}
```
## 下一步
- [能力三件套](../practice/) — 事件在 capability seam 中的角色
- [LLM 适配器](../practice/llm-adapter) — 实现一个完整的 LLM 后端

View File

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

View File

@@ -1,181 +0,0 @@
# 服务与依赖
服务 (Service) 是插件对外暴露能力的方式。依赖 (inject) 是插件声明自己需要哪些服务。
## 什么是服务
在 Harness 中,`tools``llm``agents` 都是服务。服务是挂载在 `ctx` 上的命名能力:
```ts
import type { Context } from 'cordis'
import type {} from '@deepseek-ai/dsh-tools'
import type {} from '@deepseek-ai/dsh-llm'
import type {} from '@deepseek-ai/dsh-agent'
declare const ctx: Context
ctx.tools // ToolRegistry 服务
ctx.llm // LLM 服务
ctx.agents // Agent 注册表服务
```
任何插件都可以提供一个新服务,供其他插件使用。
## 使用服务
声明 `inject` 来使用已有服务:
```ts
import type { Context } from 'cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const inject = ['tools']
export function apply(ctx: Context) {
// ctx.tools 在这里一定存在且就绪
ctx.tools.register(defineTool({
name: 'demo',
description: 'Demo tool.',
parameters: {},
async execute() {
return []
},
}))
}
```
框架保证:在 `apply` 执行时,`inject` 声明的服务已经全部就绪。如果服务还没准备好,你的插件会等着,不会执行。
## 提供服务
### 使用 Service 基类
```ts
import { Service, type Context } from 'cordis'
import type {} from '@deepseek-ai/dsh-llm'
export default class MetricsService extends Service {
static inject = ['llm'] // 本服务也可以依赖其他服务
constructor(ctx: Context) {
super(ctx, 'metrics') // 'metrics' 是服务名
}
// 服务的公开方法
record(event: string, value: number) {
// ...
}
}
```
加载这个插件后,其他插件就可以通过 `ctx.metrics` 访问它:
```ts
import type { Context } from 'cordis'
export const inject = ['metrics']
export function apply(ctx: Context) {
ctx.metrics.record('tool_call', 1)
}
```
### 类型声明
使用 TypeScript 声明合并让 `ctx.metrics` 有正确类型:
```ts
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 可选读取
`inject` 声明的依赖都是必选的:服务不存在时,插件不会加载。如果只想"有则用之",用 `ctx.get()` 读取——服务不存在时返回 `undefined`,插件照常加载:
```ts
import type { Context } from 'cordis'
// 必选:服务不存在时,插件不会加载
export const inject = ['tools']
export function apply(ctx: Context) {
// 可选读取:不声明 inject服务不存在时返回 undefined
const metrics = ctx.get('metrics')
metrics?.record('plugin_loaded', 1)
}
```
### 服务消失时的行为
如果一个必选依赖的服务在运行时消失(比如提供者被卸载):
1. 依赖它的插件自动 dispose
2. 当服务重新出现时,插件自动重新加载
这保证了不会出现"调用一个已不存在的服务"的情况。
## 服务隔离
`cordis.yml` 支持服务隔离——同一个服务可以有多个实例,不同插件组看到不同实例。用 `@cordisjs/plugin-group` 建组(`group: true` 标记组条目),并在组上声明 `isolate`,把该服务隔离进组内作用域:
```yaml
- id: group-a
name: '@cordisjs/plugin-group'
group: true
isolate:
bash: true
config:
- name: '@deepseek-ai/dsh-bash-local'
config:
timeoutMs: 5000
- name: './src/plugin-a.ts'
- id: group-b
name: '@cordisjs/plugin-group'
group: true
isolate:
bash: true
config:
- name: '@deepseek-ai/dsh-bash-local'
config:
timeoutMs: 60000
- name: './src/plugin-b.ts'
```
`plugin-a``plugin-b` 各自看到自己组内的 bash 实例,互不影响。`isolate: { bash: true }` 是必需的:不隔离的话,两个组在同一作用域注册同名服务,第二个会直接报重复注册错误。
## Harness 内置服务一览
| 服务名 | 提供者 | 用途 |
|--------|--------|------|
| `tools` | dsh-tools | Tool 注册表 |
| `llm` | dsh-llm | LLM 调用 + 适配器注册 |
| `agents` | dsh-agent | Agent 注册表 |
| `agentLoop` | dsh-agent-loop | Agent 创建与循环执行 |
| `sessions` | dsh-session | 会话存储与事件流 |
| `systemPrompt` | dsh-system-prompt | 系统提示词组装 |
| `bash` | dsh-bash实现dsh-bash-local | Bash 命令执行 |
| `fs` | dsh-fs实现dsh-fs-local | 文件系统操作 |
| `subagents` | dsh-subagent | 子代理委派 |
| `sessionPersistence` | dsh-session-persistence实现-jsonl / -sqlite | 会话持久化 |
## 下一步
- [事件系统](events) — 插件间松耦合通信
- [能力三件套](../practice/) — 服务在 seam 模式中的应用

View File

@@ -1,156 +0,0 @@
# 能力的三层拆分
当一个能力(插件)足够通用(比如"执行 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) |
## 开发你自己的三件套
### 第一步:定义接口
```ts
// 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
}
```
### 第二步:编写实现
```ts ignore-check
// 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)
```ts
// 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 ignore-check
- 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 扩展)

View File

@@ -1,182 +0,0 @@
# LLM 适配器
本文介绍如何为 Harness 接入一个新的 LLM 提供方。
## 概述
LLM 适配器是一个继承 `LlmAdapter` 的类,实现 `stream()` 方法,将 Harness 的统一请求格式转换为具体 API 的调用。
## 最小实现
```ts
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
```ts
import { CallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
async function* demo(): AsyncIterable<StreamChunk> {
// 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()` 接收的请求包含:
```ts
import type { GenerateOptions } from '@deepseek-ai/dsh-llm'
declare const options: GenerateOptions
options.model // 模型名
options.messages // 对话历史 (Message[])
options.tools // 可用的 tool schema 列表 (ToolSchema[])
options.system // 系统提示词
options.maxTokens // 最大输出 token
options.temperature // 温度
options.signal // 取消信号(必须响应)
```
你的适配器需要将这些映射到具体 API 的参数。
## 注册适配器
```ts
import type { Context } from 'cordis'
import type { LlmAdapter } from '@deepseek-ai/dsh-llm'
declare const ctx: Context
declare const adapter: LlmAdapter
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-demo'
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()` 内部做错误恢复——让异常冒泡即可。
```ts
import { LlmAdapter, type GenerateOptions, type StreamChunk } from '@deepseek-ai/dsh-llm'
class HttpAdapter extends LlmAdapter {
private endpoint = 'https://api.example.com/v1/chat'
async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
const response = await fetch(this.endpoint, { method: 'POST' })
if (!response.ok) {
throw new Error(`API error: ${response.status}`)
}
// ... 正常流式处理
}
}
```

View File

@@ -1,367 +0,0 @@
# 配置文件
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-demo'
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'
```
### repl-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-demo'
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 a coding agent powered by the {{model}} model.
Verify your work by running the code or tests. Keep answers brief and factual.
# Token 计量:统一定义模型能看到的 token 上限
- id: token-meter
name: '@deepseek-ai/dsh-token-meter'
config:
contextWindow: 128000
# 自动压缩:对话太长时自动总结旧内容,腾出上下文空间
# thresholdRatio 超过这个比例就触发压缩
# compactionRetries 是压缩后仍超标时的额外重试次数
- id: compact-basic
name: '@deepseek-ai/dsh-compact-basic'
config:
thresholdRatio: 0.8
retainTokens: 20480
maxTokens: 8192
compactionRetries: 1
# 子代理:把子任务分配给独立的 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
# 动态工作流:模型编写一段编排脚本,引擎在独立 worker 线程里运行它,
# 并通过上面的 spawn 后端把 agent() 调用分发为子代理
- id: workflow-workerthread
name: '@deepseek-ai/dsh-workflow-workerthread'
config:
provider: spawn
- id: tool-workflow
name: '@deepseek-ai/dsh-tool-workflow'
# 任务追踪:模型可以用 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-demo` 应用主体,只是把 mock 换成了真实 API加上了更多工具插件。
## 语法详解
### 插件声明字段
每个插件条目支持以下字段:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | 是 | 插件来源npm 包名或相对路径) |
| `id` | string | 否 | 实例标识符,用于日志和调试。省略时由 loader 生成并写回 |
| `config` | object | 否 | 传递给插件的配置 |
| `disabled` | boolean | 否 | 设为 `true` 临时禁用该插件 |
| `group` | boolean | 否 | 标记该条目为嵌套分组(`config` 为子条目列表) |
| `inject` | array \| object | 否 | 声明该插件依赖的服务 |
| `intercept` | object | 否 | 按服务名拦截并覆盖下游配置 |
| `isolate` | object | 否 | 服务隔离:服务名 → `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
```
## 各插件配置参考
### stdio-agent标准应用主体
**包名:** `@deepseek-ai/dsh-stdio-demo`
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `model` | string | **必填** | 使用的模型名,需与 LLM 适配器注册的名字一致 |
| `persona` | string | `''` | 系统提示词。支持 `{{model}}` 等模板变量 |
| `toolOrder` | string[] | — | 模型看到的工具顺序。省略则按字母排序 |
| `persistenceRoot` | string | `'./.sessions'` | 会话日志存储目录 |
| `welcome` | string | `'ready.'` | 启动时显示的欢迎信息 |
| `resumeSessionId` | string | — | 恢复指定会话 ID。留空则每次新建 |
### llm-deepseekDeepSeek 适配器)
**包名:** `@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-localBash 执行器)
**包名:** `@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-webWeb 工具)
**包名:** `@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[] | `['.']` | 监听文件变更的目录列表 |
| `base` | string | — | 解析 `root` 的基准目录(默认取配置文件所在目录) |
| `ignored` | string[] | `['**/node_modules', '**/.*', 'cache', 'data']` | 忽略的 glob 列表 |
| `debounce` | number | `100` | 变更合并窗口(毫秒) |
其余字段透传给 chokidar`Config` 继承 `ChokidarOptions`)。
::: tip
hmr 仅用于开发环境。它需要 `node --expose-internals` 启动参数,`demo:*` 脚本已自动添加。
:::
---
## 加载顺序
`cordis.yml` 的条目是**并发启动**的loader 对全部条目 `Promise.all`),文件顺序不决定加载顺序。真正的先后关系由依赖协调:插件声明的 `inject` 服务就绪之前,插件不会启动;服务出现后自动继续。所以**不要依赖书写顺序传递时序**——需要"先有 A 再有 B"就让 B `inject` A 提供的服务。
文件顺序只是给人读的。推荐按角色分组书写:
1. **hmr** — 热替换(仅开发时需要)
2. **LLM 适配器** — 模型后端
3. **执行器** — bash、fs 等能力提供者
4. **应用主体**`dsh-stdio-demo``dsh-acp-demo`
5. **附加插件** — compact、subagent、todo 等
应用主体内部已经捆绑了核心能力session、tools、agent-loop不需要手动加载。
## 下一步
- [开发插件](../develop/basic/) — 编写自己的插件
- [API 参考](../api/) — 查看各插件完整接口

View File

@@ -1,47 +0,0 @@
# 介绍
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-demo'
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

View File

@@ -1,98 +0,0 @@
# 快速开始
本指南带你在 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
```
### 启动 repl-agent
```sh
pnpm run demo:repl
```
```
agent REPL ready. Give it a coding task.
>
```
这就是一个完整的编程助手,它能读写文件、跑命令、拆分子任务。
试着给它一个任务:
```
> 在当前目录创建一个 hello.js内容是打印 "Hello from Harness!",然后运行它
```
## 回头看
echo-agent 和 repl-agent 用的是同一个应用框架(`@deepseek-ai/dsh-stdio-demo`),区别只在 `cordis.yml`——换了哪些插件、填了什么配置。你以后定制自己的 Agent 也是同样的方式。
## 下一步
- [配置文件](config) — 了解 `cordis.yml` 的完整语法
- [开发插件](../develop/basic/) — 编写你自己的 tool 或后端

View File

@@ -1,21 +0,0 @@
---
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 执行、子代理委派等完整工具链,复制模板即可运行。
---