@copilotkit/runtime
Version:
<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />
1 lines • 25.7 kB
Source Map (JSON)
{"version":3,"file":"tanstack.cjs","names":["EventType"],"sources":["../../../src/agent/converters/tanstack.ts"],"sourcesContent":["import type {\n BaseEvent,\n RunAgentInput,\n Message,\n TextMessageChunkEvent,\n ToolCallArgsEvent,\n ToolCallEndEvent,\n ToolCallStartEvent,\n ToolCallResultEvent,\n StateSnapshotEvent,\n StateDeltaEvent,\n ReasoningStartEvent,\n ReasoningMessageStartEvent,\n ReasoningMessageContentEvent,\n ReasoningMessageEndEvent,\n ReasoningEndEvent,\n} from \"@ag-ui/client\";\nimport { EventType } from \"@ag-ui/client\";\nimport { randomUUID } from \"@copilotkit/shared\";\n\ntype ContentPartSource =\n | { type: \"data\"; value: string; mimeType: string }\n | { type: \"url\"; value: string; mimeType?: string };\n\n/**\n * A TanStack AI content part (text, image, audio, video, or document).\n */\nexport type TanStackContentPart =\n | { type: \"text\"; content: string }\n | { type: \"image\"; source: ContentPartSource }\n | { type: \"audio\"; source: ContentPartSource }\n | { type: \"video\"; source: ContentPartSource }\n | { type: \"document\"; source: ContentPartSource };\n\n/**\n * Message format expected by TanStack AI's `chat()`.\n *\n * Content is typed as `any[]` for the multimodal case so messages are directly\n * passable to any adapter without casts — different adapters constrain which\n * modalities they accept (e.g. OpenAI only allows text + image).\n * Use `TanStackContentPart` to inspect individual parts if needed.\n */\nexport interface TanStackChatMessage {\n role: \"user\" | \"assistant\" | \"tool\";\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n content: string | null | any[];\n name?: string;\n toolCalls?: Array<{\n id: string;\n type: \"function\";\n function: { name: string; arguments: string };\n }>;\n toolCallId?: string;\n}\n\n/**\n * A TanStack AI client-side tool, derived from a frontend-provided AG-UI tool.\n *\n * Shaped to match `@tanstack/ai`'s `ClientTool` (`__toolSide: \"client\"`, no\n * `execute`): the model may CALL it, but TanStack does not run it — it pauses\n * the run and hands the call back to the AG-UI client (the CopilotKit frontend\n * / bot) to execute, mirroring CopilotKit's client-tool round-trip. `chat()`\n * accepts a JSON Schema directly as `inputSchema`, so the AG-UI tool's\n * `parameters` pass through unchanged.\n */\nexport interface TanStackClientTool {\n __toolSide: \"client\";\n name: string;\n description: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n inputSchema: any;\n}\n\n/**\n * Result of converting RunAgentInput to TanStack AI format.\n */\nexport interface TanStackInputResult {\n /** Chat messages (only user/assistant/tool roles; all others excluded) */\n messages: TanStackChatMessage[];\n /** System prompts extracted from system/developer messages, context, and state */\n systemPrompts: string[];\n /**\n * Client-side tools derived from `input.tools` (the frontend-provided tools\n * the CopilotKit client forwards on every run). Pass these into `chat()`\n * alongside any server/provider tools so the model can call the frontend's\n * generative-UI and human-in-the-loop tools; TanStack pauses the run on a\n * client-tool call and the client executes it.\n */\n tools: TanStackClientTool[];\n}\n\n/**\n * Converts AG-UI user message content to TanStack AI format.\n * Handles plain strings, multimodal parts (image/audio/video/document),\n * and legacy BinaryInputContent for backward compatibility.\n */\nfunction convertUserContent(\n content: unknown,\n): string | null | TanStackContentPart[] {\n if (!content) return null;\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return null;\n if (content.length === 0) return \"\";\n\n const parts: TanStackContentPart[] = [];\n\n for (const part of content) {\n if (!part || typeof part !== \"object\" || !(\"type\" in part)) continue;\n\n switch ((part as { type: string }).type) {\n case \"text\": {\n const text = (part as { text?: string }).text;\n if (text != null) parts.push({ type: \"text\", content: text });\n break;\n }\n\n case \"image\":\n case \"audio\":\n case \"video\":\n case \"document\": {\n const source = (part as { source?: any }).source;\n if (!source) break;\n const partType = (part as { type: string }).type as\n | \"image\"\n | \"audio\"\n | \"video\"\n | \"document\";\n if (source.type === \"data\") {\n parts.push({\n type: partType,\n source: {\n type: \"data\",\n value: source.value,\n mimeType: source.mimeType,\n },\n });\n } else if (source.type === \"url\") {\n parts.push({\n type: partType,\n source: {\n type: \"url\",\n value: source.value,\n ...(source.mimeType ? { mimeType: source.mimeType } : {}),\n },\n });\n }\n break;\n }\n\n // Legacy BinaryInputContent backward compatibility\n case \"binary\": {\n const legacy = part as {\n mimeType?: string;\n data?: string;\n url?: string;\n };\n const mimeType = legacy.mimeType ?? \"application/octet-stream\";\n const isImage = mimeType.startsWith(\"image/\");\n\n if (legacy.data) {\n const partType = isImage ? \"image\" : \"document\";\n parts.push({\n type: partType,\n source: { type: \"data\", value: legacy.data, mimeType },\n });\n } else if (legacy.url) {\n const partType = isImage ? \"image\" : \"document\";\n parts.push({\n type: partType,\n source: { type: \"url\", value: legacy.url, mimeType },\n });\n }\n break;\n }\n }\n }\n\n return parts.length > 0 ? parts : \"\";\n}\n\n/**\n * Recursively normalizes a frontend tool's JSON Schema so OpenAI accepts it as\n * a function-tool schema.\n *\n * Frontend tools are often authored with permissive Zod (`z.any()`,\n * `z.record(...)`, `.passthrough()`), which serialize to open objects —\n * `additionalProperties: {}` (an empty sub-schema) or `additionalProperties:\n * true`. OpenAI rejects both: strict mode requires `additionalProperties:\n * false`, and an empty `{}` sub-schema fails base validation (\"schema must\n * have a 'type' key\"). The classic (Vercel AI SDK) path sanitized these\n * implicitly via a Zod round-trip; the TanStack path forwards the raw schema,\n * so we close open objects here to match. (Models can't supply free-form extra\n * keys either way — same as the classic path.)\n */\nfunction sanitizeClientToolSchema(schema: unknown): unknown {\n if (Array.isArray(schema)) {\n return schema.map(sanitizeClientToolSchema);\n }\n if (!schema || typeof schema !== \"object\") {\n return schema;\n }\n const node: Record<string, unknown> = {\n ...(schema as Record<string, unknown>),\n };\n\n // Any `additionalProperties` (empty `{}`, `true`, or a sub-schema) becomes\n // `false` — the only form OpenAI accepts for strict function tools.\n if (\"additionalProperties\" in node) {\n node.additionalProperties = false;\n }\n\n if (node.properties && typeof node.properties === \"object\") {\n const props: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(\n node.properties as Record<string, unknown>,\n )) {\n props[key] = sanitizeClientToolSchema(value);\n }\n node.properties = props;\n }\n\n if (\"items\" in node) {\n node.items = sanitizeClientToolSchema(node.items);\n }\n\n for (const combinator of [\"anyOf\", \"allOf\", \"oneOf\"] as const) {\n if (Array.isArray(node[combinator])) {\n node[combinator] = (node[combinator] as unknown[]).map(\n sanitizeClientToolSchema,\n );\n }\n }\n\n return node;\n}\n\n/**\n * Converts a RunAgentInput into the format expected by TanStack AI's `chat()`.\n *\n * - Keeps only user/assistant/tool messages (activity, reasoning, and other roles are also excluded)\n * - Extracts system/developer messages into `systemPrompts`\n * - Appends context entries and application state to `systemPrompts`\n * - Preserves tool calls on assistant messages and toolCallId on tool messages\n */\nexport function convertInputToTanStackAI(\n input: RunAgentInput,\n): TanStackInputResult {\n // Allowlist: only pass user/assistant/tool messages to TanStack.\n // Other roles (system, developer, activity, reasoning) are either\n // extracted into systemPrompts or not applicable.\n const chatRoles = new Set([\"user\", \"assistant\", \"tool\"]);\n const messages: TanStackChatMessage[] = input.messages\n .filter((m: Message) => chatRoles.has(m.role))\n .map((m: Message): TanStackChatMessage => {\n const msg: TanStackChatMessage = {\n role: m.role as \"user\" | \"assistant\" | \"tool\",\n content:\n m.role === \"user\"\n ? convertUserContent(m.content)\n : typeof m.content === \"string\"\n ? m.content\n : null,\n };\n if (m.role === \"assistant\" && \"toolCalls\" in m && m.toolCalls) {\n msg.toolCalls = m.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n }));\n }\n if (m.role === \"tool\" && \"toolCallId\" in m) {\n msg.toolCallId = (m as Record<string, unknown>).toolCallId as string;\n }\n return msg;\n });\n\n const systemPrompts: string[] = [];\n for (const m of input.messages) {\n if ((m.role === \"system\" || m.role === \"developer\") && m.content) {\n systemPrompts.push(\n typeof m.content === \"string\" ? m.content : JSON.stringify(m.content),\n );\n }\n }\n\n if (input.context?.length) {\n for (const ctx of input.context) {\n systemPrompts.push(`${ctx.description}:\\n${ctx.value}`);\n }\n }\n\n if (\n input.state !== undefined &&\n input.state !== null &&\n typeof input.state === \"object\" &&\n Object.keys(input.state).length > 0\n ) {\n systemPrompts.push(\n `Application State:\\n\\`\\`\\`json\\n${JSON.stringify(input.state, null, 2)}\\n\\`\\`\\``,\n );\n }\n\n // Frontend-provided tools become client-side TanStack tools (no executor):\n // the model can call them, TanStack pauses the run, and the AG-UI client\n // executes them and resumes — the CopilotKit client-tool round-trip.\n const tools: TanStackClientTool[] = (input.tools ?? []).map((t) => ({\n __toolSide: \"client\",\n name: t.name,\n description: t.description,\n inputSchema: sanitizeClientToolSchema(t.parameters),\n }));\n\n return { messages, systemPrompts, tools };\n}\n\n/**\n * Converts a TanStack AI stream into AG-UI `BaseEvent` objects.\n *\n * This is a pure converter — it does NOT emit lifecycle events\n * (RUN_STARTED / RUN_FINISHED / RUN_ERROR). The caller (Agent class)\n * is responsible for those.\n */\nexport async function* convertTanStackStream(\n stream: AsyncIterable<unknown>,\n abortSignal: AbortSignal,\n): AsyncGenerator<BaseEvent> {\n const messageId = randomUUID();\n const toolNamesById = new Map<string, string>();\n // Track the reasoning lifecycle at two granularities so closeReasoningIfOpen\n // emits exactly the events still owed. A single boolean conflates the run\n // (REASONING_START → REASONING_END) with the message\n // (REASONING_MESSAGE_START → REASONING_MESSAGE_END) and produces a duplicate\n // REASONING_MESSAGE_END when upstream emits MSG_END but not END before\n // text/tools resume.\n let reasoningRunOpen = false;\n let reasoningMessageOpen = false;\n let reasoningMessageId = randomUUID();\n\n function* closeReasoningIfOpen(): Generator<BaseEvent> {\n if (reasoningMessageOpen) {\n reasoningMessageOpen = false;\n const msgEnd: ReasoningMessageEndEvent = {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n };\n yield msgEnd;\n }\n if (reasoningRunOpen) {\n reasoningRunOpen = false;\n const end: ReasoningEndEvent = {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n };\n yield end;\n }\n }\n\n // TanStack's chat() engine runs a multi-turn agent loop and emits a\n // RUN_STARTED / RUN_FINISHED pair PER model turn — not once for the whole\n // run. When it executes a tool itself (an MCP server tool or a provider tool\n // like web_search), it does so between turns and streams a TOOL_CALL_RESULT\n // followed by the next turn's text. The overall run lifecycle is owned by the\n // Agent wrapper (it emits exactly one outer RUN_STARTED / RUN_FINISHED), so\n // we drop TanStack's per-turn lifecycle markers and convert every content\n // event across all turns. (A previous version stopped converting at the first\n // RUN_FINISHED — that truncated the run at the first tool turn and silently\n // dropped both the tool result and the model's final answer.)\n //\n // chat() can re-announce a tool call when it re-prompts after executing it,\n // so START / END are de-duplicated by toolCallId to avoid emitting a pair\n // twice (which would violate the ag-ui verify middleware).\n const startedToolCalls = new Set<string>();\n const endedToolCalls = new Set<string>();\n\n for await (const chunk of stream) {\n if (abortSignal.aborted) break;\n\n const raw = chunk as Record<string, unknown>;\n const type = raw.type as string;\n\n // Per-turn lifecycle markers are owned by the Agent wrapper, not forwarded.\n if (type === \"RUN_STARTED\" || type === \"RUN_FINISHED\") {\n continue;\n }\n\n // Surface engine errors instead of dropping them: throw so the Agent\n // wrapper emits a terminal RUN_ERROR. Without this a failed run (e.g. a\n // provider 4xx) would finish empty with no indication of what went wrong.\n if (type === \"RUN_ERROR\") {\n throw new Error(\n typeof raw.message === \"string\" ? raw.message : \"TanStack AI run error\",\n );\n }\n\n if (type === \"TEXT_MESSAGE_CONTENT\" && raw.delta != null) {\n yield* closeReasoningIfOpen();\n const textEvent: TextMessageChunkEvent = {\n type: EventType.TEXT_MESSAGE_CHUNK,\n role: \"assistant\",\n messageId,\n delta: raw.delta as string,\n };\n yield textEvent;\n } else if (type === \"TOOL_CALL_START\") {\n const toolCallId = raw.toolCallId as string;\n if (startedToolCalls.has(toolCallId)) continue;\n startedToolCalls.add(toolCallId);\n yield* closeReasoningIfOpen();\n toolNamesById.set(toolCallId, raw.toolCallName as string);\n const startEvent: ToolCallStartEvent = {\n type: EventType.TOOL_CALL_START,\n parentMessageId: messageId,\n toolCallId,\n toolCallName: raw.toolCallName as string,\n };\n yield startEvent;\n } else if (type === \"TOOL_CALL_ARGS\") {\n // Drop args re-announced after the call has ended (the re-prompt pass);\n // forwarding them would corrupt the already-closed call's accumulated args.\n if (endedToolCalls.has(raw.toolCallId as string)) continue;\n yield* closeReasoningIfOpen();\n const argsEvent: ToolCallArgsEvent = {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId: raw.toolCallId as string,\n delta: raw.delta as string,\n };\n yield argsEvent;\n } else if (type === \"TOOL_CALL_END\") {\n const toolCallId = raw.toolCallId as string;\n if (endedToolCalls.has(toolCallId)) continue;\n endedToolCalls.add(toolCallId);\n yield* closeReasoningIfOpen();\n const endEvent: ToolCallEndEvent = {\n type: EventType.TOOL_CALL_END,\n toolCallId,\n };\n yield endEvent;\n } else if (type === \"TOOL_CALL_RESULT\") {\n yield* closeReasoningIfOpen();\n const toolCallId = raw.toolCallId as string;\n const toolName = toolNamesById.get(toolCallId);\n // Accept the payload from either `content` (canonical TanStack shape)\n // or `result` (alternate shape used by some adapters / tests). Both\n // state-tool detection and the final TOOL_CALL_RESULT serialization\n // must read the same field, otherwise STATE_SNAPSHOT/STATE_DELTA can\n // be silently dropped when upstream uses `result`.\n const rawPayload = raw.content ?? raw.result;\n\n const parsedContent =\n typeof rawPayload === \"string\" ? safeParse(rawPayload) : rawPayload;\n\n if (\n toolName === \"AGUISendStateSnapshot\" &&\n parsedContent &&\n typeof parsedContent === \"object\" &&\n \"snapshot\" in parsedContent\n ) {\n const stateSnapshotEvent: StateSnapshotEvent = {\n type: EventType.STATE_SNAPSHOT,\n snapshot: (parsedContent as Record<string, unknown>).snapshot,\n };\n yield stateSnapshotEvent;\n }\n\n if (\n toolName === \"AGUISendStateDelta\" &&\n parsedContent &&\n typeof parsedContent === \"object\" &&\n \"delta\" in parsedContent\n ) {\n const stateDeltaEvent: StateDeltaEvent = {\n type: EventType.STATE_DELTA,\n delta: (parsedContent as Record<string, unknown>).delta as never,\n };\n yield stateDeltaEvent;\n }\n\n let serializedContent: string;\n if (typeof rawPayload === \"string\") {\n serializedContent = rawPayload;\n } else {\n try {\n serializedContent = JSON.stringify(rawPayload ?? null);\n } catch {\n serializedContent = \"[Unserializable tool result]\";\n }\n }\n\n const resultEvent: ToolCallResultEvent = {\n type: EventType.TOOL_CALL_RESULT,\n role: \"tool\",\n messageId: randomUUID(),\n toolCallId,\n content: serializedContent,\n };\n yield resultEvent;\n toolNamesById.delete(toolCallId);\n } else if (type === \"REASONING_START\") {\n // If a prior reasoning run is still open (no REASONING_END before this\n // new START), close it cleanly first so MSG_END / END pair correctly.\n yield* closeReasoningIfOpen();\n reasoningRunOpen = true;\n reasoningMessageId = (raw.messageId as string) ?? randomUUID();\n const startEvt: ReasoningStartEvent = {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n };\n yield startEvt;\n } else if (type === \"REASONING_MESSAGE_START\") {\n reasoningMessageOpen = true;\n const evt: ReasoningMessageStartEvent = {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: \"reasoning\",\n };\n yield evt;\n } else if (type === \"REASONING_MESSAGE_CONTENT\") {\n const evt: ReasoningMessageContentEvent = {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningMessageId,\n delta: raw.delta as string,\n };\n yield evt;\n } else if (type === \"REASONING_MESSAGE_END\") {\n reasoningMessageOpen = false;\n const evt: ReasoningMessageEndEvent = {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n };\n yield evt;\n } else if (type === \"REASONING_END\") {\n // If upstream sends REASONING_END while a message is still open, emit\n // the missing REASONING_MESSAGE_END FIRST so the closing pair stays in\n // order (MSG_END before END). Otherwise the next non-reasoning chunk\n // would trigger closeReasoningIfOpen and emit MSG_END after END.\n if (reasoningMessageOpen) {\n reasoningMessageOpen = false;\n const msgEnd: ReasoningMessageEndEvent = {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n };\n yield msgEnd;\n }\n reasoningRunOpen = false;\n const evt: ReasoningEndEvent = {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n };\n yield evt;\n }\n }\n\n yield* closeReasoningIfOpen();\n}\n\nfunction safeParse(value: string): unknown {\n try {\n return JSON.parse(value);\n } catch {\n return value;\n }\n}\n"],"mappings":";;;;;;;;;;;AAgGA,SAAS,mBACP,SACuC;AACvC,KAAI,CAAC,QAAS,QAAO;AACrB,KAAI,OAAO,YAAY,SAAU,QAAO;AACxC,KAAI,CAAC,MAAM,QAAQ,QAAQ,CAAE,QAAO;AACpC,KAAI,QAAQ,WAAW,EAAG,QAAO;CAEjC,MAAM,QAA+B,EAAE;AAEvC,MAAK,MAAM,QAAQ,SAAS;AAC1B,MAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,EAAE,UAAU,MAAO;AAE5D,UAAS,KAA0B,MAAnC;GACE,KAAK,QAAQ;IACX,MAAM,OAAQ,KAA2B;AACzC,QAAI,QAAQ,KAAM,OAAM,KAAK;KAAE,MAAM;KAAQ,SAAS;KAAM,CAAC;AAC7D;;GAGF,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,YAAY;IACf,MAAM,SAAU,KAA0B;AAC1C,QAAI,CAAC,OAAQ;IACb,MAAM,WAAY,KAA0B;AAK5C,QAAI,OAAO,SAAS,OAClB,OAAM,KAAK;KACT,MAAM;KACN,QAAQ;MACN,MAAM;MACN,OAAO,OAAO;MACd,UAAU,OAAO;MAClB;KACF,CAAC;aACO,OAAO,SAAS,MACzB,OAAM,KAAK;KACT,MAAM;KACN,QAAQ;MACN,MAAM;MACN,OAAO,OAAO;MACd,GAAI,OAAO,WAAW,EAAE,UAAU,OAAO,UAAU,GAAG,EAAE;MACzD;KACF,CAAC;AAEJ;;GAIF,KAAK,UAAU;IACb,MAAM,SAAS;IAKf,MAAM,WAAW,OAAO,YAAY;IACpC,MAAM,UAAU,SAAS,WAAW,SAAS;AAE7C,QAAI,OAAO,MAAM;KACf,MAAM,WAAW,UAAU,UAAU;AACrC,WAAM,KAAK;MACT,MAAM;MACN,QAAQ;OAAE,MAAM;OAAQ,OAAO,OAAO;OAAM;OAAU;MACvD,CAAC;eACO,OAAO,KAAK;KACrB,MAAM,WAAW,UAAU,UAAU;AACrC,WAAM,KAAK;MACT,MAAM;MACN,QAAQ;OAAE,MAAM;OAAO,OAAO,OAAO;OAAK;OAAU;MACrD,CAAC;;AAEJ;;;;AAKN,QAAO,MAAM,SAAS,IAAI,QAAQ;;;;;;;;;;;;;;;;AAiBpC,SAAS,yBAAyB,QAA0B;AAC1D,KAAI,MAAM,QAAQ,OAAO,CACvB,QAAO,OAAO,IAAI,yBAAyB;AAE7C,KAAI,CAAC,UAAU,OAAO,WAAW,SAC/B,QAAO;CAET,MAAM,OAAgC,EACpC,GAAI,QACL;AAID,KAAI,0BAA0B,KAC5B,MAAK,uBAAuB;AAG9B,KAAI,KAAK,cAAc,OAAO,KAAK,eAAe,UAAU;EAC1D,MAAM,QAAiC,EAAE;AACzC,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAChC,KAAK,WACN,CACC,OAAM,OAAO,yBAAyB,MAAM;AAE9C,OAAK,aAAa;;AAGpB,KAAI,WAAW,KACb,MAAK,QAAQ,yBAAyB,KAAK,MAAM;AAGnD,MAAK,MAAM,cAAc;EAAC;EAAS;EAAS;EAAQ,CAClD,KAAI,MAAM,QAAQ,KAAK,YAAY,CACjC,MAAK,cAAe,KAAK,YAA0B,IACjD,yBACD;AAIL,QAAO;;;;;;;;;;AAWT,SAAgB,yBACd,OACqB;CAIrB,MAAM,YAAY,IAAI,IAAI;EAAC;EAAQ;EAAa;EAAO,CAAC;CACxD,MAAM,WAAkC,MAAM,SAC3C,QAAQ,MAAe,UAAU,IAAI,EAAE,KAAK,CAAC,CAC7C,KAAK,MAAoC;EACxC,MAAM,MAA2B;GAC/B,MAAM,EAAE;GACR,SACE,EAAE,SAAS,SACP,mBAAmB,EAAE,QAAQ,GAC7B,OAAO,EAAE,YAAY,WACnB,EAAE,UACF;GACT;AACD,MAAI,EAAE,SAAS,eAAe,eAAe,KAAK,EAAE,UAClD,KAAI,YAAY,EAAE,UAAU,KAAK,QAAQ;GACvC,IAAI,GAAG;GACP,MAAM;GACN,UAAU;IACR,MAAM,GAAG,SAAS;IAClB,WAAW,GAAG,SAAS;IACxB;GACF,EAAE;AAEL,MAAI,EAAE,SAAS,UAAU,gBAAgB,EACvC,KAAI,aAAc,EAA8B;AAElD,SAAO;GACP;CAEJ,MAAM,gBAA0B,EAAE;AAClC,MAAK,MAAM,KAAK,MAAM,SACpB,MAAK,EAAE,SAAS,YAAY,EAAE,SAAS,gBAAgB,EAAE,QACvD,eAAc,KACZ,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU,KAAK,UAAU,EAAE,QAAQ,CACtE;AAIL,KAAI,MAAM,SAAS,OACjB,MAAK,MAAM,OAAO,MAAM,QACtB,eAAc,KAAK,GAAG,IAAI,YAAY,KAAK,IAAI,QAAQ;AAI3D,KACE,MAAM,UAAU,UAChB,MAAM,UAAU,QAChB,OAAO,MAAM,UAAU,YACvB,OAAO,KAAK,MAAM,MAAM,CAAC,SAAS,EAElC,eAAc,KACZ,mCAAmC,KAAK,UAAU,MAAM,OAAO,MAAM,EAAE,CAAC,UACzE;AAaH,QAAO;EAAE;EAAU;EAAe,QAPG,MAAM,SAAS,EAAE,EAAE,KAAK,OAAO;GAClE,YAAY;GACZ,MAAM,EAAE;GACR,aAAa,EAAE;GACf,aAAa,yBAAyB,EAAE,WAAW;GACpD,EAAE;EAEsC;;;;;;;;;AAU3C,gBAAuB,sBACrB,QACA,aAC2B;CAC3B,MAAM,gDAAwB;CAC9B,MAAM,gCAAgB,IAAI,KAAqB;CAO/C,IAAI,mBAAmB;CACvB,IAAI,uBAAuB;CAC3B,IAAI,yDAAiC;CAErC,UAAU,uBAA6C;AACrD,MAAI,sBAAsB;AACxB,0BAAuB;AAKvB,SAJyC;IACvC,MAAMA,wBAAU;IAChB,WAAW;IACZ;;AAGH,MAAI,kBAAkB;AACpB,sBAAmB;AAKnB,SAJ+B;IAC7B,MAAMA,wBAAU;IAChB,WAAW;IACZ;;;CAmBL,MAAM,mCAAmB,IAAI,KAAa;CAC1C,MAAM,iCAAiB,IAAI,KAAa;AAExC,YAAW,MAAM,SAAS,QAAQ;AAChC,MAAI,YAAY,QAAS;EAEzB,MAAM,MAAM;EACZ,MAAM,OAAO,IAAI;AAGjB,MAAI,SAAS,iBAAiB,SAAS,eACrC;AAMF,MAAI,SAAS,YACX,OAAM,IAAI,MACR,OAAO,IAAI,YAAY,WAAW,IAAI,UAAU,wBACjD;AAGH,MAAI,SAAS,0BAA0B,IAAI,SAAS,MAAM;AACxD,UAAO,sBAAsB;AAO7B,SANyC;IACvC,MAAMA,wBAAU;IAChB,MAAM;IACN;IACA,OAAO,IAAI;IACZ;aAEQ,SAAS,mBAAmB;GACrC,MAAM,aAAa,IAAI;AACvB,OAAI,iBAAiB,IAAI,WAAW,CAAE;AACtC,oBAAiB,IAAI,WAAW;AAChC,UAAO,sBAAsB;AAC7B,iBAAc,IAAI,YAAY,IAAI,aAAuB;AAOzD,SANuC;IACrC,MAAMA,wBAAU;IAChB,iBAAiB;IACjB;IACA,cAAc,IAAI;IACnB;aAEQ,SAAS,kBAAkB;AAGpC,OAAI,eAAe,IAAI,IAAI,WAAqB,CAAE;AAClD,UAAO,sBAAsB;AAM7B,SALqC;IACnC,MAAMA,wBAAU;IAChB,YAAY,IAAI;IAChB,OAAO,IAAI;IACZ;aAEQ,SAAS,iBAAiB;GACnC,MAAM,aAAa,IAAI;AACvB,OAAI,eAAe,IAAI,WAAW,CAAE;AACpC,kBAAe,IAAI,WAAW;AAC9B,UAAO,sBAAsB;AAK7B,SAJmC;IACjC,MAAMA,wBAAU;IAChB;IACD;aAEQ,SAAS,oBAAoB;AACtC,UAAO,sBAAsB;GAC7B,MAAM,aAAa,IAAI;GACvB,MAAM,WAAW,cAAc,IAAI,WAAW;GAM9C,MAAM,aAAa,IAAI,WAAW,IAAI;GAEtC,MAAM,gBACJ,OAAO,eAAe,WAAW,UAAU,WAAW,GAAG;AAE3D,OACE,aAAa,2BACb,iBACA,OAAO,kBAAkB,YACzB,cAAc,cAMd,OAJ+C;IAC7C,MAAMA,wBAAU;IAChB,UAAW,cAA0C;IACtD;AAIH,OACE,aAAa,wBACb,iBACA,OAAO,kBAAkB,YACzB,WAAW,cAMX,OAJyC;IACvC,MAAMA,wBAAU;IAChB,OAAQ,cAA0C;IACnD;GAIH,IAAI;AACJ,OAAI,OAAO,eAAe,SACxB,qBAAoB;OAEpB,KAAI;AACF,wBAAoB,KAAK,UAAU,cAAc,KAAK;WAChD;AACN,wBAAoB;;AAWxB,SAPyC;IACvC,MAAMA,wBAAU;IAChB,MAAM;IACN,+CAAuB;IACvB;IACA,SAAS;IACV;AAED,iBAAc,OAAO,WAAW;aACvB,SAAS,mBAAmB;AAGrC,UAAO,sBAAsB;AAC7B,sBAAmB;AACnB,wBAAsB,IAAI,iDAAoC;AAK9D,SAJsC;IACpC,MAAMA,wBAAU;IAChB,WAAW;IACZ;aAEQ,SAAS,2BAA2B;AAC7C,0BAAuB;AAMvB,SALwC;IACtC,MAAMA,wBAAU;IAChB,WAAW;IACX,MAAM;IACP;aAEQ,SAAS,4BAMlB,OAL0C;GACxC,MAAMA,wBAAU;GAChB,WAAW;GACX,OAAO,IAAI;GACZ;WAEQ,SAAS,yBAAyB;AAC3C,0BAAuB;AAKvB,SAJsC;IACpC,MAAMA,wBAAU;IAChB,WAAW;IACZ;aAEQ,SAAS,iBAAiB;AAKnC,OAAI,sBAAsB;AACxB,2BAAuB;AAKvB,UAJyC;KACvC,MAAMA,wBAAU;KAChB,WAAW;KACZ;;AAGH,sBAAmB;AAKnB,SAJ+B;IAC7B,MAAMA,wBAAU;IAChB,WAAW;IACZ;;;AAKL,QAAO,sBAAsB;;AAG/B,SAAS,UAAU,OAAwB;AACzC,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN,SAAO"}