aboutsummaryrefslogtreecommitdiff
path: root/src/detect
diff options
context:
space:
mode:
Diffstat (limited to 'src/detect')
-rw-r--r--src/detect/manifest/tests.rs117
-rw-r--r--src/detect/manifests/grok.toml39
2 files changed, 144 insertions, 12 deletions
diff --git a/src/detect/manifest/tests.rs b/src/detect/manifest/tests.rs
index bdb0789d..fc519c7f 100644
--- a/src/detect/manifest/tests.rs
+++ b/src/detect/manifest/tests.rs
@@ -725,6 +725,123 @@ fn osc_explain(
)
}
+#[test]
+fn grok_osc_activity_does_not_depend_on_configurable_title_text() {
+ for (title, progress, expected_state, expected_rule) in [
+ (
+ "project · session · id",
+ "4;0;0",
+ AgentState::Idle,
+ "osc_progress_idle",
+ ),
+ (
+ "project · session · id",
+ "",
+ AgentState::Idle,
+ "prompt_hints_idle",
+ ),
+ ("grok", "", AgentState::Idle, "osc_title_idle"),
+ (
+ "⠋ - Waiting for response… - project",
+ "",
+ AgentState::Working,
+ "osc_title_working",
+ ),
+ (
+ "project - ⠹ - session",
+ "4;0;0",
+ AgentState::Working,
+ "osc_title_working",
+ ),
+ (
+ "project · session · id",
+ "4;1;-1",
+ AgentState::Working,
+ "osc_progress_working",
+ ),
+ (
+ "grok",
+ "4;1;-1",
+ AgentState::Working,
+ "osc_progress_working",
+ ),
+ (
+ "⚠ Action Required - project",
+ "4;1;-1",
+ AgentState::Blocked,
+ "osc_title_blocked",
+ ),
+ ] {
+ let result = osc_explain(
+ Agent::Grok,
+ "Shift+Tab:mode │ Ctrl+.:shortcuts\n",
+ title,
+ progress,
+ );
+ assert_eq!(
+ result.state, expected_state,
+ "title={title}, progress={progress}"
+ );
+ assert_eq!(
+ result.matched_rule.as_ref().map(|rule| rule.id.as_str()),
+ Some(expected_rule),
+ "title={title}, progress={progress}",
+ );
+ }
+}
+
+#[test]
+fn grok_visible_activity_outranks_idle_osc_signals() {
+ for (screen, expected_rule) in [
+ (
+ "⠴ Sleep for 8 … 1.9s 5.0s ⇣19.5k [↓][stop]\n",
+ "spinner_status_working",
+ ),
+ (
+ "Shift+Tab:mode │ Ctrl+c:cancel │ Ctrl+.:shortcuts\n",
+ "esc_cancel_hints_working",
+ ),
+ (
+ "Shift+Tab:mode │ Esc:cancel │ Ctrl+.:shortcuts\n",
+ "esc_cancel_hints_working",
+ ),
+ ("◎ 1 command still running\n", "background_status_working"),
+ ("○ 1 command still running\n", "background_status_working"),
+ ("◉ 1 command still running\n", "background_status_working"),
+ (
+ "◎ 2 commands · 1 subagent still running · send a message to interrupt\n",
+ "background_status_working",
+ ),
+ ] {
+ for progress in ["", "4;0;0"] {
+ let result = osc_explain(Agent::Grok, screen, "grok", progress);
+ assert_eq!(result.state, AgentState::Working, "screen={screen}");
+ assert_eq!(
+ result.matched_rule.as_ref().map(|rule| rule.id.as_str()),
+ Some(expected_rule)
+ );
+ }
+ let blocked = osc_explain(Agent::Grok, screen, "⚠ Action Required - grok", "4;1;-1");
+ assert_eq!(blocked.state, AgentState::Blocked);
+ }
+}
+
+#[test]
+fn grok_background_activity_requires_a_live_nonzero_status_row() {
+ for screen in [
+ "1 command still running\n",
+ "◎ 0 commands still running\n",
+ "Discussed: ◎ 1 command still running\n",
+ "◎ 1 command still running\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n",
+ "Worked for 3.9s\nShift+Tab:mode │ Ctrl+.:shortcuts\n",
+ ] {
+ assert_eq!(
+ osc_explain(Agent::Grok, screen, "project · session", "4;0;0").state,
+ AgentState::Idle
+ );
+ }
+}
+
// --- Claude OSC rules ---
#[test]
diff --git a/src/detect/manifests/grok.toml b/src/detect/manifests/grok.toml
index 30b6e9c7..67ba4ebc 100644
--- a/src/detect/manifests/grok.toml
+++ b/src/detect/manifests/grok.toml
@@ -1,16 +1,16 @@
id = "grok"
-version = "2026.07.16.2"
+version = "2026.09.18.2"
min_engine_version = 3
-updated_at = "2026-07-16T00:00:00Z"
+updated_at = "2026-09-18T00:00:00Z"
aliases = ["grok-build"]
-# Evidence: Grok Build 0.2.101 source and live pane reads.
+# Evidence: Grok Build 0.2.101 source and 1.0.34 live pane reads.
#
# Grok emits OSC 0 titles by default. Idle is "grok" or
# "<session> - grok". During a turn, the configured title gains a braille
# spinner and activity text. Permission prompts add "⚠ Action Required";
# that prefix blinks while the terminal is unfocused, so visible blocker
-# rules outrank the generic non-idle title rule.
+# rules outrank the title spinner rule.
#
# Grok also emits OSC 9;4 progress on supported terminals. Herdr retains the
# payload after "9;": "4;1;-1" while busy and "4;0;0" when idle.
@@ -85,6 +85,16 @@ region = "top_non_empty_lines(1)"
visible_working = true
line_regex = ['[⋅:⸬⁙.·]\s+[1-9][0-9]*\s+│']
+# Grok 1.0.34 moves background counts above the composer and clears OSC
+# progress between turns even while these commands are still running.
+[[rules]]
+id = "background_status_working"
+state = "working"
+priority = 1165
+region = "bottom_non_empty_lines(12)"
+visible_working = true
+line_regex = ['^\s*[○◎◉]\s+[1-9][0-9]*\s+(?:commands?|monitors?|loops?|subagents?)(?:\s+·\s+[1-9][0-9]*\s+(?:commands?|monitors?|loops?|subagents?))*\s+still running(?:\s+·\s+send a message to interrupt)?\s*$']
+
[[rules]]
id = "osc_progress_working"
state = "working"
@@ -104,16 +114,15 @@ not = [
{ regex = ['[\x{2800}-\x{28FF}]'] },
]
-# After known idle titles are excluded, any other non-empty Grok title is
-# active. OSC progress remains authoritative when a custom title omits the
-# spinner but the terminal supports progress reporting.
+# Title items are configurable; omitting "grok" does not imply activity.
+# Require a spinner, with OSC progress covering titles that omit it.
[[rules]]
id = "osc_title_working"
state = "working"
priority = 1000
region = "osc_title"
visible_working = true
-regex = ['\S']
+regex = ['(?:^|\s)[\x{2801}-\x{28FF}](?:\s|$)']
[[rules]]
id = "osc_progress_idle"
@@ -123,10 +132,12 @@ region = "osc_progress"
visible_idle = true
regex = ['^4;0;0$']
+# Visible activity outranks idle OSC signals: disabling title updates can
+# leave the startup "grok" title unchanged throughout a turn.
[[rules]]
id = "spinner_status_working"
state = "working"
-priority = 200
+priority = 1160
region = "whole_recent"
visible_working = true
line_regex = ['^\s*[\x{2801}-\x{28FF}]\s.*\[stop\]\s*$']
@@ -134,16 +145,20 @@ line_regex = ['^\s*[\x{2801}-\x{28FF}]\s.*\[stop\]\s*$']
[[rules]]
id = "esc_cancel_hints_working"
state = "working"
-priority = 190
+priority = 1155
region = "bottom_non_empty_lines(2)"
visible_working = true
-contains = ["esc:cancel", "ctrl+.:shortcuts"]
+contains = ["ctrl+.:shortcuts"]
+any = [
+ { contains = ["esc:cancel"] },
+ { contains = ["ctrl+c:cancel"] },
+]
# Pre-0.2.x working chrome kept for older Grok Build releases.
[[rules]]
id = "waiting_tool_working"
state = "working"
-priority = 120
+priority = 1140
region = "whole_recent"
visible_working = true
any = [