This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new aec407743 fix(ai): seed the run id on attach so stop works after a 
reconnect (#4613)
aec407743 is described below

commit aec407743a1f894b098f4fe81d3e1246d928d863
Author: Apulupie <[email protected]>
AuthorDate: Mon Sep 21 21:05:34 2026 +0800

    fix(ai): seed the run id on attach so stop works after a reconnect (#4613)
    
    `useAgentRun.startStream` cleared `runIdRef.current` before opening the 
stream and only learned the run id from the `run_started` event, which 
`AgentEventProjector` documents as live-only and the replay timeline has no 
counterpart for. A reload that reattached to an in-flight run through 
`useActiveRunAttach` therefore never got an id: `stop()` returned early, and 
since `Composer` derives the stop button from `isStreaming` alone the button 
stayed clickable as a no-op, while `persistRun [...]
    
    `startStream` now takes a `knownRunId` and seeds both the ref and the state 
with it. `attach` passes the run id it already holds; `send` keeps its old 
behaviour through the default, and `abort` still clears the id.
    
    Fixes #4614
---
 web/src/pages/ai/hooks/useAgentRun.test.ts | 22 ++++++++++++++++++++++
 web/src/pages/ai/hooks/useAgentRun.ts      | 14 ++++++++++----
 2 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/web/src/pages/ai/hooks/useAgentRun.test.ts 
b/web/src/pages/ai/hooks/useAgentRun.test.ts
index 207e5e253..768b05197 100644
--- a/web/src/pages/ai/hooks/useAgentRun.test.ts
+++ b/web/src/pages/ai/hooks/useAgentRun.test.ts
@@ -427,6 +427,28 @@ describe('useAgentRun', () => {
     expect(result.current.isStreaming).toBe(true);
   });
 
+  it('stopsAnAttachedRunThroughTheApiTest', async () => {
+    const { result } = render();
+
+    // The server replays persisted events on attach; the live vocabulary 
starts at run_started,
+    // so no `run_started` frame ever arrives on this stream.
+    await act(async () => {
+      void result.current.attach(7, 41, 12);
+    });
+    await act(async () => {
+      attachedStreams[0].emit(textDelta('resumed'));
+      await flushFrame();
+    });
+    expect(result.current.canStop).toBe(true);
+
+    await act(async () => {
+      await result.current.stop();
+    });
+
+    expect(stopRun).toHaveBeenCalledWith(41);
+    expect(result.current.stopRequested).toBe(true);
+  });
+
   it('surfacesAStreamFailureAndStillRefetchesTheTimelineTest', async () => {
     const onError = vi.fn();
     const refetchTimeline = vi.fn().mockResolvedValue(undefined);
diff --git a/web/src/pages/ai/hooks/useAgentRun.ts 
b/web/src/pages/ai/hooks/useAgentRun.ts
index 071c5c17a..63287c61b 100644
--- a/web/src/pages/ai/hooks/useAgentRun.ts
+++ b/web/src/pages/ai/hooks/useAgentRun.ts
@@ -281,6 +281,7 @@ export function useAgentRun(
     async (
       targetConversationId: number,
       open: (handlers: RunStreamHandlers, signal: AbortSignal) => 
Promise<void>,
+      knownRunId: number | null = null,
     ): Promise<void> => {
       // Double-submit guard: Enter twice in one tick must not admit two runs 
(the server would
       // reject the second with 409 anyway, but the UI should not even try).
@@ -307,8 +308,11 @@ export function useAgentRun(
       // screen once this run's answer lands.
       setLastRunTokensPerSecond(null);
       setLiveTokensPerSecond(null);
-      runIdRef.current = null;
-      setRunId(null);
+      // `send` learns the id from the live `run_started` frame; an attach 
already knows it from
+      // the URL, and the server never replays `run_started` — without seeding 
it here the stop
+      // button would render but address nothing until the run finished.
+      runIdRef.current = knownRunId;
+      setRunId(knownRunId);
       setLastStatus(null);
       setError('');
       setStopRequested(false);
@@ -348,8 +352,10 @@ export function useAgentRun(
 
   const attach = useCallback(
     (targetConversationId: number, targetRunId: number, after: number): 
Promise<void> =>
-      startStream(targetConversationId, (handlers, signal) =>
-        attachRunStream(targetRunId, after, handlers, signal),
+      startStream(
+        targetConversationId,
+        (handlers, signal) => attachRunStream(targetRunId, after, handlers, 
signal),
+        targetRunId,
       ),
     [startStream],
   );

Reply via email to