1363 lines · 45.5 KB · TypeScript
RawDownload
import {  MessageId,  NodeId,  PlanId,  ProviderInstanceId,  ProviderThreadId,  RunAttemptId,  RunId,  ThreadId,  TurnItemId,  type OrchestrationV2ProjectedTurnItem,  type OrchestrationV2ExecutionNode,  type OrchestrationV2RunAttempt,  type OrchestrationV2TurnItem,} from "@t3tools/contracts";import * as DateTime from "effect/DateTime";import { describe, expect, it } from "vite-plus/test"; import {  deriveActivePlanState,  deriveTimelineEntriesFromVisibleTurnItems,  deriveTimelineEntriesFromVisibleTurnItemsWithState,  deriveRevertTurnCountByUserMessageId,  findLatestProposedPlan,  isLatestRunSettled,  selectHandoffImageResources,  selectMessageImageResources,  createMessageAttachmentPreviewProjector,  providerErrorPresentation,  type TimelineEntry,  workEntryIndicatesToolFailure,  workEntryDisplayIndicatesToolFailure,  workEntryIndicatesToolSuccess,} from "./session-logic";import { makeStreamingTimelineFixture, makeThreadProjectionFixture } from "./test-fixtures";import type { ChatMessage } from "./types"; describe("V2 session presentation", () => {  it("uses run status as the settlement boundary", () => {    const runId = RunId.make("run-1");    expect(      isLatestRunSettled(        {          runId,          status: "completed",          startedAt: "2026-06-20T00:00:00.000Z",          completedAt: "2026-06-20T00:01:00.000Z",        },        null,      ),    ).toBe(true);    expect(      isLatestRunSettled(        { runId, status: "running", startedAt: null, completedAt: null },        { status: "running", activeRunId: runId },      ),    ).toBe(false);    expect(      isLatestRunSettled(        { runId, status: "queued", startedAt: null, completedAt: null },        { status: "running", activeRunId: RunId.make("run-active") },      ),    ).toBe(false);  });   it("labels provider retry progress, delay, recovery, and exhaustion", () => {    const now = DateTime.makeUnsafe("2026-06-20T00:00:00.000Z");    const retryItem = {      id: TurnItemId.make("item-provider-retry"),      threadId: ThreadId.make("thread-provider-retry"),      runId: RunId.make("run-provider-retry"),      nodeId: null,      providerThreadId: null,      providerTurnId: null,      nativeItemRef: null,      parentItemId: null,      ordinal: 1,      status: "running" as const,      title: "Provider retry",      startedAt: now,      completedAt: null,      updatedAt: now,      type: "error" as const,      failure: {        class: "provider_error" as const,        message: "Claude API overloaded.",        code: "api_error_529",        retryable: true,      },      retry: {        attempt: 2,        maxAttempts: 10,        retryDelayMs: 1_500,      },    } satisfies Extract<OrchestrationV2TurnItem, { readonly type: "error" }>;     expect(providerErrorPresentation(retryItem)).toEqual({      label: "Retrying provider (2/10)",      detail: "Claude API overloaded. Retrying in 1.5s.",    });    expect(      providerErrorPresentation({        ...retryItem,        status: "completed",        completedAt: now,      }),    ).toMatchObject({ label: "Provider recovered (2/10 retries)" });    expect(      providerErrorPresentation({        ...retryItem,        status: "failed",        retry: { ...retryItem.retry, attempt: 10 },        completedAt: now,      }),    ).toMatchObject({ label: "Provider error after 10/10 retries" });  });   it("selects the latest proposed plan for a run", () => {    const runId = RunId.make("run-1");    const planId = PlanId.make("plan-1");    const nodeId = NodeId.make("node-plan");    const now = DateTime.makeUnsafe("2026-06-20T00:00:01.000Z");    const baseProjection = makeThreadProjectionFixture();    const plan = findLatestProposedPlan(      {        ...baseProjection,        plans: [          {            id: planId,            threadId: baseProjection.thread.id,            nodeId,            kind: "proposed_plan" as const,            markdown: "Plan",            status: "active" as const,            runId,          },        ],        turnItems: [          {            id: TurnItemId.make("item-plan"),            threadId: baseProjection.thread.id,            nodeId,            providerThreadId: null,            providerTurnId: null,            nativeItemRef: null,            parentItemId: null,            ordinal: 0,            status: "completed" as const,            title: null,            startedAt: now,            completedAt: now,            updatedAt: now,            type: "proposed_plan" as const,            planId,            markdown: "Plan",            streaming: false,            runId,          },        ],        updatedAt: now,      },      runId,    );    expect(plan?.planMarkdown).toBe("Plan");  });   it("assigns run rollback to the turn-start message instead of a later steer", () => {    const runId = RunId.make("run-steered");    const turnStartMessageId = MessageId.make("message-turn-start");    const steerMessageId = MessageId.make("message-steer");    const assistantMessageId = MessageId.make("message-assistant");    const messages: ChatMessage[] = [      {        id: turnStartMessageId,        role: "user",        text: "Start",        runId,        inputIntent: "turn_start",        streaming: false,        createdAt: "2026-06-20T00:00:00.000Z",        updatedAt: "2026-06-20T00:00:00.000Z",      },      {        id: steerMessageId,        role: "user",        text: "Steer",        runId,        inputIntent: "steer",        streaming: false,        createdAt: "2026-06-20T00:00:01.000Z",        updatedAt: "2026-06-20T00:00:01.000Z",      },      {        id: assistantMessageId,        role: "assistant",        text: "Done",        runId,        streaming: false,        createdAt: "2026-06-20T00:00:02.000Z",        updatedAt: "2026-06-20T00:00:02.000Z",      },    ];    const timelineEntries: TimelineEntry[] = messages.map((message): TimelineEntry => ({      id: message.id,      kind: "message",      createdAt: message.createdAt,      message,    }));     const targets = deriveRevertTurnCountByUserMessageId({      timelineEntries,      checkpoints: [        {          runId,          checkpointTurnCount: 1,          checkpointRef: "checkpoint-run-1" as never,          status: "ready",          files: [],          assistantMessageId,          completedAt: "2026-06-20T00:00:03.000Z",        },      ],    });     expect([...targets]).toEqual([[turnStartMessageId, 0]]);    expect(targets.has(steerMessageId)).toBe(false);  });   it("uses visible turn item order and keeps provider errors in the work log", () => {    const now = DateTime.makeUnsafe("2026-06-20T00:00:00.000Z");    const threadId = ThreadId.make("thread-visible");    const runId = RunId.make("run-visible");    const base = (id: string, ordinal: number) => ({      id: TurnItemId.make(id),      threadId,      runId,      nodeId: null,      providerThreadId: null,      providerTurnId: null,      nativeItemRef: null,      parentItemId: null,      ordinal,      status: "completed" as const,      title: null,      startedAt: now,      completedAt: now,      updatedAt: now,    });    const userItem = {      ...base("item-user", 0),      type: "user_message" as const,      messageId: MessageId.make("message-user"),      inputIntent: "turn_start" as const,      text: "Start",      attachments: [],      createdBy: "user" as const,      creationSource: "web" as const,    } satisfies OrchestrationV2TurnItem;    const requestItem = {      ...base("item-interrupt-request", 1),      type: "run_interrupt_request" as const,      message: "Stopping",    } satisfies OrchestrationV2TurnItem;    const commandItem = {      ...base("item-command", 2),      type: "command_execution" as const,      input: "sleep 1",      output: "done",      exitCode: 0,    } satisfies OrchestrationV2TurnItem;    const resultItem = {      ...base("item-interrupt-result", 3),      type: "run_interrupt_result" as const,      message: "Stopped",    } satisfies OrchestrationV2TurnItem;    const todoItem = {      ...base("item-todo", 4),      type: "todo_list" as const,      planId: PlanId.make("plan-visible"),      explanation: "Keep task detail in the Tasks panel",      steps: [        { id: "step-1", text: "First", status: "completed" as const },        { id: "step-2", text: "Second", status: "pending" as const },      ],    } satisfies OrchestrationV2TurnItem;    const errorItem = {      ...base("item-error", 5),      status: "failed" as const,      type: "error" as const,      failure: {        class: "validation_error" as const,        message: "Invalid reasoning effort.",        code: "invalid_request",        retryable: false,      },    } satisfies OrchestrationV2TurnItem;    const threadCreatedItem = {      ...base("item-thread-created", 6),      type: "thread_created" as const,      title: "Follow-up thread",      targetThreadId: ThreadId.make("thread-follow-up"),      targetRunId: RunId.make("run-follow-up"),      targetProviderInstanceId: ProviderInstanceId.make("claude-default"),      targetModel: "claude-sonnet-4-6",    } satisfies OrchestrationV2TurnItem;    const workspacePreparationItem = {      ...base("item-workspace-preparation", 7),      type: "command_execution" as const,      title: "Workspace ready",      input: "Preparing workspace",      output: "Workspace preparation completed.",      exitCode: 0,    } satisfies OrchestrationV2TurnItem;    const visibleTurnItems: ReadonlyArray<OrchestrationV2ProjectedTurnItem> = [      userItem,      requestItem,      commandItem,      resultItem,      todoItem,      errorItem,      threadCreatedItem,      workspacePreparationItem,    ].map((item, position) => ({      position,      visibility: "local" as const,      sourceThreadId: threadId,      sourceItemId: item.id,      item,    }));     const entries = deriveTimelineEntriesFromVisibleTurnItems({      visibleTurnItems,      optimisticMessages: [],    });     expect(entries.map((entry) => [entry.kind, entry.id])).toEqual([      ["message", userItem.messageId],      ["event", requestItem.id],      ["work", commandItem.id],      ["event", resultItem.id],      ["work", errorItem.id],      ["event", threadCreatedItem.id],    ]);    const commandEntry = entries[2];    const userEntry = entries[0];    expect(userEntry?.kind).toBe("message");    if (userEntry?.kind === "message") {      expect(userEntry.projectedItem).toBe(visibleTurnItems[0]);      expect(userEntry.message.inputIntent).toBe("turn_start");      expect(userEntry.message.createdBy).toBe("user");      expect(userEntry.message.creationSource).toBe("web");    }    expect(commandEntry?.kind).toBe("work");    if (commandEntry?.kind === "work") {      expect(commandEntry.entry.projectedItem).toBe(visibleTurnItems[2]);      expect(commandEntry.entry.structuredPayload).toBe(commandItem);    }    const errorEntry = entries[4];    expect(errorEntry?.kind).toBe("work");    if (errorEntry?.kind === "work") {      expect(errorEntry.entry.projectedItem).toBe(visibleTurnItems[5]);      expect(errorEntry.entry.label).toBe("Provider error");      expect(errorEntry.entry.detail).toBe("Invalid reasoning effort.");      expect(errorEntry.entry.tone).toBe("info");      expect(errorEntry.entry.toolLifecycleStatus).toBe("failed");    }    const threadCreatedEntry = entries[5];    expect(threadCreatedEntry?.kind).toBe("event");    if (threadCreatedEntry?.kind === "event") {      expect(threadCreatedEntry.projectedItem.item.type).toBe("thread_created");    }  });   it.each(["pending", "running", "completed"] as const)(    "keeps %s task progress available to the composer and out of the timeline",    (stepStatus) => {      const projection = makeThreadProjectionFixture();      const now = DateTime.makeUnsafe("2026-09-04T00:00:00.000Z");      const runId = RunId.make("run-tasks");      const nodeId = NodeId.make("node-tasks");      const planId = PlanId.make("plan-tasks");      const steps = [{ id: "step-1", text: "Verify the change", status: stepStatus }];      const item = {        id: TurnItemId.make("item-tasks"),        threadId: projection.thread.id,        runId,        nodeId,        providerThreadId: null,        providerTurnId: null,        nativeItemRef: null,        parentItemId: null,        ordinal: 0,        status: "completed" as const,        title: null,        startedAt: now,        completedAt: now,        updatedAt: now,        type: "todo_list" as const,        planId,        steps,      } satisfies OrchestrationV2TurnItem;      const plans = [        {          id: planId,          threadId: projection.thread.id,          runId,          nodeId,          kind: "todo_list" as const,          status: "active" as const,          steps,        },      ];       expect(        deriveTimelineEntriesFromVisibleTurnItems({          visibleTurnItems: [            {              position: 0,              visibility: "local",              sourceThreadId: projection.thread.id,              sourceItemId: item.id,              item,            },          ],          optimisticMessages: [],          plans,        }),      ).toEqual([]);      expect(        deriveActivePlanState({ ...projection, plans, turnItems: [item] }, runId),      ).toMatchObject({        runId,        steps: [          {            step: "Verify the change",            status: stepStatus === "running" ? "inProgress" : stepStatus,          },        ],      });    },  );   it("keeps failed tool items tool-toned so groups still summarize", () => {    const failedCommand = {      id: TurnItemId.make("item-failed-command"),      threadId: ThreadId.make("thread-1"),      runId: RunId.make("run-1"),      nodeId: null,      providerThreadId: null,      providerTurnId: null,      nativeItemRef: null,      parentItemId: null,      ordinal: 0,      status: "failed" as const,      title: null,      startedAt: null,      completedAt: null,      updatedAt: DateTime.nowUnsafe(),      type: "command_execution" as const,      input: "ssh host true",      output: "connection refused",      exitCode: 255,    } satisfies OrchestrationV2TurnItem;    const entries = deriveTimelineEntriesFromVisibleTurnItems({      visibleTurnItems: [        {          position: 0,          visibility: "local" as const,          sourceThreadId: ThreadId.make("thread-1"),          sourceItemId: failedCommand.id,          item: failedCommand,        } as never,      ],      optimisticMessages: [],    });    const entry = entries[0];    expect(entry?.kind).toBe("work");    if (entry?.kind === "work") {      // An exit-code failure is still an ordinary tool row: the failed      // lifecycle status carries the marker, and an "error" tone here would      // knock the whole group out of the "Ran N commands" summary.      expect(entry.entry.tone).toBe("tool");      expect(entry.entry.toolLifecycleStatus).toBe("failed");    }  });   it("waits for a dispatched turn item before adding queued input to the timeline", () => {    const projection = makeThreadProjectionFixture();    const now = DateTime.makeUnsafe("2026-06-20T00:00:00.000Z");    const runId = RunId.make("run-dispatched-queued");    const messageId = MessageId.make("message-dispatched-queued");    const optimisticMessage = {      id: messageId,      role: "user" as const,      text: "Queued input",      runId: null,      inputIntent: "queued_turn" as const,      streaming: false,      createdAt: DateTime.formatIso(now),      updatedAt: DateTime.formatIso(now),

Lines 1–500 of 1363.