aboutsummaryrefslogtreecommitdiff
path: root/src/integration/assets/opencode
diff options
context:
space:
mode:
authorMark Jaquith <mark@jaquith.me>2026-09-05 17:08:19 -0400
committerGitHub <noreply@github.com>2026-09-06 00:08:19 +0300
commitec05328a4413e38ebdcb84365938345e9cf47431 (patch)
tree3f280531f2c81d00d2204ca7e7fb0d3b161072a6 /src/integration/assets/opencode
parent6f57f2f62c5d2591647262caf15bc3c962b9c9f8 (diff)
fix: prevent OpenCode child prompts from leaving status blocked (#3669)
Child permission and question reports omitted the session ID, clearing Herdr’s root session identity and preventing later lifecycle reports from clearing the blocked state. Track child-to-parent relationships and report prompt transitions with their root session ID, including for nested children. This preserves identity and lets existing ownership checks reject unrelated sessions. Add regression coverage for prompt routing and lifecycle transitions, and bump the OpenCode integration version to 11.
Diffstat (limited to 'src/integration/assets/opencode')
-rw-r--r--src/integration/assets/opencode/herdr-agent-state.js14
-rw-r--r--src/integration/assets/opencode/herdr-agent-state.test.ts39
-rw-r--r--src/integration/assets/opencode/herdr-tui-session.js2
3 files changed, 43 insertions, 12 deletions
diff --git a/src/integration/assets/opencode/herdr-agent-state.js b/src/integration/assets/opencode/herdr-agent-state.js
index 2840c221..45032335 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=10
+// HERDR_INTEGRATION_VERSION=11
import net from "node:net";
@@ -13,8 +13,8 @@ let requestChain = Promise.resolve();
let reportedRootSessionID;
// Track child sessions so their events cannot replace the pane's root session.
-// Their user prompts still project state without attaching the child session id.
-const childSessions = new Set();
+// User prompts carry the root id to preserve its identity and cross-talk guard.
+const childSessions = new Map();
const CHILD_EVENT_STATES = new Map([
["permission.asked", "blocked"],
["question.asked", "blocked"],
@@ -141,12 +141,16 @@ export const HerdrAgentStatePlugin = async () => {
const info = properties.info;
if (info?.id && info.parentID) {
- childSessions.add(info.id);
+ childSessions.set(info.id, info.parentID);
}
if (sessionID && childSessions.has(sessionID)) {
const state = CHILD_EVENT_STATES.get(type);
if (state) {
- await reportState(state);
+ let rootSessionID = sessionID;
+ while (childSessions.has(rootSessionID)) {
+ rootSessionID = childSessions.get(rootSessionID);
+ }
+ await reportState(state, rootSessionID);
}
return;
}
diff --git a/src/integration/assets/opencode/herdr-agent-state.test.ts b/src/integration/assets/opencode/herdr-agent-state.test.ts
index 6f8ee590..a3eea848 100644
--- a/src/integration/assets/opencode/herdr-agent-state.test.ts
+++ b/src/integration/assets/opencode/herdr-agent-state.test.ts
@@ -167,7 +167,6 @@ test("reports child prompts without replacing the root session", async () => {
event: {
type: "session.created",
properties: {
- sessionID: "child-session",
info: { id: "child-session", parentID: "root-session" },
},
},
@@ -188,11 +187,39 @@ test("reports child prompts without replacing the root session", async () => {
"working",
]);
expect(requests.map(requestSessionID)).toEqual([
- undefined,
- undefined,
- undefined,
- undefined,
- undefined,
+ "root-session",
+ "root-session",
+ "root-session",
+ "root-session",
+ "root-session",
+ ]);
+});
+
+test("routes nested child prompts to their own root, not the last active root", async () => {
+ const plugin = await loadPlugin();
+ for (const info of [
+ { id: "child-session", parentID: "root-session" },
+ { id: "nested-session", parentID: "child-session" },
+ ]) {
+ await plugin.event({ event: { type: "session.created", properties: { info } } });
+ }
+ await plugin["chat.message"]({ sessionID: "other-root" });
+ await plugin.event({
+ event: { type: "permission.asked", properties: { sessionID: "nested-session" } },
+ });
+ await plugin.event({
+ event: { type: "permission.replied", properties: { sessionID: "nested-session" } },
+ });
+ await plugin.event({
+ event: { type: "session.idle", properties: { sessionID: "nested-session" } },
+ });
+ await plugin["chat.message"]({ sessionID: "nested-session" });
+
+ expect(requests.map(requestState)).toEqual(["working", "blocked", "working"]);
+ expect(requests.map(requestSessionID)).toEqual([
+ "other-root",
+ "root-session",
+ "root-session",
]);
});
diff --git a/src/integration/assets/opencode/herdr-tui-session.js b/src/integration/assets/opencode/herdr-tui-session.js
index b5be5bbc..f4f08d57 100644
--- a/src/integration/assets/opencode/herdr-tui-session.js
+++ b/src/integration/assets/opencode/herdr-tui-session.js
@@ -1,7 +1,7 @@
// installed by herdr
// managed by herdr; reinstalling or updating the integration overwrites this file.
// HERDR_INTEGRATION_ID=opencode-tui
-// HERDR_INTEGRATION_VERSION=10
+// HERDR_INTEGRATION_VERSION=11
import net from "node:net";