@deepseek-harness-tui/dsh-tui 0.10.2 → 0.11.1
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 +175 -123
- package/bin/dsh-tui.js +748 -22
- package/cordis.patch.yml +42 -13
- package/cordis.yml +5 -2
- package/lib/types/activityPrefs.d.ts +24 -0
- package/lib/types/activityPrefs.d.ts.map +1 -1
- package/lib/types/activityPrefs.js +50 -1
- package/lib/types/adapter/channel/session-projection.d.ts.map +1 -1
- package/lib/types/adapter/channel/session-projection.js +2 -0
- package/lib/types/adapter/channel/state.d.ts.map +1 -1
- package/lib/types/adapter/channel/state.js +3 -1
- package/lib/types/adapter/channel/ui-policy.d.ts +4 -1
- package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
- package/lib/types/adapter/channel/ui-policy.js +6 -2
- package/lib/types/adapter/kernel/host-facade.d.ts.map +1 -1
- package/lib/types/adapter/kernel/host-facade.js +1 -0
- package/lib/types/adapter/kernel/replay.d.ts.map +1 -1
- package/lib/types/adapter/kernel/replay.js +3 -0
- package/lib/types/adapter/kernel/runtime.d.ts.map +1 -1
- package/lib/types/adapter/kernel/runtime.js +2 -0
- package/lib/types/adapter/kernel/slices/workspace.d.ts.map +1 -1
- package/lib/types/adapter/kernel/slices/workspace.js +1 -0
- package/lib/types/adapter/ports/channel-ui.d.ts +29 -5
- package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-view.d.ts +66 -12
- package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-workspace.d.ts +23 -0
- package/lib/types/adapter/ports/channel-workspace.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel.d.ts +4 -1
- package/lib/types/adapter/ports/channel.d.ts.map +1 -1
- package/lib/types/adapter/ports/workspace.d.ts +2 -0
- package/lib/types/adapter/ports/workspace.d.ts.map +1 -1
- package/lib/types/adapter/upstream/workspace-driver.d.ts.map +1 -1
- package/lib/types/adapter/upstream/workspace-driver.js +10 -2
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +1 -0
- package/lib/types/components/ActivityLine.d.ts +7 -2
- package/lib/types/components/ActivityLine.d.ts.map +1 -1
- package/lib/types/components/ActivityLine.js +0 -8
- package/lib/types/components/ContextBarView.d.ts +15 -7
- package/lib/types/components/ContextBarView.d.ts.map +1 -1
- package/lib/types/components/ContextBarView.js +26 -22
- package/lib/types/components/ImagePreviewOverlay.d.ts.map +1 -1
- package/lib/types/components/ImagePreviewOverlay.js +11 -5
- package/lib/types/components/Markdown.d.ts +11 -1
- package/lib/types/components/Markdown.d.ts.map +1 -1
- package/lib/types/components/Markdown.js +20 -2
- package/lib/types/components/MermaidDiagram.d.ts +13 -0
- package/lib/types/components/MermaidDiagram.d.ts.map +1 -0
- package/lib/types/components/MermaidDiagram.js +61 -0
- package/lib/types/components/MessageList.d.ts +26 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +74 -8
- package/lib/types/components/PageMargin.d.ts +1 -1
- package/lib/types/components/PromptInput.d.ts +37 -1
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +291 -30
- package/lib/types/components/ScrollbarGutter.d.ts +20 -5
- package/lib/types/components/ScrollbarGutter.d.ts.map +1 -1
- package/lib/types/components/ScrollbarGutter.js +88 -10
- package/lib/types/components/SplitDiffView.d.ts.map +1 -1
- package/lib/types/components/SplitDiffView.js +2 -1
- package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
- package/lib/types/components/StreamingMarkdown.js +30 -28
- package/lib/types/components/SubagentCard.d.ts.map +1 -1
- package/lib/types/components/SubagentCard.js +12 -6
- package/lib/types/components/SubagentDetailScene.d.ts.map +1 -1
- package/lib/types/components/SubagentDetailScene.js +8 -3
- package/lib/types/components/Tooltip.d.ts.map +1 -1
- package/lib/types/components/Tooltip.js +45 -7
- package/lib/types/components/design-system/HintLine.d.ts +1 -2
- package/lib/types/components/design-system/HintLine.d.ts.map +1 -1
- package/lib/types/components/design-system/HintLine.js +1 -2
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts +2 -1
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +2 -1
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +2 -2
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +30 -26
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/messages/UserPromptMessage.js +5 -7
- package/lib/types/components/messages/useImageInspection.d.ts.map +1 -1
- package/lib/types/components/messages/useImageInspection.js +19 -9
- package/lib/types/components/promptDraftCache.d.ts +102 -0
- package/lib/types/components/promptDraftCache.d.ts.map +1 -0
- package/lib/types/components/promptDraftCache.js +76 -0
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts +17 -1
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +40 -7
- package/lib/types/components/questions/QuestionMinimizedBar.d.ts +12 -0
- package/lib/types/components/questions/QuestionMinimizedBar.d.ts.map +1 -0
- package/lib/types/components/questions/QuestionMinimizedBar.js +43 -0
- package/lib/types/components/sessions/SessionListRow.d.ts +23 -1
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
- package/lib/types/components/sessions/SessionListRow.js +52 -5
- package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts +49 -0
- package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts.map +1 -0
- package/lib/types/components/workspaces/HomeWorkspaceRow.js +50 -0
- package/lib/types/dsh-adapter/activity-store.d.ts +221 -0
- package/lib/types/dsh-adapter/activity-store.d.ts.map +1 -0
- package/lib/types/dsh-adapter/activity-store.js +359 -0
- package/lib/types/dsh-adapter/bundled-presets.d.ts +10 -0
- package/lib/types/dsh-adapter/bundled-presets.d.ts.map +1 -0
- package/lib/types/dsh-adapter/bundled-presets.js +80 -0
- package/lib/types/dsh-adapter/channel/action-readiness.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/action-readiness.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/action-readiness.js +3 -0
- package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/agent-view-projection.js +19 -2
- package/lib/types/dsh-adapter/channel/background-action.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/background-action.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/background-action.js +53 -29
- package/lib/types/dsh-adapter/channel/binding-events.d.ts +8 -6
- package/lib/types/dsh-adapter/channel/binding-events.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/binding-events.js +5 -4
- package/lib/types/dsh-adapter/channel/binding.d.ts +19 -2
- package/lib/types/dsh-adapter/channel/binding.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/binding.js +78 -18
- package/lib/types/dsh-adapter/channel/compaction.d.ts +0 -1
- package/lib/types/dsh-adapter/channel/compaction.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/compaction.js +0 -2
- package/lib/types/dsh-adapter/channel/composer-images.d.ts +5 -0
- package/lib/types/dsh-adapter/channel/composer-images.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/composer-images.js +105 -6
- package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts +8 -0
- package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/context-bookkeeping.js +3 -2
- package/lib/types/dsh-adapter/channel/ide-selection.d.ts +58 -0
- package/lib/types/dsh-adapter/channel/ide-selection.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/ide-selection.js +226 -0
- package/lib/types/dsh-adapter/channel/input-delivery.d.ts +6 -1
- package/lib/types/dsh-adapter/channel/input-delivery.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/input-delivery.js +9 -1
- package/lib/types/dsh-adapter/channel/local-actions.d.ts +3 -19
- package/lib/types/dsh-adapter/channel/local-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/local-actions.js +2 -1
- package/lib/types/dsh-adapter/channel/model-actions.d.ts +4 -1
- package/lib/types/dsh-adapter/channel/model-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/model-actions.js +82 -6
- package/lib/types/dsh-adapter/channel/model-switch.d.ts +1 -2
- package/lib/types/dsh-adapter/channel/model-switch.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/model-switch.js +49 -31
- package/lib/types/dsh-adapter/channel/projection.d.ts +4 -0
- package/lib/types/dsh-adapter/channel/projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/projection.js +29 -19
- package/lib/types/dsh-adapter/channel/reports.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/reports.js +4 -12
- package/lib/types/dsh-adapter/channel/session-adoption.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/session-adoption.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-adoption.js +1 -0
- package/lib/types/dsh-adapter/channel/session-fork.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-fork.js +26 -2
- package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts +4 -1
- package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-live-adoption.js +2 -0
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts +10 -0
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-metadata.js +6 -4
- package/lib/types/dsh-adapter/channel/session-reset.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/session-reset.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-reset.js +0 -1
- package/lib/types/dsh-adapter/channel/session-resume.d.ts +12 -3
- package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-resume.js +292 -94
- package/lib/types/dsh-adapter/channel/session-rewind.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-rewind.js +34 -16
- package/lib/types/dsh-adapter/channel/session-tree-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-tree-actions.js +19 -3
- package/lib/types/dsh-adapter/channel/settings-host.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/settings-host.js +4 -3
- package/lib/types/dsh-adapter/channel/state.d.ts +6 -1
- package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/state.js +3 -3
- package/lib/types/dsh-adapter/channel/subagent-projection.d.ts +21 -1
- package/lib/types/dsh-adapter/channel/subagent-projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/subagent-projection.js +173 -6
- package/lib/types/dsh-adapter/channel/transcript.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/transcript.js +6 -12
- package/lib/types/dsh-adapter/channel/types.d.ts +6 -1
- package/lib/types/dsh-adapter/channel/types.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/workspace-actions.d.ts +4 -1
- package/lib/types/dsh-adapter/channel/workspace-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/workspace-actions.js +41 -1
- package/lib/types/dsh-adapter/channel.d.ts +1 -1
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +82 -20
- package/lib/types/dsh-adapter/compat/index.d.ts +1 -1
- package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/index.js +1 -1
- package/lib/types/dsh-adapter/compat/messages.d.ts +15 -0
- package/lib/types/dsh-adapter/compat/messages.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/messages.js +15 -0
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts +18 -0
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/sessionLog.js +18 -3
- package/lib/types/dsh-adapter/compat/settings.d.ts +35 -0
- package/lib/types/dsh-adapter/compat/settings.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/settings.js +57 -0
- package/lib/types/dsh-adapter/compat/shell.d.ts +25 -0
- package/lib/types/dsh-adapter/compat/shell.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/shell.js +11 -0
- package/lib/types/dsh-adapter/contract.d.ts +5 -4
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +17 -6
- package/lib/types/dsh-adapter/ide-channel.d.ts +224 -0
- package/lib/types/dsh-adapter/ide-channel.d.ts.map +1 -0
- package/lib/types/dsh-adapter/ide-channel.js +556 -0
- package/lib/types/dsh-adapter/index.d.ts +18 -3
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +32 -5
- package/lib/types/dsh-adapter/plugin.d.ts +4 -1
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +197 -49
- package/lib/types/dsh-adapter/preset-resolution.d.ts +8 -0
- package/lib/types/dsh-adapter/preset-resolution.d.ts.map +1 -1
- package/lib/types/dsh-adapter/preset-resolution.js +10 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts +53 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts.map +1 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.js +100 -0
- package/lib/types/dsh-adapter/sessionTree.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessionTree.js +3 -2
- package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
- package/lib/types/dsh-adapter/settings-sections.js +7 -4
- package/lib/types/dsh-adapter/subagents.d.ts +16 -3
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
- package/lib/types/dsh-adapter/subagents.js +92 -29
- package/lib/types/dsh-adapter/workspaces.d.ts +46 -3
- package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -1
- package/lib/types/dsh-adapter/workspaces.js +116 -0
- package/lib/types/effortPrefs.d.ts +15 -3
- package/lib/types/effortPrefs.d.ts.map +1 -1
- package/lib/types/effortPrefs.js +37 -3
- package/lib/types/history.d.ts +13 -1
- package/lib/types/history.d.ts.map +1 -1
- package/lib/types/history.js +15 -2
- package/lib/types/homePrefs.d.ts +15 -0
- package/lib/types/homePrefs.d.ts.map +1 -0
- package/lib/types/homePrefs.js +53 -0
- package/lib/types/i18n.d.ts +347 -297
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +133 -85
- package/lib/types/ink/colorize.d.ts +10 -0
- package/lib/types/ink/colorize.d.ts.map +1 -1
- package/lib/types/ink/colorize.js +21 -0
- package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
- package/lib/types/ink/components/ScrollBox.js +12 -1
- package/lib/types/ink/dom.d.ts +1 -0
- package/lib/types/ink/dom.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-copy-on-select.d.ts +5 -1
- package/lib/types/ink/hooks/use-copy-on-select.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-copy-on-select.js +15 -1
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +36 -1
- package/lib/types/ink/log-update.d.ts.map +1 -1
- package/lib/types/ink/log-update.js +19 -1
- package/lib/types/ink/output.d.ts +10 -1
- package/lib/types/ink/output.d.ts.map +1 -1
- package/lib/types/ink/output.js +109 -17
- package/lib/types/ink/render-border.d.ts +6 -1
- package/lib/types/ink/render-border.d.ts.map +1 -1
- package/lib/types/ink/render-border.js +25 -9
- package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
- package/lib/types/ink/render-node-to-output.js +48 -6
- package/lib/types/ink/renderer.d.ts +6 -0
- package/lib/types/ink/renderer.d.ts.map +1 -1
- package/lib/types/ink/renderer.js +2 -1
- package/lib/types/ink/selection.d.ts +88 -0
- package/lib/types/ink/selection.d.ts.map +1 -1
- package/lib/types/ink/selection.js +164 -0
- package/lib/types/ink/sixel-graphics.d.ts +19 -1
- package/lib/types/ink/sixel-graphics.d.ts.map +1 -1
- package/lib/types/ink/sixel-graphics.js +124 -12
- package/lib/types/ink/terminal-image.d.ts +18 -1
- package/lib/types/ink/terminal-image.d.ts.map +1 -1
- package/lib/types/ink/terminal.d.ts +17 -0
- package/lib/types/ink/terminal.d.ts.map +1 -1
- package/lib/types/ink/terminal.js +20 -1
- package/lib/types/screens/Chat.d.ts +20 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +296 -76
- package/lib/types/screens/SessionSupervisor.d.ts +84 -0
- package/lib/types/screens/SessionSupervisor.d.ts.map +1 -0
- package/lib/types/screens/SessionSupervisor.js +350 -0
- package/lib/types/screens/SessionTree.js +2 -2
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +32 -9
- package/lib/types/screens/StatusLine.d.ts +18 -1
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +38 -19
- package/lib/types/screens/StatusMetrics.d.ts +79 -22
- package/lib/types/screens/StatusMetrics.d.ts.map +1 -1
- package/lib/types/screens/StatusMetrics.js +153 -88
- package/lib/types/screens/sessionSupervisor/model.d.ts +83 -0
- package/lib/types/screens/sessionSupervisor/model.d.ts.map +1 -0
- package/lib/types/screens/sessionSupervisor/model.js +69 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts +120 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts.map +1 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.js +593 -0
- package/lib/types/sessionMounts.d.ts +272 -0
- package/lib/types/sessionMounts.d.ts.map +1 -0
- package/lib/types/sessionMounts.js +706 -0
- package/lib/types/sessions/format.d.ts.map +1 -1
- package/lib/types/sessions/format.js +4 -1
- package/lib/types/sessions/resumeFailure.d.ts +38 -0
- package/lib/types/sessions/resumeFailure.d.ts.map +1 -0
- package/lib/types/sessions/resumeFailure.js +58 -0
- package/lib/types/terminal-utils/mermaid.d.ts +44 -0
- package/lib/types/terminal-utils/mermaid.d.ts.map +1 -0
- package/lib/types/terminal-utils/mermaid.js +83 -0
- package/lib/types/tips.d.ts.map +1 -1
- package/lib/types/tips.js +175 -19
- package/lib/types/tuiDisplayPrefs.d.ts +7 -9
- package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
- package/lib/types/tuiDisplayPrefs.js +31 -28
- package/lib/types/update.d.ts +61 -0
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +194 -0
- package/lib/types/utils/fold-long-lines.d.ts +1 -1
- package/lib/types/utils/fold-long-lines.js +1 -1
- package/lib/types/utils/imageResize.d.ts +90 -0
- package/lib/types/utils/imageResize.d.ts.map +1 -0
- package/lib/types/utils/imageResize.js +272 -0
- package/lib/types/utils/keymap.d.ts +5 -1
- package/lib/types/utils/keymap.d.ts.map +1 -1
- package/lib/types/utils/keymap.js +23 -0
- package/lib/types/working-activity.d.ts +10 -9
- package/lib/types/working-activity.d.ts.map +1 -1
- package/lib/types/working-activity.js +17 -10
- package/lib/types/workspaces.d.ts +2 -1
- package/lib/types/workspaces.d.ts.map +1 -1
- package/lib/types/workspaces.js +1 -1
- package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +10 -10
- package/package.json +126 -105
- package/presets/liangshen/.dsh-tui-managed.json +1 -1
- package/presets/liangshen/agent.cordis.yml +15 -0
- package/lib/types/components/design-system/KeyboardShortcutHint.d.ts +0 -19
- package/lib/types/components/design-system/KeyboardShortcutHint.d.ts.map +0 -1
- package/lib/types/components/design-system/KeyboardShortcutHint.js +0 -14
- package/lib/types/dsh-adapter/channel/activity.d.ts +0 -20
- package/lib/types/dsh-adapter/channel/activity.d.ts.map +0 -1
- package/lib/types/dsh-adapter/channel/activity.js +0 -113
- package/lib/types/screens/AgentView.d.ts +0 -23
- package/lib/types/screens/AgentView.d.ts.map +0 -1
- package/lib/types/screens/AgentView.js +0 -546
- package/lib/types/screens/SessionBrowser.d.ts +0 -42
- package/lib/types/screens/SessionBrowser.d.ts.map +0 -1
- package/lib/types/screens/SessionBrowser.js +0 -911
- package/lib/types/terminal-utils/terminal.d.ts +0 -18
- package/lib/types/terminal-utils/terminal.d.ts.map +0 -1
- package/lib/types/terminal-utils/terminal.js +0 -96
|
@@ -18,9 +18,9 @@ import { homeDir } from '../utils/paths.js';
|
|
|
18
18
|
import { cleanRenderText, cleanScalarText } from '../dsh-adapter/sanitize.js';
|
|
19
19
|
import { deriveModelGroups, modelPickerLanding, recentCatalogModels, RECENTS_GROUP_PROVIDER, } from '../modelGroups.js';
|
|
20
20
|
import { readModelRecents, recordModelUse } from '../modelRecents.js';
|
|
21
|
-
import { sessionCwdMatches } from '../dsh-adapter/channel.js';
|
|
22
21
|
import { TuiDialogStore } from '../dsh-adapter/dialogs.js';
|
|
23
22
|
import { TuiStatusStore } from '../dsh-adapter/status.js';
|
|
23
|
+
import { ActivityStore, useActivity } from '../dsh-adapter/activity-store.js';
|
|
24
24
|
import { runProviderWizard } from '../dsh-adapter/providerWizard.js';
|
|
25
25
|
import { ApprovalStore } from '../dsh-adapter/approvals.js';
|
|
26
26
|
import { AskUserQuestionPanel } from '../components/questions/AskUserQuestionPanel.js';
|
|
@@ -52,7 +52,7 @@ import { PluginSceneBoundary } from '../components/PluginSceneBoundary.js';
|
|
|
52
52
|
import { PluginStatusViewBoundary } from '../components/PluginStatusViewBoundary.js';
|
|
53
53
|
import { ImagePreviewOverlay } from '../components/ImagePreviewOverlay.js';
|
|
54
54
|
import { SkillsPicker, SkillsPickerLoading } from '../components/SkillsPicker.js';
|
|
55
|
-
import {
|
|
55
|
+
import { SessionSupervisor } from './SessionSupervisor.js';
|
|
56
56
|
import { SessionTree } from './SessionTree.js';
|
|
57
57
|
import { Settings } from './Settings.js';
|
|
58
58
|
import { WorkspacePicker } from '../components/WorkspacePicker.js';
|
|
@@ -89,7 +89,8 @@ import instances from '../ink/instances.js';
|
|
|
89
89
|
import { useAnimationFrame } from '../ink/hooks/use-animation-frame.js';
|
|
90
90
|
import { useExternalVersion } from '../hooks/useExternalVersion.js';
|
|
91
91
|
import { TrajectoryScene } from './TrajectoryScene.js';
|
|
92
|
-
import {
|
|
92
|
+
import { resumeFailureText } from '../sessions/resumeFailure.js';
|
|
93
|
+
import { markHomeSeen } from '../homePrefs.js';
|
|
93
94
|
import { extendTrajectory, projectWave } from '../dsh-adapter/trajectory/index.js';
|
|
94
95
|
import { miniWakeWidth } from '../components/trajectory/MiniWake.js';
|
|
95
96
|
import { readTrajectorySeen, writeTrajectorySeen } from '../trajectoryPrefs.js';
|
|
@@ -199,12 +200,14 @@ let fallbackApprovalStore;
|
|
|
199
200
|
*/
|
|
200
201
|
let fallbackDialogStore;
|
|
201
202
|
let fallbackStatusStore;
|
|
203
|
+
/** Standalone mounts (tests, bare embeds) without the composition root's store. */
|
|
204
|
+
let fallbackActivityStore;
|
|
202
205
|
/** Identity of one caret-preview dismissal: the token (its title) on the
|
|
203
206
|
* image, so the same image staged twice is dismissed per token. */
|
|
204
207
|
function peekKey(image, title) {
|
|
205
208
|
return `${title ?? ''} ${image.id}`;
|
|
206
209
|
}
|
|
207
|
-
export function Chat({ channel, questionStore, approvalStore, extensionDialogs, extensionStatus, extensionShortcuts, themeHost, onExit, onUpdate, onRestart, fullscreen = false, trajectorySeen: trajectorySeenProp, injectControllerRef, renderScene, }) {
|
|
210
|
+
export function Chat({ channel, questionStore, approvalStore, extensionDialogs, extensionStatus, activityStore, extensionShortcuts, themeHost, onExit, onUpdate, onRestart, fullscreen = false, trajectorySeen: trajectorySeenProp, injectControllerRef, promptControllerRef: promptControllerRefProp, renderScene, openHomeOnBoot, }) {
|
|
208
211
|
const writeRaw = React.useContext(TerminalWriteContext);
|
|
209
212
|
// Re-render whenever the channel mutates; rows/status are read fresh below.
|
|
210
213
|
// DEFAULT lane on purpose (useExternalVersion): the channel version bumps
|
|
@@ -237,6 +240,11 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
237
240
|
// Plugin status contributions: text keys join into one line; bounded rich
|
|
238
241
|
// views keep their own rows immediately above the prompt.
|
|
239
242
|
const statusContributions = extensionStatus ?? (fallbackStatusStore ??= new TuiStatusStore());
|
|
243
|
+
// The working line: the working-activity plugin's published value for THIS
|
|
244
|
+
// session, read from its session projection. No projection value (plugin
|
|
245
|
+
// absent, or nothing published yet) simply means the classic spinner below.
|
|
246
|
+
const activityValues = activityStore ?? (fallbackActivityStore ??= new ActivityStore());
|
|
247
|
+
const workingActivity = useActivity(activityValues, channel.sessionId);
|
|
240
248
|
const subscribeStatus = React.useCallback((listener) => statusContributions.subscribe(listener), [statusContributions]);
|
|
241
249
|
const statusEntries = React.useSyncExternalStore(subscribeStatus, () => statusContributions.getSnapshot());
|
|
242
250
|
const statusViews = React.useSyncExternalStore(subscribeStatus, () => statusContributions.getViewSnapshot());
|
|
@@ -358,18 +366,33 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
358
366
|
};
|
|
359
367
|
/** `/skills` 技能目录(issue #204):null = 注册表快照在途。 */
|
|
360
368
|
const [skillsList, setSkillsList] = React.useState(null);
|
|
361
|
-
/**
|
|
362
|
-
*
|
|
363
|
-
|
|
369
|
+
/**
|
|
370
|
+
* The session supervisor — the ONE screen behind `/resume`, `/agentview`,
|
|
371
|
+
* `/home`, `/bg` and the composer's 🏠 button.
|
|
372
|
+
*
|
|
373
|
+
* Those were three screens over one domain (a workspace rail here, a
|
|
374
|
+
* search surface there, a live-status overview somewhere else), which is why
|
|
375
|
+
* each new session feature needed patching into all three and why the
|
|
376
|
+
* three disagreed about what switching a session even does. There is now a
|
|
377
|
+
* single surface and a single runtime behind every entry point: this
|
|
378
|
+
* terminal hosts several sessions, leaving one parks it rather than ending
|
|
379
|
+
* it, and a session another terminal holds is visible but not enterable.
|
|
380
|
+
*
|
|
381
|
+
* Seeded from the host's one-shot landing decision (`openHomeOnBoot`): the
|
|
382
|
+
* first ordinary launch of an installation lands here instead of on a blank
|
|
383
|
+
* conversation, because that is the launch where "which project am I working
|
|
384
|
+
* on" has not been answered yet. Every later launch starts on the chat
|
|
385
|
+
* screen, and the screen stays reachable.
|
|
386
|
+
*/
|
|
387
|
+
const [supervisorOpen, setSupervisorOpen] = React.useState(openHomeOnBoot === true);
|
|
364
388
|
/** `/tree` opens the session family tree (pi's Session Tree): every rewind
|
|
365
389
|
* fork stitched back onto the message it diverged from, hover previews,
|
|
366
|
-
* and per-node rewind/fork/adopt actions. Like the
|
|
390
|
+
* and per-node rewind/fork/adopt actions. Like the supervisor, a screen. */
|
|
367
391
|
const [treeOpen, setTreeOpen] = React.useState(false);
|
|
368
|
-
/**
|
|
369
|
-
*
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
* returns to that conversation" return target), cleared on close. */
|
|
392
|
+
/**
|
|
393
|
+
* The session backgrounded when the screen opened via ←/`/bg` (the "Esc
|
|
394
|
+
* returns to that conversation" return target), cleared on close.
|
|
395
|
+
*/
|
|
373
396
|
const [agentViewReturnId, setAgentViewReturnId] = React.useState(undefined);
|
|
374
397
|
/** Live agent-view rows: the prompt footer's "← N agents" hint reads the
|
|
375
398
|
* needs-input count from here (cached snapshot in the channel). The
|
|
@@ -378,16 +401,16 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
378
401
|
const EMPTY_AGENT_VIEW_ROWS = [];
|
|
379
402
|
const agentViewRows = React.useSyncExternalStore(listener => channel.subscribeAgentView?.(listener) ?? (() => { }), () => channel.agentViewRows?.() ?? EMPTY_AGENT_VIEW_ROWS);
|
|
380
403
|
const backgroundAgentsNeedingInput = agentViewRows.filter(row => row.status === 'needs-input' && !row.current).length;
|
|
381
|
-
/** Background the attached session and open the
|
|
404
|
+
/** Background the attached session and open the supervisor
|
|
382
405
|
* (`/bg`, `/background`, and ← on an empty prompt all land here). The
|
|
383
|
-
* backgrounded session becomes the
|
|
406
|
+
* backgrounded session becomes the screen's return target (final Esc
|
|
384
407
|
* attaches back to it). */
|
|
385
408
|
const backgroundToAgentView = React.useCallback(() => {
|
|
386
409
|
void channel.backgroundCurrent().then((result) => {
|
|
387
410
|
if (result.ok) {
|
|
388
411
|
setAgentViewReturnId(result.backgroundedSessionId);
|
|
389
412
|
agentViewOpenSessionRef.current = channel.agentId;
|
|
390
|
-
|
|
413
|
+
setSupervisorOpen(true);
|
|
391
414
|
}
|
|
392
415
|
});
|
|
393
416
|
}, [channel]);
|
|
@@ -405,6 +428,10 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
405
428
|
const [themeName, setTheme] = useTheme();
|
|
406
429
|
const { rows: terminalRows } = useTerminalSize();
|
|
407
430
|
const [showAllMessages, setShowAllMessages] = React.useState(false);
|
|
431
|
+
/** Scope the fold to this question: an aborted ask can promote its queued
|
|
432
|
+
* successor without ever publishing an idle (null) snapshot. */
|
|
433
|
+
const [minimizedQuestionKey, setMinimizedQuestionKey] = React.useState(null);
|
|
434
|
+
const questionMinimized = questionSnapshot !== null && minimizedQuestionKey === questionSnapshot.key;
|
|
408
435
|
/** Fold state for the GoalTodoPanel todo section (ctrl/cmd+q or click). */
|
|
409
436
|
const [todoCollapsed, setTodoCollapsed] = React.useState(false);
|
|
410
437
|
const [thinkingVisible, setThinkingVisible] = React.useState(true);
|
|
@@ -615,6 +642,34 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
615
642
|
});
|
|
616
643
|
setSceneOpen(true);
|
|
617
644
|
}, []);
|
|
645
|
+
/**
|
|
646
|
+
* Leave the session supervisor for the conversation.
|
|
647
|
+
*
|
|
648
|
+
* The one-shot landing preference is written here rather than at boot: a
|
|
649
|
+
* process that dies before the user ever sees the screen (a config error, a
|
|
650
|
+
* crash during the first render) must not burn the installation's only
|
|
651
|
+
* first-launch landing. Writing on the way OUT means "the user has seen it".
|
|
652
|
+
*
|
|
653
|
+
* Leaving also honours `/bg`'s return target. `/background` moved the
|
|
654
|
+
* session the user was in to the background and opened this screen; a plain
|
|
655
|
+
* Esc out of it re-attaches to that session instead of silently leaving them
|
|
656
|
+
* on the fresh one, which is what "go back to what I was doing" means. Any
|
|
657
|
+
* explicit mount inside the screen clears the target first, so this can
|
|
658
|
+
* never undo a choice the user just made.
|
|
659
|
+
*/
|
|
660
|
+
const closeHome = React.useCallback(() => {
|
|
661
|
+
suppressLogoIntroRef.current = true;
|
|
662
|
+
markHomeSeen();
|
|
663
|
+
const returnTo = agentViewReturnId;
|
|
664
|
+
setAgentViewReturnId(undefined);
|
|
665
|
+
setSupervisorOpen(false);
|
|
666
|
+
if (returnTo !== undefined && returnTo !== channel.agentId) {
|
|
667
|
+
void channel.resumeTo(returnTo).then((result) => {
|
|
668
|
+
if (result.ok)
|
|
669
|
+
repaintTranscript();
|
|
670
|
+
}).catch(() => undefined);
|
|
671
|
+
}
|
|
672
|
+
}, [agentViewReturnId, channel, repaintTranscript]);
|
|
618
673
|
/** The startup summary gives way to transcript rows after the first local command or message. */
|
|
619
674
|
const loadedContextVisible = channel.rows.length === 0 && channel.loadedContext !== undefined;
|
|
620
675
|
/** Startup context panel: collapsed by default, toggled with Ctrl+P. */
|
|
@@ -851,12 +906,121 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
851
906
|
const exitTimerRef = React.useRef(null);
|
|
852
907
|
// Live view into the prompt's text for the Ctrl+C rule (clears text when
|
|
853
908
|
// non-empty; the double-press exit only arms on an empty input).
|
|
854
|
-
const
|
|
909
|
+
const ownPromptControllerRef = React.useRef(null);
|
|
910
|
+
const promptControllerRef = promptControllerRefProp ?? ownPromptControllerRef;
|
|
911
|
+
/**
|
|
912
|
+
* Owner of the unsent draft. Every screen this component renders INSTEAD of
|
|
913
|
+
* the conversation (the session screen, the tree, settings, the jobs and
|
|
914
|
+
* subagent panels, the trajectory scene) unmounts the composer, and the
|
|
915
|
+
* composer keeps its text in local state — so without this the half-written
|
|
916
|
+
* prompt died on the way in. The slot lives here, outlives that unmount, and
|
|
917
|
+
* is dropped the moment the attached session changes so no draft can follow
|
|
918
|
+
* the user into a different conversation.
|
|
919
|
+
*/
|
|
920
|
+
const promptDraftRef = React.useRef({ current: null });
|
|
921
|
+
/**
|
|
922
|
+
* Latest channel for the unmount release below: that effect must not re-run
|
|
923
|
+
* on a channel identity change, yet its cleanup must release against the
|
|
924
|
+
* channel of the last render.
|
|
925
|
+
*/
|
|
926
|
+
const channelRef = React.useRef(channel);
|
|
927
|
+
channelRef.current = channel;
|
|
928
|
+
/**
|
|
929
|
+
* Release the staged images a WAITING snapshot alone owns.
|
|
930
|
+
*
|
|
931
|
+
* While a draft waits in the slot for the composer to remount, the snapshot
|
|
932
|
+
* is the only owner of the capabilities behind its `[Image #N]` tokens. If
|
|
933
|
+
* Chat itself goes away first (leaving an early-return screen by exiting the
|
|
934
|
+
* TUI), nothing would ever restore or discard them — the session's
|
|
935
|
+
* 128-entry FIFO would evict live entries instead. The `hasStagedImage`
|
|
936
|
+
* guard keeps a capability the channel already recycled a no-op; both calls
|
|
937
|
+
* are idempotent.
|
|
938
|
+
*
|
|
939
|
+
* Scope, deliberately narrow (review round 7): a capability a QUEUED message
|
|
940
|
+
* still references (`channel.pending`) is never revoked here. The real
|
|
941
|
+
* double-hold path is paste an image → queue the draft with Tab while the
|
|
942
|
+
* model works → recall that line from input history with ↑ (same stageId
|
|
943
|
+
* rebound to the draft) → park the composer. Delivery resolves its refs from
|
|
944
|
+
* the enqueue-time capture, so a late revoke would only bite a host that
|
|
945
|
+
* re-resolves them afterwards — this keeps the rule identical to
|
|
946
|
+
* `stageIdIsRetained` instead of relying on that.
|
|
947
|
+
*
|
|
948
|
+
* A composer that is still MOUNTED when Chat unmounts is NOT covered: React
|
|
949
|
+
* runs this parent cleanup BEFORE the child's, so the child then writes its
|
|
950
|
+
* draft into the now-dead ref and those ids ride the channel's lifetime out
|
|
951
|
+
* (the #942 review's remaining P2). Neither unmount path loses anything
|
|
952
|
+
* user-visible — the channel dies with them.
|
|
953
|
+
*/
|
|
954
|
+
React.useEffect(() => {
|
|
955
|
+
return () => {
|
|
956
|
+
const snapshot = promptDraftRef.current.current;
|
|
957
|
+
promptDraftRef.current.current = null;
|
|
958
|
+
if (snapshot === null)
|
|
959
|
+
return;
|
|
960
|
+
const queued = new Set();
|
|
961
|
+
for (const item of channelRef.current.pending) {
|
|
962
|
+
// `?? []`: a foreign/embedded host may hand us a pending entry without
|
|
963
|
+
// images, and a throw inside an unmount cleanup escapes into the exit
|
|
964
|
+
// path — every other reader of this field guards it the same way.
|
|
965
|
+
for (const image of item.images ?? [])
|
|
966
|
+
queued.add(image.stageId);
|
|
967
|
+
}
|
|
968
|
+
for (const [, stageId] of snapshot.images) {
|
|
969
|
+
if (queued.has(stageId))
|
|
970
|
+
continue;
|
|
971
|
+
if (channelRef.current.hasStagedImage?.(stageId) === true) {
|
|
972
|
+
channelRef.current.discardStagedImage(stageId);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
}, []);
|
|
977
|
+
const draftSessionId = channel.agentId;
|
|
978
|
+
/** Session the effect below last reconciled against; a change is a switch. */
|
|
979
|
+
const draftSessionRef = React.useRef(draftSessionId);
|
|
980
|
+
/**
|
|
981
|
+
* The session a fill Chat itself requested belongs to, if one is in flight.
|
|
982
|
+
*
|
|
983
|
+
* A rewind's restored message arrives in the same commit that replaces the
|
|
984
|
+
* session, and it belongs to the NEW binding — the user picked it. The
|
|
985
|
+
* composer cannot tell, so Chat says so here, at the two call sites that ask
|
|
986
|
+
* for a fill.
|
|
987
|
+
*
|
|
988
|
+
* Keyed by the session id rather than a bare flag, so it can only ever excuse
|
|
989
|
+
* the switch it was written for. Do NOT clear it when the composer consumes
|
|
990
|
+
* the fill: a child's layout effects run before the parent's, so the fill is
|
|
991
|
+
* consumed in the very commit this effect judges, and clearing it there would
|
|
992
|
+
* wipe the message the user just got back.
|
|
993
|
+
*/
|
|
994
|
+
const pendingFillRef = React.useRef(null);
|
|
995
|
+
/**
|
|
996
|
+
* Drop the composer's text when the session underneath it is replaced.
|
|
997
|
+
*
|
|
998
|
+
* A LAYOUT effect, not a passive one: the clear has to land in the commit
|
|
999
|
+
* that swaps the session. A passive effect is flushed later, and anything
|
|
1000
|
+
* typed in between (the tree's hand-off, a fast user) would be wiped with the
|
|
1001
|
+
* old conversation's text. Which DRAFT the slot keeps is a separate question,
|
|
1002
|
+
* answered by the snapshot's owner fields.
|
|
1003
|
+
*/
|
|
1004
|
+
React.useLayoutEffect(() => {
|
|
1005
|
+
if (draftSessionRef.current === draftSessionId)
|
|
1006
|
+
return;
|
|
1007
|
+
draftSessionRef.current = draftSessionId;
|
|
1008
|
+
// A stored draft can only belong to the conversation being replaced: the
|
|
1009
|
+
// composer is the one that writes it, and it writes it on the way out.
|
|
1010
|
+
promptDraftRef.current.current = null;
|
|
1011
|
+
if (pendingFillRef.current === draftSessionId) {
|
|
1012
|
+
pendingFillRef.current = null;
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
promptControllerRef.current?.clear();
|
|
1016
|
+
}, [draftSessionId]);
|
|
855
1017
|
const previewGallery = activePreview === null ? [] : activePreview.peek
|
|
856
1018
|
? promptControllerRef.current?.previewImages?.() ?? [activePreview]
|
|
857
1019
|
: overlay.kind === 'image-preview' ? overlay.gallery ?? [activePreview] : [];
|
|
1020
|
+
// Peek entries are rebuilt from the prompt every render: match by
|
|
1021
|
+
// attachment id + token title, not facade identity.
|
|
858
1022
|
const previewIndex = activePreview?.peek
|
|
859
|
-
? previewGallery.findIndex(entry => entry.image === activePreview.image && entry.title === activePreview.title)
|
|
1023
|
+
? previewGallery.findIndex(entry => entry.image.id === activePreview.image.id && entry.title === activePreview.title)
|
|
860
1024
|
: overlay.kind === 'image-preview' ? overlay.index ?? 0 : -1;
|
|
861
1025
|
const stepPreview = (delta) => {
|
|
862
1026
|
if (!activePreview?.peek) {
|
|
@@ -943,7 +1107,11 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
943
1107
|
// fullscreen (<AlternateScreen> supplies mouse tracking); a no-op
|
|
944
1108
|
// subscription in inline mode, where selection belongs to the terminal.
|
|
945
1109
|
// The copy clears the highlight and posts a transient notification.
|
|
946
|
-
useCopyOnSelect(text => channel.notify(t('copied-chars', { n: text.length }), { timeoutMs: 1500 })
|
|
1110
|
+
useCopyOnSelect(text => channel.notify(t('copied-chars', { n: text.length }), { timeoutMs: 1500 }),
|
|
1111
|
+
// Stale-selection refusal: the highlighted rows were replaced in place
|
|
1112
|
+
// (streaming overwrite), so nothing was copied — say why instead of
|
|
1113
|
+
// letting the highlight vanish silently.
|
|
1114
|
+
() => channel.notify(t('copy-refused-stale'), { timeoutMs: 2500 }));
|
|
947
1115
|
const { clearSelection: clearMouseSelection, hasSelection: hasMouseSelection } = useSelection();
|
|
948
1116
|
React.useEffect(() => {
|
|
949
1117
|
if (!channel.working || !terminalFocused)
|
|
@@ -1611,27 +1779,31 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
1611
1779
|
}
|
|
1612
1780
|
return true;
|
|
1613
1781
|
}
|
|
1614
|
-
case 'resume':
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1782
|
+
case 'resume':
|
|
1783
|
+
/**
|
|
1784
|
+
* `/resume`, `/home` and `/agentview` are one screen.
|
|
1785
|
+
*
|
|
1786
|
+
* They were three implementations of one domain and drifted apart: the
|
|
1787
|
+
* same session could be listed by all three, each with its own selection
|
|
1788
|
+
* model and its own idea of what opening one does. Keeping the three
|
|
1789
|
+
* commands is about muscle memory, not about three surfaces — every one
|
|
1790
|
+
* of them lands here, on the same runtime.
|
|
1791
|
+
*/
|
|
1792
|
+
case 'home':
|
|
1622
1793
|
case 'agentview': {
|
|
1623
|
-
// The agent view shows one screen for every session. It opens
|
|
1624
|
-
// immediately; the view reads its own rows (live + persisted).
|
|
1625
1794
|
setHelpOpen(false);
|
|
1795
|
+
// The screen opens immediately and loads its own list. Waiting for the
|
|
1796
|
+
// listing here would make it feel slower the more history a project
|
|
1797
|
+
// has, which is exactly backwards.
|
|
1626
1798
|
agentViewOpenSessionRef.current = channel.agentId;
|
|
1627
|
-
|
|
1799
|
+
setSupervisorOpen(true);
|
|
1628
1800
|
return true;
|
|
1629
1801
|
}
|
|
1630
1802
|
case 'bg':
|
|
1631
1803
|
case 'background': {
|
|
1632
1804
|
// `/background`: the attached session moves to the background
|
|
1633
1805
|
// (it keeps running in this process), the terminal lands on a fresh
|
|
1634
|
-
// session, and the
|
|
1806
|
+
// session, and the supervisor opens on top.
|
|
1635
1807
|
setHelpOpen(false);
|
|
1636
1808
|
backgroundToAgentView();
|
|
1637
1809
|
return true;
|
|
@@ -2256,6 +2428,10 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
2256
2428
|
const performRewind = async (row, mode = null) => {
|
|
2257
2429
|
const text = await channel.rewindTo(row, mode);
|
|
2258
2430
|
if (text !== null) {
|
|
2431
|
+
// The restored message belongs to the binding `rewindTo` just created,
|
|
2432
|
+
// not to the one it replaced: it is the user's choice, and the switch
|
|
2433
|
+
// effect must not treat it as the old conversation's leftovers.
|
|
2434
|
+
pendingFillRef.current = String(channel.agentId);
|
|
2259
2435
|
// Put the restored message back in the prompt for re-editing.
|
|
2260
2436
|
setHistoryFill(text);
|
|
2261
2437
|
channel.notify(t('rewind-done'));
|
|
@@ -2460,18 +2636,15 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
2460
2636
|
|| overlay.kind === 'tips'
|
|
2461
2637
|
|| (recap !== null && (!recap.auto || recap.expanded)))
|
|
2462
2638
|
return;
|
|
2463
|
-
//
|
|
2464
|
-
//
|
|
2465
|
-
// search box, which Chat would otherwise route into the prompt.
|
|
2466
|
-
if (browserOpen)
|
|
2467
|
-
return;
|
|
2468
|
-
// Same for the session tree: plain letters drive its search, clicks and
|
|
2469
|
-
// Enter drive its action menu.
|
|
2639
|
+
// The session tree owns the whole terminal while it is up: plain letters
|
|
2640
|
+
// drive its search, clicks and Enter drive its action menu.
|
|
2470
2641
|
if (treeOpen)
|
|
2471
2642
|
return;
|
|
2472
|
-
// The
|
|
2473
|
-
// its
|
|
2474
|
-
|
|
2643
|
+
// The session supervisor owns the whole terminal while it is up: its rail
|
|
2644
|
+
// and session list bind ↑/↓/Enter/Tab/Esc, its filter box takes the plain
|
|
2645
|
+
// letters that would otherwise reach the prompt, and the directory picker
|
|
2646
|
+
// and menus it opens are its own modal layers.
|
|
2647
|
+
if (supervisorOpen)
|
|
2475
2648
|
return;
|
|
2476
2649
|
// Same for the settings screen: plain letters (s save / d discard) and
|
|
2477
2650
|
// the field draft editor belong to it alone.
|
|
@@ -2575,8 +2748,17 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
2575
2748
|
// keyboard while one is pending (the panel's own useInput handles
|
|
2576
2749
|
// ↑/↓/Space/Tab/Enter/Esc; the prompt input is suspended, so nothing
|
|
2577
2750
|
// else should see these keys).
|
|
2578
|
-
if (
|
|
2751
|
+
if (approvalSnapshot !== null || dialogSnapshot !== null)
|
|
2752
|
+
return;
|
|
2753
|
+
if (questionSnapshot !== null) {
|
|
2754
|
+
// Only transcript navigation belongs here. The mounted questionnaire
|
|
2755
|
+
// owns fold/expand keys, including when it interrupts another screen.
|
|
2756
|
+
if (questionMinimized && !isSticky && (isPlainReturnInput(input, key) || key.end)) {
|
|
2757
|
+
handle?.scrollToBottom();
|
|
2758
|
+
event.stopImmediatePropagation();
|
|
2759
|
+
}
|
|
2579
2760
|
return;
|
|
2761
|
+
}
|
|
2580
2762
|
const returnCandidate = isPlainReturnInput(input, key);
|
|
2581
2763
|
const returnNow = Date.now();
|
|
2582
2764
|
const plainReturn = returnCandidate && returnNow - lastModalEnterAtRef.current >= 80;
|
|
@@ -3365,9 +3547,9 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
3365
3547
|
const approvalPanelNode = approvalSnapshot !== null ? (_jsx(ApprovalPanel, { approval: approvalSnapshot, background: approvalSnapshot.agentId !== channel.agentId, onDecide: outcome => approvals.decide(outcome) }, approvalSnapshot.key)) : null;
|
|
3366
3548
|
const questionPanelNode = questionSnapshot !== null ? (_jsx(AskUserQuestionPanel, { question: questionSnapshot.question, position: questionSnapshot.position, total: questionSnapshot.total, answered: questionSnapshot.answered, initialDraft: questionSnapshot.draft, onAnswer: selection => questionStore.answerCurrent(selection), onCancel: () => questionStore.cancelCurrent(), onBack: questionSnapshot.canGoBack
|
|
3367
3549
|
? draft => questionStore.backCurrent(draft)
|
|
3368
|
-
: undefined }, questionSnapshot.key)) : null;
|
|
3550
|
+
: undefined, collapsed: questionMinimized, onExpand: () => setMinimizedQuestionKey(null), onToggleFold: () => setMinimizedQuestionKey(previous => previous === questionSnapshot.key ? null : questionSnapshot.key), fullscreen: fullscreen }, questionSnapshot.key)) : null;
|
|
3369
3551
|
const interruptPanel = approvalPanelNode ?? questionPanelNode;
|
|
3370
|
-
const screenOpen = channel.pluginScene !== undefined ||
|
|
3552
|
+
const screenOpen = channel.pluginScene !== undefined || supervisorOpen || settingsOpen
|
|
3371
3553
|
|| subagentDetailId !== null || subagentDashboardOpen || sceneOpen;
|
|
3372
3554
|
if (interruptPanel !== null && screenOpen) {
|
|
3373
3555
|
const node = (_jsx(Box, { flexDirection: "column", width: "100%", paddingX: 1, children: interruptPanel }));
|
|
@@ -3394,37 +3576,62 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
3394
3576
|
}, children: renderScene ? renderScene(pluginScene.id, channel) : _jsxs(Text, { children: ["Scene unavailable: ", pluginScene.id] }) }));
|
|
3395
3577
|
return fullscreen ? node : _jsx(AlternateScreen, { children: node });
|
|
3396
3578
|
}
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3579
|
+
/**
|
|
3580
|
+
* The session supervisor: a screen in the same sense as the tree — an early
|
|
3581
|
+
* return after every hook above has run, so there is no transcript
|
|
3582
|
+
* underneath to repaint or bled through.
|
|
3583
|
+
*
|
|
3584
|
+
* It sits ABOVE the session tree because it is the surface a launch can
|
|
3585
|
+
* start on (`openHomeOnBoot`): a first launch has no conversation to come
|
|
3586
|
+
* back to, and every action it offers either mounts a session (which closes
|
|
3587
|
+
* it) or starts a new one.
|
|
3588
|
+
*
|
|
3589
|
+
* Behind it, every session this terminal hosts keeps running — that is the
|
|
3590
|
+
* runtime the screen describes, not an implementation detail of it. A turn
|
|
3591
|
+
* that was in flight when the user opened this screen is still in flight
|
|
3592
|
+
* while they read the list, which is why closing the screen only repaints
|
|
3593
|
+
* the transcript when the attached session actually changed.
|
|
3594
|
+
*/
|
|
3595
|
+
if (supervisorOpen) {
|
|
3596
|
+
/**
|
|
3597
|
+
* Live state per session, from the channel's own agent-view projection.
|
|
3598
|
+
* Reading the projection rather than a parallel source is what keeps this
|
|
3599
|
+
* screen and the attention hints in the composer footer from disagreeing
|
|
3600
|
+
* about which session is waiting for input.
|
|
3601
|
+
*/
|
|
3602
|
+
const agentRowOf = (sessionId) => agentViewRows.find(row => row.id === sessionId);
|
|
3603
|
+
const supervisorNode = (_jsx(SessionSupervisor, { channel: channel, home: homeDir(), onClose: closeHome, approval: approvalSnapshot, onApprove: outcome => approvals.decide(outcome), onOpenSession: async (sessionId) => {
|
|
3604
|
+
const result = await channel.resumeTo(sessionId);
|
|
3605
|
+
if (!result.ok) {
|
|
3606
|
+
const text = resumeFailureText(result);
|
|
3607
|
+
if (text !== undefined)
|
|
3608
|
+
channel.notify(text, { color: 'error', timeoutMs: 8000 });
|
|
3609
|
+
return false;
|
|
3610
|
+
}
|
|
3611
|
+
channel.notify(t('resume-resumed'));
|
|
3406
3612
|
suppressLogoIntroRef.current = true;
|
|
3407
|
-
const switched = agentViewOpenSessionRef.current !== undefined &&
|
|
3408
|
-
agentViewOpenSessionRef.current !== channel.agentId;
|
|
3409
|
-
if (switched)
|
|
3410
|
-
repaintTranscript();
|
|
3411
3613
|
setAgentViewReturnId(undefined);
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3614
|
+
setSupervisorOpen(false);
|
|
3615
|
+
repaintTranscript();
|
|
3616
|
+
return true;
|
|
3617
|
+
}, onNewSession: async (target) => {
|
|
3618
|
+
const ok = await channel.switchWorkspace(target);
|
|
3619
|
+
if (ok) {
|
|
3620
|
+
suppressLogoIntroRef.current = true;
|
|
3621
|
+
setAgentViewReturnId(undefined);
|
|
3622
|
+
setSupervisorOpen(false);
|
|
3623
|
+
repaintTranscript();
|
|
3624
|
+
}
|
|
3625
|
+
return ok;
|
|
3626
|
+
}, onStopSession: async (sessionId) => channel.stopBackgroundAgent?.(sessionId) ?? false, liveStateOf: (sessionId) => {
|
|
3627
|
+
const row = agentRowOf(sessionId);
|
|
3628
|
+
return row === undefined
|
|
3629
|
+
? undefined
|
|
3630
|
+
: { status: row.status, live: row.live, current: row.current, summary: row.summary };
|
|
3424
3631
|
} }));
|
|
3425
3632
|
// Inline hosts enter the alternate screen for the duration; full-screen
|
|
3426
3633
|
// hosts are already in it and must not nest a second one.
|
|
3427
|
-
return fullscreen ?
|
|
3634
|
+
return fullscreen ? supervisorNode : _jsx(AlternateScreen, { children: supervisorNode });
|
|
3428
3635
|
}
|
|
3429
3636
|
// The session tree follows the browser's rule exactly: it REPLACES the
|
|
3430
3637
|
// conversation (an early return after every hook above has run), so there
|
|
@@ -3432,6 +3639,10 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
3432
3639
|
// turn's prompt returns through the same fill path a rewind picker uses.
|
|
3433
3640
|
if (treeOpen) {
|
|
3434
3641
|
const tree = (_jsx(SessionTree, { channel: channel, currentSessionId: channel.agentId, onClose: () => setTreeOpen(false), onRestoreText: (text) => {
|
|
3642
|
+
// The tree rewound to a node and is handing that turn's prompt back,
|
|
3643
|
+
// exactly like the picker does. It belongs to the binding the tree
|
|
3644
|
+
// action just created.
|
|
3645
|
+
pendingFillRef.current = String(channel.agentId);
|
|
3435
3646
|
setHistoryFill(text);
|
|
3436
3647
|
} }));
|
|
3437
3648
|
return fullscreen ? tree : _jsx(AlternateScreen, { children: tree });
|
|
@@ -3568,7 +3779,7 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
3568
3779
|
// sessions keep the full intro; restored ones settle instantly.
|
|
3569
3780
|
// A remount after a whole screen closed also settles instantly
|
|
3570
3781
|
// (see suppressLogoIntroRef).
|
|
3571
|
-
skipIntro: suppressLogoIntroRef.current || channel.rows.length > 30 }, logoNonce), loadedContextVisible && (_jsx(LoadedContextPanel, { context: channel.loadedContext, open: loadedContextOpen, onToggle: toggleLoadedContext })), _jsx(MessageList, { rows: channel.rows, failureHintRowId: failureHintRowId, failureHint: t('traj-hint-failure', { key: `${modLabel}t` }), expanded: expanded, expandedRows: expandedRows, selectedId: selectionActive ? selectedId : null, onToggleRow: toggleRowExpanded, streamViewToggledRows: streamViewToggledRows, onToggleStreamView: toggleStreamView, model: channel.model, diffLayout: channel.diffLayout, thinkingFold: channel.thinkingFold, toolBackground: channel.toolBackground, foldTerminalCommand: channel.foldTerminalCommand, smoothStreaming: channel.smoothStreaming, activityFrames: channel.activityFrames, showAll: showAllMessages, thinkingVisible: thinkingVisible, historyPaintEnabled: !fullscreen, onToggleAll: () => { setShowAllMessages(previous => !previous); }, onLoadOlder: () => channel.loadOlder(), registerRowRef: registerRowRef, scrollHandle: handle, forceMountRowId: forceMountRowId, newSinceRowId: isSticky ? null : lastSeenRowIdRef.current, onUnseenCount: setUnseenCount, onTimeline: setTimeline, onOpenSubagent: setSubagentDetailId, onOpenJobs: openJobsPanel, onOpenFile: openFileActions, onPreviewImage: openImagePreview, suppressImageGraphics: activePreview !== null })] }), (() => {
|
|
3782
|
+
skipIntro: suppressLogoIntroRef.current || channel.rows.length > 30 }, logoNonce), loadedContextVisible && (_jsx(LoadedContextPanel, { context: channel.loadedContext, open: loadedContextOpen, onToggle: toggleLoadedContext })), _jsx(MessageList, { rows: channel.rows, failureHintRowId: failureHintRowId, failureHint: t('traj-hint-failure', { key: `${modLabel}t` }), expanded: expanded, expandedRows: expandedRows, selectedId: selectionActive ? selectedId : null, onToggleRow: toggleRowExpanded, streamViewToggledRows: streamViewToggledRows, onToggleStreamView: toggleStreamView, model: channel.model, diffLayout: channel.diffLayout, thinkingFold: channel.thinkingFold, toolBackground: channel.toolBackground, foldTerminalCommand: channel.foldTerminalCommand, smoothStreaming: channel.smoothStreaming, activityFrames: channel.activityFrames, showAll: showAllMessages, thinkingVisible: thinkingVisible, historyPaintEnabled: !fullscreen, onToggleAll: () => { setShowAllMessages(previous => !previous); }, onLoadOlder: () => channel.loadOlder(), registerRowRef: registerRowRef, scrollHandle: handle, forceMountRowId: forceMountRowId, newSinceRowId: isSticky ? null : lastSeenRowIdRef.current, onUnseenCount: setUnseenCount, onTimeline: setTimeline, onOpenSubagent: setSubagentDetailId, onOpenJobs: openJobsPanel, onOpenFile: openFileActions, sessionCwd: channel.cwd, onPreviewImage: openImagePreview, suppressImageGraphics: activePreview !== null })] }), (() => {
|
|
3572
3783
|
// Gutter mode (settings `dsh-tui.scrollGutter`): the timeline
|
|
3573
3784
|
// rail (default), the proportional scrollbar, or nothing. The
|
|
3574
3785
|
// slot keeps its 2 columns in both rendered modes (Qwen's
|
|
@@ -3584,9 +3795,9 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
3584
3795
|
})(), !promptEditorOpen && imagePreviewNode] }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [showPill && (_jsx(NewMessagesPill, { count: unseenCount, onClick: () => handle?.scrollToBottom() })), channel.working &&
|
|
3585
3796
|
(channel.activityEnabled &&
|
|
3586
3797
|
!channel.minimal &&
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3798
|
+
workingActivity !== undefined &&
|
|
3799
|
+
workingActivity.line !== '' &&
|
|
3800
|
+
workingActivity.phase !== 'idle' ? (_jsx(Box, { marginTop: 1, children: _jsx(ActivityLine, { activity: workingActivity, activityFrames: channel.activityFrames, warnPct: activityWarnPct, warnDanger: activityWarnPct !== undefined && activityWarnPct >= 95,
|
|
3590
3801
|
// Upload = real tokens of the last request; download =
|
|
3591
3802
|
// the animated chars/4 estimate, matching the classic
|
|
3592
3803
|
// spinner's counter (the suffix used raw chars before,
|
|
@@ -3617,7 +3828,16 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
3617
3828
|
void setClipboard(btw.answer ?? '').then(raw => { if (raw)
|
|
3618
3829
|
writeRaw?.(raw); });
|
|
3619
3830
|
channel.notify(t('copied-chars', { n: (btw.answer ?? '').length }), { timeoutMs: 1500 });
|
|
3620
|
-
} }) })) : questionPanelNode !== null ? (questionPanelNode) : null, _jsx(PromptInput, { channel: channel, suspended: promptReplacementOpen, helpOpen: helpOpen, onToggleHelp: () => { setHelpOpen(previous => !previous); }, onRunCommand: runCommand, selectionActive: promptSelectionActive, fillText: historyFill, onFillConsumed: () =>
|
|
3831
|
+
} }) })) : questionPanelNode !== null ? (questionPanelNode) : null, _jsx(PromptInput, { channel: channel, suspended: promptReplacementOpen, draftCache: promptDraftRef.current, helpOpen: helpOpen, onToggleHelp: () => { setHelpOpen(previous => !previous); }, onRunCommand: runCommand, selectionActive: promptSelectionActive, fillText: historyFill, onFillConsumed: () => setHistoryFill(null), onRewindRequest: openRewind, onBackgroundRequest: backgroundToAgentView,
|
|
3832
|
+
// The 🏠 at the head of the input row opens the same session screen
|
|
3833
|
+
// `/resume` and `/agentview` open — one surface, three doors. It is
|
|
3834
|
+
// gated on this prop rather than a setting, so hosts that mount the
|
|
3835
|
+
// prompt without a session screen (and the layout regressions that
|
|
3836
|
+
// pin the row's column budget) keep the row they had.
|
|
3837
|
+
onOpenSessions: () => {
|
|
3838
|
+
agentViewOpenSessionRef.current = channel.agentId;
|
|
3839
|
+
setSupervisorOpen(true);
|
|
3840
|
+
}, backgroundAgentsNeedingInput:
|
|
3621
3841
|
// Only the real channel supplies the seam; pre-agent-view test
|
|
3622
3842
|
// stubs must not grow the footer row (layout-dependent
|
|
3623
3843
|
// regressions pin the visible row count). The footer only
|
|
@@ -3628,7 +3848,7 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
|
|
|
3628
3848
|
// clock and shifting every row-count layout invariant.
|
|
3629
3849
|
channel.agentViewRows !== undefined && backgroundAgentsNeedingInput > 0
|
|
3630
3850
|
? backgroundAgentsNeedingInput
|
|
3631
|
-
: undefined, controllerRef: promptControllerRef, onCaretImage: handleCaretImage, caretPreviewOpen: peekPreview !== null, onDismissCaretPreview: dismissPeek }, "prompt-input"), _jsx(StatusLine, { channel: channel, selectionActive: selectionActive, helpOpen: helpOpen, wake: wakeBand === undefined
|
|
3851
|
+
: undefined, controllerRef: promptControllerRef, onCaretImage: handleCaretImage, caretPreviewOpen: peekPreview !== null, onDismissCaretPreview: dismissPeek }, "prompt-input"), _jsx(StatusLine, { channel: channel, activity: workingActivity, selectionActive: selectionActive, helpOpen: helpOpen, wake: wakeBand === undefined
|
|
3632
3852
|
? undefined
|
|
3633
3853
|
: {
|
|
3634
3854
|
band: wakeBand,
|