Skip to content

Commit d223e31

Browse files
committed
Pass active turn ID when stopping a running turn
- Send the current turn ID with thread.turn.interrupt commands - Update the reactor and browser test to preserve stop-target context
1 parent 7b0770d commit d223e31

4 files changed

Lines changed: 45 additions & 1 deletion

File tree

apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,7 @@ describe("ProviderCommandReactor", () => {
11431143
await waitFor(() => harness.interruptTurn.mock.calls.length === 1);
11441144
expect(harness.interruptTurn.mock.calls[0]?.[0]).toEqual({
11451145
threadId: "thread-1",
1146+
turnId: "turn-1",
11461147
});
11471148
});
11481149

apps/server/src/orchestration/Layers/ProviderCommandReactor.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,10 @@ const make = Effect.gen(function* () {
715715
}
716716

717717
// Orchestration turn ids are not provider turn ids, so interrupt by session.
718-
yield* providerService.interruptTurn({ threadId: event.payload.threadId });
718+
yield* providerService.interruptTurn({
719+
threadId: event.payload.threadId,
720+
...(event.payload.turnId !== undefined ? { turnId: event.payload.turnId } : {}),
721+
});
719722
});
720723

721724
const processApprovalResponseRequested = Effect.fnUntraced(function* (

apps/web/src/components/ChatView.browser.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1598,6 +1598,42 @@ describe("ChatView timeline estimator parity (full app)", () => {
15981598
}
15991599
});
16001600

1601+
it("includes the active turn id when stopping a running turn", async () => {
1602+
wsRequests.length = 0;
1603+
1604+
const mounted = await mountChatView({
1605+
viewport: DEFAULT_VIEWPORT,
1606+
snapshot: createSnapshotForTargetUser({
1607+
targetMessageId: "msg-user-stop-button-turn-id" as MessageId,
1608+
targetText: "stop button turn id target",
1609+
sessionStatus: "running",
1610+
activeTurnId: "turn-stop-button-turn-id" as TurnId,
1611+
}),
1612+
});
1613+
1614+
try {
1615+
const stopButton = await waitForElement(
1616+
() => document.querySelector<HTMLButtonElement>('button[aria-label="Stop generation"]'),
1617+
"Unable to find stop generation button.",
1618+
);
1619+
1620+
stopButton.click();
1621+
1622+
await vi.waitFor(
1623+
() =>
1624+
wsRequests.some(
1625+
(request) =>
1626+
request._tag === ORCHESTRATION_WS_METHODS.dispatchCommand &&
1627+
request.type === "thread.turn.interrupt" &&
1628+
request.turnId === "turn-stop-button-turn-id",
1629+
),
1630+
{ timeout: 8_000, interval: 16 },
1631+
);
1632+
} finally {
1633+
await mounted.cleanup();
1634+
}
1635+
});
1636+
16011637
it("keeps the new thread selected after clicking the new-thread button", async () => {
16021638
const mounted = await mountChatView({
16031639
viewport: DEFAULT_VIEWPORT,

apps/web/src/components/ChatView.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3867,6 +3867,10 @@ export default function ChatView({
38673867
type: "thread.turn.interrupt",
38683868
commandId: newCommandId(),
38693869
threadId: activeThread.id,
3870+
...(activeThread.session?.activeTurnId !== undefined &&
3871+
activeThread.session?.activeTurnId !== null
3872+
? { turnId: activeThread.session.activeTurnId }
3873+
: {}),
38703874
createdAt: new Date().toISOString(),
38713875
});
38723876
};

0 commit comments

Comments
 (0)