aboutsummaryrefslogtreecommitdiff
path: root/src/integration
diff options
context:
space:
mode:
Diffstat (limited to 'src/integration')
-rw-r--r--src/integration/assets/claude/herdr-agent-state.ps13
-rw-r--r--src/integration/assets/claude/herdr-agent-state.sh4
-rw-r--r--src/integration/assets/codex/herdr-agent-state.ps122
-rw-r--r--src/integration/assets/codex/herdr-agent-state.sh26
-rw-r--r--src/integration/assets/hermes/__init__.py12
-rw-r--r--src/integration/assets/kilo/herdr-agent-state.js7
-rw-r--r--src/integration/assets/kimi/herdr-agent-state.ps18
-rw-r--r--src/integration/assets/kimi/herdr-agent-state.sh17
-rw-r--r--src/integration/assets/omp/herdr-agent-state.ts55
-rw-r--r--src/integration/assets/opencode/herdr-agent-state.js7
-rw-r--r--src/integration/assets/pi/herdr-agent-state.ts74
-rw-r--r--src/integration/mod.rs124
12 files changed, 212 insertions, 147 deletions
diff --git a/src/integration/assets/claude/herdr-agent-state.ps1 b/src/integration/assets/claude/herdr-agent-state.ps1
index dc78efd2..dcc9d4df 100644
--- a/src/integration/assets/claude/herdr-agent-state.ps1
+++ b/src/integration/assets/claude/herdr-agent-state.ps1
@@ -2,7 +2,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=6
+# HERDR_INTEGRATION_VERSION=7
param([string]$Action = "")
@@ -17,6 +17,7 @@ try {
exit 0
}
+if (-not [string]::IsNullOrWhiteSpace($payload.agent_id)) { exit 0 }
if ($payload.hook_event_name -eq "SubagentStop") { exit 0 }
$sessionId = $payload.session_id
diff --git a/src/integration/assets/claude/herdr-agent-state.sh b/src/integration/assets/claude/herdr-agent-state.sh
index ce072189..cb5e840d 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=6
+# HERDR_INTEGRATION_VERSION=7
set -eu
@@ -50,6 +50,8 @@ if hook_input_file:
hook_event_name = str(hook_input.get("hook_event_name") or "")
is_subagent = bool(hook_input.get("agent_id"))
+if is_subagent:
+ raise SystemExit(0)
if hook_event_name == "SubagentStop":
# SubagentStop is a completion event. Older Herdr integrations mapped it
# to durable working, but Claude recap/away-summary can emit it after the
diff --git a/src/integration/assets/codex/herdr-agent-state.ps1 b/src/integration/assets/codex/herdr-agent-state.ps1
index e56948f5..d837b78a 100644
--- a/src/integration/assets/codex/herdr-agent-state.ps1
+++ b/src/integration/assets/codex/herdr-agent-state.ps1
@@ -2,7 +2,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=5
+# HERDR_INTEGRATION_VERSION=6
param([string]$Action = "")
@@ -17,11 +17,29 @@ try {
exit 0
}
+if ($payload.hook_event_name -and $payload.hook_event_name -ne "SessionStart") { exit 0 }
+
$sessionId = $payload.session_id
if ([string]::IsNullOrWhiteSpace($sessionId)) { exit 0 }
$seq = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
try {
- & herdr pane report-agent-session $env:HERDR_PANE_ID --source herdr:codex --agent codex --seq $seq --agent-session-id $sessionId 2>$null | Out-Null
+ $args = @(
+ "pane",
+ "report-agent-session",
+ $env:HERDR_PANE_ID,
+ "--source",
+ "herdr:codex",
+ "--agent",
+ "codex",
+ "--seq",
+ "$seq",
+ "--agent-session-id",
+ "$sessionId"
+ )
+ if ($payload.hook_event_name -eq "SessionStart" -and $payload.source -is [string] -and -not [string]::IsNullOrWhiteSpace($payload.source)) {
+ $args += @("--session-start-source", "$($payload.source)")
+ }
+ & herdr @args 2>$null | Out-Null
} catch {
}
diff --git a/src/integration/assets/codex/herdr-agent-state.sh b/src/integration/assets/codex/herdr-agent-state.sh
index 385c9a98..b3b08a78 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=5
+# HERDR_INTEGRATION_VERSION=6
set -eu
@@ -48,21 +48,31 @@ if hook_input_file:
except Exception:
hook_input = {}
+hook_event_name = str(hook_input.get("hook_event_name") or "")
+if hook_event_name and hook_event_name != "SessionStart":
+ 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
+session_start_source = hook_input.get("source") if hook_event_name == "SessionStart" else None
+if not isinstance(session_start_source, str) or not session_start_source:
+ session_start_source = None
if agent_session_id:
+ params = {
+ "pane_id": pane_id,
+ "source": source,
+ "agent": "codex",
+ "seq": report_seq,
+ "agent_session_id": agent_session_id,
+ }
+ if session_start_source:
+ params["session_start_source"] = session_start_source
request = {
"id": request_id,
"method": "pane.report_agent_session",
- "params": {
- "pane_id": pane_id,
- "source": source,
- "agent": "codex",
- "seq": report_seq,
- "agent_session_id": agent_session_id,
- },
+ "params": params,
}
else:
raise SystemExit(0)
diff --git a/src/integration/assets/hermes/__init__.py b/src/integration/assets/hermes/__init__.py
index 6bb0fe8a..e6f6971d 100644
--- a/src/integration/assets/hermes/__init__.py
+++ b/src/integration/assets/hermes/__init__.py
@@ -1,7 +1,7 @@
"""Hermes plugin installed by Herdr to report agent lifecycle state."""
# HERDR_INTEGRATION_ID=hermes
-# HERDR_INTEGRATION_VERSION=2
+# HERDR_INTEGRATION_VERSION=3
from __future__ import annotations
@@ -71,10 +71,6 @@ def _report(state: str, **kwargs) -> None:
_send("pane.report_agent", params)
-def _release() -> None:
- _send("pane.release_agent", {})
-
-
def _working(**kwargs) -> None:
_report("working", **kwargs)
@@ -87,11 +83,6 @@ def _idle(**kwargs) -> None:
_report("idle", **kwargs)
-def _finalize(**kwargs) -> None:
- del kwargs
- _release()
-
-
def register(ctx):
ctx.register_hook("on_session_start", _idle)
ctx.register_hook("pre_llm_call", _working)
@@ -102,4 +93,3 @@ def register(ctx):
ctx.register_hook("post_approval_response", _working)
ctx.register_hook("post_llm_call", _idle)
ctx.register_hook("on_session_end", _idle)
- ctx.register_hook("on_session_finalize", _finalize)
diff --git a/src/integration/assets/kilo/herdr-agent-state.js b/src/integration/assets/kilo/herdr-agent-state.js
index aa1a0c1b..fc12919c 100644
--- a/src/integration/assets/kilo/herdr-agent-state.js
+++ b/src/integration/assets/kilo/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=kilo
-// HERDR_INTEGRATION_VERSION=1
+// HERDR_INTEGRATION_VERSION=2
import net from "node:net";
@@ -96,10 +96,6 @@ function reportState(state, sessionID) {
return request("pane.report_agent", params);
}
-function releaseAgent() {
- return request("pane.release_agent", {});
-}
-
export const HerdrAgentStatePlugin = async () => {
if (
process.env.HERDR_ENV !== "1" ||
@@ -149,7 +145,6 @@ export const HerdrAgentStatePlugin = async () => {
await reportState("idle", sessionID);
break;
case "session.deleted":
- await releaseAgent();
break;
default:
break;
diff --git a/src/integration/assets/kimi/herdr-agent-state.ps1 b/src/integration/assets/kimi/herdr-agent-state.ps1
index d9924acc..ff6bcefe 100644
--- a/src/integration/assets/kimi/herdr-agent-state.ps1
+++ b/src/integration/assets/kimi/herdr-agent-state.ps1
@@ -2,11 +2,11 @@
# managed by herdr; reinstalling or updating the integration overwrites this file.
# add custom hooks beside this file instead of editing it.
# HERDR_INTEGRATION_ID=kimi
-# HERDR_INTEGRATION_VERSION=3
+# HERDR_INTEGRATION_VERSION=4
param([string]$Action = "")
-if (@("session", "working", "blocked", "idle", "release") -notcontains $Action) { exit 0 }
+if (@("session", "working", "blocked", "idle") -notcontains $Action) { exit 0 }
if ($env:HERDR_ENV -ne "1") { exit 0 }
if ([string]::IsNullOrWhiteSpace($env:HERDR_PANE_ID)) { exit 0 }
@@ -21,9 +21,7 @@ $seq = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
$sessionId = if ($null -ne $payload -and -not [string]::IsNullOrWhiteSpace($payload.session_id)) { $payload.session_id } else { $null }
try {
- if ($Action -eq "release") {
- & herdr pane release-agent $env:HERDR_PANE_ID --source herdr:kimi --agent kimi --seq $seq 2>$null | Out-Null
- } elseif ($Action -eq "session") {
+ if ($Action -eq "session") {
if ([string]::IsNullOrWhiteSpace($sessionId)) { exit 0 }
& herdr pane report-agent-session $env:HERDR_PANE_ID --source herdr:kimi --agent kimi --agent-session-id $sessionId --seq $seq 2>$null | Out-Null
} else {
diff --git a/src/integration/assets/kimi/herdr-agent-state.sh b/src/integration/assets/kimi/herdr-agent-state.sh
index 2c8d8567..b5b6cc4b 100644
--- a/src/integration/assets/kimi/herdr-agent-state.sh
+++ b/src/integration/assets/kimi/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=kimi
-# HERDR_INTEGRATION_VERSION=3
+# HERDR_INTEGRATION_VERSION=4
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
- session|working|blocked|idle|release) ;;
+ session|working|blocked|idle) ;;
*) exit 0 ;;
esac
@@ -54,18 +54,7 @@ agent_session_id = session_id if isinstance(session_id, str) and session_id else
request_id = f"{source}:{int(time.time() * 1000)}:{random.randrange(1_000_000):06d}"
report_seq = time.time_ns()
-if action == "release":
- request = {
- "id": request_id,
- "method": "pane.release_agent",
- "params": {
- "pane_id": pane_id,
- "source": source,
- "agent": agent,
- "seq": report_seq,
- },
- }
-elif action == "session":
+if action == "session":
if not agent_session_id:
raise SystemExit(0)
request = {
diff --git a/src/integration/assets/omp/herdr-agent-state.ts b/src/integration/assets/omp/herdr-agent-state.ts
index a6428cdd..a933e867 100644
--- a/src/integration/assets/omp/herdr-agent-state.ts
+++ b/src/integration/assets/omp/herdr-agent-state.ts
@@ -100,6 +100,35 @@ function parseDurationEnv(name: string, fallback: number): number {
return parsed;
}
+function currentSessionRef(): Record<string, unknown> | undefined {
+ if (currentAgentSessionPath) {
+ return { agent_session_path: currentAgentSessionPath };
+ }
+ if (currentAgentSessionId) {
+ return { agent_session_id: currentAgentSessionId };
+ }
+ return undefined;
+}
+
+function reportSession(): Promise<void> {
+ const sessionRef = currentSessionRef();
+ if (!sessionRef) {
+ return Promise.resolve();
+ }
+
+ return sendRequest({
+ id: `${source}:session:${Date.now()}:${Math.random().toString(36).slice(2)}`,
+ method: "pane.report_agent_session",
+ params: {
+ pane_id: paneId,
+ source,
+ agent: "omp",
+ seq: nextReportSeq(),
+ ...sessionRef,
+ },
+ });
+}
+
function sendState(state: AgentState, message?: string, seq = nextReportSeq()): Promise<void> {
return sendRequest({
id: `${source}:${Date.now()}:${Math.random().toString(36).slice(2)}`,
@@ -169,19 +198,6 @@ function retryableErrorMessage(event: any): string | undefined {
return errorMessage || "retryable provider error";
}
-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: "omp",
- seq: nextReportSeq(),
- },
- });
-}
-
export default function (pi) {
if (!enabled()) {
return;
@@ -287,11 +303,12 @@ export default function (pi) {
});
pi.on("session_start", (_event, ctx) => {
- rootSession = ctx?.hasUI === true;
- if (!rootSession) {
+ if (ctx?.hasUI !== true) {
return;
}
+ rootSession = true;
updateSessionRef(ctx);
+ void reportSession();
publishState(true);
});
@@ -326,12 +343,4 @@ export default function (pi) {
scheduleIdle();
});
-
- pi.on("session_shutdown", async () => {
- if (!rootSession) {
- return;
- }
- clearPendingTimers();
- await releaseAgent();
- });
}
diff --git a/src/integration/assets/opencode/herdr-agent-state.js b/src/integration/assets/opencode/herdr-agent-state.js
index 1e9fff51..6836227b 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=5
+// HERDR_INTEGRATION_VERSION=6
import net from "node:net";
@@ -96,10 +96,6 @@ function reportState(state, sessionID) {
return request("pane.report_agent", params);
}
-function releaseAgent() {
- return request("pane.release_agent", {});
-}
-
export const HerdrAgentStatePlugin = async () => {
if (
process.env.HERDR_ENV !== "1" ||
@@ -149,7 +145,6 @@ export const HerdrAgentStatePlugin = async () => {
await reportState("idle", sessionID);
break;
case "session.deleted":
- await releaseAgent();
break;
default:
break;
diff --git a/src/integration/assets/pi/herdr-agent-state.ts b/src/integration/assets/pi/herdr-agent-state.ts
index 0e620f1f..9af12cfd 100644
--- a/src/integration/assets/pi/herdr-agent-state.ts
+++ b/src/integration/assets/pi/herdr-agent-state.ts
@@ -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=pi
-// HERDR_INTEGRATION_VERSION=2
+// HERDR_INTEGRATION_VERSION=3
// @ts-nocheck
import { createConnection } from "node:net";
@@ -100,6 +100,35 @@ function withSessionRef(params: Record<string, unknown>): Record<string, unknown
return params;
}
+function currentSessionRef(): Record<string, unknown> | undefined {
+ if (currentAgentSessionPath) {
+ return { agent_session_path: currentAgentSessionPath };
+ }
+ if (currentAgentSessionId) {
+ return { agent_session_id: currentAgentSessionId };
+ }
+ return undefined;
+}
+
+function reportSession(): Promise<void> {
+ const sessionRef = currentSessionRef();
+ if (!sessionRef) {
+ return Promise.resolve();
+ }
+
+ return sendRequest({
+ id: `${source}:session:${Date.now()}:${Math.random().toString(36).slice(2)}`,
+ method: "pane.report_agent_session",
+ params: {
+ pane_id: paneId,
+ source,
+ agent: "pi",
+ seq: nextReportSeq(),
+ ...sessionRef,
+ },
+ });
+}
+
function sendState(state: AgentState, message?: string, seq = nextReportSeq()): Promise<void> {
return sendRequest({
id: `${source}:${Date.now()}:${Math.random().toString(36).slice(2)}`,
@@ -169,19 +198,6 @@ function retryableErrorMessage(event: any): string | undefined {
return errorMessage || "retryable provider error";
}
-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",
- seq: nextReportSeq(),
- },
- });
-}
-
export default function (pi) {
if (!enabled()) {
return;
@@ -197,6 +213,7 @@ export default function (pi) {
let lastMessage: string | undefined;
let idleTimer: ReturnType<typeof setTimeout> | undefined;
let retryTimer: ReturnType<typeof setTimeout> | undefined;
+ let rootSession = false;
function clearTimer(timer: ReturnType<typeof setTimeout> | undefined) {
if (timer) {
@@ -250,11 +267,6 @@ export default function (pi) {
idleTimer.unref?.();
}
- pi.on("session_start", (_event, ctx) => {
- updateSessionRef(ctx);
- publishState(true);
- });
-
function holdForRetry(message: string) {
clearPendingTimers();
retryHoldActive = true;
@@ -272,6 +284,9 @@ export default function (pi) {
}
pi.events.on("herdr:blocked", (data) => {
+ if (!rootSession) {
+ return;
+ }
if (!data?.active) {
blockedCount = Math.max(0, blockedCount - 1);
if (blockedCount === 0) {
@@ -287,7 +302,20 @@ export default function (pi) {
publishState();
});
+ pi.on("session_start", (_event, ctx) => {
+ if (ctx?.hasUI !== true) {
+ return;
+ }
+ rootSession = true;
+ updateSessionRef(ctx);
+ void reportSession();
+ publishState(true);
+ });
+
pi.on("agent_start", () => {
+ if (!rootSession) {
+ return;
+ }
clearPendingTimers();
clearFailureState();
agentActive = true;
@@ -295,6 +323,9 @@ export default function (pi) {
});
pi.on("agent_end", (event) => {
+ if (!rootSession) {
+ return;
+ }
if (!agentActive) {
// Pi can emit duplicate/late end events while auto-retry is already
// holding the pane in Working. Do not let an unqualified duplicate end
@@ -312,9 +343,4 @@ export default function (pi) {
scheduleIdle();
});
-
- pi.on("session_shutdown", async () => {
- clearPendingTimers();
- await releaseAgent();
- });
}
diff --git a/src/integration/mod.rs b/src/integration/mod.rs
index 59eaebd9..0a00240b 100644
--- a/src/integration/mod.rs
+++ b/src/integration/mod.rs
@@ -12,7 +12,7 @@ pub(crate) const HERDR_TAB_ID_ENV_VAR: &str = "HERDR_TAB_ID";
pub(crate) const HERDR_WORKSPACE_ID_ENV_VAR: &str = "HERDR_WORKSPACE_ID";
const PI_EXTENSION_INSTALL_NAME: &str = "herdr-agent-state.ts";
const PI_EXTENSION_ASSET: &str = include_str!("assets/pi/herdr-agent-state.ts");
-const PI_INTEGRATION_VERSION: u32 = 2;
+const PI_INTEGRATION_VERSION: u32 = 3;
const OMP_EXTENSION_INSTALL_NAME: &str = "herdr-omp-agent-state.ts";
const OMP_EXTENSION_ASSET: &str = include_str!("assets/omp/herdr-agent-state.ts");
const OMP_INTEGRATION_VERSION: u32 = 3;
@@ -27,7 +27,7 @@ const CLAUDE_HOOK_ASSET: &str = if cfg!(windows) {
} else {
include_str!("assets/claude/herdr-agent-state.sh")
};
-const CLAUDE_INTEGRATION_VERSION: u32 = 6;
+const CLAUDE_INTEGRATION_VERSION: u32 = 7;
const CLAUDE_CONFIG_DIR_ENV_VAR: &str = "CLAUDE_CONFIG_DIR";
const CODEX_HOOK_INSTALL_NAME: &str = if cfg!(windows) {
"herdr-agent-state.ps1"
@@ -39,7 +39,7 @@ const CODEX_HOOK_ASSET: &str = if cfg!(windows) {
} else {
include_str!("assets/codex/herdr-agent-state.sh")
};
-const CODEX_INTEGRATION_VERSION: u32 = 5;
+const CODEX_INTEGRATION_VERSION: u32 = 6;
const CODEX_HOME_ENV_VAR: &str = "CODEX_HOME";
const KIMI_HOOK_INSTALL_NAME: &str = if cfg!(windows) {
"herdr-agent-state.ps1"
@@ -51,12 +51,12 @@ const KIMI_HOOK_ASSET: &str = if cfg!(windows) {
} else {
include_str!("assets/kimi/herdr-agent-state.sh")
};
-const KIMI_INTEGRATION_VERSION: u32 = 3;
+const KIMI_INTEGRATION_VERSION: u32 = 4;
const KIMI_CODE_HOME_ENV_VAR: &str = "KIMI_CODE_HOME";
const KIMI_CONFIG_BLOCK_BEGIN: &str = "# >>> herdr kimi integration";
const KIMI_CONFIG_BLOCK_END: &str = "# <<< herdr kimi integration";
const KIMI_MIN_VERSION: &str = "0.14.0";
-const KIMI_HOOK_EVENTS: [(&str, &str); 10] = [
+const KIMI_HOOK_EVENTS: [(&str, &str); 9] = [
("SessionStart", "session"),
("UserPromptSubmit", "working"),
("PreToolUse", "working"),
@@ -66,7 +66,6 @@ const KIMI_HOOK_EVENTS: [(&str, &str); 10] = [
("PermissionResult", "working"),
("Stop", "idle"),
("Interrupt", "idle"),
- ("SessionEnd", "release"),
];
const COPILOT_HOOK_INSTALL_NAME: &str = if cfg!(windows) {
"herdr-agent-state.ps1"
@@ -136,16 +135,16 @@ 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 = 5;
+const OPENCODE_INTEGRATION_VERSION: u32 = 6;
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 = 1;
+const KILO_INTEGRATION_VERSION: u32 = 2;
const HERMES_PLUGIN_INSTALL_NAME: &str = "herdr-agent-state";
const HERMES_PLUGIN_MANIFEST_INSTALL_NAME: &str = "plugin.yaml";
const HERMES_PLUGIN_INIT_INSTALL_NAME: &str = "__init__.py";
const HERMES_PLUGIN_MANIFEST_ASSET: &str = include_str!("assets/hermes/plugin.yaml");
const HERMES_PLUGIN_INIT_ASSET: &str = include_str!("assets/hermes/__init__.py");
-const HERMES_INTEGRATION_VERSION: u32 = 2;
+const HERMES_INTEGRATION_VERSION: u32 = 3;
const QODERCLI_HOOK_INSTALL_NAME: &str = if cfg!(windows) {
"herdr-agent-state.ps1"
} else {
@@ -4434,7 +4433,7 @@ mod tests {
assert_eq!(claude.path, hook_path);
assert_eq!(claude.installed_version, Some(1));
- assert_eq!(claude.expected_version, 6);
+ assert_eq!(claude.expected_version, 7);
assert_eq!(claude.state, IntegrationStatusKind::Outdated);
std::env::remove_var("HOME");
@@ -4464,7 +4463,7 @@ mod tests {
assert_eq!(claude.path, hook_path);
assert_eq!(claude.installed_version, Some(2));
- assert_eq!(claude.expected_version, 6);
+ assert_eq!(claude.expected_version, 7);
assert_eq!(claude.state, IntegrationStatusKind::Outdated);
std::env::remove_var("HOME");
@@ -4597,7 +4596,7 @@ mod tests {
assert_eq!(codex.path, hook_path);
assert_eq!(codex.installed_version, Some(2));
- assert_eq!(codex.expected_version, 5);
+ assert_eq!(codex.expected_version, 6);
assert_eq!(codex.state, IntegrationStatusKind::Outdated);
std::env::remove_var("HOME");
@@ -5966,28 +5965,66 @@ mod tests {
#[test]
fn bundled_integration_assets_report_session_refs() {
- assert!(PI_EXTENSION_ASSET.contains("agent_session_path: currentAgentSessionPath"));
- assert!(PI_EXTENSION_ASSET.contains("agent_session_id: currentAgentSessionId"));
- assert!(PI_EXTENSION_ASSET.contains("publishState(true)"));
- assert!(OMP_EXTENSION_ASSET.contains("agent_session_path: currentAgentSessionPath"));
- assert!(OMP_EXTENSION_ASSET.contains("agent_session_id: currentAgentSessionId"));
- assert!(OMP_EXTENSION_ASSET.contains("publishState(true)"));
- assert!(CLAUDE_HOOK_ASSET.contains("agent_session_id"));
- assert!(CLAUDE_HOOK_ASSET.contains("agent_session_path"));
- assert!(CLAUDE_HOOK_ASSET.contains("session_start_source"));
- assert!(CLAUDE_HOOK_ASSET.contains("pane.report_agent_session"));
+ assert!(PI_EXTENSION_ASSET.contains("agent_session_path"));
+ assert!(PI_EXTENSION_ASSET.contains("agent_session_id"));
+ assert!(PI_EXTENSION_ASSET.contains("ctx?.hasUI !== true"));
+ assert!(PI_EXTENSION_ASSET.contains("pane.report_agent_session"));
+ assert!(PI_EXTENSION_ASSET.contains("pane.report_agent\""));
+ assert!(PI_EXTENSION_ASSET.contains("pi.on(\"agent_start\""));
+ assert!(PI_EXTENSION_ASSET.contains("pi.on(\"agent_end\""));
+ assert!(!PI_EXTENSION_ASSET.contains("pane.release_agent"));
+ assert!(!PI_EXTENSION_ASSET.contains("session_shutdown"));
+ assert!(OMP_EXTENSION_ASSET.contains("agent_session_path"));
+ assert!(OMP_EXTENSION_ASSET.contains("agent_session_id"));
+ assert!(OMP_EXTENSION_ASSET.contains("ctx?.hasUI !== true"));
+ assert!(OMP_EXTENSION_ASSET.contains("pane.report_agent_session"));
+ assert!(OMP_EXTENSION_ASSET.contains("pane.report_agent\""));
+ assert!(OMP_EXTENSION_ASSET.contains("pi.on(\"agent_start\""));
+ assert!(OMP_EXTENSION_ASSET.contains("pi.on(\"agent_end\""));
+ assert!(!OMP_EXTENSION_ASSET.contains("pane.release_agent"));
+ assert!(!OMP_EXTENSION_ASSET.contains("session_shutdown"));
+ assert!(
+ CLAUDE_HOOK_ASSET.contains("agent_session_id")
+ || CLAUDE_HOOK_ASSET.contains("--agent-session-id")
+ );
+ assert!(
+ CLAUDE_HOOK_ASSET.contains("agent_session_path")
+ || CLAUDE_HOOK_ASSET.contains("--agent-session-path")
+ );
+ assert!(CLAUDE_HOOK_ASSET.contains("agent_id"));
+ assert!(
+ CLAUDE_HOOK_ASSET.contains("session_start_source")
+ || CLAUDE_HOOK_ASSET.contains("--session-start-source")
+ );
+ assert!(
+ CLAUDE_HOOK_ASSET.contains("pane.report_agent_session")
+ || CLAUDE_HOOK_ASSET.contains("report-agent-session")
+ );
assert!(!CLAUDE_HOOK_ASSET.contains("\"state\": action"));
assert!(!CLAUDE_HOOK_ASSET.contains("pane.release_agent"));
- assert!(CODEX_HOOK_ASSET.contains("HERDR_HOOK_INPUT_FILE"));
- assert!(CODEX_HOOK_ASSET.contains("agent_session_id"));
- assert!(CODEX_HOOK_ASSET.contains("pane.report_agent_session"));
+ assert!(
+ CODEX_HOOK_ASSET.contains("HERDR_HOOK_INPUT_FILE")
+ || CODEX_HOOK_ASSET.contains("In.ReadToEnd")
+ );
+ assert!(
+ CODEX_HOOK_ASSET.contains("agent_session_id")
+ || CODEX_HOOK_ASSET.contains("--agent-session-id")
+ );
+ assert!(
+ CODEX_HOOK_ASSET.contains("session_start_source")
+ || CODEX_HOOK_ASSET.contains("--session-start-source")
+ );
+ assert!(
+ CODEX_HOOK_ASSET.contains("pane.report_agent_session")
+ || CODEX_HOOK_ASSET.contains("report-agent-session")
+ );
assert!(!CODEX_HOOK_ASSET.contains("\"state\": action"));
assert!(!CODEX_HOOK_ASSET.contains("pane.release_agent"));
assert!(KIMI_HOOK_ASSET.contains("source = \"herdr:kimi\""));
assert!(KIMI_HOOK_ASSET.contains("agent_session_id"));
assert!(KIMI_HOOK_ASSET.contains("pane.report_agent_session"));
assert!(KIMI_HOOK_ASSET.contains("\"state\": action"));
- assert!(KIMI_HOOK_ASSET.contains("pane.release_agent"));
+ assert!(!KIMI_HOOK_ASSET.contains("pane.release_agent"));
assert!(COPILOT_HOOK_ASSET.contains("agent_session_id"));
assert!(COPILOT_HOOK_ASSET.contains("pane.report_agent_session"));
assert!(!COPILOT_HOOK_ASSET.contains("\"state\":"));
@@ -6006,16 +6043,18 @@ mod tests {
assert!(OPENCODE_PLUGIN_ASSET.contains("params.agent_session_id = sessionID"));
assert!(OPENCODE_PLUGIN_ASSET.contains("pane.report_agent_session"));
assert!(OPENCODE_PLUGIN_ASSET.contains("reportState"));
- assert!(OPENCODE_PLUGIN_ASSET.contains("pane.release_agent"));
+ assert!(!OPENCODE_PLUGIN_ASSET.contains("pane.release_agent"));
assert!(KILO_PLUGIN_ASSET.contains("SOURCE = \"herdr:kilo\""));
assert!(KILO_PLUGIN_ASSET.contains("AGENT = \"kilo\""));
assert!(KILO_PLUGIN_ASSET.contains("pane.report_agent_session"));
assert!(KILO_PLUGIN_ASSET.contains("reportState"));
- assert!(KILO_PLUGIN_ASSET.contains("pane.release_agent"));
+ assert!(!KILO_PLUGIN_ASSET.contains("pane.release_agent"));
assert!(HERMES_PLUGIN_INIT_ASSET.contains("session_id = _session_id(kwargs)"));
assert!(HERMES_PLUGIN_INIT_ASSET.contains("agent_session_id"));
assert!(HERMES_PLUGIN_INIT_ASSET.contains("pane.report_agent\","));
- assert!(HERMES_PLUGIN_INIT_ASSET.contains("pane.release_agent"));
+ assert!(HERMES_PLUGIN_INIT_ASSET.contains("on_session_end"));
+ assert!(!HERMES_PLUGIN_INIT_ASSET.contains("on_session_finalize"));
+ assert!(!HERMES_PLUGIN_INIT_ASSET.contains("pane.release_agent"));
assert!(QODERCLI_HOOK_ASSET.contains("HERDR_HOOK_INPUT_FILE"));
assert!(QODERCLI_HOOK_ASSET.contains("agent_session_id"));
assert!(QODERCLI_HOOK_ASSET.contains("pane.report_agent_session"));
@@ -6035,26 +6074,19 @@ mod tests {
}
#[test]
- fn omp_root_session_guard_is_instance_scoped() {
- let export_start = OMP_EXTENSION_ASSET
- .find("export default function (pi)")
- .expect("omp extension exports a function");
- let root_session_decl = OMP_EXTENSION_ASSET
- .find("let rootSession = false")
- .expect("omp extension declares root session guard");
+ fn omp_session_hook_ignores_non_ui_sessions() {
let session_start_handler = OMP_EXTENSION_ASSET
.find("pi.on(\"session_start\"")
.expect("omp extension registers session_start handler");
-
- assert_eq!(
- OMP_EXTENSION_ASSET
- .matches("let rootSession = false")
- .count(),
- 1
- );
- assert!(OMP_EXTENSION_ASSET.contains("rootSession = ctx?.hasUI === true"));
- assert!(export_start < root_session_decl);
- assert!(root_session_decl < session_start_handler);
+ let non_ui_guard = OMP_EXTENSION_ASSET
+ .find("ctx?.hasUI !== true")
+ .expect("omp extension checks UI context");
+ let session_report = OMP_EXTENSION_ASSET
+ .find("void reportSession()")
+ .expect("omp extension reports root session");
+
+ assert!(session_start_handler < non_ui_guard);
+ assert!(non_ui_guard < session_report);
}
#[test]