98 lines · 2.8 KB · TypeScript
RawDownload
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  " });  });});