1291 lines · 39.9 KB · TypeScript
RawDownload
import {  MessageId,  NodeId,  PlanId,  ProviderInstanceId,  ProviderDriverKind,  ProviderThreadId,  RunId,  RunAttemptId,  ThreadId,  TurnItemId,  type OrchestrationV2RunAttempt,  type OrchestrationV2ProjectedTurnItem,  type OrchestrationV2TurnItem,} from "@t3tools/contracts";import * as DateTime from "effect/DateTime";import { describe, expect, it } from "vite-plus/test"; import {  buildThreadFeed,  deriveThreadFeedPresentation,  threadFeedActivityIsVisible,  threadFeedRunIsUnsettled,  type ThreadFeedActivity,  type ThreadFeedEntry,  togglePendingUserInputOptionSelection,  setPendingUserInputCustomAnswer,  isPendingUserInputOptionSelected,  buildPendingUserInputAnswers,} from "./threadActivity"; const threadId = ThreadId.make("thread-1");const sourceThreadId = ThreadId.make("thread-source");const runId = RunId.make("run-1"); it("keeps historical plan detail accessible from its paged turn item", () => {  const item = {    ...base("historical-plan", "2026-08-29T00:00:00.000Z", 1),    type: "proposed_plan",    planId: "plan-historical",    markdown: "Full historical plan text",    streaming: false,  } as OrchestrationV2TurnItem;   const entries = buildThreadFeed([projected(item, 0)]);  const activity = entries.flatMap((entry) =>    entry.type === "activity-group" ? entry.activities : [],  )[0];  expect(activity?.detail).toBe("Full historical plan text");  expect(activity?.getFullDetail()).toContain("Full historical plan text");}); function base(id: string, updatedAt: string, ordinal: number) {  const timestamp = DateTime.makeUnsafe(updatedAt);  return {    id: TurnItemId.make(id),    threadId,    runId,    nodeId: null,    providerThreadId: null,    providerTurnId: null,    nativeItemRef: null,    parentItemId: null,    ordinal,    status: "completed" as const,    title: null,    startedAt: timestamp,    completedAt: timestamp,    updatedAt: timestamp,  };} function projected(  item: OrchestrationV2TurnItem,  position: number,  visibility: OrchestrationV2ProjectedTurnItem["visibility"] = "local",): OrchestrationV2ProjectedTurnItem {  return {    position,    visibility,    sourceThreadId: visibility === "local" ? threadId : sourceThreadId,    sourceItemId: item.id,    item,  };} function userMessage(updatedAt = "2026-06-20T00:00:01.000Z") {  return {    ...base("item-user", updatedAt, 0),    type: "user_message" as const,    messageId: MessageId.make("message-user"),    createdBy: "user" as const,    creationSource: "mobile" as const,    inputIntent: "turn_start" as const,    text: "Run checks",    attachments: [],  };} function command(updatedAt = "2026-06-20T00:00:02.000Z") {  return {    ...base("item-command", updatedAt, 1),    type: "command_execution" as const,    input: "vp check",    output: "ok",    exitCode: 0,  };} function assistantMessage(updatedAt = "2026-06-20T00:00:03.000Z") {  return {    ...base("item-assistant", updatedAt, 2),    type: "assistant_message" as const,    messageId: MessageId.make("message-assistant"),    text: "Done",    streaming: false,  };} describe("buildThreadFeed", () => {  it("adds local feedback messages to an otherwise server-authored feed", () => {    const feed = buildThreadFeed([], {      localMessages: [        {          id: MessageId.make("feedback-local"),          role: "assistant",          text: "Feedback sent to OpenAI.\n\nThread ID: `codex-thread-1`",          turnId: null,          streaming: false,          createdAt: "2026-08-29T00:00:00.000Z",          updatedAt: "2026-08-29T00:00:00.000Z",        },      ],    });     expect(feed).toHaveLength(1);    expect(feed[0]).toMatchObject({      type: "message",      message: {        id: "feedback-local",        role: "assistant",        text: expect.stringContaining("codex-thread-1"),      },    });  });   it("anchors feedback before later committed turns and appends true optimistic messages", () => {    const laterUser = {      ...userMessage("2026-08-29T00:00:05.000Z"),      id: TurnItemId.make("item-later-user"),      messageId: MessageId.make("message-later-user"),      ordinal: 2,      text: "Later user turn",    };    const laterAssistant = {      ...assistantMessage("2026-08-29T00:00:04.000Z"),      id: TurnItemId.make("item-later-assistant"),      messageId: MessageId.make("message-later-assistant"),      ordinal: 3,      text: "Later assistant turn",    };    const localMessage = (id: string, role: "user" | "assistant") => ({      id: MessageId.make(id),      role,      text: id,      turnId: null,      streaming: false,      createdAt: "2026-08-29T00:00:03.000Z",      updatedAt: "2026-08-29T00:00:03.000Z",    });    const feed = buildThreadFeed(      [        projected(userMessage("2026-08-29T00:00:01.000Z"), 0),        projected(laterUser, 1),        projected(laterAssistant, 2),      ],      {        anchoredMessages: [          localMessage("feedback-user", "user"),          localMessage("feedback-assistant", "assistant"),          localMessage("message-later-user", "user"),        ],        localMessages: [          {            ...localMessage("optimistic-user", "user"),            createdAt: "2026-08-29T00:00:00.000Z",          },        ],      },    );    const messages = feed.filter((entry) => entry.type === "message");     expect(messages.map((entry) => entry.id)).toEqual([      "message-user",      "feedback-user",      "feedback-assistant",      "message-later-user",      "message-later-assistant",      "optimistic-user",    ]);    expect(      messages        .filter((entry) => entry.id.startsWith("feedback-"))        .every((entry) => entry.message.projectedItem === undefined),    ).toBe(true);  });   it("keeps prominent activity visible while it is running", () => {    expect(      threadFeedActivityIsVisible({ prominent: true, status: "neutral", toolLike: true }),    ).toBe(true);    expect(      threadFeedActivityIsVisible({ prominent: false, status: "neutral", toolLike: true }),    ).toBe(false);  });   it("keeps provider notices visible outside completed work folds without failure styling", () => {    const message = "Safeguards flagged this message. Switched to Opus 4.8.";    const item = {      ...base("item-system-notice", "2026-06-20T00:00:02.000Z", 1),      type: "system_notice" as const,      message,    };    const feed = buildThreadFeed([projected(item, 0)]);    const presented = deriveThreadFeedPresentation(feed, null, new Set());    const activities = presented.flatMap((entry) =>      entry.type === "activity-group" ? entry.activities : [],    );    expect(activities).toHaveLength(1);    expect(activities[0]).toMatchObject({      summary: message,      detail: message,      prominent: true,      toolLike: false,      status: null,      icon: "warning",      workEntry: { tone: "info", itemType: "system_notice" },    });    expect(presented.some((entry) => entry.type === "run-fold")).toBe(false);  });   it("presents provider retries as visible work-log activity", () => {    const retryBase = {      ...base("item-provider-retry", "2026-06-20T00:00:02.000Z", 1),      type: "error" as const,      failure: {        class: "transport_error" as const,        message: "The response stream disconnected.",        code: "responseStreamDisconnected",        retryable: true,      },      retry: {        attempt: 2,        maxAttempts: 5,        retryDelayMs: null,      },    };    const runningFeed = buildThreadFeed([      projected(        {          ...retryBase,          status: "running",          title: "Provider retry",          completedAt: null,        },        0,      ),    ]);    const recoveredFeed = buildThreadFeed([      projected(        {          ...retryBase,          status: "completed",          title: "Provider recovered",        },        0,      ),    ]);    const failedFeed = buildThreadFeed([      projected(        {          ...retryBase,          status: "failed",          title: "Provider retry failed",        },        0,      ),      projected(command("2026-06-20T00:00:03.000Z"), 1),    ]);    const runningActivity = runningFeed.find((entry) => entry.type === "activity-group")      ?.activities[0];    const recoveredActivity = recoveredFeed.find((entry) => entry.type === "activity-group")      ?.activities[0];    if (runningActivity === undefined || recoveredActivity === undefined) {      throw new Error("Expected provider retry work-log activities.");    }     expect(runningActivity).toMatchObject({      summary: "Provider retry",      status: "neutral",      toolLike: false,    });    expect(threadFeedActivityIsVisible(runningActivity)).toBe(true);    expect(recoveredActivity).toMatchObject({      summary: "Provider recovered",      status: "success",      toolLike: false,    });    const failedPresentation = deriveThreadFeedPresentation(      failedFeed,      { runId, status: "running", startedAt: null, completedAt: null },      new Set(),    );    expect(failedPresentation.map((entry) => entry.type)).toEqual([      "activity-group",      "work-toggle",    ]);    expect(      failedPresentation[0]?.type === "activity-group"        ? failedPresentation[0].activities[0]?.summary        : null,    ).toBe("Provider retry failed");  });   it.each(["pending", "running", "completed"] as const)(    "omits %s task progress without hiding adjacent conversation items",    (stepStatus) => {      const todoItem = {        ...base("item-tasks", "2026-06-20T00:00:02.500Z", 2),        type: "todo_list" as const,        planId: PlanId.make("plan-tasks"),        steps: [{ id: "step-1", text: "Verify the change", status: stepStatus }],      } satisfies OrchestrationV2TurnItem;      const user = projected(userMessage(), 0);      const tool = projected(command(), 1);      const assistant = projected(assistantMessage(), 3);       expect(buildThreadFeed([user, tool, projected(todoItem, 2), assistant])).toEqual(        buildThreadFeed([user, tool, assistant]),      );    },  );   it("hides synthetic workspace preparation activity", () => {    const workspacePreparation = projected(      {        ...command(),        title: "Workspace ready",        input: "Preparing workspace",        output: "Workspace preparation completed.",      },      0,    );     expect(buildThreadFeed([workspacePreparation])).toEqual([]);  });   it("does not treat a queued-only run as live feed activity", () => {    expect(      threadFeedRunIsUnsettled({        runId,        status: "queued",        startedAt: null,        completedAt: null,      }),    ).toBe(false);    expect(      threadFeedRunIsUnsettled({        runId,        status: "running",        startedAt: "2026-06-20T00:00:01.000Z",        completedAt: null,      }),    ).toBe(true);    expect(      threadFeedRunIsUnsettled({        runId,        status: "completed",        startedAt: "2026-06-20T00:00:01.000Z",        completedAt: null,      }),    ).toBe(true);    expect(      threadFeedRunIsUnsettled({        runId,        status: "waiting",        startedAt: "2026-06-20T00:00:01.000Z",        completedAt: null,      }),    ).toBe(true);  });   it("adds queued input only after dispatch creates its turn item", () => {    const dispatchedRunId = RunId.make("run-dispatched-queued");    const dispatchedMessageId = MessageId.make("message-dispatched-queued");    expect(buildThreadFeed([])).toEqual([]);     const promotedEntries = buildThreadFeed([      projected(        {          ...userMessage(),          id: TurnItemId.make("item-dispatched-queued"),          runId: dispatchedRunId,          messageId: dispatchedMessageId,          inputIntent: "turn_start",        },        0,      ),    ]);    expect(promotedEntries.map((entry) => entry.id)).toEqual([dispatchedMessageId]);    expect(      promotedEntries[0]?.type === "message" ? promotedEntries[0].message.inputIntent : undefined,    ).toBe("turn_start");  });   it("hides the interruption request and keeps the terminal result", () => {    const request = projected(      {        ...base("item-interrupt-request", "2026-06-20T00:00:02.000Z", 1),        type: "run_interrupt_request",        message: "Interrupt requested",      },      0,    );    const result = projected(      {        ...base("item-interrupt-result", "2026-06-20T00:00:03.000Z", 2),        type: "run_interrupt_result",        message: "Run interrupted before provider start",      },      1,    );     const activities = buildThreadFeed([request, result]).flatMap((entry) =>      entry.type === "activity-group" ? entry.activities : [],    );     expect(activities).toHaveLength(1);    expect(activities[0]?.summary).toBe("Run interrupted");    expect(activities[0]?.detail).toBe("Run interrupted before provider start");    expect(      deriveThreadFeedPresentation(        buildThreadFeed([request, result]),        {          runId,          status: "interrupted",          startedAt: "2026-06-20T00:00:01.000Z",          completedAt: "2026-06-20T00:00:03.000Z",        },        new Set(),      ).some((entry) => entry.type === "run-fold"),    ).toBe(false);  });   it("preserves authoritative V2 order instead of sorting reconstructed collections", () => {    const rows = [      projected(userMessage("2026-06-20T00:00:03.000Z"), 0),      projected(command("2026-06-20T00:00:01.000Z"), 1),      projected(assistantMessage("2026-06-20T00:00:02.000Z"), 2),    ];     const feed = buildThreadFeed(rows);    expect(feed.map((entry) => entry.type)).toEqual(["message", "activity-group", "message"]);    expect(feed.map((entry) => entry.id)).toEqual([      "message-user",      "local:thread-1:item-command",      "message-assistant",    ]);    const activity = feed.find((entry) => entry.type === "activity-group")?.activities[0];    expect(activity?.projectedItem).toBe(rows[1]);    expect(activity?.getFullDetail()).toContain('"input": "vp check"');  });   it("keeps adjacent work from different V2 attempts in separate groups", () => {    const firstRootNodeId = NodeId.make("node-attempt-1");    const secondRootNodeId = NodeId.make("node-attempt-2");    const firstCommand = { ...command(), nodeId: firstRootNodeId };    const secondCommand = {      ...command("2026-06-20T00:00:03.000Z"),      id: TurnItemId.make("item-command-retry"),      ordinal: 2,      nodeId: secondRootNodeId,    };    const attempts = [      {        id: RunAttemptId.make("attempt-1"),        runId,        attemptOrdinal: 1,        rootNodeId: firstRootNodeId,        providerInstanceId: ProviderInstanceId.make("provider-instance-1"),        providerThreadId: ProviderThreadId.make("provider-thread-1"),        providerTurnId: null,        reason: "initial",        status: "completed",        startedAt: DateTime.makeUnsafe("2026-06-20T00:00:01.000Z"),        completedAt: DateTime.makeUnsafe("2026-06-20T00:00:02.000Z"),      },      {        id: RunAttemptId.make("attempt-2"),        runId,

Lines 1–500 of 1291.