mailbox notify: name the protocol, because docker exec hides the terminal
Auto-detection is useless in the deployment this ships in, measured on tor-ms22. Terminal identity lives in env vars set by the emulator (KITTY_WINDOW_ID, TERM_PROGRAM) and docker exec does not forward them — pi inside the container sees only TERM=xterm-256color no matter what is rendering it. So the =desktop detection can never see Kitty from in there and always falls through to OSC 777, which Kitty does not implement. Result: on a containerised client the ping silently does nothing, which is the worst available failure for a feature whose entire purpose is to break a silence. Adds MEMPALACE_MAILBOX_NOTIFY=kitty and =osc777 to force one protocol and skip detection. =desktop keeps the notify.ts-style autodetect for the non-container case, unset still means in-TUI only, 0/off still silent. The Windows toast branch is dropped from the comment rather than the code path it never had here: it needs powershell.exe on PATH, which is a WSL fact, not a Linux-container one.
This commit is contained in:
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user