diff --git a/src/app-server/client.ts b/src/app-server/client.ts index 8cde630c..21e4362c 100644 --- a/src/app-server/client.ts +++ b/src/app-server/client.ts @@ -372,11 +372,17 @@ export class AppServerClient { return this.request("turn/start", params); } - steerTurn(threadId: string, expectedTurnId: string, input: string | UserInput[]): Promise { + steerTurn( + threadId: string, + expectedTurnId: string, + input: string | UserInput[], + clientUserMessageId?: string | null, + ): Promise { return this.request("turn/steer", { threadId, expectedTurnId, input: toUserInput(input), + ...(clientUserMessageId !== undefined ? { clientUserMessageId } : {}), }); } diff --git a/src/features/chat/controllers/submission/turn-submission-controller.ts b/src/features/chat/controllers/submission/turn-submission-controller.ts index 805de42f..8ea0b538 100644 --- a/src/features/chat/controllers/submission/turn-submission-controller.ts +++ b/src/features/chat/controllers/submission/turn-submission-controller.ts @@ -112,13 +112,14 @@ export class TurnSubmissionController { } const codexInput = codexInputOverride ?? this.host.codexInput(text); + const localSteerId = `local-steer-${String(Date.now())}`; this.host.setDraft("", { clearSuggestions: true }); try { - await client.steerTurn(threadId, expectedTurnId, codexInput); + await client.steerTurn(threadId, expectedTurnId, codexInput, localSteerId); this.host.state.addLocalUserMessage( localUserMessageItemFromInput({ - id: `local-steer-${String(Date.now())}`, + id: localSteerId, text, turnId: expectedTurnId, referencedThread, diff --git a/tests/app-server/app-server-client.test.ts b/tests/app-server/app-server-client.test.ts index a395227b..9073b028 100644 --- a/tests/app-server/app-server-client.test.ts +++ b/tests/app-server/app-server-client.test.ts @@ -199,13 +199,14 @@ describe("AppServerClient", () => { getTransport().emitLine({ id: 1, result: { codexHome: "/tmp/codex" } satisfies Partial }); await connecting; - const steering = client.steerTurn("thread-1", "turn-1", "Please adjust course."); + const steering = client.steerTurn("thread-1", "turn-1", "Please adjust course.", "local-steer-1"); expect(getTransport().sent[2]).toMatchObject({ id: 2, method: "turn/steer", params: { threadId: "thread-1", expectedTurnId: "turn-1", + clientUserMessageId: "local-steer-1", input: [{ type: "text", text: "Please adjust course.", text_elements: [] }], }, }); diff --git a/tests/features/chat/controllers/submission/turn-submission-controller.test.ts b/tests/features/chat/controllers/submission/turn-submission-controller.test.ts index 924a977e..e09e895f 100644 --- a/tests/features/chat/controllers/submission/turn-submission-controller.test.ts +++ b/tests/features/chat/controllers/submission/turn-submission-controller.test.ts @@ -113,9 +113,12 @@ describe("TurnSubmissionController", () => { await controller.sendTurnText("follow up"); - expect(steerTurn).toHaveBeenCalledWith("thread", "turn", textInput("follow up")); + expect(steerTurn).toHaveBeenCalledWith("thread", "turn", textInput("follow up"), expect.stringMatching(/^local-steer-\d+$/)); expect(startTurn).not.toHaveBeenCalled(); expect(host.setStatus).toHaveBeenCalledWith("Steered current turn."); - expect(stateStore.getState().displayItems.some((item) => item.kind === "message" && item.text === "follow up")).toBe(true); + const localSteerId = steerTurn.mock.calls[0]?.[3]; + expect( + stateStore.getState().displayItems.some((item) => item.kind === "message" && item.id === localSteerId && item.text === "follow up"), + ).toBe(true); }); });