import { NodeId, RuntimeRequestId, TurnItemId, type OrchestrationV2ThreadProjection, } from "@t3tools/contracts"; import { describe, expect, it } from "vite-plus/test"; import { v2Now, v2Projection } from "./orchestrationV2TestFixtures.ts"; import { derivePendingThreadRequests } from "./threadRequests.ts"; const requestId = RuntimeRequestId.make("async-question"); const nodeId = NodeId.make("async-question-node"); const projection: OrchestrationV2ThreadProjection = { ...v2Projection, runtimeRequests: [ { id: requestId, nodeId, providerTurnId: null, nativeRequestRef: null, kind: "user_input", status: "pending", responseCapability: { type: "message" }, createdAt: v2Now, resolvedAt: null, }, ], turnItems: [ { id: TurnItemId.make("async-question-item"), threadId: v2Projection.thread.id, runId: null, nodeId, providerThreadId: null, providerTurnId: null, nativeItemRef: null, parentItemId: null, ordinal: 0, status: "completed", title: null, startedAt: v2Now, completedAt: v2Now, updatedAt: v2Now, type: "user_input_request", requestId, responseMode: "message", questions: [ { id: "next", header: "Next", question: "What should happen next?", required: true, allowCustomAnswer: false, options: [{ label: "Continue", value: " continue ", description: "Resume work" }], }, ], }, ], }; describe("pending v2 questions", () => { it("keeps message responses available after the originating runtime exits", () => { expect(projection.providerSessions).toEqual([]); expect(derivePendingThreadRequests(projection).userInputs).toEqual([ { requestId, createdAt: "2026-06-20T00:00:00.000Z", responseCapability: "message", responseMode: "message", questions: [ { id: "next", header: "Next", question: "What should happen next?", required: true, allowCustomAnswer: false, multiSelect: false, options: [{ label: "Continue", value: " continue ", description: "Resume work" }], }, ], }, ]); }); it("removes answered requests from the composer while retaining their answers in projection data", () => { const answered = { ...projection, runtimeRequests: projection.runtimeRequests.map((request) => ({ ...request, status: "resolved" as const, resolvedAt: v2Now, answers: { next: " continue " }, })), }; expect(derivePendingThreadRequests(answered).userInputs).toEqual([]); expect(answered.runtimeRequests[0]?.answers).toEqual({ next: " continue " }); }); });