{"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../../../src/harness/runtime/restore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,KAAK,EACX,SAAS,IAAI,gBAAgB,EAC7B,iBAAiB,EAGjB,OAAO,EACP,aAAa,EACb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAON,KAAK,WAAW,EAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAiC5C,MAAM,MAAM,qBAAqB,GAC9B;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,WAAW,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;CAAE,GACnD;IACA,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,WAAW,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAChC,aAAa,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAC;IAC9C,SAAS,EAAE,WAAW,CAAC,gBAAgB,CAAC,CAAC;CACxC,CAAC;AAiBL,wBAAsB,eAAe,CACpC,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,qBAAqB,CAAC,CAOhC;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAuBlG;AAED,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAWhG;AAED,wBAAsB,gBAAgB,CACrC,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,CAAC,qBAAqB,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EACxD,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,SAAS,CAAC,CAmCpB","sourcesContent":["import type { Context } from \"../context.ts\";\nimport { SessionInvariantError } from \"../session/session.ts\";\nimport type {\n\tLaneState as DurableLaneState,\n\tLaneConfiguration,\n\tOperationMeta,\n\tOperationState,\n\tSession,\n\tSessionReader,\n} from \"../session/types.ts\";\nimport {\n\tbranchTip,\n\tbranchTipInventoryPrefix,\n\tlaneConfig,\n\tlaneState as laneStateValue,\n\toperationMeta,\n\toperationState,\n\ttype StoredValue,\n} from \"../session/values.ts\";\nimport type { LaneState } from \"./types.ts\";\n\nfunction isSummaryState(state: OperationState): state is Extract<OperationState, { at: `summary.${string}` }> {\n\treturn state.at.startsWith(\"summary.\");\n}\n\nfunction stateMatchesIntent(intent: OperationMeta[\"intent\"], state: OperationState): boolean {\n\tif (intent.kind === \"compaction\") return isSummaryState(state) && state.task.boundary.kind === \"finish\";\n\tif (intent.kind === \"navigation\") {\n\t\tif (state.at === \"navigation.ready_to_commit\") {\n\t\t\treturn !intent.summarize && state.targetId === intent.targetId && state.label === intent.label;\n\t\t}\n\t\treturn (\n\t\t\tintent.summarize &&\n\t\t\tisSummaryState(state) &&\n\t\t\tstate.task.boundary.kind === \"commit_navigation\" &&\n\t\t\tstate.task.boundary.targetId === intent.targetId &&\n\t\t\tstate.task.boundary.label === intent.label &&\n\t\t\tstate.task.customInstructions === intent.customInstructions\n\t\t);\n\t}\n\treturn (\n\t\tstate.at !== \"navigation.ready_to_commit\" &&\n\t\t(!isSummaryState(state) || state.task.boundary.kind === \"resume_checkpoint\")\n\t);\n}\n\ntype LaneValues = {\n\ttip: StoredValue<string | null> | undefined;\n\tconfiguration: StoredValue<LaneConfiguration> | undefined;\n\tlaneState: StoredValue<DurableLaneState> | undefined;\n};\n\nexport type ClassifiedLaneStorage =\n\t| { kind: \"absent\" }\n\t| { kind: \"branch\"; tip: StoredValue<string | null> }\n\t| {\n\t\t\tkind: \"lane\";\n\t\t\ttip: StoredValue<string | null>;\n\t\t\tconfiguration: StoredValue<LaneConfiguration>;\n\t\t\tlaneState: StoredValue<DurableLaneState>;\n\t  };\n\nfunction classifyLaneStorage(lane: string, values: LaneValues): ClassifiedLaneStorage {\n\tconst { tip, configuration, laneState } = values;\n\tif (tip === undefined && configuration === undefined && laneState === undefined) {\n\t\treturn { kind: \"absent\" };\n\t}\n\tif (tip !== undefined && configuration === undefined && laneState === undefined) {\n\t\treturn { kind: \"branch\", tip };\n\t}\n\tif (tip === undefined) throw new SessionInvariantError(`Lane ${JSON.stringify(lane)} is missing branch.tip`);\n\tif (configuration === undefined)\n\t\tthrow new SessionInvariantError(`Lane ${JSON.stringify(lane)} is missing lane.config`);\n\tif (laneState === undefined) throw new SessionInvariantError(`Lane ${JSON.stringify(lane)} is missing lane.state`);\n\treturn { kind: \"lane\", tip, configuration, laneState };\n}\n\nexport async function readLaneStorage(\n\treader: SessionReader,\n\tlane: string,\n\tcontext: Context,\n): Promise<ClassifiedLaneStorage> {\n\tconst [tip, configuration, laneState] = await Promise.all([\n\t\treader.getValue(branchTip(lane), context),\n\t\treader.getValue(laneConfig(lane), context),\n\t\treader.getValue(laneStateValue(lane), context),\n\t]);\n\treturn classifyLaneStorage(lane, { tip, configuration, laneState });\n}\n\n/** Restore every complete configured AgentLane in one coherent Session read. */\nexport function restoreSession(session: Session, context: Context): Promise<Map<string, LaneState>> {\n\treturn session.mutate(async (reader) => {\n\t\tconst [tips, configurations, states] = await Promise.all([\n\t\t\treader.scanValues(branchTipInventoryPrefix(), context),\n\t\t\treader.scanValues(laneConfig(\"\"), context),\n\t\t\treader.scanValues(laneStateValue(\"\"), context),\n\t\t]);\n\t\tconst tipByLane = new Map(tips.map((value) => [value.address.key, value]));\n\t\tconst configurationByLane = new Map(configurations.map((value) => [value.address.key, value]));\n\t\tconst stateByLane = new Map(states.map((value) => [value.address.key, value]));\n\t\tconst names = new Set([...tipByLane.keys(), ...configurationByLane.keys(), ...stateByLane.keys()]);\n\t\tconst restored = new Map<string, LaneState>();\n\t\tfor (const lane of names) {\n\t\t\tconst stored = classifyLaneStorage(lane, {\n\t\t\t\ttip: tipByLane.get(lane),\n\t\t\t\tconfiguration: configurationByLane.get(lane),\n\t\t\t\tlaneState: stateByLane.get(lane),\n\t\t\t});\n\t\t\tif (stored.kind !== \"lane\") continue;\n\t\t\trestored.set(lane, await restoreLaneState(reader, lane, stored, context));\n\t\t}\n\t\treturn restored;\n\t}, context);\n}\n\n/** Restore one configured lane without starting work or interpreting its state. */\nexport function restoreLane(session: Session, lane: string, context: Context): Promise<LaneState> {\n\treturn session.mutate(async (reader) => {\n\t\tconst stored = await readLaneStorage(reader, lane, context);\n\t\tif (stored.kind === \"absent\") {\n\t\t\tthrow new SessionInvariantError(`Lane ${JSON.stringify(lane)} is missing branch.tip`);\n\t\t}\n\t\tif (stored.kind === \"branch\") {\n\t\t\tthrow new SessionInvariantError(`Lane ${JSON.stringify(lane)} is missing lane.config`);\n\t\t}\n\t\treturn restoreLaneState(reader, lane, stored, context);\n\t}, context);\n}\n\nexport async function restoreLaneState(\n\treader: SessionReader,\n\tlane: string,\n\tstored: Extract<ClassifiedLaneStorage, { kind: \"lane\" }>,\n\tcontext: Context,\n): Promise<LaneState> {\n\tconst operationId = stored.laneState.value.currentOperationId;\n\tlet operation: LaneState[\"operation\"] = null;\n\tif (operationId !== null) {\n\t\tconst [meta, state] = await Promise.all([\n\t\t\treader.getValue(operationMeta(operationId), context),\n\t\t\treader.getValue(operationState(operationId), context),\n\t\t]);\n\t\tif (meta === undefined) throw new SessionInvariantError(`Operation ${operationId} is missing op.meta`);\n\t\tif (state === undefined) throw new SessionInvariantError(`Operation ${operationId} is missing op.state`);\n\t\tif (meta.value.operationId !== operationId) {\n\t\t\tthrow new SessionInvariantError(\n\t\t\t\t`Operation ${operationId} metadata names operation ${JSON.stringify(meta.value.operationId)}`,\n\t\t\t);\n\t\t}\n\t\tif (meta.value.lane !== lane) {\n\t\t\tthrow new SessionInvariantError(\n\t\t\t\t`Operation ${operationId} belongs to lane ${JSON.stringify(meta.value.lane)}, not ${JSON.stringify(lane)}`,\n\t\t\t);\n\t\t}\n\t\tif (!stateMatchesIntent(meta.value.intent, state.value)) {\n\t\t\tthrow new SessionInvariantError(\n\t\t\t\t`Operation ${operationId} intent ${meta.value.intent.kind} does not match state ${state.value.at}`,\n\t\t\t);\n\t\t}\n\t\toperation = { meta: meta.value, state: state.value };\n\t}\n\n\treturn {\n\t\ttipId: stored.tip.value,\n\t\tconfiguration: stored.configuration.value,\n\t\tinbox: stored.laneState.value.inbox,\n\t\tlastOperationId: stored.laneState.value.lastOperationId,\n\t\toperation,\n\t};\n}\n"]}