186 lines · 6.5 KB · TypeScript
RawDownload
import { describe, expect, it } from "@effect/vitest";import {  CommandId,  EnvironmentId,  MessageId,  EventId,  TurnItemId,  RuntimeRequestId,  type OrchestrationV2ShellSnapshot,} from "@t3tools/contracts";import * as DateTime from "effect/DateTime";import * as Effect from "effect/Effect";import * as Option from "effect/Option";import * as Schema from "effect/Schema"; import {  ORCHESTRATION_CACHE_SCHEMA_VERSION,  StoredOrchestrationShellSnapshot,  StoredOrchestrationThreadSnapshot,  decodeOrDiscardOrchestrationCache,} from "./orchestrationCache.ts";import {  v2Now,  v2Projection,  v2ShellSnapshot,  v2ThreadShell,  v2ThreadId,} from "../state/orchestrationV2TestFixtures.ts"; import { applyOrchestrationV2ProjectionEvent } from "../state/orchestrationV2Projection.ts"; const environmentId = EnvironmentId.make("environment-cache-test");const StoredShellSnapshotJson = Schema.fromJsonString(StoredOrchestrationShellSnapshot);const StoredThreadSnapshotJson = Schema.fromJsonString(StoredOrchestrationThreadSnapshot);const encodeStoredShellSnapshot = Schema.encodeSync(StoredOrchestrationShellSnapshot);const decodeStoredShellSnapshotSync = Schema.decodeUnknownSync(StoredOrchestrationShellSnapshot);const encodeStoredShellSnapshotJson = Schema.encodeSync(StoredShellSnapshotJson);const decodeStoredShellSnapshotJson = Schema.decodeUnknownSync(StoredShellSnapshotJson);const encodeStoredThreadSnapshotJson = Schema.encodeSync(StoredThreadSnapshotJson);const decodeStoredThreadSnapshotJson = Schema.decodeUnknownSync(StoredThreadSnapshotJson); class TestCacheDecodeError extends Schema.TaggedErrorClass<TestCacheDecodeError>()(  "TestCacheDecodeError",  {    message: Schema.String,  },) {} const shellSnapshotWithSummaries: OrchestrationV2ShellSnapshot = {  ...v2ShellSnapshot,  threads: [    {      ...v2ThreadShell,      pendingRuntimeRequest: {        id: RuntimeRequestId.make("runtime-request-v2"),        kind: "command",        createdAt: v2Now,      },      latestVisibleMessage: {        id: MessageId.make("message-v2"),        role: "assistant",        text: "Done",        updatedAt: v2Now,      },      latestUserMessageAt: v2Now,      settledAt: v2Now,      titleRegeneration: {        requestId: CommandId.make("title-regeneration-v2"),        startedAt: v2Now,      },    },  ],}; describe("orchestration cache envelopes", () => {  it("preserves live provider notices through persisted thread snapshots", () => {    const item = {      id: TurnItemId.make("notice-cache"),      threadId: v2ThreadId,      runId: null,      nodeId: null,      providerThreadId: null,      providerTurnId: null,      nativeItemRef: null,      parentItemId: null,      ordinal: 1,      status: "completed" as const,      title: null,      startedAt: v2Now,      completedAt: v2Now,      updatedAt: v2Now,      type: "system_notice" as const,      message: "Safeguards flagged this message. Switched to Opus 4.8.",    };    const projection = applyOrchestrationV2ProjectionEvent(v2Projection, {      id: EventId.make("notice-cache-event"),      type: "turn-item.updated",      threadId: v2ThreadId,      occurredAt: v2Now,      payload: item,    });    expect(projection).not.toBeNull();    if (projection === null) throw new Error("Expected live notice projection");    const encoded = encodeStoredThreadSnapshotJson({      schemaVersion: ORCHESTRATION_CACHE_SCHEMA_VERSION,      environmentId,      threadId: v2ThreadId,      snapshot: { snapshotSequence: 5, projection },    });    const decoded = decodeStoredThreadSnapshotJson(encoded).snapshot.projection;    expect(decoded.turnItems.find((entry) => entry.id === item.id)).toEqual(item);    expect(decoded.visibleTurnItems.find((entry) => entry.item.id === item.id)?.item).toEqual(item);  });   it.effect("round-trips V2 shell and thread cache envelopes as JSON", () =>    Effect.sync(() => {      const encodedShell = encodeStoredShellSnapshotJson({        schemaVersion: ORCHESTRATION_CACHE_SCHEMA_VERSION,        environmentId,        snapshot: shellSnapshotWithSummaries,      });      const shell = decodeStoredShellSnapshotJson(encodedShell);      const encodedThread = encodeStoredThreadSnapshotJson({        schemaVersion: ORCHESTRATION_CACHE_SCHEMA_VERSION,        environmentId,        threadId: v2ThreadId,        snapshot: { snapshotSequence: 4, projection: v2Projection },      });      const thread = decodeStoredThreadSnapshotJson(encodedThread);      const [threadShell] = shell.snapshot.threads;       expect(threadShell?.latestVisibleMessage?.text).toBe("Done");      expect(        threadShell?.latestVisibleMessage === null ||          threadShell?.latestVisibleMessage === undefined          ? null          : DateTime.formatIso(threadShell.latestVisibleMessage.updatedAt),      ).toBe("2026-06-20T00:00:00.000Z");      expect(        threadShell?.latestUserMessageAt === null || threadShell?.latestUserMessageAt === undefined          ? null          : DateTime.formatIso(threadShell.latestUserMessageAt),      ).toBe("2026-06-20T00:00:00.000Z");      expect(        threadShell?.settledAt === null || threadShell?.settledAt === undefined          ? null          : DateTime.formatIso(threadShell.settledAt),      ).toBe("2026-06-20T00:00:00.000Z");      expect(        threadShell?.titleRegeneration === null || threadShell?.titleRegeneration === undefined          ? null          : DateTime.formatIso(threadShell.titleRegeneration.startedAt),      ).toBe("2026-06-20T00:00:00.000Z");      expect(DateTime.formatIso(thread.snapshot.projection.thread.updatedAt)).toBe(        "2026-06-20T00:00:00.000Z",      );      expect(thread.snapshot.projection).toEqual(v2Projection);      expect(thread.snapshot.snapshotSequence).toBe(4);    }),  );   it.effect("discards V1-versioned cache envelopes after a decode failure", () =>    Effect.gen(function* () {      let discardCount = 0;      const encodedShell = encodeStoredShellSnapshot({        schemaVersion: ORCHESTRATION_CACHE_SCHEMA_VERSION,        environmentId,        snapshot: v2ShellSnapshot,      });      const decoded = Effect.try({        try: () => decodeStoredShellSnapshotSync({ ...encodedShell, schemaVersion: 1 }),        catch: (cause) => new TestCacheDecodeError({ message: String(cause) }),      }).pipe(Effect.map(Option.some));       const result = yield* decodeOrDiscardOrchestrationCache(        decoded,        Effect.sync(() => {          discardCount += 1;        }),      );       expect(Option.isNone(result)).toBe(true);      expect(discardCount).toBe(1);    }),  );});