langchain
Version:
Typescript bindings for langchain
1 lines • 30.1 kB
Source Map (JSON)
{"version":3,"file":"hitl.cjs","names":["z","options: NonNullable<HumanInTheLoopMiddlewareConfig>","toolCall: ToolCall","config: InterruptOnConfig","state: AgentBuiltInState","runtime: Runtime<unknown>","description: string","actionRequest: ActionRequest","reviewConfig: ReviewConfig","decision: Decision","ToolMessage","createMiddleware","AIMessage","resolvedConfigs: Record<string, InterruptOnConfig>","interruptToolCalls: ToolCall[]","autoApprovedToolCalls: ToolCall[]","actionRequests: ActionRequest[]","reviewConfigs: ReviewConfig[]","hitlRequest: HITLRequest","revisedToolCalls: ToolCall[]","artificialToolMessages: ToolMessage[]","jumpTo: JumpToTarget | undefined"],"sources":["../../../src/agents/middleware/hitl.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { z } from \"zod/v3\";\nimport { AIMessage, ToolMessage, ToolCall } from \"@langchain/core/messages\";\nimport {\n InferInteropZodInput,\n interopParse,\n} from \"@langchain/core/utils/types\";\nimport { interrupt } from \"@langchain/langgraph\";\n\nimport { createMiddleware } from \"../middleware.js\";\nimport type { AgentBuiltInState, Runtime } from \"../runtime.js\";\nimport type { JumpToTarget } from \"../constants.js\";\n\nconst DescriptionFunctionSchema = z\n .function()\n .args(\n z.custom<ToolCall>(), // toolCall\n z.custom<AgentBuiltInState>(), // state\n z.custom<Runtime<unknown>>() // runtime\n )\n .returns(z.union([z.string(), z.promise(z.string())]));\n\n/**\n * Function type that dynamically generates a description for a tool call approval request.\n *\n * @param toolCall - The tool call being reviewed\n * @param state - The current agent state\n * @param runtime - The agent runtime context\n * @returns A string description or Promise that resolves to a string description\n *\n * @example\n * ```typescript\n * import { type DescriptionFactory, type ToolCall } from \"langchain\";\n *\n * const descriptionFactory: DescriptionFactory = (toolCall, state, runtime) => {\n * return `Please review: ${toolCall.name}(${JSON.stringify(toolCall.args)})`;\n * };\n * ```\n */\nexport type DescriptionFactory = z.infer<typeof DescriptionFunctionSchema>;\n\n/**\n * The type of decision a human can make.\n */\nconst ALLOWED_DECISIONS = [\"approve\", \"edit\", \"reject\"] as const;\nconst DecisionType = z.enum(ALLOWED_DECISIONS);\nexport type DecisionType = z.infer<typeof DecisionType>;\n\nconst InterruptOnConfigSchema = z.object({\n /**\n * The decisions that are allowed for this action.\n */\n allowedDecisions: z.array(DecisionType),\n /**\n * The description attached to the request for human input.\n * Can be either:\n * - A static string describing the approval request\n * - A callable that dynamically generates the description based on agent state,\n * runtime, and tool call information\n *\n * @example\n * Static string description\n * ```typescript\n * import type { InterruptOnConfig } from \"langchain\";\n *\n * const config: InterruptOnConfig = {\n * allowedDecisions: [\"approve\", \"reject\"],\n * description: \"Please review this tool execution\"\n * };\n * ```\n *\n * @example\n * Dynamic callable description\n * ```typescript\n * import type {\n * AgentBuiltInState,\n * Runtime,\n * DescriptionFactory,\n * ToolCall,\n * InterruptOnConfig\n * } from \"langchain\";\n *\n * const formatToolDescription: DescriptionFactory = (\n * toolCall: ToolCall,\n * state: AgentBuiltInState,\n * runtime: Runtime<unknown>\n * ) => {\n * return `Tool: ${toolCall.name}\\nArguments:\\n${JSON.stringify(toolCall.args, null, 2)}`;\n * };\n *\n * const config: InterruptOnConfig = {\n * allowedDecisions: [\"approve\", \"edit\"],\n * description: formatToolDescription\n * };\n * ```\n */\n description: z.union([z.string(), DescriptionFunctionSchema]).optional(),\n /**\n * JSON schema for the arguments associated with the action, if edits are allowed.\n */\n argsSchema: z.record(z.any()).optional(),\n});\nexport type InterruptOnConfig = z.input<typeof InterruptOnConfigSchema>;\n\n/**\n * Represents an action with a name and arguments.\n */\nexport interface Action {\n /**\n * The type or name of action being requested (e.g., \"add_numbers\").\n */\n name: string;\n /**\n * Key-value pairs of arguments needed for the action (e.g., {\"a\": 1, \"b\": 2}).\n */\n args: Record<string, any>;\n}\n\n/**\n * Represents an action request with a name, arguments, and description.\n */\nexport interface ActionRequest {\n /**\n * The name of the action being requested.\n */\n name: string;\n /**\n * Key-value pairs of arguments needed for the action (e.g., {\"a\": 1, \"b\": 2}).\n */\n args: Record<string, any>;\n /**\n * The description of the action to be reviewed.\n */\n description?: string;\n}\n\n/**\n * Policy for reviewing a HITL request.\n */\nexport interface ReviewConfig {\n /**\n * Name of the action associated with this review configuration.\n */\n actionName: string;\n /**\n * The decisions that are allowed for this request.\n */\n allowedDecisions: DecisionType[];\n /**\n * JSON schema for the arguments associated with the action, if edits are allowed.\n */\n argsSchema?: Record<string, any>;\n}\n\n/**\n * Request for human feedback on a sequence of actions requested by a model.\n *\n * @example\n * ```ts\n * const hitlRequest: HITLRequest = {\n * actionRequests: [\n * { name: \"send_email\", args: { to: \"user@example.com\", subject: \"Hello\" } }\n * ],\n * reviewConfigs: [\n * {\n * actionName: \"send_email\",\n * allowedDecisions: [\"approve\", \"edit\", \"reject\"],\n * description: \"Please review the email before sending\"\n * }\n * ]\n * };\n * const response = interrupt(hitlRequest);\n * ```\n */\nexport interface HITLRequest {\n /**\n * A list of agent actions for human review.\n */\n actionRequests: ActionRequest[];\n /**\n * Review configuration for all possible actions.\n */\n reviewConfigs: ReviewConfig[];\n}\n\n/**\n * Response when a human approves the action.\n */\nexport interface ApproveDecision {\n type: \"approve\";\n}\n\n/**\n * Response when a human edits the action.\n */\nexport interface EditDecision {\n type: \"edit\";\n /**\n * Edited action for the agent to perform.\n * Ex: for a tool call, a human reviewer can edit the tool name and args.\n */\n editedAction: Action;\n}\n\n/**\n * Response when a human rejects the action.\n */\nexport interface RejectDecision {\n type: \"reject\";\n /**\n * The message sent to the model explaining why the action was rejected.\n */\n message?: string;\n}\n\n/**\n * Union of all possible decision types.\n */\nexport type Decision = ApproveDecision | EditDecision | RejectDecision;\n\n/**\n * Response payload for a HITLRequest.\n */\nexport interface HITLResponse {\n /**\n * The decisions made by the human.\n */\n decisions: Decision[];\n}\n\nconst contextSchema = z.object({\n /**\n * Mapping of tool name to allowed reviewer responses.\n * If a tool doesn't have an entry, it's auto-approved by default.\n *\n * - `true` -> pause for approval and allow approve/edit/reject decisions\n * - `false` -> auto-approve (no human review)\n * - `InterruptOnConfig` -> explicitly specify which decisions are allowed for this tool\n */\n interruptOn: z\n .record(z.union([z.boolean(), InterruptOnConfigSchema]))\n .optional(),\n /**\n * Prefix used when constructing human-facing approval messages.\n * Provides context about the tool call being reviewed; does not change the underlying action.\n *\n * Note: This prefix is only applied for tools that do not provide a custom\n * `description` via their {@link InterruptOnConfig}. If a tool specifies a custom\n * `description`, that per-tool text is used and this prefix is ignored.\n */\n descriptionPrefix: z.string().default(\"Tool execution requires approval\"),\n});\nexport type HumanInTheLoopMiddlewareConfig = InferInteropZodInput<\n typeof contextSchema\n>;\n\n/**\n * Creates a Human-in-the-Loop (HITL) middleware for tool approval and oversight.\n *\n * This middleware intercepts tool calls made by an AI agent and provides human oversight\n * capabilities before execution. It enables selective approval workflows where certain tools\n * require human intervention while others can execute automatically.\n *\n * A invocation result that has been interrupted by the middleware will have a `__interrupt__`\n * property that contains the interrupt request.\n *\n * ```ts\n * import { type HITLRequest, type HITLResponse } from \"langchain\";\n * import { type Interrupt } from \"langchain\";\n *\n * const result = await agent.invoke(request);\n * const interruptRequest = result.__interrupt__?.[0] as Interrupt<HITLRequest>;\n *\n * // Examine the action requests and review configs\n * const actionRequests = interruptRequest.value.actionRequests;\n * const reviewConfigs = interruptRequest.value.reviewConfigs;\n *\n * // Create decisions for each action\n * const resume: HITLResponse = {\n * decisions: actionRequests.map((action, i) => {\n * if (action.name === \"calculator\") {\n * return { type: \"approve\" };\n * } else if (action.name === \"write_file\") {\n * return {\n * type: \"edit\",\n * editedAction: { name: \"write_file\", args: { filename: \"safe.txt\", content: \"Safe content\" } }\n * };\n * }\n * return { type: \"reject\", message: \"Action not allowed\" };\n * })\n * };\n *\n * // Resume with decisions\n * await agent.invoke(new Command({ resume }), config);\n * ```\n *\n * ## Features\n *\n * - **Selective Tool Approval**: Configure which tools require human approval\n * - **Multiple Decision Types**: Approve, edit, or reject tool calls\n * - **Asynchronous Workflow**: Uses LangGraph's interrupt mechanism for non-blocking approval\n * - **Custom Approval Messages**: Provide context-specific descriptions for approval requests\n *\n * ## Decision Types\n *\n * When a tool requires approval, the human operator can respond with:\n * - `approve`: Execute the tool with original arguments\n * - `edit`: Modify the tool name and/or arguments before execution\n * - `reject`: Provide a manual response instead of executing the tool\n *\n * @param options - Configuration options for the middleware\n * @param options.interruptOn - Per-tool configuration mapping tool names to their settings\n * @param options.interruptOn[toolName].allowedDecisions - Array of decision types allowed for this tool (e.g., [\"approve\", \"edit\", \"reject\"])\n * @param options.interruptOn[toolName].description - Custom approval message for the tool. Can be either a static string or a callable that dynamically generates the description based on agent state, runtime, and tool call information\n * @param options.interruptOn[toolName].argsSchema - JSON schema for the arguments associated with the action, if edits are allowed\n * @param options.descriptionPrefix - Default prefix for approval messages (default: \"Tool execution requires approval\"). Only used for tools that do not define a custom `description` in their InterruptOnConfig.\n *\n * @returns A middleware instance that can be passed to `createAgent`\n *\n * @example\n * Basic usage with selective tool approval\n * ```typescript\n * import { humanInTheLoopMiddleware } from \"langchain\";\n * import { createAgent } from \"langchain\";\n *\n * const hitlMiddleware = humanInTheLoopMiddleware({\n * interruptOn: {\n * // Interrupt write_file tool and allow edits or approvals\n * \"write_file\": {\n * allowedDecisions: [\"approve\", \"edit\"],\n * description: \"⚠️ File write operation requires approval\"\n * },\n * // Auto-approve read_file tool\n * \"read_file\": false\n * }\n * });\n *\n * const agent = createAgent({\n * model: \"openai:gpt-4\",\n * tools: [writeFileTool, readFileTool],\n * middleware: [hitlMiddleware]\n * });\n * ```\n *\n * @example\n * Handling approval requests\n * ```typescript\n * import { type HITLRequest, type HITLResponse, type Interrupt } from \"langchain\";\n * import { Command } from \"@langchain/langgraph\";\n *\n * // Initial agent invocation\n * const result = await agent.invoke({\n * messages: [new HumanMessage(\"Write 'Hello' to output.txt\")]\n * }, config);\n *\n * // Check if agent is paused for approval\n * if (result.__interrupt__) {\n * const interruptRequest = result.__interrupt__?.[0] as Interrupt<HITLRequest>;\n *\n * // Show tool call details to user\n * console.log(\"Actions:\", interruptRequest.value.actionRequests);\n * console.log(\"Review configs:\", interruptRequest.value.reviewConfigs);\n *\n * // Resume with approval\n * const resume: HITLResponse = {\n * decisions: [{ type: \"approve\" }]\n * };\n * await agent.invoke(\n * new Command({ resume }),\n * config\n * );\n * }\n * ```\n *\n * @example\n * Different decision types\n * ```typescript\n * import { type HITLResponse } from \"langchain\";\n *\n * // Approve the tool call as-is\n * const resume: HITLResponse = {\n * decisions: [{ type: \"approve\" }]\n * };\n *\n * // Edit the tool arguments\n * const resume: HITLResponse = {\n * decisions: [{\n * type: \"edit\",\n * editedAction: { name: \"write_file\", args: { filename: \"safe.txt\", content: \"Modified\" } }\n * }]\n * };\n *\n * // Reject with feedback\n * const resume: HITLResponse = {\n * decisions: [{\n * type: \"reject\",\n * message: \"File operation not allowed in demo mode\"\n * }]\n * };\n * ```\n *\n * @example\n * Production use case with database operations\n * ```typescript\n * const hitlMiddleware = humanInTheLoopMiddleware({\n * interruptOn: {\n * \"execute_sql\": {\n * allowedDecisions: [\"approve\", \"edit\", \"reject\"],\n * description: \"🚨 SQL query requires DBA approval\\nPlease review for safety and performance\"\n * },\n * \"read_schema\": false, // Reading metadata is safe\n * \"delete_records\": {\n * allowedDecisions: [\"approve\", \"reject\"],\n * description: \"⛔ DESTRUCTIVE OPERATION - Requires manager approval\"\n * }\n * },\n * descriptionPrefix: \"Database operation pending approval\"\n * });\n * ```\n *\n * @example\n * Using dynamic callable descriptions\n * ```typescript\n * import { type DescriptionFactory, type ToolCall } from \"langchain\";\n * import type { AgentBuiltInState, Runtime } from \"langchain/agents\";\n *\n * // Define a dynamic description factory\n * const formatToolDescription: DescriptionFactory = (\n * toolCall: ToolCall,\n * state: AgentBuiltInState,\n * runtime: Runtime<unknown>\n * ) => {\n * return `Tool: ${toolCall.name}\\nArguments:\\n${JSON.stringify(toolCall.args, null, 2)}`;\n * };\n *\n * const hitlMiddleware = humanInTheLoopMiddleware({\n * interruptOn: {\n * \"write_file\": {\n * allowedDecisions: [\"approve\", \"edit\"],\n * // Use dynamic description that can access tool call, state, and runtime\n * description: formatToolDescription\n * },\n * // Or use an inline function\n * \"send_email\": {\n * allowedDecisions: [\"approve\", \"reject\"],\n * description: (toolCall, state, runtime) => {\n * const { to, subject } = toolCall.args;\n * return `Email to ${to}\\nSubject: ${subject}\\n\\nRequires approval before sending`;\n * }\n * }\n * }\n * });\n * ```\n *\n * @remarks\n * - Tool calls are processed in the order they appear in the AI message\n * - Auto-approved tools execute immediately without interruption\n * - Multiple tools requiring approval are bundled into a single interrupt request\n * - The middleware operates in the `afterModel` phase, intercepting before tool execution\n * - Requires a checkpointer to maintain state across interruptions\n *\n * @see {@link createAgent} for agent creation\n * @see {@link Command} for resuming interrupted execution\n * @public\n */\nexport function humanInTheLoopMiddleware(\n options: NonNullable<HumanInTheLoopMiddlewareConfig>\n) {\n const createActionAndConfig = async (\n toolCall: ToolCall,\n config: InterruptOnConfig,\n state: AgentBuiltInState,\n runtime: Runtime<unknown>\n ): Promise<{\n actionRequest: ActionRequest;\n reviewConfig: ReviewConfig;\n }> => {\n const toolName = toolCall.name;\n const toolArgs = toolCall.args;\n\n // Generate description using the description field (str or callable)\n const descriptionValue = config.description;\n let description: string;\n if (typeof descriptionValue === \"function\") {\n description = await descriptionValue(toolCall, state, runtime);\n } else if (descriptionValue !== undefined) {\n description = descriptionValue;\n } else {\n description = `${\n options.descriptionPrefix ?? \"Tool execution requires approval\"\n }\\n\\nTool: ${toolName}\\nArgs: ${JSON.stringify(toolArgs, null, 2)}`;\n }\n\n /**\n * Create ActionRequest with description\n */\n const actionRequest: ActionRequest = {\n name: toolName,\n args: toolArgs,\n description,\n };\n\n /**\n * Create ReviewConfig\n */\n const reviewConfig: ReviewConfig = {\n actionName: toolName,\n allowedDecisions: config.allowedDecisions,\n };\n\n if (config.argsSchema) {\n reviewConfig.argsSchema = config.argsSchema;\n }\n\n return { actionRequest, reviewConfig };\n };\n\n const processDecision = (\n decision: Decision,\n toolCall: ToolCall,\n config: InterruptOnConfig\n ): { revisedToolCall: ToolCall | null; toolMessage: ToolMessage | null } => {\n const allowedDecisions = config.allowedDecisions;\n if (decision.type === \"approve\" && allowedDecisions.includes(\"approve\")) {\n return { revisedToolCall: toolCall, toolMessage: null };\n }\n\n if (decision.type === \"edit\" && allowedDecisions.includes(\"edit\")) {\n const editedAction = decision.editedAction;\n\n /**\n * Validate edited action structure\n */\n if (!editedAction || typeof editedAction.name !== \"string\") {\n throw new Error(\n `Invalid edited action for tool \"${toolCall.name}\": name must be a string`\n );\n }\n if (!editedAction.args || typeof editedAction.args !== \"object\") {\n throw new Error(\n `Invalid edited action for tool \"${toolCall.name}\": args must be an object`\n );\n }\n\n return {\n revisedToolCall: {\n type: \"tool_call\",\n name: editedAction.name,\n args: editedAction.args,\n id: toolCall.id,\n },\n toolMessage: null,\n };\n }\n\n if (decision.type === \"reject\" && allowedDecisions.includes(\"reject\")) {\n /**\n * Validate that message is a string if provided\n */\n if (\n decision.message !== undefined &&\n typeof decision.message !== \"string\"\n ) {\n throw new Error(\n `Tool call response for \"${\n toolCall.name\n }\" must be a string, got ${typeof decision.message}`\n );\n }\n\n // Create a tool message with the human's text response\n const content =\n decision.message ??\n `User rejected the tool call for \\`${toolCall.name}\\` with id ${toolCall.id}`;\n\n const toolMessage = new ToolMessage({\n content,\n name: toolCall.name,\n tool_call_id: toolCall.id!,\n status: \"error\",\n });\n\n return { revisedToolCall: toolCall, toolMessage };\n }\n\n const msg = `Unexpected human decision: ${JSON.stringify(\n decision\n )}. Decision type '${decision.type}' is not allowed for tool '${\n toolCall.name\n }'. Expected one of ${JSON.stringify(\n allowedDecisions\n )} based on the tool's configuration.`;\n throw new Error(msg);\n };\n\n return createMiddleware({\n name: \"HumanInTheLoopMiddleware\",\n contextSchema,\n afterModel: {\n canJumpTo: [\"model\"],\n hook: async (state, runtime) => {\n const config = interopParse(contextSchema, {\n ...options,\n ...(runtime.context || {}),\n });\n if (!config) {\n return;\n }\n\n const { messages } = state;\n if (!messages.length) {\n return;\n }\n\n /**\n * Don't do anything if the last message isn't an AI message with tool calls.\n */\n const lastMessage = [...messages]\n .reverse()\n .find((msg) => AIMessage.isInstance(msg)) as AIMessage;\n if (!lastMessage || !lastMessage.tool_calls?.length) {\n return;\n }\n\n /**\n * If the user omits the interruptOn config, we don't do anything.\n */\n if (!config.interruptOn) {\n return;\n }\n\n /**\n * Resolve per-tool configs (boolean true -> all decisions allowed; false -> auto-approve)\n */\n const resolvedConfigs: Record<string, InterruptOnConfig> = {};\n for (const [toolName, toolConfig] of Object.entries(\n config.interruptOn\n )) {\n if (typeof toolConfig === \"boolean\") {\n if (toolConfig === true) {\n resolvedConfigs[toolName] = {\n allowedDecisions: [...ALLOWED_DECISIONS],\n };\n }\n } else if (toolConfig.allowedDecisions) {\n resolvedConfigs[toolName] = toolConfig as InterruptOnConfig;\n }\n }\n\n const interruptToolCalls: ToolCall[] = [];\n const autoApprovedToolCalls: ToolCall[] = [];\n\n for (const toolCall of lastMessage.tool_calls) {\n if (toolCall.name in resolvedConfigs) {\n interruptToolCalls.push(toolCall);\n } else {\n autoApprovedToolCalls.push(toolCall);\n }\n }\n\n /**\n * No interrupt tool calls, so we can just return.\n */\n if (!interruptToolCalls.length) {\n return;\n }\n\n /**\n * Create action requests and review configs for all tools that need approval\n */\n const actionRequests: ActionRequest[] = [];\n const reviewConfigs: ReviewConfig[] = [];\n\n for (const toolCall of interruptToolCalls) {\n const interruptConfig = resolvedConfigs[toolCall.name]!;\n\n /**\n * Create ActionRequest and ReviewConfig using helper method\n */\n const { actionRequest, reviewConfig } = await createActionAndConfig(\n toolCall,\n interruptConfig,\n state,\n runtime\n );\n actionRequests.push(actionRequest);\n reviewConfigs.push(reviewConfig);\n }\n\n /**\n * Create single HITLRequest with all actions and configs\n */\n const hitlRequest: HITLRequest = {\n actionRequests,\n reviewConfigs,\n };\n\n /**\n * Send interrupt and get response\n */\n const hitlResponse = (await interrupt(hitlRequest)) as HITLResponse;\n const decisions = hitlResponse.decisions;\n\n /**\n * Validate that decisions is a valid array before checking length\n */\n if (!decisions || !Array.isArray(decisions)) {\n throw new Error(\n \"Invalid HITLResponse: decisions must be a non-empty array\"\n );\n }\n\n /**\n * Validate that the number of decisions matches the number of interrupt tool calls\n */\n if (decisions.length !== interruptToolCalls.length) {\n throw new Error(\n `Number of human decisions (${decisions.length}) does not match number of hanging tool calls (${interruptToolCalls.length}).`\n );\n }\n\n const revisedToolCalls: ToolCall[] = [...autoApprovedToolCalls];\n const artificialToolMessages: ToolMessage[] = [];\n const hasRejectedToolCalls = decisions.some(\n (decision) => decision.type === \"reject\"\n );\n\n /**\n * Process each decision using helper method\n */\n for (let i = 0; i < decisions.length; i++) {\n const decision = decisions[i]!;\n const toolCall = interruptToolCalls[i]!;\n const interruptConfig = resolvedConfigs[toolCall.name]!;\n\n const { revisedToolCall, toolMessage } = processDecision(\n decision,\n toolCall,\n interruptConfig\n );\n\n if (\n revisedToolCall &&\n /**\n * If any decision is a rejected, we are going back to the model\n * with only the tool calls that were rejected as we don't know\n * the results of the approved/updated tool calls at this point.\n */\n (!hasRejectedToolCalls || decision.type === \"reject\")\n ) {\n revisedToolCalls.push(revisedToolCall);\n }\n if (toolMessage) {\n artificialToolMessages.push(toolMessage);\n }\n }\n\n /**\n * Update the AI message to only include approved tool calls\n */\n if (AIMessage.isInstance(lastMessage)) {\n lastMessage.tool_calls = revisedToolCalls;\n }\n\n const jumpTo: JumpToTarget | undefined = hasRejectedToolCalls\n ? \"model\"\n : undefined;\n return {\n messages: [lastMessage, ...artificialToolMessages],\n jumpTo,\n };\n },\n },\n });\n}\n"],"mappings":";;;;;;;;AAaA,MAAM,4BAA4BA,SAC/B,UAAU,CACV,KACCA,SAAE,QAAkB,EACpBA,SAAE,QAA2B,EAC7BA,SAAE,QAA0B,CAC7B,CACA,QAAQA,SAAE,MAAM,CAACA,SAAE,QAAQ,EAAEA,SAAE,QAAQA,SAAE,QAAQ,CAAC,AAAC,EAAC,CAAC;;;;AAwBxD,MAAM,oBAAoB;CAAC;CAAW;CAAQ;AAAS;AACvD,MAAM,eAAeA,SAAE,KAAK,kBAAkB;AAG9C,MAAM,0BAA0BA,SAAE,OAAO;CAIvC,kBAAkBA,SAAE,MAAM,aAAa;CA4CvC,aAAaA,SAAE,MAAM,CAACA,SAAE,QAAQ,EAAE,yBAA0B,EAAC,CAAC,UAAU;CAIxE,YAAYA,SAAE,OAAOA,SAAE,KAAK,CAAC,CAAC,UAAU;AACzC,EAAC;AAiIF,MAAM,gBAAgBA,SAAE,OAAO;CAS7B,aAAaA,SACV,OAAOA,SAAE,MAAM,CAACA,SAAE,SAAS,EAAE,uBAAwB,EAAC,CAAC,CACvD,UAAU;CASb,mBAAmBA,SAAE,QAAQ,CAAC,QAAQ,mCAAmC;AAC1E,EAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsNF,SAAgB,yBACdC,SACA;CACA,MAAM,wBAAwB,OAC5BC,UACAC,QACAC,OACAC,YAII;EACJ,MAAM,WAAW,SAAS;EAC1B,MAAM,WAAW,SAAS;EAG1B,MAAM,mBAAmB,OAAO;EAChC,IAAIC;AACJ,MAAI,OAAO,qBAAqB,YAC9B,cAAc,MAAM,iBAAiB,UAAU,OAAO,QAAQ;WACrD,qBAAqB,QAC9B,cAAc;OAEd,cAAc,GACZ,QAAQ,qBAAqB,mCAC9B,UAAU,EAAE,SAAS,QAAQ,EAAE,KAAK,UAAU,UAAU,MAAM,EAAE,EAAE;;;;EAMrE,MAAMC,gBAA+B;GACnC,MAAM;GACN,MAAM;GACN;EACD;;;;EAKD,MAAMC,eAA6B;GACjC,YAAY;GACZ,kBAAkB,OAAO;EAC1B;AAED,MAAI,OAAO,YACT,aAAa,aAAa,OAAO;AAGnC,SAAO;GAAE;GAAe;EAAc;CACvC;CAED,MAAM,kBAAkB,CACtBC,UACAP,UACAC,WAC0E;EAC1E,MAAM,mBAAmB,OAAO;AAChC,MAAI,SAAS,SAAS,aAAa,iBAAiB,SAAS,UAAU,CACrE,QAAO;GAAE,iBAAiB;GAAU,aAAa;EAAM;AAGzD,MAAI,SAAS,SAAS,UAAU,iBAAiB,SAAS,OAAO,EAAE;GACjE,MAAM,eAAe,SAAS;;;;AAK9B,OAAI,CAAC,gBAAgB,OAAO,aAAa,SAAS,SAChD,OAAM,IAAI,MACR,CAAC,gCAAgC,EAAE,SAAS,KAAK,wBAAwB,CAAC;AAG9E,OAAI,CAAC,aAAa,QAAQ,OAAO,aAAa,SAAS,SACrD,OAAM,IAAI,MACR,CAAC,gCAAgC,EAAE,SAAS,KAAK,yBAAyB,CAAC;AAI/E,UAAO;IACL,iBAAiB;KACf,MAAM;KACN,MAAM,aAAa;KACnB,MAAM,aAAa;KACnB,IAAI,SAAS;IACd;IACD,aAAa;GACd;EACF;AAED,MAAI,SAAS,SAAS,YAAY,iBAAiB,SAAS,SAAS,EAAE;;;;AAIrE,OACE,SAAS,YAAY,UACrB,OAAO,SAAS,YAAY,SAE5B,OAAM,IAAI,MACR,CAAC,wBAAwB,EACvB,SAAS,KACV,wBAAwB,EAAE,OAAO,SAAS,SAAS;GAKxD,MAAM,UACJ,SAAS,WACT,CAAC,kCAAkC,EAAE,SAAS,KAAK,WAAW,EAAE,SAAS,IAAI;GAE/E,MAAM,cAAc,IAAIO,sCAAY;IAClC;IACA,MAAM,SAAS;IACf,cAAc,SAAS;IACvB,QAAQ;GACT;AAED,UAAO;IAAE,iBAAiB;IAAU;GAAa;EAClD;EAED,MAAM,MAAM,CAAC,2BAA2B,EAAE,KAAK,UAC7C,SACD,CAAC,iBAAiB,EAAE,SAAS,KAAK,2BAA2B,EAC5D,SAAS,KACV,mBAAmB,EAAE,KAAK,UACzB,iBACD,CAAC,mCAAmC,CAAC;AACtC,QAAM,IAAI,MAAM;CACjB;AAED,QAAOC,oCAAiB;EACtB,MAAM;EACN;EACA,YAAY;GACV,WAAW,CAAC,OAAQ;GACpB,MAAM,OAAO,OAAO,YAAY;IAC9B,MAAM,wDAAsB,eAAe;KACzC,GAAG;KACH,GAAI,QAAQ,WAAW,CAAE;IAC1B,EAAC;AACF,QAAI,CAAC,OACH;IAGF,MAAM,EAAE,UAAU,GAAG;AACrB,QAAI,CAAC,SAAS,OACZ;;;;IAMF,MAAM,cAAc,CAAC,GAAG,QAAS,EAC9B,SAAS,CACT,KAAK,CAAC,QAAQC,oCAAU,WAAW,IAAI,CAAC;AAC3C,QAAI,CAAC,eAAe,CAAC,YAAY,YAAY,OAC3C;;;;AAMF,QAAI,CAAC,OAAO,YACV;;;;IAMF,MAAMC,kBAAqD,CAAE;AAC7D,SAAK,MAAM,CAAC,UAAU,WAAW,IAAI,OAAO,QAC1C,OAAO,YACR,CACC,KAAI,OAAO,eAAe,WACxB;SAAI,eAAe,MACjB,gBAAgB,YAAY,EAC1B,kBAAkB,CAAC,GAAG,iBAAkB,EACzC;IACF,WACQ,WAAW,kBACpB,gBAAgB,YAAY;IAIhC,MAAMC,qBAAiC,CAAE;IACzC,MAAMC,wBAAoC,CAAE;AAE5C,SAAK,MAAM,YAAY,YAAY,WACjC,KAAI,SAAS,QAAQ,iBACnB,mBAAmB,KAAK,SAAS;SAEjC,sBAAsB,KAAK,SAAS;;;;AAOxC,QAAI,CAAC,mBAAmB,OACtB;;;;IAMF,MAAMC,iBAAkC,CAAE;IAC1C,MAAMC,gBAAgC,CAAE;AAExC,SAAK,MAAM,YAAY,oBAAoB;KACzC,MAAM,kBAAkB,gBAAgB,SAAS;;;;KAKjD,MAAM,EAAE,eAAe,cAAc,GAAG,MAAM,sBAC5C,UACA,iBACA,OACA,QACD;KACD,eAAe,KAAK,cAAc;KAClC,cAAc,KAAK,aAAa;IACjC;;;;IAKD,MAAMC,cAA2B;KAC/B;KACA;IACD;;;;IAKD,MAAM,eAAgB,2CAAgB,YAAY;IAClD,MAAM,YAAY,aAAa;;;;AAK/B,QAAI,CAAC,aAAa,CAAC,MAAM,QAAQ,UAAU,CACzC,OAAM,IAAI,MACR;;;;AAOJ,QAAI,UAAU,WAAW,mBAAmB,OAC1C,OAAM,IAAI,MACR,CAAC,2BAA2B,EAAE,UAAU,OAAO,+CAA+C,EAAE,mBAAmB,OAAO,EAAE,CAAC;IAIjI,MAAMC,mBAA+B,CAAC,GAAG,qBAAsB;IAC/D,MAAMC,yBAAwC,CAAE;IAChD,MAAM,uBAAuB,UAAU,KACrC,CAAC,aAAa,SAAS,SAAS,SACjC;;;;AAKD,SAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;KACzC,MAAM,WAAW,UAAU;KAC3B,MAAM,WAAW,mBAAmB;KACpC,MAAM,kBAAkB,gBAAgB,SAAS;KAEjD,MAAM,EAAE,iBAAiB,aAAa,GAAG,gBACvC,UACA,UACA,gBACD;AAED,SACE,oBAMC,CAAC,wBAAwB,SAAS,SAAS,WAE5C,iBAAiB,KAAK,gBAAgB;AAExC,SAAI,aACF,uBAAuB,KAAK,YAAY;IAE3C;;;;AAKD,QAAIR,oCAAU,WAAW,YAAY,EACnC,YAAY,aAAa;IAG3B,MAAMS,SAAmC,uBACrC,UACA;AACJ,WAAO;KACL,UAAU,CAAC,aAAa,GAAG,sBAAuB;KAClD;IACD;GACF;EACF;CACF,EAAC;AACH"}