diff --git a/src/renderer/src/components/terminal-pane/pty-connection/fresh-spawn-follow-reset.ts b/src/renderer/src/components/terminal-pane/pty-connection/fresh-spawn-follow-reset.ts index b1fb8d1d642..cda86479fbc 100644 --- a/src/renderer/src/components/terminal-pane/pty-connection/fresh-spawn-follow-reset.ts +++ b/src/renderer/src/components/terminal-pane/pty-connection/fresh-spawn-follow-reset.ts @@ -85,7 +85,9 @@ export function bindFreshSpawnFollowReset(session: ConnectPanePtySession): void return '' } } - return tail.slice(-TERMINAL_RENDERER_RISK_SCAN_TAIL_CHARS) + // Why: keep the head — a suffix slice of an oversized parameter run drops + // the `\x1b[` the next scan needs to recognize the carried sequence. + return tail.slice(0, TERMINAL_RENDERER_RISK_SCAN_TAIL_CHARS) } session.foregroundRendererRiskOutputPrefersRenderRefresh = function (data: string): boolean { diff --git a/src/renderer/src/components/terminal-pane/pty-connection/fresh-spawn-start.ts b/src/renderer/src/components/terminal-pane/pty-connection/fresh-spawn-start.ts index c722170dc6b..0110f8a50d7 100644 --- a/src/renderer/src/components/terminal-pane/pty-connection/fresh-spawn-start.ts +++ b/src/renderer/src/components/terminal-pane/pty-connection/fresh-spawn-start.ts @@ -114,6 +114,12 @@ export function bindStartFreshSpawn(session: ConnectPanePtySession): void { : session.transport.getPtyId() if (resolvedPtyId && !session.claimCapturedDirectSshRetryPty(resolvedPtyId)) { session.finishReattachLiveDataDeferral(false, outputCallbacks.generation) + // Why: an outstanding declare keeps main's cooperation gate suppressing + // this paneKey's daemon-snapshot seed until something releases it. + const gen = await preSignalPromise + if (typeof gen === 'number') { + void window.api.pty.clearPendingPaneSerializer(session.cacheKey, gen).catch(() => {}) + } return null } const connectResult = @@ -193,7 +199,9 @@ export function bindStartFreshSpawn(session: ConnectPanePtySession): void { session.reconcilePtySizeAfterSpawn(resolvedPtyId, session.cols, session.rows) } const gen = await preSignalPromise - if (resolvedPtyId && (typeof gen === 'number' || isRemoteRuntimePtyId(resolvedPtyId))) { + // Why: a bound PTY owns the renderer serializer even when the declare was + // rejected; the gen token only settles or clears the pending declaration. + if (resolvedPtyId) { if (!isRemoteRuntimePtyId(resolvedPtyId) || !hasPtySerializer(resolvedPtyId)) { session.registerPaneSerializerFor(resolvedPtyId) } diff --git a/src/renderer/src/components/terminal-pane/pty-connection/interrupt-input-intent.ts b/src/renderer/src/components/terminal-pane/pty-connection/interrupt-input-intent.ts index b11b289f1cc..1206eeffc5a 100644 --- a/src/renderer/src/components/terminal-pane/pty-connection/interrupt-input-intent.ts +++ b/src/renderer/src/components/terminal-pane/pty-connection/interrupt-input-intent.ts @@ -162,7 +162,10 @@ export function installInterruptInputIntent(session: ConnectPanePtySession): voi session.questionAnsweredInference.observeSentTerminalInput(data) } session.pendingTerminalInputWrite = null - session.sequencedInterruptStatusBaseline = 0 + // Why: an unset baseline must differ from the first `?? null` snapshot so the + // first acknowledged input still advances the sequence. + session.sequencedInterruptStatusBaseline = undefined + session.interruptStatusBaselineSequence = 0 session.setPendingTerminalInputWrite = (promise: Promise): void => { session.pendingTerminalInputWrite = promise void promise.finally(() => { diff --git a/src/renderer/src/components/terminal-pane/pty-connection/reattach-result-handler.ts b/src/renderer/src/components/terminal-pane/pty-connection/reattach-result-handler.ts index 29998677fea..bb45572b7a1 100644 --- a/src/renderer/src/components/terminal-pane/pty-connection/reattach-result-handler.ts +++ b/src/renderer/src/components/terminal-pane/pty-connection/reattach-result-handler.ts @@ -57,13 +57,15 @@ export function bindHandleReattachResult(sessionBag: ConnectPanePtySession): voi coldRestoreStartup?: ColdRestoreAgentResumeStartup | null, attemptGeneration = session.transportStreamGeneration ): Promise => { - session.authoritativeReattachGeneration += 1 if (session.disposed) { return false } if (attemptGeneration !== session.transportStreamGeneration) { return false } + // Why: bump only once this attempt owns the stream, or a superseded result + // would cancel the current attempt's in-flight snapshot prepaint. + session.authoritativeReattachGeneration += 1 const connectResult = result && typeof result === 'object' && 'id' in result ? (result as PtyConnectResult) : null