dsh-plugin-guide 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.es.md +1 -1
- package/README.hi.md +1 -1
- package/README.md +1 -1
- package/README.pt.md +1 -1
- package/README.zh.md +1 -1
- package/SKILL.md +1 -1
- package/dist/dsh-plugin-dev.js +5 -5
- package/guide/links.md +1 -1
- package/guide/plugin-dev-guide.md +1 -1
- package/guide/quick-reference.es.md +2 -2
- package/guide/quick-reference.hi.md +1 -1
- package/guide/quick-reference.md +2 -2
- package/guide/quick-reference.pt.md +2 -2
- package/guide/quick-reference.zh-CN.md +2 -2
- package/package.json +3 -3
- package/references/cordis-paper-and-community.md +1 -1
- package/references/official-docs/AGENTS.md +27 -22
- package/references/official-docs/README.i18n.yaml +2 -2
- package/references/official-docs/README.md +1 -1
- package/references/official-docs/README.zh.md +19 -9
- package/references/official-docs/SNAPSHOT.md +5 -5
- package/references/official-docs/THIRD_PARTY_NOTICES.md +36 -13
- package/references/official-docs/docs/AGENTS.md +4 -4
- package/references/official-docs/docs/agent-lifecycle.i18n.yaml +2 -2
- package/references/official-docs/docs/agent-lifecycle.md +1 -1
- package/references/official-docs/docs/agent-lifecycle.zh.md +2 -2
- package/references/official-docs/docs/api-gateway.i18n.yaml +2 -2
- package/references/official-docs/docs/api-gateway.md +8 -8
- package/references/official-docs/docs/api-gateway.zh.md +8 -8
- package/references/official-docs/docs/architecture.i18n.yaml +2 -2
- package/references/official-docs/docs/architecture.md +23 -7
- package/references/official-docs/docs/architecture.zh.md +36 -20
- package/references/official-docs/docs/capability-seams.i18n.yaml +2 -2
- package/references/official-docs/docs/capability-seams.md +132 -67
- package/references/official-docs/docs/capability-seams.zh.md +132 -67
- package/references/official-docs/docs/config-catalog.i18n.yaml +2 -2
- package/references/official-docs/docs/config-catalog.md +659 -373
- package/references/official-docs/docs/config-catalog.zh.md +677 -391
- package/references/official-docs/docs/cookbook/adding-a-package.i18n.yaml +2 -2
- package/references/official-docs/docs/cookbook/adding-a-package.md +2 -3
- package/references/official-docs/docs/cookbook/adding-a-package.zh.md +8 -7
- package/references/official-docs/docs/cookbook/{adding-a-conversation-node.i18n.yaml → adding-a-remote-api.i18n.yaml} +3 -3
- package/references/official-docs/docs/cookbook/adding-a-remote-api.md +197 -0
- package/references/official-docs/docs/cookbook/adding-a-remote-api.zh.md +197 -0
- package/references/official-docs/docs/cookbook/adding-a-settings-card.i18n.yaml +6 -0
- package/references/official-docs/docs/cookbook/adding-a-settings-card.md +102 -0
- package/references/official-docs/docs/cookbook/adding-a-settings-card.zh.md +102 -0
- package/references/official-docs/docs/cookbook/adding-a-tool.i18n.yaml +2 -2
- package/references/official-docs/docs/cookbook/adding-a-tool.md +11 -4
- package/references/official-docs/docs/cookbook/adding-a-tool.zh.md +16 -9
- package/references/official-docs/docs/cookbook/adding-a-vendored-package.i18n.yaml +2 -2
- package/references/official-docs/docs/cookbook/adding-a-vendored-package.md +2 -2
- package/references/official-docs/docs/cookbook/adding-a-vendored-package.zh.md +4 -4
- package/references/official-docs/docs/cookbook/adding-an-llm-adapter.i18n.yaml +2 -2
- package/references/official-docs/docs/cookbook/adding-an-llm-adapter.md +1 -1
- package/references/official-docs/docs/cookbook/adding-an-llm-adapter.zh.md +3 -3
- package/references/official-docs/docs/cookbook/extension-cookbook.i18n.yaml +2 -2
- package/references/official-docs/docs/cookbook/extension-cookbook.md +9 -8
- package/references/official-docs/docs/cookbook/extension-cookbook.zh.md +17 -14
- package/references/official-docs/docs/cookbook/maintaining-dsh-code-review.i18n.yaml +1 -1
- package/references/official-docs/docs/cookbook/maintaining-dsh-code-review.zh.md +1 -1
- package/references/official-docs/docs/cookbook/responding-to-pr-review-on-a-stack.i18n.yaml +1 -1
- package/references/official-docs/docs/cookbook/responding-to-pr-review-on-a-stack.zh.md +1 -1
- package/references/official-docs/docs/cordis-api/context.i18n.yaml +1 -1
- package/references/official-docs/docs/cordis-api/context.zh.md +1 -1
- package/references/official-docs/docs/cordis-api/events.i18n.yaml +1 -1
- package/references/official-docs/docs/cordis-api/events.zh.md +1 -1
- package/references/official-docs/docs/cordis-api/inherited.md +1 -1
- package/references/official-docs/docs/cordis-primer.i18n.yaml +2 -2
- package/references/official-docs/docs/cordis-primer.md +2 -1
- package/references/official-docs/docs/cordis-primer.zh.md +3 -2
- package/references/official-docs/docs/cordis-tutorial/01-first-plugin.i18n.yaml +1 -1
- package/references/official-docs/docs/cordis-tutorial/01-first-plugin.zh.md +4 -4
- package/references/official-docs/docs/cordis-tutorial/02-lifecycle-and-effects.i18n.yaml +1 -1
- package/references/official-docs/docs/cordis-tutorial/02-lifecycle-and-effects.zh.md +4 -4
- package/references/official-docs/docs/cordis-tutorial/03-services.i18n.yaml +1 -1
- package/references/official-docs/docs/cordis-tutorial/03-services.zh.md +4 -4
- package/references/official-docs/docs/cordis-tutorial/04-events.i18n.yaml +1 -1
- package/references/official-docs/docs/cordis-tutorial/04-events.zh.md +4 -4
- package/references/official-docs/docs/cordis-tutorial/05-config.i18n.yaml +1 -1
- package/references/official-docs/docs/cordis-tutorial/05-config.zh.md +2 -2
- package/references/official-docs/docs/cordis-tutorial/06-composition-and-hmr.i18n.yaml +1 -1
- package/references/official-docs/docs/cordis-tutorial/06-composition-and-hmr.zh.md +3 -3
- package/references/official-docs/docs/cordis-tutorial/07-into-the-harness.i18n.yaml +2 -2
- package/references/official-docs/docs/cordis-tutorial/07-into-the-harness.md +5 -4
- package/references/official-docs/docs/cordis-tutorial/07-into-the-harness.zh.md +11 -10
- package/references/official-docs/docs/cordis-tutorial/index.i18n.yaml +1 -1
- package/references/official-docs/docs/cordis-tutorial/index.zh.md +10 -10
- package/references/official-docs/docs/deepseek-llm-api-wire-extensions.i18n.yaml +6 -0
- package/references/official-docs/docs/deepseek-llm-api-wire-extensions.md +159 -0
- package/references/official-docs/docs/deepseek-llm-api-wire-extensions.zh.md +159 -0
- package/references/official-docs/docs/defensive-patterns.i18n.yaml +1 -1
- package/references/official-docs/docs/defensive-patterns.zh.md +3 -1
- package/references/official-docs/docs/development.i18n.yaml +2 -2
- package/references/official-docs/docs/development.md +9 -13
- package/references/official-docs/docs/development.zh.md +23 -21
- package/references/official-docs/docs/event-producer-consumer.i18n.yaml +2 -2
- package/references/official-docs/docs/event-producer-consumer.md +55 -46
- package/references/official-docs/docs/event-producer-consumer.zh.md +55 -46
- package/references/official-docs/docs/glossary.i18n.yaml +2 -2
- package/references/official-docs/docs/glossary.md +1 -1
- package/references/official-docs/docs/glossary.zh.md +1 -1
- package/references/official-docs/docs/graph-atlas.i18n.yaml +2 -2
- package/references/official-docs/docs/graph-atlas.md +0 -2
- package/references/official-docs/docs/graph-atlas.zh.md +7 -9
- package/references/official-docs/docs/i18n/README.i18n.yaml +2 -2
- package/references/official-docs/docs/i18n/README.md +7 -7
- package/references/official-docs/docs/i18n/README.zh.md +11 -9
- package/references/official-docs/docs/i18n/style-samples.md +3 -3
- package/references/official-docs/docs/i18n/translation-prompt.md +3 -3
- package/references/official-docs/docs/i18n/translation-rules.i18n.yaml +2 -2
- package/references/official-docs/docs/i18n/translation-rules.md +2 -2
- package/references/official-docs/docs/i18n/translation-rules.zh.md +3 -3
- package/references/official-docs/docs/module-graph.i18n.yaml +2 -2
- package/references/official-docs/docs/module-graph.md +666 -747
- package/references/official-docs/docs/module-graph.zh.md +668 -749
- package/references/official-docs/docs/persistence-catalog.i18n.yaml +2 -2
- package/references/official-docs/docs/persistence-catalog.md +172 -40
- package/references/official-docs/docs/persistence-catalog.zh.md +180 -48
- package/references/official-docs/docs/postmortem/0001-acp-default-export-drops-inject.i18n.yaml +2 -2
- package/references/official-docs/docs/postmortem/0001-acp-default-export-drops-inject.md +2 -2
- package/references/official-docs/docs/postmortem/0001-acp-default-export-drops-inject.zh.md +3 -3
- package/references/official-docs/docs/postmortem/0002-js-expression-disabled-filesystem-tools.i18n.yaml +2 -2
- package/references/official-docs/docs/postmortem/0002-js-expression-disabled-filesystem-tools.md +2 -2
- package/references/official-docs/docs/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +2 -2
- package/references/official-docs/docs/postmortem/0003-web-agent-gui-feedback-loop.i18n.yaml +2 -2
- package/references/official-docs/docs/postmortem/0003-web-agent-gui-feedback-loop.md +2 -2
- package/references/official-docs/docs/postmortem/0003-web-agent-gui-feedback-loop.zh.md +2 -2
- package/references/official-docs/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.i18n.yaml +2 -2
- package/references/official-docs/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +1 -1
- package/references/official-docs/docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +2 -2
- package/references/official-docs/docs/postmortem/README.i18n.yaml +1 -1
- package/references/official-docs/docs/postmortem/README.zh.md +5 -5
- package/references/official-docs/docs/rescope.i18n.yaml +2 -2
- package/references/official-docs/docs/rescope.md +2 -2
- package/references/official-docs/docs/rescope.zh.md +3 -3
- package/references/official-docs/docs/subsystems/README.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/README.md +8 -2
- package/references/official-docs/docs/subsystems/README.zh.md +53 -47
- package/references/official-docs/docs/subsystems/agent-team.i18n.yaml +6 -0
- package/references/official-docs/docs/subsystems/agent-team.md +206 -0
- package/references/official-docs/docs/subsystems/agent-team.zh.md +206 -0
- package/references/official-docs/docs/subsystems/approval.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/approval.md +12 -12
- package/references/official-docs/docs/subsystems/approval.zh.md +13 -13
- package/references/official-docs/docs/subsystems/attachment.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/attachment.md +90 -7
- package/references/official-docs/docs/subsystems/attachment.zh.md +90 -7
- package/references/official-docs/docs/subsystems/client-modules.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/client-modules.md +69 -19
- package/references/official-docs/docs/subsystems/client-modules.zh.md +69 -19
- package/references/official-docs/docs/subsystems/code-runtime.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/code-runtime.md +5 -5
- package/references/official-docs/docs/subsystems/code-runtime.zh.md +6 -6
- package/references/official-docs/docs/subsystems/commands.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/commands.md +29 -6
- package/references/official-docs/docs/subsystems/commands.zh.md +30 -7
- package/references/official-docs/docs/subsystems/compaction.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/compaction.md +5 -5
- package/references/official-docs/docs/subsystems/compaction.zh.md +11 -11
- package/references/official-docs/docs/subsystems/conversation.i18n.yaml +6 -0
- package/references/official-docs/docs/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +49 -24
- package/references/official-docs/docs/{cookbook/adding-a-conversation-node.zh.md → subsystems/conversation.zh.md} +49 -24
- package/references/official-docs/docs/subsystems/core.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/core.md +110 -33
- package/references/official-docs/docs/subsystems/core.zh.md +141 -62
- package/references/official-docs/docs/subsystems/credentials.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/credentials.md +207 -11
- package/references/official-docs/docs/subsystems/credentials.zh.md +207 -11
- package/references/official-docs/docs/subsystems/extensions.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/extensions.md +27 -9
- package/references/official-docs/docs/subsystems/extensions.zh.md +30 -12
- package/references/official-docs/docs/subsystems/feedback.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/feedback.md +4 -4
- package/references/official-docs/docs/subsystems/feedback.zh.md +4 -4
- package/references/official-docs/docs/subsystems/filesystem.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/filesystem.md +17 -7
- package/references/official-docs/docs/subsystems/filesystem.zh.md +19 -9
- package/references/official-docs/docs/subsystems/goal.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/goal.md +4 -4
- package/references/official-docs/docs/subsystems/goal.zh.md +8 -8
- package/references/official-docs/docs/subsystems/invariants.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/invariants.md +2 -2
- package/references/official-docs/docs/subsystems/invariants.zh.md +5 -5
- package/references/official-docs/docs/subsystems/jobs.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/jobs.md +3 -3
- package/references/official-docs/docs/subsystems/jobs.zh.md +6 -6
- package/references/official-docs/docs/subsystems/llm-streaming.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/llm-streaming.md +193 -22
- package/references/official-docs/docs/subsystems/llm-streaming.zh.md +199 -28
- package/references/official-docs/docs/subsystems/lsp.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/lsp.md +2 -2
- package/references/official-docs/docs/subsystems/lsp.zh.md +3 -3
- package/references/official-docs/docs/subsystems/permission-presets.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/permission-presets.md +8 -8
- package/references/official-docs/docs/subsystems/permission-presets.zh.md +8 -8
- package/references/official-docs/docs/subsystems/persistence.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/persistence.md +35 -17
- package/references/official-docs/docs/subsystems/persistence.zh.md +38 -20
- package/references/official-docs/docs/subsystems/plan.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/plan.md +4 -4
- package/references/official-docs/docs/subsystems/plan.zh.md +10 -10
- package/references/official-docs/docs/subsystems/sandbox.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/sandbox.md +5 -3
- package/references/official-docs/docs/subsystems/sandbox.zh.md +8 -6
- package/references/official-docs/docs/subsystems/schedule.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/schedule.md +9 -3
- package/references/official-docs/docs/subsystems/schedule.zh.md +11 -5
- package/references/official-docs/docs/subsystems/scope.i18n.yaml +1 -1
- package/references/official-docs/docs/subsystems/scope.zh.md +1 -1
- package/references/official-docs/docs/subsystems/session-projection.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/session-projection.md +136 -67
- package/references/official-docs/docs/subsystems/session-projection.zh.md +136 -67
- package/references/official-docs/docs/subsystems/session-query.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/session-query.md +11 -3
- package/references/official-docs/docs/subsystems/session-query.zh.md +12 -4
- package/references/official-docs/docs/subsystems/session-reference.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/session-reference.md +94 -7
- package/references/official-docs/docs/subsystems/session-reference.zh.md +95 -8
- package/references/official-docs/docs/subsystems/session-telemetry.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/session-telemetry.md +5 -6
- package/references/official-docs/docs/subsystems/session-telemetry.zh.md +9 -10
- package/references/official-docs/docs/subsystems/session-title.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/session-title.md +3 -3
- package/references/official-docs/docs/subsystems/session-title.zh.md +5 -5
- package/references/official-docs/docs/subsystems/session.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/session.md +284 -39
- package/references/official-docs/docs/subsystems/session.zh.md +301 -54
- package/references/official-docs/docs/subsystems/settings.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/settings.md +105 -10
- package/references/official-docs/docs/subsystems/settings.zh.md +105 -10
- package/references/official-docs/docs/subsystems/shell.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/shell.md +3 -3
- package/references/official-docs/docs/subsystems/shell.zh.md +11 -11
- package/references/official-docs/docs/subsystems/skills.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/skills.md +26 -3
- package/references/official-docs/docs/subsystems/skills.zh.md +29 -6
- package/references/official-docs/docs/subsystems/slots.i18n.yaml +6 -0
- package/references/official-docs/docs/subsystems/slots.md +174 -0
- package/references/official-docs/docs/subsystems/slots.zh.md +174 -0
- package/references/official-docs/docs/subsystems/spill.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/spill.md +4 -4
- package/references/official-docs/docs/subsystems/spill.zh.md +6 -6
- package/references/official-docs/docs/subsystems/storage.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/storage.md +13 -5
- package/references/official-docs/docs/subsystems/storage.zh.md +16 -8
- package/references/official-docs/docs/subsystems/subagent.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/subagent.md +120 -32
- package/references/official-docs/docs/subsystems/subagent.zh.md +131 -41
- package/references/official-docs/docs/subsystems/subprocess.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/subprocess.md +3 -3
- package/references/official-docs/docs/subsystems/subprocess.zh.md +5 -5
- package/references/official-docs/docs/subsystems/system-prompt.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/system-prompt.md +21 -8
- package/references/official-docs/docs/subsystems/system-prompt.zh.md +23 -10
- package/references/official-docs/docs/subsystems/terminal.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/terminal.md +2 -2
- package/references/official-docs/docs/subsystems/terminal.zh.md +4 -4
- package/references/official-docs/docs/subsystems/todo.i18n.yaml +6 -0
- package/references/official-docs/docs/subsystems/todo.md +32 -0
- package/references/official-docs/docs/subsystems/todo.zh.md +32 -0
- package/references/official-docs/docs/subsystems/token-meter.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/token-meter.md +27 -12
- package/references/official-docs/docs/subsystems/token-meter.zh.md +28 -13
- package/references/official-docs/docs/subsystems/tools.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/tools.md +46 -46
- package/references/official-docs/docs/subsystems/tools.zh.md +54 -54
- package/references/official-docs/docs/subsystems/typert.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/typert.md +65 -58
- package/references/official-docs/docs/subsystems/typert.zh.md +67 -60
- package/references/official-docs/docs/subsystems/user-questions.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/user-questions.md +35 -35
- package/references/official-docs/docs/subsystems/user-questions.zh.md +35 -35
- package/references/official-docs/docs/subsystems/web-client.i18n.yaml +6 -0
- package/references/official-docs/docs/subsystems/web-client.md +95 -0
- package/references/official-docs/docs/subsystems/web-client.zh.md +95 -0
- package/references/official-docs/docs/subsystems/web-server.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/web-server.md +55 -9
- package/references/official-docs/docs/subsystems/web-server.zh.md +56 -10
- package/references/official-docs/docs/subsystems/web.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/web.md +14 -7
- package/references/official-docs/docs/subsystems/web.zh.md +16 -9
- package/references/official-docs/docs/subsystems/webhook.i18n.yaml +6 -0
- package/references/official-docs/docs/subsystems/webhook.md +70 -0
- package/references/official-docs/docs/subsystems/webhook.zh.md +70 -0
- package/references/official-docs/docs/subsystems/workflow.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/workflow.md +8 -8
- package/references/official-docs/docs/subsystems/workflow.zh.md +13 -13
- package/references/official-docs/docs/subsystems/workspace.i18n.yaml +2 -2
- package/references/official-docs/docs/subsystems/workspace.md +98 -5
- package/references/official-docs/docs/subsystems/workspace.zh.md +102 -9
- package/references/official-docs/docs/testing.i18n.yaml +2 -2
- package/references/official-docs/docs/testing.md +15 -10
- package/references/official-docs/docs/testing.zh.md +15 -10
- package/references/official-docs/docs/tool-catalog.i18n.yaml +2 -2
- package/references/official-docs/docs/tool-catalog.md +433 -27
- package/references/official-docs/docs/tool-catalog.zh.md +438 -29
- package/references/official-docs/docs/tool-execution-pipeline.i18n.yaml +2 -2
- package/references/official-docs/docs/tool-execution-pipeline.md +1 -1
- package/references/official-docs/docs/tool-execution-pipeline.zh.md +1 -1
- package/references/official-docs/docs/user/develop/basic/config.i18n.yaml +2 -2
- package/references/official-docs/docs/user/develop/basic/config.md +1 -1
- package/references/official-docs/docs/user/develop/basic/config.zh.md +4 -4
- package/references/official-docs/docs/user/develop/basic/index.i18n.yaml +1 -1
- package/references/official-docs/docs/user/develop/basic/index.zh.md +5 -5
- package/references/official-docs/docs/user/develop/basic/publish.i18n.yaml +2 -2
- package/references/official-docs/docs/user/develop/basic/publish.md +3 -3
- package/references/official-docs/docs/user/develop/basic/publish.zh.md +7 -7
- package/references/official-docs/docs/user/develop/basic/tool.i18n.yaml +2 -2
- package/references/official-docs/docs/user/develop/basic/tool.md +3 -3
- package/references/official-docs/docs/user/develop/basic/tool.zh.md +4 -4
- package/references/official-docs/docs/user/develop/framework/events.i18n.yaml +2 -2
- package/references/official-docs/docs/user/develop/framework/events.md +2 -2
- package/references/official-docs/docs/user/develop/framework/events.zh.md +3 -3
- package/references/official-docs/docs/user/develop/framework/index.i18n.yaml +1 -1
- package/references/official-docs/docs/user/develop/framework/index.zh.md +3 -3
- package/references/official-docs/docs/user/develop/framework/service.i18n.yaml +2 -2
- package/references/official-docs/docs/user/develop/framework/service.md +1 -1
- package/references/official-docs/docs/user/develop/framework/service.zh.md +3 -3
- package/references/official-docs/docs/user/develop/practice/dynamic-cordis.i18n.yaml +6 -0
- package/references/official-docs/docs/user/develop/practice/dynamic-cordis.md +15 -0
- package/references/official-docs/docs/user/develop/practice/dynamic-cordis.zh.md +15 -0
- package/references/official-docs/docs/user/develop/practice/index.i18n.yaml +2 -2
- package/references/official-docs/docs/user/develop/practice/index.md +1 -1
- package/references/official-docs/docs/user/develop/practice/index.zh.md +3 -3
- package/references/official-docs/docs/user/develop/practice/llm-adapter.i18n.yaml +2 -2
- package/references/official-docs/docs/user/develop/practice/llm-adapter.md +4 -3
- package/references/official-docs/docs/user/develop/practice/llm-adapter.zh.md +4 -3
- package/references/official-docs/docs/user/guide/github-review.i18n.yaml +6 -0
- package/references/official-docs/docs/user/guide/github-review.md +102 -0
- package/references/official-docs/docs/user/guide/github-review.zh.md +102 -0
- package/references/official-docs/docs/user/guide/index.i18n.yaml +2 -2
- package/references/official-docs/docs/user/guide/index.md +2 -2
- package/references/official-docs/docs/user/guide/index.zh.md +8 -8
- package/references/official-docs/docs/user/guide/mcp-memory.i18n.yaml +6 -0
- package/references/official-docs/docs/user/guide/mcp-memory.md +101 -0
- package/references/official-docs/docs/user/guide/mcp-memory.zh.md +101 -0
- package/references/official-docs/docs/user/guide/providers.i18n.yaml +2 -2
- package/references/official-docs/docs/user/guide/providers.md +40 -1
- package/references/official-docs/docs/user/guide/providers.zh.md +41 -2
- package/references/official-docs/docs/user/guide/python-sdk.i18n.yaml +2 -2
- package/references/official-docs/docs/user/guide/python-sdk.md +80 -34
- package/references/official-docs/docs/user/guide/python-sdk.zh.md +87 -41
- package/references/official-docs/docs/user/guide/schedule.i18n.yaml +6 -0
- package/references/official-docs/docs/user/guide/schedule.md +21 -0
- package/references/official-docs/docs/user/guide/schedule.zh.md +21 -0
- package/references/official-docs/docs/web-styling.i18n.yaml +1 -1
- package/references/official-docs/docs/web-styling.zh.md +2 -2
- package/references/official-docs/packages/AGENTS.md +3 -2
- package/references/official-docs/packages/README.md +104 -59
- package/references/official-docs/vendor/README.md +1 -0
- package/references/official-docs/website-docs.ts +52 -11
- package/references/sources.md +2 -2
- package/references/website-pages.md +3 -3
- package/scripts/download-sources.ps1 +1 -1
- package/scripts/sync-official-docs.ps1 +19 -11
- package/scripts/verify-kit.ps1 +0 -4
- package/templates/js/README.es.md +1 -1
- package/templates/js/README.hi.md +1 -1
- package/templates/js/README.md +1 -1
- package/templates/js/README.pt.md +1 -1
- package/templates/js/README.zh.md +1 -1
- package/templates/js/package.json +4 -4
- package/templates/ts/README.es.md +1 -1
- package/templates/ts/README.hi.md +1 -1
- package/templates/ts/README.md +1 -1
- package/templates/ts/README.pt.md +1 -1
- package/templates/ts/README.zh.md +1 -1
- package/templates/ts/package.json +4 -4
- package/references/official-docs/examples/AGENTS.md +0 -20
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write docs/cookbook/adding-a-package.md
|
|
5
|
-
adding-a-package.md:
|
|
6
|
-
adding-a-package.zh.md:
|
|
5
|
+
adding-a-package.md: f38c0d3f3bdfe126ca1de4801548c546fc5994cc
|
|
6
|
+
adding-a-package.zh.md: 11f6faa75e757b769ef84a039cef05afd1324a95
|
|
@@ -20,7 +20,7 @@ packages/<group>/<pkg>/
|
|
|
20
20
|
# (or a whitelist entry in scripts/verify-package-readme-limitations.ts)
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
Choose an existing group when one matches the package's role (`core`, `llm`, `
|
|
23
|
+
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.
|
|
24
24
|
|
|
25
25
|
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`.
|
|
26
26
|
|
|
@@ -32,7 +32,6 @@ In-package relative imports use explicit `.ts` specifiers in source (for example
|
|
|
32
32
|
|---|---|
|
|
33
33
|
| `tsconfig.base.json` | no edit for an existing group; for a new group, add a `./packages/<group>/*/src` candidate to the `@deepseek-ai/dsh-*` wildcard |
|
|
34
34
|
| `tsconfig.host.json` (Host package) or `tsconfig.client.json` (Client package) | add `{ "path": "./packages/<group>/<pkg>" }` to `references` — an ordinary package belongs to exactly one aggregate, never both. `api/remotes` uses a repository-specific split because the Host generates a contract that the Client consumes in a later phase; new packages must not copy it ([layout](../development.md#typescript-project-layout)) |
|
|
35
|
-
| `knip.json` | only if the package has entrypoints that repository discovery does not already cover |
|
|
36
35
|
|
|
37
36
|
A `packages/client/*` package additionally extends `tsconfig.base.client.json` instead of `tsconfig.base.json`, and a client plugin package declares `dsh.client` in package.json, exports `./client`, and calls the shared tsdown preset (`packages/client/tsdown.client.ts`) — see [packages/client/AGENTS.md](../../packages/client/AGENTS.md) for the client-side contract.
|
|
38
37
|
|
|
@@ -72,7 +71,7 @@ Use `SDK` only for the JSON-RPC client/server protocol used by the supported Pyt
|
|
|
72
71
|
|
|
73
72
|
## 4. Write the package README
|
|
74
73
|
|
|
75
|
-
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:
|
|
74
|
+
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:
|
|
76
75
|
|
|
77
76
|
````markdown
|
|
78
77
|
## Model Experience
|
|
@@ -20,7 +20,7 @@ packages/<group>/<pkg>/
|
|
|
20
20
|
# (or a whitelist entry in scripts/verify-package-readme-limitations.ts)
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
当已有分组与包的角色匹配时,选择该分组(`core`、`llm`、`
|
|
23
|
+
当已有分组与包的角色匹配时,选择该分组(`core`、`llm`、`shell`、`compaction`、`subagent`、`todo`、`session`、`client`/`host`、`util` 或 `test-support`)。允许新建分组,但分组只是纯容器:没有 `package.json`,没有源文件,包仍然恰好位于其下一层。
|
|
24
24
|
|
|
25
25
|
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` 之后。
|
|
26
26
|
|
|
@@ -31,8 +31,7 @@ package.json 不变式(由 `pnpm run constraints` / `scripts/check-workspace-c
|
|
|
31
31
|
| 文件 | 变更 |
|
|
32
32
|
|---|---|
|
|
33
33
|
| `tsconfig.base.json` | 已有分组无需编辑;新分组需为 `@deepseek-ai/dsh-*` 通配符添加 `./packages/<group>/*/src` 候选路径 |
|
|
34
|
-
| `tsconfig.host.json`(Host 包)或 `tsconfig.client.json`(Client 包) | 在 `references` 中添加 `{ "path": "./packages/<group>/<pkg>" }`——普通包恰好属于一个 aggregate,绝不两个都加。`api/remotes` 因 Host 生成约定与 Client 消费约定之间存在顺序依赖而使用仓库专属拆分,新增包不得仿照([布局](../development.md#typescript-project-layout)) |
|
|
35
|
-
| `knip.json` | 仅当包有仓库发现机制尚未覆盖的入口时需要 |
|
|
34
|
+
| `tsconfig.host.json`(Host 包)或 `tsconfig.client.json`(Client 包) | 在 `references` 中添加 `{ "path": "./packages/<group>/<pkg>" }`——普通包恰好属于一个 aggregate,绝不两个都加。`api/remotes` 因 Host 生成约定与 Client 消费约定之间存在顺序依赖而使用仓库专属拆分,新增包不得仿照([布局](../development.zh.md#typescript-project-layout)) |
|
|
36
35
|
|
|
37
36
|
`packages/client/*` 包改为 extends `tsconfig.base.client.json`(而非 `tsconfig.base.json`);client 插件包还需在 package.json 声明 `dsh.client`、导出 `./client`、调用共享 tsdown preset(`packages/client/tsdown.client.ts`)——client 侧见 [packages/client/AGENTS.md](../../packages/client/AGENTS.md)。
|
|
38
37
|
|
|
@@ -70,9 +69,11 @@ package.json 不变式(由 `pnpm run constraints` / `scripts/check-workspace-c
|
|
|
70
69
|
|
|
71
70
|
只对受支持的 Python 与 TypeScript SDK 所使用的 JSON-RPC 客户端/服务器协议使用 `SDK`。DeepSeek Harness 本身是 agent harness,不是 SDK 项目。产品拼写统一使用 `Typert`,不得使用 `TypeRT` 或 `typeRT`。
|
|
72
71
|
|
|
72
|
+
<a id="4-write-the-package-readme"></a>
|
|
73
|
+
|
|
73
74
|
## 4. 编写包 README
|
|
74
75
|
|
|
75
|
-
将包特有的服务 API
|
|
76
|
+
将包特有的服务 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 以如下规范序列结尾:
|
|
76
77
|
|
|
77
78
|
````markdown
|
|
78
79
|
## Model Experience
|
|
@@ -102,9 +103,9 @@ Append-only, prefix-stable, replacing, or independent behavior, including the ex
|
|
|
102
103
|
- **Consumer-visible gap** — exact missing operation or case, its consequence, and any maintainer constraint.
|
|
103
104
|
````
|
|
104
105
|
|
|
105
|
-
根据实现填写 Model Experience。每个直接、条件、上限、生命周期或辅助的模型上下文条目使用一个 H3,包含上述三个有序 H4 字段,每个字段下有一个正文段落。引用包拥有的稳定文本:系统提示词放在引出它的字段下,用带标题的 H5 加 `markdown` 围栏表示,通常归入 `What the model sees`;其他短文本以命名占位符内联,其他长文本使用相同的嵌套形式。仅概述数据依赖或提供方拥有的文本。工具 schema 条目链接到生成的[工具目录](../tool-catalog.md)中对应的锚定章节,仅说明该处缺失的差异。当作用域可以隐藏 prompt 或 schema 其中之一而不影响另一个时,将二者分开。填写 `KV Cache effect` 时,应区分仅追加增长、稳定重复的前缀、替换既有请求 token 和独立模型请求,并列出会使缓存复用失效、且由本包拥有的变化。“不使缓存失效”仅表示本包保留了已有的可复用前缀;缓存是否可用以及何时淘汰不属于本包约定。[行文标准](../../.agents/skills/dsh-prose-standard/SKILL.md)约束完整性与归属;验证器强制执行所需章节结构。
|
|
106
|
+
根据实现填写 Model Experience。每个直接、条件、上限、生命周期或辅助的模型上下文条目使用一个 H3,包含上述三个有序 H4 字段,每个字段下有一个正文段落。引用包拥有的稳定文本:系统提示词放在引出它的字段下,用带标题的 H5 加 `markdown` 围栏表示,通常归入 `What the model sees`;其他短文本以命名占位符内联,其他长文本使用相同的嵌套形式。仅概述数据依赖或提供方拥有的文本。工具 schema 条目链接到生成的[工具目录](../tool-catalog.zh.md)中对应的锚定章节,仅说明该处缺失的差异。当作用域可以隐藏 prompt 或 schema 其中之一而不影响另一个时,将二者分开。填写 `KV Cache effect` 时,应区分仅追加增长、稳定重复的前缀、替换既有请求 token 和独立模型请求,并列出会使缓存复用失效、且由本包拥有的变化。“不使缓存失效”仅表示本包保留了已有的可复用前缀;缓存是否可用以及何时淘汰不属于本包约定。[行文标准](../../.agents/skills/dsh-prose-standard/SKILL.md)约束完整性与归属;验证器强制执行所需章节结构。
|
|
106
107
|
|
|
107
|
-
没有上下文效果或仅有消费方拥有路径的包使用 [`SENTENCE_MODEL_EXPERIENCE`](../../scripts/verify-package-readme-model-experience.ts) 中经过审计的 `None, as ` 或 `Indirectly, through ` 语句,随后添加 `KV Cache effect` H4 和一个非空正文段落;与模型无关的通用包可以改为加入 `NO_MODEL_EXPERIENCE_SECTION`。两种情况都不要展开为对另一个包工作的描述。limitations [allowlist](../../scripts/verify-package-readme-limitations.ts) 独立管理。[Model Experience Agent Note](../../.agents/notes/implemented/process/2026-07-12-package-model-experience-contract.md) 记录了设计动机。
|
|
108
|
+
没有上下文效果或仅有消费方拥有路径的包使用 [`SENTENCE_MODEL_EXPERIENCE`](../../scripts/verify-package-readme-model-experience.ts) 中经过审计的 `None, as ` 或 `Indirectly, through ` 语句,随后添加 `KV Cache effect` H4 和一个非空正文段落;与模型无关的通用包可以改为加入 `NO_MODEL_EXPERIENCE_SECTION`。两种情况都不要展开为对另一个包工作的描述。limitations [allowlist](../../scripts/verify-package-readme-limitations.ts) 独立管理。[Model Experience Agent Note](../../.agents/notes/implemented/process/2026-07-12-package-model-experience-contract.zh.md) 记录了设计动机。
|
|
108
109
|
|
|
109
110
|
## 5. 验证
|
|
110
111
|
|
|
@@ -115,4 +116,4 @@ pnpm run constraints && pnpm run typecheck && pnpm run lint
|
|
|
115
116
|
pnpm run build && pnpm run hygiene
|
|
116
117
|
```
|
|
117
118
|
|
|
118
|
-
请遵循[仓库测试政策](../testing.md),执行新包所需的行为专项检查并达到相应覆盖率。
|
|
119
|
+
请遵循[仓库测试政策](../testing.zh.md),执行新包所需的行为专项检查并达到相应覆盖率。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
|
-
# pnpm run verify-translation-pairing --write docs/cookbook/adding-a-
|
|
5
|
-
adding-a-
|
|
6
|
-
adding-a-
|
|
4
|
+
# pnpm run verify-translation-pairing --write docs/cookbook/adding-a-remote-api.md
|
|
5
|
+
adding-a-remote-api.md: 0f89101e9bc3ff961d465c109f033150e93e04f6
|
|
6
|
+
adding-a-remote-api.zh.md: 0c5a57b539ecd98cb88790260a3fb8e7b41afff6
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Cookbook: adding a Remote API
|
|
2
|
+
|
|
3
|
+
English | [中文](adding-a-remote-api.zh.md)
|
|
4
|
+
|
|
5
|
+
Adding or changing a `ctx.remote` endpoint takes the five steps on this page: declare the method, declare its failures, register it on the package, consume it on the Client, and test it. Decorator semantics, lookup resolution, the generation pipeline, and the `/api` route are the mechanism and belong to the [API Gateway reference](../api-gateway.md); this page gives the action for each step and the conventions it must satisfy. Why the programming interface looks like this is in the [Typert Remote method calls Agent Note](../../.agents/notes/implemented/architecture/2026-08-02-typert-remote-method-calls.md), and why a failure is one `RemoteError` plus a code table is in the [failure vocabulary Agent Note](../../.agents/notes/implemented/architecture/2026-08-28-ctx-remote-failure-vocabulary.md).
|
|
6
|
+
|
|
7
|
+
## 1. Declare the API
|
|
8
|
+
|
|
9
|
+
The owner is a Host-side Cordis service: extend `TypertRemoteService` so the service key and the wire namespace are bound together, then mark the exposed methods with `@Remote`. Mark the business method itself when its signature already satisfies the wire conventions; write a `remoteExport*` adapter only when the shape has to change (adding `signal`, reordering parameters, exporting another name), and let that adapter call the unrenamed business method. Lookup objects (`Agent`, `Session`) may only occupy top-level parameter positions, and a method that supports cooperative cancellation takes `signal: AbortSignal` as its final parameter.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
13
|
+
import type { Agent } from '@deepseek-ai/dsh-agent'
|
|
14
|
+
import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
|
|
15
|
+
|
|
16
|
+
/** One stored note as a Client reads it. */
|
|
17
|
+
export interface NoteRow {
|
|
18
|
+
readonly noteId: string
|
|
19
|
+
readonly title: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declare module '@deepseek-ai/cordis' {
|
|
23
|
+
interface Context {
|
|
24
|
+
notesController: NotesController
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class NotesController extends TypertRemoteService {
|
|
29
|
+
constructor(ctx: Context) {
|
|
30
|
+
super(ctx, 'notesController', { namespace: 'notes' })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param agent - lookup parameter the Gateway resolves from its wire identity.
|
|
35
|
+
* @param signal - carrier cancellation, always the final parameter.
|
|
36
|
+
* @returns the notes this Agent's session owns.
|
|
37
|
+
*/
|
|
38
|
+
@Remote('list')
|
|
39
|
+
async remoteExportList(agent: Agent, signal: AbortSignal): Promise<NoteRow[]> {
|
|
40
|
+
return await this.list(agent, signal)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The in-process API the adapter above delegates to, unchanged by it. */
|
|
44
|
+
async list(agent: Agent, signal: AbortSignal): Promise<NoteRow[]> {
|
|
45
|
+
signal.throwIfAborted()
|
|
46
|
+
return await Promise.resolve([{ noteId: `${agent.id}-1`, title: 'draft' }])
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 2. Declare the failures
|
|
52
|
+
|
|
53
|
+
A Remote failure is one class, `RemoteError`: merge the domain codes into `RemoteErrorDetailsMap` through declaration merging and `throw new RemoteError(code, message, details)` at the failure point. Do not build a family of domain error classes, and do not write an exit-mapping function; an exception unrelated to this endpoint is not pre-classified, because the Gateway folds it into `gateway/internal`. Write a `catch` only to classify an arbitrary provider exception as one domain code, and attach the original exception as `cause`.
|
|
54
|
+
|
|
55
|
+
A code reads `<domain>/<reason>`, and its declaration has four placement rules:
|
|
56
|
+
|
|
57
|
+
- One producer only: declare it in the producing package, next to the throw.
|
|
58
|
+
- Several packages produce it: declare it in the lowest domain package both depend on (`session/not-found` in `core/session`, `workspace/not-found` in `dsh-workspace`).
|
|
59
|
+
- The carrier codes `gateway/bad-request`, `gateway/cancelled`, and `gateway/internal` are declared in protocol, and the Gateway infrastructure codes in gateway — use them, never copy them.
|
|
60
|
+
- A local failure that never crosses the wire stays out of the code table; express it with the caller's own type.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { RemoteError } from '@deepseek-ai/dsh-typert-protocol'
|
|
64
|
+
|
|
65
|
+
declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
66
|
+
interface RemoteErrorDetailsMap {
|
|
67
|
+
/** No stored note carries that id. */
|
|
68
|
+
'note/not-found': { readonly noteId: string }
|
|
69
|
+
/** The store refused an otherwise valid write. */
|
|
70
|
+
'note/rejected': { readonly noteId: string }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
declare const stored: ReadonlyMap<string, string>
|
|
75
|
+
declare function persist(noteId: string, title: string): Promise<void>
|
|
76
|
+
|
|
77
|
+
export async function rename(noteId: string, title: string): Promise<void> {
|
|
78
|
+
if (!stored.has(noteId)) {
|
|
79
|
+
throw new RemoteError('note/not-found', `no note "${noteId}"`, { noteId })
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
await persist(noteId, title)
|
|
83
|
+
} catch (error: unknown) {
|
|
84
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
85
|
+
throw new RemoteError('note/rejected', message, { noteId }, { cause: error })
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 3. Register it on the package
|
|
91
|
+
|
|
92
|
+
`@Remote` must live in a Loader entry plugin package; when the owner is an abstract seam, the controller goes in the matching package under `packages/api/`. The manifest gains the two generated entries and the protocol peer dependency, while on the Client side the `@deepseek-ai/dsh-api-remotes` assembly mounts the contribution and re-exports the type vocabulary that consumers need. Which generated artifact each entry points at, and how the generation pipeline is ordered, are in the [API Gateway reference](../api-gateway.md).
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"exports": {
|
|
97
|
+
"./typert": { "types": "./lib/typert.host.d.ts", "default": "./lib/typert.host.js" },
|
|
98
|
+
"./remote": { "types": "./lib/typert.remote-client.d.ts", "default": "./lib/typert.remote-client.js" }
|
|
99
|
+
},
|
|
100
|
+
"peerDependencies": { "@deepseek-ai/dsh-typert-protocol": "workspace:^" },
|
|
101
|
+
"devDependencies": { "@deepseek-ai/dsh-typert-protocol": "workspace:^" }
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Rerun `pnpm run build:lib` after changing a signature, the code table, the namespace, or an export name, because that is what hands the Client its new declarations and codecs; changing only an implementation body needs no regeneration.
|
|
106
|
+
|
|
107
|
+
## 4. Consume it on the Client
|
|
108
|
+
|
|
109
|
+
The calling plugin declares both `remote` and `remote.<namespace>` in its `inject`, and the call site writes `ctx.remote.<namespace>.<method>(...)` directly: no `Pick<ClientRemote, …>` narrowing, no hand-written method signature, no wire relay object. The result is a `RemoteResult<T>`, so branch on `if (!result.ok)` in place and discriminate by `code` rather than `instanceof` — a code branch narrows `details` on its own. An exception-flow site writes `throw result.error` (it is a real Error); whoever catches it uses `isRemoteFailure` to tell a Remote failure from a local defect and rethrows the defect. Do not write a defensive catch: a Remote call does not reject, and an assembly mistake should crash.
|
|
110
|
+
|
|
111
|
+
Fixed Host facts come from `ctx.remote.$host`: `home` and `isLoopback` are plain reads with no subscription and no generation counter, and `home` is `undefined` until the first ready frame. Refresh after a reconnect through `ctx.on('connection/reset')` or a domain's own remote event. When the caller aborts a unary call, the outcome is `gateway/cancelled` on the error branch rather than a throw.
|
|
112
|
+
|
|
113
|
+
```ts ignore-check
|
|
114
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
115
|
+
import { isRemoteFailure } from '@deepseek-ai/dsh-api-gateway/client'
|
|
116
|
+
import type {} from '@deepseek-ai/dsh-api-remotes/client'
|
|
117
|
+
|
|
118
|
+
export const inject = ['remote', 'remote.notes']
|
|
119
|
+
|
|
120
|
+
declare const ctx: Context
|
|
121
|
+
|
|
122
|
+
/** Store-side read: the error branch is handled where the code is meaningful. */
|
|
123
|
+
export async function noteTitles(): Promise<readonly string[]> {
|
|
124
|
+
const result = await ctx.remote.notes.list()
|
|
125
|
+
if (!result.ok) {
|
|
126
|
+
if (result.error.code === 'note/not-found') return []
|
|
127
|
+
throw result.error
|
|
128
|
+
}
|
|
129
|
+
return result.value.map(row => row.title)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Action-side: a Remote failure becomes copy; a local fault keeps crashing. */
|
|
133
|
+
export async function renderTitles(): Promise<string> {
|
|
134
|
+
try {
|
|
135
|
+
return (await noteTitles()).join(', ')
|
|
136
|
+
} catch (error: unknown) {
|
|
137
|
+
if (!isRemoteFailure(error)) throw error
|
|
138
|
+
return `unavailable (${error.code})`
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Fixed Host facts as plain reads. */
|
|
143
|
+
export function hostLabel(): string {
|
|
144
|
+
const { home, isLoopback } = ctx.remote.$host
|
|
145
|
+
return home ?? (isLoopback ? 'local host' : 'remote host')
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## 5. Test it
|
|
150
|
+
|
|
151
|
+
On the owner side, assert the code that was thrown: recover the failure with `remoteErrorOf` after catching, then compare `code` and the details fields you care about with `toMatchObject` — never deep-compare the error object with `toEqual`, and never assert `instanceof`.
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import { remoteErrorOf } from '@deepseek-ai/dsh-typert-protocol'
|
|
155
|
+
import { expect, it } from 'vitest'
|
|
156
|
+
|
|
157
|
+
declare function rename(noteId: string, title: string): Promise<void>
|
|
158
|
+
|
|
159
|
+
it('refuses an unknown note before writing', async () => {
|
|
160
|
+
const failure = await rename('n-404', 'fresh title').catch((error: unknown) => error)
|
|
161
|
+
|
|
162
|
+
expect(remoteErrorOf(failure)).toMatchObject({
|
|
163
|
+
code: 'note/not-found',
|
|
164
|
+
details: { noteId: 'n-404' },
|
|
165
|
+
})
|
|
166
|
+
})
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
A Client-side double returns real instances: take the `RemoteError` and `TestRemote` value imports from `@deepseek-ai/dsh-client-test-runtime`, because a value import from the `api-remotes` facade would load the unbuilt assembly chain. `TestRemote.$host` is a plain field a spec assigns directly.
|
|
170
|
+
|
|
171
|
+
```ts ignore-check
|
|
172
|
+
import { Context } from '@deepseek-ai/cordis'
|
|
173
|
+
import { RemoteError, TestRemote } from '@deepseek-ai/dsh-client-test-runtime'
|
|
174
|
+
import { expect, it } from 'vitest'
|
|
175
|
+
|
|
176
|
+
it('renders the failure code the Host reported', async () => {
|
|
177
|
+
const ctx = new Context()
|
|
178
|
+
const remote = new TestRemote(ctx, {
|
|
179
|
+
notes: {
|
|
180
|
+
list: () => Promise.resolve({
|
|
181
|
+
ok: false as const,
|
|
182
|
+
error: new RemoteError('note/not-found', 'no note "n-404"', { noteId: 'n-404' }),
|
|
183
|
+
}),
|
|
184
|
+
},
|
|
185
|
+
})
|
|
186
|
+
remote.$host = { home: '/home/fixture', isLoopback: true }
|
|
187
|
+
|
|
188
|
+
await expect(ctx.remote.notes.list()).resolves.toMatchObject({ error: { code: 'note/not-found' } })
|
|
189
|
+
})
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Verify
|
|
193
|
+
|
|
194
|
+
1. `pnpm run build:lib`: mandatory once a signature, the code table, the namespace, or an export name changed, because it produces the Client declarations and codecs.
|
|
195
|
+
2. `pnpm run typecheck`: both the Host and the Client program, where a code merged into an unreachable package turns red.
|
|
196
|
+
3. Run both sides' specs by name: `npx vitest run <owner spec> <client spec>`.
|
|
197
|
+
4. Add a recorded-session snapshot when the endpoint reaches a product-visible surface, per the [testing policy](../testing.md).
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# 实操手册:新增一个 Remote API
|
|
2
|
+
|
|
3
|
+
[English](adding-a-remote-api.md) | 中文
|
|
4
|
+
|
|
5
|
+
新增或改动一个 `ctx.remote` 端点按本页五步走:声明方法、声明失败、在包上注册、在 Client 消费、写测试。decorator 语义、lookup 解析、生成管线与 `/api` 路由属于机制,由 [API Gateway 参考](../api-gateway.zh.md)负责;本页给的是每一步的动作与必须遵守的约定。为什么是这套编程面,见 [Typert Remote 方法调用 Agent Note](../../.agents/notes/implemented/architecture/2026-08-02-typert-remote-method-calls.zh.md);为什么失败面是单个 `RemoteError` 加一张码表,见[失败词汇 Agent Note](../../.agents/notes/implemented/architecture/2026-08-28-ctx-remote-failure-vocabulary.zh.md)。
|
|
6
|
+
|
|
7
|
+
## 1. 声明 API
|
|
8
|
+
|
|
9
|
+
owner 是一个 Host 侧 Cordis 服务:继承 `TypertRemoteService` 把 service 键与 wire namespace 一起绑定,再用 `@Remote` 标注对外暴露的方法。业务方法的签名若已符合 wire 约定就直接标注它本身;只有形态需要调整(补 `signal`、换参数顺序、换导出名)才写一个 `remoteExport*` adapter,由它调用不改名的业务方法。lookup 对象(`Agent`、`Session`)只能占顶层参数位,支持协作式取消的方法把 `signal: AbortSignal` 放在最后一位。
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
13
|
+
import type { Agent } from '@deepseek-ai/dsh-agent'
|
|
14
|
+
import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
|
|
15
|
+
|
|
16
|
+
/** One stored note as a Client reads it. */
|
|
17
|
+
export interface NoteRow {
|
|
18
|
+
readonly noteId: string
|
|
19
|
+
readonly title: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declare module '@deepseek-ai/cordis' {
|
|
23
|
+
interface Context {
|
|
24
|
+
notesController: NotesController
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class NotesController extends TypertRemoteService {
|
|
29
|
+
constructor(ctx: Context) {
|
|
30
|
+
super(ctx, 'notesController', { namespace: 'notes' })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param agent - lookup parameter the Gateway resolves from its wire identity.
|
|
35
|
+
* @param signal - carrier cancellation, always the final parameter.
|
|
36
|
+
* @returns the notes this Agent's session owns.
|
|
37
|
+
*/
|
|
38
|
+
@Remote('list')
|
|
39
|
+
async remoteExportList(agent: Agent, signal: AbortSignal): Promise<NoteRow[]> {
|
|
40
|
+
return await this.list(agent, signal)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The in-process API the adapter above delegates to, unchanged by it. */
|
|
44
|
+
async list(agent: Agent, signal: AbortSignal): Promise<NoteRow[]> {
|
|
45
|
+
signal.throwIfAborted()
|
|
46
|
+
return await Promise.resolve([{ noteId: `${agent.id}-1`, title: 'draft' }])
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 2. 声明失败
|
|
52
|
+
|
|
53
|
+
Remote 失败只有一个类 `RemoteError`:域码经 declaration merging 进 `RemoteErrorDetailsMap`,失败点直接 `throw new RemoteError(code, message, details)`。不要建域异常类家族,也不要写出口映射函数;与本端点无关的异常不预先归类,Gateway 会兜底折成 `gateway/internal`。只有"把任意 provider 异常归为一个域码"这一种场景才写 `catch`,并把原始异常挂在 `cause` 上。
|
|
54
|
+
|
|
55
|
+
码名是 `<域>/<理由>`,声明落点四条:
|
|
56
|
+
|
|
57
|
+
- 只有一个生产者:声明落生产者包,紧挨抛出点。
|
|
58
|
+
- 多个包共同生产:落双方共同依赖的最低层域包(`session/not-found` 在 `core/session`,`workspace/not-found` 在 `dsh-workspace`)。
|
|
59
|
+
- 载体码 `gateway/bad-request`、`gateway/cancelled`、`gateway/internal` 已在 protocol 声明,Gateway 基础设施码已在 gateway 声明——直接用,不要复制。
|
|
60
|
+
- 不上 wire 的本地失败不进码表,用调用方自己的类型表达。
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { RemoteError } from '@deepseek-ai/dsh-typert-protocol'
|
|
64
|
+
|
|
65
|
+
declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
66
|
+
interface RemoteErrorDetailsMap {
|
|
67
|
+
/** No stored note carries that id. */
|
|
68
|
+
'note/not-found': { readonly noteId: string }
|
|
69
|
+
/** The store refused an otherwise valid write. */
|
|
70
|
+
'note/rejected': { readonly noteId: string }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
declare const stored: ReadonlyMap<string, string>
|
|
75
|
+
declare function persist(noteId: string, title: string): Promise<void>
|
|
76
|
+
|
|
77
|
+
export async function rename(noteId: string, title: string): Promise<void> {
|
|
78
|
+
if (!stored.has(noteId)) {
|
|
79
|
+
throw new RemoteError('note/not-found', `no note "${noteId}"`, { noteId })
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
await persist(noteId, title)
|
|
83
|
+
} catch (error: unknown) {
|
|
84
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
85
|
+
throw new RemoteError('note/rejected', message, { noteId }, { cause: error })
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 3. 在包上注册
|
|
91
|
+
|
|
92
|
+
`@Remote` 必须落在一个 Loader entry 插件包里;owner 是抽象 seam 时把控制器放进 `packages/api/` 下的对应包。包清单要补两个生成入口与 protocol 的 peer 依赖,Client 侧则由 `@deepseek-ai/dsh-api-remotes` 的 assembly 挂载该贡献并按需转口类型词汇。两个入口分别指向哪个生成产物、生成管线如何排序,见 [API Gateway 参考](../api-gateway.zh.md)。
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"exports": {
|
|
97
|
+
"./typert": { "types": "./lib/typert.host.d.ts", "default": "./lib/typert.host.js" },
|
|
98
|
+
"./remote": { "types": "./lib/typert.remote-client.d.ts", "default": "./lib/typert.remote-client.js" }
|
|
99
|
+
},
|
|
100
|
+
"peerDependencies": { "@deepseek-ai/dsh-typert-protocol": "workspace:^" },
|
|
101
|
+
"devDependencies": { "@deepseek-ai/dsh-typert-protocol": "workspace:^" }
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
改动了签名、码表、namespace 或导出名之后重跑 `pnpm run build:lib`,Client 才拿得到新的声明与 codec;只改实现体不需要重新生成。
|
|
106
|
+
|
|
107
|
+
## 4. 在 Client 消费
|
|
108
|
+
|
|
109
|
+
调用插件在 `inject` 里同时声明 `remote` 与 `remote.<namespace>`,调用点直写 `ctx.remote.<namespace>.<method>(...)`:不要用 `Pick<ClientRemote, …>` 窄化、不要手写方法签名、不要造 wire 中转对象。结果是 `RemoteResult<T>`,就地 `if (!result.ok)` 分支,判 `code` 而不是 `instanceof`——code 分支会自动窄化 `details`。异常流的站点写 `throw result.error`(它是真 Error);接住它的上层用 `isRemoteFailure` 区分 Remote 失败与本地缺陷,本地缺陷继续往上抛。不要写防御性 catch:Remote 调用不 reject,装配错误就该炸。
|
|
110
|
+
|
|
111
|
+
Host 的固定事实读 `ctx.remote.$host`:`home` 与 `isLoopback` 是普通值读取,没有订阅也没有 generation 计数器,`home` 在第一帧 ready 之前是 `undefined`;重连后的刷新走 `ctx.on('connection/reset')` 或各域自己的 remote 事件。调用方 abort 掉一次一元调用时,结果落在错误分支上的 `gateway/cancelled`,而不是抛出。
|
|
112
|
+
|
|
113
|
+
```ts ignore-check
|
|
114
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
115
|
+
import { isRemoteFailure } from '@deepseek-ai/dsh-api-gateway/client'
|
|
116
|
+
import type {} from '@deepseek-ai/dsh-api-remotes/client'
|
|
117
|
+
|
|
118
|
+
export const inject = ['remote', 'remote.notes']
|
|
119
|
+
|
|
120
|
+
declare const ctx: Context
|
|
121
|
+
|
|
122
|
+
/** Store-side read: the error branch is handled where the code is meaningful. */
|
|
123
|
+
export async function noteTitles(): Promise<readonly string[]> {
|
|
124
|
+
const result = await ctx.remote.notes.list()
|
|
125
|
+
if (!result.ok) {
|
|
126
|
+
if (result.error.code === 'note/not-found') return []
|
|
127
|
+
throw result.error
|
|
128
|
+
}
|
|
129
|
+
return result.value.map(row => row.title)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Action-side: a Remote failure becomes copy; a local fault keeps crashing. */
|
|
133
|
+
export async function renderTitles(): Promise<string> {
|
|
134
|
+
try {
|
|
135
|
+
return (await noteTitles()).join(', ')
|
|
136
|
+
} catch (error: unknown) {
|
|
137
|
+
if (!isRemoteFailure(error)) throw error
|
|
138
|
+
return `unavailable (${error.code})`
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Fixed Host facts as plain reads. */
|
|
143
|
+
export function hostLabel(): string {
|
|
144
|
+
const { home, isLoopback } = ctx.remote.$host
|
|
145
|
+
return home ?? (isLoopback ? 'local host' : 'remote host')
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## 5. 测试
|
|
150
|
+
|
|
151
|
+
owner 侧断言抛出的码:捕获后用 `remoteErrorOf` 取出失败,再用 `toMatchObject` 比对 `code` 与需要的 `details` 字段——不要用 `toEqual` 深比对错误对象,也不要断言 `instanceof`。
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import { remoteErrorOf } from '@deepseek-ai/dsh-typert-protocol'
|
|
155
|
+
import { expect, it } from 'vitest'
|
|
156
|
+
|
|
157
|
+
declare function rename(noteId: string, title: string): Promise<void>
|
|
158
|
+
|
|
159
|
+
it('refuses an unknown note before writing', async () => {
|
|
160
|
+
const failure = await rename('n-404', 'fresh title').catch((error: unknown) => error)
|
|
161
|
+
|
|
162
|
+
expect(remoteErrorOf(failure)).toMatchObject({
|
|
163
|
+
code: 'note/not-found',
|
|
164
|
+
details: { noteId: 'n-404' },
|
|
165
|
+
})
|
|
166
|
+
})
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Client 侧的替身返回真实例:`RemoteError` 与 `TestRemote` 的值 import 一律取自 `@deepseek-ai/dsh-client-test-runtime`,因为从 `api-remotes` facade 值 import 会拉起尚未构建的装配链。`TestRemote.$host` 是普通字段,spec 直接赋值即可。
|
|
170
|
+
|
|
171
|
+
```ts ignore-check
|
|
172
|
+
import { Context } from '@deepseek-ai/cordis'
|
|
173
|
+
import { RemoteError, TestRemote } from '@deepseek-ai/dsh-client-test-runtime'
|
|
174
|
+
import { expect, it } from 'vitest'
|
|
175
|
+
|
|
176
|
+
it('renders the failure code the Host reported', async () => {
|
|
177
|
+
const ctx = new Context()
|
|
178
|
+
const remote = new TestRemote(ctx, {
|
|
179
|
+
notes: {
|
|
180
|
+
list: () => Promise.resolve({
|
|
181
|
+
ok: false as const,
|
|
182
|
+
error: new RemoteError('note/not-found', 'no note "n-404"', { noteId: 'n-404' }),
|
|
183
|
+
}),
|
|
184
|
+
},
|
|
185
|
+
})
|
|
186
|
+
remote.$host = { home: '/home/fixture', isLoopback: true }
|
|
187
|
+
|
|
188
|
+
await expect(ctx.remote.notes.list()).resolves.toMatchObject({ error: { code: 'note/not-found' } })
|
|
189
|
+
})
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## 验证
|
|
193
|
+
|
|
194
|
+
1. `pnpm run build:lib`:签名、码表、namespace 或导出名变过就必须重跑,Client 声明与 codec 由它产出。
|
|
195
|
+
2. `pnpm run typecheck`:Host 与 Client 两个 program 都过一遍,码表的 merge 落点错了会在这里红。
|
|
196
|
+
3. 点名跑两侧 spec:`npx vitest run <owner spec> <client spec>`。
|
|
197
|
+
4. 端点属于产品可见面时补一条录制会话快照,规则见[测试策略](../testing.zh.md)。
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
|
2
|
+
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
|
+
# after editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write docs/cookbook/adding-a-settings-card.md
|
|
5
|
+
adding-a-settings-card.md: d1104299d319203789c9cd153f36b3f5e33edd10
|
|
6
|
+
adding-a-settings-card.zh.md: 3ef245d76331015f4d69db52bfbd176dff3e4bcf
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Cookbook: adding a settings card
|
|
2
|
+
|
|
3
|
+
English | [中文](adding-a-settings-card.zh.md)
|
|
4
|
+
|
|
5
|
+
How a plugin puts its own configuration on the web settings page. Nothing in this path needs a change inside this repository: the Host serves every registered settings namespace, and the **Plugins** section keys its cards on the namespace they edit, so a plugin that registers both halves is paired up automatically.
|
|
6
|
+
|
|
7
|
+
The two halves live in one package — the Host half under `src/`, the browser half under `src/client/`, exported as `./client` and declared with `dsh.client`. [`packages/client/ui-theme`](../../packages/client/ui-theme) is a worked example of that packaging; the cards this section ships live in [`packages/client/ui-settings-plugins`](../../packages/client/ui-settings-plugins).
|
|
8
|
+
|
|
9
|
+
## 1. Register the namespace (Host half)
|
|
10
|
+
|
|
11
|
+
The namespace is the join key, so pick it once and spell it in both halves. A consumer that already has a `cordis.yml` entry should register through `ctx.settings.installSection()`, which layers the entry under the user document and keeps working when no settings provider is mounted:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
15
|
+
import type {} from '@deepseek-ai/dsh-settings'
|
|
16
|
+
import z from '@deepseek-ai/schemastery'
|
|
17
|
+
|
|
18
|
+
declare function assertReachable(endpoint: string | undefined): void
|
|
19
|
+
declare function rebuildFromSettings(config: Config): void
|
|
20
|
+
|
|
21
|
+
export const MY_PLUGIN_NS = 'my-plugin'
|
|
22
|
+
|
|
23
|
+
export interface Config {
|
|
24
|
+
endpoint?: string
|
|
25
|
+
retries?: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const Config: z<Config> = z.object({
|
|
29
|
+
endpoint: z.string(),
|
|
30
|
+
retries: z.number().step(1).min(0).default(3),
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
export function apply(ctx: Context, config: Config) {
|
|
34
|
+
let source = () => config
|
|
35
|
+
ctx.inject(['settings'], (settingsCtx) => {
|
|
36
|
+
settingsCtx.settings.installSection(ctx, MY_PLUGIN_NS, Config, config, {
|
|
37
|
+
// Constraints the schema cannot express refuse the write, not the next use.
|
|
38
|
+
validate: value => void assertReachable(value.endpoint),
|
|
39
|
+
setSource: (current) => { source = current },
|
|
40
|
+
onChange: () => { rebuildFromSettings(source()) },
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`role('secret')` on a field keeps its value off every response; the card writes such a field into an `update`/`mutate` payload, or addresses a credential reference through the `credentials` domain instead. `applies: 'restart'` tells a configuration surface the owner acts on a change only at the next start.
|
|
47
|
+
|
|
48
|
+
## 2. Register the card (browser half)
|
|
49
|
+
|
|
50
|
+
The card registers into `settings.plugin.item` under its namespace and owns everything inside it — chrome, controls, and copy. It reads and writes through `ctx.settingsScope`, which fences each write with the revision it read:
|
|
51
|
+
|
|
52
|
+
```ts ignore-check
|
|
53
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis'
|
|
54
|
+
// Type-only: the keyed slot's declaration. Cross-plugin collaboration goes
|
|
55
|
+
// through cordis services; a value import fails the client bundle-purity gate.
|
|
56
|
+
import type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'
|
|
57
|
+
|
|
58
|
+
export const inject = ['slots', 'locale', 'connection', 'remote', 'settingsScope']
|
|
59
|
+
|
|
60
|
+
export function apply(ctx: ClientContext): void {
|
|
61
|
+
const card = new MyPluginCardController(ctx.settingsScope.bind({ namespace: 'my-plugin' }))
|
|
62
|
+
ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({
|
|
63
|
+
name: 'settings.plugin.item',
|
|
64
|
+
key: 'my-plugin',
|
|
65
|
+
locale: 'settings.myPlugin',
|
|
66
|
+
inject: () => card.inject(),
|
|
67
|
+
}, MyPluginCard),
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The scope snapshot carries what a form needs: the resolved `value`, the composition `base`, and the raw `user` layer, whose key **presence** — not its value — is what marks a field overridden. `scope.set(field, value)` stores one field and `scope.unset(field)` clears it back to the composition layer.
|
|
73
|
+
|
|
74
|
+
## 3. What the tab does with it
|
|
75
|
+
|
|
76
|
+
The **Plugin configuration** tab reads which namespaces the Host serves and dispatches one slot key per namespace. A card is rendered when the Host serves its key and skipped when it does not, so a deployment that never composed the Host half shows no trace of the card. A served namespace no card claims renders nothing — that is how the namespaces owned by other pages (`ui-theme`, `permission`, `llm-*`) stay off this tab.
|
|
77
|
+
|
|
78
|
+
Cards appear in the order they registered into the slot; a keyed entry declares no `order` of its own.
|
|
79
|
+
|
|
80
|
+
## Packaging
|
|
81
|
+
|
|
82
|
+
The browser half is served to the page by the [client module system](../../packages/client/modules), which scans the enabled Loader entries for packages declaring `dsh.client` and serves each one's built `./client` export. So the plugin appears on the page as soon as a `cordis.yml` mounts it — no rebuild of the web application.
|
|
83
|
+
|
|
84
|
+
```jsonc
|
|
85
|
+
{
|
|
86
|
+
"exports": {
|
|
87
|
+
".": { "types": "./lib/types/index.d.ts", "default": "./lib/index.js" },
|
|
88
|
+
"./client": { "types": "./lib/types/client/index.d.ts", "default": "./lib/client.js" }
|
|
89
|
+
},
|
|
90
|
+
"dsh": { "client": { "platform": "web", "inject": ["@deepseek-ai/dsh-client-ui-settings-plugins"] } }
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The bundle must be the loader's lazy-CJS factory artifact. Inside this repository `tsdown.config.ts` is three lines over the shared preset:
|
|
95
|
+
|
|
96
|
+
```ts ignore-check
|
|
97
|
+
import { clientBundle } from '../tsdown.client.ts'
|
|
98
|
+
|
|
99
|
+
export default clientBundle('@deepseek-ai/dsh-client-my-plugin', ['lib/types/index.js', 'lib/types/invariant.js'])
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
No published preset exposes this package, so a package outside this repository has to reproduce the same output format itself. The bundle-purity gate also rejects value imports across plugins, so a card cannot import this section's card chrome or its staged-form model — it renders its own, and owns its own staging and revision fencing. Both limits are recorded under [the section's known limitations](../../packages/client/ui-settings-plugins/README.md#known-limitations-and-deferred-work).
|