Skip to content

fix: stale permission prompt in scrollback causes false waiting_user_answer status #68

@sriharshaarangi

Description

@sriharshaarangi

Stale [y/n/t]: permission prompt in scrollback causes get_status() to return WAITING_USER_ANSWER even when terminal is idle

After a user answers a permission prompt (y/n/t), the [y/n/t]: text remains in tmux scrollback. On subsequent get_status() calls, permission_prompt_pattern matches the stale prompt text because \s* in the regex bridges across \n characters, connecting the old ]: with the current idle prompt.

Root Cause

In providers/kiro_cli.py (and q_cli.py):

self._permission_prompt_pattern = (
    r"Allow this action\?.*\[.*y.*\/.*n.*\/.*t.*\]:\s*" + self._idle_prompt_pattern
)

The pattern requires [y/n/t]: followed by \s* then the idle prompt. \s includes [\r\n\t\f\v ], so \s* bridges across newlines connecting stale ]: text to the current idle prompt.

Why This Bug Exists — Introduced by PR #61

This bug was introduced by PR #61 (484fd46, Feb 5, 2026).

Before PR #61: The idle prompt pattern ended with [\s\n]*$ (dollar anchor). The $ forced the idle prompt to be at the end of the captured text. Even with re.DOTALL and \s*, stale permission prompts could not match because the idle prompt immediately after [y/n/t]: was NOT at end-of-string — there was more output after it.

After PR #61: The $ anchor was removed. Now permission_prompt_pattern matches any [y/n/t]: followed (via \s* bridging newlines) by any idle prompt anywhere in scrollback — including stale ones from minutes ago.

# Scrollback contains:
Allow this action? ... [y/n/t]:     ← stale (answered minutes ago)
\n
[agent-name] 11% >                  ← current idle prompt

\s* matches \n, bridging the stale ]: to the current idle prompt. The re.DOTALL flag lets .* span the permission prompt text across wrapped lines, but \s* is the actual culprit — even without re.DOTALL, the match succeeds via \s*.

Summary: PR #61 removed the $ anchor that was protecting against stale matches. The fix restores the protection by a different mechanism: blocking newline bridging instead of requiring end-of-string.

Impact

  • get_status() permanently returns WAITING_USER_ANSWER after any permission prompt interaction
  • The inbox watcher skips delivery (terminal not IDLE/COMPLETED)
  • All subsequent send_message deliveries to that terminal are stuck as PENDING
  • The terminal is effectively broken for the rest of the session

Reproduction Steps

  1. Start a CAO session with kiro-cli supervisor
  2. Ask the agent to write a file (triggers permission prompt)
  3. Answer y at the [y/n/t]: prompt
  4. Wait for the agent to complete and return to idle
  5. Check status:
    curl -s http://localhost:9889/terminals/{terminal_id} | python3 -m json.tool
  6. Observe: "status": "waiting_user_answer" — should be completed or idle

Evidence from Repro (mainline commit 5302bc7)

Terminal 4b73ce4c:
  Actual state: idle at prompt "[cao-task-scoping-supervisor] 11% >"
  API status:   "waiting_user_answer"  ← WRONG

Inbox message 387: status=pending (stuck, never delivered)

Tmux pane shows the idle prompt clearly, but stale [y/n/t]: is in scrollback above.

Suggested Fix

Replace \s* with [ \t]* in the permission prompt pattern:

# Before (buggy — \s* bridges across newlines):
r"Allow this action\?.*\[.*y.*\/.*n.*\/.*t.*\]:\s*" + self._idle_prompt_pattern

# After (fixed — [ \t]* only matches horizontal whitespace):
r"Allow this action\?.*\[.*y.*\/.*n.*\/.*t.*\]:[ \t]*" + self._idle_prompt_pattern

re.DOTALL should be kept on the re.search() call — it is needed for narrow terminals where the permission prompt text wraps across lines. The fix is specifically \s*[ \t]* to block newline bridging.

Environment

  • CAO version: mainline (5302bc7)
  • kiro-cli version: 1.25.0
  • OS: macOS

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions