From bd165f50fd1730dac15b94c4583e94bd40cc8af0 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Sun, 4 Oct 2026 07:56:34 +0000 Subject: [PATCH 01/18] feat(sessionTapes): serve session tapes to the desktop app in a read-only replay mode XUM_REPLAY_TAPES maps workspace ids to tapes; onChat for a mapped workspace is served from the tape (desktop only, after the renderer egress block), and the whole process refuses sends, history changes and provider model creation. Includes the #5587 read-side fixes and the Start Here refusal dialog. Signed-off-by: Thomas Kosiewski --- .../StartHereModal/StartHereModal.tsx | 15 +- .../features/Messages/StreamErrorMessage.tsx | 14 ++ src/browser/hooks/useStartHere.ts | 6 +- src/browser/stores/WorkspaceStore.ts | 37 +++- .../applyWorkspaceChatEventToAggregator.ts | 11 +- src/common/orpc/schemas/errors.ts | 1 + src/common/types/global.d.ts | 3 + src/common/utils/messages/retryEligibility.ts | 4 +- .../utils/sessionTapes/sessionTapeLoader.ts | 10 +- .../utils/sessionTapes/sessionTapeReplay.ts | 46 ++++- src/desktop/main.ts | 34 +++- src/desktop/preload.ts | 2 + src/desktop/sessionTapeReplayEgress.ts | 86 +++++++++ src/desktop/terminalWindowManager.ts | 2 + src/node/orpc/routerSubscriptions.ts | 14 ++ src/node/services/evaluationModelFactory.ts | 8 + src/node/services/providerModelFactory.ts | 16 ++ .../services/sessionTapes/sessionTapeFile.ts | 18 +- .../sessionTapes/sessionTapeRecorder.ts | 25 ++- .../sessionTapes/sessionTapeReplaySource.ts | 180 ++++++++++++++++++ src/node/services/workspaceService.ts | 33 ++++ tests/ui/chat/startHere.test.ts | 76 +++++++- 22 files changed, 609 insertions(+), 32 deletions(-) create mode 100644 src/desktop/sessionTapeReplayEgress.ts create mode 100644 src/node/services/sessionTapes/sessionTapeReplaySource.ts diff --git a/src/browser/components/StartHereModal/StartHereModal.tsx b/src/browser/components/StartHereModal/StartHereModal.tsx index 69f565ed222..f4ebc41733a 100644 --- a/src/browser/components/StartHereModal/StartHereModal.tsx +++ b/src/browser/components/StartHereModal/StartHereModal.tsx @@ -10,6 +10,7 @@ import { import { Button } from "@/browser/components/Button/Button"; import { stopKeyboardPropagation } from "@/browser/utils/events"; import { isEditableElement, KEYBINDS, matchesKeybind } from "@/browser/utils/ui/keybinds"; +import { getErrorMessage } from "@/common/utils/errors"; interface StartHereModalProps { isOpen: boolean; @@ -30,9 +31,12 @@ export const StartHereModal: React.FC = ({ confirmDisabled = false, }) => { const [isExecuting, setIsExecuting] = useState(false); + // Why the last confirmation was refused; shown until the next attempt or cancel. + const [error, setError] = useState(undefined); const handleCancel = useCallback(() => { if (!isExecuting) { + setError(undefined); onClose(); } }, [isExecuting, onClose]); @@ -40,11 +44,13 @@ export const StartHereModal: React.FC = ({ const handleConfirm = useCallback(async () => { if (isExecuting || confirmDisabled) return; setIsExecuting(true); + setError(undefined); try { await onConfirm(); onClose(); - } catch (error) { - console.error("Start Here error:", error); + } catch (confirmError) { + setError(getErrorMessage(confirmError)); + } finally { setIsExecuting(false); } }, [isExecuting, confirmDisabled, onConfirm, onClose]); @@ -88,6 +94,11 @@ export const StartHereModal: React.FC = ({ This will start a new context from this message and preserve earlier chat history. + {error && ( +
+ {error} +
+ )}