@@ -192,83 +192,16 @@ try_clipboard_cmd_override(std::string* error_out, bool& clip_handled) {
192192// Requires Kitty (TERM=xterm-kitty / KITTY_WINDOW_ID) and `kitten` on
193193// PATH. Returns nullopt (no error_out) when not under Kitty so the
194194// caller falls through to the native path.
195- std::optional<ClipboardImage> try_kitty_clipboard (std::string* error_out) {
196- #if !defined(_WIN32)
197- // Don't gate on TERM / KITTY_WINDOW_ID: SSH strips KITTY_WINDOW_ID
198- // and may rewrite TERM, so they're unreliable on a remote host.
199- // Presence of the `kitten` binary plus "no native clipboard" (the
200- // only context we're called in) is signal enough — if the terminal
201- // on the other end of the TTY isn't actually Kitty, the kitten's
202- // OSC handshake gets no reply and the `timeout` below bounds the
203- // wait, after which we fall through.
204- if (!tool_in_path (" kitten" )) return std::nullopt ;
205-
206- // Unique temp path. tmpnam is racy in general but fine here: we own
207- // the name, write+read+unlink it immediately, no attacker window in
208- // a single-user TTY session.
209- std::string tmp = " /tmp/agentty-kclip-" + std::to_string (::getpid ()) + " .bin" ;
210-
211- // --get-clipboard --mime 'image/*' picks the first image MIME the
212- // clipboard offers; kitten transcodes raster formats to what we ask.
213- // We request a concrete png file so kitten emits PNG.
214- //
215- // `timeout` guards against a stuck terminal handshake freezing the
216- // UI thread — the kitten waits on a terminal reply that may never
217- // come (terminal not Kitty after all, OSC swallowed, perms denied).
218- // 5s is generous for a clipboard round-trip incl. a permission tap.
219- std::string have_timeout = tool_in_path (" timeout" ) ? " timeout 5 " : " " ;
220- std::string errlog = tmp + " .err" ;
221- std::string cmd = have_timeout
222- + " kitten clipboard --get-clipboard --mime 'image/png' "
223- + tmp + " </dev/tty >/dev/tty 2>" + errlog;
224- int rc = std::system (cmd.c_str ());
225-
226- CaptureResult r;
227- if (FILE * f = std::fopen (tmp.c_str (), " rb" )) {
228- char buf[8192 ];
229- std::size_t n;
230- while (r.bytes .size () < kCap && (n = std::fread (buf, 1 , sizeof (buf), f)) > 0 )
231- r.bytes .append (buf, n);
232- std::fclose (f);
233- r.status = 0 ;
234- }
235-
236- // Optional diagnostics: AGENTTY_DEBUG_FILE captures the kitten's
237- // exit code, captured bytes, and its stderr so a failed paste can
238- // be diagnosed without guessing.
239- if (const char * dbg = util::env::get_or_null<util::env::Var::DebugFile>()) {
240- std::string kerr;
241- if (FILE * ef = std::fopen (errlog.c_str (), " rb" )) {
242- char b[1024 ]; std::size_t n;
243- while ((n = std::fread (b, 1 , sizeof (b), ef)) > 0 ) kerr.append (b, n);
244- std::fclose (ef);
245- }
246- if (FILE * lf = std::fopen (dbg, " a" )) {
247- std::fprintf (lf,
248- " [kitty-clip] cmd=%s rc=%d bytes=%zu stderr=%.500s\n " ,
249- cmd.c_str (), rc, r.bytes .size (), kerr.c_str ());
250- std::fclose (lf);
251- }
252- }
253- std::remove (errlog.c_str ());
254- std::remove (tmp.c_str ());
255-
256- if (r.bytes .empty ()) {
257- if (error_out)
258- *error_out = " Kitty clipboard had no image "
259- " (copy an image, then Ctrl+V; if a permission "
260- " prompt appeared, accept it and retry)" ;
261- return std::nullopt ;
262- }
263- if (auto img = wrap (std::move (r))) return img;
264- if (error_out)
265- *error_out = " Kitty clipboard returned non-image data" ;
266- return std::nullopt ;
267- #else
268- (void )error_out;
269- return std::nullopt ;
270- #endif
271- }
195+ // NOTE: an earlier version shelled out to `kitten clipboard` here to
196+ // fetch the image over Kitty's terminal protocol in a plain SSH session.
197+ // That is fundamentally unsafe from inside a live TUI: the kitten reads
198+ // and writes the controlling /dev/tty that agentty already holds in raw
199+ // mode (bracketed paste, kitty keyboard, mouse, alt-screen). On exit the
200+ // kitten resets terminal modes to its own defaults, corrupting agentty's
201+ // terminal state and crashing the session on the next input. A correct
202+ // implementation must do the clipboard-read escape INSIDE maya's input
203+ // loop (which owns the tty), not via a subprocess. Removed until that
204+ // exists. AGENTTY_CLIPBOARD_CMD remains the supported escape hatch.
272205
273206} // namespace
274207
@@ -309,23 +242,17 @@ std::optional<ClipboardImage> read_clipboard_image(std::string* error_out) {
309242
310243 if (!has_wl_paste && !has_xclip) {
311244 // No display server reachable usually means a headless / SSH /
312- // airgap host. The image is on the user's laptop, not here. If
313- // we're running under Kitty, the kitten can fetch it over the
314- // terminal's own clipboard protocol — works in a plain SSH
315- // session, no clipboard tool needed on this host.
316- std::string kitty_err;
317- if (auto img = try_kitty_clipboard (&kitty_err)) return img;
318-
245+ // airgap host. The image is on the user's laptop, not here —
246+ // nothing on this machine can read it. Point at the override.
319247 const char * disp = std::getenv (" DISPLAY" );
320248 const char * wl = std::getenv (" WAYLAND_DISPLAY" );
321249 const bool headless = (!disp || !*disp) && (!wl || !*wl);
322250 if (headless) {
323- if (!kitty_err.empty ()) return fail_owned (std::move (kitty_err));
324251 return fail (
325252 " no clipboard on this host (headless / SSH). The image is "
326- " on your laptop, not the remote. Use a Kitty terminal (its "
327- " `kitten` fetches the clipboard over SSH), set "
328- " AGENTTY_CLIPBOARD_CMD , or attach the image by path" );
253+ " on your laptop, not the remote — set AGENTTY_CLIPBOARD_CMD "
254+ " to a command that prints the laptop's clipboard image to "
255+ " stdout , or attach the image by path" );
329256 }
330257 return fail (wayland
331258 ? " no clipboard tool — install wl-clipboard "
0 commit comments