unbridled-41 opened a new issue, #4739:
URL: https://github.com/apache/rocketmq-dashboard/issues/4739
### Studio Version
branch: rocketmq-studio
git commit id: 7ce9a68215c4cd1c7025013d02135b43b6aa6ba1
### Problem
A send that the server refuses clears the composer and never gives the draft
back. The composer clears its own draft on send and documents that the caller
must restore it when the send is rejected ("no conversation yet, 409 from the
server"), but the AI page only restores it on the *create conversation* path:
on an existing conversation the send is fired without its outcome ever reaching
the caller, so a 409 (another run in flight), a provider rejection, or a stream
that dies before its first frame leaves the operator with an empty composer.
Chain:
- `Composer.handleSend` (`web/src/pages/ai/components/Composer.tsx:256-261`)
calls `onSend(text)` and then `onChange('')`, clearing the draft (and its
sessionStorage copy). Its own contract at `:108-113`: "The composer clears the
draft itself, so a caller that has to reject the send (no conversation yet, 409
from the server) should put the text back with `onChange`".
- `AiPage.handleSend` (`web/src/pages/ai/index.tsx:119-124`) restores the
text only `if (target === null)`, with the comment "a rejected send gives the
cleared draft back".
- `useAiSend.startRun` (`web/src/pages/ai/hooks/useAiSend.ts:108-111`) used
`void optionsRef.current.send(current, request); return current;` for an
existing conversation — the id is returned whether or not the send was
admitted, so `target` was never null and the restore could not run.
- `useAgentRun.startStream`
(`web/src/pages/ai/hooks/useAgentRun.ts:280-336`) already knows the difference:
two guards return before opening anything, and a rejection from `open()` before
any frame means the run never started — none of that was reported to the caller.
- The home-page handoff has the same shape
(`web/src/pages/ai/hooks/useDraftHandoff.ts:122-123`): it applied an empty
prompt after `startRun` regardless of the result.
Trigger (deterministic): on `/ai/c/{id}`, send while another tab (or an
earlier request) holds a run in flight. The server answers 409, the page shows
the error alert and the button returns to 发送, and the composer is empty — the
prompt is gone even though it never left the browser. The same happens when the
provider rejects the prompt or the stream fails before its first frame.
### Evidence
- `web/src/pages/ai/components/Composer.tsx:108-113` (documented contract)
and `:256-261` (draft cleared on send).
- `web/src/pages/ai/index.tsx:119-124` — restore gated on `target === null`.
- `web/src/pages/ai/hooks/useAiSend.ts:108-111` — the existing-conversation
branch returned `current` unconditionally (fire-and-forget `void send(...)`).
- `web/src/pages/ai/hooks/useAgentRun.ts:285-295` (guards that refuse
without opening a stream) and `:318-336` (a rejection with no frame delivered).
- `web/src/pages/ai/hooks/useDraftHandoff.ts:122-123` — handoff cleared the
applied prompt regardless of the send result.
- Regression tests added with this report (both fail on 7ce9a682): the
composer case (409 on `/ai/c/7`, the textarea must still hold the prompt) and
the handoff case (`/ai/c/7` entered with a `prompt` router state, same
expectation).
### Impact
The operator retypes a prompt that never reached the server; the
create-conversation path already gives the draft back, so the two send paths
disagree about the same failure. For a long prompt (the composer accepts
multi-line instructions), the lost text is real work, and the only clue is a
toast that the send failed.
### Expected behavior
A send the server did not admit returns the draft to the composer, on every
path (fresh conversation, existing conversation, home-page handoff); a send
that was admitted keeps the composer cleared, even if the stream later fails
mid-answer.
### Related work
- #4736 (PR #4738) — the tool playground and this composer are both on the
AI page; that report is about a stale result, this one about a lost draft.
- `web/src/pages/ai/index.tsx:115` — the pre-existing comment "a rejected
send gives the cleared draft back" states the intended behavior this report
restores.
- #4614 and #4632 (open) — AI run lifecycle (stop after reconnect, runtime
repopulating after disable): adjacent, different defects.
#
## PR
Fix: TBD.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]