summaryrefslogtreecommitdiff
path: root/config
diff options
context:
space:
mode:
authorIgor Soarez <igor@soarez.org>2026-10-05 16:29:50 +0300
committerIgor Soarez <igor@soarez.org>2026-10-05 16:29:50 +0300
commit6a6c5821a0f368a53548f36a0bd63b035b8e1344 (patch)
tree9945da2002a49993e454fa1996bd01730278b7e5 /config
parent09fd7f0294f975f53f72bc3551099f968341419e (diff)
pi: the session name in herdr's Agent sidebar
herdr labels every pi pane "pi", so with several open the rows cannot be told apart. herdr 0.9.3's socket API has pane.report_metadata, whose display_agent replaces that label without touching lifecycle state; this reports the session name there on start and on every rename, auto-named titles included. Interactive sessions only: a pi -p or subagent run from inside a pane inherits its HERDR_* variables and would otherwise rename the pane after itself. Checked against a fake herdr socket: a print-mode run sends nothing, while the socket accepted a control message. Outside herdr it returns at once. The pi APIs it uses -- ctx.mode "tui", getSessionName, session_info_changed's name -- are as in pi 1.0.0, and link/pi.sh links it in like any tracked extension. From change request e053eb.
Diffstat (limited to 'config')
-rw-r--r--config/pi/extensions/herdr-session-name.ts84
1 files changed, 84 insertions, 0 deletions
diff --git a/config/pi/extensions/herdr-session-name.ts b/config/pi/extensions/herdr-session-name.ts
new file mode 100644
index 0000000..63663f1
--- /dev/null
+++ b/config/pi/extensions/herdr-session-name.ts
@@ -0,0 +1,84 @@
+// 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<void> {
+ 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<void> {
+ 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);
+ }
+ });
+}