Skip to content

Commit f739edc

Browse files
committed
fix(ui): keep live chat for canonical session events
1 parent ec55307 commit f739edc

3 files changed

Lines changed: 75 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Docs: https://docs.openclaw.ai
1919

2020
### Fixes
2121

22+
- Control UI/chat: keep live replies visible when a raw session alias such as `main` sends the chat turn but Gateway emits events under the canonical session key for the same run. Fixes #73716. Thanks @teebes.
2223
- TTS/Telegram: keep trusted local audio generated by the TTS tool queued for voice-note delivery even when the run-level built-in tool list omits the raw `tts` name. Fixes #74752. Thanks @Loveworld3033 and @andyliu.
2324
- TTS: require explicit user or config audio intent for the agent speech tool so dashboard chats stay text unless audio is requested. Fixes #69777. Thanks @alexandre-leng.
2425
- Plugins/config: keep bundled source-checkout plugins from being runtime-gated by install-only `minHostVersion` metadata, accept prerelease host floors, trim plugin-service startup failures to one log line, and avoid broad channel-runtime loading during base config parsing. Thanks @vincentkoc.

ui/src/ui/controllers/chat.test.ts

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ describe("handleChatEvent", () => {
7777
expect(handleChatEvent(state, undefined)).toBe(null);
7878
});
7979

80-
it("returns null when sessionKey does not match", () => {
80+
it("returns null when sessionKey does not match and no active run is in flight", () => {
8181
const state = createState({ sessionKey: "main" });
8282
const payload: ChatEventPayload = {
8383
runId: "run-1",
@@ -87,6 +87,73 @@ describe("handleChatEvent", () => {
8787
expect(handleChatEvent(state, payload)).toBe(null);
8888
});
8989

90+
it("accepts delta events for the active run when gateway emits a canonical session key", () => {
91+
const state = createState({
92+
sessionKey: "main",
93+
chatRunId: "run-1",
94+
chatStream: null,
95+
});
96+
const payload: ChatEventPayload = {
97+
runId: "run-1",
98+
sessionKey: "agent:main:main",
99+
state: "delta",
100+
message: {
101+
role: "assistant",
102+
content: [{ type: "text", text: "Live reply" }],
103+
},
104+
};
105+
106+
expect(handleChatEvent(state, payload)).toBe("delta");
107+
expect(state.chatStream).toBe("Live reply");
108+
expect(state.chatRunId).toBe("run-1");
109+
});
110+
111+
it("accepts final events for the active run when gateway emits a canonical session key", () => {
112+
const state = createState({
113+
sessionKey: "main",
114+
chatRunId: "run-1",
115+
chatStream: "Live reply",
116+
chatStreamStartedAt: 100,
117+
});
118+
const payload: ChatEventPayload = {
119+
runId: "run-1",
120+
sessionKey: "agent:main:main",
121+
state: "final",
122+
message: {
123+
role: "assistant",
124+
content: [{ type: "text", text: "Live reply" }],
125+
},
126+
};
127+
128+
expect(handleChatEvent(state, payload)).toBe("final");
129+
expect(state.chatMessages).toEqual([payload.message]);
130+
expect(state.chatRunId).toBe(null);
131+
expect(state.chatStream).toBe(null);
132+
expect(state.chatStreamStartedAt).toBe(null);
133+
});
134+
135+
it("still drops events when neither session key nor active run id matches", () => {
136+
const state = createState({
137+
sessionKey: "main",
138+
chatRunId: "run-1",
139+
chatStream: "Working...",
140+
});
141+
const payload: ChatEventPayload = {
142+
runId: "run-2",
143+
sessionKey: "agent:main:main",
144+
state: "delta",
145+
message: {
146+
role: "assistant",
147+
content: [{ type: "text", text: "Wrong run" }],
148+
},
149+
};
150+
151+
expect(handleChatEvent(state, payload)).toBe(null);
152+
expect(state.chatRunId).toBe("run-1");
153+
expect(state.chatStream).toBe("Working...");
154+
expect(state.chatMessages).toEqual([]);
155+
});
156+
90157
it("returns null for delta from another run", () => {
91158
const state = createState({
92159
sessionKey: "main",

ui/src/ui/controllers/chat.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,12 @@ export function handleChatEvent(state: ChatState, payload?: ChatEventPayload) {
714714
if (!payload) {
715715
return null;
716716
}
717-
if (payload.sessionKey !== state.sessionKey) {
717+
const sessionMatches = payload.sessionKey === state.sessionKey;
718+
const activeRunMatches =
719+
state.chatRunId !== null &&
720+
typeof payload.runId === "string" &&
721+
payload.runId === state.chatRunId;
722+
if (!sessionMatches && !activeRunMatches) {
718723
return null;
719724
}
720725

0 commit comments

Comments
 (0)