diff --git a/frontend/__tests__/utils/handle-event-for-ui.test.ts b/frontend/__tests__/utils/handle-event-for-ui.test.ts index dde84629c2..51ac8df4a5 100644 --- a/frontend/__tests__/utils/handle-event-for-ui.test.ts +++ b/frontend/__tests__/utils/handle-event-for-ui.test.ts @@ -138,4 +138,72 @@ describe("handleEventForUI", () => { anotherActionEvent, ]); }); + + it("should NOT replace ThinkAction with ThinkObservation", () => { + const mockThinkAction: ActionEvent = { + id: "test-think-action-1", + timestamp: Date.now().toString(), + source: "agent", + thought: [{ type: "text", text: "I am thinking..." }], + thinking_blocks: [], + action: { + kind: "ThinkAction", + thought: "I am thinking...", + }, + tool_name: "think", + tool_call_id: "call_think_1", + tool_call: { + id: "call_think_1", + type: "function", + function: { + name: "think", + arguments: "", + }, + }, + llm_response_id: "response_think", + security_risk: SecurityRisk.UNKNOWN, + }; + + const mockThinkObservation: ObservationEvent = { + id: "test-think-observation-1", + timestamp: Date.now().toString(), + source: "environment", + tool_name: "think", + tool_call_id: "call_think_1", + observation: { + kind: "ThinkObservation", + content: [{ type: "text", text: "Your thought has been logged." }], + }, + action_id: "test-think-action-1", + }; + + const initialUiEvents = [mockMessageEvent, mockThinkAction]; + const result = handleEventForUI(mockThinkObservation, initialUiEvents); + + // ThinkObservation should NOT be added - ThinkAction should remain + expect(result).toEqual([mockMessageEvent, mockThinkAction]); + expect(result).not.toBe(initialUiEvents); + }); + + it("should NOT add ThinkObservation even when ThinkAction is not found", () => { + const mockThinkObservation: ObservationEvent = { + id: "test-think-observation-1", + timestamp: Date.now().toString(), + source: "environment", + tool_name: "think", + tool_call_id: "call_think_1", + observation: { + kind: "ThinkObservation", + content: [{ type: "text", text: "Your thought has been logged." }], + }, + action_id: "test-think-action-not-found", + }; + + const initialUiEvents = [mockMessageEvent]; + const result = handleEventForUI(mockThinkObservation, initialUiEvents); + + // ThinkObservation should never be added to uiEvents + expect(result).toEqual([mockMessageEvent]); + expect(result).not.toBe(initialUiEvents); + }); }); diff --git a/frontend/src/utils/handle-event-for-ui.ts b/frontend/src/utils/handle-event-for-ui.ts index 1d291192cc..fe00605005 100644 --- a/frontend/src/utils/handle-event-for-ui.ts +++ b/frontend/src/utils/handle-event-for-ui.ts @@ -4,6 +4,7 @@ import { isObservationEvent } from "#/types/v1/type-guards"; /** * Handles adding an event to the UI events array * Replaces actions with observations when they arrive (so UI shows observation instead of action) + * Exception: ThinkAction is NOT replaced because the thought content is in the action, not in the observation */ export const handleEventForUI = ( event: OpenHandsEvent, @@ -12,12 +13,17 @@ export const handleEventForUI = ( const newUiEvents = [...uiEvents]; if (isObservationEvent(event)) { + // Don't add ThinkObservation at all - we keep the ThinkAction instead + // The thought content is in the action, not the observation + if (event.observation.kind === "ThinkObservation") { + return newUiEvents; + } + // Find and replace the corresponding action from uiEvents const actionIndex = newUiEvents.findIndex( (uiEvent) => uiEvent.id === event.action_id, ); if (actionIndex !== -1) { - // Replace the action with the observation newUiEvents[actionIndex] = event; } else { // Action not found in uiEvents, just add the observation