From 0b5eba0c8d59e8310cd0050a21832c5840ee0cfb Mon Sep 17 00:00:00 2001 From: Magolor Date: Tue, 25 Aug 2026 23:47:20 +0800 Subject: [PATCH] docs: rebuild the documentation skill and standards (#2983) --- ...2026-07-04-doc-tiers-and-budgets.i18n.yaml | 4 +- .../2026-07-04-doc-tiers-and-budgets.md | 2 +- .../2026-07-04-doc-tiers-and-budgets.zh.md | 2 +- ...-07-22-product-first-root-readme.i18n.yaml | 4 +- .../2026-07-22-product-first-root-readme.md | 8 +- ...2026-07-22-product-first-root-readme.zh.md | 8 +- ...-27-explicit-change-scope-report.i18n.yaml | 4 +- ...2026-07-27-explicit-change-scope-report.md | 2 +- ...6-07-27-explicit-change-scope-report.zh.md | 2 +- ...-31-coverage-exempt-heavy-suites.i18n.yaml | 4 +- ...2026-07-31-coverage-exempt-heavy-suites.md | 4 + ...6-07-31-coverage-exempt-heavy-suites.zh.md | 4 + ...8-native-windows-pull-request-ci.i18n.yaml | 4 +- ...26-08-08-native-windows-pull-request-ci.md | 2 +- ...08-08-native-windows-pull-request-ci.zh.md | 2 +- ...26-08-09-md-fragment-anchor-gate.i18n.yaml | 4 +- .../2026-08-09-md-fragment-anchor-gate.md | 2 +- .../2026-08-09-md-fragment-anchor-gate.zh.md | 2 +- ...ence-first-documentation-quality.i18n.yaml | 6 + ...20-audience-first-documentation-quality.md | 138 +++++++++ ...audience-first-documentation-quality.zh.md | 138 +++++++++ .agents/skills/.gitignore | 1 + .agents/skills/dsh-doc-standards/SKILL.md | 56 ---- .agents/skills/dsh-doc/SKILL.md | 128 +++++++++ .../dsh-doc/references/metadata-links-i18n.md | 74 +++++ .agents/skills/dsh-doc/references/review.md | 67 +++++ .../dsh-doc/references/structure-hierarchy.md | 87 ++++++ .agents/skills/dsh-doc/references/style.md | 49 ++++ .../references/website-sync.md} | 47 ++-- .../dsh-doc/templates/package-bundle.md | 103 +++++++ .../skills/dsh-doc/templates/package-group.md | 59 ++++ .../dsh-doc/templates/package-library.md | 96 +++++++ .../dsh-doc/templates/package-reference.md | 103 +++++++ .../skills/dsh-find-simplifications/SKILL.md | 2 +- .agents/skills/dsh-prose-standard/SKILL.md | 2 +- .agents/skills/dsh-trim-cot-leakage/SKILL.md | 2 +- AGENTS.md | 17 +- README.i18n.yaml | 4 +- README.md | 6 +- README.zh.md | 6 +- apps/cli/README.i18n.yaml | 4 +- apps/cli/README.md | 3 +- apps/cli/README.zh.md | 3 +- apps/cli/reference/README.i18n.yaml | 4 +- apps/cli/reference/README.md | 3 +- apps/cli/reference/README.zh.md | 3 +- docs/AGENTS.md | 6 +- docs/api-gateway.i18n.yaml | 4 +- docs/api-gateway.md | 8 +- docs/api-gateway.zh.md | 8 +- docs/architecture.i18n.yaml | 4 +- docs/architecture.md | 4 +- docs/architecture.zh.md | 4 +- docs/capability-seams.i18n.yaml | 4 +- docs/capability-seams.md | 119 ++++---- docs/capability-seams.zh.md | 119 ++++---- docs/config-catalog.i18n.yaml | 4 +- docs/config-catalog.md | 2 +- docs/config-catalog.zh.md | 2 +- docs/cookbook/adding-a-package.i18n.yaml | 4 +- docs/cookbook/adding-a-package.md | 4 +- docs/cookbook/adding-a-package.zh.md | 4 +- docs/cookbook/adding-a-tool.i18n.yaml | 4 +- docs/cookbook/adding-a-tool.md | 1 + docs/cookbook/adding-a-tool.zh.md | 1 + .../adding-a-vendored-package.i18n.yaml | 4 +- docs/cookbook/adding-a-vendored-package.md | 4 +- docs/cookbook/adding-a-vendored-package.zh.md | 4 +- docs/cookbook/adding-an-llm-adapter.i18n.yaml | 4 +- docs/cookbook/adding-an-llm-adapter.md | 2 +- docs/cookbook/adding-an-llm-adapter.zh.md | 2 +- docs/cookbook/extension-cookbook.i18n.yaml | 4 +- docs/cookbook/extension-cookbook.md | 6 +- docs/cookbook/extension-cookbook.zh.md | 6 +- docs/cordis-api/inherited.md | 2 +- docs/cordis-primer.i18n.yaml | 4 +- docs/cordis-primer.md | 3 +- docs/cordis-primer.zh.md | 3 +- docs/development.i18n.yaml | 4 +- docs/development.md | 6 +- docs/development.zh.md | 6 +- docs/glossary.i18n.yaml | 4 +- docs/glossary.md | 2 +- docs/glossary.zh.md | 2 +- docs/i18n/README.i18n.yaml | 4 +- docs/i18n/README.md | 6 +- docs/i18n/README.zh.md | 6 +- ...ession-disabled-filesystem-tools.i18n.yaml | 4 +- ...js-expression-disabled-filesystem-tools.md | 2 +- ...expression-disabled-filesystem-tools.zh.md | 2 +- ...0003-web-agent-gui-feedback-loop.i18n.yaml | 4 +- .../0003-web-agent-gui-feedback-loop.md | 4 +- .../0003-web-agent-gui-feedback-loop.zh.md | 4 +- docs/rescope.i18n.yaml | 4 +- docs/rescope.md | 4 +- docs/rescope.zh.md | 4 +- docs/subsystems/README.i18n.yaml | 4 +- docs/subsystems/README.md | 2 +- docs/subsystems/README.zh.md | 2 +- docs/subsystems/agent-team.i18n.yaml | 4 +- docs/subsystems/agent-team.md | 2 +- docs/subsystems/agent-team.zh.md | 2 +- docs/subsystems/compaction.i18n.yaml | 4 +- docs/subsystems/compaction.md | 2 +- docs/subsystems/compaction.zh.md | 2 +- docs/subsystems/core.i18n.yaml | 4 +- docs/subsystems/core.md | 7 +- docs/subsystems/core.zh.md | 7 +- docs/subsystems/feedback.i18n.yaml | 4 +- docs/subsystems/feedback.md | 2 +- docs/subsystems/feedback.zh.md | 2 +- docs/subsystems/jobs.i18n.yaml | 4 +- docs/subsystems/jobs.md | 2 +- docs/subsystems/jobs.zh.md | 2 +- docs/subsystems/llm-streaming.i18n.yaml | 4 +- docs/subsystems/llm-streaming.md | 2 +- docs/subsystems/llm-streaming.zh.md | 2 +- docs/subsystems/permission-presets.i18n.yaml | 2 +- docs/subsystems/permission-presets.md | 2 +- docs/subsystems/session-projection.i18n.yaml | 4 +- docs/subsystems/session-projection.md | 6 +- docs/subsystems/session-projection.zh.md | 6 +- docs/subsystems/session-query.i18n.yaml | 4 +- docs/subsystems/session-query.md | 2 +- docs/subsystems/session-query.zh.md | 2 +- docs/subsystems/session.i18n.yaml | 4 +- docs/subsystems/session.md | 2 +- docs/subsystems/session.zh.md | 2 +- docs/subsystems/settings.i18n.yaml | 4 +- docs/subsystems/settings.md | 2 +- docs/subsystems/settings.zh.md | 2 +- docs/subsystems/spill.i18n.yaml | 4 +- docs/subsystems/spill.md | 2 +- docs/subsystems/spill.zh.md | 2 +- docs/subsystems/subagent.i18n.yaml | 4 +- docs/subsystems/subagent.md | 2 +- docs/subsystems/subagent.zh.md | 2 +- docs/subsystems/webhook.i18n.yaml | 4 +- docs/subsystems/webhook.md | 2 +- docs/subsystems/webhook.zh.md | 2 +- docs/subsystems/workspace.i18n.yaml | 4 +- docs/subsystems/workspace.md | 4 +- docs/subsystems/workspace.zh.md | 4 +- docs/testing.i18n.yaml | 4 +- docs/testing.md | 10 +- docs/testing.zh.md | 4 +- docs/user/develop/basic/publish.i18n.yaml | 4 +- docs/user/develop/basic/publish.md | 4 +- docs/user/develop/basic/publish.zh.md | 4 +- docs/user/develop/framework/events.i18n.yaml | 4 +- docs/user/develop/framework/events.md | 2 +- docs/user/develop/framework/events.zh.md | 2 +- docs/user/guide/github-review.i18n.yaml | 4 +- docs/user/guide/github-review.md | 2 +- docs/user/guide/github-review.zh.md | 2 +- docs/user/guide/mcp-memory.i18n.yaml | 4 +- docs/user/guide/mcp-memory.md | 2 +- docs/user/guide/mcp-memory.zh.md | 2 +- package.json | 1 + packages/README.i18n.yaml | 4 +- packages/README.md | 156 +++++++---- packages/README.zh.md | 158 +++++++---- packages/acp/README.i18n.yaml | 4 +- packages/acp/README.md | 36 ++- packages/acp/README.zh.md | 38 ++- packages/acp/acp/README.i18n.yaml | 4 +- packages/acp/acp/README.md | 189 +++++++++---- packages/acp/acp/README.zh.md | 195 +++++++++---- packages/api/README.i18n.yaml | 4 +- packages/api/README.md | 53 +++- packages/api/README.zh.md | 55 +++- packages/api/gateway/README.i18n.yaml | 4 +- packages/api/gateway/README.md | 35 ++- packages/api/gateway/README.zh.md | 33 +++ packages/api/remotes/README.i18n.yaml | 4 +- packages/api/remotes/README.md | 41 ++- packages/api/remotes/README.zh.md | 42 ++- .../api/session-controller/README.i18n.yaml | 4 +- packages/api/session-controller/README.md | 48 +++- packages/api/session-controller/README.zh.md | 48 +++- .../api/workspace-controller/README.i18n.yaml | 4 +- packages/api/workspace-controller/README.md | 36 ++- .../api/workspace-controller/README.zh.md | 36 ++- packages/attachment/README.i18n.yaml | 4 +- packages/attachment/README.md | 49 +++- packages/attachment/README.zh.md | 47 +++- .../attachment-local/README.i18n.yaml | 4 +- .../attachment/attachment-local/README.md | 143 +++++++++- .../attachment/attachment-local/README.zh.md | 147 +++++++++- .../attachment/attachment/README.i18n.yaml | 4 +- packages/attachment/attachment/README.md | 127 ++++++++- packages/attachment/attachment/README.zh.md | 131 ++++++++- packages/boot/README.i18n.yaml | 4 +- packages/boot/README.md | 35 ++- packages/boot/README.zh.md | 35 ++- packages/boot/app-boot/README.i18n.yaml | 4 +- packages/boot/app-boot/README.md | 165 ++++++++--- packages/boot/app-boot/README.zh.md | 175 +++++++++--- packages/boot/cmdline/README.i18n.yaml | 4 +- packages/boot/cmdline/README.md | 140 ++++++--- packages/boot/cmdline/README.zh.md | 140 ++++++--- packages/bundle/README.i18n.yaml | 4 +- packages/bundle/README.md | 43 ++- packages/bundle/README.zh.md | 45 ++- packages/bundle/acp-app/README.i18n.yaml | 4 +- packages/bundle/acp-app/README.md | 37 +++ packages/bundle/acp-app/README.zh.md | 37 +++ packages/bundle/base/README.i18n.yaml | 4 +- packages/bundle/base/README.md | 131 ++++++++- packages/bundle/base/README.zh.md | 131 ++++++++- packages/bundle/headless/README.i18n.yaml | 4 +- packages/bundle/headless/README.md | 132 ++++++++- packages/bundle/headless/README.zh.md | 134 ++++++++- packages/bundle/sdk-app/README.i18n.yaml | 4 +- packages/bundle/sdk-app/README.md | 37 ++- packages/bundle/sdk-app/README.zh.md | 39 ++- packages/bundle/sdk-minimal/README.i18n.yaml | 4 +- packages/bundle/sdk-minimal/README.md | 81 +++++- packages/bundle/sdk-minimal/README.zh.md | 87 +++++- packages/bundle/web-app/README.i18n.yaml | 4 +- packages/bundle/web-app/README.md | 144 +++++++++- packages/bundle/web-app/README.zh.md | 146 +++++++++- packages/client/README.i18n.yaml | 4 +- packages/client/README.md | 132 ++++++--- packages/client/README.zh.md | 134 ++++++--- packages/client/connection/README.i18n.yaml | 4 +- packages/client/connection/README.md | 43 ++- packages/client/connection/README.zh.md | 43 ++- packages/client/hmr/README.i18n.yaml | 4 +- packages/client/hmr/README.md | 117 +++++++- packages/client/hmr/README.zh.md | 123 +++++++- packages/client/locale/README.i18n.yaml | 4 +- packages/client/locale/README.md | 108 ++++++- packages/client/locale/README.zh.md | 110 +++++++- packages/client/modules/README.i18n.yaml | 4 +- packages/client/modules/README.md | 113 +++++++- packages/client/modules/README.zh.md | 121 +++++++- packages/client/store/README.i18n.yaml | 4 +- packages/client/store/README.md | 30 +- packages/client/store/README.zh.md | 30 +- .../client/ui-agent-preset/README.i18n.yaml | 4 +- packages/client/ui-agent-preset/README.md | 91 +++--- packages/client/ui-agent-preset/README.zh.md | 101 ++++--- packages/client/ui-approval/README.i18n.yaml | 4 +- packages/client/ui-approval/README.md | 30 +- packages/client/ui-approval/README.zh.md | 30 +- .../client/ui-attachment/README.i18n.yaml | 4 +- packages/client/ui-attachment/README.md | 89 +++++- packages/client/ui-attachment/README.zh.md | 95 ++++++- .../client/ui-brand-official/README.i18n.yaml | 4 +- packages/client/ui-brand-official/README.md | 76 ++++- .../client/ui-brand-official/README.zh.md | 82 +++++- packages/client/ui-chat/README.i18n.yaml | 4 +- packages/client/ui-chat/README.md | 43 ++- packages/client/ui-chat/README.zh.md | 43 ++- packages/client/ui-commands/README.i18n.yaml | 4 +- packages/client/ui-commands/README.md | 80 +++++- packages/client/ui-commands/README.zh.md | 84 +++++- .../client/ui-conversation/README.i18n.yaml | 4 +- packages/client/ui-conversation/README.md | 37 ++- packages/client/ui-conversation/README.zh.md | 37 ++- .../client/ui-deliverables/README.i18n.yaml | 4 +- packages/client/ui-deliverables/README.md | 81 +++++- packages/client/ui-deliverables/README.zh.md | 87 +++++- .../README.i18n.yaml | 4 +- .../ui-directory-picker-browse/README.md | 71 ++++- .../ui-directory-picker-browse/README.zh.md | 77 ++++- .../README.i18n.yaml | 4 +- .../ui-directory-picker-native/README.md | 73 ++++- .../ui-directory-picker-native/README.zh.md | 77 ++++- packages/client/ui-goal/README.i18n.yaml | 4 +- packages/client/ui-goal/README.md | 78 +++++- packages/client/ui-goal/README.zh.md | 82 +++++- .../client/ui-input-trigger/README.i18n.yaml | 4 +- packages/client/ui-input-trigger/README.md | 77 ++++- packages/client/ui-input-trigger/README.zh.md | 81 +++++- packages/client/ui-jobs/README.i18n.yaml | 4 +- packages/client/ui-jobs/README.md | 75 ++++- packages/client/ui-jobs/README.zh.md | 83 +++++- packages/client/ui-layout/README.i18n.yaml | 4 +- packages/client/ui-layout/README.md | 71 ++++- packages/client/ui-layout/README.zh.md | 79 +++++- .../ui-message-feedback/README.i18n.yaml | 4 +- packages/client/ui-message-feedback/README.md | 72 ++++- .../client/ui-message-feedback/README.zh.md | 78 +++++- .../ui-model-selection/README.i18n.yaml | 4 +- packages/client/ui-model-selection/README.md | 77 ++++- .../client/ui-model-selection/README.zh.md | 79 +++++- .../ui-permission-presets/README.i18n.yaml | 4 +- .../client/ui-permission-presets/README.md | 78 +++++- .../client/ui-permission-presets/README.zh.md | 82 +++++- packages/client/ui-plan/README.i18n.yaml | 4 +- packages/client/ui-plan/README.md | 77 ++++- packages/client/ui-plan/README.zh.md | 85 +++++- .../client/ui-primitives/README.i18n.yaml | 4 +- packages/client/ui-primitives/README.md | 114 ++++++-- packages/client/ui-primitives/README.zh.md | 116 ++++++-- packages/client/ui-reference/README.i18n.yaml | 4 +- packages/client/ui-reference/README.md | 89 +++++- packages/client/ui-reference/README.zh.md | 95 ++++++- packages/client/ui-renderer/README.i18n.yaml | 4 +- packages/client/ui-renderer/README.md | 94 ++++++- packages/client/ui-renderer/README.zh.md | 98 ++++++- packages/client/ui-session/README.i18n.yaml | 4 +- packages/client/ui-session/README.md | 30 +- packages/client/ui-session/README.zh.md | 30 +- .../ui-settings-general/README.i18n.yaml | 4 +- packages/client/ui-settings-general/README.md | 94 ++++++- .../client/ui-settings-general/README.zh.md | 96 ++++++- .../ui-settings-models/README.i18n.yaml | 4 +- packages/client/ui-settings-models/README.md | 104 ++++++- .../client/ui-settings-models/README.zh.md | 108 ++++++- .../README.i18n.yaml | 4 +- .../ui-settings-plugin-inventory/README.md | 85 +++++- .../ui-settings-plugin-inventory/README.zh.md | 93 +++++- .../ui-settings-plugins/README.i18n.yaml | 4 +- packages/client/ui-settings-plugins/README.md | 98 +++++-- .../client/ui-settings-plugins/README.zh.md | 100 +++++-- packages/client/ui-settings/README.i18n.yaml | 4 +- packages/client/ui-settings/README.md | 95 ++++++- packages/client/ui-settings/README.zh.md | 97 ++++++- packages/client/ui-sidebar/README.i18n.yaml | 4 +- packages/client/ui-sidebar/README.md | 90 +++++- packages/client/ui-sidebar/README.zh.md | 94 ++++++- packages/client/ui-skill/README.i18n.yaml | 4 +- packages/client/ui-skill/README.md | 85 +++++- packages/client/ui-skill/README.zh.md | 87 +++++- packages/client/ui-slots/README.i18n.yaml | 4 +- packages/client/ui-slots/README.md | 94 ++++++- packages/client/ui-slots/README.zh.md | 96 ++++++- packages/client/ui-subagent/README.i18n.yaml | 4 +- packages/client/ui-subagent/README.md | 94 ++++++- packages/client/ui-subagent/README.zh.md | 96 ++++++- packages/client/ui-theme/README.i18n.yaml | 4 +- packages/client/ui-theme/README.md | 98 ++++++- packages/client/ui-theme/README.zh.md | 102 ++++++- packages/client/ui-tool/README.i18n.yaml | 4 +- packages/client/ui-tool/README.md | 96 +++++-- packages/client/ui-tool/README.zh.md | 98 +++++-- .../client/ui-trajectory/README.i18n.yaml | 4 +- packages/client/ui-trajectory/README.md | 85 +++++- packages/client/ui-trajectory/README.zh.md | 89 +++++- .../client/ui-user-questions/README.i18n.yaml | 4 +- packages/client/ui-user-questions/README.md | 87 +++++- .../client/ui-user-questions/README.zh.md | 89 +++++- .../client/ui-workflow-run/README.i18n.yaml | 4 +- packages/client/ui-workflow-run/README.md | 93 +++++- packages/client/ui-workflow-run/README.zh.md | 97 +++++-- packages/client/ui-workspace/README.i18n.yaml | 4 +- packages/client/ui-workspace/README.md | 99 ++++++- packages/client/ui-workspace/README.zh.md | 103 ++++++- packages/client/web/README.i18n.yaml | 4 +- packages/client/web/README.md | 108 ++++++- packages/client/web/README.zh.md | 112 +++++++- packages/code-runtime/README.i18n.yaml | 4 +- packages/code-runtime/README.md | 48 +++- packages/code-runtime/README.zh.md | 52 +++- .../code-runtime-python/README.i18n.yaml | 4 +- .../code-runtime-python/README.md | 112 +++++++- .../code-runtime-python/README.zh.md | 122 +++++++- .../README.i18n.yaml | 4 +- .../code-runtime-worker-thread/README.md | 150 ++++++++-- .../code-runtime-worker-thread/README.zh.md | 162 +++++++++-- .../code-runtime/README.i18n.yaml | 4 +- packages/code-runtime/code-runtime/README.md | 134 ++++++++- .../code-runtime/code-runtime/README.zh.md | 138 +++++++-- packages/compaction/README.i18n.yaml | 4 +- packages/compaction/README.md | 52 +++- packages/compaction/README.zh.md | 52 +++- .../command-compact/README.i18n.yaml | 4 +- packages/compaction/command-compact/README.md | 130 +++++++-- .../compaction/command-compact/README.zh.md | 140 +++++++-- .../compaction-basic/README.i18n.yaml | 4 +- .../compaction/compaction-basic/README.md | 200 +++++++++---- .../compaction/compaction-basic/README.zh.md | 212 ++++++++++---- .../README.i18n.yaml | 4 +- .../compaction-tool-result-pruner/README.md | 141 ++++++++-- .../README.zh.md | 147 ++++++++-- .../compaction/compaction/README.i18n.yaml | 4 +- packages/compaction/compaction/README.md | 193 +++++++++---- packages/compaction/compaction/README.zh.md | 175 ++++++++---- packages/context/README.i18n.yaml | 4 +- packages/context/README.md | 55 +++- packages/context/README.zh.md | 55 +++- .../agent-instructions/README.i18n.yaml | 4 +- packages/context/agent-instructions/README.md | 148 +++++++--- .../context/agent-instructions/README.zh.md | 168 +++++++---- .../file-reference-local/README.i18n.yaml | 4 +- .../context/file-reference-local/README.md | 115 +++++++- .../context/file-reference-local/README.zh.md | 119 +++++++- .../context/file-reference/README.i18n.yaml | 4 +- packages/context/file-reference/README.md | 102 ++++++- packages/context/file-reference/README.zh.md | 106 ++++++- .../session-reference/README.i18n.yaml | 4 +- packages/context/session-reference/README.md | 118 ++++++-- .../context/session-reference/README.zh.md | 120 ++++++-- .../context/time-context/README.i18n.yaml | 4 +- packages/context/time-context/README.md | 109 +++++-- packages/context/time-context/README.zh.md | 111 ++++++-- .../context/tmux-context/README.i18n.yaml | 4 +- packages/context/tmux-context/README.md | 113 ++++++-- packages/context/tmux-context/README.zh.md | 115 ++++++-- packages/core/README.i18n.yaml | 4 +- packages/core/README.md | 64 ++++- packages/core/README.zh.md | 64 ++++- .../core/agent-default-model/README.i18n.yaml | 4 +- packages/core/agent-default-model/README.md | 120 +++++++- .../core/agent-default-model/README.zh.md | 122 +++++++- packages/core/agent-loop/README.i18n.yaml | 4 +- packages/core/agent-loop/README.md | 184 ++++++++---- packages/core/agent-loop/README.zh.md | 204 +++++++++----- .../agent-tool-presentation/README.i18n.yaml | 4 +- .../core/agent-tool-presentation/README.md | 107 ++++++- .../core/agent-tool-presentation/README.zh.md | 113 +++++++- packages/core/agent/README.i18n.yaml | 4 +- packages/core/agent/README.md | 172 ++++++++---- packages/core/agent/README.zh.md | 180 ++++++++---- packages/core/scope/README.i18n.yaml | 4 +- packages/core/scope/README.md | 121 ++++++-- packages/core/scope/README.zh.md | 123 ++++++-- packages/core/session/README.i18n.yaml | 4 +- packages/core/session/README.md | 155 ++++++---- packages/core/session/README.zh.md | 167 ++++++----- packages/core/system-prompt/README.i18n.yaml | 4 +- packages/core/system-prompt/README.md | 158 ++++++++--- packages/core/system-prompt/README.zh.md | 164 ++++++++--- packages/core/tools/README.i18n.yaml | 4 +- packages/core/tools/README.md | 195 +++++++------ packages/core/tools/README.zh.md | 213 ++++++++------ packages/credentials/README.i18n.yaml | 4 +- packages/credentials/README.md | 47 +++- packages/credentials/README.zh.md | 47 +++- .../authorization/README.i18n.yaml | 4 +- packages/credentials/authorization/README.md | 151 +++++++--- .../credentials/authorization/README.zh.md | 157 ++++++++--- .../credentials-local/README.i18n.yaml | 4 +- .../credentials/credentials-local/README.md | 185 +++++++++--- .../credentials-local/README.zh.md | 191 ++++++++++--- .../credentials/credentials/README.i18n.yaml | 4 +- packages/credentials/credentials/README.md | 179 ++++++++++-- packages/credentials/credentials/README.zh.md | 181 +++++++++--- packages/e2b/README.i18n.yaml | 4 +- packages/e2b/README.md | 51 +++- packages/e2b/README.zh.md | 51 +++- packages/e2b/e2b/README.i18n.yaml | 4 +- packages/e2b/e2b/README.md | 133 +++++++-- packages/e2b/e2b/README.zh.md | 141 ++++++++-- packages/e2b/fs-e2b/README.i18n.yaml | 4 +- packages/e2b/fs-e2b/README.md | 126 ++++++++- packages/e2b/fs-e2b/README.zh.md | 134 ++++++++- packages/e2b/subprocess-e2b/README.i18n.yaml | 4 +- packages/e2b/subprocess-e2b/README.md | 165 +++++++++-- packages/e2b/subprocess-e2b/README.zh.md | 173 ++++++++++-- packages/examples/README.i18n.yaml | 4 +- packages/examples/README.md | 38 ++- packages/examples/README.zh.md | 40 ++- .../agent-spine-demo/README.i18n.yaml | 4 +- packages/examples/agent-spine-demo/README.md | 204 ++++++++++---- .../examples/agent-spine-demo/README.zh.md | 206 ++++++++++---- packages/experimental/README.i18n.yaml | 4 +- packages/experimental/README.md | 51 +++- packages/experimental/README.zh.md | 53 +++- .../experimental/agent-team/README.i18n.yaml | 4 +- packages/experimental/agent-team/README.md | 193 +++++++++++-- packages/experimental/agent-team/README.zh.md | 209 +++++++++++--- .../tool-agent-team/README.i18n.yaml | 4 +- .../experimental/tool-agent-team/README.md | 127 ++++++++- .../experimental/tool-agent-team/README.zh.md | 137 ++++++++- .../webworker-packer/README.i18n.yaml | 4 +- .../experimental/webworker-packer/README.md | 37 ++- .../webworker-packer/README.zh.md | 37 ++- .../tests/image-loadable.spec.ts | 5 +- .../webworker-runtime/README.i18n.yaml | 4 +- .../experimental/webworker-runtime/README.md | 37 ++- .../webworker-runtime/README.zh.md | 37 ++- packages/extensions/README.i18n.yaml | 4 +- packages/extensions/README.md | 53 +++- packages/extensions/README.zh.md | 55 +++- .../cordis-client-runner/README.i18n.yaml | 4 +- .../extensions/cordis-client-runner/README.md | 126 +++++++-- .../cordis-client-runner/README.zh.md | 144 +++++++--- .../src/client/api-catalog.ts | 2 +- .../cordis-host-runner/README.i18n.yaml | 4 +- .../extensions/cordis-host-runner/README.md | 146 +++++++--- .../cordis-host-runner/README.zh.md | 148 +++++++--- .../extensions/tool-cordis/README.i18n.yaml | 4 +- packages/extensions/tool-cordis/README.md | 162 ++++++++--- packages/extensions/tool-cordis/README.zh.md | 164 ++++++++--- .../extensions/tool-cordis/src/api-catalog.ts | 4 +- .../extensions/ui-cordis/README.i18n.yaml | 4 +- packages/extensions/ui-cordis/README.md | 124 ++++++-- packages/extensions/ui-cordis/README.zh.md | 128 +++++++-- packages/feedback/README.i18n.yaml | 4 +- packages/feedback/README.md | 42 ++- packages/feedback/README.zh.md | 42 ++- .../command-feedback/README.i18n.yaml | 4 +- packages/feedback/command-feedback/README.md | 121 ++++++-- .../feedback/command-feedback/README.zh.md | 133 +++++++-- .../message-feedback/README.i18n.yaml | 4 +- packages/feedback/message-feedback/README.md | 148 +++++++--- .../feedback/message-feedback/README.zh.md | 150 +++++++--- packages/fs/README.i18n.yaml | 4 +- packages/fs/README.md | 62 +++- packages/fs/README.zh.md | 64 ++++- packages/fs/fs-local/README.i18n.yaml | 4 +- packages/fs/fs-local/README.md | 143 ++++++++-- packages/fs/fs-local/README.zh.md | 143 ++++++++-- .../fs/fs-observation-policy/README.i18n.yaml | 4 +- packages/fs/fs-observation-policy/README.md | 132 ++++++--- .../fs/fs-observation-policy/README.zh.md | 134 ++++++--- packages/fs/fs-sandbox/README.i18n.yaml | 4 +- packages/fs/fs-sandbox/README.md | 114 +++++++- packages/fs/fs-sandbox/README.zh.md | 120 +++++++- packages/fs/fs/README.i18n.yaml | 4 +- packages/fs/fs/README.md | 130 ++++++--- packages/fs/fs/README.zh.md | 130 ++++++--- packages/fs/tool-fs-search/README.i18n.yaml | 4 +- packages/fs/tool-fs-search/README.md | 167 ++++++++--- packages/fs/tool-fs-search/README.zh.md | 169 ++++++++--- packages/fs/tool-fs/README.i18n.yaml | 4 +- packages/fs/tool-fs/README.md | 157 ++++++++--- packages/fs/tool-fs/README.zh.md | 161 ++++++++--- .../tool-str-replace-editor/README.i18n.yaml | 4 +- packages/fs/tool-str-replace-editor/README.md | 115 +++++++- .../fs/tool-str-replace-editor/README.zh.md | 119 +++++++- packages/goal/README.i18n.yaml | 4 +- packages/goal/README.md | 53 +++- packages/goal/README.zh.md | 53 +++- packages/goal/command-goal/README.i18n.yaml | 4 +- packages/goal/command-goal/README.md | 111 +++++++- packages/goal/command-goal/README.zh.md | 123 ++++++-- .../goal/goal-round-driver/README.i18n.yaml | 4 +- packages/goal/goal-round-driver/README.md | 103 ++++++- packages/goal/goal-round-driver/README.zh.md | 115 ++++++-- packages/goal/goal/README.i18n.yaml | 4 +- packages/goal/goal/README.md | 144 ++++++++-- packages/goal/goal/README.zh.md | 156 +++++++++-- packages/goal/tool-goal/README.i18n.yaml | 4 +- packages/goal/tool-goal/README.md | 113 +++++++- packages/goal/tool-goal/README.zh.md | 123 ++++++-- packages/guard/README.i18n.yaml | 4 +- packages/guard/README.md | 51 +++- packages/guard/README.zh.md | 53 +++- .../repeat-tool-reminder/README.i18n.yaml | 4 +- packages/guard/repeat-tool-reminder/README.md | 136 +++++++-- .../guard/repeat-tool-reminder/README.zh.md | 156 +++++++++-- .../guard/timeout-policy/README.i18n.yaml | 4 +- packages/guard/timeout-policy/README.md | 120 ++++++-- packages/guard/timeout-policy/README.zh.md | 124 ++++++-- packages/hooks/README.i18n.yaml | 4 +- packages/hooks/README.md | 49 +++- packages/hooks/README.zh.md | 49 +++- packages/hooks/hook-protocol/README.i18n.yaml | 4 +- packages/hooks/hook-protocol/README.md | 145 ++++++++-- packages/hooks/hook-protocol/README.zh.md | 145 ++++++++-- .../hooks/hooks-claude-code/README.i18n.yaml | 4 +- packages/hooks/hooks-claude-code/README.md | 185 +++++++++--- packages/hooks/hooks-claude-code/README.zh.md | 193 +++++++++---- packages/hooks/hooks-codex/README.i18n.yaml | 4 +- packages/hooks/hooks-codex/README.md | 185 ++++++++---- packages/hooks/hooks-codex/README.zh.md | 191 +++++++++---- packages/host/README.i18n.yaml | 4 +- packages/host/README.md | 55 +++- packages/host/README.zh.md | 57 +++- packages/host/apiproxy/README.i18n.yaml | 4 +- packages/host/apiproxy/README.md | 144 +++++++--- packages/host/apiproxy/README.zh.md | 152 +++++++--- .../directory-picker-auto/README.i18n.yaml | 4 +- packages/host/directory-picker-auto/README.md | 107 ++++++- .../host/directory-picker-auto/README.zh.md | 109 ++++++- .../directory-picker-browse/README.i18n.yaml | 4 +- .../host/directory-picker-browse/README.md | 109 ++++++- .../host/directory-picker-browse/README.zh.md | 111 +++++++- .../directory-picker-native/README.i18n.yaml | 4 +- .../host/directory-picker-native/README.md | 98 ++++++- .../host/directory-picker-native/README.zh.md | 100 ++++++- .../host/directory-picker/README.i18n.yaml | 4 +- packages/host/directory-picker/README.md | 98 ++++++- packages/host/directory-picker/README.zh.md | 102 ++++++- .../host/frontend-static/README.i18n.yaml | 4 +- packages/host/frontend-static/README.md | 105 ++++++- packages/host/frontend-static/README.zh.md | 107 ++++++- .../host/plugin-inventory/README.i18n.yaml | 4 +- packages/host/plugin-inventory/README.md | 94 ++++++- packages/host/plugin-inventory/README.zh.md | 100 ++++++- packages/host/webserver/README.i18n.yaml | 4 +- packages/host/webserver/README.md | 111 +++++++- packages/host/webserver/README.zh.md | 113 +++++++- packages/identity/README.i18n.yaml | 4 +- packages/identity/README.md | 36 ++- packages/identity/README.zh.md | 36 ++- .../anonymous-user-id/README.i18n.yaml | 4 +- packages/identity/anonymous-user-id/README.md | 130 ++++++++- .../identity/anonymous-user-id/README.zh.md | 138 ++++++++- packages/interaction/README.i18n.yaml | 4 +- packages/interaction/README.md | 55 +++- packages/interaction/README.zh.md | 57 +++- .../interaction/commands/README.i18n.yaml | 4 +- packages/interaction/commands/README.md | 126 ++++++++- packages/interaction/commands/README.zh.md | 128 ++++++++- .../permission-presets/README.i18n.yaml | 4 +- .../interaction/permission-presets/README.md | 131 ++++++++- .../permission-presets/README.zh.md | 137 ++++++++- .../tool-ask-user/README.i18n.yaml | 4 +- packages/interaction/tool-ask-user/README.md | 119 +++++++- .../interaction/tool-ask-user/README.zh.md | 127 +++++++-- .../user-approval/README.i18n.yaml | 4 +- packages/interaction/user-approval/README.md | 115 +++++++- .../interaction/user-approval/README.zh.md | 121 +++++++- .../user-questions/README.i18n.yaml | 4 +- packages/interaction/user-questions/README.md | 35 ++- .../interaction/user-questions/README.zh.md | 35 ++- packages/jobs/README.i18n.yaml | 4 +- packages/jobs/README.md | 47 +++- packages/jobs/README.zh.md | 47 +++- packages/jobs/jobs-local/README.i18n.yaml | 4 +- packages/jobs/jobs-local/README.md | 130 ++++++++- packages/jobs/jobs-local/README.zh.md | 134 ++++++++- packages/jobs/jobs/README.i18n.yaml | 4 +- packages/jobs/jobs/README.md | 128 +++++++-- packages/jobs/jobs/README.zh.md | 132 +++++++-- packages/jobs/tool-jobs/README.i18n.yaml | 4 +- packages/jobs/tool-jobs/README.md | 131 +++++++-- packages/jobs/tool-jobs/README.zh.md | 147 ++++++++-- packages/llm/README.i18n.yaml | 4 +- packages/llm/README.md | 50 +++- packages/llm/README.zh.md | 50 +++- .../README.i18n.yaml | 4 +- .../llm/deepseek-llm-api-extensions/README.md | 33 ++- .../deepseek-llm-api-extensions/README.zh.md | 33 ++- packages/llm/llm-deepseek/README.i18n.yaml | 4 +- packages/llm/llm-deepseek/README.md | 211 ++++++++------ packages/llm/llm-deepseek/README.zh.md | 229 +++++++++------ packages/llm/llm-pi-ai/README.i18n.yaml | 4 +- packages/llm/llm-pi-ai/README.md | 235 +++++++++------- packages/llm/llm-pi-ai/README.zh.md | 258 +++++++++-------- packages/llm/llm-retry/README.i18n.yaml | 4 +- packages/llm/llm-retry/README.md | 112 +++++++- packages/llm/llm-retry/README.zh.md | 130 +++++++-- packages/llm/llm/README.i18n.yaml | 4 +- packages/llm/llm/README.md | 185 ++++++++---- packages/llm/llm/README.zh.md | 189 ++++++++----- .../README.i18n.yaml | 4 +- .../README.md | 35 ++- .../README.zh.md | 35 ++- packages/llm/token-meter/README.i18n.yaml | 4 +- packages/llm/token-meter/README.md | 132 +++++++-- packages/llm/token-meter/README.zh.md | 146 +++++++--- packages/lsp/README.i18n.yaml | 4 +- packages/lsp/README.md | 51 +++- packages/lsp/README.zh.md | 51 +++- packages/lsp/lsp-stdio/README.i18n.yaml | 4 +- packages/lsp/lsp-stdio/README.md | 167 ++++++++--- packages/lsp/lsp-stdio/README.zh.md | 171 ++++++++--- packages/lsp/lsp/README.i18n.yaml | 4 +- packages/lsp/lsp/README.md | 133 +++++++-- packages/lsp/lsp/README.zh.md | 137 +++++++-- packages/lsp/tool-lsp/README.i18n.yaml | 4 +- packages/lsp/tool-lsp/README.md | 108 ++++++- packages/lsp/tool-lsp/README.zh.md | 130 +++++++-- packages/mcp/README.i18n.yaml | 4 +- packages/mcp/README.md | 49 +++- packages/mcp/README.zh.md | 49 +++- packages/mcp/mcp-client/README.i18n.yaml | 4 +- packages/mcp/mcp-client/README.md | 193 +++++++++---- packages/mcp/mcp-client/README.zh.md | 203 ++++++++++---- packages/plan/README.i18n.yaml | 4 +- packages/plan/README.md | 44 ++- packages/plan/README.zh.md | 44 ++- packages/plan/plan-mode/README.i18n.yaml | 4 +- packages/plan/plan-mode/README.md | 147 ++++++++-- packages/plan/plan-mode/README.zh.md | 173 +++++++++--- packages/preset/README.i18n.yaml | 4 +- packages/preset/README.md | 51 +++- packages/preset/README.zh.md | 51 +++- .../preset/agent-presets/README.i18n.yaml | 4 +- packages/preset/agent-presets/README.md | 231 ++++++++------- packages/preset/agent-presets/README.zh.md | 245 +++++++++------- packages/preset/persona/README.i18n.yaml | 4 +- packages/preset/persona/README.md | 102 ++++++- packages/preset/persona/README.zh.md | 112 +++++++- packages/runtime-diagnostics/README.i18n.yaml | 4 +- packages/runtime-diagnostics/README.md | 46 ++- packages/runtime-diagnostics/README.zh.md | 48 +++- .../invariants/README.i18n.yaml | 4 +- .../runtime-diagnostics/invariants/README.md | 177 ++++++++---- .../invariants/README.zh.md | 179 ++++++++---- packages/sandbox/README.i18n.yaml | 4 +- packages/sandbox/README.md | 53 +++- packages/sandbox/README.zh.md | 53 +++- .../sandbox/sandbox-local/README.i18n.yaml | 4 +- packages/sandbox/sandbox-local/README.md | 126 ++++++++- packages/sandbox/sandbox-local/README.zh.md | 136 +++++++-- .../sandbox/sandbox-policy/README.i18n.yaml | 4 +- packages/sandbox/sandbox-policy/README.md | 126 +++++++-- packages/sandbox/sandbox-policy/README.zh.md | 138 +++++++-- .../sandbox-windows-acl/README.i18n.yaml | 4 +- .../sandbox/sandbox-windows-acl/README.md | 163 ++++++++--- .../sandbox/sandbox-windows-acl/README.zh.md | 167 ++++++++--- packages/sandbox/sandbox/README.i18n.yaml | 4 +- packages/sandbox/sandbox/README.md | 158 ++++++++++- packages/sandbox/sandbox/README.zh.md | 170 ++++++++++- packages/schedule/README.i18n.yaml | 4 +- packages/schedule/README.md | 43 ++- packages/schedule/README.zh.md | 47 +++- packages/schedule/schedule/README.i18n.yaml | 4 +- packages/schedule/schedule/README.md | 149 ++++++++-- packages/schedule/schedule/README.zh.md | 181 +++++++++--- packages/sdk/README.i18n.yaml | 4 +- packages/sdk/README.md | 49 +++- packages/sdk/README.zh.md | 47 +++- packages/sdk/client/README.i18n.yaml | 4 +- packages/sdk/client/README.md | 111 +++++++- packages/sdk/client/README.zh.md | 113 +++++++- packages/sdk/protocol/README.i18n.yaml | 4 +- packages/sdk/protocol/README.md | 107 ++++++- packages/sdk/protocol/README.zh.md | 109 ++++++- packages/sdk/server/README.i18n.yaml | 4 +- packages/sdk/server/README.md | 114 +++++++- packages/sdk/server/README.zh.md | 126 +++++++-- packages/session-query/README.i18n.yaml | 4 +- packages/session-query/README.md | 47 +++- packages/session-query/README.zh.md | 47 +++- .../session-log-export/README.i18n.yaml | 4 +- .../session-log-export/README.md | 113 +++++++- .../session-log-export/README.zh.md | 117 ++++++-- .../session-query-sqlite/README.i18n.yaml | 4 +- .../session-query-sqlite/README.md | 153 ++++++++-- .../session-query-sqlite/README.zh.md | 165 +++++++++-- .../session-query/README.i18n.yaml | 4 +- .../session-query/session-query/README.md | 160 +++++++++-- .../session-query/session-query/README.zh.md | 164 +++++++++-- .../tool-session-query/README.i18n.yaml | 4 +- .../tool-session-query/README.md | 134 ++++++++- .../tool-session-query/README.zh.md | 152 ++++++++-- packages/session/README.i18n.yaml | 4 +- packages/session/README.md | 81 ++++-- packages/session/README.zh.md | 93 +++--- .../README.i18n.yaml | 4 +- .../session-checkpoint-policy/README.md | 102 ++++++- .../session-checkpoint-policy/README.zh.md | 108 ++++++- .../session-log-deepseek/README.i18n.yaml | 4 +- .../session/session-log-deepseek/README.md | 37 ++- .../session/session-log-deepseek/README.zh.md | 37 ++- .../README.i18n.yaml | 4 +- .../session-persistence-jsonl/README.md | 146 ++++++++-- .../session-persistence-jsonl/README.zh.md | 162 ++++++++--- .../README.i18n.yaml | 4 +- .../session-persistence-sqlite/README.md | 218 ++++++++++++-- .../session-persistence-sqlite/README.zh.md | 220 +++++++++++++-- .../session-persistence/README.i18n.yaml | 4 +- .../session/session-persistence/README.md | 161 +++++++---- .../session/session-persistence/README.zh.md | 171 +++++++---- .../session-projection-cache/README.i18n.yaml | 4 +- .../session-projection-cache/README.md | 126 +++++++-- .../session-projection-cache/README.zh.md | 134 +++++++-- .../session-projection-cache/src/index.ts | 4 +- .../session-projection/README.i18n.yaml | 4 +- packages/session/session-projection/README.md | 144 ++++++++-- .../session/session-projection/README.zh.md | 146 ++++++++-- .../session/session-stats/README.i18n.yaml | 4 +- packages/session/session-stats/README.md | 129 +++++++-- packages/session/session-stats/README.zh.md | 131 +++++++-- .../session-telemetry-otel/README.i18n.yaml | 4 +- .../session/session-telemetry-otel/README.md | 128 +++++++-- .../session-telemetry-otel/README.zh.md | 130 +++++++-- .../session-telemetry/README.i18n.yaml | 4 +- packages/session/session-telemetry/README.md | 118 ++++++-- .../session/session-telemetry/README.zh.md | 120 ++++++-- .../README.i18n.yaml | 4 +- .../session-title-all-prompts-llm/README.md | 97 ++++++- .../README.zh.md | 105 ++++++- .../README.i18n.yaml | 4 +- .../session-title-first-prompt-llm/README.md | 97 ++++++- .../README.zh.md | 103 ++++++- .../session-title-llm/README.i18n.yaml | 4 +- packages/session/session-title-llm/README.md | 114 ++++++-- .../session/session-title-llm/README.zh.md | 120 ++++++-- .../session/session-title/README.i18n.yaml | 4 +- packages/session/session-title/README.md | 134 +++++++-- packages/session/session-title/README.zh.md | 140 +++++++-- packages/settings/README.i18n.yaml | 4 +- packages/settings/README.md | 48 +++- packages/settings/README.zh.md | 50 +++- .../settings/settings-file/README.i18n.yaml | 4 +- packages/settings/settings-file/README.md | 147 ++++++++-- packages/settings/settings-file/README.zh.md | 153 ++++++++-- packages/settings/settings/README.i18n.yaml | 4 +- packages/settings/settings/README.md | 164 +++++++++-- packages/settings/settings/README.zh.md | 166 +++++++++-- packages/shell/README.i18n.yaml | 4 +- packages/shell/README.md | 52 +++- packages/shell/README.zh.md | 52 +++- packages/shell/bash-local/README.i18n.yaml | 4 +- packages/shell/bash-local/README.md | 141 ++++++++-- packages/shell/bash-local/README.zh.md | 151 ++++++++-- packages/shell/bash-sandbox/README.i18n.yaml | 4 +- packages/shell/bash-sandbox/README.md | 130 +++++++-- packages/shell/bash-sandbox/README.zh.md | 140 +++++++-- packages/shell/pwsh-local/README.i18n.yaml | 4 +- packages/shell/pwsh-local/README.md | 154 ++++++++-- packages/shell/pwsh-local/README.zh.md | 162 +++++++++-- packages/shell/pwsh-sandbox/README.i18n.yaml | 4 +- packages/shell/pwsh-sandbox/README.md | 137 ++++++++- packages/shell/pwsh-sandbox/README.zh.md | 141 +++++++++- packages/shell/shell-env/README.i18n.yaml | 4 +- packages/shell/shell-env/README.md | 122 ++++++-- packages/shell/shell-env/README.zh.md | 128 +++++++-- packages/shell/shell/README.i18n.yaml | 4 +- packages/shell/shell/README.md | 135 +++++++-- packages/shell/shell/README.zh.md | 141 ++++++++-- .../tool-bash-persistent/README.i18n.yaml | 4 +- packages/shell/tool-bash-persistent/README.md | 132 ++++++++- .../shell/tool-bash-persistent/README.zh.md | 150 ++++++++-- packages/shell/tool-bash/README.i18n.yaml | 4 +- packages/shell/tool-bash/README.md | 143 ++++++++-- packages/shell/tool-bash/README.zh.md | 181 ++++++++---- .../tool-pwsh-persistent/README.i18n.yaml | 4 +- packages/shell/tool-pwsh-persistent/README.md | 142 ++++++++-- .../shell/tool-pwsh-persistent/README.zh.md | 156 +++++++++-- packages/shell/tool-pwsh/README.i18n.yaml | 4 +- packages/shell/tool-pwsh/README.md | 139 +++++++-- packages/shell/tool-pwsh/README.zh.md | 183 ++++++++---- packages/skill/README.i18n.yaml | 4 +- packages/skill/README.md | 52 +++- packages/skill/README.zh.md | 54 +++- packages/skill/skill-badge/README.i18n.yaml | 4 +- packages/skill/skill-badge/README.md | 108 ++++++- packages/skill/skill-badge/README.zh.md | 110 +++++++- .../skill/skill-filesystem/README.i18n.yaml | 4 +- packages/skill/skill-filesystem/README.md | 145 ++++++++-- packages/skill/skill-filesystem/README.zh.md | 159 ++++++++--- packages/skill/skill-filesystem/src/index.ts | 2 +- packages/skill/skill/README.i18n.yaml | 4 +- packages/skill/skill/README.md | 130 +++++++-- packages/skill/skill/README.zh.md | 138 ++++++--- packages/skill/tool-skill/README.i18n.yaml | 4 +- packages/skill/tool-skill/README.md | 122 ++++++-- packages/skill/tool-skill/README.zh.md | 144 ++++++++-- packages/spill/README.i18n.yaml | 4 +- packages/spill/README.md | 49 +++- packages/spill/README.zh.md | 49 +++- packages/spill/spill-local/README.i18n.yaml | 4 +- packages/spill/spill-local/README.md | 133 +++++++-- packages/spill/spill-local/README.zh.md | 139 +++++++-- packages/spill/spill-policy/README.i18n.yaml | 4 +- packages/spill/spill-policy/README.md | 145 ++++++++-- packages/spill/spill-policy/README.zh.md | 155 ++++++++-- packages/spill/spill/README.i18n.yaml | 4 +- packages/spill/spill/README.md | 149 ++++++++-- packages/spill/spill/README.zh.md | 153 ++++++++-- packages/storage/README.i18n.yaml | 4 +- packages/storage/README.md | 51 +++- packages/storage/README.zh.md | 53 +++- .../storage/storage-domain/README.i18n.yaml | 4 +- packages/storage/storage-domain/README.md | 150 +++++++++- packages/storage/storage-domain/README.zh.md | 154 +++++++++- .../storage/storage-json/README.i18n.yaml | 4 +- packages/storage/storage-json/README.md | 139 ++++++++- packages/storage/storage-json/README.zh.md | 147 ++++++++-- .../storage/storage-sqlite/README.i18n.yaml | 4 +- packages/storage/storage-sqlite/README.md | 129 ++++++++- packages/storage/storage-sqlite/README.zh.md | 135 +++++++-- packages/storage/storage/README.i18n.yaml | 4 +- packages/storage/storage/README.md | 126 ++++++++- packages/storage/storage/README.zh.md | 132 ++++++++- packages/subagent/README.i18n.yaml | 4 +- packages/subagent/README.md | 53 +++- packages/subagent/README.zh.md | 55 +++- .../subagent/subagent-acp/README.i18n.yaml | 4 +- packages/subagent/subagent-acp/README.md | 143 +++++++--- packages/subagent/subagent-acp/README.zh.md | 159 +++++++---- .../subagent-claude-code/README.i18n.yaml | 4 +- .../subagent/subagent-claude-code/README.md | 189 ++++++++----- .../subagent-claude-code/README.zh.md | 223 +++++++++------ .../subagent/subagent-codex/README.i18n.yaml | 4 +- packages/subagent/subagent-codex/README.md | 181 +++++++----- packages/subagent/subagent-codex/README.zh.md | 217 ++++++++------ .../subagent-dsh-sdk/README.i18n.yaml | 4 +- packages/subagent/subagent-dsh-sdk/README.md | 175 ++++++++---- .../subagent/subagent-dsh-sdk/README.zh.md | 183 ++++++++---- .../subagent-fork-in-process/README.i18n.yaml | 4 +- .../subagent-fork-in-process/README.md | 129 +++++++-- .../subagent-fork-in-process/README.zh.md | 143 ++++++++-- .../README.i18n.yaml | 4 +- .../subagent-in-process-driver/README.md | 113 ++++++-- .../subagent-in-process-driver/README.zh.md | 149 +++++++--- .../README.i18n.yaml | 4 +- .../subagent-spawn-in-process/README.md | 125 ++++++++- .../subagent-spawn-in-process/README.zh.md | 133 +++++++-- packages/subagent/subagent/README.i18n.yaml | 4 +- packages/subagent/subagent/README.md | 219 ++++++++------- packages/subagent/subagent/README.zh.md | 233 ++++++++------- .../tool-subagent-control/README.i18n.yaml | 4 +- .../subagent/tool-subagent-control/README.md | 120 +++++++- .../tool-subagent-control/README.zh.md | 146 ++++++++-- .../tool-subagent-report/README.i18n.yaml | 4 +- .../subagent/tool-subagent-report/README.md | 123 +++++++- .../tool-subagent-report/README.zh.md | 143 ++++++++-- .../subagent/tool-subagent/README.i18n.yaml | 4 +- packages/subagent/tool-subagent/README.md | 186 ++++++++++-- packages/subagent/tool-subagent/README.zh.md | 206 +++++++++++--- packages/subprocess/README.i18n.yaml | 4 +- packages/subprocess/README.md | 50 +++- packages/subprocess/README.zh.md | 50 +++- .../subprocess-local/README.i18n.yaml | 4 +- .../subprocess/subprocess-local/README.md | 141 ++++++++-- .../subprocess/subprocess-local/README.zh.md | 143 ++++++++-- .../subprocess/subprocess/README.i18n.yaml | 4 +- packages/subprocess/subprocess/README.md | 153 +++++++++- packages/subprocess/subprocess/README.zh.md | 155 +++++++++- .../subprocess/win32-process/README.i18n.yaml | 4 +- packages/subprocess/win32-process/README.md | 37 ++- .../subprocess/win32-process/README.zh.md | 35 ++- packages/terminal/README.i18n.yaml | 4 +- packages/terminal/README.md | 49 +++- packages/terminal/README.zh.md | 51 +++- .../terminal/terminal-bash/README.i18n.yaml | 4 +- packages/terminal/terminal-bash/README.md | 170 ++++++++++- packages/terminal/terminal-bash/README.zh.md | 174 ++++++++++-- packages/terminal/terminal/README.i18n.yaml | 4 +- packages/terminal/terminal/README.md | 143 ++++++++-- packages/terminal/terminal/README.zh.md | 151 ++++++++-- .../terminal/tool-terminal/README.i18n.yaml | 4 +- packages/terminal/tool-terminal/README.md | 137 ++++++++- packages/terminal/tool-terminal/README.zh.md | 147 ++++++++-- packages/test-support/README.i18n.yaml | 4 +- packages/test-support/README.md | 55 +++- packages/test-support/README.zh.md | 55 +++- .../agent-loop-testkit/README.i18n.yaml | 4 +- .../test-support/agent-loop-testkit/README.md | 88 +++++- .../agent-loop-testkit/README.zh.md | 94 ++++++- .../client-runtime/README.i18n.yaml | 4 +- .../test-support/client-runtime/README.md | 122 +++++++- .../test-support/client-runtime/README.zh.md | 124 +++++++- .../llm-mock-server/README.i18n.yaml | 4 +- .../test-support/llm-mock-server/README.md | 110 +++++++- .../test-support/llm-mock-server/README.zh.md | 120 ++++++-- .../test-support/llm-replay/README.i18n.yaml | 4 +- packages/test-support/llm-replay/README.md | 138 ++++++--- packages/test-support/llm-replay/README.zh.md | 142 +++++++--- .../loader-smoke/README.i18n.yaml | 4 +- packages/test-support/loader-smoke/README.md | 113 +++++++- .../test-support/loader-smoke/README.zh.md | 121 +++++++- .../session-snapshot/README.i18n.yaml | 4 +- .../test-support/session-snapshot/README.md | 124 ++++++-- .../session-snapshot/README.zh.md | 132 +++++++-- packages/todo/README.i18n.yaml | 4 +- packages/todo/README.md | 46 ++- packages/todo/README.zh.md | 46 ++- packages/todo/tool-todo/README.i18n.yaml | 4 +- packages/todo/tool-todo/README.md | 146 ++++++++-- packages/todo/tool-todo/README.zh.md | 162 +++++++++-- packages/typert/README.i18n.yaml | 4 +- packages/typert/README.md | 53 +++- packages/typert/README.zh.md | 53 +++- packages/typert/generator/README.i18n.yaml | 4 +- packages/typert/generator/README.md | 134 +++++++-- packages/typert/generator/README.zh.md | 136 +++++++-- packages/typert/loader/README.i18n.yaml | 4 +- packages/typert/loader/README.md | 118 +++++++- packages/typert/loader/README.zh.md | 120 +++++++- packages/typert/protocol/README.i18n.yaml | 4 +- packages/typert/protocol/README.md | 129 +++++++-- packages/typert/protocol/README.zh.md | 131 +++++++-- packages/typert/registry/README.i18n.yaml | 4 +- packages/typert/registry/README.md | 130 +++++++-- packages/typert/registry/README.zh.md | 132 +++++++-- packages/util/README.i18n.yaml | 4 +- packages/util/README.md | 57 +++- packages/util/README.zh.md | 59 +++- packages/util/atomic-write/README.i18n.yaml | 4 +- packages/util/atomic-write/README.md | 126 +++++++-- packages/util/atomic-write/README.zh.md | 128 +++++++-- packages/util/brand/README.i18n.yaml | 4 +- packages/util/brand/README.md | 85 +++++- packages/util/brand/README.zh.md | 85 +++++- packages/util/crypto/README.i18n.yaml | 4 +- packages/util/crypto/README.md | 37 +++ packages/util/crypto/README.zh.md | 37 +++ packages/util/home-paths/README.i18n.yaml | 4 +- packages/util/home-paths/README.md | 106 ++++++- packages/util/home-paths/README.zh.md | 110 +++++++- .../util/launch-environment/README.i18n.yaml | 4 +- packages/util/launch-environment/README.md | 110 ++++++-- packages/util/launch-environment/README.zh.md | 112 ++++++-- packages/util/native-command/README.i18n.yaml | 4 +- packages/util/native-command/README.md | 97 ++++++- packages/util/native-command/README.zh.md | 99 ++++++- .../util/output-retention/README.i18n.yaml | 4 +- packages/util/output-retention/README.md | 198 ++++++++----- packages/util/output-retention/README.zh.md | 202 ++++++++----- packages/util/timeout/README.i18n.yaml | 4 +- packages/util/timeout/README.md | 162 ++++++++--- packages/util/timeout/README.zh.md | 168 ++++++++--- packages/util/workspace-path/README.i18n.yaml | 4 +- packages/util/workspace-path/README.md | 27 ++ packages/util/workspace-path/README.zh.md | 27 ++ packages/web/README.i18n.yaml | 4 +- packages/web/README.md | 56 +++- packages/web/README.zh.md | 58 +++- packages/web/tool-web/README.i18n.yaml | 4 +- packages/web/tool-web/README.md | 174 ++++++++++-- packages/web/tool-web/README.zh.md | 178 +++++++++--- packages/web/web-fetch-http/README.i18n.yaml | 4 +- packages/web/web-fetch-http/README.md | 144 ++++++++-- packages/web/web-fetch-http/README.zh.md | 148 ++++++++-- .../web/web-search-deepseek/README.i18n.yaml | 4 +- packages/web/web-search-deepseek/README.md | 147 ++++++++-- packages/web/web-search-deepseek/README.zh.md | 151 +++++++--- packages/web/web-search-exa/README.i18n.yaml | 4 +- packages/web/web-search-exa/README.md | 137 +++++++-- packages/web/web-search-exa/README.zh.md | 139 +++++++-- .../web-search-perplexity/README.i18n.yaml | 4 +- packages/web/web-search-perplexity/README.md | 137 +++++++-- .../web/web-search-perplexity/README.zh.md | 145 ++++++++-- packages/web/web/README.i18n.yaml | 4 +- packages/web/web/README.md | 178 +++++++++--- packages/web/web/README.zh.md | 184 +++++++++--- packages/webhook/README.i18n.yaml | 4 +- packages/webhook/README.md | 28 +- packages/webhook/README.zh.md | 28 +- .../webhook/webhook-github/README.i18n.yaml | 4 +- packages/webhook/webhook-github/README.md | 39 ++- packages/webhook/webhook-github/README.zh.md | 39 ++- packages/webhook/webhook/README.i18n.yaml | 4 +- packages/webhook/webhook/README.md | 37 ++- packages/webhook/webhook/README.zh.md | 37 ++- packages/workflow/README.i18n.yaml | 4 +- packages/workflow/README.md | 54 +++- packages/workflow/README.zh.md | 54 +++- packages/workflow/tool-ralph/README.i18n.yaml | 4 +- packages/workflow/tool-ralph/README.md | 118 ++++++-- packages/workflow/tool-ralph/README.zh.md | 144 ++++++++-- .../workflow/tool-workflow/README.i18n.yaml | 4 +- packages/workflow/tool-workflow/README.md | 113 +++++++- packages/workflow/tool-workflow/README.zh.md | 135 +++++++-- .../workflow-worker-thread/README.i18n.yaml | 4 +- .../workflow/workflow-worker-thread/README.md | 201 ++++++++----- .../workflow-worker-thread/README.zh.md | 219 +++++++++------ packages/workflow/workflow/README.i18n.yaml | 4 +- packages/workflow/workflow/README.md | 140 +++++++-- packages/workflow/workflow/README.zh.md | 148 +++++++--- packages/workspace/README.i18n.yaml | 4 +- packages/workspace/README.md | 46 ++- packages/workspace/README.zh.md | 46 ++- packages/workspace/workspace/README.i18n.yaml | 4 +- packages/workspace/workspace/README.md | 166 +++++++++-- packages/workspace/workspace/README.zh.md | 172 ++++++++++-- python/development.i18n.yaml | 4 +- python/development.md | 2 +- python/development.zh.md | 2 +- scripts/coverage-exempt.ts | 7 + scripts/doc-standard.spec.ts | 265 ++++++++++++++++++ scripts/gen-cordis-catalog.ts | 2 +- scripts/gen-doc-graphs.ts | 46 +-- scripts/run-gates.spec.ts | 12 + scripts/run-gates.ts | 54 ++-- scripts/translation-pairing.spec.ts | 26 +- scripts/verify-subsystem-pages.ts | 6 - website/AGENTS.md | 2 +- 1061 files changed, 57862 insertions(+), 12379 deletions(-) create mode 100644 .agents/notes/proposed/process/2026-08-20-audience-first-documentation-quality.i18n.yaml create mode 100644 .agents/notes/proposed/process/2026-08-20-audience-first-documentation-quality.md create mode 100644 .agents/notes/proposed/process/2026-08-20-audience-first-documentation-quality.zh.md create mode 100644 .agents/skills/.gitignore delete mode 100644 .agents/skills/dsh-doc-standards/SKILL.md create mode 100644 .agents/skills/dsh-doc/SKILL.md create mode 100644 .agents/skills/dsh-doc/references/metadata-links-i18n.md create mode 100644 .agents/skills/dsh-doc/references/review.md create mode 100644 .agents/skills/dsh-doc/references/structure-hierarchy.md create mode 100644 .agents/skills/dsh-doc/references/style.md rename .agents/skills/{dsh-doc-site-sync/SKILL.md => dsh-doc/references/website-sync.md} (57%) create mode 100644 .agents/skills/dsh-doc/templates/package-bundle.md create mode 100644 .agents/skills/dsh-doc/templates/package-group.md create mode 100644 .agents/skills/dsh-doc/templates/package-library.md create mode 100644 .agents/skills/dsh-doc/templates/package-reference.md create mode 100644 scripts/doc-standard.spec.ts diff --git a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml index e4a69cd1c1..612d81556e 100644 --- a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml +++ b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.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-07-04-doc-tiers-and-budgets.md -2026-07-04-doc-tiers-and-budgets.md: 3f263864b9b6ee9479d1133b908617f10073dd66 -2026-07-04-doc-tiers-and-budgets.zh.md: d0745d2ac7aacea0f61ea6b699fb86fa39326881 +2026-07-04-doc-tiers-and-budgets.md: 378da8f8fddafa32dc7450bfac1c5376f2c7a065 +2026-07-04-doc-tiers-and-budgets.zh.md: 1d92ed7fbbec8a9a15bf94a2d320ee88f65a9fa8 diff --git a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md index 3f263864b9..378da8f8fd 100644 --- a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md +++ b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md @@ -15,7 +15,7 @@ Standing docs accumulated repeated rules, retold incidents, duplicated package m - **One product onboarding path.** The root README owns the recommended package-run path, the source-run alternative, and compact `dsh plugin --profile` usage. The published user guide starts with tasks inside the running Web UI, then links to distinct tutorials or reference owners for other interfaces, plugin development, and advanced configuration instead of repeating Web startup. - **A narrow, hard budget gate.** [scripts/verify-doc-budgets.ts](../../../../scripts/verify-doc-budgets.ts) joins `doc-sync`: every doc listed in [scripts/doc-budgets.manifest.json](../../../../scripts/doc-budgets.manifest.json) must stay under its word ceiling (`wc -w` semantics, whole file), and a budgeted file that is missing fails the gate so a rename cannot silently orphan its budget. Scope is deliberately only the accretion-prone standing docs — the root and subtree `AGENTS.md` files, `architecture.md`, `packages/README.md`, and the standing policy docs they evict content into (`docs/testing.md`, `docs/defensive-patterns.md`). Reference docs, Agent Notes, and package READMEs are unbudgeted: length is legitimate there when every row is a fact, and review plus the slop checklist govern them. - **Ceilings are an enforcement frontier that ratchets.** A doc at or below its target keeps at least 5% headroom as its ceiling ratchets down; a doc above target keeps a frozen ceiling that prevents growth until it reaches the target (root `AGENTS.md` ≤ 1,600 words; `architecture.md` ≤ 1,800; subtree `AGENTS.md` ≤ 600 except `packages/AGENTS.md` ≤ 650 and `docs/AGENTS.md` ≤ 1,250; `packages/README.md` ≤ 600). When the gate goes red, relocate or condense; raise a ceiling only with explicit PR justification. -- **A thin workflow skill, contracts in docs.** [.agents/skills/dsh-doc-standards](../../../skills/dsh-doc-standards/SKILL.md) carries the placement/audit/red-gate workflow and defers to the standard as its source of truth, the same split as [dsh-translate-docs](../../../skills/dsh-translate-docs/SKILL.md) over the i18n contract. +- **A thin workflow skill, contracts in docs.** [.agents/skills/dsh-doc](../../../skills/dsh-doc/SKILL.md) carries the placement, audit, budget, and website workflow and defers to the standard as its source of truth, the same split as [dsh-translate-docs](../../../skills/dsh-translate-docs/SKILL.md) over the i18n contract. ## Alternatives considered diff --git a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.zh.md b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.zh.md index d0745d2ac7..1d92ed7fbb 100644 --- a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.zh.md +++ b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.zh.md @@ -15,7 +15,7 @@ Status: implemented - **单一产品入门路径。**根 README 负责推荐的包运行路径、从源码运行的备选路径和简要的 `dsh plugin --profile` 用法。已发布的用户指南从运行中的 Web UI 内部任务开始,再链接到其他界面的独立教程或插件开发与进阶配置的参考文档归属处,而不会重复介绍 Web 启动步骤。 - **范围窄且严格的预算门禁。**[scripts/verify-doc-budgets.ts](../../../../scripts/verify-doc-budgets.ts) 接入 `doc-sync`:[scripts/doc-budgets.manifest.json](../../../../scripts/doc-budgets.manifest.json) 列出的每份文档都必须低于其词数上限(采用 `wc -w` 语义,统计整个文件);预算内文件缺失也会使门禁失败,使重命名无法悄然遗落其预算。范围刻意只涵盖容易膨胀的常设文档——根目录和子树中的 `AGENTS.md` 文件、`architecture.md`、`packages/README.md`,以及它们将内容移入的常设策略文档(`docs/testing.md`、`docs/defensive-patterns.md`)。参考文档、Agent Note 和包 README 不设预算:只要每一行都是事实,长度在这些位置就是合理的;评审和赘余检查清单负责约束它们。 - **上限是只进不退的执行红线。** 达到或低于目标的文档在上限逐步下调时保留至少 5% 的余量;高于目标的文档则维持冻结的上限,在达到目标之前不得增长(根 `AGENTS.md` ≤ 1,600 词;`architecture.md` ≤ 1,800;子树 `AGENTS.md` ≤ 600,但 `packages/AGENTS.md` ≤ 650、`docs/AGENTS.md` ≤ 1,250;`packages/README.md` ≤ 600)。门禁变红时,迁移或压缩内容;只有在 PR(Pull Request)描述中给出明确理由时才提高上限。 -- **精简的工作流 skill(技能),约定归文档。**[.agents/skills/dsh-doc-standards](../../../skills/dsh-doc-standards/SKILL.md) 承载文档放置、审计和门禁失败处理工作流,并以文档标准为真源,与 [dsh-translate-docs](../../../skills/dsh-translate-docs/SKILL.md) 和 i18n 约定之间的分工相同。 +- **精简的工作流 skill(技能),约定归文档。**[.agents/skills/dsh-doc](../../../skills/dsh-doc/SKILL.md) 承载文档放置、审计、预算与站点发布工作流,并以文档标准为真源,与 [dsh-translate-docs](../../../skills/dsh-translate-docs/SKILL.md) 和 i18n 约定之间的分工相同。 ## 曾考虑的替代方案 diff --git a/.agents/notes/implemented/process/2026-07-22-product-first-root-readme.i18n.yaml b/.agents/notes/implemented/process/2026-07-22-product-first-root-readme.i18n.yaml index a76dd67754..c059945b9e 100644 --- a/.agents/notes/implemented/process/2026-07-22-product-first-root-readme.i18n.yaml +++ b/.agents/notes/implemented/process/2026-07-22-product-first-root-readme.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-07-22-product-first-root-readme.md -2026-07-22-product-first-root-readme.md: 24c6dc04f24bd758d8955824a17b1d99801ecd16 -2026-07-22-product-first-root-readme.zh.md: f5c0d821e916423c92258649c22ceed222c06439 +2026-07-22-product-first-root-readme.md: 662ff334536227e7eeac4c8328936bb57e0aecc9 +2026-07-22-product-first-root-readme.zh.md: 3295794339d6348219ab8db68bad1077d3e54ee2 diff --git a/.agents/notes/implemented/process/2026-07-22-product-first-root-readme.md b/.agents/notes/implemented/process/2026-07-22-product-first-root-readme.md index 24c6dc04f2..662ff33453 100644 --- a/.agents/notes/implemented/process/2026-07-22-product-first-root-readme.md +++ b/.agents/notes/implemented/process/2026-07-22-product-first-root-readme.md @@ -10,13 +10,11 @@ The root README is the repository's product entry point. Its product-first struc ## Decision -The root README preserves its existing structure, order, and wording wherever the underlying fact remains correct. A refresh changes only stale claims and adds material needed to represent shipped surfaces; it does not use repository growth as a reason to reframe the whole page. +The root README is a compact product and contributor entry point. It states the product identity and plugin architecture, links the documentation site, marks the developer-preview and safety status, and then gives the supported npm and source launch paths. -A note before installation thanks internal testers, states that features and experience remain unfinished, and asks for direct reports of failures, confusion, and friction through the WeCom group. The existing development-stage statement identifies DeepSeek Harness as being in internal testing. +Both launch paths start the Web UI through the `dsh` profile entry point. The source path builds the checkout before it runs `pnpm dsh web`. Detailed ACP, TUI, SDK, capability, and package guidance stays in the user guide, architecture documentation, and package map instead of being repeated on the landing page. -The user-surface section adds the ACP automation server and Python/JSON-RPC SDK beside the existing Web, TUI, and headless entries. The installed TUI remains the single `dsh` command; the Web instructions build the active checkout before running `dsh web`, and custom or reused checkout paths stay explicit. These launch paths must remain executable through a real PTY and a production build/HTTP smoke, respectively. The capability paragraph keeps its compact inventory style while adding the shipped PTY, LSP, web, goal, planning, task, sandbox, approval, settings, credentials, session-query, and telemetry families and stating that compositions select subsets. One adjacent bullet records the authoritative-session-log rule because persistence, replay, queries, telemetry, and interfaces depend on it. - -Detailed package and service inventories remain at their owning documentation. The English and Chinese README sides share the same technical structure, while their community sections continue to point to the primary channel for each language audience. The documentation website keeps a separate [quick-start entry route](../../../../docs/user/index.md) instead of presenting another product landing page. +The remaining sections link community support, contribution guidance, development documentation, agent instructions, the license, and third-party notices. The English and Chinese README sides keep the same technical structure while their community links serve their language audiences. The documentation website keeps a separate [quick-start entry route](../../../../docs/user/index.md). ## Alternatives considered diff --git a/.agents/notes/implemented/process/2026-07-22-product-first-root-readme.zh.md b/.agents/notes/implemented/process/2026-07-22-product-first-root-readme.zh.md index f5c0d821e9..3295794339 100644 --- a/.agents/notes/implemented/process/2026-07-22-product-first-root-readme.zh.md +++ b/.agents/notes/implemented/process/2026-07-22-product-first-root-readme.zh.md @@ -10,13 +10,11 @@ Status: implemented ## 决策 -只要背后的事实仍然正确,根 README 就保留既有结构、顺序和措辞。刷新时只修正陈旧声明,并补充呈现已交付内容所需的信息;不会因为仓库规模增长就重构整篇叙事。 +根 README 是简短的产品和贡献者入口。它说明产品定位与插件架构,链接文档站,标明开发者预览与安全状态,然后给出受支持的 npm 和源码启动路径。 -安装说明之前的一则文字感谢内测用户,说明功能和体验仍待完善,并邀请大家通过企业微信群直接反馈失败、困惑和不顺手之处。既有的开发阶段声明明确说明 DeepSeek Harness 处于内测阶段。 +两条启动路径都通过 `dsh` profile 入口启动 Web UI。源码路径先构建当前检出,再运行 `pnpm dsh web`。ACP、TUI、SDK、能力和包的详细说明由用户指南、架构文档与包索引维护,不在入口页重复。 -用户入口章节在已有的 Web、TUI 和 Headless 入口旁补充 ACP(Agent Client Protocol)自动化服务器和 Python/JSON-RPC SDK。安装后的 TUI 仍只需执行一条 `dsh` 命令;Web 说明要求先构建当前检出,再运行 `dsh web`,并明确处理自定义或复用的检出路径。这两条启动路径必须分别能在真实 PTY 与生产构建/HTTP 冒烟中原样执行。能力段落沿用简洁清单的写法,补充已经交付的 PTY、LSP、Web、目标、规划、任务、沙箱、审批、设置、凭据、会话查询和遥测等能力类别,并说明不同组合只选用其中一部分。相邻的一条列表项说明权威会话日志规则,因为持久化、回放、查询、遥测和各类接口都依赖它。 - -包与服务的完整清单仍由各自的归属文档维护。中英文 README 采用相同的技术结构,但社区章节仍分别指向各自语言受众的主要交流渠道。文档网站保留独立的[快速开始入口路由](../../../../docs/user/index.zh.md),不另行呈现产品首页。 +其余章节链接社区支持、贡献指南、开发文档、agent 指令、许可证与第三方声明。中英文 README 保持相同技术结构,社区链接分别服务各自语言受众。文档网站保留独立的[快速开始入口路由](../../../../docs/user/index.zh.md)。 ## 考虑过的替代方案 diff --git a/.agents/notes/implemented/process/2026-07-27-explicit-change-scope-report.i18n.yaml b/.agents/notes/implemented/process/2026-07-27-explicit-change-scope-report.i18n.yaml index b55d583430..70cfee1edc 100644 --- a/.agents/notes/implemented/process/2026-07-27-explicit-change-scope-report.i18n.yaml +++ b/.agents/notes/implemented/process/2026-07-27-explicit-change-scope-report.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-07-27-explicit-change-scope-report.md -2026-07-27-explicit-change-scope-report.md: ed09ffc44252e1e571d50537b3471cad8d68d8f9 -2026-07-27-explicit-change-scope-report.zh.md: 783f89b9b42f1706da218997a06bfe7c28a0936b +2026-07-27-explicit-change-scope-report.md: 51f79039f23408f4463d08c08774089a96ce15ab +2026-07-27-explicit-change-scope-report.zh.md: 4ea99f15c05123ae774b5bc758abe5b79b54c4c1 diff --git a/.agents/notes/implemented/process/2026-07-27-explicit-change-scope-report.md b/.agents/notes/implemented/process/2026-07-27-explicit-change-scope-report.md index ed09ffc442..51f79039f2 100644 --- a/.agents/notes/implemented/process/2026-07-27-explicit-change-scope-report.md +++ b/.agents/notes/implemented/process/2026-07-27-explicit-change-scope-report.md @@ -6,7 +6,7 @@ English | [中文](2026-07-27-explicit-change-scope-report.zh.md) ## Problem -The [pre-push workflow](../../../skills/dsh-pre-push-checks/SKILL.md) needs the diff against the actual base, but constructing `origin/` fails for a new worktree branch that tracks `origin/master` before its first push and misstates a stacked branch whose PR targets another feature branch. The [code-review](../../../skills/dsh-code-review/SKILL.md) and [documentation-audit](../../../skills/dsh-doc-standards/SKILL.md) workflows need the same current-base judgment. +The [pre-push workflow](../../../skills/dsh-pre-push-checks/SKILL.md) needs the diff against the actual base, but constructing `origin/` fails for a new worktree branch that tracks `origin/master` before its first push and misstates a stacked branch whose PR targets another feature branch. The [code-review](../../../skills/dsh-code-review/SKILL.md) and [documentation-audit](../../../skills/dsh-doc/SKILL.md) workflows need the same current-base judgment. An incorrect range undermines evidence selection because it can omit affected paths. A three-dot committed diff also says nothing about Git's separate staged, unstaged, and untracked layers. diff --git a/.agents/notes/implemented/process/2026-07-27-explicit-change-scope-report.zh.md b/.agents/notes/implemented/process/2026-07-27-explicit-change-scope-report.zh.md index 783f89b9b4..4ea99f15c0 100644 --- a/.agents/notes/implemented/process/2026-07-27-explicit-change-scope-report.zh.md +++ b/.agents/notes/implemented/process/2026-07-27-explicit-change-scope-report.zh.md @@ -6,7 +6,7 @@ Status: implemented ## 问题 -[pre-push 工作流](../../../skills/dsh-pre-push-checks/SKILL.md)需要取得相对于实际基准的 diff,但按 `origin/` 构造引用存在两类问题:对于第一次推送前跟踪 `origin/master`、尚无同名远端分支的新 worktree 分支,该引用无法解析;对于 PR(Pull Request)以另一功能分支为基准的堆叠分支,该引用会错误描述基准。[代码评审](../../../skills/dsh-code-review/SKILL.md)与[文档审计](../../../skills/dsh-doc-standards/SKILL.md)工作流同样需要判断当前基准。 +[pre-push 工作流](../../../skills/dsh-pre-push-checks/SKILL.md)需要取得相对于实际基准的 diff,但按 `origin/` 构造引用存在两类问题:对于第一次推送前跟踪 `origin/master`、尚无同名远端分支的新 worktree 分支,该引用无法解析;对于 PR(Pull Request)以另一功能分支为基准的堆叠分支,该引用会错误描述基准。[代码评审](../../../skills/dsh-code-review/SKILL.md)与[文档审计](../../../skills/dsh-doc/SKILL.md)工作流同样需要判断当前基准。 错误的范围可能遗漏受影响的路径,从而削弱证据选择。三点范围产生的已提交 diff 也完全无法说明 Git 中彼此独立的已暂存、未暂存与未跟踪层。 diff --git a/.agents/notes/implemented/process/2026-07-31-coverage-exempt-heavy-suites.i18n.yaml b/.agents/notes/implemented/process/2026-07-31-coverage-exempt-heavy-suites.i18n.yaml index 929708a328..035f553168 100644 --- a/.agents/notes/implemented/process/2026-07-31-coverage-exempt-heavy-suites.i18n.yaml +++ b/.agents/notes/implemented/process/2026-07-31-coverage-exempt-heavy-suites.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-07-31-coverage-exempt-heavy-suites.md -2026-07-31-coverage-exempt-heavy-suites.md: c587950a4cf6e79180d381e61ade0b274dbed6e7 -2026-07-31-coverage-exempt-heavy-suites.zh.md: 74ab9b1510a1d3eafa2ae56da7667afa817737cd +2026-07-31-coverage-exempt-heavy-suites.md: fe33308cfcbd709a563e697a5e62585be58f214d +2026-07-31-coverage-exempt-heavy-suites.zh.md: 233fdc403fe734b21d116240b8067eaef119cd45 diff --git a/.agents/notes/implemented/process/2026-07-31-coverage-exempt-heavy-suites.md b/.agents/notes/implemented/process/2026-07-31-coverage-exempt-heavy-suites.md index c587950a4c..fe33308cfc 100644 --- a/.agents/notes/implemented/process/2026-07-31-coverage-exempt-heavy-suites.md +++ b/.agents/notes/implemented/process/2026-07-31-coverage-exempt-heavy-suites.md @@ -21,6 +21,8 @@ Linux coverage CI and native Windows CI use [in-job partitioned coverage](2026-0 `scripts/coverage-exempt.ts` is the single roster point, holding the membership contract and the filter/exclude pairs so the two sides cannot drift. +The roster also contains the packed-image loadability suite. That suite reads built workspace artifacts while the packer and Web Worker runtime sources it imports are threshold-excluded. Native Windows makes the uninstrumented gate wait for `build`, so the suite cannot observe a partially emitted dependency closure. + ### The roster, reconciled entry by entry A suite contributes to coverage exactly when it executes measured files in-process (`coverage.include` spans the package src trees). The current roster, audited: @@ -31,6 +33,7 @@ A suite contributes to coverage exactly when it executes measured files in-proce | tools-catalog.spec additionally imports | `typert-registry` and `tool-cordis` src | Each package's own tests cover them fully (verified with focused coverage runs, zero threshold errors) | | `scripts/install-lefthook.spec.ts`, `scripts/oxlint-contract.spec.ts`, `scripts/change-scope.spec.ts`, `scripts/translation-pairing-merge.spec.ts` | None — they test `scripts/` sources (never in `coverage.include`) and work by spawning child processes | Nothing to carry | | `packages/experimental/webworker-runtime/tests/compile/transform-corpus.spec.ts` | None — it spawns a child process that transforms and imports every built bundle (Node's ESM loader is the oracle) | webworker-runtime src is threshold-excluded as a package (`vitest.config.ts`) — outside the threshold scope to begin with | +| `packages/experimental/webworker-packer/tests/image-loadable.spec.ts` | Packer and Web Worker runtime src, both threshold-excluded in `vitest.config.ts` | The suite is correctness evidence over built artifacts; native Windows runs it after build in the uninstrumented gate | ### Membership contract @@ -59,6 +62,7 @@ Measured on CI (16-core runner): the gate segment went from 424 seconds to the t ## Consequences - The exempt suites execute without adding instrumentation cost to the thresholded gate; partitioned wall-clock measurements belong to the [in-job partitioning decision](2026-08-18-in-job-partitioned-coverage.md). +- Native Windows makes the exempt gate wait for build, so the packed-image suite reads a complete workspace artifact tree. - `DSH_GATE_CONCURRENCY` has two schedulable gates in this lane again, so the aggregate scheduler is no longer a pass-through. - Adding a heavy suite to the roster requires the membership audit above; a wrong entry fails the instrumented gate loudly rather than eroding coverage silently. - The exempt suites no longer appear in the coverage report's file list of contributors; their correctness signal lives solely in the uninstrumented gate's pass/fail. diff --git a/.agents/notes/implemented/process/2026-07-31-coverage-exempt-heavy-suites.zh.md b/.agents/notes/implemented/process/2026-07-31-coverage-exempt-heavy-suites.zh.md index 74ab9b1510..233fdc403f 100644 --- a/.agents/notes/implemented/process/2026-07-31-coverage-exempt-heavy-suites.zh.md +++ b/.agents/notes/implemented/process/2026-07-31-coverage-exempt-heavy-suites.zh.md @@ -21,6 +21,8 @@ Linux 覆盖率 CI 与原生 Windows CI 在插桩门禁内部使用 [job 内分 `scripts/coverage-exempt.ts` 是唯一名单点,集中持有成员资格约定与 filter/exclude 配对,防止两侧漂移。 +该名单还包含构建镜像可加载性套件。这个套件读取工作区构建产物,而它导入的 packer 与 Web Worker runtime 源码已排除在阈值外。原生 Windows 会让无插桩门禁等待 `build`,因此该套件不会观察到只完成部分输出的依赖闭包。 + ### 豁免名单与逐项对账 一个套件对覆盖率有贡献,当且仅当它在进程内执行了被度量的文件(`coverage.include` = 包 src 树)。现行名单逐项核对: @@ -31,6 +33,7 @@ Linux 覆盖率 CI 与原生 Windows CI 在插桩门禁内部使用 [job 内分 | 其中 tools-catalog.spec 额外 import | `typert-registry`、`tool-cordis` 的 src | 两包各自的测试独立满覆盖(focused coverage 实测无阈值错误) | | `scripts/install-lefthook.spec.ts`、`scripts/oxlint-contract.spec.ts`、`scripts/change-scope.spec.ts`、`scripts/translation-pairing-merge.spec.ts` | 无——被测对象是 `scripts/` 源码(从不在 coverage.include),执行方式是 spawn 子进程 | 无需接 | | `packages/experimental/webworker-runtime/tests/compile/transform-corpus.spec.ts` | 无——spawn 子进程对全部已构建 bundle 做 transform 并 import(oracle 是 Node ESM loader) | webworker-runtime src 已整包 threshold-excluded(`vitest.config.ts`),本不在阈值口径内 | +| `packages/experimental/webworker-packer/tests/image-loadable.spec.ts` | packer 与 Web Worker runtime 源码,两者都在 `vitest.config.ts` 中排除阈值 | 该套件为构建产物提供正确性证据;原生 Windows 在构建后通过无插桩门禁运行它 | ### 成员资格约定 @@ -59,6 +62,7 @@ CI 实测(16 核 runner):拆分前 gate 段 424 秒,拆分后两 gate ## Consequences - 豁免套件在执行时不会向阈值门禁叠加插桩开销;分区墙钟数据由 [job 内分区决策](2026-08-18-in-job-partitioned-coverage.zh.md)负责记录。 +- 原生 Windows 让豁免门禁等待构建,因此构建镜像套件会读取完整的工作区产物树。 - `DSH_GATE_CONCURRENCY` 在本 lane 重新拥有两个可调度对象,聚合调度器不再是直通。 - 向名单新增重型套件必须完成上述成员资格对账;错误条目会让插桩 gate 大声失败,而不是静默侵蚀覆盖率。 - 豁免套件不再出现在覆盖率报告的贡献文件列表中;其正确性信号完全由无插桩 gate 的红绿承载。 diff --git a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.i18n.yaml b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.i18n.yaml index d0e225ad3b..68d3f86461 100644 --- a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.i18n.yaml +++ b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.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-08-native-windows-pull-request-ci.md -2026-08-08-native-windows-pull-request-ci.md: dad23f1a49393fbfc3c0798407ed9abe21ff3df2 -2026-08-08-native-windows-pull-request-ci.zh.md: 0d81b160fd40ae0a26569351bca6e631aab9a8de +2026-08-08-native-windows-pull-request-ci.md: 98f48029a86a8b07e53cd4498b27d637508e450b +2026-08-08-native-windows-pull-request-ci.zh.md: b2dba91e4a88d0da637521aae2de937672b869a3 diff --git a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md index dad23f1a49..98f48029a8 100644 --- a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md +++ b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md @@ -18,7 +18,7 @@ Every pull request also starts an ordinary independent `windows-native` job name The native job is deliberately absent from `all-checks-passed.needs` and does not use `continue-on-error`: the aggregate neither waits for it nor changes conclusion because of it, while the job retains its own unmasked result. Workspace build, production-site, and 100%-per-file coverage failures make the native job fail. Static, documentation, package, built-artifact, lint, and snapshot inventories run in the same job as observational gates: their failures remain visible without changing the native aggregate result because Linux owns their blocking verdict. -The 16-core lane admits four concurrent outer gates. Workspace build and production-site validation start immediately. Instrumented and exempt-heavy coverage both wait for the complete build: the instrumented corpus includes packer assertions over built `lib/` output, while the exempt gate's temporary Oxlint contract probes must not race source compilation. Every observational gate waits for both coverage gates to settle, regardless of outcome, before entering an available slot; its own `needs` edges still require their predecessors to pass. This also keeps later static gates that create temporary contract files from racing either coverage scan. [In-job partitioned coverage](2026-08-18-in-job-partitioned-coverage.md) uses four single-worker shards, while the exempt-heavy gate receives two workers from `DSH_COVERAGE_MAX_WORKERS=6`, for about six active coverage execution units after build. `publint` is capped at eight workers when the observational inventory starts. Every Vitest project uses forked workers because Node 24's CJS lexer fatal reproduced in shared worker threads on Windows and POSIX. Both coverage gates set Vitest's default per-test and polling budgets to 30 seconds because unrelated process, Git, SQLite, watcher, grammar, and static-gate fixtures can exceed 15 seconds only under the complete lane's concurrent Windows instrumentation. The SQLite busy-journal pacing fixture injects two busy results followed by success under the normal busy budget and observes each inter-attempt delay, keeping schema-setup scheduling outside its timing assertion. The script-only translation-pairing merge suite runs in the exempt-heavy gate because it imports only `scripts/` sources and child processes; V8 instrumentation contributes no threshold coverage there but magnifies Git-process latency. Lefthook concurrency fixtures retain their outcomes with 30-second case budgets and a 10-second process-ready probe, while the installer allows five seconds for a preempted lock owner to publish its record after exclusive creation. Directory-picker composition gives its debounced config write an explicit 15-second poll budget; workspace-context composition fixtures use a test-owned signal without an unrelated one-second deadline. These lane-scoped budgets preserve asserted outcomes, while the 120-minute job deadline still bounds a stuck run. The LSP sources and the ACL-sandbox sources remain in the Windows denominator: stub-based failure-path suites carry every in-process ACL-sandbox file to 100%, and only the runner entry stays excluded — it executes exclusively as a spawned child outside the instrumented run, its behavior pinned end-to-end by the runner suite. Narrow annotated V8 ignores cover only unreachable branches (peer-platform arms and lifecycle-unreachable guards), with their behavior tests retained on the owning platform. +The 16-core lane admits four concurrent outer gates. Workspace build and production-site validation start immediately. Instrumented and exempt-heavy coverage both wait for the complete build: the instrumented corpus includes packer assertions over built `lib/` output, while the exempt gate's temporary Oxlint contract probes must not race source compilation and its packed-image suite must read a complete artifact tree. Every observational gate waits for both coverage gates to settle, regardless of outcome, before entering an available slot; its own `needs` edges still require their predecessors to pass. This also keeps later static gates that create temporary contract files from racing either coverage scan. [In-job partitioned coverage](2026-08-18-in-job-partitioned-coverage.md) uses four single-worker shards, while the exempt-heavy gate receives two workers from `DSH_COVERAGE_MAX_WORKERS=6`, for about six active coverage execution units after build. `publint` is capped at eight workers when the observational inventory starts. Every Vitest project uses forked workers because Node 24's CJS lexer fatal reproduced in shared worker threads on Windows and POSIX. Both coverage gates set Vitest's default per-test and polling budgets to 30 seconds because unrelated process, Git, SQLite, watcher, grammar, and static-gate fixtures can exceed 15 seconds only under the complete lane's concurrent Windows instrumentation. The SQLite busy-journal pacing fixture injects two busy results followed by success under the normal busy budget and observes each inter-attempt delay, keeping schema-setup scheduling outside its timing assertion. The script-only translation-pairing merge suite runs in the exempt-heavy gate because it imports only `scripts/` sources and child processes; V8 instrumentation contributes no threshold coverage there but magnifies Git-process latency. Lefthook concurrency fixtures retain their outcomes with 30-second case budgets and a 10-second process-ready probe, while the installer allows five seconds for a preempted lock owner to publish its record after exclusive creation. Directory-picker composition gives its debounced config write an explicit 15-second poll budget; workspace-context composition fixtures use a test-owned signal without an unrelated one-second deadline. These lane-scoped budgets preserve asserted outcomes, while the 120-minute job deadline still bounds a stuck run. The LSP sources and the ACL-sandbox sources remain in the Windows denominator: stub-based failure-path suites carry every in-process ACL-sandbox file to 100%, and only the runner entry stays excluded — it executes exclusively as a spawned child outside the instrumented run, its behavior pinned end-to-end by the runner suite. Narrow annotated V8 ignores cover only unreachable branches (peer-platform arms and lifecycle-unreachable guards), with their behavior tests retained on the owning platform. The 16-core allocation is the measured capacity point for this inventory. Six-worker coverage trials produced complete passes in 6 minutes 27 seconds and 7 minutes 50 seconds, while exact-head trials with four, three, and two concurrent workers inside one instrumented Vitest process exposed unreliable fixtures and worker exits. Separate single-worker child processes retain process isolation. Historical sixteen-shard samples reduced instrumented coverage to 112.66–122.01 seconds. Under the current post-build graph, sixteen instrumented shards plus two exempt workers would schedule eighteen coverage execution units on a 16-core runner before any production-site tail or system overhead; four shards plus two exempt workers schedule six. Four deliberately trades some single-job latency for lower process-creation pressure under high self-hosted concurrency. A 32-core comparison reduced aggregate gate time by only 1.47 seconds and still triggered the CJS-lexer fatal inside a fork worker, so additional cores did not provide a reliable wall-clock improvement. diff --git a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.zh.md b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.zh.md index 0d81b160fd..b2dba91e4a 100644 --- a/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.zh.md +++ b/.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.zh.md @@ -18,7 +18,7 @@ Status: implemented 原生作业被刻意排除在 `all-checks-passed.needs` 之外,且不使用 `continue-on-error`:聚合流程既不等待它,也不会因它改变结论;该作业则保留自身未被掩盖的结果。工作区构建、生产网站和逐文件 100% 覆盖率检查失败会使原生作业失败。静态检查、文档、包、构建产物、lint 与快照清单在同一作业内作为观测性门禁运行;其失败保持可见,但不会改变原生聚合结果,因为这些检查的阻断性判定由 Linux 负责。 -16 核通道最多同时运行 4 道外层门禁。工作区构建与生产网站验证会立即启动。插桩覆盖率与豁免重型覆盖率都等待完整构建:插桩语料包含针对已构建 `lib/` 输出的打包器断言,豁免门禁的临时 Oxlint 约定探针则不得与源码编译竞态。每道观测性门禁只等待两道覆盖率门禁以任意结果结算后再进入可用槽位;各门禁自身的 `needs` 边仍要求前置门禁通过。这也使随后创建临时约定文件的静态门禁不会与任一覆盖率扫描竞态。[job 内分区覆盖率](2026-08-18-in-job-partitioned-coverage.zh.md)使用 4 个单 worker 分片,豁免重型门禁则从 `DSH_COVERAGE_MAX_WORKERS=6` 获得 2 个 worker,因此构建完成后约有 6 个活动覆盖率执行单元。观测性清单启动时,`publint` 最多使用 8 个 worker。每个 Vitest 项目都使用 fork worker,因为 Node 24 的 CJS lexer 致命故障可在 Windows 与 POSIX 的共享 worker 中复现。两项覆盖率门禁都将 Vitest 默认的单测试和轮询时间预算设为 30 秒,因为在完整通道并发的 Windows 插桩下,多个互不相关的进程、Git、SQLite、watcher、语法和静态门禁 fixture(测试前置数据)可能超过 15 秒。SQLite busy-journal 节奏 fixture 会在普通 busy 预算内先注入两次 busy 结果,再返回成功,并观察每次尝试之间的延迟,使 schema 设置的调度时间不进入该断言。translation-pairing 合并套件只导入 `scripts/` 源码和子进程,因此放入豁免重型套件门禁;V8 插桩不会为它贡献任何阈值覆盖率,却会放大 Git 进程延迟。Lefthook 并发 fixture 保留原有结果,采用 30 秒单用例预算与 10 秒进程就绪探测;安装器则允许被抢占的 lock 持有者在独占创建后用 5 秒发布记录。directory-picker 组合为防抖配置写入提供显式的 15 秒轮询预算;workspace-context 组合 fixture 使用测试自有、没有无关 1 秒截止时间的信号。这些只属于该通道的预算保留了原有断言结果,120 分钟的 job 截止时间仍会约束卡死的运行。LSP 源码与 ACL 沙箱源码仍计入 Windows 分母:基于 stub 的失败路径套件把每个进程内 ACL 沙箱文件都带到 100%,只有 runner 入口保持排除——它只作为 spawn 出的子进程在插桩运行之外执行,其行为由 runner 套件端到端钉住。窄范围且带注释的 V8 ignore 只覆盖不可达分支(另一平台专属分支、生命周期内不可达的防御守卫),其行为测试仍保留在所属平台。 +16 核通道最多同时运行 4 道外层门禁。工作区构建与生产网站验证会立即启动。插桩覆盖率与豁免重型覆盖率都等待完整构建:插桩语料包含针对已构建 `lib/` 输出的打包器断言,豁免门禁的临时 Oxlint 约定探针则不得与源码编译竞态,并且其 packed-image 套件必须读取完整的产物树。每道观测性门禁只等待两道覆盖率门禁以任意结果结算后再进入可用槽位;各门禁自身的 `needs` 边仍要求前置门禁通过。这也使随后创建临时约定文件的静态门禁不会与任一覆盖率扫描竞态。[job 内分区覆盖率](2026-08-18-in-job-partitioned-coverage.zh.md)使用 4 个单 worker 分片,豁免重型门禁则从 `DSH_COVERAGE_MAX_WORKERS=6` 获得 2 个 worker,因此构建完成后约有 6 个活动覆盖率执行单元。观测性清单启动时,`publint` 最多使用 8 个 worker。每个 Vitest 项目都使用 fork worker,因为 Node 24 的 CJS lexer 致命故障可在 Windows 与 POSIX 的共享 worker 中复现。两项覆盖率门禁都将 Vitest 默认的单测试和轮询时间预算设为 30 秒,因为在完整通道并发的 Windows 插桩下,多个互不相关的进程、Git、SQLite、watcher、语法和静态门禁 fixture(测试前置数据)可能超过 15 秒。SQLite busy-journal 节奏 fixture 会在普通 busy 预算内先注入两次 busy 结果,再返回成功,并观察每次尝试之间的延迟,使 schema 设置的调度时间不进入该断言。translation-pairing 合并套件只导入 `scripts/` 源码和子进程,因此放入豁免重型套件门禁;V8 插桩不会为它贡献任何阈值覆盖率,却会放大 Git 进程延迟。Lefthook 并发 fixture 保留原有结果,采用 30 秒单用例预算与 10 秒进程就绪探测;安装器则允许被抢占的 lock 持有者在独占创建后用 5 秒发布记录。directory-picker 组合为防抖配置写入提供显式的 15 秒轮询预算;workspace-context 组合 fixture 使用测试自有、没有无关 1 秒截止时间的信号。这些只属于该通道的预算保留了原有断言结果,120 分钟的 job 截止时间仍会约束卡死的运行。LSP 源码与 ACL 沙箱源码仍计入 Windows 分母:基于 stub 的失败路径套件把每个进程内 ACL 沙箱文件都带到 100%,只有 runner 入口保持排除——它只作为 spawn 出的子进程在插桩运行之外执行,其行为由 runner 套件端到端钉住。窄范围且带注释的 V8 ignore 只覆盖不可达分支(另一平台专属分支、生命周期内不可达的防御守卫),其行为测试仍保留在所属平台。 16 核配置是这项清单经实测选定的容量规格。使用 6 个 coverage worker 的试验分别以 6 分 27 秒和 7 分 50 秒跑出完整通过结果,而在单个插桩 Vitest 进程内使用 4 个、3 个和 2 个并发 worker 的分支头精确试验暴露出不稳定的 fixture 与 worker 退出。相互独立的单 worker 子进程保留进程隔离。历史上的 16 分片样本把插桩覆盖率缩短到 112.66–122.01 秒。在当前的构建后拓扑中,16 个插桩分片加 2 个豁免 worker 会在 16 核运行器上调度 18 个覆盖率执行单元,且尚未计入生产网站的尾部工作或系统开销;4 个分片加 2 个豁免 worker 则调度 6 个。4 个分片刻意用部分单 job 延迟换取自托管高并发下更低的进程创建压力。32 核对比仅将聚合门禁时间缩短 1.47 秒,且仍在 fork worker 内触发 CJS lexer 致命故障,因此增加核心数没有带来可靠的墙钟时间改善。 diff --git a/.agents/notes/implemented/process/2026-08-09-md-fragment-anchor-gate.i18n.yaml b/.agents/notes/implemented/process/2026-08-09-md-fragment-anchor-gate.i18n.yaml index 4d85c63d60..e7f6000221 100644 --- a/.agents/notes/implemented/process/2026-08-09-md-fragment-anchor-gate.i18n.yaml +++ b/.agents/notes/implemented/process/2026-08-09-md-fragment-anchor-gate.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-09-md-fragment-anchor-gate.md -2026-08-09-md-fragment-anchor-gate.md: a5c4029f5b11f464e09356915d5d2f9134bee616 -2026-08-09-md-fragment-anchor-gate.zh.md: 50c941b3e2d43e4991ee3a748b1b50b36611e34d +2026-08-09-md-fragment-anchor-gate.md: de0d02318b63c4892233c8c2ebe8457152def10c +2026-08-09-md-fragment-anchor-gate.zh.md: 54ac751900b508fb0dc6fa0e20f0b50b11e252a9 diff --git a/.agents/notes/implemented/process/2026-08-09-md-fragment-anchor-gate.md b/.agents/notes/implemented/process/2026-08-09-md-fragment-anchor-gate.md index a5c4029f5b..de0d02318b 100644 --- a/.agents/notes/implemented/process/2026-08-09-md-fragment-anchor-gate.md +++ b/.agents/notes/implemented/process/2026-08-09-md-fragment-anchor-gate.md @@ -14,7 +14,7 @@ English | [中文](2026-08-09-md-fragment-anchor-gate.zh.md) The slug function differs from `gen-cordis-catalog`'s region-anchor slugger (which drops underscores): the generator's headings are always reachable through its explicit `` anchors, so the two need not share one rule. Chinese pair sides follow the existing repository convention (`docs/glossary.zh.md`, `docs/cordis-primer.zh.md`): keep the English fragment in the link and place an explicit `` before the Chinese heading, so both language sides expose identical anchors. -The 15 broken fragments are fixed in the same change: stale slugs retargeted to the current headings, the relocated no-timeout contract now linked at its owning group README, and four zh documents given explicit anchors. `docs/AGENTS.md` and the `dsh-doc-standards` skill no longer prescribe the manual anchor grep for Markdown links; it survives only for anchors cited from TypeScript strings whose output never reaches gate-scanned Markdown (the three scanned references all render into scanned pages, so the gate covers them through the committed output). +The 15 broken fragments are fixed in the same change: stale slugs retargeted to the current headings, the relocated no-timeout contract now linked at its owning group README, and four zh documents given explicit anchors. `docs/AGENTS.md` and the `dsh-doc` skill no longer prescribe the manual anchor grep for Markdown links; it survives only for anchors cited from TypeScript strings whose output never reaches gate-scanned Markdown (the three scanned references all render into scanned pages, so the gate covers them through the committed output). ## Verification diff --git a/.agents/notes/implemented/process/2026-08-09-md-fragment-anchor-gate.zh.md b/.agents/notes/implemented/process/2026-08-09-md-fragment-anchor-gate.zh.md index 50c941b3e2..54ac751900 100644 --- a/.agents/notes/implemented/process/2026-08-09-md-fragment-anchor-gate.zh.md +++ b/.agents/notes/implemented/process/2026-08-09-md-fragment-anchor-gate.zh.md @@ -14,7 +14,7 @@ Status: implemented slug 函数与 `gen-cordis-catalog` 的区块锚点 slugger 不同(后者丢弃下划线):生成器的标题总能通过其显式 `` 锚点到达,两者无需共享一条规则。中文侧沿用既有语料惯例(`docs/glossary.zh.md`、`docs/cordis-primer.zh.md`):链接保留英文 fragment,在中文标题前放置显式 ``,使两个语言侧暴露相同的锚点。 -15 条坏 fragment 在同一变更中修复:陈旧 slug 重定向到当前标题,搬迁的无超时约定改链其属主 group README,四份中文文档补上显式锚点。`docs/AGENTS.md` 与 `dsh-doc-standards` skill 不再要求为 Markdown 链接手工 grep 锚点;人工 grep 只对输出从不进入受检 Markdown 的 TypeScript 字符串锚点保留(扫描到的三处全部渲染进受检页面,gate 经由提交的产物覆盖它们)。 +15 条坏 fragment 在同一变更中修复:陈旧 slug 重定向到当前标题,搬迁的无超时约定改链其属主 group README,四份中文文档补上显式锚点。`docs/AGENTS.md` 与 `dsh-doc` skill 不再要求为 Markdown 链接手工 grep 锚点;人工 grep 只对输出从不进入受检 Markdown 的 TypeScript 字符串锚点保留(三处受扫描引用全部渲染进受检页面,因此 gate 经由提交的产物覆盖它们)。 ## 验证 diff --git a/.agents/notes/proposed/process/2026-08-20-audience-first-documentation-quality.i18n.yaml b/.agents/notes/proposed/process/2026-08-20-audience-first-documentation-quality.i18n.yaml new file mode 100644 index 0000000000..3d772fc810 --- /dev/null +++ b/.agents/notes/proposed/process/2026-08-20-audience-first-documentation-quality.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/proposed/process/2026-08-20-audience-first-documentation-quality.md +2026-08-20-audience-first-documentation-quality.md: ebf9a30a7096f99b0ffa2f2bc0b61be6a178772a +2026-08-20-audience-first-documentation-quality.zh.md: d7d897c592d0ebfa225074978b8b78d7994900a4 diff --git a/.agents/notes/proposed/process/2026-08-20-audience-first-documentation-quality.md b/.agents/notes/proposed/process/2026-08-20-audience-first-documentation-quality.md new file mode 100644 index 0000000000..ebf9a30a70 --- /dev/null +++ b/.agents/notes/proposed/process/2026-08-20-audience-first-documentation-quality.md @@ -0,0 +1,138 @@ +# Agent Note: Audience-first documentation quality criteria + +Status: proposed + +English | [中文](2026-08-20-audience-first-documentation-quality.zh.md) + +## Problem + +The documentation system has strong placement, freshness, linking, bilingual, and source-equivalence checks, but it does not define “brief, intuitive, and friendly” as reviewable outcomes for users, newcomers, developers, and agents. All `doc-sync` checks and translation pairs pass, while the following design problems remain. The first three findings are the design priorities; the capacity finding explains why adding more standing rules will not solve them. + +### Semantic correctness can pass without a current owner + +The gates prove structure and generated freshness, not that maintained prose still names the live mechanism. The former `dsh-doc-site-sync` skill told authors to reuse a nonexistent `en-docs` sidebar and to add sections to a removed `sectionOrder`; [website/docs.ts](../../../../website/docs.ts) owns `en-guide`, `en-develop`, `en-reference`, and `sections`. The implemented [product-first README decision](../../implemented/process/2026-07-22-product-first-root-readme.md) describes an internal-testing notice and ACP, Python, and JSON-RPC surface sections absent from the [root README](../../../../README.md), although implemented Agent Notes must track shipped facts. + +The budget policy has the same split. [docs/AGENTS.md](../../../../docs/AGENTS.md#wordcount-budgets) states a 1,800-word target and 5% headroom for `architecture.md`, but the [budget manifest](../../../../scripts/doc-budgets.manifest.json) allows 2,400 words while the file contains 1,313. The budget gate passes because it checks the manifest ceiling, not the target or ratchet rule. High-impact prose therefore needs a named source or a focused check that consumes the source; a second hand-written copy is not a freshness mechanism. + +### Reader success is implicit rather than testable + +The standard classifies pages as tutorials or references and asks authors to classify a tutorial reader privately. It does not require a reviewable statement of the reader’s starting state, desired outcome, shortest successful path, likely failure, or next useful page. A document can therefore satisfy tier placement, links, word limits, and Markdown structure without proving that its intended reader can complete the task. + +The public site makes the pressure visible. Each locale publishes 84 pages: 3 guide pages, 17 developer pages, and 63 reference pages. The 13 English files under `docs/user/` contain 7,540 words, while 47 subsystem pages contain 100,759 words. The short Web quick start is a good product entry, but no corpus-level criterion verifies that a first-time user, a plugin newcomer, and a maintainer each has one obvious path from entry to outcome and recovery. + +### Generated accuracy and retrieval quality are conflated + +The repository contains 19 fully generated English Markdown files with 49,611 words. Forty-four of 47 subsystem pages also contain generated Cordis regions; those regions contribute 34,622 of the subsystem tier’s 100,759 words. `config-catalog.md` has 14,807 words, `tool-catalog.md` has 10,599, and the largest mixed subsystem pages contain 5,600–7,781 words. + +These are legitimate exhaustive references, so a blanket word limit would delete value. Their generators prove completeness and freshness, but the standard has no separate retrieval criterion for an agent with a limited context window or a human looking for one answer. A generated reference needs a compact entry layer, stable grouping, direct anchors, and a split rule based on lookup cost; exhaustive detail can remain exhaustive behind that entry layer. + +### The standard has no room for its next rule + +The standing documentation file is 1,320 words against a 1,320-word ceiling and a stated 1,250-word target. Root `AGENTS.md` is 1,936 words against a 1,600-word target, `packages/AGENTS.md` is 672 against 650, and `packages/README.md` is 969 against 600. The frozen ceilings prevent further growth but do not create a place for audience and outcome criteria. Adding more standing prose would deepen the problem the standard is meant to prevent. + +### Baseline + +The audit excludes `vendor/`, frozen `.agents/notes/archived/`, recorded snapshots, and fixtures. It counts 1,042 English Markdown files and 986 Chinese counterparts in the maintained corpus, with 1,106,138 English words. Active Agent Notes account for 580 files and 637,850 words; Markdown under `packages/` accounts for 276 files and 225,630 words; `docs/` accounts for 112 files and 193,456 words. These quantities describe maintenance and retrieval pressure, not defects by themselves. + +The system’s strongest properties should remain: one fact owner by tier, canonical Markdown projected into the website without copies, complete bilingual pairing, generated catalogs that fail when source changes, type-equivalent declarations, compilable TypeScript examples, checked links and anchors, and package-local model-experience and limitation contracts. The proposal changes quality criteria and entry structure, not those guarantees. + +## Proposal + +Adopt one audience-first quality contract with five definitions: + +- **Brief** means the common path contains only the facts needed for its outcome. Exhaustive contracts remain available through a direct link or generated detail; brevity never means deleting required behavior, failures, ownership, or limitations. +- **Intuitive** means the page establishes its reader’s starting state, introduces prerequisites before dependent concepts, offers one obvious next action, and uses the product or domain terms a reader will search for. +- **Friendly** means a reader can recognize success, understand material risk before acting, recover from the likely failure, and reach the next relevant depth without first learning unrelated architecture. +- **Accurate** means every durable claim has one owner and a verification path appropriate to its risk. Generated facts derive from source; hand-written workflow values link to or consume their owner instead of copying enums and paths. +- **Agent-readable** means headings, anchors, terminology, ownership, and current-versus-proposed status are explicit enough to retrieve the needed section without loading an entire corpus or reconstructing review history. + +### Prototype rules + +The [dsh-doc skill](../../../skills/dsh-doc/SKILL.md) owns the first executable version of these rules. The SQLite README pair uses the shipped packed-row implementation as evidence rather than treating its prior prose as authority. + +- Every authored package README starts with searchable YAML. A Skill-style `description` and mechanically derived `kind` are required. Four kinds map one-to-one to four skill templates: `package-group` (group map), `package-reference` (plugin or service package), `package-library` (plain module entry), and `package-bundle` (`dsh.bundle.patch`). The counterpart path, hashes, and physical line alignment belong to the merge-safe sidecar and its gate, so README frontmatter contains no `i18n` block. The title or package manifest already owns the name, the document job expresses its audience, and tags remain absent until a governed taxonomy and search consumer proves value beyond full-text search. +- Authored pages start with a three-to-five-sentence `Summary`, then a linked `Table of Contents`. Format-owned Agent Notes, postmortems, generated fragments, and machine files keep their required skeletons. +- Each substantive section starts with a short orientation before subsections, tables, or code, and the page progresses from basic user use to advanced developer and maintainer detail. +- English technical prose uses an ASD-STE100-inspired, non-certified clarity review: explicit actors and actions, stable terms, direct verbs, separated instructions and conditions, and preserved modality, exceptions, timing, and numbers. The 20-word instruction and 25-word description limits are review prompts. Precision overrides them. +- Package contracts remain beside code. Cross-package material moves deliberately toward `docs/learn/overview/`, `docs/learn/cordis/`, `docs/learn/practices/`, `docs/user/`, `docs/developer/`, `docs/developer/discussion/`, `docs/scratch/`, and the parallel `docs/subsystems/` tier. +- English and Chinese pages keep equal authority, matching structure, links, code, frontmatter layout, and exact physical line count. +- Inline pair metadata is the target replacement for sidecars. The prototype may carry both until the verifier, merge driver, recovery flow, generated-region recorder, and archive checks consume a non-self-referential pair digest. +- Repository-root internal links are the target authoring model. The prototype keeps renderer-valid relative links because leading `/` currently leaves the repository on GitHub, bypasses `verify-md-links`, and remains unprojected by the website. +- `Further Exploration` is an optional newcomer route to three to seven adjacent pages. +- Every authored page ends with `Dev Note`, the sole place for active rough context. It remains non-authoritative, links rather than duplicates task state, and is promoted or cleaned when work closes. +- Independently searchable rules, practices, examples, and decisions use small files under descriptive folders when they have distinct owners or change cadence; tightly coupled obligations stay together. + +### Criteria by document job + +| Job | Primary outcome | Required entry information | Verification | +|---|---|---|---| +| Product quick start | Complete one representative task | Prerequisites, one launch path, first success, safety boundary, next step | Built or packaged smoke for the documented path plus link/site checks | +| User task guide | Complete or recover one user task | Starting UI/API state, ordered actions, observable result, likely failure and recovery | Behavior test, screenshot review when visual state matters, or named manual owner | +| Contributor tutorial | Reach a checked development state | Supported runtime, setup commands, expected result, narrow follow-up commands | Clean-checkout command smoke on a supported environment | +| Architecture overview | Reconstruct the system from one page | Product composition, owners, dependency direction, extension points, links to detail | Source-backed package or graph checks plus focused human review | +| Package or subsystem reference | Look up one contract without reading implementation | Scope, owned types or behavior, failures, lifecycle, limitations, related owners | Existing JSDoc, type-equivalence, generated-region, README, and link checks | +| Generated reference | Locate one exact item and trust its completeness | Scope, generation owner, grouping/index, stable anchors, related conceptual guide | Deterministic `--check`, completeness fixture, site build, and retrieval-size report | +| Agent instruction or skill | Apply one workflow without stale copied values | Scope, authority links, required decisions, exact commands only when owned here | Metadata/link checks and focused tests for copied machine values | +| Proposed or implemented Agent Note | Understand a decision, trade-off, and state | Problem, proposal or decision, alternatives, acceptance or consequences | Existing lifecycle, format, pairing, and supersession checks; review owns semantic currency | + +The table belongs in one canonical quality reference. `docs/AGENTS.md` should retain only the short standing orders needed whenever documentation is edited and link to that reference. This creates budget headroom instead of placing another complete standard inside agent context. + +### Generated-reference entry and detail layers + +Every generated reference should expose a compact entry layer before exhaustive output: scope, intended lookup, grouping or index, direct links to conceptual guidance, and the generator/check command. Generators should report page words, entry count, heading count, and largest section. A page crosses a review threshold when one lookup requires scanning unrelated groups or when one page dominates agent context; the owner then splits it by a stable domain already present in source metadata rather than by an arbitrary word slice. + +The first prototype should use one large catalog and one mixed subsystem page. It should compare lookup steps, generated diff size, build time, route stability, and agent context needed for representative questions before any corpus-wide split. Existing anchors need aliases when routes move. + +### Enforcement slices + +1. Create and validate `dsh-doc`, then rewrite the `session-persistence-sqlite` README pair as a line-aligned, metadata-bearing prototype without changing runtime claims. +2. Review the rendered prototype with newcomer, user, developer, and agent tasks; revise the skill before enforcing the format elsewhere. +3. Add narrow metadata, section-order, line-alignment, link-resolution, and pairing fixtures. Keep sidecars until every merge and recovery consumer has replacement support. +4. Extract accepted standing rules into one canonical quality reference, condense `docs/AGENTS.md` below its target, and organize one coherent `docs/` topic at a time with atomic link/navigation repair. +5. Prototype generated-reference entry/detail separation on `config-catalog.md` and `docs/subsystems/core.md`; apply confirmed patterns elsewhere only after measured lookup cost falls without lost facts or route churn. + +This sequence keeps each change independently reviewable. The first three slices improve criteria and correctness without rewriting the corpus; the generated-doc prototype supplies evidence before a broader information-architecture change. + +Slices 1–3 have shipped in this form: `dsh-doc` is the consolidated standard (`dsh-doc-standards` and `dsh-doc-site-sync` are folded into it, and the site workflow carries the corrected sidebar values), the `session-persistence-sqlite` README pair is the reference example, and `pnpm run test:docs` enforces the metadata, pairing, and quick documentation checks. Slices 4–5 remain open. + +### Non-goals + +This proposal does not shorten exhaustive facts, merge audience tiers, publish internal decision records, restore an Agent Note index, split tightly coupled rules for file-count symmetry, or treat the audit as user research. It does not delete current pairing or link infrastructure before its replacement passes equivalent recovery and rendering checks. + +## Alternatives considered + +**Apply one word ceiling to every document.** Rejected because exhaustive reference rows, public contracts, and decision rationale can be long and correct. Entry-path length and lookup cost are the relevant constraints for those jobs. + +**Require one universal page template or audience frontmatter.** Rejected because it would add ceremony to generated pages, package references, and short instructions without proving reader success. The standard defines outcomes by document job, uses `kind` only where it selects a concrete package-document standard, and adds only fields that a focused check or reviewer consumes. + +**Use readability scores as the quality gate.** Rejected because formulas penalize exact technical terms and cannot detect wrong ownership, missing failure behavior, stale commands, or a broken reader journey. + +**Rewrite or split the full corpus immediately.** Rejected because the current system is mechanically healthy and many long references are appropriately exhaustive. A prototype should prove a retrieval improvement before route and translation churn spreads. + +**Keep the existing gates and rely on review for friendliness.** Rejected because the stale workflow values and budget-policy mismatch show that review alone does not preserve copied semantic claims, and the current gates do not ask whether a reader can complete a task. + +## Acceptance criteria + +- One canonical quality reference defines brief, intuitive, friendly, accurate, and agent-readable documentation by document job. +- `.agents/skills/dsh-doc` validates and directly links its metadata, structure/hierarchy, and review/prototype references without duplicating their detailed rules in `SKILL.md`. +- The SQLite README pair demonstrates searchable YAML, Summary, Table of Contents, user-to-developer progression, Further Exploration, final Dev Note, structural parity, and exact line-count equality while preserving verified package contracts. +- `docs/AGENTS.md` links that reference, remains sufficient as standing instruction, and is below its target with at least 5% headroom. +- The root user path, Web quick start, first-plugin tutorial, contributor setup, and architecture overview each name an observable outcome and a verification owner without duplicating implementation detail. +- The budget manifest records both target and temporary ceiling, and its check reports or rejects a violated headroom/ratchet state. +- The docs-site workflow contains no copied invalid sidebar name or section-owner claim, and a focused test prevents recurrence. +- The sidecar remains the single consistency record because it preserves equal authority, last-confirmed-text recovery, automatic merge safety, generated-region recording, and archive sealing without creating owner-file conflicts. +- An accepted repository-root link form renders correctly on GitHub and the documentation site and remains locally target/anchor checked before relative links are migrated. +- One large standalone catalog and one mixed subsystem page demonstrate a compact entry layer and lower measured lookup cost while preserving exhaustive generated truth, stable links, bilingual pairing, and deterministic freshness. +- `pnpm run doc-sync`, `pnpm run lint`, the focused new checks, and `git diff --check` pass. + +## Risks + +- Metadata can become boilerplate; the package README check therefore permits only fields with current retrieval, template-selection, or bilingual-consistency consumers. +- Hard sentence limits can fragment explanations or separate a condition from its consequence. The controlled-English word counts remain review prompts, and exact contracts override them. +- Exact line alignment can pressure translators into unnatural prose; review must protect meaning and may revise both sides together rather than weaken one. +- Splitting generated references can increase routes and link maintenance; prototypes must preserve aliases and measure the trade-off. +- A semantic check can become a repository-topology scanner that blocks legitimate changes; checks should cover high-risk copied values and representative journeys, while review owns prose meaning. +- Package README quick-reference tables manually repeat selected configuration defaults; until a source-driven check owns them, reviewers must verify changed values against source and the generated config catalog and keep the tables selected rather than exhaustive. +- Optimizing for short agent context can make human references fragmented; each split needs one stable conceptual owner and one obvious navigation path. +- A permanent Dev Note can become a second queue or stale history dump; completion must promote durable truth and remove resolved chatter. +- The audit uses repository structure, gates, and representative pages rather than user research. Before broad rollout, maintainers should validate the proposed reader outcomes with actual newcomer, user, developer, and agent tasks. diff --git a/.agents/notes/proposed/process/2026-08-20-audience-first-documentation-quality.zh.md b/.agents/notes/proposed/process/2026-08-20-audience-first-documentation-quality.zh.md new file mode 100644 index 0000000000..d7d897c592 --- /dev/null +++ b/.agents/notes/proposed/process/2026-08-20-audience-first-documentation-quality.zh.md @@ -0,0 +1,138 @@ +# Agent Note: 以受众为先的文档质量标准 + +Status: proposed + +[English](2026-08-20-audience-first-documentation-quality.md) | 中文 + +## 问题 + +文档系统拥有健全的放置、新鲜度、链接、双语和源等价性检查,却没有把「简短、直观、友好」定义成可供用户、新人、开发者和 agent(智能体)评审的结果。全部 `doc-sync`(文档同步门禁)检查和全部翻译配对均通过,但仍存在以下设计问题。前三项发现是设计重点;容量问题则解释了为什么增加更多常驻规则无法解决它们。 + +### 语义正确性可以在没有现行归属者的情况下通过检查 + +这些门禁证明结构和生成内容的新鲜度,却不能证明维护中的正文仍指向实际机制。以前的 `dsh-doc-site-sync` 技能曾要求作者复用并不存在的 `en-docs` 侧边栏,还要求把章节加入已经移除的 `sectionOrder`;[website/docs.ts](../../../../website/docs.ts)实际拥有 `en-guide`、`en-develop`、`en-reference` 和 `sections`。已实现的[产品优先 README 决策](../../implemented/process/2026-07-22-product-first-root-readme.zh.md)描述了内部测试说明,以及 ACP、Python 与 JSON-RPC 界面章节,但[根 README](../../../../README.zh.md)并无这些内容;与此同时,已实现 Agent Note 必须跟随已交付事实。 + +预算策略也存在相同的分裂。[docs/AGENTS.md](../../../../docs/AGENTS.md#wordcount-budgets)为 `architecture.md` 规定 1,800 词目标和 5% 余量,但[预算 manifest(元数据清单)](../../../../scripts/doc-budgets.manifest.json)允许 2,400 词,而该文件实际包含 1,313 词。预算门禁之所以通过,是因为它只检查 manifest 上限,不检查目标或棘轮规则。因此,高影响正文需要一个具名真源或消费真源的聚焦检查;第二份手写副本不是新鲜度机制。 + +### 读者成功与否是隐含判断,而不是可测试结果 + +标准把页面分成教程和参考,并要求作者私下判断教程读者的起始水平。标准没有要求留下可供评审的读者起始状态、预期结果、最短成功路径、常见失败或下一篇有用页面。因此,一份文档可以满足层级放置、链接、词数限制和 Markdown 结构,却没有证明目标读者能完成任务。 + +公共站点直观呈现了这种压力。每种语言发布 84 个页面:3 个指南页面、17 个开发页面和 63 个参考页面。`docs/user/` 下的 13 个英文文件共有 7,540 词,而 47 个子系统页面共有 100,759 词。简短的 Web 快速开始是良好的产品入口,但全语料没有标准来验证首次使用者、插件新人和维护者是否都能沿一条明确路径从入口走到结果与故障恢复。 + +### 生成内容的准确性与检索质量混为一谈 + +仓库包含 19 个完全生成的英文 Markdown 文件,共 49,611 词。47 个子系统页面中有 44 个也包含生成的 Cordis 区域;这些区域占子系统层级 100,759 词中的 34,622 词。`config-catalog.md` 有 14,807 词,`tool-catalog.md` 有 10,599 词,最大的混合子系统页面则有 5,600–7,781 词。 + +这些内容是正当的穷尽式参考,因此统一词数限制反而会删除价值。生成器证明完整性和新鲜度,但标准没有为上下文窗口有限的 agent,或只寻找一个答案的人类读者另设检索标准。生成参考需要紧凑的入口层、稳定分组、直接锚点,以及根据查询成本触发的拆分规则;穷尽式细节可以在该入口层之后继续保持穷尽。 + +### 标准没有容纳下一条规则的空间 + +常驻文档标准有 1,320 词,等于 1,320 词上限,并超过声明的 1,250 词目标。根 `AGENTS.md` 有 1,936 词,目标为 1,600;`packages/AGENTS.md` 有 672 词,目标为 650;`packages/README.md` 有 969 词,目标为 600。冻结的上限能阻止进一步增长,却没有为受众和结果标准创造位置。继续增加常驻正文会加深该标准本应防止的问题。 + +### 基线 + +本次审计排除 `vendor/`、冻结的 `.agents/notes/archived/`、录制快照和 fixture(测试前置数据)。受维护语料包含 1,042 个英文 Markdown 文件和 986 个中文对侧文件,共 1,106,138 个英文词。活跃 Agent Note 占 580 个文件和 637,850 词;`packages/` 下的 Markdown 占 276 个文件和 225,630 词;`docs/` 占 112 个文件和 193,456 词。这些数量描述维护和检索压力,本身并不构成缺陷。 + +系统最强的性质应予保留:按层级为每项事实指定一个归属者;把规范 Markdown 投影到站点而不创建副本;完整双语配对;源变更时快速失败的生成目录;源等价声明;可编译的 TypeScript 示例;受检查的链接与锚点;以及包局部的模型体验与限制约定。提案改变的是质量标准和入口结构,而不是这些保证。 + +## 提案 + +采用一套以受众为先的质量约定,并给出五项定义: + +- **简短**表示常用路径只包含达成结果所需的事实。穷尽式约定仍可通过直接链接或生成细节访问;简短绝不意味着删除必要行为、失败、所有权或限制。 +- **直观**表示页面会确定读者的起始状态,在依赖概念之前介绍前置知识,提供一个明确的下一步操作,并使用读者会搜索的产品或领域术语。 +- **友好**表示读者能识别成功,在操作前理解实质风险,从常见失败中恢复,并在无需先学习无关架构的情况下进入下一层相关细节。 +- **准确**表示每项持久事实都有一个归属者和与风险相称的验证路径。生成事实来自源;手写工作流值链接或消费其归属者,而不是复制枚举和路径。 +- **便于 agent 阅读**表示标题、锚点、术语、所有权以及当前与提议状态足够明确,无需加载整个语料或重建评审历史即可检索所需章节。 + +### 原型规则 + +[dsh-doc skill](../../../skills/dsh-doc/SKILL.md) 负责这些规则的首个可执行版本。SQLite README 对以已交付的分片行实现为证据,而不把其旧版正文当作权威。 + +- 每个撰写型包 README 都以可搜索 YAML 开头。Skill 风格的 `description` 与按机制推导的 `kind` 为必填字段。四种 kind 与四个技能模板一一对应:`package-group`(组地图)、`package-reference`(插件或服务包)、`package-library`(纯模块入口)与 `package-bundle`(`dsh.bundle.patch`)。对照文件路径、哈希与物理行对齐由支持自动合并的 sidecar 及其门禁负责,因此 README frontmatter 不包含 `i18n` 块。名称已由标题或包 manifest 归属,受众已由文档职责表达;在受治理的标签分类与搜索消费方证明其价值超过全文检索之前,不加入标签。 +- 撰写型页面先写三至五句的 `Summary`,再写带链接的 `Table of Contents`。由格式约束的 Agent Note、事故复盘、生成片段和机器文件保留其必需骨架。 +- 每个实质章节在子章节、表格或代码之前先给出简短引导,页面则从基础用户用法逐步进入高级开发者与维护者细节。 +- 英文技术正文采用受 ASD-STE100 启发但不宣称认证的清晰度评审:明确行动者与动作,稳定使用术语,使用直接动词,拆分指令与条件,并完整保留情态、例外、时序与数值。指令 20 词和描述 25 词的限制仅作评审提示。准确性高于句长。 +- 包约定留在代码旁。跨包材料有计划地向 `docs/learn/overview/`、`docs/learn/cordis/`、`docs/learn/practices/`、`docs/user/`、`docs/developer/`、`docs/developer/discussion/`、`docs/scratch/` 和平行的 `docs/subsystems/` 层级迁移。 +- 英文和中文页面保持同等权威,并在结构、链接、代码、frontmatter 布局和精确物理行数上一一对应。 +- 内联配对元数据是伴随文件的目标替代方案。在验证器、合并驱动、恢复流程、生成区域记录器和归档检查消费非自引用配对摘要之前,原型可以同时保留两者。 +- 仓库根级内部链接是目标撰写模型。原型保留渲染器可用的相对链接,因为前导 `/` 当前会在 GitHub 上离开仓库、绕过 `verify-md-links`,并且不会由站点投影。 +- `Further Exploration` 是可选的新人路径,链接三至七个相邻页面。 +- 每个撰写型页面都以 `Dev Note` 结尾,作为活跃粗略上下文的唯一位置。它保持非权威状态,只链接而不复制任务状态,并在工作结束时完成提升或清理。 +- 可独立搜索的规则、实践、示例和决策在拥有不同归属者或变更节奏时,使用描述性目录下的小文件;紧密耦合的义务保留在一起。 + +### 按文档职责划分的标准 + +| 职责 | 主要结果 | 必需入口信息 | 验证 | +|---|---|---|---| +| 产品快速开始 | 完成一项有代表性的任务 | 前置条件、一个启动路径、首次成功、安全边界、下一步 | 文档路径的构建或打包冒烟测试,加链接和站点检查 | +| 用户任务指南 | 完成一项用户任务或从中恢复 | 起始 UI/API 状态、有序操作、可观察结果、常见失败及恢复 | 行为测试;涉及视觉状态时评审截图;或指定人工归属者 | +| 贡献者教程 | 进入通过检查的开发状态 | 支持的运行时、设置命令、预期结果、聚焦的后续命令 | 在受支持环境中对干净检出运行命令冒烟测试 | +| 架构概览 | 从一个页面重建系统 | 产品组合、归属者、依赖方向、扩展点、细节链接 | 由源支持的包或图检查,加聚焦人工评审 | +| 包或子系统参考 | 无需阅读实现即可查到一项约定 | 范围、归属的类型或行为、失败、生命周期、限制、相关归属者 | 既有 JSDoc、类型等价、生成区域、README 和链接检查 | +| 生成参考 | 找到一个精确条目并信任其完整性 | 范围、生成归属者、分组或索引、稳定锚点、相关概念指南 | 确定性 `--check`、完整性 fixture、站点构建和检索规模报告 | +| Agent 指令或 skill(技能) | 在没有陈旧复制值的情况下执行一项工作流 | 范围、权威链接、必需决策、仅在此处归属时写入精确命令 | 元数据或链接检查,以及针对复制机器值的聚焦测试 | +| 提议或已实现 Agent Note | 理解决策、取舍与状态 | 问题、提案或决策、备选方案、验收或后果 | 既有生命周期、格式、配对和取代检查;语义时效性由评审负责 | + +该表应归属一份规范质量参考。`docs/AGENTS.md` 只保留每次编辑文档都需要的简短常驻规则,并链接到该参考。这样可以创造预算余量,而不是把另一份完整标准放进 agent 上下文。 + +### 生成参考的入口层与细节层 + +每份生成参考都应在穷尽式输出之前提供紧凑入口层:范围、预期查询、分组或索引、概念指南的直接链接,以及生成器或检查命令。生成器应报告页面词数、条目数、标题数和最大章节。当一次查询需要扫描无关分组,或单页主导 agent 上下文时,该页面便跨过评审阈值;随后,归属者按照源元数据中已有的稳定领域拆分页面,而不是按任意词数切片。 + +首个原型应选择一个大型目录和一个混合子系统页面。在进行全语料拆分前,它应比较查询步骤、生成 diff 大小、构建时间、路由稳定性,以及代表性问题所需的 agent 上下文。路由移动时,既有锚点需要保留别名。 + +### 执行切片 + +1. 创建并验证 `dsh-doc`,再把 `session-persistence-sqlite` README 对改写为行对齐、带元数据的原型,同时不改变运行时事实。 +2. 用新人、用户、开发者和 agent 任务评审渲染后的原型;先修订 skill,再在其他位置强制执行该格式。 +3. 添加聚焦的元数据、章节顺序、行对齐、链接解析和配对 fixture。在每个合并与恢复消费方都有替代支持前,保留伴随文件。 +4. 把已接受的常驻规则提取到一份规范质量参考,将 `docs/AGENTS.md` 精简到目标以下,并且一次只组织一个内聚的 `docs/` 主题,同时原子地修复链接与导航。 +5. 在 `config-catalog.md` 和 `docs/subsystems/core.md` 上制作生成参考入口层与细节层分离的原型;只有实测查询成本下降且没有丢失事实或造成路由扰动,才把确认后的模式应用到其他位置。 + +该顺序使每项变更都能独立评审。前三个切片在不重写语料的情况下改进标准与正确性;生成文档原型则在更广的信息架构变更前提供证据。 + +切片 1–3 已按此形式交付:`dsh-doc` 成为合并后的标准(`dsh-doc-standards` 与 `dsh-doc-site-sync` 已并入其中,站点工作流携带修正后的侧边栏值),`session-persistence-sqlite` README 对是参考示例,`pnpm run test:docs` 强制执行元数据、配对与快速文档检查。切片 4–5 仍待完成。 + +### 非目标 + +本提案不缩减穷尽式事实,不合并受众层级,不发布内部决策记录,不恢复 Agent Note 索引,不为了文件数对称而拆分紧密耦合的规则,也不把此次审计当作用户研究。在替代方案通过等价的恢复与渲染检查前,本提案不删除现有配对或链接基础设施。 + +## 考虑过的备选方案 + +**为每份文档设置统一词数上限。**不予采纳,因为穷尽式参考条目、公开约定和决策理由可以既长又正确。对这些文档职责而言,入口路径长度与查询成本才是相关约束。 + +**强制使用统一页面模板或受众前置元数据。**不予采纳,因为这会给生成页面、包参考和简短指令增加形式,却不能证明读者成功。标准按文档职责定义结果,仅在 `kind` 能选择具体包文档标准时使用它,并且只添加聚焦检查或评审会消费的字段。 + +**把可读性分数作为质量门禁。**不予采纳,因为公式会惩罚精确技术术语,却无法发现错误所有权、遗漏失败行为、陈旧命令或破损的读者路径。 + +**立即重写或拆分全部语料。**不予采纳,因为现有系统在机制上健康,许多长参考也确实应保持穷尽。原型应先证明检索有所改善,再扩散路由和翻译扰动。 + +**保留现有门禁,让评审负责友好程度。**不予采纳,因为陈旧工作流值和预算策略不一致说明,仅凭评审无法保留复制的语义事实,而现有门禁也不询问读者是否能完成任务。 + +## 验收标准 + +- 一份规范质量参考按文档职责定义简短、直观、友好、准确和便于 agent 阅读的文档。 +- `.agents/skills/dsh-doc` 通过验证,并直接链接其元数据、结构或层级及评审或原型参考,而不在 `SKILL.md` 中复制这些参考的详细规则。 +- SQLite README 对展示可搜索 YAML、Summary、Table of Contents、从用户到开发者的渐进结构、Further Exploration、结尾 Dev Note、结构一致性和精确行数相等,同时保留已验证的包约定。 +- `docs/AGENTS.md` 链接该参考,仍足以充当常驻指令,并低于其目标且至少保留 5% 余量。 +- 根级用户路径、Web 快速开始、第一个插件教程、贡献者设置和架构概览各自给出一个可观察结果与验证归属者,同时不复制实现细节。 +- 预算 manifest 同时记录目标与临时上限,其检查会报告或拒绝违反余量或棘轮规则的状态。 +- 文档站工作流不再包含复制的无效侧边栏名称或章节归属声明,并有聚焦测试防止复发。 +- sidecar 继续作为唯一一致性记录,因为它能保留同等权威、上次确认文本恢复、自动合并安全、生成区域记录和归档封存,同时不会在正文文件中制造冲突。 +- 一个已接受的仓库根级链接格式在迁移相对链接前能在 GitHub 与文档站正确渲染,并继续接受本地目标或锚点检查。 +- 一个大型独立生成目录页和一个混合子系统页面展示紧凑入口层与更低的实测查询成本,同时保留穷尽式生成事实、稳定链接、双语配对和确定性新鲜度。 +- `pnpm run doc-sync`、`pnpm run lint`、聚焦的新检查和 `git diff --check` 均通过。 + +## 风险 + +- 元数据可能沦为样板;因此包 README 检查只允许具有现行检索、模板选择或双语一致性消费方的字段。 +- 硬性句长限制可能割裂说明,或把条件与后果分开。受控英语的词数限制仅作评审提示,精确约定优先于句长。 +- 精确行对齐可能迫使译者写出不自然的正文;评审必须保护含义,并可同时修订两侧,而不是削弱其中一侧。 +- 拆分生成参考可能增加路由与链接维护;原型必须保留别名并衡量取舍。 +- 语义检查可能膨胀成阻塞正当变更的仓库拓扑扫描器;检查应覆盖高风险复制值和代表性路径,而正文含义仍由评审负责。 +- 包 README 的快速参考表会手工重复部分配置默认值;在源驱动检查接管之前,评审者必须对照源码和生成配置目录验证变更值,并让这些表保持精选而非穷尽。 +- 为缩短 agent 上下文而优化可能使人类参考变得碎片化;每次拆分都需要一个稳定概念归属者和一条明确导航路径。 +- 永久的 Dev Note 可能变成第二份队列或陈旧历史堆积;完成工作时必须提升持久事实并删除已解决的过程内容。 +- 本次审计使用仓库结构、门禁和代表性页面,而不是用户研究。在广泛推广之前,维护者应通过真实的新人、用户、开发者和 agent 任务验证提议的读者结果。 diff --git a/.agents/skills/.gitignore b/.agents/skills/.gitignore new file mode 100644 index 0000000000..8912eb5aa2 --- /dev/null +++ b/.agents/skills/.gitignore @@ -0,0 +1 @@ +*/agents/openai.yaml diff --git a/.agents/skills/dsh-doc-standards/SKILL.md b/.agents/skills/dsh-doc-standards/SKILL.md deleted file mode 100644 index c69b032d42..0000000000 --- a/.agents/skills/dsh-doc-standards/SKILL.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -name: dsh-doc-standards -description: 'Use when writing, moving, reviewing, or auditing documentation in the deepseek-harness repo — choosing hierarchy and detail, separating tutorials from references, checking tutorial progression, trimming doc slop, responding to a verify-doc-budgets failure, or requests like "improve the docs", "audit the docs", "where should this be documented", or "this doc is too long".' ---- - -# Applying the DeepSeek Harness Documentation Standard - -The documentation rules live in [docs/AGENTS.md](../../../docs/AGENTS.md). This workflow covers placement, corpus audits, budgets, and validation across Markdown, JSDoc, and code comments. It is guidance, not a script; use [dsh-prose-standard](../dsh-prose-standard/SKILL.md) for required coverage and editorial judgment, and never treat length alone as a defect. - -## Sources of truth (read, don't re-summarize) - -- [docs/AGENTS.md](../../../docs/AGENTS.md) — hierarchy, tutorial/reference forms, taxonomy, budgets, and slop checklist. -- [.agents/notes/README.md](../../notes/README.md) — when a decision earns an Agent Note, how to file it, and what goes inside one (the header block, per-lifecycle skeleton, and Alternatives-considered mandate, gated by `verify-agent-note-format`); [docs/postmortem/README.md](../../../docs/postmortem/README.md) — when an incident earns a postmortem. -- [docs/i18n/README.md](../../../docs/i18n/README.md) — the bilingual pairing rules; editing either side of a pair obligates the counterpart in the same change. -- Root [AGENTS.md](../../../AGENTS.md) — the standing orders whose budget discipline this skill protects. -- [Archived Agent Notes](../../notes/archived/AGENTS.md) — frozen historical snapshots excluded from editorial maintenance and evolving documentation gates. - -## Review structure before prose - -Apply the standard's authoring order to every human-facing document in scope. Do not apply this structural pass to Agent Notes. Classify a postmortem as a reference scoped to one incident; preserve its required chronological evidence without treating chronology as a teaching sequence. - -1. Locate the document in the repository and navigation trees. State its own subject and identify its direct children. -2. Set the permitted level of detail. Keep full detail about the document's subject, summarize direct children by purpose, responsibility, and high-level behavior, and move deeper explanations to their owning descendants with links. Treat test infrastructure as descendant-owned unless it is the document's subject. -3. Classify the document from its intended use, not its path or title. A tutorial must lead through ordered work to an observable outcome; a reference must support lookup within an explicit scope without requiring sequential reading. -4. For a tutorial, privately classify the starting reader and concepts as beginner, intermediate, or advanced. Trace each concept to its prerequisites, reorder premature material, and move optional advanced detail to a later tutorial or reference. -5. Split substantial mixed forms. Put a small secondary form in a clearly labeled section. - -Then check constraints that make placement expensive or wrong: - -- Paired docs (`pnpm run verify-translation-pairing --list`) cost a zh counterpart update and a `--write` re-record on every edit — prefer an unpaired home for content that will churn. Verbatim code blocks are byte-exact across the pair: copy a corrected fence into both files instead of translating its comments independently. -- Generated catalogs are never hand-edited; if the fact belongs there, change the generator's source. -- Before renaming or moving any doc, grep for inbound references: `verify-md-links` catches Markdown link targets AND `#fragment` anchors onto Markdown files (heading slugs and explicit ``), and `verify-doc-refs` catches `docs/*.md` citations in TypeScript comments; anchors cited from TypeScript strings still need a manual grep when their output never reaches gate-scanned Markdown. -- A move is atomic: remove from the old home, add to the new home, and fix every inbound link in the same change. - -## Audit the corpus - -After the structural pass, hunt the standard's slop checklist with the cheapest probes first. Verify and fetch the PR's live base, then run `pnpm --silent run change-scope --base ` to identify committed and dirty paths before applying semantic judgment. After a retarget or base merge, rerun the report and audit prose introduced by the new base. - -1. Measure: `pnpm run verify-doc-budgets --list`, then `git ls-files '*.md' ':(exclude)vendor/**' | xargs wc -w | sort -rn | head -30` to spot unbudgeted outliers. -2. Hunt reasoning-transcript leakage — narrated history, dead design-session citations, review choreography, control-flow narration, test walkthroughs — with [dsh-trim-cot-leakage](../dsh-trim-cot-leakage/SKILL.md), which defines the taxonomy, recall batteries, and rules for what to keep or delete. Preserve only a non-obvious contract or durable rationale; the same rationale repeated beside sibling methods keeps one home. -3. Hunt duplication by grepping distinctive phrases. Keep one home and replace other copies with links. -4. Replace hand-written catalogs, test/status inventories, and JSDoc restatements with the authoritative tree, script, or generated reference. -5. In `implemented/` Agent Notes, remove migration plans, acceptance-task checklists, and future-tense spec language. Keep concise verification contracts that identify the behaviors and tiers pinning the shipped decision, plus named coverage gaps. -6. If removing prose changes a promised behavior rather than its explanation, use a proposed Agent Note first (follow [dsh-find-simplifications](../dsh-find-simplifications/SKILL.md)). - -Exclude `.agents/notes/archived/` from corpus audits and edits. Active prose may repair, redirect, or delete an inbound link, but never follow an archive-wide cleanup into the frozen target. - -Keep every load-bearing rule, preferably as one to three lines plus a link to its rationale. Cut stories, duplicates, status notes, and the path used to derive the rule. Do not create a new explanation merely to relocate disposable reasoning. - -## When verify-doc-budgets goes red - -Apply the ordered relocate-condense-raise policy in [docs/AGENTS.md](../../../docs/AGENTS.md); this skill only supplies the workflow probes above. - -## Validation and PR hygiene - -Run at least `pnpm run doc-sync`, `pnpm run lint`, and `git diff --check`; JSDoc changes may regenerate catalogs. If a paired doc changed, follow the [lightweight routine path](../../../docs/AGENTS.md#writing-rules) and run `pnpm run verify-translation-pairing --write `. The PR body should give word deltas, explain any deliberately long exception, and list checks. diff --git a/.agents/skills/dsh-doc/SKILL.md b/.agents/skills/dsh-doc/SKILL.md new file mode 100644 index 0000000000..271c885247 --- /dev/null +++ b/.agents/skills/dsh-doc/SKILL.md @@ -0,0 +1,128 @@ +--- +name: dsh-doc +description: Create, restructure, review, audit, or migrate DeepSeek Harness Markdown documentation, package READMEs, and the documentation website using audience-first hierarchy, kind-mapped YAML metadata, bilingual line alignment, summary/contents navigation, progressive user-to-developer detail, executed-operation fact-checking, and repository validation. Use for new or revised DSH docs, docs-tree organization, documentation-quality audits and budgets, website page publishing, and bilingual documentation structure changes. +--- + +# DeepSeek Harness documentation + +## Summary + +The DeepSeek Harness documentation standard: make every page searchable, newcomer-readable, and exact enough for agents and maintainers, and keep the documentation website a tested projection of repository Markdown. Apply repository `AGENTS.md` files and executed gates first, then this workflow for kind-mapped metadata, progressive detail, line-aligned bilingual pages, corpus audits, and website publication. Preserve one owner per fact: source, tests, generated catalogs, package READMEs, guides, Agent Notes, and scratch each keep their own kind of truth. The `session-persistence-sqlite` README pair is the reference example of the format. + +## Table of Contents + +- [Workflow](#workflow) +- [Fact-check procedure: test, do not assume](#fact-check-procedure-test-do-not-assume) +- [Kind system and templates](#kind-system-and-templates) +- [Voice rules](#voice-rules) +- [Quality criteria](#quality-criteria) +- [Audit the corpus](#audit-the-corpus) +- [Wordcount budgets](#wordcount-budgets) +- [Website publication](#website-publication) +- [Detailed references](#detailed-references) +- [Validation](#validation) +- [Dev Note](#dev-note) + +## Workflow + +Follow this sequence for each requested scope. Keep the common reader path brief, but do not delete failures, ownership, limitations, or other required contracts merely to reduce words. + +1. Read root and more-specific `AGENTS.md`, [the documentation standard](../../../docs/AGENTS.md), the target page, its source/tests, navigation owner, and bilingual record. +2. Classify the page by one primary job and reader: product quick start, user task guide, contributor tutorial, architecture overview, package/subsystem reference, generated reference, agent instruction, decision record, or scratch. +3. Place the page at its nearest owner. Keep package contracts beside package code; use `docs/` for cross-package learning, user, developer, architecture, discussion, and expiring scratch material. +4. Define the reader's starting state, observable outcome, likely failure, recovery path, and next useful depth before writing details. +5. Add or revise YAML metadata — assign the `kind` that maps to the template for this document's job — then write `Summary`, `Table of Contents`, user-facing content, developer-facing content, optional `Further Exploration`, and final `Dev Note` in that order where the document type permits. +6. Update the bilingual counterpart in the same pass. Keep headings, lists, tables, code, links, frontmatter layout, and physical line count aligned. +7. Verify every claim against code, tests, generators, package metadata, or a current decision owner — and run the operations the page instructs, per the fact-check procedure below. Update the owner before any derivative artifact. +8. Run focused checks, then `pnpm run test:docs`, `pnpm run doc-sync`, `pnpm run lint`, and `git diff --check`; re-read the complete diff for correctness and then for brevity and repository fit. + +## Fact-check procedure: test, do not assume + +Documentation states how the product behaves today, and the only admissible evidence for an operation claim is having run it. This procedure is mandatory for every new document and every new paragraph that claims an operation, command, default, error, or platform difference. + +1. **Classify the subject before writing install guidance.** Read the facts, never the folder name: `package.json` for a `dsh.bundle.patch` declaration, and the entry file for the plugin shape (`apply` export or a default service export is a plugin; a plain module API is a library). A bundle installs with `dsh plugin --profile add ` and is the only package shape for which that command activates a profile layer; a plugin mounts as a `cordis.yml` row; a library is a dependency with no install path of its own. Packages with special status (libraries, bundles) get their own README template — never a plugin README with install guidance that does not apply. +2. **Run every claimed operation against the current checkout.** Execute each CLI command, config snippet, and profile or patch example exactly as the document will show it; write down only what you observed, including the exact output, warnings, and failure modes. If a claim depends on a key or a network you do not have, say so and name the verification owner instead of asserting the behavior. +3. **Delete what you could not reproduce.** Never carry a command, field, default value, or behavior from memory, analogy, or a neighboring package's README. When a claim fails to reproduce, fix the claim — not the test. +4. **Check old docs against latest master.** Before revising pre-existing pages, `git fetch origin` and compare the section against `origin/master`; the pairing sidecar recovers the last-confirmed text of either side. A stale statement on master is still wrong: correct it against the code, not against the old prose. +5. **Re-record the pair after every edit.** Each paired edit re-runs `pnpm run verify-translation-pairing --write ` so the sidecar tracks the confirmed pair. + +## Kind system and templates + +The `kind` frontmatter field selects exactly one README template. Every kind in [the metadata reference](references/metadata-links-i18n.md#the-kind-system) maps to one template file in [`templates/`](templates/), and every template backs exactly one kind; the documentation check derives the expected kind from the same mechanical facts. + +- `package-group` → [templates/package-group.md](templates/package-group.md): group maps (`packages/README.md`, `packages//README.md`) — orient the family, map its direct packages, link package-owned details. +- `package-reference` → [templates/package-reference.md](templates/package-reference.md): a Cordis plugin or service package — mount configuration, the config table, folded implementation, Model Experience and Known Limitations in the gate-owned forms. +- `package-library` → [templates/package-library.md](templates/package-library.md): a package with no plugin surface — consumer entry points, no profile-install path, no mount configuration. +- `package-bundle` → [templates/package-bundle.md](templates/package-bundle.md): a package declaring `dsh.bundle.patch` — the verified `dsh plugin` install path, layer semantics, patch document. + +Open the template before writing and follow its skeleton and rules; it states what the kind is, how the page is structured, and the fact checks each section owes. Add a new kind only together with a distinct template file, a documented repository position or declared owner, and a focused check that maps documents to it. + +## Voice rules + +These rules decide what a section may say. They apply to every authored human-facing page, and to package READMEs with particular force. + +- **Summary says what the subject does.** The opening `Summary` and the user-facing sections describe what a user or agent can DO with the subject — outcomes, benefits, when to choose it, main cost — never its role, type, or internal identity. "The seam registers `ctx.x` and appends `x/event` records" is identity narration; "you can save a note per message and it survives restarts" is what it does. +- **Developer sections explain, never enumerate.** Folded implementation content covers the overall design concept, architecture, and hand-waving dataflow — enough to understand how the package works — and links code for exact detail. No full API catalogs, exhaustive column lists, event-payload enumerations, or JSDoc restatement inside the folds. +- **Dev Note is the only slop zone.** Partial ideas, scratches, undecided directions, measured artifacts, and working hypotheses live only in the final Dev Note, marked explicitly non-authoritative. Every other section is polished, current-state prose. +- **Current state only.** No compatibility shims, migration talk, or history ("previously", "now", "no longer", renamed) outside the Dev Note; the codebase as it is today is the only subject. +- **Use controlled technical English.** Give each sentence an explicit actor and one main action when ambiguity can change behavior. Reuse one term per concept, prefer direct verbs, split stacked instructions and conditions, and preserve modality and exceptions. Apply the non-certified, ASD-STE100-inspired discipline in [the page-style reference](references/style.md#controlled-technical-english). Do not force a shorter sentence when precision would fall. + +## Quality criteria + +Use these definitions in review. Each section opens with a short orienting paragraph before subsections or exhaustive detail. + +- **Brief:** the common path contains only facts needed for its outcome; exhaustive truth remains one direct link or detail layer away. +- **Intuitive:** prerequisites precede dependent concepts, one next action is obvious, and headings use terms readers search for. +- **Friendly:** readers can recognize success, understand risk before acting, recover from likely failure, and choose whether to continue deeper. +- **Accurate:** each durable claim has one owner and a verification path proportionate to its risk. +- **Agent-readable:** metadata, stable headings, anchors, terminology, ownership, and current/proposed status support targeted retrieval without loading the corpus. +- **Newcomer-complete:** a professional engineer with no repository context can reconstruct the relevant architecture or feature through three to five linked pages. + +Do not apply a universal word limit to exhaustive references. Measure entry-path length, unrelated material scanned for one lookup, largest section, heading count, and page size; split by an existing domain owner when retrieval cost is high. + +## Audit the corpus + +Read, do not re-summarize, the owning contracts: [docs/AGENTS.md](../../../docs/AGENTS.md) for hierarchy, tutorial/reference forms, taxonomy, budgets, and the slop checklist; [.agents/notes/README.md](../../notes/README.md) for Agent Note lifecycle; [docs/i18n/README.md](../../../docs/i18n/README.md) for the bilingual pairing rules; and [root AGENTS.md](../../../AGENTS.md) for standing orders. Exclude `.agents/notes/archived/` from audits and edits — archived notes are frozen history. + +Apply the standard's authoring order to every human-facing document in scope (not to Agent Notes): locate the document and state its own subject; set the permitted detail level and move deeper explanations to owning descendants with links; classify tutorial or reference from intended use, not path; for a tutorial, order concepts by prerequisite and difficulty; split substantial mixed forms. Then check placement constraints: paired docs cost a counterpart update and a `--write` re-record on every edit; generated catalogs are never hand-edited; a move is atomic with every inbound link repaired in the same change. + +After the structural pass, hunt the slop checklist with the cheapest probes first. Use [dsh-trim-cot-leakage](../dsh-trim-cot-leakage/SKILL.md) for reasoning-transcript leakage, grep distinctive phrases to find duplicated rules, replace hand-written catalogs and status inventories with their authoritative owners, and remove migration plans and future-tense spec language from implemented Agent Notes. Measure outliers with `pnpm run verify-doc-budgets --list` and a word-count scan; if removing prose changes a promised behavior rather than its explanation, propose the behavior change first (follow [dsh-find-simplifications](../dsh-find-simplifications/SKILL.md)). Keep every load-bearing rule, preferably as one to three lines plus a link to its rationale; do not create a new explanation merely to relocate disposable reasoning. + +## Wordcount budgets + +`pnpm run verify-doc-budgets` compares standing documents against ceilings in [scripts/doc-budgets.manifest.json](../../../scripts/doc-budgets.manifest.json); a red gate follows the ordered relocate-condense-raise policy in [docs/AGENTS.md](../../../docs/AGENTS.md#wordcount-budgets). Ceilings are guardrails, not reduction targets: at or below target, retain at least 5% headroom; raise a ceiling only when the words need the space, and justify the manifest diff in the PR. + +## Website publication + +The website is a tested projection, never a second copy: [website/docs.ts](../../../website/docs.ts) is the explicit public allowlist mapping canonical `docs/` sources into route trees, [scripts/project-doc-site.ts](../../../scripts/project-doc-site.ts) rewrites them into the disposable `website/.generated/` tree, and VitePress builds that tree. Repository Markdown stays the only editable content source; translations stay sibling pairs (`foo.md`, `foo.zh.md`, `foo.i18n.yaml`), never locale directories. Edit an already published page in its canonical source only; add one manifest entry for a new page; update source, manifest entry, and inbound links atomically for a move or removal; never edit `website/.generated/`, `website/.cache/`, or `website/.dist/`. Set every `DocsPage` field deliberately and honor the projector's link rules; see [references/website-sync.md](references/website-sync.md) for the fields, sidebar collections, and preview commands. Synchronizing content into the build does not publish it: deployment stays a separate, explicitly requested step. + +## Detailed references + +Load only the reference needed for the task. Each reference links directly from this file so the skill has no deep reference chain. + +- [Metadata, links, and bilingual pairs](references/metadata-links-i18n.md): README frontmatter, the kind system and its derivation, description semantics, repository paths, line alignment, and the sidecar record. +- [Page structure and hierarchy](references/structure-hierarchy.md): mandatory section order, section summaries, user-to-developer progression, docs tree placement, small rule files, Further Exploration, and Dev Note ownership. +- [Page style](references/style.md): short Summary, `-----` section separators, foldable content sections, and emphasis discipline. +- [Review criteria](references/review.md): newcomer test, evidence checks, package README review, the reference example, and verification commands. +- [Website publication](references/website-sync.md): manifest fields, projector link rules, preview and validation, and deployment separation. + +The four README templates in [`templates/`](templates/) are the working skeletons for the four `kind` labels; open the one your document's kind names before writing. + +Use [dsh-prose-standard](../dsh-prose-standard/SKILL.md) for sentence-level contract coverage and editorial judgment. The `session-persistence-sqlite` README pair ([English](../../../packages/session/session-persistence-sqlite/README.md), [Chinese](../../../packages/session/session-persistence-sqlite/README.zh.md)) is the reference example: searchable YAML, Summary and Table of Contents, user-to-developer progression with a folded developer section, Further Exploration, canonical Model Experience and Known Limitations sections, and a final Dev Note. + +## Validation + +Validate the affected format, not merely Markdown syntax. A strong promise needs a focused valid fixture and an invalid fixture that proves the top-level gate can fail. + +- README metadata: parse YAML, map `kind` to its template and document standard, reject `name`, `audience`, ungoverned `tags`, and README-local `i18n` metadata, and reject missing or advertisement-style descriptions. +- Bilingual pages: verify structure, exact line count, terminology, link parity, and the sidecar record. +- Tutorials: exercise the documented entry path or name an explicit manual verification owner. +- Generated references: run the deterministic freshness check and report retrieval-size measures. +- Package READMEs: run model-experience and limitation checks, then package-focused tests when behavior claims changed; re-run every command the README instructs before merging a claim about it. +- Skills: run the repository's skill-invocation metadata check. + +Run `pnpm run test:docs` for the quick comprehensive documentation checks (pairing, wrap, links, README gates, budgets, skill metadata, Agent Note gates) before the full `pnpm run doc-sync`. + +## Dev Note + +None. diff --git a/.agents/skills/dsh-doc/references/metadata-links-i18n.md b/.agents/skills/dsh-doc/references/metadata-links-i18n.md new file mode 100644 index 0000000000..95c9e21d13 --- /dev/null +++ b/.agents/skills/dsh-doc/references/metadata-links-i18n.md @@ -0,0 +1,74 @@ +# Metadata, links, and bilingual pairs + +## Summary + +README metadata is a retrieval and template-selection interface, not a miniature report or advertisement. The `kind` field selects exactly one README template that exists in this skill and maps to the document standard; the frontmatter carries no field that a filename convention or an executed gate already owns. Bilingual pages keep equal authority, one-to-one structure, and exact physical line alignment. The `*.i18n.yaml` sidecar records the last-confirmed pair and supports automatic merges. Link syntax must render correctly on GitHub and the documentation site, so repository links stay renderer-valid relative URLs. + +## Table of Contents + +- [README metadata](#readme-metadata) +- [The kind system](#the-kind-system) +- [Description quality](#description-quality) +- [Repository links and path mentions](#repository-links-and-path-mentions) +- [Bilingual line alignment](#bilingual-line-alignment) +- [Bilingual consistency records](#bilingual-consistency-records) +- [Dev Note](#dev-note) + +## README metadata + +Start every authored README with YAML frontmatter. Permit custom fields, but keep common fields stable enough for search and indexing. + +```yaml +--- +description: "Example capability for users and maintainers choosing, configuring, or debugging the package." +kind: "package-reference" +--- +``` + +`description` and `kind` are required for package README pairs. The page title and package manifest already own the name, while the document job and its reader path express the audience; duplicating either in frontmatter adds no retrieval value. The counterpart path comes from the sibling filename (`README.zh.md`), and the sidecar owns pair state, so README-local `i18n` metadata is redundant. Do not add `tags` until a repository-owned taxonomy and search consumer justify them beyond description and full-text search. Keep keys lowercase and hyphenated unless an existing owner defines another spelling, and do not copy volatile code inventories into frontmatter. + +## The kind system + +`kind` selects the document template directly; every kind maps to exactly one template that exists in this skill, and no template exists without a kind. Derive the kind mechanically, in this order: + +1. The README is `packages/README.md` or `packages//README.md` → `package-group`. +2. The package manifest declares `dsh.bundle.patch` → `package-bundle`. +3. The package is in the audited library registry of `scripts/doc-standard.spec.ts` → `package-library`. +4. Everything else — a service default export or an `apply` plugin — is `package-reference`. + +| `kind` | Repository position | Template | Standard | +|---|---|---|---| +| `package-group` | `packages/README.md`, `packages//README.md` | [package-group.md](../templates/package-group.md) | Group map: orient the capability family, map its direct packages, explain composition relationships, and link package-owned details. | +| `package-reference` | `packages///README.md` with a plugin entry | [package-reference.md](../templates/package-reference.md) | Package contract: follow the [package README review standard](review.md#package-readme-review) and the canonical [package documentation requirements](../../../../docs/cookbook/adding-a-package.md#4-write-the-package-readme). | +| `package-library` | `packages///README.md` with a plain module entry | [package-library.md](../templates/package-library.md) | Library contract: consumer entry points and boundaries; no profile-install path and no mount configuration. | +| `package-bundle` | `packages///README.md` declaring `dsh.bundle.patch` | [package-bundle.md](../templates/package-bundle.md) | Installable layer: the verified `dsh plugin` install path, layer semantics, and patch document. | + +Before assigning `package-library` or `package-bundle`, inspect the facts: read `package.json` for `dsh.bundle.patch` and `src/index.ts` for the entry shape (`apply` export or a default service export is a plugin; a plain module API is a library). `dsh plugin --profile add ` installs any npm dependency, but the profile reconcile activates a layer only for a package that declares `dsh.bundle`; never present that command as an install path for a library or a plain plugin. The documentation check derives the expected kind from these same facts, rejects another value, and rejects `name`, `audience`, `tags`, and README-local `i18n` metadata. Add a new kind only with a distinct template, an unambiguous repository position or declared owner, and a focused check that maps documents to it. + +## Description quality + +Agents search frontmatter `description` values to shortlist pages before loading full documents. Write each value like a Skill description: state what the page covers and when a reader should open it. Use one or two concrete sentences, include searchable domain terms, and distinguish the page from nearby owners. Do not summarize every section, claim superiority, repeat the title, advertise vaguely, preserve change history, or write a technical status report. + +Good: `SQLite session persistence for deployments and maintainers choosing, configuring, or debugging the opt-in packed-row backend.` + +Weak: `The best and most advanced SQLite storage implementation with lots of optimizations.` + +## Repository links and path mentions + +Keep link destinations machine-checkable and mentions context-relative. Use fragment-only links for the current page's menu. Use full URLs for external resources. + +The desired internal-link model names a target from the repository root, but a leading `/docs/...` Markdown URL resolves outside the repository on GitHub, remains untouched by the website projector, and is skipped by `verify-md-links`. Until a repository-owned resolver supports root paths in every renderer, use the current renderer-valid relative URL in Markdown links and write logical path mentions such as `docs/` or `packages/session/` relative to the discussion. Never adopt an unchecked leading-slash link merely to resemble an absolute path. + +## Bilingual line alignment + +Keep English and Simplified Chinese equally authoritative. Match frontmatter key order, headings, blank lines, paragraphs, list items, tables, code fences, link targets, and total physical line count one to one. The English side points every relative link at the `.md` target; the Chinese side points it at the `.zh.md` sibling when that counterpart exists and falls back to the `.md` target otherwise — the pairing gate compares `.md` and `.zh.md` targets as the same document. Translate prose naturally within its corresponding line; do not hard-wrap either language. Keep code blocks byte-identical and reposition first-use terminology annotations without changing line structure. + +Line equality is a structural check, not proof of faithful meaning. Review still owns completeness, terminology, natural language, and whether each line expresses the same proposition. + +## Bilingual consistency records + +Keep the `*.i18n.yaml` sidecar for every bilingual pair. `verify-translation-pairing` consumes its Git blob hashes for last-confirmed-text recovery, verifies structure and exact line alignment, supports automatic merging, records generated regions, and seals archives. Re-record it with `pnpm run verify-translation-pairing --write ` after either language changes. Do not copy content hashes into README frontmatter: independent edits would change the same header line and turn otherwise mergeable prose into an owner-file conflict. + +## Dev Note + +None. diff --git a/.agents/skills/dsh-doc/references/review.md b/.agents/skills/dsh-doc/references/review.md new file mode 100644 index 0000000000..6e67637812 --- /dev/null +++ b/.agents/skills/dsh-doc/references/review.md @@ -0,0 +1,67 @@ +# Review criteria + +## Summary + +Review documentation by whether a reader completes an outcome, not by whether every template heading exists. Verify prose against code and tests, preserve exact contracts, and keep package READMEs useful to consumers while exposing enough implementation detail for maintainers. Run current repository gates; the `session-persistence-sqlite` README pair is the reference example of the format. + +## Table of Contents + +- [Newcomer test](#newcomer-test) +- [Evidence review](#evidence-review) +- [Package README review](#package-readme-review) +- [Reference example](#reference-example) +- [Verification](#verification) +- [Dev Note](#dev-note) + +## Newcomer test + +A professional engineer with no repository context should answer the following after three to five linked pages: what the product or feature does, how to run or use it safely, where its state lives, which component owns it, how it fails, and where to change it. If the reader must inspect source merely to discover the public flow, restore the missing explanation. If the reader must absorb unrelated internals, move those details deeper. + +## Evidence review + +Check each material statement against its strongest owner. Use package metadata for names and entry points, public types and JSDoc for API contracts, runtime code for behavior, tests for exercised failure paths, generated catalogs for exhaustive inventories, and active Agent Notes for rationale. Never treat a prior README, discussion, or report as stronger than current code and tests. + +For every operational claim — a CLI command, a config snippet, a default value, an error message, a platform difference — the evidence is running it, not reading it. Execute the exact command or mount the exact configuration against the current checkout before the page may state its behavior; quote only observed output, warnings, and failures. Claims that depend on unavailable keys or networks name their verification owner instead of asserting behavior. For pre-existing pages, compare against latest `origin/master` and re-verify stale statements against code. + +Classify the package before reviewing its install guidance: `dsh.bundle.patch` in `package.json` makes it a bundle (installable via `dsh plugin --profile add `, the only shape that command activates as a layer); an `apply` export or default service export makes it a plugin (mounted as a `cordis.yml` row); a plain module API makes it a library (a dependency with no install path). Reject install guidance written for another shape. + +Retain a statement only when it helps the target reader act, reason, or avoid misuse. Move rationale, history, test walkthroughs, duplicate catalogs, and unrelated package detail to their owners. + +## Package README review + +Require the following without forcing one universal internal heading set: + +- searchable YAML metadata with a precise `description` and the mechanically derived `kind` (`package-group`, `package-reference`, `package-library`, or `package-bundle`); +- a three-to-five-sentence Summary that says what the subject DOES for its user or agent reader, with a linked Table of Contents; +- controlled English with explicit actors, stable terms, direct verbs, separated instructions and conditions, and unchanged modality; +- when to choose or avoid the package; +- a smallest safe configuration or usage path when one exists — for a bundle, the verified `dsh plugin` install path; for a library, the consumer entry point; never profile-install guidance for a shape that does not take it; +- observable behavior, failures, durability, security, and performance limits relevant to consumers; +- developer-facing ownership and data/lifecycle design at concept level — overall design, architecture, hand-waving dataflow — that cannot be recovered cheaply from public types, with code links for exact detail; +- canonical Model Experience and Known Limitations sections required by package policy; +- newcomer-facing Further Exploration where adjacent docs materially help; +- a final non-authoritative Dev Note as the only home for partial ideas, scratches, and undecided directions. + +Do not restate JSDoc or generated catalogs. Link the owner and explain only the decision or relationship needed locally. Reject any user-facing section that narrates internals (function subjects, event streams, data flow) and any fold that enumerates APIs instead of explaining the concept. + +## Reference example + +The `session-persistence-sqlite` README pair ([English](../../../../packages/session/session-persistence-sqlite/README.md), [Chinese](../../../../packages/session/session-persistence-sqlite/README.zh.md)) demonstrates the format in production: searchable YAML whose `kind` selects this package-reference standard, a five-sentence Summary, a linked Table of Contents, a user-facing use section (choice, sizing, configuration, migration, safe operation) separated by horizontal rules and followed by a GitHub-native `
` fold under the developer section title (design philosophy, source map, schema tables, write path, read and recovery), Further Exploration, canonical Model Experience and Known Limitations sections, and a final folded Dev Note holding non-authoritative working context such as the annotated benchmark artifact and undecided future directions. Use its structure, evidence standards, and bilingual alignment as the model for package READMEs and cross-package pages; ground every claim the way it grounds the benchmark numbers in the Agent Note. + +## Verification + +Run the smallest focused checks while iterating, then the standing documentation checks: + +```sh +pnpm run test:docs +pnpm run verify-translation-pairing --write +pnpm run doc-sync +pnpm run lint +git diff --check +``` + +Also run the repository's skill-invocation metadata check for skill changes and compare English/Chinese physical line counts for a line-aligned pair. Re-read the final diff once for factual completeness and once for brevity, navigation, and ownership. + +## Dev Note + +None. diff --git a/.agents/skills/dsh-doc/references/structure-hierarchy.md b/.agents/skills/dsh-doc/references/structure-hierarchy.md new file mode 100644 index 0000000000..0f4935f9fa --- /dev/null +++ b/.agents/skills/dsh-doc/references/structure-hierarchy.md @@ -0,0 +1,87 @@ +# Page structure and hierarchy + +## Summary + +Each page gives a newcomer a short front door before it exposes operational or implementation depth. Cross-package learning and engineering material lives under a deliberate `docs/` hierarchy, while package contracts stay beside code. Small rule files own one independently searchable requirement, but arbitrary fragmentation is not a goal. The final Dev Note isolates active working context from the stable explanation above it. + +## Table of Contents + +- [Page order](#page-order) +- [Section progression](#section-progression) +- [Documentation hierarchy](#documentation-hierarchy) +- [Small rule files](#small-rule-files) +- [Further Exploration](#further-exploration) +- [Dev Note ownership](#dev-note-ownership) +- [Dev Note](#dev-note) + +## Page order + +Use this order for authored human-facing pages when the format owner permits it. Generated artifacts may generate the same entry sections, while Agent Notes and postmortems retain their repository-defined skeletons. + +1. YAML metadata. +2. H1 title. +3. Language switcher for a bilingual page. +4. `## Summary`: three to five explanatory sentences stating what the subject is, why a reader would care, the main operating model, and the most important boundary. +5. `## Table of Contents`: links to the page's H2 sections; keep it navigational rather than descriptive. +6. Stable content, ordered from user-facing use to developer-facing design and operational detail. +7. Optional `## Further Exploration` for newcomer-oriented links to adjacent subjects. +8. Final `## Dev Note` for non-authoritative active working context. + +Do not force a Summary/Table of Contents wrapper around tiny machine-owned files, generated fragments, or formats whose executed parser defines another header. State the exception in the format owner rather than creating invalid output. The package README gate requires `Model Experience` and `Known Limitations and Deferred Work` as the final two H2 sections: place `Further Exploration` before them and end with a final `### Dev Note` inside the limitations H2. + +## Section progression + +Open every substantive H2 with a short orienting paragraph before tables, code, or H3 subsections. Explain the section's subject and decision-relevant point; do not repeat its complete contents. + +Within a page, order content by reader depth: + +1. Basic use: when to choose the feature, required inputs, shortest safe example, observable success, and likely recovery. +2. Advanced use: configuration choices, limits, operations, and integration behavior. +3. Developer detail: ownership, lifecycle, data model, failures, performance, security, and extension points worth maintaining. + +Fold heavy developer detail and the final Dev Note behind `
` blocks with the section titles visible (mechanics in [style.md](style.md)). + +Folded developer detail is concept-level by requirement: the overall design concept, the architecture of the main components, and hand-waving dataflow — enough to understand how the package works — plus source-map tables and links to code for exact detail. It never becomes an exhaustive catalog: no full API inventories, column lists, event-payload enumerations, or JSDoc restatement. The Dev Note is the only place allowed to hold partial ideas, scratches, and undecided directions; everything else, folds included, is polished current-state prose. + +Keep exhaustive generated types, schemas, or catalogs behind a compact entry paragraph and stable index. Split them by an existing domain owner when one lookup requires scanning unrelated groups. + +## Documentation hierarchy + +Use package-local READMEs for package contracts and keep them next to source. Organize cross-package Markdown under audience and learning intent instead of leaving unrelated pages flat at `docs/`. + +```text +docs/ + learn/ + overview/ + cordis/ + practices/ + user/ + developer/ + discussion/ + scratch/ + subsystems/ +``` + +Treat this as a target map, not permission for an opportunistic mass move. Move one coherent topic at a time, repair every inbound link and website mapping atomically, preserve public routes or aliases, and keep `subsystems/` flat because its pages are logically parallel. + +`docs/scratch/` contains tracked, expiring discussion that must survive a handoff. Each scratch page names its owner, creation date, expiry, and promotion target. Local disposable notes remain ignored and uncommitted. + +## Small rule files + +Give an independently searchable rule, practice, example family, or decision one small file when it has its own owner, change cadence, inbound links, or validation. Group related files under a descriptive hierarchy such as `docs/developer/code-quality/`. Keep tightly coupled rules together when splitting would force readers to open several files to understand one obligation. + +An index page explains the folder in three to five sentences and links its direct children by purpose. It does not restate each child's rule. + +## Further Exploration + +Use this optional section for a newcomer who finished the page and wants adjacent understanding. Link three to seven directly related pages, order them from closest prerequisite to deeper exploration, and say in a short phrase what each adds. Do not turn it into a complete site index. + +## Dev Note ownership + +End authored pages with Dev Note, but keep it explicitly non-authoritative. Active hypotheses, compatibility concerns, rough alternatives, progress pointers, and unresolved questions may live there; stable behavior, required limitations, and accepted rationale belong in their ordinary owners. + +Dev Note may mirror or link task progress but must not become a second writable queue. When work closes, promote durable conclusions, move reusable rationale to an Agent Note, keep incident chronology in a postmortem, and delete resolved chatter. Git history preserves old iterations. + +## Dev Note + +The mandatory final section is intentionally the least polished part of an authored page, but it still has lifecycle discipline. A blank Dev Note should say `None.` rather than accumulate placeholder prose; generated and parser-owned formats may omit it through a named exception. diff --git a/.agents/skills/dsh-doc/references/style.md b/.agents/skills/dsh-doc/references/style.md new file mode 100644 index 0000000000..df6ff18254 --- /dev/null +++ b/.agents/skills/dsh-doc/references/style.md @@ -0,0 +1,49 @@ +# Page style + +## Summary + +Page-level style preferences that make DSH pages scannable and difficult to misread: a short Summary, controlled technical English, `-----` separators between major parts, `
` folds that keep section titles visible, and disciplined emphasis. The template is the `session-persistence-sqlite` README pair. + +## Table of Contents + +- [Short summary](#short-summary) +- [Controlled technical English](#controlled-technical-english) +- [Section separators](#section-separators) +- [Foldable content sections](#foldable-content-sections) +- [Emphasis discipline](#emphasis-discipline) +- [Dev Note](#dev-note) + +## Short summary + +Open every authored page with a short `Summary`: three to five sentences in one paragraph stating what the subject is, why the reader cares, the operating model, and the most important boundary. The Table of Contents and the sections carry the detail; placement and section order live in [structure-hierarchy.md](structure-hierarchy.md). + +## Controlled technical English + +Use an [ASD-STE100](https://www.asd-ste100.org/)-inspired review pass for English prose that an agent, translator, or non-native reader must parse. This is a clarity discipline, not certified ASD-STE100 compliance. The repository does not reproduce or validate the standard's controlled dictionary. + +- Name the actor and action. Prefer active voice when the actor matters. +- Use one stable term for each concept. Do not rotate synonyms for variety. +- Prefer direct verbs. Replace nominalizations and ambiguous phrasal verbs when a precise verb exists. +- Put one instruction in each sentence. Use a list for three or more steps or conditions. +- Split semicolons and long clause chains. Keep each paragraph on one topic. +- Remove unsupported quality adjectives and stacked hedges. Preserve every fact and degree of uncertainty from the source. + +Treat 20 words for an instruction and 25 words for a description as review prompts, not mechanical gates. Keep a longer sentence when a split would hide a condition or relationship. Never remove or strengthen `must`, `may`, `never`, timing, exceptions, numbers, or other contract terms to meet a length target. The [prose standard](../../dsh-prose-standard/SKILL.md) owns the complete-proposition rule. + +## Section separators + +Separate the major parts of a page with a `-----` horizontal rule on its own line, with a blank line before and after it. A rule directly after a paragraph would parse as a Setext heading, and a rule inside the final two H2 sections of a package README would break the Model Experience gate. The template separates: front matter → use section → folded developer section → Further Exploration → Model Experience. + +## Foldable content sections + +Fold developer-facing detail and the final Dev Note behind GitHub-native `
`/`` blocks. Keep the section title (H2 or H3) and its `` anchor visible; fold only the content under the title. Inside the block, put a blank line after ``, keep every Markdown line at column 0 (indented content becomes a code block), and close with `
` after a blank line. Headings, lists, tables, and links inside the fold parse normally and keep their anchors. The `session-persistence-sqlite` README pair demonstrates both folds: the implementation section and the Dev Note. + +In a package README, keep `## Model Experience` and `## Known Limitations and Deferred Work` as the final two H2 headings. Put the limitations anchor immediately after its H2 so it does not become part of the preceding Model Experience body. Place the final Dev Note under the limitations section as an anchored H3. A package that is explicitly exempt from the limitations section can use an H2 Dev Note. + +## Emphasis discipline + +Reserve bold for the clause that changes behavior or for the comparison that matters. In benchmark tables, bold the column headers and the best value in each row, as in the reference example. + +## Dev Note + +None. diff --git a/.agents/skills/dsh-doc-site-sync/SKILL.md b/.agents/skills/dsh-doc/references/website-sync.md similarity index 57% rename from .agents/skills/dsh-doc-site-sync/SKILL.md rename to .agents/skills/dsh-doc/references/website-sync.md index e4a7dede73..0dc05e201f 100644 --- a/.agents/skills/dsh-doc-site-sync/SKILL.md +++ b/.agents/skills/dsh-doc/references/website-sync.md @@ -1,20 +1,24 @@ ---- -name: dsh-doc-site-sync -description: Use when publishing, updating, moving, or removing DeepSeek Harness documentation website pages; editing website/docs.ts mappings or navigation; diagnosing a page missing from the VitePress site; fixing projected documentation links; or running the docs:dev, docs:check, and doc-sync workflow after website-content changes. ---- +# Website publication -# Synchronizing the DeepSeek Harness Documentation Site +## Summary -Keep repository Markdown as the only editable content source. Treat the website as a tested projection: [website/docs.ts](../../../website/docs.ts) selects public pages, [scripts/project-doc-site.ts](../../../scripts/project-doc-site.ts) rewrites them into the disposable `website/.generated/` tree, and VitePress builds that tree. The build additionally emits a raw-Markdown twin of every route (page URL minus any trailing slash, plus `.md`; index routes also get a parent-level alias) and a root `llms.txt` index; both derive from the same manifest and projector, so publishing, moving, or removing a page updates them automatically and `docs:build` fails when one is missing. +The documentation website is a tested projection of repository Markdown, never a second copy. [website/docs.ts](../../../../website/docs.ts) is the explicit public allowlist, [scripts/project-doc-site.ts](../../../../scripts/project-doc-site.ts) rewrites mapped sources into the disposable `website/.generated/` tree, and VitePress builds that tree. The build also emits a raw-Markdown twin of every route and a root `llms.txt` index from the same manifest. This reference owns the manifest fields, the projector's link rules, preview and validation commands, and the deployment boundary. -Repository translations follow the sibling pairing contract: English `foo.md`, Chinese `foo.zh.md`, and `foo.i18n.yaml` live together. Never create `zh-CN/` or other locale directories for website content. The site route trees are independent of that source layout: `foo.zh.md` projects to the root route and `foo.md` projects to the matching `/en/` route. +## Table of Contents -## Read the owning contracts +- [Manifest ownership](#manifest-ownership) +- [Classify the change](#classify-the-change) +- [DocsPage fields](#docspage-fields) +- [Preserve link behavior](#preserve-link-behavior) +- [Preview and validate](#preview-and-validate) +- [Keep deployment separate](#keep-deployment-separate) +- [Dev Note](#dev-note) -- Read [docs/AGENTS.md](../../../docs/AGENTS.md) and use [dsh-doc-standards](../dsh-doc-standards/SKILL.md) when deciding where content belongs or changing product documentation prose. -- For an edited bilingual source, follow the lightweight routine path in [docs/AGENTS.md](../../../docs/AGENTS.md#writing-rules) and the [pairing contract](../../../docs/i18n/README.md); never invoke the extended translation skill automatically. -- Read the current `DocsPage` type and entries in [website/docs.ts](../../../website/docs.ts) before changing the manifest; do not rely on a remembered field set. -- Read [website/.vitepress/config.ts](../../../website/.vitepress/config.ts) before adding a new section, sidebar collection, locale, or top-level navigation item. +## Manifest ownership + +Read [docs/AGENTS.md](../../../../docs/AGENTS.md) and the current `DocsPage` type and entries in [website/docs.ts](../../../../website/docs.ts) before changing the manifest; do not rely on a remembered field set. Read [website/.vitepress/config.ts](../../../../website/.vitepress/config.ts) before adding a new section, sidebar collection, locale, or top-level navigation item. For an edited bilingual source, follow the lightweight routine path in [docs/AGENTS.md](../../../../docs/AGENTS.md#writing-rules) and the [pairing contract](../../../../docs/i18n/README.md); never invoke the extended translation skill automatically. + +Never edit or commit `website/.generated/`, `website/.cache/`, or `website/.dist/`. Except for `website/AGENTS.md`, never add Markdown under `website/`; locale and route directories such as `website/zh-CN/`, `website/en/`, and `website/api/` are invalid source layouts. Keep generated catalogs under `docs/`, freshness-gate them there, and publish them through the manifest. ## Classify the change @@ -24,21 +28,21 @@ Repository translations follow the sibling pairing contract: English `foo.md`, C - **Publish a generated catalog:** map the generated `docs/` file, but change its generator or source metadata rather than editing the catalog by hand. - **Change site structure:** update the manifest for ordinary pages; update VitePress configuration only when the existing sidebar, section, or locale model cannot express the change. -Never edit or commit `website/.generated/`, `website/.cache/`, or `website/.dist/`. Except for `website/AGENTS.md`, never add Markdown under `website/`; locale and route directories such as `website/zh-CN/`, `website/en/`, and `website/api/` are invalid source layouts. Keep generated catalogs under `docs/`, freshness-gate them there, and publish them through the manifest. +Keep the manifest an explicit public allowlist. Do not publish RFCs, postmortems, testing guides, `AGENTS.md`, or maintainer workflows merely because they exist under `docs/`; add internal material only when the user explicitly expands what the site publishes. -## Add or update a manifest entry +## DocsPage fields -Set every `DocsPage` field deliberately: +Set every `DocsPage` field deliberately. The canonical field set and the `DocsSidebar` union live in [website/docs.ts](../../../../website/docs.ts) — read them there rather than copying values into prose; sections are owned by the `sections` record in that file, with no separate order list in the VitePress config. - `source`: repository-relative canonical Markdown path. For a complete bilingual pair, add the English `.md` path through `pairedPages()`; it derives the sibling `.zh.md`, the content locales, and counterpart aliases. - `route`: public VitePress path including the `.md` suffix. - `label`: sidebar label, not necessarily the document H1. -- `sidebar`: reuse `zh-guide`, `zh-develop`, or `en-docs` unless the information architecture genuinely needs another collection. -- `section`: reuse an existing section when possible. If adding one, also place it in `sectionOrder` in the VitePress config. +- `sidebar`: reuse an existing `DocsSidebar` collection unless the information architecture genuinely needs another one. +- `section`: reuse an existing section when possible. If adding one, also define it in the `sections` record. - `order`: stable order within the section. - `sourceAliases`: optional additional repository paths that should resolve to this page when links are projected. It does not create another public route. -Use `mirroredPages()` only for a source that intentionally falls back to the same available language in both route trees. Convert that entry to `pairedPages()` when its counterpart is added. Keep the manifest an explicit public allowlist. Do not publish RFCs, postmortems, testing guides, `AGENTS.md`, or maintainer workflows merely because they exist under `docs/`; add internal material only when the user explicitly expands what the site publishes. +Use `mirroredPages()` only for a source that intentionally falls back to the same available language in both route trees. Convert that entry to `pairedPages()` when its counterpart is added. The site route trees are independent of the source layout: `foo.zh.md` projects to the root route and `foo.md` projects to the matching `/en/` route. ## Preserve link behavior @@ -74,13 +78,18 @@ If Markdown link checks pass but the site build reports a missing fragment, foll Before committing a documentation-site change, run: ```sh +pnpm run test:docs pnpm run doc-sync pnpm run lint git diff --check ``` -Use [dsh-pre-push-checks](../dsh-pre-push-checks/SKILL.md) before pushing. Report the canonical files changed, manifest entries added or removed, public routes affected, and the exact checks run. +Use [dsh-pre-push-checks](../../dsh-pre-push-checks/SKILL.md) before pushing. Report the canonical files changed, manifest entries added or removed, public routes affected, and the exact checks run. ## Keep deployment separate Synchronizing content into the VitePress build does not publish it to the internet. Do not add GitHub Pages permissions, deployment workflows, custom domains, or public hosting unless the user explicitly requests deployment and confirms the hosting policy. + +## Dev Note + +None. diff --git a/.agents/skills/dsh-doc/templates/package-bundle.md b/.agents/skills/dsh-doc/templates/package-bundle.md new file mode 100644 index 0000000000..634d6347b0 --- /dev/null +++ b/.agents/skills/dsh-doc/templates/package-bundle.md @@ -0,0 +1,103 @@ +# Template: package-bundle + +Use this template for a package whose manifest declares `dsh.bundle.patch` — an installable profile layer: `packages/bundle/*`, `dsh-subagent-codex`, `dsh-subagent-claude-code`. The `bundle/base` README pair is the worked example. + +A bundle README leads with the profile-install path and the layer semantics; the implementation fold explains the patch document. It never presents the package as a library to import or as a single plugin to mount. + +## Frontmatter + +```yaml +--- +description: "What the bundle layer adds to a dsh --profile surface, for users composing or customizing a profile." +kind: "package-bundle" +--- +``` + +## Skeleton + +```markdown +# @deepseek-ai/dsh- + +English | [中文](README.zh.md) + +## Summary + +Three to five sentences: what a profile gains from this layer, which profiles already include it, how a user adds or removes it, and the main boundary. + +## Table of Contents + +- [Use this package](#use-this-package) +- [Understand the implementation](#understand-the-implementation) +- [Further Exploration](#further-exploration) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) + +----- + + +## Use this package + +### Install into a profile + +The verified install path — run it against the current checkout before writing: + +```text +dsh plugin --profile add @deepseek-ai/dsh- +dsh plugin --profile remove @deepseek-ai/dsh- +``` + +State where in-box bundles resolve from, what the reconcile step activates, and what fails when the patch declaration is missing. + +### What you get + +The observable surface this layer adds: tools, providers, or UI rows, and which package owns each row's behavior. + +----- + + +## Understand the implementation + +
+Implementation internals — click to expand + +The patch document: insert list, row ids, platform gating, override semantics. Source-map table links `cordis.patch.yml` and `src/`. No API catalogs. + +
+ +----- + + +## Further Exploration + +Adjacent pages: the group map, the profile contract, the composition graph. + +----- + + +## Model Experience + +The form the verify-package-readme-model-experience gate assigns (bundle carriers are `indirect` or `none`: each inserted row's package owns its model-facing behavior). + +## Known Limitations and Deferred Work + + + +Current constraints: override semantics, platform gates, and conflict rules a user must respect. + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
+``` + +## Rules + +- **Only `dsh.bundle.patch` packages use this template.** Verify the declaration in `package.json` before classifying; the `dsh plugin` reconcile activates a layer for exactly these packages. +- **Test the install path.** Run `dsh plugin --profile add ` in a scratch profile and reproduce the documented warning, layer activation, and failure modes before writing them. +- Re-run `pnpm run verify-translation-pairing --write packages///README.md` after editing the pair. diff --git a/.agents/skills/dsh-doc/templates/package-group.md b/.agents/skills/dsh-doc/templates/package-group.md new file mode 100644 index 0000000000..7a9549de76 --- /dev/null +++ b/.agents/skills/dsh-doc/templates/package-group.md @@ -0,0 +1,59 @@ +# Template: package-group + +Use this template for `packages/README.md` and every `packages//README.md`. The page is a map: it orients the capability family, lists its direct packages with one-line roles, and links package-owned details. It never restates a package's contract. + +## Frontmatter + +```yaml +--- +description: "The package group: what the packages under packages// own, for readers choosing or navigating the family." +kind: "package-group" +--- +``` + +## Skeleton + +```markdown +# / — + +English | [中文](README.zh.md) + +## Summary + +Three to five sentences: what the family provides, what a reader can DO with it, which package owns which half, and the main boundary. + +## Table of Contents + +- [Packages](#packages) +- [Related documentation](#related-documentation) +- [Dev Note](#dev-note) + +----- + + +## Packages + +One short orienting sentence, then the package map: + +| Package | Role | +|---|---| +| [``](/README.md) | One-line role: what it contributes | + + +## Related documentation + +- [Adjacent owner](../../.md) — what it adds to this family. + + +## Dev Note + +None. +``` + +## Rules + +- One row per direct package; role text states the package's contribution, never its internals. +- Add a `ctx key`, package shape, or npm-name column only when that distinction helps readers choose among the direct packages. +- Related documentation links adjacent owners (group maps, subsystem pages, Agent Notes) with a short phrase per link. +- Do not add a Model Experience or Known Limitations section; the group map owns no runtime behavior. +- Re-run `pnpm run verify-translation-pairing --write packages//README.md` after editing the pair. diff --git a/.agents/skills/dsh-doc/templates/package-library.md b/.agents/skills/dsh-doc/templates/package-library.md new file mode 100644 index 0000000000..fcd37f17b0 --- /dev/null +++ b/.agents/skills/dsh-doc/templates/package-library.md @@ -0,0 +1,96 @@ +# Template: package-library + +Use this template for a package with no plugin surface: its entry exports a plain module API and it registers nothing into a composition. Examples: `boot/app-boot`, `util/*`, `sdk/protocol`, `typert/generator`. The `boot/app-boot` README pair is the worked example. + +A library README differs from a package reference in three ways: no "install into a profile" guidance (a library is a dependency, not a layer), no mount configuration (there is no `cordis.yml` row), and a Model Experience section only in the audited form the gate assigns (most libraries are `none` or `indirect`). + +## Frontmatter + +```yaml +--- +description: "What the library lets a caller build, in one or two concrete sentences with the consuming packages or searchable domain terms." +kind: "package-library" +--- +``` + +## Skeleton + +```markdown +# @deepseek-ai/dsh- + +English | [中文](README.zh.md) + +## Summary + +Three to five sentences: what a caller can DO with the library, who consumes it, the smallest entry point, and the main boundary. + +## Table of Contents + +- [Use this package](#use-this-package) +- [Understand the implementation](#understand-the-implementation) +- [Further Exploration](#further-exploration) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) + +----- + + +## Use this package + +### When to use it + +Name the consuming call sites (which bins, packages, or runtimes import it) and when a caller should reach for it instead of a plugin. + +### Entry point + +The smallest import-plus-call that works, in a `text` or `ts` fence, followed by what success and failure look like. Link the owning contracts in `src/index.ts` for exact detail instead of restating them. + +----- + + +## Understand the implementation + +
+Implementation internals — click to expand + +Design notes and a source-map table. No API catalogs. + +
+ +----- + + +## Further Exploration + +Adjacent pages, closest prerequisite first. + +----- + + +## Model Experience + +Only the form the verify-package-readme-model-experience gate assigns this package (`none`, `indirect`, or the canonical blocks). A library never invents model effects it does not have. + +## Known Limitations and Deferred Work + + + +Current package constraints as top-level bullets; allowlist the package in scripts/verify-package-readme-limitations.ts when none exist. + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
+``` + +## Rules + +- **Classify by the entry, not the folder.** Read `src/index.ts` before choosing this template: `export default` a service class or an `apply` export makes the package a `package-reference`, and `dsh.bundle.patch` in `package.json` makes it a `package-bundle`. A plain module API without those is a library. +- **Never write profile-install guidance.** `dsh plugin --profile add ` installs any npm dependency but activates a profile layer only for `dsh.bundle`-declaring packages; for a library it is at best a no-op dependency and must not appear as an install path. +- Re-run `pnpm run verify-translation-pairing --write packages///README.md` after editing the pair. diff --git a/.agents/skills/dsh-doc/templates/package-reference.md b/.agents/skills/dsh-doc/templates/package-reference.md new file mode 100644 index 0000000000..0e8c5f341b --- /dev/null +++ b/.agents/skills/dsh-doc/templates/package-reference.md @@ -0,0 +1,103 @@ +# Template: package-reference + +Use this template for a package whose entry is a Cordis plugin — a service default export or an `apply` function — mounted in a composition. This is the default for `packages///README.md`. The `session-persistence-sqlite` README pair is the worked example of this template. + +## Frontmatter + +```yaml +--- +description: "What the package lets a reader choose, configure, or debug, in one or two concrete sentences with searchable domain terms." +kind: "package-reference" +--- +``` + +## Skeleton + +```markdown +# @deepseek-ai/dsh- + +English | [中文](README.zh.md) + +## Summary + +Three to five sentences on what a user or agent can DO with the package: outcomes, when to choose it, main cost, most important boundary. Never its role, type, or internal identity. + +## Table of Contents + +- [Use this package](#use-this-package) +- [Understand the implementation](#understand-the-implementation) +- [Further Exploration](#further-exploration) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) + +----- + + +## Use this package + +One orienting sentence: the common path. + +### When to choose it + +Choose or avoid the package: one paragraph naming the deciding conditions and the fallback package. + +### Minimal configuration + +The smallest mount that works, as a `cordis.yml` snippet, plus the config table: + +| Field | Default | Meaning | +|---|---|---| +| `` | `` or `required` | One-line meaning | + +The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-) is the exhaustive source for every accepted field. + +----- + + +## Understand the implementation + +
+Implementation internals — click to expand + +Design concept, component architecture, and hand-waving dataflow — enough to understand the package. A source-map table links files for exact detail. No API catalogs or JSDoc restatement. + +
+ +----- + + +## Further Exploration + +Three to seven adjacent pages, closest prerequisite first, one short phrase each. + +----- + + +## Model Experience + + + +## Known Limitations and Deferred Work + + + +One orienting sentence, then top-level bullets naming current package constraints. Packages with none use the allowlist in scripts/verify-package-readme-limitations.ts. + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
+``` + +## Rules + +- **Fact-check before writing.** Mount the package in a test composition and run every command, config field, default, and behavior claim this README makes. Delete anything you did not reproduce; link the generated config catalog instead of restating fields. +- **Installation guidance.** A plugin package mounts through `cordis.yml` rows. Only a package declaring `dsh.bundle.patch` installs as a profile layer via `dsh plugin --profile add ` — if this package lacks that declaration, say how it mounts in a composition, never `dsh plugin add`. +- **Model Experience and Known Limitations are gate-owned.** Match the exact headings and per-package forms the two gates enforce; update the gates' audited lists in the same change when behavior moves a package between forms. +- Re-run `pnpm run verify-translation-pairing --write packages///README.md` after editing the pair. diff --git a/.agents/skills/dsh-find-simplifications/SKILL.md b/.agents/skills/dsh-find-simplifications/SKILL.md index ef7f387c4e..12df8bb3c8 100644 --- a/.agents/skills/dsh-find-simplifications/SKILL.md +++ b/.agents/skills/dsh-find-simplifications/SKILL.md @@ -28,7 +28,7 @@ A strong simplification removes, folds, or demotes something real and has clear - Hand-rolled code reimplements what a well-maintained external package or a Node builtin at the engine floor already provides, and the swap would delete the implementation plus its dedicated tests ([dependency policy](../../notes/implemented/process/2026-07-26-dependencies-over-hand-rolling.md)). - The simplified behavior may differ slightly, but the new behavior is still reasonable and easier to explain. -Thin candidates are usually not enough for an Agent Note: deleting one typo, running `knip` once, removing an intentionally documented backend/adapter, or flagging "this looks complex" without call-site proof. +Thin candidates are not enough for an Agent Note: deleting one typo, running `knip` once, removing an intentionally documented backend/adapter, or flagging "this looks complex" without call-site proof. ## Survey Broadly diff --git a/.agents/skills/dsh-prose-standard/SKILL.md b/.agents/skills/dsh-prose-standard/SKILL.md index 42f9bbab9e..bb8347823b 100644 --- a/.agents/skills/dsh-prose-standard/SKILL.md +++ b/.agents/skills/dsh-prose-standard/SKILL.md @@ -5,7 +5,7 @@ description: Use when writing, reviewing, restoring, trimming, or auditing prose # DeepSeek Harness Prose Standard -Write enough to preserve the contract, then remove reasoning transcripts, repetition, and decoration. A contract is an obligation, invariant, precondition, postcondition, or compatibility promise that a caller, callee, implementer, producer, or consumer relies on. This skill owns editorial judgment and required prose coverage; use [dsh-doc-standards](../dsh-doc-standards/SKILL.md) for placement, budgets, bilingual pairs, and documentation gates, and [dsh-trim-cot-leakage](../dsh-trim-cot-leakage/SKILL.md) for hunting and fixing reasoning-transcript leakage. It is guidance, not a script. +Write enough to preserve the contract, then remove reasoning transcripts, repetition, and decoration. A contract is an obligation, invariant, precondition, postcondition, or compatibility promise that a caller, callee, implementer, producer, or consumer relies on. This skill owns editorial judgment and required prose coverage; use [dsh-doc](../dsh-doc/SKILL.md) for placement, budgets, bilingual pairs, and documentation gates, and [dsh-trim-cot-leakage](../dsh-trim-cot-leakage/SKILL.md) for hunting and fixing reasoning-transcript leakage. It is guidance, not a script. Treat `contract`, `boundary`, `shape`, `surface`, `seam`, `gate`, and `vocabulary` as terms to check before use, not banned words. First ask whether the exact rule, API, field set, type, validation, timing point, component split, or failure states the fact better. Keep a term when it names the exact technical subject, including caller/callee contracts and security/process boundaries. diff --git a/.agents/skills/dsh-trim-cot-leakage/SKILL.md b/.agents/skills/dsh-trim-cot-leakage/SKILL.md index 8e7ea1714d..fb49a23b7f 100644 --- a/.agents/skills/dsh-trim-cot-leakage/SKILL.md +++ b/.agents/skills/dsh-trim-cot-leakage/SKILL.md @@ -40,6 +40,6 @@ Unaided citation passes fail in both directions by deleting durable references a 1. Scope and exclusions per [dsh-prose-standard](../dsh-prose-standard/SKILL.md): require an explicit scope; never touch `vendor/` or `.agents/notes/archived/`. Recorded fixtures and snapshots are derivatives, not prose targets: change the owning source or scenario and regenerate them only when an authorized behavior change requires new evidence. 2. Audit read-only first: run the [recall batteries](references/recall-batteries.md) (with `--hidden` so `.agents/` is searched), calibrating each probe against a known positive and a near-miss negative before trusting its output, then judge every hit semantically. The batteries are probes, not the definition — each review round of the original purge found cases the batteries missed, so also read the densest prose in scope (module JSDoc, READMEs, Agent Notes) without a pattern in hand. -3. Fix owner-first per surface: generated catalogs → trace every consumer, fix the source JSDoc or generator template, then regenerate all derivatives; type-equivalence fences → fix the source JSDoc, then re-paste both bilingual pages (`verify-type-equiv` pins them); bilingual prose → update the counterpart minimally and re-record it through the [lightweight routine](../../../docs/AGENTS.md#writing-rules); bilingual fences → copy the corrected verbatim block byte-for-byte into both sides per [dsh-doc-standards](../dsh-doc-standards/SKILL.md), then re-record the pair; model- or user-visible strings → route through [dsh-prose-standard](../dsh-prose-standard/SKILL.md) and change only with owning behavior evidence, otherwise leave unchanged and report the deferral. +3. Fix owner-first per surface: generated catalogs → trace every consumer, fix the source JSDoc or generator template, then regenerate all derivatives; type-equivalence fences → fix the source JSDoc, then re-paste both bilingual pages (`verify-type-equiv` pins them); bilingual prose → update the counterpart minimally and re-record it through the [lightweight routine](../../../docs/AGENTS.md#writing-rules); bilingual fences → copy the corrected verbatim block byte-for-byte into both sides per [dsh-doc](../dsh-doc/SKILL.md), then re-record the pair; model- or user-visible strings → route through [dsh-prose-standard](../dsh-prose-standard/SKILL.md) and change only with owning behavior evidence, otherwise leave unchanged and report the deferral. 4. Before deleting anything, enumerate the passage's propositions (prose-standard) and check the [overcorrection traps](references/examples.md#overcorrection-traps): trims that flip an obligation into an endorsement, promote a hypothetical to a shipped feature, delete a true fact, or drop provenance. 5. Verify: re-run the batteries expecting only sanctioned keeps, this skill's own directory, and the owning note's quoted evidence; confirm every remaining citation resolves at HEAD; run the gates for touched surfaces (`doc-sync` for docs, `verify-type-equiv`, `verify-translation-pairing`). diff --git a/AGENTS.md b/AGENTS.md index 5d0b28d024..504a3e49ad 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,14 +1,12 @@ # AGENTS.md -DeepSeek Harness is an all-plugin agent harness on vendored Cordis. Read [docs/architecture.md](docs/architecture.md) before changing `packages/`; follow [docs/AGENTS.md](docs/AGENTS.md) for documentation. +DeepSeek Harness is an all-plugin Cordis agent harness. Read [docs/architecture.md](docs/architecture.md) before changing `packages/`; follow [docs/AGENTS.md](docs/AGENTS.md) for documentation. ## Pre-release stance: foundation over blast radius -**Remove at the first tagged release.** Until then, prefer correct foundations over compatibility shims and update every reference together. Backends reject old disk formats; SQLite increments `SCHEMA_VERSION`, while `dsh-session` holds `SESSION_FORMAT_VERSION` at `0` without a compatibility promise. +**Remove at the first tagged release.** Until then, prefer correct foundations to compatibility shims: rename or repackage freely and update every reference. Backends reject old on-disk formats. SQLite uses monotonic `SCHEMA_VERSION`; `dsh-session` keeps `SESSION_FORMAT_VERSION` at `0` with no compatibility promise. -## Application launch - -Supported Node applications launch only through `dsh` profiles; application-package bins, demos, and public SDK argv escape hatches are forbidden. [Architecture](docs/architecture.md#application-launch) owns the launch set; `pnpm run verify-application-entrypoints` enforces it. +**Application launch.** Only `dsh` profiles launch supported Node apps; package bins, demos, and public SDK argv escapes are forbidden ([rule](docs/architecture.md#application-launch)). ## Repository layout @@ -46,7 +44,7 @@ packages/ @deepseek-ai/dsh- workspaces at packages/// acp/ automation-only Agent Client Protocol server interaction/ approval/interaction capabilities, permission, commands, ask-user boot/ shared profile/application boot glue - sdk/ JSON-RPC protocol, server, and TypeScript client + sdk/ JSON-RPC protocol + TypeScript client/server examples/ reusable composition bundles (agent-spine) experimental/ private prototypes excluded from official releases support/ dev/test infrastructure @@ -79,6 +77,7 @@ pnpm run build # tsc emits lib/types, tsdown bundles runtime pnpm run hygiene # knip + publint + workspace constraints + NodeNext consumer check pnpm run check:windows-wine # ONLY when diagnosing a known Windows failure (needs wine); CI owns this signal pnpm run doc-sync # all documentation gates; leaf list in scripts/run-gates.ts +pnpm run test:docs # quick documentation checks (no build; doc-quick aggregate) pnpm run website:build # VitePress build (doubles as dead-link check) pnpm dsh --profile headless "task" # run one task from source (needs DEEPSEEK_API_KEY) pnpm run demo:code-mode -- "task" # headless Code Mode run (needs key) @@ -90,7 +89,11 @@ If a required `gh`, `pnpm`, build, test, or generator command fails because the ### Run relevant checks locally -Before pushes, use [dsh-pre-push-checks](.agents/skills/dsh-pre-push-checks/SKILL.md) to choose the smallest diff-covering checks; after `gh stack sync`, validate immediately and never merge before they pass. Report commands only. Match evidence to its surface: focused behavior tests, model/user snapshots, `doc-sync`, build/hygiene plus built smokes for published paths, and real-API e2e for provider behavior. CI owns exhaustive coverage and the platform matrix; run them locally only by request, for CI diagnosis, or for an irreducibly repository-wide change. `test:coverage`, not `test`, is the CI coverage gate ([why](docs/testing.md)). +Run checks before pushes via [dsh-pre-push-checks](.agents/skills/dsh-pre-push-checks/SKILL.md); report only commands run. After `gh stack sync`, validate immediately; do not merge before checks pass. + +- Match evidence to the surface: focused behavior tests, model/user-output snapshots, `doc-sync` for docs, built smokes for published paths, and real-API e2e for providers. +- Never default to the full suite or repeat a passing check for commit or push. CI owns exhaustive coverage and the platform matrix; rehearse all locally only by explicit request, for CI diagnosis, or for an irreducibly repository-wide change. +- `test:coverage`, not `test`, is the CI coverage gate ([why](docs/testing.md)). ## Secrets / .env diff --git a/README.i18n.yaml b/README.i18n.yaml index 1609bf9532..99b7bfa63e 100644 --- a/README.i18n.yaml +++ b/README.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 README.md -README.md: 9847d1fc35d5eceea484c229872dc8614ba3654a -README.zh.md: 6838b7c712e181dc44ca467225adf2aadf7ad947 +README.md: 8fe2204c765dbccfd79a438a3a58900b7b21f52e +README.zh.md: 3c7ad619303dad747cd5114375647b13a7629f8b diff --git a/README.md b/README.md index 9847d1fc35..8fe2204c76 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ English | [中文](README.zh.md) DeepSeek Harness (`dsh`) is an open-source agent harness developed by [DeepSeek AI](https://deepseek.com). -It uses an architecture where **everything is a plugin**, and is powered by [Cordis](https://github.com/cordiverse/cordis), whose design is described in [_A Programming Paradigm for Spatiotemporal Composability_](https://github.com/cordiverse/paper). +It is built on an **everything-is-a-plugin** architecture and powered by [Cordis](https://github.com/cordiverse/cordis), whose design is described in [_A Programming Paradigm for Spatiotemporal Composability_](https://github.com/cordiverse/paper). Documentation: [https://deepseek-harness.github.io/deepseek-harness/](https://deepseek-harness.github.io/deepseek-harness/) ## Developer preview -DeepSeek Harness is currently in _developer preview_ and is iterating rapidly. **THERE WILL BE COMPATIBILITY-BREAKING CHANGES.** +DeepSeek Harness is in _developer preview_ and iterating rapidly. **THERE WILL BE COMPATIBILITY-BREAKING CHANGES.** Review the [safety notice](SAFETY.md) before running the project. @@ -42,7 +42,7 @@ pnpm dsh web ## Community and support -- Feel free to submit feedback or bug reports through [GitHub Discussions](https://github.com/deepseek-ai/deepseek-harness/discussions). +- Submit feedback or bug reports through [GitHub Discussions](https://github.com/deepseek-ai/deepseek-harness/discussions). - Add the [`dsh-plugin`](https://github.com/topics/dsh-plugin) topic to your plugin repository for discoverability. - Join DeepSeek Harness Discord community. diff --git a/README.zh.md b/README.zh.md index 6838b7c712..3c7ad61930 100644 --- a/README.zh.md +++ b/README.zh.md @@ -4,13 +4,13 @@ DeepSeek Harness(`dsh`)是由 [DeepSeek AI](https://deepseek.com) 开发的开源 agent harness(智能体框架)。 -它采用**一切皆插件**的架构,并由 [Cordis](https://github.com/cordiverse/cordis) 驱动,其设计参见论文 [_A Programming Paradigm for Spatiotemporal Composability_](https://github.com/cordiverse/paper)。 +它构建于**一切皆插件**的架构之上,由 [Cordis](https://github.com/cordiverse/cordis) 驱动,其设计参见论文 [_A Programming Paradigm for Spatiotemporal Composability_](https://github.com/cordiverse/paper)。 文档:[https://deepseek-harness.github.io/deepseek-harness/](https://deepseek-harness.github.io/deepseek-harness/) ## 开发者预览 -DeepSeek Harness 目前处于 _开发者预览_ 阶段,正在快速迭代。**未来将出现破坏兼容性的变更。** +DeepSeek Harness 处于 _开发者预览_ 阶段,正在快速迭代。**未来将出现破坏兼容性的变更。** 运行本项目前,请阅读[安全说明](SAFETY.zh.md)。 @@ -46,7 +46,7 @@ pnpm dsh web ## 社区与支持 -- 欢迎通过 [GitHub Discussions](https://github.com/deepseek-ai/deepseek-harness/discussions) 提交反馈或 bug 报告。 +- 通过 [GitHub Discussions](https://github.com/deepseek-ai/deepseek-harness/discussions) 提交反馈或 bug 报告。 - 为你的插件仓库添加 [`dsh-plugin`](https://github.com/topics/dsh-plugin) 话题,便于被发现。 - 欢迎加入 DeepSeek Harness 企微群:扫码添加企微小助手并填写入群问卷,完成后小助手会邀请你入群。 diff --git a/apps/cli/README.i18n.yaml b/apps/cli/README.i18n.yaml index 91a69fafc0..9b6e5e5944 100644 --- a/apps/cli/README.i18n.yaml +++ b/apps/cli/README.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 apps/cli/README.md -README.md: 4cbb60483d24d350f53bffa517dcb514e353f492 -README.zh.md: 4ac4bda47cfd5dee843422c854f4306740968746 +README.md: 850a9371c1515d1e0219e9a685b638063c498ff4 +README.zh.md: 02de213d7a7158971b445511a00661183b37234c diff --git a/apps/cli/README.md b/apps/cli/README.md index 4cbb60483d..850a9371c1 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -20,7 +20,7 @@ The invoking directory is the default workspace root. The `web`, `headless`, `sd ## App arguments -The launcher parses only its own flags and hands everything after them to the booted profile, where any injected app plugin may parse the shared immutable snapshot ([`dsh-cmdline`](../../packages/boot/cmdline/README.md)). Launcher flags therefore come first, and the first token the launcher does not recognize starts the app's arguments: +The launcher parses only its own flags and hands everything after them to the booted profile, where any injected app plugin may parse the shared immutable snapshot ([`dsh-cmdline`](../../packages/boot/cmdline/README.md)). The first token the launcher does not recognize starts the app's arguments: ```sh dsh --profile web --port 8080 # --port belongs to the web app @@ -30,6 +30,7 @@ dsh --profile web --help # the web app's flags, not the launcher's dsh --help # the launcher's own help ``` + ## Profiles A profile directory holds a `package.json` (out-of-tree plugin dependencies plus the profile manifest `dsh.profile` with its ordered `bundles` list and `patchReload` lifecycle) and a `cordis.patch.yml` (the user's own patch layer). `patchReload: live` watches the profile and home-level patch files; `startup` applies them once. diff --git a/apps/cli/README.zh.md b/apps/cli/README.zh.md index 4ac4bda47c..02de213d7a 100644 --- a/apps/cli/README.zh.md +++ b/apps/cli/README.zh.md @@ -20,7 +20,7 @@ ## 应用参数 -启动器只解析自身的 flag,并将其后的所有内容交给已启动的 profile;注入该 profile 的任意应用插件都可以解析这份共享的不可变快照([`dsh-cmdline`](../../packages/boot/cmdline/README.zh.md))。因此,启动器的 flag 必须写在最前面;启动器无法识别的第一个 token 标志着应用参数的开始: +启动器只解析自身的 flag,并将其后的所有内容交给已启动的 profile;注入该 profile 的任意应用插件都可以解析这份共享的不可变快照([`dsh-cmdline`](../../packages/boot/cmdline/README.zh.md))。启动器无法识别的第一个 token 标志着应用参数的开始: ```sh dsh --profile web --port 8080 # --port belongs to the web app @@ -31,7 +31,6 @@ dsh --help # the launcher's own help ``` - ## Profile profile 目录包含一个 `package.json`,其中记录树外插件依赖,以及 profile manifest(元数据清单)`dsh.profile`、其中按顺序排列的 `bundles` 列表与 `patchReload` 生命周期;还包含一个 `cordis.patch.yml`,其中保存用户自己的 patch 层。`patchReload: live` 监视 profile 与 home 级 patch 文件,`startup` 则只应用一次。 diff --git a/apps/cli/reference/README.i18n.yaml b/apps/cli/reference/README.i18n.yaml index b625af64ef..ddebf7616b 100644 --- a/apps/cli/reference/README.i18n.yaml +++ b/apps/cli/reference/README.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 apps/cli/reference/README.md -README.md: fe8d6ef0bb296f0807de4a3ec2756016bbb510c2 -README.zh.md: e8c353f33bc9760fd6da74af33a85111cf9012aa +README.md: ba1804cb9985ae8b275c64690d63b1f56eb22041 +README.zh.md: c5fbff9310eee92251115cc61fb33abc5896aedd diff --git a/apps/cli/reference/README.md b/apps/cli/reference/README.md index fe8d6ef0bb..ba1804cb99 100644 --- a/apps/cli/reference/README.md +++ b/apps/cli/reference/README.md @@ -77,7 +77,7 @@ dsh web --dump-config dsh web --help ``` -The production Web runner needs built package and frontend artifacts (`pnpm run build`). It serves `http://127.0.0.1:3080` by default and, for a local launch, opens that canonical host URL only after the complete Loader tree settles. A non-empty inherited `SSH_CONNECTION` or `SSH_TTY` suppresses the browser handoff because the SSH client or editor owns the local forwarded address; the host URL is still printed. The CLI intentionally does not support `--host 0.0.0.0` yet and exits with a usage error. Immediately before a local handoff it prints `dsh web: opening the default browser; pass --no-open to disable`; if the operating-system handoff fails, a diagnostic on stderr states the reason, leaves the server running, and names the URL for manual use. `--trusted-host` adds named authorities accepted by the `/api` browser-trust fence. +The production Web runner needs built package and frontend artifacts (`pnpm run build`). It serves `http://127.0.0.1:3080` by default and, for a local launch, opens that canonical host URL only after the complete Loader tree settles. A non-empty inherited `SSH_CONNECTION` or `SSH_TTY` suppresses the browser handoff because the SSH client or editor owns the local forwarded address; the host URL is still printed. The CLI intentionally does not support `--host 0.0.0.0` and exits with a usage error. Immediately before a local handoff it prints `dsh web: opening the default browser; pass --no-open to disable`; if the operating-system handoff fails, a diagnostic on stderr states the reason, leaves the server running, and names the URL for manual use. `--trusted-host` adds named authorities accepted by the `/api` browser-trust fence. Process shutdown gives the plugin tree up to five seconds to dispose. The first `SIGINT`/`SIGTERM` starts that graceful drain — `SIGTERM` is a supervisor's ordinary stop request and exits 0 on every surface, `SIGINT` reports 130; a second signal forces immediate exit. If one-shot normal completion is already stuck in disposal, the first `Ctrl+C` is the escalation and exits immediately instead of being swallowed. @@ -95,6 +95,7 @@ Session telemetry stays local by default. `DSH_TELEMETRY_MODE=FULL` streams ever Install external plugin bundles through `dsh plugin --profile add `. The installed package owns its dependencies and contributes its declared `cordis.patch.yml` layer. The CLI also ships `@deepseek-ai/dsh-mcp-client` as a dependency for patch layers, but no MCP server is enabled by default because each server command is trusted executable code outside the agent sandbox. + ## Source execution From the repository root, run `pnpm run build` separately after a fresh checkout and whenever artifacts need updating, then use `pnpm dsh `. The `package.json` script launches `apps/cli/src/bin.ts` with `node --import tsx/esm` without building and forwards every argument. Missing Typert host artifacts fail profile boot through module-resolution errors without a build instruction. Once those host artifacts exist, missing frontend or client-plugin bundles fail at startup with an instruction to run `pnpm run build`. The launcher does not check freshness, so existing stale bundles can run older browser code until rebuilt. The process inherits the launch environment; set `NODE_USE_ENV_PROXY=1` when a supporting Node version must honor `HTTP_PROXY` and `HTTPS_PROXY`. The installed form launches the built `apps/cli/lib/bin.js` without rebuilding the repository. diff --git a/apps/cli/reference/README.zh.md b/apps/cli/reference/README.zh.md index e8c353f33b..c5fbff9310 100644 --- a/apps/cli/reference/README.zh.md +++ b/apps/cli/reference/README.zh.md @@ -77,7 +77,7 @@ dsh web --dump-config dsh web --help ``` -生产 Web 运行器需要已构建的包和前端产物(`pnpm run build`)。默认服务地址是 `http://127.0.0.1:3080`;本机启动时,只在完整 Loader 配置树结算后才用默认浏览器打开该规范宿主机 URL。继承的 `SSH_CONNECTION` 或 `SSH_TTY` 非空时会跳过浏览器交接,因为本地转发地址由 SSH 客户端或编辑器持有;宿主机 URL 仍会打印。CLI 目前有意不支持 `--host 0.0.0.0`,并会以用法错误退出。本机交接前会打印英文提示 `dsh web: opening the default browser; pass --no-open to disable`;若操作系统交接失败,stderr 诊断会说明原因、给出 URL 供手动访问,服务器仍继续运行。`--trusted-host` 可添加 `/api` 浏览器信任围栏接受的具名 authority。 +生产 Web 运行器需要已构建的包和前端产物(`pnpm run build`)。默认服务地址是 `http://127.0.0.1:3080`;本机启动时,只在完整 Loader 配置树结算后才用默认浏览器打开该规范宿主机 URL。继承的 `SSH_CONNECTION` 或 `SSH_TTY` 非空时会跳过浏览器交接,因为本地转发地址由 SSH 客户端或编辑器持有;宿主机 URL 仍会打印。CLI 有意不支持 `--host 0.0.0.0`,并会以用法错误退出。本机交接前会打印英文提示 `dsh web: opening the default browser; pass --no-open to disable`;若操作系统交接失败,stderr 诊断会说明原因、给出 URL 供手动访问,服务器仍继续运行。`--trusted-host` 可添加 `/api` 浏览器信任围栏接受的具名 authority。 进程关闭时,插件树最多有 5 秒完成 dispose。首次收到 `SIGINT` 或 `SIGTERM` 时会开始优雅排空:`SIGTERM` 是监督进程发出的常规停止请求,在所有运行模式下都以 0 退出;`SIGINT` 则报告 130。第二次收到信号时会立即强制退出。如果一次性运行在正常结束时已经卡在 dispose 阶段,第一次按下 `Ctrl+C` 就会直接升级为强制退出,而不会被忽略。 @@ -96,7 +96,6 @@ dsh web --help 通过 `dsh plugin --profile add ` 安装外部插件组合包。安装的包拥有其依赖,并贡献其声明的 `cordis.patch.yml` 层。CLI 还随附 `@deepseek-ai/dsh-mcp-client` 作为供 patch 层使用的依赖,但默认不启用 MCP 服务器,因为每条服务器命令都是 agent(智能体)沙箱之外的受信任可执行代码。 - ## 源码执行 请在仓库根目录中,于全新 checkout 之后及产物需要更新时单独运行 `pnpm run build`,然后使用 `pnpm dsh `。`package.json` 中的脚本不会构建,而是通过 `node --import tsx/esm` 启动 `apps/cli/src/bin.ts`,并转发所有参数。Typert Host 产物缺失时,profile 启动会因不含构建指引的模块解析错误而失败。这些 Host 产物存在后,如果前端或 Client plugin 组合包缺失,启动会失败并提示运行 `pnpm run build`。启动器不会检查产物是否为最新,因此已有的陈旧组合包可能继续运行旧版浏览器代码,直至重新构建。该进程会继承启动环境;当支持环境代理的 Node 版本必须遵循 `HTTP_PROXY` 和 `HTTPS_PROXY` 时,请设置 `NODE_USE_ENV_PROXY=1`。安装形式会直接启动构建后的 `apps/cli/lib/bin.js`,不会重新构建仓库。 diff --git a/docs/AGENTS.md b/docs/AGENTS.md index 547ae275ee..5efb5c6c40 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -1,6 +1,6 @@ # AGENTS.md — The documentation standard -This file defines document structure, Markdown tiers, writing rules, and `verify-doc-budgets` ceilings. Use [dsh-doc-standards](../.agents/skills/dsh-doc-standards/SKILL.md) for placement and validation, and [dsh-prose-standard](../.agents/skills/dsh-prose-standard/SKILL.md) for required coverage and editorial judgment; the [doc-tiers Agent Note](../.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md) owns rationale. +This file defines document structure, Markdown tiers, writing rules, and `verify-doc-budgets` ceilings. Use [dsh-doc](../.agents/skills/dsh-doc/SKILL.md) for placement and validation, and [dsh-prose-standard](../.agents/skills/dsh-prose-standard/SKILL.md) for required coverage and editorial judgment; the [doc-tiers Agent Note](../.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md) owns rationale. ## Document structure @@ -54,11 +54,11 @@ When the gate goes red: 2. **Condense** content that belongs here but can be shorter. 3. **Raise** the ceiling only when the words need the space; justify the manifest diff in the PR. A too-low ceiling is a budget bug. -Ceilings are guardrails, not reduction targets. At or below target, retain at least 5% headroom; above target, freeze the ceiling until relocation or condensation brings the document under target. Lower a ceiling only when the document still has room, and raise it when content would otherwise be deleted. Targets: root `AGENTS.md` ≤ 1,600 words; `architecture.md` ≤ 1,800; subtree `AGENTS.md` ≤ 600, except `packages/AGENTS.md` ≤ 650 and this file ≤ 1,250; `packages/README.md` ≤ 600. Review governs unbudgeted tiers. +Ceilings are guardrails, not reduction targets. At or below target, retain at least 5% headroom; above target, freeze the ceiling until relocation or condensation brings the document under target. Lower a ceiling only when the document still has room. Targets: root `AGENTS.md` ≤ 1,950; `architecture.md` ≤ 2,400; subtree `AGENTS.md` ≤ 600, except `packages/AGENTS.md` ≤ 675 and this file ≤ 1,320; `packages/README.md` ≤ 994; plus `cordis-primer.md` 600, `defensive-patterns.md` 550, `testing.md` 1,150, `examples/AGENTS.md` 310. Review governs unbudgeted tiers. ## The slop checklist -Hunt these in any doc; [dsh-doc-standards](../.agents/skills/dsh-doc-standards/SKILL.md) runs this list as an audit: +Hunt these in any doc; [dsh-doc](../.agents/skills/dsh-doc/SKILL.md) runs this list as an audit: - The same rule stated in more than one home. Grep a distinctive phrase; keep one home and link the rest. - Narrated history or war stories: "previously", "now", "no longer", "used to", "renamed", "was moved", PRs, or commits. State the current fact; link an Agent Note or postmortem when needed. diff --git a/docs/api-gateway.i18n.yaml b/docs/api-gateway.i18n.yaml index bdf326f8db..07858f39c4 100644 --- a/docs/api-gateway.i18n.yaml +++ b/docs/api-gateway.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/api-gateway.md -api-gateway.md: 60b9893675ad965c3f88677eac32352acfffeb31 -api-gateway.zh.md: fc217ce3a976fd8cf045848aa331c2115c3a4d65 +api-gateway.md: 86c43ccc5719107ded1e4004d6fabbc32b760520 +api-gateway.zh.md: 6fa170b84129eb96a70d0831158a9f9c43db256c diff --git a/docs/api-gateway.md b/docs/api-gateway.md index 60b9893675..86c43ccc57 100644 --- a/docs/api-gateway.md +++ b/docs/api-gateway.md @@ -84,7 +84,7 @@ The `api-remotes` assembly and the `ctx.remote` contract are React-independent; | Shared | `@deepseek-ai/dsh-typert-protocol` | Declares decorators, Gateway bindings, merge-extensible protocol maps, invocation descriptors, and provider types; starts no TypeScript analysis and registers no Cordis services | | Build | `@deepseek-ai/dsh-typert-generator` | Strictly analyzes Remote signatures, the type graph, lookups, Contexts, and source locations from the Host `ts.Program`, then generates Host and Host-for-Client artifacts | | Host | `@deepseek-ai/dsh-typert-registry` and Loader | Places generated Host descriptors, schemas, and business-package registrations in `ctx.typert`, and holds lookup and Context providers | -| Host | `@deepseek-ai/dsh-api-remotes` | Owns the application Agent/Session identity policy and configures the corresponding Typert lookups | +| Host | `@deepseek-ai/dsh-api-session-controller` | Owns the application Agent/Session identity policy and configures the corresponding Typert lookups | | Host | `@deepseek-ai/dsh-api-gateway` | Provides `ctx.typertGateway`, claims Remote endpoints, resolves objects or Contexts, invokes live Cordis services, and validates request and return values | | Client | `@deepseek-ai/dsh-api-gateway/client` | Provides `ctx.remote` and `remote.` child Services, mounts generated descriptors as concrete methods, and initiates, validates, and cancels calls through the Connection | | Client | `@deepseek-ai/dsh-api-remotes/client` | Explicitly selects and mounts the `/remote` contributions allowed by the application and brings the corresponding declaration merges into business code | @@ -98,7 +98,7 @@ The root build runs `build:lib:host`, `build:lib:client`, and `build:web` in ord Both tsdown passes receive the complete workspace and bundle only JavaScript emitted to `lib/types` by the corresponding tsc phase. The root config does not scan Client artifacts, classify package names, or pass a maintained filter to tsdown; package-local configs return entries for the current phase based on `DSH_BUILD_FACE`. An ordinary Client plugin produces both its Node loader entry and browser bundle during the Client phase. -`api-remotes` is the only package with split TypeScript faces. Its Host project owns the Agent/Session lookup policy, while its Client project depends on `/remote` declarations generated for business packages during Host tsdown; root aggregates and direct consumers must reference `api/remotes/tsconfig.host.json` or `api/remotes/tsconfig.client.json` respectively. The package's `clientBundle(..., { hostPhase: true })` produces its Host entry during Host tsdown and leaves only the browser entry for Client tsdown. Every other package remains registered in one aggregate. +`api/remotes`, `api/gateway`, `api/session-controller`, and `api/workspace-controller` (plus `client/connection`) split TypeScript faces. `api/remotes`' Client project depends on `/remote` declarations generated for business packages during Host tsdown; root aggregates and direct consumers must reference each split package's `tsconfig.host.json` or `tsconfig.client.json` respectively. `api-remotes`' `clientBundle(..., { hostPhase: true })` produces its Host entry during Host tsdown and leaves only the browser entry for Client tsdown. The Agent/Session lookup policy lives in `@deepseek-ai/dsh-api-session-controller`, not in `api-remotes`. Each contributing business package writes generated files to its own `lib/` directory, not to its source directory: @@ -120,11 +120,11 @@ Strict analysis requires a Remote to be a public, non-static instance method wit Remote and API Proxy share the Connection's `/api` route. The Client Remote calls `connection.rpc.call('/api', '/', { args }, signal)`; the HTTP carrier maps this to `POST /api//`, with a payload containing only a named `args` object. -The Connection performs the unified trust check for `/api` before the HTTP bridge, then dispatches inside the shared FetchHandler in interceptor order. The Typert Gateway claims only two-segment endpoints that have a strict descriptor or active SRC marker; unclaimed requests fall back to the existing API Proxy. The Connection owns transport, RPC ids, response envelopes, and request cancellation, while the Gateway owns only the Remote data protocol and business dispatch. Replacing the Connection carrier in the future does not require changes to Remote descriptors or the Client programming interface. +The Connection performs the unified trust check for `/api` before the HTTP bridge, then dispatches inside the shared FetchHandler in interceptor order. The Typert Gateway claims only two-segment endpoints that have a strict descriptor or active SRC marker; unclaimed requests fall back to the existing API Proxy. The Connection owns transport, RPC ids, response envelopes, and request cancellation, while the Gateway owns only the Remote data protocol and business dispatch. Replacing the Connection carrier does not require changes to Remote descriptors or the Client programming interface. For every call, the Gateway resolves the descriptor and live service from the current registries instead of caching business objects. It requires the fields in `args` to match the descriptor exactly, validates wire values with codecs, resolves objects or receivers through registered lookup or Context providers, invokes the service method targeted by the binding, and validates the return value. A missing provider, unknown identity, binding mismatch, missing or extra argument, schema failure, or missing method fails before entering or after leaving business code. -The lookup provider's `register()` supplies both the stable declaration and the default resolver; `configure()` supplies a resolver owned by Host composition that may execute asynchronously and is scoped to an effect lifetime. Configuration may precede provider mounting; without a provider, invocation still fails with `lookup-unavailable`, and unloading the configuration restores the provider's default policy. API Remotes owns the standard `agentFor()` semantics for `agent` and `session`: it reuses a live Agent, automatically resumes ordinary cold sessions, deduplicates concurrent resumes, and rejects identities owned by subagent routing; the `session` lookup returns that Agent's Session. The Web API Proxy supplies its Agent defaults and scope setup, then consumes the same resolver for legacy methods. Resume failures and ownership fences pass through unchanged as existing RPC errors rather than being collapsed into the Gateway's `internal` error. +The lookup provider's `register()` supplies both the stable declaration and the default resolver; `configure()` supplies a resolver owned by Host composition that may execute asynchronously and is scoped to an effect lifetime. Configuration may precede provider mounting; without a provider, invocation still fails with `lookup-unavailable`, and unloading the configuration restores the provider's default policy. The Session Controller owns the standard `agentFor()` semantics for `agent` and `session`: it reuses a live Agent, automatically resumes ordinary cold sessions, deduplicates concurrent resumes, and rejects identities owned by subagent routing; the `session` lookup returns that Agent's Session. The Web API Proxy supplies its Agent defaults and scope setup, then consumes the same resolver for legacy methods. Resume failures and ownership fences pass through unchanged as existing RPC errors rather than being collapsed into the Gateway's `internal` error. Unloading a Client contribution removes its descriptors and concrete methods together, aborts its in-flight calls, and makes stale method handles retained by external code reject further calls. A strict endpoint withdrawn on the Host also does not degrade to SRC inference, preventing a hot unload from silently weakening validation. diff --git a/docs/api-gateway.zh.md b/docs/api-gateway.zh.md index fc217ce3a9..6fa170b841 100644 --- a/docs/api-gateway.zh.md +++ b/docs/api-gateway.zh.md @@ -84,7 +84,7 @@ Client 应用只装配 `@deepseek-ai/dsh-api-remotes`。该包以运行时值导 | 共享 | `@deepseek-ai/dsh-typert-protocol` | 声明 decorator、Gateway binding、可合并协议映射、调用描述符及提供方类型;不启动 TypeScript 分析,也不注册 Cordis 服务 | | 构建 | `@deepseek-ai/dsh-typert-generator` | 从 Host `ts.Program` 严格分析 Remote 签名、类型图、lookup、Context 与源码位置,并生成 Host 和 Host-for-Client 产物 | | Host | `@deepseek-ai/dsh-typert-registry` 与 Loader | 把生成的 Host 描述符、schema 及业务包注册项放入 `ctx.typert`,并持有 lookup 与 Context 提供方 | -| Host | `@deepseek-ai/dsh-api-remotes` | 负责应用的 Agent/Session 身份策略,并配置对应的 Typert lookup | +| Host | `@deepseek-ai/dsh-api-session-controller` | 负责应用的 Agent/Session 身份策略,并配置对应的 Typert lookup | | Host | `@deepseek-ai/dsh-api-gateway` | 提供 `ctx.typertGateway`,认领 Remote endpoint,解析对象或 Context,调用实时 Cordis 服务,并校验请求值和返回值 | | Client | `@deepseek-ai/dsh-api-gateway/client` | 提供 `ctx.remote` 与 `remote.` 子服务,把生成的描述符挂成具体方法,并通过 Connection 发起、校验和取消调用 | | Client | `@deepseek-ai/dsh-api-remotes/client` | 显式选择并挂载本应用允许使用的 `/remote` 贡献,向业务代码带入对应的声明合并 | @@ -98,7 +98,7 @@ API Gateway 包同时拥有 Host dispatcher 与 Client Remote endpoint 两个对 两次 tsdown 都接收完整 workspace,且都只打包 `lib/types` 中由对应 tsc 阶段发射的 JavaScript。根配置不扫描 Client 产物、不按包名分类,也不向 tsdown 传维护式 filter;各包的本地配置根据 `DSH_BUILD_FACE` 返回当前阶段的入口。普通 Client 插件在 Client 阶段一起生成 Node loader 入口与 browser bundle。 -`api-remotes` 是唯一拆分 TypeScript face 的包特例。它的 Host project 负责 Agent/Session lookup 策略,Client project 则依赖业务包在 Host tsdown 中生成的 `/remote` 声明;根 aggregate 与直接消费方必须分别引用 `api/remotes/tsconfig.host.json` 或 `api/remotes/tsconfig.client.json`。包内 `clientBundle(..., { hostPhase: true })` 让 Host 入口在 Host tsdown 中生成,让 Client tsdown 只生成 browser 入口。其他包仍只登记在一个 aggregate 中。 +`api/remotes`、`api/gateway`、`api/session-controller` 与 `api/workspace-controller`(外加 `client/connection`)都拆分 TypeScript face。`api/remotes` 的 Client project 依赖业务包在 Host tsdown 中生成的 `/remote` 声明;根 aggregate 与直接消费方必须分别引用各拆分包自己的 `tsconfig.host.json` 或 `tsconfig.client.json`。`api-remotes` 的 `clientBundle(..., { hostPhase: true })` 让 Host 入口在 Host tsdown 中生成,让 Client tsdown 只生成 browser 入口。Agent/Session lookup 策略位于 `@deepseek-ai/dsh-api-session-controller`,而非 `api-remotes`。 每个贡献业务包把生成文件写入自己的 `lib/`,而不是源码目录: @@ -120,11 +120,11 @@ Remote Client 声明中的参数名来自 wire 字段,参数和返回类型则 Remote 与 API Proxy 共用 Connection 的 `/api` 路由。Client Remote 调用 `connection.rpc.call('/api', '/', { args }, signal)`;HTTP carrier 对应 `POST /api//`,payload 只包含一个具名 `args` 对象。 -Connection 在 HTTP bridge 之前执行 `/api` 的统一信任检查,再在共享 FetchHandler 内按 interceptor 顺序分发。Typert Gateway 只认领存在严格描述符或活跃 SRC marker 的两段式 endpoint;未认领的请求回退到既有 API Proxy。Connection 拥有传输、RPC id、响应 envelope 和请求取消,Gateway 只拥有 Remote 数据协议和业务分发。未来替换 Connection carrier 不要求改变 Remote 描述符或 Client 编程接口。 +Connection 在 HTTP bridge 之前执行 `/api` 的统一信任检查,再在共享 FetchHandler 内按 interceptor 顺序分发。Typert Gateway 只认领存在严格描述符或活跃 SRC marker 的两段式 endpoint;未认领的请求回退到既有 API Proxy。Connection 拥有传输、RPC id、响应 envelope 和请求取消,Gateway 只拥有 Remote 数据协议和业务分发。替换 Connection carrier 不要求改变 Remote 描述符或 Client 编程接口。 Gateway 每次调用都从当前注册表解析描述符和实时服务,不缓存业务对象。它要求 `args` 的字段集合与描述符完全一致,先用 codec 校验 wire 值,再通过注册的 lookup 或 Context 提供方解析对象或接收者,最后调用 binding 指向的服务方法并校验返回值。缺少提供方、identity 未命中、binding 不一致、参数缺失或多余、schema 失败和方法不存在都会在进入业务代码前或离开业务代码后失败。 -lookup 提供方的 `register()` 同时提供稳定声明和默认 resolver;`configure()` 提供由 Host 组合拥有、可异步执行且受 effect 生命周期约束的 resolver。配置可以先于提供方挂载;没有提供方时调用仍以 `lookup-unavailable` 失败,配置卸载后则恢复提供方默认策略。API Remotes 负责 `agent` 与 `session` 的标准 `agentFor()` 语义:复用 live Agent,自动恢复普通冷会话,对并发恢复去重,并拒绝由 subagent routing 拥有的 identity;`session` lookup 返回该 Agent 的 Session。Web API Proxy 提供 Agent 默认值与 scope 设置,再让旧方法使用同一个 resolver。恢复失败和 ownership fence 通过既有 RPC error 原样返回,不折叠为 Gateway 的 `internal` 错误。 +lookup 提供方的 `register()` 同时提供稳定声明和默认 resolver;`configure()` 提供由 Host 组合拥有、可异步执行且受 effect 生命周期约束的 resolver。配置可以先于提供方挂载;没有提供方时调用仍以 `lookup-unavailable` 失败,配置卸载后则恢复提供方默认策略。Session Controller 负责 `agent` 与 `session` 的标准 `agentFor()` 语义:复用 live Agent,自动恢复普通冷会话,对并发恢复去重,并拒绝由 subagent routing 拥有的 identity;`session` lookup 返回该 Agent 的 Session。Web API Proxy 提供 Agent 默认值与 scope 设置,再让旧方法使用同一个 resolver。恢复失败和 ownership fence 通过既有 RPC error 原样返回,不折叠为 Gateway 的 `internal` 错误。 Client 卸载一个贡献时会一起移除描述符和具体方法,中止其进行中的调用,并使外部仍持有的陈旧方法句柄拒绝继续调用。Host 上已经注册过的严格 endpoint 被撤回后也不会降级到 SRC 推断,以免热卸载悄然降低校验强度。 diff --git a/docs/architecture.i18n.yaml b/docs/architecture.i18n.yaml index 164230cbd6..5169a54f8b 100644 --- a/docs/architecture.i18n.yaml +++ b/docs/architecture.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/architecture.md -architecture.md: c6e01b8c30486d292694cbc26836e83522e3e760 -architecture.zh.md: 21d60d0c962097ee6853bf7a3831a2c0b727e9c9 +architecture.md: 20d03c079fa8e1f73f733992b6e938f0f539a60f +architecture.zh.md: 5448036ad6e11902e32f89d0a65be99230e27e02 diff --git a/docs/architecture.md b/docs/architecture.md index c6e01b8c30..20d03c079f 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -8,7 +8,7 @@ We recommend using an agent to explore the codebase and understand its architect ## Cordis -[Cordis](cordis-primer.md) is the framework under dsh: plugins contribute services, typed events, and reversible effects to a shared context. Every part of the product is a plugin, including the model adapter, the tool registry, the session log, and the agent loop itself, so every part is replaceable from configuration. +[Cordis](cordis-primer.md) is the framework under dsh: plugins contribute services, typed events, and reversible effects to a shared context. Every part of the product is a plugin, including the model adapter, the tool registry, the session log, and the agent loop itself, so each is replaceable from configuration. There is no privileged core to patch: you extend dsh by mounting a plugin beside the others, and registrations are effects that unwind when their plugin unloads. @@ -28,7 +28,7 @@ Layers apply to an empty entry list in this order: each bundle in the profile's Custom profiles default to live patch reload. The shipped `web` profile is live; `headless`, `sdk`, `sdk-minimal`, and `acp` apply all layers once at startup because replacing a one-shot or stdio application's dependencies after it owns work would invalidate that lifecycle. -To see the tree your machine actually boots: +To see the tree your machine boots: ```sh dsh --profile web --dump-config diff --git a/docs/architecture.zh.md b/docs/architecture.zh.md index 21d60d0c96..5448036ad6 100644 --- a/docs/architecture.zh.md +++ b/docs/architecture.zh.md @@ -8,7 +8,7 @@ ## Cordis -[Cordis](cordis-primer.zh.md) 是 dsh 底层的框架:插件向共享上下文贡献服务、类型化事件和可逆的副作用。产品的每一部分都是插件,包括模型适配器、工具注册表、会话日志,以及 agent loop(智能体循环)本身,因此每一部分都可以从配置替换。 +[Cordis](cordis-primer.zh.md) 是 dsh 底层的框架:插件向共享上下文贡献服务、类型化事件和可逆的副作用。产品的每一部分都是插件,包括模型适配器、工具注册表、会话日志,以及 agent loop(智能体循环)本身,因此每个都可以从配置替换。 不存在需要打补丁的特权内核:扩展 dsh 的方式是把插件挂载到其他插件旁边,而各项注册都是副作用,会在其插件卸载时撤销。 @@ -28,7 +28,7 @@ 自定义 profile 默认实时重载 patch。随附的 `web` profile 使用实时重载;`headless`、`sdk`、`sdk-minimal` 和 `acp` 则只在启动时应用一次所有配置层,因为一次性应用或 stdio 应用拥有工作之后,替换其依赖会破坏该生命周期。 -要查看你的机器实际启动的配置树: +要查看你的机器启动的配置树: ```sh dsh --profile web --dump-config diff --git a/docs/capability-seams.i18n.yaml b/docs/capability-seams.i18n.yaml index d7e1c70e5a..3a8354d7f4 100644 --- a/docs/capability-seams.i18n.yaml +++ b/docs/capability-seams.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/capability-seams.md -capability-seams.md: 0994b186f7daa6afbff0f1484216e55fc79170ff -capability-seams.zh.md: 48aa0a5353bba8d56f63abfcfc4cd2c601569cb6 +capability-seams.md: d86a05e240cd64a0c8a172b00fe9e1df809fe408 +capability-seams.zh.md: 1050191d696fd5a46aa28101beef118b07c29ed2 diff --git a/docs/capability-seams.md b/docs/capability-seams.md index 0994b186f7..d86a05e240 100644 --- a/docs/capability-seams.md +++ b/docs/capability-seams.md @@ -10,11 +10,13 @@ flowchart LR pkg_attachment["attachment"] svc_attachments["ctx.attachments
Durable binary attachment storage"] pkg_attachment_local["attachment-local"] - pkg_host_runtime["host-runtime"] + pkg_api_session_controller["api-session-controller"] + pkg_host_apiproxy["host-apiproxy"] + pkg_tool_fs["tool-fs"] pkg_llm_pi_ai["llm-pi-ai"] + pkg_llm_deepseek["llm-deepseek"] pkg_llm["llm"] svc_llm["ctx.llm
LLM adapter registry"] - pkg_llm_deepseek["llm-deepseek"] pkg_llm_replay["llm-replay"] pkg_agent_loop["agent-loop"] pkg_compaction_basic["compaction-basic"] @@ -32,12 +34,10 @@ flowchart LR pkg_session_persistence["session-persistence"] pkg_session_query["session-query"] pkg_session_query_sqlite["session-query-sqlite"] - pkg_subagent_inprocess["subagent-inprocess"] + pkg_subagent_in_process_driver["subagent-in-process-driver"] pkg_invariants["invariants"] pkg_message_feedback["message-feedback"] - pkg_api_session_controller["api-session-controller"] svc_sessionController["ctx.sessionController
Host Session Remote controller"] - pkg_apiproxy["apiproxy"] pkg_api_workspace_controller["api-workspace-controller"] svc_workspaceController["ctx.workspaceController
Host Workspace Remote controller"] svc_invariants["ctx.invariants
Package-owned invariant registry"] @@ -89,7 +89,6 @@ flowchart LR pkg_system_prompt["system-prompt"] svc_systemPrompt["ctx.systemPrompt
System prompt assembly registry"] pkg_tools["tools"] - pkg_tool_fs["tool-fs"] pkg_tool_terminal["tool-terminal"] pkg_tool_web["tool-web"] svc_tools["ctx.tools
Tool registry and guarded execution pipeline"] @@ -107,7 +106,6 @@ flowchart LR svc_commands["ctx.commands
Human command registry"] pkg_session_projection["session-projection"] svc_sessionProjections["ctx.sessionProjections
Session projection units"] - pkg_host_apiproxy["host-apiproxy"] pkg_session_projection_cache["session-projection-cache"] svc_sessionProjectionCache["ctx.sessionProjectionCache
Persisted projection cache"] pkg_skill["skill"] @@ -151,13 +149,13 @@ flowchart LR pkg_sandbox_policy["sandbox-policy"] svc_sandboxPolicy["ctx.sandboxPolicy
Sandbox policy home"] pkg_fs_sandbox["fs-sandbox"] - pkg_approval["approval"] + pkg_user_approval["user-approval"] svc_approval["ctx.approval
Approval seam"] pkg_permission_presets["permission-presets"] svc_permissionPresets["ctx.permissionPresets
Permission presets"] pkg_code_runtime["code-runtime"] svc_codeRuntime["ctx.codeRuntime
Code-execution seam"] - pkg_code_runtime_worker["code-runtime-worker"] + pkg_code_runtime_worker_thread["code-runtime-worker-thread"] pkg_fs["fs"] svc_fs["ctx.fs
Filesystem provider seam"] pkg_fs_local["fs-local"] @@ -171,9 +169,9 @@ flowchart LR pkg_subagent_dsh_sdk["subagent-dsh-sdk"] pkg_tool_subagent_control["tool-subagent-control"] pkg_tool_ralph["tool-ralph"] - pkg_agent_team["agent-team"] + pkg_experimental_agent_team["experimental-agent-team"] svc_agentTeams["ctx.agentTeams
Agent Teams coordination domain"] - pkg_tool_agent_team["tool-agent-team"] + pkg_experimental_tool_agent_team["experimental-tool-agent-team"] pkg_jobs["jobs"] svc_jobs["ctx.jobs
Background job registry"] pkg_jobs_local["jobs-local"] @@ -188,15 +186,15 @@ flowchart LR svc_spillStore["ctx.spillStore
Spill storage seam"] pkg_spill_local["spill-local"] pkg_spill_policy["spill-policy"] - pkg_directory_picker["directory-picker"] + pkg_host_directory_picker["host-directory-picker"] svc_directoryPicker["ctx.directoryPicker
Workspace-directory picking seam"] - pkg_directory_picker_native["directory-picker-native"] - pkg_directory_picker_browse["directory-picker-browse"] - pkg_webserver["webserver"] + pkg_host_directory_picker_native["host-directory-picker-native"] + pkg_host_directory_picker_browse["host-directory-picker-browse"] + pkg_host_webserver["host-webserver"] svc_webServer["ctx.webServer
HTTP route registration"] - pkg_connection["connection"] - pkg_modules["modules"] - pkg_hmr["hmr"] + pkg_client_connection["client-connection"] + pkg_client_modules["client-modules"] + pkg_client_hmr["client-hmr"] svc_clientModules["ctx.clientModules
Client plugin graph host"] pkg_workflow["workflow"] svc_workflowEngine["ctx.workflowEngine
Workflow script engine"] @@ -207,30 +205,26 @@ flowchart LR pkg_webhook_github["webhook-github"] pkg_lsp["lsp"] svc_lsp["ctx.lsp
Language-server navigation seam"] - pkg_lsp_local["lsp-local"] pkg_tool_lsp["tool-lsp"] svc_apiProxy["ctx.apiProxy
Host API dispatch"] pkg_cordis_host_runner["cordis-host-runner"] svc_dynamicCordisRunner["ctx.dynamicCordisRunner
Dynamic Cordis package host runner"] svc_cordisInspect["ctx.cordisInspect
Dynamic Cordis inspect registry"] - pkg_acp --> svc_approval pkg_agent --> svc_agents pkg_agent_default_model --> svc_agentDefaultModel pkg_agent_loop --> svc_agentLoop pkg_agent_presets --> svc_agentPresets - pkg_agent_team --> svc_agentTeams pkg_api_gateway --> svc_typertGateway pkg_api_session_controller --> svc_sessionController pkg_api_workspace_controller --> svc_workspaceController - pkg_apiproxy --> svc_apiProxy - pkg_approval --> svc_approval pkg_attachment --> svc_attachments pkg_attachment_local --> svc_attachments pkg_authorization --> svc_authorization pkg_bash_local --> svc_shell pkg_bash_sandbox --> svc_shell + pkg_client_modules --> svc_clientModules pkg_code_runtime --> svc_codeRuntime - pkg_code_runtime_worker --> svc_codeRuntime + pkg_code_runtime_worker_thread --> svc_codeRuntime pkg_commands --> svc_commands pkg_compaction --> svc_compaction pkg_compaction_basic --> svc_compaction @@ -240,10 +234,8 @@ flowchart LR pkg_credentials --> svc_credentials pkg_credentials_local --> svc_credentials pkg_deepseek_llm_api_extensions --> svc_deepseekLlmApiExtensions - pkg_directory_picker --> svc_directoryPicker - pkg_directory_picker_browse --> svc_directoryPicker - pkg_directory_picker_native --> svc_directoryPicker pkg_e2b --> svc_e2b + pkg_experimental_agent_team --> svc_agentTeams pkg_file_reference --> svc_fileReferences pkg_file_reference_local --> svc_fileReferences pkg_fs --> svc_fs @@ -251,6 +243,11 @@ flowchart LR pkg_fs_local --> svc_fs pkg_fs_sandbox --> svc_fs pkg_goal --> svc_goals + pkg_host_apiproxy --> svc_apiProxy + pkg_host_directory_picker --> svc_directoryPicker + pkg_host_directory_picker_browse --> svc_directoryPicker + pkg_host_directory_picker_native --> svc_directoryPicker + pkg_host_webserver --> svc_webServer pkg_invariants --> svc_invariants pkg_jobs --> svc_jobs pkg_jobs_local --> svc_jobs @@ -259,9 +256,8 @@ flowchart LR pkg_llm_pi_ai --> svc_llm pkg_llm_replay --> svc_llm pkg_lsp --> svc_lsp - pkg_lsp_local --> svc_lsp + pkg_lsp_stdio --> svc_lsp pkg_message_feedback --> svc_messageFeedback - pkg_modules --> svc_clientModules pkg_permission_presets --> svc_permissionPresets pkg_plan_mode --> svc_planMode pkg_plugin_package_inventory_deepseek --> svc_deepseekLlmApiExtensions @@ -314,6 +310,7 @@ flowchart LR pkg_tool_subagent --> svc_subagentModelSelection pkg_tools --> svc_tools pkg_typert_registry --> svc_typert + pkg_user_approval --> svc_approval pkg_user_questions --> svc_userQuestions pkg_web --> svc_web pkg_web_fetch_http --> svc_web @@ -321,32 +318,35 @@ flowchart LR pkg_web_search_exa --> svc_web pkg_web_search_perplexity --> svc_web pkg_webhook --> svc_webhookRuntime - pkg_webserver --> svc_webServer pkg_workflow --> svc_workflowEngine pkg_workflow_worker_thread --> svc_workflowEngine pkg_workspace --> svc_workspaceRegistry svc_agentDefaultModel --> pkg_headless svc_agentDefaultModel --> pkg_host_apiproxy svc_agentLoop --> pkg_agent_spine_demo - svc_agentTeams --> pkg_tool_agent_team + svc_agentTeams --> pkg_experimental_tool_agent_team svc_agents --> pkg_acp svc_agents --> pkg_agent_loop - svc_agents --> pkg_subagent_inprocess - svc_apiProxy --> pkg_connection + svc_agents --> pkg_subagent_in_process_driver + svc_apiProxy --> pkg_client_connection + svc_approval --> pkg_acp svc_approval --> pkg_tool_bash svc_approval --> pkg_tools - svc_attachments --> pkg_host_runtime + svc_attachments --> pkg_api_session_controller + svc_attachments --> pkg_host_apiproxy + svc_attachments --> pkg_llm_deepseek svc_attachments --> pkg_llm_pi_ai + svc_attachments --> pkg_tool_fs svc_authorization --> pkg_llm_pi_ai - svc_clientModules --> pkg_hmr + svc_clientModules --> pkg_client_hmr svc_codeRuntime --> pkg_tools svc_compaction --> pkg_compaction_basic svc_cordisInspect --> pkg_tool_cordis - svc_credentials --> pkg_apiproxy + svc_credentials --> pkg_host_apiproxy svc_credentials --> pkg_llm_deepseek svc_credentials --> pkg_llm_pi_ai svc_deepseekLlmApiExtensions --> pkg_llm_deepseek - svc_directoryPicker --> pkg_apiproxy + svc_directoryPicker --> pkg_host_apiproxy svc_dynamicCordisRunner --> pkg_tool_cordis svc_e2b --> pkg_fs_e2b svc_e2b --> pkg_subprocess_e2b @@ -367,7 +367,7 @@ flowchart LR svc_sandboxPolicy --> pkg_bash_sandbox svc_sandboxPolicy --> pkg_fs_sandbox svc_sandboxPolicy --> pkg_terminal_bash - svc_sessionController --> pkg_apiproxy + svc_sessionController --> pkg_host_apiproxy svc_sessionPersistence --> pkg_agent_loop svc_sessionPersistence --> pkg_hooks_claude_code svc_sessionPersistence --> pkg_hooks_codex @@ -388,8 +388,8 @@ flowchart LR svc_sessions --> pkg_session_persistence svc_sessions --> pkg_session_query svc_sessions --> pkg_session_query_sqlite - svc_sessions --> pkg_subagent_inprocess - svc_settings --> pkg_apiproxy + svc_sessions --> pkg_subagent_in_process_driver + svc_settings --> pkg_host_apiproxy svc_settings --> pkg_llm_deepseek svc_settings --> pkg_llm_pi_ai svc_shell --> pkg_hooks_claude_code @@ -436,39 +436,40 @@ flowchart LR svc_typert --> pkg_typert_loader svc_userQuestions --> pkg_tool_ask_user svc_web --> pkg_tool_web - svc_webServer --> pkg_connection - svc_webServer --> pkg_hmr - svc_webServer --> pkg_modules + svc_webServer --> pkg_client_connection + svc_webServer --> pkg_client_hmr + svc_webServer --> pkg_client_modules svc_webhookRuntime --> pkg_webhook_github svc_workflowEngine --> pkg_tool_ralph svc_workflowEngine --> pkg_tool_workflow - svc_workspaceRegistry --> pkg_apiproxy + svc_workspaceRegistry --> pkg_api_session_controller + svc_workspaceRegistry --> pkg_api_workspace_controller svc_fs -. event gate .-> pkg_fs_observation_policy ``` | ctx key | Role | Owner | Implementations | Direct consumers | Companion plugins | Note | | --- | --- | --- | --- | --- | --- | --- | -| `ctx.attachments` | `seam` | [`attachment`](../packages/attachment/attachment) | [`attachment-local`](../packages/attachment/attachment-local) | `host-runtime`, [`llm-pi-ai`](../packages/llm/llm-pi-ai) | - | The host commits accepted images before session events; provider adapters resolve authorized durable references into provider-native content. | +| `ctx.attachments` | `seam` | [`attachment`](../packages/attachment/attachment) | [`attachment-local`](../packages/attachment/attachment-local) | [`api-session-controller`](../packages/api/session-controller), [`host-apiproxy`](../packages/host/apiproxy), [`tool-fs`](../packages/fs/tool-fs), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`llm-deepseek`](../packages/llm/llm-deepseek) | - | The host commits accepted images before session events; provider adapters resolve authorized durable references into provider-native content. | | `ctx.llm` | `seam` | [`llm`](../packages/llm/llm) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`llm-replay`](../packages/test-support/llm-replay) | [`agent-loop`](../packages/core/agent-loop), [`compaction-basic`](../packages/compaction/compaction-basic) | - | Adapters register provider implementations; the loop and compaction call the provider-neutral stream service. | | `ctx.deepseekLlmApiExtensions` | `seam` | [`deepseek-llm-api-extensions`](../packages/llm/deepseek-llm-api-extensions) | [`session-log-deepseek`](../packages/session/session-log-deepseek), [`plugin-package-inventory-deepseek`](../packages/llm/plugin-package-inventory-deepseek) | [`llm-deepseek`](../packages/llm/llm-deepseek) | - | Plugins prepare independent top-level fields; the official adapter merges them and commits their delivery state after HTTP acceptance. | | `ctx.tokenMeter` | `core` | [`token-meter`](../packages/llm/token-meter) | - | [`compaction-basic`](../packages/compaction/compaction-basic) | - | Owns isolated per-session replay folds; pressure consumers share immutable revisioned measurements. | | `ctx.toolResultPruner` | `core` | [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner) | - | [`compaction-basic`](../packages/compaction/compaction-basic) | - | Rewrites oversized current tool results through replayable single-node surface replacements before summary compaction. | -| `ctx.sessions` | `core` | [`session`](../packages/core/session) | - | [`agent-loop`](../packages/core/agent-loop), [`agent`](../packages/core/agent), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), `subagent-inprocess`, [`invariants`](../packages/runtime-diagnostics/invariants), [`message-feedback`](../packages/feedback/message-feedback) | - | Owns append-only Session instances and emits the durable session event feed. | -| `ctx.sessionController` | `core` | [`api-session-controller`](../packages/api/session-controller) | - | `apiproxy` | - | Owns Session commands, cold reads, durable-event following, live control state, and Agent activation policy; apiProxy reuses its inspection and Agent-resolution operations for Session-aware domains. | +| `ctx.sessions` | `core` | [`session`](../packages/core/session) | - | [`agent-loop`](../packages/core/agent-loop), [`agent`](../packages/core/agent), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver), [`invariants`](../packages/runtime-diagnostics/invariants), [`message-feedback`](../packages/feedback/message-feedback) | - | Owns append-only Session instances and emits the durable session event feed. | +| `ctx.sessionController` | `core` | [`api-session-controller`](../packages/api/session-controller) | - | [`host-apiproxy`](../packages/host/apiproxy) | - | Owns Session commands, cold reads, durable-event following, live control state, and Agent activation policy; apiProxy reuses its inspection and Agent-resolution operations for Session-aware domains. | | `ctx.workspaceController` | `core` | [`api-workspace-controller`](../packages/api/workspace-controller) | - | - | - | Owns Workspace commands and reconnect-safe Workspace state delivery through the generated Remote namespace. | | `ctx.invariants` | `core` | [`invariants`](../packages/runtime-diagnostics/invariants) | - | [`session`](../packages/core/session), [`agent`](../packages/core/agent), [`scope`](../packages/core/scope), [`agent-loop`](../packages/core/agent-loop) | - | Companion subpaths register owner-local checks; the service owns selection, uniqueness, child fibers, and package-attributed failures. | | `ctx.typert` | `core` | [`typert-registry`](../packages/typert/registry) | - | [`typert-loader`](../packages/typert/loader), [`api-gateway`](../packages/api/gateway) | - | Plugins register live zod contributions directly or through dsh-typert-loader; the API gateway consumes invocation descriptors and providers, while other runtime consumers query schemas and reflection metadata at their own edges. | | `ctx.typertGateway` | `core` | [`api-gateway`](../packages/api/gateway) | - | - | - | Associates generated Remote descriptors with live Cordis services, resolves registered identities, and exposes unary calls through the shared Connection RPC carrier. | | `ctx.sessionPersistence` | `seam` | [`session-persistence`](../packages/session/session-persistence) | [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-persistence-sqlite`](../packages/session/session-persistence-sqlite) | [`agent-loop`](../packages/core/agent-loop), [`tool-bash`](../packages/shell/tool-bash), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), [`message-feedback`](../packages/feedback/message-feedback) | - | Backends persist the same SessionEvent vocabulary; apps choose a backend at composition time. | -| `ctx.settings` | `seam` | [`settings`](../packages/settings/settings) | [`settings-file`](../packages/settings/settings-file) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), `apiproxy` | - | Plugins register namespace schemas and resolve layered values; providers store the raw document. The LLM adapters register their entry config as the composition base under the user section; the web gateway serves redacted layered descriptors and writes the user layer. | +| `ctx.settings` | `seam` | [`settings`](../packages/settings/settings) | [`settings-file`](../packages/settings/settings-file) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`host-apiproxy`](../packages/host/apiproxy) | - | Plugins register namespace schemas and resolve layered values; providers store the raw document. The LLM adapters register their entry config as the composition base under the user section; the web gateway serves redacted layered descriptors and writes the user layer. | | `ctx.subagentModelSelection` | `core` | [`tool-subagent`](../packages/subagent/tool-subagent) | - | [`tool-subagent`](../packages/subagent/tool-subagent) | - | Owns the default-off settings namespace that Agent-scoped delegation tools sample when composing a new top-level Session. | -| `ctx.credentials` | `seam` | [`credentials`](../packages/credentials/credentials) | [`credentials-local`](../packages/credentials/credentials-local) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), `apiproxy` | - | Configuration carries references to secrets; providers own the values. Consumers resolve per operation, so a rotated credential reaches the very next request; the web gateway exposes value-free views and write-only storage. | +| `ctx.credentials` | `seam` | [`credentials`](../packages/credentials/credentials) | [`credentials-local`](../packages/credentials/credentials-local) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`host-apiproxy`](../packages/host/apiproxy) | - | Configuration carries references to secrets; providers own the values. Consumers resolve per operation, so a rotated credential reaches the very next request; the web gateway exposes value-free views and write-only storage. | | `ctx.authorization` | `seam` | [`authorization`](../packages/credentials/authorization) | - | [`llm-pi-ai`](../packages/llm/llm-pi-ai) | - | Flows are registered by the plugin that knows how to obtain one credential and keyed by the record they write; the seam owns the conversation and the one-attempt-per-key lifecycle, never the protocol. | | `ctx.sessionTelemetry` | `seam` | [`session-telemetry`](../packages/session/session-telemetry) | [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | - | - | The seam captures, redacts, and hands session records to one backend; nothing else consumes the service — its output leaves the process. | | `ctx.storage` | `seam` | [`storage`](../packages/storage/storage) | [`storage-json`](../packages/storage/storage-json), [`storage-sqlite`](../packages/storage/storage-sqlite) | [`storage-domain`](../packages/storage/storage-domain) | - | Backends register side by side under names; data forms (domain first) mount on the hub and translate typed operations into opaque KV-unit primitives. | | `ctx.storageDomain` | `core` | [`storage-domain`](../packages/storage/storage-domain) | - | [`workspace`](../packages/workspace/workspace), [`message-feedback`](../packages/feedback/message-feedback) | - | Waits for every configured backend, then publishes the domain form as one lifecycle-bound service for typed durable state. | | `ctx.messageFeedback` | `core` | [`message-feedback`](../packages/feedback/message-feedback) | - | - | - | Owns local per-assistant-message feedback, lifecycle and target validation, per-item compare-and-set, and the Host unary Remote contract without entering Session history or telemetry. | -| `ctx.workspaceRegistry` | `core` | [`workspace`](../packages/workspace/workspace) | - | `apiproxy` | - | Owns WorkspaceId-branded records over the domain facility; stable sessionIds accounts drive Host RPC and GUI projections. | +| `ctx.workspaceRegistry` | `core` | [`workspace`](../packages/workspace/workspace) | - | [`api-workspace-controller`](../packages/api/workspace-controller), [`api-session-controller`](../packages/api/session-controller) | - | Owns WorkspaceId-branded records over the domain facility; stable sessionIds accounts drive Host RPC and GUI projections. | | `ctx.sessionQuery` | `seam` | [`session-query`](../packages/session-query/session-query) | [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | [`session-reference`](../packages/context/session-reference), [`tool-session-query`](../packages/session-query/tool-session-query) | - | The interface supplies exact reads, filters, and traces; its concrete backend adds full-text reconciliation, ranking, snippets, and cursor generations, while the model consumer owns workspace authority and cursor-free rendering. | | `ctx.fileReferences` | `seam` | [`file-reference`](../packages/context/file-reference) | [`file-reference-local`](../packages/context/file-reference-local) | - | - | The interface returns path-only completion candidates within the addressed Agent cwd through its unary Remote contract; providers own namespace access and ranking without reading file contents. | | `ctx.sessionReferenceResolver` | `core` | [`session-reference`](../packages/context/session-reference) | - | - | - | Projects bounded current-surface conversation snapshots into durable untrusted message context; host adapters own mention syntax. | @@ -482,7 +483,7 @@ flowchart LR | `ctx.sessionProjections` | `core` | [`session-projection`](../packages/session/session-projection) | - | [`tool-todo`](../packages/todo/tool-todo), [`session-title`](../packages/session/session-title), [`host-apiproxy`](../packages/host/apiproxy) | - | Domains register state-driven fold units; the eager drive keeps per-session watermark states and api-proxy serves baselines and pushes changed values. | | `ctx.sessionProjectionCache` | `core` | [`session-projection-cache`](../packages/session/session-projection-cache) | - | [`host-apiproxy`](../packages/host/apiproxy) | - | Durably checkpoints projection unit states per session (throttled + turn/end/detach mandatory points) and serves the cold-read ladder: cache row + persistence tail replay, so listings never load full logs. | | `ctx.skills` | `seam` | [`skill`](../packages/skill/skill) | [`skill-badge`](../packages/skill/skill-badge), [`skill-filesystem`](../packages/skill/skill-filesystem) | [`tool-skill`](../packages/skill/tool-skill) | - | Merges provider skill catalogs; tool-skill renders the session-prefix catalog and loads complete skill bodies. | -| `ctx.agents` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/acp/acp), `subagent-inprocess` | - | Owns live Agent handles, the create/resume factory seam, and process-local initiator propagation. | +| `ctx.agents` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/acp/acp), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) | - | Owns live Agent handles, the create/resume factory seam, and process-local initiator propagation. | | `ctx.agentDefaultModel` | `core` | [`agent-default-model`](../packages/core/agent-default-model) | - | [`headless`](../packages/bundle/headless), [`host-apiproxy`](../packages/host/apiproxy) | - | Layers the default ModelSelection through settings so direct and Host-backed Agent entry points share one state owner. | | `ctx.agentLoop` | `bundle` | [`agent-loop`](../packages/core/agent-loop) | - | [`agent-spine-demo`](../packages/examples/agent-spine-demo) | - | The one concrete loop plugin; extension packages depend on dsh-agent events and services, not on this package. | | `ctx.goals` | `core` | [`goal`](../packages/goal/goal) | - | - | - | Folds revisioned objective state from the session log and keeps live continuation activation process-local. | @@ -493,23 +494,23 @@ flowchart LR | `ctx.terminals` | `seam` | [`terminal`](../packages/terminal/terminal) | [`terminal-bash`](../packages/terminal/terminal-bash) | [`tool-terminal`](../packages/terminal/tool-terminal) | - | The registry owns exact-Agent session identity and cleanup; backends own terminal mechanics, while tool-terminal exposes the owner-scoped model tools. | | `ctx.sandbox` | `seam` | [`sandbox`](../packages/sandbox/sandbox) | [`sandbox-local`](../packages/sandbox/sandbox-local) | [`bash-sandbox`](../packages/shell/bash-sandbox), [`terminal-bash`](../packages/terminal/terminal-bash) | - | Consumers hand over the exact argv they are about to spawn; same-world backends wrap it under a per-call policy and report enforcement. | | `ctx.sandboxPolicy` | `core` | [`sandbox-policy`](../packages/sandbox/sandbox-policy) | - | [`bash-sandbox`](../packages/shell/bash-sandbox), [`fs-sandbox`](../packages/fs/fs-sandbox), [`terminal-bash`](../packages/terminal/terminal-bash) | - | The one home for the deployment default mode + workspace root; only the sandboxed executor and provider read the service (the tool layers use the pure `sandbox/mode` fold it also exports). Both enforcing families read it so bash and fs cannot confine to different roots. | -| `ctx.approval` | `seam` | `approval` | [`acp`](../packages/acp/acp) | [`tools`](../packages/core/tools), [`tool-bash`](../packages/shell/tool-bash) | - | One-shot permission decisions dispatched over the `approval/request` waterfall; answerers are listeners (the ACP bridge for its own agents), absence fails closed to `unavailable`. | +| `ctx.approval` | `seam` | [`user-approval`](../packages/interaction/user-approval) | - | [`tools`](../packages/core/tools), [`tool-bash`](../packages/shell/tool-bash), [`acp`](../packages/acp/acp) | - | One-shot permission decisions dispatched over the `approval/request` waterfall; answerers are listeners (the ACP bridge for its own agents), absence fails closed to `unavailable`. | | `ctx.permissionPresets` | `core` | [`permission-presets`](../packages/interaction/permission-presets) | - | - | - | User-facing preset table (`workspace-write`/`danger-full-access`) bundling the sandbox-mode and approval-policy knobs; a switch writes one `permission/preset` event through to both knob events. | -| `ctx.codeRuntime` | `seam` | [`code-runtime`](../packages/code-runtime/code-runtime) | `code-runtime-worker` | [`tools`](../packages/core/tools) | - | Runs one model-written program against host-provided async bindings; backends differ by substrate and language (the tool registry consumes it for Code Mode). | +| `ctx.codeRuntime` | `seam` | [`code-runtime`](../packages/code-runtime/code-runtime) | [`code-runtime-worker-thread`](../packages/code-runtime/code-runtime-worker-thread) | [`tools`](../packages/core/tools) | - | Runs one model-written program against host-provided async bindings; backends differ by substrate and language (the tool registry consumes it for Code Mode). | | `ctx.fs` | `seam` | [`fs`](../packages/fs/fs) | [`fs-local`](../packages/fs/fs-local), [`fs-sandbox`](../packages/fs/fs-sandbox), [`fs-e2b`](../packages/e2b/fs-e2b) | [`tool-fs`](../packages/fs/tool-fs) | [`fs-observation-policy`](../packages/fs/fs-observation-policy) | tool-fs executes read/write/edit through ctx.fs; fs-sandbox fences mutations by the shared sandbox mode; fs-observation-policy contributes observed-state checks through the fs/* event gate. | | `ctx.compaction` | `seam` | [`compaction`](../packages/compaction/compaction) | [`compaction-basic`](../packages/compaction/compaction-basic) | [`compaction-basic`](../packages/compaction/compaction-basic) | - | The basic backend consumes post-step pressure and request-error recovery events; there is no model-facing compact tool. | | `ctx.subagents` | `seam` | [`subagent`](../packages/subagent/subagent) | [`subagent-spawn-in-process`](../packages/subagent/subagent-spawn-in-process), [`subagent-fork-in-process`](../packages/subagent/subagent-fork-in-process), [`subagent-acp`](../packages/subagent/subagent-acp), [`subagent-codex`](../packages/subagent/subagent-codex), [`subagent-claude-code`](../packages/subagent/subagent-claude-code), [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-subagent-control`](../packages/subagent/tool-subagent-control), [`tool-ralph`](../packages/workflow/tool-ralph) | - | Providers implement transports; the service also owns optional Activation-based continuation orchestration, tool-subagent selects one-shot or continuable delegation, tool-subagent-control delivers follow-ups, and tool-ralph requires one fresh structured-output route. | -| `ctx.agentTeams` | `core` | `agent-team` | - | `tool-agent-team` | - | Owns the implicit-root roster, durable peer mailbox, shared task DAG, and continuable-child lifecycle; tool-agent-team contributes the scoped model policy and controls. | +| `ctx.agentTeams` | `core` | [`experimental-agent-team`](../packages/experimental/agent-team) | - | [`experimental-tool-agent-team`](../packages/experimental/tool-agent-team) | - | Owns the implicit-root roster, durable peer mailbox, shared task DAG, and continuable-child lifecycle; tool-agent-team contributes the scoped model policy and controls. | | `ctx.jobs` | `seam` | [`jobs`](../packages/jobs/jobs) | [`jobs-local`](../packages/jobs/jobs-local) | [`tool-bash`](../packages/shell/tool-bash), [`tool-terminal`](../packages/terminal/tool-terminal), [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-jobs`](../packages/jobs/tool-jobs) | - | Producers (background bash, PTY sends, and subagent delegations) register running work; tool-jobs is the model-facing controller that reads, lists, and kills it; jobs-local is the process-local registry. | | `ctx.web` | `seam` | [`web`](../packages/web/web) | [`web-search-exa`](../packages/web/web-search-exa), [`web-search-perplexity`](../packages/web/web-search-perplexity), [`web-search-deepseek`](../packages/web/web-search-deepseek), [`web-fetch-http`](../packages/web/web-fetch-http) | [`tool-web`](../packages/web/tool-web) | - | Search and fetch providers register into one ctx.web seam; tool-web owns the stable model-facing names. | | `ctx.spillStore` | `seam` | [`spill`](../packages/spill/spill) | [`spill-local`](../packages/spill/spill-local) | [`spill-policy`](../packages/spill/spill-policy) | - | The backend saves oversized tool text and returns a model-facing locator plus retrieval hint; spill-policy is the tools/post-execute consumer that decides when to spill. | -| `ctx.directoryPicker` | `seam` | `directory-picker` | `directory-picker-native`, `directory-picker-browse` | `apiproxy` | - | Discriminated interaction capability: the native backend opens one OS chooser on the host display, the browse backend serves listing/creation primitives for the in-app browser; dual-face backends fill ui-workspace directory-flow slots from their browser halves (no wire advertisement). | -| `ctx.webServer` | `core` | `webserver` | - | `connection`, `modules`, `hmr` | - | Plain node:http carrier: named-route registry, index transform taps, and the static dist fallback; web-transport plugins register their own routes. | -| `ctx.clientModules` | `core` | `modules` | - | `hmr` | - | Composes the __DSH_BOOT__ entry graph from an incremental dsh.client scan, serves plugin bundles, and notifies rebuilt/graph-changed subscribers. | +| `ctx.directoryPicker` | `seam` | [`host-directory-picker`](../packages/host/directory-picker) | [`host-directory-picker-native`](../packages/host/directory-picker-native), [`host-directory-picker-browse`](../packages/host/directory-picker-browse) | [`host-apiproxy`](../packages/host/apiproxy) | - | Discriminated interaction capability: the native backend opens one OS chooser on the host display, the browse backend serves listing/creation primitives for the in-app browser; dual-face backends fill ui-workspace directory-flow slots from their browser halves (no wire advertisement). | +| `ctx.webServer` | `core` | [`host-webserver`](../packages/host/webserver) | - | [`client-connection`](../packages/client/connection), [`client-modules`](../packages/client/modules), [`client-hmr`](../packages/client/hmr) | - | Plain node:http carrier: named-route registry, index transform taps, and the static dist fallback; web-transport plugins register their own routes. | +| `ctx.clientModules` | `core` | [`client-modules`](../packages/client/modules) | - | [`client-hmr`](../packages/client/hmr) | - | Composes the __DSH_BOOT__ entry graph from an incremental dsh.client scan, serves plugin bundles, and notifies rebuilt/graph-changed subscribers. | | `ctx.workflowEngine` | `seam` | [`workflow`](../packages/workflow/workflow) | [`workflow-worker-thread`](../packages/workflow/workflow-worker-thread) | [`tool-workflow`](../packages/workflow/tool-workflow), [`tool-ralph`](../packages/workflow/tool-ralph) | - | One engine per context, as in bash, with no named-provider registry; the general workflow and fixed Ralph consumers start runs whose agent() calls fan out through ctx.subagents. | | `ctx.webhookRuntime` | `core` | [`webhook`](../packages/webhook/webhook) | - | [`webhook-github`](../packages/webhook/webhook-github) | - | Provider adapters dispatch authenticated deliveries; trusted plugins register independent process-local rules, and the runtime turns non-null results into ordinary Workspace-backed Sessions without delivery or completion state. | -| `ctx.lsp` | `seam` | [`lsp`](../packages/lsp/lsp) | `lsp-local` | [`tool-lsp`](../packages/lsp/tool-lsp) | - | Provider registration and selection plus normalized query execution over exactly four operations; the seam offers no protocol escape hatch, so a backend translates into the normalized request and result. | -| `ctx.apiProxy` | `core` | `apiproxy` | - | `connection` | - | The transport-agnostic host gateway face: it dispatches browser API calls, and each open host stream subscribes to the events it forwards rather than being pushed to through a broadcast verb. | +| `ctx.lsp` | `seam` | [`lsp`](../packages/lsp/lsp) | [`lsp-stdio`](../packages/lsp/lsp-stdio) | [`tool-lsp`](../packages/lsp/tool-lsp) | - | Provider registration and selection plus normalized query execution over exactly four operations; the seam offers no protocol escape hatch, so a backend translates into the normalized request and result. | +| `ctx.apiProxy` | `core` | [`host-apiproxy`](../packages/host/apiproxy) | - | [`client-connection`](../packages/client/connection) | - | The transport-agnostic host gateway face: it dispatches browser API calls, and each open host stream subscribes to the events it forwards rather than being pushed to through a broadcast verb. | | `ctx.dynamicCordisRunner` | `core` | [`cordis-host-runner`](../packages/extensions/cordis-host-runner) | - | [`tool-cordis`](../packages/extensions/tool-cordis) | - | Owns the in-memory definition registry, the vm sandbox for host halves, and the request-run round trip; browser pages reach the same service over the wire through its remote namespace. | | `ctx.cordisInspect` | `core` | [`cordis-host-runner`](../packages/extensions/cordis-host-runner) | - | [`tool-cordis`](../packages/extensions/tool-cordis) | - | Registers host inspect providers, mirrors the client provider manifest, and routes client queries through the dynamic Cordis transport. | diff --git a/docs/capability-seams.zh.md b/docs/capability-seams.zh.md index 48aa0a5353..1050191d69 100644 --- a/docs/capability-seams.zh.md +++ b/docs/capability-seams.zh.md @@ -12,11 +12,13 @@ flowchart LR pkg_attachment["attachment"] svc_attachments["ctx.attachments
Durable binary attachment storage"] pkg_attachment_local["attachment-local"] - pkg_host_runtime["host-runtime"] + pkg_api_session_controller["api-session-controller"] + pkg_host_apiproxy["host-apiproxy"] + pkg_tool_fs["tool-fs"] pkg_llm_pi_ai["llm-pi-ai"] + pkg_llm_deepseek["llm-deepseek"] pkg_llm["llm"] svc_llm["ctx.llm
LLM adapter registry"] - pkg_llm_deepseek["llm-deepseek"] pkg_llm_replay["llm-replay"] pkg_agent_loop["agent-loop"] pkg_compaction_basic["compaction-basic"] @@ -34,12 +36,10 @@ flowchart LR pkg_session_persistence["session-persistence"] pkg_session_query["session-query"] pkg_session_query_sqlite["session-query-sqlite"] - pkg_subagent_inprocess["subagent-inprocess"] + pkg_subagent_in_process_driver["subagent-in-process-driver"] pkg_invariants["invariants"] pkg_message_feedback["message-feedback"] - pkg_api_session_controller["api-session-controller"] svc_sessionController["ctx.sessionController
Host Session Remote controller"] - pkg_apiproxy["apiproxy"] pkg_api_workspace_controller["api-workspace-controller"] svc_workspaceController["ctx.workspaceController
Host Workspace Remote controller"] svc_invariants["ctx.invariants
Package-owned invariant registry"] @@ -91,7 +91,6 @@ flowchart LR pkg_system_prompt["system-prompt"] svc_systemPrompt["ctx.systemPrompt
System prompt assembly registry"] pkg_tools["tools"] - pkg_tool_fs["tool-fs"] pkg_tool_terminal["tool-terminal"] pkg_tool_web["tool-web"] svc_tools["ctx.tools
Tool registry and guarded execution pipeline"] @@ -109,7 +108,6 @@ flowchart LR svc_commands["ctx.commands
Human command registry"] pkg_session_projection["session-projection"] svc_sessionProjections["ctx.sessionProjections
Session projection units"] - pkg_host_apiproxy["host-apiproxy"] pkg_session_projection_cache["session-projection-cache"] svc_sessionProjectionCache["ctx.sessionProjectionCache
Persisted projection cache"] pkg_skill["skill"] @@ -153,13 +151,13 @@ flowchart LR pkg_sandbox_policy["sandbox-policy"] svc_sandboxPolicy["ctx.sandboxPolicy
Sandbox policy home"] pkg_fs_sandbox["fs-sandbox"] - pkg_approval["approval"] + pkg_user_approval["user-approval"] svc_approval["ctx.approval
Approval seam"] pkg_permission_presets["permission-presets"] svc_permissionPresets["ctx.permissionPresets
Permission presets"] pkg_code_runtime["code-runtime"] svc_codeRuntime["ctx.codeRuntime
Code-execution seam"] - pkg_code_runtime_worker["code-runtime-worker"] + pkg_code_runtime_worker_thread["code-runtime-worker-thread"] pkg_fs["fs"] svc_fs["ctx.fs
Filesystem provider seam"] pkg_fs_local["fs-local"] @@ -173,9 +171,9 @@ flowchart LR pkg_subagent_dsh_sdk["subagent-dsh-sdk"] pkg_tool_subagent_control["tool-subagent-control"] pkg_tool_ralph["tool-ralph"] - pkg_agent_team["agent-team"] + pkg_experimental_agent_team["experimental-agent-team"] svc_agentTeams["ctx.agentTeams
Agent Teams coordination domain"] - pkg_tool_agent_team["tool-agent-team"] + pkg_experimental_tool_agent_team["experimental-tool-agent-team"] pkg_jobs["jobs"] svc_jobs["ctx.jobs
Background job registry"] pkg_jobs_local["jobs-local"] @@ -190,15 +188,15 @@ flowchart LR svc_spillStore["ctx.spillStore
Spill storage seam"] pkg_spill_local["spill-local"] pkg_spill_policy["spill-policy"] - pkg_directory_picker["directory-picker"] + pkg_host_directory_picker["host-directory-picker"] svc_directoryPicker["ctx.directoryPicker
Workspace-directory picking seam"] - pkg_directory_picker_native["directory-picker-native"] - pkg_directory_picker_browse["directory-picker-browse"] - pkg_webserver["webserver"] + pkg_host_directory_picker_native["host-directory-picker-native"] + pkg_host_directory_picker_browse["host-directory-picker-browse"] + pkg_host_webserver["host-webserver"] svc_webServer["ctx.webServer
HTTP route registration"] - pkg_connection["connection"] - pkg_modules["modules"] - pkg_hmr["hmr"] + pkg_client_connection["client-connection"] + pkg_client_modules["client-modules"] + pkg_client_hmr["client-hmr"] svc_clientModules["ctx.clientModules
Client plugin graph host"] pkg_workflow["workflow"] svc_workflowEngine["ctx.workflowEngine
Workflow script engine"] @@ -209,30 +207,26 @@ flowchart LR pkg_webhook_github["webhook-github"] pkg_lsp["lsp"] svc_lsp["ctx.lsp
Language-server navigation seam"] - pkg_lsp_local["lsp-local"] pkg_tool_lsp["tool-lsp"] svc_apiProxy["ctx.apiProxy
Host API dispatch"] pkg_cordis_host_runner["cordis-host-runner"] svc_dynamicCordisRunner["ctx.dynamicCordisRunner
Dynamic Cordis package host runner"] svc_cordisInspect["ctx.cordisInspect
Dynamic Cordis inspect registry"] - pkg_acp --> svc_approval pkg_agent --> svc_agents pkg_agent_default_model --> svc_agentDefaultModel pkg_agent_loop --> svc_agentLoop pkg_agent_presets --> svc_agentPresets - pkg_agent_team --> svc_agentTeams pkg_api_gateway --> svc_typertGateway pkg_api_session_controller --> svc_sessionController pkg_api_workspace_controller --> svc_workspaceController - pkg_apiproxy --> svc_apiProxy - pkg_approval --> svc_approval pkg_attachment --> svc_attachments pkg_attachment_local --> svc_attachments pkg_authorization --> svc_authorization pkg_bash_local --> svc_shell pkg_bash_sandbox --> svc_shell + pkg_client_modules --> svc_clientModules pkg_code_runtime --> svc_codeRuntime - pkg_code_runtime_worker --> svc_codeRuntime + pkg_code_runtime_worker_thread --> svc_codeRuntime pkg_commands --> svc_commands pkg_compaction --> svc_compaction pkg_compaction_basic --> svc_compaction @@ -242,10 +236,8 @@ flowchart LR pkg_credentials --> svc_credentials pkg_credentials_local --> svc_credentials pkg_deepseek_llm_api_extensions --> svc_deepseekLlmApiExtensions - pkg_directory_picker --> svc_directoryPicker - pkg_directory_picker_browse --> svc_directoryPicker - pkg_directory_picker_native --> svc_directoryPicker pkg_e2b --> svc_e2b + pkg_experimental_agent_team --> svc_agentTeams pkg_file_reference --> svc_fileReferences pkg_file_reference_local --> svc_fileReferences pkg_fs --> svc_fs @@ -253,6 +245,11 @@ flowchart LR pkg_fs_local --> svc_fs pkg_fs_sandbox --> svc_fs pkg_goal --> svc_goals + pkg_host_apiproxy --> svc_apiProxy + pkg_host_directory_picker --> svc_directoryPicker + pkg_host_directory_picker_browse --> svc_directoryPicker + pkg_host_directory_picker_native --> svc_directoryPicker + pkg_host_webserver --> svc_webServer pkg_invariants --> svc_invariants pkg_jobs --> svc_jobs pkg_jobs_local --> svc_jobs @@ -261,9 +258,8 @@ flowchart LR pkg_llm_pi_ai --> svc_llm pkg_llm_replay --> svc_llm pkg_lsp --> svc_lsp - pkg_lsp_local --> svc_lsp + pkg_lsp_stdio --> svc_lsp pkg_message_feedback --> svc_messageFeedback - pkg_modules --> svc_clientModules pkg_permission_presets --> svc_permissionPresets pkg_plan_mode --> svc_planMode pkg_plugin_package_inventory_deepseek --> svc_deepseekLlmApiExtensions @@ -316,6 +312,7 @@ flowchart LR pkg_tool_subagent --> svc_subagentModelSelection pkg_tools --> svc_tools pkg_typert_registry --> svc_typert + pkg_user_approval --> svc_approval pkg_user_questions --> svc_userQuestions pkg_web --> svc_web pkg_web_fetch_http --> svc_web @@ -323,32 +320,35 @@ flowchart LR pkg_web_search_exa --> svc_web pkg_web_search_perplexity --> svc_web pkg_webhook --> svc_webhookRuntime - pkg_webserver --> svc_webServer pkg_workflow --> svc_workflowEngine pkg_workflow_worker_thread --> svc_workflowEngine pkg_workspace --> svc_workspaceRegistry svc_agentDefaultModel --> pkg_headless svc_agentDefaultModel --> pkg_host_apiproxy svc_agentLoop --> pkg_agent_spine_demo - svc_agentTeams --> pkg_tool_agent_team + svc_agentTeams --> pkg_experimental_tool_agent_team svc_agents --> pkg_acp svc_agents --> pkg_agent_loop - svc_agents --> pkg_subagent_inprocess - svc_apiProxy --> pkg_connection + svc_agents --> pkg_subagent_in_process_driver + svc_apiProxy --> pkg_client_connection + svc_approval --> pkg_acp svc_approval --> pkg_tool_bash svc_approval --> pkg_tools - svc_attachments --> pkg_host_runtime + svc_attachments --> pkg_api_session_controller + svc_attachments --> pkg_host_apiproxy + svc_attachments --> pkg_llm_deepseek svc_attachments --> pkg_llm_pi_ai + svc_attachments --> pkg_tool_fs svc_authorization --> pkg_llm_pi_ai - svc_clientModules --> pkg_hmr + svc_clientModules --> pkg_client_hmr svc_codeRuntime --> pkg_tools svc_compaction --> pkg_compaction_basic svc_cordisInspect --> pkg_tool_cordis - svc_credentials --> pkg_apiproxy + svc_credentials --> pkg_host_apiproxy svc_credentials --> pkg_llm_deepseek svc_credentials --> pkg_llm_pi_ai svc_deepseekLlmApiExtensions --> pkg_llm_deepseek - svc_directoryPicker --> pkg_apiproxy + svc_directoryPicker --> pkg_host_apiproxy svc_dynamicCordisRunner --> pkg_tool_cordis svc_e2b --> pkg_fs_e2b svc_e2b --> pkg_subprocess_e2b @@ -369,7 +369,7 @@ flowchart LR svc_sandboxPolicy --> pkg_bash_sandbox svc_sandboxPolicy --> pkg_fs_sandbox svc_sandboxPolicy --> pkg_terminal_bash - svc_sessionController --> pkg_apiproxy + svc_sessionController --> pkg_host_apiproxy svc_sessionPersistence --> pkg_agent_loop svc_sessionPersistence --> pkg_hooks_claude_code svc_sessionPersistence --> pkg_hooks_codex @@ -390,8 +390,8 @@ flowchart LR svc_sessions --> pkg_session_persistence svc_sessions --> pkg_session_query svc_sessions --> pkg_session_query_sqlite - svc_sessions --> pkg_subagent_inprocess - svc_settings --> pkg_apiproxy + svc_sessions --> pkg_subagent_in_process_driver + svc_settings --> pkg_host_apiproxy svc_settings --> pkg_llm_deepseek svc_settings --> pkg_llm_pi_ai svc_shell --> pkg_hooks_claude_code @@ -438,39 +438,40 @@ flowchart LR svc_typert --> pkg_typert_loader svc_userQuestions --> pkg_tool_ask_user svc_web --> pkg_tool_web - svc_webServer --> pkg_connection - svc_webServer --> pkg_hmr - svc_webServer --> pkg_modules + svc_webServer --> pkg_client_connection + svc_webServer --> pkg_client_hmr + svc_webServer --> pkg_client_modules svc_webhookRuntime --> pkg_webhook_github svc_workflowEngine --> pkg_tool_ralph svc_workflowEngine --> pkg_tool_workflow - svc_workspaceRegistry --> pkg_apiproxy + svc_workspaceRegistry --> pkg_api_session_controller + svc_workspaceRegistry --> pkg_api_workspace_controller svc_fs -. event gate .-> pkg_fs_observation_policy ``` | ctx 键 | 角色 | 所属包 | 实现 | 直接消费方 | 配套插件 | 说明 | | --- | --- | --- | --- | --- | --- | --- | -| `ctx.attachments` | `seam` | [`attachment`](../packages/attachment/attachment) | [`attachment-local`](../packages/attachment/attachment-local) | `host-runtime`, [`llm-pi-ai`](../packages/llm/llm-pi-ai) | - | 宿主会在会话事件之前提交已接受的图片;提供方适配器将已授权的持久引用解析为提供方原生内容。 | +| `ctx.attachments` | `seam` | [`attachment`](../packages/attachment/attachment) | [`attachment-local`](../packages/attachment/attachment-local) | [`api-session-controller`](../packages/api/session-controller), [`host-apiproxy`](../packages/host/apiproxy), [`tool-fs`](../packages/fs/tool-fs), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`llm-deepseek`](../packages/llm/llm-deepseek) | - | 宿主会在会话事件之前提交已接受的图片;提供方适配器将已授权的持久引用解析为提供方原生内容。 | | `ctx.llm` | `seam` | [`llm`](../packages/llm/llm) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`llm-replay`](../packages/test-support/llm-replay) | [`agent-loop`](../packages/core/agent-loop), [`compaction-basic`](../packages/compaction/compaction-basic) | - | 适配器注册提供方实现;agent loop(智能体循环)与压缩功能调用提供方无关的流服务。 | | `ctx.deepseekLlmApiExtensions` | `seam` | [`deepseek-llm-api-extensions`](../packages/llm/deepseek-llm-api-extensions) | [`session-log-deepseek`](../packages/session/session-log-deepseek), [`plugin-package-inventory-deepseek`](../packages/llm/plugin-package-inventory-deepseek) | [`llm-deepseek`](../packages/llm/llm-deepseek) | - | 插件准备彼此独立的顶层字段;官方适配器会合并这些字段,并在 HTTP 接受后提交其交付状态。 | | `ctx.tokenMeter` | `core` | [`token-meter`](../packages/llm/token-meter) | - | [`compaction-basic`](../packages/compaction/compaction-basic) | - | 拥有按会话隔离的回放折叠区;压力消费方共享不可变且带修订版本的测量结果。 | | `ctx.toolResultPruner` | `core` | [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner) | - | [`compaction-basic`](../packages/compaction/compaction-basic) | - | 在摘要压缩前,通过可回放的单节点表层替换来改写过大的当前工具结果。 | -| `ctx.sessions` | `core` | [`session`](../packages/core/session) | - | [`agent-loop`](../packages/core/agent-loop), [`agent`](../packages/core/agent), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), `subagent-inprocess`, [`invariants`](../packages/runtime-diagnostics/invariants), [`message-feedback`](../packages/feedback/message-feedback) | - | 拥有仅追加的 Session 实例,并发出持久的会话事件流。 | -| `ctx.sessionController` | `core` | [`api-session-controller`](../packages/api/session-controller) | - | `apiproxy` | - | 负责 Session 命令、冷读取、持久事件跟随、实时控制状态与 Agent 激活策略;apiProxy 在需要 Session 上下文的领域中复用其检查和 Agent 解析操作。 | +| `ctx.sessions` | `core` | [`session`](../packages/core/session) | - | [`agent-loop`](../packages/core/agent-loop), [`agent`](../packages/core/agent), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver), [`invariants`](../packages/runtime-diagnostics/invariants), [`message-feedback`](../packages/feedback/message-feedback) | - | 拥有仅追加的 Session 实例,并发出持久的会话事件流。 | +| `ctx.sessionController` | `core` | [`api-session-controller`](../packages/api/session-controller) | - | [`host-apiproxy`](../packages/host/apiproxy) | - | 负责 Session 命令、冷读取、持久事件跟随、实时控制状态与 Agent 激活策略;apiProxy 在需要 Session 上下文的领域中复用其检查和 Agent 解析操作。 | | `ctx.workspaceController` | `core` | [`api-workspace-controller`](../packages/api/workspace-controller) | - | - | - | 通过生成的 Remote namespace 负责 Workspace 命令和可在重连后收敛的 Workspace 状态投递。 | | `ctx.invariants` | `core` | [`invariants`](../packages/runtime-diagnostics/invariants) | - | [`session`](../packages/core/session), [`agent`](../packages/core/agent), [`scope`](../packages/core/scope), [`agent-loop`](../packages/core/agent-loop) | - | 配套子路径注册所属包本地的检查;该服务负责选择、唯一性、子 fiber,以及标明所属包的失败。 | | `ctx.typert` | `core` | [`typert-registry`](../packages/typert/registry) | - | [`typert-loader`](../packages/typert/loader), [`api-gateway`](../packages/api/gateway) | - | 插件直接或通过 dsh-typert-loader 注册实时 zod 贡献;API 网关消费调用描述符和提供方,其他运行时消费方则在各自边界查询 schema 与反射元数据。 | | `ctx.typertGateway` | `core` | [`api-gateway`](../packages/api/gateway) | - | - | - | 将生成的 Remote 描述符与实时 Cordis 服务关联,解析已注册的身份,并通过共享的 Connection RPC 载体提供一元调用。 | | `ctx.sessionPersistence` | `seam` | [`session-persistence`](../packages/session/session-persistence) | [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-persistence-sqlite`](../packages/session/session-persistence-sqlite) | [`agent-loop`](../packages/core/agent-loop), [`tool-bash`](../packages/shell/tool-bash), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), [`message-feedback`](../packages/feedback/message-feedback) | - | 各后端持久化同一套 SessionEvent 词汇;应用在组合时选择后端。 | -| `ctx.settings` | `seam` | [`settings`](../packages/settings/settings) | [`settings-file`](../packages/settings/settings-file) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), `apiproxy` | - | 插件注册命名空间 schema 并解析分层值;提供方存储原始文档。LLM(大语言模型)适配器在用户分区下将其入口配置注册为组合基础;Web 网关提供经过脱敏的分层描述符,并写入用户层。 | +| `ctx.settings` | `seam` | [`settings`](../packages/settings/settings) | [`settings-file`](../packages/settings/settings-file) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`host-apiproxy`](../packages/host/apiproxy) | - | 插件注册命名空间 schema 并解析分层值;提供方存储原始文档。LLM(大语言模型)适配器在用户分区下将其入口配置注册为组合基础;Web 网关提供经过脱敏的分层描述符,并写入用户层。 | | `ctx.subagentModelSelection` | `core` | [`tool-subagent`](../packages/subagent/tool-subagent) | - | [`tool-subagent`](../packages/subagent/tool-subagent) | - | 拥有默认关闭的设置命名空间;Agent 作用域的委派工具会在组合新顶层 Session 时读取它。 | -| `ctx.credentials` | `seam` | [`credentials`](../packages/credentials/credentials) | [`credentials-local`](../packages/credentials/credentials-local) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), `apiproxy` | - | 配置携带对机密信息的引用;提供方拥有实际值。消费方按操作解析,因此轮换后的凭据会在紧接着的下一次请求中生效;Web 网关提供不含实际值的视图和只写存储。 | +| `ctx.credentials` | `seam` | [`credentials`](../packages/credentials/credentials) | [`credentials-local`](../packages/credentials/credentials-local) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`host-apiproxy`](../packages/host/apiproxy) | - | 配置携带对机密信息的引用;提供方拥有实际值。消费方按操作解析,因此轮换后的凭据会在紧接着的下一次请求中生效;Web 网关提供不含实际值的视图和只写存储。 | | `ctx.authorization` | `seam` | [`authorization`](../packages/credentials/authorization) | - | [`llm-pi-ai`](../packages/llm/llm-pi-ai) | - | flow 由知道如何取得某份凭据的插件注册,并以其写入的记录为键;seam 拥有这段对话与"每个键同时只跑一次尝试"的生命周期,而非协议本身。 | | `ctx.sessionTelemetry` | `seam` | [`session-telemetry`](../packages/session/session-telemetry) | [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | - | - | 该 seam 捕获会话记录、进行脱敏并交给一个后端;没有其他组件消费该服务,其输出会离开当前进程。 | | `ctx.storage` | `seam` | [`storage`](../packages/storage/storage) | [`storage-json`](../packages/storage/storage-json), [`storage-sqlite`](../packages/storage/storage-sqlite) | [`storage-domain`](../packages/storage/storage-domain) | - | 各后端以不同名称并列注册;数据形态(领域优先)挂载到枢纽上,并将类型化操作转换为不透明的 KV 单元原语。 | | `ctx.storageDomain` | `core` | [`storage-domain`](../packages/storage/storage-domain) | - | [`workspace`](../packages/workspace/workspace), [`message-feedback`](../packages/feedback/message-feedback) | - | 等待所有已配置后端就绪,然后将领域形态发布为一个受生命周期约束的服务,用于类型化持久状态。 | | `ctx.messageFeedback` | `core` | [`message-feedback`](../packages/feedback/message-feedback) | - | - | - | 拥有本地逐 assistant 消息反馈、生命周期与目标校验、逐条目 compare-and-set 及 Host 一元 Remote 契约,且不进入 Session 历史或遥测。 | -| `ctx.workspaceRegistry` | `core` | [`workspace`](../packages/workspace/workspace) | - | `apiproxy` | - | 通过领域设施拥有带 WorkspaceId 品牌类型的记录;稳定的 sessionIds 账户驱动 Host RPC 与 GUI 投影。 | +| `ctx.workspaceRegistry` | `core` | [`workspace`](../packages/workspace/workspace) | - | [`api-workspace-controller`](../packages/api/workspace-controller), [`api-session-controller`](../packages/api/session-controller) | - | 通过领域设施拥有带 WorkspaceId 品牌类型的记录;稳定的 sessionIds 账户驱动 Host RPC 与 GUI 投影。 | | `ctx.sessionQuery` | `seam` | [`session-query`](../packages/session-query/session-query) | [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | [`session-reference`](../packages/context/session-reference), [`tool-session-query`](../packages/session-query/tool-session-query) | - | 该接口提供精确读取、过滤和追踪;具体后端还提供全文协调、排序、摘要片段和游标世代,而模型消费方负责工作区权限与不含游标的渲染。 | | `ctx.fileReferences` | `seam` | [`file-reference`](../packages/context/file-reference) | [`file-reference-local`](../packages/context/file-reference-local) | - | - | 该接口通过其一元 Remote 契约返回指定 Agent cwd 内仅含路径的补全候选;提供方负责命名空间访问和排序,但不会读取文件内容。 | | `ctx.sessionReferenceResolver` | `core` | [`session-reference`](../packages/context/session-reference) | - | - | - | 将当前表层中有界的对话快照投影为持久但不可信的消息上下文;Host 适配器负责提及语法。 | @@ -484,7 +485,7 @@ flowchart LR | `ctx.sessionProjections` | `core` | [`session-projection`](../packages/session/session-projection) | - | [`tool-todo`](../packages/todo/tool-todo), [`session-title`](../packages/session/session-title), [`host-apiproxy`](../packages/host/apiproxy) | - | 各领域注册由状态驱动的折叠单元;主动驱动过程维护每个会话的水位状态,api-proxy 提供基线并推送发生变化的值。 | | `ctx.sessionProjectionCache` | `core` | [`session-projection-cache`](../packages/session/session-projection-cache) | - | [`host-apiproxy`](../packages/host/apiproxy) | - | 按会话持久保存投影单元状态的检查点(节流检查点,以及轮次/结束/分离时的必选检查点),并提供冷读取阶梯:缓存行加持久化尾部回放,因此列表读取永远不需要加载完整日志。 | | `ctx.skills` | `seam` | [`skill`](../packages/skill/skill) | [`skill-badge`](../packages/skill/skill-badge), [`skill-filesystem`](../packages/skill/skill-filesystem) | [`tool-skill`](../packages/skill/tool-skill) | - | 合并提供方的 skill(技能)目录;tool-skill 渲染会话前缀目录,并加载完整的 skill 正文。 | -| `ctx.agents` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/acp/acp), `subagent-inprocess` | - | 拥有实时 Agent 句柄、创建/恢复工厂 seam,以及进程本地的发起方传播。 | +| `ctx.agents` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/acp/acp), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) | - | 拥有实时 Agent 句柄、创建/恢复工厂 seam,以及进程本地的发起方传播。 | | `ctx.agentDefaultModel` | `core` | [`agent-default-model`](../packages/core/agent-default-model) | - | [`headless`](../packages/bundle/headless), [`host-apiproxy`](../packages/host/apiproxy) | - | 通过 settings 分层默认 `ModelSelection`,让直接入口与 Host 支撑的 Agent 入口共享同一个状态所有者。 | | `ctx.agentLoop` | `bundle` | [`agent-loop`](../packages/core/agent-loop) | - | [`agent-spine-demo`](../packages/examples/agent-spine-demo) | - | 唯一的具体循环插件;扩展包依赖 dsh-agent 的事件和服务,而不依赖此包。 | | `ctx.goals` | `core` | [`goal`](../packages/goal/goal) | - | - | - | 从会话日志折叠带修订版本的目标状态,并将实时延续激活保留在进程本地。 | @@ -495,23 +496,23 @@ flowchart LR | `ctx.terminals` | `seam` | [`terminal`](../packages/terminal/terminal) | [`terminal-bash`](../packages/terminal/terminal-bash) | [`tool-terminal`](../packages/terminal/tool-terminal) | - | 注册表负责精确到 Agent 的会话身份和清理;后端负责终端机制,tool-terminal 则提供限定于所有者作用域的模型接口。 | | `ctx.sandbox` | `seam` | [`sandbox`](../packages/sandbox/sandbox) | [`sandbox-local`](../packages/sandbox/sandbox-local) | [`bash-sandbox`](../packages/shell/bash-sandbox), [`terminal-bash`](../packages/terminal/terminal-bash) | - | 消费方交出即将执行 spawn 的确切 argv;与宿主共享文件系统和内核的后端按每次调用的策略包装该 argv,并报告强制执行情况。 | | `ctx.sandboxPolicy` | `core` | [`sandbox-policy`](../packages/sandbox/sandbox-policy) | - | [`bash-sandbox`](../packages/shell/bash-sandbox), [`fs-sandbox`](../packages/fs/fs-sandbox), [`terminal-bash`](../packages/terminal/terminal-bash) | - | 统一保存部署默认模式和工作区根目录;只有沙箱执行器和提供方读取该服务(工具层使用它同时导出的纯 `sandbox/mode` 折叠区)。两类强制执行组件都读取该服务,因此 bash 与 fs 不会限制到不同的根目录。 | -| `ctx.approval` | `seam` | `approval` | [`acp`](../packages/acp/acp) | [`tools`](../packages/core/tools), [`tool-bash`](../packages/shell/tool-bash) | - | 一次性权限决策通过 `approval/request` waterfall(瀑布式事件)分派;回答方是监听器(即 ACP 为自身 agent 提供的桥接),没有回答方时以 `unavailable` 关闭失败。 | +| `ctx.approval` | `seam` | [`user-approval`](../packages/interaction/user-approval) | - | [`tools`](../packages/core/tools), [`tool-bash`](../packages/shell/tool-bash), [`acp`](../packages/acp/acp) | - | 一次性权限决策通过 `approval/request` waterfall(瀑布式事件)分派;回答方是监听器(即 ACP 为自身 agent 提供的桥接),没有回答方时以 `unavailable` 关闭失败。 | | `ctx.permissionPresets` | `core` | [`permission-presets`](../packages/interaction/permission-presets) | - | - | - | 面向用户的预设表(`workspace-write`/`danger-full-access`),将沙箱模式与审批策略选项组合在一起;一次切换会写入一个 `permission/preset` 事件,并贯通到两个选项事件。 | -| `ctx.codeRuntime` | `seam` | [`code-runtime`](../packages/code-runtime/code-runtime) | `code-runtime-worker` | [`tools`](../packages/core/tools) | - | 使用 Host 提供的异步绑定运行一段由模型编写的程序;各后端采用不同的基础环境和语言(工具注册表在 Code Mode 下消费该服务)。 | +| `ctx.codeRuntime` | `seam` | [`code-runtime`](../packages/code-runtime/code-runtime) | [`code-runtime-worker-thread`](../packages/code-runtime/code-runtime-worker-thread) | [`tools`](../packages/core/tools) | - | 使用 Host 提供的异步绑定运行一段由模型编写的程序;各后端采用不同的基础环境和语言(工具注册表在 Code Mode 下消费该服务)。 | | `ctx.fs` | `seam` | [`fs`](../packages/fs/fs) | [`fs-local`](../packages/fs/fs-local), [`fs-sandbox`](../packages/fs/fs-sandbox), [`fs-e2b`](../packages/e2b/fs-e2b) | [`tool-fs`](../packages/fs/tool-fs) | [`fs-observation-policy`](../packages/fs/fs-observation-policy) | tool-fs 通过 ctx.fs 执行读取/写入/编辑;fs-sandbox 按共享沙箱模式限制变更;fs-observation-policy 通过 fs/* 事件门禁贡献基于观测状态的检查。 | | `ctx.compaction` | `seam` | [`compaction`](../packages/compaction/compaction) | [`compaction-basic`](../packages/compaction/compaction-basic) | [`compaction-basic`](../packages/compaction/compaction-basic) | - | 基础后端消费步骤后的压力事件和请求错误恢复事件;不存在面向模型的压缩工具。 | | `ctx.subagents` | `seam` | [`subagent`](../packages/subagent/subagent) | [`subagent-spawn-in-process`](../packages/subagent/subagent-spawn-in-process), [`subagent-fork-in-process`](../packages/subagent/subagent-fork-in-process), [`subagent-acp`](../packages/subagent/subagent-acp), [`subagent-codex`](../packages/subagent/subagent-codex), [`subagent-claude-code`](../packages/subagent/subagent-claude-code), [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-subagent-control`](../packages/subagent/tool-subagent-control), [`tool-ralph`](../packages/workflow/tool-ralph) | - | 提供方实现传输;该服务还负责可选的、基于 Activation 的延续编排,tool-subagent 选择一次性或可延续委派,tool-subagent-control 传递后续消息,而 tool-ralph 要求一条全新的结构化输出路由。 | -| `ctx.agentTeams` | `core` | `agent-team` | - | `tool-agent-team` | - | 负责隐式 Root roster、持久 peer mailbox、共享任务 DAG 与 continuable child 生命周期;tool-agent-team 提供作用域化模型策略和控制工具。 | +| `ctx.agentTeams` | `core` | [`experimental-agent-team`](../packages/experimental/agent-team) | - | [`experimental-tool-agent-team`](../packages/experimental/tool-agent-team) | - | 负责隐式 Root roster、持久 peer mailbox、共享任务 DAG 与 continuable child 生命周期;tool-agent-team 提供作用域化模型策略和控制工具。 | | `ctx.jobs` | `seam` | [`jobs`](../packages/jobs/jobs) | [`jobs-local`](../packages/jobs/jobs-local) | [`tool-bash`](../packages/shell/tool-bash), [`tool-terminal`](../packages/terminal/tool-terminal), [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-jobs`](../packages/jobs/tool-jobs) | - | 生产方(后台 bash、PTY 发送和 subagent 委派)登记正在运行的工作;tool-jobs 是面向模型的控制器,用于读取、列出和终止这些工作;jobs-local 是进程本地注册表。 | | `ctx.web` | `seam` | [`web`](../packages/web/web) | [`web-search-exa`](../packages/web/web-search-exa), [`web-search-perplexity`](../packages/web/web-search-perplexity), [`web-search-deepseek`](../packages/web/web-search-deepseek), [`web-fetch-http`](../packages/web/web-fetch-http) | [`tool-web`](../packages/web/tool-web) | - | 搜索和抓取提供方注册到同一个 ctx.web seam;tool-web 负责稳定的面向模型名称。 | | `ctx.spillStore` | `seam` | [`spill`](../packages/spill/spill) | [`spill-local`](../packages/spill/spill-local) | [`spill-policy`](../packages/spill/spill-policy) | - | 后端保存过大的工具文本,并返回面向模型的定位信息和取回提示;spill-policy 是 tools/post-execute 消费方,负责决定何时 spill。 | -| `ctx.directoryPicker` | `seam` | `directory-picker` | `directory-picker-native`, `directory-picker-browse` | `apiproxy` | - | 带判别标记的交互能力:原生后端在 Host 显示设备上打开一个操作系统选择器,浏览后端为应用内浏览器提供列表与创建原语;双端后端通过其浏览器侧填充 ui-workspace 目录流程的 slot(不通过协议发布)。 | -| `ctx.webServer` | `core` | `webserver` | - | `connection`, `modules`, `hmr` | - | 普通的 node:http 载体:具名路由注册表、索引转换 tap,以及静态 dist 回退;Web 传输插件注册自己的路由。 | -| `ctx.clientModules` | `core` | `modules` | - | `hmr` | - | 通过增量 `dsh.client` 扫描组合 __DSH_BOOT__ 入口图,提供插件组合包,并通知重建/图变更订阅方。 | +| `ctx.directoryPicker` | `seam` | [`host-directory-picker`](../packages/host/directory-picker) | [`host-directory-picker-native`](../packages/host/directory-picker-native), [`host-directory-picker-browse`](../packages/host/directory-picker-browse) | [`host-apiproxy`](../packages/host/apiproxy) | - | 带判别标记的交互能力:原生后端在 Host 显示设备上打开一个操作系统选择器,浏览后端为应用内浏览器提供列表与创建原语;双端后端通过其浏览器侧填充 ui-workspace 目录流程的 slot(不通过协议发布)。 | +| `ctx.webServer` | `core` | [`host-webserver`](../packages/host/webserver) | - | [`client-connection`](../packages/client/connection), [`client-modules`](../packages/client/modules), [`client-hmr`](../packages/client/hmr) | - | 普通的 node:http 载体:具名路由注册表、索引转换 tap,以及静态 dist 回退;Web 传输插件注册自己的路由。 | +| `ctx.clientModules` | `core` | [`client-modules`](../packages/client/modules) | - | [`client-hmr`](../packages/client/hmr) | - | 通过增量 `dsh.client` 扫描组合 __DSH_BOOT__ 入口图,提供插件组合包,并通知重建/图变更订阅方。 | | `ctx.workflowEngine` | `seam` | [`workflow`](../packages/workflow/workflow) | [`workflow-worker-thread`](../packages/workflow/workflow-worker-thread) | [`tool-workflow`](../packages/workflow/tool-workflow), [`tool-ralph`](../packages/workflow/tool-ralph) | - | 每个上下文使用一个引擎,与 bash 相同,且没有具名提供方注册表;通用工作流与固定 Ralph 消费方启动运行,其中的 agent() 调用通过 ctx.subagents 扇出。 | | `ctx.webhookRuntime` | `core` | [`webhook`](../packages/webhook/webhook) | - | [`webhook-github`](../packages/webhook/webhook-github) | - | 提供方适配器分派已认证交付;可信插件注册独立的进程本地规则,runtime 把非 null 结果转换为普通的 Workspace-backed Session,不保留交付或完成状态。 | -| `ctx.lsp` | `seam` | [`lsp`](../packages/lsp/lsp) | `lsp-local` | [`tool-lsp`](../packages/lsp/tool-lsp) | - | 提供方注册与选择,加上恰好四种操作的标准化查询执行;该 seam 不提供协议逃生口,后端必须转换为标准化请求和结果。 | -| `ctx.apiProxy` | `core` | `apiproxy` | - | `connection` | - | 与传输无关的 Host 网关接口:它分派浏览器 API 调用,每条打开的 Host 流自行订阅转发事件,而不是由广播方法向其推送。 | +| `ctx.lsp` | `seam` | [`lsp`](../packages/lsp/lsp) | [`lsp-stdio`](../packages/lsp/lsp-stdio) | [`tool-lsp`](../packages/lsp/tool-lsp) | - | 提供方注册与选择,加上恰好四种操作的标准化查询执行;该 seam 不提供协议逃生口,后端必须转换为标准化请求和结果。 | +| `ctx.apiProxy` | `core` | [`host-apiproxy`](../packages/host/apiproxy) | - | [`client-connection`](../packages/client/connection) | - | 与传输无关的 Host 网关接口:它分派浏览器 API 调用,每条打开的 Host 流自行订阅转发事件,而不是由广播方法向其推送。 | | `ctx.dynamicCordisRunner` | `core` | [`cordis-host-runner`](../packages/extensions/cordis-host-runner) | - | [`tool-cordis`](../packages/extensions/tool-cordis) | - | 拥有内存定义注册表、Host 半的 vm 沙箱和 request-run 往返流程;浏览器页面通过其 Remote 命名空间在线访问同一服务。 | | `ctx.cordisInspect` | `core` | [`cordis-host-runner`](../packages/extensions/cordis-host-runner) | - | [`tool-cordis`](../packages/extensions/tool-cordis) | - | 注册 Host inspect 提供方、镜像 Client 提供方 manifest,并通过动态 Cordis 传输路由 Client 查询。 | diff --git a/docs/config-catalog.i18n.yaml b/docs/config-catalog.i18n.yaml index 759cec3796..39d4865391 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: f63a09429e4a3543153ebafa156532263005aecc -config-catalog.zh.md: f2b7edf637c968fbc92c10cfc05bdb1849136cbc +config-catalog.md: 6eca3a6253928cb74ea857bf9c54521684dce0fd +config-catalog.zh.md: c19137938fcb0dbf3a5556f732ce8e97178d5603 diff --git a/docs/config-catalog.md b/docs/config-catalog.md index f63a09429e..6eca3a6253 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -2061,7 +2061,7 @@ Requires: `skills` ```ts config-catalog /** Local filesystem skill provider configuration. */ export interface Config { - /** Unique provider name. Defaults to `local`. */ + /** Unique provider name. Defaults to `filesystem`. */ providerName?: string /** Whether project and user roots are included around custom roots. */ includeDefaultRoots?: boolean diff --git a/docs/config-catalog.zh.md b/docs/config-catalog.zh.md index f2b7edf637..c19137938f 100644 --- a/docs/config-catalog.zh.md +++ b/docs/config-catalog.zh.md @@ -2063,7 +2063,7 @@ export interface Config { ```ts config-catalog /** Local filesystem skill provider configuration. */ export interface Config { - /** Unique provider name. Defaults to `local`. */ + /** Unique provider name. Defaults to `filesystem`. */ providerName?: string /** Whether project and user roots are included around custom roots. */ includeDefaultRoots?: boolean diff --git a/docs/cookbook/adding-a-package.i18n.yaml b/docs/cookbook/adding-a-package.i18n.yaml index 1219160597..9fb58fcf63 100644 --- a/docs/cookbook/adding-a-package.i18n.yaml +++ b/docs/cookbook/adding-a-package.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/cookbook/adding-a-package.md -adding-a-package.md: a78695735957395c5c900c3294b6778904557f85 -adding-a-package.zh.md: c6d091802a6afc7c8e0dff86a45bfb41b6a1c66e +adding-a-package.md: f228fa8871284b0473bcbb461157a142161abb81 +adding-a-package.zh.md: d60472cd9abc2a1fe7b5589ee8b6ca07a3c2f4b9 diff --git a/docs/cookbook/adding-a-package.md b/docs/cookbook/adding-a-package.md index a786957359..f228fa8871 100644 --- a/docs/cookbook/adding-a-package.md +++ b/docs/cookbook/adding-a-package.md @@ -20,7 +20,7 @@ packages/// # (or a whitelist entry in scripts/verify-package-readme-limitations.ts) ``` -Choose an existing group when one matches the package's role (`core`, `llm`, `bash`, `compact`, `subagent`, `todo`, `session-persistence`, `ui`, `util`, or `support`). A new group is allowed, but it is a pure container: no `package.json`, no source files, and packages still sit exactly one level below it. +Choose an existing group when one matches the package's role (`core`, `llm`, `shell`, `compaction`, `subagent`, `todo`, `session`, `client`/`host`, `util`, or `test-support`). A new group is allowed, but it is a pure container: no `package.json`, no source files, and packages still sit exactly one level below it. package.json invariants (enforced by `pnpm run constraints` / `scripts/check-workspace-constraints.ts`): `private: true`, a `version` matching the root `package.json`, `type: module`, `main: "lib/index.js"`, `types: "lib/types/index.d.ts"`, `exports["."].types: "./lib/types/index.d.ts"`, `exports["."].default: "./lib/index.js"`, `@deepseek-ai/cordis` in BOTH peerDependencies and devDependencies (same range). Mirror every dsh peer dependency in devDependencies. `@deepseek-ai/schemastery` goes in `dependencies` (it is a runtime validator), matching agent-loop. The `files` list contains exactly `lib/index.js`, `lib/invariant.js`, `lib/types/**/*.d.ts`, and package-specific runtime artifacts recognized by the gate; a package whose runtime export points into the emitted tree also includes `lib/types/**/*.js`. Do not publish `src`, declaration maps, JS maps, or stale root declaration files. CLI app packages with a package `bin` include `lib/bin.js` immediately after `lib/index.js` in `files`. @@ -72,7 +72,7 @@ Use `SDK` only for the JSON-RPC client/server protocol used by the supported Pyt ## 4. Write the package README -Keep package-specific service API, config, events, extension points, and design notes first. The limitations section records durable consumer gaps and non-obvious maintainer constraints owned by this package; ordinary cleanup stays in its source TODO or Agent Note. An indirect Model Experience sentence may name the consumer that surfaces this package's contribution, but it does not restate that consumer's implementation. End a package README with this canonical sequence: +Keep package-specific service API, config, events, extension points, and design notes first. Choose the frontmatter `kind` from the four kind labels in the [dsh-doc metadata reference](../../.agents/skills/dsh-doc/references/metadata-links-i18n.md#the-kind-system) — group, reference, library, or bundle — matching the package's repository position and entry shape; each kind selects one README template. The limitations section records durable consumer gaps and non-obvious maintainer constraints owned by this package; ordinary cleanup stays in its source TODO or Agent Note. An indirect Model Experience sentence may name the consumer that surfaces this package's contribution, but it does not restate that consumer's implementation. End a package README with this canonical sequence: ````markdown ## Model Experience diff --git a/docs/cookbook/adding-a-package.zh.md b/docs/cookbook/adding-a-package.zh.md index c6d091802a..d60472cd9a 100644 --- a/docs/cookbook/adding-a-package.zh.md +++ b/docs/cookbook/adding-a-package.zh.md @@ -20,7 +20,7 @@ packages/// # (or a whitelist entry in scripts/verify-package-readme-limitations.ts) ``` -当已有分组与包的角色匹配时,选择该分组(`core`、`llm`、`bash`、`compact`、`subagent`、`todo`、`session-persistence`、`ui`、`util` 或 `support`)。允许新建分组,但分组只是纯容器:没有 `package.json`,没有源文件,包仍然恰好位于其下一层。 +当已有分组与包的角色匹配时,选择该分组(`core`、`llm`、`shell`、`compaction`、`subagent`、`todo`、`session`、`client`/`host`、`util` 或 `test-support`)。允许新建分组,但分组只是纯容器:没有 `package.json`,没有源文件,包仍然恰好位于其下一层。 package.json 不变式(由 `pnpm run constraints` / `scripts/check-workspace-constraints.ts` 强制执行):`private: true`,`version` 与根 `package.json` 一致,`type: module`,`main: "lib/index.js"`,`types: "lib/types/index.d.ts"`,`exports["."].types: "./lib/types/index.d.ts"`,`exports["."].default: "./lib/index.js"`,`@deepseek-ai/cordis` 同时出现在 peerDependencies 和 devDependencies 中(相同范围)。每个 dsh 对等依赖(peer dependency)都要在 devDependencies 中镜像。`@deepseek-ai/schemastery` 放在 `dependencies` 中(它是运行时校验器),与 agent-loop 保持一致。`files` 列表精确包含 `lib/index.js`、`lib/invariant.js`、`lib/types/**/*.d.ts` 以及门禁认可的包专用运行时产物;如果包的运行时 export 指向输出树,还要包含 `lib/types/**/*.js`。不要发布 `src`、声明映射、JS map 或陈旧的根声明文件。带有 `bin` 的 CLI 应用包在 `files` 中将 `lib/bin.js` 紧跟在 `lib/index.js` 之后。 @@ -74,7 +74,7 @@ package.json 不变式(由 `pnpm run constraints` / `scripts/check-workspace-c ## 4. 编写包 README -将包特有的服务 API、配置、事件、扩展点和设计说明放在前面。limitations 部分记录持久的消费方缺口和本包拥有的非显而易见的维护者约束;日常清理事项留在源码 TODO 或 Agent Note 中。间接的 Model Experience 语句可以点名暴露本包贡献的消费方,但不重述该消费方的实现。包 README 以如下规范序列结尾: +将包特有的服务 API、配置、事件、扩展点和设计说明放在前面。根据 [dsh-doc 元数据参考](../../.agents/skills/dsh-doc/references/metadata-links-i18n.md#the-kind-system)中的四种 kind 标签——组、参考、库或 bundle——选择 frontmatter 的 `kind`,使其匹配包在仓库中的位置与入口形态;每个 kind 恰好对应一个 README 模板。limitations 部分记录持久的消费方缺口和本包拥有的非显而易见的维护者约束;日常清理事项留在源码 TODO 或 Agent Note 中。间接的 Model Experience 语句可以点名暴露本包贡献的消费方,但不重述该消费方的实现。包 README 以如下规范序列结尾: ````markdown ## Model Experience diff --git a/docs/cookbook/adding-a-tool.i18n.yaml b/docs/cookbook/adding-a-tool.i18n.yaml index bf2d9d4d3f..3aaabe5ef5 100644 --- a/docs/cookbook/adding-a-tool.i18n.yaml +++ b/docs/cookbook/adding-a-tool.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/cookbook/adding-a-tool.md -adding-a-tool.md: 4e07c33dd372ae95391fcad5236832a6f7662e82 -adding-a-tool.zh.md: 17a024a0db0d63ec9ef8c9407e77a471263427c9 +adding-a-tool.md: 9ec025426a2304f4bda56ddb8e6f5c42e3e8f4a8 +adding-a-tool.zh.md: 2c73b40ff53b5b58507b716ff32dd1fea26148f4 diff --git a/docs/cookbook/adding-a-tool.md b/docs/cookbook/adding-a-tool.md index 4e07c33dd3..9ec025426a 100644 --- a/docs/cookbook/adding-a-tool.md +++ b/docs/cookbook/adding-a-tool.md @@ -78,6 +78,7 @@ Both methods return a **`card`-tagged render intent** — pick the card kind tha - `generic` supplies an optional title and content. - `terminal` supplies raw output and optional exit metadata; each UI renders its capable or fallback view. - `diff` supplies applied hunks, often derived by `output.presentationMeta` and carried in persisted `result.meta` so replay reproduces them. Mutation tools keep a diff result because the completed view replaces the pending card. + - `read` supplies a completed file window reconstructed from persisted `result.meta`: the file `path`, a 1-based `offset`, the returned `lines` (each keeping its file line number), `totalLines`, and an optional `lang` highlight hint; a UI without the `read` capability falls back to the raw result content. There is no `read` call view — a read call's pending state stays a generic card, since content exists only after `execute`. (tool-fs `read`.) - `search` supplies a discovery result reconstructed from persisted `result.meta`: grouped-by-file matches (`shape: 'matches'`, grep) or a flat path list (`shape: 'paths'`, glob), plus `truncated`/`total` so a UI never presents a capped result as complete. The view carries no result text (a UI without a search card falls back to the raw result content), and there is no `search` call view — a discovery call's pending state stays a generic card, since matches exist only after `execute`. (tool-fs-search `grep`/`glob`.) - `web` supplies a completed web retrieval, discriminated by `kind: 'search' | 'fetch'` (the structured search sources or the fetch summary), derived from `result.meta`; it carries no body copy, so a UI without the `web` capability falls back to the raw result content. (tool-web `web_search`/`web_fetch`.) diff --git a/docs/cookbook/adding-a-tool.zh.md b/docs/cookbook/adding-a-tool.zh.md index 17a024a0db..2c73b40ff5 100644 --- a/docs/cookbook/adding-a-tool.zh.md +++ b/docs/cookbook/adding-a-tool.zh.md @@ -80,6 +80,7 @@ producer 提供同步的 `cancel`、在资源清理后 settle 且不 reject 的 - `generic` 提供可选的标题和内容。 - `terminal` 提供原始输出和可选的退出元数据;各 UI 根据自身能力渲染对应视图或回退视图。 - `diff` 提供已应用的 hunk,通常由 `output.presentationMeta` 派生并通过持久化的 `result.meta` 携带,使回放能重现它们。变更类工具保留 diff 结果,因为完成后的视图会替换 pending 卡片。 + - `read` 提供从持久化 `result.meta` 重建的已完成文件窗口:文件 `path`、从 1 开始的 `offset`、返回的 `lines`(每行保留其文件行号)、`totalLines`,以及可选的 `lang` 高亮提示;不具备 `read` 能力的 UI 回退到原始结果内容。没有 `read` 调用视图——读取调用的 pending 状态保持为 generic 卡片,因为内容只在 `execute` 之后才存在。(tool-fs `read`。) - `search` 提供从持久化 `result.meta` 重建的发现型结果:按文件分组的匹配(`shape: 'matches'`,grep)或扁平路径列表(`shape: 'paths'`,glob),外加 `truncated`/`total` 使 UI 永不把被截断的结果当作完整结果呈现。该视图不携带结果文本(无 search 卡片的 UI 回退到原始结果内容),也没有 `search` 调用视图——发现型调用的 pending 状态保持为 generic 卡片,因为匹配只在 `execute` 之后才存在。(tool-fs-search 的 `grep`/`glob`。) - `web` 提供已完成的 web 检索,以 `kind: 'search' | 'fetch'` 区分(结构化的搜索来源或抓取摘要),由 `result.meta` 派生;它不携带正文副本,因此不具备 `web` 能力的 UI 回退到原始结果内容。(tool-web `web_search`/`web_fetch`。) diff --git a/docs/cookbook/adding-a-vendored-package.i18n.yaml b/docs/cookbook/adding-a-vendored-package.i18n.yaml index f7b2dbd88f..10940658a5 100644 --- a/docs/cookbook/adding-a-vendored-package.i18n.yaml +++ b/docs/cookbook/adding-a-vendored-package.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/cookbook/adding-a-vendored-package.md -adding-a-vendored-package.md: 239ac27565204332559038014fabae83fc2d1057 -adding-a-vendored-package.zh.md: 6037d4533da3353034e9860d5968d8b1f105007e +adding-a-vendored-package.md: 5c0d9b22eac4db916cd1eab37c1e90690be40e3c +adding-a-vendored-package.zh.md: 274bd467ef9a969adccdd813fbe8cdb69cf2b475 diff --git a/docs/cookbook/adding-a-vendored-package.md b/docs/cookbook/adding-a-vendored-package.md index 239ac27565..5c0d9b22ea 100644 --- a/docs/cookbook/adding-a-vendored-package.md +++ b/docs/cookbook/adding-a-vendored-package.md @@ -8,7 +8,7 @@ When the harness needs another upstream Cordis package (e.g. `@cordisjs/plugin-h ``` vendor// - package.json # from upstream; set "private": true, rescope the name, keep exports/type + package.json # from upstream; rescope the name, keep exports/type (publishable release member, no private flag) tsconfig.json # extends ../../tsconfig.base.json (see configuration below) src/ # the upstream src/ verbatim README.md LICENSE # if upstream ships them @@ -29,7 +29,7 @@ vendor// } ``` -`package.json` invariants: `"private": true` (vendored packages are never published), rescope the `name` ([mapping](../rescope.md)) while keeping upstream's `version`/`exports`/`type`, point declaration metadata at `lib/types`, publish `.d.ts` and `.d.ts.map` declaration outputs, and list its cordis deps in `peerDependencies` (matching the upstream manifest). Transitive upstream deps must themselves be vendored or already present — vendoring one package often means vendoring its dependency tree (e.g. `@cordisjs/plugin-http` pulls `@cordisjs/fetch-file`). +`package.json` invariants: rescope the `name` ([mapping](../rescope.md)) while keeping upstream's `exports`/`type`, point declaration metadata at `lib/types`, publish `.d.ts` and `.d.ts.map` declaration outputs, and list its cordis deps in `peerDependencies` (matching the upstream manifest). Vendored packages are publishable release members, so they must NOT set `private: true` and must set `publishConfig.access: public`; the `version` field follows the harness release sequence (see [vendor/README.md](../../vendor/README.md)). Transitive upstream deps must themselves be vendored or already present — vendoring one package often means vendoring its dependency tree (e.g. `@cordisjs/plugin-http` pulls `@cordisjs/fetch-file`). Local relative imports/exports in vendored TypeScript source use explicit `.ts` specifiers after copying. This is a repo-local build difference from upstream: `rewriteRelativeImportExtensions` emits `.js` runtime imports while declarations keep explicit `.ts` specifiers that NodeNext/Node16 TypeScript consumers can resolve. diff --git a/docs/cookbook/adding-a-vendored-package.zh.md b/docs/cookbook/adding-a-vendored-package.zh.md index 6037d4533d..274bd467ef 100644 --- a/docs/cookbook/adding-a-vendored-package.zh.md +++ b/docs/cookbook/adding-a-vendored-package.zh.md @@ -8,7 +8,7 @@ ``` vendor// - package.json # from upstream; set "private": true, rescope the name, keep exports/type + package.json # from upstream; rescope the name, keep exports/type (publishable release member, no private flag) tsconfig.json # extends ../../tsconfig.base.json (see configuration below) src/ # the upstream src/ verbatim README.md LICENSE # if upstream ships them @@ -29,7 +29,7 @@ vendor// } ``` -`package.json` 的不变式:`"private": true`(vendored 包永不发布);改写 `name` 的 scope([映射](../rescope.zh.md)),保留上游的 `version`/`exports`/`type`;声明元数据指向 `lib/types`;发布 `.d.ts` 与 `.d.ts.map` 声明输出;在 `peerDependencies` 中列出其 Cordis 依赖(与上游 manifest(元数据清单)一致)。传递性上游依赖本身也必须被 vendor 或已存在于仓库中——vendor 一个包往往意味着 vendor 其整条依赖树(如 `@cordisjs/plugin-http` 会拉入 `@cordisjs/fetch-file`)。 +`package.json` 的不变式:改写 `name` 的 scope([映射](../rescope.zh.md)),保留上游的 `exports`/`type`;声明元数据指向 `lib/types`;发布 `.d.ts` 与 `.d.ts.map` 声明输出;在 `peerDependencies` 中列出其 Cordis 依赖(与上游 manifest(元数据清单)一致)。vendored 包是可发布的 release member,因此不得设置 `private: true`,且必须设置 `publishConfig.access: public`;`version` 字段跟随 harness 发布序列(见 [vendor/README.md](../../vendor/README.md))。传递性上游依赖本身也必须被 vendor 或已存在于仓库中——vendor 一个包往往意味着 vendor 其整条依赖树(如 `@cordisjs/plugin-http` 会拉入 `@cordisjs/fetch-file`)。 vendored TypeScript 源码中的本地相对导入/导出在复制后使用显式 `.ts` 后缀。这是仓库本地构建与上游的差异:`rewriteRelativeImportExtensions` 输出 `.js` 运行时导入,而声明文件保留显式 `.ts` 后缀,使 NodeNext/Node16 的 TypeScript 消费方能够解析。 diff --git a/docs/cookbook/adding-an-llm-adapter.i18n.yaml b/docs/cookbook/adding-an-llm-adapter.i18n.yaml index c81b438b34..adb0aaf1f8 100644 --- a/docs/cookbook/adding-an-llm-adapter.i18n.yaml +++ b/docs/cookbook/adding-an-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/cookbook/adding-an-llm-adapter.md -adding-an-llm-adapter.md: 91ba34335ca756b28569e4082e27a37f150461ef -adding-an-llm-adapter.zh.md: 8f80f073093360881caccdd1225f173e8226448a +adding-an-llm-adapter.md: bbe4428155f2b2ed5279c5a159e9446f28068a5c +adding-an-llm-adapter.zh.md: 114745b7e07e807cbdd894ed69014455914ca68f diff --git a/docs/cookbook/adding-an-llm-adapter.md b/docs/cookbook/adding-an-llm-adapter.md index 91ba34335c..bbe4428155 100644 --- a/docs/cookbook/adding-an-llm-adapter.md +++ b/docs/cookbook/adding-an-llm-adapter.md @@ -29,7 +29,7 @@ Registration is effect-based (HMR-safe); one adapter per provider route — dupl - Allocate block `index`es in first-seen stream order; reuse the index for every delta of the same block. - Errors have exactly two sanctioned paths: THROW from `stream()` (transport and protocol failures — use `LlmError` with a stable code), or end the stream with `finish {kind: 'error' | 'aborted'}` (provider in-band failures). Consumers handle both; pick per failure class and document it. - Honor `options.signal` (pass it to fetch / your SDK). -- A `GenerateOptions` field your provider cannot honor (e.g. a `stop` list on a provider without stop sequences): throw `LlmError(..., 'UNSUPPORTED')` rather than silently dropping it. +- A `GenerateOptions` field your provider cannot honor (e.g. a `stop` list on a provider without stop sequences): throw `LlmError(..., 'UNSUPPORTED_OPTION')` rather than silently dropping it. - If the provider requires response ids, signatures, or other native metadata on follow-up calls, emit the minimal lossless-JSON projection as `finish.replayState`. Validate it when rebuilding history. `LlmRuntime` passes it only when the historical provider route and target provider route are currently owned by the exact same adapter instance; your adapter decides whether same-model, cross-model, or cross-provider restoration is legal. Never infer native replay from provider/model names alone when state is absent. Provider-specific thinking-mode toggles remain in the adapter's Config. Exact model metadata uses one provider-neutral capability seam: implement `resolveModel()` with provider/model identity and optional `context` and `reasoning` fields, declare a configured `defaultEffort` only when one exists, and honor the resolver's optional `AbortSignal`. Reasoning efforts are ordered opaque ids mapped to provider requests by the adapter. Preserve the adapter's authoritative selectable list, including an adapter-defined `off` when supported, without exposing final wire spellings or clamping unsupported values; an id need not equal its wire representation. diff --git a/docs/cookbook/adding-an-llm-adapter.zh.md b/docs/cookbook/adding-an-llm-adapter.zh.md index 8f80f07309..114745b7e0 100644 --- a/docs/cookbook/adding-an-llm-adapter.zh.md +++ b/docs/cookbook/adding-an-llm-adapter.zh.md @@ -29,7 +29,7 @@ export function apply(ctx: Context, config: Config) { - 按首次出现的流顺序分配块 `index`;同一个块的每次 delta 复用该 index。 - 错误有且仅有两条合法路径:从 `stream()` **抛出**(传输与协议故障——使用带稳定 code 的 `LlmError`),或以 `finish {kind: 'error' | 'aborted'}` 结束流(提供方带内故障)。消费方两者都处理;按故障类别选择路径并加以文档化。 - 遵守 `options.signal`(将其传递给 fetch 或你的 SDK)。 -- 如果 `GenerateOptions` 中某个字段你的提供方无法支持(例如提供方不支持 stop sequences 时收到 `stop` 列表):抛出 `LlmError(..., 'UNSUPPORTED')`,而非静默丢弃。 +- 如果 `GenerateOptions` 中某个字段你的提供方无法支持(例如提供方不支持 stop sequences 时收到 `stop` 列表):抛出 `LlmError(..., 'UNSUPPORTED_OPTION')`,而非静默丢弃。 - 如果提供方在后续调用中需要响应 ID、签名或其他原生元数据,请将其最小无损 JSON 投影作为 `finish.replayState` 发出。重建历史时验证该状态。只有历史提供方路由和目标提供方路由当前由完全相同的适配器实例拥有时,`LlmRuntime` 才会传递该状态;由适配器决定同模型、跨模型或跨提供方恢复是否合法。状态缺失时,切勿仅根据提供方/模型名称推断原生回放。 提供方特有的思考模式开关仍放在适配器的 Config 中。确切模型元数据使用一处提供方无关的能力 seam:实现 `resolveModel()`,返回提供方/模型身份以及可选的 `context` 和 `reasoning` 字段;仅当存在配置指定的默认值时才声明 `defaultEffort`;遵守解析模型时传入的可选 `AbortSignal`。推理(reasoning)强度是由适配器映射到提供方请求的有序不透明 ID。请保留适配器给出的权威可选列表,包括适配器在支持时定义的 `off`;不得暴露最终协议值的具体拼写,也不得自动调整不支持的值。ID 无需与其协议表示相同。 diff --git a/docs/cookbook/extension-cookbook.i18n.yaml b/docs/cookbook/extension-cookbook.i18n.yaml index 54aeea5d5e..24c280ae11 100644 --- a/docs/cookbook/extension-cookbook.i18n.yaml +++ b/docs/cookbook/extension-cookbook.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/cookbook/extension-cookbook.md -extension-cookbook.md: b78e1a434c0bc06179c3eb34b6141b885b939a7f -extension-cookbook.zh.md: 3b0deb668d4fae9f75248e72f64dc846589ef9bc +extension-cookbook.md: 7b8c55837038aa421850449ebf553eb1ef47218f +extension-cookbook.zh.md: 899fcca4ad3c8977044da5a17e40283905abc752 diff --git a/docs/cookbook/extension-cookbook.md b/docs/cookbook/extension-cookbook.md index b78e1a434c..7b8c558370 100644 --- a/docs/cookbook/extension-cookbook.md +++ b/docs/cookbook/extension-cookbook.md @@ -30,7 +30,7 @@ export function apply(ctx: Context) { } ``` -This waterfall is the reorderable policy layer. Use `ctx.tools.guard()` when an invariant needs a monotonic final denial, `tools/execute` when a plugin must wrap the actual dispatch lifetime (timeouts/retries/metrics; only `exec.signal` is replaceable), `tools/post-execute` for explicit result transformation, and `tools/result` for contained observation of the immutable final outcome. The [adding-a-tool guide](adding-a-tool.md#execution-policy-and-observation) gives the selection rule. +This waterfall is the reorderable policy layer. Use `ctx.tools.guard()` when an invariant needs a monotonic final denial, `tools/execute` when a plugin must wrap the dispatch lifetime (timeouts/retries/metrics; only `exec.signal` is replaceable), `tools/post-execute` for explicit result transformation, and `tools/result` for contained observation of the immutable final outcome. The [adding-a-tool guide](adding-a-tool.md#execution-policy-and-observation) gives the selection rule. ## A UI plugin @@ -117,11 +117,11 @@ Every product feature maps to a listener on a documented extension point — the | Subprocess sandbox (landlock / sandbox-exec) | use a `ctx.sandbox` backend through `dsh-bash-sandbox`; use `tools/pre-execute` for capability-level denial | | Permission system / AskUserQuestion | return `ask` from `tools/pre-execute` and answer through `ctx.approval`; register a separate model-facing ask tool for ordinary user questions | | Plan mode | [`@deepseek-ai/dsh-plan-mode`](../../packages/plan/plan-mode/README.md) — logged `plan/mode` state, the `plan:policy` guidance section, `/plan [message]` entry, `/plan off` direct exit, and the user-reviewed `exit_plan_mode` exit; enforcement stays on the independent sandbox/approval axes | -| Sub-agent delegation | the `ctx.subagents` provider registry (`dsh-subagent-spawn-in-process`/`-fork`/`-acp`/`-codex`/`-claude-code`/`-dsh-sdk`) + `dsh-tool-subagent` exposing one configured provider to the model | +| Sub-agent delegation | the `ctx.subagents` provider registry (`dsh-subagent-spawn-in-process`/`dsh-subagent-fork-in-process`/`dsh-subagent-acp`/`dsh-subagent-codex`/`dsh-subagent-claude-code`/`dsh-subagent-dsh-sdk`) + `dsh-tool-subagent` exposing one configured provider to the model | | MCP | one plugin per server: discover tools → `ctx.tools.register()` | | Skills | section + tool registration; `inject()` skill content on invocation | | Memory | section provider + tool | -| Scheduled tasks (cron) | a plugin registers model-callable scheduling tools; timer fires → `followup(…, {source: {kind: 'cron', …}})` when idle / `inject()` notification when busy | +| Scheduled tasks (cron) | a plugin registers model-callable scheduling tools; timer fires → `followup(…, {source: {kind: 'plugin', plugin: 'schedule'}})` when idle / `inject()` notification when busy | | UI (GUI; CLI emits JSONL) | listen `session/event` (assistant chunks, boundaries, tool activity); input → `followup()` | | Web Client Chat business node | register a `ConversationNodeDefinition` and `conversation.chat.node` keyed renderer | | SessionTelemetryBackend / replayable trace | `session/event` → JSONL; replay = `sessions.create(id, { seed })` | diff --git a/docs/cookbook/extension-cookbook.zh.md b/docs/cookbook/extension-cookbook.zh.md index 3b0deb668d..899fcca4ad 100644 --- a/docs/cookbook/extension-cookbook.zh.md +++ b/docs/cookbook/extension-cookbook.zh.md @@ -32,7 +32,7 @@ export function apply(ctx: Context) { } ``` -这个 waterfall(瀑布式事件)是可重排的策略层。当不变式需要单调的最终拒绝时使用 `ctx.tools.guard()`;当插件需要包裹实际分发生命周期时(超时/重试/指标;仅 `exec.signal` 可替换)使用 `tools/execute`;显式结果变换使用 `tools/post-execute`;对不可变最终结果的受限观察使用 `tools/result`。选择规则见[添加工具指南](adding-a-tool.zh.md#execution-policy-and-observation)。 +这个 waterfall(瀑布式事件)是可重排的策略层。当不变式需要单调的最终拒绝时使用 `ctx.tools.guard()`;当插件需要包裹分发生命周期时(超时/重试/指标;仅 `exec.signal` 可替换)使用 `tools/execute`;显式结果变换使用 `tools/post-execute`;对不可变最终结果的受限观察使用 `tools/result`。选择规则见[添加工具指南](adding-a-tool.zh.md#execution-policy-and-observation)。 ## UI 插件 @@ -121,11 +121,11 @@ export function apply(ctx: Context) { | 子进程沙箱(landlock / sandbox-exec) | 通过 `dsh-bash-sandbox` 使用 `ctx.sandbox` 后端;能力级别的拒绝使用 `tools/pre-execute` | | 权限系统 / AskUserQuestion | 从 `tools/pre-execute` 返回 `ask` 并通过 `ctx.approval` 应答;为普通用户提问注册一个独立的面向模型的 ask 工具 | | Plan mode | [`@deepseek-ai/dsh-plan-mode`](../../packages/plan/plan-mode/README.zh.md):落日志的 `plan/mode` 状态、`plan:policy` 引导段、`/plan [message]` 入口、`/plan off` 直接退出,以及经用户评审的 `exit_plan_mode` 出口;强制约束留在独立的沙箱/审批轴上 | -| subagent 委派 | `ctx.subagents` 提供方注册表(`dsh-subagent-spawn-in-process`/`-fork`/`-acp`/`-codex`/`-claude-code`/`-dsh-sdk`)+ `dsh-tool-subagent` 向模型暴露一个已配置的提供方 | +| subagent 委派 | `ctx.subagents` 提供方注册表(`dsh-subagent-spawn-in-process`/`dsh-subagent-fork-in-process`/`dsh-subagent-acp`/`dsh-subagent-codex`/`dsh-subagent-claude-code`/`dsh-subagent-dsh-sdk`)+ `dsh-tool-subagent` 向模型暴露一个已配置的提供方 | | MCP | 每个服务器一个插件:发现工具 → `ctx.tools.register()` | | skill(技能) | section + 工具注册;调用时通过 `inject()` 注入 skill 内容 | | 记忆 | section 提供方 + 工具 | -| 定时任务(cron) | 插件注册面向模型的调度工具;定时器触发 → 空闲时 `followup(…, {source: {kind: 'cron', …}})`/忙碌时 `inject()` 通知 | +| 定时任务(cron) | 插件注册面向模型的调度工具;定时器触发 → 空闲时 `followup(…, {source: {kind: 'plugin', plugin: 'schedule'}})`/忙碌时 `inject()` 通知 | | UI(GUI;CLI(命令行界面)输出 JSONL) | 监听 `session/event`(助手分片、边界、工具活动);输入 → `followup()` | | Web Client Chat 业务节点 | 注册 `ConversationNodeDefinition` 与 `conversation.chat.node` keyed renderer | | 遥测 / 可回放 trace | `session/event` → JSONL;回放 = `sessions.create(id, { seed })` | diff --git a/docs/cordis-api/inherited.md b/docs/cordis-api/inherited.md index a3cfdf26ea..4810487ea7 100644 --- a/docs/cordis-api/inherited.md +++ b/docs/cordis-api/inherited.md @@ -15,7 +15,7 @@ This file is GENERATED from source (`scripts/gen-cordis-catalog.ts`) and verifie - `ctx.effect` — Register a disposable side effect tied to the fiber. ([`vendor/cordis/src/fiber.ts:9`](../../vendor/cordis/src/fiber.ts)) - `ctx.get / ctx.set / ctx.provide / ctx.accessor / ctx.mixin` — Low-level service-store access and binding. ([`vendor/cordis/src/reflect.ts:7`](../../vendor/cordis/src/reflect.ts)) - `ctx.extend / ctx.isolate / ctx.intercept` — Derive a child context (scoped services / isolation / interception). ([`vendor/cordis/src/context.ts:42`](../../vendor/cordis/src/context.ts)) -- `ctx.root / ctx.scope / ctx.fiber / ctx.registry / ctx.reflect / ctx.events / ctx.logger` — Ambient handles onto the running context graph. ([`vendor/cordis/src/context.ts:16`](../../vendor/cordis/src/context.ts)) +- `ctx.root / ctx.fiber / ctx.registry / ctx.reflect / ctx.events / ctx.logger` — Ambient handles onto the running context graph. ([`vendor/cordis/src/context.ts:16`](../../vendor/cordis/src/context.ts)) - `ctx.timer (+ interval / timeout / throttle / debounce)` — Disposable timer helpers. The `timer` key is provided at runtime; the four supported helpers are mixed onto ctx directly (declared via Pick). ([`vendor/timer/src/index.ts:4`](../../vendor/timer/src/index.ts)) - `ctx.loader` — The config Loader that booted the app (present under the loader). ([`vendor/loader/src/index.ts:30`](../../vendor/loader/src/index.ts)) - `ctx.hmr` — The hot-module-reload watcher (present under the hmr plugin). ([`vendor/hmr/src/index.ts:15`](../../vendor/hmr/src/index.ts)) diff --git a/docs/cordis-primer.i18n.yaml b/docs/cordis-primer.i18n.yaml index 17e98477fb..b56974c548 100644 --- a/docs/cordis-primer.i18n.yaml +++ b/docs/cordis-primer.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-primer.md -cordis-primer.md: 2a3afe180623d89b006dfa3e73aba5567c15bbe9 -cordis-primer.zh.md: 999073673a2daf3343cf02a1dffc01e90de3b755 +cordis-primer.md: 2e5a48745cf96068bec9e31b0c6f1bf9d84b0e34 +cordis-primer.zh.md: 3706173ddb6b097a59d7b85d08f04df7d0bd372f diff --git a/docs/cordis-primer.md b/docs/cordis-primer.md index 2a3afe1806..2e5a48745c 100644 --- a/docs/cordis-primer.md +++ b/docs/cordis-primer.md @@ -9,7 +9,7 @@ Cordis is the vendored plugin framework underneath DeepSeek Harness. This primer - **A plugin is a object that implements Service.** It can be a function with optional `inject` and `apply(ctx)` fields, or a `Service` subclass whose lifecycle Cordis mounts into the current context. - **A context is a repository of services.** A service claims a stable `ctx.` such as `ctx.tools`, `ctx.llm`, or `ctx.sessions` from a context; other plugins find services via key instead of importing a concrete implementation. - **Declare service dependency via `inject`.** A plugin that names required services waits until those services exist, so load order is expressed through service requirements rather than manual boot sequencing. -- **Typed Events for communication.** Services declare event names through TypeScript declaration merging, then dispatch them as `emit`, `waterfall`, `parallel`, or `serial` depending on whether listeners observe, wrap, fan out, or run in order. +- **Typed Events for communication.** Services declare event names through TypeScript declaration merging, then dispatch them as `emit`, `waterfall`, `parallel`, `serial`, or `bail` depending on whether listeners observe, wrap, fan out, run in order, or stop at the first bail value. - **Registrations are reversible effects.** Prompt sections, tool schemas, adapters, providers, and listeners are installed through `ctx.effect()` or `ctx.on()` so reload and teardown unwind them predictably. ## Dispatch Modes @@ -22,6 +22,7 @@ Every event can have one of the following dispatch mode and can only be dispatch | `waterfall` | No | listeners observe in registration order | Yes | | `parallel` | Yes | all listeners observe the event in parallel | No | | `serial` | Yes | listeners observe in registration order | Yes | +| `bail` | No | listeners observe in registration order until one bails | Yes | The dispatch mode is part of the event's public contract. New harness events document it with an `@mode` tag so the generated catalog can check declarations against dispatch sites. diff --git a/docs/cordis-primer.zh.md b/docs/cordis-primer.zh.md index 999073673a..3706173ddb 100644 --- a/docs/cordis-primer.zh.md +++ b/docs/cordis-primer.zh.md @@ -9,7 +9,7 @@ Cordis 是 DeepSeek Harness 底层以 vendor 方式引入的插件框架。本 - **插件是实现 Service 的对象。** 它可以是一个带有可选 `inject` 和 `apply(ctx)` 字段的函数,也可以是一个 `Service` 子类,其生命周期由 Cordis 挂载到当前上下文中。 - **上下文是服务的容器。** 一个服务占据一个稳定的 `ctx.`(如 `ctx.tools`、`ctx.llm`、`ctx.sessions`);其他插件通过 key 查找服务,而非导入具体实现。 - **通过 `inject` 声明服务依赖。** 插件声明所需的服务后,会等待这些服务就绪才启动;加载顺序通过服务依赖表达,而非手动编排启动序列。 -- **类型化事件用于通信。** 服务通过 TypeScript 声明合并注册事件名,然后以 `emit`、`waterfall`(瀑布式事件)、`parallel` 或 `serial` 方式分发,分别对应监听者观察、包装、并行扇出或按序执行。 +- **类型化事件用于通信。** 服务通过 TypeScript 声明合并注册事件名,然后以 `emit`、`waterfall`(瀑布式事件)、`parallel`、`serial` 或 `bail` 方式分发,分别对应监听者观察、包装、并行扇出、按序执行或停在首个 bail 值。 - **注册是可逆的副作用。** 提示词片段、工具 schema、适配器、提供方和监听器通过 `ctx.effect()` 或 `ctx.on()` 安装,reload 和 teardown 时会按预期撤销。 @@ -24,6 +24,7 @@ Cordis 是 DeepSeek Harness 底层以 vendor 方式引入的插件框架。本 | `waterfall` | 否 | 监听器按注册顺序观察 | 是 | | `parallel` | 是 | 所有监听器并行观察事件 | 否 | | `serial` | 是 | 监听器按注册顺序观察 | 是 | +| `bail` | 否 | 监听器按注册顺序观察,直到某个监听器返回 bail 值 | 是 | 分发模式是事件公开约定的一部分。新的 harness 事件通过 `@mode` 标签记录模式,以便生成的目录可以将声明与分发调用点做交叉校验。 diff --git a/docs/development.i18n.yaml b/docs/development.i18n.yaml index 8de82be755..fad92b97e3 100644 --- a/docs/development.i18n.yaml +++ b/docs/development.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/development.md -development.md: 45ce6863a551a4f8bf5110523753fed0228825ef -development.zh.md: dc68637bc9138af833f3ff4f0c1aacd5f3edfe7f +development.md: 12797904fab31d613db92af0da0656146eeea51a +development.zh.md: c759f4236efa356c5cc82dcec758654362fcc284 diff --git a/docs/development.md b/docs/development.md index 45ce6863a5..12797904fa 100644 --- a/docs/development.md +++ b/docs/development.md @@ -43,7 +43,7 @@ Setup is complete when `pnpm run typecheck` exits successfully. ### TypeScript project layout -The repository uses isolated Host and Client aggregates. An ordinary package is registered in exactly one aggregate: Host packages in `tsconfig.host.json` and Client packages in `tsconfig.client.json`. +The repository uses isolated Host and Client aggregates. An ordinary package is registered in exactly one aggregate: Host packages in `tsconfig.host.json` and Client packages in `tsconfig.client.json`; three packages (`host/webserver`, `compaction/compaction`, `typert/registry`) are referenced by both aggregates as shared leaves so each side type-checks the same source. | File | Role | Forms a program? | |---|---|---| @@ -57,9 +57,9 @@ Host and Client stay two aggregate programs because both sides declaration-merge - `tsconfig.base.json` never gains `include` or `files`: they would leak into every extending package project and narrow the facade's match-all scope. - A script that builds a repo-wide `ts.Program` seeds `tsconfig.host.json` or `tsconfig.client.json` explicitly — never the root solution, because flattening both aggregates into one program collides the `Context` merges. -- A new package is registered in exactly one aggregate. Having both a Node loader entry and a browser entry is not a reason to split a package; an ordinary Client plugin produces both runtime artifacts during the Client build phase. +- A new package is registered in exactly one aggregate; only the split packages above carry both leaf configs, and the shared leaves are registered in both aggregates because each side must type-check the same source. Having both a Node loader entry and a browser entry is not a reason to split a package; an ordinary Client plugin produces both runtime artifacts during the Client build phase. -`api/remotes` is the repository's only package with split Host and Client tsconfigs. Its Host entry must participate in the Host Typert graph, while its Client entry imports `/remote` declarations that Host tsdown must generate first. The package-root `tsconfig.json` is therefore only a solution, and the two aggregates and direct consumers reference `tsconfig.host.json` or `tsconfig.client.json` respectively. The workspace `constraints` gate walks the reachable Project Reference graph and checks each referencing project's own compiler face: a single-config target remains valid from either face, while a split target must name the matching leaf rather than its solution root or opposite leaf; it discovers split packages from the presence of both leaf configs, so a new split joins the gate automatically. Do not copy this structure to other packages; the [`api-remotes` README](../packages/api/remotes/README.md) explains the Host/Client split and build order. +Five packages split Host and Client tsconfigs: `api/remotes`, `api/gateway`, `api/session-controller`, `api/workspace-controller`, and `client/connection`. `api/remotes`' Host entry must participate in the Host Typert graph, while its Client entry imports `/remote` declarations that Host tsdown must generate first. Each split package-root `tsconfig.json` is therefore only a solution, and the two aggregates and direct consumers reference `tsconfig.host.json` or `tsconfig.client.json` respectively. The workspace `constraints` gate walks the reachable Project Reference graph and checks each referencing project's own compiler face: a single-config target remains valid from either face, while a split target must name the matching leaf rather than its solution root or opposite leaf; it discovers split packages from the presence of both leaf configs, so a new split joins the gate automatically. The [`api-remotes` README](../packages/api/remotes/README.md) explains the Host/Client split and build order. The root build follows the generated dependency order: diff --git a/docs/development.zh.md b/docs/development.zh.md index dc68637bc9..c759f4236e 100644 --- a/docs/development.zh.md +++ b/docs/development.zh.md @@ -47,7 +47,7 @@ pnpm run typecheck ### TypeScript 项目布局 -仓库使用相互隔离的 Host 与 Client aggregate。普通包只登记进其中一个 aggregate;Host 包进入 `tsconfig.host.json`,Client 包进入 `tsconfig.client.json`。 +仓库使用相互隔离的 Host 与 Client aggregate。普通包只登记进其中一个 aggregate;Host 包进入 `tsconfig.host.json`,Client 包进入 `tsconfig.client.json`;`host/webserver`、`compaction/compaction` 与 `typert/registry` 三个包被两个 aggregate 同时引用,作为共享 leaf,让两侧对同一份源码做类型检查。 | 文件 | 角色 | 是否构成 program? | |---|---|---| @@ -61,9 +61,9 @@ Host 与 Client 保持两个 aggregate program,是因为两侧在相同键下 - `tsconfig.base.json` 永不添加 `include` 或 `files`:它们会泄漏进每个 extends 它的包项目,并收窄门面的全匹配范围。 - 构造全仓 `ts.Program` 的脚本显式以 `tsconfig.host.json` 或 `tsconfig.client.json` 为种子——根 solution 永不作为种子,因为把两个 aggregate 展平进一个 program 会撞上 `Context` 合并冲突。 -- 新包只登记进一个 aggregate。包同时具有 Node loader 入口和 browser 入口并不构成拆分理由;普通 Client 插件的两份运行时产物都在 Client 构建阶段生成。 +- 新包只登记进一个 aggregate;只有上述拆分包同时携带两个 leaf 配置,共享 leaf 因两侧需要对同一份源码做类型检查而登记进两个 aggregate。包同时具有 Node loader 入口和 browser 入口并不构成拆分理由;普通 Client 插件的两份运行时产物都在 Client 构建阶段生成。 -`api/remotes` 是唯一拆分 Host/Client tsconfig 的仓库特例。它的 Host 入口必须进入 Host Typert 图,而 Client 入口导入 Host tsdown 才会生成的 `/remote` 声明,因此本包根 `tsconfig.json` 只作为 solution,两个 aggregate 和直接消费方分别引用 `tsconfig.host.json` 或 `tsconfig.client.json`。workspace `constraints` 门禁遍历可达的 Project Reference 图,并按各引用 project 自身的 compiler face 检查:只有单一配置的目标可由任一 face 引用,拆分配置的目标则必须引用匹配的 leaf,不得引用 solution 根或另一侧 leaf;该门禁按「两个 leaf 配置同时存在」自动发现拆分包,所以新拆分的包会自动纳入管辖。不要把该结构推广到其他包;[`api-remotes` README](../packages/api/remotes/README.zh.md) 说明 Host/Client 拆分与构建顺序。 +拆分 Host/Client tsconfig 的包有五个:`api/remotes`、`api/gateway`、`api/session-controller`、`api/workspace-controller` 与 `client/connection`。`api/remotes` 的 Host 入口必须进入 Host Typert 图,而 Client 入口导入 Host tsdown 才会生成的 `/remote` 声明,因此每个拆分包根 `tsconfig.json` 只作为 solution,两个 aggregate 和直接消费方分别引用 `tsconfig.host.json` 或 `tsconfig.client.json`。workspace `constraints` 门禁遍历可达的 Project Reference 图,并按各引用 project 自身的 compiler face 检查:只有单一配置的目标可由任一 face 引用,拆分配置的目标则必须引用匹配的 leaf,不得引用 solution 根或另一侧 leaf;该门禁按「两个 leaf 配置同时存在」自动发现拆分包,所以新拆分的包会自动纳入管辖。[`api-remotes` README](../packages/api/remotes/README.zh.md) 说明 Host/Client 拆分与构建顺序。 根构建按生成依赖排序: diff --git a/docs/glossary.i18n.yaml b/docs/glossary.i18n.yaml index f462345f43..840997c1e3 100644 --- a/docs/glossary.i18n.yaml +++ b/docs/glossary.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/glossary.md -glossary.md: 9bff818d5a9f7688e8d2a2425b6e6a9555be3847 -glossary.zh.md: 98bbbefb8bfd152324b23e9791eb938398c12602 +glossary.md: 891234c487eea37e6f6beb4779c47054dd011168 +glossary.zh.md: d8fef23873d4c0098b774cf846c8fec156c2d269 diff --git a/docs/glossary.md b/docs/glossary.md index 9bff818d5a..891234c487 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -6,7 +6,7 @@ Domain vocabulary for DeepSeek Harness uses one canonical term per concept. Term ## capability-seam -- **seam** — a *swappable capability* with three roles: a **Service Definition** (the Cordis `Service` that owns its `ctx.` and vocabulary types — an abstract class such as `ShellExecutor`, or a concrete registry such as `WebRuntime`, never a TypeScript `interface`), one or more **Service Providers**, and one or more **Consumers** that inject the service. `packages/shell` is the canonical example: `dsh-shell` (Service Definition), `dsh-bash-local` / `dsh-bash-sandbox` (providers), and `dsh-tool-bash` (Consumer). Roles normally occupy separate packages when they evolve independently, but a package may own multiple roles when they are one concern (`dsh-llm` owns its Service Definition and Consumer). The seam is the complete capability, never one role; reserve the term for that meaning and name a constituent by its role, class, service, contract, or extension point. +- **seam** — a *swappable capability* with three roles: a **Service Definition** (the Cordis `Service` that owns its `ctx.` and vocabulary types — an abstract class such as `ShellExecutor`, or a concrete registry such as `WebRuntime`, never a TypeScript `interface`), one or more **Service Providers**, and one or more **Consumers** that inject the service. `packages/shell` is the canonical example: `dsh-shell` (Service Definition), `dsh-bash-local` / `dsh-bash-sandbox` (providers), and `dsh-tool-bash` (Consumer). Roles normally occupy separate packages when they evolve independently, but a package may own multiple roles when they are one concern (`dsh-user-approval` owns the approval seam's Service Definition and its concrete implementation in one package). The seam is the complete capability, never one role; reserve the term for that meaning and name a constituent by its role, class, service, contract, or extension point. ## agent-scope diff --git a/docs/glossary.zh.md b/docs/glossary.zh.md index 98bbbefb8b..d8fef23873 100644 --- a/docs/glossary.zh.md +++ b/docs/glossary.zh.md @@ -6,7 +6,7 @@ DeepSeek Harness 的领域词汇为每个概念规定一个规范术语。各术 ## capability-seam -- **seam**:一种包含三种角色的*可替换能力*:**Service Definition**(拥有自身 `ctx.` 和词汇类型的 Cordis `Service`——可以是 `ShellExecutor` 这样的抽象类,也可以是 `WebRuntime` 这样的具体注册表,绝不是 TypeScript `interface`)、一个或多个 **Service Provider**,以及一个或多个注入该服务的 **Consumer**。`packages/shell` 是规范范例:`dsh-shell`(Service Definition)、`dsh-bash-local` / `dsh-bash-sandbox`(提供方),以及 `dsh-tool-bash`(Consumer)。角色需要独立演进时通常位于不同包,但属于同一关注点时,一个包也可以承担多个角色(`dsh-llm` 同时承担 Service Definition 和 Consumer)。seam 是完整能力,绝不是其中一个角色;该术语仅保留此义,能力成员应按其角色、类、服务、约定或扩展点命名。 +- **seam**:一种包含三种角色的*可替换能力*:**Service Definition**(拥有自身 `ctx.` 和词汇类型的 Cordis `Service`——可以是 `ShellExecutor` 这样的抽象类,也可以是 `WebRuntime` 这样的具体注册表,绝不是 TypeScript `interface`)、一个或多个 **Service Provider**,以及一个或多个注入该服务的 **Consumer**。`packages/shell` 是规范范例:`dsh-shell`(Service Definition)、`dsh-bash-local` / `dsh-bash-sandbox`(提供方),以及 `dsh-tool-bash`(Consumer)。角色需要独立演进时通常位于不同包,但属于同一关注点时,一个包也可以承担多个角色(`dsh-user-approval` 在同一个包中承担 approval seam 的 Service Definition 与其具体实现)。seam 是完整能力,绝不是其中一个角色;该术语仅保留此义,能力成员应按其角色、类、服务、约定或扩展点命名。 ## agent-scope diff --git a/docs/i18n/README.i18n.yaml b/docs/i18n/README.i18n.yaml index 44a4b5f4ec..e5cf47c79c 100644 --- a/docs/i18n/README.i18n.yaml +++ b/docs/i18n/README.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/i18n/README.md -README.md: 71092a518db6b75c25a5c357168ab0b0437050b5 -README.zh.md: 568c987b16e6c88dde16bf4c769f81be264dbbcb +README.md: aa075f588d00543b862583912a687722b434de67 +README.zh.md: 08f4a1d1854d56c47b0bd9bbf0611304c892df90 diff --git a/docs/i18n/README.md b/docs/i18n/README.md index 71092a518d..aa075f588d 100644 --- a/docs/i18n/README.md +++ b/docs/i18n/README.md @@ -15,7 +15,7 @@ This repo's documentation is read by people and agents both inside and outside t foo.zh.md: 89e6c98d92887913cadf06b2adb97f26cde4849b ``` - Blob hashes, not commit hashes, so the record is computable for files edited in the same PR (`git hash-object foo.md`) and consistency is a pure content comparison. `--write` stores those snapshots in the local Git object database before recording them, including uncommitted working-tree contents, and pins every distinct stored blob under a content-addressed `refs/dsh/translation-pairing/snapshots/` ref so garbage collection cannot invalidate a recorded recovery pointer. The recorded hashes therefore recover the exact last-confirmed text of either side, so an out-of-sync pair is updated by patching the counterpart minimally against the edited side's diff — never by re-translating whole files. Routine work makes that patch directly; when the user explicitly invokes the extended workflow, `pnpm run gen-translation-brief ` can instead assemble the update at the narrowest safely aligned granularity and `--apply` can splice a code-fence-only change after structural validation ([briefed-updates Agent Note](../../.agents/notes/implemented/process/2026-07-26-briefed-minimal-translation-updates.md)). After bringing the pair back in line, `pnpm run verify-translation-pairing --write ` re-records both hashes; that yaml diff is the reviewable act of confirming consistency, which is why `--write` requires naming the pairs you confirmed (`--write --all` is the explicit corpus-wide form). + Blob hashes, not commit hashes, so the record is computable for files edited in the same PR (`git hash-object foo.md`) and consistency is a pure content comparison. `--write` stores those snapshots in the local Git object database before recording them, including uncommitted working-tree contents, and pins every distinct stored blob under a content-addressed `refs/dsh/translation-pairing/snapshots/` ref so garbage collection cannot invalidate a recorded recovery pointer. The recorded hashes recover the exact last-confirmed text of either side, so an out-of-sync pair is updated by patching the counterpart minimally against the edited side's diff — never by re-translating whole files. Routine work makes that patch directly; when the user explicitly invokes the extended workflow, `pnpm run gen-translation-brief ` can instead assemble the update at the narrowest safely aligned granularity and `--apply` can splice a code-fence-only change after structural validation ([briefed-updates Agent Note](../../.agents/notes/implemented/process/2026-07-26-briefed-minimal-translation-updates.md)). After bringing the pair back in line, `pnpm run verify-translation-pairing --write ` re-records both hashes; that yaml diff is the reviewable act of confirming consistency, which is why `--write` requires naming the pairs you confirmed (`--write --all` is the explicit corpus-wide form). When two branches contain valid confirmations of the same pair, the installed `dsh-translation-pairing` Git merge driver composes a new record only if Git's default text merge succeeds for both recorded owner-blob triplets and the merged pair retains its required switchers and structural signature. The Chinese file must retain its English backlink; an authored English source must retain its Chinese link, while a listed generated English source is exempt. Any structure the driver cannot verify remains an ordinary conflict; `pnpm run resolve-translation-pairing-conflicts` applies the same fail-closed operation to a merge that has already stopped, stages every safe pairing record, and exits unsuccessfully when other pairing conflicts remain. The [automatic pairing merges Agent Note](../../.agents/notes/implemented/process/2026-08-08-automatic-translation-pairing-merges.md) owns the mechanism and alternatives. - **Language switcher.** The Chinese file always links back immediately after its H1 heading with `[English](foo.md) | 中文`. An authored English file reciprocates there with `English | [中文](foo.zh.md)`; a listed generated English source omits that line so it remains byte-identical to generator output. A README published outside GitHub, such as PyPI project metadata, may use the canonical `https://github.com/deepseek-ai/deepseek-harness/blob/master/` URL to the same counterpart so the switcher still resolves there. @@ -37,7 +37,7 @@ Source-oriented code gates consume an exact `.zh.md` fence sequence as a derivat The practical rule this gate creates: **when a PR edits either side of a paired document, the same PR updates the counterpart directly in one terminology-guided pass and re-records the pair with `--write `**, exactly like the repo's existing doc-sync rule for code and READMEs. A PR that leaves a pair out of sync goes red in CI. -The gate's limit, stated plainly: **a green gate means the pair was confirmed consistent at these exact contents, not that the confirmation was sound.** It checks hashes and Markdown structure; it cannot judge whether the two sides actually say the same thing, or whether the wording is accurate, well-termed, and natural — that is the reviewer's half of the contract, per [translation-rules.md](translation-rules.md). A re-recorded pair with a sloppy counterpart passes the gate; it must not pass review. +The gate's limit, stated plainly: **a green gate means the pair was confirmed consistent at these exact contents, not that the confirmation was sound.** It checks hashes and Markdown structure; it cannot judge whether the two sides say the same thing, or whether the wording is accurate, well-termed, and natural — that is the reviewer's half of the contract, per [translation-rules.md](translation-rules.md). A re-recorded pair with a sloppy counterpart passes the gate; it must not pass review. ## Scope and exclusions @@ -57,4 +57,4 @@ Generated English references and graphs participate in pairing when a reviewed C ## Division of labor -Routine counterparts are updated directly by the working agent in one shot and one pass after it loads [terminology.md](terminology.md); it does not invoke a translation skill, generate a briefing, run a separate translation-review pass, or delegate to a subagent. The extended [dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md) workflow retains those heavier mechanisms for explicit user invocation. The gate checks pair completeness, recorded hashes, the Chinese backlink and authored-source switcher (with the documented generated-source exception), and its documented structural signature. Review still owns translation quality, terminology, and structural requirements that the signature does not encode. The prompt contract is executable: [scripts/translation-prompt.ts](../../scripts/translation-prompt.ts) renders the committed template (terminology injected; the template carries its own calibrated rules) into either direction and parses the three-section response, while `verify-translation-prompt` exercises both render directions and the checked-in example in `doc-sync`. +Routine counterparts are updated directly by the working agent in one pass after it loads [terminology.md](terminology.md); it does not invoke a translation skill, generate a briefing, run a separate translation-review pass, or delegate to a subagent. The extended [dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md) workflow retains those heavier mechanisms for explicit user invocation. The gate checks pair completeness, recorded hashes, the Chinese backlink and authored-source switcher (with the documented generated-source exception), and its documented structural signature. Review still owns translation quality, terminology, and structural requirements that the signature does not encode. The prompt contract is executable: [scripts/translation-prompt.ts](../../scripts/translation-prompt.ts) renders the committed template (terminology injected; the template carries its own calibrated rules) into either direction and parses the three-section response, while `verify-translation-prompt` exercises both render directions and the checked-in example in `doc-sync`. diff --git a/docs/i18n/README.zh.md b/docs/i18n/README.zh.md index 568c987b16..08f4a1d185 100644 --- a/docs/i18n/README.zh.md +++ b/docs/i18n/README.zh.md @@ -17,7 +17,7 @@ foo.zh.md: 89e6c98d92887913cadf06b2adb97f26cde4849b ``` - 用 blob hash 而不是 commit hash,这样同一个 PR 里改动的文件也能算出记录(`git hash-object foo.md`),一致性是纯内容比较。`--write` 会先把这些快照存入本地 Git 对象库再写下记录,未提交的 worktree 内容也不例外;它还会在内容寻址的 `refs/dsh/translation-pairing/snapshots/` ref 下固定每个不同的已存 blob,使垃圾回收无法让已记录的恢复指针失效。因此记录的 hash 能还原任一侧上次确认时的确切文本,所以失去同步的配对是「按被改一侧的 diff 最小化地修补另一侧」,从不整篇重译。日常工作会直接完成这份修补;用户显式调用扩展工作流时,可改由 `pnpm run gen-translation-brief ` 以能安全对齐的最窄粒度汇集这次更新,并由 `--apply` 在结构校验后拼接仅涉及围栏代码块的改动([briefed-updates Agent Note](../../.agents/notes/implemented/process/2026-07-26-briefed-minimal-translation-updates.zh.md))。两侧对齐后,`pnpm run verify-translation-pairing --write ` 重新记录两个 hash;那份 YAML diff 就是「确认一致」这个动作本身,可以被评审,也正因如此,`--write` 要求点名你确认过的配对(`--write --all` 是显式的全语料形式)。 + 用 blob hash 而不是 commit hash,这样同一个 PR 里改动的文件也能算出记录(`git hash-object foo.md`),一致性是纯内容比较。`--write` 会先把这些快照存入本地 Git 对象库再写下记录,未提交的 worktree 内容也不例外;它还会在内容寻址的 `refs/dsh/translation-pairing/snapshots/` ref 下固定每个不同的已存 blob,使垃圾回收无法让已记录的恢复指针失效。记录的 hash 能还原任一侧上次确认时的确切文本,所以失去同步的配对是「按被改一侧的 diff 最小化地修补另一侧」,从不整篇重译。日常工作会直接完成这份修补;用户显式调用扩展工作流时,可改由 `pnpm run gen-translation-brief ` 以能安全对齐的最窄粒度汇集这次更新,并由 `--apply` 在结构校验后拼接仅涉及围栏代码块的改动([briefed-updates Agent Note](../../.agents/notes/implemented/process/2026-07-26-briefed-minimal-translation-updates.zh.md))。两侧对齐后,`pnpm run verify-translation-pairing --write ` 重新记录两个 hash;那份 YAML diff 就是「确认一致」这个动作本身,可以被评审,也正因如此,`--write` 要求点名你确认过的配对(`--write --all` 是显式的全语料形式)。 当两个分支都包含同一配对的有效确认时,已安装的 `dsh-translation-pairing` Git 合并驱动只会在 Git 默认文本合并能分别干净合并记录所指向的英文三方 blob 与中文三方 blob,且合并后的配对仍保留必需的语言切换行和结构签名时,组合出一份新记录。中文文件必须保留指向英文的反向链接;普通撰写的英文源必须保留指向中文的链接,而清单内的生成英文源不作此要求。任何合并驱动无法验证的结构都保留为普通冲突;`pnpm run resolve-translation-pairing-conflicts` 会对已经停止的合并执行同一套遇错即保留冲突的操作,暂存每份可安全生成的配对记录,并在还有其他配对冲突时以非零状态退出。[自动配对合并 Agent Note](../../.agents/notes/implemented/process/2026-08-08-automatic-translation-pairing-merges.zh.md) 负责记录该机制与备选方案。 - **语言切换行。** 中文文件一律在 H1 标题后立即以 `[English](foo.md) | 中文` 链回英文。普通撰写的英文文件在同一位置以 `English | [中文](foo.zh.md)` 互链;清单内的生成英文源省略此行,以便与生成器输出逐字节一致。发布到 GitHub 以外位置的 README(例如 PyPI 项目元数据)可以改用指向同一对侧文件的规范 `https://github.com/deepseek-ai/deepseek-harness/blob/master/` URL,使切换行在该位置仍可访问。 @@ -39,7 +39,7 @@ 这个门禁带来的实际规则是:**当一个 PR 修改了已配对文档的任一侧时,同一个 PR 在术语指导下直接一次完成对侧文件的更新,并用 `--write ` 重新记录配对**,与本仓库既有的代码与 README 的 doc-sync 规则完全一致。留下失去同步的配对的 PR 会在 CI 变红。 -门禁的限制很明确:**门禁通过意味着这组文档在当前内容上的一致性得到了确认,不代表确认本身正确可靠。** 它检查记录的 hash 与 Markdown 结构;它无法判断两侧是否真的在说同样的话,也无法判断措辞是否准确、术语是否得当、行文是否自然;这部分约定由评审者把关,见 [translation-rules.md](translation-rules.zh.md)。重新记录了 hash 但另一侧翻得潦草的配对能通过门禁;它不得通过评审。 +门禁的限制很明确:**门禁通过意味着这组文档在当前内容上的一致性得到了确认,不代表确认本身正确可靠。** 它检查记录的 hash 与 Markdown 结构;它无法判断两侧是否在说同样的话,也无法判断措辞是否准确、术语是否得当、行文是否自然;这部分约定由评审者把关,见 [translation-rules.md](translation-rules.zh.md)。重新记录了 hash 但另一侧翻得潦草的配对能通过门禁;它不得通过评审。 ## 范围与排除 @@ -59,4 +59,4 @@ ## 分工 -日常更新对侧文件时,负责处理的 agent 会先加载 [terminology.md](terminology.md),再直接一次性更新且只处理一遍;它不会调用翻译 skill(技能)、生成简报、执行单独的翻译评审轮次,也不会委派给 subagent。扩展版 [dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md) 工作流保留这些较重的机制,仅供用户显式调用。门禁负责检查配对是否完整、记录的 hash、中文反向链接和普通撰写源的切换行(生成源按本文规则例外),以及本文列出的结构签名;翻译质量、术语和签名未涵盖的结构要求仍由评审把关。提示词约定也有可执行实现:[scripts/translation-prompt.ts](../../scripts/translation-prompt.ts) 会把仓库内置的模板(注入术语表;模板自带经人工校准的规则)渲染为英译中或中译英两个方向的提示词,并解析三段式响应;`doc-sync` 中的 `verify-translation-prompt` 会检查两个渲染方向与仓库内示例。 +日常更新对侧文件时,负责处理的 agent 会先加载 [terminology.md](terminology.md),再直接一次性更新;它不会调用翻译 skill(技能)、生成简报、执行单独的翻译评审轮次,也不会委派给 subagent。扩展版 [dsh-translate-docs](../../.agents/skills/dsh-translate-docs/SKILL.md) 工作流保留这些较重的机制,仅供用户显式调用。门禁负责检查配对是否完整、记录的 hash、中文反向链接和普通撰写源的切换行(生成源按本文规则例外),以及本文列出的结构签名;翻译质量、术语和签名未涵盖的结构要求仍由评审把关。提示词约定也有可执行实现:[scripts/translation-prompt.ts](../../scripts/translation-prompt.ts) 会把仓库内置的模板(注入术语表;模板自带经人工校准的规则)渲染为英译中或中译英两个方向的提示词,并解析三段式响应;`doc-sync` 中的 `verify-translation-prompt` 会检查两个渲染方向与仓库内示例。 diff --git a/docs/postmortem/0002-js-expression-disabled-filesystem-tools.i18n.yaml b/docs/postmortem/0002-js-expression-disabled-filesystem-tools.i18n.yaml index 6af53725af..b3417317ac 100644 --- a/docs/postmortem/0002-js-expression-disabled-filesystem-tools.i18n.yaml +++ b/docs/postmortem/0002-js-expression-disabled-filesystem-tools.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/postmortem/0002-js-expression-disabled-filesystem-tools.md -0002-js-expression-disabled-filesystem-tools.md: d5ead7e32d84c4ca3bda192b7b7ebd3dd618242e -0002-js-expression-disabled-filesystem-tools.zh.md: bfece9652caa6b8bbe666f01b94df32240f11fdc +0002-js-expression-disabled-filesystem-tools.md: 317b9759afda90b3864619f874f99e5a7047395f +0002-js-expression-disabled-filesystem-tools.zh.md: 1c87796c66309be28a17a6c0e71e48ae2e2fda40 diff --git a/docs/postmortem/0002-js-expression-disabled-filesystem-tools.md b/docs/postmortem/0002-js-expression-disabled-filesystem-tools.md index d5ead7e32d..317b9759af 100644 --- a/docs/postmortem/0002-js-expression-disabled-filesystem-tools.md +++ b/docs/postmortem/0002-js-expression-disabled-filesystem-tools.md @@ -36,7 +36,7 @@ The snapshot framework treated any deterministic transcript as valid behavior. H ## Guardrails added - Filesystem scenarios boot `fs.cordis.yml`, an explicit fixed full-access overlay with a paired replay config and its own request-header class. -- [`AGENTS.md`](../../AGENTS.md) and the [Cordis primer](../cordis-primer.md#loader-configuration) state that `!!js` is valid only under plugin `config` and conditional composition uses overlays. +- [`AGENTS.md`](../../AGENTS.md) and the [Cordis primer](../cordis-primer.md#loader-configuration) state that `!!js` is valid under plugin `config` and entry `disabled`; other entry metadata stays literal, so conditional composition uses overlays. - `verify-cordis-config` parses repository Cordis YAML and rejects expression nodes in Loader entry metadata, including include patches and inserted entries. - `dsh-session-snapshot` rejects structured `UNKNOWN_TOOL` results in fresh runs and committed session fixtures before they can be committed as expected outputs. diff --git a/docs/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md b/docs/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md index bfece9652c..1c87796c66 100644 --- a/docs/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +++ b/docs/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md @@ -36,7 +36,7 @@ Cordis Include 将每个 `!!js` 标量解析为一个表达式对象。Loader ## 已添加的防护措施 - 文件系统场景启动 `fs.cordis.yml`:一个显式的固定全权限 overlay,配有对应的回放配置和独立的 request-header 类。 -- [`AGENTS.md`](../../AGENTS.md) 与 [Cordis 入门](../cordis-primer.zh.md#loader-configuration)明确说明 `!!js` 仅在插件 `config` 内有效,条件式组合应使用 overlay。 +- [`AGENTS.md`](../../AGENTS.md) 与 [Cordis 入门](../cordis-primer.zh.md#loader-configuration)明确说明 `!!js` 在插件 `config` 与配置项 `disabled` 内有效;其他配置项元数据保持字面量,因此条件式组合使用 overlay。 - `verify-cordis-config` 解析仓库中的 Cordis YAML,拒绝 Loader 配置项元数据中的表达式节点(包括 include patch 和插入的配置项)。 - `dsh-session-snapshot` 在全新运行和已提交的会话 fixture 中拒绝结构化的 `UNKNOWN_TOOL` 结果,防止其被提交为预期输出。 diff --git a/docs/postmortem/0003-web-agent-gui-feedback-loop.i18n.yaml b/docs/postmortem/0003-web-agent-gui-feedback-loop.i18n.yaml index 2dd63a4a71..88081f5426 100644 --- a/docs/postmortem/0003-web-agent-gui-feedback-loop.i18n.yaml +++ b/docs/postmortem/0003-web-agent-gui-feedback-loop.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/postmortem/0003-web-agent-gui-feedback-loop.md -0003-web-agent-gui-feedback-loop.md: 45995a5dde03e4af0aa5b09bc16670c29129100d -0003-web-agent-gui-feedback-loop.zh.md: c3e67179eefe48265e14fc47ffa57f75d3cf423c +0003-web-agent-gui-feedback-loop.md: 3b836764dcf47758a6a0b472d92484f39b9ff510 +0003-web-agent-gui-feedback-loop.zh.md: 402fe05341f9ab568329c30e9db0ebde97a5753b diff --git a/docs/postmortem/0003-web-agent-gui-feedback-loop.md b/docs/postmortem/0003-web-agent-gui-feedback-loop.md index 45995a5dde..3b836764dc 100644 --- a/docs/postmortem/0003-web-agent-gui-feedback-loop.md +++ b/docs/postmortem/0003-web-agent-gui-feedback-loop.md @@ -39,8 +39,8 @@ Background process semantics were also bypassed with shell `&`, so job identity, ## Guardrails added -- The Web launcher publishes the canonical loopback URL and actual production/development mode in the logged `app:web-surface` prompt section and managed `$DSH_WEB_URL`/`$DSH_WEB_MODE` environment. -- Production guidance requires rebuilding artifacts and verifying the existing URL after refresh. Development guidance explains that `dsh web --dev` mounts only the HMR receiver; `pnpm run dev:web` in the same checkout must also rebuild client-plugin bundles, while shell and plain-package changes still require refresh. +- The Web launcher publishes the canonical loopback URL in the logged `app:web-surface` prompt section and the managed `$DSH_WEB_URL` environment. +- Production guidance requires rebuilding artifacts and verifying the existing URL after refresh. Development guidance explains that the HMR receiver is always on; `pnpm run dev:web` in the same checkout rebuilds client-plugin bundles for refresh-free reload, while shell and plain-package changes still require refresh. - `apps/web` standalone Vite serve mode rejects during configuration. Its subprocess test proves natural exit and instruments `Server.listen()` so a transient bind cannot pass unnoticed. - Layered real-path tests cover the CLI request, exact production/development prompts, shell runtime facts, same-port static replacement, source watcher rebuild, host stat polling, and browser HMR under an unchanged page identity. - PR evidence preserves screenshots from the original 3081 session and a real-model before/after GUI run; external browser, HTTP, process, and session-log observations carry acceptance. diff --git a/docs/postmortem/0003-web-agent-gui-feedback-loop.zh.md b/docs/postmortem/0003-web-agent-gui-feedback-loop.zh.md index c3e67179ee..402fe05341 100644 --- a/docs/postmortem/0003-web-agent-gui-feedback-loop.zh.md +++ b/docs/postmortem/0003-web-agent-gui-feedback-loop.zh.md @@ -39,8 +39,8 @@ agent 还通过 shell `&` 绕过了后台进程语义,因此任务身份、完 ## 已添加的防护措施 -- Web 启动器在记录到日志的 `app:web-surface` 提示词区段,以及受管的 `$DSH_WEB_URL`/`$DSH_WEB_MODE` 环境变量中,发布规范环回 URL 和实际的生产/开发模式。 -- 生产模式指南要求重新构建产物,并在刷新后验证既有 URL。开发模式指南说明,`dsh web --dev` 只挂载 HMR 接收端;同一源码检出目录中的 `pnpm run dev:web` 还必须重新构建客户端插件 bundle,而 Web shell 和普通包的改动仍然需要刷新页面。 +- Web 启动器在记录到日志的 `app:web-surface` 提示词区段和受管的 `$DSH_WEB_URL` 环境变量中发布规范环回 URL。 +- 生产指南要求重新构建产物,并在刷新后验证既有 URL。开发指南说明 HMR 接收端始终开启;同一源码检出目录中的 `pnpm run dev:web` 会重新构建客户端插件 bundle,实现免刷新的重载,而 Web shell 和普通包的改动仍然需要刷新页面。 - `apps/web` 的独立 Vite 服务模式会在配置阶段拒绝启动。其子进程测试验证进程自然退出,并插桩 `Server.listen()`,确保短暂绑定端口也不会漏检。 - 分层的真实路径测试覆盖 CLI(命令行界面)请求、精确的生产/开发模式提示词、shell 运行时事实、同端口静态产物替换、源码 watcher 重建、宿主 stat 轮询,以及页面 identity 不变的浏览器 HMR。 - PR(Pull Request)证据保留了原始 3081 会话的截图,以及真实模型驱动的 GUI 修改前后对比;验收以外部浏览器、HTTP、进程和会话日志的观测结果为准。 diff --git a/docs/rescope.i18n.yaml b/docs/rescope.i18n.yaml index 4daf4ad73d..526de65763 100644 --- a/docs/rescope.i18n.yaml +++ b/docs/rescope.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/rescope.md -rescope.md: 3dde39875021e7a4161e1ae66550e9dedf5eb4fa -rescope.zh.md: 6ee834e33dd50a16c04e3253368de165b8a46100 +rescope.md: bfcc150bcbab3abf695e6ca2a75ea149b9dcbeea +rescope.zh.md: bc4f72250249a14209156765962ceb94d9d643b4 diff --git a/docs/rescope.md b/docs/rescope.md index 3dde398750..bfcc150bcb 100644 --- a/docs/rescope.md +++ b/docs/rescope.md @@ -6,7 +6,7 @@ The Cordis framework and its foundation libraries are vendored under [`vendor/`] ## Name mapping -| Directory | Upstream name | Published name | Version | Role | +| Directory | Upstream name | Published name | Upstream version | Role | |---|---|---|---|---| | `vendor/cordis/` | `cordis` | `@deepseek-ai/cordis` | 4.0.0-rc.7 | Framework core: `Context`, `Service`, `Fiber`, events | | `vendor/cosmokit/` | `cosmokit` | `@deepseek-ai/cosmokit` | 1.8.1 | Shared utilities the framework and Schemastery build on | @@ -22,7 +22,7 @@ Subpath exports keep their path: `@cordisjs/plugin-loader/repository` becomes `@ ## What the rename does not touch -- **Directory names and versions.** `vendor/hmr/` stays `vendor/hmr/`, and every package keeps the upstream version its manifest table row records, so the vendored tree still reads as an upstream snapshot. +- **Directory names and upstream source versions.** `vendor/hmr/` stays `vendor/hmr/`, and the table records the upstream version of the pinned source snapshot, so the manifest reads as an upstream snapshot; the vendored `package.json`'s own `version` field is the harness's released manifest version, which `pnpm run release:vendor` bumps and a re-sync restores to the upstream version. - **Dependency ranges.** A dependency entry changes its key, never its range: `"cordis": "^4.0.0-rc.7"` becomes `"@deepseek-ai/cordis": "^4.0.0-rc.7"`. `linkWorkspacePackages` resolves those preserved ranges to the pinned workspaces. - **The Loader's `cordis:` builtin prefix.** `cordis:include` and `cordis:group` are a protocol prefix, not a package name. - **The `cordis.yml` configuration family**, including `*.cordis.yml`, `*.cordis.snapshot.yml`, and `cordis.patch.yml`. diff --git a/docs/rescope.zh.md b/docs/rescope.zh.md index 6ee834e33d..bc4f722502 100644 --- a/docs/rescope.zh.md +++ b/docs/rescope.zh.md @@ -6,7 +6,7 @@ Cordis 框架及其基础库以源码形式 vendored 在 [`vendor/`](../vendor/R ## 名字映射 -| 目录 | 上游名 | 发布名 | 版本 | 角色 | +| 目录 | 上游名 | 发布名 | 上游版本 | 角色 | |---|---|---|---|---| | `vendor/cordis/` | `cordis` | `@deepseek-ai/cordis` | 4.0.0-rc.7 | 框架核心:`Context`、`Service`、`Fiber`、事件 | | `vendor/cosmokit/` | `cosmokit` | `@deepseek-ai/cosmokit` | 1.8.1 | 框架与 Schemastery 共用的基础工具 | @@ -22,7 +22,7 @@ Cordis 框架及其基础库以源码形式 vendored 在 [`vendor/`](../vendor/R ## 改名不碰什么 -- **目录名与版本号。** `vendor/hmr/` 仍是 `vendor/hmr/`,每个包保留清单表那行记录的上游版本,所以 vendored 树依旧读作一份上游快照。 +- **目录名与上游源码版本。** `vendor/hmr/` 仍是 `vendor/hmr/`,清单表记录的是所钉住源码快照的上游版本,因此清单读作一份上游快照;而每个 vendored 包 `package.json` 自身的 `version` 字段是 harness 发布的清单版本,`pnpm run release:vendor` 会提升它,重新 sync 时会恢复成上游版本。 - **依赖 range。** 依赖条目只换键、不换范围:`"cordis": "^4.0.0-rc.7"` 变成 `"@deepseek-ai/cordis": "^4.0.0-rc.7"`;`linkWorkspacePackages` 靠这些保留下来的范围把它们解析到固定的 workspace。 - **Loader 的 `cordis:` 内建前缀。** `cordis:include`、`cordis:group` 是协议前缀,不是包名。 - **`cordis.yml` 配置文件家族**,包括 `*.cordis.yml`、`*.cordis.snapshot.yml`、`cordis.patch.yml`。 diff --git a/docs/subsystems/README.i18n.yaml b/docs/subsystems/README.i18n.yaml index e43820301d..f6341b0335 100644 --- a/docs/subsystems/README.i18n.yaml +++ b/docs/subsystems/README.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/README.md -README.md: 2b277650c4b9e320183f75845d68f922b31a522e -README.zh.md: 2279857e0d58cb36e8027c196075622344d705ae +README.md: f3340ebeb426fe9cc5c4570b3c8a3440cc1682ec +README.zh.md: 2e4523f03d1a942d9f93abf34ad2cc8687b413b8 diff --git a/docs/subsystems/README.md b/docs/subsystems/README.md index 2b277650c4..f3340ebeb4 100644 --- a/docs/subsystems/README.md +++ b/docs/subsystems/README.md @@ -15,7 +15,7 @@ One page per subsystem of the DeepSeek Harness: what it is, the data structures | [schedule.md](schedule.md) | Session-local reminder records, durable transitions, active views, and ordinary-conversation delivery | | [todo.md](todo.md) | the todo package's whole-list item type, durable event ownership, projection, and open-turn invariant | | [commands.md](commands.md) | the human-command registry service: definitions, adapter discovery, direct invocation, results, and parsing views | -| [session.md](session.md) | the full `SessionEventMap` variant catalog, `TurnTrigger`/`TurnEndReason`, `deriveMessages()`, execution enclosure, and standalone events | +| [session.md](session.md) | the full `SessionEventMap` variant catalog, `TurnEndReason`, `deriveMessages()`, execution enclosure, and standalone events | | [persistence.md](persistence.md) | the durability seam: `SessionPersistence`, JSONL + SQLite backends, `session/flush`, crash recovery, `SessionHeader` | | [settings.md](settings.md) | the user-settings seam: `SettingsNamespace` registration, layered resolution (defaults → composition `base` → user document), owner scopes, hot commits | | [credentials.md](credentials.md) | the credential seam: `CredentialRef` references (never values) in configuration, per-operation resolution, UI-safe `CredentialInfo`, provider source layers | diff --git a/docs/subsystems/README.zh.md b/docs/subsystems/README.zh.md index 2279857e0d..2e4523f03d 100644 --- a/docs/subsystems/README.zh.md +++ b/docs/subsystems/README.zh.md @@ -15,7 +15,7 @@ | [schedule.md](schedule.zh.md) | 仅限 Session 内的提醒记录、持久转换、活动视图与普通对话交付 | | [todo.md](todo.zh.md) | todo 包的整列表条目类型、持久事件所有权、投影和开放轮次不变量 | | [commands.md](commands.zh.md) | 人类命令注册表服务:定义、适配器发现、直接调用、结果与解析视图 | -| [session.md](session.zh.md) | 完整的 `SessionEventMap` 变体目录、`TurnTrigger`/`TurnEndReason`、`deriveMessages()`、执行封闭与独立事件 | +| [session.md](session.zh.md) | 完整的 `SessionEventMap` 变体目录、`TurnEndReason`、`deriveMessages()`、执行封闭与独立事件 | | [persistence.md](persistence.zh.md) | 持久性 seam:`SessionPersistence`、JSONL + SQLite 后端、`session/flush`、崩溃恢复、`SessionHeader` | | [settings.md](settings.zh.md) | 用户设置 seam:`SettingsNamespace` 注册、分层解析(默认值 → 组合 `base` → 用户文档)、owner scope、热提交 | | [credentials.md](credentials.zh.md) | 凭据 seam:配置中的 `CredentialRef` 引用(绝不含值)、按操作解析、对 UI 安全的 `CredentialInfo`、提供方来源层 | diff --git a/docs/subsystems/agent-team.i18n.yaml b/docs/subsystems/agent-team.i18n.yaml index 21c926c520..1aaeea97b7 100644 --- a/docs/subsystems/agent-team.i18n.yaml +++ b/docs/subsystems/agent-team.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/agent-team.md -agent-team.md: d3cca68124bfa199c4a81d6c67c6305ce7f65b6c -agent-team.zh.md: 773e734a55cf5282fc9507a754d2d4dff8cb5978 +agent-team.md: 8704bff3071d4e084e95dafcd61b7eea65278f61 +agent-team.zh.md: dfa55743e5ddbde054f87180edd37a788af84663 diff --git a/docs/subsystems/agent-team.md b/docs/subsystems/agent-team.md index d3cca68124..8704bff307 100644 --- a/docs/subsystems/agent-team.md +++ b/docs/subsystems/agent-team.md @@ -74,7 +74,7 @@ interface TeamTaskSnapshot { ## Replay -`foldTeam()` replays one root Session into the roster, task board, and queued-minus-delivered mailbox that every Team operation reads. It selects records by `TeamId`, so events inherited by an ordinary fork retain the ancestor id and never enter the new root's state. Session event `seq` and `time` remain the ordering and timing record; Team snapshots do not duplicate them. Roster and task reads reach callers as views that add owner name, readiness, and write-scope warnings, while pending mail stays internal to delivery and recovery. The package [README](../../packages/experimental/agent-team/README.md) owns operation, authorization, recovery, and limit behavior. +`foldTeam()` replays one root Session into the roster, task board, and queued-minus-delivered mailbox that every Team operation reads. It selects records by `TeamId`, so events inherited by an ordinary fork retain the ancestor id and never enter the new root's state. Session event `seq` and `time` remain the ordering and timing record; Team snapshots do not duplicate them. Roster and task reads reach callers as views; pending mail stays internal to delivery and recovery. The package [README](../../packages/experimental/agent-team/README.md) owns operation, authorization, recovery, and limit behavior. diff --git a/docs/subsystems/agent-team.zh.md b/docs/subsystems/agent-team.zh.md index 773e734a55..dfa55743e5 100644 --- a/docs/subsystems/agent-team.zh.md +++ b/docs/subsystems/agent-team.zh.md @@ -74,7 +74,7 @@ interface TeamTaskSnapshot { ## 回放 -`foldTeam()` 把一个 Root Session 回放成每个 Team 操作所读取的 roster、任务板与 queued-minus-delivered mailbox。它按 `TeamId` 选取记录,因此普通 fork 继承的 event 保留 ancestor id,绝不会进入新 Root 的状态。Session event 的 `seq` 与 `time` 继续负责顺序和时间记录,Team snapshot 不再重复保存它们。roster 与 task 读取以 view 形式到达调用方,附带 owner name、readiness 与 write-scope 警告,而 pending 邮件仅供投递与恢复内部使用。包 [README](../../packages/experimental/agent-team/README.zh.md)负责 operation、authorization、recovery 和限制行为。 +`foldTeam()` 把一个 Root Session 回放成每个 Team 操作所读取的 roster、任务板与 queued-minus-delivered mailbox。它按 `TeamId` 选取记录,因此普通 fork 继承的 event 保留 ancestor id,绝不会进入新 Root 的状态。Session event 的 `seq` 与 `time` 继续负责顺序和时间记录,Team snapshot 不再重复保存它们。roster 与 task 读取以 view 形式到达调用方,而 pending 邮件仅供投递与恢复内部使用。包 [README](../../packages/experimental/agent-team/README.zh.md)负责 operation、authorization、recovery 和限制行为。 diff --git a/docs/subsystems/compaction.i18n.yaml b/docs/subsystems/compaction.i18n.yaml index 6ddba3abd7..b8937331d1 100644 --- a/docs/subsystems/compaction.i18n.yaml +++ b/docs/subsystems/compaction.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/compaction.md -compaction.md: 4b6e9dee81cb30d42bb6194776457e354102b529 -compaction.zh.md: a9c57b64798be1d0361125eac68d589b3e0f1c66 +compaction.md: 019912531636848404a33d772fec3131cafa0287 +compaction.zh.md: 9acc4d8250d0274be525d826e4391dce1a606392 diff --git a/docs/subsystems/compaction.md b/docs/subsystems/compaction.md index 4b6e9dee81..0199125316 100644 --- a/docs/subsystems/compaction.md +++ b/docs/subsystems/compaction.md @@ -83,7 +83,7 @@ type ManualCompactionErrorCode = `changed` and `summary` leave the conversation surface unchanged but still close and persist the failed attempt in the log. `commit` may follow partial mutation; `persistence` means the in-memory bracket closed but its flush failed. Cancellation remains separate and throws the exact abort reason after required cleanup. -Pressure compaction runs at serial `agent/pre-step` before request derivation. Once pressure or canonical overflow qualifies, compaction-basic invokes optional [`ctx.toolResultPruner`](../../packages/compaction/compaction-tool-result-pruner/README.md) before range selection, remeasures through `ctx.tokenMeter`, and can advance the surface without a summary. Failed-request recovery runs through `agent/request-error` after the failed step closes and returns a retry action only when the surface replacement generation advances, even if later summary work throws after pruning; cancellation still wins. Region boundaries preserve tool-call/result pairing but not whole turns, allowing early closed steps of one oversized turn to compact. `dsh-compaction-basic` owns thresholds, retained-tail policy, overflow caps, and failure handling. +Pressure compaction runs at the `agent/pre-step` waterfall before request derivation. Once pressure or canonical overflow qualifies, compaction-basic invokes optional [`ctx.toolResultPruner`](../../packages/compaction/compaction-tool-result-pruner/README.md) before range selection, remeasures through `ctx.tokenMeter`, and can advance the surface without a summary. Failed-request recovery runs through `agent/request-error` after the failed step closes and returns a retry action only when the surface replacement generation advances, even if later summary work throws after pruning; cancellation still wins. Region boundaries preserve tool-call/result pairing but not whole turns, allowing early closed steps of one oversized turn to compact. `dsh-compaction-basic` owns thresholds, retained-tail policy, overflow caps, and failure handling. The Service Definition exports `toolPairingBalancedBefore(session, seq)` and `toolPairingBalancedAfter(session, seq)` for the tool-call/result pairing checks before and after a seq. Both validate current surface membership and reject missing seqs and orphan results; the [package contract](../../packages/compaction/compaction/README.md#tool-pairing-boundaries) defines their cache behavior. diff --git a/docs/subsystems/compaction.zh.md b/docs/subsystems/compaction.zh.md index a9c57b6479..9acc4d8250 100644 --- a/docs/subsystems/compaction.zh.md +++ b/docs/subsystems/compaction.zh.md @@ -83,7 +83,7 @@ type ManualCompactionErrorCode = `changed` 和 `summary` 保持会话表层不变,但仍会闭合失败尝试并将其持久化到日志。`commit` 可能发生在部分变更之后;`persistence` 表示内存中的标记对已闭合,但 flush 失败。取消独立于这些失败,并在完成必要清理后抛出原始 abort 原因。 -压力压缩在串行 `agent/pre-step` 中运行,先于请求推导。一旦压力或规范化溢出满足条件,compaction-basic 会在选择范围前调用可选的 [`ctx.toolResultPruner`](../../packages/compaction/compaction-tool-result-pruner/README.zh.md),再通过 `ctx.tokenMeter` 重新测量,并且可以在不生成摘要的情况下推进 surface。失败请求的恢复在失败的步骤关闭后通过 `agent/request-error` 运行;仅当 surface replacement generation 前进时才返回重试动作,即便后续摘要工作在剪枝后抛异常亦如此;取消仍然优先。区域边界保持工具调用/结果配对,但不保持整个轮次,因此一个过大轮次中较早关闭的步骤可以被压缩。`dsh-compaction-basic` 拥有阈值、保留尾部策略、溢出上限与失败处理。 +压力压缩在 `agent/pre-step` waterfall(瀑布式事件)中运行,先于请求推导。一旦压力或规范化溢出满足条件,compaction-basic 会在选择范围前调用可选的 [`ctx.toolResultPruner`](../../packages/compaction/compaction-tool-result-pruner/README.zh.md),再通过 `ctx.tokenMeter` 重新测量,并且可以在不生成摘要的情况下推进 surface。失败请求的恢复在失败的步骤关闭后通过 `agent/request-error` 运行;仅当 surface replacement generation 前进时才返回重试动作,即便后续摘要工作在剪枝后抛异常亦如此;取消仍然优先。区域边界保持工具调用/结果配对,但不保持整个轮次,因此一个过大轮次中较早关闭的步骤可以被压缩。`dsh-compaction-basic` 拥有阈值、保留尾部策略、溢出上限与失败处理。 该 Service Definition 导出 `toolPairingBalancedBefore(session, seq)` 与 `toolPairingBalancedAfter(session, seq)`,用于检查 seq 之前与之后的工具调用/结果配对。两者都会验证当前 surface 成员关系,并拒绝缺失的 seq 与遗留结果;[包约定](../../packages/compaction/compaction/README.zh.md#tool-pairing-boundaries)定义其缓存行为。 diff --git a/docs/subsystems/core.i18n.yaml b/docs/subsystems/core.i18n.yaml index 6825069021..1b16dff02b 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: d3564b6d50e0087be25f5dd1abc7b19507fd1c21 -core.zh.md: 0f7e49141e27b18ec8b75e944460d3ac04a88c0a +core.md: 15135ad1dcacd762fbf31668c13ea0760cd9bc95 +core.zh.md: 7e0270a49f11ef01e6ffb2ca8e1af5831a738198 diff --git a/docs/subsystems/core.md b/docs/subsystems/core.md index d3564b6d50..15135ad1dc 100644 --- a/docs/subsystems/core.md +++ b/docs/subsystems/core.md @@ -202,7 +202,7 @@ type AgentCancelCause = | { readonly kind: 'disposed' } ``` -The cause is a TypeScript-enforced same-process input. An active cancellation holder copies it into the runtime-only `AbortSignal.reason`; a signal grants cooperating listeners no classification authority. Durable `turn/end` retains the coarse `{ kind: 'aborted' }` outcome; recording who requested cancellation would require a separate durable event rather than overloading the terminal result. +The cause is a TypeScript-enforced same-process input. An active cancellation holder copies it into the runtime-only `AbortSignal.reason`; a signal grants cooperating listeners no classification authority. Durable `turn/end` records the outcome as `{ kind: 'aborted', reason: TurnEndCancelCause }`, so the cancel cause lands in the terminal result. The [event taxonomy](../architecture.md#events) owns the `agent/*` lifecycle, checkpoint, and waterfall contracts. Turn and step boundaries are durable session events rather than agent emits. @@ -239,7 +239,7 @@ type PreStepDecision = type RequestErrorAction = { kind: 'retry' } | undefined ``` -`agent/pre-step` is the only serial listener chain before request derivation. `agent/turn-stopping` runs when a turn has no tool or steering continuation, before one final steering drain. +`agent/pre-step` is the only waterfall listener chain before request derivation. `agent/turn-stopping` runs when a turn has no tool or steering continuation, before one final steering drain. `agent/session-start` carries a `SessionStartSource` (why the session lifecycle began; a bridge keys its SessionStart matcher on it): @@ -287,14 +287,13 @@ declare module '@deepseek-ai/dsh-llm' { } ``` -Six canonical maps use this pattern; a plugin author extends these: +Five canonical maps use this pattern; a plugin author extends these: | Map | Package | Derives | Catalog | |---|---|---|---| | `ContentBlockMap` | dsh-llm | `ContentBlock` | [llm-streaming.md](llm-streaming.md#content-blocks-and-messages) | | `MessageSourceMap` | dsh-llm | `MessageSource` | [llm-streaming.md](llm-streaming.md#content-blocks-and-messages) | | `FinishReasonMap` | dsh-llm | `FinishReason` | [llm-streaming.md](llm-streaming.md#the-model-request-and-result) | -| `TurnTriggerMap` | dsh-session | `TurnTrigger` | [session.md](session.md) | | `TurnEndReasonMap` | dsh-session | `TurnEndReason` | [session.md](session.md) | | `SessionEventMap` | dsh-session | `SessionEvent` | [session.md](session.md) | diff --git a/docs/subsystems/core.zh.md b/docs/subsystems/core.zh.md index 0f7e49141e..7e0270a49f 100644 --- a/docs/subsystems/core.zh.md +++ b/docs/subsystems/core.zh.md @@ -206,7 +206,7 @@ type AgentCancelCause = | { readonly kind: 'disposed' } ``` -cause 是由 TypeScript 强制约束的同进程输入。活跃的取消持有者会将它复制到仅运行时的 `AbortSignal.reason`;signal 不授予协作监听器任何分类权限。持久 `turn/end` 保留粗粒度 `{ kind: 'aborted' }` 结果;若需记录谁请求了取消,应使用单独的持久事件,而不是让终态结果承担额外含义。 +cause 是由 TypeScript 强制约束的同进程输入。活跃的取消持有者会将它复制到仅运行时的 `AbortSignal.reason`;signal 不授予协作监听器任何分类权限。持久 `turn/end` 以 `{ kind: 'aborted', reason: TurnEndCancelCause }` 记录结果,取消原因随终态结果一起持久化。 [事件分类](../architecture.zh.md#events)负责 `agent/*` 生命周期、检查点与 waterfall(瀑布式事件)约定。轮次和步骤边界是持久会话事件,而不是 agent emit。 @@ -247,7 +247,7 @@ type PreStepDecision = type RequestErrorAction = { kind: 'retry' } | undefined ``` -`agent/pre-step` 是请求推导前唯一的串行监听器链。`agent/turn-stopping` 在轮次没有工具或 steering(中途引导)后续时运行,先于最后一次 steering 排空。 +`agent/pre-step` 是请求推导前唯一的 waterfall(瀑布式)监听器链。`agent/turn-stopping` 在轮次没有工具或 steering(中途引导)后续时运行,先于最后一次 steering 排空。 `agent/session-start` 携带 `SessionStartSource`(会话生命周期为何开始;桥接层据此匹配其 SessionStart): @@ -295,14 +295,13 @@ declare module '@deepseek-ai/dsh-llm' { } ``` -六个规范 map 使用此模式;插件作者扩展它们: +五个规范 map 使用此模式;插件作者扩展它们: | Map | 包 | 派生 | 目录 | |---|---|---|---| | `ContentBlockMap` | dsh-llm | `ContentBlock` | [llm-streaming.md](llm-streaming.zh.md#content-blocks-and-messages) | | `MessageSourceMap` | dsh-llm | `MessageSource` | [llm-streaming.md](llm-streaming.zh.md#content-blocks-and-messages) | | `FinishReasonMap` | dsh-llm | `FinishReason` | [llm-streaming.md](llm-streaming.zh.md#the-model-request-and-result) | -| `TurnTriggerMap` | dsh-session | `TurnTrigger` | [session.md](session.zh.md) | | `TurnEndReasonMap` | dsh-session | `TurnEndReason` | [session.md](session.zh.md) | | `SessionEventMap` | dsh-session | `SessionEvent` | [session.md](session.zh.md) | diff --git a/docs/subsystems/feedback.i18n.yaml b/docs/subsystems/feedback.i18n.yaml index 02e816c689..9e6b0d6424 100644 --- a/docs/subsystems/feedback.i18n.yaml +++ b/docs/subsystems/feedback.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/feedback.md -feedback.md: e4e67bffbdbd62bf8842948b77c86b3be89b5879 -feedback.zh.md: 56990fe68f594fdfe6699f8e444a78abf3e59c5d +feedback.md: 046765b65773834b1804aa2a915625a3d6c9f099 +feedback.zh.md: 55b8e7d1b5b2c8ca5613d9888ef7f415b34ed5dc diff --git a/docs/subsystems/feedback.md b/docs/subsystems/feedback.md index e4e67bffbd..046765b657 100644 --- a/docs/subsystems/feedback.md +++ b/docs/subsystems/feedback.md @@ -212,7 +212,7 @@ One `MessageFeedbackController` per Session backs every message control in that ## Boundaries and limitations - The mutation queue is process-local. Storage-domain has no cross-process conditional write, so multiple Host writers to one storage root have no compare-and-swap or lost-update guarantee. -- Session persistence has no durable deletion API. The service does not treat `session/disposed` or `host/session-removed` as deletion and therefore performs no fake cascade; orphan sidecar rows may remain after out-of-band log removal. +- Session persistence has no durable deletion API. The service does not treat `session/disposed` or `api-session/removed` as deletion and therefore performs no fake cascade; orphan sidecar rows may remain after out-of-band log removal. - A request in the narrow interval after live detach but before the persistence catalog materializes the header can receive `session-not-found`; callers retry after retirement materialization. - Cold requests scan the complete Session snapshot catalog because persistence has no lookup-by-id metadata operation. One Session row also has no item-count or aggregate-byte cap; `maxNoteBytes` bounds only each note until a concrete consumer owns a row policy. - Header identity detects a reused id only when `{createdAt, cwd}` differs; a cloned log retaining the same header identity is indistinguishable by this contract. diff --git a/docs/subsystems/feedback.zh.md b/docs/subsystems/feedback.zh.md index 56990fe68f..55b8e7d1b5 100644 --- a/docs/subsystems/feedback.zh.md +++ b/docs/subsystems/feedback.zh.md @@ -212,7 +212,7 @@ Plugin disposal 会先关闭变更接纳,排空已进入各 Session 队列的 ## 边界与限制 - 变更队列仅在进程内生效。storage-domain 没有跨进程条件写,因此多个 Host 写入同一存储根目录时,不提供 compare-and-swap 或防止丢失更新的保证。 -- Session persistence 没有持久删除接口。服务不把 `session/disposed` 或 `host/session-removed` 当作删除,因此不伪造级联;在带外移除日志后,孤儿伴随记录可能继续存在。 +- Session persistence 没有持久删除接口。服务不把 `session/disposed` 或 `api-session/removed` 当作删除,因此不伪造级联;在带外移除日志后,孤儿伴随记录可能继续存在。 - 请求若恰好落在 live detach 之后、persistence catalog 物化 header 之前的极短窗口,可能收到 `session-not-found`;调用方应在 retirement materialization 后重试。 - 由于 persistence 没有按 id 读取元数据的操作,cold 请求会扫描完整的 Session snapshot 目录。单个 Session 行也没有条目数或聚合字节上限;在具体消费方拥有行策略之前,`maxNoteBytes` 只限制每条备注。 - 只有 `{createdAt, cwd}` 不同时,header 身份才能识别复用的 id;本约定无法区分保留相同 header 身份的克隆日志。 diff --git a/docs/subsystems/jobs.i18n.yaml b/docs/subsystems/jobs.i18n.yaml index ac7311524c..53ba3397ea 100644 --- a/docs/subsystems/jobs.i18n.yaml +++ b/docs/subsystems/jobs.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/jobs.md -jobs.md: 65e92a122b4870c7de295e33d115243b4da8f4d8 -jobs.zh.md: 115370b4ddef03b091e7c5b1d6bc4f65e5fce097 +jobs.md: 092da7b6f7e10c5713d3d6c9c5c46f925084f86a +jobs.zh.md: 9dc390eb30901664edb47c6727a54ddd83839fa7 diff --git a/docs/subsystems/jobs.md b/docs/subsystems/jobs.md index 65e92a122b..092da7b6f7 100644 --- a/docs/subsystems/jobs.md +++ b/docs/subsystems/jobs.md @@ -154,7 +154,7 @@ interface JobRead { ## Service behavior -The abstract [`JobRegistry`](../../packages/jobs/jobs/src/index.ts) Service Definition specifies atomic `start`, caller-scoped `get` and `list`, `read`, `kill`, bounded `wait`, failure-isolated `onJobDone` and `onJobsChanged` listeners, and when `attachController` becomes available; [`LocalJobRegistry`](../../packages/jobs/jobs-local/src/index.ts) is the process-local Service Provider. Authorization compares owner sessions; owner cleanup and admission use the exact registered `Agent` instance. The local provider's positive-safe-integer `maxConcurrentJobsPerOwner` config defaults to `10` and counts `running` plus `stopping` records per exact owner, with one shared bucket for unowned jobs; terminal producer settlement releases capacity. See [`dsh-jobs`](../../packages/jobs/jobs/README.md) for the Service Definition contract, [`dsh-jobs-local`](../../packages/jobs/jobs-local/README.md) for the registry lifecycle and admission policy, and [`dsh-tool-jobs`](../../packages/jobs/tool-jobs/README.md) for the model-facing Consumer. +The abstract [`JobRegistry`](../../packages/jobs/jobs/src/index.ts) Service Definition specifies atomic `start`, caller-scoped `get` and `list`, `read`, `kill`, bounded `wait`, failure-isolated `onJobDone` and `onJobsChanged` listeners, and `attachController`; [`LocalJobRegistry`](../../packages/jobs/jobs-local/src/index.ts) is the process-local Service Provider. Authorization compares owner sessions; owner cleanup and admission use the exact registered `Agent` instance. The local provider's positive-safe-integer `maxConcurrentJobsPerOwner` config defaults to `10` and counts `running` plus `stopping` records per exact owner, with one shared bucket for unowned jobs; terminal producer settlement releases capacity. See [`dsh-jobs`](../../packages/jobs/jobs/README.md) for the Service Definition contract, [`dsh-jobs-local`](../../packages/jobs/jobs-local/README.md) for the registry lifecycle and admission policy, and [`dsh-tool-jobs`](../../packages/jobs/tool-jobs/README.md) for the model-facing Consumer. diff --git a/docs/subsystems/jobs.zh.md b/docs/subsystems/jobs.zh.md index 115370b4dd..9dc390eb30 100644 --- a/docs/subsystems/jobs.zh.md +++ b/docs/subsystems/jobs.zh.md @@ -154,7 +154,7 @@ interface JobRead { ## 服务行为 -抽象的 [`JobRegistry`](../../packages/jobs/jobs/src/index.ts) Service Definition 规定原子 `start`、限定调用方作用域的 `get` 和 `list`、`read`、`kill`、有界 `wait`、故障隔离的 `onJobDone` 与 `onJobsChanged` 监听器,以及 `attachController` 何时可用;[`LocalJobRegistry`](../../packages/jobs/jobs-local/src/index.ts) 是其进程局部 Service Provider。授权会比较拥有者会话;拥有者清理与准入会使用确切的已注册 `Agent` 实例。本地 Service Provider 的 `maxConcurrentJobsPerOwner` 配置必须是正的安全整数,默认值为 `10`;它按确切 owner 统计 `running` 与 `stopping` 记录,所有无 owner 任务共享一个服务级桶,并在生产方终止结算后释放容量。Service Definition 约定见 [`dsh-jobs`](../../packages/jobs/jobs/README.zh.md),注册表生命周期与准入策略见 [`dsh-jobs-local`](../../packages/jobs/jobs-local/README.zh.md),面向模型的 Consumer 见 [`dsh-tool-jobs`](../../packages/jobs/tool-jobs/README.zh.md)。 +抽象的 [`JobRegistry`](../../packages/jobs/jobs/src/index.ts) Service Definition 规定原子 `start`、限定调用方作用域的 `get` 和 `list`、`read`、`kill`、有界 `wait`、故障隔离的 `onJobDone` 与 `onJobsChanged` 监听器,以及 `attachController`;[`LocalJobRegistry`](../../packages/jobs/jobs-local/src/index.ts) 是其进程局部 Service Provider。授权会比较拥有者会话;拥有者清理与准入会使用确切的已注册 `Agent` 实例。本地 Service Provider 的 `maxConcurrentJobsPerOwner` 配置必须是正的安全整数,默认值为 `10`;它按确切 owner 统计 `running` 与 `stopping` 记录,所有无 owner 任务共享一个服务级桶,并在生产方终止结算后释放容量。Service Definition 约定见 [`dsh-jobs`](../../packages/jobs/jobs/README.zh.md),注册表生命周期与准入策略见 [`dsh-jobs-local`](../../packages/jobs/jobs-local/README.zh.md),面向模型的 Consumer 见 [`dsh-tool-jobs`](../../packages/jobs/tool-jobs/README.zh.md)。 diff --git a/docs/subsystems/llm-streaming.i18n.yaml b/docs/subsystems/llm-streaming.i18n.yaml index a89451f7ad..2f11df1c7a 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: 73e8dc4a6a5b5408a5c85dcbeac4cfa2108a9ddd -llm-streaming.zh.md: 7f98029f35100ec9c72f55c509f20b6709315f47 +llm-streaming.md: 8db1d95d91095d09eeb4a0399e1449688cab52cc +llm-streaming.zh.md: fd2d483cbbecac9b1015d67e0e14aa67b67ef08e diff --git a/docs/subsystems/llm-streaming.md b/docs/subsystems/llm-streaming.md index 73e8dc4a6a..8db1d95d91 100644 --- a/docs/subsystems/llm-streaming.md +++ b/docs/subsystems/llm-streaming.md @@ -411,7 +411,7 @@ One model call is a fully-assembled `GenerateOptions`. The adapter answers with Source: [`packages/llm/llm/src/types.ts`](../../packages/llm/llm/src/types.ts) -Provider and model discovery uses small provider-neutral descriptors. A model catalog is advisory: routing still keys on a registered provider, and an adapter may accept unlisted model ids. +Provider and model discovery uses small provider-neutral descriptors. A model catalog is advisory: routing still keys on a registered provider. Registering an adapter returns a handle: the disposer, plus the atomic route replacement a plugin whose route set is user-configurable needs. diff --git a/docs/subsystems/llm-streaming.zh.md b/docs/subsystems/llm-streaming.zh.md index 7f98029f35..fd2d483cbb 100644 --- a/docs/subsystems/llm-streaming.zh.md +++ b/docs/subsystems/llm-streaming.zh.md @@ -417,7 +417,7 @@ declare class BlockAssembler { 源码:[`packages/llm/llm/src/types.ts`](../../packages/llm/llm/src/types.ts) -提供方与模型发现使用小型、提供方无关的描述符。模型目录仅供参考:路由仍以已注册提供方为键,适配器也可以接受未列出的模型 id。 +提供方与模型发现使用小型、提供方无关的描述符。模型目录仅供参考:路由仍以已注册提供方为键。 注册适配器会返回一个句柄:既是释放器,也带有原子的路由替换——路由集合由用户配置决定的插件正需要它。 diff --git a/docs/subsystems/permission-presets.i18n.yaml b/docs/subsystems/permission-presets.i18n.yaml index 4c8afb10ac..3e7fe77aff 100644 --- a/docs/subsystems/permission-presets.i18n.yaml +++ b/docs/subsystems/permission-presets.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/permission-presets.md -permission-presets.md: 4afa5063f2ab107415fd6429cf0cee6a4ecdfe4f +permission-presets.md: ccbf1a0133571e56b780aadf9f08150d291fda35 permission-presets.zh.md: 3ec3fde572da32c3276cfcea2db2b256f55c845c diff --git a/docs/subsystems/permission-presets.md b/docs/subsystems/permission-presets.md index 4afa5063f2..ccbf1a0133 100644 --- a/docs/subsystems/permission-presets.md +++ b/docs/subsystems/permission-presets.md @@ -63,7 +63,7 @@ interface PresetOption { ## Switching and the `permission/preset` event -`set(session, name)` resolves the preset (unknown names throw), appends a log-only `permission/preset` event unless `name` is already the effective preset, then writes each knob through its own setter — `setSandboxMode` from [dsh-sandbox-policy](../../packages/sandbox/sandbox-policy) and `setApprovalPolicy` from [dsh-user-approval](../../packages/interaction/user-approval) — only when that knob's effective value changes. The selection event precedes the knob events in the same turn, and re-selecting the effective preset appends nothing at all. +`set(session, name)` resolves the preset (unknown names throw), appends a log-only `permission/preset` event unless `name` is already the effective preset, then writes each knob through its own setter — `setSandboxMode` from [dsh-sandbox-policy](../../packages/sandbox/sandbox-policy) and `setApprovalPolicy` from [dsh-user-approval](../../packages/interaction/user-approval) — only when that knob's effective value changes. The selection event precedes the knob events in the same turn, and re-selecting the effective preset appends nothing. `permission/preset` is durable, log-only user intent: it stays out of the model transcript (the knob events own the model-visible consequences through their consumers), and it exists so `current()` can preserve WHICH preset the user chose when two presets share a bundle; `effectivePermissionPreset(events)` folds the last one, and replay needs no catch-up state. The complete event declaration is in the [persistence log event catalog](../persistence-catalog.md); the method signatures are in the generated [service catalog](#ctxpermissionpresets--permissionpresetservice). diff --git a/docs/subsystems/session-projection.i18n.yaml b/docs/subsystems/session-projection.i18n.yaml index 5ee836c7be..4bdbc164e8 100644 --- a/docs/subsystems/session-projection.i18n.yaml +++ b/docs/subsystems/session-projection.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-projection.md -session-projection.md: 67e212e5c0379d2446fd0baed3f511c4ea2630f2 -session-projection.zh.md: 337cb08853998d325dd063b99b601ae99552285b +session-projection.md: fd40408c380a3694c6b91b9ad79417cd0feb738b +session-projection.zh.md: 392a537416bc7968e04997724e97512db4c6232d diff --git a/docs/subsystems/session-projection.md b/docs/subsystems/session-projection.md index 67e212e5c0..fd40408c38 100644 --- a/docs/subsystems/session-projection.md +++ b/docs/subsystems/session-projection.md @@ -2,7 +2,7 @@ English | [中文](session-projection.zh.md) -The session-projection seam — a [capability seam](../capability-seams.md) through which domain host plugins serve whole current values of log-derived per-session state to client carriers: the Service Definition and registry ([dsh-session-projection](../../packages/session/session-projection), `ctx.sessionProjections`), domain contributors (each registering one pure unit), and carriers ([dsh-host-apiproxy](../../packages/host/apiproxy)'s history tail page and `session/projection` push frame). It is one optional capability, not part of the agent-loop spine. The framework drives, the domain computes: the registry subscribes to `session/event` once and folds every committed event through every unit; domains hold no subscriptions and clients never fold domain events — they receive finished values. Design authority: the [session-projection RFC](../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md); drive/cache/feed contracts: the [package README](../../packages/session/session-projection/README.md). +The session-projection seam — a [capability seam](../capability-seams.md) through which domain host plugins serve whole current values of log-derived per-session state to client carriers: the Service Definition and registry ([dsh-session-projection](../../packages/session/session-projection), `ctx.sessionProjections`), domain contributors (each registering one pure unit), and carriers ([dsh-session-controller](../../packages/api/session-controller)'s history tail page and `session/projection` push frame). It is one optional capability, not part of the agent-loop spine. The framework drives, the domain computes: the registry subscribes to `session/event` once and folds every committed event through every unit; domains hold no subscriptions and clients never fold domain events — they receive finished values. Design authority: the [session-projection RFC](../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md); drive/cache/feed contracts: the [package README](../../packages/session/session-projection/README.md). Source: [`packages/session/session-projection/src/index.ts`](../../packages/session/session-projection/src/index.ts) @@ -99,7 +99,7 @@ type ProjectionChangeListener = ( ## The registry: `ctx.sessionProjections` -`SessionProjectionRegistry` ([signatures](#ctxsessionprojections--sessionprojectionregistry)) owns the drive: one `session/event` subscription, eager `apply` over every registered unit, and per-session per-unit watermark cells. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Registration is an effect whose disposer rides the calling fiber: an unloaded domain plugin's key (with its cached cells) disappears from subsequent drives and snapshots, and clients read that as capability absence; duplicate keys throw. Domain plugins register under `ctx.inject(['sessionProjections'], …)` so headless assemblies without the registry stay unaffected. +`SessionProjectionRegistry` ([signatures](#ctxsessionprojections--sessionprojectionregistry)) owns the drive: one `session/event` subscription, eager `apply` over every registered unit, and per-session per-unit watermark cells. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Registration is an effect whose disposer rides the calling fiber: an unloaded domain plugin's key (with its cached cells) disappears from subsequent drives and snapshots, and clients read that as capability absence; a duplicate key with a different `stateVersion` throws, while same-version registrants share one unit and are counted. Domain plugins register under `ctx.inject(['sessionProjections'], …)` so headless assemblies without the registry stay unaffected. @@ -144,7 +144,7 @@ cachedSnapshot( meta: SessionHeader, keys?: readonly Extract @@ -144,7 +144,7 @@ cachedSnapshot( meta: SessionHeader, keys?: readonly Extract { `validate` runs after the schema admits a value, so it sees defaults and the composition base exactly as the owner will. `dsh-llm-pi-ai` uses it to refuse a provider profile it could not serve at the write that produced it, rather than storing one that would disable every route in its namespace. -`applies` is a UI hint, not a mechanism: a `restart` owner simply never watches, so its value is read once at construction and configuration surfaces can badge the pending change. +`applies` is a UI hint, not a mechanism: a `restart` owner never watches, so its value is read once at construction and configuration surfaces can badge the pending change. ```ts type-equiv /** When a namespace's changes take effect for its owner. */ diff --git a/docs/subsystems/settings.zh.md b/docs/subsystems/settings.zh.md index c09d0dbd57..06e82d6d25 100644 --- a/docs/subsystems/settings.zh.md +++ b/docs/subsystems/settings.zh.md @@ -51,7 +51,7 @@ interface SettingsRegisterOptions { `validate` 在 schema 接纳该值之后运行,因此它看到的默认值和组合 base 与 owner 实际看到的完全一致。`dsh-llm-pi-ai` 用它在写入处拒绝自己无法服务的提供方 profile,而不是先存下来、再让该 namespace 下每条路由失效。 -`applies` 是 UI 提示而非机制:`restart` 的 owner 只是从不 watch,其值在构造期读取一次,配置界面可为待生效变更加标。 +`applies` 是 UI 提示而非机制:`restart` 的 owner 从不 watch,其值在构造期读取一次,配置界面可为待生效变更加标。 ```ts type-equiv /** When a namespace's changes take effect for its owner. */ diff --git a/docs/subsystems/spill.i18n.yaml b/docs/subsystems/spill.i18n.yaml index 700683c015..b8d92153b3 100644 --- a/docs/subsystems/spill.i18n.yaml +++ b/docs/subsystems/spill.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/spill.md -spill.md: 8356fc45f8581c6776238f212592c4ea2841a96a -spill.zh.md: 353c5fea63ff6284c922a9bbf7d6d07f124fb9bd +spill.md: 1ea23747ddb42c7b36d71e8125dff059ba985b3c +spill.zh.md: a10512f0c6ff26be3dc2637cd8c19606ba4fbdc6 diff --git a/docs/subsystems/spill.md b/docs/subsystems/spill.md index 8356fc45f8..1ea23747dd 100644 --- a/docs/subsystems/spill.md +++ b/docs/subsystems/spill.md @@ -38,7 +38,7 @@ interface SpillOwner { } ``` -`SpillOwner.sessionId` is the save-time storage namespace. Forked sessions inherit existing spill locators from the seeded log; those artifacts are not copied or re-owned, and spills produced after the fork use the child session id. A retention-period cleanup may expire old locators with other old session artifacts; the spill seam does not define a per-session cleanup policy. +A retention-period cleanup may expire old locators with other old session artifacts; the spill seam does not define a per-session cleanup policy. ```ts type-equiv /** diff --git a/docs/subsystems/spill.zh.md b/docs/subsystems/spill.zh.md index 353c5fea63..a10512f0c6 100644 --- a/docs/subsystems/spill.zh.md +++ b/docs/subsystems/spill.zh.md @@ -38,7 +38,7 @@ interface SpillOwner { } ``` -`SpillOwner.sessionId` 是保存时的存储命名空间。fork 后的会话会从种子日志继承已有的 spill 定位符;这些产物不会被复制或重新取得所有权,fork 后产生的 spill 则使用子会话 id。保留期清理可以连同其他旧会话产物一起使旧定位符失效;spill seam 不定义逐会话的清理策略。 +保留期清理可以连同其他旧会话产物一起使旧定位符失效;spill seam 不定义逐会话的清理策略。 ```ts type-equiv /** diff --git a/docs/subsystems/subagent.i18n.yaml b/docs/subsystems/subagent.i18n.yaml index e59c3a9c9a..9e316a93c6 100644 --- a/docs/subsystems/subagent.i18n.yaml +++ b/docs/subsystems/subagent.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/subagent.md -subagent.md: 66ebb7eb45f45092c1165d3f689ea1635d9cb3fe -subagent.zh.md: 8cdc897ddd12f4e1ff03aa8ba4cd82d4150784d3 +subagent.md: e06d721427a195519817e4cfcf7c2bb38f572970 +subagent.zh.md: 0364360c783c1de1f6303b1a9cbc431f14db6dc2 diff --git a/docs/subsystems/subagent.md b/docs/subsystems/subagent.md index 66ebb7eb45..e06d721427 100644 --- a/docs/subsystems/subagent.md +++ b/docs/subsystems/subagent.md @@ -4,7 +4,7 @@ English | [中文](subagent.zh.md) The subagent seam lets an agent delegate work to a child agent. Like [bash](shell.md), it is **one optional capability**, not part of the agent loop, so its types live here rather than in [core.md](core.md). It differs from the other capability seams because **multiple provider implementations coexist** in one context, registered by name (`ctx.subagents`), while bash allows only one executor. Its registry follows the [LLM adapter registry](llm-streaming.md), not the single-service bash executor. -Service Definition: [dsh-subagent](../../packages/subagent/subagent) (`ctx.subagents` + the vocabulary below). Service Providers are sibling packages (`dsh-subagent-spawn-in-process`, `-fork`, `-acp`, `-codex`, `-claude-code`, `-dsh-sdk`); the model-facing Consumers are [dsh-tool-subagent](../../packages/subagent/tool-subagent) (per-provider delegation), [dsh-tool-subagent-control](../../packages/subagent/tool-subagent-control) (the optional global `send_message`, `interrupt_agent`, and `list_agents` controls), and [dsh-tool-subagent-report](../../packages/subagent/tool-subagent-report) (the optional child-scoped `report` return channel). The same `ctx.subagents` service owns continuable-child orchestration through an internal activation manager and read-only child and descendant discovery straight from the session store and optional session persistence. Product-provider rationale lives in [the Codex and Claude Code Agent Note](../../.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md); common-seam rationale lives in [the subagent Agent Note](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md), [the continuable subagents Agent Note](../../.agents/notes/implemented/feature/2026-07-28-continuable-subagent-conversations.md), [the report-tool Agent Note](../../.agents/notes/implemented/feature/2026-07-30-continuable-subagent-report-tool.md), [the durable catalog Agent Note](../../.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md), [the list-identity-projection Agent Note](../../.agents/notes/implemented/architecture/2026-08-06-subagent-list-identity-projection.md), and [the merged-service Agent Note](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md). +Service Definition: [dsh-subagent](../../packages/subagent/subagent) (`ctx.subagents` + the vocabulary below). Service Providers are sibling packages (`dsh-subagent-spawn-in-process`, `dsh-subagent-fork-in-process`, `dsh-subagent-acp`, `dsh-subagent-codex`, `dsh-subagent-claude-code`, `dsh-subagent-dsh-sdk`); the model-facing Consumers are [dsh-tool-subagent](../../packages/subagent/tool-subagent) (per-provider delegation), [dsh-tool-subagent-control](../../packages/subagent/tool-subagent-control) (the optional global `send_message`, `interrupt_agent`, and `list_agents` controls), and [dsh-tool-subagent-report](../../packages/subagent/tool-subagent-report) (the optional child-scoped `report` return channel). The same `ctx.subagents` service owns continuable-child orchestration through an internal activation manager and read-only child and descendant discovery straight from the session store and optional session persistence. Product-provider rationale lives in [the Codex and Claude Code Agent Note](../../.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md); common-seam rationale lives in [the subagent Agent Note](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md), [the continuable subagents Agent Note](../../.agents/notes/implemented/feature/2026-07-28-continuable-subagent-conversations.md), [the report-tool Agent Note](../../.agents/notes/implemented/feature/2026-07-30-continuable-subagent-report-tool.md), [the durable catalog Agent Note](../../.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.md), [the list-identity-projection Agent Note](../../.agents/notes/implemented/architecture/2026-08-06-subagent-list-identity-projection.md), and [the merged-service Agent Note](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md). Sources: [`packages/subagent/subagent/src/types.ts`](../../packages/subagent/subagent/src/types.ts), [`packages/subagent/subagent/src/index.ts`](../../packages/subagent/subagent/src/index.ts), and [`packages/subagent/subagent/src/continuation.ts`](../../packages/subagent/subagent/src/continuation.ts) diff --git a/docs/subsystems/subagent.zh.md b/docs/subsystems/subagent.zh.md index 8cdc897ddd..0364360c78 100644 --- a/docs/subsystems/subagent.zh.md +++ b/docs/subsystems/subagent.zh.md @@ -4,7 +4,7 @@ subagent seam 让一个 agent(智能体)将工作委派给子 agent。与 [bash](shell.zh.md) 一样,它是**一项可选能力**,不属于 agent loop(智能体循环),因此其类型定义在此而非 [core.md](core.zh.md) 中。它不同于其他能力 seam,因为**同一上下文中可共存多个提供方实现**,并按名称注册(`ctx.subagents`),而 bash 只允许一个执行器。该注册表遵循 [LLM(大语言模型)适配器注册表](llm-streaming.zh.md),而非单服务的 bash 执行器。 -Service Definition:[dsh-subagent](../../packages/subagent/subagent)(`ctx.subagents` + 下文词汇)。Service Provider 是六个兄弟包:`dsh-subagent-spawn-in-process`、`-fork`、`-acp`、`-codex`、`-claude-code`、`-dsh-sdk`;面向模型的 Consumer 包括 [dsh-tool-subagent](../../packages/subagent/tool-subagent)(按提供方委派)、[dsh-tool-subagent-control](../../packages/subagent/tool-subagent-control)(可选的全局 `send_message`、`interrupt_agent` 与 `list_agents` 控制工具)和 [dsh-tool-subagent-report](../../packages/subagent/tool-subagent-report)(可选的 child 作用域 `report` 返回通道)。同一个 `ctx.subagents` 服务通过内部激活管理器负责可继续子 agent 编排,并直接基于会话存储和可选的会话持久化提供只读的 child 与后代发现。产品提供方设计理由见 [Codex 与 Claude Code Agent Note](../../.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.zh.md);通用 seam 的设计理由见 [subagent Agent Note](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.zh.md)、[可继续 subagent Agent Note](../../.agents/notes/implemented/feature/2026-07-28-continuable-subagent-conversations.zh.md)、[report 工具 Agent Note](../../.agents/notes/implemented/feature/2026-07-30-continuable-subagent-report-tool.zh.md)、[持久化目录 Agent Note](../../.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.zh.md)、[列表身份投影 Agent Note](../../.agents/notes/implemented/architecture/2026-08-06-subagent-list-identity-projection.zh.md)和[服务合并 Agent Note](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.zh.md)。 +Service Definition:[dsh-subagent](../../packages/subagent/subagent)(`ctx.subagents` + 下文词汇)。Service Provider 是六个兄弟包:`dsh-subagent-spawn-in-process`、`dsh-subagent-fork-in-process`、`dsh-subagent-acp`、`dsh-subagent-codex`、`dsh-subagent-claude-code`、`dsh-subagent-dsh-sdk`;面向模型的 Consumer 包括 [dsh-tool-subagent](../../packages/subagent/tool-subagent)(按提供方委派)、[dsh-tool-subagent-control](../../packages/subagent/tool-subagent-control)(可选的全局 `send_message`、`interrupt_agent` 与 `list_agents` 控制工具)和 [dsh-tool-subagent-report](../../packages/subagent/tool-subagent-report)(可选的 child 作用域 `report` 返回通道)。同一个 `ctx.subagents` 服务通过内部激活管理器负责可继续子 agent 编排,并直接基于会话存储和可选的会话持久化提供只读的 child 与后代发现。产品提供方设计理由见 [Codex 与 Claude Code Agent Note](../../.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.zh.md);通用 seam 的设计理由见 [subagent Agent Note](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.zh.md)、[可继续 subagent Agent Note](../../.agents/notes/implemented/feature/2026-07-28-continuable-subagent-conversations.zh.md)、[report 工具 Agent Note](../../.agents/notes/implemented/feature/2026-07-30-continuable-subagent-report-tool.zh.md)、[持久化目录 Agent Note](../../.agents/notes/implemented/feature/2026-07-22-durable-subagent-catalog-and-list-agents.zh.md)、[列表身份投影 Agent Note](../../.agents/notes/implemented/architecture/2026-08-06-subagent-list-identity-projection.zh.md)和[服务合并 Agent Note](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.zh.md)。 源码:[`packages/subagent/subagent/src/types.ts`](../../packages/subagent/subagent/src/types.ts)、[`packages/subagent/subagent/src/index.ts`](../../packages/subagent/subagent/src/index.ts)和 [`packages/subagent/subagent/src/continuation.ts`](../../packages/subagent/subagent/src/continuation.ts) diff --git a/docs/subsystems/webhook.i18n.yaml b/docs/subsystems/webhook.i18n.yaml index 7e178c58fd..c27aa424bb 100644 --- a/docs/subsystems/webhook.i18n.yaml +++ b/docs/subsystems/webhook.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/webhook.md -webhook.md: fc59fe8dc233892289d9f1096c6d3c457684bf97 -webhook.zh.md: 509482cbca4dae45b077789266718fcafcc14489 +webhook.md: 5f04c99141c5630add46db3b55c3af78e2df2bdf +webhook.zh.md: 2eadf03aa5f32ae6f384e6b32a95e67bc0fdab7e diff --git a/docs/subsystems/webhook.md b/docs/subsystems/webhook.md index fc59fe8dc2..5f04c99141 100644 --- a/docs/subsystems/webhook.md +++ b/docs/subsystems/webhook.md @@ -18,7 +18,7 @@ The Webhook subsystem turns authenticated external deliveries into optional ordi ## Fire-and-forget dispatch -`dispatch()` snapshots the currently matching rules, schedules each independently, and returns before any callback settles. Throws and rejections are contained per rule. Registration disposal removes the rule before aborting and draining its active calls, so no later delivery can enter code that is unloading. +`dispatch()` snapshots the matching rules, schedules each independently, and returns before any callback settles. Throws and rejections are contained per rule. Registration disposal removes the rule before aborting and draining its active calls, so no later delivery can enter code that is unloading. The runtime has no queue, retry, deduplication, execution status, crash replay, Agent-status listener, or completion result. Repeated delivery may create repeated Sessions. The only active-operation table is private teardown bookkeeping and disappears with the process. diff --git a/docs/subsystems/webhook.zh.md b/docs/subsystems/webhook.zh.md index 509482cbca..2eadf03aa5 100644 --- a/docs/subsystems/webhook.zh.md +++ b/docs/subsystems/webhook.zh.md @@ -18,7 +18,7 @@ Webhook 子系统会把已通过身份验证的外部交付转换为可选的普 ## Fire-and-forget 分发 -`dispatch()` 会快照当前匹配规则,彼此独立地调度每个规则,并在任何回调结算前返回。抛出与拒绝按规则分别被包含。注册 disposer 会先移除规则,再中止并排空活动调用,因此后续交付无法进入正在卸载的代码。 +`dispatch()` 会快照匹配规则,彼此独立地调度每个规则,并在任何回调结算前返回。抛出与拒绝按规则分别被包含。注册 disposer 会先移除规则,再中止并排空活动调用,因此后续交付无法进入正在卸载的代码。 runtime 没有队列、重试、去重、执行状态、崩溃重放、Agent 状态监听器或完成结果。重复交付可能创建重复 Session。唯一的活动操作表是私有 teardown 记账,并随进程消失。 diff --git a/docs/subsystems/workspace.i18n.yaml b/docs/subsystems/workspace.i18n.yaml index cf675b2162..a41ab20b51 100644 --- a/docs/subsystems/workspace.i18n.yaml +++ b/docs/subsystems/workspace.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/workspace.md -workspace.md: bf2ba88b84b65cdd289f4bd603354dbd7027b239 -workspace.zh.md: a1a190c2c4c006d067620ab6d8494cba947b0368 +workspace.md: af2a8d4c5abbde04764e26751ba178fa99b2b3ef +workspace.zh.md: f54444f052c8faffc7670468d5423123cd3aeeac diff --git a/docs/subsystems/workspace.md b/docs/subsystems/workspace.md index bf2ba88b84..af2a8d4c5a 100644 --- a/docs/subsystems/workspace.md +++ b/docs/subsystems/workspace.md @@ -117,13 +117,13 @@ Ownership truth is the record's ordered `sessionIds`, never derived from session ## The registry: `ctx.workspaceRegistry` -`WorkspaceRegistry` ([signatures](#ctxworkspaceregistry--workspaceregistry)) owns registration and resolution. `create(path, title?)` canonicalizes the path, rejects a nonexistent path (the original `ENOENT`) or a non-directory, returns the existing entity unchanged when the canonical path is already owned, and otherwise creates a record with `title ?? basename(path)` prepended to the durable registry order — a new record cannot duplicate an existing display title (`WorkspaceNameConflictError`). `get(id)` and the ordered `list()` are synchronous cache reads; `resolveByPath(path)` applies the same realpath canon without creating. `delete(id)` removes only the registration, order entry, and session account — the directory, user files, live sessions, and persisted logs are never touched, so those sessions become Ungrouped ([decision](../../.agents/notes/implemented/feature/2026-07-27-workspace-registration-deletion.md)); unknown ids return `false`. Create and delete persist a pending-mutation marker before their two writes (record + order) can diverge; startup resolves exactly the marked mutation — by deleting the marked table row, which completes an interrupted delete and rolls back an interrupted create (the registration is re-creatable, so rollback is the safe direction) — and an unmarked order/table mismatch fails loud as corruption. +`WorkspaceRegistry` ([signatures](#ctxworkspaceregistry--workspaceregistry)) owns registration and resolution. `create(path, title?)` canonicalizes the path, rejects a nonexistent path (the original `ENOENT`) or a non-directory, returns the existing entity unchanged when the canonical path is already owned, and otherwise creates a record with `title ?? basename(path)` prepended to the durable registry order (different canonical paths may share a display title). `get(id)` and the ordered `list()` are synchronous cache reads; `resolveByPath(path)` applies the same realpath canon without creating. `delete(id)` removes only the registration, order entry, and session account — the directory, user files, live sessions, and persisted logs are never touched, so those sessions become Ungrouped ([decision](../../.agents/notes/implemented/feature/2026-07-27-workspace-registration-deletion.md)); unknown ids return `false`. Create and delete persist a pending-mutation marker before their two writes (record + order) can diverge; startup resolves exactly the marked mutation — by deleting the marked table row, which completes an interrupted delete and rolls back an interrupted create (the registration is re-creatable, so rollback is the safe direction) — and an unmarked order/table mismatch fails loud as corruption. Sessions get their cwd at create time from whoever creates them, not from this registry — the API gateway resolves a new session's cwd from the chosen workspace's `path` (falling back to an explicit or default cwd), creates the session so the cwd lands in its immutable [`SessionHeader`](persistence.md#sessionheader--metadata-beside-the-log), then calls `attachSession`, which re-validates that stored header cwd against the workspace path. On the first successful start, the registry bootstraps history from persisted headers alone (`id`, `cwd`, `createdAt` — never event bodies), grouping sessions with a valid canonical cwd into per-directory workspaces, newest first; the initialized marker is written last so an interrupted bootstrap resumes safely. The bootstrap is one-time: cwd-less legacy sessions stay Ungrouped, and sessions created afterwards join a workspace only through `attachSession`. ## Consumers -[dsh-host-apiproxy](../../packages/host/apiproxy) is the product consumer: it serves workspace CRUD to GUI clients over `ctx.workspaceRegistry` and performs the create-session-then-attach flow above. [dsh-agent-instructions](../../packages/context/agent-instructions) is **not** a consumer despite the name: it discovers AGENTS.md-style instruction files under an agent's own cwd and never touches `ctx.workspaceRegistry` — the shared word refers to the user's working directory, not to this registry's entities. +[`dsh-workspace-controller`](../../packages/api/workspace-controller) serves workspace CRUD to GUI clients over `ctx.workspaceRegistry`, and [`dsh-session-controller`](../../packages/api/session-controller) performs the create-session-then-attach flow above. [dsh-agent-instructions](../../packages/context/agent-instructions) is **not** a consumer despite the name: it discovers AGENTS.md-style instruction files under an agent's own cwd and never touches `ctx.workspaceRegistry` — the shared word refers to the user's working directory, not to this registry's entities. diff --git a/docs/subsystems/workspace.zh.md b/docs/subsystems/workspace.zh.md index a1a190c2c4..f54444f052 100644 --- a/docs/subsystems/workspace.zh.md +++ b/docs/subsystems/workspace.zh.md @@ -117,13 +117,13 @@ interface Workspace { ## 注册表:`ctx.workspaceRegistry` -`WorkspaceRegistry`([签名](#ctxworkspaceregistry--workspaceregistry))拥有注册与解析。`create(path, title?)` 规范化路径,拒绝不存在的路径(原样传出原始 `ENOENT`)或非目录;当规范路径已被拥有时原样返回既有实体;否则创建一条标题为 `title ?? basename(path)` 的记录并前插到持久的注册表顺序中——新记录不得与既有显示标题重复(`WorkspaceNameConflictError`)。`get(id)` 与有序的 `list()` 是同步缓存读取;`resolveByPath(path)` 应用同一套 realpath 规范但不创建。`delete(id)` 只移除注册记录、顺序条目和会话账本——目录、用户文件、实时会话和已持久化日志一概不动,因此这些会话变为 Ungrouped([决策](../../.agents/notes/implemented/feature/2026-07-27-workspace-registration-deletion.zh.md));未知 id 返回 `false`。create 与 delete 会在其两次写入(记录 + 顺序)可能分叉之前先持久写入一个待定变更标记;启动时恰好解决被标记的那次变更——通过删除被标记的表行:这会补完被中断的 delete,并回滚被中断的 create(注册可以重建,因此回滚是安全方向)——而没有标记的顺序/表不一致则作为损坏大声失败。 +`WorkspaceRegistry`([签名](#ctxworkspaceregistry--workspaceregistry))拥有注册与解析。`create(path, title?)` 规范化路径,拒绝不存在的路径(原样传出原始 `ENOENT`)或非目录;当规范路径已被拥有时原样返回既有实体;否则创建一条标题为 `title ?? basename(path)` 的记录并前插到持久的注册表顺序中(不同规范路径可以共享同一显示标题)。`get(id)` 与有序的 `list()` 是同步缓存读取;`resolveByPath(path)` 应用同一套 realpath 规范但不创建。`delete(id)` 只移除注册记录、顺序条目和会话账本——目录、用户文件、实时会话和已持久化日志一概不动,因此这些会话变为 Ungrouped([决策](../../.agents/notes/implemented/feature/2026-07-27-workspace-registration-deletion.zh.md));未知 id 返回 `false`。create 与 delete 会在其两次写入(记录 + 顺序)可能分叉之前先持久写入一个待定变更标记;启动时恰好解决被标记的那次变更——通过删除被标记的表行:这会补完被中断的 delete,并回滚被中断的 create(注册可以重建,因此回滚是安全方向)——而没有标记的顺序/表不一致则作为损坏大声失败。 会话的 cwd 在创建时由创建者赋予,而不是由本注册表赋予——API 网关从所选工作区的 `path` 解析新会话的 cwd(回退到显式或默认 cwd),先创建会话使 cwd 落入其不可变的 [`SessionHeader`](persistence.zh.md#sessionheader--metadata-beside-the-log),再调用 `attachSession`,后者会把已存储的 header cwd 与工作区路径重新校验一遍。首次成功启动时,注册表仅凭已持久化的 header(`id`、`cwd`、`createdAt`——绝不读事件正文)引导历史:把规范 cwd 有效的会话按目录分组为工作区,最新的排在最前;「已初始化」标记最后写入,因此被中断的引导可以安全续跑。引导只发生这一次:没有 cwd 的历史遗留会话保持 Ungrouped,此后创建的会话只能通过 `attachSession` 加入工作区。 ## 消费方 -[dsh-host-apiproxy](../../packages/host/apiproxy) 是产品消费方:它经 `ctx.workspaceRegistry` 向 GUI 客户端提供工作区的 CRUD,并执行上文「先建会话再 attach」的流程。[dsh-agent-instructions](../../packages/context/agent-instructions) 尽管名字如此,却**不是**消费方:它在 agent 自己的 cwd 下发现 AGENTS.md 风格的指令文件,从不触碰 `ctx.workspaceRegistry`——两者共用的这个词指的是用户的工作目录,而非本注册表的实体。 +[`dsh-workspace-controller`](../../packages/api/workspace-controller) 经 `ctx.workspaceRegistry` 向 GUI 客户端提供工作区 CRUD,[`dsh-session-controller`](../../packages/api/session-controller) 执行上文「先建会话再 attach」的流程。[dsh-agent-instructions](../../packages/context/agent-instructions) 尽管名字如此,却**不是**消费方:它在 agent 自己的 cwd 下发现 AGENTS.md 风格的指令文件,从不触碰 `ctx.workspaceRegistry`——两者共用的这个词指的是用户的工作目录,而非本注册表的实体。 diff --git a/docs/testing.i18n.yaml b/docs/testing.i18n.yaml index 1472642ecb..e25a35536c 100644 --- a/docs/testing.i18n.yaml +++ b/docs/testing.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/testing.md -testing.md: 6815d32f6ceff5eaf49a7fc18ca0d4e42d438ba2 -testing.zh.md: 6d0564b7dfa640bbec25ac79fa7b794ecf468f64 +testing.md: c21c67bba85387e35e80d16217c2673653281672 +testing.zh.md: 008bee58918510c8afb695a7e0b880828d698227 diff --git a/docs/testing.md b/docs/testing.md index 6815d32f6c..c21c67bba8 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -7,13 +7,13 @@ How this repo tests, tier by tier, and the rules that keep a green suite meaning ## Tiers - **Unit** (`pnpm run test`): vitest over package and example specs under their `tests/**` directories plus repository script specs under `scripts/**/*.spec.ts`; tests stay with the code area they exercise. Every registry gets an HMR-safety test (dispose the contributing fiber, assert cleanup). Prefer edge cases, error paths, event ordering, concurrency races, and permanent tests for contract regressions (see `packages/core/agent-loop/tests/contract-regressions.spec.ts`). -- **Coverage gate** (`pnpm run test:coverage`): the gating run, per-file 100% on `packages/*/*/src`. An uncovered line is often dead code the gate is correctly flagging for deletion, not a missing test to bolt on. Line coverage is necessary, never sufficient — it proves lines ran, not that the feature works as shipped. Per-file 100% on `packages/shell/pwsh-local/src` needs a real `pwsh`: without one its executor suites self-skip and `vitest.config.ts` exempts the file so pwsh-less hosts stay green, while CI runners ship pwsh and enforce the full bar. +- **Coverage gate** (`pnpm run test:coverage`): the gating run, per-file 100% on `packages/*/*/src`. An uncovered line is often dead code the gate flags for deletion, not a missing test to bolt on. Line coverage is necessary, never sufficient — it proves lines ran, not that the feature works as shipped. Per-file 100% on `packages/shell/pwsh-local/src` needs a real `pwsh`: without one its executor suites self-skip and `vitest.config.ts` exempts the file so pwsh-less hosts stay green, while CI runners ship pwsh and enforce the full bar. - **Real-API e2e** (`pnpm run test:e2e`): with-key tests against live provider APIs — the DeepSeek model plus provider-specific smokes that gate on their own keys (`EXA_API_KEY`, `PERPLEXITY_API_KEY`, …); each suite self-skips without its key so keyless CI stays green ([real-API e2e Agent Note](../.agents/notes/implemented/testing/2026-06-19-real-api-e2e-ci.md)). - **Owner-local expected output** (`pnpm run test:expected`): keyless assembled CLI/process expectations without a recorded-session round trip. Drivers use `*.expected.e2e.ts` beside `tests/expected/`; CI runs built exports. Package/script expectations use `test`, while browser expectations use `test:web`. - **Snapshot** (`pnpm run test:snapshot`): a top-level scenario's recorded `session.jsonl` supplies user input and model replay, then serves as the expected persisted result. Process scenarios start through `dsh`: headless owns one-shot behavior, the SDK owns persistent control, ACP owns automation-protocol behavior, and Web retains browser/ARIA evidence beside the same session. `snapshot.yml` declares the profile, composition/header class, recording policy, exceptional replay or input metadata, and workspace facts. Typed tokens preserve parent/child identity relationships; only header pins own prompt/schema sidecars. A mutating scenario independently compares the complete `workspace.expected/` tree, which record and refresh never rewrite. Use `test:snapshot:record` when a model transcript changes and `test:snapshot:refresh` when replay input remains valid; review every resulting diff. - **Web browser snapshot** (`pnpm run test:web`; required Linux PR gate): Chromium compares session-driven output under `snapshots/web/` and UI-only output under `apps/web/tests/expected/`. CI forces read-only `DSH_SNAPSHOT=replay`, never writing expected outputs; record/refresh stay local and every diff is reviewed ([web e2e lane](../.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.md), [CI gate decision](../.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.md)). `test:web` [builds first](../.agents/notes/implemented/bug-fix/2026-07-28-themed-scrollbars-and-reserved-gutter.md) for plugin CSS. -Session fixtures keep headers and payloads but omit body sequence/time envelopes. Replay synthesizes them; runtime persistence is unchanged. Fixtures use canonical packed rows; [the migrator](../scripts/migrate-packed-session-fixtures.ts) rewrites old layouts. +Session fixtures keep headers and payloads but omit body sequence/time envelopes. Replay synthesizes them. Fixtures use canonical packed rows; [the migrator](../scripts/migrate-packed-session-fixtures.ts) rewrites old layouts. ## The with-key policy: inference is cheap here @@ -21,18 +21,18 @@ We are DeepSeek — do not ration real-API tests. A no-key test proves plumbing; ## Prefer the real implementation over a mock -Mock only the expensive or non-deterministic boundary (LLM adapter, network, clock); keep everything downstream real. A hand-rolled stand-in proves the bridge moves bytes, not that the shipping tool behaves as asserted. Bridge tool-call tests use the scripted mock model with the real tool and executor: `makeBridgeHarness({ withBash: true })` plugs in `dsh-bash-local` and `dsh-tool-bash`, then runs `echo`. +Mock only the expensive or non-deterministic boundary (LLM adapter, network, clock); keep everything downstream real. A hand-rolled stand-in proves the bridge moves bytes, not that the shipping tool behaves as asserted. Bridge tool-call tests keep the real tool registry and pipeline behind the scripted mock model: `makeBridgeHarness()` mounts the loop, session store, tool registry, and JSONL persistence with a `MockAdapter` as the only mock (packages/acp/acp/tests/harness.ts). Recovery tests separate pre/post-chunk failures by step and prove failed chunks derive no message or tool side effect. Cover exhaustion, cancellation, policy composition, persistence, status, wire counts, transport-closing idle timeouts, and shipping Loader composition. ## Verify the world, not the self-report -An e2e assertion re-runs the command or re-reads the file externally; a keyword probe on the agent's own output lets a cheating agent pass. Assert untouched files are byte-identical. e2e tests own their resources: create the harness in the test, dispose in `afterEach` (even on failure/retry/timeout); shared fixtures live in a plain `tests/harness.ts`, never another `*.e2e.ts` (importing a spec re-registers its `describe` and duplicates real API calls). +An e2e assertion re-runs the command or re-reads the file externally; a keyword probe on the agent's own output lets a cheating agent pass. Assert untouched files are byte-identical. e2e tests own their resources: create it in the test, dispose in `afterEach` (even on failure/retry/timeout); shared fixtures live in a plain `tests/harness.ts`, never another `*.e2e.ts` (importing a spec re-registers its `describe` and duplicates real API calls). ## Test the real entry path - Product-visible plugins require a non-unit REAL-composition test. Hand-built `ctx.plugin(...)` suites are insufficient: boot test-only `cordis.yml` through Loader and app/process, mock only external services or nondeterministic inputs, and assert model-visible request/log, durable state, or user-visible output. Keep opt-ins out of shipped defaults. -- A guard only guards if the regression actually fails it. For a plugin without `inject` (bundle/composition plugins), a Loader smoke stays green when a default export replaces the required named exports — add an explicit `expect('default' in mod).toBe(false)` plus an `unwrapExports` round-trip assertion, and prove it: introduce the regression, watch red, revert. +- A guard only guards if the regression fails it. For a plugin without `inject` (bundle/composition plugins), a Loader smoke stays green when a default export replaces the required named exports — add an explicit `expect('default' in mod).toBe(false)` plus an `unwrapExports` round-trip assertion, and prove it: introduce the regression, watch red, revert. - "Real entry path" means the published artifact: a package `bin` runs built `lib/bin.js` under plain `node`, exposing failures tsx masks (settle races, module resolution, swallowed load failures). The same applies to non-index runtime entries (the worker-thread sibling `lib/worker.cjs`) and singleton modules shared across bundles (`packages/sdk/server/tests/built-scope-carrier.e2e.ts`). Keep the built-artifact smokes green (`packages/examples/*/tests/built-bin.e2e.ts`, `packages/code-runtime/code-runtime-worker-thread/tests/built-lib.e2e.ts`), and assert a genuinely-missing config exits non-zero. ## Test resolution: source plane only diff --git a/docs/testing.zh.md b/docs/testing.zh.md index 6d0564b7df..008bee5891 100644 --- a/docs/testing.zh.md +++ b/docs/testing.zh.md @@ -21,7 +21,7 @@ ## 优先使用真实实现而非 mock -只 mock 开销高或不确定的边界(LLM(大语言模型)适配器、网络、时钟);下游一切保持真实。手写替身只能证明桥接层在搬运字节,不能证明交付的工具行为符合断言。桥接工具调用测试将脚本化 mock 模型与真实工具和执行器配合使用:`makeBridgeHarness({ withBash: true })` 接入 `dsh-bash-local` 与 `dsh-tool-bash`,然后运行 `echo`。 +只 mock 开销高或不确定的边界(LLM(大语言模型)适配器、网络、时钟);下游一切保持真实。手写替身只能证明桥接层在搬运字节,不能证明交付的工具行为符合断言。桥接工具调用测试把真实的工具注册表与执行管线保留在脚本化 mock 模型下游:`makeBridgeHarness()`(packages/acp/acp/tests/harness.ts)挂载 agent loop、会话存储、工具注册表与 JSONL 持久化,唯一 mock 是脚本化 `MockAdapter`。 恢复测试按步骤区分分片前与分片后的失败,并证明失败分片不会派生出消息或工具副作用。覆盖耗尽、取消、策略组合、持久化、状态、协议计数、会关闭传输的空闲超时,以及交付的 Loader 组合。 @@ -32,7 +32,7 @@ e2e 断言应重新运行命令或从外部重新读取文件;对 agent 自身 ## 测试真实入口路径 - 产品可见的插件必须有一个非单元的真实组合测试。手动构建的 `ctx.plugin(...)` 套件不够:通过 Loader 和 app/process 启动仅用于测试的 `cordis.yml`,只 mock 外部服务或非确定性输入,断言模型可见的请求/日志、持久状态或用户可见输出。不要把 opt-in 选项混入交付默认值。 -- 一个守卫只有在回归真的能让它失败时才有效。对于没有 `inject` 的插件(bundle/组合插件),Loader 冒烟测试在默认导出替换必需的具名导出时仍然绿着——需要添加显式的 `expect('default' in mod).toBe(false)` 加 `unwrapExports` 往返断言,并证明它有效:引入回归、观察变红、回退。 +- 一个守卫只有在回归能让它失败时才有效。对于没有 `inject` 的插件(bundle/组合插件),Loader 冒烟测试在默认导出替换必需的具名导出时仍然绿着——需要添加显式的 `expect('default' in mod).toBe(false)` 加 `unwrapExports` 往返断言,并证明它有效:引入回归、观察变红、回退。 - 「真实入口路径」指已发布的产物:包的 `bin` 所运行的是构建后的 `lib/bin.js`,并由普通 `node` 执行,从而暴露 tsx 会掩盖的失败(结算竞态、模块解析、被吞掉的加载失败)。同样的规则适用于非 index 运行时入口(worker-thread 的同级文件 `lib/worker.cjs`),也适用于多个 bundle 共享的单例模块(`packages/sdk/server/tests/built-scope-carrier.e2e.ts`)。保持构建产物冒烟测试绿色(`packages/examples/*/tests/built-bin.e2e.ts`、`packages/code-runtime/code-runtime-worker-thread/tests/built-lib.e2e.ts`),并断言真正缺失的配置以非零状态退出。 ## 测试解析:仅限源码 diff --git a/docs/user/develop/basic/publish.i18n.yaml b/docs/user/develop/basic/publish.i18n.yaml index e1443bc31f..99f24a191b 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: 17f83e5448d5cfc65cd128c3fe1abbaa88925f77 -publish.zh.md: 00b8885d6cbd39fa9899e110b1d31ef96175e127 +publish.md: 89a28e2e44a5171a3f5c48c003f5af5ec2e9e750 +publish.zh.md: 590fee6f035af3009e25bf3669ba4321951f7d07 diff --git a/docs/user/develop/basic/publish.md b/docs/user/develop/basic/publish.md index 17f83e5448..89a28e2e44 100644 --- a/docs/user/develop/basic/publish.md +++ b/docs/user/develop/basic/publish.md @@ -53,7 +53,7 @@ export function apply() { } ``` -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: +Create `hello-plugin/cordis.patch.yml`. The patch is a YAML array like the `--patch` overlays you wrote, except plugin rows reference the package by name instead of a relative source path so Node resolution finds the installed code: ```yaml - insert: @@ -170,7 +170,7 @@ But a git install fetches **sources, not built artifacts**: nothing runs your `b and re-run the `add`. -Treat that allowance as what it is: **permission to execute the package's code on your machine at install time**, outside any sandbox the agent runs under. Only allow packages whose source you trust, and pin a commit (`github:you/hello-plugin#`) so a later push cannot silently change what runs. +Treat that allowance as **permission to execute the package's code on your machine at install time**, outside any sandbox the agent runs under. Only allow packages whose source you trust, and pin a commit (`github:you/hello-plugin#`) so a later push cannot silently change what runs. If you would rather not ask users for the allowance, distribute built artifacts instead — neither form needs any build permission: diff --git a/docs/user/develop/basic/publish.zh.md b/docs/user/develop/basic/publish.zh.md index 00b8885d6c..590fee6f03 100644 --- a/docs/user/develop/basic/publish.zh.md +++ b/docs/user/develop/basic/publish.zh.md @@ -53,7 +53,7 @@ export function apply() { } ``` -创建 `hello-plugin/cordis.patch.yml`。这个 patch 与一直在写的 `--patch` overlay 一样,是一个 patch 条目的 YAML 数组;区别是插件行按包名而不是相对源码路径引用这个包,这样 Node 的模块解析才能找到已安装的代码: +创建 `hello-plugin/cordis.patch.yml`。这个 patch 与你写过的 `--patch` overlay 一样,是一个 patch 条目的 YAML 数组;区别是插件行按包名而不是相对源码路径引用这个包,这样 Node 的模块解析才能找到已安装的代码: ```yaml - insert: @@ -170,7 +170,7 @@ dsh plugin --profile demo add github:you/hello-plugin 然后重新执行 `add`。 -请如实看待这项授权:**允许该包的代码在安装时于你的机器上执行**,且不在 agent 运行的任何沙箱之内。只对源码可信的包授权,并锁定 commit(`github:you/hello-plugin#`),让后续推送无法悄悄改变实际运行的内容。 +请把这项授权视为**允许该包的代码在安装时于你的机器上执行**,且不在 agent 运行的任何沙箱之内。只对源码可信的包授权,并锁定 commit(`github:you/hello-plugin#`),让后续推送无法悄悄改变实际运行的内容。 如果不想让用户做这项授权,就改为分发构建产物——以下两种形式都不需要任何构建权限: diff --git a/docs/user/develop/framework/events.i18n.yaml b/docs/user/develop/framework/events.i18n.yaml index d3ac77ddf3..7c3c92b655 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: 17ce5f6c4a70e406b5e3d9e5dd26182f62dc9868 -events.zh.md: 6936a5ad6c51393d2fbcd5103d4e418ce010d569 +events.md: c8c1bc753a2353f735f3f0d56f1c7e7f37b5be6d +events.zh.md: 366c37ca4dc02a97d8ecb43d0425da141ec4253d diff --git a/docs/user/develop/framework/events.md b/docs/user/develop/framework/events.md index 17ce5f6c4a..c8c1bc753a 100644 --- a/docs/user/develop/framework/events.md +++ b/docs/user/develop/framework/events.md @@ -101,7 +101,7 @@ declare module '@deepseek-ai/cordis' { ## Cordis events and session records -Harness Cordis events use `namespace/action` names, including `agent/step`, `agent/request`, `agent/request-error`, `tools/result`, and `session/event`. The generated `cordis-surface` regions on the [subsystem pages](../../../subsystems/core.md) record complete signatures and modes. +Harness Cordis events use `namespace/action` names, including `agent/pre-step`, `agent/request`, `agent/request-error`, `tools/result`, and `session/event`. The generated `cordis-surface` regions on the [subsystem pages](../../../subsystems/core.md) record complete signatures and modes. `turn/*`, `step/*`, `tool/call`, `tool/result`, and `compaction/*` are durable session-event types, not same-named Cordis events. To observe them, listen to `session/event` and inspect `event.type`. diff --git a/docs/user/develop/framework/events.zh.md b/docs/user/develop/framework/events.zh.md index 6936a5ad6c..366c37ca4d 100644 --- a/docs/user/develop/framework/events.zh.md +++ b/docs/user/develop/framework/events.zh.md @@ -101,7 +101,7 @@ declare module '@deepseek-ai/cordis' { ## Cordis 事件与会话记录 -Harness 的 Cordis 事件遵循 `namespace/action` 命名,例如 `agent/step`、`agent/request`、`agent/request-error`、`tools/result` 和 `session/event`。完整签名与触发模式见[子系统页面](../../../subsystems/core.zh.md)上生成的 `cordis-surface` 区块。 +Harness 的 Cordis 事件遵循 `namespace/action` 命名,例如 `agent/pre-step`、`agent/request`、`agent/request-error`、`tools/result` 和 `session/event`。完整签名与触发模式见[子系统页面](../../../subsystems/core.zh.md)上生成的 `cordis-surface` 区块。 `turn/*`、`step/*`、`tool/call`、`tool/result` 和 `compaction/*` 是持久化的会话事件类型,不是同名 Cordis 事件。需要观察它们时,监听 `session/event` 并检查 `event.type`。 diff --git a/docs/user/guide/github-review.i18n.yaml b/docs/user/guide/github-review.i18n.yaml index f070062352..19c790a17d 100644 --- a/docs/user/guide/github-review.i18n.yaml +++ b/docs/user/guide/github-review.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/github-review.md -github-review.md: a104c1ec8d32d23ccbcb88a492ed2a9ede474c01 -github-review.zh.md: 4800dbc65c098e871573ac01d3904e6ab1b991f9 +github-review.md: 69c61df95ce7d461b8a120cf6ae4a568ce201f78 +github-review.zh.md: 114aa6546d2366cf34a4a2d3057e9a26e4c43c96 diff --git a/docs/user/guide/github-review.md b/docs/user/guide/github-review.md index a104c1ec8d..69c61df95c 100644 --- a/docs/user/guide/github-review.md +++ b/docs/user/guide/github-review.md @@ -97,6 +97,6 @@ if (workspacePath === undefined) return null ## Delivery semantics -The webhook runtime stores no delivery or execution state. Repeated delivery runs the rule again and may create another Session. A crash loses rule calls that have not admitted their prompt. After prompt admission, the ordinary Session log, persistence, Workspace, and Agent lifecycle own the work. +The webhook runtime stores no delivery or execution state. Repeated delivery runs the rule and may create another Session. A crash loses rule calls that have not admitted their prompt. After prompt admission, the ordinary Session log, persistence, Workspace, and Agent lifecycle own the work. The webhook secret authenticates inbound GitHub data only. It grants neither rule code nor the created Agent outbound GitHub access; configure that authority separately when a rule or Agent needs it. diff --git a/docs/user/guide/github-review.zh.md b/docs/user/guide/github-review.zh.md index 4800dbc65c..114aa6546d 100644 --- a/docs/user/guide/github-review.zh.md +++ b/docs/user/guide/github-review.zh.md @@ -97,6 +97,6 @@ if (workspacePath === undefined) return null ## 交付语义 -webhook runtime 不存储交付或执行状态。重复交付会再次运行规则,并可能创建另一个 Session。崩溃会丢失尚未接纳提示词的规则调用。提示词接纳后,工作由普通 Session 日志、persistence、Workspace 与 Agent 生命周期拥有。 +webhook runtime 不存储交付或执行状态。重复交付会运行规则,并可能创建另一个 Session。崩溃会丢失尚未接纳提示词的规则调用。提示词接纳后,工作由普通 Session 日志、persistence、Workspace 与 Agent 生命周期拥有。 webhook 密钥只验证入站 GitHub 数据。它不会向规则代码或所创建 Agent 授予出站 GitHub 访问权;规则或 Agent 需要时应单独配置该权限。 diff --git a/docs/user/guide/mcp-memory.i18n.yaml b/docs/user/guide/mcp-memory.i18n.yaml index 055f25ce02..7b8f1ec095 100644 --- a/docs/user/guide/mcp-memory.i18n.yaml +++ b/docs/user/guide/mcp-memory.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/mcp-memory.md -mcp-memory.md: b7654f2d751556ab5d51f90570e7f1f7915e6ea5 -mcp-memory.zh.md: 36b26160fd29e706a98eacd57182f7a655983d24 +mcp-memory.md: 2bc3c6be49b78224932c66e7d0d832dcc026816b +mcp-memory.zh.md: 66dd3400b64e8534835b9b39121a0ff8b48af6a9 diff --git a/docs/user/guide/mcp-memory.md b/docs/user/guide/mcp-memory.md index b7654f2d75..2bc3c6be49 100644 --- a/docs/user/guide/mcp-memory.md +++ b/docs/user/guide/mcp-memory.md @@ -79,7 +79,7 @@ Use one unique value and keep the provider's storage scope unchanged throughout: 2. Create DSH session B in the same running Host. Do not copy session A's conversation. Ask: `What is my validation drink? Check memory.` Confirm the model called the provider's search or recall tool and returned the value. 3. Still in session B, ask: `Use that preference to suggest one drink for the meeting.` Confirm the answer uses the recalled value. -A new DSH session is required; a Host restart is not. Restart or HMR is needed only after an MCP child crashes because the current generic client does not auto-reconnect; its tool registrations remain until plugin disposal or a successful re-sync, and calls can fail against the closed transport. Initial discovery is asynchronous, so wait for the provider's `mcp__...` tools before sending the first validation prompt. +A new DSH session is required; a Host restart is not. A crashed MCP child triggers automatic reconnection with backoff and a tool re-sync; tools stay listed and calls fail only during the outage, and after the reconnect budget is exhausted the tools are unregistered and reconnection stops until a reload or restart. Initial discovery is asynchronous, so wait for the provider's `mcp__...` tools before sending the first validation prompt. ## Bring another MCP server diff --git a/docs/user/guide/mcp-memory.zh.md b/docs/user/guide/mcp-memory.zh.md index 36b26160fd..66dd3400b6 100644 --- a/docs/user/guide/mcp-memory.zh.md +++ b/docs/user/guide/mcp-memory.zh.md @@ -79,7 +79,7 @@ Engram 负责存储和项目选择:它默认使用 `~/.engram`,从 DSH 工 2. 在同一个仍在运行的 Host 中创建 DSH 会话 B。不要复制会话 A 的对话。提出:`What is my validation drink? Check memory.`。确认模型调用了提供方的搜索或召回工具,并返回该值。 3. 继续在会话 B 中提出:`Use that preference to suggest one drink for the meeting.`。确认回答使用了召回的值。 -必须新建 DSH 会话,但不需要重启 Host。只有 MCP 子进程崩溃后才需要重启或执行 HMR(热模块替换),因为当前的通用客户端不会自动重连;其工具注册会一直保留,直到插件 dispose(资源释放)或成功重新同步,针对已关闭传输的调用可能失败。初始发现过程是异步的,因此发送第一条验证提示词前,请等待提供方的 `mcp__...` 工具出现。 +必须新建 DSH 会话,但不需要重启 Host。MCP 子进程崩溃后会触发带退避的自动重连与工具重新同步;停机期间工具仍保持列出,调用只在停机期间失败;重连预算耗尽后工具会被注销,重连停止,直到重新加载或重启。初始发现过程是异步的,因此发送第一条验证提示词前,请等待提供方的 `mcp__...` 工具出现。 ## 接入其他 MCP 服务器 diff --git a/package.json b/package.json index 9d18b63251..65b9786b40 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "verify-skill-invocation-metadata": "tsx scripts/verify-skill-invocation-metadata.ts", "verify-translation-prompt": "tsx scripts/verify-translation-prompt.ts", "verify-translation-pairing": "tsx scripts/verify-translation-pairing.ts", + "test:docs": "tsx scripts/run-gates.ts doc-quick", "resolve-translation-pairing-conflicts": "tsx scripts/merge-translation-pairing.ts --resolve", "gen-translation-brief": "tsx scripts/gen-translation-brief.ts", "verify-doc-budgets": "tsx scripts/verify-doc-budgets.ts", diff --git a/packages/README.i18n.yaml b/packages/README.i18n.yaml index 77300ee360..7816391ff8 100644 --- a/packages/README.i18n.yaml +++ b/packages/README.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 packages/README.md -README.md: d663289f32586f49ef0294b21d8675b483a7725b -README.zh.md: 6569b05c5b557bfb131abee4e29f8c146aabc04d +README.md: c3dcbd3032e3eadad21b640f3a614ef5a1a6c6e7 +README.zh.md: 80518ef0de7c2022db45aefa411f507870cf09aa diff --git a/packages/README.md b/packages/README.md index d663289f32..c3dcbd3032 100644 --- a/packages/README.md +++ b/packages/README.md @@ -1,71 +1,115 @@ +--- +description: "The DeepSeek Harness package workspace: how the npm packages under packages/ are grouped, what each group owns, and the conventions that bind them." +kind: "package-group" +--- + # Packages English | [中文](README.zh.md) -npm scope: `@deepseek-ai/dsh-*`; Cordis `Service` subclasses and function plugins contribute through `ctx.effect()`, `ctx.on()`, or `ctx.waterfall()`. Rules: [package](AGENTS.md), [root](../AGENTS.md#conventions). +## Summary -## Hierarchy +The harness is assembled from npm packages under `packages/`, grouped by capability family: sessions and the agent loop, model-facing tools, shell and filesystem execution, web access, subagents, and the rest. Use this page as the top-level map: find the owning group, then open its README for the package list. Every package is scoped `@deepseek-ai/dsh-*` and lives in exactly one group; each group README is the authoritative package map for its family. -Groups hold `packages///`; names stay `@deepseek-ai/dsh-`. **Group READMEs own package/ctx-key maps.** +## Table of Contents -| Group | Role | Release expectation | -|---|---|---| -| [`core/`](core/README.md) | Product API spine: sessions, prompts, tools, agent services, and the concrete loop | Product — stable API | -| [`api/`](api/README.md) | Remote BFF assembly and Typert RPC gateway | Product — stable API | -| [`typert/`](typert/README.md) | Type graph generation, artifact loading, and runtime registry | Product — stable API | -| [`goal/`](goal/README.md) | Same-session goal persistence and lifecycle | Product — stable API | -| [`schedule/`](schedule/README.md) | Session-local scheduled follow-ups | Product — stable API | -| [`feedback/`](feedback/README.md) | Human feedback | Product — stable API | -| [`identity/`](identity/README.md) | Shared anonymous identity | Product — stable API | -| [`llm/`](llm/README.md) | LLM capability family: the abstract service + provider adapters | Product — stable API | -| [`e2b/`](e2b/README.md) | E2B providers | POC | -| [`subprocess/`](subprocess/README.md) | Subprocess capability family: Service Definition, local process-tree provider, and shared Win32 process library | Product — stable API | -| [`shell/`](shell/README.md) | Bash capability family: executor seam, local impl, model-facing tool | Product — stable API | -| [`terminal/`](terminal/README.md) | Persistent PTY capability family: owner-scoped sessions, local implementation, and model-facing tools | Product — stable API | -| [`code-runtime/`](code-runtime/README.md) | Code-execution capability family: Service Definition + worker-thread provider + Code Mode Consumer | Product — stable API | -| [`sandbox/`](sandbox/README.md) | Process-confinement seam; bwrap/Landlock/Seatbelt backends | Product — stable API | -| [`fs/`](fs/README.md) | Filesystem capability family: seam, local impl, model-facing file tools, bash-backed discovery tools | Product — stable API | -| [`lsp/`](lsp/README.md) | LSP capability family: seam, generic stdio provider, and the `lsp` tool | Product — stable API | -| [`skill/`](skill/README.md) | Skill capability family: the provider registry, local provider, and model-facing catalog/loader | Product — stable API | -| [`compaction/`](compaction/README.md) | Compaction capability family: Service Definition + basic provider + command Consumer | Product — stable API | -| [`context/`](context/README.md) | Model-visible request context, including workspace instructions and time context | Product — stable API | -| [`subagent/`](subagent/README.md) | Subagent capability family: the provider-registry contract and the model-facing delegation tool | Product — stable API | -| [`jobs/`](jobs/README.md) | Generic background-job runtime and model-facing `job_*` control tools | Product — stable API | -| [`experimental/`](experimental/README.md) | Private prototypes and internal-only plugins | Unreleased | -| [`workflow/`](workflow/README.md) | Workflow seam, worker-thread engine, and model-facing `workflow`/`ralph` tools | Product — stable API | -| [`webhook/`](webhook/README.md) | Verified external events, rules, and fire-and-forget Workspace Sessions | Product — stable API | -| [`web/`](web/README.md) | Web capability family: seam, search/fetch provider impls, and the model-facing web tools | Product — stable API | -| [`attachment/`](attachment/README.md) | Durable attachment identity, validation, local content-addressed storage | Product — stable API | -| [`spill/`](spill/README.md) | Spill capability family: storage seam, local impl, tool-result spill policy | Product — stable API | -| [`todo/`](todo/README.md) | The model-facing `todo_write` tool | Product — stable API | -| [`plan/`](plan/README.md) | Plan collaboration state with a direct entry command and reviewed exit | Product — stable API | -| [`preset/`](preset/README.md) | Per-session agent composition from preset `cordis.yml` files | Product — stable API | -| [`guard/`](guard/README.md) | Loop-hygiene guards: advisory repeat-call reminders + the `tools/execute` deadline enforcer | Product — stable API | -| [`bundle/`](bundle/README.md) | Installable `dsh --profile` patch layers | Product — stable API | -| [`extensions/`](extensions/README.md) | Agent runtime self-modification: live plugin/service inspection and model-written plugin mount/unmount ([design](../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md)) | Product — stable API | -| [`hooks/`](hooks/README.md) | Hook bridges + the shared Claude Code / Codex wire-protocol library | Product — stable API | -| [`session/`](session/README.md) | Durable session data plane: persistence seam + JSONL/SQLite backends, projection seam, log-backed titles, session reporting | Product — stable API | -| [`session-query/`](session-query/README.md) | Session retrieval family: logical corpus, bounded reads, lineage, event relationships, semantic filtering, and SQLite full-text search | Product — stable API | -| [`settings/`](settings/README.md) | User-settings seam + file-backed provider | Product — stable API | -| [`credentials/`](credentials/README.md) | Credential reference/record seam + env-over-`.env` provider + authorization flows | Product — stable API | -| [`storage/`](storage/README.md) | Non-session storage hub + backends + domain form | Product — stable API | -| [`workspace/`](workspace/README.md) | Workspace entity | Product — stable API | -| [`sdk/`](sdk/README.md) | Out-of-process SDK: JSON-RPC protocol and TypeScript client/server | Product — stable API | -| [`acp/`](acp/README.md) | Automation-only Agent Client Protocol server | Product — stable API | -| [`interaction/`](interaction/README.md) | Human-collaboration plane: approval/interaction seams, permission preset, commands, ask-user tool | Product — stable API | -| [`boot/`](boot/README.md) | Shared app-bin boot glue | Product — stable API | -| [`host/`](host/README.md) | Web-GUI host half: API gateway + HTTP route server | Product — stable API | -| [`client/`](client/README.md) | Web-GUI browser half: shell, wire, object services, slots, `ui-*` plugins | Product — stable API | -| [`examples/`](examples/README.md) | Reusable composition bundles for tests and custom deployments | Support — composition infra | -| [`test-support/`](test-support/README.md) | Support infrastructure (testkits, invariants, replay, Loader smokes) | Support — lower compatibility expectations | -| [`util/`](util/README.md) | Low-level zero-dependency utilities shared across groups (`Branded`, Harness home/path helpers, timeout, retention) | Support — small, stable, harness-dep-free | +- [Package groups](#package-groups) +- [Release expectations](#release-expectations) +- [Dependencies](#dependencies) +- [Package README contracts](#package-readme-contracts) +- [Dev Note](#dev-note) -New packages join existing groups; new groups update their README and this table. +----- + +## Package groups + +Every package lives in exactly one group; new packages join existing groups, and a new group updates its own README and this table. + +| Group | Role | +|---|---| +| [`core/`](core/README.md) | Product API spine: sessions, prompts, tools, agent services, and the concrete loop | +| [`api/`](api/README.md) | Remote BFF assembly and Typert RPC gateway | +| [`typert/`](typert/README.md) | Type graph generation, artifact loading, and runtime registry | +| [`goal/`](goal/README.md) | Same-session goal persistence and lifecycle | +| [`schedule/`](schedule/README.md) | Session-local scheduled follow-ups | +| [`feedback/`](feedback/README.md) | Human feedback capture and command | +| [`identity/`](identity/README.md) | Shared anonymous identity | +| [`llm/`](llm/README.md) | LLM capability family: abstract service + provider adapters | +| [`e2b/`](e2b/README.md) | E2B remote-runtime providers | +| [`subprocess/`](subprocess/README.md) | Subprocess capability family: Service Definition + local process-tree provider | +| [`shell/`](shell/README.md) | Bash capability family: executor seam, local impl, model-facing tools | +| [`terminal/`](terminal/README.md) | Persistent PTY capability family: owner-scoped sessions, local implementation, model-facing tools | +| [`code-runtime/`](code-runtime/README.md) | Code-execution capability family: Service Definition + worker-thread provider + Code Mode Consumer | +| [`sandbox/`](sandbox/README.md) | Process-confinement seam; bwrap/Landlock/Seatbelt backends | +| [`fs/`](fs/README.md) | Filesystem capability family: seam, local impl, model-facing file tools, discovery tools | +| [`lsp/`](lsp/README.md) | LSP capability family: seam, generic stdio provider, and the `lsp` tool | +| [`skill/`](skill/README.md) | Skill capability family: provider registry, local provider, model-facing catalog/loader | +| [`compaction/`](compaction/README.md) | Compaction capability family: Service Definition + basic provider + command Consumer | +| [`context/`](context/README.md) | Model-visible request context: workspace instructions, time context, references | +| [`subagent/`](subagent/README.md) | Subagent capability family: provider-registry contract and model-facing delegation tools | +| [`jobs/`](jobs/README.md) | Generic background-job runtime and model-facing job control tools | +| [`experimental/`](experimental/README.md) | Private prototypes and internal-only plugins | +| [`workflow/`](workflow/README.md) | Workflow seam, worker-thread engine, and model-facing `workflow`/`ralph` tools | +| [`webhook/`](webhook/README.md) | Verified external events, trusted rules, and fire-and-forget Workspace Sessions | +| [`web/`](web/README.md) | Web capability family: seam, search/fetch providers, model-facing web tools | +| [`attachment/`](attachment/README.md) | Durable attachment identity, validation, local content-addressed storage | +| [`spill/`](spill/README.md) | Spill capability family: storage seam, local impl, tool-result spill policy | +| [`todo/`](todo/README.md) | The model-facing `todo_write` tool | +| [`plan/`](plan/README.md) | Plan collaboration state with a direct entry command and reviewed exit | +| [`preset/`](preset/README.md) | Per-session agent composition from preset `cordis.yml` files | +| [`guard/`](guard/README.md) | Loop-hygiene guards: advisory repeat-call reminders + the `tools/execute` deadline enforcer | +| [`bundle/`](bundle/README.md) | Installable `dsh --profile` patch layers | +| [`extensions/`](extensions/README.md) | Agent runtime self-modification: live plugin/service inspection and model-written mount/unmount | +| [`hooks/`](hooks/README.md) | Hook bridges + the shared Claude Code / Codex wire-protocol library | +| [`session/`](session/README.md) | Durable session data plane: persistence seam + backends, projection seam, log-backed titles, session reporting | +| [`session-query/`](session-query/README.md) | Session retrieval family: logical corpus, bounded reads, lineage, semantic filtering, SQLite full-text search | +| [`settings/`](settings/README.md) | User-settings seam + file-backed provider | +| [`credentials/`](credentials/README.md) | Credential-reference and credential-record seam + env-over-`.env` provider + authorization flows that ask a human | +| [`storage/`](storage/README.md) | Non-session storage hub + backends + domain form | +| [`workspace/`](workspace/README.md) | Workspace entity | +| [`sdk/`](sdk/README.md) | Out-of-process SDK: JSON-RPC protocol and TypeScript client/server | +| [`acp/`](acp/README.md) | Automation-only Agent Client Protocol server | +| [`interaction/`](interaction/README.md) | Human-collaboration plane: approval/interaction seams, permission preset, commands, ask-user tool | +| [`boot/`](boot/README.md) | Shared app-bin boot glue | +| [`host/`](host/README.md) | Web-GUI host half: API gateway + HTTP route server | +| [`client/`](client/README.md) | Web-GUI browser half: shell, wire, object services, slots, `ui-*` plugins | +| [`examples/`](examples/README.md) | Reusable composition bundles for tests and custom deployments | +| [`test-support/`](test-support/README.md) | Support infrastructure (testkits, invariants, replay, Loader smokes) | +| [`runtime-diagnostics/`](runtime-diagnostics/README.md) | Runtime diagnostics: package-owned invariant checks and reports | +| [`util/`](util/README.md) | Low-level zero-dependency utilities shared across groups (`Branded`, home/path helpers, timeout, retention) | + +----- + + +## Release expectations + +Most groups are product — stable API. The exceptions: `e2b/` is a POC, `experimental/` is unreleased, and `examples/`, `test-support/`, `runtime-diagnostics/`, and `util/` are support with lower compatibility expectations. + +----- + + ## Dependencies The dependency graph is generated: [docs/module-graph.md](../docs/module-graph.md) (`pnpm run gen-module-graph`, freshness-gated in CI). **Extension plugins depend on Service Definitions, never concrete providers.** `dsh-agent-loop` is swappable; UI, hook, and tool plugins use `dsh-agent`. Composition bundles, including `dsh-agent-spine-demo`, may depend on spine plugins. Capabilities separate Service Definition / Service Provider / Consumer roles when they evolve independently; see [capability seams](../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md). -Package READMEs cover purpose, APIs, extension points, and [Model Experience](../docs/cookbook/adding-a-package.md#4-write-the-package-readme) unless on the model-agnostic [omission allowlist](../scripts/verify-package-readme-model-experience.ts). They also carry `## Known Limitations and Deferred Work` or use its [allowlist](../scripts/verify-package-readme-limitations.ts). +----- + + +## Package README contracts + +Every package README covers purpose, configuration, extension points, and [Model Experience](../docs/cookbook/adding-a-package.md#4-write-the-package-readme) unless the model-agnostic [omission allowlist](../scripts/verify-package-readme-model-experience.ts) exempts it. It also carries `## Known Limitations and Deferred Work` or uses its [allowlist](../scripts/verify-package-readme-limitations.ts). Package conventions — exports, service access, invariants, tests — live in [packages/AGENTS.md](AGENTS.md). + +----- + + +## Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/README.zh.md b/packages/README.zh.md index 6569b05c5b..80518ef0de 100644 --- a/packages/README.zh.md +++ b/packages/README.zh.md @@ -1,71 +1,115 @@ +--- +description: "DeepSeek Harness 包工作区:packages/ 下的 npm 包如何分组、每个组负责什么,以及约束它们的约定。" +kind: "package-group" +--- + # 包 [English](README.md) | 中文 -npm scope 为 `@deepseek-ai/dsh-*`;Cordis `Service` 子类和函数插件通过 `ctx.effect()`、`ctx.on()` 或 `ctx.waterfall()` 注册。规则见[包](AGENTS.md)与[根规则](../AGENTS.md#conventions)。 +## 概述 -## 层级结构 +harness 由 `packages/` 下的 npm 包组装而成,按能力系列分组:会话与 agent 循环、面向模型的工具、shell 与文件系统执行、Web 访问、subagent 等等。把本页当作顶层地图使用:先找到拥有某能力的组,再打开其 README 查看包列表。每个包都以 `@deepseek-ai/dsh-*` 为作用域、只属于一个组;每个组的 README 都是该能力系列的权威包映射。 -包按组置于 `packages///`;包名仍为 `@deepseek-ai/dsh-`。**组 README 负责包/ctx 键映射。** +## 目录 -| 组 | 职责 | 发布预期 | -|---|---|---| -| [`core/`](core/README.zh.md) | 产品 API 主干:会话、提示词、工具、agent(智能体)服务与具体循环 | 产品:稳定 API | -| [`api/`](api/README.zh.md) | Remote BFF 装配与 Typert RPC 网关 | 产品:稳定 API | -| [`typert/`](typert/README.zh.md) | 类型图生成、产物加载与运行时注册表 | 产品:稳定 API | -| [`goal/`](goal/README.zh.md) | 同会话 goal 的持久化与生命周期 | 产品:稳定 API | -| [`schedule/`](schedule/README.zh.md) | 仅限会话内的定时后续操作 | 产品:稳定 API | -| [`feedback/`](feedback/README.zh.md) | 人类反馈 | 产品:稳定 API | -| [`identity/`](identity/README.zh.md) | 共享匿名身份 | 产品:稳定 API | -| [`llm/`](llm/README.zh.md) | LLM(大语言模型)能力系列:抽象服务 + 提供方适配器 | 产品:稳定 API | -| [`e2b/`](e2b/README.zh.md) | E2B 提供方 | POC | -| [`subprocess/`](subprocess/README.zh.md) | 子进程能力系列:Service Definition、本地进程树提供方与共享 Win32 进程库 | 产品:稳定 API | -| [`shell/`](shell/README.zh.md) | Bash 能力系列:执行器 seam、本地实现、面向模型的工具 | 产品:稳定 API | -| [`terminal/`](terminal/README.zh.md) | 持久 PTY 能力系列:限定所有者范围的会话、本地实现和面向模型的工具 | 产品:稳定 API | -| [`code-runtime/`](code-runtime/README.zh.md) | 代码执行能力系列:Service Definition + worker 线程提供方 + Code Mode Consumer | 产品:稳定 API | -| [`sandbox/`](sandbox/README.zh.md) | 进程限制 seam;bwrap/Landlock/Seatbelt 后端 | 产品:稳定 API | -| [`fs/`](fs/README.zh.md) | 文件系统能力系列:seam、本地实现、面向模型的文件工具、由 bash 支持的发现工具 | 产品:稳定 API | -| [`lsp/`](lsp/README.zh.md) | LSP 能力系列:seam、通用 stdio 提供方和 `lsp` 工具 | 产品:稳定 API | -| [`skill/`](skill/README.zh.md) | skill(技能)能力系列:提供方注册表、本地提供方和面向模型的目录/loader | 产品:稳定 API | -| [`compaction/`](compaction/README.zh.md) | 压缩(compaction)能力系列:Service Definition + 基础提供方 + 命令 Consumer | 产品:稳定 API | -| [`context/`](context/README.zh.md) | 模型可见请求上下文,包括 workspace 指令和时间上下文 | 产品:稳定 API | -| [`subagent/`](subagent/README.zh.md) | subagent 能力系列:提供方注册表约定和面向模型的委托工具 | 产品:稳定 API | -| [`jobs/`](jobs/README.zh.md) | 通用后台任务运行时和面向模型的 `job_*` 控制工具 | 产品:稳定 API | -| [`experimental/`](experimental/README.zh.md) | 私有原型与内部专用插件 | 不发布 | -| [`workflow/`](workflow/README.zh.md) | 工作流 seam、worker 线程引擎和面向模型的 `workflow`/`ralph` 工具 | 产品:稳定 API | -| [`webhook/`](webhook/README.zh.md) | 已验证外部事件、规则与 fire-and-forget Workspace Session | 产品:稳定 API | -| [`web/`](web/README.zh.md) | Web 能力系列:seam、搜索/获取提供方实现和面向模型的 Web 工具 | 产品:稳定 API | -| [`attachment/`](attachment/README.zh.md) | 持久附件标识、校验、本地内容寻址存储 | 产品:稳定 API | -| [`spill/`](spill/README.zh.md) | spill 能力系列:存储 seam、本地实现、工具结果 spill 策略 | 产品:稳定 API | -| [`todo/`](todo/README.zh.md) | 面向模型的 `todo_write` 工具 | 产品:稳定 API | -| [`plan/`](plan/README.zh.md) | Plan 协作状态,提供直接进入命令与经评审的退出 | 产品:稳定 API | -| [`preset/`](preset/README.zh.md) | 由 preset `cordis.yml` 按会话组装 agent | 产品:稳定 API | -| [`guard/`](guard/README.zh.md) | 循环卫生守卫:建议性重复调用提醒 + `tools/execute` 截止时间强制执行器 | 产品:稳定 API | -| [`bundle/`](bundle/README.zh.md) | 可安装的 `dsh --profile` 补丁层 | 产品:稳定 API | -| [`extensions/`](extensions/README.zh.md) | agent 运行时自修改:实时插件/服务检查和模型所写插件挂载/卸载([设计](../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.zh.md)) | 产品:稳定 API | -| [`hooks/`](hooks/README.zh.md) | 钩子桥接 + 共享的 Claude Code/Codex 线协议库 | 产品:稳定 API | -| [`session/`](session/README.zh.md) | 持久会话数据平面:持久化 seam + JSONL/SQLite 后端、投影 seam、基于日志的标题、会话上报 | 产品:稳定 API | -| [`session-query/`](session-query/README.zh.md) | 会话检索系列:逻辑语料库、有界读取、血缘、事件关系、语义过滤和 SQLite 全文搜索 | 产品:稳定 API | -| [`settings/`](settings/README.zh.md) | 用户设置 seam + 基于文件的提供方 | 产品:稳定 API | -| [`credentials/`](credentials/README.zh.md) | 凭据引用/记录 seam + 环境变量优先于 `.env` 的提供方 + 授权 flow | 产品:稳定 API | -| [`storage/`](storage/README.zh.md) | 非会话存储中枢 + 后端 + 领域形式 | 产品:稳定 API | -| [`workspace/`](workspace/README.zh.md) | Workspace 实体 | 产品:稳定 API | -| [`sdk/`](sdk/README.zh.md) | 进程外 SDK:JSON-RPC 协议与 TypeScript 客户端/服务器 | 产品:稳定 API | -| [`acp/`](acp/README.zh.md) | 仅面向自动化的 ACP(Agent Client Protocol)服务器 | 产品:稳定 API | -| [`interaction/`](interaction/README.zh.md) | 人机协作平面:批准/交互 seam、权限预设、命令、询问用户的工具 | 产品:稳定 API | -| [`boot/`](boot/README.zh.md) | 共享的 app bin 启动粘合层 | 产品:稳定 API | -| [`host/`](host/README.zh.md) | web GUI 宿主半侧:API 网关 + HTTP 路由服务器 | 产品:稳定 API | -| [`client/`](client/README.zh.md) | web GUI 浏览器半侧:shell、协议层、对象服务、slot、`ui-*` 插件 | 产品:稳定 API | -| [`examples/`](examples/README.zh.md) | 供测试与自定义部署复用的组合包 | 支持:组合基础设施 | -| [`test-support/`](test-support/README.zh.md) | 支持基础设施(testkit、不变式、回放、Loader 冒烟测试) | 支持:兼容性预期较低 | -| [`util/`](util/README.zh.md) | 组间共享的低层零依赖工具(`Branded`、Harness home/路径辅助函数、超时、留存) | 支持:小型、稳定、无 harness 依赖 | +- [包分组](#package-groups) +- [发布预期](#release-expectations) +- [依赖](#dependencies) +- [包 README 约定](#package-readme-contracts) +- [开发备注](#dev-note) -新包加入现有组;新组更新其 README 和此表。 +----- + +## 包分组 + +每个包只属于一个组;新包加入现有组,新组则更新其自身 README 与本表。 + +| 组 | 职责 | +|---|---| +| [`core/`](core/README.zh.md) | 产品 API 主干:会话、提示词、工具、agent 服务与具体循环 | +| [`api/`](api/README.zh.md) | Remote BFF 装配与 Typert RPC 网关 | +| [`typert/`](typert/README.zh.md) | 类型图生成、产物加载与运行时注册表 | +| [`goal/`](goal/README.zh.md) | 同会话 goal 的持久化与生命周期 | +| [`schedule/`](schedule/README.zh.md) | 仅限会话内的定时后续操作 | +| [`feedback/`](feedback/README.zh.md) | 人类反馈的采集与命令 | +| [`identity/`](identity/README.zh.md) | 共享匿名身份 | +| [`llm/`](llm/README.zh.md) | LLM 能力系列:抽象服务 + 提供方适配器 | +| [`e2b/`](e2b/README.zh.md) | E2B 远程运行时提供方 | +| [`subprocess/`](subprocess/README.zh.md) | 子进程能力系列:Service Definition + 本地进程树提供方 | +| [`shell/`](shell/README.zh.md) | Bash 能力系列:执行器 seam、本地实现、面向模型的工具 | +| [`terminal/`](terminal/README.zh.md) | 持久 PTY 能力系列:限定所有者范围的会话、本地实现、面向模型的工具 | +| [`code-runtime/`](code-runtime/README.zh.md) | 代码执行能力系列:Service Definition + worker 线程提供方 + Code Mode Consumer | +| [`sandbox/`](sandbox/README.zh.md) | 进程限制 seam;bwrap/Landlock/Seatbelt 后端 | +| [`fs/`](fs/README.zh.md) | 文件系统能力系列:seam、本地实现、面向模型的文件工具、发现工具 | +| [`lsp/`](lsp/README.zh.md) | LSP 能力系列:seam、通用 stdio 提供方和 `lsp` 工具 | +| [`skill/`](skill/README.zh.md) | skill 能力系列:提供方注册表、本地提供方、面向模型的目录/loader | +| [`compaction/`](compaction/README.zh.md) | 压缩能力系列:Service Definition + 基础提供方 + 命令 Consumer | +| [`context/`](context/README.zh.md) | 模型可见请求上下文:workspace 指令、时间上下文、引用 | +| [`subagent/`](subagent/README.zh.md) | subagent 能力系列:提供方注册表约定和面向模型的委托工具 | +| [`jobs/`](jobs/README.zh.md) | 通用后台任务运行时和面向模型的作业控制工具 | +| [`experimental/`](experimental/README.zh.md) | 私有原型与内部专用插件 | +| [`workflow/`](workflow/README.zh.md) | 工作流 seam、worker 线程引擎、面向模型的 `workflow`/`ralph` 工具 | +| [`webhook/`](webhook/README.zh.md) | 已验证外部事件、受信规则与即发即弃 Workspace Session | +| [`web/`](web/README.zh.md) | Web 能力系列:seam、搜索/获取提供方、面向模型的 Web 工具 | +| [`attachment/`](attachment/README.zh.md) | 持久附件标识、校验、本地内容寻址存储 | +| [`spill/`](spill/README.zh.md) | spill 能力系列:存储 seam、本地实现、工具结果 spill 策略 | +| [`todo/`](todo/README.zh.md) | 面向模型的 `todo_write` 工具 | +| [`plan/`](plan/README.zh.md) | Plan 协作状态,提供直接进入命令与经评审的退出 | +| [`preset/`](preset/README.zh.md) | 由 preset `cordis.yml` 按会话组装 agent | +| [`guard/`](guard/README.zh.md) | 循环卫生守卫:建议性重复调用提醒 + `tools/execute` 截止时间强制执行器 | +| [`bundle/`](bundle/README.zh.md) | 可安装的 `dsh --profile` 补丁层 | +| [`extensions/`](extensions/README.zh.md) | agent 运行时自修改:实时插件/服务检查与模型所写挂载/卸载 | +| [`hooks/`](hooks/README.zh.md) | 钩子桥接 + 共享的 Claude Code / Codex 线协议库 | +| [`session/`](session/README.zh.md) | 持久会话数据平面:持久化 seam + 后端、投影 seam、基于日志的标题、会话上报 | +| [`session-query/`](session-query/README.zh.md) | 会话检索系列:逻辑语料库、有界读取、血缘、语义过滤、SQLite 全文搜索 | +| [`settings/`](settings/README.zh.md) | 用户设置 seam + 基于文件的提供方 | +| [`credentials/`](credentials/README.zh.md) | 凭据引用/记录 seam + 环境变量优先于 `.env` 的提供方 + 询问人类的授权 flow | +| [`storage/`](storage/README.zh.md) | 非会话存储中枢 + 后端 + 领域形式 | +| [`workspace/`](workspace/README.zh.md) | Workspace 实体 | +| [`sdk/`](sdk/README.zh.md) | 进程外 SDK:JSON-RPC 协议与 TypeScript 客户端/服务器 | +| [`acp/`](acp/README.zh.md) | 仅面向自动化的 Agent Client Protocol 服务器 | +| [`interaction/`](interaction/README.zh.md) | 人机协作平面:批准/交互 seam、权限预设、命令、询问用户的工具 | +| [`boot/`](boot/README.zh.md) | 共享的 app bin 启动粘合层 | +| [`host/`](host/README.zh.md) | web GUI 宿主半侧:API 网关 + HTTP 路由服务器 | +| [`client/`](client/README.zh.md) | web GUI 浏览器半侧:shell、协议层、对象服务、slot、`ui-*` 插件 | +| [`examples/`](examples/README.zh.md) | 供测试与自定义部署复用的组合包 | +| [`test-support/`](test-support/README.zh.md) | 支持基础设施(testkit、不变式、回放、Loader 冒烟测试) | +| [`runtime-diagnostics/`](runtime-diagnostics/README.zh.md) | 运行时诊断:按包归属的运行时不变式检查与报告 | +| [`util/`](util/README.zh.md) | 组间共享的低层零依赖工具(`Branded`、home/路径辅助函数、超时、留存) | + +----- + + +## 发布预期 + +大多数组是产品——稳定 API。例外:`e2b/` 是 POC,`experimental/` 不发布,`examples/`、`test-support/`、`runtime-diagnostics/` 与 `util/` 是兼容性预期较低的支持组。 + +----- + + ## 依赖 依赖图由工具生成:[docs/module-graph.md](../docs/module-graph.zh.md)(`pnpm run gen-module-graph`,CI 中有新鲜度门禁)。 -**扩展插件依赖 Service Definition,绝不依赖具体提供方。** `dsh-agent-loop` 可替换;UI、钩子和工具插件使用 `dsh-agent`。包括 `dsh-agent-spine-demo` 在内的组合包可以依赖主干插件。能力会将需要独立演进的 Service Definition/Service Provider/Consumer 角色分离;详见[能力 seam](../.agents/notes/implemented/architecture/2026-06-13-capability-seams.zh.md)。 +**扩展插件依赖 Service Definition,绝不依赖具体提供方。** `dsh-agent-loop` 可替换;UI、钩子和工具插件使用 `dsh-agent`。包括 `dsh-agent-spine-demo` 在内的组合包可以依赖主干插件。能力在需要独立演进时分离 Service Definition / Service Provider / Consumer 角色;详见[能力 seam](../.agents/notes/implemented/architecture/2026-06-13-capability-seams.zh.md)。 -包 README 覆盖用途、API、扩展点和[模型体验](../docs/cookbook/adding-a-package.zh.md#4-write-the-package-readme);列入模型无关[省略允许清单](../scripts/verify-package-readme-model-experience.ts)的包除外。它们还要包含 `## Known Limitations and Deferred Work`,或列入其[允许清单](../scripts/verify-package-readme-limitations.ts)。 +----- + + +## 包 README 约定 + +每个包 README 都覆盖用途、配置、扩展点与[模型体验](../docs/cookbook/adding-a-package.zh.md#4-write-the-package-readme),列入模型无关[省略允许清单](../scripts/verify-package-readme-model-experience.ts)的包除外。它还要包含 `## Known Limitations and Deferred Work`,或列入其[允许清单](../scripts/verify-package-readme-limitations.ts)。包约定——导出、服务访问、不变式、测试——见 [packages/AGENTS.md](AGENTS.md)。 + +----- + + +## 开发备注 + +
+维护者的工作上下文——点击展开 + +无。 + +
diff --git a/packages/acp/README.i18n.yaml b/packages/acp/README.i18n.yaml index 987ea5b637..a5628d5807 100644 --- a/packages/acp/README.i18n.yaml +++ b/packages/acp/README.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 packages/acp/README.md -README.md: 97af6d164b265bf0e98e3c9f5a444cffad4face5 -README.zh.md: 01999462f73ae269c784cf63af51d32b80c03d3b +README.md: 20640ec4bdc5e9e9d2ac51e1f54fb2f587652e1c +README.zh.md: 09fd7a3f7d40ff91d3e6516bf1c4c2075affc76f diff --git a/packages/acp/README.md b/packages/acp/README.md index 97af6d164b..20640ec4bd 100644 --- a/packages/acp/README.md +++ b/packages/acp/README.md @@ -1,11 +1,41 @@ +--- +description: "The Agent Client Protocol package group: the automation-only server that exposes fresh harness agents to programmatic clients over JSON-RPC stdio." +kind: "package-group" +--- + # acp/ — Agent Client Protocol automation English | [中文](README.zh.md) -The ACP group exposes harness agents to programmatic clients over the Agent Client Protocol. It is an interoperability transport, not a presentation or human-interaction layer; the matching out-of-process subagent *client* lives in [`subagent/subagent-acp`](../subagent/subagent-acp/README.md) because it implements the subagent provider interface. +## Summary + +The acp group provides one package: a server that lets programs and automation run persistent DeepSeek Harness agents over the standard Agent Client Protocol. A client can create, list, resume, and close sessions; attach standard MCP servers; select model options; send text and image prompts; receive semantic updates; answer permission prompts; and cancel work without a human in the loop. The matching client for spawning such a server from another harness lives in `subagent/subagent-acp`. This page maps the group; the package README owns the per-package contract. + +## Table of Contents + +- [Packages](#packages) +- [Related documentation](#related-documentation) +- [Dev Note](#dev-note) + +----- + + +## Packages | Package | Role | |---|---| -| [`acp/`](acp/README.md) | Automation-only ACP server. | +| [`acp/`](acp/README.md) | Lets programs manage persistent agents over ACP, attach MCP servers, select model options, prompt and cancel work, and receive semantic updates | -The server contract is documented in [`acp/README.md`](acp/README.md). +----- + + +## Related documentation + +- [dsh-subagent-acp](../subagent/subagent-acp/README.md) — the out-of-process ACP client that spawns and drives this server. +- [ACP as an automation-only protocol](../../.agents/notes/implemented/simplification/2026-07-23-acp-automation-only-protocol.md) — the design record for the automation contract and its wire boundaries. +- [Multiplex concurrent ACP sessions over one connection](../../.agents/notes/implemented/feature/2026-06-14-acp-multi-session.md) — per-session isolation, ownership, and teardown decisions. + + +## Dev Note + +None. diff --git a/packages/acp/README.zh.md b/packages/acp/README.zh.md index 01999462f7..09fd7a3f7d 100644 --- a/packages/acp/README.zh.md +++ b/packages/acp/README.zh.md @@ -1,11 +1,41 @@ -# acp/:Agent Client Protocol 自动化 +--- +description: "ACP(Agent Client Protocol)包组:通过 JSON-RPC stdio 将全新 harness agent 暴露给程序化客户端的仅自动化服务器。" +kind: "package-group" +--- + +# acp/ — Agent Client Protocol 自动化 [English](README.md) | 中文 -ACP(Agent Client Protocol)组通过该协议将 harness 中的 agent(智能体)公开给程序化客户端。它是互操作传输层,不是展示或人机交互层;配对的进程外 subagent *客户端*在 [`subagent/subagent-acp`](../subagent/subagent-acp/README.zh.md),因为它实现的是 subagent 提供方接口。 +## 概述 + +acp 组提供一个包:一台服务器,让程序与自动化可以通过标准 Agent Client Protocol 运行持久 DeepSeek Harness agent。客户端可以创建、列出、恢复与关闭会话,挂载标准 MCP 服务器,选择模型选项,发送文本与图片提示词,接收语义更新,响应权限提示并取消工作——无需人类参与。从另一个 harness 启动这种服务器的配套客户端位于 `subagent/subagent-acp`。本页是组的映射;包 README 负责各自的包级约定。 + +## 目录 + +- [包](#packages) +- [相关文档](#related-documentation) +- [开发备注](#dev-note) + +----- + + +## 包 | 包 | 职责 | |---|---| -| [`acp/`](acp/README.zh.md) | 仅面向自动化的 ACP 服务器。 | +| [`acp/`](acp/README.zh.md) | 让程序通过 ACP 管理持久 agent、挂载 MCP 服务器、选择模型选项、发送或取消工作并接收语义更新 | -服务器约定见 [`acp/README.md`](acp/README.zh.md)。 +----- + + +## 相关文档 + +- [dsh-subagent-acp](../subagent/subagent-acp/README.zh.md)——spawn 并驱动本服务器的进程外 ACP 客户端。 +- [ACP 作为仅面向自动化的协议](../../.agents/notes/implemented/simplification/2026-07-23-acp-automation-only-protocol.zh.md)——自动化约定及其协议边界的决策记录。 +- [在单个连接上多路复用并发 ACP 会话](../../.agents/notes/implemented/feature/2026-06-14-acp-multi-session.zh.md)——按会话隔离、归属与清理决策。 + + +## 开发备注 + +无。 diff --git a/packages/acp/acp/README.i18n.yaml b/packages/acp/acp/README.i18n.yaml index 1eb4921876..304b9e8d6d 100644 --- a/packages/acp/acp/README.i18n.yaml +++ b/packages/acp/acp/README.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 packages/acp/acp/README.md -README.md: 0e8689632d7115bd65f4849e2ba5d402a1cb62d4 -README.zh.md: 0dcde8c2467481454181d146a1eaae2a3ebac1dc +README.md: 5ba5b740dd35a99b57c82c4ee18753781742b7a7 +README.zh.md: 80391c879109fdc44e9b396ee4ca0016316dbad7 diff --git a/packages/acp/acp/README.md b/packages/acp/acp/README.md index 0e8689632d..5ba5b740dd 100644 --- a/packages/acp/acp/README.md +++ b/packages/acp/acp/README.md @@ -1,95 +1,141 @@ +--- +description: "Automation-only Agent Client Protocol server for programmatic clients and maintainers driving DeepSeek Harness agents over JSON-RPC stdio." +kind: "package-reference" +--- + # @deepseek-ai/dsh-acp English | [中文](README.zh.md) -Automation-only [Agent Client Protocol](https://agentclientprotocol.com) v1 server over JSON-RPC stdio. Trusted programmatic clients can discover standard configuration, create or resume persistent harness Agents, attach MCP servers, prompt and cancel work, receive semantic execution updates, and close one session without affecting others. +## Summary -This package is not a UI integration. It emits standard ACP semantic data, never DSH presentation cards, terminal views, diffs, locations, plans, titles, todos, custom methods, custom capability flags, or DSH-specific `_meta`. Client `_meta` is accepted as protocol metadata and has no private DSH meaning. +`dsh-acp` lets trusted programs drive persistent DeepSeek Harness agents over the standard [Agent Client Protocol](https://agentclientprotocol.com): create or resume sessions, list resumable sessions, attach standard MCP servers, select a model and reasoning effort, prompt or cancel work, receive semantic execution updates, and close one session without affecting others. It is built for automation — out-of-process subagents, test runners, and scripted controllers — rather than the DSH user interface: it emits standard ACP messages, thoughts, generic tool lifecycle, configuration, and context usage, never private DSH presentation data or methods. Session persistence enables list, resume, and close across process restarts, while deletion, fork, transcript replay, additional directories, and interactive UI surfaces remain unsupported. The repository's own ACP client is `dsh-subagent-acp`, and `pnpm dsh --profile acp` starts a ready-to-use server. Setup and usage come first; the implementation details live in a collapsible developer section below. -## Plugin +## Table of Contents -`apply(ctx, config)` opens an ACP SDK agent app on stdin/stdout and drives `ctx.agents`. Stdout is reserved for protocol frames. Complete lifecycle support requires `ctx.sessionPersistence`. +- [Use this package](#use-this-package) +- [Understand the implementation](#understand-the-implementation) +- [Further Exploration](#further-exploration) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) -| Config | Default | Meaning | +----- + + +## Use this package + +Use this package when a script, test runner, or another harness needs to run agent work end to end through a standard automation protocol. The common path is: start the server, create or resume a session, optionally mount MCP servers and select model options, send a prompt, consume semantic updates, and close the session. + +### When to choose it + +Choose it when automation should own the interaction: an out-of-process subagent, test runner, or scripted controller that manages persistent sessions, tools, model selection, and permissions. Avoid it when a human needs DSH-specific presentation cards, plans, titles, todos, terminal views, or elicitation; this server intentionally exposes only the standard ACP v1 surface. + +### Minimal configuration + +Every session the server creates uses the provider and model configured here. Both fields are optional so another agent or request listener can supply them; the runnable demo composition sets both. Stdout carries only protocol traffic, so keep logging off it. + +```yaml +- name: '@deepseek-ai/dsh-acp' + config: + provider: deepseek-official + model: deepseek-v4-pro +``` + +| Field | Default | Meaning | |---|---|---| -| `provider` | — | Initial provider route for each created or resumed Agent. | -| `model` | — | Initial exact model for each created or resumed Agent. | -| `sessionListPageSize` | `100` | Positive maximum number of summaries in one `session/list` page. | +| `provider` | — | Provider route for every session's agent | +| `model` | — | Model for every session's agent | +| `sessionListPageSize` | `100` | Maximum summaries returned in one `session/list` page | -`provider` and `model` may be omitted when another Agent request listener supplies the initial route. The runnable ACP composition requires both. +The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-acp) is the exhaustive source for every accepted field and its JSDoc. -## Standard ACP v1 surface +### Start a server -| Method or notification | Behavior | +`pnpm dsh --profile acp` starts the shipped stdio server. The `acp` profile mounts session persistence, so clients can list, resume, and close persistent sessions. [`@deepseek-ai/dsh-subagent-acp`](../../subagent/subagent-acp/README.md) starts the same profile for out-of-process delegation. + + +### Protocol contract + +One connection can run several sessions at once, each independent. The calls a client makes: + +| Call | What you get | |---|---| -| `initialize` | Negotiates stable ACP v1. Advertises standard `session/list`, `session/resume`, `session/close`, and Streamable HTTP MCP support. Image prompts are advertised only when a durable attachment store and the configured exact route support them. | -| `authenticate` | No-op because the server advertises no authentication methods. | -| `session/new` | Creates one Agent with an absolute primary `cwd`, validates and mounts standard stdio or HTTP MCP servers before publishing the Agent, explicitly materializes its durable header, and returns the complete configuration-option state. | -| `session/list` | Returns deterministic newest-first pages of persisted, resumable top-level sessions. Summaries contain only `sessionId` and absolute `cwd`; cursors are opaque keyset tokens. An optional absolute `cwd` filter uses physical-directory identity when paths exist. Active sessions and subagent/fork descendants are omitted. | -| `session/resume` | Rejects an active id, verifies the persisted canonical workspace before Agent composition, restores the log without replaying it to the client, mounts the request's MCP servers, and returns the complete configuration-option state. | -| `session/close` | Cancels active work, drains ordered updates and continuable descendants, flushes persistence, and disposes only that Agent scope. Persisted state remains available to `session/list` and `session/resume`. | -| `session/set_config_option` | Sets an advertised `model` or `reasoning_effort` value and returns the complete resulting state. Invalid ids and values reject as invalid params. | -| `session/prompt` | Admits ordered text, resource links, and supported images; permits one in-flight prompt per session; and settles only after Agent idle plus ordered update delivery. | -| `session/cancel` | Cancels the addressed prompt admission or turn through its prompt-owned cancellation path. With no ACP prompt in flight it cancels autonomous work; unknown ids are no-ops. | -| `$/cancel_request` | Cancellation of a `session/prompt` JSON-RPC request uses the same prompt-owned path as `session/cancel`. | -| `session/update` | Emits committed message, thought, generic tool lifecycle, configuration, and context-usage updates described below. | -| `session/request_permission` | Requests one standard one-shot allow or reject decision after the referenced `tool_call` notification has been delivered. | +| `initialize` | Stable ACP v1 plus `session/list`, `session/resume`, `session/close`, and Streamable HTTP MCP support; image prompts only when the durable attachment store and configured exact route support them. | +| `authenticate` | Immediate success; the server requires no authentication. | +| `session/new` | A fresh persistent agent whose absolute workspace and stdio or HTTP MCP servers are validated before publication, plus its complete configuration-option state. | +| `session/list` | Deterministic newest-first pages of persisted, resumable root sessions; an optional absolute `cwd` filter uses physical-directory identity where possible. | +| `session/resume` | A persisted inactive session whose canonical workspace is verified before composition; its log is restored without replaying old updates. | +| `session/close` | Quiescent cancellation, update draining, descendant disposal, persistence flush, and disposal of only the addressed Agent scope. | +| `session/set_config_option` | A serialized update to the advertised `model` or `reasoning_effort`, returning the complete resulting state. | +| `session/prompt` | Ordered text, resource links, and supported images, one prompt at a time per session; settlement follows Agent idle and ordered update delivery. | +| `session/cancel` / `$/cancel_request` | The prompt-owned cancellation path; without an ACP prompt in flight it cancels autonomous work, while unknown session ids are no-ops. | +| `session/update` | Committed assistant messages and thoughts, generic tool lifecycle, configuration changes, and context usage, serialized per session. | +| `session/request_permission` | A permission prompt with one-shot allow/reject choices; your client can answer automatically. | -Unsupported surfaces are omitted from capabilities or reject when addressed: `session/load`, `session/delete`, `session/fork`, additional directories, SSE and ACP-transport MCP, modes, commands, plans, terminals, client filesystem operations, and elicitation. +Session configuration offers opaque provider/model choices from the live LLM service catalog and a `reasoning_effort` selector when the exact model declares one. A prompt snapshots that selection before asynchronous image admission and pins it across every model step in that turn; a concurrent option change applies to the next turn. ACP clients are trusted controllers: stdio MCP entries authorize their absolute commands and environment, HTTP entries authorize their absolute HTTP(S) URLs and headers, and any initial connection or discovery failure rolls back the unpublished Agent. Unsupported surfaces are omitted or reject: `session/load`, deletion, fork, additional directories, SSE or ACP-transport MCP, modes, commands, plans, terminals, client filesystem operations, and elicitation. -## Session configuration +----- -Every new or resumed session returns standard select options: + +## Understand the implementation -- `model` groups choices by provider from the advisory LLM catalog. Values are opaque strings carrying the exact provider/model pair; clients must return them unchanged. -- `reasoning_effort` is derived from the selected exact model and is omitted when that model does not declare reasoning choices. When the adapter exposes choices but preserves the provider's own default, a `Provider default` choice represents omitting an explicit effort. +
+Implementation internals — click to expand -The ACP plugin's `provider` and `model` config establish the initial selection. Adapter topology changes emit `config_option_update` with the complete current state. Mutations are serialized per session. +This section explains how the server realizes the behavior above and points at the code that implements it; the observable behavior is fully covered in [Use this package](#use-this-package). -An accepted prompt snapshots the selected route before asynchronous image admission. Its per-session module associates that snapshot with the identified inbox message until claim, then pins the same provider, model, and reasoning effort across image validation, prompt variables, and every model step in that turn. A concurrent option change applies to the next ACP turn. +### Design philosophy -## MCP trust and isolation +The server is an automation transport with an intentionally standard public protocol. Three commitments shape it: -ACP clients are trusted automation controllers. A stdio declaration authorizes DSH to execute its absolute command in the session `cwd` with the supplied arguments and environment entries. An HTTP declaration authorizes requests to its absolute HTTP(S) URL with the supplied headers. DSH does not reinterpret client metadata or add private cwd, timeout, or transport fields. +- **Standard semantic updates only.** The wire carries committed messages and thoughts, generic tool lifecycle, configuration, and context usage; raw provider deltas, retry attempts, DSH presentation data, and unsupported content stay off the wire. +- **Truthful capability and configuration state.** `initialize` advertises only mounted support, topology changes publish complete configuration options, and a prompt pins the exact route it admitted. +- **Quiescence before settlement.** Prompt and close operations settle only after their owned admission, Agent activity, ordered updates, descendants, persistence, and disposal have reached the required terminal state. -Server names are validated and converted to stable DSH MCP namespaces; duplicate normalized names reject before Agent publication. Environment names/values and HTTP headers are validated, including case-insensitive duplicate headers. Standard stdio and Streamable HTTP clients use `dsh-mcp-client`'s existing tool-call timeout and reconnect defaults. Initial connection and tool discovery must succeed, so any failure rolls back the unpublished Agent. +The decision history lives in the [ACP as an automation-only protocol note](../../../.agents/notes/implemented/simplification/2026-07-23-acp-automation-only-protocol.md) and the [multi-session note](../../../.agents/notes/implemented/feature/2026-06-14-acp-multi-session.md). -Each Agent scope owns its MCP registrations and connections. The same server namespace may therefore exist in independent ACP sessions, while a duplicate inside one session still fails. Session close, connection loss, and plugin disposal release the scoped tools and transports. +### Source map -## Semantic updates - -Per-session delivery is serialized and drained before prompt completion: - -| Durable DSH fact | Standard ACP update | +| File | Role | |---|---| -| Committed assistant text or image | `agent_message_chunk` with the durable message id | -| Committed reasoning | `agent_thought_chunk` with the durable message id | -| Durable tool call | `tool_call` with the DSH call id, canonical DSH tool name as `title`, generic `other` kind, and parsed input when valid JSON | -| Durable tool result | `tool_call_update` with the same call id, completed/failed status, and standard content blocks | -| Known context capacity plus measured context pressure | `usage_update` | -| LLM adapter topology change | `config_option_update` with all options | +| [`src/index.ts`](src/index.ts) | Plugin entry: `Config` schema, `AgentSideConnection` wiring, per-session records, admission and settlement, teardown | +| [`src/content.ts`](src/content.ts) | Wire-content admission and projection: image validation, route recheck, prompt reconstruction, assistant block conversion | +| [`src/codec.ts`](src/codec.ts) | Pure turn-ending to ACP `stopReason` mapping | +| [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant; this transport owns no durable package-local event stream) | -Raw model deltas, retry attempts, presentation data, and unsupported core content never enter the ACP wire. Committed images are re-read and integrity-verified before inline base64 delivery. A missing or corrupt committed image fails the correlated prompt instead of producing a placeholder. +### Admission and prompt settlement -## Lifecycle and outcomes +Each session permits one in-flight prompt. Admission validates the whole prompt batch, snapshots the selected route, rechecks the exact Agent identity and image capability, persists image attachments, and only then queues the user message — a cancellation that wins admission never enqueues a late turn. Once queued, the session module associates the snapshot with the inbox message until claim and pins the same provider, model, and reasoning effort across prompt variables and every model step in that turn. Per-session update delivery is serialized; committed images are re-read and integrity-verified, so a missing or corrupt image fails the correlated prompt instead of emitting a placeholder. Settlement precedence is explicit cancellation, committed-output failure, interval-wide Agent failure, then the correlated turn ending. -One connection may own several independent sessions. Exact Agent identity guards event and permission routing. Each per-session module owns its Agent handle, MCP mounts, future and turn-pinned model selections, prompt slot, update chain, and memoized close operation. +### Teardown and connection ownership -Explicit close, connection loss, and plugin disposal use the same quiescent teardown. Teardown stops new work, cancels prompt admission and Agent activity, drains committed updates, disposes continuable descendants child-first, flushes the session, and releases every Agent scope. Failures are reported only after all owned teardown work settles; other frontends sharing the Context are untouched. +Each session module owns its Agent handle, MCP mounts, future and turn-pinned model selections, prompt slot, update chain, and memoized close operation. Explicit close, client disconnect, and Cordis disposal use the same quiescent teardown: stop new work, cancel prompt admission and Agent activity, drain committed updates, dispose continuable descendants child-first, flush persistence, and release the owned Agent scope. A session close leaves persisted state available for list and resume, and other sessions or frontends sharing the Context remain untouched. -Prompt settlement precedence is explicit cancellation, committed-output failure, interval-wide Agent failure, then the correlated turn ending. Standard outcomes include `end_turn`, `max_tokens`, and `cancelled`; correlated model failures become standard JSON-RPC errors. No additional DSH result object is returned. +
-## Running +----- -`pnpm --dir /path/to/deepseek-harness dsh --profile acp` boots the repository's automation server profile. The generic keyless conformance test launches this profile through `dsh` and drives it using only the ACP SDK, including model selection, MCP attachment, close, process restart, list/resume, and cancellation. + +## Further Exploration +Read these pages when the package-level contract is not enough. They move from the matching client to the design records behind the automation contract. + +- [dsh-subagent-acp](../../subagent/subagent-acp/README.md) — the out-of-process ACP client that spawns and drives this server. +- [ACP as an automation-only protocol](../../../.agents/notes/implemented/simplification/2026-07-23-acp-automation-only-protocol.md) — the design record for the automation contract and its wire boundaries. +- [Multiplex concurrent ACP sessions over one connection](../../../.agents/notes/implemented/feature/2026-06-14-acp-multi-session.md) — per-session isolation, ownership, and teardown decisions. +- [Extension cookbook](../../../docs/cookbook/extension-cookbook.md) — this package as the automation-only worked example for extension authors. + +----- + + ## Model Experience ### Prompt content #### What the model sees -`session/prompt` produces an ordinary logged user message. Text/image order is preserved; adjacent text is concatenated; a resource link becomes a bracketed `[resource_link name=… uri=…]` reference. Inline image base64 is discarded after durable admission. Protocol metadata, client capabilities, permission choices, session ids, and ACP configuration objects do not enter model requests. +`session/prompt` preserves text and image order in one user message: adjacent text concatenates, and a resource link appears as a bracketed `[resource_link name=… uri=…]` reference the model may open with its own tools. Inline image base64 is discarded after batch admission, so the durable message contains only verified attachment references. Protocol metadata, client capabilities, permission choices, and session ids never enter the model request. #### Token effect @@ -99,9 +145,38 @@ Prompt content, tool calls/results, and durable image references remain in that Append-only while the selected route and assembled prefix stay unchanged. A model change starts the next ACP turn on the new route. +### Permission decisions + +#### What the model sees + +Nothing directly. The owning tool records its allowed, rejected, cancelled, or unavailable outcome through the normal tool-result path. + +#### Token effect + +Only the owning tool result contributes tokens. + +#### KV Cache effect + +Append-only through the owning tool result. + ## Known Limitations and Deferred Work -- Only one primary workspace is supported. Additional directories remain unsupported. -- Only PNG, JPEG, WebP, and GIF prompt images are supported, subject to the attachment store and exact model route. -- MCP resources and prompts have no DSH consumer; ACP mounts expose MCP tools only. -- Session deletion, fork, transcript replay through `session/load`, modes, commands, plans, terminals, client filesystem operations, and elicitation remain outside this automation surface. + + + +These limits define when this package is a poor fit or needs special operational care. They are current package constraints, not a protocol comparison or a task backlog. + +- **One primary workspace** — additional directories remain unsupported. +- **Raster prompt images only** — PNG, JPEG, WebP, and GIF require a durable attachment store and an exact image-capable route. +- **MCP tools only** — MCP resources and prompts have no DSH consumer. +- **No transcript replay or interactive extensions** — session deletion, fork, `session/load`, modes, commands, plans, terminals, client filesystem operations, and elicitation remain outside this automation surface. + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/acp/acp/README.zh.md b/packages/acp/acp/README.zh.md index 0dcde8c246..80391c8791 100644 --- a/packages/acp/acp/README.zh.md +++ b/packages/acp/acp/README.zh.md @@ -1,97 +1,141 @@ +--- +description: "面向程序化客户端与维护者的仅自动化 Agent Client Protocol 服务器,用于通过 JSON-RPC stdio 驱动 DeepSeek Harness agent。" +kind: "package-reference" +--- + # @deepseek-ai/dsh-acp [English](README.md) | 中文 -通过 JSON-RPC stdio 提供的仅面向自动化的 [Agent Client Protocol](https://agentclientprotocol.com) v1 服务器。受信任的程序化客户端可以发现标准配置、创建或恢复持久化的 harness Agent、挂载 MCP 服务器、提示和取消工作、接收语义执行更新,并在不影响其他会话的情况下关闭单个会话。 +## 概述 -此包不是 UI 集成。它只发出标准 ACP 语义数据,绝不发出 DSH 展示卡片、终端视图、diff、位置、计划、标题、todo、自定义方法、自定义能力标记或 DSH 专用 `_meta`。客户端 `_meta` 仅作为协议元数据接收,不具有 DSH 私有含义。 +`dsh-acp` 让受信程序可以通过标准 [Agent Client Protocol(ACP)](https://agentclientprotocol.com) 驱动持久 DeepSeek Harness agent:创建或恢复会话、列出可恢复会话、挂载标准 MCP 服务器、选择模型与推理强度、发送或取消工作、接收语义执行更新,并关闭一个会话而不影响其他会话。它是为自动化而生的——进程外 subagent、测试运行器与脚本化控制器——而不是 DSH 用户界面:它发送标准 ACP 消息、thought、通用工具生命周期、配置与上下文用量,绝不发送 DSH 私有呈现数据或方法。会话持久化支持跨进程重启的列出、恢复与关闭,而删除、fork、转录回放、附加目录与交互式 UI 界面仍不支持。仓库自带的 ACP 客户端是 `dsh-subagent-acp`,`pnpm dsh --profile acp` 会启动一个开箱即用的服务器。设置与用法在前;实现细节放在下方可折叠的开发者章节中。 -## 插件 +## 目录 -`apply(ctx, config)` 在 stdin/stdout 上打开 ACP SDK agent app,并驱动 `ctx.agents`。Stdout 专用于协议帧。完整生命周期支持要求挂载 `ctx.sessionPersistence`。 +- [使用本包](#use-this-package) +- [理解实现](#understand-the-implementation) +- [进一步探索](#further-exploration) +- [模型体验](#model-experience) +- [已知限制与延期工作](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) -| 配置 | 默认值 | 含义 | +----- + + +## 使用本包 + +当脚本、测试运行器或另一个 harness 需要通过标准自动化协议端到端运行 agent 工作时,使用本包。常用路径是:启动服务器、创建或恢复会话、按需挂载 MCP 服务器并选择模型选项、发送提示词、消费语义更新,再关闭会话。 + +### 何时选择 + +当自动化应拥有交互时选择它:管理持久会话、工具、模型选择与权限的进程外 subagent、测试运行器或脚本化控制器。当人类需要 DSH 专用呈现卡片、计划、标题、todo、终端视图或 elicitation 时请避开;本服务器刻意只提供标准 ACP v1 界面。 + +### 最小配置 + +服务器创建的每个会话都使用此处配置的提供方与模型。两个字段都是可选的,以便由另一个 agent/request 监听器提供;可运行的演示组合会同时设置两者。Stdout 只承载协议流量,因此请让日志远离它。 + +```yaml +- name: '@deepseek-ai/dsh-acp' + config: + provider: deepseek-official + model: deepseek-v4-pro +``` + +| 字段 | 默认值 | 含义 | |---|---|---| -| `provider` | 无 | 每个新建或恢复 Agent 的初始提供方路由。 | -| `model` | 无 | 每个新建或恢复 Agent 的初始确切模型。 | -| `sessionListPageSize` | `100` | 单个 `session/list` 页面返回的摘要数量上限,必须为正数。 | +| `provider` | — | 每个会话 agent 的提供方路由 | +| `model` | — | 每个会话 agent 的模型 | +| `sessionListPageSize` | `100` | 单页 `session/list` 返回的最大摘要数量 | -当另一个 Agent 请求监听器提供初始路由时,可以省略 `provider` 和 `model`。可运行 ACP 组合同时要求两者。 +生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-acp)是每个受支持字段及其 JSDoc 的穷尽式真源。 - +### 启动服务器 -## 标准 ACP v1 接口 +`pnpm dsh --profile acp` 会启动随附的 stdio 服务器。`acp` profile 会挂载会话持久化,因此客户端可以列出、恢复和关闭持久会话。[`@deepseek-ai/dsh-subagent-acp`](../../subagent/subagent-acp/README.zh.md) 会启动同一 profile 来执行进程外委派。 -| 方法或通知 | 行为 | + +### 协议约定 + +一个连接可以同时运行多个会话,彼此独立。客户端发出的调用如下: + +| 调用 | 你会得到什么 | |---|---| -| `initialize` | 协商稳定 ACP v1。公布标准 `session/list`、`session/resume`、`session/close` 和 Streamable HTTP MCP 支持。只有持久附件存储和配置的确切路由都支持图片时,才公布图片提示词能力。 | -| `authenticate` | 空操作,因为服务器不公布身份验证方法。 | -| `session/new` | 使用绝对主 `cwd` 创建一个 Agent;在公布 Agent 前校验并挂载标准 stdio 或 HTTP MCP 服务器;显式实体化其持久 header;返回完整配置选项状态。 | -| `session/list` | 按创建时间从新到旧,确定性分页返回已持久化且可恢复的顶层会话。摘要只包含 `sessionId` 和绝对 `cwd`;cursor 是不透明的 keyset token。可选绝对 `cwd` 过滤器会在路径存在时比较物理目录身份。活动会话以及 subagent/fork 后代不会出现。 | -| `session/resume` | 拒绝活动 id;在组合 Agent 前校验持久化会话的规范工作区;恢复日志但不向客户端重放;挂载该请求的 MCP 服务器;返回完整配置选项状态。 | -| `session/close` | 取消活动工作、drain 有序更新和可继续后代、flush 持久化,并只释放该 Agent scope。持久化状态仍可供 `session/list` 和 `session/resume` 使用。 | -| `session/set_config_option` | 设置已公布的 `model` 或 `reasoning_effort` 值,并返回完整结果状态。无效 id 或值以 invalid params 拒绝。 | -| `session/prompt` | 准入有序文本、资源链接和受支持图片;每个会话只允许一个进行中的提示词;只在 Agent 空闲且有序更新交付完成后结算。 | -| `session/cancel` | 通过提示词自有取消路径取消指定的准入或轮次。没有 ACP 提示词进行时取消自主工作;未知 id 为空操作。 | -| `$/cancel_request` | 取消 `session/prompt` JSON-RPC 请求时,使用与 `session/cancel` 相同的提示词自有路径。 | -| `session/update` | 发出下文所述的已提交消息、思考、通用工具生命周期、配置和上下文用量更新。 | -| `session/request_permission` | 在引用的 `tool_call` 通知交付后,请求一次标准的一次性允许或拒绝决定。 | +| `initialize` | 稳定 ACP v1,以及 `session/list`、`session/resume`、`session/close` 与 Streamable HTTP MCP 支持;图片提示词只在持久附件存储和配置的确切路由支持时公布。 | +| `authenticate` | 立即成功;服务器不需要身份验证。 | +| `session/new` | 全新持久 agent;其绝对工作区与 stdio 或 HTTP MCP 服务器会在发布前通过校验,并返回完整配置选项状态。 | +| `session/list` | 按确定的新到旧顺序分页返回已持久、可恢复的根会话;可选绝对 `cwd` 筛选会尽可能使用物理目录标识。 | +| `session/resume` | 恢复一个已持久且非活跃的会话;组合前校验其规范工作区,并恢复日志但不回放旧更新。 | +| `session/close` | 停稳式取消、更新 drain、后代释放、持久化 flush,并且只释放指定 Agent 作用域。 | +| `session/set_config_option` | 串行更新公布的 `model` 或 `reasoning_effort`,并返回完整结果状态。 | +| `session/prompt` | 有序文本、资源链接与受支持图片,每个会话一次一个提示词;Agent 空闲且有序更新交付后才结算。 | +| `session/cancel` / `$/cancel_request` | 提示词所拥有的取消路径;没有进行中的 ACP 提示词时取消自主工作,未知会话 id 则为空操作。 | +| `session/update` | 已提交 assistant 消息与 thought、通用工具生命周期、配置变化与上下文用量,按会话串行交付。 | +| `session/request_permission` | 带一次性允许/拒绝选项的权限提示;你的客户端可以自动回答。 | -未支持的接口不会出现在能力中,或在被调用时拒绝:`session/load`、`session/delete`、`session/fork`、附加目录、SSE 和 ACP 传输 MCP、模式、命令、计划、终端、客户端文件系统操作以及 elicitation。 +会话配置从实时 LLM 服务目录提供不透明的提供方/模型选项,并在确切模型声明推理选项时提供 `reasoning_effort`。提示词会在异步图片准入前快照该选择,并在该轮的每个模型步骤中固定它;并发选项变更从下一轮开始生效。ACP 客户端是受信控制器:stdio MCP 条目授权其绝对命令与环境,HTTP 条目授权其绝对 HTTP(S) URL 与 header;初始连接或发现失败会回滚尚未发布的 Agent。不支持的界面会被省略或拒绝:`session/load`、删除、fork、附加目录、SSE 或 ACP 传输 MCP、mode、命令、计划、终端、客户端文件系统操作与 elicitation。 -## 会话配置 +----- -每个新建或恢复的会话都会返回标准 select 选项: + +## 理解实现 -- `model` 根据建议性 LLM catalog 按提供方分组。值是不透明字符串,携带确切的提供方/模型对;客户端必须原样返回。 -- `reasoning_effort` 来自所选确切模型;该模型未声明推理选项时省略。如果 adapter 公开选项但保留提供方自身默认值,`Provider default` 选项表示不显式指定 effort。 +
+实现细节——点击展开 -ACP 插件的 `provider` 和 `model` 配置建立初始选择。Adapter 拓扑变化会发送包含完整当前状态的 `config_option_update`。每个会话会串行处理配置变更。 +本节解释服务器如何实现上述行为,并指出实现它的代码位置;可观察行为已在[使用本包](#use-this-package)中完整说明。 -已接受的提示词会在异步图片准入前快照所选路由。Per-session 模块会把该快照与已识别 inbox 消息关联到 claim 时刻,再把同一提供方、模型和 reasoning effort 固定到图片校验、提示词变量以及该轮次中的每个模型步骤。并发配置变更从下一个 ACP 轮次开始生效。 +### 设计理念 -## MCP 信任与隔离 +服务器是刻意采用标准公开协议的自动化传输。三项承诺塑造了它: -ACP 客户端是受信任的自动化控制器。stdio 声明授权 DSH 在会话 `cwd` 中执行其绝对命令,并使用所给参数和环境项。HTTP 声明授权向其绝对 HTTP(S) URL 发送带所给 header 的请求。DSH 不重新解释客户端元数据,也不增加私有 cwd、超时或传输字段。 +- **只发送标准语义更新。** 协议承载已提交消息与 thought、通用工具生命周期、配置与上下文用量;原始提供方增量、重试尝试、DSH 呈现数据与不受支持内容不会进入协议。 +- **诚实的能力与配置状态。** `initialize` 只公布已挂载支持,拓扑变化会发布完整配置选项,提示词则固定其准入时的确切路由。 +- **停稳后才结算。** 提示词与关闭操作只在其拥有的准入、Agent 活动、有序更新、后代、持久化与释放达到所需终态后才结算。 -服务器名称会经过校验并转换为稳定的 DSH MCP namespace;重复的规范化名称会在 Agent 公布前拒绝。环境变量名/值和 HTTP header 会被校验,其中 header 重复检查不区分大小写。标准 stdio 与 Streamable HTTP 客户端使用 `dsh-mcp-client` 现有的工具调用超时和重连默认值。初始连接和工具发现必须成功,因此任何失败都会回滚尚未公布的 Agent。 +决策历史记录在 [ACP 作为仅面向自动化的协议笔记](../../../.agents/notes/implemented/simplification/2026-07-23-acp-automation-only-protocol.zh.md) 与[多会话笔记](../../../.agents/notes/implemented/feature/2026-06-14-acp-multi-session.zh.md) 中。 -每个 Agent scope 拥有自己的 MCP 注册和连接。因此,独立 ACP 会话可以使用相同服务器 namespace,而同一会话内的重复仍会失败。会话关闭、连接丢失和插件释放都会移除 scoped 工具和传输。 +### 源码地图 -## 语义更新 - -每个会话会串行交付更新,并在提示词完成前 drain: - -| 持久 DSH 事实 | 标准 ACP 更新 | +| 文件 | 职责 | |---|---| -| 已提交 assistant 文本或图片 | 携带持久消息 id 的 `agent_message_chunk` | -| 已提交 reasoning | 携带持久消息 id 的 `agent_thought_chunk` | -| 持久工具调用 | `tool_call`:使用 DSH call id、规范 DSH 工具名作为 `title`、通用 `other` kind,并在参数为有效 JSON 时提供解析后的输入 | -| 持久工具结果 | `tool_call_update`:使用相同 call id、completed/failed 状态和标准内容块 | -| 已知上下文容量和已测上下文压力 | `usage_update` | -| LLM adapter 拓扑变化 | 包含全部选项的 `config_option_update` | +| [`src/index.ts`](src/index.ts) | 插件入口:`Config` schema、`AgentSideConnection` 接线、按会话记录、准入与结算、清理 | +| [`src/content.ts`](src/content.ts) | 协议内容准入与投影:图片校验、路由重查、提示词重建、assistant 块转换 | +| [`src/codec.ts`](src/codec.ts) | 轮次结束到 ACP `stopReason` 的纯映射 | +| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式;本传输不拥有持久包内事件流) | -原始模型 delta、重试尝试、展示数据和不受支持的核心内容绝不会进入 ACP wire。已提交图片在以内联 base64 交付前会重新读取并校验完整性。已提交图片缺失或损坏会使关联提示词失败,而不会产生占位符。 +### 准入与提示词结算 -## 生命周期与结果 +每个会话只允许一个正在处理的提示词。准入先校验整个提示词批次、快照所选路由、重新检查 Agent 是否为同一对象与图片能力、持久化图片附件,然后才把用户消息入队——赢得准入的取消绝不会入队迟到的轮次。入队后,会话模块把该快照与 inbox 消息关联到认领时刻,并在提示词变量与该轮的每个模型步骤中固定相同的提供方、模型与推理强度。按会话更新会串行交付;已提交图片会重新读取并验证完整性,因此图片缺失或损坏会让关联提示词失败,而不是发出占位符。结算优先级依次为显式取消、已提交输出失败、区间内 Agent 失败、关联轮次结束。 -一个连接可以拥有多个独立会话。事件和权限路由会校验确切 Agent 身份。每个 per-session 模块拥有自己的 Agent handle、MCP 挂载、未来选择和轮次固定的模型选择、提示词槽位、更新链以及记忆化关闭操作。 +### 清理与连接归属 -显式关闭、连接丢失和插件释放使用同一个完全停稳的 teardown。Teardown 会停止新工作、取消提示词准入和 Agent 活动、drain 已提交更新、按 child-first 顺序释放可继续后代、flush 会话,并释放每个 Agent scope。只有在所有自有 teardown 工作结算后才报告失败;共享该 Context 的其他前端不受影响。 +每个会话模块拥有其 Agent 句柄、MCP 挂载、未来与轮次固定的模型选择、提示词槽位、更新链和记忆化关闭操作。显式关闭、客户端断开与 Cordis 释放使用同一停稳式清理流程:停止新工作、取消提示词准入与 Agent 活动、drain 已提交更新、按子优先顺序释放可继续后代、flush 持久化并释放所拥有的 Agent 作用域。会话关闭后,持久状态仍可供列出与恢复;共享上下文的其他会话或前端不受影响。 -提示词结算优先级依次为显式取消、已提交输出失败、区间内 Agent 失败、关联轮次结束。标准结果包括 `end_turn`、`max_tokens` 和 `cancelled`;关联模型失败成为标准 JSON-RPC error。不会返回额外 DSH 结果对象。 +
-## 运行 +----- -`pnpm --dir /path/to/deepseek-harness dsh --profile acp` 启动仓库的自动化服务器 profile。通用 keyless conformance 测试通过 `dsh` 启动此 profile,并只使用 ACP SDK 驱动它,覆盖模型选择、MCP 挂载、关闭、进程重启、列出/恢复和取消。 + +## 进一步探索 +当包级约定不够用时阅读以下页面。它们从匹配的客户端逐步进入自动化约定背后的设计记录。 + +- [dsh-subagent-acp](../../subagent/subagent-acp/README.zh.md)——spawn 并驱动本服务器的进程外 ACP 客户端。 +- [ACP 作为仅面向自动化的协议](../../../.agents/notes/implemented/simplification/2026-07-23-acp-automation-only-protocol.zh.md)——自动化约定及其协议边界的决策记录。 +- [在单个连接上多路复用并发 ACP 会话](../../../.agents/notes/implemented/feature/2026-06-14-acp-multi-session.zh.md)——按会话隔离、归属与清理决策。 +- [扩展实操手册](../../../docs/cookbook/extension-cookbook.zh.md)——本包作为扩展作者的仅自动化完整示例。 + +----- + + ## 模型体验 ### 提示词内容 -#### 模型看到的内容 +#### 模型看到什么 -`session/prompt` 产生普通的已记录用户消息。文本/图片顺序会保留;相邻文本会拼接;资源链接会变成带方括号的 `[resource_link name=… uri=…]` 引用。内联图片 base64 在持久准入后即被丢弃。协议元数据、客户端能力、权限选择、会话 id 和 ACP 配置对象不会进入模型请求。 +`session/prompt` 会在一条用户消息中保留文本与图片顺序:相邻文本会拼接,资源链接则表示为带方括号的 `[resource_link name=… uri=…]` 引用,模型可以使用自身工具打开它。内联图片 base64 在批量准入后即被丢弃,因此持久消息只包含经过校验的附件引用。协议元数据、客户端能力、权限选择与会话 id 绝不进入模型请求。 #### Token 影响 @@ -99,11 +143,40 @@ ACP 客户端是受信任的自动化控制器。stdio 声明授权 DSH 在会 #### KV Cache 影响 -当所选路由和已组装前缀不变时仅追加。模型变更会让下一个 ACP 轮次使用新路由。 +仅追加;新用户消息位于可复用请求前缀之后,不会使先前缓存条目失效。 -## 已知限制与暂缓事项 +### 权限决策 -- 只支持一个主 workspace。附加目录仍不受支持。 -- 提示词图片只支持 PNG、JPEG、WebP 和 GIF,并受附件存储和确切模型路由约束。 -- MCP resource 和 prompt 没有 DSH consumer;ACP 挂载只公开 MCP 工具。 -- 会话删除、fork、通过 `session/load` 重放 transcript、模式、命令、计划、终端、客户端文件系统操作和 elicitation 仍不属于此自动化接口。 +#### 模型看到什么 + +不会直接看到任何内容。所属工具通过常规工具结果路径记录其结果:允许、拒绝、取消或不可用。 + +#### Token 影响 + +只有所属工具的结果会贡献 token。 + +#### KV Cache 影响 + +仅通过所属工具的结果追加。 + +## 已知限制与延期工作 + + + + +这些限制说明本包何时不合适,或何时需要特别的运维注意。它们是当前包约束,不是协议对比或任务积压。 + +- **仅一个主 workspace**——附加目录仍不支持。 +- **仅光栅提示词图片**——PNG、JPEG、WebP 与 GIF 要求持久附件存储及确切的图片能力路由。 +- **仅 MCP 工具**——MCP resource 与 prompt 没有 DSH 消费方。 +- **没有转录回放或交互式扩展**——会话删除、fork、`session/load`、mode、命令、计划、终端、客户端文件系统操作与 elicitation 仍不属于此自动化界面。 + + +### 开发备注 + +
+维护者的工作上下文——点击展开 + +无。 + +
diff --git a/packages/api/README.i18n.yaml b/packages/api/README.i18n.yaml index 8ec7bd4c18..37fe0e96a9 100644 --- a/packages/api/README.i18n.yaml +++ b/packages/api/README.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 packages/api/README.md -README.md: 2db5c518f75a1bba5790146a5f1b91a4fa5bf745 -README.zh.md: a8216db373068dae791610075419b144231d38d5 +README.md: 1d9bcc49684ee557bc62b8ba2bbe79918c5de5ca +README.zh.md: 2aae832f8dc9ece0f15283efded7c9c05b5bff88 diff --git a/packages/api/README.md b/packages/api/README.md index 2db5c518f7..1d9bcc4968 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -1,17 +1,56 @@ +--- +description: "Package map for the application's Remote layer: typed Client-to-Host capability calls, results, and forwarded events, for users and maintainers navigating the group." +kind: "package-group" +--- + # api/ — Remote API layers English | [中文](README.zh.md) -The application-facing Remote stack. `remotes` owns BFF policy and the selected business API, while `gateway` implements the Typert unary RPC endpoints shared by Host and Client environments. +## Summary + +The `api/` group provides the application's Remote layer: a Client environment can call the business capabilities running on the Host — manage goals, run commands, list the plugin inventory, discover file and session references — as typed method calls, and receive the results or forwarded Host events. `remotes` decides which capabilities are exposed and how each call reaches the right session's agent; `gateway` carries the calls and their results between Client and Host. The stack runs over the application's shared Connection; streaming session data is deliberately outside it. + +## Table of Contents + +- [Packages](#packages) +- [Related documentation](#related-documentation) +- [Dev Note](#dev-note) + +----- + + +## Packages + +The packages below provide the Remote layer; the package READMEs own the exhaustive contracts. | Package | Role | ctx key | |---|---|---| -| [`remotes/`](remotes/README.md) | Host Agent/Session lookup policy and Client Remote contribution assembly | no service; configures `ctx.typert` and consumes `ctx.remote` | -| [`gateway/`](gateway/README.md) | Host Typert dispatcher and Client Remote endpoint | `ctx.typertGateway` / `ctx.remote` | +| [`remotes/`](remotes/README.md) | Chooses which Host capabilities and events the Client can consume. | — | +| [`gateway/`](gateway/README.md) | Carries typed unary calls, multiplexed streams, and forwarded Host events. | `ctx.typertGateway` / `ctx.remote` | +| [`session-controller/`](session-controller/README.md) | Owns Session commands, history streams, live control state, and Agent/Session identity policy. | `ctx.sessionController` / `ctx.remote.session` | +| [`workspace-controller/`](workspace-controller/README.md) | Owns Workspace mutations and the complete Client Workspace projection. | `ctx.workspaceController` / `ctx.remote.workspace` | -The runtime dependency direction is `remotes → gateway → connection → webserver`: the BFF consumes the shared `TypertClientRemote` contract, Gateway delegates transport to Connection, and Connection mounts on the HTTP server. Cordis service injection and Client module metadata preserve this order without importing the concrete Gateway from the Remotes Client entry. +Remote calls run Client → Host over the application's shared Connection. API Gateway owns Remote transport, while the two controller packages own Session and Workspace behavior. Endpoints without a Remote definition fall through to the application's API Proxy. -## Known Limitations and Deferred Work +----- -- Connection and WebServer remain at [`client/connection`](../client/connection/README.md) and [`host/webserver`](../host/webserver/README.md); a later package-only move can place them under `api/connection` and `api/webserver` without changing their service contracts. -- The legacy API Proxy remains at [`host/apiproxy`](../host/apiproxy/README.md) as the fallback for methods not yet migrated to Remote. It consumes the Host resolver owned by `api-remotes` so migrated and legacy methods retain one Agent/Session identity policy. + +## Related documentation + +Start with the API Gateway reference to see the Remote model end to end, then the Typert subsystem page for the shared definitions, and the carrier and fallback packages for how calls travel and how endpoints without Remote definitions are served. + +- [API Gateway reference](../../docs/api-gateway.md) — the current-state reference for the Typert API Gateway: programming model, generation pipeline, and runtime invocation. +- [Typert subsystem reference](../../docs/subsystems/typert.md) — the public contracts shared by protocol, Gateway, and consumer assemblies. +- [Connection](../client/connection/README.md) — the RPC carrier, `/api` trust fence, and response envelopes behind every Remote call. +- [API Proxy](../host/apiproxy/README.md) — the fallback for endpoints without Remote descriptors. + + +## Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/api/README.zh.md b/packages/api/README.zh.md index a8216db373..2aae832f8d 100644 --- a/packages/api/README.zh.md +++ b/packages/api/README.zh.md @@ -1,17 +1,56 @@ -# api/:Remote API 层 +--- +description: "应用 Remote 层的包映射:类型化的 Client 到 Host 能力调用、结果与转发事件,供用户与维护者浏览该组。" +kind: "package-group" +--- + +# api/ — Remote API 层 [English](README.md) | 中文 -面向应用的 Remote 技术栈。`remotes` 负责 BFF 策略和选定的业务 API,`gateway` 则实现 Host 与 Client 环境共用的 Typert 一元 RPC endpoint。 +## 概述 + +`api/` 组提供应用的 Remote 层:Client 环境可以调用运行在 Host 上的业务能力——管理目标、运行命令、查看插件清单、发现文件与会话引用——调用方式是类型化方法,并接收结果或转发的 Host 事件。`remotes` 决定暴露哪些能力、以及每次调用如何到达正确会话的 agent;`gateway` 在 Client 与 Host 之间承载调用及其结果。技术栈运行在应用共享的 Connection 之上;流式会话数据刻意不在其中。 + +## 目录 + +- [包](#packages) +- [相关文档](#related-documentation) +- [开发备注](#dev-note) + +----- + + +## 包 + +下面两个包共同提供 Remote 层;穷尽式约定以各包 README 为准。 | 包 | 职责 | ctx key | |---|---|---| -| [`remotes/`](remotes/README.zh.md) | Host Agent/Session lookup 策略与 Client Remote contribution 装配 | 无服务;配置 `ctx.typert` 并消费 `ctx.remote` | -| [`gateway/`](gateway/README.zh.md) | Host Typert 分发器与 Client Remote endpoint | `ctx.typertGateway` / `ctx.remote` | +| [`remotes/`](remotes/README.zh.md) | 决定 Client 可以消费哪些 Host 能力与事件。 | — | +| [`gateway/`](gateway/README.zh.md) | 承载带类型的单次调用、多路复用 stream 与转发的 Host 事件。 | `ctx.typertGateway` / `ctx.remote` | +| [`session-controller/`](session-controller/README.zh.md) | 拥有 Session 命令、历史 stream、实时控制状态与 Agent/Session 身份策略。 | `ctx.sessionController` / `ctx.remote.session` | +| [`workspace-controller/`](workspace-controller/README.zh.md) | 拥有 Workspace 变更与完整 Client Workspace 投影。 | `ctx.workspaceController` / `ctx.remote.workspace` | -运行时依赖方向为 `remotes → gateway → connection → webserver`:BFF 消费共享的 `TypertClientRemote` 约定,Gateway 把传输交给 Connection,Connection 再挂载到 HTTP server。Cordis 服务注入与 Client 模块元数据在不让 Remotes Client 入口导入具体 Gateway 实现的前提下维持该顺序。 +Remote 调用沿 Client → Host 方向运行在应用共享的 Connection 之上。API Gateway 拥有 Remote 传输,两个 controller 包分别拥有 Session 与 Workspace 行为。没有 Remote 定义的 endpoint 会回退到应用的 API Proxy。 -## 已知限制与延期工作 +----- -- Connection 与 WebServer 仍位于 [`client/connection`](../client/connection/README.zh.md) 和 [`host/webserver`](../host/webserver/README.zh.md);后续可以只移动包,将它们放到 `api/connection` 和 `api/webserver` 下,而无需改变服务约定。 -- 旧 API Proxy 仍位于 [`host/apiproxy`](../host/apiproxy/README.zh.md),作为尚未迁移到 Remote 的方法的回退路径。它使用由 `api-remotes` 持有的 Host resolver,使已迁移与旧方法共用同一套 Agent/Session 身份策略。 + +## 相关文档 + +先读 API Gateway 参考以端到端了解 Remote 模型,再读 Typert 子系统页了解共享定义,以及载体与回退包了解调用如何传输、没有 Remote 定义的 endpoint 如何被服务。 + +- [API Gateway 参考](../../docs/api-gateway.zh.md)——Typert API Gateway 的现状参考:编程模型、生成流水线与运行时调用。 +- [Typert 子系统参考](../../docs/subsystems/typert.zh.md)——protocol、Gateway 与消费方装配共享的公共约定。 +- [Connection](../client/connection/README.zh.md)——每次 Remote 调用背后的 RPC 载体、`/api` 信任围栏与响应封装。 +- [API Proxy](../host/apiproxy/README.zh.md)——没有 Remote 描述符的 endpoint 的回退路径。 + + +## 开发备注 + +
+维护者的工作上下文——点击展开 + +无。 + +
diff --git a/packages/api/gateway/README.i18n.yaml b/packages/api/gateway/README.i18n.yaml index b0a004e667..9bc399b963 100644 --- a/packages/api/gateway/README.i18n.yaml +++ b/packages/api/gateway/README.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 packages/api/gateway/README.md -README.md: 1c3860ec836bcbefd26b04dca76b56955847c755 -README.zh.md: ca3d9f75d5f226a21c44ba59b1befcbe5f321bac +README.md: 2e0cb32e4db6c1bee8576a5addd5492fb7ef53ac +README.zh.md: f67e13f6b1f789da02796397a121e59a55427cca diff --git a/packages/api/gateway/README.md b/packages/api/gateway/README.md index 1c3860ec83..2e0cb32e4d 100644 --- a/packages/api/gateway/README.md +++ b/packages/api/gateway/README.md @@ -1,9 +1,27 @@ +--- +description: "Typed Client-to-Host calls and streams: dispatch, validation, cancellation, reconnection, and forwarded Host events." +kind: "package-reference" +--- + # @deepseek-ai/dsh-api-gateway English | [中文](README.zh.md) +## Summary + Two-sided Typert RPC endpoint for Host and Client Cordis environments. The Host entry provides `ctx.typertGateway`, while `@deepseek-ai/dsh-api-gateway/client` provides `ctx.remote`; both consume the same generated `InvocationDescriptor` contract and leave business selection to API Remotes. Connection carries unary request correlation, trust, and response envelopes, while Gateway owns multiplexed Remote streams. +## Table of Contents + +- [Host service: `TypertGatewayService` (ctx key: `typertGateway`)](#host-service-typertgatewayservice-ctx-key-typertgateway) +- [Client service: `ClientRemote` (ctx key: `remote`)](#client-service-clientremote-ctx-key-remote) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) + +----- + + ## Host service: `TypertGatewayService` (ctx key: `typertGateway`) `ctx.typertGateway.invoke()` resolves the current descriptor and Cordis Service for each call, validates exact named arguments, resolves registered object or Context identities, invokes the public business method, and validates its result. Business Services extend `TypertRemoteService` and mark methods with `@Remote` or `@RemoteScope` from [`dsh-typert-protocol`](../../typert/protocol/README.md); `bindTypertRemote()` remains available when another base class owns inheritance. @@ -18,6 +36,7 @@ A stream Remote uses `@Remote({ mode: 'stream' })` and returns an `Iterable` or Host composition can register one application event source through `registerRemoteEvents()`. Gateway reserves the internal `$events` logical endpoint for that source, accepts only empty `args`, and aborts streams opened by the registration when the source is withdrawn. API Remotes owns the event selection, argument validation, and per-Client queues. Its source factory attaches incremental listeners synchronously; Gateway then yields `{ type: 'ready' }` before iterating the source, so the Client starts baseline reads only after incremental delivery is ready. + ## Client service: `ClientRemote` (ctx key: `remote`) `ctx.remote.$mount()` validates and registers a generated Host-for-Client contribution, then installs concrete direct and scoped methods for the calling Cordis fiber. Each namespace is a traced `remote.` child Service and unloads after its last method is withdrawn. Duplicate endpoints, namespace collisions, and descriptors without strict generated codecs fail before methods become callable. @@ -26,10 +45,11 @@ Each unary call validates positional inputs, constructs the descriptor's exact n `ctx.remote.$stream()` returns a single-consumer `RemoteStream` spanning physical carrier generations. It permits one immediate retry while the Host remains available, otherwise waits for the next connected Host generation, and annotates each item with its physical generation. The domain consumer validates and accepts each generation's opening value; business and protocol failures remain terminal. `RemoteSnapshotStream` adds one opening snapshot followed by deltas. `RemoteJournalStream` adds follow-before-page opening, pagination, reconnect catch-up, and gap repair over domain-defined inclusive entry ranges; it removes complete duplicates and rejects gaps, inverted ranges, and partial overlaps. Disposing any stream cancels its requests and resolves after the active iterator is fully stopped. -`ctx.remote.$on()` subscribes to one forwarded Host event. Its legal keys are exactly the Host assembly's forwarding selection, and the listener type is the owning package's own Cordis `Events` declaration, so no second signature can drift from it. Each subscription belongs to the calling fiber and disappears with it. The Client Remote service registers the `$events` pump as a Connection generation source when it activates, whether or not any `$on` listener exists. Browsers use Remote mux, while in-process compositions use `connection.rpc.open`; the `ready` item and `host.describe` jointly establish a Connection generation. Carrier failure, Remote stream failure, unexpected normal completion, a non-ready opening item, or a malformed event item ends that generation and lets Connection reopen it after backoff. Ordinary notifications run in registration order and isolate listener failures. Agent-scoped waterfalls let a listener return a result, call `next()`, or reject; Gateway returns that outcome through the existing HTTP unary carrier. +`ctx.remote.$on()` subscribes to one forwarded Host event. Its legal keys are exactly the Host assembly's forwarding selection, and the listener type is the owning package's own Cordis `Events` declaration, so no second signature can drift from it. Each subscription belongs to the calling fiber and disappears with it. The Client Remote service registers the `$events` pump as a Connection generation source when it activates, whether any `$on` listener exists. Browsers use Remote mux, while in-process compositions use `connection.rpc.open`; the `ready` item and `host.describe` jointly establish a Connection generation. Carrier failure, Remote stream failure, unexpected normal completion, a non-ready opening item, or a malformed event item ends that generation and lets Connection reopen it after backoff. Ordinary notifications run in registration order and isolate listener failures. Agent-scoped waterfalls let a listener return a result, call `next()`, or reject; Gateway returns that outcome through the existing HTTP unary carrier. Generated declaration merges provide the TypeScript API through the shared `TypertClientRemote` contract. The Client entry contains no Host Service or Host Cordis interface merge, and method lookup and invocation use ordinary objects and functions rather than a JavaScript Proxy. + ## Model Experience None, as the package dispatches application calls and registers no prompt, tool, or session event. @@ -40,9 +60,22 @@ No direct effect; invoked business Services own any model-visible result. ## Known Limitations and Deferred Work + + - The Connection adapter maps ordinary dispatch failures and business exceptions to the RPC `internal` code with empty details; lookup-policy errors carried by `TypertLookupFailure` are returned unchanged. Structured `TypertGatewayError` categories remain available only to same-process callers. - SRC mode supports unique identifier parameters without destructuring, defaults, or rest parameters. It validates JSON safety rather than generated business types and never infers optional fields. - Only strict generated contributions can mount on the Client face. SRC markers have no Client codec or type projection. - `$stream()` supervises carrier replacement but does not infer replay semantics; each domain owns its resume cursor or replacement-baseline validation and normal-end classification. Connection generations reopen the internal `$events` stream; one-way notifications are not replayed, while pending scoped waterfalls retain their event id across replay. - Lookup resolvers are configured per key; an individual Remote parameter or endpoint cannot currently select a live-only policy under the same `agent`/`session` key. - Forwarded events reach `$on` without business-payload projection or redaction. Ordinary notifications are not replayed after reconnect; Agent-scoped waterfalls project only the top-level Agent identity needed to select the Client Context and carry their own pending lifetime. + + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/api/gateway/README.zh.md b/packages/api/gateway/README.zh.md index ca3d9f75d5..f67e13f6b1 100644 --- a/packages/api/gateway/README.zh.md +++ b/packages/api/gateway/README.zh.md @@ -1,9 +1,27 @@ +--- +description: "带类型的 Client 到 Host 调用与 stream:分派、校验、取消、重连与转发的 Host 事件。" +kind: "package-reference" +--- + # @deepseek-ai/dsh-api-gateway [English](README.md) | 中文 +## 概述 + 为 Host 与 Client 两侧的 Cordis 环境提供 Typert RPC endpoint。Host 入口提供 `ctx.typertGateway`,`@deepseek-ai/dsh-api-gateway/client` 则提供 `ctx.remote`;两者使用同一份生成的 `InvocationDescriptor` 约定,并将业务选择交给 API Remotes。Connection 承载一元调用的请求关联、信任和响应 envelope,Gateway 则拥有多路复用的 Remote 流。 +## 目录 + +- [Host 服务:`TypertGatewayService`(ctx key:`typertGateway`)](#host-service-typertgatewayservice-ctx-key-typertgateway) +- [Client 服务:`ClientRemote`(ctx key:`remote`)](#client-service-clientremote-ctx-key-remote) +- [模型体验](#model-experience) +- [已知限制与延期工作](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) + +----- + + ## Host 服务:`TypertGatewayService`(ctx key:`typertGateway`) 每次调用时,`ctx.typertGateway.invoke()` 都会解析当前的描述符和 Cordis 服务,校验具名参数是否完全匹配,解析已注册的对象或 Context 身份标识,调用公开的业务方法,并校验其结果。业务服务继承 [`dsh-typert-protocol`](../../typert/protocol/README.zh.md) 的 `TypertRemoteService`,并用 `@Remote` 或 `@RemoteScope` 标记方法;已有其他基类时仍可改用 `bindTypertRemote()`。 @@ -18,6 +36,7 @@ Connection 可用时,Host 入口会在 Connection 共享的 `/api` FetchHandle Host 组合可通过 `registerRemoteEvents()` 注册唯一的应用事件 source。Gateway 为它保留内部 `$events` logical endpoint,只接受空 `args`,并在 source 撤回时中止该注册打开的 stream。事件名单、参数校验和每 Client 队列由 API Remotes 拥有。source factory 在返回 iterable 前同步挂好增量 listener;Gateway 随后先产出 `{ type: 'ready' }`,再迭代 source,让 Client 只在增量投递就绪后开始 baseline 读取。 + ## Client 服务:`ClientRemote`(ctx key:`remote`) `ctx.remote.$mount()` 会校验并注册生成的 Host-for-Client 贡献项,然后为发起调用的 Cordis fiber 安装具体的直接方法和作用域方法。每个 namespace 都是可追踪的 `remote.` 子 Service,并在最后一个方法撤回后卸载。重复端点、命名空间冲突,以及缺少生成的严格编解码器的描述符,都会在方法可调用前报错。 @@ -30,6 +49,7 @@ Host 组合可通过 `registerRemoteEvents()` 注册唯一的应用事件 source 生成的声明合并通过共享的 `TypertClientRemote` 约定提供 TypeScript API。Client 入口不包含 Host 服务或 Host Cordis 接口合并;方法查找和调用使用普通对象与函数,而不使用 JavaScript Proxy。 + ## 模型体验 无,因为该包分发应用调用,不注册任何提示词、工具或会话事件。 @@ -40,9 +60,22 @@ Host 组合可通过 `registerRemoteEvents()` 注册唯一的应用事件 source ## 已知限制与延期工作 + + - Connection 适配器将普通分发故障和业务异常映射为 RPC 的 `internal` 代码,且不附带详细信息;`TypertLookupFailure` 携带的 lookup 策略错误会原样返回。结构化的 `TypertGatewayError` 类别仅供同进程调用方使用。 - SRC 模式仅支持名称唯一的标识符参数,不支持解构、默认值或剩余参数。它只校验值能否安全表示为 JSON,不校验生成的业务类型,也绝不会推断可选字段。 - Client 侧只能挂载严格模式生成的贡献项。SRC 标记不具备 Client 编解码器或类型投影。 - `$stream()` 监督载体替换,但不推断回放语义;各领域自行拥有恢复 cursor 或替换 baseline 的校验,以及正常结束的分类。Connection generation 会重开内部 `$events`;单向通知不会重放,仍处于 pending 的 scoped waterfall 则沿用同一个 event id 重放。 - lookup resolver 按 key 配置;当前无法让单个 Remote 参数或 endpoint 在同一 `agent`/`session` key 下选择 live-only 策略。 - 被转发的事件到达 `$on` 时不做业务载荷投影或脱敏。普通通知在重连后不重放;Agent-scoped waterfall 只投影选择 Client Context 所需的顶层 Agent 身份,并自行携带 pending 生命周期。 + + + +### 开发备注 + +
+维护者工作上下文——点击展开 + +无。 + +
diff --git a/packages/api/remotes/README.i18n.yaml b/packages/api/remotes/README.i18n.yaml index fd100820c1..4cd5c6c9ea 100644 --- a/packages/api/remotes/README.i18n.yaml +++ b/packages/api/remotes/README.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 packages/api/remotes/README.md -README.md: 0c4f0fcab4a741f457f1ffbdd9a4ba7688b9d32c -README.zh.md: f83e31e63b57f09b16403df911154c9d93b07958 +README.md: 7817f30c531abffeb9156e1afaf8c6fe0697431e +README.zh.md: 4529ee60289fc43978f9526d4abb3e2cbf073687 diff --git a/packages/api/remotes/README.md b/packages/api/remotes/README.md index 0c4f0fcab4..7817f30c53 100644 --- a/packages/api/remotes/README.md +++ b/packages/api/remotes/README.md @@ -1,15 +1,39 @@ +--- +description: "Application Remote assembly: selects typed Host capabilities and forwarded events for Client consumers." +kind: "package-reference" +--- + # @deepseek-ai/dsh-api-remotes English | [中文](README.zh.md) +## Summary + Two-sided BFF for Host Remote capabilities selected by this application. The Host entry owns the forwarded-event selection and registers its application event source with API Gateway; the Client entry imports generated `/remote` artifacts as runtime values, mounts each contribution through `ctx.remote.$mount()`, and re-exports their declaration merges. Client business packages depend on this facade rather than the Gateway implementation or individual Remote runtime entries. +## Table of Contents + +- [Use this package](#use-this-package) +- [Forwarded Host events](#forwarded-host-events) +- [Build boundary](#build-boundary) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) + +----- + + +## Use this package + [`@deepseek-ai/dsh-api-session-controller`](../session-controller/README.md) owns Agent and Session identity policy, including the Typert lookup resolvers used by other namespaces. This package only selects and mounts that generated Session contribution; it does not duplicate activation policy. -The current Client assembly mounts Commands, Goal, dynamic Cordis, file and Session references, read-only Host plugin inventory, message feedback, Session Controller, and Workspace Controller contributions. Cordis effect ownership withdraws every contribution when this assembly unloads, while `@deepseek-ai/dsh-api-gateway/client` owns descriptor validation, traced namespace Services, direct and scoped methods, invocation, streams, and cancellation. The Client entry consumes the shared `TypertClientRemote` interface through Cordis and does not import the concrete Gateway. It re-exports the Gateway Client face's declaration merges type-only, so a consumer reaching the forwarded-event vocabulary through this facade gains no runtime edge to the Gateway implementation. +The Client assembly mounts Commands, Goal, dynamic Cordis, file and Session references, read-only Host plugin inventory, message feedback, Session Controller, and Workspace Controller contributions. Cordis effect ownership withdraws every contribution when this assembly unloads, while `@deepseek-ai/dsh-api-gateway/client` owns descriptor validation, traced namespace Services, direct and scoped methods, invocation, streams, and cancellation. The Client entry consumes the shared `TypertClientRemote` interface through Cordis and does not import the concrete Gateway. It re-exports the Gateway Client face's declaration merges type-only, so a consumer reaching the forwarded-event vocabulary through this facade gains no runtime edge to the Gateway implementation. This package owns no physical transport or Host service discovery. It projects the application selection into generated Remote contributions and an independent Host event source per Client; API Gateway owns endpoints, carriers, cancellation, and reconnection. Its Client face can be reused by Web or a future TUI that provides the same React-free `ctx.remote` contract. +----- + + ## Forwarded Host events `src/remote-events.ts` holds `API_REMOTE_FORWARDED_EVENTS`, the allowlist of Host Cordis events this application forwards without renaming, and therefore the legal key set of `ctx.remote.$on`; each entry also selects ordinary emission or Agent-scoped waterfall delivery. The type-only `src/types.ts` derives its selection face. Forwarding one more event requires one entry in that array: the type projection, consumer key face, and Host forwarding loop all derive from it. @@ -18,6 +42,7 @@ The listener signature is not restated here. Each allowlisted event's Cordis `Ev The Host entry registers an independent allowlist listener set and queue for each Client stream. It rejects non-JSON ordinary-event arguments before enqueueing. For a waterfall, it projects only the top-level Agent identity and JSON request fields; a Client result must also be lossless JSON, while `next()` delegates to the following Host listener. The source attaches all listeners synchronously before `ctx.typertGateway.registerRemoteEvents()` exposes Gateway's internal `$events` logical stream, so its first `ready` item proves that incremental delivery is active. Withdrawing the registration aborts active streams; API Proxy does not participate in event forwarding or Connection generation. + ## Build boundary An ordinary repository package belongs to one TypeScript face: Host packages are registered in the root `tsconfig.host.json`, and Client packages in the root `tsconfig.client.json`. `api-remotes` is the only deliberate exception because its Host entry must participate in the Host Typert graph, while `src/client/index.ts` cannot compile until Host tsdown has generated the business packages' `/remote` declarations. @@ -28,6 +53,7 @@ That exception is not just a `files` entry. The root `tsconfig.base.json` maps ` The package-local `clientBundle(..., { hostPhase: true })` makes Host tsdown bundle the Host entry and the later Client tsdown bundle only the browser entry. Ordinary Client plugins remain single Client projects and produce both their Node loader entry and browser bundle during Client tsdown; do not copy this package's split merely because a package has both `src/index.ts` and `src/client/index.ts`. + ## Model Experience None, as this BFF selects Remote application methods and forwarded events but registers nothing model-facing. @@ -38,6 +64,19 @@ No direct effect; mounted Host capabilities own any model-visible behavior they ## Known Limitations and Deferred Work + + - The capability set is fixed by explicit build-time value imports; the Client does not discover the Host's active Services or Remote definitions at runtime. - Additional capabilities require an explicit `/remote` value import and mount in this assembly. - Ordinary forwarded events are not replayed; state that requires reliable recovery needs an owner-provided query, cursor, or opening baseline. + + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/api/remotes/README.zh.md b/packages/api/remotes/README.zh.md index f83e31e63b..4529ee6028 100644 --- a/packages/api/remotes/README.zh.md +++ b/packages/api/remotes/README.zh.md @@ -1,15 +1,39 @@ +--- +description: "应用 Remote 装配:为 Client 消费方选择带类型的 Host 能力与转发事件。" +kind: "package-reference" +--- + # @deepseek-ai/dsh-api-remotes [English](README.md) | 中文 +## 概述 + 为本应用选定的 Host Remote 能力提供双侧 BFF。Host 入口拥有转发事件名单并向 API Gateway 注册应用事件 source;Client 入口以运行时值形式导入生成的 `/remote` 产物,通过 `ctx.remote.$mount()` 挂载每项贡献,并重新导出对应的声明合并。Client 业务包依赖该外观,而不依赖 Gateway 实现或单独的 Remote 运行时入口。 +## 目录 + +- [使用本包](#use-this-package) +- [转发的 Host 事件](#forwarded-host-events) +- [构建边界](#build-boundary) +- [模型体验](#model-experience) +- [已知限制与暂缓事项](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) + +----- + + +## 使用本包 + [`@deepseek-ai/dsh-api-session-controller`](../session-controller/README.zh.md) 拥有 Agent 与 Session 身份策略,包括供其他 namespace 使用的 Typert lookup resolver。本包只选择并挂载生成的 Session contribution,不复制激活策略。 -当前 Client 组合挂载 Commands、Goal、动态 Cordis、文件与 Session 引用、只读 Host 插件清单、消息反馈、Session Controller 和 Workspace Controller contribution。该组合卸载时,Cordis effect 的所有权机制会撤回所有贡献;`@deepseek-ai/dsh-api-gateway/client` 负责描述符校验、可追踪 namespace Service、直接与作用域方法、调用、流与取消。Client 入口通过 Cordis 消费共享的 `TypertClientRemote` 接口,不导入具体 Gateway;它只以 type-only 形式重新导出 Gateway Client face 的声明合并,因此消费端经由本外观取到转发事件词汇时,运行时不会多出一条通往 Gateway 实现的边。 +Client 组合挂载 Commands、Goal、动态 Cordis、文件与 Session 引用、只读 Host 插件清单、消息反馈、Session Controller 和 Workspace Controller contribution。该组合卸载时,Cordis effect 的所有权机制会撤回所有贡献;`@deepseek-ai/dsh-api-gateway/client` 负责描述符校验、可追踪 namespace Service、直接与作用域方法、调用、流与取消。Client 入口通过 Cordis 消费共享的 `TypertClientRemote` 接口,不导入具体 Gateway;它只以 type-only 形式重新导出 Gateway Client face 的声明合并,因此消费端经由本外观取到转发事件词汇时,运行时不会多出一条通往 Gateway 实现的边。 本包不拥有物理传输或 Host 服务发现。它只把应用选择投影为生成的 Remote contribution 和唯一的 Host Cordis event source;API Gateway 负责 endpoint、carrier、取消与重连。Web 或未来的 TUI 只要提供同一份不依赖 React 的 `ctx.remote` 约定,均可复用其 Client face。 +----- + + ## 转发的 Host 事件 `src/remote-events.ts` 持有 `API_REMOTE_FORWARDED_EVENTS`,即本应用不改名转发给消费端的 Host Cordis 事件名单;每个条目还会选择普通发送或 Agent-scoped waterfall 投递。该名单同时就是 `ctx.remote.$on` 的合法键集,只含类型的 `src/types.ts` 派生其选择面。多转发一个事件只需在该数组里加一项:类型投影、消费端键面与 Host 转发循环全部由它派生。 @@ -18,6 +42,7 @@ Host entry 为每条 Client stream 独立注册 allowlist listener 和队列,并在普通事件入队前拒绝非 JSON 参数。对于 waterfall,它只投影顶层 Agent 身份与 JSON 请求字段;Client 结果也必须能无损表示为 JSON,而 `next()` 会委托给后续 Host listener。该 source 在 `ctx.typertGateway.registerRemoteEvents()` 暴露 Gateway 内部的 `$events` logical stream 前同步挂好所有 listener,因此首个 `ready` 项能证明增量投递已就绪。撤回注册会中止活动 stream;API Proxy 不参与事件转发或 Connection generation。 + ## 构建边界 仓库中的普通包只属于一个 TypeScript face:Host 包登记在根 `tsconfig.host.json`,Client 包登记在根 `tsconfig.client.json`。`api-remotes` 是唯一刻意拆分的特例,因为它的 Host 入口要参与 Host Typert 图,而 `src/client/index.ts` 必须等 Host tsdown 生成业务包的 `/remote` 声明后才能编译。 @@ -26,9 +51,9 @@ Host entry 为每条 Client stream 独立注册 allowlist listener 和队列, 这条例外不止是一行 `files`。根 `tsconfig.base.json` 把 `@deepseek-ai/dsh-api-remotes/types` 映射到 `src/types.ts`——**源平面**,与其余所有 workspace 子路径一致,也与生成的 `/remote` 产物相反(后者没有 `paths` 条目,靠 `exports` 命中构建产物)。于是两个 face 都把同一份名单与类型投影收进各自的 program,并向 `lib/types` 发射逐字相同的 `remote-events` 与 `types` 输出;`.tsbuildinfo` 仍各自独立。没有任何门禁强制两个 face 的源文件互不重叠——`scripts/project-reference-faces.ts` 只校验「引用一个 split project 必须指到对应 face」——因此本段记录这次双列为何是有意的。 - 包内 `clientBundle(..., { hostPhase: true })` 让 Host tsdown 打包 Host 入口,让后续 Client tsdown 只打包 browser 入口。普通 Client 插件仍使用单一 Client project,并在 Client tsdown 阶段一起生成 Node loader 入口和 browser bundle;不得因一个包同时存在 `src/index.ts` 与 `src/client/index.ts` 就复制本包的拆分。 + ## 模型体验 无,因为该 BFF 只选择 Remote 应用方法和转发事件,不注册任何模型接口。 @@ -39,6 +64,19 @@ Host entry 为每条 Client stream 独立注册 allowlist listener 和队列, ## 已知限制与暂缓事项 + + - 能力集合由构建时显式导入的值固定确定;Client 不会在运行时发现 Host 中已启用的服务或 Remote 定义。 - 若要增加能力,必须显式导入相应的 `/remote` 值并在此组合中挂载。 - 只有仍在等待的作用域 waterfall 会在重连后重放;单向通知仍是相互隔离的 best-effort 投递。 + + + +### 开发备注 + +
+维护者工作上下文——点击展开 + +无。 + +
diff --git a/packages/api/session-controller/README.i18n.yaml b/packages/api/session-controller/README.i18n.yaml index d417370290..3325cc86a4 100644 --- a/packages/api/session-controller/README.i18n.yaml +++ b/packages/api/session-controller/README.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 packages/api/session-controller/README.md -README.md: 510f336c76dd80ed01bdd2bd4a364106f418831f -README.zh.md: 4503a8f9d0bcfc7f669f00cbc4db69e0d3efd3cd +README.md: 815276847f538f99352e0f0e55fc19af5da67470 +README.zh.md: 51bf5a98c62aaa3fcb2156c029416a4d26515f0b diff --git a/packages/api/session-controller/README.md b/packages/api/session-controller/README.md index 510f336c76..815276847f 100644 --- a/packages/api/session-controller/README.md +++ b/packages/api/session-controller/README.md @@ -1,8 +1,27 @@ +--- +description: "Host and Client session control: create, resume, prompt, follow history, and project live session state." +kind: "package-reference" +--- # Session Controller English | [中文](README.zh.md) -`@deepseek-ai/dsh-api-session-controller` owns the Host `ctx.sessionController` service and the generated Client `ctx.remote.session` namespace. It serves Session list, search, creation, model selection, rename, fork, prompt, attachment, queue, cancellation, message-aligned history, live log following, and Host-wide control state. +## Summary + +`@deepseek-ai/dsh-api-session-controller` owns the Host `ctx.sessionController` service and the generated Client `ctx.remote.session` namespace. It serves Session list, search, creation, model selection, rename, fork, prompt, attachment, queue, cancellation, message-aligned history, live log following, and Host-wide control state. Use it through API Gateway when a Client needs these Session operations. + +## Table of Contents + +- [Use this package](#use-this-package) +- [Configuration](#configuration) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) + +----- + + +## Use this package History pages and follow opening snapshots carry a discriminated `SessionHistoryRecord`. Both variants use `{ type, event }`: `type: 'event'` carries one raw `SessionWireEvent`, while `type: 'chunks'` carries one lossless `ChunkRowEvent` for consecutive same-block `assistant/chunk` deltas. Both inner values expose `type`, `seq`, `time`, and `data`, so the Client retains each accepted record as one `SessionEventLikeEntry` without record-by-record conversion. A packed event's `seq` and `time` identify its first member, and `data` retains the fragment and timestamp-gap arrays. Live follow frames remain individual `event` records. Tool arguments, result content, failures, and `tool/result.data.meta` pass through unchanged; the controller does not resolve a Tool definition, run a presenter, or attach UI data. @@ -10,6 +29,20 @@ Each endpoint states its activation policy. List, search, attachment, history pa The Client adapter exposes `SessionEventStream`, a Gateway `RemoteJournalStream` bound to one ordinary or direct-subagent address. It opens follow before the initial page, publishes only contiguous `replace`, `prepend`, and `append` changes, and repairs reconnect or sequence gaps through a tail page. Ordinary records cover `[event.seq, event.seq]`; packed rows cover `[event.seq, event.seq + memberCount - 1]`. A business, persistence, or unresolved continuity failure terminates the stream, while only physical carrier loss selects automatic resumption. `SessionControlStream` is a Gateway `RemoteSnapshotStream`; every generation opens with a complete process-local baseline, so reconnect replaces queue, jobs, and projection state instead of treating transient values as durable events. +----- + + +## Configuration + +| Field | Default | Meaning | +|---|---:|---| +| `coldBlankProbeMaxBytes` | `1,024` | Maximum physical size of a cold Session artifact eligible for blankness verification; `0` disables probes | + +The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-api-session-controller) is the exhaustive source for accepted fields and their JSDoc. + +----- + + ## Model Experience None, as invoked Agent commands own any model-visible effect. @@ -20,5 +53,18 @@ No direct effect; model requests remain owned by the Agent and LLM packages. ## Known Limitations and Deferred Work + + - Control baselines represent process-local state and therefore cannot reconstruct jobs after a Host restart. - A failed follow resumption remains visible to the caller instead of retrying indefinitely. + + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/api/session-controller/README.zh.md b/packages/api/session-controller/README.zh.md index 4503a8f9d0..51bf5a98c6 100644 --- a/packages/api/session-controller/README.zh.md +++ b/packages/api/session-controller/README.zh.md @@ -1,8 +1,27 @@ +--- +description: "Host 与 Client 会话控制:创建、恢复、提示、跟随历史并投影实时会话状态。" +kind: "package-reference" +--- # Session Controller [English](README.md) | 中文 -`@deepseek-ai/dsh-api-session-controller` 拥有 Host 的 `ctx.sessionController` 服务和生成的 Client `ctx.remote.session` namespace。它提供 Session 列表、搜索、创建、模型选择、重命名、fork、prompt、附件、queue、取消、按消息对齐的历史、live 日志跟随和 Host 范围 control 状态。 +## 概述 + +`@deepseek-ai/dsh-api-session-controller` 拥有 Host 的 `ctx.sessionController` 服务和生成的 Client `ctx.remote.session` namespace。它提供 Session 列表、搜索、创建、模型选择、重命名、fork、prompt、附件、queue、取消、按消息对齐的历史、live 日志跟随和 Host 范围 control 状态。当 Client 需要这些 Session 操作时,请通过 API Gateway 使用它。 + +## 目录 + +- [使用本包](#use-this-package) +- [配置](#configuration) +- [模型体验](#model-experience) +- [已知限制与延期工作](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) + +----- + + +## 使用本包 历史页与 follow opening snapshot 携带带判别字段的 `SessionHistoryRecord`。两个分支都使用 `{ type, event }`:`type: 'event'` 携带一个原始 `SessionWireEvent`,`type: 'chunks'` 则携带一个由连续且属于同一 block 的 `assistant/chunk` delta 组成的无损 `ChunkRowEvent`。两种内部值都公开 `type`、`seq`、`time` 与 `data`,因此 Client 无需逐 record 转换,就能把每条已接受 record 保留为一个 `SessionEventLikeEntry`。packed event 的 `seq` 与 `time` 表示首成员,`data` 保留 fragment 与 timestamp-gap 数组。实时 follow frame 继续携带单个 `event` record。工具参数、结果内容、失败信息和 `tool/result.data.meta` 原样通过;controller 不解析 Tool definition、不运行 presenter,也不附加 UI 数据。 @@ -10,6 +29,20 @@ Client adapter 提供 `SessionEventStream`,即绑定到一个普通 Session 或 direct subagent address 的 Gateway `RemoteJournalStream`。它在读取首个 page 前打开 follow,只发布连续的 `replace`、`prepend` 和 `append` 变更,并通过 tail page 修复重连或 seq 缺口。普通 record 覆盖 `[event.seq, event.seq]`,packed row 覆盖 `[event.seq, event.seq + memberCount - 1]`。业务、persistence 或无法恢复的连续性错误会终止 stream,只有物理载体断开才触发自动恢复。`SessionControlStream` 是 Gateway `RemoteSnapshotStream`;每代都以完整的进程本地 baseline 开始,因此重连会替换 queue、jobs 和 projection 状态,而不会把瞬态值当作 durable event。 +----- + + +## 配置 + +| 字段 | 默认值 | 含义 | +|---|---:|---| +| `coldBlankProbeMaxBytes` | `1,024` | 可进行空白状态验证的冷 Session 工件最大物理大小;`0` 禁用探测 | + +生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-api-session-controller)是所有受支持字段及其 JSDoc 的完整来源。 + +----- + + ## 模型体验 无,因为被调用的 Agent 命令拥有任何模型可见效果。 @@ -20,5 +53,18 @@ Client adapter 提供 `SessionEventStream`,即绑定到一个普通 Session ## 已知限制与延期工作 + + - Control baseline 表示进程本地状态,因此 Host 重启后无法重建 jobs。 - follow 恢复失败会对调用方可见,而不会无限重试。 + + + +### 开发备注 + +
+维护者工作上下文——点击展开 + +无。 + +
diff --git a/packages/api/workspace-controller/README.i18n.yaml b/packages/api/workspace-controller/README.i18n.yaml index fedd773a90..eee1d5bd5a 100644 --- a/packages/api/workspace-controller/README.i18n.yaml +++ b/packages/api/workspace-controller/README.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 packages/api/workspace-controller/README.md -README.md: 0e126f1a0cc52353cb479f42a592e8997207e2e5 -README.zh.md: 2a1c56d8c02ffe7ac6a797b2c620ce3be42a3cc4 +README.md: d2f89e6c9f0118be9150650c1c6dec859375f990 +README.zh.md: 3411ca0642df7fa7f8652d1863905868f54cf4bd diff --git a/packages/api/workspace-controller/README.md b/packages/api/workspace-controller/README.md index 0e126f1a0c..d2f89e6c9f 100644 --- a/packages/api/workspace-controller/README.md +++ b/packages/api/workspace-controller/README.md @@ -1,13 +1,34 @@ +--- +description: "Host and Client workspace control: mutate workspace navigation and follow its complete projection." +kind: "package-reference" +--- # Workspace Controller English | [中文](README.zh.md) -`@deepseek-ai/dsh-api-workspace-controller` owns the Host `ctx.workspaceController` service and the generated Client `ctx.remote.workspace` namespace. Its Remote methods create, rename, remove, and reorder Workspaces, reorder Sessions within a Workspace, archive Sessions from Workspace navigation, and follow the complete Workspace projection. +## Summary + +`@deepseek-ai/dsh-api-workspace-controller` owns the Host `ctx.workspaceController` service and the generated Client `ctx.remote.workspace` namespace. Its Remote methods create, rename, remove, and reorder Workspaces, reorder Sessions within a Workspace, archive Sessions from Workspace navigation, and follow the complete Workspace projection. Use it through API Gateway when a Client must change or follow Workspace navigation. + +## Table of Contents + +- [Use this package](#use-this-package) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) + +----- + + +## Use this package The Host controller serializes mutations whose correctness depends on current registry state and returns stable `WorkspaceError` values for expected failures. Its `follow()` stream synchronously attaches to durable Workspace changes, emits one complete baseline first, then emits ordered `upsert`, `remove`, `order`, and `archived` increments. A reconnect starts another generation with a replacement baseline, so consumers do not depend on receiving every increment while disconnected. The Client entry provides `ClientWorkspaceModel` and `createWorkspaceStateStream()`. The model owns Workspace rows, registry order, archived Session ids, unary mutation echoes, and stream/unary race resolution. A newer Host row wins by `updatedAt`; a committed stream order outranks an older unary response; a removed Workspace id cannot be resurrected by delayed data. The package exposes framework-neutral snapshots and subscriptions, leaving navigation policy and React hooks to the UI owner. +----- + + ## Model Experience None, as Workspace organization is browser and Host control state and registers no prompt, tool, or session event. @@ -18,5 +39,18 @@ No direct effect; Workspace mutations do not alter model requests. ## Known Limitations and Deferred Work + + - `follow()` replaces the whole projection after reconnect and has no durable cursor or incremental catch-up protocol. - Process-local deletion markers prevent delayed data from reviving a removed Workspace only for the lifetime of the Client model. + + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/api/workspace-controller/README.zh.md b/packages/api/workspace-controller/README.zh.md index 2a1c56d8c0..3411ca0642 100644 --- a/packages/api/workspace-controller/README.zh.md +++ b/packages/api/workspace-controller/README.zh.md @@ -1,13 +1,34 @@ +--- +description: "Host 与 Client 工作区控制:修改工作区导航并跟随其完整投影。" +kind: "package-reference" +--- # Workspace Controller [English](README.md) | 中文 -`@deepseek-ai/dsh-api-workspace-controller` 拥有 Host 的 `ctx.workspaceController` 服务和生成的 Client `ctx.remote.workspace` namespace。它的 Remote 方法负责创建、重命名、移除和重排 Workspace,在 Workspace 内重排 Session,从 Workspace 导航中归档 Session,以及跟随完整的 Workspace 投影。 +## 概述 + +`@deepseek-ai/dsh-api-workspace-controller` 拥有 Host 的 `ctx.workspaceController` 服务和生成的 Client `ctx.remote.workspace` namespace。它的 Remote 方法负责创建、重命名、移除和重排 Workspace,在 Workspace 内重排 Session,从 Workspace 导航中归档 Session,以及跟随完整的 Workspace 投影。当 Client 必须修改或跟随 Workspace 导航时,请通过 API Gateway 使用它。 + +## 目录 + +- [使用本包](#use-this-package) +- [模型体验](#model-experience) +- [已知限制与延期工作](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) + +----- + + +## 使用本包 Host 控制器会串行执行正确性取决于当前 registry 状态的变更,并为预期失败返回稳定的 `WorkspaceError` 值。它的 `follow()` 流会同步订阅持久 Workspace 变更,先发出一份完整 baseline,再按顺序发出 `upsert`、`remove`、`order` 和 `archived` 增量。重连会以替换 baseline 开始新一代,因此消费方不依赖收到断线期间的每个增量。 Client 入口提供 `ClientWorkspaceModel` 和 `createWorkspaceStateStream()`。该模型拥有 Workspace 行、registry 顺序、已归档 Session id、一元变更回声,以及流与一元调用的竞态处理。较新的 Host 行按 `updatedAt` 获胜;已提交的流顺序优先于较旧的一元响应;已经移除的 Workspace id 不会被延迟数据复活。该包公开与框架无关的快照和订阅,把导航策略与 React hook 留给 UI owner。 +----- + + ## 模型体验 无,因为 Workspace 组织属于浏览器与 Host 控制状态,并且不注册提示词、工具或会话事件。 @@ -18,5 +39,18 @@ Client 入口提供 `ClientWorkspaceModel` 和 `createWorkspaceStateStream()`。 ## 已知限制与延期工作 + + - `follow()` 在重连后替换完整投影,不提供持久 cursor 或增量追赶协议。 - 进程本地删除标记只会在 Client 模型生命周期内阻止延迟数据复活已移除的 Workspace。 + + + +### 开发备注 + +
+维护者工作上下文——点击展开 + +无。 + +
diff --git a/packages/attachment/README.i18n.yaml b/packages/attachment/README.i18n.yaml index 89845aedf6..1c6b007c86 100644 --- a/packages/attachment/README.i18n.yaml +++ b/packages/attachment/README.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 packages/attachment/README.md -README.md: 556c98b40f601a631e65ccb2121d30fa74b97399 -README.zh.md: 2915e8e207acfdc389b04c609d86ec2ef9c083b9 +README.md: c1afde5f4bd44371fdc5417ad087456dfaa4f054 +README.zh.md: 568d9dc93b7a60d9c346fc8d9cd931d92bdecf35 diff --git a/packages/attachment/README.md b/packages/attachment/README.md index 556c98b40f..c1afde5f4b 100644 --- a/packages/attachment/README.md +++ b/packages/attachment/README.md @@ -1,14 +1,51 @@ -# attachment/ - durable attachment capability family +--- +description: "Package map for the durable image attachment capability family: what you can do with image attachments, and where your images are stored." +kind: "package-group" +--- + +# attachment/ — durable attachment capability family English | [中文](README.zh.md) -The durable binary attachment seam and its local filesystem implementation. Both are product packages. +## Summary + +The `attachment/` group provides durable image attachments: attach images to prompts and commands, and the harness saves them on your machine, shows them again in conversation history, and sends them to the model in later turns. The shipped `dsh` composition enables this with no setup. The capability and its storage are split across two packages, described below. Stored images survive restarts and are never deleted automatically, and only raster image formats are supported. + +## Table of Contents + +- [Packages](#packages) +- [Related documentation](#related-documentation) +- [Dev Note](#dev-note) + +----- + + +## Packages + +These two packages provide durable image attachments; each README describes what you can do with its part. | Package | Role | ctx key | |---|---|---| -| `attachment/` | Immutable attachment references, image limits, and storage service | `ctx.attachments` | -| `attachment-local/` | Content-addressed private storage below `DSH_HOME` | (registers on `ctx.attachments`) | +| [`attachment/`](attachment/README.md) | Image attachments for prompts and commands that persist and come back in history | `ctx.attachments` | +| [`attachment-local/`](attachment-local/README.md) | Stores your attached images on this machine below `DSH_HOME` | registers on `ctx.attachments` | -Unsent browser drafts are intentionally outside this capability. Bytes enter durable storage only when a user prompt is submitted or when a provider adapter commits structured model output. +----- -See [durable image attachments](../../docs/subsystems/attachment.md) for reference validation, storage, and verified-read contracts. + +## Related documentation + +Start with the subsystem reference for the service contract, then the capability-seam table and the configuration surface of the local backend. + +- [Attachment subsystem reference](../../docs/subsystems/attachment.md) — service contract, payload types, and the `ctx.attachments` cordis surface. +- [Capability seams](../../docs/capability-seams.md) — the Service Definition / Service Provider / Consumer split this family follows. +- [Generated configuration catalog](../../docs/config-catalog.md#deepseek-aidsh-attachment-local) — every accepted field of the local backend. + + +## Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/attachment/README.zh.md b/packages/attachment/README.zh.md index 2915e8e207..568d9dc93b 100644 --- a/packages/attachment/README.zh.md +++ b/packages/attachment/README.zh.md @@ -1,14 +1,51 @@ +--- +description: "持久图片附件能力族的包映射:你可以用图片附件做什么,以及你的图片存放在哪里。" +kind: "package-group" +--- + # attachment/:持久附件能力族 [English](README.md) | 中文 -持久二进制附件 seam 及其本地文件系统实现。两者均为产品包。 +## 概述 + +`attachment/` 组提供持久图片附件:把图片附加到提示词和命令,harness 会把它保存到你的机器上,重新显示在对话历史中,并在后续轮次发送给模型。随附的 `dsh` 组合无需任何设置即可支持这一点。该能力与它的存储拆分为两个包,见下文。已存储的图片在重启后依然存在且永远不会被自动删除,并且只支持光栅图片格式。 + +## 目录 + +- [包](#packages) +- [相关文档](#related-documentation) +- [开发备注](#dev-note) + +----- + + +## 包 + +这两个包提供持久图片附件;每个 README 描述其各自部分可以做什么。 | 包 | 角色 | ctx 键 | |---|---|---| -| `attachment/` | 不可变附件引用、图片限制和存储服务 | `ctx.attachments` | -| `attachment-local/` | `DSH_HOME` 下的私有内容寻址存储 | (注册至 `ctx.attachments`) | +| [`attachment/`](attachment/README.zh.md) | 可用于提示词与命令、会持久保存并回到历史中的图片附件 | `ctx.attachments` | +| [`attachment-local/`](attachment-local/README.zh.md) | 把附加图片存储在本机 `DSH_HOME` 下 | 注册到 `ctx.attachments` | -未发送的浏览器草稿刻意位于这项能力之外。只有用户提交提示词,或提供方适配器提交结构化模型输出时,字节才进入持久存储。 +----- -有关引用校验、存储和经过校验的读取约定,参见[持久图片附件](../../docs/subsystems/attachment.zh.md)。 + +## 相关文档 + +先从子系统参考了解服务约定,再看能力 seam 表与本地后端的配置面。 + +- [附件子系统参考](../../docs/subsystems/attachment.zh.md)——服务约定、载荷类型与 `ctx.attachments` 的 cordis 接口面。 +- [能力 seam](../../docs/capability-seams.zh.md)——本家族遵循的 Service Definition / Service Provider / Consumer 拆分。 +- [生成配置目录](../../docs/config-catalog.zh.md#deepseek-aidsh-attachment-local)——本地后端的每个受支持字段。 + + +## 开发备注 + +
+维护者的工作上下文——点击展开 + +无。 + +
diff --git a/packages/attachment/attachment-local/README.i18n.yaml b/packages/attachment/attachment-local/README.i18n.yaml index 353d5d1d7b..f3250efc09 100644 --- a/packages/attachment/attachment-local/README.i18n.yaml +++ b/packages/attachment/attachment-local/README.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 packages/attachment/attachment-local/README.md -README.md: 7bd0283e7f922ecfa2be4b67e296f5c0016f4302 -README.zh.md: 6e8ad367cf7e81f373c112e27cace07491638e0f +README.md: 2b4c4e17ee8578d63c3f2ef44f7b2c44f116906a +README.zh.md: f44b0cf975b2a86c8e5b7c8ba585701fe88c19f2 diff --git a/packages/attachment/attachment-local/README.md b/packages/attachment/attachment-local/README.md index 7bd0283e7f..2b4c4e17ee 100644 --- a/packages/attachment/attachment-local/README.md +++ b/packages/attachment/attachment-local/README.md @@ -1,25 +1,150 @@ +--- +description: "Local storage for your attached images below DSH_HOME, for users and maintainers choosing or debugging where image attachments are kept." +kind: "package-reference" +--- + # @deepseek-ai/dsh-attachment-local English | [中文](README.zh.md) -The private local implementation of [`@deepseek-ai/dsh-attachment`](../attachment). Objects land at `/attachments/v1/objects//` and are addressed by an opaque `sha256:` id. Each process proves a home durable once by syncing every ancestor entry to the filesystem root. Writes use a private staging directory, a synced temporary file, an atomic exclusive hard-link publish, owner-read-only object permissions, and directory syncs on the publication path (POSIX; Windows relies on filesystem metadata journaling) so the reported reference survives a crash. +## Summary -Admission accepts at most 20 images and 200MiB of encoded source bytes per message. Each source may use up to 20MiB, 64,000,000 pixels, and 8192px per side. It then prepares a provider-independent normalized attachment. EXIF orientation is applied, metadata and color profiles are removed, pixels become 8-bit sRGB/sRGBA, and the raster is reduced proportionally to the `normalizedImageMaxPixels` total-pixel budget (2048x2048 by default) with a `normalizedImageMaxDimension` long-edge cap (8192px by default), so extreme aspect ratios keep their short-edge resolution instead of collapsing under a long-edge rule. The normalized attachment has its own `normalizedImageMaxBytes` encoded-byte target (4MiB by default). Transparent pixels are retained; Sharp/libvips may omit an alpha plane whose samples are all opaque. Sources with an alpha channel encode as WebP (effort 0) and opaque sources as JPEG, both on the quality ladder 85, 75, 60. Each ladder step runs only after the preceding step exceeds the target, and when every step exceeds it the smallest output is kept; provider byte caps stay enforced by the route that transmits the bytes. A clean, single-frame 8-bit sRGB/sRGBA PNG, JPEG, or WebP already within both normalization limits passes through byte-identically; 16-bit PNG, GIF, animated input, metadata, orientation, and incompatible color spaces force conversion. The source and converted attachment are each fully decoded once. `saveImages` prepares and verifies every normalized attachment once before publishing the batch, so validation failure leaves no partial references and commit does not repeat full image encoding. +This package provides the local storage and image-processing backend for attachments: source images are validated, oriented, stripped of metadata and color profiles, normalized to 8-bit sRGB/sRGBA, and saved below `DSH_HOME`; route-specific request versions are derived and cached separately. It is what the shipped `dsh` composition uses, so durable image attachments work without configuration. Identical normalized images are stored only once, concurrent reads of one request variant share work, and stored images stay readable after later admission-limit changes. Storage is local to this machine — other hosts cannot read these images — and objects are never deleted automatically. -Request versions live below `/attachments/v1/request-images/`. `readImageRequest` scales the stored normalized attachment under a total-pixel budget without enlargement, then applies a separate encoded-byte target. The request encoder uses the same alpha routing and quality ladder as normalization, WebP (effort 0) at 85, 75, 60 for alpha sources and JPEG at those qualities for opaque sources, executed lazily and keeping the smallest output when every quality exceeds the target. Its cache identity includes the attachment id, transform version, pixel and byte budgets, and fixed encoder settings. Cached bytes are header-probed for format, 8-bit sRGB/sRGBA, dimension, and alpha facts before use; a mismatch regenerates the entry. Concurrent calls for one identity share one transform and cache write; cancelling one waiter does not cancel the shared work. Callers compose ordered batches from singular reads, while the service's FIFO limiter applies `imageCompressionConcurrency` to simultaneous normalization and request transforms. The setting ranges from 1 through 8 and defaults to 2; file publication remains ordered after preparation. +## Table of Contents -`DSH_HOME` resolves through the shared path policy: explicit config, `$DSH_HOME`, then `~/.dsh`. Session logs contain only the reference and verified metadata. `imageHostPath` derives the normalized object's absolute host path and does not inspect the tool execution world. At request assembly, an LLM consumer asks the mounted filesystem to map that host object into its execution world. A host-backed filesystem returns a process path; a remote filesystem without a shared mount returns no path. The mapped path is absent from durable history and from `RequestImageAttachment`. `readImage` forwards optional cancellation into the filesystem read, observes it around verification, and preserves it instead of wrapping it as `ATTACHMENT_READ_FAILED`. +- [Use this package](#use-this-package) +- [Understand the implementation](#understand-the-implementation) +- [Further Exploration](#further-exploration) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) +----- + + +## Use this package + +In the default composition, attach images to a prompt or command and they are stored on this machine automatically. If you compose your own setup, mounting this one plugin gives you durable image attachments. + +### Minimal configuration + +Mount the plugin with no required configuration. The defaults below define what you can attach; the generated configuration catalog is the exhaustive source for every field. + +```yaml +- name: '@deepseek-ai/dsh-attachment-local' +``` + +| Field | Default | Meaning | +|---|---|---| +| `dshHome` | resolved | Explicit harness home; omitted follows `$DSH_HOME`, then `~/.dsh` | +| `maxImageBytes` | `20 MiB` | Maximum encoded source bytes accepted for one image | +| `maxImagesPerMessage` | `20` | Maximum image count accepted in one submitted message | +| `maxMessageImageBytes` | `200 MiB` | Maximum aggregate encoded source bytes in one submitted message | +| `maxImagePixels` | `64,000,000` | Maximum source width multiplied by height | +| `maxImageDimension` | `8192` | Maximum source width or height | +| `normalizedImageMaxPixels` | `2048 × 2048` | Total-pixel budget of the stored normalized image | +| `normalizedImageMaxDimension` | `8192` | Maximum long edge after applying the total-pixel budget | +| `normalizedImageMaxBytes` | `4 MiB` | Encoded-byte target; the smallest quality-ladder output is kept when none fits | +| `imageCompressionConcurrency` | `2` | FIFO limit for concurrent normalization and request transforms | + +The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-attachment-local) is the exhaustive source for every accepted field and its JSDoc. + +### Where your images are stored and how long they last + +Attached images are kept below `/attachments/v1` on this machine. Stored images are never deleted automatically, identical images are stored only once, and a later tightening of the limits never makes already-saved images unreadable. If your images must be readable from another machine, this package is not the right fit. + +### What happens when you attach an image + +Attach an image and its source limits, media, dimensions, and pixels are checked before it is normalized and saved. EXIF orientation is applied, metadata and color profiles are removed, transparency is preserved, and the raster is reduced under a total-pixel budget plus a long-edge cap. Alpha images use WebP and opaque images use JPEG on the shared 85/75/60 quality ladder; the smallest output is retained when every candidate exceeds the byte target. An accepted image reappears in history and later turns, including after restart; the selected model route receives a cached request version and, when its filesystem maps the host object, a read-only execution-world path. + +### What can go wrong + +An image can be refused when you attach it: unsupported format, over the byte, pixel, or per-side dimension limits, or bytes that do not match their declared type. On a later read, an image that was deleted or corrupted on disk fails with a clear error. Each failure carries a stable code so the client and protocol adapters can explain it in their own words. + +----- + + +## Understand the implementation + +
+Implementation internals — click to expand + +This section explains the durability and verification design behind the storage, and the write and read paths that realize it; observable behavior is fully covered in [Use this package](#use-this-package). + +### Design decisions + +- **Durability by fsync chain, not existence.** A synced file alone does not survive a crash when its directory entry never reached storage, so the write path syncs every ancestor entry to a process-proven boundary before a reference can reach a session checkpoint. +- **Normalize once, project per route.** Admission persists one provider-independent normalized attachment; request projection derives deterministic variants without rewriting durable history. +- **Lazy alpha-routed encoding.** Alpha images use WebP and opaque images use JPEG; quality candidates run in 85/75/60 order, and the smallest output is retained when none meets the encoded-byte target. +- **Limits are write-time policy.** Byte, total-pixel, and per-side dimension limits bind admission only, so tightening them later never makes admitted history unreadable. + +### Write and read paths + +Objects land at `/attachments/v1/objects//`; equal bytes deduplicate to one object and one `sha256:` id. Before the first write, the process syncs every ancestor directory of the home down to the filesystem root once, so a directory another process created but has not yet synced is never mistaken for a safe boundary. Writes then stage bytes in `v1/tmp`, sync the temporary file, publish with an atomic exclusive hard link, and sync the publication directories — on Windows, filesystem metadata journaling owns entry durability. Once the save resolves, the reported reference is durable. + +Admission accepts up to 20 images and 200 MiB of source bytes per message; one source may use up to 20 MiB, 64 million pixels, and 8192 pixels per side. It applies orientation, removes metadata and color profiles, and normalizes under a 2048×2048 total-pixel budget, an 8192-pixel long edge, and a 4 MiB encoded-byte target. Extreme aspect ratios therefore retain their short-edge resolution. Clean single-frame 8-bit sRGB/sRGBA PNG, JPEG, or WebP input already within those limits passes through byte-identically; GIF, animation, metadata, orientation, 16-bit PNG, and incompatible color spaces force conversion. + +Request versions live below `/attachments/v1/request-images/`. `readImageRequest` scales without enlargement to a route pixel budget, then applies a separate encoded-byte target through the same alpha routing and quality ladder. Its cache identity includes the attachment id, transform version, budgets, and fixed encoder settings; cached bytes are header-probed for format, 8-bit sRGB/sRGBA, dimensions, and alpha facts, and a mismatch regenerates the entry. Concurrent callers share one transform and cache write, while cancellation stops shared work only when no waiter remains. `imageHostPath` derives the normalized object's host path, and the mounted filesystem may map that path into its execution world without writing it to durable history. + +### Source map + +| File | Role | +|---|---| +| [`src/index.ts`](src/index.ts) | Plugin entry: `LocalAttachmentStore`, `Config` schema, defaults | +| [`src/store.ts`](src/store.ts) | Content-addressed write and verified read: staging, hard-link publish, fsync chain, digest verification | +| [`src/normalization.ts`](src/normalization.ts) + [`src/encoding.ts`](src/encoding.ts) | Provider-independent normalization and bounded format/quality candidates | +| [`src/request-image.ts`](src/request-image.ts) | Route-specific request transforms, cache identity, and singleflight | +| [`src/image.ts`](src/image.ts) | Full raster decode and metadata verification | +| [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant; immutable writes and verified reads enforced at the backend boundary) | + +
+ +----- + + +## Further Exploration + +For the full service contract and payload types, read the subsystem reference; for the capability this storage backs, read the seam package. + +- [Attachment subsystem reference](../../../docs/subsystems/attachment.md) — service contract, payload types, and the `ctx.attachments` cordis surface. +- [Attachment seam package](../attachment/README.md) — the image attachment capability this storage backs. +- [Generated configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-attachment-local) — every accepted config field and its source declaration. +- [Home paths resolution](../../util/home-paths/README.md) — how `DSH_HOME` resolves from explicit config, environment, and the user home. + +----- + + ## Model Experience -Indirectly, through request descriptors. When the current execution filesystem maps this backend's host object, the model receives each retained or offloaded image's identity, dimensions, media type, read-only mapped path, matching extension for a writable copy, and a warning that normalization may have resized or re-encoded the upload. +Indirectly, through request descriptors. A mapped execution filesystem lets the model see each image's identity, dimensions, media type, read-only process path, writable-copy extension, and normalization warning alongside the request bytes. #### KV Cache effect -Normalization and request projection are deterministic. An unchanged attachment and route policy reuse identical cached request bytes on later turns. Execution-world path mapping is resolved separately and can change historical descriptor text without changing those bytes or their `variantId`. +Normalization and request projection are deterministic. An unchanged attachment and route policy reuse identical cached request bytes on later turns; execution-world path mapping can change descriptor text without changing those bytes or their `variantId`. ## Known Limitations and Deferred Work -- Objects are retained indefinitely; reference-aware garbage collection is deferred. -- Animated GIF sources keep only their first frame; animation is outside the version-one image contract. -- The normalization and request encoders are pinned by the installed sharp/libvips build; an encoder or transform-version upgrade re-addresses future normalized attachments or request variants while existing objects stay valid. + + + +These limits describe what this storage can and cannot do; they are current package constraints. + +- **Images are kept forever** — stored images are never deleted automatically, and nothing collects unreferenced objects. +- **Local to this machine** — images live on the machine that runs the harness; other hosts cannot read them. +- **Animated GIF becomes static** — normalization retains only the first frame; animation is outside the version-one image contract. +- **Encoder output is versioned** — the installed Sharp/libvips build pins normalization and request bytes; an encoder or transform-version upgrade re-addresses future variants while existing objects remain valid. + + +### Dev Note + +
+Working context for maintainers — click to expand + +This Dev Note is working context for maintainers: undecided directions and open questions. It is explicitly non-authoritative — shipped behavior and limits live in the sections above and the package code. + +#### Future: retention and remote storage + +Retention and garbage collection are deferred because resumed and forked sessions may share immutable objects, and a backend serving remote runtimes or shared storage would need its own durability proof. Both directions are undecided; the local storage currently retains every object under `DSH_HOME`. + +
diff --git a/packages/attachment/attachment-local/README.zh.md b/packages/attachment/attachment-local/README.zh.md index 6e8ad367cf..f44b0cf975 100644 --- a/packages/attachment/attachment-local/README.zh.md +++ b/packages/attachment/attachment-local/README.zh.md @@ -1,25 +1,150 @@ +--- +description: "DSH_HOME 下附加图片的本地存储,供用户与维护者选择或排查图片附件的存放位置。" +kind: "package-reference" +--- + # @deepseek-ai/dsh-attachment-local [English](README.md) | 中文 -这是 [`@deepseek-ai/dsh-attachment`](../attachment) 的私有本地实现。对象存放在 `/attachments/v1/objects//`,并通过不透明的 `sha256:` 标识符寻址。每个进程都会把每级祖先目录项同步到文件系统根目录,以此一次性证明 home 已持久化。写入使用私有暂存目录、经过同步的临时文件、原子且排他的硬链接发布、仅所有者可读的对象权限,并对发布路径执行目录同步(适用于 POSIX;Windows 依赖文件系统元数据日志),确保已报告的引用能够在崩溃后继续存在。 +## 概述 -每条消息最多准入 20 张图片,源图编码字节总量不超过 200MiB。每张源图不得超过 20MiB、64,000,000 像素和单边 8192px。随后生成提供方无关的规范化附件:应用 EXIF 方向,删除元数据和色彩配置文件,转换为 8-bit sRGB/sRGBA,并保持宽高比把像素总量缩到 `normalizedImageMaxPixels` 总像素预算内(默认 2048×2048),再受 `normalizedImageMaxDimension` 长边上限约束(默认 8192px),因此极端长宽比的图保留短边分辨率,而不会在长边规则下坍缩。规范化附件有独立的 `normalizedImageMaxBytes` 编码字节目标(默认 4MiB)。透明像素会保留;当所有 alpha 样本均为不透明时,Sharp/libvips 可能省略没有实际作用的 alpha 平面。带 alpha 通道的源图编码为 WebP(effort 0),不透明源图编码为 JPEG,共用质量阶梯 85、75、60。只有前一档超过目标时才会执行下一档;全部档位都超过目标时保留最小的产物,提供方字节硬上限仍由传输该字节的路由执行。已经处于两个规范化上限内的干净、单帧、8-bit sRGB/sRGBA PNG、JPEG 或 WebP 按字节原样直通;16-bit PNG、GIF、动图、元数据、方向和不兼容色彩空间都会触发转换。源图和转换后的附件各完整解码一次。`saveImages` 在发布任何批次成员前为每张图片各准备并验证一次规范化附件,因此校验失败不会留下部分引用,提交阶段也不会重复执行完整图片编码。 +本包提供附件的本地存储与图片处理后端:源图经过校验、方向修正、元数据与色彩配置移除,并规范化为 8-bit sRGB/sRGBA 后保存在 `DSH_HOME` 下;路由专用请求版本另行派生并缓存。随附的 `dsh` 组合使用的就是它,因此持久图片附件无需配置即可工作。相同规范化图片只存一份,同一请求变体的并发读取共享工作,即使后来收紧准入限制,已存图片仍然可读。存储仅限本机——其他主机无法读取这些图片——对象也永远不会自动删除。 -请求版本保存在 `/attachments/v1/request-images/`。`readImageRequest` 在不放大小图的前提下,把存储的规范化附件缩放到总像素预算内,再应用独立的编码字节目标。请求编码器与规范化共用同一套 alpha 路由和质量阶梯:带 alpha 的源图依次尝试质量 85、75、60 的 WebP(effort 0),不透明源图依次尝试这些质量的 JPEG;候选按需执行,全部档位都超过目标时保留最小的产物。缓存身份包含附件 ID、变换策略版本、像素和字节预算及固定编码参数。缓存字节在使用前经头部探测校验格式、8-bit sRGB/sRGBA、尺寸和 alpha 事实;不匹配则重新生成该条目。同一身份的并发调用共享一次变换和缓存写入;取消一个等待方不会取消共享任务。调用方组合单数读取得到有序批次,服务的 FIFO 限流器通过 `imageCompressionConcurrency` 限制同时执行的规范化和请求变换。该配置范围为 1 至 8,默认值为 2;文件发布仍在准备结束后按顺序执行。 +## 目录 -`DSH_HOME` 按共享路径策略解析:显式配置、`$DSH_HOME`,最后是 `~/.dsh`。会话日志只包含引用和经过校验的元数据。`imageHostPath` 派生规范化对象的绝对宿主路径,不检查工具执行环境。组装请求时,LLM 消费方要求当前文件系统把该宿主对象映射到其执行环境。宿主文件系统返回进程路径;没有共享挂载的远程文件系统不返回路径。映射后的路径不进入持久历史,也不进入 `RequestImageAttachment`。`readImage` 会把可选取消信号传入文件系统读取、在校验前后观察该信号,并保留取消语义,而不会将其包装成 `ATTACHMENT_READ_FAILED`。 +- [使用本包](#use-this-package) +- [理解实现](#understand-the-implementation) +- [进一步探索](#further-exploration) +- [模型体验](#model-experience) +- [已知限制与延期工作](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) +----- + + +## 使用本包 + +在默认组合中,把图片附加到提示词或命令,它们会自动保存到本机。自行组合时,挂载这一个插件即可获得持久图片附件。 + +### 最小配置 + +挂载插件,无需任何必填配置。下表默认值定义你可以附加什么;生成的配置目录是每个字段的穷尽式真源。 + +```yaml +- name: '@deepseek-ai/dsh-attachment-local' +``` + +| 字段 | 默认值 | 含义 | +|---|---|---| +| `dshHome` | 自动解析 | 显式 harness home;省略时依次跟随 `$DSH_HOME` 与 `~/.dsh` | +| `maxImageBytes` | `20 MiB` | 单张图片接受的最大编码源字节数 | +| `maxImagesPerMessage` | `20` | 单条提交消息接受的最大图片数量 | +| `maxMessageImageBytes` | `200 MiB` | 单条提交消息接受的最大编码源图字节总数 | +| `maxImagePixels` | `64,000,000` | 源图接受的最大宽度乘以高度 | +| `maxImageDimension` | `8192` | 源图接受的最大宽度或高度 | +| `normalizedImageMaxPixels` | `2048 × 2048` | 已存规范化图片的总像素预算 | +| `normalizedImageMaxDimension` | `8192` | 应用总像素预算后的最大长边 | +| `normalizedImageMaxBytes` | `4 MiB` | 编码字节目标;没有候选满足时保留质量阶梯中的最小输出 | +| `imageCompressionConcurrency` | `2` | 并发规范化与请求变换的 FIFO 上限 | + +生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-attachment-local)是每个受支持字段及其 JSDoc 的穷尽式真源。 + +### 图片存储在哪里、会保留多久 + +附加的图片保存在本机的 `/attachments/v1` 下。已存储的图片永远不会被自动删除,相同图片只会存储一份,之后收紧限制也绝不会让已保存的图片不可读。如果你的图片需要能从另一台机器读取,本包并不合适。 + +### 附加图片时会发生什么 + +附加图片后,会先检查源图限制、媒体类型、尺寸与像素,再完成规范化并保存。系统应用 EXIF 方向、移除元数据与色彩配置、保留透明度,并按总像素预算与长边上限缩小光栅。带 alpha 的图片使用 WebP,不透明图片使用 JPEG,共享 85/75/60 质量阶梯;全部候选都超过字节目标时保留最小输出。被接受的图片会重新出现在历史和后续轮次中,重启后也不例外;所选模型路由会收到缓存的请求版本,并在其文件系统可映射宿主对象时收到只读执行世界路径。 + +### 可能出什么问题 + +附加图片时可能被拒绝:格式不受支持、超出字节、像素或单边尺寸限制,或者字节与声明类型不符。之后读取时,磁盘上被删除或损坏的图片会以明确错误失败。每个失败都带有稳定错误码,客户端与协议适配器可以用自己的措辞解释。 + +----- + + +## 理解实现 + +
+实现细节——点击展开 + +本节解释存储背后的持久性与校验设计,以及实现它的写入与读取路径;可观察行为已在[使用本包](#use-this-package)中完整说明。 + +### 设计决策 + +- **持久性靠 fsync 链,而非存在性。** 当目录项从未到达存储时,仅同步文件无法在崩溃后存活,因此写入路径会在引用可能到达会话检查点前,把每个祖先条目同步到进程已验证的边界。 +- **一次规范化,按路由投影。** 准入持久保存一份提供方无关的规范化附件;请求投影派生确定性变体而不改写持久历史。 +- **惰性 alpha 路由编码。** 带 alpha 的图片使用 WebP,不透明图片使用 JPEG;质量候选按 85/75/60 顺序运行,没有候选满足编码字节目标时保留最小输出。 +- **限制是写入时策略。** 字节、总像素与单边尺寸限制只约束准入,因此之后收紧它们绝不会让已接纳的历史不可读。 + +### 写入与读取路径 + +对象存放在 `/attachments/v1/objects//`;相同字节会去重为同一个对象和同一个 `sha256:` 标识符。首次写入前,进程会把 home 的每个祖先目录逐级同步到文件系统根目录,因此绝不会把另一个进程已创建但尚未同步的目录误认为安全边界。随后,写入过程把字节暂存到 `v1/tmp`、同步临时文件、以原子且排他的硬链接发布,并同步发布目录——在 Windows 上,文件系统元数据日志负责目录项持久性。保存成功后,已报告的引用即持久。 + +准入允许每条消息最多 20 张图片与 200 MiB 源字节;单个源图最多 20 MiB、6400 万像素与单边 8192 像素。系统应用方向、移除元数据与色彩配置,并把规范化结果限制在 2048×2048 总像素预算、8192 像素长边和 4 MiB 编码字节目标内,因此极端宽高比会保留短边分辨率。已经满足限制的干净、单帧、8-bit sRGB/sRGBA PNG、JPEG 或 WebP 会逐字节直通;GIF、动画、元数据、方向、16-bit PNG 与不兼容色彩空间会触发转换。 + +请求版本位于 `/attachments/v1/request-images/`。`readImageRequest` 在不放大的前提下缩放到路由像素预算,再通过相同的 alpha 路由与质量阶梯应用独立编码字节目标。缓存身份包含附件 id、变换版本、预算与固定编码参数;缓存字节会先探测格式、8-bit sRGB/sRGBA、尺寸与 alpha 信息,不匹配时重新生成。并发调用方共享一次变换与缓存写入,且只在没有等待方时由取消停止共享工作。`imageHostPath` 派生规范化对象的宿主路径,挂载的文件系统可以把该路径映射进执行世界,而不会写入持久历史。 + +### 源码地图 + +| 文件 | 职责 | +|---|---| +| [`src/index.ts`](src/index.ts) | 插件入口:`LocalAttachmentStore`、`Config` schema、默认值 | +| [`src/store.ts`](src/store.ts) | 内容寻址写入与校验读取:暂存、硬链接发布、fsync 链、摘要校验 | +| [`src/normalization.ts`](src/normalization.ts) + [`src/encoding.ts`](src/encoding.ts) | 提供方无关的规范化与有界格式/质量候选 | +| [`src/request-image.ts`](src/request-image.ts) | 路由专用请求变换、缓存身份与 singleflight | +| [`src/image.ts`](src/image.ts) | 完整光栅解码与元数据校验 | +| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式;不可变写入与校验读取在后端边界直接强制) | + +
+ +----- + + +## 进一步探索 + +完整的服务约定与载荷类型请看子系统参考;这份存储所支撑的能力请看 seam 包。 + +- [附件子系统参考](../../../docs/subsystems/attachment.zh.md)——服务约定、载荷类型与 `ctx.attachments` 的 cordis 接口面。 +- [附件 seam 包](../attachment/README.zh.md)——本存储支撑的图片附件能力。 +- [生成配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-attachment-local)——每个受支持配置字段及其源声明。 +- [Home 路径解析](../../util/home-paths/README.zh.md)——`DSH_HOME` 如何从显式配置、环境变量与用户主目录解析。 + +----- + + ## 模型体验 -该包通过请求描述间接影响模型。当前执行文件系统能够映射本后端的宿主对象时,描述会给出每张保留或被 offload 图片的身份、尺寸、媒体类型、映射后的只读路径、复制到可写位置时使用的匹配扩展名,以及规范化过程可能缩小或重新编码上传图片的提醒。 +本包通过请求描述符间接影响模型。执行文件系统可以映射宿主对象时,模型会随请求字节看到每张图片的身份、尺寸、媒体类型、只读进程路径、可写副本扩展名与规范化警告。 -#### KV 缓存影响 +#### KV Cache 影响 -规范化和请求投影都是确定性的。附件和路由策略不变时,之后各轮会复用相同的缓存请求字节。执行环境路径单独解析;它的映射变化会改变历史描述文本,但不会改变请求字节或 `variantId`。 +规范化和请求投影都是确定性的。附件和路由策略不变时,之后各轮会复用相同的缓存请求字节;执行世界路径映射可以改变描述符文本,而不会改变这些字节或其 `variantId`。 -## 已知限制与待完成工作 +## 已知限制与延期工作 -- 对象会无限期保留;基于引用的垃圾回收尚未实现。 -- 动态 GIF 源图只保留首帧;动画在版本一图片契约之外。 -- 规范化和请求版本编码器由安装的 sharp/libvips 构建钉定;编码器或变换策略版本升级会让未来的规范化附件或请求变体产生新地址,已有对象保持有效。 + + + +这些限制描述了这份存储能做什么、不能做什么;它们是当前包约束。 + +- **图片会永久保留**——已存储的图片永远不会被自动删除,也没有任何机制回收未被引用的对象。 +- **仅限本机**——图片存放在运行 harness 的机器上;其他主机无法读取。 +- **动态 GIF 变为静态**——规范化只保留第一帧;动画不属于版本一图片约定。 +- **编码器输出带版本**——已安装的 Sharp/libvips 构建钉定规范化与请求字节;编码器或变换版本升级会让未来变体产生新地址,已有对象继续有效。 + + +### 开发备注 + +
+维护者的工作上下文——点击展开 + +本开发备注是维护者的工作上下文:尚未决定的探索方向与开放问题。它明确不具权威性——已交付的行为与限制以上文和包代码为准。 + +#### 未来:保留与远程存储 + +保留与垃圾回收被推迟,因为恢复和 fork 后的会话可能共享不可变对象;服务于远程运行时或共享存储的后端则需要自己的持久性证明。两个方向都尚未决定;本地存储当前在 `DSH_HOME` 下保留所有对象。 + +
diff --git a/packages/attachment/attachment/README.i18n.yaml b/packages/attachment/attachment/README.i18n.yaml index 2ef7675417..e67d95604d 100644 --- a/packages/attachment/attachment/README.i18n.yaml +++ b/packages/attachment/attachment/README.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 packages/attachment/attachment/README.md -README.md: 976bfc82a4cf8a626259ffcddabcbead8ba03154 -README.zh.md: bc33293b34295250c332d6111fb0d52e506c47db +README.md: 01a5d6ee143ff53175dd7327cd6d419af61938a3 +README.zh.md: 1a1d297297a6815ce5338391af0182e471f99000 diff --git a/packages/attachment/attachment/README.md b/packages/attachment/attachment/README.md index 976bfc82a4..01a5d6ee14 100644 --- a/packages/attachment/attachment/README.md +++ b/packages/attachment/attachment/README.md @@ -1,23 +1,134 @@ +--- +description: "Durable image attachments for users and maintainers attaching, reusing, or debugging images in prompts and commands." +kind: "package-reference" +--- + # @deepseek-ai/dsh-attachment English | [中文](README.zh.md) -The durable attachment seam. `ctx.attachments` validates and durably commits a provider-independent normalized image, then returns a serializable `ImageAttachmentRef`; consumers never persist browser paths, object URLs, provider URLs, local storage paths, or base64 in session events. +## Summary -Unsent composer images remain browser-owned temporary drafts. `validateImage` runs the complete admission policy without persisting. `saveImages` owns batch count and aggregate-byte limits, prepares every normalized attachment before publishing any member, then commits in order and returns references only after the complete batch succeeds. A later storage failure returns no partial references, although an earlier immutable content-addressed object may remain unreachable until reference-aware garbage collection exists. `AttachmentError.code` uses the closed `AttachmentErrorCode` string union. Its `ImageAdmissionErrorCode` subset marks caller-correctable image-input failures; `isImageAdmissionError` recognizes that subset at runtime so each protocol adapter can map its own error vocabulary. `saveImage` commits one accepted image before any model-visible session event is published and returns its `ImageAttachmentRef`. When normalization reduces the raster, the reference records the orientation-applied input size in `originalDimensions`. `readImage` verifies the normalized attachment against its logged metadata. `readImageRequest` deterministically derives a route-sized request version whose identity covers the attachment id, transform version, pixel and byte budgets, and encoder settings. The pure `requestImageDimensions` export computes that projection's aspect-preserving dimensions from a total-pixel budget, so providers and request pricing share one geometry. `imageHostPath` optionally exposes the provider-owned object's absolute host path; it makes no claim that the current model tools can read that path. An LLM consumer combines this location with the mounted filesystem's execution-world mapping when it serializes a request. That current access path remains separate from the request version and its `variantId`. Callers compose ordered batches with `Promise.all(refs.map(...))`; the local implementation still bounds compression through its instance limiter, cache, and singleflight. Callers may cancel reads and projections; implementations preserve cancellation instead of translating it into a storage failure. +You can attach images to prompts and commands, and the harness keeps provider-independent normalized versions durably: each source image is admitted and normalized before your message is processed, reappears in conversation history, and is projected to the selected model route in later turns of the same session. The shipped `dsh` composition enables this with no setup. Attached images survive restarts, while browser paths, provider URLs, local storage paths, and base64 never enter durable session events. Only raster formats (PNG, JPEG, WebP, GIF) are accepted, and unsent composer drafts stay in the browser until you submit. Stored images are never deleted automatically, and non-image files, audio, and video are not supported yet. -`admitEncodedImages(attachments, images)` is the shared wire entry used by every RPC endpoint that accepts browser uploads (the session prompt endpoint and the command executor): it enforces canonical base64 on every member, then delegates batch admission — limits, validation, ordered commit — to `saveImages`. The base64 upload form is `EncodedImageAttachment`, exported from `@deepseek-ai/dsh-attachment/types` so wire contracts can reference it. +## Table of Contents +- [Use this package](#use-this-package) +- [Understand the implementation](#understand-the-implementation) +- [Further Exploration](#further-exploration) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) + +----- + + +## Use this package + +Image attachments work end to end: attach an image to a prompt or a command, and it is saved, shown in history, and sent to the model without any further action from you. In the default `dsh` composition everything is already wired; when you compose your own setup, one plugin enables the capability. + +### Attach images to a prompt + +Attach one or more images to a user prompt in the client UI. Each source is checked, normalized to a provider-independent 8-bit sRGB/sRGBA raster, and saved before your message is processed; if any image is refused, the whole message fails and nothing is published. Supported source formats are PNG, JPEG, WebP, and GIF; a deployment controls source limits separately from normalized-storage and route-specific request limits. The one plugin below enables durable image attachments (the shipped base composition already mounts it): + +```yaml +- name: '@deepseek-ai/dsh-attachment-local' +``` + +### Pass images to commands + +Commands that accept image input receive attached images the same way. If a command does not accept images, the harness refuses with an error message instead of silently dropping them. + +### Reuse images across the session + +Saved normalized images stay in conversation history and are projected into deterministic, route-sized request versions in later turns; after a restart, a resumed session shows and reuses the same images. When the current execution filesystem maps the stored host object, the request descriptor also carries a read-only process path that the model can inspect. When history or a request version is read back, the stored bytes are checked against what was recorded, so a missing, corrupted, or swapped image surfaces as an error rather than wrong bytes. + +### What can go wrong + +An image can be refused when you attach it — unsupported format, over the size, pixel, or dimension limits, or bytes that do not match their declared type — and the message then fails as a whole. Later, a history read can fail if the stored image was deleted or corrupted on disk. Failures carry stable codes so the client and protocol adapters can explain them in their own words. + +----- + + +## Understand the implementation + +
+Implementation internals — click to expand + +This section explains the design decisions behind the seam and the service operations that realize the user-visible behavior; observable behavior is fully covered in [Use this package](#use-this-package). + +### Design decisions + +- **Normalize and persist before event.** Every source is prepared and verified before the batch publishes in order, so the session log never references a partial or failed normalization. +- **Immutable and retention-neutral.** Objects are immutable once published; resumed and forked sessions may share them, so reference-aware garbage collection is deferred rather than tied to any one session's deletion. +- **Verify on read.** Reads check bytes and metadata against the logged reference before returning them, and request projections fully decode cached bytes, so a missing, corrupted, or swapped object fails closed. +- **Role-neutral image blocks.** The `ImageBlock` content block in `dsh-llm` carries an `ImageAttachmentRef`; provider adapters resolve it into deterministic request versions with explicit pixel and byte budgets, while execution filesystems may map the immutable host object to a model-readable process path. +- **Error routing by code.** `AttachmentError` re-implements the `HarnessError` shape instead of extending it because the base lives in `dsh-llm`, which depends on this package; consumers route on `code`, never on the prototype chain. + +### Service operations + +The service family runs one admission-and-storage flow: every entry point enforces source batch limits and canonical base64, prepares provider-independent normalized attachments before publishing any member, and commits them durably in input order without partial results. `readImageRequest` derives deterministic route-sized variants whose identity includes the attachment id, transform version, pixel and byte budgets, and encoder settings. The pure `requestImageDimensions` export computes each projection's aspect-preserving dimensions from a total-pixel budget, so providers and request pricing share one geometry. `imageHostPath` exposes an implementation-owned host location only to trusted same-process consumers that need execution-world mapping. Callers compose ordered batches while the implementation owns compression concurrency, caching, and singleflight. Reads and projections preserve caller cancellation. Failures carry stable machine-readable codes, and the caller-correctable admission subset is recognizable at runtime so each protocol adapter maps its own vocabulary; the exact per-operation contracts live in [`src/index.ts`](src/index.ts) and [`src/error.ts`](src/error.ts). + +### Source map + +| File | Role | +|---|---| +| [`src/index.ts`](src/index.ts) | Plugin entry: abstract `AttachmentStore` service and re-exports | +| [`src/types.ts`](src/types.ts) | Durable vocabulary: references, limits, upload and store payloads | +| [`src/admission.ts`](src/admission.ts) | `admitEncodedImages`: canonical-base64 enforcement, then `saveImages` delegation | +| [`src/error.ts`](src/error.ts) | `AttachmentError` class and the `isImageAdmissionError` runtime subset | +| [`src/brand.ts`](src/brand.ts) | `AttachmentId` branded opaque identifier | +| [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant; implementations enforce immutable-store checks) | + +
+ +----- + + +## Further Exploration + +For the full service contract and payload types, read the subsystem reference; for the storage that backs this capability, read the local backend. + +- [Attachment subsystem reference](../../../docs/subsystems/attachment.md) — service contract, payload types, and the `ctx.attachments` cordis surface. +- [Local filesystem backend](../attachment-local/README.md) — where your attached images are stored on this machine. +- [Capability seams](../../../docs/capability-seams.md) — how this capability family is split into roles. + +----- + + ## Model Experience -Indirectly, through the role-neutral core `ImageBlock` and provider adapters that resolve its durable reference into an exact request version. Request descriptors expose the complete attachment id and actual request dimensions. When the attachment backend exposes a host object and the current execution filesystem maps it, the descriptor also exposes the resulting read-only path; it states that normalization may have resized or re-encoded the upload. +Indirectly, through the provider adapter, which resolves each durable reference into an exact request version and sends its stable attachment id and actual dimensions beside the image. When the execution filesystem maps the stored object, the descriptor also includes a read-only process path and a matching extension for a writable copy. #### KV Cache effect -Adding an image changes the provider request and therefore invalidates the affected request suffix. A changed execution-world path can also change historical descriptor text without changing the deterministic request version. +Adding an image changes the provider request and therefore invalidates the affected request suffix. ## Known Limitations and Deferred Work -- Version one accepts PNG, JPEG, WebP, and GIF only. -- Retention and garbage collection are deferred because resumed and forked sessions may share immutable objects. -- Generic files, audio, video, and persistent unsent drafts require separate lifecycle and provider contracts. + + + +These limits describe what image attachments can and cannot do; they are current package constraints, not a task backlog. + +- **Raster images only** — PNG, JPEG, WebP, and GIF are accepted; generic files, audio, and video are not supported yet. +- **Images are never deleted** — stored images are retained indefinitely; nothing removes them automatically. +- **Unsent drafts are not saved** — a composer draft stays in the browser until you submit the message. + + +### Dev Note + +
+Working context for maintainers — click to expand + +This Dev Note is working context for maintainers: undecided directions and open questions. It is explicitly non-authoritative — shipped behavior and limits live in the sections above and the package code. + +#### Future: reference-aware garbage collection + +Resumed and forked sessions may share immutable objects, so any retention policy needs a reference model that accounts for session lineage before objects can be collected. No decision is recorded yet; the local backend currently retains everything. + +#### Future: non-image attachments and assistant-side output + +Generic files, audio, and video would need separate lifecycle and provider contracts, and the role-neutral `ImageBlock` leaves assistant-side image output as forward compatibility — current production adapters declare text-only output, so only user content carries images. Both directions are undecided. + +
diff --git a/packages/attachment/attachment/README.zh.md b/packages/attachment/attachment/README.zh.md index bc33293b34..1a1d297297 100644 --- a/packages/attachment/attachment/README.zh.md +++ b/packages/attachment/attachment/README.zh.md @@ -1,23 +1,134 @@ +--- +description: "持久图片附件,供用户与维护者在提示词与命令中附加、复用或排查图片。" +kind: "package-reference" +--- + # @deepseek-ai/dsh-attachment [English](README.md) | 中文 -持久附件服务边界。`ctx.attachments` 校验并持久提交提供方无关的规范化图片,随后返回可序列化的 `ImageAttachmentRef`;消费方绝不会在会话事件中持久保存浏览器路径、对象 URL、提供方 URL、本地存储路径或 base64。 +## 概述 -未发送的输入区图片仍是由浏览器持有的临时草稿。`validateImage` 运行完整准入策略但不执行持久化。`saveImages` 负责批次图片数量和总字节限制,在发布任何成员前准备全部规范化附件,然后按顺序提交,并且只在完整批次成功后返回引用。后续存储失败不会返回部分引用,但较早写入的不可变内容寻址对象可能保持不可达,直至具备按引用感知的垃圾回收。`AttachmentError.code` 使用封闭的 `AttachmentErrorCode` 字符串联合类型。其 `ImageAdmissionErrorCode` 子集标记可由调用方修正的图片输入失败;`isImageAdmissionError` 在运行时识别该子集,使每个协议适配器可以映射自己的错误词汇。`saveImage` 会在发布任何模型可见的会话事件前提交一张已接受的图片,并直接返回 `ImageAttachmentRef`。规范化过程缩小图片时,引用会通过 `originalDimensions` 记录应用方向后的输入尺寸。`readImage` 根据已记录的元数据校验规范化附件。`readImageRequest` 确定性派生路由所需的请求版本,其身份覆盖附件 ID、变换策略版本、像素和字节预算及编码参数。纯函数导出 `requestImageDimensions` 按总像素预算计算该投影的保持宽高比尺寸,使提供方与请求定价共享同一套几何计算。`imageHostPath` 可以给出提供方所持对象的绝对宿主路径,但不保证当前模型工具能够读取它。LLM 消费方在序列化请求时将这个位置与当前文件系统提供的执行环境映射组合起来。解析出的访问路径独立于请求版本及其 `variantId`。调用方通过 `Promise.all(refs.map(...))` 组合有序批次,本地实现仍通过实例级限流器、缓存和 singleflight 限制压缩并发。调用方可以取消读取和投影;实现保留取消结果,不把它转换为存储失败。 +你可以把图片附加到提示词和命令中,harness 会持久保存提供方无关的规范化版本:每张源图都会在你的消息被处理前准入并规范化,重新出现在对话历史中,并在同一会话的后续轮次投影到所选模型路由。随附的 `dsh` 组合无需任何配置即可支持这一点。已附加的图片在重启后依然存在,而浏览器路径、提供方 URL、本地存储路径与 base64 绝不会进入持久会话事件。只接受光栅格式(PNG、JPEG、WebP、GIF),未发送的输入区草稿在提交前仍留在浏览器中。已存储的图片永远不会被自动删除,通用文件、音频和视频暂不支持。 -`admitEncodedImages(attachments, images)` 是每个接受浏览器上传的 RPC 端点(会话 prompt 端点与命令执行器)共用的 wire 入口:它对每个成员强制执行规范 base64,随后把批量准入——限额、校验、有序提交——委托给 `saveImages`。base64 上传形式为 `EncodedImageAttachment`,从 `@deepseek-ai/dsh-attachment/types` 导出,供 wire 契约引用。 +## 目录 +- [使用本包](#use-this-package) +- [理解实现](#understand-the-implementation) +- [进一步探索](#further-exploration) +- [模型体验](#model-experience) +- [已知限制与延期工作](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) + +----- + + +## 使用本包 + +图片附件端到端可用:把图片附加到提示词或命令,它会自动保存、显示在历史中并发送给模型,无需你再做任何操作。在默认 `dsh` 组合中一切都已接好;自行组合时,一个插件即可启用该能力。 + +### 在提示词中附加图片 + +在客户端 UI 中向用户提示词附加一张或多张图片。每个源图都会在你的消息被处理前接受检查、规范化为提供方无关的 8-bit sRGB/sRGBA 光栅并保存;如果任何一张图片被拒绝,整条消息都会失败且不会发布任何内容。支持的源格式为 PNG、JPEG、WebP 与 GIF;部署方分别控制源图限制、规范化存储限制与路由专用请求限制。下面这一个插件即可启用持久图片附件(随附的 base 组合已经挂载它): + +```yaml +- name: '@deepseek-ai/dsh-attachment-local' +``` + +### 把图片传给命令 + +接受图片输入的命令以相同方式接收附加图片。如果某个命令不接受图片,harness 会以错误消息拒绝,而不是静默丢弃。 + +### 在整个会话中复用图片 + +已保存的规范化图片会保留在对话历史中,并在后续轮次投影为确定性的路由尺寸请求版本;重启后,恢复的会话会显示并复用相同的图片。当前执行文件系统可以映射已存宿主对象时,请求描述符还会携带模型可检查的只读进程路径。回读历史或请求版本时,已存储的字节会与记录的内容比对,因此缺失、损坏或被替换的图片会以错误形式呈现,而不是错误的字节。 + +### 可能出什么问题 + +附加图片时可能被拒绝——格式不受支持、超出大小、像素或尺寸限制,或者字节与声明类型不符——此时整条消息失败。之后,如果磁盘上的图片被删除或损坏,历史读取也可能失败。失败带有稳定错误码,客户端与协议适配器可以用自己的措辞解释它们。 + +----- + + +## 理解实现 + +
+实现细节——点击展开 + +本节解释 seam 背后的设计决策,以及实现用户可见行为的服务操作;可观察行为已在[使用本包](#use-this-package)中完整说明。 + +### 设计决策 + +- **事件前完成规范化与持久化。** 每个源图都会在批次按序发布前完成准备与校验,因此会话日志绝不会引用部分完成或规范化失败的对象。 +- **不可变且保留策略中立。** 对象一经发布即不可变;恢复和 fork 后的会话可能共享它们,因此引用感知的垃圾回收被推迟,而不是与任何单个会话的删除绑定。 +- **读取时校验。** 读取在返回前把字节和元数据与记录的引用比对,请求投影还会完整解码缓存字节,因此缺失、损坏或被替换的对象都会失败关闭。 +- **角色无关的图片块。** `dsh-llm` 中的 `ImageBlock` 内容块携带 `ImageAttachmentRef`;提供方适配器以显式像素与字节预算把引用解析为确定性请求版本,执行文件系统则可以把不可变宿主对象映射为模型可读的进程路径。 +- **按错误码路由。** `AttachmentError` 重新实现 `HarnessError` 的结构而不是继承它,因为基类位于 `dsh-llm`,而后者依赖本包;消费方按 `code` 路由,绝不依赖原型链。 + +### 服务操作 + +服务族运行同一条准入与存储流程:每个入口都强制执行源批次限制与规范 base64,在发布任何成员前准备提供方无关的规范化附件,再按输入顺序持久提交而不产生部分结果。`readImageRequest` 派生确定性的路由尺寸变体,其身份包含附件 id、变换版本、像素与字节预算及编码参数。纯函数导出 `requestImageDimensions` 会按总像素预算计算每个投影保持宽高比的尺寸,使提供方与请求定价共享同一套几何计算。`imageHostPath` 只向需要执行世界映射的受信任同进程消费方暴露实现拥有的宿主位置。调用方组合有序批次,而实现拥有压缩并发、缓存与 singleflight。读取和投影保留调用方的取消语义。失败带有稳定且机器可读的错误码,运行时即可识别可由调用方修正的准入子集,让每个协议适配器映射自己的词汇;各操作的确切约定见 [`src/index.ts`](src/index.ts) 与 [`src/error.ts`](src/error.ts)。 + +### 源码地图 + +| 文件 | 职责 | +|---|---| +| [`src/index.ts`](src/index.ts) | 插件入口:抽象 `AttachmentStore` 服务与再导出 | +| [`src/types.ts`](src/types.ts) | 持久词汇:引用、限额、上传与存储载荷 | +| [`src/admission.ts`](src/admission.ts) | `admitEncodedImages`:规范 base64 强制,随后委托 `saveImages` | +| [`src/error.ts`](src/error.ts) | `AttachmentError` 类与 `isImageAdmissionError` 运行时子集 | +| [`src/brand.ts`](src/brand.ts) | `AttachmentId` 带类型标记的不透明标识符 | +| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式;实现负责强制不可变存储检查) | + +
+ +----- + + +## 进一步探索 + +完整的服务约定与载荷类型请看子系统参考;支撑这一能力的存储请看本地后端。 + +- [附件子系统参考](../../../docs/subsystems/attachment.zh.md)——服务约定、载荷类型与 `ctx.attachments` 的 cordis 接口面。 +- [本地文件系统后端](../attachment-local/README.zh.md)——你的附加图片在本机上的存储位置。 +- [能力 seam](../../../docs/capability-seams.zh.md)——本能力家族如何拆分为多个角色。 + +----- + + ## 模型体验 -该包通过角色无关的核心 `ImageBlock`,以及把持久引用解析为确定请求版本的提供方适配器,间接影响模型。请求描述会公开完整附件 ID 和实际请求尺寸。附件后端给出宿主对象且当前执行文件系统能够映射该对象时,描述还会公开映射后的只读路径,并说明规范化过程可能缩小或重新编码上传图片。 +该包通过提供方适配器间接影响模型;适配器会把每个持久引用解析为确切请求版本,并在图片旁发送稳定附件 id 与实际尺寸。执行文件系统可以映射已存对象时,描述符还会包含只读进程路径,以及可写副本使用的匹配扩展名。 -#### KV 缓存影响 +#### KV Cache 影响 -添加图片会改变提供方请求,因此会使受影响的请求后缀失效。即使确定性的请求版本不变,执行环境路径变化也会改变历史描述文本。 +添加图片会改变提供方请求,因此会使受影响的请求后缀失效。 -## 已知限制与待完成工作 +## 已知限制与延期工作 -- 第一版仅接受 PNG、JPEG、WebP 和 GIF。 -- 保留策略与垃圾回收尚未实现,因为恢复和 fork 后的会话可能共享不可变对象。 -- 通用文件、音频、视频和持久的未发送草稿需要单独的生命周期与提供方契约。 + + + +这些限制描述了图片附件能做什么、不能做什么;它们是当前包约束,而非任务积压。 + +- **仅支持光栅图片**——接受 PNG、JPEG、WebP 与 GIF;通用文件、音频和视频暂不支持。 +- **图片永远不会被删除**——已存储的图片无限期保留;没有任何机制自动移除它们。 +- **未发送的草稿不会保存**——输入区草稿在提交消息前一直留在浏览器中。 + + +### 开发备注 + +
+维护者的工作上下文——点击展开 + +本开发备注是维护者的工作上下文:尚未决定的探索方向与开放问题。它明确不具权威性——已交付的行为与限制以上文和包代码为准。 + +#### 未来:引用感知的垃圾回收 + +恢复和 fork 后的会话可能共享不可变对象,因此任何保留策略都需要一个能考虑会话血缘的引用模型,之后才能回收对象。目前尚未记录任何决定;本地后端当前保留一切。 + +#### 未来:非图片附件与助手侧输出 + +通用文件、音频与视频需要单独的生命周期与提供方契约;角色无关的 `ImageBlock` 也把助手侧图片输出留作前瞻兼容——当前生产适配器声明只输出文本,因此只有用户内容携带图片。两个方向都尚未决定。 + +
diff --git a/packages/boot/README.i18n.yaml b/packages/boot/README.i18n.yaml index a56a10d02a..49f4b9ac97 100644 --- a/packages/boot/README.i18n.yaml +++ b/packages/boot/README.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 packages/boot/README.md -README.md: a6de8cdb4cecaf03cbadabd7c3f85bf2cedddf01 -README.zh.md: acadd2409f21a3c10d4a5dd2ce406d512c51e911 +README.md: 0d13420c8408e046bc538ebb06f5f7c0d23eb1b1 +README.zh.md: ebc0f82eb79fc310d0b944f4bb965e72391ead3f diff --git a/packages/boot/README.md b/packages/boot/README.md index a6de8cdb4c..0d13420c84 100644 --- a/packages/boot/README.md +++ b/packages/boot/README.md @@ -1,12 +1,39 @@ +--- +description: "The boot package group: how dsh app bins start — environment loading, profile and patch layers, clear startup failures, and app-owned command lines." +kind: "package-group" +--- + # boot/ — shared app-bin boot glue English | [中文](README.zh.md) -The channel-neutral boot library shared by `apps/cli` and test-only Loader fixtures. +## Summary + +The boot group provides what every dsh app bin needs to start: `app-boot` turns a `cordis.yml` plus your environment and patch layers into a running app with clear failure messages, and `cmdline` lets the app own its command-line flags and `--help`. With these packages you can run `dsh` or write a new application or test fixture that boots the same way. Both are libraries imported by `apps/cli` and test-only Loader fixtures, never plugins a composition loads. This page maps the group; each package README owns its per-package contract. + +## Table of Contents + +- [Packages](#packages) +- [Related documentation](#related-documentation) +- [Dev Note](#dev-note) + + +## Packages | Package | Role | ctx key | |---|---|---| -| `app-boot/` | Shared boot glue for the app bins: `.env` loading, fail-loud Loader guards, snapshot-aware config resolution, the settle-the-tree boot sequence | (library for the bins) | -| `cmdline/` | Launcher-to-app command-line handoff and app-owned startup parsing | `cmdlineArgs`, `appExit` | +| [`app-boot`](app-boot/README.md) | Boots a dsh app from a `cordis.yml`: loads `.env`, applies profile and patch layers, and reports startup failures clearly | (library for the bins) | +| [`cmdline`](cmdline/README.md) | Lets the app own its flags, `--help`, and exit code; passes everything after the launcher's flags through verbatim | `cmdlineArgs`, `appExit` | -The boot sequence and personal-config contract are documented in [`app-boot/README.md`](app-boot/README.md); app-owned command lines are documented in [`cmdline/README.md`](cmdline/README.md). + +## Related documentation + +- [dsh app](../../apps/cli/README.md) — the `dsh` bin that consumes these helpers for its boot sequence. +- [Profile bundles](../bundle/README.md) — installable patch layers that `dsh --profile` compositions mount. +- [dsh-home-paths](../util/home-paths/README.md) — the harness-home resolver both packages build on. +- [App-owned command-line decision](../../.agents/notes/implemented/architecture/2026-08-06-app-owned-command-line.md) — why an app owns its flag family instead of the launcher. + + +## Dev Note + +None. diff --git a/packages/boot/README.zh.md b/packages/boot/README.zh.md index acadd2409f..ebc0f82eb7 100644 --- a/packages/boot/README.zh.md +++ b/packages/boot/README.zh.md @@ -1,12 +1,39 @@ +--- +description: "boot 包组:dsh app bin 如何启动——环境加载、profile 与 patch 层、清晰的启动失败信息,以及由应用持有的命令行。" +kind: "package-group" +--- + # boot/:共享的 app bin 启动粘合层 [English](README.md) | 中文 -由 `apps/cli` 与仅限测试的 Loader fixture 共享、与渠道无关的启动库。 +## 概述 + +boot 组提供每个 dsh app bin 启动所需的全部能力:`app-boot` 把 `cordis.yml` 连同你的环境与 patch 层变成运行中的应用,并给出清晰的失败信息;`cmdline` 让应用持有自己的命令行 flag 与 `--help`。借助这些包,你可以运行 `dsh`,也可以编写以同样方式启动的新应用或测试 fixture。两者都是 `apps/cli` 与测试专用 Loader fixture 导入的库,绝不是组合加载的插件。本页是组的映射;各包 README 负责各自的包级约定。 + +## 目录 + +- [包](#packages) +- [相关文档](#related-documentation) +- [开发备注](#dev-note) + + +## 包 | 包 | 职责 | ctx 键 | |---|---|---| -| `app-boot/` | app bin 的共享启动粘合层:加载 `.env`、会明确报错的 Loader 保护机制、感知快照的配置解析,以及等待整棵树停稳的启动序列 | (供各 bin 使用的库) | -| `cmdline/` | 启动器到应用的命令行交接,以及由应用持有的启动解析 | `cmdlineArgs`、`appExit` | +| [`app-boot`](app-boot/README.zh.md) | 从 `cordis.yml` 启动 dsh 应用:加载 `.env`、应用 profile 与 patch 层,并清晰报告启动失败 | (供各 bin 使用的库) | +| [`cmdline`](cmdline/README.zh.md) | 让应用持有自己的 flag、`--help` 与退出码;启动器自身 flag 之后的一切原样传入 | `cmdlineArgs`、`appExit` | -启动序列与个人配置约定见 [`app-boot/README.md`](app-boot/README.zh.md);由应用持有的命令行见 [`cmdline/README.md`](cmdline/README.zh.md)。 + +## 相关文档 + +- [dsh 应用](../../apps/cli/README.zh.md)——在其启动序列中使用这些 helper 的 `dsh` bin。 +- [Profile 组合包](../bundle/README.zh.md)——可由 `dsh --profile` 组合挂载的可安装 patch 层。 +- [dsh-home-paths](../util/home-paths/README.zh.md)——两个包都依赖的 harness home 解析器。 +- [应用持有命令行决策](../../.agents/notes/implemented/architecture/2026-08-06-app-owned-command-line.zh.md)——为什么 flag 家族由应用持有而非启动器。 + + +## 开发备注 + +无。 diff --git a/packages/boot/app-boot/README.i18n.yaml b/packages/boot/app-boot/README.i18n.yaml index 7f7c3badcf..5c976b9148 100644 --- a/packages/boot/app-boot/README.i18n.yaml +++ b/packages/boot/app-boot/README.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 packages/boot/app-boot/README.md -README.md: a7c6272fd96fdf24b4f21bb8b60c087af4a4dc96 -README.zh.md: 09a63765414ba76d78a7c88f3777f63eecd835f7 +README.md: 20c93552113f39c349615ef56fdd9ee7f814c5bc +README.zh.md: a6cb2401c73d0dfeb68250c9cde338f807af7edd diff --git a/packages/boot/app-boot/README.md b/packages/boot/app-boot/README.md index a7c6272fd9..20c9355211 100644 --- a/packages/boot/app-boot/README.md +++ b/packages/boot/app-boot/README.md @@ -1,60 +1,153 @@ -# `@deepseek-ai/dsh-app-boot` +--- +description: "Shared Loader boot support for dsh profiles and the temporary Python SDK runtime: environment layers, patches, diagnostics, and configuration preview." +kind: "package-library" +--- + +# @deepseek-ai/dsh-app-boot English | [中文](README.zh.md) -Shared Loader boot glue for [`dsh`](../../../apps/cli/README.md) profiles, including the CLI packaged by the [Python runtime wheel](../../../python/README.md). The product launcher owns profile composition and process lifecycle. Direct-config helpers serve lower-level embedders and tests; they do not define another supported application entrypoint. +## Summary -| Export | Role | +`dsh-app-boot` is the shared Loader boot library behind `dsh` profiles, including the CLI packaged by the Python runtime wheel. It loads environment layers, composes profile bundles and patches, boots every plugin, and returns the running app or identifies the failed plugin and cause. Product applications use the `dsh` launcher instead of publishing separate bins; direct-config helpers remain only for lower-level embedders and tests. You can preview the effective configuration before booting, select live or startup-only patch application per profile, and let a terminal-owning app restore its terminal before a fatal exit. + +## Table of Contents + +- [Use this package](#use-this-package) +- [Understand the implementation](#understand-the-implementation) +- [Further Exploration](#further-exploration) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) + +----- + + +## Use this package + +Starting an app with this package is a small, explicit entry point: you give it a config file and it runs the whole boot. This section covers what you can do and what you get; the helper calls behind each outcome are documented in the folded implementation section. + +### When to use it + +Use it when implementing the shared `dsh` launcher or embedding its lower-level boot helpers. Product features belong in profile bundles instead of new application bins; code that only adds plugins to an already-running app mounts those plugins directly. + +### Starting the app + +You give your entry point a config file, and the process starts the whole app: it loads your environment layers, applies patches and profiles, boots every plugin, and returns once the app is running. In replay mode it boots the sibling `cordis.snapshot.yml` instead, so a recorded session reproduces identically. The smallest entry point is two calls: + +```text +installFailLoud('dsh') +const ctx = await boot('dsh', resolveConfigPath(argv[2], process.env.DSH_SNAPSHOT)) +``` + +With that entry point, success looks like a running app with every plugin active; failure is never silent — one labelled line names the failing plugin and the stage, and the process exits nonzero. The app context is torn down before the error is reported, so nothing keeps running half-started. + + +### Profiles + +A profile is how one dsh installation ships different app surfaces: `web`, `headless`, `acp`, `sdk`, and `sdk-minimal` start distinct compositions from the same launcher. A profile lives at `$DSH_HOME/profiles/` and combines installable bundles, its own `cordis.patch.yml`, and `patchReload: live | startup`; omitted reload policy keeps the historical `live` default for custom profiles. The shipped `web` template uses live reload, while the other shipped templates apply patches only at startup. `sdk-minimal` names only its standalone bundle; the other templates retain base-plus-mode stacks. `dsh plugin` creates custom profiles, and a missing bundle or one without a patch declaration fails startup loudly. + +Your machine-local preferences also live in the Harness home: + +- **`.env`** — your ordinary environment layers: the invoking directory's file outranks the Harness-home file, and both sit below the inherited environment. Variables that decide how the process starts (`PATH`, proxies, `DSH_*`, `XDG_*` and similar) are rejected from files: export them instead. For a non-product bin that just wants one directory's `.env`, a missing file is fine and an unloadable one prints one labelled warning line. +- **`cordis.patch.yml`** — your tweak layer, applied after every bundle layer (per-profile first, then the home-level file, which therefore outranks it): replace one entry's whole config (restating the fields you keep), insert new entries, or interpolate `!!js` expressions at boot. A patch naming an entry that does not exist prints a stderr warning; an empty or comments-only file fails boot — disable the layer with `[]` instead. + +Profiles with `patchReload: live` watch both user patch files: a valid edit recomposes without restart, while a rejected edit leaves the last good app running. A `startup` profile installs neither those watchers nor the launcher's watch-only HMR fallback. + +### Previewing the effective configuration + +Before you boot, you can print the exact configuration the app will mount: the dump shows the composed entry list with `!!js` expressions verbatim, grouped under comments naming each source file and the patch layers that changed it, as one loadable YAML document. Patches that match no row are reported with their layer label; a missing, unparsable, or invalid config fails the dump. + +### What you see when startup fails + +Startup failure is a single labelled line plus a nonzero exit — never a silent hang or a raw stack dump. The message names the failing plugin; a plugin that threw keeps its original error, and an entry that never started is reported with the services it was waiting for. + +If your app owns the terminal, it can hand the terminal back before the process exits, so your shell is never left in raw mode. The handoff is bounded: a stuck cleanup delays the fatal exit but never cancels it. + +### Telling the agent where the harness lives + +When your app boots a model-backed agent, you can tell the agent where the DSH implementation checkout lives: it learns that path and that it must not infer the working directory from it — it should use `pwd`. The instruction appears once near the top of the system prompt. Apps without a system prompt service skip it; in development, reloading the system prompt drops it until the next boot. + +----- + + +## Understand the implementation + +
+Implementation internals — click to expand + +This section explains how the outcomes above are realized and points at the code that realizes them; everything here is developer-facing and not needed to use the package. + +### Design notes + +- **Channel-neutral library.** The package carries no loader hooks and no dev-mode surface; the [`dsh` app](../../../apps/cli/README.md) owns its Node source-launch hook and consumes these helpers for the boot sequence, and built consumers use plain Node package resolution. +- **Two Loader builtins.** `mountRootInclude` registers `cordis:include` and `cordis:group` as Loader builtins: a group row gives one `isolate` realm to a provider and its consumers together, and an agent preset outside this workspace cannot resolve `@deepseek-ai/cordis-plugin-group` by name. Both load through the ambient module pipeline rather than the included tree's own specifier resolution. +- **Profile module fallback.** Bare plugin specifiers resolve through the Loader from the config directory. Plain Node maintains one symlink per package in the installation dependency closure. A packaged executable instead reads each installed export map with Node ESM conditions and writes real proxy packages that re-export virtual module URLs, because an operating-system symlink cannot enter pkg's `/snapshot` tree. Missing exports stay unavailable, malformed maps fail startup, and a cross-process writer lock replaces stale entries without exposing partial proxies. +- **One rejection checkpoint.** `assertEntriesActivated` keeps the exact reasons it folds into the boot diagnostic visible through the next process rejection checkpoint, so `installFailLoud` coalesces Loader's duplicate notification while unrelated unhandled rejections remain fatal. +- **Two-stage failure labels.** `boot()` distinguishes `host preparation failed` — `prepare` threw before any config-tree entry mounted — from `plugin tree failed to load`, and appends the deepest plugin error's stack so the startup diagnostic preserves the original activation error instead of only the wrap chain. + +### Helper behavior + +The exports each own one stage of the boot: config resolution and snapshot replay, layered environment loading, fail-loud reporting, activation auditing, patch parsing, root-include mounting, config dump rendering, live patch watching, profile composition, and the harness-source section. Per-export contracts live in the code, not this README — see [`src/index.ts`](src/index.ts) and [`src/profile.ts`](src/profile.ts). + +### Source map + +| File | Role | |---|---| -| `resolveConfigPath(path, snapshotMode, cwd?)` | Absolute config path; `snapshotMode === 'replay'` swaps a `cordis.yml`/`.yaml` basename for its sibling `cordis.snapshot.yml` | -| `loadEnv(binName, dir?, warn?)` | Load the gitignored `.env` (Node `process.loadEnvFile`); absent file is fine, an unloadable one warns a single labelled line (default: stderr) | -| `loadLayeredEnv(binName, cwd?, warn?)` | Build the product CLI's frozen inherited > project `.env` > user `.env` snapshot, reject bootstrap-only file variables, and materialize accepted file values without replacing inherited ones | -| `installFailLoud(binName, proc?, release?)` | Turn an unhandled boot or later Loader rejection into one labelled stderr line + `exit(1)`; the optional `release` teardown is awaited between the two (bounded by `FAIL_LOUD_RELEASE_TIMEOUT_MS`) so a terminal-owning surface restores the terminal before exit; returns the uninstaller | -| `FAIL_LOUD_RELEASE_TIMEOUT_MS` | How long `installFailLoud` waits for its `release` hook; a wedged disposer delays the fatal exit, never cancels it | -| `assertEntriesLoaded(ctx, binName)` | Throw when a settled tree holds an enabled entry with no fiber, reporting every unresolved plugin name as a Cordis startup failure | -| `assertEntriesActivated(ctx, binName)` | Include the `assertEntriesLoaded` check, then await every enabled entry after the Loader settles; throw with each failed plugin's original stack or each pending plugin's unresolved services | -| `loadOptionalPatches(binName, file)` | Parse an optional patch-list file (a profile's `cordis.patch.yml`) — a top-level YAML array of include `PatchOptions` (id-targeted config overrides, `insert` lists, `!!js` allowed); absent file → `undefined`, an unreadable/unparsable/non-array file throws | -| `loadOverlayPatches(binName, file)` | Parse a required top-level YAML array containing the same include `PatchOptions` entries described above; relative plugin names in inserted rows resolve beside this file, while a patch `name` used to assert an existing row stays literal; a missing file also throws because the caller named it | -| `mountRootInclude(ctx, absoluteConfigPath, patches?, bareModuleBaseUrl?)` | Register the statically imported `cordis:include` and `cordis:group` builtins, mount the include, and retain the exact root entry used by user patch-layer HMR; an optional module base anchors bare package names to the installed host while relative names stay config-relative | -| `watchUserPatches(ctx, options)` | Register the named patch file with the existing Cordis HMR service; each add/change/removal transactionally recomposes the full patch list through the caller's `compose` closure (app-owned layers around the current user layer) and returns an async disposer | -| `resolveProfileDir` / `initProfile` / `loadProfile` / `readProfileManifest` / `writeProfileManifest` / `resolveBundleDir` / `composeEntries` / `healProfilesModuleFallback` / `PROFILE_TEMPLATES` / `DEFAULT_PROFILE_BUNDLES` / `DEFAULT_PROFILE_PATCH_RELOAD` / `PROFILES_DIR` / `PROFILE_PATCH_FILENAME` | Profile machinery and patch-file lifecycle (see [Profiles](#profiles)) | -| `boot(binName, absoluteConfigPath, patches?, prepare?, bareModuleBaseUrl?)` | Create the root context, expose `dshHomePath(...segments)` to Loader `!!js` config expressions, install Loader, run optional host preparation before config-tree entries mount (`prepare` may use Loader and provide launcher-owned context slots), then mount and await the include tree, assert entries loaded and activated, and return the root context — or dispose the partial context and reject a labelled error; the optional module base has the same resolution semantics as `mountRootInclude` | -| `renderConfigDump(binName, absoluteConfigPath, layers, warn?)` | Compose the base config and labeled overlay layers offline with the include's own parser and patch algorithm (`entryListSchema`/`applyEntryPatches`), so the result equals what `boot()` mounts, and render YAML with `!!js` expressions verbatim; each run of rows that shares one source file and the same patch layers is preceded by a `# ==` comment naming that file and those layers, keeping the output one loadable document; a patch matching no row goes to `warn` with its layer label (default: one stderr line), and read, parse, or field validation failures throw | -| `addHarnessSourceSection(ctx, sourceRoot)` | Add a global `harness:source` prompt section (ordered just after the harness identity, before the persona) telling the agent the on-disk path to the DSH implementation checkout while warning it not to infer the current working directory from that path and to use `pwd` instead; a no-op returning `undefined` when the booted tree has no `systemPrompt` service. The section is registered against that service's fiber, so a dev HMR reload of the system prompt drops it until the next boot | -| `HARNESS_SOURCE_SECTION` | The `'harness:source'` section name `addHarnessSourceSection` registers under | +| [`src/index.ts`](src/index.ts) | Boot helpers: config resolution, environment loading, fail-loud guard, activation audit, patch parsing, config dump, harness-source section | +| [`src/profile.ts`](src/profile.ts) | Profile discovery, initialization, bundle resolution, module fallback | +| [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant; boundary and replay tests cover the protocol mapping) | -Loader settlement rejects import and lifecycle failures with the failing entry and stage; `boot()` disposes the partial context and wraps that failure with the bin name. Entries settlement leaves behind are audited separately: `assertEntriesLoaded` turns an enabled fiber-less entry into a rejection naming every unresolved plugin, and `assertEntriesActivated` awaits each failed fiber to include its original stack in the startup rejection and names each pending entry's unresolved services. Before throwing, the audit marks those exact rejection reasons through one process checkpoint so `installFailLoud` coalesces Loader's duplicate notification while every unrelated unhandled rejection remains fatal. +
-The Loader mounts entries concurrently, so a surface can already own the terminal when something else fails: exiting without the tree's own teardown would leave raw mode, bracketed paste, and the keyboard protocol set on the user's shell, and an in-flight terminal query's reply would land as literal text at the next prompt. A config-tree failure settles through `boot()`, whose disposal of the partial context runs the surface's own shutdown before the labelled rejection. For the rejections `boot()` cannot see — a plugin's detached async work rejecting during or after mounting — a terminal-owning bin passes `release` to dispose the tree before the exit commits; `dsh` captures the root context in `boot()`'s `prepare` hook rather than from its return value so the hook covers the whole mounting window. While a release is in flight the handler stays installed and latched: the first rejection is the reported one, and later rejections (teardown's own included) are swallowed rather than becoming uncaught and killing the process mid-teardown. +----- -`cordis:group` is registered beside `cordis:include` so a composition can give one `isolate` realm to a provider and its consumers together. Both load through the ambient module pipeline rather than the included tree's own specifier resolution, which is what lets a composition outside this workspace — an agent preset under the Harness home — use a group row at all. + +## Further Exploration -Bare plugin specifiers in a config (`@deepseek-ai/dsh-*`, npm packages) resolve through the Cordis Loader's internal module loader. They resolve from the config directory by default; a closed runtime passes `bareModuleBaseUrl` to `boot` or `mountRootInclude` so its installed package tree remains authoritative even when the config lives inside another Node project. Relative specifiers always resolve against the config directory. Repository bins install Loader's optional `node-addon-require-builtin` peer; external callers must supply it or install plugins where plain Node import resolution can find them. The built `dsh-app-boot` artifact embeds the statically mounted Include implementation while leaving Loader external, so the include tree and host bind to one Loader peer. The `pnpm dsh` source path additionally maps manifest-declared workspace packages to their TypeScript source; its configuration gate requires every shipped raw/Web bare plugin to appear in the resolver manifest's `dependencies`. +Read these pages when the package-level contract is not enough. They move from the shared boot mechanics to the composition model and the decision evidence behind it. -This package carries no loader hooks and no dev-mode surface. The [`dsh` app](../../../apps/cli/README.md) owns its Node source-launch hook and consumes these helpers for the boot sequence; built consumers continue to use plain Node package resolution. +- [Cordis primer](../../../docs/cordis-primer.md) — Loader, `!!js` config expressions, and include/group semantics. +- [dsh app](../../../apps/cli/README.md) — the `dsh` bin that consumes these helpers. +- [dsh-cmdline](../cmdline/README.md) — the launcher-to-app command-line handoff the bins use. +- [Profile bundles](../../bundle/README.md) — installable patch layers composed into `dsh --profile`. +- [dsh-home-paths](../../util/home-paths/README.md) — the Harness-home resolver (`resolveDshHome`). +- [Configuration source ownership](../../../.agents/notes/implemented/architecture/2026-08-04-configuration-source-ownership.md) — why a discovered file may not decide bootstrap behavior. +- [Profile plugin bundles](../../../.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.md) — the profile and bundle composition design. -## Profiles - -A profile is a directory under `$DSH_HOME/profiles/` (the Harness home resolves through [`resolveDshHome`](../../util/home-paths/README.md): `$DSH_HOME`, else `~/.dsh`) holding a `package.json` — out-of-tree plugin `dependencies` plus the profile manifest `dsh.profile` with its ordered `bundles` layer list and `patchReload: live | startup` — and the user's own `cordis.patch.yml`. `live` watches the profile and home-level patch files after boot; `startup` applies every layer once. A missing value keeps the historical `live` default for custom profiles. A bundle is an npm package whose manifest declares `"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }`; `loadProfile` resolves each `dsh.profile.bundles` name two-anchored (the dsh installation first, then the profile directory) and fails loud on a listed package without a bundle declaration. `composeEntries` applies patch layers over an empty entry list through the include's own `applyEntryPatches`, so composition, flag derivation, and config dumps cannot drift from what boots. `healProfilesModuleFallback` maintains the flat `$DSH_HOME/profiles/node_modules` directory. Plain Node writes one symlink per package in the installation dependency closure; a pkg executable resolves available explicit exports directly from each installed manifest with Node ESM import conditions and writes real proxy packages that re-export virtual module URLs, because an operating-system symlink cannot enter pkg's `/snapshot` tree. Export targets absent from an installed package remain unavailable without blocking its other exports; malformed export maps fail startup. An executable-only or declaration-only package with no module entry produces no proxy. A complete matching generation returns without acquiring the writer lock. A missing or stale entry acquires the cross-process lock, rechecks the full generation, and repairs it without exposing partial proxies; either carrier replaces the other carrier's managed entry. Both forms let profile plugins resolve installation packages through Node's ordinary parent walk and preserve one module instance for external plugin peers. `PROFILE_TEMPLATES` auto-initializes `web` with live reload and `headless`/`sdk`/`sdk-minimal`/`acp` with startup-only patches; `sdk-minimal` lists only its standalone bundle, while the other templates retain their base-plus-mode stacks. Other names fail loud until `initProfile` creates them through `dsh plugin`. `loadProfile` normalizes an exact installation-owned bundle tuple and a missing reload choice to its shipped template while preserving every explicit reload choice and every other manifest field; any extra, missing, or reordered bundle makes the list user-owned and leaves it unchanged. - -User-level machine-local preferences also live in the Harness home: - -- **`.env`** — the product CLI's ordinary environment layers: the invoking directory's file outranks the Harness-home file, and both sit below the inherited environment. `loadLayeredEnv` snapshots each value's source, rejects [bootstrap-only file variables](../../../.agents/notes/implemented/architecture/2026-08-04-configuration-source-ownership.md#decision) case-insensitively, and materializes accepted values into `process.env` for Loader expressions and third-party libraries. Managed credentials live separately in [`.credentials.yaml`](../../credentials/credentials-local/README.md); a credential left in either `.env` remains a lower-priority fallback. -- **`cordis.patch.yml`** (home level) and **`profiles//cordis.patch.yml`** — the user patch layers, applied after every bundle layer (per-profile first, then the home-level file, which therefore outranks it): an id-targeted patch replaces the named entry's whole `config` (restate unchanged fields), `insert` adds entries, and `!!js` expressions interpolate at mount. A patch naming an entry id absent from the composed tree is a stderr warning. An empty or comments-only file throws (it parses to nothing, not to a list); disable the layer with `[]`. - -Every `patchReload: live` profile keeps both user patch files live through `watchUserPatches`. The watcher targets the exact path even when the file or immediate parent does not exist, serializes bursts, and recomposes the user patches inside the caller's layer order (bundle layers below, overlays above). A rejected read, parse, or Loader candidate leaves the last good tree running and the HMR service broadcasts `hmr/config-update-failed(filename, Error)` after logging it; observer failures are contained. Disposing the context closes the watcher and drains an active refresh. A `startup` profile installs neither these watchers nor the launcher's watch-only HMR fallback. +----- + ## Model Experience -Indirectly, through the plugin tree it loads, which determines the prompts, schemas, messages, and model adapter in the resulting application; the one export that contributes model-visible text, `addHarnessSourceSection`, does so only when a consumer calls it after boot. +Indirectly, through the loaded plugin tree, which alone contributes model context; the one export that adds model-visible text, `addHarnessSourceSection`, does so only when a consumer calls it after boot. #### KV Cache effect -No direct invalidation from `boot()`; a consumer that calls `addHarnessSourceSection` places one short line near the system prompt's head, before per-request content, so it does not invalidate the cache across turns, and any other request-prefix change is owned by the named consumer. +Boot itself invalidates nothing in the request prefix. A consumer that calls `addHarnessSourceSection` places one short line near the system prompt's head, before per-request content, so it does not invalidate the cache across turns; any other request-prefix change is owned by the named consumer. ## Known Limitations and Deferred Work + + + +These limits describe when this boot library is a poor fit or needs special care. They are current package constraints, not a task backlog. + - **Bare package specifiers depend on Loader internals** — production bins need Loader's optional native helper; an in-process caller without it must use resolvable relative/file specifiers or provide its own module-resolution hook. - **Snapshot replay swapping is basename-specific** — only a config ending in `cordis.yml` or `cordis.yaml` maps to the sibling `cordis.snapshot.yml`; custom config names require caller-managed selection. - **Environment discovery is launch-scoped** — `loadLayeredEnv` reads only the invocation directory and Harness home once; it does not search parents or follow a workspace selected later. `loadEnv` remains the one-directory helper for non-product bins. - **A user patch replaces the whole matched config** — an id-targeted patch does not deep-merge, so a profile override restates the bundle fields it keeps. + + +### Dev Note + +
+Working context for maintainers — click to expand + +This Dev Note is working context for maintainers: open design questions and directions that are not decided. It is explicitly non-authoritative — shipped behavior, limits, and accepted rationale live in the sections above, the package code, and the linked Agent Notes. + +#### Open: config dump stability + +`renderConfigDump` output is a loadable YAML document whose `# ==` provenance comments and `!!js`-verbatim rendering serve the `--dump-config` diagnostic. Nothing promises byte stability across package versions; decide whether the dump becomes a serialization contract before anything consumes it programmatically. + +
diff --git a/packages/boot/app-boot/README.zh.md b/packages/boot/app-boot/README.zh.md index 09a6376541..a6cb2401c7 100644 --- a/packages/boot/app-boot/README.zh.md +++ b/packages/boot/app-boot/README.zh.md @@ -1,60 +1,153 @@ -# `@deepseek-ai/dsh-app-boot` +--- +description: "dsh profile 与临时 Python SDK 运行时的共享 Loader 启动支持:环境层、patch、诊断与配置预览。" +kind: "package-library" +--- + +# @deepseek-ai/dsh-app-boot [English](README.md) | 中文 -供 [`dsh`](../../../apps/cli/README.zh.md) profile 共用的 Loader 启动粘合层,也用于 [Python 运行时 wheel](../../../python/README.zh.md)打包的 CLI。产品启动器负责 profile 组合与进程生命周期。直接配置 helper 服务于底层 embedder 与测试,不会定义另一个受支持的应用入口。 +## 概述 -| 导出 | 职责 | +`dsh-app-boot` 是 `dsh` profile(包括 Python 运行时 wheel 所打包的 CLI)背后的共享 Loader 启动库。它加载环境层、组合 profile bundle 与 patch、启动每个插件,再返回运行中的应用,或指出失败插件与原因。产品应用使用 `dsh` launcher 而不发布单独 bin;直接配置 helper 只保留给低层嵌入方与测试。你还可以在启动前预览生效配置,按 profile 选择实时或仅启动时应用 patch,并让持有终端的应用在致命退出前恢复终端。 + +## 目录 + +- [使用本包](#use-this-package) +- [理解实现](#understand-the-implementation) +- [进一步探索](#further-exploration) +- [模型体验](#model-experience) +- [已知限制与延期工作](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) + +----- + + +## 使用本包 + +用此包启动应用是一个小而显式的入口:你给它一个配置文件,它运行整个启动过程。本节说明你能做什么、能得到什么;每个结果背后的 helper 调用记录在下方可折叠的实现章节中。 + +### 何时使用 + +在实现共享 `dsh` launcher 或嵌入其低层启动 helper 时使用它。产品功能应放入 profile bundle,而不是新增应用 bin;只向已运行应用添加插件的代码直接挂载插件即可。 + +### 启动应用 + +你把配置文件交给入口,进程就会启动整个应用:加载环境层、应用 patch 与 profile、启动每个插件,并在应用运行后返回。在回放模式下,它会启动同级的 `cordis.snapshot.yml` 替代文件,使已记录的会话能够原样复现。最小的入口只需两次调用: + +```text +installFailLoud('dsh') +const ctx = await boot('dsh', resolveConfigPath(argv[2], process.env.DSH_SNAPSHOT)) +``` + +有了这个入口,成功就是每个插件都已激活的运行中应用;失败绝不会悄无声息——一行带标签的信息点名失败的插件与阶段,进程以非零码退出。错误上报前会先拆卸应用上下文,因此不会留下半启动的残留。 + + +### Profile + +profile 是同一套 dsh 安装提供不同应用界面的方式:`web`、`headless`、`acp`、`sdk` 与 `sdk-minimal` 从同一 launcher 启动不同组合。profile 位于 `$DSH_HOME/profiles/`,由可安装 bundle、自身 `cordis.patch.yml` 与 `patchReload: live | startup` 组成;自定义 profile 省略 reload 策略时保留历史 `live` 默认值。随产品交付的 `web` 模板实时重载,其他随附模板只在启动时应用 patch。`sdk-minimal` 只列出自身的独立 bundle,其他模板保留 base 加模式 bundle 的栈。`dsh plugin` 创建自定义 profile;缺失 bundle 或未声明 patch 的 bundle 会让启动明确失败。 + +你的机器本地偏好同样位于 harness home 中: + +- **`.env`**——你的普通环境层:调用目录的文件优先于 harness home 的文件,两者都低于继承环境。决定进程如何启动的变量(`PATH`、代理、`DSH_*`、`XDG_*` 等)会被文件拒绝:请改为导出。对于只想加载某个目录 `.env` 的非产品 bin,文件缺失不影响启动,文件无法加载时输出一行带标签的警告。 +- **`cordis.patch.yml`**——你的 tweak 层,应用在所有组合包层之后(先应用逐 profile 的文件,再应用 home 级文件,因此后者优先级更高):替换某个条目的整个 config(重述你要保留的字段)、插入新条目,或在启动时插值 `!!js` 表达式。patch 指定的条目不存在时输出 stderr 警告;空文件或仅含注释的文件会导致启动失败——如需禁用该层,请改用 `[]`。 + +带 `patchReload: live` 的 profile 会监视两份用户 patch 文件:有效编辑无需重启即可重新组合,被拒绝的编辑则让最后一个可用应用继续运行。`startup` profile 既不安装这些监视器,也不安装 launcher 的仅监视 HMR 回退。 + +### 预览生效配置 + +启动前,你可以打印应用将挂载的确切配置:dump 会以 `!!js` 表达式原样展示组合后的条目列表,并按注释分组标明每个源文件及其 patch 层,输出是一份可加载的 YAML 文档。未匹配到任何行的 patch 会连同其层标签一起报告;配置缺失、无法解析或字段无效都会使 dump 失败。 + +### 启动失败时你会看到什么 + +启动失败是一行带标签的信息加非零退出码——绝不是静默卡死或原始堆栈倾倒。信息会点名失败的插件;抛错的插件保留原始错误,从未启动的条目会连同它等待的服务一起报告。 + +如果你的应用持有终端,它可以在进程退出前把终端交还,你的 shell 绝不会残留在 raw 模式。交还过程有界:卡住的清理只会延迟致命退出,而不会取消它。 + +### 告诉 agent harness 所在位置 + +当你的应用启动模型驱动的 agent 时,你可以告诉 agent DSH 实现代码 checkout 的位置:它得知该路径,也知道不得据此推断工作目录——它应使用 `pwd`。这条指示在系统提示词靠前位置出现一次。没有系统提示词服务的应用会跳过;开发环境中,重新加载系统提示词后它会消失,直至下次启动。 + +----- + + +## 理解实现 + +
+实现细节——点击展开 + +本节解释上述结果如何实现,并指出实现它们的代码位置;这里的内容面向开发者,使用本包并不需要。 + +### 设计说明 + +- **与渠道无关的库。** 此包不包含 loader 钩子,也不提供开发模式接口;[`dsh` 应用](../../../apps/cli/README.zh.md) 持有自己的 Node 源码启动钩子,并在启动序列中使用这些 helper,构建后的消费方则使用普通 Node 包解析。 +- **两个 Loader builtin。** `mountRootInclude` 把 `cordis:include` 与 `cordis:group` 注册为 Loader builtin:group 行能把一个提供方与它的消费方放进同一个 `isolate` realm,而位于本工作区之外的 agent preset 无法按名称解析 `@deepseek-ai/cordis-plugin-group`。两者都通过宿主的模块管线加载,而非被包含树自身的说明符解析。 +- **Profile 模块后备机制。** 裸插件 specifier 由 Loader 从配置目录解析。普通 Node 会为安装依赖闭包中的每个包维护一个符号链接。打包可执行文件无法让操作系统符号链接进入 pkg 的 `/snapshot` 树,因此会按 Node ESM 条件读取已安装包的 export map,并写入重新导出虚拟模块 URL 的真实代理包。缺失 export 保持不可用,错误 export map 会让启动失败,跨进程 writer lock 则会在不暴露部分代理的情况下替换陈旧条目。 +- **单一 rejection 检查点。** `assertEntriesActivated` 把折入启动诊断的确切原因保持到下一个进程级 rejection 检查点可见,使 `installFailLoud` 能合并 Loader 的重复通知,而所有无关的未处理 rejection 仍然致命。 +- **两阶段失败标签。** `boot()` 区分 `host preparation failed`(`prepare` 在任何配置树条目挂载前抛出)与 `plugin tree failed to load`(此后的一切失败),并追加最深层插件错误的堆栈,使启动诊断保留原始激活错误,而不只是包装链。 + +### Helper 行为 + +每个导出各负责启动的一个阶段:配置解析与快照回放、分层环境加载、明确报错的保护机制、激活审计、patch 解析、根 include 挂载、配置 dump 渲染、活动 patch 监视、profile 组合,以及 harness 源码段落。各导出的约定在代码中,不在本 README——见 [`src/index.ts`](src/index.ts) 与 [`src/profile.ts`](src/profile.ts)。 + +### 源码地图 + +| 文件 | 职责 | |---|---| -| `resolveConfigPath(path, snapshotMode, cwd?)` | 生成绝对配置路径;当 `snapshotMode === 'replay'` 时,把 basename 为 `cordis.yml`/`.yaml` 的文件替换为同级 `cordis.snapshot.yml` | -| `loadEnv(binName, dir?, warn?)` | 加载已被 git 忽略的 `.env`(Node `process.loadEnvFile`);文件不存在不影响启动,文件无法加载时输出一行带标签的警告(默认写入 stderr) | -| `loadLayeredEnv(binName, cwd?, warn?)` | 构建产品 CLI(命令行界面)冻结的「继承环境 > 项目 `.env` > 用户 `.env`」快照,拒绝文件中的 bootstrap-only 变量,并在不替换继承值的前提下物化其余文件值 | -| `installFailLoud(binName, proc?, release?)` | 将启动期或后续未处理的 Loader 拒绝转换为一行带标签的 stderr 消息并执行 `exit(1)`;两者之间会等待可选的 `release` 清理钩子(以 `FAIL_LOUD_RELEASE_TIMEOUT_MS` 为上限),使持有终端的界面能在退出前恢复终端;返回卸载函数 | -| `FAIL_LOUD_RELEASE_TIMEOUT_MS` | `installFailLoud` 等待其 `release` 回调的时长;卡死的 disposer 只会延迟致命退出,而不会取消它 | -| `assertEntriesLoaded(ctx, binName)` | 树结算后,如果其中存在已启用但没有 fiber 的条目,则抛出异常,并以 Cordis 启动故障的形式报告每个未解析插件的名称 | -| `assertEntriesActivated(ctx, binName)` | 先执行 `assertEntriesLoaded` 检查,再在 Loader 结算后等待每个已启用配置项;抛出的错误包含每个失败插件的原始错误堆栈,或每个等待中插件尚未解析的服务 | -| `loadOptionalPatches(binName, file)` | 解析一份可选的 patch 列表文件(即 profile 的 `cordis.patch.yml`):其顶层是一个 YAML 数组,内容为 include 的 `PatchOptions`(按 id 定位的配置覆盖、`insert` 列表,允许 `!!js`);文件不存在时返回 `undefined`,文件不可读、不可解析或内容不是数组时抛出异常 | -| `loadOverlayPatches(binName, file)` | 解析必需的顶层 YAML 数组,其中包含与上文相同的 include `PatchOptions` 条目;插入行中的相对插件名以该文件所在目录解析,而用于断言已有行的 patch `name` 保持字面值;文件缺失也会抛出异常,因为该文件是调用方指名的 | -| `mountRootInclude(ctx, absoluteConfigPath, patches?, bareModuleBaseUrl?)` | 注册静态导入的 `cordis:include` 与 `cordis:group` builtin,挂载 include,并保留用户 patch 层 HMR(热模块替换)使用的确切根配置项;可选模块基准会把裸包名锚定到已安装宿主,而相对名称仍以配置目录为基准 | -| `watchUserPatches(ctx, options)` | 向现有 Cordis HMR 服务注册指名的 patch 文件;每次新增、变更或移除都会通过调用方的 `compose` 闭包(应用自有层围绕当前用户层)以事务方式重新组合完整 patch 列表,并返回异步 disposer | -| `resolveProfileDir` / `initProfile` / `loadProfile` / `readProfileManifest` / `writeProfileManifest` / `resolveBundleDir` / `composeEntries` / `healProfilesModuleFallback` / `PROFILE_TEMPLATES` / `DEFAULT_PROFILE_BUNDLES` / `DEFAULT_PROFILE_PATCH_RELOAD` / `PROFILES_DIR` / `PROFILE_PATCH_FILENAME` | Profile 机制与 patch 文件生命周期(见 [Profile](#profiles)) | -| `boot(binName, absoluteConfigPath, patches?, prepare?, bareModuleBaseUrl?)` | 创建根上下文,向 Loader `!!js` 配置表达式暴露 `dshHomePath(...segments)` 并安装 Loader,在配置树条目挂载前执行可选的宿主准备操作(`prepare` 可以使用 Loader,也可以提供由启动器拥有的上下文插槽),再挂载并等待 include 树结算,断言所有条目均已加载并激活,最后返回根上下文——失败时 dispose(资源释放)部分构造的上下文,并以带标签的错误 reject;可选模块基准与 `mountRootInclude` 的解析语义相同 | -| `renderConfigDump(binName, absoluteConfigPath, layers, warn?)` | 使用 include 自己的解析器和补丁算法(`entryListSchema`/`applyEntryPatches`)离线合成基础配置与带标签的覆盖层,使结果与 `boot()` 挂载的内容一致,再渲染为 YAML,并原样保留 `!!js` 表达式;每段来源于同一文件且由相同补丁层修改的连续行之前都有一条 `# ==` 注释,标明该文件和这些补丁层,输出仍是一份可加载的文档;未匹配到行的补丁连同其层标签交给 `warn`(默认:一行 stderr),读取、解析或字段验证失败则抛出 | -| `addHarnessSourceSection(ctx, sourceRoot)` | 添加全局 `harness:source` 提示词段落(顺序紧随 harness 身份、位于 persona 之前),告知 agent(智能体)DSH 实现代码 checkout 的磁盘路径,同时提醒它不得据此推断当前工作目录,而应使用 `pwd`;如果已启动树没有此项服务,则不执行操作并返回 `undefined`。这里的服务是 `systemPrompt`;该段落注册到它的 fiber,因此开发环境 HMR 重新加载系统提示词后,它会消失直至下次启动 | -| `HARNESS_SOURCE_SECTION` | `'harness:source'` 段落名称,供 `addHarnessSourceSection` 注册使用 | +| [`src/index.ts`](src/index.ts) | 启动 helper:配置解析、环境加载、会明确报错的保护机制、激活审计、patch 解析、配置 dump、harness 源码段落 | +| [`src/profile.ts`](src/profile.ts) | profile 发现、初始化、组合包解析、模块后备机制 | +| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式;边界与回放测试覆盖其协议映射) | -Loader 结算会在导入或生命周期失败时返回拒绝结果,并携带失败的配置项与阶段;`boot()` 会 dispose 部分构造的上下文,并用 bin 名称包装该失败。结算后遗留的配置项由独立审计处理:`assertEntriesLoaded` 将已启用却没有 fiber 的配置项转换为 rejection 并列出每个未解析插件;`assertEntriesActivated` 会显式等待每个失败的 fiber,把原始错误堆栈写入启动 rejection,并列出每个等待中配置项尚未解析的服务。抛出错误前,审计会通过一个进程级检查点标记这些 rejection 的确切原因,从而让 `installFailLoud` 将 Loader 的重复通知合并为一次,而所有无关的未处理 rejection 仍然致命。 +
-Loader 并发挂载各个条目,因此当其他环节失败时,某个界面可能已经持有终端:此时不经过整棵树自身的拆卸就退出,会把 raw 模式、bracketed paste 和键盘协议残留在用户的 shell 上,而尚未返回的终端查询响应会在下一个提示符处显示为字面文本。配置树失败会经 `boot()` 结算:它先 dispose 部分构建的上下文(从而执行该界面自身的 shutdown),再抛出带标签的 rejection。对于 `boot()` 看不到的 rejection(插件游离的异步工作在挂载期间或挂载完成后失败),持有终端的 bin 会传入 `release`,在提交退出前 dispose 整棵树;`dsh` 在 `boot()` 的 `prepare` 回调中捕获根上下文,而不是取其返回值,使该回调覆盖整个挂载窗口。release 执行期间,处理函数保持注册并处于锁定状态:被报告的始终是第一个 rejection,后续拒绝(包括拆卸自身产生的拒绝)会被忽略,而不会变成未捕获错误、在拆卸中途杀死进程。 +----- -`cordis:group` 与 `cordis:include` 一并注册,使一份组装能把一个提供方与它的消费方放进同一个 `isolate` realm。两者都通过宿主的模块管线加载,而非被包含树自身的说明符解析,这正是让本工作区之外的组装——放在 harness home 下的 agent preset——能够使用 group 行的原因。 + +## 进一步探索 -配置中的裸插件 specifier(`@deepseek-ai/dsh-*`、npm 包)通过 Cordis Loader 的内部模块 loader 解析。默认情况下,它们从配置目录解析;封闭运行时会向 `boot` 或 `mountRootInclude` 传入 `bareModuleBaseUrl`,使已安装包树保持权威,即使配置位于另一个 Node 项目中也不受遮蔽。相对 specifier 始终以配置目录为基准解析。仓库 bin 会安装 Loader 的可选对等依赖(peer dependency) `node-addon-require-builtin`;外部调用方必须提供该组件,或者把插件安装到普通 Node import 解析可以找到的位置。构建后的 `dsh-app-boot` 产物内嵌静态挂载的 Include 实现,但仍将 Loader 保持为外部依赖,因此 include 树与宿主会绑定到同一个 Loader peer。`pnpm dsh` 源码路径还会将 manifest(元数据清单)声明的 workspace 包映射到其 TypeScript 源码;其配置门禁要求每个随附的原始/Web 裸插件都出现在解析所用 manifest 的 `dependencies` 中。 +当包级约定不够用时阅读以下页面。它们从共享启动机制逐步进入组合模型及其背后的决策证据。 -此包不包含 loader 钩子,也不提供开发模式接口。[`dsh` 应用](../../../apps/cli/README.zh.md) 持有自己的 Node 源码启动钩子,并在启动序列中使用这些 helper;构建后的消费方仍使用普通 Node 包解析。 +- [Cordis 入门](../../../docs/cordis-primer.zh.md)——Loader、`!!js` 配置表达式,以及 include/group 语义。 +- [dsh 应用](../../../apps/cli/README.zh.md)——消费这些 helper 的 `dsh` bin。 +- [dsh-cmdline](../cmdline/README.zh.md)——各 bin 使用的启动器到应用命令行交接。 +- [Profile 组合包](../../bundle/README.zh.md)——组合进 `dsh --profile` 的可安装 patch 层。 +- [dsh-home-paths](../../util/home-paths/README.zh.md)——harness home 解析器(`resolveDshHome`)。 +- [配置来源归属](../../../.agents/notes/implemented/architecture/2026-08-04-configuration-source-ownership.zh.md)——被发现的文件为何不得决定 bootstrap 行为。 +- [Profile 插件组合包](../../../.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.zh.md)——profile 与组合包组合设计。 -## Profiles - -profile 是位于 `$DSH_HOME/profiles/` 下的目录(harness home 由 [`resolveDshHome`](../../util/home-paths/README.zh.md) 解析:先取 `$DSH_HOME`,否则取 `~/.dsh`),其中包含一个 `package.json`(树外插件 `dependencies`,加上 profile manifest `dsh.profile` 及其有序的 `bundles` 层列表和 `patchReload: live | startup`)和用户自己的 `cordis.patch.yml`。`live` 会在启动后监视 profile 与 home 级 patch 文件;`startup` 只应用每层一次。缺失值为自定义 profile 保留历史 `live` 默认值。组合包是在 manifest 中声明 `"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }` 的 npm 包;`loadProfile` 以双锚点解析每个 `dsh.profile.bundles` 名称(先从 dsh 安装目录,再从 profile 目录),列出的包若没有组合包声明则明确报错。`composeEntries` 通过 include 自己的 `applyEntryPatches` 在空条目列表之上应用各 patch 层,因此组合、标志推导和配置 dump 绝不会与实际启动内容发生偏离。`healProfilesModuleFallback` 维护扁平的 `$DSH_HOME/profiles/node_modules` 目录。普通 Node 为安装依赖闭包中的每个包写入一个符号链接;pkg 可执行程序则直接从每个已安装 manifest 中按 Node ESM import 条件解析实际存在的显式 exports,并写入重新导出虚拟模块 URL 的真实代理包,因为操作系统符号链接无法进入 pkg 的 `/snapshot` 树。安装包中不存在的 export 目标保持不可用,但不阻塞其他 exports;格式错误的 exports map 会导致启动失败。只有可执行入口或类型声明入口而没有模块入口的包不会生成代理。完整且匹配的 generation 不会获取写入锁。缺失或过期的配置项会获取跨进程锁、重新检查完整 generation,并在不暴露半成品代理的前提下修复;两种载体都会替换另一种载体留下的受管条目。两种形式都使 profile 插件可以通过 Node 常规的逐级向上查找解析安装包,并让外部插件 peer 共用一个模块实例。`PROFILE_TEMPLATES` 首次使用时以实时重载初始化 `web`,以仅启动时 patch 初始化 `headless`/`sdk`/`sdk-minimal`/`acp`;`sdk-minimal` 只列出自己的独立组合包,其他模板保留 base 加模式层的组合。其他名称在通过 `dsh plugin` 由 `initProfile` 创建前都会明确报错。`loadProfile` 会把安装自有的精确组合包元组和缺失的重载选择规范化为随附模板,同时保留每个显式重载选择和 manifest 中其他所有字段;组合包一旦有任何额外、缺失或重排,列表就归用户所有并保持不变。 - -用户级的机器本地偏好同样位于 harness home 中: - -- **`.env`**:产品 CLI 的普通环境层;调用目录的文件优先于 harness home 的文件,两者都低于继承环境。`loadLayeredEnv` 记录每个值的来源,按不区分大小写的方式拒绝 [bootstrap-only 文件变量](../../../.agents/notes/implemented/architecture/2026-08-04-configuration-source-ownership.zh.md#decision),并把其余值物化进 `process.env`,供 Loader 表达式和第三方库使用。受管凭据另存于 [`.credentials.yaml`](../../credentials/credentials-local/README.zh.md);留在任一 `.env` 中的凭据仍是低优先级后备值。 -- **`cordis.patch.yml`**(home 级)与 **`profiles//cordis.patch.yml`**:用户 patch 层,应用在所有组合包层之后(先应用逐 profile 的文件,再应用 home 级文件,因此后者优先级更高):按 id 定位的 patch 会替换对应条目的整个 `config`(未改字段也要重述),`insert` 会添加条目,`!!js` 表达式则在挂载时插值。如果 patch 指定的条目 id 不在组合后的树中,则输出一条 stderr 警告。空文件或仅含注释的文件会抛出异常(其解析结果为空,而不是列表);如需禁用该层,请使用 `[]`。 - -每个 `patchReload: live` profile 都通过 `watchUserPatches` 保持两个用户 patch 文件实时生效。即使文件或其直接父目录不存在,watcher 仍会监视确切路径;它会串行处理突发变更,并按调用方的层次顺序重新组合用户 patch(组合包层在下、overlay 在上)。读取失败、解析失败或 Loader 候选被拒时,最后一个可用树会继续运行;HMR 服务记录错误后广播 `hmr/config-update-failed(filename, Error)`,并隔离观察方失败。上下文 dispose 时会关闭 watcher,并等待进行中的刷新结束。`startup` profile 不安装这些 watcher,也不安装启动器的仅监视 HMR fallback。 +----- + ## 模型体验 -模型通过此包加载的插件树间接受到影响;该树决定最终应用中的提示词、schema、消息和模型适配器。唯一贡献模型可见文本的导出 `addHarnessSourceSection`,也只有在消费方启动后调用它时才会产生影响。 +模型通过此包加载的插件树间接受影响——只有该树贡献模型上下文;唯一贡献模型可见文本的导出 `addHarnessSourceSection`,也只有在消费方启动后调用它时才会产生影响。 #### KV Cache 影响 -`boot()` 不会直接使缓存失效;消费方调用 `addHarnessSourceSection` 时,会在系统提示词靠前位置、逐请求内容之前添加一行短文本,因此不会使跨轮次缓存失效。请求前缀的其他任何变化均由相应的具名消费方负责。 +启动本身不会使请求前缀中的任何内容失效。消费方调用 `addHarnessSourceSection` 时,会在系统提示词靠前位置、逐请求内容之前添加一行短文本,因此不会使跨轮次缓存失效;请求前缀的其他任何变化均由相应的具名消费方负责。 -## 已知限制与暂缓事项 +## 已知限制与延期工作 -- **裸包 specifier 依赖 Loader 内部机制**:生产 bin 需要 Loader 的可选原生辅助组件;没有该辅助组件的进程内调用方必须使用可解析的相对/file specifier,或提供自己的模块解析钩子。 -- **快照回放替换仅识别特定 basename**:只有以 `cordis.yml` 或 `cordis.yaml` 结尾的配置会映射到同级 `cordis.snapshot.yml`;自定义配置名称需要调用方自行选择。 -- **环境发现以启动为界**:`loadLayeredEnv` 只读取一次调用目录与 harness home 中的 `.env`;它不搜索父目录,也不跟随之后选择的 workspace。`loadEnv` 仍是非产品 bin 使用的单目录 helper。 -- **用户 patch 会替换匹配到的整个配置**:按 id 定位的 patch 不做深度合并,因此 profile 覆盖必须重述需要保留的组合包字段。 + + + +这些限制说明此启动库在何时不合适,或何时需要特别注意。它们是当前包约束,不是任务积压。 + +- **裸包 specifier 依赖 Loader 内部机制**——生产 bin 需要 Loader 的可选原生辅助组件;没有该辅助组件的进程内调用方必须使用可解析的相对/file specifier,或提供自己的模块解析钩子。 +- **快照回放替换仅识别特定 basename**——只有以 `cordis.yml` 或 `cordis.yaml` 结尾的配置会映射到同级 `cordis.snapshot.yml`;自定义配置名称需要调用方自行选择。 +- **环境发现以启动为界**——`loadLayeredEnv` 只读取一次调用目录与 harness home 中的 `.env`;它不搜索父目录,也不跟随之后选择的 workspace。`loadEnv` 仍是非产品 bin 使用的单目录 helper。 +- **用户 patch 会替换匹配到的整个配置**——按 id 定位的 patch 不做深度合并,因此 profile 覆盖必须重述需要保留的组合包字段。 + + +### 开发备注 + +
+维护者的工作上下文——点击展开 + +本开发备注是维护者的工作上下文:开放设计问题与尚未决定的探索方向。它明确不具权威性——已交付的行为、限制与既定理由以上文、包代码和相关 Agent Note 为准。 + +#### 待定:配置 dump 稳定性 + +`renderConfigDump` 的输出是一份可加载的 YAML 文档,其 `# ==` 来源注释与 `!!js` 原样渲染服务于 `--dump-config` 诊断。任何内容都不承诺跨包版本的字节稳定性;在程序化消费该输出之前,请决定 dump 是否成为序列化约定。 + +
diff --git a/packages/boot/cmdline/README.i18n.yaml b/packages/boot/cmdline/README.i18n.yaml index ad263beac9..2d4371b1a9 100644 --- a/packages/boot/cmdline/README.i18n.yaml +++ b/packages/boot/cmdline/README.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 packages/boot/cmdline/README.md -README.md: 4a0244679e0451a196ff6bb55a7eaea9e53775d9 -README.zh.md: 77063ccd3e3107ba9ab01b1a06eae49f54c1006a +README.md: 7ea825d8ef309cc295e3334bf5ae220e3e0524e5 +README.zh.md: 9f5f876ec6d1b408e2156dc3075499885032a141 diff --git a/packages/boot/cmdline/README.md b/packages/boot/cmdline/README.md index 4a0244679e..7ea825d8ef 100644 --- a/packages/boot/cmdline/README.md +++ b/packages/boot/cmdline/README.md @@ -1,44 +1,54 @@ -# `@deepseek-ai/dsh-cmdline` +--- +description: "App-owned command lines for dsh app bins: your app parses its own flags, --help, and exit behavior from the launcher's remaining arguments." +kind: "package-library" +--- + +# @deepseek-ai/dsh-cmdline English | [中文](README.zh.md) -The command line a dsh launcher hands to the app it boots. The launcher parses only its own flags (`--profile`, `--patch`, the config dumps) and hands **everything after them** to the tree verbatim, so an app owns its flag family, its `--help` text, and its parse errors instead of the launcher knowing them. +## Summary -## The launcher values +`dsh-cmdline` lets your app own its command line: the launcher keeps only its own flags (`--profile`, `--patch`, the config dumps) and passes everything after them to your app verbatim, so your app decides its flags, its `--help` text, and its parse errors. Values you parse from those arguments win over any default written in the config, without writing anything back. Your app also gets a bounded way to ask for process exit, wired to the launcher's shutdown. Use it when you write an app bin that accepts its own flags; it adds no prompt, schema, or model-facing surface of its own. -A launcher calls `provideCmdline(ctx, host)` before any tree entry mounts, which provides: +## Table of Contents -- `ctx.cmdlineArgs` — the invocation's inner arguments. `get()` is the whole interface, and it returns a snapshot: `dsh --profile tui --resume abc` yields `['--resume', 'abc']`. -- `ctx.appExit` — a bounded process-exit request, wired to the launcher's shutdown controller. -- `ctx.appReady` — the launcher's successful-startup signal. It commits only after the Loader tree and launcher-owned setup succeed; failed or externally terminated startup never calls pending listeners. +- [Use this package](#use-this-package) +- [Understand the implementation](#understand-the-implementation) +- [Further Exploration](#further-exploration) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) -An embedding host with no command line provides an empty list; that is the honest answer, not a missing value. +----- -`exitOnStdinEnd(ctx, label)` binds a successfully accepted stdio application's EOF to `ctx.appExit(0)` after `ctx.appReady` commits. It never reads or resumes stdin, so the protocol transport receives bytes buffered before it mounts. A startup rejection wins over a racing EOF, an already-ended stream still requests shutdown after successful startup, and the calling plugin's fiber removes both pending listeners. An app calls it inside the same command action that publishes its startup service, so help and rejected arguments leave the transport and EOF lifecycle unmounted. + +## Use this package -## Ordinary providers and injected config +Your app reads the invocation's inner arguments at startup, and any number of its plugins can use them. The common path: a startup plugin reads the arguments, parses them, and publishes the parsed values; other rows configure themselves from those values. -Any app plugin may inject `cmdlineArgs`, parse it, and publish an ordinary app-owned service. `parseCmdline(ctx, program)` is only a commander adapter; the program's own action owns validation and the published service: +### The launcher values -```ts ignore -export const name = 'web-startup' -export const inject = ['cmdlineArgs'] +The launcher makes three things available to your app: -export function apply(ctx: Context): void { - const program = webCommand() - program.action(() => ctx.provide('webStartup', webValuesFrom(program))) - parseCmdline(ctx, program) -} -``` +- `ctx.cmdlineArgs` — the inner arguments of your invocation. Reading them returns an immutable snapshot and never consumes or changes them: `dsh --profile tui --resume abc` gives your app `['--resume', 'abc']`. +- `ctx.appExit` — a way to ask the process to exit once the tree has shut down, wired to the launcher's shutdown controller. +- `ctx.appReady` — the successful-startup signal, committed only after the Loader tree and launcher-owned setup succeed. -Its Loader row carries no launcher marker or special kind: +An app launched with no arguments sees an empty list — that is the honest answer, not a missing value. + +`exitOnStdinEnd(ctx, label)` binds a successfully started stdio application's EOF to `ctx.appExit(0)`. It never reads or resumes stdin, so a protocol transport receives bytes buffered before it mounts; startup rejection wins over a racing EOF, and the owning fiber removes both pending listeners. + +### Parsing your flags + +You bring your own commander program: declare your flags and your actions, and the package runs it against the inner arguments. Your action is the only place validation happens, and it publishes whatever your rows need. The plugin's Loader row carries no special marker: ```yaml - id: web-startup name: '@deepseek-ai/dsh-web-app/startup' ``` -Every row configured from those values uses ordinary service injection and direct lazy config access: +Rows configured from the parsed values inject the published service and read it directly in their config: ```yaml - id: webserver @@ -49,21 +59,67 @@ Every row configured from those values uses ordinary service injection and direc port: !!js ctx.webStartup.port ?? 3080 ``` -`parseCmdline` refuses at load a program in which no command declares an action, routes every command's exit and output through the launcher (commander copies those settings into subcommands only at registration), and parses the immutable arguments; commander runs the invoked command's synchronous action on success. An action rejects an invalid invocation with `program.error(...)` — before publishing, since statements ahead of the rejection have already run. On `--help`, `--version`, a parse error, or that rejection, the helper writes commander's text and requests exit; the provider publishes nothing, so dependent rows never activate. +The outcomes: `dsh --profile web --port 8080` starts the server on port 8080 even when the config says 3080, because the flag wins. `--help` prints your app's help and exits 0 without starting anything; a rejected value (for example a non-numeric port) prints your error and exits nonzero, and no row that depends on the parsed values ever starts. -### How injection orders config +### How flags beat config values -Loader defers a row's `!!js` interpolation until that row's declared injections are active, then evaluates against the row's plugin context. The example above can therefore read `ctx.webStartup` directly: Cordis has already populated that injected service before Loader asks for `webserver`'s config. Include trees preserve nested expression nodes until each target row reaches this point. Provider replacement and live patch reload repeat interpolation against the current injected services, so a launch flag cannot be silently reset. +The value written beside a `!!js` expression is the fallback: the flag wins when present, the written value is used otherwise. Resolution happens once at startup, after your parser ran, so a flag is never silently reset by a later config reload. -### Shared immutable arguments +### Reading the same arguments from several plugins -`get()` does not consume or mutate argv. Multiple plugins can parse the same snapshot and independently provide services. The launcher does not inspect the composition for a command-line owner; a profile with no reader simply ignores its app arguments. +Any number of plugins can read the same arguments — reading never consumes them — and each can parse what it needs and publish its own values. The launcher does not decide who owns the command line: an app with no reader ignores its arguments. -An out-of-tree plugin brings its own commander copy, so commander's control-flow errors are detected structurally rather than by class identity; an identity check would rethrow a printed help as a fatal load failure. +Apps built outside this repository behave the same way: their `--help` prints and exits instead of crashing, even though they carry their own commander copy. +----- + + +## Understand the implementation + +
+Implementation internals — click to expand + +This section explains how the outcomes above are realized and points at the code that realizes them; everything here is developer-facing and not needed to use the package. + +### Design notes + +- **Launcher facts, not config.** `cmdlineArgs` and `appExit` are provided on the host context before the tree mounts; they are not Loader rows, so no composition owns or overrides them. +- **Positional split.** The launcher recognizes no app row: the first token after its own flags starts the app's arguments, so the app owns its flag family, its `--help` text, and its parse errors. +- **Structural error detection.** `isCommanderError` reads commander's error code prefix instead of using `instanceof`, because an out-of-tree plugin brings its own commander copy whose `CommanderError` identity differs; `configureExitAndOutput` walks every subcommand because commander copies exit and output settings only at registration. +- **Injectable output streams.** `internals` holds the output streams so tests can capture commander's text without touching the process. + +### Parsing contract + +The parse path is one small family with two owners: `provideCmdline` freezes the host arguments and provides `cmdlineArgs` and `appExit` before any tree entry mounts, and `parseCmdline` runs your commander program against the immutable arguments, routing every command's help, version, and error output through the launcher. A rejected value, `--help`, or `--version` prints commander's text and requests `ctx.appExit` without publishing anything, so dependent rows never activate; Loader defers each row's `!!js` interpolation until its declared injections are active. Per-export contracts live in the code, not this README — see [`src/index.ts`](src/index.ts). + +### Source map + +| File | Role | +|---|---| +| [`src/index.ts`](src/index.ts) | `CmdlineArgs`/`AppExit` types, `provideCmdline`, `parseCmdline`, commander exit/output routing | +| [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant; Loader settlement reports missing services) | + +
+ +----- + + +## Further Exploration + +Read these pages when the package-level contract is not enough. They move from the handoff mechanism to the apps that consume it and the decisions behind it. + +- [App-owned command-line decision](../../../.agents/notes/implemented/architecture/2026-08-06-app-owned-command-line.md) — why apps own their flag family and how the handoff works. +- [Command-line seam trim](../../../.agents/notes/implemented/architecture/2026-08-11-cmdline-seam-trim.md) — the seams reduced to existing interfaces. +- [dsh-app-boot](../app-boot/README.md) — the boot sequence that provides these launcher values. +- [dsh-web-app bundle](../../bundle/web-app/README.md) — an app that owns the Web flag family through this package. +- [dsh-headless bundle](../../bundle/headless/README.md) — the one-shot runner that reads its task from the command line. + +----- + + ## Model Experience -None, as this package resolves the process's own command line before any session exists. +None, as this package resolves the process command line before any session exists; configured rows own every model-visible consequence. #### KV Cache effect @@ -71,7 +127,25 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **Launcher flags must precede app arguments.** The split is positional: the first token the launcher does not recognize starts the inner arguments, so `--patch` placed after an app flag belongs to the app. The launcher's parser consumes one `--`, so an app argument that must survive as a literal `--` needs `-- --`. -- **An app-owned service has no statically declared provider.** Consumer rows name it through ordinary injection; a bundle that omits its provider fails at settlement with pending entries naming the service rather than at load. -- **A user patch that replaces a row's whole `config` drops its expressions.** A flag beats the value written beside it, not a literal a user wrote in place of the expression; keeping the expression is what keeps the flag winning. -- **EOF means successful application shutdown.** `exitOnStdinEnd` is for a stdio protocol process whose client owns stdin; an interactive application with unrelated stdin semantics does not call it. + + + +These limits describe where app-owned command lines are a poor fit or need special care. They are current package constraints, not a task backlog. + +- **Launcher flags must precede app arguments** — the split is positional: the first token the launcher does not recognize starts the inner arguments, so `--patch` placed after an app flag belongs to the app. The launcher's parser consumes one `--`, so an app argument that must survive as a literal `--` needs `-- --`. +- **An app-owned service has no statically declared provider** — consumer rows name it through ordinary injection; a bundle that omits its provider fails at settlement with pending entries naming the service rather than at load. +- **A user patch that replaces a row's whole `config` drops its expressions** — a flag beats the value written beside it, not a literal a user wrote in place of the expression; keeping the expression is what keeps the flag winning. + + +### Dev Note + +
+Working context for maintainers — click to expand + +This Dev Note is working context for maintainers: open design questions and directions that are not decided. It is explicitly non-authoritative — shipped behavior, limits, and accepted rationale live in the sections above, the package code, and the linked Agent Notes. + +#### Open: parser surface + +`parseCmdline` is a commander adapter, not a command-line framework: help, version, and error output follow commander's formatting, and the exit/output routing assumes commander's control-flow model. A different parser would need its own routing and error handling; nothing in the `cmdlineArgs` service contract depends on commander. + +
diff --git a/packages/boot/cmdline/README.zh.md b/packages/boot/cmdline/README.zh.md index 77063ccd3e..9f5f876ec6 100644 --- a/packages/boot/cmdline/README.zh.md +++ b/packages/boot/cmdline/README.zh.md @@ -1,44 +1,54 @@ -# `@deepseek-ai/dsh-cmdline` +--- +description: "dsh app bin 的应用自有命令行:应用从启动器剩余参数中解析自己的 flag、--help 与退出行为。" +kind: "package-library" +--- + +# @deepseek-ai/dsh-cmdline [English](README.md) | 中文 -dsh 启动器交给它所引导应用的那条命令行。启动器只解析属于自己的 flag(`--profile`、`--patch`、配置 dump),并把**其后的一切**原样交给配置树,因此 flag 家族、`--help` 文本和解析错误都由应用自己持有,启动器不必知道它们。 +## 概述 -## 启动器提供的值 +`dsh-cmdline` 让你的应用持有自己的命令行:启动器只保留属于自己的 flag(`--profile`、`--patch`、配置 dump),并把**其后的一切**原样交给你的应用,因此 flag、`--help` 文本与解析错误都由你的应用决定。你从这些参数解析出的值会胜过配置中写下的任何默认值,且无需写回任何内容。你的应用还获得一个有边界的进程退出请求,接到启动器的关停上。当你编写接受自有 flag 的应用 bin 时使用它;它本身不增加任何提示词、schema 或面向模型的表面。 -启动器在任何配置树条目挂载之前调用 `provideCmdline(ctx, host)`,它提供: +## 目录 -- `ctx.cmdlineArgs`:本次调用的内层参数。`get()` 就是它的全部接口,返回一份快照:`dsh --profile tui --resume abc` 得到 `['--resume', 'abc']`。 -- `ctx.appExit`:一个有边界的进程退出请求,接到启动器的关停控制器上。 -- `ctx.appReady`:启动器的成功启动信号。只有 Loader 树和启动器自身的设置都成功后才会提交;启动失败或被外部终止时,待处理 listener 永远不会被调用。 +- [使用本包](#use-this-package) +- [理解实现](#understand-the-implementation) +- [进一步探索](#further-exploration) +- [模型体验](#model-experience) +- [已知限制与延期工作](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) -没有命令行的嵌入宿主提供空列表;这是诚实的答案,而不是缺失的值。 +----- -`exitOnStdinEnd(ctx, label)` 会在 `ctx.appReady` 提交后,把已成功接受的 stdio 应用 EOF 接到 `ctx.appExit(0)`。它从不读取或恢复 stdin,因此协议 transport 会收到挂载前缓冲的字节。启动失败与 EOF 竞争时由启动失败决定结果;绑定前已经结束的 stream 仍会在启动成功后请求关闭;调用插件的 fiber 会移除两个待处理 listener。应用在发布启动服务的同一个命令 action 中调用它,因此 help 与被拒参数不会挂载 transport 或 EOF 生命周期。 + +## 使用本包 -## 普通提供方与注入配置 +你的应用在启动时读取本次调用的内层参数,任意数量的插件都可以使用它们。常用路径是:启动插件读取参数、解析它们,再发布解析后的值;其他行由这些值配置自身。 -任何应用插件都可以注入 `cmdlineArgs`、解析它,再发布一个普通的应用自有服务。`parseCmdline(ctx, program)` 只适配 commander;校验与发布的服务都归 program 自己的 action 持有: +### 启动器提供的值 -```ts ignore -export const name = 'web-startup' -export const inject = ['cmdlineArgs'] +启动器向你的应用提供三样东西: -export function apply(ctx: Context): void { - const program = webCommand() - program.action(() => ctx.provide('webStartup', webValuesFrom(program))) - parseCmdline(ctx, program) -} -``` +- `ctx.cmdlineArgs`——本次调用的内层参数。读取它返回一份不可变快照,且绝不会消费或修改它们:`dsh --profile tui --resume abc` 给你的应用 `['--resume', 'abc']`。 +- `ctx.appExit`——在整棵树关闭后请求进程退出的方式,接到启动器的关停控制器上。 +- `ctx.appReady`——成功启动信号,只在 Loader 树与 launcher 自有设置成功后提交。 -它的 Loader 行不携带启动器标记,也没有特殊类型: +没有参数的启动会看到空列表——这是诚实的答案,而不是缺失的值。 + +`exitOnStdinEnd(ctx, label)` 把已成功启动的 stdio 应用 EOF 绑定到 `ctx.appExit(0)`。它绝不读取或恢复 stdin,因此协议传输会收到挂载前已缓冲的字节;启动拒绝优先于竞态 EOF,拥有它的 fiber 会移除两项待处理监听。 + +### 解析你的 flag + +你自带自己的 commander program:声明你的 flag 与 action,本包会针对内层参数运行它。校验只发生在你的 action 中,并由它发布你的行所需的任何值。插件的 Loader 行不携带特殊标记: ```yaml - id: web-startup name: '@deepseek-ai/dsh-web-app/startup' ``` -所有由这些取值配置的行都使用普通服务注入,并在惰性配置中直接访问该服务: +由解析值配置的行注入发布的服务,并在其配置中直接读取它: ```yaml - id: webserver @@ -49,21 +59,67 @@ export function apply(ctx: Context): void { port: !!js ctx.webStartup.port ?? 3080 ``` -`parseCmdline` 在加载时拒绝整棵命令树中没有任何命令声明 action 的 program,把每个命令的退出与输出都接到启动器上(commander 只在注册时把这些设置复制进子命令),再解析不可变参数;解析成功时 commander 运行被调用命令的同步 action。action 用 `program.error(...)` 拒绝无效调用——必须先拒绝后发布,因为写在拒绝之前的语句已经执行。遇到 `--help`、`--version`、解析错误或这种拒绝时,该适配器输出 commander 文本并请求退出;提供方什么也不发布,因此依赖行不会激活。 +结果:即使配置写的是 3080,`dsh --profile web --port 8080` 也会让服务器监听 8080 端口,因为 flag 优先。`--help` 打印你的应用帮助并以 0 退出、不启动任何内容;被拒绝的值(例如非数字端口)打印你的错误并以非零码退出,任何依赖解析值的行都不会启动。 -### 注入如何排列配置求值 +### flag 如何胜过配置值 -Loader 会把一行的 `!!js` 插值推迟到该行声明的注入全部激活之后,再基于该行的插件上下文求值。所以上例可以直接读取 `ctx.webStartup`:Loader 索取 `webserver` 的配置之前,Cordis 已经填入了这个注入服务。Include 树会保留嵌套表达式节点,直到各个目标行到达这一时点。提供方替换与活动 patch 重载都会针对当前注入服务重新插值,因此启动 flag 不会被悄悄重置。 +写在 `!!js` 表达式旁的值是后备:flag 存在时 flag 优先,否则使用写下的值。解析在启动时、你的解析器运行之后发生一次,因此 flag 绝不会被之后的配置重载悄悄重置。 -### 共享不可变参数 +### 多个插件读取同一份参数 -`get()` 不会消费或修改 argv。多个插件可以解析同一份快照,并分别提供服务。启动器不会检查组合中的命令行所有者;没有读取方的 profile 只会忽略自己的应用参数。 +任意数量的插件都可以读取同一份参数——读取绝不会消费它们——每个插件都能解析自己需要的部分并发布各自的值。启动器不会决定谁是命令行的所有者:没有读取方的应用会忽略自己的参数。 -树外插件会带来自己的一份 commander 副本,因此 commander 的控制流错误按结构识别,而不是按类身份识别;按身份判断会把已经打印出来的 help 重新抛成致命的加载失败。 +本仓库之外构建的应用行为一致:即使它们自带 commander 副本,其 `--help` 也会打印并退出,而不是崩溃。 +----- + + +## 理解实现 + +
+实现细节——点击展开 + +本节解释上述结果如何实现,并指出实现它们的代码位置;这里的内容面向开发者,使用本包并不需要。 + +### 设计说明 + +- **启动器事实,而非配置。** `cmdlineArgs` 与 `appExit` 在树挂载前提供到宿主上下文上;它们不是 Loader 行,因此没有任何组合持有或覆盖它们。 +- **按位置切分。** 启动器不认识任何应用行:自身 flag 之后的第一个 token 就是应用参数的起点,因此 flag 家族、`--help` 文本与解析错误都由应用自己持有。 +- **结构化错误识别。** `isCommanderError` 读取 commander 的错误码前缀,而不是用 `instanceof`,因为树外插件会带来自己的一份 commander 副本,其 `CommanderError` 身份不同;`configureExitAndOutput` 会遍历每个子命令,因为 commander 只在注册时复制退出与输出设置。 +- **可注入的输出流。** `internals` 持有输出流,使测试无需触碰进程即可捕获 commander 的文本。 + +### 解析约定 + +解析路径是一个只有两个所有者的小家族:`provideCmdline` 冻结宿主参数,并在任何配置树条目挂载前提供 `cmdlineArgs` 与 `appExit`;`parseCmdline` 针对不可变参数运行你的 commander program,把每个命令的 help、version 与错误输出都接到启动器上。被拒绝的值、`--help` 或 `--version` 会打印 commander 文本并请求 `ctx.appExit`,且不发布任何内容,因此依赖行绝不会激活;Loader 会把每行的 `!!js` 插值推迟到该行声明的注入全部激活之后。各导出的约定在代码中,不在本 README——见 [`src/index.ts`](src/index.ts)。 + +### 源码地图 + +| 文件 | 职责 | +|---|---| +| [`src/index.ts`](src/index.ts) | `CmdlineArgs`/`AppExit` 类型、`provideCmdline`、`parseCmdline`、commander 退出/输出路由 | +| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式;Loader 结算会报告缺失的服务) | + +
+ +----- + + +## 进一步探索 + +当包级约定不够用时阅读以下页面。它们从交接机制逐步进入消费它的应用及其背后的决策。 + +- [应用持有命令行决策](../../../.agents/notes/implemented/architecture/2026-08-06-app-owned-command-line.zh.md)——为什么 flag 家族由应用持有,以及交接如何运作。 +- [命令行 seam 精简](../../../.agents/notes/implemented/architecture/2026-08-11-cmdline-seam-trim.zh.md)——缩减到既有接口的各 seam。 +- [dsh-app-boot](../app-boot/README.zh.md)——提供这些启动器值的启动序列。 +- [dsh-web-app 组合包](../../bundle/web-app/README.zh.md)——通过此包持有 Web flag 家族的应用。 +- [dsh-headless 组合包](../../bundle/headless/README.zh.md)——从命令行读取任务的一次性 runner。 + +----- + + ## 模型体验 -无。本包在任何会话存在之前解析进程自身的命令行。 +无。本包在任何会话存在之前解析进程自身的命令行;配置行持有每一个模型可见的后果。 #### KV Cache 影响 @@ -71,7 +127,25 @@ Loader 会把一行的 `!!js` 插值推迟到该行声明的注入全部激活 ## 已知限制与延期工作 -- **启动器的 flag 必须写在应用参数之前**:切分按位置进行,启动器不认识的第一个 token 就是内层参数的起点,因此写在某个应用 flag 之后的 `--patch` 属于应用。启动器的解析器会消耗掉一个 `--`,因此必须以字面量 `--` 存活到应用的参数需要写成 `-- --`。 -- **应用自有服务没有静态声明的提供方**:消费行通过普通注入点名它;缺少提供方的组合包会在结算时失败,由待处理条目点名该服务,而不是在加载时失败。 -- **用户 patch 若整体替换某行的 `config`,会连同其中的表达式一起丢掉**:flag 胜过的是表达式旁写着的那个值,而不是用户用字面量替换掉表达式之后的结果;保留表达式才能保留 flag 的优先级。 -- **EOF 表示应用成功关闭**:`exitOnStdinEnd` 适用于由客户端持有 stdin 的 stdio 协议进程;stdin 另有交互语义的应用不会调用它。 + + + +这些限制说明应用自有命令行在何时不合适,或何时需要特别注意。它们是当前包约束,不是任务积压。 + +- **启动器的 flag 必须写在应用参数之前**——切分按位置进行:启动器不认识的第一个 token 就是内层参数的起点,因此写在某个应用 flag 之后的 `--patch` 属于应用。启动器的解析器会消耗掉一个 `--`,因此必须以字面量 `--` 存活到应用的参数需要写成 `-- --`。 +- **应用自有服务没有静态声明的提供方**——消费行通过普通注入点名它;缺少提供方的组合包会在结算时失败,由待处理条目点名该服务,而不是在加载时失败。 +- **用户 patch 若整体替换某行的 `config`,会连同其中的表达式一起丢掉**——flag 胜过的是表达式旁写着的那个值,而不是用户用字面量替换掉表达式之后的结果;保留表达式才能保留 flag 的优先级。 + + +### 开发备注 + +
+维护者的工作上下文——点击展开 + +本开发备注是维护者的工作上下文:开放设计问题与尚未决定的探索方向。它明确不具权威性——已交付的行为、限制与既定理由以上文、包代码和相关 Agent Note 为准。 + +#### 待定:解析器表面 + +`parseCmdline` 是 commander 适配器,而不是命令行框架:help、version 与错误输出遵循 commander 的格式,退出/输出路由也假定 commander 的控制流模型。改用其他解析器需要它自己的路由与错误处理;`cmdlineArgs` 服务约定中没有任何内容依赖 commander。 + +
diff --git a/packages/bundle/README.i18n.yaml b/packages/bundle/README.i18n.yaml index 6ad818ee64..a7ba321f44 100644 --- a/packages/bundle/README.i18n.yaml +++ b/packages/bundle/README.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 packages/bundle/README.md -README.md: c0ea5be0c6f1b2457166b24a1718f6ab3aa0ffe6 -README.zh.md: 6c3520668d8f83cfbdc75652fac81f29d9bb810d +README.md: 6311b61d5e20a13d1f1ac67729ab5bc24f3ec902 +README.zh.md: cb7e27a47a708193198c4dc0c15f95cb7f907a1e diff --git a/packages/bundle/README.md b/packages/bundle/README.md index c0ea5be0c6..6311b61d5e 100644 --- a/packages/bundle/README.md +++ b/packages/bundle/README.md @@ -1,18 +1,45 @@ +--- +description: "Ready-made dsh profile bundles for the shared core, browser GUI, one-shot task, ACP, and SDK application surfaces." +kind: "package-group" +--- + # bundle/ — profile plugin bundles English | [中文](README.zh.md) -Profile bundles: npm packages whose manifest declares `"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }`, making them installable patch layers for `dsh --profile` compositions ([profile contract](../boot/app-boot/README.md#profiles)). A bundle's substance is its patch list; some also ship runtime glue plugins their patch mounts. +## Summary -The manifest declaration, not this directory, defines Bundle identity. Domain packages can carry their own optional Profile layer; the [Codex and Claude Code subagent packages](../subagent/README.md) are directly installable examples. +This group maps the installable patch layers used by `dsh --profile`. Each package declares `dsh.bundle.patch`; the launcher stacks those patch documents to assemble a named profile. The `web`, `headless`, `acp`, and `sdk` profiles build on `dsh-base`, while `sdk-minimal` supplies its complete tree in one bundle. Domain packages can declare additional layers outside this directory. + +## Table of Contents + +- [Packages](#packages) +- [Related documentation](#related-documentation) +- [Dev Note](#dev-note) + + +## Packages | Package | Role | ctx key | |---|---|---| -| [`base/`](base/README.md) | The shared dsh core applied first by base-backed profiles | — (patch only) | -| [`acp-app/`](acp-app/README.md) | Automation-only ACP stdio application over base | mounts the ACP bridge | -| [`web-app/`](web-app/README.md) | Browser surface: web patch layer + runtime glue plugin | mounts rows | -| [`headless/`](headless/README.md) | Direct one-shot task mode over base, with no Host or Web layer | mounts `headless-runner` | -| [`sdk-app/`](sdk-app/README.md) | SDK stdio JSON-RPC application over base | mounts the SDK server | -| [`sdk-minimal/`](sdk-minimal/README.md) | Standalone minimal SDK application without base or Web | — (complete patch tree) | +| [`base`](base/README.md) | Shared core for base-backed profiles | — (patch only) | +| [`acp-app`](acp-app/README.md) | Automation-only ACP stdio application over base | mounts the ACP bridge | +| [`web-app`](web-app/README.md) | Browser application layer over base | mounts Web rows | +| [`headless`](headless/README.md) | One-shot command-line task application over base | `headless-runner` | +| [`sdk-app`](sdk-app/README.md) | SDK JSON-RPC stdio application over base | mounts the SDK server | +| [`sdk-minimal`](sdk-minimal/README.md) | Standalone minimal SDK application without base or Web | — (complete patch tree) | In-box bundles resolve from the dsh installation; out-of-tree bundles install into a profile through `dsh plugin --profile add `. + + +## Related documentation + +- [dsh app](../../apps/cli/README.md) — the `dsh` command that starts a profile. +- [app-boot](../boot/app-boot/README.md) — how profiles are resolved, layered, and customized. +- [Profile plugin bundles note](../../.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.md) — the profile and bundle composition design. +- [Generated composition graph](../../apps/cli/composition.md) — the exact composition each shipped profile uses. + + +## Dev Note + +None. diff --git a/packages/bundle/README.zh.md b/packages/bundle/README.zh.md index 6c3520668d..cb7e27a47a 100644 --- a/packages/bundle/README.zh.md +++ b/packages/bundle/README.zh.md @@ -1,18 +1,45 @@ -# bundle/ — profile 插件组合包 +--- +description: "共享核心、浏览器 GUI、一次性任务、ACP 与 SDK 应用表层的现成 dsh profile bundle。" +kind: "package-group" +--- + +# bundle/:profile 插件组合包 [English](README.md) | 中文 -Profile 组合包:在 manifest(元数据清单)中声明 `"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }` 的 npm 包,因此可作为 patch 层安装进 `dsh --profile` 组合([profile 约定](../boot/app-boot/README.zh.md#profiles))。组合包的实体是它的 patch 列表;有些组合包还附带由其 patch 挂载的运行时粘合插件。 +## 概述 -Bundle 身份由 manifest 声明决定,而不是由本目录决定。领域包可以携带自己的可选 Profile 层;[Codex 与 Claude Code subagent 包](../subagent/README.zh.md)就是可直接安装的例子。 +本组列出 `dsh --profile` 使用的可安装 patch 层。每个包都声明 `dsh.bundle.patch`;启动器会叠放这些 patch 文档来组装具名 profile。`web`、`headless`、`acp` 与 `sdk` profile 以 `dsh-base` 为基础,`sdk-minimal` 则由一个 bundle 提供完整配置树。领域包也可以在本目录之外声明附加层。 + +## 目录 + +- [包](#packages) +- [相关文档](#related-documentation) +- [开发备注](#dev-note) + + +## 包 | 包 | 职责 | ctx key | |---|---|---| -| [`base/`](base/README.zh.md) | 基于 base 的 profile 最先应用的共享 dsh 核心 | —(仅 patch) | -| [`acp-app/`](acp-app/README.zh.md) | 运行在 base 之上的 automation-only ACP stdio 应用 | 挂载 ACP bridge | -| [`web-app/`](web-app/README.zh.md) | 浏览器表层:web patch 层 + 运行时粘合插件 | 挂载多条配置行 | -| [`headless/`](headless/README.zh.md) | 直接运行在 base 之上的一次性任务模式,不含 Host 或 Web 层 | 挂载 `headless-runner` | -| [`sdk-app/`](sdk-app/README.zh.md) | 运行在 base 之上的 SDK stdio JSON-RPC 应用 | 挂载 SDK server | -| [`sdk-minimal/`](sdk-minimal/README.zh.md) | 不含 base 或 Web 的独立极简 SDK 应用 | 无(完整 patch 树) | +| [`base`](base/README.zh.md) | 基于 base 的 profile 共享核心 | —(仅 patch) | +| [`acp-app`](acp-app/README.zh.md) | 基于 base 的纯自动化 ACP stdio 应用 | 挂载 ACP bridge | +| [`web-app`](web-app/README.zh.md) | 基于 base 的浏览器应用层 | 挂载 Web 配置项 | +| [`headless`](headless/README.zh.md) | 基于 base 的一次性命令行任务应用 | `headless-runner` | +| [`sdk-app`](sdk-app/README.zh.md) | 基于 base 的 SDK JSON-RPC stdio 应用 | 挂载 SDK server | +| [`sdk-minimal`](sdk-minimal/README.zh.md) | 不使用 base 或 Web 的独立极简 SDK 应用 | —(完整 patch 树) | 内置组合包从 dsh 安装目录解析;树外(out-of-tree)组合包通过 `dsh plugin --profile add ` 安装进 profile。 + + +## 相关文档 + +- [dsh 应用](../../apps/cli/README.zh.md)——启动 profile 的 `dsh` 命令。 +- [app-boot](../boot/app-boot/README.zh.md)——profile 如何解析、分层与定制。 +- [Profile 组合包设计笔记](../../.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.zh.md)——profile 与组合包的组合设计。 +- [生成组合图](../../apps/cli/composition.md)——每个已发布 profile 使用的确切组合。 + + +## 开发备注 + +无。 diff --git a/packages/bundle/acp-app/README.i18n.yaml b/packages/bundle/acp-app/README.i18n.yaml index 72a6015edf..3f7399f380 100644 --- a/packages/bundle/acp-app/README.i18n.yaml +++ b/packages/bundle/acp-app/README.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 packages/bundle/acp-app/README.md -README.md: 5cb1e33319c5da04de19d3c8eb1c24cb72ec2312 -README.zh.md: 13debf94c4fe209a74f0174fda7195c383bf590a +README.md: 90846589d3ffa4eae71bc892251b07b69d96ce87 +README.zh.md: 0669e080e05c62f877e970e54d9f0b8ea946c4cc diff --git a/packages/bundle/acp-app/README.md b/packages/bundle/acp-app/README.md index 5cb1e33319..90846589d3 100644 --- a/packages/bundle/acp-app/README.md +++ b/packages/bundle/acp-app/README.md @@ -1,19 +1,43 @@ +--- +description: "Automation-only ACP stdio application profile for users and maintainers launching persistent harness agents." +kind: "package-bundle" +--- + # `@deepseek-ai/dsh-acp-app` English | [中文](README.zh.md) +## Summary + The automation-only ACP stdio application as a `dsh` profile bundle over [`dsh-base`](../base/README.md). It inherits the base's disabled module-HMR policy; its patch sets the coding-agent persona and default model route, mounts an app-owned zero-option command provider, and starts [`dsh-acp`](../../acp/acp/README.md) only after that provider accepts the invocation. `dsh --profile acp --help` therefore writes help and exits without claiming stdin or stdout. +## Table of Contents + +- [Use this package](#use-this-package) +- [Standard automation workflow](#standard-automation-workflow) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) + +----- + + +## Use this package + The startup provider binds stdin EOF to the launcher's bounded successful shutdown. ACP connection close, SIGINT, and SIGTERM drain the bridge-owned agents and the root profile tree before exit. Stdout is reserved for newline-delimited ACP JSON-RPC frames. The bundle disables model-generated session titles because ACP exposes no title surface; deterministic fallback titles remain durable without an auxiliary model request. The inherited projection cache checkpoints ACP-created sessions for later consumers; its durability barrier flushes each covered log prefix before publishing the cache row and may split otherwise coalesced JSONL runs. A deployment selects a different complete composition through profile bundles and patch files, not another app bin. The shipped row creates sessions with `deepseek-official` and `deepseek-v4-flash`; a later patch can replace that row's complete config. The base profile owns adapters, tools, persistence, policy, settings, credentials, and the per-session workspace supplied by the ACP client. +----- + + ## Standard automation workflow An ACP v1 SDK client initializes `dsh --profile acp`, creates a session with an absolute `cwd` and optional standard stdio/HTTP MCP declarations, chooses an advertised `model` or `reasoning_effort`, prompts while observing standard semantic updates, then calls `session/close`. Another process can use `session/list` and `session/resume` against the same profile persistence root; resume reconnects the MCP declarations supplied by that request and does not replay history. The complete supported method matrix, MCP trust model, update mapping, and stop reasons live in the [`dsh-acp` protocol contract](../../acp/acp/README.md#standard-acp-v1-surface). This profile adds no private method, capability, `_meta`, environment variable, or transport field. The keyless control-surface conformance test drives the real profile through the public ACP SDK. + ## Model Experience ### ACP coding-agent persona @@ -32,6 +56,19 @@ Stable for a fixed profile, provider, model, and tool roster. Profile changes ta ## Known Limitations and Deferred Work + + - **A profile can omit the ACP bridge** — a custom ACP launch profile must retain this bundle or another `dsh-acp` row; otherwise no peer answers the client. - **User plugins can violate stdout purity** — profile and per-launch patches are trusted application composition. The shipped bundle writes no non-protocol stdout, but it cannot contain an arbitrary inserted plugin. - **Configuration changes require restart** — the shipped `acp` profile uses `patchReload: startup` so one stdio connection never observes a replacement bridge or Agent dependency. + + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/bundle/acp-app/README.zh.md b/packages/bundle/acp-app/README.zh.md index 13debf94c4..0669e080e0 100644 --- a/packages/bundle/acp-app/README.zh.md +++ b/packages/bundle/acp-app/README.zh.md @@ -1,19 +1,43 @@ +--- +description: "面向启动持久 harness agent 的用户与维护者,说明纯自动化 ACP stdio 应用 profile。" +kind: "package-bundle" +--- + # `@deepseek-ai/dsh-acp-app` [English](README.md) | 中文 +## 概述 + 以 [`dsh-base`](../base/README.zh.md) 为基础的 automation-only ACP stdio 应用 `dsh` profile 组合包。它继承 base 默认禁用模块 HMR(热模块替换)的策略;其 patch 设置 coding agent(编程智能体)persona 与默认模型路由、挂载应用自有的零选项命令提供方,并且只在该提供方接受调用后启动 [`dsh-acp`](../../acp/acp/README.zh.md)。因此,`dsh --profile acp --help` 会写出 help 并退出,不会占用 stdin 或 stdout。 +## 目录 + +- [使用本包](#use-this-package) +- [标准自动化工作流](#standard-automation-workflow) +- [模型体验](#model-experience) +- [已知限制与待办事项](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) + +----- + + +## 使用本包 + 启动提供方把 stdin EOF 绑定到启动器的有界成功关闭。ACP 连接关闭、SIGINT 与 SIGTERM 会在退出前排空 bridge 自有 agent 以及根 profile 树。Stdout 仅保留给换行分隔的 ACP JSON-RPC frame。ACP 不提供 title 表层,因此本组合包禁用模型生成的 session title;确定性的 fallback title 仍会持久化,但不发起辅助模型请求。继承的投影缓存会为 ACP 创建的会话写入检查点,供后续消费方使用;其持久性屏障会在发布缓存行前 flush 所覆盖的日志前缀,因此可能拆分原本会合并的 JSONL 行。部署方通过 profile 组合包与 patch 文件选择另一套完整组合,而不是使用另一个 app bin。 随附配置项使用 `deepseek-official` 与 `deepseek-v4-flash` 创建 session;后续 patch 可以替换该配置项的完整 config。base profile 负责适配器、工具、持久化、策略、settings 与 credentials;ACP client 为每个 session 提供工作区。 +----- + + ## 标准自动化工作流 ACP v1 SDK 客户端先初始化 `dsh --profile acp`,再用绝对 `cwd` 与可选的标准 stdio/HTTP MCP 声明创建 session,选择公开的 `model` 或 `reasoning_effort`,在观察标准语义更新的同时提交提示词,最后调用 `session/close`。另一个进程可以针对同一个 profile 持久化根目录使用 `session/list` 与 `session/resume`;resume 会重新连接该请求提供的 MCP 声明,但不会重放历史。 完整的受支持方法矩阵、MCP 信任模型、更新映射与停止原因见 [`dsh-acp` 协议约定](../../acp/acp/README.zh.md#standard-acp-v1-surface)。该 profile 不增加私有方法、能力、`_meta`、环境变量或传输字段。免密钥控制面一致性测试通过公开 ACP SDK 驱动真实 profile。 + ## 模型体验 ### ACP coding-agent persona @@ -32,6 +56,19 @@ ACP v1 SDK 客户端先初始化 `dsh --profile acp`,再用绝对 `cwd` 与可 ## 已知限制与待办事项 + + - **profile 可以省略 ACP bridge**:自定义 ACP 启动 profile 必须保留本组合包或另一个 `dsh-acp` 配置项;否则没有 peer 响应 client。 - **用户插件可能破坏 stdout 纯净性**:profile 与单次启动 patch 属于受信任的应用组合。随附组合包不会向 stdout 写入非协议内容,但无法约束任意插入的插件。 - **配置更改需要重启**:随附 `acp` profile 使用 `patchReload: startup`,确保一条 stdio 连接不会观察到 bridge 或 Agent 依赖被替换。 + + + +### 开发备注 + +
+维护者工作上下文——点击展开 + +无。 + +
diff --git a/packages/bundle/base/README.i18n.yaml b/packages/bundle/base/README.i18n.yaml index 0ab5140ece..21455662b4 100644 --- a/packages/bundle/base/README.i18n.yaml +++ b/packages/bundle/base/README.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 packages/bundle/base/README.md -README.md: 9fb1264f39aee3f3961ff4fd3c07f35b0b7cefdf -README.zh.md: 844254acc1f349a9debf38494e8d97f5f7e2d00b +README.md: 4fc828a5614245bf89867e2c52d1b2019e300921 +README.zh.md: 0d80938229624dd98536bf4798e1541fd84bd8e1 diff --git a/packages/bundle/base/README.md b/packages/bundle/base/README.md index 9fb1264f39..4fc828a561 100644 --- a/packages/bundle/base/README.md +++ b/packages/bundle/base/README.md @@ -1,24 +1,137 @@ -# `@deepseek-ai/dsh-base` +--- +description: "The shared dsh core: model access, tools, durable sessions, and safety defaults for every dsh --profile surface, for users composing or customizing a profile." +kind: "package-bundle" +--- + +# @deepseek-ai/dsh-base English | [中文](README.zh.md) -The shared dsh core as a profile bundle: [`cordis.patch.yml`](cordis.patch.yml) inserts every base plugin row — model adapters, the shared [`agent-default-model`](../../core/agent-default-model/README.md) selection, tools, persistence, policy, settings/credentials, telemetry, and the core spawn/fork subagent providers — over the empty profile root, as the first layer of each base-backed profile's `dsh.profile.bundles` list. The standalone [`sdk-minimal`](../sdk-minimal/README.md) profile deliberately does not include this bundle. The optional Codex and Claude Code providers stay outside this package and its production dependency closure; a Profile installs either [product provider Bundle](../../subagent/README.md) only when needed. The default `@deepseek-ai/dsh` production closure therefore includes neither product provider, the Claude Agent SDK, nor the Codex wrapper and platform payloads. Later bundle layers (e.g. [`dsh-web-app`](../web-app/README.md)) and the user's profile `cordis.patch.yml` override these rows by id; a patch replaces a row's whole `config`, so mode-specific values live in mode bundles, not here. The package has no runtime API; the profile composer resolves the patch through the `dsh.bundle.patch` manifest field, never through code. +## Summary -The base module-HMR row is disabled. A profile with a tested source-module reload lifecycle enables that row explicitly; `patchReload: live` config watching is independent and uses the launcher's watch-only fallback while module HMR remains disabled. +Every base-backed `dsh --profile` surface runs on `dsh-base`, so those surfaces share a model connection, the full tool set, durable session history, and workspace safety defaults. The shipped `sdk-minimal` profile deliberately uses a complete standalone tree instead. You rarely touch this bundle directly — shipped base-backed profiles already include it, and a custom base-backed profile names it first. When you need different defaults, change your profile patch or add a later bundle; this package is not a library you import. -The patch gates both shell stacks by platform on its own rows: `bash-sandbox`/`tool-bash` carry `disabled: !!js process.platform === 'win32'` (bash has no Windows runner), and their twins `pwsh-sandbox`/`tool-pwsh` mount on win32 only with the inverted expression — one shared patch file, exactly one shell stack per host. The permission surface stays exactly as on POSIX: `sandbox`/`sandbox-policy` enforce the file-effect policy through the Windows ACL restricted-token runner (the win32 chain of `dsh-sandbox-local` → `@deepseek-ai/dsh-sandbox-windows-acl`), the permission switcher and the approval service run unchanged, and `fs-sandbox` keeps fencing `ctx.fs` writes — mounting `dsh-fs-local` alongside it would double-register `ctx.fs` and fail the load. A Windows host that prefers the unconfined local pwsh executor or full access overrides these rows through its profile or home `cordis.patch.yml` (the bash-restore recipe must be complete: disable `pwsh-sandbox`/`tool-pwsh` AND re-enable `bash-sandbox`/`tool-bash` — both executor families register the same `bash` service, so an incomplete recipe fails loud at load). POSIX hosts see the pwsh rows disabled. +## Table of Contents -The row set and its rationale are documented inline in the patch file; the [generated composition graph](../../../apps/cli/composition.md) renders it. +- [Use this package](#use-this-package) +- [Understand the implementation](#understand-the-implementation) +- [Further Exploration](#further-exploration) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) +----- + + +## Use this package + +You get the dsh core automatically: the shipped `web` and `headless` profiles already include it, and a custom profile names it as its first bundle. After that, everything works with no further configuration. + +### A minimal custom profile + +To build a profile on the shared core, create a profile with a `package.json` that names `@deepseek-ai/dsh-base` first: + +```json +{ + "name": "my-profile", + "private": true, + "dsh": { + "profile": { + "bundles": ["@deepseek-ai/dsh-base"] + } + } +} +``` + +Run `dsh --profile my-profile "your task"` and you get a working agent with model access, tools, persistence, and the default permission policy. The shipped `web` and `headless` profiles are created for you on first use. To add more bundles, run `dsh plugin --profile add `; in-box bundles resolve from the dsh installation. The profile contract is documented in the [app-boot profile section](../../boot/app-boot/README.md). + +### What you get + +Out of the box, every profile built on this core provides: a DeepSeek model connection (the provider and model are configurable, and you can enable extra providers from your settings), the full tool set — file editing, shell commands, web search, subagents, task and goal tracking — durable sessions that survive restarts, and the default permission policy that confines file writes to your workspace and asks before risky actions. Telemetry stays off unless you opt in. + +### Shell tools per platform + +On macOS and Linux you get the bash shell tools; on Windows you get the PowerShell twins instead, so exactly one shell stack is available per machine. The safety behavior is identical on every platform. A Windows host that prefers the unconfined PowerShell executor can switch the shell rows in its profile patch — the switch must disable both PowerShell rows and re-enable both bash rows, otherwise the profile fails to load. + +### Changing the defaults + +To change what a profile built on this core provides — a different default model, a stricter permission mode, extra or fewer tools — edit your profile's `cordis.patch.yml` or add a later bundle. Each patch entry replaces the target's whole configuration, so restate every setting you want to keep. Keep the sandboxed filesystem provider as the single file-write path: adding the plain filesystem provider on top of it makes the profile fail to load. + +----- + + +## Understand the implementation + +
+Implementation internals — click to expand + +The bundle is a static patch document: one `insert` list applied over the empty profile root. It mounts no service, emits no events, and holds no mutable state; each inserted row's package owns that row's behavior and invariants. + +### Composition mechanics + +A patch replaces the targeted row's whole `config` rather than merging into it. Later bundle layers and the user's profile `cordis.patch.yml` override rows by id, with the last write winning per row. Rows whose value differs by mode do not live here: each mode bundle restates its complete configuration, keeping any single row down to one bundle layer plus the user's. The full row set and its rationale are documented inline in [`cordis.patch.yml`](cordis.patch.yml); the [generated composition graph](../../../apps/cli/composition.md) renders it. + +### Platform gating + +The patch gates the two shell stacks by platform on its own rows: `bash-sandbox` and `tool-bash` carry `disabled: !!js process.platform === 'win32'`, and their twins `pwsh-sandbox` and `tool-pwsh` mount on win32 only with the inverted expression. The permission surface stays identical to POSIX: the sandbox policy executes the same file-effect policy through the Windows ACL restricted-token runner (`dsh-sandbox-local` → `@deepseek-ai/dsh-sandbox-windows-acl`), and `fs-sandbox` keeps fencing `ctx.fs` writes — mounting `dsh-fs-local` alongside it would double-register `ctx.fs` and fail the load. + +### Source map + +| File | Role | +|---|---| +| [`cordis.patch.yml`](cordis.patch.yml) | The bundle substance: the base plugin rows, with per-row rationale as inline comments | +| [`src/index.ts`](src/index.ts) | Package entry; carries no runtime API | +| [`src/invariant.ts`](src/invariant.ts) | Invariant companion: no runtime invariant; each inserted row's package owns its invariants | +| [`tests/base.spec.ts`](tests/base.spec.ts) | Manifest declaration and platform-gating checks | + +### Invariant ownership + +The invariant companion registers an empty installer because the package is a static patch-list carrier: each inserted row's own package carries that row's invariants, and the bundle owns no mutable relation to check. + +
+ +----- + + +## Further Exploration + +Read these pages when you want to go deeper into profiles, the surfaces built on this core, or the exact composition. + +- [app-boot profile section](../../boot/app-boot/README.md) — how profiles are resolved, layered, and customized. +- [Bundle package map](../README.md) — the surfaces built on this core. +- [Generated composition graph](../../../apps/cli/composition.md) — the exact plugin set each shipped profile uses. +- [Profile plugin bundles note](../../../.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.md) — the profile and bundle composition design. +- [Codex and Claude Code provider bundles](../../subagent/README.md) — optional provider bundles you can install on top. + +----- + + ## Model Experience -Indirectly, through the inserted rows: this bundle selects the shipped persona-less prompt base, tool set, and DeepSeek adapter that mode bundles specialize, and contributes no model-visible text of its own. +Indirectly, through each inserted row's package, which owns that row's model-facing behavior. #### KV Cache effect -None directly; each inserted row's package owns its effect. +The bundle itself adds no request prefix; each inserted row's package owns any cache effect. ## Known Limitations and Deferred Work -- **A patch replaces whole row configs** — profile overrides must restate every field a row keeps; there is no deep-merge layer. -- **The Windows temp grant is a private per-session subdirectory** — `workspace-write` confines writes to the workspace plus the session's own temp subdirectory (`\dsh-`, TMP/TEMP rewritten for confined children); `read-only` grants nothing. See `@deepseek-ai/dsh-sandbox-windows-acl`. + + + +These limits tell you when the core needs extra care or where an override must go. They are current package constraints, not a general comparison or a task backlog. + +- **Overrides replace whole settings blocks** — a patch entry replaces the target's entire configuration, so your override must restate every setting you want to keep; nothing merges automatically. +- **Per-surface settings belong to the surface's bundle** — a default that differs between the web GUI and headless mode lives in that surface's bundle, not in the shared core. +- **Windows temp grants are private per-session subdirectories** — `workspace-write` confines writes to the workspace plus the session's own temp subdirectory (`\dsh-`, TMP/TEMP rewritten for confined children); `read-only` grants nothing. See `@deepseek-ai/dsh-sandbox-windows-acl`. +- **Adding the plain filesystem provider on top of the sandboxed one fails the profile** — the two register the same service, so the profile refuses to load; use one or the other. + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/bundle/base/README.zh.md b/packages/bundle/base/README.zh.md index 844254acc1..0d80938229 100644 --- a/packages/bundle/base/README.zh.md +++ b/packages/bundle/base/README.zh.md @@ -1,24 +1,137 @@ -# `@deepseek-ai/dsh-base` +--- +description: "共享的 dsh 核心:为每个 dsh --profile 表层提供模型访问、工具、持久会话与安全默认值,供用户组合或定制 profile。" +kind: "package-bundle" +--- + +# @deepseek-ai/dsh-base [English](README.md) | 中文 -以 profile 组合包形式交付的共享 dsh 核心:[`cordis.patch.yml`](cordis.patch.yml) 在空的 profile 根之上插入全部基础插件行——模型适配器、共享的 [`agent-default-model`](../../core/agent-default-model/README.zh.md) 选择、工具、持久化、策略、settings/credentials、遥测与核心 spawn/fork subagent provider——作为每个基于 base 的 profile 的 `dsh.profile.bundles` 列表中的第一层。独立的 [`sdk-minimal`](../sdk-minimal/README.zh.md) profile 刻意不包含本组合包。可选的 Codex 与 Claude Code provider 不属于本包及其生产依赖闭包;Profile 仅在需要时安装任一[产品 provider Bundle](../../subagent/README.zh.md)。因此,默认的 `@deepseek-ai/dsh` 生产依赖闭包既不包含任一产品 provider、Claude Agent SDK,也不包含 Codex wrapper 及其平台载荷。后续的组合包层(例如 [`dsh-web-app`](../web-app/README.zh.md))和用户 profile 的 `cordis.patch.yml` 按 id 覆盖这些行;patch 会替换目标行的整个 `config`,因此模式专属的值放在各模式组合包中,而不是这里。该包没有运行时 API;profile 组合器通过 manifest(元数据清单)的 `dsh.bundle.patch` 字段解析 patch,绝不通过代码。 +## 概述 -base 的模块 HMR 配置项默认禁用。具有经过验证的源码模块重载生命周期的 profile 必须显式启用该配置项;`patchReload: live` 配置监视与之独立,在模块 HMR 保持禁用时使用启动器的仅监视 fallback。 +每个基于 base 的 `dsh --profile` 表层都运行在 `dsh-base` 上,因此这些表层共享模型连接、完整工具集、持久会话历史和 workspace 安全默认值。随附的 `sdk-minimal` profile 刻意改用完整的独立配置树。你通常不直接操作本 bundle——随附的 base-backed profile 已经包含它,自定义 base-backed profile 则把它放在第一位。需要其他默认值时,应修改自己的 profile patch 或添加后续 bundle;本包不是供导入的库。 -patch 在自身上按平台门控两个 shell 栈:`bash-sandbox`/`tool-bash` 携带 `disabled: !!js process.platform === 'win32'`(bash 没有 Windows runner),它们的孪生行 `pwsh-sandbox`/`tool-pwsh` 以取反的表达式仅在 win32 挂载——同一份 patch 文件,每个宿主恰好挂载一个 shell 栈。权限面与 POSIX 完全一致:`sandbox`/`sandbox-policy` 通过 Windows ACL 受限令牌 runner(`dsh-sandbox-local` 的 win32 链 → `@deepseek-ai/dsh-sandbox-windows-acl`)执行文件效果策略,权限切换器与 approval 服务原样运行,`fs-sandbox` 继续围栏 `ctx.fs` 写入——在其旁再挂载 `dsh-fs-local` 会重复注册 `ctx.fs` 并在加载时失败。偏好不受沙盒约束的本地 pwsh 执行器或完整访问的 Windows 主机通过其 profile 或 home 的 `cordis.patch.yml` 覆盖这些行(bash 恢复配方必须完整:禁用 `pwsh-sandbox`/`tool-pwsh` 并重新启用 `bash-sandbox`/`tool-bash`——两个执行器家族注册同一个 `bash` 服务,配方不完整会在加载时直接报错)。POSIX 主机看到的是被禁用的 pwsh 行。 +## 目录 -行集合及其设计依据以行内注释写在 patch 文件里;[生成的组合图](../../../apps/cli/composition.md)负责渲染它。 +- [使用本包](#use-this-package) +- [理解实现](#understand-the-implementation) +- [进一步探索](#further-exploration) +- [模型体验](#model-experience) +- [已知限制与延期工作](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) +----- + + +## 使用本包 + +你会自动获得 dsh 核心:随发行版交付的 `web` 与 `headless` profile 已包含它,自定义 profile 则把它列为第一个组合包。之后一切无需任何额外配置即可工作。 + +### 最小自定义 profile + +要在共享核心之上构建 profile,请创建一个 profile,其 `package.json` 把 `@deepseek-ai/dsh-base` 列在首位: + +```json +{ + "name": "my-profile", + "private": true, + "dsh": { + "profile": { + "bundles": ["@deepseek-ai/dsh-base"] + } + } +} +``` + +运行 `dsh --profile my-profile "your task"`,你就得到一个可用的 agent(智能体),带模型访问、工具、持久化与默认权限策略。随发行版交付的 `web` 与 `headless` profile 会在首次使用时为你创建。要添加更多组合包,运行 `dsh plugin --profile add `;内置组合包从 dsh 安装目录解析。profile 约定见 [app-boot 的 profile 章节](../../boot/app-boot/README.zh.md)。 + +### 你得到什么 + +开箱即用,基于本核心构建的每个 profile 都提供:DeepSeek 模型连接(provider 与模型可配置,你还可以在设置中启用额外 provider)、完整工具集——文件编辑、shell 命令、web 搜索、subagent、任务与目标跟踪——可跨重启存活的持久会话,以及默认权限策略:把文件写入限制在工作区内,危险操作前征询许可。遥测默认关闭,除非你主动开启。 + +### 各平台的 shell 工具 + +在 macOS 与 Linux 上你获得 bash shell 工具;在 Windows 上则获得对应的 PowerShell 孪生工具,因此每台机器恰好有一套 shell 栈。各平台的安全行为完全一致。偏好不受沙盒约束的 PowerShell 执行器的 Windows 主机可以在其 profile patch 中切换 shell 行——切换必须同时禁用两个 PowerShell 行并重新启用两个 bash 行,否则 profile 无法加载。 + +### 更改默认值 + +要改变基于本核心构建的 profile 提供的内容——不同的默认模型、更严格的权限模式、更多或更少的工具——请编辑 profile 的 `cordis.patch.yml` 或添加后面的组合包。每个 patch 条目会替换目标的整个配置,因此请重述每个想保留的设置。保持沙箱化文件系统提供方作为唯一的文件写入路径:在其之上再添加普通文件系统提供方会导致 profile 加载失败。 + +----- + + +## 理解实现 + +
+实现细节——点击展开 + +本组合包是一份静态 patch 文档:一个应用到空 profile 根之上的 `insert` 列表。它不挂载任何服务、不发出任何事件、也不持有任何可变状态;每条插入行所属的包负责该行的行为与不变式。 + +### 组合机制 + +patch 会替换目标行的整个 `config`,而不是合并进它。后续组合包层与用户的 profile `cordis.patch.yml` 按 id 覆盖行,每行最后一次写入生效。按模式取值不同的行不属于这里:每个模式组合包重述自己的完整配置,让任何单一行最多只属于一个组合包层加用户层。完整行集合及其设计依据以行内注释写在 [`cordis.patch.yml`](cordis.patch.yml) 里;[生成的组合图](../../../apps/cli/composition.md)负责渲染它。 + +### 平台门控 + +patch 在自身上按平台门控两个 shell 栈:`bash-sandbox` 与 `tool-bash` 携带 `disabled: !!js process.platform === 'win32'`,孪生行 `pwsh-sandbox` 与 `tool-pwsh` 以取反的表达式仅在 win32 挂载。权限面与 POSIX 完全一致:沙箱策略通过 Windows ACL 受限令牌 runner(`dsh-sandbox-local` → `@deepseek-ai/dsh-sandbox-windows-acl`)执行相同的文件效果策略,`fs-sandbox` 继续围栏 `ctx.fs` 写入——在其旁再挂载 `dsh-fs-local` 会重复注册 `ctx.fs` 并在加载时失败。 + +### 源码地图 + +| 文件 | 职责 | +|---|---| +| [`cordis.patch.yml`](cordis.patch.yml) | 组合包的实体:基础插件行,附以行内注释说明各行依据 | +| [`src/index.ts`](src/index.ts) | 包入口;不携带任何运行时 API | +| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件:无运行时不变式;每条插入行所属的包负责自己的不变式 | +| [`tests/base.spec.ts`](tests/base.spec.ts) | manifest 声明与平台门控检查 | + +### 不变式归属 + +不变式伴生插件注册一个空安装器,因为本包是静态 patch 列表载体:每条插入行由所属的包携带其不变式,组合包自身没有任何可审计的可变关系。 + +
+ +----- + + +## 进一步探索 + +当你想深入了解 profile、基于本核心构建的表层或确切组合时,阅读以下页面。 + +- [app-boot 的 profile 章节](../../boot/app-boot/README.zh.md)——profile 如何解析、分层与定制。 +- [组合包包映射](../README.zh.md)——基于本核心构建的表层。 +- [生成组合图](../../../apps/cli/composition.md)——每个已发布 profile 使用的确切插件集合。 +- [Profile 组合包设计笔记](../../../.agents/notes/implemented/architecture/2026-08-05-profile-plugin-bundles.zh.md)——profile 与组合包的组合设计。 +- [Codex 与 Claude Code provider 组合包](../../subagent/README.zh.md)——可叠加安装的可选 provider 组合包。 + +----- + + ## 模型体验 -通过插入的行间接产生影响:该组合包选定了随发行版交付的无 persona 提示词基座、工具集合与 DeepSeek 适配器,供各模式组合包进一步特化;它自身不贡献任何模型可见文本。 +通过每条插入行所属的包间接产生影响,由各包负责其行的模型可见行为。 #### KV Cache 影响 -无直接影响;每条插入行的影响由其所属的包负责。 +组合包本身不添加任何请求前缀;每条插入行所属的包负责各自的缓存影响。 -## 已知限制与暂缓事项 +## 已知限制与延期工作 -- **patch 会替换整行 `config`**:profile 覆盖必须重述该行需要保留的每个字段;不存在深度合并层。 + + + +这些限制告诉你核心何时需要额外注意、覆盖应放在哪里。它们是当前包约束,不是通用对比或任务积压。 + +- **覆盖会替换整个设置块**——patch 条目会替换目标的整个配置,因此你的覆盖必须重述每个想保留的设置;不会自动合并。 +- **按表层的设置属于该表层的组合包**——web GUI 与 headless 模式取值不同的默认值放在对应表层的组合包里,而不是共享核心。 - **Windows 的临时目录授权是按会话的私有子目录**——`workspace-write` 把写入限制在工作区与会话自己的 temp 子目录(`\dsh-`,受限子进程的 TMP/TEMP 被改写);`read-only` 不授予任何临时目录写入权限。见 `@deepseek-ai/dsh-sandbox-windows-acl`。 +- **在沙箱化文件系统提供方之上添加普通提供方会导致 profile 失败**——两者注册同一个服务,profile 因此拒绝加载;二选一。 + + +### 开发备注 + +
+维护者的工作上下文——点击展开 + +无。 + +
diff --git a/packages/bundle/headless/README.i18n.yaml b/packages/bundle/headless/README.i18n.yaml index 5ec404e814..fc0be1d54b 100644 --- a/packages/bundle/headless/README.i18n.yaml +++ b/packages/bundle/headless/README.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 packages/bundle/headless/README.md -README.md: 516b21ca4b31fcd8c69b7bdb1f0d5fe60d5e43e2 -README.zh.md: 37781a79629dd1a183045ef533eb9849b2b0219a +README.md: 952ae369e58b60389a951f2f48a080d735a3d9ad +README.zh.md: 8d9084550fd295c19b679b64cf17f0e2cdafbeb6 diff --git a/packages/bundle/headless/README.md b/packages/bundle/headless/README.md index 516b21ca4b..952ae369e5 100644 --- a/packages/bundle/headless/README.md +++ b/packages/bundle/headless/README.md @@ -1,24 +1,136 @@ -# `@deepseek-ai/dsh-headless` +--- +description: "One-shot task mode for dsh: run a single task from the command line and get the final answer printed, for users scripting or automating dsh." +kind: "package-bundle" +--- + +# @deepseek-ai/dsh-headless English | [中文](README.zh.md) -The dsh one-shot bundle. [`cordis.patch.yml`](cordis.patch.yml) rides directly over [`dsh-base`](../base/README.md): it inherits the base's disabled module-HMR policy and projection cache, supplies the coding persona and tool mode, mounts Code Mode's worker as a core execution capability, and inserts this package's `headless-runner` plugin (config `{task}`, resolved from the injected `headlessStartup` provider). It mounts no Host, HTTP server, Web runtime, or browser plugin. The cache checkpoints each persisted one-shot session for later consumers; its durability barrier flushes each covered log prefix before publishing the cache row and may split otherwise coalesced JSONL runs. +## Summary -After the Loader settles, the runner reads the shared [`ctx.agentDefaultModel`](../../core/agent-default-model/README.md), creates one fresh persisted Agent through `ctx.agents`, submits the task as an ordinary user message, and waits for quiescence. Each non-empty provider reasoning delta from that Agent is written to stderr as it arrives under a `dsh: reasoning:` heading; consecutive deltas remain one section, and the runner terminates the section before later output when the provider supplied no trailing newline. It then flushes the Session before folding the owned durable event interval, writes the last non-empty assistant text to stdout, and requests exit through the launcher-provided `ctx.appExit` host hook ([`dsh-cmdline`](../../boot/cmdline/README.md)) (final `turn/end` completed → 0, otherwise 1). A terminal `error` reason also writes its code and message to stderr; a successful run with no reasoning keeps stderr empty. The process opens no listening port. +`dsh-headless` runs one dsh task from the command line and prints the final answer, then exits — no GUI, no server, no browser. Type `dsh --profile headless "run the tests"` and the agent works through the task with the same model, tools, and safety defaults as every other surface. It is ideal for scripts, CI, and one-off jobs: the process opens no ports and leaves nothing running behind. The exit code tells you the outcome — 0 when the task completed, 1 when it aborted or errored. The main boundary: one task per invocation, with no interactive follow-up. -The task text is this app's command line: the ordinary `headless-startup` provider ([`src/startup.ts`](src/startup.ts)) injects `ctx.cmdlineArgs` ([`dsh-cmdline`](../../boot/cmdline/README.md)), reads the positional argument of `dsh --profile headless "task"`, prints the app's `--help`, and provides `headlessStartup`; the runner injects that service and reads its task from lazy config. A missing or whitespace-only task is rejected before the runner activates. +## Table of Contents +- [Use this package](#use-this-package) +- [Understand the implementation](#understand-the-implementation) +- [Further Exploration](#further-exploration) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) + +----- + + +## Use this package + +Run one task, get the final answer, and exit. The task is the command line itself, so the whole invocation is the smallest working example. + +### Running a one-shot task + +```sh +dsh --profile headless "run the tests" +``` + +The agent works through the task, streams each non-empty provider reasoning delta to stderr under a `dsh: reasoning:` heading, then prints the final answer on stdout and exits. Consecutive reasoning deltas stay in one section, and the runner closes that section before later output when the provider supplied no trailing newline. A successful run without reasoning keeps stderr empty; a failure exits 1 and prints `dsh: : ` to stderr. A missing or blank task is rejected before anything runs. The task text is supplied through the single `task` setting: + +| Field | Default | Meaning | +|---|---|---| +| `task` | required | The task text for the single run | + +The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-headless) is the exhaustive source for every accepted field and its JSDoc. + +### When to use it + +Use headless for scripted or automated dsh runs — CI steps, batch jobs, quick answers from a terminal. Avoid it when you need a multi-turn interactive session or a GUI; the browser surface ([dsh-web-app](../web-app/README.md)) serves that. The process stays alive only for the run, opens no listening port, and exits on its own, so it fits pipelines that wait on the process. + +### Help and task errors + +`dsh --profile headless --help` prints the command's help text and exits without running anything. A missing or whitespace-only task is a usage error: nothing runs and the process exits 1. + +----- + + +## Understand the implementation + +
+Implementation internals — click to expand + +The runner is a direct driver over the core API carrier: it creates one fresh Agent through the registry and folds the owned durable event interval into one process-level outcome. + +### Run flow + +The runner awaits the complete application (`ctx.get('loader')?.await()`) so the composed tools and adapters are not half-mounted, reads the shared [`agentDefaultModel`](../../core/agent-default-model/README.md) selection, creates one fresh persisted Agent with that provider and model, and submits the task as an ordinary user message. It streams that Agent's non-empty reasoning deltas to stderr, waits for quiescence, then flushes the Session and folds the owned interval (`firstSeq` onward) into the last non-empty `assistant/message` text and final `turn/end` reason. It writes the final text to stdout and requests exit. + +### Patch surface over base + +The patch rides over `dsh-base`: it inherits the projection cache, sets the coding persona on the base `system-prompt` row, keeps the same temporary process-wide Code Mode opt-in (`DSH_TOOLS_MODE`) as the Web surface, disables the shared HMR row, inserts Code Mode's worker as a core execution capability, and mounts the startup provider and the runner. The cache checkpoints each persisted one-shot session for later consumers; its durability barrier flushes each covered log prefix before publishing the cache row and may split otherwise coalesced JSONL runs. The startup provider ([`src/startup.ts`](src/startup.ts)) injects `ctx.cmdlineArgs` ([`dsh-cmdline`](../../boot/cmdline/README.md)), reads the positional argument, prints the app's `--help`, and provides `headlessStartup`; the runner injects that service and reads its task from lazy config. + +### Exit mapping + +A completed final `turn/end` exits 0; any other outcome — aborted, error, or no turn in the owned interval — exits 1. An `error` reason also writes `dsh: : ` to stderr. A direct driver failure (for example, Agent creation) writes `dsh: ` to stderr and exits 1. + +### Source map + +| File | Role | +|---|---| +| [`src/index.ts`](src/index.ts) | The `headless-runner` plugin: run flow, output contract, exit mapping | +| [`src/startup.ts`](src/startup.ts) | The `headless-startup` provider: task positional and `--help` | +| [`cordis.patch.yml`](cordis.patch.yml) | The one-shot patch over `dsh-base` | +| [`src/invariant.ts`](src/invariant.ts) | Invariant companion: no runtime invariant; the observable contract is process-level | +| [`tests/headless.spec.ts`](tests/headless.spec.ts) | Run flow, aggregation, flush, and exit mapping | +| [`tests/startup.spec.ts`](tests/startup.spec.ts) | Command-line parsing over a real Loader tree | + +### Invariant ownership + +The invariant companion registers an empty installer because the runner's observable contract (final text on stdout, exit code by turn-end reason) is process-level and owned by the launcher e2e; the plugin registers nothing and holds no mutable relation to audit inside the tree. + +
+ +----- + + +## Further Exploration + +Read these pages when you want to go deeper into the shared core, the sibling GUI, or the command-line handoff. + +- [Bundle package map](../README.md) — the surfaces built on the same core. +- [dsh-base](../base/README.md) — the shared core headless runs on. +- [dsh-web-app](../web-app/README.md) — the interactive browser sibling for multi-turn work. +- [dsh-cmdline](../../boot/cmdline/README.md) — how the launcher hands the command line to the app. +- [Generated configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-headless) — every accepted config field and its source declaration. + +----- + + ## Model Experience -None, as the runner submits the task as an ordinary user message; prompts and tools belong to the base and headless bundle rows. +None, as the runner submits the task as an ordinary user message and the composed base and headless rows own the prompts and tools. #### KV Cache effect -None; the runner adds nothing to the request prefix. +The runner adds nothing to the request prefix; it only drives one user message through the composed tree. ## Known Limitations and Deferred Work -- **One submitted task only** — the runner has no interactive follow-up surface; it waits through any work the Agent completes before returning to idle and prints the last non-empty assistant message in that interval. -- **No pre-token heartbeat** — stderr remains silent until the provider emits a non-empty reasoning delta; a provider that delays its first streamed token exposes no earlier progress signal. -- **Reasoning enters stderr logs** — redirection and supervisors may retain substantially more and potentially sensitive model output; route stderr to a controlled sink when that content must not be collected. -- **`ctx.appExit` is launcher-owned** — booting the headless profile outside the `dsh` launcher fails loud at activation until the host provides the exit request. + + + +These limits tell you when headless does not fit and what it needs from the `dsh` launcher. They are current package constraints, not a general CLI comparison or a task backlog. + +- **One task per run** — after the task is answered the process exits; there is no interactive follow-up, so split multi-step work into separate runs. +- **Runs through the `dsh` launcher** — starting the headless profile another way fails at startup, because only the launcher can request the process exit. +- **No pre-token heartbeat** — stderr stays silent until the provider emits a non-empty reasoning delta; a delayed first token exposes no earlier progress signal. +- **Reasoning enters stderr logs** — redirection and supervisors may retain substantially more and potentially sensitive model output; route stderr to a controlled sink when needed. +- **Only reasoning and the final answer are printed** — a run without an assistant message prints an empty stdout line and exits 1; intermediate tool output is not printed. + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/bundle/headless/README.zh.md b/packages/bundle/headless/README.zh.md index 37781a7962..8d9084550f 100644 --- a/packages/bundle/headless/README.zh.md +++ b/packages/bundle/headless/README.zh.md @@ -1,24 +1,136 @@ -# `@deepseek-ai/dsh-headless` +--- +description: "dsh 的一次性任务模式:从命令行运行单个任务并打印最终答案,供用户脚本化或自动化 dsh。" +kind: "package-bundle" +--- + +# @deepseek-ai/dsh-headless [English](README.md) | 中文 -dsh 一次性任务组合包。[`cordis.patch.yml`](cordis.patch.yml) 直接叠加在 [`dsh-base`](../base/README.zh.md) 之上:继承 base 默认禁用模块 HMR(热模块替换)的策略与投影缓存,提供编码 persona 和工具模式,将 Code Mode 的 worker 作为核心执行能力挂载,并插入本包的 `headless-runner` 插件(配置为 `{task}`,从注入的 `headlessStartup` 提供方解析)。它不挂载任何 Host、HTTP server、Web runtime 或浏览器插件。缓存会为每个持久化的一次性会话写入检查点,供后续消费方使用;其持久性屏障会在发布缓存行前 flush 所覆盖的日志前缀,因此可能拆分原本会合并的 JSONL 行。 +## 概述 -Loader 结算后,runner 读取共享的 [`ctx.agentDefaultModel`](../../core/agent-default-model/README.zh.md),通过 `ctx.agents` 创建一个全新的持久化 Agent(智能体),将任务作为普通用户消息提交,并等待完全停稳。该 Agent 每次产生非空的提供方推理分片时,runner 都会在 `dsh: reasoning:` 标题下将其即时写入 stderr;连续分片保留在同一段中,提供方没有输出末尾换行时,runner 会在后续输出前终止该段。随后,它对 Session 执行 flush,再汇总自身持有的持久化事件区间,将最后一条非空 assistant 文本写入 stdout,并经启动器提供的 `ctx.appExit` 宿主钩子([`dsh-cmdline`](../../boot/cmdline/README.zh.md))请求退出(最终 `turn/end` 完成 → 0,否则为 1)。最终结束原因为 `error` 时,还会将 code 与 message 写入 stderr;没有推理内容的成功运行会保持 stderr 为空。进程不会打开监听端口。 +`dsh-headless` 从命令行运行一个 dsh 任务并打印最终答案,然后退出——没有 GUI、没有服务器、没有浏览器。输入 `dsh --profile headless "run the tests"`,agent(智能体)会以与其他表层相同的模型、工具与安全默认值完成该任务。它非常适合脚本、CI 与一次性任务:进程不打开任何端口,也不会留下任何后台运行的东西。退出码告诉你结果——任务完成时为 0,中止或出错时为 1。主要边界:每次调用只运行一个任务,没有交互式后续。 -任务文本就是这个应用的命令行:普通 `headless-startup` 提供方([`src/startup.ts`](src/startup.ts))注入 `ctx.cmdlineArgs`([`dsh-cmdline`](../../boot/cmdline/README.zh.md)),读取 `dsh --profile headless "task"` 的位置参数、打印应用自己的 `--help`,并提供 `headlessStartup`;runner 注入该服务,再从惰性配置中读取任务。缺失或只有空白的任务会在 runner 激活前被拒绝。 +## 目录 +- [使用本包](#use-this-package) +- [理解实现](#understand-the-implementation) +- [进一步探索](#further-exploration) +- [模型体验](#model-experience) +- [已知限制与延期工作](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) + +----- + + +## 使用本包 + +运行一个任务,获得最终答案,然后退出。任务就是命令行本身,因此整条命令就是最小的可运行示例。 + +### 运行一次性任务 + +```sh +dsh --profile headless "run the tests" +``` + +agent(智能体)会完成该任务,把提供方的每个非空推理增量流式写入 stderr 的 `dsh: reasoning:` 段,然后把最终答案写入 stdout 并退出。连续推理增量保持在同一段中;提供方未给尾换行时,runner 会在后续输出前结束该段。没有推理内容的成功运行保持 stderr 为空;失败时退出码为 1,并以 `dsh: : ` 向 stderr 写入错误。缺失或空白任务会在任何内容运行前被拒绝。任务文本通过唯一的 `task` 设置提供: + +| 字段 | 默认值 | 含义 | +|---|---|---| +| `task` | 必填 | 单次运行的任务文本 | + +生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-headless)是每个受支持字段及其 JSDoc 的穷尽式真源。 + +### 何时使用 + +在脚本化或自动化的 dsh 运行中使用 headless——CI 步骤、批处理任务、从终端快速获取答案。当需要多轮交互会话或 GUI 时请避免它;浏览器表层([dsh-web-app](../web-app/README.zh.md))负责这类场景。进程只为本次运行而存活,不打开监听端口,并且自行退出,因此适合等待进程结束的流水线。 + +### 帮助与任务错误 + +`dsh --profile headless --help` 打印该命令的帮助文本并直接退出,不运行任何内容。缺失或只有空白的任务属于用法错误:什么都不运行,进程退出 1。 + +----- + + +## 理解实现 + +
+实现细节——点击展开 + +runner 是核心 API 载体之上的直接驱动器:它通过注册表创建一个全新的 Agent(智能体),并把所属的持久化事件区间折叠成一个进程级结果。 + +### 运行流程 + +runner 等待整个应用结算(`ctx.get('loader')?.await()`),确保已组合的工具与适配器不会半挂载,读取共享的 [`agentDefaultModel`](../../core/agent-default-model/README.zh.md) 选择,用该 provider 与模型创建一个全新的持久化 Agent(智能体),并把任务作为普通用户消息提交。它把该 Agent 的非空推理增量流式写入 stderr、等待完全停稳,然后 flush Session,并把所属区间(从 `firstSeq` 起)折叠为最后一条非空 `assistant/message` 文本与最终 `turn/end` 原因。最后,它把最终文本写入 stdout 并请求退出。 + +### 叠加在 base 之上的 patch 表层 + +patch 叠加在 `dsh-base` 之上:继承投影缓存,在基础 `system-prompt` 行上设置编码 persona,保留与 Web 表层相同的临时进程级 Code Mode 开关(`DSH_TOOLS_MODE`),禁用共享的 HMR 行,把 Code Mode 的 worker 作为核心执行能力插入,并挂载启动提供方与 runner。缓存为每个已持久化的一次性会话写入检查点,供后续消费方使用;其持久性屏障会在发布缓存行前 flush 所覆盖的日志前缀,因此可能拆分原本会合并的 JSONL 行。启动提供方([`src/startup.ts`](src/startup.ts))注入 `ctx.cmdlineArgs`([`dsh-cmdline`](../../boot/cmdline/README.zh.md)),读取位置参数、打印应用自己的 `--help`,并提供 `headlessStartup`;runner 注入该服务,再从惰性配置中读取任务。 + +### 退出映射 + +最终 `turn/end` 完成时退出码为 0;任何其他结果——aborted、error,或所属区间内没有轮次——退出码为 1。结束原因为 `error` 时还会向 stderr 写入 `dsh: : `。直接驱动器失败(例如 Agent 创建失败)向 stderr 写入 `dsh: ` 并退出 1。 + +### 源码地图 + +| 文件 | 职责 | +|---|---| +| [`src/index.ts`](src/index.ts) | `headless-runner` 插件:运行流程、输出约定、退出映射 | +| [`src/startup.ts`](src/startup.ts) | `headless-startup` 提供方:任务位置参数与 `--help` | +| [`cordis.patch.yml`](cordis.patch.yml) | 叠加在 `dsh-base` 之上的一次性 patch | +| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件:无运行时不变式;可观察约定是进程级的 | +| [`tests/headless.spec.ts`](tests/headless.spec.ts) | 运行流程、汇总、flush 与退出映射 | +| [`tests/startup.spec.ts`](tests/startup.spec.ts) | 在真实 Loader 树上的命令行解析 | + +### 不变式归属 + +不变式伴生插件注册一个空安装器,因为 runner 的可观察约定(stdout 的最终文本、按轮次结束原因决定的退出码)是进程级的、由启动器 e2e 负责;插件不注册任何内容,树内也没有任何可变关系可审计。 + +
+ +----- + + +## 进一步探索 + +当你想深入了解共享核心、兄弟 GUI 或命令行交接时,阅读以下页面。 + +- [组合包包映射](../README.zh.md)——基于同一核心构建的表层。 +- [dsh-base](../base/README.zh.md)——headless 运行其上的共享核心。 +- [dsh-web-app](../web-app/README.zh.md)——用于多轮工作的交互式浏览器兄弟表层。 +- [dsh-cmdline](../../boot/cmdline/README.zh.md)——启动器如何把命令行交给应用。 +- [生成配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-headless)——每个受支持配置字段及其源声明。 + +----- + + ## 模型体验 -无影响,因为 runner 把任务作为普通用户消息提交;提示词与工具由 base 和 headless 组合包中的相应条目提供。 +无,因为 runner 把任务作为普通用户消息提交,提示词与工具由组合出的 base 与 headless 行提供。 #### KV Cache 影响 -无;runner 不向请求前缀添加任何内容。 +runner 不向请求前缀添加任何内容;它只是把一条用户消息驱动经过组合出的配置树。 -## 已知限制与暂缓事项 +## 已知限制与延期工作 -- **只提交一个任务**:runner 没有用于交互式后续输入的 surface;它会等待 Agent 在返回 idle 前完成的所有工作,并打印该区间内最后一条非空 assistant 消息。 -- **首个 token 前没有心跳**:在提供方发出非空推理分片前,stderr 保持静默;如果提供方延迟首个流式 token,系统不会提供更早的进度信号。 -- **推理会进入 stderr 日志**:重定向与监督进程可能保留明显更多且可能敏感的模型输出;不得收集该内容时,应将 stderr 送往受控目标。 -- **`ctx.appExit` 由启动器持有**:在 `dsh` 启动器之外启动 headless profile 会在激活时明确报错,直到宿主提供该退出请求。 + + + +这些限制告诉你 headless 何时不适用、它需要 `dsh` 启动器提供什么。它们是当前包约束,不是通用的 CLI 对比或任务积压。 + +- **每次运行一个任务**——任务得到回答后进程即退出;没有交互式后续,因此多步工作请拆成多次运行。 +- **通过 `dsh` 启动器运行**——以其他方式启动 headless profile 会在启动时失败,因为只有启动器能请求进程退出。 +- **首个 token 前没有心跳**——提供方发出第一个非空推理增量前,stderr 保持静默;延迟首个 token 的提供方不会更早给出进度信号。 +- **推理进入 stderr 日志**——重定向与监督进程可能保留更多且可能敏感的模型输出;需要时应把 stderr 路由到受控位置。 +- **只打印推理和最终答案**——没有 assistant 消息的运行向 stdout 打印空行并以 1 退出;中间工具输出不会打印。 + + +### 开发备注 + +
+维护者的工作上下文——点击展开 + +无。 + +
diff --git a/packages/bundle/sdk-app/README.i18n.yaml b/packages/bundle/sdk-app/README.i18n.yaml index 9da7f6576a..83a58e47bb 100644 --- a/packages/bundle/sdk-app/README.i18n.yaml +++ b/packages/bundle/sdk-app/README.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 packages/bundle/sdk-app/README.md -README.md: 5c65c4a318683d51e930473a9715d290e331e60a -README.zh.md: 935e2d88ce7730c6f6cef39dc9ff3d1ddb644bb4 +README.md: 36b1964d6a22a3fa1ddc384cdf5f973b51fc1ca4 +README.zh.md: b4871e937ec1f6a7e8b03ac09a0bd03bbb0f55ca diff --git a/packages/bundle/sdk-app/README.md b/packages/bundle/sdk-app/README.md index 5c65c4a318..36b1964d6a 100644 --- a/packages/bundle/sdk-app/README.md +++ b/packages/bundle/sdk-app/README.md @@ -1,8 +1,27 @@ +--- +description: "SDK stdio application profile for users and maintainers launching a JSON-RPC harness runtime." +kind: "package-bundle" +--- + # `@deepseek-ai/dsh-sdk-app` English | [中文](README.zh.md) -The SDK stdio application as a `dsh` profile bundle over [`dsh-base`](../base/README.md). It inherits the base's disabled module-HMR policy; its patch sets the coding-agent persona, mounts an app-owned zero-option command provider, and starts [`dsh-sdk-jsonrpc-server`](../../sdk/server/README.md) only after that provider accepts the invocation. `dsh --profile sdk --help` therefore writes help and exits without claiming stdin or stdout. The standalone [`sdk-minimal`](../sdk-minimal/README.md) bundle reuses the same startup provider and supplies its own profile name. +## Summary + +The SDK stdio application as a `dsh` profile bundle over [`dsh-base`](../base/README.md). It inherits the base's disabled module-HMR policy; its patch sets the coding-agent persona, mounts an app-owned zero-option command provider, and starts [`dsh-sdk-jsonrpc-server`](../../sdk/server/README.md) only after that provider accepts the invocation. `dsh --profile sdk --help` therefore writes help and exits without claiming stdin or stdout. The standalone [`sdk-minimal`](../sdk-minimal/README.md) bundle reuses the same startup provider with its own profile name. + +## Table of Contents + +- [Use this package](#use-this-package) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) + +----- + + +## Use this package The startup provider binds stdin EOF to the launcher's bounded successful shutdown. SDK protocol `shutdown`, SIGINT, and SIGTERM retain their owning server or launcher paths; disposal drains the root profile tree and persistence. Stdout is reserved for newline-delimited JSON-RPC frames. The bundle disables model-generated session titles because the SDK exposes no title surface; deterministic fallback titles remain durable without an auxiliary model request. The inherited projection cache checkpoints SDK-created sessions for later consumers; its durability barrier flushes each covered log prefix before publishing the cache row and may split otherwise coalesced JSONL runs. A deployment selects a different complete composition through profile bundles and patch files, not another app bin. @@ -12,6 +31,9 @@ The startup provider binds stdin EOF to the launcher's bounded successful shutdo `DSH_MAX_TOKENS_AS_SUCCESS` retains the SDK deployment mapping: unset or JSON `true` reports token-limited subagent completion as accepted, while JSON `false` reports it as an error. Provider/model and workspace cwd arrive through the SDK initialization request; the base profile owns adapters, tools, persistence, policy, settings, and credentials. +----- + + ## Model Experience ### SDK coding-agent persona @@ -30,6 +52,19 @@ Stable for a fixed profile, provider, model, and tool roster. Profile changes ta ## Known Limitations and Deferred Work + + - **A profile can omit the SDK server** — a custom profile selected by the TypeScript client must retain this bundle or another `dsh-sdk-jsonrpc-server` row; client initialization fails when no peer answers. - **User plugins can violate stdout purity** — profile and per-launch patches are trusted application composition. The shipped bundle writes no non-protocol stdout, but it cannot contain an arbitrary inserted plugin. - **Configuration changes require restart** — the shipped `sdk` profile uses `patchReload: startup` so one stdio connection never observes a replacement server or Agent dependency. + + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/bundle/sdk-app/README.zh.md b/packages/bundle/sdk-app/README.zh.md index 935e2d88ce..b4871e937e 100644 --- a/packages/bundle/sdk-app/README.zh.md +++ b/packages/bundle/sdk-app/README.zh.md @@ -1,17 +1,39 @@ +--- +description: "面向启动 JSON-RPC harness 运行时的用户与维护者,说明 SDK stdio 应用 profile。" +kind: "package-bundle" +--- + # `@deepseek-ai/dsh-sdk-app` [English](README.md) | 中文 -以 [`dsh-base`](../base/README.zh.md) 为基础的 SDK stdio 应用 `dsh` profile 组合包。它继承 base 默认禁用模块 HMR(热模块替换)的策略;其 patch 设置 coding agent(编程智能体)persona、挂载应用自有的零选项命令提供方,并且只在该提供方接受调用后启动 [`dsh-sdk-jsonrpc-server`](../../sdk/server/README.zh.md)。因此,`dsh --profile sdk --help` 会写出 help 并退出,不会占用 stdin 或 stdout。独立的 [`sdk-minimal`](../sdk-minimal/README.zh.md) 组合包复用同一个启动提供方,并提供自己的 profile 名称。 +## 概述 + +以 [`dsh-base`](../base/README.zh.md) 为基础的 SDK stdio 应用 `dsh` profile 组合包。它继承 base 默认禁用模块 HMR(热模块替换)的策略;其 patch 设置 coding agent(编程智能体)persona、挂载应用自有的零选项命令提供方,并且只在该提供方接受调用后启动 [`dsh-sdk-jsonrpc-server`](../../sdk/server/README.zh.md)。因此,`dsh --profile sdk --help` 会写出 help 并退出,不会占用 stdin 或 stdout。独立的 [`sdk-minimal`](../sdk-minimal/README.zh.md) bundle 复用同一个启动提供方,并提供自己的 profile 名称。 + +## 目录 + +- [使用本包](#use-this-package) +- [模型体验](#model-experience) +- [已知限制与延期工作](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) + +----- + + +## 使用本包 启动提供方把 stdin EOF 接到启动器的有界成功关闭流程。SDK 协议 `shutdown`、SIGINT 与 SIGTERM 继续使用各自所属的 server 或启动器路径;dispose(资源释放)会排空根 profile 配置树与持久化。stdout 专用于按换行分隔的 JSON-RPC 帧。SDK 不提供 title 表层,因此本组合包禁用模型生成的 session title;确定性的 fallback title 仍会持久化,但不发起辅助模型请求。继承的投影缓存会为 SDK 创建的会话写入检查点,供后续消费方使用;其持久性屏障会在发布缓存行前 flush 所覆盖的日志前缀,因此可能拆分原本会合并的 JSONL 行。部署通过 profile 组合包与 patch 文件选择另一套完整组合,而不是使用另一个应用 bin。 | 配置 | 默认值 | 行为 | |---|---|---| -| `profile` | `sdk` | 命令 help 中呈现的 profile 名称;挂载此提供方的组合包会设置自己的随附 profile 名称。 | +| `profile` | `sdk` | 命令帮助中显示的 profile 名称;挂载此提供方的 bundle 会设置自己的随附 profile 名称。 | `DSH_MAX_TOKENS_AS_SUCCESS` 保留 SDK 部署映射:未设置或 JSON `true` 把 token 达限的 subagent 完成报告为已接受,JSON `false` 则报告为错误。模型提供方/模型与工作区 cwd 通过 SDK 初始化请求传入;base profile 拥有适配器、工具、持久化、策略、settings 与 credentials。 +----- + + ## 模型体验 ### SDK coding agent persona @@ -30,6 +52,19 @@ profile 会在 base 工具与上下文贡献之前提供 `You are a coding agent ## 已知限制与延期工作 + + - **profile 可以省略 SDK server**:TypeScript client 选择的自定义 profile 必须保留本组合包或另一个 `dsh-sdk-jsonrpc-server` 配置项;没有 peer 响应时,client 初始化会失败。 - **用户插件可以破坏 stdout 纯净性**:profile 与逐次启动 patch 属于受信任应用组合。随附组合包不会向 stdout 写入非协议内容,但无法约束任意插入插件。 - **配置变化需要重启**:随附 `sdk` profile 使用 `patchReload: startup`,因此一个 stdio 连接不会观察到 server 或 Agent 依赖被替换。 + + + +### 开发备注 + +
+维护者工作上下文——点击展开 + +无。 + +
diff --git a/packages/bundle/sdk-minimal/README.i18n.yaml b/packages/bundle/sdk-minimal/README.i18n.yaml index 0c606565e9..8230113dff 100644 --- a/packages/bundle/sdk-minimal/README.i18n.yaml +++ b/packages/bundle/sdk-minimal/README.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 packages/bundle/sdk-minimal/README.md -README.md: 3c8d4efa7540e8ff317c897f2e2f463e448610bb -README.zh.md: 54a9d99c95322a34433aec81cb6d37cb16e5015e +README.md: cf9333cd4304f24344b79d54b4149dadf246c325 +README.zh.md: eeda31bff9e34fd20dae0e43c5678c0e398e0649 diff --git a/packages/bundle/sdk-minimal/README.md b/packages/bundle/sdk-minimal/README.md index 3c8d4efa75..cf9333cd43 100644 --- a/packages/bundle/sdk-minimal/README.md +++ b/packages/bundle/sdk-minimal/README.md @@ -1,15 +1,76 @@ +--- +description: "Standalone two-tool SDK profile for users who need a minimal cross-platform coding agent without the shared base bundle." +kind: "package-bundle" +--- + # `@deepseek-ai/dsh-sdk-minimal` English | [中文](README.zh.md) -Standalone minimal SDK application bundle for `dsh --profile sdk-minimal`. Its single insert is the complete Cordis tree: SDK stdio startup and JSON-RPC serving, one environment-configured DeepSeek adapter, the executor-less agent spine, local subprocess and unrestricted filesystem providers, a platform-selected persistent shell PTY, the string-replace editor, and uncompressed JSONL session persistence under `$DSH_HOME/sessions`. It deliberately does not include [`dsh-base`](../base/README.md), Web, settings, managed credentials, telemetry, compaction, workspace instructions, skills, jobs tools, subagents, or any other model-facing tool. +## Summary -The profile remains part of the ordinary launcher and layering model. The bundle supplies the complete default tree; the profile patch, home patch, and ordered `--patch` files can replace rows or insert external bundles above it. `dsh plugin --profile sdk-minimal` manages persistent dependencies. The shipped template uses startup-only patches so one stdio connection never observes replacement of its server or agent dependencies. +Use `dsh --profile sdk-minimal` when an SDK client needs a small, explicit coding-agent runtime. The profile advertises a platform-selected persistent shell and `str_replace_editor`, persists sessions as uncompressed JSONL, and selects the model from the SDK initialization request. It supplies a complete Cordis tree and deliberately excludes `dsh-base`, Web, settings, managed credentials, telemetry, compaction, workspace instructions, skills, jobs, and subagents. Its danger-full-access policy lets the shell and editor modify any path available to the process, so use it only with an isolated workspace. -`DEEPSEEK_API_KEY` supplies the adapter credential. The SDK initialization request is the sole model selection; the adapter accepts that model id even when it is absent from its advisory catalog. `DSH_CONTEXT_WINDOW` sets the fallback capacity for such models, and `DSH_SYSTEM_PROMPT` replaces the default persona. The process working directory is the sandbox-policy workspace and local-filesystem root. The bundle sets `danger-full-access`; its persistent shell and editor can modify any path available to the process. +## Table of Contents -Exactly one persistent shell stack mounts by platform: Bash on Linux/macOS or PowerShell on Windows. Both use a 300-second timeout and one owner-scoped terminal; the other platform rows remain disabled. +- [Use this package](#use-this-package) +- [Understand the implementation](#understand-the-implementation) +- [Further Exploration](#further-exploration) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) +----- + + +## Use this package + +Launch the profile directly or select it from the Python SDK. Supply an explicit `DSH_HOME`, use a disposable workspace, and provide the model credential through `DEEPSEEK_API_KEY`. + +```sh +export DSH_HOME=/absolute/path/to/example-dsh-home +dsh --profile sdk-minimal +``` + +`DSH_CONTEXT_WINDOW` sets the fallback capacity for a model absent from the adapter's advisory catalog. `DSH_SYSTEM_PROMPT` replaces the default persona. The SDK initialization request is the sole model selection and overrides environment defaults. + +Use `dsh plugin --profile sdk-minimal` to manage persistent external dependencies. Profile, home, and ordered `--patch` files can replace rows or insert bundles above the complete default tree. The shipped template applies patches only at startup. + +The profile mounts exactly one persistent shell stack: Bash on Linux and macOS, or PowerShell on Windows. Both stacks use a 300-second timeout and one owner-scoped terminal; the other platform's rows remain disabled. + +----- + + +## Understand the implementation + +
+Implementation internals — click to expand + +The bundle's single insert is the complete application tree: SDK stdio startup and JSON-RPC serving, one environment-configured DeepSeek adapter, the executor-less agent spine, local subprocess and unrestricted filesystem providers, a platform-selected persistent shell PTY, the string-replace editor, and uncompressed JSONL persistence under `$DSH_HOME/sessions`. It does not inherit another bundle, so every extra row is an explicit profile change. + +### Source map + +| File | Role | +|---|---| +| [`cordis.patch.yml`](cordis.patch.yml) | Complete standalone profile tree and its environment-backed defaults | +| [`src/index.ts`](src/index.ts) | Bundle package entry | +| [`src/invariant.ts`](src/invariant.ts) | Invariant companion for the static composition | +| [`tests/sdk-minimal.spec.ts`](tests/sdk-minimal.spec.ts) | Exact composition, profile-name, and platform-selection checks | + +
+ +----- + + +## Further Exploration + +- [Python SDK example](../../../python/sdk/examples/README.md) — launches this profile from Python against an explicit Harness home. +- [SDK application bundle](../sdk-app/README.md) — the JSON-RPC application layer reused by full and minimal SDK profiles. +- [Base bundle](../base/README.md) — the full product foundation that this profile deliberately omits. + +----- + + ## Model Experience ### Minimal coding-agent composition @@ -28,5 +89,17 @@ Stable for a fixed persona, platform, provider, model, and bundle patch stack. P ## Known Limitations and Deferred Work + + - **The composition intentionally omits shared product services** — select `dsh --profile sdk` when settings, managed credentials, policy presets, telemetry, Web tools, or the full default tool roster are required. - **User patches can expand the tree and corrupt stdout** — profile customization is trusted application composition; a plugin that writes ordinary text to stdout can break JSON-RPC framing. + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/bundle/sdk-minimal/README.zh.md b/packages/bundle/sdk-minimal/README.zh.md index 54a9d99c95..eeda31bff9 100644 --- a/packages/bundle/sdk-minimal/README.zh.md +++ b/packages/bundle/sdk-minimal/README.zh.md @@ -1,22 +1,83 @@ +--- +description: "供需要不含共享 base bundle 的极简跨平台 coding agent 的用户使用的独立双工具 SDK profile。" +kind: "package-bundle" +--- + # `@deepseek-ai/dsh-sdk-minimal` [English](README.md) | 中文 -供 `dsh --profile sdk-minimal` 使用的独立极简 SDK 应用组合包。它的单个 insert 构成完整 Cordis 树:SDK stdio 启动与 JSON-RPC 对外服务、一个由环境配置的 DeepSeek 适配器、无执行器的 agent 主干、本地子进程与不受限文件系统提供方、按平台选择的持久 shell PTY、字符串替换编辑器,以及位于 `$DSH_HOME/sessions` 的未压缩 JSONL 会话持久化。它刻意不包含 [`dsh-base`](../base/README.zh.md)、Web、settings、托管凭据、遥测、压缩(compaction)、workspace 指令、skills、jobs 工具、subagent 或任何其他面向模型的工具。 +## 概述 -该 profile 仍遵循普通 launcher 与分层模型。组合包提供完整默认树;profile patch、home patch 与有序 `--patch` 文件可以在其上替换配置项或插入外部组合包。`dsh plugin --profile sdk-minimal` 管理持久依赖。随附模板仅在启动时应用 patch,因此一个 stdio 连接不会观察到服务器或 agent 依赖在运行中被替换。 +当 SDK 客户端需要小型、显式的 coding agent 运行时时,请使用 `dsh --profile sdk-minimal`。该 profile 只公布按平台选择的持久 shell 与 `str_replace_editor`,把会话持久化为未压缩 JSONL,并从 SDK 初始化请求选择模型。它提供完整 Cordis 配置树,并刻意排除 `dsh-base`、Web、settings、托管凭据、遥测、compaction、workspace 指令、skills、jobs 与 subagent。其 danger-full-access 策略允许 shell 与编辑器修改进程可访问的任何路径,因此只能配合隔离 workspace 使用。 -`DEEPSEEK_API_KEY` 提供适配器凭据。SDK 初始化请求是唯一模型选择;即使该模型 id 不在适配器的建议目录中,适配器也会接受它。`DSH_CONTEXT_WINDOW` 为这类模型设置后备容量,`DSH_SYSTEM_PROMPT` 替换默认 persona。进程工作目录同时作为沙箱策略 workspace 与本地文件系统根目录。该组合包设置 `danger-full-access`;其持久 shell 与编辑器可以修改进程可访问的任何路径。 +## 目录 -运行时会按平台恰好挂载一套持久 shell:Linux/macOS 使用 Bash,Windows 使用 PowerShell。两者都使用 300 秒超时与一个 agent 自有终端;另一平台的配置项保持禁用。 +- [使用本包](#use-this-package) +- [理解实现](#understand-the-implementation) +- [进一步探索](#further-exploration) +- [模型体验](#model-experience) +- [已知限制与延期工作](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) +----- + + +## 使用本包 + +直接启动该 profile,或从 Python SDK 选择它。提供显式 `DSH_HOME`、使用一次性 workspace,并通过 `DEEPSEEK_API_KEY` 提供模型凭据。 + +```sh +export DSH_HOME=/absolute/path/to/example-dsh-home +dsh --profile sdk-minimal +``` + +`DSH_CONTEXT_WINDOW` 为不在适配器建议目录中的模型设置后备容量。`DSH_SYSTEM_PROMPT` 替换默认 persona。SDK 初始化请求是唯一模型选择,并覆盖环境默认值。 + +使用 `dsh plugin --profile sdk-minimal` 管理持久外部依赖。Profile、home 与有序 `--patch` 文件可以在完整默认配置树上替换配置项或插入 bundle。随附模板只在启动时应用 patch。 + +该 profile 只挂载一套持久 shell:Linux 和 macOS 使用 Bash,Windows 使用 PowerShell。两套配置都使用 300 秒超时与一个 agent 自有终端;另一平台的配置项保持禁用。 + +----- + + +## 理解实现 + +
+实现细节——点击展开 + +该 bundle 的单个 insert 就是完整应用配置树:SDK stdio 启动与 JSON-RPC 服务、一个由环境配置的 DeepSeek 适配器、无执行器 agent 主干、本地子进程与不受限文件系统提供方、按平台选择的持久 shell PTY、字符串替换编辑器,以及位于 `$DSH_HOME/sessions` 的未压缩 JSONL 持久化。它不继承其他 bundle,因此每个额外配置项都是显式 profile 变更。 + +### 源码地图 + +| 文件 | 职责 | +|---|---| +| [`cordis.patch.yml`](cordis.patch.yml) | 完整独立 profile 配置树及其环境默认值 | +| [`src/index.ts`](src/index.ts) | Bundle 包入口 | +| [`src/invariant.ts`](src/invariant.ts) | 静态组合的不变式伴生插件 | +| [`tests/sdk-minimal.spec.ts`](tests/sdk-minimal.spec.ts) | 精确组合、profile 名称与平台选择检查 | + +
+ +----- + + +## 进一步探索 + +- [Python SDK 示例](../../../python/sdk/examples/README.zh.md)——从 Python 针对显式 Harness home 启动本 profile。 +- [SDK 应用 bundle](../sdk-app/README.zh.md)——完整与极简 SDK profile 复用的 JSON-RPC 应用层。 +- [Base bundle](../base/README.zh.md)——本 profile 刻意省略的完整产品基础。 + +----- + + ## 模型体验 ### 极简 coding agent 组合 #### 模型看到的内容 -系统提示词取 `DSH_SYSTEM_PROMPT`,未设置时使用 `You are a helpful software engineer assistant.`。对外公布的工具只有 Linux/macOS 上 agent 所有的持久 `bash` 或 Windows 上的 `pwsh`,外加 `str_replace_editor`;运行时上下文、workspace 指令、skills、jobs 控制、compaction 与 Harness 身份均不存在。 +系统提示词取 `DSH_SYSTEM_PROMPT`,未设置时使用 `You are a helpful software engineer assistant.`。对外公布的工具只有 Linux/macOS 上 agent 所有的持久 `bash` 或 Windows 上的 `pwsh`,外加 `str_replace_editor`;运行时上下文、workspace 指令、skills、jobs 控制、compaction 与 Harness 身份均不存在。 #### Token 影响 @@ -24,9 +85,21 @@ #### KV Cache 影响 -当 persona、平台、提供方、模型与组合包 patch 栈固定时保持稳定。Profile 变更在下一个进程生效。 +当 persona、平台、提供方、模型与 bundle patch 栈固定时保持稳定。Profile 变更在下一个进程生效。 -## 已知限制与待办工作 +## 已知限制与延期工作 + + - **该组合刻意省略共享产品服务** — 需要 settings、托管凭据、权限策略预设、遥测、Web 工具或完整默认工具清单时,请选择 `dsh --profile sdk`。 - **用户 patch 可以扩展配置树并破坏 stdout** — profile 自定义属于受信任的应用组合;向 stdout 写入普通文本的插件会破坏 JSON-RPC 分帧。 + + +### 开发备注 + +
+维护者工作上下文——点击展开 + +无。 + +
diff --git a/packages/bundle/web-app/README.i18n.yaml b/packages/bundle/web-app/README.i18n.yaml index 5bc7b88f27..237686c71a 100644 --- a/packages/bundle/web-app/README.i18n.yaml +++ b/packages/bundle/web-app/README.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 packages/bundle/web-app/README.md -README.md: dd3b5787000be18920e6b773f27ebe65abe4d20a -README.zh.md: 6e9e99b8485ebb527e69c38e28abf9c9eef44303 +README.md: ed2c341c3eb5b3a5ad2b298c4babd0fc19a066e2 +README.zh.md: 1f57a0beff91fc341bef6f9c6e79dab4f1d1b564 diff --git a/packages/bundle/web-app/README.md b/packages/bundle/web-app/README.md index dd3b578700..ed2c341c3e 100644 --- a/packages/bundle/web-app/README.md +++ b/packages/bundle/web-app/README.md @@ -1,15 +1,123 @@ -# `@deepseek-ai/dsh-web-app` +--- +description: "The browser GUI for dsh: interactive chat, model and settings management, and session history, for users running the dsh web surface." +kind: "package-bundle" +--- + +# @deepseek-ai/dsh-web-app English | [中文](README.zh.md) -The dsh browser-surface bundle. [`cordis.patch.yml`](cordis.patch.yml) rides over [`dsh-base`](../base/README.md): it sets the coding persona, inserts the Web host rows (webserver, API gateway, workspace) and the browser plugin roster, the always-on client-plugin reload chain ([`dsh-client-hmr`](../../client/hmr/README.md), idle until a rebuild watcher rewrites client bundles), and mounts this package's `web-runtime` glue plugin (config `{openBrowser, printUrl, surfaceContext, trustedHosts}`). The storage stack and the projection cache ride in the shared base bundle; the web overlay's workspace and message-feedback rows consume the base `storageDomain` service. That plugin resolves the built frontend dist through `@deepseek-ai/dsh-web-frontend`'s exports, samples bind-dependent LAN trust once, provides it as `webRuntime` to the browser-trust fence and client roster, mounts the [`frontend-static`](../../host/frontend-static/README.md) fallback owner, and registers the harness-source and web-surface prompt sections plus the bash-visible `DSH_WEB_URL` runtime variable when `surfaceContext` is true. After its Loader tree settles and Connection authentication is available, it prints the `dsh web:` root URL with the fresh process token when `printUrl` is true and opens that authenticated URL in the default browser when `openBrowser` is true and the inherited `SSH_CONNECTION` and `SSH_TTY` are blank or absent. The model prompt and `DSH_WEB_URL` retain the clean canonical URL without credentials. An SSH launch keeps the tokenized URL line but suppresses browser handoff because the SSH client or editor owns the local forwarded address. Immediately before a handoff, the runtime prints `dsh web: opening the default browser; pass --no-open to disable`. A short-lived Node helper runs the maintained platform opener with the canonical scrubbed child environment. On Windows it stays alive until the short-lived PowerShell launcher exits, because `open` reports spawn before that launcher has handed the URL to the shell; elsewhere the helper stops after the opener accepts spawn. A helper failure writes a credential-free diagnostic with its reason and points to the startup URL without stopping the server, and no path waits for the browser to exit. This bundle also owns the app command line: the ordinary `web-startup` provider ([`src/startup.ts`](src/startup.ts)) injects `ctx.cmdlineArgs` ([`dsh-cmdline`](../../boot/cmdline/README.md)), parses `--host`, `--port`, repeatable `--trusted-host`, `--no-open`, and the app's `--help`, then provides `webStartup`; browser opening defaults on for local launches, and `--no-open` turns it off for this invocation. It rejects `--host 0.0.0.0` before publishing that service because the CLI intentionally does not support all-interfaces binding. Flag-configured rows inject the service and read it directly from lazy config, so nothing binds a port before argument resolution and `dsh --profile web --help` starts no server. [`dsh-headless`](../headless/README.md) is a sibling surface over the same base and does not mount this bundle. +## Summary -The base module-HMR row remains disabled. The Web profile's `patchReload: live` lifecycle uses the launcher's config-only watcher; the browser-facing `dsh-client-hmr` reload chain is separate from server module HMR. +Run `dsh --profile web` and the interface opens in your default browser, ready for interactive chat with the agent. You get the conversation view, model and settings management, and session history, backed by the same model access, tools, and safety defaults as every other surface. The command prints a tokenized startup URL; the browser exchanges that token for a signed session cookie and redirects to the clean root URL. You can change the port, suppress the browser handoff, and allow extra hosts from the command line; binding all network interfaces is intentionally not supported. Choose it for interactive work in the browser; `dsh-headless` is the one-shot command-line sibling. -## Model retry defaults +## Table of Contents -Web uses the shared bounded normal default of five eligible retries after the initial request. The `deepseek-official` route and settings-added pi-ai routes use that default when they omit `retryPolicy`; explicit provider policies still win. Web adds no retry-specific composition override, so the same omission behavior applies to non-Web profiles. +- [Use this package](#use-this-package) +- [Understand the implementation](#understand-the-implementation) +- [Further Exploration](#further-exploration) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) +----- + + +## Use this package + +Start the GUI, open your browser, and start talking to the agent. The flags fine-tune the invocation. + +### Starting the Web GUI + +```sh +dsh --profile web +dsh --profile web --no-open --port 8080 +``` + +After startup you see a `dsh web:` line whose root URL carries a fresh process token. Unless `--no-open` or an SSH session suppresses it, the default browser opens that URL, receives a signed cookie, and redirects to the clean root page. You know it worked when the page loads and you can chat with the agent. Two failures to expect: if the frontend is not built, startup stops with a build hint (`pnpm run build` in a checkout); if the browser cannot be opened, a credential-free diagnostic prints to stderr while the server keeps running — open the printed startup URL yourself. + +### Configuration + +Most users never set these; the command-line flags feed the four settings below — `--host`, `--port`, and `--trusted-host` come from the invocation, and `--no-open` turns the browser handoff off for that invocation: + +| Field | Default | Meaning | +|---|---|---| +| `openBrowser` | `true` | Open the default browser after startup; SSH launches suppress it | +| `printUrl` | `true` | Print the `dsh web:` URL line at startup | +| `surfaceContext` | `true` | Give the agent GUI-orientation context and expose `DSH_WEB_URL` to its shell commands | +| `trustedHosts` | `[]` | Extra hosts allowed to reach the GUI from the network | + +The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-web-app) is the exhaustive source for every accepted field and its JSDoc. + +### LAN access and trusted hosts + +By default the GUI accepts connections from this machine only. A deployment that binds all network interfaces also allows browsers from the LAN, and the printed URL then includes a LAN address; `--trusted-host` adds extra hosts in either case. Host and Origin checks control reachability, while the token exchange authenticates every Host API method and WebSocket stream. The LAN addresses are sampled once at startup, so a network change later is not picked up — restart the GUI to re-advertise. + +### Running over SSH + +When you launch `dsh --profile web` over SSH, the URL line still prints but the browser is not opened for you: the SSH client or editor owns the local forwarding address. Open the forwarded URL on your machine yourself; the printed URL names the remote host's loopback endpoint. + +### Per-session agent setup + +Each browser session composes its own agent from the shipped presets (the `standard` preset by default), instead of sharing one process-wide tool set. You can change the default preset or add your own presets under `$DSH_HOME/.agent-presets`. + +----- + + +## Understand the implementation + +
+Implementation internals — click to expand + +The bundle is one patch plus one runtime glue plugin. The storage stack and projection cache come from `dsh-base`; the web overlay's workspace and message-feedback rows consume that shared `storageDomain` service. The patch restates the surface-specific values the base deliberately omits, inserts the web-only host rows and browser roster, then moves the agent plane behind presets. The glue plugin owns dist serving, trust sampling, prompt sections, the bash variable, and the readiness announcements. + +### Patch semantics + +A patch replaces the targeted row's whole `config`, so each web row restates every key it owns: the persona, the `DSH_TOOLS_MODE` Code Mode opt-in, and the `session-query-sqlite` values on the base rows, then `insert` adds the web host rows, transport, and browser roster. The per-agent tool rows the base mounts process-wide are disabled here and the preset roster takes over; the reasoning for each host-plane versus preset-plane decision is inline in the patch. + +### Readiness + +The URL line and browser handoff are readiness signals: supervisors RPC as soon as they observe the line, and a browser requests the page as soon as it opens, so both run only after the Loader tree settles and Connection authentication is available — or immediately in a hand-built tree without a Loader. A tree disposed mid-boot announces nothing. + +### LAN trust sampling + +`resolveLanTrust` samples the network once at boot: a loopback bind (`127.0.0.1`) derives no LAN addresses, while an all-interfaces bind adds every non-internal IPv4 literal. The derived literals plus the explicit `--trusted-host` authorities form the `/api` browser-trust fence, and the printed LAN URL always matches that fence. + +### Source map + +| File | Role | +|---|---| +| [`src/index.ts`](src/index.ts) | The `web-app` glue plugin: dist resolution, LAN trust sampling, prompt sections, bash variable, URL line, browser handoff | +| [`src/startup.ts`](src/startup.ts) | The `web-startup` provider: `--host`, `--port`, `--trusted-host`, `--no-open`, `--help` | +| [`cordis.patch.yml`](cordis.patch.yml) | The web patch: restated base values, web host rows, browser roster, agent plane behind presets | +| [`src/invariant.ts`](src/invariant.ts) | Invariant companion: no runtime invariant; every contribution is registry-disposed | +| [`tests/web-app.spec.ts`](tests/web-app.spec.ts) | Dist resolution, fallback seat, prompt sections, readiness | +| [`tests/startup.spec.ts`](tests/startup.spec.ts) | Command-line parsing over a real Loader tree | +| [`tests/trusted-hosts.spec.ts`](tests/trusted-hosts.spec.ts) | LAN-trust sampling | +| [`tests/browser-open.spec.ts`](tests/browser-open.spec.ts) | Default-browser handoff after the page is reachable | + +### Invariant ownership + +The invariant companion registers an empty installer because every contribution — the frontend-static child plugin, the prompt sections, and the bash variable registration — is registry-disposed with the fiber, and each owning registry's package carries that relation's invariant. + +
+ +----- + + +## Further Exploration + +Read these pages when you want to go deeper into the shared core, the browser reload pipeline, or the built frontend. + +- [Bundle package map](../README.md) — the surfaces built on the same core. +- [dsh-base](../base/README.md) — the shared core the GUI runs on. +- [dsh-client-hmr](../../client/hmr/README.md) — how client-plugin changes reload during development. +- [frontend-static](../../host/frontend-static/README.md) — how the built frontend is served. +- [Generated configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-web-app) — every accepted config field and its source declaration. + +----- + + ## Model Experience ### Harness-source and Web-surface context @@ -28,8 +136,24 @@ The prompt section sits near the system prompt's head and is stable for the life ## Known Limitations and Deferred Work -- **The frontend dist must be built** — `require.resolve` of the dist fails loud at activation with a build hint; there is no source-serving fallback. -- **`lanAddresses` is a boot-time snapshot** — interface changes after boot are not re-advertised; the printed LAN URL always matches the configured trust fence. -- **Only handoff startup is observable** — observation ends when the platform opener accepts spawn, except that Windows waits for its short-lived PowerShell launcher to exit; a later browser exit is not reported, and the printed URL remains the manual fallback. -- **SSH forwarding owns the browser URL** — the printed canonical URL names the remote host's loopback endpoint; automatic handoff is suppressed, and the SSH client or editor must expose and open its local forwarded address. -- **Browser command overrides are launch-only** — a discovered `.env` may not set `BROWSER`; only an inherited value may reach an opener path that honors the variable, so a checkout cannot choose an executable for automatic handoff. + + + +These limits tell you what to expect in unusual setups — a source checkout, SSH sessions, or strict networks. They are current package constraints, not a general browser comparison or a task backlog. + +- **The frontend must be built** — a source checkout needs `pnpm run build` first; startup stops with a build hint when the dist is missing, and there is no source-serving fallback. +- **LAN addresses are sampled once at startup** — interface changes after boot are not re-advertised; the printed LAN URL always matches what was sampled. +- **Only the handoff start is observable** — the GUI reports that the browser was asked to open, not that it actually opened; a later browser exit is never reported, and the printed URL is your manual fallback. +- **SSH sessions keep the URL but skip the browser handoff** — the printed URL names the remote host's loopback endpoint; the SSH client or editor must expose and open the local forwarded address. +- **`BROWSER` overrides only come from the environment** — a discovered `.env` cannot set `BROWSER`; only an inherited value can choose the executable for the automatic handoff. +- **Binding all network interfaces is not supported** — `--host 0.0.0.0` is rejected at startup for safety; use the default loopback host. + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/bundle/web-app/README.zh.md b/packages/bundle/web-app/README.zh.md index 6e9e99b848..1f57a0beff 100644 --- a/packages/bundle/web-app/README.zh.md +++ b/packages/bundle/web-app/README.zh.md @@ -1,20 +1,128 @@ -# `@deepseek-ai/dsh-web-app` +--- +description: "dsh 的浏览器 GUI:交互式聊天、模型与设置管理、会话历史,供用户运行 dsh web 表层。" +kind: "package-bundle" +--- + +# @deepseek-ai/dsh-web-app [English](README.md) | 中文 -dsh 浏览器表层组合包。[`cordis.patch.yml`](cordis.patch.yml) 叠加在 [`dsh-base`](../base/README.zh.md) 之上:设置 coding persona,插入 Web 宿主行(webserver、API 网关、workspace)、浏览器插件名录与始终挂载的客户端插件重载链([`dsh-client-hmr`](../../client/hmr/README.zh.md),在重建 watcher 改写客户端 bundle 之前保持空闲),并挂载本包的 `web-runtime` 粘合插件(配置为 `{openBrowser, printUrl, surfaceContext, trustedHosts}`)。存储栈与投影缓存随共享 base 组合包提供;Web 覆盖层的 workspace 与 message-feedback 行消费 base 的 `storageDomain` 服务。该插件通过 `@deepseek-ai/dsh-web-frontend` 的 exports 解析已构建的前端 dist,只采样一次依赖 bind 的 LAN 信任信息并将其作为 `webRuntime` 提供给浏览器信任栅栏和客户端名录,挂载 [`frontend-static`](../../host/frontend-static/README.zh.md) 回退席位所有者,并在 `surfaceContext` 为 true 时注册 Harness 源码与 Web 表层提示词段落,以及 bash 可见的 `DSH_WEB_URL` 运行时变量。Loader 配置树结算且 Connection 认证可用后,它在 `printUrl` 为 true 时打印带新进程令牌的 `dsh web:` 根 URL;`openBrowser` 为 true 且继承的 `SSH_CONNECTION` 与 `SSH_TTY` 均为空或不存在时,才会用默认浏览器打开该认证 URL。模型提示词与 `DSH_WEB_URL` 仍携带不含凭据的干净规范 URL。SSH 启动仍保留带令牌的 URL 行,但会跳过浏览器交接,因为本地转发地址由 SSH 客户端或编辑器持有。交接前,运行时会打印英文提示 `dsh web: opening the default browser; pass --no-open to disable`。短生命周期 Node helper 使用规范的脱敏子进程环境运行受维护的平台 opener。在 Windows 上,helper 会保持存活,直至短生命周期的 PowerShell launcher 退出,因为 `open` 会在 launcher 把 URL 交给 shell 之前、仅在 spawn 时返回;其他平台则在 opener 接受 spawn 后结束。helper 失败时会向 stderr 写入不含凭据的原因并指向启动 URL,不会停止服务器,且任何路径都不会等待浏览器退出。本组合包还持有应用命令行:普通 `web-startup` 提供方([`src/startup.ts`](src/startup.ts))注入 `ctx.cmdlineArgs`([`dsh-cmdline`](../../boot/cmdline/README.zh.md)),解析 `--host`、`--port`、可重复的 `--trusted-host`、`--no-open` 以及应用自己的 `--help`,再提供 `webStartup`;本机启动默认会打开浏览器,`--no-open` 则只对本次调用关闭该行为。它会在发布该服务前拒绝 `--host 0.0.0.0`,因为 CLI 不支持绑定所有网络接口。由 flag 配置的行会注入该服务,并在惰性配置中直接读取它,因此参数解析完成前不会有任何东西绑定端口,`dsh --profile web --help` 也不会启动服务器。[`dsh-headless`](../headless/README.zh.md) 是同一 base 之上的同级表层,不挂载本组合包。 +## 概述 -base 的模块 HMR 配置项保持禁用。Web profile 的 `patchReload: live` 生命周期使用启动器的仅配置 watcher;面向浏览器的 `dsh-client-hmr` 重载链与服务器模块 HMR 相互独立。 +运行 `dsh --profile web`,界面会在你的默认浏览器中打开,即可与 agent(智能体)交互式聊天。你会获得会话视图、模型与设置管理以及会话历史,背后与其他表层相同的模型访问、工具与安全默认值。该命令会打印带 token 的启动 URL;浏览器用该 token 换取签名会话 cookie,再重定向到干净的根 URL。你可以从命令行更改端口、关闭浏览器交接并允许额外主机;有意不支持绑定所有网络接口。需要浏览器中的交互式工作时选择它;`dsh-headless` 是一次性的命令行兄弟表层。 -## 模型重试默认值 +## 目录 -Web 使用共享的有界 normal 默认值,在首次请求后最多再重试五次符合条件的失败。`deepseek-official` 与由 settings 新增的 pi-ai 路由在省略 `retryPolicy` 时使用该默认值;显式提供方策略仍然优先。Web 不再增加重试专用的组合覆盖,因此非 Web profile 的省略行为与之相同。 +- [使用本包](#use-this-package) +- [理解实现](#understand-the-implementation) +- [进一步探索](#further-exploration) +- [模型体验](#model-experience) +- [已知限制与延期工作](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) +----- + + +## 使用本包 + +启动 GUI、打开浏览器,然后开始与 agent(智能体)对话。flag 用于微调本次调用。 + +### 启动 Web GUI + +```sh +dsh --profile web +dsh --profile web --no-open --port 8080 +``` + +启动后你会看到 `dsh web:` 行,其根 URL 携带新的进程 token。除非 `--no-open` 或 SSH 会话抑制,否则默认浏览器会打开该 URL、取得签名 cookie,再重定向到干净的根页面。页面加载且你可以与 agent(智能体)对话,就说明成功了。两种可预期的失败:前端未构建时,启动会以构建提示停止(checkout 中运行 `pnpm run build`);浏览器无法打开时,stderr 会打印不含凭据的诊断,但服务器会继续运行——请自行打开已打印的启动 URL。 + +### 配置 + +大多数用户不需要设置这些;命令行 flag 会提供给下面四个设置——`--host`、`--port` 与 `--trusted-host` 来自本次调用,`--no-open` 仅对本次调用关闭浏览器交接: + +| 字段 | 默认值 | 含义 | +|---|---|---| +| `openBrowser` | `true` | 启动后用默认浏览器打开;SSH 启动会抑制它 | +| `printUrl` | `true` | 启动时打印 `dsh web:` URL 行 | +| `surfaceContext` | `true` | 给 agent(智能体)提供 GUI 定位上下文,并把 `DSH_WEB_URL` 暴露给其 shell 命令 | +| `trustedHosts` | `[]` | 允许从网络访问 GUI 的额外主机 | + +生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-web-app)是每个受支持字段及其 JSDoc 的穷尽式真源。 + +### LAN 访问与可信主机 + +默认情况下 GUI 只接受本机的连接。绑定所有网络接口的部署也会允许 LAN 内的浏览器访问,此时打印的 URL 会附带一个 LAN 地址;`--trusted-host` 在两种情况下都能添加额外主机。Host 与 Origin 检查控制可达性,token 交换则认证每个 Host API 方法与 WebSocket stream。LAN 地址只在启动时采样一次,因此之后的网络变化不会被感知——重启 GUI 以重新公告。 + +### 通过 SSH 运行 + +通过 SSH 启动 `dsh --profile web` 时,URL 行仍会打印,但不会为你打开浏览器:本地转发地址由 SSH 客户端或编辑器持有。请在自己的机器上打开转发后的 URL;打印出的 URL 指向远端宿主机 loopback 端点。 + +### 按会话的 agent 设置 + +每个浏览器会话都从随发行版交付的 preset(默认 `standard`)组合自己的 agent(智能体),而不是共享一套进程级工具集。你可以更改默认 preset,或在 `$DSH_HOME/.agent-presets` 下添加自己的 preset。 + +----- + + +## 理解实现 + +
+实现细节——点击展开 + +本组合包是一份 patch 加一个运行时粘合插件。patch 重述 base 刻意省略的表层专属值,插入仅 Web 使用的宿主行与浏览器名录,然后把 agent 层移到 preset 之后;粘合插件负责 dist 服务、信任采样、提示词段落、bash 变量与就绪宣告。 + +### patch 语义 + +patch 会替换目标行的整个 `config`,因此每个 Web 行都重述自己拥有的每个键:基础行上的 persona、`DSH_TOOLS_MODE` Code Mode 开关与 `session-query-sqlite` 值,随后 `insert` 添加 Web 宿主行、传输层与浏览器名录。base 以进程级挂载的按 agent 工具行在这里被禁用,由 preset 名录接管;每项宿主层与 preset 层归属决策的理由以行内注释写在 patch 里。 + +### 就绪宣告 + +URL 行与浏览器交接都是就绪信号:监督方一观察到该行就发起 RPC,浏览器一打开就请求页面,因此两者只在 Loader 配置树结算且 Connection 认证可用后运行——在没有 Loader 的手工构建树中则立即运行。启动中途被释放的树不会宣告任何内容。 + +### LAN 信任采样 + +`resolveLanTrust` 在启动时只采样一次网络:loopback 绑定(`127.0.0.1`)不派生任何 LAN 地址,绑定所有网卡则会加入每个非 internal IPv4 字面量。派生字面量加上显式的 `--trusted-host` 权威标识组成 `/api` 浏览器信任栅栏,打印的 LAN URL 始终与该栅栏一致。 + +### 源码地图 + +| 文件 | 职责 | +|---|---| +| [`src/index.ts`](src/index.ts) | `web-app` 粘合插件:dist 解析、LAN 信任采样、提示词段落、bash 变量、URL 行、浏览器交接 | +| [`src/startup.ts`](src/startup.ts) | `web-startup` 提供方:`--host`、`--port`、`--trusted-host`、`--no-open`、`--help` | +| [`cordis.patch.yml`](cordis.patch.yml) | Web patch:重述的基础值、Web 宿主行、浏览器名录、preset 之后的 agent 层 | +| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件:无运行时不变式;每项贡献都由 registry 释放 | +| [`tests/web-app.spec.ts`](tests/web-app.spec.ts) | dist 解析、fallback 席位、提示词段落、就绪宣告 | +| [`tests/startup.spec.ts`](tests/startup.spec.ts) | 在真实 Loader 树上的命令行解析 | +| [`tests/trusted-hosts.spec.ts`](tests/trusted-hosts.spec.ts) | LAN 信任采样 | +| [`tests/browser-open.spec.ts`](tests/browser-open.spec.ts) | 页面可达后的默认浏览器交接 | + +### 不变式归属 + +不变式伴生插件注册一个空安装器,因为每项贡献——frontend-static 子插件、提示词段落与 bash 变量注册——都会随 fiber 由 registry 释放,且每个所属 registry 的包负责该关系的不变式。 + +
+ +----- + + +## 进一步探索 + +当你想深入了解共享核心、浏览器重载流水线或已构建的前端时,阅读以下页面。 + +- [组合包包映射](../README.zh.md)——基于同一核心构建的表层。 +- [dsh-base](../base/README.zh.md)——GUI 运行其上的共享核心。 +- [dsh-client-hmr](../../client/hmr/README.zh.md)——开发期间客户端插件变更如何重载。 +- [frontend-static](../../host/frontend-static/README.zh.md)——已构建的前端如何被服务。 +- [生成配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-web-app)——每个受支持配置字段及其源声明。 + +----- + + ## 模型体验 ### Harness 源码与 Web 表层上下文 -#### 模型看到的内容 +#### 模型看到什么 当 `surfaceContext` 为 true 时,`harness:source` 段落标明磁盘上的 Harness 实现,但不会声称它就是工作目录;全局段落 `app:web-surface`(first-party 顺序 −800)则向模型说明 GUI:规范的本地 URL、「this page」指代什么、更新约定(重载接收端始终开启;无刷新重载还需要 `pnpm run dev:web` watcher),以及不要启动替代服务器的指令。`DSH_WEB_URL` 还会连同描述出现在受管 bash 环境中,每次调用时从运行中的服务器解析。当它为 false 时,这两个段落和该变量都不会注册。 @@ -28,8 +136,24 @@ Web 使用共享的有界 normal 默认值,在首次请求后最多再重试 ## 已知限制与延期工作 -- **前端 dist 必须已构建**:对 dist 的 `require.resolve` 在激活时明确报错并给出构建提示;没有从源码直接服务的回退路径。 -- **`lanAddresses` 是启动期快照**:启动后的网卡变化不会重新公告;打印的 LAN URL 始终与配置的信任栅栏一致。 -- **只观测交接启动**:平台 opener 接受 spawn 后即结束观察,但 Windows 会等待其短生命周期 PowerShell launcher 退出;之后的浏览器退出不会上报,已打印 URL 仍是手动访问的回退路径。 -- **SSH 转发持有浏览器 URL**:打印出的规范 URL 指向远端宿主机 loopback 端点;自动交接会被跳过,SSH 客户端或编辑器必须暴露并打开其本地转发地址。 -- **浏览器命令覆盖只能来自启动环境**:被发现的 `.env` 不得设置 `BROWSER`;只有继承值可以抵达会读取该变量的 opener 路径,避免 checkout 为自动交接选择可执行文件。 + + + +这些限制告诉你在不常见的环境下会遇到什么——源码 checkout、SSH 会话或严格网络。它们是当前包约束,不是通用的浏览器对比或任务积压。 + +- **前端必须已构建**——源码 checkout 需要先运行 `pnpm run build`;dist 缺失时启动会以构建提示停止,且没有从源码直接服务的回退路径。 +- **LAN 地址只在启动时采样一次**——启动后的网卡变化不会重新公告;打印的 LAN URL 始终与采样结果一致。 +- **只能观察到交接的启动**——GUI 只报告浏览器被请求打开,而不是它确实打开了;之后的浏览器退出永远不会上报,打印的 URL 是你的手动回退路径。 +- **SSH 会话保留 URL 但跳过浏览器交接**——打印的 URL 指向远端宿主机 loopback 端点;SSH 客户端或编辑器必须暴露并打开本地转发地址。 +- **`BROWSER` 覆盖只能来自环境**——被发现的 `.env` 不能设置 `BROWSER`;只有继承值能为自动交接选择可执行文件。 +- **不支持绑定所有网络接口**——出于安全考虑,`--host 0.0.0.0` 会在启动时被拒绝;请使用默认 loopback 主机。 + + +### 开发备注 + +
+维护者的工作上下文——点击展开 + +无。 + +
diff --git a/packages/client/README.i18n.yaml b/packages/client/README.i18n.yaml index db771381dd..9a60503d9c 100644 --- a/packages/client/README.i18n.yaml +++ b/packages/client/README.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 packages/client/README.md -README.md: eaf01b282ead3c4638435e3d02d6a803ad1faa15 -README.zh.md: 2ff4b1529f07e0f31b3d06a9577d3c480dd34916 +README.md: 51661c58b40d8fe1c9cde161871845c5898c1ac2 +README.zh.md: 6e7f5351442f29a34096ebaede9ca1b4e45b5940 diff --git a/packages/client/README.md b/packages/client/README.md index eaf01b282e..51661c58b4 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -1,52 +1,94 @@ +--- +description: "Package map for the web GUI browser half: shell boot, browser-host communication, shared client services, localization, development reload, and the UI feature plugins." +kind: "package-group" +--- + # client/ — web-GUI browser half English | [中文](README.zh.md) -The browser side of the dsh web GUI: shell boot, browser-host communication, shared UI services, and feature plugins. Authoring rules live in [AGENTS.md](AGENTS.md); the host half is [`host/`](../host/README.md). All except `test-runtime` are **product** packages named `@deepseek-ai/dsh-client-`. +## Summary -| Package | Purpose | -|---|---| -| [`web/`](web/README.md) | Boots the browser shell from the client entry graph. | -| [`ui-renderer/`](ui-renderer/README.md) | Binds slot data to React and mounts the assembled application after client boot settles. | -| [`modules/`](modules/README.md) | Loads browser-side client modules. | -| [`connection/`](connection/README.md) | Maintains browser-host RPC communication and event delivery. | -| [`hmr/`](hmr/README.md) | Refreshes client plugins during development. | -| [`locale/`](locale/README.md) | Provides localization preferences and message dictionaries. | -| [`store/`](store/README.md) | Provides React-free observable and snapshot-store primitives. | -| [`test-runtime/`](../test-support/client-runtime/README.md) | Provides shared repository test support for client feature packages. | -| [`ui-slots/`](ui-slots/README.md) | Defines how UI features register and compose extension slots. | -| [`ui-session/`](ui-session/README.md) | Adapts Session Controller state into standard Slot sources and hooks. | -| [`ui-theme/`](ui-theme/README.md) | Applies the selected color theme. | -| [`ui-primitives/`](ui-primitives/README.md) | Provides shared React controls, icons, and content renderers. | -| [`ui-attachment/`](ui-attachment/README.md) | Registers composer and message-image attachment presentation. | -| [`ui-layout/`](ui-layout/README.md) | Arranges the main application regions. | -| [`ui-sidebar/`](ui-sidebar/README.md) | Presents workspace and session navigation. | -| [`ui-brand-official/`](ui-brand-official/README.md) | Fills the generic browser-brand slots with the official name and marks. | -| [`ui-workspace/`](ui-workspace/README.md) | Provides workspace selection and creation surfaces. | -| [`ui-conversation/`](ui-conversation/README.md) | Presents the active conversation and its input surface. | -| [`ui-chat/`](ui-chat/README.md) | Projects and renders the Chat conversation target. | -| [`ui-approval/`](ui-approval/README.md) | Presents approval requests and returns user decisions. | -| [`ui-tool/`](ui-tool/README.md) | Composes Tool call trees and keyed per-Tool views. | -| [`ui-workflow-run/`](ui-workflow-run/README.md) | Replays durable workflow runs as nested Chat disclosures with live-only child navigation. | -| [`ui-goal/`](ui-goal/README.md) | Presents and manages the current goal. | -| [`ui-trajectory/`](ui-trajectory/README.md) | Presents alternate views of agent activity. | -| [`ui-commands/`](ui-commands/README.md) | Provides session-aware command discovery and dispatch. | -| [`ui-input-trigger/`](ui-input-trigger/README.md) | Coordinates inline command and reference suggestions. | -| [`ui-skill/`](ui-skill/README.md) | Adds skill references to inline suggestions. | -| [`ui-reference/`](ui-reference/README.md) | Unified Web `@file` / `@session` reference source. | -| [`ui-subagent/`](ui-subagent/README.md) | Provides subagent navigation, child transcript states, and inline references. | -| [`ui-jobs/`](ui-jobs/README.md) | Lists this session's background jobs in the conversation header. | -| [`ui-model-selection/`](ui-model-selection/README.md) | Provides model selection in conversation surfaces. | -| [`ui-permission/`](ui-permission-presets/README.md) | Configures default permissions and switches the current session's access. | -| [`ui-plan/`](ui-plan/README.md) | Presents active plan-mode status and its exit control. | -| [`ui-settings-plugins/`](ui-settings-plugins/README.md) | Owns the Plugins settings section, its tab extension point, and configurable host-plane plugin cards. | -| [`ui-user-questions/`](ui-user-questions/README.md) | Presents interactive questions requested by the agent. | -| [`ui-agent-preset/`](ui-agent-preset/README.md) | Selects a session's agent preset and authors preset compositions. | -| [`ui-settings/`](ui-settings/README.md) | Hosts the settings interface and its extension areas. | -| [`ui-settings-general/`](ui-settings-general/README.md) | Provides the general settings section. | -| [`ui-settings-models/`](ui-settings-models/README.md) | Provides model-provider configuration and DeepSeek onboarding. | -| [`ui-settings-plugin-inventory/`](ui-settings-plugin-inventory/README.md) | Contributes the read-only Host Loader inventory tab to Plugins settings. | +The `client/` group runs the browser half of the dsh web GUI: it boots the web shell, loads browser-side plugin modules, keeps browser-to-host RPC and event delivery alive, and provides the shared client services and UI feature plugins that render the application. UI features compose through the slot system — each plugin fills declared extension slots with typed props and stores, and the shell renders the assembled tree. All packages here are product packages named `@deepseek-ai/dsh-client-`; the host half that serves the page lives in [`host/`](../host/README.md). Authoring rules live in [AGENTS.md](AGENTS.md), and the module graph, slot model, and object layer are documented in the related notes below. -Each child reference owns its contract and detailed behavior. The [slot system standard](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md) and [web client architecture note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md) own the cross-package composition and loading decisions. +## Table of Contents -The subsystem reference is [client-modules.md](../../docs/subsystems/client-modules.md); the [slot system standard](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md) is the definitive slot model, and the [web client architecture note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md) owns the loading chain and object layer. +- [Packages](#packages) +- [Related documentation](#related-documentation) +- [Dev Note](#dev-note) + +----- + + +## Packages + +The kernel packages boot and serve the page; the UI feature packages present it. Each package README owns its contract and configuration. + +| Package | Role | ctx key | +|---|---|---| +| [`web/`](web/README.md) | Boots the browser shell | — | +| [`modules/`](modules/README.md) | Loads browser-side client modules | `ctx.clientModules` / `ctx.modules` | +| [`connection/`](connection/README.md) | Maintains browser-host RPC communication and event delivery | `ctx.connection` | +| [`store/`](store/README.md) | Provides React-free observable and snapshot-store primitives | — | +| [`hmr/`](hmr/README.md) | Refreshes client plugins during development | — | +| [`locale/`](locale/README.md) | Provides localization preferences and message dictionaries | `ctx.locale` | +| [`test-runtime/`](../test-support/client-runtime/README.md) | Shared repository test support for client feature packages | — | +| [`ui-renderer/`](ui-renderer/README.md) | Binds slot data to React and mounts the assembled application | `ctx.uiRenderer` | +| [`ui-slots/`](ui-slots/README.md) | Defines how UI features register and compose extension slots | — | +| [`ui-session/`](ui-session/README.md) | Adapts Session Controller state into standard Slot sources and hooks | — | +| [`ui-theme/`](ui-theme/README.md) | Applies the selected color theme | — | +| [`ui-primitives/`](ui-primitives/README.md) | Provides shared React controls, icons, and content renderers | — | +| [`ui-attachment/`](ui-attachment/README.md) | Registers composer and message-image attachment presentation | — | +| [`ui-layout/`](ui-layout/README.md) | Arranges the main application regions | — | +| [`ui-sidebar/`](ui-sidebar/README.md) | Presents workspace and session navigation | — | +| [`ui-brand-official/`](ui-brand-official/README.md) | Fills the generic browser-brand slots with the official name and marks | — | +| [`ui-workspace/`](ui-workspace/README.md) | Provides workspace selection and creation surfaces | — | +| [`ui-conversation/`](ui-conversation/README.md) | Presents the active conversation and its input surface | — | +| [`ui-chat/`](ui-chat/README.md) | Projects and renders the Chat conversation target | — | +| [`ui-approval/`](ui-approval/README.md) | Presents approval requests and returns user decisions | — | +| [`ui-tool/`](ui-tool/README.md) | Composes Tool call trees and keyed per-Tool views | — | +| [`ui-workflow-run/`](ui-workflow-run/README.md) | Replays durable workflow runs as nested chat disclosures | — | +| [`ui-goal/`](ui-goal/README.md) | Presents and manages the current goal | — | +| [`ui-trajectory/`](ui-trajectory/README.md) | Presents alternate views of agent activity | — | +| [`ui-commands/`](ui-commands/README.md) | Provides session-aware command discovery and dispatch | — | +| [`ui-input-trigger/`](ui-input-trigger/README.md) | Coordinates inline command and reference suggestions | — | +| [`ui-skill/`](ui-skill/README.md) | Adds skill references to inline suggestions | — | +| [`ui-reference/`](ui-reference/README.md) | Unified Web `@file` / `@session` reference source | — | +| [`ui-subagent/`](ui-subagent/README.md) | Provides subagent navigation, child transcript states, and inline references | — | +| [`ui-jobs/`](ui-jobs/README.md) | Lists this session's background jobs in the conversation header | — | +| [`ui-model-selection/`](ui-model-selection/README.md) | Provides model selection in conversation surfaces | — | +| [`ui-permission-presets/`](ui-permission-presets/README.md) | Configures default permissions and switches the current session's access | — | +| [`ui-plan/`](ui-plan/README.md) | Presents active plan-mode status and its exit control | — | +| [`ui-settings-plugins/`](ui-settings-plugins/README.md) | Owns the Plugins settings section, its tab extension point, and configurable host-plane plugin cards | — | +| [`ui-user-questions/`](ui-user-questions/README.md) | Presents interactive questions requested by the agent | — | +| [`ui-agent-preset/`](ui-agent-preset/README.md) | Selects a session's agent preset and authors preset compositions | — | +| [`ui-settings/`](ui-settings/README.md) | Hosts the settings interface and its extension areas | — | +| [`ui-settings-general/`](ui-settings-general/README.md) | Provides the general settings section | — | +| [`ui-settings-models/`](ui-settings-models/README.md) | Provides model-provider configuration and DeepSeek onboarding | — | +| [`ui-settings-plugin-inventory/`](ui-settings-plugin-inventory/README.md) | Contributes the read-only Host Loader inventory tab to Plugins settings | — | +| [`ui-deliverables/`](ui-deliverables/README.md) | Produces the produced-files turn tail and clickable final-response file references | — | +| [`ui-message-feedback/`](ui-message-feedback/README.md) | Contributes per-message feedback controls to the assistant-message action strip | — | +| [`ui-directory-picker-browse/`](ui-directory-picker-browse/README.md) | In-app directory browsing surface for the workspace directory flow | — | +| [`ui-directory-picker-native/`](ui-directory-picker-native/README.md) | Native directory-picker surface driving the host's OS chooser | — | + +----- + + +## Related documentation + +Start with the subsystem reference and the two notes that own the cross-package composition decisions, then the host half that serves this page. + +- [Client modules subsystem](../../docs/subsystems/client-modules.md) — the web plugin table: `dsh.client` declarations, the boot graph wire, and the bundle route. +- [Slot system standard](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md) — the definitive slot model: registration, props shares, and stores. +- [Web client architecture note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md) — the loading chain, object layer, and client services. +- [Host group map](../host/README.md) — the host half that serves this browser half. + + +## Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/client/README.zh.md b/packages/client/README.zh.md index 2ff4b1529f..6e7f535144 100644 --- a/packages/client/README.zh.md +++ b/packages/client/README.zh.md @@ -1,52 +1,94 @@ -# client/ — web GUI 浏览器端 +--- +description: "web GUI 浏览器侧的包映射:外壳启动、浏览器与宿主通信、共享客户端服务、本地化、开发重载与 UI 功能插件。" +kind: "package-group" +--- + +# client/ — Web GUI 浏览器侧 [English](README.md) | 中文 -dsh web GUI 的浏览器侧:shell 启动、浏览器与宿主通信、共享 UI 服务和功能插件。编写规则见 [AGENTS.md](AGENTS.md);宿主半侧是 [`host/`](../host/README.zh.md)。除 `test-runtime` 外,均为名为 `@deepseek-ai/dsh-client-` 的**产品**包。 +## 概述 -| 包 | 目的 | -|---|---| -| [`web/`](web/README.zh.md) | 从客户端条目图启动浏览器 shell。 | -| [`ui-renderer/`](ui-renderer/README.zh.md) | 将 slot 数据绑定到 React,并在客户端启动稳定后挂载组装完成的应用。 | -| [`modules/`](modules/README.zh.md) | 加载浏览器侧客户端模块。 | -| [`connection/`](connection/README.zh.md) | 维护浏览器与宿主之间的 RPC 通信和事件传递。 | -| [`hmr/`](hmr/README.zh.md) | 在开发期间刷新客户端插件。 | -| [`locale/`](locale/README.zh.md) | 提供本地化偏好与消息词典。 | -| [`store/`](store/README.zh.md) | 提供不依赖 React 的 observable 与 snapshot-store 基础设施。 | -| [`test-runtime/`](../test-support/client-runtime/README.zh.md) | 为客户端功能包提供共享的仓库测试支持。 | -| [`ui-slots/`](ui-slots/README.zh.md) | 定义 UI 功能注册和组合扩展 slot 的方式。 | -| [`ui-session/`](ui-session/README.zh.md) | 把 Session Controller 状态适配为标准 Slot source 与 hook。 | -| [`ui-theme/`](ui-theme/README.zh.md) | 应用所选颜色主题。 | -| [`ui-primitives/`](ui-primitives/README.zh.md) | 提供共享 React 控件、图标和内容渲染器。 | -| [`ui-attachment/`](ui-attachment/README.zh.md) | 注册输入框与消息图片的附件呈现。 | -| [`ui-layout/`](ui-layout/README.zh.md) | 排列应用的主要区域。 | -| [`ui-sidebar/`](ui-sidebar/README.zh.md) | 展示工作区与会话导航。 | -| [`ui-brand-official/`](ui-brand-official/README.zh.md) | 使用官方名称和标记填充通用浏览器品牌 slot。 | -| [`ui-workspace/`](ui-workspace/README.zh.md) | 提供工作区选择与创建界面。 | -| [`ui-conversation/`](ui-conversation/README.zh.md) | 展示当前对话及其输入界面。 | -| [`ui-chat/`](ui-chat/README.zh.md) | 投影并渲染 Chat conversation target。 | -| [`ui-approval/`](ui-approval/README.zh.md) | 展示审批请求并返回用户决定。 | -| [`ui-tool/`](ui-tool/README.zh.md) | 编排工具调用树和按工具键控的视图。 | -| [`ui-workflow-run/`](ui-workflow-run/README.zh.md) | 把持久工作流运行回放为 Chat 嵌套折叠项,并只为实时子 Session 提供导航。 | -| [`ui-goal/`](ui-goal/README.zh.md) | 展示和管理当前目标。 | -| [`ui-trajectory/`](ui-trajectory/README.zh.md) | 提供 agent(智能体)活动的其他视图。 | -| [`ui-commands/`](ui-commands/README.zh.md) | 提供会话感知的命令发现与分发。 | -| [`ui-input-trigger/`](ui-input-trigger/README.zh.md) | 协调内联命令和引用建议。 | -| [`ui-skill/`](ui-skill/README.zh.md) | 向内联建议添加 skill(技能)引用。 | -| [`ui-reference/`](ui-reference/README.zh.md) | 统一的 Web `@file` / `@session` 引用 source。 | -| [`ui-subagent/`](ui-subagent/README.zh.md) | 提供 subagent(子 agent)导航、子级 transcript(文本记录)的状态和内联引用。 | -| [`ui-jobs/`](ui-jobs/README.zh.md) | 在会话标题栏列出当前会话的后台任务。 | -| [`ui-model-selection/`](ui-model-selection/README.zh.md) | 在对话界面中提供模型选择。 | -| [`ui-permission/`](ui-permission-presets/README.zh.md) | 配置默认权限并切换当前会话的访问模式。 | -| [`ui-plan/`](ui-plan/README.zh.md) | 展示生效中的 plan mode 状态及其退出控件。 | -| [`ui-settings-plugins/`](ui-settings-plugins/README.zh.md) | 拥有“插件”设置分区、它的标签页扩展点,以及可配置的宿主平面插件卡片。 | -| [`ui-user-questions/`](ui-user-questions/README.zh.md) | 展示 agent 请求的交互式问题。 | -| [`ui-agent-preset/`](ui-agent-preset/README.zh.md) | 选择会话的 agent 预设,并编写预设组合。 | -| [`ui-settings/`](ui-settings/README.zh.md) | 承载设置界面及其扩展区域。 | -| [`ui-settings-general/`](ui-settings-general/README.zh.md) | 提供常规设置分区。 | -| [`ui-settings-models/`](ui-settings-models/README.zh.md) | 提供模型提供方配置与 DeepSeek 配置引导。 | -| [`ui-settings-plugin-inventory/`](ui-settings-plugin-inventory/README.zh.md) | 向“插件”设置贡献只读的 Host Loader 清单标签页。 | +`client/` 组运行 dsh web GUI 的浏览器侧:它启动 web 外壳、加载浏览器侧插件模块、维持浏览器与宿主之间的 RPC 与事件投递,并提供渲染应用所需的共享客户端服务与 UI 功能插件。UI 功能通过 slot 系统组合——每个插件填充已声明的扩展 slot,携带类型化 props 与 store,由外壳渲染组装后的整棵树。本组所有包均为产品包,名为 `@deepseek-ai/dsh-client-`;服务于页面的宿主半侧位于 [`host/`](../host/README.zh.md)。编写规则见 [AGENTS.md](AGENTS.md),模块图、slot 模型与对象层的说明见下方相关文档。 -每个子文档负责自身的约定和详细行为。[slot 系统标准](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.zh.md)与 [Web 客户端架构 Agent Note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.zh.md)负责跨包组合与加载决策。 +## 目录 -子系统参考是 [client-modules.md](../../docs/subsystems/client-modules.zh.md);[slot 系统标准](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.zh.md)是权威 slot 模型,[web 客户端架构 Agent Note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.zh.md)拥有加载链与对象层。 +- [包](#packages) +- [相关文档](#related-documentation) +- [开发备注](#dev-note) + +----- + + +## 包 + +内核包负责启动与服务于页面,UI 功能包负责呈现页面。各包的 README 拥有自己的约定与配置。 + +| 包 | 职责 | ctx 键 | +|---|---|---| +| [`web/`](web/README.zh.md) | 启动浏览器外壳 | — | +| [`modules/`](modules/README.zh.md) | 加载浏览器侧客户端模块 | `ctx.clientModules` / `ctx.modules` | +| [`connection/`](connection/README.zh.md) | 维护浏览器与宿主之间的 RPC 通信与事件投递 | `ctx.connection` | +| [`store/`](store/README.zh.md) | 提供不依赖 React 的 observable 与 snapshot-store 原语 | — | +| [`hmr/`](hmr/README.zh.md) | 在开发期间刷新客户端插件 | — | +| [`locale/`](locale/README.zh.md) | 提供本地化偏好与消息词典 | `ctx.locale` | +| [`test-runtime/`](../test-support/client-runtime/README.zh.md) | 为客户端功能包提供共享的仓库测试支持 | — | +| [`ui-renderer/`](ui-renderer/README.zh.md) | 将 slot 数据绑定到 React,并挂载组装完成的应用 | `ctx.uiRenderer` | +| [`ui-slots/`](ui-slots/README.zh.md) | 定义 UI 功能注册与组合扩展 slot 的方式 | — | +| [`ui-session/`](ui-session/README.zh.md) | 把 Session Controller 状态适配为标准 Slot source 与 hook | — | +| [`ui-theme/`](ui-theme/README.zh.md) | 应用所选颜色主题 | — | +| [`ui-primitives/`](ui-primitives/README.zh.md) | 提供共享 React 控件、图标与内容渲染器 | — | +| [`ui-attachment/`](ui-attachment/README.zh.md) | 注册输入框与消息图片的附件呈现 | — | +| [`ui-layout/`](ui-layout/README.zh.md) | 排列应用的主要区域 | — | +| [`ui-sidebar/`](ui-sidebar/README.zh.md) | 展示工作区与会话导航 | — | +| [`ui-brand-official/`](ui-brand-official/README.zh.md) | 用官方名称与标记填充通用浏览器品牌 slot | — | +| [`ui-workspace/`](ui-workspace/README.zh.md) | 提供工作区选择与创建界面 | — | +| [`ui-conversation/`](ui-conversation/README.zh.md) | 展示当前对话及其输入界面 | — | +| [`ui-chat/`](ui-chat/README.zh.md) | 投影并渲染 Chat 对话 target | — | +| [`ui-approval/`](ui-approval/README.zh.md) | 展示批准请求并返回用户决策 | — | +| [`ui-tool/`](ui-tool/README.zh.md) | 编排工具调用树与按工具键控的视图 | — | +| [`ui-workflow-run/`](ui-workflow-run/README.zh.md) | 把持久工作流运行回放为嵌套对话折叠项 | — | +| [`ui-goal/`](ui-goal/README.zh.md) | 展示与管理当前目标 | — | +| [`ui-trajectory/`](ui-trajectory/README.zh.md) | 提供 agent(智能体)活动的其他视图 | — | +| [`ui-commands/`](ui-commands/README.zh.md) | 提供会话感知的命令发现与分发 | — | +| [`ui-input-trigger/`](ui-input-trigger/README.zh.md) | 协调内联命令与引用建议 | — | +| [`ui-skill/`](ui-skill/README.zh.md) | 向内联建议添加 skill(技能)引用 | — | +| [`ui-reference/`](ui-reference/README.zh.md) | 统一的 Web `@file` / `@session` 引用 source | — | +| [`ui-subagent/`](ui-subagent/README.zh.md) | 提供 subagent(子智能体)导航、子级 transcript(文本记录)状态与内联引用 | — | +| [`ui-jobs/`](ui-jobs/README.zh.md) | 在会话标题栏列出当前会话的后台任务 | — | +| [`ui-model-selection/`](ui-model-selection/README.zh.md) | 在对话界面中提供模型选择 | — | +| [`ui-permission-presets/`](ui-permission-presets/README.zh.md) | 配置默认权限并切换当前会话的访问模式 | — | +| [`ui-plan/`](ui-plan/README.zh.md) | 展示生效中的 plan mode 状态及其退出控件 | — | +| [`ui-settings-plugins/`](ui-settings-plugins/README.zh.md) | 拥有“插件”设置分区、其标签页扩展点与可配置的宿主平面插件卡片 | — | +| [`ui-user-questions/`](ui-user-questions/README.zh.md) | 展示 agent 请求的交互式问题 | — | +| [`ui-agent-preset/`](ui-agent-preset/README.zh.md) | 选择会话的 agent 预设并编写预设组合 | — | +| [`ui-settings/`](ui-settings/README.zh.md) | 承载设置界面及其扩展区域 | — | +| [`ui-settings-general/`](ui-settings-general/README.zh.md) | 提供常规设置分区 | — | +| [`ui-settings-models/`](ui-settings-models/README.zh.md) | 提供模型提供方配置与 DeepSeek 引导 | — | +| [`ui-settings-plugin-inventory/`](ui-settings-plugin-inventory/README.zh.md) | 向“插件”设置贡献只读的 Host Loader 清单标签页 | — | +| [`ui-deliverables/`](ui-deliverables/README.zh.md) | 生成已产出文件的轮次尾部与可点击的最终响应文件引用 | — | +| [`ui-message-feedback/`](ui-message-feedback/README.zh.md) | 向助手消息操作条贡献逐消息反馈控件 | — | +| [`ui-directory-picker-browse/`](ui-directory-picker-browse/README.zh.md) | 面向工作区目录流程的应用内目录浏览界面 | — | +| [`ui-directory-picker-native/`](ui-directory-picker-native/README.zh.md) | 驱动宿主 OS 选择器的原生目录选择界面 | — | + +----- + + +## 相关文档 + +先从子系统参考与两份拥有跨包组合决策的 Agent Note 读起,再看服务于本页的宿主半侧。 + +- [客户端模块子系统](../../docs/subsystems/client-modules.zh.md)——web 插件表:`dsh.client` 声明、启动图协议与 bundle 路由。 +- [slot 系统标准](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.zh.md)——权威 slot 模型:注册、props 份额与 store。 +- [web 客户端架构 Agent Note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.zh.md)——加载链、对象层与客户端服务。 +- [宿主组地图](../host/README.zh.md)——服务于本浏览器半侧的宿主半侧。 + + +## 开发备注 + +
+维护者的工作上下文——点击展开 + +无。 + +
diff --git a/packages/client/connection/README.i18n.yaml b/packages/client/connection/README.i18n.yaml index 34c03db722..75ab67d3de 100644 --- a/packages/client/connection/README.i18n.yaml +++ b/packages/client/connection/README.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 packages/client/connection/README.md -README.md: 4aa85765b009bd9231687976b43bc923ba41177a -README.zh.md: da784c1ce2d966fe8f650d9ebb3b9c6313271829 +README.md: 72f8ee150c1879f920f189ad3d0221fce449c451 +README.zh.md: 3ef1e22554f568e5d4fe24556fafbc7ce0413209 diff --git a/packages/client/connection/README.md b/packages/client/connection/README.md index 4aa85765b0..72f8ee150c 100644 --- a/packages/client/connection/README.md +++ b/packages/client/connection/README.md @@ -1,11 +1,35 @@ +--- +description: "Browser-host wire layer for the web GUI: the shared API client, event-stream delivery with reconnect, the /api HTTP bridge, and the browser-trust fence, for users and maintainers composing or debugging the connection." +kind: "package-reference" +--- + # @deepseek-ai/dsh-client-connection English | [中文](README.zh.md) +## Summary + Protocol and connection-generation layer. The Client plugin mounts `ctx.connection`, containing the shared API client, current-page loopback state, generation-scoped observable `hostDescription`, a generic RPC carrier, and the registration point for one generation source and the connection loop. A generation publishes `hostDescription` and calls `onConnected` only after its source is ready and `host.describe` succeeds; source completion, failure, withdrawal, or an explicit stop clears that value before `ConnectionController` reconnects with backoff. +## Table of Contents + +- [Use this package](#use-this-package) +- [Browser authentication and request trust](#browser-authentication-and-request-trust) +- [Connection generation](#connection-generation) +- [Model Experience](#model-experience) +- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work) +- [Dev Note](#dev-note) + +----- + + +## Use this package + The browser uses HTTP POST for API Proxy and generic Remote unary calls. API Gateway owns the `/api/remote.mux` WebSocket and its logical streams; in-process compositions provide equivalent Remote streams through `connection.rpc.open` without opening a WebSocket. The Host half owns the sole `/api` route, Fetch bridge, browser authentication, and Host/Origin checks. Typert Gateway claims its Remote endpoints first, and unclaimed requests fall through to API Proxy. Loopback hostname classification remains package-internal to the browser-facing Client state. +----- + + ## Browser authentication and request trust Every Host RPC method and WebSocket stream requires one browser session; there is no method-specific loopback tier. Each process mints a random launch token. `dsh-web-app` prints and opens the ordinary root URL with `?token=...`; `frontend-static` delegates root and index requests to `ctx.connection.authorizeIndex`, which accepts that token only on `GET /`, writes an authority-bound signed cookie, and redirects to clean `/`. A missing, expired, malformed, or wrong-authority cookie returns 401 before RPC dispatch. Static assets remain public. The HTTP carrier accepts no query token outside the root exchange and no Authorization-header token. @@ -14,12 +38,14 @@ The cookie signing secret is the owner-scoped `client-connection/browser-session Before authentication, every request still passes `src/api-request-trust.ts`. Its `Host` must be loopback or match a `trustedHosts` entry: exact on `host:port`, any port on port-less entries, both sides WHATWG-normalized. An attached `Origin` must equal that Host and `sec-fetch-site: cross-site` is refused. Malformed configured authorities fail plugin load. These checks defend DNS rebinding and cross-site browser requests; they never establish identity. A failed Host/Origin check returns 403, while a trusted but unauthenticated request returns 401. `dsh web --host 0.0.0.0` remains unsupported. Decision records: [browser request trust](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md) and [browser token authentication](../../../.agents/notes/implemented/architecture/2026-08-24-browser-token-authentication.md). + ## Connection generation API Gateway Client registers the internal `$events` logical stream as the sole generation source, independently of whether any `$on` listener exists. The Host attaches all incremental listeners in the API Remotes source factory, then sends one `{ type: 'ready' }` item before events. `ConnectionController` waits for that item and `host.describe` in parallel; `onConnected` cannot start baseline reads until both succeed, so baseline acquisition cannot race ahead of incremental observation. An ended `$events` stream, a Remote stream error, a non-ready opening item, or a malformed event item invalidates the current generation. The controller immediately withdraws `hostDescription`, publishes `reconnecting`, and rebuilds the `$events` plus `host.describe` handshake after backoff. Gateway mux reconnects the physical WebSocket; Connection generation reopens the logical stream and establishes the next baseline starting point. + ## Model Experience None, as the wire consumer layer moves already-composed messages between browser and host; nothing here reaches a model request. @@ -30,6 +56,19 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work + + - **The `/api` bridge buffers each request body in memory** — `maxRequestBodyBytes` (default 300 MiB, sized for the default 200 MiB aggregate image limit after base64 expansion plus envelope headroom) is therefore also the per-request resident bound; a streaming body path would be needed to lower it without shrinking the image limits. -- **The browser cookie is not marked `Secure`** — loopback HTTP is the shipped transport, so deployments that make the same authority reachable over plaintext networking can expose the bearer cookie in transit. -- **There is no logout operation** — clearing the browser cookie ends one browser session; deleting the owner credential record and restarting `dsh` revokes every session, and the next Connection activation creates a new signing secret. +- **The browser cookie is not marked `Secure`** — loopback HTTP is the shipped transport, so exposing the same authority over plaintext networking can expose the bearer cookie in transit. +- **There is no logout operation** — clearing the browser cookie ends one browser session; deleting the owner credential record and restarting `dsh` revokes every session. + + + +### Dev Note + +
+Working context for maintainers — click to expand + +None. + +
diff --git a/packages/client/connection/README.zh.md b/packages/client/connection/README.zh.md index da784c1ce2..3ef1e22554 100644 --- a/packages/client/connection/README.zh.md +++ b/packages/client/connection/README.zh.md @@ -1,11 +1,35 @@ +--- +description: "面向用户与维护者的浏览器-宿主线层说明:共享 API 客户端、带重连的事件流投递、/api HTTP 桥与浏览器信任栅栏,用于组合或排查连接。" +kind: "package-reference" +--- + # @deepseek-ai/dsh-client-connection [English](README.md) | 中文 +## 概述 + 协议与连接世代层:Client 插件挂载 `ctx.connection`,包含共享 API 客户端、当前页面的 loopback 状态、按 generation 生效的可观察 `hostDescription`、通用 RPC carrier,以及单一 generation source 与连接循环的注册面。每个 generation 只在 source 已就绪且 `host.describe` 成功后发布 `hostDescription` 并调用 `onConnected`;source 结束、失败、被撤回或显式 stop 都会清空该值,再由 `ConnectionController` 退避重连。 +## 目录 + +- [使用本包](#use-this-package) +- [浏览器认证与请求信任](#browser-authentication-and-request-trust) +- [Connection generation](#connection-generation) +- [模型体验](#model-experience) +- [已知限制与暂缓事项](#known-limitations-and-deferred-work) +- [开发备注](#dev-note) + +----- + + +## 使用本包 + 浏览器通过 HTTP POST 执行 API Proxy 一元调用与通用 Remote 一元调用;API Gateway 自己拥有 `/api/remote.mux` WebSocket 及其逻辑流。进程内组合通过 `connection.rpc.open` 提供等价的 Remote 流,不打开 WebSocket。Host half 拥有唯一 `/api` route、Fetch bridge、浏览器认证与 Host/Origin 校验;Typert Gateway 先认领自己的 Remote endpoint,未认领的请求再回退 API Proxy。Loopback hostname 判定只供浏览器侧当前页面状态使用,留在包内。 +----- + + ## 浏览器认证与请求信任 每个 Host RPC 方法和 WebSocket stream 都要求同一个浏览器会话,不存在按方法区分的 loopback 层。每个进程生成一个随机启动令牌。`dsh-web-app` 打印并打开带 `?token=...` 的普通根 URL;`frontend-static` 把根路径和 index 请求交给 `ctx.connection.authorizeIndex`,后者只在 `GET /` 接受该令牌,写入绑定 authority 的签名 cookie,再重定向到干净的 `/`。缺失、过期、畸形或 authority 不匹配的 cookie 会在 RPC 分发前得到 401。静态资源保持公开。HTTP 载体不在根路径交换之外接受 query token,也不接受 Authorization header token。 @@ -14,12 +38,14 @@ cookie 签名密钥是 `ctx.credentials` 中由 `client-connection/browser-sessi 认证之前,每个请求仍经过 `src/api-request-trust.ts`。其 `Host` 必须是 loopback,或与 `trustedHosts` 条目匹配:带端口的 `host:port` 精确匹配,不带端口的条目匹配任意端口,两侧均经 WHATWG 归一化。若附带 `Origin`,它必须等于该 Host;`sec-fetch-site: cross-site` 一律拒绝。畸形配置 authority 会让插件加载失败。这些检查防御 DNS rebinding 与跨站浏览器请求,绝不建立身份。Host/Origin 校验失败返回 403;Host 可信但未认证的请求返回 401。`dsh web --host 0.0.0.0` 仍不受支持。决策记录:[浏览器请求信任](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.zh.md)与[浏览器令牌认证](../../../.agents/notes/implemented/architecture/2026-08-24-browser-token-authentication.zh.md)。 + ## Connection generation API Gateway Client 把内部 `$events` logical stream 注册为唯一 generation source,与有无 `$on` 订阅无关。Host 在 API Remotes source factory 同步挂好所有增量 listener 后,先发送唯一 `{ type: 'ready' }` 项,再发送事件。`ConnectionController` 并行等待该 ready 与 `host.describe`;只有两者都成功才允许 `onConnected` 启动 baseline 读取,因此 baseline 不会跑在增量 listener 前面。 `$events` 结束、返回 Remote stream error、收到非 ready 首项或畸形事件项,都会使当前 generation 失效。Controller 立即撤回 `hostDescription`、发布 `reconnecting`,并在退避后重建 `$events` 与 `host.describe` 握手。Gateway mux 自己负责重建底层 WebSocket;Connection 世代负责重建 logical stream 与 baseline 起点。 + ## 模型体验 无。协议消费层只在浏览器与主机之间搬运已经组合好的消息;这里没有任何内容进入模型请求。 @@ -30,6 +56,19 @@ API Gateway Client 把内部 `$events` logical stream 注册为唯一 generation ## 已知限制与暂缓事项 + + - **`/api` 桥把每个请求体整体缓冲在内存里**:`maxRequestBodyBytes`(默认 300 MiB,按默认 200 MiB 图片总量上限经 base64 膨胀加信封余量得出)因此同时是单请求的驻留内存上界;要降低它而不缩小图片限额,需要流式请求体路径。 -- **浏览器 cookie 不带 `Secure`**:随附载体是 loopback HTTP;若部署把同一 authority 经明文网络暴露,bearer cookie 可能在传输中泄露。 -- **没有 logout 操作**:清除浏览器 cookie 会结束单个浏览器会话;删除 owner 凭据记录并重启 `dsh` 会撤销全部会话,下一次 Connection 激活会创建新的签名密钥。 +- **浏览器 cookie 不带 `Secure`**:随附载体是 loopback HTTP;若部署经明文网络暴露同一 authority,bearer cookie 可能在传输中泄露。 +- **没有 logout 操作**:清除浏览器 cookie 会结束单个浏览器会话;删除 owner 凭据记录并重启 `dsh` 会撤销全部会话。 + + + +### 开发备注 + +
+维护者工作上下文——点击展开 + +无。 + +
diff --git a/packages/client/hmr/README.i18n.yaml b/packages/client/hmr/README.i18n.yaml index 889ae51a03..df1a0ea553 100644 --- a/packages/client/hmr/README.i18n.yaml +++ b/packages/client/hmr/README.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 packages/client/hmr/README.md -README.md: 82d203fd9e19590b93ab1f9bf8e2c0673ce1204b -README.zh.md: 8b62b4948400c0a91232f897e306792cdf3260e7 +README.md: 88438279301d4a893f2c245a6580e63ea0ca7930 +README.zh.md: f56abdbb0473a850af933be0653e217fd51668de diff --git a/packages/client/hmr/README.md b/packages/client/hmr/README.md index 82d203fd9e..8843827930 100644 --- a/packages/client/hmr/README.md +++ b/packages/client/hmr/README.md @@ -1,14 +1,106 @@ +--- +description: "Development-only hot reload for browser client plugins: rebuilding a plugin bundle swaps the running plugin in place, for developers iterating on the web GUI." +kind: "package-reference" +--- + # @deepseek-ai/dsh-client-hmr English | [中文](README.zh.md) -Hot reload for script-loaded client plugins. The web bundle mounts the row unconditionally; without a rebuild watcher (`pnpm run dev:web`) rewriting client bundles, the poll observes no changes and the chain stays idle. +## Summary -The browser half subscribes to the system SSE channel (`GET /plugins/events`) and reloads one plugin per `rebuilt` frame through a serialized queue. The frame revision makes `invalidate` select that plugin's immutable one-resource combo URL instead of its initial multi-resource URL; `prefetch` loads and registers the new factory while the old fiber still serves. The remaining sequence is `registry.delete` (before the fiber: a bare fiber dispose trips the vendored Loader's self-dispose branch, which would mark the entry disabled), drain the old fiber, delete `entry.fiber`, remove owned `