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
|
// emulator on the human's machine. Opt-in because writing raw
|
||||||
// escapes to stdout is a behaviour change on shared machines,
|
// escapes to stdout is a behaviour change on shared machines,
|
||||||
// not because it is unreliable.
|
// not because it is unreliable.
|
||||||
|
// =kitty =osc777 force one protocol, skipping detection entirely.
|
||||||
// =0 silent, for anyone who wants the mailbox without pings.
|
// =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
|
// 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
|
// 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.
|
// reader to ignore it, which is the failure this whole feature exists to undo.
|
||||||
const mailboxNotifyMode = (process.env.MEMPALACE_MAILBOX_NOTIFY ?? "").trim().toLowerCase();
|
const mailboxNotifyMode = (process.env.MEMPALACE_MAILBOX_NOTIFY ?? "").trim().toLowerCase();
|
||||||
const mailboxNotifyEnabled = mailboxNotifyMode !== "0" && mailboxNotifyMode !== "off";
|
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. */
|
/** Terminal-native notification. Best-effort; never throws into a handler. */
|
||||||
const notifyTerminal = (title: string, body: string): void => {
|
const notifyTerminal = (title: string, body: string): void => {
|
||||||
try {
|
try {
|
||||||
const esc = (s: string): string => s.replace(/[\x00-\x1f\x7f;]/g, " ");
|
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:d=0;${esc(title)}\x1b\\`);
|
||||||
process.stdout.write(`\x1b]99;i=1:p=body;${esc(body)}\x1b\\`);
|
process.stdout.write(`\x1b]99;i=1:p=body;${esc(body)}\x1b\\`);
|
||||||
return;
|
return;
|
||||||
@@ -1257,7 +1274,7 @@ export default async function mempalaceExtension(pi: ExtensionAPI) {
|
|||||||
} catch {
|
} catch {
|
||||||
/* ignore: UI may be gone by the time the poll resolves */
|
/* ignore: UI may be gone by the time the poll resolves */
|
||||||
}
|
}
|
||||||
if (mailboxNotifyDesktop) notifyTerminal("MemPalace mailbox", summary);
|
if (mailboxNotifyTerminal) notifyTerminal("MemPalace mailbox", summary);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
/* best-effort delivery: never break a turn over a mailbox read */
|
/* best-effort delivery: never break a turn over a mailbox read */
|
||||||
|
|||||||
Reference in New Issue
Block a user