aboutsummaryrefslogtreecommitdiff
path: root/src/integration/assets
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-04-01 19:30:58 +0300
committerCan Celik <ogulcancelik@gmail.com>2026-04-01 19:32:26 +0300
commitca4270b31fe93c180f7321cb4006cb5e7307d889 (patch)
tree72628e311a1b8e5ebf78c74cbc61d990253dc4c4 /src/integration/assets
parentfb0c9717cfa961a47d63272e622e9e35a001082a (diff)
feat: add authoritative pi hook integration
Diffstat (limited to 'src/integration/assets')
-rw-r--r--src/integration/assets/pi/herdr-agent-state.ts125
1 files changed, 125 insertions, 0 deletions
diff --git a/src/integration/assets/pi/herdr-agent-state.ts b/src/integration/assets/pi/herdr-agent-state.ts
new file mode 100644
index 00000000..f51f05d1
--- /dev/null
+++ b/src/integration/assets/pi/herdr-agent-state.ts
@@ -0,0 +1,125 @@
+// installed by herdr
+// safe to edit. this integration only activates inside herdr-managed panes.
+// @ts-nocheck
+
+import { createConnection } from "node:net";
+
+const HERDR_ENV = process.env.HERDR_ENV;
+const socketPath = process.env.HERDR_SOCKET_PATH;
+const paneId = process.env.HERDR_PANE_ID;
+const source = "herdr:pi";
+
+function enabled() {
+ return HERDR_ENV === "1" && !!socketPath && !!paneId;
+}
+
+function sendRequest(request: unknown): Promise<void> {
+ if (!enabled()) {
+ return Promise.resolve();
+ }
+
+ return new Promise((resolve) => {
+ let done = false;
+ const finish = () => {
+ if (done) return;
+ done = true;
+ socket.destroy();
+ resolve();
+ };
+
+ const socket = createConnection(socketPath!);
+ socket.on("error", finish);
+ socket.on("connect", () => socket.write(`${JSON.stringify(request)}\n`));
+ socket.on("data", finish);
+ socket.on("end", finish);
+ const timeout = setTimeout(finish, 500);
+ timeout.unref?.();
+ });
+}
+
+function sendState(state: "working" | "blocked" | "idle", message?: string): Promise<void> {
+ return sendRequest({
+ id: `${source}:${Date.now()}:${Math.random().toString(36).slice(2)}`,
+ method: "pane.report_agent",
+ params: {
+ pane_id: paneId,
+ source,
+ agent: "pi",
+ state,
+ message,
+ },
+ });
+}
+
+function releaseAgent(): Promise<void> {
+ return sendRequest({
+ id: `${source}:release:${Date.now()}:${Math.random().toString(36).slice(2)}`,
+ method: "pane.release_agent",
+ params: {
+ pane_id: paneId,
+ source,
+ agent: "pi",
+ },
+ });
+}
+
+export default function (pi) {
+ if (!enabled()) {
+ return;
+ }
+
+ let agentActive = false;
+ let blockedCount = 0;
+ let blockedMessage: string | undefined;
+ let lastState: "working" | "blocked" | "idle" | undefined;
+ let lastMessage: string | undefined;
+
+ function desiredState() {
+ if (blockedCount > 0) {
+ return { state: "blocked" as const, message: blockedMessage };
+ }
+ if (agentActive) {
+ return { state: "working" as const, message: undefined };
+ }
+ return { state: "idle" as const, message: undefined };
+ }
+
+ function publishState() {
+ const next = desiredState();
+ if (next.state === lastState && next.message === lastMessage) {
+ return;
+ }
+ lastState = next.state;
+ lastMessage = next.message;
+ void sendState(next.state, next.message);
+ }
+
+ pi.events.on("herdr:blocked", (data) => {
+ if (!data?.active) {
+ blockedCount = Math.max(0, blockedCount - 1);
+ if (blockedCount === 0) {
+ blockedMessage = undefined;
+ }
+ publishState();
+ return;
+ }
+
+ blockedCount += 1;
+ blockedMessage = data.label;
+ publishState();
+ });
+
+ pi.on("agent_start", () => {
+ agentActive = true;
+ publishState();
+ });
+
+ pi.on("agent_end", () => {
+ agentActive = false;
+ publishState();
+ });
+
+ pi.on("session_shutdown", async () => {
+ await releaseAgent();
+ });
+}