Where does the bug appear (feature/product)?
Cursor IDE
Describe the Bug
Summary
Agent Composer waits 1–3 minutes after a shell command has already finished (or after a server is already serving). This is not a slow cargo/wasmtime process. Two harness behaviors force the wait:
-
AwaitShelldoes not treat process completion as a match when the model waits onexit_code:. That string only appears in the terminal footer. The waiter is specified not to match header/footer metadata. The command exits; the agent still blocks untilblock_until_ms(often 180000). -
A background server (
block_until_ms: 0) stays a tracked task. After the agent has already used it (curl succeeded), the harness later aborts it and injects a system “task has finished” user turn, minutes after the useful work. That looks like the agent is still waiting.
These are product issues. Agent mistakes (chaining build && serve, | tail, oversized timeouts) make them worse, but the two items above are reproducible even with a correct agent.
Bug 1 — Completed command, waiter still blocks for the full timeout
What happened
Shell 885331: cargo fmt && cargo test -p rocci-cli
- Tests finished in 26.61s; whole command 51.3s
- Footer already present:
---
exit_code: 101
elapsed_ms: 51309
ended_at: 2026-08-30T10:58:01.025Z
---
The agent then called the shell waiter with:
AwaitShell(shell_id=885331, pattern="exit_code:", block_until_ms=180000)
Actual: waiter did not return on process exit. It sat until the 180s budget because exit_code: is footer metadata and is excluded from pattern matching.
Expected: either
- return immediately when the process has exited (footer written), or
- reject / no-op
pattern: "exit_code:"as invalid, or - document and train models that completion is signaled by the tool result /
ended_at, never by matchingexit_code:in the file.
Same class of miss: 971161 (cargo test … | tail -40) aborted at 30s with an empty body. The agent then waited 180s for test result:|error:, which never appeared.
Bug 2 — Late “task finished” turn for an intentional background server
What happened
wasmtime serve started with block_until_ms: 0 (correct). Ready line appeared immediately:
Serving HTTP on http://127.0.0.1:18080/
The agent curled GET / and got the expected body. The serve process then stayed tracked.
Terminal 971162:
- started
2026-08-30T10:23:31Z - ready immediately
- later
status: failed,exit_code: unknown,elapsed_ms: 13415
Terminal 971163 (same pattern, env-log serve):
- started
10:28:20Z - printed
Serving HTTPplus the expected stderr line - later aborted (
elapsed_ms: 19354)
Actual: at 12:24 and 12:29 local, Cursor injected:
The following task has finished. … status: aborted
title: Start wasmtime serve …
title: Serve env-log component …
Those turns interrupted the implementation chat long after the proof was done and committed. From the UI this is “the agent waited several minutes.”
Expected:
- A
block_until_ms: 0process that is a server should not generate a blocking user-visible “task finished” turn after abort, unless the current turn is waiting on thatshell_id. - Aborting a background serve after the agent already moved on should be silent (or a small status chip), not a new user message the agent must answer.
Minimal reproduction (no project required)
A. Footer wait (Bug 1)
- Agent Shell:
echo ready && sleep 2(finishes in ~2s). - Agent
AwaitShellon that id withpattern: "exit_code:",block_until_ms: 180000. - Observe: command file already has
exit_code: 0; waiter still blocks up to 3 minutes.
B. Late abort notification (Bug 2)
- Agent Shell with
block_until_ms: 0:python3 -m http.server 8765. - Wait only until the serving line, then
curl -sS http://127.0.0.1:8765/. - Continue the chat with an unrelated message; do not kill the server in that turn.
- Observe: minutes later, a system “task has finished / aborted” user turn for that server.
What this is not
- Not a hung
wasmtimeorcargo(those printed “Serving HTTP” /test result:on time). - Not “always wait
block_until_msfor finite jobs” — finite Shell calls that return on process exit are fine. The hang is AwaitShell after exit and background-task notifications. - Not asking you to change how long
cargomay run. Asking for completion and background-task signaling that matches the file the UI already wrote.
Asks
- Treat process exit as a terminal condition for
AwaitShelleven whenpatternis set (pattern = “unblock early”, exit = always unblock). - Do not allow / do not honor
pattern: "exit_code:"if footers are excluded from matching — return immediately with “already completed” if the footer exists. - Do not inject a Composer user turn when a
block_until_ms: 0server is later aborted, unless that turn is explicitly waiting on it. - Optional: surface
ended_at/exit_codein the waiter tool result so models stop scraping the footer.
Happy to attach the two terminal files (971162.txt, 885331.txt) and the exported chat if useful.
Steps to Reproduce
Two separate reproductions. Do them in a new Agent chat. You never call AwaitShell yourself — the prompts below make the agent do it.
A. Finished command, agent still waits ~3 minutes
- New Agent chat, empty folder is fine.
- Paste:
Run exactly: echo ready && sleep 2
When the Shell tool returns or backgrounds the job, immediately call AwaitShell
on that shell_id with:
pattern: "exit_code:"
block_until_ms: 180000
Do not start any other command. Tell me when AwaitShell returns and how long it waited.
- Watch the tool calls (not just the chat).
Pass (bug): echo finishes in ~2s. The terminal file already has a footer like exit_code: 0. The next tool is AwaitShell and it stays pending for up to 180s.
Fail (fixed / not this bug): AwaitShell returns as soon as the process exits, or the agent refuses pattern: "exit_code:" and says the job already completed.
B. Background server → late “task finished” turn
- New Agent chat.
- Paste:
Start this with block_until_ms: 0 (background, do not wait for exit):
python3 -m http.server 8765 --bind 127.0.0.1
AwaitShell only until the output contains "Serving HTTP".
Then run: curl -sS -D - http://127.0.0.1:8765/ | head -20
Leave the server running. Do not kill it. Reply with the HTTP status only.
- After the agent replies with
200, send a second message:
Thanks. Do not touch the server. Just say ok.
- Wait, or stop the background task from the UI / end the turn and sit idle for a few minutes.
Pass (bug): A later system user message appears, e.g. “The following task has finished” / status: aborted, for “Start http.server” (or similar). The agent then writes a status reply even though you already had the curl result.
Fail (fixed): No injected “task finished” turn. The server stays a quiet background task, or it dies without a new user message.
Optional check (same class as A)
If A is “too synthetic,” this is the real-session variant:
Run: cargo test -p rocci-wasi-http
When it finishes, AwaitShell that same shell_id with pattern "exit_code:" and block_until_ms 180000.
Same signal: tests already printed test result: and the file has exit_code:; waiter still burns the 180s.
Do A and B as two posts or two numbered sections. Do not combine them in one chat — a leftover http.server confuses timing.
If you only have time for one, A is the clearer product bug (wrong waiter contract). B is what you felt in the WASI session.
Operating System
MacOS
Version Information
Version: 3.18.9
VS Code Extension API: 1.128.0
Commit: 2ba48ff3f7514cc4643c52ca9f7b3173d9b66130
Date: 2026-08-27T01:42:22.092Z
Layout: IDE
Build Type: Stable
Release Track: Default
Electron: 40.10.3
Chromium: 144.0.7559.236
Node.js: 24.15.0
V8: 14.4.258.32-electron.0
xterm.js: 6.1.0-beta.291
OS: Darwin arm64 24.6.0
For AI issues: add Request ID with privacy disabled
839750e0-85d2-4e9f-8a1a-14c5469d8dbf
Does this stop you from using Cursor
No - Cursor works, but with this issue