@deepseek-harness-tui/dsh-tui 0.10.2 → 0.11.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 +171 -124
- package/bin/dsh-tui.js +748 -22
- package/cordis.patch.yml +38 -10
- package/cordis.yml +2 -0
- 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/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 +5 -1
- 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 +22 -2
- package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-view.d.ts +66 -0
- 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/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/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 +72 -7
- 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 +243 -28
- 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/StreamingMarkdown.d.ts.map +1 -1
- package/lib/types/components/StreamingMarkdown.js +30 -28
- package/lib/types/components/Tooltip.d.ts.map +1 -1
- package/lib/types/components/Tooltip.js +45 -7
- 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/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.map +1 -1
- package/lib/types/dsh-adapter/channel/background-action.js +52 -29
- 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/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.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-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 +3 -0
- package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-live-adoption.js +1 -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 +2 -2
- package/lib/types/dsh-adapter/channel/session-resume.d.ts +11 -2
- package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-resume.js +291 -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 +1 -1
- package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/state.js +1 -1
- 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.map +1 -1
- package/lib/types/dsh-adapter/channel.js +67 -6
- 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/settings.d.ts +33 -0
- package/lib/types/dsh-adapter/compat/settings.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/settings.js +43 -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 +4 -4
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +15 -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 +5 -2
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +173 -42
- 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/subagents.d.ts.map +1 -1
- package/lib/types/dsh-adapter/subagents.js +6 -5
- 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/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 +257 -296
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +78 -84
- 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/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 +60 -0
- package/lib/types/ink/selection.d.ts.map +1 -1
- package/lib/types/ink/selection.js +129 -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 +17 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +284 -72
- 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 +13 -0
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +36 -17
- 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/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/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 +120 -103
- package/presets/liangshen/.dsh-tui-managed.json +1 -1
- package/presets/liangshen/agent.cordis.yml +15 -0
- 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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentHandle } from '@deepseek-ai/dsh-agent';
|
|
1
|
+
import type { Agent, AgentHandle } from '@deepseek-ai/dsh-agent';
|
|
2
2
|
import type { Context } from '@deepseek-ai/cordis';
|
|
3
3
|
import { type SessionEvent } from '@deepseek-ai/dsh-session';
|
|
4
4
|
import { resetSessionProjection } from './session-reset.js';
|
|
@@ -22,7 +22,13 @@ export declare function createSessionResumeActions(ctx: Context, state: ResumeSt
|
|
|
22
22
|
model: string;
|
|
23
23
|
}, deps: {
|
|
24
24
|
owner: Pick<ChannelOwner, 'current'>;
|
|
25
|
-
binding: Pick<Binding, 'agent' | 'capture' | 'isCurrent' | 'prepare' | 'abandon' | 'adopt'>;
|
|
25
|
+
binding: Pick<Binding, 'agent' | 'capture' | 'isCurrent' | 'prepare' | 'abandon' | 'adopt' | 'waitForDisposal'>;
|
|
26
|
+
/**
|
|
27
|
+
* Adopt an agent this process already has live. `/resume` uses it so a
|
|
28
|
+
* target that is already running here is re-attached in place (parking the
|
|
29
|
+
* session left behind) instead of being resumed a second time from its log.
|
|
30
|
+
*/
|
|
31
|
+
adoptLive(target: Agent): Promise<ResumeResult>;
|
|
26
32
|
backgroundHandles: Map<string, AgentHandle>;
|
|
27
33
|
rowIds: {
|
|
28
34
|
value: number;
|
|
@@ -41,6 +47,9 @@ export declare function createSessionResumeActions(ctx: Context, state: ResumeSt
|
|
|
41
47
|
refreshLoadedContext(): Promise<void>;
|
|
42
48
|
refreshSkillCommands(): Promise<void>;
|
|
43
49
|
clearStagedImages(): void;
|
|
50
|
+
/** Drop the live IDE selection: the adopted session's cwd differs from
|
|
51
|
+
* the one the selection was made in. */
|
|
52
|
+
resetIdeSelection(): void;
|
|
44
53
|
settleCompaction(): Promise<void>;
|
|
45
54
|
sessionSwitchVetoed(kind: 'new' | 'resume' | 'agent-view', targetSessionId?: string): Promise<boolean>;
|
|
46
55
|
notify: ChannelState['notify'];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-resume.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/session-resume.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"session-resume.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/session-resume.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAEpF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAmBvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAgC,KAAK,qBAAqB,EAAE,MAAM,iCAAiC,CAAA;AAC1G,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE5D,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACtD,KAAK,WAAW,GAAG,IAAI,CACrB,YAAY,EACV,SAAS,GACT,QAAQ,GACR,SAAS,GACT,KAAK,GACL,YAAY,GACZ,aAAa,GACb,UAAU,GACV,OAAO,GACP,eAAe,GACf,eAAe,GACf,cAAc,GACd,iBAAiB,GACjB,SAAS,GACT,MAAM,CACT,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAA;AAEhD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B,CAAA;AAgBD,8DAA8D;AAC9D,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE;IACP,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;CACd,EACD,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACpC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,iBAAiB,CAAC,CAAA;IAC/G;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAC/C,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IAC3C,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IACzB,cAAc,IAAI,IAAI,CAAA;IACtB,cAAc,IAAI,IAAI,CAAA;IACtB,SAAS,IAAI,IAAI,CAAA;IACjB,MAAM,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI,CAAA;IAC7C,YAAY,IAAI,IAAI,CAAA;IACpB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACxD,gBAAgB,IAAI,IAAI,CAAA;IACxB,SAAS,IAAI,IAAI,CAAA;IACjB,eAAe,IAAI,IAAI,CAAA;IACvB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,iBAAiB,IAAI,IAAI,CAAA;IACzB;6CACyC;IACzC,iBAAiB,IAAI,IAAI,CAAA;IACzB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,mBAAmB,CAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,YAAY,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACtG,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAC9B,qBAAqB,CAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChH,OAAO,EAAE,qBAAqB,CAAA;CAC/B;4BA+LoC,MAAM,QAAQ,QAAQ,GAAG,YAAY,eAAe,OAAO,KAAG,OAAO,CAAC,YAAY,CAAC;0BAiDrF,MAAM,KAAG,OAAO,CAAC,YAAY,CAAC;sBA4K1C,OAAO,CAAC,OAAO,CAAC;oCA7GM,gBAAgB,KAAG,OAAO,CAAC,OAAO,CAAC;EAgHjF"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { SessionId } from '@deepseek-ai/dsh-session';
|
|
3
3
|
import { explicitModelRoute, recordedModelRoute, resolveModelRoute, validateModelRoute } from '../../modelRoute.js';
|
|
4
|
-
import { clearResumeTarget, touchAgentViewSession, touchSession
|
|
4
|
+
import { clearResumeTarget, writeResumeTarget, touchAgentViewSession, touchSession } from '../../sessionHistory.js';
|
|
5
|
+
import { mountFailureText } from '../../sessions/resumeFailure.js';
|
|
5
6
|
import { t } from '../../i18n.js';
|
|
6
7
|
import { readModelPref } from '../../modelPrefs.js';
|
|
7
8
|
import { migratePresetPref, readPresetPref } from '../../presetPrefs.js';
|
|
8
9
|
import { agentViewHasTurns } from '../agent-view.js';
|
|
10
|
+
import { occupancyOf, readSessionOwners, reserveMount, reserveNewSession, } from '../../sessionMounts.js';
|
|
9
11
|
import { ensureLegacySessionEventTypes } from '../compat/index.js';
|
|
10
12
|
import { snapshotLiveSessionEvents } from '../compat/liveSession.js';
|
|
11
13
|
import { composePreset, resolvePersistedPreset, resolvePersistedRoute } from '../presets.js';
|
|
@@ -39,96 +41,272 @@ export function createSessionResumeActions(ctx, state, options, deps) {
|
|
|
39
41
|
void deps.refreshSkillCommands();
|
|
40
42
|
};
|
|
41
43
|
/** Resume a persisted session, optionally parking the attached agent for agent-view attachment. */
|
|
42
|
-
const resume = async (sessionId, kind, keepCurrent, adoption, entrySession
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
...(composed.setup === undefined ? {} : { setup: composed.setup }),
|
|
61
|
-
}));
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
65
|
-
deps.notify(t('resume-failed', { err: message }), { color: 'error', timeoutMs: 8000 });
|
|
66
|
-
return { ok: false, reason: 'failed', error: message };
|
|
67
|
-
}
|
|
68
|
-
if (!deps.binding.isCurrent(adoption)) {
|
|
69
|
-
await deps.binding.abandon(handle);
|
|
70
|
-
return { ok: false, reason: 'cancelled' };
|
|
71
|
-
}
|
|
44
|
+
const resume = async (sessionId, kind, keepCurrent, adoption, entrySession,
|
|
45
|
+
/**
|
|
46
|
+
* The ledger reservation this call holds for `sessionId`; ended by this
|
|
47
|
+
* function on every exit. `settle` on a commit (the agent is in the
|
|
48
|
+
* registry from then on), `abandon` on every path that ends WITHOUT one —
|
|
49
|
+
* including a throwing `binding.adopt()` and a setup failure inside its
|
|
50
|
+
* tail, which revokes the candidate.
|
|
51
|
+
*/
|
|
52
|
+
reservation) => {
|
|
53
|
+
/**
|
|
54
|
+
* The reservation is ended on EVERY exit from here, so the attempt owns it
|
|
55
|
+
* for the whole body: `settle` on a commit (the agent is in the registry
|
|
56
|
+
* from then on), `abandon` on everything else — a throwing
|
|
57
|
+
* `binding.adopt()`, a setup failure inside its tail, and also a throw from
|
|
58
|
+
* the preset/route reads below, which used to sit outside any guard and
|
|
59
|
+
* would have left a pin that republishes the session as ours on every beat.
|
|
60
|
+
*/
|
|
61
|
+
let committed = false;
|
|
72
62
|
try {
|
|
73
|
-
await
|
|
63
|
+
return await runResume();
|
|
74
64
|
}
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
finally {
|
|
66
|
+
if (committed)
|
|
67
|
+
reservation?.settle();
|
|
68
|
+
else
|
|
69
|
+
reservation?.abandon();
|
|
77
70
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
71
|
+
async function runResume() {
|
|
72
|
+
const agents = ctx.get('agents');
|
|
73
|
+
if (!agents) {
|
|
74
|
+
deps.notify(t('resume-unavailable'), { color: 'error' });
|
|
75
|
+
return { ok: false, reason: 'unavailable' };
|
|
76
|
+
}
|
|
77
|
+
ensureLegacySessionEventTypes();
|
|
78
|
+
const composed = await composePreset(ctx, await resolvePersistedPreset(ctx, SessionId(sessionId)));
|
|
79
|
+
const explicitRoute = explicitModelRoute({ provider: options.configuredProvider, model: options.configuredModel });
|
|
80
|
+
const persistedRoute = await resolvePersistedRoute(ctx, SessionId(sessionId));
|
|
81
|
+
let handle;
|
|
82
|
+
try {
|
|
83
|
+
handle = await deps.binding.prepare(adoption, () => agents.resume({
|
|
84
|
+
resumeSessionId: SessionId(sessionId),
|
|
85
|
+
agentOptions: {
|
|
86
|
+
provider: explicitRoute?.provider ?? persistedRoute?.provider,
|
|
87
|
+
model: explicitRoute?.model ?? persistedRoute?.model,
|
|
88
|
+
},
|
|
89
|
+
...(composed.setup === undefined ? {} : { setup: composed.setup }),
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
94
|
+
deps.notify(t('resume-failed', { err: message }), { color: 'error', timeoutMs: 8000 });
|
|
95
|
+
return { ok: false, reason: 'failed', error: message };
|
|
96
|
+
}
|
|
97
|
+
if (!deps.binding.isCurrent(adoption)) {
|
|
98
|
+
await deps.binding.abandon(handle);
|
|
99
|
+
return { ok: false, reason: 'cancelled' };
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
await attachSessionToWorkspace(ctx, handle.agent.session.header.cwd ?? state.cwd, SessionId(sessionId));
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
deps.notify(t('resume-attach-failed', { err: error instanceof Error ? error.message : String(error) }), { color: 'warning', timeoutMs: 8000 });
|
|
106
|
+
}
|
|
107
|
+
if (entrySession !== undefined && (!deps.owner.current() || deps.binding.agent.session !== entrySession)) {
|
|
108
|
+
await deps.binding.abandon(handle);
|
|
109
|
+
deps.notify(t('resume-session-changed'), { color: 'error' });
|
|
110
|
+
return { ok: false, reason: 'failed', error: 'live session changed during resume' };
|
|
111
|
+
}
|
|
112
|
+
// `adopt` is a transaction: it revokes the candidate and disposes it when
|
|
113
|
+
// the tail fails, and it can also refuse before the tail ever runs. Only a
|
|
114
|
+
// normal return is a commit, which is what the outer finally keys on.
|
|
115
|
+
const result = deps.binding.adopt(handle, adoption, (committedBinding, disposePrevious) => {
|
|
116
|
+
const previousSessionId = String(committedBinding.agent.session.id);
|
|
117
|
+
state.cwd = handle.agent.session.header.cwd ?? state.cwd;
|
|
118
|
+
state.displayCwd = deps.describeWorkspace(state.cwd).description ?? state.cwd;
|
|
119
|
+
deps.resetIdeSelection();
|
|
120
|
+
deps.refreshGitBranch();
|
|
121
|
+
// Reset the input FIFO and pending-decision indicators BEFORE the first
|
|
122
|
+
// emit (main's bind → clear → refresh order); see the /new tail.
|
|
123
|
+
deps.clearStagedImages();
|
|
124
|
+
resetAndBind(handle, composed.agentPreset, explicitRoute ?? recordedModelRoute(snapshotLiveSessionEvents(handle.agent.session)), true);
|
|
125
|
+
writeResumeTarget(sessionId);
|
|
126
|
+
touchSession(sessionId);
|
|
127
|
+
state.emit();
|
|
128
|
+
const keepPrevious = keepCurrent && committedBinding.handle !== undefined
|
|
129
|
+
&& (committedBinding.handle.agent.status === 'running' || agentViewHasTurns(snapshotLiveSessionEvents(committedBinding.handle.agent.session)));
|
|
130
|
+
if (committedBinding.handle !== undefined) {
|
|
131
|
+
if (keepPrevious) {
|
|
132
|
+
deps.backgroundHandles.set(previousSessionId, committedBinding.handle);
|
|
133
|
+
disposePrevious('park');
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
disposePrevious('dispose');
|
|
137
|
+
}
|
|
101
138
|
}
|
|
102
|
-
|
|
103
|
-
|
|
139
|
+
if (kind === 'agent-view') {
|
|
140
|
+
touchAgentViewSession(sessionId);
|
|
141
|
+
touchAgentViewSession(previousSessionId);
|
|
104
142
|
}
|
|
143
|
+
deps.notifySessionSwitched(kind, sessionId, previousSessionId);
|
|
144
|
+
return { ok: true };
|
|
145
|
+
});
|
|
146
|
+
committed = true;
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Reserve `sessionId` for a disk resume on THIS terminal.
|
|
152
|
+
*
|
|
153
|
+
* Returning a reservation means the caller owns the session in the ledger
|
|
154
|
+
* until it calls `finish()`. That covers the whole attempt, not just the
|
|
155
|
+
* `agents.resume` call: the awaits in between (veto, compaction, preset and
|
|
156
|
+
* route reads) are exactly the window a publisher beat — or a peer — could
|
|
157
|
+
* otherwise slip into.
|
|
158
|
+
* @param sessionId - Session about to be resumed from disk.
|
|
159
|
+
* @returns The reservation, or the refusal to hand back to the caller.
|
|
160
|
+
*/
|
|
161
|
+
const reserveDiskResume = async (sessionId) => {
|
|
162
|
+
// Cheap pre-filter for the friendly toast: the authoritative conflict test
|
|
163
|
+
// runs inside reserveMount, under the ledger lock.
|
|
164
|
+
const occupancy = occupancyOf(sessionId, readSessionOwners());
|
|
165
|
+
if (occupancy.kind === 'occupied') {
|
|
166
|
+
deps.notify(t('resume-session-occupied', { pid: occupancy.pid }), { color: 'error', timeoutMs: 8000 });
|
|
167
|
+
return { ok: false, result: { ok: false, reason: 'occupied', pid: occupancy.pid } };
|
|
168
|
+
}
|
|
169
|
+
const reserved = await reserveMount(sessionId);
|
|
170
|
+
if (!reserved.ok) {
|
|
171
|
+
if (reserved.reason === 'occupied') {
|
|
172
|
+
const pid = reserved.holders[0] ?? 0;
|
|
173
|
+
deps.notify(t('resume-session-occupied', { pid }), { color: 'error', timeoutMs: 8000 });
|
|
174
|
+
return { ok: false, result: { ok: false, reason: 'occupied', pid } };
|
|
105
175
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return { ok: true };
|
|
112
|
-
});
|
|
176
|
+
// Both of these are refusals, not conflicts, and neither may be dressed
|
|
177
|
+
// up as "held by pid 0". The generic failure branch carries the sentence.
|
|
178
|
+
return { ok: false, result: { ok: false, reason: 'failed', error: mountFailureText(reserved) } };
|
|
179
|
+
}
|
|
180
|
+
return { ok: true, reservation: reserved.reservation };
|
|
113
181
|
};
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
182
|
+
/**
|
|
183
|
+
* Reserve a freshly minted session id for a create on THIS terminal. See
|
|
184
|
+
* `reserveNewSession`: a new id cannot conflict, so a refusal is a loss of
|
|
185
|
+
* announcement rather than a reason to stop, and it is warned about.
|
|
186
|
+
* @param sessionId - Session id minted for the new agent.
|
|
187
|
+
* @returns The reservation (a no-op when the claim could not be made).
|
|
188
|
+
*/
|
|
189
|
+
const reserveCreatedSession = async (sessionId) => {
|
|
190
|
+
const { reservation, failure } = await reserveNewSession(sessionId);
|
|
191
|
+
if (failure !== undefined && failure.reason !== 'occupied') {
|
|
192
|
+
deps.notify(mountFailureText(failure), { color: 'warning', timeoutMs: 8000 });
|
|
193
|
+
}
|
|
194
|
+
return reservation;
|
|
195
|
+
};
|
|
196
|
+
const resumeInto = async (sessionId, kind, keepCurrent) => {
|
|
117
197
|
const adoption = deps.binding.capture();
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
198
|
+
const agents = ctx.get('agents');
|
|
199
|
+
if (!agents) {
|
|
200
|
+
deps.notify(t('resume-unavailable'), { color: 'error' });
|
|
201
|
+
return { ok: false, reason: 'unavailable' };
|
|
121
202
|
}
|
|
203
|
+
// Same reservation as `/resume`: this is the other public door onto the
|
|
204
|
+
// disk path (`ChannelUi.attachToAgent`), and a mount that skips the ledger
|
|
205
|
+
// is a mount a peer can race from the outside.
|
|
206
|
+
const reserved = await reserveDiskResume(sessionId);
|
|
207
|
+
if (!reserved.ok)
|
|
208
|
+
return reserved.result;
|
|
209
|
+
return resume(sessionId, kind, keepCurrent, adoption, undefined, reserved.reservation);
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* `/resume` — mount a session on THIS terminal, the same non-destructive way
|
|
213
|
+
* `/agentview` does.
|
|
214
|
+
*
|
|
215
|
+
* There used to be two different mount models behind these two commands.
|
|
216
|
+
* `/agentview` parked the session it left behind (it kept running in this
|
|
217
|
+
* process and was reachable again from the view), while `/resume` DISPOSED
|
|
218
|
+
* it, and refused outright while a turn was running. That split was never a
|
|
219
|
+
* decision about resuming; it was an accident of the two commands growing
|
|
220
|
+
* separately, and it is what made `/resume` feel like a different product
|
|
221
|
+
* from the session overview sitting one command away.
|
|
222
|
+
*
|
|
223
|
+
* The unified model is the overview's, because it is the one that matches
|
|
224
|
+
* what a TUI terminal actually is: one process that can host several agent
|
|
225
|
+
* sessions at once. So:
|
|
226
|
+
*
|
|
227
|
+
* - Leaving a session parks it (`keepCurrent`), it does not end it, and the
|
|
228
|
+
* parked handle is reachable from the session screen afterwards.
|
|
229
|
+
* - A running turn is not a refusal. The user is switching what they are
|
|
230
|
+
* LOOKING at, not asking the model to stop; the turn keeps running in the
|
|
231
|
+
* background and its row keeps reporting progress.
|
|
232
|
+
*
|
|
233
|
+
* The one thing that IS refused is a session another TUI process already has
|
|
234
|
+
* mounted: two processes driving one append-only log interleave its events.
|
|
235
|
+
* The occupancy check happens BEFORE the resume awaits (which yield), and
|
|
236
|
+
* the claim is published immediately after it so this process owns the
|
|
237
|
+
* session before any other process can pass the same check.
|
|
238
|
+
*
|
|
239
|
+
* The reservation also outlives those awaits: it is released only when the
|
|
240
|
+
* attempt ends without a commit. The publisher's beat derives its set from
|
|
241
|
+
* the agent registry, which does not list the target yet, so a reservation
|
|
242
|
+
* that was not held across them could be erased by this process's own
|
|
243
|
+
* heartbeat and handed straight to a peer.
|
|
244
|
+
*/
|
|
245
|
+
const resumeTo = async (sessionId) => {
|
|
246
|
+
const adoption = deps.binding.capture();
|
|
122
247
|
const agents = ctx.get('agents');
|
|
123
248
|
if (!agents) {
|
|
124
249
|
deps.notify(t('resume-unavailable'), { color: 'error' });
|
|
125
250
|
return { ok: false, reason: 'unavailable' };
|
|
126
251
|
}
|
|
127
|
-
|
|
252
|
+
// The attached session as of entry, so a rival switch that commits while
|
|
253
|
+
// the awaits below yield cannot be adopted over.
|
|
254
|
+
const entrySession = deps.binding.agent.session;
|
|
255
|
+
// Already attached: this is a no-op, not a switch.
|
|
256
|
+
//
|
|
257
|
+
// The live-adoption path below hands `backgroundHandles.get(targetId)` to
|
|
258
|
+
// `binding.switchTo()`, and a session this terminal is CURRENTLY attached
|
|
259
|
+
// to has no entry there — its handle is the binding's own `currentHandle`.
|
|
260
|
+
// Calling it with `undefined` therefore parks nothing, and the adoption's
|
|
261
|
+
// default disposition disposes the handle that IS running, so a second
|
|
262
|
+
// Enter on the `current` row stopped the live turn while reporting success.
|
|
263
|
+
// `attachToAgent()` has always short-circuited here; this is the same rule
|
|
264
|
+
// for the unified screen's `/resume` path.
|
|
265
|
+
if (String(sessionId) === String(entrySession.id))
|
|
266
|
+
return { ok: true };
|
|
267
|
+
// Switching away starts disposal without blocking the synchronous commit.
|
|
268
|
+
// Do not adopt that closing Agent (or reopen its still-held JSONL writer).
|
|
269
|
+
await deps.binding.waitForDisposal(sessionId);
|
|
270
|
+
if (!deps.binding.isCurrent(adoption))
|
|
271
|
+
return { ok: false, reason: 'cancelled' };
|
|
272
|
+
// A live agent of this process is already mounted here; there is nothing
|
|
273
|
+
// to claim and nothing that can be occupied. Adoption takes the live
|
|
274
|
+
// handle (parking the current one) with no occupancy round-trip.
|
|
275
|
+
const live = agents.get?.(SessionId(sessionId));
|
|
276
|
+
let reservation;
|
|
277
|
+
if (live === undefined) {
|
|
278
|
+
const reserved = await reserveDiskResume(sessionId);
|
|
279
|
+
if (!reserved.ok)
|
|
280
|
+
return reserved.result;
|
|
281
|
+
reservation = reserved.reservation;
|
|
282
|
+
}
|
|
283
|
+
if (await deps.sessionSwitchVetoed('resume', sessionId)) {
|
|
284
|
+
reservation?.abandon();
|
|
128
285
|
return { ok: false, reason: 'cancelled' };
|
|
286
|
+
}
|
|
129
287
|
await deps.settleCompaction();
|
|
130
|
-
|
|
131
|
-
|
|
288
|
+
if (!deps.binding.isCurrent(adoption) || deps.binding.agent.session !== entrySession) {
|
|
289
|
+
reservation?.abandon();
|
|
290
|
+
return { ok: false, reason: 'cancelled' };
|
|
291
|
+
}
|
|
292
|
+
// The target was read BEFORE those awaits. Re-read it in BOTH directions:
|
|
293
|
+
// the registry can have replaced or dropped that agent while we yielded
|
|
294
|
+
// (adopting the captured object would hand the screen a session nothing
|
|
295
|
+
// owns any more), and it can also have GROWN one — a peer action mounting
|
|
296
|
+
// this very session here means the disk path below would resume a log this
|
|
297
|
+
// process is already driving. `agent-view-projection.attach` has always made
|
|
298
|
+
// the first check.
|
|
299
|
+
const liveNow = agents.get?.(SessionId(sessionId));
|
|
300
|
+
if (liveNow !== live) {
|
|
301
|
+
reservation?.abandon();
|
|
302
|
+
return { ok: false, reason: 'cancelled' };
|
|
303
|
+
}
|
|
304
|
+
// A live target is adopted in place — the same path `/agentview` uses, so
|
|
305
|
+
// the session being left is parked rather than disposed of. Only a target
|
|
306
|
+
// with no live agent here goes back to the persistence backend.
|
|
307
|
+
if (live !== undefined)
|
|
308
|
+
return deps.adoptLive(live);
|
|
309
|
+
return resume(sessionId, 'agent-view', true, adoption, entrySession, reservation);
|
|
132
310
|
};
|
|
133
311
|
const newSessionWithTarget = async (target) => {
|
|
134
312
|
// The typed workspace target seam still creates a real Agent/session; it
|
|
@@ -181,6 +359,10 @@ export function createSessionResumeActions(ctx, state, options, deps) {
|
|
|
181
359
|
}
|
|
182
360
|
return false;
|
|
183
361
|
}
|
|
362
|
+
// Reserve BEFORE the factory runs: the moment `agents.create` returns, this
|
|
363
|
+
// process holds the only write handle on a log no peer has been told about
|
|
364
|
+
// yet, and the publisher would not name it until its next beat.
|
|
365
|
+
const reservation = await reserveCreatedSession(sessionId);
|
|
184
366
|
let handle;
|
|
185
367
|
try {
|
|
186
368
|
handle = await deps.binding.prepare(adoption, () => agents.create({
|
|
@@ -191,6 +373,7 @@ export function createSessionResumeActions(ctx, state, options, deps) {
|
|
|
191
373
|
}));
|
|
192
374
|
}
|
|
193
375
|
catch (error) {
|
|
376
|
+
reservation.abandon();
|
|
194
377
|
if (current()) {
|
|
195
378
|
const message = error instanceof Error ? error.message : String(error);
|
|
196
379
|
deps.notify(t('new-session-failed', { err: message }), { color: 'error', timeoutMs: 8000 });
|
|
@@ -199,6 +382,7 @@ export function createSessionResumeActions(ctx, state, options, deps) {
|
|
|
199
382
|
}
|
|
200
383
|
if (!current()) {
|
|
201
384
|
await deps.binding.abandon(handle);
|
|
385
|
+
reservation.abandon();
|
|
202
386
|
return false;
|
|
203
387
|
}
|
|
204
388
|
try {
|
|
@@ -210,30 +394,43 @@ export function createSessionResumeActions(ctx, state, options, deps) {
|
|
|
210
394
|
}
|
|
211
395
|
if (!current()) {
|
|
212
396
|
await deps.binding.abandon(handle);
|
|
397
|
+
reservation.abandon();
|
|
213
398
|
return false;
|
|
214
399
|
}
|
|
215
400
|
// Do not catch this synchronous commit tail. A post-commit setup failure
|
|
216
401
|
// is owned by binding.adopt(), which revokes the new live handle and must
|
|
217
402
|
// reject its caller rather than masquerade as an ordinary precommit false.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
403
|
+
let committed = false;
|
|
404
|
+
try {
|
|
405
|
+
const result = deps.binding.adopt(handle, adoption, (committedBinding, disposePrevious) => {
|
|
406
|
+
const previousSessionId = String(committedBinding.agent.session.id);
|
|
407
|
+
// The target only becomes shared channel state inside the successful
|
|
408
|
+
// adoption tail. A losing prepared handle therefore cannot publish or
|
|
409
|
+
// roll back another workspace's cwd.
|
|
410
|
+
state.cwd = targetCwd;
|
|
411
|
+
state.displayCwd = targetDisplayCwd ?? deps.describeWorkspace(targetCwd).description ?? targetCwd;
|
|
412
|
+
deps.resetIdeSelection();
|
|
413
|
+
// Reset the input FIFO and the pending-decision indicators BEFORE the
|
|
414
|
+
// first emit: a submit enqueued from a session-changed subscriber must
|
|
415
|
+
// land on a fresh chain instead of behind the replaced session's parked
|
|
416
|
+
// promise (main's bind → clear → refresh order).
|
|
417
|
+
deps.clearStagedImages();
|
|
418
|
+
resetAndBind(handle, composed.agentPreset, route, false);
|
|
419
|
+
clearResumeTarget();
|
|
420
|
+
touchSession(handle.agent.id);
|
|
421
|
+
disposePrevious('dispose');
|
|
422
|
+
deps.notifySessionSwitched('new', String(handle.agent.id), previousSessionId);
|
|
423
|
+
return true;
|
|
424
|
+
});
|
|
425
|
+
committed = true;
|
|
426
|
+
return result;
|
|
427
|
+
}
|
|
428
|
+
finally {
|
|
429
|
+
if (committed)
|
|
430
|
+
reservation.settle();
|
|
431
|
+
else
|
|
432
|
+
reservation.abandon();
|
|
433
|
+
}
|
|
237
434
|
};
|
|
238
435
|
const newSession = () => newSessionWithTarget();
|
|
239
436
|
return { resumeInto, resumeTo, newSession, newSessionWithTarget };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-rewind.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/session-rewind.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"session-rewind.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/session-rewind.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AASvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEvD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACtD,KAAK,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,CAAC,CAAA;AAgB/E,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACpC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC,CAAA;IACjF,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAC9B,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,YAAY,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,CAAA;IAC1K,qBAAqB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1F,IAEa,KAAK,OAAO,EAAE,OAAM,MAAM,GAAG,IAAW,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsGhF"}
|
|
@@ -6,6 +6,7 @@ import { dispatchTuiDecision } from '../extension-events.js';
|
|
|
6
6
|
import { normalizeRewindDoneSummary } from './decisions.js';
|
|
7
7
|
import { composePreset, runningPresetOf } from '../presets.js';
|
|
8
8
|
import { attachSessionToWorkspace } from '../workspace.js';
|
|
9
|
+
import { reserveNewSession } from '../../sessionMounts.js';
|
|
9
10
|
async function waitForTurnEnd(session, fromSeq, timeoutMs) {
|
|
10
11
|
const deadline = Date.now() + timeoutMs;
|
|
11
12
|
while (Date.now() < deadline) {
|
|
@@ -65,6 +66,10 @@ export function createRewindToAction(ctx, state, deps) {
|
|
|
65
66
|
return null;
|
|
66
67
|
}
|
|
67
68
|
const composed = await composePreset(ctx, runningPresetOf(deps.binding.agent.session));
|
|
69
|
+
// Announce the id before the factory: the rewind creates the child's log
|
|
70
|
+
// here, and the publisher only learns the id from the registry on its next
|
|
71
|
+
// beat.
|
|
72
|
+
const { reservation } = await reserveNewSession(String(childId));
|
|
68
73
|
let handle;
|
|
69
74
|
try {
|
|
70
75
|
handle = await deps.binding.prepare(adoption, () => agents.create(liveSessionCreateOptions({
|
|
@@ -87,11 +92,13 @@ export function createRewindToAction(ctx, state, deps) {
|
|
|
87
92
|
})));
|
|
88
93
|
}
|
|
89
94
|
catch {
|
|
95
|
+
reservation.abandon();
|
|
90
96
|
deps.notify(t('rewind-create-failed'), { color: 'error' });
|
|
91
97
|
return null;
|
|
92
98
|
}
|
|
93
99
|
if (!deps.binding.isCurrent(adoption)) {
|
|
94
100
|
await deps.binding.abandon(handle);
|
|
101
|
+
reservation.abandon();
|
|
95
102
|
return null;
|
|
96
103
|
}
|
|
97
104
|
try {
|
|
@@ -102,27 +109,38 @@ export function createRewindToAction(ctx, state, deps) {
|
|
|
102
109
|
}
|
|
103
110
|
if (!deps.owner.current()) {
|
|
104
111
|
await deps.binding.abandon(handle);
|
|
112
|
+
reservation.abandon();
|
|
105
113
|
return null;
|
|
106
114
|
}
|
|
107
|
-
|
|
115
|
+
// `adoptForkedAgent` is the commit: the child is the session this process
|
|
116
|
+
// now drives, so the reservation hands over to the registry. It THROWS when
|
|
117
|
+
// the adoption transaction revokes the candidate, so it is guarded.
|
|
108
118
|
try {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
const sourceSessionId = deps.adoptForkedAgent(handle, adoption, snapshotLiveSessionEvents(handle.agent.session), composed.agentPreset, childId);
|
|
120
|
+
reservation.settle();
|
|
121
|
+
try {
|
|
122
|
+
void dispatchTuiDecision(ctx, 'tui/rewind-done', {
|
|
123
|
+
text: row.text,
|
|
124
|
+
mode,
|
|
125
|
+
boundarySeq: boundary,
|
|
126
|
+
sourceSessionId,
|
|
127
|
+
childSessionId: String(childId),
|
|
128
|
+
sessionId: String(childId),
|
|
129
|
+
cwd: state.cwd,
|
|
130
|
+
}, normalizeRewindDoneSummary).then(summary => {
|
|
131
|
+
if (typeof summary === 'string')
|
|
132
|
+
deps.notify(summary, { timeoutMs: 6000 });
|
|
133
|
+
}).catch((error) => ctx.logger.warn('dsh-tui: tui/rewind-done dispatch failed: %o', error));
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
ctx.logger.warn('dsh-tui: tui/rewind-done dispatch failed: %o', error);
|
|
137
|
+
}
|
|
138
|
+
deps.notifySessionSwitched('rewind', String(childId), sourceSessionId);
|
|
139
|
+
return row.text;
|
|
121
140
|
}
|
|
122
141
|
catch (error) {
|
|
123
|
-
|
|
142
|
+
reservation.abandon();
|
|
143
|
+
throw error;
|
|
124
144
|
}
|
|
125
|
-
deps.notifySessionSwitched('rewind', String(childId), sourceSessionId);
|
|
126
|
-
return row.text;
|
|
127
145
|
};
|
|
128
146
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-tree-actions.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/session-tree-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"session-tree-actions.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/session-tree-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAUvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACtD,KAAK,eAAe,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,CAAC,CAAA;AAYnF,+GAA+G;AAC/G,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACpC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC,CAAA;IACjF,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAC9B,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,YAAY,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,CAAA;IAC1K,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CACnG,IAEa,WAAW,MAAM,EAAE,KAAK,MAAM,EAAE,OAAM,QAAQ,GAAG,MAAiB,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAyH1G"}
|