Skip to content
13 changes: 13 additions & 0 deletions packages/client-runtime/src/handoff.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ describe("handoff endpoints shared by web and mobile", () => {

const projection = {
runs: [{ ...run("source", 1, from, "source"), startedAt: "2026-10-11T00:00:00Z" }],
visibleTurnItems: [{ item: { type: "user_message" } }],
} as unknown as OrchestrationV2ThreadProjection;

describe("pending context handoff", () => {
Expand All @@ -89,6 +90,18 @@ describe("pending context handoff", () => {
expect(resolvePendingHandoff(projection, { instanceId: from, model: "source" }, [])).toBeNull();
expect(resolvePendingHandoff(projection, { instanceId: from, model: "other" }, [])).toBeNull();
});
it("stays hidden while the thread's timeline is empty", () => {
const empty = { ...projection, visibleTurnItems: [] };
expect(resolvePendingHandoff(empty, { instanceId: to, model: "first" }, [])).toBeNull();
// Paging can leave a window of only tool rows after the prompt.
const toolRowsOnly = {
...projection,
visibleTurnItems: [{ item: { type: "command_execution" } }],
} as unknown as OrchestrationV2ThreadProjection;
expect(
resolvePendingHandoff(toolRowsOnly, { instanceId: to, model: "first" }, [])?.to.model,
).toBe("first");
});
it("only previews a same-provider model change when it needs a fresh native thread", () => {
const providers = [
{ instanceId: from, requiresNewThreadForModelChange: true },
Expand Down
5 changes: 4 additions & 1 deletion packages/client-runtime/src/handoff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ export function resolvePendingHandoff(
selection: ModelSelection | null | undefined,
providers: ReadonlyArray<ServerProvider>,
): HandoffEndpoints | null {
const from = projection?.runs.findLast(
// A thread with an empty timeline has nothing to hand off. Any loaded row
// counts: paging can leave a window of only tool rows.
if (!projection?.visibleTurnItems.length) return null;
const from = projection.runs.findLast(
(run) => run.startedAt !== null && run.status !== "rolled_back",
)?.modelSelection;
if (from === undefined || selection == null) return null;
Expand Down
Loading