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]

Reply via email to