import { CheckpointScopeId, RunId, ThreadId } from "@t3tools/contracts";import * as Context from "effect/Context";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Schema from "effect/Schema"; import * as VcsStatusBroadcaster from "../vcs/VcsStatusBroadcaster.ts";import * as WorkspaceEntries from "../workspace/WorkspaceEntries.ts";import * as CheckpointCapture from "./CheckpointCaptureService.ts";import * as ProjectionStore from "./ProjectionStore.ts"; export class RunFinalizationError extends Schema.TaggedErrorClass<RunFinalizationError>()( "RunFinalizationError", { threadId: ThreadId, runId: RunId, scopeId: CheckpointScopeId, operation: Schema.Literals(["capture-checkpoint", "refresh-workspace"]), cause: Schema.Defect(), },) {} export class RunFinalizationRefreshError extends Schema.TaggedErrorClass<RunFinalizationRefreshError>()( "RunFinalizationRefreshError", { cwd: Schema.String, cause: Schema.Defect() },) {} export class RunFinalizationObserver extends Context.Reference<{ readonly refresh: (cwd: string) => Effect.Effect<void, RunFinalizationRefreshError>;}>("t3/orchestration-v2/RunFinalizationObserver", { defaultValue: () => ({ refresh: () => Effect.void }),}) {} export class RunFinalizationService extends Context.Service< RunFinalizationService, { readonly finalize: (input: { readonly threadId: ThreadId; readonly runId: RunId; readonly scopeId: CheckpointScopeId; }) => Effect.Effect<void, RunFinalizationError>; }>()("t3/orchestration-v2/RunFinalizationService") {} export const make = Effect.gen(function* () { const checkpointCapture = yield* CheckpointCapture.CheckpointCaptureServiceV2; const projections = yield* ProjectionStore.ProjectionStoreV2; const observer = yield* RunFinalizationObserver; const finalize: RunFinalizationService["Service"]["finalize"] = Effect.fn( "RunFinalizationService.finalize", )(function* (input) { yield* checkpointCapture .execute(input) .pipe( Effect.mapError( (cause) => new RunFinalizationError({ ...input, operation: "capture-checkpoint", cause }), ), ); const projection = yield* projections .getThreadProjection(input.threadId) .pipe( Effect.mapError( (cause) => new RunFinalizationError({ ...input, operation: "refresh-workspace", cause }), ), ); const cwd = projection.checkpointScopes.find((scope) => scope.id === input.scopeId)?.cwd; if (cwd !== undefined) { yield* observer .refresh(cwd) .pipe( Effect.mapError( (cause) => new RunFinalizationError({ ...input, operation: "refresh-workspace", cause }), ), ); } }); return RunFinalizationService.of({ finalize });}); export const layer = Layer.effect(RunFinalizationService, make); export const observerLive = Layer.effect( RunFinalizationObserver, Effect.gen(function* () { const workspaceEntries = yield* WorkspaceEntries.WorkspaceEntries; const vcsStatus = yield* VcsStatusBroadcaster.VcsStatusBroadcaster; return { refresh: (cwd: string) => Effect.all([workspaceEntries.refresh(cwd), vcsStatus.refreshStatus(cwd)], { discard: true, concurrency: "unbounded", }).pipe(Effect.mapError((cause) => new RunFinalizationRefreshError({ cwd, cause }))), }; }),);
import { CheckpointScopeId, RunId, ThreadId } from "@t3tools/contracts";
import * as Context from "effect/Context";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Schema from "effect/Schema";
import * as VcsStatusBroadcaster from "../vcs/VcsStatusBroadcaster.ts";
import * as WorkspaceEntries from "../workspace/WorkspaceEntries.ts";
import * as CheckpointCapture from "./CheckpointCaptureService.ts";
import * as ProjectionStore from "./ProjectionStore.ts";
export class RunFinalizationError extends Schema.TaggedErrorClass<RunFinalizationError>()(
"RunFinalizationError",
{
threadId: ThreadId,
runId: RunId,
scopeId: CheckpointScopeId,
operation: Schema.Literals(["capture-checkpoint", "refresh-workspace"]),
cause: Schema.Defect(),
},
) {}
export class RunFinalizationRefreshError extends Schema.TaggedErrorClass<RunFinalizationRefreshError>()(
"RunFinalizationRefreshError",
{ cwd: Schema.String, cause: Schema.Defect() },
) {}
export class RunFinalizationObserver extends Context.Reference<{
readonly refresh: (cwd: string) => Effect.Effect<void, RunFinalizationRefreshError>;
}>("t3/orchestration-v2/RunFinalizationObserver", {
defaultValue: () => ({ refresh: () => Effect.void }),
}) {}
export class RunFinalizationService extends Context.Service<
RunFinalizationService,
{
readonly finalize: (input: {
readonly threadId: ThreadId;
readonly runId: RunId;
readonly scopeId: CheckpointScopeId;
}) => Effect.Effect<void, RunFinalizationError>;
}
>()("t3/orchestration-v2/RunFinalizationService") {}
export const make = Effect.gen(function* () {
const checkpointCapture = yield* CheckpointCapture.CheckpointCaptureServiceV2;
const projections = yield* ProjectionStore.ProjectionStoreV2;
const observer = yield* RunFinalizationObserver;
const finalize: RunFinalizationService["Service"]["finalize"] = Effect.fn(
"RunFinalizationService.finalize",
)(function* (input) {
yield* checkpointCapture
.execute(input)
.pipe(
Effect.mapError(
(cause) => new RunFinalizationError({ ...input, operation: "capture-checkpoint", cause }),
),
);
const projection = yield* projections
.getThreadProjection(input.threadId)
.pipe(
Effect.mapError(
(cause) => new RunFinalizationError({ ...input, operation: "refresh-workspace", cause }),
),
);
const cwd = projection.checkpointScopes.find((scope) => scope.id === input.scopeId)?.cwd;
if (cwd !== undefined) {
yield* observer
.refresh(cwd)
.pipe(
Effect.mapError(
(cause) =>
new RunFinalizationError({ ...input, operation: "refresh-workspace", cause }),
),
);
}
});
return RunFinalizationService.of({ finalize });
});
export const layer = Layer.effect(RunFinalizationService, make);
export const observerLive = Layer.effect(
RunFinalizationObserver,
Effect.gen(function* () {
const workspaceEntries = yield* WorkspaceEntries.WorkspaceEntries;
const vcsStatus = yield* VcsStatusBroadcaster.VcsStatusBroadcaster;
return {
refresh: (cwd: string) =>
Effect.all([workspaceEntries.refresh(cwd), vcsStatus.refreshStatus(cwd)], {
discard: true,
concurrency: "unbounded",
}).pipe(Effect.mapError((cause) => new RunFinalizationRefreshError({ cwd, cause }))),
};
}),
);