aboutsummaryrefslogtreecommitdiff
path: root/src/integration/assets
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-02 21:20:28 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-02 21:25:32 +0300
commit295b09ca25edbccac65401ee84fa420dd3888aad (patch)
treed54ed875518e042869ed3f43e95f0b9ee59f5d34 /src/integration/assets
parent48641e253f1aa3886b470cbebec60103247a7622 (diff)
feat: make claude codex opencode hooks session-only
Diffstat (limited to 'src/integration/assets')
-rw-r--r--src/integration/assets/claude/herdr-agent-state.sh27
-rw-r--r--src/integration/assets/codex/herdr-agent-state.sh23
-rw-r--r--src/integration/assets/opencode/herdr-agent-state.js74
3 files changed, 27 insertions, 97 deletions
diff --git a/src/integration/assets/claude/herdr-agent-state.sh b/src/integration/assets/claude/herdr-agent-state.sh
index e2a970d3..12068a69 100644
--- a/src/integration/assets/claude/herdr-agent-state.sh
+++ b/src/integration/assets/claude/herdr-agent-state.sh
@@ -3,7 +3,7 @@
# managed by herdr; reinstalling or updating the integration overwrites this file.
# add custom hooks beside this file instead of editing it.
# HERDR_INTEGRATION_ID=claude
-# HERDR_INTEGRATION_VERSION=4
+# HERDR_INTEGRATION_VERSION=5
set -eu
@@ -13,7 +13,7 @@ trap 'rm -f "$hook_input_file"' EXIT HUP INT TERM
cat >"$hook_input_file" 2>/dev/null || true
case "$action" in
- working|idle|blocked|release) ;;
+ session) ;;
*) exit 0 ;;
esac
@@ -55,39 +55,24 @@ if hook_event_name == "SubagentStop":
# to durable working, but Claude recap/away-summary can emit it after the
# main turn has already stopped. Never let it revive an idle pane.
raise SystemExit(0)
-if is_subagent and action in ("idle", "release"):
- # Subagent completion must not make the parent pane look done early.
- raise SystemExit(0)
-
request_id = f"{source}:{int(time.time() * 1000)}:{random.randrange(1_000_000):06d}"
report_seq = time.time_ns()
session_id = hook_input.get("session_id")
agent_session_id = session_id if isinstance(session_id, str) and session_id else None
-if action == "release":
+if agent_session_id:
request = {
"id": request_id,
- "method": "pane.release_agent",
+ "method": "pane.report_agent_session",
"params": {
"pane_id": pane_id,
"source": source,
"agent": "claude",
"seq": report_seq,
+ "agent_session_id": agent_session_id,
},
}
else:
- request = {
- "id": request_id,
- "method": "pane.report_agent",
- "params": {
- "pane_id": pane_id,
- "source": source,
- "agent": "claude",
- "state": action,
- "seq": report_seq,
- },
- }
- if agent_session_id:
- request["params"]["agent_session_id"] = agent_session_id
+ raise SystemExit(0)
try:
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
diff --git a/src/integration/assets/codex/herdr-agent-state.sh b/src/integration/assets/codex/herdr-agent-state.sh
index 5341d71c..385c9a98 100644
--- a/src/integration/assets/codex/herdr-agent-state.sh
+++ b/src/integration/assets/codex/herdr-agent-state.sh
@@ -3,7 +3,7 @@
# managed by herdr; reinstalling or updating the integration overwrites this file.
# add custom hooks beside this file instead of editing it.
# HERDR_INTEGRATION_ID=codex
-# HERDR_INTEGRATION_VERSION=4
+# HERDR_INTEGRATION_VERSION=5
set -eu
@@ -13,7 +13,7 @@ trap 'rm -f "$hook_input_file"' EXIT HUP INT TERM
cat >"$hook_input_file" 2>/dev/null || true
case "$action" in
- working|idle|blocked|release) ;;
+ session) ;;
*) exit 0 ;;
esac
@@ -52,31 +52,20 @@ request_id = f"{source}:{int(time.time() * 1000)}:{random.randrange(1_000_000):0
report_seq = time.time_ns()
session_id = hook_input.get("session_id")
agent_session_id = session_id if isinstance(session_id, str) and session_id else None
-if action == "release":
+if agent_session_id:
request = {
"id": request_id,
- "method": "pane.release_agent",
+ "method": "pane.report_agent_session",
"params": {
"pane_id": pane_id,
"source": source,
"agent": "codex",
"seq": report_seq,
+ "agent_session_id": agent_session_id,
},
}
else:
- request = {
- "id": request_id,
- "method": "pane.report_agent",
- "params": {
- "pane_id": pane_id,
- "source": source,
- "agent": "codex",
- "state": action,
- "seq": report_seq,
- },
- }
- if agent_session_id:
- request["params"]["agent_session_id"] = agent_session_id
+ raise SystemExit(0)
try:
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
diff --git a/src/integration/assets/opencode/herdr-agent-state.js b/src/integration/assets/opencode/herdr-agent-state.js
index ed0958fd..c2fd79a6 100644
--- a/src/integration/assets/opencode/herdr-agent-state.js
+++ b/src/integration/assets/opencode/herdr-agent-state.js
@@ -2,7 +2,7 @@
// managed by herdr; reinstalling or updating the integration overwrites this file.
// add custom hooks/plugins beside this file instead of editing it.
// HERDR_INTEGRATION_ID=opencode
-// HERDR_INTEGRATION_VERSION=3
+// HERDR_INTEGRATION_VERSION=4
import net from "node:net";
@@ -20,7 +20,10 @@ function sessionIDFromProperties(properties) {
: undefined;
}
-function reportState(action, sessionID) {
+function reportSession(sessionID) {
+ if (!sessionID) {
+ return Promise.resolve();
+ }
const paneId = process.env.HERDR_PANE_ID;
const socketPath = process.env.HERDR_SOCKET_PATH;
@@ -31,26 +34,16 @@ function reportState(action, sessionID) {
const requestId = `${SOURCE}:${Date.now()}:${Math.floor(Math.random() * 1_000_000)
.toString()
.padStart(6, "0")}`;
- const params =
- action === "release"
- ? {
- pane_id: paneId,
- source: SOURCE,
- agent: "opencode",
- seq: nextReportSeq(),
- }
- : {
- pane_id: paneId,
- source: SOURCE,
- agent: "opencode",
- state: action,
- seq: nextReportSeq(),
- ...(sessionID ? { agent_session_id: sessionID } : {}),
- };
const request = {
id: requestId,
- method: action === "release" ? "pane.release_agent" : "pane.report_agent",
- params,
+ method: "pane.report_agent_session",
+ params: {
+ pane_id: paneId,
+ source: SOURCE,
+ agent: "opencode",
+ seq: nextReportSeq(),
+ agent_session_id: sessionID,
+ },
};
return new Promise((resolve) => {
@@ -81,53 +74,16 @@ export const HerdrAgentStatePlugin = async () => {
}
return {
- dispose: async () => {
- await reportState("release");
- },
event: async ({ event }) => {
const type = event?.type;
const properties = event?.properties ?? {};
const sessionID = sessionIDFromProperties(properties);
switch (type) {
- case "permission.asked":
- case "question.asked":
- await reportState("blocked", sessionID);
- break;
- case "permission.replied": {
- const reply = properties.reply ?? properties.response;
- if (reply === "reject") {
- await reportState("idle", sessionID);
- } else if (reply === "once" || reply === "always") {
- await reportState("working", sessionID);
- }
- break;
- }
- case "question.replied":
- await reportState("working", sessionID);
- break;
- case "question.rejected":
- await reportState("idle", sessionID);
- break;
case "session.created":
case "session.updated":
- // session.created and session.updated are metadata events; lifecycle
- // state comes from session.status and the deprecated session.idle.
- break;
- case "session.status": {
- const status =
- typeof properties.status === "string"
- ? properties.status
- : properties.status?.type;
- if (status === "busy" || status === "retry") {
- await reportState("working", sessionID);
- } else if (status === "idle") {
- await reportState("idle", sessionID);
- }
- break;
- }
- case "session.idle":
- await reportState("idle", sessionID);
+ case "session.status":
+ await reportSession(sessionID);
break;
default:
break;