dsh-plugin-dev-kb 1.4.0 → 1.5.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/CHANGELOG.md +25 -0
- package/README.en.md +6 -6
- package/README.md +10 -5
- package/kb/INDEX.md +8 -4
- package/kb/extra/AGENTS.md +4 -3
- package/kb/extra/agent-lifecycle.md +91 -0
- package/kb/extra/agent-lifecycle.zh.md +93 -0
- package/kb/extra/api-gateway.md +172 -0
- package/kb/extra/api-gateway.zh.md +178 -0
- package/kb/extra/architecture.md +165 -0
- package/kb/extra/architecture.zh.md +169 -0
- package/kb/extra/capability-seams.md +714 -0
- package/kb/extra/capability-seams.zh.md +716 -0
- package/kb/extra/cli-help.md +203 -0
- package/kb/extra/cli-help.zh.md +205 -0
- package/kb/extra/config-catalog.md +4801 -0
- package/kb/extra/config-catalog.zh.md +4803 -0
- package/kb/extra/cookbook/adding-a-package.md +170 -0
- package/kb/extra/cookbook/adding-a-package.zh.md +170 -0
- package/kb/extra/cookbook/adding-a-plugin-preview.md +52 -0
- package/kb/extra/cookbook/adding-a-plugin-preview.zh.md +52 -0
- package/kb/extra/cookbook/adding-a-session-format-version.md +7 -5
- package/kb/extra/cookbook/adding-a-session-format-version.zh.md +7 -5
- package/kb/extra/cookbook/adding-a-settings-card.md +72 -0
- package/kb/extra/cookbook/adding-a-settings-card.zh.md +72 -0
- package/kb/extra/cookbook/adding-a-tool.md +101 -0
- package/kb/extra/cookbook/adding-a-tool.zh.md +103 -0
- package/kb/extra/cookbook/adding-an-llm-adapter.md +43 -0
- package/kb/extra/cookbook/adding-an-llm-adapter.zh.md +43 -0
- package/kb/extra/cookbook/extension-cookbook.md +132 -0
- package/kb/extra/cookbook/extension-cookbook.zh.md +136 -0
- package/kb/extra/cookbook/reviewing-persistence-type-changes.md +11 -9
- package/kb/extra/cookbook/reviewing-persistence-type-changes.zh.md +11 -9
- package/kb/extra/cordis-api/context.md +364 -0
- package/kb/extra/cordis-api/context.zh.md +366 -0
- package/kb/extra/cordis-api/events.md +207 -0
- package/kb/extra/cordis-api/events.zh.md +209 -0
- package/kb/extra/cordis-api/fiber.md +375 -0
- package/kb/extra/cordis-api/fiber.zh.md +377 -0
- package/kb/extra/cordis-api/inherited.md +38 -0
- package/kb/extra/cordis-api/registry.md +152 -0
- package/kb/extra/cordis-api/registry.zh.md +154 -0
- package/kb/extra/cordis-api/service.md +102 -0
- package/kb/extra/cordis-api/service.zh.md +104 -0
- package/kb/extra/cordis-tutorial/01-first-plugin.md +95 -0
- package/kb/extra/cordis-tutorial/01-first-plugin.zh.md +95 -0
- package/kb/extra/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
- package/kb/extra/cordis-tutorial/02-lifecycle-and-effects.zh.md +98 -0
- package/kb/extra/cordis-tutorial/03-services.md +98 -0
- package/kb/extra/cordis-tutorial/03-services.zh.md +98 -0
- package/kb/extra/cordis-tutorial/04-events.md +144 -0
- package/kb/extra/cordis-tutorial/04-events.zh.md +144 -0
- package/kb/extra/cordis-tutorial/05-config.md +111 -0
- package/kb/extra/cordis-tutorial/05-config.zh.md +111 -0
- package/kb/extra/cordis-tutorial/06-composition-and-hmr.md +113 -0
- package/kb/extra/cordis-tutorial/06-composition-and-hmr.zh.md +113 -0
- package/kb/extra/cordis-tutorial/07-into-the-harness.md +108 -0
- package/kb/extra/cordis-tutorial/07-into-the-harness.zh.md +108 -0
- package/kb/extra/cordis-tutorial/index.md +60 -0
- package/kb/extra/cordis-tutorial/index.zh.md +62 -0
- package/kb/extra/deepseek-llm-api-wire-extensions.md +5 -5
- package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +5 -5
- package/kb/extra/defensive-patterns/README.md +25 -0
- package/kb/extra/defensive-patterns/README.zh.md +27 -0
- package/kb/extra/defensive-patterns/client-components.md +493 -0
- package/kb/extra/defensive-patterns/client-components.zh.md +495 -0
- package/kb/extra/defensive-patterns/conversation-and-session.md +316 -0
- package/kb/extra/defensive-patterns/conversation-and-session.zh.md +316 -0
- package/kb/extra/defensive-patterns/general.md +222 -0
- package/kb/extra/defensive-patterns/general.zh.md +252 -0
- package/kb/extra/defensive-patterns/package-surface.md +231 -0
- package/kb/extra/defensive-patterns/package-surface.zh.md +231 -0
- package/kb/extra/defensive-patterns/remote-and-api.md +115 -0
- package/kb/extra/defensive-patterns/remote-and-api.zh.md +115 -0
- package/kb/extra/development.md +31 -11
- package/kb/extra/development.zh.md +32 -11
- package/kb/extra/event-producer-consumer.md +40 -39
- package/kb/extra/event-producer-consumer.zh.md +40 -39
- package/kb/extra/experimental-persistence-catalog.md +17 -0
- package/kb/extra/experimental-persistence-catalog.zh.md +17 -0
- package/kb/extra/glossary.md +1 -0
- package/kb/extra/glossary.zh.md +1 -0
- package/kb/extra/i18n/style-samples.md +2 -2
- package/kb/extra/i18n/terminology.md +1 -0
- package/kb/extra/module-graph.md +274 -195
- package/kb/extra/module-graph.zh.md +274 -195
- package/kb/extra/persistence-catalog.md +9140 -0
- package/kb/extra/persistence-catalog.zh.md +9142 -0
- package/kb/extra/persistence-changes/2026-09-13-working-directory.md +48 -0
- package/kb/extra/persistence-changes/2026-09-13-working-directory.zh.md +48 -0
- package/kb/extra/persistence-changes/2026-09-16-session-format-v4.md +2 -2
- package/kb/extra/persistence-changes/2026-09-16-session-format-v4.zh.md +2 -2
- package/kb/extra/persistence-changes/2026-09-19-external-subagent-catalog.md +48 -0
- package/kb/extra/persistence-changes/2026-09-19-external-subagent-catalog.zh.md +48 -0
- package/kb/extra/persistence-changes/2026-09-30-job-session-lifecycle.md +52 -0
- package/kb/extra/persistence-changes/2026-09-30-job-session-lifecycle.zh.md +52 -0
- package/kb/extra/persistence-changes/2026-10-05-ptc-operation-metadata.md +48 -0
- package/kb/extra/persistence-changes/2026-10-05-ptc-operation-metadata.zh.md +48 -0
- package/kb/extra/persistence-changes/2026-10-05-working-directory-attribution.md +60 -0
- package/kb/extra/persistence-changes/2026-10-05-working-directory-attribution.zh.md +60 -0
- package/kb/extra/persistence-changes/2026-10-07-title-reasoning-effort.md +48 -0
- package/kb/extra/persistence-changes/2026-10-07-title-reasoning-effort.zh.md +48 -0
- package/kb/extra/persistence-changes/2026-10-13-deepseek-search-thinking.md +48 -0
- package/kb/extra/persistence-changes/2026-10-13-deepseek-search-thinking.zh.md +48 -0
- package/kb/extra/persistence-changes/2026-10-13-user-rpc-principal.md +60 -0
- package/kb/extra/persistence-changes/2026-10-13-user-rpc-principal.zh.md +60 -0
- package/kb/extra/persistence-changes/README.md +12 -10
- package/kb/extra/persistence-changes/README.zh.md +12 -10
- package/kb/extra/persistence-changes/historical-formats/README.md +1 -1
- package/kb/extra/persistence-changes/historical-formats/README.zh.md +1 -1
- package/kb/extra/persistence-changes/releases/dsh-v0.0.1-rc.2.md +50 -50
- package/kb/extra/persistence-changes/releases/dsh-v0.0.1-rc.2.zh.md +50 -50
- package/kb/extra/persistence-changes/releases/dsh-v0.0.1-rc.3.md +14 -14
- package/kb/extra/persistence-changes/releases/dsh-v0.0.1-rc.3.zh.md +14 -14
- package/kb/extra/persistence-changes/releases/dsh-v0.1.0-rc.7.md +3 -3
- package/kb/extra/persistence-changes/releases/dsh-v0.1.0-rc.7.zh.md +3 -3
- package/kb/extra/persistence-changes/releases/dsh-v0.1.0-rc.8.md +10 -10
- package/kb/extra/persistence-changes/releases/dsh-v0.1.0-rc.8.zh.md +10 -10
- package/kb/extra/persistence-changes/releases/dsh-v0.1.1-rc.1.md +3 -3
- package/kb/extra/persistence-changes/releases/dsh-v0.1.1-rc.1.zh.md +3 -3
- package/kb/extra/persistence-changes/releases/dsh-v0.1.1-rc.2.md +13 -13
- package/kb/extra/persistence-changes/releases/dsh-v0.1.1-rc.2.zh.md +13 -13
- package/kb/extra/persistence-changes/releases/dsh-v0.1.2-alpha.1.md +63 -63
- package/kb/extra/persistence-changes/releases/dsh-v0.1.2-alpha.1.zh.md +63 -63
- package/kb/extra/persistence-changes/releases/dsh-v0.1.2-alpha.2.md +54 -54
- package/kb/extra/persistence-changes/releases/dsh-v0.1.2-alpha.2.zh.md +54 -54
- package/kb/extra/persistence-changes/releases/dsh-v0.1.2-alpha.4.md +7 -7
- package/kb/extra/persistence-changes/releases/dsh-v0.1.2-alpha.4.zh.md +7 -7
- package/kb/extra/persistence-changes/releases/dsh-v0.1.3-alpha.1.md +27 -27
- package/kb/extra/persistence-changes/releases/dsh-v0.1.3-alpha.1.zh.md +27 -27
- package/kb/extra/persistence-changes/releases/dsh-v0.1.3-alpha.2.md +4 -4
- package/kb/extra/persistence-changes/releases/dsh-v0.1.3-alpha.2.zh.md +4 -4
- package/kb/extra/persistence-changes/releases/dsh-v0.1.5-alpha.1.md +27 -27
- package/kb/extra/persistence-changes/releases/dsh-v0.1.5-alpha.1.zh.md +27 -27
- package/kb/extra/persistence-changes/releases/dsh-v0.1.5-alpha.2.md +7 -7
- package/kb/extra/persistence-changes/releases/dsh-v0.1.5-alpha.2.zh.md +7 -7
- package/kb/extra/session-format-status.md +6 -6
- package/kb/extra/session-format-status.zh.md +6 -6
- package/kb/extra/subsystems/README.md +76 -0
- package/kb/extra/subsystems/README.zh.md +76 -0
- package/kb/extra/subsystems/agent-team.md +10 -10
- package/kb/extra/subsystems/agent-team.zh.md +10 -10
- package/kb/extra/subsystems/approval.md +170 -0
- package/kb/extra/subsystems/approval.zh.md +170 -0
- package/kb/extra/subsystems/boot.md +128 -4
- package/kb/extra/subsystems/boot.zh.md +128 -4
- package/kb/extra/subsystems/claude-code-mods.md +1 -1
- package/kb/extra/subsystems/claude-code-mods.zh.md +1 -1
- package/kb/extra/subsystems/client-modules.md +198 -0
- package/kb/extra/subsystems/client-modules.zh.md +198 -0
- package/kb/extra/subsystems/client-resources.md +92 -0
- package/kb/extra/subsystems/client-resources.zh.md +92 -0
- package/kb/extra/subsystems/commands.md +223 -0
- package/kb/extra/subsystems/commands.zh.md +223 -0
- package/kb/extra/subsystems/compaction.md +284 -0
- package/kb/extra/subsystems/compaction.zh.md +284 -0
- package/kb/extra/subsystems/computer-use.md +3 -0
- package/kb/extra/subsystems/computer-use.zh.md +3 -0
- package/kb/extra/subsystems/conversation.md +301 -0
- package/kb/extra/subsystems/conversation.zh.md +303 -0
- package/kb/extra/subsystems/core.md +1113 -0
- package/kb/extra/subsystems/core.zh.md +1125 -0
- package/kb/extra/subsystems/credentials.md +493 -0
- package/kb/extra/subsystems/credentials.zh.md +493 -0
- package/kb/extra/subsystems/filesystem.md +530 -0
- package/kb/extra/subsystems/filesystem.zh.md +530 -0
- package/kb/extra/subsystems/goal.md +313 -0
- package/kb/extra/subsystems/goal.zh.md +313 -0
- package/kb/extra/subsystems/jobs.md +554 -0
- package/kb/extra/subsystems/jobs.zh.md +554 -0
- package/kb/extra/subsystems/llm-streaming.md +1196 -0
- package/kb/extra/subsystems/llm-streaming.zh.md +1202 -0
- package/kb/extra/subsystems/lsp.md +202 -0
- package/kb/extra/subsystems/lsp.zh.md +202 -0
- package/kb/extra/subsystems/permission-presets.md +167 -0
- package/kb/extra/subsystems/permission-presets.zh.md +167 -0
- package/kb/extra/subsystems/persistence.md +418 -0
- package/kb/extra/subsystems/persistence.zh.md +418 -0
- package/kb/extra/subsystems/plan.md +87 -0
- package/kb/extra/subsystems/plan.zh.md +87 -0
- package/kb/extra/subsystems/product-telemetry.md +4 -2
- package/kb/extra/subsystems/product-telemetry.zh.md +4 -2
- package/kb/extra/subsystems/ptc-runtime.md +250 -0
- package/kb/extra/subsystems/ptc-runtime.zh.md +250 -0
- package/kb/extra/subsystems/sandbox.md +221 -0
- package/kb/extra/subsystems/sandbox.zh.md +221 -0
- package/kb/extra/subsystems/schedule.md +550 -0
- package/kb/extra/subsystems/schedule.zh.md +550 -0
- package/kb/extra/subsystems/scope.md +59 -0
- package/kb/extra/subsystems/scope.zh.md +61 -0
- package/kb/extra/subsystems/session-projection.md +356 -0
- package/kb/extra/subsystems/session-projection.zh.md +356 -0
- package/kb/extra/subsystems/session-query.md +511 -0
- package/kb/extra/subsystems/session-query.zh.md +511 -0
- package/kb/extra/subsystems/session-reference.md +224 -0
- package/kb/extra/subsystems/session-reference.zh.md +224 -0
- package/kb/extra/subsystems/session-telemetry.md +212 -0
- package/kb/extra/subsystems/session-telemetry.zh.md +212 -0
- package/kb/extra/subsystems/session-title.md +213 -0
- package/kb/extra/subsystems/session-title.zh.md +213 -0
- package/kb/extra/subsystems/session.md +1339 -0
- package/kb/extra/subsystems/session.zh.md +1343 -0
- package/kb/extra/subsystems/settings.md +155 -0
- package/kb/extra/subsystems/settings.zh.md +155 -0
- package/kb/extra/subsystems/shell.md +314 -0
- package/kb/extra/subsystems/shell.zh.md +314 -0
- package/kb/extra/subsystems/sidebar-right.md +160 -0
- package/kb/extra/subsystems/sidebar-right.zh.md +160 -0
- package/kb/extra/subsystems/skills.md +352 -0
- package/kb/extra/subsystems/skills.zh.md +352 -0
- package/kb/extra/subsystems/slots.md +200 -0
- package/kb/extra/subsystems/slots.zh.md +204 -0
- package/kb/extra/subsystems/spill.md +124 -0
- package/kb/extra/subsystems/spill.zh.md +124 -0
- package/kb/extra/subsystems/ssh.md +22 -11
- package/kb/extra/subsystems/ssh.zh.md +22 -11
- package/kb/extra/subsystems/storage.md +259 -0
- package/kb/extra/subsystems/storage.zh.md +259 -0
- package/kb/extra/subsystems/subagent.md +686 -0
- package/kb/extra/subsystems/subagent.zh.md +688 -0
- package/kb/extra/subsystems/subprocess.md +321 -0
- package/kb/extra/subsystems/subprocess.zh.md +321 -0
- package/kb/extra/subsystems/system-prompt.md +254 -0
- package/kb/extra/subsystems/system-prompt.zh.md +254 -0
- package/kb/extra/subsystems/terminal.md +184 -0
- package/kb/extra/subsystems/terminal.zh.md +184 -0
- package/kb/extra/subsystems/token-meter.md +106 -0
- package/kb/extra/subsystems/token-meter.zh.md +106 -0
- package/kb/extra/subsystems/tools.md +747 -0
- package/kb/extra/subsystems/tools.zh.md +747 -0
- package/kb/extra/subsystems/translation.md +103 -0
- package/kb/extra/subsystems/translation.zh.md +103 -0
- package/kb/extra/subsystems/typert.md +690 -0
- package/kb/extra/subsystems/typert.zh.md +690 -0
- package/kb/extra/subsystems/user-questions.md +307 -0
- package/kb/extra/subsystems/user-questions.zh.md +307 -0
- package/kb/extra/subsystems/web-client.md +100 -0
- package/kb/extra/subsystems/web-client.zh.md +100 -0
- package/kb/extra/subsystems/web-server.md +302 -0
- package/kb/extra/subsystems/web-server.zh.md +302 -0
- package/kb/extra/subsystems/web.md +206 -0
- package/kb/extra/subsystems/web.zh.md +206 -0
- package/kb/extra/subsystems/webhook.md +1 -1
- package/kb/extra/subsystems/webhook.zh.md +1 -1
- package/kb/extra/subsystems/workflow.md +278 -0
- package/kb/extra/subsystems/workflow.zh.md +278 -0
- package/kb/extra/subsystems/working-directory.md +64 -0
- package/kb/extra/subsystems/working-directory.zh.md +64 -0
- package/kb/extra/subsystems/workspace.md +647 -0
- package/kb/extra/subsystems/workspace.zh.md +647 -0
- package/kb/extra/subsystems/worktrees.md +76 -0
- package/kb/extra/subsystems/worktrees.zh.md +76 -0
- package/kb/extra/testing.md +1 -1
- package/kb/extra/testing.zh.md +1 -1
- package/kb/extra/tool-catalog.md +2824 -0
- package/kb/extra/tool-catalog.zh.md +2832 -0
- package/kb/extra/tool-execution-pipeline.md +65 -0
- package/kb/extra/tool-execution-pipeline.zh.md +67 -0
- package/kb/extra/ui-radius.md +6 -4
- package/kb/extra/ui-radius.zh.md +6 -4
- package/kb/extra/upgrade-guide/v0.2.0-rc.2/bundled-python-tests/guide.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.0-rc.2/bundled-python-tests/guide.zh.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.0-rc.2/primary-runtime-python-aliases/guide.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.0-rc.2/primary-runtime-python-aliases/guide.zh.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/deepseek-model-discovery/guide.md +17 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/deepseek-model-discovery/guide.zh.md +17 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/desktop-test-auth-popup/guide.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/desktop-test-auth-popup/guide.zh.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/experimental-package-composition/guide.md +28 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/experimental-package-composition/guide.zh.md +28 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/instruction-home-override/guide.md +20 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/instruction-home-override/guide.zh.md +20 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/job-id-identity/guide.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/job-id-identity/guide.zh.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/jsonl-persistence-plugin/guide.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/jsonl-persistence-plugin/guide.zh.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/mock-messages-events/guide.md +17 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/mock-messages-events/guide.zh.md +17 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/native-subagent-bundle-tools/guide.md +31 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/native-subagent-bundle-tools/guide.zh.md +31 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/new-session-generations/guide.md +20 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/new-session-generations/guide.zh.md +20 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/plugin-option-placement/guide.md +20 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/plugin-option-placement/guide.zh.md +20 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/python-optional-resources/guide.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/python-optional-resources/guide.zh.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/python-ptc-sandbox/guide.md +21 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/python-ptc-sandbox/guide.zh.md +21 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/session-title-adapters/guide.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/session-title-adapters/guide.zh.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/session-title-provider-strategy/guide.md +20 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/session-title-provider-strategy/guide.zh.md +20 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/ssh-helper-launch/guide.md +19 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/ssh-helper-launch/guide.zh.md +19 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/subagent-activations/guide.md +59 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/subagent-activations/guide.zh.md +59 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/subagent-preset-composition/guide.md +21 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/subagent-preset-composition/guide.zh.md +21 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/team-direct-inbox/guide.md +21 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/team-direct-inbox/guide.zh.md +21 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/terminal-command-labels/guide.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/terminal-command-labels/guide.zh.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/tool-presentation-mode/guide.md +21 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/tool-presentation-mode/guide.zh.md +21 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/web-listener-trust-config/guide.md +36 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/web-listener-trust-config/guide.zh.md +36 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/working-directory/guide.md +25 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.1/working-directory/guide.zh.md +25 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/deepseek-account-identity-watch/guide.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/deepseek-account-identity-watch/guide.zh.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/deepseek-account-model-catalog/guide.md +27 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/deepseek-account-model-catalog/guide.zh.md +27 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/desktop-font-size-shortcuts/guide.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/desktop-font-size-shortcuts/guide.zh.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/from-default-profile-replaces-target/guide.md +20 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/from-default-profile-replaces-target/guide.zh.md +20 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/operator-connect/guide.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/operator-connect/guide.zh.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/pi-ai-azure-provider/guide.md +19 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/pi-ai-azure-provider/guide.zh.md +19 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/pi-ai-model-discovery/guide.md +17 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/pi-ai-model-discovery/guide.zh.md +17 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/plugin-install-over-installed/guide.md +20 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/plugin-install-over-installed/guide.zh.md +20 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/sdk-minimal-image-compaction/guide.md +32 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/sdk-minimal-image-compaction/guide.zh.md +32 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/stored-credentials-precedence/guide.md +25 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.2/stored-credentials-precedence/guide.zh.md +25 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.3/plugin-engines-dsh/guide.md +21 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.3/plugin-engines-dsh/guide.zh.md +21 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.4/account-notification-fields/guide.md +18 -0
- package/kb/extra/upgrade-guide/v0.2.1-alpha.4/account-notification-fields/guide.zh.md +18 -0
- package/kb/meta/search-index.json +1932 -366
- package/kb/meta/source.json +9 -8
- package/kb/site/develop/basic/publish.md +3 -1
- package/kb/site/en/develop/basic/publish.md +3 -1
- package/kb/site/en/guide/github-review.md +7 -10
- package/kb/site/en/guide/plugin-market.md +151 -0
- package/kb/site/en/guide/providers.md +27 -4
- package/kb/site/en/guide/public-deployments.md +10 -6
- package/kb/site/en/guide/python-sdk.md +8 -9
- package/kb/site/en/reference/api-gateway.md +10 -3
- package/kb/site/en/reference/capability-seams.md +71 -24
- package/kb/site/en/reference/config-catalog.md +452 -191
- package/kb/site/en/reference/experimental-persistence-catalog.md +20 -0
- package/kb/site/en/reference/index.md +11 -12
- package/kb/site/en/reference/persistence-catalog.md +552 -192
- package/kb/site/en/reference/subsystems/client-modules.md +14 -0
- package/kb/site/en/reference/subsystems/conversation.md +14 -0
- package/kb/site/en/reference/subsystems/core.md +3 -1
- package/kb/site/en/reference/subsystems/credentials.md +13 -12
- package/kb/site/en/reference/subsystems/index.md +4 -1
- package/kb/site/en/reference/subsystems/jobs.md +62 -7
- package/kb/site/en/reference/subsystems/llm-streaming.md +82 -15
- package/kb/site/en/reference/subsystems/persistence.md +2 -0
- package/kb/site/en/reference/subsystems/plan.md +1 -1
- package/kb/site/en/reference/subsystems/ptc-runtime.md +24 -6
- package/kb/site/en/reference/subsystems/session-query.md +2 -0
- package/kb/site/en/reference/subsystems/session-title.md +14 -5
- package/kb/site/en/reference/subsystems/session.md +59 -14
- package/kb/site/en/reference/subsystems/sidebar-right.md +2 -0
- package/kb/site/en/reference/subsystems/skills.md +2 -2
- package/kb/site/en/reference/subsystems/slots.md +80 -83
- package/kb/site/en/reference/subsystems/subagent.md +175 -247
- package/kb/site/en/reference/subsystems/system-prompt.md +32 -2
- package/kb/site/en/reference/subsystems/tools.md +3 -3
- package/kb/site/en/reference/subsystems/typert.md +222 -17
- package/kb/site/en/reference/subsystems/web-client.md +1 -1
- package/kb/site/en/reference/subsystems/web-server.md +87 -11
- package/kb/site/en/reference/subsystems/web.md +2 -2
- package/kb/site/en/reference/subsystems/workflow.md +1 -1
- package/kb/site/en/reference/subsystems/workspace.md +25 -37
- package/kb/site/en/reference/tool-catalog.md +156 -51
- package/kb/site/guide/github-review.md +7 -10
- package/kb/site/guide/plugin-market.md +151 -0
- package/kb/site/guide/providers.md +27 -4
- package/kb/site/guide/public-deployments.md +10 -6
- package/kb/site/guide/python-sdk.md +8 -9
- package/kb/site/reference/api-gateway.md +16 -3
- package/kb/site/reference/capability-seams.md +71 -24
- package/kb/site/reference/config-catalog.md +452 -191
- package/kb/site/reference/experimental-persistence-catalog.md +20 -0
- package/kb/site/reference/index.md +11 -12
- package/kb/site/reference/persistence-catalog.md +552 -192
- package/kb/site/reference/subsystems/client-modules.md +14 -0
- package/kb/site/reference/subsystems/conversation.md +16 -0
- package/kb/site/reference/subsystems/core.md +5 -1
- package/kb/site/reference/subsystems/credentials.md +13 -12
- package/kb/site/reference/subsystems/index.md +4 -1
- package/kb/site/reference/subsystems/jobs.md +62 -7
- package/kb/site/reference/subsystems/llm-streaming.md +82 -15
- package/kb/site/reference/subsystems/persistence.md +2 -0
- package/kb/site/reference/subsystems/plan.md +1 -1
- package/kb/site/reference/subsystems/ptc-runtime.md +24 -6
- package/kb/site/reference/subsystems/scope.md +2 -0
- package/kb/site/reference/subsystems/session-query.md +2 -0
- package/kb/site/reference/subsystems/session-title.md +14 -5
- package/kb/site/reference/subsystems/session.md +59 -14
- package/kb/site/reference/subsystems/sidebar-right.md +2 -0
- package/kb/site/reference/subsystems/skills.md +2 -2
- package/kb/site/reference/subsystems/slots.md +84 -83
- package/kb/site/reference/subsystems/subagent.md +177 -251
- package/kb/site/reference/subsystems/system-prompt.md +32 -2
- package/kb/site/reference/subsystems/tools.md +3 -3
- package/kb/site/reference/subsystems/typert.md +222 -17
- package/kb/site/reference/subsystems/web-client.md +1 -1
- package/kb/site/reference/subsystems/web-server.md +87 -11
- package/kb/site/reference/subsystems/web.md +2 -2
- package/kb/site/reference/subsystems/workflow.md +1 -1
- package/kb/site/reference/subsystems/workspace.md +25 -37
- package/kb/site/reference/tool-catalog.md +160 -55
- package/package.json +2 -2
- package/skills/dsh-dev-sandbox.md +70 -0
- package/kb/extra/defensive-patterns.md +0 -33
- package/kb/extra/defensive-patterns.zh.md +0 -35
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# Conversation assembly
|
|
2
|
+
|
|
3
|
+
English | [中文](conversation.zh.md)
|
|
4
|
+
|
|
5
|
+
Conversation is the target-neutral assembly layer between a Client `SessionEventLikeEntry` window and browser views. [`ui-conversation`](../../packages/client/ui-conversation/README.md) owns the event and view registries, one identity-stable binding per `SessionBinding`, Turn/Step locations, incremental Context assembly, target sources, the shared shell, and input orchestration. Target packages such as [`ui-chat`](../../packages/client/ui-chat/README.md) and [`ui-trajectory`](../../packages/client/ui-trajectory/README.md) own their Definitions, final snapshots, and rendering.
|
|
6
|
+
|
|
7
|
+
This page defines the data model and the extension path for a business-owned Conversation node. The [Web Client architecture](web-client.md) places the subsystem between Client models and Slots; the [Conversation Node assembly decision](../../.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.md) owns its rationale.
|
|
8
|
+
|
|
9
|
+
## Data model and ownership
|
|
10
|
+
|
|
11
|
+
The Session Controller owns the contiguous loaded logical-event window. Each `SessionEventLikeEntry` is either `{ type: 'event', event: SessionEvent }` for one durable event or `{ type: 'transient', event: AssistantLiveChunkEvent }` for one Client-only `assistant/live-chunk` presentation. Both inner events expose `type`, `seq`, `time`, and `data`. `ui-conversation` passes these entries to the assembler without opening a second history stream. One `ConversationNodeAssembler` per Session applies every registered Definition and publishes an independent source for each registered view target.
|
|
12
|
+
|
|
13
|
+
| Concept | Owner and purpose |
|
|
14
|
+
|---|---|
|
|
15
|
+
| Event Definition | A business package matches one durable or Client-only transient event at a time, correlates it by stable `(kind, id)`, folds deterministic State, and optionally materializes one target node. |
|
|
16
|
+
| Context | The engine-owned ordered Matches and current State for one `(kind, id)`. Durable and transient events may be starts. The earliest loaded start initializes State; later Matches update it. Update-only evidence stays pending until its start is loaded. |
|
|
17
|
+
| Location | The engine-owned Session, Turn, or Step coordinates derived from durable boundary events. Definitions may publish typed data onto one Turn or Step. |
|
|
18
|
+
| View Definition | A target package creates one incremental builder per Session and owns the final snapshot type for that target. |
|
|
19
|
+
| Group Definition | A business package derives one target's root references and group snapshots from materialized Nodes; it owns membership, segmentation, summaries, and incremental caches. |
|
|
20
|
+
| View | A Slot entry such as Chat or Trajectory reads only its target snapshot and renders target-owned nodes. |
|
|
21
|
+
|
|
22
|
+
Chat and Trajectory may recognize the same durable event family, but each keeps its own Definition State and final node payload. Shared target-neutral machinery is limited to identity routing, ordered replay, Location data, predecessor dependencies, and publication cadence.
|
|
23
|
+
|
|
24
|
+
## Target activation
|
|
25
|
+
|
|
26
|
+
Each Session keeps a monotonic set of active targets. Creating or reading a target source does not activate it. The shell explicitly activates its persisted or newly selected View, while another consumer activates a target through its first source subscription. First activation creates that target's builder and calls `replace()` once from the current target-indexed Contexts. Later flushes call `apply()` for every active target, and unsubscription does not remove one.
|
|
27
|
+
|
|
28
|
+
The shell owns View selection and resolves the registered preferred View or Chat fallback before rendering when a binding is created or selected as current, and after View-roster changes. The assembler receives only the resolved target id and does not select Chat or another default target. A third-party View participates through the same selection and activation operations. A View Definition may expose `toolCallFocus(callId)`; the shell supplies Inspect only for a visible target that declares this capability, and the target maps the call id to its own focus identity.
|
|
29
|
+
|
|
30
|
+
<a id="group-definitions"></a>
|
|
31
|
+
## Group Definitions
|
|
32
|
+
|
|
33
|
+
The optional `ctx.uiConversation.groups.register(definition)` contribution uses the same effect-owned registry lifecycle as event and view registrations. It requires an existing View target and rejects duplicate target registrations without constructing a Builder. Each Session creates its own Group context only when that target activates. Registration replacement discards the old context and clears its observed groups during the existing registry rebuild.
|
|
34
|
+
|
|
35
|
+
The [group types](../../packages/client/ui-conversation/src/client/contract/groups.ts) define the complete protocol:
|
|
36
|
+
|
|
37
|
+
| Type | Meaning |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `ConversationGroupDefinition<Node, State, Data>` | `create()` initializes Session-local State; `update(context, input)` returns its next State; `buildGroups(context)` returns pending output or `null`. Replacement input requires entries and a complete group replacement. |
|
|
40
|
+
| `ConversationGroupInput<Node>` | `replace` supplies target order, timeline, and synchronous `readNode`, `readTurn`, and `readPosition` readers. `apply` adds projected `previous/current` Node changes, lifecycle `changedTurns`, and `changedTurnOrders`. Do not retain readers in State. |
|
|
41
|
+
| `GroupNodePosition` | Owning Turn, when present, and immediate previous/next visible Node keys. Neighbours preserve interruptions that a Turn-only key list omits. |
|
|
42
|
+
| `NodeReference` / `GroupReference` | Branded `NodeKey` or `GroupKey`, distinguished by `kind`. A Node reference may select a renderer-owned `groupPart`; omission selects the whole Node. |
|
|
43
|
+
| `GroupSnapshot<Data>` | Immutable group key, business data, and ordered Node references. Groups cannot contain other groups or own source Node data. |
|
|
44
|
+
| `GroupUpdate<Data>` | `entries` replaces the complete root sequence; only apply input may omit it to preserve the sequence. Replacement input requires both `entries` and `groups.replace`. `groups.replace.snapshots` replaces all groups; `groups.apply.upserts/removes` updates only named group records. Removal does not delete Nodes. |
|
|
45
|
+
| `ConversationGroupDataMap` | Declaration-merged target-to-data association shared by registration and `views.grouped(target)`. Undeclared targets have no group payload type. |
|
|
46
|
+
| `ConversationGroupedView<Data>` | Stable root `entries` and keyed `groupSource(key)` readers; a removed group reads as `undefined`. |
|
|
47
|
+
|
|
48
|
+
The Builder exposes `groupInput()` when grouping is registered; missing input fails at first activation. It records the Node values after its own projections and preserves content-only order identity. The assembler supplies changed Turns with every update; direct ungrouped Builder callers may omit them. Builders with independent sources defer their notifications to `publish()`. First activation and ordinary flush share the same sequence: materialize Location data and Nodes, update the Builder, call the Group Definition, validate and install grouping, then publish every affected target and Location source. The group phase reuses the existing publication cadence.
|
|
49
|
+
|
|
50
|
+
Target position indexes supply the readers and identify Turns whose visible keys or neighbours changed. `changedTurnOrders` includes both owners of a moved Node and Turns adjacent to inserted or removed unscoped Nodes; lifecycle-only changes remain in `changedTurns`. A business Definition can resegment those Turns and refresh only groups containing changed content. Structural output still replaces the complete root-reference array, without requiring unchanged Node contents to be reread.
|
|
51
|
+
|
|
52
|
+
The Group store validates the whole submitted result before installation: root Group references and records correspond one-to-one, all referenced Nodes exist, and each `(NodeKey, groupPart)` occupies at most one root or member position. A whole Node cannot coexist with one of its parts. Duplicate upserts, duplicate removals, and simultaneous upsert/removal of a group fail. Data-only upserts retain root and member arrays, do not read Nodes, and notify only changed group sources; full replacement revalidates every reference.
|
|
53
|
+
|
|
54
|
+
Renderers switch on the two reference kinds and choose components in the owning View, not through a renderer field in group data. Root Node references and referenced group members form the complete render list; unreferenced target Nodes remain stored but do not render. The group body reads members separately from summary data. Presentation modes must retain component types, keys, and member parents; changing data membership may legitimately remount a moved member. The framework does not interpret part completeness, segmentation, or presentation policies.
|
|
55
|
+
|
|
56
|
+
## Replayable event families
|
|
57
|
+
|
|
58
|
+
Choose one stable business id before writing the Definition. Every event that contributes to the same Node must carry that id or derive it independently from its own payload; the client must never assign an update to “the latest unfinished” Context.
|
|
59
|
+
|
|
60
|
+
For a review job, the event contract could be:
|
|
61
|
+
|
|
62
|
+
| Event | Role | Required durable facts |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `review/start` | unique start | `reviewId`, Turn/Step coordinates, title |
|
|
65
|
+
| `review/progress` | update | the same `reviewId`, coordinates, replayable progress |
|
|
66
|
+
| `review/end` | update | the same `reviewId`, coordinates, final summary |
|
|
67
|
+
|
|
68
|
+
Use the producer-owned branded id type across the process boundary. Put the `SessionEventMap` merge and payload types on the producer's type-only export, then import that export for side effects from the client package. Each `(kind, id)` may have at most one start event. A single-event business can use the event's stable identity, such as `event.seq`, as its Definition-local id.
|
|
69
|
+
|
|
70
|
+
Incremental events are supported. Prefer whole-value checkpoints when the producer can emit them cheaply, because they remain useful when the start is outside the loaded window. Each delta must carry the stable id and produce deterministic State when replayed in ascending log `seq`; it must not depend on live-only memory. If the current history window contains only updates, the assembler keeps a pending Context and builds no State until an older page supplies the start. If the product must render before the start is loaded, a terminal or checkpoint event must carry enough whole fallback state for the Definition to build that result directly; do not recover it by scanning unrelated events.
|
|
71
|
+
|
|
72
|
+
Live Assistant deltas arrive as Client-only `assistant/live-chunk` events. Reconnect baselines expand the active process-local compact stream into the same transient events, while durable `assistant/message` and `assistant/attempt` events embed complete compact streams for history replay. A transient event may initialize a Context. Every tool delta and its later `tool/call` can match as start under the same callId, even before the name arrives; only the earliest calls `start()`, and later Matches call `update()`. A preparing Tool node becomes visible only after its name is available. Historical pages do not expand settled messages into live deltas. A Definition that consumes Assistant output handles live chunks and durable settlements through the same matching and update interfaces, while unrelated Definitions return `null` without expanding a stream.
|
|
73
|
+
|
|
74
|
+
## Definition and typed Chat payload
|
|
75
|
+
|
|
76
|
+
The example keeps the producer declarations and client contribution in one block so the complete relationship is visible. In a package family, keep the branded id and `SessionEventMap` declaration with the event producer, and keep the Definition, Chat data merge, and renderer in the client plugin.
|
|
77
|
+
|
|
78
|
+
```ts ignore-check
|
|
79
|
+
import { createElement } from 'react'
|
|
80
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis'
|
|
81
|
+
import type { Branded } from '@deepseek-ai/dsh-brand'
|
|
82
|
+
import type {
|
|
83
|
+
ConversationLocation, ConversationNodeContext,
|
|
84
|
+
ConversationNodeDefinition,
|
|
85
|
+
} from '@deepseek-ai/dsh-client-ui-conversation/client'
|
|
86
|
+
import type { ChatNodeViewProps } from '@deepseek-ai/dsh-client-ui-chat/client'
|
|
87
|
+
|
|
88
|
+
type ReviewId = Branded<'ReviewId'>
|
|
89
|
+
|
|
90
|
+
interface ReviewStartData {
|
|
91
|
+
readonly reviewId: ReviewId
|
|
92
|
+
readonly turn: number
|
|
93
|
+
readonly step: number
|
|
94
|
+
readonly title: string
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface ReviewProgressData {
|
|
98
|
+
readonly reviewId: ReviewId
|
|
99
|
+
readonly turn: number
|
|
100
|
+
readonly step: number
|
|
101
|
+
readonly completed: number
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
interface ReviewEndData {
|
|
105
|
+
readonly reviewId: ReviewId
|
|
106
|
+
readonly turn: number
|
|
107
|
+
readonly step: number
|
|
108
|
+
readonly summary: string
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
declare module '@deepseek-ai/dsh-session/types' {
|
|
112
|
+
interface SessionEventMap {
|
|
113
|
+
/**
|
|
114
|
+
* Opens one durable review job.
|
|
115
|
+
* @mode emit
|
|
116
|
+
* @param data - stable identity, location, and initial display state.
|
|
117
|
+
*/
|
|
118
|
+
'review/start': ReviewStartData
|
|
119
|
+
/**
|
|
120
|
+
* Records replayable progress for one review job.
|
|
121
|
+
* @mode emit
|
|
122
|
+
* @param data - stable identity, location, and latest progress.
|
|
123
|
+
*/
|
|
124
|
+
'review/progress': ReviewProgressData
|
|
125
|
+
/**
|
|
126
|
+
* Closes one review job with its final summary.
|
|
127
|
+
* @mode emit
|
|
128
|
+
* @param data - stable identity, location, and final display state.
|
|
129
|
+
*/
|
|
130
|
+
'review/end': ReviewEndData
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
interface ReviewChatData {
|
|
135
|
+
readonly title: string
|
|
136
|
+
readonly completed: number
|
|
137
|
+
readonly status: 'running' | 'completed'
|
|
138
|
+
readonly summary?: string
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
declare module '@deepseek-ai/dsh-client-ui-chat/client' {
|
|
142
|
+
interface ChatNodeDataMap {
|
|
143
|
+
'review-job': ReviewChatData
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
declare module '@deepseek-ai/dsh-client-ui-conversation/client' {
|
|
148
|
+
interface ConversationStepDataMap {
|
|
149
|
+
'review-job': ReviewChatData
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
interface ReviewState extends ReviewChatData {
|
|
154
|
+
readonly turn: number
|
|
155
|
+
readonly step: number
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function locationOf(context: ConversationNodeContext): ConversationLocation {
|
|
159
|
+
return context.start?.location ?? context.matches[0]?.location ?? { kind: 'unresolved' }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function viewData(state: ReviewState): ReviewChatData {
|
|
163
|
+
return {
|
|
164
|
+
title: state.title,
|
|
165
|
+
completed: state.completed,
|
|
166
|
+
status: state.status,
|
|
167
|
+
...state.summary === undefined ? {} : { summary: state.summary },
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const reviewDefinition: ConversationNodeDefinition<ReviewState> = {
|
|
172
|
+
kind: 'review-job',
|
|
173
|
+
target: 'chat',
|
|
174
|
+
match: (event) => {
|
|
175
|
+
if (event.type === 'review/start') {
|
|
176
|
+
return { id: String(event.data.reviewId), role: 'start' }
|
|
177
|
+
}
|
|
178
|
+
if (event.type === 'review/progress' || event.type === 'review/end') {
|
|
179
|
+
return { id: String(event.data.reviewId), role: 'update' }
|
|
180
|
+
}
|
|
181
|
+
return null
|
|
182
|
+
},
|
|
183
|
+
start: (_context, match) => {
|
|
184
|
+
if (match.event.type !== 'review/start') throw new Error('review-job requires review/start')
|
|
185
|
+
return {
|
|
186
|
+
turn: match.event.data.turn,
|
|
187
|
+
step: match.event.data.step,
|
|
188
|
+
title: match.event.data.title,
|
|
189
|
+
completed: 0,
|
|
190
|
+
status: 'running',
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
update: (context, match) => {
|
|
194
|
+
if (match.event.type === 'review/progress') {
|
|
195
|
+
return { ...context.state, completed: match.event.data.completed }
|
|
196
|
+
}
|
|
197
|
+
if (match.event.type === 'review/end') {
|
|
198
|
+
return { ...context.state, completed: 100, status: 'completed', summary: match.event.data.summary }
|
|
199
|
+
}
|
|
200
|
+
return context.state
|
|
201
|
+
},
|
|
202
|
+
publication: match => match.event.type === 'review/progress'
|
|
203
|
+
? 'animation-frame'
|
|
204
|
+
: 'immediate',
|
|
205
|
+
buildLocationData: (context, scope) => {
|
|
206
|
+
if (scope !== 'step' || context.state === undefined) return null
|
|
207
|
+
return {
|
|
208
|
+
kind: 'step',
|
|
209
|
+
turn: context.state.turn,
|
|
210
|
+
step: context.state.step,
|
|
211
|
+
key: 'review-job',
|
|
212
|
+
value: viewData(context.state),
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
buildViewNode: (context) => {
|
|
216
|
+
if (context.state === undefined) return null
|
|
217
|
+
return {
|
|
218
|
+
key: context.key,
|
|
219
|
+
kind: 'review-job',
|
|
220
|
+
id: context.id,
|
|
221
|
+
target: 'chat',
|
|
222
|
+
anchorSeq: context.start?.event.seq ?? context.matches[0]?.event.seq ?? 0,
|
|
223
|
+
location: locationOf(context),
|
|
224
|
+
visibility: 'visible',
|
|
225
|
+
data: viewData(context.state),
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function ReviewNodeView({ node }: ChatNodeViewProps<'review-job'>) {
|
|
231
|
+
const text = node.data.summary ?? `${node.data.title}: ${node.data.completed}%`
|
|
232
|
+
return createElement('p', null, text)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export const inject = ['uiConversation', 'slots']
|
|
236
|
+
|
|
237
|
+
export function apply(ctx: ClientContext): void {
|
|
238
|
+
ctx.uiConversation.events.register(reviewDefinition)
|
|
239
|
+
ctx.slots.inject('conversation.chat.node', () => ctx.slots.register({
|
|
240
|
+
name: 'conversation.chat.node',
|
|
241
|
+
key: 'review-job',
|
|
242
|
+
}, ReviewNodeView))
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
`match(event)` is an identity extractor, not a fold: it receives only the current `SessionEventLike` and returns the Definition-local id and lifecycle role. After a match, the assembler locates the Context by `(kind, id)` and initializes from its earliest loaded start, whether durable or transient. Every later Match calls `update`, including another start. Removing transient Matches reselects the start from remaining evidence and recomputes State; no remaining start leaves State undefined. Both functions return the State that the engine adopts; returning a new immutable value is preferred, but a function that mutates and returns the same object has the same adoption semantics.
|
|
247
|
+
|
|
248
|
+
`ConversationNodeDefinitionInput` accepts either this function or a read-only table from event type to matching function. Function-form registrations receive every event; table registrations receive only their listed types. The Registry copies table entries and precomputes ordered candidate Sets when registrations change, keeping candidate construction out of per-event dispatch. Replace a table by unregistering and registering again. Resolved `ConversationNodeDefinition` values retain a callable `match(event)` in both cases.
|
|
249
|
+
|
|
250
|
+
A match result with `exclusive: true` claims that event on the Definition's target. The assembler evaluates every match of the event before accepting any, so the outcome is independent of registration order: other Definitions of the claimed target build no Context for the event, and the target's unmatched-event fallback does not run. Definitions of other targets and Definitions without a target still receive it. Two claims on one target, or a claim from a Definition without a target, throw during dispatch; a claim returned by the fallback itself has no effect. A business Definition uses a claim to present one kind of message that a generic Definition of the same target would otherwise also present, such as a late question reply.
|
|
251
|
+
|
|
252
|
+
`buildLocationData(context, scope)` optionally publishes Definition-owned data onto an engine-owned Turn or Step. Use declaration merging to give each key a precise value type. Another Node in the same Location can consume that value through its constrained slot hook, such as `useTurnData(key)`, without receiving the Session or scanning `snapshot.chat.nodes`.
|
|
253
|
+
|
|
254
|
+
`target` and `buildViewNode(context)` declare one target-owned rendering contribution and must appear together. Preserve `context.key` as the React-facing identity, choose `anchorSeq` from the matched event ordering, and return only renderer-ready data. Once a target Node has been published, keep returning the same key; use `visibility: 'hidden'` when it must temporarily leave the visible flow rather than withdrawing it with `null`.
|
|
255
|
+
|
|
256
|
+
A Chat Node also chooses where its row stands through `groupTree`. Positions are ordered from the outermost inward, and a Node at a position also has the effect of every inner one. Chat process grouping, whole-Turn folding, and Turn-input detection read only this position; [group-tree.ts](../../packages/client/ui-chat/src/client/contract/group-tree.ts) holds the complete contract.
|
|
257
|
+
|
|
258
|
+
| The Node is | Declare |
|
|
259
|
+
|---|---|
|
|
260
|
+
| A person's input claimed in Step 1; its Turn opens an exchange | `exchange` |
|
|
261
|
+
| Another input of its Turn, such as later human input or a trigger notice; before the process it is the opening input, after it the whole Turn stays open | `input` |
|
|
262
|
+
| A Turn notice that stays outside whole-Turn folding, such as an error or the completed-turn footer | `turn` |
|
|
263
|
+
| An independent row that whole-Turn folding still hides, such as a model retry | `process` |
|
|
264
|
+
| Ordinary process content that joins the surrounding step run | omit `groupTree` |
|
|
265
|
+
|
|
266
|
+
The [Chat business rules](../../packages/client/ui-chat/src/client/conversation-nodes/README.md#process-grouping) list the positions the built-in Nodes declare. When a generic Definition of the same target also matches the event your Node presents, claim the event with `exclusive: true` as described above.
|
|
267
|
+
|
|
268
|
+
## Predecessor reads
|
|
269
|
+
|
|
270
|
+
Some Definitions need the latest earlier State of another business kind. `start` receives a `ConversationContextReader`; call `reader.previous<State>(kind)` there instead of accepting a Context collection or scanning events. The reader returns the nearest started Context before the current start `seq` as read-only data.
|
|
271
|
+
|
|
272
|
+
The assembler records that dependency. If an older prepend later supplies a nearer predecessor, closes a previously unknown window gap, or revises the predecessor State, it reruns the dependent Context from `start` and replays its updates in ascending `seq`. The queried Definition remains responsible for writing useful State; the reader exposes no business-specific query methods and grants no mutation authority over another Context.
|
|
273
|
+
|
|
274
|
+
## Window update paths
|
|
275
|
+
|
|
276
|
+
History may be requested from the tail backward one page at a time. The Session journal validates non-overlapping logical sequence ranges first; the Assembler then orders accepted inputs by their first `seq` before State replay.
|
|
277
|
+
|
|
278
|
+
| Path | Engine work | Definition-visible behavior |
|
|
279
|
+
|---|---|---|
|
|
280
|
+
| Replace on open, resync, or gap repair | Rebuild the loaded window, match every standard event or packed run once per Definition, then replay each started Context | `start`, followed by its updates in ascending logical `seq`; pending update-only Contexts remain without State |
|
|
281
|
+
| Prepend one older page | Match only fresh older inputs, merge them into Contexts by `(kind, id)`, preserve existing keyed nodes, and replay only affected Contexts and dependencies | A newly found scalar start activates its collected scalar and packed updates; a changed Location or predecessor may rerun the Context |
|
|
282
|
+
| Append one live event | Call each Definition's `match` once, look up the matched Context by key, and update only that Context | One scalar `update` and one requested publication for a matching post-start event; no existing Context scan |
|
|
283
|
+
|
|
284
|
+
With `D` registered Definitions, one incoming scalar event or packed run performs `D` current-input matches and constant-time Context-key lookup after a match. Definition code must preserve that property: do not traverse the complete event window, every Context, `context.matches`, or the rendered Node collection on the normal append path. Use State for accumulated facts, Location data for same-Turn/Step sharing, and `reader.previous()` for indexed predecessor dependencies.
|
|
285
|
+
|
|
286
|
+
`publication` controls when changed State is materialized. Use `immediate` for structural or terminal changes, `animation-frame` for high-frequency visible deltas, and `none` when the State change feeds only a later publication. The engine applies every scalar update in log order and every packed run in one batch update; cadence only coalesces view publication.
|
|
287
|
+
|
|
288
|
+
## Verification obligations
|
|
289
|
+
|
|
290
|
+
Add focused tests that establish these outcomes:
|
|
291
|
+
|
|
292
|
+
1. A complete window passed through replace produces the expected final State, Location data, Node payload, and `anchorSeq`.
|
|
293
|
+
2. An update-only tail stays pending; prepending its start produces the same result as a complete replace.
|
|
294
|
+
3. Initial history followed by live append produces the same result as replaying the combined window.
|
|
295
|
+
4. Prepending an older page adds earlier rows without replacing existing keyed Node values whose data did not change.
|
|
296
|
+
5. Repeated visible deltas preserve `context.key` and publish at most once per animation frame when requested.
|
|
297
|
+
6. The keyed renderer consumes `node.data` and constrained Location hooks only; it does not scan the Session event window, Contexts, or Chat Nodes.
|
|
298
|
+
7. Scalar and packed Assistant history produce the same final State, timing boundaries, and target snapshot, while one packed run remains one Match through replace, prepend, Location replay, and registry rebuild.
|
|
299
|
+
8. Creating a target source performs no builder work; explicit selection or the first subscription performs one complete replacement, later updates reach every active target, and repeated activation performs no replacement.
|
|
300
|
+
|
|
301
|
+
Use [`packages/client/ui-chat/src/client/conversation-nodes/assistant.ts`](../../packages/client/ui-chat/src/client/conversation-nodes/assistant.ts) for streaming and interruption, [`inbox.ts`](../../packages/client/ui-chat/src/client/conversation-nodes/inbox.ts) plus [`message.ts`](../../packages/client/ui-chat/src/client/conversation-nodes/message.ts) for predecessor queries, and [`packages/client/ui-deliverables`](../../packages/client/ui-deliverables) for a Definition that publishes Turn data without creating its own Node.
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# Conversation 组装
|
|
2
|
+
|
|
3
|
+
[English](conversation.md) | 中文
|
|
4
|
+
|
|
5
|
+
Conversation 是 Client `SessionEventLikeEntry` window 与浏览器 view 之间的 target-neutral assembly 层。[`ui-conversation`](../../packages/client/ui-conversation/README.zh.md)拥有 event 与 view registry、每个 `SessionBinding` 对应的 identity-stable binding、Turn/Step Location、增量 Context assembly、target source、共享 shell 与输入编排。[`ui-chat`](../../packages/client/ui-chat/README.zh.md)和 [`ui-trajectory`](../../packages/client/ui-trajectory/README.zh.md)等 target 包拥有各自的 Definition、最终 snapshot 与渲染。
|
|
6
|
+
|
|
7
|
+
本文定义数据模型与业务自有 Conversation node 的扩展路径。[Web Client 架构](web-client.zh.md)说明该子系统在 Client model 与 Slots 之间的位置;[Conversation Node 组装决策](../../.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.zh.md)记录其设计理由。
|
|
8
|
+
|
|
9
|
+
## 数据模型与所有权
|
|
10
|
+
|
|
11
|
+
Session Controller 拥有连续的已加载逻辑 event window。每个 `SessionEventLikeEntry` 要么是表示一个持久事件的 `{ type: 'event', event: SessionEvent }`,要么是表示一个 Client-only `assistant/live-chunk` 呈现的 `{ type: 'transient', event: AssistantLiveChunkEvent }`;两种内部 event 都公开 `type`、`seq`、`time` 与 `data`。`ui-conversation` 把这些 entry 直接交给 assembler,不另开 history stream。每个 Session 对应一个 `ConversationNodeAssembler`,它应用所有已注册 Definition,并为每个已注册 view target 发布独立 source。
|
|
12
|
+
|
|
13
|
+
| 概念 | Owner 与用途 |
|
|
14
|
+
|---|---|
|
|
15
|
+
| Event Definition | 业务包一次匹配一个持久 event 或 Client-only 瞬态 event,以稳定 `(kind, id)` 关联输入、折叠确定性 State,并可选择 materialize 一个 target node。 |
|
|
16
|
+
| Context | Engine 为一个 `(kind, id)` 拥有的有序 Match 与当前 State。持久和瞬态事件都可以作为 start。当前最早的 start 初始化 State,后续 Match 更新它。只有 update 的证据保持 pending,直到其 start 加载。 |
|
|
17
|
+
| Location | Engine 根据持久 boundary event 推导的 Session、Turn 或 Step 坐标。Definition 可以向一个 Turn 或 Step 发布类型化数据。 |
|
|
18
|
+
| View Definition | Target 包为每个 Session 创建一个增量 builder,并拥有该 target 的最终 snapshot 类型。 |
|
|
19
|
+
| Group Definition | 业务包从物化后的 Node 派生一个目标的根引用和组快照,拥有成员归属、分段、摘要及增量缓存。 |
|
|
20
|
+
| View | Chat 或 Trajectory 等 Slot entry 只读取自身 target snapshot,并渲染 target 自有 node。 |
|
|
21
|
+
|
|
22
|
+
Chat 与 Trajectory 可以识别同一个持久 event family,但各自保留自己的 Definition State 与最终 node payload。共享的 target-neutral 机制只包括 identity routing、有序 replay、Location data、predecessor dependency 与 publication cadence。
|
|
23
|
+
|
|
24
|
+
## Target 激活
|
|
25
|
+
|
|
26
|
+
每个 Session 都保留单调增长的 active target 集合。创建或读取 target source 不会激活它。shell 会显式激活持久化选择或新选择的 View,其他消费者则通过 target source 的首个订阅激活 target。首次激活会创建该 target 的 builder,并从当前按 target 索引的 Context 调用一次 `replace()`。后续 flush 对每个 active target 调用 `apply()`,取消订阅不会移除 target。
|
|
27
|
+
|
|
28
|
+
shell 拥有 View 选择,并在 binding 创建、被选为 current 或 View roster 变化时,于渲染前解析已注册的偏好 View 或 Chat fallback。assembler 只接收解析后的 target id,不自行选择 Chat 或其他默认 target。第三方 View 使用相同的选择与激活操作。View Definition 可以提供 `toolCallFocus(callId)`;shell 仅为声明了此能力且可见的目标提供 Inspect,由目标将调用 id 映射为自己的焦点标识。
|
|
29
|
+
|
|
30
|
+
<a id="group-definitions"></a>
|
|
31
|
+
## Group Definition
|
|
32
|
+
|
|
33
|
+
可选的 `ctx.uiConversation.groups.register(definition)` 贡献沿用事件及视图注册的 effect 生命周期。它要求已有 View 目标,拒绝重复目标注册,不构造 Builder。每个 Session 仅在目标激活时创建自己的 Group 上下文。注册替换通过既有注册重建流程丢弃旧上下文,并清空其被观察的组。
|
|
34
|
+
|
|
35
|
+
[分组类型](../../packages/client/ui-conversation/src/client/contract/groups.ts)定义完整协议:
|
|
36
|
+
|
|
37
|
+
| 类型 | 含义 |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `ConversationGroupDefinition<Node, State, Data>` | `create()` 初始化 Session 内的 State;`update(context, input)` 返回下一份 State;`buildGroups(context)` 返回待发布输出或 `null`。替换输入要求输出 entries 与完整组替换。 |
|
|
40
|
+
| `ConversationGroupInput<Node>` | `replace` 提供目标顺序、时间线及同步的 `readNode`、`readTurn`、`readPosition` 读取器。`apply` 增加投影后的 `previous/current` 节点变化、生命周期 `changedTurns` 和 `changedTurnOrders`。不要把读取器留在 State 中。 |
|
|
41
|
+
| `GroupNodePosition` | 所属 Turn(如有)及前后紧邻的可见 Node 键。相邻关系保留仅按 Turn 取键时会遗漏的分隔。 |
|
|
42
|
+
| `NodeReference` / `GroupReference` | 以 `kind` 区分的品牌类型 `NodeKey` 或 `GroupKey`。Node 引用可选择渲染器拥有的 `groupPart`,省略表示整个 Node。 |
|
|
43
|
+
| `GroupSnapshot<Data>` | 不可变的组键、业务数据和有序 Node 引用。Group 不包含其他 Group,也不拥有原 Node 数据。 |
|
|
44
|
+
| `GroupUpdate<Data>` | `entries` 替换完整根序列,仅 apply 输入允许省略它以保留原序列。替换输入必须同时提供 `entries` 与 `groups.replace`。`groups.replace.snapshots` 替换全部组;`groups.apply.upserts/removes` 只更新具名组记录。删除组不删除 Node。 |
|
|
45
|
+
| `ConversationGroupDataMap` | 通过声明合并关联目标与数据类型,注册及 `views.grouped(target)` 共用。未声明的目标没有组载荷类型。 |
|
|
46
|
+
| `ConversationGroupedView<Data>` | 稳定的根 `entries` 与按键 `groupSource(key)` 读取器;已删除的组返回 `undefined`。 |
|
|
47
|
+
|
|
48
|
+
注册分组时 Builder 必须提供 `groupInput()`,缺少输入方法在首次激活时报错。它记录自身投影后的节点值,并在仅正文更新时保留顺序数组身份。assembler 每次更新都提供变化轮次,直接调用未分组 Builder 的调用方可以省略。拥有独立来源的 Builder 把通知推迟到 `publish()`。首次激活与普通 flush 共用同一顺序:物化 Location 数据和 Node,更新 Builder,调用 Group Definition,校验并安装分组,再发布全部受影响目标及 Location 来源。分组阶段复用既有发布节奏。
|
|
49
|
+
|
|
50
|
+
目标位置索引提供这些读取器,并标识可见键或相邻关系发生变化的 Turn。`changedTurnOrders` 包含移动 Node 的新旧所属轮次,以及相邻的轮次外 Node 被插入或移除所影响的轮次;仅生命周期变化仍由 `changedTurns` 提供。业务 Definition 可只重分这些轮次,并只刷新包含变化内容的组。结构输出仍替换完整根引用数组,但不要求重读未变化的 Node 内容。
|
|
51
|
+
|
|
52
|
+
Group 存储在安装前校验完整提交结果:根 Group 引用与记录一一对应,引用的 Node 全部存在,每个 `(NodeKey, groupPart)` 在根和成员位置中最多占位一次。整 Node 不能与自身任一部分共存。重复 upsert、重复删除以及同时 upsert 和删除一个组都会报错。仅数据 upsert 保留根及成员数组、不读取 Node,并只通知变化组的来源;完整替换重新校验全部引用。
|
|
53
|
+
|
|
54
|
+
渲染器按两种引用 kind 切换,由所属 View 选择组件,不通过组数据中的 renderer 字段选择。根 Node 引用与被引用组的成员构成完整渲染列表;未被引用的目标 Node 仍保留在存储中,但不渲染。组 body 将成员与摘要数据分开读取。展示模式必须保留组件类型、key 和成员父级;业务数据改变成员归属时,移动成员可以正常重挂载。框架不解释部分内容完备性、分段或展示策略。
|
|
55
|
+
|
|
56
|
+
## 可回放 event family
|
|
57
|
+
|
|
58
|
+
编写 Definition 前先选定稳定的业务 id。构成同一个 Node 的每条事件都必须携带该 id,或只凭自身 payload 独立推导出该 id;Client 绝不能把 update 猜测为属于“最近一个未完成”的 Context。
|
|
59
|
+
|
|
60
|
+
以一个 review job 为例,事件约定可以是:
|
|
61
|
+
|
|
62
|
+
| 事件 | 角色 | 必须持久化的事实 |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `review/start` | 唯一 start | `reviewId`、Turn/Step 坐标、标题 |
|
|
65
|
+
| `review/progress` | update | 相同的 `reviewId`、坐标、可回放进度 |
|
|
66
|
+
| `review/end` | update | 相同的 `reviewId`、坐标、最终摘要 |
|
|
67
|
+
|
|
68
|
+
跨进程边界使用生产方拥有的 branded id 类型。把 `SessionEventMap` 合并和 payload 类型放在生产方的纯类型导出中,再由 Client 包通过仅类型副作用导入该导出。每个 `(kind, id)` 最多只能有一条 start 事件。单事件业务可以把事件自身的稳定身份(例如 `event.seq`)作为 Definition 内部 id。
|
|
69
|
+
|
|
70
|
+
系统支持增量事件。如果生产方能以较低成本发出 whole-value checkpoint,应优先采用,因为 start 位于已加载窗口之外时它仍可直接使用。每条 delta 都必须携带稳定 id,并且按照日志 `seq` 升序回放时能够确定性地产生 State;它不能依赖只存在于实时内存中的状态。如果当前历史窗口只有 update,Assembler 会保留一个 pending Context,并在更早分页补齐 start 前不构造 State。如果产品必须在 start 尚未加载时渲染,terminal 或 checkpoint 事件就必须携带足够的完整 fallback 状态,让 Definition 能直接构造结果;不要通过扫描无关事件恢复它。
|
|
71
|
+
|
|
72
|
+
实时 Assistant delta 作为 Client-only `assistant/live-chunk` event 到达。重连 baseline 会把活跃的进程内紧凑 stream 展开为相同的瞬态 event,持久 `assistant/message` 与 `assistant/attempt` event 则嵌入完整紧凑 stream 供历史回放。瞬态 event 可以初始化 Context。每条工具 delta 和后续 `tool/call` 都可以按同一个 callId 匹配为 start,即使名称尚未到达;只有最早的 Match 调用 `start()`,后续 Match 调用 `update()`。准备态 Tool 节点只有在名称可用后才显示。历史分页不把已结束消息展开为实时 delta。消费 Assistant 输出的 Definition 通过同一组匹配和更新接口处理 live chunk 与持久 settlement,其他 Definition 直接返回 `null`,无需展开 stream。
|
|
73
|
+
|
|
74
|
+
<a id="definition-and-typed-chat-payload"></a>
|
|
75
|
+
|
|
76
|
+
## Definition 与类型化 Chat payload
|
|
77
|
+
|
|
78
|
+
为了完整展示关联关系,下面把生产方声明和 Client 贡献写在同一个代码块里。实际的包族中,branded id 与 `SessionEventMap` 声明留在事件生产方,Definition、Chat data 合并与 renderer 留在 Client 插件。
|
|
79
|
+
|
|
80
|
+
```ts ignore-check
|
|
81
|
+
import { createElement } from 'react'
|
|
82
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis'
|
|
83
|
+
import type { Branded } from '@deepseek-ai/dsh-brand'
|
|
84
|
+
import type {
|
|
85
|
+
ConversationLocation, ConversationNodeContext,
|
|
86
|
+
ConversationNodeDefinition,
|
|
87
|
+
} from '@deepseek-ai/dsh-client-ui-conversation/client'
|
|
88
|
+
import type { ChatNodeViewProps } from '@deepseek-ai/dsh-client-ui-chat/client'
|
|
89
|
+
|
|
90
|
+
type ReviewId = Branded<'ReviewId'>
|
|
91
|
+
|
|
92
|
+
interface ReviewStartData {
|
|
93
|
+
readonly reviewId: ReviewId
|
|
94
|
+
readonly turn: number
|
|
95
|
+
readonly step: number
|
|
96
|
+
readonly title: string
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
interface ReviewProgressData {
|
|
100
|
+
readonly reviewId: ReviewId
|
|
101
|
+
readonly turn: number
|
|
102
|
+
readonly step: number
|
|
103
|
+
readonly completed: number
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
interface ReviewEndData {
|
|
107
|
+
readonly reviewId: ReviewId
|
|
108
|
+
readonly turn: number
|
|
109
|
+
readonly step: number
|
|
110
|
+
readonly summary: string
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
declare module '@deepseek-ai/dsh-session/types' {
|
|
114
|
+
interface SessionEventMap {
|
|
115
|
+
/**
|
|
116
|
+
* Opens one durable review job.
|
|
117
|
+
* @mode emit
|
|
118
|
+
* @param data - stable identity, location, and initial display state.
|
|
119
|
+
*/
|
|
120
|
+
'review/start': ReviewStartData
|
|
121
|
+
/**
|
|
122
|
+
* Records replayable progress for one review job.
|
|
123
|
+
* @mode emit
|
|
124
|
+
* @param data - stable identity, location, and latest progress.
|
|
125
|
+
*/
|
|
126
|
+
'review/progress': ReviewProgressData
|
|
127
|
+
/**
|
|
128
|
+
* Closes one review job with its final summary.
|
|
129
|
+
* @mode emit
|
|
130
|
+
* @param data - stable identity, location, and final display state.
|
|
131
|
+
*/
|
|
132
|
+
'review/end': ReviewEndData
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
interface ReviewChatData {
|
|
137
|
+
readonly title: string
|
|
138
|
+
readonly completed: number
|
|
139
|
+
readonly status: 'running' | 'completed'
|
|
140
|
+
readonly summary?: string
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
declare module '@deepseek-ai/dsh-client-ui-chat/client' {
|
|
144
|
+
interface ChatNodeDataMap {
|
|
145
|
+
'review-job': ReviewChatData
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
declare module '@deepseek-ai/dsh-client-ui-conversation/client' {
|
|
150
|
+
interface ConversationStepDataMap {
|
|
151
|
+
'review-job': ReviewChatData
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
interface ReviewState extends ReviewChatData {
|
|
156
|
+
readonly turn: number
|
|
157
|
+
readonly step: number
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function locationOf(context: ConversationNodeContext): ConversationLocation {
|
|
161
|
+
return context.start?.location ?? context.matches[0]?.location ?? { kind: 'unresolved' }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function viewData(state: ReviewState): ReviewChatData {
|
|
165
|
+
return {
|
|
166
|
+
title: state.title,
|
|
167
|
+
completed: state.completed,
|
|
168
|
+
status: state.status,
|
|
169
|
+
...state.summary === undefined ? {} : { summary: state.summary },
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const reviewDefinition: ConversationNodeDefinition<ReviewState> = {
|
|
174
|
+
kind: 'review-job',
|
|
175
|
+
target: 'chat',
|
|
176
|
+
match: (event) => {
|
|
177
|
+
if (event.type === 'review/start') {
|
|
178
|
+
return { id: String(event.data.reviewId), role: 'start' }
|
|
179
|
+
}
|
|
180
|
+
if (event.type === 'review/progress' || event.type === 'review/end') {
|
|
181
|
+
return { id: String(event.data.reviewId), role: 'update' }
|
|
182
|
+
}
|
|
183
|
+
return null
|
|
184
|
+
},
|
|
185
|
+
start: (_context, match) => {
|
|
186
|
+
if (match.event.type !== 'review/start') throw new Error('review-job requires review/start')
|
|
187
|
+
return {
|
|
188
|
+
turn: match.event.data.turn,
|
|
189
|
+
step: match.event.data.step,
|
|
190
|
+
title: match.event.data.title,
|
|
191
|
+
completed: 0,
|
|
192
|
+
status: 'running',
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
update: (context, match) => {
|
|
196
|
+
if (match.event.type === 'review/progress') {
|
|
197
|
+
return { ...context.state, completed: match.event.data.completed }
|
|
198
|
+
}
|
|
199
|
+
if (match.event.type === 'review/end') {
|
|
200
|
+
return { ...context.state, completed: 100, status: 'completed', summary: match.event.data.summary }
|
|
201
|
+
}
|
|
202
|
+
return context.state
|
|
203
|
+
},
|
|
204
|
+
publication: match => match.event.type === 'review/progress'
|
|
205
|
+
? 'animation-frame'
|
|
206
|
+
: 'immediate',
|
|
207
|
+
buildLocationData: (context, scope) => {
|
|
208
|
+
if (scope !== 'step' || context.state === undefined) return null
|
|
209
|
+
return {
|
|
210
|
+
kind: 'step',
|
|
211
|
+
turn: context.state.turn,
|
|
212
|
+
step: context.state.step,
|
|
213
|
+
key: 'review-job',
|
|
214
|
+
value: viewData(context.state),
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
buildViewNode: (context) => {
|
|
218
|
+
if (context.state === undefined) return null
|
|
219
|
+
return {
|
|
220
|
+
key: context.key,
|
|
221
|
+
kind: 'review-job',
|
|
222
|
+
id: context.id,
|
|
223
|
+
target: 'chat',
|
|
224
|
+
anchorSeq: context.start?.event.seq ?? context.matches[0]?.event.seq ?? 0,
|
|
225
|
+
location: locationOf(context),
|
|
226
|
+
visibility: 'visible',
|
|
227
|
+
data: viewData(context.state),
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function ReviewNodeView({ node }: ChatNodeViewProps<'review-job'>) {
|
|
233
|
+
const text = node.data.summary ?? `${node.data.title}: ${node.data.completed}%`
|
|
234
|
+
return createElement('p', null, text)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export const inject = ['uiConversation', 'slots']
|
|
238
|
+
|
|
239
|
+
export function apply(ctx: ClientContext): void {
|
|
240
|
+
ctx.uiConversation.events.register(reviewDefinition)
|
|
241
|
+
ctx.slots.inject('conversation.chat.node', () => ctx.slots.register({
|
|
242
|
+
name: 'conversation.chat.node',
|
|
243
|
+
key: 'review-job',
|
|
244
|
+
}, ReviewNodeView))
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
`match(event)` 是身份提取器,不是 fold:它只能收到当前 `SessionEventLike`,并返回 Definition 内部 id 与生命周期角色。命中后,Assembler 通过 `(kind, id)` 定位 Context;当前最早的 start 初始化 State,不论它是持久事件还是瞬态事件。后续所有 Match,包括其他 start,都调用 `update`。移除瞬态 Match 后,从剩余事件重新选择 start 并重算 State;没有剩余 start 时 State 为 undefined。两个函数都必须返回引擎随后采用的 State;推荐返回新的 immutable value,但函数原地修改后返回同一对象时,采用语义也相同。
|
|
249
|
+
|
|
250
|
+
`ConversationNodeDefinitionInput` 接受上述函数,或事件类型到匹配函数的只读表。函数形式的注册接收所有事件;表形式只接收列出的类型。Registry 在注册关系变化时复制表条目并预计算有序候选 Set,不在逐事件分发时构造候选集合。替换表时先注销,再重新注册。两种形式解析后的 `ConversationNodeDefinition` 都保留可调用的 `match(event)`。
|
|
251
|
+
|
|
252
|
+
match 结果带 `exclusive: true` 时,即在该 Definition 的 target 上认领这个事件。Assembler 先求出该事件的全部 match,再逐个接收,因此结果与注册顺序无关:被认领 target 上的其他 Definition 不为该事件建立 Context,该 target 的未匹配事件 fallback 也不运行;其他 target 的 Definition 与没有 target 的 Definition 照常接收。同一 target 被认领两次,或没有 target 的 Definition 发起认领,都会在分发时抛错;fallback 自身返回的认领不起作用。业务 Definition 用认领来呈现某一类消息,而同一 target 上的通用 Definition 原本也会呈现它,例如迟到的问题回答。
|
|
253
|
+
|
|
254
|
+
`buildLocationData(context, scope)` 可以把 Definition 拥有的数据发布到引擎拥有的 Turn 或 Step 上。通过 declaration merging 为每个 key 指定精确 value 类型。同一 Location 内的另一个 Node 可以使用受限 slot hook(例如 `useTurnData(key)`)读取该值,无须取得 Session,也无须扫描 `snapshot.chat.nodes`。
|
|
255
|
+
|
|
256
|
+
`target` 与 `buildViewNode(context)` 必须同时声明一项由 target 拥有的渲染贡献。把 `context.key` 保留为 React 侧身份,根据持久排序证据选择 `anchorSeq`,并且只返回 renderer 可以直接使用的数据。某个 target Node 一旦发布,就要继续返回同一个 key;需要暂时离开可见流时使用 `visibility: 'hidden'`,不要改为返回 `null` 撤回它。
|
|
257
|
+
|
|
258
|
+
Chat Node 还通过 `groupTree` 选择所在行的位置。位置由外到内排列,站在某个位置的 Node 同时具有所有内层位置的效果。Chat 的过程分组、整轮折叠和轮次输入判断只读取这个位置;完整契约见 [group-tree.ts](../../packages/client/ui-chat/src/client/contract/group-tree.ts)。
|
|
259
|
+
|
|
260
|
+
| Node 是 | 声明 |
|
|
261
|
+
|---|---|
|
|
262
|
+
| Step 1 认领的人的输入,所在轮次开启一个交互段 | `exchange` |
|
|
263
|
+
| 所在轮次的其他输入,如之后的人的输入或触发通知;位于过程之前时是开场输入,之后出现时整个轮次保持展开 | `input` |
|
|
264
|
+
| 留在整轮折叠之外的轮次通知,如错误或已完成轮次页脚 | `turn` |
|
|
265
|
+
| 整轮折叠仍会隐藏的独立行,如模型重试 | `process` |
|
|
266
|
+
| 并入相邻步骤组的普通过程内容 | 不声明 `groupTree` |
|
|
267
|
+
|
|
268
|
+
各内置 Node 声明的位置见 [Chat 业务规则](../../packages/client/ui-chat/src/client/conversation-nodes/README.zh.md#process-grouping)。如果同一 target 的通用 Definition 也会匹配你的 Node 所呈现的事件,按上文用 `exclusive: true` 认领该事件。
|
|
269
|
+
|
|
270
|
+
## Predecessor read
|
|
271
|
+
|
|
272
|
+
有些 Definition 需要另一个业务 kind 在当前位置之前的最新 State。`start` 会收到 `ConversationContextReader`;应在这里调用 `reader.previous<State>(kind)`,不要接收 Context 集合或扫描事件。Reader 返回当前 start `seq` 之前最近一个已启动 Context 的只读数据。
|
|
273
|
+
|
|
274
|
+
Assembler 会记录这项依赖。如果后续 older prepend 带来了更近的前序 Context、补齐了原先未知的窗口缺口,或者前序 State 被修订,引擎会从 `start` 重新运行依赖方 Context,并按 `seq` 升序回放其 update。被查询的 Definition 仍负责把有用信息写入自身 State;Reader 不提供业务专用查询方法,也不授予修改其他 Context 的权限。
|
|
275
|
+
|
|
276
|
+
## Window 更新路径
|
|
277
|
+
|
|
278
|
+
历史可能从尾部开始一页一页向前请求。Session journal 先校验互不重叠的逻辑 seq range,Assembler 再按每个已接受 input 的首 `seq` 排序并进入 State 回放。
|
|
279
|
+
|
|
280
|
+
| 路径 | 引擎工作 | Definition 可观察到的行为 |
|
|
281
|
+
|---|---|---|
|
|
282
|
+
| open、resync 或 gap repair 时 replace | 重建已加载窗口,每条标准 event 或 packed run 对每个 Definition 匹配一次,再回放每个已有 start 的 Context | 先执行 `start`,再按逻辑 `seq` 升序执行其 update;只有 update 的 pending Context 仍没有 State |
|
|
283
|
+
| prepend 一页更早历史 | 只匹配新增的更早 input,按 `(kind, id)` 合并进 Context,保留现有 keyed node,并只重放受影响的 Context 与依赖 | 新发现的 scalar start 会激活已收集的 scalar 与 packed update;Location 或前序依赖变化也可能重跑 Context |
|
|
284
|
+
| append 一条实时事件 | 每个 Definition 各调用一次 `match`,按 key 查找命中的 Context,只更新该 Context | 对 start 之后的匹配事件执行一次 scalar `update` 并请求一次发布;不扫描已有 Context |
|
|
285
|
+
|
|
286
|
+
注册 `D` 个 Definition 时,一条新 scalar event 或 packed run 会进行 `D` 次仅当前 input 匹配;命中后的 Context key 查询是常数时间。Definition 代码必须维持这个性质:正常 append 热路径不得遍历完整事件窗口、所有 Context、`context.matches` 或已渲染 Node 集合。累计事实放进 State,同 Turn/Step 共享信息放进 Location data,有索引的前序依赖使用 `reader.previous()`。
|
|
287
|
+
|
|
288
|
+
`publication` 控制发生 State 变更后何时物化。结构或 terminal 变化使用 `immediate`,高频可见 delta 使用 `animation-frame`,只为后续发布积累 State 时使用 `none`。引擎按日志顺序应用每条 scalar update,并用一次 batch update 应用一个 packed run;该选项只合并视图发布频率。
|
|
289
|
+
|
|
290
|
+
## 验证要求
|
|
291
|
+
|
|
292
|
+
添加聚焦测试,证明以下结果:
|
|
293
|
+
|
|
294
|
+
1. 完整窗口通过 replace 后产生预期的最终 State、Location data、Node payload 与 `anchorSeq`。
|
|
295
|
+
2. 只有 update 的尾部窗口保持 pending;prepend 其 start 后,结果与完整 replace 相同。
|
|
296
|
+
3. 初始历史后继续实时 append,与回放合并后的完整窗口得到相同结果。
|
|
297
|
+
4. prepend 更早分页只增加更早的行;数据未变化的既有 keyed Node value 不被替换。
|
|
298
|
+
5. 重复的可见 delta 保持 `context.key`,并在请求 `animation-frame` 时每帧最多发布一次。
|
|
299
|
+
6. keyed renderer 只消费 `node.data` 与受限 Location hook,不扫描 Session 事件窗口、Context 或 Chat Node。
|
|
300
|
+
7. scalar 与 packed Assistant 历史产生相同的最终 State、timing boundary 和 target snapshot;一个 packed run 在 replace、prepend、Location replay 与 registry rebuild 中始终只保留一个 Match。
|
|
301
|
+
8. 创建 target source 不执行 builder 工作;显式选择或首次订阅执行一次完整 replace,后续更新送达所有 active target,重复激活不会再次 replace。
|
|
302
|
+
|
|
303
|
+
流式与中断处理可参考 [`packages/client/ui-chat/src/client/conversation-nodes/assistant.ts`](../../packages/client/ui-chat/src/client/conversation-nodes/assistant.ts),前序查询可参考 [`inbox.ts`](../../packages/client/ui-chat/src/client/conversation-nodes/inbox.ts) 与 [`message.ts`](../../packages/client/ui-chat/src/client/conversation-nodes/message.ts),只发布 Turn data 而不创建自有 Node 的例子见 [`packages/client/ui-deliverables`](../../packages/client/ui-deliverables)。
|