1import {
2 EnvironmentId,
3 OrchestrationV2ShellSnapshotJson,
4 OrchestrationV2ThreadDetailSnapshot,
5 OrchestrationV2ThreadProjectionJson,
6 ThreadId,
7} from "@t3tools/contracts";
8import * as Effect from "effect/Effect";
9import * as Option from "effect/Option";
10import * as Schema from "effect/Schema";
11
12export const ORCHESTRATION_CACHE_SCHEMA_VERSION = 3 as const;
13/**
14 * Thread snapshots version on their own so a thread-only invalidation keeps
15 * shell caches warm. 4 drops bounded windows that lost queued runs' items (#16987).
16 */
17export const ORCHESTRATION_THREAD_CACHE_SCHEMA_VERSION = 4 as const;
18
19export const StoredOrchestrationShellSnapshot = Schema.Struct({
20 schemaVersion: Schema.Literal(ORCHESTRATION_CACHE_SCHEMA_VERSION),
21 environmentId: EnvironmentId,
22 snapshot: OrchestrationV2ShellSnapshotJson,
23});
24
25export const StoredOrchestrationThreadSnapshot = Schema.Struct({
26 schemaVersion: Schema.Literal(ORCHESTRATION_THREAD_CACHE_SCHEMA_VERSION),
27 environmentId: EnvironmentId,
28 threadId: ThreadId,
29 snapshot: OrchestrationV2ThreadDetailSnapshot.mapFields((fields) => ({
30 ...fields,
31 projection: OrchestrationV2ThreadProjectionJson,
32 })),
33});
34
35/** Invalid orchestration caches are disposable and must never block live synchronization. */
36export function decodeOrDiscardOrchestrationCache<A, E, R, E2, R2>(
37 decode: Effect.Effect<Option.Option<A>, E, R>,
38 discard: Effect.Effect<void, E2, R2>,
39) {
40 return decode.pipe(Effect.catch(() => discard.pipe(Effect.as(Option.none<A>()))));
41}