diff options
| author | akbash <akbash@herdr.dev> | 2026-09-13 03:04:27 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-13 02:04:27 +0200 |
| commit | aa6b531a423067d8a71c2a57b41185bff17036be (patch) | |
| tree | 72fff1245d085d1b8aca3fbb124d007a731070ab | |
| parent | 98a8c6ce52a054b582a6210c79d76e10a99ccccc (diff) | |
fix: update grok session after new (#2683)
* fix: update grok session after new
refs #2681
* fix: avoid powershell args shadowing
refs #2681
---------
Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
| -rw-r--r-- | docs/next/website/src/content/docs/integrations.mdx | 2 | ||||
| -rw-r--r-- | docs/next/website/src/content/docs/ja/integrations.mdx | 2 | ||||
| -rw-r--r-- | docs/next/website/src/content/docs/session-state.mdx | 2 | ||||
| -rw-r--r-- | docs/next/website/src/content/docs/zh-cn/integrations.mdx | 2 | ||||
| -rw-r--r-- | src/integration/assets/grok/herdr-agent-state.ps1 | 20 | ||||
| -rw-r--r-- | src/integration/assets/grok/herdr-agent-state.sh | 20 | ||||
| -rw-r--r-- | src/integration/mod.rs | 2 | ||||
| -rw-r--r-- | src/integration/tests.rs | 35 | ||||
| -rw-r--r-- | src/terminal/state.rs | 65 | ||||
| -rw-r--r-- | tests/cli/hooks.rs | 22 |
10 files changed, 155 insertions, 17 deletions
diff --git a/docs/next/website/src/content/docs/integrations.mdx b/docs/next/website/src/content/docs/integrations.mdx index 4cbd0a63..e8a5a7f3 100644 --- a/docs/next/website/src/content/docs/integrations.mdx +++ b/docs/next/website/src/content/docs/integrations.mdx @@ -91,7 +91,7 @@ Use `HERDR_BIN_PATH` and the CLI wrappers for portable integrations. Code that n Some integrations report native agent session references. Herdr uses official session references to resume Claude Code, Codex, Devin CLI, Droid, Kimi Code CLI, Qoder CLI, Qwen Code, Cursor Agent CLI, Grok CLI, GitHub Copilot CLI, Pi, OMP, Hermes Agent, OpenCode, Kilo Code CLI, MastraCode, and Antigravity CLI panes after a Herdr server restart unless `[session] resume_agents_on_restore = false` disables it. -Native session restore requires current Herdr integrations: Pi integration version `2`, OMP version `3`, Claude Code version `6`, Codex version `5`, GitHub Copilot CLI version `2`, Devin CLI version `2`, Droid version `2`, Kimi Code CLI version `3`, Qoder CLI version `2`, Qwen Code version `1`, Cursor Agent CLI version `1`, Grok CLI version `1`, OpenCode version `5`, Kilo Code CLI version `1`, Hermes Agent version `5`, MastraCode version `1`, or Antigravity CLI version `1`. Check installed versions with `herdr integration status`. +Native session restore requires current Herdr integrations: Pi integration version `2`, OMP version `3`, Claude Code version `6`, Codex version `5`, GitHub Copilot CLI version `2`, Devin CLI version `2`, Droid version `2`, Kimi Code CLI version `3`, Qoder CLI version `2`, Qwen Code version `1`, Cursor Agent CLI version `1`, Grok CLI version `2`, OpenCode version `5`, Kilo Code CLI version `1`, Hermes Agent version `5`, MastraCode version `1`, or Antigravity CLI version `1`. Check installed versions with `herdr integration status`. ## Pi diff --git a/docs/next/website/src/content/docs/ja/integrations.mdx b/docs/next/website/src/content/docs/ja/integrations.mdx index 124010e1..0ff99933 100644 --- a/docs/next/website/src/content/docs/ja/integrations.mdx +++ b/docs/next/website/src/content/docs/ja/integrations.mdx @@ -93,7 +93,7 @@ Herdr の外では何もしないように、`HERDR_ENV=1` で必要な変数が 一部のインテグレーションは、エージェントのネイティブセッション参照を報告します。Herdr は公式のセッション参照を使って、`[session] resume_agents_on_restore = false` で無効化されていない限り、Herdr サーバーの再起動後に Claude Code、Codex、Devin CLI、Droid、Kimi Code CLI、Qoder CLI、Qwen Code、Cursor Agent CLI、Grok CLI、GitHub Copilot CLI、Pi、OMP、Hermes Agent、OpenCode、Kilo Code CLI、MastraCode、Antigravity CLI のペインを resume します。 -エージェントネイティブのセッション復元には最新の Herdr インテグレーションが必要です: Pi インテグレーションはバージョン `2`、OMP は `3`、Claude Code は `6`、Codex は `5`、GitHub Copilot CLI は `2`、Devin CLI は `2`、Droid は `2`、Kimi Code CLI は `3`、Qoder CLI は `2`、Qwen Code は `1`、Cursor Agent CLI は `1`、Grok CLI は `1`、OpenCode は `5`、Kilo Code CLI は `1`、Hermes Agent は `5`、MastraCode は `1`、Antigravity CLI は `1` です。インストール済みバージョンは `herdr integration status` で確認してください。 +エージェントネイティブのセッション復元には最新の Herdr インテグレーションが必要です: Pi インテグレーションはバージョン `2`、OMP は `3`、Claude Code は `6`、Codex は `5`、GitHub Copilot CLI は `2`、Devin CLI は `2`、Droid は `2`、Kimi Code CLI は `3`、Qoder CLI は `2`、Qwen Code は `1`、Cursor Agent CLI は `1`、Grok CLI は `2`、OpenCode は `5`、Kilo Code CLI は `1`、Hermes Agent は `5`、MastraCode は `1`、Antigravity CLI は `1` です。インストール済みバージョンは `herdr integration status` で確認してください。 ## Pi diff --git a/docs/next/website/src/content/docs/session-state.mdx b/docs/next/website/src/content/docs/session-state.mdx index ed147193..e47e57a8 100644 --- a/docs/next/website/src/content/docs/session-state.mdx +++ b/docs/next/website/src/content/docs/session-state.mdx @@ -70,7 +70,7 @@ Native session restore requires these Herdr integration versions or newer: | Claude Code | `6` | `claude --resume <id>` | | Codex | `5` | `codex resume <id>` | | Cursor Agent CLI | `1` | `cursor-agent --resume <id>` | -| Grok CLI | `1` | `grok --resume <id>` | +| Grok CLI | `2` | `grok --resume <id>` | | GitHub Copilot CLI | `2` | `copilot --resume=<id>` | | Devin CLI | `2` | `devin --resume <id>` | | Droid | `2` | `droid --resume <id>` | diff --git a/docs/next/website/src/content/docs/zh-cn/integrations.mdx b/docs/next/website/src/content/docs/zh-cn/integrations.mdx index e5535808..aaf84e54 100644 --- a/docs/next/website/src/content/docs/zh-cn/integrations.mdx +++ b/docs/next/website/src/content/docs/zh-cn/integrations.mdx @@ -93,7 +93,7 @@ Herdr 以两种不同方式使用集成: 一些集成会上报智能体的原生会话引用。除非被 `[session] resume_agents_on_restore = false` 禁用,Herdr 会在服务器重启后使用官方会话引用恢复 Claude Code、Codex、Devin CLI、Droid、Kimi Code CLI、Qoder CLI、Qwen Code、Cursor Agent CLI、Grok CLI、GitHub Copilot CLI、Pi、OMP、Hermes Agent、OpenCode、Kilo Code CLI、MastraCode 和 Antigravity CLI 的窗格。 -原生会话恢复需要最新的 Herdr 集成: Pi 集成版本 `2`、OMP 版本 `3`、Claude Code 版本 `6`、Codex 版本 `5`、GitHub Copilot CLI 版本 `2`、Devin CLI 版本 `2`、Droid 版本 `2`、Kimi Code CLI 版本 `3`、Qoder CLI 版本 `2`、Qwen Code 版本 `1`、Cursor Agent CLI 版本 `1`、Grok CLI 版本 `1`、OpenCode 版本 `5`、Kilo Code CLI 版本 `1`、Hermes Agent 版本 `5`、MastraCode 版本 `1`、Antigravity CLI 版本 `1`。用 `herdr integration status` 查看已安装版本。 +原生会话恢复需要最新的 Herdr 集成: Pi 集成版本 `2`、OMP 版本 `3`、Claude Code 版本 `6`、Codex 版本 `5`、GitHub Copilot CLI 版本 `2`、Devin CLI 版本 `2`、Droid 版本 `2`、Kimi Code CLI 版本 `3`、Qoder CLI 版本 `2`、Qwen Code 版本 `1`、Cursor Agent CLI 版本 `1`、Grok CLI 版本 `2`、OpenCode 版本 `5`、Kilo Code CLI 版本 `1`、Hermes Agent 版本 `5`、MastraCode 版本 `1`、Antigravity CLI 版本 `1`。用 `herdr integration status` 查看已安装版本。 ## Pi diff --git a/src/integration/assets/grok/herdr-agent-state.ps1 b/src/integration/assets/grok/herdr-agent-state.ps1 index 69c4f515..1b183a3b 100644 --- a/src/integration/assets/grok/herdr-agent-state.ps1 +++ b/src/integration/assets/grok/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=grok -# HERDR_INTEGRATION_VERSION=1 +# HERDR_INTEGRATION_VERSION=2 param([string]$Action = "") @@ -26,6 +26,12 @@ $event = if ($null -ne $payload -and $payload.hook_event_name -is [string]) { } if ($null -ne $event -and $event -notin @("session_start", "SessionStart", "sessionStart")) { exit 0 } +$sessionStartSource = if ($null -ne $payload -and $payload.source -is [string]) { + $payload.source +} else { + $null +} + $sessionId = $env:GROK_SESSION_ID if ([string]::IsNullOrWhiteSpace($sessionId) -and $null -ne $payload) { if ($payload.session_id -is [string]) { $sessionId = $payload.session_id } @@ -35,7 +41,17 @@ if ([string]::IsNullOrWhiteSpace($sessionId)) { exit 0 } $seq = [DateTime]::UtcNow.Ticks $herdr = if ([string]::IsNullOrWhiteSpace($env:HERDR_BIN_PATH)) { "herdr" } else { $env:HERDR_BIN_PATH } +$herdrArgs = @( + "pane", "report-agent-session", $env:HERDR_PANE_ID, + "--source", "herdr:grok", + "--agent", "grok", + "--seq", "$seq", + "--agent-session-id", "$sessionId" +) +if (-not [string]::IsNullOrWhiteSpace($sessionStartSource)) { + $herdrArgs += @("--session-start-source", "$sessionStartSource") +} try { - & $herdr pane report-agent-session $env:HERDR_PANE_ID --source herdr:grok --agent grok --seq $seq --agent-session-id $sessionId 2>$null | Out-Null + & $herdr @herdrArgs 2>$null | Out-Null } catch { } diff --git a/src/integration/assets/grok/herdr-agent-state.sh b/src/integration/assets/grok/herdr-agent-state.sh index bdf0f671..3eaa3431 100644 --- a/src/integration/assets/grok/herdr-agent-state.sh +++ b/src/integration/assets/grok/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=grok -# HERDR_INTEGRATION_VERSION=1 +# HERDR_INTEGRATION_VERSION=2 set -eu @@ -62,6 +62,7 @@ def first_text(*keys): hook_event_name = first_text("hook_event_name", "hookEventName") if hook_event_name not in (None, "session_start", "SessionStart", "sessionStart"): raise SystemExit(0) +session_start_source = first_text("source") # Grok injects GROK_SESSION_ID into every hook process; prefer it and fall # back to the event payload's session id fields. @@ -72,16 +73,19 @@ if not agent_session_id: request_id = f"{source}:{int(time.time() * 1000)}:{random.randrange(1_000_000):06d}" report_seq = time.time_ns() +params = { + "pane_id": pane_id, + "source": source, + "agent": "grok", + "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": "grok", - "seq": report_seq, - "agent_session_id": agent_session_id, - }, + "params": params, } try: diff --git a/src/integration/mod.rs b/src/integration/mod.rs index 49808ca2..55dcb2be 100644 --- a/src/integration/mod.rs +++ b/src/integration/mod.rs @@ -296,7 +296,7 @@ const GROK_HOOK_ASSET: &str = if cfg!(windows) { } else { include_str!("assets/grok/herdr-agent-state.sh") }; -const GROK_INTEGRATION_VERSION: u32 = 1; +const GROK_INTEGRATION_VERSION: u32 = 2; pub(crate) const INSTALL_WARNING_PREFIX: &str = "warning:"; diff --git a/src/integration/tests.rs b/src/integration/tests.rs index dc597d1d..e8aad72b 100644 --- a/src/integration/tests.rs +++ b/src/integration/tests.rs @@ -2897,6 +2897,7 @@ fn bundled_integration_asset_versions_match_expected_versions() { MASTRACODE_HOOK_ASSET, MASTRACODE_INTEGRATION_VERSION, ), + ("grok", GROK_HOOK_ASSET, GROK_INTEGRATION_VERSION), ] { assert_eq!( parse_integration_version(asset), @@ -4056,7 +4057,39 @@ fn install_antigravity_cli_errors_when_config_dir_missing() { } #[test] -fn grok_v1_integration_status_is_current() { +fn grok_v1_integration_status_is_outdated() { + let _lock = integration_env_lock(); + let base = unique_base(); + let grok_dir = base.join(".grok"); + let hooks_dir = grok_dir.join("hooks"); + fs::create_dir_all(&hooks_dir).unwrap(); + std::env::set_var(GROK_CONFIG_DIR_ENV_VAR, &grok_dir); + let hook_path = hooks_dir.join(GROK_HOOK_INSTALL_NAME); + fs::write( + &hook_path, + "#!/bin/sh\n# HERDR_INTEGRATION_ID=grok\n# HERDR_INTEGRATION_VERSION=1\n", + ) + .unwrap(); + fs::write( + hooks_dir.join(GROK_HOOK_CONFIG_INSTALL_NAME), + serde_json::to_string(&grok_hook_config(&hook_path)).unwrap(), + ) + .unwrap(); + + let grok = installed_integration_statuses() + .into_iter() + .find(|status| status.target == crate::api::schema::IntegrationTarget::Grok) + .expect("grok integration status"); + assert_eq!(grok.installed_version, Some(1)); + assert_eq!(grok.expected_version, GROK_INTEGRATION_VERSION); + assert_eq!(grok.state, IntegrationStatusKind::Outdated); + + clear_integration_path_env(); + let _ = fs::remove_dir_all(base); +} + +#[test] +fn grok_v2_integration_status_is_current() { let _lock = integration_env_lock(); let base = unique_base(); let grok_dir = base.join(".grok"); diff --git a/src/terminal/state.rs b/src/terminal/state.rs index dc28af10..a7c84574 100644 --- a/src/terminal/state.rs +++ b/src/terminal/state.rs @@ -1331,6 +1331,7 @@ impl TerminalState { | ("herdr:hermes", "hermes", Some("startup" | "new" | "resume")) | ("herdr:opencode", "opencode", Some("select")) | ("herdr:pi", "pi", Some("new" | "resume" | "fork")) + | ("herdr:grok", "grok", Some("new")) | ( "herdr:omp", "omp", @@ -1635,7 +1636,8 @@ impl TerminalState { session_ref: &crate::agent_resume::AgentSessionRef, session_start_source: Option<&str>, ) -> bool { - Self::session_start_source_is_recognized(session_start_source) + (source, agent_label) != ("herdr:grok", "grok") + && Self::session_start_source_is_recognized(session_start_source) && self.foreground_agent_confirms_session_owner(source, agent_label, session_ref) } @@ -4653,6 +4655,38 @@ mod tests { } #[test] + fn grok_new_session_ref_replaces_existing_session_ref() { + let mut terminal = test_terminal(); + terminal + .set_agent_session_ref( + "herdr:grok".into(), + "grok".into(), + crate::agent_resume::AgentSessionRef::id("grok-old"), + Some(20), + ) + .expect("initial session should be accepted"); + + let mutation = terminal + .set_agent_session_ref_for_session_start( + "herdr:grok".into(), + "grok".into(), + crate::agent_resume::AgentSessionRef::id("grok-new"), + Some(21), + Some("new".into()), + ) + .expect("new should replace the grok session"); + + assert!(mutation.session_ref_changed); + assert_eq!( + terminal + .persisted_agent_session + .as_ref() + .map(|session| session.session_ref.value.as_str()), + Some("grok-new") + ); + } + + #[test] fn opencode_server_new_does_not_replace_existing_session_ref() { let mut terminal = test_terminal(); terminal.set_detected_state(Some(Agent::OpenCode), AgentState::Idle); @@ -5106,6 +5140,35 @@ mod tests { } #[test] + fn grok_new_session_does_not_replace_a_different_owner() { + let mut terminal = test_terminal(); + terminal.set_persisted_agent_session(crate::agent_resume::PersistedAgentSession { + source: "herdr:claude".into(), + agent: "claude".into(), + session_ref: crate::agent_resume::AgentSessionRef::id("claude-session").unwrap(), + }); + terminal.set_detected_state(Some(Agent::Grok), AgentState::Idle); + + let mutation = terminal.set_agent_session_ref_for_session_start( + "herdr:grok".into(), + "grok".into(), + crate::agent_resume::AgentSessionRef::id("grok-session"), + Some(21), + Some("new".into()), + ); + + assert!(mutation.is_none()); + assert_eq!( + terminal.persisted_agent_session.as_ref().map(|session| ( + session.source.as_str(), + session.agent.as_str(), + session.session_ref.value.as_str() + )), + Some(("herdr:claude", "claude", "claude-session")) + ); + } + + #[test] fn foreground_agent_session_replaces_stale_different_owner_session_ref() { for session_start_source in ["resume", "startup"] { let mut terminal = test_terminal(); diff --git a/tests/cli/hooks.rs b/tests/cli/hooks.rs index de2bb271..31978a03 100644 --- a/tests/cli/hooks.rs +++ b/tests/cli/hooks.rs @@ -37,6 +37,15 @@ fn run_devin_hook( ) } +fn run_grok_hook(hook_input: &str, envs: &[(&str, &str)]) -> Option<serde_json::Value> { + run_shell_hook_with_env( + "src/integration/assets/grok/herdr-agent-state.sh", + &["session"], + hook_input, + envs, + ) +} + fn run_shell_hook(asset_path: &str, args: &[&str], hook_input: &str) -> Option<serde_json::Value> { run_shell_hook_with_env(asset_path, args, hook_input, &[]) } @@ -238,6 +247,19 @@ fn copilot_hook_reports_session_id_from_stdin() { } #[test] +fn grok_hook_reports_new_session_source() { + let request = run_grok_hook( + r#"{"hook_event_name":"session_start","source":"new","session_id":"new-session"}"#, + &[("GROK_SESSION_ID", "new-session")], + ) + .expect("grok session start should report session identity"); + + assert_eq!(request["method"], "pane.report_agent_session"); + assert_eq!(request["params"]["agent_session_id"], "new-session"); + assert_eq!(request["params"]["session_start_source"], "new"); +} + +#[test] fn copilot_hook_does_not_report_lifecycle_state() { for payload in [ r#"{"hook_event_name":"UserPromptSubmit","session_id":"copilot-session","prompt":"run tests"}"#, |
