Skip to content
Source
Sign inSign up
Open History to see the last commit

orchestrationCache.ts

52 lines1.9 KB

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// Older caches may contain human turns that bypassed server history budgets.
14export const ORCHESTRATION_THREAD_CACHE_SCHEMA_VERSION = 5 as const;
15​
16/**
17 * The server snapshot the cached rows descend from. Written only for rows that have one, so a
18 * cache from before it existed decodes and never resumes.
19 */
20export const ShellCacheOrigin = Schema.Struct({
21 serverVersion: Schema.String,
22 databaseMigration: Schema.NullOr(Schema.Int),
23 omitIdleSubagents: Schema.Boolean,
24 omitOlderSettled: Schema.Boolean,
25 /** Epoch milliseconds of that snapshot; replay does not renew it. */
26 snapshotAt: Schema.Number,
27});
28export type ShellCacheOrigin = typeof ShellCacheOrigin.Type;
29​
30export const StoredOrchestrationShellSnapshot = Schema.Struct({
31 schemaVersion: Schema.Literal(ORCHESTRATION_CACHE_SCHEMA_VERSION),
32 environmentId: EnvironmentId,
33 snapshot: OrchestrationV2ShellSnapshotJson,
34 origin: Schema.optionalKey(ShellCacheOrigin),
35});
36​
37export const StoredOrchestrationThreadSnapshot = Schema.Struct({
38 schemaVersion: Schema.Literal(ORCHESTRATION_THREAD_CACHE_SCHEMA_VERSION),
39 environmentId: EnvironmentId,
40 threadId: ThreadId,
41 snapshot: OrchestrationV2ThreadDetailSnapshot.mapFields((fields) => ({
42 ...fields,
43 projection: OrchestrationV2ThreadProjectionJson,
44 })),
45});
46​
47export function decodeOrDiscardOrchestrationCache<A, E, R, E2, R2>(
48 decode: Effect.Effect<Option.Option<A>, E, R>,
49 discard: Effect.Effect<void, E2, R2>,
50) {
51 return decode.pipe(Effect.catch(() => discard.pipe(Effect.as(Option.none<A>()))));