aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-01 23:47:40 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-01 23:47:40 +0300
commit9709ba6938bd720fea6d8dd15346adf9db2b7f4b (patch)
tree4da522fa3a5e43ef25bef2a12b12734caa839688
parent36a4608603a82dbf32ec8a013dd47047cf02c6a3 (diff)
fix: detect kiro subagent approvals
refs #388
-rw-r--r--docs/next/CHANGELOG.md1
-rw-r--r--src/detect/agents/kiro.rs32
-rw-r--r--src/detect/mod.rs6
3 files changed, 32 insertions, 7 deletions
diff --git a/docs/next/CHANGELOG.md b/docs/next/CHANGELOG.md
index 130143cc..37b315c8 100644
--- a/docs/next/CHANGELOG.md
+++ b/docs/next/CHANGELOG.md
@@ -10,6 +10,7 @@
- Native agent session restore is now enabled by default for supported panes with current official integrations. Set `[session] resume_agents_on_restore = false` to disable it.
### Fixed
+- Kiro sub-agent tool approval prompts are now detected as blocked instead of working. (#388)
- Pane input no longer waits behind the PTY actor's idle read poll, restoring responsive typing at quiet shell prompts. (#379)
- Pane apps that query OSC 4 ANSI palette colors now receive the active terminal palette response, so OpenCode and similar TUIs can enable system-theme behavior inside Herdr. (#387)
- Pane text selection now derives its highlight colors from the host terminal or active Herdr palette instead of forcing the theme's blue accent. (#298)
diff --git a/src/detect/agents/kiro.rs b/src/detect/agents/kiro.rs
index 5ef1fbca..f40b9bcb 100644
--- a/src/detect/agents/kiro.rs
+++ b/src/detect/agents/kiro.rs
@@ -3,16 +3,11 @@ use super::super::AgentState;
/// Kiro CLI detection.
///
/// Kiro exposes reliable working and idle terminal markers. Tool approval
-/// prompts render with a stable "requires approval" line and an action menu.
+/// prompts render with stable approval wording and an action menu.
pub(super) fn detect(content: &str) -> AgentState {
let lower = content.to_lowercase();
- let has_approval_request = lower.contains("requires approval");
- let has_approval_actions = lower.contains("yes, single permission")
- || lower.contains("trust, always allow")
- || lower.contains("no (tab to edit)")
- || lower.contains("esc to close");
- if has_approval_request && has_approval_actions {
+ if has_kiro_blocked_prompt(&lower) {
return AgentState::Blocked;
}
@@ -25,6 +20,29 @@ pub(super) fn detect(content: &str) -> AgentState {
AgentState::Idle
}
+fn has_kiro_blocked_prompt(lower_content: &str) -> bool {
+ has_tool_approval_prompt(lower_content) || has_subagent_approval_prompt(lower_content)
+}
+
+fn has_tool_approval_prompt(lower_content: &str) -> bool {
+ let has_approval_request = lower_content.contains("requires approval");
+ let has_approval_actions = lower_content.contains("yes, single permission")
+ || lower_content.contains("trust, always allow")
+ || lower_content.contains("no (tab to edit)")
+ || lower_content.contains("esc to close");
+ has_approval_request && has_approval_actions
+}
+
+fn has_subagent_approval_prompt(lower_content: &str) -> bool {
+ let has_approval_request = (lower_content.contains("tool approval")
+ || lower_content.contains("tool approvals"))
+ && lower_content.contains("pending from subagents");
+ let has_approval_actions = lower_content.contains("approve all pending")
+ || lower_content.contains("configure individually")
+ || lower_content.contains("exit (cancel subagents)");
+ has_approval_request && has_approval_actions
+}
+
fn has_kiro_tool_spinner(content: &str) -> bool {
content.lines().any(|line| {
let trimmed = line.trim_start();
diff --git a/src/detect/mod.rs b/src/detect/mod.rs
index b9806b19..e5f84628 100644
--- a/src/detect/mod.rs
+++ b/src/detect/mod.rs
@@ -1900,6 +1900,12 @@ mod tests {
}
#[test]
+ fn kiro_blocked_on_subagent_tool_approval_prompt() {
+ let screen = " Please delegate to a sub-agent to search the web\n\n● Orchestrating (1 agent)\n esc to cancel\n ctrl+g open agent monitor\n ● web-research kiro_default ⚠ tool approval needed\n\n ◐ Tasks · 1 done · 2 remaining ctrl+x to expand\n──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\n ⚠ 3 tool approvals pending from subagents\n ❯ (a) Approve all pending\n (f) Approve all pending and auto-approve all future requests\n (c) Configure individually (agent monitor)\n (x) Exit (cancel subagents) %";
+ assert_eq!(detect_state(Some(Agent::Kiro), screen), AgentState::Blocked);
+ }
+
+ #[test]
fn kiro_does_not_treat_stale_failure_spinner_as_working() {
let screen = "● 1 MCP failure — see /mcp\n─────────────────────────────────────────────────────\nKiro · auto · ◔ 6%\n\n ask a question or describe a task ↵";
assert_eq!(detect_state(Some(Agent::Kiro), screen), AgentState::Idle);