@tanstack/ai-persistence 0.6.4 → 0.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-runs.js","names":[],"sources":["../../src/subagent-runs.ts"],"sourcesContent":["import {\n StreamProcessor,\n convertMessagesToModelMessages,\n modelMessagesToUIMessages,\n subagentHostMessageId,\n wireSubagentInfo,\n} from '@tanstack/ai'\nimport type {\n Interrupt,\n ModelMessage,\n RunAgentResumeItem,\n RunStore,\n StreamChunk,\n SubagentStatus,\n SubagentWireInfo,\n UIMessage,\n} from '@tanstack/ai'\nimport { mergeStoredMessages } from './merge-stored'\nimport type { InterruptStore, MessageStore } from './types'\n\nfunction withSubagentInfo(\n metadata: ModelMessage['metadata'],\n info: SubagentWireInfo,\n): Record<string, unknown> {\n const source =\n metadata != null && typeof metadata === 'object'\n ? (metadata as Record<string, unknown>)\n : {}\n const tanstack =\n source.tanstack != null && typeof source.tanstack === 'object'\n ? (source.tanstack as Record<string, unknown>)\n : {}\n return { ...source, tanstack: { ...tanstack, subagent: info } }\n}\n\nfunction childStoreId(subagentRunId: string) {\n return `subagent:${subagentRunId}`\n}\n\nfunction readSubagentRunId(chunk: StreamChunk) {\n if (!('subagentRunId' in chunk)) return\n const id = chunk.subagentRunId\n return typeof id === 'string' && id !== '' ? id : undefined\n}\n\nfunction assistantId(runId: string) {\n return subagentHostMessageId(runId)\n}\n\nfunction messageText(message: ModelMessage) {\n return typeof message.content === 'string' ? message.content : ''\n}\n\nfunction readModelRunId(message: ModelMessage) {\n const metadata = message.metadata\n if (metadata == null || typeof metadata !== 'object') return\n if (!('tanstack' in metadata)) return\n const tanstack = metadata.tanstack\n if (tanstack == null || typeof tanstack !== 'object') return\n if (!('runId' in tanstack)) return\n const runId = tanstack.runId\n return typeof runId === 'string' && runId !== '' ? runId : undefined\n}\n\nfunction withRunId(message: ModelMessage, runId: string): ModelMessage {\n const metadata = message.metadata\n const tanstack =\n metadata != null &&\n typeof metadata === 'object' &&\n 'tanstack' in metadata &&\n metadata.tanstack != null &&\n typeof metadata.tanstack === 'object'\n ? metadata.tanstack\n : {}\n return {\n ...message,\n metadata: {\n ...metadata,\n tanstack: { ...tanstack, runId },\n },\n }\n}\n\nfunction sameChildText(stored: string, incoming: string) {\n if (stored === '' || incoming === '') return false\n if (stored === incoming) return true\n const blocks = stored.split('\\n\\n').filter((block) => block.includes(':\\n'))\n if (blocks.length === 0) return false\n return blocks.every((block) => incoming.includes(block))\n}\n\n// A live client mints its own assistant id. The stored row uses\n// `assistant:${runId}`. When the client sends the turn again, copy `runId`\n// onto the new row so the cards still rebuild.\nfunction keepSubagentRunIds(\n stored: ReadonlyArray<ModelMessage>,\n merged: Array<ModelMessage>,\n) {\n const dropped = stored.filter((message) => {\n const runId = readModelRunId(message)\n if (runId === undefined) return false\n return !merged.some(\n (item) => item.id !== undefined && item.id === message.id,\n )\n })\n // The user message before a message, as the turn it answers.\n const turnOf = (list: ReadonlyArray<ModelMessage>, index: number) =>\n list.slice(0, index).findLast((message) => message.role === 'user')?.id\n const used = new Set<number>()\n for (const previous of dropped) {\n const runId = readModelRunId(previous)\n if (runId === undefined) continue\n if (merged.some((message) => readModelRunId(message) === runId)) continue\n const previousText = messageText(previous)\n const previousTurn = turnOf(stored, stored.indexOf(previous))\n const index = merged.findIndex((message, messageIndex) => {\n if (used.has(messageIndex)) return false\n if (message.role !== 'assistant') return false\n if (readModelRunId(message) !== undefined) return false\n // No child text yet (a child waits for approval): match the first\n // assistant reply to the same user message.\n if (previousText === '') {\n return (\n previousTurn !== undefined &&\n turnOf(merged, messageIndex) === previousTurn &&\n merged[messageIndex - 1]?.role === 'user'\n )\n }\n return sameChildText(previousText, messageText(message))\n })\n if (index === -1) continue\n const host = merged[index]\n if (!host) continue\n used.add(index)\n merged[index] = withRunId(host, runId)\n }\n return merged\n}\nfunction childText(messages: ReadonlyArray<UIMessage>) {\n return messages\n .flatMap((message) =>\n message.role === 'assistant'\n ? message.parts.flatMap((part) =>\n part.type === 'text' && part.content.trim() !== ''\n ? [part.content.trim()]\n : [],\n )\n : [],\n )\n .join('\\n\\n')\n}\n\n// Nested children have their own run and transcript. Leave their cards out\n// of this child's transcript, so a rebuilt card does not show them twice.\nfunction withoutCards(messages: ReadonlyArray<UIMessage>): Array<UIMessage> {\n return messages.map((message) => ({\n ...message,\n parts: message.parts.filter((part) => part.type !== 'subagent'),\n }))\n}\n\n/**\n * Card data for a stored child transcript. It rides on the first message, in\n * `metadata.tanstack.subagent`, the same shape the wire uses.\n */\nexport function storedSubagentInfo(\n messages: ReadonlyArray<ModelMessage>,\n): SubagentWireInfo | undefined {\n // Same validation as the wire path. Malformed stored data reads as absent.\n return wireSubagentInfo(messages[0])\n}\n\ntype ChildNote = {\n name: string\n /** The thread and run that feed this note now. */\n threadId: string\n runId: string\n /** Last transcript write, for the write interval. */\n savedAt: number\n /** The run that started this child. A resume keeps it. */\n parentRunId: string\n parentSubagentRunId?: string\n parentToolCallId?: string\n processor: StreamProcessor\n status: SubagentStatus\n interruptIds?: Array<string>\n error?: { message: string; code?: string }\n metadata?: Record<string, unknown>\n}\n\nexport function createSubagentRunRecorder(stores: {\n messages: MessageStore\n runs?: RunStore\n interrupts?: InterruptStore\n /** Minimum milliseconds between writes while a child streams. */\n intervalMs?: number\n}) {\n // One recorder serves every run of a middleware instance. Notes leave the\n // map when their child or their run ends.\n const children = new Map<string, ChildNote>()\n const intervalMs = stores.intervalMs ?? 1000\n // Resume entries each run answers. Committed when the run finishes or\n // suspends. Dropped on abort.\n const answered = new Map<string, Array<RunAgentResumeItem>>()\n const parentSavedAt = new Map<string, number>()\n\n async function loadMessages(threadId: string) {\n return stores.messages.loadThread(threadId)\n }\n\n // The child and its ancestors, nearest first. Each keeps its own stream.\n function lineage(subagentRunId: string) {\n const notes: Array<ChildNote> = []\n let id: string | undefined = subagentRunId\n for (let depth = 0; id !== undefined && depth < 64; depth++) {\n const note = children.get(id)\n if (!note) break\n notes.push(note)\n id = note.parentSubagentRunId\n }\n return notes\n }\n\n async function saveChild(subagentRunId: string) {\n const note = children.get(subagentRunId)\n if (!note) return\n note.savedAt = Date.now()\n const info: SubagentWireInfo = {\n name: note.name,\n status: note.status,\n ...(note.parentSubagentRunId !== undefined && {\n parentSubagentRunId: note.parentSubagentRunId,\n }),\n ...(note.parentToolCallId !== undefined && {\n parentToolCallId: note.parentToolCallId,\n }),\n ...(note.interruptIds !== undefined && {\n interruptIds: note.interruptIds,\n }),\n ...(note.error !== undefined && { error: note.error }),\n ...(note.metadata !== undefined && { metadata: note.metadata }),\n }\n const transcript = convertMessagesToModelMessages(\n withoutCards(note.processor.getMessages()),\n )\n const [first, ...rest] = transcript\n const head: ModelMessage = first\n ? { ...first, metadata: withSubagentInfo(first.metadata, info) }\n : {\n id: `child:${subagentRunId}`,\n role: 'assistant',\n content: '',\n metadata: withSubagentInfo(undefined, { ...info, placeholder: true }),\n }\n await stores.messages.saveThread(childStoreId(subagentRunId), [\n head,\n ...rest,\n ])\n }\n\n // The parent message for a routed run holds the children's text. It exists\n // even before any child writes text, so a reload can show a waiting card.\n async function saveParent(threadId: string, runId: string) {\n const notes = [...children.values()].filter(\n (note) =>\n note.parentRunId === runId &&\n note.parentSubagentRunId === undefined &&\n note.parentToolCallId === undefined,\n )\n if (notes.length === 0) return\n const content = notes\n .map((note) => {\n const text = childText(note.processor.getMessages())\n return text === '' ? '' : `${note.name}:\\n${text}`\n })\n .filter((block) => block !== '')\n .join('\\n\\n')\n const stored = await loadMessages(threadId)\n // A resume updates the message of the run that started the child.\n const index = stored.findIndex(\n (message) => readModelRunId(message) === runId,\n )\n const host = stored[index]\n if (host) {\n const next = [...stored]\n next[index] = { ...host, content }\n await stores.messages.saveThread(threadId, next)\n return\n }\n const assistant: ModelMessage = {\n id: assistantId(runId),\n role: 'assistant',\n content,\n metadata: { tanstack: { runId } },\n }\n await stores.messages.saveThread(threadId, [...stored, assistant])\n }\n\n async function startChild(\n input: { threadId: string; runId: string },\n chunk: Extract<StreamChunk, { type: 'SUBAGENT_STARTED' }>,\n ) {\n const id = chunk.subagentRunId\n const record = await stores.runs?.createOrResume({\n runId: id,\n threadId: childStoreId(id),\n startedAt: Date.now(),\n parentRunId: chunk.parentSubagentRunId ?? input.runId,\n subagentRunId: id,\n name: chunk.name,\n })\n const existing = children.get(id)\n if (existing) {\n existing.threadId = input.threadId\n existing.runId = input.runId\n existing.status = 'running'\n delete existing.interruptIds\n if (chunk.metadata !== undefined) existing.metadata = chunk.metadata\n } else {\n // A resume continues the stored transcript.\n const stored = (await loadMessages(childStoreId(id))).filter(\n (message) => storedSubagentInfo([message])?.placeholder !== true,\n )\n children.set(id, {\n name: chunk.name,\n threadId: input.threadId,\n runId: input.runId,\n savedAt: 0,\n parentRunId:\n record?.parentRunId ?? chunk.parentSubagentRunId ?? input.runId,\n ...(chunk.parentSubagentRunId !== undefined && {\n parentSubagentRunId: chunk.parentSubagentRunId,\n }),\n ...(chunk.parentToolCallId !== undefined && {\n parentToolCallId: chunk.parentToolCallId,\n }),\n ...(chunk.metadata !== undefined && { metadata: chunk.metadata }),\n processor: new StreamProcessor({\n subagentRunId: id,\n initialMessages: modelMessagesToUIMessages(stored),\n }),\n status: 'running',\n })\n }\n if (record && record.status !== 'running') {\n await stores.runs?.update(id, { status: 'running' })\n }\n // The parent child shows this one as a nested card.\n if (chunk.parentSubagentRunId !== undefined) {\n children.get(chunk.parentSubagentRunId)?.processor.processChunk(chunk)\n }\n await saveChild(id)\n }\n\n async function settleChild(\n chunk: Extract<\n StreamChunk,\n { type: 'SUBAGENT_FINISHED' | 'SUBAGENT_ERROR' }\n >,\n ) {\n const id = chunk.subagentRunId\n const note = children.get(id)\n if (!note) return\n try {\n await writeSettled(note, id, chunk)\n } finally {\n // A routed child stays until its run ends: the parent message reads its\n // text. Nested children and children started by a tool call go now.\n if (\n note.parentSubagentRunId !== undefined ||\n note.parentToolCallId !== undefined\n ) {\n children.delete(id)\n }\n }\n }\n\n async function writeSettled(\n note: ChildNote,\n id: string,\n chunk: Extract<\n StreamChunk,\n { type: 'SUBAGENT_FINISHED' | 'SUBAGENT_ERROR' }\n >,\n ) {\n note.processor.finalizeStream()\n if (note.parentSubagentRunId !== undefined) {\n children.get(note.parentSubagentRunId)?.processor.processChunk(chunk)\n }\n if (chunk.type === 'SUBAGENT_ERROR') {\n const stopped = chunk.message === 'Stopped'\n note.status = 'error'\n note.error = { message: chunk.message }\n await saveChild(id)\n await stores.runs?.update(id, {\n status: stopped ? 'aborted' : 'failed',\n finishedAt: Date.now(),\n ...(!stopped ? { error: { message: chunk.message } } : {}),\n })\n return\n }\n if (chunk.outcome?.type === 'suspended') {\n note.status = 'suspended'\n note.interruptIds = chunk.outcome.interruptIds ?? []\n await saveChild(id)\n await stores.runs?.update(id, { status: 'interrupted' })\n return\n }\n note.status = 'finished'\n await saveChild(id)\n await stores.runs?.update(id, {\n status: 'completed',\n finishedAt: Date.now(),\n })\n }\n\n async function commitAnswers(runId: string) {\n const entries = answered.get(runId) ?? []\n answered.delete(runId)\n for (const entry of entries) {\n if (entry.status === 'cancelled') {\n await stores.interrupts?.cancel(entry.interruptId)\n } else {\n await stores.interrupts?.resolve(entry.interruptId, entry.payload)\n }\n }\n }\n\n /** The notes a run fed. */\n function notesOf(runId: string) {\n return [...children].filter(([, note]) => note.runId === runId)\n }\n\n function forget(runId: string) {\n for (const [id] of notesOf(runId)) children.delete(id)\n parentSavedAt.delete(runId)\n }\n\n async function settleOpenChildren(\n runId: string,\n status: 'completed' | 'failed' | 'aborted',\n error?: { message: string },\n ) {\n for (const [subagentRunId] of notesOf(runId)) {\n await saveChild(subagentRunId)\n const current = await stores.runs?.get(subagentRunId)\n if (current && current.status !== 'running') continue\n await stores.runs?.update(subagentRunId, {\n status,\n finishedAt: Date.now(),\n ...(error ? { error } : {}),\n })\n }\n }\n\n /** Write the parent messages of this thread's routed children. */\n async function saveParents(threadId: string) {\n const runIds = new Set(\n [...children.values()]\n .filter((note) => note.threadId === threadId)\n .map((note) => note.parentRunId),\n )\n for (const runId of runIds) await saveParent(threadId, runId)\n }\n\n return {\n async start(input: {\n threadId: string\n runId: string\n messages: ReadonlyArray<UIMessage | ModelMessage>\n resume?: ReadonlyArray<RunAgentResumeItem>\n }) {\n await stores.runs?.createOrResume({\n runId: input.runId,\n threadId: input.threadId,\n startedAt: Date.now(),\n })\n if (input.resume?.length) answered.set(input.runId, [...input.resume])\n const incoming = convertMessagesToModelMessages([...input.messages])\n const stored = await loadMessages(input.threadId)\n const merged = mergeStoredMessages(stored, incoming)\n await stores.messages.saveThread(\n input.threadId,\n keepSubagentRunIds(stored, merged),\n )\n },\n\n async chunk(input: {\n threadId: string\n runId: string\n chunk: StreamChunk\n }) {\n const chunk = input.chunk\n if (chunk.type === 'SUBAGENT_STARTED') {\n await startChild(input, chunk)\n return\n }\n if (\n chunk.type === 'SUBAGENT_FINISHED' ||\n chunk.type === 'SUBAGENT_ERROR'\n ) {\n await settleChild(chunk)\n await saveParents(input.threadId)\n return\n }\n const subagentRunId = readSubagentRunId(chunk)\n if (!subagentRunId) return\n const notes = lineage(subagentRunId)\n if (notes.length === 0) return\n for (const note of notes) note.processor.processChunk(chunk)\n // A streaming child writes at most once per interval. Its terminal\n // event and the run's end always write.\n const now = Date.now()\n const note = notes[0]\n if (note && now - note.savedAt >= intervalMs) {\n await saveChild(subagentRunId)\n }\n if (\n chunk.type === 'TEXT_MESSAGE_CONTENT' &&\n now - (parentSavedAt.get(input.runId) ?? 0) >= intervalMs\n ) {\n parentSavedAt.set(input.runId, now)\n await saveParents(input.threadId)\n }\n },\n\n async suspend(input: {\n threadId: string\n runId: string\n interrupts: ReadonlyArray<Interrupt>\n }) {\n await commitAnswers(input.runId)\n await saveParents(input.threadId)\n for (const [subagentRunId] of notesOf(input.runId)) {\n await saveChild(subagentRunId)\n }\n for (const interrupt of input.interrupts) {\n await stores.interrupts?.create({\n interruptId: interrupt.id,\n runId: input.runId,\n threadId: input.threadId,\n requestedAt: Date.now(),\n payload: { ...interrupt },\n })\n }\n await stores.runs?.update(input.runId, { status: 'interrupted' })\n forget(input.runId)\n },\n\n async finish(input: { threadId: string; runId: string }) {\n await commitAnswers(input.runId)\n await saveParents(input.threadId)\n await settleOpenChildren(input.runId, 'completed')\n await stores.runs?.update(input.runId, {\n status: 'completed',\n finishedAt: Date.now(),\n })\n forget(input.runId)\n },\n\n async abort(input: { threadId: string; runId: string; error?: unknown }) {\n const aborted =\n input.error instanceof Error && input.error.name === 'AbortError'\n const message =\n input.error instanceof Error ? input.error.message : 'Run failed'\n await settleOpenChildren(\n input.runId,\n aborted ? 'aborted' : 'failed',\n aborted ? undefined : { message },\n )\n await saveParents(input.threadId)\n await stores.runs?.update(input.runId, {\n status: aborted ? 'aborted' : 'failed',\n finishedAt: Date.now(),\n ...(!aborted ? { error: { message } } : {}),\n })\n answered.delete(input.runId)\n forget(input.runId)\n },\n }\n}\n"],"mappings":"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"}
@@ -7,7 +7,7 @@ type MakePersistence = () => Promise<AIPersistence> | AIPersistence;
7
7
  * policy in `../types.ts`. It was optional for one release cycle and silently
8
8
  * disabled reconnect on every backend that had not caught up.
9
9
  */
10
- type OptionalRunStoreMethod = 'listByThread' | 'listReclaimable';
10
+ type OptionalRunStoreMethod = 'listByThread' | 'listByParentRun' | 'listReclaimable';
11
11
  /** Dotted `store.method` key a backend passes to declare an omitted method. */
12
12
  export type PersistenceConformanceMethodKey = `runs.${OptionalRunStoreMethod}`;
13
13
  export interface PersistenceConformanceOptions {
@@ -21,6 +21,9 @@ export interface PersistenceConformanceOptions {
21
21
  * OPTIONAL store methods this backend intentionally does not implement, as
22
22
  * `'runs.listByThread'` and friends. A method that is absent and NOT listed
23
23
  * here fails the suite; a listed one is reported as a skipped case.
24
+ * `listByParentRun` is the exception. Subagent support is opt-in, its cases
25
+ * skip on their own, and a `'runs.listByParentRun'` entry is accepted but
26
+ * has no effect.
24
27
  */
25
28
  skipMethods?: Array<PersistenceConformanceMethodKey>;
26
29
  }
@@ -21,7 +21,9 @@ import { beforeAll, describe, expect, it } from "vitest";
21
21
  * a SKIPPED case, never as a pass. Silent gaps are not allowed: a case that did
22
22
  * not run must never be indistinguishable from one that did. A chat-only
23
23
  * adapter therefore passes `skip: ['generationRuns', 'artifacts', 'blobs']`,
24
- * and a generation-only one skips the four state stores.
24
+ * and a generation-only one skips the four state stores. `listByParentRun` is
25
+ * the exception. Subagent support is opt-in, its cases skip on their own, and
26
+ * a `'runs.listByParentRun'` entry is accepted but has no effect.
25
27
  *
26
28
  * NOT COVERED HERE: the four durable-run fields on `RunRecord` (`sandboxKey`,
27
29
  * `detachedSince`, `cancelRequested`, `driverEpoch`). They exist for durable
@@ -103,6 +105,9 @@ function runPersistenceConformance(name, makePersistence, options) {
103
105
  if (skipMethods.has(key)) return false;
104
106
  throw new Error(`AIPersistence conformance: optional method '${key}' is not implemented. Implement it, or pass { skipMethods: ['${key}'] } if the omission is intentional.`);
105
107
  }
108
+ function supportsSubagents(runs) {
109
+ return typeof runs.listByParentRun === "function";
110
+ }
106
111
  describe("messages", () => {
107
112
  it("round-trips a thread and returns [] for unknown threads", async (ctx) => {
108
113
  const store = resolveStore("messages");
@@ -283,6 +288,56 @@ function runPersistenceConformance(name, makePersistence, options) {
283
288
  finishedAt: 20
284
289
  });
285
290
  });
291
+ it("round-trips subagent link fields and ignores them on resume", async (ctx) => {
292
+ const store = resolveStore("runs");
293
+ if (!store) return ctx.skip("store not provided");
294
+ if (!supportsSubagents(store)) return ctx.skip("runs.listByParentRun not implemented");
295
+ const parent = await store.createOrResume({
296
+ runId: "lp-parent",
297
+ threadId: "lp-thread",
298
+ startedAt: 1
299
+ });
300
+ expect(parent.parentRunId).toBeUndefined();
301
+ expect(parent.subagentRunId).toBeUndefined();
302
+ expect(parent.name).toBeUndefined();
303
+ const child = await store.createOrResume({
304
+ runId: "lp-child",
305
+ threadId: "subagent:lp-child",
306
+ startedAt: 2,
307
+ parentRunId: "lp-parent",
308
+ subagentRunId: "lp-sub",
309
+ name: "researcher"
310
+ });
311
+ expect(child).toMatchObject({
312
+ runId: "lp-child",
313
+ threadId: "subagent:lp-child",
314
+ startedAt: 2,
315
+ parentRunId: "lp-parent",
316
+ subagentRunId: "lp-sub",
317
+ name: "researcher"
318
+ });
319
+ const resumed = await store.createOrResume({
320
+ runId: "lp-child",
321
+ threadId: "lp-other-thread",
322
+ startedAt: 99,
323
+ parentRunId: "lp-other-parent",
324
+ subagentRunId: "lp-other-sub",
325
+ name: "writer"
326
+ });
327
+ expect(resumed).toMatchObject({
328
+ runId: "lp-child",
329
+ threadId: "subagent:lp-child",
330
+ startedAt: 2,
331
+ parentRunId: "lp-parent",
332
+ subagentRunId: "lp-sub",
333
+ name: "researcher"
334
+ });
335
+ expect(await store.get("lp-child")).toMatchObject({
336
+ parentRunId: "lp-parent",
337
+ subagentRunId: "lp-sub",
338
+ name: "researcher"
339
+ });
340
+ });
286
341
  it("findActiveRun returns the most recent running run for a thread", async (ctx) => {
287
342
  const store = resolveStore("runs");
288
343
  if (!store) return ctx.skip("store not provided");
@@ -339,6 +394,44 @@ function runPersistenceConformance(name, makePersistence, options) {
339
394
  const listed = await runs.listByThread("lt");
340
395
  expect(listed.map((r) => r.runId)).toEqual(["lt-a", "lt-b"]);
341
396
  });
397
+ it("lists child runs by parent when supported", async (ctx) => {
398
+ const runs = resolveStore("runs");
399
+ if (!runs) return ctx.skip("store not provided");
400
+ if (!supportsSubagents(runs)) return ctx.skip("runs.listByParentRun not implemented");
401
+ await runs.createOrResume({
402
+ runId: "lbp-parent",
403
+ threadId: "lbp-thread",
404
+ startedAt: 1
405
+ });
406
+ await runs.createOrResume({
407
+ runId: "lbp-b",
408
+ threadId: "subagent:lbp-b",
409
+ startedAt: 20,
410
+ parentRunId: "lbp-parent",
411
+ subagentRunId: "lbp-sub-b",
412
+ name: "seo"
413
+ });
414
+ await runs.createOrResume({
415
+ runId: "lbp-a",
416
+ threadId: "subagent:lbp-a",
417
+ startedAt: 10,
418
+ parentRunId: "lbp-parent",
419
+ subagentRunId: "lbp-sub-a",
420
+ name: "researcher"
421
+ });
422
+ await runs.createOrResume({
423
+ runId: "lbp-other",
424
+ threadId: "subagent:lbp-other",
425
+ startedAt: 5,
426
+ parentRunId: "lbp-elsewhere",
427
+ subagentRunId: "lbp-sub-other",
428
+ name: "writer"
429
+ });
430
+ const listed = await runs.listByParentRun("lbp-parent");
431
+ expect(listed.map((run) => run.runId)).toEqual(["lbp-a", "lbp-b"]);
432
+ expect(listed.map((run) => run.name)).toEqual(["researcher", "seo"]);
433
+ expect(await runs.listByParentRun("lbp-missing")).toEqual([]);
434
+ });
342
435
  it("lists reclaimable detached runs when supported", async (ctx) => {
343
436
  const runs = resolveStore("runs");
344
437
  if (!runs) return ctx.skip("store not provided");
@@ -1 +1 @@
1
- {"version":3,"file":"conformance.js","names":[],"sources":["../../../src/testkit/conformance.ts"],"sourcesContent":["/**\n * Shared conformance suite for the `AIPersistence` store contract.\n *\n * Every backend runs this identical suite — the in-memory reference store and\n * every adapter you write against your own database — so that schema drift or\n * an implementation gap fails immediately. It exercises every method of every\n * store the persistence exposes and is the authoritative compatibility gate for\n * the store interfaces in `../types.ts`.\n *\n * Covers all seven stores: the four chat state stores (`messages`, `runs`,\n * `interrupts`, `metadata`) and the three generation stores (`generationRuns`,\n * `artifacts`, `blobs`). Locks are not part of this suite — they are a separate\n * coordination concern (`LockStore` + `withLocks`), not a store.\n *\n * SKIPPING (declare or fail): a backend that deliberately omits a store must\n * declare it in `options.skip`, and one that omits an OPTIONAL store method\n * must declare it in `options.skipMethods`. Anything absent and not declared\n * fails the suite loudly, and anything declared absent is reported by vitest as\n * a SKIPPED case, never as a pass. Silent gaps are not allowed: a case that did\n * not run must never be indistinguishable from one that did. A chat-only\n * adapter therefore passes `skip: ['generationRuns', 'artifacts', 'blobs']`,\n * and a generation-only one skips the four state stores.\n *\n * NOT COVERED HERE: the four durable-run fields on `RunRecord` (`sandboxKey`,\n * `detachedSince`, `cancelRequested`, `driverEpoch`). They exist for durable\n * sandboxed runs, a chat app never writes them, and requiring them here made every\n * backend implement four columns and an omitted-vs-explicit-undefined rule it had no\n * use for. They are proven by `runDurableRunFieldsConformance` from\n * `@tanstack/ai-sandbox/testkit`, next to the takeover and reaper suites that consume\n * them. A backend that never runs sandboxes can leave the columns out.\n *\n * RESERVED RUN-ID PREFIX: `rc-` belongs to the `listReclaimable` case, which\n * filters the method's (not thread-scoped) result down to `rc-` ids before an\n * exact-set assertion. A new case in `describe('runs')` must NOT seed a run id\n * starting with `rc-`, or it silently changes that expected set.\n */\nimport { beforeAll, describe, expect, it } from 'vitest'\nimport type { ModelMessage } from '@tanstack/ai'\nimport type {\n AIPersistence,\n AIPersistenceStores,\n ArtifactRecord,\n RunStore,\n} from '../types'\n\ntype MakePersistence = () => Promise<AIPersistence> | AIPersistence\n\n/**\n * Methods that are optional on the `RunStore` contract.\n *\n * `findActiveRun` is deliberately NOT here: it is REQUIRED, per the evolution\n * policy in `../types.ts`. It was optional for one release cycle and silently\n * disabled reconnect on every backend that had not caught up.\n */\ntype OptionalRunStoreMethod = 'listByThread' | 'listReclaimable'\n\n/** Dotted `store.method` key a backend passes to declare an omitted method. */\nexport type PersistenceConformanceMethodKey = `runs.${OptionalRunStoreMethod}`\n\n/**\n * Unwrap a value the store contract says must be present. Fails the test with a\n * readable message instead of a non-null assertion (banned in this package) or\n * an early `return` that would pass silently.\n */\nfunction required<TValue>(\n value: TValue | null | undefined,\n what: string,\n): TValue {\n if (value == null) {\n throw new Error(`AIPersistence conformance: expected ${what} to exist`)\n }\n return value\n}\n\n/** Read a `BlobObject.body` to completion as one contiguous buffer. */\nasync function drainStream(\n stream: ReadableStream<Uint8Array>,\n): Promise<Uint8Array> {\n const chunks: Array<Uint8Array> = []\n let total = 0\n const reader = stream.getReader()\n for (;;) {\n const { value, done } = await reader.read()\n if (done) break\n chunks.push(value)\n total += value.byteLength\n }\n const bytes = new Uint8Array(total)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n return bytes\n}\n\nexport interface PersistenceConformanceOptions {\n /**\n * Store keys this backend intentionally does not provide. Any store that is\n * absent from the persistence and NOT listed here fails the suite, so a\n * dropped/misconfigured store can never pass silently.\n */\n skip?: Array<keyof AIPersistenceStores>\n /**\n * OPTIONAL store methods this backend intentionally does not implement, as\n * `'runs.listByThread'` and friends. A method that is absent and NOT listed\n * here fails the suite; a listed one is reported as a skipped case.\n */\n skipMethods?: Array<PersistenceConformanceMethodKey>\n}\n\n/**\n * Register a Vitest suite that validates `makePersistence()` against the full\n * `AIPersistence` contract — every store it provides, and none it declares\n * skipped.\n */\nexport function runPersistenceConformance(\n name: string,\n makePersistence: MakePersistence,\n options?: PersistenceConformanceOptions,\n): void {\n const skip = new Set<keyof AIPersistenceStores>(options?.skip ?? [])\n const skipMethods = new Set<PersistenceConformanceMethodKey>(\n options?.skipMethods ?? [],\n )\n\n describe(`AIPersistence conformance: ${name}`, () => {\n let persistence: AIPersistence\n\n beforeAll(async () => {\n persistence = await makePersistence()\n })\n\n /**\n * Return the store for `key`, or `null` when the backend intentionally\n * skips it. Throws (failing the test) when a store is missing but was not\n * declared in `options.skip`.\n */\n function resolveStore<TKey extends keyof AIPersistenceStores>(\n key: TKey,\n ): NonNullable<AIPersistenceStores[TKey]> | null {\n const store = persistence.stores[key]\n if (store) return store\n if (skip.has(key)) return null\n throw new Error(\n `AIPersistence conformance: store '${key}' is missing. ` +\n `Provide it, or pass { skip: ['${key}'] } if the omission is intentional.`,\n )\n }\n\n /**\n * Narrow `runs` to a store that definitely implements the optional method\n * `methodName`, so the case can call it without a non-null assertion.\n *\n * Returns `false` only when the omission was declared in\n * `options.skipMethods` (the caller then reports a skip). An undeclared\n * omission throws, mirroring `resolveStore`: a case that cannot run must\n * never be reported as a pass.\n */\n function hasRunsMethod<TName extends OptionalRunStoreMethod>(\n runs: RunStore,\n methodName: TName,\n ): runs is RunStore & Required<Pick<RunStore, TName>> {\n if (runs[methodName]) return true\n const key: PersistenceConformanceMethodKey = `runs.${methodName}`\n if (skipMethods.has(key)) return false\n throw new Error(\n `AIPersistence conformance: optional method '${key}' is not implemented. ` +\n `Implement it, or pass { skipMethods: ['${key}'] } if the omission is intentional.`,\n )\n }\n\n describe('messages', () => {\n // One-argument loadThread is the full-thread contract. Paging\n // (`limit` / `before`) is an optional hint; this suite does not require it.\n it('round-trips a thread and returns [] for unknown threads', async (ctx) => {\n const store = resolveStore('messages')\n if (!store) return ctx.skip('store not provided')\n\n expect(await store.loadThread('thread-unknown')).toEqual([])\n\n await store.saveThread('thread-msg', [\n { role: 'user', content: 'hi' },\n { role: 'assistant', content: 'hello' },\n ])\n const loaded = await store.loadThread('thread-msg')\n expect(Array.isArray(loaded)).toBe(true)\n expect(loaded).toEqual([\n { role: 'user', content: 'hi' },\n { role: 'assistant', content: 'hello' },\n ])\n\n // Overwrites, not appends.\n await store.saveThread('thread-msg', [\n { role: 'user', content: 'redo' },\n ])\n expect(await store.loadThread('thread-msg')).toEqual([\n { role: 'user', content: 'redo' },\n ])\n })\n\n it('round-trips rich message shapes with deep equality', async (ctx) => {\n const store = resolveStore('messages')\n if (!store) return ctx.skip('store not provided')\n\n const rich: Array<ModelMessage> = [\n { role: 'user', content: 'plain string' },\n {\n // Tool-call message with JSON arguments.\n role: 'assistant',\n content: '',\n toolCalls: [\n {\n id: 'call-1',\n type: 'function',\n function: {\n name: 'search',\n arguments: '{\"query\":\"weather in Paris\"}',\n },\n },\n ],\n },\n {\n // Tool result message.\n role: 'tool',\n content: '{\"temperature\":21,\"unit\":\"C\"}',\n toolCallId: 'call-1',\n },\n {\n // Multi-part content: text + image reference.\n role: 'user',\n content: [\n { type: 'text', content: 'What is in this image?' },\n {\n type: 'image',\n source: {\n type: 'url',\n value: 'https://example.com/cat.png',\n mimeType: 'image/png',\n },\n },\n ],\n },\n {\n // Reasoning / thinking part.\n role: 'assistant',\n content: 'Here is my answer.',\n thinking: [\n {\n content: 'The user is asking about the image.',\n signature: 'sig-1',\n },\n ],\n },\n ]\n\n await store.saveThread('thread-rich', rich)\n expect(await store.loadThread('thread-rich')).toEqual(rich)\n })\n })\n\n describe('runs', () => {\n it('creates, resumes idempotently, updates, and gets', async (ctx) => {\n const store = resolveStore('runs')\n if (!store) return ctx.skip('store not provided')\n\n expect(await store.get('run-missing')).toBeNull()\n\n const created = await store.createOrResume({\n runId: 'run-1',\n threadId: 'thread-1',\n startedAt: 1000,\n })\n expect(created).toMatchObject({\n runId: 'run-1',\n threadId: 'thread-1',\n status: 'running',\n startedAt: 1000,\n })\n\n // createOrResume is idempotent: returns the existing record unchanged.\n const resumed = await store.createOrResume({\n runId: 'run-1',\n threadId: 'thread-different',\n startedAt: 9999,\n })\n expect(resumed).toMatchObject({\n runId: 'run-1',\n threadId: 'thread-1',\n startedAt: 1000,\n })\n\n await store.update('run-1', {\n status: 'completed',\n finishedAt: 2000,\n usage: { promptTokens: 3, completionTokens: 4, totalTokens: 7 },\n })\n const done = await store.get('run-1')\n expect(done).toMatchObject({\n runId: 'run-1',\n status: 'completed',\n finishedAt: 2000,\n usage: { promptTokens: 3, completionTokens: 4, totalTokens: 7 },\n })\n\n const resumedAfterUpdate = await store.createOrResume({\n runId: 'run-1',\n threadId: 'thread-different',\n startedAt: 9999,\n })\n expect(resumedAfterUpdate).toEqual(done)\n\n // `error` is a structured RunError: the prose `message` plus the\n // optional machine-branchable `code`. Both must survive the round-trip,\n // so a backend that flattens the record to a bare string fails here.\n await store.update('run-1', {\n status: 'failed',\n error: { message: 'boom', code: 'provider_overloaded' },\n })\n const failed = await store.get('run-1')\n expect(failed?.status).toBe('failed')\n expect(failed?.error).toEqual({\n message: 'boom',\n code: 'provider_overloaded',\n })\n\n // Updating a missing run is a no-op (does not throw, does not create).\n await store.update('run-absent', { status: 'completed' })\n expect(await store.get('run-absent')).toBeNull()\n })\n\n // The idempotency invariant has teeth precisely where it is dangerous:\n // resuming a run that already FINISHED must not resurrect it. An adapter\n // written as `INSERT ... ON CONFLICT DO UPDATE SET status='running'`\n // looks correct on a still-running record and silently revives dead ones,\n // after which `findActiveRun` hands clients a run that will never emit\n // again. Assert the terminal status and `finishedAt` both survive.\n it('createOrResume never resurrects a finished run', async (ctx) => {\n const store = resolveStore('runs')\n if (!store) return ctx.skip('store not provided')\n\n await store.createOrResume({\n runId: 'nc-1',\n threadId: 'nc-t',\n startedAt: 10,\n })\n await store.update('nc-1', { status: 'completed', finishedAt: 20 })\n\n // Resume with a DIFFERENT status/startedAt: both must be ignored.\n const resumed = await store.createOrResume({\n runId: 'nc-1',\n threadId: 'nc-t',\n startedAt: 999,\n status: 'running',\n })\n expect(resumed).toMatchObject({\n runId: 'nc-1',\n status: 'completed',\n startedAt: 10,\n finishedAt: 20,\n })\n\n // And the stored record itself was not rewritten either.\n expect(await store.get('nc-1')).toMatchObject({\n status: 'completed',\n startedAt: 10,\n finishedAt: 20,\n })\n })\n\n it('findActiveRun returns the most recent running run for a thread', async (ctx) => {\n const store = resolveStore('runs')\n if (!store) return ctx.skip('store not provided')\n\n const thread = 'thread-active'\n expect(await store.findActiveRun(thread)).toBeNull()\n\n await store.createOrResume({\n runId: 'active-1',\n threadId: thread,\n startedAt: 1000,\n })\n await store.createOrResume({\n runId: 'active-2',\n threadId: thread,\n startedAt: 2000,\n })\n // Most-recent running run wins.\n expect(await store.findActiveRun(thread)).toMatchObject({\n runId: 'active-2',\n status: 'running',\n })\n\n // A different thread's running run is not returned.\n await store.createOrResume({\n runId: 'other-1',\n threadId: 'thread-other',\n startedAt: 3000,\n })\n expect(await store.findActiveRun(thread)).toMatchObject({\n runId: 'active-2',\n })\n\n // Once the newest finishes, the older running run becomes active.\n await store.update('active-2', {\n status: 'completed',\n finishedAt: 2500,\n })\n expect(await store.findActiveRun(thread)).toMatchObject({\n runId: 'active-1',\n status: 'running',\n })\n\n // With none running, it is null.\n await store.update('active-1', {\n status: 'completed',\n finishedAt: 1500,\n })\n expect(await store.findActiveRun(thread)).toBeNull()\n })\n\n // `listByThread` is optional on the RunStore contract; a declared omission\n // is reported as skipped and an undeclared one fails. Any backend that has\n // it must return that thread's runs ordered ascending by `startedAt`.\n it('lists runs by thread when supported', async (ctx) => {\n const runs = resolveStore('runs')\n if (!runs) return ctx.skip('store not provided')\n if (!hasRunsMethod(runs, 'listByThread')) {\n return ctx.skip('runs.listByThread not implemented')\n }\n\n await runs.createOrResume({\n runId: 'lt-b',\n threadId: 'lt',\n startedAt: 2,\n })\n await runs.createOrResume({\n runId: 'lt-a',\n threadId: 'lt',\n startedAt: 1,\n })\n const listed = await runs.listByThread('lt')\n expect(listed.map((r) => r.runId)).toEqual(['lt-a', 'lt-b'])\n })\n\n // `listReclaimable` is optional on the RunStore contract; a declared\n // omission is reported as skipped and an undeclared one fails. Any\n // backend that has it must\n // surface only runs where ALL THREE hold: status === 'running',\n // detachedSince is set, and detachedSince <= now - ttlMs (inclusive\n // cutoff). Each negative fixture below pins one of those conditions so\n // a backend that drops any single check (e.g. \"return every run\", or\n // \"ignore status\", or \"ignore detachedSince\") fails this case. Do not\n // simplify these away to a bare `toContain` — that is exactly the\n // weakness this case was strengthened to catch.\n it('lists reclaimable detached runs when supported', async (ctx) => {\n const runs = resolveStore('runs')\n if (!runs) return ctx.skip('store not provided')\n if (!hasRunsMethod(runs, 'listReclaimable')) {\n return ctx.skip('runs.listReclaimable not implemented')\n }\n\n const now = 10_000\n const ttlMs = 5_000\n const cutoff = now - ttlMs // 5_000\n\n // Positive: running, detached well past the cutoff.\n await runs.createOrResume({\n runId: 'rc-included',\n threadId: 'rc-t',\n startedAt: 1,\n })\n await runs.update('rc-included', { detachedSince: 1_000 })\n\n // Positive boundary: detachedSince exactly equals the cutoff. Pins\n // the `<=` (inclusive) semantics — a backend that uses `<` instead\n // would wrongly exclude this run.\n await runs.createOrResume({\n runId: 'rc-boundary',\n threadId: 'rc-t',\n startedAt: 1,\n })\n await runs.update('rc-boundary', { detachedSince: cutoff })\n\n // Negative: still running, but detached AFTER the cutoff (not yet\n // abandoned long enough). Pins the `<= cutoff` comparison — a\n // backend that returns every detached run regardless of how recent\n // would wrongly include this one.\n await runs.createOrResume({\n runId: 'rc-too-recent',\n threadId: 'rc-t',\n startedAt: 1,\n })\n await runs.update('rc-too-recent', { detachedSince: cutoff + 1 })\n\n // Negative: detached past the cutoff, but no longer running (already\n // completed). Pins the `status === 'running'` check — a backend\n // that ignores status would wrongly include this one.\n await runs.createOrResume({\n runId: 'rc-completed',\n threadId: 'rc-t',\n startedAt: 1,\n })\n await runs.update('rc-completed', {\n detachedSince: 1_000,\n status: 'completed',\n finishedAt: 2_000,\n })\n\n // Negative: running, but never detached at all. Pins the\n // `detachedSince !== undefined` check — a backend that treats a\n // missing `detachedSince` as \"always reclaimable\" would wrongly\n // include this one.\n await runs.createOrResume({\n runId: 'rc-never-detached',\n threadId: 'rc-t',\n startedAt: 1,\n })\n\n const reclaimable = await runs.listReclaimable({ now, ttlMs })\n\n // Scope the assertion to ids seeded by this case: `listReclaimable`\n // is not thread-scoped, so it also sees `'running'` runs seeded by\n // sibling cases in this shared-store `describe('runs', ...)` block\n // (e.g. `other-1`, `lt-a`, `lt-b`). Those all lack `detachedSince`,\n // so a correct implementation already excludes them — but filtering\n // here keeps this assertion from depending on that fact holding for\n // every other case forever. Ordering is not part of this method's\n // contract, so sort before an exact-set comparison.\n const ourIds = reclaimable\n .map((r) => r.runId)\n .filter((id) => id.startsWith('rc-'))\n .sort()\n expect(ourIds).toEqual(['rc-boundary', 'rc-included'])\n\n // The four assertions below are scoped by exact runId (never by the\n // `rc-` exact-set comparison above), so each uses its own randomUUID\n // fixture and cannot perturb the fixed-set assertion just made.\n\n // (1) `ttlMs: 0` pins the cutoff as inclusive: a run detached at\n // exactly `now` (cutoff === now) must still come back. A backend\n // using strict `<` instead of `<=` would silently never reclaim a\n // run detached exactly at the boundary.\n const zeroTtlRunId = `rc-${crypto.randomUUID()}`\n await runs.createOrResume({\n runId: zeroTtlRunId,\n threadId: 'rc-t',\n startedAt: 1,\n })\n await runs.update(zeroTtlRunId, { detachedSince: now })\n const zeroTtlReclaimable = await runs.listReclaimable({\n now,\n ttlMs: 0,\n })\n expect(\n zeroTtlReclaimable.find((r) => r.runId === zeroTtlRunId)\n ?.detachedSince,\n ).toBe(now)\n\n // (2) Re-attaching — `update(runId, { detachedSince: undefined })`\n // — must drop the run out of the list. This is the most important\n // assertion in this case: a SQL `SET`-clause builder that filters\n // `undefined` out of the patch (`'field' in patch` instead of\n // `patch.field !== undefined`) keeps the old `detachedSince`, so a\n // run a user has actively re-attached to still looks detached — and\n // the reaper then cancels a run someone is watching.\n const reattachedRunId = `rc-${crypto.randomUUID()}`\n await runs.createOrResume({\n runId: reattachedRunId,\n threadId: 'rc-t',\n startedAt: 1,\n })\n await runs.update(reattachedRunId, { detachedSince: 1_000 })\n await runs.update(reattachedRunId, { detachedSince: undefined })\n const afterReattach = await runs.listReclaimable({ now, ttlMs })\n expect(afterReattach.some((r) => r.runId === reattachedRunId)).toBe(\n false,\n )\n\n // (3) No terminal status (`completed` / `failed` / `aborted`) ever\n // appears, whatever its `detachedSince`.\n const terminalStatuses = ['completed', 'failed', 'aborted'] as const\n const terminalRunIds = await Promise.all(\n terminalStatuses.map(async (status) => {\n const runId = `rc-${crypto.randomUUID()}`\n await runs.createOrResume({ runId, threadId: 'rc-t', startedAt: 1 })\n await runs.update(runId, {\n status,\n detachedSince: 1_000,\n finishedAt: 2_000,\n })\n return runId\n }),\n )\n const afterTerminal = await runs.listReclaimable({ now, ttlMs })\n expect(\n afterTerminal.some((r) => terminalRunIds.includes(r.runId)),\n ).toBe(false)\n\n // (4) `'interrupted'` does not appear. The documented predicate is\n // `status === 'running'`; an interrupted run is a human-in-the-loop\n // pause that interrupt-resume continues, not abandoned work a reaper\n // should tear down.\n const interruptedRunId = `rc-${crypto.randomUUID()}`\n await runs.createOrResume({\n runId: interruptedRunId,\n threadId: 'rc-t',\n startedAt: 1,\n })\n await runs.update(interruptedRunId, {\n status: 'interrupted',\n detachedSince: 1_000,\n })\n const afterInterrupted = await runs.listReclaimable({ now, ttlMs })\n expect(afterInterrupted.some((r) => r.runId === interruptedRunId)).toBe(\n false,\n )\n })\n })\n\n describe('interrupts', () => {\n it('creates, resolves, cancels, and lists by thread and run', async (ctx) => {\n const store = resolveStore('interrupts')\n if (!store) return ctx.skip('store not provided')\n\n expect(await store.get('int-missing')).toBeNull()\n\n await store.create({\n interruptId: 'int-1',\n runId: 'run-i',\n threadId: 'thread-i',\n requestedAt: 10,\n payload: { tool: 'search', args: { q: 'x' } },\n })\n await store.create({\n interruptId: 'int-2',\n runId: 'run-i',\n threadId: 'thread-i',\n requestedAt: 20,\n payload: { tool: 'write' },\n })\n await store.create({\n interruptId: 'int-3',\n runId: 'run-other',\n threadId: 'thread-i',\n requestedAt: 30,\n payload: {},\n })\n\n const one = await store.get('int-1')\n expect(one).toMatchObject({\n interruptId: 'int-1',\n runId: 'run-i',\n threadId: 'thread-i',\n status: 'pending',\n requestedAt: 10,\n payload: { tool: 'search', args: { q: 'x' } },\n })\n\n expect(\n (await store.list('thread-i')).map((r) => r.interruptId),\n ).toEqual(['int-1', 'int-2', 'int-3'])\n expect(\n (await store.listByRun('run-i')).map((r) => r.interruptId),\n ).toEqual(['int-1', 'int-2'])\n expect(\n (await store.listPending('thread-i')).map((r) => r.interruptId),\n ).toEqual(['int-1', 'int-2', 'int-3'])\n\n await store.resolve('int-1', { ok: true })\n const resolved = await store.get('int-1')\n expect(resolved?.status).toBe('resolved')\n expect(resolved?.response).toEqual({ ok: true })\n expect(typeof resolved?.resolvedAt).toBe('number')\n\n await store.cancel('int-2')\n const cancelled = await store.get('int-2')\n expect(cancelled?.status).toBe('cancelled')\n expect(typeof cancelled?.resolvedAt).toBe('number')\n\n expect(\n (await store.listPending('thread-i')).map((r) => r.interruptId),\n ).toEqual(['int-3'])\n expect(\n (await store.listPendingByRun('run-i')).map((r) => r.interruptId),\n ).toEqual([])\n })\n\n it('create is insert-if-absent: a duplicate id never clobbers a resolved interrupt', async (ctx) => {\n const store = resolveStore('interrupts')\n if (!store) return ctx.skip('store not provided')\n\n await store.create({\n interruptId: 'int-dup',\n runId: 'run-dup',\n threadId: 'thread-dup',\n requestedAt: 100,\n payload: { attempt: 1 },\n })\n await store.resolve('int-dup', { answer: 42 })\n\n // A second create with the SAME id must be a no-op — not overwrite the\n // now-resolved record back to pending with a fresh payload.\n await store.create({\n interruptId: 'int-dup',\n runId: 'run-dup',\n threadId: 'thread-dup',\n requestedAt: 200,\n payload: { attempt: 2 },\n })\n\n const after = await store.get('int-dup')\n expect(after?.status).toBe('resolved')\n expect(after?.response).toEqual({ answer: 42 })\n expect(after?.payload).toEqual({ attempt: 1 })\n expect(after?.requestedAt).toBe(100)\n })\n\n it('lists ordered by requestedAt ascending even when inserts are out of order', async (ctx) => {\n const store = resolveStore('interrupts')\n if (!store) return ctx.skip('store not provided')\n\n // Insert later-timestamped first so Map insertion order would reverse\n // requestedAt order without an explicit sort.\n await store.create({\n interruptId: 'int-late',\n runId: 'run-order',\n threadId: 'thread-order',\n requestedAt: 300,\n payload: {},\n })\n await store.create({\n interruptId: 'int-early',\n runId: 'run-order',\n threadId: 'thread-order',\n requestedAt: 100,\n payload: {},\n })\n await store.create({\n interruptId: 'int-mid',\n runId: 'run-order',\n threadId: 'thread-order',\n requestedAt: 200,\n payload: {},\n })\n\n expect(\n (await store.list('thread-order')).map((r) => r.interruptId),\n ).toEqual(['int-early', 'int-mid', 'int-late'])\n expect(\n (await store.listPending('thread-order')).map((r) => r.interruptId),\n ).toEqual(['int-early', 'int-mid', 'int-late'])\n expect(\n (await store.listByRun('run-order')).map((r) => r.interruptId),\n ).toEqual(['int-early', 'int-mid', 'int-late'])\n })\n })\n\n describe('generationRuns', () => {\n it('creates, resumes idempotently, updates, and gets', async () => {\n const store = resolveStore('generationRuns')\n if (!store) return\n\n expect(await store.get('gen-missing')).toBeNull()\n\n const created = await store.createOrResume({\n runId: 'gen-1',\n threadId: 'gen-thread-1',\n activity: 'image',\n provider: 'openai',\n model: 'gpt-image-1',\n startedAt: 1000,\n })\n expect(created).toMatchObject({\n runId: 'gen-1',\n activity: 'image',\n provider: 'openai',\n model: 'gpt-image-1',\n status: 'running',\n startedAt: 1000,\n })\n // threadId is the slot the run fills and is required: it must round-trip\n // exactly, since findLatestForThread is the only query that finds a run.\n expect(created.threadId).toBe('gen-thread-1')\n\n // Idempotent: the stored record comes back untouched by the new input.\n const resumed = await store.createOrResume({\n runId: 'gen-1',\n activity: 'video',\n provider: 'google',\n model: 'veo-3',\n startedAt: 9999,\n threadId: 'thread-late',\n })\n expect(resumed).toMatchObject({\n runId: 'gen-1',\n activity: 'image',\n provider: 'openai',\n model: 'gpt-image-1',\n startedAt: 1000,\n })\n // Idempotency covers threadId too: the late scope must not overwrite the\n // one the run was filed under.\n expect(resumed.threadId).toBe('gen-thread-1')\n\n await store.update('gen-1', {\n status: 'completed',\n finishedAt: 2000,\n result: { images: [{ url: 'https://example.com/a.png' }] },\n artifacts: [\n {\n role: 'output',\n artifactId: 'art-1',\n runId: 'gen-1',\n threadId: 'thread-gen',\n name: 'a.png',\n mimeType: 'image/png',\n size: 3,\n createdAt: '2024-01-01T00:00:00.000Z',\n source: {\n activity: 'image',\n path: 'images.0',\n provider: 'openai',\n model: 'gpt-image-1',\n },\n },\n ],\n usage: { promptTokens: 1, completionTokens: 2, totalTokens: 3 },\n })\n const done = await store.get('gen-1')\n expect(done).toMatchObject({\n status: 'completed',\n finishedAt: 2000,\n result: { images: [{ url: 'https://example.com/a.png' }] },\n usage: { promptTokens: 1, completionTokens: 2, totalTokens: 3 },\n })\n expect(done?.artifacts).toHaveLength(1)\n expect(done?.artifacts?.[0]).toMatchObject({\n artifactId: 'art-1',\n mimeType: 'image/png',\n })\n\n await store.update('gen-1', {\n status: 'failed',\n error: { message: 'boom', code: 'provider_error' },\n })\n const failed = await store.get('gen-1')\n expect(failed?.status).toBe('failed')\n expect(failed?.error).toEqual({\n message: 'boom',\n code: 'provider_error',\n })\n\n // Patching a missing run is a no-op (does not throw, does not create).\n await store.update('gen-absent', { status: 'completed' })\n expect(await store.get('gen-absent')).toBeNull()\n })\n\n // `findLatestForThread` is REQUIRED: `reconstructGeneration` hydrates a\n // server-driven client from the stable thread id alone, so a backend that\n // always answers `null` silently restores nothing rather than degrading.\n it('findLatestForThread returns the most recently started linked run', async () => {\n const store = resolveStore('generationRuns')\n if (!store) return\n\n const thread = 'thread-gen-latest'\n expect(await store.findLatestForThread(thread)).toBeNull()\n\n // Insert out of order so insertion order cannot stand in for startedAt.\n await store.createOrResume({\n runId: 'gen-late',\n activity: 'image',\n provider: 'openai',\n model: 'gpt-image-1',\n startedAt: 3000,\n threadId: thread,\n })\n await store.createOrResume({\n runId: 'gen-early',\n activity: 'image',\n provider: 'openai',\n model: 'gpt-image-1',\n startedAt: 1000,\n threadId: thread,\n })\n expect(await store.findLatestForThread(thread)).toMatchObject({\n runId: 'gen-late',\n })\n\n // Another thread's run is never returned, and unlike findActiveRun a\n // TERMINAL run still counts — this is \"the latest\", not \"the active\".\n await store.createOrResume({\n runId: 'gen-other',\n activity: 'image',\n provider: 'openai',\n model: 'gpt-image-1',\n startedAt: 9000,\n threadId: 'thread-gen-other',\n })\n await store.update('gen-late', {\n status: 'completed',\n finishedAt: 3500,\n })\n expect(await store.findLatestForThread(thread)).toMatchObject({\n runId: 'gen-late',\n status: 'completed',\n })\n\n // A run with no thread link is not attributed to any thread.\n expect(await store.findLatestForThread('thread-unlinked')).toBeNull()\n })\n })\n\n describe('artifacts', () => {\n const artifact = (\n overrides: Partial<ArtifactRecord> & Pick<ArtifactRecord, 'artifactId'>,\n ): ArtifactRecord => ({\n runId: 'run-art',\n threadId: 'thread-art',\n name: 'image.png',\n mimeType: 'image/png',\n size: 3,\n createdAt: 100,\n ...overrides,\n })\n\n it('saves as an upsert, gets, and lists by run', async () => {\n const store = resolveStore('artifacts')\n if (!store) return\n\n expect(await store.get('art-missing')).toBeNull()\n expect(await store.list('run-unknown')).toEqual([])\n\n await store.save(\n artifact({\n artifactId: '\\u{10000}',\n blobKey: 'artifacts/run-art/art-a',\n createdAt: 100,\n }),\n )\n await store.save(\n artifact({\n artifactId: '\\u{e000}',\n sourceUrl: 'https://provider.example/expiring.png',\n createdAt: 100,\n }),\n )\n await store.save(\n artifact({ artifactId: 'art-c', runId: 'run-art-other' }),\n )\n\n expect(await store.get('\\u{10000}')).toMatchObject({\n artifactId: '\\u{10000}',\n runId: 'run-art',\n threadId: 'thread-art',\n blobKey: 'artifacts/run-art/art-a',\n name: 'image.png',\n mimeType: 'image/png',\n size: 3,\n createdAt: 100,\n })\n expect(await store.get('\\u{e000}')).toMatchObject({\n sourceUrl: 'https://provider.example/expiring.png',\n })\n\n expect((await store.list('run-art')).map((r) => r.artifactId)).toEqual([\n '\\u{e000}',\n '\\u{10000}',\n ])\n\n // save() is insert-OR-OVERWRITE: re-saving an id corrects the record.\n await store.save(\n artifact({ artifactId: '\\u{10000}', name: 'renamed.png', size: 9 }),\n )\n const updated = await store.get('\\u{10000}')\n expect(updated).toMatchObject({ name: 'renamed.png', size: 9 })\n expect(updated?.blobKey).toBeUndefined()\n expect((await store.list('run-art')).map((r) => r.artifactId)).toEqual([\n '\\u{e000}',\n '\\u{10000}',\n ])\n })\n\n it('lists a thread in deterministic createdAt and artifactId order', async () => {\n const store = resolveStore('artifacts')\n if (!store) return\n\n await store.save(\n artifact({\n artifactId: 'thread-b',\n threadId: 'thread-order',\n createdAt: 2,\n }),\n )\n await store.save(\n artifact({\n artifactId: 'thread-a',\n threadId: 'thread-order',\n createdAt: 2,\n }),\n )\n await store.save(\n artifact({\n artifactId: 'thread-early',\n threadId: 'thread-order',\n createdAt: 1,\n }),\n )\n await store.save(\n artifact({\n artifactId: 'other',\n threadId: 'thread-other',\n createdAt: 0,\n }),\n )\n\n expect(\n (await store.listForThread('thread-order')).map((r) => r.artifactId),\n ).toEqual(['thread-early', 'thread-a', 'thread-b'])\n })\n\n it('orders artifact IDs by UTF-8 bytes after createdAt', async () => {\n const store = resolveStore('artifacts')\n if (!store) return\n\n await store.save(\n artifact({\n artifactId: '\\u{10000}',\n threadId: 'thread-utf8',\n createdAt: 1,\n }),\n )\n await store.save(\n artifact({\n artifactId: '\\u{e000}',\n threadId: 'thread-utf8',\n createdAt: 1,\n }),\n )\n await store.save(\n artifact({ artifactId: 'a', threadId: 'thread-utf8', createdAt: 1 }),\n )\n\n expect(\n (await store.listForThread('thread-utf8')).map((r) => r.artifactId),\n ).toEqual(['a', '\\u{e000}', '\\u{10000}'])\n })\n\n it('deletes one artifact and every artifact for a run', async () => {\n const store = resolveStore('artifacts')\n if (!store) return\n\n await store.save(artifact({ artifactId: 'art-d1', runId: 'run-del' }))\n await store.save(artifact({ artifactId: 'art-d2', runId: 'run-del' }))\n await store.save(\n artifact({ artifactId: 'art-keep', runId: 'run-keep' }),\n )\n\n await store.delete('art-d1')\n expect(await store.get('art-d1')).toBeNull()\n expect((await store.list('run-del')).map((r) => r.artifactId)).toEqual([\n 'art-d2',\n ])\n\n // Deleting an absent id is a silent no-op, mirroring BlobStore.delete.\n await store.delete('art-d1')\n\n await store.deleteForRun('run-del')\n expect(await store.list('run-del')).toEqual([])\n expect(await store.get('art-d2')).toBeNull()\n // Scoped to the run: another run's artifacts survive.\n expect((await store.list('run-keep')).map((r) => r.artifactId)).toEqual(\n ['art-keep'],\n )\n // deleteForRun on a run with no artifacts is a no-op.\n await store.deleteForRun('run-del')\n })\n })\n\n describe('blobs', () => {\n it('round-trips bytes and metadata through put/get/head', async () => {\n const store = resolveStore('blobs')\n if (!store) return\n\n expect(await store.get('blob-missing')).toBeNull()\n expect(await store.head('blob-missing')).toBeNull()\n\n const bytes = new Uint8Array([1, 2, 3, 4])\n const put = await store.put('blob/a', bytes, {\n contentType: 'image/png',\n customMetadata: { runId: 'run-blob' },\n })\n expect(put).toMatchObject({\n key: 'blob/a',\n size: 4,\n contentType: 'image/png',\n customMetadata: { runId: 'run-blob' },\n })\n\n const object = required(await store.get('blob/a'), 'blob/a')\n expect(new Uint8Array(await object.arrayBuffer())).toEqual(bytes)\n expect(object).toMatchObject({\n key: 'blob/a',\n size: 4,\n contentType: 'image/png',\n customMetadata: { runId: 'run-blob' },\n })\n\n const head = await store.head('blob/a')\n expect(head).toMatchObject({ key: 'blob/a', size: 4 })\n\n // A string body encodes as UTF-8 and reads back through text().\n await store.put('blob/text', 'héllo')\n const text = required(await store.get('blob/text'), 'blob/text')\n expect(await text.text()).toBe('héllo')\n\n // An ArrayBuffer body is accepted too.\n await store.put('blob/buffer', new Uint8Array([9, 9]).buffer)\n const buffered = required(await store.get('blob/buffer'), 'blob/buffer')\n expect(new Uint8Array(await buffered.arrayBuffer())).toEqual(\n new Uint8Array([9, 9]),\n )\n })\n\n it('accepts a stream body with no declared length', async () => {\n const store = resolveStore('blobs')\n if (!store) return\n\n // A TransformStream's readable side carries no declared length —\n // exactly what a fetch-based producer hands the store when the origin\n // chunks its reply (or its length can't be trusted). Byte bodies take\n // a different branch in most stores and prove nothing about the\n // streaming path, so this case is the one that keeps a store honest:\n // it must drain the stream, not require a length up front.\n const bytes = new Uint8Array(64 * 1024).map((_, i) => i % 251)\n const source = required(new Response(bytes).body, 'response body')\n const lengthless = source.pipeThrough(\n new TransformStream<Uint8Array, Uint8Array>(),\n )\n const put = await store.put('blob/stream', lengthless, {\n contentType: 'application/octet-stream',\n })\n expect(put.size).toBe(bytes.byteLength)\n\n const object = required(await store.get('blob/stream'), 'blob/stream')\n expect(new Uint8Array(await object.arrayBuffer())).toEqual(bytes)\n expect(object.size).toBe(bytes.byteLength)\n })\n\n it('accepts expectedLength as an advisory hint for a stream body', async () => {\n const store = resolveStore('blobs')\n if (!store) return\n\n // The real-world combination: a length-less stream PLUS the hint —\n // which is what the artifact middleware sends when the origin declared\n // a trustworthy `content-length`. The hint is advisory (a store may\n // use it to pick an upload strategy); the drained bytes stay the\n // record of truth, so `size` must still be the count of what arrived.\n const bytes = new Uint8Array(9 * 1024).map((_, i) => i % 251)\n const source = required(new Response(bytes).body, 'response body')\n const lengthless = source.pipeThrough(\n new TransformStream<Uint8Array, Uint8Array>(),\n )\n const put = await store.put('blob/stream-hint', lengthless, {\n expectedLength: bytes.byteLength,\n })\n expect(put.size).toBe(bytes.byteLength)\n\n const object = required(\n await store.get('blob/stream-hint'),\n 'blob/stream-hint',\n )\n expect(new Uint8Array(await object.arrayBuffer())).toEqual(bytes)\n expect(object.size).toBe(bytes.byteLength)\n })\n\n it('serves a byte range and reports the slice it served', async () => {\n const store = resolveStore('blobs')\n if (!store) return\n\n // Range reads are what a serve route turns into `206` +\n // `Content-Range` — the shape `<video>` seeking is built on. `size`\n // keeps reporting the WHOLE object so the route can finish the\n // `Content-Range` header from the same result.\n const bytes = new Uint8Array(1024).map((_, i) => i % 251)\n await store.put('blob/range', bytes)\n\n const middle = required(\n await store.get('blob/range', { range: { offset: 100, length: 50 } }),\n 'blob/range',\n )\n expect(new Uint8Array(await middle.arrayBuffer())).toEqual(\n bytes.slice(100, 150),\n )\n expect(middle.size).toBe(bytes.byteLength)\n expect(middle.range).toEqual({ offset: 100, length: 50 })\n\n // No `length`: from the offset to the end.\n const tail = required(\n await store.get('blob/range', { range: { offset: 1000 } }),\n 'blob/range',\n )\n expect(new Uint8Array(await tail.arrayBuffer())).toEqual(\n bytes.slice(1000),\n )\n expect(tail.range).toEqual({ offset: 1000, length: 24 })\n\n // A `length` past the end is clamped, not an error: `bytes=1000-2000`\n // against a 1 KiB object is a legal request that serves 24 bytes.\n const clamped = required(\n await store.get('blob/range', {\n range: { offset: 1000, length: 1000 },\n }),\n 'blob/range',\n )\n expect(clamped.range).toEqual({ offset: 1000, length: 24 })\n expect((await clamped.arrayBuffer()).byteLength).toBe(24)\n\n // The streaming accessor carries the same slice as arrayBuffer().\n const streamed = required(\n await store.get('blob/range', { range: { offset: 10, length: 5 } }),\n 'blob/range',\n )\n const body = streamed.body\n if (body) {\n expect(await drainStream(body)).toEqual(bytes.slice(10, 15))\n }\n\n // A whole-object read reports no `range` — a caller that sees one\n // takes the bytes for a slice and would answer `206` for all of them.\n const whole = required(await store.get('blob/range'), 'blob/range')\n expect(whole.range).toBeUndefined()\n })\n\n it('overwrites an existing key and deletes silently', async () => {\n const store = resolveStore('blobs')\n if (!store) return\n\n const first = await store.put('blob/over', new Uint8Array([1]), {\n contentType: 'text/plain',\n customMetadata: { v: '1' },\n })\n const second = await store.put('blob/over', new Uint8Array([2, 2, 2]), {\n contentType: 'application/octet-stream',\n customMetadata: { v: '2' },\n })\n\n expect(second).toMatchObject({\n size: 3,\n contentType: 'application/octet-stream',\n customMetadata: { v: '2' },\n })\n // When a backend exposes etags at all, new bytes get a new one.\n if (first.etag !== undefined && second.etag !== undefined) {\n expect(second.etag).not.toBe(first.etag)\n }\n const after = required(await store.get('blob/over'), 'blob/over')\n expect(new Uint8Array(await after.arrayBuffer())).toEqual(\n new Uint8Array([2, 2, 2]),\n )\n\n await store.delete('blob/over')\n expect(await store.get('blob/over')).toBeNull()\n expect(await store.head('blob/over')).toBeNull()\n // Deleting an absent key is a no-op, not an error.\n await store.delete('blob/over')\n })\n\n it('lists by literal prefix in ascending key order', async () => {\n const store = resolveStore('blobs')\n if (!store) return\n\n // `_` and `%` are LIKE metacharacters: a SQL backend that forgets to\n // escape them would match `list-x/…` here. And SQLite's LIKE is\n // case-insensitive for ASCII, so `LIST_/` must not match either.\n await store.put('list_/b', new Uint8Array([2]))\n await store.put('list_/a', new Uint8Array([1]))\n await store.put('list_/c', new Uint8Array([3]))\n await store.put('list-x/d', new Uint8Array([4]))\n await store.put('LIST_/e', new Uint8Array([5]))\n\n const page = await store.list({ prefix: 'list_/' })\n expect(page.objects.map((o) => o.key)).toEqual([\n 'list_/a',\n 'list_/b',\n 'list_/c',\n ])\n expect(page.truncated).toBeFalsy()\n\n expect(\n (await store.list({ prefix: 'list_/nothing-here' })).objects,\n ).toEqual([])\n })\n\n it('pages with a cursor and returns an empty page for limit 0', async () => {\n const store = resolveStore('blobs')\n if (!store) return\n\n for (const key of ['page/a', 'page/b', 'page/c', 'page/d', 'page/e']) {\n await store.put(key, new Uint8Array([1]))\n }\n\n const empty = await store.list({ prefix: 'page/', limit: 0 })\n expect(empty.objects).toEqual([])\n expect(empty.truncated).toBeFalsy()\n expect(empty.cursor).toBeUndefined()\n\n // Walk every key exactly once: each page's cursor resumes strictly\n // after the last key it returned.\n const seen: Array<string> = []\n let cursor: string | undefined\n for (let guard = 0; guard < 10; guard++) {\n const result = await store.list({\n prefix: 'page/',\n limit: 2,\n ...(cursor !== undefined ? { cursor } : {}),\n })\n seen.push(...result.objects.map((o) => o.key))\n if (!result.truncated) break\n expect(result.cursor).toBe(result.objects.at(-1)?.key)\n cursor = result.cursor\n }\n expect(seen).toEqual(['page/a', 'page/b', 'page/c', 'page/d', 'page/e'])\n\n // An exact-fit limit is not truncated: no key matches beyond the page.\n const exact = await store.list({ prefix: 'page/', limit: 5 })\n expect(exact.objects.map((o) => o.key)).toEqual(seen)\n expect(exact.truncated).toBeFalsy()\n })\n })\n\n describe('metadata', () => {\n it('sets, gets, namespaces, and deletes without composite-key collisions', async (ctx) => {\n const store = resolveStore('metadata')\n if (!store) return ctx.skip('store not provided')\n\n expect(await store.get('scope-a', 'k')).toBeNull()\n\n await store.set('scope-a', 'k', { n: 1 })\n await store.set('scope-b', 'k', { n: 2 })\n expect(await store.get('scope-a', 'k')).toEqual({ n: 1 })\n expect(await store.get('scope-b', 'k')).toEqual({ n: 2 })\n\n await store.set('scope-a', 'k', { n: 3 })\n expect(await store.get('scope-a', 'k')).toEqual({ n: 3 })\n\n await store.delete('scope-a', 'k')\n expect(await store.get('scope-a', 'k')).toBeNull()\n // Delete is namespaced: scope-b untouched.\n expect(await store.get('scope-b', 'k')).toEqual({ n: 2 })\n\n // Composite identity must not alias across colon-containing parts.\n // ('a:b','c') and ('a','b:c') are distinct pairs.\n await store.set('a:b', 'c', 'left')\n await store.set('a', 'b:c', 'right')\n expect(await store.get('a:b', 'c')).toBe('left')\n expect(await store.get('a', 'b:c')).toBe('right')\n await store.delete('a:b', 'c')\n expect(await store.get('a:b', 'c')).toBeNull()\n expect(await store.get('a', 'b:c')).toBe('right')\n })\n })\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"conformance.js","names":[],"sources":["../../../src/testkit/conformance.ts"],"sourcesContent":["/**\n * Shared conformance suite for the `AIPersistence` store contract.\n *\n * Every backend runs this identical suite — the in-memory reference store and\n * every adapter you write against your own database — so that schema drift or\n * an implementation gap fails immediately. It exercises every method of every\n * store the persistence exposes and is the authoritative compatibility gate for\n * the store interfaces in `../types.ts`.\n *\n * Covers all seven stores: the four chat state stores (`messages`, `runs`,\n * `interrupts`, `metadata`) and the three generation stores (`generationRuns`,\n * `artifacts`, `blobs`). Locks are not part of this suite — they are a separate\n * coordination concern (`LockStore` + `withLocks`), not a store.\n *\n * SKIPPING (declare or fail): a backend that deliberately omits a store must\n * declare it in `options.skip`, and one that omits an OPTIONAL store method\n * must declare it in `options.skipMethods`. Anything absent and not declared\n * fails the suite loudly, and anything declared absent is reported by vitest as\n * a SKIPPED case, never as a pass. Silent gaps are not allowed: a case that did\n * not run must never be indistinguishable from one that did. A chat-only\n * adapter therefore passes `skip: ['generationRuns', 'artifacts', 'blobs']`,\n * and a generation-only one skips the four state stores. `listByParentRun` is\n * the exception. Subagent support is opt-in, its cases skip on their own, and\n * a `'runs.listByParentRun'` entry is accepted but has no effect.\n *\n * NOT COVERED HERE: the four durable-run fields on `RunRecord` (`sandboxKey`,\n * `detachedSince`, `cancelRequested`, `driverEpoch`). They exist for durable\n * sandboxed runs, a chat app never writes them, and requiring them here made every\n * backend implement four columns and an omitted-vs-explicit-undefined rule it had no\n * use for. They are proven by `runDurableRunFieldsConformance` from\n * `@tanstack/ai-sandbox/testkit`, next to the takeover and reaper suites that consume\n * them. A backend that never runs sandboxes can leave the columns out.\n *\n * RESERVED RUN-ID PREFIX: `rc-` belongs to the `listReclaimable` case, which\n * filters the method's (not thread-scoped) result down to `rc-` ids before an\n * exact-set assertion. A new case in `describe('runs')` must NOT seed a run id\n * starting with `rc-`, or it silently changes that expected set.\n */\nimport { beforeAll, describe, expect, it } from 'vitest'\nimport type { ModelMessage } from '@tanstack/ai'\nimport type {\n AIPersistence,\n AIPersistenceStores,\n ArtifactRecord,\n RunStore,\n} from '../types'\n\ntype MakePersistence = () => Promise<AIPersistence> | AIPersistence\n\n/**\n * Methods that are optional on the `RunStore` contract.\n *\n * `findActiveRun` is deliberately NOT here: it is REQUIRED, per the evolution\n * policy in `../types.ts`. It was optional for one release cycle and silently\n * disabled reconnect on every backend that had not caught up.\n */\ntype OptionalRunStoreMethod =\n | 'listByThread'\n | 'listByParentRun'\n | 'listReclaimable'\n\n/** Dotted `store.method` key a backend passes to declare an omitted method. */\nexport type PersistenceConformanceMethodKey = `runs.${OptionalRunStoreMethod}`\n\n/**\n * Unwrap a value the store contract says must be present. Fails the test with a\n * readable message instead of a non-null assertion (banned in this package) or\n * an early `return` that would pass silently.\n */\nfunction required<TValue>(\n value: TValue | null | undefined,\n what: string,\n): TValue {\n if (value == null) {\n throw new Error(`AIPersistence conformance: expected ${what} to exist`)\n }\n return value\n}\n\n/** Read a `BlobObject.body` to completion as one contiguous buffer. */\nasync function drainStream(\n stream: ReadableStream<Uint8Array>,\n): Promise<Uint8Array> {\n const chunks: Array<Uint8Array> = []\n let total = 0\n const reader = stream.getReader()\n for (;;) {\n const { value, done } = await reader.read()\n if (done) break\n chunks.push(value)\n total += value.byteLength\n }\n const bytes = new Uint8Array(total)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n return bytes\n}\n\nexport interface PersistenceConformanceOptions {\n /**\n * Store keys this backend intentionally does not provide. Any store that is\n * absent from the persistence and NOT listed here fails the suite, so a\n * dropped/misconfigured store can never pass silently.\n */\n skip?: Array<keyof AIPersistenceStores>\n /**\n * OPTIONAL store methods this backend intentionally does not implement, as\n * `'runs.listByThread'` and friends. A method that is absent and NOT listed\n * here fails the suite; a listed one is reported as a skipped case.\n * `listByParentRun` is the exception. Subagent support is opt-in, its cases\n * skip on their own, and a `'runs.listByParentRun'` entry is accepted but\n * has no effect.\n */\n skipMethods?: Array<PersistenceConformanceMethodKey>\n}\n\n/**\n * Register a Vitest suite that validates `makePersistence()` against the full\n * `AIPersistence` contract — every store it provides, and none it declares\n * skipped.\n */\nexport function runPersistenceConformance(\n name: string,\n makePersistence: MakePersistence,\n options?: PersistenceConformanceOptions,\n): void {\n const skip = new Set<keyof AIPersistenceStores>(options?.skip ?? [])\n const skipMethods = new Set<PersistenceConformanceMethodKey>(\n options?.skipMethods ?? [],\n )\n\n describe(`AIPersistence conformance: ${name}`, () => {\n let persistence: AIPersistence\n\n beforeAll(async () => {\n persistence = await makePersistence()\n })\n\n /**\n * Return the store for `key`, or `null` when the backend intentionally\n * skips it. Throws (failing the test) when a store is missing but was not\n * declared in `options.skip`.\n */\n function resolveStore<TKey extends keyof AIPersistenceStores>(\n key: TKey,\n ): NonNullable<AIPersistenceStores[TKey]> | null {\n const store = persistence.stores[key]\n if (store) return store\n if (skip.has(key)) return null\n throw new Error(\n `AIPersistence conformance: store '${key}' is missing. ` +\n `Provide it, or pass { skip: ['${key}'] } if the omission is intentional.`,\n )\n }\n\n /**\n * Narrow `runs` to a store that definitely implements the optional method\n * `methodName`, so the case can call it without a non-null assertion.\n *\n * Returns `false` only when the omission was declared in\n * `options.skipMethods` (the caller then reports a skip). An undeclared\n * omission throws, mirroring `resolveStore`: a case that cannot run must\n * never be reported as a pass.\n */\n function hasRunsMethod<TName extends OptionalRunStoreMethod>(\n runs: RunStore,\n methodName: TName,\n ): runs is RunStore & Required<Pick<RunStore, TName>> {\n if (runs[methodName]) return true\n const key: PersistenceConformanceMethodKey = `runs.${methodName}`\n if (skipMethods.has(key)) return false\n throw new Error(\n `AIPersistence conformance: optional method '${key}' is not implemented. ` +\n `Implement it, or pass { skipMethods: ['${key}'] } if the omission is intentional.`,\n )\n }\n\n // Subagent support is opt-in. A store without `listByParentRun` skips the\n // subagent checks (vitest reports them as skipped) and needs no\n // `skipMethods` entry.\n function supportsSubagents(\n runs: RunStore,\n ): runs is RunStore & Required<Pick<RunStore, 'listByParentRun'>> {\n return typeof runs.listByParentRun === 'function'\n }\n\n describe('messages', () => {\n // One-argument loadThread is the full-thread contract. Paging\n // (`limit` / `before`) is an optional hint; this suite does not require it.\n it('round-trips a thread and returns [] for unknown threads', async (ctx) => {\n const store = resolveStore('messages')\n if (!store) return ctx.skip('store not provided')\n\n expect(await store.loadThread('thread-unknown')).toEqual([])\n\n await store.saveThread('thread-msg', [\n { role: 'user', content: 'hi' },\n { role: 'assistant', content: 'hello' },\n ])\n const loaded = await store.loadThread('thread-msg')\n expect(Array.isArray(loaded)).toBe(true)\n expect(loaded).toEqual([\n { role: 'user', content: 'hi' },\n { role: 'assistant', content: 'hello' },\n ])\n\n // Overwrites, not appends.\n await store.saveThread('thread-msg', [\n { role: 'user', content: 'redo' },\n ])\n expect(await store.loadThread('thread-msg')).toEqual([\n { role: 'user', content: 'redo' },\n ])\n })\n\n it('round-trips rich message shapes with deep equality', async (ctx) => {\n const store = resolveStore('messages')\n if (!store) return ctx.skip('store not provided')\n\n const rich: Array<ModelMessage> = [\n { role: 'user', content: 'plain string' },\n {\n // Tool-call message with JSON arguments.\n role: 'assistant',\n content: '',\n toolCalls: [\n {\n id: 'call-1',\n type: 'function',\n function: {\n name: 'search',\n arguments: '{\"query\":\"weather in Paris\"}',\n },\n },\n ],\n },\n {\n // Tool result message.\n role: 'tool',\n content: '{\"temperature\":21,\"unit\":\"C\"}',\n toolCallId: 'call-1',\n },\n {\n // Multi-part content: text + image reference.\n role: 'user',\n content: [\n { type: 'text', content: 'What is in this image?' },\n {\n type: 'image',\n source: {\n type: 'url',\n value: 'https://example.com/cat.png',\n mimeType: 'image/png',\n },\n },\n ],\n },\n {\n // Reasoning / thinking part.\n role: 'assistant',\n content: 'Here is my answer.',\n thinking: [\n {\n content: 'The user is asking about the image.',\n signature: 'sig-1',\n },\n ],\n },\n ]\n\n await store.saveThread('thread-rich', rich)\n expect(await store.loadThread('thread-rich')).toEqual(rich)\n })\n })\n\n describe('runs', () => {\n it('creates, resumes idempotently, updates, and gets', async (ctx) => {\n const store = resolveStore('runs')\n if (!store) return ctx.skip('store not provided')\n\n expect(await store.get('run-missing')).toBeNull()\n\n const created = await store.createOrResume({\n runId: 'run-1',\n threadId: 'thread-1',\n startedAt: 1000,\n })\n expect(created).toMatchObject({\n runId: 'run-1',\n threadId: 'thread-1',\n status: 'running',\n startedAt: 1000,\n })\n\n // createOrResume is idempotent: returns the existing record unchanged.\n const resumed = await store.createOrResume({\n runId: 'run-1',\n threadId: 'thread-different',\n startedAt: 9999,\n })\n expect(resumed).toMatchObject({\n runId: 'run-1',\n threadId: 'thread-1',\n startedAt: 1000,\n })\n\n await store.update('run-1', {\n status: 'completed',\n finishedAt: 2000,\n usage: { promptTokens: 3, completionTokens: 4, totalTokens: 7 },\n })\n const done = await store.get('run-1')\n expect(done).toMatchObject({\n runId: 'run-1',\n status: 'completed',\n finishedAt: 2000,\n usage: { promptTokens: 3, completionTokens: 4, totalTokens: 7 },\n })\n\n const resumedAfterUpdate = await store.createOrResume({\n runId: 'run-1',\n threadId: 'thread-different',\n startedAt: 9999,\n })\n expect(resumedAfterUpdate).toEqual(done)\n\n // `error` is a structured RunError: the prose `message` plus the\n // optional machine-branchable `code`. Both must survive the round-trip,\n // so a backend that flattens the record to a bare string fails here.\n await store.update('run-1', {\n status: 'failed',\n error: { message: 'boom', code: 'provider_overloaded' },\n })\n const failed = await store.get('run-1')\n expect(failed?.status).toBe('failed')\n expect(failed?.error).toEqual({\n message: 'boom',\n code: 'provider_overloaded',\n })\n\n // Updating a missing run is a no-op (does not throw, does not create).\n await store.update('run-absent', { status: 'completed' })\n expect(await store.get('run-absent')).toBeNull()\n })\n\n // The idempotency invariant has teeth precisely where it is dangerous:\n // resuming a run that already FINISHED must not resurrect it. An adapter\n // written as `INSERT ... ON CONFLICT DO UPDATE SET status='running'`\n // looks correct on a still-running record and silently revives dead ones,\n // after which `findActiveRun` hands clients a run that will never emit\n // again. Assert the terminal status and `finishedAt` both survive.\n it('createOrResume never resurrects a finished run', async (ctx) => {\n const store = resolveStore('runs')\n if (!store) return ctx.skip('store not provided')\n\n await store.createOrResume({\n runId: 'nc-1',\n threadId: 'nc-t',\n startedAt: 10,\n })\n await store.update('nc-1', { status: 'completed', finishedAt: 20 })\n\n // Resume with a DIFFERENT status/startedAt: both must be ignored.\n const resumed = await store.createOrResume({\n runId: 'nc-1',\n threadId: 'nc-t',\n startedAt: 999,\n status: 'running',\n })\n expect(resumed).toMatchObject({\n runId: 'nc-1',\n status: 'completed',\n startedAt: 10,\n finishedAt: 20,\n })\n\n // And the stored record itself was not rewritten either.\n expect(await store.get('nc-1')).toMatchObject({\n status: 'completed',\n startedAt: 10,\n finishedAt: 20,\n })\n })\n\n // `parentRunId`, `subagentRunId`, and `name` travel with createOrResume.\n // A parent row omits them. A child row keeps the values from the first\n // insert. A later createOrResume for that runId must not overwrite them.\n // Only a store with subagent support (`listByParentRun`) must keep them.\n it('round-trips subagent link fields and ignores them on resume', async (ctx) => {\n const store = resolveStore('runs')\n if (!store) return ctx.skip('store not provided')\n if (!supportsSubagents(store)) {\n return ctx.skip('runs.listByParentRun not implemented')\n }\n\n const parent = await store.createOrResume({\n runId: 'lp-parent',\n threadId: 'lp-thread',\n startedAt: 1,\n })\n expect(parent.parentRunId).toBeUndefined()\n expect(parent.subagentRunId).toBeUndefined()\n expect(parent.name).toBeUndefined()\n\n const child = await store.createOrResume({\n runId: 'lp-child',\n threadId: 'subagent:lp-child',\n startedAt: 2,\n parentRunId: 'lp-parent',\n subagentRunId: 'lp-sub',\n name: 'researcher',\n })\n expect(child).toMatchObject({\n runId: 'lp-child',\n threadId: 'subagent:lp-child',\n startedAt: 2,\n parentRunId: 'lp-parent',\n subagentRunId: 'lp-sub',\n name: 'researcher',\n })\n\n const resumed = await store.createOrResume({\n runId: 'lp-child',\n threadId: 'lp-other-thread',\n startedAt: 99,\n parentRunId: 'lp-other-parent',\n subagentRunId: 'lp-other-sub',\n name: 'writer',\n })\n expect(resumed).toMatchObject({\n runId: 'lp-child',\n threadId: 'subagent:lp-child',\n startedAt: 2,\n parentRunId: 'lp-parent',\n subagentRunId: 'lp-sub',\n name: 'researcher',\n })\n expect(await store.get('lp-child')).toMatchObject({\n parentRunId: 'lp-parent',\n subagentRunId: 'lp-sub',\n name: 'researcher',\n })\n })\n\n it('findActiveRun returns the most recent running run for a thread', async (ctx) => {\n const store = resolveStore('runs')\n if (!store) return ctx.skip('store not provided')\n\n const thread = 'thread-active'\n expect(await store.findActiveRun(thread)).toBeNull()\n\n await store.createOrResume({\n runId: 'active-1',\n threadId: thread,\n startedAt: 1000,\n })\n await store.createOrResume({\n runId: 'active-2',\n threadId: thread,\n startedAt: 2000,\n })\n // Most-recent running run wins.\n expect(await store.findActiveRun(thread)).toMatchObject({\n runId: 'active-2',\n status: 'running',\n })\n\n // A different thread's running run is not returned.\n await store.createOrResume({\n runId: 'other-1',\n threadId: 'thread-other',\n startedAt: 3000,\n })\n expect(await store.findActiveRun(thread)).toMatchObject({\n runId: 'active-2',\n })\n\n // Once the newest finishes, the older running run becomes active.\n await store.update('active-2', {\n status: 'completed',\n finishedAt: 2500,\n })\n expect(await store.findActiveRun(thread)).toMatchObject({\n runId: 'active-1',\n status: 'running',\n })\n\n // With none running, it is null.\n await store.update('active-1', {\n status: 'completed',\n finishedAt: 1500,\n })\n expect(await store.findActiveRun(thread)).toBeNull()\n })\n\n // `listByThread` is optional on the RunStore contract; a declared omission\n // is reported as skipped and an undeclared one fails. Any backend that has\n // it must return that thread's runs ordered ascending by `startedAt`.\n it('lists runs by thread when supported', async (ctx) => {\n const runs = resolveStore('runs')\n if (!runs) return ctx.skip('store not provided')\n if (!hasRunsMethod(runs, 'listByThread')) {\n return ctx.skip('runs.listByThread not implemented')\n }\n\n await runs.createOrResume({\n runId: 'lt-b',\n threadId: 'lt',\n startedAt: 2,\n })\n await runs.createOrResume({\n runId: 'lt-a',\n threadId: 'lt',\n startedAt: 1,\n })\n const listed = await runs.listByThread('lt')\n expect(listed.map((r) => r.runId)).toEqual(['lt-a', 'lt-b'])\n })\n\n // `listByParentRun` is optional and is skipped when absent. A store that\n // has it returns only that parent's children, oldest `startedAt` first,\n // and [] for an unknown parent.\n it('lists child runs by parent when supported', async (ctx) => {\n const runs = resolveStore('runs')\n if (!runs) return ctx.skip('store not provided')\n if (!supportsSubagents(runs)) {\n return ctx.skip('runs.listByParentRun not implemented')\n }\n\n await runs.createOrResume({\n runId: 'lbp-parent',\n threadId: 'lbp-thread',\n startedAt: 1,\n })\n await runs.createOrResume({\n runId: 'lbp-b',\n threadId: 'subagent:lbp-b',\n startedAt: 20,\n parentRunId: 'lbp-parent',\n subagentRunId: 'lbp-sub-b',\n name: 'seo',\n })\n await runs.createOrResume({\n runId: 'lbp-a',\n threadId: 'subagent:lbp-a',\n startedAt: 10,\n parentRunId: 'lbp-parent',\n subagentRunId: 'lbp-sub-a',\n name: 'researcher',\n })\n await runs.createOrResume({\n runId: 'lbp-other',\n threadId: 'subagent:lbp-other',\n startedAt: 5,\n parentRunId: 'lbp-elsewhere',\n subagentRunId: 'lbp-sub-other',\n name: 'writer',\n })\n\n const listed = await runs.listByParentRun('lbp-parent')\n expect(listed.map((run) => run.runId)).toEqual(['lbp-a', 'lbp-b'])\n expect(listed.map((run) => run.name)).toEqual(['researcher', 'seo'])\n expect(await runs.listByParentRun('lbp-missing')).toEqual([])\n })\n\n // `listReclaimable` is optional on the RunStore contract; a declared\n // omission is reported as skipped and an undeclared one fails. Any\n // backend that has it must\n // surface only runs where ALL THREE hold: status === 'running',\n // detachedSince is set, and detachedSince <= now - ttlMs (inclusive\n // cutoff). Each negative fixture below pins one of those conditions so\n // a backend that drops any single check (e.g. \"return every run\", or\n // \"ignore status\", or \"ignore detachedSince\") fails this case. Do not\n // simplify these away to a bare `toContain` — that is exactly the\n // weakness this case was strengthened to catch.\n it('lists reclaimable detached runs when supported', async (ctx) => {\n const runs = resolveStore('runs')\n if (!runs) return ctx.skip('store not provided')\n if (!hasRunsMethod(runs, 'listReclaimable')) {\n return ctx.skip('runs.listReclaimable not implemented')\n }\n\n const now = 10_000\n const ttlMs = 5_000\n const cutoff = now - ttlMs // 5_000\n\n // Positive: running, detached well past the cutoff.\n await runs.createOrResume({\n runId: 'rc-included',\n threadId: 'rc-t',\n startedAt: 1,\n })\n await runs.update('rc-included', { detachedSince: 1_000 })\n\n // Positive boundary: detachedSince exactly equals the cutoff. Pins\n // the `<=` (inclusive) semantics — a backend that uses `<` instead\n // would wrongly exclude this run.\n await runs.createOrResume({\n runId: 'rc-boundary',\n threadId: 'rc-t',\n startedAt: 1,\n })\n await runs.update('rc-boundary', { detachedSince: cutoff })\n\n // Negative: still running, but detached AFTER the cutoff (not yet\n // abandoned long enough). Pins the `<= cutoff` comparison — a\n // backend that returns every detached run regardless of how recent\n // would wrongly include this one.\n await runs.createOrResume({\n runId: 'rc-too-recent',\n threadId: 'rc-t',\n startedAt: 1,\n })\n await runs.update('rc-too-recent', { detachedSince: cutoff + 1 })\n\n // Negative: detached past the cutoff, but no longer running (already\n // completed). Pins the `status === 'running'` check — a backend\n // that ignores status would wrongly include this one.\n await runs.createOrResume({\n runId: 'rc-completed',\n threadId: 'rc-t',\n startedAt: 1,\n })\n await runs.update('rc-completed', {\n detachedSince: 1_000,\n status: 'completed',\n finishedAt: 2_000,\n })\n\n // Negative: running, but never detached at all. Pins the\n // `detachedSince !== undefined` check — a backend that treats a\n // missing `detachedSince` as \"always reclaimable\" would wrongly\n // include this one.\n await runs.createOrResume({\n runId: 'rc-never-detached',\n threadId: 'rc-t',\n startedAt: 1,\n })\n\n const reclaimable = await runs.listReclaimable({ now, ttlMs })\n\n // Scope the assertion to ids seeded by this case: `listReclaimable`\n // is not thread-scoped, so it also sees `'running'` runs seeded by\n // sibling cases in this shared-store `describe('runs', ...)` block\n // (e.g. `other-1`, `lt-a`, `lt-b`). Those all lack `detachedSince`,\n // so a correct implementation already excludes them — but filtering\n // here keeps this assertion from depending on that fact holding for\n // every other case forever. Ordering is not part of this method's\n // contract, so sort before an exact-set comparison.\n const ourIds = reclaimable\n .map((r) => r.runId)\n .filter((id) => id.startsWith('rc-'))\n .sort()\n expect(ourIds).toEqual(['rc-boundary', 'rc-included'])\n\n // The four assertions below are scoped by exact runId (never by the\n // `rc-` exact-set comparison above), so each uses its own randomUUID\n // fixture and cannot perturb the fixed-set assertion just made.\n\n // (1) `ttlMs: 0` pins the cutoff as inclusive: a run detached at\n // exactly `now` (cutoff === now) must still come back. A backend\n // using strict `<` instead of `<=` would silently never reclaim a\n // run detached exactly at the boundary.\n const zeroTtlRunId = `rc-${crypto.randomUUID()}`\n await runs.createOrResume({\n runId: zeroTtlRunId,\n threadId: 'rc-t',\n startedAt: 1,\n })\n await runs.update(zeroTtlRunId, { detachedSince: now })\n const zeroTtlReclaimable = await runs.listReclaimable({\n now,\n ttlMs: 0,\n })\n expect(\n zeroTtlReclaimable.find((r) => r.runId === zeroTtlRunId)\n ?.detachedSince,\n ).toBe(now)\n\n // (2) Re-attaching — `update(runId, { detachedSince: undefined })`\n // — must drop the run out of the list. This is the most important\n // assertion in this case: a SQL `SET`-clause builder that filters\n // `undefined` out of the patch (`'field' in patch` instead of\n // `patch.field !== undefined`) keeps the old `detachedSince`, so a\n // run a user has actively re-attached to still looks detached — and\n // the reaper then cancels a run someone is watching.\n const reattachedRunId = `rc-${crypto.randomUUID()}`\n await runs.createOrResume({\n runId: reattachedRunId,\n threadId: 'rc-t',\n startedAt: 1,\n })\n await runs.update(reattachedRunId, { detachedSince: 1_000 })\n await runs.update(reattachedRunId, { detachedSince: undefined })\n const afterReattach = await runs.listReclaimable({ now, ttlMs })\n expect(afterReattach.some((r) => r.runId === reattachedRunId)).toBe(\n false,\n )\n\n // (3) No terminal status (`completed` / `failed` / `aborted`) ever\n // appears, whatever its `detachedSince`.\n const terminalStatuses = ['completed', 'failed', 'aborted'] as const\n const terminalRunIds = await Promise.all(\n terminalStatuses.map(async (status) => {\n const runId = `rc-${crypto.randomUUID()}`\n await runs.createOrResume({ runId, threadId: 'rc-t', startedAt: 1 })\n await runs.update(runId, {\n status,\n detachedSince: 1_000,\n finishedAt: 2_000,\n })\n return runId\n }),\n )\n const afterTerminal = await runs.listReclaimable({ now, ttlMs })\n expect(\n afterTerminal.some((r) => terminalRunIds.includes(r.runId)),\n ).toBe(false)\n\n // (4) `'interrupted'` does not appear. The documented predicate is\n // `status === 'running'`; an interrupted run is a human-in-the-loop\n // pause that interrupt-resume continues, not abandoned work a reaper\n // should tear down.\n const interruptedRunId = `rc-${crypto.randomUUID()}`\n await runs.createOrResume({\n runId: interruptedRunId,\n threadId: 'rc-t',\n startedAt: 1,\n })\n await runs.update(interruptedRunId, {\n status: 'interrupted',\n detachedSince: 1_000,\n })\n const afterInterrupted = await runs.listReclaimable({ now, ttlMs })\n expect(afterInterrupted.some((r) => r.runId === interruptedRunId)).toBe(\n false,\n )\n })\n })\n\n describe('interrupts', () => {\n it('creates, resolves, cancels, and lists by thread and run', async (ctx) => {\n const store = resolveStore('interrupts')\n if (!store) return ctx.skip('store not provided')\n\n expect(await store.get('int-missing')).toBeNull()\n\n await store.create({\n interruptId: 'int-1',\n runId: 'run-i',\n threadId: 'thread-i',\n requestedAt: 10,\n payload: { tool: 'search', args: { q: 'x' } },\n })\n await store.create({\n interruptId: 'int-2',\n runId: 'run-i',\n threadId: 'thread-i',\n requestedAt: 20,\n payload: { tool: 'write' },\n })\n await store.create({\n interruptId: 'int-3',\n runId: 'run-other',\n threadId: 'thread-i',\n requestedAt: 30,\n payload: {},\n })\n\n const one = await store.get('int-1')\n expect(one).toMatchObject({\n interruptId: 'int-1',\n runId: 'run-i',\n threadId: 'thread-i',\n status: 'pending',\n requestedAt: 10,\n payload: { tool: 'search', args: { q: 'x' } },\n })\n\n expect(\n (await store.list('thread-i')).map((r) => r.interruptId),\n ).toEqual(['int-1', 'int-2', 'int-3'])\n expect(\n (await store.listByRun('run-i')).map((r) => r.interruptId),\n ).toEqual(['int-1', 'int-2'])\n expect(\n (await store.listPending('thread-i')).map((r) => r.interruptId),\n ).toEqual(['int-1', 'int-2', 'int-3'])\n\n await store.resolve('int-1', { ok: true })\n const resolved = await store.get('int-1')\n expect(resolved?.status).toBe('resolved')\n expect(resolved?.response).toEqual({ ok: true })\n expect(typeof resolved?.resolvedAt).toBe('number')\n\n await store.cancel('int-2')\n const cancelled = await store.get('int-2')\n expect(cancelled?.status).toBe('cancelled')\n expect(typeof cancelled?.resolvedAt).toBe('number')\n\n expect(\n (await store.listPending('thread-i')).map((r) => r.interruptId),\n ).toEqual(['int-3'])\n expect(\n (await store.listPendingByRun('run-i')).map((r) => r.interruptId),\n ).toEqual([])\n })\n\n it('create is insert-if-absent: a duplicate id never clobbers a resolved interrupt', async (ctx) => {\n const store = resolveStore('interrupts')\n if (!store) return ctx.skip('store not provided')\n\n await store.create({\n interruptId: 'int-dup',\n runId: 'run-dup',\n threadId: 'thread-dup',\n requestedAt: 100,\n payload: { attempt: 1 },\n })\n await store.resolve('int-dup', { answer: 42 })\n\n // A second create with the SAME id must be a no-op — not overwrite the\n // now-resolved record back to pending with a fresh payload.\n await store.create({\n interruptId: 'int-dup',\n runId: 'run-dup',\n threadId: 'thread-dup',\n requestedAt: 200,\n payload: { attempt: 2 },\n })\n\n const after = await store.get('int-dup')\n expect(after?.status).toBe('resolved')\n expect(after?.response).toEqual({ answer: 42 })\n expect(after?.payload).toEqual({ attempt: 1 })\n expect(after?.requestedAt).toBe(100)\n })\n\n it('lists ordered by requestedAt ascending even when inserts are out of order', async (ctx) => {\n const store = resolveStore('interrupts')\n if (!store) return ctx.skip('store not provided')\n\n // Insert later-timestamped first so Map insertion order would reverse\n // requestedAt order without an explicit sort.\n await store.create({\n interruptId: 'int-late',\n runId: 'run-order',\n threadId: 'thread-order',\n requestedAt: 300,\n payload: {},\n })\n await store.create({\n interruptId: 'int-early',\n runId: 'run-order',\n threadId: 'thread-order',\n requestedAt: 100,\n payload: {},\n })\n await store.create({\n interruptId: 'int-mid',\n runId: 'run-order',\n threadId: 'thread-order',\n requestedAt: 200,\n payload: {},\n })\n\n expect(\n (await store.list('thread-order')).map((r) => r.interruptId),\n ).toEqual(['int-early', 'int-mid', 'int-late'])\n expect(\n (await store.listPending('thread-order')).map((r) => r.interruptId),\n ).toEqual(['int-early', 'int-mid', 'int-late'])\n expect(\n (await store.listByRun('run-order')).map((r) => r.interruptId),\n ).toEqual(['int-early', 'int-mid', 'int-late'])\n })\n })\n\n describe('generationRuns', () => {\n it('creates, resumes idempotently, updates, and gets', async () => {\n const store = resolveStore('generationRuns')\n if (!store) return\n\n expect(await store.get('gen-missing')).toBeNull()\n\n const created = await store.createOrResume({\n runId: 'gen-1',\n threadId: 'gen-thread-1',\n activity: 'image',\n provider: 'openai',\n model: 'gpt-image-1',\n startedAt: 1000,\n })\n expect(created).toMatchObject({\n runId: 'gen-1',\n activity: 'image',\n provider: 'openai',\n model: 'gpt-image-1',\n status: 'running',\n startedAt: 1000,\n })\n // threadId is the slot the run fills and is required: it must round-trip\n // exactly, since findLatestForThread is the only query that finds a run.\n expect(created.threadId).toBe('gen-thread-1')\n\n // Idempotent: the stored record comes back untouched by the new input.\n const resumed = await store.createOrResume({\n runId: 'gen-1',\n activity: 'video',\n provider: 'google',\n model: 'veo-3',\n startedAt: 9999,\n threadId: 'thread-late',\n })\n expect(resumed).toMatchObject({\n runId: 'gen-1',\n activity: 'image',\n provider: 'openai',\n model: 'gpt-image-1',\n startedAt: 1000,\n })\n // Idempotency covers threadId too: the late scope must not overwrite the\n // one the run was filed under.\n expect(resumed.threadId).toBe('gen-thread-1')\n\n await store.update('gen-1', {\n status: 'completed',\n finishedAt: 2000,\n result: { images: [{ url: 'https://example.com/a.png' }] },\n artifacts: [\n {\n role: 'output',\n artifactId: 'art-1',\n runId: 'gen-1',\n threadId: 'thread-gen',\n name: 'a.png',\n mimeType: 'image/png',\n size: 3,\n createdAt: '2024-01-01T00:00:00.000Z',\n source: {\n activity: 'image',\n path: 'images.0',\n provider: 'openai',\n model: 'gpt-image-1',\n },\n },\n ],\n usage: { promptTokens: 1, completionTokens: 2, totalTokens: 3 },\n })\n const done = await store.get('gen-1')\n expect(done).toMatchObject({\n status: 'completed',\n finishedAt: 2000,\n result: { images: [{ url: 'https://example.com/a.png' }] },\n usage: { promptTokens: 1, completionTokens: 2, totalTokens: 3 },\n })\n expect(done?.artifacts).toHaveLength(1)\n expect(done?.artifacts?.[0]).toMatchObject({\n artifactId: 'art-1',\n mimeType: 'image/png',\n })\n\n await store.update('gen-1', {\n status: 'failed',\n error: { message: 'boom', code: 'provider_error' },\n })\n const failed = await store.get('gen-1')\n expect(failed?.status).toBe('failed')\n expect(failed?.error).toEqual({\n message: 'boom',\n code: 'provider_error',\n })\n\n // Patching a missing run is a no-op (does not throw, does not create).\n await store.update('gen-absent', { status: 'completed' })\n expect(await store.get('gen-absent')).toBeNull()\n })\n\n // `findLatestForThread` is REQUIRED: `reconstructGeneration` hydrates a\n // server-driven client from the stable thread id alone, so a backend that\n // always answers `null` silently restores nothing rather than degrading.\n it('findLatestForThread returns the most recently started linked run', async () => {\n const store = resolveStore('generationRuns')\n if (!store) return\n\n const thread = 'thread-gen-latest'\n expect(await store.findLatestForThread(thread)).toBeNull()\n\n // Insert out of order so insertion order cannot stand in for startedAt.\n await store.createOrResume({\n runId: 'gen-late',\n activity: 'image',\n provider: 'openai',\n model: 'gpt-image-1',\n startedAt: 3000,\n threadId: thread,\n })\n await store.createOrResume({\n runId: 'gen-early',\n activity: 'image',\n provider: 'openai',\n model: 'gpt-image-1',\n startedAt: 1000,\n threadId: thread,\n })\n expect(await store.findLatestForThread(thread)).toMatchObject({\n runId: 'gen-late',\n })\n\n // Another thread's run is never returned, and unlike findActiveRun a\n // TERMINAL run still counts — this is \"the latest\", not \"the active\".\n await store.createOrResume({\n runId: 'gen-other',\n activity: 'image',\n provider: 'openai',\n model: 'gpt-image-1',\n startedAt: 9000,\n threadId: 'thread-gen-other',\n })\n await store.update('gen-late', {\n status: 'completed',\n finishedAt: 3500,\n })\n expect(await store.findLatestForThread(thread)).toMatchObject({\n runId: 'gen-late',\n status: 'completed',\n })\n\n // A run with no thread link is not attributed to any thread.\n expect(await store.findLatestForThread('thread-unlinked')).toBeNull()\n })\n })\n\n describe('artifacts', () => {\n const artifact = (\n overrides: Partial<ArtifactRecord> & Pick<ArtifactRecord, 'artifactId'>,\n ): ArtifactRecord => ({\n runId: 'run-art',\n threadId: 'thread-art',\n name: 'image.png',\n mimeType: 'image/png',\n size: 3,\n createdAt: 100,\n ...overrides,\n })\n\n it('saves as an upsert, gets, and lists by run', async () => {\n const store = resolveStore('artifacts')\n if (!store) return\n\n expect(await store.get('art-missing')).toBeNull()\n expect(await store.list('run-unknown')).toEqual([])\n\n await store.save(\n artifact({\n artifactId: '\\u{10000}',\n blobKey: 'artifacts/run-art/art-a',\n createdAt: 100,\n }),\n )\n await store.save(\n artifact({\n artifactId: '\\u{e000}',\n sourceUrl: 'https://provider.example/expiring.png',\n createdAt: 100,\n }),\n )\n await store.save(\n artifact({ artifactId: 'art-c', runId: 'run-art-other' }),\n )\n\n expect(await store.get('\\u{10000}')).toMatchObject({\n artifactId: '\\u{10000}',\n runId: 'run-art',\n threadId: 'thread-art',\n blobKey: 'artifacts/run-art/art-a',\n name: 'image.png',\n mimeType: 'image/png',\n size: 3,\n createdAt: 100,\n })\n expect(await store.get('\\u{e000}')).toMatchObject({\n sourceUrl: 'https://provider.example/expiring.png',\n })\n\n expect((await store.list('run-art')).map((r) => r.artifactId)).toEqual([\n '\\u{e000}',\n '\\u{10000}',\n ])\n\n // save() is insert-OR-OVERWRITE: re-saving an id corrects the record.\n await store.save(\n artifact({ artifactId: '\\u{10000}', name: 'renamed.png', size: 9 }),\n )\n const updated = await store.get('\\u{10000}')\n expect(updated).toMatchObject({ name: 'renamed.png', size: 9 })\n expect(updated?.blobKey).toBeUndefined()\n expect((await store.list('run-art')).map((r) => r.artifactId)).toEqual([\n '\\u{e000}',\n '\\u{10000}',\n ])\n })\n\n it('lists a thread in deterministic createdAt and artifactId order', async () => {\n const store = resolveStore('artifacts')\n if (!store) return\n\n await store.save(\n artifact({\n artifactId: 'thread-b',\n threadId: 'thread-order',\n createdAt: 2,\n }),\n )\n await store.save(\n artifact({\n artifactId: 'thread-a',\n threadId: 'thread-order',\n createdAt: 2,\n }),\n )\n await store.save(\n artifact({\n artifactId: 'thread-early',\n threadId: 'thread-order',\n createdAt: 1,\n }),\n )\n await store.save(\n artifact({\n artifactId: 'other',\n threadId: 'thread-other',\n createdAt: 0,\n }),\n )\n\n expect(\n (await store.listForThread('thread-order')).map((r) => r.artifactId),\n ).toEqual(['thread-early', 'thread-a', 'thread-b'])\n })\n\n it('orders artifact IDs by UTF-8 bytes after createdAt', async () => {\n const store = resolveStore('artifacts')\n if (!store) return\n\n await store.save(\n artifact({\n artifactId: '\\u{10000}',\n threadId: 'thread-utf8',\n createdAt: 1,\n }),\n )\n await store.save(\n artifact({\n artifactId: '\\u{e000}',\n threadId: 'thread-utf8',\n createdAt: 1,\n }),\n )\n await store.save(\n artifact({ artifactId: 'a', threadId: 'thread-utf8', createdAt: 1 }),\n )\n\n expect(\n (await store.listForThread('thread-utf8')).map((r) => r.artifactId),\n ).toEqual(['a', '\\u{e000}', '\\u{10000}'])\n })\n\n it('deletes one artifact and every artifact for a run', async () => {\n const store = resolveStore('artifacts')\n if (!store) return\n\n await store.save(artifact({ artifactId: 'art-d1', runId: 'run-del' }))\n await store.save(artifact({ artifactId: 'art-d2', runId: 'run-del' }))\n await store.save(\n artifact({ artifactId: 'art-keep', runId: 'run-keep' }),\n )\n\n await store.delete('art-d1')\n expect(await store.get('art-d1')).toBeNull()\n expect((await store.list('run-del')).map((r) => r.artifactId)).toEqual([\n 'art-d2',\n ])\n\n // Deleting an absent id is a silent no-op, mirroring BlobStore.delete.\n await store.delete('art-d1')\n\n await store.deleteForRun('run-del')\n expect(await store.list('run-del')).toEqual([])\n expect(await store.get('art-d2')).toBeNull()\n // Scoped to the run: another run's artifacts survive.\n expect((await store.list('run-keep')).map((r) => r.artifactId)).toEqual(\n ['art-keep'],\n )\n // deleteForRun on a run with no artifacts is a no-op.\n await store.deleteForRun('run-del')\n })\n })\n\n describe('blobs', () => {\n it('round-trips bytes and metadata through put/get/head', async () => {\n const store = resolveStore('blobs')\n if (!store) return\n\n expect(await store.get('blob-missing')).toBeNull()\n expect(await store.head('blob-missing')).toBeNull()\n\n const bytes = new Uint8Array([1, 2, 3, 4])\n const put = await store.put('blob/a', bytes, {\n contentType: 'image/png',\n customMetadata: { runId: 'run-blob' },\n })\n expect(put).toMatchObject({\n key: 'blob/a',\n size: 4,\n contentType: 'image/png',\n customMetadata: { runId: 'run-blob' },\n })\n\n const object = required(await store.get('blob/a'), 'blob/a')\n expect(new Uint8Array(await object.arrayBuffer())).toEqual(bytes)\n expect(object).toMatchObject({\n key: 'blob/a',\n size: 4,\n contentType: 'image/png',\n customMetadata: { runId: 'run-blob' },\n })\n\n const head = await store.head('blob/a')\n expect(head).toMatchObject({ key: 'blob/a', size: 4 })\n\n // A string body encodes as UTF-8 and reads back through text().\n await store.put('blob/text', 'héllo')\n const text = required(await store.get('blob/text'), 'blob/text')\n expect(await text.text()).toBe('héllo')\n\n // An ArrayBuffer body is accepted too.\n await store.put('blob/buffer', new Uint8Array([9, 9]).buffer)\n const buffered = required(await store.get('blob/buffer'), 'blob/buffer')\n expect(new Uint8Array(await buffered.arrayBuffer())).toEqual(\n new Uint8Array([9, 9]),\n )\n })\n\n it('accepts a stream body with no declared length', async () => {\n const store = resolveStore('blobs')\n if (!store) return\n\n // A TransformStream's readable side carries no declared length —\n // exactly what a fetch-based producer hands the store when the origin\n // chunks its reply (or its length can't be trusted). Byte bodies take\n // a different branch in most stores and prove nothing about the\n // streaming path, so this case is the one that keeps a store honest:\n // it must drain the stream, not require a length up front.\n const bytes = new Uint8Array(64 * 1024).map((_, i) => i % 251)\n const source = required(new Response(bytes).body, 'response body')\n const lengthless = source.pipeThrough(\n new TransformStream<Uint8Array, Uint8Array>(),\n )\n const put = await store.put('blob/stream', lengthless, {\n contentType: 'application/octet-stream',\n })\n expect(put.size).toBe(bytes.byteLength)\n\n const object = required(await store.get('blob/stream'), 'blob/stream')\n expect(new Uint8Array(await object.arrayBuffer())).toEqual(bytes)\n expect(object.size).toBe(bytes.byteLength)\n })\n\n it('accepts expectedLength as an advisory hint for a stream body', async () => {\n const store = resolveStore('blobs')\n if (!store) return\n\n // The real-world combination: a length-less stream PLUS the hint —\n // which is what the artifact middleware sends when the origin declared\n // a trustworthy `content-length`. The hint is advisory (a store may\n // use it to pick an upload strategy); the drained bytes stay the\n // record of truth, so `size` must still be the count of what arrived.\n const bytes = new Uint8Array(9 * 1024).map((_, i) => i % 251)\n const source = required(new Response(bytes).body, 'response body')\n const lengthless = source.pipeThrough(\n new TransformStream<Uint8Array, Uint8Array>(),\n )\n const put = await store.put('blob/stream-hint', lengthless, {\n expectedLength: bytes.byteLength,\n })\n expect(put.size).toBe(bytes.byteLength)\n\n const object = required(\n await store.get('blob/stream-hint'),\n 'blob/stream-hint',\n )\n expect(new Uint8Array(await object.arrayBuffer())).toEqual(bytes)\n expect(object.size).toBe(bytes.byteLength)\n })\n\n it('serves a byte range and reports the slice it served', async () => {\n const store = resolveStore('blobs')\n if (!store) return\n\n // Range reads are what a serve route turns into `206` +\n // `Content-Range` — the shape `<video>` seeking is built on. `size`\n // keeps reporting the WHOLE object so the route can finish the\n // `Content-Range` header from the same result.\n const bytes = new Uint8Array(1024).map((_, i) => i % 251)\n await store.put('blob/range', bytes)\n\n const middle = required(\n await store.get('blob/range', { range: { offset: 100, length: 50 } }),\n 'blob/range',\n )\n expect(new Uint8Array(await middle.arrayBuffer())).toEqual(\n bytes.slice(100, 150),\n )\n expect(middle.size).toBe(bytes.byteLength)\n expect(middle.range).toEqual({ offset: 100, length: 50 })\n\n // No `length`: from the offset to the end.\n const tail = required(\n await store.get('blob/range', { range: { offset: 1000 } }),\n 'blob/range',\n )\n expect(new Uint8Array(await tail.arrayBuffer())).toEqual(\n bytes.slice(1000),\n )\n expect(tail.range).toEqual({ offset: 1000, length: 24 })\n\n // A `length` past the end is clamped, not an error: `bytes=1000-2000`\n // against a 1 KiB object is a legal request that serves 24 bytes.\n const clamped = required(\n await store.get('blob/range', {\n range: { offset: 1000, length: 1000 },\n }),\n 'blob/range',\n )\n expect(clamped.range).toEqual({ offset: 1000, length: 24 })\n expect((await clamped.arrayBuffer()).byteLength).toBe(24)\n\n // The streaming accessor carries the same slice as arrayBuffer().\n const streamed = required(\n await store.get('blob/range', { range: { offset: 10, length: 5 } }),\n 'blob/range',\n )\n const body = streamed.body\n if (body) {\n expect(await drainStream(body)).toEqual(bytes.slice(10, 15))\n }\n\n // A whole-object read reports no `range` — a caller that sees one\n // takes the bytes for a slice and would answer `206` for all of them.\n const whole = required(await store.get('blob/range'), 'blob/range')\n expect(whole.range).toBeUndefined()\n })\n\n it('overwrites an existing key and deletes silently', async () => {\n const store = resolveStore('blobs')\n if (!store) return\n\n const first = await store.put('blob/over', new Uint8Array([1]), {\n contentType: 'text/plain',\n customMetadata: { v: '1' },\n })\n const second = await store.put('blob/over', new Uint8Array([2, 2, 2]), {\n contentType: 'application/octet-stream',\n customMetadata: { v: '2' },\n })\n\n expect(second).toMatchObject({\n size: 3,\n contentType: 'application/octet-stream',\n customMetadata: { v: '2' },\n })\n // When a backend exposes etags at all, new bytes get a new one.\n if (first.etag !== undefined && second.etag !== undefined) {\n expect(second.etag).not.toBe(first.etag)\n }\n const after = required(await store.get('blob/over'), 'blob/over')\n expect(new Uint8Array(await after.arrayBuffer())).toEqual(\n new Uint8Array([2, 2, 2]),\n )\n\n await store.delete('blob/over')\n expect(await store.get('blob/over')).toBeNull()\n expect(await store.head('blob/over')).toBeNull()\n // Deleting an absent key is a no-op, not an error.\n await store.delete('blob/over')\n })\n\n it('lists by literal prefix in ascending key order', async () => {\n const store = resolveStore('blobs')\n if (!store) return\n\n // `_` and `%` are LIKE metacharacters: a SQL backend that forgets to\n // escape them would match `list-x/…` here. And SQLite's LIKE is\n // case-insensitive for ASCII, so `LIST_/` must not match either.\n await store.put('list_/b', new Uint8Array([2]))\n await store.put('list_/a', new Uint8Array([1]))\n await store.put('list_/c', new Uint8Array([3]))\n await store.put('list-x/d', new Uint8Array([4]))\n await store.put('LIST_/e', new Uint8Array([5]))\n\n const page = await store.list({ prefix: 'list_/' })\n expect(page.objects.map((o) => o.key)).toEqual([\n 'list_/a',\n 'list_/b',\n 'list_/c',\n ])\n expect(page.truncated).toBeFalsy()\n\n expect(\n (await store.list({ prefix: 'list_/nothing-here' })).objects,\n ).toEqual([])\n })\n\n it('pages with a cursor and returns an empty page for limit 0', async () => {\n const store = resolveStore('blobs')\n if (!store) return\n\n for (const key of ['page/a', 'page/b', 'page/c', 'page/d', 'page/e']) {\n await store.put(key, new Uint8Array([1]))\n }\n\n const empty = await store.list({ prefix: 'page/', limit: 0 })\n expect(empty.objects).toEqual([])\n expect(empty.truncated).toBeFalsy()\n expect(empty.cursor).toBeUndefined()\n\n // Walk every key exactly once: each page's cursor resumes strictly\n // after the last key it returned.\n const seen: Array<string> = []\n let cursor: string | undefined\n for (let guard = 0; guard < 10; guard++) {\n const result = await store.list({\n prefix: 'page/',\n limit: 2,\n ...(cursor !== undefined ? { cursor } : {}),\n })\n seen.push(...result.objects.map((o) => o.key))\n if (!result.truncated) break\n expect(result.cursor).toBe(result.objects.at(-1)?.key)\n cursor = result.cursor\n }\n expect(seen).toEqual(['page/a', 'page/b', 'page/c', 'page/d', 'page/e'])\n\n // An exact-fit limit is not truncated: no key matches beyond the page.\n const exact = await store.list({ prefix: 'page/', limit: 5 })\n expect(exact.objects.map((o) => o.key)).toEqual(seen)\n expect(exact.truncated).toBeFalsy()\n })\n })\n\n describe('metadata', () => {\n it('sets, gets, namespaces, and deletes without composite-key collisions', async (ctx) => {\n const store = resolveStore('metadata')\n if (!store) return ctx.skip('store not provided')\n\n expect(await store.get('scope-a', 'k')).toBeNull()\n\n await store.set('scope-a', 'k', { n: 1 })\n await store.set('scope-b', 'k', { n: 2 })\n expect(await store.get('scope-a', 'k')).toEqual({ n: 1 })\n expect(await store.get('scope-b', 'k')).toEqual({ n: 2 })\n\n await store.set('scope-a', 'k', { n: 3 })\n expect(await store.get('scope-a', 'k')).toEqual({ n: 3 })\n\n await store.delete('scope-a', 'k')\n expect(await store.get('scope-a', 'k')).toBeNull()\n // Delete is namespaced: scope-b untouched.\n expect(await store.get('scope-b', 'k')).toEqual({ n: 2 })\n\n // Composite identity must not alias across colon-containing parts.\n // ('a:b','c') and ('a','b:c') are distinct pairs.\n await store.set('a:b', 'c', 'left')\n await store.set('a', 'b:c', 'right')\n expect(await store.get('a:b', 'c')).toBe('left')\n expect(await store.get('a', 'b:c')).toBe('right')\n await store.delete('a:b', 'c')\n expect(await store.get('a:b', 'c')).toBeNull()\n expect(await store.get('a', 'b:c')).toBe('right')\n })\n })\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEA,SAAS,SACP,OACA,MACQ;CACR,IAAI,SAAS,MACX,MAAM,IAAI,MAAM,uCAAuC,KAAK,UAAU;CAExE,OAAO;AACT;;AAGA,eAAe,YACb,QACqB;CACrB,MAAM,SAA4B,CAAC;CACnC,IAAI,QAAQ;CACZ,MAAM,SAAS,OAAO,UAAU;CAChC,SAAS;EACP,MAAM,EAAE,OAAO,SAAS,MAAM,OAAO,KAAK;EAC1C,IAAI,MAAM;EACV,OAAO,KAAK,KAAK;EACjB,SAAS,MAAM;CACjB;CACA,MAAM,QAAQ,IAAI,WAAW,KAAK;CAClC,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,IAAI,OAAO,MAAM;EACvB,UAAU,MAAM;CAClB;CACA,OAAO;AACT;;;;;;AAyBA,SAAgB,0BACd,MACA,iBACA,SACM;CACN,MAAM,OAAO,IAAI,IAA+B,SAAS,QAAQ,CAAC,CAAC;CACnE,MAAM,cAAc,IAAI,IACtB,SAAS,eAAe,CAAC,CAC3B;CAEA,SAAS,8BAA8B,cAAc;EACnD,IAAI;EAEJ,UAAU,YAAY;GACpB,cAAc,MAAM,gBAAgB;EACtC,CAAC;;;;;;EAOD,SAAS,aACP,KAC+C;GAC/C,MAAM,QAAQ,YAAY,OAAO;GACjC,IAAI,OAAO,OAAO;GAClB,IAAI,KAAK,IAAI,GAAG,GAAG,OAAO;GAC1B,MAAM,IAAI,MACR,qCAAqC,IAAI,8CACN,IAAI,qCACzC;EACF;;;;;;;;;;EAWA,SAAS,cACP,MACA,YACoD;GACpD,IAAI,KAAK,aAAa,OAAO;GAC7B,MAAM,MAAuC,QAAQ;GACrD,IAAI,YAAY,IAAI,GAAG,GAAG,OAAO;GACjC,MAAM,IAAI,MACR,+CAA+C,IAAI,+DACP,IAAI,qCAClD;EACF;EAKA,SAAS,kBACP,MACgE;GAChE,OAAO,OAAO,KAAK,oBAAoB;EACzC;EAEA,SAAS,kBAAkB;GAGzB,GAAG,2DAA2D,OAAO,QAAQ;IAC3E,MAAM,QAAQ,aAAa,UAAU;IACrC,IAAI,CAAC,OAAO,OAAO,IAAI,KAAK,oBAAoB;IAEhD,OAAO,MAAM,MAAM,WAAW,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAE3D,MAAM,MAAM,WAAW,cAAc,CACnC;KAAE,MAAM;KAAQ,SAAS;IAAK,GAC9B;KAAE,MAAM;KAAa,SAAS;IAAQ,CACxC,CAAC;IACD,MAAM,SAAS,MAAM,MAAM,WAAW,YAAY;IAClD,OAAO,MAAM,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI;IACvC,OAAO,MAAM,CAAC,CAAC,QAAQ,CACrB;KAAE,MAAM;KAAQ,SAAS;IAAK,GAC9B;KAAE,MAAM;KAAa,SAAS;IAAQ,CACxC,CAAC;IAGD,MAAM,MAAM,WAAW,cAAc,CACnC;KAAE,MAAM;KAAQ,SAAS;IAAO,CAClC,CAAC;IACD,OAAO,MAAM,MAAM,WAAW,YAAY,CAAC,CAAC,CAAC,QAAQ,CACnD;KAAE,MAAM;KAAQ,SAAS;IAAO,CAClC,CAAC;GACH,CAAC;GAED,GAAG,sDAAsD,OAAO,QAAQ;IACtE,MAAM,QAAQ,aAAa,UAAU;IACrC,IAAI,CAAC,OAAO,OAAO,IAAI,KAAK,oBAAoB;IAEhD,MAAM,OAA4B;KAChC;MAAE,MAAM;MAAQ,SAAS;KAAe;KACxC;MAEE,MAAM;MACN,SAAS;MACT,WAAW,CACT;OACE,IAAI;OACJ,MAAM;OACN,UAAU;QACR,MAAM;QACN,WAAW;OACb;MACF,CACF;KACF;KACA;MAEE,MAAM;MACN,SAAS;MACT,YAAY;KACd;KACA;MAEE,MAAM;MACN,SAAS,CACP;OAAE,MAAM;OAAQ,SAAS;MAAyB,GAClD;OACE,MAAM;OACN,QAAQ;QACN,MAAM;QACN,OAAO;QACP,UAAU;OACZ;MACF,CACF;KACF;KACA;MAEE,MAAM;MACN,SAAS;MACT,UAAU,CACR;OACE,SAAS;OACT,WAAW;MACb,CACF;KACF;IACF;IAEA,MAAM,MAAM,WAAW,eAAe,IAAI;IAC1C,OAAO,MAAM,MAAM,WAAW,aAAa,CAAC,CAAC,CAAC,QAAQ,IAAI;GAC5D,CAAC;EACH,CAAC;EAED,SAAS,cAAc;GACrB,GAAG,oDAAoD,OAAO,QAAQ;IACpE,MAAM,QAAQ,aAAa,MAAM;IACjC,IAAI,CAAC,OAAO,OAAO,IAAI,KAAK,oBAAoB;IAEhD,OAAO,MAAM,MAAM,IAAI,aAAa,CAAC,CAAC,CAAC,SAAS;IAEhD,MAAM,UAAU,MAAM,MAAM,eAAe;KACzC,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,cAAc;KAC5B,OAAO;KACP,UAAU;KACV,QAAQ;KACR,WAAW;IACb,CAAC;IAGD,MAAM,UAAU,MAAM,MAAM,eAAe;KACzC,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,cAAc;KAC5B,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IAED,MAAM,MAAM,OAAO,SAAS;KAC1B,QAAQ;KACR,YAAY;KACZ,OAAO;MAAE,cAAc;MAAG,kBAAkB;MAAG,aAAa;KAAE;IAChE,CAAC;IACD,MAAM,OAAO,MAAM,MAAM,IAAI,OAAO;IACpC,OAAO,IAAI,CAAC,CAAC,cAAc;KACzB,OAAO;KACP,QAAQ;KACR,YAAY;KACZ,OAAO;MAAE,cAAc;MAAG,kBAAkB;MAAG,aAAa;KAAE;IAChE,CAAC;IAED,MAAM,qBAAqB,MAAM,MAAM,eAAe;KACpD,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,OAAO,kBAAkB,CAAC,CAAC,QAAQ,IAAI;IAKvC,MAAM,MAAM,OAAO,SAAS;KAC1B,QAAQ;KACR,OAAO;MAAE,SAAS;MAAQ,MAAM;KAAsB;IACxD,CAAC;IACD,MAAM,SAAS,MAAM,MAAM,IAAI,OAAO;IACtC,OAAO,QAAQ,MAAM,CAAC,CAAC,KAAK,QAAQ;IACpC,OAAO,QAAQ,KAAK,CAAC,CAAC,QAAQ;KAC5B,SAAS;KACT,MAAM;IACR,CAAC;IAGD,MAAM,MAAM,OAAO,cAAc,EAAE,QAAQ,YAAY,CAAC;IACxD,OAAO,MAAM,MAAM,IAAI,YAAY,CAAC,CAAC,CAAC,SAAS;GACjD,CAAC;GAQD,GAAG,kDAAkD,OAAO,QAAQ;IAClE,MAAM,QAAQ,aAAa,MAAM;IACjC,IAAI,CAAC,OAAO,OAAO,IAAI,KAAK,oBAAoB;IAEhD,MAAM,MAAM,eAAe;KACzB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,MAAM,MAAM,OAAO,QAAQ;KAAE,QAAQ;KAAa,YAAY;IAAG,CAAC;IAGlE,MAAM,UAAU,MAAM,MAAM,eAAe;KACzC,OAAO;KACP,UAAU;KACV,WAAW;KACX,QAAQ;IACV,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,cAAc;KAC5B,OAAO;KACP,QAAQ;KACR,WAAW;KACX,YAAY;IACd,CAAC;IAGD,OAAO,MAAM,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,cAAc;KAC5C,QAAQ;KACR,WAAW;KACX,YAAY;IACd,CAAC;GACH,CAAC;GAMD,GAAG,+DAA+D,OAAO,QAAQ;IAC/E,MAAM,QAAQ,aAAa,MAAM;IACjC,IAAI,CAAC,OAAO,OAAO,IAAI,KAAK,oBAAoB;IAChD,IAAI,CAAC,kBAAkB,KAAK,GAC1B,OAAO,IAAI,KAAK,sCAAsC;IAGxD,MAAM,SAAS,MAAM,MAAM,eAAe;KACxC,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,OAAO,OAAO,WAAW,CAAC,CAAC,cAAc;IACzC,OAAO,OAAO,aAAa,CAAC,CAAC,cAAc;IAC3C,OAAO,OAAO,IAAI,CAAC,CAAC,cAAc;IAElC,MAAM,QAAQ,MAAM,MAAM,eAAe;KACvC,OAAO;KACP,UAAU;KACV,WAAW;KACX,aAAa;KACb,eAAe;KACf,MAAM;IACR,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,cAAc;KAC1B,OAAO;KACP,UAAU;KACV,WAAW;KACX,aAAa;KACb,eAAe;KACf,MAAM;IACR,CAAC;IAED,MAAM,UAAU,MAAM,MAAM,eAAe;KACzC,OAAO;KACP,UAAU;KACV,WAAW;KACX,aAAa;KACb,eAAe;KACf,MAAM;IACR,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,cAAc;KAC5B,OAAO;KACP,UAAU;KACV,WAAW;KACX,aAAa;KACb,eAAe;KACf,MAAM;IACR,CAAC;IACD,OAAO,MAAM,MAAM,IAAI,UAAU,CAAC,CAAC,CAAC,cAAc;KAChD,aAAa;KACb,eAAe;KACf,MAAM;IACR,CAAC;GACH,CAAC;GAED,GAAG,kEAAkE,OAAO,QAAQ;IAClF,MAAM,QAAQ,aAAa,MAAM;IACjC,IAAI,CAAC,OAAO,OAAO,IAAI,KAAK,oBAAoB;IAEhD,MAAM,SAAS;IACf,OAAO,MAAM,MAAM,cAAc,MAAM,CAAC,CAAC,CAAC,SAAS;IAEnD,MAAM,MAAM,eAAe;KACzB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,MAAM,MAAM,eAAe;KACzB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IAED,OAAO,MAAM,MAAM,cAAc,MAAM,CAAC,CAAC,CAAC,cAAc;KACtD,OAAO;KACP,QAAQ;IACV,CAAC;IAGD,MAAM,MAAM,eAAe;KACzB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,OAAO,MAAM,MAAM,cAAc,MAAM,CAAC,CAAC,CAAC,cAAc,EACtD,OAAO,WACT,CAAC;IAGD,MAAM,MAAM,OAAO,YAAY;KAC7B,QAAQ;KACR,YAAY;IACd,CAAC;IACD,OAAO,MAAM,MAAM,cAAc,MAAM,CAAC,CAAC,CAAC,cAAc;KACtD,OAAO;KACP,QAAQ;IACV,CAAC;IAGD,MAAM,MAAM,OAAO,YAAY;KAC7B,QAAQ;KACR,YAAY;IACd,CAAC;IACD,OAAO,MAAM,MAAM,cAAc,MAAM,CAAC,CAAC,CAAC,SAAS;GACrD,CAAC;GAKD,GAAG,uCAAuC,OAAO,QAAQ;IACvD,MAAM,OAAO,aAAa,MAAM;IAChC,IAAI,CAAC,MAAM,OAAO,IAAI,KAAK,oBAAoB;IAC/C,IAAI,CAAC,cAAc,MAAM,cAAc,GACrC,OAAO,IAAI,KAAK,mCAAmC;IAGrD,MAAM,KAAK,eAAe;KACxB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,MAAM,KAAK,eAAe;KACxB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,MAAM,SAAS,MAAM,KAAK,aAAa,IAAI;IAC3C,OAAO,OAAO,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,MAAM,CAAC;GAC7D,CAAC;GAKD,GAAG,6CAA6C,OAAO,QAAQ;IAC7D,MAAM,OAAO,aAAa,MAAM;IAChC,IAAI,CAAC,MAAM,OAAO,IAAI,KAAK,oBAAoB;IAC/C,IAAI,CAAC,kBAAkB,IAAI,GACzB,OAAO,IAAI,KAAK,sCAAsC;IAGxD,MAAM,KAAK,eAAe;KACxB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,MAAM,KAAK,eAAe;KACxB,OAAO;KACP,UAAU;KACV,WAAW;KACX,aAAa;KACb,eAAe;KACf,MAAM;IACR,CAAC;IACD,MAAM,KAAK,eAAe;KACxB,OAAO;KACP,UAAU;KACV,WAAW;KACX,aAAa;KACb,eAAe;KACf,MAAM;IACR,CAAC;IACD,MAAM,KAAK,eAAe;KACxB,OAAO;KACP,UAAU;KACV,WAAW;KACX,aAAa;KACb,eAAe;KACf,MAAM;IACR,CAAC;IAED,MAAM,SAAS,MAAM,KAAK,gBAAgB,YAAY;IACtD,OAAO,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,OAAO,CAAC;IACjE,OAAO,OAAO,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc,KAAK,CAAC;IACnE,OAAO,MAAM,KAAK,gBAAgB,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;GAC9D,CAAC;GAYD,GAAG,kDAAkD,OAAO,QAAQ;IAClE,MAAM,OAAO,aAAa,MAAM;IAChC,IAAI,CAAC,MAAM,OAAO,IAAI,KAAK,oBAAoB;IAC/C,IAAI,CAAC,cAAc,MAAM,iBAAiB,GACxC,OAAO,IAAI,KAAK,sCAAsC;IAGxD,MAAM,MAAM;IACZ,MAAM,QAAQ;IACd,MAAM,SAAS;IAGf,MAAM,KAAK,eAAe;KACxB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,MAAM,KAAK,OAAO,eAAe,EAAE,eAAe,IAAM,CAAC;IAKzD,MAAM,KAAK,eAAe;KACxB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,MAAM,KAAK,OAAO,eAAe,EAAE,eAAe,OAAO,CAAC;IAM1D,MAAM,KAAK,eAAe;KACxB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,MAAM,KAAK,OAAO,iBAAiB,EAAE,eAAe,KAAW,CAAC;IAKhE,MAAM,KAAK,eAAe;KACxB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,MAAM,KAAK,OAAO,gBAAgB;KAChC,eAAe;KACf,QAAQ;KACR,YAAY;IACd,CAAC;IAMD,MAAM,KAAK,eAAe;KACxB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IAYD,MAAM,UAAS,MAVW,KAAK,gBAAgB;KAAE;KAAK;IAAM,CAAC,EAAA,CAW1D,KAAK,MAAM,EAAE,KAAK,CAAC,CACnB,QAAQ,OAAO,GAAG,WAAW,KAAK,CAAC,CAAC,CACpC,KAAK;IACR,OAAO,MAAM,CAAC,CAAC,QAAQ,CAAC,eAAe,aAAa,CAAC;IAUrD,MAAM,eAAe,MAAM,OAAO,WAAW;IAC7C,MAAM,KAAK,eAAe;KACxB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,MAAM,KAAK,OAAO,cAAc,EAAE,eAAe,IAAI,CAAC;IACtD,MAAM,qBAAqB,MAAM,KAAK,gBAAgB;KACpD;KACA,OAAO;IACT,CAAC;IACD,OACE,mBAAmB,MAAM,MAAM,EAAE,UAAU,YAAY,CAAC,EACpD,aACN,CAAC,CAAC,KAAK,GAAG;IASV,MAAM,kBAAkB,MAAM,OAAO,WAAW;IAChD,MAAM,KAAK,eAAe;KACxB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,MAAM,KAAK,OAAO,iBAAiB,EAAE,eAAe,IAAM,CAAC;IAC3D,MAAM,KAAK,OAAO,iBAAiB,EAAE,eAAe,KAAA,EAAU,CAAC;IAC/D,MAAM,gBAAgB,MAAM,KAAK,gBAAgB;KAAE;KAAK;IAAM,CAAC;IAC/D,OAAO,cAAc,MAAM,MAAM,EAAE,UAAU,eAAe,CAAC,CAAC,CAAC,KAC7D,KACF;IAKA,MAAM,iBAAiB,MAAM,QAAQ,IACnC;KAFwB;KAAa;KAAU;IAE/C,CAAA,CAAiB,IAAI,OAAO,WAAW;KACrC,MAAM,QAAQ,MAAM,OAAO,WAAW;KACtC,MAAM,KAAK,eAAe;MAAE;MAAO,UAAU;MAAQ,WAAW;KAAE,CAAC;KACnE,MAAM,KAAK,OAAO,OAAO;MACvB;MACA,eAAe;MACf,YAAY;KACd,CAAC;KACD,OAAO;IACT,CAAC,CACH;IACA,MAAM,gBAAgB,MAAM,KAAK,gBAAgB;KAAE;KAAK;IAAM,CAAC;IAC/D,OACE,cAAc,MAAM,MAAM,eAAe,SAAS,EAAE,KAAK,CAAC,CAC5D,CAAC,CAAC,KAAK,KAAK;IAMZ,MAAM,mBAAmB,MAAM,OAAO,WAAW;IACjD,MAAM,KAAK,eAAe;KACxB,OAAO;KACP,UAAU;KACV,WAAW;IACb,CAAC;IACD,MAAM,KAAK,OAAO,kBAAkB;KAClC,QAAQ;KACR,eAAe;IACjB,CAAC;IACD,MAAM,mBAAmB,MAAM,KAAK,gBAAgB;KAAE;KAAK;IAAM,CAAC;IAClE,OAAO,iBAAiB,MAAM,MAAM,EAAE,UAAU,gBAAgB,CAAC,CAAC,CAAC,KACjE,KACF;GACF,CAAC;EACH,CAAC;EAED,SAAS,oBAAoB;GAC3B,GAAG,2DAA2D,OAAO,QAAQ;IAC3E,MAAM,QAAQ,aAAa,YAAY;IACvC,IAAI,CAAC,OAAO,OAAO,IAAI,KAAK,oBAAoB;IAEhD,OAAO,MAAM,MAAM,IAAI,aAAa,CAAC,CAAC,CAAC,SAAS;IAEhD,MAAM,MAAM,OAAO;KACjB,aAAa;KACb,OAAO;KACP,UAAU;KACV,aAAa;KACb,SAAS;MAAE,MAAM;MAAU,MAAM,EAAE,GAAG,IAAI;KAAE;IAC9C,CAAC;IACD,MAAM,MAAM,OAAO;KACjB,aAAa;KACb,OAAO;KACP,UAAU;KACV,aAAa;KACb,SAAS,EAAE,MAAM,QAAQ;IAC3B,CAAC;IACD,MAAM,MAAM,OAAO;KACjB,aAAa;KACb,OAAO;KACP,UAAU;KACV,aAAa;KACb,SAAS,CAAC;IACZ,CAAC;IAED,MAAM,MAAM,MAAM,MAAM,IAAI,OAAO;IACnC,OAAO,GAAG,CAAC,CAAC,cAAc;KACxB,aAAa;KACb,OAAO;KACP,UAAU;KACV,QAAQ;KACR,aAAa;KACb,SAAS;MAAE,MAAM;MAAU,MAAM,EAAE,GAAG,IAAI;KAAE;IAC9C,CAAC;IAED,QACG,MAAM,MAAM,KAAK,UAAU,EAAA,CAAG,KAAK,MAAM,EAAE,WAAW,CACzD,CAAC,CAAC,QAAQ;KAAC;KAAS;KAAS;IAAO,CAAC;IACrC,QACG,MAAM,MAAM,UAAU,OAAO,EAAA,CAAG,KAAK,MAAM,EAAE,WAAW,CAC3D,CAAC,CAAC,QAAQ,CAAC,SAAS,OAAO,CAAC;IAC5B,QACG,MAAM,MAAM,YAAY,UAAU,EAAA,CAAG,KAAK,MAAM,EAAE,WAAW,CAChE,CAAC,CAAC,QAAQ;KAAC;KAAS;KAAS;IAAO,CAAC;IAErC,MAAM,MAAM,QAAQ,SAAS,EAAE,IAAI,KAAK,CAAC;IACzC,MAAM,WAAW,MAAM,MAAM,IAAI,OAAO;IACxC,OAAO,UAAU,MAAM,CAAC,CAAC,KAAK,UAAU;IACxC,OAAO,UAAU,QAAQ,CAAC,CAAC,QAAQ,EAAE,IAAI,KAAK,CAAC;IAC/C,OAAO,OAAO,UAAU,UAAU,CAAC,CAAC,KAAK,QAAQ;IAEjD,MAAM,MAAM,OAAO,OAAO;IAC1B,MAAM,YAAY,MAAM,MAAM,IAAI,OAAO;IACzC,OAAO,WAAW,MAAM,CAAC,CAAC,KAAK,WAAW;IAC1C,OAAO,OAAO,WAAW,UAAU,CAAC,CAAC,KAAK,QAAQ;IAElD,QACG,MAAM,MAAM,YAAY,UAAU,EAAA,CAAG,KAAK,MAAM,EAAE,WAAW,CAChE,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;IACnB,QACG,MAAM,MAAM,iBAAiB,OAAO,EAAA,CAAG,KAAK,MAAM,EAAE,WAAW,CAClE,CAAC,CAAC,QAAQ,CAAC,CAAC;GACd,CAAC;GAED,GAAG,kFAAkF,OAAO,QAAQ;IAClG,MAAM,QAAQ,aAAa,YAAY;IACvC,IAAI,CAAC,OAAO,OAAO,IAAI,KAAK,oBAAoB;IAEhD,MAAM,MAAM,OAAO;KACjB,aAAa;KACb,OAAO;KACP,UAAU;KACV,aAAa;KACb,SAAS,EAAE,SAAS,EAAE;IACxB,CAAC;IACD,MAAM,MAAM,QAAQ,WAAW,EAAE,QAAQ,GAAG,CAAC;IAI7C,MAAM,MAAM,OAAO;KACjB,aAAa;KACb,OAAO;KACP,UAAU;KACV,aAAa;KACb,SAAS,EAAE,SAAS,EAAE;IACxB,CAAC;IAED,MAAM,QAAQ,MAAM,MAAM,IAAI,SAAS;IACvC,OAAO,OAAO,MAAM,CAAC,CAAC,KAAK,UAAU;IACrC,OAAO,OAAO,QAAQ,CAAC,CAAC,QAAQ,EAAE,QAAQ,GAAG,CAAC;IAC9C,OAAO,OAAO,OAAO,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,CAAC;IAC7C,OAAO,OAAO,WAAW,CAAC,CAAC,KAAK,GAAG;GACrC,CAAC;GAED,GAAG,6EAA6E,OAAO,QAAQ;IAC7F,MAAM,QAAQ,aAAa,YAAY;IACvC,IAAI,CAAC,OAAO,OAAO,IAAI,KAAK,oBAAoB;IAIhD,MAAM,MAAM,OAAO;KACjB,aAAa;KACb,OAAO;KACP,UAAU;KACV,aAAa;KACb,SAAS,CAAC;IACZ,CAAC;IACD,MAAM,MAAM,OAAO;KACjB,aAAa;KACb,OAAO;KACP,UAAU;KACV,aAAa;KACb,SAAS,CAAC;IACZ,CAAC;IACD,MAAM,MAAM,OAAO;KACjB,aAAa;KACb,OAAO;KACP,UAAU;KACV,aAAa;KACb,SAAS,CAAC;IACZ,CAAC;IAED,QACG,MAAM,MAAM,KAAK,cAAc,EAAA,CAAG,KAAK,MAAM,EAAE,WAAW,CAC7D,CAAC,CAAC,QAAQ;KAAC;KAAa;KAAW;IAAU,CAAC;IAC9C,QACG,MAAM,MAAM,YAAY,cAAc,EAAA,CAAG,KAAK,MAAM,EAAE,WAAW,CACpE,CAAC,CAAC,QAAQ;KAAC;KAAa;KAAW;IAAU,CAAC;IAC9C,QACG,MAAM,MAAM,UAAU,WAAW,EAAA,CAAG,KAAK,MAAM,EAAE,WAAW,CAC/D,CAAC,CAAC,QAAQ;KAAC;KAAa;KAAW;IAAU,CAAC;GAChD,CAAC;EACH,CAAC;EAED,SAAS,wBAAwB;GAC/B,GAAG,oDAAoD,YAAY;IACjE,MAAM,QAAQ,aAAa,gBAAgB;IAC3C,IAAI,CAAC,OAAO;IAEZ,OAAO,MAAM,MAAM,IAAI,aAAa,CAAC,CAAC,CAAC,SAAS;IAEhD,MAAM,UAAU,MAAM,MAAM,eAAe;KACzC,OAAO;KACP,UAAU;KACV,UAAU;KACV,UAAU;KACV,OAAO;KACP,WAAW;IACb,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,cAAc;KAC5B,OAAO;KACP,UAAU;KACV,UAAU;KACV,OAAO;KACP,QAAQ;KACR,WAAW;IACb,CAAC;IAGD,OAAO,QAAQ,QAAQ,CAAC,CAAC,KAAK,cAAc;IAG5C,MAAM,UAAU,MAAM,MAAM,eAAe;KACzC,OAAO;KACP,UAAU;KACV,UAAU;KACV,OAAO;KACP,WAAW;KACX,UAAU;IACZ,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,cAAc;KAC5B,OAAO;KACP,UAAU;KACV,UAAU;KACV,OAAO;KACP,WAAW;IACb,CAAC;IAGD,OAAO,QAAQ,QAAQ,CAAC,CAAC,KAAK,cAAc;IAE5C,MAAM,MAAM,OAAO,SAAS;KAC1B,QAAQ;KACR,YAAY;KACZ,QAAQ,EAAE,QAAQ,CAAC,EAAE,KAAK,4BAA4B,CAAC,EAAE;KACzD,WAAW,CACT;MACE,MAAM;MACN,YAAY;MACZ,OAAO;MACP,UAAU;MACV,MAAM;MACN,UAAU;MACV,MAAM;MACN,WAAW;MACX,QAAQ;OACN,UAAU;OACV,MAAM;OACN,UAAU;OACV,OAAO;MACT;KACF,CACF;KACA,OAAO;MAAE,cAAc;MAAG,kBAAkB;MAAG,aAAa;KAAE;IAChE,CAAC;IACD,MAAM,OAAO,MAAM,MAAM,IAAI,OAAO;IACpC,OAAO,IAAI,CAAC,CAAC,cAAc;KACzB,QAAQ;KACR,YAAY;KACZ,QAAQ,EAAE,QAAQ,CAAC,EAAE,KAAK,4BAA4B,CAAC,EAAE;KACzD,OAAO;MAAE,cAAc;MAAG,kBAAkB;MAAG,aAAa;KAAE;IAChE,CAAC;IACD,OAAO,MAAM,SAAS,CAAC,CAAC,aAAa,CAAC;IACtC,OAAO,MAAM,YAAY,EAAE,CAAC,CAAC,cAAc;KACzC,YAAY;KACZ,UAAU;IACZ,CAAC;IAED,MAAM,MAAM,OAAO,SAAS;KAC1B,QAAQ;KACR,OAAO;MAAE,SAAS;MAAQ,MAAM;KAAiB;IACnD,CAAC;IACD,MAAM,SAAS,MAAM,MAAM,IAAI,OAAO;IACtC,OAAO,QAAQ,MAAM,CAAC,CAAC,KAAK,QAAQ;IACpC,OAAO,QAAQ,KAAK,CAAC,CAAC,QAAQ;KAC5B,SAAS;KACT,MAAM;IACR,CAAC;IAGD,MAAM,MAAM,OAAO,cAAc,EAAE,QAAQ,YAAY,CAAC;IACxD,OAAO,MAAM,MAAM,IAAI,YAAY,CAAC,CAAC,CAAC,SAAS;GACjD,CAAC;GAKD,GAAG,oEAAoE,YAAY;IACjF,MAAM,QAAQ,aAAa,gBAAgB;IAC3C,IAAI,CAAC,OAAO;IAEZ,MAAM,SAAS;IACf,OAAO,MAAM,MAAM,oBAAoB,MAAM,CAAC,CAAC,CAAC,SAAS;IAGzD,MAAM,MAAM,eAAe;KACzB,OAAO;KACP,UAAU;KACV,UAAU;KACV,OAAO;KACP,WAAW;KACX,UAAU;IACZ,CAAC;IACD,MAAM,MAAM,eAAe;KACzB,OAAO;KACP,UAAU;KACV,UAAU;KACV,OAAO;KACP,WAAW;KACX,UAAU;IACZ,CAAC;IACD,OAAO,MAAM,MAAM,oBAAoB,MAAM,CAAC,CAAC,CAAC,cAAc,EAC5D,OAAO,WACT,CAAC;IAID,MAAM,MAAM,eAAe;KACzB,OAAO;KACP,UAAU;KACV,UAAU;KACV,OAAO;KACP,WAAW;KACX,UAAU;IACZ,CAAC;IACD,MAAM,MAAM,OAAO,YAAY;KAC7B,QAAQ;KACR,YAAY;IACd,CAAC;IACD,OAAO,MAAM,MAAM,oBAAoB,MAAM,CAAC,CAAC,CAAC,cAAc;KAC5D,OAAO;KACP,QAAQ;IACV,CAAC;IAGD,OAAO,MAAM,MAAM,oBAAoB,iBAAiB,CAAC,CAAC,CAAC,SAAS;GACtE,CAAC;EACH,CAAC;EAED,SAAS,mBAAmB;GAC1B,MAAM,YACJ,eACoB;IACpB,OAAO;IACP,UAAU;IACV,MAAM;IACN,UAAU;IACV,MAAM;IACN,WAAW;IACX,GAAG;GACL;GAEA,GAAG,8CAA8C,YAAY;IAC3D,MAAM,QAAQ,aAAa,WAAW;IACtC,IAAI,CAAC,OAAO;IAEZ,OAAO,MAAM,MAAM,IAAI,aAAa,CAAC,CAAC,CAAC,SAAS;IAChD,OAAO,MAAM,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAElD,MAAM,MAAM,KACV,SAAS;KACP,YAAY;KACZ,SAAS;KACT,WAAW;IACb,CAAC,CACH;IACA,MAAM,MAAM,KACV,SAAS;KACP,YAAY;KACZ,WAAW;KACX,WAAW;IACb,CAAC,CACH;IACA,MAAM,MAAM,KACV,SAAS;KAAE,YAAY;KAAS,OAAO;IAAgB,CAAC,CAC1D;IAEA,OAAO,MAAM,MAAM,IAAI,IAAW,CAAC,CAAC,CAAC,cAAc;KACjD,YAAY;KACZ,OAAO;KACP,UAAU;KACV,SAAS;KACT,MAAM;KACN,UAAU;KACV,MAAM;KACN,WAAW;IACb,CAAC;IACD,OAAO,MAAM,MAAM,IAAI,GAAU,CAAC,CAAC,CAAC,cAAc,EAChD,WAAW,wCACb,CAAC;IAED,QAAQ,MAAM,MAAM,KAAK,SAAS,EAAA,CAAG,KAAK,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,QAAQ,CACrE,KACA,IACF,CAAC;IAGD,MAAM,MAAM,KACV,SAAS;KAAE,YAAY;KAAa,MAAM;KAAe,MAAM;IAAE,CAAC,CACpE;IACA,MAAM,UAAU,MAAM,MAAM,IAAI,IAAW;IAC3C,OAAO,OAAO,CAAC,CAAC,cAAc;KAAE,MAAM;KAAe,MAAM;IAAE,CAAC;IAC9D,OAAO,SAAS,OAAO,CAAC,CAAC,cAAc;IACvC,QAAQ,MAAM,MAAM,KAAK,SAAS,EAAA,CAAG,KAAK,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,QAAQ,CACrE,KACA,IACF,CAAC;GACH,CAAC;GAED,GAAG,kEAAkE,YAAY;IAC/E,MAAM,QAAQ,aAAa,WAAW;IACtC,IAAI,CAAC,OAAO;IAEZ,MAAM,MAAM,KACV,SAAS;KACP,YAAY;KACZ,UAAU;KACV,WAAW;IACb,CAAC,CACH;IACA,MAAM,MAAM,KACV,SAAS;KACP,YAAY;KACZ,UAAU;KACV,WAAW;IACb,CAAC,CACH;IACA,MAAM,MAAM,KACV,SAAS;KACP,YAAY;KACZ,UAAU;KACV,WAAW;IACb,CAAC,CACH;IACA,MAAM,MAAM,KACV,SAAS;KACP,YAAY;KACZ,UAAU;KACV,WAAW;IACb,CAAC,CACH;IAEA,QACG,MAAM,MAAM,cAAc,cAAc,EAAA,CAAG,KAAK,MAAM,EAAE,UAAU,CACrE,CAAC,CAAC,QAAQ;KAAC;KAAgB;KAAY;IAAU,CAAC;GACpD,CAAC;GAED,GAAG,sDAAsD,YAAY;IACnE,MAAM,QAAQ,aAAa,WAAW;IACtC,IAAI,CAAC,OAAO;IAEZ,MAAM,MAAM,KACV,SAAS;KACP,YAAY;KACZ,UAAU;KACV,WAAW;IACb,CAAC,CACH;IACA,MAAM,MAAM,KACV,SAAS;KACP,YAAY;KACZ,UAAU;KACV,WAAW;IACb,CAAC,CACH;IACA,MAAM,MAAM,KACV,SAAS;KAAE,YAAY;KAAK,UAAU;KAAe,WAAW;IAAE,CAAC,CACrE;IAEA,QACG,MAAM,MAAM,cAAc,aAAa,EAAA,CAAG,KAAK,MAAM,EAAE,UAAU,CACpE,CAAC,CAAC,QAAQ;KAAC;KAAK;KAAY;IAAW,CAAC;GAC1C,CAAC;GAED,GAAG,qDAAqD,YAAY;IAClE,MAAM,QAAQ,aAAa,WAAW;IACtC,IAAI,CAAC,OAAO;IAEZ,MAAM,MAAM,KAAK,SAAS;KAAE,YAAY;KAAU,OAAO;IAAU,CAAC,CAAC;IACrE,MAAM,MAAM,KAAK,SAAS;KAAE,YAAY;KAAU,OAAO;IAAU,CAAC,CAAC;IACrE,MAAM,MAAM,KACV,SAAS;KAAE,YAAY;KAAY,OAAO;IAAW,CAAC,CACxD;IAEA,MAAM,MAAM,OAAO,QAAQ;IAC3B,OAAO,MAAM,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,SAAS;IAC3C,QAAQ,MAAM,MAAM,KAAK,SAAS,EAAA,CAAG,KAAK,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,QAAQ,CACrE,QACF,CAAC;IAGD,MAAM,MAAM,OAAO,QAAQ;IAE3B,MAAM,MAAM,aAAa,SAAS;IAClC,OAAO,MAAM,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC9C,OAAO,MAAM,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,SAAS;IAE3C,QAAQ,MAAM,MAAM,KAAK,UAAU,EAAA,CAAG,KAAK,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,QAC9D,CAAC,UAAU,CACb;IAEA,MAAM,MAAM,aAAa,SAAS;GACpC,CAAC;EACH,CAAC;EAED,SAAS,eAAe;GACtB,GAAG,uDAAuD,YAAY;IACpE,MAAM,QAAQ,aAAa,OAAO;IAClC,IAAI,CAAC,OAAO;IAEZ,OAAO,MAAM,MAAM,IAAI,cAAc,CAAC,CAAC,CAAC,SAAS;IACjD,OAAO,MAAM,MAAM,KAAK,cAAc,CAAC,CAAC,CAAC,SAAS;IAElD,MAAM,QAAQ,IAAI,WAAW;KAAC;KAAG;KAAG;KAAG;IAAC,CAAC;IACzC,MAAM,MAAM,MAAM,MAAM,IAAI,UAAU,OAAO;KAC3C,aAAa;KACb,gBAAgB,EAAE,OAAO,WAAW;IACtC,CAAC;IACD,OAAO,GAAG,CAAC,CAAC,cAAc;KACxB,KAAK;KACL,MAAM;KACN,aAAa;KACb,gBAAgB,EAAE,OAAO,WAAW;IACtC,CAAC;IAED,MAAM,SAAS,SAAS,MAAM,MAAM,IAAI,QAAQ,GAAG,QAAQ;IAC3D,OAAO,IAAI,WAAW,MAAM,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK;IAChE,OAAO,MAAM,CAAC,CAAC,cAAc;KAC3B,KAAK;KACL,MAAM;KACN,aAAa;KACb,gBAAgB,EAAE,OAAO,WAAW;IACtC,CAAC;IAED,MAAM,OAAO,MAAM,MAAM,KAAK,QAAQ;IACtC,OAAO,IAAI,CAAC,CAAC,cAAc;KAAE,KAAK;KAAU,MAAM;IAAE,CAAC;IAGrD,MAAM,MAAM,IAAI,aAAa,OAAO;IACpC,MAAM,OAAO,SAAS,MAAM,MAAM,IAAI,WAAW,GAAG,WAAW;IAC/D,OAAO,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO;IAGtC,MAAM,MAAM,IAAI,eAAe,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM;IAC5D,MAAM,WAAW,SAAS,MAAM,MAAM,IAAI,aAAa,GAAG,aAAa;IACvE,OAAO,IAAI,WAAW,MAAM,SAAS,YAAY,CAAC,CAAC,CAAC,CAAC,QACnD,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,CACvB;GACF,CAAC;GAED,GAAG,iDAAiD,YAAY;IAC9D,MAAM,QAAQ,aAAa,OAAO;IAClC,IAAI,CAAC,OAAO;IAQZ,MAAM,yBAAQ,IAAI,WAAW,KAAS,EAAA,CAAE,KAAK,GAAG,MAAM,IAAI,GAAG;IAE7D,MAAM,aADS,SAAS,IAAI,SAAS,KAAK,CAAC,CAAC,MAAM,eAC/B,CAAA,CAAO,YACxB,IAAI,gBAAwC,CAC9C;IACA,MAAM,MAAM,MAAM,MAAM,IAAI,eAAe,YAAY,EACrD,aAAa,2BACf,CAAC;IACD,OAAO,IAAI,IAAI,CAAC,CAAC,KAAK,MAAM,UAAU;IAEtC,MAAM,SAAS,SAAS,MAAM,MAAM,IAAI,aAAa,GAAG,aAAa;IACrE,OAAO,IAAI,WAAW,MAAM,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK;IAChE,OAAO,OAAO,IAAI,CAAC,CAAC,KAAK,MAAM,UAAU;GAC3C,CAAC;GAED,GAAG,gEAAgE,YAAY;IAC7E,MAAM,QAAQ,aAAa,OAAO;IAClC,IAAI,CAAC,OAAO;IAOZ,MAAM,yBAAQ,IAAI,WAAW,IAAQ,EAAA,CAAE,KAAK,GAAG,MAAM,IAAI,GAAG;IAE5D,MAAM,aADS,SAAS,IAAI,SAAS,KAAK,CAAC,CAAC,MAAM,eAC/B,CAAA,CAAO,YACxB,IAAI,gBAAwC,CAC9C;IACA,MAAM,MAAM,MAAM,MAAM,IAAI,oBAAoB,YAAY,EAC1D,gBAAgB,MAAM,WACxB,CAAC;IACD,OAAO,IAAI,IAAI,CAAC,CAAC,KAAK,MAAM,UAAU;IAEtC,MAAM,SAAS,SACb,MAAM,MAAM,IAAI,kBAAkB,GAClC,kBACF;IACA,OAAO,IAAI,WAAW,MAAM,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK;IAChE,OAAO,OAAO,IAAI,CAAC,CAAC,KAAK,MAAM,UAAU;GAC3C,CAAC;GAED,GAAG,uDAAuD,YAAY;IACpE,MAAM,QAAQ,aAAa,OAAO;IAClC,IAAI,CAAC,OAAO;IAMZ,MAAM,yBAAQ,IAAI,WAAW,IAAI,EAAA,CAAE,KAAK,GAAG,MAAM,IAAI,GAAG;IACxD,MAAM,MAAM,IAAI,cAAc,KAAK;IAEnC,MAAM,SAAS,SACb,MAAM,MAAM,IAAI,cAAc,EAAE,OAAO;KAAE,QAAQ;KAAK,QAAQ;IAAG,EAAE,CAAC,GACpE,YACF;IACA,OAAO,IAAI,WAAW,MAAM,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,QACjD,MAAM,MAAM,KAAK,GAAG,CACtB;IACA,OAAO,OAAO,IAAI,CAAC,CAAC,KAAK,MAAM,UAAU;IACzC,OAAO,OAAO,KAAK,CAAC,CAAC,QAAQ;KAAE,QAAQ;KAAK,QAAQ;IAAG,CAAC;IAGxD,MAAM,OAAO,SACX,MAAM,MAAM,IAAI,cAAc,EAAE,OAAO,EAAE,QAAQ,IAAK,EAAE,CAAC,GACzD,YACF;IACA,OAAO,IAAI,WAAW,MAAM,KAAK,YAAY,CAAC,CAAC,CAAC,CAAC,QAC/C,MAAM,MAAM,GAAI,CAClB;IACA,OAAO,KAAK,KAAK,CAAC,CAAC,QAAQ;KAAE,QAAQ;KAAM,QAAQ;IAAG,CAAC;IAIvD,MAAM,UAAU,SACd,MAAM,MAAM,IAAI,cAAc,EAC5B,OAAO;KAAE,QAAQ;KAAM,QAAQ;IAAK,EACtC,CAAC,GACD,YACF;IACA,OAAO,QAAQ,KAAK,CAAC,CAAC,QAAQ;KAAE,QAAQ;KAAM,QAAQ;IAAG,CAAC;IAC1D,QAAQ,MAAM,QAAQ,YAAY,EAAA,CAAG,UAAU,CAAC,CAAC,KAAK,EAAE;IAOxD,MAAM,OAJW,SACf,MAAM,MAAM,IAAI,cAAc,EAAE,OAAO;KAAE,QAAQ;KAAI,QAAQ;IAAE,EAAE,CAAC,GAClE,YAEW,CAAA,CAAS;IACtB,IAAI,MACF,OAAO,MAAM,YAAY,IAAI,CAAC,CAAC,CAAC,QAAQ,MAAM,MAAM,IAAI,EAAE,CAAC;IAK7D,MAAM,QAAQ,SAAS,MAAM,MAAM,IAAI,YAAY,GAAG,YAAY;IAClE,OAAO,MAAM,KAAK,CAAC,CAAC,cAAc;GACpC,CAAC;GAED,GAAG,mDAAmD,YAAY;IAChE,MAAM,QAAQ,aAAa,OAAO;IAClC,IAAI,CAAC,OAAO;IAEZ,MAAM,QAAQ,MAAM,MAAM,IAAI,aAAa,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;KAC9D,aAAa;KACb,gBAAgB,EAAE,GAAG,IAAI;IAC3B,CAAC;IACD,MAAM,SAAS,MAAM,MAAM,IAAI,aAAa,IAAI,WAAW;KAAC;KAAG;KAAG;IAAC,CAAC,GAAG;KACrE,aAAa;KACb,gBAAgB,EAAE,GAAG,IAAI;IAC3B,CAAC;IAED,OAAO,MAAM,CAAC,CAAC,cAAc;KAC3B,MAAM;KACN,aAAa;KACb,gBAAgB,EAAE,GAAG,IAAI;IAC3B,CAAC;IAED,IAAI,MAAM,SAAS,KAAA,KAAa,OAAO,SAAS,KAAA,GAC9C,OAAO,OAAO,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI;IAEzC,MAAM,QAAQ,SAAS,MAAM,MAAM,IAAI,WAAW,GAAG,WAAW;IAChE,OAAO,IAAI,WAAW,MAAM,MAAM,YAAY,CAAC,CAAC,CAAC,CAAC,QAChD,IAAI,WAAW;KAAC;KAAG;KAAG;IAAC,CAAC,CAC1B;IAEA,MAAM,MAAM,OAAO,WAAW;IAC9B,OAAO,MAAM,MAAM,IAAI,WAAW,CAAC,CAAC,CAAC,SAAS;IAC9C,OAAO,MAAM,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS;IAE/C,MAAM,MAAM,OAAO,WAAW;GAChC,CAAC;GAED,GAAG,kDAAkD,YAAY;IAC/D,MAAM,QAAQ,aAAa,OAAO;IAClC,IAAI,CAAC,OAAO;IAKZ,MAAM,MAAM,IAAI,WAAW,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;IAC9C,MAAM,MAAM,IAAI,WAAW,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;IAC9C,MAAM,MAAM,IAAI,WAAW,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;IAC9C,MAAM,MAAM,IAAI,YAAY,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;IAC/C,MAAM,MAAM,IAAI,WAAW,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;IAE9C,MAAM,OAAO,MAAM,MAAM,KAAK,EAAE,QAAQ,SAAS,CAAC;IAClD,OAAO,KAAK,QAAQ,KAAK,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ;KAC7C;KACA;KACA;IACF,CAAC;IACD,OAAO,KAAK,SAAS,CAAC,CAAC,UAAU;IAEjC,QACG,MAAM,MAAM,KAAK,EAAE,QAAQ,qBAAqB,CAAC,EAAA,CAAG,OACvD,CAAC,CAAC,QAAQ,CAAC,CAAC;GACd,CAAC;GAED,GAAG,6DAA6D,YAAY;IAC1E,MAAM,QAAQ,aAAa,OAAO;IAClC,IAAI,CAAC,OAAO;IAEZ,KAAK,MAAM,OAAO;KAAC;KAAU;KAAU;KAAU;KAAU;IAAQ,GACjE,MAAM,MAAM,IAAI,KAAK,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;IAG1C,MAAM,QAAQ,MAAM,MAAM,KAAK;KAAE,QAAQ;KAAS,OAAO;IAAE,CAAC;IAC5D,OAAO,MAAM,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC;IAChC,OAAO,MAAM,SAAS,CAAC,CAAC,UAAU;IAClC,OAAO,MAAM,MAAM,CAAC,CAAC,cAAc;IAInC,MAAM,OAAsB,CAAC;IAC7B,IAAI;IACJ,KAAK,IAAI,QAAQ,GAAG,QAAQ,IAAI,SAAS;KACvC,MAAM,SAAS,MAAM,MAAM,KAAK;MAC9B,QAAQ;MACR,OAAO;MACP,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;KAC3C,CAAC;KACD,KAAK,KAAK,GAAG,OAAO,QAAQ,KAAK,MAAM,EAAE,GAAG,CAAC;KAC7C,IAAI,CAAC,OAAO,WAAW;KACvB,OAAO,OAAO,MAAM,CAAC,CAAC,KAAK,OAAO,QAAQ,GAAG,EAAE,CAAC,EAAE,GAAG;KACrD,SAAS,OAAO;IAClB;IACA,OAAO,IAAI,CAAC,CAAC,QAAQ;KAAC;KAAU;KAAU;KAAU;KAAU;IAAQ,CAAC;IAGvE,MAAM,QAAQ,MAAM,MAAM,KAAK;KAAE,QAAQ;KAAS,OAAO;IAAE,CAAC;IAC5D,OAAO,MAAM,QAAQ,KAAK,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,IAAI;IACpD,OAAO,MAAM,SAAS,CAAC,CAAC,UAAU;GACpC,CAAC;EACH,CAAC;EAED,SAAS,kBAAkB;GACzB,GAAG,wEAAwE,OAAO,QAAQ;IACxF,MAAM,QAAQ,aAAa,UAAU;IACrC,IAAI,CAAC,OAAO,OAAO,IAAI,KAAK,oBAAoB;IAEhD,OAAO,MAAM,MAAM,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC,SAAS;IAEjD,MAAM,MAAM,IAAI,WAAW,KAAK,EAAE,GAAG,EAAE,CAAC;IACxC,MAAM,MAAM,IAAI,WAAW,KAAK,EAAE,GAAG,EAAE,CAAC;IACxC,OAAO,MAAM,MAAM,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC;IACxD,OAAO,MAAM,MAAM,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC;IAExD,MAAM,MAAM,IAAI,WAAW,KAAK,EAAE,GAAG,EAAE,CAAC;IACxC,OAAO,MAAM,MAAM,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC;IAExD,MAAM,MAAM,OAAO,WAAW,GAAG;IACjC,OAAO,MAAM,MAAM,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC,SAAS;IAEjD,OAAO,MAAM,MAAM,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC;IAIxD,MAAM,MAAM,IAAI,OAAO,KAAK,MAAM;IAClC,MAAM,MAAM,IAAI,KAAK,OAAO,OAAO;IACnC,OAAO,MAAM,MAAM,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,KAAK,MAAM;IAC/C,OAAO,MAAM,MAAM,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO;IAChD,MAAM,MAAM,OAAO,OAAO,GAAG;IAC7B,OAAO,MAAM,MAAM,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,SAAS;IAC7C,OAAO,MAAM,MAAM,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO;GAClD,CAAC;EACH,CAAC;CACH,CAAC;AACH"}