mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
* feat(web-shell): add Send now for queued mid-turn messages
A message typed while a turn runs waits for the model response that is
streaming and for its tool batch, so a correction typed into a long answer
reached the model seconds later, and Stop was the only faster path.
Typing keeps that behavior: the message stays queued, editable and
deletable until the next tool boundary. A new Send now action on a queued
row asks the daemon to deliver the queued messages at once.
POST /session/:id/mid-turn-messages/send-now, advertised by
session_mid_turn_send_now, sends the agent craft/midTurnSendNow. While a
foreground turn streams a response that has not started a tool call, the
agent drains the user's messages, cuts the response short, keeps the part
already streamed, and answers in the same turn. Running tools are never
interrupted, a response that started a tool call is never cut, queue-only
steering is not taken early, and a request is scoped to the turn that
received it. Input taken by a turn that is then stopped or fails is kept
in that turn's conversation.
The TypeScript SDK gains sendMidTurnMessagesNow on both clients.
* fix(web-shell): address review findings on Send now
- Restart the Stop-hook state when a continuation answers input that
cut its response short, so the next Stop check is not hook-forced and
the consecutive-block count and reasons start over.
- Give input a finished turn keeps unsent a 2 s resolution deadline of
its own; the deadline also cancels media bridges.
- Requeue a failed send-now drain in its original order, and log a
send-now request the agent rejects.
- In Web Shell, reconcile a row whose message already left the queue
and ignore a repeated click while a request is out.
- State the tool-call carve-out wherever Send now is described, and
document goal turns, the narrow no-answer window and token accounting.
- Add tests for the review's gaps, drive the drain deadline with fake
timers, and add the route to the multi-workspace failure tables.
* fix(web-shell): keep send-now input a stopped tool boundary leaves unbuilt
When a turn is stopped while a tool-boundary drain resolves a message,
the drain now hands back the messages it has not built that an early
send-now drain took, so the end-of-turn keep records them. Input the
boundary drain took for itself is still dropped there, as before.
- Tests: the stopped boundary keeps that input once and carries nothing
into the next turn; a Goal continuation is cut; a background
notification turn is not; a failed send-now drain does not requeue a
message that left the queue meanwhile.
- Bridge and SDK contracts: the cut applies to a foreground turn's
streaming response, and `requested: true` does not mean a cut.
- Protocol and design docs: the timed-out send-now drain, resending a
settled id, the 10-message drain limit, and the tool-boundary drop.
* test(web-shell): pin send-now behavior questioned in review
- Assert that cron and background-notification turns never open their
response to interruption and that a send-now request arms nothing
while they stream.
- A response cut short by queued input still answers that input when it
ends with an error, while an error from a response its drain did not
cut remains the turn's error. Explain in `watch` and the design doc why
the cut response's error is absorbed: the cut does not always arrive as
an AbortError, and the response was given up.
- A message whose deletion missed while a failing drain held it is still
delivered, which matches the `{ removed: false }` answer the client
got. Document this in the protocol reference.
- Rename a requeue test to say it covers a queue-only entry.
* docs(web-shell): scope the abort invariant and drain-held removals
- The main prompt loop's catch states that other AbortErrors surface.
Note that a response cut short by queued input reaches that catch only
when the turn itself is stopped meanwhile, because `watch` absorbs
whatever a cut response throws.
- In the force-drain paragraph, add the drain-held case of
`{ removed: false }`. Such an id must not be re-posted, and its listing
in `settledMessageIds` is not final, because a failed drain returns it
to the queue without an event.