// Shows the pi session name in herdr's (or tmuxrdr's) Agent sidebar, in place of // the "pi" label. // // herdr's own pi integration (herdr-agent-state.ts, which herdr installs and // overwrites) reports lifecycle state only. This sits beside it and reports // display-only pane metadata: `display_agent` replaces the `agent` token in // sidebar rows. `agent: "pi"` guards it, so it stops applying once the pane no // longer runs pi. With no session name, the label is cleared back to "pi". // // Reported on session start (including /resume, /new and reloads) and whenever // the name changes (/name, --name, or an extension's pi.setSessionName()). // TUI sessions only: print, JSON and RPC runs launched from inside a pane (a // review panel, a subagent) inherit the pane's HERDR_* variables and would // otherwise rename the pane after themselves. // // API: pane.report_metadata, herdr socket API (herdr 0.9.3). import net from "node:net"; const socketPath = process.env.HERDR_SOCKET_PATH; const paneId = process.env.HERDR_PANE_ID; const source = "user:pi-session-name"; function enabled(): boolean { return process.env.HERDR_ENV === "1" && !!socketPath && !!paneId; } function send(request: unknown, timeoutMs = 1000): Promise { return new Promise((resolve) => { const endpoint = process.platform === "win32" ? `\\\\.\\pipe\\${socketPath}` : socketPath!; const socket = net.createConnection(endpoint); const finish = () => { clearTimeout(timer); socket.destroy(); resolve(); }; const timer = setTimeout(finish, timeoutMs); timer.unref?.(); socket.on("error", finish); socket.on("end", finish); socket.on("data", finish); socket.on("connect", () => socket.write(`${JSON.stringify(request)}\n`)); }); } let seq = Date.now() * 1000; function report(name: string | undefined): Promise { const label = name?.trim(); seq += 1; return send({ id: `${source}:${seq}`, method: "pane.report_metadata", params: { pane_id: paneId, source, agent: "pi", seq, ...(label ? { display_agent: label } : { clear_display_agent: true }), }, }); } export default function (pi: any) { if (!enabled()) { return; } let active = false; pi.on("session_start", async (_event: unknown, ctx: any) => { active = ctx?.mode === "tui"; if (active) { await report(pi.getSessionName()); } }); pi.on("session_info_changed", async (event: { name?: string }) => { if (active) { await report(event?.name); } }); }