@deepseek-harness-tui/dsh-tui 0.14.1 → 0.16.0
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.md +163 -17
- package/bin/dsh-tui.js +200 -34
- package/cordis.patch.yml +16 -52
- package/cordis.yml +6 -2
- package/guide/dsh-tui-guide/claude-backend.en.md +44 -7
- package/guide/dsh-tui-guide/claude-backend.md +28 -6
- package/guide/dsh-tui-guide/codex-backend.en.md +47 -10
- package/guide/dsh-tui-guide/codex-backend.md +26 -4
- package/guide/dsh-tui-guide/configuration.en.md +168 -18
- package/guide/dsh-tui-guide/configuration.md +144 -16
- package/guide/dsh-tui-guide/getting-started.en.md +15 -0
- package/guide/dsh-tui-guide/getting-started.md +15 -0
- package/guide/dsh-tui-guide/interaction.en.md +64 -24
- package/guide/dsh-tui-guide/interaction.md +49 -19
- package/guide/dsh-tui-guide/plugins.en.md +97 -3
- package/guide/dsh-tui-guide/plugins.md +55 -2
- package/guide/dsh-tui-guide/themes.en.md +23 -14
- package/guide/dsh-tui-guide/themes.md +19 -12
- package/guide/dsh-tui-guide/user-guide.en.md +53 -21
- package/guide/dsh-tui-guide/user-guide.md +43 -22
- package/lib/settings.json +103 -9
- package/lib/types/adapter/channel/projection.d.ts.map +1 -1
- package/lib/types/adapter/channel/projection.js +1 -0
- package/lib/types/adapter/channel/ui-policy.d.ts +1 -1
- package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
- package/lib/types/adapter/channel/ui-policy.js +4 -0
- package/lib/types/adapter/channel/ui.d.ts +2 -0
- package/lib/types/adapter/channel/ui.d.ts.map +1 -1
- package/lib/types/adapter/channel/ui.js +6 -2
- package/lib/types/adapter/kernel/runtime.d.ts.map +1 -1
- package/lib/types/adapter/kernel/runtime.js +2 -0
- package/lib/types/adapter/ports/channel-catalog.d.ts +5 -1
- package/lib/types/adapter/ports/channel-catalog.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-settings.d.ts +14 -3
- package/lib/types/adapter/ports/channel-settings.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-ui.d.ts +56 -12
- package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-view.d.ts +68 -0
- package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel.d.ts +2 -0
- package/lib/types/adapter/ports/channel.d.ts.map +1 -1
- package/lib/types/adapter/spec/backend-capabilities.d.ts +22 -0
- package/lib/types/adapter/spec/backend-capabilities.d.ts.map +1 -0
- package/lib/types/adapter/spec/backend-capabilities.js +49 -0
- package/lib/types/adapter/spec/protocol-constants.d.ts.map +1 -1
- package/lib/types/adapter/spec/protocol-constants.js +10 -3
- package/lib/types/adapter/spec/tui-contributions.d.ts +3 -0
- package/lib/types/adapter/spec/tui-contributions.d.ts.map +1 -1
- package/lib/types/adapter/spec/tui-contributions.js +7 -0
- package/lib/types/adapter/standard/registry.d.ts +6 -0
- package/lib/types/adapter/standard/registry.d.ts.map +1 -1
- package/lib/types/adapter/standard/registry.js +65 -15
- package/lib/types/adapter/standard/tui-extension.d.ts.map +1 -1
- package/lib/types/adapter/standard/tui-extension.js +7 -1
- package/lib/types/adapter/standard/types.d.ts +10 -1
- package/lib/types/adapter/standard/types.d.ts.map +1 -1
- package/lib/types/adapter/upstream/host-descriptor-driver.d.ts.map +1 -1
- package/lib/types/adapter/upstream/host-descriptor-driver.js +52 -0
- package/lib/types/agent/backend-manifest.d.ts +152 -0
- package/lib/types/agent/backend-manifest.d.ts.map +1 -0
- package/lib/types/agent/backend-manifest.js +14 -0
- package/lib/types/agent/backend.d.ts +71 -5
- package/lib/types/agent/backend.d.ts.map +1 -1
- package/lib/types/agent/capabilities.d.ts +6 -0
- package/lib/types/agent/capabilities.d.ts.map +1 -1
- package/lib/types/agent/events.d.ts +3 -2
- package/lib/types/agent/events.d.ts.map +1 -1
- package/lib/types/agent/index.d.ts +2 -0
- package/lib/types/agent/index.d.ts.map +1 -1
- package/lib/types/agent/refs.d.ts +11 -1
- package/lib/types/agent/refs.d.ts.map +1 -1
- package/lib/types/agent/starting-session.d.ts +4 -0
- package/lib/types/agent/starting-session.d.ts.map +1 -0
- package/lib/types/agent/starting-session.js +19 -0
- package/lib/types/backends/claude/activity.d.ts +1 -1
- package/lib/types/backends/claude/activity.d.ts.map +1 -1
- package/lib/types/backends/claude/activity.js +5 -5
- package/lib/types/backends/claude/auth.d.ts.map +1 -1
- package/lib/types/backends/claude/auth.js +10 -3
- package/lib/types/backends/claude/backend.d.ts.map +1 -1
- package/lib/types/backends/claude/backend.js +103 -29
- package/lib/types/backends/claude/catalog.d.ts +2 -1
- package/lib/types/backends/claude/catalog.d.ts.map +1 -1
- package/lib/types/backends/claude/catalog.js +15 -5
- package/lib/types/backends/claude/channels.d.ts +5 -3
- package/lib/types/backends/claude/channels.d.ts.map +1 -1
- package/lib/types/backends/claude/channels.js +5 -4
- package/lib/types/backends/claude/contract.d.ts +4 -0
- package/lib/types/backends/claude/contract.d.ts.map +1 -1
- package/lib/types/backends/claude/contract.js +4 -0
- package/lib/types/backends/claude/controls.d.ts +4 -0
- package/lib/types/backends/claude/controls.d.ts.map +1 -1
- package/lib/types/backends/claude/controls.js +34 -42
- package/lib/types/backends/claude/dialogs.d.ts +8 -2
- package/lib/types/backends/claude/dialogs.d.ts.map +1 -1
- package/lib/types/backends/claude/dialogs.js +24 -16
- package/lib/types/backends/claude/images.d.ts +1 -1
- package/lib/types/backends/claude/images.d.ts.map +1 -1
- package/lib/types/backends/claude/images.js +7 -7
- package/lib/types/backends/claude/manifest.d.ts +21 -0
- package/lib/types/backends/claude/manifest.d.ts.map +1 -0
- package/lib/types/backends/claude/manifest.js +29 -0
- package/lib/types/backends/claude/older-history.js +2 -2
- package/lib/types/backends/claude/permissions.js +11 -11
- package/lib/types/backends/claude/prefs.d.ts +4 -2
- package/lib/types/backends/claude/prefs.d.ts.map +1 -1
- package/lib/types/backends/claude/prefs.js +4 -3
- package/lib/types/backends/claude/process.d.ts +16 -4
- package/lib/types/backends/claude/process.d.ts.map +1 -1
- package/lib/types/backends/claude/process.js +100 -37
- package/lib/types/backends/claude/send-message.d.ts +1 -1
- package/lib/types/backends/claude/send-message.d.ts.map +1 -1
- package/lib/types/backends/claude/session/auth.js +22 -22
- package/lib/types/backends/claude/session/channels.d.ts +4 -0
- package/lib/types/backends/claude/session/channels.d.ts.map +1 -1
- package/lib/types/backends/claude/session/channels.js +5 -3
- package/lib/types/backends/claude/session/lifecycle.d.ts.map +1 -1
- package/lib/types/backends/claude/session/lifecycle.js +31 -23
- package/lib/types/backends/claude/session/store.js +8 -8
- package/lib/types/backends/claude/session/subagents.js +2 -2
- package/lib/types/backends/claude/session/types.d.ts +7 -0
- package/lib/types/backends/claude/session/types.d.ts.map +1 -1
- package/lib/types/backends/claude/side-query.js +3 -3
- package/lib/types/backends/claude/task-output.js +5 -5
- package/lib/types/backends/claude/text.d.ts +778 -0
- package/lib/types/backends/claude/text.d.ts.map +1 -0
- package/lib/types/backends/claude/text.js +298 -0
- package/lib/types/backends/claude/transcript-file.d.ts +1 -1
- package/lib/types/backends/claude/transcript-file.d.ts.map +1 -1
- package/lib/types/backends/claude/transcript-file.js +4 -4
- package/lib/types/backends/claude/translate/attempts.js +2 -2
- package/lib/types/backends/claude/translate/core.js +3 -3
- package/lib/types/backends/claude/translate/messages.js +4 -4
- package/lib/types/backends/claude/translate/system.js +14 -14
- package/lib/types/backends/claude/translate/tasks.js +2 -2
- package/lib/types/backends/claude/translate/types.d.ts +1 -1
- package/lib/types/backends/claude/translate/types.d.ts.map +1 -1
- package/lib/types/backends/codex/auth/external-tokens.js +16 -16
- package/lib/types/backends/codex/auth/status.js +14 -14
- package/lib/types/backends/codex/backend.d.ts +4 -19
- package/lib/types/backends/codex/backend.d.ts.map +1 -1
- package/lib/types/backends/codex/backend.js +118 -25
- package/lib/types/backends/codex/catalog.d.ts +2 -1
- package/lib/types/backends/codex/catalog.d.ts.map +1 -1
- package/lib/types/backends/codex/catalog.js +63 -18
- package/lib/types/backends/codex/channels.d.ts +6 -3
- package/lib/types/backends/codex/channels.d.ts.map +1 -1
- package/lib/types/backends/codex/channels.js +15 -13
- package/lib/types/backends/codex/contract.d.ts +3 -0
- package/lib/types/backends/codex/contract.d.ts.map +1 -1
- package/lib/types/backends/codex/contract.js +3 -0
- package/lib/types/backends/codex/detect.d.ts.map +1 -1
- package/lib/types/backends/codex/detect.js +9 -6
- package/lib/types/backends/codex/manifest.d.ts +30 -0
- package/lib/types/backends/codex/manifest.d.ts.map +1 -0
- package/lib/types/backends/codex/manifest.js +23 -0
- package/lib/types/backends/codex/prefs.d.ts +4 -2
- package/lib/types/backends/codex/prefs.d.ts.map +1 -1
- package/lib/types/backends/codex/prefs.js +4 -3
- package/lib/types/backends/codex/rpc/hub.js +2 -2
- package/lib/types/backends/codex/session/activity.d.ts +1 -1
- package/lib/types/backends/codex/session/activity.d.ts.map +1 -1
- package/lib/types/backends/codex/session/activity.js +13 -13
- package/lib/types/backends/codex/session/approvals.js +30 -30
- package/lib/types/backends/codex/session/async-questions.d.ts +17 -0
- package/lib/types/backends/codex/session/async-questions.d.ts.map +1 -0
- package/lib/types/backends/codex/session/async-questions.js +89 -0
- package/lib/types/backends/codex/session/commands.d.ts +8 -2
- package/lib/types/backends/codex/session/commands.d.ts.map +1 -1
- package/lib/types/backends/codex/session/commands.js +71 -23
- package/lib/types/backends/codex/session/controls.d.ts +1 -1
- package/lib/types/backends/codex/session/controls.d.ts.map +1 -1
- package/lib/types/backends/codex/session/controls.js +21 -16
- package/lib/types/backends/codex/session/goals.d.ts.map +1 -1
- package/lib/types/backends/codex/session/goals.js +3 -3
- package/lib/types/backends/codex/session/history.js +2 -2
- package/lib/types/backends/codex/session/images.js +8 -8
- package/lib/types/backends/codex/session/input.d.ts +6 -2
- package/lib/types/backends/codex/session/input.d.ts.map +1 -1
- package/lib/types/backends/codex/session/input.js +55 -13
- package/lib/types/backends/codex/session/plan-review.js +6 -6
- package/lib/types/backends/codex/session/session.d.ts.map +1 -1
- package/lib/types/backends/codex/session/session.js +100 -42
- package/lib/types/backends/codex/session/side-query.d.ts.map +1 -1
- package/lib/types/backends/codex/session/side-query.js +27 -9
- package/lib/types/backends/codex/session/state.d.ts +6 -1
- package/lib/types/backends/codex/session/state.d.ts.map +1 -1
- package/lib/types/backends/codex/session/subagents.js +5 -5
- package/lib/types/backends/codex/session/tasks.js +4 -4
- package/lib/types/backends/codex/text.d.ts +933 -0
- package/lib/types/backends/codex/text.d.ts.map +1 -0
- package/lib/types/backends/codex/text.js +313 -0
- package/lib/types/backends/codex/translate/items.d.ts.map +1 -1
- package/lib/types/backends/codex/translate/items.js +7 -5
- package/lib/types/backends/codex/translate/live.d.ts.map +1 -1
- package/lib/types/backends/codex/translate/live.js +11 -8
- package/lib/types/backends/codex/translate/notices.js +10 -10
- package/lib/types/backends/codex/translate/question-reply.d.ts +11 -0
- package/lib/types/backends/codex/translate/question-reply.d.ts.map +1 -0
- package/lib/types/backends/codex/translate/question-reply.js +53 -0
- package/lib/types/backends/shared/channel-slug.d.ts +4 -0
- package/lib/types/backends/shared/channel-slug.d.ts.map +1 -0
- package/lib/types/backends/shared/channel-slug.js +25 -0
- package/lib/types/backends/shared/channel-tokens.d.ts.map +1 -1
- package/lib/types/backends/shared/channel-tokens.js +30 -23
- package/lib/types/{channel → backends/shared}/elicitation.d.ts +78 -21
- package/lib/types/backends/shared/elicitation.d.ts.map +1 -0
- package/lib/types/{channel → backends/shared}/elicitation.js +64 -66
- package/lib/types/backends/shared/image-size.d.ts +33 -0
- package/lib/types/backends/shared/image-size.d.ts.map +1 -0
- package/lib/types/backends/shared/image-size.js +134 -0
- package/lib/types/{utils → backends/shared}/jsonl.d.ts +5 -1
- package/lib/types/backends/shared/jsonl.d.ts.map +1 -0
- package/lib/types/{utils → backends/shared}/jsonl.js +5 -1
- package/lib/types/backends/shared/localized-text.d.ts +30 -0
- package/lib/types/backends/shared/localized-text.d.ts.map +1 -0
- package/lib/types/backends/shared/localized-text.js +24 -0
- package/lib/types/backends/shared/platform-text.d.ts +88 -0
- package/lib/types/backends/shared/platform-text.d.ts.map +1 -0
- package/lib/types/backends/shared/platform-text.js +33 -0
- package/lib/types/branding.d.ts +13 -0
- package/lib/types/branding.d.ts.map +1 -1
- package/lib/types/branding.js +18 -0
- package/lib/types/channel/channel-slug.d.ts +3 -1
- package/lib/types/channel/channel-slug.d.ts.map +1 -1
- package/lib/types/channel/channel-slug.js +3 -1
- package/lib/types/channel/projection.d.ts.map +1 -1
- package/lib/types/channel/projection.js +10 -0
- package/lib/types/channel/sanitize.d.ts +7 -0
- package/lib/types/channel/sanitize.d.ts.map +1 -1
- package/lib/types/channel/sanitize.js +9 -0
- package/lib/types/channel/usage.d.ts.map +1 -1
- package/lib/types/channel/usage.js +5 -0
- package/lib/types/commands.d.ts +15 -0
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +58 -10
- package/lib/types/components/AgentMessageComposer.d.ts.map +1 -1
- package/lib/types/components/AgentMessageComposer.js +3 -2
- package/lib/types/components/BtwPanel.d.ts.map +1 -1
- package/lib/types/components/BtwPanel.js +4 -2
- package/lib/types/components/Chat/JobCard.js +3 -3
- package/lib/types/components/CommandSuggestions.d.ts.map +1 -1
- package/lib/types/components/CommandSuggestions.js +24 -7
- package/lib/types/components/ContextBarView.d.ts +5 -1
- package/lib/types/components/ContextBarView.d.ts.map +1 -1
- package/lib/types/components/ContextBarView.js +6 -3
- package/lib/types/components/ExtensionDialog.d.ts.map +1 -1
- package/lib/types/components/ExtensionDialog.js +2 -3
- package/lib/types/components/GoalTodoPanel.d.ts.map +1 -1
- package/lib/types/components/GoalTodoPanel.js +14 -3
- package/lib/types/components/InlineMathParagraph.js +1 -1
- package/lib/types/components/KernelPicker.d.ts.map +1 -1
- package/lib/types/components/KernelPicker.js +1 -1
- package/lib/types/components/LogoV2.d.ts +4 -1
- package/lib/types/components/LogoV2.d.ts.map +1 -1
- package/lib/types/components/LogoV2.js +14 -9
- package/lib/types/components/MarkdownTable.d.ts.map +1 -1
- package/lib/types/components/MarkdownTable.js +4 -3
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +109 -15
- package/lib/types/components/ModelPicker.d.ts +24 -31
- package/lib/types/components/ModelPicker.d.ts.map +1 -1
- package/lib/types/components/ModelPicker.js +115 -40
- package/lib/types/components/PermissionsPicker.d.ts +10 -1
- package/lib/types/components/PermissionsPicker.d.ts.map +1 -1
- package/lib/types/components/PermissionsPicker.js +24 -16
- package/lib/types/components/PluginInstallPane.d.ts +21 -0
- package/lib/types/components/PluginInstallPane.d.ts.map +1 -0
- package/lib/types/components/PluginInstallPane.js +84 -0
- package/lib/types/components/PluginManagePane.d.ts +21 -0
- package/lib/types/components/PluginManagePane.d.ts.map +1 -0
- package/lib/types/components/PluginManagePane.js +69 -0
- package/lib/types/components/PromptEditor.d.ts +3 -3
- package/lib/types/components/PromptInput.d.ts +21 -3
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +65 -121
- package/lib/types/components/SdkInstallWizard.d.ts +3 -1
- package/lib/types/components/SdkInstallWizard.d.ts.map +1 -1
- package/lib/types/components/SdkInstallWizard.js +8 -1
- package/lib/types/components/SearchBox.d.ts +13 -15
- package/lib/types/components/SearchBox.d.ts.map +1 -1
- package/lib/types/components/SearchBox.js +58 -27
- package/lib/types/components/SubagentCard.d.ts +13 -1
- package/lib/types/components/SubagentCard.d.ts.map +1 -1
- package/lib/types/components/SubagentCard.js +27 -12
- package/lib/types/components/SubagentDashboard.d.ts +8 -1
- package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
- package/lib/types/components/SubagentDashboard.js +58 -16
- package/lib/types/components/ThemePicker.d.ts +1 -1
- package/lib/types/components/ThemePicker.d.ts.map +1 -1
- package/lib/types/components/ThemePicker.js +14 -11
- package/lib/types/components/Tooltip.js +1 -1
- package/lib/types/components/WorkingSpinner.js +1 -1
- package/lib/types/components/WorkspaceFlowPicker.d.ts.map +1 -1
- package/lib/types/components/WorkspaceFlowPicker.js +4 -3
- package/lib/types/components/composerImageTokens.d.ts +24 -0
- package/lib/types/components/composerImageTokens.d.ts.map +1 -0
- package/lib/types/components/composerImageTokens.js +37 -0
- package/lib/types/components/design-system/InputCaret.d.ts +7 -0
- package/lib/types/components/design-system/InputCaret.d.ts.map +1 -0
- package/lib/types/components/design-system/InputCaret.js +9 -0
- package/lib/types/components/design-system/ListItem.d.ts +13 -2
- package/lib/types/components/design-system/ListItem.d.ts.map +1 -1
- package/lib/types/components/design-system/ListItem.js +11 -5
- package/lib/types/components/design-system/ThemeProvider.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemeProvider.js +6 -8
- package/lib/types/components/kernelCatalog.d.ts +55 -11
- package/lib/types/components/kernelCatalog.d.ts.map +1 -1
- package/lib/types/components/kernelCatalog.js +42 -23
- package/lib/types/components/launchpadActions.d.ts +4 -0
- package/lib/types/components/launchpadActions.d.ts.map +1 -1
- package/lib/types/components/launchpadActions.js +5 -4
- package/lib/types/components/launchpadLayout.d.ts +14 -1
- package/lib/types/components/launchpadLayout.d.ts.map +1 -1
- package/lib/types/components/launchpadLayout.js +38 -19
- package/lib/types/components/messages/TeamMessageBubble.d.ts +20 -0
- package/lib/types/components/messages/TeamMessageBubble.d.ts.map +1 -0
- package/lib/types/components/messages/TeamMessageBubble.js +15 -0
- package/lib/types/components/messages/UserPromptMessage.d.ts +4 -3
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/messages/UserPromptMessage.js +4 -3
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +8 -3
- package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
- package/lib/types/components/questions/PlanReviewPanel.js +4 -3
- package/lib/types/components/questions/ProviderModelEditor.d.ts.map +1 -1
- package/lib/types/components/questions/ProviderModelEditor.js +3 -13
- package/lib/types/components/sessions/ForeignSessionRow.d.ts +3 -1
- package/lib/types/components/sessions/ForeignSessionRow.d.ts.map +1 -1
- package/lib/types/components/sessions/ForeignSessionRow.js +5 -2
- package/lib/types/components/sessions/SessionListRow.d.ts +3 -1
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
- package/lib/types/components/sessions/SessionListRow.js +5 -2
- package/lib/types/components/sidePanel/PanelBar.d.ts +1 -0
- package/lib/types/components/sidePanel/PanelBar.d.ts.map +1 -1
- package/lib/types/components/sidePanel/PanelBar.js +21 -17
- package/lib/types/components/sidePanel/PanelHost.d.ts.map +1 -1
- package/lib/types/components/sidePanel/PanelHost.js +5 -1
- package/lib/types/components/sidePanel/PanelStore.d.ts +8 -0
- package/lib/types/components/sidePanel/PanelStore.d.ts.map +1 -1
- package/lib/types/components/sidePanel/PanelStore.js +11 -0
- package/lib/types/components/sidePanel/SidePanelColumn.d.ts +1 -1
- package/lib/types/components/sidePanel/SidePanelColumn.d.ts.map +1 -1
- package/lib/types/components/sidePanel/SidePanelColumn.js +15 -2
- package/lib/types/components/sidePanel/SidePanelLayout.d.ts +6 -4
- package/lib/types/components/sidePanel/SidePanelLayout.d.ts.map +1 -1
- package/lib/types/components/sidePanel/SidePanelLayout.js +33 -7
- package/lib/types/components/sidePanel/SidePanelRuntimeContext.d.ts +7 -0
- package/lib/types/components/sidePanel/SidePanelRuntimeContext.d.ts.map +1 -1
- package/lib/types/components/sidePanel/SidePanelRuntimeContext.js +4 -0
- package/lib/types/components/sidePanel/btw/BtwComposer.d.ts +7 -2
- package/lib/types/components/sidePanel/btw/BtwComposer.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/BtwComposer.js +28 -8
- package/lib/types/components/sidePanel/btw/BtwPanelAdapter.d.ts +2 -1
- package/lib/types/components/sidePanel/btw/BtwPanelAdapter.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/BtwPanelAdapter.js +156 -21
- package/lib/types/components/sidePanel/btw/BtwThreadScene.d.ts +6 -2
- package/lib/types/components/sidePanel/btw/BtwThreadScene.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/BtwThreadScene.js +64 -16
- package/lib/types/components/sidePanel/btw/BtwThreadView.d.ts +15 -5
- package/lib/types/components/sidePanel/btw/BtwThreadView.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/BtwThreadView.js +101 -10
- package/lib/types/components/sidePanel/btw/threads.d.ts +18 -0
- package/lib/types/components/sidePanel/btw/threads.d.ts.map +1 -1
- package/lib/types/components/sidePanel/btw/threads.js +51 -0
- package/lib/types/components/sidePanel/builtinPanels.d.ts.map +1 -1
- package/lib/types/components/sidePanel/builtinPanels.js +5 -1
- package/lib/types/components/sidePanel/companion/CompanionPanel.js +2 -2
- package/lib/types/components/sidePanel/companion/SplashMascot.d.ts +5 -1
- package/lib/types/components/sidePanel/companion/SplashMascot.d.ts.map +1 -1
- package/lib/types/components/sidePanel/companion/SplashMascot.js +19 -4
- package/lib/types/components/sidePanel/companion/skins.d.ts +8 -0
- package/lib/types/components/sidePanel/companion/skins.d.ts.map +1 -1
- package/lib/types/components/sidePanel/companion/skins.js +57 -12
- package/lib/types/components/sidePanel/types.d.ts +7 -1
- package/lib/types/components/sidePanel/types.d.ts.map +1 -1
- package/lib/types/components/sidePanel/useSidePanel.d.ts +2 -0
- package/lib/types/components/sidePanel/useSidePanel.d.ts.map +1 -1
- package/lib/types/components/sidePanel/useSidePanel.js +11 -1
- package/lib/types/components/splashArtRows.d.ts +29 -0
- package/lib/types/components/splashArtRows.d.ts.map +1 -0
- package/lib/types/components/splashArtRows.js +31 -0
- package/lib/types/components/splashFonts.d.ts +24 -1
- package/lib/types/components/splashFonts.d.ts.map +1 -1
- package/lib/types/components/splashFonts.js +40 -5
- package/lib/types/components/teamRoster.d.ts +70 -0
- package/lib/types/components/teamRoster.d.ts.map +1 -0
- package/lib/types/components/teamRoster.js +78 -0
- package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts +3 -1
- package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts.map +1 -1
- package/lib/types/components/workspaces/HomeWorkspaceRow.js +5 -2
- package/lib/types/dsh-adapter/admission-loader.d.ts +102 -0
- package/lib/types/dsh-adapter/admission-loader.d.ts.map +1 -0
- package/lib/types/dsh-adapter/admission-loader.js +301 -0
- package/lib/types/dsh-adapter/agent-view.d.ts.map +1 -1
- package/lib/types/dsh-adapter/agent-view.js +3 -1
- package/lib/types/dsh-adapter/approvals.d.ts +7 -0
- package/lib/types/dsh-adapter/approvals.d.ts.map +1 -1
- package/lib/types/dsh-adapter/approvals.js +45 -1
- package/lib/types/dsh-adapter/backend/translate.d.ts.map +1 -1
- package/lib/types/dsh-adapter/backend/translate.js +71 -8
- package/lib/types/dsh-adapter/backend-admission.d.ts +64 -0
- package/lib/types/dsh-adapter/backend-admission.d.ts.map +1 -0
- package/lib/types/dsh-adapter/backend-admission.js +34 -0
- package/lib/types/dsh-adapter/backend-assembly.d.ts +31 -0
- package/lib/types/dsh-adapter/backend-assembly.d.ts.map +1 -0
- package/lib/types/dsh-adapter/backend-assembly.js +225 -0
- package/lib/types/dsh-adapter/backend-contribution.d.ts +62 -0
- package/lib/types/dsh-adapter/backend-contribution.d.ts.map +1 -0
- package/lib/types/dsh-adapter/backend-contribution.js +78 -0
- package/lib/types/dsh-adapter/backend-registry.d.ts +136 -0
- package/lib/types/dsh-adapter/backend-registry.d.ts.map +1 -0
- package/lib/types/dsh-adapter/backend-registry.js +304 -0
- package/lib/types/dsh-adapter/backends.d.ts +67 -24
- package/lib/types/dsh-adapter/backends.d.ts.map +1 -1
- package/lib/types/dsh-adapter/backends.generated.d.ts +12 -0
- package/lib/types/dsh-adapter/backends.generated.d.ts.map +1 -0
- package/lib/types/dsh-adapter/backends.generated.js +6 -0
- package/lib/types/dsh-adapter/backends.js +91 -46
- package/lib/types/dsh-adapter/bundled-presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/bundled-presets.js +60 -3
- package/lib/types/dsh-adapter/channel/action-readiness.d.ts +2 -0
- package/lib/types/dsh-adapter/channel/action-readiness.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/action-readiness.js +9 -3
- package/lib/types/dsh-adapter/channel/agent-view-projection.js +2 -2
- package/lib/types/dsh-adapter/channel/background-action.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/background-action.js +8 -3
- package/lib/types/dsh-adapter/channel/binding-events.d.ts +17 -3
- package/lib/types/dsh-adapter/channel/binding-events.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/binding-events.js +19 -46
- package/lib/types/dsh-adapter/channel/channel-host.d.ts +45 -0
- package/lib/types/dsh-adapter/channel/channel-host.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/channel-host.js +1 -0
- package/lib/types/dsh-adapter/channel/command-completions.d.ts +5 -0
- package/lib/types/dsh-adapter/channel/command-completions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/command-completions.js +79 -6
- package/lib/types/dsh-adapter/channel/compaction.d.ts +7 -2
- package/lib/types/dsh-adapter/channel/compaction.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/compaction.js +100 -26
- package/lib/types/dsh-adapter/channel/composer-images.d.ts +4 -2
- package/lib/types/dsh-adapter/channel/composer-images.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/composer-images.js +36 -5
- package/lib/types/dsh-adapter/channel/cordis-host.d.ts +14 -0
- package/lib/types/dsh-adapter/channel/cordis-host.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/cordis-host.js +35 -0
- package/lib/types/dsh-adapter/channel/core/actions.d.ts +3 -3
- package/lib/types/dsh-adapter/channel/core/actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/core/actions.js +15 -6
- package/lib/types/dsh-adapter/channel/core/binding-feed.d.ts +10 -3
- package/lib/types/dsh-adapter/channel/core/binding-feed.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/core/binding-feed.js +5 -1
- package/lib/types/dsh-adapter/channel/core/compose.d.ts +12 -28
- package/lib/types/dsh-adapter/channel/core/compose.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/core/compose.js +330 -117
- package/lib/types/dsh-adapter/channel/core/files.d.ts +7 -2
- package/lib/types/dsh-adapter/channel/core/files.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/core/files.js +1 -0
- package/lib/types/dsh-adapter/channel/core/host.d.ts +12 -6
- package/lib/types/dsh-adapter/channel/core/host.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/core/host.js +69 -35
- package/lib/types/dsh-adapter/channel/core/local-actions.d.ts +5 -3
- package/lib/types/dsh-adapter/channel/core/local-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/core/local-actions.js +5 -2
- package/lib/types/dsh-adapter/channel/core/session-controls.js +2 -2
- package/lib/types/dsh-adapter/channel/core/session-switch.d.ts +7 -4
- package/lib/types/dsh-adapter/channel/core/session-switch.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/core/session-switch.js +8 -4
- package/lib/types/dsh-adapter/channel/core/sessions.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/core/sessions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/core/sessions.js +75 -16
- package/lib/types/dsh-adapter/channel/extensions.d.ts +4 -3
- package/lib/types/dsh-adapter/channel/extensions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/extensions.js +109 -30
- package/lib/types/dsh-adapter/channel/input-delivery.d.ts +2 -2
- package/lib/types/dsh-adapter/channel/input-delivery.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/input-delivery.js +14 -5
- package/lib/types/dsh-adapter/channel/job-projection.d.ts +4 -0
- package/lib/types/dsh-adapter/channel/job-projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/job-projection.js +205 -24
- package/lib/types/dsh-adapter/channel/mentions.d.ts +3 -3
- package/lib/types/dsh-adapter/channel/mentions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts +7 -0
- package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/mode-permission-actions.js +235 -13
- package/lib/types/dsh-adapter/channel/model-actions.d.ts +5 -1
- package/lib/types/dsh-adapter/channel/model-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/model-actions.js +48 -29
- package/lib/types/dsh-adapter/channel/model-selection.d.ts +17 -0
- package/lib/types/dsh-adapter/channel/model-selection.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/model-selection.js +109 -0
- package/lib/types/dsh-adapter/channel/model-switch.d.ts +12 -21
- package/lib/types/dsh-adapter/channel/model-switch.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/model-switch.js +39 -102
- package/lib/types/dsh-adapter/channel/permissions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/permissions.js +4 -1
- package/lib/types/dsh-adapter/channel/projection-helpers.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/projection-helpers.js +2 -0
- package/lib/types/dsh-adapter/channel/projection.js +1 -1
- package/lib/types/dsh-adapter/channel/reports.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/reports.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/reports.js +11 -3
- package/lib/types/dsh-adapter/channel/session-fork.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-fork.js +33 -20
- package/lib/types/dsh-adapter/channel/session-listing.d.ts +20 -0
- package/lib/types/dsh-adapter/channel/session-listing.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/session-listing.js +51 -0
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts +3 -1
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-metadata.js +60 -30
- package/lib/types/dsh-adapter/channel/session-reset.js +2 -2
- package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-resume.js +22 -8
- package/lib/types/dsh-adapter/channel/session-rewind.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-rewind.js +38 -24
- package/lib/types/dsh-adapter/channel/session-tree-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-tree-actions.js +38 -24
- package/lib/types/dsh-adapter/channel/settings-host.d.ts +5 -2
- package/lib/types/dsh-adapter/channel/settings-host.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/settings-host.js +136 -5
- package/lib/types/dsh-adapter/channel/state.d.ts +43 -1
- package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/state.js +14 -1
- package/lib/types/dsh-adapter/channel/subagent-projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/subagent-projection.js +84 -12
- package/lib/types/dsh-adapter/channel/task-completion.d.ts +6 -0
- package/lib/types/dsh-adapter/channel/task-completion.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/task-completion.js +114 -0
- package/lib/types/dsh-adapter/channel/transcript.d.ts +8 -1
- package/lib/types/dsh-adapter/channel/transcript.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/transcript.js +20 -3
- package/lib/types/dsh-adapter/channel/upstream-retry.d.ts +81 -0
- package/lib/types/dsh-adapter/channel/upstream-retry.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/upstream-retry.js +138 -0
- package/lib/types/dsh-adapter/channel/workspace-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/workspace-actions.js +9 -8
- package/lib/types/dsh-adapter/channel-ui.d.ts +3 -1
- package/lib/types/dsh-adapter/channel-ui.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel-ui.js +4 -2
- package/lib/types/dsh-adapter/channel.d.ts +2 -2
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +19 -5
- package/lib/types/dsh-adapter/compat/agent-team-0.2.0.d.ts +55 -0
- package/lib/types/dsh-adapter/compat/agent-team-0.2.0.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/agent-team-0.2.0.js +96 -0
- package/lib/types/dsh-adapter/context-occupancy.d.ts +81 -4
- package/lib/types/dsh-adapter/context-occupancy.d.ts.map +1 -1
- package/lib/types/dsh-adapter/context-occupancy.js +108 -17
- package/lib/types/dsh-adapter/contract.d.ts +4 -4
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +20 -5
- package/lib/types/dsh-adapter/effect-ledger.d.ts +7 -0
- package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -1
- package/lib/types/dsh-adapter/effect-ledger.js +8 -1
- package/lib/types/dsh-adapter/entry-slot.d.ts +38 -0
- package/lib/types/dsh-adapter/entry-slot.d.ts.map +1 -0
- package/lib/types/dsh-adapter/entry-slot.js +28 -0
- package/lib/types/dsh-adapter/fresh-agent.d.ts +22 -0
- package/lib/types/dsh-adapter/fresh-agent.d.ts.map +1 -0
- package/lib/types/dsh-adapter/fresh-agent.js +226 -0
- package/lib/types/dsh-adapter/host-access.d.ts +16 -1
- package/lib/types/dsh-adapter/host-access.d.ts.map +1 -1
- package/lib/types/dsh-adapter/host-access.js +148 -2
- package/lib/types/dsh-adapter/host-contract.d.ts +103 -0
- package/lib/types/dsh-adapter/host-contract.d.ts.map +1 -0
- package/lib/types/dsh-adapter/host-contract.js +77 -0
- package/lib/types/dsh-adapter/host-dsh.d.ts +112 -0
- package/lib/types/dsh-adapter/host-dsh.d.ts.map +1 -0
- package/lib/types/dsh-adapter/host-dsh.js +481 -0
- package/lib/types/dsh-adapter/host-entry.d.ts +9 -0
- package/lib/types/dsh-adapter/host-entry.d.ts.map +1 -0
- package/lib/types/dsh-adapter/host-entry.js +253 -0
- package/lib/types/dsh-adapter/index.d.ts +53 -17
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +63 -9
- package/lib/types/dsh-adapter/install/executors.d.ts +44 -0
- package/lib/types/dsh-adapter/install/executors.d.ts.map +1 -0
- package/lib/types/dsh-adapter/install/executors.js +15 -0
- package/lib/types/dsh-adapter/install/pnpm-profile-add.d.ts +3 -0
- package/lib/types/dsh-adapter/install/pnpm-profile-add.d.ts.map +1 -0
- package/lib/types/dsh-adapter/install/pnpm-profile-add.js +150 -0
- package/lib/types/dsh-adapter/invariant.d.ts +7 -2
- package/lib/types/dsh-adapter/invariant.d.ts.map +1 -1
- package/lib/types/dsh-adapter/invariant.js +6 -3
- package/lib/types/dsh-adapter/jobs.d.ts +7 -0
- package/lib/types/dsh-adapter/jobs.d.ts.map +1 -1
- package/lib/types/dsh-adapter/jobs.js +13 -0
- package/lib/types/dsh-adapter/lite-profile.d.ts +50 -0
- package/lib/types/dsh-adapter/lite-profile.d.ts.map +1 -0
- package/lib/types/dsh-adapter/lite-profile.js +49 -0
- package/lib/types/dsh-adapter/migrate/index.js +2 -2
- package/lib/types/dsh-adapter/migrate/parse/jsonl.d.ts +1 -1
- package/lib/types/dsh-adapter/migrate/parse/jsonl.d.ts.map +1 -1
- package/lib/types/dsh-adapter/migrate/parse/jsonl.js +4 -3
- package/lib/types/dsh-adapter/oauth/profiles.d.ts.map +1 -1
- package/lib/types/dsh-adapter/oauth/profiles.js +5 -1
- package/lib/types/dsh-adapter/panels.d.ts +7 -0
- package/lib/types/dsh-adapter/panels.d.ts.map +1 -1
- package/lib/types/dsh-adapter/panels.js +41 -2
- package/lib/types/dsh-adapter/plugin-completions.d.ts +32 -0
- package/lib/types/dsh-adapter/plugin-completions.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-completions.js +80 -0
- package/lib/types/dsh-adapter/plugin-declaration.d.ts +119 -0
- package/lib/types/dsh-adapter/plugin-declaration.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-declaration.js +183 -0
- package/lib/types/dsh-adapter/plugin-feed.d.ts +94 -0
- package/lib/types/dsh-adapter/plugin-feed.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-feed.js +135 -0
- package/lib/types/dsh-adapter/plugin-guard.d.ts +115 -0
- package/lib/types/dsh-adapter/plugin-guard.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-guard.js +108 -0
- package/lib/types/dsh-adapter/plugin-host.d.ts +13 -2
- package/lib/types/dsh-adapter/plugin-host.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin-host.js +34 -8
- package/lib/types/dsh-adapter/plugin-install.d.ts +160 -0
- package/lib/types/dsh-adapter/plugin-install.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-install.js +545 -0
- package/lib/types/dsh-adapter/plugin-manage.d.ts +178 -0
- package/lib/types/dsh-adapter/plugin-manage.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-manage.js +468 -0
- package/lib/types/dsh-adapter/plugin-manager.d.ts +144 -0
- package/lib/types/dsh-adapter/plugin-manager.d.ts.map +1 -0
- package/lib/types/dsh-adapter/plugin-manager.js +83 -0
- package/lib/types/dsh-adapter/plugin.d.ts +50 -1
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +918 -234
- package/lib/types/dsh-adapter/plugins-info.d.ts +130 -8
- package/lib/types/dsh-adapter/plugins-info.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugins-info.js +469 -44
- package/lib/types/dsh-adapter/process-exit.d.ts +43 -0
- package/lib/types/dsh-adapter/process-exit.d.ts.map +1 -0
- package/lib/types/dsh-adapter/process-exit.js +60 -0
- package/lib/types/dsh-adapter/root-dispose.d.ts +18 -0
- package/lib/types/dsh-adapter/root-dispose.d.ts.map +1 -0
- package/lib/types/dsh-adapter/root-dispose.js +28 -0
- package/lib/types/dsh-adapter/session-list-metadata.d.ts +20 -0
- package/lib/types/dsh-adapter/session-list-metadata.d.ts.map +1 -0
- package/lib/types/dsh-adapter/session-list-metadata.js +56 -0
- package/lib/types/dsh-adapter/session-projection-store.d.ts +66 -0
- package/lib/types/dsh-adapter/session-projection-store.d.ts.map +1 -0
- package/lib/types/dsh-adapter/session-projection-store.js +149 -0
- package/lib/types/dsh-adapter/session-seed.d.ts +11 -0
- package/lib/types/dsh-adapter/session-seed.d.ts.map +1 -0
- package/lib/types/dsh-adapter/session-seed.js +47 -0
- package/lib/types/dsh-adapter/sessions/list.d.ts +3 -1
- package/lib/types/dsh-adapter/sessions/list.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessions/list.js +11 -5
- package/lib/types/dsh-adapter/sessions/snapshot.d.ts +0 -2
- package/lib/types/dsh-adapter/sessions/snapshot.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessions/snapshot.js +7 -70
- package/lib/types/dsh-adapter/settingsEditor.js +2 -2
- package/lib/types/dsh-adapter/sideQuestion.d.ts +21 -0
- package/lib/types/dsh-adapter/sideQuestion.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sideQuestion.js +53 -0
- package/lib/types/dsh-adapter/startup-args.d.ts +2 -0
- package/lib/types/dsh-adapter/startup-args.d.ts.map +1 -1
- package/lib/types/dsh-adapter/startup-args.js +5 -0
- package/lib/types/dsh-adapter/subagents.d.ts +33 -1
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
- package/lib/types/dsh-adapter/subagents.js +112 -9
- package/lib/types/dsh-adapter/team-mode.d.ts +74 -0
- package/lib/types/dsh-adapter/team-mode.d.ts.map +1 -0
- package/lib/types/dsh-adapter/team-mode.js +188 -0
- package/lib/types/dsh-adapter/team-roster.d.ts +67 -0
- package/lib/types/dsh-adapter/team-roster.d.ts.map +1 -0
- package/lib/types/dsh-adapter/team-roster.js +136 -0
- package/lib/types/dsh-adapter/team-tools.d.ts +58 -0
- package/lib/types/dsh-adapter/team-tools.d.ts.map +1 -0
- package/lib/types/dsh-adapter/team-tools.js +208 -0
- package/lib/types/dsh-adapter/tui-settings.d.ts +57 -0
- package/lib/types/dsh-adapter/tui-settings.d.ts.map +1 -0
- package/lib/types/dsh-adapter/tui-settings.js +199 -0
- package/lib/types/dsh-adapter/types.d.ts +1 -1
- package/lib/types/dsh-adapter/types.d.ts.map +1 -1
- package/lib/types/effortPrefs.d.ts +6 -3
- package/lib/types/effortPrefs.d.ts.map +1 -1
- package/lib/types/effortPrefs.js +7 -4
- package/lib/types/hostEntryRoute.d.ts +18 -0
- package/lib/types/hostEntryRoute.d.ts.map +1 -0
- package/lib/types/hostEntryRoute.js +57 -0
- package/lib/types/i18n.d.ts +961 -1439
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +443 -387
- package/lib/types/ink/components/AlternateScreen.js +2 -2
- package/lib/types/ink/components/App.d.ts +1 -0
- package/lib/types/ink/components/App.d.ts.map +1 -1
- package/lib/types/ink/components/App.js +16 -4
- package/lib/types/ink/components/CursorDeclarationContext.d.ts +9 -0
- package/lib/types/ink/components/CursorDeclarationContext.d.ts.map +1 -1
- package/lib/types/ink/components/CursorDeclarationContext.js +2 -0
- package/lib/types/ink/components/ScrollBox.d.ts +9 -0
- package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
- package/lib/types/ink/components/ScrollBox.js +23 -0
- package/lib/types/ink/dom.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-copy-on-select.d.ts +2 -2
- package/lib/types/ink/hooks/use-copy-on-select.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-copy-on-select.js +13 -7
- package/lib/types/ink/hooks/use-declared-cursor.d.ts +11 -3
- package/lib/types/ink/hooks/use-declared-cursor.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-declared-cursor.js +30 -10
- package/lib/types/ink/ink.d.ts +22 -17
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +151 -40
- package/lib/types/ink/kitty-graphics.d.ts +2 -0
- package/lib/types/ink/kitty-graphics.d.ts.map +1 -1
- package/lib/types/ink/kitty-graphics.js +6 -1
- package/lib/types/ink/node-cache.d.ts +2 -1
- package/lib/types/ink/node-cache.d.ts.map +1 -1
- package/lib/types/ink/output.d.ts +10 -4
- package/lib/types/ink/output.d.ts.map +1 -1
- package/lib/types/ink/output.js +14 -10
- package/lib/types/ink/render-node-to-output.d.ts +5 -2
- package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
- package/lib/types/ink/render-node-to-output.js +27 -9
- package/lib/types/ink/renderer.d.ts +1 -0
- package/lib/types/ink/renderer.d.ts.map +1 -1
- package/lib/types/ink/renderer.js +4 -3
- package/lib/types/ink/root.d.ts +4 -1
- package/lib/types/ink/root.d.ts.map +1 -1
- package/lib/types/ink/screen.d.ts +4 -2
- package/lib/types/ink/screen.d.ts.map +1 -1
- package/lib/types/ink/selection.d.ts +10 -1
- package/lib/types/ink/selection.d.ts.map +1 -1
- package/lib/types/ink/selection.js +94 -25
- package/lib/types/ink/stdin-handoff.d.ts +11 -0
- package/lib/types/ink/stdin-handoff.d.ts.map +1 -0
- package/lib/types/ink/stdin-handoff.js +20 -0
- package/lib/types/ink/styles.d.ts +2 -1
- package/lib/types/ink/styles.d.ts.map +1 -1
- package/lib/types/ink/terminal-image.d.ts +6 -4
- package/lib/types/ink/terminal-image.d.ts.map +1 -1
- package/lib/types/ink/terminal-image.js +16 -39
- package/lib/types/ink/terminal-notification.d.ts +8 -0
- package/lib/types/ink/terminal-notification.d.ts.map +1 -0
- package/lib/types/ink/terminal-notification.js +83 -0
- package/lib/types/ink/terminal.d.ts +9 -0
- package/lib/types/ink/terminal.d.ts.map +1 -1
- package/lib/types/ink/terminal.js +34 -1
- package/lib/types/ink/termio/csi.d.ts +11 -0
- package/lib/types/ink/termio/csi.d.ts.map +1 -1
- package/lib/types/ink/termio/csi.js +13 -0
- package/lib/types/ink/update-overflow-guard.d.ts +6 -0
- package/lib/types/ink/update-overflow-guard.d.ts.map +1 -1
- package/lib/types/ink/update-overflow-guard.js +17 -0
- package/lib/types/ink/wrap-text.d.ts +6 -0
- package/lib/types/ink/wrap-text.d.ts.map +1 -1
- package/lib/types/ink/wrap-text.js +17 -0
- package/lib/types/kernelPrefs.d.ts +130 -17
- package/lib/types/kernelPrefs.d.ts.map +1 -1
- package/lib/types/kernelPrefs.js +98 -35
- package/lib/types/math/inline-layout.d.ts +2 -0
- package/lib/types/math/inline-layout.d.ts.map +1 -1
- package/lib/types/math/inline-layout.js +5 -27
- package/lib/types/modelGroups.d.ts +15 -27
- package/lib/types/modelGroups.d.ts.map +1 -1
- package/lib/types/modelGroups.js +17 -31
- package/lib/types/modelPrefs.d.ts +5 -4
- package/lib/types/modelPrefs.d.ts.map +1 -1
- package/lib/types/modelPrefs.js +5 -4
- package/lib/types/modelRoute.d.ts +23 -9
- package/lib/types/modelRoute.d.ts.map +1 -1
- package/lib/types/modelRoute.js +25 -11
- package/lib/types/native-ts/yoga-layout/index.d.ts.map +1 -1
- package/lib/types/native-ts/yoga-layout/index.js +8 -1
- package/lib/types/permissionPrefs.d.ts +34 -0
- package/lib/types/permissionPrefs.d.ts.map +1 -0
- package/lib/types/permissionPrefs.js +61 -0
- package/lib/types/permissionPresetLabels.d.ts +25 -0
- package/lib/types/permissionPresetLabels.d.ts.map +1 -0
- package/lib/types/permissionPresetLabels.js +33 -0
- package/lib/types/plugin-host.d.ts +1 -0
- package/lib/types/plugin-host.d.ts.map +1 -1
- package/lib/types/preset-patch.d.ts +22 -0
- package/lib/types/preset-patch.d.ts.map +1 -0
- package/lib/types/preset-patch.js +14 -0
- package/lib/types/presetPrefs.d.ts +17 -3
- package/lib/types/presetPrefs.d.ts.map +1 -1
- package/lib/types/presetPrefs.js +20 -3
- package/lib/types/reload.d.ts +6 -10
- package/lib/types/reload.d.ts.map +1 -1
- package/lib/types/reload.js +14 -14
- package/lib/types/screens/Chat.d.ts +55 -16
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +742 -280
- package/lib/types/screens/Launchpad.d.ts +33 -17
- package/lib/types/screens/Launchpad.d.ts.map +1 -1
- package/lib/types/screens/Launchpad.js +256 -78
- package/lib/types/screens/SessionSupervisor.d.ts.map +1 -1
- package/lib/types/screens/SessionSupervisor.js +53 -12
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +7 -0
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +24 -7
- package/lib/types/screens/StatusMetrics.d.ts +46 -1
- package/lib/types/screens/StatusMetrics.d.ts.map +1 -1
- package/lib/types/screens/StatusMetrics.js +72 -0
- package/lib/types/screens/chat/useKernelPicker.d.ts +8 -4
- package/lib/types/screens/chat/useKernelPicker.d.ts.map +1 -1
- package/lib/types/screens/chat/useKernelPicker.js +61 -21
- package/lib/types/screens/chat/useModelPicker.d.ts +42 -0
- package/lib/types/screens/chat/useModelPicker.d.ts.map +1 -0
- package/lib/types/screens/chat/useModelPicker.js +182 -0
- package/lib/types/screens/chat/usePluginWriteFlow.d.ts +68 -0
- package/lib/types/screens/chat/usePluginWriteFlow.d.ts.map +1 -0
- package/lib/types/screens/chat/usePluginWriteFlow.js +92 -0
- package/lib/types/screens/chatOverlay.d.ts +30 -5
- package/lib/types/screens/chatOverlay.d.ts.map +1 -1
- package/lib/types/screens/chatOverlay.js +0 -1
- package/lib/types/screens/sessionSupervisor/ForeignSessionPanes.d.ts +4 -1
- package/lib/types/screens/sessionSupervisor/ForeignSessionPanes.d.ts.map +1 -1
- package/lib/types/screens/sessionSupervisor/ForeignSessionPanes.js +26 -5
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts +1 -1
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts.map +1 -1
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.js +51 -29
- package/lib/types/sessionHistory.d.ts +5 -0
- package/lib/types/sessionHistory.d.ts.map +1 -1
- package/lib/types/sessionHistory.js +5 -0
- package/lib/types/sessionModes.d.ts +5 -3
- package/lib/types/sessionModes.d.ts.map +1 -1
- package/lib/types/sessionModes.js +9 -3
- package/lib/types/sessions/listSnapshot.d.ts +6 -0
- package/lib/types/sessions/listSnapshot.d.ts.map +1 -0
- package/lib/types/sessions/listSnapshot.js +67 -0
- package/lib/types/settings/definitions.d.ts +50 -0
- package/lib/types/settings/definitions.d.ts.map +1 -1
- package/lib/types/settings/definitions.js +42 -8
- package/lib/types/terminal-utils/cjk-emphasis.d.ts +28 -0
- package/lib/types/terminal-utils/cjk-emphasis.d.ts.map +1 -0
- package/lib/types/terminal-utils/cjk-emphasis.js +31 -0
- package/lib/types/terminal-utils/markdown.d.ts +3 -1
- package/lib/types/terminal-utils/markdown.d.ts.map +1 -1
- package/lib/types/terminal-utils/markdown.js +7 -2
- package/lib/types/theme.d.ts +29 -12
- package/lib/types/theme.d.ts.map +1 -1
- package/lib/types/theme.js +310 -23
- package/lib/types/themeCatalog.d.ts.map +1 -1
- package/lib/types/themeCatalog.js +21 -2
- package/lib/types/tips.js +4 -4
- package/lib/types/tuiSettingsFile.d.ts +56 -0
- package/lib/types/tuiSettingsFile.d.ts.map +1 -0
- package/lib/types/tuiSettingsFile.js +206 -0
- package/lib/types/ui.d.ts +2 -0
- package/lib/types/ui.d.ts.map +1 -1
- package/lib/types/ui.js +2 -0
- package/lib/types/update.d.ts +72 -26
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +131 -43
- package/lib/types/utils/bmp.d.ts +7 -0
- package/lib/types/utils/bmp.d.ts.map +1 -1
- package/lib/types/utils/bmp.js +27 -0
- package/lib/types/utils/bootTrace.d.ts +2 -0
- package/lib/types/utils/bootTrace.d.ts.map +1 -0
- package/lib/types/utils/bootTrace.js +20 -0
- package/lib/types/utils/clipboard.d.ts.map +1 -1
- package/lib/types/utils/clipboard.js +11 -3
- package/lib/types/utils/credentials.d.ts +14 -2
- package/lib/types/utils/credentials.d.ts.map +1 -1
- package/lib/types/utils/credentials.js +34 -11
- package/lib/types/utils/imageResize.d.ts +10 -20
- package/lib/types/utils/imageResize.d.ts.map +1 -1
- package/lib/types/utils/imageResize.js +61 -134
- package/lib/types/utils/inputPaste.d.ts +2 -2
- package/lib/types/utils/inputPaste.js +2 -2
- package/lib/types/utils/pastedImagePath.d.ts +5 -0
- package/lib/types/utils/pastedImagePath.d.ts.map +1 -1
- package/lib/types/utils/pastedImagePath.js +33 -0
- package/lib/types/utils/paths.d.ts +11 -0
- package/lib/types/utils/paths.d.ts.map +1 -1
- package/lib/types/utils/paths.js +13 -0
- package/package.json +150 -114
- package/presets/winbash.mjs +716 -0
- package/tui-profile/feed/README.md +63 -0
- package/tui-profile/feed/first-party.json +10 -0
- package/tui-profile/protocols/profile-definitions.d.ts +1 -0
- package/tui-profile/protocols/profile-definitions.js +14 -0
- package/tui-profile/protocols/tui-contributions.d.ts +55 -0
- package/tui-profile/protocols/tui-contributions.js +141 -0
- package/tui-profile/registry/contracts/backend-v1alpha1.json +13 -0
- package/tui-profile/registry/registry-0.15.json +12 -0
- package/lib/types/backends/claude/install.d.ts +0 -15
- package/lib/types/backends/claude/install.d.ts.map +0 -1
- package/lib/types/backends/claude/install.js +0 -91
- package/lib/types/channel/elicitation.d.ts.map +0 -1
- package/lib/types/dsh-adapter/channel/session-lineage.d.ts +0 -42
- package/lib/types/dsh-adapter/channel/session-lineage.d.ts.map +0 -1
- package/lib/types/dsh-adapter/channel/session-lineage.js +0 -51
- package/lib/types/utils/jsonl.d.ts.map +0 -1
|
@@ -4,12 +4,19 @@ import { SessionId } from '@deepseek-ai/dsh-session';
|
|
|
4
4
|
import UserQuestionService from '@deepseek-ai/dsh-user-questions';
|
|
5
5
|
import * as toolAskUser from '@deepseek-ai/dsh-tool-ask-user';
|
|
6
6
|
import Schema from '@deepseek-ai/schemastery';
|
|
7
|
-
import { Config
|
|
7
|
+
import { Config } from './index.js';
|
|
8
8
|
import { configValues, createSettingsScope, resolveSettingsNamespace } from './compat/settings.js';
|
|
9
9
|
import { createChannel } from './channel.js';
|
|
10
10
|
import { createDshSession } from './backend/session.js';
|
|
11
|
-
import {
|
|
11
|
+
import { createTuiSettingsService } from './tui-settings.js';
|
|
12
|
+
import { EDITABLE_CONFIG_KEYS } from '../settings/definitions.js';
|
|
13
|
+
import { kernelEntriesOf } from '../components/kernelCatalog.js';
|
|
14
|
+
import { openBackendStartup, prepareBackendStartup, probeKernels, installSurfaceFor } from './backends.js';
|
|
15
|
+
import { backendLabel, isBackendIdSyntax, isRegisteredBackend, listBackends, listOfferedBackends, loadBackend, parseBackendChoice, unloadBackends } from './backend-registry.js';
|
|
16
|
+
import { createPluginInstallEntry } from './plugin-install.js';
|
|
17
|
+
import { createPluginManageEntry } from './plugin-manage.js';
|
|
12
18
|
import { formatSessionRef } from '../agent/refs.js';
|
|
19
|
+
import { createStartingSession } from '../agent/starting-session.js';
|
|
13
20
|
import { createChannelSceneOutlet } from './channel-scene-outlet.js';
|
|
14
21
|
import { mountChannelUi } from './channel-ui.js';
|
|
15
22
|
import { bindChannelCommands } from './channel/commands.js';
|
|
@@ -18,6 +25,7 @@ import { createChildStderrReporter, installChildStderrGuard } from './childStder
|
|
|
18
25
|
import { removeClipboardImageDir } from '../utils/clipboard.js';
|
|
19
26
|
import { appendCrashLog, serializeCrashDetail, unserializableCrashDetail } from '../utils/crashDetail.js';
|
|
20
27
|
import { logForDebugging } from '../utils/debug.js';
|
|
28
|
+
import { createTerminalNotifier } from '../ink/terminal-notification.js';
|
|
21
29
|
import { isEnvTruthy } from '../utils/envUtils.js';
|
|
22
30
|
import { QuestionStore, bindQuestionStore } from './questions.js';
|
|
23
31
|
import { prepareQuestionAnswerer } from './questions-answerer.js';
|
|
@@ -29,23 +37,24 @@ import { readActivityFrames } from '../activityPrefs.js';
|
|
|
29
37
|
import { commitFullscreenFactoryMigration, planFullscreenFactoryMigration, readAppliedMigrations } from '../migrationPrefs.js';
|
|
30
38
|
import { readModelPref } from '../modelPrefs.js';
|
|
31
39
|
import { explicitModelRoute, recordedModelRoute, resolveModelRoute, validateModelRoute } from '../modelRoute.js';
|
|
32
|
-
import { migratePresetPref, readPresetPref } from '../presetPrefs.js';
|
|
40
|
+
import { migratePresetPref, presetOverrideFromEnv, readPresetPref } from '../presetPrefs.js';
|
|
33
41
|
import { readEffortPref } from '../effortPrefs.js';
|
|
34
42
|
import { composePreset, filterMinimalPresetTools, resolvePersistedPreset, resolvePersistedRoute, runningPresetOf } from './presets.js';
|
|
43
|
+
import { createFreshAgent, isUnstoredFreshSession } from './fresh-agent.js';
|
|
35
44
|
import { ensurePackagedPresets } from './packaged-presets.js';
|
|
36
45
|
import { registerBundledPresets } from './bundled-presets.js';
|
|
37
46
|
import { ensureLegacySessionEventTypes, snapshotLiveSessionEvents } from './compat/index.js';
|
|
38
|
-
import { clearResumeTarget, resumeTargetFromArgv, writeResumeTarget } from '../sessionHistory.js';
|
|
39
|
-
import { initialPromptFromCmdlineArgs } from './startup-args.js';
|
|
47
|
+
import { clearResumeTarget, resumeTargetFromArgv, stripResumeArgs, writeResumeTarget } from '../sessionHistory.js';
|
|
48
|
+
import { cmdlineArgsOf, initialPromptFromCmdlineArgs } from './startup-args.js';
|
|
40
49
|
import { readHomePrefs } from '../homePrefs.js';
|
|
41
50
|
import { handoffEventTag, formatHandoffNotice } from '../handoffEvents.js';
|
|
42
51
|
import { armFirstFrameAck, beginHandoffAck, handoffAttemptId, ownsAltScreenExit } from '../handoffAck.js';
|
|
43
|
-
import {
|
|
52
|
+
import { DSH_BACKEND_ID, HOST_NOTICE_ENV, KERNEL_SWITCH_HANDOFF_ENV, RESUME_BACKEND_ENV, RESUME_RETRY_ENV, parseBackendId, readKernelPrefs, resolveRememberedBackend, resolveResumeTarget, writeKernelPrefs } from '../kernelPrefs.js';
|
|
44
53
|
import { shouldOfferOnboarding } from '../onboardingPrefs.js';
|
|
45
54
|
import { resolveSessionCwd } from '../utils/workspaceRoot.js';
|
|
46
|
-
import { beginRestartAttempt, checkForTuiUpdate, installedTuiVersion, isBootDeadlockTarget, isStandaloneRuntime, isVersionNewer, logRestartEvent, resolveDshProfileName, resolveTuiUpdateTarget, restartTui, updateTuiAndRestart, writeHandoffNotice, writeLastRunRecord } from '../update.js';
|
|
55
|
+
import { LAUNCH_PROMPT_SENT_ENV, beginRestartAttempt, checkForTuiUpdate, installedTuiVersion, isBootDeadlockTarget, isStandaloneRuntime, isVersionNewer, logRestartEvent, resolveDshProfileName, resolveTuiUpdateTarget, restartTui, updateTuiAndRestart, writeHandoffNotice, writeLastRunRecord } from '../update.js';
|
|
47
56
|
import { getLang, isLang, resolveStartupLang, setLang, t, writeLangPref } from '../i18n.js';
|
|
48
|
-
import { applyBtwContextBudget, applyBtwContextTurns, applyCodeFrameStyle, applyCompanionSkin, applyImageBacking, applyMathImageBacking, applyMathImageScale, applyMathRendering, applyMermaidDiagrams, applyPageMargin, applySidePanelOpen,
|
|
57
|
+
import { applyBtwContextBudget, applyBtwContextTurns, applyCodeFrameStyle, applyCompanionSkin, applyImageBacking, applyMathImageBacking, applyMathImageScale, applyMathRendering, applyMermaidDiagrams, applyPageMargin, applySidePanelOpen, applySidePanelRatio, applySidePanelSplitEnabled, BTW_CONTEXT_BUDGET_MAX, BTW_CONTEXT_BUDGET_MIN, BTW_CONTEXT_TURNS_MAX, BTW_CONTEXT_TURNS_MIN, DEFAULT_PAGE_MARGIN, DEFAULT_SIDE_PANEL_IDS, DEFAULT_STATUS_BAR, isPageMarginMode, normalizeJobGroupFold, normalizePageMargin, normalizeScrollGutter, normalizeSidePanelPanels, normalizeStatusBar, normalizeToolBackground, parsePageMarginSpec, resolveMathRendering, SIDE_PANEL_ID_PATTERN } from '../tuiDisplayPrefs.js';
|
|
49
58
|
import { draftComboConflicts, effectiveComboString, parseComboDraft, setKeymapOverrides, SHORTCUT_ACTIONS, } from '../utils/keymap.js';
|
|
50
59
|
import { attachHerdrIntegration } from '../herdr.js';
|
|
51
60
|
import { logMouseDebug } from '../utils/debug.js';
|
|
@@ -55,8 +64,11 @@ import { startSessionMountHeartbeat } from './session-mount-heartbeat.js';
|
|
|
55
64
|
import { reserveMount } from '../sessionMounts.js';
|
|
56
65
|
import { getHostDialogStore } from './dialogs.js';
|
|
57
66
|
import { getHostStatusStore } from './status.js';
|
|
67
|
+
import { attachSessionListMetadata } from './session-list-metadata.js';
|
|
58
68
|
import { createActivityStore } from './activity-store.js';
|
|
59
69
|
import { createContextOccupancyStore } from './context-occupancy.js';
|
|
70
|
+
import { createTeamRosterStore } from './team-roster.js';
|
|
71
|
+
import { createTeamCommandEntry, filterTeamReplacedTools } from './team-mode.js';
|
|
60
72
|
import { getHostToastStore } from './toast.js';
|
|
61
73
|
import { getHostShortcuts } from './shortcuts.js';
|
|
62
74
|
import { getHostThemes } from './themes.js';
|
|
@@ -64,6 +76,10 @@ import { attachSessionToWorkspace } from './workspace.js';
|
|
|
64
76
|
import { createLocalWorkspaceRuntime, getHostWorkspaceRuntime } from './workspaces.js';
|
|
65
77
|
import { getHostSettingsSections, getLocalSettingsSectionsHost } from './settings-sections.js';
|
|
66
78
|
import { compositionRoot, withHostRootCapability } from './host-access.js';
|
|
79
|
+
import { armAdmissionLoader } from './admission-loader.js';
|
|
80
|
+
import { assembleBackendsFromProfile } from './backend-assembly.js';
|
|
81
|
+
import { applyConfiguredSidePanelPanels, applySidePanelPanelsFromSettings } from './panels.js';
|
|
82
|
+
import { disposeRootSettled } from './root-dispose.js';
|
|
67
83
|
import { render, ThemeProvider, AlternateScreen } from '../ui.js';
|
|
68
84
|
import { PageMargin } from '../components/PageMargin.js';
|
|
69
85
|
import { normalizeSplashFont } from '../components/splashFonts.js';
|
|
@@ -73,7 +89,11 @@ import instances from '../ink/instances.js';
|
|
|
73
89
|
import { cursorMove, DISABLE_KITTY_KEYBOARD, DISABLE_MODIFY_OTHER_KEYS, DISABLE_WIN32_INPUT_MODE } from '../ink/termio/csi.js';
|
|
74
90
|
import { DBP, DFE, DISABLE_MOUSE_TRACKING, EXIT_ALT_SCREEN, SHOW_CURSOR } from '../ink/termio/dec.js';
|
|
75
91
|
import { CLEAR_ITERM2_PROGRESS, CLEAR_TAB_STATUS, supportsTabStatus, wrapForMultiplexer } from '../ink/termio/osc.js';
|
|
76
|
-
import { fatalReasonForExit, registerProcessGuardFatalSink } from '../ink/update-overflow-guard.js';
|
|
92
|
+
import { addProcessErrorAbsorber, fatalReasonForExit, registerProcessGuardFatalSink } from '../ink/update-overflow-guard.js';
|
|
93
|
+
import { CHANNEL_UI_LIFETIME_ENDED } from '../adapter/channel/ui.js';
|
|
94
|
+
import { markBoot } from '../utils/bootTrace.js';
|
|
95
|
+
import { TERMINATION_SIGNALS, dieBySignal } from './process-exit.js';
|
|
96
|
+
import { StartupOpenError } from './channel/state.js';
|
|
77
97
|
/**
|
|
78
98
|
* Interactive TUI front door for DeepSeek Harness agents.
|
|
79
99
|
*
|
|
@@ -98,6 +118,11 @@ import { fatalReasonForExit, registerProcessGuardFatalSink } from '../ink/update
|
|
|
98
118
|
let lastBootedFullscreen;
|
|
99
119
|
// Image preferences also stay fixed across host recomposes until /restart.
|
|
100
120
|
let lastBootedTerminalImages;
|
|
121
|
+
// A replacement's first process already sent the launch prompt. Read once per
|
|
122
|
+
// process (a recompose must not resend it) and dropped from the environment,
|
|
123
|
+
// so a `dsh-tui "prompt"` run from inside the session keeps its own prompt.
|
|
124
|
+
const launchPromptSent = process.env[LAUNCH_PROMPT_SENT_ENV] === '1';
|
|
125
|
+
delete process.env[LAUNCH_PROMPT_SENT_ENV];
|
|
101
126
|
// Kept importable from here: the startup parser moved to its own dependency-free
|
|
102
127
|
// module so argv probes can load it without the whole plugin graph.
|
|
103
128
|
export { initialPromptFromCmdlineArgs };
|
|
@@ -128,8 +153,10 @@ export function resolveTuiHostMode(stdoutIsTTY = process.stdout.isTTY === true,
|
|
|
128
153
|
const explicitTuiLaunch = env.DSH_TUI_LAUNCHER_VERSION !== undefined || isStandaloneRuntime();
|
|
129
154
|
return explicitTuiLaunch ? 'invalid-explicit-launch' : 'headless-host';
|
|
130
155
|
}
|
|
131
|
-
export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
156
|
+
export async function apply(ctx, runtimeConfig, configOwner = ctx, runtimeOptions = {}) {
|
|
132
157
|
const config = configValues(runtimeConfig);
|
|
158
|
+
const taskNotifier = createTerminalNotifier(process.stdout);
|
|
159
|
+
taskNotifier.configure(config.taskNotifications, config.notificationMethod);
|
|
133
160
|
// /restart handoff diagnosis: the replacement process is marked by env and
|
|
134
161
|
// logs its boot progress to ~/.dsh-tui/restart.log (ordinary launches stay
|
|
135
162
|
// silent). First line lands before anything in this function can throw.
|
|
@@ -203,21 +230,40 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
203
230
|
}
|
|
204
231
|
// Validate settings before creating an agent or taking over the terminal.
|
|
205
232
|
const tuiSettingsNs = resolveSettingsNamespace(configOwner, Config);
|
|
233
|
+
// Register before agent creation so its first checkpoint includes web list metadata.
|
|
234
|
+
attachSessionListMetadata(ctx);
|
|
235
|
+
// The TUI's own settings layer (~/.dsh-tui/settings.json): the same
|
|
236
|
+
// document on every kernel and entry. Other namespaces still go
|
|
237
|
+
// to the host's settings service (absent in the standalone entry).
|
|
238
|
+
const tuiSettings = createTuiSettingsService({
|
|
239
|
+
ns: tuiSettingsNs,
|
|
240
|
+
profile: resolveDshProfileName() ?? runtimeOptions.profile ?? 'dsh-tui',
|
|
241
|
+
keys: EDITABLE_CONFIG_KEYS,
|
|
242
|
+
delegate: () => ctx.get('settings'),
|
|
243
|
+
});
|
|
244
|
+
// The in-process DSH kernel's screen mounts before DSH has composed: what
|
|
245
|
+
// needs DSH's services runs later, in `attachDsh`.
|
|
246
|
+
const entrySlot = runtimeOptions.entrySlot;
|
|
247
|
+
const dshInEntry = entrySlot?.dsh === true;
|
|
206
248
|
// Modern hosts own a declarative registry; old hosts discover directories.
|
|
207
249
|
// A modern bundle failure must not silently fall back to obsolete files.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
250
|
+
const installPresets = async (presetCtx) => {
|
|
251
|
+
if (!await registerBundledPresets(presetCtx))
|
|
252
|
+
try {
|
|
253
|
+
for (const result of ensurePackagedPresets()) {
|
|
254
|
+
if (result.status === 'conflict') {
|
|
255
|
+
presetCtx.logger.warn(`dsh-tui: packaged preset "${result.id}" was not installed because an unmanaged preset already uses that id`);
|
|
256
|
+
}
|
|
213
257
|
}
|
|
214
258
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
259
|
+
catch (error) {
|
|
260
|
+
// A read-only home must not make the whole terminal unusable; the other
|
|
261
|
+
// official and user presets remain available.
|
|
262
|
+
presetCtx.logger.warn(`dsh-tui: unable to install packaged presets (${error instanceof Error ? error.message : String(error)})`);
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
if (!dshInEntry)
|
|
266
|
+
await installPresets(ctx);
|
|
221
267
|
// UI language resolution: DSH_TUI_LANG env var wins, then the
|
|
222
268
|
// settings.yaml `dsh-tui.lang` user layer (applied once the settings
|
|
223
269
|
// namespace registers below), then cordis.yml `lang`, then the
|
|
@@ -263,6 +309,14 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
263
309
|
}
|
|
264
310
|
}
|
|
265
311
|
}
|
|
312
|
+
// The questionnaire store every backend's questions park in (a non-DSH
|
|
313
|
+
// session's `question.request` events too); Chat renders it.
|
|
314
|
+
const questionStore = new QuestionStore(adapterRuntimeFor(ctx));
|
|
315
|
+
bindQuestionStore(ctx, questionStore);
|
|
316
|
+
// One store, one teardown effect on both API lines. The compatibility
|
|
317
|
+
// adapter binds either registration to this Cordis fiber; this separate
|
|
318
|
+
// effect rejects asks still parked in the UI during teardown.
|
|
319
|
+
ctx.effect(() => () => questionStore.rejectAll());
|
|
266
320
|
// DSH user-interaction seam: the model's ask_user_question tool parks on
|
|
267
321
|
// the userQuestions service until a UI answerer responds. Mount the
|
|
268
322
|
// service when the composition doesn't (the official dsh-base
|
|
@@ -272,46 +326,49 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
272
326
|
// seat is registered below; the 0.1.2 line's agent-aware waterfall needs the
|
|
273
327
|
// channel owner and is therefore registered immediately after the channel
|
|
274
328
|
// is created. Optional-service access goes through `ctx.get`, not the
|
|
275
|
-
// inject proxy.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
?
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
329
|
+
// inject proxy. The in-process DSH kernel mounts these from its dsh-tui row
|
|
330
|
+
// once DSH has composed: mounting them on the entry's root first would
|
|
331
|
+
// take the `userQuestions` seat the profile's own row registers.
|
|
332
|
+
const mountDshQuestionSeams = (dshCtx) => {
|
|
333
|
+
const userQuestions = dshCtx.get('userQuestions') ?? new UserQuestionService(dshCtx);
|
|
334
|
+
dshCtx.plugin(toolAskUser);
|
|
335
|
+
// The host-level tool mount above is intentional for the TUI and for user
|
|
336
|
+
// presets, but the official Minimal preset is a single-tool trajectory (one
|
|
337
|
+
// persistent shell: bash on POSIX, pwsh on Windows). Filter only that preset
|
|
338
|
+
// at the final assembly boundary. Reading the session on every assembly also
|
|
339
|
+
// makes blank-session /preset switches and resumed sessions behave correctly.
|
|
340
|
+
// Agent Teams is decided at the same boundary: presets keep their legacy
|
|
341
|
+
// delegation rows, and a request that carries `spawn_teammate` drops
|
|
342
|
+
// `subagent` / `subagent_fork` (team-mode.ts), so a team bundle switched
|
|
343
|
+
// at run time reaches the sessions already running.
|
|
344
|
+
dshCtx.on('system-prompt/assemble', async (_assembly, context, next) => {
|
|
345
|
+
const assembled = filterTeamReplacedTools(await next());
|
|
346
|
+
const presetId = context.agent === undefined ? undefined : runningPresetOf(context.agent.session);
|
|
347
|
+
return filterMinimalPresetTools(assembled, presetId);
|
|
348
|
+
});
|
|
349
|
+
// `/debug-prompt` snapshots the final provider-neutral request at the
|
|
350
|
+
// llm/stream boundary, after every prompt and tool contributor has run.
|
|
351
|
+
registerPromptDebug(dshCtx);
|
|
352
|
+
// API selection and registration live behind one adapter boundary. The UI
|
|
353
|
+
// bootstrap only translates a legacy seat conflict into its visible notice.
|
|
354
|
+
const registration = prepareQuestionAnswerer(dshCtx, userQuestions, questionStore);
|
|
355
|
+
const seatDecision = registration.kind === 'legacy' ? registration.yieldDecision : undefined;
|
|
356
|
+
let notice;
|
|
357
|
+
if (seatDecision?.action === 'alert-unverified') {
|
|
358
|
+
dshCtx.logger.error(`dsh-tui: user-questions provider seat is held by a component self-reporting as ${seatDecision.incumbentId} ` +
|
|
359
|
+
'(identity not host-verified); this TUI will not register its questionnaire and model questions may be answered by it');
|
|
360
|
+
notice = t('question-provider-occupied-unverified', { id: seatDecision.incumbentId ?? '' });
|
|
361
|
+
}
|
|
362
|
+
else if (seatDecision?.action === 'alert') {
|
|
363
|
+
const displayId = seatDecision.incumbentId ?? t('question-provider-occupied-unknown');
|
|
364
|
+
dshCtx.logger.error(`dsh-tui: user-questions provider seat is held by a non-host component (${displayId}); ` +
|
|
365
|
+
'this TUI will not register its questionnaire and model questions may be answered by it');
|
|
366
|
+
notice = t('question-provider-occupied', { id: displayId });
|
|
367
|
+
}
|
|
368
|
+
return { registration, notice };
|
|
369
|
+
};
|
|
370
|
+
const questionSeams = dshInEntry ? undefined : mountDshQuestionSeams(ctx);
|
|
371
|
+
let questionSeatNotice = questionSeams?.notice;
|
|
315
372
|
// Child-process stderr guard (issue #17): MCP servers spawned with an
|
|
316
373
|
// inherited stderr (the MCP SDK's stdio default) write straight to the
|
|
317
374
|
// terminal device from the child process, bypassing the renderer's own
|
|
@@ -344,20 +401,14 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
344
401
|
provider: config.provider,
|
|
345
402
|
model: config.model,
|
|
346
403
|
};
|
|
347
|
-
// Atomic route resolution (issue #67):
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
//
|
|
351
|
-
//
|
|
352
|
-
// without
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
&& configuredDefault.provider.length > 0
|
|
356
|
-
&& typeof configuredDefault.model === 'string'
|
|
357
|
-
&& configuredDefault.model.length > 0
|
|
358
|
-
? { provider: configuredDefault.provider, model: configuredDefault.model }
|
|
359
|
-
: undefined;
|
|
360
|
-
const startupRoute = resolveModelRoute(configuredRoute, readModelPref(), harnessDefault);
|
|
404
|
+
// Atomic route resolution (issue #67): the persisted `/model` choice — the
|
|
405
|
+
// route the last session ran — wins whole; else a complete cordis.yml route
|
|
406
|
+
// wins whole as the deployment DEFAULT (it decides the first run, never a
|
|
407
|
+
// later one); else Harness's provider-neutral agent-default-model
|
|
408
|
+
// selection. The local DeepSeek pair remains the final fallback for bare
|
|
409
|
+
// embedders without that service. This lets optional provider bundles supply
|
|
410
|
+
// the same default to Web and TUI without patching this front door by name.
|
|
411
|
+
const { startupRoute, deploymentRoute } = startupRoutes(ctx, configuredRoute);
|
|
361
412
|
// Session cwd (issue #96): explicit cordis.yml `cwd` wins; otherwise the
|
|
362
413
|
// git worktree root containing the launch directory (the launch directory
|
|
363
414
|
// itself outside any worktree), so `@` completion and mention expansion
|
|
@@ -413,11 +464,23 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
413
464
|
ctx.logger.warn('dsh-tui: tuiPluginHost service is not mounted; plugin grant store, runtime generation and Host Descriptor are unavailable. ' +
|
|
414
465
|
'The bundle patch is older than the installed dsh-tui package — update the globally installed dsh-tui launcher to match the profile (issue #183).');
|
|
415
466
|
}
|
|
467
|
+
// Admit third-party activations from their package-root `dsh-plugin.json`.
|
|
468
|
+
armAdmissionLoader(ctx);
|
|
469
|
+
// Stage D-1: the backends installed bundles declare join the registry *here*,
|
|
470
|
+
// before anything below parses a backend choice (`parseBackendChoice` reads the
|
|
471
|
+
// registry for membership, so an id registered later reads as unknown: dsh plus a
|
|
472
|
+
// warning). Synchronous on purpose — a bundle's own rows are composed after this
|
|
473
|
+
// one, so waiting for them would be waiting past the parse. This is the caller
|
|
474
|
+
// notes 0009's "Wired (W-2)" section was still missing.
|
|
475
|
+
assembleBackendsFromProfile(ctx);
|
|
476
|
+
// The in-process DSH kernel mounts before the profile's workspace providers
|
|
477
|
+
// exist: a provider URI resolves in `attachDsh`, and an unresolvable one
|
|
478
|
+
// fails the startup session there instead of the mount.
|
|
416
479
|
const initialWorkspace = requestedWorkspace === undefined
|
|
417
480
|
? undefined
|
|
418
481
|
: await workspaceService.resolve(requestedWorkspace);
|
|
419
|
-
if (requestedWorkspace !== undefined && initialWorkspace === undefined) {
|
|
420
|
-
throw new Error(
|
|
482
|
+
if (requestedWorkspace !== undefined && initialWorkspace === undefined && !dshInEntry) {
|
|
483
|
+
throw new Error(unsupportedWorkspaceTarget(requestedWorkspace));
|
|
421
484
|
}
|
|
422
485
|
const sessionCwd = initialWorkspace?.cwd ?? resolveSessionCwd(config.cwd);
|
|
423
486
|
const meta = { cwd: sessionCwd };
|
|
@@ -425,8 +488,12 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
425
488
|
// `dsh --profile tui` forwards `--resume` verbatim instead, so fall back to
|
|
426
489
|
// the same app-argv snapshot as the initial prompt. Raw process.argv also
|
|
427
490
|
// contains the DSH launcher's own -- and is only a legacy embedder fallback.
|
|
428
|
-
const
|
|
429
|
-
|
|
491
|
+
const cmdlineArgs = cmdlineArgsOf(ctx);
|
|
492
|
+
// The raw request, deliberately self-contained: `scripts/verify-startup-argv.mjs`
|
|
493
|
+
// replays this very statement (picked out of the compiled `apply` by name) to check
|
|
494
|
+
// the launcher's argv interception end to end, so it has to stay computable from
|
|
495
|
+
// `config` and `cmdlineArgs` alone. Whether the target may actually be used is
|
|
496
|
+
// decided further down, once the backend this boot lands on is known.
|
|
430
497
|
const launchSessionId = config.sessionId ?? resumeTargetFromArgv(cmdlineArgs ?? process.argv.slice(2));
|
|
431
498
|
// The session's backend (Config `backend`, `dsh-tui --backend`). A non-DSH
|
|
432
499
|
// backend opens its own session here and skips everything DSH-specific
|
|
@@ -447,28 +514,118 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
447
514
|
const handoffBackendRaw = process.env[KERNEL_SWITCH_HANDOFF_ENV];
|
|
448
515
|
if (handoffBackendRaw !== undefined)
|
|
449
516
|
delete process.env[KERNEL_SWITCH_HANDOFF_ENV];
|
|
450
|
-
const handoffBackend =
|
|
517
|
+
const handoffBackend = parseBackendChoice(handoffBackendRaw);
|
|
451
518
|
// Fullscreen kernel switch: the old process spawned this one with an ACK
|
|
452
519
|
// pipe on fd 3 and the alternate screen still open (see handoffAck.ts).
|
|
453
520
|
const handoffAck = beginHandoffAck();
|
|
454
521
|
if (handoffAck !== undefined) {
|
|
455
522
|
logRestartEvent('handoff/boot: ack armed', { attemptId: handoffAttemptId() ?? '' });
|
|
456
523
|
}
|
|
457
|
-
|
|
524
|
+
// Every source is parsed in two stages (P0 D1): syntax first, then the
|
|
525
|
+
// registry. An id that is syntactically valid but not installed must behave
|
|
526
|
+
// exactly like an unknown value did before the set was open — dsh plus a
|
|
527
|
+
// warning, never a crashed boot. `envKnown` carries that into the resolver for
|
|
528
|
+
// the one source that arrives raw; the other three are parsed above/beside.
|
|
529
|
+
const configuredBackend = parseBackendChoice(config.backend);
|
|
530
|
+
const rememberedBackend = readKernelPrefs().backend;
|
|
531
|
+
const entryKernel = runtimeOptions.entryKernel;
|
|
532
|
+
const backendChoice = entryKernel ?? (dshInEntry ? DSH_BACKEND_ID : resolveRememberedBackend({
|
|
458
533
|
...(handoffBackend === undefined ? {} : { handoff: handoffBackend }),
|
|
459
|
-
configured:
|
|
534
|
+
configured: configuredBackend,
|
|
460
535
|
envRaw: rawBackend,
|
|
461
|
-
|
|
536
|
+
envKnown: isRegisteredBackend,
|
|
537
|
+
memory: isRegisteredBackend(rememberedBackend) ? rememberedBackend : undefined,
|
|
538
|
+
}));
|
|
539
|
+
const rawBackendGiven = rawBackend === undefined ? '' : rawBackend.trim();
|
|
540
|
+
// The launcher marks a *derived* resume target with the backend it read it from
|
|
541
|
+
// (RESUME_BACKEND_ENV); a target the user placed carries no mark. Read and
|
|
542
|
+
// deleted right away, like the kernel handoff above, so no child inherits it.
|
|
543
|
+
// The mark is parsed with the syntax gate alone: the source is typically a
|
|
544
|
+
// backend that is NOT registered here, which is the whole reason to check.
|
|
545
|
+
// A marked target belongs to that one backend, and this boot may have landed
|
|
546
|
+
// elsewhere — an uninstalled plugin id falls back to dsh (P0 D1), an unset
|
|
547
|
+
// DSH_TUI_BACKEND follows the remembered kernel instead. Handing it over either
|
|
548
|
+
// fails or resumes an unrelated session that happens to share the id, so it is
|
|
549
|
+
// revoked rather than carried across (PR #1380 review R2) — and a revocation is
|
|
550
|
+
// a refusal, not a cold start (roadmap §6 item 11, B-2a).
|
|
551
|
+
const resumeBackendRaw = process.env[RESUME_BACKEND_ENV];
|
|
552
|
+
if (resumeBackendRaw !== undefined)
|
|
553
|
+
delete process.env[RESUME_BACKEND_ENV];
|
|
554
|
+
// A safe-mode retry carries a licence to survive a revoked target (the launcher
|
|
555
|
+
// sets RESUME_RETRY_ENV; see resolveResumeTarget). Read and deleted right away,
|
|
556
|
+
// like the mark above: no child may inherit it.
|
|
557
|
+
const resumeRetry = process.env[RESUME_RETRY_ENV] !== undefined;
|
|
558
|
+
delete process.env[RESUME_RETRY_ENV];
|
|
559
|
+
const resumeTarget = resolveResumeTarget({
|
|
560
|
+
sessionId: launchSessionId,
|
|
561
|
+
sourceBackend: parseBackendId(resumeBackendRaw),
|
|
562
|
+
backendChoice,
|
|
563
|
+
retry: resumeRetry,
|
|
462
564
|
});
|
|
463
|
-
|
|
464
|
-
|
|
565
|
+
const effectiveSessionId = resumeTarget.kind === 'usable' ? resumeTarget.sessionId : undefined;
|
|
566
|
+
// The non-DSH path below opens `backendChoice`'s own session — a revoked target
|
|
567
|
+
// must not reach it either.
|
|
568
|
+
const configuredSessionId = resumeTarget.kind === 'revoked' ? undefined : config.sessionId;
|
|
569
|
+
// Safe-mode retries replay the original argv. Revocation must also remove its
|
|
570
|
+
// resume flags, or either startup path can reopen the replacement backend's
|
|
571
|
+
// own last session after promising a cold start. Prompt parsing keeps raw argv.
|
|
572
|
+
const startupArgv = resumeTarget.kind === 'revoked'
|
|
573
|
+
? stripResumeArgs(cmdlineArgs ?? process.argv.slice(2))
|
|
574
|
+
: cmdlineArgs ?? process.argv.slice(2);
|
|
575
|
+
if (resumeTarget.kind === 'revoked') {
|
|
576
|
+
// Fail closed (roadmap §6 item 11): the target belongs to another backend, so
|
|
577
|
+
// this boot neither carries it over nor quietly starts a session nobody asked
|
|
578
|
+
// for. A fatal refusal is thrown into the startup funnel, which reports it on
|
|
579
|
+
// stderr and exits non-zero after restoring the terminal; the only tolerated
|
|
580
|
+
// case is the safe-mode retry, whose target comes from last-run.json and whose
|
|
581
|
+
// failure would close the user's last way back (RESUME_RETRY_ENV).
|
|
582
|
+
// The advice must be executable (PR #1449 review R2): when the source backend
|
|
583
|
+
// is not registered here, `--backend <from> --resume` would hit this same
|
|
584
|
+
// refusal, so that branch says the backend is unavailable instead. The other
|
|
585
|
+
// branch names the id (`kernelId`), never the display label — `--backend`
|
|
586
|
+
// takes an id.
|
|
587
|
+
const refusalKey = !resumeTarget.fatal
|
|
588
|
+
? 'resume-target-revoked-retry'
|
|
589
|
+
: isRegisteredBackend(resumeTarget.from) ? 'resume-target-revoked' : 'resume-target-revoked-uninstalled';
|
|
590
|
+
const refusal = t(refusalKey, {
|
|
591
|
+
from: resumeTarget.from,
|
|
592
|
+
kernel: backendLabel(backendChoice),
|
|
593
|
+
kernelId: backendChoice,
|
|
594
|
+
});
|
|
595
|
+
if (resumeTarget.fatal)
|
|
596
|
+
throw new Error(refusal);
|
|
597
|
+
ctx.logger.warn(refusal);
|
|
598
|
+
}
|
|
599
|
+
// Config and handoff outrank env: an ignored env value cannot invalidate the
|
|
600
|
+
// selected backend's resume target or claim that this boot fell back to DSH.
|
|
601
|
+
if (handoffBackend === undefined && configuredBackend === undefined
|
|
602
|
+
&& rawBackendGiven !== '' && !isRegisteredBackend(rawBackendGiven.toLowerCase())) {
|
|
603
|
+
// Two cases, two sentences: a typo is not an uninstalled plugin (P0 D1).
|
|
604
|
+
const installed = listBackends().map(entry => entry.manifest.id).join(', ');
|
|
605
|
+
// The plain fallback (no resume request) keeps P0 D1's shape: dsh plus a
|
|
606
|
+
// warning, exit code 0. With a resume target in hand it must not: dsh would
|
|
607
|
+
// be asked to open an id that belongs to the backend the user named, which
|
|
608
|
+
// either fails or resumes a session of another backend (roadmap §6 item 11).
|
|
609
|
+
// Nothing is resumed, nothing is created — the boot stops here instead.
|
|
610
|
+
if (effectiveSessionId !== undefined) {
|
|
611
|
+
throw new Error(t('resume-backend-unavailable', {
|
|
612
|
+
backend: rawBackendGiven,
|
|
613
|
+
session: effectiveSessionId,
|
|
614
|
+
installed,
|
|
615
|
+
}));
|
|
616
|
+
}
|
|
617
|
+
ctx.logger.warn(isBackendIdSyntax(rawBackendGiven)
|
|
618
|
+
? `dsh-tui: DSH_TUI_BACKEND="${rawBackend}" names no installed backend (registered: ${installed}); starting on dsh`
|
|
619
|
+
: `dsh-tui: DSH_TUI_BACKEND="${rawBackend}" is not a valid backend id (lowercase letters, digits and dashes); starting on dsh`);
|
|
465
620
|
}
|
|
466
621
|
/**
|
|
467
622
|
* Whether a Config row or DSH_TUI_BACKEND overrides the selector's
|
|
468
623
|
* remembered kernel. The selector then says so: a switch still restarts
|
|
469
624
|
* onto the chosen kernel, but the next plain launch follows the override.
|
|
625
|
+
* Only a registered Config choice pins: the schema keeps syntactically
|
|
626
|
+
* valid but uninstalled ids too, which must not prevent a memory fallback.
|
|
470
627
|
*/
|
|
471
|
-
const backendPinned =
|
|
628
|
+
const backendPinned = configuredBackend !== undefined
|
|
472
629
|
|| (rawBackend !== undefined && rawBackend.trim() !== '');
|
|
473
630
|
// A remembered kernel whose backend cannot open (its SDK uninstalled, its
|
|
474
631
|
// session store unreadable, …) must not kill the boot: without an explicit
|
|
@@ -477,27 +634,74 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
477
634
|
// Explicit choices (flag/Config/handoff) and explicit resume targets keep
|
|
478
635
|
// the hard failure: silently swapping what the user named would be worse.
|
|
479
636
|
let backendStart;
|
|
637
|
+
/** The startup session still opening (`deferBackendOpen`); the channel adopts it. */
|
|
638
|
+
let backendStartup;
|
|
639
|
+
/** Once set, the channel's binding owns the session. */
|
|
640
|
+
let backendStartupAdopted = false;
|
|
480
641
|
let backendFallbackNotice;
|
|
642
|
+
const deferBackendOpen = runtimeOptions.deferBackendOpen === true;
|
|
643
|
+
if (deferBackendOpen && backendChoice === 'dsh' && !dshInEntry) {
|
|
644
|
+
throw new Error('dsh-tui: the standalone entry cannot host the DSH kernel; start it through the profile launcher');
|
|
645
|
+
}
|
|
646
|
+
/** The in-process DSH kernel's startup session: settled by `attachDsh`
|
|
647
|
+
* (the profile's dsh-tui row) or by a failed composition. */
|
|
648
|
+
let settleDshStartup;
|
|
649
|
+
let dshStartupSettled = false;
|
|
650
|
+
const dshStartup = dshInEntry
|
|
651
|
+
? new Promise((resolve, reject) => {
|
|
652
|
+
settleDshStartup = {
|
|
653
|
+
resolve: startup => { dshStartupSettled = true; resolve(startup); },
|
|
654
|
+
reject: error => { dshStartupSettled = true; reject(error); },
|
|
655
|
+
};
|
|
656
|
+
})
|
|
657
|
+
: undefined;
|
|
658
|
+
// A boot that fails before the channel holds the startup must not leak an
|
|
659
|
+
// unhandled rejection.
|
|
660
|
+
dshStartup?.catch(() => undefined);
|
|
481
661
|
if (backendChoice !== 'dsh') {
|
|
482
662
|
try {
|
|
483
|
-
|
|
663
|
+
const startupInput = {
|
|
484
664
|
cwd: sessionCwd,
|
|
485
|
-
stderr: line => {
|
|
665
|
+
stderr: (line) => {
|
|
486
666
|
logForDebugging(`[${backendChoice}-stderr] ${line}`);
|
|
487
667
|
stderrReporter.push(line);
|
|
488
668
|
},
|
|
489
|
-
...(
|
|
490
|
-
argv:
|
|
491
|
-
}
|
|
669
|
+
...(configuredSessionId === undefined ? {} : { configuredSessionId }),
|
|
670
|
+
argv: startupArgv,
|
|
671
|
+
};
|
|
672
|
+
// One load per launch, shared by the deferred path (the entry mounts
|
|
673
|
+
// between `prepare` and `start`) and the eager one.
|
|
674
|
+
const backendModule = await loadBackend(backendChoice);
|
|
675
|
+
if (deferBackendOpen) {
|
|
676
|
+
const prepared = await prepareBackendStartup(ctx, backendModule, startupInput);
|
|
677
|
+
const startup = prepared.start().then(opened => ({ session: opened.session, history: opened.initialHistory }));
|
|
678
|
+
backendStartup = startup;
|
|
679
|
+
// As for dshStartup above; the effect closes an unadopted session.
|
|
680
|
+
startup.catch(() => undefined);
|
|
681
|
+
ctx.effect(() => () => {
|
|
682
|
+
if (backendStartupAdopted)
|
|
683
|
+
return;
|
|
684
|
+
void startup.then(opened => opened.session.dispose(), () => undefined).catch(() => undefined);
|
|
685
|
+
}, 'dsh-tui deferred backend startup');
|
|
686
|
+
backendStart = { ...prepared, session: createStartingSession(prepared.backendId, sessionCwd), initialHistory: [] };
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
backendStart = await openBackendStartup(ctx, backendModule, startupInput);
|
|
690
|
+
}
|
|
492
691
|
}
|
|
493
692
|
catch (error) {
|
|
494
|
-
if (backendPinned || handoffBackend !== undefined ||
|
|
693
|
+
if (deferBackendOpen || backendPinned || handoffBackend !== undefined || effectiveSessionId !== undefined)
|
|
495
694
|
throw error;
|
|
496
695
|
const reason = error instanceof Error ? error.message : String(error);
|
|
497
696
|
logForDebugging(`dsh-tui: remembered backend "${backendChoice}" failed to open (${reason}); falling back to dsh`);
|
|
498
|
-
backendFallbackNotice = t('kernel-memory-fallback', { name:
|
|
697
|
+
backendFallbackNotice = t('kernel-memory-fallback', { name: backendLabel(backendChoice), reason });
|
|
499
698
|
}
|
|
500
699
|
}
|
|
700
|
+
// Resolve a surviving bare request on the backend that actually opened.
|
|
701
|
+
// This also covers a remembered backend whose startup fell back to DSH.
|
|
702
|
+
const bootSessionId = backendStart === undefined
|
|
703
|
+
? effectiveSessionId ?? resumeTargetFromArgv(startupArgv)
|
|
704
|
+
: backendStart.resumedSessionId;
|
|
501
705
|
// The backend session (and its child process) belongs to this fiber until the
|
|
502
706
|
// channel adopts it: a boot that throws before then disposes the fiber's
|
|
503
707
|
// effects, and this one stops the child instead of leaking it. Dispose is
|
|
@@ -515,11 +719,15 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
515
719
|
const backendPermissions = backendStart === undefined ? undefined : new PermissionStore();
|
|
516
720
|
if (backendPermissions !== undefined)
|
|
517
721
|
ctx.effect(() => () => backendPermissions.settleAll());
|
|
518
|
-
const
|
|
722
|
+
const resolved = backendStart !== undefined || dshInEntry
|
|
519
723
|
? { agent: undefined, handle: undefined, agentPreset: undefined, route: undefined }
|
|
520
|
-
: await resolveAgent(ctx,
|
|
724
|
+
: await resolveAgent(ctx, bootSessionId, configuredRoute, startupRoute, deploymentRoute, meta, config.preset);
|
|
725
|
+
let agent = resolved.agent;
|
|
726
|
+
const { handle, agentPreset, route: createdRoute } = resolved;
|
|
521
727
|
// Workspace ownership is a DSH session-store fact (skipped off DSH).
|
|
522
|
-
|
|
728
|
+
// `ctx` here is the context that owns the session: this plugin's, or the
|
|
729
|
+
// dsh-tui row's for the in-process DSH kernel (`attachDsh`).
|
|
730
|
+
const attachWorkspaceOwnership = async (ctx, agent) => {
|
|
523
731
|
try {
|
|
524
732
|
// Opening a persisted TUI session is an explicit ownership action too.
|
|
525
733
|
// Older TUI versions only wrote the Session log, so attaching on every
|
|
@@ -556,6 +764,9 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
556
764
|
// loud instead of silently leaving the conversation Ungrouped.
|
|
557
765
|
ctx.logger.warn(`dsh-tui: session "${agent.session.id}" workspace attachment failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
558
766
|
}
|
|
767
|
+
};
|
|
768
|
+
if (agent !== undefined)
|
|
769
|
+
await attachWorkspaceOwnership(ctx, agent);
|
|
559
770
|
// Status-line route: the exact route the agent runs with — on create the
|
|
560
771
|
// validated startup resolution, on resume the route the target session's
|
|
561
772
|
// own records carry (a complete cordis.yml pin wins over them).
|
|
@@ -579,6 +790,9 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
579
790
|
// empty and the channel falls back to the last-request sample — as does a
|
|
580
791
|
// non-DSH session, whose id the DSH meter never projects.
|
|
581
792
|
const contextOccupancyStore = createContextOccupancyStore(ctx);
|
|
793
|
+
// Read side of the Agent Teams `agentTeam` projection, for the same TDZ
|
|
794
|
+
// reason; empty unless the profile mounts the team service.
|
|
795
|
+
const teamRosterStore = createTeamRosterStore(ctx);
|
|
582
796
|
// The channel holds a backend session; this DSH one owns the resolved
|
|
583
797
|
// handle (disposed by the binding when a later adoption replaces it).
|
|
584
798
|
let startupSession;
|
|
@@ -586,16 +800,36 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
586
800
|
startupSession = backendStart.session;
|
|
587
801
|
else if (agent !== undefined)
|
|
588
802
|
startupSession = createDshSession(ctx, { agent, handle });
|
|
803
|
+
else if (dshInEntry)
|
|
804
|
+
startupSession = createStartingSession('dsh', sessionCwd);
|
|
589
805
|
else
|
|
590
806
|
throw new Error('dsh-tui: no startup session was opened');
|
|
807
|
+
// The in-process DSH kernel's route and Config pins from its dsh-tui row,
|
|
808
|
+
// set in `attachDsh` before adoption; the getters below serve them.
|
|
809
|
+
const rowFacts = {};
|
|
810
|
+
/**
|
|
811
|
+
* The in-process DSH kernel's `/new` while no DSH session was adopted (its
|
|
812
|
+
* startup open failed). Installed by `attachDsh`.
|
|
813
|
+
*/
|
|
814
|
+
let openDshSession;
|
|
815
|
+
let compositionFailed = false;
|
|
816
|
+
/** How the in-process DSH kernel's composition ended: a failing startup open
|
|
817
|
+
* waits for it, so a failed composition (the root cause) is reported. */
|
|
818
|
+
let settleComposition = () => undefined;
|
|
819
|
+
const compositionSettled = new Promise(resolve => { settleComposition = resolve; });
|
|
591
820
|
const rawChannel = createChannel(ctx, startupSession, {
|
|
592
821
|
// The namespace this boot actually registered the settings section under
|
|
593
822
|
// (the Config owner's Loader id; custom ids are supported). Chat and the
|
|
594
823
|
// channel's own settings reads look the section up by it.
|
|
595
824
|
settingsNs: tuiSettingsNs,
|
|
825
|
+
onTaskComplete: preview => {
|
|
826
|
+
if (!shadow)
|
|
827
|
+
taskNotifier.notify(t('task-complete-notification'), preview);
|
|
828
|
+
},
|
|
829
|
+
settingsService: tuiSettings,
|
|
596
830
|
// The backend reports its model with its first turn (`system/init`);
|
|
597
831
|
// until then the status line names the backend.
|
|
598
|
-
model
|
|
832
|
+
get model() { return rowFacts.route?.model ?? (backendStart !== undefined ? backendStart.label : displayRoute.model); },
|
|
599
833
|
...(backendStart === undefined || backendPermissions === undefined ? {} : {
|
|
600
834
|
backendLabel: backendStart.label,
|
|
601
835
|
openSession: backendStart.open,
|
|
@@ -605,6 +839,17 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
605
839
|
sessionPrefs: backendStart.sessionPrefs,
|
|
606
840
|
initialHistory: backendStart.initialHistory,
|
|
607
841
|
resumeCommand: backendStart.resumeCommand,
|
|
842
|
+
...(backendStartup === undefined ? {} : { startup: backendStartup }),
|
|
843
|
+
}),
|
|
844
|
+
// The in-process DSH kernel: a DSH placeholder until the row's session.
|
|
845
|
+
...(dshStartup === undefined ? {} : {
|
|
846
|
+
backendLabel: backendLabel('dsh'),
|
|
847
|
+
startup: dshStartup,
|
|
848
|
+
openSession: async (target) => {
|
|
849
|
+
if (target.kind !== 'create' || openDshSession === undefined)
|
|
850
|
+
throw new Error(t('new-session-unavailable'));
|
|
851
|
+
return await openDshSession(target.cwd);
|
|
852
|
+
},
|
|
608
853
|
}),
|
|
609
854
|
// The activity projection only pushes on change; read the current value as
|
|
610
855
|
// soon as this session binds so a resumed or reattached session renders its
|
|
@@ -619,6 +864,8 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
619
864
|
// change, so a resumed session reads one baseline at bind time.
|
|
620
865
|
contextPressure: contextOccupancyStore,
|
|
621
866
|
seedContextOccupancy: session => contextOccupancyStore.seed(session),
|
|
867
|
+
team: teamRosterStore,
|
|
868
|
+
seedTeam: session => teamRosterStore.seed(session),
|
|
622
869
|
// A RESUMED session keeps its persisted header cwd (issue #96 review):
|
|
623
870
|
// pre-upgrade sessions recorded the launch directory, and re-resolving
|
|
624
871
|
// from the current launch directory would split @ expansion / file
|
|
@@ -626,17 +873,19 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
626
873
|
// sessions record sessionCwd at creation, so both agree there.
|
|
627
874
|
// A resumed backend session runs where it was recorded.
|
|
628
875
|
cwd: backendStart?.session.cwd ?? agent?.session.header.cwd ?? sessionCwd,
|
|
629
|
-
provider
|
|
876
|
+
get provider() { return rowFacts.route?.provider ?? (backendStart !== undefined ? backendStart.backendId : displayRoute.provider); },
|
|
630
877
|
// Raw cordis.yml route (undefined when unset): the channel's
|
|
631
878
|
// new-session path re-resolves prefs against these, and resume passes
|
|
632
879
|
// only explicit values so the target session's own record wins.
|
|
633
|
-
configuredModel: config.model,
|
|
634
|
-
configuredProvider: config.provider,
|
|
880
|
+
get configuredModel() { return rowFacts.config === undefined ? config.model : rowFacts.config.model; },
|
|
881
|
+
get configuredProvider() { return rowFacts.config === undefined ? config.provider : rowFacts.config.provider; },
|
|
635
882
|
// Raw cordis.yml `lang` / `activityFrames`: /reload must not override a
|
|
636
883
|
// static deployment choice with the persisted preference.
|
|
637
884
|
configuredLang: config.lang,
|
|
638
885
|
configuredActivityFrames: config.activityFrames,
|
|
639
|
-
|
|
886
|
+
// Read when the DSH extensions attach (the adoption), after the row's
|
|
887
|
+
// facts are in; the display fields below are fixed at the first frame.
|
|
888
|
+
get effort() { return rowFacts.config === undefined ? config.effort : rowFacts.config.effort; },
|
|
640
889
|
activity: config.activity,
|
|
641
890
|
// Explicit cordis.yml value (static deployment choice) wins over the
|
|
642
891
|
// runtime `/activity` preference, which wins over the default.
|
|
@@ -645,14 +894,17 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
645
894
|
contextBar: config.contextBar,
|
|
646
895
|
// Same precedence for the agent preset: cordis.yml `preset` over the
|
|
647
896
|
// persisted `/preset` choice; undefined adopts the roster default.
|
|
648
|
-
configuredPreset: config.preset,
|
|
897
|
+
get configuredPreset() { return rowFacts.config === undefined ? config.preset : rowFacts.config.preset; },
|
|
649
898
|
agentPreset,
|
|
650
899
|
// Shift+Tab session-mode cycle (undefined → the built-in default/
|
|
651
900
|
// plan/full cycle in sessionModes.ts).
|
|
652
|
-
modes: config.modes,
|
|
901
|
+
get modes() { return rowFacts.config === undefined ? config.modes : rowFacts.config.modes; },
|
|
653
902
|
// Edit/Write diff presentation (schema default 'auto'); the /settings
|
|
654
903
|
// screen edits this key live through the dsh-tui namespace.
|
|
655
904
|
diffLayout: config.diffLayout,
|
|
905
|
+
// Upstream auto-retry seeding (cordis.yml upstreamRetry, default on):
|
|
906
|
+
// only an explicit opt-out disables it.
|
|
907
|
+
get upstreamRetry() { return rowFacts.config === undefined ? config.upstreamRetry : rowFacts.config.upstreamRetry; },
|
|
656
908
|
thinkingFold: config.thinkingFold,
|
|
657
909
|
jobGroupFold: config.jobGroupFold,
|
|
658
910
|
toolBackground: config.toolBackground,
|
|
@@ -674,6 +926,7 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
674
926
|
// 的改动由 applyBrand 实时接上(branding.ts 负责解析)。
|
|
675
927
|
brand: config.brand,
|
|
676
928
|
});
|
|
929
|
+
backendStartupAdopted = backendStartup !== undefined;
|
|
677
930
|
// Register the live Channel for the adapter Kernel. The Channel driver
|
|
678
931
|
// resolves it lazily from the composition root, so this can be called after
|
|
679
932
|
// the plugin-host Kernel started without requiring a re-mount.
|
|
@@ -682,9 +935,9 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
682
935
|
// child activation context.
|
|
683
936
|
const unregisterTuiChannel = registerTuiChannel(compositionRoot(ctx), rawChannel);
|
|
684
937
|
ctx.effect(() => () => { unregisterTuiChannel(); });
|
|
685
|
-
const pluginHost = ctx.get('tuiPluginHost');
|
|
686
938
|
const adapterRuntime = adapterRuntimeFor(ctx);
|
|
687
|
-
|
|
939
|
+
// Looked up per probe: the plugin-host row can compose after the mount.
|
|
940
|
+
const uiMount = mountChannelUi(ctx, rawChannel, ctx.get('tuiPluginHost'), adapterRuntime.mode, () => ctx.get('tuiPluginHost'));
|
|
688
941
|
const channel = uiMount.channel;
|
|
689
942
|
bindChannelCommands(rawChannel, channel);
|
|
690
943
|
// last-run.json for the launcher's crash retry (see writeLastRunRecord):
|
|
@@ -692,6 +945,8 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
692
945
|
// at that point. The kernel-switch branch skips the refresh because the
|
|
693
946
|
// replacement writes its own record when it boots.
|
|
694
947
|
const bootAttemptId = `${process.pid.toString(36)}-${Date.now().toString(36)}`;
|
|
948
|
+
/** None before DSH has composed (the in-process DSH kernel) or off DSH. */
|
|
949
|
+
const liveDshAgent = () => dshAgentOn(ctx, channel.agentId);
|
|
695
950
|
const refreshLastRunRecord = () => {
|
|
696
951
|
// An observational composition (replay/embedding) is not "the instance the
|
|
697
952
|
// user ran last" — it must not overwrite the interactive record.
|
|
@@ -701,7 +956,7 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
701
956
|
? backendStart.persisted(channel.agentId, channel.rows)
|
|
702
957
|
: isExitResumable({
|
|
703
958
|
pendingCount: channel.pending.length,
|
|
704
|
-
liveAgent:
|
|
959
|
+
liveAgent: liveDshAgent(),
|
|
705
960
|
startupAgent: agent,
|
|
706
961
|
});
|
|
707
962
|
writeLastRunRecord({
|
|
@@ -725,6 +980,13 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
725
980
|
// lands once the channel can actually show it.
|
|
726
981
|
if (backendFallbackNotice !== undefined)
|
|
727
982
|
notifyChannel(backendFallbackNotice, { color: 'warning' });
|
|
983
|
+
// The entry could not use the installed dsh and fell back (this process,
|
|
984
|
+
// or the `dsh --profile` it handed the launch to): say why, once.
|
|
985
|
+
const hostNotice = process.env[HOST_NOTICE_ENV];
|
|
986
|
+
delete process.env[HOST_NOTICE_ENV];
|
|
987
|
+
if (hostNotice !== undefined && hostNotice !== '') {
|
|
988
|
+
notifyChannel(t('host-dsh-unavailable', { reason: hostNotice }), { color: 'warning', timeoutMs: 10000 });
|
|
989
|
+
}
|
|
728
990
|
const submitChannel = text => {
|
|
729
991
|
if (!shadow)
|
|
730
992
|
channel.submit(text);
|
|
@@ -744,7 +1006,9 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
744
1006
|
applySidePanelSplitEnabled(config.sidePanel?.splitEnabled);
|
|
745
1007
|
applySidePanelOpen(config.sidePanel?.open);
|
|
746
1008
|
applySidePanelRatio(config.sidePanel?.ratio);
|
|
747
|
-
|
|
1009
|
+
// Registered ids survive: on the profile path a third-party row may have
|
|
1010
|
+
// registered its panel before this row applied (./panels.ts).
|
|
1011
|
+
applyConfiguredSidePanelPanels(config.sidePanel?.panels);
|
|
748
1012
|
applyCompanionSkin(config.companion?.skin);
|
|
749
1013
|
applyBtwContextTurns(config.btw?.contextTurns);
|
|
750
1014
|
applyBtwContextBudget(config.btw?.contextBudget);
|
|
@@ -759,14 +1023,16 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
759
1023
|
// Without the extensions row (tuiToast absent) plugin toasts are dropped
|
|
760
1024
|
// by the runtime itself — same soft-degrade contract as the other seams.
|
|
761
1025
|
// Delivery uses the same owner-bound UI capability as all renderer actions.
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
1026
|
+
const bindToastSink = () => {
|
|
1027
|
+
getHostToastStore(ctx.get('tuiToast'))?.setSink(delivery => {
|
|
1028
|
+
notifyChannel(delivery.text, { color: delivery.color, timeoutMs: delivery.timeoutMs });
|
|
1029
|
+
});
|
|
1030
|
+
};
|
|
1031
|
+
bindToastSink();
|
|
1032
|
+
if (questionSeams?.registration.kind === 'waterfall') {
|
|
767
1033
|
// Ownership follows the mutable channel; registration cleanup belongs to
|
|
768
1034
|
// this Cordis fiber.
|
|
769
|
-
|
|
1035
|
+
questionSeams.registration.register(channel);
|
|
770
1036
|
}
|
|
771
1037
|
// Fullscreen layout decision: the settings user layer (edited through the
|
|
772
1038
|
// /settings screen) overrides cordis.yml when set. The settings injection
|
|
@@ -777,6 +1043,11 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
777
1043
|
// (swapping layouts requires re-mounting the whole tree).
|
|
778
1044
|
let bootedFullscreen = config.fullscreen === true;
|
|
779
1045
|
let bootedTerminalImages = lastBootedTerminalImages ?? config.terminalImages ?? true;
|
|
1046
|
+
// The launchpad boot decision (settings `launchpad`): same
|
|
1047
|
+
// restart-to-apply pattern as fullscreen — resolved before the tree mounts
|
|
1048
|
+
// from the settings user layer over cordis.yml; a mid-session edit is for
|
|
1049
|
+
// the next boot, the running session keeps what it booted with.
|
|
1050
|
+
let bootedLaunchpad = config.launchpad !== false;
|
|
780
1051
|
let rendererSettingsFrozen = false;
|
|
781
1052
|
const terminalImagesDisabledByEnv = isEnvTruthy(process.env.DSH_TUI_DISABLE_TERMINAL_IMAGES);
|
|
782
1053
|
// The settings service may come up AFTER this plugin's apply: the cordis
|
|
@@ -791,10 +1062,7 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
791
1062
|
resolveSettingsReady = () => resolve();
|
|
792
1063
|
setTimeout(resolve, 300);
|
|
793
1064
|
});
|
|
794
|
-
|
|
795
|
-
// volatile Config fields instead; both paths apply edits without remounting.
|
|
796
|
-
ctx.inject(['settings'], (settingsCtx) => {
|
|
797
|
-
// Loader targets the Config owner's fiber, not the injected child fiber.
|
|
1065
|
+
((settingsCtx) => {
|
|
798
1066
|
const scope = createSettingsScope(configOwner, settingsCtx.settings, tuiSettingsNs, Schema.object({
|
|
799
1067
|
diffLayout: Schema.union(['auto', 'split', 'unified']).default('auto'),
|
|
800
1068
|
thinkingFold: Schema.union(['preview', 'full']).default('preview'),
|
|
@@ -822,6 +1090,8 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
822
1090
|
expandEditor: Schema.boolean(),
|
|
823
1091
|
// Same no-default rule: applyDisplay resolves `?? config.smoothStreaming ?? true`.
|
|
824
1092
|
smoothStreaming: Schema.boolean(),
|
|
1093
|
+
taskNotifications: Schema.union(['off', 'unfocused', 'always']),
|
|
1094
|
+
notificationMethod: Schema.union(['auto', 'osc9', 'bel']),
|
|
825
1095
|
// Same no-default rule: applyDisplay resolves `?? config.mermaidDiagrams ?? true`.
|
|
826
1096
|
mermaidDiagrams: Schema.boolean(),
|
|
827
1097
|
// Code-frame shape; unset keeps the light rail frame.
|
|
@@ -913,6 +1183,10 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
913
1183
|
// Same no-default rule: unset keeps cordis.yml's `fullscreen`
|
|
914
1184
|
// decisive; set overrides it from the next boot on.
|
|
915
1185
|
fullscreen: Schema.boolean(),
|
|
1186
|
+
// Same no-default rule as fullscreen: the launchpad boot gate reads
|
|
1187
|
+
// `value.launchpad ?? config.launchpad ?? true`, so cordis.yml stays
|
|
1188
|
+
// decisive while the user layer is unset.
|
|
1189
|
+
launchpad: Schema.boolean(),
|
|
916
1190
|
// Unset inherits cordis.yml; a saved choice takes effect after restart.
|
|
917
1191
|
terminalImages: Schema.boolean(),
|
|
918
1192
|
// Built-in action-shortcut overrides, one optional combo string per
|
|
@@ -969,8 +1243,14 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
969
1243
|
if (typeof value.fullscreen === 'boolean') {
|
|
970
1244
|
bootedFullscreen = value.fullscreen;
|
|
971
1245
|
}
|
|
1246
|
+
if (typeof value.launchpad === 'boolean') {
|
|
1247
|
+
bootedLaunchpad = value.launchpad;
|
|
1248
|
+
}
|
|
972
1249
|
bootedTerminalImages = lastBootedTerminalImages ?? value.terminalImages ?? config.terminalImages ?? true;
|
|
973
1250
|
};
|
|
1251
|
+
/** False only during the boot application below: a registered panel absent
|
|
1252
|
+
* from its CSV is not a user removal (./panels.ts). */
|
|
1253
|
+
let settingsEditsLive = true;
|
|
974
1254
|
// The /settings language field writes `lang` through the settings
|
|
975
1255
|
// service (user layer): apply it live and mirror it to lang.json so
|
|
976
1256
|
// the /lang command and next-boot resolution agree. DSH_TUI_LANG
|
|
@@ -1002,6 +1282,7 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1002
1282
|
channel.setPromptSessionLabel(value.promptSessionLabel ?? config.promptSessionLabel ?? false);
|
|
1003
1283
|
channel.setExpandEditor(value.expandEditor ?? config.expandEditor ?? true);
|
|
1004
1284
|
channel.setSmoothStreaming(value.smoothStreaming ?? config.smoothStreaming ?? true);
|
|
1285
|
+
taskNotifier.configure(value.taskNotifications ?? config.taskNotifications, value.notificationMethod ?? config.notificationMethod);
|
|
1005
1286
|
applyMermaidDiagrams(value.mermaidDiagrams ?? config.mermaidDiagrams);
|
|
1006
1287
|
applyCodeFrameStyle(value.codeFrameStyle ?? config.codeFrameStyle);
|
|
1007
1288
|
applyMathRendering(resolveMathRendering(value, config));
|
|
@@ -1016,7 +1297,12 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1016
1297
|
applySidePanelSplitEnabled(value.sidePanel?.splitEnabled ?? config.sidePanel?.splitEnabled);
|
|
1017
1298
|
applySidePanelOpen(value.sidePanel?.open ?? config.sidePanel?.open);
|
|
1018
1299
|
applySidePanelRatio(value.sidePanel?.ratio ?? config.sidePanel?.ratio);
|
|
1019
|
-
|
|
1300
|
+
// A registered panel missing from a live edit was unticked in /settings.
|
|
1301
|
+
const panelsCsv = value.sidePanel?.panels ?? config.sidePanel?.panels;
|
|
1302
|
+
if (settingsEditsLive)
|
|
1303
|
+
applySidePanelPanelsFromSettings(panelsCsv);
|
|
1304
|
+
else
|
|
1305
|
+
applyConfiguredSidePanelPanels(panelsCsv);
|
|
1020
1306
|
applyCompanionSkin(value.companion?.skin ?? config.companion?.skin);
|
|
1021
1307
|
applyBtwContextTurns(value.btw?.contextTurns ?? config.btw?.contextTurns);
|
|
1022
1308
|
applyBtwContextBudget(value.btw?.contextBudget ?? config.btw?.contextBudget);
|
|
@@ -1090,8 +1376,11 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1090
1376
|
notifyChannel(t('settings-fullscreen-migrated'), { color: 'warning' });
|
|
1091
1377
|
}
|
|
1092
1378
|
const { fullscreen: staleFullscreen, ...migratedSettings } = bootSettings;
|
|
1379
|
+
settingsEditsLive = false;
|
|
1093
1380
|
apply(fullscreenMigration === 'unset' ? migratedSettings : bootSettings);
|
|
1381
|
+
settingsEditsLive = true;
|
|
1094
1382
|
let lastTerminalImages = bootSettings.terminalImages ?? config.terminalImages ?? true;
|
|
1383
|
+
let lastLaunchpad = bootSettings.launchpad ?? config.launchpad ?? true;
|
|
1095
1384
|
settingsCtx.effect(() => scope.watch(next => {
|
|
1096
1385
|
apply(next);
|
|
1097
1386
|
if (typeof next.fullscreen === 'boolean' && next.fullscreen !== bootedFullscreen) {
|
|
@@ -1102,9 +1391,14 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1102
1391
|
channel.notify(t('settings-terminal-images-restart'), { color: 'warning' });
|
|
1103
1392
|
}
|
|
1104
1393
|
lastTerminalImages = terminalImages;
|
|
1394
|
+
const launchpad = next.launchpad ?? config.launchpad ?? true;
|
|
1395
|
+
if (launchpad !== lastLaunchpad && launchpad !== bootedLaunchpad) {
|
|
1396
|
+
notifyChannel(t('settings-launchpad-restart'), { color: 'warning' });
|
|
1397
|
+
}
|
|
1398
|
+
lastLaunchpad = launchpad;
|
|
1105
1399
|
}));
|
|
1106
1400
|
resolveSettingsReady?.();
|
|
1107
|
-
});
|
|
1401
|
+
})({ settings: tuiSettings, effect: ctx.effect.bind(ctx) });
|
|
1108
1402
|
// The /settings screen's own section: the dsh-tui namespace comes from
|
|
1109
1403
|
// the settings registration above, and the declared selects write `lang`
|
|
1110
1404
|
// and `diffLayout` back through the settings service's revision-fenced
|
|
@@ -1147,9 +1441,12 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1147
1441
|
// left this registration silently skipped and /settings read-only.
|
|
1148
1442
|
// channel.ts reads through the same fallback, so both sides meet in the
|
|
1149
1443
|
// same registry either way.
|
|
1444
|
+
// The in-process DSH kernel registers into the local host first and moves
|
|
1445
|
+
// the section to the composition's service once it exists.
|
|
1446
|
+
let rehomeSettingsSection = () => undefined;
|
|
1150
1447
|
{
|
|
1151
|
-
|
|
1152
|
-
const
|
|
1448
|
+
let settingsSections = getHostSettingsSections(ctx.get('tuiSettingsSections')) ?? getLocalSettingsSectionsHost(ctx);
|
|
1449
|
+
const tuiSection = {
|
|
1153
1450
|
ns: tuiSettingsNs,
|
|
1154
1451
|
title: 'dsh-tui',
|
|
1155
1452
|
groups: [...SETTING_GROUPS],
|
|
@@ -1258,6 +1555,14 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1258
1555
|
return String(typeof value === 'boolean' ? value : config.mermaidDiagrams !== false);
|
|
1259
1556
|
},
|
|
1260
1557
|
},
|
|
1558
|
+
{
|
|
1559
|
+
...settingField('taskNotifications'),
|
|
1560
|
+
format: value => String(value ?? config.taskNotifications ?? 'unfocused'),
|
|
1561
|
+
},
|
|
1562
|
+
{
|
|
1563
|
+
...settingField('notificationMethod'),
|
|
1564
|
+
format: value => String(value ?? config.notificationMethod ?? 'auto'),
|
|
1565
|
+
},
|
|
1261
1566
|
{
|
|
1262
1567
|
...settingField('codeFrameStyle'),
|
|
1263
1568
|
},
|
|
@@ -1284,10 +1589,10 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1284
1589
|
...settingField('effortDefault'),
|
|
1285
1590
|
format(value) {
|
|
1286
1591
|
// Unset in settings.yaml: show what a boot would actually start
|
|
1287
|
-
// on (the
|
|
1592
|
+
// on (the persisted /effort choice → the cordis effort default)
|
|
1288
1593
|
// instead of a misleading blank.
|
|
1289
1594
|
if (value === undefined || value === null || value === 'auto') {
|
|
1290
|
-
return config.effort ??
|
|
1595
|
+
return readEffortPref() ?? config.effort ?? 'auto';
|
|
1291
1596
|
}
|
|
1292
1597
|
return String(value);
|
|
1293
1598
|
},
|
|
@@ -1440,6 +1745,14 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1440
1745
|
return { kind: 'set', value: budget };
|
|
1441
1746
|
},
|
|
1442
1747
|
},
|
|
1748
|
+
{
|
|
1749
|
+
...settingField('launchpad'),
|
|
1750
|
+
format(value) {
|
|
1751
|
+
// Unset in settings.yaml: the effective default is on — same
|
|
1752
|
+
// rule as expandEditor's field.
|
|
1753
|
+
return String(typeof value === 'boolean' ? value : config.launchpad !== false);
|
|
1754
|
+
},
|
|
1755
|
+
},
|
|
1443
1756
|
{
|
|
1444
1757
|
...settingField('companion.skin'),
|
|
1445
1758
|
},
|
|
@@ -1471,8 +1784,17 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1471
1784
|
...settingField('minimal'),
|
|
1472
1785
|
},
|
|
1473
1786
|
],
|
|
1474
|
-
}
|
|
1475
|
-
|
|
1787
|
+
};
|
|
1788
|
+
let unregister = settingsSections.register(tuiSection);
|
|
1789
|
+
ctx.effect(() => () => { unregister(); });
|
|
1790
|
+
rehomeSettingsSection = () => {
|
|
1791
|
+
const service = getHostSettingsSections(ctx.get('tuiSettingsSections'));
|
|
1792
|
+
if (service === undefined || service === settingsSections)
|
|
1793
|
+
return;
|
|
1794
|
+
unregister();
|
|
1795
|
+
settingsSections = service;
|
|
1796
|
+
unregister = service.register(tuiSection);
|
|
1797
|
+
};
|
|
1476
1798
|
}
|
|
1477
1799
|
// DSH approval seam: the permission layer asks ApprovalService.request(),
|
|
1478
1800
|
// which dispatches an `approval/request` waterfall. With no answerer the
|
|
@@ -1486,19 +1808,24 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1486
1808
|
bindApprovalStore(ctx, approvalStore);
|
|
1487
1809
|
// A non-DSH backend answers its own permission prompts: the DSH answerer
|
|
1488
1810
|
// is not registered for it.
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1811
|
+
// The in-process DSH kernel installs it in `attachDsh`.
|
|
1812
|
+
const mountDshApprovals = (dshCtx) => {
|
|
1813
|
+
if (dshCtx.get('approval') !== undefined && backendStart === undefined) {
|
|
1814
|
+
dshCtx.on('approval/request', (req, next) => approvalStore.park(req).catch(() => next()));
|
|
1815
|
+
// Badge-flip push (P-4): React does not know the session log appended —
|
|
1816
|
+
// a source-badge verdict that only flips inside getSnapshot() surfaces
|
|
1817
|
+
// solely when something else re-renders. Feed the session firehose to
|
|
1818
|
+
// the store: it reacts only to tool/result (the sole verdict-flipping
|
|
1819
|
+
// event type), and its internal log-length memo skips appends from any
|
|
1820
|
+
// session other than the active ask's, so no agent filtering is needed
|
|
1821
|
+
// here. The firehose fires post-commit, after the event entered
|
|
1822
|
+
// session.events, so the recheck sees the settled result.
|
|
1823
|
+
dshCtx.on('session/event', (session, event) => approvalStore.noteSessionEvent(session.id, event));
|
|
1824
|
+
dshCtx.effect(() => () => approvalStore.settleAll('cancelled'));
|
|
1825
|
+
}
|
|
1826
|
+
};
|
|
1827
|
+
if (!dshInEntry)
|
|
1828
|
+
mountDshApprovals(ctx);
|
|
1502
1829
|
// The agent view reads parked ask ids for its "needs input" state.
|
|
1503
1830
|
rawChannel.bindApprovalStore(approvalStore);
|
|
1504
1831
|
// The panel source Chat renders: the backend's own prompts off DSH.
|
|
@@ -1518,9 +1845,25 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1518
1845
|
// Submit once the channel exists; delivery goes through the normal pending/inbox
|
|
1519
1846
|
// chain, so no special timing is needed. The parser separates startup flags
|
|
1520
1847
|
// from literal prompt text.
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1848
|
+
// The standalone entry has no `ctx.cmdlineArgs` (its argv is the app's); a
|
|
1849
|
+
// replacement keeps the arguments but the first process sent their prompt.
|
|
1850
|
+
const initialPrompt = launchPromptSent ? '' : initialPromptFromCmdlineArgs(cmdlineArgs ?? process.argv.slice(2));
|
|
1851
|
+
if (initialPrompt) {
|
|
1852
|
+
// A placeholder session refuses sends and nothing replays them after
|
|
1853
|
+
// adoption: hold the prompt until the channel is ready.
|
|
1854
|
+
if (channel.ready) {
|
|
1855
|
+
submitChannel(initialPrompt);
|
|
1856
|
+
}
|
|
1857
|
+
else {
|
|
1858
|
+
const unsubscribe = channel.subscribe(() => {
|
|
1859
|
+
if (!channel.ready)
|
|
1860
|
+
return;
|
|
1861
|
+
unsubscribe();
|
|
1862
|
+
submitChannel(initialPrompt);
|
|
1863
|
+
});
|
|
1864
|
+
ctx.effect(() => unsubscribe, 'dsh-tui initial prompt after startup');
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1524
1867
|
// Attach the stderr reporter to the live channel and flush anything a
|
|
1525
1868
|
// startup-spawned server produced while the channel didn't exist yet.
|
|
1526
1869
|
notifyStderr = (text, options) => notifyChannel(text, options);
|
|
@@ -1554,10 +1897,13 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1554
1897
|
// accepted; the exit funnel then respawns onto that kernel (a NEW session —
|
|
1555
1898
|
// no resume markers at all).
|
|
1556
1899
|
let backendSwitchRequested;
|
|
1900
|
+
// A signal or `ctx.appExit` via the entry's exit seam: the clean-exit branch
|
|
1901
|
+
// runs as for `/quit`, then ends the process by the signal / with the code.
|
|
1902
|
+
let processExitRequest;
|
|
1557
1903
|
// The profile this process was booted with (`dsh --profile <name>`); dsh
|
|
1558
1904
|
// exposes it nowhere else, and /update must update the installation the
|
|
1559
1905
|
// user is actually running, not a hard-coded one.
|
|
1560
|
-
const profile = resolveDshProfileName();
|
|
1906
|
+
const profile = resolveDshProfileName() ?? runtimeOptions.profile;
|
|
1561
1907
|
// Single exit funnel: `/exit` and double Ctrl+C land here, and so does
|
|
1562
1908
|
// the unmount triggered by a cordis context teardown — but the two must
|
|
1563
1909
|
// not share a fate (issue #12). Teardown only unmounts the UI; user exit
|
|
@@ -1565,7 +1911,11 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1565
1911
|
// update handoff or resume hint).
|
|
1566
1912
|
/** The session id for a restart or update handoff, or empty when the backend
|
|
1567
1913
|
* has not persisted it and the replacement must start fresh. */
|
|
1568
|
-
const handoffSessionId = () =>
|
|
1914
|
+
const handoffSessionId = () => {
|
|
1915
|
+
if (backendStart !== undefined)
|
|
1916
|
+
return backendStart.persisted(channel.agentId, channel.rows) ? channel.agentId : '';
|
|
1917
|
+
return isUnstoredFreshSession(liveDshAgent()?.session) ? '' : channel.agentId;
|
|
1918
|
+
};
|
|
1569
1919
|
const handoffHint = backendStart === undefined ? undefined : (sessionId) => backendStart.resumeCommand(sessionId);
|
|
1570
1920
|
const funnel = createExitFunnel({
|
|
1571
1921
|
onUserExit: error => {
|
|
@@ -1589,18 +1939,14 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1589
1939
|
// never CLEARS a marker, so a session the user still has cannot be
|
|
1590
1940
|
// dropped by a failure that happened before the first message landed.
|
|
1591
1941
|
writeResumeMarkers: () => {
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1942
|
+
writeCrashResumeMarkers({
|
|
1943
|
+
ctx,
|
|
1944
|
+
backendStart,
|
|
1945
|
+
channel,
|
|
1595
1946
|
startupAgent: agent,
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
}
|
|
1599
|
-
// Non-DSH sessions use their backend preference as the launcher marker.
|
|
1600
|
-
if (backendStart !== undefined && backendStart.persisted(channel.agentId, channel.rows))
|
|
1601
|
-
backendStart.sessionPrefs.setLastSession(channel.agentId);
|
|
1602
|
-
// So the launcher's retry reopens this session, not the boot-time one.
|
|
1603
|
-
refreshLastRunRecord();
|
|
1947
|
+
writeResumeTarget,
|
|
1948
|
+
refreshLastRunRecord,
|
|
1949
|
+
});
|
|
1604
1950
|
},
|
|
1605
1951
|
finish: crashLine => {
|
|
1606
1952
|
void finishExit(ctx, instance, bootedFullscreen, undefined, crashLine, () => disposeRootAndExit(ctx, 1));
|
|
@@ -1611,8 +1957,13 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1611
1957
|
if (updateRequested) {
|
|
1612
1958
|
try {
|
|
1613
1959
|
// Non-DSH sessions keep their marker in backend prefs, not DSH's `resume.txt`.
|
|
1614
|
-
if (backendStart === undefined)
|
|
1615
|
-
|
|
1960
|
+
if (backendStart === undefined) {
|
|
1961
|
+
const sessionId = handoffSessionId();
|
|
1962
|
+
if (sessionId === '')
|
|
1963
|
+
clearResumeTarget();
|
|
1964
|
+
else
|
|
1965
|
+
writeResumeTarget(sessionId);
|
|
1966
|
+
}
|
|
1616
1967
|
else if (backendStart.persisted(channel.agentId, channel.rows))
|
|
1617
1968
|
backendStart.sessionPrefs.setLastSession(channel.agentId);
|
|
1618
1969
|
}
|
|
@@ -1623,7 +1974,7 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1623
1974
|
const hintText = isStandaloneRuntime()
|
|
1624
1975
|
? t('update-standalone-starting')
|
|
1625
1976
|
: t('update-starting');
|
|
1626
|
-
void finishExit(ctx, instance, bootedFullscreen, hintText, undefined, () => runUpdate(ctx, profile, handoffSessionId(), updateTargetVersion, backendChoice, handoffHint));
|
|
1977
|
+
void finishExit(ctx, instance, bootedFullscreen, hintText, undefined, () => runUpdate(ctx, profile, handoffSessionId(), updateTargetVersion, backendChoice, handoffHint, exitSeam));
|
|
1627
1978
|
return;
|
|
1628
1979
|
}
|
|
1629
1980
|
// The kernel selector's switch: the same respawn machinery, no resume.
|
|
@@ -1633,30 +1984,41 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1633
1984
|
// inherited DSH_TUI_RESUME_SESSION marker from the replacement env.
|
|
1634
1985
|
// kernel.json was already written when the choice was accepted.
|
|
1635
1986
|
if (backendSwitchRequested !== undefined) {
|
|
1636
|
-
|
|
1987
|
+
// Captured: the closure below runs after this block, where the outer
|
|
1988
|
+
// `let` is no longer narrowed.
|
|
1989
|
+
const switchingTo = backendSwitchRequested;
|
|
1990
|
+
logRestartEvent('funnel: backend-switch branch entered', { backend: switchingTo });
|
|
1637
1991
|
// Fullscreen keeps the alternate screen and writes the "switching"
|
|
1638
1992
|
// notice into it until the replacement takes over (see handoffAck.ts);
|
|
1639
1993
|
// inline restores the main screen and writes the notice there.
|
|
1640
|
-
logRestartEvent(handoffEventTag('starting'), { backend:
|
|
1994
|
+
logRestartEvent(handoffEventTag('starting'), { backend: switchingTo });
|
|
1641
1995
|
const keepAlt = bootedFullscreen;
|
|
1642
|
-
void finishExit(ctx, instance, bootedFullscreen, formatHandoffNotice('starting', { name:
|
|
1643
|
-
backend:
|
|
1996
|
+
void finishExit(ctx, instance, bootedFullscreen, formatHandoffNotice('starting', { name: backendLabel(switchingTo), color: process.stdout.isTTY === true }), undefined, () => runRestart(ctx, profile, '', undefined, {
|
|
1997
|
+
backend: switchingTo,
|
|
1998
|
+
// The replacement's parent prints the same name this process just
|
|
1999
|
+
// put on screen (update.ts has no registry to look it up in).
|
|
2000
|
+
backendName: backendLabel(switchingTo),
|
|
1644
2001
|
...(keepAlt ? { handoffScreen: 'alt' } : {}),
|
|
1645
|
-
}), { keepAltScreen: keepAlt });
|
|
2002
|
+
}, exitSeam), { keepAltScreen: keepAlt });
|
|
1646
2003
|
return;
|
|
1647
2004
|
}
|
|
1648
2005
|
// `/restart`: same handoff as the update path, no installation step.
|
|
1649
|
-
//
|
|
1650
|
-
//
|
|
2006
|
+
// A fresh initialization-only session has no log to reopen. Restart
|
|
2007
|
+
// it fresh; saved sessions keep their identity across the handoff.
|
|
1651
2008
|
if (restartRequested) {
|
|
1652
2009
|
beginRestartAttempt(channel.agentId);
|
|
1653
2010
|
logRestartEvent('funnel: /restart branch entered');
|
|
1654
2011
|
try {
|
|
1655
|
-
if (backendStart === undefined)
|
|
1656
|
-
|
|
2012
|
+
if (backendStart === undefined) {
|
|
2013
|
+
const sessionId = handoffSessionId();
|
|
2014
|
+
if (sessionId === '')
|
|
2015
|
+
clearResumeTarget();
|
|
2016
|
+
else
|
|
2017
|
+
writeResumeTarget(sessionId);
|
|
2018
|
+
}
|
|
1657
2019
|
else if (backendStart.persisted(channel.agentId, channel.rows))
|
|
1658
2020
|
backendStart.sessionPrefs.setLastSession(channel.agentId);
|
|
1659
|
-
logRestartEvent('funnel: resume target
|
|
2021
|
+
logRestartEvent('funnel: resume target updated');
|
|
1660
2022
|
}
|
|
1661
2023
|
catch (error) {
|
|
1662
2024
|
// Resume persistence is best effort and must never block a restart.
|
|
@@ -1665,7 +2027,7 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1665
2027
|
});
|
|
1666
2028
|
}
|
|
1667
2029
|
refreshLastRunRecord();
|
|
1668
|
-
void finishExit(ctx, instance, bootedFullscreen, t('restart-starting'), undefined, () => runRestart(ctx, profile, handoffSessionId(), handoffHint, { kernel: backendChoice }));
|
|
2030
|
+
void finishExit(ctx, instance, bootedFullscreen, t('restart-starting'), undefined, () => runRestart(ctx, profile, handoffSessionId(), handoffHint, { kernel: backendChoice }, exitSeam));
|
|
1669
2031
|
return;
|
|
1670
2032
|
}
|
|
1671
2033
|
// Judge against the live session behind the channel (channel.agentId),
|
|
@@ -1689,7 +2051,7 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1689
2051
|
else {
|
|
1690
2052
|
const resumable = isExitResumable({
|
|
1691
2053
|
pendingCount: channel.pending.length,
|
|
1692
|
-
liveAgent:
|
|
2054
|
+
liveAgent: liveDshAgent(),
|
|
1693
2055
|
startupAgent: agent,
|
|
1694
2056
|
});
|
|
1695
2057
|
try {
|
|
@@ -1707,10 +2069,39 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1707
2069
|
}
|
|
1708
2070
|
// Same resumability as the markers above.
|
|
1709
2071
|
refreshLastRunRecord();
|
|
1710
|
-
|
|
2072
|
+
const request = processExitRequest;
|
|
2073
|
+
void finishExit(ctx, instance, bootedFullscreen, hint, undefined, () => {
|
|
2074
|
+
if (request === undefined)
|
|
2075
|
+
disposeRootAndExit(ctx, 0);
|
|
2076
|
+
else if (request.kind === 'code')
|
|
2077
|
+
disposeRootAndExit(ctx, request.code);
|
|
2078
|
+
// A signal: the process dies by it once the root is down (bounded
|
|
2079
|
+
// as any dispose; the stalled case dies by it too).
|
|
2080
|
+
else
|
|
2081
|
+
disposeRootAndThen(ctx, () => { dieBySignal(request.signal); }, () => { dieBySignal(request.signal); });
|
|
2082
|
+
});
|
|
1711
2083
|
},
|
|
1712
2084
|
});
|
|
1713
2085
|
const handleExit = funnel.handleExit;
|
|
2086
|
+
const exitSeam = runtimeOptions.exitSeam;
|
|
2087
|
+
const requestProcessExit = (request) => {
|
|
2088
|
+
// A handoff is under way: no backstop, which could kill the supervisor of
|
|
2089
|
+
// a replacement already up; superviseReplacement takes the seam over.
|
|
2090
|
+
if (updateRequested || restartRequested || backendSwitchRequested !== undefined)
|
|
2091
|
+
return 'supervising';
|
|
2092
|
+
// A `/quit` or a crash is already ending this process.
|
|
2093
|
+
if (exited)
|
|
2094
|
+
return 'pending';
|
|
2095
|
+
processExitRequest = request;
|
|
2096
|
+
logRestartEvent('funnel: process exit requested', request.kind === 'signal' ? { signal: request.signal } : { code: request.code });
|
|
2097
|
+
if (handleExit())
|
|
2098
|
+
return 'exiting';
|
|
2099
|
+
// The tree is being torn down: the funnel cannot own the process.
|
|
2100
|
+
processExitRequest = undefined;
|
|
2101
|
+
return 'refused';
|
|
2102
|
+
};
|
|
2103
|
+
if (exitSeam !== undefined)
|
|
2104
|
+
exitSeam.request = requestProcessExit;
|
|
1714
2105
|
/** The launchpad kernel selector's accept path (the Chat side passes the
|
|
1715
2106
|
* chosen kernel through onSwitchBackend — the prop wiring lands together
|
|
1716
2107
|
* with the selector's Chat rendering). Persists the choice BEFORE the
|
|
@@ -1724,7 +2115,7 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1724
2115
|
backendSwitchRequested = backend;
|
|
1725
2116
|
writeKernelPrefs({ backend });
|
|
1726
2117
|
logRestartEvent('command: backend switch accepted', { backend });
|
|
1727
|
-
notifyChannel(t('kernel-switch-restarting', { name:
|
|
2118
|
+
notifyChannel(t('kernel-switch-restarting', { name: backendLabel(backend) }));
|
|
1728
2119
|
handleExit();
|
|
1729
2120
|
};
|
|
1730
2121
|
/** /channel changed the active channel's connection. The running CLI child
|
|
@@ -1818,18 +2209,21 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1818
2209
|
* and opens straight into its conversation.
|
|
1819
2210
|
*/
|
|
1820
2211
|
const dshBoot = backendStart === undefined;
|
|
1821
|
-
const noResume = isLandingLaunch({ launchSessionId, initialPrompt });
|
|
2212
|
+
const noResume = isLandingLaunch({ launchSessionId: bootSessionId, initialPrompt });
|
|
1822
2213
|
const openHomeOnBoot = dshBoot && !homeSeen && noResume && requestedWorkspace === undefined;
|
|
1823
2214
|
/**
|
|
1824
2215
|
* The launchpad is NOT one-shot the way the workspace home is: every
|
|
1825
2216
|
* ordinary launch starts on it, because it is where the first sentence gets
|
|
1826
2217
|
* typed rather than a tutorial that retires itself — on every backend: a
|
|
1827
2218
|
* remembered claude kernel lands here exactly like a dsh one (the `dshBoot`
|
|
1828
|
-
* gate below is deliberately absent). `
|
|
1829
|
-
*
|
|
1830
|
-
*
|
|
2219
|
+
* gate below is deliberately absent). The /settings `launchpad` toggle
|
|
2220
|
+
* (settings user layer over cordis.yml, resolved into `bootedLaunchpad`
|
|
2221
|
+
* above) is the persistent off switch — off boots straight into the chat
|
|
2222
|
+
* page. `DSH_TUI_NO_LAUNCHPAD=1` remains the env escape hatch (an
|
|
2223
|
+
* automation that wants the old blank conversation and no dialog in front
|
|
2224
|
+
* of it).
|
|
1831
2225
|
*/
|
|
1832
|
-
const launchpadOnBoot = noResume && process.env.DSH_TUI_NO_LAUNCHPAD !== '1';
|
|
2226
|
+
const launchpadOnBoot = noResume && bootedLaunchpad && process.env.DSH_TUI_NO_LAUNCHPAD !== '1';
|
|
1833
2227
|
/**
|
|
1834
2228
|
* The first-run guide. Gated on its own preference (not on `homeSeen`): the
|
|
1835
2229
|
* two answer different questions, and an install that already knows its
|
|
@@ -1839,7 +2233,8 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1839
2233
|
// 品牌镜像初值(branding.ts):首帧渲染前铺好,避免 Claude 后端先画一屏
|
|
1840
2234
|
// 蓝再变橙。Chat 里的 effect 会在品牌解析变化时跟进更新这个镜像。
|
|
1841
2235
|
setActiveBrand(resolveBrand(config.brand, backendChoice));
|
|
1842
|
-
const
|
|
2236
|
+
const liveThemeHost = () => getHostThemes(ctx.get('tuiThemes'));
|
|
2237
|
+
const buildChat = () => React.createElement(Chat, {
|
|
1843
2238
|
channel,
|
|
1844
2239
|
renderScene: createChannelSceneOutlet(() => rawChannel.pluginScene),
|
|
1845
2240
|
questionStore,
|
|
@@ -1859,7 +2254,7 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1859
2254
|
// no longer runs a second activity tracker of its own.
|
|
1860
2255
|
activityStore,
|
|
1861
2256
|
extensionShortcuts: getHostShortcuts(ctx.get('tuiShortcuts')),
|
|
1862
|
-
themeHost,
|
|
2257
|
+
themeHost: liveThemeHost(),
|
|
1863
2258
|
// Full-screen surfaces inside Chat — the trajectory scene and the session
|
|
1864
2259
|
// browser — enter the alt screen themselves in inline mode; in fullscreen
|
|
1865
2260
|
// the tree is already wrapped below, so they must not nest.
|
|
@@ -1880,12 +2275,39 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1880
2275
|
onSwitchBackend: switchBackend,
|
|
1881
2276
|
// /channel: restart with a new session after the connection changed.
|
|
1882
2277
|
onRestartFreshSession: restartFreshSession,
|
|
2278
|
+
// The backend registry as the picker sees it: the UI takes data, not the
|
|
2279
|
+
// registry (P0). dsh first, then the manifest order — picker order. Entries
|
|
2280
|
+
// that registered without an admission are not offered (B-2).
|
|
2281
|
+
kernelEntries: kernelEntriesOf(listOfferedBackends()),
|
|
1883
2282
|
onProbeKernels: () => probeKernels(ctx, sessionCwd),
|
|
1884
|
-
// The kernel picker's SDK install wizard (
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
2283
|
+
// The kernel picker's SDK install wizard (a dim row's Enter): the surface of
|
|
2284
|
+
// whichever row the user picked, looked up by id — the manifest says what to
|
|
2285
|
+
// install and with which executor, this host holds the actions.
|
|
2286
|
+
onResolveSdkInstall: installSurfaceFor,
|
|
2287
|
+
// `/plugin install <spec>` (C-4): the write path. The wiring lives beside
|
|
2288
|
+
// the flow (`createPluginInstallEntry`) so a regression can drive the real
|
|
2289
|
+
// feed load, declaration read and profile-relative path resolution with a
|
|
2290
|
+
// fixture context. `undefined` ⇒ not an install form, or no service to
|
|
2291
|
+
// install through; the screen then runs the read-only report instead.
|
|
2292
|
+
onPluginInstall: createPluginInstallEntry(ctx, {
|
|
2293
|
+
ownVersion: installedTuiVersion(),
|
|
2294
|
+
profileName: profile ?? resolveDshProfileName(),
|
|
2295
|
+
}),
|
|
2296
|
+
// `/plugin remove|enable|disable` (C-5): the lifecycle write path. Wired the
|
|
2297
|
+
// same way and for the same reason — the entry is where the real profile, the
|
|
2298
|
+
// declaration read and the data directory meet, so a regression can drive
|
|
2299
|
+
// them with a fixture context. `undefined` ⇒ not one of those forms, or no
|
|
2300
|
+
// service to act through; the screen then runs the read-only report.
|
|
2301
|
+
onPluginManage: createPluginManageEntry(ctx, {
|
|
2302
|
+
profileName: profile ?? resolveDshProfileName(),
|
|
2303
|
+
}),
|
|
2304
|
+
// `/team [status] | on | off`: the Agent Teams bundle through the same
|
|
2305
|
+
// lifecycle flow (team-mode.ts).
|
|
2306
|
+
onTeamCommand: createTeamCommandEntry(ctx, {
|
|
2307
|
+
profileName: profile ?? resolveDshProfileName(),
|
|
2308
|
+
// The host reports `unknown` when it has no version: read the installed one.
|
|
2309
|
+
kernelVersion: entrySlot?.kernelVersion === 'unknown' ? undefined : entrySlot?.kernelVersion,
|
|
2310
|
+
}),
|
|
1889
2311
|
kernelPinned: backendPinned,
|
|
1890
2312
|
// Only a `dsh --profile <name>` launch has a profile installation for
|
|
1891
2313
|
// `/update` to act on; source checkouts and `--config` overlays get the
|
|
@@ -1957,17 +2379,32 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1957
2379
|
// the screen border). It must sit INSIDE AlternateScreen: the alt-screen
|
|
1958
2380
|
// box sizes itself to the real terminal rows, while PageMargin reports
|
|
1959
2381
|
// content-box dimensions to everything below it.
|
|
1960
|
-
const
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
2382
|
+
const buildTree = () => {
|
|
2383
|
+
const chat = buildChat();
|
|
2384
|
+
const marginChildren = bootedFullscreen
|
|
2385
|
+
? React.createElement(AlternateScreen, null, React.createElement(PageMargin, null, chat))
|
|
2386
|
+
: React.createElement(PageMargin, null, chat);
|
|
2387
|
+
return React.createElement(ThemeProvider, {
|
|
2388
|
+
themeHost: liveThemeHost(),
|
|
2389
|
+
children: marginChildren,
|
|
2390
|
+
});
|
|
2391
|
+
};
|
|
2392
|
+
const tree = buildTree();
|
|
1967
2393
|
// Kernel-switch replacement: send the ready ACK once the first frame after
|
|
1968
2394
|
// adoption is flushed. Does nothing on an ordinary boot.
|
|
1969
2395
|
armFirstFrameAck(process.stdout);
|
|
1970
|
-
|
|
2396
|
+
markBoot('render-start');
|
|
2397
|
+
// The entry's composition right after the mount freezes the event loop for
|
|
2398
|
+
// a while: it waits for the first frame to reach the terminal.
|
|
2399
|
+
const awaitsFirstFrame = entrySlot !== undefined;
|
|
2400
|
+
let frameFlushed = () => undefined;
|
|
2401
|
+
const firstFrame = awaitsFirstFrame ? new Promise(resolve => { frameFlushed = resolve; }) : undefined;
|
|
2402
|
+
instance = await render(tree, {
|
|
2403
|
+
exitOnCtrlC: false,
|
|
2404
|
+
terminalImages: bootedTerminalImages,
|
|
2405
|
+
...(awaitsFirstFrame ? { onFrame: () => { frameFlushed(); } } : {}),
|
|
2406
|
+
});
|
|
2407
|
+
markBoot('render-done');
|
|
1971
2408
|
const isRecompose = lastBootedFullscreen !== undefined;
|
|
1972
2409
|
lastBootedFullscreen = bootedFullscreen;
|
|
1973
2410
|
lastBootedTerminalImages = bootedTerminalImages;
|
|
@@ -1981,14 +2418,32 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
1981
2418
|
// External injection channel (dsh.nvim etc.): expose a per-session local
|
|
1982
2419
|
// socket that appends text into the prompt input and submits it. Optional
|
|
1983
2420
|
// integration — a bind failure degrades to "no channel" and never fails the
|
|
1984
|
-
// session.
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
2421
|
+
// session. It follows the bound session: a placeholder still opening (empty
|
|
2422
|
+
// id) binds nothing, and adoption or a session switch rebinds under the new
|
|
2423
|
+
// id and cwd, closing the previous socket and discovery record (a connected
|
|
2424
|
+
// client reconnects through servers.json). Closed on teardown so neither leaks.
|
|
2425
|
+
let injectChannel = null;
|
|
2426
|
+
let injectSessionId = '';
|
|
2427
|
+
const bindInjectChannel = () => {
|
|
2428
|
+
if (channel.agentId === injectSessionId)
|
|
2429
|
+
return;
|
|
2430
|
+
injectChannel?.close();
|
|
2431
|
+
injectChannel = null;
|
|
2432
|
+
injectSessionId = channel.agentId;
|
|
2433
|
+
if (injectSessionId === '')
|
|
2434
|
+
return;
|
|
2435
|
+
injectChannel = openInjectChannel(injectSessionId, channel.cwd, {
|
|
2436
|
+
append: (text) => injectControllerRef.current?.append(text),
|
|
2437
|
+
submit: () => injectControllerRef.current?.submit(),
|
|
2438
|
+
}, (message) => ctx.logger.warn(`dsh-tui: ${message}`));
|
|
2439
|
+
};
|
|
2440
|
+
bindInjectChannel();
|
|
2441
|
+
const unsubscribeInject = channel.subscribe(bindInjectChannel);
|
|
2442
|
+
ctx.effect(() => () => {
|
|
2443
|
+
unsubscribeInject();
|
|
2444
|
+
injectChannel?.close();
|
|
2445
|
+
injectChannel = null;
|
|
2446
|
+
}, 'dsh-tui inject channel');
|
|
1992
2447
|
// Cross-process session mounting: publish the sessions this process has
|
|
1993
2448
|
// mounted so another TUI (a different terminal process on the same machine)
|
|
1994
2449
|
// can see them as occupied and refuse to mount the same log. Two processes
|
|
@@ -2034,9 +2489,138 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
2034
2489
|
// the process, so a later fatal error falls back to Node's default crash
|
|
2035
2490
|
// instead of reaching a disposed ctx.
|
|
2036
2491
|
registerProcessGuardFatalSink(undefined);
|
|
2492
|
+
if (exitSeam?.request === requestProcessExit)
|
|
2493
|
+
exitSeam.request = undefined;
|
|
2037
2494
|
rawChannel.releaseContributions();
|
|
2038
2495
|
instance?.unmount();
|
|
2039
2496
|
});
|
|
2497
|
+
/** The first frame's write, then stdout drained (bounded: a renderer that
|
|
2498
|
+
* never draws must not hold the composition back). */
|
|
2499
|
+
const flushFirstFrame = async () => {
|
|
2500
|
+
await Promise.race([firstFrame ?? Promise.resolve(), new Promise(resolve => { setTimeout(resolve, 1000).unref(); })]);
|
|
2501
|
+
await new Promise(resolve => { process.stdout.write('', () => { resolve(); }); });
|
|
2502
|
+
};
|
|
2503
|
+
/**
|
|
2504
|
+
* Services composed after the mount: re-read what the screen took as values
|
|
2505
|
+
* at mount (Chat's stores, theme host, toast sink, /settings section).
|
|
2506
|
+
*/
|
|
2507
|
+
const refreshHostServices = () => {
|
|
2508
|
+
rehomeSettingsSection();
|
|
2509
|
+
bindToastSink();
|
|
2510
|
+
if (!exited)
|
|
2511
|
+
instance?.rerender(buildTree());
|
|
2512
|
+
};
|
|
2513
|
+
if (entrySlot !== undefined)
|
|
2514
|
+
entrySlot.firstFrameFlushed = flushFirstFrame;
|
|
2515
|
+
// The kernels without a dsh-tui row: no row to refresh the mounted screen,
|
|
2516
|
+
// so the slot does it.
|
|
2517
|
+
if (entrySlot !== undefined && !entrySlot.dsh)
|
|
2518
|
+
entrySlot.composeSucceeded = () => { refreshHostServices(); };
|
|
2519
|
+
if (entrySlot?.dsh === true) {
|
|
2520
|
+
entrySlot.composeSucceeded = () => { settleComposition(true); };
|
|
2521
|
+
entrySlot.composeFailed = (error, logPath) => {
|
|
2522
|
+
settleComposition(false);
|
|
2523
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
2524
|
+
logForDebugging(`dsh-tui: host composition failed (${reason})${logPath === undefined ? '' : ` — startup report: ${logPath}`}`);
|
|
2525
|
+
compositionFailed = true;
|
|
2526
|
+
openDshSession = undefined;
|
|
2527
|
+
// No DSH to open on: the failure row names the report and the ways out.
|
|
2528
|
+
const hint = logPath === undefined ? t('startup-compose-failed-hint') : t('startup-compose-failed-hint-log', { path: logPath });
|
|
2529
|
+
if (!dshStartupSettled) {
|
|
2530
|
+
settleDshStartup?.reject(new StartupOpenError(reason.split('\n')[0] ?? reason, hint, { cause: error }));
|
|
2531
|
+
return;
|
|
2532
|
+
}
|
|
2533
|
+
if (exited)
|
|
2534
|
+
return;
|
|
2535
|
+
// The session may already be adopted (the audit runs after the tree
|
|
2536
|
+
// settled): a row that stays, not a toast.
|
|
2537
|
+
try {
|
|
2538
|
+
channel.pushLocal(t('startup-open-failed', { backend: backendLabel('dsh'), err: reason.split('\n')[0] ?? reason }), [hint]);
|
|
2539
|
+
}
|
|
2540
|
+
catch {
|
|
2541
|
+
}
|
|
2542
|
+
};
|
|
2543
|
+
entrySlot.attachDsh = async (rowCtx, rowRuntimeConfig) => {
|
|
2544
|
+
const dshCtx = rowCtx;
|
|
2545
|
+
// The composition failed (its row is on screen), or the process is
|
|
2546
|
+
// leaving (the root dispose waited for the Loader, which started this row).
|
|
2547
|
+
if (compositionFailed || exited)
|
|
2548
|
+
return;
|
|
2549
|
+
try {
|
|
2550
|
+
const rowConfig = configValues(rowRuntimeConfig);
|
|
2551
|
+
rowFacts.config = rowConfig;
|
|
2552
|
+
await installPresets(dshCtx);
|
|
2553
|
+
const seams = mountDshQuestionSeams(dshCtx);
|
|
2554
|
+
if (seams.notice !== undefined)
|
|
2555
|
+
notifyChannel(seams.notice, { color: 'error' });
|
|
2556
|
+
if (seams.registration.kind === 'waterfall')
|
|
2557
|
+
seams.registration.register(channel);
|
|
2558
|
+
mountDshApprovals(dshCtx);
|
|
2559
|
+
refreshHostServices();
|
|
2560
|
+
const rowRoute = { provider: rowConfig.provider, model: rowConfig.model };
|
|
2561
|
+
// The same startup/deployment split the plugin's own boot makes (issue #67).
|
|
2562
|
+
const { startupRoute: rowStartupRoute, deploymentRoute: rowDeploymentRoute } = startupRoutes(dshCtx, rowRoute);
|
|
2563
|
+
const openOnRow = (sessionId, openMeta) => resolveAgent(dshCtx, sessionId, rowRoute, rowStartupRoute, rowDeploymentRoute, openMeta, rowConfig.preset);
|
|
2564
|
+
/** The opened agent is this screen's from now on; returns its route. */
|
|
2565
|
+
const takeOpened = async (opened) => {
|
|
2566
|
+
agent = opened.agent;
|
|
2567
|
+
await attachWorkspaceOwnership(dshCtx, opened.agent);
|
|
2568
|
+
rowFacts.route = opened.route ?? rowStartupRoute;
|
|
2569
|
+
return rowFacts.route;
|
|
2570
|
+
};
|
|
2571
|
+
// `/new` after a failed startup open: DSH's create path on this row.
|
|
2572
|
+
openDshSession = async (cwd) => {
|
|
2573
|
+
const created = await openOnRow(undefined, { cwd });
|
|
2574
|
+
await takeOpened(created);
|
|
2575
|
+
refreshLastRunRecord();
|
|
2576
|
+
return createDshSession(dshCtx, { agent: created.agent, handle: created.handle });
|
|
2577
|
+
};
|
|
2578
|
+
// The entry mounted on rebuilt defaults: the row's own `workspace` /
|
|
2579
|
+
// `cwd` decide the session directory, with `apply`'s precedence. A
|
|
2580
|
+
// provider-URI target resolves now that the providers exist.
|
|
2581
|
+
const rowWorkspace = rowConfig.workspace ?? requestedWorkspace;
|
|
2582
|
+
let startupMeta = { cwd: resolveSessionCwd(rowConfig.cwd) };
|
|
2583
|
+
if (rowWorkspace !== undefined) {
|
|
2584
|
+
if (initialWorkspace !== undefined && rowWorkspace === requestedWorkspace) {
|
|
2585
|
+
startupMeta = meta;
|
|
2586
|
+
}
|
|
2587
|
+
else {
|
|
2588
|
+
const workspaces = getHostWorkspaceRuntime(dshCtx.get('tuiWorkspaces'));
|
|
2589
|
+
const target = workspaces === undefined ? undefined : await workspaces.resolve(rowWorkspace);
|
|
2590
|
+
if (target === undefined)
|
|
2591
|
+
throw new Error(unsupportedWorkspaceTarget(rowWorkspace));
|
|
2592
|
+
startupMeta = { cwd: target.cwd };
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
// A revoked target (the safe-mode retry's warning path) cold-starts here too.
|
|
2596
|
+
const rowSessionId = resumeTarget.kind === 'revoked'
|
|
2597
|
+
? undefined
|
|
2598
|
+
: rowConfig.sessionId ?? resumeTargetFromArgv(cmdlineArgsOf(dshCtx) ?? process.argv.slice(2));
|
|
2599
|
+
const opened = await openOnRow(rowSessionId, startupMeta);
|
|
2600
|
+
const session = createDshSession(dshCtx, { agent: opened.agent, handle: opened.handle });
|
|
2601
|
+
// The composition failed while this opened: close the unadoptable session.
|
|
2602
|
+
if (compositionFailed) {
|
|
2603
|
+
await session.dispose().catch(() => undefined);
|
|
2604
|
+
return;
|
|
2605
|
+
}
|
|
2606
|
+
const route = await takeOpened(opened);
|
|
2607
|
+
settleDshStartup?.resolve({
|
|
2608
|
+
session,
|
|
2609
|
+
history: [],
|
|
2610
|
+
route,
|
|
2611
|
+
...(opened.agentPreset === undefined ? {} : { agentPreset: opened.agentPreset }),
|
|
2612
|
+
});
|
|
2613
|
+
refreshLastRunRecord();
|
|
2614
|
+
}
|
|
2615
|
+
catch (error) {
|
|
2616
|
+
logForDebugging(`dsh-tui: DSH session failed to open (${error instanceof Error ? error.message : String(error)})`);
|
|
2617
|
+
// A composition that fails reports itself (its row names the report).
|
|
2618
|
+
if (!await compositionSettled)
|
|
2619
|
+
return;
|
|
2620
|
+
settleDshStartup?.reject(error);
|
|
2621
|
+
}
|
|
2622
|
+
};
|
|
2623
|
+
}
|
|
2040
2624
|
// The TUI is the front door: when the user unmounts it (Ctrl+C), dispose
|
|
2041
2625
|
// the app tree and exit the process. The rejection handler covers
|
|
2042
2626
|
// error-driven unmounts — without it a rejected exitPromise became an
|
|
@@ -2044,6 +2628,9 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
2044
2628
|
// is swallowed by the funnel (issue #12).
|
|
2045
2629
|
void instance.waitUntilExit().then(handleExit, handleExit);
|
|
2046
2630
|
}
|
|
2631
|
+
function unsupportedWorkspaceTarget(target) {
|
|
2632
|
+
return `dsh-tui: unsupported or unavailable workspace target: ${target}`;
|
|
2633
|
+
}
|
|
2047
2634
|
/**
|
|
2048
2635
|
* Attach to an existing agent, resume a persisted session (`dsh-tui --resume`
|
|
2049
2636
|
* feeds the id through `config.sessionId`), or create a fresh one. Resume
|
|
@@ -2053,26 +2640,45 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
|
|
|
2053
2640
|
* without a session id.
|
|
2054
2641
|
*
|
|
2055
2642
|
* Preset composition (issue #8): a create resolves the requested preset
|
|
2056
|
-
* (
|
|
2057
|
-
* default) and mounts it in the factory's setup hook;
|
|
2058
|
-
* preset the session's own log records. Without the
|
|
2059
|
-
* as before presets existed.
|
|
2643
|
+
* (`DSH_TUI_PRESET` over the persisted `/preset` choice over the cordis.yml
|
|
2644
|
+
* default over the roster default) and mounts it in the factory's setup hook;
|
|
2645
|
+
* a resume re-mounts the preset the session's own log records. Without the
|
|
2646
|
+
* roster both paths behave as before presets existed.
|
|
2060
2647
|
*
|
|
2061
2648
|
* Model route (issues #14/#30/#67): a create adopts the caller's atomically
|
|
2062
2649
|
* resolved route (validated against the adapter catalog below); a resume
|
|
2063
|
-
*
|
|
2064
|
-
*
|
|
2650
|
+
* prefers the route the target session's own records carry and falls back to
|
|
2651
|
+
* a COMPLETE cordis.yml route — a provider-only pin must not half-override
|
|
2652
|
+
* the session's route, and the static route is only the deployment default.
|
|
2653
|
+
*/
|
|
2654
|
+
/**
|
|
2655
|
+
* The startup route and the deployment route a rejected preference falls back
|
|
2656
|
+
* to (issue #67): the persisted `/model` pick wins whole, else a complete
|
|
2657
|
+
* cordis.yml route, else Harness's agent-default-model selection — never the
|
|
2658
|
+
* lock the user's own pick replaced, never a half-pinned config.
|
|
2065
2659
|
*/
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2660
|
+
function startupRoutes(ctx, configuredRoute) {
|
|
2661
|
+
const selected = ctx.get('agentDefaultModel')?.currentSelection?.();
|
|
2662
|
+
const harnessDefault = typeof selected?.provider === 'string' && selected.provider.length > 0
|
|
2663
|
+
&& typeof selected.model === 'string' && selected.model.length > 0
|
|
2664
|
+
? { provider: selected.provider, model: selected.model }
|
|
2665
|
+
: undefined;
|
|
2666
|
+
return {
|
|
2667
|
+
startupRoute: resolveModelRoute(configuredRoute, readModelPref(), harnessDefault),
|
|
2668
|
+
deploymentRoute: resolveModelRoute(configuredRoute, undefined, harnessDefault),
|
|
2669
|
+
};
|
|
2670
|
+
}
|
|
2671
|
+
async function resolveAgent(ctx, requestedSessionId, configuredRoute, startupRoute, deploymentRoute, meta, configuredPreset) {
|
|
2672
|
+
// Deployment fallback (issue #67): a resumed session continues on the route
|
|
2673
|
+
// its own request/header records carry (issue #30) — the route it actually
|
|
2674
|
+
// ran is more specific than any static config — and only a session that
|
|
2675
|
+
// records none adopts the complete cordis.yml route. The recorded route is
|
|
2676
|
+
// ALSO fed back into agentOptions (not just the status line): a resume
|
|
2071
2677
|
// whose cordis.yml pins only `provider` would otherwise leave
|
|
2072
2678
|
// agentOptions.model undefined, which breaks the `{{model}}` persona
|
|
2073
2679
|
// variable for the resumed agent's own assembly and for every subagent it
|
|
2074
2680
|
// spawns (dsh-subagent inherits `parent.options.model`).
|
|
2075
|
-
const resumeRoute = explicitModelRoute(configuredRoute);
|
|
2681
|
+
const resumeRoute = explicitModelRoute(configuredRoute) ?? deploymentRoute;
|
|
2076
2682
|
if (requestedSessionId !== undefined) {
|
|
2077
2683
|
const resumeId = SessionId(requestedSessionId);
|
|
2078
2684
|
const existing = ctx.agents.get(resumeId);
|
|
@@ -2125,8 +2731,8 @@ async function resolveAgent(ctx, requestedSessionId, configuredRoute, startupRou
|
|
|
2125
2731
|
const composed = await composePreset(ctx, persisted);
|
|
2126
2732
|
const recorded = await resolvePersistedRoute(ctx, resumeId);
|
|
2127
2733
|
const resumeOptions = {
|
|
2128
|
-
provider:
|
|
2129
|
-
model:
|
|
2734
|
+
provider: recorded?.provider ?? resumeRoute?.provider,
|
|
2735
|
+
model: recorded?.model ?? resumeRoute?.model,
|
|
2130
2736
|
};
|
|
2131
2737
|
const resumed = await ctx.agents.resume({
|
|
2132
2738
|
resumeSessionId: resumeId,
|
|
@@ -2134,14 +2740,14 @@ async function resolveAgent(ctx, requestedSessionId, configuredRoute, startupRou
|
|
|
2134
2740
|
...(composed.setup === undefined ? {} : { setup: composed.setup }),
|
|
2135
2741
|
});
|
|
2136
2742
|
// Status-line route on resume: the route the session actually
|
|
2137
|
-
// continues on —
|
|
2138
|
-
//
|
|
2139
|
-
//
|
|
2743
|
+
// continues on — its own request/header record, else the complete
|
|
2744
|
+
// cordis.yml deployment route (a bare log that pins none falls back to
|
|
2745
|
+
// the startup resolution, best effort).
|
|
2140
2746
|
return {
|
|
2141
2747
|
agent: resumed.agent,
|
|
2142
2748
|
handle: resumed,
|
|
2143
2749
|
agentPreset: composed.agentPreset,
|
|
2144
|
-
route:
|
|
2750
|
+
route: recordedModelRoute(snapshotLiveSessionEvents(resumed.agent.session)) ?? resumeRoute,
|
|
2145
2751
|
};
|
|
2146
2752
|
}
|
|
2147
2753
|
catch (error) {
|
|
@@ -2165,18 +2771,24 @@ async function resolveAgent(ctx, requestedSessionId, configuredRoute, startupRou
|
|
|
2165
2771
|
}
|
|
2166
2772
|
}
|
|
2167
2773
|
const sessionId = SessionId(randomUUID());
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2774
|
+
// Fresh-session preset precedence (issue #8): the DSH_TUI_PRESET launch
|
|
2775
|
+
// instruction, then the persisted `/preset` choice (the standing user
|
|
2776
|
+
// default), then the cordis.yml value as the deployment default, then the
|
|
2777
|
+
// roster default. The pref is migrated only when it was the effective
|
|
2778
|
+
// source — an override must not rewrite the remembered id.
|
|
2779
|
+
const presetOverride = presetOverrideFromEnv();
|
|
2780
|
+
const presetPref = readPresetPref();
|
|
2781
|
+
const composed = await composePreset(ctx, presetOverride ?? presetPref ?? configuredPreset);
|
|
2782
|
+
if (presetOverride === undefined && !migratePresetPref(presetPref, composed.agentPreset)) {
|
|
2171
2783
|
ctx.logger.warn(`dsh-tui: resolved preset preference "${presetPref}" as "${composed.agentPreset}" but could not persist the migrated id`);
|
|
2172
2784
|
}
|
|
2173
2785
|
// Fresh-session route precedence (issues #14/#30/#67): resolved atomically
|
|
2174
|
-
// by the caller (
|
|
2786
|
+
// by the caller (the persisted `/model` choice > a complete cordis.yml route
|
|
2175
2787
|
// > the harness default), then validated against the adapter catalog — a
|
|
2176
|
-
// stale persisted choice falls back to the
|
|
2177
|
-
// of reaching the server as an unknown model name.
|
|
2788
|
+
// stale persisted choice falls back to the deployment route wholesale
|
|
2789
|
+
// instead of reaching the server as an unknown model name.
|
|
2178
2790
|
const llm = ctx.get('llm');
|
|
2179
|
-
const { route, rejected } = await validateModelRoute(llm, startupRoute);
|
|
2791
|
+
const { route, rejected } = await validateModelRoute(llm, startupRoute, deploymentRoute);
|
|
2180
2792
|
if (rejected !== undefined) {
|
|
2181
2793
|
ctx.logger.warn(`dsh-tui: model route ${rejected.provider}/${rejected.model} is not advertised by provider "${rejected.provider}"; falling back to ${route.provider}/${route.model}`);
|
|
2182
2794
|
}
|
|
@@ -2193,7 +2805,7 @@ async function resolveAgent(ctx, requestedSessionId, configuredRoute, startupRou
|
|
|
2193
2805
|
const bootReservation = bootReserved.ok ? bootReserved.reservation : undefined;
|
|
2194
2806
|
let created;
|
|
2195
2807
|
try {
|
|
2196
|
-
created = await ctx.agents
|
|
2808
|
+
created = await createFreshAgent(ctx, ctx.agents, {
|
|
2197
2809
|
sessionId,
|
|
2198
2810
|
meta: {
|
|
2199
2811
|
...meta,
|
|
@@ -2276,6 +2888,31 @@ export function runCrashExit(deps) {
|
|
|
2276
2888
|
}
|
|
2277
2889
|
deps.finish(`dsh-tui crashed: ${detail.message}`);
|
|
2278
2890
|
}
|
|
2891
|
+
/** None where the root has no `ctx.agents` (before DSH composes, or a non-DSH kernel). */
|
|
2892
|
+
function dshAgentOn(ctx, agentId) {
|
|
2893
|
+
return ctx.get('agents')?.get(SessionId(agentId));
|
|
2894
|
+
}
|
|
2895
|
+
/**
|
|
2896
|
+
* The crash tail's resume markers (exported for scripts/verify-shutdown-fallback).
|
|
2897
|
+
* Leaves the marker a clean exit would, never CLEARS one.
|
|
2898
|
+
*/
|
|
2899
|
+
export function writeCrashResumeMarkers(deps) {
|
|
2900
|
+
const { ctx, backendStart, channel } = deps;
|
|
2901
|
+
if (backendStart === undefined) {
|
|
2902
|
+
if (isExitResumable({
|
|
2903
|
+
pendingCount: channel.pending.length,
|
|
2904
|
+
liveAgent: dshAgentOn(ctx, channel.agentId),
|
|
2905
|
+
startupAgent: deps.startupAgent,
|
|
2906
|
+
})) {
|
|
2907
|
+
deps.writeResumeTarget(channel.agentId);
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
else if (backendStart.persisted(channel.agentId, channel.rows)) {
|
|
2911
|
+
// Non-DSH sessions use their backend preference as the launcher marker.
|
|
2912
|
+
backendStart.sessionPrefs.setLastSession(channel.agentId);
|
|
2913
|
+
}
|
|
2914
|
+
deps.refreshLastRunRecord();
|
|
2915
|
+
}
|
|
2279
2916
|
/**
|
|
2280
2917
|
* Whether a user exit should leave the resume marker (and print the resume
|
|
2281
2918
|
* hint). Must be judged against the LIVE session behind the channel, not the
|
|
@@ -2291,6 +2928,8 @@ export function isExitResumable(deps) {
|
|
|
2291
2928
|
const agent = deps.liveAgent ?? deps.startupAgent;
|
|
2292
2929
|
if (agent === undefined)
|
|
2293
2930
|
return false;
|
|
2931
|
+
if (isUnstoredFreshSession(agent.session))
|
|
2932
|
+
return false;
|
|
2294
2933
|
return (deps.pendingCount > 0 ||
|
|
2295
2934
|
snapshotLiveSessionEvents(agent.session).some(event => event.type === 'user/message' && event.data.source.kind === 'user'));
|
|
2296
2935
|
}
|
|
@@ -2336,7 +2975,7 @@ export async function finishExit(ctx, instance, fullscreen, notice, stderrNotice
|
|
|
2336
2975
|
}
|
|
2337
2976
|
const cursor = fullscreen ? '' : cursorMoveToFrameEnd(runtime);
|
|
2338
2977
|
try {
|
|
2339
|
-
runtime?.detachForShutdown?.();
|
|
2978
|
+
await runtime?.detachForShutdown?.();
|
|
2340
2979
|
// The /update continuation spawns children that inherit this stdin;
|
|
2341
2980
|
// strip the readable pump so the parent cannot swallow their input
|
|
2342
2981
|
// (issues #284/#307). Harmless on plain exits — the process exits
|
|
@@ -2466,11 +3105,47 @@ function writeStream(stream, data) {
|
|
|
2466
3105
|
function preservedSessionTail(sessionId, hint) {
|
|
2467
3106
|
return sessionId === '' ? '\n\n' : ` Your session is preserved — resume with:\n${hint(sessionId)}\n\n`;
|
|
2468
3107
|
}
|
|
2469
|
-
|
|
3108
|
+
/**
|
|
3109
|
+
* The entry's signals while following a replacement: SIGTERM is forwarded;
|
|
3110
|
+
* SIGINT/SIGHUP already reach it through the process group. A replacement
|
|
3111
|
+
* that dies by a termination signal ends this process the same way.
|
|
3112
|
+
*/
|
|
3113
|
+
function superviseReplacement(exitSeam, options) {
|
|
3114
|
+
if (exitSeam === undefined)
|
|
3115
|
+
return options;
|
|
3116
|
+
let replacement;
|
|
3117
|
+
exitSeam.request = request => {
|
|
3118
|
+
if (replacement === undefined)
|
|
3119
|
+
return 'refused';
|
|
3120
|
+
const forwarded = request.kind === 'signal' && request.signal === 'SIGTERM';
|
|
3121
|
+
if (forwarded)
|
|
3122
|
+
replacement.kill('SIGTERM');
|
|
3123
|
+
logRestartEvent('supervisor: exit request while following the replacement', request.kind === 'signal' ? { signal: request.signal, forwarded } : { code: request.code });
|
|
3124
|
+
return 'supervising';
|
|
3125
|
+
};
|
|
3126
|
+
return {
|
|
3127
|
+
...options,
|
|
3128
|
+
onSpawn: child => { replacement = child; },
|
|
3129
|
+
onTerminationSignal: signal => {
|
|
3130
|
+
if (TERMINATION_SIGNALS.includes(signal))
|
|
3131
|
+
dieBySignal(signal);
|
|
3132
|
+
},
|
|
3133
|
+
};
|
|
3134
|
+
}
|
|
3135
|
+
function runRestart(ctx, profile, sessionId, hint = id => resumeCommand(profile, id), options = {}, exitSeam) {
|
|
2470
3136
|
logRestartEvent('runRestart: entered, disposing cordis root');
|
|
2471
3137
|
disposeRootAndThen(ctx, () => {
|
|
2472
3138
|
logRestartEvent('runRestart: root disposed, starting restartTui');
|
|
2473
|
-
|
|
3139
|
+
const supervision = superviseReplacement(exitSeam, options);
|
|
3140
|
+
// The React tree stays mounted while supervising, so a late UI read of the
|
|
3141
|
+
// ended channel is logged instead of crashing the supervisor.
|
|
3142
|
+
addProcessErrorAbsorber(error => {
|
|
3143
|
+
if (!(error instanceof Error) || error.message !== CHANNEL_UI_LIFETIME_ENDED)
|
|
3144
|
+
return false;
|
|
3145
|
+
logRestartEvent('supervisor: late UI read after dispose ignored', { at: error.stack?.split('\n').slice(1, 4).map(line => line.trim()).join(' | ') });
|
|
3146
|
+
return true;
|
|
3147
|
+
});
|
|
3148
|
+
void restartTui(sessionId, supervision).then(restartCode => {
|
|
2474
3149
|
logRestartEvent('runRestart: restartTui resolved', { restartCode });
|
|
2475
3150
|
if (restartCode !== 0) {
|
|
2476
3151
|
writeHandoffNotice(`\ndsh-tui restart failed to spawn (exit ${restartCode}).${preservedSessionTail(sessionId, hint)}`);
|
|
@@ -2484,13 +3159,13 @@ function runRestart(ctx, profile, sessionId, hint = id => resumeCommand(profile,
|
|
|
2484
3159
|
});
|
|
2485
3160
|
});
|
|
2486
3161
|
}
|
|
2487
|
-
function runUpdate(ctx, profile, sessionId, targetVersion, kernel, hint = id => resumeCommand(profile, id)) {
|
|
3162
|
+
function runUpdate(ctx, profile, sessionId, targetVersion, kernel, hint = id => resumeCommand(profile, id), exitSeam) {
|
|
2488
3163
|
disposeRootAndThen(ctx, () => {
|
|
2489
3164
|
if (profile === undefined) {
|
|
2490
3165
|
process.stderr.write(`\n${t('update-aborted-no-profile')}\n`);
|
|
2491
3166
|
process.exit(1);
|
|
2492
3167
|
}
|
|
2493
|
-
void updateTuiAndRestart(sessionId, profile, targetVersion, kernel).then(({ updateCode, restartCode }) => {
|
|
3168
|
+
void updateTuiAndRestart(sessionId, profile, targetVersion, kernel, superviseReplacement(exitSeam, {})).then(({ updateCode, restartCode }) => {
|
|
2494
3169
|
if (updateCode !== 0) {
|
|
2495
3170
|
process.stderr.write(`\ndsh-tui update failed (exit ${updateCode}).${preservedSessionTail(sessionId, hint)}`);
|
|
2496
3171
|
}
|
|
@@ -2534,16 +3209,25 @@ function resumeCommand(profile, sessionId) {
|
|
|
2534
3209
|
* handoff (update/restart) may legitimately take longer than the bound, and
|
|
2535
3210
|
* reporting failure on a clean exit would mislead wrapper scripts.
|
|
2536
3211
|
*/
|
|
2537
|
-
function disposeRootAndThen(ctx, done,
|
|
3212
|
+
function disposeRootAndThen(ctx, done, fallback = 1) {
|
|
3213
|
+
const startedAt = Date.now();
|
|
2538
3214
|
const timer = setTimeout(() => {
|
|
2539
3215
|
// Diagnosis for a stalled disposal: without this line the fallback exit
|
|
2540
3216
|
// is indistinguishable from a successful handoff in the field.
|
|
2541
|
-
logRestartEvent('dispose: timeout, taking fallback exit', { fallbackCode });
|
|
2542
|
-
|
|
3217
|
+
logRestartEvent('dispose: timeout, taking fallback exit', typeof fallback === 'number' ? { fallbackCode: fallback } : { fallback: 'signal' });
|
|
3218
|
+
if (typeof fallback === 'number')
|
|
3219
|
+
process.exit(fallback);
|
|
3220
|
+
else
|
|
3221
|
+
fallback();
|
|
2543
3222
|
}, 5000);
|
|
2544
3223
|
timer.unref();
|
|
2545
|
-
|
|
3224
|
+
// A composition still running into this root settles first.
|
|
3225
|
+
// The pooled, process-wide resources of every backend this process actually
|
|
3226
|
+
// loaded are closed here — after the fiber, never before (P0 D4-P2/P3).
|
|
3227
|
+
void disposeRootSettled(ctx, () => withHostRootCapability(() => ctx.root.fiber.dispose())).finally(() => unloadBackends()).then(() => {
|
|
2546
3228
|
clearTimeout(timer);
|
|
3229
|
+
// The teardown's length, to restart.log only (never the terminal).
|
|
3230
|
+
logRestartEvent('dispose: root disposed', { ms: Date.now() - startedAt });
|
|
2547
3231
|
done();
|
|
2548
3232
|
}, () => {
|
|
2549
3233
|
clearTimeout(timer);
|