diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c676a923b3..43a968fe309 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Talk: preserve explicit `null` payloads on controller-created turn and output-audio lifecycle events. - Agents/TUI: keep local custom provider runs from loading plugin runtime and auth alias metadata when plugins are disabled. - Agents/TUI: restore in-flight TUI run switch-back behavior, keep no-policy native hook fallback available, guard vanished workspaces, and keep lightweight isolated subagents lightweight. - Agents/media: keep async image, music, and video generation starts from ending the Codex turn, so mixed requests can continue with summaries or other work while media renders in the background. diff --git a/src/talk/talk-session-controller.test.ts b/src/talk/talk-session-controller.test.ts index 551925bf6dc..012ba96b4e9 100644 --- a/src/talk/talk-session-controller.test.ts +++ b/src/talk/talk-session-controller.test.ts @@ -134,6 +134,20 @@ describe("createTalkSessionController", () => { expect(talk.outputAudioActive).toBe(false); }); + it("preserves explicit null lifecycle payloads", () => { + const talk = createController(); + + const started = talk.startTurn({ payload: null }); + const ended = talk.endTurn({ payload: null, turnId: started.turnId }); + const audioStarted = talk.startOutputAudio({ payload: null }); + const audioDone = talk.finishOutputAudio({ payload: null }); + + expect(started.event?.payload).toBeNull(); + expect(ended.ok ? ended.event.payload : undefined).toBeNull(); + expect(audioStarted.event?.payload).toBeNull(); + expect(audioDone?.payload).toBeNull(); + }); + it("notifies an event hook for emitted and controller-created events", () => { const events: string[] = []; const talk = createTalkSessionController( diff --git a/src/talk/talk-session-controller.ts b/src/talk/talk-session-controller.ts index d0fc5519c81..cc878ba1d36 100644 --- a/src/talk/talk-session-controller.ts +++ b/src/talk/talk-session-controller.ts @@ -56,6 +56,10 @@ export type TalkSessionControllerOptions = { sequencer?: TalkEventSequencer; }; +function defaultTalkEventPayload(payload: unknown): unknown { + return payload === undefined ? {} : payload; +} + export function createTalkSessionController( params: TalkSessionControllerParams, options: TalkSessionControllerOptions = {}, @@ -111,7 +115,7 @@ export function createTalkSessionController( event: emit({ type: "turn.started", turnId, - payload: startParams.payload ?? {}, + payload: defaultTalkEventPayload(startParams.payload), }), }; }; @@ -132,7 +136,7 @@ export function createTalkSessionController( event: emit({ type, turnId, - payload: paramsForTurn.payload ?? {}, + payload: defaultTalkEventPayload(paramsForTurn.payload), final: true, }), }; @@ -174,7 +178,7 @@ export function createTalkSessionController( return emit({ type: "output.audio.done", turnId, - payload: paramsForOutput.payload ?? {}, + payload: defaultTalkEventPayload(paramsForOutput.payload), final: true, }); }, @@ -189,7 +193,7 @@ export function createTalkSessionController( event: emit({ type: "output.audio.started", turnId: turn.turnId, - payload: paramsForOutput.payload ?? {}, + payload: defaultTalkEventPayload(paramsForOutput.payload), }), }; },