diff --git a/extensions/pi/mempalace.ts b/extensions/pi/mempalace.ts index 18c835c..4f48fc8 100644 --- a/extensions/pi/mempalace.ts +++ b/extensions/pi/mempalace.ts @@ -1183,20 +1183,37 @@ export default async function mempalaceExtension(pi: ExtensionAPI) { // emulator on the human's machine. Opt-in because writing raw // escapes to stdout is a behaviour change on shared machines, // not because it is unreliable. + // =kitty =osc777 force one protocol, skipping detection entirely. // =0 silent, for anyone who wants the mailbox without pings. // + // WHY FORCING EXISTS — measured on tor-ms22 2026-08-26, and it invalidates + // detection in exactly the deployment this ships in. Terminal identity lives in + // env vars set by the emulator (KITTY_WINDOW_ID, TERM_PROGRAM) and `docker exec` + // does NOT forward them: inside the container pi sees only TERM=xterm-256color + // no matter what is rendering it. So detection can never see Kitty from in here + // and always falls through to OSC 777, which Kitty does not implement — on a + // containerised client the notification would silently do nothing, the worst + // possible failure for a feature whose whole job is to break a silence. Naming + // the protocol ends the guessing. + // // Fired only when something is actually due, i.e. the same condition as the // delivery itself: a notification that fires on an empty poll would teach its // reader to ignore it, which is the failure this whole feature exists to undo. const mailboxNotifyMode = (process.env.MEMPALACE_MAILBOX_NOTIFY ?? "").trim().toLowerCase(); const mailboxNotifyEnabled = mailboxNotifyMode !== "0" && mailboxNotifyMode !== "off"; - const mailboxNotifyDesktop = mailboxNotifyMode === "desktop"; + const mailboxNotifyTerminal = + mailboxNotifyMode === "desktop" || + mailboxNotifyMode === "kitty" || + mailboxNotifyMode === "osc777"; /** Terminal-native notification. Best-effort; never throws into a handler. */ const notifyTerminal = (title: string, body: string): void => { try { const esc = (s: string): string => s.replace(/[\x00-\x1f\x7f;]/g, " "); - if (process.env.KITTY_WINDOW_ID) { + if ( + mailboxNotifyMode === "kitty" || + (mailboxNotifyMode === "desktop" && !!process.env.KITTY_WINDOW_ID) + ) { process.stdout.write(`\x1b]99;i=1:d=0;${esc(title)}\x1b\\`); process.stdout.write(`\x1b]99;i=1:p=body;${esc(body)}\x1b\\`); return; @@ -1257,7 +1274,7 @@ export default async function mempalaceExtension(pi: ExtensionAPI) { } catch { /* ignore: UI may be gone by the time the poll resolves */ } - if (mailboxNotifyDesktop) notifyTerminal("MemPalace mailbox", summary); + if (mailboxNotifyTerminal) notifyTerminal("MemPalace mailbox", summary); } } catch { /* best-effort delivery: never break a turn over a mailbox read */