fix(docs): address documentation site review

This commit is contained in:
Yichen Jiang
2026-07-13 17:49:30 +08:00
parent 341b56ebc3
commit 6be219a0bc
12 changed files with 333 additions and 212 deletions
+9 -8
View File
@@ -4,10 +4,11 @@
## 定义 Config 类型
在插件中导出一个 `Config` 类型和可选的默认值
在插件中导出一个 `Config` 类型和同名的 Schemastery schema;默认值直接写在 schema 中
```typescript
import type { Context } from 'cordis'
import Schema from 'schemastery'
export const name = 'my-plugin'
@@ -17,11 +18,11 @@ export interface Config {
verbose?: boolean
}
export const Config = {
greeting: 'Hello',
maxRetries: 3,
verbose: false,
}
export const Config: Schema<Config> = Schema.object({
greeting: Schema.string().default('Hello'),
maxRetries: Schema.number().default(3),
verbose: Schema.boolean().default(false),
})
export function apply(ctx: Context, config: Config) {
console.log(config.greeting) // 用户配置或默认值
@@ -37,7 +38,7 @@ export function apply(ctx: Context, config: Config) {
maxRetries: 5
```
未提供字段使用导出的 `Config` 对象中的默认值。
插件加载时,Cordis 会通过导出的 schema 校验配置,并填充未提供字段的默认值。不要导出普通对象作为 `Config`,因为它不满足 Cordis 要求的 Standard Schema 接口
## Schema 校验
@@ -92,7 +93,7 @@ export interface Config {
```typescript
export function apply(ctx: Context, config: Config) {
if (!ctx.llm.hasAdapter(config.model)) {
if (!ctx.llm.models().includes(config.model)) {
throw new Error(`Model "${config.model}" is not registered by any LLM adapter`)
}
}
+4 -9
View File
@@ -28,10 +28,8 @@ import type { Context } from 'cordis'
export const name = 'hello-plugin'
export function apply(ctx: Context) {
// 监听 agent-loop 的 ready 事件
ctx.on('ready', () => {
console.log('[hello-plugin] 插件已加载!')
})
// apply 被调用时,插件的必选依赖已就绪
console.log('[hello-plugin] 插件已加载!')
}
```
@@ -100,17 +98,14 @@ export default {
### 类形式
```typescript
import { Service } from 'cordis'
import { Service, type Context } from 'cordis'
export default class MyService extends Service {
static inject = ['tools']
constructor(ctx: Context) {
super(ctx, 'myService')
}
start() {
// 服务启动逻辑
// 构造函数内完成同步初始化
}
}
```
+5 -7
View File
@@ -133,14 +133,14 @@ defineTool({
// ...
presentCall(args) {
return {
intent: 'terminal',
title: `bash(${JSON.stringify(args.command).slice(0, 60)})`,
card: 'terminal',
title: args.command,
}
},
presentResult(args, result) {
return {
intent: 'terminal',
body: result.content.map(b => b.type === 'text' ? b.text : '').join(''),
card: 'terminal',
output: result.content.map(b => b.type === 'text' ? b.text : '').join(''),
}
},
})
@@ -156,9 +156,7 @@ defineTool({
// 这样就够了:
ctx.tools.register(defineTool({ /* ... */ }))
// 不需要:
// const dispose = ctx.tools.register(...)
// ctx.on('dispose', dispose)
// 不需要额外保存 disposer 或注册清理逻辑
```
## 完整实战示例
+18 -32
View File
@@ -24,15 +24,15 @@ Cordis 提供多种事件触发模式,适用于不同场景:
### emit — 广播
所有监听器并行执行,不关心返回值:
所有监听器同步执行,不关心返回值:
```typescript
// 触发
ctx.emit('agent/turn-end', { agentId, turnIndex })
ctx.emit('my-plugin/ready', { id: 'worker-1' })
// 监听
ctx.on('agent/turn-end', ({ agentId, turnIndex }) => {
console.log(`Turn ${turnIndex} ended`)
ctx.on('my-plugin/ready', ({ id }) => {
console.log(`${id} is ready`)
})
```
@@ -53,7 +53,7 @@ ctx.on('some-check', (input) => {
### serial — 顺序执行
所有监听器按注册顺序依次执行(异步安全)
监听器按注册顺序依次执行,并等待异步结果;第一个返回非空值的监听器会终止后续执行
```typescript
await ctx.serial('setup-phase', context)
@@ -61,18 +61,16 @@ await ctx.serial('setup-phase', context)
### waterfall — 管道
每个监听器接收前一个的输出,形成数据管道。**必须调用 `next()` 传递给下游**,不调用即为否决:
每个监听器可以包装下游返回值,形成处理链。**必须调用 `next()` 传递给下游**,不调用即为否决:
```typescript
// 触发
const finalMessages = await ctx.waterfall('llm/pre-request', messages)
const output = await ctx.waterfall('my-plugin/transform', input, async () => input)
// 监听(必须调用 next
ctx.on('llm/pre-request', async (messages, next) => {
// 可以修改 messages
messages.push(extraMessage)
// 必须调用 next() 传递给下一个监听器
return next(messages)
ctx.on('my-plugin/transform', async (_input, next) => {
const downstream = await next()
return downstream.trim()
})
```
@@ -89,6 +87,7 @@ declare module 'cordis' {
interface Events {
'my-plugin/ready': (payload: { id: string }) => void
'my-plugin/check': (input: string) => boolean | undefined
'my-plugin/transform': (input: string, next: () => Promise<string>) => Promise<string>
}
}
@@ -96,20 +95,11 @@ declare module 'cordis' {
// 都有正确的类型推导
```
## 命名约定
## Cordis 事件与会话记录
Harness 事件遵循 `namespace/action` 命名
Harness 的 Cordis 事件遵循 `namespace/action` 命名,例如 `agent/pre-step``agent/request``agent/step-result``tools/result``session/event`。完整签名与触发模式见[Events 目录](../../../../cordis-catalog/events.md)。
```
agent/pre-step — agent 执行一步之前
agent/post-step — agent 执行一步之后
tool/call — tool 被调用
tool/result — tool 返回结果
llm/pre-request — LLM 请求发送前
session/event — 会话事件被记录
compact/start — 压缩开始
compact/end — 压缩结束
```
`turn/*``step/*``tool/call``tool/result``compact/*` 是持久化的会话事件类型,不是同名 Cordis 事件。需要观察它们时,监听 `session/event` 并检查 `event.type`
## 事件也是效果
@@ -118,7 +108,7 @@ compact/end — 压缩结束
```typescript
export function apply(ctx: Context) {
// 这个监听器在插件 dispose 时自动清理
ctx.on('agent/turn-end', handler)
ctx.on('tools/result', handler)
}
```
@@ -132,14 +122,10 @@ import type { Context } from 'cordis'
export const name = 'tool-logger'
export function apply(ctx: Context) {
ctx.on('tool/call', ({ name, args }) => {
console.log(`[tool] ${name}(${JSON.stringify(args)})`)
})
ctx.on('tool/result', ({ name, result }) => {
ctx.on('tools/result', (exec, result) => {
console.log(`[tool] ${exec.name}(${JSON.stringify(exec.arguments)})`)
const text = result.content
.filter(b => b.type === 'text')
.map(b => b.text)
.map(block => block.type === 'text' ? block.text : '')
.join('')
console.log(`[tool result] ${text.slice(0, 100)}`)
})
+1 -11
View File
@@ -105,14 +105,6 @@ fiber.dispose()
export function apply(ctx: Context) {
console.log('plugin loading')
ctx.on('ready', () => {
console.log('context ready')
})
ctx.on('dispose', () => {
console.log('plugin disposing')
})
ctx.effect(() => {
console.log('effect registered')
return () => console.log('effect cleaned up')
@@ -124,12 +116,10 @@ export function apply(ctx: Context) {
```
plugin loading
effect registered
context ready
```
卸载时输出(逆序)
卸载时输出:
```
plugin disposing
effect cleaned up
```
+13 -4
View File
@@ -90,8 +90,11 @@ export default class MetricsService extends Service {
// 必选:服务不存在时,插件不会加载
export const inject = ['tools']
// 可选:服务不存在时,插件仍然加载,但 ctx.xxx 可能是 undefined
export const inject = { optional: ['metrics'] }
// 可选:不写入 inject,使用时通过 ctx.get() 查询
export function apply(ctx: Context) {
const metrics = ctx.get('metrics')
metrics?.record('plugin_loaded', 1)
}
```
### 服务消失时的行为
@@ -109,7 +112,10 @@ export const inject = { optional: ['metrics'] }
```yaml
- id: group-a
name: 'group:'
name: '@cordisjs/plugin-group'
group: true
isolate:
bash: true
config:
- name: '@deepseek-ai/dsh-bash-local'
config:
@@ -117,7 +123,10 @@ export const inject = { optional: ['metrics'] }
- name: './src/plugin-a.ts'
- id: group-b
name: 'group:'
name: '@cordisjs/plugin-group'
group: true
isolate:
bash: true
config:
- name: '@deepseek-ai/dsh-bash-local'
config:
@@ -114,6 +114,8 @@ interface GenerateOptions {
maxTokens?: number
/** 温度 */
temperature?: number
/** 取消或卸载时中止进行中的请求 */
signal?: AbortSignal
}
```
@@ -160,7 +162,10 @@ mock 适配器是学习 StreamChunk 协议的最佳起点——它用纯本地
```typescript
async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
const response = await fetch(this.endpoint, { /* ... */ })
const response = await fetch(this.endpoint, {
// ...method、headers 和 body
signal: options.signal,
})
if (!response.ok) {
throw new Error(`API error: ${response.status}`)
}
+1 -1
View File
@@ -13,7 +13,7 @@ hero:
link: /develop/basic/
features:
- title: 插件化架构
details: 基于 Cordis 效果系统,所有能力通过插件注册,加载即生效、卸载即还原。
details: 基于 Cordis 插件系统,所有能力通过插件注册,加载即生效、卸载即还原。
- title: 配置即组合
details: 一个 cordis.yml 决定整个 Agent 的能力组合——换模型、加工具,只需改一行配置。
- title: 开箱即用
+34 -3
View File
@@ -4,7 +4,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import type { DocsPage } from '../website/docs.ts'
import { docsPages, type DocsPage } from '../website/docs.ts'
import { addProjectionFrontmatter, rewriteMarkdown } from './project-doc-site.ts'
const roots: string[] = []
@@ -25,8 +25,10 @@ function fixture(): { root: string; pages: DocsPage[] } {
return {
root,
pages: [
{ source: 'docs/a.md', route: 'en/a.md', label: 'A', sidebar: 'en-docs', section: 'Test', order: 1 },
{ source: 'docs/b.md', route: 'en/reference/b.md', label: 'B', sidebar: 'en-docs', section: 'Test', order: 2 },
{ locale: 'root', contentLocale: 'en-US', source: 'docs/a.md', route: 'a.md', label: 'A', sidebar: 'zh-reference', section: 'Test', order: 1 },
{ locale: 'root', contentLocale: 'en-US', source: 'docs/b.md', route: 'reference-root/b.md', label: 'B', sidebar: 'zh-reference', section: 'Test', order: 2 },
{ locale: 'en', contentLocale: 'en-US', source: 'docs/a.md', route: 'en/a.md', label: 'A', sidebar: 'en-reference', section: 'Test', order: 1 },
{ locale: 'en', contentLocale: 'en-US', source: 'docs/b.md', route: 'en/reference/b.md', label: 'B', sidebar: 'en-reference', section: 'Test', order: 2 },
],
}
}
@@ -36,6 +38,7 @@ describe('rewriteMarkdown', () => {
const { root, pages } = fixture()
const source = '[B](b.md#part) [source](../packages/tool.ts:2) [web](https://example.com)\n'
expect(rewriteMarkdown(source, {
locale: 'en',
sourcePath: 'docs/a.md',
route: 'en/a.md',
pages,
@@ -48,9 +51,22 @@ describe('rewriteMarkdown', () => {
)
})
it('selects the published target in the current site locale', () => {
const { root, pages } = fixture()
expect(rewriteMarkdown('[B](b.md)\n', {
locale: 'root',
sourcePath: 'docs/a.md',
route: 'a.md',
pages,
repoRoot: root,
repositoryRef: 'abc123',
})).toBe('[B](./reference-root/b.md)\n')
})
it('uses raw GitHub content for unpublished images', () => {
const { root, pages } = fixture()
expect(rewriteMarkdown('![logo](../packages/logo.svg)\n', {
locale: 'en',
sourcePath: 'docs/a.md',
route: 'en/a.md',
pages,
@@ -63,6 +79,7 @@ describe('rewriteMarkdown', () => {
const { root, pages } = fixture()
const source = '```md\n[B](b.md)\n```\n'
expect(rewriteMarkdown(source, {
locale: 'en',
sourcePath: 'docs/a.md',
route: 'en/a.md',
pages,
@@ -74,6 +91,7 @@ describe('rewriteMarkdown', () => {
it('fails loud when a relative target is missing', () => {
const { root, pages } = fixture()
expect(() => rewriteMarkdown('[missing](missing.md)\n', {
locale: 'en',
sourcePath: 'docs/a.md',
route: 'en/a.md',
pages,
@@ -83,6 +101,19 @@ describe('rewriteMarkdown', () => {
})
})
describe('docsPages locale routes', () => {
it('publishes the same canonical source at every corresponding locale route', () => {
const byRoute = new Map(docsPages.map(page => [page.route, page]))
for (const page of docsPages.filter(page => page.locale === 'root')) {
const counterpart = byRoute.get(`en/${page.route}`)
expect(counterpart, page.route).toBeDefined()
expect(counterpart?.locale).toBe('en')
expect(counterpart?.source).toBe(page.source)
expect(counterpart?.contentLocale).toBe(page.contentLocale)
}
})
})
describe('addProjectionFrontmatter', () => {
it('adds frontmatter to an ordinary Markdown page', () => {
expect(addProjectionFrontmatter('# Guide\n', 'docs/guide.md')).toBe(
+11 -7
View File
@@ -11,7 +11,7 @@ import { fromMarkdown } from 'mdast-util-from-markdown'
import { gfmFromMarkdown } from 'mdast-util-gfm'
import { gfm } from 'micromark-extension-gfm'
import type { Nodes } from 'mdast'
import { docsPages, type DocsPage } from '../website/docs.ts'
import { docsPages, type DocsLocale, type DocsPage } from '../website/docs.ts'
const REPOSITORY_URL = 'https://github.com/deepseek-harness/deepseek-harness'
const root = resolve(import.meta.dirname, '..')
@@ -25,6 +25,7 @@ interface Replacement {
/** Inputs for rewriting one canonical Markdown page. */
export interface RewriteMarkdownOptions {
locale: DocsLocale
sourcePath: string
route: string
pages: DocsPage[]
@@ -62,14 +63,16 @@ function routeTarget(fromRoute: string, toRoute: string, suffix: string): string
return `${target.startsWith('.') ? target : `./${target}`}${suffix}`
}
function sourceMap(pages: DocsPage[]): Map<string, DocsPage> {
const map = new Map<string, DocsPage>()
function sourceMap(pages: DocsPage[]): Map<string, Map<DocsLocale, DocsPage>> {
const map = new Map<string, Map<DocsLocale, DocsPage>>()
for (const page of pages) {
for (const source of [page.source, ...(page.sourceAliases ?? [])]) {
if (map.has(source)) {
throw new Error(`project-doc-site: duplicate source or alias ${JSON.stringify(source)}.`)
const localized = map.get(source) ?? new Map<DocsLocale, DocsPage>()
if (localized.has(page.locale)) {
throw new Error(`project-doc-site: duplicate source or alias ${JSON.stringify(source)} for locale ${JSON.stringify(page.locale)}.`)
}
map.set(source, page)
localized.set(page.locale, page)
map.set(source, localized)
}
}
return map
@@ -132,7 +135,7 @@ export function rewriteMarkdown(source: string, options: RewriteMarkdownOptions)
if (path === '') return
const { absPath, line } = resolveRepositoryTarget(sourceAbs, path, options.repoRoot)
const targetPath = repoPath(absPath, options.repoRoot)
const page = published.get(targetPath)
const page = published.get(targetPath)?.get(options.locale)
const nextUrl = page === undefined
? githubTarget(absPath, line, suffix, options.repositoryRef, options.repoRoot, node.type === 'image')
: routeTarget(options.route, page.route, suffix)
@@ -205,6 +208,7 @@ export function projectDocs(): void {
const markdown = readFileSync(sourceAbs, 'utf8')
const projected = rewriteMarkdown(markdown, {
sourcePath: page.source,
locale: page.locale,
route: page.route,
pages: docsPages,
repoRoot: root,
+54 -7
View File
@@ -13,6 +13,14 @@ const sectionOrder = [
'基础',
'框架能力',
'实战',
'概念',
'生成参考',
'数据结构',
'开发手册',
'Guide',
'Basics',
'Framework',
'Practice',
'Concepts',
'Generated reference',
'Data structures',
@@ -20,7 +28,7 @@ const sectionOrder = [
]
function sidebar(collection: DocsPage['sidebar']): DefaultTheme.SidebarItem[] {
const pages = docsPages.filter(page => page.sidebar === collection && page.route !== 'index.md')
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) ?? []
@@ -51,7 +59,36 @@ function escapeVueInterpolation(html: string): string {
}
const sharedTheme: Pick<DefaultTheme.Config, 'search' | 'socialLinks' | 'editLink'> = {
search: { provider: 'local' },
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' },
],
@@ -81,14 +118,22 @@ export default withMermaid({
nav: [
{ text: '入门', link: '/guide/', activeMatch: '^/guide/' },
{ text: '开发', link: '/develop/basic/', activeMatch: '^/develop/' },
{ text: 'Reference', link: '/en/', activeMatch: '^/en/' },
{ 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: {
@@ -97,12 +142,14 @@ export default withMermaid({
link: '/en/',
themeConfig: {
nav: [
{ text: 'Concepts', link: '/en/' },
{ text: 'Reference', link: '/en/config-catalog' },
{ text: '中文指南', link: '/guide/' },
{ 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/': sidebar('en-docs'),
'/en/guide/': sidebar('en-guide'),
'/en/develop/': sidebar('en-develop'),
'/en/reference/': sidebar('en-reference'),
},
editLink: {
pattern: ({ frontmatter }: PageData) => {
+177 -122
View File
@@ -1,20 +1,38 @@
/**
* Canonical publication manifest for the documentation website.
*
* Markdown stays in its owning repository tier. This manifest only maps a
* source file to its public route and navigation placement.
* 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. */
sidebar: 'zh-guide' | 'zh-develop' | 'en-docs'
/** 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. */
@@ -23,191 +41,228 @@ export interface DocsPage {
sourceAliases?: string[]
}
const zhGuide: DocsPage[] = [
interface MirroredPage {
source: string
route: string
contentLocale: DocsPage['contentLocale']
label: Record<DocsLocale, string>
sidebar: Record<DocsLocale, DocsSidebar | null>
section: Record<DocsLocale, string>
order: number
sourceAliases?: string[]
}
function mirroredPages(pages: MirroredPage[]): DocsPage[] {
return pages.flatMap(page => (['root', 'en'] as const).map(locale => ({
locale,
contentLocale: page.contentLocale,
source: page.source,
route: locale === 'root' ? page.route : `en/${page.route}`,
label: page.label[locale],
sidebar: page.sidebar[locale],
section: page.section[locale],
order: page.order,
...(page.sourceAliases === undefined ? {} : { sourceAliases: page.sourceAliases }),
})))
}
const homeAndGuide = mirroredPages([
{
source: 'docs/user/zh-CN/index.md',
route: 'index.md',
label: 'DeepSeek Harness',
sidebar: 'zh-guide',
section: '入门',
contentLocale: 'zh-CN',
label: { root: 'DeepSeek Harness', en: 'DeepSeek Harness' },
sidebar: { root: null, en: null },
section: { root: '首页', en: 'Home' },
order: 0,
},
{
source: 'docs/user/zh-CN/guide/index.md',
route: 'guide/index.md',
label: '介绍',
sidebar: 'zh-guide',
section: '入门',
contentLocale: 'zh-CN',
label: { root: '介绍', en: 'Introduction' },
sidebar: { root: 'zh-guide', en: 'en-guide' },
section: { root: '入门', en: 'Guide' },
order: 1,
sourceAliases: ['docs/user/zh-CN/guide'],
},
{
source: 'docs/user/zh-CN/guide/quickstart.md',
route: 'guide/quickstart.md',
label: '快速开始',
sidebar: 'zh-guide',
section: '入门',
contentLocale: 'zh-CN',
label: { root: '快速开始', en: 'Quick start' },
sidebar: { root: 'zh-guide', en: 'en-guide' },
section: { root: '入门', en: 'Guide' },
order: 2,
},
{
source: 'docs/user/zh-CN/guide/config.md',
route: 'guide/config.md',
label: '配置文件',
sidebar: 'zh-guide',
section: '入门',
contentLocale: 'zh-CN',
label: { root: '配置文件', en: 'Configuration' },
sidebar: { root: 'zh-guide', en: 'en-guide' },
section: { root: '入门', en: 'Guide' },
order: 3,
},
]
])
const zhDevelop: DocsPage[] = [
const develop = mirroredPages([
{
source: 'docs/user/zh-CN/develop/basic/index.md',
route: 'develop/basic/index.md',
label: '第一个插件',
sidebar: 'zh-develop',
section: '基础',
contentLocale: 'zh-CN',
label: { root: '第一个插件', en: 'First plugin' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '基础', en: 'Basics' },
order: 1,
sourceAliases: ['docs/user/zh-CN/develop/basic'],
},
{
source: 'docs/user/zh-CN/develop/basic/tool.md',
route: 'develop/basic/tool.md',
label: '开发一个 Tool',
sidebar: 'zh-develop',
section: '基础',
contentLocale: 'zh-CN',
label: { root: '开发一个 Tool', en: 'Build a tool' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '基础', en: 'Basics' },
order: 2,
},
{
source: 'docs/user/zh-CN/develop/basic/config.md',
route: 'develop/basic/config.md',
label: '插件配置',
sidebar: 'zh-develop',
section: '基础',
contentLocale: 'zh-CN',
label: { root: '插件配置', en: 'Plugin configuration' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '基础', en: 'Basics' },
order: 3,
},
{
source: 'docs/user/zh-CN/develop/framework/index.md',
route: 'develop/framework/index.md',
label: '插件与生命周期',
sidebar: 'zh-develop',
section: '框架能力',
contentLocale: 'zh-CN',
label: { root: '插件与生命周期', en: 'Plugin lifecycle' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '框架能力', en: 'Framework' },
order: 1,
sourceAliases: ['docs/user/zh-CN/develop/framework'],
},
{
source: 'docs/user/zh-CN/develop/framework/service.md',
route: 'develop/framework/service.md',
label: '服务与依赖',
sidebar: 'zh-develop',
section: '框架能力',
contentLocale: 'zh-CN',
label: { root: '服务与依赖', en: 'Services and dependencies' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '框架能力', en: 'Framework' },
order: 2,
},
{
source: 'docs/user/zh-CN/develop/framework/events.md',
route: 'develop/framework/events.md',
label: '事件系统',
sidebar: 'zh-develop',
section: '框架能力',
contentLocale: 'zh-CN',
label: { root: '事件系统', en: 'Event system' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '框架能力', en: 'Framework' },
order: 3,
},
{
source: 'docs/user/zh-CN/develop/practice/index.md',
route: 'develop/practice/index.md',
label: '能力的三层拆分',
sidebar: 'zh-develop',
section: '实战',
contentLocale: 'zh-CN',
label: { root: '能力的三层拆分', en: 'Capability layering' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '实战', en: 'Practice' },
order: 1,
sourceAliases: ['docs/user/zh-CN/develop/practice'],
},
{
source: 'docs/user/zh-CN/develop/practice/llm-adapter.md',
route: 'develop/practice/llm-adapter.md',
label: 'LLM 适配器',
sidebar: 'zh-develop',
section: '实战',
contentLocale: 'zh-CN',
label: { root: 'LLM 适配器', en: 'LLM adapter' },
sidebar: { root: 'zh-develop', en: 'en-develop' },
section: { root: '实战', en: 'Practice' },
order: 2,
},
]
])
const enOverview: DocsPage[] = ([
['docs/architecture.md', 'en/index.md', 'Architecture'],
['docs/cordis-primer.md', 'en/cordis-primer.md', 'Cordis primer'],
['docs/capability-seams.md', 'en/capability-seams.md', 'Capability services'],
['docs/agent-lifecycle.md', 'en/agent-lifecycle.md', 'Agent lifecycle'],
['docs/tool-execution-pipeline.md', 'en/tool-execution-pipeline.md', 'Tool execution'],
] as const).map(([source, route, label], order) => ({
source,
route,
label,
sidebar: 'en-docs',
section: 'Concepts',
order,
}))
const enCatalogs: DocsPage[] = ([
['docs/config-catalog.md', 'en/config-catalog.md', 'Plugin configuration'],
['docs/tool-catalog.md', 'en/tool-catalog.md', 'Tool schemas'],
['docs/cordis-catalog/services.md', 'en/cordis-catalog/services.md', 'Services'],
['docs/cordis-catalog/events.md', 'en/cordis-catalog/events.md', 'Events'],
['docs/persistence-catalog.md', 'en/persistence-catalog.md', 'Persistence events'],
] as const).map(([source, route, label], order) => ({
source,
route,
label,
sidebar: 'en-docs',
section: 'Generated reference',
order,
}))
const corePages = [
['core.md', 'Core data structures'],
['session.md', 'Sessions'],
['tools.md', 'Tools'],
['llm-streaming.md', 'LLM streaming'],
['bash.md', '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 access'],
['persistence.md', 'Session persistence'],
] as const
const enCore: DocsPage[] = corePages.map(([file, label], order) => ({
source: `docs/core-data-structures/${file}`,
route: `en/core-data-structures/${file}`,
label,
sidebar: 'en-docs',
section: 'Data structures',
order,
...(file === 'core.md' ? { sourceAliases: ['docs/core-data-structures'] } : {}),
}))
const enCookbook: DocsPage[] = ([
['adding-a-package.md', 'Adding a package'],
['adding-a-tool.md', 'Adding a tool'],
['adding-an-llm-adapter.md', 'Adding an LLM adapter'],
['extension-cookbook.md', 'Extension patterns'],
] as const).map(([file, label], order) => ({
source: `docs/cookbook/${file}`,
route: `en/cookbook/${file}`,
label,
sidebar: 'en-docs',
section: 'Cookbook',
order,
}))
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,
})),
...([
['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[] = [
...zhGuide,
...zhDevelop,
...enOverview,
...enCatalogs,
...enCore,
...enCookbook,
...homeAndGuide,
...develop,
...reference,
]