From 62080d49c265d3a7a5bf8efcae524a29e3320b8e Mon Sep 17 00:00:00 2001 From: Turtle Date: Thu, 13 Aug 2026 13:43:43 +0800 Subject: [PATCH 1/4] docs: fix release smoke test failures --- ...-13-published-document-fragments.i18n.yaml | 6 + ...2026-08-13-published-document-fragments.md | 27 +++ ...6-08-13-published-document-fragments.zh.md | 27 +++ docs/architecture.i18n.yaml | 2 +- docs/architecture.zh.md | 4 + docs/config-catalog.i18n.yaml | 4 +- docs/config-catalog.md | 210 ++++++++++++++++++ docs/config-catalog.zh.md | 210 ++++++++++++++++++ docs/cookbook/adding-a-tool.i18n.yaml | 2 +- docs/cookbook/adding-a-tool.zh.md | 2 + docs/cookbook/extension-cookbook.i18n.yaml | 2 +- docs/cookbook/extension-cookbook.zh.md | 2 + docs/cordis-primer.i18n.yaml | 2 +- docs/cordis-primer.zh.md | 2 + docs/cordis-tutorial/03-services.i18n.yaml | 4 +- docs/cordis-tutorial/03-services.md | 2 +- docs/cordis-tutorial/03-services.zh.md | 2 +- .../06-composition-and-hmr.i18n.yaml | 4 +- .../cordis-tutorial/06-composition-and-hmr.md | 2 +- .../06-composition-and-hmr.zh.md | 2 +- docs/cordis-tutorial/index.i18n.yaml | 2 +- docs/cordis-tutorial/index.zh.md | 2 + docs/persistence-catalog.i18n.yaml | 4 +- docs/persistence-catalog.md | 88 ++++++++ docs/persistence-catalog.zh.md | 88 ++++++++ docs/subsystems/core.i18n.yaml | 4 +- docs/subsystems/core.md | 2 + docs/subsystems/core.zh.md | 2 + docs/subsystems/llm-streaming.i18n.yaml | 2 +- docs/subsystems/llm-streaming.md | 2 + docs/subsystems/persistence.i18n.yaml | 4 +- docs/subsystems/persistence.md | 2 + docs/subsystems/persistence.zh.md | 2 + docs/subsystems/session.i18n.yaml | 2 +- docs/subsystems/session.md | 2 + docs/tool-catalog.i18n.yaml | 4 +- docs/tool-catalog.md | 48 ++++ docs/tool-catalog.zh.md | 48 ++++ docs/user/develop/basic/index.i18n.yaml | 4 +- docs/user/develop/basic/index.md | 6 +- docs/user/develop/basic/index.zh.md | 6 +- docs/user/develop/basic/publish.i18n.yaml | 4 +- docs/user/develop/basic/publish.md | 45 ++-- docs/user/develop/basic/publish.zh.md | 45 ++-- docs/user/develop/framework/events.i18n.yaml | 4 +- docs/user/develop/framework/events.md | 6 +- docs/user/develop/framework/events.zh.md | 6 +- docs/user/develop/framework/service.i18n.yaml | 4 +- docs/user/develop/framework/service.md | 4 +- docs/user/develop/framework/service.zh.md | 6 +- docs/user/develop/practice/index.i18n.yaml | 4 +- docs/user/develop/practice/index.md | 2 +- docs/user/develop/practice/index.zh.md | 2 +- .../develop/practice/llm-adapter.i18n.yaml | 4 +- docs/user/develop/practice/llm-adapter.md | 20 +- docs/user/develop/practice/llm-adapter.zh.md | 20 +- docs/user/guide/index.i18n.yaml | 4 +- docs/user/guide/index.md | 8 +- docs/user/guide/index.zh.md | 8 +- package.json | 7 +- scripts/gen-config-catalog.ts | 10 +- scripts/gen-persistence-catalog.ts | 4 +- scripts/gen-tool-catalog.ts | 3 +- scripts/run-gates.ts | 2 +- scripts/verify-doc-site-fragments.spec.ts | 67 ++++++ scripts/verify-doc-site-fragments.ts | 143 ++++++++++++ 66 files changed, 1150 insertions(+), 124 deletions(-) create mode 100644 .agents/notes/implemented/process/2026-08-13-published-document-fragments.i18n.yaml create mode 100644 .agents/notes/implemented/process/2026-08-13-published-document-fragments.md create mode 100644 .agents/notes/implemented/process/2026-08-13-published-document-fragments.zh.md create mode 100644 scripts/verify-doc-site-fragments.spec.ts create mode 100644 scripts/verify-doc-site-fragments.ts diff --git a/.agents/notes/implemented/process/2026-08-13-published-document-fragments.i18n.yaml b/.agents/notes/implemented/process/2026-08-13-published-document-fragments.i18n.yaml new file mode 100644 index 0000000000..1051ae0b32 --- /dev/null +++ b/.agents/notes/implemented/process/2026-08-13-published-document-fragments.i18n.yaml @@ -0,0 +1,6 @@ +# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each +# side as of the last confirmed-consistent state. Both languages carry equal authority; +# after editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-13-published-document-fragments.md +2026-08-13-published-document-fragments.md: 9837d4ec1335f8da9910b3d2c2167f4c6d3a1aab +2026-08-13-published-document-fragments.zh.md: f4c68ae01082ab53a251c9951906538f0f4ae770 diff --git a/.agents/notes/implemented/process/2026-08-13-published-document-fragments.md b/.agents/notes/implemented/process/2026-08-13-published-document-fragments.md new file mode 100644 index 0000000000..9837d4ec13 --- /dev/null +++ b/.agents/notes/implemented/process/2026-08-13-published-document-fragments.md @@ -0,0 +1,27 @@ +# Agent Note: Validate published document fragments + +Status: implemented + +English | [中文](2026-08-13-published-document-fragments.zh.md) + +## Problem + +`verify-md-links` validates fragments with GitHub's Markdown heading ids, while the documentation website renders headings with VitePress. Punctuation-heavy headings and translated headings can therefore pass source validation but produce links to ids absent from the published HTML. A successful VitePress build validates target pages, not fragment ids. + +## Decision + +`docs:build` and its MPA variant run `verify-doc-site-fragments` after VitePress emits `website/.dist`. The verifier parses every emitted HTML page, resolves each internal fragment link against VitePress clean URLs, and fails when the output is absent or either the target page or requested id is missing. Unit tests cover absent output, clean URLs, `.html` aliases, same-page links, encoded ids, missing ids, missing routes, and external-link exclusion. + +Generated config, tool, and persistence catalogs emit explicit GitHub-compatible id aliases before punctuation-heavy headings. Authored translated pages add explicit language-neutral aliases when their localized VitePress heading id differs from the shared fragment used by the bilingual pair. Source Markdown validation remains independent and continues to reject links that do not resolve under repository rendering. + +## Alternatives considered + +**Use locale-specific fragments.** Bilingual pairs intentionally preserve identical link targets. Locale-specific fragments would make the two sources disagree and would require every link producer to know the target locale's translated heading. + +**Rely on VitePress heading ids.** Those ids depend on rendered punctuation and localized heading text. They do not preserve the GitHub ids already used by repository links and generated references. + +**Check source Markdown only.** This leaves the published artifact unverified and cannot detect differences between the GitHub and VitePress slug algorithms. + +## Consequences + +Every production documentation build reads its emitted HTML once, adding a bounded post-build check to the existing site build. Cross-page fragment links now require an id that survives publication. Explicit aliases become part of the published reference and let headings change language or punctuation without invalidating established fragments. diff --git a/.agents/notes/implemented/process/2026-08-13-published-document-fragments.zh.md b/.agents/notes/implemented/process/2026-08-13-published-document-fragments.zh.md new file mode 100644 index 0000000000..f4c68ae010 --- /dev/null +++ b/.agents/notes/implemented/process/2026-08-13-published-document-fragments.zh.md @@ -0,0 +1,27 @@ +# Agent Note: 校验已发布文档的 fragment + +Status: implemented + +[English](2026-08-13-published-document-fragments.md) | 中文 + +## Problem + +`verify-md-links` 使用 GitHub 的 Markdown 标题 id 校验 fragment,而文档网站使用 VitePress 渲染标题。包含较多标点的标题与翻译后的标题可能通过源码校验,却在已发布 HTML 中没有对应 id。VitePress 构建成功只会校验目标页面,不会校验 fragment id。 + +## Decision + +`docs:build` 及其 MPA 变体会在 VitePress 生成 `website/.dist` 后运行 `verify-doc-site-fragments`。该校验器解析每个生成的 HTML 页面,按照 VitePress clean URL 解析每个内部 fragment 链接,并在构建产物不存在、目标页面不存在或请求的 id 缺失时失败。单元测试覆盖缺失构建产物、clean URL、`.html` 别名、同页链接、编码 id、缺失 id、缺失路由和外部链接排除。 + +生成的配置、工具和持久化目录会在包含较多标点的标题前生成与 GitHub 兼容的显式 id 别名。如果翻译页面的 VitePress 本地化标题 id 与双语对侧文件共享的 fragment 不同,编写者会添加语言无关的显式别名。源码 Markdown 校验保持独立,仍会拒绝在仓库渲染规则下无法解析的链接。 + +## Alternatives considered + +**使用各语言专属的 fragment。** 双语对侧文件会刻意保留相同的链接目标。语言专属 fragment 会使两侧源码不一致,还会要求每个链接生成方都了解目标语言翻译后的标题。 + +**依赖 VitePress 标题 id。** 这些 id 取决于渲染后的标点与本地化标题文本,无法保留仓库链接和生成引用已经使用的 GitHub id。 + +**只检查 Markdown 源码。** 这种做法不会校验发布产物,也无法发现 GitHub 与 VitePress slug 算法之间的差异。 + +## Consequences + +每次生产文档构建都会读取一次生成的 HTML,在现有网站构建后增加一个有界检查。跨页面 fragment 链接必须指向发布后仍存在的 id。显式别名成为已发布参考的一部分,使标题更换语言或标点后仍能保留既有 fragment。 diff --git a/docs/architecture.i18n.yaml b/docs/architecture.i18n.yaml index f077b66226..d1556bb542 100644 --- a/docs/architecture.i18n.yaml +++ b/docs/architecture.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/architecture.md architecture.md: 77000ce9d4608d440e1d903eb80a42f2ed6435ef -architecture.zh.md: 268724d52c82e31c4fc2b51db59823f3720f9b91 +architecture.zh.md: f2f5310f665b86b86587307e7ce31c5841b96317 diff --git a/docs/architecture.zh.md b/docs/architecture.zh.md index 268724d52c..f2f5310f66 100644 --- a/docs/architecture.zh.md +++ b/docs/architecture.zh.md @@ -50,6 +50,8 @@ dsh --profile web --dump-config | [`core/scope`](subsystems/scope.md) | 按 agent 划分作用域的注册原语 | 库,无 ctx 键 | | [`llm/llm`](subsystems/llm-streaming.md) | 消息与流式词汇表,以及适配器 seam | `ctx.llm` | + + ## 事件 事件就是扩展点,而选对事件域是大多数改动的第一个决定。 @@ -60,6 +62,8 @@ dsh --profile web --dump-config [事件映射](event-producer-consumer.md)列出每个事件的生产方与消费方。 + + ## 轮次流程 一个**步骤**是一次模型请求加上它调用的工具。一个**轮次**包含零个或多个步骤:它在领取首条输入之前打开,并在不再欠下任何工作时关闭。 diff --git a/docs/config-catalog.i18n.yaml b/docs/config-catalog.i18n.yaml index b1bec33ca0..b3aa609638 100644 --- a/docs/config-catalog.i18n.yaml +++ b/docs/config-catalog.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/config-catalog.md -config-catalog.md: 19bfa6d1fb847de4a7207f42dabd67d43f288361 -config-catalog.zh.md: fda208e8fcd2ff6dd697efed84a4073ecbd5a912 +config-catalog.md: dfa03e7109fbfeee425469e457186a9ad7e3cb28 +config-catalog.zh.md: dc20cd90e5839be67a086b8e64a814f1013ce3af diff --git a/docs/config-catalog.md b/docs/config-catalog.md index 19bfa6d1fb..dfa03e7109 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -9,6 +9,8 @@ This file is GENERATED from source (`scripts/gen-config-catalog.ts`) and verifie A `Requires:` line lists the service keys the plugin `inject`s: its `cordis.yml` tree must also load providers for those services. Scope is the harness tier (`packages/`); the vendored cordis plugins a config tree may also load (`hmr`, the console logger, …) are pinned upstream source ([vendoring policy](../vendor/README.md)) and not catalogued here. + + ## `@deepseek-ai/dsh-acp` Requires: `agents` @@ -29,6 +31,8 @@ Depends on: `Stream` (`@agentclientprotocol/sdk`) Source: [`packages/acp/acp/src/index.ts:70`](../packages/acp/acp/src/index.ts) + + ## `@deepseek-ai/dsh-acp-demo` ```ts config-catalog @@ -82,6 +86,8 @@ Depends on: [`agentCore`](../packages/examples/agent-spine-demo/src/index.ts) · Source: [`packages/examples/acp-demo/src/index.ts:39`](../packages/examples/acp-demo/src/index.ts) + + ## `@deepseek-ai/dsh-agent-default-model` ```ts config-catalog @@ -96,6 +102,8 @@ export interface Config { Source: [`packages/core/agent-default-model/src/index.ts:41`](../packages/core/agent-default-model/src/index.ts) + + ## `@deepseek-ai/dsh-agent-instructions` ```ts config-catalog @@ -124,6 +132,8 @@ export interface Config { Source: [`packages/context/agent-instructions/src/config.ts:18`](../packages/context/agent-instructions/src/config.ts) + + ## `@deepseek-ai/dsh-agent-loop` Requires: `agents` · `sessions` · `llm` · `tools` · `systemPrompt` @@ -154,6 +164,8 @@ Depends on: [`AgentOptions`](subsystems/core.md) · [`SessionId`](subsystems/cor Source: [`packages/core/agent-loop/src/index.ts:255`](../packages/core/agent-loop/src/index.ts) + + ## `@deepseek-ai/dsh-agent-presets` Requires: `loader` @@ -190,6 +202,8 @@ export type PresetTrust = 'system' | 'user' Source: [`packages/preset/agent-presets/src/preset.ts:52`](../packages/preset/agent-presets/src/preset.ts) + + ## `@deepseek-ai/dsh-agent-spine-demo` ```ts config-catalog @@ -280,6 +294,8 @@ Depends on: [`AgentLoopConfig`](#deepseek-aidsh-agent-loop) · [`GoalDomainConfi Source: [`packages/examples/agent-spine-demo/src/index.ts:92`](../packages/examples/agent-spine-demo/src/index.ts) + + ## `@deepseek-ai/dsh-agent-tool-presentation` Requires: `tools` @@ -302,6 +318,8 @@ Depends on: [`ToolPresentationMode`](subsystems/tools.md) Source: [`packages/core/agent-tool-presentation/src/index.ts:38`](../packages/core/agent-tool-presentation/src/index.ts) + + ## `@deepseek-ai/dsh-attachment-local` ```ts config-catalog @@ -322,6 +340,8 @@ export interface Config { Source: [`packages/attachment/attachment-local/src/index.ts:24`](../packages/attachment/attachment-local/src/index.ts) + + ## `@deepseek-ai/dsh-bash-local` Requires: `subprocess` @@ -346,6 +366,8 @@ export interface Config { Source: [`packages/shell/bash-local/src/index.ts:41`](../packages/shell/bash-local/src/index.ts) + + ## `@deepseek-ai/dsh-bash-sandbox` Requires: `subprocess` · `sandbox` · `sandboxPolicy` @@ -365,6 +387,8 @@ Depends on: [`LocalConfig`](#deepseek-aidsh-bash-local) Source: [`packages/shell/bash-sandbox/src/index.ts:35`](../packages/shell/bash-sandbox/src/index.ts) + + ## `@deepseek-ai/dsh-client-connection` Requires: `webServer` @@ -388,6 +412,8 @@ export interface ConnectionConfig { Source: [`packages/client/connection/src/index.ts:50`](../packages/client/connection/src/index.ts) + + ## `@deepseek-ai/dsh-client-hmr` Requires: `clientModules` · `webServer` @@ -402,6 +428,8 @@ export interface Config { Source: [`packages/client/hmr/src/index.ts:31`](../packages/client/hmr/src/index.ts) + + ## `@deepseek-ai/dsh-code-runtime-worker-thread` ```ts config-catalog @@ -437,6 +465,8 @@ export interface Config { Source: [`packages/code-runtime/code-runtime-worker-thread/src/index.ts:25`](../packages/code-runtime/code-runtime-worker-thread/src/index.ts) + + ## `@deepseek-ai/dsh-compaction-basic` Requires: `llm` · `tokenMeter` · `sessions` @@ -481,6 +511,8 @@ export interface ModelCompactPolicyConfig extends CompactionPolicyConfig { Source: [`packages/compaction/compaction-basic/src/types.ts:38`](../packages/compaction/compaction-basic/src/types.ts) + + ## `@deepseek-ai/dsh-compaction-tool-result-pruner` Requires: `tokenMeter` @@ -499,6 +531,8 @@ export interface ToolResultPruneConfig { Source: [`packages/compaction/compaction-tool-result-pruner/src/types.ts:4`](../packages/compaction/compaction-tool-result-pruner/src/types.ts) + + ## `@deepseek-ai/dsh-cordis-host-runner` Requires: `tools` @@ -513,6 +547,8 @@ export interface Config { Source: [`packages/extensions/cordis-host-runner/src/index.ts:88`](../packages/extensions/cordis-host-runner/src/index.ts) + + ## `@deepseek-ai/dsh-credentials-local` ```ts config-catalog @@ -531,6 +567,8 @@ export interface Config { Source: [`packages/credentials/credentials-local/src/index.ts:55`](../packages/credentials/credentials-local/src/index.ts) + + ## `@deepseek-ai/dsh-e2b` ```ts config-catalog @@ -547,6 +585,8 @@ export interface Config { Source: [`packages/e2b/e2b/src/index.ts:43`](../packages/e2b/e2b/src/index.ts) + + ## `@deepseek-ai/dsh-fs-local` ```ts config-catalog @@ -564,6 +604,8 @@ export interface Config { Source: [`packages/fs/fs-local/src/index.ts:41`](../packages/fs/fs-local/src/index.ts) + + ## `@deepseek-ai/dsh-fs-sandbox` Requires: `sandboxPolicy` @@ -582,6 +624,8 @@ Depends on: [`LocalConfig`](#deepseek-aidsh-fs-local) Source: [`packages/fs/fs-sandbox/src/index.ts:49`](../packages/fs/fs-sandbox/src/index.ts) + + ## `@deepseek-ai/dsh-goal` Requires: `agents` @@ -596,6 +640,8 @@ export interface Config { Source: [`packages/goal/goal/src/index.ts:116`](../packages/goal/goal/src/index.ts) + + ## `@deepseek-ai/dsh-headless` Requires: `agentDefaultModel` · `agents` · `sessions` @@ -610,6 +656,8 @@ export interface Config { Source: [`packages/bundle/headless/src/index.ts:31`](../packages/bundle/headless/src/index.ts) + + ## `@deepseek-ai/dsh-hooks-claude-code` Requires: `shell` @@ -646,6 +694,8 @@ export interface Config { Source: [`packages/hooks/hooks-claude-code/src/index.ts:45`](../packages/hooks/hooks-claude-code/src/index.ts) + + ## `@deepseek-ai/dsh-hooks-codex` Requires: `shell` @@ -671,6 +721,8 @@ export interface Config { Source: [`packages/hooks/hooks-codex/src/index.ts:44`](../packages/hooks/hooks-codex/src/index.ts) + + ## `@deepseek-ai/dsh-host-apiproxy` Requires: `agentDefaultModel` · `agents` · `attachments` · `directoryPicker` · `llm` · `sessions` · `subagents` · `sessionQuery` · `tools` · `userQuestions` · `workspaceRegistry` @@ -697,6 +749,8 @@ export interface Config { Source: [`packages/host/apiproxy/src/index.ts:41`](../packages/host/apiproxy/src/index.ts) + + ## `@deepseek-ai/dsh-host-directory-picker-browse` ```ts config-catalog @@ -709,6 +763,8 @@ export interface Config { Source: [`packages/host/directory-picker-browse/src/index.ts:181`](../packages/host/directory-picker-browse/src/index.ts) + + ## `@deepseek-ai/dsh-host-frontend-static` Requires: `webServer` @@ -723,6 +779,8 @@ export interface Config { Source: [`packages/host/frontend-static/src/index.ts:28`](../packages/host/frontend-static/src/index.ts) + + ## `@deepseek-ai/dsh-host-webserver` ```ts config-catalog @@ -737,6 +795,8 @@ export interface Config { Source: [`packages/host/webserver/src/index.ts:45`](../packages/host/webserver/src/index.ts) + + ## `@deepseek-ai/dsh-invariants` ```ts config-catalog @@ -753,6 +813,8 @@ export interface Config { Source: [`packages/runtime-diagnostics/invariants/src/index.ts:15`](../packages/runtime-diagnostics/invariants/src/index.ts) + + ## `@deepseek-ai/dsh-jobs-local` ```ts config-catalog @@ -768,6 +830,8 @@ export interface Config { Source: [`packages/jobs/jobs-local/src/index.ts:31`](../packages/jobs/jobs-local/src/index.ts) + + ## `@deepseek-ai/dsh-llm-deepseek` Requires: `llm` @@ -821,6 +885,8 @@ Depends on: [`RetryPolicyConfig`](../packages/llm/llm/src/index.ts) Source: [`packages/llm/llm-deepseek/src/index.ts:62`](../packages/llm/llm-deepseek/src/index.ts) + + ## `@deepseek-ai/dsh-llm-pi-ai` Requires: `llm` @@ -1009,6 +1075,8 @@ Depends on: `Api` (`@earendil-works/pi-ai`) · `CacheRetention` (`@earendil-work Source: [`packages/llm/llm-pi-ai/src/config.ts:172`](../packages/llm/llm-pi-ai/src/config.ts) + + ## `@deepseek-ai/dsh-llm-replay` Requires: `llm` @@ -1075,6 +1143,8 @@ Depends on: [`ModelModality`](../packages/llm/llm/src/index.ts) · [`RetryPolicy Source: [`packages/test-support/llm-replay/src/index.ts:776`](../packages/test-support/llm-replay/src/index.ts) + + ## `@deepseek-ai/dsh-llm-retry` Requires: `agents` @@ -1086,6 +1156,8 @@ export type Config = Readonly> Source: [`packages/llm/llm-retry/src/index.ts:24`](../packages/llm/llm-retry/src/index.ts) + + ## `@deepseek-ai/dsh-lsp-stdio` Requires: `fs` · `lsp` · `subprocess` @@ -1126,6 +1198,8 @@ export interface LspLocalServerConfig { Source: [`packages/lsp/lsp-stdio/src/index.ts:82`](../packages/lsp/lsp-stdio/src/index.ts) + + ## `@deepseek-ai/dsh-mcp-client` Requires: `tools` @@ -1197,6 +1271,8 @@ export interface ReconnectConfig { Source: [`packages/mcp/mcp-client/src/index.ts:98`](../packages/mcp/mcp-client/src/index.ts) + + ## `@deepseek-ai/dsh-message-feedback` Requires: `storageDomain` · `sessionPersistence` · `sessions` @@ -1211,6 +1287,8 @@ export interface Config { Source: [`packages/feedback/message-feedback/src/index.ts:49`](../packages/feedback/message-feedback/src/index.ts) + + ## `@deepseek-ai/dsh-permission-presets` Requires: `shell` · `approval` · `sessions` @@ -1248,6 +1326,8 @@ Depends on: [`ApprovalPolicy`](subsystems/approval.md) · [`SandboxMode`](subsys Source: [`packages/interaction/permission-presets/src/index.ts:140`](../packages/interaction/permission-presets/src/index.ts) + + ## `@deepseek-ai/dsh-persona` Requires: `systemPrompt` @@ -1270,6 +1350,8 @@ export interface Config { Source: [`packages/preset/persona/src/index.ts:34`](../packages/preset/persona/src/index.ts) + + ## `@deepseek-ai/dsh-plan-mode` Requires: `tools` · `systemPrompt` @@ -1284,6 +1366,8 @@ export interface PlanModeConfig { Source: [`packages/plan/plan-mode/src/index.ts:70`](../packages/plan/plan-mode/src/index.ts) + + ## `@deepseek-ai/dsh-pwsh-local` Requires: `subprocess` @@ -1315,6 +1399,8 @@ export interface Config { Source: [`packages/shell/pwsh-local/src/index.ts:58`](../packages/shell/pwsh-local/src/index.ts) + + ## `@deepseek-ai/dsh-pwsh-sandbox` Requires: `subprocess` · `sandbox` · `sandboxPolicy` @@ -1335,6 +1421,8 @@ Depends on: [`LocalConfig`](#deepseek-aidsh-pwsh-local) Source: [`packages/shell/pwsh-sandbox/src/index.ts:40`](../packages/shell/pwsh-sandbox/src/index.ts) + + ## `@deepseek-ai/dsh-repeat-tool-reminder` ```ts config-catalog @@ -1367,6 +1455,8 @@ export interface Config { Source: [`packages/guard/repeat-tool-reminder/src/index.ts:28`](../packages/guard/repeat-tool-reminder/src/index.ts) + + ## `@deepseek-ai/dsh-sandbox-local` ```ts config-catalog @@ -1397,6 +1487,8 @@ export interface Config { Source: [`packages/sandbox/sandbox-local/src/index.ts:44`](../packages/sandbox/sandbox-local/src/index.ts) + + ## `@deepseek-ai/dsh-sandbox-policy` ```ts config-catalog @@ -1422,6 +1514,8 @@ Depends on: [`SandboxMode`](subsystems/sandbox.md) Source: [`packages/sandbox/sandbox-policy/src/index.ts:67`](../packages/sandbox/sandbox-policy/src/index.ts) + + ## `@deepseek-ai/dsh-sdk-jsonrpc-server` Requires: `agents` @@ -1444,6 +1538,8 @@ Depends on: `Readable` (`node:stream`) · `Writable` (`node:stream`) Source: [`packages/sdk/server/src/index.ts:25`](../packages/sdk/server/src/index.ts) + + ## `@deepseek-ai/dsh-session-persistence-jsonl` Requires: `sessions` @@ -1481,6 +1577,8 @@ export type JsonlCompression = 'zstd' | 'none' Source: [`packages/session/session-persistence-jsonl/src/index.ts:60`](../packages/session/session-persistence-jsonl/src/index.ts) + + ## `@deepseek-ai/dsh-session-persistence-sqlite` Requires: `sessions` @@ -1524,6 +1622,8 @@ export type JournalMode = 'wal' | 'delete' | 'truncate' | 'persist' Source: [`packages/session/session-persistence-sqlite/src/index.ts:70`](../packages/session/session-persistence-sqlite/src/index.ts) + + ## `@deepseek-ai/dsh-session-projection-cache` Requires: `storageDomain` · `sessionProjections` · `sessionPersistence` · `sessions` @@ -1545,6 +1645,8 @@ export interface Config { Source: [`packages/session/session-projection-cache/src/index.ts:42`](../packages/session/session-projection-cache/src/index.ts) + + ## `@deepseek-ai/dsh-session-query-sqlite` Requires: `sessions` @@ -1589,6 +1691,8 @@ Depends on: [`SessionQueryConfig`](../packages/session-query/session-query/src/i Source: [`packages/session-query/session-query-sqlite/src/index.ts:89`](../packages/session-query/session-query-sqlite/src/index.ts) + + ## `@deepseek-ai/dsh-session-reference` Requires: `sessionQuery` @@ -1607,6 +1711,8 @@ export interface Config { Source: [`packages/context/session-reference/src/config.ts:11`](../packages/context/session-reference/src/config.ts) + + ## `@deepseek-ai/dsh-session-telemetry-otel` Requires: `sessions` @@ -1651,6 +1757,8 @@ Depends on: `BatchLogRecordProcessorOptions` (`@opentelemetry/sdk-logs`) · `OTL Source: [`packages/session/session-telemetry-otel/src/index.ts:91`](../packages/session/session-telemetry-otel/src/index.ts) + + ## `@deepseek-ai/dsh-session-title` Requires: `sessions` @@ -1669,6 +1777,8 @@ export interface Config { Source: [`packages/session/session-title/src/index.ts:79`](../packages/session/session-title/src/index.ts) + + ## `@deepseek-ai/dsh-session-title-all-prompts-llm` Requires: `sessionTitle` · `llm` · `sessions` @@ -1682,6 +1792,8 @@ Depends on: [`SessionTitleLlmConfig`](../packages/session/session-title-llm/src/ Source: [`packages/session/session-title-all-prompts-llm/src/index.ts:15`](../packages/session/session-title-all-prompts-llm/src/index.ts) + + ## `@deepseek-ai/dsh-session-title-first-prompt-llm` Requires: `sessionTitle` · `llm` · `sessions` @@ -1695,6 +1807,8 @@ Depends on: [`SessionTitleLlmConfig`](../packages/session/session-title-llm/src/ Source: [`packages/session/session-title-first-prompt-llm/src/index.ts:15`](../packages/session/session-title-first-prompt-llm/src/index.ts) + + ## `@deepseek-ai/dsh-settings-file` ```ts config-catalog @@ -1713,6 +1827,8 @@ export interface Config { Source: [`packages/settings/settings-file/src/index.ts:21`](../packages/settings/settings-file/src/index.ts) + + ## `@deepseek-ai/dsh-shell-env` ```ts config-catalog @@ -1725,6 +1841,8 @@ export interface Config { Source: [`packages/shell/shell-env/src/index.ts:29`](../packages/shell/shell-env/src/index.ts) + + ## `@deepseek-ai/dsh-skill` ```ts config-catalog @@ -1737,6 +1855,8 @@ export interface Config { Source: [`packages/skill/skill/src/index.ts:279`](../packages/skill/skill/src/index.ts) + + ## `@deepseek-ai/dsh-skill-filesystem` Requires: `skills` @@ -1773,6 +1893,8 @@ export interface Config { Source: [`packages/skill/skill-filesystem/src/index.ts:49`](../packages/skill/skill-filesystem/src/index.ts) + + ## `@deepseek-ai/dsh-spill-local` ```ts config-catalog @@ -1789,6 +1911,8 @@ export interface Config { Source: [`packages/spill/spill-local/src/index.ts:22`](../packages/spill/spill-local/src/index.ts) + + ## `@deepseek-ai/dsh-spill-policy` Requires: `tools` @@ -1807,6 +1931,8 @@ export interface Config { Source: [`packages/spill/spill-policy/src/index.ts:60`](../packages/spill/spill-policy/src/index.ts) + + ## `@deepseek-ai/dsh-storage-domain` Requires: `storage` @@ -1828,6 +1954,8 @@ export interface Config { Source: [`packages/storage/storage-domain/src/index.ts:52`](../packages/storage/storage-domain/src/index.ts) + + ## `@deepseek-ai/dsh-storage-json` Requires: `storage` @@ -1847,6 +1975,8 @@ export interface Config { Source: [`packages/storage/storage-json/src/index.ts:27`](../packages/storage/storage-json/src/index.ts) + + ## `@deepseek-ai/dsh-storage-sqlite` Requires: `storage` @@ -1885,6 +2015,8 @@ export type JournalMode = 'wal' | 'delete' | 'truncate' | 'persist' Source: [`packages/storage/storage-sqlite/src/index.ts:24`](../packages/storage/storage-sqlite/src/index.ts) + + ## `@deepseek-ai/dsh-subagent-acp` Requires: `subagents` · `subprocess` @@ -1936,6 +2068,8 @@ export type PermissionPolicy = 'allow' | 'reject' Source: [`packages/subagent/subagent-acp/src/index.ts:27`](../packages/subagent/subagent-acp/src/index.ts) + + ## `@deepseek-ai/dsh-subagent-claude-code` Requires: `subagents` · `subprocess` @@ -1955,6 +2089,8 @@ export interface Config { Source: [`packages/subagent/subagent-claude-code/src/index.ts:32`](../packages/subagent/subagent-claude-code/src/index.ts) + + ## `@deepseek-ai/dsh-subagent-codex` Requires: `subagents` · `subprocess` @@ -1974,6 +2110,8 @@ export interface Config { Source: [`packages/subagent/subagent-codex/src/index.ts:30`](../packages/subagent/subagent-codex/src/index.ts) + + ## `@deepseek-ai/dsh-subagent-dsh-sdk` Requires: `subagents` @@ -2025,6 +2163,8 @@ export interface Config { Source: [`packages/subagent/subagent-dsh-sdk/src/index.ts:29`](../packages/subagent/subagent-dsh-sdk/src/index.ts) + + ## `@deepseek-ai/dsh-subagent-fork-in-process` Requires: `subagents` @@ -2039,6 +2179,8 @@ export interface Config { Source: [`packages/subagent/subagent-fork-in-process/src/index.ts:31`](../packages/subagent/subagent-fork-in-process/src/index.ts) + + ## `@deepseek-ai/dsh-subagent-spawn-in-process` Requires: `subagents` @@ -2053,6 +2195,8 @@ export interface Config { Source: [`packages/subagent/subagent-spawn-in-process/src/index.ts:25`](../packages/subagent/subagent-spawn-in-process/src/index.ts) + + ## `@deepseek-ai/dsh-subprocess-e2b` Requires: `e2b` @@ -2067,6 +2211,8 @@ export interface Config { Source: [`packages/e2b/subprocess-e2b/src/index.ts:25`](../packages/e2b/subprocess-e2b/src/index.ts) + + ## `@deepseek-ai/dsh-system-prompt` ```ts config-catalog @@ -2092,6 +2238,8 @@ export interface Config { Source: [`packages/core/system-prompt/src/index.ts:186`](../packages/core/system-prompt/src/index.ts) + + ## `@deepseek-ai/dsh-terminal-bash` Requires: `terminals` · `sandboxPolicy` · `subprocess` @@ -2135,6 +2283,8 @@ export interface Config { Source: [`packages/terminal/terminal-bash/src/config.ts:6`](../packages/terminal/terminal-bash/src/config.ts) + + ## `@deepseek-ai/dsh-time-context` Requires: `agents` @@ -2151,6 +2301,8 @@ export interface Config { Source: [`packages/context/time-context/src/index.ts:27`](../packages/context/time-context/src/index.ts) + + ## `@deepseek-ai/dsh-tmux-context` Requires: `agents` @@ -2165,6 +2317,8 @@ export interface Config { Source: [`packages/context/tmux-context/src/index.ts:34`](../packages/context/tmux-context/src/index.ts) + + ## `@deepseek-ai/dsh-token-meter` ```ts config-catalog @@ -2174,6 +2328,8 @@ export type TokenMeterConfig = Record Source: [`packages/llm/token-meter/src/types.ts:12`](../packages/llm/token-meter/src/types.ts) + + ## `@deepseek-ai/dsh-tool-bash` Requires: `tools` · `shell` · `systemPrompt` · `shellEnv` @@ -2188,6 +2344,8 @@ export interface Config { Source: [`packages/shell/tool-bash/src/index.ts:34`](../packages/shell/tool-bash/src/index.ts) + + ## `@deepseek-ai/dsh-tool-bash-persistent` Requires: `tools` · `terminals` @@ -2208,6 +2366,8 @@ export interface Config { Source: [`packages/shell/tool-bash-persistent/src/index.ts:405`](../packages/shell/tool-bash-persistent/src/index.ts) + + ## `@deepseek-ai/dsh-tool-fs` Requires: `tools` · `fs` · `systemPrompt` @@ -2228,6 +2388,8 @@ export interface Config { Source: [`packages/fs/tool-fs/src/index.ts:25`](../packages/fs/tool-fs/src/index.ts) + + ## `@deepseek-ai/dsh-tool-fs-search` Requires: `tools` · `systemPrompt` · `subprocess` @@ -2261,6 +2423,8 @@ export interface Config { Source: [`packages/fs/tool-fs-search/src/index.ts:73`](../packages/fs/tool-fs-search/src/index.ts) + + ## `@deepseek-ai/dsh-tool-goal` Requires: `agents` · `goals` · `tools` · `systemPrompt` @@ -2275,6 +2439,8 @@ export interface Config { Source: [`packages/goal/tool-goal/src/index.ts:26`](../packages/goal/tool-goal/src/index.ts) + + ## `@deepseek-ai/dsh-tool-jobs` Requires: `tools` · `jobs` · `systemPrompt` @@ -2307,6 +2473,8 @@ export type CompletionDelivery = 'quiet' | 'wakeup' Source: [`packages/jobs/tool-jobs/src/index.ts:32`](../packages/jobs/tool-jobs/src/index.ts) + + ## `@deepseek-ai/dsh-tool-lsp` Requires: `tools` · `lsp` · `systemPrompt` @@ -2325,6 +2493,8 @@ export interface Config { Source: [`packages/lsp/tool-lsp/src/index.ts:58`](../packages/lsp/tool-lsp/src/index.ts) + + ## `@deepseek-ai/dsh-tool-pwsh` Requires: `tools` · `shell` · `systemPrompt` · `shellEnv` @@ -2339,6 +2509,8 @@ export interface Config { Source: [`packages/shell/tool-pwsh/src/index.ts:52`](../packages/shell/tool-pwsh/src/index.ts) + + ## `@deepseek-ai/dsh-tool-ralph` Requires: `tools` · `workflowEngine` · `subagents` · `systemPrompt` @@ -2359,6 +2531,8 @@ export interface Config { Source: [`packages/workflow/tool-ralph/src/index.ts:23`](../packages/workflow/tool-ralph/src/index.ts) + + ## `@deepseek-ai/dsh-tool-session-query` Requires: `tools` · `systemPrompt` · `sessionQuery` @@ -2375,6 +2549,8 @@ export interface Config { Source: [`packages/session-query/tool-session-query/src/index.ts:29`](../packages/session-query/tool-session-query/src/index.ts) + + ## `@deepseek-ai/dsh-tool-skill` Requires: `agents` · `tools` · `skills` @@ -2389,6 +2565,8 @@ export interface Config { Source: [`packages/skill/tool-skill/src/index.ts:61`](../packages/skill/tool-skill/src/index.ts) + + ## `@deepseek-ai/dsh-tool-str-replace-editor` Requires: `tools` · `fs` @@ -2405,6 +2583,8 @@ export interface Config { Source: [`packages/fs/tool-str-replace-editor/src/index.ts:497`](../packages/fs/tool-str-replace-editor/src/index.ts) + + ## `@deepseek-ai/dsh-tool-subagent` Requires: `tools` · `subagents` · `systemPrompt` @@ -2468,6 +2648,8 @@ Depends on: [`AgentOptions`](subsystems/core.md) Source: [`packages/subagent/tool-subagent/src/index.ts:29`](../packages/subagent/tool-subagent/src/index.ts) + + ## `@deepseek-ai/dsh-tool-subagent-report` Requires: `subagents` · `tools` · `systemPrompt` @@ -2488,6 +2670,8 @@ Depends on: [`SubagentReportDelivery`](subsystems/subagent.md) Source: [`packages/subagent/tool-subagent-report/src/index.ts:27`](../packages/subagent/tool-subagent-report/src/index.ts) + + ## `@deepseek-ai/dsh-tool-terminal` Requires: `terminals` · `tools` · `systemPrompt` @@ -2504,6 +2688,8 @@ export interface Config { Source: [`packages/terminal/tool-terminal/src/index.ts:35`](../packages/terminal/tool-terminal/src/index.ts) + + ## `@deepseek-ai/dsh-tool-todo` Requires: `tools` @@ -2524,6 +2710,8 @@ export interface Config { Source: [`packages/todo/tool-todo/src/index.ts:29`](../packages/todo/tool-todo/src/index.ts) + + ## `@deepseek-ai/dsh-tool-web` Requires: `tools` · `web` · `systemPrompt` @@ -2548,6 +2736,8 @@ export interface Config { Source: [`packages/web/tool-web/src/index.ts:37`](../packages/web/tool-web/src/index.ts) + + ## `@deepseek-ai/dsh-tool-workflow` Requires: `tools` · `workflowEngine` · `systemPrompt` @@ -2564,6 +2754,8 @@ export interface Config { Source: [`packages/workflow/tool-workflow/src/index.ts:33`](../packages/workflow/tool-workflow/src/index.ts) + + ## `@deepseek-ai/dsh-tools` Requires: `systemPrompt` @@ -2598,6 +2790,8 @@ export type ToolPresentationMode = 'native' | 'code' | 'both' Source: [`packages/core/tools/src/index.ts:654`](../packages/core/tools/src/index.ts) + + ## `@deepseek-ai/dsh-typert-loader` Requires: `typert` · `loader` @@ -2612,6 +2806,8 @@ export interface Config { Source: [`packages/typert/loader/src/index.ts:47`](../packages/typert/loader/src/index.ts) + + ## `@deepseek-ai/dsh-user-approval` ```ts config-catalog @@ -2641,6 +2837,8 @@ export type ApprovalPolicy = 'ask' | 'never' Source: [`packages/interaction/user-approval/src/index.ts:177`](../packages/interaction/user-approval/src/index.ts) + + ## `@deepseek-ai/dsh-web` ```ts config-catalog @@ -2660,6 +2858,8 @@ export interface WebRuntimeConfig { Source: [`packages/web/web/src/index.ts:55`](../packages/web/web/src/index.ts) + + ## `@deepseek-ai/dsh-web-app` Requires: `webServer` @@ -2683,6 +2883,8 @@ export interface Config { Source: [`packages/bundle/web-app/src/index.ts:38`](../packages/bundle/web-app/src/index.ts) + + ## `@deepseek-ai/dsh-web-fetch-http` Requires: `web` @@ -2707,6 +2909,8 @@ export interface Config { Source: [`packages/web/web-fetch-http/src/index.ts:34`](../packages/web/web-fetch-http/src/index.ts) + + ## `@deepseek-ai/dsh-web-search-deepseek` Requires: `web` @@ -2733,6 +2937,8 @@ export interface Config { Source: [`packages/web/web-search-deepseek/src/index.ts:46`](../packages/web/web-search-deepseek/src/index.ts) + + ## `@deepseek-ai/dsh-web-search-exa` Requires: `web` @@ -2755,6 +2961,8 @@ export interface Config { Source: [`packages/web/web-search-exa/src/index.ts:38`](../packages/web/web-search-exa/src/index.ts) + + ## `@deepseek-ai/dsh-web-search-perplexity` Requires: `web` @@ -2777,6 +2985,8 @@ export interface Config { Source: [`packages/web/web-search-perplexity/src/index.ts:32`](../packages/web/web-search-perplexity/src/index.ts) + + ## `@deepseek-ai/dsh-workflow-worker-thread` Requires: `subagents` diff --git a/docs/config-catalog.zh.md b/docs/config-catalog.zh.md index fda208e8fc..dc20cd90e5 100644 --- a/docs/config-catalog.zh.md +++ b/docs/config-catalog.zh.md @@ -11,6 +11,8 @@ `Requires:` 行列出插件通过 `inject` 注入的服务键:其 `cordis.yml` 树还必须加载这些服务的提供者。范围限定为 harness 层级(`packages/`);配置树还可能加载的 vendored cordis 插件(`hmr`、控制台日志记录器等)固定为上游源代码(参见 [vendoring policy](../vendor/README.md)),未收录于此目录。 + + ## `@deepseek-ai/dsh-acp` 需要:`agents` @@ -31,6 +33,8 @@ export interface AcpConfig { 来源:[`packages/acp/acp/src/index.ts:70`](../packages/acp/acp/src/index.ts) + + ## `@deepseek-ai/dsh-acp-demo` ```ts config-catalog @@ -84,6 +88,8 @@ export interface Config { 来源:[`packages/examples/acp-demo/src/index.ts:39`](../packages/examples/acp-demo/src/index.ts) + + ## `@deepseek-ai/dsh-agent-default-model` ```ts config-catalog @@ -98,6 +104,8 @@ export interface Config { 来源:[`packages/core/agent-default-model/src/index.ts:41`](../packages/core/agent-default-model/src/index.ts) + + ## `@deepseek-ai/dsh-agent-instructions` ```ts config-catalog @@ -126,6 +134,8 @@ export interface Config { 来源:[`packages/context/agent-instructions/src/config.ts:18`](../packages/context/agent-instructions/src/config.ts) + + ## `@deepseek-ai/dsh-agent-loop` 需要:`agents` · `sessions` · `llm` · `tools` · `systemPrompt` @@ -156,6 +166,8 @@ export interface Config { 来源:[`packages/core/agent-loop/src/index.ts:255`](../packages/core/agent-loop/src/index.ts) + + ## `@deepseek-ai/dsh-agent-presets` 需要:`loader` @@ -192,6 +204,8 @@ export type PresetTrust = 'system' | 'user' 来源:[`packages/preset/agent-presets/src/preset.ts:52`](../packages/preset/agent-presets/src/preset.ts) + + ## `@deepseek-ai/dsh-agent-spine-demo` ```ts config-catalog @@ -282,6 +296,8 @@ export interface GoalConfig { 来源:[`packages/examples/agent-spine-demo/src/index.ts:92`](../packages/examples/agent-spine-demo/src/index.ts) + + ## `@deepseek-ai/dsh-agent-tool-presentation` 需要:`tools` @@ -304,6 +320,8 @@ export interface Config { 来源:[`packages/core/agent-tool-presentation/src/index.ts:38`](../packages/core/agent-tool-presentation/src/index.ts) + + ## `@deepseek-ai/dsh-attachment-local` ```ts config-catalog @@ -324,6 +342,8 @@ export interface Config { 来源:[`packages/attachment/attachment-local/src/index.ts:24`](../packages/attachment/attachment-local/src/index.ts) + + ## `@deepseek-ai/dsh-bash-local` 需要:`subprocess` @@ -348,6 +368,8 @@ export interface Config { 来源:[`packages/shell/bash-local/src/index.ts:41`](../packages/shell/bash-local/src/index.ts) + + ## `@deepseek-ai/dsh-bash-sandbox` 需要:`subprocess` · `sandbox` · `sandboxPolicy` @@ -367,6 +389,8 @@ export type Config = LocalConfig 来源:[`packages/shell/bash-sandbox/src/index.ts:35`](../packages/shell/bash-sandbox/src/index.ts) + + ## `@deepseek-ai/dsh-client-connection` 需要:`webServer` @@ -390,6 +414,8 @@ export interface ConnectionConfig { 来源:[`packages/client/connection/src/index.ts:50`](../packages/client/connection/src/index.ts) + + ## `@deepseek-ai/dsh-client-hmr` 需要:`clientModuleHost` · `webServer` @@ -404,6 +430,8 @@ export interface Config { 来源:[`packages/client/hmr/src/index.ts:31`](../packages/client/hmr/src/index.ts) + + ## `@deepseek-ai/dsh-code-runtime-worker-thread` ```ts config-catalog @@ -439,6 +467,8 @@ export interface Config { 来源:[`packages/code-runtime/code-runtime-worker-thread/src/index.ts:25`](../packages/code-runtime/code-runtime-worker-thread/src/index.ts) + + ## `@deepseek-ai/dsh-compaction-basic` 需要:`llm` · `tokenMeter` · `sessions` @@ -483,6 +513,8 @@ export interface ModelCompactPolicyConfig extends CompactionPolicyConfig { 来源:[`packages/compaction/compaction-basic/src/types.ts:38`](../packages/compaction/compaction-basic/src/types.ts) + + ## `@deepseek-ai/dsh-compaction-tool-result-pruner` 需要:`tokenMeter` @@ -501,6 +533,8 @@ export interface ToolResultPruneConfig { 来源:[`packages/compaction/compaction-tool-result-pruner/src/types.ts:4`](../packages/compaction/compaction-tool-result-pruner/src/types.ts) + + ## `@deepseek-ai/dsh-cordis-host-runner` 需要:`tools` @@ -515,6 +549,8 @@ export interface Config { 来源:[`packages/extensions/cordis-host-runner/src/index.ts:88`](../packages/extensions/cordis-host-runner/src/index.ts) + + ## `@deepseek-ai/dsh-credentials-local` ```ts config-catalog @@ -533,6 +569,8 @@ export interface Config { 来源:[`packages/credentials/credentials-local/src/index.ts:55`](../packages/credentials/credentials-local/src/index.ts) + + ## `@deepseek-ai/dsh-e2b` ```ts config-catalog @@ -549,6 +587,8 @@ export interface Config { 来源:[`packages/e2b/e2b/src/index.ts:43`](../packages/e2b/e2b/src/index.ts) + + ## `@deepseek-ai/dsh-fs-local` ```ts config-catalog @@ -566,6 +606,8 @@ export interface Config { 来源:[`packages/fs/fs-local/src/index.ts:41`](../packages/fs/fs-local/src/index.ts) + + ## `@deepseek-ai/dsh-fs-sandbox` 需要:`sandboxPolicy` @@ -584,6 +626,8 @@ export type Config = LocalConfig 来源:[`packages/fs/fs-sandbox/src/index.ts:49`](../packages/fs/fs-sandbox/src/index.ts) + + ## `@deepseek-ai/dsh-goal` 需要:`agents` @@ -598,6 +642,8 @@ export interface Config { 来源:[`packages/goal/goal/src/index.ts:116`](../packages/goal/goal/src/index.ts) + + ## `@deepseek-ai/dsh-headless` 需要:`agentDefaultModel` · `agents` · `sessions` @@ -612,6 +658,8 @@ export interface Config { 来源:[`packages/bundle/headless/src/index.ts:31`](../packages/bundle/headless/src/index.ts) + + ## `@deepseek-ai/dsh-hooks-claude-code` 需要:`bash` @@ -648,6 +696,8 @@ export interface Config { 来源:[`packages/hooks/hooks-claude-code/src/index.ts:45`](../packages/hooks/hooks-claude-code/src/index.ts) + + ## `@deepseek-ai/dsh-hooks-codex` 需要:`bash` @@ -673,6 +723,8 @@ export interface Config { 来源:[`packages/hooks/hooks-codex/src/index.ts:44`](../packages/hooks/hooks-codex/src/index.ts) + + ## `@deepseek-ai/dsh-host-apiproxy` 需要:`agentDefaultModel` · `agents` · `attachments` · `directoryPicker` · `llm` · `sessions` · `subagents` · `sessionQuery` · `tools` · `userInteraction` · `workspace` @@ -699,6 +751,8 @@ export interface Config { 来源:[`packages/host/apiproxy/src/index.ts:41`](../packages/host/apiproxy/src/index.ts) + + ## `@deepseek-ai/dsh-host-directory-picker-browse` ```ts config-catalog @@ -711,6 +765,8 @@ export interface Config { 来源:[`packages/host/directory-picker-browse/src/index.ts:181`](../packages/host/directory-picker-browse/src/index.ts) + + ## `@deepseek-ai/dsh-host-frontend-static` 需要:`webServer` @@ -725,6 +781,8 @@ export interface Config { 来源:[`packages/host/frontend-static/src/index.ts:28`](../packages/host/frontend-static/src/index.ts) + + ## `@deepseek-ai/dsh-host-webserver` ```ts config-catalog @@ -739,6 +797,8 @@ export interface Config { 来源:[`packages/host/webserver/src/index.ts:45`](../packages/host/webserver/src/index.ts) + + ## `@deepseek-ai/dsh-invariants` ```ts config-catalog @@ -755,6 +815,8 @@ export interface Config { 来源:[`packages/runtime-diagnostics/invariants/src/index.ts:15`](../packages/runtime-diagnostics/invariants/src/index.ts) + + ## `@deepseek-ai/dsh-jobs-local` ```ts config-catalog @@ -770,6 +832,8 @@ export interface Config { 来源:[`packages/jobs/jobs-local/src/index.ts:31`](../packages/jobs/jobs-local/src/index.ts) + + ## `@deepseek-ai/dsh-llm-deepseek` 需要:`llm` @@ -823,6 +887,8 @@ export interface DeepSeekCatalogModel { 来源:[`packages/llm/llm-deepseek/src/index.ts:62`](../packages/llm/llm-deepseek/src/index.ts) + + ## `@deepseek-ai/dsh-llm-pi-ai` 需要:`llm` @@ -1011,6 +1077,8 @@ type WithheldThinkingFormat = 'chat-template' | 'qwen-chat-template' 来源:[`packages/llm/llm-pi-ai/src/config.ts:172`](../packages/llm/llm-pi-ai/src/config.ts) + + ## `@deepseek-ai/dsh-llm-replay` 需要:`llm` @@ -1077,6 +1145,8 @@ export interface ReplayModelConfig { 来源:[`packages/test-support/llm-replay/src/index.ts:776`](../packages/test-support/llm-replay/src/index.ts) + + ## `@deepseek-ai/dsh-llm-retry` 需要:`agents` @@ -1088,6 +1158,8 @@ export type Config = Readonly> 来源:[`packages/llm/llm-retry/src/index.ts:24`](../packages/llm/llm-retry/src/index.ts) + + ## `@deepseek-ai/dsh-lsp-stdio` 需要:`fs` · `lsp` · `subprocess` @@ -1128,6 +1200,8 @@ export interface LspLocalServerConfig { 来源:[`packages/lsp/lsp-stdio/src/index.ts:82`](../packages/lsp/lsp-stdio/src/index.ts) + + ## `@deepseek-ai/dsh-mcp-client` 需要:`tools` @@ -1199,6 +1273,8 @@ export interface ReconnectConfig { 来源:[`packages/mcp/mcp-client/src/index.ts:98`](../packages/mcp/mcp-client/src/index.ts) + + ## `@deepseek-ai/dsh-message-feedback` 需要:`storageDomain` · `sessionPersistence` · `sessions` @@ -1213,6 +1289,8 @@ export interface Config { 来源:[`packages/feedback/message-feedback/src/index.ts:49`](../packages/feedback/message-feedback/src/index.ts) + + ## `@deepseek-ai/dsh-permission-presets` 需要:`bash` · `approval` · `sessions` @@ -1250,6 +1328,8 @@ export interface PresetSpec { 来源:[`packages/interaction/permission-presets/src/index.ts:140`](../packages/interaction/permission-presets/src/index.ts) + + ## `@deepseek-ai/dsh-persona` 需要:`systemPrompt` @@ -1272,6 +1352,8 @@ export interface Config { 来源:[`packages/preset/persona/src/index.ts:34`](../packages/preset/persona/src/index.ts) + + ## `@deepseek-ai/dsh-plan-mode` 需要:`tools` · `systemPrompt` @@ -1286,6 +1368,8 @@ export interface PlanModeConfig { 来源:[`packages/plan/plan-mode/src/index.ts:70`](../packages/plan/plan-mode/src/index.ts) + + ## `@deepseek-ai/dsh-pwsh-local` 需要:`subprocess` @@ -1317,6 +1401,8 @@ export interface Config { 来源:[`packages/shell/pwsh-local/src/index.ts:58`](../packages/shell/pwsh-local/src/index.ts) + + ## `@deepseek-ai/dsh-pwsh-sandbox` 需要:`subprocess` · `sandbox` · `sandboxPolicy` @@ -1337,6 +1423,8 @@ export type Config = LocalConfig 来源:[`packages/shell/pwsh-sandbox/src/index.ts:40`](../packages/shell/pwsh-sandbox/src/index.ts) + + ## `@deepseek-ai/dsh-repeat-tool-reminder` ```ts config-catalog @@ -1369,6 +1457,8 @@ export interface Config { 来源:[`packages/guard/repeat-tool-reminder/src/index.ts:28`](../packages/guard/repeat-tool-reminder/src/index.ts) + + ## `@deepseek-ai/dsh-sandbox-local` ```ts config-catalog @@ -1399,6 +1489,8 @@ export interface Config { 来源:[`packages/sandbox/sandbox-local/src/index.ts:44`](../packages/sandbox/sandbox-local/src/index.ts) + + ## `@deepseek-ai/dsh-sandbox-policy` ```ts config-catalog @@ -1424,6 +1516,8 @@ export interface Config { 来源:[`packages/sandbox/sandbox-policy/src/index.ts:67`](../packages/sandbox/sandbox-policy/src/index.ts) + + ## `@deepseek-ai/dsh-sdk-jsonrpc-server` 需要:`agents` @@ -1446,6 +1540,8 @@ export interface JsonRpcConfig { 来源:[`packages/sdk/server/src/index.ts:29`](../packages/sdk/server/src/index.ts) + + ## `@deepseek-ai/dsh-session-persistence-jsonl` 需要:`sessions` @@ -1483,6 +1579,8 @@ export type JsonlCompression = 'zstd' | 'none' 来源:[`packages/session/session-persistence-jsonl/src/index.ts:60`](../packages/session/session-persistence-jsonl/src/index.ts) + + ## `@deepseek-ai/dsh-session-persistence-sqlite` 需要:`sessions` @@ -1526,6 +1624,8 @@ export type JournalMode = 'wal' | 'delete' | 'truncate' | 'persist' 来源:[`packages/session/session-persistence-sqlite/src/index.ts:70`](../packages/session/session-persistence-sqlite/src/index.ts) + + ## `@deepseek-ai/dsh-session-projection-cache` 需要:`storageDomain` · `sessionProjections` · `sessionPersistence` · `sessions` @@ -1547,6 +1647,8 @@ export interface Config { 来源:[`packages/session/session-projection-cache/src/index.ts:42`](../packages/session/session-projection-cache/src/index.ts) + + ## `@deepseek-ai/dsh-session-query-sqlite` 需要:`sessions` @@ -1591,6 +1693,8 @@ export type JournalMode = 'wal' | 'delete' | 'truncate' | 'persist' 来源:[`packages/session-query/session-query-sqlite/src/index.ts:89`](../packages/session-query/session-query-sqlite/src/index.ts) + + ## `@deepseek-ai/dsh-session-reference` 需要:`sessionQuery` @@ -1609,6 +1713,8 @@ export interface Config { 来源:[`packages/context/session-reference/src/config.ts:11`](../packages/context/session-reference/src/config.ts) + + ## `@deepseek-ai/dsh-session-telemetry-otel` 需要:`sessions` @@ -1653,6 +1759,8 @@ export enum SessionTelemetryMode { 来源:[`packages/session/session-telemetry-otel/src/index.ts:91`](../packages/session/session-telemetry-otel/src/index.ts) + + ## `@deepseek-ai/dsh-session-title` 需要:`sessions` @@ -1671,6 +1779,8 @@ export interface Config { 来源:[`packages/session/session-title/src/index.ts:79`](../packages/session/session-title/src/index.ts) + + ## `@deepseek-ai/dsh-session-title-all-prompts-llm` 需要:`sessionTitle` · `llm` · `sessions` @@ -1684,6 +1794,8 @@ export type Config = SessionTitleLlmConfig 来源:[`packages/session/session-title-all-prompts-llm/src/index.ts:15`](../packages/session/session-title-all-prompts-llm/src/index.ts) + + ## `@deepseek-ai/dsh-session-title-first-prompt-llm` 需要:`sessionTitle` · `llm` · `sessions` @@ -1697,6 +1809,8 @@ export type Config = SessionTitleLlmConfig 来源:[`packages/session/session-title-first-prompt-llm/src/index.ts:15`](../packages/session/session-title-first-prompt-llm/src/index.ts) + + ## `@deepseek-ai/dsh-settings-file` ```ts config-catalog @@ -1715,6 +1829,8 @@ export interface Config { 来源:[`packages/settings/settings-file/src/index.ts:21`](../packages/settings/settings-file/src/index.ts) + + ## `@deepseek-ai/dsh-shell-env` ```ts config-catalog @@ -1727,6 +1843,8 @@ export interface Config { 来源:[`packages/shell/shell-env/src/index.ts:29`](../packages/shell/shell-env/src/index.ts) + + ## `@deepseek-ai/dsh-skill` ```ts config-catalog @@ -1739,6 +1857,8 @@ export interface Config { 来源:[`packages/skill/skill/src/index.ts:279`](../packages/skill/skill/src/index.ts) + + ## `@deepseek-ai/dsh-skill-filesystem` 需要:`skills` @@ -1775,6 +1895,8 @@ export interface Config { 来源:[`packages/skill/skill-filesystem/src/index.ts:49`](../packages/skill/skill-filesystem/src/index.ts) + + ## `@deepseek-ai/dsh-spill-local` ```ts config-catalog @@ -1791,6 +1913,8 @@ export interface Config { 来源:[`packages/spill/spill-local/src/index.ts:22`](../packages/spill/spill-local/src/index.ts) + + ## `@deepseek-ai/dsh-spill-policy` 需要:`tools` @@ -1809,6 +1933,8 @@ export interface Config { 来源:[`packages/spill/spill-policy/src/index.ts:60`](../packages/spill/spill-policy/src/index.ts) + + ## `@deepseek-ai/dsh-storage-domain` 需要:`storage` @@ -1830,6 +1956,8 @@ export interface Config { 来源:[`packages/storage/storage-domain/src/index.ts:52`](../packages/storage/storage-domain/src/index.ts) + + ## `@deepseek-ai/dsh-storage-json` 需要:`storage` @@ -1849,6 +1977,8 @@ export interface Config { 来源:[`packages/storage/storage-json/src/index.ts:27`](../packages/storage/storage-json/src/index.ts) + + ## `@deepseek-ai/dsh-storage-sqlite` 需要:`storage` @@ -1887,6 +2017,8 @@ export type JournalMode = 'wal' | 'delete' | 'truncate' | 'persist' 来源:[`packages/storage/storage-sqlite/src/index.ts:24`](../packages/storage/storage-sqlite/src/index.ts) + + ## `@deepseek-ai/dsh-subagent-acp` 需要:`subagents` · `subprocess` @@ -1938,6 +2070,8 @@ export type PermissionPolicy = 'allow' | 'reject' 来源:[`packages/subagent/subagent-acp/src/index.ts:27`](../packages/subagent/subagent-acp/src/index.ts) + + ## `@deepseek-ai/dsh-subagent-claude-code` 需要:`subagents` · `subprocess` @@ -1957,6 +2091,8 @@ export interface Config { 来源:[`packages/subagent/subagent-claude-code/src/index.ts:32`](../packages/subagent/subagent-claude-code/src/index.ts) + + ## `@deepseek-ai/dsh-subagent-codex` 需要:`subagents` · `subprocess` @@ -1976,6 +2112,8 @@ export interface Config { 来源:[`packages/subagent/subagent-codex/src/index.ts:30`](../packages/subagent/subagent-codex/src/index.ts) + + ## `@deepseek-ai/dsh-subagent-dsh-sdk` 需要:`subagents` @@ -2027,6 +2165,8 @@ export interface Config { 来源:[`packages/subagent/subagent-dsh-sdk/src/index.ts:29`](../packages/subagent/subagent-dsh-sdk/src/index.ts) + + ## `@deepseek-ai/dsh-subagent-fork-in-process` 需要:`subagents` @@ -2041,6 +2181,8 @@ export interface Config { 来源:[`packages/subagent/subagent-fork-in-process/src/index.ts:31`](../packages/subagent/subagent-fork-in-process/src/index.ts) + + ## `@deepseek-ai/dsh-subagent-spawn-in-process` 需要:`subagents` @@ -2055,6 +2197,8 @@ export interface Config { 来源:[`packages/subagent/subagent-spawn-in-process/src/index.ts:25`](../packages/subagent/subagent-spawn-in-process/src/index.ts) + + ## `@deepseek-ai/dsh-subprocess-e2b` 需要:`e2b` @@ -2069,6 +2213,8 @@ export interface Config { 来源:[`packages/e2b/subprocess-e2b/src/index.ts:25`](../packages/e2b/subprocess-e2b/src/index.ts) + + ## `@deepseek-ai/dsh-system-prompt` ```ts config-catalog @@ -2094,6 +2240,8 @@ export interface Config { 来源:[`packages/core/system-prompt/src/index.ts:186`](../packages/core/system-prompt/src/index.ts) + + ## `@deepseek-ai/dsh-terminal-bash` 需要:`pty` · `sandboxPolicy` · `subprocess` @@ -2137,6 +2285,8 @@ export interface Config { 来源:[`packages/terminal/terminal-bash/src/config.ts:6`](../packages/terminal/terminal-bash/src/config.ts) + + ## `@deepseek-ai/dsh-time-context` 需要:`agents` @@ -2153,6 +2303,8 @@ export interface Config { 来源:[`packages/context/time-context/src/index.ts:27`](../packages/context/time-context/src/index.ts) + + ## `@deepseek-ai/dsh-tmux-context` 需要:`agents` @@ -2167,6 +2319,8 @@ export interface Config { 来源:[`packages/context/tmux-context/src/index.ts:34`](../packages/context/tmux-context/src/index.ts) + + ## `@deepseek-ai/dsh-token-meter` ```ts config-catalog @@ -2176,6 +2330,8 @@ export type TokenMeterConfig = Record 来源:[`packages/llm/token-meter/src/types.ts:12`](../packages/llm/token-meter/src/types.ts) + + ## `@deepseek-ai/dsh-tool-bash` 需要:`tools` · `bash` · `systemPrompt` · `bashEnv` @@ -2190,6 +2346,8 @@ export interface Config { 来源:[`packages/shell/tool-bash/src/index.ts:34`](../packages/shell/tool-bash/src/index.ts) + + ## `@deepseek-ai/dsh-tool-bash-persistent` 需要:`tools` · `pty` @@ -2210,6 +2368,8 @@ export interface Config { 来源:[`packages/shell/tool-bash-persistent/src/index.ts:405`](../packages/shell/tool-bash-persistent/src/index.ts) + + ## `@deepseek-ai/dsh-tool-fs` 需要:`tools` · `fs` · `systemPrompt` @@ -2230,6 +2390,8 @@ export interface Config { 来源:[`packages/fs/tool-fs/src/index.ts:25`](../packages/fs/tool-fs/src/index.ts) + + ## `@deepseek-ai/dsh-tool-fs-search` 需要:`tools` · `systemPrompt` · `subprocess` @@ -2263,6 +2425,8 @@ export interface Config { 来源:[`packages/fs/tool-fs-search/src/index.ts:73`](../packages/fs/tool-fs-search/src/index.ts) + + ## `@deepseek-ai/dsh-tool-goal` 需要:`agents` · `goals` · `tools` · `systemPrompt` @@ -2277,6 +2441,8 @@ export interface Config { 来源:[`packages/goal/tool-goal/src/index.ts:26`](../packages/goal/tool-goal/src/index.ts) + + ## `@deepseek-ai/dsh-tool-jobs` 需要:`tools` · `tasks` · `systemPrompt` @@ -2309,6 +2475,8 @@ export type CompletionDelivery = 'quiet' | 'wakeup' 来源:[`packages/jobs/tool-jobs/src/index.ts:32`](../packages/jobs/tool-jobs/src/index.ts) + + ## `@deepseek-ai/dsh-tool-lsp` 需要:`tools` · `lsp` · `systemPrompt` @@ -2327,6 +2495,8 @@ export interface Config { 来源:[`packages/lsp/tool-lsp/src/index.ts:58`](../packages/lsp/tool-lsp/src/index.ts) + + ## `@deepseek-ai/dsh-tool-pwsh` 需要:`tools` · `bash` · `systemPrompt` · `bashEnv` @@ -2341,6 +2511,8 @@ export interface Config { 来源:[`packages/shell/tool-pwsh/src/index.ts:52`](../packages/shell/tool-pwsh/src/index.ts) + + ## `@deepseek-ai/dsh-tool-ralph` 需要:`tools` · `workflows` · `subagents` · `systemPrompt` @@ -2361,6 +2533,8 @@ export interface Config { 来源:[`packages/workflow/tool-ralph/src/index.ts:23`](../packages/workflow/tool-ralph/src/index.ts) + + ## `@deepseek-ai/dsh-tool-session-query` 需要:`tools` · `systemPrompt` · `sessionQuery` @@ -2377,6 +2551,8 @@ export interface Config { 来源:[`packages/session-query/tool-session-query/src/index.ts:29`](../packages/session-query/tool-session-query/src/index.ts) + + ## `@deepseek-ai/dsh-tool-skill` 需要:`agents` · `tools` · `skills` @@ -2391,6 +2567,8 @@ export interface Config { 来源:[`packages/skill/tool-skill/src/index.ts:61`](../packages/skill/tool-skill/src/index.ts) + + ## `@deepseek-ai/dsh-tool-str-replace-editor` 需要:`tools` · `fs` @@ -2407,6 +2585,8 @@ export interface Config { 来源:[`packages/fs/tool-str-replace-editor/src/index.ts:497`](../packages/fs/tool-str-replace-editor/src/index.ts) + + ## `@deepseek-ai/dsh-tool-subagent` 需要:`tools` · `subagents` · `systemPrompt` @@ -2470,6 +2650,8 @@ export interface Config { 来源:[`packages/subagent/tool-subagent/src/index.ts:29`](../packages/subagent/tool-subagent/src/index.ts) + + ## `@deepseek-ai/dsh-tool-subagent-report` 需要:`subagents` · `tools` · `systemPrompt` @@ -2490,6 +2672,8 @@ export interface Config { 来源:[`packages/subagent/tool-subagent-report/src/index.ts:27`](../packages/subagent/tool-subagent-report/src/index.ts) + + ## `@deepseek-ai/dsh-tool-terminal` 需要:`pty` · `tools` · `systemPrompt` @@ -2506,6 +2690,8 @@ export interface Config { 来源:[`packages/terminal/tool-terminal/src/index.ts:35`](../packages/terminal/tool-terminal/src/index.ts) + + ## `@deepseek-ai/dsh-tool-todo` 需要:`tools` @@ -2526,6 +2712,8 @@ export interface Config { 来源:[`packages/todo/tool-todo/src/index.ts:29`](../packages/todo/tool-todo/src/index.ts) + + ## `@deepseek-ai/dsh-tool-web` 需要:`tools` · `web` · `systemPrompt` @@ -2550,6 +2738,8 @@ export interface Config { 来源:[`packages/web/tool-web/src/index.ts:37`](../packages/web/tool-web/src/index.ts) + + ## `@deepseek-ai/dsh-tool-workflow` 需要:`tools` · `workflows` · `systemPrompt` @@ -2566,6 +2756,8 @@ export interface Config { 来源:[`packages/workflow/tool-workflow/src/index.ts:33`](../packages/workflow/tool-workflow/src/index.ts) + + ## `@deepseek-ai/dsh-tools` 需要:`systemPrompt` @@ -2600,6 +2792,8 @@ export type ToolPresentationMode = 'native' | 'code' | 'both' 来源:[`packages/core/tools/src/index.ts:654`](../packages/core/tools/src/index.ts) + + ## `@deepseek-ai/dsh-typert-loader` 需要:`typert` · `loader` @@ -2614,6 +2808,8 @@ export interface Config { 来源:[`packages/typert/loader/src/index.ts:47`](../packages/typert/loader/src/index.ts) + + ## `@deepseek-ai/dsh-user-approval` ```ts config-catalog @@ -2643,6 +2839,8 @@ export type ApprovalPolicy = 'ask' | 'never' 来源:[`packages/interaction/user-approval/src/index.ts:177`](../packages/interaction/user-approval/src/index.ts) + + ## `@deepseek-ai/dsh-web` ```ts config-catalog @@ -2662,6 +2860,8 @@ export interface WebRuntimeConfig { 来源:[`packages/web/web/src/index.ts:55`](../packages/web/web/src/index.ts) + + ## `@deepseek-ai/dsh-web-app` 需要:`webServer` @@ -2685,6 +2885,8 @@ export interface Config { 来源:[`packages/bundle/web-app/src/index.ts:38`](../packages/bundle/web-app/src/index.ts) + + ## `@deepseek-ai/dsh-web-fetch-http` 需要:`web` @@ -2709,6 +2911,8 @@ export interface Config { 来源:[`packages/web/web-fetch-http/src/index.ts:34`](../packages/web/web-fetch-http/src/index.ts) + + ## `@deepseek-ai/dsh-web-search-deepseek` 需要:`web` @@ -2735,6 +2939,8 @@ export interface Config { 来源:[`packages/web/web-search-deepseek/src/index.ts:46`](../packages/web/web-search-deepseek/src/index.ts) + + ## `@deepseek-ai/dsh-web-search-exa` 需要:`web` @@ -2757,6 +2963,8 @@ export interface Config { 来源:[`packages/web/web-search-exa/src/index.ts:38`](../packages/web/web-search-exa/src/index.ts) + + ## `@deepseek-ai/dsh-web-search-perplexity` 需要:`web` @@ -2779,6 +2987,8 @@ export interface Config { 来源:[`packages/web/web-search-perplexity/src/index.ts:32`](../packages/web/web-search-perplexity/src/index.ts) + + ## `@deepseek-ai/dsh-workflow-worker-thread` 需要:`subagents` diff --git a/docs/cookbook/adding-a-tool.i18n.yaml b/docs/cookbook/adding-a-tool.i18n.yaml index d7f08cc5af..b39b907128 100644 --- a/docs/cookbook/adding-a-tool.i18n.yaml +++ b/docs/cookbook/adding-a-tool.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/cookbook/adding-a-tool.md adding-a-tool.md: 37516521de4d00de964003fd6f877831774fdcd3 -adding-a-tool.zh.md: 9455fa3b8d8b87632724ad4b035f184cb0c17993 +adding-a-tool.zh.md: 27a90ce19653333a0a6afd989115e614a1c8f9f4 diff --git a/docs/cookbook/adding-a-tool.zh.md b/docs/cookbook/adding-a-tool.zh.md index 9455fa3b8d..27a90ce196 100644 --- a/docs/cookbook/adding-a-tool.zh.md +++ b/docs/cookbook/adding-a-tool.zh.md @@ -54,6 +54,8 @@ export function apply(ctx: Context) { producer 提供同步的 `cancel`、在资源清理后 settle 且不 reject 的 `done`,以及可选的消费式 `readOutput`(负责有界输出的格式化)。预先中止的调用属于失败,因为此时没有任务,其 id 无法满足成功输出 schema。`ctx.jobs.start()` 发布 id 后,应使用任务自有的取消信号,而不是 `exec.signal`:之后取消外层调用只会停止等待本次调用,不会终止已经发布的工作;该生命周期归 `job_kill`、owner dispose 和服务 teardown 所有。前台工作仍与 `exec.signal` 耦合。流式 producer 的示例和完整约定见[后台任务运行时 Agent Note](../../.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md)与 `dsh-tool-bash`。 + + ## 执行策略与观测 尽量不要把部署策略内建到工具中。使用 `tools/pre-execute` 实现可扩展的允许/拒绝/询问策略(见[权限门禁示例](extension-cookbook.md#a-hook-plugin-permission-gate-example));使用 `ctx.tools.guard()` 设置最终的单调拒绝,后续监听器无法撤销;使用 `tools/execute` 为分发添加截止时间、重试或指标收集;使用 `tools/post-execute` 替换展示内容或返回值、阻止结果,或附加模型可见上下文;使用 `tools/result` 观测不可变的归一化结果而不改变它。替换内容不会阻止程序化访问 `value`;保密策略会屏蔽或替换该值。沙箱实现也可以在工具的执行器实现中运行;[`dsh-tools` README](../../packages/core/tools/README.md#extension-points) 定义每个扩展点的输入、顺序、返回值和失败行为。 diff --git a/docs/cookbook/extension-cookbook.i18n.yaml b/docs/cookbook/extension-cookbook.i18n.yaml index a496c31981..d55871fa50 100644 --- a/docs/cookbook/extension-cookbook.i18n.yaml +++ b/docs/cookbook/extension-cookbook.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/cookbook/extension-cookbook.md extension-cookbook.md: 9618a3522c5566636fe3e49f7eca93d1e113d51a -extension-cookbook.zh.md: 7b82d7d1ff239cc2139ee424613e818669f3e2e8 +extension-cookbook.zh.md: 540bc6867016d095ccae0fb0fdc79bc1b1c26290 diff --git a/docs/cookbook/extension-cookbook.zh.md b/docs/cookbook/extension-cookbook.zh.md index 7b82d7d1ff..540bc68670 100644 --- a/docs/cookbook/extension-cookbook.zh.md +++ b/docs/cookbook/extension-cookbook.zh.md @@ -8,6 +8,8 @@ harness 扩展的参考模式。代码片段省略了 import 和辅助实现, 工具在 `ctx.tools` 上注册。带注解的 `defineTool` 示例(类型化的 `execute` 参数、结果构造、`run_in_background` 模式)见 [adding-a-tool.md](adding-a-tool.md)——该指南是工具定义的真源。`ctx.tools.register()` 也直接接受原始 JSON Schema `ToolDefinition`(MCP 来源的工具就是这样到达的);`defineTool` 是第一方工具使用的类型化辅助函数。 + + ## 钩子插件(以权限门禁为例) 这个权限门禁是钩子插件的一个示例。它从 `tools/pre-execute` 门禁返回一个类型化的决策,用于允许或拒绝一次调用;沙箱、权限和 plan-mode 插件都可以使用该扩展点。钩子插件也可以拦截其他扩展点,本身并不等同于权限门禁。「原生钩子」是在拦截点上运行的普通 Cordis 插件,不需要外部协议。 diff --git a/docs/cordis-primer.i18n.yaml b/docs/cordis-primer.i18n.yaml index cec0660aae..949846bee5 100644 --- a/docs/cordis-primer.i18n.yaml +++ b/docs/cordis-primer.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/cordis-primer.md cordis-primer.md: 2a3afe180623d89b006dfa3e73aba5567c15bbe9 -cordis-primer.zh.md: bdce14cf9f157959d6419f88c9c69570102b9c0c +cordis-primer.zh.md: d4d60f60717ffdc01499fdffadba2808557b285f diff --git a/docs/cordis-primer.zh.md b/docs/cordis-primer.zh.md index bdce14cf9f..d4d60f6071 100644 --- a/docs/cordis-primer.zh.md +++ b/docs/cordis-primer.zh.md @@ -12,6 +12,8 @@ Cordis 是 DeepSeek Harness 底层以 vendor 方式引入的插件框架。本 - **类型化事件用于通信。** 服务通过 TypeScript 声明合并注册事件名,然后以 `emit`、`waterfall`(瀑布式事件)、`parallel` 或 `serial` 方式分发,分别对应监听者观察、包装、并行扇出或按序执行。 - **注册是可逆的副作用。** 提示词片段、工具 schema、适配器、提供方和监听器通过 `ctx.effect()` 或 `ctx.on()` 安装,reload 和 teardown 时会按预期撤销。 + + ## 分发模式 每个事件具有以下分发模式之一,且只能通过对应方法分发。 diff --git a/docs/cordis-tutorial/03-services.i18n.yaml b/docs/cordis-tutorial/03-services.i18n.yaml index 372e028a47..80ea0f275f 100644 --- a/docs/cordis-tutorial/03-services.i18n.yaml +++ b/docs/cordis-tutorial/03-services.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/cordis-tutorial/03-services.md -03-services.md: 32007284be99ef46b4621089c9b3a80317e77189 -03-services.zh.md: d82be29aa69686b8dc10cc6a45a658683c017cbd +03-services.md: ebfc400dbbc701a3c164c7d30c371dec879d7d73 +03-services.zh.md: fcfd8be7f7fe654a4f4943cf591b5ab7bfc27fc6 diff --git a/docs/cordis-tutorial/03-services.md b/docs/cordis-tutorial/03-services.md index 32007284be..ebfc400dbb 100644 --- a/docs/cordis-tutorial/03-services.md +++ b/docs/cordis-tutorial/03-services.md @@ -75,7 +75,7 @@ Swap the two lines in `cordis.yml` and rerun: same output. Try removing `./greet `inject` is not a one-shot boot check. If a required service disappears while the app runs — its provider was unloaded or hot-replaced — every dependent plugin is unloaded too, and loads again when the service returns. Combined with effects ([chapter 2](02-lifecycle-and-effects.md)), this prevents a running consumer from retaining a reference to an unavailable service: its own registrations are unwound when the dependency disappears. -This is also why service replacement works in config: unload the `dsh-bash-local` entry, mount a different `bash` provider, and every plugin injecting `'bash'` cleanly restarts against the new implementation. +This is also why service replacement works in config: unload the `dsh-bash-local` entry, mount a different `shell` provider, and every plugin injecting `'shell'` cleanly restarts against the new implementation. ## Optional dependencies diff --git a/docs/cordis-tutorial/03-services.zh.md b/docs/cordis-tutorial/03-services.zh.md index d82be29aa6..fcfd8be7f7 100644 --- a/docs/cordis-tutorial/03-services.zh.md +++ b/docs/cordis-tutorial/03-services.zh.md @@ -75,7 +75,7 @@ Hello, world! `inject` 并非一次性的启动检查。如果应用运行期间所需服务消失,例如提供方被卸载或热替换,每个依赖插件也会随之卸载,并在服务恢复后再次加载。结合 effect([第 2 章](02-lifecycle-and-effects.md)),这能防止运行中的消费方保留对不可用服务的引用:依赖消失时,它自己的注册也会撤销。 -这也是配置中可以替换服务的原因:卸载 Cordis 配置项 `dsh-bash-local`,挂载另一个 `bash` 提供方,所有注入 `'bash'` 的插件都会重新启动并使用新实现。 +这也是配置中可以替换服务的原因:卸载 Cordis 配置项 `dsh-bash-local`,挂载另一个 `shell` 提供方,所有注入 `'shell'` 的插件都会重新启动并使用新实现。 ## 可选依赖 diff --git a/docs/cordis-tutorial/06-composition-and-hmr.i18n.yaml b/docs/cordis-tutorial/06-composition-and-hmr.i18n.yaml index 6a79850803..c61ee1a5ea 100644 --- a/docs/cordis-tutorial/06-composition-and-hmr.i18n.yaml +++ b/docs/cordis-tutorial/06-composition-and-hmr.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/cordis-tutorial/06-composition-and-hmr.md -06-composition-and-hmr.md: 87ea26014657ae8c8199e1ebb486556c827d96ca -06-composition-and-hmr.zh.md: 830f55de7c1be351fe701cb068197543602619a7 +06-composition-and-hmr.md: 2b53aa28be99851e77a71de76337f2beb4003e8d +06-composition-and-hmr.zh.md: cd4afa1d5465a5442bfd6b771ebcd61c46fe2983 diff --git a/docs/cordis-tutorial/06-composition-and-hmr.md b/docs/cordis-tutorial/06-composition-and-hmr.md index 87ea260146..2b53aa28be 100644 --- a/docs/cordis-tutorial/06-composition-and-hmr.md +++ b/docs/cordis-tutorial/06-composition-and-hmr.md @@ -18,7 +18,7 @@ A config entry accepts metadata beyond `name` and `config`: `id` gives the entry a stable identity so the loader can tell an edit to an existing entry apart from a removal plus an addition. `disabled: true` unmounts a plugin without deleting its entry — flip it back and the plugin (and everything PENDING on its services) loads again. -Groups nest a sub-list of entries that load and unload as one unit, and `isolate` gives a group its own instance of a service name — two groups can each see a differently-configured `bash` without affecting each other. The [Cordis primer](../cordis-primer.md) and the [service isolation example](../user/develop/framework/service.md#service-isolation) cover the details. +Groups nest a sub-list of entries that load and unload as one unit, and `isolate` gives a group its own instance of a service name — two groups can each see a differently configured `shell` provider without affecting each other. The [Cordis primer](../cordis-primer.md) and the [service isolation example](../user/develop/framework/service.md#service-isolation) cover the details. ## Hot module replacement diff --git a/docs/cordis-tutorial/06-composition-and-hmr.zh.md b/docs/cordis-tutorial/06-composition-and-hmr.zh.md index 830f55de7c..cd4afa1d54 100644 --- a/docs/cordis-tutorial/06-composition-and-hmr.zh.md +++ b/docs/cordis-tutorial/06-composition-and-hmr.zh.md @@ -18,7 +18,7 @@ Cordis 配置项除了 `name` 和 `config`,还接受其他元数据: `id` 为 Cordis 配置项提供稳定标识,使 loader 能区分修改现有 Cordis 配置项与先删除再添加。`disabled: true` 会卸载插件而不删除其 Cordis 配置项;改回原值后,插件以及所有因依赖其服务而处于 PENDING 的插件都会再次加载。 -组可以嵌套一份 Cordis 配置项子列表,并将其作为一个单元加载和卸载;`isolate` 则为一个组提供某项服务名称的独立实例,因此两个组可以各自看到配置不同的 `bash`,互不影响。[Cordis 入门](../cordis-primer.md)和[服务隔离示例](../user/develop/framework/service.md#service-isolation)介绍了详细内容。 +组可以嵌套一份 Cordis 配置项子列表,并将其作为一个单元加载和卸载;`isolate` 则为一个组提供某项服务名称的独立实例,因此两个组可以各自看到配置不同的 `shell` 提供方,互不影响。[Cordis 入门](../cordis-primer.md)和[服务隔离示例](../user/develop/framework/service.md#service-isolation)介绍了详细内容。 ## 热模块替换 diff --git a/docs/cordis-tutorial/index.i18n.yaml b/docs/cordis-tutorial/index.i18n.yaml index 9e18b6899e..68591da3e4 100644 --- a/docs/cordis-tutorial/index.i18n.yaml +++ b/docs/cordis-tutorial/index.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/cordis-tutorial/index.md index.md: c51965e186ce8d78b577c1005c1c3e1831b91be1 -index.zh.md: 8811930eba0f3e24ecbc47521e65582502257473 +index.zh.md: 22e1918672a34219ca0cf5efaa43a8eb693e6996 diff --git a/docs/cordis-tutorial/index.zh.md b/docs/cordis-tutorial/index.zh.md index 8811930eba..22e1918672 100644 --- a/docs/cordis-tutorial/index.zh.md +++ b/docs/cordis-tutorial/index.zh.md @@ -10,6 +10,8 @@ Cordis 是 DeepSeek Harness 底层的插件框架:它是一个小型运行时 如果你要为 harness 本身编写插件——由 `cordis.yml` 加载、在 Web UI 中驱动,而不是下面这个启动器——请从[第一个 Harness 插件](../user/develop/basic/index.md)开始。 + + ## 准备工作 你需要克隆本仓库并安装依赖;[开发指南](../development.md#setup-tutorial)列出了前置条件。本教程不需要 API 密钥;所有示例均可在无密钥环境中运行。 diff --git a/docs/persistence-catalog.i18n.yaml b/docs/persistence-catalog.i18n.yaml index 37859f5f2e..bc6e3dde66 100644 --- a/docs/persistence-catalog.i18n.yaml +++ b/docs/persistence-catalog.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/persistence-catalog.md -persistence-catalog.md: 032f7ae45d05b688fd317e9aa46363f658f19b76 -persistence-catalog.zh.md: 4f3ace46f7abb2d87be1cd66cbead0ac6b0cccda +persistence-catalog.md: c400298f7d37c590918820bcbda10e6550f197e8 +persistence-catalog.zh.md: 65ec0e3fbdd226c51a371dc9a90f10db5c929c7a diff --git a/docs/persistence-catalog.md b/docs/persistence-catalog.md index 032f7ae45d..c400298f7d 100644 --- a/docs/persistence-catalog.md +++ b/docs/persistence-catalog.md @@ -96,6 +96,8 @@ Sources: [`packages/core/session/src/types.ts:336`](../packages/core/session/src ### `agent/*` + + #### `agent/inbox/spliced` — log-only ```ts persistence-catalog @@ -117,6 +119,8 @@ Source: [`packages/core/agent/src/types.ts:19`](../packages/core/agent/src/types ### `agent-preset/*` + + #### `agent-preset/selected` — log-only ```ts persistence-catalog @@ -133,6 +137,8 @@ Source: [`packages/preset/agent-presets/src/session.ts:26`](../packages/preset/a ### `approval/*` + + #### `approval/asked` — log-only ```ts persistence-catalog @@ -156,6 +162,8 @@ Types: [CallId](subsystems/core.md) Source: [`packages/interaction/user-approval/src/index.ts:44`](../packages/interaction/user-approval/src/index.ts) + + #### `approval/decided` — log-only ```ts persistence-catalog @@ -172,6 +180,8 @@ Source: [`packages/interaction/user-approval/src/index.ts:44`](../packages/inter Source: [`packages/interaction/user-approval/src/index.ts:55`](../packages/interaction/user-approval/src/index.ts) + + #### `approval/policy` — log-only ```ts persistence-catalog @@ -194,6 +204,8 @@ Source: [`packages/interaction/user-approval/src/index.ts:67`](../packages/inter ### `assistant/*` + + #### `assistant/chunk` — log-only ```ts persistence-catalog @@ -205,6 +217,8 @@ Types: [StreamChunk](subsystems/llm-streaming.md) Source: [`packages/core/session/src/types.ts:266`](../packages/core/session/src/types.ts) + + #### `assistant/message` — surface ```ts persistence-catalog @@ -223,6 +237,8 @@ Source: [`packages/core/session/src/types.ts:273`](../packages/core/session/src/ ### `command/*` + + #### `command/done` — log-only ```ts persistence-catalog @@ -242,6 +258,8 @@ Source: [`packages/core/session/src/types.ts:273`](../packages/core/session/src/ Source: [`packages/interaction/commands/src/types.ts:95`](../packages/interaction/commands/src/types.ts) + + #### `command/run` — log-only ```ts persistence-catalog @@ -262,6 +280,8 @@ Source: [`packages/interaction/commands/src/types.ts:88`](../packages/interactio ### `compaction/*` + + #### `compaction/end` — log-only ```ts persistence-catalog @@ -274,6 +294,8 @@ Source: [`packages/interaction/commands/src/types.ts:88`](../packages/interactio Source: [`packages/compaction/compaction/src/types.ts:71`](../packages/compaction/compaction/src/types.ts) + + #### `compaction/prune` — log-only ```ts persistence-catalog @@ -298,6 +320,8 @@ Source: [`packages/compaction/compaction/src/types.ts:71`](../packages/compactio Source: [`packages/compaction/compaction/src/types.ts:81`](../packages/compaction/compaction/src/types.ts) + + #### `compaction/start` — log-only ```ts persistence-catalog @@ -311,6 +335,8 @@ Source: [`packages/compaction/compaction/src/types.ts:81`](../packages/compactio Source: [`packages/compaction/compaction/src/types.ts:23`](../packages/compaction/compaction/src/types.ts) + + #### `compaction/summary` — log-only ```ts persistence-catalog @@ -365,6 +391,8 @@ Source: [`packages/compaction/compaction/src/types.ts:33`](../packages/compactio ### `feedback/*` + + #### `feedback/record` — log-only ```ts persistence-catalog @@ -379,6 +407,8 @@ Source: [`packages/feedback/command-feedback/src/index.ts:62`](../packages/feedb ### `goal/*` + + #### `goal/change` — log-only ```ts persistence-catalog @@ -392,6 +422,8 @@ Source: [`packages/goal/goal/src/domain.ts:66`](../packages/goal/goal/src/domain ### `hook/*` + + #### `hook/invoked` — log-only ```ts persistence-catalog @@ -415,6 +447,8 @@ Source: [`packages/goal/goal/src/domain.ts:66`](../packages/goal/goal/src/domain Source: [`packages/hooks/hook-protocol/src/types.ts:19`](../packages/hooks/hook-protocol/src/types.ts) + + #### `hook/result` — log-only ```ts persistence-catalog @@ -438,6 +472,8 @@ Source: [`packages/hooks/hook-protocol/src/types.ts:31`](../packages/hooks/hook- ### `llm/*` + + #### `llm/retry` — log-only ```ts persistence-catalog @@ -447,6 +483,8 @@ Source: [`packages/hooks/hook-protocol/src/types.ts:31`](../packages/hooks/hook- Source: [`packages/llm/llm-retry/src/types.ts:9`](../packages/llm/llm-retry/src/types.ts) + + #### `llm/retry-started` — log-only ```ts persistence-catalog @@ -458,6 +496,8 @@ Source: [`packages/llm/llm-retry/src/types.ts:11`](../packages/llm/llm-retry/src ### `permission/*` + + #### `permission/preset` — log-only ```ts persistence-catalog @@ -474,6 +514,8 @@ Source: [`packages/interaction/permission-presets/src/index.ts:50`](../packages/ ### `plan/*` + + #### `plan/mode` — log-only ```ts persistence-catalog @@ -489,6 +531,8 @@ Source: [`packages/plan/plan-mode/src/index.ts:53`](../packages/plan/plan-mode/s ### `request/*` + + #### `request/context` — log-only ```ts persistence-catalog @@ -501,6 +545,8 @@ Source: [`packages/plan/plan-mode/src/index.ts:53`](../packages/plan/plan-mode/s Source: [`packages/core/session/src/types.ts:309`](../packages/core/session/src/types.ts) + + #### `request/header` — log-only ```ts persistence-catalog @@ -515,6 +561,8 @@ Source: [`packages/core/session/src/types.ts:304`](../packages/core/session/src/ ### `sandbox/*` + + #### `sandbox/mode` — log-only ```ts persistence-catalog @@ -536,6 +584,8 @@ Source: [`packages/sandbox/sandbox-policy/src/session-mode.ts:33`](../packages/s ### `schedule/*` + + #### `schedule/change` — log-only ```ts persistence-catalog @@ -552,6 +602,8 @@ Source: [`packages/schedule/schedule/src/types.ts:219`](../packages/schedule/sch ### `session/*` + + #### `session/end-seed` — log-only ```ts persistence-catalog @@ -582,6 +634,8 @@ Source: [`packages/schedule/schedule/src/types.ts:219`](../packages/schedule/sch Source: [`packages/core/session/src/types.ts:332`](../packages/core/session/src/types.ts) + + #### `session/title` — log-only ```ts persistence-catalog @@ -596,6 +650,8 @@ Types: [SessionTitleEventData](subsystems/session-title.md) Source: [`packages/session/session-title/src/index.ts:100`](../packages/session/session-title/src/index.ts) + + #### `session/title-llm-request` — log-only ```ts persistence-catalog @@ -609,6 +665,8 @@ Source: [`packages/session/session-title-llm/src/index.ts:43`](../packages/sessi ### `step/*` + + #### `step/end` — log-only ```ts persistence-catalog @@ -618,6 +676,8 @@ Source: [`packages/session/session-title-llm/src/index.ts:43`](../packages/sessi Source: [`packages/core/session/src/types.ts:256`](../packages/core/session/src/types.ts) + + #### `step/start` — log-only ```ts persistence-catalog @@ -629,6 +689,8 @@ Source: [`packages/core/session/src/types.ts:254`](../packages/core/session/src/ ### `subagent/*` + + #### `subagent/descriptor` — log-only ```ts persistence-catalog @@ -646,6 +708,8 @@ Source: [`packages/subagent/subagent/src/descriptor.ts:37`](../packages/subagent ### `todo/*` + + #### `todo/write` — log-only ```ts persistence-catalog @@ -659,6 +723,8 @@ Source: [`packages/core/session/src/types.ts:299`](../packages/core/session/src/ ### `tool/*` + + #### `tool/call` — log-only ```ts persistence-catalog @@ -674,6 +740,8 @@ Types: [CallId](subsystems/core.md) Source: [`packages/core/session/src/types.ts:279`](../packages/core/session/src/types.ts) + + #### `tool/code-dispatch` — log-only ```ts persistence-catalog @@ -697,6 +765,8 @@ Source: [`packages/core/session/src/types.ts:279`](../packages/core/session/src/ Source: [`packages/core/tools/src/types.ts:56`](../packages/core/tools/src/types.ts) + + #### `tool/code-dispatch-start` — log-only ```ts persistence-catalog @@ -718,6 +788,8 @@ Source: [`packages/core/tools/src/types.ts:56`](../packages/core/tools/src/types Source: [`packages/core/tools/src/types.ts:40`](../packages/core/tools/src/types.ts) + + #### `tool/result` — surface ```ts persistence-catalog @@ -745,6 +817,8 @@ Source: [`packages/core/session/src/types.ts:291`](../packages/core/session/src/ ### `tool-workflow/*` + + #### `tool-workflow/agent-end` — log-only ```ts persistence-catalog @@ -757,6 +831,8 @@ Source: [`packages/core/session/src/types.ts:291`](../packages/core/session/src/ Source: [`packages/workflow/tool-workflow/src/types.ts:57`](../packages/workflow/tool-workflow/src/types.ts) + + #### `tool-workflow/agent-start` — log-only ```ts persistence-catalog @@ -769,6 +845,8 @@ Source: [`packages/workflow/tool-workflow/src/types.ts:57`](../packages/workflow Source: [`packages/workflow/tool-workflow/src/types.ts:52`](../packages/workflow/tool-workflow/src/types.ts) + + #### `tool-workflow/run-end` — log-only ```ts persistence-catalog @@ -781,6 +859,8 @@ Source: [`packages/workflow/tool-workflow/src/types.ts:52`](../packages/workflow Source: [`packages/workflow/tool-workflow/src/types.ts:62`](../packages/workflow/tool-workflow/src/types.ts) + + #### `tool-workflow/run-start` — log-only ```ts persistence-catalog @@ -795,6 +875,8 @@ Source: [`packages/workflow/tool-workflow/src/types.ts:47`](../packages/workflow ### `turn/*` + + #### `turn/end` — log-only ```ts persistence-catalog @@ -813,6 +895,8 @@ Types: [TurnEndReason](subsystems/session.md) Source: [`packages/core/session/src/types.ts:252`](../packages/core/session/src/types.ts) + + #### `turn/start` — log-only ```ts persistence-catalog @@ -829,6 +913,8 @@ Source: [`packages/core/session/src/types.ts:243`](../packages/core/session/src/ ### `user/*` + + #### `user/message` — surface ```ts persistence-catalog @@ -846,6 +932,8 @@ Source: [`packages/core/session/src/types.ts:264`](../packages/core/session/src/ ### `web/*` + + #### `web/deepseek-search-llm-request` — log-only ```ts persistence-catalog diff --git a/docs/persistence-catalog.zh.md b/docs/persistence-catalog.zh.md index 4f3ace46f7..65ec0e3fbd 100644 --- a/docs/persistence-catalog.zh.md +++ b/docs/persistence-catalog.zh.md @@ -98,6 +98,8 @@ export type SessionEvent = { ### `agent/*` + + #### `agent/inbox/spliced` — log-only ```ts persistence-catalog @@ -119,6 +121,8 @@ export type SessionEvent = { ### `agent-preset/*` + + #### `agent-preset/selected` — log-only ```ts persistence-catalog @@ -135,6 +139,8 @@ export type SessionEvent = { ### `approval/*` + + #### `approval/asked` — log-only ```ts persistence-catalog @@ -158,6 +164,8 @@ export type SessionEvent = { 来源:[`packages/interaction/user-approval/src/index.ts:44`](../packages/interaction/user-approval/src/index.ts) + + #### `approval/decided` — log-only ```ts persistence-catalog @@ -174,6 +182,8 @@ export type SessionEvent = { 来源:[`packages/interaction/user-approval/src/index.ts:55`](../packages/interaction/user-approval/src/index.ts) + + #### `approval/policy` — log-only ```ts persistence-catalog @@ -196,6 +206,8 @@ export type SessionEvent = { ### `assistant/*` + + #### `assistant/chunk` — log-only ```ts persistence-catalog @@ -207,6 +219,8 @@ export type SessionEvent = { 来源:[`packages/core/session/src/types.ts:266`](../packages/core/session/src/types.ts) + + #### `assistant/message` — surface ```ts persistence-catalog @@ -225,6 +239,8 @@ export type SessionEvent = { ### `command/*` + + #### `command/done` — log-only ```ts persistence-catalog @@ -244,6 +260,8 @@ export type SessionEvent = { 来源:[`packages/interaction/commands/src/types.ts:95`](../packages/interaction/commands/src/types.ts) + + #### `command/run` — log-only ```ts persistence-catalog @@ -264,6 +282,8 @@ export type SessionEvent = { ### `compaction/*` + + #### `compaction/end` — log-only ```ts persistence-catalog @@ -276,6 +296,8 @@ export type SessionEvent = { 来源:[`packages/compaction/compaction/src/types.ts:71`](../packages/compaction/compaction/src/types.ts) + + #### `compaction/prune` — log-only ```ts persistence-catalog @@ -300,6 +322,8 @@ export type SessionEvent = { 来源:[`packages/compaction/compaction/src/types.ts:81`](../packages/compaction/compaction/src/types.ts) + + #### `compaction/start` — log-only ```ts persistence-catalog @@ -313,6 +337,8 @@ export type SessionEvent = { 来源:[`packages/compaction/compaction/src/types.ts:23`](../packages/compaction/compaction/src/types.ts) + + #### `compaction/summary` — log-only ```ts persistence-catalog @@ -367,6 +393,8 @@ export type SessionEvent = { ### `feedback/*` + + #### `feedback/record` — log-only ```ts persistence-catalog @@ -381,6 +409,8 @@ export type SessionEvent = { ### `goal/*` + + #### `goal/change` — log-only ```ts persistence-catalog @@ -394,6 +424,8 @@ export type SessionEvent = { ### `hook/*` + + #### `hook/invoked` — log-only ```ts persistence-catalog @@ -417,6 +449,8 @@ export type SessionEvent = { 来源:[`packages/hooks/hook-protocol/src/types.ts:19`](../packages/hooks/hook-protocol/src/types.ts) + + #### `hook/result` — log-only ```ts persistence-catalog @@ -440,6 +474,8 @@ export type SessionEvent = { ### `llm/*` + + #### `llm/retry` — log-only ```ts persistence-catalog @@ -449,6 +485,8 @@ export type SessionEvent = { 来源:[`packages/llm/llm-retry/src/types.ts:9`](../packages/llm/llm-retry/src/types.ts) + + #### `llm/retry-started` — log-only ```ts persistence-catalog @@ -460,6 +498,8 @@ export type SessionEvent = { ### `permission/*` + + #### `permission/preset` — log-only ```ts persistence-catalog @@ -476,6 +516,8 @@ export type SessionEvent = { ### `plan/*` + + #### `plan/mode` — log-only ```ts persistence-catalog @@ -491,6 +533,8 @@ export type SessionEvent = { ### `request/*` + + #### `request/context` — log-only ```ts persistence-catalog @@ -503,6 +547,8 @@ export type SessionEvent = { 来源:[`packages/core/session/src/types.ts:309`](../packages/core/session/src/types.ts) + + #### `request/header` — log-only ```ts persistence-catalog @@ -517,6 +563,8 @@ export type SessionEvent = { ### `sandbox/*` + + #### `sandbox/mode` — log-only ```ts persistence-catalog @@ -538,6 +586,8 @@ export type SessionEvent = { ### `schedule/*` + + #### `schedule/change` — log-only ```ts persistence-catalog @@ -554,6 +604,8 @@ export type SessionEvent = { ### `session/*` + + #### `session/end-seed` — log-only ```ts persistence-catalog @@ -584,6 +636,8 @@ export type SessionEvent = { 来源:[`packages/core/session/src/types.ts:332`](../packages/core/session/src/types.ts) + + #### `session/title` — log-only ```ts persistence-catalog @@ -598,6 +652,8 @@ export type SessionEvent = { 来源:[`packages/session/session-title/src/index.ts:100`](../packages/session/session-title/src/index.ts) + + #### `session/title-llm-request` — log-only ```ts persistence-catalog @@ -611,6 +667,8 @@ export type SessionEvent = { ### `step/*` + + #### `step/end` — log-only ```ts persistence-catalog @@ -620,6 +678,8 @@ export type SessionEvent = { 来源:[`packages/core/session/src/types.ts:256`](../packages/core/session/src/types.ts) + + #### `step/start` — log-only ```ts persistence-catalog @@ -631,6 +691,8 @@ export type SessionEvent = { ### `subagent/*` + + #### `subagent/descriptor` — log-only ```ts persistence-catalog @@ -648,6 +710,8 @@ export type SessionEvent = { ### `todo/*` + + #### `todo/write` — log-only ```ts persistence-catalog @@ -661,6 +725,8 @@ export type SessionEvent = { ### `tool/*` + + #### `tool/call` — log-only ```ts persistence-catalog @@ -676,6 +742,8 @@ export type SessionEvent = { 来源:[`packages/core/session/src/types.ts:279`](../packages/core/session/src/types.ts) + + #### `tool/code-dispatch` — log-only ```ts persistence-catalog @@ -699,6 +767,8 @@ export type SessionEvent = { 来源:[`packages/core/tools/src/types.ts:56`](../packages/core/tools/src/types.ts) + + #### `tool/code-dispatch-start` — log-only ```ts persistence-catalog @@ -720,6 +790,8 @@ export type SessionEvent = { 来源:[`packages/core/tools/src/types.ts:40`](../packages/core/tools/src/types.ts) + + #### `tool/result` — surface ```ts persistence-catalog @@ -747,6 +819,8 @@ export type SessionEvent = { ### `tool-workflow/*` + + #### `tool-workflow/agent-end` — log-only ```ts persistence-catalog @@ -759,6 +833,8 @@ export type SessionEvent = { 来源:[`packages/workflow/tool-workflow/src/types.ts:57`](../packages/workflow/tool-workflow/src/types.ts) + + #### `tool-workflow/agent-start` — log-only ```ts persistence-catalog @@ -771,6 +847,8 @@ export type SessionEvent = { 来源:[`packages/workflow/tool-workflow/src/types.ts:52`](../packages/workflow/tool-workflow/src/types.ts) + + #### `tool-workflow/run-end` — log-only ```ts persistence-catalog @@ -783,6 +861,8 @@ export type SessionEvent = { 来源:[`packages/workflow/tool-workflow/src/types.ts:62`](../packages/workflow/tool-workflow/src/types.ts) + + #### `tool-workflow/run-start` — log-only ```ts persistence-catalog @@ -797,6 +877,8 @@ export type SessionEvent = { ### `turn/*` + + #### `turn/end` — log-only ```ts persistence-catalog @@ -815,6 +897,8 @@ export type SessionEvent = { 来源:[`packages/core/session/src/types.ts:252`](../packages/core/session/src/types.ts) + + #### `turn/start` — log-only ```ts persistence-catalog @@ -831,6 +915,8 @@ export type SessionEvent = { ### `user/*` + + #### `user/message` — surface ```ts persistence-catalog @@ -848,6 +934,8 @@ export type SessionEvent = { ### `web/*` + + #### `web/deepseek-search-llm-request` — log-only ```ts persistence-catalog diff --git a/docs/subsystems/core.i18n.yaml b/docs/subsystems/core.i18n.yaml index 041f874d47..1265b726fe 100644 --- a/docs/subsystems/core.i18n.yaml +++ b/docs/subsystems/core.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/subsystems/core.md -core.md: 6eda80cbd164168c3c4a846c9bc50a8fc12b0c92 -core.zh.md: 14c2e78bc3fff959fb0c0040eae0f382985ecdf4 +core.md: d14ad52e57572d5b0b110a0b16ff734e3499bdf5 +core.zh.md: 9ace28731f2532f571b66f2e7f6a3ea4a2c4e345 diff --git a/docs/subsystems/core.md b/docs/subsystems/core.md index 6eda80cbd1..d14ad52e57 100644 --- a/docs/subsystems/core.md +++ b/docs/subsystems/core.md @@ -257,6 +257,8 @@ Its full fields, the `defineTool`/`ValueSchemaSpec`/`ParameterSchemaSpec` typed Two patterns recur across every subsystem and are documented once, here. + + ### The `…Map → derived-union` pattern Almost every extensible sum type in the harness follows one pattern: an interface keyed by a discriminant tag (the `…Map`), from which the union is derived with `keyof`. Plugins add variants by **declaration merging** — no edit to the owning package. diff --git a/docs/subsystems/core.zh.md b/docs/subsystems/core.zh.md index 14c2e78bc3..9ace28731f 100644 --- a/docs/subsystems/core.zh.md +++ b/docs/subsystems/core.zh.md @@ -263,6 +263,8 @@ type SessionStartSource = 'startup' | 'resume' | 'clear' | 'compact' 两个模式在每个子系统中反复出现,只在此处记录一次。 + + ### `…Map → derived-union` 模式 harness 中几乎所有可扩展的和类型都遵循同一模式:一个以判别标签为键的接口(`…Map`),联合类型由 `keyof` 派生。插件通过**声明合并**添加变体——无需修改拥有该类型的包。 diff --git a/docs/subsystems/llm-streaming.i18n.yaml b/docs/subsystems/llm-streaming.i18n.yaml index d1d2810cfe..5708a7b6d5 100644 --- a/docs/subsystems/llm-streaming.i18n.yaml +++ b/docs/subsystems/llm-streaming.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/subsystems/llm-streaming.md -llm-streaming.md: 0f395245332e735c04997bd1ca82f66fa9286104 +llm-streaming.md: 0d3a0d53c875c9d943146ba44b775d81fc9cae01 llm-streaming.zh.md: fbaa47d14d57e7377be4db6ecaa04f11997572a6 diff --git a/docs/subsystems/llm-streaming.md b/docs/subsystems/llm-streaming.md index 0f39524533..0d3a0d53c8 100644 --- a/docs/subsystems/llm-streaming.md +++ b/docs/subsystems/llm-streaming.md @@ -151,6 +151,8 @@ type ContextFormed = | { readonly form: 'recall' } ``` + + ## `StreamChunk` — the raw protocol A streaming response interleaves several typed blocks (text, reasoning, multiple tool calls). `index` ties each delta to its block; `block-end` carries the fully-assembled `ContentBlock` so consumers don't have to re-assemble deltas themselves. It is a **closed** discriminated union — a `switch` over `type` ends with `assertNever`, so adding a variant breaks compilation at every consumer that must handle it. diff --git a/docs/subsystems/persistence.i18n.yaml b/docs/subsystems/persistence.i18n.yaml index 6d0ca447a2..2950610348 100644 --- a/docs/subsystems/persistence.i18n.yaml +++ b/docs/subsystems/persistence.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/subsystems/persistence.md -persistence.md: fde8348d64a200eda5133abf66deedee6be09857 -persistence.zh.md: a2a836d81d153a2697c1d739345501e7e6770eef +persistence.md: 5b1b224e419aca205baba69894ed64467b8fb4e1 +persistence.zh.md: a91e7d66b92270c82287d054e619b665e96ea206 diff --git a/docs/subsystems/persistence.md b/docs/subsystems/persistence.md index fde8348d64..5b1b224e41 100644 --- a/docs/subsystems/persistence.md +++ b/docs/subsystems/persistence.md @@ -36,6 +36,8 @@ interface SessionLocation { } ``` + + ## `SessionHeader` — metadata beside the log Per-session metadata travels **separately** from the event log: format version, cwd, lineage, and the seed boundary are storage concerns, not conversation events, so they stay out of `SessionEventMap` and never reach `deriveMessages()`. The header is attached to a `Session` via `session.header`. diff --git a/docs/subsystems/persistence.zh.md b/docs/subsystems/persistence.zh.md index a2a836d81d..a91e7d66b9 100644 --- a/docs/subsystems/persistence.zh.md +++ b/docs/subsystems/persistence.zh.md @@ -36,6 +36,8 @@ interface SessionLocation { } ``` + + ## `SessionHeader`:日志旁的元数据 每个会话的元数据与事件日志**分开**存储:格式版本、cwd、血统与 seed 边界是存储层关注点而非对话事件,因此不进入 `SessionEventMap`,也不会到达 `deriveMessages()`。header 通过 `session.header` 附加到 `Session` 上。 diff --git a/docs/subsystems/session.i18n.yaml b/docs/subsystems/session.i18n.yaml index 8ee4185e54..5eacc07962 100644 --- a/docs/subsystems/session.i18n.yaml +++ b/docs/subsystems/session.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/subsystems/session.md -session.md: 4c40971fe58952b32635aa5eb767a42f73108b00 +session.md: 1741b0dbf8b79187e2777cba17dbfb7c5a698b94 session.zh.md: d958b03fcdad91b58cc277636e599cce46a2c7fb diff --git a/docs/subsystems/session.md b/docs/subsystems/session.md index 4c40971fe5..1741b0dbf8 100644 --- a/docs/subsystems/session.md +++ b/docs/subsystems/session.md @@ -149,6 +149,8 @@ interface TodoItem { } ``` + + ### The request header event: `request/header` The request envelope — the `EpochHeader` (call config + markers for adapter-supplied defaults + rendered system prompt + assembled tool schemas) — is logged session state, so every conversation request is a pure function of the log (the reconstructability Agent Note). A full `request/header` snapshot with reason `'initial'` or `'resume'` records each loop-instance boundary; a later changed request records another full snapshot with reason `'change'`. `foldRequestHeader(events)` reconstructs the header by selecting the latest snapshot. The event is not a `SurfaceEventType`: it produces no LLM message. diff --git a/docs/tool-catalog.i18n.yaml b/docs/tool-catalog.i18n.yaml index a2083a076c..3ed16072ca 100644 --- a/docs/tool-catalog.i18n.yaml +++ b/docs/tool-catalog.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/tool-catalog.md -tool-catalog.md: 3d73ed1ef5346f620808e9d6b04299291ba1ed4c -tool-catalog.zh.md: 90573928a24630e802d98441bc0e340baf582b23 +tool-catalog.md: 50563c97c6cd5496871ea7fa52c4823a56b088fd +tool-catalog.zh.md: ed0c7e3f70cffbecd3d20a1556bcb0cd4204df00 diff --git a/docs/tool-catalog.md b/docs/tool-catalog.md index 3d73ed1ef5..50563c97c6 100644 --- a/docs/tool-catalog.md +++ b/docs/tool-catalog.md @@ -40,6 +40,8 @@ This table connects model-visible tool names to the plugin package and service s | `@deepseek-ai/dsh-tool-workflow` | `workflow` | `ctx.tools`, `ctx.workflowEngine`, `ctx.systemPrompt`, `a calling Agent (exec.agent parents the script children)` | `tool/call`, `tool/result` | - | - | | `@deepseek-ai/dsh-tool-web` | `web_fetch`, `web_search` | `ctx.tools`, `ctx.web`, `ctx.systemPrompt` | `tool/call`, `tool/result` | - | web_search and web_fetch keep provider selection behind ctx.web so model-visible schemas stay stable across backend swaps. | + + ## `@deepseek-ai/dsh-tool-ask-user` ### `ask_user_question` @@ -112,6 +114,8 @@ Source: [`packages/interaction/tool-ask-user/src/index.ts`](../packages/interact ask_user_question pauses the tool call until the active UI provider returns a human answer. + + ## `@deepseek-ai/dsh-tools` ### `run_code` @@ -142,6 +146,8 @@ Source: [`packages/core/tools/src/code-mode.ts`](../packages/core/tools/src/code Owned by the tool registry as a reserved transport outside filterable capability layers under `mode: code` / `mode: both` (see the Code Mode Agent Note). Under `code` it is the registry's only wire contribution; the other visible capabilities are declared in a generated SDK section in the loaded runtime's language, and a program calls them through bindings scheduled under the native concurrency contract (submission-ordered starts and policy; concurrency-safe bodies overlap up to `maxParallelSubCalls`) that re-enter the complete guarded tool pipeline and link each nested execution to this outer result. + + ## `@deepseek-ai/dsh-plan-mode` ### `exit_plan_mode` @@ -167,6 +173,8 @@ Source: [`packages/plan/plan-mode/src/index.ts`](../packages/plan/plan-mode/src/ exit_plan_mode stays in the model-facing schema while planning is inactive so transitions add no tool-catalog churn on top of the plan-policy change. Its execute path rejects calls outside plan mode; in plan mode it presents the plan over the user-questions seam (approve / keep planning with feedback), and approval logs plan mode inactive at the step boundary. + + ## `@deepseek-ai/dsh-tool-bash` ### `bash` @@ -209,6 +217,8 @@ Source: [`packages/shell/tool-bash/src/index.ts`](../packages/shell/tool-bash/sr The bash tool is the model-facing consumer of the bash executor seam. A `run_in_background` run registers with the generic `ctx.jobs` runtime and is collected/stopped through the `job_*` tools from `@deepseek-ai/dsh-tool-jobs`; the `enableRunInBackground` config (default true) removes the parameter entirely when disabled. + + ## `@deepseek-ai/dsh-tool-pwsh` ### `pwsh` @@ -251,6 +261,8 @@ Source: [`packages/shell/tool-pwsh/src/index.ts`](../packages/shell/tool-pwsh/sr The pwsh tool is the PowerShell-dialect consumer of the bash executor seam for Windows compositions (a PowerShell executor such as `@deepseek-ai/dsh-pwsh-local` backs `ctx.shell`); it mirrors the bash tool call-for-call minus sandbox controls — `run_in_background` runs register with the generic `ctx.jobs` runtime and are collected/stopped through the `job_*` tools, and the managed `DSH_*` environment comes from `@deepseek-ai/dsh-shell-env`. Each call runs in a fresh process (no persistent PTY session), with native `C:\...` paths and `$env:NAME` variables. + + ## `@deepseek-ai/dsh-tool-cordis` ### `cordis_define` @@ -487,6 +499,8 @@ Source: [`packages/extensions/tool-cordis/src/index.ts`](../packages/extensions/ Not in any shipped tree (a deliberate opt-in — dynamic package code reaches the real runtime, see .agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md). The toolset injects `ctx.dynamicCordisRunner` from `@deepseek-ai/dsh-cordis-host-runner`, which owns the definition registry and the vm sandbox; a composition missing it never activates the tools. A running package may register ADDITIONAL model-visible tools until it is stopped, undefined, or DSH restarts; a full changed request header logs those tool-set changes. + + ## `@deepseek-ai/dsh-tool-bash-persistent` ### `bash` @@ -512,6 +526,8 @@ Source: [`packages/shell/tool-bash-persistent/src/index.ts`](../packages/shell/t One owner-isolated persistent bash tool; deployment composition supplies the PTY backend and may override the model-facing environment description. + + ## `@deepseek-ai/dsh-tool-str-replace-editor` ### `str_replace_editor` @@ -580,6 +596,8 @@ Source: [`packages/fs/tool-str-replace-editor/src/index.ts`](../packages/fs/tool Standalone view/create/unique literal replace/line insert tool over the filesystem seam; it composes with any shell or terminal API. + + ## `@deepseek-ai/dsh-tool-fs` ### `edit` @@ -695,6 +713,8 @@ Source: [`packages/fs/tool-fs/src/index.ts`](../packages/fs/tool-fs/src/index.ts The read-before-write/edit policy is added by `@deepseek-ai/dsh-fs-observation-policy` (an `fs/*` event-gate plugin, no schema change); a deployment that loads these tools is expected to also load it. `read_image` is not registered without `ctx.attachments`; its schema is route-independent, and execution refuses unless the exact routed model declares image input. + + ## `@deepseek-ai/dsh-tool-fs-search` ### `glob` @@ -753,6 +773,8 @@ Source: [`packages/fs/tool-fs-search/src/index.ts`](../packages/fs/tool-fs-searc glob and grep are unconditional discovery tools that spawn the packaged ripgrep binary (`@vscode/ripgrep`) through ctx.subprocess as ordinary foreground calls (never background jobs) — no host `rg` install and no shell layer. The catalog uses `sampleOverCapGlobResults: true`; deployments must choose that behavior explicitly. Capped results save the complete formatted list through the optional ctx.spillStore backend; returned locators are follow-up-readable/searchable when the backend exposes local paths in co-located deployments. + + ## `@deepseek-ai/dsh-tool-terminal` ### `terminal_close` @@ -916,6 +938,8 @@ Source: [`packages/terminal/tool-terminal/src/index.ts`](../packages/terminal/to The six terminal tools are opt-in and complement one-shot shell/filesystem tools. `terminal_send(run_in_background: true)` registers with `ctx.jobs`; TUI, named key sequences, BEL, resize, auto-start, and cross-agent sharing are absent from the schema. + + ## `@deepseek-ai/dsh-tool-goal` ### `create_goal` @@ -1008,6 +1032,8 @@ Source: [`packages/goal/tool-goal/src/index.ts`](../packages/goal/tool-goal/src/ create, edit, pause, and resume require direct-human root authority; complete and blocked also accept the exact current goal round. The default blocked lower bound is three admitted rounds. + + ## `@deepseek-ai/dsh-schedule` ### `schedule_create` @@ -1103,6 +1129,8 @@ Source: [`packages/schedule/schedule/src/tools.ts`](../packages/schedule/schedul Registered only inside live root Agent scopes created after the opt-in Schedule plugin loads. Version 1 accepts after_seconds, explicit absolute at, and bounded fixed-rate every_seconds, and discloses session-local delivery; management reads and mutations require the shared Session persistence barrier. + + ## `@deepseek-ai/dsh-tool-lsp` ### `lsp` @@ -1149,6 +1177,8 @@ Source: [`packages/lsp/tool-lsp/src/index.ts`](../packages/lsp/tool-lsp/src/inde The lsp tool keeps provider selection and language-server subprocesses behind ctx.lsp, so its model-visible schema stays stable across providers. Requires a registered provider (e.g. `@deepseek-ai/dsh-lsp-stdio`) at runtime; without one, a query returns the structured `LSP_UNAVAILABLE` error rather than changing the schema. + + ## `@deepseek-ai/dsh-tool-ralph` ### `ralph` @@ -1178,6 +1208,8 @@ Source: [`packages/workflow/tool-ralph/src/index.ts`](../packages/workflow/tool- A fixed foreground workflow starts one fresh structured child per round; the model selects only the immutable objective and an optional round cap. + + ## `@deepseek-ai/dsh-tool-skill` ### `skill` @@ -1201,6 +1233,8 @@ Load the full instructions for an available skill. Call this with the exact skil Source: [`packages/skill/tool-skill/src/index.ts`](../packages/skill/tool-skill/src/index.ts) + + ## `@deepseek-ai/dsh-tool-session-query` ### `session_event_read` @@ -1434,6 +1468,8 @@ Source: [`packages/session-query/tool-session-query/src/index.ts`](../packages/s The five read-only tools hide provider cursors and authorize every result from the immutable calling agent session. The package is opt-in; compositions that need enforced deadlines or bounded inline output also mount the generic timeout or spill policies. + + ## `@deepseek-ai/dsh-tool-subagent` ### `subagent` @@ -1468,6 +1504,8 @@ Source: [`packages/subagent/tool-subagent/src/index.ts`](../packages/subagent/to The registered tool name is the load-time `toolName` config (default `subagent`); the schema above is that default. The shipped compositions load this package once per subagent backend, so the model additionally sees `subagent_fork` bound to the fork backend. Each instance's description, `run_in_background` parameter, and system-prompt policy follow its own `backgroundMode` and `enableRunInBackground`, so the two shipped schemas are not identical: `subagent` is `continuable` and defaults omitted calls to background with automatic settlement delivery, while `subagent_fork` stays `one-shot` and defaults them to foreground — see `packages/bundle/base/cordis.patch.yml` and `examples/acp-agent/cordis.yml`. + + ## `@deepseek-ai/dsh-tool-subagent-control` ### `interrupt_agent` @@ -1541,6 +1579,8 @@ Source: [`packages/subagent/tool-subagent-control/src/index.ts`](../packages/sub The globally named control tools over continuable background subagents: provider-bound `tool-subagent` instances register distinct delegation tools, while this package registers `send_message` and `interrupt_agent` once, plus `list_agents` from its separately loaded `/list-agents` plugin (whose catalog rows use the sessionProjections and live Agent registries). + + ## `@deepseek-ai/dsh-tool-subagent-report` ### `report` @@ -1566,6 +1606,8 @@ Source: [`packages/subagent/tool-subagent-report/src/index.ts`](../packages/suba Registered per continuable in-process child rather than globally, so this schema is visible only inside such a child and survives its global `toolFilter`. The same contribution installs the child-scoped `tool:report` prompt section, which this catalog does not render. The parent-facing `send_message` tool is installed independently. + + ## `@deepseek-ai/dsh-tool-jobs` ### `job_kill` @@ -1637,6 +1679,8 @@ Source: [`packages/jobs/tool-jobs/src/index.ts`](../packages/jobs/tool-jobs/src/ The kind-agnostic background-job controller: background bash commands, PTY sends, and subagents are read, listed, and killed through the same three tools. Loading the plugin attaches the controller that arms producers' `ctx.jobs.start()`. + + ## `@deepseek-ai/dsh-tool-todo` ### `todo_write` @@ -1685,6 +1729,8 @@ Source: [`packages/todo/tool-todo/src/index.ts`](../packages/todo/tool-todo/src/ todo_write is session-owned state; UIs render the latest todo/write event as a checklist. `allowParallelInProgress` is required with no default, so the catalog states its choice: `true`, whose description invites several `in_progress` items. A deployment choosing `false` receives the same tool with a description asking for exactly one active task. + + ## `@deepseek-ai/dsh-tool-workflow` ### `workflow` @@ -1778,6 +1824,8 @@ Constraints: concurrency and total-agent caps apply; no filesystem, network, tim Source: [`packages/workflow/tool-workflow/src/index.ts`](../packages/workflow/tool-workflow/src/index.ts) + + ## `@deepseek-ai/dsh-tool-web` ### `web_fetch` diff --git a/docs/tool-catalog.zh.md b/docs/tool-catalog.zh.md index 90573928a2..ed0c7e3f70 100644 --- a/docs/tool-catalog.zh.md +++ b/docs/tool-catalog.zh.md @@ -42,6 +42,8 @@ | `@deepseek-ai/dsh-tool-workflow` | `workflow` | `ctx.tools`、`ctx.workflowEngine`、`ctx.systemPrompt`、`a calling Agent (exec.agent parents the script children)` | `tool/call`、`tool/result` | - | - | | `@deepseek-ai/dsh-tool-web` | `web_fetch`、`web_search` | `ctx.tools`、`ctx.web`、`ctx.systemPrompt` | `tool/call`、`tool/result` | - | web_search 和 web_fetch 将提供方选择置于 ctx.web 之后,使模型可见 schema 在更换后端时保持稳定。 | + + ## `@deepseek-ai/dsh-tool-ask-user` ### `ask_user_question` @@ -114,6 +116,8 @@ ask_user_question 会暂停工具调用,直到当前 UI 提供方返回人类答案。 + + ## `@deepseek-ai/dsh-tools` ### `run_code` @@ -144,6 +148,8 @@ ask_user_question 会暂停工具调用,直到当前 UI 提供方返回人类 在 `mode: code`/`mode: both` 下,它由工具注册表所有,作为可过滤能力层之外的保留传输机制(参见 Code Mode Agent Note)。在 `code` 下,它是注册表对协议格式的唯一贡献;其他可见能力在使用已加载运行时语言生成的 SDK 章节中声明。程序通过 binding 调用这些能力,调用按照原生并发约定调度:启动顺序和策略遵循提交顺序,并发安全的函数体最多重叠执行 `maxParallelSubCalls` 个。调用会重新进入完整且受守卫保护的工具流水线,并将每个嵌套执行关联到此外层结果。 + + ## `@deepseek-ai/dsh-plan-mode` ### `exit_plan_mode` @@ -169,6 +175,8 @@ ask_user_question 会暂停工具调用,直到当前 UI 提供方返回人类 规划未激活时,exit_plan_mode 仍保留在面向模型的 schema 中,这样状态转换不会在规划策略变更之外额外造成工具目录变动。其执行路径会拒绝规划模式之外的调用;在规划模式下,它通过用户交互 seam 提交计划(批准/根据反馈继续规划),批准后会在步骤边界记录规划模式已停用。 + + ## `@deepseek-ai/dsh-tool-bash` ### `bash` @@ -211,6 +219,8 @@ ask_user_question 会暂停工具调用,直到当前 UI 提供方返回人类 bash 工具是 bash 执行器 seam 面向模型的消费方。使用 `run_in_background` 的运行会注册到通用 `ctx.jobs` 运行时,并通过 `job_*` 工具(来自 `@deepseek-ai/dsh-tool-jobs`)收集/停止;禁用 `enableRunInBackground` 配置(默认为 true)后,该参数会被完全移除。 + + ## `@deepseek-ai/dsh-tool-pwsh` ### `pwsh` @@ -253,6 +263,8 @@ bash 工具是 bash 执行器 seam 面向模型的消费方。使用 `run_in_bac pwsh 工具是 Windows 组合中 bash 执行器 seam 的 PowerShell 方言消费方(由 `@deepseek-ai/dsh-pwsh-local` 等 PowerShell 执行器为 `ctx.shell` 提供后端);除沙箱接口外,它逐项对应 bash 工具调用。使用 `run_in_background` 的运行会注册到通用 `ctx.jobs` 运行时,并通过 `job_*` 工具收集/停止;托管的 `DSH_*` 环境来自 `@deepseek-ai/dsh-shell-env`。每次调用都在新进程中运行,不使用持久 PTY 会话。路径采用原生 `C:\...` 形式,变量采用 `$env:NAME`。 + + ## `@deepseek-ai/dsh-tool-cordis` ### `cordis_define` @@ -489,6 +501,8 @@ pwsh 工具是 Windows 组合中 bash 执行器 seam 的 PowerShell 方言消费 不在任何随产品发布的树中,需要显式选择启用;动态 Package 代码可以访问真实运行时,见 .agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md。该工具集注入 `@deepseek-ai/dsh-cordis-host-runner` 提供的 `ctx.dynamicCordisRunner`,后者拥有定义注册表和 vm 沙箱;组合缺少它时这些工具不会激活。运行中的 Package 在停止、undefine 或 DSH 重启前可以注册**额外的**模型可见工具;发生这类工具集变化时,系统会记录完整且有变动的请求头。 + + ## `@deepseek-ai/dsh-tool-bash-persistent` ### `bash` @@ -514,6 +528,8 @@ pwsh 工具是 Windows 组合中 bash 执行器 seam 的 PowerShell 方言消费 一个按所有者隔离的持久 bash 工具;部署组合提供 PTY 后端,并可覆盖面向模型的环境描述。 + + ## `@deepseek-ai/dsh-tool-str-replace-editor` ### `str_replace_editor` @@ -584,6 +600,8 @@ pwsh 工具是 Windows 组合中 bash 执行器 seam 的 PowerShell 方言消费 基于文件系统 seam 的独立查看/创建/唯一字面量替换/按行插入工具;可与任何 shell 或终端接口组合。 + + ## `@deepseek-ai/dsh-tool-fs` ### `edit` @@ -699,6 +717,8 @@ pwsh 工具是 Windows 组合中 bash 执行器 seam 的 PowerShell 方言消费 先读后写/编辑策略由 `@deepseek-ai/dsh-fs-observation-policy` 添加;它是一个 `fs/*` 事件门禁插件,不会改变 schema。加载这些工具的部署按预期也应加载该插件。没有 `ctx.attachments` 时 `read_image` 不会注册;其 schema 与路由无关,执行时除非确切路由的模型声明图像输入,否则拒绝。 + + ## `@deepseek-ai/dsh-tool-fs-search` ### `glob` @@ -757,6 +777,8 @@ pwsh 工具是 Windows 组合中 bash 执行器 seam 的 PowerShell 方言消费 glob 和 grep 是无条件可用的发现工具,通过 ctx.subprocess spawn 随包提供的 ripgrep 二进制文件(`@vscode/ripgrep`),并作为普通前台调用运行,绝不作为后台任务;无需在宿主机安装 `rg`,也不经过 shell 层。本目录使用 `sampleOverCapGlobResults: true`;部署必须显式选择该行为。结果超过上限时,会通过可选的 ctx.spillStore 后端保存完整的格式化列表;在共置部署中,如果后端公开本地路径,返回的定位信息可供后续读取/搜索。 + + ## `@deepseek-ai/dsh-tool-terminal` ### `terminal_close` @@ -920,6 +942,8 @@ glob 和 grep 是无条件可用的发现工具,通过 ctx.subprocess spawn 这 6 个终端工具需要选择启用,用于补充一次性 bash/文件系统工具。`terminal_send(run_in_background: true)` 会注册到 `ctx.jobs`;schema 不包含 TUI、具名按键序列、BEL、调整尺寸、自动启动和跨 agent 共享。 + + ## `@deepseek-ai/dsh-tool-goal` ### `create_goal` @@ -1012,6 +1036,8 @@ glob 和 grep 是无条件可用的发现工具,通过 ctx.subprocess spawn create、edit、pause 和 resume 要求直接来自人类的根权限;complete 和 blocked 也接受确切的当前 Goal Round。blocked 的默认下限是 3 个获准的 Round。 + + ## `@deepseek-ai/dsh-schedule` ### `schedule_create` @@ -1107,6 +1133,8 @@ create、edit、pause 和 resume 要求直接来自人类的根权限;complete 仅在选择启用的 Schedule 插件加载后创建的 live 根 Agent scope 内注册。版本 1 接受 after_seconds、显式绝对 at 和有界固定速率 every_seconds,并披露 session-local 交付;管理读取与变更必须通过共享的 Session 持久化 barrier。 + + ## `@deepseek-ai/dsh-tool-lsp` ### `lsp` @@ -1153,6 +1181,8 @@ create、edit、pause 和 resume 要求直接来自人类的根权限;complete lsp 工具将提供方选择和语言服务器子进程置于 ctx.lsp 之后,因此其模型可见 schema 在更换提供方时保持稳定。运行时要求已注册提供方,例如 `@deepseek-ai/dsh-lsp-stdio`;如果没有提供方,查询会返回结构化 `LSP_UNAVAILABLE` 错误,而不会改变 schema。 + + ## `@deepseek-ai/dsh-tool-ralph` ### `ralph` @@ -1182,6 +1212,8 @@ lsp 工具将提供方选择和语言服务器子进程置于 ctx.lsp 之后, 固定的前台工作流会在每个 Round 启动一个全新的结构化子级;模型只能选择不可变目标和可选的 Round 上限。 + + ## `@deepseek-ai/dsh-tool-skill` ### `skill` @@ -1205,6 +1237,8 @@ lsp 工具将提供方选择和语言服务器子进程置于 ctx.lsp 之后, 来源:[`packages/skill/tool-skill/src/index.ts`](../packages/skill/tool-skill/src/index.ts) + + ## `@deepseek-ai/dsh-tool-session-query` ### `session_event_read` @@ -1438,6 +1472,8 @@ lsp 工具将提供方选择和语言服务器子进程置于 ctx.lsp 之后, 这 5 个只读工具会隐藏提供方游标,并根据不可变的调用 agent 会话为每个结果授权。该包需要选择启用;需要强制截止时间或限制行内输出的组合还会挂载通用超时或 spill 策略。 + + ## `@deepseek-ai/dsh-tool-subagent` ### `subagent` @@ -1472,6 +1508,8 @@ lsp 工具将提供方选择和语言服务器子进程置于 ctx.lsp 之后, 注册的工具名称取决于加载时 `toolName` 配置(默认为 `subagent`);上述 schema 对应默认值。随产品发布的组合会为每个 subagent 后端加载一次该包,因此模型还会看到绑定到 fork 后端的 `subagent_fork`。每个实例的描述、`run_in_background` 参数与 system prompt 策略取决于它自己的 `backgroundMode` 和 `enableRunInBackground`,因此两个随附 schema 并不相同:`subagent` 为 `continuable`,省略参数时默认后台运行,并由 runtime 自动投递结束结果;`subagent_fork` 保持 `one-shot`,省略参数时默认前台运行。详见 `packages/bundle/base/cordis.patch.yml` 和 `examples/acp-agent/cordis.yml`。 + + ## `@deepseek-ai/dsh-tool-subagent-control` ### `interrupt_agent` @@ -1545,6 +1583,8 @@ lsp 工具将提供方选择和语言服务器子进程置于 ctx.lsp 之后, 这些是控制可继续后台 subagent 的全局命名工具:绑定提供方的 `tool-subagent` 实例注册不同的委派工具;本包注册一次 `send_message` 和 `interrupt_agent`,另由 `list_agents` 通过单独加载的 `/list-agents` 插件提供,其目录行使用 sessionProjections 和实时 Agent 注册表。 + + ## `@deepseek-ai/dsh-tool-subagent-report` ### `report` @@ -1570,6 +1610,8 @@ lsp 工具将提供方选择和语言服务器子进程置于 ctx.lsp 之后, 按可继续的进程内子级注册,而非全局注册,因此该 schema 仅在这种子级内部可见,并且不受其全局 `toolFilter` 影响。同一份贡献还会安装子级作用域的 `tool:report` 系统提示词 section,本目录不渲染该 section。面向父级的 `send_message` 工具单独安装。 + + ## `@deepseek-ai/dsh-tool-jobs` ### `job_kill` @@ -1641,6 +1683,8 @@ lsp 工具将提供方选择和语言服务器子进程置于 ctx.lsp 之后, 与任务种类无关的后台任务控制器:后台 bash 命令、PTY 发送和 subagent 都通过相同的 3 个工具读取、列出和终止。加载该插件会挂接控制器,从而启用生产方的 `ctx.jobs.start()`。 + + ## `@deepseek-ai/dsh-tool-todo` ### `todo_write` @@ -1689,6 +1733,8 @@ lsp 工具将提供方选择和语言服务器子进程置于 ctx.lsp 之后, todo_write 是会话所有的状态;UI 将最新的 todo/write 事件渲染为检查清单。`allowParallelInProgress` 是没有默认值的必填项,因此本目录明确选择 `true`,对应描述允许同时存在多个 `in_progress` 项。选择 `false` 的部署会获得同一工具,但描述会要求只能有 1 个活动任务。 + + ## `@deepseek-ai/dsh-tool-workflow` ### `workflow` @@ -1783,6 +1829,8 @@ todo_write 是会话所有的状态;UI 将最新的 todo/write 事件渲染为 来源:[`packages/workflow/tool-workflow/src/index.ts`](../packages/workflow/tool-workflow/src/index.ts) + + ## `@deepseek-ai/dsh-tool-web` ### `web_fetch` diff --git a/docs/user/develop/basic/index.i18n.yaml b/docs/user/develop/basic/index.i18n.yaml index 0dd8723e1b..96e27f6c6d 100644 --- a/docs/user/develop/basic/index.i18n.yaml +++ b/docs/user/develop/basic/index.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/user/develop/basic/index.md -index.md: 494b7869be6ffdf5767fac260b36b2585305b516 -index.zh.md: a55b8e31151c5cfa5445069974be9fa022fea1eb +index.md: 08199624e638aaf4a36b04446c39c228b2af6025 +index.zh.md: c45a30d0bfffaf4a6c78303f9ca043c3397c8a08 diff --git a/docs/user/develop/basic/index.md b/docs/user/develop/basic/index.md index 494b7869be..08199624e6 100644 --- a/docs/user/develop/basic/index.md +++ b/docs/user/develop/basic/index.md @@ -45,14 +45,16 @@ export function apply(ctx: Context) { ## Register it in cordis.yml -Create `scratch-plugin/cordis.yml` as a Web overlay that inserts the local plugin: +Run `pwd` from the repository root, then create `scratch-plugin/cordis.yml` as a Web overlay that inserts the local plugin. Replace `/absolute/path/to/deepseek-harness` below with the printed path: ```yaml - insert: - id: hello - name: './src/my-plugin.ts' + name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts' ``` +The plugin path must be absolute. A patch file contributes configuration but does not change the profile directory from which the loader resolves module paths. + Start the Web UI with that overlay: ```sh diff --git a/docs/user/develop/basic/index.zh.md b/docs/user/develop/basic/index.zh.md index a55b8e3115..c45a30d0bf 100644 --- a/docs/user/develop/basic/index.zh.md +++ b/docs/user/develop/basic/index.zh.md @@ -45,14 +45,16 @@ export function apply(ctx: Context) { ## 注册到 cordis.yml -创建 `scratch-plugin/cordis.yml`,作为插入本地插件的 Web 覆盖层: +在仓库根目录运行 `pwd`,然后创建 `scratch-plugin/cordis.yml`,作为插入本地插件的 Web 覆盖层。请将下文的 `/absolute/path/to/deepseek-harness` 替换为命令打印的路径: ```yaml - insert: - id: hello - name: './src/my-plugin.ts' + name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts' ``` +插件路径必须是绝对路径。patch 文件只贡献配置,不会改变 loader 解析模块路径时使用的 profile 目录。 + 使用该覆盖层启动 Web UI: ```sh diff --git a/docs/user/develop/basic/publish.i18n.yaml b/docs/user/develop/basic/publish.i18n.yaml index 649408dead..694a7ff663 100644 --- a/docs/user/develop/basic/publish.i18n.yaml +++ b/docs/user/develop/basic/publish.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/user/develop/basic/publish.md -publish.md: 588531a28020ebe620643cd1aaaa43de000e658a -publish.zh.md: b86bd43369c027972705394fded43ae053248c0f +publish.md: edc85b74049c5b993efb02663195fcc0001b4620 +publish.zh.md: cb824ad12eec0d706efa17b28f3bdc874ba59131 diff --git a/docs/user/develop/basic/publish.md b/docs/user/develop/basic/publish.md index 588531a280..edc85b7404 100644 --- a/docs/user/develop/basic/publish.md +++ b/docs/user/develop/basic/publish.md @@ -2,7 +2,7 @@ English | [中文](publish.zh.md) -The previous tutorials loaded a local plugin through a `--patch` overlay. This tutorial packages it as an installable **bundle**, installs it into a **profile** with `dsh plugin add`, and explains the layer order that determines the composed configuration. Complete [plugin configuration](./config.md) first. +The previous tutorials loaded a local plugin through a `--patch` overlay. This tutorial packages it as an installable **bundle**, installs it into a **profile** with `pnpm dsh plugin add`, and explains the layer order that determines the composed configuration. Complete [plugin configuration](./config.md) first. ## Two concepts, two manifests @@ -11,10 +11,16 @@ Installation is built on two concepts. Both are described by a `package.json`, b - A **bundle** is an npm package that ships a configuration layer. Its manifest declares `dsh.bundle`, answering "what does this package contribute?": a patch file that inserts or overrides plugin rows. - A **profile** is a directory under `$DSH_HOME/profiles/` describing one runnable composition. Its manifest declares `dsh.profile`, answering "which bundles compose this setup, in what order?". -A bundle is what you author and distribute; a profile is what a user boots with `dsh --profile `. Nothing is both. +A bundle is what you author and distribute; a profile is what a user boots from this source checkout with `pnpm dsh --profile `. Nothing is both. ### The bundle manifest +From the repository root, create the package directory: + +```sh +mkdir -p hello-plugin +``` + ``` hello-plugin/ ├── package.json # declares dsh.bundle @@ -22,6 +28,8 @@ hello-plugin/ └── index.js # plugin modules the patch rows reference ``` +Create `hello-plugin/package.json`: + ```json { "name": "dsh-hello-plugin", @@ -33,7 +41,17 @@ hello-plugin/ } ``` -The patch file is a YAML array of patch entries, like the `--patch` overlays you have been writing, except plugin rows reference the package by name instead of a relative source path so Node resolution finds the installed code: +Create `hello-plugin/index.js` with the plugin entry point: + +```js +export const name = 'hello-plugin' + +export function apply() { + console.log('[hello-plugin] plugin loaded!') +} +``` + +Create `hello-plugin/cordis.patch.yml`. The patch is a YAML array like the `--patch` overlays you have been writing, except plugin rows reference the package by name instead of a relative source path so Node resolution finds the installed code: ```yaml - insert: @@ -41,7 +59,7 @@ The patch file is a YAML array of patch entries, like the `--patch` overlays you name: dsh-hello-plugin ``` -A package without the `dsh.bundle` declaration still installs, but only as a plain dependency: `dsh plugin` prints a warning and activates no layer. Use that package format for a library that plugin packages import rather than a plugin users enable. +A package without the `dsh.bundle` declaration still installs, but only as a plain dependency: `pnpm dsh plugin` prints a warning and activates no layer. Use that package format for a library that plugin packages import rather than a plugin users enable. ### The profile manifest @@ -50,15 +68,14 @@ A profile directory holds two files: - `package.json` — the profile's out-of-tree plugin dependencies (managed by pnpm) plus the `dsh.profile` manifest with its ordered `bundles` list. - `cordis.patch.yml` — the user's own patch layer, applied after every bundle layer. -You never write a profile manifest by hand: `dsh plugin` creates and maintains it. The next section shows the result. +You never write a profile manifest by hand: `pnpm dsh plugin` creates and maintains it. The next section shows the result. ## Install into a profile -`dsh plugin --profile ` forwards to pnpm in the profile directory, so every pnpm verb works. Install your package from its checkout: +`pnpm dsh plugin --profile ` forwards to pnpm in the profile directory, so every pnpm verb works. From the repository root, install the package checkout: ```sh -cd hello-plugin -dsh plugin --profile demo add . +pnpm dsh plugin --profile demo add ./hello-plugin ``` The first use initializes the profile (with `@deepseek-ai/dsh-base` as its first bundle), pnpm links the checkout, and `dsh` appends the bundle to `dsh.profile.bundles` because the package declares `dsh.bundle`: @@ -84,11 +101,11 @@ The first use initializes the profile (with `@deepseek-ai/dsh-base` as its first Verify the layer without booting, then boot: ```sh -dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer -dsh --profile demo +pnpm dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer +pnpm dsh --profile demo ``` -`dsh plugin --profile demo remove dsh-hello-plugin` removes both the dependency and the layer. +`pnpm dsh plugin --profile demo remove dsh-hello-plugin` removes both the dependency and the layer. ## The loading order @@ -136,7 +153,7 @@ On `--help`, the provider publishes no service, so those rows never activate. Lo Publishing to a registry is not required — users can install straight from a git host: ```sh -dsh plugin --profile demo add github:you/hello-plugin +pnpm dsh plugin --profile demo add github:you/hello-plugin ``` But a git install fetches **sources, not built artifacts**: nothing runs your `build` script, so a TypeScript package arrives without its `lib/` output and fails to load. Two things must happen, one on each side: @@ -155,8 +172,8 @@ Treat that allowance as what it is: **permission to execute the package's code o If you would rather not ask users for the allowance, distribute built artifacts instead — neither form needs any build permission: -- **Publish to npm** with `lib/` built at `pnpm publish` time; `dsh plugin add your-package` then installs prebuilt code. -- **Ship a tarball** from `pnpm pack`; users run `dsh plugin add ./hello-plugin-0.1.0.tgz`. +- **Publish to npm** with `lib/` built at `pnpm publish` time; `pnpm dsh plugin add your-package` then installs prebuilt code. +- **Ship a tarball** from `pnpm pack`; users run `pnpm dsh plugin add ./hello-plugin-0.1.0.tgz`. ## Next steps diff --git a/docs/user/develop/basic/publish.zh.md b/docs/user/develop/basic/publish.zh.md index b86bd43369..cb824ad12e 100644 --- a/docs/user/develop/basic/publish.zh.md +++ b/docs/user/develop/basic/publish.zh.md @@ -2,7 +2,7 @@ [English](publish.md) | 中文 -前几篇教程通过 `--patch` overlay 加载本地插件。本教程把它打包成可安装的**组合包**(bundle),用 `dsh plugin add` 安装进一个 **profile**,并解释决定组合后配置的层顺序。请先完成[插件配置](./config.md)。 +前几篇教程通过 `--patch` overlay 加载本地插件。本教程把它打包成可安装的**组合包**(bundle),用 `pnpm dsh plugin add` 安装进一个 **profile**,并解释决定组合后配置的层顺序。请先完成[插件配置](./config.md)。 ## 两个概念,两种 manifest @@ -11,10 +11,16 @@ - **组合包**是附带一个配置层的 npm 包。它的 manifest 声明 `dsh.bundle`,回答的是"这个包贡献什么?":一个插入或覆盖插件行的 patch 文件。 - **profile** 是位于 `$DSH_HOME/profiles/` 下、描述一份可启动组合的目录。它的 manifest 声明 `dsh.profile`,回答的是"这套配置由哪些组合包按什么顺序组成?"。 -组合包是你编写并分发的东西;profile 是用户用 `dsh --profile ` 启动的东西。没有东西同时是两者。 +组合包是你编写并分发的东西;profile 是用户在当前源码 checkout 中用 `pnpm dsh --profile ` 启动的东西。没有东西同时是两者。 ### 组合包 manifest +在仓库根目录创建包目录: + +```sh +mkdir -p hello-plugin +``` + ``` hello-plugin/ ├── package.json # declares dsh.bundle @@ -22,6 +28,8 @@ hello-plugin/ └── index.js # plugin modules the patch rows reference ``` +创建 `hello-plugin/package.json`: + ```json { "name": "dsh-hello-plugin", @@ -33,7 +41,17 @@ hello-plugin/ } ``` -patch 文件与一直在写的 `--patch` overlay 一样,是一个 patch 条目的 YAML 数组;区别是插件行按包名而不是相对源码路径引用这个包,这样 Node 的模块解析才能找到已安装的代码: +创建 `hello-plugin/index.js`,写入插件入口: + +```js +export const name = 'hello-plugin' + +export function apply() { + console.log('[hello-plugin] plugin loaded!') +} +``` + +创建 `hello-plugin/cordis.patch.yml`。这个 patch 与一直在写的 `--patch` overlay 一样,是一个 patch 条目的 YAML 数组;区别是插件行按包名而不是相对源码路径引用这个包,这样 Node 的模块解析才能找到已安装的代码: ```yaml - insert: @@ -41,7 +59,7 @@ patch 文件与一直在写的 `--patch` overlay 一样,是一个 patch 条目 name: dsh-hello-plugin ``` -没有 `dsh.bundle` 声明的包仍然可以安装,但只作为普通依赖:`dsh plugin` 会打印警告,且不激活任何层。如果一个库供插件包 import,而不是供用户启用,就使用这种包格式。 +没有 `dsh.bundle` 声明的包仍然可以安装,但只作为普通依赖:`pnpm dsh plugin` 会打印警告,且不激活任何层。如果一个库供插件包 import,而不是供用户启用,就使用这种包格式。 ### profile manifest @@ -50,15 +68,14 @@ profile 目录包含两个文件: - `package.json` — profile 的树外插件依赖(由 pnpm 管理),加上 `dsh.profile` manifest 及其有序的 `bundles` 列表。 - `cordis.patch.yml` — 用户自己的 patch 层,在每个组合包层之后应用。 -profile manifest 从不需要手写:`dsh plugin` 负责创建和维护它。下一节展示其结果。 +profile manifest 从不需要手写:`pnpm dsh plugin` 负责创建和维护它。下一节展示其结果。 ## 安装进 profile -`dsh plugin --profile ` 在 profile 目录内转发给 pnpm,因此所有 pnpm 子命令都可用。从 checkout 安装你的包: +`pnpm dsh plugin --profile ` 在 profile 目录内转发给 pnpm,因此所有 pnpm 子命令都可用。在仓库根目录安装该包的 checkout: ```sh -cd hello-plugin -dsh plugin --profile demo add . +pnpm dsh plugin --profile demo add ./hello-plugin ``` 首次使用会初始化 profile(`@deepseek-ai/dsh-base` 作为它的第一个组合包),pnpm 链接该 checkout,而 `dsh` 因为这个包声明了 `dsh.bundle`,把它追加进 `dsh.profile.bundles`: @@ -84,11 +101,11 @@ dsh plugin --profile demo add . 先不启动、只验证该层,再启动: ```sh -dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer -dsh --profile demo +pnpm dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer +pnpm dsh --profile demo ``` -`dsh plugin --profile demo remove dsh-hello-plugin` 会同时移除依赖和对应的层。 +`pnpm dsh plugin --profile demo remove dsh-hello-plugin` 会同时移除依赖和对应的层。 ## 加载顺序 @@ -136,7 +153,7 @@ dsh --profile demo 发布到注册表不是必须的——用户可以直接从 git 托管安装: ```sh -dsh plugin --profile demo add github:you/hello-plugin +pnpm dsh plugin --profile demo add github:you/hello-plugin ``` 但 git 安装拉取的是**源码,不是构建产物**:没有任何环节运行你的 `build` 脚本,因此 TypeScript 包到手时没有 `lib/` 输出,加载会失败。必须两边各做一件事: @@ -155,8 +172,8 @@ dsh plugin --profile demo add github:you/hello-plugin 如果不想让用户做这项授权,就改为分发构建产物——以下两种形式都不需要任何构建权限: -- **发布到 npm**,在 `pnpm publish` 时构建好 `lib/`;`dsh plugin add your-package` 安装的就是预构建代码。 -- **交付 tarball**:用 `pnpm pack` 打包;用户执行 `dsh plugin add ./hello-plugin-0.1.0.tgz`。 +- **发布到 npm**,在 `pnpm publish` 时构建好 `lib/`;`pnpm dsh plugin add your-package` 安装的就是预构建代码。 +- **交付 tarball**:用 `pnpm pack` 打包;用户执行 `pnpm dsh plugin add ./hello-plugin-0.1.0.tgz`。 ## 下一步 diff --git a/docs/user/develop/framework/events.i18n.yaml b/docs/user/develop/framework/events.i18n.yaml index b5f1cc7745..7b62e40d71 100644 --- a/docs/user/develop/framework/events.i18n.yaml +++ b/docs/user/develop/framework/events.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/user/develop/framework/events.md -events.md: 4a6ecbad614cf2debccaee9ace1086273d8fb95b -events.zh.md: c77747bf6ac767c69e4f2d9cfe7375b71bd388e6 +events.md: 1d9fe5c8f5068de6ad8b2abaa85cf67be35c8459 +events.zh.md: 8bb9447a270cc2db966b1e01298a831a60a1b9c1 diff --git a/docs/user/develop/framework/events.md b/docs/user/develop/framework/events.md index 4a6ecbad61..1d9fe5c8f5 100644 --- a/docs/user/develop/framework/events.md +++ b/docs/user/develop/framework/events.md @@ -40,7 +40,7 @@ ctx.on('my-plugin/ready', ({ id }) => { ### bail — short circuit -Listeners run in order; the first non-`undefined` result becomes the final result: +Listeners run in order; the first result other than `null`, `false`, or `undefined` becomes the final result: ```ts ignore-check // Dispatch @@ -49,13 +49,13 @@ const result = ctx.bail('some-check', input) // Listen: a returned value stops later listeners. ctx.on('some-check', (input) => { if (shouldBlock(input)) return 'blocked' - // Return undefined to continue to the next listener. + // Return null, false, or undefined to continue to the next listener. }) ``` ### serial — ordered execution -Listeners run in registration order and asynchronous results are awaited. The first listener to return a non-empty value stops further execution: +Listeners run in registration order and asynchronous results are awaited. The first result other than `null`, `false`, or `undefined` stops further execution: ```ts ignore-check await ctx.serial('setup-phase', context) diff --git a/docs/user/develop/framework/events.zh.md b/docs/user/develop/framework/events.zh.md index c77747bf6a..8bb9447a27 100644 --- a/docs/user/develop/framework/events.zh.md +++ b/docs/user/develop/framework/events.zh.md @@ -40,7 +40,7 @@ ctx.on('my-plugin/ready', ({ id }) => { ### bail — 短路 -依次调用监听器,第一个非 `undefined` 的返回值将作为最终结果: +监听器按顺序运行,第一个不是 `null`、`false` 或 `undefined` 的返回值会成为最终结果: ```ts ignore-check // Dispatch @@ -49,13 +49,13 @@ const result = ctx.bail('some-check', input) // Listen: a returned value stops later listeners. ctx.on('some-check', (input) => { if (shouldBlock(input)) return 'blocked' - // Return undefined to continue to the next listener. + // Return null, false, or undefined to continue to the next listener. }) ``` ### serial — 顺序执行 -监听器按注册顺序依次执行,并等待异步结果;第一个返回非空值的监听器会终止后续执行: +监听器按注册顺序依次执行,并等待异步结果;第一个不是 `null`、`false` 或 `undefined` 的返回值会终止后续执行: ```ts ignore-check await ctx.serial('setup-phase', context) diff --git a/docs/user/develop/framework/service.i18n.yaml b/docs/user/develop/framework/service.i18n.yaml index 69b91821ba..507fc26f86 100644 --- a/docs/user/develop/framework/service.i18n.yaml +++ b/docs/user/develop/framework/service.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/user/develop/framework/service.md -service.md: a9e873f1ba969b1f2d22266f8ec03207f84eeddc -service.zh.md: a201280bc38176f57f5354ab205ae700c7b04a89 +service.md: 03f4e7dc4df934495a4b203066183753b621339e +service.zh.md: 2f4c01e0ea6a87a87ca694b265b07e07098f3e59 diff --git a/docs/user/develop/framework/service.md b/docs/user/develop/framework/service.md index a9e873f1ba..03f4e7dc4d 100644 --- a/docs/user/develop/framework/service.md +++ b/docs/user/develop/framework/service.md @@ -117,7 +117,7 @@ This prevents a plugin from calling a service that no longer exists. name: '@deepseek-ai/cordis-plugin-group' group: true isolate: - bash: true + shell: true config: - name: '@deepseek-ai/dsh-bash-local' config: @@ -128,7 +128,7 @@ This prevents a plugin from calling a service that no longer exists. name: '@deepseek-ai/cordis-plugin-group' group: true isolate: - bash: true + shell: true config: - name: '@deepseek-ai/dsh-bash-local' config: diff --git a/docs/user/develop/framework/service.zh.md b/docs/user/develop/framework/service.zh.md index a201280bc3..2f4c01e0ea 100644 --- a/docs/user/develop/framework/service.zh.md +++ b/docs/user/develop/framework/service.zh.md @@ -108,6 +108,8 @@ export function apply(ctx: Context) { 这可以防止插件调用已不存在的服务。 + + ## 服务隔离 `cordis.yml` 支持服务隔离——同一个服务可以有多个实例,不同插件组看到不同实例: @@ -117,7 +119,7 @@ export function apply(ctx: Context) { name: '@deepseek-ai/cordis-plugin-group' group: true isolate: - bash: true + shell: true config: - name: '@deepseek-ai/dsh-bash-local' config: @@ -128,7 +130,7 @@ export function apply(ctx: Context) { name: '@deepseek-ai/cordis-plugin-group' group: true isolate: - bash: true + shell: true config: - name: '@deepseek-ai/dsh-bash-local' config: diff --git a/docs/user/develop/practice/index.i18n.yaml b/docs/user/develop/practice/index.i18n.yaml index 166269706c..2db0900779 100644 --- a/docs/user/develop/practice/index.i18n.yaml +++ b/docs/user/develop/practice/index.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/user/develop/practice/index.md -index.md: 56ca7249b8b156ceb78327b547413dbf65726dcc -index.zh.md: aec5980ea45cfd5f8f7408877743920e68e15cbd +index.md: cc6bd7a234305f6fa193341f15354b40855f72e9 +index.zh.md: aed13b00f9bc74946aace614953bed30705c8281 diff --git a/docs/user/develop/practice/index.md b/docs/user/develop/practice/index.md index 56ca7249b8..cc6bd7a234 100644 --- a/docs/user/develop/practice/index.md +++ b/docs/user/develop/practice/index.md @@ -23,7 +23,7 @@ The Bash execution capability consists of: └─────────────┘ └──────────────────┘ └──────────────┘ ▲ │ └────────────────────────────────────────────┘ - inject: ['bash'] + inject: ['shell'] ``` ## Benefits of the split diff --git a/docs/user/develop/practice/index.zh.md b/docs/user/develop/practice/index.zh.md index aec5980ea4..aed13b00f9 100644 --- a/docs/user/develop/practice/index.zh.md +++ b/docs/user/develop/practice/index.zh.md @@ -23,7 +23,7 @@ └─────────────┘ └──────────────────┘ └──────────────┘ ▲ │ └────────────────────────────────────────────┘ - inject: ['bash'] + inject: ['shell'] ``` ## 拆分的好处 diff --git a/docs/user/develop/practice/llm-adapter.i18n.yaml b/docs/user/develop/practice/llm-adapter.i18n.yaml index 6c91f15cb1..d7d431bd5e 100644 --- a/docs/user/develop/practice/llm-adapter.i18n.yaml +++ b/docs/user/develop/practice/llm-adapter.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/user/develop/practice/llm-adapter.md -llm-adapter.md: aba4a6d0c8ee42e78ca5a804d9a0dd9b31c1e240 -llm-adapter.zh.md: 5c7a6483e29a28871257df6a31e13d859a245097 +llm-adapter.md: 882e82d880ac622cf886c6c24e75a1987e1c6702 +llm-adapter.zh.md: 27c480af2a19a68ac35900a2ce1b5e9dbdc85bf8 diff --git a/docs/user/develop/practice/llm-adapter.md b/docs/user/develop/practice/llm-adapter.md index aba4a6d0c8..882e82d880 100644 --- a/docs/user/develop/practice/llm-adapter.md +++ b/docs/user/develop/practice/llm-adapter.md @@ -32,12 +32,12 @@ class MyAdapter extends LlmAdapter { export interface Config { apiKey: string - models: string[] + providers: string[] } export const Config: Schema = Schema.object({ apiKey: Schema.string().required(), - models: Schema.array(Schema.string()).required(), + providers: Schema.array(Schema.string()).required(), }) export const name = 'my-llm-adapter' @@ -45,7 +45,7 @@ export const inject = ['llm'] export function apply(ctx: Context, config: Config) { const adapter = new MyAdapter(config.apiKey) - ctx.llm.registerAdapter(config.models, adapter) + ctx.llm.registerAdapter(config.providers, adapter) } ``` @@ -117,10 +117,10 @@ Override `resolveModel(provider, model, signal?)` to return exact provider/model ## Register an adapter ```ts ignore-check -ctx.llm.registerAdapter(['model-name-1', 'model-name-2'], adapter) +ctx.llm.registerAdapter(['my-provider'], adapter) ``` -The first argument lists the model names handled by the adapter. If `cordis.yml` selects `model: model-name-1`, the service routes that request to this adapter. +The first argument lists provider routes handled by the adapter. `GenerateOptions.provider` selects the registered adapter, while `GenerateOptions.model` passes an adapter-owned model id without lifecycle registration. Override `listModels()` when the adapter can advertise model choices to selectors. ## Use it from cordis.yml @@ -129,18 +129,16 @@ The first argument lists the model names handled by the adapter. If `cordis.yml` name: './src/my-llm-adapter.ts' config: apiKey: !!js process.env.MY_API_KEY - models: - - my-model-v1 - - my-model-v2 + providers: + - my-provider - id: agent-loop name: '@deepseek-ai/dsh-agent-loop' config: agents: - id: main - provider: my-llm - model: my-model-v1 # References the model registered above. - workspaceContext: false + provider: my-provider + model: my-model-v1 ``` ## Reference implementations diff --git a/docs/user/develop/practice/llm-adapter.zh.md b/docs/user/develop/practice/llm-adapter.zh.md index 5c7a6483e2..27c480af2a 100644 --- a/docs/user/develop/practice/llm-adapter.zh.md +++ b/docs/user/develop/practice/llm-adapter.zh.md @@ -32,12 +32,12 @@ class MyAdapter extends LlmAdapter { export interface Config { apiKey: string - models: string[] + providers: string[] } export const Config: Schema = Schema.object({ apiKey: Schema.string().required(), - models: Schema.array(Schema.string()).required(), + providers: Schema.array(Schema.string()).required(), }) export const name = 'my-llm-adapter' @@ -45,7 +45,7 @@ export const inject = ['llm'] export function apply(ctx: Context, config: Config) { const adapter = new MyAdapter(config.apiKey) - ctx.llm.registerAdapter(config.models, adapter) + ctx.llm.registerAdapter(config.providers, adapter) } ``` @@ -117,10 +117,10 @@ async function* exampleChunks(): AsyncIterable { ## 注册适配器 ```ts ignore-check -ctx.llm.registerAdapter(['model-name-1', 'model-name-2'], adapter) +ctx.llm.registerAdapter(['my-provider'], adapter) ``` -第一个参数是该适配器支持的模型名列表。当用户在 `cordis.yml` 中配置 `model: model-name-1` 时,框架会将请求路由到该适配器。 +第一个参数是该适配器处理的提供方路由列表。`GenerateOptions.provider` 选择已注册的适配器,`GenerateOptions.model` 则传入由适配器拥有、无需在生命周期启动时注册的模型 id。适配器能够向选择器公布模型选项时,请覆写 `listModels()`。 ## 在 cordis.yml 中使用 @@ -129,18 +129,16 @@ ctx.llm.registerAdapter(['model-name-1', 'model-name-2'], adapter) name: './src/my-llm-adapter.ts' config: apiKey: !!js process.env.MY_API_KEY - models: - - my-model-v1 - - my-model-v2 + providers: + - my-provider - id: agent-loop name: '@deepseek-ai/dsh-agent-loop' config: agents: - id: main - provider: my-llm - model: my-model-v1 # References the model registered above. - workspaceContext: false + provider: my-provider + model: my-model-v1 ``` ## 实战参考 diff --git a/docs/user/guide/index.i18n.yaml b/docs/user/guide/index.i18n.yaml index 51f8a8802d..f6727b6bc4 100644 --- a/docs/user/guide/index.i18n.yaml +++ b/docs/user/guide/index.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/user/guide/index.md -index.md: 80d288b1aba37e7f0863fe5fc8237cbd2a6ab9b5 -index.zh.md: addfbc94ff93ed015e52f509a23a3f981e36770b +index.md: 282a5c11b317a8fb8706bb03f41cf03fb2aca49d +index.zh.md: 4ec53b16fc8987b5eb40ef4854cb41d1436a9015 diff --git a/docs/user/guide/index.md b/docs/user/guide/index.md index 80d288b1ab..282a5c11b3 100644 --- a/docs/user/guide/index.md +++ b/docs/user/guide/index.md @@ -2,9 +2,7 @@ English | [中文](index.zh.md) -Start the Web UI through the [root README](../../../README.md#run); the command prints its URL. This guide begins after that server is running. - -The invoking directory is the default workspace, so the agent can inspect and modify the project where you started `dsh`. +Start the Web UI through the [root README](../../../README.md#run); the command prints its URL. This guide begins after that server is running. The `dsh` process uses its invoking directory as the default filesystem location, but a fresh Web UI has no selected workspace until you add one. ## Configure a model @@ -12,6 +10,10 @@ Open **Settings → Models**, enter a DeepSeek API key, and save it. The model r The [model configuration guide](./providers.md) covers other providers and custom OpenAI-compatible endpoints. +## Choose a workspace + +Click **Choose workspace**, add the project directory where you started `dsh`, and select it. The session composer remains unavailable until a workspace is selected. + ## Run a task Start a session and send: diff --git a/docs/user/guide/index.zh.md b/docs/user/guide/index.zh.md index addfbc94ff..4ec53b16fc 100644 --- a/docs/user/guide/index.zh.md +++ b/docs/user/guide/index.zh.md @@ -2,9 +2,7 @@ [English](index.md) | 中文 -先按照[根 README](../../../README.md#run)启动 Web UI;命令会打印其访问地址。本指南从服务器已经运行的状态开始。 - -调用目录是默认工作区,因此 agent(智能体)可以检查并修改启动 `dsh` 时所在的项目。 +先按照[根 README](../../../README.md#run)启动 Web UI;命令会打印其访问地址。本指南从服务器已经运行的状态开始。`dsh` 进程会把调用目录作为默认文件系统位置,但新的 Web UI 在添加工作区前不会选中任何工作区。 ## 配置模型 @@ -12,6 +10,10 @@ [模型配置指南](./providers.md)介绍其他提供方和自定义 OpenAI 兼容端点。 +## 选择工作区 + +点击**选择工作区**,添加启动 `dsh` 时所在的项目目录,然后选中它。选中工作区前,会话输入框不可用。 + ## 运行任务 启动一个会话并发送: diff --git a/package.json b/package.json index cf0f8b917f..9adfdf5cb4 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "doc-typecheck:contracts-ready": "tsx scripts/doc-typecheck.ts", "verify-md-wrap": "tsx scripts/verify-md-wrap.ts", "verify-md-links": "tsx scripts/verify-md-links.ts", + "verify-doc-site-fragments": "tsx scripts/verify-doc-site-fragments.ts", "verify-public-repository-links": "tsx scripts/verify-public-repository-links.ts", "verify-doc-refs": "tsx scripts/verify-doc-refs.ts", "verify-package-paths": "tsx scripts/verify-package-paths.ts", @@ -86,10 +87,10 @@ "gen-translation-brief": "tsx scripts/gen-translation-brief.ts", "verify-doc-budgets": "tsx scripts/verify-doc-budgets.ts", "docs:dev": "pnpm --filter @deepseek-ai/website run dev", - "docs:build": "pnpm --filter @deepseek-ai/website run build", - "docs:build:mpa": "pnpm --filter @deepseek-ai/website exec vitepress build . --mpa", + "docs:build": "pnpm --filter @deepseek-ai/website run build && pnpm run verify-doc-site-fragments", + "docs:build:mpa": "pnpm --filter @deepseek-ai/website exec vitepress build . --mpa && pnpm run verify-doc-site-fragments", "docs:preview": "pnpm --filter @deepseek-ai/website run preview", - "docs:check": "pnpm exec vitest run scripts/project-doc-site.spec.ts && pnpm run docs:build", + "docs:check": "pnpm exec vitest run scripts/project-doc-site.spec.ts scripts/verify-doc-site-fragments.spec.ts && pnpm run docs:build", "website:dev": "pnpm run docs:dev", "website:build": "pnpm run docs:build", "verify-package-readme-limitations": "tsx scripts/verify-package-readme-limitations.ts", diff --git a/scripts/gen-config-catalog.ts b/scripts/gen-config-catalog.ts index 5abc85c896..83fe8e7809 100644 --- a/scripts/gen-config-catalog.ts +++ b/scripts/gen-config-catalog.ts @@ -12,6 +12,7 @@ import { dirname, resolve, sep } from 'node:path' import ts from 'typescript' import { LINK_MAP } from './gen-cordis-catalog.ts' import { parseJsDoc, pointer, rawJsDoc } from './jsdoc.ts' +import { githubSlug } from './verify-md-links.ts' const root = resolve(import.meta.dirname, '..') const OUT = 'docs/config-catalog.md' @@ -766,11 +767,6 @@ export function collectConfigCatalog(scanRoot: string = root): CatalogEntry[] { return entries.sort((a, b) => a.pkg.localeCompare(b.pkg)) } -/** GitHub-style anchor slug for a `## \`pkg\`` heading. */ -function slug(heading: string): string { - return heading.toLowerCase().replace(/[^a-z0-9 -]/g, '').replace(/ /g, '-') -} - /** Render the `Requires:` service-key line, or '' when the plugin injects nothing. */ function requiresLine(inject: string[]): string { return inject.length ? `Requires: ${inject.map(k => `\`${k}\``).join(' · ')}` : '' @@ -782,7 +778,7 @@ function requiresLine(inject: string[]): string { function refLink(ref: TypeRef, byName: Map): string { const target = byName.get(ref.specifier) if (target?.kind === 'config' && ref.imported === target.configTypeName) { - return `[\`${ref.alias}\`](#${slug(target.pkg)})` + return `[\`${ref.alias}\`](#${githubSlug(target.pkg)})` } const page = LINK_MAP[ref.imported] if (page) return `[\`${ref.alias}\`](subsystems/${page})` @@ -792,7 +788,7 @@ function refLink(ref: TypeRef, byName: Map): string { /** Render one configurable plugin's section. */ function renderConfigEntry(entry: CatalogEntry, byName: Map): string[] { - const out = [`## \`${entry.pkg}\``, ''] + const out = [``, '', `## \`${entry.pkg}\``, ''] const requires = requiresLine(entry.inject) if (requires) out.push(requires, '') out.push('```' + FENCE, ...(entry.pastes ?? []).map(p => p.text).join('\n\n').split('\n'), '```', '') diff --git a/scripts/gen-persistence-catalog.ts b/scripts/gen-persistence-catalog.ts index 5ca27b5260..38100fd531 100644 --- a/scripts/gen-persistence-catalog.ts +++ b/scripts/gen-persistence-catalog.ts @@ -10,6 +10,7 @@ import { globSync, readFileSync, writeFileSync } from 'node:fs' import { resolve, sep } from 'node:path' import ts from 'typescript' import { parseJsDoc, pointer, rawJsDoc, reportViolations } from './jsdoc.ts' +import { githubSlug } from './verify-md-links.ts' const root = resolve(import.meta.dirname, '..') const OUT = 'docs/persistence-catalog.md' @@ -341,7 +342,8 @@ function typeLinks(payload: string): string { /** Render one log event entry. */ function renderEvent(e: AnnotatedLogEventEntry): string[] { - const out = [`#### \`${e.name}\` — ${e.surface ? 'surface' : 'log-only'}`, ''] + const heading = `${e.name} — ${e.surface ? 'surface' : 'log-only'}` + const out = [``, '', `#### \`${e.name}\` — ${e.surface ? 'surface' : 'log-only'}`, ''] out.push('```' + FENCE, e.declaration, '```', '') const links = typeLinks(e.payload) if (links) out.push(links, '') diff --git a/scripts/gen-tool-catalog.ts b/scripts/gen-tool-catalog.ts index c6c7fb117f..005eb4aef9 100644 --- a/scripts/gen-tool-catalog.ts +++ b/scripts/gen-tool-catalog.ts @@ -63,6 +63,7 @@ import * as ToolWeb from '@deepseek-ai/dsh-tool-web' import VmWorkflowEngine from '@deepseek-ai/dsh-workflow-worker-thread' import * as ToolRalph from '@deepseek-ai/dsh-tool-ralph' import * as ToolWorkflow from '@deepseek-ai/dsh-tool-workflow' +import { githubSlug } from './verify-md-links.ts' /** Attachment seam marker that makes the attachments-conditional `read_image` schema harvestable. */ class CatalogAttachmentStore extends AttachmentStore { @@ -704,7 +705,7 @@ export function render(catalog: ToolCatalog): string { '', ] for (const entry of catalog) { - lines.push(`## \`${entry.pkg}\``, '') + lines.push(``, '', `## \`${entry.pkg}\``, '') for (const schema of entry.schemas) { // Collection validated that every harvested schema has a source. const source = entry.sources[schema.name] as string diff --git a/scripts/run-gates.ts b/scripts/run-gates.ts index e0b07041c0..9673439fbc 100644 --- a/scripts/run-gates.ts +++ b/scripts/run-gates.ts @@ -606,7 +606,7 @@ function docSyncLeafGates(options: { pnpmScript('translation-prompt', 'verify-translation-prompt', { label: 'translation prompt' }), pnpmScript('translation-pairing', 'verify-translation-pairing', { label: 'translation pairing' }), pnpmScript('doc-budgets', 'verify-doc-budgets', { label: 'doc budgets' }), - pnpmExec('docs-site-projection', ['vitest', 'run', 'scripts/project-doc-site.spec.ts'], { + pnpmExec('docs-site-projection', ['vitest', 'run', 'scripts/project-doc-site.spec.ts', 'scripts/verify-doc-site-fragments.spec.ts'], { label: 'documentation projection', }), // Keep the VitePress build itself in one gate because projection rewrites website/.generated. diff --git a/scripts/verify-doc-site-fragments.spec.ts b/scripts/verify-doc-site-fragments.spec.ts new file mode 100644 index 0000000000..2d041a33d5 --- /dev/null +++ b/scripts/verify-doc-site-fragments.spec.ts @@ -0,0 +1,67 @@ +/** Tests for built-site fragment validation. */ + +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 { inspectSiteFragments } from './verify-doc-site-fragments.ts' + +const roots: string[] = [] + +afterEach(() => { + for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }) +}) + +function fixture(): string { + const root = mkdtempSync(join(tmpdir(), 'dsh-doc-fragments-')) + roots.push(root) + mkdirSync(join(root, 'guide'), { recursive: true }) + writeFileSync(join(root, 'index.html'), 'start') + writeFileSync(join(root, 'guide/start.html'), [ + '

Ready

', + '', + 'same page', + 'html alias', + 'root', + 'external', + ].join('')) + return root +} + +describe('inspectSiteFragments', () => { + it('rejects a directory with no built pages', () => { + const root = mkdtempSync(join(tmpdir(), 'dsh-doc-fragments-empty-')) + roots.push(root) + + expect(() => inspectSiteFragments(root)).toThrow('no HTML files found') + }) + + it('resolves clean, encoded, and same-page routes', () => { + const root = fixture() + writeFileSync(join(root, 'guide/encoded.html'), '

Encoded

self') + + expect(inspectSiteFragments(root)).toEqual({ checked: 5, broken: [] }) + }) + + it('reports missing ids and missing built routes', () => { + const root = fixture() + writeFileSync(join(root, 'guide/broken.html'), [ + 'id', + 'route', + ].join('')) + + expect(inspectSiteFragments(root).broken).toEqual([ + { + source: 'guide/broken.html', + href: './start#missing', + target: 'guide/start.html', + fragment: 'missing', + }, + { + source: 'guide/broken.html', + href: './absent#missing', + fragment: 'missing', + }, + ]) + }) +}) diff --git a/scripts/verify-doc-site-fragments.ts b/scripts/verify-doc-site-fragments.ts new file mode 100644 index 0000000000..d42cc752b0 --- /dev/null +++ b/scripts/verify-doc-site-fragments.ts @@ -0,0 +1,143 @@ +/** + * Verify fragment links against the HTML emitted by VitePress. Markdown and + * VitePress use different heading-slug algorithms, so source-link validation + * alone cannot prove that a published fragment exists. + * + * Run with `tsx scripts/verify-doc-site-fragments.ts` after `docs:build`. + */ + +import { globSync, readFileSync } from 'node:fs' +import { resolve, sep } from 'node:path' +import { JSDOM } from 'jsdom' + +const root = resolve(import.meta.dirname, '..') + +/** One fragment reference that does not resolve in the built site. */ +export interface BrokenSiteFragment { + /** HTML file containing the link. */ + source: string + /** Link value as emitted by VitePress. */ + href: string + /** Built HTML target, or `undefined` when the route was not emitted. */ + target?: string + /** Decoded fragment id requested by the link. */ + fragment: string +} + +/** Result of checking every fragment-bearing anchor in a built site. */ +export interface SiteFragmentReport { + /** Number of internal fragment references inspected. */ + checked: number + /** References whose route or fragment id is absent. */ + broken: BrokenSiteFragment[] +} + +interface BuiltPage { + file: string + route: string + ids: Set + document: Document +} + +function posixPath(path: string): string { + return path.split(sep).join('/') +} + +function routeFor(file: string): string { + if (file === 'index.html') return '/' + if (file.endsWith('/index.html')) return `/${file.slice(0, -'index.html'.length)}` + return `/${file.slice(0, -'.html'.length)}` +} + +function aliasesFor(page: BuiltPage): string[] { + if (page.route === '/') return ['/', '/index', '/index.html'] + if (page.route.endsWith('/')) { + const stem = page.route.slice(0, -1) + return [page.route, stem, `${stem}/index`, `${stem}/index.html`] + } + return [page.route, `${page.route}.html`] +} + +function decodedFragment(hash: string): string { + try { + return decodeURIComponent(hash.slice(1)) + } catch { + return hash.slice(1) + } +} + +/** + * Check fragment-bearing links in a VitePress output directory. + * + * @param distRoot - Directory containing generated HTML files. + * @returns Counted internal links and every unresolved target. + */ +export function inspectSiteFragments(distRoot: string): SiteFragmentReport { + const files = globSync('**/*.html', { cwd: distRoot }).map(posixPath).sort() + if (files.length === 0) { + throw new Error(`verify-doc-site-fragments: no HTML files found under ${distRoot}; run docs:build first.`) + } + const pages: BuiltPage[] = files.map((file) => { + const document = new JSDOM(readFileSync(resolve(distRoot, file), 'utf8')).window.document + const ids = new Set() + for (const element of document.querySelectorAll('[id]')) ids.add(element.id) + for (const element of document.querySelectorAll('a[name]')) { + const name = element.getAttribute('name') + if (name !== null) ids.add(name) + } + return { file, route: routeFor(file), ids, document } + }) + + const byRoute = new Map() + for (const page of pages) { + for (const alias of aliasesFor(page)) byRoute.set(alias, page) + } + + const origin = 'https://dsh-docs.invalid' + const broken: BrokenSiteFragment[] = [] + let checked = 0 + for (const page of pages) { + for (const anchor of page.document.querySelectorAll('a[href]')) { + const href = anchor.getAttribute('href') + if (href === null || !href.includes('#')) continue + let targetUrl: URL + try { + targetUrl = new URL(href, `${origin}${page.route}`) + } catch { + continue + } + if (targetUrl.origin !== origin || targetUrl.hash === '') continue + const fragment = decodedFragment(targetUrl.hash) + if (fragment === '') continue + checked++ + const target = byRoute.get(targetUrl.pathname) + if (target === undefined || !target.ids.has(fragment)) { + broken.push({ + source: page.file, + href, + ...(target === undefined ? {} : { target: target.file }), + fragment, + }) + } + } + } + return { checked, broken } +} + +function main(): number { + const distRoot = resolve(root, 'website/.dist') + const report = inspectSiteFragments(distRoot) + if (report.broken.length === 0) { + console.log(`verify-doc-site-fragments: ${report.checked} internal fragment reference(s) resolve.`) + return 0 + } + + console.error(`verify-doc-site-fragments: ${report.broken.length} broken fragment reference(s):`) + for (const item of report.broken) { + const target = item.target === undefined ? 'target route was not built' : `${item.target} has no id ${JSON.stringify(item.fragment)}` + console.error(` ${item.source}: ${JSON.stringify(item.href)} (${target})`) + } + return 1 +} + +if (import.meta.main) process.exitCode = main() From bca790481c02c710e1c8b339efdf1ec7c2404370 Mon Sep 17 00:00:00 2001 From: Turtle Date: Thu, 13 Aug 2026 13:58:06 +0800 Subject: [PATCH 2/4] docs: document stable fragment aliases --- .agents/skills/dsh-doc-site-sync/SKILL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.agents/skills/dsh-doc-site-sync/SKILL.md b/.agents/skills/dsh-doc-site-sync/SKILL.md index 5d39470d72..bc5b3f48dd 100644 --- a/.agents/skills/dsh-doc-site-sync/SKILL.md +++ b/.agents/skills/dsh-doc-site-sync/SKILL.md @@ -49,6 +49,7 @@ Write normal repository-relative Markdown links in canonical docs. The projector - An image is the exception: its file is copied into the generated tree and referenced from there, so the site serves it regardless of repository visibility. It must be a regular file inside the repository. - External URLs, site-absolute URLs, email links, and fragment-only links remain unchanged. - A missing repository-relative target fails projection instead of silently producing a broken link. +- Cross-language and generated-document fragments use the English GitHub heading id as their canonical id. If a translated or generated heading emits another id, place an explicit `` immediately before it; add generated aliases in the owning generator. Do not write website-specific routes into canonical Markdown just to satisfy VitePress. Use `sourceAliases` for directory-style repository links that should resolve to a mapped index page. From 4201bbceca8104f6a26013e49c44239b6a257929 Mon Sep 17 00:00:00 2001 From: Turtle Date: Thu, 13 Aug 2026 14:02:07 +0800 Subject: [PATCH 3/4] docs: use installed dsh command in publish tutorial --- docs/user/develop/basic/publish.i18n.yaml | 4 ++-- docs/user/develop/basic/publish.md | 26 +++++++++++------------ docs/user/develop/basic/publish.zh.md | 26 +++++++++++------------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/user/develop/basic/publish.i18n.yaml b/docs/user/develop/basic/publish.i18n.yaml index 694a7ff663..e640e971cb 100644 --- a/docs/user/develop/basic/publish.i18n.yaml +++ b/docs/user/develop/basic/publish.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/user/develop/basic/publish.md -publish.md: edc85b74049c5b993efb02663195fcc0001b4620 -publish.zh.md: cb824ad12eec0d706efa17b28f3bdc874ba59131 +publish.md: 8283f9e7ff0c28580343975d67c5715d17c53074 +publish.zh.md: 5a87901fe39aa00e94db81dc840e39a52e0cc88c diff --git a/docs/user/develop/basic/publish.md b/docs/user/develop/basic/publish.md index edc85b7404..8283f9e7ff 100644 --- a/docs/user/develop/basic/publish.md +++ b/docs/user/develop/basic/publish.md @@ -2,7 +2,7 @@ English | [中文](publish.zh.md) -The previous tutorials loaded a local plugin through a `--patch` overlay. This tutorial packages it as an installable **bundle**, installs it into a **profile** with `pnpm dsh plugin add`, and explains the layer order that determines the composed configuration. Complete [plugin configuration](./config.md) first. +The previous tutorials loaded a local plugin through a `--patch` overlay. This tutorial packages it as an installable **bundle**, installs it into a **profile** with `dsh plugin add`, and explains the layer order that determines the composed configuration. It assumes the `dsh` CLI is installed. Complete [plugin configuration](./config.md) first. ## Two concepts, two manifests @@ -11,11 +11,11 @@ Installation is built on two concepts. Both are described by a `package.json`, b - A **bundle** is an npm package that ships a configuration layer. Its manifest declares `dsh.bundle`, answering "what does this package contribute?": a patch file that inserts or overrides plugin rows. - A **profile** is a directory under `$DSH_HOME/profiles/` describing one runnable composition. Its manifest declares `dsh.profile`, answering "which bundles compose this setup, in what order?". -A bundle is what you author and distribute; a profile is what a user boots from this source checkout with `pnpm dsh --profile `. Nothing is both. +A bundle is what you author and distribute; a profile is what a user boots with `dsh --profile `. Nothing is both. ### The bundle manifest -From the repository root, create the package directory: +Create the package directory: ```sh mkdir -p hello-plugin @@ -59,7 +59,7 @@ Create `hello-plugin/cordis.patch.yml`. The patch is a YAML array like the `--pa name: dsh-hello-plugin ``` -A package without the `dsh.bundle` declaration still installs, but only as a plain dependency: `pnpm dsh plugin` prints a warning and activates no layer. Use that package format for a library that plugin packages import rather than a plugin users enable. +A package without the `dsh.bundle` declaration still installs, but only as a plain dependency: `dsh plugin` prints a warning and activates no layer. Use that package format for a library that plugin packages import rather than a plugin users enable. ### The profile manifest @@ -68,14 +68,14 @@ A profile directory holds two files: - `package.json` — the profile's out-of-tree plugin dependencies (managed by pnpm) plus the `dsh.profile` manifest with its ordered `bundles` list. - `cordis.patch.yml` — the user's own patch layer, applied after every bundle layer. -You never write a profile manifest by hand: `pnpm dsh plugin` creates and maintains it. The next section shows the result. +You never write a profile manifest by hand: `dsh plugin` creates and maintains it. The next section shows the result. ## Install into a profile -`pnpm dsh plugin --profile ` forwards to pnpm in the profile directory, so every pnpm verb works. From the repository root, install the package checkout: +`dsh plugin --profile ` forwards to pnpm in the profile directory, so every pnpm verb works. From the directory that contains `hello-plugin`, install the package checkout: ```sh -pnpm dsh plugin --profile demo add ./hello-plugin +dsh plugin --profile demo add ./hello-plugin ``` The first use initializes the profile (with `@deepseek-ai/dsh-base` as its first bundle), pnpm links the checkout, and `dsh` appends the bundle to `dsh.profile.bundles` because the package declares `dsh.bundle`: @@ -101,11 +101,11 @@ The first use initializes the profile (with `@deepseek-ai/dsh-base` as its first Verify the layer without booting, then boot: ```sh -pnpm dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer -pnpm dsh --profile demo +dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer +dsh --profile demo ``` -`pnpm dsh plugin --profile demo remove dsh-hello-plugin` removes both the dependency and the layer. +`dsh plugin --profile demo remove dsh-hello-plugin` removes both the dependency and the layer. ## The loading order @@ -153,7 +153,7 @@ On `--help`, the provider publishes no service, so those rows never activate. Lo Publishing to a registry is not required — users can install straight from a git host: ```sh -pnpm dsh plugin --profile demo add github:you/hello-plugin +dsh plugin --profile demo add github:you/hello-plugin ``` But a git install fetches **sources, not built artifacts**: nothing runs your `build` script, so a TypeScript package arrives without its `lib/` output and fails to load. Two things must happen, one on each side: @@ -172,8 +172,8 @@ Treat that allowance as what it is: **permission to execute the package's code o If you would rather not ask users for the allowance, distribute built artifacts instead — neither form needs any build permission: -- **Publish to npm** with `lib/` built at `pnpm publish` time; `pnpm dsh plugin add your-package` then installs prebuilt code. -- **Ship a tarball** from `pnpm pack`; users run `pnpm dsh plugin add ./hello-plugin-0.1.0.tgz`. +- **Publish to npm** with `lib/` built at `pnpm publish` time; `dsh plugin add your-package` then installs prebuilt code. +- **Ship a tarball** from `pnpm pack`; users run `dsh plugin add ./hello-plugin-0.1.0.tgz`. ## Next steps diff --git a/docs/user/develop/basic/publish.zh.md b/docs/user/develop/basic/publish.zh.md index cb824ad12e..5a87901fe3 100644 --- a/docs/user/develop/basic/publish.zh.md +++ b/docs/user/develop/basic/publish.zh.md @@ -2,7 +2,7 @@ [English](publish.md) | 中文 -前几篇教程通过 `--patch` overlay 加载本地插件。本教程把它打包成可安装的**组合包**(bundle),用 `pnpm dsh plugin add` 安装进一个 **profile**,并解释决定组合后配置的层顺序。请先完成[插件配置](./config.md)。 +前几篇教程通过 `--patch` overlay 加载本地插件。本教程把它打包成可安装的**组合包**(bundle),用 `dsh plugin add` 安装进一个 **profile**,并解释决定组合后配置的层顺序。本文假设 `dsh` CLI 已安装。请先完成[插件配置](./config.md)。 ## 两个概念,两种 manifest @@ -11,11 +11,11 @@ - **组合包**是附带一个配置层的 npm 包。它的 manifest 声明 `dsh.bundle`,回答的是"这个包贡献什么?":一个插入或覆盖插件行的 patch 文件。 - **profile** 是位于 `$DSH_HOME/profiles/` 下、描述一份可启动组合的目录。它的 manifest 声明 `dsh.profile`,回答的是"这套配置由哪些组合包按什么顺序组成?"。 -组合包是你编写并分发的东西;profile 是用户在当前源码 checkout 中用 `pnpm dsh --profile ` 启动的东西。没有东西同时是两者。 +组合包是你编写并分发的东西;profile 是用户用 `dsh --profile ` 启动的东西。没有东西同时是两者。 ### 组合包 manifest -在仓库根目录创建包目录: +创建包目录: ```sh mkdir -p hello-plugin @@ -59,7 +59,7 @@ export function apply() { name: dsh-hello-plugin ``` -没有 `dsh.bundle` 声明的包仍然可以安装,但只作为普通依赖:`pnpm dsh plugin` 会打印警告,且不激活任何层。如果一个库供插件包 import,而不是供用户启用,就使用这种包格式。 +没有 `dsh.bundle` 声明的包仍然可以安装,但只作为普通依赖:`dsh plugin` 会打印警告,且不激活任何层。如果一个库供插件包 import,而不是供用户启用,就使用这种包格式。 ### profile manifest @@ -68,14 +68,14 @@ profile 目录包含两个文件: - `package.json` — profile 的树外插件依赖(由 pnpm 管理),加上 `dsh.profile` manifest 及其有序的 `bundles` 列表。 - `cordis.patch.yml` — 用户自己的 patch 层,在每个组合包层之后应用。 -profile manifest 从不需要手写:`pnpm dsh plugin` 负责创建和维护它。下一节展示其结果。 +profile manifest 从不需要手写:`dsh plugin` 负责创建和维护它。下一节展示其结果。 ## 安装进 profile -`pnpm dsh plugin --profile ` 在 profile 目录内转发给 pnpm,因此所有 pnpm 子命令都可用。在仓库根目录安装该包的 checkout: +`dsh plugin --profile ` 在 profile 目录内转发给 pnpm,因此所有 pnpm 子命令都可用。在包含 `hello-plugin` 的目录中安装该包的 checkout: ```sh -pnpm dsh plugin --profile demo add ./hello-plugin +dsh plugin --profile demo add ./hello-plugin ``` 首次使用会初始化 profile(`@deepseek-ai/dsh-base` 作为它的第一个组合包),pnpm 链接该 checkout,而 `dsh` 因为这个包声明了 `dsh.bundle`,把它追加进 `dsh.profile.bundles`: @@ -101,11 +101,11 @@ pnpm dsh plugin --profile demo add ./hello-plugin 先不启动、只验证该层,再启动: ```sh -pnpm dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer -pnpm dsh --profile demo +dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer +dsh --profile demo ``` -`pnpm dsh plugin --profile demo remove dsh-hello-plugin` 会同时移除依赖和对应的层。 +`dsh plugin --profile demo remove dsh-hello-plugin` 会同时移除依赖和对应的层。 ## 加载顺序 @@ -153,7 +153,7 @@ pnpm dsh --profile demo 发布到注册表不是必须的——用户可以直接从 git 托管安装: ```sh -pnpm dsh plugin --profile demo add github:you/hello-plugin +dsh plugin --profile demo add github:you/hello-plugin ``` 但 git 安装拉取的是**源码,不是构建产物**:没有任何环节运行你的 `build` 脚本,因此 TypeScript 包到手时没有 `lib/` 输出,加载会失败。必须两边各做一件事: @@ -172,8 +172,8 @@ pnpm dsh plugin --profile demo add github:you/hello-plugin 如果不想让用户做这项授权,就改为分发构建产物——以下两种形式都不需要任何构建权限: -- **发布到 npm**,在 `pnpm publish` 时构建好 `lib/`;`pnpm dsh plugin add your-package` 安装的就是预构建代码。 -- **交付 tarball**:用 `pnpm pack` 打包;用户执行 `pnpm dsh plugin add ./hello-plugin-0.1.0.tgz`。 +- **发布到 npm**,在 `pnpm publish` 时构建好 `lib/`;`dsh plugin add your-package` 安装的就是预构建代码。 +- **交付 tarball**:用 `pnpm pack` 打包;用户执行 `dsh plugin add ./hello-plugin-0.1.0.tgz`。 ## 下一步 From 3a1a4e2cd864e449cf3cd9de837a29b2bea35f7c Mon Sep 17 00:00:00 2001 From: Turtle Date: Thu, 13 Aug 2026 14:30:17 +0800 Subject: [PATCH 4/4] docs: address fragment review feedback --- ...-13-published-document-fragments.i18n.yaml | 4 ++-- ...2026-08-13-published-document-fragments.md | 4 ++-- ...6-08-13-published-document-fragments.zh.md | 4 ++-- .agents/skills/dsh-doc-site-sync/SKILL.md | 4 +++- scripts/run-gates.ts | 2 +- scripts/verify-doc-site-fragments.spec.ts | 24 +++++++++++++++++-- scripts/verify-doc-site-fragments.ts | 24 +++++++++++++++---- 7 files changed, 51 insertions(+), 15 deletions(-) diff --git a/.agents/notes/implemented/process/2026-08-13-published-document-fragments.i18n.yaml b/.agents/notes/implemented/process/2026-08-13-published-document-fragments.i18n.yaml index 1051ae0b32..d90edf5075 100644 --- a/.agents/notes/implemented/process/2026-08-13-published-document-fragments.i18n.yaml +++ b/.agents/notes/implemented/process/2026-08-13-published-document-fragments.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-13-published-document-fragments.md -2026-08-13-published-document-fragments.md: 9837d4ec1335f8da9910b3d2c2167f4c6d3a1aab -2026-08-13-published-document-fragments.zh.md: f4c68ae01082ab53a251c9951906538f0f4ae770 +2026-08-13-published-document-fragments.md: 4146a592a97b5d8b4e1d3fbabd0f035074cc8001 +2026-08-13-published-document-fragments.zh.md: 6cf76dfa349bbd61d1dd51cb683b696e4c405435 diff --git a/.agents/notes/implemented/process/2026-08-13-published-document-fragments.md b/.agents/notes/implemented/process/2026-08-13-published-document-fragments.md index 9837d4ec13..4146a592a9 100644 --- a/.agents/notes/implemented/process/2026-08-13-published-document-fragments.md +++ b/.agents/notes/implemented/process/2026-08-13-published-document-fragments.md @@ -10,9 +10,9 @@ English | [中文](2026-08-13-published-document-fragments.zh.md) ## Decision -`docs:build` and its MPA variant run `verify-doc-site-fragments` after VitePress emits `website/.dist`. The verifier parses every emitted HTML page, resolves each internal fragment link against VitePress clean URLs, and fails when the output is absent or either the target page or requested id is missing. Unit tests cover absent output, clean URLs, `.html` aliases, same-page links, encoded ids, missing ids, missing routes, and external-link exclusion. +`docs:build` and its MPA variant run `verify-doc-site-fragments` after VitePress emits `website/.dist`. The verifier parses every emitted HTML page, resolves each internal fragment link against VitePress clean URLs, and fails when the output is absent, routes are ambiguous, an href is malformed, or either the target page or requested id is missing. Unit tests cover those failures plus clean URLs, `.html` aliases, same-page links, encoded and literal ids, and external-link exclusion. -Generated config, tool, and persistence catalogs emit explicit GitHub-compatible id aliases before punctuation-heavy headings. Authored translated pages add explicit language-neutral aliases when their localized VitePress heading id differs from the shared fragment used by the bilingual pair. Source Markdown validation remains independent and continues to reject links that do not resolve under repository rendering. +Any fragment target heading whose GitHub id differs from its VitePress id carries an explicit GitHub-compatible alias. Authored English and translated pages place the alias before the heading; translated pages use the English id shared by the bilingual pair. Generated config, tool, and persistence catalogs emit the alias from their owning generator. Source Markdown validation remains independent and continues to reject links that do not resolve under repository rendering. ## Alternatives considered diff --git a/.agents/notes/implemented/process/2026-08-13-published-document-fragments.zh.md b/.agents/notes/implemented/process/2026-08-13-published-document-fragments.zh.md index f4c68ae010..6cf76dfa34 100644 --- a/.agents/notes/implemented/process/2026-08-13-published-document-fragments.zh.md +++ b/.agents/notes/implemented/process/2026-08-13-published-document-fragments.zh.md @@ -10,9 +10,9 @@ Status: implemented ## Decision -`docs:build` 及其 MPA 变体会在 VitePress 生成 `website/.dist` 后运行 `verify-doc-site-fragments`。该校验器解析每个生成的 HTML 页面,按照 VitePress clean URL 解析每个内部 fragment 链接,并在构建产物不存在、目标页面不存在或请求的 id 缺失时失败。单元测试覆盖缺失构建产物、clean URL、`.html` 别名、同页链接、编码 id、缺失 id、缺失路由和外部链接排除。 +`docs:build` 及其 MPA 变体会在 VitePress 生成 `website/.dist` 后运行 `verify-doc-site-fragments`。该校验器解析每个生成的 HTML 页面,按照 VitePress clean URL 解析每个内部 fragment 链接,并在构建产物不存在、路由有歧义、href 格式错误、目标页面不存在或请求的 id 缺失时失败。单元测试覆盖这些失败,以及 clean URL、`.html` 别名、同页链接、编码和字面 id 与外部链接排除。 -生成的配置、工具和持久化目录会在包含较多标点的标题前生成与 GitHub 兼容的显式 id 别名。如果翻译页面的 VitePress 本地化标题 id 与双语对侧文件共享的 fragment 不同,编写者会添加语言无关的显式别名。源码 Markdown 校验保持独立,仍会拒绝在仓库渲染规则下无法解析的链接。 +任何 GitHub id 与 VitePress id 不同的 fragment 目标标题都会带有与 GitHub 兼容的显式别名。英文手写页面和翻译页面会在标题前添加别名;翻译页面使用双语对侧文件共享的英文 id。生成的配置、工具和持久化目录由所属生成器输出别名。源码 Markdown 校验保持独立,仍会拒绝在仓库渲染规则下无法解析的链接。 ## Alternatives considered diff --git a/.agents/skills/dsh-doc-site-sync/SKILL.md b/.agents/skills/dsh-doc-site-sync/SKILL.md index bc5b3f48dd..91dd42ae8a 100644 --- a/.agents/skills/dsh-doc-site-sync/SKILL.md +++ b/.agents/skills/dsh-doc-site-sync/SKILL.md @@ -49,7 +49,7 @@ Write normal repository-relative Markdown links in canonical docs. The projector - An image is the exception: its file is copied into the generated tree and referenced from there, so the site serves it regardless of repository visibility. It must be a regular file inside the repository. - External URLs, site-absolute URLs, email links, and fragment-only links remain unchanged. - A missing repository-relative target fails projection instead of silently producing a broken link. -- Cross-language and generated-document fragments use the English GitHub heading id as their canonical id. If a translated or generated heading emits another id, place an explicit `` immediately before it; add generated aliases in the owning generator. +- Cross-page fragments use the English GitHub heading id as their canonical id. If an authored heading emits a different VitePress id, place an explicit `` immediately before it; add generated aliases in the owning generator. Do not write website-specific routes into canonical Markdown just to satisfy VitePress. Use `sourceAliases` for directory-style repository links that should resolve to a mapped index page. @@ -69,6 +69,8 @@ Run the focused website gate before treating the mapping as valid: pnpm docs:check ``` +If Markdown link checks pass but the site build reports a missing fragment, follow the `verify-doc-site-fragments` source and target paths. Preserve the English GitHub id with an explicit alias in authored Markdown or in the owning generator. + Before committing a documentation-site change, run: ```sh diff --git a/scripts/run-gates.ts b/scripts/run-gates.ts index 9673439fbc..27664fab5e 100644 --- a/scripts/run-gates.ts +++ b/scripts/run-gates.ts @@ -607,7 +607,7 @@ function docSyncLeafGates(options: { pnpmScript('translation-pairing', 'verify-translation-pairing', { label: 'translation pairing' }), pnpmScript('doc-budgets', 'verify-doc-budgets', { label: 'doc budgets' }), pnpmExec('docs-site-projection', ['vitest', 'run', 'scripts/project-doc-site.spec.ts', 'scripts/verify-doc-site-fragments.spec.ts'], { - label: 'documentation projection', + label: 'documentation site checks', }), // Keep the VitePress build itself in one gate because projection rewrites website/.generated. pnpmScript('docs-site-build', options.docsBuildScript ?? 'docs:build', { label: 'documentation build' }), diff --git a/scripts/verify-doc-site-fragments.spec.ts b/scripts/verify-doc-site-fragments.spec.ts index 2d041a33d5..d06d46f53b 100644 --- a/scripts/verify-doc-site-fragments.spec.ts +++ b/scripts/verify-doc-site-fragments.spec.ts @@ -38,9 +38,29 @@ describe('inspectSiteFragments', () => { it('resolves clean, encoded, and same-page routes', () => { const root = fixture() - writeFileSync(join(root, 'guide/encoded.html'), '

Encoded

self') + writeFileSync( + join(root, 'guide/encoded.html'), + '

Encoded

Literal

encodedliteral', + ) - expect(inspectSiteFragments(root)).toEqual({ checked: 5, broken: [] }) + expect(inspectSiteFragments(root)).toEqual({ checked: 6, broken: [] }) + }) + + it('rejects ambiguous built routes', () => { + const root = fixture() + writeFileSync(join(root, 'guide.html'), '

Flat

') + writeFileSync(join(root, 'guide/index.html'), '

Index

') + + expect(() => inspectSiteFragments(root)).toThrow('share route "/guide"') + }) + + it('rejects malformed fragment hrefs', () => { + const root = fixture() + writeFileSync(join(root, 'guide/invalid.html'), 'invalid') + + expect(() => inspectSiteFragments(root)).toThrow( + 'guide/invalid.html has invalid fragment href "http://[invalid]#fragment"', + ) }) it('reports missing ids and missing built routes', () => { diff --git a/scripts/verify-doc-site-fragments.ts b/scripts/verify-doc-site-fragments.ts index d42cc752b0..c086dedccb 100644 --- a/scripts/verify-doc-site-fragments.ts +++ b/scripts/verify-doc-site-fragments.ts @@ -3,7 +3,8 @@ * VitePress use different heading-slug algorithms, so source-link validation * alone cannot prove that a published fragment exists. * - * Run with `tsx scripts/verify-doc-site-fragments.ts` after `docs:build`. + * This runs as part of `docs:build` and can also run directly after a build + * with `tsx scripts/verify-doc-site-fragments.ts`. */ import { globSync, readFileSync } from 'node:fs' @@ -61,7 +62,9 @@ function aliasesFor(page: BuiltPage): string[] { function decodedFragment(hash: string): string { try { return decodeURIComponent(hash.slice(1)) - } catch { + } catch (error) { + if (!(error instanceof URIError)) throw error + // URIError means malformed percent encoding; preserve the literal id for comparison. return hash.slice(1) } } @@ -90,7 +93,15 @@ export function inspectSiteFragments(distRoot: string): SiteFragmentReport { const byRoute = new Map() for (const page of pages) { - for (const alias of aliasesFor(page)) byRoute.set(alias, page) + for (const alias of aliasesFor(page)) { + const existing = byRoute.get(alias) + if (existing !== undefined && existing !== page) { + throw new Error( + `verify-doc-site-fragments: built pages ${existing.file} and ${page.file} share route ${JSON.stringify(alias)}.`, + ) + } + byRoute.set(alias, page) + } } const origin = 'https://dsh-docs.invalid' @@ -103,8 +114,11 @@ export function inspectSiteFragments(distRoot: string): SiteFragmentReport { let targetUrl: URL try { targetUrl = new URL(href, `${origin}${page.route}`) - } catch { - continue + } catch (error) { + throw new Error( + `verify-doc-site-fragments: ${page.file} has invalid fragment href ${JSON.stringify(href)}.`, + { cause: error }, + ) } if (targetUrl.origin !== origin || targetUrl.hash === '') continue const fragment = decodedFragment(targetUrl.hash)