453 lines · 16.3 KB · TypeScript
RawDownload
// @effect-diagnostics nodeBuiltinImport:offimport * as NodeFS from "node:fs";import * as NodeOS from "node:os";import * as NodePath from "node:path"; import * as NodeServices from "@effect/platform-node/NodeServices";import {  ProviderDriverKind,  ProviderInstanceId,  ThreadId,  type AgentSessionImportSource,} from "@t3tools/contracts";import { assert, expect, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as SqlClient from "effect/unstable/sql/SqlClient"; import {  makeSqlitePersistenceLive,  SqlitePersistenceMemory,} from "../../persistence/Layers/Sqlite.ts";import * as ProviderSessionRuntime from "../../persistence/ProviderSessionRuntime.ts";import { ProviderSessionDirectory } from "../Services/ProviderSessionDirectory.ts";import { ProviderSessionDirectoryLive } from "./ProviderSessionDirectory.ts"; const importedSource = {  provider: "codex",  providerInstanceId: ProviderInstanceId.make("codex"),  providerSessionId: "provider-session",  filePath: "/tmp/provider-session.jsonl",  size: 100,  mtimeMs: 1_000,  device: 1,  inode: 123,  birthtimeMs: 500,} satisfies AgentSessionImportSource; function makeDirectoryLayer<E, R>(persistenceLayer: Layer.Layer<SqlClient.SqlClient, E, R>) {  const runtimeRepositoryLayer = ProviderSessionRuntime.layer.pipe(Layer.provide(persistenceLayer));  return Layer.mergeAll(    persistenceLayer,    runtimeRepositoryLayer,    ProviderSessionDirectoryLive.pipe(Layer.provide(runtimeRepositoryLayer)),    NodeServices.layer,  );} it.layer(makeDirectoryLayer(SqlitePersistenceMemory))("ProviderSessionDirectoryLive", (it) => {  it.effect("upserts and reads thread bindings", () =>    Effect.gen(function* () {      const directory = yield* ProviderSessionDirectory;      const runtimeRepository = yield* ProviderSessionRuntime.ProviderSessionRuntimeRepository;       const initialThreadId = ThreadId.make("thread-1");       yield* directory.upsert({        provider: ProviderDriverKind.make("codex"),        providerInstanceId: ProviderInstanceId.make("codex"),        threadId: initialThreadId,      });       const provider = yield* directory.getProvider(initialThreadId);      assert.equal(provider, "codex");      const resolvedBinding = yield* directory.getBinding(initialThreadId);      expect(Option.getOrThrow(resolvedBinding)).toMatchObject({        threadId: initialThreadId,        provider: ProviderDriverKind.make("codex"),      });      if (Option.isSome(resolvedBinding)) {        assert.equal(resolvedBinding.value.threadId, initialThreadId);      }       const nextThreadId = ThreadId.make("thread-2");       yield* directory.upsert({        provider: ProviderDriverKind.make("codex"),        providerInstanceId: ProviderInstanceId.make("codex"),        threadId: nextThreadId,      });      const updatedBinding = yield* directory.getBinding(nextThreadId);      assert.equal(Option.isSome(updatedBinding), true);      if (Option.isSome(updatedBinding)) {        assert.equal(updatedBinding.value.threadId, nextThreadId);      }       const runtime = yield* runtimeRepository.getByThreadId({ threadId: nextThreadId });      assert.equal(Option.isSome(runtime), true);      if (Option.isSome(runtime)) {        assert.equal(runtime.value.threadId, nextThreadId);        assert.equal(runtime.value.status, "running");        assert.equal(runtime.value.providerName, "codex");      }       const threadIds = yield* directory.listThreadIds();      expect(threadIds).toEqual(expect.arrayContaining([initialThreadId, nextThreadId]));    }),  );   it.effect("persists runtime fields and merges payload updates", () =>    Effect.gen(function* () {      const directory = yield* ProviderSessionDirectory;      const runtimeRepository = yield* ProviderSessionRuntime.ProviderSessionRuntimeRepository;       const threadId = ThreadId.make("thread-runtime");       yield* directory.upsert({        provider: ProviderDriverKind.make("codex"),        providerInstanceId: ProviderInstanceId.make("codex"),        threadId,        status: "starting",        resumeCursor: {          threadId: "provider-thread-runtime",        },        runtimePayload: {          cwd: "/tmp/project",          model: "gpt-5-codex",        },      });       yield* directory.upsert({        provider: ProviderDriverKind.make("codex"),        providerInstanceId: ProviderInstanceId.make("codex"),        threadId,        status: "running",        runtimePayload: {          activeTurnId: "turn-1",        },      });       const runtime = yield* runtimeRepository.getByThreadId({ threadId });      assert.equal(Option.isSome(runtime), true);      if (Option.isSome(runtime)) {        assert.equal(runtime.value.threadId, threadId);        assert.equal(runtime.value.status, "running");        assert.deepEqual(runtime.value.resumeCursor, {          threadId: "provider-thread-runtime",        });        assert.deepEqual(runtime.value.runtimePayload, {          cwd: "/tmp/project",          model: "gpt-5-codex",          activeTurnId: "turn-1",        });      }    }),  );   it.effect("keeps the existing binding when an insert conflicts", () =>    Effect.gen(function* () {      const directory = yield* ProviderSessionDirectory;      const threadId = ThreadId.make("thread-insert-conflict");       yield* directory.upsert({        provider: ProviderDriverKind.make("codex"),        providerInstanceId: ProviderInstanceId.make("codex"),        threadId,        status: "running",        resumeCursor: { threadId: "active-provider-thread" },      });       yield* directory.upsert(        {          provider: ProviderDriverKind.make("codex"),          providerInstanceId: ProviderInstanceId.make("codex"),          threadId,          status: "stopped",          resumeCursor: { threadId: "stale-provider-thread" },        },        { onConflict: "ignore" },      );       const binding = yield* directory.getBinding(threadId);      expect(Option.getOrThrow(binding)).toMatchObject({        threadId,        status: "running",        resumeCursor: { threadId: "active-provider-thread" },      });    }),  );   it.effect("records source files without replacing the current provider session", () =>    Effect.gen(function* () {      const directory = yield* ProviderSessionDirectory;      const repository = yield* ProviderSessionRuntime.ProviderSessionRuntimeRepository;      const source = { ...importedSource, providerSessionId: "record-source" };      const threadId = ThreadId.make(        `import:${source.providerInstanceId}:${source.providerSessionId}`,      );      const runtimePayload = { cwd: "/tmp/project", activeTurnId: "active-turn" };      yield* directory.upsert({        threadId,        provider: ProviderDriverKind.make("claudeAgent"),        providerInstanceId: ProviderInstanceId.make("claude-current"),        status: "running",        resumeCursor: { resume: "current-native-session" },        runtimePayload,      });      const before = Option.getOrThrow(yield* repository.getByThreadId({ threadId }));       yield* directory.recordImportedTranscript({ threadId, source });      const replacement = { ...source, size: 200, mtimeMs: 2_000 };      yield* directory.recordImportedTranscript({ threadId, source: replacement });      const secondFile = { ...source, filePath: "/tmp/provider-session-copy.jsonl" };      yield* directory.recordImportedTranscript({ threadId, source: secondFile });       expect(Option.getOrThrow(yield* repository.getByThreadId({ threadId }))).toEqual({        ...before,        runtimePayload: { ...runtimePayload, importedTranscripts: [replacement, secondFile] },      });    }),  );   it.effect("does not create a binding when recording an imported transcript", () =>    Effect.gen(function* () {      const directory = yield* ProviderSessionDirectory;      const threadId = ThreadId.make("import:codex:missing-source-binding");       yield* directory.recordImportedTranscript({ threadId, source: importedSource });       expect(Option.isNone(yield* directory.getBinding(threadId))).toBe(true);    }),  );   it.effect("keeps newly recorded sources when a runtime write uses a stale payload", () =>    Effect.gen(function* () {      const directory = yield* ProviderSessionDirectory;      const repository = yield* ProviderSessionRuntime.ProviderSessionRuntimeRepository;      const firstSource = { ...importedSource, providerSessionId: "stale-source" };      const threadId = ThreadId.make(        `import:${firstSource.providerInstanceId}:${firstSource.providerSessionId}`,      );      yield* directory.upsert({        threadId,        provider: ProviderDriverKind.make("codex"),        providerInstanceId: ProviderInstanceId.make("codex"),        status: "stopped",        resumeCursor: { threadId: "original-native-session" },        runtimePayload: { cwd: "/tmp/stale-source-project" },      });      yield* directory.recordImportedTranscript({ threadId, source: firstSource });      const stale = Option.getOrThrow(yield* repository.getByThreadId({ threadId }));      const secondSource = { ...firstSource, filePath: "/tmp/stale-source-copy.jsonl" };      yield* directory.recordImportedTranscript({ threadId, source: secondSource });       yield* repository.upsert({        ...stale,        status: "running",        resumeCursor: { threadId: "new-native-session" },        lastSeenAt: "2026-08-24T10:00:00.000Z",      });       expect(Option.getOrThrow(yield* repository.getByThreadId({ threadId }))).toEqual({        ...stale,        status: "running",        resumeCursor: { threadId: "new-native-session" },        lastSeenAt: "2026-08-24T10:00:00.000Z",        runtimePayload: {          cwd: "/tmp/stale-source-project",          importedTranscripts: [firstSource, secondSource],        },      });    }),  );   it.effect("reserves imported source records for the atomic recording method", () =>    Effect.gen(function* () {      const directory = yield* ProviderSessionDirectory;      for (const onConflict of ["update", "ignore"] as const) {        const source = { ...importedSource, providerSessionId: `reserved-source-${onConflict}` };        const threadId = ThreadId.make(          `import:${source.providerInstanceId}:${source.providerSessionId}`,        );        const binding = {          threadId,          provider: ProviderDriverKind.make("codex"),          providerInstanceId: ProviderInstanceId.make("codex"),        };        yield* directory.upsert(          { ...binding, runtimePayload: { cwd: "/tmp/project", importedTranscripts: [source] } },          { onConflict },        );        expect(Option.getOrThrow(yield* directory.getBinding(threadId)).runtimePayload).toEqual({          cwd: "/tmp/project",        });         yield* directory.recordImportedTranscript({ threadId, source });        yield* directory.upsert({ ...binding, runtimePayload: null });         expect(Option.getOrThrow(yield* directory.getBinding(threadId)).runtimePayload).toEqual({          importedTranscripts: [source],        });      }    }),  );   it.effect("lists persisted bindings with metadata in oldest-first order", () =>    Effect.gen(function* () {      const directory = yield* ProviderSessionDirectory;      const runtimeRepository = yield* ProviderSessionRuntime.ProviderSessionRuntimeRepository;       const olderThreadId = ThreadId.make("thread-runtime-older");      const newerThreadId = ThreadId.make("thread-runtime-newer");       yield* runtimeRepository.upsert({        threadId: newerThreadId,        providerName: "codex",        providerInstanceId: null,        adapterKey: "codex",        runtimeMode: "full-access",        status: "running",        lastSeenAt: "2026-04-14T12:05:00.000Z",        resumeCursor: {          opaque: "resume-newer",        },        runtimePayload: {          cwd: "/tmp/newer",        },      });       yield* runtimeRepository.upsert({        threadId: olderThreadId,        providerName: "claudeAgent",        providerInstanceId: null,        adapterKey: "claudeAgent",        runtimeMode: "approval-required",        status: "starting",        lastSeenAt: "2026-04-14T12:00:00.000Z",        resumeCursor: {          opaque: "resume-older",        },        runtimePayload: {          cwd: "/tmp/older",        },      });       const bindings = (yield* directory.listBindings()).filter(        (binding) => binding.threadId === olderThreadId || binding.threadId === newerThreadId,      );       assert.deepEqual(bindings, [        {          threadId: olderThreadId,          provider: ProviderDriverKind.make("claudeAgent"),          providerInstanceId: ProviderInstanceId.make("claudeAgent"),          adapterKey: "claudeAgent",          runtimeMode: "approval-required",          status: "starting",          lastSeenAt: "2026-04-14T12:00:00.000Z",          resumeCursor: {            opaque: "resume-older",          },          runtimePayload: {            cwd: "/tmp/older",          },        },        {          threadId: newerThreadId,          provider: ProviderDriverKind.make("codex"),          providerInstanceId: ProviderInstanceId.make("codex"),          adapterKey: "codex",          runtimeMode: "full-access",          status: "running",          lastSeenAt: "2026-04-14T12:05:00.000Z",          resumeCursor: {            opaque: "resume-newer",          },          runtimePayload: {            cwd: "/tmp/newer",          },        },      ]);    }),  );   it.effect(    "resets adapterKey to the new provider when provider changes without an explicit adapter key",    () =>      Effect.gen(function* () {        const directory = yield* ProviderSessionDirectory;        const runtimeRepository = yield* ProviderSessionRuntime.ProviderSessionRuntimeRepository;        const threadId = ThreadId.make("thread-provider-change");         yield* runtimeRepository.upsert({          threadId,          providerName: "claudeAgent",          providerInstanceId: null,          adapterKey: "claudeAgent",          runtimeMode: "full-access",          status: "running",          lastSeenAt: "2026-01-01T00:00:00.000Z",          resumeCursor: null,          runtimePayload: null,        });         yield* directory.upsert({          provider: ProviderDriverKind.make("codex"),          providerInstanceId: ProviderInstanceId.make("codex"),          threadId,        });         const runtime = yield* runtimeRepository.getByThreadId({ threadId });        assert.equal(Option.isSome(runtime), true);        if (Option.isSome(runtime)) {          assert.equal(runtime.value.providerName, "codex");          assert.equal(runtime.value.adapterKey, "codex");        }      }),  );   it.effect("rehydrates persisted mappings across layer restart", () =>    Effect.gen(function* () {      const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-directory-"));      const dbPath = NodePath.join(tempDir, "orchestration.sqlite");      const directoryLayer = makeDirectoryLayer(makeSqlitePersistenceLive(dbPath));       const threadId = ThreadId.make("thread-restart");       yield* Effect.gen(function* () {        const directory = yield* ProviderSessionDirectory;        yield* directory.upsert({          provider: ProviderDriverKind.make("codex"),          providerInstanceId: ProviderInstanceId.make("codex"),          threadId,        });      }).pipe(Effect.provide(directoryLayer));       yield* Effect.gen(function* () {        const directory = yield* ProviderSessionDirectory;        const sql = yield* SqlClient.SqlClient;        const provider = yield* directory.getProvider(threadId);        assert.equal(provider, "codex");         const resolvedBinding = yield* directory.getBinding(threadId);        expect(Option.getOrThrow(resolvedBinding)).toMatchObject({          threadId,          provider: ProviderDriverKind.make("codex"),        });        if (Option.isSome(resolvedBinding)) {          assert.equal(resolvedBinding.value.threadId, threadId);        }         const legacyTableRows = yield* sql<{ readonly name: string }>`          SELECT name          FROM sqlite_master          WHERE type = 'table' AND name = 'provider_sessions'        `;        assert.equal(legacyTableRows.length, 0);      }).pipe(Effect.provide(directoryLayer));       NodeFS.rmSync(tempDir, { recursive: true, force: true });    }),  );});