awslabs / awslabs/cli-agent-orchestrator

[bug] cursor_cli: status detection fails on Free-plan "Auto" status bar, and stays PROCESSING after a turn ends

Open
#752 1 comment 0 reactions 1 assignee Claimed by @AstroCan17 View on GitHub
Dominant language
Python
Stars
1.3k
Forks
267
Avg merge
1d 23h
Merged PRs (30d)
70

Description

## Summary

Two defects in `cursor_cli` status detection, both in the v2026 TUI heuristics added
for #299. They are filed together because the second one is invisible until the first
is fixed: with (1) in place a session never launches, so (2) can never be reached.

Environment: CAO 2.5.0, Cursor CLI `2026.09.02-c22c1a3`, Linux, **Cursor Free plan**.

---

## Defect 1 — `TUI_STATUS_BAR_PATTERN` cannot match a Free-plan status bar, so launch always times out

`providers/cursor_cli.py`:

```python
TUI_STATUS_BAR_PATTERN = r"Run Everything|Composer \d"
```

IDLE is gated on it:

```python
if not processing_indicator_in_tail and re.search(TUI_STATUS_BAR_PATTERN, clean):
return TerminalStatus.COMPLETED if self._turns > 0 else TerminalStatus.IDLE
return TerminalStatus.UNKNOWN
```

The status bar renders the **active model name**. On a Cursor **Free** plan a named
model cannot be selected:

```
Error: Named models unavailable
Free plans can only use Auto. Switch to Auto or upgrade plans to continue.
```

so the bar renders `Auto`, and neither alternative in the pattern ever appears:

```
Cursor Agent
v2026.09.02-c22c1a3
→ Plan, search, build anything
Auto case-study | modelling-simulation
0%
```

`TUI_PLACEHOLDER_PATTERN` matches here (`Plan, search, build anything`); only the
status-bar half fails. `get_status()` stays `UNKNOWN`, the launch wait times out, and
CAO tears the session down before any task is delivered:

```
14:16:16 tmux - INFO - send_keys: cao-smoke4:dev_cursor-a42c - keys length: 116
14:16:17 terminal - INFO - wait_until_status [5edf6bb1]: waiting for {idle, completed}, timeout=60.0s
14:17:16 terminal - WARNING - wait_until_status [5edf6bb1]: timeout waiting for {idle, completed}
14:17:16 tmux - INFO - Killed tmux session: cao-smoke4
```

**Control run.** The same profile pinned to `--model composer-2.5` renders
`Composer 2.5` in the bar, matches `Composer \d`, and reaches IDLE in 5.7 s:

```
14:11:23 tmux - DEBUG - send_keys: cursor-agent --force --model composer-2.5 --plugin-dir ... --approve-mcps
14:11:24 terminal - INFO - wait_until_status [409c8350]: waiting for {idle, completed}, timeout=60.0s
14:11:29 status_monitor - INFO - Terminal 409c8350 status changed: idle
14:11:30 terminal - INFO - wait_until_status [409c8350]: reached idle
```

That run then failed on the plan restriction above, so on a Free plan there is no
configuration that works: a named model is rejected by Cursor, and Auto is invisible
to CAO.

Reproduced 4/4 times with Auto (twice via `model: auto`, twice with no `model:` key so
Cursor uses its own default), and 1/1 with a named model. Ruled out along the way:
`--model auto` versus omitting `--model` (no difference), and the rotating `Tip:`
line — with `hints: false` in `~/.cursor/cli-config.json` the banner renders once and
stays static, and detection still fails.

### Suggested fix

```diff
-TUI_STATUS_BAR_PATTERN = r"Run Everything|Composer \d"
+TUI_STATUS_BAR_PATTERN = r"Run Everything|Composer \d|\bAuto\b"
```

`\b`-anchored to limit false hits on the word "Auto" in agent output; the IDLE branch
is gated on the absence of the processing indicator anyway. A model-name-derived
readiness signal is fragile in general — "Auto" is a first-class Cursor state, and any
future model family would need another alternative here.

---

## Defect 2 — the processing indicator is matched anywhere in a 1 KB tail, so a terminal stays PROCESSING after its turn ends

This one is **not** Free-plan specific; it should affect every Cursor v2026 turn.

```python
TUI_TAIL_WINDOW = 1024
tail = clean[-TUI_TAIL_WINDOW:]
processing_indicator_in_tail = (
re.search(TUI_PROCESSING_INDICATOR_PATTERN, tail, re.IGNORECASE) is not None
)
```

The module's own comment states the contract precisely:

> the "ctrl+c to stop" hint, which Cursor renders **on the same line as the
> placeholder** while the agent is actively working on a turn

Searching the whole window is a weaker form of that check, and it breaks exactly when
a turn **ends**. The finished screen is compact (placeholder, status bar, progress
percentage), so the last working frame's `ctrl+c to stop` remains inside the 1 KB
window and `processing_indicator_in_tail` never clears.

Observed with Defect 1 patched: the agent finished its answer, the pane stopped
changing, and `get_status()` still returned `PROCESSING` 8+ minutes later. An
orchestrated worker would never be seen to complete — `assign` / `handoff` would block
until their own timeout.

### Suggested fix

Anchor the indicator to the last rendered input-box line, which is what the comment
already describes, and fall back to the current tail search when no placeholder line
is present (older text-mode builds):

```python
_placeholder_lines = [
line for line in tail.splitlines()
if re.search(TUI_PLACEHOLDER_PATTERN, line, re.IGNORECASE)
]
if _placeholder_lines:
processing_indicator_in_tail = (
re.search(TUI_PROCESSING_INDICATOR_PATTERN, _placeholder_lines[-1], re.IGNORECASE)
is not None
)
else:
processing_indicator_in_tail = (
re.search(TUI_PROCESSING_INDICATOR_PATTERN, tail, re.IGNORECASE) is not None
)
```

---

## Verification of both fixes together

With both patches applied locally and `cao-server` restarted, a headless launch on a
Free-plan account completes end to end:

```
14:42:25 processing
14:42:49 completed
```

The worker executed real tool calls (Glob, Grep) and returned a correct answer, checked
independently against `grep -cE '^\s*(async )?def test_'` on the same files.

Happy to split this into two issues, or to open a PR with both changes plus tests, if
that is more useful.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.