291 lines · 9.2 KB · TypeScript
RawDownload
/** * CheckpointDiffQuery - Query interface for computed checkpoint diffs. * * Provides read-only diff operations across checkpoint snapshots used by * orchestration APIs. * * @module CheckpointDiffQuery */import {  type CheckpointRef,  OrchestrationGetTurnDiffResult,  type OrchestrationGetFullThreadDiffInput,  type OrchestrationGetFullThreadDiffResult,  type OrchestrationGetTurnDiffInput,  type OrchestrationGetTurnDiffResult as OrchestrationGetTurnDiffResultType,  type ThreadId,} from "@t3tools/contracts";import * as Context from "effect/Context";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Schema from "effect/Schema"; import * as ProjectionSnapshotQuery from "../orchestration/Services/ProjectionSnapshotQuery.ts";import {  CheckpointDiffResultInvalidError,  CheckpointRefUnavailableError,  CheckpointThreadNotFoundError,  CheckpointTurnRangeUnavailableError,  CheckpointWorkspacePathMissingError,} from "./Errors.ts";import type { CheckpointServiceError } from "./Errors.ts";import { checkpointRefForThreadTurn } from "./Utils.ts";import * as CheckpointStore from "./CheckpointStore.ts"; /** Service tag for checkpoint diff queries. */export class CheckpointDiffQuery extends Context.Service<  CheckpointDiffQuery,  {    /**     * Read the patch diff for a single turn checkpoint transition.     *     * Verifies checkpoint availability in both projection state and filesystem.     */    readonly getTurnDiff: (      input: OrchestrationGetTurnDiffInput,    ) => Effect.Effect<OrchestrationGetTurnDiffResultType, CheckpointServiceError>;     /**     * Read the full patch diff across a thread range of checkpoints.     *     * Uses turn-diff semantics with `fromTurnCount = 0`.     */    readonly getFullThreadDiff: (      input: OrchestrationGetFullThreadDiffInput,    ) => Effect.Effect<OrchestrationGetFullThreadDiffResult, CheckpointServiceError>;  }>()("t3/checkpointing/CheckpointDiffQuery") {} const isTurnDiffResult = Schema.is(OrchestrationGetTurnDiffResult); function buildTurnDiffResult(  input: {    readonly threadId: ThreadId;    readonly fromTurnCount: number;    readonly toTurnCount: number;  },  diff: string,): OrchestrationGetTurnDiffResultType {  return {    threadId: input.threadId,    fromTurnCount: input.fromTurnCount,    toTurnCount: input.toTurnCount,    diff,  };} export const make = Effect.gen(function* () {  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery.ProjectionSnapshotQuery;  const checkpointStore = yield* CheckpointStore.CheckpointStore;   const getTurnDiff: CheckpointDiffQuery["Service"]["getTurnDiff"] = Effect.fn("getTurnDiff")(    function* (input) {      const operation = "CheckpointDiffQuery.getTurnDiff";      const ignoreWhitespace = input.ignoreWhitespace ?? true;      yield* Effect.annotateCurrentSpan({        "checkpoint.thread_id": input.threadId,        "checkpoint.from_turn_count": input.fromTurnCount,        "checkpoint.to_turn_count": input.toTurnCount,        "checkpoint.ignore_whitespace": ignoreWhitespace,      });       if (input.fromTurnCount === input.toTurnCount) {        const emptyDiff: OrchestrationGetTurnDiffResultType = {          threadId: input.threadId,          fromTurnCount: input.fromTurnCount,          toTurnCount: input.toTurnCount,          diff: "",        };        if (!isTurnDiffResult(emptyDiff)) {          return yield* new CheckpointDiffResultInvalidError({            operation,            threadId: input.threadId,          });        }        return emptyDiff;      }       const threadContext = yield* projectionSnapshotQuery        .getThreadCheckpointContext(input.threadId)        .pipe(Effect.withSpan("checkpoint.turnDiff.lookupContext"));      if (Option.isNone(threadContext)) {        return yield* new CheckpointThreadNotFoundError({          operation,          threadId: input.threadId,        });      }       const maxTurnCount = threadContext.value.checkpoints.reduce(        (max, checkpoint) => Math.max(max, checkpoint.checkpointTurnCount),        0,      );      if (input.toTurnCount > maxTurnCount) {        return yield* new CheckpointTurnRangeUnavailableError({          operation,          threadId: input.threadId,          requestedTurnCount: input.toTurnCount,          availableTurnCount: maxTurnCount,        });      }       const workspaceCwd = threadContext.value.worktreePath ?? threadContext.value.workspaceRoot;      if (!workspaceCwd) {        return yield* new CheckpointWorkspacePathMissingError({          operation,          threadId: input.threadId,        });      }       const fromCheckpointRef =        input.fromTurnCount === 0          ? checkpointRefForThreadTurn(input.threadId, 0)          : threadContext.value.checkpoints.find(              (checkpoint) => checkpoint.checkpointTurnCount === input.fromTurnCount,            )?.checkpointRef;      if (!fromCheckpointRef) {        return yield* new CheckpointRefUnavailableError({          operation,          threadId: input.threadId,          turnCount: input.fromTurnCount,          checkpoint: "from",        });      }       const toCheckpointRef = threadContext.value.checkpoints.find(        (checkpoint) => checkpoint.checkpointTurnCount === input.toTurnCount,      )?.checkpointRef;      if (!toCheckpointRef) {        return yield* new CheckpointRefUnavailableError({          operation,          threadId: input.threadId,          turnCount: input.toTurnCount,          checkpoint: "to",        });      }       const diff = yield* checkpointStore        .diffCheckpoints({          cwd: workspaceCwd,          fromCheckpointRef,          toCheckpointRef,          fallbackFromToHead: false,          ignoreWhitespace,        })        .pipe(Effect.withSpan("checkpoint.turnDiff.diffCheckpoints"));       const turnDiff = buildTurnDiffResult(input, diff);      if (!isTurnDiffResult(turnDiff)) {        return yield* new CheckpointDiffResultInvalidError({          operation,          threadId: input.threadId,        });      }       return turnDiff;    },  );   const getFullThreadDiff: CheckpointDiffQuery["Service"]["getFullThreadDiff"] = Effect.fn(    "CheckpointDiffQuery.getFullThreadDiff",  )(function* (input) {    const operation = "CheckpointDiffQuery.getFullThreadDiff";    const ignoreWhitespace = input.ignoreWhitespace ?? true;    yield* Effect.annotateCurrentSpan({      "checkpoint.thread_id": input.threadId,      "checkpoint.from_turn_count": 0,      "checkpoint.to_turn_count": input.toTurnCount,      "checkpoint.ignore_whitespace": ignoreWhitespace,      "checkpoint.diff_kind": "full-thread",    });     if (input.toTurnCount === 0) {      const emptyDiff = buildTurnDiffResult(        {          threadId: input.threadId,          fromTurnCount: 0,          toTurnCount: 0,        },        "",      );      if (!isTurnDiffResult(emptyDiff)) {        return yield* new CheckpointDiffResultInvalidError({          operation,          threadId: input.threadId,        });      }      return emptyDiff satisfies OrchestrationGetFullThreadDiffResult;    }     const threadContext = yield* projectionSnapshotQuery      .getFullThreadDiffContext(input.threadId, input.toTurnCount)      .pipe(Effect.withSpan("checkpoint.fullThread.lookupContext"));     if (Option.isNone(threadContext)) {      return yield* new CheckpointThreadNotFoundError({        operation,        threadId: input.threadId,      });    }     if (input.toTurnCount > threadContext.value.latestCheckpointTurnCount) {      return yield* new CheckpointTurnRangeUnavailableError({        operation,        threadId: input.threadId,        requestedTurnCount: input.toTurnCount,        availableTurnCount: threadContext.value.latestCheckpointTurnCount,      });    }     const workspaceCwd = threadContext.value.worktreePath ?? threadContext.value.workspaceRoot;    if (!workspaceCwd) {      return yield* new CheckpointWorkspacePathMissingError({        operation,        threadId: input.threadId,      });    }     if (!threadContext.value.toCheckpointRef) {      return yield* new CheckpointRefUnavailableError({        operation,        threadId: input.threadId,        turnCount: input.toTurnCount,        checkpoint: "to",      });    }     const diff = yield* checkpointStore      .diffCheckpoints({        cwd: workspaceCwd,        fromCheckpointRef: checkpointRefForThreadTurn(input.threadId, 0),        toCheckpointRef: threadContext.value.toCheckpointRef as CheckpointRef,        fallbackFromToHead: false,        ignoreWhitespace,      })      .pipe(Effect.withSpan("checkpoint.fullThread.diffCheckpoints"));     const turnDiff = buildTurnDiffResult(      {        threadId: input.threadId,        fromTurnCount: 0,        toTurnCount: input.toTurnCount,      },      diff,    );    if (!isTurnDiffResult(turnDiff)) {      return yield* new CheckpointDiffResultInvalidError({        operation,        threadId: input.threadId,      });    }     return turnDiff satisfies OrchestrationGetFullThreadDiffResult;  });   return CheckpointDiffQuery.of({    getTurnDiff,    getFullThreadDiff,  });}); export const layer = Layer.effect(CheckpointDiffQuery, make);