Skip to content

fix(qwen): make Stop actually stop instead of replaying the prompt - #351

Merged
saucam merged 2 commits into
mainfrom
fix/qwen-interrupt-restarts-turn
Oct 4, 2026
Merged

saucam merged 2 commits into
mainfrom
fix/qwen-interrupt-restarts-turn

Conversation

@saucam

@saucam saucam commented Oct 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

On the qwen backend, pressing Stop did not stop the task: the agent restarted the same prompt from scratch a few seconds later, and repeated Stops just kept restarting it.

Cause. The qwen provider interrupted a turn with Query.interrupt(). In qwen-code, that control request aborts a session-wide AbortController that the CLI never resets. The in-flight turn stops, but the CLI then exits with CLI process exited with code 1 on its next stdin read. The consumer's isBackingSessionMissing() heuristic matches that message and "recovers" by resetting to a fresh backing session and replaying the last prompt via onRecoveryNeeded. So an interrupt turned into a full restart that also lost the backing session's history.

Fix. interrupt now goes straight to the hard abort that was previously only the fallback path: it aborts codeoid's own controller, closes the input queue, and clears pending tool state. Because codeoid's controller is marked aborted, the consumer's catch skips recovery. The next send() finds the input queue closed, rebuilds the loop with resume: <backingId>, and keeps context.

The Claude provider's "keep-warm" Query.interrupt() is unaffected; this is specific to how qwen-code handles that request.

Test plan

  • New src/tests/provider-qwen-interrupt.test.ts (mocks @qwen-code/sdk, modelling the CLI dying after a soft interrupt):
    • interrupt hard-aborts, never calls the soft interrupt(), emits no error, and does not fire onRecoveryNeeded
    • the next turn rebuilds with resume on the same backing id
  • Confirmed the first test fails without the fix (logs backing session missing — scheduling recovery)
  • bun test src/tests/provider-qwen*.test.ts, bun run typecheck, biome clean on changed files
  • Manual: start a qwen session, send a long task, hit Stop; the session should go idle and stay idle, and a follow-up message should continue with context

🤖 Generated with Claude Code

The qwen provider interrupted a turn via Query.interrupt(). In qwen-code
that control request aborts a session-wide controller the CLI never
resets, so the CLI exits with code 1 on its next stdin read. The consumer
treats "exited with code 1" as a missing backing session and recovers by
replaying the last prompt into a fresh session, so every Stop restarted
the task from scratch.

Interrupt now hard-aborts (marking codeoid's controller aborted, so no
recovery fires) and clears pending tool state. The next send rebuilds the
loop with `resume`, keeping context.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@saucam
saucam merged commit 86c836a into main Oct 4, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants