diff --git a/packages/coding-agent/src/utils/clipboard.ts b/packages/coding-agent/src/utils/clipboard.ts index 9bb10d5c9..b991636a8 100644 --- a/packages/coding-agent/src/utils/clipboard.ts +++ b/packages/coding-agent/src/utils/clipboard.ts @@ -104,15 +104,25 @@ export async function copyToClipboard(text: string): Promise { try { // Verify wl-copy exists (spawn errors are async and won't be caught) execSync("which wl-copy", { stdio: "ignore" }); - // wl-copy with execSync hangs due to fork behavior; use spawn instead - const proc = spawn("wl-copy", [], { stdio: ["pipe", "ignore", "ignore"] }); - proc.stdin.on("error", () => { - // Ignore EPIPE errors if wl-copy exits early + // wl-copy with execSync hangs due to fork behavior; use spawn instead. + // Await the exit code and only claim success on a clean exit, so a + // failed wl-copy falls through to the xclip/OSC 52 fallbacks. + const wlCopyExit = await new Promise((resolve) => { + const proc = spawn("wl-copy", [], { stdio: ["pipe", "ignore", "ignore"] }); + proc.on("error", () => resolve(1)); + proc.on("close", (code) => resolve(code ?? 1)); + proc.stdin.on("error", () => { + // Ignore EPIPE errors if wl-copy exits early + }); + proc.stdin.write(text); + proc.stdin.end(); }); - proc.stdin.write(text); - proc.stdin.end(); - proc.unref(); - copied = true; + if (wlCopyExit === 0) { + copied = true; + } else if (hasX11Display) { + copyToX11Clipboard(options); + copied = true; + } } catch { if (hasX11Display) { copyToX11Clipboard(options);