diff --git a/src/renderer/src/components/terminal-pane/terminal-agent-paste-bracketing.test.ts b/src/renderer/src/components/terminal-pane/terminal-agent-paste-bracketing.test.ts index 003ab987f7d..e30764b788e 100644 --- a/src/renderer/src/components/terminal-pane/terminal-agent-paste-bracketing.test.ts +++ b/src/renderer/src/components/terminal-pane/terminal-agent-paste-bracketing.test.ts @@ -110,6 +110,16 @@ describe('shouldForceBracketedMultilinePasteForPane', () => { ).toBe(true) }) + it('degrades to the pre-fix path instead of throwing on a malformed pane key', () => { + // Why: makePaneKey throws on a non-UUID leaf or a tabId containing ':'. The throw + // would escape before the paste helper's catch is attached, making the paste a + // silent no-op with no error surface. + expect(() => decide({ leafId: 'not-a-uuid' })).not.toThrow() + expect(decide({ leafId: 'not-a-uuid' })).toBe(false) + expect(() => decide({ tabId: 'tab:with:colons' })).not.toThrow() + expect(decide({ tabId: 'tab:with:colons' })).toBe(false) + }) + it('still brackets when shellForeground is latched true while an agent owns the pane', () => { // Why: shellForeground is republished only at OSC 133 boundaries, so a shell with // no 133 integration leaves it true while an agent runs. Measured live: vetoing on diff --git a/src/renderer/src/components/terminal-pane/terminal-agent-paste-bracketing.ts b/src/renderer/src/components/terminal-pane/terminal-agent-paste-bracketing.ts index 4a617efc4d5..13b6c69231f 100644 --- a/src/renderer/src/components/terminal-pane/terminal-agent-paste-bracketing.ts +++ b/src/renderer/src/components/terminal-pane/terminal-agent-paste-bracketing.ts @@ -21,6 +21,13 @@ import type { PaneForegroundAgentEntry } from '../../store/slices/pane-foregroun * in transit (ConPTY, replay) — never an opt-out. Keep this narrow for that reason. * Bracketing a program that never negotiated is measurably worse than useless: the * markers arrive as literal payload bytes and ICRNL still turns the CR into a submit. + * + * Why this diverges from terminal-ctrl-enter / terminal-windows-shift-enter, which veto + * on shellForeground/routingRevoked and require routingTrusted: those resolvers decide + * where to ROUTE input bytes, so a forged identity misdelivers keystrokes. This one only + * decides whether to WRAP a paste, and the payload is ESC-sanitized downstream, so the + * worst a forged identity buys is a literal ESC[200~ printed at a program. Do not + * "align" this with those gates — measured live, that reinstates the submit bug. */ export function shouldForceBracketedMultilinePasteForPane({ isWindowsClient, @@ -38,8 +45,20 @@ export function shouldForceBracketedMultilinePasteForPane({ if (isWindowsClient) { return true } - const paneKey = makePaneKey(tabId, leafId) + let paneKey: string + try { + paneKey = makePaneKey(tabId, leafId) + } catch { + // Why: unreachable today (pane.leafId is a minted UUID), but a legacy/malformed + // layout must degrade to the pre-fix path, not throw out of the paste handler -- + // the throw would escape before pasteTerminalClipboard's catch is attached and + // the paste would be a silent no-op with no error surface. + return false + } // Why: a process-table confirmed agent is the strongest evidence there is. + // Note this branch is dead for remote-runtime and SSH panes: isForegroundTrackingAllowed + // (pty-connection) returns false for them, so paneForegroundAgentByPaneKey stays empty + // and the status row below is the only evidence a remote pane ever has. if (isTuiAgent(paneForegroundAgentByPaneKey[paneKey]?.agent)) { return true }