novacode 0.5.3 → 0.5.5
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/README.md +0 -13
- package/dist/app-QfQR2FN9.mjs +21 -0
- package/dist/app-QfQR2FN9.mjs.map +1 -0
- package/dist/main.mjs +21 -21
- package/dist/main.mjs.map +1 -1
- package/package.json +1 -1
- package/src/provider/stream.ts +1 -3
- package/src/session/store.ts +27 -4
- package/src/tui/app.tsx +23 -216
- package/src/tui/components/liveArea.tsx +73 -0
- package/src/tui/components/message.tsx +113 -0
- package/src/tui/components/statusBar.tsx +58 -0
- package/dist/app-BZ42XPxw.mjs +0 -21
- package/dist/app-BZ42XPxw.mjs.map +0 -1
package/dist/main.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.mjs","names":["#abort","#resolve","#events","#done","#result","#doneResolve","#api","#model","#apiKey","#baseUrl","#system","#tools","#messages","#db","#nextSeq"],"sources":["../src/provider/gemini.ts","../src/provider/openai.ts","../src/provider/stream.ts","../src/util.ts","../src/agent/loop.ts","../src/agent/agent.ts","../src/agent/prompt.ts","../src/session/store.ts","../src/commands/session.ts","../src/config/providers.ts","../src/config/store.ts","../src/tui/prompts.tsx","../src/onboarding/wizard.ts","../src/tools/fs.ts","../src/tools/git.ts","../src/tools/search.ts","../src/tools/shell.ts","../src/tools/web.ts","../src/tools/index.ts","../src/update.ts","../src/main.ts"],"sourcesContent":["import type {\n\tAssistantResult,\n\tContentPart,\n\tMsg,\n\tStopReason,\n\tStreamEvent,\n\tStreamFn,\n\tStreamOpts,\n\tToolDef,\n\tUsage,\n} from \"../types.ts\"\nimport { EventStream } from \"./stream.ts\"\n\ninterface GeminiPart {\n\ttext?: string\n\tthought?: boolean | string\n\tinline_data?: { mime_type: string; data: string }\n\tfunction_call?: { name: string; args: Record<string, unknown> }\n\tfunction_response?: { name: string; response: Record<string, unknown> }\n\tthought_signature?: string\n}\n\ninterface GeminiContent {\n\trole: \"user\" | \"model\"\n\tparts: GeminiPart[]\n}\n\n/**\n * Maps our internal Msg format to the Gemini 'Content' format.\n * Groups consecutive tool_result messages into a single Gemini message.\n */\nfunction msgsToGemini(messages: Msg[]): GeminiContent[] {\n\tconst contents: GeminiContent[] = []\n\n\tfor (const msg of messages) {\n\t\tif (msg.role === \"user\") {\n\t\t\tconst parts: GeminiPart[] =\n\t\t\t\ttypeof msg.content === \"string\"\n\t\t\t\t\t? [{ text: msg.content }]\n\t\t\t\t\t: msg.content.map((c) => {\n\t\t\t\t\t\t\tif (c.type === \"text\") return { text: c.text }\n\t\t\t\t\t\t\tif (c.type === \"image\") return { inline_data: { mime_type: c.mime, data: c.data } }\n\t\t\t\t\t\t\treturn { text: \"\" }\n\t\t\t\t\t\t})\n\t\t\tcontents.push({ role: \"user\", parts })\n\t\t} else if (msg.role === \"assistant\") {\n\t\t\tconst parts: GeminiPart[] = msg.content.map((c) => {\n\t\t\t\tif (c.type === \"text\") return { text: c.text, thought_signature: c.signature }\n\t\t\t\tif (c.type === \"thinking\")\n\t\t\t\t\treturn { thought: true, text: c.text, thought_signature: c.signature }\n\t\t\t\tif (c.type === \"tool_call\")\n\t\t\t\t\treturn { function_call: { name: c.name, args: c.args }, thought_signature: c.signature }\n\t\t\t\treturn { text: \"\" }\n\t\t\t})\n\t\t\tcontents.push({ role: \"model\", parts })\n\t\t} else if (msg.role === \"tool_result\") {\n\t\t\tconst part: GeminiPart = {\n\t\t\t\tfunction_response: {\n\t\t\t\t\tname: msg.tool,\n\t\t\t\t\tresponse: {\n\t\t\t\t\t\tcontent: msg.content\n\t\t\t\t\t\t\t.map((c) => (c.type === \"text\" ? c.text : JSON.stringify(c)))\n\t\t\t\t\t\t\t.join(\"\\n\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tconst last = contents[contents.length - 1]\n\t\t\t// Gemini requires alternating roles; multiple function_responses group into one 'user' message.\n\t\t\tif (last && last.role === \"user\" && last.parts.some((p) => p.function_response)) {\n\t\t\t\tlast.parts.push(part)\n\t\t\t} else {\n\t\t\t\tcontents.push({ role: \"user\", parts: [part] })\n\t\t\t}\n\t\t}\n\t}\n\n\treturn contents\n}\n\nfunction toolsToGemini(tools: ToolDef[]): unknown[] {\n\tif (tools.length === 0) return []\n\treturn [\n\t\t{\n\t\t\tfunction_declarations: tools.map((t) => ({\n\t\t\t\tname: t.name,\n\t\t\t\tdescription: t.description,\n\t\t\t\tparameters: t.parameters,\n\t\t\t})),\n\t\t},\n\t]\n}\n\nexport const streamGemini: StreamFn = (\n\topts: StreamOpts,\n): EventStream<StreamEvent, AssistantResult> => {\n\tconst es = new EventStream<StreamEvent, AssistantResult>()\n\n\t;(async () => {\n\t\ttry {\n\t\t\tconst baseUrl = opts.baseUrl || \"https://generativelanguage.googleapis.com\"\n\t\t\tconst url = `${baseUrl}/v1beta/models/${opts.model.id}:streamGenerateContent?alt=sse&key=${opts.apiKey}`\n\n\t\t\tconst body = {\n\t\t\t\tcontents: msgsToGemini(opts.messages),\n\t\t\t\tsystem_instruction: opts.system ? { parts: [{ text: opts.system }] } : undefined,\n\t\t\t\ttools: opts.tools.length > 0 ? toolsToGemini(opts.tools) : undefined,\n\t\t\t\tgenerationConfig: {\n\t\t\t\t\tthinkingConfig: opts.model.supportsThinking ? { thinkingLevel: \"low\" } : undefined,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\"Api-Revision\": \"2026-05-20\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(body),\n\t\t\t\tsignal: opts.signal,\n\t\t\t})\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text()\n\t\t\t\tlet msg = text\n\t\t\t\ttry {\n\t\t\t\t\tconst json = JSON.parse(text)\n\t\t\t\t\tmsg = json.error?.message || json.message || text\n\t\t\t\t} catch {\n\t\t\t\t\t/* use raw text */\n\t\t\t\t}\n\n\t\t\t\tconst errorMsg = `Gemini Error (${response.status}): ${msg}`\n\t\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\t\tes.finish({\n\t\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\t\tstop: \"error\",\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst reader = response.body?.getReader()\n\t\t\tif (!reader) {\n\t\t\t\tes.finish({ content: [], usage: { in: 0, out: 0 }, stop: \"error\" })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst decoder = new TextDecoder()\n\t\t\tlet buffer = \"\"\n\t\t\tlet usage: Usage = { in: 0, out: 0 }\n\t\t\tlet stop: StopReason = \"stop\"\n\t\t\tconst content: ContentPart[] = []\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\tconst lines = buffer.split(\"\\n\")\n\t\t\t\tbuffer = lines.pop() ?? \"\"\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst trimmed = line.trim()\n\t\t\t\t\tif (!trimmed?.startsWith(\"data: \")) continue\n\t\t\t\t\tconst data = trimmed.slice(6)\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst chunk = JSON.parse(data)\n\t\t\t\t\t\tconst candidate = chunk.candidates?.[0]\n\n\t\t\t\t\t\t// Handle usage metadata\n\t\t\t\t\t\tif (chunk.usageMetadata) {\n\t\t\t\t\t\t\tusage = {\n\t\t\t\t\t\t\t\tin: chunk.usageMetadata.promptTokenCount || usage.in,\n\t\t\t\t\t\t\t\tout: chunk.usageMetadata.candidatesTokenCount || usage.out,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tes.push({ type: \"usage\", usage })\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!candidate) continue\n\n\t\t\t\t\t\t// Map finish reason\n\t\t\t\t\t\tif (candidate.finishReason) {\n\t\t\t\t\t\t\tconst reason = candidate.finishReason\n\t\t\t\t\t\t\tif (reason === \"STOP\") stop = \"stop\"\n\t\t\t\t\t\t\telse if (reason === \"MAX_TOKENS\") stop = \"length\"\n\t\t\t\t\t\t\telse if (reason === \"SAFETY\" || reason === \"RECITATION\" || reason === \"OTHER\")\n\t\t\t\t\t\t\t\tstop = \"error\"\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst parts = candidate.content?.parts\n\t\t\t\t\t\tif (parts) {\n\t\t\t\t\t\t\tfor (const part of parts) {\n\t\t\t\t\t\t\t\tconst sig = part.thought_signature || part.thoughtSignature\n\n\t\t\t\t\t\t\t\t// Handle text and thinking deltas\n\t\t\t\t\t\t\t\tif (part.text) {\n\t\t\t\t\t\t\t\t\tif (part.thought === true || typeof part.thought === \"string\") {\n\t\t\t\t\t\t\t\t\t\tconst thoughtText = typeof part.thought === \"string\" ? part.thought : part.text\n\t\t\t\t\t\t\t\t\t\tes.push({ type: \"thinking_delta\", text: thoughtText })\n\t\t\t\t\t\t\t\t\t\tconst last = content[content.length - 1]\n\t\t\t\t\t\t\t\t\t\tif (last?.type === \"thinking\") {\n\t\t\t\t\t\t\t\t\t\t\tlast.text += thoughtText\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tcontent.push({ type: \"thinking\", text: thoughtText, signature: sig })\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tes.push({ type: \"text_delta\", text: part.text })\n\t\t\t\t\t\t\t\t\t\tconst last = content[content.length - 1]\n\t\t\t\t\t\t\t\t\t\tif (last?.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\t\tlast.text += part.text\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tcontent.push({ type: \"text\", text: part.text, signature: sig })\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Handle function calls (can be snake_case or camelCase in some API versions)\n\t\t\t\t\t\t\t\tconst fc = part.functionCall || part.function_call\n\t\t\t\t\t\t\t\tif (fc) {\n\t\t\t\t\t\t\t\t\tconst name = fc.name\n\t\t\t\t\t\t\t\t\tconst args = (fc.args as Record<string, unknown>) || {}\n\t\t\t\t\t\t\t\t\tconst id = `call_${Math.random().toString(36).slice(2, 9)}`\n\n\t\t\t\t\t\t\t\t\tconst toolCall: ContentPart = {\n\t\t\t\t\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\t\t\targs,\n\t\t\t\t\t\t\t\t\t\tsignature: sig,\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tcontent.push(toolCall)\n\t\t\t\t\t\t\t\t\tes.push({ type: \"tool_call\", call: toolCall })\n\t\t\t\t\t\t\t\t\tstop = \"tool_use\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (_e) {\n\t\t\t\t\t\tif (data.trim() !== \"\" && data.trim() !== \"[DONE]\") {\n\t\t\t\t\t\t\t// skip noise\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tes.finish({ content, usage, stop })\n\t\t} catch (e) {\n\t\t\tif (opts.signal?.aborted) return\n\t\t\tconst errorMsg = `Gemini Network/Request Error: ${e instanceof Error ? e.message : String(e)}`\n\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\tes.finish({\n\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\tstop: \"error\",\n\t\t\t})\n\t\t}\n\t})()\n\n\treturn es\n}\n","import type {\n\tAssistantResult,\n\tMsg,\n\tStopReason,\n\tStreamEvent,\n\tStreamFn,\n\tStreamOpts,\n\tToolDef,\n\tUsage,\n} from \"../types.ts\"\nimport { EventStream } from \"./stream.ts\"\n\nfunction msgToOpenAI(msg: Msg): Record<string, unknown> {\n\tif (msg.role === \"user\") {\n\t\treturn {\n\t\t\trole: \"user\",\n\t\t\tcontent:\n\t\t\t\ttypeof msg.content === \"string\"\n\t\t\t\t\t? msg.content\n\t\t\t\t\t: msg.content.map((c) => {\n\t\t\t\t\t\t\tif (c.type === \"text\") return { type: \"text\", text: c.text }\n\t\t\t\t\t\t\tif (c.type === \"image\")\n\t\t\t\t\t\t\t\treturn { type: \"image_url\", image_url: { url: `data:${c.mime};base64,${c.data}` } }\n\t\t\t\t\t\t\treturn { type: \"text\", text: \"\" }\n\t\t\t\t\t\t}),\n\t\t}\n\t}\n\tif (msg.role === \"assistant\") {\n\t\tconst textParts: string[] = []\n\t\tconst toolCalls: unknown[] = []\n\n\t\tfor (const c of msg.content) {\n\t\t\tif (c.type === \"text\") textParts.push(c.text)\n\t\t\t// thinking parts are internal — never sent back to the API\n\t\t\tif (c.type === \"tool_call\")\n\t\t\t\ttoolCalls.push({\n\t\t\t\t\ttype: \"function\",\n\t\t\t\t\tid: c.id,\n\t\t\t\t\tfunction: { name: c.name, arguments: JSON.stringify(c.args) },\n\t\t\t\t})\n\t\t}\n\n\t\tconst result: Record<string, unknown> = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: textParts.length > 0 ? textParts.join(\"\") : null,\n\t\t}\n\t\tif (toolCalls.length > 0) result.tool_calls = toolCalls\n\t\treturn result\n\t}\n\t// tool_result\n\tif (msg.role === \"tool_result\") {\n\t\treturn {\n\t\t\trole: \"tool\",\n\t\t\ttool_call_id: msg.callId,\n\t\t\tcontent: msg.content.map((c) => (c.type === \"text\" ? c.text : JSON.stringify(c))).join(\"\\n\"),\n\t\t}\n\t}\n\treturn { role: \"user\", content: \"\" }\n}\n\nfunction toolsToOpenAI(tools: ToolDef[]): unknown[] {\n\treturn tools.map((t) => ({\n\t\ttype: \"function\",\n\t\tfunction: {\n\t\t\tname: t.name,\n\t\t\tdescription: t.description,\n\t\t\tparameters: t.parameters,\n\t\t},\n\t}))\n}\n\nexport const streamOpenAI: StreamFn = (\n\topts: StreamOpts,\n): EventStream<StreamEvent, AssistantResult> => {\n\tconst es = new EventStream<StreamEvent, AssistantResult>()\n\n\t;(async () => {\n\t\ttry {\n\t\t\tconst body = {\n\t\t\t\tmodel: opts.model.id,\n\t\t\t\tmessages: [{ role: \"system\", content: opts.system }, ...opts.messages.map(msgToOpenAI)],\n\t\t\t\ttools: opts.tools.length > 0 ? toolsToOpenAI(opts.tools) : undefined,\n\t\t\t\tstream: true,\n\t\t\t}\n\n\t\t\tconst response = await fetch(`${opts.baseUrl}/chat/completions`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${opts.apiKey}`,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(body),\n\t\t\t\tsignal: opts.signal,\n\t\t\t})\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text()\n\t\t\t\tconst errorMsg = `API error ${response.status}: ${text}`\n\t\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\t\tes.finish({\n\t\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\t\tstop: \"error\",\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst reader = response.body?.getReader()\n\t\t\tif (!reader) {\n\t\t\t\tes.finish({ content: [], usage: { in: 0, out: 0 }, stop: \"error\" })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst decoder = new TextDecoder()\n\t\t\tlet buffer = \"\"\n\t\t\tconst currentToolCalls = new Map<number, { id: string; name: string; args: string }>()\n\t\t\tlet usage: Usage = { in: 0, out: 0 }\n\t\t\tlet textContent = \"\"\n\t\t\tlet stop = \"stop\"\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\tconst lines = buffer.split(\"\\n\")\n\t\t\t\tbuffer = lines.pop() ?? \"\"\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst trimmed = line.trim()\n\t\t\t\t\tif (!trimmed?.startsWith(\"data: \")) continue\n\t\t\t\t\tconst data = trimmed.slice(6)\n\t\t\t\t\tif (data === \"[DONE]\") continue\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst chunk = JSON.parse(data)\n\t\t\t\t\t\tconst delta = chunk.choices?.[0]?.delta\n\t\t\t\t\t\tif (!delta) continue\n\n\t\t\t\t\t\tif (delta.content) {\n\t\t\t\t\t\t\tes.push({ type: \"text_delta\", text: delta.content })\n\t\t\t\t\t\t\ttextContent += delta.content\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (delta.tool_calls) {\n\t\t\t\t\t\t\tfor (const tc of delta.tool_calls) {\n\t\t\t\t\t\t\t\tconst idx = tc.index ?? 0\n\t\t\t\t\t\t\t\tif (!currentToolCalls.has(idx)) {\n\t\t\t\t\t\t\t\t\tcurrentToolCalls.set(idx, {\n\t\t\t\t\t\t\t\t\t\tid: tc.id ?? \"\",\n\t\t\t\t\t\t\t\t\t\tname: tc.function?.name ?? \"\",\n\t\t\t\t\t\t\t\t\t\targs: \"\",\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst existing = currentToolCalls.get(idx)!\n\t\t\t\t\t\t\t\tif (tc.id) existing.id = tc.id\n\t\t\t\t\t\t\t\tif (tc.function?.name) existing.name = tc.function.name\n\t\t\t\t\t\t\t\tif (tc.function?.arguments) existing.args += tc.function.arguments\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (chunk.usage) {\n\t\t\t\t\t\t\tusage = {\n\t\t\t\t\t\t\t\tin: chunk.usage.prompt_tokens ?? 0,\n\t\t\t\t\t\t\t\tout: chunk.usage.completion_tokens ?? 0,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tes.push({ type: \"usage\", usage })\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst finishReason = chunk.choices?.[0]?.finish_reason\n\t\t\t\t\t\tif (finishReason) stop = finishReason\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Skip malformed JSON chunks\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst content: AssistantResult[\"content\"] = []\n\t\t\tif (textContent) {\n\t\t\t\tcontent.push({ type: \"text\", text: textContent })\n\t\t\t}\n\t\t\tfor (const [, tc] of currentToolCalls) {\n\t\t\t\tcontent.push({\n\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\tid: tc.id,\n\t\t\t\t\tname: tc.name,\n\t\t\t\t\targs: JSON.parse(tc.args || \"{}\"),\n\t\t\t\t})\n\t\t\t\tes.push({\n\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\tcall: {\n\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\tid: tc.id,\n\t\t\t\t\t\tname: tc.name,\n\t\t\t\t\t\targs: JSON.parse(tc.args || \"{}\"),\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tstop = \"tool_use\"\n\t\t\t}\n\n\t\t\tes.finish({ content, usage, stop: stop as StopReason })\n\t\t} catch (e) {\n\t\t\tif (opts.signal?.aborted) return\n\t\t\tconst errorMsg = `Unexpected error: ${e instanceof Error ? e.message : String(e)}`\n\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\tes.finish({\n\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\tstop: \"error\",\n\t\t\t})\n\t\t}\n\t})()\n\n\treturn es\n}\n","import type { AgentEvent, ApiFormat, AssistantResult, StreamFn, StreamOpts } from \"../types.ts\"\nimport { streamGemini } from \"./gemini.ts\"\nimport { streamOpenAI } from \"./openai.ts\"\n\nexport type { AssistantResult, StreamEvent, StreamFn, StreamOpts } from \"../types.ts\"\n\n/*\n * Push-based async event stream.\n *\n * Producers call push()/finish(). Consumers iterate with for-await-of.\n * Backpressure is implicit: push() resolves immediately; the iterator\n * awaits the next value only when the consumer asks for it.\n */\nexport class EventStream<T, R> {\n\t#events: T[] = []\n\t#done = false\n\t#result?: R\n\t#resolve?: (value: T) => void\n\t#doneResolve?: (value: R) => void\n\t#abort = false\n\n\tpush(event: T): void {\n\t\tif (this.#abort) return\n\t\t// If a consumer is already waiting, deliver directly — skip the queue\n\t\tif (this.#resolve) {\n\t\t\tconst resolve = this.#resolve\n\t\t\tthis.#resolve = undefined\n\t\t\tresolve(event)\n\t\t} else {\n\t\t\tthis.#events.push(event)\n\t\t}\n\t}\n\n\tfinish(result: R): void {\n\t\tthis.#done = true\n\t\tthis.#result = result\n\t\t// Wake up a suspended iterator so it can see done=true and exit\n\t\tif (this.#resolve) {\n\t\t\t// undefined is a sentinel — the iterator loop checks done after waking\n\t\t\tthis.#resolve(undefined as T)\n\t\t}\n\t\tif (this.#doneResolve) {\n\t\t\tthis.#doneResolve(result)\n\t\t}\n\t}\n\n\tabort(): void {\n\t\tthis.#abort = true\n\t\tthis.#done = true\n\t\tif (this.#resolve) {\n\t\t\tthis.#resolve(undefined as T)\n\t\t}\n\t\tif (this.#doneResolve) {\n\t\t\tthis.#doneResolve(undefined as R)\n\t\t}\n\t}\n\n\tasync *[Symbol.asyncIterator](): AsyncGenerator<T> {\n\t\twhile (!this.#done || this.#events.length > 0) {\n\t\t\tif (this.#events.length > 0) {\n\t\t\t\tyield this.#events.shift() as T\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (this.#done) break\n\t\t\tconst item = await new Promise<T | undefined>((resolve) => {\n\t\t\t\tthis.#resolve = resolve as (value: T) => void\n\t\t\t})\n\t\t\tif (item !== undefined && this.#events.length === 0) {\n\t\t\t\tyield item\n\t\t\t} else if (this.#events.length > 0) {\n\t\t\t\tyield this.#events.shift() as T\n\t\t\t}\n\t\t}\n\t}\n\n\tget result(): R | undefined {\n\t\treturn this.#result\n\t}\n\n\tget isDone(): boolean {\n\t\treturn this.#done\n\t}\n}\n\n// Internal map of registered provider implementations\nconst registry = new Map<ApiFormat, StreamFn>([\n\t[\"openai\", streamOpenAI],\n\t[\"gemini\", streamGemini],\n])\n\nexport function register(api: ApiFormat, fn: StreamFn): void {\n\tregistry.set(api, fn)\n}\n\n// Bridges provider-specific StreamEvents into AgentEvents so the loop and TUI deal with one type.\nexport function stream(opts: StreamOpts): EventStream<AgentEvent, AssistantResult> {\n\tconst fn = registry.get(opts.api)\n\tif (!fn) throw new Error(`No provider registered for API format: ${opts.api}`)\n\n\t// Bridge layer: converts provider-specific StreamEvents into the agent's\n\t// AgentEvent shape, so the loop and TUI only deal with one event type.\n\tconst providerStream = fn(opts)\n\tconst agentStream = new EventStream<AgentEvent, AssistantResult>()\n\n\t;(async () => {\n\t\tfor await (const event of providerStream) {\n\t\t\tif (event.type === \"text_delta\") {\n\t\t\t\tagentStream.push({ type: \"text_delta\", text: event.text ?? \"\" })\n\t\t\t} else if (event.type === \"thinking_delta\") {\n\t\t\t\tagentStream.push({ type: \"thinking_delta\", text: event.text ?? \"\" })\n\t\t\t} else if (event.type === \"tool_call\" && event.call) {\n\t\t\t\tagentStream.push({\n\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\tcall: {\n\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\tid: event.call.id,\n\t\t\t\t\t\tname: event.call.name,\n\t\t\t\t\t\targs: event.call.args,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t} else if (event.type === \"usage\" && event.usage) {\n\t\t\t\tagentStream.push({ type: \"usage\", usage: event.usage })\n\t\t\t}\n\t\t}\n\n\t\tconst res = providerStream.result\n\t\tif (res) {\n\t\t\tagentStream.finish(res)\n\t\t} else {\n\t\t\t// Fallback for unexpected closure\n\t\t\tagentStream.finish({ content: [], usage: { in: 0, out: 0 }, stop: \"stop\" })\n\t\t}\n\t})()\n\n\treturn agentStream\n}\n\nexport function getRegisteredApis(): ApiFormat[] {\n\treturn [...registry.keys()]\n}\n","import { isAbsolute, relative } from \"node:path\"\nimport chalk from \"chalk\"\nimport type { Msg, TextPart } from \"./types.ts\"\n\n// ~4 chars per token for English/code. Close enough for capacity warnings.\nexport function estimateTokens(messages: Msg[]): number {\n\tlet chars = 0\n\tfor (const msg of messages) {\n\t\tif (typeof msg.content === \"string\") {\n\t\t\tchars += msg.content.length\n\t\t} else if (Array.isArray(msg.content)) {\n\t\t\tfor (const part of msg.content) {\n\t\t\t\tif (part.type === \"text\") chars += part.text.length\n\t\t\t}\n\t\t}\n\t}\n\treturn Math.ceil(chars / 4)\n}\n\nexport function textPart(s: string): TextPart {\n\treturn { type: \"text\", text: s }\n}\n\nexport function getRelativeIfInside(cwd: string, filePath: string): string {\n\tif (filePath === cwd || filePath.startsWith(`${cwd}/`)) {\n\t\treturn relative(cwd, filePath) || \".\"\n\t}\n\treturn filePath\n}\n\nexport function makeRelative(val: string): string {\n\tif (typeof val !== \"string\") return val\n\n\tlet pathVal = val\n\tlet prefix = \"\"\n\tif (val.startsWith(\"file://\")) {\n\t\tpathVal = val.slice(7)\n\t\tprefix = \"file://\"\n\t}\n\n\tif (isAbsolute(pathVal)) {\n\t\tconst cwd = process.cwd()\n\t\treturn prefix + getRelativeIfInside(cwd, pathVal)\n\t}\n\treturn val\n}\n\nexport function formatToolArgs(\n\targs: Record<string, unknown> | undefined,\n\tuseChalk = false,\n): string {\n\tif (!args) return \"\"\n\treturn Object.entries(args)\n\t\t.map(([k, v]) => {\n\t\t\tconst val = typeof v === \"string\" ? makeRelative(v) : JSON.stringify(v)\n\t\t\tconst valStr = val.length > 40 ? `${val.slice(0, 40)}…` : val\n\t\t\tconst keyStr = useChalk ? chalk.dim(`${k}:`) : `${k}:`\n\t\t\treturn `${keyStr} ${valStr}`\n\t\t})\n\t\t.join(\" \")\n}\n","/**\n * Core agent loop that orchestrates model interaction and tool execution.\n * Handles turns, tool routing, safety checks, and event streaming.\n */\nimport { EventStream, stream } from \"../provider/stream.ts\"\nimport type {\n\tAgentEvent,\n\tAssistantMsg,\n\tLoopCtx,\n\tLoopOpts,\n\tMsg,\n\tToolCallPart,\n\tToolResultMsg,\n} from \"../types.ts\"\nimport { estimateTokens, textPart } from \"../util.ts\"\n\n// Safety cap so a misbehaving model can't loop forever\nconst MAX_TURNS = 50\n\nconst isToolCall = (c: unknown): c is ToolCallPart =>\n\ttypeof c === \"object\" && c !== null && (c as ToolCallPart).type === \"tool_call\"\n\n/**\n * Start a long-running agent session that yields an EventStream of updates.\n */\nexport function run(\n\tinput: string,\n\tctx: LoopCtx,\n\topts: LoopOpts,\n\tsignal?: AbortSignal,\n): EventStream<AgentEvent, Msg[]> {\n\tconst es = new EventStream<AgentEvent, Msg[]>()\n\tconst out: Msg[] = []\n\tconst maxTurns = opts.maxTurns ?? MAX_TURNS\n\n\tconst userMsg: Msg = { role: \"user\", content: input, ts: Date.now() }\n\tlet activeCtx: LoopCtx = { ...ctx, messages: [...ctx.messages, userMsg] }\n\tout.push(userMsg)\n\n\tconst tick = async () => {\n\t\tes.push({ type: \"start\" })\n\n\t\ttry {\n\t\t\tlet turns = 0\n\t\t\twhile (turns < maxTurns) {\n\t\t\t\tif (signal?.aborted) break\n\n\t\t\t\tturns++\n\t\t\t\tes.push({ type: \"turn\" })\n\n\t\t\t\t// Warn before hitting the hard limit so the caller can compact/summarize\n\t\t\t\tconst approxTokens = estimateTokens(activeCtx.messages)\n\t\t\t\tif (approxTokens > opts.model.contextWindow * 0.9) {\n\t\t\t\t\tes.push({\n\t\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\t\ttext: `[warning] Approaching context limit (~${Math.round(approxTokens / 1000)}k / ${Math.round(opts.model.contextWindow / 1000)}k tokens)`,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tconst reply = await getReply(activeCtx, opts, es, signal)\n\t\t\t\tout.push(reply)\n\t\t\t\tactiveCtx = { ...activeCtx, messages: [...activeCtx.messages, reply] }\n\t\t\t\tes.push({ type: \"assistant_msg\", msg: reply })\n\n\t\t\t\tif (reply.stop === \"error\" || reply.stop === \"aborted\") {\n\t\t\t\t\tes.push({ type: \"turn_end\", msg: reply, results: [] })\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tconst calls = reply.content.filter(isToolCall)\n\t\t\t\tif (calls.length === 0) {\n\t\t\t\t\tes.push({ type: \"turn_end\", msg: reply, results: [] })\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\t// Execute tool calls\n\t\t\t\tconst results: ToolResultMsg[] = []\n\t\t\t\tfor (const call of calls) {\n\t\t\t\t\tif (signal?.aborted) break\n\n\t\t\t\t\tconst tool = activeCtx.tools.find((t) => t.def.name === call.name)\n\t\t\t\t\tif (!tool) {\n\t\t\t\t\t\tconst errResult: ToolResultMsg = {\n\t\t\t\t\t\t\trole: \"tool_result\",\n\t\t\t\t\t\t\tcallId: call.id,\n\t\t\t\t\t\t\ttool: call.name,\n\t\t\t\t\t\t\tcontent: [textPart(`Unknown tool: ${call.name}`)],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresults.push(errResult)\n\t\t\t\t\t\tactiveCtx = { ...activeCtx, messages: [...activeCtx.messages, errResult] }\n\t\t\t\t\t\tout.push(errResult)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// beforeTool lets callers block dangerous operations (e.g. rm -rf)\n\t\t\t\t\tconst blocked = await opts.beforeTool?.(call, call.args, activeCtx)\n\t\t\t\t\tif (blocked?.block) {\n\t\t\t\t\t\tconst blockResult: ToolResultMsg = {\n\t\t\t\t\t\t\trole: \"tool_result\",\n\t\t\t\t\t\t\tcallId: call.id,\n\t\t\t\t\t\t\ttool: call.name,\n\t\t\t\t\t\t\tcontent: [textPart(blocked.reason ?? \"Blocked\")],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresults.push(blockResult)\n\t\t\t\t\t\tactiveCtx = { ...activeCtx, messages: [...activeCtx.messages, blockResult] }\n\t\t\t\t\t\tout.push(blockResult)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// Execute\n\t\t\t\t\tconst result = await tool.execute(call.args, signal)\n\t\t\t\t\tconst toolMsg: ToolResultMsg = {\n\t\t\t\t\t\trole: \"tool_result\",\n\t\t\t\t\t\tcallId: call.id,\n\t\t\t\t\t\ttool: call.name,\n\t\t\t\t\t\targs: call.args,\n\t\t\t\t\t\tcontent: result.content,\n\t\t\t\t\t\tisError: result.isError,\n\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t}\n\n\t\t\t\t\tresults.push(toolMsg)\n\t\t\t\t\tactiveCtx = { ...activeCtx, messages: [...activeCtx.messages, toolMsg] }\n\t\t\t\t\tout.push(toolMsg)\n\t\t\t\t\tes.push({ type: \"tool_result\", callId: call.id, result: toolMsg, args: call.args })\n\n\t\t\t\t\tawait opts.afterTool?.(call, toolMsg, activeCtx)\n\t\t\t\t}\n\n\t\t\t\tes.push({ type: \"turn_end\", msg: reply, results })\n\t\t\t}\n\n\t\t\tif (turns >= maxTurns) {\n\t\t\t\tes.push({\n\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\ttext: `[max turns reached (${maxTurns})]`,\n\t\t\t\t})\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif ((e as Error).name === \"AbortError\") {\n\t\t\t\tes.finish(out)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\n\t\tes.finish(out)\n\t}\n\n\ttick()\n\treturn es\n}\n\nasync function getReply(\n\tctx: LoopCtx,\n\topts: LoopOpts,\n\tes: EventStream<AgentEvent, Msg[]>,\n\tsignal?: AbortSignal,\n): Promise<AssistantMsg> {\n\tconst providerStream = stream({\n\t\tapi: opts.api,\n\t\tmodel: opts.model,\n\t\tapiKey: opts.apiKey,\n\t\tbaseUrl: opts.baseUrl,\n\t\tsystem: ctx.system,\n\t\tmessages: ctx.messages,\n\t\ttools: ctx.tools.map((t) => t.def),\n\t\tsignal,\n\t})\n\n\tconst content: AssistantMsg[\"content\"] = []\n\tlet usage = { in: 0, out: 0 }\n\n\t// Accumulate content and proxy events to the outer stream\n\tfor await (const ev of providerStream) {\n\t\tif (ev.type === \"text_delta\" && ev.text) {\n\t\t\tconst last = content[content.length - 1]\n\t\t\tif (last?.type === \"text\") {\n\t\t\t\tlast.text += ev.text\n\t\t\t} else {\n\t\t\t\tcontent.push(textPart(ev.text))\n\t\t\t}\n\t\t\tes.push({ type: \"text_delta\", text: ev.text })\n\t\t} else if (ev.type === \"thinking_delta\" && ev.text) {\n\t\t\tconst last = content[content.length - 1]\n\t\t\tif (last?.type === \"thinking\") {\n\t\t\t\tlast.text += ev.text\n\t\t\t} else {\n\t\t\t\tcontent.push({ type: \"thinking\", text: ev.text })\n\t\t\t}\n\t\t\tes.push({ type: \"thinking_delta\", text: ev.text })\n\t\t} else if (ev.type === \"tool_call\" && ev.call) {\n\t\t\tcontent.push(ev.call)\n\t\t\tes.push({ type: \"tool_call\", call: ev.call })\n\t\t} else if (ev.type === \"usage\" && ev.usage) {\n\t\t\tusage = ev.usage\n\t\t\tes.push({ type: \"usage\", usage })\n\t\t}\n\t}\n\n\tconst rawContent =\n\t\tproviderStream.result?.content && providerStream.result.content.length > 0\n\t\t\t? providerStream.result.content\n\t\t\t: content\n\n\tconst hasTool = rawContent.some((p) => p.type === \"tool_call\")\n\tconst cleanedContent = hasTool\n\t\t? rawContent.filter((p) => p.type !== \"text\" || p.text.trim().length > 0)\n\t\t: rawContent\n\n\tconst res = providerStream.result\n\tif (res) {\n\t\treturn {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: cleanedContent,\n\t\t\tmodel: opts.model.id,\n\t\t\tprovider: opts.model.provider,\n\t\t\tusage: res.usage,\n\t\t\tstop: res.stop,\n\t\t\tts: Date.now(),\n\t\t}\n\t}\n\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: cleanedContent,\n\t\tmodel: opts.model.id,\n\t\tprovider: opts.model.provider,\n\t\tusage,\n\t\tstop: cleanedContent.some((c) => c.type === \"tool_call\") ? \"tool_use\" : \"stop\",\n\t\tts: Date.now(),\n\t}\n}\n","import type { EventStream } from \"../provider/stream.ts\"\nimport type { AgentEvent, ApiFormat, LoopCtx, LoopOpts, Model, Msg, Tool } from \"../types.ts\"\nimport { run } from \"./loop.ts\"\n\nexport class Agent {\n\t#api: ApiFormat\n\t#model: Model\n\t#system: string\n\t#messages: Msg[] = []\n\t#tools: Tool[]\n\t#apiKey: string\n\t#baseUrl: string\n\n\tconstructor(opts: {\n\t\tapi: ApiFormat\n\t\tmodel: Model\n\t\tapiKey: string\n\t\tbaseUrl: string\n\t\tsystem: string\n\t\ttools: Tool[]\n\t\tmessages?: Msg[]\n\t}) {\n\t\tthis.#api = opts.api\n\t\tthis.#model = opts.model\n\t\tthis.#apiKey = opts.apiKey\n\t\tthis.#baseUrl = opts.baseUrl\n\t\tthis.#system = opts.system\n\t\tthis.#tools = opts.tools\n\t\tthis.#messages = opts.messages ?? []\n\t}\n\n\tget model(): Model {\n\t\treturn this.#model\n\t}\n\n\tget messages(): Msg[] {\n\t\treturn this.#messages\n\t}\n\n\tget tools(): Tool[] {\n\t\treturn this.#tools\n\t}\n\n\tget apiKey(): string {\n\t\treturn this.#apiKey\n\t}\n\n\tget baseUrl(): string {\n\t\treturn this.#baseUrl\n\t}\n\n\tupdateConfig(opts: { api: ApiFormat; model: Model; apiKey: string; baseUrl: string }): void {\n\t\tthis.#api = opts.api\n\t\tthis.#model = opts.model\n\t\tthis.#apiKey = opts.apiKey\n\t\tthis.#baseUrl = opts.baseUrl\n\t}\n\n\tsetTools(tools: Tool[]): void {\n\t\tthis.#tools = tools\n\t}\n\n\tsetMessages(msgs: Msg[]): void {\n\t\tthis.#messages = msgs\n\t}\n\n\tsetModel(model: Model): void {\n\t\tthis.#model = model\n\t}\n\n\tprompt(input: string, signal?: AbortSignal): EventStream<AgentEvent, Msg[]> {\n\t\tconst ctx: LoopCtx = {\n\t\t\tsystem: this.#system,\n\t\t\tmessages: this.#messages,\n\t\t\ttools: this.#tools,\n\t\t}\n\n\t\tconst opts: LoopOpts = {\n\t\t\tapi: this.#api,\n\t\t\tmodel: this.#model,\n\t\t\tapiKey: this.#apiKey,\n\t\t\tbaseUrl: this.#baseUrl,\n\t\t}\n\n\t\treturn run(input, ctx, opts, signal)\n\t}\n}\n","/**\n * Logic for constructing the foundational system instruction given the environment and tools.\n */\n\nimport os from \"node:os\"\nimport type { Tool } from \"../types.ts\"\n\nexport function buildSystemPrompt(cwd: string, tools: Tool[]): string {\n\tconst toolList = tools.map((t) => `- ${t.def.name}: ${t.def.description}`).join(\"\\n\")\n\tconst platform = os.platform()\n\tconst arch = os.arch()\n\tconst release = os.release()\n\tconst shell = process.env.SHELL || \"unknown\"\n\n\treturn `You are Nova, an expert coding assistant. Help users with coding tasks using the tools available.\n\nFormat your responses with clean, standard markdown. Use headers (##, ###), bold text (**bold**), inline code (\\`code\\`), and code blocks (\\`\\`\\`lang) to make your output clear and readable in the terminal.\n\n# Tools\n\n${toolList}\n\n# Environment\n\n- Working directory: ${cwd}\n- Operating System: ${platform} (${release})\n- Architecture: ${arch}\n- Shell: ${shell}\n- Date: ${new Date().toISOString().split(\"T\")[0]}\n\n# Guidelines\n\n- Use tools to fulfill requests. Do not fabricate file contents.\n- Explain what you are doing and why before each tool call.\n- Use the \"bash\" tool for ls, git, and other shell operations.\n- Always read a file before editing it.\n- Prefer edit over write for existing files.\n- Run relevant tests after making changes.\n- If a command fails, read the error carefully before retrying.\n- For multi-file changes, plan first, then execute.\n- When done, briefly summarize what was changed.\n- Be concise and direct.\n\n# Safety\n\n- Never delete files outside the working directory.\n- Never run destructive commands unless the user explicitly confirms.\n- If unsure, ask for clarification.\n- Never expose API keys, tokens, or secrets.`\n}\n","import { join } from \"node:path\"\nimport BetterSqlite3 from \"better-sqlite3\"\nimport type { Msg, Session } from \"../types.ts\"\n\nconst SCHEMA = `\nCREATE TABLE IF NOT EXISTS sessions (\n\tid TEXT PRIMARY KEY,\n\tcwd TEXT NOT NULL,\n\tmodel TEXT NOT NULL,\n\tprovider TEXT NOT NULL,\n\ttitle TEXT,\n\tcreated INTEGER NOT NULL,\n\tupdated INTEGER NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS messages (\n\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\tsession_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n\tseq INTEGER NOT NULL,\n\trole TEXT NOT NULL,\n\tcontent TEXT NOT NULL,\n\tts INTEGER NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id, seq);\n\nCREATE TABLE IF NOT EXISTS compactions (\n\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\tsession_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n\tsummary TEXT NOT NULL,\n\tfiles_read TEXT NOT NULL DEFAULT '[]',\n\tfiles_wrote TEXT NOT NULL DEFAULT '[]',\n\tseq_before INTEGER NOT NULL,\n\tts INTEGER NOT NULL\n);\n`\n\nfunction generateId(): string {\n\treturn `${Date.now().toString(36)}-${crypto.randomUUID().slice(0, 8)}`\n}\n\nexport class SessionStore {\n\t#db: BetterSqlite3.Database\n\n\tconstructor(dbPath: string) {\n\t\tthis.#db = new BetterSqlite3(dbPath)\n\t\tthis.#db.pragma(\"journal_mode = WAL\")\n\t\tthis.#db.pragma(\"foreign_keys = ON\")\n\t\tthis.#db.exec(SCHEMA)\n\t}\n\n\tcreate(cwd: string, model: string, provider: string): Session {\n\t\tconst id = generateId()\n\t\tconst now = Date.now()\n\t\tthis.#db\n\t\t\t.prepare(\n\t\t\t\t\"INSERT INTO sessions (id, cwd, model, provider, title, created, updated) VALUES ($id, $cwd, $model, $provider, $title, $created, $updated)\",\n\t\t\t)\n\t\t\t.run({\n\t\t\t\tid: id,\n\t\t\t\tcwd: cwd,\n\t\t\t\tmodel: model,\n\t\t\t\tprovider: provider,\n\t\t\t\ttitle: null,\n\t\t\t\tcreated: now,\n\t\t\t\tupdated: now,\n\t\t\t})\n\t\treturn { id, cwd, model, provider, title: null, created: now, updated: now }\n\t}\n\n\tget(id: string): Session | null {\n\t\treturn (\n\t\t\t(this.#db\n\t\t\t\t.prepare(\n\t\t\t\t\t\"SELECT id, cwd, model, provider, title, created, updated FROM sessions WHERE id = $id\",\n\t\t\t\t)\n\t\t\t\t.get({ id: id }) as Session | null) ?? null\n\t\t)\n\t}\n\n\tlist(limit = 50): Session[] {\n\t\treturn this.#db\n\t\t\t.prepare(\n\t\t\t\t\"SELECT id, cwd, model, provider, title, created, updated FROM sessions ORDER BY updated DESC LIMIT $limit\",\n\t\t\t)\n\t\t\t.all({ limit: limit }) as Session[]\n\t}\n\n\tdelete(id: string): boolean {\n\t\tconst result = this.#db.prepare(\"DELETE FROM sessions WHERE id = $id\").run({ id: id })\n\t\treturn result.changes > 0\n\t}\n\n\tappend(sessionId: string, msg: Msg): void {\n\t\tconst seq = this.#nextSeq(sessionId)\n\t\tthis.#db\n\t\t\t.prepare(\n\t\t\t\t\"INSERT INTO messages (session_id, seq, role, content, ts) VALUES ($sid, $seq, $role, $content, $ts)\",\n\t\t\t)\n\t\t\t.run({\n\t\t\t\tsid: sessionId,\n\t\t\t\tseq: seq,\n\t\t\t\trole: msg.role,\n\t\t\t\tcontent: JSON.stringify(msg),\n\t\t\t\tts: msg.ts,\n\t\t\t})\n\t\tthis.#db\n\t\t\t.prepare(\"UPDATE sessions SET updated = $now WHERE id = $id\")\n\t\t\t.run({ now: Date.now(), id: sessionId })\n\t}\n\n\tappendMany(sessionId: string, msgs: Msg[]): void {\n\t\tconst tx = this.#db.transaction(() => {\n\t\t\tfor (const msg of msgs) {\n\t\t\t\tthis.append(sessionId, msg)\n\t\t\t}\n\t\t})\n\t\ttx()\n\t}\n\n\tmessages(sessionId: string): Msg[] {\n\t\tconst rows = this.#db\n\t\t\t.prepare(\"SELECT content FROM messages WHERE session_id = $sid ORDER BY seq ASC\")\n\t\t\t.all({ sid: sessionId }) as { content: string }[]\n\t\treturn rows.map((r) => JSON.parse(r.content) as Msg)\n\t}\n\n\tmessagesAfter(sessionId: string, afterSeq: number): Msg[] {\n\t\tconst rows = this.#db\n\t\t\t.prepare(\n\t\t\t\t\"SELECT content FROM messages WHERE session_id = $sid AND seq > $seq ORDER BY seq ASC\",\n\t\t\t)\n\t\t\t.all({ sid: sessionId, seq: afterSeq }) as { content: string }[]\n\t\treturn rows.map((r) => JSON.parse(r.content) as Msg)\n\t}\n\n\tsetTitle(sessionId: string, title: string): void {\n\t\tthis.#db\n\t\t\t.prepare(\"UPDATE sessions SET title = $title WHERE id = $id\")\n\t\t\t.run({ title: title, id: sessionId })\n\t}\n\n\tmessageCount(sessionId: string): number {\n\t\tconst row = this.#db\n\t\t\t.prepare(\"SELECT COUNT(*) as count FROM messages WHERE session_id = $sid\")\n\t\t\t.get({ sid: sessionId }) as { count: number }\n\t\treturn row.count\n\t}\n\n\tsaveCompaction(\n\t\tsessionId: string,\n\t\tsummary: string,\n\t\tfilesRead: string[],\n\t\tfilesWrote: string[],\n\t\tseqBefore: number,\n\t): void {\n\t\tthis.#db\n\t\t\t.prepare(\n\t\t\t\t\"INSERT INTO compactions (session_id, summary, files_read, files_wrote, seq_before, ts) VALUES ($sid, $summary, $read, $wrote, $seq, $ts)\",\n\t\t\t)\n\t\t\t.run({\n\t\t\t\tsid: sessionId,\n\t\t\t\tsummary: summary,\n\t\t\t\tread: JSON.stringify(filesRead),\n\t\t\t\twrote: JSON.stringify(filesWrote),\n\t\t\t\tseq: seqBefore,\n\t\t\t\tts: Date.now(),\n\t\t\t})\n\t}\n\n\tgetLatestCompaction(sessionId: string): { summary: string; seqBefore: number } | null {\n\t\treturn (\n\t\t\t(this.#db\n\t\t\t\t.prepare(\n\t\t\t\t\t\"SELECT summary, seq_before FROM compactions WHERE session_id = $sid ORDER BY ts DESC LIMIT 1\",\n\t\t\t\t)\n\t\t\t\t.get({ sid: sessionId }) as { summary: string; seqBefore: number } | null) ?? null\n\t\t)\n\t}\n\n\ttruncateBeforeSeq(sessionId: string, seq: number): void {\n\t\tthis.#db\n\t\t\t.prepare(\"DELETE FROM messages WHERE session_id = $sid AND seq < $seq\")\n\t\t\t.run({ sid: sessionId, seq: seq })\n\t}\n\n\tclose(): void {\n\t\tthis.#db.close()\n\t}\n\n\t#nextSeq(sessionId: string): number {\n\t\tconst row = this.#db\n\t\t\t.prepare(\"SELECT MAX(seq) as maxSeq FROM messages WHERE session_id = $sid\")\n\t\t\t.get({ sid: sessionId }) as { maxSeq: number | null }\n\t\treturn (row.maxSeq ?? 0) + 1\n\t}\n}\n\nlet _store: SessionStore | null = null\n\nexport function getSessionStore(dir?: string): SessionStore {\n\tif (_store) return _store\n\tconst dbPath = join(dir ?? join(process.env.HOME ?? \"~\", \".novacode\"), \"sessions.db\")\n\t_store = new SessionStore(dbPath)\n\treturn _store\n}\n","import { getSessionStore } from \"../session/store.ts\"\n\nexport async function handleSessionCommand(args: string[]): Promise<void> {\n\tconst store = getSessionStore()\n\tconst [subcommand, id] = args\n\n\tif (subcommand === \"list\" || subcommand === \"ls\") {\n\t\tconst sessions = store.list()\n\t\tif (sessions.length === 0) {\n\t\t\tconsole.log(\"No sessions found.\")\n\t\t\treturn\n\t\t}\n\n\t\tconsole.log(\"ID\".padEnd(25), \"MODEL\".padEnd(20), \"UPDATED\")\n\t\tconsole.log(\"-\".repeat(70))\n\t\tfor (const s of sessions) {\n\t\t\tconst date = new Date(s.updated).toLocaleString()\n\t\t\tconsole.log(s.id.padEnd(25), s.model.padEnd(20), date)\n\t\t}\n\t\treturn\n\t}\n\n\tif (subcommand === \"delete\" || subcommand === \"rm\") {\n\t\tif (!id) {\n\t\t\tconsole.error(\"Usage: novacode session delete <id>\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t\tconst success = store.delete(id)\n\t\tif (success) {\n\t\t\tconsole.log(`Deleted session: ${id}`)\n\t\t} else {\n\t\t\tconsole.error(`Session not found: ${id}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\t\treturn\n\t}\n\n\tconsole.error(\"Unknown session subcommand. Use 'list' or 'delete'.\")\n\tprocess.exit(1)\n}\n","import type { Model, ProviderDef } from \"../types.ts\"\n\nexport const PROVIDERS: ProviderDef[] = [\n\t{\n\t\tid: \"glm\",\n\t\tname: \"GLM (Z.AI)\",\n\t\tapi: \"openai\",\n\t\tbaseUrl: \"https://api.z.ai/api/coding/paas/v4\",\n\t\tenvKey: \"GLM_API_KEY\",\n\t},\n\t{\n\t\tid: \"gemini\",\n\t\tname: \"Gemini (Google)\",\n\t\tapi: \"gemini\",\n\t\tbaseUrl: \"https://generativelanguage.googleapis.com\",\n\t\tenvKey: \"GEMINI_API_KEY\",\n\t},\n\t{\n\t\tid: \"deepseek\",\n\t\tname: \"DeepSeek\",\n\t\tapi: \"openai\",\n\t\tbaseUrl: \"https://api.deepseek.com\",\n\t\tenvKey: \"DEEPSEEK_API_KEY\",\n\t},\n\t{\n\t\tid: \"openai\",\n\t\tname: \"OpenAI\",\n\t\tapi: \"openai\",\n\t\tbaseUrl: \"https://api.openai.com/v1\",\n\t\tenvKey: \"OPENAI_API_KEY\",\n\t},\n]\n\nexport const MODELS: Model[] = [\n\t// GLM\n\t{\n\t\tid: \"glm-5.1\",\n\t\tname: \"GLM-5.1\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-5\",\n\t\tname: \"GLM-5\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-5-turbo\",\n\t\tname: \"GLM-5 Turbo\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-4.7\",\n\t\tname: \"GLM-4.7\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-4.7-flash\",\n\t\tname: \"GLM-4.7 Flash (Free)\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-4.5-flash\",\n\t\tname: \"GLM-4.5 Flash (Free)\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t// Gemini\n\t{\n\t\tid: \"gemini-3.5-flash\",\n\t\tname: \"Gemini 3.5 Flash\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-pro-preview\",\n\t\tname: \"Gemini 3.1 Pro Preview\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 2_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-pro-preview-customtools\",\n\t\tname: \"Gemini 3.1 Pro (Custom Tools)\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 2_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-flash-lite\",\n\t\tname: \"Gemini 3.1 Flash-Lite\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-flash-lite-preview\",\n\t\tname: \"Gemini 3.1 Flash-Lite Preview\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3-flash-preview\",\n\t\tname: \"Gemini 3 Flash Preview\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-2.5-pro\",\n\t\tname: \"Gemini 2.5 Pro\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 2_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"gemini-2.5-flash\",\n\t\tname: \"Gemini 2.5 Flash\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"gemini-2.5-flash-lite\",\n\t\tname: \"Gemini 2.5 Flash-Lite\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"gemini-2.5-computer-use-preview-10-2025\",\n\t\tname: \"Gemini 2.5 Computer Use\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t// DeepSeek\n\t{\n\t\tid: \"deepseek-chat\",\n\t\tname: \"DeepSeek V3\",\n\t\tprovider: \"deepseek\",\n\t\tcontextWindow: 64_000,\n\t\tmaxTokens: 8_192,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"deepseek-reasoner\",\n\t\tname: \"DeepSeek R1\",\n\t\tprovider: \"deepseek\",\n\t\tcontextWindow: 64_000,\n\t\tmaxTokens: 8_192,\n\t\tsupportsThinking: true,\n\t},\n\t// OpenAI\n\t{\n\t\tid: \"gpt-4o\",\n\t\tname: \"GPT-4o\",\n\t\tprovider: \"openai\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 16_384,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"o4-mini\",\n\t\tname: \"o4-mini\",\n\t\tprovider: \"openai\",\n\t\tcontextWindow: 200_000,\n\t\tmaxTokens: 100_000,\n\t\tsupportsThinking: true,\n\t},\n]\n\nexport function getProvider(id: string): ProviderDef | undefined {\n\treturn PROVIDERS.find((p) => p.id === id)\n}\n\nexport function getModelsForProvider(providerId: string): Model[] {\n\treturn MODELS.filter((m) => m.provider === providerId)\n}\n","import { chmod, mkdir, readFile, stat, writeFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport type { NovaAuth, NovaConfig } from \"../types.ts\"\n\nconst NOVA_DIR = () => join(process.env.HOME ?? \"~\", \".novacode\")\nconst CONFIG_PATH = () => join(NOVA_DIR(), \"config.json\")\nconst AUTH_PATH = () => join(NOVA_DIR(), \"auth.json\")\n\nconst defaultConfig: NovaConfig = {\n\tprovider: \"\",\n\tmodel: \"\",\n}\n\nconst defaultAuth: NovaAuth = {\n\tapiKeys: {},\n}\n\nexport async function configExists(): Promise<boolean> {\n\ttry {\n\t\tawait stat(CONFIG_PATH())\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\nexport async function loadConfig(): Promise<NovaConfig> {\n\ttry {\n\t\tconst raw = JSON.parse(await readFile(CONFIG_PATH(), \"utf-8\"))\n\t\treturn { ...defaultConfig, ...raw }\n\t} catch {\n\t\treturn { ...defaultConfig }\n\t}\n}\n\nexport async function loadAuth(): Promise<NovaAuth> {\n\ttry {\n\t\tconst raw = JSON.parse(await readFile(AUTH_PATH(), \"utf-8\"))\n\t\treturn { ...defaultAuth, ...raw }\n\t} catch {\n\t\treturn { ...defaultAuth }\n\t}\n}\n\nasync function ensureDir(): Promise<void> {\n\tawait mkdir(NOVA_DIR(), { recursive: true })\n}\n\nexport async function saveConfig(config: NovaConfig): Promise<void> {\n\tawait ensureDir()\n\tawait writeFile(CONFIG_PATH(), JSON.stringify(config, null, 2))\n}\n\nexport async function saveAuth(auth: NovaAuth): Promise<void> {\n\tawait ensureDir()\n\tawait writeFile(AUTH_PATH(), JSON.stringify(auth, null, 2))\n\ttry {\n\t\tawait chmod(AUTH_PATH(), 0o600)\n\t} catch {\n\t\t// chmod may fail on some platforms, non-fatal\n\t}\n}\n\nexport function getNovaDir(): string {\n\treturn NOVA_DIR()\n}\n","import { Box, render, Text, useInput } from \"ink\"\nimport { useState } from \"react\"\n\ninterface SelectOption {\n\tvalue: string\n\tlabel: string\n\thint?: string\n}\n\nexport function SelectPrompt({\n\tmessage,\n\toptions,\n\theader,\n\tonSelect,\n}: {\n\tmessage: string\n\toptions: SelectOption[]\n\theader?: string\n\tonSelect: (value: string | null) => void\n}) {\n\tconst [idx, setIdx] = useState(0)\n\n\tuseInput((_, key) => {\n\t\tif (key.escape) {\n\t\t\tonSelect(null)\n\t\t\treturn\n\t\t}\n\t\tif (key.upArrow) {\n\t\t\tsetIdx((i) => (i - 1 + options.length) % options.length)\n\t\t\treturn\n\t\t}\n\t\tif (key.downArrow) {\n\t\t\tsetIdx((i) => (i + 1) % options.length)\n\t\t\treturn\n\t\t}\n\t\tif (key.return) {\n\t\t\tonSelect(options[idx]?.value ?? null)\n\t\t}\n\t})\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t{header && (\n\t\t\t\t<Box marginBottom={1}>\n\t\t\t\t\t<Text>{header}</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t<Box marginBottom={1}>\n\t\t\t\t<Text bold>{message}</Text>\n\t\t\t</Box>\n\t\t\t{options.map((opt, i) => (\n\t\t\t\t<Box key={opt.value}>\n\t\t\t\t\t<Text color={i === idx ? \"green\" : undefined}>\n\t\t\t\t\t\t{i === idx ? \"❯ \" : \" \"}\n\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t</Text>\n\t\t\t\t\t{opt.hint && i === idx && <Text dimColor> {opt.hint}</Text>}\n\t\t\t\t</Box>\n\t\t\t))}\n\t\t\t<Box marginTop={1}>\n\t\t\t\t<Text dimColor>↑↓ navigate · Enter select · Esc cancel</Text>\n\t\t\t</Box>\n\t\t</Box>\n\t)\n}\n\nexport function PasswordPrompt({\n\tmessage,\n\tvalidate,\n\tonSubmit,\n}: {\n\tmessage: string\n\tvalidate?: (v: string) => string | undefined\n\tonSubmit: (value: string | null) => void\n}) {\n\tconst [value, setValue] = useState(\"\")\n\tconst [error, setError] = useState(\"\")\n\n\tuseInput((ch, key) => {\n\t\tif (key.escape) {\n\t\t\tonSubmit(null)\n\t\t\treturn\n\t\t}\n\t\tif (key.return) {\n\t\t\tconst err = validate?.(value)\n\t\t\tif (err) {\n\t\t\t\tsetError(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tonSubmit(value)\n\t\t\treturn\n\t\t}\n\t\tif (key.backspace || key.delete) {\n\t\t\tsetValue((v) => v.slice(0, -1))\n\t\t\tsetError(\"\")\n\t\t\treturn\n\t\t}\n\t\tif (ch) {\n\t\t\tsetValue((v) => v + ch)\n\t\t\tsetError(\"\")\n\t\t}\n\t})\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t<Box marginBottom={1}>\n\t\t\t\t<Text bold>{message}</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Text color=\"green\">│ </Text>\n\t\t\t\t<Text dimColor>{\"*\".repeat(value.length)}</Text>\n\t\t\t\t<Text color=\"green\">│</Text>\n\t\t\t</Box>\n\t\t\t{error && (\n\t\t\t\t<Box>\n\t\t\t\t\t<Text color=\"red\">{error}</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t<Box marginTop={1}>\n\t\t\t\t<Text dimColor>Enter submit · Esc cancel</Text>\n\t\t\t</Box>\n\t\t</Box>\n\t)\n}\n\nexport function ConfirmPrompt({\n\tmessage,\n\tonConfirm,\n}: {\n\tmessage: string\n\tonConfirm: (value: boolean | null) => void\n}) {\n\tconst [yes, setYes] = useState(true)\n\n\tuseInput((_, key) => {\n\t\tif (key.escape) {\n\t\t\tonConfirm(null)\n\t\t\treturn\n\t\t}\n\t\tif (key.leftArrow || key.rightArrow || key.tab) {\n\t\t\tsetYes((y) => !y)\n\t\t\treturn\n\t\t}\n\t\tif (key.return) {\n\t\t\tonConfirm(yes)\n\t\t}\n\t})\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t<Box marginBottom={1}>\n\t\t\t\t<Text bold>{message}</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Text color={yes ? \"green\" : undefined}>{yes ? \"❯ \" : \" \"}Yes</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Text color={!yes ? \"red\" : undefined}>{!yes ? \"❯ \" : \" \"}No</Text>\n\t\t\t</Box>\n\t\t\t<Box marginTop={1}>\n\t\t\t\t<Text dimColor>←→ toggle · Enter confirm · Esc cancel</Text>\n\t\t\t</Box>\n\t\t</Box>\n\t)\n}\n\n// Standalone wrappers for use outside the main TUI (e.g. onboarding)\n\nexport function standaloneSelect(\n\tmessage: string,\n\toptions: SelectOption[],\n\theader?: string,\n): Promise<string | null> {\n\treturn new Promise((resolve) => {\n\t\tconst { unmount } = render(\n\t\t\t<SelectPrompt\n\t\t\t\tmessage={message}\n\t\t\t\toptions={options}\n\t\t\t\theader={header}\n\t\t\t\tonSelect={(v) => {\n\t\t\t\t\tunmount()\n\t\t\t\t\tresolve(v)\n\t\t\t\t}}\n\t\t\t/>,\n\t\t)\n\t})\n}\n\nexport function standalonePassword(\n\tmessage: string,\n\tvalidate?: (v: string) => string | undefined,\n): Promise<string | null> {\n\treturn new Promise((resolve) => {\n\t\tconst { unmount } = render(\n\t\t\t<PasswordPrompt\n\t\t\t\tmessage={message}\n\t\t\t\tvalidate={validate}\n\t\t\t\tonSubmit={(v) => {\n\t\t\t\t\tunmount()\n\t\t\t\t\tresolve(v)\n\t\t\t\t}}\n\t\t\t/>,\n\t\t)\n\t})\n}\n","import chalk from \"chalk\"\nimport { getModelsForProvider, getProvider, PROVIDERS } from \"../config/providers.ts\"\nimport { saveAuth, saveConfig } from \"../config/store.ts\"\nimport { standalonePassword, standaloneSelect } from \"../tui/prompts.tsx\"\nimport type { NovaConfig } from \"../types.ts\"\n\nexport async function runOnboarding(): Promise<NovaConfig> {\n\tconsole.log(chalk.bold.cyan(\"\\n⚡ Nova — your coding companion\\n\"))\n\n\tconst providerId = await standaloneSelect(\n\t\t\"Pick a provider\",\n\t\tPROVIDERS.map((p) => ({ value: p.id, label: p.name })),\n\t)\n\tif (!providerId) {\n\t\tconsole.log(chalk.dim(\"Cancelled\"))\n\t\tprocess.exit(0)\n\t}\n\n\tconst provider = getProvider(providerId)\n\tif (!provider) {\n\t\tconsole.log(chalk.red(\"Unknown provider\"))\n\t\tprocess.exit(1)\n\t}\n\n\tconst apiKey = await standalonePassword(`Enter ${provider.name} API key`)\n\tif (!apiKey) {\n\t\tconsole.log(chalk.dim(\"Cancelled\"))\n\t\tprocess.exit(0)\n\t}\n\n\tconst models = getModelsForProvider(providerId)\n\tconst modelId = await standaloneSelect(\n\t\t\"Pick a default model\",\n\t\tmodels.map((m) => ({\n\t\t\tvalue: m.id,\n\t\t\tlabel: `${m.name} (${(m.contextWindow / 1000).toFixed(0)}k ctx)`,\n\t\t})),\n\t)\n\tif (!modelId) {\n\t\tconsole.log(chalk.dim(\"Cancelled\"))\n\t\tprocess.exit(0)\n\t}\n\n\tconst config: NovaConfig = {\n\t\tprovider: providerId,\n\t\tmodel: modelId,\n\t}\n\n\tawait saveConfig(config)\n\tawait saveAuth({ apiKeys: { [providerId]: apiKey } })\n\n\tconsole.log(chalk.green(\"\\n✓ Ready. Type your prompt or /help for commands\\n\"))\n\treturn config\n}\n","/**\n * Filesystem tools for reading, writing, and editing files.\n * Includes safety checks to prevent path traversal.\n */\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\"\nimport { dirname, extname, resolve } from \"node:path\"\nimport type { Tool, ToolResult } from \"../types.ts\"\nimport { getRelativeIfInside, textPart } from \"../util.ts\"\n\n// Extensions we return as base64 images instead of text\nconst IMAGES = new Set([\".jpg\", \".jpeg\", \".png\", \".gif\", \".webp\"])\n\nfunction safePath(cwd: string, p: string): string {\n\tconst abs = resolve(cwd, p)\n\tif (abs !== cwd && !abs.startsWith(`${cwd}/`)) {\n\t\tthrow new Error(`Path outside project: ${p}`)\n\t}\n\treturn abs\n}\n\nexport function readTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"read\",\n\t\t\tdescription:\n\t\t\t\t\"Read file contents. Supports text and images (jpg, png, gif, webp). Text output is truncated to 2000 lines.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Path to file (relative or absolute)\" },\n\t\t\t\t\toffset: { type: \"number\", description: \"Start line (1-based, default 1)\" },\n\t\t\t\t\tlimit: { type: \"number\", description: \"Max lines to read (default 2000)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"path\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst filePath = safePath(cwd, args.path as string)\n\t\t\t\t// Return images as base64 so the LLM can process them visually\n\t\t\t\tconst ext = extname(filePath).toLowerCase()\n\t\t\t\tif (IMAGES.has(ext)) {\n\t\t\t\t\tconst buf = await readFile(filePath)\n\t\t\t\t\tconst b64 = buf.toString(\"base64\")\n\t\t\t\t\tconst mime = ext === \".jpg\" ? \"image/jpeg\" : `image/${ext.slice(1)}`\n\t\t\t\t\treturn { content: [{ type: \"image\", data: b64, mime }], isError: false }\n\t\t\t\t}\n\n\t\t\t\tconst content = await readFile(filePath, \"utf-8\")\n\t\t\t\tconst lines = content.split(\"\\n\")\n\t\t\t\tconst offset = Math.max(0, (Number(args.offset ?? 1) || 1) - 1)\n\t\t\t\tconst limit = Number(args.limit ?? 2000) || 2000\n\t\t\t\tconst slice = lines.slice(offset, offset + limit)\n\t\t\t\tconst truncated = offset + limit < lines.length\n\n\t\t\t\tconst out = slice.join(\"\\n\")\n\t\t\t\tconst suffix = truncated ? `\\n…${lines.length - offset - limit} more lines` : \"\"\n\n\t\t\t\treturn { content: [textPart(out + suffix)], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error reading file: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\nexport function writeTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"write\",\n\t\t\tdescription: \"Write content to a file. Creates the file and parent directories if needed.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Path to file\" },\n\t\t\t\t\tcontent: { type: \"string\", description: \"Content to write\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"path\", \"content\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst filePath = safePath(cwd, args.path as string)\n\t\t\t\tconst content = args.content as string\n\t\t\t\tawait mkdir(dirname(filePath), { recursive: true })\n\t\t\t\tawait writeFile(filePath, content)\n\t\t\t\tconst relPath = getRelativeIfInside(cwd, filePath)\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Wrote ${content.length} bytes → ${relPath}`)],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error writing file: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n// Requires oldText to be unique to avoid ambiguous replacements.\nexport function editTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"edit\",\n\t\t\tdescription:\n\t\t\t\t\"Edit a file using exact text replacement. Each edit's oldText must be unique in the file.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Path to file\" },\n\t\t\t\t\tedits: {\n\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Array of {oldText, newText} replacements. oldText must be unique. Non-overlapping.\",\n\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\toldText: { type: \"string\", description: \"Exact text to find (must be unique)\" },\n\t\t\t\t\t\t\t\tnewText: { type: \"string\", description: \"Replacement text\" },\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\trequired: [\"oldText\", \"newText\"],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trequired: [\"path\", \"edits\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst filePath = safePath(cwd, args.path as string)\n\t\t\t\tlet content: string\n\t\t\t\ttry {\n\t\t\t\t\tcontent = await readFile(filePath, \"utf-8\")\n\t\t\t\t} catch {\n\t\t\t\t\treturn { content: [textPart(`File not found: ${args.path}`)], isError: true }\n\t\t\t\t}\n\t\t\t\tconst edits = args.edits as Array<{ oldText: string; newText: string }>\n\n\t\t\t\t// Validate all edits before applying any — avoids partial writes on bad input\n\t\t\t\tfor (const edit of edits) {\n\t\t\t\t\tconst count = content.split(edit.oldText).length - 1\n\t\t\t\t\tif (count === 0) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [textPart(`oldText not found: \"${edit.oldText.slice(0, 80)}…\"`)],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Ambiguous match would replace the wrong occurrence\n\t\t\t\t\tif (count > 1) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\ttextPart(\n\t\t\t\t\t\t\t\t\t`oldText found ${count} times — add surrounding context to make it unique: \"${edit.oldText.slice(0, 60)}…\"`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Apply edits sequentially\n\t\t\t\tfor (const edit of edits) {\n\t\t\t\t\tcontent = content.replace(edit.oldText, edit.newText)\n\t\t\t\t}\n\n\t\t\t\tawait writeFile(filePath, content)\n\t\t\t\tconst relPath = getRelativeIfInside(cwd, filePath)\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\ttextPart(\n\t\t\t\t\t\t\t`Edited ${relPath} (${edits.length} replacement${edits.length > 1 ? \"s\" : \"\"})`,\n\t\t\t\t\t\t),\n\t\t\t\t\t],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error editing file: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Git tools for executing safe repository operations programmatically.\n */\nimport { spawn } from \"node:child_process\"\nimport type { Tool, ToolResult } from \"../types.ts\"\nimport { textPart } from \"../util.ts\"\n\nexport function gitTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"git\",\n\t\t\tdescription:\n\t\t\t\t\"Execute safe, non-interactive git commands (status, diff, log, add, commit) in the repository.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\taction: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\tenum: [\"status\", \"diff\", \"log\", \"add\", \"commit\"],\n\t\t\t\t\t\tdescription: \"The git action to execute\",\n\t\t\t\t\t},\n\t\t\t\t\targs: {\n\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\tdescription: \"Optional additional arguments or file paths for the git action\",\n\t\t\t\t\t\titems: { type: \"string\" },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trequired: [\"action\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst action = args.action as string\n\t\t\tconst extraArgs = (args.args as string[]) || []\n\n\t\t\tconst allowed = new Set([\"status\", \"diff\", \"log\", \"add\", \"commit\"])\n\t\t\tif (!allowed.has(action)) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: Git action '${action}' is not supported.`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst cmd = [\"git\", action, ...extraArgs]\n\t\t\t\tconst proc = spawn(cmd[0]!, cmd.slice(1), {\n\t\t\t\t\tcwd,\n\t\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t\tenv: { ...process.env, PAGER: \"cat\" },\n\t\t\t\t})\n\n\t\t\t\tlet stdout = \"\"\n\t\t\t\tlet stderr = \"\"\n\t\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstdout += chunk.toString()\n\t\t\t\t})\n\t\t\t\tproc.stderr.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstderr += chunk.toString()\n\t\t\t\t})\n\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tproc.kill(\"SIGKILL\")\n\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t}\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\t\t\tlet exitCode: number\n\t\t\t\ttry {\n\t\t\t\t\texitCode = await new Promise<number>((resolve, reject) => {\n\t\t\t\t\t\tproc.on(\"error\", reject)\n\t\t\t\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t\t\t\t})\n\t\t\t\t} finally {\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort)\n\t\t\t\t}\n\n\t\t\t\t// Prevent context window blowout by truncating very large outputs\n\t\t\t\tconst MAX = 50_000\n\t\t\t\tlet out = \"\"\n\t\t\t\tif (stdout) out += stdout.slice(0, MAX)\n\t\t\t\tif (stderr) {\n\t\t\t\t\tif (out) out += \"\\n\"\n\t\t\t\t\tout += stderr.slice(0, MAX - out.length)\n\t\t\t\t}\n\t\t\t\tif (out.length >= MAX) out += \"\\n…truncated\"\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(out || \"(no output)\")],\n\t\t\t\t\tisError: exitCode !== 0,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error running git: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Search tools for finding files and content.\n * Uses 'rg' (ripgrep) if available, falling back to a pure JS implementation.\n */\n\nimport { spawn } from \"node:child_process\"\nimport { readdir, readFile } from \"node:fs/promises\"\nimport { relative, resolve } from \"node:path\"\nimport { glob } from \"glob\"\nimport type { Tool, ToolResult } from \"../types.ts\"\n\nimport { textPart } from \"../util.ts\"\n\n/**\n * Tool for finding files by glob pattern.\n */\nexport function globTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"glob\",\n\t\t\tdescription: \"Find files by glob pattern (e.g. **/*.ts, src/**/*.test.ts).\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpattern: { type: \"string\", description: \"Glob pattern (e.g. **/*.ts)\" },\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory to search in (default .)\" },\n\t\t\t\t\tnocase: { type: \"boolean\", description: \"Case-insensitive search (default false)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"pattern\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst rawPath = (args.path as string) || \".\"\n\t\t\t\tconst dir = resolve(cwd, rawPath)\n\t\t\t\tif (dir !== cwd && !dir.startsWith(`${cwd}/`)) {\n\t\t\t\t\tthrow new Error(`Path outside project: ${rawPath}`)\n\t\t\t\t}\n\n\t\t\t\tconst pattern = args.pattern as string\n\t\t\t\tconst nocase = !!args.nocase\n\t\t\t\tconst files = await glob(pattern, { cwd: dir, nocase })\n\t\t\t\tconst sliced = files.slice(0, 500)\n\t\t\t\tconst relSearchPath = relative(cwd, dir)\n\t\t\t\tconst prefix = relSearchPath ? `${relSearchPath}/` : \"\"\n\t\t\t\tconst relFiles = sliced.map((f) => prefix + f)\n\t\t\t\tconst out = relFiles.length > 0 ? relFiles.join(\"\\n\") : \"No files found\"\n\t\t\t\treturn { content: [textPart(out)], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Tool for searching file contents using regex.\n */\nexport function grepTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"grep\",\n\t\t\tdescription:\n\t\t\t\t\"Search file contents with a regex pattern. Returns matching lines with file paths and line numbers.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpattern: { type: \"string\", description: \"Regex pattern to search for\" },\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory or file to search in (default .)\" },\n\t\t\t\t\tglob: { type: \"string\", description: \"File filter glob (e.g. *.ts)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"pattern\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst rawPath = (args.path as string) || \".\"\n\t\t\t\tconst dir = resolve(cwd, rawPath)\n\t\t\t\tif (dir !== cwd && !dir.startsWith(`${cwd}/`)) {\n\t\t\t\t\tthrow new Error(`Path outside project: ${rawPath}`)\n\t\t\t\t}\n\n\t\t\t\tconst pattern = args.pattern as string\n\t\t\t\tconst globFilter = args.glob as string | undefined\n\t\t\t\tconst relSearchPath = relative(cwd, dir) || \".\"\n\n\t\t\t\t// rg is 10-100x faster than our JS fallback, but isn't always installed\n\t\t\t\ttry {\n\t\t\t\t\tconst cmd = [\"rg\", \"--line-number\", \"--max-count\", \"200\"]\n\t\t\t\t\tif (globFilter) cmd.push(`--glob=${globFilter}`)\n\t\t\t\t\tcmd.push(\"--\", pattern, relSearchPath)\n\n\t\t\t\t\tconst proc = spawn(cmd[0]!, cmd.slice(1), {\n\t\t\t\t\t\tcwd,\n\t\t\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t\t})\n\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\tproc.kill()\n\t\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t\t}\n\t\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\t\t\t\tlet stdout = \"\"\n\t\t\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\t\tstdout += chunk.toString()\n\t\t\t\t\t})\n\n\t\t\t\t\tlet exitCode: number\n\t\t\t\t\ttry {\n\t\t\t\t\t\texitCode = await new Promise<number>((resolve, reject) => {\n\t\t\t\t\t\t\tproc.on(\"error\", reject)\n\t\t\t\t\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t\t\t\t\t})\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort)\n\t\t\t\t\t}\n\n\t\t\t\t\tif (exitCode === 0) {\n\t\t\t\t\t\tconst lines = stdout.split(\"\\n\").slice(0, 200).join(\"\\n\")\n\t\t\t\t\t\treturn { content: [textPart(lines || \"No matches\")], isError: false }\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// rg not available, fall through\n\t\t\t\t}\n\n\t\t\t\t// Pure JS fallback when rg is not available\n\t\t\t\tconst files = await glob(globFilter || \"**/*\", {\n\t\t\t\t\tcwd: dir,\n\t\t\t\t\tignore: [\"**/node_modules/**\", \"**/.git/**\"],\n\t\t\t\t})\n\t\t\t\tconst prefix = relSearchPath === \".\" ? \"\" : `${relSearchPath}/`\n\t\t\t\tconst re = new RegExp(pattern, \"i\")\n\t\t\t\tconst matches: string[] = []\n\t\t\t\tfor (const file of files.slice(0, 500)) {\n\t\t\t\t\tif (signal?.aborted) break\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst content = await readFile(resolve(dir, file), \"utf-8\")\n\t\t\t\t\t\tconst lines = content.split(\"\\n\")\n\t\t\t\t\t\tfor (let i = 0; i < lines.length && matches.length < 200; i++) {\n\t\t\t\t\t\t\tconst line = lines[i]\n\t\t\t\t\t\t\tif (line && re.test(line)) matches.push(`${prefix}${file}:${i + 1}:${line}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Skip binary/unreadable files silently\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(matches.join(\"\\n\") || \"No matches\")],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Tool for listing directory entries.\n */\nexport function lsTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"ls\",\n\t\t\tdescription: \"List directory contents.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory to list (default .)\" },\n\t\t\t\t},\n\t\t\t\trequired: [],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst dir = resolve(cwd, (args.path as string) || \".\")\n\t\t\t\tconst entries = await readdir(dir, { withFileTypes: true })\n\t\t\t\tconst lines = entries.map((e) => {\n\t\t\t\t\tconst suffix = e.isDirectory() ? \"/\" : e.isSymbolicLink() ? \"@\" : \"\"\n\t\t\t\t\treturn `${e.name}${suffix}`\n\t\t\t\t})\n\t\t\t\treturn { content: [textPart(lines.join(\"\\n\") || \"(empty)\")], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Tool for visualizing a truncated directory tree.\n */\nexport function treeTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"tree\",\n\t\t\tdescription:\n\t\t\t\t\"Print a visual directory tree structure, ignoring common ignored folders like node_modules and .git.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory to start tree from (default .)\" },\n\t\t\t\t\tdepth: { type: \"number\", description: \"Maximum depth to traverse (default 3)\" },\n\t\t\t\t},\n\t\t\t\trequired: [],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst startDir = resolve(cwd, (args.path as string) || \".\")\n\t\t\t\tconst maxDepth = Number(args.depth ?? 3) || 3\n\n\t\t\t\tconst ignoreList = new Set([\n\t\t\t\t\t\".git\",\n\t\t\t\t\t\"node_modules\",\n\t\t\t\t\t\"dist\",\n\t\t\t\t\t\"build\",\n\t\t\t\t\t\".svelte-kit\",\n\t\t\t\t\t\".next\",\n\t\t\t\t\t\"out\",\n\t\t\t\t\t\".scannerwork\",\n\t\t\t\t\t\"coverage\",\n\t\t\t\t])\n\n\t\t\t\tasync function walk(dir: string, currentDepth: number, prefix: string): Promise<string> {\n\t\t\t\t\tif (currentDepth > maxDepth) return \"\"\n\t\t\t\t\tlet result = \"\"\n\n\t\t\t\t\tconst entries = await readdir(dir, { withFileTypes: true })\n\t\t\t\t\tconst sorted = entries\n\t\t\t\t\t\t.filter((e) => !ignoreList.has(e.name))\n\t\t\t\t\t\t.sort((a, b) => {\n\t\t\t\t\t\t\tif (a.isDirectory() && !b.isDirectory()) return -1\n\t\t\t\t\t\t\tif (!a.isDirectory() && b.isDirectory()) return 1\n\t\t\t\t\t\t\treturn a.name.localeCompare(b.name)\n\t\t\t\t\t\t})\n\n\t\t\t\t\tfor (let i = 0; i < sorted.length; i++) {\n\t\t\t\t\t\tconst entry = sorted[i]!\n\t\t\t\t\t\tconst isLast = i === sorted.length - 1\n\t\t\t\t\t\tconst connector = isLast ? \"└── \" : \"├── \"\n\t\t\t\t\t\tconst childPrefix = prefix + (isLast ? \" \" : \"│ \")\n\n\t\t\t\t\t\tresult += `${prefix}${connector}${entry.name}${entry.isDirectory() ? \"/\" : \"\"}\\n`\n\n\t\t\t\t\t\tif (entry.isDirectory()) {\n\t\t\t\t\t\t\tresult += await walk(resolve(dir, entry.name), currentDepth + 1, childPrefix)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn result\n\t\t\t\t}\n\n\t\t\t\tconst treeText = await walk(startDir, 1, \"\")\n\t\t\t\treturn { content: [textPart(treeText || \"(empty)\")], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Tool for executing shell commands within the project root.\n * Supports timeouts and output truncation to protect the context window.\n */\nimport { spawn } from \"node:child_process\"\nimport type { Tool, ToolResult } from \"../types.ts\"\n\nimport { textPart } from \"../util.ts\"\n\nexport function bashTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"bash\",\n\t\t\tdescription:\n\t\t\t\t\"Execute a shell command. Returns stdout and stderr. Timeout after N seconds (default 120).\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tcommand: { type: \"string\", description: \"Shell command to run\" },\n\t\t\t\t\ttimeout: { type: \"number\", description: \"Timeout in seconds (default 120)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"command\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst command = args.command as string\n\t\t\tconst timeoutMs = (Number(args.timeout) || 120) * 1000\n\n\t\t\ttry {\n\t\t\t\tconst proc = spawn(\"sh\", [\"-c\", command], { cwd, stdio: [\"ignore\", \"pipe\", \"pipe\"] })\n\n\t\t\t\tlet stdout = \"\"\n\t\t\t\tlet stderr = \"\"\n\t\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstdout += chunk.toString()\n\t\t\t\t})\n\t\t\t\tproc.stderr.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstderr += chunk.toString()\n\t\t\t\t})\n\n\t\t\t\tlet killed = false\n\t\t\t\tconst timer = setTimeout(() => {\n\t\t\t\t\tkilled = true\n\t\t\t\t\tproc.kill(\"SIGKILL\")\n\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t}, timeoutMs)\n\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tkilled = true\n\t\t\t\t\tproc.kill(\"SIGKILL\")\n\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t}\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\t\t\tlet exitCode: number\n\t\t\t\ttry {\n\t\t\t\t\texitCode = await new Promise<number>((resolve, reject) => {\n\t\t\t\t\t\tproc.on(\"error\", reject)\n\t\t\t\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t\t\t\t})\n\t\t\t\t} finally {\n\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort)\n\t\t\t\t}\n\n\t\t\t\t// Prevent context-window blowout from noisy commands\n\t\t\t\tconst MAX = 50_000\n\t\t\t\tlet out = \"\"\n\t\t\t\tif (stdout) out += stdout.slice(0, MAX)\n\t\t\t\tif (stderr) {\n\t\t\t\t\tif (out) out += \"\\n\"\n\t\t\t\t\tout += stderr.slice(0, MAX - out.length)\n\t\t\t\t}\n\t\t\t\tif (out.length >= MAX) out += `\\n…truncated`\n\n\t\t\t\tif (killed) out += `\\n[timeout after ${timeoutMs / 1000}s]`\n\t\t\t\tout += `\\n[exit ${exitCode}]`\n\n\t\t\t\treturn { content: [textPart(out)], isError: exitCode !== 0 || killed }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Web tools for searching and fetching internet content.\n * Uses DuckDuckGo HTML for search (no API key needed) and Bun's built-in\n * fetch for reading URLs.\n */\nimport type { Tool, ToolResult } from \"../types.ts\"\nimport { textPart } from \"../util.ts\"\n\nconst MAX_CONTENT = 50_000\n\n// Minimal HTML → plaintext: strips tags, decodes entities, collapses whitespace\nfunction htmlToText(html: string): string {\n\tlet text = html\n\t\t// Remove HTML comments\n\t\t.replace(/<!--[\\s\\S]*?-->/g, \"\")\n\t\t// Remove script and style blocks entirely\n\t\t.replace(/<script[\\s\\S]*?<\\/script>/gi, \"\")\n\t\t.replace(/<style[\\s\\S]*?<\\/style>/gi, \"\")\n\t\t// Keep link hrefs visible (supports single, double, or no quotes)\n\t\t.replace(/<a[^>]*href=[\"']?([^\"'>\\s]*)[\"']?[^>]*>([\\s\\S]*?)<\\/a>/gi, \"[$2]($1)\")\n\t\t// Block-level tags → newlines\n\t\t.replace(/<\\/?(p|div|br|h[1-6]|li|tr|blockquote|pre|hr)[^>]*>/gi, \"\\n\")\n\t\t// Remove remaining tags\n\t\t.replace(/<[^>]+>/g, \"\")\n\t\t// Decode common HTML entities (both named and numeric, single/double quotes)\n\t\t.replace(/&/g, \"&\")\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/"/g, '\"')\n\t\t.replace(/"/g, '\"')\n\t\t.replace(/"/g, '\"')\n\t\t.replace(/'/g, \"'\")\n\t\t.replace(/'/g, \"'\")\n\t\t.replace(/'/g, \"'\")\n\t\t.replace(/“/g, '\"')\n\t\t.replace(/”/g, '\"')\n\t\t.replace(/‘/g, \"'\")\n\t\t.replace(/’/g, \"'\")\n\t\t.replace(/ /g, \" \")\n\t\t// Collapse whitespace but keep paragraph breaks\n\t\t.replace(/[ \\t]+/g, \" \")\n\t\t.replace(/\\n{3,}/g, \"\\n\\n\")\n\t\t.trim()\n\n\tif (text.length > MAX_CONTENT) {\n\t\ttext = `${text.slice(0, MAX_CONTENT)}\\n…truncated`\n\t}\n\treturn text\n}\n\nexport function webSearchTool(): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"web_search\",\n\t\t\tdescription:\n\t\t\t\t\"Search the web using DuckDuckGo. Returns up to 10 results with titles, URLs, and snippets. Use this when you need information from the internet.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tquery: { type: \"string\", description: \"Search query\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"query\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst query = args.query as string\n\t\t\tif (!query.trim()) {\n\t\t\t\treturn { content: [textPart(\"Error: empty search query\")], isError: true }\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`\n\t\t\t\tconst resp = await fetch(url, {\n\t\t\t\t\tsignal: signal ?? undefined,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"User-Agent\":\n\t\t\t\t\t\t\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36\",\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\tif (!resp.ok) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [textPart(`Search failed: HTTP ${resp.status}`)],\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst html = await resp.text()\n\n\t\t\t\t// Split HTML into blocks by result__body to isolate each search result safely\n\t\t\t\tconst blocks: string[] = []\n\t\t\t\tconst containerRegex = /<div[^>]*class=\"[^\"]*result__body[^\"]*\"[^>]*>/gi\n\t\t\t\tconst indices: number[] = []\n\t\t\t\tfor (const match of html.matchAll(containerRegex)) {\n\t\t\t\t\tif (match.index !== undefined) {\n\t\t\t\t\t\tindices.push(match.index)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (indices.length > 0) {\n\t\t\t\t\tfor (let i = 0; i < indices.length; i++) {\n\t\t\t\t\t\tconst start = indices[i]!\n\t\t\t\t\t\tconst end = indices[i + 1] ?? html.length\n\t\t\t\t\t\tblocks.push(html.slice(start, end))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst results: string[] = []\n\t\t\t\tconst titleRegex = /<a[^>]*class=\"result__a\"[^>]*href=\"([^\"]*)\"[^>]*>([\\s\\S]*?)<\\/a>/i\n\t\t\t\tconst snippetRegex = /<a[^>]*class=\"result__snippet\"[^>]*>([\\s\\S]*?)<\\/a>/i\n\n\t\t\t\tfor (const block of blocks) {\n\t\t\t\t\tif (results.length >= 10) break\n\n\t\t\t\t\tconst titleMatch = titleRegex.exec(block)\n\t\t\t\t\tif (!titleMatch) continue\n\n\t\t\t\t\tconst rawUrl = titleMatch[1]!\n\t\t\t\t\tconst title = htmlToText(titleMatch[2]!)\n\n\t\t\t\t\tconst snippetMatch = snippetRegex.exec(block)\n\t\t\t\t\tconst snippet = snippetMatch ? htmlToText(snippetMatch[1]!) : \"\"\n\n\t\t\t\t\t// DuckDuckGo wraps URLs through a redirect; extract the actual URL\n\t\t\t\t\tlet cleanUrl = rawUrl\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Prepend protocol/host if DuckDuckGo returns a relative path or protocol-relative URL\n\t\t\t\t\t\tconst urlToParse = rawUrl.startsWith(\"//\")\n\t\t\t\t\t\t\t? `https:${rawUrl}`\n\t\t\t\t\t\t\t: rawUrl.startsWith(\"/\")\n\t\t\t\t\t\t\t\t? `https://duckduckgo.com${rawUrl}`\n\t\t\t\t\t\t\t\t: rawUrl\n\t\t\t\t\t\tconst param = new URL(urlToParse).searchParams.get(\"uddg\")\n\t\t\t\t\t\tif (param) cleanUrl = param\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Not a redirect URL, use as-is\n\t\t\t\t\t}\n\n\t\t\t\t\tresults.push(`## ${title}\\n${cleanUrl}\\n${snippet}`)\n\t\t\t\t}\n\n\t\t\t\tif (results.length === 0) {\n\t\t\t\t\treturn { content: [textPart(\"No results found.\")], isError: false }\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(results.join(\"\\n\\n\"))],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tconst msg = (e as Error).message\n\t\t\t\tif (msg.includes(\"abort\")) {\n\t\t\t\t\treturn { content: [textPart(\"Search aborted.\")], isError: true }\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Search error: ${msg}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\nexport function webFetchTool(): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"web_fetch\",\n\t\t\tdescription:\n\t\t\t\t\"Fetch and read the content of a web page. Returns the page text with HTML tags stripped. Useful for reading documentation, articles, or API references.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\turl: { type: \"string\", description: \"URL to fetch\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"url\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst url = args.url as string\n\t\t\tif (!url.trim()) {\n\t\t\t\treturn { content: [textPart(\"Error: empty URL\")], isError: true }\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tnew URL(url)\n\t\t\t} catch {\n\t\t\t\treturn { content: [textPart(`Error: invalid URL: ${url}`)], isError: true }\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst resp = await fetch(url, {\n\t\t\t\t\tsignal: signal ?? undefined,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"User-Agent\":\n\t\t\t\t\t\t\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36\",\n\t\t\t\t\t\tAccept: \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\n\t\t\t\t\t},\n\t\t\t\t\tredirect: \"follow\",\n\t\t\t\t})\n\n\t\t\t\tif (!resp.ok) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [textPart(`Fetch failed: HTTP ${resp.status} ${resp.statusText}`)],\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst contentType = (resp.headers.get(\"content-type\") ?? \"\").toLowerCase()\n\t\t\t\tconst body = await resp.text()\n\n\t\t\t\t// Sniff HTML: check content-type or if body has HTML markup signature\n\t\t\t\tconst isHtml =\n\t\t\t\t\tcontentType.includes(\"text/html\") ||\n\t\t\t\t\tcontentType.includes(\"application/xhtml+xml\") ||\n\t\t\t\t\tbody.trim().toLowerCase().startsWith(\"<!doctype html\") ||\n\t\t\t\t\tbody.trim().toLowerCase().startsWith(\"<html\")\n\n\t\t\t\tif (isHtml) {\n\t\t\t\t\tconst text = htmlToText(body)\n\t\t\t\t\treturn { content: [textPart(text)], isError: false }\n\t\t\t\t}\n\n\t\t\t\t// For plain text, JSON, etc. return as-is (truncated if needed)\n\t\t\t\tconst truncated =\n\t\t\t\t\tbody.length > MAX_CONTENT ? `${body.slice(0, MAX_CONTENT)}\\n…truncated` : body\n\t\t\t\treturn { content: [textPart(truncated)], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\tconst msg = (e as Error).message\n\t\t\t\tif (msg.includes(\"abort\")) {\n\t\t\t\t\treturn { content: [textPart(\"Fetch aborted.\")], isError: true }\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Fetch error: ${msg}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","import type { Tool } from \"../types.ts\"\nimport { editTool, readTool, writeTool } from \"./fs.ts\"\nimport { gitTool } from \"./git.ts\"\nimport { globTool, grepTool, lsTool, treeTool } from \"./search.ts\"\nimport { bashTool } from \"./shell.ts\"\nimport { webFetchTool, webSearchTool } from \"./web.ts\"\n\nexport function getAllTools(cwd: string): Tool[] {\n\treturn [\n\t\treadTool(cwd),\n\t\twriteTool(cwd),\n\t\teditTool(cwd),\n\t\tbashTool(cwd),\n\t\tglobTool(cwd),\n\t\tgrepTool(cwd),\n\t\tlsTool(cwd),\n\t\ttreeTool(cwd),\n\t\tgitTool(cwd),\n\t\twebSearchTool(),\n\t\twebFetchTool(),\n\t]\n}\n\nexport function getDefaultTools(cwd: string): Tool[] {\n\treturn [\n\t\treadTool(cwd),\n\t\twriteTool(cwd),\n\t\teditTool(cwd),\n\t\tbashTool(cwd),\n\t\twebSearchTool(),\n\t\twebFetchTool(),\n\t]\n}\n","import { spawn } from \"node:child_process\"\nimport { readFile } from \"node:fs/promises\"\nimport { dirname, join } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport semver from \"semver\"\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nlet cachedLatest: string | null = null\nlet cachedCurrent: string | null = null\n\nexport async function getCurrentVersion(): Promise<string> {\n\tif (cachedCurrent) return cachedCurrent\n\ttry {\n\t\tconst raw = await readFile(join(__dirname, \"..\", \"package.json\"), \"utf-8\")\n\t\tconst pkg = JSON.parse(raw)\n\t\tcachedCurrent = (pkg.version as string) ?? \"0.0.0\"\n\t\treturn cachedCurrent\n\t} catch {\n\t\treturn \"0.0.0\"\n\t}\n}\n\nexport async function getLatestVersion(): Promise<string | null> {\n\tif (cachedLatest) return cachedLatest\n\ttry {\n\t\tconst proc = spawn(\"npm\", [\"info\", \"novacode\", \"version\"], {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t\t})\n\t\tconst text = await new Promise<string>((resolve, reject) => {\n\t\t\tlet out = \"\"\n\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tout += chunk.toString()\n\t\t\t})\n\t\t\tproc.on(\"error\", reject)\n\t\t\tproc.on(\"close\", () => resolve(out.trim()))\n\t\t})\n\t\tif (text) {\n\t\t\tcachedLatest = text\n\t\t\treturn text\n\t\t}\n\t} catch {}\n\treturn null\n}\n\nexport async function checkForUpdate(): Promise<{\n\thasUpdate: boolean\n\tcurrent: string\n\tlatest: string\n} | null> {\n\tconst current = await getCurrentVersion()\n\tconst latest = await getLatestVersion()\n\tif (!latest) return null\n\treturn {\n\t\thasUpdate: semver.gt(latest, current),\n\t\tcurrent,\n\t\tlatest,\n\t}\n}\n\nexport async function runUpdate(silent = false): Promise<boolean> {\n\ttry {\n\t\tconst proc = spawn(\"npm\", [\"update\", \"-g\", \"novacode\"], {\n\t\t\tstdio: silent ? \"ignore\" : \"inherit\",\n\t\t})\n\t\tconst exitCode = await new Promise<number>((resolve, reject) => {\n\t\t\tproc.on(\"error\", reject)\n\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t})\n\t\tif (exitCode === 0) {\n\t\t\tif (!silent) {\n\t\t\t\tconsole.log(\"✓ novacode updated to latest version successfully.\")\n\t\t\t}\n\t\t\treturn true\n\t\t} else {\n\t\t\tif (!silent) {\n\t\t\t\tconsole.error(`Update failed (exit code ${exitCode})`)\n\t\t\t\tprocess.exit(1)\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\t} catch (e) {\n\t\tif (!silent) {\n\t\t\tconsole.error(`Update failed: ${(e as Error).message}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\t\treturn false\n\t}\n}\n","#!/usr/bin/env node\nimport { parseArgs } from \"node:util\"\n/**\n * Entry point for the nova CLI.\n * Handles configuration, CLI flags, and runs interactive TUI mode.\n */\nimport chalk from \"chalk\"\nimport { Agent } from \"./agent/agent.ts\"\nimport { buildSystemPrompt } from \"./agent/prompt.ts\"\nimport { handleSessionCommand } from \"./commands/session.ts\"\nimport { getProvider, MODELS } from \"./config/providers.ts\"\nimport { configExists, loadAuth, loadConfig } from \"./config/store.ts\"\nimport { runOnboarding } from \"./onboarding/wizard.ts\"\nimport { getSessionStore } from \"./session/store.ts\"\nimport { getAllTools } from \"./tools/index.ts\"\nimport { getCurrentVersion, runUpdate } from \"./update.ts\"\n\nfunction parseCli() {\n\tconst { values, positionals } = parseArgs({\n\t\toptions: {\n\t\t\thelp: { type: \"boolean\", short: \"h\" },\n\t\t\tversion: { type: \"boolean\", short: \"v\" },\n\t\t\tprovider: { type: \"string\" },\n\t\t\tmodel: { type: \"string\" },\n\t\t\t\"api-key\": { type: \"string\" },\n\t\t\tsession: { type: \"string\", short: \"s\" },\n\t\t},\n\t\tstrict: true,\n\t\tallowPositionals: true,\n\t})\n\n\treturn { flags: values, args: positionals }\n}\n\nfunction findModel(modelId: string, providerId?: string) {\n\treturn MODELS.find((m) => {\n\t\tif (providerId) return m.provider === providerId && m.id === modelId\n\t\treturn m.id === modelId\n\t})\n}\n\nasync function main() {\n\tconst { flags, args } = parseCli()\n\n\tif (flags.version) {\n\t\tconst version = await getCurrentVersion()\n\t\tconsole.log(`nova ${version}`)\n\t\tprocess.exit(0)\n\t}\n\n\tif (flags.help) {\n\t\tconsole.log(`nova — open-source coding agent\n\nUsage:\n nova Interactive mode\n nova update Update to latest version\n nova session <cmd> Session management (list, delete)\n nova --session <id> Resume a session\n\nOptions:\n -h, --help Show help\n -v, --version Show version\n --provider <id> Provider to use\n --model <id> Model to use\n --api-key <key> API key override\n -s, --session <id> Resume session by ID`)\n\t\tprocess.exit(0)\n\t}\n\n\t// Handle session subcommand\n\tif (args[0] === \"session\") {\n\t\tawait handleSessionCommand(args.slice(1))\n\t\treturn\n\t}\n\n\t// Handle update subcommand\n\tif (args[0] === \"update\") {\n\t\tawait runUpdate()\n\t\treturn\n\t}\n\n\t// Reject positional args — use interactive mode with / commands\n\tif (args.length > 0) {\n\t\tconsole.error(chalk.yellow(`Unknown command: ${args.join(\" \")}`))\n\t\tconsole.error(\"Run `nova --help` for usage.\")\n\t\tprocess.exit(1)\n\t}\n\n\tconst controller = new AbortController()\n\n\tconst onSignal = () => {\n\t\tcontroller.abort()\n\t\tprocess.stderr.write(\"\\nAborted.\\n\")\n\t\tprocess.exit(130)\n\t}\n\tprocess.on(\"SIGINT\", onSignal)\n\tprocess.on(\"SIGTERM\", onSignal)\n\n\t// First-run onboarding\n\tconst config = await ((await configExists()) ? loadConfig() : runOnboarding())\n\tconst auth = await loadAuth()\n\n\t// CLI overrides\n\tconst providerId = (flags.provider as string) || config.provider\n\tconst modelId = (flags.model as string) || config.model\n\tconst apiKey = (flags[\"api-key\"] as string) || auth.apiKeys[providerId]\n\n\tconst provider = getProvider(providerId)\n\tif (!provider) {\n\t\tconsole.error(`Unknown provider: ${providerId}`)\n\t\tconsole.error(`Available: ${getProvider(\"glm\") ? \"glm, \" : \"\"}gemini, deepseek, openai`)\n\t\tprocess.exit(1)\n\t}\n\n\tif (!apiKey) {\n\t\tconsole.error(\n\t\t\t`No API key for ${provider.name}. Set ${provider.envKey} or run nova for onboarding.`,\n\t\t)\n\t\tprocess.exit(1)\n\t}\n\n\tconst model = findModel(modelId, providerId)\n\tif (!model) {\n\t\tconsole.error(`Unknown model: ${modelId}`)\n\t\tconsole.error(\"Available models:\")\n\t\tfor (const m of MODELS.filter((m) => m.provider === providerId)) {\n\t\t\tconsole.error(` ${m.id} — ${m.name}`)\n\t\t}\n\t\tprocess.exit(1)\n\t}\n\n\tconst cwd = process.cwd()\n\tconst tools = getAllTools(cwd)\n\tconst system = buildSystemPrompt(cwd, tools)\n\n\t// Session persistence\n\tconst store = getSessionStore()\n\tconst session = flags.session\n\t\t? store.get(flags.session as string)\n\t\t: store.create(cwd, model.id, providerId)\n\n\tif (flags.session && !session) {\n\t\tconsole.error(`Session not found: ${flags.session}`)\n\t\tprocess.exit(1)\n\t}\n\n\tconst sessionId = session!.id\n\tconst existingMessages = store.messages(sessionId)\n\n\tconst agent = new Agent({\n\t\tapi: provider.api,\n\t\tmodel,\n\t\tapiKey,\n\t\tbaseUrl: provider.baseUrl,\n\t\tsystem,\n\t\ttools,\n\t\tmessages: existingMessages,\n\t})\n\n\t// Interactive TUI mode\n\tprocess.off(\"SIGINT\", onSignal)\n\tprocess.off(\"SIGTERM\", onSignal)\n\tconst { interactive } = await import(\"./tui/app.tsx\")\n\tawait interactive(agent, store, sessionId)\n}\n\nprocess.on(\"unhandledRejection\", (reason) => {\n\tconsole.error(\"Unhandled rejection:\", reason)\n\tprocess.exit(1)\n})\n\nmain().catch((e) => {\n\tconsole.error(\"Fatal:\", e)\n\tprocess.exit(1)\n})\n"],"mappings":";ulBA+BA,SAAS,GAAa,EAAkC,CACvD,IAAM,EAA4B,CAAC,EAEnC,IAAK,IAAM,KAAO,EACjB,GAAI,EAAI,OAAS,OAAQ,CACxB,IAAM,EACL,OAAO,EAAI,SAAY,SACpB,CAAC,CAAE,KAAM,EAAI,OAAQ,CAAC,EACtB,EAAI,QAAQ,IAAK,GACb,EAAE,OAAS,OAAe,CAAE,KAAM,EAAE,IAAK,EACzC,EAAE,OAAS,QAAgB,CAAE,YAAa,CAAE,UAAW,EAAE,KAAM,KAAM,EAAE,IAAK,CAAE,EAC3E,CAAE,KAAM,EAAG,CAClB,EACJ,EAAS,KAAK,CAAE,KAAM,OAAQ,OAAM,CAAC,CACtC,MAAO,GAAI,EAAI,OAAS,YAAa,CACpC,IAAM,EAAsB,EAAI,QAAQ,IAAK,GACxC,EAAE,OAAS,OAAe,CAAE,KAAM,EAAE,KAAM,kBAAmB,EAAE,SAAU,EACzE,EAAE,OAAS,WACP,CAAE,QAAS,GAAM,KAAM,EAAE,KAAM,kBAAmB,EAAE,SAAU,EAClE,EAAE,OAAS,YACP,CAAE,cAAe,CAAE,KAAM,EAAE,KAAM,KAAM,EAAE,IAAK,EAAG,kBAAmB,EAAE,SAAU,EACjF,CAAE,KAAM,EAAG,CAClB,EACD,EAAS,KAAK,CAAE,KAAM,QAAS,OAAM,CAAC,CACvC,MAAO,GAAI,EAAI,OAAS,cAAe,CACtC,IAAM,EAAmB,CACxB,kBAAmB,CAClB,KAAM,EAAI,KACV,SAAU,CACT,QAAS,EAAI,QACX,IAAK,GAAO,EAAE,OAAS,OAAS,EAAE,KAAO,KAAK,UAAU,CAAC,CAAE,EAC3D,KAAK;CAAI,CACZ,CACD,CACD,EAEM,EAAO,EAAS,EAAS,OAAS,GAEpC,GAAQ,EAAK,OAAS,QAAU,EAAK,MAAM,KAAM,GAAM,EAAE,iBAAiB,EAC7E,EAAK,MAAM,KAAK,CAAI,EAEpB,EAAS,KAAK,CAAE,KAAM,OAAQ,MAAO,CAAC,CAAI,CAAE,CAAC,CAE/C,CAGD,OAAO,CACR,CAEA,SAAS,GAAc,EAA6B,CAEnD,OADI,EAAM,SAAW,EAAU,CAAC,EACzB,CACN,CACC,sBAAuB,EAAM,IAAK,IAAO,CACxC,KAAM,EAAE,KACR,YAAa,EAAE,YACf,WAAY,EAAE,UACf,EAAE,CACH,CACD,CACD,CAEA,MAAa,GACZ,GAC+C,CAC/C,IAAM,EAAK,IAAI,EAmKf,OAjKE,SAAY,CACb,GAAI,CAEH,IAAM,EAAM,GADI,EAAK,SAAW,4CACT,iBAAiB,EAAK,MAAM,GAAG,qCAAqC,EAAK,SAE1F,EAAO,CACZ,SAAU,GAAa,EAAK,QAAQ,EACpC,mBAAoB,EAAK,OAAS,CAAE,MAAO,CAAC,CAAE,KAAM,EAAK,MAAO,CAAC,CAAE,EAAI,IAAA,GACvE,MAAO,EAAK,MAAM,OAAS,EAAI,GAAc,EAAK,KAAK,EAAI,IAAA,GAC3D,iBAAkB,CACjB,eAAgB,EAAK,MAAM,iBAAmB,CAAE,cAAe,KAAM,EAAI,IAAA,EAC1E,CACD,EAEM,EAAW,MAAM,MAAM,EAAK,CACjC,OAAQ,OACR,QAAS,CACR,eAAgB,mBAChB,eAAgB,YACjB,EACA,KAAM,KAAK,UAAU,CAAI,EACzB,OAAQ,EAAK,MACd,CAAC,EAED,GAAI,CAAC,EAAS,GAAI,CACjB,IAAM,EAAO,MAAM,EAAS,KAAK,EAC7B,EAAM,EACV,GAAI,CACH,IAAM,EAAO,KAAK,MAAM,CAAI,EAC5B,EAAM,EAAK,OAAO,SAAW,EAAK,SAAW,CAC9C,MAAQ,CAER,CAEA,IAAM,EAAW,iBAAiB,EAAS,OAAO,KAAK,IACvD,EAAG,KAAK,CAAE,KAAM,aAAc,KAAM,CAAS,CAAC,EAC9C,EAAG,OAAO,CACT,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,CAAS,CAAC,EAC1C,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EACvB,KAAM,OACP,CAAC,EACD,MACD,CAEA,IAAM,EAAS,EAAS,MAAM,UAAU,EACxC,GAAI,CAAC,EAAQ,CACZ,EAAG,OAAO,CAAE,QAAS,CAAC,EAAG,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EAAG,KAAM,OAAQ,CAAC,EAClE,MACD,CAEA,IAAM,EAAU,IAAI,YAChB,EAAS,GACT,EAAe,CAAE,GAAI,EAAG,IAAK,CAAE,EAC/B,EAAmB,OACjB,EAAyB,CAAC,EAEhC,OAAa,CACZ,GAAM,CAAE,OAAM,SAAU,MAAM,EAAO,KAAK,EAC1C,GAAI,EAAM,MAEV,GAAU,EAAQ,OAAO,EAAO,CAAE,OAAQ,EAAK,CAAC,EAChD,IAAM,EAAQ,EAAO,MAAM;CAAI,EAC/B,EAAS,EAAM,IAAI,GAAK,GAExB,IAAK,IAAM,KAAQ,EAAO,CACzB,IAAM,EAAU,EAAK,KAAK,EAC1B,GAAI,CAAC,GAAS,WAAW,QAAQ,EAAG,SACpC,IAAM,EAAO,EAAQ,MAAM,CAAC,EAE5B,GAAI,CACH,IAAM,EAAQ,KAAK,MAAM,CAAI,EACvB,EAAY,EAAM,aAAa,GAWrC,GARI,EAAM,gBACT,EAAQ,CACP,GAAI,EAAM,cAAc,kBAAoB,EAAM,GAClD,IAAK,EAAM,cAAc,sBAAwB,EAAM,GACxD,EACA,EAAG,KAAK,CAAE,KAAM,QAAS,OAAM,CAAC,GAG7B,CAAC,EAAW,SAGhB,GAAI,EAAU,aAAc,CAC3B,IAAM,EAAS,EAAU,aACrB,IAAW,OAAQ,EAAO,OACrB,IAAW,aAAc,EAAO,UAChC,IAAW,UAAY,IAAW,cAAgB,IAAW,WACrE,EAAO,QACT,CAEA,IAAM,EAAQ,EAAU,SAAS,MACjC,GAAI,EACH,IAAK,IAAM,KAAQ,EAAO,CACzB,IAAM,EAAM,EAAK,mBAAqB,EAAK,iBAG3C,GAAI,EAAK,KACR,GAAI,EAAK,UAAY,IAAQ,OAAO,EAAK,SAAY,SAAU,CAC9D,IAAM,EAAc,OAAO,EAAK,SAAY,SAAW,EAAK,QAAU,EAAK,KAC3E,EAAG,KAAK,CAAE,KAAM,iBAAkB,KAAM,CAAY,CAAC,EACrD,IAAM,EAAO,EAAQ,EAAQ,OAAS,GAClC,GAAM,OAAS,WAClB,EAAK,MAAQ,EAEb,EAAQ,KAAK,CAAE,KAAM,WAAY,KAAM,EAAa,UAAW,CAAI,CAAC,CAEtE,KAAO,CACN,EAAG,KAAK,CAAE,KAAM,aAAc,KAAM,EAAK,IAAK,CAAC,EAC/C,IAAM,EAAO,EAAQ,EAAQ,OAAS,GAClC,GAAM,OAAS,OAClB,EAAK,MAAQ,EAAK,KAElB,EAAQ,KAAK,CAAE,KAAM,OAAQ,KAAM,EAAK,KAAM,UAAW,CAAI,CAAC,CAEhE,CAID,IAAM,EAAK,EAAK,cAAgB,EAAK,cACrC,GAAI,EAAI,CACP,IAAM,EAAO,EAAG,KACV,EAAQ,EAAG,MAAoC,CAAC,EAGhD,EAAwB,CAC7B,KAAM,YACN,GAAA,QAJkB,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,EAAG,CAAC,IAKvD,OACA,OACA,UAAW,CACZ,EACA,EAAQ,KAAK,CAAQ,EACrB,EAAG,KAAK,CAAE,KAAM,YAAa,KAAM,CAAS,CAAC,EAC7C,EAAO,UACR,CACD,CAEF,MAAa,CACR,EAAK,KAAK,IAAM,IAAM,EAAK,KAAK,CAGrC,CACD,CACD,CAEA,EAAG,OAAO,CAAE,UAAS,QAAO,MAAK,CAAC,CACnC,OAAS,EAAG,CACX,GAAI,EAAK,QAAQ,QAAS,OAC1B,IAAM,EAAW,iCAAiC,aAAa,MAAQ,EAAE,QAAU,OAAO,CAAC,IAC3F,EAAG,KAAK,CAAE,KAAM,aAAc,KAAM,CAAS,CAAC,EAC9C,EAAG,OAAO,CACT,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,CAAS,CAAC,EAC1C,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EACvB,KAAM,OACP,CAAC,CACF,CACD,GAAG,EAEI,CACR,ECxPA,SAAS,GAAY,EAAmC,CACvD,GAAI,EAAI,OAAS,OAChB,MAAO,CACN,KAAM,OACN,QACC,OAAO,EAAI,SAAY,SACpB,EAAI,QACJ,EAAI,QAAQ,IAAK,GACb,EAAE,OAAS,OAAe,CAAE,KAAM,OAAQ,KAAM,EAAE,IAAK,EACvD,EAAE,OAAS,QACP,CAAE,KAAM,YAAa,UAAW,CAAE,IAAK,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAO,CAAE,EAC5E,CAAE,KAAM,OAAQ,KAAM,EAAG,CAChC,CACL,EAED,GAAI,EAAI,OAAS,YAAa,CAC7B,IAAM,EAAsB,CAAC,EACvB,EAAuB,CAAC,EAE9B,IAAK,IAAM,KAAK,EAAI,QACf,EAAE,OAAS,QAAQ,EAAU,KAAK,EAAE,IAAI,EAExC,EAAE,OAAS,aACd,EAAU,KAAK,CACd,KAAM,WACN,GAAI,EAAE,GACN,SAAU,CAAE,KAAM,EAAE,KAAM,UAAW,KAAK,UAAU,EAAE,IAAI,CAAE,CAC7D,CAAC,EAGH,IAAM,EAAkC,CACvC,KAAM,YACN,QAAS,EAAU,OAAS,EAAI,EAAU,KAAK,EAAE,EAAI,IACtD,EAEA,OADI,EAAU,OAAS,IAAG,EAAO,WAAa,GACvC,CACR,CASA,OAPI,EAAI,OAAS,cACT,CACN,KAAM,OACN,aAAc,EAAI,OAClB,QAAS,EAAI,QAAQ,IAAK,GAAO,EAAE,OAAS,OAAS,EAAE,KAAO,KAAK,UAAU,CAAC,CAAE,EAAE,KAAK;CAAI,CAC5F,EAEM,CAAE,KAAM,OAAQ,QAAS,EAAG,CACpC,CAEA,SAAS,GAAc,EAA6B,CACnD,OAAO,EAAM,IAAK,IAAO,CACxB,KAAM,WACN,SAAU,CACT,KAAM,EAAE,KACR,YAAa,EAAE,YACf,WAAY,EAAE,UACf,CACD,EAAE,CACH,CAEA,MAAa,GACZ,GAC+C,CAC/C,IAAM,EAAK,IAAI,EA2If,OAzIE,SAAY,CACb,GAAI,CACH,IAAM,EAAO,CACZ,MAAO,EAAK,MAAM,GAClB,SAAU,CAAC,CAAE,KAAM,SAAU,QAAS,EAAK,MAAO,EAAG,GAAG,EAAK,SAAS,IAAI,EAAW,CAAC,EACtF,MAAO,EAAK,MAAM,OAAS,EAAI,GAAc,EAAK,KAAK,EAAI,IAAA,GAC3D,OAAQ,EACT,EAEM,EAAW,MAAM,MAAM,GAAG,EAAK,QAAQ,mBAAoB,CAChE,OAAQ,OACR,QAAS,CACR,eAAgB,mBAChB,cAAe,UAAU,EAAK,QAC/B,EACA,KAAM,KAAK,UAAU,CAAI,EACzB,OAAQ,EAAK,MACd,CAAC,EAED,GAAI,CAAC,EAAS,GAAI,CACjB,IAAM,EAAO,MAAM,EAAS,KAAK,EAC3B,EAAW,aAAa,EAAS,OAAO,IAAI,IAClD,EAAG,KAAK,CAAE,KAAM,aAAc,KAAM,CAAS,CAAC,EAC9C,EAAG,OAAO,CACT,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,CAAS,CAAC,EAC1C,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EACvB,KAAM,OACP,CAAC,EACD,MACD,CAEA,IAAM,EAAS,EAAS,MAAM,UAAU,EACxC,GAAI,CAAC,EAAQ,CACZ,EAAG,OAAO,CAAE,QAAS,CAAC,EAAG,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EAAG,KAAM,OAAQ,CAAC,EAClE,MACD,CAEA,IAAM,EAAU,IAAI,YAChB,EAAS,GACP,EAAmB,IAAI,IACzB,EAAe,CAAE,GAAI,EAAG,IAAK,CAAE,EAC/B,EAAc,GACd,EAAO,OAEX,OAAa,CACZ,GAAM,CAAE,OAAM,SAAU,MAAM,EAAO,KAAK,EAC1C,GAAI,EAAM,MAEV,GAAU,EAAQ,OAAO,EAAO,CAAE,OAAQ,EAAK,CAAC,EAChD,IAAM,EAAQ,EAAO,MAAM;CAAI,EAC/B,EAAS,EAAM,IAAI,GAAK,GAExB,IAAK,IAAM,KAAQ,EAAO,CACzB,IAAM,EAAU,EAAK,KAAK,EAC1B,GAAI,CAAC,GAAS,WAAW,QAAQ,EAAG,SACpC,IAAM,EAAO,EAAQ,MAAM,CAAC,EACxB,OAAS,SAEb,GAAI,CACH,IAAM,EAAQ,KAAK,MAAM,CAAI,EACvB,EAAQ,EAAM,UAAU,IAAI,MAClC,GAAI,CAAC,EAAO,SAOZ,GALI,EAAM,UACT,EAAG,KAAK,CAAE,KAAM,aAAc,KAAM,EAAM,OAAQ,CAAC,EACnD,GAAe,EAAM,SAGlB,EAAM,WACT,IAAK,IAAM,KAAM,EAAM,WAAY,CAClC,IAAM,EAAM,EAAG,OAAS,EACnB,EAAiB,IAAI,CAAG,GAC5B,EAAiB,IAAI,EAAK,CACzB,GAAI,EAAG,IAAM,GACb,KAAM,EAAG,UAAU,MAAQ,GAC3B,KAAM,EACP,CAAC,EAEF,IAAM,EAAW,EAAiB,IAAI,CAAG,EACrC,EAAG,KAAI,EAAS,GAAK,EAAG,IACxB,EAAG,UAAU,OAAM,EAAS,KAAO,EAAG,SAAS,MAC/C,EAAG,UAAU,YAAW,EAAS,MAAQ,EAAG,SAAS,UAC1D,CAGG,EAAM,QACT,EAAQ,CACP,GAAI,EAAM,MAAM,eAAiB,EACjC,IAAK,EAAM,MAAM,mBAAqB,CACvC,EACA,EAAG,KAAK,CAAE,KAAM,QAAS,OAAM,CAAC,GAGjC,IAAM,EAAe,EAAM,UAAU,IAAI,cACrC,IAAc,EAAO,EAC1B,MAAQ,CAER,CACD,CACD,CAEA,IAAM,EAAsC,CAAC,EACzC,GACH,EAAQ,KAAK,CAAE,KAAM,OAAQ,KAAM,CAAY,CAAC,EAEjD,IAAK,GAAM,EAAG,KAAO,EACpB,EAAQ,KAAK,CACZ,KAAM,YACN,GAAI,EAAG,GACP,KAAM,EAAG,KACT,KAAM,KAAK,MAAM,EAAG,MAAQ,IAAI,CACjC,CAAC,EACD,EAAG,KAAK,CACP,KAAM,YACN,KAAM,CACL,KAAM,YACN,GAAI,EAAG,GACP,KAAM,EAAG,KACT,KAAM,KAAK,MAAM,EAAG,MAAQ,IAAI,CACjC,CACD,CAAC,EACD,EAAO,WAGR,EAAG,OAAO,CAAE,UAAS,QAAa,MAAmB,CAAC,CACvD,OAAS,EAAG,CACX,GAAI,EAAK,QAAQ,QAAS,OAC1B,IAAM,EAAW,qBAAqB,aAAa,MAAQ,EAAE,QAAU,OAAO,CAAC,IAC/E,EAAG,KAAK,CAAE,KAAM,aAAc,KAAM,CAAS,CAAC,EAC9C,EAAG,OAAO,CACT,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,CAAS,CAAC,EAC1C,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EACvB,KAAM,OACP,CAAC,CACF,CACD,GAAG,EAEI,CACR,ECzMA,IAAa,EAAb,KAA+B,CAC9B,GAAe,CAAC,EAChB,GAAQ,GACR,GACA,GACA,GACA,GAAS,GAET,KAAK,EAAgB,CAChB,SAAKA,GAET,GAAI,KAAKC,GAAU,CAClB,IAAM,EAAU,KAAKA,GACrB,KAAKA,GAAW,IAAA,GAChB,EAAQ,CAAK,CACd,MACC,KAAKC,GAAQ,KAAK,CAAK,CAEzB,CAEA,OAAO,EAAiB,CACvB,KAAKC,GAAQ,GACb,KAAKC,GAAU,EAEX,KAAKH,IAER,KAAKA,GAAS,IAAA,EAAc,EAEzB,KAAKI,IACR,KAAKA,GAAa,CAAM,CAE1B,CAEA,OAAc,CACb,KAAKL,GAAS,GACd,KAAKG,GAAQ,GACT,KAAKF,IACR,KAAKA,GAAS,IAAA,EAAc,EAEzB,KAAKI,IACR,KAAKA,GAAa,IAAA,EAAc,CAElC,CAEA,OAAQ,OAAO,gBAAoC,CAClD,KAAO,CAAC,KAAKF,IAAS,KAAKD,GAAQ,OAAS,GAAG,CAC9C,GAAI,KAAKA,GAAQ,OAAS,EAAG,CAC5B,MAAM,KAAKA,GAAQ,MAAM,EACzB,QACD,CACA,GAAI,KAAKC,GAAO,MAChB,IAAM,EAAO,MAAM,IAAI,QAAwB,GAAY,CAC1D,KAAKF,GAAW,CACjB,CAAC,EACG,IAAS,IAAA,IAAa,KAAKC,GAAQ,SAAW,EACjD,MAAM,EACI,KAAKA,GAAQ,OAAS,IAChC,MAAM,KAAKA,GAAQ,MAAM,EAE3B,CACD,CAEA,IAAI,QAAwB,CAC3B,OAAO,KAAKE,EACb,CAEA,IAAI,QAAkB,CACrB,OAAO,KAAKD,EACb,CACD,EAGA,MAAM,GAAW,IAAI,IAAyB,CAC7C,CAAC,SAAU,EAAY,EACvB,CAAC,SAAU,EAAY,CACxB,CAAC,EAOD,SAAgB,EAAO,EAA4D,CAClF,IAAM,EAAK,GAAS,IAAI,EAAK,GAAG,EAChC,GAAI,CAAC,EAAI,MAAU,MAAM,0CAA0C,EAAK,KAAK,EAI7E,IAAM,EAAiB,EAAG,CAAI,EACxB,EAAc,IAAI,EAgCxB,OA9BE,SAAY,CACb,UAAW,IAAM,KAAS,EACrB,EAAM,OAAS,aAClB,EAAY,KAAK,CAAE,KAAM,aAAc,KAAM,EAAM,MAAQ,EAAG,CAAC,EACrD,EAAM,OAAS,iBACzB,EAAY,KAAK,CAAE,KAAM,iBAAkB,KAAM,EAAM,MAAQ,EAAG,CAAC,EACzD,EAAM,OAAS,aAAe,EAAM,KAC9C,EAAY,KAAK,CAChB,KAAM,YACN,KAAM,CACL,KAAM,YACN,GAAI,EAAM,KAAK,GACf,KAAM,EAAM,KAAK,KACjB,KAAM,EAAM,KAAK,IAClB,CACD,CAAC,EACS,EAAM,OAAS,SAAW,EAAM,OAC1C,EAAY,KAAK,CAAE,KAAM,QAAS,MAAO,EAAM,KAAM,CAAC,EAIxD,IAAM,EAAM,EAAe,OACvB,EACH,EAAY,OAAO,CAAG,EAGtB,EAAY,OAAO,CAAE,QAAS,CAAC,EAAG,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EAAG,KAAM,MAAO,CAAC,CAE5E,GAAG,EAEI,CACR,CClIA,SAAgB,EAAe,EAAyB,CACvD,IAAI,EAAQ,EACZ,IAAK,IAAM,KAAO,EACjB,GAAI,OAAO,EAAI,SAAY,SAC1B,GAAS,EAAI,QAAQ,YACf,GAAI,MAAM,QAAQ,EAAI,OAAO,MAC9B,IAAM,KAAQ,EAAI,QAClB,EAAK,OAAS,SAAQ,GAAS,EAAK,KAAK,QAIhD,OAAO,KAAK,KAAK,EAAQ,CAAC,CAC3B,CAEA,SAAgB,EAAS,EAAqB,CAC7C,MAAO,CAAE,KAAM,OAAQ,KAAM,CAAE,CAChC,CAEA,SAAgB,EAAoB,EAAa,EAA0B,CAI1E,OAHI,IAAa,GAAO,EAAS,WAAW,GAAG,EAAI,EAAE,EAC7C,EAAS,EAAK,CAAQ,GAAK,IAE5B,CACR,CAEA,SAAgB,GAAa,EAAqB,CACjD,GAAI,OAAO,GAAQ,SAAU,OAAO,EAEpC,IAAI,EAAU,EACV,EAAS,GAMb,GALI,EAAI,WAAW,SAAS,IAC3B,EAAU,EAAI,MAAM,CAAC,EACrB,EAAS,WAGN,EAAW,CAAO,EAAG,CACxB,IAAM,EAAM,QAAQ,IAAI,EACxB,OAAO,EAAS,EAAoB,EAAK,CAAO,CACjD,CACA,OAAO,CACR,CAEA,SAAgB,GACf,EACA,EAAW,GACF,CAET,OADK,EACE,OAAO,QAAQ,CAAI,EACxB,KAAK,CAAC,EAAG,KAAO,CAChB,IAAM,EAAM,OAAO,GAAM,SAAW,GAAa,CAAC,EAAI,KAAK,UAAU,CAAC,EAChE,EAAS,EAAI,OAAS,GAAK,GAAG,EAAI,MAAM,EAAG,EAAE,EAAE,GAAK,EAE1D,MAAO,GADQ,EAAW,EAAM,IAAI,GAAG,EAAE,EAAE,EAAI,GAAG,EAAE,GACnC,GAAG,GACrB,CAAC,EACA,KAAK,GAAG,EARQ,EASnB,CC3CA,MAEM,GAAc,GACnB,OAAO,GAAM,YAAY,GAAe,EAAmB,OAAS,YAKrE,SAAgB,GACf,EACA,EACA,EACA,EACiC,CACjC,IAAM,EAAK,IAAI,EACT,EAAa,CAAC,EACd,EAAW,EAAK,UAAY,GAE5B,EAAe,CAAE,KAAM,OAAQ,QAAS,EAAO,GAAI,KAAK,IAAI,CAAE,EAChE,EAAqB,CAAE,GAAG,EAAK,SAAU,CAAC,GAAG,EAAI,SAAU,CAAO,CAAE,EAsHxE,OArHA,EAAI,KAAK,CAAO,GAoHhB,SAlHyB,CACxB,EAAG,KAAK,CAAE,KAAM,OAAQ,CAAC,EAEzB,GAAI,CACH,IAAI,EAAQ,EACZ,KAAO,EAAQ,GACV,IAAQ,SADY,CAGxB,IACA,EAAG,KAAK,CAAE,KAAM,MAAO,CAAC,EAGxB,IAAM,EAAe,EAAe,EAAU,QAAQ,EAClD,EAAe,EAAK,MAAM,cAAgB,IAC7C,EAAG,KAAK,CACP,KAAM,aACN,KAAM,yCAAyC,KAAK,MAAM,EAAe,GAAI,EAAE,MAAM,KAAK,MAAM,EAAK,MAAM,cAAgB,GAAI,EAAE,UAClI,CAAC,EAGF,IAAM,EAAQ,MAAM,GAAS,EAAW,EAAM,EAAI,CAAM,EAKxD,GAJA,EAAI,KAAK,CAAK,EACd,EAAY,CAAE,GAAG,EAAW,SAAU,CAAC,GAAG,EAAU,SAAU,CAAK,CAAE,EACrE,EAAG,KAAK,CAAE,KAAM,gBAAiB,IAAK,CAAM,CAAC,EAEzC,EAAM,OAAS,SAAW,EAAM,OAAS,UAAW,CACvD,EAAG,KAAK,CAAE,KAAM,WAAY,IAAK,EAAO,QAAS,CAAC,CAAE,CAAC,EACrD,KACD,CAEA,IAAM,EAAQ,EAAM,QAAQ,OAAO,EAAU,EAC7C,GAAI,EAAM,SAAW,EAAG,CACvB,EAAG,KAAK,CAAE,KAAM,WAAY,IAAK,EAAO,QAAS,CAAC,CAAE,CAAC,EACrD,KACD,CAGA,IAAM,EAA2B,CAAC,EAClC,IAAK,IAAM,KAAQ,EAAO,CACzB,GAAI,GAAQ,QAAS,MAErB,IAAM,EAAO,EAAU,MAAM,KAAM,GAAM,EAAE,IAAI,OAAS,EAAK,IAAI,EACjE,GAAI,CAAC,EAAM,CACV,IAAM,EAA2B,CAChC,KAAM,cACN,OAAQ,EAAK,GACb,KAAM,EAAK,KACX,QAAS,CAAC,EAAS,iBAAiB,EAAK,MAAM,CAAC,EAChD,QAAS,GACT,GAAI,KAAK,IAAI,CACd,EACA,EAAQ,KAAK,CAAS,EACtB,EAAY,CAAE,GAAG,EAAW,SAAU,CAAC,GAAG,EAAU,SAAU,CAAS,CAAE,EACzE,EAAI,KAAK,CAAS,EAClB,QACD,CAGA,IAAM,EAAU,MAAM,EAAK,aAAa,EAAM,EAAK,KAAM,CAAS,EAClE,GAAI,GAAS,MAAO,CACnB,IAAM,EAA6B,CAClC,KAAM,cACN,OAAQ,EAAK,GACb,KAAM,EAAK,KACX,QAAS,CAAC,EAAS,EAAQ,QAAU,SAAS,CAAC,EAC/C,QAAS,GACT,GAAI,KAAK,IAAI,CACd,EACA,EAAQ,KAAK,CAAW,EACxB,EAAY,CAAE,GAAG,EAAW,SAAU,CAAC,GAAG,EAAU,SAAU,CAAW,CAAE,EAC3E,EAAI,KAAK,CAAW,EACpB,QACD,CAGA,IAAM,EAAS,MAAM,EAAK,QAAQ,EAAK,KAAM,CAAM,EAC7C,EAAyB,CAC9B,KAAM,cACN,OAAQ,EAAK,GACb,KAAM,EAAK,KACX,KAAM,EAAK,KACX,QAAS,EAAO,QAChB,QAAS,EAAO,QAChB,GAAI,KAAK,IAAI,CACd,EAEA,EAAQ,KAAK,CAAO,EACpB,EAAY,CAAE,GAAG,EAAW,SAAU,CAAC,GAAG,EAAU,SAAU,CAAO,CAAE,EACvE,EAAI,KAAK,CAAO,EAChB,EAAG,KAAK,CAAE,KAAM,cAAe,OAAQ,EAAK,GAAI,OAAQ,EAAS,KAAM,EAAK,IAAK,CAAC,EAElF,MAAM,EAAK,YAAY,EAAM,EAAS,CAAS,CAChD,CAEA,EAAG,KAAK,CAAE,KAAM,WAAY,IAAK,EAAO,SAAQ,CAAC,CAClD,CAEI,GAAS,GACZ,EAAG,KAAK,CACP,KAAM,aACN,KAAM,uBAAuB,EAAS,GACvC,CAAC,CAEH,OAAS,EAAG,CACX,GAAK,EAAY,OAAS,aAAc,CACvC,EAAG,OAAO,CAAG,EACb,MACD,CACA,MAAM,CACP,CAEA,EAAG,OAAO,CAAG,CACd,GAEK,EACE,CACR,CAEA,eAAe,GACd,EACA,EACA,EACA,EACwB,CACxB,IAAM,EAAiB,EAAO,CAC7B,IAAK,EAAK,IACV,MAAO,EAAK,MACZ,OAAQ,EAAK,OACb,QAAS,EAAK,QACd,OAAQ,EAAI,OACZ,SAAU,EAAI,SACd,MAAO,EAAI,MAAM,IAAK,GAAM,EAAE,GAAG,EACjC,QACD,CAAC,EAEK,EAAmC,CAAC,EACtC,EAAQ,CAAE,GAAI,EAAG,IAAK,CAAE,EAG5B,UAAW,IAAM,KAAM,EACtB,GAAI,EAAG,OAAS,cAAgB,EAAG,KAAM,CACxC,IAAM,EAAO,EAAQ,EAAQ,OAAS,GAClC,GAAM,OAAS,OAClB,EAAK,MAAQ,EAAG,KAEhB,EAAQ,KAAK,EAAS,EAAG,IAAI,CAAC,EAE/B,EAAG,KAAK,CAAE,KAAM,aAAc,KAAM,EAAG,IAAK,CAAC,CAC9C,MAAO,GAAI,EAAG,OAAS,kBAAoB,EAAG,KAAM,CACnD,IAAM,EAAO,EAAQ,EAAQ,OAAS,GAClC,GAAM,OAAS,WAClB,EAAK,MAAQ,EAAG,KAEhB,EAAQ,KAAK,CAAE,KAAM,WAAY,KAAM,EAAG,IAAK,CAAC,EAEjD,EAAG,KAAK,CAAE,KAAM,iBAAkB,KAAM,EAAG,IAAK,CAAC,CAClD,MAAW,EAAG,OAAS,aAAe,EAAG,MACxC,EAAQ,KAAK,EAAG,IAAI,EACpB,EAAG,KAAK,CAAE,KAAM,YAAa,KAAM,EAAG,IAAK,CAAC,GAClC,EAAG,OAAS,SAAW,EAAG,QACpC,EAAQ,EAAG,MACX,EAAG,KAAK,CAAE,KAAM,QAAS,OAAM,CAAC,GAIlC,IAAM,EACL,EAAe,QAAQ,SAAW,EAAe,OAAO,QAAQ,OAAS,EACtE,EAAe,OAAO,QACtB,EAGE,EADU,EAAW,KAAM,GAAM,EAAE,OAAS,WACrB,EAC1B,EAAW,OAAQ,GAAM,EAAE,OAAS,QAAU,EAAE,KAAK,KAAK,EAAE,OAAS,CAAC,EACtE,EAEG,EAAM,EAAe,OAa3B,OAZI,EACI,CACN,KAAM,YACN,QAAS,EACT,MAAO,EAAK,MAAM,GAClB,SAAU,EAAK,MAAM,SACrB,MAAO,EAAI,MACX,KAAM,EAAI,KACV,GAAI,KAAK,IAAI,CACd,EAGM,CACN,KAAM,YACN,QAAS,EACT,MAAO,EAAK,MAAM,GAClB,SAAU,EAAK,MAAM,SACrB,QACA,KAAM,EAAe,KAAM,GAAM,EAAE,OAAS,WAAW,EAAI,WAAa,OACxE,GAAI,KAAK,IAAI,CACd,CACD,CCxOA,IAAa,GAAb,KAAmB,CAClB,GACA,GACA,GACA,GAAmB,CAAC,EACpB,GACA,GACA,GAEA,YAAY,EAQT,CACF,KAAKG,GAAO,EAAK,IACjB,KAAKC,GAAS,EAAK,MACnB,KAAKC,GAAU,EAAK,OACpB,KAAKC,GAAW,EAAK,QACrB,KAAKC,GAAU,EAAK,OACpB,KAAKC,GAAS,EAAK,MACnB,KAAKC,GAAY,EAAK,UAAY,CAAC,CACpC,CAEA,IAAI,OAAe,CAClB,OAAO,KAAKL,EACb,CAEA,IAAI,UAAkB,CACrB,OAAO,KAAKK,EACb,CAEA,IAAI,OAAgB,CACnB,OAAO,KAAKD,EACb,CAEA,IAAI,QAAiB,CACpB,OAAO,KAAKH,EACb,CAEA,IAAI,SAAkB,CACrB,OAAO,KAAKC,EACb,CAEA,aAAa,EAA+E,CAC3F,KAAKH,GAAO,EAAK,IACjB,KAAKC,GAAS,EAAK,MACnB,KAAKC,GAAU,EAAK,OACpB,KAAKC,GAAW,EAAK,OACtB,CAEA,SAAS,EAAqB,CAC7B,KAAKE,GAAS,CACf,CAEA,YAAY,EAAmB,CAC9B,KAAKC,GAAY,CAClB,CAEA,SAAS,EAAoB,CAC5B,KAAKL,GAAS,CACf,CAEA,OAAO,EAAe,EAAsD,CAc3E,OAAO,GAAI,EAAO,CAZjB,OAAQ,KAAKG,GACb,SAAU,KAAKE,GACf,MAAO,KAAKD,EAUO,EAAG,CANtB,IAAK,KAAKL,GACV,MAAO,KAAKC,GACZ,OAAQ,KAAKC,GACb,QAAS,KAAKC,EAGW,EAAG,CAAM,CACpC,CACD,EC/EA,SAAgB,GAAkB,EAAa,EAAuB,CACrE,IAAM,EAAW,EAAM,IAAK,GAAM,KAAK,EAAE,IAAI,KAAK,IAAI,EAAE,IAAI,aAAa,EAAE,KAAK;CAAI,EAC9E,EAAW,EAAG,SAAS,EACvB,EAAO,EAAG,KAAK,EAIrB,MAAO;;;;;;EAMN,EAAS;;;;uBAIY,EAAI;sBACL,EAAS,IAdd,EAAG,QAcqB,EAAE;kBACzB,EAAK;WAdR,QAAQ,IAAI,OAAS,UAenB;UACP,IAAI,KAAK,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;6CAqBjD,CCZA,SAAS,IAAqB,CAC7B,MAAO,GAAG,KAAK,IAAI,EAAE,SAAS,EAAE,EAAE,GAAG,OAAO,WAAW,EAAE,MAAM,EAAG,CAAC,GACpE,CAEA,IAAa,GAAb,KAA0B,CACzB,GAEA,YAAY,EAAgB,CAC3B,KAAKI,GAAM,IAAI,EAAc,CAAM,EACnC,KAAKA,GAAI,OAAO,oBAAoB,EACpC,KAAKA,GAAI,OAAO,mBAAmB,EACnC,KAAKA,GAAI,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAM,CACrB,CAEA,OAAO,EAAa,EAAe,EAA2B,CAC7D,IAAM,EAAK,GAAW,EAChB,EAAM,KAAK,IAAI,EAcrB,OAbA,KAAKA,GACH,QACA,4IACD,EACC,IAAI,CACA,KACC,MACE,QACG,WACV,MAAO,KACP,QAAS,EACT,QAAS,CACV,CAAC,EACK,CAAE,KAAI,MAAK,QAAO,WAAU,MAAO,KAAM,QAAS,EAAK,QAAS,CAAI,CAC5E,CAEA,IAAI,EAA4B,CAC/B,OACE,KAAKA,GACJ,QACA,uFACD,EACC,IAAI,CAAM,IAAG,CAAC,GAAwB,IAE1C,CAEA,KAAK,EAAQ,GAAe,CAC3B,OAAO,KAAKA,GACV,QACA,2GACD,EACC,IAAI,CAAS,OAAM,CAAC,CACvB,CAEA,OAAO,EAAqB,CAE3B,OADe,KAAKA,GAAI,QAAQ,qCAAqC,EAAE,IAAI,CAAM,IAAG,CACxE,EAAE,QAAU,CACzB,CAEA,OAAO,EAAmB,EAAgB,CACzC,IAAM,EAAM,KAAKC,GAAS,CAAS,EACnC,KAAKD,GACH,QACA,qGACD,EACC,IAAI,CACJ,IAAK,EACA,MACL,KAAM,EAAI,KACV,QAAS,KAAK,UAAU,CAAG,EAC3B,GAAI,EAAI,EACT,CAAC,EACF,KAAKA,GACH,QAAQ,mDAAmD,EAC3D,IAAI,CAAE,IAAK,KAAK,IAAI,EAAG,GAAI,CAAU,CAAC,CACzC,CAEA,WAAW,EAAmB,EAAmB,CAMhD,KALgBA,GAAI,gBAAkB,CACrC,IAAK,IAAM,KAAO,EACjB,KAAK,OAAO,EAAW,CAAG,CAE5B,CACC,EAAE,CACJ,CAEA,SAAS,EAA0B,CAIlC,OAHa,KAAKA,GAChB,QAAQ,uEAAuE,EAC/E,IAAI,CAAE,IAAK,CAAU,CACb,EAAE,IAAK,GAAM,KAAK,MAAM,EAAE,OAAO,CAAQ,CACpD,CAEA,cAAc,EAAmB,EAAyB,CAMzD,OALa,KAAKA,GAChB,QACA,sFACD,EACC,IAAI,CAAE,IAAK,EAAW,IAAK,CAAS,CAC5B,EAAE,IAAK,GAAM,KAAK,MAAM,EAAE,OAAO,CAAQ,CACpD,CAEA,SAAS,EAAmB,EAAqB,CAChD,KAAKA,GACH,QAAQ,mDAAmD,EAC3D,IAAI,CAAS,QAAO,GAAI,CAAU,CAAC,CACtC,CAEA,aAAa,EAA2B,CAIvC,OAHY,KAAKA,GACf,QAAQ,gEAAgE,EACxE,IAAI,CAAE,IAAK,CAAU,CACd,EAAE,KACZ,CAEA,eACC,EACA,EACA,EACA,EACA,EACO,CACP,KAAKA,GACH,QACA,0IACD,EACC,IAAI,CACJ,IAAK,EACI,UACT,KAAM,KAAK,UAAU,CAAS,EAC9B,MAAO,KAAK,UAAU,CAAU,EAChC,IAAK,EACL,GAAI,KAAK,IAAI,CACd,CAAC,CACH,CAEA,oBAAoB,EAAkE,CACrF,OACE,KAAKA,GACJ,QACA,8FACD,EACC,IAAI,CAAE,IAAK,CAAU,CAAC,GAAuD,IAEjF,CAEA,kBAAkB,EAAmB,EAAmB,CACvD,KAAKA,GACH,QAAQ,6DAA6D,EACrE,IAAI,CAAE,IAAK,EAAgB,KAAI,CAAC,CACnC,CAEA,OAAc,CACb,KAAKA,GAAI,MAAM,CAChB,CAEA,GAAS,EAA2B,CAInC,OAHY,KAAKA,GACf,QAAQ,iEAAiE,EACzE,IAAI,CAAE,IAAK,CAAU,CACb,EAAE,QAAU,GAAK,CAC5B,CACD,EAEA,IAAI,EAA8B,KAElC,SAAgB,EAAgB,EAA4B,CAI3D,OAHI,IAEJ,EAAS,IAAI,GADE,EAAK,GAAO,EAAK,QAAQ,IAAI,MAAQ,IAAK,WAAW,EAAG,aACxC,CAAC,EACzB,EACR,CC3MA,eAAsB,GAAqB,EAA+B,CACzE,IAAM,EAAQ,EAAgB,EACxB,CAAC,EAAY,GAAM,EAEzB,GAAI,IAAe,QAAU,IAAe,KAAM,CACjD,IAAM,EAAW,EAAM,KAAK,EAC5B,GAAI,EAAS,SAAW,EAAG,CAC1B,QAAQ,IAAI,oBAAoB,EAChC,MACD,CAEA,QAAQ,IAAI,KAAK,OAAO,EAAE,EAAG,QAAQ,OAAO,EAAE,EAAG,SAAS,EAC1D,QAAQ,IAAI,IAAI,OAAO,EAAE,CAAC,EAC1B,IAAK,IAAM,KAAK,EAAU,CACzB,IAAM,EAAO,IAAI,KAAK,EAAE,OAAO,EAAE,eAAe,EAChD,QAAQ,IAAI,EAAE,GAAG,OAAO,EAAE,EAAG,EAAE,MAAM,OAAO,EAAE,EAAG,CAAI,CACtD,CACA,MACD,CAEA,GAAI,IAAe,UAAY,IAAe,KAAM,CAC9C,IACJ,QAAQ,MAAM,qCAAqC,EACnD,QAAQ,KAAK,CAAC,GAEC,EAAM,OAAO,CACnB,EACT,QAAQ,IAAI,oBAAoB,GAAI,GAEpC,QAAQ,MAAM,sBAAsB,GAAI,EACxC,QAAQ,KAAK,CAAC,GAEf,MACD,CAEA,QAAQ,MAAM,qDAAqD,EACnE,QAAQ,KAAK,CAAC,CACf,CCrCA,MAAa,EAA2B,CACvC,CACC,GAAI,MACJ,KAAM,aACN,IAAK,SACL,QAAS,sCACT,OAAQ,aACT,EACA,CACC,GAAI,SACJ,KAAM,kBACN,IAAK,SACL,QAAS,4CACT,OAAQ,gBACT,EACA,CACC,GAAI,WACJ,KAAM,WACN,IAAK,SACL,QAAS,2BACT,OAAQ,kBACT,EACA,CACC,GAAI,SACJ,KAAM,SACN,IAAK,SACL,QAAS,4BACT,OAAQ,gBACT,CACD,EAEa,EAAkB,CAE9B,CACC,GAAI,UACJ,KAAM,UACN,SAAU,MACV,cAAe,MACf,UAAW,KACX,iBAAkB,EACnB,EACA,CACC,GAAI,QACJ,KAAM,QACN,SAAU,MACV,cAAe,MACf,UAAW,KACX,iBAAkB,EACnB,EACA,CACC,GAAI,cACJ,KAAM,cACN,SAAU,MACV,cAAe,MACf,UAAW,KACX,iBAAkB,EACnB,EACA,CACC,GAAI,UACJ,KAAM,UACN,SAAU,MACV,cAAe,MACf,UAAW,KACX,iBAAkB,EACnB,EACA,CACC,GAAI,gBACJ,KAAM,uBACN,SAAU,MACV,cAAe,MACf,UAAW,KACX,iBAAkB,EACnB,EACA,CACC,GAAI,gBACJ,KAAM,uBACN,SAAU,MACV,cAAe,MACf,UAAW,KACX,iBAAkB,EACnB,EAEA,CACC,GAAI,mBACJ,KAAM,mBACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,yBACJ,KAAM,yBACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,qCACJ,KAAM,gCACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,wBACJ,KAAM,wBACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,gCACJ,KAAM,gCACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,yBACJ,KAAM,yBACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,iBACJ,KAAM,iBACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,mBACJ,KAAM,mBACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,wBACJ,KAAM,wBACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,0CACJ,KAAM,0BACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EAEA,CACC,GAAI,gBACJ,KAAM,cACN,SAAU,WACV,cAAe,KACf,UAAW,KACX,iBAAkB,EACnB,EACA,CACC,GAAI,oBACJ,KAAM,cACN,SAAU,WACV,cAAe,KACf,UAAW,KACX,iBAAkB,EACnB,EAEA,CACC,GAAI,SACJ,KAAM,SACN,SAAU,SACV,cAAe,MACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,UACJ,KAAM,UACN,SAAU,SACV,cAAe,IACf,UAAW,IACX,iBAAkB,EACnB,CACD,EAEA,SAAgB,EAAY,EAAqC,CAChE,OAAO,EAAU,KAAM,GAAM,EAAE,KAAO,CAAE,CACzC,CAEA,SAAgB,GAAqB,EAA6B,CACjE,OAAO,EAAO,OAAQ,GAAM,EAAE,WAAa,CAAU,CACtD,CC1MA,MAAM,MAAiB,EAAK,QAAQ,IAAI,MAAQ,IAAK,WAAW,EAC1D,MAAoB,EAAK,EAAS,EAAG,aAAa,EAClD,MAAkB,EAAK,EAAS,EAAG,WAAW,EAE9C,EAA4B,CACjC,SAAU,GACV,MAAO,EACR,EAEM,EAAwB,CAC7B,QAAS,CAAC,CACX,EAEA,eAAsB,IAAiC,CACtD,GAAI,CAEH,OADA,MAAM,EAAK,EAAY,CAAC,EACjB,EACR,MAAQ,CACP,MAAO,EACR,CACD,CAEA,eAAsB,GAAkC,CACvD,GAAI,CACH,IAAM,EAAM,KAAK,MAAM,MAAM,EAAS,EAAY,EAAG,OAAO,CAAC,EAC7D,MAAO,CAAE,GAAG,EAAe,GAAG,CAAI,CACnC,MAAQ,CACP,MAAO,CAAE,GAAG,CAAc,CAC3B,CACD,CAEA,eAAsB,GAA8B,CACnD,GAAI,CACH,IAAM,EAAM,KAAK,MAAM,MAAM,EAAS,EAAU,EAAG,OAAO,CAAC,EAC3D,MAAO,CAAE,GAAG,EAAa,GAAG,CAAI,CACjC,MAAQ,CACP,MAAO,CAAE,GAAG,CAAY,CACzB,CACD,CAEA,eAAe,GAA2B,CACzC,MAAM,EAAM,EAAS,EAAG,CAAE,UAAW,EAAK,CAAC,CAC5C,CAEA,eAAsB,EAAW,EAAmC,CACnE,MAAM,EAAU,EAChB,MAAM,EAAU,EAAY,EAAG,KAAK,UAAU,EAAQ,KAAM,CAAC,CAAC,CAC/D,CAEA,eAAsB,EAAS,EAA+B,CAC7D,MAAM,EAAU,EAChB,MAAM,EAAU,EAAU,EAAG,KAAK,UAAU,EAAM,KAAM,CAAC,CAAC,EAC1D,GAAI,CACH,MAAM,EAAM,EAAU,EAAG,GAAK,CAC/B,MAAQ,CAER,CACD,CCpDA,SAAgB,EAAa,CAC5B,UACA,UACA,SACA,YAME,CACF,GAAM,CAAC,EAAK,GAAU,EAAS,CAAC,EAoBhC,OAlBA,GAAU,EAAG,IAAQ,CACpB,GAAI,EAAI,OAAQ,CACf,EAAS,IAAI,EACb,MACD,CACA,GAAI,EAAI,QAAS,CAChB,EAAQ,IAAO,EAAI,EAAI,EAAQ,QAAU,EAAQ,MAAM,EACvD,MACD,CACA,GAAI,EAAI,UAAW,CAClB,EAAQ,IAAO,EAAI,GAAK,EAAQ,MAAM,EACtC,MACD,CACI,EAAI,QACP,EAAS,EAAQ,IAAM,OAAS,IAAI,CAEtC,CAAC,EAGA,EAAC,EAAD,CAAK,cAAc,SAAS,SAAU,WAAtC,CACE,GACA,EAAC,EAAD,CAAK,aAAc,WAClB,EAAC,EAAD,CAAA,SAAO,CAAa,CAAA,CAChB,CAAA,EAEN,EAAC,EAAD,CAAK,aAAc,WAClB,EAAC,EAAD,CAAM,KAAA,YAAM,CAAc,CAAA,CACtB,CAAA,EACJ,EAAQ,KAAK,EAAK,IAClB,EAAC,EAAD,CAAA,SAAA,CACC,EAAC,EAAD,CAAM,MAAO,IAAM,EAAM,QAAU,IAAA,YAAnC,CACE,IAAM,EAAM,KAAO,KACnB,EAAI,KACA,IACL,EAAI,MAAQ,IAAM,GAAO,EAAC,EAAD,CAAM,SAAA,YAAN,CAAe,IAAE,EAAI,IAAW,GACtD,CAAA,EANK,EAAI,KAMT,CACL,EACD,EAAC,EAAD,CAAK,UAAW,WACf,EAAC,EAAD,CAAM,SAAA,YAAS,yCAA6C,CAAA,CACxD,CAAA,CACD,GAEP,CAEA,SAAgB,EAAe,CAC9B,UACA,WACA,YAKE,CACF,GAAM,CAAC,EAAO,GAAY,EAAS,EAAE,EAC/B,CAAC,EAAO,GAAY,EAAS,EAAE,EA2BrC,OAzBA,GAAU,EAAI,IAAQ,CACrB,GAAI,EAAI,OAAQ,CACf,EAAS,IAAI,EACb,MACD,CACA,GAAI,EAAI,OAAQ,CACf,IAAM,EAAM,IAAW,CAAK,EAC5B,GAAI,EAAK,CACR,EAAS,CAAG,EACZ,MACD,CACA,EAAS,CAAK,EACd,MACD,CACA,GAAI,EAAI,WAAa,EAAI,OAAQ,CAChC,EAAU,GAAM,EAAE,MAAM,EAAG,EAAE,CAAC,EAC9B,EAAS,EAAE,EACX,MACD,CACI,IACH,EAAU,GAAM,EAAI,CAAE,EACtB,EAAS,EAAE,EAEb,CAAC,EAGA,EAAC,EAAD,CAAK,cAAc,SAAS,SAAU,WAAtC,CACC,EAAC,EAAD,CAAK,aAAc,WAClB,EAAC,EAAD,CAAM,KAAA,YAAM,CAAc,CAAA,CACtB,CAAA,EACL,EAAC,EAAD,CAAA,SAAA,CACC,EAAC,EAAD,CAAM,MAAM,iBAAQ,IAAQ,CAAA,EAC5B,EAAC,EAAD,CAAM,SAAA,YAAU,IAAI,OAAO,EAAM,MAAM,CAAQ,CAAA,EAC/C,EAAC,EAAD,CAAM,MAAM,iBAAQ,GAAO,CAAA,CACvB,CAAA,CAAA,EACJ,GACA,EAAC,EAAD,CAAA,SACC,EAAC,EAAD,CAAM,MAAM,eAAO,CAAY,CAAA,CAC3B,CAAA,EAEN,EAAC,EAAD,CAAK,UAAW,WACf,EAAC,EAAD,CAAM,SAAA,YAAS,2BAA+B,CAAA,CAC1C,CAAA,CACD,GAEP,CAEA,SAAgB,GAAc,CAC7B,UACA,aAIE,CACF,GAAM,CAAC,EAAK,GAAU,EAAS,EAAI,EAgBnC,OAdA,GAAU,EAAG,IAAQ,CACpB,GAAI,EAAI,OAAQ,CACf,EAAU,IAAI,EACd,MACD,CACA,GAAI,EAAI,WAAa,EAAI,YAAc,EAAI,IAAK,CAC/C,EAAQ,GAAM,CAAC,CAAC,EAChB,MACD,CACI,EAAI,QACP,EAAU,CAAG,CAEf,CAAC,EAGA,EAAC,EAAD,CAAK,cAAc,SAAS,SAAU,WAAtC,CACC,EAAC,EAAD,CAAK,aAAc,WAClB,EAAC,EAAD,CAAM,KAAA,YAAM,CAAc,CAAA,CACtB,CAAA,EACL,EAAC,EAAD,CAAA,SACC,EAAC,EAAD,CAAM,MAAO,EAAM,QAAU,IAAA,YAA7B,CAAyC,EAAM,KAAO,KAAK,KAAS,GAChE,CAAA,EACL,EAAC,EAAD,CAAA,SACC,EAAC,EAAD,CAAM,MAAQ,EAAc,IAAA,GAAR,eAApB,CAAyC,EAAa,KAAP,KAAY,IAAQ,GAC/D,CAAA,EACL,EAAC,EAAD,CAAK,UAAW,WACf,EAAC,EAAD,CAAM,SAAA,YAAS,wCAA4C,CAAA,CACvD,CAAA,CACD,GAEP,CAIA,SAAgB,GACf,EACA,EACA,EACyB,CACzB,OAAO,IAAI,QAAS,GAAY,CAC/B,GAAM,CAAE,WAAY,EACnB,EAAC,EAAD,CACU,UACA,UACD,SACR,SAAW,GAAM,CAChB,EAAQ,EACR,EAAQ,CAAC,CACV,CACA,CAAA,CACF,CACD,CAAC,CACF,CAEA,SAAgB,GACf,EACA,EACyB,CACzB,OAAO,IAAI,QAAS,GAAY,CAC/B,GAAM,CAAE,WAAY,EACnB,EAAC,EAAD,CACU,UACC,WACV,SAAW,GAAM,CAChB,EAAQ,EACR,EAAQ,CAAC,CACV,CACA,CAAA,CACF,CACD,CAAC,CACF,CCtMA,eAAsB,IAAqC,CAC1D,QAAQ,IAAI,EAAM,KAAK,KAAK;;CAAoC,CAAC,EAEjE,IAAM,EAAa,MAAM,GACxB,kBACA,EAAU,IAAK,IAAO,CAAE,MAAO,EAAE,GAAI,MAAO,EAAE,IAAK,EAAE,CACtD,EACK,IACJ,QAAQ,IAAI,EAAM,IAAI,WAAW,CAAC,EAClC,QAAQ,KAAK,CAAC,GAGf,IAAM,EAAW,EAAY,CAAU,EAClC,IACJ,QAAQ,IAAI,EAAM,IAAI,kBAAkB,CAAC,EACzC,QAAQ,KAAK,CAAC,GAGf,IAAM,EAAS,MAAM,GAAmB,SAAS,EAAS,KAAK,SAAS,EACnE,IACJ,QAAQ,IAAI,EAAM,IAAI,WAAW,CAAC,EAClC,QAAQ,KAAK,CAAC,GAIf,IAAM,EAAU,MAAM,GACrB,uBAFc,GAAqB,CAG9B,EAAE,IAAK,IAAO,CAClB,MAAO,EAAE,GACT,MAAO,GAAG,EAAE,KAAK,KAAK,EAAE,cAAgB,KAAM,QAAQ,CAAC,EAAE,OAC1D,EAAE,CACH,EACK,IACJ,QAAQ,IAAI,EAAM,IAAI,WAAW,CAAC,EAClC,QAAQ,KAAK,CAAC,GAGf,IAAM,EAAqB,CAC1B,SAAU,EACV,MAAO,CACR,EAMA,OAJA,MAAM,EAAW,CAAM,EACvB,MAAM,EAAS,CAAE,QAAS,EAAG,GAAa,CAAO,CAAE,CAAC,EAEpD,QAAQ,IAAI,EAAM,MAAM;;CAAqD,CAAC,EACvE,CACR,CC3CA,MAAM,GAAS,IAAI,IAAI,CAAC,OAAQ,QAAS,OAAQ,OAAQ,OAAO,CAAC,EAEjE,SAAS,EAAS,EAAa,EAAmB,CACjD,IAAM,EAAM,EAAQ,EAAK,CAAC,EAC1B,GAAI,IAAQ,GAAO,CAAC,EAAI,WAAW,GAAG,EAAI,EAAE,EAC3C,MAAU,MAAM,yBAAyB,GAAG,EAE7C,OAAO,CACR,CAEA,SAAgB,GAAS,EAAmB,CAC3C,MAAO,CACN,IAAK,CACJ,KAAM,OACN,YACC,8GACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,KAAM,CAAE,KAAM,SAAU,YAAa,qCAAsC,EAC3E,OAAQ,CAAE,KAAM,SAAU,YAAa,iCAAkC,EACzE,MAAO,CAAE,KAAM,SAAU,YAAa,kCAAmC,CAC1E,EACA,SAAU,CAAC,MAAM,CAClB,CACD,EACA,MAAM,QAAQ,EAA2B,CACxC,GAAI,CACH,IAAM,EAAW,EAAS,EAAK,EAAK,IAAc,EAE5C,EAAM,EAAQ,CAAQ,EAAE,YAAY,EAC1C,GAAI,GAAO,IAAI,CAAG,EAIjB,MAAO,CAAE,QAAS,CAAC,CAAE,KAAM,QAAS,MAFxB,MADM,EAAS,CAAQ,GACnB,SAAS,QAEmB,EAAG,KADlC,IAAQ,OAAS,aAAe,SAAS,EAAI,MAAM,CAAC,GACb,CAAC,EAAG,QAAS,EAAM,EAIxE,IAAM,GAAQ,MADQ,EAAS,EAAU,OAAO,GAC1B,MAAM;CAAI,EAC1B,EAAS,KAAK,IAAI,GAAI,OAAO,EAAK,QAAU,CAAC,GAAK,GAAK,CAAC,EACxD,EAAQ,OAAO,EAAK,OAAS,GAAI,GAAK,IACtC,EAAQ,EAAM,MAAM,EAAQ,EAAS,CAAK,EAC1C,EAAY,EAAS,EAAQ,EAAM,OAKzC,MAAO,CAAE,QAAS,CAAC,EAHP,EAAM,KAAK;CAGO,GAFf,EAAY,MAAM,EAAM,OAAS,EAAS,EAAM,aAAe,GAEtC,CAAC,EAAG,QAAS,EAAM,CAC5D,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,uBAAwB,EAAY,SAAS,CAAC,EACjE,QAAS,EACV,CACD,CACD,CACD,CACD,CAEA,SAAgB,GAAU,EAAmB,CAC5C,MAAO,CACN,IAAK,CACJ,KAAM,QACN,YAAa,8EACb,WAAY,CACX,KAAM,SACN,WAAY,CACX,KAAM,CAAE,KAAM,SAAU,YAAa,cAAe,EACpD,QAAS,CAAE,KAAM,SAAU,YAAa,kBAAmB,CAC5D,EACA,SAAU,CAAC,OAAQ,SAAS,CAC7B,CACD,EACA,MAAM,QAAQ,EAA2B,CACxC,GAAI,CACH,IAAM,EAAW,EAAS,EAAK,EAAK,IAAc,EAC5C,EAAU,EAAK,QACrB,MAAM,EAAM,EAAQ,CAAQ,EAAG,CAAE,UAAW,EAAK,CAAC,EAClD,MAAM,EAAU,EAAU,CAAO,EACjC,IAAM,EAAU,EAAoB,EAAK,CAAQ,EACjD,MAAO,CACN,QAAS,CAAC,EAAS,SAAS,EAAQ,OAAO,WAAW,GAAS,CAAC,EAChE,QAAS,EACV,CACD,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,uBAAwB,EAAY,SAAS,CAAC,EACjE,QAAS,EACV,CACD,CACD,CACD,CACD,CAGA,SAAgB,GAAS,EAAmB,CAC3C,MAAO,CACN,IAAK,CACJ,KAAM,OACN,YACC,4FACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,KAAM,CAAE,KAAM,SAAU,YAAa,cAAe,EACpD,MAAO,CACN,KAAM,QACN,YACC,qFACD,MAAO,CACN,KAAM,SACN,WAAY,CACX,QAAS,CAAE,KAAM,SAAU,YAAa,qCAAsC,EAC9E,QAAS,CAAE,KAAM,SAAU,YAAa,kBAAmB,CAC5D,EACA,SAAU,CAAC,UAAW,SAAS,CAChC,CACD,CACD,EACA,SAAU,CAAC,OAAQ,OAAO,CAC3B,CACD,EACA,MAAM,QAAQ,EAA2B,CACxC,GAAI,CACH,IAAM,EAAW,EAAS,EAAK,EAAK,IAAc,EAC9C,EACJ,GAAI,CACH,EAAU,MAAM,EAAS,EAAU,OAAO,CAC3C,MAAQ,CACP,MAAO,CAAE,QAAS,CAAC,EAAS,mBAAmB,EAAK,MAAM,CAAC,EAAG,QAAS,EAAK,CAC7E,CACA,IAAM,EAAQ,EAAK,MAGnB,IAAK,IAAM,KAAQ,EAAO,CACzB,IAAM,EAAQ,EAAQ,MAAM,EAAK,OAAO,EAAE,OAAS,EACnD,GAAI,IAAU,EACb,MAAO,CACN,QAAS,CAAC,EAAS,uBAAuB,EAAK,QAAQ,MAAM,EAAG,EAAE,EAAE,GAAG,CAAC,EACxE,QAAS,EACV,EAGD,GAAI,EAAQ,EACX,MAAO,CACN,QAAS,CACR,EACC,iBAAiB,EAAM,uDAAuD,EAAK,QAAQ,MAAM,EAAG,EAAE,EAAE,GACzG,CACD,EACA,QAAS,EACV,CAEF,CAGA,IAAK,IAAM,KAAQ,EAClB,EAAU,EAAQ,QAAQ,EAAK,QAAS,EAAK,OAAO,EAKrD,OAFA,MAAM,EAAU,EAAU,CAAO,EAE1B,CACN,QAAS,CACR,EACC,UAJa,EAAoB,EAAK,CAItB,EAAE,IAAI,EAAM,OAAO,cAAc,EAAM,OAAS,EAAI,IAAM,GAAG,EAC9E,CACD,EACA,QAAS,EACV,CACD,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,uBAAwB,EAAY,SAAS,CAAC,EACjE,QAAS,EACV,CACD,CACD,CACD,CACD,CCrLA,SAAgB,GAAQ,EAAmB,CAC1C,MAAO,CACN,IAAK,CACJ,KAAM,MACN,YACC,iGACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,OAAQ,CACP,KAAM,SACN,KAAM,CAAC,SAAU,OAAQ,MAAO,MAAO,QAAQ,EAC/C,YAAa,2BACd,EACA,KAAM,CACL,KAAM,QACN,YAAa,iEACb,MAAO,CAAE,KAAM,QAAS,CACzB,CACD,EACA,SAAU,CAAC,QAAQ,CACpB,CACD,EACA,MAAM,QAAQ,EAAM,EAA6B,CAChD,IAAM,EAAS,EAAK,OACd,EAAa,EAAK,MAAqB,CAAC,EAG9C,GAAI,CAAC,IADe,IAAI,CAAC,SAAU,OAAQ,MAAO,MAAO,QAAQ,CACtD,EAAE,IAAI,CAAM,EACtB,MAAO,CACN,QAAS,CAAC,EAAS,sBAAsB,EAAO,oBAAoB,CAAC,EACrE,QAAS,EACV,EAGD,GAAI,CACH,IAAM,EAAM,CAAC,MAAO,EAAQ,GAAG,CAAS,EAClC,EAAO,EAAM,EAAI,GAAK,EAAI,MAAM,CAAC,EAAG,CACzC,MACA,MAAO,CAAC,SAAU,OAAQ,MAAM,EAChC,IAAK,CAAE,GAAG,QAAQ,IAAK,MAAO,KAAM,CACrC,CAAC,EAEG,EAAS,GACT,EAAS,GACb,EAAK,OAAO,GAAG,OAAS,GAAkB,CACzC,GAAU,EAAM,SAAS,CAC1B,CAAC,EACD,EAAK,OAAO,GAAG,OAAS,GAAkB,CACzC,GAAU,EAAM,SAAS,CAC1B,CAAC,EAED,IAAM,MAAgB,CACrB,EAAK,KAAK,SAAS,EACnB,EAAK,OAAO,QAAQ,EACpB,EAAK,OAAO,QAAQ,CACrB,EACA,GAAQ,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,EAEzD,IAAI,EACJ,GAAI,CACH,EAAW,MAAM,IAAI,SAAiB,EAAS,IAAW,CACzD,EAAK,GAAG,QAAS,CAAM,EACvB,EAAK,GAAG,QAAU,GAAS,EAAQ,GAAQ,EAAE,CAAC,CAC/C,CAAC,CACF,QAAU,CACT,GAAQ,oBAAoB,QAAS,CAAO,CAC7C,CAGA,IAAM,EAAM,IACR,EAAM,GAQV,OAPI,IAAQ,GAAO,EAAO,MAAM,EAAG,CAAG,GAClC,IACC,IAAK,GAAO;GAChB,GAAO,EAAO,MAAM,EAAG,EAAM,EAAI,MAAM,GAEpC,EAAI,QAAU,IAAK,GAAO;aAEvB,CACN,QAAS,CAAC,EAAS,GAAO,aAAa,CAAC,EACxC,QAAS,IAAa,CACvB,CACD,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,sBAAuB,EAAY,SAAS,CAAC,EAChE,QAAS,EACV,CACD,CACD,CACD,CACD,CClFA,SAAgB,GAAS,EAAmB,CAC3C,MAAO,CACN,IAAK,CACJ,KAAM,OACN,YAAa,+DACb,WAAY,CACX,KAAM,SACN,WAAY,CACX,QAAS,CAAE,KAAM,SAAU,YAAa,6BAA8B,EACtE,KAAM,CAAE,KAAM,SAAU,YAAa,oCAAqC,EAC1E,OAAQ,CAAE,KAAM,UAAW,YAAa,yCAA0C,CACnF,EACA,SAAU,CAAC,SAAS,CACrB,CACD,EACA,MAAM,QAAQ,EAA2B,CACxC,GAAI,CACH,IAAM,EAAW,EAAK,MAAmB,IACnC,EAAM,EAAQ,EAAK,CAAO,EAChC,GAAI,IAAQ,GAAO,CAAC,EAAI,WAAW,GAAG,EAAI,EAAE,EAC3C,MAAU,MAAM,yBAAyB,GAAS,EAGnD,IAAM,EAAU,EAAK,QAGf,GAAS,MADK,EAAK,EAAS,CAAE,IAAK,EAAK,OAAA,CAD9B,CAAC,EAAK,MAC+B,CAAC,GACjC,MAAM,EAAG,GAAG,EAC3B,EAAgB,EAAS,EAAK,CAAG,EACjC,EAAS,EAAgB,GAAG,EAAc,GAAK,GAC/C,EAAW,EAAO,IAAK,GAAM,EAAS,CAAC,EAE7C,MAAO,CAAE,QAAS,CAAC,EADP,EAAS,OAAS,EAAI,EAAS,KAAK;CAAI,EAAI,gBACzB,CAAC,EAAG,QAAS,EAAM,CACnD,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,UAAW,EAAY,SAAS,CAAC,EACpD,QAAS,EACV,CACD,CACD,CACD,CACD,CAKA,SAAgB,GAAS,EAAmB,CAC3C,MAAO,CACN,IAAK,CACJ,KAAM,OACN,YACC,sGACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,QAAS,CAAE,KAAM,SAAU,YAAa,6BAA8B,EACtE,KAAM,CAAE,KAAM,SAAU,YAAa,4CAA6C,EAClF,KAAM,CAAE,KAAM,SAAU,YAAa,8BAA+B,CACrE,EACA,SAAU,CAAC,SAAS,CACrB,CACD,EACA,MAAM,QAAQ,EAAM,EAA6B,CAChD,GAAI,CACH,IAAM,EAAW,EAAK,MAAmB,IACnC,EAAM,EAAQ,EAAK,CAAO,EAChC,GAAI,IAAQ,GAAO,CAAC,EAAI,WAAW,GAAG,EAAI,EAAE,EAC3C,MAAU,MAAM,yBAAyB,GAAS,EAGnD,IAAM,EAAU,EAAK,QACf,EAAa,EAAK,KAClB,EAAgB,EAAS,EAAK,CAAG,GAAK,IAG5C,GAAI,CACH,IAAM,EAAM,CAAC,KAAM,gBAAiB,cAAe,KAAK,EACpD,GAAY,EAAI,KAAK,UAAU,GAAY,EAC/C,EAAI,KAAK,KAAM,EAAS,CAAa,EAErC,IAAM,EAAO,EAAM,EAAI,GAAK,EAAI,MAAM,CAAC,EAAG,CACzC,MACA,MAAO,CAAC,SAAU,OAAQ,MAAM,CACjC,CAAC,EAEK,MAAgB,CACrB,EAAK,KAAK,EACV,EAAK,OAAO,QAAQ,EACpB,EAAK,OAAO,QAAQ,CACrB,EACA,GAAQ,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,EAEzD,IAAI,EAAS,GACb,EAAK,OAAO,GAAG,OAAS,GAAkB,CACzC,GAAU,EAAM,SAAS,CAC1B,CAAC,EAED,IAAI,EACJ,GAAI,CACH,EAAW,MAAM,IAAI,SAAiB,EAAS,IAAW,CACzD,EAAK,GAAG,QAAS,CAAM,EACvB,EAAK,GAAG,QAAU,GAAS,EAAQ,GAAQ,EAAE,CAAC,CAC/C,CAAC,CACF,QAAU,CACT,GAAQ,oBAAoB,QAAS,CAAO,CAC7C,CAEA,GAAI,IAAa,EAEhB,MAAO,CAAE,QAAS,CAAC,EADL,EAAO,MAAM;CAAI,EAAE,MAAM,EAAG,GAAG,EAAE,KAAK;CACpB,GAAK,YAAY,CAAC,EAAG,QAAS,EAAM,CAEtE,MAAQ,CAER,CAGA,IAAM,EAAQ,MAAM,EAAK,GAAc,OAAQ,CAC9C,IAAK,EACL,OAAQ,CAAC,qBAAsB,YAAY,CAC5C,CAAC,EACK,EAAS,IAAkB,IAAM,GAAK,GAAG,EAAc,GACvD,EAAK,IAAI,OAAO,EAAS,GAAG,EAC5B,EAAoB,CAAC,EAC3B,IAAK,IAAM,KAAQ,EAAM,MAAM,EAAG,GAAG,EAAG,CACvC,GAAI,GAAQ,QAAS,MACrB,GAAI,CAEH,IAAM,GAAQ,MADQ,EAAS,EAAQ,EAAK,CAAI,EAAG,OAAO,GACpC,MAAM;CAAI,EAChC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,QAAU,EAAQ,OAAS,IAAK,IAAK,CAC9D,IAAM,EAAO,EAAM,GACf,GAAQ,EAAG,KAAK,CAAI,GAAG,EAAQ,KAAK,GAAG,IAAS,EAAK,GAAG,EAAI,EAAE,GAAG,GAAM,CAC5E,CACD,MAAQ,CAER,CACD,CACA,MAAO,CACN,QAAS,CAAC,EAAS,EAAQ,KAAK;CAAI,GAAK,YAAY,CAAC,EACtD,QAAS,EACV,CACD,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,UAAW,EAAY,SAAS,CAAC,EACpD,QAAS,EACV,CACD,CACD,CACD,CACD,CAKA,SAAgB,GAAO,EAAmB,CACzC,MAAO,CACN,IAAK,CACJ,KAAM,KACN,YAAa,2BACb,WAAY,CACX,KAAM,SACN,WAAY,CACX,KAAM,CAAE,KAAM,SAAU,YAAa,+BAAgC,CACtE,EACA,SAAU,CAAC,CACZ,CACD,EACA,MAAM,QAAQ,EAA2B,CACxC,GAAI,CAOH,MAAO,CAAE,QAAS,CAAC,GAJL,MADQ,EADV,EAAQ,EAAM,EAAK,MAAmB,GAClB,EAAG,CAAE,cAAe,EAAK,CAAC,GACpC,IAAK,GAAM,CAChC,IAAM,EAAS,EAAE,YAAY,EAAI,IAAM,EAAE,eAAe,EAAI,IAAM,GAClE,MAAO,GAAG,EAAE,OAAO,GACpB,CACgC,EAAE,KAAK;CAAI,GAAK,SAAS,CAAC,EAAG,QAAS,EAAM,CAC7E,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,UAAW,EAAY,SAAS,CAAC,EACpD,QAAS,EACV,CACD,CACD,CACD,CACD,CAKA,SAAgB,GAAS,EAAmB,CAC3C,MAAO,CACN,IAAK,CACJ,KAAM,OACN,YACC,uGACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,KAAM,CAAE,KAAM,SAAU,YAAa,0CAA2C,EAChF,MAAO,CAAE,KAAM,SAAU,YAAa,uCAAwC,CAC/E,EACA,SAAU,CAAC,CACZ,CACD,EACA,MAAM,QAAQ,EAA2B,CACxC,GAAI,CACH,IAAM,EAAW,EAAQ,EAAM,EAAK,MAAmB,GAAG,EACpD,EAAW,OAAO,EAAK,OAAS,CAAC,GAAK,EAEtC,EAAa,IAAI,IAAI,CAC1B,OACA,eACA,OACA,QACA,cACA,QACA,MACA,eACA,UACD,CAAC,EAED,eAAe,EAAK,EAAa,EAAsB,EAAiC,CACvF,GAAI,EAAe,EAAU,MAAO,GACpC,IAAI,EAAS,GAGP,GAAS,MADO,EAAQ,EAAK,CAAE,cAAe,EAAK,CAAC,GAExD,OAAQ,GAAM,CAAC,EAAW,IAAI,EAAE,IAAI,CAAC,EACrC,MAAM,EAAG,IACL,EAAE,YAAY,GAAK,CAAC,EAAE,YAAY,EAAU,GAC5C,CAAC,EAAE,YAAY,GAAK,EAAE,YAAY,EAAU,EACzC,EAAE,KAAK,cAAc,EAAE,IAAI,CAClC,EAEF,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,IAAK,CACvC,IAAM,EAAQ,EAAO,GACf,EAAS,IAAM,EAAO,OAAS,EAC/B,EAAY,EAAS,OAAS,OAC9B,EAAc,GAAU,EAAS,OAAS,QAEhD,GAAU,GAAG,IAAS,IAAY,EAAM,OAAO,EAAM,YAAY,EAAI,IAAM,GAAG,IAE1E,EAAM,YAAY,IACrB,GAAU,MAAM,EAAK,EAAQ,EAAK,EAAM,IAAI,EAAG,EAAe,EAAG,CAAW,EAE9E,CACA,OAAO,CACR,CAGA,MAAO,CAAE,QAAS,CAAC,EAAS,MADL,EAAK,EAAU,EAAG,EAAE,GACH,SAAS,CAAC,EAAG,QAAS,EAAM,CACrE,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,UAAW,EAAY,SAAS,CAAC,EACpD,QAAS,EACV,CACD,CACD,CACD,CACD,CCxQA,SAAgB,GAAS,EAAmB,CAC3C,MAAO,CACN,IAAK,CACJ,KAAM,OACN,YACC,6FACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,QAAS,CAAE,KAAM,SAAU,YAAa,sBAAuB,EAC/D,QAAS,CAAE,KAAM,SAAU,YAAa,kCAAmC,CAC5E,EACA,SAAU,CAAC,SAAS,CACrB,CACD,EACA,MAAM,QAAQ,EAAM,EAA6B,CAChD,IAAM,EAAU,EAAK,QACf,GAAa,OAAO,EAAK,OAAO,GAAK,KAAO,IAElD,GAAI,CACH,IAAM,EAAO,EAAM,KAAM,CAAC,KAAM,CAAO,EAAG,CAAE,MAAK,MAAO,CAAC,SAAU,OAAQ,MAAM,CAAE,CAAC,EAEhF,EAAS,GACT,EAAS,GACb,EAAK,OAAO,GAAG,OAAS,GAAkB,CACzC,GAAU,EAAM,SAAS,CAC1B,CAAC,EACD,EAAK,OAAO,GAAG,OAAS,GAAkB,CACzC,GAAU,EAAM,SAAS,CAC1B,CAAC,EAED,IAAI,EAAS,GACP,EAAQ,eAAiB,CAC9B,EAAS,GACT,EAAK,KAAK,SAAS,EACnB,EAAK,OAAO,QAAQ,EACpB,EAAK,OAAO,QAAQ,CACrB,EAAG,CAAS,EAEN,MAAgB,CACrB,EAAS,GACT,EAAK,KAAK,SAAS,EACnB,EAAK,OAAO,QAAQ,EACpB,EAAK,OAAO,QAAQ,CACrB,EACA,GAAQ,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,EAEzD,IAAI,EACJ,GAAI,CACH,EAAW,MAAM,IAAI,SAAiB,EAAS,IAAW,CACzD,EAAK,GAAG,QAAS,CAAM,EACvB,EAAK,GAAG,QAAU,GAAS,EAAQ,GAAQ,EAAE,CAAC,CAC/C,CAAC,CACF,QAAU,CACT,aAAa,CAAK,EAClB,GAAQ,oBAAoB,QAAS,CAAO,CAC7C,CAGA,IAAM,EAAM,IACR,EAAM,GAWV,OAVI,IAAQ,GAAO,EAAO,MAAM,EAAG,CAAG,GAClC,IACC,IAAK,GAAO;GAChB,GAAO,EAAO,MAAM,EAAG,EAAM,EAAI,MAAM,GAEpC,EAAI,QAAU,IAAK,GAAO;aAE1B,IAAQ,GAAO,oBAAoB,EAAY,IAAK,KACxD,GAAO,WAAW,EAAS,GAEpB,CAAE,QAAS,CAAC,EAAS,CAAG,CAAC,EAAG,QAAS,IAAa,GAAK,CAAO,CACtE,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,UAAW,EAAY,SAAS,CAAC,EACpD,QAAS,EACV,CACD,CACD,CACD,CACD,CCjFA,MAAM,EAAc,IAGpB,SAAS,EAAW,EAAsB,CACzC,IAAI,EAAO,EAET,QAAQ,mBAAoB,EAAE,EAE9B,QAAQ,8BAA+B,EAAE,EACzC,QAAQ,4BAA6B,EAAE,EAEvC,QAAQ,2DAA4D,UAAU,EAE9E,QAAQ,wDAAyD;CAAI,EAErE,QAAQ,WAAY,EAAE,EAEtB,QAAQ,SAAU,GAAG,EACrB,QAAQ,QAAS,GAAG,EACpB,QAAQ,QAAS,GAAG,EACpB,QAAQ,UAAW,GAAG,EACtB,QAAQ,SAAU,GAAG,EACrB,QAAQ,UAAW,GAAG,EACtB,QAAQ,SAAU,GAAG,EACrB,QAAQ,UAAW,GAAG,EACtB,QAAQ,UAAW,GAAG,EACtB,QAAQ,WAAY,GAAG,EACvB,QAAQ,WAAY,GAAG,EACvB,QAAQ,WAAY,GAAG,EACvB,QAAQ,WAAY,GAAG,EACvB,QAAQ,UAAW,GAAG,EAEtB,QAAQ,UAAW,GAAG,EACtB,QAAQ,UAAW;;CAAM,EACzB,KAAK,EAKP,OAHI,EAAK,OAAS,IACjB,EAAO,GAAG,EAAK,MAAM,EAAG,CAAW,EAAE,eAE/B,CACR,CAEA,SAAgB,IAAsB,CACrC,MAAO,CACN,IAAK,CACJ,KAAM,aACN,YACC,mJACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,MAAO,CAAE,KAAM,SAAU,YAAa,cAAe,CACtD,EACA,SAAU,CAAC,OAAO,CACnB,CACD,EACA,MAAM,QAAQ,EAAM,EAA6B,CAChD,IAAM,EAAQ,EAAK,MACnB,GAAI,CAAC,EAAM,KAAK,EACf,MAAO,CAAE,QAAS,CAAC,EAAS,2BAA2B,CAAC,EAAG,QAAS,EAAK,EAG1E,GAAI,CACH,IAAM,EAAM,uCAAuC,mBAAmB,CAAK,IACrE,EAAO,MAAM,MAAM,EAAK,CAC7B,OAAQ,GAAU,IAAA,GAClB,QAAS,CACR,aACC,uHACF,CACD,CAAC,EAED,GAAI,CAAC,EAAK,GACT,MAAO,CACN,QAAS,CAAC,EAAS,uBAAuB,EAAK,QAAQ,CAAC,EACxD,QAAS,EACV,EAGD,IAAM,EAAO,MAAM,EAAK,KAAK,EAGvB,EAAmB,CAAC,EACpB,EAAiB,kDACjB,EAAoB,CAAC,EAC3B,IAAK,IAAM,KAAS,EAAK,SAAS,CAAc,EAC3C,EAAM,QAAU,IAAA,IACnB,EAAQ,KAAK,EAAM,KAAK,EAI1B,GAAI,EAAQ,OAAS,EACpB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAAK,CACxC,IAAM,EAAQ,EAAQ,GAChB,EAAM,EAAQ,EAAI,IAAM,EAAK,OACnC,EAAO,KAAK,EAAK,MAAM,EAAO,CAAG,CAAC,CACnC,CAGD,IAAM,EAAoB,CAAC,EACrB,EAAa,oEACb,EAAe,uDAErB,IAAK,IAAM,KAAS,EAAQ,CAC3B,GAAI,EAAQ,QAAU,GAAI,MAE1B,IAAM,EAAa,EAAW,KAAK,CAAK,EACxC,GAAI,CAAC,EAAY,SAEjB,IAAM,EAAS,EAAW,GACpB,EAAQ,EAAW,EAAW,EAAG,EAEjC,EAAe,EAAa,KAAK,CAAK,EACtC,EAAU,EAAe,EAAW,EAAa,EAAG,EAAI,GAG1D,EAAW,EACf,GAAI,CAEH,IAAM,EAAa,EAAO,WAAW,IAAI,EACtC,SAAS,IACT,EAAO,WAAW,GAAG,EACpB,yBAAyB,IACzB,EACE,EAAQ,IAAI,IAAI,CAAU,EAAE,aAAa,IAAI,MAAM,EACrD,IAAO,EAAW,EACvB,MAAQ,CAER,CAEA,EAAQ,KAAK,MAAM,EAAM,IAAI,EAAS,IAAI,GAAS,CACpD,CAMA,OAJI,EAAQ,SAAW,EACf,CAAE,QAAS,CAAC,EAAS,mBAAmB,CAAC,EAAG,QAAS,EAAM,EAG5D,CACN,QAAS,CAAC,EAAS,EAAQ,KAAK;;CAAM,CAAC,CAAC,EACxC,QAAS,EACV,CACD,OAAS,EAAG,CACX,IAAM,EAAO,EAAY,QAIzB,OAHI,EAAI,SAAS,OAAO,EAChB,CAAE,QAAS,CAAC,EAAS,iBAAiB,CAAC,EAAG,QAAS,EAAK,EAEzD,CACN,QAAS,CAAC,EAAS,iBAAiB,GAAK,CAAC,EAC1C,QAAS,EACV,CACD,CACD,CACD,CACD,CAEA,SAAgB,IAAqB,CACpC,MAAO,CACN,IAAK,CACJ,KAAM,YACN,YACC,0JACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,IAAK,CAAE,KAAM,SAAU,YAAa,cAAe,CACpD,EACA,SAAU,CAAC,KAAK,CACjB,CACD,EACA,MAAM,QAAQ,EAAM,EAA6B,CAChD,IAAM,EAAM,EAAK,IACjB,GAAI,CAAC,EAAI,KAAK,EACb,MAAO,CAAE,QAAS,CAAC,EAAS,kBAAkB,CAAC,EAAG,QAAS,EAAK,EAGjE,GAAI,CACH,IAAI,IAAI,CAAG,CACZ,MAAQ,CACP,MAAO,CAAE,QAAS,CAAC,EAAS,uBAAuB,GAAK,CAAC,EAAG,QAAS,EAAK,CAC3E,CAEA,GAAI,CACH,IAAM,EAAO,MAAM,MAAM,EAAK,CAC7B,OAAQ,GAAU,IAAA,GAClB,QAAS,CACR,aACC,wHACD,OAAQ,iEACT,EACA,SAAU,QACX,CAAC,EAED,GAAI,CAAC,EAAK,GACT,MAAO,CACN,QAAS,CAAC,EAAS,sBAAsB,EAAK,OAAO,GAAG,EAAK,YAAY,CAAC,EAC1E,QAAS,EACV,EAGD,IAAM,GAAe,EAAK,QAAQ,IAAI,cAAc,GAAK,IAAI,YAAY,EACnE,EAAO,MAAM,EAAK,KAAK,EAiB7B,OAbC,EAAY,SAAS,WAAW,GAChC,EAAY,SAAS,uBAAuB,GAC5C,EAAK,KAAK,EAAE,YAAY,EAAE,WAAW,gBAAgB,GACrD,EAAK,KAAK,EAAE,YAAY,EAAE,WAAW,OAAO,EAIrC,CAAE,QAAS,CAAC,EADN,EAAW,CACO,CAAC,CAAC,EAAG,QAAS,EAAM,EAM7C,CAAE,QAAS,CAAC,EADlB,EAAK,OAAS,EAAc,GAAG,EAAK,MAAM,EAAG,CAAW,EAAE,cAAgB,CACtC,CAAC,EAAG,QAAS,EAAM,CACzD,OAAS,EAAG,CACX,IAAM,EAAO,EAAY,QAIzB,OAHI,EAAI,SAAS,OAAO,EAChB,CAAE,QAAS,CAAC,EAAS,gBAAgB,CAAC,EAAG,QAAS,EAAK,EAExD,CACN,QAAS,CAAC,EAAS,gBAAgB,GAAK,CAAC,EACzC,QAAS,EACV,CACD,CACD,CACD,CACD,CCvOA,SAAgB,GAAY,EAAqB,CAChD,MAAO,CACN,GAAS,CAAG,EACZ,GAAU,CAAG,EACb,GAAS,CAAG,EACZ,GAAS,CAAG,EACZ,GAAS,CAAG,EACZ,GAAS,CAAG,EACZ,GAAO,CAAG,EACV,GAAS,CAAG,EACZ,GAAQ,CAAG,EACX,GAAc,EACd,GAAa,CACd,CACD,CCfA,MAAM,GAAY,EAAQ,GAAc,OAAO,KAAK,GAAG,CAAC,EAExD,IAAI,EAA8B,KAC9B,EAA+B,KAEnC,eAAsB,GAAqC,CAC1D,GAAI,EAAe,OAAO,EAC1B,GAAI,CACH,IAAM,EAAM,MAAM,EAAS,EAAK,GAAW,KAAM,cAAc,EAAG,OAAO,EAGzE,MADA,GADY,KAAK,MAAM,CACJ,EAAE,SAAsB,QACpC,CACR,MAAQ,CACP,MAAO,OACR,CACD,CAEA,eAAsB,IAA2C,CAChE,GAAI,EAAc,OAAO,EACzB,GAAI,CACH,IAAM,EAAO,EAAM,MAAO,CAAC,OAAQ,WAAY,SAAS,EAAG,CAC1D,MAAO,CAAC,SAAU,OAAQ,QAAQ,CACnC,CAAC,EACK,EAAO,MAAM,IAAI,SAAiB,EAAS,IAAW,CAC3D,IAAI,EAAM,GACV,EAAK,OAAO,GAAG,OAAS,GAAkB,CACzC,GAAO,EAAM,SAAS,CACvB,CAAC,EACD,EAAK,GAAG,QAAS,CAAM,EACvB,EAAK,GAAG,YAAe,EAAQ,EAAI,KAAK,CAAC,CAAC,CAC3C,CAAC,EACD,GAAI,EAEH,MADA,GAAe,EACR,CAET,MAAQ,CAAC,CACT,OAAO,IACR,CAEA,eAAsB,IAIZ,CACT,IAAM,EAAU,MAAM,EAAkB,EAClC,EAAS,MAAM,GAAiB,EAEtC,OADK,EACE,CACN,UAAW,GAAO,GAAG,EAAQ,CAAO,EACpC,UACA,QACD,EALoB,IAMrB,CAEA,eAAsB,EAAU,EAAS,GAAyB,CACjE,GAAI,CACH,IAAM,EAAO,EAAM,MAAO,CAAC,SAAU,KAAM,UAAU,EAAG,CACvD,MAAO,EAAS,SAAW,SAC5B,CAAC,EACK,EAAW,MAAM,IAAI,SAAiB,EAAS,IAAW,CAC/D,EAAK,GAAG,QAAS,CAAM,EACvB,EAAK,GAAG,QAAU,GAAS,EAAQ,GAAQ,EAAE,CAAC,CAC/C,CAAC,EAWA,OAVG,IAAa,GACX,GACJ,QAAQ,IAAI,oDAAoD,EAE1D,KAEF,IACJ,QAAQ,MAAM,4BAA4B,EAAS,EAAE,EACrD,QAAQ,KAAK,CAAC,GAER,GAET,OAAS,EAAG,CAKX,OAJK,IACJ,QAAQ,MAAM,kBAAmB,EAAY,SAAS,EACtD,QAAQ,KAAK,CAAC,GAER,EACR,CACD,CCvEA,SAAS,IAAW,CACnB,GAAM,CAAE,SAAQ,eAAgB,EAAU,CACzC,QAAS,CACR,KAAM,CAAE,KAAM,UAAW,MAAO,GAAI,EACpC,QAAS,CAAE,KAAM,UAAW,MAAO,GAAI,EACvC,SAAU,CAAE,KAAM,QAAS,EAC3B,MAAO,CAAE,KAAM,QAAS,EACxB,UAAW,CAAE,KAAM,QAAS,EAC5B,QAAS,CAAE,KAAM,SAAU,MAAO,GAAI,CACvC,EACA,OAAQ,GACR,iBAAkB,EACnB,CAAC,EAED,MAAO,CAAE,MAAO,EAAQ,KAAM,CAAY,CAC3C,CAEA,SAAS,GAAU,EAAiB,EAAqB,CACxD,OAAO,EAAO,KAAM,GACf,EAAmB,EAAE,WAAa,GAAc,EAAE,KAAO,EACtD,EAAE,KAAO,CAChB,CACF,CAEA,eAAe,IAAO,CACrB,GAAM,CAAE,QAAO,QAAS,GAAS,EAEjC,GAAI,EAAM,QAAS,CAClB,IAAM,EAAU,MAAM,EAAkB,EACxC,QAAQ,IAAI,QAAQ,GAAS,EAC7B,QAAQ,KAAK,CAAC,CACf,CAsBA,GApBI,EAAM,OACT,QAAQ,IAAI;;;;;;;;;;;;;;+CAciC,EAC7C,QAAQ,KAAK,CAAC,GAIX,EAAK,KAAO,UAAW,CAC1B,MAAM,GAAqB,EAAK,MAAM,CAAC,CAAC,EACxC,MACD,CAGA,GAAI,EAAK,KAAO,SAAU,CACzB,MAAM,EAAU,EAChB,MACD,CAGI,EAAK,OAAS,IACjB,QAAQ,MAAM,EAAM,OAAO,oBAAoB,EAAK,KAAK,GAAG,GAAG,CAAC,EAChE,QAAQ,MAAM,8BAA8B,EAC5C,QAAQ,KAAK,CAAC,GAGf,IAAM,EAAa,IAAI,gBAEjB,MAAiB,CACtB,EAAW,MAAM,EACjB,QAAQ,OAAO,MAAM;;CAAc,EACnC,QAAQ,KAAK,GAAG,CACjB,EACA,QAAQ,GAAG,SAAU,CAAQ,EAC7B,QAAQ,GAAG,UAAW,CAAQ,EAG9B,IAAM,EAAS,MAAQ,MAAM,GAAa,EAAK,EAAW,EAAI,GAAc,GACtE,EAAO,MAAM,EAAS,EAGtB,EAAc,EAAM,UAAuB,EAAO,SAClD,EAAW,EAAM,OAAoB,EAAO,MAC5C,EAAU,EAAM,YAAyB,EAAK,QAAQ,GAEtD,EAAW,EAAY,CAAU,EAClC,IACJ,QAAQ,MAAM,qBAAqB,GAAY,EAC/C,QAAQ,MAAM,cAAc,EAAY,KAAK,EAAI,QAAU,GAAG,yBAAyB,EACvF,QAAQ,KAAK,CAAC,GAGV,IACJ,QAAQ,MACP,kBAAkB,EAAS,KAAK,QAAQ,EAAS,OAAO,6BACzD,EACA,QAAQ,KAAK,CAAC,GAGf,IAAM,EAAQ,GAAU,EAAS,CAAU,EAC3C,GAAI,CAAC,EAAO,CACX,QAAQ,MAAM,kBAAkB,GAAS,EACzC,QAAQ,MAAM,mBAAmB,EACjC,IAAK,IAAM,KAAK,EAAO,OAAQ,GAAM,EAAE,WAAa,CAAU,EAC7D,QAAQ,MAAM,KAAK,EAAE,GAAG,KAAK,EAAE,MAAM,EAEtC,QAAQ,KAAK,CAAC,CACf,CAEA,IAAM,EAAM,QAAQ,IAAI,EAClB,EAAQ,GAAY,CAAG,EACvB,EAAS,GAAkB,EAAK,CAAK,EAGrC,EAAQ,EAAgB,EACxB,EAAU,EAAM,QACnB,EAAM,IAAI,EAAM,OAAiB,EACjC,EAAM,OAAO,EAAK,EAAM,GAAI,CAAU,EAErC,EAAM,SAAW,CAAC,IACrB,QAAQ,MAAM,sBAAsB,EAAM,SAAS,EACnD,QAAQ,KAAK,CAAC,GAGf,IAAM,EAAY,EAAS,GACrB,EAAmB,EAAM,SAAS,CAAS,EAE3C,EAAQ,IAAI,GAAM,CACvB,IAAK,EAAS,IACd,QACA,SACA,QAAS,EAAS,QAClB,SACA,QACA,SAAU,CACX,CAAC,EAGD,QAAQ,IAAI,SAAU,CAAQ,EAC9B,QAAQ,IAAI,UAAW,CAAQ,EAC/B,GAAM,CAAE,eAAgB,MAAM,OAAO,sBACrC,MAAM,EAAY,EAAO,EAAO,CAAS,CAC1C,CAEA,QAAQ,GAAG,qBAAuB,GAAW,CAC5C,QAAQ,MAAM,uBAAwB,CAAM,EAC5C,QAAQ,KAAK,CAAC,CACf,CAAC,EAED,GAAK,EAAE,MAAO,GAAM,CACnB,QAAQ,MAAM,SAAU,CAAC,EACzB,QAAQ,KAAK,CAAC,CACf,CAAC"}
|
|
1
|
+
{"version":3,"file":"main.mjs","names":["#abort","#resolve","#events","#done","#result","#doneResolve","#api","#model","#apiKey","#baseUrl","#system","#tools","#messages","#db","#open","#nextSeq"],"sources":["../src/provider/gemini.ts","../src/provider/openai.ts","../src/provider/stream.ts","../src/util.ts","../src/agent/loop.ts","../src/agent/agent.ts","../src/agent/prompt.ts","../src/session/store.ts","../src/commands/session.ts","../src/config/providers.ts","../src/config/store.ts","../src/tui/prompts.tsx","../src/onboarding/wizard.ts","../src/tools/fs.ts","../src/tools/git.ts","../src/tools/search.ts","../src/tools/shell.ts","../src/tools/web.ts","../src/tools/index.ts","../src/update.ts","../src/main.ts"],"sourcesContent":["import type {\n\tAssistantResult,\n\tContentPart,\n\tMsg,\n\tStopReason,\n\tStreamEvent,\n\tStreamFn,\n\tStreamOpts,\n\tToolDef,\n\tUsage,\n} from \"../types.ts\"\nimport { EventStream } from \"./stream.ts\"\n\ninterface GeminiPart {\n\ttext?: string\n\tthought?: boolean | string\n\tinline_data?: { mime_type: string; data: string }\n\tfunction_call?: { name: string; args: Record<string, unknown> }\n\tfunction_response?: { name: string; response: Record<string, unknown> }\n\tthought_signature?: string\n}\n\ninterface GeminiContent {\n\trole: \"user\" | \"model\"\n\tparts: GeminiPart[]\n}\n\n/**\n * Maps our internal Msg format to the Gemini 'Content' format.\n * Groups consecutive tool_result messages into a single Gemini message.\n */\nfunction msgsToGemini(messages: Msg[]): GeminiContent[] {\n\tconst contents: GeminiContent[] = []\n\n\tfor (const msg of messages) {\n\t\tif (msg.role === \"user\") {\n\t\t\tconst parts: GeminiPart[] =\n\t\t\t\ttypeof msg.content === \"string\"\n\t\t\t\t\t? [{ text: msg.content }]\n\t\t\t\t\t: msg.content.map((c) => {\n\t\t\t\t\t\t\tif (c.type === \"text\") return { text: c.text }\n\t\t\t\t\t\t\tif (c.type === \"image\") return { inline_data: { mime_type: c.mime, data: c.data } }\n\t\t\t\t\t\t\treturn { text: \"\" }\n\t\t\t\t\t\t})\n\t\t\tcontents.push({ role: \"user\", parts })\n\t\t} else if (msg.role === \"assistant\") {\n\t\t\tconst parts: GeminiPart[] = msg.content.map((c) => {\n\t\t\t\tif (c.type === \"text\") return { text: c.text, thought_signature: c.signature }\n\t\t\t\tif (c.type === \"thinking\")\n\t\t\t\t\treturn { thought: true, text: c.text, thought_signature: c.signature }\n\t\t\t\tif (c.type === \"tool_call\")\n\t\t\t\t\treturn { function_call: { name: c.name, args: c.args }, thought_signature: c.signature }\n\t\t\t\treturn { text: \"\" }\n\t\t\t})\n\t\t\tcontents.push({ role: \"model\", parts })\n\t\t} else if (msg.role === \"tool_result\") {\n\t\t\tconst part: GeminiPart = {\n\t\t\t\tfunction_response: {\n\t\t\t\t\tname: msg.tool,\n\t\t\t\t\tresponse: {\n\t\t\t\t\t\tcontent: msg.content\n\t\t\t\t\t\t\t.map((c) => (c.type === \"text\" ? c.text : JSON.stringify(c)))\n\t\t\t\t\t\t\t.join(\"\\n\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tconst last = contents[contents.length - 1]\n\t\t\t// Gemini requires alternating roles; multiple function_responses group into one 'user' message.\n\t\t\tif (last && last.role === \"user\" && last.parts.some((p) => p.function_response)) {\n\t\t\t\tlast.parts.push(part)\n\t\t\t} else {\n\t\t\t\tcontents.push({ role: \"user\", parts: [part] })\n\t\t\t}\n\t\t}\n\t}\n\n\treturn contents\n}\n\nfunction toolsToGemini(tools: ToolDef[]): unknown[] {\n\tif (tools.length === 0) return []\n\treturn [\n\t\t{\n\t\t\tfunction_declarations: tools.map((t) => ({\n\t\t\t\tname: t.name,\n\t\t\t\tdescription: t.description,\n\t\t\t\tparameters: t.parameters,\n\t\t\t})),\n\t\t},\n\t]\n}\n\nexport const streamGemini: StreamFn = (\n\topts: StreamOpts,\n): EventStream<StreamEvent, AssistantResult> => {\n\tconst es = new EventStream<StreamEvent, AssistantResult>()\n\n\t;(async () => {\n\t\ttry {\n\t\t\tconst baseUrl = opts.baseUrl || \"https://generativelanguage.googleapis.com\"\n\t\t\tconst url = `${baseUrl}/v1beta/models/${opts.model.id}:streamGenerateContent?alt=sse&key=${opts.apiKey}`\n\n\t\t\tconst body = {\n\t\t\t\tcontents: msgsToGemini(opts.messages),\n\t\t\t\tsystem_instruction: opts.system ? { parts: [{ text: opts.system }] } : undefined,\n\t\t\t\ttools: opts.tools.length > 0 ? toolsToGemini(opts.tools) : undefined,\n\t\t\t\tgenerationConfig: {\n\t\t\t\t\tthinkingConfig: opts.model.supportsThinking ? { thinkingLevel: \"low\" } : undefined,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\"Api-Revision\": \"2026-05-20\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(body),\n\t\t\t\tsignal: opts.signal,\n\t\t\t})\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text()\n\t\t\t\tlet msg = text\n\t\t\t\ttry {\n\t\t\t\t\tconst json = JSON.parse(text)\n\t\t\t\t\tmsg = json.error?.message || json.message || text\n\t\t\t\t} catch {\n\t\t\t\t\t/* use raw text */\n\t\t\t\t}\n\n\t\t\t\tconst errorMsg = `Gemini Error (${response.status}): ${msg}`\n\t\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\t\tes.finish({\n\t\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\t\tstop: \"error\",\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst reader = response.body?.getReader()\n\t\t\tif (!reader) {\n\t\t\t\tes.finish({ content: [], usage: { in: 0, out: 0 }, stop: \"error\" })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst decoder = new TextDecoder()\n\t\t\tlet buffer = \"\"\n\t\t\tlet usage: Usage = { in: 0, out: 0 }\n\t\t\tlet stop: StopReason = \"stop\"\n\t\t\tconst content: ContentPart[] = []\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\tconst lines = buffer.split(\"\\n\")\n\t\t\t\tbuffer = lines.pop() ?? \"\"\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst trimmed = line.trim()\n\t\t\t\t\tif (!trimmed?.startsWith(\"data: \")) continue\n\t\t\t\t\tconst data = trimmed.slice(6)\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst chunk = JSON.parse(data)\n\t\t\t\t\t\tconst candidate = chunk.candidates?.[0]\n\n\t\t\t\t\t\t// Handle usage metadata\n\t\t\t\t\t\tif (chunk.usageMetadata) {\n\t\t\t\t\t\t\tusage = {\n\t\t\t\t\t\t\t\tin: chunk.usageMetadata.promptTokenCount || usage.in,\n\t\t\t\t\t\t\t\tout: chunk.usageMetadata.candidatesTokenCount || usage.out,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tes.push({ type: \"usage\", usage })\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!candidate) continue\n\n\t\t\t\t\t\t// Map finish reason\n\t\t\t\t\t\tif (candidate.finishReason) {\n\t\t\t\t\t\t\tconst reason = candidate.finishReason\n\t\t\t\t\t\t\tif (reason === \"STOP\") stop = \"stop\"\n\t\t\t\t\t\t\telse if (reason === \"MAX_TOKENS\") stop = \"length\"\n\t\t\t\t\t\t\telse if (reason === \"SAFETY\" || reason === \"RECITATION\" || reason === \"OTHER\")\n\t\t\t\t\t\t\t\tstop = \"error\"\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst parts = candidate.content?.parts\n\t\t\t\t\t\tif (parts) {\n\t\t\t\t\t\t\tfor (const part of parts) {\n\t\t\t\t\t\t\t\tconst sig = part.thought_signature || part.thoughtSignature\n\n\t\t\t\t\t\t\t\t// Handle text and thinking deltas\n\t\t\t\t\t\t\t\tif (part.text) {\n\t\t\t\t\t\t\t\t\tif (part.thought === true || typeof part.thought === \"string\") {\n\t\t\t\t\t\t\t\t\t\tconst thoughtText = typeof part.thought === \"string\" ? part.thought : part.text\n\t\t\t\t\t\t\t\t\t\tes.push({ type: \"thinking_delta\", text: thoughtText })\n\t\t\t\t\t\t\t\t\t\tconst last = content[content.length - 1]\n\t\t\t\t\t\t\t\t\t\tif (last?.type === \"thinking\") {\n\t\t\t\t\t\t\t\t\t\t\tlast.text += thoughtText\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tcontent.push({ type: \"thinking\", text: thoughtText, signature: sig })\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tes.push({ type: \"text_delta\", text: part.text })\n\t\t\t\t\t\t\t\t\t\tconst last = content[content.length - 1]\n\t\t\t\t\t\t\t\t\t\tif (last?.type === \"text\") {\n\t\t\t\t\t\t\t\t\t\t\tlast.text += part.text\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tcontent.push({ type: \"text\", text: part.text, signature: sig })\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Handle function calls (can be snake_case or camelCase in some API versions)\n\t\t\t\t\t\t\t\tconst fc = part.functionCall || part.function_call\n\t\t\t\t\t\t\t\tif (fc) {\n\t\t\t\t\t\t\t\t\tconst name = fc.name\n\t\t\t\t\t\t\t\t\tconst args = (fc.args as Record<string, unknown>) || {}\n\t\t\t\t\t\t\t\t\tconst id = `call_${Math.random().toString(36).slice(2, 9)}`\n\n\t\t\t\t\t\t\t\t\tconst toolCall: ContentPart = {\n\t\t\t\t\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\t\t\targs,\n\t\t\t\t\t\t\t\t\t\tsignature: sig,\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tcontent.push(toolCall)\n\t\t\t\t\t\t\t\t\tes.push({ type: \"tool_call\", call: toolCall })\n\t\t\t\t\t\t\t\t\tstop = \"tool_use\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (_e) {\n\t\t\t\t\t\tif (data.trim() !== \"\" && data.trim() !== \"[DONE]\") {\n\t\t\t\t\t\t\t// skip noise\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tes.finish({ content, usage, stop })\n\t\t} catch (e) {\n\t\t\tif (opts.signal?.aborted) return\n\t\t\tconst errorMsg = `Gemini Network/Request Error: ${e instanceof Error ? e.message : String(e)}`\n\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\tes.finish({\n\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\tstop: \"error\",\n\t\t\t})\n\t\t}\n\t})()\n\n\treturn es\n}\n","import type {\n\tAssistantResult,\n\tMsg,\n\tStopReason,\n\tStreamEvent,\n\tStreamFn,\n\tStreamOpts,\n\tToolDef,\n\tUsage,\n} from \"../types.ts\"\nimport { EventStream } from \"./stream.ts\"\n\nfunction msgToOpenAI(msg: Msg): Record<string, unknown> {\n\tif (msg.role === \"user\") {\n\t\treturn {\n\t\t\trole: \"user\",\n\t\t\tcontent:\n\t\t\t\ttypeof msg.content === \"string\"\n\t\t\t\t\t? msg.content\n\t\t\t\t\t: msg.content.map((c) => {\n\t\t\t\t\t\t\tif (c.type === \"text\") return { type: \"text\", text: c.text }\n\t\t\t\t\t\t\tif (c.type === \"image\")\n\t\t\t\t\t\t\t\treturn { type: \"image_url\", image_url: { url: `data:${c.mime};base64,${c.data}` } }\n\t\t\t\t\t\t\treturn { type: \"text\", text: \"\" }\n\t\t\t\t\t\t}),\n\t\t}\n\t}\n\tif (msg.role === \"assistant\") {\n\t\tconst textParts: string[] = []\n\t\tconst toolCalls: unknown[] = []\n\n\t\tfor (const c of msg.content) {\n\t\t\tif (c.type === \"text\") textParts.push(c.text)\n\t\t\t// thinking parts are internal — never sent back to the API\n\t\t\tif (c.type === \"tool_call\")\n\t\t\t\ttoolCalls.push({\n\t\t\t\t\ttype: \"function\",\n\t\t\t\t\tid: c.id,\n\t\t\t\t\tfunction: { name: c.name, arguments: JSON.stringify(c.args) },\n\t\t\t\t})\n\t\t}\n\n\t\tconst result: Record<string, unknown> = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: textParts.length > 0 ? textParts.join(\"\") : null,\n\t\t}\n\t\tif (toolCalls.length > 0) result.tool_calls = toolCalls\n\t\treturn result\n\t}\n\t// tool_result\n\tif (msg.role === \"tool_result\") {\n\t\treturn {\n\t\t\trole: \"tool\",\n\t\t\ttool_call_id: msg.callId,\n\t\t\tcontent: msg.content.map((c) => (c.type === \"text\" ? c.text : JSON.stringify(c))).join(\"\\n\"),\n\t\t}\n\t}\n\treturn { role: \"user\", content: \"\" }\n}\n\nfunction toolsToOpenAI(tools: ToolDef[]): unknown[] {\n\treturn tools.map((t) => ({\n\t\ttype: \"function\",\n\t\tfunction: {\n\t\t\tname: t.name,\n\t\t\tdescription: t.description,\n\t\t\tparameters: t.parameters,\n\t\t},\n\t}))\n}\n\nexport const streamOpenAI: StreamFn = (\n\topts: StreamOpts,\n): EventStream<StreamEvent, AssistantResult> => {\n\tconst es = new EventStream<StreamEvent, AssistantResult>()\n\n\t;(async () => {\n\t\ttry {\n\t\t\tconst body = {\n\t\t\t\tmodel: opts.model.id,\n\t\t\t\tmessages: [{ role: \"system\", content: opts.system }, ...opts.messages.map(msgToOpenAI)],\n\t\t\t\ttools: opts.tools.length > 0 ? toolsToOpenAI(opts.tools) : undefined,\n\t\t\t\tstream: true,\n\t\t\t}\n\n\t\t\tconst response = await fetch(`${opts.baseUrl}/chat/completions`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${opts.apiKey}`,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(body),\n\t\t\t\tsignal: opts.signal,\n\t\t\t})\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text()\n\t\t\t\tconst errorMsg = `API error ${response.status}: ${text}`\n\t\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\t\tes.finish({\n\t\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\t\tstop: \"error\",\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst reader = response.body?.getReader()\n\t\t\tif (!reader) {\n\t\t\t\tes.finish({ content: [], usage: { in: 0, out: 0 }, stop: \"error\" })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst decoder = new TextDecoder()\n\t\t\tlet buffer = \"\"\n\t\t\tconst currentToolCalls = new Map<number, { id: string; name: string; args: string }>()\n\t\t\tlet usage: Usage = { in: 0, out: 0 }\n\t\t\tlet textContent = \"\"\n\t\t\tlet stop = \"stop\"\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true })\n\t\t\t\tconst lines = buffer.split(\"\\n\")\n\t\t\t\tbuffer = lines.pop() ?? \"\"\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tconst trimmed = line.trim()\n\t\t\t\t\tif (!trimmed?.startsWith(\"data: \")) continue\n\t\t\t\t\tconst data = trimmed.slice(6)\n\t\t\t\t\tif (data === \"[DONE]\") continue\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst chunk = JSON.parse(data)\n\t\t\t\t\t\tconst delta = chunk.choices?.[0]?.delta\n\t\t\t\t\t\tif (!delta) continue\n\n\t\t\t\t\t\tif (delta.content) {\n\t\t\t\t\t\t\tes.push({ type: \"text_delta\", text: delta.content })\n\t\t\t\t\t\t\ttextContent += delta.content\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (delta.tool_calls) {\n\t\t\t\t\t\t\tfor (const tc of delta.tool_calls) {\n\t\t\t\t\t\t\t\tconst idx = tc.index ?? 0\n\t\t\t\t\t\t\t\tif (!currentToolCalls.has(idx)) {\n\t\t\t\t\t\t\t\t\tcurrentToolCalls.set(idx, {\n\t\t\t\t\t\t\t\t\t\tid: tc.id ?? \"\",\n\t\t\t\t\t\t\t\t\t\tname: tc.function?.name ?? \"\",\n\t\t\t\t\t\t\t\t\t\targs: \"\",\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst existing = currentToolCalls.get(idx)!\n\t\t\t\t\t\t\t\tif (tc.id) existing.id = tc.id\n\t\t\t\t\t\t\t\tif (tc.function?.name) existing.name = tc.function.name\n\t\t\t\t\t\t\t\tif (tc.function?.arguments) existing.args += tc.function.arguments\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (chunk.usage) {\n\t\t\t\t\t\t\tusage = {\n\t\t\t\t\t\t\t\tin: chunk.usage.prompt_tokens ?? 0,\n\t\t\t\t\t\t\t\tout: chunk.usage.completion_tokens ?? 0,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tes.push({ type: \"usage\", usage })\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst finishReason = chunk.choices?.[0]?.finish_reason\n\t\t\t\t\t\tif (finishReason) stop = finishReason\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Skip malformed JSON chunks\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst content: AssistantResult[\"content\"] = []\n\t\t\tif (textContent) {\n\t\t\t\tcontent.push({ type: \"text\", text: textContent })\n\t\t\t}\n\t\t\tfor (const [, tc] of currentToolCalls) {\n\t\t\t\tcontent.push({\n\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\tid: tc.id,\n\t\t\t\t\tname: tc.name,\n\t\t\t\t\targs: JSON.parse(tc.args || \"{}\"),\n\t\t\t\t})\n\t\t\t\tes.push({\n\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\tcall: {\n\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\tid: tc.id,\n\t\t\t\t\t\tname: tc.name,\n\t\t\t\t\t\targs: JSON.parse(tc.args || \"{}\"),\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tstop = \"tool_use\"\n\t\t\t}\n\n\t\t\tes.finish({ content, usage, stop: stop as StopReason })\n\t\t} catch (e) {\n\t\t\tif (opts.signal?.aborted) return\n\t\t\tconst errorMsg = `Unexpected error: ${e instanceof Error ? e.message : String(e)}`\n\t\t\tes.push({ type: \"text_delta\", text: errorMsg })\n\t\t\tes.finish({\n\t\t\t\tcontent: [{ type: \"text\", text: errorMsg }],\n\t\t\t\tusage: { in: 0, out: 0 },\n\t\t\t\tstop: \"error\",\n\t\t\t})\n\t\t}\n\t})()\n\n\treturn es\n}\n","import type { AgentEvent, ApiFormat, AssistantResult, StreamFn, StreamOpts } from \"../types.ts\"\nimport { streamGemini } from \"./gemini.ts\"\nimport { streamOpenAI } from \"./openai.ts\"\n\nexport type { AssistantResult, StreamEvent, StreamFn, StreamOpts } from \"../types.ts\"\n\n/*\n * Push-based async event stream.\n *\n * Producers call push()/finish(). Consumers iterate with for-await-of.\n * Backpressure is implicit: push() resolves immediately; the iterator\n * awaits the next value only when the consumer asks for it.\n */\nexport class EventStream<T, R> {\n\t#events: T[] = []\n\t#done = false\n\t#result?: R\n\t#resolve?: (value: T) => void\n\t#doneResolve?: (value: R) => void\n\t#abort = false\n\n\tpush(event: T): void {\n\t\tif (this.#abort) return\n\t\t// If a consumer is already waiting, deliver directly — skip the queue\n\t\tif (this.#resolve) {\n\t\t\tconst resolve = this.#resolve\n\t\t\tthis.#resolve = undefined\n\t\t\tresolve(event)\n\t\t} else {\n\t\t\tthis.#events.push(event)\n\t\t}\n\t}\n\n\tfinish(result: R): void {\n\t\tthis.#done = true\n\t\tthis.#result = result\n\t\t// Wake up a suspended iterator so it can see done=true and exit\n\t\tif (this.#resolve) {\n\t\t\t// undefined is a sentinel — the iterator loop checks done after waking\n\t\t\tthis.#resolve(undefined as T)\n\t\t}\n\t\tif (this.#doneResolve) {\n\t\t\tthis.#doneResolve(result)\n\t\t}\n\t}\n\n\tabort(): void {\n\t\tthis.#abort = true\n\t\tthis.#done = true\n\t\tif (this.#resolve) {\n\t\t\tthis.#resolve(undefined as T)\n\t\t}\n\t\tif (this.#doneResolve) {\n\t\t\tthis.#doneResolve(undefined as R)\n\t\t}\n\t}\n\n\tasync *[Symbol.asyncIterator](): AsyncGenerator<T> {\n\t\twhile (!this.#done || this.#events.length > 0) {\n\t\t\tif (this.#events.length > 0) {\n\t\t\t\tyield this.#events.shift() as T\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (this.#done) break\n\t\t\tconst item = await new Promise<T | undefined>((resolve) => {\n\t\t\t\tthis.#resolve = resolve as (value: T) => void\n\t\t\t})\n\t\t\tif (item !== undefined) {\n\t\t\t\tyield item\n\t\t\t}\n\t\t}\n\t}\n\n\tget result(): R | undefined {\n\t\treturn this.#result\n\t}\n\n\tget isDone(): boolean {\n\t\treturn this.#done\n\t}\n}\n\n// Internal map of registered provider implementations\nconst registry = new Map<ApiFormat, StreamFn>([\n\t[\"openai\", streamOpenAI],\n\t[\"gemini\", streamGemini],\n])\n\nexport function register(api: ApiFormat, fn: StreamFn): void {\n\tregistry.set(api, fn)\n}\n\n// Bridges provider-specific StreamEvents into AgentEvents so the loop and TUI deal with one type.\nexport function stream(opts: StreamOpts): EventStream<AgentEvent, AssistantResult> {\n\tconst fn = registry.get(opts.api)\n\tif (!fn) throw new Error(`No provider registered for API format: ${opts.api}`)\n\n\t// Bridge layer: converts provider-specific StreamEvents into the agent's\n\t// AgentEvent shape, so the loop and TUI only deal with one event type.\n\tconst providerStream = fn(opts)\n\tconst agentStream = new EventStream<AgentEvent, AssistantResult>()\n\n\t;(async () => {\n\t\tfor await (const event of providerStream) {\n\t\t\tif (event.type === \"text_delta\") {\n\t\t\t\tagentStream.push({ type: \"text_delta\", text: event.text ?? \"\" })\n\t\t\t} else if (event.type === \"thinking_delta\") {\n\t\t\t\tagentStream.push({ type: \"thinking_delta\", text: event.text ?? \"\" })\n\t\t\t} else if (event.type === \"tool_call\" && event.call) {\n\t\t\t\tagentStream.push({\n\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\tcall: {\n\t\t\t\t\t\ttype: \"tool_call\",\n\t\t\t\t\t\tid: event.call.id,\n\t\t\t\t\t\tname: event.call.name,\n\t\t\t\t\t\targs: event.call.args,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t} else if (event.type === \"usage\" && event.usage) {\n\t\t\t\tagentStream.push({ type: \"usage\", usage: event.usage })\n\t\t\t}\n\t\t}\n\n\t\tconst res = providerStream.result\n\t\tif (res) {\n\t\t\tagentStream.finish(res)\n\t\t} else {\n\t\t\t// Fallback for unexpected closure\n\t\t\tagentStream.finish({ content: [], usage: { in: 0, out: 0 }, stop: \"stop\" })\n\t\t}\n\t})()\n\n\treturn agentStream\n}\n\nexport function getRegisteredApis(): ApiFormat[] {\n\treturn [...registry.keys()]\n}\n","import { isAbsolute, relative } from \"node:path\"\nimport chalk from \"chalk\"\nimport type { Msg, TextPart } from \"./types.ts\"\n\n// ~4 chars per token for English/code. Close enough for capacity warnings.\nexport function estimateTokens(messages: Msg[]): number {\n\tlet chars = 0\n\tfor (const msg of messages) {\n\t\tif (typeof msg.content === \"string\") {\n\t\t\tchars += msg.content.length\n\t\t} else if (Array.isArray(msg.content)) {\n\t\t\tfor (const part of msg.content) {\n\t\t\t\tif (part.type === \"text\") chars += part.text.length\n\t\t\t}\n\t\t}\n\t}\n\treturn Math.ceil(chars / 4)\n}\n\nexport function textPart(s: string): TextPart {\n\treturn { type: \"text\", text: s }\n}\n\nexport function getRelativeIfInside(cwd: string, filePath: string): string {\n\tif (filePath === cwd || filePath.startsWith(`${cwd}/`)) {\n\t\treturn relative(cwd, filePath) || \".\"\n\t}\n\treturn filePath\n}\n\nexport function makeRelative(val: string): string {\n\tif (typeof val !== \"string\") return val\n\n\tlet pathVal = val\n\tlet prefix = \"\"\n\tif (val.startsWith(\"file://\")) {\n\t\tpathVal = val.slice(7)\n\t\tprefix = \"file://\"\n\t}\n\n\tif (isAbsolute(pathVal)) {\n\t\tconst cwd = process.cwd()\n\t\treturn prefix + getRelativeIfInside(cwd, pathVal)\n\t}\n\treturn val\n}\n\nexport function formatToolArgs(\n\targs: Record<string, unknown> | undefined,\n\tuseChalk = false,\n): string {\n\tif (!args) return \"\"\n\treturn Object.entries(args)\n\t\t.map(([k, v]) => {\n\t\t\tconst val = typeof v === \"string\" ? makeRelative(v) : JSON.stringify(v)\n\t\t\tconst valStr = val.length > 40 ? `${val.slice(0, 40)}…` : val\n\t\t\tconst keyStr = useChalk ? chalk.dim(`${k}:`) : `${k}:`\n\t\t\treturn `${keyStr} ${valStr}`\n\t\t})\n\t\t.join(\" \")\n}\n","/**\n * Core agent loop that orchestrates model interaction and tool execution.\n * Handles turns, tool routing, safety checks, and event streaming.\n */\nimport { EventStream, stream } from \"../provider/stream.ts\"\nimport type {\n\tAgentEvent,\n\tAssistantMsg,\n\tLoopCtx,\n\tLoopOpts,\n\tMsg,\n\tToolCallPart,\n\tToolResultMsg,\n} from \"../types.ts\"\nimport { estimateTokens, textPart } from \"../util.ts\"\n\n// Safety cap so a misbehaving model can't loop forever\nconst MAX_TURNS = 50\n\nconst isToolCall = (c: unknown): c is ToolCallPart =>\n\ttypeof c === \"object\" && c !== null && (c as ToolCallPart).type === \"tool_call\"\n\n/**\n * Start a long-running agent session that yields an EventStream of updates.\n */\nexport function run(\n\tinput: string,\n\tctx: LoopCtx,\n\topts: LoopOpts,\n\tsignal?: AbortSignal,\n): EventStream<AgentEvent, Msg[]> {\n\tconst es = new EventStream<AgentEvent, Msg[]>()\n\tconst out: Msg[] = []\n\tconst maxTurns = opts.maxTurns ?? MAX_TURNS\n\n\tconst userMsg: Msg = { role: \"user\", content: input, ts: Date.now() }\n\tlet activeCtx: LoopCtx = { ...ctx, messages: [...ctx.messages, userMsg] }\n\tout.push(userMsg)\n\n\tconst tick = async () => {\n\t\tes.push({ type: \"start\" })\n\n\t\ttry {\n\t\t\tlet turns = 0\n\t\t\twhile (turns < maxTurns) {\n\t\t\t\tif (signal?.aborted) break\n\n\t\t\t\tturns++\n\t\t\t\tes.push({ type: \"turn\" })\n\n\t\t\t\t// Warn before hitting the hard limit so the caller can compact/summarize\n\t\t\t\tconst approxTokens = estimateTokens(activeCtx.messages)\n\t\t\t\tif (approxTokens > opts.model.contextWindow * 0.9) {\n\t\t\t\t\tes.push({\n\t\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\t\ttext: `[warning] Approaching context limit (~${Math.round(approxTokens / 1000)}k / ${Math.round(opts.model.contextWindow / 1000)}k tokens)`,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tconst reply = await getReply(activeCtx, opts, es, signal)\n\t\t\t\tout.push(reply)\n\t\t\t\tactiveCtx = { ...activeCtx, messages: [...activeCtx.messages, reply] }\n\t\t\t\tes.push({ type: \"assistant_msg\", msg: reply })\n\n\t\t\t\tif (reply.stop === \"error\" || reply.stop === \"aborted\") {\n\t\t\t\t\tes.push({ type: \"turn_end\", msg: reply, results: [] })\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tconst calls = reply.content.filter(isToolCall)\n\t\t\t\tif (calls.length === 0) {\n\t\t\t\t\tes.push({ type: \"turn_end\", msg: reply, results: [] })\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\t// Execute tool calls\n\t\t\t\tconst results: ToolResultMsg[] = []\n\t\t\t\tfor (const call of calls) {\n\t\t\t\t\tif (signal?.aborted) break\n\n\t\t\t\t\tconst tool = activeCtx.tools.find((t) => t.def.name === call.name)\n\t\t\t\t\tif (!tool) {\n\t\t\t\t\t\tconst errResult: ToolResultMsg = {\n\t\t\t\t\t\t\trole: \"tool_result\",\n\t\t\t\t\t\t\tcallId: call.id,\n\t\t\t\t\t\t\ttool: call.name,\n\t\t\t\t\t\t\tcontent: [textPart(`Unknown tool: ${call.name}`)],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresults.push(errResult)\n\t\t\t\t\t\tactiveCtx = { ...activeCtx, messages: [...activeCtx.messages, errResult] }\n\t\t\t\t\t\tout.push(errResult)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// beforeTool lets callers block dangerous operations (e.g. rm -rf)\n\t\t\t\t\tconst blocked = await opts.beforeTool?.(call, call.args, activeCtx)\n\t\t\t\t\tif (blocked?.block) {\n\t\t\t\t\t\tconst blockResult: ToolResultMsg = {\n\t\t\t\t\t\t\trole: \"tool_result\",\n\t\t\t\t\t\t\tcallId: call.id,\n\t\t\t\t\t\t\ttool: call.name,\n\t\t\t\t\t\t\tcontent: [textPart(blocked.reason ?? \"Blocked\")],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresults.push(blockResult)\n\t\t\t\t\t\tactiveCtx = { ...activeCtx, messages: [...activeCtx.messages, blockResult] }\n\t\t\t\t\t\tout.push(blockResult)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// Execute\n\t\t\t\t\tconst result = await tool.execute(call.args, signal)\n\t\t\t\t\tconst toolMsg: ToolResultMsg = {\n\t\t\t\t\t\trole: \"tool_result\",\n\t\t\t\t\t\tcallId: call.id,\n\t\t\t\t\t\ttool: call.name,\n\t\t\t\t\t\targs: call.args,\n\t\t\t\t\t\tcontent: result.content,\n\t\t\t\t\t\tisError: result.isError,\n\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t}\n\n\t\t\t\t\tresults.push(toolMsg)\n\t\t\t\t\tactiveCtx = { ...activeCtx, messages: [...activeCtx.messages, toolMsg] }\n\t\t\t\t\tout.push(toolMsg)\n\t\t\t\t\tes.push({ type: \"tool_result\", callId: call.id, result: toolMsg, args: call.args })\n\n\t\t\t\t\tawait opts.afterTool?.(call, toolMsg, activeCtx)\n\t\t\t\t}\n\n\t\t\t\tes.push({ type: \"turn_end\", msg: reply, results })\n\t\t\t}\n\n\t\t\tif (turns >= maxTurns) {\n\t\t\t\tes.push({\n\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\ttext: `[max turns reached (${maxTurns})]`,\n\t\t\t\t})\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tif ((e as Error).name === \"AbortError\") {\n\t\t\t\tes.finish(out)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthrow e\n\t\t}\n\n\t\tes.finish(out)\n\t}\n\n\ttick()\n\treturn es\n}\n\nasync function getReply(\n\tctx: LoopCtx,\n\topts: LoopOpts,\n\tes: EventStream<AgentEvent, Msg[]>,\n\tsignal?: AbortSignal,\n): Promise<AssistantMsg> {\n\tconst providerStream = stream({\n\t\tapi: opts.api,\n\t\tmodel: opts.model,\n\t\tapiKey: opts.apiKey,\n\t\tbaseUrl: opts.baseUrl,\n\t\tsystem: ctx.system,\n\t\tmessages: ctx.messages,\n\t\ttools: ctx.tools.map((t) => t.def),\n\t\tsignal,\n\t})\n\n\tconst content: AssistantMsg[\"content\"] = []\n\tlet usage = { in: 0, out: 0 }\n\n\t// Accumulate content and proxy events to the outer stream\n\tfor await (const ev of providerStream) {\n\t\tif (ev.type === \"text_delta\" && ev.text) {\n\t\t\tconst last = content[content.length - 1]\n\t\t\tif (last?.type === \"text\") {\n\t\t\t\tlast.text += ev.text\n\t\t\t} else {\n\t\t\t\tcontent.push(textPart(ev.text))\n\t\t\t}\n\t\t\tes.push({ type: \"text_delta\", text: ev.text })\n\t\t} else if (ev.type === \"thinking_delta\" && ev.text) {\n\t\t\tconst last = content[content.length - 1]\n\t\t\tif (last?.type === \"thinking\") {\n\t\t\t\tlast.text += ev.text\n\t\t\t} else {\n\t\t\t\tcontent.push({ type: \"thinking\", text: ev.text })\n\t\t\t}\n\t\t\tes.push({ type: \"thinking_delta\", text: ev.text })\n\t\t} else if (ev.type === \"tool_call\" && ev.call) {\n\t\t\tcontent.push(ev.call)\n\t\t\tes.push({ type: \"tool_call\", call: ev.call })\n\t\t} else if (ev.type === \"usage\" && ev.usage) {\n\t\t\tusage = ev.usage\n\t\t\tes.push({ type: \"usage\", usage })\n\t\t}\n\t}\n\n\tconst rawContent =\n\t\tproviderStream.result?.content && providerStream.result.content.length > 0\n\t\t\t? providerStream.result.content\n\t\t\t: content\n\n\tconst hasTool = rawContent.some((p) => p.type === \"tool_call\")\n\tconst cleanedContent = hasTool\n\t\t? rawContent.filter((p) => p.type !== \"text\" || p.text.trim().length > 0)\n\t\t: rawContent\n\n\tconst res = providerStream.result\n\tif (res) {\n\t\treturn {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: cleanedContent,\n\t\t\tmodel: opts.model.id,\n\t\t\tprovider: opts.model.provider,\n\t\t\tusage: res.usage,\n\t\t\tstop: res.stop,\n\t\t\tts: Date.now(),\n\t\t}\n\t}\n\n\treturn {\n\t\trole: \"assistant\",\n\t\tcontent: cleanedContent,\n\t\tmodel: opts.model.id,\n\t\tprovider: opts.model.provider,\n\t\tusage,\n\t\tstop: cleanedContent.some((c) => c.type === \"tool_call\") ? \"tool_use\" : \"stop\",\n\t\tts: Date.now(),\n\t}\n}\n","import type { EventStream } from \"../provider/stream.ts\"\nimport type { AgentEvent, ApiFormat, LoopCtx, LoopOpts, Model, Msg, Tool } from \"../types.ts\"\nimport { run } from \"./loop.ts\"\n\nexport class Agent {\n\t#api: ApiFormat\n\t#model: Model\n\t#system: string\n\t#messages: Msg[] = []\n\t#tools: Tool[]\n\t#apiKey: string\n\t#baseUrl: string\n\n\tconstructor(opts: {\n\t\tapi: ApiFormat\n\t\tmodel: Model\n\t\tapiKey: string\n\t\tbaseUrl: string\n\t\tsystem: string\n\t\ttools: Tool[]\n\t\tmessages?: Msg[]\n\t}) {\n\t\tthis.#api = opts.api\n\t\tthis.#model = opts.model\n\t\tthis.#apiKey = opts.apiKey\n\t\tthis.#baseUrl = opts.baseUrl\n\t\tthis.#system = opts.system\n\t\tthis.#tools = opts.tools\n\t\tthis.#messages = opts.messages ?? []\n\t}\n\n\tget model(): Model {\n\t\treturn this.#model\n\t}\n\n\tget messages(): Msg[] {\n\t\treturn this.#messages\n\t}\n\n\tget tools(): Tool[] {\n\t\treturn this.#tools\n\t}\n\n\tget apiKey(): string {\n\t\treturn this.#apiKey\n\t}\n\n\tget baseUrl(): string {\n\t\treturn this.#baseUrl\n\t}\n\n\tupdateConfig(opts: { api: ApiFormat; model: Model; apiKey: string; baseUrl: string }): void {\n\t\tthis.#api = opts.api\n\t\tthis.#model = opts.model\n\t\tthis.#apiKey = opts.apiKey\n\t\tthis.#baseUrl = opts.baseUrl\n\t}\n\n\tsetTools(tools: Tool[]): void {\n\t\tthis.#tools = tools\n\t}\n\n\tsetMessages(msgs: Msg[]): void {\n\t\tthis.#messages = msgs\n\t}\n\n\tsetModel(model: Model): void {\n\t\tthis.#model = model\n\t}\n\n\tprompt(input: string, signal?: AbortSignal): EventStream<AgentEvent, Msg[]> {\n\t\tconst ctx: LoopCtx = {\n\t\t\tsystem: this.#system,\n\t\t\tmessages: this.#messages,\n\t\t\ttools: this.#tools,\n\t\t}\n\n\t\tconst opts: LoopOpts = {\n\t\t\tapi: this.#api,\n\t\t\tmodel: this.#model,\n\t\t\tapiKey: this.#apiKey,\n\t\t\tbaseUrl: this.#baseUrl,\n\t\t}\n\n\t\treturn run(input, ctx, opts, signal)\n\t}\n}\n","/**\n * Logic for constructing the foundational system instruction given the environment and tools.\n */\n\nimport os from \"node:os\"\nimport type { Tool } from \"../types.ts\"\n\nexport function buildSystemPrompt(cwd: string, tools: Tool[]): string {\n\tconst toolList = tools.map((t) => `- ${t.def.name}: ${t.def.description}`).join(\"\\n\")\n\tconst platform = os.platform()\n\tconst arch = os.arch()\n\tconst release = os.release()\n\tconst shell = process.env.SHELL || \"unknown\"\n\n\treturn `You are Nova, an expert coding assistant. Help users with coding tasks using the tools available.\n\nFormat your responses with clean, standard markdown. Use headers (##, ###), bold text (**bold**), inline code (\\`code\\`), and code blocks (\\`\\`\\`lang) to make your output clear and readable in the terminal.\n\n# Tools\n\n${toolList}\n\n# Environment\n\n- Working directory: ${cwd}\n- Operating System: ${platform} (${release})\n- Architecture: ${arch}\n- Shell: ${shell}\n- Date: ${new Date().toISOString().split(\"T\")[0]}\n\n# Guidelines\n\n- Use tools to fulfill requests. Do not fabricate file contents.\n- Explain what you are doing and why before each tool call.\n- Use the \"bash\" tool for ls, git, and other shell operations.\n- Always read a file before editing it.\n- Prefer edit over write for existing files.\n- Run relevant tests after making changes.\n- If a command fails, read the error carefully before retrying.\n- For multi-file changes, plan first, then execute.\n- When done, briefly summarize what was changed.\n- Be concise and direct.\n\n# Safety\n\n- Never delete files outside the working directory.\n- Never run destructive commands unless the user explicitly confirms.\n- If unsure, ask for clarification.\n- Never expose API keys, tokens, or secrets.`\n}\n","import { unlinkSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport BetterSqlite3 from \"better-sqlite3\"\nimport type { Msg, Session } from \"../types.ts\"\n\nconst SCHEMA = `\nCREATE TABLE IF NOT EXISTS sessions (\n\tid TEXT PRIMARY KEY,\n\tcwd TEXT NOT NULL,\n\tmodel TEXT NOT NULL,\n\tprovider TEXT NOT NULL,\n\ttitle TEXT,\n\tcreated INTEGER NOT NULL,\n\tupdated INTEGER NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS messages (\n\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\tsession_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n\tseq INTEGER NOT NULL,\n\trole TEXT NOT NULL,\n\tcontent TEXT NOT NULL,\n\tts INTEGER NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id, seq);\n\nCREATE TABLE IF NOT EXISTS compactions (\n\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\tsession_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,\n\tsummary TEXT NOT NULL,\n\tfiles_read TEXT NOT NULL DEFAULT '[]',\n\tfiles_wrote TEXT NOT NULL DEFAULT '[]',\n\tseq_before INTEGER NOT NULL,\n\tts INTEGER NOT NULL\n);\n`\n\nfunction generateId(): string {\n\treturn `${Date.now().toString(36)}-${crypto.randomUUID().slice(0, 8)}`\n}\n\nexport class SessionStore {\n\t#db: BetterSqlite3.Database\n\n\tconstructor(dbPath: string) {\n\t\tthis.#db = SessionStore.#open(dbPath)\n\t}\n\n\t// Opens and fully initialises the DB. If anything throws (e.g. corrupt file),\n\t// the bad file is deleted and a fresh DB is created and returned.\n\tstatic #open(dbPath: string): BetterSqlite3.Database {\n\t\tconst init = (db: BetterSqlite3.Database) => {\n\t\t\tdb.pragma(\"journal_mode = WAL\")\n\t\t\tdb.pragma(\"foreign_keys = ON\")\n\t\t\tdb.exec(SCHEMA)\n\t\t\treturn db\n\t\t}\n\t\ttry {\n\t\t\treturn init(new BetterSqlite3(dbPath))\n\t\t} catch {\n\t\t\t// Delete the main DB and WAL sidecar files — all three must go or\n\t\t\t// SQLite will fail again trying to replay a corrupt WAL on reopen.\n\t\t\tfor (const f of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(f)\n\t\t\t\t} catch {\n\t\t\t\t\t// file may already be absent — ignore\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn init(new BetterSqlite3(dbPath))\n\t\t}\n\t}\n\n\tcreate(cwd: string, model: string, provider: string): Session {\n\t\tconst id = generateId()\n\t\tconst now = Date.now()\n\t\tthis.#db\n\t\t\t.prepare(\n\t\t\t\t\"INSERT INTO sessions (id, cwd, model, provider, title, created, updated) VALUES ($id, $cwd, $model, $provider, $title, $created, $updated)\",\n\t\t\t)\n\t\t\t.run({\n\t\t\t\tid: id,\n\t\t\t\tcwd: cwd,\n\t\t\t\tmodel: model,\n\t\t\t\tprovider: provider,\n\t\t\t\ttitle: null,\n\t\t\t\tcreated: now,\n\t\t\t\tupdated: now,\n\t\t\t})\n\t\treturn { id, cwd, model, provider, title: null, created: now, updated: now }\n\t}\n\n\tget(id: string): Session | null {\n\t\treturn (\n\t\t\t(this.#db\n\t\t\t\t.prepare(\n\t\t\t\t\t\"SELECT id, cwd, model, provider, title, created, updated FROM sessions WHERE id = $id\",\n\t\t\t\t)\n\t\t\t\t.get({ id: id }) as Session | null) ?? null\n\t\t)\n\t}\n\n\tlist(limit = 50): Session[] {\n\t\treturn this.#db\n\t\t\t.prepare(\n\t\t\t\t\"SELECT id, cwd, model, provider, title, created, updated FROM sessions ORDER BY updated DESC LIMIT $limit\",\n\t\t\t)\n\t\t\t.all({ limit: limit }) as Session[]\n\t}\n\n\tdelete(id: string): boolean {\n\t\tconst result = this.#db.prepare(\"DELETE FROM sessions WHERE id = $id\").run({ id: id })\n\t\treturn result.changes > 0\n\t}\n\n\tappend(sessionId: string, msg: Msg): void {\n\t\tconst seq = this.#nextSeq(sessionId)\n\t\tthis.#db\n\t\t\t.prepare(\n\t\t\t\t\"INSERT INTO messages (session_id, seq, role, content, ts) VALUES ($sid, $seq, $role, $content, $ts)\",\n\t\t\t)\n\t\t\t.run({\n\t\t\t\tsid: sessionId,\n\t\t\t\tseq: seq,\n\t\t\t\trole: msg.role,\n\t\t\t\tcontent: JSON.stringify(msg),\n\t\t\t\tts: msg.ts,\n\t\t\t})\n\t\tthis.#db\n\t\t\t.prepare(\"UPDATE sessions SET updated = $now WHERE id = $id\")\n\t\t\t.run({ now: Date.now(), id: sessionId })\n\t}\n\n\tappendMany(sessionId: string, msgs: Msg[]): void {\n\t\tconst tx = this.#db.transaction(() => {\n\t\t\tfor (const msg of msgs) {\n\t\t\t\tthis.append(sessionId, msg)\n\t\t\t}\n\t\t})\n\t\ttx()\n\t}\n\n\tmessages(sessionId: string): Msg[] {\n\t\tconst rows = this.#db\n\t\t\t.prepare(\"SELECT content FROM messages WHERE session_id = $sid ORDER BY seq ASC\")\n\t\t\t.all({ sid: sessionId }) as { content: string }[]\n\t\treturn rows.map((r) => JSON.parse(r.content) as Msg)\n\t}\n\n\tmessagesAfter(sessionId: string, afterSeq: number): Msg[] {\n\t\tconst rows = this.#db\n\t\t\t.prepare(\n\t\t\t\t\"SELECT content FROM messages WHERE session_id = $sid AND seq > $seq ORDER BY seq ASC\",\n\t\t\t)\n\t\t\t.all({ sid: sessionId, seq: afterSeq }) as { content: string }[]\n\t\treturn rows.map((r) => JSON.parse(r.content) as Msg)\n\t}\n\n\tsetTitle(sessionId: string, title: string): void {\n\t\tthis.#db\n\t\t\t.prepare(\"UPDATE sessions SET title = $title WHERE id = $id\")\n\t\t\t.run({ title: title, id: sessionId })\n\t}\n\n\tmessageCount(sessionId: string): number {\n\t\tconst row = this.#db\n\t\t\t.prepare(\"SELECT COUNT(*) as count FROM messages WHERE session_id = $sid\")\n\t\t\t.get({ sid: sessionId }) as { count: number }\n\t\treturn row.count\n\t}\n\n\tsaveCompaction(\n\t\tsessionId: string,\n\t\tsummary: string,\n\t\tfilesRead: string[],\n\t\tfilesWrote: string[],\n\t\tseqBefore: number,\n\t): void {\n\t\tthis.#db\n\t\t\t.prepare(\n\t\t\t\t\"INSERT INTO compactions (session_id, summary, files_read, files_wrote, seq_before, ts) VALUES ($sid, $summary, $read, $wrote, $seq, $ts)\",\n\t\t\t)\n\t\t\t.run({\n\t\t\t\tsid: sessionId,\n\t\t\t\tsummary: summary,\n\t\t\t\tread: JSON.stringify(filesRead),\n\t\t\t\twrote: JSON.stringify(filesWrote),\n\t\t\t\tseq: seqBefore,\n\t\t\t\tts: Date.now(),\n\t\t\t})\n\t}\n\n\tgetLatestCompaction(sessionId: string): { summary: string; seqBefore: number } | null {\n\t\treturn (\n\t\t\t(this.#db\n\t\t\t\t.prepare(\n\t\t\t\t\t\"SELECT summary, seq_before FROM compactions WHERE session_id = $sid ORDER BY ts DESC LIMIT 1\",\n\t\t\t\t)\n\t\t\t\t.get({ sid: sessionId }) as { summary: string; seqBefore: number } | null) ?? null\n\t\t)\n\t}\n\n\ttruncateBeforeSeq(sessionId: string, seq: number): void {\n\t\tthis.#db\n\t\t\t.prepare(\"DELETE FROM messages WHERE session_id = $sid AND seq < $seq\")\n\t\t\t.run({ sid: sessionId, seq: seq })\n\t}\n\n\tclose(): void {\n\t\tthis.#db.close()\n\t}\n\n\t#nextSeq(sessionId: string): number {\n\t\tconst row = this.#db\n\t\t\t.prepare(\"SELECT MAX(seq) as maxSeq FROM messages WHERE session_id = $sid\")\n\t\t\t.get({ sid: sessionId }) as { maxSeq: number | null }\n\t\treturn (row.maxSeq ?? 0) + 1\n\t}\n}\n\nlet _store: SessionStore | null = null\n\nexport function getSessionStore(dir?: string): SessionStore {\n\tif (_store) return _store\n\tconst dbPath = join(dir ?? join(process.env.HOME ?? \"~\", \".novacode\"), \"sessions.db\")\n\t_store = new SessionStore(dbPath)\n\treturn _store\n}\n","import { getSessionStore } from \"../session/store.ts\"\n\nexport async function handleSessionCommand(args: string[]): Promise<void> {\n\tconst store = getSessionStore()\n\tconst [subcommand, id] = args\n\n\tif (subcommand === \"list\" || subcommand === \"ls\") {\n\t\tconst sessions = store.list()\n\t\tif (sessions.length === 0) {\n\t\t\tconsole.log(\"No sessions found.\")\n\t\t\treturn\n\t\t}\n\n\t\tconsole.log(\"ID\".padEnd(25), \"MODEL\".padEnd(20), \"UPDATED\")\n\t\tconsole.log(\"-\".repeat(70))\n\t\tfor (const s of sessions) {\n\t\t\tconst date = new Date(s.updated).toLocaleString()\n\t\t\tconsole.log(s.id.padEnd(25), s.model.padEnd(20), date)\n\t\t}\n\t\treturn\n\t}\n\n\tif (subcommand === \"delete\" || subcommand === \"rm\") {\n\t\tif (!id) {\n\t\t\tconsole.error(\"Usage: novacode session delete <id>\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t\tconst success = store.delete(id)\n\t\tif (success) {\n\t\t\tconsole.log(`Deleted session: ${id}`)\n\t\t} else {\n\t\t\tconsole.error(`Session not found: ${id}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\t\treturn\n\t}\n\n\tconsole.error(\"Unknown session subcommand. Use 'list' or 'delete'.\")\n\tprocess.exit(1)\n}\n","import type { Model, ProviderDef } from \"../types.ts\"\n\nexport const PROVIDERS: ProviderDef[] = [\n\t{\n\t\tid: \"glm\",\n\t\tname: \"GLM (Z.AI)\",\n\t\tapi: \"openai\",\n\t\tbaseUrl: \"https://api.z.ai/api/coding/paas/v4\",\n\t\tenvKey: \"GLM_API_KEY\",\n\t},\n\t{\n\t\tid: \"gemini\",\n\t\tname: \"Gemini (Google)\",\n\t\tapi: \"gemini\",\n\t\tbaseUrl: \"https://generativelanguage.googleapis.com\",\n\t\tenvKey: \"GEMINI_API_KEY\",\n\t},\n\t{\n\t\tid: \"deepseek\",\n\t\tname: \"DeepSeek\",\n\t\tapi: \"openai\",\n\t\tbaseUrl: \"https://api.deepseek.com\",\n\t\tenvKey: \"DEEPSEEK_API_KEY\",\n\t},\n\t{\n\t\tid: \"openai\",\n\t\tname: \"OpenAI\",\n\t\tapi: \"openai\",\n\t\tbaseUrl: \"https://api.openai.com/v1\",\n\t\tenvKey: \"OPENAI_API_KEY\",\n\t},\n]\n\nexport const MODELS: Model[] = [\n\t// GLM\n\t{\n\t\tid: \"glm-5.1\",\n\t\tname: \"GLM-5.1\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-5\",\n\t\tname: \"GLM-5\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-5-turbo\",\n\t\tname: \"GLM-5 Turbo\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-4.7\",\n\t\tname: \"GLM-4.7\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-4.7-flash\",\n\t\tname: \"GLM-4.7 Flash (Free)\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"glm-4.5-flash\",\n\t\tname: \"GLM-4.5 Flash (Free)\",\n\t\tprovider: \"glm\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 4096,\n\t\tsupportsThinking: false,\n\t},\n\t// Gemini\n\t{\n\t\tid: \"gemini-3.5-flash\",\n\t\tname: \"Gemini 3.5 Flash\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-pro-preview\",\n\t\tname: \"Gemini 3.1 Pro Preview\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 2_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-pro-preview-customtools\",\n\t\tname: \"Gemini 3.1 Pro (Custom Tools)\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 2_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-flash-lite\",\n\t\tname: \"Gemini 3.1 Flash-Lite\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3.1-flash-lite-preview\",\n\t\tname: \"Gemini 3.1 Flash-Lite Preview\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-3-flash-preview\",\n\t\tname: \"Gemini 3 Flash Preview\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: true,\n\t},\n\t{\n\t\tid: \"gemini-2.5-pro\",\n\t\tname: \"Gemini 2.5 Pro\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 2_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"gemini-2.5-flash\",\n\t\tname: \"Gemini 2.5 Flash\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"gemini-2.5-flash-lite\",\n\t\tname: \"Gemini 2.5 Flash-Lite\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"gemini-2.5-computer-use-preview-10-2025\",\n\t\tname: \"Gemini 2.5 Computer Use\",\n\t\tprovider: \"gemini\",\n\t\tcontextWindow: 1_000_000,\n\t\tmaxTokens: 65_536,\n\t\tsupportsThinking: false,\n\t},\n\t// DeepSeek\n\t{\n\t\tid: \"deepseek-chat\",\n\t\tname: \"DeepSeek V3\",\n\t\tprovider: \"deepseek\",\n\t\tcontextWindow: 64_000,\n\t\tmaxTokens: 8_192,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"deepseek-reasoner\",\n\t\tname: \"DeepSeek R1\",\n\t\tprovider: \"deepseek\",\n\t\tcontextWindow: 64_000,\n\t\tmaxTokens: 8_192,\n\t\tsupportsThinking: true,\n\t},\n\t// OpenAI\n\t{\n\t\tid: \"gpt-4o\",\n\t\tname: \"GPT-4o\",\n\t\tprovider: \"openai\",\n\t\tcontextWindow: 128_000,\n\t\tmaxTokens: 16_384,\n\t\tsupportsThinking: false,\n\t},\n\t{\n\t\tid: \"o4-mini\",\n\t\tname: \"o4-mini\",\n\t\tprovider: \"openai\",\n\t\tcontextWindow: 200_000,\n\t\tmaxTokens: 100_000,\n\t\tsupportsThinking: true,\n\t},\n]\n\nexport function getProvider(id: string): ProviderDef | undefined {\n\treturn PROVIDERS.find((p) => p.id === id)\n}\n\nexport function getModelsForProvider(providerId: string): Model[] {\n\treturn MODELS.filter((m) => m.provider === providerId)\n}\n","import { chmod, mkdir, readFile, stat, writeFile } from \"node:fs/promises\"\nimport { join } from \"node:path\"\nimport type { NovaAuth, NovaConfig } from \"../types.ts\"\n\nconst NOVA_DIR = () => join(process.env.HOME ?? \"~\", \".novacode\")\nconst CONFIG_PATH = () => join(NOVA_DIR(), \"config.json\")\nconst AUTH_PATH = () => join(NOVA_DIR(), \"auth.json\")\n\nconst defaultConfig: NovaConfig = {\n\tprovider: \"\",\n\tmodel: \"\",\n}\n\nconst defaultAuth: NovaAuth = {\n\tapiKeys: {},\n}\n\nexport async function configExists(): Promise<boolean> {\n\ttry {\n\t\tawait stat(CONFIG_PATH())\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\nexport async function loadConfig(): Promise<NovaConfig> {\n\ttry {\n\t\tconst raw = JSON.parse(await readFile(CONFIG_PATH(), \"utf-8\"))\n\t\treturn { ...defaultConfig, ...raw }\n\t} catch {\n\t\treturn { ...defaultConfig }\n\t}\n}\n\nexport async function loadAuth(): Promise<NovaAuth> {\n\ttry {\n\t\tconst raw = JSON.parse(await readFile(AUTH_PATH(), \"utf-8\"))\n\t\treturn { ...defaultAuth, ...raw }\n\t} catch {\n\t\treturn { ...defaultAuth }\n\t}\n}\n\nasync function ensureDir(): Promise<void> {\n\tawait mkdir(NOVA_DIR(), { recursive: true })\n}\n\nexport async function saveConfig(config: NovaConfig): Promise<void> {\n\tawait ensureDir()\n\tawait writeFile(CONFIG_PATH(), JSON.stringify(config, null, 2))\n}\n\nexport async function saveAuth(auth: NovaAuth): Promise<void> {\n\tawait ensureDir()\n\tawait writeFile(AUTH_PATH(), JSON.stringify(auth, null, 2))\n\ttry {\n\t\tawait chmod(AUTH_PATH(), 0o600)\n\t} catch {\n\t\t// chmod may fail on some platforms, non-fatal\n\t}\n}\n\nexport function getNovaDir(): string {\n\treturn NOVA_DIR()\n}\n","import { Box, render, Text, useInput } from \"ink\"\nimport { useState } from \"react\"\n\ninterface SelectOption {\n\tvalue: string\n\tlabel: string\n\thint?: string\n}\n\nexport function SelectPrompt({\n\tmessage,\n\toptions,\n\theader,\n\tonSelect,\n}: {\n\tmessage: string\n\toptions: SelectOption[]\n\theader?: string\n\tonSelect: (value: string | null) => void\n}) {\n\tconst [idx, setIdx] = useState(0)\n\n\tuseInput((_, key) => {\n\t\tif (key.escape) {\n\t\t\tonSelect(null)\n\t\t\treturn\n\t\t}\n\t\tif (key.upArrow) {\n\t\t\tsetIdx((i) => (i - 1 + options.length) % options.length)\n\t\t\treturn\n\t\t}\n\t\tif (key.downArrow) {\n\t\t\tsetIdx((i) => (i + 1) % options.length)\n\t\t\treturn\n\t\t}\n\t\tif (key.return) {\n\t\t\tonSelect(options[idx]?.value ?? null)\n\t\t}\n\t})\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t{header && (\n\t\t\t\t<Box marginBottom={1}>\n\t\t\t\t\t<Text>{header}</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t<Box marginBottom={1}>\n\t\t\t\t<Text bold>{message}</Text>\n\t\t\t</Box>\n\t\t\t{options.map((opt, i) => (\n\t\t\t\t<Box key={opt.value}>\n\t\t\t\t\t<Text color={i === idx ? \"green\" : undefined}>\n\t\t\t\t\t\t{i === idx ? \"❯ \" : \" \"}\n\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t</Text>\n\t\t\t\t\t{opt.hint && i === idx && <Text dimColor> {opt.hint}</Text>}\n\t\t\t\t</Box>\n\t\t\t))}\n\t\t\t<Box marginTop={1}>\n\t\t\t\t<Text dimColor>↑↓ navigate · Enter select · Esc cancel</Text>\n\t\t\t</Box>\n\t\t</Box>\n\t)\n}\n\nexport function PasswordPrompt({\n\tmessage,\n\tvalidate,\n\tonSubmit,\n}: {\n\tmessage: string\n\tvalidate?: (v: string) => string | undefined\n\tonSubmit: (value: string | null) => void\n}) {\n\tconst [value, setValue] = useState(\"\")\n\tconst [error, setError] = useState(\"\")\n\n\tuseInput((ch, key) => {\n\t\tif (key.escape) {\n\t\t\tonSubmit(null)\n\t\t\treturn\n\t\t}\n\t\tif (key.return) {\n\t\t\tconst err = validate?.(value)\n\t\t\tif (err) {\n\t\t\t\tsetError(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tonSubmit(value)\n\t\t\treturn\n\t\t}\n\t\tif (key.backspace || key.delete) {\n\t\t\tsetValue((v) => v.slice(0, -1))\n\t\t\tsetError(\"\")\n\t\t\treturn\n\t\t}\n\t\tif (ch) {\n\t\t\tsetValue((v) => v + ch)\n\t\t\tsetError(\"\")\n\t\t}\n\t})\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t<Box marginBottom={1}>\n\t\t\t\t<Text bold>{message}</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Text color=\"green\">│ </Text>\n\t\t\t\t<Text dimColor>{\"*\".repeat(value.length)}</Text>\n\t\t\t\t<Text color=\"green\">│</Text>\n\t\t\t</Box>\n\t\t\t{error && (\n\t\t\t\t<Box>\n\t\t\t\t\t<Text color=\"red\">{error}</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t<Box marginTop={1}>\n\t\t\t\t<Text dimColor>Enter submit · Esc cancel</Text>\n\t\t\t</Box>\n\t\t</Box>\n\t)\n}\n\nexport function ConfirmPrompt({\n\tmessage,\n\tonConfirm,\n}: {\n\tmessage: string\n\tonConfirm: (value: boolean | null) => void\n}) {\n\tconst [yes, setYes] = useState(true)\n\n\tuseInput((_, key) => {\n\t\tif (key.escape) {\n\t\t\tonConfirm(null)\n\t\t\treturn\n\t\t}\n\t\tif (key.leftArrow || key.rightArrow || key.tab) {\n\t\t\tsetYes((y) => !y)\n\t\t\treturn\n\t\t}\n\t\tif (key.return) {\n\t\t\tonConfirm(yes)\n\t\t}\n\t})\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1}>\n\t\t\t<Box marginBottom={1}>\n\t\t\t\t<Text bold>{message}</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Text color={yes ? \"green\" : undefined}>{yes ? \"❯ \" : \" \"}Yes</Text>\n\t\t\t</Box>\n\t\t\t<Box>\n\t\t\t\t<Text color={!yes ? \"red\" : undefined}>{!yes ? \"❯ \" : \" \"}No</Text>\n\t\t\t</Box>\n\t\t\t<Box marginTop={1}>\n\t\t\t\t<Text dimColor>←→ toggle · Enter confirm · Esc cancel</Text>\n\t\t\t</Box>\n\t\t</Box>\n\t)\n}\n\n// Standalone wrappers for use outside the main TUI (e.g. onboarding)\n\nexport function standaloneSelect(\n\tmessage: string,\n\toptions: SelectOption[],\n\theader?: string,\n): Promise<string | null> {\n\treturn new Promise((resolve) => {\n\t\tconst { unmount } = render(\n\t\t\t<SelectPrompt\n\t\t\t\tmessage={message}\n\t\t\t\toptions={options}\n\t\t\t\theader={header}\n\t\t\t\tonSelect={(v) => {\n\t\t\t\t\tunmount()\n\t\t\t\t\tresolve(v)\n\t\t\t\t}}\n\t\t\t/>,\n\t\t)\n\t})\n}\n\nexport function standalonePassword(\n\tmessage: string,\n\tvalidate?: (v: string) => string | undefined,\n): Promise<string | null> {\n\treturn new Promise((resolve) => {\n\t\tconst { unmount } = render(\n\t\t\t<PasswordPrompt\n\t\t\t\tmessage={message}\n\t\t\t\tvalidate={validate}\n\t\t\t\tonSubmit={(v) => {\n\t\t\t\t\tunmount()\n\t\t\t\t\tresolve(v)\n\t\t\t\t}}\n\t\t\t/>,\n\t\t)\n\t})\n}\n","import chalk from \"chalk\"\nimport { getModelsForProvider, getProvider, PROVIDERS } from \"../config/providers.ts\"\nimport { saveAuth, saveConfig } from \"../config/store.ts\"\nimport { standalonePassword, standaloneSelect } from \"../tui/prompts.tsx\"\nimport type { NovaConfig } from \"../types.ts\"\n\nexport async function runOnboarding(): Promise<NovaConfig> {\n\tconsole.log(chalk.bold.cyan(\"\\n⚡ Nova — your coding companion\\n\"))\n\n\tconst providerId = await standaloneSelect(\n\t\t\"Pick a provider\",\n\t\tPROVIDERS.map((p) => ({ value: p.id, label: p.name })),\n\t)\n\tif (!providerId) {\n\t\tconsole.log(chalk.dim(\"Cancelled\"))\n\t\tprocess.exit(0)\n\t}\n\n\tconst provider = getProvider(providerId)\n\tif (!provider) {\n\t\tconsole.log(chalk.red(\"Unknown provider\"))\n\t\tprocess.exit(1)\n\t}\n\n\tconst apiKey = await standalonePassword(`Enter ${provider.name} API key`)\n\tif (!apiKey) {\n\t\tconsole.log(chalk.dim(\"Cancelled\"))\n\t\tprocess.exit(0)\n\t}\n\n\tconst models = getModelsForProvider(providerId)\n\tconst modelId = await standaloneSelect(\n\t\t\"Pick a default model\",\n\t\tmodels.map((m) => ({\n\t\t\tvalue: m.id,\n\t\t\tlabel: `${m.name} (${(m.contextWindow / 1000).toFixed(0)}k ctx)`,\n\t\t})),\n\t)\n\tif (!modelId) {\n\t\tconsole.log(chalk.dim(\"Cancelled\"))\n\t\tprocess.exit(0)\n\t}\n\n\tconst config: NovaConfig = {\n\t\tprovider: providerId,\n\t\tmodel: modelId,\n\t}\n\n\tawait saveConfig(config)\n\tawait saveAuth({ apiKeys: { [providerId]: apiKey } })\n\n\tconsole.log(chalk.green(\"\\n✓ Ready. Type your prompt or /help for commands\\n\"))\n\treturn config\n}\n","/**\n * Filesystem tools for reading, writing, and editing files.\n * Includes safety checks to prevent path traversal.\n */\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\"\nimport { dirname, extname, resolve } from \"node:path\"\nimport type { Tool, ToolResult } from \"../types.ts\"\nimport { getRelativeIfInside, textPart } from \"../util.ts\"\n\n// Extensions we return as base64 images instead of text\nconst IMAGES = new Set([\".jpg\", \".jpeg\", \".png\", \".gif\", \".webp\"])\n\nfunction safePath(cwd: string, p: string): string {\n\tconst abs = resolve(cwd, p)\n\tif (abs !== cwd && !abs.startsWith(`${cwd}/`)) {\n\t\tthrow new Error(`Path outside project: ${p}`)\n\t}\n\treturn abs\n}\n\nexport function readTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"read\",\n\t\t\tdescription:\n\t\t\t\t\"Read file contents. Supports text and images (jpg, png, gif, webp). Text output is truncated to 2000 lines.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Path to file (relative or absolute)\" },\n\t\t\t\t\toffset: { type: \"number\", description: \"Start line (1-based, default 1)\" },\n\t\t\t\t\tlimit: { type: \"number\", description: \"Max lines to read (default 2000)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"path\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst filePath = safePath(cwd, args.path as string)\n\t\t\t\t// Return images as base64 so the LLM can process them visually\n\t\t\t\tconst ext = extname(filePath).toLowerCase()\n\t\t\t\tif (IMAGES.has(ext)) {\n\t\t\t\t\tconst buf = await readFile(filePath)\n\t\t\t\t\tconst b64 = buf.toString(\"base64\")\n\t\t\t\t\tconst mime = ext === \".jpg\" ? \"image/jpeg\" : `image/${ext.slice(1)}`\n\t\t\t\t\treturn { content: [{ type: \"image\", data: b64, mime }], isError: false }\n\t\t\t\t}\n\n\t\t\t\tconst content = await readFile(filePath, \"utf-8\")\n\t\t\t\tconst lines = content.split(\"\\n\")\n\t\t\t\tconst offset = Math.max(0, (Number(args.offset ?? 1) || 1) - 1)\n\t\t\t\tconst limit = Number(args.limit ?? 2000) || 2000\n\t\t\t\tconst slice = lines.slice(offset, offset + limit)\n\t\t\t\tconst truncated = offset + limit < lines.length\n\n\t\t\t\tconst out = slice.join(\"\\n\")\n\t\t\t\tconst suffix = truncated ? `\\n…${lines.length - offset - limit} more lines` : \"\"\n\n\t\t\t\treturn { content: [textPart(out + suffix)], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error reading file: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\nexport function writeTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"write\",\n\t\t\tdescription: \"Write content to a file. Creates the file and parent directories if needed.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Path to file\" },\n\t\t\t\t\tcontent: { type: \"string\", description: \"Content to write\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"path\", \"content\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst filePath = safePath(cwd, args.path as string)\n\t\t\t\tconst content = args.content as string\n\t\t\t\tawait mkdir(dirname(filePath), { recursive: true })\n\t\t\t\tawait writeFile(filePath, content)\n\t\t\t\tconst relPath = getRelativeIfInside(cwd, filePath)\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Wrote ${content.length} bytes → ${relPath}`)],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error writing file: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n// Requires oldText to be unique to avoid ambiguous replacements.\nexport function editTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"edit\",\n\t\t\tdescription:\n\t\t\t\t\"Edit a file using exact text replacement. Each edit's oldText must be unique in the file.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Path to file\" },\n\t\t\t\t\tedits: {\n\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\"Array of {oldText, newText} replacements. oldText must be unique. Non-overlapping.\",\n\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\toldText: { type: \"string\", description: \"Exact text to find (must be unique)\" },\n\t\t\t\t\t\t\t\tnewText: { type: \"string\", description: \"Replacement text\" },\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\trequired: [\"oldText\", \"newText\"],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trequired: [\"path\", \"edits\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst filePath = safePath(cwd, args.path as string)\n\t\t\t\tlet content: string\n\t\t\t\ttry {\n\t\t\t\t\tcontent = await readFile(filePath, \"utf-8\")\n\t\t\t\t} catch {\n\t\t\t\t\treturn { content: [textPart(`File not found: ${args.path}`)], isError: true }\n\t\t\t\t}\n\t\t\t\tconst edits = args.edits as Array<{ oldText: string; newText: string }>\n\n\t\t\t\t// Validate all edits before applying any — avoids partial writes on bad input\n\t\t\t\tfor (const edit of edits) {\n\t\t\t\t\tconst count = content.split(edit.oldText).length - 1\n\t\t\t\t\tif (count === 0) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [textPart(`oldText not found: \"${edit.oldText.slice(0, 80)}…\"`)],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Ambiguous match would replace the wrong occurrence\n\t\t\t\t\tif (count > 1) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\ttextPart(\n\t\t\t\t\t\t\t\t\t`oldText found ${count} times — add surrounding context to make it unique: \"${edit.oldText.slice(0, 60)}…\"`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tisError: true,\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Apply edits sequentially\n\t\t\t\tfor (const edit of edits) {\n\t\t\t\t\tcontent = content.replace(edit.oldText, edit.newText)\n\t\t\t\t}\n\n\t\t\t\tawait writeFile(filePath, content)\n\t\t\t\tconst relPath = getRelativeIfInside(cwd, filePath)\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\ttextPart(\n\t\t\t\t\t\t\t`Edited ${relPath} (${edits.length} replacement${edits.length > 1 ? \"s\" : \"\"})`,\n\t\t\t\t\t\t),\n\t\t\t\t\t],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error editing file: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Git tools for executing safe repository operations programmatically.\n */\nimport { spawn } from \"node:child_process\"\nimport type { Tool, ToolResult } from \"../types.ts\"\nimport { textPart } from \"../util.ts\"\n\nexport function gitTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"git\",\n\t\t\tdescription:\n\t\t\t\t\"Execute safe, non-interactive git commands (status, diff, log, add, commit) in the repository.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\taction: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\tenum: [\"status\", \"diff\", \"log\", \"add\", \"commit\"],\n\t\t\t\t\t\tdescription: \"The git action to execute\",\n\t\t\t\t\t},\n\t\t\t\t\targs: {\n\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\tdescription: \"Optional additional arguments or file paths for the git action\",\n\t\t\t\t\t\titems: { type: \"string\" },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trequired: [\"action\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst action = args.action as string\n\t\t\tconst extraArgs = (args.args as string[]) || []\n\n\t\t\tconst allowed = new Set([\"status\", \"diff\", \"log\", \"add\", \"commit\"])\n\t\t\tif (!allowed.has(action)) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: Git action '${action}' is not supported.`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst cmd = [\"git\", action, ...extraArgs]\n\t\t\t\tconst proc = spawn(cmd[0]!, cmd.slice(1), {\n\t\t\t\t\tcwd,\n\t\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t\tenv: { ...process.env, PAGER: \"cat\" },\n\t\t\t\t})\n\n\t\t\t\tlet stdout = \"\"\n\t\t\t\tlet stderr = \"\"\n\t\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstdout += chunk.toString()\n\t\t\t\t})\n\t\t\t\tproc.stderr.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstderr += chunk.toString()\n\t\t\t\t})\n\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tproc.kill(\"SIGKILL\")\n\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t}\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\t\t\tlet exitCode: number\n\t\t\t\ttry {\n\t\t\t\t\texitCode = await new Promise<number>((resolve, reject) => {\n\t\t\t\t\t\tproc.on(\"error\", reject)\n\t\t\t\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t\t\t\t})\n\t\t\t\t} finally {\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort)\n\t\t\t\t}\n\n\t\t\t\t// Prevent context window blowout by truncating very large outputs\n\t\t\t\tconst MAX = 50_000\n\t\t\t\tlet out = \"\"\n\t\t\t\tif (stdout) out += stdout.slice(0, MAX)\n\t\t\t\tif (stderr) {\n\t\t\t\t\tif (out) out += \"\\n\"\n\t\t\t\t\tout += stderr.slice(0, MAX - out.length)\n\t\t\t\t}\n\t\t\t\tif (out.length >= MAX) out += \"\\n…truncated\"\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(out || \"(no output)\")],\n\t\t\t\t\tisError: exitCode !== 0,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error running git: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Search tools for finding files and content.\n * Uses 'rg' (ripgrep) if available, falling back to a pure JS implementation.\n */\n\nimport { spawn } from \"node:child_process\"\nimport { readdir, readFile } from \"node:fs/promises\"\nimport { relative, resolve } from \"node:path\"\nimport { glob } from \"glob\"\nimport type { Tool, ToolResult } from \"../types.ts\"\n\nimport { textPart } from \"../util.ts\"\n\n/**\n * Tool for finding files by glob pattern.\n */\nexport function globTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"glob\",\n\t\t\tdescription: \"Find files by glob pattern (e.g. **/*.ts, src/**/*.test.ts).\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpattern: { type: \"string\", description: \"Glob pattern (e.g. **/*.ts)\" },\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory to search in (default .)\" },\n\t\t\t\t\tnocase: { type: \"boolean\", description: \"Case-insensitive search (default false)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"pattern\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst rawPath = (args.path as string) || \".\"\n\t\t\t\tconst dir = resolve(cwd, rawPath)\n\t\t\t\tif (dir !== cwd && !dir.startsWith(`${cwd}/`)) {\n\t\t\t\t\tthrow new Error(`Path outside project: ${rawPath}`)\n\t\t\t\t}\n\n\t\t\t\tconst pattern = args.pattern as string\n\t\t\t\tconst nocase = !!args.nocase\n\t\t\t\tconst files = await glob(pattern, { cwd: dir, nocase })\n\t\t\t\tconst sliced = files.slice(0, 500)\n\t\t\t\tconst relSearchPath = relative(cwd, dir)\n\t\t\t\tconst prefix = relSearchPath ? `${relSearchPath}/` : \"\"\n\t\t\t\tconst relFiles = sliced.map((f) => prefix + f)\n\t\t\t\tconst out = relFiles.length > 0 ? relFiles.join(\"\\n\") : \"No files found\"\n\t\t\t\treturn { content: [textPart(out)], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Tool for searching file contents using regex.\n */\nexport function grepTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"grep\",\n\t\t\tdescription:\n\t\t\t\t\"Search file contents with a regex pattern. Returns matching lines with file paths and line numbers.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpattern: { type: \"string\", description: \"Regex pattern to search for\" },\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory or file to search in (default .)\" },\n\t\t\t\t\tglob: { type: \"string\", description: \"File filter glob (e.g. *.ts)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"pattern\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst rawPath = (args.path as string) || \".\"\n\t\t\t\tconst dir = resolve(cwd, rawPath)\n\t\t\t\tif (dir !== cwd && !dir.startsWith(`${cwd}/`)) {\n\t\t\t\t\tthrow new Error(`Path outside project: ${rawPath}`)\n\t\t\t\t}\n\n\t\t\t\tconst pattern = args.pattern as string\n\t\t\t\tconst globFilter = args.glob as string | undefined\n\t\t\t\tconst relSearchPath = relative(cwd, dir) || \".\"\n\n\t\t\t\t// rg is 10-100x faster than our JS fallback, but isn't always installed\n\t\t\t\ttry {\n\t\t\t\t\tconst cmd = [\"rg\", \"--line-number\", \"--max-count\", \"200\"]\n\t\t\t\t\tif (globFilter) cmd.push(`--glob=${globFilter}`)\n\t\t\t\t\tcmd.push(\"--\", pattern, relSearchPath)\n\n\t\t\t\t\tconst proc = spawn(cmd[0]!, cmd.slice(1), {\n\t\t\t\t\t\tcwd,\n\t\t\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\t\t})\n\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\tproc.kill()\n\t\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t\t}\n\t\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\t\t\t\tlet stdout = \"\"\n\t\t\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\t\tstdout += chunk.toString()\n\t\t\t\t\t})\n\n\t\t\t\t\tlet exitCode: number\n\t\t\t\t\ttry {\n\t\t\t\t\t\texitCode = await new Promise<number>((resolve, reject) => {\n\t\t\t\t\t\t\tproc.on(\"error\", reject)\n\t\t\t\t\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t\t\t\t\t})\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort)\n\t\t\t\t\t}\n\n\t\t\t\t\tif (exitCode === 0) {\n\t\t\t\t\t\tconst lines = stdout.split(\"\\n\").slice(0, 200).join(\"\\n\")\n\t\t\t\t\t\treturn { content: [textPart(lines || \"No matches\")], isError: false }\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// rg not available, fall through\n\t\t\t\t}\n\n\t\t\t\t// Pure JS fallback when rg is not available\n\t\t\t\tconst files = await glob(globFilter || \"**/*\", {\n\t\t\t\t\tcwd: dir,\n\t\t\t\t\tignore: [\"**/node_modules/**\", \"**/.git/**\"],\n\t\t\t\t})\n\t\t\t\tconst prefix = relSearchPath === \".\" ? \"\" : `${relSearchPath}/`\n\t\t\t\tconst re = new RegExp(pattern, \"i\")\n\t\t\t\tconst matches: string[] = []\n\t\t\t\tfor (const file of files.slice(0, 500)) {\n\t\t\t\t\tif (signal?.aborted) break\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst content = await readFile(resolve(dir, file), \"utf-8\")\n\t\t\t\t\t\tconst lines = content.split(\"\\n\")\n\t\t\t\t\t\tfor (let i = 0; i < lines.length && matches.length < 200; i++) {\n\t\t\t\t\t\t\tconst line = lines[i]\n\t\t\t\t\t\t\tif (line && re.test(line)) matches.push(`${prefix}${file}:${i + 1}:${line}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Skip binary/unreadable files silently\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(matches.join(\"\\n\") || \"No matches\")],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Tool for listing directory entries.\n */\nexport function lsTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"ls\",\n\t\t\tdescription: \"List directory contents.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory to list (default .)\" },\n\t\t\t\t},\n\t\t\t\trequired: [],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst dir = resolve(cwd, (args.path as string) || \".\")\n\t\t\t\tconst entries = await readdir(dir, { withFileTypes: true })\n\t\t\t\tconst lines = entries.map((e) => {\n\t\t\t\t\tconst suffix = e.isDirectory() ? \"/\" : e.isSymbolicLink() ? \"@\" : \"\"\n\t\t\t\t\treturn `${e.name}${suffix}`\n\t\t\t\t})\n\t\t\t\treturn { content: [textPart(lines.join(\"\\n\") || \"(empty)\")], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Tool for visualizing a truncated directory tree.\n */\nexport function treeTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"tree\",\n\t\t\tdescription:\n\t\t\t\t\"Print a visual directory tree structure, ignoring common ignored folders like node_modules and .git.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tpath: { type: \"string\", description: \"Directory to start tree from (default .)\" },\n\t\t\t\t\tdepth: { type: \"number\", description: \"Maximum depth to traverse (default 3)\" },\n\t\t\t\t},\n\t\t\t\trequired: [],\n\t\t\t},\n\t\t},\n\t\tasync execute(args): Promise<ToolResult> {\n\t\t\ttry {\n\t\t\t\tconst startDir = resolve(cwd, (args.path as string) || \".\")\n\t\t\t\tconst maxDepth = Number(args.depth ?? 3) || 3\n\n\t\t\t\tconst ignoreList = new Set([\n\t\t\t\t\t\".git\",\n\t\t\t\t\t\"node_modules\",\n\t\t\t\t\t\"dist\",\n\t\t\t\t\t\"build\",\n\t\t\t\t\t\".svelte-kit\",\n\t\t\t\t\t\".next\",\n\t\t\t\t\t\"out\",\n\t\t\t\t\t\".scannerwork\",\n\t\t\t\t\t\"coverage\",\n\t\t\t\t])\n\n\t\t\t\tasync function walk(dir: string, currentDepth: number, prefix: string): Promise<string> {\n\t\t\t\t\tif (currentDepth > maxDepth) return \"\"\n\t\t\t\t\tlet result = \"\"\n\n\t\t\t\t\tconst entries = await readdir(dir, { withFileTypes: true })\n\t\t\t\t\tconst sorted = entries\n\t\t\t\t\t\t.filter((e) => !ignoreList.has(e.name))\n\t\t\t\t\t\t.sort((a, b) => {\n\t\t\t\t\t\t\tif (a.isDirectory() && !b.isDirectory()) return -1\n\t\t\t\t\t\t\tif (!a.isDirectory() && b.isDirectory()) return 1\n\t\t\t\t\t\t\treturn a.name.localeCompare(b.name)\n\t\t\t\t\t\t})\n\n\t\t\t\t\tfor (let i = 0; i < sorted.length; i++) {\n\t\t\t\t\t\tconst entry = sorted[i]!\n\t\t\t\t\t\tconst isLast = i === sorted.length - 1\n\t\t\t\t\t\tconst connector = isLast ? \"└── \" : \"├── \"\n\t\t\t\t\t\tconst childPrefix = prefix + (isLast ? \" \" : \"│ \")\n\n\t\t\t\t\t\tresult += `${prefix}${connector}${entry.name}${entry.isDirectory() ? \"/\" : \"\"}\\n`\n\n\t\t\t\t\t\tif (entry.isDirectory()) {\n\t\t\t\t\t\t\tresult += await walk(resolve(dir, entry.name), currentDepth + 1, childPrefix)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn result\n\t\t\t\t}\n\n\t\t\t\tconst treeText = await walk(startDir, 1, \"\")\n\t\t\t\treturn { content: [textPart(treeText || \"(empty)\")], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Tool for executing shell commands within the project root.\n * Supports timeouts and output truncation to protect the context window.\n */\nimport { spawn } from \"node:child_process\"\nimport type { Tool, ToolResult } from \"../types.ts\"\n\nimport { textPart } from \"../util.ts\"\n\nexport function bashTool(cwd: string): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"bash\",\n\t\t\tdescription:\n\t\t\t\t\"Execute a shell command. Returns stdout and stderr. Timeout after N seconds (default 120).\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tcommand: { type: \"string\", description: \"Shell command to run\" },\n\t\t\t\t\ttimeout: { type: \"number\", description: \"Timeout in seconds (default 120)\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"command\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst command = args.command as string\n\t\t\tconst timeoutMs = (Number(args.timeout) || 120) * 1000\n\n\t\t\ttry {\n\t\t\t\tconst proc = spawn(\"sh\", [\"-c\", command], { cwd, stdio: [\"ignore\", \"pipe\", \"pipe\"] })\n\n\t\t\t\tlet stdout = \"\"\n\t\t\t\tlet stderr = \"\"\n\t\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstdout += chunk.toString()\n\t\t\t\t})\n\t\t\t\tproc.stderr.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\tstderr += chunk.toString()\n\t\t\t\t})\n\n\t\t\t\tlet killed = false\n\t\t\t\tconst timer = setTimeout(() => {\n\t\t\t\t\tkilled = true\n\t\t\t\t\tproc.kill(\"SIGKILL\")\n\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t}, timeoutMs)\n\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tkilled = true\n\t\t\t\t\tproc.kill(\"SIGKILL\")\n\t\t\t\t\tproc.stdout.destroy()\n\t\t\t\t\tproc.stderr.destroy()\n\t\t\t\t}\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\t\t\tlet exitCode: number\n\t\t\t\ttry {\n\t\t\t\t\texitCode = await new Promise<number>((resolve, reject) => {\n\t\t\t\t\t\tproc.on(\"error\", reject)\n\t\t\t\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t\t\t\t})\n\t\t\t\t} finally {\n\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort)\n\t\t\t\t}\n\n\t\t\t\t// Prevent context-window blowout from noisy commands\n\t\t\t\tconst MAX = 50_000\n\t\t\t\tlet out = \"\"\n\t\t\t\tif (stdout) out += stdout.slice(0, MAX)\n\t\t\t\tif (stderr) {\n\t\t\t\t\tif (out) out += \"\\n\"\n\t\t\t\t\tout += stderr.slice(0, MAX - out.length)\n\t\t\t\t}\n\t\t\t\tif (out.length >= MAX) out += `\\n…truncated`\n\n\t\t\t\tif (killed) out += `\\n[timeout after ${timeoutMs / 1000}s]`\n\t\t\t\tout += `\\n[exit ${exitCode}]`\n\n\t\t\t\treturn { content: [textPart(out)], isError: exitCode !== 0 || killed }\n\t\t\t} catch (e) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Error: ${(e as Error).message}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Web tools for searching and fetching internet content.\n * Uses DuckDuckGo HTML for search (no API key needed) and Bun's built-in\n * fetch for reading URLs.\n */\nimport type { Tool, ToolResult } from \"../types.ts\"\nimport { textPart } from \"../util.ts\"\n\nconst MAX_CONTENT = 50_000\n\n// Minimal HTML → plaintext: strips tags, decodes entities, collapses whitespace\nfunction htmlToText(html: string): string {\n\tlet text = html\n\t\t// Remove HTML comments\n\t\t.replace(/<!--[\\s\\S]*?-->/g, \"\")\n\t\t// Remove script and style blocks entirely\n\t\t.replace(/<script[\\s\\S]*?<\\/script>/gi, \"\")\n\t\t.replace(/<style[\\s\\S]*?<\\/style>/gi, \"\")\n\t\t// Keep link hrefs visible (supports single, double, or no quotes)\n\t\t.replace(/<a[^>]*href=[\"']?([^\"'>\\s]*)[\"']?[^>]*>([\\s\\S]*?)<\\/a>/gi, \"[$2]($1)\")\n\t\t// Block-level tags → newlines\n\t\t.replace(/<\\/?(p|div|br|h[1-6]|li|tr|blockquote|pre|hr)[^>]*>/gi, \"\\n\")\n\t\t// Remove remaining tags\n\t\t.replace(/<[^>]+>/g, \"\")\n\t\t// Decode common HTML entities (both named and numeric, single/double quotes)\n\t\t.replace(/&/g, \"&\")\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/"/g, '\"')\n\t\t.replace(/"/g, '\"')\n\t\t.replace(/"/g, '\"')\n\t\t.replace(/'/g, \"'\")\n\t\t.replace(/'/g, \"'\")\n\t\t.replace(/'/g, \"'\")\n\t\t.replace(/“/g, '\"')\n\t\t.replace(/”/g, '\"')\n\t\t.replace(/‘/g, \"'\")\n\t\t.replace(/’/g, \"'\")\n\t\t.replace(/ /g, \" \")\n\t\t// Collapse whitespace but keep paragraph breaks\n\t\t.replace(/[ \\t]+/g, \" \")\n\t\t.replace(/\\n{3,}/g, \"\\n\\n\")\n\t\t.trim()\n\n\tif (text.length > MAX_CONTENT) {\n\t\ttext = `${text.slice(0, MAX_CONTENT)}\\n…truncated`\n\t}\n\treturn text\n}\n\nexport function webSearchTool(): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"web_search\",\n\t\t\tdescription:\n\t\t\t\t\"Search the web using DuckDuckGo. Returns up to 10 results with titles, URLs, and snippets. Use this when you need information from the internet.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tquery: { type: \"string\", description: \"Search query\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"query\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst query = args.query as string\n\t\t\tif (!query.trim()) {\n\t\t\t\treturn { content: [textPart(\"Error: empty search query\")], isError: true }\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`\n\t\t\t\tconst resp = await fetch(url, {\n\t\t\t\t\tsignal: signal ?? undefined,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"User-Agent\":\n\t\t\t\t\t\t\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36\",\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\tif (!resp.ok) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [textPart(`Search failed: HTTP ${resp.status}`)],\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst html = await resp.text()\n\n\t\t\t\t// Split HTML into blocks by result__body to isolate each search result safely\n\t\t\t\tconst blocks: string[] = []\n\t\t\t\tconst containerRegex = /<div[^>]*class=\"[^\"]*result__body[^\"]*\"[^>]*>/gi\n\t\t\t\tconst indices: number[] = []\n\t\t\t\tfor (const match of html.matchAll(containerRegex)) {\n\t\t\t\t\tif (match.index !== undefined) {\n\t\t\t\t\t\tindices.push(match.index)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (indices.length > 0) {\n\t\t\t\t\tfor (let i = 0; i < indices.length; i++) {\n\t\t\t\t\t\tconst start = indices[i]!\n\t\t\t\t\t\tconst end = indices[i + 1] ?? html.length\n\t\t\t\t\t\tblocks.push(html.slice(start, end))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst results: string[] = []\n\t\t\t\tconst titleRegex = /<a[^>]*class=\"result__a\"[^>]*href=\"([^\"]*)\"[^>]*>([\\s\\S]*?)<\\/a>/i\n\t\t\t\tconst snippetRegex = /<a[^>]*class=\"result__snippet\"[^>]*>([\\s\\S]*?)<\\/a>/i\n\n\t\t\t\tfor (const block of blocks) {\n\t\t\t\t\tif (results.length >= 10) break\n\n\t\t\t\t\tconst titleMatch = titleRegex.exec(block)\n\t\t\t\t\tif (!titleMatch) continue\n\n\t\t\t\t\tconst rawUrl = titleMatch[1]!\n\t\t\t\t\tconst title = htmlToText(titleMatch[2]!)\n\n\t\t\t\t\tconst snippetMatch = snippetRegex.exec(block)\n\t\t\t\t\tconst snippet = snippetMatch ? htmlToText(snippetMatch[1]!) : \"\"\n\n\t\t\t\t\t// DuckDuckGo wraps URLs through a redirect; extract the actual URL\n\t\t\t\t\tlet cleanUrl = rawUrl\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Prepend protocol/host if DuckDuckGo returns a relative path or protocol-relative URL\n\t\t\t\t\t\tconst urlToParse = rawUrl.startsWith(\"//\")\n\t\t\t\t\t\t\t? `https:${rawUrl}`\n\t\t\t\t\t\t\t: rawUrl.startsWith(\"/\")\n\t\t\t\t\t\t\t\t? `https://duckduckgo.com${rawUrl}`\n\t\t\t\t\t\t\t\t: rawUrl\n\t\t\t\t\t\tconst param = new URL(urlToParse).searchParams.get(\"uddg\")\n\t\t\t\t\t\tif (param) cleanUrl = param\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Not a redirect URL, use as-is\n\t\t\t\t\t}\n\n\t\t\t\t\tresults.push(`## ${title}\\n${cleanUrl}\\n${snippet}`)\n\t\t\t\t}\n\n\t\t\t\tif (results.length === 0) {\n\t\t\t\t\treturn { content: [textPart(\"No results found.\")], isError: false }\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(results.join(\"\\n\\n\"))],\n\t\t\t\t\tisError: false,\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tconst msg = (e as Error).message\n\t\t\t\tif (msg.includes(\"abort\")) {\n\t\t\t\t\treturn { content: [textPart(\"Search aborted.\")], isError: true }\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Search error: ${msg}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\nexport function webFetchTool(): Tool {\n\treturn {\n\t\tdef: {\n\t\t\tname: \"web_fetch\",\n\t\t\tdescription:\n\t\t\t\t\"Fetch and read the content of a web page. Returns the page text with HTML tags stripped. Useful for reading documentation, articles, or API references.\",\n\t\t\tparameters: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\turl: { type: \"string\", description: \"URL to fetch\" },\n\t\t\t\t},\n\t\t\t\trequired: [\"url\"],\n\t\t\t},\n\t\t},\n\t\tasync execute(args, signal): Promise<ToolResult> {\n\t\t\tconst url = args.url as string\n\t\t\tif (!url.trim()) {\n\t\t\t\treturn { content: [textPart(\"Error: empty URL\")], isError: true }\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tnew URL(url)\n\t\t\t} catch {\n\t\t\t\treturn { content: [textPart(`Error: invalid URL: ${url}`)], isError: true }\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst resp = await fetch(url, {\n\t\t\t\t\tsignal: signal ?? undefined,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"User-Agent\":\n\t\t\t\t\t\t\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36\",\n\t\t\t\t\t\tAccept: \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\n\t\t\t\t\t},\n\t\t\t\t\tredirect: \"follow\",\n\t\t\t\t})\n\n\t\t\t\tif (!resp.ok) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [textPart(`Fetch failed: HTTP ${resp.status} ${resp.statusText}`)],\n\t\t\t\t\t\tisError: true,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst contentType = (resp.headers.get(\"content-type\") ?? \"\").toLowerCase()\n\t\t\t\tconst body = await resp.text()\n\n\t\t\t\t// Sniff HTML: check content-type or if body has HTML markup signature\n\t\t\t\tconst isHtml =\n\t\t\t\t\tcontentType.includes(\"text/html\") ||\n\t\t\t\t\tcontentType.includes(\"application/xhtml+xml\") ||\n\t\t\t\t\tbody.trim().toLowerCase().startsWith(\"<!doctype html\") ||\n\t\t\t\t\tbody.trim().toLowerCase().startsWith(\"<html\")\n\n\t\t\t\tif (isHtml) {\n\t\t\t\t\tconst text = htmlToText(body)\n\t\t\t\t\treturn { content: [textPart(text)], isError: false }\n\t\t\t\t}\n\n\t\t\t\t// For plain text, JSON, etc. return as-is (truncated if needed)\n\t\t\t\tconst truncated =\n\t\t\t\t\tbody.length > MAX_CONTENT ? `${body.slice(0, MAX_CONTENT)}\\n…truncated` : body\n\t\t\t\treturn { content: [textPart(truncated)], isError: false }\n\t\t\t} catch (e) {\n\t\t\t\tconst msg = (e as Error).message\n\t\t\t\tif (msg.includes(\"abort\")) {\n\t\t\t\t\treturn { content: [textPart(\"Fetch aborted.\")], isError: true }\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [textPart(`Fetch error: ${msg}`)],\n\t\t\t\t\tisError: true,\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n","import type { Tool } from \"../types.ts\"\nimport { editTool, readTool, writeTool } from \"./fs.ts\"\nimport { gitTool } from \"./git.ts\"\nimport { globTool, grepTool, lsTool, treeTool } from \"./search.ts\"\nimport { bashTool } from \"./shell.ts\"\nimport { webFetchTool, webSearchTool } from \"./web.ts\"\n\nexport function getAllTools(cwd: string): Tool[] {\n\treturn [\n\t\treadTool(cwd),\n\t\twriteTool(cwd),\n\t\teditTool(cwd),\n\t\tbashTool(cwd),\n\t\tglobTool(cwd),\n\t\tgrepTool(cwd),\n\t\tlsTool(cwd),\n\t\ttreeTool(cwd),\n\t\tgitTool(cwd),\n\t\twebSearchTool(),\n\t\twebFetchTool(),\n\t]\n}\n\nexport function getDefaultTools(cwd: string): Tool[] {\n\treturn [\n\t\treadTool(cwd),\n\t\twriteTool(cwd),\n\t\teditTool(cwd),\n\t\tbashTool(cwd),\n\t\twebSearchTool(),\n\t\twebFetchTool(),\n\t]\n}\n","import { spawn } from \"node:child_process\"\nimport { readFile } from \"node:fs/promises\"\nimport { dirname, join } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport semver from \"semver\"\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nlet cachedLatest: string | null = null\nlet cachedCurrent: string | null = null\n\nexport async function getCurrentVersion(): Promise<string> {\n\tif (cachedCurrent) return cachedCurrent\n\ttry {\n\t\tconst raw = await readFile(join(__dirname, \"..\", \"package.json\"), \"utf-8\")\n\t\tconst pkg = JSON.parse(raw)\n\t\tcachedCurrent = (pkg.version as string) ?? \"0.0.0\"\n\t\treturn cachedCurrent\n\t} catch {\n\t\treturn \"0.0.0\"\n\t}\n}\n\nexport async function getLatestVersion(): Promise<string | null> {\n\tif (cachedLatest) return cachedLatest\n\ttry {\n\t\tconst proc = spawn(\"npm\", [\"info\", \"novacode\", \"version\"], {\n\t\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t\t})\n\t\tconst text = await new Promise<string>((resolve, reject) => {\n\t\t\tlet out = \"\"\n\t\t\tproc.stdout.on(\"data\", (chunk: Buffer) => {\n\t\t\t\tout += chunk.toString()\n\t\t\t})\n\t\t\tproc.on(\"error\", reject)\n\t\t\tproc.on(\"close\", () => resolve(out.trim()))\n\t\t})\n\t\tif (text) {\n\t\t\tcachedLatest = text\n\t\t\treturn text\n\t\t}\n\t} catch {}\n\treturn null\n}\n\nexport async function checkForUpdate(): Promise<{\n\thasUpdate: boolean\n\tcurrent: string\n\tlatest: string\n} | null> {\n\tconst current = await getCurrentVersion()\n\tconst latest = await getLatestVersion()\n\tif (!latest) return null\n\treturn {\n\t\thasUpdate: semver.gt(latest, current),\n\t\tcurrent,\n\t\tlatest,\n\t}\n}\n\nexport async function runUpdate(silent = false): Promise<boolean> {\n\ttry {\n\t\tconst proc = spawn(\"npm\", [\"update\", \"-g\", \"novacode\"], {\n\t\t\tstdio: silent ? \"ignore\" : \"inherit\",\n\t\t})\n\t\tconst exitCode = await new Promise<number>((resolve, reject) => {\n\t\t\tproc.on(\"error\", reject)\n\t\t\tproc.on(\"close\", (code) => resolve(code ?? -1))\n\t\t})\n\t\tif (exitCode === 0) {\n\t\t\tif (!silent) {\n\t\t\t\tconsole.log(\"✓ novacode updated to latest version successfully.\")\n\t\t\t}\n\t\t\treturn true\n\t\t} else {\n\t\t\tif (!silent) {\n\t\t\t\tconsole.error(`Update failed (exit code ${exitCode})`)\n\t\t\t\tprocess.exit(1)\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\t} catch (e) {\n\t\tif (!silent) {\n\t\t\tconsole.error(`Update failed: ${(e as Error).message}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\t\treturn false\n\t}\n}\n","#!/usr/bin/env node\nimport { parseArgs } from \"node:util\"\n/**\n * Entry point for the nova CLI.\n * Handles configuration, CLI flags, and runs interactive TUI mode.\n */\nimport chalk from \"chalk\"\nimport { Agent } from \"./agent/agent.ts\"\nimport { buildSystemPrompt } from \"./agent/prompt.ts\"\nimport { handleSessionCommand } from \"./commands/session.ts\"\nimport { getProvider, MODELS } from \"./config/providers.ts\"\nimport { configExists, loadAuth, loadConfig } from \"./config/store.ts\"\nimport { runOnboarding } from \"./onboarding/wizard.ts\"\nimport { getSessionStore } from \"./session/store.ts\"\nimport { getAllTools } from \"./tools/index.ts\"\nimport { getCurrentVersion, runUpdate } from \"./update.ts\"\n\nfunction parseCli() {\n\tconst { values, positionals } = parseArgs({\n\t\toptions: {\n\t\t\thelp: { type: \"boolean\", short: \"h\" },\n\t\t\tversion: { type: \"boolean\", short: \"v\" },\n\t\t\tprovider: { type: \"string\" },\n\t\t\tmodel: { type: \"string\" },\n\t\t\t\"api-key\": { type: \"string\" },\n\t\t\tsession: { type: \"string\", short: \"s\" },\n\t\t},\n\t\tstrict: true,\n\t\tallowPositionals: true,\n\t})\n\n\treturn { flags: values, args: positionals }\n}\n\nfunction findModel(modelId: string, providerId?: string) {\n\treturn MODELS.find((m) => {\n\t\tif (providerId) return m.provider === providerId && m.id === modelId\n\t\treturn m.id === modelId\n\t})\n}\n\nasync function main() {\n\tconst { flags, args } = parseCli()\n\n\tif (flags.version) {\n\t\tconst version = await getCurrentVersion()\n\t\tconsole.log(`nova ${version}`)\n\t\tprocess.exit(0)\n\t}\n\n\tif (flags.help) {\n\t\tconsole.log(`nova — open-source coding agent\n\nUsage:\n nova Interactive mode\n nova update Update to latest version\n nova session <cmd> Session management (list, delete)\n nova --session <id> Resume a session\n\nOptions:\n -h, --help Show help\n -v, --version Show version\n --provider <id> Provider to use\n --model <id> Model to use\n --api-key <key> API key override\n -s, --session <id> Resume session by ID`)\n\t\tprocess.exit(0)\n\t}\n\n\t// Handle session subcommand\n\tif (args[0] === \"session\") {\n\t\tawait handleSessionCommand(args.slice(1))\n\t\treturn\n\t}\n\n\t// Handle update subcommand\n\tif (args[0] === \"update\") {\n\t\tawait runUpdate()\n\t\treturn\n\t}\n\n\t// Reject positional args — use interactive mode with / commands\n\tif (args.length > 0) {\n\t\tconsole.error(chalk.yellow(`Unknown command: ${args.join(\" \")}`))\n\t\tconsole.error(\"Run `nova --help` for usage.\")\n\t\tprocess.exit(1)\n\t}\n\n\tconst controller = new AbortController()\n\n\tconst onSignal = () => {\n\t\tcontroller.abort()\n\t\tprocess.stderr.write(\"\\nAborted.\\n\")\n\t\tprocess.exit(130)\n\t}\n\tprocess.on(\"SIGINT\", onSignal)\n\tprocess.on(\"SIGTERM\", onSignal)\n\n\t// First-run onboarding\n\tconst config = await ((await configExists()) ? loadConfig() : runOnboarding())\n\tconst auth = await loadAuth()\n\n\t// CLI overrides\n\tconst providerId = (flags.provider as string) || config.provider\n\tconst modelId = (flags.model as string) || config.model\n\tconst apiKey = (flags[\"api-key\"] as string) || auth.apiKeys[providerId]\n\n\tconst provider = getProvider(providerId)\n\tif (!provider) {\n\t\tconsole.error(`Unknown provider: ${providerId}`)\n\t\tconsole.error(`Available: ${getProvider(\"glm\") ? \"glm, \" : \"\"}gemini, deepseek, openai`)\n\t\tprocess.exit(1)\n\t}\n\n\tif (!apiKey) {\n\t\tconsole.error(\n\t\t\t`No API key for ${provider.name}. Set ${provider.envKey} or run nova for onboarding.`,\n\t\t)\n\t\tprocess.exit(1)\n\t}\n\n\tconst model = findModel(modelId, providerId)\n\tif (!model) {\n\t\tconsole.error(`Unknown model: ${modelId}`)\n\t\tconsole.error(\"Available models:\")\n\t\tfor (const m of MODELS.filter((m) => m.provider === providerId)) {\n\t\t\tconsole.error(` ${m.id} — ${m.name}`)\n\t\t}\n\t\tprocess.exit(1)\n\t}\n\n\tconst cwd = process.cwd()\n\tconst tools = getAllTools(cwd)\n\tconst system = buildSystemPrompt(cwd, tools)\n\n\t// Session persistence\n\tconst store = getSessionStore()\n\tconst session = flags.session\n\t\t? store.get(flags.session as string)\n\t\t: store.create(cwd, model.id, providerId)\n\n\tif (flags.session && !session) {\n\t\tconsole.error(`Session not found: ${flags.session}`)\n\t\tprocess.exit(1)\n\t}\n\n\tconst sessionId = session!.id\n\tconst existingMessages = store.messages(sessionId)\n\n\tconst agent = new Agent({\n\t\tapi: provider.api,\n\t\tmodel,\n\t\tapiKey,\n\t\tbaseUrl: provider.baseUrl,\n\t\tsystem,\n\t\ttools,\n\t\tmessages: existingMessages,\n\t})\n\n\t// Interactive TUI mode\n\tprocess.off(\"SIGINT\", onSignal)\n\tprocess.off(\"SIGTERM\", onSignal)\n\tconst { interactive } = await import(\"./tui/app.tsx\")\n\tawait interactive(agent, store, sessionId)\n}\n\nprocess.on(\"unhandledRejection\", (reason) => {\n\tconsole.error(\"Unhandled rejection:\", reason)\n\tprocess.exit(1)\n})\n\nmain().catch((e) => {\n\tconsole.error(\"Fatal:\", e)\n\tprocess.exit(1)\n})\n"],"mappings":";4nBA+BA,SAAS,GAAa,EAAkC,CACvD,IAAM,EAA4B,CAAC,EAEnC,IAAK,IAAM,KAAO,EACjB,GAAI,EAAI,OAAS,OAAQ,CACxB,IAAM,EACL,OAAO,EAAI,SAAY,SACpB,CAAC,CAAE,KAAM,EAAI,OAAQ,CAAC,EACtB,EAAI,QAAQ,IAAK,GACb,EAAE,OAAS,OAAe,CAAE,KAAM,EAAE,IAAK,EACzC,EAAE,OAAS,QAAgB,CAAE,YAAa,CAAE,UAAW,EAAE,KAAM,KAAM,EAAE,IAAK,CAAE,EAC3E,CAAE,KAAM,EAAG,CAClB,EACJ,EAAS,KAAK,CAAE,KAAM,OAAQ,OAAM,CAAC,CACtC,MAAO,GAAI,EAAI,OAAS,YAAa,CACpC,IAAM,EAAsB,EAAI,QAAQ,IAAK,GACxC,EAAE,OAAS,OAAe,CAAE,KAAM,EAAE,KAAM,kBAAmB,EAAE,SAAU,EACzE,EAAE,OAAS,WACP,CAAE,QAAS,GAAM,KAAM,EAAE,KAAM,kBAAmB,EAAE,SAAU,EAClE,EAAE,OAAS,YACP,CAAE,cAAe,CAAE,KAAM,EAAE,KAAM,KAAM,EAAE,IAAK,EAAG,kBAAmB,EAAE,SAAU,EACjF,CAAE,KAAM,EAAG,CAClB,EACD,EAAS,KAAK,CAAE,KAAM,QAAS,OAAM,CAAC,CACvC,MAAO,GAAI,EAAI,OAAS,cAAe,CACtC,IAAM,EAAmB,CACxB,kBAAmB,CAClB,KAAM,EAAI,KACV,SAAU,CACT,QAAS,EAAI,QACX,IAAK,GAAO,EAAE,OAAS,OAAS,EAAE,KAAO,KAAK,UAAU,CAAC,CAAE,EAC3D,KAAK;CAAI,CACZ,CACD,CACD,EAEM,EAAO,EAAS,EAAS,OAAS,GAEpC,GAAQ,EAAK,OAAS,QAAU,EAAK,MAAM,KAAM,GAAM,EAAE,iBAAiB,EAC7E,EAAK,MAAM,KAAK,CAAI,EAEpB,EAAS,KAAK,CAAE,KAAM,OAAQ,MAAO,CAAC,CAAI,CAAE,CAAC,CAE/C,CAGD,OAAO,CACR,CAEA,SAAS,GAAc,EAA6B,CAEnD,OADI,EAAM,SAAW,EAAU,CAAC,EACzB,CACN,CACC,sBAAuB,EAAM,IAAK,IAAO,CACxC,KAAM,EAAE,KACR,YAAa,EAAE,YACf,WAAY,EAAE,UACf,EAAE,CACH,CACD,CACD,CAEA,MAAa,GACZ,GAC+C,CAC/C,IAAM,EAAK,IAAI,EAmKf,OAjKE,SAAY,CACb,GAAI,CAEH,IAAM,EAAM,GADI,EAAK,SAAW,4CACT,iBAAiB,EAAK,MAAM,GAAG,qCAAqC,EAAK,SAE1F,EAAO,CACZ,SAAU,GAAa,EAAK,QAAQ,EACpC,mBAAoB,EAAK,OAAS,CAAE,MAAO,CAAC,CAAE,KAAM,EAAK,MAAO,CAAC,CAAE,EAAI,IAAA,GACvE,MAAO,EAAK,MAAM,OAAS,EAAI,GAAc,EAAK,KAAK,EAAI,IAAA,GAC3D,iBAAkB,CACjB,eAAgB,EAAK,MAAM,iBAAmB,CAAE,cAAe,KAAM,EAAI,IAAA,EAC1E,CACD,EAEM,EAAW,MAAM,MAAM,EAAK,CACjC,OAAQ,OACR,QAAS,CACR,eAAgB,mBAChB,eAAgB,YACjB,EACA,KAAM,KAAK,UAAU,CAAI,EACzB,OAAQ,EAAK,MACd,CAAC,EAED,GAAI,CAAC,EAAS,GAAI,CACjB,IAAM,EAAO,MAAM,EAAS,KAAK,EAC7B,EAAM,EACV,GAAI,CACH,IAAM,EAAO,KAAK,MAAM,CAAI,EAC5B,EAAM,EAAK,OAAO,SAAW,EAAK,SAAW,CAC9C,MAAQ,CAER,CAEA,IAAM,EAAW,iBAAiB,EAAS,OAAO,KAAK,IACvD,EAAG,KAAK,CAAE,KAAM,aAAc,KAAM,CAAS,CAAC,EAC9C,EAAG,OAAO,CACT,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,CAAS,CAAC,EAC1C,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EACvB,KAAM,OACP,CAAC,EACD,MACD,CAEA,IAAM,EAAS,EAAS,MAAM,UAAU,EACxC,GAAI,CAAC,EAAQ,CACZ,EAAG,OAAO,CAAE,QAAS,CAAC,EAAG,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EAAG,KAAM,OAAQ,CAAC,EAClE,MACD,CAEA,IAAM,EAAU,IAAI,YAChB,EAAS,GACT,EAAe,CAAE,GAAI,EAAG,IAAK,CAAE,EAC/B,EAAmB,OACjB,EAAyB,CAAC,EAEhC,OAAa,CACZ,GAAM,CAAE,OAAM,SAAU,MAAM,EAAO,KAAK,EAC1C,GAAI,EAAM,MAEV,GAAU,EAAQ,OAAO,EAAO,CAAE,OAAQ,EAAK,CAAC,EAChD,IAAM,EAAQ,EAAO,MAAM;CAAI,EAC/B,EAAS,EAAM,IAAI,GAAK,GAExB,IAAK,IAAM,KAAQ,EAAO,CACzB,IAAM,EAAU,EAAK,KAAK,EAC1B,GAAI,CAAC,GAAS,WAAW,QAAQ,EAAG,SACpC,IAAM,EAAO,EAAQ,MAAM,CAAC,EAE5B,GAAI,CACH,IAAM,EAAQ,KAAK,MAAM,CAAI,EACvB,EAAY,EAAM,aAAa,GAWrC,GARI,EAAM,gBACT,EAAQ,CACP,GAAI,EAAM,cAAc,kBAAoB,EAAM,GAClD,IAAK,EAAM,cAAc,sBAAwB,EAAM,GACxD,EACA,EAAG,KAAK,CAAE,KAAM,QAAS,OAAM,CAAC,GAG7B,CAAC,EAAW,SAGhB,GAAI,EAAU,aAAc,CAC3B,IAAM,EAAS,EAAU,aACrB,IAAW,OAAQ,EAAO,OACrB,IAAW,aAAc,EAAO,UAChC,IAAW,UAAY,IAAW,cAAgB,IAAW,WACrE,EAAO,QACT,CAEA,IAAM,EAAQ,EAAU,SAAS,MACjC,GAAI,EACH,IAAK,IAAM,KAAQ,EAAO,CACzB,IAAM,EAAM,EAAK,mBAAqB,EAAK,iBAG3C,GAAI,EAAK,KACR,GAAI,EAAK,UAAY,IAAQ,OAAO,EAAK,SAAY,SAAU,CAC9D,IAAM,EAAc,OAAO,EAAK,SAAY,SAAW,EAAK,QAAU,EAAK,KAC3E,EAAG,KAAK,CAAE,KAAM,iBAAkB,KAAM,CAAY,CAAC,EACrD,IAAM,EAAO,EAAQ,EAAQ,OAAS,GAClC,GAAM,OAAS,WAClB,EAAK,MAAQ,EAEb,EAAQ,KAAK,CAAE,KAAM,WAAY,KAAM,EAAa,UAAW,CAAI,CAAC,CAEtE,KAAO,CACN,EAAG,KAAK,CAAE,KAAM,aAAc,KAAM,EAAK,IAAK,CAAC,EAC/C,IAAM,EAAO,EAAQ,EAAQ,OAAS,GAClC,GAAM,OAAS,OAClB,EAAK,MAAQ,EAAK,KAElB,EAAQ,KAAK,CAAE,KAAM,OAAQ,KAAM,EAAK,KAAM,UAAW,CAAI,CAAC,CAEhE,CAID,IAAM,EAAK,EAAK,cAAgB,EAAK,cACrC,GAAI,EAAI,CACP,IAAM,EAAO,EAAG,KACV,EAAQ,EAAG,MAAoC,CAAC,EAGhD,EAAwB,CAC7B,KAAM,YACN,GAAA,QAJkB,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,EAAG,CAAC,IAKvD,OACA,OACA,UAAW,CACZ,EACA,EAAQ,KAAK,CAAQ,EACrB,EAAG,KAAK,CAAE,KAAM,YAAa,KAAM,CAAS,CAAC,EAC7C,EAAO,UACR,CACD,CAEF,MAAa,CACR,EAAK,KAAK,IAAM,IAAM,EAAK,KAAK,CAGrC,CACD,CACD,CAEA,EAAG,OAAO,CAAE,UAAS,QAAO,MAAK,CAAC,CACnC,OAAS,EAAG,CACX,GAAI,EAAK,QAAQ,QAAS,OAC1B,IAAM,EAAW,iCAAiC,aAAa,MAAQ,EAAE,QAAU,OAAO,CAAC,IAC3F,EAAG,KAAK,CAAE,KAAM,aAAc,KAAM,CAAS,CAAC,EAC9C,EAAG,OAAO,CACT,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,CAAS,CAAC,EAC1C,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EACvB,KAAM,OACP,CAAC,CACF,CACD,GAAG,EAEI,CACR,ECxPA,SAAS,GAAY,EAAmC,CACvD,GAAI,EAAI,OAAS,OAChB,MAAO,CACN,KAAM,OACN,QACC,OAAO,EAAI,SAAY,SACpB,EAAI,QACJ,EAAI,QAAQ,IAAK,GACb,EAAE,OAAS,OAAe,CAAE,KAAM,OAAQ,KAAM,EAAE,IAAK,EACvD,EAAE,OAAS,QACP,CAAE,KAAM,YAAa,UAAW,CAAE,IAAK,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAO,CAAE,EAC5E,CAAE,KAAM,OAAQ,KAAM,EAAG,CAChC,CACL,EAED,GAAI,EAAI,OAAS,YAAa,CAC7B,IAAM,EAAsB,CAAC,EACvB,EAAuB,CAAC,EAE9B,IAAK,IAAM,KAAK,EAAI,QACf,EAAE,OAAS,QAAQ,EAAU,KAAK,EAAE,IAAI,EAExC,EAAE,OAAS,aACd,EAAU,KAAK,CACd,KAAM,WACN,GAAI,EAAE,GACN,SAAU,CAAE,KAAM,EAAE,KAAM,UAAW,KAAK,UAAU,EAAE,IAAI,CAAE,CAC7D,CAAC,EAGH,IAAM,EAAkC,CACvC,KAAM,YACN,QAAS,EAAU,OAAS,EAAI,EAAU,KAAK,EAAE,EAAI,IACtD,EAEA,OADI,EAAU,OAAS,IAAG,EAAO,WAAa,GACvC,CACR,CASA,OAPI,EAAI,OAAS,cACT,CACN,KAAM,OACN,aAAc,EAAI,OAClB,QAAS,EAAI,QAAQ,IAAK,GAAO,EAAE,OAAS,OAAS,EAAE,KAAO,KAAK,UAAU,CAAC,CAAE,EAAE,KAAK;CAAI,CAC5F,EAEM,CAAE,KAAM,OAAQ,QAAS,EAAG,CACpC,CAEA,SAAS,GAAc,EAA6B,CACnD,OAAO,EAAM,IAAK,IAAO,CACxB,KAAM,WACN,SAAU,CACT,KAAM,EAAE,KACR,YAAa,EAAE,YACf,WAAY,EAAE,UACf,CACD,EAAE,CACH,CAEA,MAAa,GACZ,GAC+C,CAC/C,IAAM,EAAK,IAAI,EA2If,OAzIE,SAAY,CACb,GAAI,CACH,IAAM,EAAO,CACZ,MAAO,EAAK,MAAM,GAClB,SAAU,CAAC,CAAE,KAAM,SAAU,QAAS,EAAK,MAAO,EAAG,GAAG,EAAK,SAAS,IAAI,EAAW,CAAC,EACtF,MAAO,EAAK,MAAM,OAAS,EAAI,GAAc,EAAK,KAAK,EAAI,IAAA,GAC3D,OAAQ,EACT,EAEM,EAAW,MAAM,MAAM,GAAG,EAAK,QAAQ,mBAAoB,CAChE,OAAQ,OACR,QAAS,CACR,eAAgB,mBAChB,cAAe,UAAU,EAAK,QAC/B,EACA,KAAM,KAAK,UAAU,CAAI,EACzB,OAAQ,EAAK,MACd,CAAC,EAED,GAAI,CAAC,EAAS,GAAI,CACjB,IAAM,EAAO,MAAM,EAAS,KAAK,EAC3B,EAAW,aAAa,EAAS,OAAO,IAAI,IAClD,EAAG,KAAK,CAAE,KAAM,aAAc,KAAM,CAAS,CAAC,EAC9C,EAAG,OAAO,CACT,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,CAAS,CAAC,EAC1C,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EACvB,KAAM,OACP,CAAC,EACD,MACD,CAEA,IAAM,EAAS,EAAS,MAAM,UAAU,EACxC,GAAI,CAAC,EAAQ,CACZ,EAAG,OAAO,CAAE,QAAS,CAAC,EAAG,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EAAG,KAAM,OAAQ,CAAC,EAClE,MACD,CAEA,IAAM,EAAU,IAAI,YAChB,EAAS,GACP,EAAmB,IAAI,IACzB,EAAe,CAAE,GAAI,EAAG,IAAK,CAAE,EAC/B,EAAc,GACd,EAAO,OAEX,OAAa,CACZ,GAAM,CAAE,OAAM,SAAU,MAAM,EAAO,KAAK,EAC1C,GAAI,EAAM,MAEV,GAAU,EAAQ,OAAO,EAAO,CAAE,OAAQ,EAAK,CAAC,EAChD,IAAM,EAAQ,EAAO,MAAM;CAAI,EAC/B,EAAS,EAAM,IAAI,GAAK,GAExB,IAAK,IAAM,KAAQ,EAAO,CACzB,IAAM,EAAU,EAAK,KAAK,EAC1B,GAAI,CAAC,GAAS,WAAW,QAAQ,EAAG,SACpC,IAAM,EAAO,EAAQ,MAAM,CAAC,EACxB,OAAS,SAEb,GAAI,CACH,IAAM,EAAQ,KAAK,MAAM,CAAI,EACvB,EAAQ,EAAM,UAAU,IAAI,MAClC,GAAI,CAAC,EAAO,SAOZ,GALI,EAAM,UACT,EAAG,KAAK,CAAE,KAAM,aAAc,KAAM,EAAM,OAAQ,CAAC,EACnD,GAAe,EAAM,SAGlB,EAAM,WACT,IAAK,IAAM,KAAM,EAAM,WAAY,CAClC,IAAM,EAAM,EAAG,OAAS,EACnB,EAAiB,IAAI,CAAG,GAC5B,EAAiB,IAAI,EAAK,CACzB,GAAI,EAAG,IAAM,GACb,KAAM,EAAG,UAAU,MAAQ,GAC3B,KAAM,EACP,CAAC,EAEF,IAAM,EAAW,EAAiB,IAAI,CAAG,EACrC,EAAG,KAAI,EAAS,GAAK,EAAG,IACxB,EAAG,UAAU,OAAM,EAAS,KAAO,EAAG,SAAS,MAC/C,EAAG,UAAU,YAAW,EAAS,MAAQ,EAAG,SAAS,UAC1D,CAGG,EAAM,QACT,EAAQ,CACP,GAAI,EAAM,MAAM,eAAiB,EACjC,IAAK,EAAM,MAAM,mBAAqB,CACvC,EACA,EAAG,KAAK,CAAE,KAAM,QAAS,OAAM,CAAC,GAGjC,IAAM,EAAe,EAAM,UAAU,IAAI,cACrC,IAAc,EAAO,EAC1B,MAAQ,CAER,CACD,CACD,CAEA,IAAM,EAAsC,CAAC,EACzC,GACH,EAAQ,KAAK,CAAE,KAAM,OAAQ,KAAM,CAAY,CAAC,EAEjD,IAAK,GAAM,EAAG,KAAO,EACpB,EAAQ,KAAK,CACZ,KAAM,YACN,GAAI,EAAG,GACP,KAAM,EAAG,KACT,KAAM,KAAK,MAAM,EAAG,MAAQ,IAAI,CACjC,CAAC,EACD,EAAG,KAAK,CACP,KAAM,YACN,KAAM,CACL,KAAM,YACN,GAAI,EAAG,GACP,KAAM,EAAG,KACT,KAAM,KAAK,MAAM,EAAG,MAAQ,IAAI,CACjC,CACD,CAAC,EACD,EAAO,WAGR,EAAG,OAAO,CAAE,UAAS,QAAa,MAAmB,CAAC,CACvD,OAAS,EAAG,CACX,GAAI,EAAK,QAAQ,QAAS,OAC1B,IAAM,EAAW,qBAAqB,aAAa,MAAQ,EAAE,QAAU,OAAO,CAAC,IAC/E,EAAG,KAAK,CAAE,KAAM,aAAc,KAAM,CAAS,CAAC,EAC9C,EAAG,OAAO,CACT,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,CAAS,CAAC,EAC1C,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EACvB,KAAM,OACP,CAAC,CACF,CACD,GAAG,EAEI,CACR,ECzMA,IAAa,EAAb,KAA+B,CAC9B,GAAe,CAAC,EAChB,GAAQ,GACR,GACA,GACA,GACA,GAAS,GAET,KAAK,EAAgB,CAChB,SAAKA,GAET,GAAI,KAAKC,GAAU,CAClB,IAAM,EAAU,KAAKA,GACrB,KAAKA,GAAW,IAAA,GAChB,EAAQ,CAAK,CACd,MACC,KAAKC,GAAQ,KAAK,CAAK,CAEzB,CAEA,OAAO,EAAiB,CACvB,KAAKC,GAAQ,GACb,KAAKC,GAAU,EAEX,KAAKH,IAER,KAAKA,GAAS,IAAA,EAAc,EAEzB,KAAKI,IACR,KAAKA,GAAa,CAAM,CAE1B,CAEA,OAAc,CACb,KAAKL,GAAS,GACd,KAAKG,GAAQ,GACT,KAAKF,IACR,KAAKA,GAAS,IAAA,EAAc,EAEzB,KAAKI,IACR,KAAKA,GAAa,IAAA,EAAc,CAElC,CAEA,OAAQ,OAAO,gBAAoC,CAClD,KAAO,CAAC,KAAKF,IAAS,KAAKD,GAAQ,OAAS,GAAG,CAC9C,GAAI,KAAKA,GAAQ,OAAS,EAAG,CAC5B,MAAM,KAAKA,GAAQ,MAAM,EACzB,QACD,CACA,GAAI,KAAKC,GAAO,MAChB,IAAM,EAAO,MAAM,IAAI,QAAwB,GAAY,CAC1D,KAAKF,GAAW,CACjB,CAAC,EACG,IAAS,IAAA,KACZ,MAAM,EAER,CACD,CAEA,IAAI,QAAwB,CAC3B,OAAO,KAAKG,EACb,CAEA,IAAI,QAAkB,CACrB,OAAO,KAAKD,EACb,CACD,EAGA,MAAM,GAAW,IAAI,IAAyB,CAC7C,CAAC,SAAU,EAAY,EACvB,CAAC,SAAU,EAAY,CACxB,CAAC,EAOD,SAAgB,EAAO,EAA4D,CAClF,IAAM,EAAK,GAAS,IAAI,EAAK,GAAG,EAChC,GAAI,CAAC,EAAI,MAAU,MAAM,0CAA0C,EAAK,KAAK,EAI7E,IAAM,EAAiB,EAAG,CAAI,EACxB,EAAc,IAAI,EAgCxB,OA9BE,SAAY,CACb,UAAW,IAAM,KAAS,EACrB,EAAM,OAAS,aAClB,EAAY,KAAK,CAAE,KAAM,aAAc,KAAM,EAAM,MAAQ,EAAG,CAAC,EACrD,EAAM,OAAS,iBACzB,EAAY,KAAK,CAAE,KAAM,iBAAkB,KAAM,EAAM,MAAQ,EAAG,CAAC,EACzD,EAAM,OAAS,aAAe,EAAM,KAC9C,EAAY,KAAK,CAChB,KAAM,YACN,KAAM,CACL,KAAM,YACN,GAAI,EAAM,KAAK,GACf,KAAM,EAAM,KAAK,KACjB,KAAM,EAAM,KAAK,IAClB,CACD,CAAC,EACS,EAAM,OAAS,SAAW,EAAM,OAC1C,EAAY,KAAK,CAAE,KAAM,QAAS,MAAO,EAAM,KAAM,CAAC,EAIxD,IAAM,EAAM,EAAe,OACvB,EACH,EAAY,OAAO,CAAG,EAGtB,EAAY,OAAO,CAAE,QAAS,CAAC,EAAG,MAAO,CAAE,GAAI,EAAG,IAAK,CAAE,EAAG,KAAM,MAAO,CAAC,CAE5E,GAAG,EAEI,CACR,CChIA,SAAgB,EAAe,EAAyB,CACvD,IAAI,EAAQ,EACZ,IAAK,IAAM,KAAO,EACjB,GAAI,OAAO,EAAI,SAAY,SAC1B,GAAS,EAAI,QAAQ,YACf,GAAI,MAAM,QAAQ,EAAI,OAAO,MAC9B,IAAM,KAAQ,EAAI,QAClB,EAAK,OAAS,SAAQ,GAAS,EAAK,KAAK,QAIhD,OAAO,KAAK,KAAK,EAAQ,CAAC,CAC3B,CAEA,SAAgB,EAAS,EAAqB,CAC7C,MAAO,CAAE,KAAM,OAAQ,KAAM,CAAE,CAChC,CAEA,SAAgB,EAAoB,EAAa,EAA0B,CAI1E,OAHI,IAAa,GAAO,EAAS,WAAW,GAAG,EAAI,EAAE,EAC7C,EAAS,EAAK,CAAQ,GAAK,IAE5B,CACR,CAEA,SAAgB,GAAa,EAAqB,CACjD,GAAI,OAAO,GAAQ,SAAU,OAAO,EAEpC,IAAI,EAAU,EACV,EAAS,GAMb,GALI,EAAI,WAAW,SAAS,IAC3B,EAAU,EAAI,MAAM,CAAC,EACrB,EAAS,WAGN,EAAW,CAAO,EAAG,CACxB,IAAM,EAAM,QAAQ,IAAI,EACxB,OAAO,EAAS,EAAoB,EAAK,CAAO,CACjD,CACA,OAAO,CACR,CAEA,SAAgB,GACf,EACA,EAAW,GACF,CAET,OADK,EACE,OAAO,QAAQ,CAAI,EACxB,KAAK,CAAC,EAAG,KAAO,CAChB,IAAM,EAAM,OAAO,GAAM,SAAW,GAAa,CAAC,EAAI,KAAK,UAAU,CAAC,EAChE,EAAS,EAAI,OAAS,GAAK,GAAG,EAAI,MAAM,EAAG,EAAE,EAAE,GAAK,EAE1D,MAAO,GADQ,EAAW,EAAM,IAAI,GAAG,EAAE,EAAE,EAAI,GAAG,EAAE,GACnC,GAAG,GACrB,CAAC,EACA,KAAK,GAAG,EARQ,EASnB,CC3CA,MAEM,GAAc,GACnB,OAAO,GAAM,YAAY,GAAe,EAAmB,OAAS,YAKrE,SAAgB,GACf,EACA,EACA,EACA,EACiC,CACjC,IAAM,EAAK,IAAI,EACT,EAAa,CAAC,EACd,EAAW,EAAK,UAAY,GAE5B,EAAe,CAAE,KAAM,OAAQ,QAAS,EAAO,GAAI,KAAK,IAAI,CAAE,EAChE,EAAqB,CAAE,GAAG,EAAK,SAAU,CAAC,GAAG,EAAI,SAAU,CAAO,CAAE,EAsHxE,OArHA,EAAI,KAAK,CAAO,GAoHhB,SAlHyB,CACxB,EAAG,KAAK,CAAE,KAAM,OAAQ,CAAC,EAEzB,GAAI,CACH,IAAI,EAAQ,EACZ,KAAO,EAAQ,GACV,IAAQ,SADY,CAGxB,IACA,EAAG,KAAK,CAAE,KAAM,MAAO,CAAC,EAGxB,IAAM,EAAe,EAAe,EAAU,QAAQ,EAClD,EAAe,EAAK,MAAM,cAAgB,IAC7C,EAAG,KAAK,CACP,KAAM,aACN,KAAM,yCAAyC,KAAK,MAAM,EAAe,GAAI,EAAE,MAAM,KAAK,MAAM,EAAK,MAAM,cAAgB,GAAI,EAAE,UAClI,CAAC,EAGF,IAAM,EAAQ,MAAM,GAAS,EAAW,EAAM,EAAI,CAAM,EAKxD,GAJA,EAAI,KAAK,CAAK,EACd,EAAY,CAAE,GAAG,EAAW,SAAU,CAAC,GAAG,EAAU,SAAU,CAAK,CAAE,EACrE,EAAG,KAAK,CAAE,KAAM,gBAAiB,IAAK,CAAM,CAAC,EAEzC,EAAM,OAAS,SAAW,EAAM,OAAS,UAAW,CACvD,EAAG,KAAK,CAAE,KAAM,WAAY,IAAK,EAAO,QAAS,CAAC,CAAE,CAAC,EACrD,KACD,CAEA,IAAM,EAAQ,EAAM,QAAQ,OAAO,EAAU,EAC7C,GAAI,EAAM,SAAW,EAAG,CACvB,EAAG,KAAK,CAAE,KAAM,WAAY,IAAK,EAAO,QAAS,CAAC,CAAE,CAAC,EACrD,KACD,CAGA,IAAM,EAA2B,CAAC,EAClC,IAAK,IAAM,KAAQ,EAAO,CACzB,GAAI,GAAQ,QAAS,MAErB,IAAM,EAAO,EAAU,MAAM,KAAM,GAAM,EAAE,IAAI,OAAS,EAAK,IAAI,EACjE,GAAI,CAAC,EAAM,CACV,IAAM,EAA2B,CAChC,KAAM,cACN,OAAQ,EAAK,GACb,KAAM,EAAK,KACX,QAAS,CAAC,EAAS,iBAAiB,EAAK,MAAM,CAAC,EAChD,QAAS,GACT,GAAI,KAAK,IAAI,CACd,EACA,EAAQ,KAAK,CAAS,EACtB,EAAY,CAAE,GAAG,EAAW,SAAU,CAAC,GAAG,EAAU,SAAU,CAAS,CAAE,EACzE,EAAI,KAAK,CAAS,EAClB,QACD,CAGA,IAAM,EAAU,MAAM,EAAK,aAAa,EAAM,EAAK,KAAM,CAAS,EAClE,GAAI,GAAS,MAAO,CACnB,IAAM,EAA6B,CAClC,KAAM,cACN,OAAQ,EAAK,GACb,KAAM,EAAK,KACX,QAAS,CAAC,EAAS,EAAQ,QAAU,SAAS,CAAC,EAC/C,QAAS,GACT,GAAI,KAAK,IAAI,CACd,EACA,EAAQ,KAAK,CAAW,EACxB,EAAY,CAAE,GAAG,EAAW,SAAU,CAAC,GAAG,EAAU,SAAU,CAAW,CAAE,EAC3E,EAAI,KAAK,CAAW,EACpB,QACD,CAGA,IAAM,EAAS,MAAM,EAAK,QAAQ,EAAK,KAAM,CAAM,EAC7C,EAAyB,CAC9B,KAAM,cACN,OAAQ,EAAK,GACb,KAAM,EAAK,KACX,KAAM,EAAK,KACX,QAAS,EAAO,QAChB,QAAS,EAAO,QAChB,GAAI,KAAK,IAAI,CACd,EAEA,EAAQ,KAAK,CAAO,EACpB,EAAY,CAAE,GAAG,EAAW,SAAU,CAAC,GAAG,EAAU,SAAU,CAAO,CAAE,EACvE,EAAI,KAAK,CAAO,EAChB,EAAG,KAAK,CAAE,KAAM,cAAe,OAAQ,EAAK,GAAI,OAAQ,EAAS,KAAM,EAAK,IAAK,CAAC,EAElF,MAAM,EAAK,YAAY,EAAM,EAAS,CAAS,CAChD,CAEA,EAAG,KAAK,CAAE,KAAM,WAAY,IAAK,EAAO,SAAQ,CAAC,CAClD,CAEI,GAAS,GACZ,EAAG,KAAK,CACP,KAAM,aACN,KAAM,uBAAuB,EAAS,GACvC,CAAC,CAEH,OAAS,EAAG,CACX,GAAK,EAAY,OAAS,aAAc,CACvC,EAAG,OAAO,CAAG,EACb,MACD,CACA,MAAM,CACP,CAEA,EAAG,OAAO,CAAG,CACd,GAEK,EACE,CACR,CAEA,eAAe,GACd,EACA,EACA,EACA,EACwB,CACxB,IAAM,EAAiB,EAAO,CAC7B,IAAK,EAAK,IACV,MAAO,EAAK,MACZ,OAAQ,EAAK,OACb,QAAS,EAAK,QACd,OAAQ,EAAI,OACZ,SAAU,EAAI,SACd,MAAO,EAAI,MAAM,IAAK,GAAM,EAAE,GAAG,EACjC,QACD,CAAC,EAEK,EAAmC,CAAC,EACtC,EAAQ,CAAE,GAAI,EAAG,IAAK,CAAE,EAG5B,UAAW,IAAM,KAAM,EACtB,GAAI,EAAG,OAAS,cAAgB,EAAG,KAAM,CACxC,IAAM,EAAO,EAAQ,EAAQ,OAAS,GAClC,GAAM,OAAS,OAClB,EAAK,MAAQ,EAAG,KAEhB,EAAQ,KAAK,EAAS,EAAG,IAAI,CAAC,EAE/B,EAAG,KAAK,CAAE,KAAM,aAAc,KAAM,EAAG,IAAK,CAAC,CAC9C,MAAO,GAAI,EAAG,OAAS,kBAAoB,EAAG,KAAM,CACnD,IAAM,EAAO,EAAQ,EAAQ,OAAS,GAClC,GAAM,OAAS,WAClB,EAAK,MAAQ,EAAG,KAEhB,EAAQ,KAAK,CAAE,KAAM,WAAY,KAAM,EAAG,IAAK,CAAC,EAEjD,EAAG,KAAK,CAAE,KAAM,iBAAkB,KAAM,EAAG,IAAK,CAAC,CAClD,MAAW,EAAG,OAAS,aAAe,EAAG,MACxC,EAAQ,KAAK,EAAG,IAAI,EACpB,EAAG,KAAK,CAAE,KAAM,YAAa,KAAM,EAAG,IAAK,CAAC,GAClC,EAAG,OAAS,SAAW,EAAG,QACpC,EAAQ,EAAG,MACX,EAAG,KAAK,CAAE,KAAM,QAAS,OAAM,CAAC,GAIlC,IAAM,EACL,EAAe,QAAQ,SAAW,EAAe,OAAO,QAAQ,OAAS,EACtE,EAAe,OAAO,QACtB,EAGE,EADU,EAAW,KAAM,GAAM,EAAE,OAAS,WACrB,EAC1B,EAAW,OAAQ,GAAM,EAAE,OAAS,QAAU,EAAE,KAAK,KAAK,EAAE,OAAS,CAAC,EACtE,EAEG,EAAM,EAAe,OAa3B,OAZI,EACI,CACN,KAAM,YACN,QAAS,EACT,MAAO,EAAK,MAAM,GAClB,SAAU,EAAK,MAAM,SACrB,MAAO,EAAI,MACX,KAAM,EAAI,KACV,GAAI,KAAK,IAAI,CACd,EAGM,CACN,KAAM,YACN,QAAS,EACT,MAAO,EAAK,MAAM,GAClB,SAAU,EAAK,MAAM,SACrB,QACA,KAAM,EAAe,KAAM,GAAM,EAAE,OAAS,WAAW,EAAI,WAAa,OACxE,GAAI,KAAK,IAAI,CACd,CACD,CCxOA,IAAa,GAAb,KAAmB,CAClB,GACA,GACA,GACA,GAAmB,CAAC,EACpB,GACA,GACA,GAEA,YAAY,EAQT,CACF,KAAKG,GAAO,EAAK,IACjB,KAAKC,GAAS,EAAK,MACnB,KAAKC,GAAU,EAAK,OACpB,KAAKC,GAAW,EAAK,QACrB,KAAKC,GAAU,EAAK,OACpB,KAAKC,GAAS,EAAK,MACnB,KAAKC,GAAY,EAAK,UAAY,CAAC,CACpC,CAEA,IAAI,OAAe,CAClB,OAAO,KAAKL,EACb,CAEA,IAAI,UAAkB,CACrB,OAAO,KAAKK,EACb,CAEA,IAAI,OAAgB,CACnB,OAAO,KAAKD,EACb,CAEA,IAAI,QAAiB,CACpB,OAAO,KAAKH,EACb,CAEA,IAAI,SAAkB,CACrB,OAAO,KAAKC,EACb,CAEA,aAAa,EAA+E,CAC3F,KAAKH,GAAO,EAAK,IACjB,KAAKC,GAAS,EAAK,MACnB,KAAKC,GAAU,EAAK,OACpB,KAAKC,GAAW,EAAK,OACtB,CAEA,SAAS,EAAqB,CAC7B,KAAKE,GAAS,CACf,CAEA,YAAY,EAAmB,CAC9B,KAAKC,GAAY,CAClB,CAEA,SAAS,EAAoB,CAC5B,KAAKL,GAAS,CACf,CAEA,OAAO,EAAe,EAAsD,CAc3E,OAAO,GAAI,EAAO,CAZjB,OAAQ,KAAKG,GACb,SAAU,KAAKE,GACf,MAAO,KAAKD,EAUO,EAAG,CANtB,IAAK,KAAKL,GACV,MAAO,KAAKC,GACZ,OAAQ,KAAKC,GACb,QAAS,KAAKC,EAGW,EAAG,CAAM,CACpC,CACD,EC/EA,SAAgB,GAAkB,EAAa,EAAuB,CACrE,IAAM,EAAW,EAAM,IAAK,GAAM,KAAK,EAAE,IAAI,KAAK,IAAI,EAAE,IAAI,aAAa,EAAE,KAAK;CAAI,EAC9E,EAAW,EAAG,SAAS,EACvB,EAAO,EAAG,KAAK,EAIrB,MAAO;;;;;;EAMN,EAAS;;;;uBAIY,EAAI;sBACL,EAAS,IAdd,EAAG,QAcqB,EAAE;kBACzB,EAAK;WAdR,QAAQ,IAAI,OAAS,UAenB;UACP,IAAI,KAAK,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;6CAqBjD,CCXA,SAAS,IAAqB,CAC7B,MAAO,GAAG,KAAK,IAAI,EAAE,SAAS,EAAE,EAAE,GAAG,OAAO,WAAW,EAAE,MAAM,EAAG,CAAC,GACpE,CAEA,IAAa,GAAb,MAAa,CAAa,CACzB,GAEA,YAAY,EAAgB,CAC3B,KAAKI,GAAM,EAAaC,GAAM,CAAM,CACrC,CAIA,MAAOA,GAAM,EAAwC,CACpD,IAAM,EAAQ,IACb,EAAG,OAAO,oBAAoB,EAC9B,EAAG,OAAO,mBAAmB,EAC7B,EAAG,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAM,EACP,GAER,GAAI,CACH,OAAO,EAAK,IAAI,EAAc,CAAM,CAAC,CACtC,MAAQ,CAGP,IAAK,IAAM,IAAK,CAAC,EAAQ,GAAG,EAAO,MAAO,GAAG,EAAO,KAAK,EACxD,GAAI,CACH,EAAW,CAAC,CACb,MAAQ,CAER,CAED,OAAO,EAAK,IAAI,EAAc,CAAM,CAAC,CACtC,CACD,CAEA,OAAO,EAAa,EAAe,EAA2B,CAC7D,IAAM,EAAK,GAAW,EAChB,EAAM,KAAK,IAAI,EAcrB,OAbA,KAAKD,GACH,QACA,4IACD,EACC,IAAI,CACA,KACC,MACE,QACG,WACV,MAAO,KACP,QAAS,EACT,QAAS,CACV,CAAC,EACK,CAAE,KAAI,MAAK,QAAO,WAAU,MAAO,KAAM,QAAS,EAAK,QAAS,CAAI,CAC5E,CAEA,IAAI,EAA4B,CAC/B,OACE,KAAKA,GACJ,QACA,uFACD,EACC,IAAI,CAAM,IAAG,CAAC,GAAwB,IAE1C,CAEA,KAAK,EAAQ,GAAe,CAC3B,OAAO,KAAKA,GACV,QACA,2GACD,EACC,IAAI,CAAS,OAAM,CAAC,CACvB,CAEA,OAAO,EAAqB,CAE3B,OADe,KAAKA,GAAI,QAAQ,qCAAqC,EAAE,IAAI,CAAM,IAAG,CACxE,EAAE,QAAU,CACzB,CAEA,OAAO,EAAmB,EAAgB,CACzC,IAAM,EAAM,KAAKE,GAAS,CAAS,EACnC,KAAKF,GACH,QACA,qGACD,EACC,IAAI,CACJ,IAAK,EACA,MACL,KAAM,EAAI,KACV,QAAS,KAAK,UAAU,CAAG,EAC3B,GAAI,EAAI,EACT,CAAC,EACF,KAAKA,GACH,QAAQ,mDAAmD,EAC3D,IAAI,CAAE,IAAK,KAAK,IAAI,EAAG,GAAI,CAAU,CAAC,CACzC,CAEA,WAAW,EAAmB,EAAmB,CAMhD,KALgBA,GAAI,gBAAkB,CACrC,IAAK,IAAM,KAAO,EACjB,KAAK,OAAO,EAAW,CAAG,CAE5B,CACC,EAAE,CACJ,CAEA,SAAS,EAA0B,CAIlC,OAHa,KAAKA,GAChB,QAAQ,uEAAuE,EAC/E,IAAI,CAAE,IAAK,CAAU,CACb,EAAE,IAAK,GAAM,KAAK,MAAM,EAAE,OAAO,CAAQ,CACpD,CAEA,cAAc,EAAmB,EAAyB,CAMzD,OALa,KAAKA,GAChB,QACA,sFACD,EACC,IAAI,CAAE,IAAK,EAAW,IAAK,CAAS,CAC5B,EAAE,IAAK,GAAM,KAAK,MAAM,EAAE,OAAO,CAAQ,CACpD,CAEA,SAAS,EAAmB,EAAqB,CAChD,KAAKA,GACH,QAAQ,mDAAmD,EAC3D,IAAI,CAAS,QAAO,GAAI,CAAU,CAAC,CACtC,CAEA,aAAa,EAA2B,CAIvC,OAHY,KAAKA,GACf,QAAQ,gEAAgE,EACxE,IAAI,CAAE,IAAK,CAAU,CACd,EAAE,KACZ,CAEA,eACC,EACA,EACA,EACA,EACA,EACO,CACP,KAAKA,GACH,QACA,0IACD,EACC,IAAI,CACJ,IAAK,EACI,UACT,KAAM,KAAK,UAAU,CAAS,EAC9B,MAAO,KAAK,UAAU,CAAU,EAChC,IAAK,EACL,GAAI,KAAK,IAAI,CACd,CAAC,CACH,CAEA,oBAAoB,EAAkE,CACrF,OACE,KAAKA,GACJ,QACA,8FACD,EACC,IAAI,CAAE,IAAK,CAAU,CAAC,GAAuD,IAEjF,CAEA,kBAAkB,EAAmB,EAAmB,CACvD,KAAKA,GACH,QAAQ,6DAA6D,EACrE,IAAI,CAAE,IAAK,EAAgB,KAAI,CAAC,CACnC,CAEA,OAAc,CACb,KAAKA,GAAI,MAAM,CAChB,CAEA,GAAS,EAA2B,CAInC,OAHY,KAAKA,GACf,QAAQ,iEAAiE,EACzE,IAAI,CAAE,IAAK,CAAU,CACb,EAAE,QAAU,GAAK,CAC5B,CACD,EAEA,IAAI,EAA8B,KAElC,SAAgB,EAAgB,EAA4B,CAI3D,OAHI,IAEJ,EAAS,IAAI,GADE,EAAK,GAAO,EAAK,QAAQ,IAAI,MAAQ,IAAK,WAAW,EAAG,aACxC,CAAC,EACzB,EACR,CClOA,eAAsB,GAAqB,EAA+B,CACzE,IAAM,EAAQ,EAAgB,EACxB,CAAC,EAAY,GAAM,EAEzB,GAAI,IAAe,QAAU,IAAe,KAAM,CACjD,IAAM,EAAW,EAAM,KAAK,EAC5B,GAAI,EAAS,SAAW,EAAG,CAC1B,QAAQ,IAAI,oBAAoB,EAChC,MACD,CAEA,QAAQ,IAAI,KAAK,OAAO,EAAE,EAAG,QAAQ,OAAO,EAAE,EAAG,SAAS,EAC1D,QAAQ,IAAI,IAAI,OAAO,EAAE,CAAC,EAC1B,IAAK,IAAM,KAAK,EAAU,CACzB,IAAM,EAAO,IAAI,KAAK,EAAE,OAAO,EAAE,eAAe,EAChD,QAAQ,IAAI,EAAE,GAAG,OAAO,EAAE,EAAG,EAAE,MAAM,OAAO,EAAE,EAAG,CAAI,CACtD,CACA,MACD,CAEA,GAAI,IAAe,UAAY,IAAe,KAAM,CAC9C,IACJ,QAAQ,MAAM,qCAAqC,EACnD,QAAQ,KAAK,CAAC,GAEC,EAAM,OAAO,CACnB,EACT,QAAQ,IAAI,oBAAoB,GAAI,GAEpC,QAAQ,MAAM,sBAAsB,GAAI,EACxC,QAAQ,KAAK,CAAC,GAEf,MACD,CAEA,QAAQ,MAAM,qDAAqD,EACnE,QAAQ,KAAK,CAAC,CACf,CCrCA,MAAa,EAA2B,CACvC,CACC,GAAI,MACJ,KAAM,aACN,IAAK,SACL,QAAS,sCACT,OAAQ,aACT,EACA,CACC,GAAI,SACJ,KAAM,kBACN,IAAK,SACL,QAAS,4CACT,OAAQ,gBACT,EACA,CACC,GAAI,WACJ,KAAM,WACN,IAAK,SACL,QAAS,2BACT,OAAQ,kBACT,EACA,CACC,GAAI,SACJ,KAAM,SACN,IAAK,SACL,QAAS,4BACT,OAAQ,gBACT,CACD,EAEa,EAAkB,CAE9B,CACC,GAAI,UACJ,KAAM,UACN,SAAU,MACV,cAAe,MACf,UAAW,KACX,iBAAkB,EACnB,EACA,CACC,GAAI,QACJ,KAAM,QACN,SAAU,MACV,cAAe,MACf,UAAW,KACX,iBAAkB,EACnB,EACA,CACC,GAAI,cACJ,KAAM,cACN,SAAU,MACV,cAAe,MACf,UAAW,KACX,iBAAkB,EACnB,EACA,CACC,GAAI,UACJ,KAAM,UACN,SAAU,MACV,cAAe,MACf,UAAW,KACX,iBAAkB,EACnB,EACA,CACC,GAAI,gBACJ,KAAM,uBACN,SAAU,MACV,cAAe,MACf,UAAW,KACX,iBAAkB,EACnB,EACA,CACC,GAAI,gBACJ,KAAM,uBACN,SAAU,MACV,cAAe,MACf,UAAW,KACX,iBAAkB,EACnB,EAEA,CACC,GAAI,mBACJ,KAAM,mBACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,yBACJ,KAAM,yBACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,qCACJ,KAAM,gCACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,wBACJ,KAAM,wBACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,gCACJ,KAAM,gCACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,yBACJ,KAAM,yBACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,iBACJ,KAAM,iBACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,mBACJ,KAAM,mBACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,wBACJ,KAAM,wBACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,0CACJ,KAAM,0BACN,SAAU,SACV,cAAe,IACf,UAAW,MACX,iBAAkB,EACnB,EAEA,CACC,GAAI,gBACJ,KAAM,cACN,SAAU,WACV,cAAe,KACf,UAAW,KACX,iBAAkB,EACnB,EACA,CACC,GAAI,oBACJ,KAAM,cACN,SAAU,WACV,cAAe,KACf,UAAW,KACX,iBAAkB,EACnB,EAEA,CACC,GAAI,SACJ,KAAM,SACN,SAAU,SACV,cAAe,MACf,UAAW,MACX,iBAAkB,EACnB,EACA,CACC,GAAI,UACJ,KAAM,UACN,SAAU,SACV,cAAe,IACf,UAAW,IACX,iBAAkB,EACnB,CACD,EAEA,SAAgB,EAAY,EAAqC,CAChE,OAAO,EAAU,KAAM,GAAM,EAAE,KAAO,CAAE,CACzC,CAEA,SAAgB,GAAqB,EAA6B,CACjE,OAAO,EAAO,OAAQ,GAAM,EAAE,WAAa,CAAU,CACtD,CC1MA,MAAM,MAAiB,EAAK,QAAQ,IAAI,MAAQ,IAAK,WAAW,EAC1D,MAAoB,EAAK,EAAS,EAAG,aAAa,EAClD,MAAkB,EAAK,EAAS,EAAG,WAAW,EAE9C,EAA4B,CACjC,SAAU,GACV,MAAO,EACR,EAEM,EAAwB,CAC7B,QAAS,CAAC,CACX,EAEA,eAAsB,IAAiC,CACtD,GAAI,CAEH,OADA,MAAM,EAAK,EAAY,CAAC,EACjB,EACR,MAAQ,CACP,MAAO,EACR,CACD,CAEA,eAAsB,GAAkC,CACvD,GAAI,CACH,IAAM,EAAM,KAAK,MAAM,MAAM,EAAS,EAAY,EAAG,OAAO,CAAC,EAC7D,MAAO,CAAE,GAAG,EAAe,GAAG,CAAI,CACnC,MAAQ,CACP,MAAO,CAAE,GAAG,CAAc,CAC3B,CACD,CAEA,eAAsB,GAA8B,CACnD,GAAI,CACH,IAAM,EAAM,KAAK,MAAM,MAAM,EAAS,EAAU,EAAG,OAAO,CAAC,EAC3D,MAAO,CAAE,GAAG,EAAa,GAAG,CAAI,CACjC,MAAQ,CACP,MAAO,CAAE,GAAG,CAAY,CACzB,CACD,CAEA,eAAe,GAA2B,CACzC,MAAM,EAAM,EAAS,EAAG,CAAE,UAAW,EAAK,CAAC,CAC5C,CAEA,eAAsB,EAAW,EAAmC,CACnE,MAAM,EAAU,EAChB,MAAM,EAAU,EAAY,EAAG,KAAK,UAAU,EAAQ,KAAM,CAAC,CAAC,CAC/D,CAEA,eAAsB,EAAS,EAA+B,CAC7D,MAAM,EAAU,EAChB,MAAM,EAAU,EAAU,EAAG,KAAK,UAAU,EAAM,KAAM,CAAC,CAAC,EAC1D,GAAI,CACH,MAAM,EAAM,EAAU,EAAG,GAAK,CAC/B,MAAQ,CAER,CACD,CCpDA,SAAgB,EAAa,CAC5B,UACA,UACA,SACA,YAME,CACF,GAAM,CAAC,EAAK,GAAU,EAAS,CAAC,EAoBhC,OAlBA,GAAU,EAAG,IAAQ,CACpB,GAAI,EAAI,OAAQ,CACf,EAAS,IAAI,EACb,MACD,CACA,GAAI,EAAI,QAAS,CAChB,EAAQ,IAAO,EAAI,EAAI,EAAQ,QAAU,EAAQ,MAAM,EACvD,MACD,CACA,GAAI,EAAI,UAAW,CAClB,EAAQ,IAAO,EAAI,GAAK,EAAQ,MAAM,EACtC,MACD,CACI,EAAI,QACP,EAAS,EAAQ,IAAM,OAAS,IAAI,CAEtC,CAAC,EAGA,EAAC,EAAD,CAAK,cAAc,SAAS,SAAU,WAAtC,CACE,GACA,EAAC,EAAD,CAAK,aAAc,WAClB,EAAC,EAAD,CAAA,SAAO,CAAa,CAAA,CAChB,CAAA,EAEN,EAAC,EAAD,CAAK,aAAc,WAClB,EAAC,EAAD,CAAM,KAAA,YAAM,CAAc,CAAA,CACtB,CAAA,EACJ,EAAQ,KAAK,EAAK,IAClB,EAAC,EAAD,CAAA,SAAA,CACC,EAAC,EAAD,CAAM,MAAO,IAAM,EAAM,QAAU,IAAA,YAAnC,CACE,IAAM,EAAM,KAAO,KACnB,EAAI,KACA,IACL,EAAI,MAAQ,IAAM,GAAO,EAAC,EAAD,CAAM,SAAA,YAAN,CAAe,IAAE,EAAI,IAAW,GACtD,CAAA,EANK,EAAI,KAMT,CACL,EACD,EAAC,EAAD,CAAK,UAAW,WACf,EAAC,EAAD,CAAM,SAAA,YAAS,yCAA6C,CAAA,CACxD,CAAA,CACD,GAEP,CAEA,SAAgB,EAAe,CAC9B,UACA,WACA,YAKE,CACF,GAAM,CAAC,EAAO,GAAY,EAAS,EAAE,EAC/B,CAAC,EAAO,GAAY,EAAS,EAAE,EA2BrC,OAzBA,GAAU,EAAI,IAAQ,CACrB,GAAI,EAAI,OAAQ,CACf,EAAS,IAAI,EACb,MACD,CACA,GAAI,EAAI,OAAQ,CACf,IAAM,EAAM,IAAW,CAAK,EAC5B,GAAI,EAAK,CACR,EAAS,CAAG,EACZ,MACD,CACA,EAAS,CAAK,EACd,MACD,CACA,GAAI,EAAI,WAAa,EAAI,OAAQ,CAChC,EAAU,GAAM,EAAE,MAAM,EAAG,EAAE,CAAC,EAC9B,EAAS,EAAE,EACX,MACD,CACI,IACH,EAAU,GAAM,EAAI,CAAE,EACtB,EAAS,EAAE,EAEb,CAAC,EAGA,EAAC,EAAD,CAAK,cAAc,SAAS,SAAU,WAAtC,CACC,EAAC,EAAD,CAAK,aAAc,WAClB,EAAC,EAAD,CAAM,KAAA,YAAM,CAAc,CAAA,CACtB,CAAA,EACL,EAAC,EAAD,CAAA,SAAA,CACC,EAAC,EAAD,CAAM,MAAM,iBAAQ,IAAQ,CAAA,EAC5B,EAAC,EAAD,CAAM,SAAA,YAAU,IAAI,OAAO,EAAM,MAAM,CAAQ,CAAA,EAC/C,EAAC,EAAD,CAAM,MAAM,iBAAQ,GAAO,CAAA,CACvB,CAAA,CAAA,EACJ,GACA,EAAC,EAAD,CAAA,SACC,EAAC,EAAD,CAAM,MAAM,eAAO,CAAY,CAAA,CAC3B,CAAA,EAEN,EAAC,EAAD,CAAK,UAAW,WACf,EAAC,EAAD,CAAM,SAAA,YAAS,2BAA+B,CAAA,CAC1C,CAAA,CACD,GAEP,CAEA,SAAgB,GAAc,CAC7B,UACA,aAIE,CACF,GAAM,CAAC,EAAK,GAAU,EAAS,EAAI,EAgBnC,OAdA,GAAU,EAAG,IAAQ,CACpB,GAAI,EAAI,OAAQ,CACf,EAAU,IAAI,EACd,MACD,CACA,GAAI,EAAI,WAAa,EAAI,YAAc,EAAI,IAAK,CAC/C,EAAQ,GAAM,CAAC,CAAC,EAChB,MACD,CACI,EAAI,QACP,EAAU,CAAG,CAEf,CAAC,EAGA,EAAC,EAAD,CAAK,cAAc,SAAS,SAAU,WAAtC,CACC,EAAC,EAAD,CAAK,aAAc,WAClB,EAAC,EAAD,CAAM,KAAA,YAAM,CAAc,CAAA,CACtB,CAAA,EACL,EAAC,EAAD,CAAA,SACC,EAAC,EAAD,CAAM,MAAO,EAAM,QAAU,IAAA,YAA7B,CAAyC,EAAM,KAAO,KAAK,KAAS,GAChE,CAAA,EACL,EAAC,EAAD,CAAA,SACC,EAAC,EAAD,CAAM,MAAQ,EAAc,IAAA,GAAR,eAApB,CAAyC,EAAa,KAAP,KAAY,IAAQ,GAC/D,CAAA,EACL,EAAC,EAAD,CAAK,UAAW,WACf,EAAC,EAAD,CAAM,SAAA,YAAS,wCAA4C,CAAA,CACvD,CAAA,CACD,GAEP,CAIA,SAAgB,GACf,EACA,EACA,EACyB,CACzB,OAAO,IAAI,QAAS,GAAY,CAC/B,GAAM,CAAE,WAAY,EACnB,EAAC,EAAD,CACU,UACA,UACD,SACR,SAAW,GAAM,CAChB,EAAQ,EACR,EAAQ,CAAC,CACV,CACA,CAAA,CACF,CACD,CAAC,CACF,CAEA,SAAgB,GACf,EACA,EACyB,CACzB,OAAO,IAAI,QAAS,GAAY,CAC/B,GAAM,CAAE,WAAY,EACnB,EAAC,EAAD,CACU,UACC,WACV,SAAW,GAAM,CAChB,EAAQ,EACR,EAAQ,CAAC,CACV,CACA,CAAA,CACF,CACD,CAAC,CACF,CCtMA,eAAsB,IAAqC,CAC1D,QAAQ,IAAI,EAAM,KAAK,KAAK;;CAAoC,CAAC,EAEjE,IAAM,EAAa,MAAM,GACxB,kBACA,EAAU,IAAK,IAAO,CAAE,MAAO,EAAE,GAAI,MAAO,EAAE,IAAK,EAAE,CACtD,EACK,IACJ,QAAQ,IAAI,EAAM,IAAI,WAAW,CAAC,EAClC,QAAQ,KAAK,CAAC,GAGf,IAAM,EAAW,EAAY,CAAU,EAClC,IACJ,QAAQ,IAAI,EAAM,IAAI,kBAAkB,CAAC,EACzC,QAAQ,KAAK,CAAC,GAGf,IAAM,EAAS,MAAM,GAAmB,SAAS,EAAS,KAAK,SAAS,EACnE,IACJ,QAAQ,IAAI,EAAM,IAAI,WAAW,CAAC,EAClC,QAAQ,KAAK,CAAC,GAIf,IAAM,EAAU,MAAM,GACrB,uBAFc,GAAqB,CAG9B,EAAE,IAAK,IAAO,CAClB,MAAO,EAAE,GACT,MAAO,GAAG,EAAE,KAAK,KAAK,EAAE,cAAgB,KAAM,QAAQ,CAAC,EAAE,OAC1D,EAAE,CACH,EACK,IACJ,QAAQ,IAAI,EAAM,IAAI,WAAW,CAAC,EAClC,QAAQ,KAAK,CAAC,GAGf,IAAM,EAAqB,CAC1B,SAAU,EACV,MAAO,CACR,EAMA,OAJA,MAAM,EAAW,CAAM,EACvB,MAAM,EAAS,CAAE,QAAS,EAAG,GAAa,CAAO,CAAE,CAAC,EAEpD,QAAQ,IAAI,EAAM,MAAM;;CAAqD,CAAC,EACvE,CACR,CC3CA,MAAM,GAAS,IAAI,IAAI,CAAC,OAAQ,QAAS,OAAQ,OAAQ,OAAO,CAAC,EAEjE,SAAS,EAAS,EAAa,EAAmB,CACjD,IAAM,EAAM,EAAQ,EAAK,CAAC,EAC1B,GAAI,IAAQ,GAAO,CAAC,EAAI,WAAW,GAAG,EAAI,EAAE,EAC3C,MAAU,MAAM,yBAAyB,GAAG,EAE7C,OAAO,CACR,CAEA,SAAgB,GAAS,EAAmB,CAC3C,MAAO,CACN,IAAK,CACJ,KAAM,OACN,YACC,8GACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,KAAM,CAAE,KAAM,SAAU,YAAa,qCAAsC,EAC3E,OAAQ,CAAE,KAAM,SAAU,YAAa,iCAAkC,EACzE,MAAO,CAAE,KAAM,SAAU,YAAa,kCAAmC,CAC1E,EACA,SAAU,CAAC,MAAM,CAClB,CACD,EACA,MAAM,QAAQ,EAA2B,CACxC,GAAI,CACH,IAAM,EAAW,EAAS,EAAK,EAAK,IAAc,EAE5C,EAAM,EAAQ,CAAQ,EAAE,YAAY,EAC1C,GAAI,GAAO,IAAI,CAAG,EAIjB,MAAO,CAAE,QAAS,CAAC,CAAE,KAAM,QAAS,MAFxB,MADM,EAAS,CAAQ,GACnB,SAAS,QAEmB,EAAG,KADlC,IAAQ,OAAS,aAAe,SAAS,EAAI,MAAM,CAAC,GACb,CAAC,EAAG,QAAS,EAAM,EAIxE,IAAM,GAAQ,MADQ,EAAS,EAAU,OAAO,GAC1B,MAAM;CAAI,EAC1B,EAAS,KAAK,IAAI,GAAI,OAAO,EAAK,QAAU,CAAC,GAAK,GAAK,CAAC,EACxD,EAAQ,OAAO,EAAK,OAAS,GAAI,GAAK,IACtC,EAAQ,EAAM,MAAM,EAAQ,EAAS,CAAK,EAC1C,EAAY,EAAS,EAAQ,EAAM,OAKzC,MAAO,CAAE,QAAS,CAAC,EAHP,EAAM,KAAK;CAGO,GAFf,EAAY,MAAM,EAAM,OAAS,EAAS,EAAM,aAAe,GAEtC,CAAC,EAAG,QAAS,EAAM,CAC5D,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,uBAAwB,EAAY,SAAS,CAAC,EACjE,QAAS,EACV,CACD,CACD,CACD,CACD,CAEA,SAAgB,GAAU,EAAmB,CAC5C,MAAO,CACN,IAAK,CACJ,KAAM,QACN,YAAa,8EACb,WAAY,CACX,KAAM,SACN,WAAY,CACX,KAAM,CAAE,KAAM,SAAU,YAAa,cAAe,EACpD,QAAS,CAAE,KAAM,SAAU,YAAa,kBAAmB,CAC5D,EACA,SAAU,CAAC,OAAQ,SAAS,CAC7B,CACD,EACA,MAAM,QAAQ,EAA2B,CACxC,GAAI,CACH,IAAM,EAAW,EAAS,EAAK,EAAK,IAAc,EAC5C,EAAU,EAAK,QACrB,MAAM,EAAM,EAAQ,CAAQ,EAAG,CAAE,UAAW,EAAK,CAAC,EAClD,MAAM,EAAU,EAAU,CAAO,EACjC,IAAM,EAAU,EAAoB,EAAK,CAAQ,EACjD,MAAO,CACN,QAAS,CAAC,EAAS,SAAS,EAAQ,OAAO,WAAW,GAAS,CAAC,EAChE,QAAS,EACV,CACD,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,uBAAwB,EAAY,SAAS,CAAC,EACjE,QAAS,EACV,CACD,CACD,CACD,CACD,CAGA,SAAgB,GAAS,EAAmB,CAC3C,MAAO,CACN,IAAK,CACJ,KAAM,OACN,YACC,4FACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,KAAM,CAAE,KAAM,SAAU,YAAa,cAAe,EACpD,MAAO,CACN,KAAM,QACN,YACC,qFACD,MAAO,CACN,KAAM,SACN,WAAY,CACX,QAAS,CAAE,KAAM,SAAU,YAAa,qCAAsC,EAC9E,QAAS,CAAE,KAAM,SAAU,YAAa,kBAAmB,CAC5D,EACA,SAAU,CAAC,UAAW,SAAS,CAChC,CACD,CACD,EACA,SAAU,CAAC,OAAQ,OAAO,CAC3B,CACD,EACA,MAAM,QAAQ,EAA2B,CACxC,GAAI,CACH,IAAM,EAAW,EAAS,EAAK,EAAK,IAAc,EAC9C,EACJ,GAAI,CACH,EAAU,MAAM,EAAS,EAAU,OAAO,CAC3C,MAAQ,CACP,MAAO,CAAE,QAAS,CAAC,EAAS,mBAAmB,EAAK,MAAM,CAAC,EAAG,QAAS,EAAK,CAC7E,CACA,IAAM,EAAQ,EAAK,MAGnB,IAAK,IAAM,KAAQ,EAAO,CACzB,IAAM,EAAQ,EAAQ,MAAM,EAAK,OAAO,EAAE,OAAS,EACnD,GAAI,IAAU,EACb,MAAO,CACN,QAAS,CAAC,EAAS,uBAAuB,EAAK,QAAQ,MAAM,EAAG,EAAE,EAAE,GAAG,CAAC,EACxE,QAAS,EACV,EAGD,GAAI,EAAQ,EACX,MAAO,CACN,QAAS,CACR,EACC,iBAAiB,EAAM,uDAAuD,EAAK,QAAQ,MAAM,EAAG,EAAE,EAAE,GACzG,CACD,EACA,QAAS,EACV,CAEF,CAGA,IAAK,IAAM,KAAQ,EAClB,EAAU,EAAQ,QAAQ,EAAK,QAAS,EAAK,OAAO,EAKrD,OAFA,MAAM,EAAU,EAAU,CAAO,EAE1B,CACN,QAAS,CACR,EACC,UAJa,EAAoB,EAAK,CAItB,EAAE,IAAI,EAAM,OAAO,cAAc,EAAM,OAAS,EAAI,IAAM,GAAG,EAC9E,CACD,EACA,QAAS,EACV,CACD,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,uBAAwB,EAAY,SAAS,CAAC,EACjE,QAAS,EACV,CACD,CACD,CACD,CACD,CCrLA,SAAgB,GAAQ,EAAmB,CAC1C,MAAO,CACN,IAAK,CACJ,KAAM,MACN,YACC,iGACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,OAAQ,CACP,KAAM,SACN,KAAM,CAAC,SAAU,OAAQ,MAAO,MAAO,QAAQ,EAC/C,YAAa,2BACd,EACA,KAAM,CACL,KAAM,QACN,YAAa,iEACb,MAAO,CAAE,KAAM,QAAS,CACzB,CACD,EACA,SAAU,CAAC,QAAQ,CACpB,CACD,EACA,MAAM,QAAQ,EAAM,EAA6B,CAChD,IAAM,EAAS,EAAK,OACd,EAAa,EAAK,MAAqB,CAAC,EAG9C,GAAI,CAAC,IADe,IAAI,CAAC,SAAU,OAAQ,MAAO,MAAO,QAAQ,CACtD,EAAE,IAAI,CAAM,EACtB,MAAO,CACN,QAAS,CAAC,EAAS,sBAAsB,EAAO,oBAAoB,CAAC,EACrE,QAAS,EACV,EAGD,GAAI,CACH,IAAM,EAAM,CAAC,MAAO,EAAQ,GAAG,CAAS,EAClC,EAAO,EAAM,EAAI,GAAK,EAAI,MAAM,CAAC,EAAG,CACzC,MACA,MAAO,CAAC,SAAU,OAAQ,MAAM,EAChC,IAAK,CAAE,GAAG,QAAQ,IAAK,MAAO,KAAM,CACrC,CAAC,EAEG,EAAS,GACT,EAAS,GACb,EAAK,OAAO,GAAG,OAAS,GAAkB,CACzC,GAAU,EAAM,SAAS,CAC1B,CAAC,EACD,EAAK,OAAO,GAAG,OAAS,GAAkB,CACzC,GAAU,EAAM,SAAS,CAC1B,CAAC,EAED,IAAM,MAAgB,CACrB,EAAK,KAAK,SAAS,EACnB,EAAK,OAAO,QAAQ,EACpB,EAAK,OAAO,QAAQ,CACrB,EACA,GAAQ,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,EAEzD,IAAI,EACJ,GAAI,CACH,EAAW,MAAM,IAAI,SAAiB,EAAS,IAAW,CACzD,EAAK,GAAG,QAAS,CAAM,EACvB,EAAK,GAAG,QAAU,GAAS,EAAQ,GAAQ,EAAE,CAAC,CAC/C,CAAC,CACF,QAAU,CACT,GAAQ,oBAAoB,QAAS,CAAO,CAC7C,CAGA,IAAM,EAAM,IACR,EAAM,GAQV,OAPI,IAAQ,GAAO,EAAO,MAAM,EAAG,CAAG,GAClC,IACC,IAAK,GAAO;GAChB,GAAO,EAAO,MAAM,EAAG,EAAM,EAAI,MAAM,GAEpC,EAAI,QAAU,IAAK,GAAO;aAEvB,CACN,QAAS,CAAC,EAAS,GAAO,aAAa,CAAC,EACxC,QAAS,IAAa,CACvB,CACD,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,sBAAuB,EAAY,SAAS,CAAC,EAChE,QAAS,EACV,CACD,CACD,CACD,CACD,CClFA,SAAgB,GAAS,EAAmB,CAC3C,MAAO,CACN,IAAK,CACJ,KAAM,OACN,YAAa,+DACb,WAAY,CACX,KAAM,SACN,WAAY,CACX,QAAS,CAAE,KAAM,SAAU,YAAa,6BAA8B,EACtE,KAAM,CAAE,KAAM,SAAU,YAAa,oCAAqC,EAC1E,OAAQ,CAAE,KAAM,UAAW,YAAa,yCAA0C,CACnF,EACA,SAAU,CAAC,SAAS,CACrB,CACD,EACA,MAAM,QAAQ,EAA2B,CACxC,GAAI,CACH,IAAM,EAAW,EAAK,MAAmB,IACnC,EAAM,EAAQ,EAAK,CAAO,EAChC,GAAI,IAAQ,GAAO,CAAC,EAAI,WAAW,GAAG,EAAI,EAAE,EAC3C,MAAU,MAAM,yBAAyB,GAAS,EAGnD,IAAM,EAAU,EAAK,QAGf,GAAS,MADK,EAAK,EAAS,CAAE,IAAK,EAAK,OAAA,CAD9B,CAAC,EAAK,MAC+B,CAAC,GACjC,MAAM,EAAG,GAAG,EAC3B,EAAgB,EAAS,EAAK,CAAG,EACjC,EAAS,EAAgB,GAAG,EAAc,GAAK,GAC/C,EAAW,EAAO,IAAK,GAAM,EAAS,CAAC,EAE7C,MAAO,CAAE,QAAS,CAAC,EADP,EAAS,OAAS,EAAI,EAAS,KAAK;CAAI,EAAI,gBACzB,CAAC,EAAG,QAAS,EAAM,CACnD,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,UAAW,EAAY,SAAS,CAAC,EACpD,QAAS,EACV,CACD,CACD,CACD,CACD,CAKA,SAAgB,GAAS,EAAmB,CAC3C,MAAO,CACN,IAAK,CACJ,KAAM,OACN,YACC,sGACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,QAAS,CAAE,KAAM,SAAU,YAAa,6BAA8B,EACtE,KAAM,CAAE,KAAM,SAAU,YAAa,4CAA6C,EAClF,KAAM,CAAE,KAAM,SAAU,YAAa,8BAA+B,CACrE,EACA,SAAU,CAAC,SAAS,CACrB,CACD,EACA,MAAM,QAAQ,EAAM,EAA6B,CAChD,GAAI,CACH,IAAM,EAAW,EAAK,MAAmB,IACnC,EAAM,EAAQ,EAAK,CAAO,EAChC,GAAI,IAAQ,GAAO,CAAC,EAAI,WAAW,GAAG,EAAI,EAAE,EAC3C,MAAU,MAAM,yBAAyB,GAAS,EAGnD,IAAM,EAAU,EAAK,QACf,EAAa,EAAK,KAClB,EAAgB,EAAS,EAAK,CAAG,GAAK,IAG5C,GAAI,CACH,IAAM,EAAM,CAAC,KAAM,gBAAiB,cAAe,KAAK,EACpD,GAAY,EAAI,KAAK,UAAU,GAAY,EAC/C,EAAI,KAAK,KAAM,EAAS,CAAa,EAErC,IAAM,EAAO,EAAM,EAAI,GAAK,EAAI,MAAM,CAAC,EAAG,CACzC,MACA,MAAO,CAAC,SAAU,OAAQ,MAAM,CACjC,CAAC,EAEK,MAAgB,CACrB,EAAK,KAAK,EACV,EAAK,OAAO,QAAQ,EACpB,EAAK,OAAO,QAAQ,CACrB,EACA,GAAQ,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,EAEzD,IAAI,EAAS,GACb,EAAK,OAAO,GAAG,OAAS,GAAkB,CACzC,GAAU,EAAM,SAAS,CAC1B,CAAC,EAED,IAAI,EACJ,GAAI,CACH,EAAW,MAAM,IAAI,SAAiB,EAAS,IAAW,CACzD,EAAK,GAAG,QAAS,CAAM,EACvB,EAAK,GAAG,QAAU,GAAS,EAAQ,GAAQ,EAAE,CAAC,CAC/C,CAAC,CACF,QAAU,CACT,GAAQ,oBAAoB,QAAS,CAAO,CAC7C,CAEA,GAAI,IAAa,EAEhB,MAAO,CAAE,QAAS,CAAC,EADL,EAAO,MAAM;CAAI,EAAE,MAAM,EAAG,GAAG,EAAE,KAAK;CACpB,GAAK,YAAY,CAAC,EAAG,QAAS,EAAM,CAEtE,MAAQ,CAER,CAGA,IAAM,EAAQ,MAAM,EAAK,GAAc,OAAQ,CAC9C,IAAK,EACL,OAAQ,CAAC,qBAAsB,YAAY,CAC5C,CAAC,EACK,EAAS,IAAkB,IAAM,GAAK,GAAG,EAAc,GACvD,EAAK,IAAI,OAAO,EAAS,GAAG,EAC5B,EAAoB,CAAC,EAC3B,IAAK,IAAM,KAAQ,EAAM,MAAM,EAAG,GAAG,EAAG,CACvC,GAAI,GAAQ,QAAS,MACrB,GAAI,CAEH,IAAM,GAAQ,MADQ,EAAS,EAAQ,EAAK,CAAI,EAAG,OAAO,GACpC,MAAM;CAAI,EAChC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,QAAU,EAAQ,OAAS,IAAK,IAAK,CAC9D,IAAM,EAAO,EAAM,GACf,GAAQ,EAAG,KAAK,CAAI,GAAG,EAAQ,KAAK,GAAG,IAAS,EAAK,GAAG,EAAI,EAAE,GAAG,GAAM,CAC5E,CACD,MAAQ,CAER,CACD,CACA,MAAO,CACN,QAAS,CAAC,EAAS,EAAQ,KAAK;CAAI,GAAK,YAAY,CAAC,EACtD,QAAS,EACV,CACD,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,UAAW,EAAY,SAAS,CAAC,EACpD,QAAS,EACV,CACD,CACD,CACD,CACD,CAKA,SAAgB,GAAO,EAAmB,CACzC,MAAO,CACN,IAAK,CACJ,KAAM,KACN,YAAa,2BACb,WAAY,CACX,KAAM,SACN,WAAY,CACX,KAAM,CAAE,KAAM,SAAU,YAAa,+BAAgC,CACtE,EACA,SAAU,CAAC,CACZ,CACD,EACA,MAAM,QAAQ,EAA2B,CACxC,GAAI,CAOH,MAAO,CAAE,QAAS,CAAC,GAJL,MADQ,EADV,EAAQ,EAAM,EAAK,MAAmB,GAClB,EAAG,CAAE,cAAe,EAAK,CAAC,GACpC,IAAK,GAAM,CAChC,IAAM,EAAS,EAAE,YAAY,EAAI,IAAM,EAAE,eAAe,EAAI,IAAM,GAClE,MAAO,GAAG,EAAE,OAAO,GACpB,CACgC,EAAE,KAAK;CAAI,GAAK,SAAS,CAAC,EAAG,QAAS,EAAM,CAC7E,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,UAAW,EAAY,SAAS,CAAC,EACpD,QAAS,EACV,CACD,CACD,CACD,CACD,CAKA,SAAgB,GAAS,EAAmB,CAC3C,MAAO,CACN,IAAK,CACJ,KAAM,OACN,YACC,uGACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,KAAM,CAAE,KAAM,SAAU,YAAa,0CAA2C,EAChF,MAAO,CAAE,KAAM,SAAU,YAAa,uCAAwC,CAC/E,EACA,SAAU,CAAC,CACZ,CACD,EACA,MAAM,QAAQ,EAA2B,CACxC,GAAI,CACH,IAAM,EAAW,EAAQ,EAAM,EAAK,MAAmB,GAAG,EACpD,EAAW,OAAO,EAAK,OAAS,CAAC,GAAK,EAEtC,EAAa,IAAI,IAAI,CAC1B,OACA,eACA,OACA,QACA,cACA,QACA,MACA,eACA,UACD,CAAC,EAED,eAAe,EAAK,EAAa,EAAsB,EAAiC,CACvF,GAAI,EAAe,EAAU,MAAO,GACpC,IAAI,EAAS,GAGP,GAAS,MADO,EAAQ,EAAK,CAAE,cAAe,EAAK,CAAC,GAExD,OAAQ,GAAM,CAAC,EAAW,IAAI,EAAE,IAAI,CAAC,EACrC,MAAM,EAAG,IACL,EAAE,YAAY,GAAK,CAAC,EAAE,YAAY,EAAU,GAC5C,CAAC,EAAE,YAAY,GAAK,EAAE,YAAY,EAAU,EACzC,EAAE,KAAK,cAAc,EAAE,IAAI,CAClC,EAEF,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,IAAK,CACvC,IAAM,EAAQ,EAAO,GACf,EAAS,IAAM,EAAO,OAAS,EAC/B,EAAY,EAAS,OAAS,OAC9B,EAAc,GAAU,EAAS,OAAS,QAEhD,GAAU,GAAG,IAAS,IAAY,EAAM,OAAO,EAAM,YAAY,EAAI,IAAM,GAAG,IAE1E,EAAM,YAAY,IACrB,GAAU,MAAM,EAAK,EAAQ,EAAK,EAAM,IAAI,EAAG,EAAe,EAAG,CAAW,EAE9E,CACA,OAAO,CACR,CAGA,MAAO,CAAE,QAAS,CAAC,EAAS,MADL,EAAK,EAAU,EAAG,EAAE,GACH,SAAS,CAAC,EAAG,QAAS,EAAM,CACrE,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,UAAW,EAAY,SAAS,CAAC,EACpD,QAAS,EACV,CACD,CACD,CACD,CACD,CCxQA,SAAgB,GAAS,EAAmB,CAC3C,MAAO,CACN,IAAK,CACJ,KAAM,OACN,YACC,6FACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,QAAS,CAAE,KAAM,SAAU,YAAa,sBAAuB,EAC/D,QAAS,CAAE,KAAM,SAAU,YAAa,kCAAmC,CAC5E,EACA,SAAU,CAAC,SAAS,CACrB,CACD,EACA,MAAM,QAAQ,EAAM,EAA6B,CAChD,IAAM,EAAU,EAAK,QACf,GAAa,OAAO,EAAK,OAAO,GAAK,KAAO,IAElD,GAAI,CACH,IAAM,EAAO,EAAM,KAAM,CAAC,KAAM,CAAO,EAAG,CAAE,MAAK,MAAO,CAAC,SAAU,OAAQ,MAAM,CAAE,CAAC,EAEhF,EAAS,GACT,EAAS,GACb,EAAK,OAAO,GAAG,OAAS,GAAkB,CACzC,GAAU,EAAM,SAAS,CAC1B,CAAC,EACD,EAAK,OAAO,GAAG,OAAS,GAAkB,CACzC,GAAU,EAAM,SAAS,CAC1B,CAAC,EAED,IAAI,EAAS,GACP,EAAQ,eAAiB,CAC9B,EAAS,GACT,EAAK,KAAK,SAAS,EACnB,EAAK,OAAO,QAAQ,EACpB,EAAK,OAAO,QAAQ,CACrB,EAAG,CAAS,EAEN,MAAgB,CACrB,EAAS,GACT,EAAK,KAAK,SAAS,EACnB,EAAK,OAAO,QAAQ,EACpB,EAAK,OAAO,QAAQ,CACrB,EACA,GAAQ,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,EAEzD,IAAI,EACJ,GAAI,CACH,EAAW,MAAM,IAAI,SAAiB,EAAS,IAAW,CACzD,EAAK,GAAG,QAAS,CAAM,EACvB,EAAK,GAAG,QAAU,GAAS,EAAQ,GAAQ,EAAE,CAAC,CAC/C,CAAC,CACF,QAAU,CACT,aAAa,CAAK,EAClB,GAAQ,oBAAoB,QAAS,CAAO,CAC7C,CAGA,IAAM,EAAM,IACR,EAAM,GAWV,OAVI,IAAQ,GAAO,EAAO,MAAM,EAAG,CAAG,GAClC,IACC,IAAK,GAAO;GAChB,GAAO,EAAO,MAAM,EAAG,EAAM,EAAI,MAAM,GAEpC,EAAI,QAAU,IAAK,GAAO;aAE1B,IAAQ,GAAO,oBAAoB,EAAY,IAAK,KACxD,GAAO,WAAW,EAAS,GAEpB,CAAE,QAAS,CAAC,EAAS,CAAG,CAAC,EAAG,QAAS,IAAa,GAAK,CAAO,CACtE,OAAS,EAAG,CACX,MAAO,CACN,QAAS,CAAC,EAAS,UAAW,EAAY,SAAS,CAAC,EACpD,QAAS,EACV,CACD,CACD,CACD,CACD,CCjFA,MAAM,EAAc,IAGpB,SAAS,EAAW,EAAsB,CACzC,IAAI,EAAO,EAET,QAAQ,mBAAoB,EAAE,EAE9B,QAAQ,8BAA+B,EAAE,EACzC,QAAQ,4BAA6B,EAAE,EAEvC,QAAQ,2DAA4D,UAAU,EAE9E,QAAQ,wDAAyD;CAAI,EAErE,QAAQ,WAAY,EAAE,EAEtB,QAAQ,SAAU,GAAG,EACrB,QAAQ,QAAS,GAAG,EACpB,QAAQ,QAAS,GAAG,EACpB,QAAQ,UAAW,GAAG,EACtB,QAAQ,SAAU,GAAG,EACrB,QAAQ,UAAW,GAAG,EACtB,QAAQ,SAAU,GAAG,EACrB,QAAQ,UAAW,GAAG,EACtB,QAAQ,UAAW,GAAG,EACtB,QAAQ,WAAY,GAAG,EACvB,QAAQ,WAAY,GAAG,EACvB,QAAQ,WAAY,GAAG,EACvB,QAAQ,WAAY,GAAG,EACvB,QAAQ,UAAW,GAAG,EAEtB,QAAQ,UAAW,GAAG,EACtB,QAAQ,UAAW;;CAAM,EACzB,KAAK,EAKP,OAHI,EAAK,OAAS,IACjB,EAAO,GAAG,EAAK,MAAM,EAAG,CAAW,EAAE,eAE/B,CACR,CAEA,SAAgB,IAAsB,CACrC,MAAO,CACN,IAAK,CACJ,KAAM,aACN,YACC,mJACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,MAAO,CAAE,KAAM,SAAU,YAAa,cAAe,CACtD,EACA,SAAU,CAAC,OAAO,CACnB,CACD,EACA,MAAM,QAAQ,EAAM,EAA6B,CAChD,IAAM,EAAQ,EAAK,MACnB,GAAI,CAAC,EAAM,KAAK,EACf,MAAO,CAAE,QAAS,CAAC,EAAS,2BAA2B,CAAC,EAAG,QAAS,EAAK,EAG1E,GAAI,CACH,IAAM,EAAM,uCAAuC,mBAAmB,CAAK,IACrE,EAAO,MAAM,MAAM,EAAK,CAC7B,OAAQ,GAAU,IAAA,GAClB,QAAS,CACR,aACC,uHACF,CACD,CAAC,EAED,GAAI,CAAC,EAAK,GACT,MAAO,CACN,QAAS,CAAC,EAAS,uBAAuB,EAAK,QAAQ,CAAC,EACxD,QAAS,EACV,EAGD,IAAM,EAAO,MAAM,EAAK,KAAK,EAGvB,EAAmB,CAAC,EACpB,EAAiB,kDACjB,EAAoB,CAAC,EAC3B,IAAK,IAAM,KAAS,EAAK,SAAS,CAAc,EAC3C,EAAM,QAAU,IAAA,IACnB,EAAQ,KAAK,EAAM,KAAK,EAI1B,GAAI,EAAQ,OAAS,EACpB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAAK,CACxC,IAAM,EAAQ,EAAQ,GAChB,EAAM,EAAQ,EAAI,IAAM,EAAK,OACnC,EAAO,KAAK,EAAK,MAAM,EAAO,CAAG,CAAC,CACnC,CAGD,IAAM,EAAoB,CAAC,EACrB,EAAa,oEACb,EAAe,uDAErB,IAAK,IAAM,KAAS,EAAQ,CAC3B,GAAI,EAAQ,QAAU,GAAI,MAE1B,IAAM,EAAa,EAAW,KAAK,CAAK,EACxC,GAAI,CAAC,EAAY,SAEjB,IAAM,EAAS,EAAW,GACpB,EAAQ,EAAW,EAAW,EAAG,EAEjC,EAAe,EAAa,KAAK,CAAK,EACtC,EAAU,EAAe,EAAW,EAAa,EAAG,EAAI,GAG1D,EAAW,EACf,GAAI,CAEH,IAAM,EAAa,EAAO,WAAW,IAAI,EACtC,SAAS,IACT,EAAO,WAAW,GAAG,EACpB,yBAAyB,IACzB,EACE,EAAQ,IAAI,IAAI,CAAU,EAAE,aAAa,IAAI,MAAM,EACrD,IAAO,EAAW,EACvB,MAAQ,CAER,CAEA,EAAQ,KAAK,MAAM,EAAM,IAAI,EAAS,IAAI,GAAS,CACpD,CAMA,OAJI,EAAQ,SAAW,EACf,CAAE,QAAS,CAAC,EAAS,mBAAmB,CAAC,EAAG,QAAS,EAAM,EAG5D,CACN,QAAS,CAAC,EAAS,EAAQ,KAAK;;CAAM,CAAC,CAAC,EACxC,QAAS,EACV,CACD,OAAS,EAAG,CACX,IAAM,EAAO,EAAY,QAIzB,OAHI,EAAI,SAAS,OAAO,EAChB,CAAE,QAAS,CAAC,EAAS,iBAAiB,CAAC,EAAG,QAAS,EAAK,EAEzD,CACN,QAAS,CAAC,EAAS,iBAAiB,GAAK,CAAC,EAC1C,QAAS,EACV,CACD,CACD,CACD,CACD,CAEA,SAAgB,IAAqB,CACpC,MAAO,CACN,IAAK,CACJ,KAAM,YACN,YACC,0JACD,WAAY,CACX,KAAM,SACN,WAAY,CACX,IAAK,CAAE,KAAM,SAAU,YAAa,cAAe,CACpD,EACA,SAAU,CAAC,KAAK,CACjB,CACD,EACA,MAAM,QAAQ,EAAM,EAA6B,CAChD,IAAM,EAAM,EAAK,IACjB,GAAI,CAAC,EAAI,KAAK,EACb,MAAO,CAAE,QAAS,CAAC,EAAS,kBAAkB,CAAC,EAAG,QAAS,EAAK,EAGjE,GAAI,CACH,IAAI,IAAI,CAAG,CACZ,MAAQ,CACP,MAAO,CAAE,QAAS,CAAC,EAAS,uBAAuB,GAAK,CAAC,EAAG,QAAS,EAAK,CAC3E,CAEA,GAAI,CACH,IAAM,EAAO,MAAM,MAAM,EAAK,CAC7B,OAAQ,GAAU,IAAA,GAClB,QAAS,CACR,aACC,wHACD,OAAQ,iEACT,EACA,SAAU,QACX,CAAC,EAED,GAAI,CAAC,EAAK,GACT,MAAO,CACN,QAAS,CAAC,EAAS,sBAAsB,EAAK,OAAO,GAAG,EAAK,YAAY,CAAC,EAC1E,QAAS,EACV,EAGD,IAAM,GAAe,EAAK,QAAQ,IAAI,cAAc,GAAK,IAAI,YAAY,EACnE,EAAO,MAAM,EAAK,KAAK,EAiB7B,OAbC,EAAY,SAAS,WAAW,GAChC,EAAY,SAAS,uBAAuB,GAC5C,EAAK,KAAK,EAAE,YAAY,EAAE,WAAW,gBAAgB,GACrD,EAAK,KAAK,EAAE,YAAY,EAAE,WAAW,OAAO,EAIrC,CAAE,QAAS,CAAC,EADN,EAAW,CACO,CAAC,CAAC,EAAG,QAAS,EAAM,EAM7C,CAAE,QAAS,CAAC,EADlB,EAAK,OAAS,EAAc,GAAG,EAAK,MAAM,EAAG,CAAW,EAAE,cAAgB,CACtC,CAAC,EAAG,QAAS,EAAM,CACzD,OAAS,EAAG,CACX,IAAM,EAAO,EAAY,QAIzB,OAHI,EAAI,SAAS,OAAO,EAChB,CAAE,QAAS,CAAC,EAAS,gBAAgB,CAAC,EAAG,QAAS,EAAK,EAExD,CACN,QAAS,CAAC,EAAS,gBAAgB,GAAK,CAAC,EACzC,QAAS,EACV,CACD,CACD,CACD,CACD,CCvOA,SAAgB,GAAY,EAAqB,CAChD,MAAO,CACN,GAAS,CAAG,EACZ,GAAU,CAAG,EACb,GAAS,CAAG,EACZ,GAAS,CAAG,EACZ,GAAS,CAAG,EACZ,GAAS,CAAG,EACZ,GAAO,CAAG,EACV,GAAS,CAAG,EACZ,GAAQ,CAAG,EACX,GAAc,EACd,GAAa,CACd,CACD,CCfA,MAAM,GAAY,EAAQ,GAAc,OAAO,KAAK,GAAG,CAAC,EAExD,IAAI,EAA8B,KAC9B,EAA+B,KAEnC,eAAsB,GAAqC,CAC1D,GAAI,EAAe,OAAO,EAC1B,GAAI,CACH,IAAM,EAAM,MAAM,EAAS,EAAK,GAAW,KAAM,cAAc,EAAG,OAAO,EAGzE,MADA,GADY,KAAK,MAAM,CACJ,EAAE,SAAsB,QACpC,CACR,MAAQ,CACP,MAAO,OACR,CACD,CAEA,eAAsB,IAA2C,CAChE,GAAI,EAAc,OAAO,EACzB,GAAI,CACH,IAAM,EAAO,EAAM,MAAO,CAAC,OAAQ,WAAY,SAAS,EAAG,CAC1D,MAAO,CAAC,SAAU,OAAQ,QAAQ,CACnC,CAAC,EACK,EAAO,MAAM,IAAI,SAAiB,EAAS,IAAW,CAC3D,IAAI,EAAM,GACV,EAAK,OAAO,GAAG,OAAS,GAAkB,CACzC,GAAO,EAAM,SAAS,CACvB,CAAC,EACD,EAAK,GAAG,QAAS,CAAM,EACvB,EAAK,GAAG,YAAe,EAAQ,EAAI,KAAK,CAAC,CAAC,CAC3C,CAAC,EACD,GAAI,EAEH,MADA,GAAe,EACR,CAET,MAAQ,CAAC,CACT,OAAO,IACR,CAEA,eAAsB,IAIZ,CACT,IAAM,EAAU,MAAM,EAAkB,EAClC,EAAS,MAAM,GAAiB,EAEtC,OADK,EACE,CACN,UAAW,GAAO,GAAG,EAAQ,CAAO,EACpC,UACA,QACD,EALoB,IAMrB,CAEA,eAAsB,GAAU,EAAS,GAAyB,CACjE,GAAI,CACH,IAAM,EAAO,EAAM,MAAO,CAAC,SAAU,KAAM,UAAU,EAAG,CACvD,MAAO,EAAS,SAAW,SAC5B,CAAC,EACK,EAAW,MAAM,IAAI,SAAiB,EAAS,IAAW,CAC/D,EAAK,GAAG,QAAS,CAAM,EACvB,EAAK,GAAG,QAAU,GAAS,EAAQ,GAAQ,EAAE,CAAC,CAC/C,CAAC,EAWA,OAVG,IAAa,GACX,GACJ,QAAQ,IAAI,oDAAoD,EAE1D,KAEF,IACJ,QAAQ,MAAM,4BAA4B,EAAS,EAAE,EACrD,QAAQ,KAAK,CAAC,GAER,GAET,OAAS,EAAG,CAKX,OAJK,IACJ,QAAQ,MAAM,kBAAmB,EAAY,SAAS,EACtD,QAAQ,KAAK,CAAC,GAER,EACR,CACD,CCvEA,SAAS,IAAW,CACnB,GAAM,CAAE,SAAQ,eAAgB,EAAU,CACzC,QAAS,CACR,KAAM,CAAE,KAAM,UAAW,MAAO,GAAI,EACpC,QAAS,CAAE,KAAM,UAAW,MAAO,GAAI,EACvC,SAAU,CAAE,KAAM,QAAS,EAC3B,MAAO,CAAE,KAAM,QAAS,EACxB,UAAW,CAAE,KAAM,QAAS,EAC5B,QAAS,CAAE,KAAM,SAAU,MAAO,GAAI,CACvC,EACA,OAAQ,GACR,iBAAkB,EACnB,CAAC,EAED,MAAO,CAAE,MAAO,EAAQ,KAAM,CAAY,CAC3C,CAEA,SAAS,GAAU,EAAiB,EAAqB,CACxD,OAAO,EAAO,KAAM,GACf,EAAmB,EAAE,WAAa,GAAc,EAAE,KAAO,EACtD,EAAE,KAAO,CAChB,CACF,CAEA,eAAe,IAAO,CACrB,GAAM,CAAE,QAAO,QAAS,GAAS,EAEjC,GAAI,EAAM,QAAS,CAClB,IAAM,EAAU,MAAM,EAAkB,EACxC,QAAQ,IAAI,QAAQ,GAAS,EAC7B,QAAQ,KAAK,CAAC,CACf,CAsBA,GApBI,EAAM,OACT,QAAQ,IAAI;;;;;;;;;;;;;;+CAciC,EAC7C,QAAQ,KAAK,CAAC,GAIX,EAAK,KAAO,UAAW,CAC1B,MAAM,GAAqB,EAAK,MAAM,CAAC,CAAC,EACxC,MACD,CAGA,GAAI,EAAK,KAAO,SAAU,CACzB,MAAM,GAAU,EAChB,MACD,CAGI,EAAK,OAAS,IACjB,QAAQ,MAAM,EAAM,OAAO,oBAAoB,EAAK,KAAK,GAAG,GAAG,CAAC,EAChE,QAAQ,MAAM,8BAA8B,EAC5C,QAAQ,KAAK,CAAC,GAGf,IAAM,EAAa,IAAI,gBAEjB,MAAiB,CACtB,EAAW,MAAM,EACjB,QAAQ,OAAO,MAAM;;CAAc,EACnC,QAAQ,KAAK,GAAG,CACjB,EACA,QAAQ,GAAG,SAAU,CAAQ,EAC7B,QAAQ,GAAG,UAAW,CAAQ,EAG9B,IAAM,EAAS,MAAQ,MAAM,GAAa,EAAK,EAAW,EAAI,GAAc,GACtE,EAAO,MAAM,EAAS,EAGtB,EAAc,EAAM,UAAuB,EAAO,SAClD,EAAW,EAAM,OAAoB,EAAO,MAC5C,EAAU,EAAM,YAAyB,EAAK,QAAQ,GAEtD,EAAW,EAAY,CAAU,EAClC,IACJ,QAAQ,MAAM,qBAAqB,GAAY,EAC/C,QAAQ,MAAM,cAAc,EAAY,KAAK,EAAI,QAAU,GAAG,yBAAyB,EACvF,QAAQ,KAAK,CAAC,GAGV,IACJ,QAAQ,MACP,kBAAkB,EAAS,KAAK,QAAQ,EAAS,OAAO,6BACzD,EACA,QAAQ,KAAK,CAAC,GAGf,IAAM,EAAQ,GAAU,EAAS,CAAU,EAC3C,GAAI,CAAC,EAAO,CACX,QAAQ,MAAM,kBAAkB,GAAS,EACzC,QAAQ,MAAM,mBAAmB,EACjC,IAAK,IAAM,KAAK,EAAO,OAAQ,GAAM,EAAE,WAAa,CAAU,EAC7D,QAAQ,MAAM,KAAK,EAAE,GAAG,KAAK,EAAE,MAAM,EAEtC,QAAQ,KAAK,CAAC,CACf,CAEA,IAAM,EAAM,QAAQ,IAAI,EAClB,EAAQ,GAAY,CAAG,EACvB,EAAS,GAAkB,EAAK,CAAK,EAGrC,EAAQ,EAAgB,EACxB,EAAU,EAAM,QACnB,EAAM,IAAI,EAAM,OAAiB,EACjC,EAAM,OAAO,EAAK,EAAM,GAAI,CAAU,EAErC,EAAM,SAAW,CAAC,IACrB,QAAQ,MAAM,sBAAsB,EAAM,SAAS,EACnD,QAAQ,KAAK,CAAC,GAGf,IAAM,EAAY,EAAS,GACrB,EAAmB,EAAM,SAAS,CAAS,EAE3C,EAAQ,IAAI,GAAM,CACvB,IAAK,EAAS,IACd,QACA,SACA,QAAS,EAAS,QAClB,SACA,QACA,SAAU,CACX,CAAC,EAGD,QAAQ,IAAI,SAAU,CAAQ,EAC9B,QAAQ,IAAI,UAAW,CAAQ,EAC/B,GAAM,CAAE,eAAgB,MAAM,OAAO,sBACrC,MAAM,EAAY,EAAO,EAAO,CAAS,CAC1C,CAEA,QAAQ,GAAG,qBAAuB,GAAW,CAC5C,QAAQ,MAAM,uBAAwB,CAAM,EAC5C,QAAQ,KAAK,CAAC,CACf,CAAC,EAED,GAAK,EAAE,MAAO,GAAM,CACnB,QAAQ,MAAM,SAAU,CAAC,EACzB,QAAQ,KAAK,CAAC,CACf,CAAC"}
|