agentix-cli 0.16.0 → 0.18.0
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.
- package/dist/agent-RDVGL2TW.js +2 -0
- package/dist/audit-Z47AQFFD.js +3 -0
- package/dist/audit-Z47AQFFD.js.map +1 -0
- package/dist/board-dashboard-KX2CNOSY.js +7282 -0
- package/dist/board-dashboard-KX2CNOSY.js.map +1 -0
- package/dist/chunk-34NG7UDX.js +11 -0
- package/dist/chunk-34NG7UDX.js.map +1 -0
- package/dist/{chunk-6MKAXEE5.js → chunk-4ITQP3XL.js} +2 -2
- package/dist/{chunk-6MKAXEE5.js.map → chunk-4ITQP3XL.js.map} +1 -1
- package/dist/chunk-53NRX2NA.js +2 -0
- package/dist/chunk-53NRX2NA.js.map +1 -0
- package/dist/chunk-6PPIFDOQ.js +1742 -0
- package/dist/chunk-6PPIFDOQ.js.map +1 -0
- package/dist/chunk-AFRHKA7B.js +2 -0
- package/dist/chunk-AFRHKA7B.js.map +1 -0
- package/dist/chunk-ASSJ4Z6Z.js +164 -0
- package/dist/chunk-ASSJ4Z6Z.js.map +1 -0
- package/dist/chunk-BLUXL6HR.js +854 -0
- package/dist/chunk-BLUXL6HR.js.map +1 -0
- package/dist/chunk-BTAEYL3P.js +72 -0
- package/dist/chunk-BTAEYL3P.js.map +1 -0
- package/dist/chunk-CDNWCYF7.js +2 -0
- package/dist/chunk-CDNWCYF7.js.map +1 -0
- package/dist/chunk-DJEZ2HVU.js +5 -0
- package/dist/chunk-DJEZ2HVU.js.map +1 -0
- package/dist/{chunk-FLOGK2U7.js → chunk-DSCMCVTV.js} +3 -3
- package/dist/chunk-DSCMCVTV.js.map +1 -0
- package/dist/chunk-EW27SCCC.js +2 -0
- package/dist/chunk-EW27SCCC.js.map +1 -0
- package/dist/chunk-HPDHNIA3.js +2 -0
- package/dist/chunk-IM376SD6.js +13 -0
- package/dist/chunk-IM376SD6.js.map +1 -0
- package/dist/chunk-J4WKMKTP.js +5 -0
- package/dist/{chunk-YI2T5QYB.js.map → chunk-J4WKMKTP.js.map} +1 -1
- package/dist/chunk-JGXE2YNV.js +46 -0
- package/dist/chunk-JGXE2YNV.js.map +1 -0
- package/dist/chunk-JXEPJ3YD.js +118 -0
- package/dist/chunk-JXEPJ3YD.js.map +1 -0
- package/dist/chunk-LY7JGDV7.js +6 -0
- package/dist/chunk-LY7JGDV7.js.map +1 -0
- package/dist/chunk-N67JZAXU.js +2 -0
- package/dist/chunk-N67JZAXU.js.map +1 -0
- package/dist/chunk-SR3X25TN.js +3 -0
- package/dist/chunk-SR3X25TN.js.map +1 -0
- package/dist/{chunk-7MBTMCKY.js → chunk-TRQSP67X.js} +2 -2
- package/dist/chunk-U364GNQX.js +719 -0
- package/dist/chunk-U364GNQX.js.map +1 -0
- package/dist/chunk-VJUTQAPJ.js +2 -0
- package/dist/chunk-VJUTQAPJ.js.map +1 -0
- package/dist/{chunk-HI24KAEY.js → chunk-WJXU5F4Z.js} +3 -4
- package/dist/{chunk-HI24KAEY.js.map → chunk-WJXU5F4Z.js.map} +1 -1
- package/dist/{chunk-QZP65I2H.js → chunk-ZE2YFX4K.js} +2 -2
- package/dist/cli.js +1 -320
- package/dist/cli.js.map +1 -1
- package/dist/{compaction-JBZZ7I7Y.js → compaction-S5H6DTOR.js} +6 -6
- package/dist/compaction-S5H6DTOR.js.map +1 -0
- package/dist/config-UOHEG4BW.js +2 -0
- package/dist/config-mutator-KSEBWKJH.js +2 -0
- package/dist/context-planner-2RGHJHSO.js +17 -0
- package/dist/context-planner-2RGHJHSO.js.map +1 -0
- package/dist/debug-TGAP7E7W.js +2 -0
- package/dist/discover-OATU7SHI.js +4 -0
- package/dist/discover-OATU7SHI.js.map +1 -0
- package/dist/editor-chat-FUBQLMAQ.js +137 -0
- package/dist/editor-chat-FUBQLMAQ.js.map +1 -0
- package/dist/heal-YP7QI7D3.js +2 -0
- package/dist/inbox-O5UB3YQN.js +315 -0
- package/dist/inbox-O5UB3YQN.js.map +1 -0
- package/dist/index.d.ts +3390 -1814
- package/dist/index.js +23 -53
- package/dist/index.js.map +1 -1
- package/dist/ingest-whatsapp-C3O5UPBX.js +6 -0
- package/dist/ingest-whatsapp-C3O5UPBX.js.map +1 -0
- package/dist/loader-BGWA7IRY.js +2 -0
- package/dist/loader-LBHKSXBK.js +2 -0
- package/dist/memory-extract-KKPKAOPH.js +2 -0
- package/dist/processes-7W27G3J3.js +256 -0
- package/dist/processes-7W27G3J3.js.map +1 -0
- package/dist/program-2MDELQLX.js +2 -0
- package/dist/providers-WCQSKTNP.js +2 -0
- package/dist/query-FZAL7AQD.js +2 -0
- package/dist/recipes-LZ56TA3X.js +2 -0
- package/dist/registry-ONJSILH4.js +2 -0
- package/dist/scheduler-XRM2RJIV.js +2 -0
- package/dist/slack-D27BE37Q.js +3 -0
- package/dist/slack-D27BE37Q.js.map +1 -0
- package/dist/store-4UCO56K2.js +2 -0
- package/dist/store-4UCO56K2.js.map +1 -0
- package/dist/{subagent-2SJHSSJS.js → subagent-EPOT3QDM.js} +2 -2
- package/dist/task-store-3NUWLYRY.js +2 -0
- package/dist/task-store-3NUWLYRY.js.map +1 -0
- package/dist/telegram-UWV67KWG.js +3 -0
- package/dist/telegram-UWV67KWG.js.map +1 -0
- package/dist/timers-QB4KYNAE.js +2 -0
- package/dist/timers-QB4KYNAE.js.map +1 -0
- package/dist/types-ECMEC5BZ.js +2 -0
- package/dist/types-ECMEC5BZ.js.map +1 -0
- package/dist/types-TTB6DJUE.js +2 -0
- package/dist/types-TTB6DJUE.js.map +1 -0
- package/dist/usage-dashboard-FQGPPABD.js +803 -0
- package/dist/usage-dashboard-FQGPPABD.js.map +1 -0
- package/dist/web/workflow-editor.global.js +1450 -0
- package/dist/web/workflow-editor.global.js.map +1 -0
- package/dist/whatsapp-KJ2NRKKQ.js +3 -0
- package/dist/whatsapp-KJ2NRKKQ.js.map +1 -0
- package/dist/{whatsapp-state-6D6FHHD2.js → whatsapp-state-3ZVQ6MQD.js} +2 -2
- package/dist/{wiki-UDPYFJBL.js → wiki-OUYKM7MB.js} +2 -2
- package/dist/wiki-OUYKM7MB.js.map +1 -0
- package/dist/workspace-setup-6GDBEORT.js +2 -0
- package/dist/workspace-setup-6GDBEORT.js.map +1 -0
- package/package.json +19 -2
- package/dist/agent-BCDPSPKU.js +0 -2
- package/dist/board-dashboard-LA7WW56M.js +0 -5668
- package/dist/board-dashboard-LA7WW56M.js.map +0 -1
- package/dist/chunk-6U2XOQHU.js +0 -7
- package/dist/chunk-6U2XOQHU.js.map +0 -1
- package/dist/chunk-7FJQGD6U.js +0 -6
- package/dist/chunk-7FJQGD6U.js.map +0 -1
- package/dist/chunk-ABDP7TDL.js +0 -71
- package/dist/chunk-ABDP7TDL.js.map +0 -1
- package/dist/chunk-CXY2RYNI.js +0 -11
- package/dist/chunk-CXY2RYNI.js.map +0 -1
- package/dist/chunk-FIOB3MEX.js +0 -2
- package/dist/chunk-FLOGK2U7.js.map +0 -1
- package/dist/chunk-HVC6FJNP.js +0 -108
- package/dist/chunk-HVC6FJNP.js.map +0 -1
- package/dist/chunk-K7BE4E35.js +0 -138
- package/dist/chunk-K7BE4E35.js.map +0 -1
- package/dist/chunk-KXJSLLSQ.js +0 -199
- package/dist/chunk-KXJSLLSQ.js.map +0 -1
- package/dist/chunk-KXZMYLHQ.js +0 -46
- package/dist/chunk-KXZMYLHQ.js.map +0 -1
- package/dist/chunk-M7HKBG3V.js +0 -2
- package/dist/chunk-M7HKBG3V.js.map +0 -1
- package/dist/chunk-MCJUAE6V.js +0 -6
- package/dist/chunk-MCJUAE6V.js.map +0 -1
- package/dist/chunk-RB7265LO.js +0 -12
- package/dist/chunk-RB7265LO.js.map +0 -1
- package/dist/chunk-YI2T5QYB.js +0 -5
- package/dist/compaction-JBZZ7I7Y.js.map +0 -1
- package/dist/config-LN6JEHMF.js +0 -2
- package/dist/config-mutator-24TH7PHM.js +0 -2
- package/dist/debug-3MEUO2P2.js +0 -2
- package/dist/heal-WKY5I4KB.js +0 -2
- package/dist/loader-PTCXLJEX.js +0 -2
- package/dist/memory-extract-AD7KMRPC.js +0 -2
- package/dist/providers-VUIWO3ZK.js +0 -2
- package/dist/query-PKGYE5HR.js +0 -2
- package/dist/registry-ASC2QS5K.js +0 -2
- package/dist/scheduler-A3TQOCKT.js +0 -2
- package/dist/types-OIFU3KSQ.js +0 -2
- package/dist/usage-dashboard-QX3CF6KK.js +0 -404
- package/dist/usage-dashboard-QX3CF6KK.js.map +0 -1
- package/dist/workspace-setup-ZWTT42XT.js +0 -2
- /package/dist/{agent-BCDPSPKU.js.map → agent-RDVGL2TW.js.map} +0 -0
- /package/dist/{chunk-FIOB3MEX.js.map → chunk-HPDHNIA3.js.map} +0 -0
- /package/dist/{chunk-7MBTMCKY.js.map → chunk-TRQSP67X.js.map} +0 -0
- /package/dist/{chunk-QZP65I2H.js.map → chunk-ZE2YFX4K.js.map} +0 -0
- /package/dist/{config-LN6JEHMF.js.map → config-UOHEG4BW.js.map} +0 -0
- /package/dist/{config-mutator-24TH7PHM.js.map → config-mutator-KSEBWKJH.js.map} +0 -0
- /package/dist/{debug-3MEUO2P2.js.map → debug-TGAP7E7W.js.map} +0 -0
- /package/dist/{heal-WKY5I4KB.js.map → heal-YP7QI7D3.js.map} +0 -0
- /package/dist/{loader-PTCXLJEX.js.map → loader-BGWA7IRY.js.map} +0 -0
- /package/dist/{memory-extract-AD7KMRPC.js.map → loader-LBHKSXBK.js.map} +0 -0
- /package/dist/{providers-VUIWO3ZK.js.map → memory-extract-KKPKAOPH.js.map} +0 -0
- /package/dist/{query-PKGYE5HR.js.map → program-2MDELQLX.js.map} +0 -0
- /package/dist/{registry-ASC2QS5K.js.map → providers-WCQSKTNP.js.map} +0 -0
- /package/dist/{scheduler-A3TQOCKT.js.map → query-FZAL7AQD.js.map} +0 -0
- /package/dist/{types-OIFU3KSQ.js.map → recipes-LZ56TA3X.js.map} +0 -0
- /package/dist/{wiki-UDPYFJBL.js.map → registry-ONJSILH4.js.map} +0 -0
- /package/dist/{workspace-setup-ZWTT42XT.js.map → scheduler-XRM2RJIV.js.map} +0 -0
- /package/dist/{subagent-2SJHSSJS.js.map → subagent-EPOT3QDM.js.map} +0 -0
- /package/dist/{whatsapp-state-6D6FHHD2.js.map → whatsapp-state-3ZVQ6MQD.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/a2a/client.ts","../src/a2a/mesh.ts","../src/channels/group-log.ts","../src/utils/ellipsize.ts","../src/channels/block-stream.ts","../src/channels/router.ts","../src/channels/telegram-format.ts","../src/channels/telegram.ts","../src/channels/whatsapp.ts","../src/daemon/index.ts","../src/channels/discord.ts","../src/channels/slack.ts","../src/channels/gitlab.ts","../src/daemon/logger.ts","../src/daemon/webhooks.ts","../src/agents/landscape.ts","../src/agents/heartbeat.ts","../src/services/matcher.ts","../src/business/organization.ts","../src/business/schedule.ts","../src/business/reporter.ts","../src/business/kpi.ts","../src/business/http.ts","../src/business/day-cycle.ts","../src/business/index.ts","../src/utils/get-package-info.ts"],"sourcesContent":["import type { AgentCard, Task, MessagePart, JsonRpcResponse } from \"./types\"\n\n// --- A2A Client: call remote agents via A2A protocol ---\n\nexport class A2AClient {\n private baseUrl: string\n private token?: string\n\n constructor(baseUrl: string, token?: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, \"\")\n this.token = token\n }\n\n /**\n * Fetch the remote agent's card.\n */\n async getAgentCard(): Promise<AgentCard> {\n const res = await fetch(`${this.baseUrl}/.well-known/agent-card.json`, {\n headers: this.headers(),\n })\n if (!res.ok) throw new Error(`Failed to fetch agent card: ${res.status}`)\n return res.json() as Promise<AgentCard>\n }\n\n /**\n * Send a task synchronously and wait for completion.\n */\n async sendTask(\n text: string,\n metadata?: Record<string, unknown>,\n ): Promise<Task> {\n const response = await this.rpc(\"tasks/send\", {\n id: `task-${Date.now().toString(36)}`,\n message: {\n role: \"user\",\n parts: [{ type: \"text\", text }] as MessagePart[],\n },\n metadata,\n })\n\n if (response.error) {\n throw new Error(`A2A error: ${response.error.message}`)\n }\n\n return response.result as Task\n }\n\n /**\n * Send a task with SSE streaming.\n */\n async *sendTaskStream(\n text: string,\n metadata?: Record<string, unknown>,\n ): AsyncIterable<{ state: string; message?: string; final: boolean }> {\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n id: 1,\n method: \"tasks/sendSubscribe\",\n params: {\n id: `task-${Date.now().toString(36)}`,\n message: {\n role: \"user\",\n parts: [{ type: \"text\", text }],\n },\n metadata,\n },\n })\n\n const res = await fetch(this.baseUrl, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n \"Accept\": \"text/event-stream\",\n },\n body,\n })\n\n if (!res.ok || !res.body) {\n throw new Error(`A2A stream error: ${res.status}`)\n }\n\n const reader = res.body.getReader()\n const decoder = new TextDecoder()\n let buffer = \"\"\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split(\"\\n\")\n buffer = lines.pop() || \"\"\n\n for (const line of lines) {\n if (line.startsWith(\"data: \")) {\n try {\n const data = JSON.parse(line.slice(6))\n yield {\n state: data.state,\n message: data.message?.parts?.[0]?.text,\n final: data.final,\n }\n } catch {\n // Skip malformed events\n }\n }\n }\n }\n }\n\n /**\n * Get task status by ID.\n */\n async getTask(taskId: string): Promise<Task> {\n const response = await this.rpc(\"tasks/get\", { id: taskId })\n if (response.error) {\n throw new Error(`A2A error: ${response.error.message}`)\n }\n return response.result as Task\n }\n\n /**\n * Cancel a running task.\n */\n async cancelTask(taskId: string): Promise<Task> {\n const response = await this.rpc(\"tasks/cancel\", { id: taskId })\n if (response.error) {\n throw new Error(`A2A error: ${response.error.message}`)\n }\n return response.result as Task\n }\n\n private async rpc(method: string, params: Record<string, unknown>): Promise<JsonRpcResponse> {\n const res = await fetch(this.baseUrl, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n jsonrpc: \"2.0\",\n id: Date.now(),\n method,\n params,\n }),\n })\n\n if (!res.ok) {\n throw new Error(`A2A HTTP error: ${res.status}`)\n }\n\n return res.json() as Promise<JsonRpcResponse>\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = {}\n if (this.token) {\n h[\"Authorization\"] = `Bearer ${this.token}`\n }\n return h\n }\n}\n","import type { MeshPeer, DaemonConfig } from \"@/daemon/config\"\nimport type { AgentCard, AgentSkill } from \"./types\"\nimport { A2AClient } from \"./client\"\n\n// --- A2A Mesh: peer discovery, health checks, agent directory ---\n\nexport interface PeerState {\n peer: MeshPeer\n client: A2AClient\n healthy: boolean\n lastCheck?: Date\n agentCard?: AgentCard\n agents: AgentSkill[]\n}\n\nexport class A2AMesh {\n private peers: Map<string, PeerState> = new Map()\n private healthTimer?: ReturnType<typeof setInterval>\n private config: DaemonConfig\n private log: (...args: unknown[]) => void\n\n constructor(\n config: DaemonConfig,\n log: (...args: unknown[]) => void = console.error.bind(console, \"[mesh]\"),\n ) {\n this.config = config\n this.log = log\n\n for (const peer of config.mesh.peers) {\n this.peers.set(peer.name, {\n peer,\n client: new A2AClient(peer.url, peer.token),\n healthy: false,\n agents: [],\n })\n }\n }\n\n /**\n * Start the mesh: discover peers and begin health checks.\n */\n async start(): Promise<void> {\n this.log(`Mesh starting with ${this.peers.size} peer(s)`)\n\n // Initial discovery\n await this.discoverAll()\n\n // Periodic health checks\n const interval = this.config.mesh.healthCheck.interval * 1000\n this.healthTimer = setInterval(() => this.discoverAll(), interval)\n }\n\n async stop(): Promise<void> {\n if (this.healthTimer) {\n clearInterval(this.healthTimer)\n }\n }\n\n /**\n * Discover agent cards from all peers.\n */\n async discoverAll(): Promise<void> {\n const results = await Promise.allSettled(\n Array.from(this.peers.entries()).map(([name, state]) =>\n this.discoverPeer(name, state),\n ),\n )\n\n const healthy = Array.from(this.peers.values()).filter((p) => p.healthy).length\n this.log(`Discovery complete: ${healthy}/${this.peers.size} peers healthy`)\n }\n\n private async discoverPeer(name: string, state: PeerState): Promise<void> {\n const timeout = this.config.mesh.healthCheck.timeout * 1000\n\n try {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeout)\n\n const card = await state.client.getAgentCard()\n clearTimeout(timer)\n\n state.healthy = true\n state.lastCheck = new Date()\n state.agentCard = card\n state.agents = card.skills || []\n\n this.log(`Peer \"${name}\" healthy: ${card.name} (${state.agents.length} skills)`)\n } catch (e: any) {\n state.healthy = false\n state.lastCheck = new Date()\n this.log(`Peer \"${name}\" unreachable: ${e.message}`)\n }\n }\n\n /**\n * Send a task to a remote peer by name.\n * Uses the peer's /task HTTP endpoint (agentx daemon API).\n * If no agent specified, uses the first available agent on the peer.\n */\n async sendTask(peerName: string, text: string, agentId?: string): Promise<string> {\n const state = this.peers.get(peerName)\n if (!state) throw new Error(`Unknown peer: ${peerName}`)\n if (!state.healthy) throw new Error(`Peer \"${peerName}\" is not healthy`)\n\n // Default to first agent on the peer\n const agent = agentId || state.agents[0]?.id\n if (!agent) throw new Error(`Peer \"${peerName}\" has no agents`)\n\n const url = `${state.peer.url}/task`\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" }\n if (state.peer.token) {\n headers[\"Authorization\"] = `Bearer ${state.peer.token}`\n }\n\n const res = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify({ agent, message: text }),\n })\n\n if (!res.ok) {\n throw new Error(`Peer \"${peerName}\" /task error: ${res.status}`)\n }\n\n const data = await res.json() as { content?: string; error?: string }\n if (data.error) throw new Error(`Peer \"${peerName}\" agent error: ${data.error}`)\n return data.content || \"No response\"\n }\n\n /**\n * Find a peer that has a specific skill.\n */\n findPeerWithSkill(skillId: string): PeerState | undefined {\n for (const state of this.peers.values()) {\n if (state.healthy && state.agents.some((a) => a.id === skillId)) {\n return state\n }\n }\n return undefined\n }\n\n /**\n * Get the combined agent directory across all healthy peers.\n */\n directory(): Array<{\n peer: string\n peerUrl: string\n healthy: boolean\n skills: AgentSkill[]\n lastCheck?: Date\n }> {\n return Array.from(this.peers.entries()).map(([name, state]) => ({\n peer: name,\n peerUrl: state.peer.url,\n healthy: state.healthy,\n skills: state.agents,\n lastCheck: state.lastCheck,\n }))\n }\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { ellipsize } from \"@/utils/ellipsize\"\n\n// --- Persistent group conversation log ---\n// Stores recent messages per group chat in .agentx/groups/{chatId}.jsonl\n// Survives daemon restarts. Trimmed to max entries on write.\n\nexport interface GroupLogEntry {\n sender: string\n text: string\n timestamp: number\n}\n\nconst MAX_ENTRIES = 30\nconst MAX_CONTEXT_CHARS = 6000\nconst MAX_ENTRY_CHARS = 1500\n/** Drop identical (sender,text) re-sends that arrive within this window.\n * Protects the log from users who mash Enter N times or from clients that\n * mirror the same message across bots. Observed cost: 6× Seif spam blew the\n * prompt budget on the next agent's first turn. */\nconst REPEAT_WINDOW_MS = 60_000\n\n/** Bucket a timestamp into a stable 15-min window so the rendered history\n * header only changes every quarter-hour. Stamping every line with HH:MM\n * invalidates the cache on every new message; bucketing preserves it. */\nfunction bucketLabel(ts: number): string {\n const d = new Date(ts)\n const hh = d.getUTCHours().toString().padStart(2, \"0\")\n const mm = Math.floor(d.getUTCMinutes() / 15) * 15\n return `${hh}:${mm.toString().padStart(2, \"0\")}`\n}\n\nexport class GroupLog {\n private dir: string\n private cache: Map<string, GroupLogEntry[]> = new Map()\n\n constructor(baseDir: string = resolve(process.cwd(), \".agentx/groups\")) {\n this.dir = baseDir\n mkdirSync(this.dir, { recursive: true })\n }\n\n /**\n * Log a message in a group conversation.\n */\n add(chatId: string, sender: string, text: string): void {\n const log = this.load(chatId)\n const now = Date.now()\n const ellipsized = ellipsize(text, MAX_ENTRY_CHARS)\n\n // On-insert dedup: if the most recent entry has the same sender+text and\n // arrived within REPEAT_WINDOW_MS, skip. Don't walk further back — spam\n // is usually contiguous, and a legitimate \"say it again\" later is still\n // useful signal for the agent.\n const last = log[log.length - 1]\n if (last && last.sender === sender && last.text === ellipsized && now - last.timestamp < REPEAT_WINDOW_MS) {\n return\n }\n\n log.push({ sender, text: ellipsized, timestamp: now })\n\n // Trim\n while (log.length > MAX_ENTRIES) log.shift()\n\n this.cache.set(chatId, log)\n this.save(chatId, log)\n }\n\n /**\n * Build conversation context string for an agent.\n * Returns recent messages so the agent knows what was discussed.\n */\n buildContext(chatId: string): string {\n const log = this.load(chatId)\n if (log.length <= 1) return \"\"\n\n const lines: string[] = [\"[Recent group conversation]\"]\n let chars = 0\n\n // Walk backwards from most recent, skip the very last (it's the current\n // message). Coalesce runs of identical (sender,text) into one line with a\n // repeat count — protects the prompt from any spam that predates on-insert\n // dedup (historical log entries) and keeps the context compact.\n // Bucket headers only appear when the 15-min window changes, so the\n // rendered history is byte-stable within the bucket (cache-friendly).\n let pending: { line: string; repeat: number; bucket: string } | null = null\n const buffered: string[] = []\n let lastBucket = \"\"\n const flushPending = (): boolean => {\n if (!pending) return true\n const out = pending.repeat > 1 ? `${pending.line} [×${pending.repeat}]` : pending.line\n if (chars + out.length > MAX_CONTEXT_CHARS) return false\n buffered.push(out)\n chars += out.length\n // Emit a bucket header when the window changes (we're walking back in\n // time — so the \"new bucket\" is older than the previous one).\n if (pending.bucket !== lastBucket) {\n const hdr = `— ${pending.bucket} —`\n if (chars + hdr.length > MAX_CONTEXT_CHARS) { pending = null; return false }\n buffered.push(hdr)\n chars += hdr.length\n lastBucket = pending.bucket\n }\n pending = null\n return true\n }\n\n for (let i = log.length - 2; i >= 0; i--) {\n const entry = log[i]\n const line = `${entry.sender}: ${entry.text}`\n const bucket = bucketLabel(entry.timestamp)\n if (pending && pending.line === line && pending.bucket === bucket) {\n pending.repeat++\n continue\n }\n if (!flushPending()) break\n pending = { line, repeat: 1, bucket }\n }\n flushPending()\n // Splice buffered entries (in reverse — they're back-to-front) under the\n // opening header in oldest-first order.\n for (let i = buffered.length - 1; i >= 0; i--) lines.splice(1, 0, buffered[i])\n\n if (lines.length <= 1) return \"\"\n lines.push(\"[End of conversation — respond to the latest message]\")\n return lines.join(\"\\n\")\n }\n\n /**\n * Get raw log entries for a group.\n */\n getEntries(chatId: string): GroupLogEntry[] {\n return [...this.load(chatId)]\n }\n\n // --- Persistence ---\n\n private filePath(chatId: string): string {\n const safe = chatId.replace(/[^a-zA-Z0-9_-]/g, \"_\")\n return resolve(this.dir, `${safe}.json`)\n }\n\n private load(chatId: string): GroupLogEntry[] {\n // Check cache first\n if (this.cache.has(chatId)) return this.cache.get(chatId)!\n\n // Load from disk\n const path = this.filePath(chatId)\n if (!existsSync(path)) {\n this.cache.set(chatId, [])\n return []\n }\n\n try {\n const data = JSON.parse(readFileSync(path, \"utf-8\"))\n const entries = Array.isArray(data) ? data : []\n this.cache.set(chatId, entries)\n return entries\n } catch {\n this.cache.set(chatId, [])\n return []\n }\n }\n\n private save(chatId: string, entries: GroupLogEntry[]): void {\n try {\n writeFileSync(this.filePath(chatId), JSON.stringify(entries))\n } catch {\n // Best-effort persistence\n }\n }\n}\n","// --- Smart truncation ---\n//\n// Hard `.slice(0, N)` cuts words and lines at arbitrary offsets, which turns\n// multi-line webhook bodies (GitLab pipeline events, MR descriptions, long\n// agent responses) into visibly-broken fragments in Telegram notifications.\n// Prefer a line boundary, fall back to a word boundary, only hard-cut as\n// last resort. Append \"…\" only when truncation actually happened.\n\nexport function ellipsize(text: string, maxChars: number, ellipsis: string = \"…\"): string {\n if (!text) return \"\"\n if (text.length <= maxChars) return text\n\n const budget = maxChars - ellipsis.length\n if (budget <= 0) return text.slice(0, maxChars)\n\n const slice = text.slice(0, budget)\n\n // Prefer the last line break if it's past 60% of the budget — keeps\n // multi-line bodies readable instead of chopping mid-line.\n const nl = slice.lastIndexOf(\"\\n\")\n if (nl > budget * 0.6) return slice.slice(0, nl) + \"\\n\" + ellipsis\n\n // Fall back to the last word boundary past 60% of the budget.\n const sp = Math.max(slice.lastIndexOf(\" \"), slice.lastIndexOf(\"\\t\"))\n if (sp > budget * 0.6) return slice.slice(0, sp) + ellipsis\n\n return slice + ellipsis\n}\n\n/**\n * Take the first `maxLines` lines, then ellipsize the result to `maxChars`.\n * Useful for multi-line event bodies where only the summary lines matter.\n */\nexport function firstLines(text: string, maxLines: number, maxChars: number): string {\n if (!text) return \"\"\n const lines = text.split(\"\\n\")\n const picked = lines.slice(0, maxLines).join(\"\\n\")\n return ellipsize(picked, maxChars)\n}\n","// --- Smart Block Streaming ---\n//\n// Intelligent chunking for channel message delivery.\n// Instead of sending raw text deltas, accumulates text and emits\n// well-formed blocks that respect markdown structure.\n//\n// Features:\n// - minChars: don't emit until enough content (avoids fragment spam)\n// - Paragraph-aware breaks: prefer splitting at \\n\\n boundaries\n// - Code fence protection: never split inside ```...``` blocks\n// - Coalescing: merge rapid consecutive blocks via idle debounce\n// - Human-like pacing: optional randomized delay between blocks\n\nexport interface BlockStreamConfig {\n /** Minimum characters before emitting a block (default: 80) */\n minChars: number\n /** Maximum characters per block (default: 4000) */\n maxChars: number\n /** Idle time (ms) before flushing partial content (default: 2000) */\n idleFlushMs: number\n /** Minimum time between emissions (ms) (default: 1500) */\n minIntervalMs: number\n /** Whether to add human-like random delay (default: false) */\n humanPacing: boolean\n}\n\nconst DEFAULT_CONFIG: BlockStreamConfig = {\n minChars: 80,\n maxChars: 4000,\n idleFlushMs: 2000,\n minIntervalMs: 1500,\n humanPacing: false,\n}\n\n/** Per-channel default overrides */\nexport const CHANNEL_DEFAULTS: Record<string, Partial<BlockStreamConfig>> = {\n telegram: { minChars: 60, maxChars: 4096, minIntervalMs: 1500 },\n whatsapp: { minChars: 40, maxChars: 3000, minIntervalMs: 2000, humanPacing: true },\n discord: { minChars: 80, maxChars: 2000, minIntervalMs: 1000 },\n gitlab: { minChars: 200, maxChars: 60000, minIntervalMs: 3000 },\n}\n\nexport type BlockEmitter = (block: string) => void | Promise<void>\n\n/**\n * Smart block streamer that accumulates text deltas and emits\n * well-formed blocks respecting markdown structure.\n */\nexport class BlockStream {\n private config: BlockStreamConfig\n private buffer = \"\"\n private emitter: BlockEmitter\n private lastEmitTime = 0\n private idleTimer?: ReturnType<typeof setTimeout>\n private insideCodeFence = false\n private fencePattern = /^```/\n /**\n * Serialized chain of pending emissions. Each emit appends to this chain so\n * emissions run in order, and `flush()` can `await` the tail to guarantee\n * all emissions have completed before the caller proceeds — critical to\n * avoid the router racing to send a final message while a flushed-but-not-\n * yet-executed first emission is still queued (which would produce two\n * separate outbound messages instead of one send + subsequent edits).\n */\n private lastEmission: Promise<void> = Promise.resolve()\n\n constructor(emitter: BlockEmitter, config?: Partial<BlockStreamConfig>, channel?: string) {\n const channelDefaults = channel ? CHANNEL_DEFAULTS[channel] : {}\n this.config = { ...DEFAULT_CONFIG, ...channelDefaults, ...config }\n this.emitter = emitter\n }\n\n /**\n * Feed a text delta into the stream.\n */\n push(delta: string): void {\n this.buffer += delta\n\n // Reset idle timer\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n }\n\n // Track code fence state\n this.updateCodeFenceState(delta)\n\n // Check if we should emit\n if (this.shouldEmit()) {\n this.emit()\n } else {\n // Set idle flush timer\n this.idleTimer = setTimeout(() => {\n if (this.buffer.length > 0) {\n this.emit()\n }\n }, this.config.idleFlushMs)\n }\n }\n\n /**\n * Flush any remaining content (call when stream ends). Awaits all pending\n * emissions so the caller can safely inspect state (e.g. whether a message\n * was already sent) immediately after flush() returns.\n */\n async flush(): Promise<void> {\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n this.idleTimer = undefined\n }\n if (this.buffer.length > 0) {\n this.emitImmediate(this.buffer)\n this.buffer = \"\"\n }\n await this.lastEmission\n }\n\n /**\n * Get the full accumulated text so far (buffer + already emitted).\n */\n get pendingLength(): number {\n return this.buffer.length\n }\n\n private shouldEmit(): boolean {\n // Don't emit if inside a code fence (wait for it to close)\n if (this.insideCodeFence) return false\n\n // Don't emit below minimum chars\n if (this.buffer.length < this.config.minChars) return false\n\n // Respect minimum interval\n const elapsed = Date.now() - this.lastEmitTime\n if (elapsed < this.config.minIntervalMs) return false\n\n // Emit if we have a good break point\n return this.findBreakPoint() > 0\n }\n\n private emit(): void {\n const breakPoint = this.findBreakPoint()\n if (breakPoint <= 0 && this.buffer.length < this.config.maxChars) return\n\n const cutAt = breakPoint > 0\n ? breakPoint\n : Math.min(this.buffer.length, this.config.maxChars)\n\n const block = this.buffer.slice(0, cutAt)\n this.buffer = this.buffer.slice(cutAt)\n\n if (this.config.humanPacing) {\n const delay = 800 + Math.random() * 1700 // 800-2500ms\n setTimeout(() => this.emitImmediate(block), delay)\n } else {\n this.emitImmediate(block)\n }\n }\n\n private emitImmediate(block: string): void {\n this.lastEmitTime = Date.now()\n // Append to the serialized chain so emissions run in order and flush()\n // can await the tail. Swallow per-emission errors — streaming is\n // best-effort and must not break the chain for subsequent blocks.\n this.lastEmission = this.lastEmission.then(() =>\n Promise.resolve()\n .then(() => this.emitter(block))\n .catch(() => { /* best-effort */ })\n )\n }\n\n /**\n * Find the best point to break the buffer.\n * Priority: paragraph > newline > sentence > word.\n * Returns -1 if no good break found.\n */\n private findBreakPoint(): number {\n const buf = this.buffer\n const max = Math.min(buf.length, this.config.maxChars)\n\n // 1. Paragraph break (\\n\\n)\n const paraIdx = buf.lastIndexOf(\"\\n\\n\", max)\n if (paraIdx >= this.config.minChars) return paraIdx + 2\n\n // 2. Single newline\n const nlIdx = buf.lastIndexOf(\"\\n\", max)\n if (nlIdx >= this.config.minChars) return nlIdx + 1\n\n // 3. Sentence end (. ! ? followed by space)\n for (let i = max - 1; i >= this.config.minChars; i--) {\n if ((buf[i] === \".\" || buf[i] === \"!\" || buf[i] === \"?\") &&\n (i + 1 >= buf.length || buf[i + 1] === \" \" || buf[i + 1] === \"\\n\")) {\n return i + 1\n }\n }\n\n // 4. Hard cap\n if (buf.length >= this.config.maxChars) {\n // Find nearest word break\n const spaceIdx = buf.lastIndexOf(\" \", max)\n if (spaceIdx >= this.config.minChars) return spaceIdx + 1\n return max\n }\n\n return -1\n }\n\n /**\n * Track whether we're inside a code fence.\n * Don't break inside fences to keep markdown valid.\n */\n private updateCodeFenceState(delta: string): void {\n const lines = delta.split(\"\\n\")\n for (const line of lines) {\n if (this.fencePattern.test(line.trim())) {\n this.insideCodeFence = !this.insideCodeFence\n }\n }\n }\n}\n","import type { DaemonConfig } from \"@/daemon/config\"\nimport type { AgentRegistry } from \"@/agents/registry\"\nimport type { A2AMesh } from \"@/a2a/mesh\"\nimport type { ChannelAdapter, IncomingMessage, OutgoingMessage } from \"./types\"\nimport type { TelegramAdapter } from \"./telegram\"\nimport type { HookRegistry } from \"@/hooks\"\nimport { GroupLog } from \"./group-log\"\nimport { HandoverStore, type HandoverOverride } from \"./handover-store\"\nimport { BlockStream } from \"./block-stream\"\nimport { ellipsize, firstLines } from \"@/utils/ellipsize\"\nimport type { ServiceMatcher } from \"@/services/matcher\"\nimport type { BusinessLayer } from \"@/business\"\nimport { readFileSync, writeFileSync, existsSync, mkdirSync, appendFileSync } from \"fs\"\nimport { resolve, dirname } from \"path\"\n\n/**\n * Crash-safe inflight task log. Every message we commit to handling is\n * appended as a `start` entry to .agentx/router/inflight.jsonl. When the\n * handler returns (success OR error), we append a matching `done` entry.\n *\n * On daemon boot, any `start` without a `done` is replayed through the\n * handler with a `replay` flag that bypasses dedup — because a crash\n * between start and done means the user never got a reply.\n *\n * Trade-off vs always replaying: we only resume work that was IN PROGRESS\n * at crash time. Genuine handler failures (task returned an error) still\n * get their `done` marker and are NOT retried — so we don't infinite-loop\n * on a poison-pill message.\n */\ninterface InflightStart {\n type: \"start\"\n id: string\n channel: string\n accountId: string\n text: string\n sender: IncomingMessage[\"sender\"]\n group?: IncomingMessage[\"group\"]\n replyTo?: string\n replyToText?: string\n timestamp: string\n resolvedAgent?: string\n preferNode?: string\n ts: number\n}\n\nclass InflightLog {\n private filePath: string\n\n constructor(baseDir: string) {\n this.filePath = resolve(baseDir, \".agentx/router/inflight.jsonl\")\n mkdirSync(dirname(this.filePath), { recursive: true })\n }\n\n start(entry: Omit<InflightStart, \"type\" | \"ts\">): void {\n try {\n const line = JSON.stringify({ type: \"start\", ts: Date.now(), ...entry }) + \"\\n\"\n appendFileSync(this.filePath, line)\n } catch { /* best-effort persistence */ }\n }\n\n done(id: string): void {\n try {\n appendFileSync(this.filePath, JSON.stringify({ type: \"done\", id, ts: Date.now() }) + \"\\n\")\n } catch { /* */ }\n }\n\n /** Scan the log and return all `start` entries without a matching `done`. */\n loadUnfinished(): InflightStart[] {\n if (!existsSync(this.filePath)) return []\n try {\n const raw = readFileSync(this.filePath, \"utf-8\")\n const byId = new Map<string, InflightStart>()\n for (const line of raw.split(\"\\n\")) {\n if (!line.trim()) continue\n try {\n const entry = JSON.parse(line) as { type: string; id: string } & InflightStart\n if (entry.type === \"start\") byId.set(entry.id, entry as InflightStart)\n else if (entry.type === \"done\") byId.delete(entry.id)\n } catch { /* skip corrupt line */ }\n }\n return [...byId.values()]\n } catch { return [] }\n }\n\n /** Rewrite the file with only currently-unfinished entries. Call on graceful\n * stop or periodically — keeps the file from growing without bound. */\n compact(): void {\n try {\n const unfinished = this.loadUnfinished()\n const out = unfinished.map((e) => JSON.stringify(e)).join(\"\\n\") + (unfinished.length ? \"\\n\" : \"\")\n writeFileSync(this.filePath, out)\n } catch { /* */ }\n }\n}\n\n// --- Message Router ---\n// Routes channel messages to agents. Supports:\n// - Typing indicator while processing\n// - Streaming response edits\n// - Seen reaction (👀) on mention\n// - Bot-to-bot: if response mentions another agent, route it\n// - Correct bot account sends the reply (not always the first one)\n\nconst STREAM_EDIT_INTERVAL_MS = 1500\nconst TYPING_INTERVAL_MS = 4000\n\nexport class MessageRouter {\n private registry: AgentRegistry\n private config: DaemonConfig\n private channels: Map<string, ChannelAdapter> = new Map()\n private hooks?: HookRegistry\n private mesh?: A2AMesh\n private serviceMatcher?: ServiceMatcher\n private business?: BusinessLayer\n private groupLog: GroupLog\n /** Runtime per-chat routing overrides (handovers). Consulted at the top\n * of resolveAgent; config routes + mention matching only run when no\n * override applies. */\n private handoverStore: HandoverStore\n private log: (...args: unknown[]) => void\n\n /** Exposed so the admin panel + registry can share the same store. */\n getHandoverStore(): HandoverStore { return this.handoverStore }\n\n /**\n * Short-TTL cache of recently processed incoming message IDs, keyed by\n * `<channel>:<accountId>:<id>`. Prevents duplicate replies from:\n * - GitLab webhook retries (same object_attributes.id redelivered)\n * - WhatsApp Baileys double-emitting messages.upsert (notify + append)\n * - Telegram polling re-delivering an update after a long-poll hiccup\n * - A daemon crash-loop replaying messages the previous process had\n * already handled (fixed by persisting this map to disk — see below)\n *\n * The key INCLUDES accountId so two bots in the same Telegram group\n * (different accountId) both get to run their routing logic — the router's\n * downstream boundAccount check then picks the one bot that should actually\n * reply. Without accountId in the key, the second bot's legitimate view of\n * the same Telegram message_id would be wrongly suppressed.\n *\n * Persisted to .agentx/router/dedup.json so survival-across-restart is not\n * just a Telegram concern — the same universal LRU covers every channel.\n * Writes are debounced (every 20 new ids, or 5s, whichever first) and the\n * file is pruned to only live TTL entries before each write.\n */\n private recentMessageIds: Map<string, number> = new Map()\n private readonly MESSAGE_ID_TTL_MS = 5 * 60 * 1000\n private readonly MESSAGE_ID_MAX_ENTRIES = 2000\n private readonly DEDUP_STORE_PATH = \".agentx/router/dedup.json\"\n private dedupDirtyCount = 0\n private dedupSaveTimer?: ReturnType<typeof setTimeout>\n\n /** Crash-safe inflight log (see InflightLog class above). */\n private inflight: InflightLog\n\n constructor(\n registry: AgentRegistry,\n config: DaemonConfig,\n hooks?: HookRegistry,\n log: (...args: unknown[]) => void = console.error.bind(console, \"[router]\"),\n ) {\n this.registry = registry\n this.config = config\n this.hooks = hooks\n this.log = log\n this.groupLog = new GroupLog()\n this.handoverStore = new HandoverStore({ log: (...a) => log(\"[handover]\", ...a) })\n this.inflight = new InflightLog(process.cwd())\n this.loadDedupFromDisk()\n }\n\n private loadDedupFromDisk(): void {\n try {\n const p = resolve(process.cwd(), this.DEDUP_STORE_PATH)\n if (!existsSync(p)) return\n const raw = JSON.parse(readFileSync(p, \"utf-8\")) as Record<string, number>\n const now = Date.now()\n let restored = 0\n for (const [key, ts] of Object.entries(raw)) {\n if (typeof ts === \"number\" && now - ts < this.MESSAGE_ID_TTL_MS) {\n this.recentMessageIds.set(key, ts)\n restored++\n }\n }\n if (restored > 0) {\n this.log(`Router dedup: restored ${restored} recent message ids from disk`)\n }\n } catch {\n // Corrupt file — start fresh, not worth failing startup over\n }\n }\n\n private scheduleDedupSave(): void {\n this.dedupDirtyCount++\n // Fast flush on bursty traffic (every 20 new ids) plus a 5s debounce\n // safety net for slow-trickle activity so we never lose more than a few\n // seconds of entries in a hard crash.\n if (this.dedupDirtyCount >= 20) {\n this.flushDedupToDisk()\n return\n }\n if (this.dedupSaveTimer) return\n this.dedupSaveTimer = setTimeout(() => this.flushDedupToDisk(), 5000)\n }\n\n private flushDedupToDisk(): void {\n if (this.dedupSaveTimer) {\n clearTimeout(this.dedupSaveTimer)\n this.dedupSaveTimer = undefined\n }\n this.dedupDirtyCount = 0\n try {\n const now = Date.now()\n const live: Record<string, number> = {}\n for (const [key, ts] of this.recentMessageIds) {\n if (now - ts < this.MESSAGE_ID_TTL_MS) live[key] = ts\n }\n const p = resolve(process.cwd(), this.DEDUP_STORE_PATH)\n const dir = dirname(p)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileSync(p, JSON.stringify(live))\n } catch {\n // Best-effort — a failed write means at-most-restart-window of replays,\n // not a correctness problem.\n }\n }\n\n /** Called by the daemon on graceful shutdown so no in-memory ids are lost. */\n flushPersistence(): void {\n this.flushDedupToDisk()\n // Compact the inflight log so the file doesn't grow unboundedly across\n // clean restarts. A crash skips this; the next boot just reads the\n // untrimmed log and does the same replay — still correct, just slightly\n // slower on the first scan.\n this.inflight.compact()\n }\n\n setMesh(mesh: A2AMesh): void {\n this.mesh = mesh\n }\n\n setServiceMatcher(matcher: ServiceMatcher): void {\n this.serviceMatcher = matcher\n }\n\n setBusiness(business: BusinessLayer): void {\n this.business = business\n }\n\n addChannel(adapter: ChannelAdapter): void {\n this.channels.set(adapter.name, adapter)\n adapter.onMessage((msg) => this.handleMessage(adapter, msg))\n }\n\n /**\n * Send an outbound message to any registered channel.\n * Used for agent-initiated messages, cron notifications, cross-channel routing.\n *\n * If accountId is not provided for Telegram, auto-resolves from agentId binding.\n */\n async sendOutbound(msg: OutgoingMessage & { accountId?: string }): Promise<string | void> {\n const adapter = this.channels.get(msg.channel)\n if (!adapter) {\n throw new Error(`Unknown channel: \"${msg.channel}\". Available: ${[...this.channels.keys()].join(\", \")}`)\n }\n\n // Auto-resolve Telegram accountId from agentId if not provided\n let accountId = msg.accountId\n if (adapter.name === \"telegram\" && !accountId && msg.agentId) {\n accountId = this.getAccountForAgent(msg.agentId)\n }\n\n this.log(`Outbound [${msg.channel}] -> ${msg.chatId}: ${msg.text.slice(0, 80)}`)\n\n if (adapter.name === \"telegram\" && accountId) {\n return this.adapterSend(adapter, { ...msg, accountId })\n }\n\n return adapter.send(msg)\n }\n\n /** List registered channel names. */\n getChannelNames(): string[] {\n return [...this.channels.keys()]\n }\n\n async startAll(): Promise<void> {\n for (const [name, adapter] of this.channels) {\n this.log(`Starting channel: ${name}`)\n await adapter.start()\n }\n // Replay any work the previous process committed to but never finished.\n // Deferred until after channel startup so adapters can actually send\n // replies. Fire-and-forget — we don't block normal traffic on it.\n this.replayInflight().catch((e) => this.log(`Inflight replay error: ${e.message}`))\n }\n\n /**\n * On startup, re-run any incoming message that was recorded as `start` but\n * never got a matching `done` in the inflight log. Those are tasks the\n * previous process committed to handling but crashed before completing.\n */\n private async replayInflight(): Promise<void> {\n const unfinished = this.inflight.loadUnfinished()\n if (unfinished.length === 0) return\n this.log(`Inflight replay: ${unfinished.length} task(s) to resume from previous run`)\n for (const entry of unfinished) {\n const adapter = this.channels.get(entry.channel)\n if (!adapter) {\n this.log(`Inflight replay: adapter \"${entry.channel}\" not available, skipping task ${entry.id}`)\n // Mark it done so we don't re-attempt forever — channel may be\n // permanently disabled in the current config.\n this.inflight.done(entry.id)\n continue\n }\n const msg: IncomingMessage = {\n id: entry.id,\n channel: entry.channel,\n accountId: entry.accountId,\n sender: entry.sender,\n group: entry.group,\n text: entry.text,\n replyTo: entry.replyTo,\n replyToText: entry.replyToText,\n timestamp: new Date(entry.timestamp),\n resolvedAgent: entry.resolvedAgent,\n preferNode: entry.preferNode,\n }\n this.log(`Inflight replay: ${entry.channel}/${entry.id} (agent=${entry.resolvedAgent || \"resolve\"})`)\n this.handleMessage(adapter, msg, { replay: true })\n .catch((e) => this.log(`Inflight replay failed for ${entry.id}: ${e.message}`))\n // Always mark done on replay — single-shot by design. If the replayed\n // handler re-committed and wrote its own done, the duplicate is\n // harmless (loadUnfinished dedups by id). If the handler returned\n // early (agent removed from config, no adapter, etc.), this clears\n // the stale entry so we don't retry forever.\n .finally(() => this.inflight.done(entry.id))\n }\n // Compact the file now that we've snapshotted — old entries are either\n // being replayed (will get a fresh start/done) or already drained.\n this.inflight.compact()\n }\n\n async stopAll(): Promise<void> {\n for (const [name, adapter] of this.channels) {\n this.log(`Stopping channel: ${name}`)\n await adapter.stop()\n }\n }\n\n /**\n * Returns true if this message id was processed within MESSAGE_ID_TTL_MS.\n * Stamps the key when false. Size-bounded GC avoids unbounded growth.\n */\n private isDuplicateMessage(msg: IncomingMessage): boolean {\n if (!msg.id) return false\n const key = `${msg.channel}:${msg.accountId || \"default\"}:${msg.id}`\n const now = Date.now()\n if (this.recentMessageIds.size >= this.MESSAGE_ID_MAX_ENTRIES) {\n for (const [k, t] of this.recentMessageIds) {\n if (now - t > this.MESSAGE_ID_TTL_MS) this.recentMessageIds.delete(k)\n }\n }\n const seen = this.recentMessageIds.get(key)\n if (seen && now - seen < this.MESSAGE_ID_TTL_MS) return true\n this.recentMessageIds.set(key, now)\n this.scheduleDedupSave()\n return false\n }\n\n private async handleMessage(\n adapter: ChannelAdapter,\n msg: IncomingMessage,\n opts: { replay?: boolean } = {},\n ): Promise<void> {\n // Dedup: drop redeliveries of the same incoming message id within a TTL.\n // Guards against GitLab webhook retries, Baileys double-emit, and Telegram\n // offset hiccups. Scoped per-accountId so multi-bot Telegram groups still\n // route each account's legitimate view of the message.\n //\n // Replay-on-resume (from inflight log) deliberately bypasses dedup — a\n // replayed message is one the previous process committed to handling but\n // crashed before finishing, so the dedup entry is stale and the user\n // still hasn't received their answer.\n if (!opts.replay && this.isDuplicateMessage(msg)) {\n this.log(`Duplicate ${msg.channel} message dropped: ${msg.accountId || \"default\"}/${msg.id}`)\n return\n }\n\n // Pre-hook\n if (this.hooks?.has(\"pre:channel-message\" as any)) {\n const hookResult = await this.hooks.execute(\"pre:channel-message\" as any, {\n event: \"pre:channel-message\" as any,\n channel: msg.channel,\n sender: msg.sender.name,\n text: msg.text,\n group: msg.group?.name,\n })\n\n if (hookResult.blocked) {\n this.log(`Message blocked by hook: ${hookResult.message}`)\n return\n }\n\n if (hookResult.modified?.text) {\n msg = { ...msg, text: hookResult.modified.text as string }\n }\n }\n\n // Log ALL group messages for conversation context (before agent resolution)\n if (msg.group) {\n const chatId = msg.group.id\n this.groupLog.add(chatId, msg.sender.name, msg.text)\n }\n\n // Check if message matches a defined service (before agent routing)\n if (this.serviceMatcher) {\n const chatId = msg.group?.id || msg.sender.id\n const matched = this.serviceMatcher.match(msg.text, msg.sender.id, msg.channel)\n if (matched) {\n this.log(`Service matched: \"${matched.service.name}\" for ${msg.sender.name} (trigger: ${matched.trigger})`)\n const replyAccountId = msg.accountId\n this.adapterReact(adapter, chatId, msg.id, \"👀\", replyAccountId)\n const typingTimer = this.startTypingLoop(adapter, chatId, replyAccountId)\n\n await this.serviceMatcher.execute(\n matched.service,\n this.registry,\n { channel: msg.channel, sender: msg.sender.name, chatId },\n async (text) => {\n clearInterval(typingTimer)\n await this.adapterSend(adapter, {\n channel: msg.channel, chatId, text, replyTo: msg.id, accountId: replyAccountId,\n })\n },\n )\n return\n }\n }\n\n // Resolve agent — check local first, then mesh peers\n const agentId = this.resolveAgent(msg)\n\n if (!agentId) {\n return\n }\n\n // Dedup: in groups, multiple bot accounts receive the same message.\n // Only the account BOUND to this agent should handle it.\n if (msg.group && msg.channel === \"telegram\") {\n const boundAccount = this.getAccountForAgent(agentId)\n if (boundAccount && boundAccount !== msg.accountId) {\n return\n }\n }\n\n const chatId = msg.group?.id || msg.sender.id\n\n // From here on we're committed to processing this message. Write a `start`\n // entry to the inflight log so a crash between now and the task's\n // completion gets the replay treatment on next boot. `done` is always\n // written via the try/finally wrapper further down.\n this.inflight.start({\n id: msg.id,\n channel: msg.channel,\n accountId: msg.accountId,\n text: msg.text,\n sender: msg.sender,\n group: msg.group,\n replyTo: msg.replyTo,\n replyToText: msg.replyToText,\n timestamp: (msg.timestamp instanceof Date ? msg.timestamp : new Date()).toISOString(),\n resolvedAgent: agentId,\n preferNode: msg.preferNode,\n })\n\n try { return await this.processResolvedMessage(adapter, msg, agentId, chatId) }\n finally { this.inflight.done(msg.id) }\n }\n\n /** The actual processing body — split out so handleMessage can wrap it in\n * the inflight try/finally without drowning the happy path in indentation. */\n private async processResolvedMessage(\n adapter: ChannelAdapter,\n msg: IncomingMessage,\n agentId: string,\n chatId: string,\n ): Promise<void> {\n // If preferNode is set, skip local and route directly to the specified mesh peer\n if (msg.preferNode) {\n this.log(`preferNode=\"${msg.preferNode}\" — forcing mesh routing for agent \"${agentId}\"`)\n const routed = await this.handleViaMeshByPeer(adapter, msg, agentId, msg.preferNode)\n if (!routed) {\n this.log(`Mesh peer \"${msg.preferNode}\" not found or unhealthy for agent \"${agentId}\"`)\n }\n return\n }\n\n const agentDef = this.registry.getAgent(agentId)\n\n // Agent not found locally — try forwarding to a mesh peer\n if (!agentDef) {\n const routed = await this.handleViaMeshByAgentId(adapter, msg, agentId)\n if (!routed) {\n this.log(`Agent \"${agentId}\" not found locally or on any mesh peer`)\n }\n return\n }\n\n const agentName = agentDef.name || agentId\n\n // Determine which bot account should send the response\n const replyAccountId = this.getAccountForAgent(agentId) || msg.accountId\n\n this.log(\n `Routing [${msg.channel}/${msg.sender.name}] -> \"${agentName}\": ${msg.text.slice(0, 80)}`,\n )\n\n // React with 👀 to acknowledge\n this.adapterReact(adapter, chatId, msg.id, \"👀\", replyAccountId)\n\n // Start typing indicator loop (from the correct bot)\n const typingTimer = this.startTypingLoop(adapter, chatId, replyAccountId)\n\n // Streaming setup with smart block streaming\n const canStream = typeof adapter.editMessage === \"function\"\n let sentMessageId: string | undefined\n let fullStreamText = \"\"\n\n // Smart block streamer handles chunking, code fence protection, and pacing\n const blockStream = canStream\n ? new BlockStream(\n async (block: string) => {\n fullStreamText += block\n if (!sentMessageId) {\n const preview = fullStreamText.length > 20\n ? fullStreamText\n : `_${agentName} is writing..._\\n\\n${fullStreamText}`\n try {\n sentMessageId = await this.adapterSend(adapter, {\n channel: msg.channel,\n chatId,\n text: preview,\n replyTo: msg.id,\n accountId: replyAccountId,\n })\n } catch { /* retry next block */ }\n } else {\n try {\n await this.adapterEdit(adapter, chatId, sentMessageId, fullStreamText, undefined, replyAccountId)\n } catch { /* retry next block */ }\n }\n },\n undefined,\n msg.channel,\n )\n : undefined\n\n const onDelta = blockStream\n ? (_delta: string, _fullText: string) => {\n blockStream.push(_delta)\n }\n : undefined\n\n // Enrich channelMeta agents with handles from config\n if (msg.channelMeta?.agents) {\n for (const agent of msg.channelMeta.agents) {\n if (!agent.handle) {\n const def = this.registry.getAgent(agent.id)\n if (def) {\n agent.handle = def.mentions.find((m: string) => m.startsWith(\"@\"))\n agent.name = def.name\n }\n }\n }\n }\n\n // Build group conversation context (recent messages from the group)\n const groupContext = msg.group ? this.groupLog.buildContext(chatId) : \"\"\n const messageWithContext = groupContext\n ? `${groupContext}\\n\\n${msg.sender.name}: ${msg.text}`\n : msg.text\n\n // Execute agent task\n const response = await this.registry.execute(\n {\n message: messageWithContext,\n agentId,\n context: {\n channel: msg.channel,\n sender: msg.sender.name,\n senderId: msg.sender.id,\n senderUsername: msg.sender.username,\n group: msg.group?.name,\n chatId, // stable ID for session keying (issue path for GitLab, group ID for Telegram)\n mediaPath: msg.media?.path,\n mediaType: msg.media?.type,\n replyToText: msg.replyToText,\n channelMeta: msg.channelMeta,\n },\n },\n onDelta,\n )\n\n clearInterval(typingTimer)\n\n // Flush any remaining streamed content. Awaited so that sentMessageId is\n // guaranteed set (if a stream block arrived) before we decide whether the\n // final write goes via adapterSend (new message) or adapterEdit (update).\n // Skipping the await caused double-sends on fast streams: the flushed\n // first emission queued an adapterSend, the router moved on with\n // sentMessageId still undefined, then the router's own adapterSend fired\n // — producing two separate messages instead of one edited.\n await blockStream?.flush()\n\n if (response.error) {\n // Queued messages are not errors — the message will be processed later\n if (response.error.startsWith(\"__queued__\")) {\n clearInterval(typingTimer)\n this.log(`Message queued for ${agentName}`)\n return\n }\n\n this.log(`Agent error: ${response.error}`)\n const errorText = `Error: ${response.error}`\n if (sentMessageId) {\n await this.adapterEdit(adapter, chatId, sentMessageId, errorText, \"plain\", replyAccountId)\n } else {\n await this.adapterSend(adapter, {\n channel: msg.channel,\n chatId,\n text: errorText,\n replyTo: msg.id,\n parseMode: \"plain\",\n accountId: replyAccountId,\n })\n }\n return\n }\n\n // Post-hook\n let responseText = response.content\n if (this.hooks?.has(\"post:channel-message\" as any)) {\n const hookResult = await this.hooks.execute(\"post:channel-message\" as any, {\n event: \"post:channel-message\" as any,\n channel: msg.channel,\n sender: msg.sender.name,\n response: responseText,\n agentId,\n })\n\n if (hookResult.blocked) {\n this.log(`Response blocked by hook: ${hookResult.message}`)\n return\n }\n\n if (hookResult.modified?.response) {\n responseText = hookResult.modified.response as string\n }\n }\n\n // Prefix response with agent identity on shared-identity channels.\n // GitLab: when no per-agent token (all share one user)\n // WhatsApp: always (single phone number, all agents share it)\n if (msg.channel === \"gitlab\" && responseText) {\n const gitlabAdapter = this.channels.get(\"gitlab\") as any\n const hasOwnToken = gitlabAdapter?.getAgentToken?.(agentId)\n if (!hasOwnToken) {\n responseText = `> **${agentName}** (${agentId})\\n\\n${responseText}`\n }\n }\n if (msg.channel === \"whatsapp\" && responseText) {\n responseText = `*${agentName}*\\n\\n${responseText}`\n }\n\n // Final message\n let sentResponseId: string | undefined\n if (responseText) {\n if (sentMessageId) {\n await this.adapterEdit(adapter, chatId, sentMessageId, responseText, undefined, replyAccountId)\n sentResponseId = sentMessageId\n } else {\n sentResponseId = await this.adapterSend(adapter, {\n channel: msg.channel,\n chatId,\n text: responseText,\n replyTo: msg.id,\n accountId: replyAccountId,\n agentId,\n })\n }\n }\n\n // Log bot response in group conversation\n if (msg.group && responseText) {\n this.groupLog.add(chatId, agentName, responseText)\n }\n\n // Notify on long-running task completion (cross-channel)\n const notifyConfig = this.config.notifications\n if (notifyConfig?.destination && response.duration) {\n const thresholdMs = (notifyConfig.longTaskThreshold || 30) * 1000\n const shouldNotify =\n (response.duration >= thresholdMs && !response.error && notifyConfig.on?.taskComplete) ||\n (response.error && notifyConfig.on?.taskError)\n\n if (shouldNotify) {\n const durSec = Math.round(response.duration / 1000)\n const status = response.error ? \"failed\" : \"completed\"\n // Preserve line boundaries so pipeline/MR bodies aren't chopped\n // mid-word (e.g., \"Duration: 328s\\nPipeline #369\" shouldn't become\n // \"Duration: 328s\\nP\"). Keep the first few lines + more chars.\n const trigger = firstLines(msg.text, 6, 300)\n const preview = response.error\n ? ellipsize(response.error, 300)\n : firstLines(response.content, 6, 400)\n const notifyText = `${status === \"failed\" ? \"🔴\" : \"✅\"} **${agentName}** ${status} (${durSec}s)\\n${msg.channel}/${msg.sender.name}: ${trigger}\\n${preview}`\n\n this.sendOutbound({\n channel: notifyConfig.destination.channel,\n chatId: notifyConfig.destination.chatId,\n text: notifyText,\n agentId,\n accountId: notifyConfig.destination.accountId,\n }).catch((e) => {\n this.log(`Task notification failed: ${e.message}`)\n })\n }\n }\n\n // Business layer: record task completion for KPI utilization tracking.\n if (this.business && response.duration) {\n this.business.recordTaskCompletion(\n agentId,\n Math.round(response.duration / 1000),\n !response.error,\n msg.channel,\n )\n }\n\n // GitLab: auto-log time spent on the issue/MR\n if (msg.channel === \"gitlab\" && response.duration && !response.error) {\n const gitlabAdapter = this.channels.get(\"gitlab\") as any\n if (gitlabAdapter) {\n gitlabAdapter.logTimeSpent(chatId, response.duration, agentId).catch((e: any) => {\n this.log(`GitLab time tracking failed: ${e.message}`)\n })\n }\n }\n\n // Bot-to-bot delegation: if response mentions another agent, route to them.\n // Works on Telegram, WhatsApp, and Discord. Not GitLab (uses its own @mention webhook flow).\n const delegationChannels = [\"telegram\", \"whatsapp\", \"discord\", \"slack\"]\n if (responseText && sentResponseId && delegationChannels.includes(msg.channel)) {\n this.handleBotToBotChain(adapter, msg, agentId, responseText, sentResponseId, 0).catch((e) => {\n this.log(`Bot-to-bot error: ${e.message}`)\n })\n }\n }\n\n private static readonly MAX_BOT_CHAIN_DEPTH = 3\n\n /**\n * Bot-to-bot conversation chain.\n * Guards:\n * 1. Max depth (default 3)\n * 2. No agent called twice in the same chain (prevents A→B→A→B loops)\n * Works on Telegram (multi-account), WhatsApp (shared number), Discord.\n */\n private async handleBotToBotChain(\n adapter: ChannelAdapter,\n originalMsg: IncomingMessage,\n sourceAgentId: string,\n responseText: string,\n responseMessageId: string,\n depth: number,\n visited: Set<string> = new Set(),\n ): Promise<void> {\n if (depth >= MessageRouter.MAX_BOT_CHAIN_DEPTH) {\n this.log(`Bot-to-bot: max depth (${depth}) reached, stopping`)\n return\n }\n\n // Track who's been in this chain\n visited.add(sourceAgentId)\n\n for (const [id, def] of Object.entries(this.config.agents)) {\n if (id === sourceAgentId) continue\n\n // Stop if this agent was already in the chain (prevents A→B→A loop)\n if (visited.has(id)) {\n this.log(`Bot-to-bot: \"${id}\" already participated, stopping chain`)\n continue\n }\n\n // Only trigger bot-to-bot on explicit @-handle mentions (e.g. @my_bot),\n // not bare keywords which appear in normal conversation text.\n const atMentions = def.mentions.filter((m: string) => m.startsWith(\"@\"))\n const mentioned = atMentions.some((m: string) =>\n responseText.toLowerCase().includes(m.toLowerCase()),\n )\n if (!mentioned) continue\n\n this.log(`Bot-to-bot [${depth + 1}]: \"${sourceAgentId}\" -> \"${id}\"`)\n\n const chatId = originalMsg.group?.id || originalMsg.sender.id\n const targetAccountId = this.getAccountForAgent(id)\n const sourceAccountId = this.getAccountForAgent(sourceAgentId)\n\n try {\n // Target bot reacts 👀 to the source bot's message\n this.adapterReact(adapter, chatId, responseMessageId, \"👀\", targetAccountId)\n\n // Target bot shows typing\n const typingTimer = this.startTypingLoop(adapter, chatId, targetAccountId)\n\n // Include original user message as context so target bot knows the full picture\n const contextMessage = depth === 0\n ? `[Original from ${originalMsg.sender.name}]: ${originalMsg.text}\\n\\n[${sourceAgentId} said]: ${responseText}`\n : responseText\n\n const response = await this.registry.execute({\n message: contextMessage,\n agentId: id,\n context: {\n channel: originalMsg.channel,\n sender: `agent:${sourceAgentId}`,\n group: originalMsg.group?.name,\n },\n })\n\n clearInterval(typingTimer)\n\n if (response.content && !response.error) {\n // Prefix with agent identity on shared-number channels\n let replyText = response.content\n if (originalMsg.channel === \"whatsapp\") {\n replyText = `*${def.name}*\\n\\n${replyText}`\n }\n\n const sentId = await this.adapterSend(adapter, {\n channel: originalMsg.channel,\n chatId,\n text: replyText,\n accountId: targetAccountId,\n })\n\n // Chain: check if this response also mentions another agent\n if (sentId && response.content) {\n await this.handleBotToBotChain(\n adapter, originalMsg, id, response.content, sentId as string, depth + 1, visited,\n )\n }\n } else if (response.error) {\n this.log(`Bot-to-bot \"${id}\" error: ${response.error}`)\n }\n } catch (e: any) {\n this.log(`Bot-to-bot \"${id}\" failed: ${e.message}`)\n }\n\n break // Route to first mentioned agent per level\n }\n }\n\n // --- Adapter helpers that pass accountId for Telegram ---\n\n private async adapterSend(\n adapter: ChannelAdapter,\n msg: { channel: string; chatId: string; text: string; replyTo?: string; parseMode?: string; accountId?: string; agentId?: string },\n ): Promise<string> {\n // For Telegram, pass accountId so the correct bot sends the message\n if (adapter.name === \"telegram\" && msg.accountId) {\n return (adapter as unknown as TelegramAdapter).send({\n ...msg,\n parseMode: msg.parseMode as any,\n accountId: msg.accountId,\n }) as Promise<string>\n }\n return (adapter.send(msg as any) || \"\") as Promise<string>\n }\n\n private async adapterEdit(\n adapter: ChannelAdapter,\n chatId: string,\n messageId: string,\n text: string,\n parseMode?: string,\n accountId?: string,\n ): Promise<boolean> {\n if (adapter.name === \"telegram\" && accountId) {\n return (adapter as unknown as TelegramAdapter).editMessage(chatId, messageId, text, parseMode, accountId)\n }\n return adapter.editMessage?.(chatId, messageId, text, parseMode) ?? false\n }\n\n private adapterReact(\n adapter: ChannelAdapter,\n chatId: string,\n messageId: string,\n emoji: string,\n accountId?: string,\n ): void {\n if (adapter.name === \"telegram\" && accountId) {\n (adapter as unknown as TelegramAdapter).react(chatId, messageId, emoji, accountId)\n } else {\n adapter.react?.(chatId, messageId, emoji)\n }\n }\n\n private startTypingLoop(\n adapter: ChannelAdapter,\n chatId: string,\n accountId?: string,\n ): ReturnType<typeof setInterval> {\n const sendTyping = () => {\n if (adapter.name === \"telegram\" && accountId) {\n (adapter as unknown as TelegramAdapter).sendTyping(chatId, accountId)\n } else {\n adapter.sendTyping?.(chatId)\n }\n }\n\n sendTyping()\n return setInterval(sendTyping, TYPING_INTERVAL_MS)\n }\n\n // --- Agent resolution ---\n\n /**\n * Handle a message by routing to a mesh peer's agent.\n * Searches peer agent cards for mention matches.\n */\n private async handleViaMesh(\n adapter: ChannelAdapter,\n msg: IncomingMessage,\n ): Promise<boolean> {\n if (!this.mesh) return false\n\n const textLower = msg.text.toLowerCase()\n const directory = this.mesh.directory()\n\n for (const peer of directory) {\n if (!peer.healthy) continue\n\n for (const skill of peer.skills) {\n // Check if the message mentions this remote agent by name or ID\n if (\n textLower.includes(skill.id.toLowerCase()) ||\n textLower.includes(skill.name.toLowerCase())\n ) {\n this.log(`Mesh routing [${msg.channel}/${msg.sender.name}] -> peer \"${peer.peer}\" agent \"${skill.id}\"`)\n\n const chatId = msg.group?.id || msg.sender.id\n const replyAccountId = msg.accountId\n\n // React + typing\n this.adapterReact(adapter, chatId, msg.id, \"👀\", replyAccountId)\n const typingTimer = this.startTypingLoop(adapter, chatId, replyAccountId)\n\n try {\n const response = await this.mesh.sendTask(peer.peer, msg.text, skill.id)\n\n clearInterval(typingTimer)\n\n if (response) {\n // Prefix with remote agent name so user knows who's responding\n const header = `**${skill.name}** _(${peer.peer})_:\\n\\n`\n await this.adapterSend(adapter, {\n channel: msg.channel,\n chatId,\n text: header + response,\n replyTo: msg.id,\n accountId: replyAccountId,\n })\n }\n\n return true\n } catch (e: any) {\n clearInterval(typingTimer)\n this.log(`Mesh routing error: ${e.message}`)\n\n await this.adapterSend(adapter, {\n channel: msg.channel,\n chatId,\n text: `Error from ${peer.peer}/${skill.name}: ${e.message}`,\n replyTo: msg.id,\n parseMode: \"plain\",\n accountId: replyAccountId,\n })\n return true\n }\n }\n }\n }\n\n return false\n }\n\n /**\n * Route to a mesh peer by resolved agentId (not text matching).\n * Used when the channel adapter resolved the agent but it's not local.\n */\n private async handleViaMeshByAgentId(\n adapter: ChannelAdapter,\n msg: IncomingMessage,\n agentId: string,\n ): Promise<boolean> {\n if (!this.mesh) return false\n\n const directory = this.mesh.directory()\n\n for (const peer of directory) {\n if (!peer.healthy) continue\n\n const skill = peer.skills.find(s => s.id === agentId)\n if (!skill) continue\n\n this.log(`Mesh routing by agentId [${msg.channel}/${msg.sender.name}] -> peer \"${peer.peer}\" agent \"${agentId}\"`)\n\n const chatId = msg.group?.id || msg.sender.id\n const replyAccountId = msg.accountId\n\n this.adapterReact(adapter, chatId, msg.id, \"👀\", replyAccountId)\n const typingTimer = this.startTypingLoop(adapter, chatId, replyAccountId)\n\n try {\n const response = await this.mesh.sendTask(peer.peer, msg.text, agentId)\n clearInterval(typingTimer)\n\n if (response) {\n await this.adapterSend(adapter, {\n channel: msg.channel,\n chatId,\n text: response,\n replyTo: msg.id,\n accountId: replyAccountId,\n })\n }\n\n return true\n } catch (e: any) {\n clearInterval(typingTimer)\n this.log(`Mesh routing error for ${agentId}: ${e.message}`)\n\n await this.adapterSend(adapter, {\n channel: msg.channel,\n chatId,\n text: `Error from ${peer.peer}/${skill.name}: ${e.message}`,\n replyTo: msg.id,\n parseMode: \"plain\",\n accountId: replyAccountId,\n })\n return true\n }\n }\n\n return false\n }\n\n /**\n * Route to a specific named mesh peer by agentId.\n * Used when preferNode is set on the incoming message.\n */\n private async handleViaMeshByPeer(\n adapter: ChannelAdapter,\n msg: IncomingMessage,\n agentId: string,\n peerName: string,\n ): Promise<boolean> {\n if (!this.mesh) return false\n\n const directory = this.mesh.directory()\n const peer = directory.find(p => p.peer === peerName)\n\n if (!peer || !peer.healthy) return false\n\n const skill = peer.skills.find(s => s.id === agentId) ?? { name: agentId, id: agentId }\n\n this.log(`Mesh routing by preferNode [${msg.channel}/${msg.sender.name}] -> peer \"${peerName}\" agent \"${agentId}\"`)\n\n const chatId = msg.group?.id || msg.sender.id\n const replyAccountId = msg.accountId\n\n this.adapterReact(adapter, chatId, msg.id, \"👀\", replyAccountId)\n const typingTimer = this.startTypingLoop(adapter, chatId, replyAccountId)\n\n const start = Date.now()\n try {\n const response = await this.mesh.sendTask(peerName, msg.text, agentId)\n const duration = Date.now() - start\n clearInterval(typingTimer)\n\n if (response) {\n await this.adapterSend(adapter, {\n channel: msg.channel,\n chatId,\n text: response,\n replyTo: msg.id,\n accountId: replyAccountId,\n })\n }\n\n if (msg.channel === \"gitlab\" && duration) {\n const gitlabAdapter = this.channels.get(\"gitlab\") as any\n if (gitlabAdapter) {\n gitlabAdapter.logTimeSpent(chatId, duration, agentId).catch((e: any) => {\n this.log(`GitLab time tracking (mesh) failed: ${e.message}`)\n })\n }\n }\n\n return true\n } catch (e: any) {\n clearInterval(typingTimer)\n this.log(`Mesh routing error for ${peerName}/${agentId}: ${e.message}`)\n\n await this.adapterSend(adapter, {\n channel: msg.channel,\n chatId,\n text: `Error from ${peerName}/${skill.name}: ${e.message}`,\n replyTo: msg.id,\n parseMode: \"plain\",\n accountId: replyAccountId,\n })\n return true\n }\n }\n\n private getAccountForAgent(agentId: string): string | undefined {\n for (const [accountId, account] of Object.entries(this.config.channels.telegram.accounts)) {\n if (account.agentBinding === agentId) {\n return accountId\n }\n }\n return undefined\n }\n\n private resolveAgent(msg: IncomingMessage): string | undefined {\n // For GitLab: the adapter resolves agents deterministically via agentMappings\n // (GitLab @username -> agentId). Don't use registry.findByMention() here\n // because that matches Telegram handles, not GitLab usernames.\n if (msg.channel === \"gitlab\") {\n if (msg.resolvedAgent) {\n this.log(`GitLab agent resolved by adapter: ${msg.resolvedAgent}`)\n return msg.resolvedAgent\n }\n return undefined\n }\n\n // Runtime override takes precedence over every config-driven route.\n // This is how operator-initiated handovers redirect traffic without a\n // daemon restart.\n const chatId = msg.group?.id ?? msg.sender.id\n const override = this.handoverStore.get(msg.channel, chatId, msg.accountId)\n if (override) {\n this.log(\n `[handover] ${msg.channel}:${chatId}${msg.accountId ? `:${msg.accountId}` : \"\"} -> ${override.toAgent} (handover)`,\n )\n return override.toAgent\n }\n\n // Pre-resolved by channel adapter (WhatsApp route-based, etc.)\n if (msg.resolvedAgent) {\n return msg.resolvedAgent\n }\n\n // DM: route to the account's bound agent\n if (!msg.group) {\n if (msg.channel === \"telegram\") {\n const account = this.config.channels.telegram.accounts[msg.accountId]\n return account?.agentBinding\n }\n if (msg.channel === \"whatsapp\") {\n return this.config.channels.whatsapp.defaultAgent\n }\n return undefined\n }\n\n // Messages from another bot (cross-daemon Telegram cascades, e.g. one bot\n // quoting another bot's handle in a prose reply) must only route on an\n // explicit `@`-prefixed handle of a local agent. Bare-word matches like\n // \"nadia\" in text or \"devops-mtgl\" mentioned in a reply would otherwise\n // trigger spurious activations across daemons.\n const atMentionsOnly = msg.sender.isBot === true\n\n // Group: check policy\n if (msg.channel === \"telegram\") {\n const policy = this.config.channels.telegram.policy\n if (policy.group === \"mention-required\") {\n const agentId = this.registry.findByMention(msg.text, { atMentionsOnly })\n if (!agentId) return undefined\n return agentId\n }\n }\n\n // Default: mention matching, then account binding\n const mentionAgent = this.registry.findByMention(msg.text, { atMentionsOnly })\n if (mentionAgent) return mentionAgent\n // If this is a bot-origin message with no explicit @ match, drop it —\n // don't fall through to account-binding which would route every bot reply\n // in the group to the bound agent.\n if (atMentionsOnly) return undefined\n\n if (msg.channel === \"telegram\") {\n const account = this.config.channels.telegram.accounts[msg.accountId]\n return account?.agentBinding\n }\n\n return this.config.channels.whatsapp.defaultAgent\n }\n}\n","// --- Convert standard Markdown to Telegram HTML ---\n//\n// Uses parse_mode: \"HTML\" (not MarkdownV2) — much simpler and more reliable.\n// Based on OpenClaw's approach: markdown → HTML tags.\n//\n// Supported Telegram HTML tags:\n// <b>bold</b>, <i>italic</i>, <s>strikethrough</s>,\n// <code>inline code</code>, <pre><code>code block</code></pre>,\n// <a href=\"url\">link</a>, <blockquote>quote</blockquote>,\n// <tg-spoiler>spoiler</tg-spoiler>\n//\n// Tables are converted to bullet-point format (Telegram has no table support).\n// File extensions that look like TLDs are wrapped in <code> to prevent\n// Telegram generating spurious link previews.\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n}\n\n/**\n * Convert standard Markdown (Claude output) to Telegram HTML.\n */\nexport function markdownToTelegramHtml(md: string): string {\n // Strip MarkdownV2 escape backslashes that Claude Code sometimes outputs\n // e.g., \\_ → _, \\# → #, \\| → |, \\. → ., \\! → !, \\- → -, \\( → (, \\) → )\n const cleaned = md.replace(/\\\\([_*\\[\\]()~`>#+=|{}.!\\\\-])/g, \"$1\")\n const lines = cleaned.split(\"\\n\")\n const result: string[] = []\n let inCodeBlock = false\n let codeBlockLang = \"\"\n let codeBlockLines: string[] = []\n let inBlockquote = false\n let tableHeaders: string[] = []\n let inTable = false\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n\n // Code block toggle\n if (line.trimStart().startsWith(\"```\")) {\n if (!inCodeBlock) {\n inCodeBlock = true\n codeBlockLang = line.trimStart().slice(3).trim()\n codeBlockLines = []\n continue\n } else {\n inCodeBlock = false\n const code = escapeHtml(codeBlockLines.join(\"\\n\"))\n // Language hint as comment for context\n const langHint = codeBlockLang ? `// ${codeBlockLang}\\n` : \"\"\n result.push(`<pre><code>${langHint}${code}</code></pre>`)\n codeBlockLang = \"\"\n continue\n }\n }\n\n if (inCodeBlock) {\n codeBlockLines.push(line)\n continue\n }\n\n // Close blockquote if we left it\n if (inBlockquote && !line.trimStart().startsWith(\">\")) {\n result.push(\"</blockquote>\")\n inBlockquote = false\n }\n\n // Close table if we left it\n if (inTable && !line.trim().startsWith(\"|\")) {\n inTable = false\n tableHeaders = []\n }\n\n // Blockquote\n if (line.trimStart().startsWith(\"> \")) {\n const content = line.replace(/^>\\s*/, \"\")\n if (!inBlockquote) {\n result.push(\"<blockquote>\")\n inBlockquote = true\n }\n result.push(convertInline(content))\n continue\n }\n\n // Headers → bold with blank line before\n const headerMatch = line.match(/^(#{1,6})\\s+(.+)$/)\n if (headerMatch) {\n result.push(\"\")\n result.push(`<b>${convertInline(headerMatch[2])}</b>`)\n continue\n }\n\n // Horizontal rule\n if (/^[-*_]{3,}\\s*$/.test(line.trim())) {\n result.push(\"———\")\n continue\n }\n\n // Table handling — convert to readable bullet format\n if (line.trim().startsWith(\"|\") && line.trim().endsWith(\"|\")) {\n const cells = line.split(\"|\").slice(1, -1).map(c => c.trim())\n\n // Skip separator rows (|---|---|)\n if (/^\\|[\\s\\-:|]+\\|$/.test(line.trim())) {\n inTable = true\n continue\n }\n\n if (!inTable) {\n // This is the header row\n tableHeaders = cells\n inTable = true\n continue\n }\n\n // Data row — render as bullet with header labels\n if (tableHeaders.length > 0 && cells.length > 0) {\n if (cells.length >= 2) {\n // First cell as bold label, rest as key: value pairs\n const parts: string[] = []\n for (let c = 0; c < cells.length; c++) {\n if (c === 0) {\n parts.push(`<b>${convertInline(cells[c])}</b>`)\n } else {\n const header = tableHeaders[c] ? `${convertInline(tableHeaders[c])}: ` : \"\"\n parts.push(`${header}${convertInline(cells[c])}`)\n }\n }\n result.push(`• ${parts.join(\" — \")}`)\n } else {\n result.push(`• ${convertInline(cells[0])}`)\n }\n }\n continue\n }\n\n // Unordered list\n const ulMatch = line.match(/^(\\s*)[-*+]\\s+(.+)$/)\n if (ulMatch) {\n const indent = ulMatch[1].length > 0 ? \" \" : \"\"\n result.push(`${indent}• ${convertInline(ulMatch[2])}`)\n continue\n }\n\n // Ordered list\n const olMatch = line.match(/^(\\s*)\\d+[.)]\\s+(.+)$/)\n if (olMatch) {\n const indent = olMatch[1].length > 0 ? \" \" : \"\"\n const num = line.match(/^(\\s*)(\\d+)/)?.[2] || \"1\"\n result.push(`${indent}${num}. ${convertInline(olMatch[2])}`)\n continue\n }\n\n // Empty line\n if (!line.trim()) {\n result.push(\"\")\n continue\n }\n\n // Normal text\n result.push(convertInline(line))\n }\n\n // Close unclosed blocks\n if (inCodeBlock) {\n result.push(`<pre><code>${escapeHtml(codeBlockLines.join(\"\\n\"))}</code></pre>`)\n }\n if (inBlockquote) {\n result.push(\"</blockquote>\")\n }\n\n let html = result.join(\"\\n\").trim()\n\n // Wrap file extensions that look like TLDs to prevent Telegram link previews\n // e.g., \"config.ts\" would get wrapped as \"config<code>.ts</code>\"\n html = wrapFileRefsInCode(html)\n\n return html\n}\n\n/**\n * Convert inline markdown to HTML tags.\n */\nfunction convertInline(text: string): string {\n let result = escapeHtml(text)\n\n // Inline code (first, to protect content from further processing)\n result = result.replace(/`([^`]+)`/g, \"<code>$1</code>\")\n\n // Links: [text](url) — must come before bold/italic to preserve URLs\n result = result.replace(\n /\\[([^\\]]+)\\]\\(([^)]+)\\)/g,\n (_m, label, url) => `<a href=\"${escapeHtml(url)}\">${label}</a>`,\n )\n\n // Bold+italic: ***text***\n result = result.replace(/\\*\\*\\*(.+?)\\*\\*\\*/g, \"<b><i>$1</i></b>\")\n\n // Bold: **text**\n result = result.replace(/\\*\\*(.+?)\\*\\*/g, \"<b>$1</b>\")\n\n // Italic: *text* or _text_ (not inside other tags)\n result = result.replace(/(?<!\\*)\\*([^*]+?)\\*(?!\\*)/g, \"<i>$1</i>\")\n\n // Strikethrough: ~~text~~\n result = result.replace(/~~(.+?)~~/g, \"<s>$1</s>\")\n\n // Spoiler: ||text||\n result = result.replace(/\\|\\|(.+?)\\|\\|/g, \"<tg-spoiler>$1</tg-spoiler>\")\n\n return result\n}\n\n/**\n * Wrap file extensions that share TLDs in <code> to prevent Telegram\n * from generating spurious link previews (e.g., config.ts, main.py).\n * Skips content already inside <code>, <pre>, or <a> tags.\n */\nconst FILE_EXT_TLDS = /(?<=\\w)\\.(ts|js|py|rs|go|rb|cs|sh|md|yml|yaml|toml|json|env|css|html|xml|sql|tf|hcl)(?=[\\s,;:)\\]}<]|$)/gi\n\nfunction wrapFileRefsInCode(html: string): string {\n // Split on existing tags to avoid modifying content inside them\n const parts = html.split(/(<\\/?(?:code|pre|a)[^>]*>)/gi)\n let insideTag = false\n\n return parts.map((part) => {\n if (/<(?:code|pre|a)\\b/i.test(part)) {\n insideTag = true\n return part\n }\n if (/<\\/(?:code|pre|a)>/i.test(part)) {\n insideTag = false\n return part\n }\n if (insideTag) return part\n\n return part.replace(FILE_EXT_TLDS, \"<code>.$1</code>\")\n }).join(\"\")\n}\n","import type { ChannelAdapter, IncomingMessage, OutgoingMessage, ChannelMeta } from \"./types\"\nimport { markdownToTelegramHtml } from \"./telegram-format\"\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from \"fs\"\nimport { resolve, dirname } from \"path\"\n\n// --- Telegram Bot API adapter (long-polling, no dependencies) ---\n\ninterface TelegramUpdate {\n update_id: number\n message?: {\n message_id: number\n from: { id: number; first_name: string; last_name?: string; username?: string; is_bot?: boolean }\n chat: { id: number; type: string; title?: string }\n text?: string\n caption?: string\n date: number\n reply_to_message?: { message_id: number; text?: string; caption?: string; from?: { first_name: string } }\n photo?: Array<{ file_id: string; file_unique_id: string; width: number; height: number; file_size?: number }>\n voice?: { file_id: string; duration: number; mime_type?: string }\n audio?: { file_id: string; duration: number; mime_type?: string; title?: string }\n video?: { file_id: string; duration: number; mime_type?: string }\n document?: { file_id: string; file_name?: string; mime_type?: string }\n sticker?: { file_id: string; emoji?: string }\n }\n my_chat_member?: {\n chat: { id: number; type: string; title?: string }\n from: { id: number; first_name: string }\n new_chat_member: {\n user: { id: number; username?: string; is_bot?: boolean }\n status: string // \"member\" | \"administrator\" | \"left\" | \"kicked\" | \"creator\"\n }\n }\n}\n\ninterface TelegramAccountConfig {\n token: string\n agentBinding: string\n allowFrom?: string[]\n}\n\n// --- Persistent group membership store ---\n// Tracks which bots are in which groups, persisted to .agentx/telegram/groups.json\n// Updated via my_chat_member events and one-time API seed per group.\n\ninterface GroupMembership {\n /** groupId → { accountId → { username, status, updatedAt } } */\n [groupId: string]: {\n name?: string\n bots: {\n [accountId: string]: {\n username: string\n agentId: string\n status: string // \"member\" | \"administrator\" | \"left\" | \"kicked\"\n updatedAt: string\n }\n }\n }\n}\n\nclass TelegramGroupStore {\n private data: GroupMembership = {}\n private filePath: string\n private dirty = false\n\n constructor(dataDir: string) {\n this.filePath = resolve(dataDir, \".agentx/telegram/groups.json\")\n this.load()\n }\n\n private load(): void {\n try {\n if (existsSync(this.filePath)) {\n this.data = JSON.parse(readFileSync(this.filePath, \"utf-8\"))\n }\n } catch {\n this.data = {}\n }\n }\n\n private save(): void {\n try {\n mkdirSync(dirname(this.filePath), { recursive: true })\n writeFileSync(this.filePath, JSON.stringify(this.data, null, 2))\n this.dirty = false\n } catch {\n // best-effort\n }\n }\n\n /** Record a bot's membership status in a group. */\n setBotStatus(groupId: string, groupName: string | undefined, accountId: string, agentId: string, username: string, status: string): void {\n if (!this.data[groupId]) {\n this.data[groupId] = { name: groupName, bots: {} }\n }\n if (groupName) this.data[groupId].name = groupName\n this.data[groupId].bots[accountId] = {\n username,\n agentId,\n status,\n updatedAt: new Date().toISOString(),\n }\n this.dirty = true\n this.save()\n }\n\n /** Get all active bots in a group. */\n getGroupBots(groupId: string): Array<{ accountId: string; agentId: string; username: string }> {\n const group = this.data[groupId]\n if (!group) return []\n return Object.entries(group.bots)\n .filter(([, info]) => info.status !== \"left\" && info.status !== \"kicked\")\n .map(([accountId, info]) => ({\n accountId,\n agentId: info.agentId,\n username: info.username,\n }))\n }\n\n /** Check if we have data for a group. */\n hasGroup(groupId: string): boolean {\n return !!this.data[groupId]\n }\n}\n\n/**\n * Persist long-poll `offset` per account to disk so a daemon restart doesn't\n * re-fetch updates Telegram still holds in its 24h retention window. Without\n * this, a crash loop (e.g. restart-counter cascade) causes the same message\n * to be handled N times — seen on 2026-04-15 when clawd spun through 20\n * systemd restarts while a zombie daemon held the pidfile, resulting in 6×\n * duplicate replies from the queued incoming messages.\n */\nclass TelegramOffsetStore {\n private offsets: Record<string, number> = {}\n private filePath: string\n private dirty = false\n private saveTimer?: ReturnType<typeof setTimeout>\n\n constructor(dataDir: string) {\n this.filePath = resolve(dataDir, \".agentx/telegram/offsets.json\")\n try {\n if (existsSync(this.filePath)) {\n this.offsets = JSON.parse(readFileSync(this.filePath, \"utf-8\"))\n }\n } catch { this.offsets = {} }\n }\n\n get(accountId: string): number {\n return this.offsets[accountId] || 0\n }\n\n set(accountId: string, offset: number): void {\n if (this.offsets[accountId] === offset) return\n this.offsets[accountId] = offset\n this.dirty = true\n // Debounce disk writes — each poll can produce many offset updates.\n if (!this.saveTimer) {\n this.saveTimer = setTimeout(() => this.flush(), 500)\n }\n }\n\n flush(): void {\n if (this.saveTimer) {\n clearTimeout(this.saveTimer)\n this.saveTimer = undefined\n }\n if (!this.dirty) return\n try {\n const dir = dirname(this.filePath)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileSync(this.filePath, JSON.stringify(this.offsets, null, 2))\n this.dirty = false\n } catch { /* best-effort */ }\n }\n}\n\nexport class TelegramAdapter implements ChannelAdapter {\n readonly name = \"telegram\"\n private accounts: Map<string, TelegramAccountConfig>\n private offsetStore: TelegramOffsetStore\n private handler?: (msg: IncomingMessage) => Promise<void>\n private polling = false\n private log: (...args: unknown[]) => void\n /** Global allowlist fallback. When a per-account allowFrom is unset,\n * this list applies. When BOTH are unset, everything is rejected\n * (closed by default). */\n private globalAllowFrom?: string[]\n\n constructor(\n accounts: Record<string, TelegramAccountConfig>,\n opts: { policy?: { allowFrom?: string[] } } = {},\n log: (...args: unknown[]) => void = console.error.bind(console, \"[telegram]\"),\n ) {\n this.accounts = new Map(Object.entries(accounts))\n this.globalAllowFrom = opts.policy?.allowFrom\n this.log = log\n this.groupStore = new TelegramGroupStore(process.cwd())\n this.offsetStore = new TelegramOffsetStore(process.cwd())\n }\n\n /** Effective sender allowlist for an account. Returns undefined when the\n * account has no explicit config AND the global policy is unset — the\n * caller treats undefined as \"closed, drop everything\". */\n private effectiveAllowFrom(accountId: string): string[] | undefined {\n const cfg = this.accounts.get(accountId)\n if (cfg?.allowFrom !== undefined) return cfg.allowFrom\n return this.globalAllowFrom\n }\n\n /** Accept a message only when at least one allowlist entry matches the\n * sender's user id, the chat id, or the sender's @username. */\n private isAllowed(\n accountId: string,\n fromId: number | string | undefined,\n chatId: number | string | undefined,\n fromUsername?: string,\n ): boolean {\n const list = this.effectiveAllowFrom(accountId)\n if (!list || list.length === 0) return false\n const fromStr = fromId != null ? String(fromId) : \"\"\n const chatStr = chatId != null ? String(chatId) : \"\"\n const userLc = fromUsername?.toLowerCase()\n for (const entry of list) {\n if (!entry) continue\n if (entry === fromStr || entry === chatStr) return true\n if (entry.startsWith(\"@\") && userLc && entry.slice(1).toLowerCase() === userLc) return true\n }\n return false\n }\n\n onMessage(handler: (msg: IncomingMessage) => Promise<void>): void {\n this.handler = handler\n }\n\n async start(): Promise<void> {\n this.polling = true\n\n const entries = Array.from(this.accounts.entries())\n this.log(`${entries.length} Telegram account(s) to start`)\n\n for (let i = 0; i < entries.length; i++) {\n const [accountId, config] = entries[i]\n this.log(`Starting polling for account \"${accountId}\" (${i + 1}/${entries.length})`)\n try {\n // Drop any stale webhook/long-poll session from a previous run.\n // This prevents 409 conflicts when restarting.\n await this.apiCall(config.token, \"deleteWebhook\", { drop_pending_updates: false }).catch(() => {})\n\n const me = await this.apiCall(config.token, \"getMe\")\n const botUserId = me.result?.id\n const botUsername = me.result?.username\n this.log(`Bot @${botUsername} ready (account: ${accountId})`)\n if (botUserId) {\n this.botInfo.set(accountId, { userId: botUserId, username: botUsername || accountId })\n }\n this.pollLoop(accountId, config)\n } catch (e: any) {\n this.log(`Failed to verify bot for account \"${accountId}\": ${e.message}`)\n }\n // Small delay between account starts to avoid Telegram rate limits\n if (i < entries.length - 1) {\n await new Promise((r) => setTimeout(r, 300))\n }\n }\n\n this.log(`All ${entries.length} Telegram account(s) started`)\n }\n\n async stop(): Promise<void> {\n this.polling = false\n // Flush any debounced offset writes BEFORE we release the Telegram session\n // — otherwise a clean stop could drop the last in-memory offset update.\n this.offsetStore.flush()\n // Abort in-flight long-poll requests by making a short getUpdates call\n // with offset=-1 on each account. This immediately releases Telegram's\n // server-side session so the next start won't get 409 conflicts.\n const aborts = Array.from(this.accounts.entries()).map(async ([accountId, config]) => {\n try {\n await this.apiCall(config.token, \"getUpdates\", { offset: -1, timeout: 0 })\n } catch {\n // Best-effort\n }\n })\n await Promise.allSettled(aborts)\n }\n\n /**\n * Get token for a specific account ID. Used by router to send from correct bot.\n */\n getTokenForAccount(accountId: string): string | undefined {\n return this.accounts.get(accountId)?.token\n }\n\n /**\n * Get the default (first) account token as fallback.\n */\n private getDefaultToken(): string | undefined {\n const [, config] = Array.from(this.accounts.entries())[0]\n return config?.token\n }\n\n /**\n * Resolve which token to use: prefer accountId, fall back to chatAccountMap, then default.\n */\n private resolveToken(chatId: string, accountId?: string): string | undefined {\n if (accountId) {\n const token = this.getTokenForAccount(accountId)\n if (token) return token\n }\n return this.chatAccountMap.get(chatId)\n ? this.getTokenForAccount(this.chatAccountMap.get(chatId)!)\n : this.getDefaultToken()\n }\n\n /** Track which account a chat was last seen on (for DMs) */\n private chatAccountMap: Map<string, string> = new Map()\n // Bot user IDs and usernames resolved at startup (accountId → { userId, username })\n private botInfo: Map<string, { userId: number; username: string }> = new Map()\n // Persistent group membership store\n private groupStore: TelegramGroupStore\n\n /**\n * Send a message. Returns the sent message ID.\n * Pass accountId to send from a specific bot account.\n */\n async send(msg: OutgoingMessage & { accountId?: string }): Promise<string> {\n const token = this.resolveToken(msg.chatId, msg.accountId)\n if (!token) {\n this.log(\"No telegram token found for sending\")\n return \"\"\n }\n\n const maxLen = 4096\n const text = msg.text.length > maxLen ? msg.text.slice(0, maxLen - 3) + \"...\" : msg.text\n\n // Convert markdown to Telegram MarkdownV2\n const formatted = msg.parseMode === \"markdown\" || msg.parseMode === undefined\n ? markdownToTelegramHtml(text)\n : text\n\n const params: Record<string, unknown> = {\n chat_id: msg.chatId,\n text: formatted,\n parse_mode: \"HTML\",\n }\n\n if (msg.replyTo) {\n params.reply_to_message_id = parseInt(msg.replyTo, 10)\n }\n\n if (msg.parseMode === \"html\") {\n params.parse_mode = \"HTML\"\n params.text = text\n } else if (msg.parseMode === \"plain\") {\n delete params.parse_mode\n params.text = text\n }\n\n try {\n const result = await this.apiCall(token, \"sendMessage\", params)\n return String(result.result?.message_id || \"\")\n } catch (e: any) {\n // Retry without formatting if MarkdownV2 fails\n if (params.parse_mode) {\n delete params.parse_mode\n params.text = text\n const result = await this.apiCall(token, \"sendMessage\", params)\n return String(result.result?.message_id || \"\")\n }\n throw e\n }\n }\n\n /**\n * Edit an existing message (for streaming updates).\n */\n async editMessage(chatId: string, messageId: string, text: string, parseMode?: string, accountId?: string): Promise<boolean> {\n const token = this.resolveToken(chatId, accountId)\n if (!token) return false\n\n const maxLen = 4096\n const trimmed = text.length > maxLen ? text.slice(0, maxLen - 3) + \"...\" : text\n\n const formatted = parseMode !== \"html\" && parseMode !== \"plain\"\n ? markdownToTelegramHtml(trimmed)\n : trimmed\n\n const params: Record<string, unknown> = {\n chat_id: chatId,\n message_id: parseInt(messageId, 10),\n text: formatted,\n parse_mode: \"HTML\",\n }\n\n if (parseMode === \"html\") {\n params.parse_mode = \"HTML\"\n params.text = trimmed\n } else if (parseMode === \"plain\") {\n delete params.parse_mode\n params.text = trimmed\n }\n\n try {\n await this.apiCall(token, \"editMessageText\", params)\n return true\n } catch (e: any) {\n if (e.message?.includes(\"message is not modified\")) return true\n if (params.parse_mode) {\n delete params.parse_mode\n params.text = trimmed\n try {\n await this.apiCall(token, \"editMessageText\", params)\n return true\n } catch { return false }\n }\n return false\n }\n }\n\n /**\n * React to a message with an emoji.\n */\n async react(chatId: string, messageId: string, emoji: string = \"👀\", accountId?: string): Promise<void> {\n const token = this.resolveToken(chatId, accountId)\n if (!token) return\n\n try {\n await this.apiCall(token, \"setMessageReaction\", {\n chat_id: chatId,\n message_id: parseInt(messageId, 10),\n reaction: [{ type: \"emoji\", emoji }],\n })\n } catch {\n // Best-effort\n }\n }\n\n /**\n * Send typing indicator.\n */\n async sendTyping(chatId: string, accountId?: string): Promise<void> {\n const token = this.resolveToken(chatId, accountId)\n if (!token) return\n\n try {\n await this.apiCall(token, \"sendChatAction\", {\n chat_id: chatId,\n action: \"typing\",\n })\n } catch {\n // Best-effort\n }\n }\n\n // --- Internal ---\n\n private async pollLoop(accountId: string, config: TelegramAccountConfig): Promise<void> {\n let consecutiveErrors = 0\n\n while (this.polling) {\n try {\n const offset = this.offsetStore.get(accountId) || 0\n const data = await this.apiCall(config.token, \"getUpdates\", {\n offset: offset || undefined,\n timeout: 30,\n allowed_updates: [\"message\", \"my_chat_member\"],\n })\n\n const updates: TelegramUpdate[] = data.result || []\n\n for (const update of updates) {\n this.offsetStore.set(accountId, update.update_id + 1)\n\n if (update.message && this.handler) {\n const msg = update.message\n\n // Extract text from any message type\n let text = msg.text || msg.caption || \"\"\n let mediaInfo: IncomingMessage[\"media\"] | undefined\n\n // Handle media messages\n const hasPhoto = msg.photo && msg.photo.length > 0\n const hasVoice = !!msg.voice\n const hasAudio = !!msg.audio\n const hasVideo = !!msg.video\n const hasDocument = !!msg.document\n const hasMedia = hasPhoto || hasVoice || hasAudio || hasVideo || hasDocument\n\n if (hasMedia) {\n // Download media file\n let fileId: string | undefined\n let mime = \"application/octet-stream\"\n\n if (hasPhoto) {\n fileId = msg.photo![msg.photo!.length - 1].file_id // largest photo\n mime = \"image/jpeg\"\n if (!text) text = \"[Photo attached — please describe what you see]\"\n } else if (hasVoice) {\n fileId = msg.voice!.file_id\n mime = msg.voice!.mime_type || \"audio/ogg\"\n if (!text) text = \"[Voice message — please transcribe and respond]\"\n } else if (hasAudio) {\n fileId = msg.audio!.file_id\n mime = msg.audio!.mime_type || \"audio/mpeg\"\n if (!text) text = `[Audio: ${msg.audio!.title || \"audio file\"}]`\n } else if (hasVideo) {\n fileId = msg.video!.file_id\n mime = msg.video!.mime_type || \"video/mp4\"\n if (!text) text = \"[Video attached]\"\n } else if (hasDocument) {\n fileId = msg.document!.file_id\n mime = msg.document!.mime_type || \"application/octet-stream\"\n if (!text) text = `[Document: ${msg.document!.file_name || \"file\"}]`\n }\n\n if (fileId) {\n try {\n // Get file path from Telegram\n const fileInfo = await this.apiCall(config.token, \"getFile\", { file_id: fileId })\n const filePath = fileInfo.result?.file_path\n if (filePath) {\n // Download file\n const fileUrl = `https://api.telegram.org/file/bot${config.token}/${filePath}`\n const res = await fetch(fileUrl)\n if (res.ok) {\n const buffer = Buffer.from(await res.arrayBuffer())\n const ext = mime.split(\"/\")[1]?.split(\";\")[0] || \"bin\"\n const { mkdirSync, writeFileSync } = await import(\"fs\")\n const { randomUUID } = await import(\"crypto\")\n const { resolve, join } = await import(\"path\")\n const mediaDir = resolve(process.cwd(), \".agentx/media/telegram\")\n mkdirSync(mediaDir, { recursive: true })\n const fileName = msg.document?.file_name || `${randomUUID()}.${ext}`\n const localPath = join(mediaDir, fileName)\n writeFileSync(localPath, buffer)\n mediaInfo = { path: localPath, type: mime, fileName }\n }\n }\n } catch (e: any) {\n this.log(`Media download failed: ${e.message}`)\n }\n }\n }\n\n if (!text) continue\n\n // Sender allowlist gate — enforced BEFORE the router sees anything\n // so an unauthorized message burns zero tokens and leaves no trace\n // in the agent's session log.\n if (!this.isAllowed(accountId, msg.from?.id, msg.chat?.id, msg.from?.username)) {\n this.log(\n `[telegram/${accountId}] dropped message from ${msg.from?.id}${msg.from?.username ? ` (@${msg.from.username})` : \"\"} in chat ${msg.chat?.id} — not in allowlist`,\n )\n continue\n }\n\n // Build channel meta for groups (verified bot membership)\n const isGroup = msg.chat.type !== \"private\"\n const groupId = String(msg.chat.id)\n let channelMeta: ChannelMeta | undefined\n if (isGroup) {\n // Seed group membership on first encounter via API\n if (!this.groupStore.hasGroup(groupId)) {\n await this.seedGroupMembership(groupId, msg.chat.title)\n }\n channelMeta = await this.getChannelMeta(groupId)\n }\n\n const incoming: IncomingMessage = {\n id: String(msg.message_id),\n channel: \"telegram\",\n accountId,\n sender: {\n id: String(msg.from.id),\n name: [msg.from.first_name, msg.from.last_name].filter(Boolean).join(\" \"),\n username: msg.from.username,\n isBot: msg.from.is_bot === true,\n },\n group: isGroup\n ? { id: groupId, name: msg.chat.title || \"\" }\n : undefined,\n text,\n media: mediaInfo,\n replyTo: msg.reply_to_message\n ? String(msg.reply_to_message.message_id)\n : undefined,\n replyToText: msg.reply_to_message\n ? (msg.reply_to_message.text || msg.reply_to_message.caption || `[message from ${msg.reply_to_message.from?.first_name || \"unknown\"}]`)\n : undefined,\n timestamp: new Date(msg.date * 1000),\n raw: update,\n channelMeta,\n }\n\n // Track chat→account mapping for DM replies\n this.chatAccountMap.set(String(msg.chat.id), accountId)\n\n\n this.handler(incoming).catch((e) => {\n this.log(`Error handling message: ${e.message}`)\n })\n }\n\n // Handle bot membership changes (added/removed from group)\n if (update.my_chat_member) {\n const mcm = update.my_chat_member\n const chatId = String(mcm.chat.id)\n const chatTitle = mcm.chat.title\n const status = mcm.new_chat_member.status\n const botUsername = mcm.new_chat_member.user.username || accountId\n\n this.groupStore.setBotStatus(\n chatId,\n chatTitle,\n accountId,\n config.agentBinding,\n botUsername,\n status,\n )\n\n this.log(`Group membership: @${botUsername} is now \"${status}\" in \"${chatTitle || chatId}\"`)\n }\n }\n consecutiveErrors = 0\n } catch (e: any) {\n consecutiveErrors++\n const backoff = Math.min(5000 * Math.pow(2, consecutiveErrors - 1), 60000)\n this.log(`Poll error (${accountId}): ${e.message} [retry in ${backoff / 1000}s, errors: ${consecutiveErrors}]`)\n await new Promise((r) => setTimeout(r, backoff))\n }\n }\n }\n\n /**\n * Get verified context for a Telegram chat.\n * Reads from persistent group store (fed by my_chat_member events + API seed).\n */\n async getChannelMeta(chatId: string): Promise<ChannelMeta | undefined> {\n const bots = this.groupStore.getGroupBots(chatId)\n if (!bots.length) return undefined\n\n return {\n channel: \"telegram\",\n agents: bots.map(b => ({\n id: b.agentId,\n name: b.username,\n handle: `@${b.username}`,\n })),\n facts: [`${bots.length} bot(s) verified in this group`],\n }\n }\n\n /**\n * Seed group membership by querying the Telegram API for each bot.\n * Called once per group on first encounter, then maintained via my_chat_member events.\n */\n private async seedGroupMembership(groupId: string, groupTitle?: string): Promise<void> {\n this.log(`Seeding group membership for \"${groupTitle || groupId}\"`)\n\n for (const [accountId, config] of this.accounts) {\n const info = this.botInfo.get(accountId)\n if (!info) continue\n\n try {\n const res = await this.apiCall(config.token, \"getChatMember\", {\n chat_id: Number(groupId),\n user_id: info.userId,\n })\n const status = res.result?.status\n if (status) {\n this.groupStore.setBotStatus(\n groupId,\n groupTitle,\n accountId,\n config.agentBinding,\n info.username,\n status,\n )\n }\n } catch {\n // Bot not in group or API error — record as \"left\"\n this.groupStore.setBotStatus(\n groupId,\n groupTitle,\n accountId,\n config.agentBinding,\n info.username,\n \"left\",\n )\n }\n }\n\n this.log(`Seeded: ${this.groupStore.getGroupBots(groupId).length} bot(s) in \"${groupTitle || groupId}\"`)\n }\n\n private async apiCall(\n token: string,\n method: string,\n params?: Record<string, unknown>,\n ): Promise<any> {\n const url = `https://api.telegram.org/bot${token}/${method}`\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: params ? JSON.stringify(params) : undefined,\n })\n\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`Telegram API error: ${res.status} ${text}`)\n }\n\n return res.json()\n }\n}\n","import type { ChannelAdapter, IncomingMessage, OutgoingMessage } from \"./types\"\nimport { existsSync, mkdirSync, writeFileSync } from \"fs\"\nimport { resolve, join } from \"path\"\nimport { randomUUID } from \"crypto\"\n\n// --- WhatsApp adapter using Baileys (WhatsApp Web multi-device) ---\n// Baileys is an optional dependency. If not installed, adapter logs a warning.\n//\n// First run: displays QR code in terminal for pairing.\n// Subsequent runs: reconnects automatically using saved session.\n\nexport interface WhatsAppRoute {\n contact?: string // phone number or partial match\n group?: string // group name or JID partial match\n agent: string // agent ID to route to\n}\n\nexport class WhatsAppAdapter implements ChannelAdapter {\n readonly name = \"whatsapp\"\n private sessionDir: string\n private defaultAgent?: string\n private allowFrom?: string[]\n private routes: WhatsAppRoute[]\n private handler?: (msg: IncomingMessage) => Promise<void>\n private sock: any = null\n private reconnectAttempts = 0\n /** Generation counter — incremented on each start() to ignore stale socket events */\n private generation = 0\n private sentMessageIds: Set<string> = new Set() // Track our own replies to prevent loops\n private log: (...args: unknown[]) => void\n\n /** Fires whenever the underlying WhatsApp socket emits a new QR or clears it.\n * Passing null means the session is now connected. */\n private onQR?: (qr: string | null) => void\n /** Fires whenever the connection state changes (connecting / open / close). */\n private onStatus?: (status: \"connecting\" | \"open\" | \"close\", detail?: string) => void\n\n constructor(\n config: {\n sessionDir: string\n defaultAgent?: string\n allowFrom?: string[]\n routes?: WhatsAppRoute[]\n onQR?: (qr: string | null) => void\n onStatus?: (status: \"connecting\" | \"open\" | \"close\", detail?: string) => void\n },\n log: (...args: unknown[]) => void = console.error.bind(console, \"[whatsapp]\"),\n ) {\n this.sessionDir = resolve(config.sessionDir)\n this.defaultAgent = config.defaultAgent\n this.allowFrom = config.allowFrom\n this.routes = config.routes || []\n this.onQR = config.onQR\n this.onStatus = config.onStatus\n this.log = log\n }\n\n /**\n * Resolve which agent handles a message based on routes.\n */\n resolveAgent(senderPhone: string, groupName?: string, groupJid?: string): string | undefined {\n for (const route of this.routes) {\n // Match by contact phone number\n if (route.contact) {\n const normalized = route.contact.replace(/\\+/g, \"\")\n if (senderPhone.includes(normalized) || normalized.includes(senderPhone)) {\n return route.agent\n }\n }\n // Match by group name or JID\n if (route.group && (groupName || groupJid)) {\n const pattern = route.group.toLowerCase()\n if (\n groupName?.toLowerCase().includes(pattern) ||\n groupJid?.toLowerCase().includes(pattern)\n ) {\n return route.agent\n }\n }\n }\n return this.defaultAgent\n }\n\n onMessage(handler: (msg: IncomingMessage) => Promise<void>): void {\n this.handler = handler\n }\n\n async start(): Promise<void> {\n // Dynamic import — Baileys is optional\n let makeWASocket: any\n let useMultiFileAuthState: any\n let DisconnectReason: any\n\n let baileys: any\n try {\n baileys = await import(\"@whiskeysockets/baileys\")\n makeWASocket = baileys.default || baileys.makeWASocket\n useMultiFileAuthState = baileys.useMultiFileAuthState\n DisconnectReason = baileys.DisconnectReason\n } catch {\n this.log(\"WhatsApp requires @whiskeysockets/baileys. Install with:\")\n this.log(\" npm install @whiskeysockets/baileys\")\n return\n }\n\n mkdirSync(this.sessionDir, { recursive: true })\n\n // Increment generation — stale socket events from previous start() calls\n // will see a mismatched generation and be ignored\n const myGeneration = ++this.generation\n\n // Close existing socket before creating a new one\n if (this.sock) {\n try { this.sock.end(undefined) } catch {}\n this.sock = null\n }\n\n const { state, saveCreds } = await useMultiFileAuthState(this.sessionDir)\n\n // Fetch latest WhatsApp Web version (critical for avoiding 405 errors)\n let version: [number, number, number] | undefined\n try {\n const { version: v } = await baileys.fetchLatestBaileysVersion()\n version = v\n this.log(`WhatsApp Web version: ${v.join(\".\")}`)\n } catch {\n this.log(\"Could not fetch WA version, using default\")\n }\n\n // Silent logger with proper method stubs (Baileys v7 requires all methods)\n const silentLogger = {\n level: \"silent\",\n trace: () => {}, debug: () => {}, info: () => {},\n warn: () => {}, fatal: () => {},\n error: (...args: any[]) => this.log(\"WA error:\", ...args),\n child: () => silentLogger,\n } as any\n\n this.sock = makeWASocket({\n auth: {\n creds: state.creds,\n keys: baileys.makeCacheableSignalKeyStore\n ? baileys.makeCacheableSignalKeyStore(state.keys, silentLogger)\n : state.keys,\n },\n ...(version ? { version } : {}),\n logger: silentLogger,\n printQRInTerminal: false,\n browser: [\"agentx\", \"server\", \"1.0\"],\n syncFullHistory: false,\n markOnlineOnConnect: false,\n })\n\n // Save credentials on update\n this.sock.ev.on(\"creds.update\", saveCreds)\n\n // Debug: log all events to diagnose message reception\n this.sock.ev.on(\"messaging-history.set\", (data: any) => {\n this.log(`WA history sync: ${data.messages?.length || 0} messages, ${data.isLatest ? \"latest\" : \"partial\"}`)\n })\n\n // Handle connection updates (ignore events from stale sockets via generation check)\n this.sock.ev.on(\"connection.update\", async (update: any) => {\n const { connection, lastDisconnect, qr } = update\n if (myGeneration !== this.generation) return // stale socket event\n\n if (qr) {\n this.log(\"Scan QR code with WhatsApp to connect:\")\n // Publish the raw QR so the admin dashboard can render it in-browser.\n try { this.onQR?.(qr) } catch { /* consumer crashed — don't block the socket */ }\n // Render QR as text in terminal too (keeps SSH-only setups working).\n try {\n // @ts-ignore - no type declarations for qrcode-terminal\n const { default: qrcode } = await import(\"qrcode-terminal\") as any\n qrcode.generate(qr, { small: true })\n } catch {\n // If qrcode-terminal not available, log raw QR string\n this.log(`QR: ${qr}`)\n this.log(\"Install qrcode-terminal for visual QR: npm install qrcode-terminal\")\n }\n }\n if (connection) {\n try { this.onStatus?.(connection, lastDisconnect?.error?.message) } catch { /* */ }\n }\n if (connection === \"open\") {\n // Clear the cached QR once the pairing succeeds so the dashboard stops\n // showing a scannable code the user already consumed.\n try { this.onQR?.(null) } catch { /* */ }\n }\n\n if (connection === \"close\") {\n const statusCode = lastDisconnect?.error?.output?.statusCode\n const reason = lastDisconnect?.error?.output?.payload?.message || \"\"\n this.log(`WhatsApp connection closed (status: ${statusCode}, reason: ${reason})`)\n\n if (statusCode === DisconnectReason?.loggedOut || statusCode === 401) {\n this.log(\"Logged out. Delete session dir and restart to re-scan QR.\")\n } else if (statusCode === 440 || statusCode === 408) {\n // 440 = conflict:replaced (another session took over)\n // 408 = connection timed out (QR not scanned)\n this.reconnectAttempts++\n\n if (this.reconnectAttempts >= 5) {\n this.log(`WhatsApp: giving up after ${this.reconnectAttempts} conflict attempts. Another session is active for this number — close it or re-scan QR.`)\n return // Stop reconnecting\n }\n\n const delay = Math.min(10_000 * Math.pow(2, this.reconnectAttempts - 1), 120_000)\n this.log(`Reconnecting in ${delay / 1000}s (attempt ${this.reconnectAttempts}/5)...`)\n setTimeout(() => this.start(), delay)\n } else if (statusCode === 515) {\n // Stream error — restart after delay\n this.log(\"Stream error, reconnecting in 5s...\")\n setTimeout(() => this.start(), 5000)\n } else if (statusCode !== undefined) {\n this.log(\"Reconnecting in 3s...\")\n setTimeout(() => this.start(), 3000)\n }\n // If statusCode is undefined, don't reconnect (likely startup failure)\n }\n\n if (connection === \"open\") {\n // Don't reset reconnectAttempts here — it gets reset on first\n // successful message send/receive (proving connection is stable)\n this.log(\"WhatsApp connected\")\n }\n })\n\n // Handle incoming messages\n this.sock.ev.on(\"messages.upsert\", async (m: any) => {\n // Connection is confirmed stable — reset reconnect backoff\n if (this.reconnectAttempts > 0) {\n this.log(`WhatsApp connection stable, resetting reconnect counter (was ${this.reconnectAttempts})`)\n this.reconnectAttempts = 0\n }\n\n this.log(`WA messages.upsert: ${m.messages?.length || 0} messages, type: ${m.type}`)\n\n if (!this.handler) return\n\n for (const msg of m.messages || []) {\n const jidShort = (msg.key.remoteJid || \"\").replace(/@.*/, \"\").slice(-6)\n const hasText = !!(msg.message?.conversation || msg.message?.extendedTextMessage?.text)\n this.log(`WA msg: from=${jidShort} fromMe=${msg.key.fromMe} hasText=${hasText} type=${Object.keys(msg.message || {}).join(\",\")}`)\n\n // Skip status broadcasts\n if (msg.key.remoteJid === \"status@broadcast\") continue\n\n // Skip messages we sent as replies (prevents loops)\n if (msg.key.id && this.sentMessageIds.has(msg.key.id)) {\n this.sentMessageIds.delete(msg.key.id)\n continue\n }\n\n // For fromMe: only process self-chat (messaging yourself = talking to agent)\n // OpenClaw pattern: if your own number is in allowFrom, self-chat is enabled.\n // WhatsApp uses LID format for self-chat remoteJid.\n if (msg.key.fromMe) {\n const me = this.sock?.user\n const remoteJid = msg.key.remoteJid || \"\"\n // Self-chat: remoteJid matches our JID or LID\n const myJid = me?.id?.replace(/:.*/, \"\") || \"\" // 21624309128\n const myLid = me?.lid?.replace(/:.*/, \"\") || \"\" // 214997540012179\n const chatUser = remoteJid.replace(/:.*/, \"\").replace(/@.*/, \"\")\n const isSelfChat = (chatUser === myJid) || (chatUser === myLid)\n if (!isSelfChat) continue\n }\n\n // Extract text content + detect media\n let text = msg.message?.conversation\n || msg.message?.extendedTextMessage?.text\n || msg.message?.imageMessage?.caption\n || msg.message?.videoMessage?.caption\n || \"\"\n\n // Detect media type\n const msgContent = msg.message || {}\n const hasImage = !!msgContent.imageMessage\n const hasAudio = !!msgContent.audioMessage\n const hasVideo = !!msgContent.videoMessage\n const hasDocument = !!msgContent.documentMessage\n const hasSticker = !!msgContent.stickerMessage\n const hasMedia = hasImage || hasAudio || hasVideo || hasDocument || hasSticker\n\n // Add media placeholder to text if no caption\n if (hasMedia && !text) {\n if (hasImage) text = \"[Image attached — please describe what you see]\"\n else if (hasAudio) text = \"[Voice message attached — please transcribe and respond]\"\n else if (hasVideo) text = \"[Video attached]\"\n else if (hasDocument) text = `[Document: ${msgContent.documentMessage?.fileName || \"file\"}]`\n else if (hasSticker) text = \"[Sticker]\"\n }\n\n if (!text) continue\n\n const jid = msg.key.remoteJid || \"\"\n const isGroup = jid.endsWith(\"@g.us\")\n\n // For DMs: remoteJid is the conversation partner\n // For groups: participant is the sender\n const chatPhone = jid.replace(/@.*$/, \"\")\n const senderJid = isGroup ? (msg.key.participant || \"\") : jid\n const senderPhone = senderJid.replace(/@.*$/, \"\")\n\n // Allowlist check — match against chat (conversation partner) not just sender\n // This allows the owner (fromMe) to talk to their agent via any contact\n if (this.allowFrom?.length && !msg.key.fromMe) {\n const allowed = this.allowFrom.some(p => {\n const normalized = p.replace(/\\+/g, \"\")\n return senderPhone.includes(normalized) || chatPhone.includes(normalized)\n })\n if (!allowed) continue\n }\n\n const senderName = msg.key.fromMe ? \"me\" : (msg.pushName || senderPhone)\n\n // Get group name if available\n let groupName: string | undefined\n if (isGroup && this.sock) {\n try {\n const metadata = await this.sock.groupMetadata(jid)\n groupName = metadata.subject\n } catch { /* group metadata unavailable */ }\n }\n\n // Route to agent based on contact/group rules\n const resolvedAgent = this.resolveAgent(senderPhone, groupName, isGroup ? jid : undefined)\n if (!resolvedAgent) {\n this.log(`No route for ${isGroup ? `group ${groupName || jid}` : senderPhone}, skipping`)\n continue\n }\n\n // Download and save media if present\n let mediaInfo: IncomingMessage[\"media\"] | undefined\n if (hasMedia && this.sock) {\n try {\n const baileys = await import(\"@whiskeysockets/baileys\")\n const buffer = await baileys.downloadMediaMessage(msg, \"buffer\", {}, {\n reuploadRequest: this.sock.updateMediaMessage,\n logger: this.sock.logger,\n })\n if (buffer) {\n const mime = msgContent.imageMessage?.mimetype\n || msgContent.audioMessage?.mimetype || \"audio/ogg\"\n || msgContent.videoMessage?.mimetype || \"video/mp4\"\n || msgContent.documentMessage?.mimetype\n || msgContent.stickerMessage?.mimetype || \"image/webp\"\n || \"application/octet-stream\"\n const ext = mime.split(\"/\")[1]?.split(\";\")[0] || \"bin\"\n const mediaDir = resolve(this.sessionDir, \"../media/inbound\")\n mkdirSync(mediaDir, { recursive: true })\n const fileName = msgContent.documentMessage?.fileName || `${randomUUID()}.${ext}`\n const filePath = join(mediaDir, fileName)\n writeFileSync(filePath, buffer)\n mediaInfo = { path: filePath, type: mime, fileName }\n this.log(`WA media saved: ${mime} -> ${filePath}`)\n }\n } catch (e: any) {\n this.log(`WA media download failed: ${e.message}`)\n }\n }\n\n const incoming: IncomingMessage = {\n id: msg.key.id || String(Date.now()),\n channel: \"whatsapp\",\n accountId: \"default\",\n sender: {\n id: msg.key.fromMe\n ? (this.sock?.user?.id?.replace(/:.*/, \"\") || senderPhone) + \"@s.whatsapp.net\"\n : senderPhone,\n name: senderName,\n username: senderPhone,\n },\n group: isGroup ? {\n id: jid,\n name: groupName || jid,\n } : undefined,\n text,\n media: mediaInfo,\n replyTo: msg.message?.extendedTextMessage?.contextInfo?.stanzaId,\n timestamp: new Date((msg.messageTimestamp || 0) * 1000),\n raw: msg,\n resolvedAgent,\n }\n\n this.handler(incoming).catch((e: any) => {\n this.log(`Error handling message: ${e.message}`)\n })\n }\n })\n }\n\n async stop(): Promise<void> {\n if (this.sock) {\n this.sock.end()\n this.sock = null\n }\n }\n\n async send(msg: OutgoingMessage): Promise<string> {\n if (!this.sock) {\n this.log(\"WhatsApp not connected\")\n return \"\"\n }\n\n const jid = msg.chatId.includes(\"@\")\n ? msg.chatId\n : `${msg.chatId}@s.whatsapp.net`\n\n try {\n const sent = await this.sock.sendMessage(jid, { text: msg.text })\n const sentId = sent?.key?.id || \"\"\n // Track to prevent loop (our own reply echoed back as fromMe)\n if (sentId) this.sentMessageIds.add(sentId)\n return sentId\n } catch (e: any) {\n this.log(`Send error: ${e.message}`)\n return \"\"\n }\n }\n\n async editMessage(chatId: string, messageId: string, text: string): Promise<boolean> {\n if (!this.sock) return false\n const jid = chatId.includes(\"@\") ? chatId : `${chatId}@s.whatsapp.net`\n try {\n await this.sock.sendMessage(jid, {\n text,\n edit: { remoteJid: jid, id: messageId, fromMe: true },\n })\n return true\n } catch { return false }\n }\n\n async sendTyping(chatId: string): Promise<void> {\n if (!this.sock) return\n const jid = chatId.includes(\"@\") ? chatId : `${chatId}@s.whatsapp.net`\n try {\n await this.sock.sendPresenceUpdate(\"composing\", jid)\n } catch { /* best-effort */ }\n }\n\n async react(chatId: string, messageId: string, emoji: string = \"👀\"): Promise<void> {\n if (!this.sock) return\n const jid = chatId.includes(\"@\") ? chatId : `${chatId}@s.whatsapp.net`\n try {\n await this.sock.sendMessage(jid, {\n react: { text: emoji, key: { remoteJid: jid, id: messageId } },\n })\n } catch { /* best-effort */ }\n }\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from \"http\"\nimport { writeFileSync, existsSync, unlinkSync, mkdirSync, watch, type FSWatcher } from \"fs\"\nimport { resolve, dirname } from \"path\"\nimport { loadDaemonConfig, validateWorkspaces, type DaemonConfig } from \"./config\"\nimport { AgentRegistry, setGlobalRegistry } from \"@/agents/registry\"\nimport { MessageRouter } from \"@/channels/router\"\nimport { TelegramAdapter } from \"@/channels/telegram\"\nimport { WhatsAppAdapter } from \"@/channels/whatsapp\"\nimport { DiscordAdapter } from \"@/channels/discord\"\nimport { SlackAdapter } from \"@/channels/slack\"\nimport { GitLabAdapter } from \"@/channels/gitlab\"\nimport { CronScheduler } from \"@/crons/scheduler\"\nimport { Logger } from \"./logger\"\nimport { WebhookHandler } from \"./webhooks\"\nimport { A2AMesh } from \"@/a2a/mesh\"\nimport { HookRegistry, loadHooks } from \"@/hooks\"\nimport { LandscapeBuilder } from \"@/agents/landscape\"\nimport { HeartbeatManager } from \"@/agents/heartbeat\"\nimport { setupAllWorkspaces } from \"@/agents/workspace-setup\"\nimport { ServiceMatcher } from \"@/services/matcher\"\nimport { BusinessLayer } from \"@/business\"\n\n// --- AgentX Daemon: the thin orchestration layer ---\n//\n// This is NOT an AI runtime. It's a message router + scheduler + mesh\n// that triggers Claude Code sessions in workspace directories.\n//\n// Each agent = a workspace with .claude/ config\n// agentx just orchestrates WHEN and WHERE Claude Code runs.\n\nexport class AgentXDaemon {\n private config: DaemonConfig\n private registry: AgentRegistry\n private router: MessageRouter\n private cron: CronScheduler\n private mesh?: A2AMesh\n private hooks: HookRegistry\n private landscape: LandscapeBuilder\n private heartbeat: HeartbeatManager\n private business?: BusinessLayer\n private httpServer?: ReturnType<typeof createServer>\n private webhooks: WebhookHandler\n private log: (...args: unknown[]) => void\n private sseClients: Set<ServerResponse> = new Set()\n private configPath?: string\n private configWatcher?: FSWatcher\n private reloadTimer?: ReturnType<typeof setTimeout>\n\n constructor(configPath?: string) {\n const logger = new Logger(\"agentx\")\n const baseLog = logger.asConsoleLog()\n\n // Wrap log to also broadcast to SSE clients\n this.log = (...args: unknown[]) => {\n baseLog(...args)\n const line = args.map(a => typeof a === \"string\" ? a : JSON.stringify(a)).join(\" \")\n this.broadcastSSE(\"log\", line)\n }\n\n // Load config\n this.log(\"Loading configuration...\")\n this.configPath = configPath\n this.config = loadDaemonConfig(configPath)\n\n // Validate\n const warnings = validateWorkspaces(this.config)\n for (const w of warnings) {\n this.log(` ⚠ ${w}`)\n }\n\n // Set up agent workspaces with Claude Code best practices (non-destructive)\n const [, portStr] = this.config.node.bind.split(\":\")\n setupAllWorkspaces(this.config.agents, portStr || \"19900\", this.log)\n\n // Initialize hooks\n this.hooks = new HookRegistry()\n loadHooks(process.cwd(), this.hooks)\n\n // Initialize landscape builder\n this.landscape = new LandscapeBuilder(this.config)\n\n // Initialize agent registry\n this.registry = new AgentRegistry(this.config, this.log)\n setGlobalRegistry(this.registry)\n this.registry.setLandscape(this.landscape)\n\n // Initialize heartbeat manager\n this.heartbeat = new HeartbeatManager(this.registry, this.log)\n for (const [id, agent] of Object.entries(this.config.agents)) {\n if (agent.heartbeat?.enabled) {\n this.heartbeat.register(id, agent.heartbeat)\n }\n }\n\n // Initialize message router\n this.router = new MessageRouter(this.registry, this.config, this.hooks, this.log)\n this.webhooks = new WebhookHandler(this.registry, {}, this.log)\n\n // Initialize service matcher (automated client services)\n if (Object.keys(this.config.services).length > 0) {\n const serviceMatcher = new ServiceMatcher(this.config.services, this.log)\n this.router.setServiceMatcher(serviceMatcher)\n }\n\n // Initialize cron scheduler with failure notifications\n this.cron = new CronScheduler(this.config, this.registry, this.hooks, this.log)\n this.cron.setNotifyCallback(async (jobId, agent, error, consecutiveErrors) => {\n const msg = `Cron \"${jobId}\" failed (${consecutiveErrors}x)\\nAgent: ${agent}\\nError: ${error.slice(0, 300)}`\n this.log(`[CRON ALERT] ${msg}`)\n this.broadcastSSE(\"cron-failure\", JSON.stringify({ jobId, agent, error, consecutiveErrors }))\n\n // Send to the cron job's configured notify destination (if set)\n const cronDef = this.config.crons[jobId]\n if (cronDef?.notify) {\n try {\n await this.router.sendOutbound({\n channel: cronDef.notify.channel,\n chatId: cronDef.notify.chatId,\n text: `🔴 **Cron \"${jobId}\" failed** (${consecutiveErrors}x)\\n${error.slice(0, 300)}`,\n agentId: agent,\n accountId: cronDef.notify.accountId,\n })\n } catch (e: any) {\n this.log(`[CRON ALERT] notify send failed: ${e.message}`)\n }\n }\n })\n\n // Initialize mesh (if enabled)\n if (this.config.mesh.enabled) {\n this.mesh = new A2AMesh(this.config, this.log)\n this.router.setMesh(this.mesh)\n }\n\n // Initialize business layer (if enabled)\n if (this.config.business?.enabled) {\n try {\n this.business = new BusinessLayer(\n this.config.business,\n this.config,\n this.registry,\n this.router,\n this.log,\n )\n this.router.setBusiness(this.business)\n } catch (e: any) {\n this.log(`[business] initialization failed: ${e.message}`)\n }\n }\n }\n\n async start(): Promise<void> {\n this.log(\"\")\n this.log(\" ┌─────────────────────────────────────┐\")\n this.log(\" │ agentx daemon │\")\n this.log(\" └─────────────────────────────────────┘\")\n this.log(\"\")\n this.log(` Node: ${this.config.node.name} (${this.config.node.id})`)\n this.log(` Bind: ${this.config.node.bind}`)\n this.log(\"\")\n\n // 1. Start channels\n await this.startChannels()\n\n // 2. Start cron scheduler\n await this.cron.start()\n\n // 3. Start mesh\n if (this.mesh) {\n await this.mesh.start()\n }\n\n // 4. Build agent landscape (after mesh so remote peers are discovered)\n const meshPeers = this.mesh?.directory().map(p => ({\n peer: p.peer,\n healthy: p.healthy,\n skills: p.skills,\n })) || []\n this.landscape.build(meshPeers)\n this.log(` Landscape: built for ${Object.keys(this.config.agents).length} agents`)\n\n // 5. Schedule midnight cost tracking hook\n this.scheduleMidnightHook()\n\n // 5b. Start business layer day cycle (if configured)\n if (this.business) {\n this.business.start()\n this.log(` ${this.business.summary()}`)\n }\n\n // 6. Start HTTP API\n await this.startHttpApi()\n\n // Print agent summary\n this.log(\"\")\n this.log(\" Agents:\")\n for (const agent of this.registry.list()) {\n this.log(` ${agent.id} (${agent.tier}) → ${agent.workspace}`)\n }\n\n // Print cron summary\n const cronJobs = this.cron.list()\n if (cronJobs.length) {\n this.log(\"\")\n this.log(\" Cron Jobs:\")\n for (const job of cronJobs) {\n const status = job.enabled ? \"enabled\" : \"disabled\"\n this.log(` ${job.id} [${status}] → ${job.agent} (${job.schedule})`)\n }\n }\n\n // Print mesh summary\n if (this.mesh) {\n this.log(\"\")\n this.log(\" Mesh Peers:\")\n for (const peer of this.mesh.directory()) {\n const status = peer.healthy ? \"✓\" : \"✗\"\n this.log(` ${status} ${peer.peer} (${peer.peerUrl})`)\n }\n }\n\n // Start config file watcher (opt-out via AGENTX_AUTO_RELOAD=false)\n if (process.env.AGENTX_AUTO_RELOAD !== \"false\") {\n this.startConfigWatcher()\n }\n\n // Drop old task-history folders past the retention window.\n try {\n const removed = this.registry.pruneTaskHistory()\n if (removed > 0) this.log(` Pruned ${removed} old task-history folder(s)`)\n } catch { /* best-effort */ }\n\n // Warm the per-agent last-summary cache from disk so dashboard cards have\n // something to show the instant the daemon boots.\n try {\n const loaded = this.registry.hydrateLastSummariesFromDisk()\n if (loaded > 0) this.log(` Loaded last-activity summaries for ${loaded} agent(s)`)\n } catch { /* best-effort */ }\n\n this.log(\"\")\n this.log(\" Ready.\")\n this.log(\"\")\n\n // Write PID file\n const pidFile = resolve(process.cwd(), \".agentx/daemon.pid\")\n mkdirSync(dirname(pidFile), { recursive: true })\n writeFileSync(pidFile, String(process.pid))\n this.log(` PID: ${process.pid} (${pidFile})`)\n\n // Catch unhandled errors — log but don't crash\n process.on(\"uncaughtException\", (err) => {\n this.log(`UNCAUGHT EXCEPTION: ${err.message}`)\n this.log(err.stack || \"\")\n })\n process.on(\"unhandledRejection\", (reason) => {\n this.log(`UNHANDLED REJECTION: ${reason}`)\n })\n\n // Graceful shutdown\n let stopping = false\n const shutdown = async (signal: string) => {\n if (stopping) return\n stopping = true\n this.log(`\\n Received ${signal}, shutting down gracefully...`)\n await this.stop()\n }\n process.on(\"SIGINT\", () => shutdown(\"SIGINT\"))\n process.on(\"SIGTERM\", () => shutdown(\"SIGTERM\"))\n }\n\n async stop(): Promise<void> {\n const start = Date.now()\n\n try {\n this.log(\" Stopping channels...\")\n await Promise.race([this.router.stopAll(), new Promise(r => setTimeout(r, 5000))])\n } catch (e: any) {\n this.log(` Channel stop error: ${e.message}`)\n }\n\n try {\n // Persist router dedup + any debounced per-channel state before exit so\n // a clean stop doesn't drop the last in-memory window of processed ids.\n this.router.flushPersistence?.()\n } catch (e: any) {\n this.log(` Router flush error: ${e.message}`)\n }\n\n try {\n this.log(\" Stopping crons (saving last run times)...\")\n await this.cron.stop()\n } catch {}\n\n try {\n this.log(\" Stopping heartbeats...\")\n this.heartbeat.stopAll()\n } catch {}\n\n try {\n if (this.business) {\n this.log(\" Stopping business layer...\")\n this.business.stop()\n }\n } catch {}\n\n try {\n if (this.mesh) {\n this.log(\" Stopping mesh...\")\n await this.mesh.stop()\n }\n } catch {}\n\n if (this.midnightTimer) clearTimeout(this.midnightTimer)\n\n if (this.reloadTimer) clearTimeout(this.reloadTimer)\n if (this.configWatcher) {\n try { this.configWatcher.close() } catch { /* best effort */ }\n }\n\n if (this.httpServer) {\n this.httpServer.close()\n }\n\n // Clean PID file\n try {\n const pidFile = resolve(process.cwd(), \".agentx/daemon.pid\")\n if (existsSync(pidFile)) unlinkSync(pidFile)\n } catch {}\n\n this.log(` Shutdown complete (${Date.now() - start}ms)`)\n process.exit(0)\n }\n\n private midnightTimer?: ReturnType<typeof setTimeout>\n\n /**\n * Watch agentx.json for external edits (e.g. `agentx config set ...`) and\n * reload cron jobs + in-memory config. Channels / agents / mesh changes\n * still require a restart — we log a warning so the operator knows.\n */\n private startConfigWatcher(): void {\n const path = this.configPath || resolve(process.cwd(), \"agentx.json\")\n if (!existsSync(path)) return\n try {\n this.configWatcher = watch(path, { persistent: false }, (eventType) => {\n if (eventType !== \"change\") return\n if (this.reloadTimer) clearTimeout(this.reloadTimer)\n this.reloadTimer = setTimeout(() => {\n this.reload().catch((e) => this.log(`[reload] failed: ${e?.message || e}`))\n }, 500) // debounce\n })\n this.log(` Watching ${path} for config changes`)\n } catch (e: any) {\n this.log(` Config watcher failed to start: ${e.message}`)\n }\n }\n\n /**\n * Re-read agentx.json, diff against the in-memory config, apply what we\n * can hot-reload (crons, notify-destination, business metadata), and warn\n * about sections that require a daemon restart (channels, agents, mesh,\n * node.bind, providers).\n */\n async reload(): Promise<{ applied: string[]; restartRequired: string[]; error?: string }> {\n let next: DaemonConfig\n try {\n next = loadDaemonConfig(this.configPath)\n } catch (e: any) {\n this.log(`[reload] config invalid, keeping previous: ${e.message}`)\n return { applied: [], restartRequired: [], error: e.message }\n }\n\n const applied: string[] = []\n const restartRequired: string[] = []\n\n // 1. Crons — safe to hot-swap (stop + reinit)\n if (JSON.stringify(this.config.crons) !== JSON.stringify(next.crons)) {\n try {\n await this.cron.stop()\n this.cron = new CronScheduler(next, this.registry, this.hooks, this.log)\n this.cron.setNotifyCallback(async (jobId, agent, error, consecutiveErrors) => {\n this.log(`[CRON ALERT] Cron \"${jobId}\" failed (${consecutiveErrors}x) — ${error.slice(0, 200)}`)\n this.broadcastSSE(\"cron-failure\", JSON.stringify({ jobId, agent, error, consecutiveErrors }))\n const cronDef = next.crons[jobId]\n if (cronDef?.notify) {\n try {\n await this.router.sendOutbound({\n channel: cronDef.notify.channel,\n chatId: cronDef.notify.chatId,\n text: `Cron \"${jobId}\" failed (${consecutiveErrors}x)\\n${error.slice(0, 300)}`,\n agentId: agent,\n accountId: cronDef.notify.accountId,\n })\n } catch (e: any) {\n this.log(`[CRON ALERT] notify send failed: ${e.message}`)\n }\n }\n })\n await this.cron.start()\n applied.push(\"crons\")\n } catch (e: any) {\n this.log(`[reload] cron reload failed: ${e.message}`)\n }\n }\n\n // 2. Sections that require a restart\n const restartKeys: Array<keyof DaemonConfig> = [\"agents\", \"channels\", \"mesh\", \"providers\", \"node\", \"services\"]\n for (const k of restartKeys) {\n if (JSON.stringify((this.config as any)[k]) !== JSON.stringify((next as any)[k])) {\n restartRequired.push(String(k))\n }\n }\n\n // 3. Swap in the new config so read-only endpoints (GET /crons etc.) reflect it\n this.config = next\n\n if (applied.length) this.log(`[reload] applied: ${applied.join(\", \")}`)\n if (restartRequired.length) {\n this.log(`[reload] restart required to apply changes in: ${restartRequired.join(\", \")}`)\n this.broadcastSSE(\"reload-partial\", JSON.stringify({ applied, restartRequired }))\n } else if (applied.length) {\n this.broadcastSSE(\"reload-complete\", JSON.stringify({ applied }))\n }\n\n return { applied, restartRequired }\n }\n\n private scheduleMidnightHook(): void {\n const scheduleNext = () => {\n const now = new Date()\n // Next midnight in Africa/Tunis (5s buffer to ensure day rollover)\n const tunisStr = now.toLocaleString(\"en-US\", { timeZone: \"Africa/Tunis\" })\n const tunisNow = new Date(tunisStr)\n\n const target = new Date(tunisNow)\n target.setDate(target.getDate() + 1)\n target.setHours(0, 0, 5, 0)\n\n const tunisOffset = tunisNow.getTime() - now.getTime()\n const delay = target.getTime() - tunisOffset - now.getTime()\n\n this.log(` Cost tracking: next run in ${Math.round(delay / 60_000)}min`)\n\n this.midnightTimer = setTimeout(async () => {\n await this.runDailyCostReport()\n scheduleNext()\n }, Math.max(delay, 60_000)) // min 1 minute guard\n }\n\n scheduleNext()\n }\n\n private async runDailyCostReport(): Promise<void> {\n const yesterday = new Date(Date.now() - 86400_000).toISOString().slice(0, 10)\n const tracker = this.registry.getTokenTracker()\n const report = tracker.generateDailyReport(yesterday)\n\n if (!report || report.totalTasks === 0) {\n this.log(` Cost tracking: no usage for ${yesterday}, skipping`)\n return\n }\n\n tracker.appendToTokenCosts(report)\n this.log(\n ` Cost tracking: ${yesterday} — ${report.totalTasks} tasks, $${report.totalCost.toFixed(4)} (top: ${report.topAgent} $${report.topCost.toFixed(4)})`,\n )\n }\n\n private async startChannels(): Promise<void> {\n // Telegram\n if (this.config.channels.telegram.enabled) {\n const accounts = this.config.channels.telegram.accounts\n if (Object.keys(accounts).length > 0) {\n const policy = this.config.channels.telegram.policy\n const telegram = new TelegramAdapter(\n accounts,\n { policy: { allowFrom: policy?.allowFrom } },\n this.log,\n )\n this.router.addChannel(telegram)\n const globalSize = policy?.allowFrom?.length ?? 0\n const closedAccts = Object.entries(accounts).filter(\n ([, c]) => !c.allowFrom && globalSize === 0,\n ).length\n if (closedAccts > 0) {\n this.log(\n ` Telegram: enabled — WARNING: ${closedAccts} account(s) have no allowFrom (global or per-account). All incoming messages will be DROPPED.`,\n )\n } else {\n this.log(` Telegram: enabled — global allowFrom entries: ${globalSize}`)\n }\n }\n }\n\n // WhatsApp\n if (this.config.channels.whatsapp.enabled) {\n const { setWhatsAppQR, setWhatsAppStatus } = await import(\"./whatsapp-state\")\n const whatsapp = new WhatsAppAdapter(\n {\n sessionDir: this.config.channels.whatsapp.sessionDir,\n defaultAgent: this.config.channels.whatsapp.defaultAgent,\n allowFrom: this.config.channels.whatsapp.allowFrom,\n routes: this.config.channels.whatsapp.routes,\n // Publish QR + status so /api/admin/channels/whatsapp/state can\n // surface the pairing code in the browser.\n onQR: setWhatsAppQR,\n onStatus: setWhatsAppStatus,\n },\n this.log,\n )\n this.router.addChannel(whatsapp)\n this.log(` WhatsApp: enabled (${this.config.channels.whatsapp.routes.length} routes)`)\n }\n\n // Discord\n if (this.config.channels.discord?.enabled && this.config.channels.discord.token) {\n const discord = new DiscordAdapter(\n {\n token: this.config.channels.discord.token,\n agentBinding: this.config.channels.discord.agentBinding,\n },\n this.log,\n )\n this.router.addChannel(discord)\n this.log(\" Discord: enabled\")\n }\n\n // Slack\n if (this.config.channels.slack?.enabled && this.config.channels.slack.botToken && this.config.channels.slack.appToken) {\n const slack = new SlackAdapter(\n {\n botToken: this.config.channels.slack.botToken,\n appToken: this.config.channels.slack.appToken,\n agentBinding: this.config.channels.slack.agentBinding,\n },\n this.log,\n )\n this.router.addChannel(slack)\n this.log(\" Slack: enabled\")\n } else if (this.config.channels.slack?.enabled) {\n this.log(\" Slack: enabled in config but missing botToken/appToken — skipped\")\n }\n\n // GitLab\n if (this.config.channels.gitlab?.enabled && this.config.channels.gitlab.token) {\n const gitlab = new GitLabAdapter(\n {\n webhookPort: this.config.channels.gitlab.webhookPort,\n webhookSecret: this.config.channels.gitlab.webhookSecret,\n host: this.config.channels.gitlab.host,\n token: this.config.channels.gitlab.token,\n routes: this.config.channels.gitlab.routes,\n agentMappings: this.config.channels.gitlab.agentMappings,\n },\n this.log,\n this.hooks,\n )\n // Wire up mesh reaction forwarder — for agents hosted on remote peers\n if (this.mesh) {\n gitlab.setReactForwarder(async (node, project, noteableType, noteableIid, noteId, agentId) => {\n const peer = this.mesh!.directory().find(p => p.peer === node && p.healthy)\n if (!peer) {\n this.log(`[gitlab] react forward: peer \"${node}\" not found or unhealthy`)\n return\n }\n const url = `${peer.peerUrl}/gitlab/react`\n try {\n const r = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ project, noteableType, noteableIid, noteId, agentId }),\n })\n const respText = await r.text().catch(() => \"\")\n this.log(`[gitlab] react forward -> ${url} : ${r.status} ${respText.slice(0, 200)}`)\n } catch (e: any) {\n this.log(`[gitlab] react forward FAILED -> ${url} : ${e.message}`)\n }\n })\n\n // Forward note posting to peer so reply identity stays under the\n // agent's real GitLab user (e.g. @devops-noqta) instead of whatever\n // the local global token resolves to (the group-access-token bot).\n gitlab.setSendNoteForwarder(async (node, project, noteableType, noteableIid, agentId, text): Promise<string> => {\n const peer = this.mesh!.directory().find(p => p.peer === node && p.healthy)\n if (!peer) {\n this.log(`[gitlab] send-note forward: peer \"${node}\" not found or unhealthy`)\n return \"\"\n }\n const url = `${peer.peerUrl}/gitlab/send-note`\n try {\n const r = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ project, noteableType, noteableIid, agentId, text }),\n })\n const data = await r.json().catch(() => ({}))\n this.log(`[gitlab] send-note forward -> ${url} : ${r.status} noteId=${(data as any).noteId || \"?\"}`)\n return (data as any).noteId || \"\"\n } catch (e: any) {\n this.log(`[gitlab] send-note forward FAILED -> ${url} : ${e.message}`)\n return \"\"\n }\n })\n\n // Same for time tracking — peer posts /add_spent_time as its own user.\n gitlab.setLogTimeForwarder(async (node, project, noteableType, noteableIid, agentId, durationMs): Promise<void> => {\n const peer = this.mesh!.directory().find(p => p.peer === node && p.healthy)\n if (!peer) {\n this.log(`[gitlab] log-time forward: peer \"${node}\" not found or unhealthy`)\n return\n }\n const url = `${peer.peerUrl}/gitlab/log-time`\n try {\n const r = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ project, noteableType, noteableIid, agentId, durationMs }),\n })\n const respText = await r.text().catch(() => \"\")\n this.log(`[gitlab] log-time forward -> ${url} : ${r.status} ${respText.slice(0, 160)}`)\n } catch (e: any) {\n this.log(`[gitlab] log-time forward FAILED -> ${url} : ${e.message}`)\n }\n })\n }\n this.router.addChannel(gitlab)\n this.log(` GitLab: enabled (${this.config.channels.gitlab.routes.length} project routes, webhook :${this.config.channels.gitlab.webhookPort})`)\n }\n\n await this.router.startAll()\n }\n\n private async startHttpApi(): Promise<void> {\n const [host, portStr] = this.config.node.bind.split(\":\")\n const port = parseInt(portStr || \"18800\", 10)\n\n this.httpServer = createServer(async (req, res) => {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\")\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\")\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\")\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204)\n res.end()\n return\n }\n\n await this.handleHttp(req, res)\n })\n\n this.httpServer.on(\"error\", (err: any) => {\n if (err.code === \"EADDRINUSE\") {\n this.log(` ERROR: Port ${port} is already in use. Retrying in 5s...`)\n setTimeout(() => {\n this.httpServer?.close()\n this.httpServer?.listen(port, host || \"0.0.0.0\")\n }, 5000)\n } else {\n this.log(` HTTP error: ${err.message}`)\n }\n })\n\n this.httpServer.listen(port, host || \"0.0.0.0\", () => {\n this.log(` HTTP API: http://${host || \"0.0.0.0\"}:${port}`)\n })\n }\n\n /**\n * Broadcast an SSE event to all connected clients.\n */\n private broadcastSSE(event: string, data: string): void {\n if (this.sseClients.size === 0) return\n const payload = `event: ${event}\\ndata: ${JSON.stringify({ time: new Date().toISOString(), message: data })}\\n\\n`\n for (const client of this.sseClients) {\n try { client.write(payload) } catch { this.sseClients.delete(client) }\n }\n }\n\n /**\n * Handle SSE connection for live event streaming.\n * GET /events — streams daemon logs in real-time.\n */\n private handleSSE(req: IncomingMessage, res: ServerResponse): void {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"Connection\": \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n })\n\n // Send current status as first event\n const agents = this.registry.list()\n const active = agents.filter(a => a.active > 0)\n res.write(`event: status\\ndata: ${JSON.stringify({\n node: this.config.node.name,\n agents: agents.length,\n active: active.map(a => ({ id: a.id, name: a.name, tasks: a.active })),\n mesh: this.mesh?.directory().map(p => ({ peer: p.peer, healthy: p.healthy })) || [],\n })}\\n\\n`)\n\n this.sseClients.add(res)\n req.on(\"close\", () => this.sseClients.delete(res))\n }\n\n /**\n * Stream a single running task's live output as SSE.\n * Sends a `start` event with the existing buffer (so a late opener catches up),\n * then `chunk` events for every new delta, and `end` when the task finishes.\n */\n private handleTaskStream(req: IncomingMessage, res: ServerResponse, _agentId: string, taskId: string): void {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"Connection\": \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n \"X-Accel-Buffering\": \"no\",\n })\n let closed = false\n const send = (ev: string, data: unknown) => {\n if (closed) return\n try { res.write(`event: ${ev}\\ndata: ${JSON.stringify(data)}\\n\\n`) } catch { closed = true }\n }\n const sub = this.registry.subscribeToTaskOutput(taskId, (chunk) => send(\"chunk\", { text: chunk }))\n if (!sub) {\n send(\"error\", { message: \"task not found or already evicted\" })\n res.end()\n return\n }\n send(\"start\", { taskId, initial: sub.initial, done: sub.done })\n if (sub.done) {\n send(\"end\", { reason: \"already finished\" })\n res.end()\n return\n }\n // Heartbeat so proxies don't kill idle SSE connections.\n const heartbeat = setInterval(() => { if (!closed) try { res.write(\": ping\\n\\n\") } catch { /* */ } }, 15000)\n req.on(\"close\", () => {\n closed = true\n clearInterval(heartbeat)\n sub.unsubscribe()\n })\n }\n\n private async handleHttp(req: IncomingMessage, res: ServerResponse): Promise<void> {\n const url = new URL(req.url || \"/\", `http://${req.headers.host || \"localhost\"}`)\n const path = url.pathname\n\n try {\n // SSE live event stream\n if (req.method === \"GET\" && path === \"/events\") {\n this.handleSSE(req, res)\n return\n }\n\n // Dynamic routes (before static switch)\n if (req.method === \"POST\" && path.startsWith(\"/webhook/\")) {\n await this.webhooks.handle(req, res, path)\n return\n }\n\n // SSE stream for a single running task — drives the dashboard modal.\n // GET /agents/:agentId/tasks/:taskId/stream\n const taskStreamMatch = req.method === \"GET\" && path.match(/^\\/agents\\/([^/]+)\\/tasks\\/([^/]+)\\/stream$/)\n if (taskStreamMatch) {\n this.handleTaskStream(req, res, taskStreamMatch[1], taskStreamMatch[2])\n return\n }\n\n // Persisted task history (for the dashboard \"Recent activities\" panel).\n // GET /agents/:agentId/tasks?limit=N → list of summaries (newest first)\n // GET /agents/:agentId/tasks/:taskId → one full record (transcript + response)\n const taskHistoryListMatch = req.method === \"GET\" && path.match(/^\\/agents\\/([^/]+)\\/tasks$/)\n if (taskHistoryListMatch) {\n const limit = Math.max(1, Math.min(500, parseInt(url.searchParams.get(\"limit\") || \"50\", 10) || 50))\n this.json(res, 200, this.registry.listTaskHistory(taskHistoryListMatch[1], limit))\n return\n }\n const taskRecordMatch = req.method === \"GET\" && path.match(/^\\/agents\\/([^/]+)\\/tasks\\/([^/]+)$/)\n if (taskRecordMatch) {\n const rec = this.registry.getTaskRecord(taskRecordMatch[1], taskRecordMatch[2])\n if (!rec) { this.json(res, 404, { error: \"not found\" }); return }\n this.json(res, 200, rec)\n return\n }\n\n // OpenAI-compatible endpoint for ElevenLabs, Cursor, etc.\n // POST /v1/chat/completions or /llm/:agentId/v1/chat/completions\n if (req.method === \"POST\" && (path === \"/v1/chat/completions\" || path.match(/^\\/llm\\/[^/]+\\/v1\\/chat\\/completions$/))) {\n await this.handleOpenAICompat(req, res, path)\n return\n }\n\n // Business layer endpoints (/business/status, /business/work, ...)\n if (this.business && path.startsWith(\"/business\")) {\n const handled = await this.business.handleHttp(`${req.method} ${path}`, req, res)\n if (handled) return\n }\n\n switch (`${req.method} ${path}`) {\n case \"GET /health\":\n this.json(res, 200, {\n status: \"ok\",\n node: this.config.node,\n uptime: process.uptime(),\n agents: this.registry.list(),\n crons: this.cron.list().map((j) => ({ id: j.id, enabled: j.enabled, nextRun: j.nextRun })),\n mesh: this.mesh?.directory() || [],\n usage: this.registry.getTodayUsage(),\n })\n break\n\n case \"GET /usage\":\n this.json(res, 200, this.registry.getUsage(7))\n break\n\n case \"GET /agents\":\n this.json(res, 200, this.registry.list())\n break\n\n case \"GET /whatsapp/state\": {\n const { getWhatsAppState } = await import(\"./whatsapp-state\")\n this.json(res, 200, getWhatsAppState())\n break\n }\n\n case \"GET /whatsapp/qr.svg\": {\n const { getWhatsAppState } = await import(\"./whatsapp-state\")\n const s = getWhatsAppState()\n if (!s.qr) { res.writeHead(204, { \"Cache-Control\": \"no-store\" }); res.end(); break }\n try {\n // @ts-ignore — qrcode has no shipped types\n const mod = await import(\"qrcode\")\n const QRCode: any = (mod as any).default || mod\n const svg: string = await QRCode.toString(s.qr, { type: \"svg\", errorCorrectionLevel: \"L\", margin: 1, color: { dark: \"#000\", light: \"#fff\" } })\n res.writeHead(200, { \"Content-Type\": \"image/svg+xml; charset=utf-8\", \"Cache-Control\": \"no-store\" })\n res.end(svg)\n } catch (e: any) {\n res.writeHead(503, { \"Content-Type\": \"text/plain; charset=utf-8\" })\n res.end(\"qrcode package not installed or failed: \" + (e?.message || \"unknown\"))\n }\n break\n }\n\n case \"GET /crons\":\n this.json(res, 200, this.cron.list())\n break\n\n case \"GET /crons/health\":\n this.json(res, 200, this.cron.health())\n break\n\n case \"GET /mesh\":\n this.json(res, 200, this.mesh?.directory() || [])\n break\n\n case \"GET /debug\": {\n const { getDebugState, getDebugLogs } = await import(\"@/observability/debug\")\n this.json(res, 200, { ...getDebugState(), recentLogs: getDebugLogs(50) })\n break\n }\n\n case \"POST /debug/on\": {\n const { setDebug } = await import(\"@/observability/debug\")\n const cats = url.searchParams.get(\"categories\")?.split(\",\") || [\"all\"]\n setDebug(true, cats as any)\n this.log(`Debug enabled: ${cats.join(\", \")}`)\n this.json(res, 200, { enabled: true, categories: cats })\n break\n }\n\n case \"POST /debug/off\": {\n const { setDebug } = await import(\"@/observability/debug\")\n setDebug(false)\n this.log(\"Debug disabled\")\n this.json(res, 200, { enabled: false })\n break\n }\n\n case \"POST /gitlab/react\": {\n // Forwarded from a mesh peer: perform a 👀 reaction using local agent token\n const body = await readBody(req)\n const { project, noteableType, noteableIid, noteId, agentId } = body as any\n const mappings = this.config.channels.gitlab?.agentMappings || []\n const mapping = mappings.find((m: any) => m.agentId === agentId)\n const token = mapping?.token || this.config.channels.gitlab?.token\n const host = this.config.channels.gitlab?.host\n this.log(`[gitlab/react] agent=\"${agentId}\" project=\"${project}\" mappingFound=${!!mapping} mappingToken=${!!mapping?.token} globalToken=${!!this.config.channels.gitlab?.token}(len=${(this.config.channels.gitlab?.token || \"\").length}) host=${host || \"MISSING\"}`)\n if (!token || !host) {\n this.json(res, 404, {\n error: \"no gitlab token for agent\",\n debug: { agentId, mappingFound: !!mapping, hasMappingToken: !!mapping?.token, hasGlobalToken: !!this.config.channels.gitlab?.token, hasHost: !!host },\n })\n break\n }\n const encoded = encodeURIComponent(project)\n const ep = noteableType === \"issue\"\n ? `${host}/api/v4/projects/${encoded}/issues/${noteableIid}/notes/${noteId}/award_emoji`\n : `${host}/api/v4/projects/${encoded}/merge_requests/${noteableIid}/notes/${noteId}/award_emoji`\n try {\n const glRes = await fetch(ep, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", \"PRIVATE-TOKEN\": token },\n body: JSON.stringify({ name: \"eyes\" }),\n })\n const respBody = await glRes.text().catch(() => \"\")\n this.log(`[gitlab/react] -> POST ${ep} : ${glRes.status} ${respBody.slice(0, 120)}`)\n this.json(res, 200, { ok: glRes.ok, status: glRes.status, gitlabResponse: respBody.slice(0, 200) })\n } catch (e: any) {\n this.log(`[gitlab/react] FETCH ERROR: ${e.message}`)\n this.json(res, 500, { error: e.message })\n }\n break\n }\n\n case \"POST /gitlab/send-note\": {\n // Forwarded from a mesh peer: post a note using local agent token\n // so the comment shows up under the agent's real GitLab user.\n const body = await readBody(req)\n const { project, noteableType, noteableIid, agentId, text } = body as any\n const mappings = this.config.channels.gitlab?.agentMappings || []\n const mapping = mappings.find((m: any) => m.agentId === agentId)\n const token = mapping?.token || this.config.channels.gitlab?.token\n const host = this.config.channels.gitlab?.host\n if (!token || !host) {\n this.json(res, 404, { error: \"no gitlab token for agent\", debug: { agentId, hasToken: !!token, hasHost: !!host } })\n break\n }\n const encoded = encodeURIComponent(project)\n const ep = noteableType === \"issue\"\n ? `${host}/api/v4/projects/${encoded}/issues/${noteableIid}/notes`\n : `${host}/api/v4/projects/${encoded}/merge_requests/${noteableIid}/notes`\n try {\n const glRes = await fetch(ep, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", \"PRIVATE-TOKEN\": token },\n body: JSON.stringify({ body: text }),\n })\n const respBody = await glRes.text().catch(() => \"\")\n let noteId = \"\"\n try { noteId = String((JSON.parse(respBody) as any).id || \"\") } catch { /* ignore */ }\n this.log(`[gitlab/send-note] agent=\"${agentId}\" -> POST ${ep} : ${glRes.status} noteId=${noteId || \"?\"}`)\n this.json(res, 200, { ok: glRes.ok, status: glRes.status, noteId })\n } catch (e: any) {\n this.log(`[gitlab/send-note] FETCH ERROR: ${e.message}`)\n this.json(res, 500, { error: e.message })\n }\n break\n }\n\n case \"POST /gitlab/log-time\": {\n // Forwarded from a mesh peer: log spent time under the agent's own user.\n const body = await readBody(req)\n const { project, noteableType, noteableIid, agentId, durationMs } = body as any\n const mappings = this.config.channels.gitlab?.agentMappings || []\n const mapping = mappings.find((m: any) => m.agentId === agentId)\n const token = mapping?.token || this.config.channels.gitlab?.token\n const host = this.config.channels.gitlab?.host\n if (!token || !host) {\n this.json(res, 404, { error: \"no gitlab token for agent\" })\n break\n }\n const totalSeconds = Math.max(60, Math.round(Number(durationMs) / 1000))\n const hours = Math.floor(totalSeconds / 3600)\n const minutes = Math.ceil((totalSeconds % 3600) / 60)\n const duration = hours > 0 ? `${hours}h${minutes > 0 ? ` ${minutes}m` : \"\"}` : `${minutes}m`\n const encoded = encodeURIComponent(project)\n const seg = noteableType === \"merge_request\" ? \"merge_requests\" : \"issues\"\n const ep = `${host}/api/v4/projects/${encoded}/${seg}/${noteableIid}/add_spent_time`\n try {\n const glRes = await fetch(ep, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", \"PRIVATE-TOKEN\": token },\n body: JSON.stringify({ duration }),\n })\n this.log(`[gitlab/log-time] agent=\"${agentId}\" duration=${duration} -> POST ${ep} : ${glRes.status}`)\n this.json(res, 200, { ok: glRes.ok, status: glRes.status, duration })\n } catch (e: any) {\n this.log(`[gitlab/log-time] FETCH ERROR: ${e.message}`)\n this.json(res, 500, { error: e.message })\n }\n break\n }\n\n case \"POST /reload\": {\n try {\n const result = await this.reload()\n this.json(res, 200, { ok: true, ...result })\n } catch (e: any) {\n this.json(res, 500, { ok: false, error: e?.message || String(e) })\n }\n break\n }\n\n case \"POST /send\": {\n const body = await readBody(req)\n if (!body.channel || !body.chatId || !body.text) {\n this.json(res, 400, { error: \"Required: channel, chatId, text\" })\n return\n }\n try {\n const messageId = await this.router.sendOutbound({\n channel: body.channel as string,\n chatId: body.chatId as string,\n text: body.text as string,\n replyTo: body.replyTo as string | undefined,\n parseMode: body.parseMode as any,\n agentId: body.agentId as string | undefined,\n accountId: body.accountId as string | undefined,\n })\n this.json(res, 200, { ok: true, messageId: messageId || null })\n } catch (e: any) {\n this.json(res, 400, { error: e.message })\n }\n break\n }\n\n case \"GET /channels\":\n this.json(res, 200, this.router.getChannelNames())\n break\n\n case \"GET /services\":\n this.json(res, 200, (this.router as any).serviceMatcher?.list() || [])\n break\n\n case \"POST /task\": {\n const body = await readBody(req)\n const agentId = (body.agent as string) || this.config.node.defaultAgent\n if (!agentId || !body.message) {\n this.json(res, 400, { error: \"Missing: message (and no defaultAgent configured)\" })\n return\n }\n // No-op onDelta enables stream-json runtime mode so the dashboard\n // task modal can see tool calls + tool results live. The caller still\n // awaits the final response — they don't see deltas, just the result.\n const response = await this.registry.execute(\n {\n agentId,\n message: body.message as string,\n context: body.context as any,\n },\n () => {},\n )\n this.json(res, response.error ? 500 : 200, response)\n break\n }\n\n case \"POST /ask\":\n case \"GET /ask\": {\n // Voice-optimized endpoint for Siri/voice assistants\n // Accepts message via body.message (POST) or ?q= (GET)\n const url = new URL(req.url || \"/\", `http://${req.headers.host || \"localhost\"}`)\n let message = url.searchParams.get(\"q\") || url.searchParams.get(\"message\") || \"\"\n if (req.method === \"POST\") {\n const body = await readBody(req)\n message = (body.message as string) || (body.q as string) || message\n }\n\n const agentId = this.config.node.defaultAgent\n if (!agentId) {\n this.json(res, 400, { error: \"No defaultAgent configured in node config\" })\n return\n }\n if (!message) {\n this.json(res, 400, { error: \"Missing message (pass as ?q=... or {message:...})\" })\n return\n }\n\n // Prepend instruction so the agent responds in a TTS-friendly way\n const voicePrompt = `[VOICE MODE — Your response will be spoken aloud by a TTS engine. Keep it to 2-3 short sentences. Use plain language, no markdown, no code blocks, no bullet points, no URLs. Speak conversationally.]\\n\\n${message}`\n\n const response = await this.registry.execute({\n agentId,\n message: voicePrompt,\n context: { channel: \"voice\", sender: \"Siri\" },\n })\n\n // Convert response to speakable text (TTS-friendly)\n const speakable = toSpeakable(response.content)\n\n this.json(res, response.error ? 500 : 200, {\n text: speakable,\n full: response.content,\n error: response.error,\n duration: response.duration,\n })\n break\n }\n\n case \"POST /mesh/task\": {\n const body = await readBody(req)\n if (!body.peer || !body.message) {\n this.json(res, 400, { error: \"Missing: peer, message\" })\n return\n }\n if (!this.mesh) {\n this.json(res, 400, { error: \"Mesh not enabled\" })\n return\n }\n const result = await this.mesh.sendTask(body.peer as string, body.message as string, body.agent as string | undefined)\n this.json(res, 200, { response: result })\n break\n }\n\n // A2A agent card discovery\n case \"GET /.well-known/agent-card.json\":\n this.json(res, 200, {\n name: this.config.node.name,\n description: `AgentX daemon node \"${this.config.node.name}\"`,\n url: `http://${this.config.node.bind}`,\n version: \"1.0.0\",\n capabilities: {\n streaming: false,\n pushNotifications: false,\n stateTransitionHistory: false,\n },\n skills: this.registry.list().map((a) => ({\n id: a.id,\n name: a.name,\n description: `Agent \"${a.name}\" (${a.tier})`,\n tags: [a.tier],\n })),\n defaultInputModes: [\"text\"],\n defaultOutputModes: [\"text\"],\n })\n break\n\n // --- Wiki API (for mesh sync) ---\n\n case \"GET /wiki/agents\": {\n const hub = this.registry.getWikiHub()\n this.json(res, 200, {\n nodeId: this.config.node.id,\n agents: hub.summary(),\n })\n break\n }\n\n case \"GET /wiki/entries\": {\n const hub = this.registry.getWikiHub()\n const agentId = url.searchParams.get(\"agent\") || undefined\n const after = url.searchParams.get(\"after\") || undefined\n const entries = agentId\n ? hub.getAgentEntries(agentId)\n : hub.getSharedStore().listEntries({ after })\n this.json(res, 200, {\n nodeId: this.config.node.id,\n count: entries.length,\n entries: entries.map(e => ({\n id: e.id,\n date: e.date,\n agentId: e.agentId,\n source: e.source,\n sourceContext: e.sourceContext,\n content: e.content,\n })),\n })\n break\n }\n\n case \"GET /wiki/articles\": {\n const hub = this.registry.getWikiHub()\n const agentId = url.searchParams.get(\"agent\")\n if (!agentId) {\n this.json(res, 400, { error: \"?agent= required\" })\n break\n }\n const store = hub.getAgentWiki(agentId)\n const index = store.rebuildIndex()\n this.json(res, 200, {\n nodeId: this.config.node.id,\n agentId,\n articles: index.articles,\n })\n break\n }\n\n case \"GET /wiki/article\": {\n const hub = this.registry.getWikiHub()\n const agentId = url.searchParams.get(\"agent\")\n const articlePath = url.searchParams.get(\"path\")\n if (!agentId || !articlePath) {\n this.json(res, 400, { error: \"?agent= and ?path= required\" })\n break\n }\n const store = hub.getAgentWiki(agentId)\n const article = store.readArticle(articlePath)\n if (!article) {\n this.json(res, 404, { error: \"Article not found\" })\n break\n }\n this.json(res, 200, {\n nodeId: this.config.node.id,\n agentId,\n path: article.path,\n title: article.meta.title,\n tags: article.meta.tags,\n owner: article.meta.owner,\n created: article.meta.created,\n lastUpdated: article.meta.lastUpdated,\n sources: article.meta.sources,\n content: article.content,\n })\n break\n }\n\n default:\n this.json(res, 404, {\n error: \"Not found\",\n endpoints: [\n \"GET /health\",\n \"GET /agents\",\n \"GET /crons\",\n \"GET /mesh\",\n \"GET /wiki/agents\",\n \"GET /wiki/entries[?agent=X&after=YYYY-MM-DD]\",\n \"GET /wiki/articles?agent=X\",\n \"POST /task { agent, message, context? }\",\n \"POST /mesh/task { peer, message }\",\n \"POST /webhook/:agentId[/:source] — webhook callback\",\n \"POST /reload — re-read agentx.json (hot-swaps crons)\",\n \"GET /.well-known/agent-card.json\",\n ],\n })\n }\n } catch (e: any) {\n this.json(res, 500, { error: e.message })\n }\n }\n\n /**\n * OpenAI-compatible chat completions endpoint.\n * Allows ElevenLabs, Cursor, or any OpenAI-compatible client to use an AgentX agent as an LLM.\n *\n * Routes:\n * POST /v1/chat/completions — uses \"model\" field as agent ID\n * POST /llm/:agentId/v1/chat/completions — explicit agent ID in URL\n *\n * Request format (OpenAI): { model, messages: [{role, content}], stream?, temperature? }\n * Response format (OpenAI): { id, object, choices: [{message: {role, content}}], usage }\n */\n private async handleOpenAICompat(req: IncomingMessage, res: ServerResponse, path: string): Promise<void> {\n const body = await readBody(req)\n\n // Resolve agent ID: from URL path or \"model\" field\n const pathMatch = path.match(/^\\/llm\\/([^/]+)\\//)\n const fallbackAgent = Object.keys(this.config.agents)[0] || \"default\"\n const agentId = pathMatch?.[1] || (body.model as string) || fallbackAgent\n\n // Extract messages — use the last user message as the task\n const messages = (body.messages as Array<{ role: string; content: string }>) || []\n const lastUserMsg = [...messages].reverse().find(m => m.role === \"user\")\n\n if (!lastUserMsg?.content) {\n this.json(res, 400, { error: { message: \"No user message found\", type: \"invalid_request_error\" } })\n return\n }\n\n // Build conversation context from message history\n const historyLines = messages\n .slice(0, -1) // exclude the last message (it's the prompt)\n .map(m => `${m.role === \"user\" ? \"User\" : \"Assistant\"}: ${m.content.slice(0, 200)}`)\n\n const contextPrefix = historyLines.length > 0\n ? `[Conversation]\\n${historyLines.slice(-10).join(\"\\n\")}\\n\\n`\n : \"\"\n\n const stream = body.stream === true\n\n if (stream) {\n // SSE streaming response\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"Connection\": \"keep-alive\",\n })\n\n const requestId = `chatcmpl-${Date.now().toString(36)}`\n\n const response = await this.registry.execute({\n agentId,\n message: contextPrefix + lastUserMsg.content,\n context: { channel: \"api\", sender: \"openai-compat\" },\n })\n\n const content = response.error || response.content || \"\"\n\n // Send as a single chunk (Claude Code doesn't stream to us via execFile)\n const chunk = {\n id: requestId,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model: agentId,\n choices: [{\n index: 0,\n delta: { role: \"assistant\", content },\n finish_reason: \"stop\",\n }],\n }\n res.write(`data: ${JSON.stringify(chunk)}\\n\\n`)\n res.write(\"data: [DONE]\\n\\n\")\n res.end()\n } else {\n // Standard response\n const response = await this.registry.execute({\n agentId,\n message: contextPrefix + lastUserMsg.content,\n context: { channel: \"api\", sender: \"openai-compat\" },\n })\n\n const content = response.error || response.content || \"\"\n const tokens = Math.ceil(content.length / 4)\n\n this.json(res, 200, {\n id: `chatcmpl-${Date.now().toString(36)}`,\n object: \"chat.completion\",\n created: Math.floor(Date.now() / 1000),\n model: agentId,\n choices: [{\n index: 0,\n message: { role: \"assistant\", content },\n finish_reason: \"stop\",\n }],\n usage: {\n prompt_tokens: Math.ceil(lastUserMsg.content.length / 4),\n completion_tokens: tokens,\n total_tokens: Math.ceil(lastUserMsg.content.length / 4) + tokens,\n },\n })\n }\n }\n\n private json(res: ServerResponse, status: number, data: unknown): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\" })\n res.end(JSON.stringify(data, null, 2))\n }\n}\n\n/**\n * Convert text to TTS-friendly speech.\n * Strips markdown, expands technical terms, removes code, handles symbols.\n */\nfunction toSpeakable(text: string): string {\n let s = text\n\n // Remove code blocks entirely (don't read code aloud)\n s = s.replace(/```[\\s\\S]*?```/g, \". \")\n s = s.replace(/`[^`]*`/g, \"\") // inline code\n\n // Remove markdown syntax characters\n s = s.replace(/\\*\\*(.*?)\\*\\*/g, \"$1\") // bold\n s = s.replace(/__(.*?)__/g, \"$1\") // bold\n s = s.replace(/\\*([^*]+)\\*/g, \"$1\") // italic\n s = s.replace(/_([^_]+)_/g, \"$1\") // italic\n s = s.replace(/~~(.*?)~~/g, \"$1\") // strikethrough\n\n // Links: keep text, drop URL\n s = s.replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n\n // Images: describe\n s = s.replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, \"image$1 \")\n\n // Headers: remove # markers\n s = s.replace(/^#{1,6}\\s+/gm, \"\")\n\n // List bullets: convert to pauses\n s = s.replace(/^[\\s]*[-*+]\\s+/gm, \". \")\n s = s.replace(/^[\\s]*\\d+\\.\\s+/gm, \". \")\n\n // Horizontal rules\n s = s.replace(/^[-=_]{3,}$/gm, \"\")\n\n // Blockquotes\n s = s.replace(/^>\\s*/gm, \"\")\n\n // URLs in plain text: replace with \"link\"\n s = s.replace(/https?:\\/\\/\\S+/g, \"link\")\n\n // File paths: keep readable\n s = s.replace(/\\/\\w+(\\/\\w+)+/g, (match) => match.split(\"/\").filter(Boolean).join(\" slash \"))\n\n // Technical symbols → words\n s = s.replace(/&/g, \" and \")\n s = s.replace(/@/g, \" at \")\n s = s.replace(/=>/g, \" returns \")\n s = s.replace(/->/g, \" to \")\n s = s.replace(/\\|/g, \" or \")\n\n // Common abbreviations\n s = s.replace(/\\be\\.g\\./gi, \"for example\")\n s = s.replace(/\\bi\\.e\\./gi, \"that is\")\n s = s.replace(/\\betc\\./gi, \"etcetera\")\n s = s.replace(/\\bvs\\.?\\b/gi, \"versus\")\n\n // Collapse whitespace\n s = s.replace(/\\n{2,}/g, \". \")\n s = s.replace(/\\n/g, \" \")\n s = s.replace(/\\s+/g, \" \")\n s = s.replace(/\\.\\s*\\./g, \".\")\n s = s.replace(/\\s+([.,!?])/g, \"$1\")\n\n // Strip leading/trailing whitespace and punctuation\n s = s.trim().replace(/^[.,:;]+\\s*/, \"\")\n\n // Cap at 800 chars for voice UX\n if (s.length > 800) {\n s = s.slice(0, 800).replace(/\\s+\\S*$/, \"\") + \"...\"\n }\n\n return s\n}\n\nasync function readBody(req: IncomingMessage): Promise<Record<string, unknown>> {\n return new Promise((resolve, reject) => {\n let body = \"\"\n req.on(\"data\", (chunk: Buffer) => (body += chunk.toString()))\n req.on(\"end\", () => {\n try {\n resolve(body ? JSON.parse(body) : {})\n } catch {\n resolve({})\n }\n })\n req.on(\"error\", reject)\n })\n}\n","import type { ChannelAdapter, IncomingMessage, OutgoingMessage } from \"./types\"\n\n// --- Discord adapter using discord.js ---\n// discord.js is an optional dependency.\n// Bot needs MESSAGE_CONTENT intent enabled in Discord Developer Portal.\n\nexport class DiscordAdapter implements ChannelAdapter {\n readonly name = \"discord\"\n private token: string\n private agentBinding?: string\n private handler?: (msg: IncomingMessage) => Promise<void>\n private client: any = null\n private log: (...args: unknown[]) => void\n\n constructor(\n config: { token: string; agentBinding?: string },\n log: (...args: unknown[]) => void = console.error.bind(console, \"[discord]\"),\n ) {\n this.token = config.token\n this.agentBinding = config.agentBinding\n this.log = log\n }\n\n onMessage(handler: (msg: IncomingMessage) => Promise<void>): void {\n this.handler = handler\n }\n\n async start(): Promise<void> {\n let Discord: any\n\n try {\n // @ts-ignore — optional dependency, not always installed\n Discord = await import(\"discord.js\")\n } catch {\n this.log(\"Discord requires discord.js. Install with:\")\n this.log(\" npm install discord.js\")\n return\n }\n\n const { Client, GatewayIntentBits } = Discord\n\n this.client = new Client({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.DirectMessages,\n ],\n })\n\n this.client.on(\"ready\", () => {\n this.log(`Discord connected as ${this.client.user?.tag}`)\n })\n\n this.client.on(\"messageCreate\", async (msg: any) => {\n if (!this.handler) return\n if (msg.author.bot) return\n\n // Check if bot is mentioned or it's a DM\n const isMentioned = msg.mentions.users.has(this.client.user?.id)\n const isDM = !msg.guild\n\n if (!isMentioned && !isDM) return\n\n // Strip bot mention from text\n let text = msg.content\n if (this.client.user) {\n text = text.replace(new RegExp(`<@!?${this.client.user.id}>`, \"g\"), \"\").trim()\n }\n\n if (!text) return\n\n const incoming: IncomingMessage = {\n id: msg.id,\n channel: \"discord\",\n accountId: \"default\",\n sender: {\n id: msg.author.id,\n name: msg.author.displayName || msg.author.username,\n username: msg.author.username,\n },\n group: msg.guild ? {\n id: msg.channelId,\n name: msg.channel?.name || msg.channelId,\n } : undefined,\n text,\n replyTo: msg.reference?.messageId,\n timestamp: msg.createdAt,\n raw: msg,\n }\n\n this.handler(incoming).catch((e: any) => {\n this.log(`Error handling message: ${e.message}`)\n })\n })\n\n try {\n await this.client.login(this.token)\n } catch (e: any) {\n this.log(`Discord login failed: ${e.message}`)\n }\n }\n\n async stop(): Promise<void> {\n if (this.client) {\n this.client.destroy()\n this.client = null\n }\n }\n\n async send(msg: OutgoingMessage): Promise<string> {\n if (!this.client) return \"\"\n try {\n const channel = await this.client.channels.fetch(msg.chatId)\n if (!channel?.isTextBased()) return \"\"\n\n const sent = await channel.send({\n content: msg.text,\n ...(msg.replyTo ? { reply: { messageReference: msg.replyTo } } : {}),\n })\n return sent.id\n } catch (e: any) {\n this.log(`Send error: ${e.message}`)\n return \"\"\n }\n }\n\n async editMessage(chatId: string, messageId: string, text: string): Promise<boolean> {\n if (!this.client) return false\n try {\n const channel = await this.client.channels.fetch(chatId)\n if (!channel?.isTextBased()) return false\n const message = await channel.messages.fetch(messageId)\n await message.edit(text)\n return true\n } catch { return false }\n }\n\n async sendTyping(chatId: string): Promise<void> {\n if (!this.client) return\n try {\n const channel = await this.client.channels.fetch(chatId)\n if (channel?.isTextBased()) await channel.sendTyping()\n } catch { /* best-effort */ }\n }\n\n async react(chatId: string, messageId: string, emoji: string = \"👀\"): Promise<void> {\n if (!this.client) return\n try {\n const channel = await this.client.channels.fetch(chatId)\n if (!channel?.isTextBased()) return\n const message = await channel.messages.fetch(messageId)\n await message.react(emoji)\n } catch { /* best-effort */ }\n }\n}\n","import type { ChannelAdapter, IncomingMessage, OutgoingMessage } from \"./types\"\n\n// --- Slack adapter (Socket Mode) ---\n//\n// Two optional deps — both peer-installed via `npm i @slack/socket-mode @slack/web-api`\n// when the operator enables this channel. Kept optional so agentx-cli stays\n// lean for installs that never touch Slack.\n//\n// Prerequisites on the Slack side:\n// - Create a Slack app at https://api.slack.com/apps\n// - Enable Socket Mode; generate an app-level token with `connections:write`\n// (starts with xapp-). Put it in .env as SLACK_APP_TOKEN.\n// - Install the bot to the workspace, grab the bot token (starts with xoxb-).\n// Put it in .env as SLACK_BOT_TOKEN.\n// - Scopes needed: chat:write, channels:history, groups:history,\n// im:history, mpim:history, reactions:write, app_mentions:read, users:read.\n// - Subscribe to events: app_mention, message.channels, message.im,\n// message.mpim, message.groups.\n//\n// Routing: DMs always route (no mention required). In channels/groups, the\n// bot has to be @-mentioned — same policy as Discord, matches our \"mention-\n// required in groups\" default.\n\nexport class SlackAdapter implements ChannelAdapter {\n readonly name = \"slack\"\n private botToken: string\n private appToken: string\n private agentBinding?: string\n private handler?: (msg: IncomingMessage) => Promise<void>\n private socket: any = null\n private web: any = null\n private botUserId?: string\n private log: (...args: unknown[]) => void\n\n constructor(\n config: { botToken: string; appToken: string; agentBinding?: string },\n log: (...args: unknown[]) => void = console.error.bind(console, \"[slack]\"),\n ) {\n this.botToken = config.botToken\n this.appToken = config.appToken\n this.agentBinding = config.agentBinding\n this.log = log\n }\n\n onMessage(handler: (msg: IncomingMessage) => Promise<void>): void {\n this.handler = handler\n }\n\n async start(): Promise<void> {\n let SocketModeMod: any\n let WebApiMod: any\n try {\n // @ts-ignore — optional deps\n SocketModeMod = await import(\"@slack/socket-mode\")\n // @ts-ignore\n WebApiMod = await import(\"@slack/web-api\")\n } catch {\n this.log(\"Slack requires @slack/socket-mode and @slack/web-api. Install with:\")\n this.log(\" npm install @slack/socket-mode @slack/web-api\")\n return\n }\n\n const { SocketModeClient } = SocketModeMod\n const { WebClient } = WebApiMod\n\n this.web = new WebClient(this.botToken)\n\n // Resolve our own bot user id once so we can detect mentions + skip self-posts.\n try {\n const authResult = await this.web.auth.test()\n this.botUserId = authResult.user_id\n this.log(`Slack connected as @${authResult.user} (${this.botUserId}) in team ${authResult.team}`)\n } catch (e: any) {\n this.log(`Slack auth.test failed: ${e.message}`)\n return\n }\n\n this.socket = new SocketModeClient({ appToken: this.appToken })\n\n // Socket Mode emits generic \"message\" and \"app_mention\" events. We handle\n // both so direct mentions still fire even if the workspace dropped the\n // message-channels subscription.\n const handle = async (payload: any, ack: () => Promise<void>) => {\n try { await ack() } catch { /* */ }\n if (!this.handler) return\n const event = payload.event\n if (!event || typeof event.text !== \"string\") return\n // Skip our own bot posts and any message that carries a bot_id (prevents\n // bot-on-bot cascades within a workspace).\n if (event.bot_id || event.user === this.botUserId || event.subtype === \"bot_message\") return\n\n const isDM = event.channel_type === \"im\"\n const isMentioned = this.botUserId && event.text.includes(`<@${this.botUserId}>`)\n if (!isDM && !isMentioned) return\n\n // Strip our bot mention from the text so the agent receives a clean prompt.\n let text = event.text\n if (this.botUserId) {\n text = text.replace(new RegExp(`<@${this.botUserId}>`, \"g\"), \"\").trim()\n }\n if (!text) return\n\n // Resolve the sender's display name (best-effort; fall back to the raw id).\n let senderName = event.user || \"slack-user\"\n try {\n const info = await this.web.users.info({ user: event.user })\n senderName = info?.user?.profile?.display_name || info?.user?.real_name || senderName\n } catch { /* */ }\n\n const incoming: IncomingMessage = {\n id: event.client_msg_id || event.ts,\n channel: \"slack\",\n accountId: \"default\",\n sender: {\n id: event.user,\n name: senderName,\n username: senderName,\n },\n group: !isDM ? { id: event.channel, name: event.channel } : undefined,\n text,\n replyTo: event.thread_ts && event.thread_ts !== event.ts ? event.thread_ts : undefined,\n timestamp: event.ts ? new Date(parseFloat(event.ts) * 1000) : new Date(),\n raw: event,\n }\n try { await this.handler(incoming) }\n catch (e: any) { this.log(`Error handling message: ${e.message}`) }\n }\n\n this.socket.on(\"app_mention\", handle)\n this.socket.on(\"message\", handle)\n this.socket.on(\"error\", (e: any) => this.log(`Slack socket error: ${e?.message || e}`))\n\n try {\n await this.socket.start()\n } catch (e: any) {\n this.log(`Slack socket start failed: ${e.message}`)\n }\n }\n\n async stop(): Promise<void> {\n if (this.socket) {\n try { await this.socket.disconnect() } catch { /* */ }\n this.socket = null\n }\n this.web = null\n }\n\n async send(msg: OutgoingMessage): Promise<string> {\n if (!this.web) return \"\"\n try {\n const r = await this.web.chat.postMessage({\n channel: msg.chatId,\n text: msg.text,\n thread_ts: msg.replyTo,\n // Slack's mrkdwn is closest to our default. Plain keeps literal braces\n // and code fences without escaping.\n mrkdwn: msg.parseMode !== \"plain\",\n })\n return r.ts as string\n } catch (e: any) {\n this.log(`Send error: ${e.message}`)\n return \"\"\n }\n }\n\n async editMessage(chatId: string, messageId: string, text: string): Promise<boolean> {\n if (!this.web) return false\n try {\n await this.web.chat.update({ channel: chatId, ts: messageId, text })\n return true\n } catch (e: any) {\n this.log(`Edit error: ${e.message}`)\n return false\n }\n }\n\n async react(chatId: string, messageId: string, emoji: string = \"eyes\"): Promise<void> {\n if (!this.web) return\n try {\n const name = emoji.replace(/^:|:$/g, \"\")\n await this.web.reactions.add({ channel: chatId, timestamp: messageId, name })\n } catch { /* best-effort — the reaction emoji might already be set */ }\n }\n}\n","import type { ChannelAdapter, IncomingMessage, OutgoingMessage, ChannelMeta } from \"./types\"\nimport { createServer, type IncomingMessage as HttpRequest, type ServerResponse } from \"http\"\nimport { debug } from \"@/observability/debug\"\nimport type { HookRegistry } from \"@/hooks\"\n\n// --- GitLab webhook channel adapter ---\n//\n// Receives GitLab webhook events (note/comment, issue, MR, push, pipeline)\n// and routes @mention comments to agents. Agents reply as GitLab API comments.\n//\n// Config:\n// channels.gitlab:\n// enabled: true\n// webhookPort: 18810 # separate port for GitLab webhooks\n// webhookSecret: \"secret\" # validates X-Gitlab-Token header\n// host: \"https://gitlab.example.com\"\n// token: \"${GITLAB_TOKEN}\" # for posting comments back\n// routes:\n// - project: \"org/my-project\"\n// agent: \"pm-agent\"\n// - project: \"*\"\n// agent: \"default-agent\" # default for unmatched projects\n\nexport interface GitLabRoute {\n project: string // \"group/project\" or \"*\" for default\n agent: string\n}\n\nexport interface GitLabAgentMapping {\n agentId: string\n gitlabUsernames: string[] // GitLab @usernames that map to this agent\n keywords: string[] // keywords in comments that trigger this agent (e.g. \"coder\", \"devops\")\n token?: string // per-agent GitLab PAT — posts comments as this agent's user\n node?: string // if set, forward to this mesh peer instead of handling locally\n}\n\nexport interface GitLabChannelConfig {\n webhookPort: number\n webhookSecret?: string\n host: string\n token: string\n routes: GitLabRoute[]\n agentMappings?: GitLabAgentMapping[] // @mention -> agent mappings\n}\n\ninterface GitLabNoteEvent {\n object_kind: \"note\"\n event_type: string\n user: { name: string; username: string }\n project: { path_with_namespace: string; web_url: string }\n object_attributes: {\n id: number\n note: string\n noteable_type: string // \"Issue\", \"MergeRequest\", \"Commit\"\n url: string\n }\n issue?: { iid: number; title: string; state: string }\n merge_request?: { iid: number; title: string; state: string; source_branch: string; target_branch: string }\n}\n\ninterface GitLabIssueEvent {\n object_kind: \"issue\"\n user: { name: string; username: string }\n project: { path_with_namespace: string }\n object_attributes: {\n iid: number\n title: string\n description: string\n state: string\n action: string\n url: string\n assignee_ids?: number[]\n }\n}\n\ninterface GitLabMREvent {\n object_kind: \"merge_request\"\n user: { name: string; username: string }\n project: { path_with_namespace: string }\n object_attributes: {\n iid: number\n title: string\n description: string\n state: string\n action: string\n source_branch: string\n target_branch: string\n url: string\n }\n}\n\ninterface GitLabPipelineEvent {\n object_kind: \"pipeline\"\n user: { name: string; username: string }\n project: { path_with_namespace: string }\n object_attributes: {\n id: number\n ref: string\n status: string\n duration: number\n }\n}\n\ntype GitLabEvent = GitLabNoteEvent | GitLabIssueEvent | GitLabMREvent | GitLabPipelineEvent | Record<string, unknown>\n\nexport class GitLabAdapter implements ChannelAdapter {\n readonly name = \"gitlab\"\n private config: GitLabChannelConfig\n private handler?: (msg: IncomingMessage) => Promise<void>\n private server?: ReturnType<typeof createServer>\n private botUsername?: string // resolved on first API call\n private botUsernames: Set<string> = new Set() // all known bot users (to prevent cascading)\n private sentNoteIds: Set<string> = new Set() // track our own comments\n /** Maps actual GitLab username -> agentId (resolved from tokens at startup) */\n private usernameToAgent: Map<string, string> = new Map()\n /** Maps agentId -> actual GitLab username (resolved from tokens at startup) */\n private agentToUsername: Map<string, string> = new Map()\n private log: (...args: unknown[]) => void\n private hooks?: HookRegistry\n private reactForwarder?: (node: string, project: string, noteableType: string, noteableIid: string, noteId: number, agentId: string) => Promise<void>\n private sendNoteForwarder?: (node: string, project: string, noteableType: string, noteableIid: string, agentId: string, text: string) => Promise<string>\n private logTimeForwarder?: (node: string, project: string, noteableType: string, noteableIid: string, agentId: string, durationMs: number) => Promise<void>\n\n constructor(config: GitLabChannelConfig, log: (...args: unknown[]) => void = console.error.bind(console, \"[gitlab]\"), hooks?: HookRegistry) {\n this.config = config\n this.log = log\n this.hooks = hooks\n }\n\n setReactForwarder(fn: (node: string, project: string, noteableType: string, noteableIid: string, noteId: number, agentId: string) => Promise<void>): void {\n this.reactForwarder = fn\n }\n\n setSendNoteForwarder(fn: (node: string, project: string, noteableType: string, noteableIid: string, agentId: string, text: string) => Promise<string>): void {\n this.sendNoteForwarder = fn\n }\n\n setLogTimeForwarder(fn: (node: string, project: string, noteableType: string, noteableIid: string, agentId: string, durationMs: number) => Promise<void>): void {\n this.logTimeForwarder = fn\n }\n\n onMessage(handler: (msg: IncomingMessage) => Promise<void>): void {\n this.handler = handler\n }\n\n async start(): Promise<void> {\n // Resolve bot usernames from ALL tokens (global + per-agent)\n // This is critical for cascade prevention — we must know every username\n // that posts on behalf of an agent.\n const tokensToResolve: Array<{ label: string; token: string }> = []\n\n if (this.config.token) {\n tokensToResolve.push({ label: \"global\", token: this.config.token })\n }\n for (const mapping of this.config.agentMappings || []) {\n if (mapping.token) {\n tokensToResolve.push({ label: mapping.agentId, token: mapping.token })\n }\n }\n\n // Resolve usernames from all tokens in parallel\n const resolutions = await Promise.allSettled(\n tokensToResolve.map(async ({ label, token }) => {\n const res = await fetch(`${this.config.host}/api/v4/user`, {\n headers: { \"PRIVATE-TOKEN\": token },\n })\n if (!res.ok) throw new Error(`${res.status}`)\n const data = await res.json() as any\n return { label, username: data.username as string }\n })\n )\n\n for (const result of resolutions) {\n if (result.status === \"fulfilled\") {\n const { label, username } = result.value\n this.botUsernames.add(username)\n if (label === \"global\") {\n this.botUsername = username\n this.log(`Global bot user: ${username}`)\n } else {\n // Map actual username <-> agentId (authoritative — from API, not config)\n this.usernameToAgent.set(username.toLowerCase(), label)\n this.agentToUsername.set(label, username.toLowerCase())\n this.log(`Agent \"${label}\" -> GitLab user @${username}`)\n }\n }\n }\n\n // Also register configured gitlabUsernames for cascade prevention\n // AND as fallback mention matching (if token resolution failed)\n for (const mapping of this.config.agentMappings || []) {\n for (const username of mapping.gitlabUsernames) {\n this.botUsernames.add(username)\n // Remote-routed mappings (node property) take explicit priority over token resolution.\n // Token resolution is authoritative only for local agents.\n const alreadySet = this.usernameToAgent.has(username.toLowerCase())\n if (!alreadySet || mapping.node) {\n this.usernameToAgent.set(username.toLowerCase(), mapping.agentId)\n }\n }\n // Fallback: if token resolution didn't set agentToUsername, use first configured username\n if (!this.agentToUsername.has(mapping.agentId) && mapping.gitlabUsernames.length > 0) {\n this.agentToUsername.set(mapping.agentId, mapping.gitlabUsernames[0].toLowerCase())\n }\n }\n\n this.log(`Bot users (${this.botUsernames.size}): ${[...this.botUsernames].join(\", \")}`)\n this.log(`Username->Agent map: ${[...this.usernameToAgent.entries()].map(([u, a]) => `@${u}->${a}`).join(\", \")}`)\n\n this.server = createServer(async (req, res) => {\n if (req.method === \"POST\") {\n await this.handleWebhook(req, res)\n } else {\n res.writeHead(200, { \"Content-Type\": \"text/plain\" })\n res.end(\"GitLab webhook endpoint. POST events here.\")\n }\n })\n\n this.server.on(\"error\", (err: any) => {\n if (err.code === \"EADDRINUSE\") {\n this.log(`GitLab webhook port ${this.config.webhookPort} in use, retrying in 5s...`)\n setTimeout(() => {\n this.server?.close()\n this.server?.listen(this.config.webhookPort)\n }, 5000)\n } else {\n this.log(`GitLab webhook server error: ${err.message}`)\n }\n })\n\n this.server.listen(this.config.webhookPort, () => {\n this.log(`GitLab webhook listening on :${this.config.webhookPort}`)\n })\n }\n\n async stop(): Promise<void> {\n if (this.server) this.server.close()\n }\n\n /**\n * Send a reply — posts a comment back to GitLab via API.\n * chatId format: \"project:noteable_type:iid\" (e.g. \"org/project:issue:123\")\n */\n async send(msg: OutgoingMessage): Promise<string> {\n // chatId format: \"group/project:type:iid\" — split from the end\n const parts = msg.chatId.split(\":\")\n if (parts.length < 3) {\n this.log(`Invalid chatId for GitLab reply: ${msg.chatId}`)\n return \"\"\n }\n const iid = parts.pop()!\n const noteableType = parts.pop()!\n const project = parts.join(\":\") // rejoin in case project path had colons\n\n const encodedProject = encodeURIComponent(project)\n let endpoint: string\n\n switch (noteableType) {\n case \"issue\":\n endpoint = `${this.config.host}/api/v4/projects/${encodedProject}/issues/${iid}/notes`\n break\n case \"merge_request\":\n endpoint = `${this.config.host}/api/v4/projects/${encodedProject}/merge_requests/${iid}/notes`\n break\n default:\n this.log(`Unsupported noteable type: ${noteableType}`)\n return \"\"\n }\n\n // Identity rules — posts must go out under the agent's own GitLab user,\n // never under the shared group-access-token user (e.g. @group_<id>_bot_*):\n //\n // 1. If the agent has a per-agent token configured locally → use it.\n // 2. Else if the agent lives on a remote peer (mapping.node) and a\n // sendNoteForwarder is wired → forward the post there. The peer uses\n // its own local token (owned by that agent's GitLab user) to POST.\n // 3. Else fall back to the global token (signed content from a generic\n // shared identity — legacy path, only reached for local agents with\n // no token mapping).\n const mapping = this.config.agentMappings?.find((m) => m.agentId === msg.agentId)\n const agentToken = this.getAgentToken(msg.agentId)\n if (!agentToken && mapping?.node && this.sendNoteForwarder) {\n try {\n const body = `${msg.text}\\n\\n<!-- agentx:${msg.agentId || \"unknown\"} -->`\n const noteId = await this.sendNoteForwarder(mapping.node, project, noteableType, iid, msg.agentId || \"\", body)\n if (noteId) this.sentNoteIds.add(noteId)\n return noteId\n } catch (e: any) {\n this.log(`GitLab send forward to \"${mapping.node}\" failed: ${e.message} — skipping (would post as group bot)`)\n return \"\"\n }\n }\n\n const token = agentToken || this.config.token\n debug.webhook(\"gitlab\", \"send\", `agentId=\"${msg.agentId}\" token=${agentToken ? \"per-agent\" : \"GLOBAL(\" + this.botUsername + \")\"}`)\n\n try {\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"PRIVATE-TOKEN\": token,\n },\n // Append hidden signature so we can detect our own comments on webhook\n body: JSON.stringify({ body: `${msg.text}\\n\\n<!-- agentx:${msg.agentId || \"unknown\"} -->` }),\n })\n\n if (!res.ok) {\n const text = await res.text()\n this.log(`GitLab API error: ${res.status} ${text}`)\n return \"\"\n }\n\n const data = await res.json() as any\n const noteId = String(data.id || \"\")\n if (noteId) this.sentNoteIds.add(noteId)\n return noteId\n } catch (e: any) {\n this.log(`GitLab send error: ${e.message}`)\n return \"\"\n }\n }\n\n // --- Webhook handling ---\n\n private async handleWebhook(req: HttpRequest, res: ServerResponse): Promise<void> {\n // Validate secret\n if (this.config.webhookSecret) {\n const token = req.headers[\"x-gitlab-token\"]\n if (token !== this.config.webhookSecret) {\n res.writeHead(401, { \"Content-Type\": \"application/json\" })\n res.end(JSON.stringify({ error: \"Invalid token\" }))\n return\n }\n }\n\n const body = await this.readBody(req)\n const event = body as GitLabEvent\n const objectKind = ((event as any).object_kind || (event as any).event_type || \"unknown\") as string\n\n this.log(`Event: ${objectKind} from ${(event as any).project?.path_with_namespace || \"unknown\"}`)\n\n // Route based on event type\n switch (objectKind) {\n case \"note\":\n await this.handleNote(event as GitLabNoteEvent, res)\n break\n case \"issue\":\n await this.handleIssue(event as GitLabIssueEvent, res)\n break\n case \"merge_request\":\n await this.handleMR(event as GitLabMREvent, res)\n break\n case \"pipeline\":\n await this.handlePipeline(event as GitLabPipelineEvent, res)\n break\n default:\n this.log(`Unhandled event: ${objectKind}`)\n res.writeHead(200)\n res.end(\"ok\")\n }\n }\n\n /**\n * Handle note/comment events — the primary use case.\n * Routes @mention comments to the right agent.\n */\n private async handleNote(event: GitLabNoteEvent, res: ServerResponse): Promise<void> {\n if (!this.handler) { res.writeHead(200); res.end(\"ok\"); return }\n\n const note = event.object_attributes.note\n const project = event.project.path_with_namespace\n const user = event.user\n const noteId = String(event.object_attributes.id)\n\n // PRIMARY CASCADE PREVENTION: Check for AgentX signature.\n // Every comment posted by AgentX has <!-- agentx:AGENT_ID --> appended.\n // This is the most reliable check — immune to race conditions and\n // username misconfiguration.\n const signatureMatch = note.match(/<!-- agentx:(\\S+) -->/)\n if (signatureMatch) {\n const sourceAgent = signatureMatch[1]\n // Allow bot-to-bot handoff: if an agent's comment @mentions a DIFFERENT agent\n const mentions = note.match(/@(\\w[\\w.-]*)/g)?.map(m => m.slice(1).replace(/[.]+$/, \"\")) || []\n const mentionsDifferentAgent = mentions.some(m => {\n const targetAgent = this.usernameToAgent.get(m.toLowerCase())\n return targetAgent && targetAgent !== sourceAgent\n })\n if (!mentionsDifferentAgent) {\n this.log(`AgentX comment from ${sourceAgent}, no cross-agent mention, skipping (note ${noteId})`)\n res.writeHead(200); res.end(\"ok\"); return\n }\n this.log(`Bot-to-bot handoff: ${sourceAgent} → ${mentions.join(\", \")} (note ${noteId})`)\n }\n\n // SECONDARY: Also check sentNoteIds (catches race with signature)\n if (this.sentNoteIds.has(noteId)) {\n this.sentNoteIds.delete(noteId)\n res.writeHead(200); res.end(\"ok\"); return\n }\n\n // TERTIARY: Skip comments from known bot users that have no @mentions\n // (catches comments posted via CLI tools without the signature)\n const mentions = note.match(/@(\\w[\\w.-]*)/g)?.map(m => m.slice(1).replace(/[.]+$/, \"\")) || []\n if (mentions.length === 0) {\n this.log(`No @mention in note ${noteId}, skipping`)\n res.writeHead(200); res.end(\"ok\"); return\n }\n\n if (this.isBotUser(user.username) && !signatureMatch) {\n this.log(`Bot comment from ${user.username} without signature, skipping (note ${noteId})`)\n res.writeHead(200); res.end(\"ok\"); return\n }\n\n // Determine the noteable context\n let noteableType = \"\"\n let noteableIid = \"\"\n let noteableTitle = \"\"\n if (event.issue) {\n noteableType = \"issue\"\n noteableIid = String(event.issue.iid)\n noteableTitle = event.issue.title\n } else if (event.merge_request) {\n noteableType = \"merge_request\"\n noteableIid = String(event.merge_request.iid)\n noteableTitle = event.merge_request.title\n }\n\n // Resolve agent deterministically from GitLab @mention -> usernameToAgent map.\n debug.webhook(\"gitlab\", \"handleNote\", `noteId=${noteId} mentions=[${mentions.join(\",\")}] user=${user.username}`)\n\n const resolvedAgentId = this.resolveAgentFromMention(note)\n debug.webhook(\"gitlab\", \"resolve\", `agentId=${resolvedAgentId ?? \"NONE\"}`)\n\n if (!resolvedAgentId) {\n this.log(`[handleNote] @mentions in note ${noteId} are not agents (${mentions.join(\", \")}), skipping`)\n res.writeHead(200); res.end(\"ok\"); return\n }\n\n const targetAgentId = resolvedAgentId\n\n // React with 👀 using the RESOLVED agent's own token (deterministic identity)\n const agentMapping = this.config.agentMappings?.find(m => m.agentId === targetAgentId)\n const agentToken = agentMapping?.token\n const agentNode = (agentMapping as any)?.node as string | undefined\n debug.webhook(\"gitlab\", \"token\", `agent=\"${targetAgentId}\" mapping=${agentMapping ? \"found\" : \"MISSING\"} hasToken=${!!agentToken} node=${agentNode ?? \"local\"}`)\n this.reactToNote(project, noteableType, noteableIid, event.object_attributes.id, agentToken, agentNode, targetAgentId).catch(() => {})\n\n const chatId = `${project}:${noteableType}:${noteableIid}`\n\n const channelMeta = await this.getChannelMeta(chatId)\n\n // Download any images attached in the comment\n const noteClean = note.replace(/\\n*<!-- agentx:\\S+ -->/g, \"\")\n const imageAttachment = await this.downloadNoteImages(noteClean, project, agentToken || this.config.token)\n\n const incoming: IncomingMessage = {\n id: String(event.object_attributes.id),\n channel: \"gitlab\",\n accountId: \"default\",\n sender: {\n id: chatId,\n name: user.name,\n username: user.username,\n },\n text: `[GitLab ${noteableType} #${noteableIid}: ${noteableTitle}]\\n${user.name} commented:\\n${noteClean}`,\n timestamp: new Date(),\n raw: event,\n resolvedAgent: targetAgentId,\n preferNode: agentMapping?.node,\n channelMeta: channelMeta ? { ...channelMeta, issue: { type: noteableType, iid: noteableIid, title: noteableTitle } } : undefined,\n media: imageAttachment,\n }\n\n this.handler(incoming).catch((e) => {\n this.log(`Error handling note: ${e.message}`)\n })\n\n res.writeHead(200)\n res.end(\"ok\")\n }\n\n /**\n * Handle issue events (opened, updated, closed).\n * Only routes events from human users, not bot-triggered updates.\n */\n private async handleIssue(event: GitLabIssueEvent, res: ServerResponse): Promise<void> {\n if (!this.handler) { res.writeHead(200); res.end(\"ok\"); return }\n\n // Skip bot-triggered issue updates (label changes, assignments by agents)\n if (this.isBotUser(event.user.username)) {\n res.writeHead(200); res.end(\"ok\"); return\n }\n\n const attrs = event.object_attributes\n const project = event.project.path_with_namespace\n const defaultAgentId = this.resolveAgent(project)\n\n // Fire `on:gitlab-issue` hook so projects can customize routing — e.g.\n // trigger a specific agent on assignment rather than the project default.\n // The hook may:\n // - return `blocked: true` to suppress the default dispatch\n // - return `modified.dispatch: Array<{ agentId, preferNode?, prompt? }>`\n // to dispatch to one or more specific agents (skips default)\n // - return nothing, letting the default project-agent behavior run\n // The event body stays inside the context so hook scripts have full detail\n // (changes.assignees, attrs.assignees, labels, etc.) without us re-parsing.\n let dispatch: Array<{ agentId: string; preferNode?: string; prompt?: string; assignee?: string }> | undefined\n let hookBlocked = false\n if (this.hooks?.has(\"on:gitlab-issue\" as any)) {\n try {\n const result = await this.hooks.execute(\"on:gitlab-issue\" as any, {\n event: \"on:gitlab-issue\" as any,\n issueEvent: event,\n project,\n iid: attrs.iid,\n title: attrs.title,\n description: attrs.description || \"\",\n url: attrs.url,\n action: attrs.action,\n usernameToAgent: Object.fromEntries(this.usernameToAgent.entries()),\n agentMappings: this.config.agentMappings || [],\n defaultAgentId: defaultAgentId || null,\n })\n if (result.blocked) {\n hookBlocked = true\n }\n const modDispatch = (result.modified as any)?.dispatch\n if (Array.isArray(modDispatch) && modDispatch.length > 0) {\n dispatch = modDispatch.filter((d: any) => d && typeof d.agentId === \"string\")\n }\n } catch (e: any) {\n this.log(`on:gitlab-issue hook error: ${e.message}`)\n }\n }\n\n // Hook-driven dispatch: route one IncomingMessage per dispatch entry.\n if (dispatch && dispatch.length > 0) {\n for (const d of dispatch) {\n const mapping = this.config.agentMappings?.find((m) => m.agentId === d.agentId)\n const chatId = `${project}:issue:${attrs.iid}`\n const channelMeta = await this.getChannelMeta(chatId)\n const id = `issue-hook-${attrs.iid}-${d.agentId}${d.assignee ? `-${d.assignee}` : \"\"}-${attrs.action}`\n const incoming: IncomingMessage = {\n id,\n channel: \"gitlab\",\n accountId: \"default\",\n sender: {\n id: chatId,\n name: event.user.name,\n username: event.user.username,\n },\n text: d.prompt || `[GitLab Issue #${attrs.iid} ${attrs.action}]: ${attrs.title}\\n${attrs.description?.slice(0, 500) || \"\"}\\nURL: ${attrs.url}`,\n timestamp: new Date(),\n raw: event,\n resolvedAgent: d.agentId,\n preferNode: d.preferNode || mapping?.node,\n channelMeta: channelMeta ? {\n ...channelMeta,\n issue: { type: \"issue\", iid: String(attrs.iid), title: attrs.title },\n } : undefined,\n }\n this.log(`Issue #${attrs.iid} hook dispatch -> agent \"${d.agentId}\"${d.preferNode || mapping?.node ? ` (remote: ${d.preferNode || mapping?.node})` : \"\"}`)\n this.handler(incoming).catch((e) => this.log(`Error handling hook dispatch: ${e.message}`))\n }\n res.writeHead(200); res.end(\"ok\"); return\n }\n\n if (hookBlocked) {\n this.log(`Issue #${attrs.iid}: on:gitlab-issue hook suppressed default dispatch`)\n res.writeHead(200); res.end(\"ok\"); return\n }\n\n // Default: route generic issue event to the project's configured agent.\n const incoming: IncomingMessage = {\n id: `issue-${attrs.iid}-${attrs.action}`,\n channel: \"gitlab\",\n accountId: \"default\",\n sender: {\n id: `${project}:issue:${attrs.iid}`,\n name: event.user.name,\n username: event.user.username,\n },\n // No group — sender.id has project:type:iid for reply routing\n text: `[GitLab Issue #${attrs.iid} ${attrs.action}]: ${attrs.title}\\n${attrs.description?.slice(0, 500) || \"\"}\\nURL: ${attrs.url}`,\n timestamp: new Date(),\n raw: event,\n resolvedAgent: defaultAgentId,\n }\n\n this.handler(incoming).catch((e) => this.log(`Error handling issue: ${e.message}`))\n res.writeHead(200)\n res.end(\"ok\")\n }\n\n /**\n * Handle merge request events.\n */\n private async handleMR(event: GitLabMREvent, res: ServerResponse): Promise<void> {\n if (!this.handler) { res.writeHead(200); res.end(\"ok\"); return }\n\n // Skip bot-triggered MR updates\n if (this.isBotUser(event.user.username)) {\n res.writeHead(200); res.end(\"ok\"); return\n }\n\n const attrs = event.object_attributes\n const project = event.project.path_with_namespace\n const agentId = this.resolveAgent(project)\n\n const incoming: IncomingMessage = {\n id: `mr-${attrs.iid}-${attrs.action}`,\n channel: \"gitlab\",\n accountId: \"default\",\n sender: {\n id: `${project}:merge_request:${attrs.iid}`,\n name: event.user.name,\n username: event.user.username,\n },\n // No group — sender.id has project:type:iid for reply routing\n text: `[GitLab MR !${attrs.iid} ${attrs.action}]: ${attrs.title}\\nBranch: ${attrs.source_branch} -> ${attrs.target_branch}\\n${attrs.description?.slice(0, 500) || \"\"}\\nURL: ${attrs.url}`,\n timestamp: new Date(),\n raw: event,\n resolvedAgent: agentId,\n }\n\n this.handler(incoming).catch((e) => this.log(`Error handling MR: ${e.message}`))\n res.writeHead(200)\n res.end(\"ok\")\n }\n\n /**\n * Handle pipeline events (success, failed).\n */\n private async handlePipeline(event: GitLabPipelineEvent, res: ServerResponse): Promise<void> {\n const attrs = event.object_attributes\n const project = event.project.path_with_namespace\n const terminalStatuses = [\"success\", \"failed\", \"canceled\"]\n\n // Fire on:gitlab-pipeline hook for all terminal pipelines (side-effect hooks, e.g. time logging)\n if (terminalStatuses.includes(attrs.status) && this.hooks?.has(\"on:gitlab-pipeline\" as any)) {\n this.hooks.execute(\"on:gitlab-pipeline\" as any, {\n event: \"on:gitlab-pipeline\" as any,\n pipelineId: attrs.id,\n status: attrs.status,\n ref: attrs.ref,\n duration: attrs.duration,\n project,\n projectId: (event as any).project?.id,\n raw: event,\n }).catch((e: Error) => this.log(`on:gitlab-pipeline hook error: ${e.message}`))\n }\n\n // Only route to agent on failures\n if (!this.handler || attrs.status !== \"failed\") {\n res.writeHead(200)\n res.end(\"ok\")\n return\n }\n\n const agentId = this.resolveAgent(project)\n\n const incoming: IncomingMessage = {\n id: `pipeline-${attrs.id}`,\n channel: \"gitlab\",\n accountId: \"default\",\n sender: {\n id: `${project}:pipeline:${attrs.id}`,\n name: event.user.name,\n username: event.user.username,\n },\n // No group — sender.id has project:type:iid for reply routing\n text: `[GitLab Pipeline FAILED] Project: ${project}\\nRef: ${attrs.ref}\\nDuration: ${attrs.duration}s\\nPlease investigate the failure.`,\n timestamp: new Date(),\n raw: event,\n resolvedAgent: agentId,\n }\n\n this.handler(incoming).catch((e) => this.log(`Error handling pipeline: ${e.message}`))\n res.writeHead(200)\n res.end(\"ok\")\n }\n\n /**\n * Resolve agent from @mentions in a comment.\n * Uses the authoritative usernameToAgent map (built from API token resolution\n * at startup) — not the manually configured gitlabUsernames which may be wrong.\n */\n private resolveAgentFromMention(text: string): string | undefined {\n // Extract @mentions from the comment, strip trailing dots/punctuation\n const mentions = text.match(/@(\\w[\\w.-]*)/g)?.map(m => m.slice(1).replace(/[.]+$/, \"\").toLowerCase()) || []\n if (mentions.length === 0) return undefined\n\n // Check against the authoritative username->agent map (resolved from tokens)\n for (const mention of mentions) {\n const agentId = this.usernameToAgent.get(mention)\n if (agentId) {\n this.log(`Mention @${mention} -> agent \"${agentId}\" (resolved from token)`)\n return agentId\n }\n }\n\n return undefined\n }\n\n /**\n * Resolve which agent handles a project based on routes.\n */\n private resolveAgent(project: string): string | undefined {\n for (const route of this.config.routes) {\n if (route.project === project || route.project === \"*\") {\n return route.agent\n }\n }\n return undefined\n }\n\n /**\n * Get verified context for a GitLab project chat.\n * chatId format: \"project/path:issue:123\" or \"project/path:merge_request:456\"\n */\n async getChannelMeta(chatId: string): Promise<ChannelMeta | undefined> {\n const parts = chatId.split(\":\")\n const project = parts[0]\n const noteableType = parts[1]\n const noteableIid = parts[2]\n\n // Find all agents mapped to this project\n const agents: ChannelMeta[\"agents\"] = []\n for (const route of this.config.routes) {\n if (route.project === project || route.project === \"*\") {\n agents.push({ id: route.agent, name: route.agent })\n }\n }\n for (const mapping of this.config.agentMappings || []) {\n if (!agents.some(a => a.id === mapping.agentId)) {\n agents.push({ id: mapping.agentId, name: mapping.agentId })\n }\n }\n\n const facts: string[] = [\n \"This is a GitLab webhook event — respond as a GitLab comment\",\n \"Do NOT use Telegram handles or delegate to other agents\",\n \"Do NOT mention other agents by name — you are the only agent responding to this event\",\n \"Stay in your role — do not act as or speak for other agents\",\n ]\n\n return {\n channel: \"gitlab\",\n agents,\n project,\n issue: noteableType && noteableIid ? { type: noteableType, iid: noteableIid, title: \"\" } : undefined,\n facts,\n }\n }\n\n /**\n * Check if a username belongs to a known bot/agent user.\n */\n private isBotUser(username: string): boolean {\n return this.botUsernames.has(username)\n }\n\n /**\n * Get the per-agent GitLab token from agentMappings.\n * Falls back to undefined if no per-agent token is configured.\n */\n getAgentToken(agentId?: string): string | undefined {\n if (!agentId || !this.config.agentMappings?.length) return undefined\n const mapping = this.config.agentMappings.find(m => m.agentId === agentId)\n return mapping?.token\n }\n\n /**\n * Find the per-agent token for the first @mentioned agent in note text.\n */\n private getTokenForMentionedAgent(text: string): string | undefined {\n if (!this.config.agentMappings?.length) return undefined\n const mentionedUsers = text.match(/@(\\w[\\w.-]*)/g)?.map(m => m.slice(1).replace(/[.]+$/, \"\").toLowerCase()) || []\n for (const mapping of this.config.agentMappings) {\n if (mapping.token && mapping.gitlabUsernames.some(u => mentionedUsers.includes(u.toLowerCase()))) {\n return mapping.token\n }\n }\n return undefined\n }\n\n /**\n * React to a GitLab note with an emoji (👀 eyes) to acknowledge receipt.\n * ONLY uses the agent's own token — never the global token (which may\n * belong to a different agent user, causing the wrong identity to react).\n */\n private async reactToNote(project: string, noteableType: string, noteableIid: string, noteId: number, agentToken?: string, node?: string, agentId?: string): Promise<void> {\n // Agent lives on a remote mesh peer — forward the reaction request there\n if (!agentToken && node && agentId && this.reactForwarder) {\n this.log(`Forwarding 👀 reaction for \"${agentId}\" to mesh peer \"${node}\"`)\n await this.reactForwarder(node, project, noteableType, noteableIid, noteId, agentId)\n return\n }\n\n if (!agentToken) {\n this.log(`No token for reaction on note ${noteId} (agent \"${agentId}\") — skipping`)\n return\n }\n\n const encodedProject = encodeURIComponent(project)\n let endpoint: string\n\n switch (noteableType) {\n case \"issue\":\n endpoint = `${this.config.host}/api/v4/projects/${encodedProject}/issues/${noteableIid}/notes/${noteId}/award_emoji`\n break\n case \"merge_request\":\n endpoint = `${this.config.host}/api/v4/projects/${encodedProject}/merge_requests/${noteableIid}/notes/${noteId}/award_emoji`\n break\n default:\n return\n }\n\n try {\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"PRIVATE-TOKEN\": agentToken,\n },\n body: JSON.stringify({ name: \"eyes\" }),\n })\n if (!res.ok) {\n this.log(`Reaction failed on note ${noteId}: ${res.status}`)\n }\n } catch (e: any) {\n this.log(`Failed to react to note ${noteId}: ${e.message}`)\n }\n }\n\n /**\n * Log time spent on a GitLab issue/MR after agent completes work.\n * Uses the /add_spent_time API endpoint.\n * chatId format: \"project:type:iid\" (e.g. \"mtgl/mtgl-system-v2:issue:646\")\n */\n async logTimeSpent(chatId: string, durationMs: number, agentId?: string): Promise<void> {\n const parts = chatId.split(\":\")\n if (parts.length < 3) return\n\n const iid = parts.pop()!\n const noteableType = parts.pop()!\n const project = parts.join(\":\")\n\n // Convert ms to GitLab duration string (minimum 1m)\n const totalSeconds = Math.max(60, Math.round(durationMs / 1000))\n const hours = Math.floor(totalSeconds / 3600)\n const minutes = Math.ceil((totalSeconds % 3600) / 60)\n const duration = hours > 0 ? `${hours}h${minutes > 0 ? ` ${minutes}m` : \"\"}` : `${minutes}m`\n\n const encodedProject = encodeURIComponent(project)\n const typeSegment = noteableType === \"merge_request\" ? \"merge_requests\" : \"issues\"\n const endpoint = `${this.config.host}/api/v4/projects/${encodedProject}/${typeSegment}/${iid}/add_spent_time`\n\n // Same identity rules as send(): for remote-hosted agents with no local\n // per-agent token, forward the time-log to the peer so it posts as the\n // agent's real GitLab user rather than the shared group bot.\n const mapping = this.config.agentMappings?.find((m) => m.agentId === agentId)\n const agentToken = this.getAgentToken(agentId)\n if (!agentToken && mapping?.node && this.logTimeForwarder) {\n try {\n await this.logTimeForwarder(mapping.node, project, noteableType, iid, agentId || \"\", durationMs)\n this.log(`Time logged (via peer \"${mapping.node}\"): ${duration} on ${project} ${typeSegment}/${iid} (${agentId})`)\n } catch (e: any) {\n this.log(`Time log forward to \"${mapping.node}\" failed: ${e.message} — skipping (would log as group bot)`)\n }\n return\n }\n\n const token = agentToken || this.config.token\n\n try {\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"PRIVATE-TOKEN\": token,\n },\n body: JSON.stringify({ duration }),\n })\n\n if (res.ok) {\n this.log(`Time logged: ${duration} on ${project} ${noteableType} #${iid} (${agentId || \"global\"})`)\n } else {\n const text = await res.text()\n this.log(`Time log failed (${res.status}): ${text.slice(0, 100)}`)\n }\n } catch (e: any) {\n this.log(`Time log error: ${e.message}`)\n }\n }\n\n /**\n * Extract and download images from a GitLab comment.\n * GitLab markdown images: \n * Returns the first image found as media attachment, or undefined.\n */\n private async downloadNoteImages(\n note: string,\n project: string,\n token: string,\n ): Promise<IncomingMessage[\"media\"] | undefined> {\n // Match GitLab upload paths:  or full URLs\n const imagePattern = /!\\[[^\\]]*\\]\\(([^)]+\\.(?:png|jpg|jpeg|gif|webp|svg))\\)/gi\n const matches = [...note.matchAll(imagePattern)]\n if (matches.length === 0) return undefined\n\n const imagePath = matches[0][1] // First image\n let imageUrl: string\n\n if (imagePath.startsWith(\"http\")) {\n imageUrl = imagePath\n } else {\n // Relative path — resolve against GitLab project\n const encodedProject = encodeURIComponent(project)\n imageUrl = `${this.config.host}/${project}${imagePath}`\n }\n\n try {\n const res = await fetch(imageUrl, {\n headers: { \"PRIVATE-TOKEN\": token },\n })\n if (!res.ok) {\n this.log(`Failed to download image: ${res.status} ${imageUrl}`)\n return undefined\n }\n\n const buffer = Buffer.from(await res.arrayBuffer())\n const contentType = res.headers.get(\"content-type\") || \"image/png\"\n const ext = contentType.split(\"/\")[1]?.split(\";\")[0] || \"png\"\n\n // Save to disk\n const { mkdirSync, writeFileSync } = await import(\"fs\")\n const { resolve } = await import(\"path\")\n const { randomUUID } = await import(\"crypto\")\n const mediaDir = resolve(process.cwd(), \".agentx/media/gitlab\")\n mkdirSync(mediaDir, { recursive: true })\n const fileName = `${randomUUID().slice(0, 8)}.${ext}`\n const filePath = resolve(mediaDir, fileName)\n writeFileSync(filePath, buffer)\n\n this.log(`Downloaded GitLab image: ${filePath} (${buffer.length} bytes)`)\n\n return {\n path: filePath,\n type: contentType,\n fileName: imagePath.split(\"/\").pop() || fileName,\n }\n } catch (e: any) {\n this.log(`Image download error: ${e.message}`)\n return undefined\n }\n }\n\n private async readBody(req: HttpRequest): Promise<Record<string, unknown>> {\n return new Promise((resolve) => {\n let body = \"\"\n req.on(\"data\", (chunk: Buffer) => (body += chunk.toString()))\n req.on(\"end\", () => {\n try { resolve(body ? JSON.parse(body) : {}) }\n catch { resolve({ raw: body }) }\n })\n req.on(\"error\", () => resolve({}))\n })\n }\n}\n","// --- Structured logger for the daemon ---\n// Outputs JSON lines to stderr for machine parsing, human-readable to stdout.\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\"\n\ninterface LogEntry {\n time: string\n level: LogLevel\n module: string\n msg: string\n [key: string]: unknown\n}\n\nexport class Logger {\n private module: string\n private minLevel: LogLevel\n private static levelOrder: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 }\n\n constructor(module: string, minLevel: LogLevel = \"info\") {\n this.module = module\n this.minLevel = minLevel\n }\n\n child(module: string): Logger {\n return new Logger(`${this.module}:${module}`, this.minLevel)\n }\n\n debug(msg: string, data?: Record<string, unknown>): void {\n this.emit(\"debug\", msg, data)\n }\n\n info(msg: string, data?: Record<string, unknown>): void {\n this.emit(\"info\", msg, data)\n }\n\n warn(msg: string, data?: Record<string, unknown>): void {\n this.emit(\"warn\", msg, data)\n }\n\n error(msg: string, data?: Record<string, unknown>): void {\n this.emit(\"error\", msg, data)\n }\n\n /**\n * Create a console.error-compatible function for backward compat.\n */\n asConsoleLog(): (...args: unknown[]) => void {\n return (...args: unknown[]) => {\n const msg = args.map(a => typeof a === \"string\" ? a : JSON.stringify(a)).join(\" \")\n // Strip [agentx] prefix if present\n const clean = msg.replace(/^\\[agentx\\]\\s*/, \"\")\n if (clean) this.info(clean)\n }\n }\n\n private emit(level: LogLevel, msg: string, data?: Record<string, unknown>): void {\n if (Logger.levelOrder[level] < Logger.levelOrder[this.minLevel]) return\n\n const entry: LogEntry = {\n time: new Date().toISOString(),\n level,\n module: this.module,\n msg,\n ...data,\n }\n\n // Human-readable to stderr (what the user sees)\n const prefix = level === \"error\" ? \"ERROR\" : level === \"warn\" ? \"WARN\" : \"\"\n const tag = `[${this.module}]`\n const display = prefix ? `${tag} ${prefix}: ${msg}` : `${tag} ${msg}`\n console.error(display)\n }\n}\n","import type { IncomingMessage, ServerResponse } from \"http\"\nimport type { AgentRegistry } from \"@/agents/registry\"\n\n// --- Webhook handler ---\n// Routes incoming webhooks from GitLab, GitHub, Stripe, Sentry, etc.\n// to the appropriate agent with parsed payload context.\n//\n// Endpoints:\n// POST /webhook/:agentId — route to specific agent\n// POST /webhook/:agentId/:source — route with source hint (gitlab, github, etc.)\n//\n// The webhook payload is parsed and a human-readable summary is built\n// so the agent doesn't need to parse raw JSON.\n\ninterface WebhookConfig {\n /** Secret for validating webhook signatures (optional) */\n secrets?: Record<string, string> // source -> secret\n}\n\nexport class WebhookHandler {\n private registry: AgentRegistry\n private config: WebhookConfig\n private log: (...args: unknown[]) => void\n\n constructor(\n registry: AgentRegistry,\n config: WebhookConfig = {},\n log: (...args: unknown[]) => void = console.error.bind(console, \"[webhook]\"),\n ) {\n this.registry = registry\n this.config = config\n this.log = log\n }\n\n /**\n * Handle an incoming webhook request.\n * URL format: /webhook/:agentId or /webhook/:agentId/:source\n */\n async handle(req: IncomingMessage, res: ServerResponse, path: string): Promise<void> {\n // Parse path: /webhook/<agentId> or /webhook/<agentId>/<source>\n const parts = path.replace(/^\\/webhook\\/?/, \"\").split(\"/\").filter(Boolean)\n const agentId = parts[0]\n const sourceHint = parts[1] // optional: \"gitlab\", \"github\", \"stripe\", \"sentry\"\n\n if (!agentId) {\n this.sendJson(res, 400, { error: \"Missing agent ID. Use /webhook/:agentId\" })\n return\n }\n\n // Read body\n const body = await this.readBody(req)\n\n // Detect source from headers if not in URL\n const source = sourceHint || this.detectSource(req.headers)\n\n // Parse the webhook payload into a human-readable message\n const summary = this.buildSummary(source, body, req.headers)\n\n this.log(`Webhook [${source}] -> ${agentId}: ${summary.slice(0, 100)}`)\n\n // Execute on the agent\n try {\n const response = await this.registry.execute({\n message: summary,\n agentId,\n context: {\n channel: `webhook:${source}`,\n sender: `webhook:${source}`,\n },\n })\n\n this.sendJson(res, response.error ? 500 : 200, {\n ok: !response.error,\n agent: agentId,\n source,\n response: response.content?.slice(0, 500),\n error: response.error,\n duration: response.duration,\n })\n } catch (e: any) {\n this.sendJson(res, 500, { error: e.message })\n }\n }\n\n /**\n * Detect webhook source from request headers.\n */\n private detectSource(headers: Record<string, string | string[] | undefined>): string {\n if (headers[\"x-gitlab-event\"] || headers[\"x-gitlab-token\"]) return \"gitlab\"\n if (headers[\"x-github-event\"]) return \"github\"\n if (headers[\"stripe-signature\"]) return \"stripe\"\n if (headers[\"sentry-hook-resource\"]) return \"sentry\"\n if (headers[\"x-vercel-signature\"]) return \"vercel\"\n if (headers[\"x-hub-signature-256\"]) return \"github\"\n return \"unknown\"\n }\n\n /**\n * Build a human-readable summary from the webhook payload.\n * Agent reads this instead of raw JSON.\n */\n private buildSummary(source: string, body: Record<string, unknown>, headers: Record<string, string | string[] | undefined>): string {\n const lines: string[] = [`[Webhook from ${source}]`]\n\n switch (source) {\n case \"gitlab\": {\n const event = headers[\"x-gitlab-event\"] as string || body.object_kind as string || \"event\"\n const project = (body.project as any)?.path_with_namespace || \"\"\n const user = (body.user as any)?.name || (body.user_username as string) || \"\"\n\n lines.push(`Event: ${event}`)\n if (project) lines.push(`Project: ${project}`)\n if (user) lines.push(`User: ${user}`)\n\n // Push events\n if (body.ref) lines.push(`Ref: ${body.ref}`)\n if (body.commits && Array.isArray(body.commits)) {\n lines.push(`Commits: ${body.commits.length}`)\n for (const c of (body.commits as any[]).slice(0, 3)) {\n lines.push(` - ${c.message?.split(\"\\n\")[0] || \"no message\"} (${c.author?.name || \"\"})`)\n }\n }\n\n // MR events\n const mr = body.object_attributes as any\n if (mr?.title) {\n lines.push(`Title: ${mr.title}`)\n lines.push(`State: ${mr.state || \"\"}`)\n lines.push(`Action: ${mr.action || \"\"}`)\n if (mr.source_branch) lines.push(`Branch: ${mr.source_branch} -> ${mr.target_branch}`)\n if (mr.url) lines.push(`URL: ${mr.url}`)\n }\n\n // Issue events\n if (mr?.iid && !mr?.source_branch) {\n lines.push(`Issue #${mr.iid}: ${mr.title || \"\"}`)\n if (mr.description) lines.push(`Description: ${mr.description.slice(0, 200)}`)\n }\n\n // Pipeline events\n if (body.object_kind === \"pipeline\") {\n const attrs = body.object_attributes as any\n lines.push(`Pipeline: ${attrs?.status || \"\"} (${attrs?.ref || \"\"})`)\n lines.push(`Duration: ${attrs?.duration || 0}s`)\n }\n break\n }\n\n case \"github\": {\n const event = headers[\"x-github-event\"] as string || \"event\"\n const repo = (body.repository as any)?.full_name || \"\"\n const sender = (body.sender as any)?.login || \"\"\n\n lines.push(`Event: ${event}`)\n if (repo) lines.push(`Repository: ${repo}`)\n if (sender) lines.push(`Sender: ${sender}`)\n\n // Push\n if (body.ref) lines.push(`Ref: ${body.ref}`)\n if (body.commits && Array.isArray(body.commits)) {\n for (const c of (body.commits as any[]).slice(0, 3)) {\n lines.push(` - ${c.message?.split(\"\\n\")[0] || \"\"} (${c.author?.name || \"\"})`)\n }\n }\n\n // PR\n const pr = body.pull_request as any\n if (pr) {\n lines.push(`PR #${pr.number}: ${pr.title}`)\n lines.push(`Action: ${body.action}`)\n lines.push(`Branch: ${pr.head?.ref} -> ${pr.base?.ref}`)\n }\n\n // Issue\n const issue = body.issue as any\n if (issue) {\n lines.push(`Issue #${issue.number}: ${issue.title}`)\n lines.push(`Action: ${body.action}`)\n }\n break\n }\n\n case \"stripe\": {\n const type = body.type as string || \"event\"\n const data = (body.data as any)?.object || {}\n\n lines.push(`Event: ${type}`)\n if (data.amount) lines.push(`Amount: ${(data.amount / 100).toFixed(2)} ${data.currency?.toUpperCase() || \"\"}`)\n if (data.customer_email) lines.push(`Customer: ${data.customer_email}`)\n if (data.description) lines.push(`Description: ${data.description}`)\n if (data.status) lines.push(`Status: ${data.status}`)\n break\n }\n\n case \"sentry\": {\n const resource = headers[\"sentry-hook-resource\"] as string || \"event\"\n lines.push(`Resource: ${resource}`)\n\n const data = body.data as any || body\n if (data.error?.title) lines.push(`Error: ${data.error.title}`)\n if (data.error?.culprit) lines.push(`Culprit: ${data.error.culprit}`)\n if (data.error?.metadata?.value) lines.push(`Message: ${data.error.metadata.value}`)\n if (body.url) lines.push(`URL: ${body.url}`)\n break\n }\n\n default:\n lines.push(`Headers: ${JSON.stringify(Object.keys(headers).filter(k => k.startsWith(\"x-\")).slice(0, 5))}`)\n lines.push(`Payload keys: ${Object.keys(body).slice(0, 10).join(\", \")}`)\n // Include raw body summary for unknown sources\n const raw = JSON.stringify(body).slice(0, 500)\n lines.push(`Body: ${raw}`)\n }\n\n return lines.join(\"\\n\")\n }\n\n private async readBody(req: IncomingMessage): Promise<Record<string, unknown>> {\n return new Promise((resolve) => {\n let body = \"\"\n req.on(\"data\", (chunk: Buffer) => (body += chunk.toString()))\n req.on(\"end\", () => {\n try { resolve(body ? JSON.parse(body) : {}) }\n catch { resolve({ raw: body }) }\n })\n req.on(\"error\", () => resolve({}))\n })\n }\n\n private sendJson(res: ServerResponse, status: number, data: unknown): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\" })\n res.end(JSON.stringify(data, null, 2))\n }\n}\n","import type { DaemonConfig, AgentDef } from \"@/daemon/config\"\n\n// --- Landscape Builder ---\n//\n// Generates a cached \"world model\" string for each agent.\n// Built once at daemon startup from config + mesh discovery.\n// Gives every agent awareness of the full system: team, channels, rules.\n\nexport interface MeshPeerInfo {\n peer: string\n healthy: boolean\n skills: Array<{ id: string; name: string; description: string }>\n}\n\n/**\n * Build and cache per-agent landscape strings.\n */\nexport class LandscapeBuilder {\n private cache: Map<string, string> = new Map()\n private config: DaemonConfig\n private meshPeers: MeshPeerInfo[] = []\n\n constructor(config: DaemonConfig) {\n this.config = config\n }\n\n /** Fingerprint of the peer set that produced the current cache. A rebuild\n * is skipped when `refresh()` gets a topologically-identical set — this\n * is the common case on every mesh heartbeat and rebuilding would\n * needlessly invalidate every agent's cached prompt prefix. */\n private fingerprint = \"\"\n\n /**\n * Build landscape strings for all agents. Call at startup.\n */\n build(meshPeers?: MeshPeerInfo[]): void {\n if (meshPeers) this.meshPeers = meshPeers\n this.cache.clear()\n this.fingerprint = this.computeFingerprint()\n\n for (const agentId of Object.keys(this.config.agents)) {\n this.cache.set(agentId, this.renderForAgent(agentId))\n }\n }\n\n /** Set-only signature of peers + their skill IDs. Health is excluded on\n * purpose — we don't want a flapping heartbeat to rebuild the cache. */\n private computeFingerprint(): string {\n const parts = [...this.meshPeers]\n .sort((a, b) => a.peer.localeCompare(b.peer))\n .map(p => `${p.peer}:${p.skills.map(s => s.id).sort().join(\",\")}`)\n return parts.join(\"|\")\n }\n\n /**\n * Get the cached landscape string for a specific agent.\n */\n getForAgent(agentId: string): string | undefined {\n return this.cache.get(agentId)\n }\n\n /**\n * Rebuild after mesh topology changes. Skipped when the peer set is\n * unchanged so a flapping health heartbeat doesn't invalidate every\n * agent's cached prompt prefix.\n */\n refresh(meshPeers: MeshPeerInfo[]): void {\n this.meshPeers = meshPeers\n const next = this.computeFingerprint()\n if (next === this.fingerprint && this.cache.size > 0) return\n this.build()\n }\n\n private renderForAgent(selfId: string): string {\n const lines: string[] = [\"[Landscape]\"]\n const selfDef = this.config.agents[selfId]\n\n // Node identity\n lines.push(`Node: ${this.config.node.name} (${this.config.node.id})`)\n\n // Self\n const selfHandle = this.getPrimaryHandle(selfId)\n const selfCap = this.extractCapability(selfDef)\n const handleStr = selfHandle ? ` (${selfHandle})` : \"\"\n lines.push(`You: ${selfDef.name}${handleStr} — ${selfCap}`)\n lines.push(\"\")\n\n // Local team\n const localAgents = Object.entries(this.config.agents).filter(([id]) => id !== selfId)\n lines.push(`Available agents on this node (${localAgents.length} — not all may be in the current group):`)\n for (const [id, def] of localAgents) {\n const handle = this.getPrimaryHandle(id)\n const cap = this.extractCapability(def)\n const h = handle ? ` (${handle})` : \"\"\n lines.push(`• ${def.name}${h} — ${cap}`)\n }\n\n // Remote mesh agents. Deliberately NOT filtered by `healthy` — a transient\n // heartbeat flap would otherwise rebuild the landscape and bust every\n // agent's prompt cache. Listing a peer that's momentarily unreachable\n // costs nothing (the A2A call will fail cleanly and the agent will\n // surface that to the user); filtering them out costs us a cache-create\n // on every flap. Health shows up in /health and /live, not here.\n const remotePeers = this.meshPeers.filter(p => p.skills.length > 0)\n if (remotePeers.length) {\n lines.push(\"Remote:\")\n // Stable ordering so the peer list doesn't shuffle between builds and\n // invalidate the cache even when the set is unchanged.\n const sortedPeers = [...remotePeers].sort((a, b) => a.peer.localeCompare(b.peer))\n for (const peer of sortedPeers) {\n const sortedSkills = [...peer.skills].sort((a, b) => a.id.localeCompare(b.id))\n for (const skill of sortedSkills) {\n lines.push(`• ${skill.name} [${peer.peer}] — ${skill.description.slice(0, 60)}`)\n }\n }\n }\n\n // Channels\n lines.push(\"\")\n lines.push(this.renderChannels())\n\n // Rules\n lines.push(\"\")\n lines.push(\"[Rules]\")\n const handles = [selfHandle, selfId].filter(Boolean).join(\" / \")\n lines.push(`- Only respond when YOU (${handles}) are mentioned or this is a DM to you`)\n lines.push(\"- If another agent was mentioned and you were NOT, stay silent — they will handle it\")\n lines.push(\"- The agent list above shows all agents on the node — do NOT assume they are all in the current chat group\")\n lines.push(\"- When asked about group members, only mention agents you have seen in the conversation history\")\n lines.push(\"- To delegate on Telegram: mention the agent's handle in your response\")\n lines.push(\"- On GitLab: reply directly, no Telegram handles. To send to other channels, use the /send API below\")\n lines.push(\"- On WhatsApp: mention another agent's handle to delegate (shared number — name prefixed automatically). To send to other channels, use /send API\")\n\n // Cross-channel outbound messaging\n const [, portStr] = this.config.node.bind.split(\":\")\n const port = portStr || \"19900\"\n lines.push(\"\")\n lines.push(\"[Cross-Channel Messaging]\")\n lines.push(`You can send messages to ANY channel proactively (not just reply):`)\n lines.push(` curl -X POST http://localhost:${port}/send -H \"Content-Type: application/json\" -d '{\"channel\":\"<channel>\",\"chatId\":\"<id>\",\"text\":\"<message>\"}'`)\n lines.push(\"Channels: telegram, whatsapp, gitlab, discord\")\n lines.push(\"ChatId formats:\")\n lines.push(' telegram: numeric (e.g. \"-1001234567890\" for group, \"123456\" for DM)')\n lines.push(' gitlab: \"group/project:issue:123\" or \"group/project:merge_request:45\"')\n lines.push(' whatsapp: JID (e.g. \"+21612345678@s.whatsapp.net\")')\n lines.push(\"Use this when asked to notify someone on a different channel, post to an issue, or broadcast updates.\")\n\n // Background monitoring capability\n lines.push(\"\")\n lines.push(\"[Background Monitoring]\")\n lines.push(\"You can watch things in the background while continuing to work:\")\n lines.push(\"- Tail log files and react to errors as they appear\")\n lines.push(\"- Poll CI/CD pipelines and report when status changes\")\n lines.push(\"- Watch directories for file changes\")\n lines.push(\"Use the Monitor tool for this — it runs a script in the background and streams output to you.\")\n\n // Agent Teams capability\n lines.push(\"\")\n lines.push(\"[Agent Teams]\")\n lines.push(\"For complex tasks that benefit from parallel work, you can spawn an agent team:\")\n lines.push(\"- Tell Claude Code to 'create an agent team' with specialized teammates\")\n lines.push(\"- Each teammate works independently with its own context window\")\n lines.push(\"- Teammates coordinate via shared task list and direct messaging\")\n lines.push(\"- Best for: code review (security + performance + tests), debugging with competing hypotheses, multi-module features\")\n lines.push(\"- Use teams when work can be parallelized. Use single session for sequential/simple tasks.\")\n\n return lines.join(\"\\n\")\n }\n\n private renderChannels(): string {\n const parts: string[] = []\n const ch = this.config.channels\n\n if (ch.telegram.enabled) {\n const pol = ch.telegram.policy\n parts.push(`telegram(groups:${pol.group}, DMs:${pol.dm}, delegation:yes)`)\n }\n if (ch.gitlab?.enabled) {\n parts.push(\"gitlab(webhooks, no delegation)\")\n }\n if (ch.whatsapp.enabled) {\n parts.push(\"whatsapp(DMs only, no delegation)\")\n }\n if (ch.discord?.enabled) {\n parts.push(\"discord(enabled)\")\n }\n\n return `Channels: ${parts.join(\", \")}`\n }\n\n /**\n * Get the primary Telegram handle for an agent (the @username).\n */\n private getPrimaryHandle(agentId: string): string | undefined {\n const def = this.config.agents[agentId]\n if (!def) return undefined\n return def.mentions.find(m => m.startsWith(\"@\"))\n }\n\n /**\n * Extract a short capability description from systemPrompt.\n * Takes the first sentence, max 80 chars.\n */\n private extractCapability(def: AgentDef): string {\n if (!def.systemPrompt) return def.name\n\n // Take first sentence (up to period, max 80 chars)\n const first = def.systemPrompt.split(/[.\\n]/)[0]?.trim() || def.name\n return first.length > 80 ? first.slice(0, 77) + \"...\" : first\n }\n}\n","import type { AgentRegistry } from \"./registry\"\nimport type { MemoryStore } from \"./memory-store\"\n\n// --- Heartbeat System ---\n//\n// Periodic check-in that runs WITHIN the active session context.\n// Unlike cron (which creates isolated sessions), heartbeat carries\n// the full conversation context — useful for inbox checks, health\n// monitoring, and proactive agent behavior.\n//\n// Configured per-agent in agentx.json:\n// agents.<id>.heartbeat: {\n// enabled: true,\n// intervalMinutes: 30,\n// prompt: \"Check inbox, pending tasks, and system health.\"\n// }\n\nexport interface HeartbeatConfig {\n enabled: boolean\n /** Interval in minutes between heartbeats (default: 30) */\n intervalMinutes: number\n /** Prompt to execute during heartbeat */\n prompt: string\n /** Channel to execute in (default: \"heartbeat\") */\n channel?: string\n}\n\ninterface HeartbeatState {\n agentId: string\n config: HeartbeatConfig\n timer?: ReturnType<typeof setInterval>\n lastRun?: Date\n runCount: number\n errors: number\n}\n\n/**\n * Manages periodic heartbeat runs for agents.\n * Heartbeats run in the agent's existing session context,\n * preserving conversation history and memory.\n */\nexport class HeartbeatManager {\n private registry: AgentRegistry\n private memoryStore?: MemoryStore\n private states: Map<string, HeartbeatState> = new Map()\n private log: (...args: unknown[]) => void\n\n constructor(\n registry: AgentRegistry,\n log: (...args: unknown[]) => void = console.error.bind(console, \"[heartbeat]\"),\n memoryStore?: MemoryStore,\n ) {\n this.registry = registry\n this.log = log\n this.memoryStore = memoryStore\n }\n\n /**\n * Register a heartbeat for an agent.\n */\n register(agentId: string, config: HeartbeatConfig): void {\n if (!config.enabled) return\n\n // Clear existing timer if re-registering\n this.stop(agentId)\n\n const state: HeartbeatState = {\n agentId,\n config,\n runCount: 0,\n errors: 0,\n }\n\n const intervalMs = (config.intervalMinutes || 30) * 60 * 1000\n\n state.timer = setInterval(() => {\n this.runHeartbeat(state).catch((e) => {\n this.log(`[${agentId}] heartbeat error: ${e.message}`)\n state.errors++\n })\n }, intervalMs)\n\n this.states.set(agentId, state)\n this.log(`Registered heartbeat for \"${agentId}\" every ${config.intervalMinutes}min`)\n }\n\n /**\n * Stop heartbeat for an agent.\n */\n stop(agentId: string): void {\n const state = this.states.get(agentId)\n if (state?.timer) {\n clearInterval(state.timer)\n state.timer = undefined\n }\n this.states.delete(agentId)\n }\n\n /**\n * Stop all heartbeats.\n */\n stopAll(): void {\n for (const [id] of this.states) {\n this.stop(id)\n }\n }\n\n /**\n * Run a single heartbeat for an agent.\n */\n private async runHeartbeat(state: HeartbeatState): Promise<void> {\n const { agentId, config } = state\n\n this.log(`[${agentId}] heartbeat running...`)\n state.lastRun = new Date()\n state.runCount++\n\n const channel = config.channel || \"heartbeat\"\n\n try {\n // Inject memory recall context if memories are due for review\n let prompt = config.prompt\n if (this.memoryStore) {\n const recallContext = this.memoryStore.buildRecallContext(agentId)\n if (recallContext) {\n prompt = `${recallContext}\\n\\n${prompt}`\n }\n }\n\n const response = await this.registry.execute({\n message: prompt,\n agentId,\n context: {\n channel,\n sender: \"system:heartbeat\",\n chatId: `heartbeat:${agentId}`,\n },\n })\n\n if (response.error) {\n this.log(`[${agentId}] heartbeat error: ${response.error}`)\n state.errors++\n } else {\n this.log(\n `[${agentId}] heartbeat completed (${response.duration}ms, ` +\n `${response.content.length} chars)`,\n )\n }\n } catch (error: any) {\n this.log(`[${agentId}] heartbeat failed: ${error.message}`)\n state.errors++\n }\n }\n\n /**\n * Manually trigger a heartbeat for an agent.\n */\n async trigger(agentId: string): Promise<void> {\n const state = this.states.get(agentId)\n if (!state) {\n this.log(`[${agentId}] no heartbeat registered`)\n return\n }\n await this.runHeartbeat(state)\n }\n\n /**\n * List all registered heartbeats and their status.\n */\n list(): Array<{\n agentId: string\n intervalMinutes: number\n lastRun?: Date\n runCount: number\n errors: number\n }> {\n return Array.from(this.states.values()).map((s) => ({\n agentId: s.agentId,\n intervalMinutes: s.config.intervalMinutes,\n lastRun: s.lastRun,\n runCount: s.runCount,\n errors: s.errors,\n }))\n }\n}\n","import type { AgentRegistry } from \"@/agents/registry\"\nimport { debug } from \"@/observability/debug\"\n\n// --- Service Matcher: pattern-based interception for automated services ---\n//\n// Sits before agent routing in the message flow. If an incoming message\n// matches a defined service trigger, the service prompt is executed\n// instead of routing to the default agent.\n//\n// Use case: predictable client requests (\"send me the monthly report\")\n// that don't need the full LLM reasoning — just execute a known prompt.\n\nexport interface ServiceTrigger {\n pattern: string\n channel?: string // limit to specific channel (e.g. \"whatsapp\")\n}\n\nexport interface ServiceNotify {\n channel: string\n chatId: string\n accountId?: string\n}\n\nexport interface ServiceDef {\n id: string\n name: string\n triggers: ServiceTrigger[]\n allowedContacts?: string[]\n agent: string\n prompt: string\n schedule?: string\n timezone?: string\n notify?: ServiceNotify\n}\n\ninterface CompiledService {\n def: ServiceDef\n patterns: Array<{ regex: RegExp; channel?: string }>\n}\n\nexport interface ServiceMatch {\n service: ServiceDef\n trigger: string // which pattern matched\n captured: string // what text matched\n}\n\nexport class ServiceMatcher {\n private services: CompiledService[] = []\n private log: (...args: unknown[]) => void\n\n constructor(\n serviceDefs: Record<string, Omit<ServiceDef, \"id\">>,\n log: (...args: unknown[]) => void = console.error.bind(console, \"[services]\"),\n ) {\n this.log = log\n\n for (const [id, def] of Object.entries(serviceDefs)) {\n const compiled: CompiledService = {\n def: { ...def, id },\n patterns: [],\n }\n\n for (const trigger of def.triggers) {\n try {\n compiled.patterns.push({\n regex: new RegExp(trigger.pattern, \"i\"),\n channel: trigger.channel,\n })\n } catch (e: any) {\n this.log(`Invalid regex in service \"${id}\": ${trigger.pattern} — ${e.message}`)\n }\n }\n\n if (compiled.patterns.length > 0) {\n this.services.push(compiled)\n }\n }\n\n this.log(`${this.services.length} service(s) loaded`)\n }\n\n /**\n * Match an incoming message against all service triggers.\n * Returns the first matching service or null.\n */\n match(text: string, senderId: string, channel: string): ServiceMatch | null {\n for (const svc of this.services) {\n // Check contact allowlist (if set)\n if (svc.def.allowedContacts?.length) {\n const senderNorm = senderId.replace(/[^0-9]/g, \"\")\n const allowed = svc.def.allowedContacts.some(c => {\n const contactNorm = c.replace(/[^0-9]/g, \"\")\n return senderNorm.includes(contactNorm) || contactNorm.includes(senderNorm)\n })\n if (!allowed) continue\n }\n\n // Check triggers\n for (const pattern of svc.patterns) {\n // Channel filter\n if (pattern.channel && pattern.channel !== channel) continue\n\n const match = text.match(pattern.regex)\n if (match) {\n debug.cat(\"agent\", `Service match: \"${svc.def.name}\" (trigger: ${pattern.regex.source})`)\n return {\n service: svc.def,\n trigger: pattern.regex.source,\n captured: match[0],\n }\n }\n }\n }\n\n return null\n }\n\n /**\n * Execute a matched service.\n * Sends the service's predefined prompt to the agent, returns the response.\n */\n async execute(\n service: ServiceDef,\n registry: AgentRegistry,\n context: { channel: string; sender: string; chatId: string },\n replyFn: (text: string) => Promise<void>,\n ): Promise<void> {\n this.log(`Executing service \"${service.name}\" -> agent \"${service.agent}\"`)\n\n try {\n const response = await registry.execute({\n message: service.prompt,\n agentId: service.agent,\n context: {\n channel: context.channel,\n sender: context.sender,\n chatId: `service:${service.id}:${context.chatId}`,\n },\n })\n\n if (response.error) {\n this.log(`Service \"${service.name}\" failed: ${response.error}`)\n await replyFn(`Service \"${service.name}\" encountered an error: ${response.error}`)\n return\n }\n\n // Reply on the same channel\n if (response.content) {\n await replyFn(response.content)\n }\n\n this.log(`Service \"${service.name}\" completed in ${response.duration}ms`)\n } catch (e: any) {\n this.log(`Service \"${service.name}\" threw: ${e.message}`)\n await replyFn(`Service error: ${e.message}`)\n }\n }\n\n /**\n * Get all services that have a schedule (for cron registration).\n */\n getScheduledServices(): ServiceDef[] {\n return this.services\n .filter(s => s.def.schedule)\n .map(s => s.def)\n }\n\n /**\n * List all registered services.\n */\n list(): Array<{ id: string; name: string; triggers: number; agent: string; scheduled: boolean }> {\n return this.services.map(s => ({\n id: s.def.id,\n name: s.def.name,\n triggers: s.patterns.length,\n agent: s.def.agent,\n scheduled: !!s.def.schedule,\n }))\n }\n}\n","import type { BusinessConfig, BusinessOrgEntry, BusinessRole } from \"./config\"\n\n// --- Organization: org-chart queries and validation ---\n\nexport interface EmployeeProfile {\n agentId: string\n role: BusinessRole\n roleKey: string\n reportsTo?: string\n schedule: BusinessOrgEntry[\"schedule\"]\n utilizationTarget: number\n}\n\nexport class Organization {\n private employees: Map<string, EmployeeProfile> = new Map()\n\n constructor(config: BusinessConfig) {\n for (const [agentId, entry] of Object.entries(config.orgChart)) {\n const role = config.roles[entry.role]\n if (!role) {\n throw new Error(`Org chart references unknown role \"${entry.role}\" for agent \"${agentId}\"`)\n }\n this.employees.set(agentId, {\n agentId,\n role,\n roleKey: entry.role,\n reportsTo: entry.reportsTo,\n schedule: entry.schedule,\n utilizationTarget: entry.utilizationTarget,\n })\n }\n\n // Validate reportsTo refs\n for (const emp of this.employees.values()) {\n if (emp.reportsTo && !this.employees.has(emp.reportsTo)) {\n throw new Error(`Agent \"${emp.agentId}\" reports to unknown agent \"${emp.reportsTo}\"`)\n }\n }\n\n // Cycle detection\n for (const emp of this.employees.values()) {\n const seen = new Set<string>()\n let cur: string | undefined = emp.agentId\n while (cur) {\n if (seen.has(cur)) {\n throw new Error(`Reporting cycle detected involving \"${emp.agentId}\"`)\n }\n seen.add(cur)\n cur = this.employees.get(cur)?.reportsTo\n }\n }\n }\n\n get(agentId: string): EmployeeProfile | undefined {\n return this.employees.get(agentId)\n }\n\n all(): EmployeeProfile[] {\n return [...this.employees.values()]\n }\n\n reportsTo(agentId: string): string | undefined {\n return this.employees.get(agentId)?.reportsTo\n }\n\n directReports(agentId: string): string[] {\n return [...this.employees.values()]\n .filter((e) => e.reportsTo === agentId)\n .map((e) => e.agentId)\n }\n\n /** Walk up the org tree to the root. First element is `agentId` itself. */\n chainOfCommand(agentId: string): string[] {\n const chain: string[] = []\n let cur: string | undefined = agentId\n while (cur && !chain.includes(cur)) {\n chain.push(cur)\n cur = this.employees.get(cur)?.reportsTo\n }\n return chain\n }\n}\n","import type { BusinessConfig, BusinessSchedule } from \"./config\"\nimport type { Organization } from \"./organization\"\n\n// --- Schedule: pure functions over org chart work hours ---\n\nconst DAY_MAP: Record<string, number> = {\n sun: 0, mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6,\n}\n\n/** Get {hour, minute, dayOfWeek (0-6, sun=0)} in the business timezone for `when`. */\nfunction tzParts(when: Date, timezone: string): { hour: number; minute: number; dow: number } {\n // Intl.DateTimeFormat with timeZone gives us the wall-clock time in that zone.\n const fmt = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timezone,\n weekday: \"short\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false,\n })\n const parts = fmt.formatToParts(when)\n const weekday = (parts.find((p) => p.type === \"weekday\")?.value || \"sun\").toLowerCase().slice(0, 3)\n const hourStr = parts.find((p) => p.type === \"hour\")?.value || \"00\"\n const minStr = parts.find((p) => p.type === \"minute\")?.value || \"00\"\n // \"24\" can appear in en-US locale for midnight — normalize.\n const hour = parseInt(hourStr, 10) % 24\n return {\n hour,\n minute: parseInt(minStr, 10),\n dow: DAY_MAP[weekday] ?? 0,\n }\n}\n\nfunction parseHHMM(s: string): number {\n const [h, m] = s.split(\":\").map(Number)\n return h * 60 + m\n}\n\nfunction worksOnDay(schedule: BusinessSchedule, dow: number): boolean {\n return schedule.days.some((d) => DAY_MAP[d] === dow)\n}\n\n/** Is `schedule` active at `when`? (Respects business timezone and lunch break.) */\nexport function isScheduleActive(schedule: BusinessSchedule, when: Date, timezone: string): boolean {\n const { hour, minute, dow } = tzParts(when, timezone)\n if (!worksOnDay(schedule, dow)) return false\n const nowMin = hour * 60 + minute\n const startMin = parseHHMM(schedule.start)\n const endMin = parseHHMM(schedule.end)\n if (nowMin < startMin || nowMin >= endMin) return false\n if (schedule.lunch) {\n const lStart = parseHHMM(schedule.lunch.start)\n const lEnd = parseHHMM(schedule.lunch.end)\n if (nowMin >= lStart && nowMin < lEnd) return false\n }\n return true\n}\n\nexport class Schedule {\n constructor(\n private org: Organization,\n private config: BusinessConfig,\n ) {}\n\n isOnClock(agentId: string, when: Date = new Date()): boolean {\n const emp = this.org.get(agentId)\n if (!emp) return false\n return isScheduleActive(emp.schedule, when, this.config.timezone)\n }\n\n /** Is `when` exactly (to the minute) the scheduled day-start for this agent? */\n isDayStart(agentId: string, when: Date = new Date()): boolean {\n const emp = this.org.get(agentId)\n if (!emp) return false\n const { hour, minute, dow } = tzParts(when, this.config.timezone)\n if (!worksOnDay(emp.schedule, dow)) return false\n const startMin = parseHHMM(emp.schedule.start)\n return hour * 60 + minute === startMin\n }\n\n /** Is `when` exactly the scheduled day-end for this agent? */\n isDayEnd(agentId: string, when: Date = new Date()): boolean {\n const emp = this.org.get(agentId)\n if (!emp) return false\n const { hour, minute, dow } = tzParts(when, this.config.timezone)\n if (!worksOnDay(emp.schedule, dow)) return false\n const endMin = parseHHMM(emp.schedule.end)\n return hour * 60 + minute === endMin\n }\n\n clockedInAgents(when: Date = new Date()): string[] {\n return this.org.all()\n .filter((e) => this.isOnClock(e.agentId, when))\n .map((e) => e.agentId)\n }\n}\n","import type { MessageRouter } from \"@/channels/router\"\nimport type { BusinessConfig } from \"./config\"\nimport type { Organization } from \"./organization\"\n\n// --- Reporter: posts to main channel + escalates blockers up the org chart ---\n\nexport class Reporter {\n constructor(\n private router: MessageRouter,\n private org: Organization,\n private config: BusinessConfig,\n private log: (...args: unknown[]) => void,\n ) {}\n\n async postToMain(text: string, agentId?: string): Promise<void> {\n const dest = this.config.mainChannel\n try {\n await this.router.sendOutbound({\n channel: dest.channel,\n chatId: dest.chatId,\n text,\n agentId,\n accountId: dest.accountId,\n })\n } catch (e: any) {\n this.log(`[business] postToMain failed: ${e.message}`)\n }\n }\n\n /** Post an escalation message up the chain of command. */\n async escalate(fromAgent: string, subject: string, detail: string): Promise<void> {\n const chain = this.org.chainOfCommand(fromAgent).slice(1) // drop self\n const targets = chain.length ? chain.slice(0, 1) : [] // first direct manager\n const body = `⚠️ **Escalation from ${fromAgent}** — ${subject}\\n\\n${detail}`\n\n if (!targets.length) {\n // No manager → fall back to main channel\n await this.postToMain(body, fromAgent)\n return\n }\n\n // Post to main channel tagging the manager (they watch the main channel).\n await this.postToMain(`${body}\\n\\ncc: ${targets.map((t) => `@${t}`).join(\" \")}`, fromAgent)\n }\n}\n","import { writeFileSync, existsSync, mkdirSync, readFileSync } from \"fs\"\nimport { resolve } from \"path\"\nimport type { Organization } from \"./organization\"\nimport type { Schedule } from \"./schedule\"\n\n// --- KPI: running utilization + daily summary ---\n\nexport interface DailyStats {\n date: string // YYYY-MM-DD\n perAgent: Record<string, {\n tasksCompleted: number\n timeLoggedSeconds: number\n onClockSeconds: number\n blockers: number\n utilization: number // timeLogged / onClockSeconds (capped at 1)\n }>\n}\n\nexport interface TaskCompletion {\n agentId: string\n durationSeconds: number\n success: boolean\n channel?: string\n}\n\nfunction today(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\nexport class KPI {\n private stats: DailyStats\n private dir: string\n private onClockTick: Map<string, number> = new Map()\n\n constructor(\n private org: Organization,\n private schedule: Schedule,\n private log: (...args: unknown[]) => void,\n ) {\n this.dir = resolve(process.cwd(), \".agentx/kpi\")\n if (!existsSync(this.dir)) mkdirSync(this.dir, { recursive: true })\n this.stats = this.loadOrInit()\n }\n\n private loadOrInit(): DailyStats {\n const f = resolve(this.dir, `${today()}.json`)\n if (existsSync(f)) {\n try { return JSON.parse(readFileSync(f, \"utf-8\")) } catch { /* fall through */ }\n }\n const perAgent: DailyStats[\"perAgent\"] = {}\n for (const emp of this.org.all()) {\n perAgent[emp.agentId] = {\n tasksCompleted: 0,\n timeLoggedSeconds: 0,\n onClockSeconds: 0,\n blockers: 0,\n utilization: 0,\n }\n }\n return { date: today(), perAgent }\n }\n\n /** Called by the day-cycle minute tick to accumulate on-clock time. */\n tick(): void {\n const now = Date.now()\n for (const emp of this.org.all()) {\n if (!this.schedule.isOnClock(emp.agentId)) {\n this.onClockTick.delete(emp.agentId)\n continue\n }\n const prev = this.onClockTick.get(emp.agentId)\n if (prev) {\n const delta = Math.min(now - prev, 120_000) / 1000 // cap at 2 min in case of lag\n const row = this.stats.perAgent[emp.agentId]\n if (row) row.onClockSeconds += delta\n }\n this.onClockTick.set(emp.agentId, now)\n }\n this.recomputeUtil()\n this.persist()\n }\n\n recordTaskCompletion(t: TaskCompletion): void {\n const row = this.stats.perAgent[t.agentId]\n if (!row) return\n row.tasksCompleted += 1\n row.timeLoggedSeconds += t.durationSeconds\n this.recomputeUtil()\n this.persist()\n }\n\n recordBlocker(agentId: string): void {\n const row = this.stats.perAgent[agentId]\n if (!row) return\n row.blockers += 1\n this.persist()\n }\n\n private recomputeUtil(): void {\n for (const row of Object.values(this.stats.perAgent)) {\n row.utilization = row.onClockSeconds > 0\n ? Math.min(1, row.timeLoggedSeconds / row.onClockSeconds)\n : 0\n }\n }\n\n private persist(): void {\n // Rotate if day changed\n if (this.stats.date !== today()) {\n this.flush()\n this.stats = this.loadOrInit()\n }\n try {\n writeFileSync(resolve(this.dir, `${this.stats.date}.json`), JSON.stringify(this.stats, null, 2))\n } catch (e: any) {\n this.log(`[business] KPI persist failed: ${e.message}`)\n }\n }\n\n flush(): void {\n this.persist()\n }\n\n snapshot(): DailyStats {\n return JSON.parse(JSON.stringify(this.stats))\n }\n\n /** Markdown summary for end-of-day main-channel post. */\n dailySummaryText(): string {\n const lines: string[] = [`📊 **Daily report — ${this.stats.date}**`, \"\"]\n let totalLogged = 0\n let totalOnClock = 0\n for (const [agentId, row] of Object.entries(this.stats.perAgent)) {\n totalLogged += row.timeLoggedSeconds\n totalOnClock += row.onClockSeconds\n const hrs = (row.timeLoggedSeconds / 3600).toFixed(1)\n const util = (row.utilization * 100).toFixed(0)\n lines.push(`• **${agentId}** — ${row.tasksCompleted} tasks, ${hrs}h logged, ${util}% utilization${row.blockers ? `, ${row.blockers} blocker(s)` : \"\"}`)\n }\n lines.push(\"\")\n const overallUtil = totalOnClock > 0 ? ((totalLogged / totalOnClock) * 100).toFixed(0) : \"0\"\n lines.push(`**Team:** ${(totalLogged / 3600).toFixed(1)}h logged / ${(totalOnClock / 3600).toFixed(1)}h on-clock → ${overallUtil}% utilization`)\n return lines.join(\"\\n\")\n }\n}\n","import type { IncomingMessage, ServerResponse } from \"http\"\nimport type { WorkSource } from \"./work-pool\"\nimport type { Organization } from \"./organization\"\nimport type { Schedule } from \"./schedule\"\nimport type { Reporter } from \"./reporter\"\nimport type { KPI } from \"./kpi\"\n\n// --- Business HTTP endpoints ---\n// Agents running in their Claude Code workspaces reach the daemon over HTTP\n// (localhost:18800 by default). These endpoints expose the work pool,\n// reporter, and KPI snapshot so an agent's lifecycle prompts can call them.\n\nexport interface BusinessHttpDeps {\n workSource: WorkSource\n org: Organization\n schedule: Schedule\n reporter: Reporter\n kpi: KPI\n}\n\nfunction json(res: ServerResponse, status: number, body: unknown): void {\n res.statusCode = status\n res.setHeader(\"Content-Type\", \"application/json\")\n res.end(JSON.stringify(body))\n}\n\nasync function readJson(req: IncomingMessage): Promise<Record<string, unknown>> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = []\n req.on(\"data\", (c) => chunks.push(c as Buffer))\n req.on(\"end\", () => {\n const raw = Buffer.concat(chunks).toString(\"utf-8\")\n if (!raw) return resolve({})\n try { resolve(JSON.parse(raw)) } catch (e) { reject(e) }\n })\n req.on(\"error\", reject)\n })\n}\n\n/**\n * Try to handle a business HTTP request. Returns true if handled.\n * Call from the daemon's handleHttp() before the default case.\n */\nexport async function handleBusinessHttp(\n route: string, // \"METHOD PATH\"\n req: IncomingMessage,\n res: ServerResponse,\n deps: BusinessHttpDeps,\n): Promise<boolean> {\n // GET /business/status — full snapshot\n if (route === \"GET /business/status\") {\n const now = new Date()\n const onClock = deps.schedule.clockedInAgents(now)\n json(res, 200, {\n time: now.toISOString(),\n onClock,\n employees: deps.org.all().map((e) => ({\n agentId: e.agentId,\n role: e.role.title,\n reportsTo: e.reportsTo,\n onClock: onClock.includes(e.agentId),\n })),\n kpi: deps.kpi.snapshot(),\n })\n return true\n }\n\n // GET /business/work?agent=<id>\n if (route.startsWith(\"GET /business/work\")) {\n const url = new URL(req.url || \"/\", \"http://x\")\n const agentId = url.searchParams.get(\"agent\")\n if (!agentId) { json(res, 400, { error: \"?agent= required\" }); return true }\n try {\n const items = await deps.workSource.listOpen(agentId)\n json(res, 200, { items })\n } catch (e: any) {\n json(res, 500, { error: e.message })\n }\n return true\n }\n\n // POST /business/claim { agent, itemId }\n if (route === \"POST /business/claim\") {\n const body = await readJson(req)\n const { agent, itemId } = body as { agent?: string; itemId?: string }\n if (!agent || !itemId) { json(res, 400, { error: \"agent and itemId required\" }); return true }\n try {\n await deps.workSource.claim(agent, itemId)\n json(res, 200, { ok: true })\n } catch (e: any) {\n json(res, 500, { error: e.message })\n }\n return true\n }\n\n // POST /business/report { itemId, status, note?, timeSeconds?, blocker? }\n if (route === \"POST /business/report\") {\n const body = await readJson(req) as {\n itemId?: string; status?: string; note?: string; timeSeconds?: number; blocker?: string\n }\n if (!body.itemId || !body.status) { json(res, 400, { error: \"itemId and status required\" }); return true }\n if (![\"in-progress\", \"done\", \"blocked\"].includes(body.status)) {\n json(res, 400, { error: \"status must be in-progress | done | blocked\" }); return true\n }\n try {\n await deps.workSource.report(body.itemId, {\n status: body.status as any,\n note: body.note,\n timeSeconds: body.timeSeconds,\n blocker: body.blocker,\n })\n json(res, 200, { ok: true })\n } catch (e: any) {\n json(res, 500, { error: e.message })\n }\n return true\n }\n\n // POST /business/escalate { agent, subject, detail }\n if (route === \"POST /business/escalate\") {\n const body = await readJson(req) as { agent?: string; subject?: string; detail?: string }\n if (!body.agent || !body.subject) { json(res, 400, { error: \"agent and subject required\" }); return true }\n await deps.reporter.escalate(body.agent, body.subject, body.detail || \"\")\n deps.kpi.recordBlocker(body.agent)\n json(res, 200, { ok: true })\n return true\n }\n\n // POST /business/post { agent, text }\n if (route === \"POST /business/post\") {\n const body = await readJson(req) as { agent?: string; text?: string }\n if (!body.text) { json(res, 400, { error: \"text required\" }); return true }\n await deps.reporter.postToMain(body.text, body.agent)\n json(res, 200, { ok: true })\n return true\n }\n\n return false\n}\n\n/**\n * Human-readable tool documentation, injected into agent workspace CLAUDE.md\n * when the business layer is enabled. Agents use `curl` to call these.\n */\nexport function businessToolsDoc(daemonBase: string): string {\n return `## Business Layer Tools\n\nDuring business hours you'll receive lifecycle prompts ([STANDUP], [WORK], [WRAP]).\nUse these HTTP endpoints on the local daemon to act on them.\n\n- **List your work:**\n \\`curl -s ${daemonBase}/business/work?agent=<your-id>\\`\n- **Claim an item** (optional — some sources no-op):\n \\`curl -s -X POST ${daemonBase}/business/claim -d '{\"agent\":\"<your-id>\",\"itemId\":\"<id>\"}'\\`\n- **Report progress or completion:**\n \\`curl -s -X POST ${daemonBase}/business/report \\\\\n -d '{\"itemId\":\"<id>\",\"status\":\"done\",\"timeSeconds\":1800,\"note\":\"shipped\"}'\\`\n status: \\`in-progress\\` | \\`done\\` | \\`blocked\\`\n- **Escalate a blocker** up the org chart:\n \\`curl -s -X POST ${daemonBase}/business/escalate \\\\\n -d '{\"agent\":\"<your-id>\",\"subject\":\"short\",\"detail\":\"why it's stuck\"}'\\`\n- **Post to the main channel** (daily plans, reports):\n \\`curl -s -X POST ${daemonBase}/business/post \\\\\n -d '{\"agent\":\"<your-id>\",\"text\":\"your update\"}'\\`\n\nYour org chart, role, and schedule are fixed in agentx.json under \\`business.orgChart\\`.\n`\n}\n","import type { AgentRegistry } from \"@/agents/registry\"\nimport type { DaemonConfig } from \"@/daemon/config\"\nimport type { BusinessConfig } from \"./config\"\nimport type { Organization } from \"./organization\"\nimport type { Schedule } from \"./schedule\"\nimport type { Reporter } from \"./reporter\"\nimport type { KPI } from \"./kpi\"\nimport type { WorkSource } from \"./work-pool\"\nimport { businessToolsDoc } from \"./http\"\n\n// --- Day cycle: minute ticker that fires standup / work-tick / wrap prompts ---\n\nconst TICK_MS = 60_000 // one minute\n\ninterface AgentDayState {\n standupFiredAt?: number // Date.now when standup was triggered today\n wrapFiredAt?: number\n lastWorkTickAt?: number\n hadWorkToday?: boolean // to avoid wrap if agent did nothing\n}\n\nexport class DayCycle {\n private timer?: ReturnType<typeof setInterval>\n private day: Map<string, AgentDayState> = new Map()\n private currentDate: string = new Date().toISOString().slice(0, 10)\n private runningTask: Set<string> = new Set() // agentIds with an in-flight day-cycle call\n\n constructor(\n private config: BusinessConfig,\n private org: Organization,\n private schedule: Schedule,\n private registry: AgentRegistry,\n private reporter: Reporter,\n private kpi: KPI,\n private workSource: WorkSource,\n private daemonBase: string,\n private log: (...args: unknown[]) => void,\n ) {\n for (const emp of org.all()) this.day.set(emp.agentId, {})\n }\n\n start(): void {\n this.log(\"[business] day cycle starting\")\n // Tick immediately then every minute\n this.tick().catch((e) => this.log(`[business] tick error: ${e.message}`))\n this.timer = setInterval(() => {\n this.tick().catch((e) => this.log(`[business] tick error: ${e.message}`))\n }, TICK_MS)\n }\n\n stop(): void {\n if (this.timer) clearInterval(this.timer)\n this.timer = undefined\n this.kpi.flush()\n }\n\n private async tick(): Promise<void> {\n const now = new Date()\n const iso = now.toISOString().slice(0, 10)\n\n // Day rollover: reset per-agent day state\n if (iso !== this.currentDate) {\n this.currentDate = iso\n for (const key of this.day.keys()) this.day.set(key, {})\n }\n\n // KPI on-clock accrual\n this.kpi.tick()\n\n for (const emp of this.org.all()) {\n const state = this.day.get(emp.agentId) || {}\n this.day.set(emp.agentId, state)\n\n // Never fire multiple day-cycle tasks in parallel for the same agent\n if (this.runningTask.has(emp.agentId)) continue\n\n // 1. Morning standup — exactly at day-start minute\n if (this.schedule.isDayStart(emp.agentId, now) && !state.standupFiredAt) {\n state.standupFiredAt = Date.now()\n this.fireStandup(emp.agentId).catch((e) => this.log(`[business] standup err: ${e.message}`))\n continue\n }\n\n // 2. End-of-day wrap — exactly at day-end minute\n if (this.schedule.isDayEnd(emp.agentId, now) && !state.wrapFiredAt) {\n state.wrapFiredAt = Date.now()\n this.fireWrap(emp.agentId).catch((e) => this.log(`[business] wrap err: ${e.message}`))\n continue\n }\n\n // 3. Work tick — during business hours at workTickMinutes cadence\n if (this.schedule.isOnClock(emp.agentId, now)) {\n const elapsed = state.lastWorkTickAt ? (Date.now() - state.lastWorkTickAt) / 60_000 : Infinity\n if (elapsed >= this.config.workTickMinutes) {\n state.lastWorkTickAt = Date.now()\n this.fireWorkTick(emp.agentId).catch((e) => this.log(`[business] work-tick err: ${e.message}`))\n }\n }\n }\n }\n\n private roleContext(agentId: string): string {\n const emp = this.org.get(agentId)\n if (!emp) return \"\"\n const manager = emp.reportsTo ? `reporting to ${emp.reportsTo}` : \"(no manager — top of org chart)\"\n const responsibilities = emp.role.responsibilities.length\n ? `\\nResponsibilities:\\n${emp.role.responsibilities.map((r) => `- ${r}`).join(\"\\n\")}`\n : \"\"\n const sop = emp.role.sopPath ? `\\nSOP: @${emp.role.sopPath}` : \"\"\n return `You are ${agentId}, role: ${emp.role.title} ${manager}.${responsibilities}${sop}\\n\\n${businessToolsDoc(this.daemonBase)}`\n }\n\n private async runLifecycle(agentId: string, prompt: string): Promise<string | undefined> {\n if (this.runningTask.has(agentId)) return\n this.runningTask.add(agentId)\n const start = Date.now()\n try {\n const resp = await this.registry.execute({\n message: prompt,\n agentId,\n context: { channel: \"business\", chatId: `day-cycle:${this.currentDate}:${agentId}` },\n })\n const seconds = Math.round((resp.duration ?? (Date.now() - start)) / 1000)\n if (!resp.error) {\n this.kpi.recordTaskCompletion({ agentId, durationSeconds: seconds, success: true, channel: \"business\" })\n }\n return resp.content\n } finally {\n this.runningTask.delete(agentId)\n }\n }\n\n private async fireStandup(agentId: string): Promise<void> {\n this.log(`[business] STANDUP → ${agentId}`)\n const role = this.roleContext(agentId)\n const prompt =\n`${role}\n\n[STANDUP] It's the start of your work day.\n1. Call GET /business/work?agent=${agentId} to see your open work items.\n2. Pick your top 3 for today, in priority order.\n3. Post your plan to the main channel via POST /business/post — keep it to 4-6 short lines.\n4. Flag any blockers via POST /business/escalate.`\n const content = await this.runLifecycle(agentId, prompt)\n if (content) {\n const state = this.day.get(agentId) || {}\n state.hadWorkToday = true\n this.day.set(agentId, state)\n }\n }\n\n private async fireWorkTick(agentId: string): Promise<void> {\n const items = await this.workSource.listOpen(agentId).catch(() => [])\n if (!items.length) return // nothing assigned → skip (don't fake work)\n\n this.log(`[business] WORK-TICK → ${agentId} (${items.length} open)`)\n const role = this.roleContext(agentId)\n const prompt =\n`${role}\n\n[WORK] Continue your highest-priority open task.\n- Call GET /business/work?agent=${agentId} to see what's assigned to you.\n- If you complete one, POST /business/report with status:done and the time you spent.\n- If blocked, POST /business/escalate with a concise blocker description.\n- Then move on to the next item. Keep working until you hit a real stopping point — don't idle.`\n const content = await this.runLifecycle(agentId, prompt)\n if (content) {\n const state = this.day.get(agentId) || {}\n state.hadWorkToday = true\n this.day.set(agentId, state)\n }\n }\n\n private async fireWrap(agentId: string): Promise<void> {\n const state = this.day.get(agentId) || {}\n if (!state.hadWorkToday) {\n this.log(`[business] WRAP skipped for ${agentId} (no work done today)`)\n return\n }\n this.log(`[business] WRAP → ${agentId}`)\n const role = this.roleContext(agentId)\n const prompt =\n`${role}\n\n[WRAP] End of work day. Post a concise daily report to the main channel via POST /business/post:\n- Tasks closed today\n- Time logged (total)\n- Carry-overs to tomorrow\n- Any outstanding blockers\nKeep it under 8 lines.`\n await this.runLifecycle(agentId, prompt)\n }\n}\n","import type { AgentRegistry } from \"@/agents/registry\"\nimport type { MessageRouter } from \"@/channels/router\"\nimport type { DaemonConfig } from \"@/daemon/config\"\nimport type { BusinessConfig } from \"./config\"\nimport { Organization } from \"./organization\"\nimport { Schedule } from \"./schedule\"\nimport { createWorkSource, type WorkSource } from \"./work-pool\"\nimport { Reporter } from \"./reporter\"\nimport { KPI } from \"./kpi\"\nimport { DayCycle } from \"./day-cycle\"\nimport { handleBusinessHttp, type BusinessHttpDeps } from \"./http\"\nimport type { IncomingMessage, ServerResponse } from \"http\"\n\nexport { businessToolsDoc } from \"./http\"\nexport type { BusinessConfig } from \"./config\"\n\n/**\n * Business layer facade — constructed once by the daemon when\n * config.business?.enabled is true.\n */\nexport class BusinessLayer {\n public readonly org: Organization\n public readonly schedule: Schedule\n public readonly workSource: WorkSource\n public readonly reporter: Reporter\n public readonly kpi: KPI\n public readonly dayCycle: DayCycle\n\n constructor(\n business: BusinessConfig,\n daemon: DaemonConfig,\n registry: AgentRegistry,\n router: MessageRouter,\n log: (...args: unknown[]) => void,\n ) {\n this.org = new Organization(business)\n this.schedule = new Schedule(this.org, business)\n this.workSource = createWorkSource(business, daemon, log)\n this.reporter = new Reporter(router, this.org, business, log)\n this.kpi = new KPI(this.org, this.schedule, log)\n\n // Prefer loopback URL for local agents; external agents can override via env.\n const [host, port] = daemon.node.bind.split(\":\")\n const daemonBase = `http://${host === \"0.0.0.0\" ? \"127.0.0.1\" : host}:${port || \"18800\"}`\n\n this.dayCycle = new DayCycle(\n business, this.org, this.schedule, registry, this.reporter, this.kpi, this.workSource, daemonBase, log,\n )\n }\n\n start(): void {\n this.dayCycle.start()\n }\n\n stop(): void {\n this.dayCycle.stop()\n }\n\n /** Record a task completion (called by router when an agent finishes a task). */\n recordTaskCompletion(agentId: string, durationSeconds: number, success: boolean, channel?: string): void {\n this.kpi.recordTaskCompletion({ agentId, durationSeconds, success, channel })\n }\n\n async handleHttp(route: string, req: IncomingMessage, res: ServerResponse): Promise<boolean> {\n const deps: BusinessHttpDeps = {\n workSource: this.workSource,\n org: this.org,\n schedule: this.schedule,\n reporter: this.reporter,\n kpi: this.kpi,\n }\n return handleBusinessHttp(route, req, res, deps)\n }\n\n /** Short startup banner line. */\n summary(): string {\n const onClock = this.schedule.clockedInAgents().length\n return `Business layer: ${this.org.all().length} employees, ${onClock} on-clock now`\n }\n}\n","import path from \"path\"\nimport fs from \"fs-extra\"\nimport { type PackageJson } from \"type-fest\"\n\n/**\n * Read the agentx-cli package.json so commands like --version work.\n *\n * Looks in three places, in order:\n * 1. A package.json beside the compiled entry (dist/cli.js → ../package.json),\n * which is how npm-installed usage finds it.\n * 2. A package.json in the CWD, which is what `pnpm dev` / `tsx src/cli.ts`\n * implicitly relies on.\n * 3. A safe fallback so the CLI never crashes on a truly bare environment\n * (e.g. a fresh user running `agentx setup` from an empty temp dir).\n */\nexport function getPackageInfo(): PackageJson {\n const candidates: string[] = []\n\n // npm / tsup bundled output puts cli.js in dist/; package.json is one dir up.\n // __dirname is dist/ at runtime, so resolve relative to that.\n try {\n candidates.push(path.resolve(__dirname, \"..\", \"package.json\"))\n candidates.push(path.resolve(__dirname, \"package.json\"))\n } catch { /* __dirname may be undefined in exotic setups */ }\n\n candidates.push(path.resolve(process.cwd(), \"package.json\"))\n\n for (const p of candidates) {\n try {\n if (fs.existsSync(p)) {\n const pkg = fs.readJSONSync(p) as PackageJson\n if (pkg && pkg.name) return pkg\n }\n } catch { /* try next candidate */ }\n }\n\n // Minimal fallback — lets --version and help output still work.\n return { name: \"agentix-cli\", version: \"0.0.0\" } as PackageJson\n}\n"],"mappings":"+YAIO,IAAMA,EAAN,KAAgB,CACb,QACA,MAER,YAAYC,EAAiBC,EAAgB,CAC3C,KAAK,QAAUD,EAAQ,QAAQ,MAAO,EAAE,EACxC,KAAK,MAAQC,CACf,CAKA,MAAM,cAAmC,CACvC,IAAMC,EAAM,MAAM,MAAM,GAAG,KAAK,sCAAuC,CACrE,QAAS,KAAK,QAAQ,CACxB,CAAC,EACD,GAAI,CAACA,EAAI,GAAI,MAAM,IAAI,MAAM,+BAA+BA,EAAI,QAAQ,EACxE,OAAOA,EAAI,KAAK,CAClB,CAKA,MAAM,SACJC,EACAC,EACe,CACf,IAAMC,EAAW,MAAM,KAAK,IAAI,aAAc,CAC5C,GAAI,QAAQ,KAAK,IAAI,EAAE,SAAS,EAAE,IAClC,QAAS,CACP,KAAM,OACN,MAAO,CAAC,CAAE,KAAM,OAAQ,KAAAF,CAAK,CAAC,CAChC,EACA,SAAAC,CACF,CAAC,EAED,GAAIC,EAAS,MACX,MAAM,IAAI,MAAM,cAAcA,EAAS,MAAM,SAAS,EAGxD,OAAOA,EAAS,MAClB,CAKA,MAAO,eACLF,EACAC,EACoE,CACpE,IAAME,EAAO,KAAK,UAAU,CAC1B,QAAS,MACT,GAAI,EACJ,OAAQ,sBACR,OAAQ,CACN,GAAI,QAAQ,KAAK,IAAI,EAAE,SAAS,EAAE,IAClC,QAAS,CACP,KAAM,OACN,MAAO,CAAC,CAAE,KAAM,OAAQ,KAAAH,CAAK,CAAC,CAChC,EACA,SAAAC,CACF,CACF,CAAC,EAEKF,EAAM,MAAM,MAAM,KAAK,QAAS,CACpC,OAAQ,OACR,QAAS,CACP,GAAG,KAAK,QAAQ,EAChB,eAAgB,mBAChB,OAAU,mBACZ,EACA,KAAAI,CACF,CAAC,EAED,GAAI,CAACJ,EAAI,IAAM,CAACA,EAAI,KAClB,MAAM,IAAI,MAAM,qBAAqBA,EAAI,QAAQ,EAGnD,IAAMK,EAASL,EAAI,KAAK,UAAU,EAC5BM,EAAU,IAAI,YAChBC,EAAS,GAEb,OAAa,CACX,GAAM,CAAE,KAAAC,EAAM,MAAAC,CAAM,EAAI,MAAMJ,EAAO,KAAK,EAC1C,GAAIG,EAAM,MAEVD,GAAUD,EAAQ,OAAOG,EAAO,CAAE,OAAQ,EAAK,CAAC,EAChD,IAAMC,EAAQH,EAAO,MAAM;AAAA,CAAI,EAC/BA,EAASG,EAAM,IAAI,GAAK,GAExB,QAAWC,KAAQD,EACjB,GAAIC,EAAK,WAAW,QAAQ,EAC1B,GAAI,CACF,IAAMC,EAAO,KAAK,MAAMD,EAAK,MAAM,CAAC,CAAC,EACrC,KAAM,CACJ,MAAOC,EAAK,MACZ,QAASA,EAAK,SAAS,QAAQ,CAAC,GAAG,KACnC,MAAOA,EAAK,KACd,CACF,MAAE,CAEF,EAIR,CAKA,MAAM,QAAQC,EAA+B,CAC3C,IAAMV,EAAW,MAAM,KAAK,IAAI,YAAa,CAAE,GAAIU,CAAO,CAAC,EAC3D,GAAIV,EAAS,MACX,MAAM,IAAI,MAAM,cAAcA,EAAS,MAAM,SAAS,EAExD,OAAOA,EAAS,MAClB,CAKA,MAAM,WAAWU,EAA+B,CAC9C,IAAMV,EAAW,MAAM,KAAK,IAAI,eAAgB,CAAE,GAAIU,CAAO,CAAC,EAC9D,GAAIV,EAAS,MACX,MAAM,IAAI,MAAM,cAAcA,EAAS,MAAM,SAAS,EAExD,OAAOA,EAAS,MAClB,CAEA,MAAc,IAAIW,EAAgBC,EAA2D,CAC3F,IAAMf,EAAM,MAAM,MAAM,KAAK,QAAS,CACpC,OAAQ,OACR,QAAS,CACP,GAAG,KAAK,QAAQ,EAChB,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAU,CACnB,QAAS,MACT,GAAI,KAAK,IAAI,EACb,OAAAc,EACA,OAAAC,CACF,CAAC,CACH,CAAC,EAED,GAAI,CAACf,EAAI,GACP,MAAM,IAAI,MAAM,mBAAmBA,EAAI,QAAQ,EAGjD,OAAOA,EAAI,KAAK,CAClB,CAEQ,SAAkC,CACxC,IAAMgB,EAA4B,CAAC,EACnC,OAAI,KAAK,QACPA,EAAE,cAAmB,UAAU,KAAK,SAE/BA,CACT,CACF,ECnJO,IAAMC,EAAN,KAAc,CACX,MAAgC,IAAI,IACpC,YACA,OACA,IAER,YACEC,EACAC,EAAoC,QAAQ,MAAM,KAAK,QAAS,QAAQ,EACxE,CACA,KAAK,OAASD,EACd,KAAK,IAAMC,EAEX,QAAWC,KAAQF,EAAO,KAAK,MAC7B,KAAK,MAAM,IAAIE,EAAK,KAAM,CACxB,KAAAA,EACA,OAAQ,IAAIC,EAAUD,EAAK,IAAKA,EAAK,KAAK,EAC1C,QAAS,GACT,OAAQ,CAAC,CACX,CAAC,CAEL,CAKA,MAAM,OAAuB,CAC3B,KAAK,IAAI,sBAAsB,KAAK,MAAM,cAAc,EAGxD,MAAM,KAAK,YAAY,EAGvB,IAAME,EAAW,KAAK,OAAO,KAAK,YAAY,SAAW,IACzD,KAAK,YAAc,YAAY,IAAM,KAAK,YAAY,EAAGA,CAAQ,CACnE,CAEA,MAAM,MAAsB,CACtB,KAAK,aACP,cAAc,KAAK,WAAW,CAElC,CAKA,MAAM,aAA6B,CACjC,IAAMC,EAAU,MAAM,QAAQ,WAC5B,MAAM,KAAK,KAAK,MAAM,QAAQ,CAAC,EAAE,IAAI,CAAC,CAACC,EAAMC,CAAK,IAChD,KAAK,aAAaD,EAAMC,CAAK,CAC/B,CACF,EAEMC,EAAU,MAAM,KAAK,KAAK,MAAM,OAAO,CAAC,EAAE,OAAQC,GAAMA,EAAE,OAAO,EAAE,OACzE,KAAK,IAAI,uBAAuBD,KAAW,KAAK,MAAM,oBAAoB,CAC5E,CAEA,MAAc,aAAaF,EAAcC,EAAiC,CACxE,IAAMG,EAAU,KAAK,OAAO,KAAK,YAAY,QAAU,IAEvD,GAAI,CACF,IAAMC,EAAa,IAAI,gBACjBC,EAAQ,WAAW,IAAMD,EAAW,MAAM,EAAGD,CAAO,EAEpDG,EAAO,MAAMN,EAAM,OAAO,aAAa,EAC7C,aAAaK,CAAK,EAElBL,EAAM,QAAU,GAChBA,EAAM,UAAY,IAAI,KACtBA,EAAM,UAAYM,EAClBN,EAAM,OAASM,EAAK,QAAU,CAAC,EAE/B,KAAK,IAAI,SAASP,eAAkBO,EAAK,SAASN,EAAM,OAAO,gBAAgB,CACjF,OAASO,EAAP,CACAP,EAAM,QAAU,GAChBA,EAAM,UAAY,IAAI,KACtB,KAAK,IAAI,SAASD,mBAAsBQ,EAAE,SAAS,CACrD,CACF,CAOA,MAAM,SAASC,EAAkBC,EAAcC,EAAmC,CAChF,IAAMV,EAAQ,KAAK,MAAM,IAAIQ,CAAQ,EACrC,GAAI,CAACR,EAAO,MAAM,IAAI,MAAM,iBAAiBQ,GAAU,EACvD,GAAI,CAACR,EAAM,QAAS,MAAM,IAAI,MAAM,SAASQ,mBAA0B,EAGvE,IAAMG,EAAQD,GAAWV,EAAM,OAAO,CAAC,GAAG,GAC1C,GAAI,CAACW,EAAO,MAAM,IAAI,MAAM,SAASH,kBAAyB,EAE9D,IAAMI,EAAM,GAAGZ,EAAM,KAAK,WACpBa,EAAkC,CAAE,eAAgB,kBAAmB,EACzEb,EAAM,KAAK,QACba,EAAQ,cAAmB,UAAUb,EAAM,KAAK,SAGlD,IAAMc,EAAM,MAAM,MAAMF,EAAK,CAC3B,OAAQ,OACR,QAAAC,EACA,KAAM,KAAK,UAAU,CAAE,MAAAF,EAAO,QAASF,CAAK,CAAC,CAC/C,CAAC,EAED,GAAI,CAACK,EAAI,GACP,MAAM,IAAI,MAAM,SAASN,mBAA0BM,EAAI,QAAQ,EAGjE,IAAMC,EAAO,MAAMD,EAAI,KAAK,EAC5B,GAAIC,EAAK,MAAO,MAAM,IAAI,MAAM,SAASP,mBAA0BO,EAAK,OAAO,EAC/E,OAAOA,EAAK,SAAW,aACzB,CAKA,kBAAkBC,EAAwC,CACxD,QAAWhB,KAAS,KAAK,MAAM,OAAO,EACpC,GAAIA,EAAM,SAAWA,EAAM,OAAO,KAAMiB,GAAMA,EAAE,KAAOD,CAAO,EAC5D,OAAOhB,CAIb,CAKA,WAMG,CACD,OAAO,MAAM,KAAK,KAAK,MAAM,QAAQ,CAAC,EAAE,IAAI,CAAC,CAACD,EAAMC,CAAK,KAAO,CAC9D,KAAMD,EACN,QAASC,EAAM,KAAK,IACpB,QAASA,EAAM,QACf,OAAQA,EAAM,OACd,UAAWA,EAAM,SACnB,EAAE,CACJ,CACF,EChKA,OAAS,gBAAAkB,GAAc,iBAAAC,GAAe,cAAAC,GAAY,aAAAC,OAAiB,KACnE,OAAS,WAAAC,OAAe,OCOjB,SAASC,EAAUC,EAAcC,EAAkBC,EAAmB,SAAa,CACxF,GAAI,CAACF,EAAM,MAAO,GAClB,GAAIA,EAAK,QAAUC,EAAU,OAAOD,EAEpC,IAAMG,EAASF,EAAWC,EAAS,OACnC,GAAIC,GAAU,EAAG,OAAOH,EAAK,MAAM,EAAGC,CAAQ,EAE9C,IAAMG,EAAQJ,EAAK,MAAM,EAAGG,CAAM,EAI5BE,EAAKD,EAAM,YAAY;AAAA,CAAI,EACjC,GAAIC,EAAKF,EAAS,GAAK,OAAOC,EAAM,MAAM,EAAGC,CAAE,EAAI;AAAA,EAAOH,EAG1D,IAAMI,EAAK,KAAK,IAAIF,EAAM,YAAY,GAAG,EAAGA,EAAM,YAAY,GAAI,CAAC,EACnE,OAAIE,EAAKH,EAAS,GAAYC,EAAM,MAAM,EAAGE,CAAE,EAAIJ,EAE5CE,EAAQF,CACjB,CAMO,SAASK,GAAWP,EAAcQ,EAAkBP,EAA0B,CACnF,GAAI,CAACD,EAAM,MAAO,GAElB,IAAMS,EADQT,EAAK,MAAM;AAAA,CAAI,EACR,MAAM,EAAGQ,CAAQ,EAAE,KAAK;AAAA,CAAI,EACjD,OAAOT,EAAUU,EAAQR,CAAQ,CACnC,CDxBA,IAAMS,GAAc,GACdC,GAAoB,IACpBC,GAAkB,KAKlBC,GAAmB,IAKzB,SAASC,GAAYC,EAAoB,CACvC,IAAMC,EAAI,IAAI,KAAKD,CAAE,EACfE,EAAKD,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAG,GAAG,EAC/CE,EAAK,KAAK,MAAMF,EAAE,cAAc,EAAI,EAAE,EAAI,GAChD,MAAO,GAAGC,KAAMC,EAAG,SAAS,EAAE,SAAS,EAAG,GAAG,GAC/C,CAEO,IAAMC,EAAN,KAAe,CACZ,IACA,MAAsC,IAAI,IAElD,YAAYC,EAAkBC,GAAQ,QAAQ,IAAI,EAAG,gBAAgB,EAAG,CACtE,KAAK,IAAMD,EACXE,GAAU,KAAK,IAAK,CAAE,UAAW,EAAK,CAAC,CACzC,CAKA,IAAIC,EAAgBC,EAAgBC,EAAoB,CACtD,IAAMC,EAAM,KAAK,KAAKH,CAAM,EACtBI,EAAM,KAAK,IAAI,EACfC,EAAaC,EAAUJ,EAAMb,EAAe,EAM5CkB,EAAOJ,EAAIA,EAAI,OAAS,CAAC,EAC/B,GAAI,EAAAI,GAAQA,EAAK,SAAWN,GAAUM,EAAK,OAASF,GAAcD,EAAMG,EAAK,UAAYjB,IAOzF,KAHAa,EAAI,KAAK,CAAE,OAAAF,EAAQ,KAAMI,EAAY,UAAWD,CAAI,CAAC,EAG9CD,EAAI,OAAShB,IAAagB,EAAI,MAAM,EAE3C,KAAK,MAAM,IAAIH,EAAQG,CAAG,EAC1B,KAAK,KAAKH,EAAQG,CAAG,EACvB,CAMA,aAAaH,EAAwB,CACnC,IAAMG,EAAM,KAAK,KAAKH,CAAM,EAC5B,GAAIG,EAAI,QAAU,EAAG,MAAO,GAE5B,IAAMK,EAAkB,CAAC,6BAA6B,EAClDC,EAAQ,EAQRC,EAAmE,KACjEC,EAAqB,CAAC,EACxBC,EAAa,GACXC,EAAe,IAAe,CAClC,GAAI,CAACH,EAAS,MAAO,GACrB,IAAMI,EAAMJ,EAAQ,OAAS,EAAI,GAAGA,EAAQ,cAAWA,EAAQ,UAAYA,EAAQ,KACnF,GAAID,EAAQK,EAAI,OAAS1B,GAAmB,MAAO,GAKnD,GAJAuB,EAAS,KAAKG,CAAG,EACjBL,GAASK,EAAI,OAGTJ,EAAQ,SAAWE,EAAY,CACjC,IAAMG,EAAM,UAAKL,EAAQ,gBACzB,GAAID,EAAQM,EAAI,OAAS3B,GAAqB,OAAAsB,EAAU,KAAa,GACrEC,EAAS,KAAKI,CAAG,EACjBN,GAASM,EAAI,OACbH,EAAaF,EAAQ,OAEvB,OAAAA,EAAU,KACH,EACT,EAEA,QAASM,EAAIb,EAAI,OAAS,EAAGa,GAAK,EAAGA,IAAK,CACxC,IAAMC,EAAQd,EAAIa,CAAC,EACbE,EAAO,GAAGD,EAAM,WAAWA,EAAM,OACjCE,EAAS5B,GAAY0B,EAAM,SAAS,EAC1C,GAAIP,GAAWA,EAAQ,OAASQ,GAAQR,EAAQ,SAAWS,EAAQ,CACjET,EAAQ,SACR,SAEF,GAAI,CAACG,EAAa,EAAG,MACrBH,EAAU,CAAE,KAAAQ,EAAM,OAAQ,EAAG,OAAAC,CAAO,EAEtCN,EAAa,EAGb,QAASG,EAAIL,EAAS,OAAS,EAAGK,GAAK,EAAGA,IAAKR,EAAM,OAAO,EAAG,EAAGG,EAASK,CAAC,CAAC,EAE7E,OAAIR,EAAM,QAAU,EAAU,IAC9BA,EAAM,KAAK,4DAAuD,EAC3DA,EAAM,KAAK;AAAA,CAAI,EACxB,CAKA,WAAWR,EAAiC,CAC1C,MAAO,CAAC,GAAG,KAAK,KAAKA,CAAM,CAAC,CAC9B,CAIQ,SAASA,EAAwB,CACvC,IAAMoB,EAAOpB,EAAO,QAAQ,kBAAmB,GAAG,EAClD,OAAOF,GAAQ,KAAK,IAAK,GAAGsB,QAAW,CACzC,CAEQ,KAAKpB,EAAiC,CAE5C,GAAI,KAAK,MAAM,IAAIA,CAAM,EAAG,OAAO,KAAK,MAAM,IAAIA,CAAM,EAGxD,IAAMqB,EAAO,KAAK,SAASrB,CAAM,EACjC,GAAI,CAACsB,GAAWD,CAAI,EAClB,YAAK,MAAM,IAAIrB,EAAQ,CAAC,CAAC,EAClB,CAAC,EAGV,GAAI,CACF,IAAMuB,EAAO,KAAK,MAAMC,GAAaH,EAAM,OAAO,CAAC,EAC7CI,EAAU,MAAM,QAAQF,CAAI,EAAIA,EAAO,CAAC,EAC9C,YAAK,MAAM,IAAIvB,EAAQyB,CAAO,EACvBA,CACT,MAAE,CACA,YAAK,MAAM,IAAIzB,EAAQ,CAAC,CAAC,EAClB,CAAC,CACV,CACF,CAEQ,KAAKA,EAAgByB,EAAgC,CAC3D,GAAI,CACFC,GAAc,KAAK,SAAS1B,CAAM,EAAG,KAAK,UAAUyB,CAAO,CAAC,CAC9D,MAAE,CAEF,CACF,CACF,EEjJA,IAAME,GAAoC,CACxC,SAAU,GACV,SAAU,IACV,YAAa,IACb,cAAe,KACf,YAAa,EACf,EAGaC,GAA+D,CAC1E,SAAU,CAAE,SAAU,GAAI,SAAU,KAAM,cAAe,IAAK,EAC9D,SAAU,CAAE,SAAU,GAAI,SAAU,IAAM,cAAe,IAAM,YAAa,EAAK,EACjF,QAAS,CAAE,SAAU,GAAI,SAAU,IAAM,cAAe,GAAK,EAC7D,OAAQ,CAAE,SAAU,IAAK,SAAU,IAAO,cAAe,GAAK,CAChE,EAQaC,EAAN,KAAkB,CACf,OACA,OAAS,GACT,QACA,aAAe,EACf,UACA,gBAAkB,GAClB,aAAe,OASf,aAA8B,QAAQ,QAAQ,EAEtD,YAAYC,EAAuBC,EAAqCC,EAAkB,CACxF,IAAMC,EAAkBD,EAAUJ,GAAiBI,CAAO,EAAI,CAAC,EAC/D,KAAK,OAAS,CAAE,GAAGL,GAAgB,GAAGM,EAAiB,GAAGF,CAAO,EACjE,KAAK,QAAUD,CACjB,CAKA,KAAKI,EAAqB,CACxB,KAAK,QAAUA,EAGX,KAAK,WACP,aAAa,KAAK,SAAS,EAI7B,KAAK,qBAAqBA,CAAK,EAG3B,KAAK,WAAW,EAClB,KAAK,KAAK,EAGV,KAAK,UAAY,WAAW,IAAM,CAC5B,KAAK,OAAO,OAAS,GACvB,KAAK,KAAK,CAEd,EAAG,KAAK,OAAO,WAAW,CAE9B,CAOA,MAAM,OAAuB,CACvB,KAAK,YACP,aAAa,KAAK,SAAS,EAC3B,KAAK,UAAY,QAEf,KAAK,OAAO,OAAS,IACvB,KAAK,cAAc,KAAK,MAAM,EAC9B,KAAK,OAAS,IAEhB,MAAM,KAAK,YACb,CAKA,IAAI,eAAwB,CAC1B,OAAO,KAAK,OAAO,MACrB,CAEQ,YAAsB,CAS5B,OAPI,KAAK,iBAGL,KAAK,OAAO,OAAS,KAAK,OAAO,UAGrB,KAAK,IAAI,EAAI,KAAK,aACpB,KAAK,OAAO,cAAsB,GAGzC,KAAK,eAAe,EAAI,CACjC,CAEQ,MAAa,CACnB,IAAMC,EAAa,KAAK,eAAe,EACvC,GAAIA,GAAc,GAAK,KAAK,OAAO,OAAS,KAAK,OAAO,SAAU,OAElE,IAAMC,EAAQD,EAAa,EACvBA,EACA,KAAK,IAAI,KAAK,OAAO,OAAQ,KAAK,OAAO,QAAQ,EAE/CE,EAAQ,KAAK,OAAO,MAAM,EAAGD,CAAK,EAGxC,GAFA,KAAK,OAAS,KAAK,OAAO,MAAMA,CAAK,EAEjC,KAAK,OAAO,YAAa,CAC3B,IAAME,EAAQ,IAAM,KAAK,OAAO,EAAI,KACpC,WAAW,IAAM,KAAK,cAAcD,CAAK,EAAGC,CAAK,OAEjD,KAAK,cAAcD,CAAK,CAE5B,CAEQ,cAAcA,EAAqB,CACzC,KAAK,aAAe,KAAK,IAAI,EAI7B,KAAK,aAAe,KAAK,aAAa,KAAK,IACzC,QAAQ,QAAQ,EACb,KAAK,IAAM,KAAK,QAAQA,CAAK,CAAC,EAC9B,MAAM,IAAM,CAAoB,CAAC,CACtC,CACF,CAOQ,gBAAyB,CAC/B,IAAME,EAAM,KAAK,OACXC,EAAM,KAAK,IAAID,EAAI,OAAQ,KAAK,OAAO,QAAQ,EAG/CE,EAAUF,EAAI,YAAY;AAAA;AAAA,EAAQC,CAAG,EAC3C,GAAIC,GAAW,KAAK,OAAO,SAAU,OAAOA,EAAU,EAGtD,IAAMC,EAAQH,EAAI,YAAY;AAAA,EAAMC,CAAG,EACvC,GAAIE,GAAS,KAAK,OAAO,SAAU,OAAOA,EAAQ,EAGlD,QAAS,EAAIF,EAAM,EAAG,GAAK,KAAK,OAAO,SAAU,IAC/C,IAAKD,EAAI,CAAC,IAAM,KAAOA,EAAI,CAAC,IAAM,KAAOA,EAAI,CAAC,IAAM,OAC/C,EAAI,GAAKA,EAAI,QAAUA,EAAI,EAAI,CAAC,IAAM,KAAOA,EAAI,EAAI,CAAC,IAAM;AAAA,GAC/D,OAAO,EAAI,EAKf,GAAIA,EAAI,QAAU,KAAK,OAAO,SAAU,CAEtC,IAAMI,EAAWJ,EAAI,YAAY,IAAKC,CAAG,EACzC,OAAIG,GAAY,KAAK,OAAO,SAAiBA,EAAW,EACjDH,EAGT,MAAO,EACT,CAMQ,qBAAqBN,EAAqB,CAChD,IAAMU,EAAQV,EAAM,MAAM;AAAA,CAAI,EAC9B,QAAWW,KAAQD,EACb,KAAK,aAAa,KAAKC,EAAK,KAAK,CAAC,IACpC,KAAK,gBAAkB,CAAC,KAAK,gBAGnC,CACF,EC7MA,OAAS,gBAAAC,GAAc,iBAAAC,GAAe,cAAAC,GAAY,aAAAC,GAAW,kBAAAC,OAAsB,KACnF,OAAS,WAAAC,GAAS,WAAAC,OAAe,OAgCjC,IAAMC,GAAN,KAAkB,CACR,SAER,YAAYC,EAAiB,CAC3B,KAAK,SAAWC,GAAQD,EAAS,+BAA+B,EAChEE,GAAUC,GAAQ,KAAK,QAAQ,EAAG,CAAE,UAAW,EAAK,CAAC,CACvD,CAEA,MAAMC,EAAiD,CACrD,GAAI,CACF,IAAMC,EAAO,KAAK,UAAU,CAAE,KAAM,QAAS,GAAI,KAAK,IAAI,EAAG,GAAGD,CAAM,CAAC,EAAI;AAAA,EAC3EE,GAAe,KAAK,SAAUD,CAAI,CACpC,MAAE,CAAsC,CAC1C,CAEA,KAAKE,EAAkB,CACrB,GAAI,CACFD,GAAe,KAAK,SAAU,KAAK,UAAU,CAAE,KAAM,OAAQ,GAAAC,EAAI,GAAI,KAAK,IAAI,CAAE,CAAC,EAAI;AAAA,CAAI,CAC3F,MAAE,CAAc,CAClB,CAGA,gBAAkC,CAChC,GAAI,CAACC,GAAW,KAAK,QAAQ,EAAG,MAAO,CAAC,EACxC,GAAI,CACF,IAAMC,EAAMC,GAAa,KAAK,SAAU,OAAO,EACzCC,EAAO,IAAI,IACjB,QAAWN,KAAQI,EAAI,MAAM;AAAA,CAAI,EAC/B,GAAKJ,EAAK,KAAK,EACf,GAAI,CACF,IAAMD,EAAQ,KAAK,MAAMC,CAAI,EACzBD,EAAM,OAAS,QAASO,EAAK,IAAIP,EAAM,GAAIA,CAAsB,EAC5DA,EAAM,OAAS,QAAQO,EAAK,OAAOP,EAAM,EAAE,CACtD,MAAE,CAAgC,CAEpC,MAAO,CAAC,GAAGO,EAAK,OAAO,CAAC,CAC1B,MAAE,CAAQ,MAAO,CAAC,CAAE,CACtB,CAIA,SAAgB,CACd,GAAI,CACF,IAAMC,EAAa,KAAK,eAAe,EACjCC,EAAMD,EAAW,IAAKE,GAAM,KAAK,UAAUA,CAAC,CAAC,EAAE,KAAK;AAAA,CAAI,GAAKF,EAAW,OAAS;AAAA,EAAO,IAC9FG,GAAc,KAAK,SAAUF,CAAG,CAClC,MAAE,CAAc,CAClB,CACF,EAWA,IAAMG,GAAqB,IAEdC,GAAN,KAAoB,CACjB,SACA,OACA,SAAwC,IAAI,IAC5C,MACA,KACA,eACA,SACA,SAIA,cACA,IAGR,kBAAkC,CAAE,OAAO,KAAK,aAAc,CAsBtD,iBAAwC,IAAI,IACnC,kBAAoB,EAAI,GAAK,IAC7B,uBAAyB,IACzB,iBAAmB,4BAC5B,gBAAkB,EAClB,eAGA,SAER,YACEC,EACAC,EACAC,EACAC,EAAoC,QAAQ,MAAM,KAAK,QAAS,UAAU,EAC1E,CACA,KAAK,SAAWH,EAChB,KAAK,OAASC,EACd,KAAK,MAAQC,EACb,KAAK,IAAMC,EACX,KAAK,SAAW,IAAIC,EACpB,KAAK,cAAgB,IAAIC,GAAc,CAAE,IAAK,IAAIC,IAAMH,EAAI,aAAc,GAAGG,CAAC,CAAE,CAAC,EACjF,KAAK,SAAW,IAAIC,GAAY,QAAQ,IAAI,CAAC,EAC7C,KAAK,kBAAkB,CACzB,CAEQ,mBAA0B,CAChC,GAAI,CACF,IAAMC,EAAIC,GAAQ,QAAQ,IAAI,EAAG,KAAK,gBAAgB,EACtD,GAAI,CAACC,GAAWF,CAAC,EAAG,OACpB,IAAMG,EAAM,KAAK,MAAMC,GAAaJ,EAAG,OAAO,CAAC,EACzCK,EAAM,KAAK,IAAI,EACjBC,EAAW,EACf,OAAW,CAACC,EAAKC,CAAE,IAAK,OAAO,QAAQL,CAAG,EACpC,OAAOK,GAAO,UAAYH,EAAMG,EAAK,KAAK,oBAC5C,KAAK,iBAAiB,IAAID,EAAKC,CAAE,EACjCF,KAGAA,EAAW,GACb,KAAK,IAAI,0BAA0BA,gCAAuC,CAE9E,MAAE,CAEF,CACF,CAEQ,mBAA0B,CAKhC,GAJA,KAAK,kBAID,KAAK,iBAAmB,GAAI,CAC9B,KAAK,iBAAiB,EACtB,OAEE,KAAK,iBACT,KAAK,eAAiB,WAAW,IAAM,KAAK,iBAAiB,EAAG,GAAI,EACtE,CAEQ,kBAAyB,CAC3B,KAAK,iBACP,aAAa,KAAK,cAAc,EAChC,KAAK,eAAiB,QAExB,KAAK,gBAAkB,EACvB,GAAI,CACF,IAAMD,EAAM,KAAK,IAAI,EACfI,EAA+B,CAAC,EACtC,OAAW,CAACF,EAAKC,CAAE,IAAK,KAAK,iBACvBH,EAAMG,EAAK,KAAK,oBAAmBC,EAAKF,CAAG,EAAIC,GAErD,IAAMR,EAAIC,GAAQ,QAAQ,IAAI,EAAG,KAAK,gBAAgB,EAChDS,EAAMC,GAAQX,CAAC,EAChBE,GAAWQ,CAAG,GAAGE,GAAUF,EAAK,CAAE,UAAW,EAAK,CAAC,EACxDG,GAAcb,EAAG,KAAK,UAAUS,CAAI,CAAC,CACvC,MAAE,CAGF,CACF,CAGA,kBAAyB,CACvB,KAAK,iBAAiB,EAKtB,KAAK,SAAS,QAAQ,CACxB,CAEA,QAAQK,EAAqB,CAC3B,KAAK,KAAOA,CACd,CAEA,kBAAkBC,EAA+B,CAC/C,KAAK,eAAiBA,CACxB,CAEA,YAAYC,EAA+B,CACzC,KAAK,SAAWA,CAClB,CAEA,WAAWC,EAA+B,CACxC,KAAK,SAAS,IAAIA,EAAQ,KAAMA,CAAO,EACvCA,EAAQ,UAAWC,GAAQ,KAAK,cAAcD,EAASC,CAAG,CAAC,CAC7D,CAQA,MAAM,aAAaA,EAAuE,CACxF,IAAMD,EAAU,KAAK,SAAS,IAAIC,EAAI,OAAO,EAC7C,GAAI,CAACD,EACH,MAAM,IAAI,MAAM,qBAAqBC,EAAI,wBAAwB,CAAC,GAAG,KAAK,SAAS,KAAK,CAAC,EAAE,KAAK,IAAI,GAAG,EAIzG,IAAIC,EAAYD,EAAI,UAOpB,OANID,EAAQ,OAAS,YAAc,CAACE,GAAaD,EAAI,UACnDC,EAAY,KAAK,mBAAmBD,EAAI,OAAO,GAGjD,KAAK,IAAI,aAAaA,EAAI,eAAeA,EAAI,WAAWA,EAAI,KAAK,MAAM,EAAG,EAAE,GAAG,EAE3ED,EAAQ,OAAS,YAAcE,EAC1B,KAAK,YAAYF,EAAS,CAAE,GAAGC,EAAK,UAAAC,CAAU,CAAC,EAGjDF,EAAQ,KAAKC,CAAG,CACzB,CAGA,iBAA4B,CAC1B,MAAO,CAAC,GAAG,KAAK,SAAS,KAAK,CAAC,CACjC,CAEA,MAAM,UAA0B,CAC9B,OAAW,CAACE,EAAMH,CAAO,IAAK,KAAK,SACjC,KAAK,IAAI,qBAAqBG,GAAM,EACpC,MAAMH,EAAQ,MAAM,EAKtB,KAAK,eAAe,EAAE,MAAO,GAAM,KAAK,IAAI,0BAA0B,EAAE,SAAS,CAAC,CACpF,CAOA,MAAc,gBAAgC,CAC5C,IAAMI,EAAa,KAAK,SAAS,eAAe,EAChD,GAAIA,EAAW,SAAW,EAC1B,MAAK,IAAI,oBAAoBA,EAAW,4CAA4C,EACpF,QAAWC,KAASD,EAAY,CAC9B,IAAMJ,EAAU,KAAK,SAAS,IAAIK,EAAM,OAAO,EAC/C,GAAI,CAACL,EAAS,CACZ,KAAK,IAAI,6BAA6BK,EAAM,yCAAyCA,EAAM,IAAI,EAG/F,KAAK,SAAS,KAAKA,EAAM,EAAE,EAC3B,SAEF,IAAMJ,EAAuB,CAC3B,GAAII,EAAM,GACV,QAASA,EAAM,QACf,UAAWA,EAAM,UACjB,OAAQA,EAAM,OACd,MAAOA,EAAM,MACb,KAAMA,EAAM,KACZ,QAASA,EAAM,QACf,YAAaA,EAAM,YACnB,UAAW,IAAI,KAAKA,EAAM,SAAS,EACnC,cAAeA,EAAM,cACrB,WAAYA,EAAM,UACpB,EACA,KAAK,IAAI,oBAAoBA,EAAM,WAAWA,EAAM,aAAaA,EAAM,eAAiB,YAAY,EACpG,KAAK,cAAcL,EAASC,EAAK,CAAE,OAAQ,EAAK,CAAC,EAC9C,MAAOK,GAAM,KAAK,IAAI,8BAA8BD,EAAM,OAAOC,EAAE,SAAS,CAAC,EAM7E,QAAQ,IAAM,KAAK,SAAS,KAAKD,EAAM,EAAE,CAAC,EAI/C,KAAK,SAAS,QAAQ,EACxB,CAEA,MAAM,SAAyB,CAC7B,OAAW,CAACF,EAAMH,CAAO,IAAK,KAAK,SACjC,KAAK,IAAI,qBAAqBG,GAAM,EACpC,MAAMH,EAAQ,KAAK,CAEvB,CAMQ,mBAAmBC,EAA+B,CACxD,GAAI,CAACA,EAAI,GAAI,MAAO,GACpB,IAAMX,EAAM,GAAGW,EAAI,WAAWA,EAAI,WAAa,aAAaA,EAAI,KAC1Db,EAAM,KAAK,IAAI,EACrB,GAAI,KAAK,iBAAiB,MAAQ,KAAK,uBACrC,OAAW,CAACmB,EAAGC,CAAC,IAAK,KAAK,iBACpBpB,EAAMoB,EAAI,KAAK,mBAAmB,KAAK,iBAAiB,OAAOD,CAAC,EAGxE,IAAME,EAAO,KAAK,iBAAiB,IAAInB,CAAG,EAC1C,OAAImB,GAAQrB,EAAMqB,EAAO,KAAK,kBAA0B,IACxD,KAAK,iBAAiB,IAAInB,EAAKF,CAAG,EAClC,KAAK,kBAAkB,EAChB,GACT,CAEA,MAAc,cACZY,EACAC,EACAS,EAA6B,CAAC,EACf,CAUf,GAAI,CAACA,EAAK,QAAU,KAAK,mBAAmBT,CAAG,EAAG,CAChD,KAAK,IAAI,aAAaA,EAAI,4BAA4BA,EAAI,WAAa,aAAaA,EAAI,IAAI,EAC5F,OAIF,GAAI,KAAK,OAAO,IAAI,qBAA4B,EAAG,CACjD,IAAMU,EAAa,MAAM,KAAK,MAAM,QAAQ,sBAA8B,CACxE,MAAO,sBACP,QAASV,EAAI,QACb,OAAQA,EAAI,OAAO,KACnB,KAAMA,EAAI,KACV,MAAOA,EAAI,OAAO,IACpB,CAAC,EAED,GAAIU,EAAW,QAAS,CACtB,KAAK,IAAI,4BAA4BA,EAAW,SAAS,EACzD,OAGEA,EAAW,UAAU,OACvBV,EAAM,CAAE,GAAGA,EAAK,KAAMU,EAAW,SAAS,IAAe,GAK7D,GAAIV,EAAI,MAAO,CACb,IAAMW,EAASX,EAAI,MAAM,GACzB,KAAK,SAAS,IAAIW,EAAQX,EAAI,OAAO,KAAMA,EAAI,IAAI,EAIrD,GAAI,KAAK,eAAgB,CACvB,IAAMW,EAASX,EAAI,OAAO,IAAMA,EAAI,OAAO,GACrCY,EAAU,KAAK,eAAe,MAAMZ,EAAI,KAAMA,EAAI,OAAO,GAAIA,EAAI,OAAO,EAC9E,GAAIY,EAAS,CACX,KAAK,IAAI,qBAAqBA,EAAQ,QAAQ,aAAaZ,EAAI,OAAO,kBAAkBY,EAAQ,UAAU,EAC1G,IAAMC,EAAiBb,EAAI,UAC3B,KAAK,aAAaD,EAASY,EAAQX,EAAI,GAAI,YAAMa,CAAc,EAC/D,IAAMC,EAAc,KAAK,gBAAgBf,EAASY,EAAQE,CAAc,EAExE,MAAM,KAAK,eAAe,QACxBD,EAAQ,QACR,KAAK,SACL,CAAE,QAASZ,EAAI,QAAS,OAAQA,EAAI,OAAO,KAAM,OAAAW,CAAO,EACxD,MAAOI,GAAS,CACd,cAAcD,CAAW,EACzB,MAAM,KAAK,YAAYf,EAAS,CAC9B,QAASC,EAAI,QAAS,OAAAW,EAAQ,KAAAI,EAAM,QAASf,EAAI,GAAI,UAAWa,CAClE,CAAC,CACH,CACF,EACA,QAKJ,IAAMG,EAAU,KAAK,aAAahB,CAAG,EAErC,GAAI,CAACgB,EACH,OAKF,GAAIhB,EAAI,OAASA,EAAI,UAAY,WAAY,CAC3C,IAAMiB,EAAe,KAAK,mBAAmBD,CAAO,EACpD,GAAIC,GAAgBA,IAAiBjB,EAAI,UACvC,OAIJ,IAAMW,EAASX,EAAI,OAAO,IAAMA,EAAI,OAAO,GAM3C,KAAK,SAAS,MAAM,CAClB,GAAIA,EAAI,GACR,QAASA,EAAI,QACb,UAAWA,EAAI,UACf,KAAMA,EAAI,KACV,OAAQA,EAAI,OACZ,MAAOA,EAAI,MACX,QAASA,EAAI,QACb,YAAaA,EAAI,YACjB,WAAYA,EAAI,qBAAqB,KAAOA,EAAI,UAAY,IAAI,MAAQ,YAAY,EACpF,cAAegB,EACf,WAAYhB,EAAI,UAClB,CAAC,EAED,GAAI,CAAE,OAAO,MAAM,KAAK,uBAAuBD,EAASC,EAAKgB,EAASL,CAAM,CAAE,QAC9E,CAAU,KAAK,SAAS,KAAKX,EAAI,EAAE,CAAE,CACvC,CAIA,MAAc,uBACZD,EACAC,EACAgB,EACAL,EACe,CAEf,GAAIX,EAAI,WAAY,CAClB,KAAK,IAAI,eAAeA,EAAI,sDAAiDgB,IAAU,EACxE,MAAM,KAAK,oBAAoBjB,EAASC,EAAKgB,EAAShB,EAAI,UAAU,GAEjF,KAAK,IAAI,cAAcA,EAAI,iDAAiDgB,IAAU,EAExF,OAGF,IAAME,EAAW,KAAK,SAAS,SAASF,CAAO,EAG/C,GAAI,CAACE,EAAU,CACE,MAAM,KAAK,uBAAuBnB,EAASC,EAAKgB,CAAO,GAEpE,KAAK,IAAI,UAAUA,0CAAgD,EAErE,OAGF,IAAMG,EAAYD,EAAS,MAAQF,EAG7BH,EAAiB,KAAK,mBAAmBG,CAAO,GAAKhB,EAAI,UAE/D,KAAK,IACH,YAAYA,EAAI,WAAWA,EAAI,OAAO,aAAamB,OAAenB,EAAI,KAAK,MAAM,EAAG,EAAE,GACxF,EAGA,KAAK,aAAaD,EAASY,EAAQX,EAAI,GAAI,YAAMa,CAAc,EAG/D,IAAMC,EAAc,KAAK,gBAAgBf,EAASY,EAAQE,CAAc,EAGlEO,EAAY,OAAOrB,EAAQ,aAAgB,WAC7CsB,EACAC,EAAiB,GAGfC,EAAcH,EAChB,IAAII,EACF,MAAOC,GAAkB,CAEvB,GADAH,GAAkBG,EACbJ,EAcH,GAAI,CACF,MAAM,KAAK,YAAYtB,EAASY,EAAQU,EAAeC,EAAgB,OAAWT,CAAc,CAClG,MAAE,CAA+B,KAhBf,CAClB,IAAMa,EAAUJ,EAAe,OAAS,GACpCA,EACA,IAAIH;AAAA;AAAA,EAA+BG,IACvC,GAAI,CACFD,EAAgB,MAAM,KAAK,YAAYtB,EAAS,CAC9C,QAASC,EAAI,QACb,OAAAW,EACA,KAAMe,EACN,QAAS1B,EAAI,GACb,UAAWa,CACb,CAAC,CACH,MAAE,CAA+B,EAMrC,EACA,OACAb,EAAI,OACN,EACA,OAEE2B,EAAUJ,EACZ,CAACK,EAAgBC,IAAsB,CACrCN,EAAY,KAAKK,CAAM,CACzB,EACA,OAGJ,GAAI5B,EAAI,aAAa,QACnB,QAAW8B,KAAS9B,EAAI,YAAY,OAClC,GAAI,CAAC8B,EAAM,OAAQ,CACjB,IAAMC,EAAM,KAAK,SAAS,SAASD,EAAM,EAAE,EACvCC,IACFD,EAAM,OAASC,EAAI,SAAS,KAAMC,GAAcA,EAAE,WAAW,GAAG,CAAC,EACjEF,EAAM,KAAOC,EAAI,OAOzB,IAAME,EAAejC,EAAI,MAAQ,KAAK,SAAS,aAAaW,CAAM,EAAI,GAChEuB,EAAqBD,EACvB,GAAGA;AAAA;AAAA,EAAmBjC,EAAI,OAAO,SAASA,EAAI,OAC9CA,EAAI,KAGFmC,EAAW,MAAM,KAAK,SAAS,QACnC,CACE,QAASD,EACT,QAAAlB,EACA,QAAS,CACP,QAAShB,EAAI,QACb,OAAQA,EAAI,OAAO,KACnB,SAAUA,EAAI,OAAO,GACrB,eAAgBA,EAAI,OAAO,SAC3B,MAAOA,EAAI,OAAO,KAClB,OAAAW,EACA,UAAWX,EAAI,OAAO,KACtB,UAAWA,EAAI,OAAO,KACtB,YAAaA,EAAI,YACjB,YAAaA,EAAI,WACnB,CACF,EACA2B,CACF,EAaA,GAXA,cAAcb,CAAW,EASzB,MAAMS,GAAa,MAAM,EAErBY,EAAS,MAAO,CAElB,GAAIA,EAAS,MAAM,WAAW,YAAY,EAAG,CAC3C,cAAcrB,CAAW,EACzB,KAAK,IAAI,sBAAsBK,GAAW,EAC1C,OAGF,KAAK,IAAI,gBAAgBgB,EAAS,OAAO,EACzC,IAAMC,EAAY,UAAUD,EAAS,QACjCd,EACF,MAAM,KAAK,YAAYtB,EAASY,EAAQU,EAAee,EAAW,QAASvB,CAAc,EAEzF,MAAM,KAAK,YAAYd,EAAS,CAC9B,QAASC,EAAI,QACb,OAAAW,EACA,KAAMyB,EACN,QAASpC,EAAI,GACb,UAAW,QACX,UAAWa,CACb,CAAC,EAEH,OAIF,IAAIwB,EAAeF,EAAS,QAC5B,GAAI,KAAK,OAAO,IAAI,sBAA6B,EAAG,CAClD,IAAMzB,EAAa,MAAM,KAAK,MAAM,QAAQ,uBAA+B,CACzE,MAAO,uBACP,QAASV,EAAI,QACb,OAAQA,EAAI,OAAO,KACnB,SAAUqC,EACV,QAAArB,CACF,CAAC,EAED,GAAIN,EAAW,QAAS,CACtB,KAAK,IAAI,6BAA6BA,EAAW,SAAS,EAC1D,OAGEA,EAAW,UAAU,WACvB2B,EAAe3B,EAAW,SAAS,UAOnCV,EAAI,UAAY,UAAYqC,IACR,KAAK,SAAS,IAAI,QAAQ,GACb,gBAAgBrB,CAAO,IAExDqB,EAAe,OAAOlB,QAAgBH;AAAA;AAAA,EAAeqB,MAGrDrC,EAAI,UAAY,YAAcqC,IAChCA,EAAe,IAAIlB;AAAA;AAAA,EAAiBkB,KAItC,IAAIC,EACAD,IACEhB,GACF,MAAM,KAAK,YAAYtB,EAASY,EAAQU,EAAegB,EAAc,OAAWxB,CAAc,EAC9FyB,EAAiBjB,GAEjBiB,EAAiB,MAAM,KAAK,YAAYvC,EAAS,CAC/C,QAASC,EAAI,QACb,OAAAW,EACA,KAAM0B,EACN,QAASrC,EAAI,GACb,UAAWa,EACX,QAAAG,CACF,CAAC,GAKDhB,EAAI,OAASqC,GACf,KAAK,SAAS,IAAI1B,EAAQQ,EAAWkB,CAAY,EAInD,IAAME,EAAe,KAAK,OAAO,cACjC,GAAIA,GAAc,aAAeJ,EAAS,SAAU,CAClD,IAAMK,GAAeD,EAAa,mBAAqB,IAAM,IAK7D,GAHGJ,EAAS,UAAYK,GAAe,CAACL,EAAS,OAASI,EAAa,IAAI,cACxEJ,EAAS,OAASI,EAAa,IAAI,UAEpB,CAChB,IAAME,EAAS,KAAK,MAAMN,EAAS,SAAW,GAAI,EAC5CO,EAASP,EAAS,MAAQ,SAAW,YAIrCQ,EAAUC,GAAW5C,EAAI,KAAM,EAAG,GAAG,EACrC0B,EAAUS,EAAS,MACrBU,EAAUV,EAAS,MAAO,GAAG,EAC7BS,GAAWT,EAAS,QAAS,EAAG,GAAG,EACjCW,EAAa,GAAGJ,IAAW,SAAW,YAAO,cAASvB,OAAeuB,MAAWD;AAAA,EAAazC,EAAI,WAAWA,EAAI,OAAO,SAAS2C;AAAA,EAAYjB,IAElJ,KAAK,aAAa,CAChB,QAASa,EAAa,YAAY,QAClC,OAAQA,EAAa,YAAY,OACjC,KAAMO,EACN,QAAA9B,EACA,UAAWuB,EAAa,YAAY,SACtC,CAAC,EAAE,MAAOlC,GAAM,CACd,KAAK,IAAI,6BAA6BA,EAAE,SAAS,CACnD,CAAC,GAeL,GAVI,KAAK,UAAY8B,EAAS,UAC5B,KAAK,SAAS,qBACZnB,EACA,KAAK,MAAMmB,EAAS,SAAW,GAAI,EACnC,CAACA,EAAS,MACVnC,EAAI,OACN,EAIEA,EAAI,UAAY,UAAYmC,EAAS,UAAY,CAACA,EAAS,MAAO,CACpE,IAAMY,EAAgB,KAAK,SAAS,IAAI,QAAQ,EAC5CA,GACFA,EAAc,aAAapC,EAAQwB,EAAS,SAAUnB,CAAO,EAAE,MAAOX,GAAW,CAC/E,KAAK,IAAI,gCAAgCA,EAAE,SAAS,CACtD,CAAC,EAODgC,GAAgBC,GADO,CAAC,WAAY,WAAY,UAAW,OAAO,EACb,SAAStC,EAAI,OAAO,GAC3E,KAAK,oBAAoBD,EAASC,EAAKgB,EAASqB,EAAcC,EAAgB,CAAC,EAAE,MAAOjC,GAAM,CAC5F,KAAK,IAAI,qBAAqBA,EAAE,SAAS,CAC3C,CAAC,CAEL,CAWA,MAAc,oBACZN,EACAiD,EACAC,EACAZ,EACAa,EACAC,EACAC,EAAuB,IAAI,IACZ,CACf,GAAID,GAAS9E,GAAc,oBAAqB,CAC9C,KAAK,IAAI,0BAA0B8E,sBAA0B,EAC7D,OAIFC,EAAQ,IAAIH,CAAa,EAEzB,OAAW,CAACI,EAAItB,CAAG,IAAK,OAAO,QAAQ,KAAK,OAAO,MAAM,EAAG,CAC1D,GAAIsB,IAAOJ,EAAe,SAG1B,GAAIG,EAAQ,IAAIC,CAAE,EAAG,CACnB,KAAK,IAAI,gBAAgBA,yCAA0C,EACnE,SASF,GAAI,CAJetB,EAAI,SAAS,OAAQC,GAAcA,EAAE,WAAW,GAAG,CAAC,EAC1C,KAAMA,GACjCK,EAAa,YAAY,EAAE,SAASL,EAAE,YAAY,CAAC,CACrD,EACgB,SAEhB,KAAK,IAAI,eAAemB,EAAQ,QAAQF,UAAsBI,IAAK,EAEnE,IAAM1C,EAASqC,EAAY,OAAO,IAAMA,EAAY,OAAO,GACrDM,EAAkB,KAAK,mBAAmBD,CAAE,EAC5CE,EAAkB,KAAK,mBAAmBN,CAAa,EAE7D,GAAI,CAEF,KAAK,aAAalD,EAASY,EAAQuC,EAAmB,YAAMI,CAAe,EAG3E,IAAMxC,EAAc,KAAK,gBAAgBf,EAASY,EAAQ2C,CAAe,EAGnEE,EAAiBL,IAAU,EAC7B,kBAAkBH,EAAY,OAAO,UAAUA,EAAY;AAAA;AAAA,GAAYC,YAAwBZ,IAC/FA,EAEEF,EAAW,MAAM,KAAK,SAAS,QAAQ,CAC3C,QAASqB,EACT,QAASH,EACT,QAAS,CACP,QAASL,EAAY,QACrB,OAAQ,SAASC,IACjB,MAAOD,EAAY,OAAO,IAC5B,CACF,CAAC,EAID,GAFA,cAAclC,CAAW,EAErBqB,EAAS,SAAW,CAACA,EAAS,MAAO,CAEvC,IAAIsB,EAAYtB,EAAS,QACrBa,EAAY,UAAY,aAC1BS,EAAY,IAAI1B,EAAI;AAAA;AAAA,EAAY0B,KAGlC,IAAMC,EAAS,MAAM,KAAK,YAAY3D,EAAS,CAC7C,QAASiD,EAAY,QACrB,OAAArC,EACA,KAAM8C,EACN,UAAWH,CACb,CAAC,EAGGI,GAAUvB,EAAS,SACrB,MAAM,KAAK,oBACTpC,EAASiD,EAAaK,EAAIlB,EAAS,QAASuB,EAAkBP,EAAQ,EAAGC,CAC3E,OAEOjB,EAAS,OAClB,KAAK,IAAI,eAAekB,aAAclB,EAAS,OAAO,CAE1D,OAAS9B,EAAP,CACA,KAAK,IAAI,eAAegD,cAAehD,EAAE,SAAS,CACpD,CAEA,MAEJ,CAIA,MAAc,YACZN,EACAC,EACiB,CAEjB,OAAID,EAAQ,OAAS,YAAcC,EAAI,UAC7BD,EAAuC,KAAK,CAClD,GAAGC,EACH,UAAWA,EAAI,UACf,UAAWA,EAAI,SACjB,CAAC,EAEKD,EAAQ,KAAKC,CAAU,GAAK,EACtC,CAEA,MAAc,YACZD,EACAY,EACAgD,EACA5C,EACA6C,EACA3D,EACkB,CAClB,OAAIF,EAAQ,OAAS,YAAcE,EACzBF,EAAuC,YAAYY,EAAQgD,EAAW5C,EAAM6C,EAAW3D,CAAS,EAEnGF,EAAQ,cAAcY,EAAQgD,EAAW5C,EAAM6C,CAAS,GAAK,EACtE,CAEQ,aACN7D,EACAY,EACAgD,EACAE,EACA5D,EACM,CACFF,EAAQ,OAAS,YAAcE,EAChCF,EAAuC,MAAMY,EAAQgD,EAAWE,EAAO5D,CAAS,EAEjFF,EAAQ,QAAQY,EAAQgD,EAAWE,CAAK,CAE5C,CAEQ,gBACN9D,EACAY,EACAV,EACgC,CAChC,IAAM6D,EAAa,IAAM,CACnB/D,EAAQ,OAAS,YAAcE,EAChCF,EAAuC,WAAWY,EAAQV,CAAS,EAEpEF,EAAQ,aAAaY,CAAM,CAE/B,EAEA,OAAAmD,EAAW,EACJ,YAAYA,EAAY1F,EAAkB,CACnD,CAQA,MAAc,cACZ2B,EACAC,EACkB,CAClB,GAAI,CAAC,KAAK,KAAM,MAAO,GAEvB,IAAM+D,EAAY/D,EAAI,KAAK,YAAY,EACjCgE,EAAY,KAAK,KAAK,UAAU,EAEtC,QAAWC,KAAQD,EACjB,GAAKC,EAAK,SAEV,QAAWC,KAASD,EAAK,OAEvB,GACEF,EAAU,SAASG,EAAM,GAAG,YAAY,CAAC,GACzCH,EAAU,SAASG,EAAM,KAAK,YAAY,CAAC,EAC3C,CACA,KAAK,IAAI,iBAAiBlE,EAAI,WAAWA,EAAI,OAAO,kBAAkBiE,EAAK,gBAAgBC,EAAM,KAAK,EAEtG,IAAMvD,EAASX,EAAI,OAAO,IAAMA,EAAI,OAAO,GACrCa,EAAiBb,EAAI,UAG3B,KAAK,aAAaD,EAASY,EAAQX,EAAI,GAAI,YAAMa,CAAc,EAC/D,IAAMC,EAAc,KAAK,gBAAgBf,EAASY,EAAQE,CAAc,EAExE,GAAI,CACF,IAAMsB,EAAW,MAAM,KAAK,KAAK,SAAS8B,EAAK,KAAMjE,EAAI,KAAMkE,EAAM,EAAE,EAIvE,GAFA,cAAcpD,CAAW,EAErBqB,EAAU,CAEZ,IAAMgC,EAAS,KAAKD,EAAM,YAAYD,EAAK;AAAA;AAAA,EAC3C,MAAM,KAAK,YAAYlE,EAAS,CAC9B,QAASC,EAAI,QACb,OAAAW,EACA,KAAMwD,EAAShC,EACf,QAASnC,EAAI,GACb,UAAWa,CACb,CAAC,EAGH,MAAO,EACT,OAASR,EAAP,CACA,qBAAcS,CAAW,EACzB,KAAK,IAAI,uBAAuBT,EAAE,SAAS,EAE3C,MAAM,KAAK,YAAYN,EAAS,CAC9B,QAASC,EAAI,QACb,OAAAW,EACA,KAAM,cAAcsD,EAAK,QAAQC,EAAM,SAAS7D,EAAE,UAClD,QAASL,EAAI,GACb,UAAW,QACX,UAAWa,CACb,CAAC,EACM,EACT,GAKN,MAAO,EACT,CAMA,MAAc,uBACZd,EACAC,EACAgB,EACkB,CAClB,GAAI,CAAC,KAAK,KAAM,MAAO,GAEvB,IAAMgD,EAAY,KAAK,KAAK,UAAU,EAEtC,QAAWC,KAAQD,EAAW,CAC5B,GAAI,CAACC,EAAK,QAAS,SAEnB,IAAMC,EAAQD,EAAK,OAAO,KAAKG,GAAKA,EAAE,KAAOpD,CAAO,EACpD,GAAI,CAACkD,EAAO,SAEZ,KAAK,IAAI,4BAA4BlE,EAAI,WAAWA,EAAI,OAAO,kBAAkBiE,EAAK,gBAAgBjD,IAAU,EAEhH,IAAML,EAASX,EAAI,OAAO,IAAMA,EAAI,OAAO,GACrCa,EAAiBb,EAAI,UAE3B,KAAK,aAAaD,EAASY,EAAQX,EAAI,GAAI,YAAMa,CAAc,EAC/D,IAAMC,EAAc,KAAK,gBAAgBf,EAASY,EAAQE,CAAc,EAExE,GAAI,CACF,IAAMsB,EAAW,MAAM,KAAK,KAAK,SAAS8B,EAAK,KAAMjE,EAAI,KAAMgB,CAAO,EACtE,qBAAcF,CAAW,EAErBqB,GACF,MAAM,KAAK,YAAYpC,EAAS,CAC9B,QAASC,EAAI,QACb,OAAAW,EACA,KAAMwB,EACN,QAASnC,EAAI,GACb,UAAWa,CACb,CAAC,EAGI,EACT,OAASR,EAAP,CACA,qBAAcS,CAAW,EACzB,KAAK,IAAI,0BAA0BE,MAAYX,EAAE,SAAS,EAE1D,MAAM,KAAK,YAAYN,EAAS,CAC9B,QAASC,EAAI,QACb,OAAAW,EACA,KAAM,cAAcsD,EAAK,QAAQC,EAAM,SAAS7D,EAAE,UAClD,QAASL,EAAI,GACb,UAAW,QACX,UAAWa,CACb,CAAC,EACM,EACT,EAGF,MAAO,EACT,CAMA,MAAc,oBACZd,EACAC,EACAgB,EACAqD,EACkB,CAClB,GAAI,CAAC,KAAK,KAAM,MAAO,GAGvB,IAAMJ,EADY,KAAK,KAAK,UAAU,EACf,KAAKnF,GAAKA,EAAE,OAASuF,CAAQ,EAEpD,GAAI,CAACJ,GAAQ,CAACA,EAAK,QAAS,MAAO,GAEnC,IAAMC,EAAQD,EAAK,OAAO,KAAKG,GAAKA,EAAE,KAAOpD,CAAO,GAAK,CAAE,KAAMA,EAAS,GAAIA,CAAQ,EAEtF,KAAK,IAAI,+BAA+BhB,EAAI,WAAWA,EAAI,OAAO,kBAAkBqE,aAAoBrD,IAAU,EAElH,IAAML,EAASX,EAAI,OAAO,IAAMA,EAAI,OAAO,GACrCa,EAAiBb,EAAI,UAE3B,KAAK,aAAaD,EAASY,EAAQX,EAAI,GAAI,YAAMa,CAAc,EAC/D,IAAMC,EAAc,KAAK,gBAAgBf,EAASY,EAAQE,CAAc,EAElEyD,EAAQ,KAAK,IAAI,EACvB,GAAI,CACF,IAAMnC,EAAW,MAAM,KAAK,KAAK,SAASkC,EAAUrE,EAAI,KAAMgB,CAAO,EAC/DuD,EAAW,KAAK,IAAI,EAAID,EAa9B,GAZA,cAAcxD,CAAW,EAErBqB,GACF,MAAM,KAAK,YAAYpC,EAAS,CAC9B,QAASC,EAAI,QACb,OAAAW,EACA,KAAMwB,EACN,QAASnC,EAAI,GACb,UAAWa,CACb,CAAC,EAGCb,EAAI,UAAY,UAAYuE,EAAU,CACxC,IAAMxB,EAAgB,KAAK,SAAS,IAAI,QAAQ,EAC5CA,GACFA,EAAc,aAAapC,EAAQ4D,EAAUvD,CAAO,EAAE,MAAOX,GAAW,CACtE,KAAK,IAAI,uCAAuCA,EAAE,SAAS,CAC7D,CAAC,EAIL,MAAO,EACT,OAASA,EAAP,CACA,qBAAcS,CAAW,EACzB,KAAK,IAAI,0BAA0BuD,KAAYrD,MAAYX,EAAE,SAAS,EAEtE,MAAM,KAAK,YAAYN,EAAS,CAC9B,QAASC,EAAI,QACb,OAAAW,EACA,KAAM,cAAc0D,KAAYH,EAAM,SAAS7D,EAAE,UACjD,QAASL,EAAI,GACb,UAAW,QACX,UAAWa,CACb,CAAC,EACM,EACT,CACF,CAEQ,mBAAmBG,EAAqC,CAC9D,OAAW,CAACf,EAAWuE,CAAO,IAAK,OAAO,QAAQ,KAAK,OAAO,SAAS,SAAS,QAAQ,EACtF,GAAIA,EAAQ,eAAiBxD,EAC3B,OAAOf,CAIb,CAEQ,aAAaD,EAA0C,CAI7D,GAAIA,EAAI,UAAY,SAClB,OAAIA,EAAI,eACN,KAAK,IAAI,qCAAqCA,EAAI,eAAe,EAC1DA,EAAI,eAEb,OAMF,IAAMW,EAASX,EAAI,OAAO,IAAMA,EAAI,OAAO,GACrCyE,EAAW,KAAK,cAAc,IAAIzE,EAAI,QAASW,EAAQX,EAAI,SAAS,EAC1E,GAAIyE,EACF,YAAK,IACH,cAAczE,EAAI,WAAWW,IAASX,EAAI,UAAY,IAAIA,EAAI,YAAc,SAASyE,EAAS,oBAChG,EACOA,EAAS,QAIlB,GAAIzE,EAAI,cACN,OAAOA,EAAI,cAIb,GAAI,CAACA,EAAI,MACP,OAAIA,EAAI,UAAY,WACF,KAAK,OAAO,SAAS,SAAS,SAASA,EAAI,SAAS,GACpD,aAEdA,EAAI,UAAY,WACX,KAAK,OAAO,SAAS,SAAS,aAEvC,OAQF,IAAM0E,EAAiB1E,EAAI,OAAO,QAAU,GAG5C,GAAIA,EAAI,UAAY,YACH,KAAK,OAAO,SAAS,SAAS,OAClC,QAAU,mBAAoB,CACvC,IAAMgB,EAAU,KAAK,SAAS,cAAchB,EAAI,KAAM,CAAE,eAAA0E,CAAe,CAAC,EACxE,OAAK1D,GAAS,OAMlB,IAAM2D,EAAe,KAAK,SAAS,cAAc3E,EAAI,KAAM,CAAE,eAAA0E,CAAe,CAAC,EAC7E,GAAIC,EAAc,OAAOA,EAIzB,GAAI,CAAAD,EAEJ,OAAI1E,EAAI,UAAY,WACF,KAAK,OAAO,SAAS,SAAS,SAASA,EAAI,SAAS,GACpD,aAGX,KAAK,OAAO,SAAS,SAAS,YACvC,CACF,EA/kCa4E,EAANvG,GA6oBLwG,EA7oBWD,EA6oBa,sBAAsB,GCxuBhD,SAASE,EAAWC,EAAsB,CACxC,OAAOA,EACJ,QAAQ,KAAM,OAAO,EACrB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,MAAM,CACzB,CAKO,SAASC,GAAuBC,EAAoB,CAIzD,IAAMC,EADUD,EAAG,QAAQ,gCAAiC,IAAI,EAC1C,MAAM;AAAA,CAAI,EAC1BE,EAAmB,CAAC,EACtBC,EAAc,GACdC,EAAgB,GAChBC,EAA2B,CAAC,EAC5BC,EAAe,GACfC,EAAyB,CAAC,EAC1BC,EAAU,GAEd,QAASC,EAAI,EAAGA,EAAIR,EAAM,OAAQQ,IAAK,CACrC,IAAMC,EAAOT,EAAMQ,CAAC,EAGpB,GAAIC,EAAK,UAAU,EAAE,WAAW,KAAK,EACnC,GAAKP,EAKE,CACLA,EAAc,GACd,IAAMQ,EAAOd,EAAWQ,EAAe,KAAK;AAAA,CAAI,CAAC,EAE3CO,EAAWR,EAAgB,MAAMA;AAAA,EAAoB,GAC3DF,EAAO,KAAK,cAAcU,IAAWD,gBAAmB,EACxDP,EAAgB,GAChB,aAZgB,CAChBD,EAAc,GACdC,EAAgBM,EAAK,UAAU,EAAE,MAAM,CAAC,EAAE,KAAK,EAC/CL,EAAiB,CAAC,EAClB,SAYJ,GAAIF,EAAa,CACfE,EAAe,KAAKK,CAAI,EACxB,SAgBF,GAZIJ,GAAgB,CAACI,EAAK,UAAU,EAAE,WAAW,GAAG,IAClDR,EAAO,KAAK,eAAe,EAC3BI,EAAe,IAIbE,GAAW,CAACE,EAAK,KAAK,EAAE,WAAW,GAAG,IACxCF,EAAU,GACVD,EAAe,CAAC,GAIdG,EAAK,UAAU,EAAE,WAAW,IAAI,EAAG,CACrC,IAAMG,EAAUH,EAAK,QAAQ,QAAS,EAAE,EACnCJ,IACHJ,EAAO,KAAK,cAAc,EAC1BI,EAAe,IAEjBJ,EAAO,KAAKY,EAAcD,CAAO,CAAC,EAClC,SAIF,IAAME,EAAcL,EAAK,MAAM,mBAAmB,EAClD,GAAIK,EAAa,CACfb,EAAO,KAAK,EAAE,EACdA,EAAO,KAAK,MAAMY,EAAcC,EAAY,CAAC,CAAC,OAAO,EACrD,SAIF,GAAI,iBAAiB,KAAKL,EAAK,KAAK,CAAC,EAAG,CACtCR,EAAO,KAAK,oBAAK,EACjB,SAIF,GAAIQ,EAAK,KAAK,EAAE,WAAW,GAAG,GAAKA,EAAK,KAAK,EAAE,SAAS,GAAG,EAAG,CAC5D,IAAMM,EAAQN,EAAK,MAAM,GAAG,EAAE,MAAM,EAAG,EAAE,EAAE,IAAIO,GAAKA,EAAE,KAAK,CAAC,EAG5D,GAAI,kBAAkB,KAAKP,EAAK,KAAK,CAAC,EAAG,CACvCF,EAAU,GACV,SAGF,GAAI,CAACA,EAAS,CAEZD,EAAeS,EACfR,EAAU,GACV,SAIF,GAAID,EAAa,OAAS,GAAKS,EAAM,OAAS,EAC5C,GAAIA,EAAM,QAAU,EAAG,CAErB,IAAME,EAAkB,CAAC,EACzB,QAASD,EAAI,EAAGA,EAAID,EAAM,OAAQC,IAChC,GAAIA,IAAM,EACRC,EAAM,KAAK,MAAMJ,EAAcE,EAAMC,CAAC,CAAC,OAAO,MACzC,CACL,IAAME,EAASZ,EAAaU,CAAC,EAAI,GAAGH,EAAcP,EAAaU,CAAC,CAAC,MAAQ,GACzEC,EAAM,KAAK,GAAGC,IAASL,EAAcE,EAAMC,CAAC,CAAC,GAAG,EAGpDf,EAAO,KAAK,UAAKgB,EAAM,KAAK,UAAK,GAAG,OAEpChB,EAAO,KAAK,UAAKY,EAAcE,EAAM,CAAC,CAAC,GAAG,EAG9C,SAIF,IAAMI,EAAUV,EAAK,MAAM,qBAAqB,EAChD,GAAIU,EAAS,CACX,IAAMC,EAASD,EAAQ,CAAC,EAAE,OAAS,EAAI,KAAO,GAC9ClB,EAAO,KAAK,GAAGmB,WAAWP,EAAcM,EAAQ,CAAC,CAAC,GAAG,EACrD,SAIF,IAAME,EAAUZ,EAAK,MAAM,uBAAuB,EAClD,GAAIY,EAAS,CACX,IAAMD,EAASC,EAAQ,CAAC,EAAE,OAAS,EAAI,KAAO,GACxCC,EAAMb,EAAK,MAAM,aAAa,IAAI,CAAC,GAAK,IAC9CR,EAAO,KAAK,GAAGmB,IAASE,MAAQT,EAAcQ,EAAQ,CAAC,CAAC,GAAG,EAC3D,SAIF,GAAI,CAACZ,EAAK,KAAK,EAAG,CAChBR,EAAO,KAAK,EAAE,EACd,SAIFA,EAAO,KAAKY,EAAcJ,CAAI,CAAC,EAI7BP,GACFD,EAAO,KAAK,cAAcL,EAAWQ,EAAe,KAAK;AAAA,CAAI,CAAC,gBAAgB,EAE5EC,GACFJ,EAAO,KAAK,eAAe,EAG7B,IAAIsB,EAAOtB,EAAO,KAAK;AAAA,CAAI,EAAE,KAAK,EAIlC,OAAAsB,EAAOC,GAAmBD,CAAI,EAEvBA,CACT,CAKA,SAASV,EAAchB,EAAsB,CAC3C,IAAII,EAASL,EAAWC,CAAI,EAG5B,OAAAI,EAASA,EAAO,QAAQ,aAAc,iBAAiB,EAGvDA,EAASA,EAAO,QACd,2BACA,CAACwB,EAAIC,EAAOC,IAAQ,YAAY/B,EAAW+B,CAAG,MAAMD,OACtD,EAGAzB,EAASA,EAAO,QAAQ,qBAAsB,kBAAkB,EAGhEA,EAASA,EAAO,QAAQ,iBAAkB,WAAW,EAGrDA,EAASA,EAAO,QAAQ,6BAA8B,WAAW,EAGjEA,EAASA,EAAO,QAAQ,aAAc,WAAW,EAGjDA,EAASA,EAAO,QAAQ,iBAAkB,6BAA6B,EAEhEA,CACT,CAOA,IAAM2B,GAAgB,2GAEtB,SAASJ,GAAmBD,EAAsB,CAEhD,IAAMN,EAAQM,EAAK,MAAM,8BAA8B,EACnDM,EAAY,GAEhB,OAAOZ,EAAM,IAAKa,GACZ,qBAAqB,KAAKA,CAAI,GAChCD,EAAY,GACLC,GAEL,sBAAsB,KAAKA,CAAI,GACjCD,EAAY,GACLC,GAELD,EAAkBC,EAEfA,EAAK,QAAQF,GAAe,kBAAkB,CACtD,EAAE,KAAK,EAAE,CACZ,CC/OA,OAAS,gBAAAG,GAAc,iBAAAC,GAAe,aAAAC,GAAW,cAAAC,OAAkB,KACnE,OAAS,WAAAC,GAAS,WAAAC,OAAe,OAwDjC,IAAMC,GAAN,KAAyB,CACf,KAAwB,CAAC,EACzB,SACA,MAAQ,GAEhB,YAAYC,EAAiB,CAC3B,KAAK,SAAWH,GAAQG,EAAS,8BAA8B,EAC/D,KAAK,KAAK,CACZ,CAEQ,MAAa,CACnB,GAAI,CACEJ,GAAW,KAAK,QAAQ,IAC1B,KAAK,KAAO,KAAK,MAAMH,GAAa,KAAK,SAAU,OAAO,CAAC,EAE/D,MAAE,CACA,KAAK,KAAO,CAAC,CACf,CACF,CAEQ,MAAa,CACnB,GAAI,CACFE,GAAUG,GAAQ,KAAK,QAAQ,EAAG,CAAE,UAAW,EAAK,CAAC,EACrDJ,GAAc,KAAK,SAAU,KAAK,UAAU,KAAK,KAAM,KAAM,CAAC,CAAC,EAC/D,KAAK,MAAQ,EACf,MAAE,CAEF,CACF,CAGA,aAAaO,EAAiBC,EAA+BC,EAAmBC,EAAiBC,EAAkBC,EAAsB,CAClI,KAAK,KAAKL,CAAO,IACpB,KAAK,KAAKA,CAAO,EAAI,CAAE,KAAMC,EAAW,KAAM,CAAC,CAAE,GAE/CA,IAAW,KAAK,KAAKD,CAAO,EAAE,KAAOC,GACzC,KAAK,KAAKD,CAAO,EAAE,KAAKE,CAAS,EAAI,CACnC,SAAAE,EACA,QAAAD,EACA,OAAAE,EACA,UAAW,IAAI,KAAK,EAAE,YAAY,CACpC,EACA,KAAK,MAAQ,GACb,KAAK,KAAK,CACZ,CAGA,aAAaL,EAAkF,CAC7F,IAAMM,EAAQ,KAAK,KAAKN,CAAO,EAC/B,OAAKM,EACE,OAAO,QAAQA,EAAM,IAAI,EAC7B,OAAO,CAAC,CAAC,CAAEC,CAAI,IAAMA,EAAK,SAAW,QAAUA,EAAK,SAAW,QAAQ,EACvE,IAAI,CAAC,CAACL,EAAWK,CAAI,KAAO,CAC3B,UAAAL,EACA,QAASK,EAAK,QACd,SAAUA,EAAK,QACjB,EAAE,EAPe,CAAC,CAQtB,CAGA,SAASP,EAA0B,CACjC,MAAO,CAAC,CAAC,KAAK,KAAKA,CAAO,CAC5B,CACF,EAUMQ,GAAN,KAA0B,CAChB,QAAkC,CAAC,EACnC,SACA,MAAQ,GACR,UAER,YAAYT,EAAiB,CAC3B,KAAK,SAAWH,GAAQG,EAAS,+BAA+B,EAChE,GAAI,CACEJ,GAAW,KAAK,QAAQ,IAC1B,KAAK,QAAU,KAAK,MAAMH,GAAa,KAAK,SAAU,OAAO,CAAC,EAElE,MAAE,CAAQ,KAAK,QAAU,CAAC,CAAE,CAC9B,CAEA,IAAIU,EAA2B,CAC7B,OAAO,KAAK,QAAQA,CAAS,GAAK,CACpC,CAEA,IAAIA,EAAmBO,EAAsB,CACvC,KAAK,QAAQP,CAAS,IAAMO,IAChC,KAAK,QAAQP,CAAS,EAAIO,EAC1B,KAAK,MAAQ,GAER,KAAK,YACR,KAAK,UAAY,WAAW,IAAM,KAAK,MAAM,EAAG,GAAG,GAEvD,CAEA,OAAc,CAKZ,GAJI,KAAK,YACP,aAAa,KAAK,SAAS,EAC3B,KAAK,UAAY,QAEf,EAAC,KAAK,MACV,GAAI,CACF,IAAMC,EAAMb,GAAQ,KAAK,QAAQ,EAC5BF,GAAWe,CAAG,GAAGhB,GAAUgB,EAAK,CAAE,UAAW,EAAK,CAAC,EACxDjB,GAAc,KAAK,SAAU,KAAK,UAAU,KAAK,QAAS,KAAM,CAAC,CAAC,EAClE,KAAK,MAAQ,EACf,MAAE,CAA0B,CAC9B,CACF,EAEakB,EAAN,KAAgD,CAC5C,KAAO,WACR,SACA,YACA,QACA,QAAU,GACV,IAIA,gBAER,YACEC,EACAC,EAA8C,CAAC,EAC/CC,EAAoC,QAAQ,MAAM,KAAK,QAAS,YAAY,EAC5E,CACA,KAAK,SAAW,IAAI,IAAI,OAAO,QAAQF,CAAQ,CAAC,EAChD,KAAK,gBAAkBC,EAAK,QAAQ,UACpC,KAAK,IAAMC,EACX,KAAK,WAAa,IAAIhB,GAAmB,QAAQ,IAAI,CAAC,EACtD,KAAK,YAAc,IAAIU,GAAoB,QAAQ,IAAI,CAAC,CAC1D,CAKQ,mBAAmBN,EAAyC,CAClE,IAAMa,EAAM,KAAK,SAAS,IAAIb,CAAS,EACvC,OAAIa,GAAK,YAAc,OAAkBA,EAAI,UACtC,KAAK,eACd,CAIQ,UACNb,EACAc,EACAC,EACAC,EACS,CACT,IAAMC,EAAO,KAAK,mBAAmBjB,CAAS,EAC9C,GAAI,CAACiB,GAAQA,EAAK,SAAW,EAAG,MAAO,GACvC,IAAMC,EAAUJ,GAAU,KAAO,OAAOA,CAAM,EAAI,GAC5CK,EAAUJ,GAAU,KAAO,OAAOA,CAAM,EAAI,GAC5CK,EAASJ,GAAc,YAAY,EACzC,QAAWK,KAASJ,EAClB,GAAKI,IACDA,IAAUH,GAAWG,IAAUF,GAC/BE,EAAM,WAAW,GAAG,GAAKD,GAAUC,EAAM,MAAM,CAAC,EAAE,YAAY,IAAMD,GAAQ,MAAO,GAEzF,MAAO,EACT,CAEA,UAAUE,EAAwD,CAChE,KAAK,QAAUA,CACjB,CAEA,MAAM,OAAuB,CAC3B,KAAK,QAAU,GAEf,IAAMC,EAAU,MAAM,KAAK,KAAK,SAAS,QAAQ,CAAC,EAClD,KAAK,IAAI,GAAGA,EAAQ,qCAAqC,EAEzD,QAASC,EAAI,EAAGA,EAAID,EAAQ,OAAQC,IAAK,CACvC,GAAM,CAACxB,EAAWyB,CAAM,EAAIF,EAAQC,CAAC,EACrC,KAAK,IAAI,iCAAiCxB,OAAewB,EAAI,KAAKD,EAAQ,SAAS,EACnF,GAAI,CAGF,MAAM,KAAK,QAAQE,EAAO,MAAO,gBAAiB,CAAE,qBAAsB,EAAM,CAAC,EAAE,MAAM,IAAM,CAAC,CAAC,EAEjG,IAAMC,EAAK,MAAM,KAAK,QAAQD,EAAO,MAAO,OAAO,EAC7CE,EAAYD,EAAG,QAAQ,GACvBE,EAAcF,EAAG,QAAQ,SAC/B,KAAK,IAAI,QAAQE,qBAA+B5B,IAAY,EACxD2B,GACF,KAAK,QAAQ,IAAI3B,EAAW,CAAE,OAAQ2B,EAAW,SAAUC,GAAe5B,CAAU,CAAC,EAEvF,KAAK,SAASA,EAAWyB,CAAM,CACjC,OAASI,EAAP,CACA,KAAK,IAAI,qCAAqC7B,OAAe6B,EAAE,SAAS,CAC1E,CAEIL,EAAID,EAAQ,OAAS,GACvB,MAAM,IAAI,QAASO,GAAM,WAAWA,EAAG,GAAG,CAAC,EAI/C,KAAK,IAAI,OAAOP,EAAQ,oCAAoC,CAC9D,CAEA,MAAM,MAAsB,CAC1B,KAAK,QAAU,GAGf,KAAK,YAAY,MAAM,EAIvB,IAAMQ,EAAS,MAAM,KAAK,KAAK,SAAS,QAAQ,CAAC,EAAE,IAAI,MAAO,CAAC/B,EAAWyB,CAAM,IAAM,CACpF,GAAI,CACF,MAAM,KAAK,QAAQA,EAAO,MAAO,aAAc,CAAE,OAAQ,GAAI,QAAS,CAAE,CAAC,CAC3E,MAAE,CAEF,CACF,CAAC,EACD,MAAM,QAAQ,WAAWM,CAAM,CACjC,CAKA,mBAAmB/B,EAAuC,CACxD,OAAO,KAAK,SAAS,IAAIA,CAAS,GAAG,KACvC,CAKQ,iBAAsC,CAC5C,GAAM,CAAC,CAAEyB,CAAM,EAAI,MAAM,KAAK,KAAK,SAAS,QAAQ,CAAC,EAAE,CAAC,EACxD,OAAOA,GAAQ,KACjB,CAKQ,aAAaV,EAAgBf,EAAwC,CAC3E,GAAIA,EAAW,CACb,IAAMgC,EAAQ,KAAK,mBAAmBhC,CAAS,EAC/C,GAAIgC,EAAO,OAAOA,EAEpB,OAAO,KAAK,eAAe,IAAIjB,CAAM,EACjC,KAAK,mBAAmB,KAAK,eAAe,IAAIA,CAAM,CAAE,EACxD,KAAK,gBAAgB,CAC3B,CAGQ,eAAsC,IAAI,IAE1C,QAA6D,IAAI,IAEjE,WAMR,MAAM,KAAKkB,EAAgE,CACzE,IAAMD,EAAQ,KAAK,aAAaC,EAAI,OAAQA,EAAI,SAAS,EACzD,GAAI,CAACD,EACH,YAAK,IAAI,qCAAqC,EACvC,GAGT,IAAME,EAAS,KACTC,EAAOF,EAAI,KAAK,OAASC,EAASD,EAAI,KAAK,MAAM,EAAGC,EAAS,CAAC,EAAI,MAAQD,EAAI,KAG9EG,EAAYH,EAAI,YAAc,YAAcA,EAAI,YAAc,OAChEI,GAAuBF,CAAI,EAC3BA,EAEEG,EAAkC,CACtC,QAASL,EAAI,OACb,KAAMG,EACN,WAAY,MACd,EAEIH,EAAI,UACNK,EAAO,oBAAsB,SAASL,EAAI,QAAS,EAAE,GAGnDA,EAAI,YAAc,QACpBK,EAAO,WAAa,OACpBA,EAAO,KAAOH,GACLF,EAAI,YAAc,UAC3B,OAAOK,EAAO,WACdA,EAAO,KAAOH,GAGhB,GAAI,CACF,IAAMI,EAAS,MAAM,KAAK,QAAQP,EAAO,cAAeM,CAAM,EAC9D,OAAO,OAAOC,EAAO,QAAQ,YAAc,EAAE,CAC/C,OAASV,EAAP,CAEA,GAAIS,EAAO,WAAY,CACrB,OAAOA,EAAO,WACdA,EAAO,KAAOH,EACd,IAAMI,EAAS,MAAM,KAAK,QAAQP,EAAO,cAAeM,CAAM,EAC9D,OAAO,OAAOC,EAAO,QAAQ,YAAc,EAAE,EAE/C,MAAMV,CACR,CACF,CAKA,MAAM,YAAYd,EAAgByB,EAAmBL,EAAcM,EAAoBzC,EAAsC,CAC3H,IAAMgC,EAAQ,KAAK,aAAajB,EAAQf,CAAS,EACjD,GAAI,CAACgC,EAAO,MAAO,GAEnB,IAAME,EAAS,KACTQ,EAAUP,EAAK,OAASD,EAASC,EAAK,MAAM,EAAGD,EAAS,CAAC,EAAI,MAAQC,EAErEC,EAAYK,IAAc,QAAUA,IAAc,QACpDJ,GAAuBK,CAAO,EAC9BA,EAEEJ,EAAkC,CACtC,QAASvB,EACT,WAAY,SAASyB,EAAW,EAAE,EAClC,KAAMJ,EACN,WAAY,MACd,EAEIK,IAAc,QAChBH,EAAO,WAAa,OACpBA,EAAO,KAAOI,GACLD,IAAc,UACvB,OAAOH,EAAO,WACdA,EAAO,KAAOI,GAGhB,GAAI,CACF,aAAM,KAAK,QAAQV,EAAO,kBAAmBM,CAAM,EAC5C,EACT,OAAST,EAAP,CACA,GAAIA,EAAE,SAAS,SAAS,yBAAyB,EAAG,MAAO,GAC3D,GAAIS,EAAO,WAAY,CACrB,OAAOA,EAAO,WACdA,EAAO,KAAOI,EACd,GAAI,CACF,aAAM,KAAK,QAAQV,EAAO,kBAAmBM,CAAM,EAC5C,EACT,MAAE,CAAQ,MAAO,EAAM,EAEzB,MAAO,EACT,CACF,CAKA,MAAM,MAAMvB,EAAgByB,EAAmBG,EAAgB,YAAM3C,EAAmC,CACtG,IAAMgC,EAAQ,KAAK,aAAajB,EAAQf,CAAS,EACjD,GAAKgC,EAEL,GAAI,CACF,MAAM,KAAK,QAAQA,EAAO,qBAAsB,CAC9C,QAASjB,EACT,WAAY,SAASyB,EAAW,EAAE,EAClC,SAAU,CAAC,CAAE,KAAM,QAAS,MAAAG,CAAM,CAAC,CACrC,CAAC,CACH,MAAE,CAEF,CACF,CAKA,MAAM,WAAW5B,EAAgBf,EAAmC,CAClE,IAAMgC,EAAQ,KAAK,aAAajB,EAAQf,CAAS,EACjD,GAAKgC,EAEL,GAAI,CACF,MAAM,KAAK,QAAQA,EAAO,iBAAkB,CAC1C,QAASjB,EACT,OAAQ,QACV,CAAC,CACH,MAAE,CAEF,CACF,CAIA,MAAc,SAASf,EAAmByB,EAA8C,CACtF,IAAImB,EAAoB,EAExB,KAAO,KAAK,SACV,GAAI,CACF,IAAMrC,EAAS,KAAK,YAAY,IAAIP,CAAS,GAAK,EAO5C6C,GANO,MAAM,KAAK,QAAQpB,EAAO,MAAO,aAAc,CAC1D,OAAQlB,GAAU,OAClB,QAAS,GACT,gBAAiB,CAAC,UAAW,gBAAgB,CAC/C,CAAC,GAEsC,QAAU,CAAC,EAElD,QAAWuC,KAAUD,EAAS,CAG5B,GAFA,KAAK,YAAY,IAAI7C,EAAW8C,EAAO,UAAY,CAAC,EAEhDA,EAAO,SAAW,KAAK,QAAS,CAClC,IAAMb,EAAMa,EAAO,QAGfX,EAAOF,EAAI,MAAQA,EAAI,SAAW,GAClCc,EAGEC,EAAWf,EAAI,OAASA,EAAI,MAAM,OAAS,EAC3CgB,EAAW,CAAC,CAAChB,EAAI,MACjBiB,EAAW,CAAC,CAACjB,EAAI,MACjBkB,EAAW,CAAC,CAAClB,EAAI,MACjBmB,EAAc,CAAC,CAACnB,EAAI,SAG1B,GAFiBe,GAAYC,GAAYC,GAAYC,GAAYC,EAEnD,CAEZ,IAAIC,EACAC,EAAO,2BAwBX,GAtBIN,GACFK,EAASpB,EAAI,MAAOA,EAAI,MAAO,OAAS,CAAC,EAAE,QAC3CqB,EAAO,aACFnB,IAAMA,EAAO,yDACTc,GACTI,EAASpB,EAAI,MAAO,QACpBqB,EAAOrB,EAAI,MAAO,WAAa,YAC1BE,IAAMA,EAAO,yDACTe,GACTG,EAASpB,EAAI,MAAO,QACpBqB,EAAOrB,EAAI,MAAO,WAAa,aAC1BE,IAAMA,EAAO,WAAWF,EAAI,MAAO,OAAS,kBACxCkB,GACTE,EAASpB,EAAI,MAAO,QACpBqB,EAAOrB,EAAI,MAAO,WAAa,YAC1BE,IAAMA,EAAO,qBACTiB,IACTC,EAASpB,EAAI,SAAU,QACvBqB,EAAOrB,EAAI,SAAU,WAAa,2BAC7BE,IAAMA,EAAO,cAAcF,EAAI,SAAU,WAAa,YAGzDoB,EACF,GAAI,CAGF,IAAME,GADW,MAAM,KAAK,QAAQ9B,EAAO,MAAO,UAAW,CAAE,QAAS4B,CAAO,CAAC,GACtD,QAAQ,UAClC,GAAIE,EAAU,CAEZ,IAAMC,EAAU,oCAAoC/B,EAAO,SAAS8B,IAC9DE,EAAM,MAAM,MAAMD,CAAO,EAC/B,GAAIC,EAAI,GAAI,CACV,IAAMC,EAAS,OAAO,KAAK,MAAMD,EAAI,YAAY,CAAC,EAC5CE,EAAML,EAAK,MAAM,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,EAAE,CAAC,GAAK,MAC3C,CAAE,UAAA9D,EAAW,cAAAD,CAAc,EAAI,KAAM,QAAO,IAAI,EAChD,CAAE,WAAAqE,EAAW,EAAI,KAAM,QAAO,QAAQ,EACtC,CAAE,QAAAlE,EAAS,KAAAmE,CAAK,EAAI,KAAM,QAAO,MAAM,EACvCC,EAAWpE,EAAQ,QAAQ,IAAI,EAAG,wBAAwB,EAChEF,EAAUsE,EAAU,CAAE,UAAW,EAAK,CAAC,EACvC,IAAMC,EAAW9B,EAAI,UAAU,WAAa,GAAG2B,GAAW,KAAKD,IACzDK,EAAYH,EAAKC,EAAUC,CAAQ,EACzCxE,EAAcyE,EAAWN,CAAM,EAC/BX,EAAY,CAAE,KAAMiB,EAAW,KAAMV,EAAM,SAAAS,CAAS,GAG1D,OAASlC,EAAP,CACA,KAAK,IAAI,0BAA0BA,EAAE,SAAS,CAChD,EAIJ,GAAI,CAACM,EAAM,SAKX,GAAI,CAAC,KAAK,UAAUnC,EAAWiC,EAAI,MAAM,GAAIA,EAAI,MAAM,GAAIA,EAAI,MAAM,QAAQ,EAAG,CAC9E,KAAK,IACH,aAAajC,2BAAmCiC,EAAI,MAAM,KAAKA,EAAI,MAAM,SAAW,MAAMA,EAAI,KAAK,YAAc,cAAcA,EAAI,MAAM,4BAC3I,EACA,SAIF,IAAMgC,EAAUhC,EAAI,KAAK,OAAS,UAC5BnC,EAAU,OAAOmC,EAAI,KAAK,EAAE,EAC9BiC,EACAD,IAEG,KAAK,WAAW,SAASnE,CAAO,GACnC,MAAM,KAAK,oBAAoBA,EAASmC,EAAI,KAAK,KAAK,EAExDiC,EAAc,MAAM,KAAK,eAAepE,CAAO,GAGjD,IAAMqE,EAA4B,CAChC,GAAI,OAAOlC,EAAI,UAAU,EACzB,QAAS,WACT,UAAAjC,EACA,OAAQ,CACN,GAAI,OAAOiC,EAAI,KAAK,EAAE,EACtB,KAAM,CAACA,EAAI,KAAK,WAAYA,EAAI,KAAK,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,EACxE,SAAUA,EAAI,KAAK,SACnB,MAAOA,EAAI,KAAK,SAAW,EAC7B,EACA,MAAOgC,EACH,CAAE,GAAInE,EAAS,KAAMmC,EAAI,KAAK,OAAS,EAAG,EAC1C,OACJ,KAAAE,EACA,MAAOY,EACP,QAASd,EAAI,iBACT,OAAOA,EAAI,iBAAiB,UAAU,EACtC,OACJ,YAAaA,EAAI,iBACZA,EAAI,iBAAiB,MAAQA,EAAI,iBAAiB,SAAW,iBAAiBA,EAAI,iBAAiB,MAAM,YAAc,aACxH,OACJ,UAAW,IAAI,KAAKA,EAAI,KAAO,GAAI,EACnC,IAAKa,EACL,YAAAoB,CACF,EAGA,KAAK,eAAe,IAAI,OAAOjC,EAAI,KAAK,EAAE,EAAGjC,CAAS,EAGtD,KAAK,QAAQmE,CAAQ,EAAE,MAAOtC,GAAM,CAClC,KAAK,IAAI,2BAA2BA,EAAE,SAAS,CACjD,CAAC,EAIH,GAAIiB,EAAO,eAAgB,CACzB,IAAMsB,EAAMtB,EAAO,eACb/B,EAAS,OAAOqD,EAAI,KAAK,EAAE,EAC3BC,EAAYD,EAAI,KAAK,MACrBjE,EAASiE,EAAI,gBAAgB,OAC7BxC,EAAcwC,EAAI,gBAAgB,KAAK,UAAYpE,EAEzD,KAAK,WAAW,aACde,EACAsD,EACArE,EACAyB,EAAO,aACPG,EACAzB,CACF,EAEA,KAAK,IAAI,sBAAsByB,aAAuBzB,UAAekE,GAAatD,IAAS,GAG/F6B,EAAoB,CACtB,OAASf,EAAP,CACAe,IACA,IAAM0B,EAAU,KAAK,IAAI,IAAO,KAAK,IAAI,EAAG1B,EAAoB,CAAC,EAAG,GAAK,EACzE,KAAK,IAAI,eAAe5C,OAAe6B,EAAE,qBAAqByC,EAAU,iBAAkB1B,IAAoB,EAC9G,MAAM,IAAI,QAAS,GAAM,WAAW,EAAG0B,CAAO,CAAC,CACjD,CAEJ,CAMA,MAAM,eAAevD,EAAkD,CACrE,IAAMwD,EAAO,KAAK,WAAW,aAAaxD,CAAM,EAChD,GAAKwD,EAAK,OAEV,MAAO,CACL,QAAS,WACT,OAAQA,EAAK,IAAIC,IAAM,CACrB,GAAIA,EAAE,QACN,KAAMA,EAAE,SACR,OAAQ,IAAIA,EAAE,UAChB,EAAE,EACF,MAAO,CAAC,GAAGD,EAAK,sCAAsC,CACxD,CACF,CAMA,MAAc,oBAAoBzE,EAAiB2E,EAAoC,CACrF,KAAK,IAAI,iCAAiCA,GAAc3E,IAAU,EAElE,OAAW,CAACE,EAAWyB,CAAM,IAAK,KAAK,SAAU,CAC/C,IAAMpB,EAAO,KAAK,QAAQ,IAAIL,CAAS,EACvC,GAAKK,EAEL,GAAI,CAKF,IAAMF,GAJM,MAAM,KAAK,QAAQsB,EAAO,MAAO,gBAAiB,CAC5D,QAAS,OAAO3B,CAAO,EACvB,QAASO,EAAK,MAChB,CAAC,GACkB,QAAQ,OACvBF,GACF,KAAK,WAAW,aACdL,EACA2E,EACAzE,EACAyB,EAAO,aACPpB,EAAK,SACLF,CACF,CAEJ,MAAE,CAEA,KAAK,WAAW,aACdL,EACA2E,EACAzE,EACAyB,EAAO,aACPpB,EAAK,SACL,MACF,CACF,EAGF,KAAK,IAAI,WAAW,KAAK,WAAW,aAAaP,CAAO,EAAE,qBAAqB2E,GAAc3E,IAAU,CACzG,CAEA,MAAc,QACZkC,EACA0C,EACApC,EACc,CACd,IAAMqC,EAAM,+BAA+B3C,KAAS0C,IAC9CjB,EAAM,MAAM,MAAMkB,EAAK,CAC3B,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAMrC,EAAS,KAAK,UAAUA,CAAM,EAAI,MAC1C,CAAC,EAED,GAAI,CAACmB,EAAI,GAAI,CACX,IAAMtB,EAAO,MAAMsB,EAAI,KAAK,EAC5B,MAAM,IAAI,MAAM,uBAAuBA,EAAI,UAAUtB,GAAM,EAG7D,OAAOsB,EAAI,KAAK,CAClB,CACF,ECzsBA,OAAqB,aAAAmB,GAAW,iBAAAC,OAAqB,KACrD,OAAS,WAAAC,GAAS,QAAAC,OAAY,OAC9B,OAAS,cAAAC,OAAkB,SAcpB,IAAMC,EAAN,KAAgD,CAC5C,KAAO,WACR,WACA,aACA,UACA,OACA,QACA,KAAY,KACZ,kBAAoB,EAEpB,WAAa,EACb,eAA8B,IAAI,IAClC,IAIA,KAEA,SAER,YACEC,EAQAC,EAAoC,QAAQ,MAAM,KAAK,QAAS,YAAY,EAC5E,CACA,KAAK,WAAaL,GAAQI,EAAO,UAAU,EAC3C,KAAK,aAAeA,EAAO,aAC3B,KAAK,UAAYA,EAAO,UACxB,KAAK,OAASA,EAAO,QAAU,CAAC,EAChC,KAAK,KAAOA,EAAO,KACnB,KAAK,SAAWA,EAAO,SACvB,KAAK,IAAMC,CACb,CAKA,aAAaC,EAAqBC,EAAoBC,EAAuC,CAC3F,QAAWC,KAAS,KAAK,OAAQ,CAE/B,GAAIA,EAAM,QAAS,CACjB,IAAMC,EAAaD,EAAM,QAAQ,QAAQ,MAAO,EAAE,EAClD,GAAIH,EAAY,SAASI,CAAU,GAAKA,EAAW,SAASJ,CAAW,EACrE,OAAOG,EAAM,MAIjB,GAAIA,EAAM,QAAUF,GAAaC,GAAW,CAC1C,IAAMG,EAAUF,EAAM,MAAM,YAAY,EACxC,GACEF,GAAW,YAAY,EAAE,SAASI,CAAO,GACzCH,GAAU,YAAY,EAAE,SAASG,CAAO,EAExC,OAAOF,EAAM,OAInB,OAAO,KAAK,YACd,CAEA,UAAUG,EAAwD,CAChE,KAAK,QAAUA,CACjB,CAEA,MAAM,OAAuB,CAE3B,IAAIC,EACAC,EACAC,EAEAC,EACJ,GAAI,CACFA,EAAU,KAAM,QAAO,yBAAyB,EAChDH,EAAeG,EAAQ,SAAWA,EAAQ,aAC1CF,EAAwBE,EAAQ,sBAChCD,EAAmBC,EAAQ,gBAC7B,MAAE,CACA,KAAK,IAAI,0DAA0D,EACnE,KAAK,IAAI,uCAAuC,EAChD,MACF,CAEAlB,GAAU,KAAK,WAAY,CAAE,UAAW,EAAK,CAAC,EAI9C,IAAMmB,EAAe,EAAE,KAAK,WAG5B,GAAI,KAAK,KAAM,CACb,GAAI,CAAE,KAAK,KAAK,IAAI,MAAS,CAAE,MAAE,CAAO,CACxC,KAAK,KAAO,KAGd,GAAM,CAAE,MAAAC,EAAO,UAAAC,CAAU,EAAI,MAAML,EAAsB,KAAK,UAAU,EAGpEM,EACJ,GAAI,CACF,GAAM,CAAE,QAASC,CAAE,EAAI,MAAML,EAAQ,0BAA0B,EAC/DI,EAAUC,EACV,KAAK,IAAI,yBAAyBA,EAAE,KAAK,GAAG,GAAG,CACjD,MAAE,CACA,KAAK,IAAI,2CAA2C,CACtD,CAGA,IAAMC,EAAe,CACnB,MAAO,SACP,MAAO,IAAM,CAAC,EAAG,MAAO,IAAM,CAAC,EAAG,KAAM,IAAM,CAAC,EAC/C,KAAM,IAAM,CAAC,EAAG,MAAO,IAAM,CAAC,EAC9B,MAAO,IAAIC,IAAgB,KAAK,IAAI,YAAa,GAAGA,CAAI,EACxD,MAAO,IAAMD,CACf,EAEA,KAAK,KAAOT,EAAa,CACvB,KAAM,CACJ,MAAOK,EAAM,MACb,KAAMF,EAAQ,4BACVA,EAAQ,4BAA4BE,EAAM,KAAMI,CAAY,EAC5DJ,EAAM,IACZ,EACA,GAAIE,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAC7B,OAAQE,EACR,kBAAmB,GACnB,QAAS,CAAC,SAAU,SAAU,KAAK,EACnC,gBAAiB,GACjB,oBAAqB,EACvB,CAAC,EAGD,KAAK,KAAK,GAAG,GAAG,eAAgBH,CAAS,EAGzC,KAAK,KAAK,GAAG,GAAG,wBAA0BK,GAAc,CACtD,KAAK,IAAI,oBAAoBA,EAAK,UAAU,QAAU,eAAeA,EAAK,SAAW,SAAW,WAAW,CAC7G,CAAC,EAGD,KAAK,KAAK,GAAG,GAAG,oBAAqB,MAAOC,GAAgB,CAC1D,GAAM,CAAE,WAAAC,EAAY,eAAAC,EAAgB,GAAAC,CAAG,EAAIH,EAC3C,GAAIR,IAAiB,KAAK,WAE1B,IAAIW,EAAI,CACN,KAAK,IAAI,wCAAwC,EAEjD,GAAI,CAAE,KAAK,OAAOA,CAAE,CAAE,MAAE,CAAwD,CAEhF,GAAI,CAEF,GAAM,CAAE,QAASC,CAAO,EAAI,KAAM,QAAO,iBAAiB,EAC1DA,EAAO,SAASD,EAAI,CAAE,MAAO,EAAK,CAAC,CACrC,MAAE,CAEA,KAAK,IAAI,OAAOA,GAAI,EACpB,KAAK,IAAI,oEAAoE,CAC/E,EAEF,GAAIF,EACF,GAAI,CAAE,KAAK,WAAWA,EAAYC,GAAgB,OAAO,OAAO,CAAE,MAAE,CAAc,CAEpF,GAAID,IAAe,OAGjB,GAAI,CAAE,KAAK,OAAO,IAAI,CAAE,MAAE,CAAc,CAG1C,GAAIA,IAAe,QAAS,CAC1B,IAAMI,EAAaH,GAAgB,OAAO,QAAQ,WAC5CI,EAASJ,GAAgB,OAAO,QAAQ,SAAS,SAAW,GAGlE,GAFA,KAAK,IAAI,uCAAuCG,cAAuBC,IAAS,EAE5ED,IAAef,GAAkB,WAAae,IAAe,IAC/D,KAAK,IAAI,2DAA2D,UAC3DA,IAAe,KAAOA,IAAe,IAAK,CAKnD,GAFA,KAAK,oBAED,KAAK,mBAAqB,EAAG,CAC/B,KAAK,IAAI,6BAA6B,KAAK,+GAA0G,EACrJ,OAGF,IAAME,EAAQ,KAAK,IAAI,IAAS,KAAK,IAAI,EAAG,KAAK,kBAAoB,CAAC,EAAG,IAAO,EAChF,KAAK,IAAI,mBAAmBA,EAAQ,iBAAkB,KAAK,yBAAyB,EACpF,WAAW,IAAM,KAAK,MAAM,EAAGA,CAAK,OAC3BF,IAAe,KAExB,KAAK,IAAI,qCAAqC,EAC9C,WAAW,IAAM,KAAK,MAAM,EAAG,GAAI,GAC1BA,IAAe,SACxB,KAAK,IAAI,uBAAuB,EAChC,WAAW,IAAM,KAAK,MAAM,EAAG,GAAI,GAKnCJ,IAAe,QAGjB,KAAK,IAAI,oBAAoB,EAEjC,CAAC,EAGD,KAAK,KAAK,GAAG,GAAG,kBAAmB,MAAOO,GAAW,CASnD,GAPI,KAAK,kBAAoB,IAC3B,KAAK,IAAI,gEAAgE,KAAK,oBAAoB,EAClG,KAAK,kBAAoB,GAG3B,KAAK,IAAI,uBAAuBA,EAAE,UAAU,QAAU,qBAAqBA,EAAE,MAAM,EAE/E,EAAC,KAAK,QAEV,QAAWC,KAAOD,EAAE,UAAY,CAAC,EAAG,CAClC,IAAME,GAAYD,EAAI,IAAI,WAAa,IAAI,QAAQ,MAAO,EAAE,EAAE,MAAM,EAAE,EAChEE,EAAU,CAAC,EAAEF,EAAI,SAAS,cAAgBA,EAAI,SAAS,qBAAqB,MAIlF,GAHA,KAAK,IAAI,gBAAgBC,YAAmBD,EAAI,IAAI,kBAAkBE,UAAgB,OAAO,KAAKF,EAAI,SAAW,CAAC,CAAC,EAAE,KAAK,GAAG,GAAG,EAG5HA,EAAI,IAAI,YAAc,mBAAoB,SAG9C,GAAIA,EAAI,IAAI,IAAM,KAAK,eAAe,IAAIA,EAAI,IAAI,EAAE,EAAG,CACrD,KAAK,eAAe,OAAOA,EAAI,IAAI,EAAE,EACrC,SAMF,GAAIA,EAAI,IAAI,OAAQ,CAClB,IAAMG,EAAK,KAAK,MAAM,KAChBC,EAAYJ,EAAI,IAAI,WAAa,GAEjCK,EAAQF,GAAI,IAAI,QAAQ,MAAO,EAAE,GAAK,GACtCG,EAAQH,GAAI,KAAK,QAAQ,MAAO,EAAE,GAAK,GACvCI,EAAWH,EAAU,QAAQ,MAAO,EAAE,EAAE,QAAQ,MAAO,EAAE,EAE/D,GAAI,EADgBG,IAAaF,GAAWE,IAAaD,GACxC,SAInB,IAAIE,EAAOR,EAAI,SAAS,cACnBA,EAAI,SAAS,qBAAqB,MAClCA,EAAI,SAAS,cAAc,SAC3BA,EAAI,SAAS,cAAc,SAC3B,GAGCS,EAAaT,EAAI,SAAW,CAAC,EAC7BU,EAAW,CAAC,CAACD,EAAW,aACxBE,EAAW,CAAC,CAACF,EAAW,aACxBG,EAAW,CAAC,CAACH,EAAW,aACxBI,EAAc,CAAC,CAACJ,EAAW,gBAC3BK,EAAa,CAAC,CAACL,EAAW,eAC1BM,EAAWL,GAAYC,GAAYC,GAAYC,GAAeC,EAWpE,GARIC,GAAY,CAACP,IACXE,EAAUF,EAAO,uDACZG,EAAUH,EAAO,gEACjBI,EAAUJ,EAAO,mBACjBK,EAAaL,EAAO,cAAcC,EAAW,iBAAiB,UAAY,UAC1EK,IAAYN,EAAO,cAG1B,CAACA,EAAM,SAEX,IAAMQ,EAAMhB,EAAI,IAAI,WAAa,GAC3BiB,EAAUD,EAAI,SAAS,OAAO,EAI9BE,EAAYF,EAAI,QAAQ,OAAQ,EAAE,EAElC5C,GADY6C,EAAWjB,EAAI,IAAI,aAAe,GAAMgB,GAC5B,QAAQ,OAAQ,EAAE,EAIhD,GAAI,KAAK,WAAW,QAAU,CAAChB,EAAI,IAAI,QAKjC,CAJY,KAAK,UAAU,KAAKmB,GAAK,CACvC,IAAM3C,EAAa2C,EAAE,QAAQ,MAAO,EAAE,EACtC,OAAO/C,EAAY,SAASI,CAAU,GAAK0C,EAAU,SAAS1C,CAAU,CAC1E,CAAC,EACa,SAGhB,IAAM4C,EAAapB,EAAI,IAAI,OAAS,KAAQA,EAAI,UAAY5B,EAGxDC,EACJ,GAAI4C,GAAW,KAAK,KAClB,GAAI,CAEF5C,GADiB,MAAM,KAAK,KAAK,cAAc2C,CAAG,GAC7B,OACvB,MAAE,CAAyC,CAI7C,IAAMK,EAAgB,KAAK,aAAajD,EAAaC,EAAW4C,EAAUD,EAAM,MAAS,EACzF,GAAI,CAACK,EAAe,CAClB,KAAK,IAAI,gBAAgBJ,EAAU,SAAS5C,GAAa2C,IAAQ5C,aAAuB,EACxF,SAIF,IAAIkD,EACJ,GAAIP,GAAY,KAAK,KACnB,GAAI,CAEF,IAAMQ,EAAS,MADC,KAAM,QAAO,yBAAyB,GACzB,qBAAqBvB,EAAK,SAAU,CAAC,EAAG,CACnE,gBAAiB,KAAK,KAAK,mBAC3B,OAAQ,KAAK,KAAK,MACpB,CAAC,EACD,GAAIuB,EAAQ,CACV,IAAMC,EAAOf,EAAW,cAAc,UACjCA,EAAW,cAAc,UAAY,YAKpCgB,EAAMD,EAAK,MAAM,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,EAAE,CAAC,GAAK,MAC3CE,EAAW5D,GAAQ,KAAK,WAAY,kBAAkB,EAC5DF,GAAU8D,EAAU,CAAE,UAAW,EAAK,CAAC,EACvC,IAAMC,GAAWlB,EAAW,iBAAiB,UAAY,GAAGzC,GAAW,KAAKyD,IACtEG,GAAW7D,GAAK2D,EAAUC,EAAQ,EACxC9D,GAAc+D,GAAUL,CAAM,EAC9BD,EAAY,CAAE,KAAMM,GAAU,KAAMJ,EAAM,SAAAG,EAAS,EACnD,KAAK,IAAI,mBAAmBH,QAAWI,IAAU,EAErD,OAASC,EAAP,CACA,KAAK,IAAI,6BAA6BA,EAAE,SAAS,CACnD,CAGF,IAAMC,GAA4B,CAChC,GAAI9B,EAAI,IAAI,IAAM,OAAO,KAAK,IAAI,CAAC,EACnC,QAAS,WACT,UAAW,UACX,OAAQ,CACN,GAAIA,EAAI,IAAI,QACP,KAAK,MAAM,MAAM,IAAI,QAAQ,MAAO,EAAE,GAAK5B,GAAe,kBAC3DA,EACJ,KAAMgD,EACN,SAAUhD,CACZ,EACA,MAAO6C,EAAU,CACf,GAAID,EACJ,KAAM3C,GAAa2C,CACrB,EAAI,OACJ,KAAAR,EACA,MAAOc,EACP,QAAStB,EAAI,SAAS,qBAAqB,aAAa,SACxD,UAAW,IAAI,MAAMA,EAAI,kBAAoB,GAAK,GAAI,EACtD,IAAKA,EACL,cAAAqB,CACF,EAEA,KAAK,QAAQS,EAAQ,EAAE,MAAOD,GAAW,CACvC,KAAK,IAAI,2BAA2BA,EAAE,SAAS,CACjD,CAAC,EAEL,CAAC,CACH,CAEA,MAAM,MAAsB,CACtB,KAAK,OACP,KAAK,KAAK,IAAI,EACd,KAAK,KAAO,KAEhB,CAEA,MAAM,KAAK7B,EAAuC,CAChD,GAAI,CAAC,KAAK,KACR,YAAK,IAAI,wBAAwB,EAC1B,GAGT,IAAMgB,EAAMhB,EAAI,OAAO,SAAS,GAAG,EAC/BA,EAAI,OACJ,GAAGA,EAAI,wBAEX,GAAI,CAEF,IAAM+B,GADO,MAAM,KAAK,KAAK,YAAYf,EAAK,CAAE,KAAMhB,EAAI,IAAK,CAAC,IAC3C,KAAK,IAAM,GAEhC,OAAI+B,GAAQ,KAAK,eAAe,IAAIA,CAAM,EACnCA,CACT,OAASF,EAAP,CACA,YAAK,IAAI,eAAeA,EAAE,SAAS,EAC5B,EACT,CACF,CAEA,MAAM,YAAYG,EAAgBC,EAAmBzB,EAAgC,CACnF,GAAI,CAAC,KAAK,KAAM,MAAO,GACvB,IAAMQ,EAAMgB,EAAO,SAAS,GAAG,EAAIA,EAAS,GAAGA,mBAC/C,GAAI,CACF,aAAM,KAAK,KAAK,YAAYhB,EAAK,CAC/B,KAAAR,EACA,KAAM,CAAE,UAAWQ,EAAK,GAAIiB,EAAW,OAAQ,EAAK,CACtD,CAAC,EACM,EACT,MAAE,CAAQ,MAAO,EAAM,CACzB,CAEA,MAAM,WAAWD,EAA+B,CAC9C,GAAI,CAAC,KAAK,KAAM,OAChB,IAAMhB,EAAMgB,EAAO,SAAS,GAAG,EAAIA,EAAS,GAAGA,mBAC/C,GAAI,CACF,MAAM,KAAK,KAAK,mBAAmB,YAAahB,CAAG,CACrD,MAAE,CAA0B,CAC9B,CAEA,MAAM,MAAMgB,EAAgBC,EAAmBC,EAAgB,YAAqB,CAClF,GAAI,CAAC,KAAK,KAAM,OAChB,IAAMlB,EAAMgB,EAAO,SAAS,GAAG,EAAIA,EAAS,GAAGA,mBAC/C,GAAI,CACF,MAAM,KAAK,KAAK,YAAYhB,EAAK,CAC/B,MAAO,CAAE,KAAMkB,EAAO,IAAK,CAAE,UAAWlB,EAAK,GAAIiB,CAAU,CAAE,CAC/D,CAAC,CACH,MAAE,CAA0B,CAC9B,CACF,EC/bAE,KAHA,OAAS,gBAAAC,OAA+D,OACxE,OAAS,iBAAAC,GAAe,cAAAC,GAAY,cAAAC,GAAY,aAAAC,GAAW,SAAAC,OAA6B,KACxF,OAAS,WAAAC,GAAS,WAAAC,OAAe,OCI1B,IAAMC,EAAN,KAA+C,CAC3C,KAAO,UACR,MACA,aACA,QACA,OAAc,KACd,IAER,YACEC,EACAC,EAAoC,QAAQ,MAAM,KAAK,QAAS,WAAW,EAC3E,CACA,KAAK,MAAQD,EAAO,MACpB,KAAK,aAAeA,EAAO,aAC3B,KAAK,IAAMC,CACb,CAEA,UAAUC,EAAwD,CAChE,KAAK,QAAUA,CACjB,CAEA,MAAM,OAAuB,CAC3B,IAAIC,EAEJ,GAAI,CAEFA,EAAU,KAAM,QAAO,YAAY,CACrC,MAAE,CACA,KAAK,IAAI,4CAA4C,EACrD,KAAK,IAAI,0BAA0B,EACnC,MACF,CAEA,GAAM,CAAE,OAAAC,EAAQ,kBAAAC,CAAkB,EAAIF,EAEtC,KAAK,OAAS,IAAIC,EAAO,CACvB,QAAS,CACPC,EAAkB,OAClBA,EAAkB,cAClBA,EAAkB,eAClBA,EAAkB,cACpB,CACF,CAAC,EAED,KAAK,OAAO,GAAG,QAAS,IAAM,CAC5B,KAAK,IAAI,wBAAwB,KAAK,OAAO,MAAM,KAAK,CAC1D,CAAC,EAED,KAAK,OAAO,GAAG,gBAAiB,MAAOC,GAAa,CAElD,GADI,CAAC,KAAK,SACNA,EAAI,OAAO,IAAK,OAGpB,IAAMC,EAAcD,EAAI,SAAS,MAAM,IAAI,KAAK,OAAO,MAAM,EAAE,EACzDE,EAAO,CAACF,EAAI,MAElB,GAAI,CAACC,GAAe,CAACC,EAAM,OAG3B,IAAIC,EAAOH,EAAI,QAKf,GAJI,KAAK,OAAO,OACdG,EAAOA,EAAK,QAAQ,IAAI,OAAO,OAAO,KAAK,OAAO,KAAK,MAAO,GAAG,EAAG,EAAE,EAAE,KAAK,GAG3E,CAACA,EAAM,OAEX,IAAMC,EAA4B,CAChC,GAAIJ,EAAI,GACR,QAAS,UACT,UAAW,UACX,OAAQ,CACN,GAAIA,EAAI,OAAO,GACf,KAAMA,EAAI,OAAO,aAAeA,EAAI,OAAO,SAC3C,SAAUA,EAAI,OAAO,QACvB,EACA,MAAOA,EAAI,MAAQ,CACjB,GAAIA,EAAI,UACR,KAAMA,EAAI,SAAS,MAAQA,EAAI,SACjC,EAAI,OACJ,KAAAG,EACA,QAASH,EAAI,WAAW,UACxB,UAAWA,EAAI,UACf,IAAKA,CACP,EAEA,KAAK,QAAQI,CAAQ,EAAE,MAAOC,GAAW,CACvC,KAAK,IAAI,2BAA2BA,EAAE,SAAS,CACjD,CAAC,CACH,CAAC,EAED,GAAI,CACF,MAAM,KAAK,OAAO,MAAM,KAAK,KAAK,CACpC,OAASA,EAAP,CACA,KAAK,IAAI,yBAAyBA,EAAE,SAAS,CAC/C,CACF,CAEA,MAAM,MAAsB,CACtB,KAAK,SACP,KAAK,OAAO,QAAQ,EACpB,KAAK,OAAS,KAElB,CAEA,MAAM,KAAKL,EAAuC,CAChD,GAAI,CAAC,KAAK,OAAQ,MAAO,GACzB,GAAI,CACF,IAAMM,EAAU,MAAM,KAAK,OAAO,SAAS,MAAMN,EAAI,MAAM,EAC3D,OAAKM,GAAS,YAAY,GAEb,MAAMA,EAAQ,KAAK,CAC9B,QAASN,EAAI,KACb,GAAIA,EAAI,QAAU,CAAE,MAAO,CAAE,iBAAkBA,EAAI,OAAQ,CAAE,EAAI,CAAC,CACpE,CAAC,GACW,GANwB,EAOtC,OAASK,EAAP,CACA,YAAK,IAAI,eAAeA,EAAE,SAAS,EAC5B,EACT,CACF,CAEA,MAAM,YAAYE,EAAgBC,EAAmBL,EAAgC,CACnF,GAAI,CAAC,KAAK,OAAQ,MAAO,GACzB,GAAI,CACF,IAAMG,EAAU,MAAM,KAAK,OAAO,SAAS,MAAMC,CAAM,EACvD,OAAKD,GAAS,YAAY,GAE1B,MADgB,MAAMA,EAAQ,SAAS,MAAME,CAAS,GACxC,KAAKL,CAAI,EAChB,IAH6B,EAItC,MAAE,CAAQ,MAAO,EAAM,CACzB,CAEA,MAAM,WAAWI,EAA+B,CAC9C,GAAK,KAAK,OACV,GAAI,CACF,IAAMD,EAAU,MAAM,KAAK,OAAO,SAAS,MAAMC,CAAM,EACnDD,GAAS,YAAY,GAAG,MAAMA,EAAQ,WAAW,CACvD,MAAE,CAA0B,CAC9B,CAEA,MAAM,MAAMC,EAAgBC,EAAmBC,EAAgB,YAAqB,CAClF,GAAK,KAAK,OACV,GAAI,CACF,IAAMH,EAAU,MAAM,KAAK,OAAO,SAAS,MAAMC,CAAM,EACvD,GAAI,CAACD,GAAS,YAAY,EAAG,OAE7B,MADgB,MAAMA,EAAQ,SAAS,MAAME,CAAS,GACxC,MAAMC,CAAK,CAC3B,MAAE,CAA0B,CAC9B,CACF,ECpIO,IAAMC,GAAN,KAA6C,CACzC,KAAO,QACR,SACA,SACA,aACA,QACA,OAAc,KACd,IAAW,KACX,UACA,IAER,YACEC,EACAC,EAAoC,QAAQ,MAAM,KAAK,QAAS,SAAS,EACzE,CACA,KAAK,SAAWD,EAAO,SACvB,KAAK,SAAWA,EAAO,SACvB,KAAK,aAAeA,EAAO,aAC3B,KAAK,IAAMC,CACb,CAEA,UAAUC,EAAwD,CAChE,KAAK,QAAUA,CACjB,CAEA,MAAM,OAAuB,CAC3B,IAAIC,EACAC,EACJ,GAAI,CAEFD,EAAgB,KAAM,QAAO,oBAAoB,EAEjDC,EAAY,KAAM,QAAO,gBAAgB,CAC3C,MAAE,CACA,KAAK,IAAI,qEAAqE,EAC9E,KAAK,IAAI,iDAAiD,EAC1D,MACF,CAEA,GAAM,CAAE,iBAAAC,CAAiB,EAAIF,EACvB,CAAE,UAAAG,CAAU,EAAIF,EAEtB,KAAK,IAAM,IAAIE,EAAU,KAAK,QAAQ,EAGtC,GAAI,CACF,IAAMC,EAAa,MAAM,KAAK,IAAI,KAAK,KAAK,EAC5C,KAAK,UAAYA,EAAW,QAC5B,KAAK,IAAI,uBAAuBA,EAAW,SAAS,KAAK,sBAAsBA,EAAW,MAAM,CAClG,OAASC,EAAP,CACA,KAAK,IAAI,2BAA2BA,EAAE,SAAS,EAC/C,MACF,CAEA,KAAK,OAAS,IAAIH,EAAiB,CAAE,SAAU,KAAK,QAAS,CAAC,EAK9D,IAAMI,EAAS,MAAOC,EAAcC,IAA6B,CAC/D,GAAI,CAAE,MAAMA,EAAI,CAAE,MAAE,CAAc,CAClC,GAAI,CAAC,KAAK,QAAS,OACnB,IAAMC,EAAQF,EAAQ,MAItB,GAHI,CAACE,GAAS,OAAOA,EAAM,MAAS,UAGhCA,EAAM,QAAUA,EAAM,OAAS,KAAK,WAAaA,EAAM,UAAY,cAAe,OAEtF,IAAMC,EAAOD,EAAM,eAAiB,KAC9BE,EAAc,KAAK,WAAaF,EAAM,KAAK,SAAS,KAAK,KAAK,YAAY,EAChF,GAAI,CAACC,GAAQ,CAACC,EAAa,OAG3B,IAAIC,EAAOH,EAAM,KAIjB,GAHI,KAAK,YACPG,EAAOA,EAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,aAAc,GAAG,EAAG,EAAE,EAAE,KAAK,GAEpE,CAACA,EAAM,OAGX,IAAIC,EAAaJ,EAAM,MAAQ,aAC/B,GAAI,CACF,IAAMK,EAAO,MAAM,KAAK,IAAI,MAAM,KAAK,CAAE,KAAML,EAAM,IAAK,CAAC,EAC3DI,EAAaC,GAAM,MAAM,SAAS,cAAgBA,GAAM,MAAM,WAAaD,CAC7E,MAAE,CAAc,CAEhB,IAAME,EAA4B,CAChC,GAAIN,EAAM,eAAiBA,EAAM,GACjC,QAAS,QACT,UAAW,UACX,OAAQ,CACN,GAAIA,EAAM,KACV,KAAMI,EACN,SAAUA,CACZ,EACA,MAAQH,EAAoD,OAA7C,CAAE,GAAID,EAAM,QAAS,KAAMA,EAAM,OAAQ,EACxD,KAAAG,EACA,QAASH,EAAM,WAAaA,EAAM,YAAcA,EAAM,GAAKA,EAAM,UAAY,OAC7E,UAAWA,EAAM,GAAK,IAAI,KAAK,WAAWA,EAAM,EAAE,EAAI,GAAI,EAAI,IAAI,KAClE,IAAKA,CACP,EACA,GAAI,CAAE,MAAM,KAAK,QAAQM,CAAQ,CAAE,OAC5BV,EAAP,CAAiB,KAAK,IAAI,2BAA2BA,EAAE,SAAS,CAAE,CACpE,EAEA,KAAK,OAAO,GAAG,cAAeC,CAAM,EACpC,KAAK,OAAO,GAAG,UAAWA,CAAM,EAChC,KAAK,OAAO,GAAG,QAAUD,GAAW,KAAK,IAAI,uBAAuBA,GAAG,SAAWA,GAAG,CAAC,EAEtF,GAAI,CACF,MAAM,KAAK,OAAO,MAAM,CAC1B,OAASA,EAAP,CACA,KAAK,IAAI,8BAA8BA,EAAE,SAAS,CACpD,CACF,CAEA,MAAM,MAAsB,CAC1B,GAAI,KAAK,OAAQ,CACf,GAAI,CAAE,MAAM,KAAK,OAAO,WAAW,CAAE,MAAE,CAAc,CACrD,KAAK,OAAS,KAEhB,KAAK,IAAM,IACb,CAEA,MAAM,KAAKW,EAAuC,CAChD,GAAI,CAAC,KAAK,IAAK,MAAO,GACtB,GAAI,CASF,OARU,MAAM,KAAK,IAAI,KAAK,YAAY,CACxC,QAASA,EAAI,OACb,KAAMA,EAAI,KACV,UAAWA,EAAI,QAGf,OAAQA,EAAI,YAAc,OAC5B,CAAC,GACQ,EACX,OAASX,EAAP,CACA,YAAK,IAAI,eAAeA,EAAE,SAAS,EAC5B,EACT,CACF,CAEA,MAAM,YAAYY,EAAgBC,EAAmBN,EAAgC,CACnF,GAAI,CAAC,KAAK,IAAK,MAAO,GACtB,GAAI,CACF,aAAM,KAAK,IAAI,KAAK,OAAO,CAAE,QAASK,EAAQ,GAAIC,EAAW,KAAAN,CAAK,CAAC,EAC5D,EACT,OAASP,EAAP,CACA,YAAK,IAAI,eAAeA,EAAE,SAAS,EAC5B,EACT,CACF,CAEA,MAAM,MAAMY,EAAgBC,EAAmBC,EAAgB,OAAuB,CACpF,GAAK,KAAK,IACV,GAAI,CACF,IAAMC,EAAOD,EAAM,QAAQ,SAAU,EAAE,EACvC,MAAM,KAAK,IAAI,UAAU,IAAI,CAAE,QAASF,EAAQ,UAAWC,EAAW,KAAAE,CAAK,CAAC,CAC9E,MAAE,CAAoE,CACxE,CACF,ECtLA,OAAS,gBAAAC,OAA8E,OAwGhF,IAAMC,GAAN,KAA8C,CAC1C,KAAO,SACR,OACA,QACA,OACA,YACA,aAA4B,IAAI,IAChC,YAA2B,IAAI,IAE/B,gBAAuC,IAAI,IAE3C,gBAAuC,IAAI,IAC3C,IACA,MACA,eACA,kBACA,iBAER,YAAYC,EAA6BC,EAAoC,QAAQ,MAAM,KAAK,QAAS,UAAU,EAAGC,EAAsB,CAC1I,KAAK,OAASF,EACd,KAAK,IAAMC,EACX,KAAK,MAAQC,CACf,CAEA,kBAAkBC,EAAwI,CACxJ,KAAK,eAAiBA,CACxB,CAEA,qBAAqBA,EAAwI,CAC3J,KAAK,kBAAoBA,CAC3B,CAEA,oBAAoBA,EAA4I,CAC9J,KAAK,iBAAmBA,CAC1B,CAEA,UAAUC,EAAwD,CAChE,KAAK,QAAUA,CACjB,CAEA,MAAM,OAAuB,CAI3B,IAAMC,EAA2D,CAAC,EAE9D,KAAK,OAAO,OACdA,EAAgB,KAAK,CAAE,MAAO,SAAU,MAAO,KAAK,OAAO,KAAM,CAAC,EAEpE,QAAWC,KAAW,KAAK,OAAO,eAAiB,CAAC,EAC9CA,EAAQ,OACVD,EAAgB,KAAK,CAAE,MAAOC,EAAQ,QAAS,MAAOA,EAAQ,KAAM,CAAC,EAKzE,IAAMC,EAAc,MAAM,QAAQ,WAChCF,EAAgB,IAAI,MAAO,CAAE,MAAAG,EAAO,MAAAC,CAAM,IAAM,CAC9C,IAAMC,EAAM,MAAM,MAAM,GAAG,KAAK,OAAO,mBAAoB,CACzD,QAAS,CAAE,gBAAiBD,CAAM,CACpC,CAAC,EACD,GAAI,CAACC,EAAI,GAAI,MAAM,IAAI,MAAM,GAAGA,EAAI,QAAQ,EAC5C,IAAMC,EAAO,MAAMD,EAAI,KAAK,EAC5B,MAAO,CAAE,MAAAF,EAAO,SAAUG,EAAK,QAAmB,CACpD,CAAC,CACH,EAEA,QAAWC,KAAUL,EACnB,GAAIK,EAAO,SAAW,YAAa,CACjC,GAAM,CAAE,MAAAJ,EAAO,SAAAK,CAAS,EAAID,EAAO,MACnC,KAAK,aAAa,IAAIC,CAAQ,EAC1BL,IAAU,UACZ,KAAK,YAAcK,EACnB,KAAK,IAAI,oBAAoBA,GAAU,IAGvC,KAAK,gBAAgB,IAAIA,EAAS,YAAY,EAAGL,CAAK,EACtD,KAAK,gBAAgB,IAAIA,EAAOK,EAAS,YAAY,CAAC,EACtD,KAAK,IAAI,UAAUL,sBAA0BK,GAAU,GAO7D,QAAWP,KAAW,KAAK,OAAO,eAAiB,CAAC,EAAG,CACrD,QAAWO,KAAYP,EAAQ,gBAC7B,KAAK,aAAa,IAAIO,CAAQ,GAI1B,CADe,KAAK,gBAAgB,IAAIA,EAAS,YAAY,CAAC,GAC/CP,EAAQ,OACzB,KAAK,gBAAgB,IAAIO,EAAS,YAAY,EAAGP,EAAQ,OAAO,EAIhE,CAAC,KAAK,gBAAgB,IAAIA,EAAQ,OAAO,GAAKA,EAAQ,gBAAgB,OAAS,GACjF,KAAK,gBAAgB,IAAIA,EAAQ,QAASA,EAAQ,gBAAgB,CAAC,EAAE,YAAY,CAAC,EAItF,KAAK,IAAI,cAAc,KAAK,aAAa,UAAU,CAAC,GAAG,KAAK,YAAY,EAAE,KAAK,IAAI,GAAG,EACtF,KAAK,IAAI,wBAAwB,CAAC,GAAG,KAAK,gBAAgB,QAAQ,CAAC,EAAE,IAAI,CAAC,CAACQ,EAAGC,CAAC,IAAM,IAAID,MAAMC,GAAG,EAAE,KAAK,IAAI,GAAG,EAEhH,KAAK,OAASC,GAAa,MAAOC,EAAKP,IAAQ,CACzCO,EAAI,SAAW,OACjB,MAAM,KAAK,cAAcA,EAAKP,CAAG,GAEjCA,EAAI,UAAU,IAAK,CAAE,eAAgB,YAAa,CAAC,EACnDA,EAAI,IAAI,4CAA4C,EAExD,CAAC,EAED,KAAK,OAAO,GAAG,QAAUQ,GAAa,CAChCA,EAAI,OAAS,cACf,KAAK,IAAI,uBAAuB,KAAK,OAAO,uCAAuC,EACnF,WAAW,IAAM,CACf,KAAK,QAAQ,MAAM,EACnB,KAAK,QAAQ,OAAO,KAAK,OAAO,WAAW,CAC7C,EAAG,GAAI,GAEP,KAAK,IAAI,gCAAgCA,EAAI,SAAS,CAE1D,CAAC,EAED,KAAK,OAAO,OAAO,KAAK,OAAO,YAAa,IAAM,CAChD,KAAK,IAAI,gCAAgC,KAAK,OAAO,aAAa,CACpE,CAAC,CACH,CAEA,MAAM,MAAsB,CACtB,KAAK,QAAQ,KAAK,OAAO,MAAM,CACrC,CAMA,MAAM,KAAKC,EAAuC,CAEhD,IAAMC,EAAQD,EAAI,OAAO,MAAM,GAAG,EAClC,GAAIC,EAAM,OAAS,EACjB,YAAK,IAAI,oCAAoCD,EAAI,QAAQ,EAClD,GAET,IAAME,EAAMD,EAAM,IAAI,EAChBE,EAAeF,EAAM,IAAI,EACzBG,EAAUH,EAAM,KAAK,GAAG,EAExBI,EAAiB,mBAAmBD,CAAO,EAC7CE,EAEJ,OAAQH,EAAc,CACpB,IAAK,QACHG,EAAW,GAAG,KAAK,OAAO,wBAAwBD,YAAyBH,UAC3E,MACF,IAAK,gBACHI,EAAW,GAAG,KAAK,OAAO,wBAAwBD,oBAAiCH,UACnF,MACF,QACE,YAAK,IAAI,8BAA8BC,GAAc,EAC9C,EACX,CAYA,IAAMhB,EAAU,KAAK,OAAO,eAAe,KAAMoB,GAAMA,EAAE,UAAYP,EAAI,OAAO,EAC1EQ,EAAa,KAAK,cAAcR,EAAI,OAAO,EACjD,GAAI,CAACQ,GAAcrB,GAAS,MAAQ,KAAK,kBACvC,GAAI,CACF,IAAMsB,EAAO,GAAGT,EAAI;AAAA;AAAA,cAAuBA,EAAI,SAAW,gBACpDU,EAAS,MAAM,KAAK,kBAAkBvB,EAAQ,KAAMiB,EAASD,EAAcD,EAAKF,EAAI,SAAW,GAAIS,CAAI,EAC7G,OAAIC,GAAQ,KAAK,YAAY,IAAIA,CAAM,EAChCA,CACT,OAASC,EAAP,CACA,YAAK,IAAI,2BAA2BxB,EAAQ,iBAAiBwB,EAAE,mDAA8C,EACtG,EACT,CAGF,IAAMrB,EAAQkB,GAAc,KAAK,OAAO,MACxCI,EAAM,QAAQ,SAAU,OAAQ,YAAYZ,EAAI,kBAAkBQ,EAAa,YAAc,UAAY,KAAK,YAAc,KAAK,EAEjI,GAAI,CACF,IAAMjB,EAAM,MAAM,MAAMe,EAAU,CAChC,OAAQ,OACR,QAAS,CACP,eAAgB,mBAChB,gBAAiBhB,CACnB,EAEA,KAAM,KAAK,UAAU,CAAE,KAAM,GAAGU,EAAI;AAAA;AAAA,cAAuBA,EAAI,SAAW,eAAgB,CAAC,CAC7F,CAAC,EAED,GAAI,CAACT,EAAI,GAAI,CACX,IAAMsB,EAAO,MAAMtB,EAAI,KAAK,EAC5B,YAAK,IAAI,qBAAqBA,EAAI,UAAUsB,GAAM,EAC3C,GAGT,IAAMrB,EAAO,MAAMD,EAAI,KAAK,EACtBmB,EAAS,OAAOlB,EAAK,IAAM,EAAE,EACnC,OAAIkB,GAAQ,KAAK,YAAY,IAAIA,CAAM,EAChCA,CACT,OAASC,EAAP,CACA,YAAK,IAAI,sBAAsBA,EAAE,SAAS,EACnC,EACT,CACF,CAIA,MAAc,cAAcb,EAAkBP,EAAoC,CAEhF,GAAI,KAAK,OAAO,eACAO,EAAI,QAAQ,gBAAgB,IAC5B,KAAK,OAAO,cAAe,CACvCP,EAAI,UAAU,IAAK,CAAE,eAAgB,kBAAmB,CAAC,EACzDA,EAAI,IAAI,KAAK,UAAU,CAAE,MAAO,eAAgB,CAAC,CAAC,EAClD,OAKJ,IAAMuB,EADO,MAAM,KAAK,SAAShB,CAAG,EAE9BiB,EAAeD,EAAc,aAAgBA,EAAc,YAAc,UAK/E,OAHA,KAAK,IAAI,UAAUC,UAAoBD,EAAc,SAAS,qBAAuB,WAAW,EAGxFC,EAAY,CAClB,IAAK,OACH,MAAM,KAAK,WAAWD,EAA0BvB,CAAG,EACnD,MACF,IAAK,QACH,MAAM,KAAK,YAAYuB,EAA2BvB,CAAG,EACrD,MACF,IAAK,gBACH,MAAM,KAAK,SAASuB,EAAwBvB,CAAG,EAC/C,MACF,IAAK,WACH,MAAM,KAAK,eAAeuB,EAA8BvB,CAAG,EAC3D,MACF,QACE,KAAK,IAAI,oBAAoBwB,GAAY,EACzCxB,EAAI,UAAU,GAAG,EACjBA,EAAI,IAAI,IAAI,CAChB,CACF,CAMA,MAAc,WAAWuB,EAAwBvB,EAAoC,CACnF,GAAI,CAAC,KAAK,QAAS,CAAEA,EAAI,UAAU,GAAG,EAAGA,EAAI,IAAI,IAAI,EAAG,OAExD,IAAMyB,EAAOF,EAAM,kBAAkB,KAC/BV,EAAUU,EAAM,QAAQ,oBACxBG,EAAOH,EAAM,KACbJ,EAAS,OAAOI,EAAM,kBAAkB,EAAE,EAM1CI,EAAiBF,EAAK,MAAM,uBAAuB,EACzD,GAAIE,EAAgB,CAClB,IAAMC,EAAcD,EAAe,CAAC,EAE9BE,EAAWJ,EAAK,MAAM,eAAe,GAAG,IAAIT,GAAKA,EAAE,MAAM,CAAC,EAAE,QAAQ,QAAS,EAAE,CAAC,GAAK,CAAC,EAK5F,GAAI,CAJ2Ba,EAAS,KAAKb,GAAK,CAChD,IAAMc,EAAc,KAAK,gBAAgB,IAAId,EAAE,YAAY,CAAC,EAC5D,OAAOc,GAAeA,IAAgBF,CACxC,CAAC,EAC4B,CAC3B,KAAK,IAAI,uBAAuBA,6CAAuDT,IAAS,EAChGnB,EAAI,UAAU,GAAG,EAAGA,EAAI,IAAI,IAAI,EAAG,OAErC,KAAK,IAAI,uBAAuB4B,YAAiBC,EAAS,KAAK,IAAI,WAAWV,IAAS,EAIzF,GAAI,KAAK,YAAY,IAAIA,CAAM,EAAG,CAChC,KAAK,YAAY,OAAOA,CAAM,EAC9BnB,EAAI,UAAU,GAAG,EAAGA,EAAI,IAAI,IAAI,EAAG,OAKrC,IAAM6B,EAAWJ,EAAK,MAAM,eAAe,GAAG,IAAIT,GAAKA,EAAE,MAAM,CAAC,EAAE,QAAQ,QAAS,EAAE,CAAC,GAAK,CAAC,EAC5F,GAAIa,EAAS,SAAW,EAAG,CACzB,KAAK,IAAI,uBAAuBV,aAAkB,EAClDnB,EAAI,UAAU,GAAG,EAAGA,EAAI,IAAI,IAAI,EAAG,OAGrC,GAAI,KAAK,UAAU0B,EAAK,QAAQ,GAAK,CAACC,EAAgB,CACpD,KAAK,IAAI,oBAAoBD,EAAK,8CAA8CP,IAAS,EACzFnB,EAAI,UAAU,GAAG,EAAGA,EAAI,IAAI,IAAI,EAAG,OAIrC,IAAIY,EAAe,GACfmB,EAAc,GACdC,EAAgB,GAChBT,EAAM,OACRX,EAAe,QACfmB,EAAc,OAAOR,EAAM,MAAM,GAAG,EACpCS,EAAgBT,EAAM,MAAM,OACnBA,EAAM,gBACfX,EAAe,gBACfmB,EAAc,OAAOR,EAAM,cAAc,GAAG,EAC5CS,EAAgBT,EAAM,cAAc,OAItCF,EAAM,QAAQ,SAAU,aAAc,UAAUF,eAAoBU,EAAS,KAAK,GAAG,WAAWH,EAAK,UAAU,EAE/G,IAAMO,EAAkB,KAAK,wBAAwBR,CAAI,EAGzD,GAFAJ,EAAM,QAAQ,SAAU,UAAW,WAAWY,GAAmB,QAAQ,EAErE,CAACA,EAAiB,CACpB,KAAK,IAAI,kCAAkCd,qBAA0BU,EAAS,KAAK,IAAI,cAAc,EACrG7B,EAAI,UAAU,GAAG,EAAGA,EAAI,IAAI,IAAI,EAAG,OAGrC,IAAMkC,EAAgBD,EAGhBE,EAAe,KAAK,OAAO,eAAe,KAAKnB,GAAKA,EAAE,UAAYkB,CAAa,EAC/EjB,EAAakB,GAAc,MAC3BC,EAAaD,GAAsB,KACzCd,EAAM,QAAQ,SAAU,QAAS,UAAUa,cAA0BC,EAAe,QAAU,sBAAsB,CAAC,CAAClB,UAAmBmB,GAAa,SAAS,EAC/J,KAAK,YAAYvB,EAASD,EAAcmB,EAAaR,EAAM,kBAAkB,GAAIN,EAAYmB,EAAWF,CAAa,EAAE,MAAM,IAAM,CAAC,CAAC,EAErI,IAAMG,EAAS,GAAGxB,KAAWD,KAAgBmB,IAEvCO,EAAc,MAAM,KAAK,eAAeD,CAAM,EAG9CE,EAAYd,EAAK,QAAQ,0BAA2B,EAAE,EACtDe,EAAkB,MAAM,KAAK,mBAAmBD,EAAW1B,EAASI,GAAc,KAAK,OAAO,KAAK,EAEnGwB,EAA4B,CAChC,GAAI,OAAOlB,EAAM,kBAAkB,EAAE,EACrC,QAAS,SACT,UAAW,UACX,OAAQ,CACN,GAAIc,EACJ,KAAMX,EAAK,KACX,SAAUA,EAAK,QACjB,EACA,KAAM,WAAWd,MAAiBmB,MAAgBC;AAAA,EAAmBN,EAAK;AAAA,EAAoBa,IAC9F,UAAW,IAAI,KACf,IAAKhB,EACL,cAAeW,EACf,WAAYC,GAAc,KAC1B,YAAaG,EAAc,CAAE,GAAGA,EAAa,MAAO,CAAE,KAAM1B,EAAc,IAAKmB,EAAa,MAAOC,CAAc,CAAE,EAAI,OACvH,MAAOQ,CACT,EAEA,KAAK,QAAQC,CAAQ,EAAE,MAAOrB,GAAM,CAClC,KAAK,IAAI,wBAAwBA,EAAE,SAAS,CAC9C,CAAC,EAEDpB,EAAI,UAAU,GAAG,EACjBA,EAAI,IAAI,IAAI,CACd,CAMA,MAAc,YAAYuB,EAAyBvB,EAAoC,CACrF,GAAI,CAAC,KAAK,QAAS,CAAEA,EAAI,UAAU,GAAG,EAAGA,EAAI,IAAI,IAAI,EAAG,OAGxD,GAAI,KAAK,UAAUuB,EAAM,KAAK,QAAQ,EAAG,CACvCvB,EAAI,UAAU,GAAG,EAAGA,EAAI,IAAI,IAAI,EAAG,OAGrC,IAAM0C,EAAQnB,EAAM,kBACdV,EAAUU,EAAM,QAAQ,oBACxBoB,EAAiB,KAAK,aAAa9B,CAAO,EAW5C+B,EACAC,EAAc,GAClB,GAAI,KAAK,OAAO,IAAI,iBAAwB,EAC1C,GAAI,CACF,IAAM3C,EAAS,MAAM,KAAK,MAAM,QAAQ,kBAA0B,CAChE,MAAO,kBACP,WAAYqB,EACZ,QAAAV,EACA,IAAK6B,EAAM,IACX,MAAOA,EAAM,MACb,YAAaA,EAAM,aAAe,GAClC,IAAKA,EAAM,IACX,OAAQA,EAAM,OACd,gBAAiB,OAAO,YAAY,KAAK,gBAAgB,QAAQ,CAAC,EAClE,cAAe,KAAK,OAAO,eAAiB,CAAC,EAC7C,eAAgBC,GAAkB,IACpC,CAAC,EACGzC,EAAO,UACT2C,EAAc,IAEhB,IAAMC,EAAe5C,EAAO,UAAkB,SAC1C,MAAM,QAAQ4C,CAAW,GAAKA,EAAY,OAAS,IACrDF,EAAWE,EAAY,OAAQC,GAAWA,GAAK,OAAOA,EAAE,SAAY,QAAQ,EAEhF,OAAS3B,EAAP,CACA,KAAK,IAAI,+BAA+BA,EAAE,SAAS,CACrD,CAIF,GAAIwB,GAAYA,EAAS,OAAS,EAAG,CACnC,QAAWG,KAAKH,EAAU,CACxB,IAAMhD,EAAU,KAAK,OAAO,eAAe,KAAMoB,GAAMA,EAAE,UAAY+B,EAAE,OAAO,EACxEV,EAAS,GAAGxB,WAAiB6B,EAAM,MACnCJ,EAAc,MAAM,KAAK,eAAeD,CAAM,EAE9CI,EAA4B,CAChC,GAFS,cAAcC,EAAM,OAAOK,EAAE,UAAUA,EAAE,SAAW,IAAIA,EAAE,WAAa,MAAML,EAAM,SAG5F,QAAS,SACT,UAAW,UACX,OAAQ,CACN,GAAIL,EACJ,KAAMd,EAAM,KAAK,KACjB,SAAUA,EAAM,KAAK,QACvB,EACA,KAAMwB,EAAE,QAAU,kBAAkBL,EAAM,OAAOA,EAAM,YAAYA,EAAM;AAAA,EAAUA,EAAM,aAAa,MAAM,EAAG,GAAG,GAAK;AAAA,OAAYA,EAAM,MACzI,UAAW,IAAI,KACf,IAAKnB,EACL,cAAewB,EAAE,QACjB,WAAYA,EAAE,YAAcnD,GAAS,KACrC,YAAa0C,EAAc,CACzB,GAAGA,EACH,MAAO,CAAE,KAAM,QAAS,IAAK,OAAOI,EAAM,GAAG,EAAG,MAAOA,EAAM,KAAM,CACrE,EAAI,MACN,EACA,KAAK,IAAI,UAAUA,EAAM,+BAA+BK,EAAE,WAAWA,EAAE,YAAcnD,GAAS,KAAO,aAAamD,EAAE,YAAcnD,GAAS,QAAU,IAAI,EACzJ,KAAK,QAAQ6C,CAAQ,EAAE,MAAOrB,GAAM,KAAK,IAAI,iCAAiCA,EAAE,SAAS,CAAC,EAE5FpB,EAAI,UAAU,GAAG,EAAGA,EAAI,IAAI,IAAI,EAAG,OAGrC,GAAI6C,EAAa,CACf,KAAK,IAAI,UAAUH,EAAM,uDAAuD,EAChF1C,EAAI,UAAU,GAAG,EAAGA,EAAI,IAAI,IAAI,EAAG,OAIrC,IAAMyC,EAA4B,CAChC,GAAI,SAASC,EAAM,OAAOA,EAAM,SAChC,QAAS,SACT,UAAW,UACX,OAAQ,CACN,GAAI,GAAG7B,WAAiB6B,EAAM,MAC9B,KAAMnB,EAAM,KAAK,KACjB,SAAUA,EAAM,KAAK,QACvB,EAEA,KAAM,kBAAkBmB,EAAM,OAAOA,EAAM,YAAYA,EAAM;AAAA,EAAUA,EAAM,aAAa,MAAM,EAAG,GAAG,GAAK;AAAA,OAAYA,EAAM,MAC7H,UAAW,IAAI,KACf,IAAKnB,EACL,cAAeoB,CACjB,EAEA,KAAK,QAAQF,CAAQ,EAAE,MAAOrB,GAAM,KAAK,IAAI,yBAAyBA,EAAE,SAAS,CAAC,EAClFpB,EAAI,UAAU,GAAG,EACjBA,EAAI,IAAI,IAAI,CACd,CAKA,MAAc,SAASuB,EAAsBvB,EAAoC,CAC/E,GAAI,CAAC,KAAK,QAAS,CAAEA,EAAI,UAAU,GAAG,EAAGA,EAAI,IAAI,IAAI,EAAG,OAGxD,GAAI,KAAK,UAAUuB,EAAM,KAAK,QAAQ,EAAG,CACvCvB,EAAI,UAAU,GAAG,EAAGA,EAAI,IAAI,IAAI,EAAG,OAGrC,IAAM0C,EAAQnB,EAAM,kBACdV,EAAUU,EAAM,QAAQ,oBACxByB,EAAU,KAAK,aAAanC,CAAO,EAEnC4B,EAA4B,CAChC,GAAI,MAAMC,EAAM,OAAOA,EAAM,SAC7B,QAAS,SACT,UAAW,UACX,OAAQ,CACN,GAAI,GAAG7B,mBAAyB6B,EAAM,MACtC,KAAMnB,EAAM,KAAK,KACjB,SAAUA,EAAM,KAAK,QACvB,EAEA,KAAM,eAAemB,EAAM,OAAOA,EAAM,YAAYA,EAAM;AAAA,UAAkBA,EAAM,oBAAoBA,EAAM;AAAA,EAAkBA,EAAM,aAAa,MAAM,EAAG,GAAG,GAAK;AAAA,OAAYA,EAAM,MACpL,UAAW,IAAI,KACf,IAAKnB,EACL,cAAeyB,CACjB,EAEA,KAAK,QAAQP,CAAQ,EAAE,MAAOrB,GAAM,KAAK,IAAI,sBAAsBA,EAAE,SAAS,CAAC,EAC/EpB,EAAI,UAAU,GAAG,EACjBA,EAAI,IAAI,IAAI,CACd,CAKA,MAAc,eAAeuB,EAA4BvB,EAAoC,CAC3F,IAAM0C,EAAQnB,EAAM,kBACdV,EAAUU,EAAM,QAAQ,oBAkB9B,GAjByB,CAAC,UAAW,SAAU,UAAU,EAGpC,SAASmB,EAAM,MAAM,GAAK,KAAK,OAAO,IAAI,oBAA2B,GACxF,KAAK,MAAM,QAAQ,qBAA6B,CAC9C,MAAO,qBACP,WAAYA,EAAM,GAClB,OAAQA,EAAM,OACd,IAAKA,EAAM,IACX,SAAUA,EAAM,SAChB,QAAA7B,EACA,UAAYU,EAAc,SAAS,GACnC,IAAKA,CACP,CAAC,EAAE,MAAOH,GAAa,KAAK,IAAI,kCAAkCA,EAAE,SAAS,CAAC,EAI5E,CAAC,KAAK,SAAWsB,EAAM,SAAW,SAAU,CAC9C1C,EAAI,UAAU,GAAG,EACjBA,EAAI,IAAI,IAAI,EACZ,OAGF,IAAMgD,EAAU,KAAK,aAAanC,CAAO,EAEnC4B,EAA4B,CAChC,GAAI,YAAYC,EAAM,KACtB,QAAS,SACT,UAAW,UACX,OAAQ,CACN,GAAI,GAAG7B,cAAoB6B,EAAM,KACjC,KAAMnB,EAAM,KAAK,KACjB,SAAUA,EAAM,KAAK,QACvB,EAEA,KAAM,qCAAqCV;AAAA,OAAiB6B,EAAM;AAAA,YAAkBA,EAAM;AAAA,iCAC1F,UAAW,IAAI,KACf,IAAKnB,EACL,cAAeyB,CACjB,EAEA,KAAK,QAAQP,CAAQ,EAAE,MAAOrB,GAAM,KAAK,IAAI,4BAA4BA,EAAE,SAAS,CAAC,EACrFpB,EAAI,UAAU,GAAG,EACjBA,EAAI,IAAI,IAAI,CACd,CAOQ,wBAAwBsB,EAAkC,CAEhE,IAAMO,EAAWP,EAAK,MAAM,eAAe,GAAG,IAAIN,GAAKA,EAAE,MAAM,CAAC,EAAE,QAAQ,QAAS,EAAE,EAAE,YAAY,CAAC,GAAK,CAAC,EAC1G,GAAIa,EAAS,SAAW,EAGxB,QAAWoB,KAAWpB,EAAU,CAC9B,IAAMmB,EAAU,KAAK,gBAAgB,IAAIC,CAAO,EAChD,GAAID,EACF,YAAK,IAAI,YAAYC,eAAqBD,0BAAgC,EACnEA,EAKb,CAKQ,aAAanC,EAAqC,CACxD,QAAWqC,KAAS,KAAK,OAAO,OAC9B,GAAIA,EAAM,UAAYrC,GAAWqC,EAAM,UAAY,IACjD,OAAOA,EAAM,KAInB,CAMA,MAAM,eAAeb,EAAkD,CACrE,IAAM3B,EAAQ2B,EAAO,MAAM,GAAG,EACxBxB,EAAUH,EAAM,CAAC,EACjBE,EAAeF,EAAM,CAAC,EACtBqB,EAAcrB,EAAM,CAAC,EAGrByC,EAAgC,CAAC,EACvC,QAAWD,KAAS,KAAK,OAAO,QAC1BA,EAAM,UAAYrC,GAAWqC,EAAM,UAAY,MACjDC,EAAO,KAAK,CAAE,GAAID,EAAM,MAAO,KAAMA,EAAM,KAAM,CAAC,EAGtD,QAAWtD,KAAW,KAAK,OAAO,eAAiB,CAAC,EAC7CuD,EAAO,KAAK,GAAK,EAAE,KAAOvD,EAAQ,OAAO,GAC5CuD,EAAO,KAAK,CAAE,GAAIvD,EAAQ,QAAS,KAAMA,EAAQ,OAAQ,CAAC,EAW9D,MAAO,CACL,QAAS,SACT,OAAAuD,EACA,QAAAtC,EACA,MAAOD,GAAgBmB,EAAc,CAAE,KAAMnB,EAAc,IAAKmB,EAAa,MAAO,EAAG,EAAI,OAC3F,MAZsB,CACtB,oEACA,0DACA,6FACA,kEACF,CAQA,CACF,CAKQ,UAAU5B,EAA2B,CAC3C,OAAO,KAAK,aAAa,IAAIA,CAAQ,CACvC,CAMA,cAAc6C,EAAsC,CAClD,MAAI,CAACA,GAAW,CAAC,KAAK,OAAO,eAAe,OAAQ,OACpC,KAAK,OAAO,cAAc,KAAKhC,GAAKA,EAAE,UAAYgC,CAAO,GACzD,KAClB,CAKQ,0BAA0B1B,EAAkC,CAClE,GAAI,CAAC,KAAK,OAAO,eAAe,OAAQ,OACxC,IAAM8B,EAAiB9B,EAAK,MAAM,eAAe,GAAG,IAAIN,GAAKA,EAAE,MAAM,CAAC,EAAE,QAAQ,QAAS,EAAE,EAAE,YAAY,CAAC,GAAK,CAAC,EAChH,QAAWpB,KAAW,KAAK,OAAO,cAChC,GAAIA,EAAQ,OAASA,EAAQ,gBAAgB,KAAKQ,GAAKgD,EAAe,SAAShD,EAAE,YAAY,CAAC,CAAC,EAC7F,OAAOR,EAAQ,KAIrB,CAOA,MAAc,YAAYiB,EAAiBD,EAAsBmB,EAAqBZ,EAAgBF,EAAqBoC,EAAeL,EAAiC,CAEzK,GAAI,CAAC/B,GAAcoC,GAAQL,GAAW,KAAK,eAAgB,CACzD,KAAK,IAAI,sCAA+BA,oBAA0BK,IAAO,EACzE,MAAM,KAAK,eAAeA,EAAMxC,EAASD,EAAcmB,EAAaZ,EAAQ6B,CAAO,EACnF,OAGF,GAAI,CAAC/B,EAAY,CACf,KAAK,IAAI,iCAAiCE,aAAkB6B,qBAAsB,EAClF,OAGF,IAAMlC,EAAiB,mBAAmBD,CAAO,EAC7CE,EAEJ,OAAQH,EAAc,CACpB,IAAK,QACHG,EAAW,GAAG,KAAK,OAAO,wBAAwBD,YAAyBiB,WAAqBZ,gBAChG,MACF,IAAK,gBACHJ,EAAW,GAAG,KAAK,OAAO,wBAAwBD,oBAAiCiB,WAAqBZ,gBACxG,MACF,QACE,MACJ,CAEA,GAAI,CACF,IAAMnB,EAAM,MAAM,MAAMe,EAAU,CAChC,OAAQ,OACR,QAAS,CACP,eAAgB,mBAChB,gBAAiBE,CACnB,EACA,KAAM,KAAK,UAAU,CAAE,KAAM,MAAO,CAAC,CACvC,CAAC,EACIjB,EAAI,IACP,KAAK,IAAI,2BAA2BmB,MAAWnB,EAAI,QAAQ,CAE/D,OAASoB,EAAP,CACA,KAAK,IAAI,2BAA2BD,MAAWC,EAAE,SAAS,CAC5D,CACF,CAOA,MAAM,aAAaiB,EAAgBiB,EAAoBN,EAAiC,CACtF,IAAMtC,EAAQ2B,EAAO,MAAM,GAAG,EAC9B,GAAI3B,EAAM,OAAS,EAAG,OAEtB,IAAMC,EAAMD,EAAM,IAAI,EAChBE,EAAeF,EAAM,IAAI,EACzBG,EAAUH,EAAM,KAAK,GAAG,EAGxB6C,EAAe,KAAK,IAAI,GAAI,KAAK,MAAMD,EAAa,GAAI,CAAC,EACzDE,EAAQ,KAAK,MAAMD,EAAe,IAAI,EACtCE,EAAU,KAAK,KAAMF,EAAe,KAAQ,EAAE,EAC9CG,EAAWF,EAAQ,EAAI,GAAGA,KAASC,EAAU,EAAI,IAAIA,KAAa,KAAO,GAAGA,KAE5E3C,EAAiB,mBAAmBD,CAAO,EAC3C8C,EAAc/C,IAAiB,gBAAkB,iBAAmB,SACpEG,EAAW,GAAG,KAAK,OAAO,wBAAwBD,KAAkB6C,KAAehD,mBAKnFf,EAAU,KAAK,OAAO,eAAe,KAAMoB,GAAMA,EAAE,UAAYgC,CAAO,EACtE/B,EAAa,KAAK,cAAc+B,CAAO,EAC7C,GAAI,CAAC/B,GAAcrB,GAAS,MAAQ,KAAK,iBAAkB,CACzD,GAAI,CACF,MAAM,KAAK,iBAAiBA,EAAQ,KAAMiB,EAASD,EAAcD,EAAKqC,GAAW,GAAIM,CAAU,EAC/F,KAAK,IAAI,0BAA0B1D,EAAQ,WAAW8D,QAAe7C,KAAW8C,KAAehD,MAAQqC,IAAU,CACnH,OAAS5B,EAAP,CACA,KAAK,IAAI,wBAAwBxB,EAAQ,iBAAiBwB,EAAE,kDAA6C,CAC3G,CACA,OAGF,IAAMrB,EAAQkB,GAAc,KAAK,OAAO,MAExC,GAAI,CACF,IAAMjB,EAAM,MAAM,MAAMe,EAAU,CAChC,OAAQ,OACR,QAAS,CACP,eAAgB,mBAChB,gBAAiBhB,CACnB,EACA,KAAM,KAAK,UAAU,CAAE,SAAA2D,CAAS,CAAC,CACnC,CAAC,EAED,GAAI1D,EAAI,GACN,KAAK,IAAI,gBAAgB0D,QAAe7C,KAAWD,MAAiBD,MAAQqC,GAAW,WAAW,MAC7F,CACL,IAAM1B,EAAO,MAAMtB,EAAI,KAAK,EAC5B,KAAK,IAAI,oBAAoBA,EAAI,YAAYsB,EAAK,MAAM,EAAG,GAAG,GAAG,EAErE,OAASF,EAAP,CACA,KAAK,IAAI,mBAAmBA,EAAE,SAAS,CACzC,CACF,CAOA,MAAc,mBACZK,EACAZ,EACAd,EAC+C,CAE/C,IAAM6D,EAAe,0DACfC,EAAU,CAAC,GAAGpC,EAAK,SAASmC,CAAY,CAAC,EAC/C,GAAIC,EAAQ,SAAW,EAAG,OAE1B,IAAMC,EAAYD,EAAQ,CAAC,EAAE,CAAC,EAC1BE,EAEJ,GAAID,EAAU,WAAW,MAAM,EAC7BC,EAAWD,MACN,CAEL,IAAMhD,EAAiB,mBAAmBD,CAAO,EACjDkD,EAAW,GAAG,KAAK,OAAO,QAAQlD,IAAUiD,IAG9C,GAAI,CACF,IAAM9D,EAAM,MAAM,MAAM+D,EAAU,CAChC,QAAS,CAAE,gBAAiBhE,CAAM,CACpC,CAAC,EACD,GAAI,CAACC,EAAI,GAAI,CACX,KAAK,IAAI,6BAA6BA,EAAI,UAAU+D,GAAU,EAC9D,OAGF,IAAMC,EAAS,OAAO,KAAK,MAAMhE,EAAI,YAAY,CAAC,EAC5CiE,EAAcjE,EAAI,QAAQ,IAAI,cAAc,GAAK,YACjDkE,EAAMD,EAAY,MAAM,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,EAAE,CAAC,GAAK,MAGlD,CAAE,UAAAE,EAAW,cAAAC,CAAc,EAAI,KAAM,QAAO,IAAI,EAChD,CAAE,QAAAC,CAAQ,EAAI,KAAM,QAAO,MAAM,EACjC,CAAE,WAAAC,CAAW,EAAI,KAAM,QAAO,QAAQ,EACtCC,EAAWF,EAAQ,QAAQ,IAAI,EAAG,sBAAsB,EAC9DF,EAAUI,EAAU,CAAE,UAAW,EAAK,CAAC,EACvC,IAAMC,EAAW,GAAGF,EAAW,EAAE,MAAM,EAAG,CAAC,KAAKJ,IAC1CO,EAAWJ,EAAQE,EAAUC,CAAQ,EAC3C,OAAAJ,EAAcK,EAAUT,CAAM,EAE9B,KAAK,IAAI,4BAA4BS,MAAaT,EAAO,eAAe,EAEjE,CACL,KAAMS,EACN,KAAMR,EACN,SAAUH,EAAU,MAAM,GAAG,EAAE,IAAI,GAAKU,CAC1C,CACF,OAASpD,EAAP,CACA,KAAK,IAAI,yBAAyBA,EAAE,SAAS,EAC7C,MACF,CACF,CAEA,MAAc,SAASb,EAAoD,CACzE,OAAO,IAAI,QAAS8D,GAAY,CAC9B,IAAInD,EAAO,GACXX,EAAI,GAAG,OAASmE,GAAmBxD,GAAQwD,EAAM,SAAS,CAAE,EAC5DnE,EAAI,GAAG,MAAO,IAAM,CAClB,GAAI,CAAE8D,EAAQnD,EAAO,KAAK,MAAMA,CAAI,EAAI,CAAC,CAAC,CAAE,MAC5C,CAAQmD,EAAQ,CAAE,IAAKnD,CAAK,CAAC,CAAE,CACjC,CAAC,EACDX,EAAI,GAAG,QAAS,IAAM8D,EAAQ,CAAC,CAAC,CAAC,CACnC,CAAC,CACH,CACF,EC77BO,IAAMM,EAAN,KAAa,CACV,OACA,SAGR,YAAYC,EAAgBC,EAAqB,OAAQ,CACvD,KAAK,OAASD,EACd,KAAK,SAAWC,CAClB,CAEA,MAAMD,EAAwB,CAC5B,OAAO,IAAID,EAAO,GAAG,KAAK,UAAUC,IAAU,KAAK,QAAQ,CAC7D,CAEA,MAAME,EAAaC,EAAsC,CACvD,KAAK,KAAK,QAASD,EAAKC,CAAI,CAC9B,CAEA,KAAKD,EAAaC,EAAsC,CACtD,KAAK,KAAK,OAAQD,EAAKC,CAAI,CAC7B,CAEA,KAAKD,EAAaC,EAAsC,CACtD,KAAK,KAAK,OAAQD,EAAKC,CAAI,CAC7B,CAEA,MAAMD,EAAaC,EAAsC,CACvD,KAAK,KAAK,QAASD,EAAKC,CAAI,CAC9B,CAKA,cAA6C,CAC3C,MAAO,IAAIC,IAAoB,CAG7B,IAAMC,EAFMD,EAAK,IAAIE,GAAK,OAAOA,GAAM,SAAWA,EAAI,KAAK,UAAUA,CAAC,CAAC,EAAE,KAAK,GAAG,EAE/D,QAAQ,iBAAkB,EAAE,EAC1CD,GAAO,KAAK,KAAKA,CAAK,CAC5B,CACF,CAEQ,KAAKE,EAAiBL,EAAaC,EAAsC,CAC/E,GAAIJ,EAAO,WAAWQ,CAAK,EAAIR,EAAO,WAAW,KAAK,QAAQ,EAAG,OAEjE,IAAMS,EAAkB,CACtB,KAAM,IAAI,KAAK,EAAE,YAAY,EAC7B,MAAAD,EACA,OAAQ,KAAK,OACb,IAAAL,EACA,GAAGC,CACL,EAGMM,EAASF,IAAU,QAAU,QAAUA,IAAU,OAAS,OAAS,GACnEG,EAAM,IAAI,KAAK,UACfC,EAAUF,EAAS,GAAGC,KAAOD,MAAWP,IAAQ,GAAGQ,KAAOR,IAChE,QAAQ,MAAMS,CAAO,CACvB,CACF,EA3DaC,EAANb,EAGLc,EAHWD,EAGI,aAAuC,CAAE,MAAO,EAAG,KAAM,EAAG,KAAM,EAAG,MAAO,CAAE,GCGxF,IAAME,GAAN,KAAqB,CAClB,SACA,OACA,IAER,YACEC,EACAC,EAAwB,CAAC,EACzBC,EAAoC,QAAQ,MAAM,KAAK,QAAS,WAAW,EAC3E,CACA,KAAK,SAAWF,EAChB,KAAK,OAASC,EACd,KAAK,IAAMC,CACb,CAMA,MAAM,OAAOC,EAAsBC,EAAqBC,EAA6B,CAEnF,IAAMC,EAAQD,EAAK,QAAQ,gBAAiB,EAAE,EAAE,MAAM,GAAG,EAAE,OAAO,OAAO,EACnEE,EAAUD,EAAM,CAAC,EACjBE,EAAaF,EAAM,CAAC,EAE1B,GAAI,CAACC,EAAS,CACZ,KAAK,SAASH,EAAK,IAAK,CAAE,MAAO,yCAA0C,CAAC,EAC5E,OAIF,IAAMK,EAAO,MAAM,KAAK,SAASN,CAAG,EAG9BO,EAASF,GAAc,KAAK,aAAaL,EAAI,OAAO,EAGpDQ,EAAU,KAAK,aAAaD,EAAQD,EAAMN,EAAI,OAAO,EAE3D,KAAK,IAAI,YAAYO,SAAcH,MAAYI,EAAQ,MAAM,EAAG,GAAG,GAAG,EAGtE,GAAI,CACF,IAAMC,EAAW,MAAM,KAAK,SAAS,QAAQ,CAC3C,QAASD,EACT,QAAAJ,EACA,QAAS,CACP,QAAS,WAAWG,IACpB,OAAQ,WAAWA,GACrB,CACF,CAAC,EAED,KAAK,SAASN,EAAKQ,EAAS,MAAQ,IAAM,IAAK,CAC7C,GAAI,CAACA,EAAS,MACd,MAAOL,EACP,OAAAG,EACA,SAAUE,EAAS,SAAS,MAAM,EAAG,GAAG,EACxC,MAAOA,EAAS,MAChB,SAAUA,EAAS,QACrB,CAAC,CACH,OAASC,EAAP,CACA,KAAK,SAAST,EAAK,IAAK,CAAE,MAAOS,EAAE,OAAQ,CAAC,CAC9C,CACF,CAKQ,aAAaC,EAAgE,CACnF,OAAIA,EAAQ,gBAAgB,GAAKA,EAAQ,gBAAgB,EAAU,SAC/DA,EAAQ,gBAAgB,EAAU,SAClCA,EAAQ,kBAAkB,EAAU,SACpCA,EAAQ,sBAAsB,EAAU,SACxCA,EAAQ,oBAAoB,EAAU,SACtCA,EAAQ,qBAAqB,EAAU,SACpC,SACT,CAMQ,aAAaJ,EAAgBD,EAA+BK,EAAgE,CAClI,IAAMC,EAAkB,CAAC,iBAAiBL,IAAS,EAEnD,OAAQA,EAAQ,CACd,IAAK,SAAU,CACb,IAAMM,EAAQF,EAAQ,gBAAgB,GAAeL,EAAK,aAAyB,QAC7EQ,EAAWR,EAAK,SAAiB,qBAAuB,GACxDS,EAAQT,EAAK,MAAc,MAASA,EAAK,eAA4B,GAQ3E,GANAM,EAAM,KAAK,UAAUC,GAAO,EACxBC,GAASF,EAAM,KAAK,YAAYE,GAAS,EACzCC,GAAMH,EAAM,KAAK,SAASG,GAAM,EAGhCT,EAAK,KAAKM,EAAM,KAAK,QAAQN,EAAK,KAAK,EACvCA,EAAK,SAAW,MAAM,QAAQA,EAAK,OAAO,EAAG,CAC/CM,EAAM,KAAK,YAAYN,EAAK,QAAQ,QAAQ,EAC5C,QAAWU,KAAMV,EAAK,QAAkB,MAAM,EAAG,CAAC,EAChDM,EAAM,KAAK,OAAOI,EAAE,SAAS,MAAM;AAAA,CAAI,EAAE,CAAC,GAAK,iBAAiBA,EAAE,QAAQ,MAAQ,KAAK,EAK3F,IAAMC,EAAKX,EAAK,kBAgBhB,GAfIW,GAAI,QACNL,EAAM,KAAK,UAAUK,EAAG,OAAO,EAC/BL,EAAM,KAAK,UAAUK,EAAG,OAAS,IAAI,EACrCL,EAAM,KAAK,WAAWK,EAAG,QAAU,IAAI,EACnCA,EAAG,eAAeL,EAAM,KAAK,WAAWK,EAAG,oBAAoBA,EAAG,eAAe,EACjFA,EAAG,KAAKL,EAAM,KAAK,QAAQK,EAAG,KAAK,GAIrCA,GAAI,KAAO,CAACA,GAAI,gBAClBL,EAAM,KAAK,UAAUK,EAAG,QAAQA,EAAG,OAAS,IAAI,EAC5CA,EAAG,aAAaL,EAAM,KAAK,gBAAgBK,EAAG,YAAY,MAAM,EAAG,GAAG,GAAG,GAI3EX,EAAK,cAAgB,WAAY,CACnC,IAAMY,EAAQZ,EAAK,kBACnBM,EAAM,KAAK,aAAaM,GAAO,QAAU,OAAOA,GAAO,KAAO,KAAK,EACnEN,EAAM,KAAK,aAAaM,GAAO,UAAY,IAAI,EAEjD,KACF,CAEA,IAAK,SAAU,CACb,IAAML,EAAQF,EAAQ,gBAAgB,GAAe,QAC/CQ,EAAQb,EAAK,YAAoB,WAAa,GAC9Cc,EAAUd,EAAK,QAAgB,OAAS,GAQ9C,GANAM,EAAM,KAAK,UAAUC,GAAO,EACxBM,GAAMP,EAAM,KAAK,eAAeO,GAAM,EACtCC,GAAQR,EAAM,KAAK,WAAWQ,GAAQ,EAGtCd,EAAK,KAAKM,EAAM,KAAK,QAAQN,EAAK,KAAK,EACvCA,EAAK,SAAW,MAAM,QAAQA,EAAK,OAAO,EAC5C,QAAWU,KAAMV,EAAK,QAAkB,MAAM,EAAG,CAAC,EAChDM,EAAM,KAAK,OAAOI,EAAE,SAAS,MAAM;AAAA,CAAI,EAAE,CAAC,GAAK,OAAOA,EAAE,QAAQ,MAAQ,KAAK,EAKjF,IAAMK,EAAKf,EAAK,aACZe,IACFT,EAAM,KAAK,OAAOS,EAAG,WAAWA,EAAG,OAAO,EAC1CT,EAAM,KAAK,WAAWN,EAAK,QAAQ,EACnCM,EAAM,KAAK,WAAWS,EAAG,MAAM,UAAUA,EAAG,MAAM,KAAK,GAIzD,IAAMC,EAAQhB,EAAK,MACfgB,IACFV,EAAM,KAAK,UAAUU,EAAM,WAAWA,EAAM,OAAO,EACnDV,EAAM,KAAK,WAAWN,EAAK,QAAQ,GAErC,KACF,CAEA,IAAK,SAAU,CACb,IAAMiB,EAAOjB,EAAK,MAAkB,QAC9BkB,EAAQlB,EAAK,MAAc,QAAU,CAAC,EAE5CM,EAAM,KAAK,UAAUW,GAAM,EACvBC,EAAK,QAAQZ,EAAM,KAAK,YAAYY,EAAK,OAAS,KAAK,QAAQ,CAAC,KAAKA,EAAK,UAAU,YAAY,GAAK,IAAI,EACzGA,EAAK,gBAAgBZ,EAAM,KAAK,aAAaY,EAAK,gBAAgB,EAClEA,EAAK,aAAaZ,EAAM,KAAK,gBAAgBY,EAAK,aAAa,EAC/DA,EAAK,QAAQZ,EAAM,KAAK,WAAWY,EAAK,QAAQ,EACpD,KACF,CAEA,IAAK,SAAU,CACb,IAAMC,EAAWd,EAAQ,sBAAsB,GAAe,QAC9DC,EAAM,KAAK,aAAaa,GAAU,EAElC,IAAMD,EAAOlB,EAAK,MAAeA,EAC7BkB,EAAK,OAAO,OAAOZ,EAAM,KAAK,UAAUY,EAAK,MAAM,OAAO,EAC1DA,EAAK,OAAO,SAASZ,EAAM,KAAK,YAAYY,EAAK,MAAM,SAAS,EAChEA,EAAK,OAAO,UAAU,OAAOZ,EAAM,KAAK,YAAYY,EAAK,MAAM,SAAS,OAAO,EAC/ElB,EAAK,KAAKM,EAAM,KAAK,QAAQN,EAAK,KAAK,EAC3C,KACF,CAEA,QACEM,EAAM,KAAK,YAAY,KAAK,UAAU,OAAO,KAAKD,CAAO,EAAE,OAAOe,GAAKA,EAAE,WAAW,IAAI,CAAC,EAAE,MAAM,EAAG,CAAC,CAAC,GAAG,EACzGd,EAAM,KAAK,iBAAiB,OAAO,KAAKN,CAAI,EAAE,MAAM,EAAG,EAAE,EAAE,KAAK,IAAI,GAAG,EAEvE,IAAMqB,EAAM,KAAK,UAAUrB,CAAI,EAAE,MAAM,EAAG,GAAG,EAC7CM,EAAM,KAAK,SAASe,GAAK,CAC7B,CAEA,OAAOf,EAAM,KAAK;AAAA,CAAI,CACxB,CAEA,MAAc,SAASZ,EAAwD,CAC7E,OAAO,IAAI,QAAS4B,GAAY,CAC9B,IAAItB,EAAO,GACXN,EAAI,GAAG,OAAS6B,GAAmBvB,GAAQuB,EAAM,SAAS,CAAE,EAC5D7B,EAAI,GAAG,MAAO,IAAM,CAClB,GAAI,CAAE4B,EAAQtB,EAAO,KAAK,MAAMA,CAAI,EAAI,CAAC,CAAC,CAAE,MAC5C,CAAQsB,EAAQ,CAAE,IAAKtB,CAAK,CAAC,CAAE,CACjC,CAAC,EACDN,EAAI,GAAG,QAAS,IAAM4B,EAAQ,CAAC,CAAC,CAAC,CACnC,CAAC,CACH,CAEQ,SAAS3B,EAAqB6B,EAAgBN,EAAqB,CACzEvB,EAAI,UAAU6B,EAAQ,CAAE,eAAgB,kBAAmB,CAAC,EAC5D7B,EAAI,IAAI,KAAK,UAAUuB,EAAM,KAAM,CAAC,CAAC,CACvC,CACF,ECxNO,IAAMO,GAAN,KAAuB,CACpB,MAA6B,IAAI,IACjC,OACA,UAA4B,CAAC,EAErC,YAAYC,EAAsB,CAChC,KAAK,OAASA,CAChB,CAMQ,YAAc,GAKtB,MAAMC,EAAkC,CAClCA,IAAW,KAAK,UAAYA,GAChC,KAAK,MAAM,MAAM,EACjB,KAAK,YAAc,KAAK,mBAAmB,EAE3C,QAAWC,KAAW,OAAO,KAAK,KAAK,OAAO,MAAM,EAClD,KAAK,MAAM,IAAIA,EAAS,KAAK,eAAeA,CAAO,CAAC,CAExD,CAIQ,oBAA6B,CAInC,MAHc,CAAC,GAAG,KAAK,SAAS,EAC7B,KAAK,CAACC,EAAGC,IAAMD,EAAE,KAAK,cAAcC,EAAE,IAAI,CAAC,EAC3C,IAAIC,GAAK,GAAGA,EAAE,QAAQA,EAAE,OAAO,IAAIC,GAAKA,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,GAAG,GAAG,EACtD,KAAK,GAAG,CACvB,CAKA,YAAYJ,EAAqC,CAC/C,OAAO,KAAK,MAAM,IAAIA,CAAO,CAC/B,CAOA,QAAQD,EAAiC,CACvC,KAAK,UAAYA,EAEb,EADS,KAAK,mBAAmB,IACxB,KAAK,aAAe,KAAK,MAAM,KAAO,IACnD,KAAK,MAAM,CACb,CAEQ,eAAeM,EAAwB,CAC7C,IAAMC,EAAkB,CAAC,aAAa,EAChCC,EAAU,KAAK,OAAO,OAAOF,CAAM,EAGzCC,EAAM,KAAK,SAAS,KAAK,OAAO,KAAK,SAAS,KAAK,OAAO,KAAK,KAAK,EAGpE,IAAME,EAAa,KAAK,iBAAiBH,CAAM,EACzCI,EAAU,KAAK,kBAAkBF,CAAO,EACxCG,EAAYF,EAAa,KAAKA,KAAgB,GACpDF,EAAM,KAAK,QAAQC,EAAQ,OAAOG,YAAeD,GAAS,EAC1DH,EAAM,KAAK,EAAE,EAGb,IAAMK,EAAc,OAAO,QAAQ,KAAK,OAAO,MAAM,EAAE,OAAO,CAAC,CAACC,CAAE,IAAMA,IAAOP,CAAM,EACrFC,EAAM,KAAK,kCAAkCK,EAAY,qDAAgD,EACzG,OAAW,CAACC,EAAIC,CAAG,IAAKF,EAAa,CACnC,IAAMG,EAAS,KAAK,iBAAiBF,CAAE,EACjCG,EAAM,KAAK,kBAAkBF,CAAG,EAChCG,EAAIF,EAAS,KAAKA,KAAY,GACpCR,EAAM,KAAK,UAAKO,EAAI,OAAOG,YAAOD,GAAK,EASzC,IAAME,EAAc,KAAK,UAAU,OAAOd,GAAKA,EAAE,OAAO,OAAS,CAAC,EAClE,GAAIc,EAAY,OAAQ,CACtBX,EAAM,KAAK,SAAS,EAGpB,IAAMY,EAAc,CAAC,GAAGD,CAAW,EAAE,KAAK,CAAChB,EAAGC,IAAMD,EAAE,KAAK,cAAcC,EAAE,IAAI,CAAC,EAChF,QAAWiB,KAAQD,EAAa,CAC9B,IAAME,EAAe,CAAC,GAAGD,EAAK,MAAM,EAAE,KAAK,CAAClB,EAAGC,IAAMD,EAAE,GAAG,cAAcC,EAAE,EAAE,CAAC,EAC7E,QAAWmB,KAASD,EAClBd,EAAM,KAAK,UAAKe,EAAM,SAASF,EAAK,gBAAWE,EAAM,YAAY,MAAM,EAAG,EAAE,GAAG,GAMrFf,EAAM,KAAK,EAAE,EACbA,EAAM,KAAK,KAAK,eAAe,CAAC,EAGhCA,EAAM,KAAK,EAAE,EACbA,EAAM,KAAK,SAAS,EACpB,IAAMgB,EAAU,CAACd,EAAYH,CAAM,EAAE,OAAO,OAAO,EAAE,KAAK,KAAK,EAC/DC,EAAM,KAAK,4BAA4BgB,yCAA+C,EACtFhB,EAAM,KAAK,2FAAsF,EACjGA,EAAM,KAAK,iHAA4G,EACvHA,EAAM,KAAK,iGAAiG,EAC5GA,EAAM,KAAK,wEAAwE,EACnFA,EAAM,KAAK,sGAAsG,EACjHA,EAAM,KAAK,wJAAmJ,EAG9J,GAAM,CAAC,CAAEiB,CAAO,EAAI,KAAK,OAAO,KAAK,KAAK,MAAM,GAAG,EAC7CC,EAAOD,GAAW,QACxB,OAAAjB,EAAM,KAAK,EAAE,EACbA,EAAM,KAAK,2BAA2B,EACtCA,EAAM,KAAK,oEAAoE,EAC/EA,EAAM,KAAK,mCAAmCkB,4GAA+G,EAC7JlB,EAAM,KAAK,+CAA+C,EAC1DA,EAAM,KAAK,iBAAiB,EAC5BA,EAAM,KAAK,wEAAwE,EACnFA,EAAM,KAAK,yEAAyE,EACpFA,EAAM,KAAK,sDAAsD,EACjEA,EAAM,KAAK,uGAAuG,EAGlHA,EAAM,KAAK,EAAE,EACbA,EAAM,KAAK,yBAAyB,EACpCA,EAAM,KAAK,kEAAkE,EAC7EA,EAAM,KAAK,qDAAqD,EAChEA,EAAM,KAAK,uDAAuD,EAClEA,EAAM,KAAK,sCAAsC,EACjDA,EAAM,KAAK,oGAA+F,EAG1GA,EAAM,KAAK,EAAE,EACbA,EAAM,KAAK,eAAe,EAC1BA,EAAM,KAAK,iFAAiF,EAC5FA,EAAM,KAAK,yEAAyE,EACpFA,EAAM,KAAK,iEAAiE,EAC5EA,EAAM,KAAK,kEAAkE,EAC7EA,EAAM,KAAK,sHAAsH,EACjIA,EAAM,KAAK,4FAA4F,EAEhGA,EAAM,KAAK;AAAA,CAAI,CACxB,CAEQ,gBAAyB,CAC/B,IAAMmB,EAAkB,CAAC,EACnBC,EAAK,KAAK,OAAO,SAEvB,GAAIA,EAAG,SAAS,QAAS,CACvB,IAAMC,EAAMD,EAAG,SAAS,OACxBD,EAAM,KAAK,mBAAmBE,EAAI,cAAcA,EAAI,qBAAqB,EAE3E,OAAID,EAAG,QAAQ,SACbD,EAAM,KAAK,iCAAiC,EAE1CC,EAAG,SAAS,SACdD,EAAM,KAAK,mCAAmC,EAE5CC,EAAG,SAAS,SACdD,EAAM,KAAK,kBAAkB,EAGxB,aAAaA,EAAM,KAAK,IAAI,GACrC,CAKQ,iBAAiBzB,EAAqC,CAC5D,IAAMa,EAAM,KAAK,OAAO,OAAOb,CAAO,EACtC,GAAKa,EACL,OAAOA,EAAI,SAAS,KAAKe,GAAKA,EAAE,WAAW,GAAG,CAAC,CACjD,CAMQ,kBAAkBf,EAAuB,CAC/C,GAAI,CAACA,EAAI,aAAc,OAAOA,EAAI,KAGlC,IAAMgB,EAAQhB,EAAI,aAAa,MAAM,OAAO,EAAE,CAAC,GAAG,KAAK,GAAKA,EAAI,KAChE,OAAOgB,EAAM,OAAS,GAAKA,EAAM,MAAM,EAAG,EAAE,EAAI,MAAQA,CAC1D,CACF,ECzKO,IAAMC,GAAN,KAAuB,CACpB,SACA,YACA,OAAsC,IAAI,IAC1C,IAER,YACEC,EACAC,EAAoC,QAAQ,MAAM,KAAK,QAAS,aAAa,EAC7EC,EACA,CACA,KAAK,SAAWF,EAChB,KAAK,IAAMC,EACX,KAAK,YAAcC,CACrB,CAKA,SAASC,EAAiBC,EAA+B,CACvD,GAAI,CAACA,EAAO,QAAS,OAGrB,KAAK,KAAKD,CAAO,EAEjB,IAAME,EAAwB,CAC5B,QAAAF,EACA,OAAAC,EACA,SAAU,EACV,OAAQ,CACV,EAEME,GAAcF,EAAO,iBAAmB,IAAM,GAAK,IAEzDC,EAAM,MAAQ,YAAY,IAAM,CAC9B,KAAK,aAAaA,CAAK,EAAE,MAAOE,GAAM,CACpC,KAAK,IAAI,IAAIJ,uBAA6BI,EAAE,SAAS,EACrDF,EAAM,QACR,CAAC,CACH,EAAGC,CAAU,EAEb,KAAK,OAAO,IAAIH,EAASE,CAAK,EAC9B,KAAK,IAAI,6BAA6BF,YAAkBC,EAAO,oBAAoB,CACrF,CAKA,KAAKD,EAAuB,CAC1B,IAAME,EAAQ,KAAK,OAAO,IAAIF,CAAO,EACjCE,GAAO,QACT,cAAcA,EAAM,KAAK,EACzBA,EAAM,MAAQ,QAEhB,KAAK,OAAO,OAAOF,CAAO,CAC5B,CAKA,SAAgB,CACd,OAAW,CAACK,CAAE,IAAK,KAAK,OACtB,KAAK,KAAKA,CAAE,CAEhB,CAKA,MAAc,aAAaH,EAAsC,CAC/D,GAAM,CAAE,QAAAF,EAAS,OAAAC,CAAO,EAAIC,EAE5B,KAAK,IAAI,IAAIF,yBAA+B,EAC5CE,EAAM,QAAU,IAAI,KACpBA,EAAM,WAEN,IAAMI,EAAUL,EAAO,SAAW,YAElC,GAAI,CAEF,IAAIM,EAASN,EAAO,OACpB,GAAI,KAAK,YAAa,CACpB,IAAMO,EAAgB,KAAK,YAAY,mBAAmBR,CAAO,EAC7DQ,IACFD,EAAS,GAAGC;AAAA;AAAA,EAAoBD,KAIpC,IAAME,EAAW,MAAM,KAAK,SAAS,QAAQ,CAC3C,QAASF,EACT,QAAAP,EACA,QAAS,CACP,QAAAM,EACA,OAAQ,mBACR,OAAQ,aAAaN,GACvB,CACF,CAAC,EAEGS,EAAS,OACX,KAAK,IAAI,IAAIT,uBAA6BS,EAAS,OAAO,EAC1DP,EAAM,UAEN,KAAK,IACH,IAAIF,2BAAiCS,EAAS,eAC3CA,EAAS,QAAQ,eACtB,CAEJ,OAASC,EAAP,CACA,KAAK,IAAI,IAAIV,wBAA8BU,EAAM,SAAS,EAC1DR,EAAM,QACR,CACF,CAKA,MAAM,QAAQF,EAAgC,CAC5C,IAAME,EAAQ,KAAK,OAAO,IAAIF,CAAO,EACrC,GAAI,CAACE,EAAO,CACV,KAAK,IAAI,IAAIF,4BAAkC,EAC/C,OAEF,MAAM,KAAK,aAAaE,CAAK,CAC/B,CAKA,MAMG,CACD,OAAO,MAAM,KAAK,KAAK,OAAO,OAAO,CAAC,EAAE,IAAKS,IAAO,CAClD,QAASA,EAAE,QACX,gBAAiBA,EAAE,OAAO,gBAC1B,QAASA,EAAE,QACX,SAAUA,EAAE,SACZ,OAAQA,EAAE,MACZ,EAAE,CACJ,CACF,EC1IO,IAAMC,GAAN,KAAqB,CAClB,SAA8B,CAAC,EAC/B,IAER,YACEC,EACAC,EAAoC,QAAQ,MAAM,KAAK,QAAS,YAAY,EAC5E,CACA,KAAK,IAAMA,EAEX,OAAW,CAACC,EAAIC,CAAG,IAAK,OAAO,QAAQH,CAAW,EAAG,CACnD,IAAMI,EAA4B,CAChC,IAAK,CAAE,GAAGD,EAAK,GAAAD,CAAG,EAClB,SAAU,CAAC,CACb,EAEA,QAAWG,KAAWF,EAAI,SACxB,GAAI,CACFC,EAAS,SAAS,KAAK,CACrB,MAAO,IAAI,OAAOC,EAAQ,QAAS,GAAG,EACtC,QAASA,EAAQ,OACnB,CAAC,CACH,OAASC,EAAP,CACA,KAAK,IAAI,6BAA6BJ,OAAQG,EAAQ,kBAAaC,EAAE,SAAS,CAChF,CAGEF,EAAS,SAAS,OAAS,GAC7B,KAAK,SAAS,KAAKA,CAAQ,EAI/B,KAAK,IAAI,GAAG,KAAK,SAAS,0BAA0B,CACtD,CAMA,MAAMG,EAAcC,EAAkBC,EAAsC,CAC1E,QAAWC,KAAO,KAAK,SAAU,CAE/B,GAAIA,EAAI,IAAI,iBAAiB,OAAQ,CACnC,IAAMC,EAAaH,EAAS,QAAQ,UAAW,EAAE,EAKjD,GAAI,CAJYE,EAAI,IAAI,gBAAgB,KAAK,GAAK,CAChD,IAAME,EAAc,EAAE,QAAQ,UAAW,EAAE,EAC3C,OAAOD,EAAW,SAASC,CAAW,GAAKA,EAAY,SAASD,CAAU,CAC5E,CAAC,EACa,SAIhB,QAAWE,KAAWH,EAAI,SAAU,CAElC,GAAIG,EAAQ,SAAWA,EAAQ,UAAYJ,EAAS,SAEpD,IAAMK,EAAQP,EAAK,MAAMM,EAAQ,KAAK,EACtC,GAAIC,EACF,OAAAC,EAAM,IAAI,QAAS,mBAAmBL,EAAI,IAAI,mBAAmBG,EAAQ,MAAM,SAAS,EACjF,CACL,QAASH,EAAI,IACb,QAASG,EAAQ,MAAM,OACvB,SAAUC,EAAM,CAAC,CACnB,GAKN,OAAO,IACT,CAMA,MAAM,QACJE,EACAC,EACAC,EACAC,EACe,CACf,KAAK,IAAI,sBAAsBH,EAAQ,mBAAmBA,EAAQ,QAAQ,EAE1E,GAAI,CACF,IAAMI,EAAW,MAAMH,EAAS,QAAQ,CACtC,QAASD,EAAQ,OACjB,QAASA,EAAQ,MACjB,QAAS,CACP,QAASE,EAAQ,QACjB,OAAQA,EAAQ,OAChB,OAAQ,WAAWF,EAAQ,MAAME,EAAQ,QAC3C,CACF,CAAC,EAED,GAAIE,EAAS,MAAO,CAClB,KAAK,IAAI,YAAYJ,EAAQ,iBAAiBI,EAAS,OAAO,EAC9D,MAAMD,EAAQ,YAAYH,EAAQ,+BAA+BI,EAAS,OAAO,EACjF,OAIEA,EAAS,SACX,MAAMD,EAAQC,EAAS,OAAO,EAGhC,KAAK,IAAI,YAAYJ,EAAQ,sBAAsBI,EAAS,YAAY,CAC1E,OAASd,EAAP,CACA,KAAK,IAAI,YAAYU,EAAQ,gBAAgBV,EAAE,SAAS,EACxD,MAAMa,EAAQ,kBAAkBb,EAAE,SAAS,CAC7C,CACF,CAKA,sBAAqC,CACnC,OAAO,KAAK,SACT,OAAOe,GAAKA,EAAE,IAAI,QAAQ,EAC1B,IAAIA,GAAKA,EAAE,GAAG,CACnB,CAKA,MAAiG,CAC/F,OAAO,KAAK,SAAS,IAAIA,IAAM,CAC7B,GAAIA,EAAE,IAAI,GACV,KAAMA,EAAE,IAAI,KACZ,SAAUA,EAAE,SAAS,OACrB,MAAOA,EAAE,IAAI,MACb,UAAW,CAAC,CAACA,EAAE,IAAI,QACrB,EAAE,CACJ,CACF,ECtKO,IAAMC,GAAN,KAAmB,CAChB,UAA0C,IAAI,IAEtD,YAAYC,EAAwB,CAClC,OAAW,CAACC,EAASC,CAAK,IAAK,OAAO,QAAQF,EAAO,QAAQ,EAAG,CAC9D,IAAMG,EAAOH,EAAO,MAAME,EAAM,IAAI,EACpC,GAAI,CAACC,EACH,MAAM,IAAI,MAAM,sCAAsCD,EAAM,oBAAoBD,IAAU,EAE5F,KAAK,UAAU,IAAIA,EAAS,CAC1B,QAAAA,EACA,KAAAE,EACA,QAASD,EAAM,KACf,UAAWA,EAAM,UACjB,SAAUA,EAAM,SAChB,kBAAmBA,EAAM,iBAC3B,CAAC,EAIH,QAAWE,KAAO,KAAK,UAAU,OAAO,EACtC,GAAIA,EAAI,WAAa,CAAC,KAAK,UAAU,IAAIA,EAAI,SAAS,EACpD,MAAM,IAAI,MAAM,UAAUA,EAAI,sCAAsCA,EAAI,YAAY,EAKxF,QAAWA,KAAO,KAAK,UAAU,OAAO,EAAG,CACzC,IAAMC,EAAO,IAAI,IACbC,EAA0BF,EAAI,QAClC,KAAOE,GAAK,CACV,GAAID,EAAK,IAAIC,CAAG,EACd,MAAM,IAAI,MAAM,uCAAuCF,EAAI,UAAU,EAEvEC,EAAK,IAAIC,CAAG,EACZA,EAAM,KAAK,UAAU,IAAIA,CAAG,GAAG,WAGrC,CAEA,IAAIL,EAA8C,CAChD,OAAO,KAAK,UAAU,IAAIA,CAAO,CACnC,CAEA,KAAyB,CACvB,MAAO,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC,CACpC,CAEA,UAAUA,EAAqC,CAC7C,OAAO,KAAK,UAAU,IAAIA,CAAO,GAAG,SACtC,CAEA,cAAcA,EAA2B,CACvC,MAAO,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC,EAC/B,OAAQM,GAAMA,EAAE,YAAcN,CAAO,EACrC,IAAKM,GAAMA,EAAE,OAAO,CACzB,CAGA,eAAeN,EAA2B,CACxC,IAAMO,EAAkB,CAAC,EACrBF,EAA0BL,EAC9B,KAAOK,GAAO,CAACE,EAAM,SAASF,CAAG,GAC/BE,EAAM,KAAKF,CAAG,EACdA,EAAM,KAAK,UAAU,IAAIA,CAAG,GAAG,UAEjC,OAAOE,CACT,CACF,EC5EA,IAAMC,GAAkC,CACtC,IAAK,EAAG,IAAK,EAAG,IAAK,EAAG,IAAK,EAAG,IAAK,EAAG,IAAK,EAAG,IAAK,CACvD,EAGA,SAASC,GAAQC,EAAYC,EAAiE,CAS5F,IAAMC,EAPM,IAAI,KAAK,eAAe,QAAS,CAC3C,SAAUD,EACV,QAAS,QACT,KAAM,UACN,OAAQ,UACR,OAAQ,EACV,CAAC,EACiB,cAAcD,CAAI,EAC9BG,GAAWD,EAAM,KAAME,GAAMA,EAAE,OAAS,SAAS,GAAG,OAAS,OAAO,YAAY,EAAE,MAAM,EAAG,CAAC,EAC5FC,EAAUH,EAAM,KAAME,GAAMA,EAAE,OAAS,MAAM,GAAG,OAAS,KACzDE,EAASJ,EAAM,KAAME,GAAMA,EAAE,OAAS,QAAQ,GAAG,OAAS,KAGhE,MAAO,CACL,KAFW,SAASC,EAAS,EAAE,EAAI,GAGnC,OAAQ,SAASC,EAAQ,EAAE,EAC3B,IAAKR,GAAQK,CAAO,GAAK,CAC3B,CACF,CAEA,SAASI,EAAUC,EAAmB,CACpC,GAAM,CAACC,EAAGC,CAAC,EAAIF,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM,EACtC,OAAOC,EAAI,GAAKC,CAClB,CAEA,SAASC,GAAWC,EAA4BC,EAAsB,CACpE,OAAOD,EAAS,KAAK,KAAME,GAAMhB,GAAQgB,CAAC,IAAMD,CAAG,CACrD,CAGO,SAASE,GAAiBH,EAA4BZ,EAAYC,EAA2B,CAClG,GAAM,CAAE,KAAAe,EAAM,OAAAC,EAAQ,IAAAJ,CAAI,EAAId,GAAQC,EAAMC,CAAQ,EACpD,GAAI,CAACU,GAAWC,EAAUC,CAAG,EAAG,MAAO,GACvC,IAAMK,EAASF,EAAO,GAAKC,EACrBE,EAAWZ,EAAUK,EAAS,KAAK,EACnCQ,EAASb,EAAUK,EAAS,GAAG,EACrC,GAAIM,EAASC,GAAYD,GAAUE,EAAQ,MAAO,GAClD,GAAIR,EAAS,MAAO,CAClB,IAAMS,EAASd,EAAUK,EAAS,MAAM,KAAK,EACvCU,EAAOf,EAAUK,EAAS,MAAM,GAAG,EACzC,GAAIM,GAAUG,GAAUH,EAASI,EAAM,MAAO,GAEhD,MAAO,EACT,CAEO,IAAMC,GAAN,KAAe,CACpB,YACUC,EACAC,EACR,CAFQ,SAAAD,EACA,YAAAC,CACP,CAEH,UAAUC,EAAiB1B,EAAa,IAAI,KAAiB,CAC3D,IAAM2B,EAAM,KAAK,IAAI,IAAID,CAAO,EAChC,OAAKC,EACEZ,GAAiBY,EAAI,SAAU3B,EAAM,KAAK,OAAO,QAAQ,EAD/C,EAEnB,CAGA,WAAW0B,EAAiB1B,EAAa,IAAI,KAAiB,CAC5D,IAAM2B,EAAM,KAAK,IAAI,IAAID,CAAO,EAChC,GAAI,CAACC,EAAK,MAAO,GACjB,GAAM,CAAE,KAAAX,EAAM,OAAAC,EAAQ,IAAAJ,CAAI,EAAId,GAAQC,EAAM,KAAK,OAAO,QAAQ,EAChE,GAAI,CAACW,GAAWgB,EAAI,SAAUd,CAAG,EAAG,MAAO,GAC3C,IAAMM,EAAWZ,EAAUoB,EAAI,SAAS,KAAK,EAC7C,OAAOX,EAAO,GAAKC,IAAWE,CAChC,CAGA,SAASO,EAAiB1B,EAAa,IAAI,KAAiB,CAC1D,IAAM2B,EAAM,KAAK,IAAI,IAAID,CAAO,EAChC,GAAI,CAACC,EAAK,MAAO,GACjB,GAAM,CAAE,KAAAX,EAAM,OAAAC,EAAQ,IAAAJ,CAAI,EAAId,GAAQC,EAAM,KAAK,OAAO,QAAQ,EAChE,GAAI,CAACW,GAAWgB,EAAI,SAAUd,CAAG,EAAG,MAAO,GAC3C,IAAMO,EAASb,EAAUoB,EAAI,SAAS,GAAG,EACzC,OAAOX,EAAO,GAAKC,IAAWG,CAChC,CAEA,gBAAgBpB,EAAa,IAAI,KAAkB,CACjD,OAAO,KAAK,IAAI,IAAI,EACjB,OAAQ4B,GAAM,KAAK,UAAUA,EAAE,QAAS5B,CAAI,CAAC,EAC7C,IAAK4B,GAAMA,EAAE,OAAO,CACzB,CACF,ECxFO,IAAMC,GAAN,KAAe,CACpB,YACUC,EACAC,EACAC,EACAC,EACR,CAJQ,YAAAH,EACA,SAAAC,EACA,YAAAC,EACA,SAAAC,CACP,CAEH,MAAM,WAAWC,EAAcC,EAAiC,CAC9D,IAAMC,EAAO,KAAK,OAAO,YACzB,GAAI,CACF,MAAM,KAAK,OAAO,aAAa,CAC7B,QAASA,EAAK,QACd,OAAQA,EAAK,OACb,KAAAF,EACA,QAAAC,EACA,UAAWC,EAAK,SAClB,CAAC,CACH,OAASC,EAAP,CACA,KAAK,IAAI,iCAAiCA,EAAE,SAAS,CACvD,CACF,CAGA,MAAM,SAASC,EAAmBC,EAAiBC,EAA+B,CAChF,IAAMC,EAAQ,KAAK,IAAI,eAAeH,CAAS,EAAE,MAAM,CAAC,EAClDI,EAAUD,EAAM,OAASA,EAAM,MAAM,EAAG,CAAC,EAAI,CAAC,EAC9CE,EAAO,kCAAwBL,cAAiBC;AAAA;AAAA,EAAcC,IAEpE,GAAI,CAACE,EAAQ,OAAQ,CAEnB,MAAM,KAAK,WAAWC,EAAML,CAAS,EACrC,OAIF,MAAM,KAAK,WAAW,GAAGK;AAAA;AAAA,MAAeD,EAAQ,IAAKE,GAAM,IAAIA,GAAG,EAAE,KAAK,GAAG,IAAKN,CAAS,CAC5F,CACF,EC5CA,OAAS,iBAAAO,GAAe,cAAAC,GAAY,aAAAC,GAAW,gBAAAC,OAAoB,KACnE,OAAS,WAAAC,OAAe,OAwBxB,SAASC,IAAgB,CACvB,OAAO,IAAI,KAAK,EAAE,YAAY,EAAE,MAAM,EAAG,EAAE,CAC7C,CAEO,IAAMC,GAAN,KAAU,CAKf,YACUC,EACAC,EACAC,EACR,CAHQ,SAAAF,EACA,cAAAC,EACA,SAAAC,EAER,KAAK,IAAML,GAAQ,QAAQ,IAAI,EAAG,aAAa,EAC1CH,GAAW,KAAK,GAAG,GAAGC,GAAU,KAAK,IAAK,CAAE,UAAW,EAAK,CAAC,EAClE,KAAK,MAAQ,KAAK,WAAW,CAC/B,CAZQ,MACA,IACA,YAAmC,IAAI,IAYvC,YAAyB,CAC/B,IAAMQ,EAAIN,GAAQ,KAAK,IAAK,GAAGC,GAAM,QAAQ,EAC7C,GAAIJ,GAAWS,CAAC,EACd,GAAI,CAAE,OAAO,KAAK,MAAMP,GAAaO,EAAG,OAAO,CAAC,CAAE,MAAE,CAA2B,CAEjF,IAAMC,EAAmC,CAAC,EAC1C,QAAWC,KAAO,KAAK,IAAI,IAAI,EAC7BD,EAASC,EAAI,OAAO,EAAI,CACtB,eAAgB,EAChB,kBAAmB,EACnB,eAAgB,EAChB,SAAU,EACV,YAAa,CACf,EAEF,MAAO,CAAE,KAAMP,GAAM,EAAG,SAAAM,CAAS,CACnC,CAGA,MAAa,CACX,IAAME,EAAM,KAAK,IAAI,EACrB,QAAWD,KAAO,KAAK,IAAI,IAAI,EAAG,CAChC,GAAI,CAAC,KAAK,SAAS,UAAUA,EAAI,OAAO,EAAG,CACzC,KAAK,YAAY,OAAOA,EAAI,OAAO,EACnC,SAEF,IAAME,EAAO,KAAK,YAAY,IAAIF,EAAI,OAAO,EAC7C,GAAIE,EAAM,CACR,IAAMC,EAAQ,KAAK,IAAIF,EAAMC,EAAM,IAAO,EAAI,IACxCE,EAAM,KAAK,MAAM,SAASJ,EAAI,OAAO,EACvCI,IAAKA,EAAI,gBAAkBD,GAEjC,KAAK,YAAY,IAAIH,EAAI,QAASC,CAAG,EAEvC,KAAK,cAAc,EACnB,KAAK,QAAQ,CACf,CAEA,qBAAqBI,EAAyB,CAC5C,IAAMD,EAAM,KAAK,MAAM,SAASC,EAAE,OAAO,EACpCD,IACLA,EAAI,gBAAkB,EACtBA,EAAI,mBAAqBC,EAAE,gBAC3B,KAAK,cAAc,EACnB,KAAK,QAAQ,EACf,CAEA,cAAcC,EAAuB,CACnC,IAAMF,EAAM,KAAK,MAAM,SAASE,CAAO,EAClCF,IACLA,EAAI,UAAY,EAChB,KAAK,QAAQ,EACf,CAEQ,eAAsB,CAC5B,QAAWA,KAAO,OAAO,OAAO,KAAK,MAAM,QAAQ,EACjDA,EAAI,YAAcA,EAAI,eAAiB,EACnC,KAAK,IAAI,EAAGA,EAAI,kBAAoBA,EAAI,cAAc,EACtD,CAER,CAEQ,SAAgB,CAElB,KAAK,MAAM,OAASX,GAAM,IAC5B,KAAK,MAAM,EACX,KAAK,MAAQ,KAAK,WAAW,GAE/B,GAAI,CACFL,GAAcI,GAAQ,KAAK,IAAK,GAAG,KAAK,MAAM,WAAW,EAAG,KAAK,UAAU,KAAK,MAAO,KAAM,CAAC,CAAC,CACjG,OAAS,EAAP,CACA,KAAK,IAAI,kCAAkC,EAAE,SAAS,CACxD,CACF,CAEA,OAAc,CACZ,KAAK,QAAQ,CACf,CAEA,UAAuB,CACrB,OAAO,KAAK,MAAM,KAAK,UAAU,KAAK,KAAK,CAAC,CAC9C,CAGA,kBAA2B,CACzB,IAAMe,EAAkB,CAAC,mCAAuB,KAAK,MAAM,SAAU,EAAE,EACnEC,EAAc,EACdC,EAAe,EACnB,OAAW,CAACH,EAASF,CAAG,IAAK,OAAO,QAAQ,KAAK,MAAM,QAAQ,EAAG,CAChEI,GAAeJ,EAAI,kBACnBK,GAAgBL,EAAI,eACpB,IAAMM,GAAON,EAAI,kBAAoB,MAAM,QAAQ,CAAC,EAC9CO,GAAQP,EAAI,YAAc,KAAK,QAAQ,CAAC,EAC9CG,EAAM,KAAK,YAAOD,cAAeF,EAAI,yBAAyBM,cAAgBC,iBAAoBP,EAAI,SAAW,KAAKA,EAAI,sBAAwB,IAAI,EAExJG,EAAM,KAAK,EAAE,EACb,IAAMK,EAAcH,EAAe,GAAMD,EAAcC,EAAgB,KAAK,QAAQ,CAAC,EAAI,IACzF,OAAAF,EAAM,KAAK,cAAcC,EAAc,MAAM,QAAQ,CAAC,gBAAgBC,EAAe,MAAM,QAAQ,CAAC,sBAAiBG,gBAA0B,EACxIL,EAAM,KAAK;AAAA,CAAI,CACxB,CACF,EC5HA,SAASM,EAAKC,EAAqBC,EAAgBC,EAAqB,CACtEF,EAAI,WAAaC,EACjBD,EAAI,UAAU,eAAgB,kBAAkB,EAChDA,EAAI,IAAI,KAAK,UAAUE,CAAI,CAAC,CAC9B,CAEA,eAAeC,GAASC,EAAwD,CAC9E,OAAO,IAAI,QAAQ,CAACC,EAASC,IAAW,CACtC,IAAMC,EAAmB,CAAC,EAC1BH,EAAI,GAAG,OAASI,GAAMD,EAAO,KAAKC,CAAW,CAAC,EAC9CJ,EAAI,GAAG,MAAO,IAAM,CAClB,IAAMK,EAAM,OAAO,OAAOF,CAAM,EAAE,SAAS,OAAO,EAClD,GAAI,CAACE,EAAK,OAAOJ,EAAQ,CAAC,CAAC,EAC3B,GAAI,CAAEA,EAAQ,KAAK,MAAMI,CAAG,CAAC,CAAE,OAASC,EAAP,CAAYJ,EAAOI,CAAC,CAAE,CACzD,CAAC,EACDN,EAAI,GAAG,QAASE,CAAM,CACxB,CAAC,CACH,CAMA,eAAsBK,GACpBC,EACAR,EACAJ,EACAa,EACkB,CAElB,GAAID,IAAU,uBAAwB,CACpC,IAAME,EAAM,IAAI,KACVC,EAAUF,EAAK,SAAS,gBAAgBC,CAAG,EACjD,OAAAf,EAAKC,EAAK,IAAK,CACb,KAAMc,EAAI,YAAY,EACtB,QAAAC,EACA,UAAWF,EAAK,IAAI,IAAI,EAAE,IAAKH,IAAO,CACpC,QAASA,EAAE,QACX,KAAMA,EAAE,KAAK,MACb,UAAWA,EAAE,UACb,QAASK,EAAQ,SAASL,EAAE,OAAO,CACrC,EAAE,EACF,IAAKG,EAAK,IAAI,SAAS,CACzB,CAAC,EACM,GAIT,GAAID,EAAM,WAAW,oBAAoB,EAAG,CAE1C,IAAMI,EADM,IAAI,IAAIZ,EAAI,KAAO,IAAK,UAAU,EAC1B,aAAa,IAAI,OAAO,EAC5C,GAAI,CAACY,EAAW,OAAAjB,EAAKC,EAAK,IAAK,CAAE,MAAO,kBAAmB,CAAC,EAAU,GACtE,GAAI,CACF,IAAMiB,EAAQ,MAAMJ,EAAK,WAAW,SAASG,CAAO,EACpDjB,EAAKC,EAAK,IAAK,CAAE,MAAAiB,CAAM,CAAC,CAC1B,OAASP,EAAP,CACAX,EAAKC,EAAK,IAAK,CAAE,MAAOU,EAAE,OAAQ,CAAC,CACrC,CACA,MAAO,GAIT,GAAIE,IAAU,uBAAwB,CACpC,IAAMV,EAAO,MAAMC,GAASC,CAAG,EACzB,CAAE,MAAAc,EAAO,OAAAC,CAAO,EAAIjB,EAC1B,GAAI,CAACgB,GAAS,CAACC,EAAU,OAAApB,EAAKC,EAAK,IAAK,CAAE,MAAO,2BAA4B,CAAC,EAAU,GACxF,GAAI,CACF,MAAMa,EAAK,WAAW,MAAMK,EAAOC,CAAM,EACzCpB,EAAKC,EAAK,IAAK,CAAE,GAAI,EAAK,CAAC,CAC7B,OAASU,EAAP,CACAX,EAAKC,EAAK,IAAK,CAAE,MAAOU,EAAE,OAAQ,CAAC,CACrC,CACA,MAAO,GAIT,GAAIE,IAAU,wBAAyB,CACrC,IAAMV,EAAO,MAAMC,GAASC,CAAG,EAG/B,GAAI,CAACF,EAAK,QAAU,CAACA,EAAK,OAAU,OAAAH,EAAKC,EAAK,IAAK,CAAE,MAAO,4BAA6B,CAAC,EAAU,GACpG,GAAI,CAAC,CAAC,cAAe,OAAQ,SAAS,EAAE,SAASE,EAAK,MAAM,EAC1D,OAAAH,EAAKC,EAAK,IAAK,CAAE,MAAO,6CAA8C,CAAC,EAAU,GAEnF,GAAI,CACF,MAAMa,EAAK,WAAW,OAAOX,EAAK,OAAQ,CACxC,OAAQA,EAAK,OACb,KAAMA,EAAK,KACX,YAAaA,EAAK,YAClB,QAASA,EAAK,OAChB,CAAC,EACDH,EAAKC,EAAK,IAAK,CAAE,GAAI,EAAK,CAAC,CAC7B,OAASU,EAAP,CACAX,EAAKC,EAAK,IAAK,CAAE,MAAOU,EAAE,OAAQ,CAAC,CACrC,CACA,MAAO,GAIT,GAAIE,IAAU,0BAA2B,CACvC,IAAMV,EAAO,MAAMC,GAASC,CAAG,EAC/B,MAAI,CAACF,EAAK,OAAS,CAACA,EAAK,SAAWH,EAAKC,EAAK,IAAK,CAAE,MAAO,4BAA6B,CAAC,EAAU,KACpG,MAAMa,EAAK,SAAS,SAASX,EAAK,MAAOA,EAAK,QAASA,EAAK,QAAU,EAAE,EACxEW,EAAK,IAAI,cAAcX,EAAK,KAAK,EACjCH,EAAKC,EAAK,IAAK,CAAE,GAAI,EAAK,CAAC,EACpB,IAIT,GAAIY,IAAU,sBAAuB,CACnC,IAAMV,EAAO,MAAMC,GAASC,CAAG,EAC/B,OAAKF,EAAK,MACV,MAAMW,EAAK,SAAS,WAAWX,EAAK,KAAMA,EAAK,KAAK,EACpDH,EAAKC,EAAK,IAAK,CAAE,GAAI,EAAK,CAAC,EACpB,KAHWD,EAAKC,EAAK,IAAK,CAAE,MAAO,eAAgB,CAAC,EAAU,IAMvE,MAAO,EACT,CAMO,SAASoB,GAAiBC,EAA4B,CAC3D,MAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAMKA;AAAA;AAAA,sBAEQA;AAAA;AAAA,sBAEAA;AAAA;AAAA;AAAA;AAAA,sBAIAA;AAAA;AAAA;AAAA,sBAGAA;AAAA;AAAA;AAAA;AAAA,CAKtB,CC3JA,IAAMC,GAAU,IASHC,GAAN,KAAe,CAMpB,YACUC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACR,CATQ,YAAAR,EACA,SAAAC,EACA,cAAAC,EACA,cAAAC,EACA,cAAAC,EACA,SAAAC,EACA,gBAAAC,EACA,gBAAAC,EACA,SAAAC,EAER,QAAWC,KAAOR,EAAI,IAAI,EAAG,KAAK,IAAI,IAAIQ,EAAI,QAAS,CAAC,CAAC,CAC3D,CAjBQ,MACA,IAAkC,IAAI,IACtC,YAAsB,IAAI,KAAK,EAAE,YAAY,EAAE,MAAM,EAAG,EAAE,EAC1D,YAA2B,IAAI,IAgBvC,OAAc,CACZ,KAAK,IAAI,+BAA+B,EAExC,KAAK,KAAK,EAAE,MAAO,GAAM,KAAK,IAAI,0BAA0B,EAAE,SAAS,CAAC,EACxE,KAAK,MAAQ,YAAY,IAAM,CAC7B,KAAK,KAAK,EAAE,MAAO,GAAM,KAAK,IAAI,0BAA0B,EAAE,SAAS,CAAC,CAC1E,EAAGX,EAAO,CACZ,CAEA,MAAa,CACP,KAAK,OAAO,cAAc,KAAK,KAAK,EACxC,KAAK,MAAQ,OACb,KAAK,IAAI,MAAM,CACjB,CAEA,MAAc,MAAsB,CAClC,IAAMY,EAAM,IAAI,KACVC,EAAMD,EAAI,YAAY,EAAE,MAAM,EAAG,EAAE,EAGzC,GAAIC,IAAQ,KAAK,YAAa,CAC5B,KAAK,YAAcA,EACnB,QAAWC,KAAO,KAAK,IAAI,KAAK,EAAG,KAAK,IAAI,IAAIA,EAAK,CAAC,CAAC,EAIzD,KAAK,IAAI,KAAK,EAEd,QAAWH,KAAO,KAAK,IAAI,IAAI,EAAG,CAChC,IAAMI,EAAQ,KAAK,IAAI,IAAIJ,EAAI,OAAO,GAAK,CAAC,EAI5C,GAHA,KAAK,IAAI,IAAIA,EAAI,QAASI,CAAK,EAG3B,MAAK,YAAY,IAAIJ,EAAI,OAAO,EAGpC,IAAI,KAAK,SAAS,WAAWA,EAAI,QAASC,CAAG,GAAK,CAACG,EAAM,eAAgB,CACvEA,EAAM,eAAiB,KAAK,IAAI,EAChC,KAAK,YAAYJ,EAAI,OAAO,EAAE,MAAOK,GAAM,KAAK,IAAI,2BAA2BA,EAAE,SAAS,CAAC,EAC3F,SAIF,GAAI,KAAK,SAAS,SAASL,EAAI,QAASC,CAAG,GAAK,CAACG,EAAM,YAAa,CAClEA,EAAM,YAAc,KAAK,IAAI,EAC7B,KAAK,SAASJ,EAAI,OAAO,EAAE,MAAOK,GAAM,KAAK,IAAI,wBAAwBA,EAAE,SAAS,CAAC,EACrF,SAIE,KAAK,SAAS,UAAUL,EAAI,QAASC,CAAG,IAC1BG,EAAM,gBAAkB,KAAK,IAAI,EAAIA,EAAM,gBAAkB,IAAS,MACvE,KAAK,OAAO,kBACzBA,EAAM,eAAiB,KAAK,IAAI,EAChC,KAAK,aAAaJ,EAAI,OAAO,EAAE,MAAOK,GAAM,KAAK,IAAI,6BAA6BA,EAAE,SAAS,CAAC,IAItG,CAEQ,YAAYC,EAAyB,CAC3C,IAAMN,EAAM,KAAK,IAAI,IAAIM,CAAO,EAChC,GAAI,CAACN,EAAK,MAAO,GACjB,IAAMO,EAAUP,EAAI,UAAY,gBAAgBA,EAAI,YAAc,uCAC5DQ,EAAmBR,EAAI,KAAK,iBAAiB,OAC/C;AAAA;AAAA,EAAwBA,EAAI,KAAK,iBAAiB,IAAK,GAAM,KAAK,GAAG,EAAE,KAAK;AAAA,CAAI,IAChF,GACES,EAAMT,EAAI,KAAK,QAAU;AAAA,QAAWA,EAAI,KAAK,UAAY,GAC/D,MAAO,WAAWM,YAAkBN,EAAI,KAAK,SAASO,KAAWC,IAAmBC;AAAA;AAAA,EAAUC,GAAiB,KAAK,UAAU,GAChI,CAEA,MAAc,aAAaJ,EAAiBK,EAA6C,CACvF,GAAI,KAAK,YAAY,IAAIL,CAAO,EAAG,OACnC,KAAK,YAAY,IAAIA,CAAO,EAC5B,IAAMM,EAAQ,KAAK,IAAI,EACvB,GAAI,CACF,IAAMC,EAAO,MAAM,KAAK,SAAS,QAAQ,CACvC,QAASF,EACT,QAAAL,EACA,QAAS,CAAE,QAAS,WAAY,OAAQ,aAAa,KAAK,eAAeA,GAAU,CACrF,CAAC,EACKQ,EAAU,KAAK,OAAOD,EAAK,UAAa,KAAK,IAAI,EAAID,GAAU,GAAI,EACzE,OAAKC,EAAK,OACR,KAAK,IAAI,qBAAqB,CAAE,QAAAP,EAAS,gBAAiBQ,EAAS,QAAS,GAAM,QAAS,UAAW,CAAC,EAElGD,EAAK,OACd,QAAE,CACA,KAAK,YAAY,OAAOP,CAAO,CACjC,CACF,CAEA,MAAc,YAAYA,EAAgC,CACxD,KAAK,IAAI,6BAAwBA,GAAS,EAE1C,IAAMK,EACV,GAFiB,KAAK,YAAYL,CAAO;AAAA;AAAA;AAAA,mCAKNA;AAAA;AAAA;AAAA,mDAK/B,GADgB,MAAM,KAAK,aAAaA,EAASK,CAAM,EAC1C,CACX,IAAMP,EAAQ,KAAK,IAAI,IAAIE,CAAO,GAAK,CAAC,EACxCF,EAAM,aAAe,GACrB,KAAK,IAAI,IAAIE,EAASF,CAAK,EAE/B,CAEA,MAAc,aAAaE,EAAgC,CACzD,IAAMS,EAAQ,MAAM,KAAK,WAAW,SAAST,CAAO,EAAE,MAAM,IAAM,CAAC,CAAC,EACpE,GAAI,CAACS,EAAM,OAAQ,OAEnB,KAAK,IAAI,+BAA0BT,MAAYS,EAAM,cAAc,EAEnE,IAAMJ,EACV,GAFiB,KAAK,YAAYL,CAAO;AAAA;AAAA;AAAA,kCAKPA;AAAA;AAAA;AAAA,sGAK9B,GADgB,MAAM,KAAK,aAAaA,EAASK,CAAM,EAC1C,CACX,IAAMP,EAAQ,KAAK,IAAI,IAAIE,CAAO,GAAK,CAAC,EACxCF,EAAM,aAAe,GACrB,KAAK,IAAI,IAAIE,EAASF,CAAK,EAE/B,CAEA,MAAc,SAASE,EAAgC,CAErD,GAAI,EADU,KAAK,IAAI,IAAIA,CAAO,GAAK,CAAC,GAC7B,aAAc,CACvB,KAAK,IAAI,+BAA+BA,wBAA8B,EACtE,OAEF,KAAK,IAAI,0BAAqBA,GAAS,EAEvC,IAAMK,EACV,GAFiB,KAAK,YAAYL,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAUrC,MAAM,KAAK,aAAaA,EAASK,CAAM,CACzC,CACF,EC5KO,IAAMK,GAAN,KAAoB,CACT,IACA,SACA,WACA,SACA,IACA,SAEhB,YACEC,EACAC,EACAC,EACAC,EACAC,EACA,CACA,KAAK,IAAM,IAAIC,GAAaL,CAAQ,EACpC,KAAK,SAAW,IAAIM,GAAS,KAAK,IAAKN,CAAQ,EAC/C,KAAK,WAAaO,GAAiBP,EAAUC,EAAQG,CAAG,EACxD,KAAK,SAAW,IAAII,GAASL,EAAQ,KAAK,IAAKH,EAAUI,CAAG,EAC5D,KAAK,IAAM,IAAIK,GAAI,KAAK,IAAK,KAAK,SAAUL,CAAG,EAG/C,GAAM,CAACM,EAAMC,CAAI,EAAIV,EAAO,KAAK,KAAK,MAAM,GAAG,EACzCW,EAAa,UAAUF,IAAS,UAAY,YAAcA,KAAQC,GAAQ,UAEhF,KAAK,SAAW,IAAIE,GAClBb,EAAU,KAAK,IAAK,KAAK,SAAUE,EAAU,KAAK,SAAU,KAAK,IAAK,KAAK,WAAYU,EAAYR,CACrG,CACF,CAEA,OAAc,CACZ,KAAK,SAAS,MAAM,CACtB,CAEA,MAAa,CACX,KAAK,SAAS,KAAK,CACrB,CAGA,qBAAqBU,EAAiBC,EAAyBC,EAAkBC,EAAwB,CACvG,KAAK,IAAI,qBAAqB,CAAE,QAAAH,EAAS,gBAAAC,EAAiB,QAAAC,EAAS,QAAAC,CAAQ,CAAC,CAC9E,CAEA,MAAM,WAAWC,EAAeC,EAAsBC,EAAuC,CAC3F,IAAMC,EAAyB,CAC7B,WAAY,KAAK,WACjB,IAAK,KAAK,IACV,SAAU,KAAK,SACf,SAAU,KAAK,SACf,IAAK,KAAK,GACZ,EACA,OAAOC,GAAmBJ,EAAOC,EAAKC,EAAKC,CAAI,CACjD,CAGA,SAAkB,CAChB,IAAME,EAAU,KAAK,SAAS,gBAAgB,EAAE,OAChD,MAAO,mBAAmB,KAAK,IAAI,IAAI,EAAE,qBAAqBA,gBAChE,CACF,EfjDO,IAAMC,GAAN,KAAmB,CAChB,OACA,SACA,OACA,KACA,KACA,MACA,UACA,UACA,SACA,WACA,SACA,IACA,WAAkC,IAAI,IACtC,WACA,cACA,YAER,YAAYC,EAAqB,CAE/B,IAAMC,EADS,IAAIC,EAAO,QAAQ,EACX,aAAa,EAGpC,KAAK,IAAM,IAAIC,IAAoB,CACjCF,EAAQ,GAAGE,CAAI,EACf,IAAMC,EAAOD,EAAK,IAAIE,GAAK,OAAOA,GAAM,SAAWA,EAAI,KAAK,UAAUA,CAAC,CAAC,EAAE,KAAK,GAAG,EAClF,KAAK,aAAa,MAAOD,CAAI,CAC/B,EAGA,KAAK,IAAI,0BAA0B,EACnC,KAAK,WAAaJ,EAClB,KAAK,OAASM,GAAiBN,CAAU,EAGzC,IAAMO,EAAWC,GAAmB,KAAK,MAAM,EAC/C,QAAWC,KAAKF,EACd,KAAK,IAAI,YAAOE,GAAG,EAIrB,GAAM,CAAC,CAAEC,CAAO,EAAI,KAAK,OAAO,KAAK,KAAK,MAAM,GAAG,EACnDC,GAAmB,KAAK,OAAO,OAAQD,GAAW,QAAS,KAAK,GAAG,EAGnE,KAAK,MAAQ,IAAIE,GACjBC,GAAU,QAAQ,IAAI,EAAG,KAAK,KAAK,EAGnC,KAAK,UAAY,IAAIC,GAAiB,KAAK,MAAM,EAGjD,KAAK,SAAW,IAAIC,GAAc,KAAK,OAAQ,KAAK,GAAG,EACvDC,GAAkB,KAAK,QAAQ,EAC/B,KAAK,SAAS,aAAa,KAAK,SAAS,EAGzC,KAAK,UAAY,IAAIC,GAAiB,KAAK,SAAU,KAAK,GAAG,EAC7D,OAAW,CAACC,EAAIC,CAAK,IAAK,OAAO,QAAQ,KAAK,OAAO,MAAM,EACrDA,EAAM,WAAW,SACnB,KAAK,UAAU,SAASD,EAAIC,EAAM,SAAS,EAS/C,GAJA,KAAK,OAAS,IAAIC,EAAc,KAAK,SAAU,KAAK,OAAQ,KAAK,MAAO,KAAK,GAAG,EAChF,KAAK,SAAW,IAAIC,GAAe,KAAK,SAAU,CAAC,EAAG,KAAK,GAAG,EAG1D,OAAO,KAAK,KAAK,OAAO,QAAQ,EAAE,OAAS,EAAG,CAChD,IAAMC,EAAiB,IAAIC,GAAe,KAAK,OAAO,SAAU,KAAK,GAAG,EACxE,KAAK,OAAO,kBAAkBD,CAAc,EAkC9C,GA9BA,KAAK,KAAO,IAAIE,GAAc,KAAK,OAAQ,KAAK,SAAU,KAAK,MAAO,KAAK,GAAG,EAC9E,KAAK,KAAK,kBAAkB,MAAOC,EAAON,EAAOO,EAAOC,IAAsB,CAC5E,IAAMC,EAAM,SAASH,cAAkBE;AAAA,SAA+BR;AAAA,SAAiBO,EAAM,MAAM,EAAG,GAAG,IACzG,KAAK,IAAI,gBAAgBE,GAAK,EAC9B,KAAK,aAAa,eAAgB,KAAK,UAAU,CAAE,MAAAH,EAAO,MAAAN,EAAO,MAAAO,EAAO,kBAAAC,CAAkB,CAAC,CAAC,EAG5F,IAAME,EAAU,KAAK,OAAO,MAAMJ,CAAK,EACvC,GAAII,GAAS,OACX,GAAI,CACF,MAAM,KAAK,OAAO,aAAa,CAC7B,QAASA,EAAQ,OAAO,QACxB,OAAQA,EAAQ,OAAO,OACvB,KAAM,qBAAcJ,gBAAoBE;AAAA,EAAwBD,EAAM,MAAM,EAAG,GAAG,IAClF,QAASP,EACT,UAAWU,EAAQ,OAAO,SAC5B,CAAC,CACH,OAASC,EAAP,CACA,KAAK,IAAI,oCAAoCA,EAAE,SAAS,CAC1D,CAEJ,CAAC,EAGG,KAAK,OAAO,KAAK,UACnB,KAAK,KAAO,IAAIC,EAAQ,KAAK,OAAQ,KAAK,GAAG,EAC7C,KAAK,OAAO,QAAQ,KAAK,IAAI,GAI3B,KAAK,OAAO,UAAU,QACxB,GAAI,CACF,KAAK,SAAW,IAAIC,GAClB,KAAK,OAAO,SACZ,KAAK,OACL,KAAK,SACL,KAAK,OACL,KAAK,GACP,EACA,KAAK,OAAO,YAAY,KAAK,QAAQ,CACvC,OAASF,EAAP,CACA,KAAK,IAAI,qCAAqCA,EAAE,SAAS,CAC3D,CAEJ,CAEA,MAAM,OAAuB,CAC3B,KAAK,IAAI,EAAE,EACX,KAAK,IAAI,8OAA2C,EACpD,KAAK,IAAI,sDAA4C,EACrD,KAAK,IAAI,8OAA2C,EACpD,KAAK,IAAI,EAAE,EACX,KAAK,IAAI,WAAW,KAAK,OAAO,KAAK,SAAS,KAAK,OAAO,KAAK,KAAK,EACpE,KAAK,IAAI,WAAW,KAAK,OAAO,KAAK,MAAM,EAC3C,KAAK,IAAI,EAAE,EAGX,MAAM,KAAK,cAAc,EAGzB,MAAM,KAAK,KAAK,MAAM,EAGlB,KAAK,MACP,MAAM,KAAK,KAAK,MAAM,EAIxB,IAAMG,EAAY,KAAK,MAAM,UAAU,EAAE,IAAIC,IAAM,CACjD,KAAMA,EAAE,KACR,QAASA,EAAE,QACX,OAAQA,EAAE,MACZ,EAAE,GAAK,CAAC,EACR,KAAK,UAAU,MAAMD,CAAS,EAC9B,KAAK,IAAI,0BAA0B,OAAO,KAAK,KAAK,OAAO,MAAM,EAAE,eAAe,EAGlF,KAAK,qBAAqB,EAGtB,KAAK,WACP,KAAK,SAAS,MAAM,EACpB,KAAK,IAAI,KAAK,KAAK,SAAS,QAAQ,GAAG,GAIzC,MAAM,KAAK,aAAa,EAGxB,KAAK,IAAI,EAAE,EACX,KAAK,IAAI,WAAW,EACpB,QAAWd,KAAS,KAAK,SAAS,KAAK,EACrC,KAAK,IAAI,OAAOA,EAAM,OAAOA,EAAM,gBAAWA,EAAM,WAAW,EAIjE,IAAMgB,EAAW,KAAK,KAAK,KAAK,EAChC,GAAIA,EAAS,OAAQ,CACnB,KAAK,IAAI,EAAE,EACX,KAAK,IAAI,cAAc,EACvB,QAAWC,KAAOD,EAAU,CAC1B,IAAME,EAASD,EAAI,QAAU,UAAY,WACzC,KAAK,IAAI,OAAOA,EAAI,OAAOC,aAAaD,EAAI,UAAUA,EAAI,WAAW,GAKzE,GAAI,KAAK,KAAM,CACb,KAAK,IAAI,EAAE,EACX,KAAK,IAAI,eAAe,EACxB,QAAWE,KAAQ,KAAK,KAAK,UAAU,EAAG,CACxC,IAAMD,EAASC,EAAK,QAAU,SAAM,SACpC,KAAK,IAAI,OAAOD,KAAUC,EAAK,SAASA,EAAK,UAAU,GAKvD,QAAQ,IAAI,qBAAuB,SACrC,KAAK,mBAAmB,EAI1B,GAAI,CACF,IAAMC,EAAU,KAAK,SAAS,iBAAiB,EAC3CA,EAAU,GAAG,KAAK,IAAI,YAAYA,8BAAoC,CAC5E,MAAE,CAA0B,CAI5B,GAAI,CACF,IAAMC,EAAS,KAAK,SAAS,6BAA6B,EACtDA,EAAS,GAAG,KAAK,IAAI,wCAAwCA,YAAiB,CACpF,MAAE,CAA0B,CAE5B,KAAK,IAAI,EAAE,EACX,KAAK,IAAI,UAAU,EACnB,KAAK,IAAI,EAAE,EAGX,IAAMC,EAAUC,GAAQ,QAAQ,IAAI,EAAG,oBAAoB,EAC3DC,GAAUC,GAAQH,CAAO,EAAG,CAAE,UAAW,EAAK,CAAC,EAC/CI,GAAcJ,EAAS,OAAO,QAAQ,GAAG,CAAC,EAC1C,KAAK,IAAI,UAAU,QAAQ,QAAQA,IAAU,EAG7C,QAAQ,GAAG,oBAAsBK,GAAQ,CACvC,KAAK,IAAI,uBAAuBA,EAAI,SAAS,EAC7C,KAAK,IAAIA,EAAI,OAAS,EAAE,CAC1B,CAAC,EACD,QAAQ,GAAG,qBAAuBC,GAAW,CAC3C,KAAK,IAAI,wBAAwBA,GAAQ,CAC3C,CAAC,EAGD,IAAIC,EAAW,GACTC,EAAW,MAAOC,GAAmB,CACrCF,IACJA,EAAW,GACX,KAAK,IAAI;AAAA,aAAgBE,gCAAqC,EAC9D,MAAM,KAAK,KAAK,EAClB,EACA,QAAQ,GAAG,SAAU,IAAMD,EAAS,QAAQ,CAAC,EAC7C,QAAQ,GAAG,UAAW,IAAMA,EAAS,SAAS,CAAC,CACjD,CAEA,MAAM,MAAsB,CAC1B,IAAME,EAAQ,KAAK,IAAI,EAEvB,GAAI,CACF,KAAK,IAAI,wBAAwB,EACjC,MAAM,QAAQ,KAAK,CAAC,KAAK,OAAO,QAAQ,EAAG,IAAI,QAAQC,GAAK,WAAWA,EAAG,GAAI,CAAC,CAAC,CAAC,CACnF,OAAStB,EAAP,CACA,KAAK,IAAI,yBAAyBA,EAAE,SAAS,CAC/C,CAEA,GAAI,CAGF,KAAK,OAAO,mBAAmB,CACjC,OAASA,EAAP,CACA,KAAK,IAAI,yBAAyBA,EAAE,SAAS,CAC/C,CAEA,GAAI,CACF,KAAK,IAAI,6CAA6C,EACtD,MAAM,KAAK,KAAK,KAAK,CACvB,MAAE,CAAO,CAET,GAAI,CACF,KAAK,IAAI,0BAA0B,EACnC,KAAK,UAAU,QAAQ,CACzB,MAAE,CAAO,CAET,GAAI,CACE,KAAK,WACP,KAAK,IAAI,8BAA8B,EACvC,KAAK,SAAS,KAAK,EAEvB,MAAE,CAAO,CAET,GAAI,CACE,KAAK,OACP,KAAK,IAAI,oBAAoB,EAC7B,MAAM,KAAK,KAAK,KAAK,EAEzB,MAAE,CAAO,CAKT,GAHI,KAAK,eAAe,aAAa,KAAK,aAAa,EAEnD,KAAK,aAAa,aAAa,KAAK,WAAW,EAC/C,KAAK,cACP,GAAI,CAAE,KAAK,cAAc,MAAM,CAAE,MAAE,CAA0B,CAG3D,KAAK,YACP,KAAK,WAAW,MAAM,EAIxB,GAAI,CACF,IAAMW,EAAUC,GAAQ,QAAQ,IAAI,EAAG,oBAAoB,EACvDW,GAAWZ,CAAO,GAAGa,GAAWb,CAAO,CAC7C,MAAE,CAAO,CAET,KAAK,IAAI,wBAAwB,KAAK,IAAI,EAAIU,MAAU,EACxD,QAAQ,KAAK,CAAC,CAChB,CAEQ,cAOA,oBAA2B,CACjC,IAAMI,EAAO,KAAK,YAAcb,GAAQ,QAAQ,IAAI,EAAG,aAAa,EACpE,GAAKW,GAAWE,CAAI,EACpB,GAAI,CACF,KAAK,cAAgBC,GAAMD,EAAM,CAAE,WAAY,EAAM,EAAIE,GAAc,CACjEA,IAAc,WACd,KAAK,aAAa,aAAa,KAAK,WAAW,EACnD,KAAK,YAAc,WAAW,IAAM,CAClC,KAAK,OAAO,EAAE,MAAO3B,GAAM,KAAK,IAAI,oBAAoBA,GAAG,SAAWA,GAAG,CAAC,CAC5E,EAAG,GAAG,EACR,CAAC,EACD,KAAK,IAAI,cAAcyB,sBAAyB,CAClD,OAASzB,EAAP,CACA,KAAK,IAAI,qCAAqCA,EAAE,SAAS,CAC3D,CACF,CAQA,MAAM,QAAoF,CACxF,IAAI4B,EACJ,GAAI,CACFA,EAAOpD,GAAiB,KAAK,UAAU,CACzC,OAASwB,EAAP,CACA,YAAK,IAAI,8CAA8CA,EAAE,SAAS,EAC3D,CAAE,QAAS,CAAC,EAAG,gBAAiB,CAAC,EAAG,MAAOA,EAAE,OAAQ,CAC9D,CAEA,IAAM6B,EAAoB,CAAC,EACrBC,EAA4B,CAAC,EAGnC,GAAI,KAAK,UAAU,KAAK,OAAO,KAAK,IAAM,KAAK,UAAUF,EAAK,KAAK,EACjE,GAAI,CACF,MAAM,KAAK,KAAK,KAAK,EACrB,KAAK,KAAO,IAAIlC,GAAckC,EAAM,KAAK,SAAU,KAAK,MAAO,KAAK,GAAG,EACvE,KAAK,KAAK,kBAAkB,MAAOjC,EAAON,EAAOO,EAAOC,IAAsB,CAC5E,KAAK,IAAI,sBAAsBF,cAAkBE,cAAyBD,EAAM,MAAM,EAAG,GAAG,GAAG,EAC/F,KAAK,aAAa,eAAgB,KAAK,UAAU,CAAE,MAAAD,EAAO,MAAAN,EAAO,MAAAO,EAAO,kBAAAC,CAAkB,CAAC,CAAC,EAC5F,IAAME,EAAU6B,EAAK,MAAMjC,CAAK,EAChC,GAAII,GAAS,OACX,GAAI,CACF,MAAM,KAAK,OAAO,aAAa,CAC7B,QAASA,EAAQ,OAAO,QACxB,OAAQA,EAAQ,OAAO,OACvB,KAAM,SAASJ,cAAkBE;AAAA,EAAwBD,EAAM,MAAM,EAAG,GAAG,IAC3E,QAASP,EACT,UAAWU,EAAQ,OAAO,SAC5B,CAAC,CACH,OAASC,EAAP,CACA,KAAK,IAAI,oCAAoCA,EAAE,SAAS,CAC1D,CAEJ,CAAC,EACD,MAAM,KAAK,KAAK,MAAM,EACtB6B,EAAQ,KAAK,OAAO,CACtB,OAAS7B,EAAP,CACA,KAAK,IAAI,gCAAgCA,EAAE,SAAS,CACtD,CAIF,IAAM+B,EAAyC,CAAC,SAAU,WAAY,OAAQ,YAAa,OAAQ,UAAU,EAC7G,QAAWC,KAAKD,EACV,KAAK,UAAW,KAAK,OAAeC,CAAC,CAAC,IAAM,KAAK,UAAWJ,EAAaI,CAAC,CAAC,GAC7EF,EAAgB,KAAK,OAAOE,CAAC,CAAC,EAKlC,YAAK,OAASJ,EAEVC,EAAQ,QAAQ,KAAK,IAAI,qBAAqBA,EAAQ,KAAK,IAAI,GAAG,EAClEC,EAAgB,QAClB,KAAK,IAAI,kDAAkDA,EAAgB,KAAK,IAAI,GAAG,EACvF,KAAK,aAAa,iBAAkB,KAAK,UAAU,CAAE,QAAAD,EAAS,gBAAAC,CAAgB,CAAC,CAAC,GACvED,EAAQ,QACjB,KAAK,aAAa,kBAAmB,KAAK,UAAU,CAAE,QAAAA,CAAQ,CAAC,CAAC,EAG3D,CAAE,QAAAA,EAAS,gBAAAC,CAAgB,CACpC,CAEQ,sBAA6B,CACnC,IAAMG,EAAe,IAAM,CACzB,IAAMC,EAAM,IAAI,KAEVC,EAAWD,EAAI,eAAe,QAAS,CAAE,SAAU,cAAe,CAAC,EACnEE,EAAW,IAAI,KAAKD,CAAQ,EAE5BE,EAAS,IAAI,KAAKD,CAAQ,EAChCC,EAAO,QAAQA,EAAO,QAAQ,EAAI,CAAC,EACnCA,EAAO,SAAS,EAAG,EAAG,EAAG,CAAC,EAE1B,IAAMC,EAAcF,EAAS,QAAQ,EAAIF,EAAI,QAAQ,EAC/CK,EAAQF,EAAO,QAAQ,EAAIC,EAAcJ,EAAI,QAAQ,EAE3D,KAAK,IAAI,gCAAgC,KAAK,MAAMK,EAAQ,GAAM,MAAM,EAExE,KAAK,cAAgB,WAAW,SAAY,CAC1C,MAAM,KAAK,mBAAmB,EAC9BN,EAAa,CACf,EAAG,KAAK,IAAIM,EAAO,GAAM,CAAC,CAC5B,EAEAN,EAAa,CACf,CAEA,MAAc,oBAAoC,CAChD,IAAMO,EAAY,IAAI,KAAK,KAAK,IAAI,EAAI,KAAS,EAAE,YAAY,EAAE,MAAM,EAAG,EAAE,EACtEC,EAAU,KAAK,SAAS,gBAAgB,EACxCC,EAASD,EAAQ,oBAAoBD,CAAS,EAEpD,GAAI,CAACE,GAAUA,EAAO,aAAe,EAAG,CACtC,KAAK,IAAI,iCAAiCF,aAAqB,EAC/D,OAGFC,EAAQ,mBAAmBC,CAAM,EACjC,KAAK,IACH,oBAAoBF,YAAeE,EAAO,sBAAsBA,EAAO,UAAU,QAAQ,CAAC,WAAWA,EAAO,aAAaA,EAAO,QAAQ,QAAQ,CAAC,IACnJ,CACF,CAEA,MAAc,eAA+B,CAE3C,GAAI,KAAK,OAAO,SAAS,SAAS,QAAS,CACzC,IAAMC,EAAW,KAAK,OAAO,SAAS,SAAS,SAC/C,GAAI,OAAO,KAAKA,CAAQ,EAAE,OAAS,EAAG,CACpC,IAAMC,EAAS,KAAK,OAAO,SAAS,SAAS,OACvCC,EAAW,IAAIC,EACnBH,EACA,CAAE,OAAQ,CAAE,UAAWC,GAAQ,SAAU,CAAE,EAC3C,KAAK,GACP,EACA,KAAK,OAAO,WAAWC,CAAQ,EAC/B,IAAME,EAAaH,GAAQ,WAAW,QAAU,EAC1CI,EAAc,OAAO,QAAQL,CAAQ,EAAE,OAC3C,CAAC,CAAC,CAAEM,CAAC,IAAM,CAACA,EAAE,WAAaF,IAAe,CAC5C,EAAE,OACEC,EAAc,EAChB,KAAK,IACH,uCAAkCA,gGACpC,EAEA,KAAK,IAAI,wDAAmDD,GAAY,GAM9E,GAAI,KAAK,OAAO,SAAS,SAAS,QAAS,CACzC,GAAM,CAAE,cAAAG,EAAe,kBAAAC,CAAkB,EAAI,KAAM,QAAO,8BAAkB,EACtEC,EAAW,IAAIC,EACnB,CACE,WAAY,KAAK,OAAO,SAAS,SAAS,WAC1C,aAAc,KAAK,OAAO,SAAS,SAAS,aAC5C,UAAW,KAAK,OAAO,SAAS,SAAS,UACzC,OAAQ,KAAK,OAAO,SAAS,SAAS,OAGtC,KAAMH,EACN,SAAUC,CACZ,EACA,KAAK,GACP,EACA,KAAK,OAAO,WAAWC,CAAQ,EAC/B,KAAK,IAAI,wBAAwB,KAAK,OAAO,SAAS,SAAS,OAAO,gBAAgB,EAIxF,GAAI,KAAK,OAAO,SAAS,SAAS,SAAW,KAAK,OAAO,SAAS,QAAQ,MAAO,CAC/E,IAAME,EAAU,IAAIC,EAClB,CACE,MAAO,KAAK,OAAO,SAAS,QAAQ,MACpC,aAAc,KAAK,OAAO,SAAS,QAAQ,YAC7C,EACA,KAAK,GACP,EACA,KAAK,OAAO,WAAWD,CAAO,EAC9B,KAAK,IAAI,oBAAoB,EAI/B,GAAI,KAAK,OAAO,SAAS,OAAO,SAAW,KAAK,OAAO,SAAS,MAAM,UAAY,KAAK,OAAO,SAAS,MAAM,SAAU,CACrH,IAAME,EAAQ,IAAIC,GAChB,CACE,SAAU,KAAK,OAAO,SAAS,MAAM,SACrC,SAAU,KAAK,OAAO,SAAS,MAAM,SACrC,aAAc,KAAK,OAAO,SAAS,MAAM,YAC3C,EACA,KAAK,GACP,EACA,KAAK,OAAO,WAAWD,CAAK,EAC5B,KAAK,IAAI,kBAAkB,OAClB,KAAK,OAAO,SAAS,OAAO,SACrC,KAAK,IAAI,yEAAoE,EAI/E,GAAI,KAAK,OAAO,SAAS,QAAQ,SAAW,KAAK,OAAO,SAAS,OAAO,MAAO,CAC7E,IAAME,EAAS,IAAIC,GACjB,CACE,YAAa,KAAK,OAAO,SAAS,OAAO,YACzC,cAAe,KAAK,OAAO,SAAS,OAAO,cAC3C,KAAM,KAAK,OAAO,SAAS,OAAO,KAClC,MAAO,KAAK,OAAO,SAAS,OAAO,MACnC,OAAQ,KAAK,OAAO,SAAS,OAAO,OACpC,cAAe,KAAK,OAAO,SAAS,OAAO,aAC7C,EACA,KAAK,IACL,KAAK,KACP,EAEI,KAAK,OACPD,EAAO,kBAAkB,MAAOE,EAAMC,EAASC,EAAcC,EAAaC,EAAQC,IAAY,CAC5F,IAAMzD,EAAO,KAAK,KAAM,UAAU,EAAE,KAAKJ,GAAKA,EAAE,OAASwD,GAAQxD,EAAE,OAAO,EAC1E,GAAI,CAACI,EAAM,CACT,KAAK,IAAI,iCAAiCoD,2BAA8B,EACxE,OAEF,IAAMM,EAAM,GAAG1D,EAAK,uBACpB,GAAI,CACF,IAAMc,EAAI,MAAM,MAAM4C,EAAK,CACzB,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAM,KAAK,UAAU,CAAE,QAAAL,EAAS,aAAAC,EAAc,YAAAC,EAAa,OAAAC,EAAQ,QAAAC,CAAQ,CAAC,CAC9E,CAAC,EACKE,EAAW,MAAM7C,EAAE,KAAK,EAAE,MAAM,IAAM,EAAE,EAC9C,KAAK,IAAI,6BAA6B4C,OAAS5C,EAAE,UAAU6C,EAAS,MAAM,EAAG,GAAG,GAAG,CACrF,OAASnE,EAAP,CACA,KAAK,IAAI,oCAAoCkE,OAASlE,EAAE,SAAS,CACnE,CACF,CAAC,EAKD0D,EAAO,qBAAqB,MAAOE,EAAMC,EAASC,EAAcC,EAAaE,EAASG,IAA0B,CAC9G,IAAM5D,EAAO,KAAK,KAAM,UAAU,EAAE,KAAKJ,GAAKA,EAAE,OAASwD,GAAQxD,EAAE,OAAO,EAC1E,GAAI,CAACI,EACH,YAAK,IAAI,qCAAqCoD,2BAA8B,EACrE,GAET,IAAMM,EAAM,GAAG1D,EAAK,2BACpB,GAAI,CACF,IAAMc,EAAI,MAAM,MAAM4C,EAAK,CACzB,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAM,KAAK,UAAU,CAAE,QAAAL,EAAS,aAAAC,EAAc,YAAAC,EAAa,QAAAE,EAAS,KAAAG,CAAK,CAAC,CAC5E,CAAC,EACKC,EAAO,MAAM/C,EAAE,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,EAC5C,YAAK,IAAI,iCAAiC4C,OAAS5C,EAAE,iBAAkB+C,EAAa,QAAU,KAAK,EAC3FA,EAAa,QAAU,EACjC,OAASrE,EAAP,CACA,YAAK,IAAI,wCAAwCkE,OAASlE,EAAE,SAAS,EAC9D,EACT,CACF,CAAC,EAGD0D,EAAO,oBAAoB,MAAOE,EAAMC,EAASC,EAAcC,EAAaE,EAASK,IAA8B,CACjH,IAAM9D,EAAO,KAAK,KAAM,UAAU,EAAE,KAAKJ,GAAKA,EAAE,OAASwD,GAAQxD,EAAE,OAAO,EAC1E,GAAI,CAACI,EAAM,CACT,KAAK,IAAI,oCAAoCoD,2BAA8B,EAC3E,OAEF,IAAMM,EAAM,GAAG1D,EAAK,0BACpB,GAAI,CACF,IAAMc,EAAI,MAAM,MAAM4C,EAAK,CACzB,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAM,KAAK,UAAU,CAAE,QAAAL,EAAS,aAAAC,EAAc,YAAAC,EAAa,QAAAE,EAAS,WAAAK,CAAW,CAAC,CAClF,CAAC,EACKH,EAAW,MAAM7C,EAAE,KAAK,EAAE,MAAM,IAAM,EAAE,EAC9C,KAAK,IAAI,gCAAgC4C,OAAS5C,EAAE,UAAU6C,EAAS,MAAM,EAAG,GAAG,GAAG,CACxF,OAASnE,EAAP,CACA,KAAK,IAAI,uCAAuCkE,OAASlE,EAAE,SAAS,CACtE,CACF,CAAC,GAEH,KAAK,OAAO,WAAW0D,CAAM,EAC7B,KAAK,IAAI,sBAAsB,KAAK,OAAO,SAAS,OAAO,OAAO,mCAAmC,KAAK,OAAO,SAAS,OAAO,cAAc,EAGjJ,MAAM,KAAK,OAAO,SAAS,CAC7B,CAEA,MAAc,cAA8B,CAC1C,GAAM,CAACa,EAAM3F,CAAO,EAAI,KAAK,OAAO,KAAK,KAAK,MAAM,GAAG,EACjD4F,EAAO,SAAS5F,GAAW,QAAS,EAAE,EAE5C,KAAK,WAAa6F,GAAa,MAAOC,EAAKC,IAAQ,CAKjD,GAJAA,EAAI,UAAU,8BAA+B,GAAG,EAChDA,EAAI,UAAU,+BAAgC,oBAAoB,EAClEA,EAAI,UAAU,+BAAgC,6BAA6B,EAEvED,EAAI,SAAW,UAAW,CAC5BC,EAAI,UAAU,GAAG,EACjBA,EAAI,IAAI,EACR,OAGF,MAAM,KAAK,WAAWD,EAAKC,CAAG,CAChC,CAAC,EAED,KAAK,WAAW,GAAG,QAAU3D,GAAa,CACpCA,EAAI,OAAS,cACf,KAAK,IAAI,iBAAiBwD,wCAA2C,EACrE,WAAW,IAAM,CACf,KAAK,YAAY,MAAM,EACvB,KAAK,YAAY,OAAOA,EAAMD,GAAQ,SAAS,CACjD,EAAG,GAAI,GAEP,KAAK,IAAI,iBAAiBvD,EAAI,SAAS,CAE3C,CAAC,EAED,KAAK,WAAW,OAAOwD,EAAMD,GAAQ,UAAW,IAAM,CACpD,KAAK,IAAI,sBAAsBA,GAAQ,aAAaC,GAAM,CAC5D,CAAC,CACH,CAKQ,aAAaI,EAAeP,EAAoB,CACtD,GAAI,KAAK,WAAW,OAAS,EAAG,OAChC,IAAMQ,EAAU,UAAUD;AAAA,QAAgB,KAAK,UAAU,CAAE,KAAM,IAAI,KAAK,EAAE,YAAY,EAAG,QAASP,CAAK,CAAC;AAAA;AAAA,EAC1G,QAAWS,KAAU,KAAK,WACxB,GAAI,CAAEA,EAAO,MAAMD,CAAO,CAAE,MAAE,CAAQ,KAAK,WAAW,OAAOC,CAAM,CAAE,CAEzE,CAMQ,UAAUJ,EAAsBC,EAA2B,CACjEA,EAAI,UAAU,IAAK,CACjB,eAAgB,oBAChB,gBAAiB,WACjB,WAAc,aACd,8BAA+B,GACjC,CAAC,EAGD,IAAMI,EAAS,KAAK,SAAS,KAAK,EAC5BC,EAASD,EAAO,OAAOxG,GAAKA,EAAE,OAAS,CAAC,EAC9CoG,EAAI,MAAM;AAAA,QAAwB,KAAK,UAAU,CAC/C,KAAM,KAAK,OAAO,KAAK,KACvB,OAAQI,EAAO,OACf,OAAQC,EAAO,IAAIzG,IAAM,CAAE,GAAIA,EAAE,GAAI,KAAMA,EAAE,KAAM,MAAOA,EAAE,MAAO,EAAE,EACrE,KAAM,KAAK,MAAM,UAAU,EAAE,IAAI6B,IAAM,CAAE,KAAMA,EAAE,KAAM,QAASA,EAAE,OAAQ,EAAE,GAAK,CAAC,CACpF,CAAC;AAAA;AAAA,CAAO,EAER,KAAK,WAAW,IAAIuE,CAAG,EACvBD,EAAI,GAAG,QAAS,IAAM,KAAK,WAAW,OAAOC,CAAG,CAAC,CACnD,CAOQ,iBAAiBD,EAAsBC,EAAqBM,EAAkBC,EAAsB,CAC1GP,EAAI,UAAU,IAAK,CACjB,eAAgB,oBAChB,gBAAiB,WACjB,WAAc,aACd,8BAA+B,IAC/B,oBAAqB,IACvB,CAAC,EACD,IAAIQ,EAAS,GACPC,EAAO,CAACC,EAAYhB,IAAkB,CAC1C,GAAI,CAAAc,EACJ,GAAI,CAAER,EAAI,MAAM,UAAUU;AAAA,QAAa,KAAK,UAAUhB,CAAI;AAAA;AAAA,CAAO,CAAE,MAAE,CAAQc,EAAS,EAAK,CAC7F,EACMG,EAAM,KAAK,SAAS,sBAAsBJ,EAASK,GAAUH,EAAK,QAAS,CAAE,KAAMG,CAAM,CAAC,CAAC,EACjG,GAAI,CAACD,EAAK,CACRF,EAAK,QAAS,CAAE,QAAS,mCAAoC,CAAC,EAC9DT,EAAI,IAAI,EACR,OAGF,GADAS,EAAK,QAAS,CAAE,OAAAF,EAAQ,QAASI,EAAI,QAAS,KAAMA,EAAI,IAAK,CAAC,EAC1DA,EAAI,KAAM,CACZF,EAAK,MAAO,CAAE,OAAQ,kBAAmB,CAAC,EAC1CT,EAAI,IAAI,EACR,OAGF,IAAMa,EAAY,YAAY,IAAM,CAAE,GAAI,CAACL,EAAQ,GAAI,CAAER,EAAI,MAAM;AAAA;AAAA,CAAY,CAAE,MAAE,CAAc,CAAE,EAAG,IAAK,EAC3GD,EAAI,GAAG,QAAS,IAAM,CACpBS,EAAS,GACT,cAAcK,CAAS,EACvBF,EAAI,YAAY,CAClB,CAAC,CACH,CAEA,MAAc,WAAWZ,EAAsBC,EAAoC,CACjF,IAAMT,EAAM,IAAI,IAAIQ,EAAI,KAAO,IAAK,UAAUA,EAAI,QAAQ,MAAQ,aAAa,EACzEjD,EAAOyC,EAAI,SAEjB,GAAI,CAEF,GAAIQ,EAAI,SAAW,OAASjD,IAAS,UAAW,CAC9C,KAAK,UAAUiD,EAAKC,CAAG,EACvB,OAIF,GAAID,EAAI,SAAW,QAAUjD,EAAK,WAAW,WAAW,EAAG,CACzD,MAAM,KAAK,SAAS,OAAOiD,EAAKC,EAAKlD,CAAI,EACzC,OAKF,IAAMgE,EAAkBf,EAAI,SAAW,OAASjD,EAAK,MAAM,6CAA6C,EACxG,GAAIgE,EAAiB,CACnB,KAAK,iBAAiBf,EAAKC,EAAKc,EAAgB,CAAC,EAAGA,EAAgB,CAAC,CAAC,EACtE,OAMF,IAAMC,EAAuBhB,EAAI,SAAW,OAASjD,EAAK,MAAM,4BAA4B,EAC5F,GAAIiE,EAAsB,CACxB,IAAMC,EAAQ,KAAK,IAAI,EAAG,KAAK,IAAI,IAAK,SAASzB,EAAI,aAAa,IAAI,OAAO,GAAK,KAAM,EAAE,GAAK,EAAE,CAAC,EAClG,KAAK,KAAKS,EAAK,IAAK,KAAK,SAAS,gBAAgBe,EAAqB,CAAC,EAAGC,CAAK,CAAC,EACjF,OAEF,IAAMC,EAAkBlB,EAAI,SAAW,OAASjD,EAAK,MAAM,qCAAqC,EAChG,GAAImE,EAAiB,CACnB,IAAMC,EAAM,KAAK,SAAS,cAAcD,EAAgB,CAAC,EAAGA,EAAgB,CAAC,CAAC,EAC9E,GAAI,CAACC,EAAK,CAAE,KAAK,KAAKlB,EAAK,IAAK,CAAE,MAAO,WAAY,CAAC,EAAG,OACzD,KAAK,KAAKA,EAAK,IAAKkB,CAAG,EACvB,OAKF,GAAInB,EAAI,SAAW,SAAWjD,IAAS,wBAA0BA,EAAK,MAAM,uCAAuC,GAAI,CACrH,MAAM,KAAK,mBAAmBiD,EAAKC,EAAKlD,CAAI,EAC5C,OAIF,GAAI,KAAK,UAAYA,EAAK,WAAW,WAAW,GAC9B,MAAM,KAAK,SAAS,WAAW,GAAGiD,EAAI,UAAUjD,IAAQiD,EAAKC,CAAG,EACnE,OAGf,OAAQ,GAAGD,EAAI,UAAUjD,IAAQ,CAC/B,IAAK,cACH,KAAK,KAAKkD,EAAK,IAAK,CAClB,OAAQ,KACR,KAAM,KAAK,OAAO,KAClB,OAAQ,QAAQ,OAAO,EACvB,OAAQ,KAAK,SAAS,KAAK,EAC3B,MAAO,KAAK,KAAK,KAAK,EAAE,IAAKmB,IAAO,CAAE,GAAIA,EAAE,GAAI,QAASA,EAAE,QAAS,QAASA,EAAE,OAAQ,EAAE,EACzF,KAAM,KAAK,MAAM,UAAU,GAAK,CAAC,EACjC,MAAO,KAAK,SAAS,cAAc,CACrC,CAAC,EACD,MAEF,IAAK,aACH,KAAK,KAAKnB,EAAK,IAAK,KAAK,SAAS,SAAS,CAAC,CAAC,EAC7C,MAEF,IAAK,cACH,KAAK,KAAKA,EAAK,IAAK,KAAK,SAAS,KAAK,CAAC,EACxC,MAEF,IAAK,sBAAuB,CAC1B,GAAM,CAAE,iBAAAoB,CAAiB,EAAI,KAAM,QAAO,8BAAkB,EAC5D,KAAK,KAAKpB,EAAK,IAAKoB,EAAiB,CAAC,EACtC,KACF,CAEA,IAAK,uBAAwB,CAC3B,GAAM,CAAE,iBAAAA,CAAiB,EAAI,KAAM,QAAO,8BAAkB,EACtDC,EAAID,EAAiB,EAC3B,GAAI,CAACC,EAAE,GAAI,CAAErB,EAAI,UAAU,IAAK,CAAE,gBAAiB,UAAW,CAAC,EAAGA,EAAI,IAAI,EAAG,MAC7E,GAAI,CAEF,IAAMsB,EAAM,KAAM,QAAO,QAAQ,EAE3BC,EAAc,MADCD,EAAY,SAAWA,GACX,SAASD,EAAE,GAAI,CAAE,KAAM,MAAO,qBAAsB,IAAK,OAAQ,EAAG,MAAO,CAAE,KAAM,OAAQ,MAAO,MAAO,CAAE,CAAC,EAC7IrB,EAAI,UAAU,IAAK,CAAE,eAAgB,+BAAgC,gBAAiB,UAAW,CAAC,EAClGA,EAAI,IAAIuB,CAAG,CACb,OAASlG,EAAP,CACA2E,EAAI,UAAU,IAAK,CAAE,eAAgB,2BAA4B,CAAC,EAClEA,EAAI,IAAI,4CAA8C3E,GAAG,SAAW,UAAU,CAChF,CACA,KACF,CAEA,IAAK,aACH,KAAK,KAAK2E,EAAK,IAAK,KAAK,KAAK,KAAK,CAAC,EACpC,MAEF,IAAK,oBACH,KAAK,KAAKA,EAAK,IAAK,KAAK,KAAK,OAAO,CAAC,EACtC,MAEF,IAAK,YACH,KAAK,KAAKA,EAAK,IAAK,KAAK,MAAM,UAAU,GAAK,CAAC,CAAC,EAChD,MAEF,IAAK,aAAc,CACjB,GAAM,CAAE,cAAAwB,EAAe,aAAAC,CAAa,EAAI,KAAM,QAAO,qBAAuB,EAC5E,KAAK,KAAKzB,EAAK,IAAK,CAAE,GAAGwB,EAAc,EAAG,WAAYC,EAAa,EAAE,CAAE,CAAC,EACxE,KACF,CAEA,IAAK,iBAAkB,CACrB,GAAM,CAAE,SAAAC,CAAS,EAAI,KAAM,QAAO,qBAAuB,EACnDC,EAAOpC,EAAI,aAAa,IAAI,YAAY,GAAG,MAAM,GAAG,GAAK,CAAC,KAAK,EACrEmC,EAAS,GAAMC,CAAW,EAC1B,KAAK,IAAI,kBAAkBA,EAAK,KAAK,IAAI,GAAG,EAC5C,KAAK,KAAK3B,EAAK,IAAK,CAAE,QAAS,GAAM,WAAY2B,CAAK,CAAC,EACvD,KACF,CAEA,IAAK,kBAAmB,CACtB,GAAM,CAAE,SAAAD,CAAS,EAAI,KAAM,QAAO,qBAAuB,EACzDA,EAAS,EAAK,EACd,KAAK,IAAI,gBAAgB,EACzB,KAAK,KAAK1B,EAAK,IAAK,CAAE,QAAS,EAAM,CAAC,EACtC,KACF,CAEA,IAAK,qBAAsB,CAEzB,IAAM4B,EAAO,MAAMC,EAAS9B,CAAG,EACzB,CAAE,QAAAb,EAAS,aAAAC,EAAc,YAAAC,EAAa,OAAAC,EAAQ,QAAAC,CAAQ,EAAIsC,EAE1DE,GADW,KAAK,OAAO,SAAS,QAAQ,eAAiB,CAAC,GACvC,KAAMC,GAAWA,EAAE,UAAYzC,CAAO,EACzD0C,EAAQF,GAAS,OAAS,KAAK,OAAO,SAAS,QAAQ,MACvDlC,EAAO,KAAK,OAAO,SAAS,QAAQ,KAE1C,GADA,KAAK,IAAI,yBAAyBN,eAAqBJ,mBAAyB,CAAC,CAAC4C,kBAAwB,CAAC,CAACA,GAAS,qBAAqB,CAAC,CAAC,KAAK,OAAO,SAAS,QAAQ,cAAc,KAAK,OAAO,SAAS,QAAQ,OAAS,IAAI,gBAAgBlC,GAAQ,WAAW,EAChQ,CAACoC,GAAS,CAACpC,EAAM,CACnB,KAAK,KAAKI,EAAK,IAAK,CAClB,MAAO,4BACP,MAAO,CAAE,QAAAV,EAAS,aAAc,CAAC,CAACwC,EAAS,gBAAiB,CAAC,CAACA,GAAS,MAAO,eAAgB,CAAC,CAAC,KAAK,OAAO,SAAS,QAAQ,MAAO,QAAS,CAAC,CAAClC,CAAK,CACtJ,CAAC,EACD,MAEF,IAAMqC,EAAU,mBAAmB/C,CAAO,EACpCgD,EAAK/C,IAAiB,QACxB,GAAGS,qBAAwBqC,YAAkB7C,WAAqBC,gBAClE,GAAGO,qBAAwBqC,oBAA0B7C,WAAqBC,gBAC9E,GAAI,CACF,IAAM8C,EAAQ,MAAM,MAAMD,EAAI,CAC5B,OAAQ,OACR,QAAS,CAAE,eAAgB,mBAAoB,gBAAiBF,CAAM,EACtE,KAAM,KAAK,UAAU,CAAE,KAAM,MAAO,CAAC,CACvC,CAAC,EACKI,EAAW,MAAMD,EAAM,KAAK,EAAE,MAAM,IAAM,EAAE,EAClD,KAAK,IAAI,0BAA0BD,OAAQC,EAAM,UAAUC,EAAS,MAAM,EAAG,GAAG,GAAG,EACnF,KAAK,KAAKpC,EAAK,IAAK,CAAE,GAAImC,EAAM,GAAI,OAAQA,EAAM,OAAQ,eAAgBC,EAAS,MAAM,EAAG,GAAG,CAAE,CAAC,CACpG,OAAS/G,EAAP,CACA,KAAK,IAAI,+BAA+BA,EAAE,SAAS,EACnD,KAAK,KAAK2E,EAAK,IAAK,CAAE,MAAO3E,EAAE,OAAQ,CAAC,CAC1C,CACA,KACF,CAEA,IAAK,yBAA0B,CAG7B,IAAMuG,EAAO,MAAMC,EAAS9B,CAAG,EACzB,CAAE,QAAAb,EAAS,aAAAC,EAAc,YAAAC,EAAa,QAAAE,EAAS,KAAAG,CAAK,EAAImC,EAGxDI,GAFW,KAAK,OAAO,SAAS,QAAQ,eAAiB,CAAC,GACvC,KAAMD,GAAWA,EAAE,UAAYzC,CAAO,GACxC,OAAS,KAAK,OAAO,SAAS,QAAQ,MACvDM,EAAO,KAAK,OAAO,SAAS,QAAQ,KAC1C,GAAI,CAACoC,GAAS,CAACpC,EAAM,CACnB,KAAK,KAAKI,EAAK,IAAK,CAAE,MAAO,4BAA6B,MAAO,CAAE,QAAAV,EAAS,SAAU,CAAC,CAAC0C,EAAO,QAAS,CAAC,CAACpC,CAAK,CAAE,CAAC,EAClH,MAEF,IAAMqC,EAAU,mBAAmB/C,CAAO,EACpCgD,EAAK/C,IAAiB,QACxB,GAAGS,qBAAwBqC,YAAkB7C,UAC7C,GAAGQ,qBAAwBqC,oBAA0B7C,UACzD,GAAI,CACF,IAAM+C,EAAQ,MAAM,MAAMD,EAAI,CAC5B,OAAQ,OACR,QAAS,CAAE,eAAgB,mBAAoB,gBAAiBF,CAAM,EACtE,KAAM,KAAK,UAAU,CAAE,KAAMvC,CAAK,CAAC,CACrC,CAAC,EACK2C,EAAW,MAAMD,EAAM,KAAK,EAAE,MAAM,IAAM,EAAE,EAC9C9C,EAAS,GACb,GAAI,CAAEA,EAAS,OAAQ,KAAK,MAAM+C,CAAQ,EAAU,IAAM,EAAE,CAAE,MAAE,CAAqB,CACrF,KAAK,IAAI,6BAA6B9C,cAAoB4C,OAAQC,EAAM,iBAAiB9C,GAAU,KAAK,EACxG,KAAK,KAAKW,EAAK,IAAK,CAAE,GAAImC,EAAM,GAAI,OAAQA,EAAM,OAAQ,OAAA9C,CAAO,CAAC,CACpE,OAAShE,EAAP,CACA,KAAK,IAAI,mCAAmCA,EAAE,SAAS,EACvD,KAAK,KAAK2E,EAAK,IAAK,CAAE,MAAO3E,EAAE,OAAQ,CAAC,CAC1C,CACA,KACF,CAEA,IAAK,wBAAyB,CAE5B,IAAMuG,EAAO,MAAMC,EAAS9B,CAAG,EACzB,CAAE,QAAAb,EAAS,aAAAC,EAAc,YAAAC,EAAa,QAAAE,EAAS,WAAAK,CAAW,EAAIiC,EAG9DI,GAFW,KAAK,OAAO,SAAS,QAAQ,eAAiB,CAAC,GACvC,KAAMD,GAAWA,EAAE,UAAYzC,CAAO,GACxC,OAAS,KAAK,OAAO,SAAS,QAAQ,MACvDM,EAAO,KAAK,OAAO,SAAS,QAAQ,KAC1C,GAAI,CAACoC,GAAS,CAACpC,EAAM,CACnB,KAAK,KAAKI,EAAK,IAAK,CAAE,MAAO,2BAA4B,CAAC,EAC1D,MAEF,IAAMqC,EAAe,KAAK,IAAI,GAAI,KAAK,MAAM,OAAO1C,CAAU,EAAI,GAAI,CAAC,EACjE2C,EAAQ,KAAK,MAAMD,EAAe,IAAI,EACtCE,EAAU,KAAK,KAAMF,EAAe,KAAQ,EAAE,EAC9CG,EAAWF,EAAQ,EAAI,GAAGA,KAASC,EAAU,EAAI,IAAIA,KAAa,KAAO,GAAGA,KAC5EN,EAAU,mBAAmB/C,CAAO,EAEpCgD,EAAK,GAAGtC,qBAAwBqC,KAD1B9C,IAAiB,gBAAkB,iBAAmB,YACVC,mBACxD,GAAI,CACF,IAAM+C,EAAQ,MAAM,MAAMD,EAAI,CAC5B,OAAQ,OACR,QAAS,CAAE,eAAgB,mBAAoB,gBAAiBF,CAAM,EACtE,KAAM,KAAK,UAAU,CAAE,SAAAQ,CAAS,CAAC,CACnC,CAAC,EACD,KAAK,IAAI,4BAA4BlD,eAAqBkD,aAAoBN,OAAQC,EAAM,QAAQ,EACpG,KAAK,KAAKnC,EAAK,IAAK,CAAE,GAAImC,EAAM,GAAI,OAAQA,EAAM,OAAQ,SAAAK,CAAS,CAAC,CACtE,OAASnH,EAAP,CACA,KAAK,IAAI,kCAAkCA,EAAE,SAAS,EACtD,KAAK,KAAK2E,EAAK,IAAK,CAAE,MAAO3E,EAAE,OAAQ,CAAC,CAC1C,CACA,KACF,CAEA,IAAK,eAAgB,CACnB,GAAI,CACF,IAAMoH,EAAS,MAAM,KAAK,OAAO,EACjC,KAAK,KAAKzC,EAAK,IAAK,CAAE,GAAI,GAAM,GAAGyC,CAAO,CAAC,CAC7C,OAASpH,EAAP,CACA,KAAK,KAAK2E,EAAK,IAAK,CAAE,GAAI,GAAO,MAAO3E,GAAG,SAAW,OAAOA,CAAC,CAAE,CAAC,CACnE,CACA,KACF,CAEA,IAAK,aAAc,CACjB,IAAMuG,EAAO,MAAMC,EAAS9B,CAAG,EAC/B,GAAI,CAAC6B,EAAK,SAAW,CAACA,EAAK,QAAU,CAACA,EAAK,KAAM,CAC/C,KAAK,KAAK5B,EAAK,IAAK,CAAE,MAAO,iCAAkC,CAAC,EAChE,OAEF,GAAI,CACF,IAAM0C,EAAY,MAAM,KAAK,OAAO,aAAa,CAC/C,QAASd,EAAK,QACd,OAAQA,EAAK,OACb,KAAMA,EAAK,KACX,QAASA,EAAK,QACd,UAAWA,EAAK,UAChB,QAASA,EAAK,QACd,UAAWA,EAAK,SAClB,CAAC,EACD,KAAK,KAAK5B,EAAK,IAAK,CAAE,GAAI,GAAM,UAAW0C,GAAa,IAAK,CAAC,CAChE,OAASrH,EAAP,CACA,KAAK,KAAK2E,EAAK,IAAK,CAAE,MAAO3E,EAAE,OAAQ,CAAC,CAC1C,CACA,KACF,CAEA,IAAK,gBACH,KAAK,KAAK2E,EAAK,IAAK,KAAK,OAAO,gBAAgB,CAAC,EACjD,MAEF,IAAK,gBACH,KAAK,KAAKA,EAAK,IAAM,KAAK,OAAe,gBAAgB,KAAK,GAAK,CAAC,CAAC,EACrE,MAEF,IAAK,aAAc,CACjB,IAAM4B,EAAO,MAAMC,EAAS9B,CAAG,EACzBT,EAAWsC,EAAK,OAAoB,KAAK,OAAO,KAAK,aAC3D,GAAI,CAACtC,GAAW,CAACsC,EAAK,QAAS,CAC7B,KAAK,KAAK5B,EAAK,IAAK,CAAE,MAAO,mDAAoD,CAAC,EAClF,OAKF,IAAM2C,EAAW,MAAM,KAAK,SAAS,QACnC,CACE,QAAArD,EACA,QAASsC,EAAK,QACd,QAASA,EAAK,OAChB,EACA,IAAM,CAAC,CACT,EACA,KAAK,KAAK5B,EAAK2C,EAAS,MAAQ,IAAM,IAAKA,CAAQ,EACnD,KACF,CAEA,IAAK,YACL,IAAK,WAAY,CAGf,IAAMpD,EAAM,IAAI,IAAIQ,EAAI,KAAO,IAAK,UAAUA,EAAI,QAAQ,MAAQ,aAAa,EAC3E6C,EAAUrD,EAAI,aAAa,IAAI,GAAG,GAAKA,EAAI,aAAa,IAAI,SAAS,GAAK,GAC9E,GAAIQ,EAAI,SAAW,OAAQ,CACzB,IAAM6B,EAAO,MAAMC,EAAS9B,CAAG,EAC/B6C,EAAWhB,EAAK,SAAuBA,EAAK,GAAgBgB,EAG9D,IAAMtD,EAAU,KAAK,OAAO,KAAK,aACjC,GAAI,CAACA,EAAS,CACZ,KAAK,KAAKU,EAAK,IAAK,CAAE,MAAO,2CAA4C,CAAC,EAC1E,OAEF,GAAI,CAAC4C,EAAS,CACZ,KAAK,KAAK5C,EAAK,IAAK,CAAE,MAAO,mDAAoD,CAAC,EAClF,OAIF,IAAM6C,EAAc;AAAA;AAAA,EAA6MD,IAE3ND,EAAW,MAAM,KAAK,SAAS,QAAQ,CAC3C,QAAArD,EACA,QAASuD,EACT,QAAS,CAAE,QAAS,QAAS,OAAQ,MAAO,CAC9C,CAAC,EAGKC,EAAYC,GAAYJ,EAAS,OAAO,EAE9C,KAAK,KAAK3C,EAAK2C,EAAS,MAAQ,IAAM,IAAK,CACzC,KAAMG,EACN,KAAMH,EAAS,QACf,MAAOA,EAAS,MAChB,SAAUA,EAAS,QACrB,CAAC,EACD,KACF,CAEA,IAAK,kBAAmB,CACtB,IAAMf,EAAO,MAAMC,EAAS9B,CAAG,EAC/B,GAAI,CAAC6B,EAAK,MAAQ,CAACA,EAAK,QAAS,CAC/B,KAAK,KAAK5B,EAAK,IAAK,CAAE,MAAO,wBAAyB,CAAC,EACvD,OAEF,GAAI,CAAC,KAAK,KAAM,CACd,KAAK,KAAKA,EAAK,IAAK,CAAE,MAAO,kBAAmB,CAAC,EACjD,OAEF,IAAMyC,EAAS,MAAM,KAAK,KAAK,SAASb,EAAK,KAAgBA,EAAK,QAAmBA,EAAK,KAA2B,EACrH,KAAK,KAAK5B,EAAK,IAAK,CAAE,SAAUyC,CAAO,CAAC,EACxC,KACF,CAGA,IAAK,mCACH,KAAK,KAAKzC,EAAK,IAAK,CAClB,KAAM,KAAK,OAAO,KAAK,KACvB,YAAa,uBAAuB,KAAK,OAAO,KAAK,QACrD,IAAK,UAAU,KAAK,OAAO,KAAK,OAChC,QAAS,QACT,aAAc,CACZ,UAAW,GACX,kBAAmB,GACnB,uBAAwB,EAC1B,EACA,OAAQ,KAAK,SAAS,KAAK,EAAE,IAAKpG,IAAO,CACvC,GAAIA,EAAE,GACN,KAAMA,EAAE,KACR,YAAa,UAAUA,EAAE,UAAUA,EAAE,QACrC,KAAM,CAACA,EAAE,IAAI,CACf,EAAE,EACF,kBAAmB,CAAC,MAAM,EAC1B,mBAAoB,CAAC,MAAM,CAC7B,CAAC,EACD,MAIF,IAAK,mBAAoB,CACvB,IAAMoJ,EAAM,KAAK,SAAS,WAAW,EACrC,KAAK,KAAKhD,EAAK,IAAK,CAClB,OAAQ,KAAK,OAAO,KAAK,GACzB,OAAQgD,EAAI,QAAQ,CACtB,CAAC,EACD,KACF,CAEA,IAAK,oBAAqB,CACxB,IAAMA,EAAM,KAAK,SAAS,WAAW,EAC/B1D,EAAUC,EAAI,aAAa,IAAI,OAAO,GAAK,OAC3C0D,EAAQ1D,EAAI,aAAa,IAAI,OAAO,GAAK,OACzC2D,EAAU5D,EACZ0D,EAAI,gBAAgB1D,CAAO,EAC3B0D,EAAI,eAAe,EAAE,YAAY,CAAE,MAAAC,CAAM,CAAC,EAC9C,KAAK,KAAKjD,EAAK,IAAK,CAClB,OAAQ,KAAK,OAAO,KAAK,GACzB,MAAOkD,EAAQ,OACf,QAASA,EAAQ,IAAI7H,IAAM,CACzB,GAAIA,EAAE,GACN,KAAMA,EAAE,KACR,QAASA,EAAE,QACX,OAAQA,EAAE,OACV,cAAeA,EAAE,cACjB,QAASA,EAAE,OACb,EAAE,CACJ,CAAC,EACD,KACF,CAEA,IAAK,qBAAsB,CACzB,IAAM2H,EAAM,KAAK,SAAS,WAAW,EAC/B1D,EAAUC,EAAI,aAAa,IAAI,OAAO,EAC5C,GAAI,CAACD,EAAS,CACZ,KAAK,KAAKU,EAAK,IAAK,CAAE,MAAO,kBAAmB,CAAC,EACjD,MAGF,IAAMmD,EADQH,EAAI,aAAa1D,CAAO,EAClB,aAAa,EACjC,KAAK,KAAKU,EAAK,IAAK,CAClB,OAAQ,KAAK,OAAO,KAAK,GACzB,QAAAV,EACA,SAAU6D,EAAM,QAClB,CAAC,EACD,KACF,CAEA,IAAK,oBAAqB,CACxB,IAAMH,EAAM,KAAK,SAAS,WAAW,EAC/B1D,EAAUC,EAAI,aAAa,IAAI,OAAO,EACtC6D,EAAc7D,EAAI,aAAa,IAAI,MAAM,EAC/C,GAAI,CAACD,GAAW,CAAC8D,EAAa,CAC5B,KAAK,KAAKpD,EAAK,IAAK,CAAE,MAAO,6BAA8B,CAAC,EAC5D,MAGF,IAAMqD,EADQL,EAAI,aAAa1D,CAAO,EAChB,YAAY8D,CAAW,EAC7C,GAAI,CAACC,EAAS,CACZ,KAAK,KAAKrD,EAAK,IAAK,CAAE,MAAO,mBAAoB,CAAC,EAClD,MAEF,KAAK,KAAKA,EAAK,IAAK,CAClB,OAAQ,KAAK,OAAO,KAAK,GACzB,QAAAV,EACA,KAAM+D,EAAQ,KACd,MAAOA,EAAQ,KAAK,MACpB,KAAMA,EAAQ,KAAK,KACnB,MAAOA,EAAQ,KAAK,MACpB,QAASA,EAAQ,KAAK,QACtB,YAAaA,EAAQ,KAAK,YAC1B,QAASA,EAAQ,KAAK,QACtB,QAASA,EAAQ,OACnB,CAAC,EACD,KACF,CAEA,QACE,KAAK,KAAKrD,EAAK,IAAK,CAClB,MAAO,YACP,UAAW,CACT,eACA,eACA,cACA,aACA,oBACA,gDACA,8BACA,0CACA,oCACA,4DACA,6DACA,mCACF,CACF,CAAC,CACL,CACF,OAAS3E,EAAP,CACA,KAAK,KAAK2E,EAAK,IAAK,CAAE,MAAO3E,EAAE,OAAQ,CAAC,CAC1C,CACF,CAaA,MAAc,mBAAmB0E,EAAsBC,EAAqBlD,EAA6B,CACvG,IAAM8E,EAAO,MAAMC,EAAS9B,CAAG,EAGzBuD,EAAYxG,EAAK,MAAM,mBAAmB,EAC1CyG,EAAgB,OAAO,KAAK,KAAK,OAAO,MAAM,EAAE,CAAC,GAAK,UACtDjE,EAAUgE,IAAY,CAAC,GAAM1B,EAAK,OAAoB2B,EAGtDC,EAAY5B,EAAK,UAAyD,CAAC,EAC3E6B,EAAc,CAAC,GAAGD,CAAQ,EAAE,QAAQ,EAAE,KAAK,GAAK,EAAE,OAAS,MAAM,EAEvE,GAAI,CAACC,GAAa,QAAS,CACzB,KAAK,KAAKzD,EAAK,IAAK,CAAE,MAAO,CAAE,QAAS,wBAAyB,KAAM,uBAAwB,CAAE,CAAC,EAClG,OAIF,IAAM0D,EAAeF,EAClB,MAAM,EAAG,EAAE,EACX,IAAI,GAAK,GAAG,EAAE,OAAS,OAAS,OAAS,gBAAgB,EAAE,QAAQ,MAAM,EAAG,GAAG,GAAG,EAE/EG,EAAgBD,EAAa,OAAS,EACxC;AAAA,EAAmBA,EAAa,MAAM,GAAG,EAAE,KAAK;AAAA,CAAI;AAAA;AAAA,EACpD,GAIJ,GAFe9B,EAAK,SAAW,GAEnB,CAEV5B,EAAI,UAAU,IAAK,CACjB,eAAgB,oBAChB,gBAAiB,WACjB,WAAc,YAChB,CAAC,EAED,IAAM4D,EAAY,YAAY,KAAK,IAAI,EAAE,SAAS,EAAE,IAE9CjB,EAAW,MAAM,KAAK,SAAS,QAAQ,CAC3C,QAAArD,EACA,QAASqE,EAAgBF,EAAY,QACrC,QAAS,CAAE,QAAS,MAAO,OAAQ,eAAgB,CACrD,CAAC,EAEKI,EAAUlB,EAAS,OAASA,EAAS,SAAW,GAGhD/B,EAAQ,CACZ,GAAIgD,EACJ,OAAQ,wBACR,QAAS,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACrC,MAAOtE,EACP,QAAS,CAAC,CACR,MAAO,EACP,MAAO,CAAE,KAAM,YAAa,QAAAuE,CAAQ,EACpC,cAAe,MACjB,CAAC,CACH,EACA7D,EAAI,MAAM,SAAS,KAAK,UAAUY,CAAK;AAAA;AAAA,CAAO,EAC9CZ,EAAI,MAAM;AAAA;AAAA,CAAkB,EAC5BA,EAAI,IAAI,MACH,CAEL,IAAM2C,EAAW,MAAM,KAAK,SAAS,QAAQ,CAC3C,QAAArD,EACA,QAASqE,EAAgBF,EAAY,QACrC,QAAS,CAAE,QAAS,MAAO,OAAQ,eAAgB,CACrD,CAAC,EAEKI,EAAUlB,EAAS,OAASA,EAAS,SAAW,GAChDmB,EAAS,KAAK,KAAKD,EAAQ,OAAS,CAAC,EAE3C,KAAK,KAAK7D,EAAK,IAAK,CAClB,GAAI,YAAY,KAAK,IAAI,EAAE,SAAS,EAAE,IACtC,OAAQ,kBACR,QAAS,KAAK,MAAM,KAAK,IAAI,EAAI,GAAI,EACrC,MAAOV,EACP,QAAS,CAAC,CACR,MAAO,EACP,QAAS,CAAE,KAAM,YAAa,QAAAuE,CAAQ,EACtC,cAAe,MACjB,CAAC,EACD,MAAO,CACL,cAAe,KAAK,KAAKJ,EAAY,QAAQ,OAAS,CAAC,EACvD,kBAAmBK,EACnB,aAAc,KAAK,KAAKL,EAAY,QAAQ,OAAS,CAAC,EAAIK,CAC5D,CACF,CAAC,EAEL,CAEQ,KAAK9D,EAAqBpE,EAAgB8D,EAAqB,CACrEM,EAAI,UAAUpE,EAAQ,CAAE,eAAgB,kBAAmB,CAAC,EAC5DoE,EAAI,IAAI,KAAK,UAAUN,EAAM,KAAM,CAAC,CAAC,CACvC,CACF,EAMA,SAASqD,GAAYtD,EAAsB,CACzC,IAAI4B,EAAI5B,EAGR,OAAA4B,EAAIA,EAAE,QAAQ,kBAAmB,IAAI,EACrCA,EAAIA,EAAE,QAAQ,WAAY,EAAE,EAG5BA,EAAIA,EAAE,QAAQ,iBAAkB,IAAI,EACpCA,EAAIA,EAAE,QAAQ,aAAc,IAAI,EAChCA,EAAIA,EAAE,QAAQ,eAAgB,IAAI,EAClCA,EAAIA,EAAE,QAAQ,aAAc,IAAI,EAChCA,EAAIA,EAAE,QAAQ,aAAc,IAAI,EAGhCA,EAAIA,EAAE,QAAQ,yBAA0B,IAAI,EAG5CA,EAAIA,EAAE,QAAQ,0BAA2B,UAAU,EAGnDA,EAAIA,EAAE,QAAQ,eAAgB,EAAE,EAGhCA,EAAIA,EAAE,QAAQ,mBAAoB,IAAI,EACtCA,EAAIA,EAAE,QAAQ,mBAAoB,IAAI,EAGtCA,EAAIA,EAAE,QAAQ,gBAAiB,EAAE,EAGjCA,EAAIA,EAAE,QAAQ,UAAW,EAAE,EAG3BA,EAAIA,EAAE,QAAQ,kBAAmB,MAAM,EAGvCA,EAAIA,EAAE,QAAQ,iBAAmB0C,GAAUA,EAAM,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,KAAK,SAAS,CAAC,EAG3F1C,EAAIA,EAAE,QAAQ,KAAM,OAAO,EAC3BA,EAAIA,EAAE,QAAQ,KAAM,MAAM,EAC1BA,EAAIA,EAAE,QAAQ,MAAO,WAAW,EAChCA,EAAIA,EAAE,QAAQ,MAAO,MAAM,EAC3BA,EAAIA,EAAE,QAAQ,MAAO,MAAM,EAG3BA,EAAIA,EAAE,QAAQ,aAAc,aAAa,EACzCA,EAAIA,EAAE,QAAQ,aAAc,SAAS,EACrCA,EAAIA,EAAE,QAAQ,YAAa,UAAU,EACrCA,EAAIA,EAAE,QAAQ,cAAe,QAAQ,EAGrCA,EAAIA,EAAE,QAAQ,UAAW,IAAI,EAC7BA,EAAIA,EAAE,QAAQ,MAAO,GAAG,EACxBA,EAAIA,EAAE,QAAQ,OAAQ,GAAG,EACzBA,EAAIA,EAAE,QAAQ,WAAY,GAAG,EAC7BA,EAAIA,EAAE,QAAQ,eAAgB,IAAI,EAGlCA,EAAIA,EAAE,KAAK,EAAE,QAAQ,cAAe,EAAE,EAGlCA,EAAE,OAAS,MACbA,EAAIA,EAAE,MAAM,EAAG,GAAG,EAAE,QAAQ,UAAW,EAAE,EAAI,OAGxCA,CACT,CAEA,eAAeQ,EAAS9B,EAAwD,CAC9E,OAAO,IAAI,QAAQ,CAAC9D,EAAS+H,IAAW,CACtC,IAAIpC,EAAO,GACX7B,EAAI,GAAG,OAASa,GAAmBgB,GAAQhB,EAAM,SAAS,CAAE,EAC5Db,EAAI,GAAG,MAAO,IAAM,CAClB,GAAI,CACF9D,EAAQ2F,EAAO,KAAK,MAAMA,CAAI,EAAI,CAAC,CAAC,CACtC,MAAE,CACA3F,EAAQ,CAAC,CAAC,CACZ,CACF,CAAC,EACD8D,EAAI,GAAG,QAASiE,CAAM,CACxB,CAAC,CACH,CgBh5CA,OAAOC,OAAU,OACjB,OAAOC,OAAQ,WAcR,SAASC,IAA8B,CAC5C,IAAMC,EAAuB,CAAC,EAI9B,GAAI,CACFA,EAAW,KAAKH,GAAK,QAAQ,UAAW,KAAM,cAAc,CAAC,EAC7DG,EAAW,KAAKH,GAAK,QAAQ,UAAW,cAAc,CAAC,CACzD,MAAE,CAA0D,CAE5DG,EAAW,KAAKH,GAAK,QAAQ,QAAQ,IAAI,EAAG,cAAc,CAAC,EAE3D,QAAWI,KAAKD,EACd,GAAI,CACF,GAAIF,GAAG,WAAWG,CAAC,EAAG,CACpB,IAAMC,EAAMJ,GAAG,aAAaG,CAAC,EAC7B,GAAIC,GAAOA,EAAI,KAAM,OAAOA,EAEhC,MAAE,CAAiC,CAIrC,MAAO,CAAE,KAAM,cAAe,QAAS,OAAQ,CACjD","names":["A2AClient","baseUrl","token","res","text","metadata","response","body","reader","decoder","buffer","done","value","lines","line","data","taskId","method","params","h","A2AMesh","config","log","peer","A2AClient","interval","results","name","state","healthy","p","timeout","controller","timer","card","e","peerName","text","agentId","agent","url","headers","res","data","skillId","a","readFileSync","writeFileSync","existsSync","mkdirSync","resolve","ellipsize","text","maxChars","ellipsis","budget","slice","nl","sp","firstLines","maxLines","picked","MAX_ENTRIES","MAX_CONTEXT_CHARS","MAX_ENTRY_CHARS","REPEAT_WINDOW_MS","bucketLabel","ts","d","hh","mm","GroupLog","baseDir","resolve","mkdirSync","chatId","sender","text","log","now","ellipsized","ellipsize","last","lines","chars","pending","buffered","lastBucket","flushPending","out","hdr","i","entry","line","bucket","safe","path","existsSync","data","readFileSync","entries","writeFileSync","DEFAULT_CONFIG","CHANNEL_DEFAULTS","BlockStream","emitter","config","channel","channelDefaults","delta","breakPoint","cutAt","block","delay","buf","max","paraIdx","nlIdx","spaceIdx","lines","line","readFileSync","writeFileSync","existsSync","mkdirSync","appendFileSync","resolve","dirname","InflightLog","baseDir","resolve","mkdirSync","dirname","entry","line","appendFileSync","id","existsSync","raw","readFileSync","byId","unfinished","out","e","writeFileSync","TYPING_INTERVAL_MS","_MessageRouter","registry","config","hooks","log","GroupLog","HandoverStore","a","InflightLog","p","resolve","existsSync","raw","readFileSync","now","restored","key","ts","live","dir","dirname","mkdirSync","writeFileSync","mesh","matcher","business","adapter","msg","accountId","name","unfinished","entry","e","k","t","seen","opts","hookResult","chatId","matched","replyAccountId","typingTimer","text","agentId","boundAccount","agentDef","agentName","canStream","sentMessageId","fullStreamText","blockStream","BlockStream","block","preview","onDelta","_delta","_fullText","agent","def","m","groupContext","messageWithContext","response","errorText","responseText","sentResponseId","notifyConfig","thresholdMs","durSec","status","trigger","firstLines","ellipsize","notifyText","gitlabAdapter","originalMsg","sourceAgentId","responseMessageId","depth","visited","id","targetAccountId","sourceAccountId","contextMessage","replyText","sentId","messageId","parseMode","emoji","sendTyping","textLower","directory","peer","skill","header","s","peerName","start","duration","account","override","atMentionsOnly","mentionAgent","MessageRouter","__publicField","escapeHtml","text","markdownToTelegramHtml","md","lines","result","inCodeBlock","codeBlockLang","codeBlockLines","inBlockquote","tableHeaders","inTable","i","line","code","langHint","content","convertInline","headerMatch","cells","c","parts","header","ulMatch","indent","olMatch","num","html","wrapFileRefsInCode","_m","label","url","FILE_EXT_TLDS","insideTag","part","readFileSync","writeFileSync","mkdirSync","existsSync","resolve","dirname","TelegramGroupStore","dataDir","groupId","groupName","accountId","agentId","username","status","group","info","TelegramOffsetStore","offset","dir","TelegramAdapter","accounts","opts","log","cfg","fromId","chatId","fromUsername","list","fromStr","chatStr","userLc","entry","handler","entries","i","config","me","botUserId","botUsername","e","r","aborts","token","msg","maxLen","text","formatted","markdownToTelegramHtml","params","result","messageId","parseMode","trimmed","emoji","consecutiveErrors","updates","update","mediaInfo","hasPhoto","hasVoice","hasAudio","hasVideo","hasDocument","fileId","mime","filePath","fileUrl","res","buffer","ext","randomUUID","join","mediaDir","fileName","localPath","isGroup","channelMeta","incoming","mcm","chatTitle","backoff","bots","b","groupTitle","method","url","mkdirSync","writeFileSync","resolve","join","randomUUID","WhatsAppAdapter","config","log","senderPhone","groupName","groupJid","route","normalized","pattern","handler","makeWASocket","useMultiFileAuthState","DisconnectReason","baileys","myGeneration","state","saveCreds","version","v","silentLogger","args","data","update","connection","lastDisconnect","qr","qrcode","statusCode","reason","delay","m","msg","jidShort","hasText","me","remoteJid","myJid","myLid","chatUser","text","msgContent","hasImage","hasAudio","hasVideo","hasDocument","hasSticker","hasMedia","jid","isGroup","chatPhone","p","senderName","resolvedAgent","mediaInfo","buffer","mime","ext","mediaDir","fileName","filePath","e","incoming","sentId","chatId","messageId","emoji","init_config","createServer","writeFileSync","existsSync","unlinkSync","mkdirSync","watch","resolve","dirname","DiscordAdapter","config","log","handler","Discord","Client","GatewayIntentBits","msg","isMentioned","isDM","text","incoming","e","channel","chatId","messageId","emoji","SlackAdapter","config","log","handler","SocketModeMod","WebApiMod","SocketModeClient","WebClient","authResult","e","handle","payload","ack","event","isDM","isMentioned","text","senderName","info","incoming","msg","chatId","messageId","emoji","name","createServer","GitLabAdapter","config","log","hooks","fn","handler","tokensToResolve","mapping","resolutions","label","token","res","data","result","username","u","a","createServer","req","err","msg","parts","iid","noteableType","project","encodedProject","endpoint","m","agentToken","body","noteId","e","debug","text","event","objectKind","note","user","signatureMatch","sourceAgent","mentions","targetAgent","noteableIid","noteableTitle","resolvedAgentId","targetAgentId","agentMapping","agentNode","chatId","channelMeta","noteClean","imageAttachment","incoming","attrs","defaultAgentId","dispatch","hookBlocked","modDispatch","d","agentId","mention","route","agents","mentionedUsers","node","durationMs","totalSeconds","hours","minutes","duration","typeSegment","imagePattern","matches","imagePath","imageUrl","buffer","contentType","ext","mkdirSync","writeFileSync","resolve","randomUUID","mediaDir","fileName","filePath","chunk","_Logger","module","minLevel","msg","data","args","clean","a","level","entry","prefix","tag","display","Logger","__publicField","WebhookHandler","registry","config","log","req","res","path","parts","agentId","sourceHint","body","source","summary","response","e","headers","lines","event","project","user","c","mr","attrs","repo","sender","pr","issue","type","data","resource","k","raw","resolve","chunk","status","LandscapeBuilder","config","meshPeers","agentId","a","b","p","s","selfId","lines","selfDef","selfHandle","selfCap","handleStr","localAgents","id","def","handle","cap","h","remotePeers","sortedPeers","peer","sortedSkills","skill","handles","portStr","port","parts","ch","pol","m","first","HeartbeatManager","registry","log","memoryStore","agentId","config","state","intervalMs","e","id","channel","prompt","recallContext","response","error","s","ServiceMatcher","serviceDefs","log","id","def","compiled","trigger","e","text","senderId","channel","svc","senderNorm","contactNorm","pattern","match","debug","service","registry","context","replyFn","response","s","Organization","config","agentId","entry","role","emp","seen","cur","e","chain","DAY_MAP","tzParts","when","timezone","parts","weekday","p","hourStr","minStr","parseHHMM","s","h","m","worksOnDay","schedule","dow","d","isScheduleActive","hour","minute","nowMin","startMin","endMin","lStart","lEnd","Schedule","org","config","agentId","emp","e","Reporter","router","org","config","log","text","agentId","dest","e","fromAgent","subject","detail","chain","targets","body","t","writeFileSync","existsSync","mkdirSync","readFileSync","resolve","today","KPI","org","schedule","log","f","perAgent","emp","now","prev","delta","row","t","agentId","lines","totalLogged","totalOnClock","hrs","util","overallUtil","json","res","status","body","readJson","req","resolve","reject","chunks","c","raw","e","handleBusinessHttp","route","deps","now","onClock","agentId","items","agent","itemId","businessToolsDoc","daemonBase","TICK_MS","DayCycle","config","org","schedule","registry","reporter","kpi","workSource","daemonBase","log","emp","now","iso","key","state","e","agentId","manager","responsibilities","sop","businessToolsDoc","prompt","start","resp","seconds","items","BusinessLayer","business","daemon","registry","router","log","Organization","Schedule","createWorkSource","Reporter","KPI","host","port","daemonBase","DayCycle","agentId","durationSeconds","success","channel","route","req","res","deps","handleBusinessHttp","onClock","AgentXDaemon","configPath","baseLog","Logger","args","line","a","loadDaemonConfig","warnings","validateWorkspaces","w","portStr","setupAllWorkspaces","HookRegistry","loadHooks","LandscapeBuilder","AgentRegistry","setGlobalRegistry","HeartbeatManager","id","agent","MessageRouter","WebhookHandler","serviceMatcher","ServiceMatcher","CronScheduler","jobId","error","consecutiveErrors","msg","cronDef","e","A2AMesh","BusinessLayer","meshPeers","p","cronJobs","job","status","peer","removed","loaded","pidFile","resolve","mkdirSync","dirname","writeFileSync","err","reason","stopping","shutdown","signal","start","r","existsSync","unlinkSync","path","watch","eventType","next","applied","restartRequired","restartKeys","k","scheduleNext","now","tunisStr","tunisNow","target","tunisOffset","delay","yesterday","tracker","report","accounts","policy","telegram","TelegramAdapter","globalSize","closedAccts","c","setWhatsAppQR","setWhatsAppStatus","whatsapp","WhatsAppAdapter","discord","DiscordAdapter","slack","SlackAdapter","gitlab","GitLabAdapter","node","project","noteableType","noteableIid","noteId","agentId","url","respText","text","data","durationMs","host","port","createServer","req","res","event","payload","client","agents","active","_agentId","taskId","closed","send","ev","sub","chunk","heartbeat","taskStreamMatch","taskHistoryListMatch","limit","taskRecordMatch","rec","j","getWhatsAppState","s","mod","svg","getDebugState","getDebugLogs","setDebug","cats","body","readBody","mapping","m","token","encoded","ep","glRes","respBody","totalSeconds","hours","minutes","duration","result","messageId","response","message","voicePrompt","speakable","toSpeakable","hub","after","entries","index","articlePath","article","pathMatch","fallbackAgent","messages","lastUserMsg","historyLines","contextPrefix","requestId","content","tokens","match","reject","path","fs","getPackageInfo","candidates","p","pkg"]}
|