fix: scope opencode hook to main agent and adopt new sessions
refs #765
This commit is contained in:
parent
64a12de1cd
commit
74fa90cecc
|
|
@ -71,7 +71,9 @@ pub fn session_ref_from_report(
|
|||
|
||||
pub fn normalize_session_start_source(value: Option<String>) -> Option<String> {
|
||||
match value.as_deref().map(str::trim) {
|
||||
Some(source @ ("startup" | "resume" | "clear" | "compact")) => Some(source.to_string()),
|
||||
Some(source @ ("startup" | "resume" | "clear" | "compact" | "new")) => {
|
||||
Some(source.to_string())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -518,6 +520,10 @@ mod tests {
|
|||
normalize_session_start_source(Some("compact".into())),
|
||||
Some("compact".into())
|
||||
);
|
||||
assert_eq!(
|
||||
normalize_session_start_source(Some("new".into())),
|
||||
Some("new".into())
|
||||
);
|
||||
assert_eq!(
|
||||
normalize_session_start_source(Some(" resume ".into())),
|
||||
Some("resume".into())
|
||||
|
|
|
|||
|
|
@ -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=6
|
||||
// HERDR_INTEGRATION_VERSION=7
|
||||
|
||||
import net from "node:net";
|
||||
|
||||
|
|
@ -10,6 +10,11 @@ const SOURCE = "herdr:opencode";
|
|||
const AGENT = "opencode";
|
||||
let reportSeq = Date.now() * 1000;
|
||||
|
||||
// Subagent (task tool) sessions carry a parentID; the main agent session does
|
||||
// not. Their lifecycle events would otherwise clobber the pane's real state, so
|
||||
// learn child session ids from session.created/updated and drop their reports.
|
||||
const childSessions = new Set();
|
||||
|
||||
function nextReportSeq() {
|
||||
reportSeq += 1;
|
||||
return reportSeq;
|
||||
|
|
@ -81,11 +86,15 @@ function request(method, params) {
|
|||
});
|
||||
}
|
||||
|
||||
function reportSession(sessionID) {
|
||||
function reportSession(sessionID, sessionStartSource) {
|
||||
if (!sessionID) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return request("pane.report_agent_session", { agent_session_id: sessionID });
|
||||
const params = { agent_session_id: sessionID };
|
||||
if (sessionStartSource) {
|
||||
params.session_start_source = sessionStartSource;
|
||||
}
|
||||
return request("pane.report_agent_session", params);
|
||||
}
|
||||
|
||||
function reportState(state, sessionID) {
|
||||
|
|
@ -107,6 +116,9 @@ export const HerdrAgentStatePlugin = async () => {
|
|||
|
||||
return {
|
||||
"chat.message": async ({ sessionID }) => {
|
||||
if (sessionID && childSessions.has(sessionID)) {
|
||||
return;
|
||||
}
|
||||
await reportState("working", sessionID);
|
||||
},
|
||||
event: async ({ event }) => {
|
||||
|
|
@ -114,8 +126,21 @@ export const HerdrAgentStatePlugin = async () => {
|
|||
const properties = event?.properties ?? {};
|
||||
const sessionID = sessionIDFromProperties(properties);
|
||||
|
||||
const info = properties.info;
|
||||
if (info?.id && info.parentID) {
|
||||
childSessions.add(info.id);
|
||||
}
|
||||
if (sessionID && childSessions.has(sessionID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case "session.created":
|
||||
// A root session.created is a genuine new-session start (subagent
|
||||
// creates are dropped above). Signal it so herdr replaces the pane's
|
||||
// prior session id instead of treating the change as cross-talk.
|
||||
await reportSession(sessionID, "new");
|
||||
break;
|
||||
case "session.updated":
|
||||
await reportSession(sessionID);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ const DROID_REMOVED_LIFECYCLE_HOOK_EVENTS: [(&str, &str); 9] = [
|
|||
];
|
||||
const OPENCODE_PLUGIN_INSTALL_NAME: &str = "herdr-agent-state.js";
|
||||
const OPENCODE_PLUGIN_ASSET: &str = include_str!("assets/opencode/herdr-agent-state.js");
|
||||
const OPENCODE_INTEGRATION_VERSION: u32 = 6;
|
||||
const OPENCODE_INTEGRATION_VERSION: u32 = 7;
|
||||
const KILO_PLUGIN_INSTALL_NAME: &str = "herdr-agent-state.js";
|
||||
const KILO_PLUGIN_ASSET: &str = include_str!("assets/kilo/herdr-agent-state.js");
|
||||
const KILO_INTEGRATION_VERSION: u32 = 2;
|
||||
|
|
|
|||
|
|
@ -837,7 +837,7 @@ impl TerminalState {
|
|||
"herdr:codex",
|
||||
"codex",
|
||||
Some("startup" | "clear" | "resume" | "compact")
|
||||
)
|
||||
) | ("herdr:opencode", "opencode", Some("new"))
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -3268,6 +3268,70 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opencode_new_session_ref_replaces_existing_session_ref() {
|
||||
let mut terminal = test_terminal();
|
||||
terminal
|
||||
.set_agent_session_ref(
|
||||
"herdr:opencode".into(),
|
||||
"opencode".into(),
|
||||
crate::agent_resume::AgentSessionRef::id("opencode-old"),
|
||||
Some(20),
|
||||
)
|
||||
.expect("initial session should be accepted");
|
||||
|
||||
let mutation = terminal
|
||||
.set_agent_session_ref_for_session_start(
|
||||
"herdr:opencode".into(),
|
||||
"opencode".into(),
|
||||
crate::agent_resume::AgentSessionRef::id("opencode-new"),
|
||||
Some(21),
|
||||
Some("new".into()),
|
||||
)
|
||||
.expect("new should replace the session");
|
||||
|
||||
assert!(mutation.session_ref_changed);
|
||||
assert_eq!(
|
||||
terminal
|
||||
.persisted_agent_session
|
||||
.as_ref()
|
||||
.map(|session| session.session_ref.value.as_str()),
|
||||
Some("opencode-new")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opencode_session_ref_without_start_source_does_not_replace_existing() {
|
||||
let mut terminal = test_terminal();
|
||||
terminal
|
||||
.set_agent_session_ref(
|
||||
"herdr:opencode".into(),
|
||||
"opencode".into(),
|
||||
crate::agent_resume::AgentSessionRef::id("opencode-old"),
|
||||
Some(20),
|
||||
)
|
||||
.expect("initial session should be accepted");
|
||||
|
||||
// session.updated reports carry no session_start_source, so a different
|
||||
// id must not displace the established session (cross-talk guard).
|
||||
let mutation = terminal.set_agent_session_ref_for_session_start(
|
||||
"herdr:opencode".into(),
|
||||
"opencode".into(),
|
||||
crate::agent_resume::AgentSessionRef::id("opencode-other"),
|
||||
Some(21),
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(mutation.is_none());
|
||||
assert_eq!(
|
||||
terminal
|
||||
.persisted_agent_session
|
||||
.as_ref()
|
||||
.map(|session| session.session_ref.value.as_str()),
|
||||
Some("opencode-old")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn different_owner_session_ref_does_not_replace_existing_session_ref() {
|
||||
let mut terminal = test_terminal();
|
||||
|
|
|
|||
Loading…
Reference in New Issue