@cryer/star-cli 0.3.10 → 0.3.11

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.
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  ToolRegistry,
4
4
  createDefaultRegistry
5
- } from "./chunk-URC7JXKX.js";
5
+ } from "./chunk-Q5FJN4LN.js";
6
6
  import {
7
7
  TodoStore,
8
8
  createTodoTools,
9
9
  setTodoPersistGuard
10
- } from "./chunk-A5PXPWHB.js";
10
+ } from "./chunk-LSYGHLRU.js";
11
11
  export {
12
12
  TodoStore,
13
13
  ToolRegistry,
@@ -15,4 +15,4 @@ export {
15
15
  createTodoTools,
16
16
  setTodoPersistGuard
17
17
  };
18
- //# sourceMappingURL=tools-7EBGCYYE.js.map
18
+ //# sourceMappingURL=tools-BH55BVIS.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cryer/star-cli",
3
- "version": "0.3.10",
3
+ "version": "0.3.11",
4
4
  "description": "An AI agent command-line interface",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -11,29 +11,27 @@
11
11
  },
12
12
  "homepage": "https://github.com/cryer/star-cli#readme",
13
13
  "bugs": "https://github.com/cryer/star-cli/issues",
14
- "keywords": ["ai", "agent", "cli", "llm", "openai", "anthropic", "terminal"],
14
+ "keywords": [
15
+ "ai",
16
+ "agent",
17
+ "cli",
18
+ "llm",
19
+ "openai",
20
+ "anthropic",
21
+ "terminal"
22
+ ],
15
23
  "bin": {
16
24
  "star": "./dist/main.js"
17
25
  },
18
- "files": ["dist"],
26
+ "files": [
27
+ "dist"
28
+ ],
19
29
  "publishConfig": {
20
30
  "access": "public"
21
31
  },
22
32
  "engines": {
23
33
  "node": ">=20.3"
24
34
  },
25
- "scripts": {
26
- "build": "tsup",
27
- "dev": "tsup --watch",
28
- "start": "node dist/main.js",
29
- "test": "vitest run",
30
- "test:watch": "vitest",
31
- "test:pipeline": "node scripts/pipeline.mjs",
32
- "typecheck": "tsc --noEmit",
33
- "lint": "biome check .",
34
- "lint:fix": "biome check --write .",
35
- "prepublishOnly": "pnpm test:pipeline"
36
- },
37
35
  "dependencies": {
38
36
  "@ai-sdk/anthropic": "^1.2.0",
39
37
  "@ai-sdk/openai": "^1",
@@ -55,5 +53,16 @@
55
53
  "tsx": "^4.19.0",
56
54
  "typescript": "^5.7.0",
57
55
  "vitest": "^3.0.0"
56
+ },
57
+ "scripts": {
58
+ "build": "tsup",
59
+ "dev": "tsup --watch",
60
+ "start": "node dist/main.js",
61
+ "test": "vitest run",
62
+ "test:watch": "vitest",
63
+ "test:pipeline": "node scripts/pipeline.mjs",
64
+ "typecheck": "tsc --noEmit",
65
+ "lint": "biome check .",
66
+ "lint:fix": "biome check --write ."
58
67
  }
59
- }
68
+ }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/agent/agent-tasks.ts","../src/core/http-error.ts","../src/core/image.ts","../src/config/paths.ts","../src/tasks/manager.ts","../src/tools/bash.ts","../src/config/env.ts","../src/tools/fs/snapshots.ts","../src/tools/todo.ts","../src/core/image-resize.ts","../src/core/sensitive.ts","../src/tools/fs/util.ts","../src/tools/web/fetch.ts"],"sourcesContent":["import { EventEmitter } from \"node:events\";\n\nexport type AgentTaskStatus = \"running\" | \"completed\" | \"failed\" | \"stopped\";\n\nexport interface AgentTaskSnapshot {\n id: string;\n prompt: string;\n description?: string;\n status: AgentTaskStatus;\n startedAt: number;\n endedAt?: number;\n result: string;\n}\n\ninterface AgentTaskRecord {\n id: string;\n prompt: string;\n description?: string;\n status: AgentTaskStatus;\n startedAt: number;\n endedAt?: number;\n result: string;\n controller: AbortController;\n notified: boolean;\n}\n\n// Finished records are kept only so task_output can still answer for recent\n// tasks; once the parent has been notified they are dropped oldest-first\n// beyond this cap, so a long session cannot accumulate them without bound.\nconst MAX_FINISHED_RECORDS = 50;\n\n// Background subagent runs, mirroring src/tasks/manager.ts for shell tasks.\n// The payload is a promise-producing closure (a child AgentLoop) instead of\n// a child process; finished results are drained into the parent loop's\n// message stream at the next step boundary so the main agent learns\n// outcomes without polling, and are also surfaced to the UI via \"update\".\nexport class AgentTaskManager extends EventEmitter {\n private records = new Map<string, AgentTaskRecord>();\n private counter = 0;\n\n start(\n run: (signal: AbortSignal) => Promise<string>,\n meta: { prompt: string; description?: string },\n ): AgentTaskSnapshot {\n const id = `agent-${++this.counter}`;\n const rec: AgentTaskRecord = {\n id,\n prompt: meta.prompt,\n description: meta.description,\n status: \"running\",\n startedAt: Date.now(),\n result: \"\",\n controller: new AbortController(),\n notified: false,\n };\n this.records.set(id, rec);\n void run(rec.controller.signal)\n .then((result) => this.finish(rec, \"completed\", result))\n .catch((error) =>\n this.finish(rec, \"failed\", error instanceof Error ? error.message : String(error)),\n );\n this.emitUpdate(rec);\n return this.snapshot(rec);\n }\n\n get(id: string): AgentTaskSnapshot | undefined {\n const rec = this.records.get(id);\n return rec ? this.snapshot(rec) : undefined;\n }\n\n list(): AgentTaskSnapshot[] {\n return [...this.records.values()].map((rec) => this.snapshot(rec));\n }\n\n runningCount(): number {\n let count = 0;\n for (const rec of this.records.values()) {\n if (rec.status === \"running\") count += 1;\n }\n return count;\n }\n\n kill(id: string): AgentTaskSnapshot | undefined {\n const rec = this.records.get(id);\n if (!rec || rec.status !== \"running\") return undefined;\n rec.controller.abort();\n this.finish(rec, \"stopped\", rec.result || \"Stopped by user.\");\n return this.snapshot(rec);\n }\n\n cleanup(): AgentTaskSnapshot[] {\n const killed: AgentTaskSnapshot[] = [];\n for (const rec of this.records.values()) {\n if (rec.status === \"running\") {\n rec.controller.abort();\n this.finish(rec, \"stopped\", rec.result || \"Stopped on shutdown.\");\n killed.push(this.snapshot(rec));\n }\n }\n return killed;\n }\n\n // Finished tasks whose result has not yet been handed to the parent loop.\n drainNotifications(): AgentTaskSnapshot[] {\n const drained: AgentTaskSnapshot[] = [];\n for (const rec of this.records.values()) {\n if (rec.status !== \"running\" && !rec.notified) {\n rec.notified = true;\n drained.push(this.snapshot(rec));\n }\n }\n this.pruneFinished();\n return drained;\n }\n\n // Map iteration is insertion-ordered, so the first finished records met are\n // the oldest. Never drop an un-notified record: the parent has not seen its\n // result yet. Running records are untouched.\n private pruneFinished(): void {\n let finished = 0;\n for (const rec of this.records.values()) {\n if (rec.status !== \"running\") finished += 1;\n }\n let excess = finished - MAX_FINISHED_RECORDS;\n if (excess <= 0) return;\n for (const rec of this.records.values()) {\n if (excess <= 0) return;\n if (rec.status !== \"running\" && rec.notified) {\n this.records.delete(rec.id);\n excess -= 1;\n }\n }\n }\n\n private finish(rec: AgentTaskRecord, status: AgentTaskStatus, result: string): void {\n if (rec.status !== \"running\") return;\n rec.status = status;\n rec.result = result;\n rec.endedAt = Date.now();\n this.emitUpdate(rec);\n }\n\n private emitUpdate(rec: AgentTaskRecord): void {\n this.emit(\"update\", this.snapshot(rec));\n }\n\n private snapshot(rec: AgentTaskRecord): AgentTaskSnapshot {\n return {\n id: rec.id,\n prompt: rec.prompt,\n description: rec.description,\n status: rec.status,\n startedAt: rec.startedAt,\n endedAt: rec.endedAt,\n result: rec.result,\n };\n }\n}\n\nexport const defaultAgentTasks = new AgentTaskManager();\n\n// One-line REPL announcement for a task status change. A failure is the final\n// state after the child loop's own retries, so the reason (first line of the\n// failure report, which reads \"Subagent failed: <cause>\") is surfaced inline —\n// a bare \"failed\" looks like a transient hiccup even though the full report\n// has been delivered to the main agent, which decides how to recover.\nexport function formatAgentTaskUpdate(task: AgentTaskSnapshot): string {\n const label = task.description ? `: ${task.description}` : \"\";\n if (task.status === \"running\") {\n return `Background subagent ${task.id} started${label}`;\n }\n let text = `Background subagent ${task.id} ${task.status}${label}`;\n if (task.status === \"failed\") {\n const reason = (task.result.split(\"\\n\")[0] ?? \"\").replace(/^Subagent failed:\\s*/, \"\");\n if (reason) {\n text += ` — ${reason.length > 140 ? `${reason.slice(0, 140)}…` : reason}`;\n }\n }\n return text;\n}\n","// The AI SDK's APICallError carries the raw response body; relays often put\n// the real failure reason there while the message stays generic. Shared by\n// the retry policy (llm/retry.ts) and the oversized-image detector\n// (core/image.ts) — kept in core so neither has to import across layers.\nexport function responseBodyOf(error: unknown): string | undefined {\n const body = (error as { responseBody?: unknown }).responseBody;\n return typeof body === \"string\" && body.length > 0 ? body : undefined;\n}\n","import type { StreamErrorInfo } from \"./events\";\nimport { responseBodyOf } from \"./http-error\";\nimport type { CoreMessage } from \"./messages\";\n\n// Images with a longer side above this many pixels are downsampled before\n// being sent (clipboard/@mention paths) and stripped on retry when a provider\n// rejects them.\nexport const MAX_IMAGE_DIMENSION = 2000;\n// Byte-size fallback for images whose dimensions cannot be probed.\nexport const OVERSIZED_IMAGE_FALLBACK_BYTES = 5 * 1024 * 1024;\nexport const IMAGE_REMOVED_PLACEHOLDER = \"[image removed: too large for the model]\";\nexport const IMAGE_UNSUPPORTED_PLACEHOLDER = \"[image removed: the model cannot read images]\";\n\nexport interface ImageDimensions {\n width: number;\n height: number;\n}\n\nconst PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\nfunction probePng(buf: Buffer): ImageDimensions | null {\n if (buf.length < 24) return null;\n for (let i = 0; i < PNG_SIGNATURE.length; i++) {\n if (buf[i] !== PNG_SIGNATURE[i]) return null;\n }\n if (buf.toString(\"ascii\", 12, 16) !== \"IHDR\") return null;\n return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) };\n}\n\n// Start-of-frame markers that carry dimensions (excludes DHT/JPG/DAC/RST).\nconst JPEG_SOF_MARKERS = new Set([\n 0xc0, 0xc1, 0xc2, 0xc3, 0xc5, 0xc6, 0xc7, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf,\n]);\n\nfunction probeJpeg(buf: Buffer): ImageDimensions | null {\n if (buf.length < 4 || buf[0] !== 0xff || buf[1] !== 0xd8) return null;\n let offset = 2;\n while (offset + 3 < buf.length) {\n if (buf[offset] !== 0xff) return null;\n const marker = buf[offset + 1];\n if (marker === undefined) return null;\n if (marker === 0xd8 || marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) {\n offset += 2;\n continue;\n }\n const length = buf.readUInt16BE(offset + 2);\n if (length < 2) return null;\n if (JPEG_SOF_MARKERS.has(marker)) {\n if (offset + 9 > buf.length) return null;\n return { height: buf.readUInt16BE(offset + 5), width: buf.readUInt16BE(offset + 7) };\n }\n offset += 2 + length;\n }\n return null;\n}\n\nfunction probeGif(buf: Buffer): ImageDimensions | null {\n if (buf.length < 10) return null;\n const signature = buf.toString(\"ascii\", 0, 6);\n if (signature !== \"GIF87a\" && signature !== \"GIF89a\") return null;\n return { width: buf.readUInt16LE(6), height: buf.readUInt16LE(8) };\n}\n\nfunction probeWebp(buf: Buffer): ImageDimensions | null {\n if (buf.length < 30) return null;\n if (buf.toString(\"ascii\", 0, 4) !== \"RIFF\" || buf.toString(\"ascii\", 8, 12) !== \"WEBP\") {\n return null;\n }\n const chunk = buf.toString(\"ascii\", 12, 16);\n if (chunk === \"VP8X\") {\n return { width: 1 + buf.readUIntLE(24, 3), height: 1 + buf.readUIntLE(27, 3) };\n }\n if (chunk === \"VP8 \") {\n return {\n width: buf.readUInt16LE(26) & 0x3fff,\n height: buf.readUInt16LE(28) & 0x3fff,\n };\n }\n if (chunk === \"VP8L\") {\n if (buf[20] !== 0x2f) return null;\n const b1 = buf[21] as number;\n const b2 = buf[22] as number;\n const b3 = buf[23] as number;\n const b4 = buf[24] as number;\n return {\n width: 1 + (((b2 & 0x3f) << 8) | b1),\n height: 1 + (((b4 & 0x0f) << 10) | (b3 << 2) | ((b2 & 0xc0) >> 6)),\n };\n }\n return null;\n}\n\n// Reads pixel dimensions straight from encoded image bytes; null when the\n// format is unrecognized or the header is truncated.\nexport function probeImageDimensions(buf: Buffer): ImageDimensions | null {\n return probePng(buf) ?? probeJpeg(buf) ?? probeGif(buf) ?? probeWebp(buf);\n}\n\n// A provider rejected the request because of an oversized image: a 4xx naming\n// images together with a size/dimension keyword. The message and the response\n// body are tested together — relays often answer with a generic message and\n// put the real reason in the body. Plain \"request too large\" or\n// context-length errors never match — stripping images would not help those.\nconst IMAGE_SIZE_KEYWORDS =\n /too[\\s-]*(large|big)|exceeds?|dimensions?|resolution|size\\s*limit|max(?:imum)?[\\s-]*size/i;\n\nexport function isOversizedImageError(error: StreamErrorInfo): boolean {\n const status = error.statusCode;\n if (status === undefined || status < 400 || status >= 500) return false;\n const text = [error.message, responseBodyOf(error)]\n .filter((part): part is string => typeof part === \"string\")\n .join(\"\\n\");\n return /image/i.test(text) && IMAGE_SIZE_KEYWORDS.test(text);\n}\n\n// A text-only endpoint rejected the request because it contained any image\n// at all (llama.cpp-style \"this server was started without the vision\n// encoder … it cannot read images\", providers answering \"this model does\n// not support images\"). Unlike isOversizedImageError no size keyword is\n// required — the remedy is stripping EVERY image part, not just big ones.\nconst VISION_UNSUPPORTED_KEYWORDS =\n /vision\\s*encoder|can(?:not|'t) (?:read|process|handle|accept) images?|does(?:\\s*not|n't) support (?:image|vision|multimodal)|images?(?:\\s*input)? (?:are|is) not supported|not a vision|no vision (?:capability|support|encoder)|multimodal(?:\\s*input)? (?:is )?not supported/i;\n\nexport function isVisionUnsupportedError(error: StreamErrorInfo): boolean {\n const status = error.statusCode;\n if (status === undefined || status < 400 || status >= 500) return false;\n const text = [error.message, responseBodyOf(error)]\n .filter((part): part is string => typeof part === \"string\")\n .join(\"\\n\");\n return /image|vision|multimodal/i.test(text) && VISION_UNSUPPORTED_KEYWORDS.test(text);\n}\n\nfunction imagePartBytes(image: unknown): Buffer | null {\n if (typeof image === \"string\") {\n const base64 = image.startsWith(\"data:\") ? image.slice(image.indexOf(\",\") + 1) : image;\n try {\n return Buffer.from(base64, \"base64\");\n } catch {\n return null;\n }\n }\n if (image instanceof Uint8Array) return Buffer.from(image);\n return null;\n}\n\nexport function isOversizedImageData(buf: Buffer): boolean {\n const dims = probeImageDimensions(buf);\n if (dims) return Math.max(dims.width, dims.height) > MAX_IMAGE_DIMENSION;\n return buf.length > OVERSIZED_IMAGE_FALLBACK_BYTES;\n}\n\n// Replaces every oversized image part in the history with a text placeholder.\n// Dimension-probeable images are judged by pixels; anything else falls back\n// to the byte cap. The input array is not mutated.\nexport function stripOversizedImages(messages: CoreMessage[]): {\n messages: CoreMessage[];\n removed: number;\n} {\n let removed = 0;\n const result = messages.map((message) => {\n if (!Array.isArray(message.content)) return message;\n let changed = false;\n const content = message.content.map((part) => {\n if (part.type !== \"image\") return part;\n const bytes = imagePartBytes(part.image);\n if (!bytes || !isOversizedImageData(bytes)) return part;\n changed = true;\n removed++;\n return { type: \"text\" as const, text: IMAGE_REMOVED_PLACEHOLDER };\n });\n return changed ? ({ ...message, content } as CoreMessage) : message;\n });\n return { messages: removed > 0 ? result : messages, removed };\n}\n\n// Replaces EVERY image part in the history with a text placeholder — the\n// remedy when the endpoint cannot read images at all (text-only model), not\n// just oversized ones. The input array is not mutated.\nexport function stripAllImages(messages: CoreMessage[]): {\n messages: CoreMessage[];\n removed: number;\n} {\n let removed = 0;\n const result = messages.map((message) => {\n if (!Array.isArray(message.content)) return message;\n let changed = false;\n const content = message.content.map((part) => {\n if (part.type !== \"image\") return part;\n changed = true;\n removed++;\n return { type: \"text\" as const, text: IMAGE_UNSUPPORTED_PLACEHOLDER };\n });\n return changed ? ({ ...message, content } as CoreMessage) : message;\n });\n return { messages: removed > 0 ? result : messages, removed };\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\n\nexport function starHome(): string {\n return process.env.STAR_HOME ?? path.join(os.homedir(), \".star-cli\");\n}\n\nexport function globalConfigPath(): string {\n return path.join(starHome(), \"config.toml\");\n}\n\nexport function sessionsDir(): string {\n return path.join(starHome(), \"sessions\");\n}\n\nexport function userMemoryPath(): string {\n return path.join(starHome(), \"MEMORY.md\");\n}\n\n// Internal bare git repositories tracking the whole working tree per project\n// directory (one per cwd, keyed by base64url of the resolved path) for the\n// git-snapshot /undo//redo path. Kept outside the user's own .git.\nexport function gitTreesDir(): string {\n return path.join(starHome(), \"git-trees\");\n}\n\n// dotenv-style API key store written by /connect; config.toml only references\n// the variable names (apiKeyEnv), never the keys themselves.\nexport function envFilePath(): string {\n return path.join(starHome(), \".env\");\n}\n\nexport function projectConfigPath(cwd: string): string {\n return path.join(cwd, \".star\", \"config.toml\");\n}\n","import { type ChildProcess, spawn } from \"node:child_process\";\nimport { EventEmitter } from \"node:events\";\nimport { childEnv, decodeOutput, killTree, resolveShell, trimUtf8Start } from \"../tools/bash\";\n\nexport type TaskStatus = \"running\" | \"completed\" | \"failed\" | \"timed_out\" | \"stopped\";\n\nexport interface TaskSnapshot {\n id: string;\n // Which agent loop started the task (\"root\" for the main agent, a\n // subagent's agentId otherwise); the task_* tools scope subagents to\n // their own tasks while root reaches all.\n ownerId: string;\n command: string;\n description?: string;\n status: TaskStatus;\n startedAt: number;\n endedAt?: number;\n exitCode?: number | null;\n output: string;\n}\n\nexport const MAX_TASK_OUTPUT = 100_000;\nexport const DEFAULT_TASK_TIMEOUT = 3600;\n// Finished records are pruned to this many most-recent entries so a long\n// session's task history cannot grow without bound; running tasks are never\n// evicted.\nexport const MAX_FINISHED_RECORDS = 50;\n\nconst TRUNCATED_PREFIX = \"[... earlier output truncated ...]\\n\";\n\ninterface TaskRecord {\n id: string;\n ownerId: string;\n command: string;\n description?: string;\n status: TaskStatus;\n startedAt: number;\n endedAt?: number;\n exitCode?: number | null;\n // Output accumulates as raw bytes and decodes once per snapshot: a\n // multi-byte character split across chunks must not become U+FFFD, and\n // Windows OEM-codepage (GBK) output needs the whole buffer to re-decode.\n outputChunks: Buffer[];\n outputBytes: number;\n truncated: boolean;\n child: ChildProcess;\n timer?: NodeJS.Timeout;\n}\n\nexport interface StartTaskOptions {\n command: string;\n description?: string;\n cwd: string;\n timeoutSeconds?: number;\n // Owning agent id for task_* scoping; defaults to \"root\".\n ownerId?: string;\n // Child environment; defaults to the scrubbed childEnv() (.env API keys\n // removed) so a background command can't print the stored secrets.\n env?: NodeJS.ProcessEnv;\n}\n\nexport class TaskManager extends EventEmitter {\n private records = new Map<string, TaskRecord>();\n private seq = 0;\n\n start(opts: StartTaskOptions): TaskSnapshot {\n const id = `task-${++this.seq}`;\n const spec = resolveShell();\n const child = spawn(spec.shell, spec.wrap(opts.command), {\n cwd: opts.cwd,\n env: opts.env ?? childEnv(),\n windowsHide: true,\n // No stdin: background commands that read it would hang until the\n // (much longer) task timeout; give them EOF immediately.\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n // POSIX: the child leads its own process group so killTree can SIGKILL\n // the whole tree with a negative pid (Windows uses taskkill /t).\n detached: process.platform !== \"win32\",\n });\n const rec: TaskRecord = {\n id,\n ownerId: opts.ownerId ?? \"root\",\n command: opts.command,\n description: opts.description,\n status: \"running\",\n startedAt: Date.now(),\n outputChunks: [],\n outputBytes: 0,\n truncated: false,\n child,\n };\n this.records.set(id, rec);\n\n const append = (d: Buffer) => {\n rec.outputChunks.push(d);\n rec.outputBytes += d.length;\n if (rec.outputBytes > MAX_TASK_OUTPUT) {\n const merged = Buffer.concat(rec.outputChunks, rec.outputBytes);\n const kept = trimUtf8Start(merged.subarray(merged.length - MAX_TASK_OUTPUT));\n rec.outputChunks = [kept];\n rec.outputBytes = kept.length;\n rec.truncated = true;\n }\n };\n child.stdout.on(\"data\", append);\n child.stderr.on(\"data\", append);\n child.on(\"error\", (err) => {\n append(Buffer.from(`Failed to start shell '${spec.label}': ${err.message}\\n`));\n if (rec.status === \"running\") {\n this.finish(rec, \"failed\", null);\n }\n });\n child.on(\"close\", (code) => {\n if (rec.status !== \"running\") return;\n this.finish(rec, code === 0 ? \"completed\" : \"failed\", code);\n });\n\n const timeoutSeconds = opts.timeoutSeconds ?? DEFAULT_TASK_TIMEOUT;\n rec.timer = setTimeout(() => this.terminate(rec, \"timed_out\"), timeoutSeconds * 1000);\n rec.timer.unref();\n\n this.emitUpdate(rec);\n return this.snapshot(rec);\n }\n\n get(id: string): TaskSnapshot | undefined {\n const rec = this.records.get(id);\n return rec ? this.snapshot(rec) : undefined;\n }\n\n list(): TaskSnapshot[] {\n return [...this.records.values()].map((rec) => this.snapshot(rec));\n }\n\n runningCount(): number {\n let count = 0;\n for (const rec of this.records.values()) {\n if (rec.status === \"running\") count += 1;\n }\n return count;\n }\n\n kill(id: string): TaskSnapshot | undefined {\n const rec = this.records.get(id);\n if (!rec || rec.status !== \"running\") return undefined;\n this.terminate(rec, \"stopped\");\n return this.snapshot(rec);\n }\n\n cleanup(): TaskSnapshot[] {\n const killed: TaskSnapshot[] = [];\n for (const rec of this.records.values()) {\n if (rec.status === \"running\") {\n this.terminate(rec, \"stopped\");\n killed.push(this.snapshot(rec));\n }\n }\n return killed;\n }\n\n private terminate(rec: TaskRecord, status: TaskStatus): void {\n killTree(rec.child);\n this.finish(rec, status, null);\n }\n\n private finish(rec: TaskRecord, status: TaskStatus, exitCode: number | null): void {\n if (rec.timer) clearTimeout(rec.timer);\n rec.status = status;\n rec.exitCode = exitCode;\n rec.endedAt = Date.now();\n this.pruneFinished();\n this.emitUpdate(rec);\n }\n\n private pruneFinished(): void {\n const finished = [...this.records.values()].filter((rec) => rec.status !== \"running\");\n if (finished.length <= MAX_FINISHED_RECORDS) return;\n finished.sort((a, b) => (a.endedAt ?? 0) - (b.endedAt ?? 0));\n for (const rec of finished.slice(0, finished.length - MAX_FINISHED_RECORDS)) {\n this.records.delete(rec.id);\n }\n }\n\n private emitUpdate(rec: TaskRecord): void {\n this.emit(\"update\", this.snapshot(rec));\n }\n\n private snapshot(rec: TaskRecord): TaskSnapshot {\n const output = decodeOutput(Buffer.concat(rec.outputChunks, rec.outputBytes));\n return {\n id: rec.id,\n ownerId: rec.ownerId,\n command: rec.command,\n description: rec.description,\n status: rec.status,\n startedAt: rec.startedAt,\n endedAt: rec.endedAt,\n exitCode: rec.exitCode,\n output: rec.truncated ? TRUNCATED_PREFIX + output : output,\n };\n }\n}\n\nexport const defaultTaskManager = new TaskManager();\n","import { type ChildProcess, spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport { getEnvFileKeys } from \"../config/env\";\nimport { defaultTaskManager } from \"../tasks/manager\";\nimport type { Tool, ToolResult } from \"./types\";\n\nexport const MAX_OUTPUT = 30000;\nconst HALF_OUTPUT = MAX_OUTPUT / 2;\nconst DEFAULT_TIMEOUT = 120;\nconst MAX_TIMEOUT = 600;\n\nexport interface ShellSpec {\n shell: string;\n wrap: (command: string) => string[];\n label: string;\n}\n\nfunction findOnPath(exe: string, exclude?: (dir: string) => boolean): string | null {\n for (const dir of (process.env.PATH ?? \"\").split(path.delimiter)) {\n if (!dir || exclude?.(dir)) {\n continue;\n }\n const full = path.join(dir, exe);\n if (existsSync(full)) {\n return full;\n }\n }\n return null;\n}\n\nfunction resolveShellUncached(): ShellSpec {\n if (process.platform !== \"win32\") {\n return { shell: \"sh\", wrap: (c) => [\"-c\", c], label: \"sh\" };\n }\n const isWslStub = (dir: string) => /\\\\(system32|windowsapps)\\\\?$/i.test(dir.trim());\n const fromPath = findOnPath(\"bash.exe\", isWslStub);\n if (fromPath) {\n return { shell: fromPath, wrap: (c) => [\"-c\", c], label: \"bash\" };\n }\n const gitExe = findOnPath(\"git.exe\");\n if (gitExe) {\n const sibling = path.join(path.dirname(path.dirname(gitExe)), \"bin\", \"bash.exe\");\n if (existsSync(sibling)) {\n return { shell: sibling, wrap: (c) => [\"-c\", c], label: \"bash\" };\n }\n }\n // Portable fallback: the standard Git for Windows install locations, built\n // from the Program Files environment variables instead of hardcoded drives.\n const seen = new Set<string>();\n for (const root of [\n process.env.ProgramFiles,\n process.env[\"ProgramFiles(x86)\"],\n process.env.ProgramW6432,\n ]) {\n if (!root) continue;\n const candidate = path.join(root, \"Git\", \"bin\", \"bash.exe\");\n const key = candidate.toLowerCase();\n if (seen.has(key)) continue;\n seen.add(key);\n if (existsSync(candidate)) {\n return { shell: candidate, wrap: (c) => [\"-c\", c], label: \"bash\" };\n }\n }\n const comspec = process.env.ComSpec ?? \"cmd.exe\";\n return { shell: comspec, wrap: (c) => [\"/d\", \"/s\", \"/c\", c], label: \"cmd\" };\n}\n\n// The PATH scan runs once per process; the shell cannot change mid-session.\nlet cachedSpec: ShellSpec | null = null;\n\nexport function resolveShell(): ShellSpec {\n if (!cachedSpec) {\n cachedSpec = resolveShellUncached();\n }\n return cachedSpec;\n}\n\nconst schema = z.object({\n command: z.string().describe(\"Shell command to execute\"),\n timeout: z\n .number()\n .positive()\n .max(MAX_TIMEOUT)\n .optional()\n .describe(\"Timeout in seconds (default 120, max 600)\"),\n description: z.string().optional().describe(\"Short description of what the command does\"),\n run_in_background: z\n .boolean()\n .optional()\n .describe(\n \"Run the command in the background and return a task id immediately (default false). Use task_output/task_kill to inspect or stop it.\",\n ),\n});\n\nexport function killTree(child: ChildProcess): void {\n if (process.platform === \"win32\") {\n if (child.pid) {\n spawn(\"taskkill\", [\"/pid\", String(child.pid), \"/t\", \"/f\"], {\n windowsHide: true,\n stdio: \"ignore\",\n }).unref();\n }\n return;\n }\n // POSIX children spawn detached, so the pid leads a process group and a\n // negative-pid kill reaches shell grandchildren too.\n if (child.pid) {\n try {\n process.kill(-child.pid, \"SIGKILL\");\n return;\n } catch {\n // group already gone (or not detached): fall back to the direct kill\n }\n }\n child.kill(\"SIGKILL\");\n}\n\n// Bounds memory while a command streams: once the output passes MAX_OUTPUT\n// bytes the first half is kept verbatim, the second half rolls, and the\n// dropped middle is counted so toString() matches truncating the unbounded\n// output. Chunks accumulate as raw bytes and decode once in toString(), so\n// a multi-byte character split across chunk boundaries stays intact.\nclass BoundedOutput {\n private head: Buffer = Buffer.alloc(0);\n private tail: Buffer = Buffer.alloc(0);\n private dropped = 0;\n private overflow = false;\n\n append(chunk: Buffer): void {\n if (!this.overflow) {\n this.head = this.head.length === 0 ? chunk : Buffer.concat([this.head, chunk]);\n if (this.head.length <= MAX_OUTPUT) {\n return;\n }\n this.overflow = true;\n this.tail = this.head.subarray(HALF_OUTPUT);\n this.head = this.head.subarray(0, HALF_OUTPUT);\n if (this.tail.length > HALF_OUTPUT) {\n this.dropped = this.tail.length - HALF_OUTPUT;\n this.tail = this.tail.subarray(this.tail.length - HALF_OUTPUT);\n }\n return;\n }\n this.tail = this.tail.length === 0 ? chunk : Buffer.concat([this.tail, chunk]);\n if (this.tail.length > HALF_OUTPUT) {\n this.dropped += this.tail.length - HALF_OUTPUT;\n this.tail = this.tail.subarray(this.tail.length - HALF_OUTPUT);\n }\n }\n\n toString(): string {\n if (!this.overflow) {\n return decodeOutput(this.head);\n }\n // The head/tail seam can split a multi-byte character; drop the partial\n // sequence so it doesn't decode as U+FFFD (or trip the GBK fallback).\n const head = decodeOutput(trimUtf8End(this.head));\n const tail = decodeOutput(trimUtf8Start(this.tail));\n return `${head}\\n... [${this.dropped} bytes truncated] ...\\n${tail}`;\n }\n}\n\n// Drops a trailing incomplete UTF-8 sequence (1-3 bytes) so a truncation\n// seam doesn't decode to U+FFFD; the other half of the character is gone\n// either way.\nexport function trimUtf8End(buf: Buffer): Buffer {\n let cont = 0;\n let i = buf.length;\n while (i > 0 && cont < 3 && ((buf[i - 1] ?? 0) & 0xc0) === 0x80) {\n i--;\n cont++;\n }\n if (i === 0) return buf;\n const lead = buf[i - 1] ?? 0;\n if (lead < 0x80 || (lead & 0xc0) === 0x80) return buf;\n const needed = lead < 0xe0 ? 1 : lead < 0xf0 ? 2 : 3;\n return cont < needed ? buf.subarray(0, i - 1) : buf;\n}\n\n// Drops leading UTF-8 continuation bytes: after truncating to a byte window\n// the first bytes may be the tail of a character whose lead was cut.\nexport function trimUtf8Start(buf: Buffer): Buffer {\n let i = 0;\n while (i < buf.length && i < 3 && ((buf[i] ?? 0) & 0xc0) === 0x80) i++;\n return i === 0 ? buf : buf.subarray(i);\n}\n\n// Decodes captured child output once, after the chunks are concatenated.\n// On Windows a UTF-8 decode failure means the bytes came from a native\n// program writing the OEM codepage (GBK on Chinese systems) — Node ships\n// full-icu, so the \"gbk\" TextDecoder label is always available as fallback.\nexport function decodeOutput(buf: Buffer, platform: NodeJS.Platform = process.platform): string {\n const text = buf.toString(\"utf8\");\n if (platform === \"win32\" && text.includes(\"�\")) {\n try {\n return new TextDecoder(\"gbk\").decode(buf);\n } catch {\n return text;\n }\n }\n return text;\n}\n\n// Environment for child processes: process.env minus the keys loadEnvFile\n// read from ~/.star-cli/.env (API secrets). In auto mode a child could\n// otherwise print them with `env` or exfiltrate via `curl $KEY`. The main\n// process's own process.env keeps them for provider calls.\nexport function childEnv(): NodeJS.ProcessEnv {\n const env = { ...process.env };\n for (const key of getEnvFileKeys()) {\n delete env[key];\n }\n return env;\n}\n\nexport const bashTool: Tool<typeof schema> = {\n name: \"bash\",\n description:\n \"Execute a shell command (Git Bash on Windows when available, otherwise cmd; sh elsewhere). Stdout and stderr are merged. Output is truncated to 30000 bytes. Set run_in_background for long-running commands; it returns a task id for task_list/task_output/task_kill.\",\n permission: \"exec\",\n parameters: schema,\n execute(args, ctx) {\n if (args.run_in_background) {\n const task = defaultTaskManager.start({\n command: args.command,\n description: args.description,\n cwd: ctx.cwd,\n timeoutSeconds: args.timeout,\n ownerId: ctx.agentId ?? \"root\",\n env: childEnv(),\n });\n return Promise.resolve({\n content: `Background task started: ${task.id}\\ncommand: ${args.command}\\ndescription: ${args.description ?? \"(none)\"}`,\n });\n }\n const timeoutSeconds = Math.min(args.timeout ?? DEFAULT_TIMEOUT, MAX_TIMEOUT);\n const spec = resolveShell();\n return new Promise<ToolResult>((resolve) => {\n const child = spawn(spec.shell, spec.wrap(args.command), {\n cwd: ctx.cwd,\n env: childEnv(),\n windowsHide: true,\n detached: process.platform !== \"win32\",\n // No stdin: a command that reads it (cat, read, input()) must get\n // EOF immediately instead of hanging until the timeout.\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n const output = new BoundedOutput();\n let settled = false;\n const finish = (result: ToolResult) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n ctx.abortSignal?.removeEventListener(\"abort\", onAbort);\n resolve(result);\n };\n const onAbort = () => {\n killTree(child);\n finish({ content: `${output}\\nCommand aborted`, isError: true });\n };\n const timer = setTimeout(() => {\n killTree(child);\n finish({\n content: `${output}\\nCommand timed out after ${timeoutSeconds}s`,\n isError: true,\n });\n }, timeoutSeconds * 1000);\n timer.unref();\n child.stdout.on(\"data\", (d: Buffer) => output.append(d));\n child.stderr.on(\"data\", (d: Buffer) => output.append(d));\n child.on(\"error\", (err) => {\n finish({ content: `Failed to start shell '${spec.label}': ${err.message}`, isError: true });\n });\n ctx.abortSignal?.addEventListener(\"abort\", onAbort);\n child.on(\"close\", (code) => {\n const trimmed = output.toString().replace(/\\s+$/, \"\");\n if (code === 0) {\n finish({ content: trimmed || \"(no output)\" });\n return;\n }\n finish({\n content: `${trimmed}${trimmed ? \"\\n\" : \"\"}Exit code: ${code ?? \"unknown\"}`,\n isError: true,\n });\n });\n });\n },\n};\n","import fs from \"node:fs\";\nimport { envFilePath } from \"./paths\";\n\nconst ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n// Parses dotenv-style KEY=VALUE lines; blank lines and # comments are\n// skipped, surrounding quotes on the value are stripped.\nexport function parseEnvContent(content: string): Record<string, string> {\n const vars: Record<string, string> = {};\n for (const rawLine of content.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (!line || line.startsWith(\"#\")) continue;\n const eq = line.indexOf(\"=\");\n if (eq <= 0) continue;\n const key = line.slice(0, eq).trim();\n if (!ENV_KEY_PATTERN.test(key)) continue;\n let value = line.slice(eq + 1).trim();\n if (\n value.length >= 2 &&\n ((value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\")))\n ) {\n value = value.slice(1, -1);\n }\n vars[key] = value;\n }\n return vars;\n}\n\n// Keys the .env file declared when loadEnvFile parsed it (whether or not\n// they won over the real environment). bash scrubs these from child-process\n// environments: the file exists to store API secrets, and in auto mode a\n// plain `env` or `curl $KEY` would otherwise print them. The main process\n// keeps process.env untouched for its own provider calls.\nconst envFileKeySet = new Set<string>();\n\nexport function getEnvFileKeys(): ReadonlySet<string> {\n return envFileKeySet;\n}\n\n// Fills process.env from ~/.star-cli/.env. Variables already set in the real\n// environment always win — the file only supplies missing values. Never\n// throws: a missing or unreadable file means no keys.\nexport function loadEnvFile(filePath: string = envFilePath()): void {\n let content: string;\n try {\n content = fs.readFileSync(filePath, \"utf8\");\n } catch {\n return;\n }\n for (const [key, value] of Object.entries(parseEnvContent(content))) {\n envFileKeySet.add(key);\n if (process.env[key] === undefined) {\n process.env[key] = value;\n }\n }\n}\n\n// Returns content with key set to value: the existing KEY= line is replaced\n// in place (comments and other variables untouched), otherwise a new line is\n// appended.\nexport function upsertEnvContent(content: string, key: string, value: string): string {\n const entry = `${key}=${value.replace(/[\\r\\n]/g, \"\")}`;\n const pattern = new RegExp(`^\\\\s*${key}\\\\s*=`);\n const lines = content.split(\"\\n\");\n while (lines.length > 0 && (lines[lines.length - 1] ?? \"\").trim() === \"\") lines.pop();\n for (let i = 0; i < lines.length; i++) {\n if (pattern.test(lines[i] ?? \"\")) {\n lines[i] = entry;\n return `${lines.join(\"\\n\")}\\n`;\n }\n }\n lines.push(entry);\n return `${lines.join(\"\\n\")}\\n`;\n}\n","import { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rm, stat, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport type { SnapshotContext } from \"../types\";\n\nexport type SnapshotOwner = \"root\" | \"subagent\";\n\nexport interface FileSnapshot {\n // Checkpoint id, monotonically increasing per process (seeded from the\n // resumed session's records, so ids stay unique across resume). Assigned\n // when the snapshot is captured; failed tool calls leave gaps, which is fine.\n id: number;\n path: string;\n existed: boolean;\n // Pre-change content. Held in memory only between captureSnapshot and\n // pushSnapshot (which spills it to disk); afterwards null and read lazily\n // from contentFile, so a full stack of large files costs no RAM. Raw bytes\n // (Buffer) end to end so binary files survive capture → spill → restore\n // byte-exact; a utf8 round-trip would corrupt them with U+FFFD. edit_file\n // still pushes a string (it only edits text) — written back as utf8.\n content: string | Buffer | null;\n // Set when the pre-change content exceeded MAX_SNAPSHOT_CONTENT_BYTES: only\n // metadata is kept (no content, no persisted content file) and revert()\n // skips the file instead of restoring it.\n contentTooLarge?: boolean;\n // Lazy source for the old content once it left memory: the per-process\n // spill file (pushed snapshots) or the persisted session checkpoint content\n // file (snapshots hydrated from a resumed session).\n contentFile?: string;\n toolName: string;\n timestamp: number;\n // Seq of the conversation turn that produced this snapshot (0 = no turn\n // context, e.g. direct tool use in tests). Monotonically increasing per\n // process, so a turn's snapshots can be reverted without touching older ones.\n turn: number;\n // Index of the user message that started the turn, used by /rewind to\n // retract the conversation to the same point. -1 = unknown (no turn\n // context, or the change came from a subagent turn).\n messageIndex: number;\n // Who made the change. Root changes are undoable per turn; subagent changes\n // span arbitrary parent turns, so turn-scoped listings and reverts exclude\n // them unless includeSubagent is passed. Missing means root (checkpoints\n // persisted before this field stay undoable).\n owner?: SnapshotOwner;\n}\n\n// Persistence callbacks, bound by the agent loop when a session store is\n// attached. Every snapshot that enters or leaves the in-memory stack is\n// mirrored to the session directory so /rewind works after resume.\nexport interface SnapshotHooks {\n onPush?(snapshot: FileSnapshot): void | Promise<void>;\n onRemove?(ids: number[]): void | Promise<void>;\n}\n\nconst MAX_SNAPSHOTS = 50;\nexport const MAX_SNAPSHOT_CONTENT_BYTES = 5 * 1024 * 1024;\n\nconst stack: FileSnapshot[] = [];\nlet activeTurn = 0;\nlet activeTurnMessageIndex = -1;\nlet checkpointSeq = 0;\nlet hooks: SnapshotHooks | null = null;\n\n// Spilled snapshot contents live in a per-process temp directory (unique per\n// module instance so parallel test workers never collide) and are deleted\n// when their snapshot leaves the stack. Spilling is a memory optimization\n// only: on any failure the content just stays in memory.\nconst spillDir = path.join(\n os.tmpdir(),\n `star-cli-snapshots-${process.pid}-${randomUUID().slice(0, 8)}`,\n);\n\nfunction isSpillPath(p: string): boolean {\n return p.startsWith(spillDir + path.sep);\n}\n\nasync function spillContent(snapshot: FileSnapshot): Promise<void> {\n if (snapshot.content === null || snapshot.contentFile) return;\n try {\n const file = path.join(spillDir, `${snapshot.id}.snap`);\n await mkdir(spillDir, { recursive: true, mode: 0o700 });\n // Strings default to utf8 (same as before); Buffers are written raw so\n // binary snapshots spill byte-exact.\n await writeFile(file, snapshot.content, { mode: 0o600 });\n snapshot.contentFile = file;\n snapshot.content = null;\n } catch {\n // keep the content in memory\n }\n}\n\n// Deletes the spill file of a snapshot leaving the stack. Never touches a\n// contentFile that points at a persisted session checkpoint — the onRemove\n// hook owns those.\nasync function discardSpill(snapshot: FileSnapshot): Promise<void> {\n if (snapshot.contentFile && isSpillPath(snapshot.contentFile)) {\n await rm(snapshot.contentFile, { force: true }).catch(() => {});\n }\n}\n\nexport function setSnapshotHooks(next: SnapshotHooks | null): void {\n hooks = next;\n}\n\nexport function nextSnapshotId(): number {\n return ++checkpointSeq;\n}\n\n// Called by the agent loop at the start of every conversation turn; returns\n// the seq assigned to the turn. Tool-made snapshots record this seq and the\n// message index the turn started at.\nexport function beginTurn(messageIndex = -1): number {\n activeTurnMessageIndex = messageIndex;\n return ++activeTurn;\n}\n\nexport function currentTurnSeq(): number {\n return activeTurn;\n}\n\nexport function currentTurnMessageIndex(): number {\n return activeTurnMessageIndex;\n}\n\nfunction ownerOf(snapshot: FileSnapshot): SnapshotOwner {\n return snapshot.owner ?? \"root\";\n}\n\nexport async function captureSnapshot(\n filePath: string,\n toolName: string,\n snapshotContext?: SnapshotContext,\n): Promise<FileSnapshot> {\n let content: string | Buffer | null = null;\n let existed = false;\n let contentTooLarge = false;\n const st = await stat(filePath).catch(() => null);\n if (st?.isFile()) {\n existed = true;\n if (st.size > MAX_SNAPSHOT_CONTENT_BYTES) {\n contentTooLarge = true;\n } else {\n try {\n // No encoding: capture the raw bytes so a later restore is byte-exact\n // even for binary files (write_file may overwrite one).\n content = await readFile(filePath);\n } catch {\n // unreadable files keep the old behavior: recorded as non-existent\n existed = false;\n }\n }\n }\n return {\n id: nextSnapshotId(),\n path: filePath,\n existed,\n content,\n contentTooLarge: contentTooLarge || undefined,\n toolName,\n timestamp: Date.now(),\n turn: snapshotContext?.turn ?? activeTurn,\n messageIndex: snapshotContext?.messageIndex ?? activeTurnMessageIndex,\n owner: snapshotContext?.owner ?? \"root\",\n };\n}\n\nexport async function pushSnapshot(snapshot: FileSnapshot): Promise<void> {\n stack.push(snapshot);\n try {\n // Hooks run before the spill: checkpoint persistence reads\n // snapshot.content, which the spill then clears.\n await hooks?.onPush?.(snapshot);\n } catch (err) {\n // The file write already succeeded; failing the tool over checkpoint\n // persistence would make the model retry the same write.\n if (process.env.STAR_DEBUG) {\n console.error(\"[star] failed to persist snapshot checkpoint:\", err);\n }\n }\n await spillContent(snapshot);\n if (stack.length > MAX_SNAPSHOTS) {\n const evicted = stack.splice(0, stack.length - MAX_SNAPSHOTS);\n for (const old of evicted) await discardSpill(old);\n await removeFromStore(evicted.map((s) => s.id));\n }\n}\n\nasync function removeFromStore(ids: number[]): Promise<void> {\n if (ids.length > 0) await hooks?.onRemove?.(ids);\n}\n\nexport function snapshotCount(): number {\n return stack.length;\n}\n\nexport function listSnapshots(): FileSnapshot[] {\n return [...stack];\n}\n\nexport function clearSnapshots(): void {\n for (const snapshot of stack) void discardSpill(snapshot);\n stack.length = 0;\n}\n\n// Replaces the in-memory stack with checkpoints loaded from a resumed\n// session's persisted records. The id counter is advanced past the loaded\n// records so new checkpoints never collide with persisted ones.\nexport function hydrateSnapshots(snapshots: FileSnapshot[]): void {\n for (const snapshot of stack) void discardSpill(snapshot);\n stack.length = 0;\n stack.push(...snapshots.slice(-MAX_SNAPSHOTS));\n for (const snapshot of stack) {\n if (snapshot.id > checkpointSeq) checkpointSeq = snapshot.id;\n }\n}\n\nasync function resolveContent(snapshot: FileSnapshot): Promise<string | Buffer | null> {\n if (snapshot.content !== null) return snapshot.content;\n if (!snapshot.contentFile) return null;\n try {\n // No encoding: the spill/checkpoint file holds raw bytes (utf8 for\n // string-origin content), and revert writes them back verbatim.\n return await readFile(snapshot.contentFile);\n } catch {\n return null;\n }\n}\n\nasync function revert(snapshot: FileSnapshot): Promise<string> {\n if (snapshot.existed) {\n if (snapshot.contentTooLarge) {\n return `Skipped ${snapshot.path}: content too large, not restored.`;\n }\n const content = await resolveContent(snapshot);\n if (content === null) {\n return `Skipped ${snapshot.path}: the snapshot content is no longer available.`;\n }\n await mkdir(path.dirname(snapshot.path), { recursive: true });\n await writeFile(snapshot.path, content);\n return `Restored ${snapshot.path} to its state before ${snapshot.toolName}.`;\n }\n await rm(snapshot.path, { force: true });\n return `Deleted ${snapshot.path} (it did not exist before ${snapshot.toolName}).`;\n}\n\nexport async function undoLastSnapshot(): Promise<string> {\n const snapshot = stack[stack.length - 1];\n if (!snapshot) {\n return \"Nothing to undo (/undo only reverts file changes made by write_file/edit_file, and none are recorded yet).\";\n }\n try {\n const message = await revert(snapshot);\n stack.pop();\n await discardSpill(snapshot);\n await removeFromStore([snapshot.id]);\n return message;\n } catch (err) {\n return `Failed to revert ${snapshot.path}: ${(err as Error).message} (checkpoint kept)`;\n }\n}\n\n// Read-only view of the snapshots a turn-scoped /undo would revert, in the\n// same newest-first order undoTurnSnapshots applies them. Subagent snapshots\n// are excluded unless includeSubagent is set.\nexport function listTurnSnapshots(turn: number, includeSubagent = false): FileSnapshot[] {\n return stack\n .filter(\n (snapshot) => snapshot.turn === turn && (includeSubagent || ownerOf(snapshot) === \"root\"),\n )\n .reverse();\n}\n\n// Old content of a snapshot, read lazily from the spill file or the\n// persisted session checkpoint content file. Exported for the read-only\n// /undo preview; revert() uses the same path. Binary snapshots decode\n// lossy here — the preview is display-only, restores stay byte-exact.\nexport async function resolveSnapshotContent(snapshot: FileSnapshot): Promise<string | null> {\n const content = await resolveContent(snapshot);\n if (content === null) return null;\n return typeof content === \"string\" ? content : content.toString(\"utf8\");\n}\n\n// Reverts every snapshot created by the given turn, newest first, and leaves\n// snapshots from other turns untouched. A snapshot that fails to revert stays\n// on the stack (and keeps its persisted checkpoint); the returned messages\n// report each file's outcome.\nexport async function undoTurnSnapshots(turn: number, includeSubagent = false): Promise<string[]> {\n const matches: FileSnapshot[] = [];\n for (let i = stack.length - 1; i >= 0; i--) {\n const snapshot = stack[i];\n if (snapshot && snapshot.turn === turn && (includeSubagent || ownerOf(snapshot) === \"root\")) {\n matches.push(snapshot);\n }\n }\n const messages: string[] = [];\n const revertedIds: number[] = [];\n for (const snapshot of matches) {\n try {\n messages.push(await revert(snapshot));\n const idx = stack.indexOf(snapshot);\n if (idx >= 0) stack.splice(idx, 1);\n revertedIds.push(snapshot.id);\n await discardSpill(snapshot);\n } catch (err) {\n messages.push(\n `Failed to revert ${snapshot.path}: ${(err as Error).message} (checkpoint kept)`,\n );\n }\n }\n await removeFromStore(revertedIds);\n return messages;\n}\n\n// Removes a turn's snapshots from the stack and the persisted store WITHOUT\n// reverting them: the git-tree /undo path already restored the whole working\n// tree, so replaying the per-file reverts would double-restore. Returns the\n// number of dropped snapshots.\nexport async function dropTurnSnapshots(turn: number, includeSubagent = false): Promise<number> {\n const dropped: number[] = [];\n const droppedSnapshots: FileSnapshot[] = [];\n for (let i = stack.length - 1; i >= 0; i--) {\n const snapshot = stack[i];\n if (snapshot && snapshot.turn === turn && (includeSubagent || ownerOf(snapshot) === \"root\")) {\n dropped.push(snapshot.id);\n droppedSnapshots.push(snapshot);\n stack.splice(i, 1);\n }\n }\n for (const snapshot of droppedSnapshots) await discardSpill(snapshot);\n await removeFromStore(dropped);\n return dropped.length;\n}\n\nexport interface RewindResult {\n reverted: string[];\n // Smallest conversation message index among the reverted snapshots; the\n // caller retracts the conversation back to this point. null when none of\n // the reverted snapshots carries turn context.\n messageIndex: number | null;\n}\n\n// Reverts every snapshot from checkpoint `id` onwards, newest first, restoring\n// the file state as it was just before that checkpoint. Successfully reverted\n// snapshots are dropped from the stack (and from the persisted store via\n// hooks); a snapshot that fails to revert stays on the stack, keeps its\n// persisted checkpoint, and gets a failure line in the result.\nexport async function rewindToSnapshot(\n id: number,\n includeSubagent = false,\n): Promise<RewindResult | null> {\n if (!stack.some((snapshot) => snapshot.id === id)) return null;\n const matches: FileSnapshot[] = [];\n for (let i = stack.length - 1; i >= 0; i--) {\n const snapshot = stack[i];\n if (snapshot && snapshot.id >= id && (includeSubagent || ownerOf(snapshot) === \"root\")) {\n matches.push(snapshot);\n }\n }\n const reverted: string[] = [];\n const revertedIds: number[] = [];\n let messageIndex: number | null = null;\n for (const snapshot of matches) {\n try {\n reverted.push(await revert(snapshot));\n const idx = stack.indexOf(snapshot);\n if (idx >= 0) stack.splice(idx, 1);\n revertedIds.push(snapshot.id);\n await discardSpill(snapshot);\n if (snapshot.messageIndex >= 0) {\n messageIndex =\n messageIndex === null\n ? snapshot.messageIndex\n : Math.min(messageIndex, snapshot.messageIndex);\n }\n } catch (err) {\n reverted.push(\n `Failed to revert ${snapshot.path}: ${(err as Error).message} (checkpoint kept)`,\n );\n }\n }\n await removeFromStore(revertedIds);\n return { reverted, messageIndex };\n}\n","import { mkdir, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport type { Tool } from \"./types\";\n\nexport interface TodoItem {\n id: number;\n title: string;\n status: \"pending\" | \"in_progress\" | \"done\";\n}\n\nconst todoItemSchema = z.object({\n id: z.number().int(),\n title: z.string(),\n status: z.enum([\"pending\", \"in_progress\", \"done\"]),\n});\n\nconst fileSchema = z.object({\n sessionId: z.string().nullable(),\n items: z.array(todoItemSchema),\n});\n\nconst writeSchema = z.object({\n todos: z.array(todoItemSchema).describe(\"The full todo list, replacing the current one\"),\n});\n\nconst readSchema = z.object({});\n\nfunction fileFor(cwd: string): string {\n return path.join(cwd, \".star\", \"todos.json\");\n}\n\n// Wired by the agent loop to the permission mode: readonly/plan sessions\n// still keep the in-memory list but must not touch .star/todos.json.\nlet persistGuard: () => boolean = () => true;\n\nexport function setTodoPersistGuard(guard: () => boolean): void {\n persistGuard = guard;\n}\n\n// The session that owns the persisted todo list. .star/todos.json is\n// per-project-directory, so stamping it with the owning session id is what\n// keeps one session's list from leaking into another session resumed in the\n// same directory. null = no session bound (e.g. print mode without a store);\n// such files never match a later -c/-r resume.\nlet boundSessionId: string | null = null;\n\nexport function setTodoSession(id: string | null): void {\n boundSessionId = id;\n}\n\nexport class TodoStore {\n private items = new Map<number, TodoItem>();\n\n list(): TodoItem[] {\n return [...this.items.values()];\n }\n\n replace(items: TodoItem[]): void {\n this.items.clear();\n for (const item of items) {\n this.items.set(item.id, item);\n }\n }\n\n async load(cwd: string, sessionId?: string): Promise<void> {\n let raw: string;\n try {\n raw = await readFile(fileFor(cwd), \"utf8\");\n } catch {\n return;\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n if (Array.isArray(parsed)) {\n // Legacy pre-upgrade format (a bare array): adopt it so existing\n // todos survive the upgrade — the next save stamps the current\n // session id.\n this.replace(parsed.filter((it) => todoItemSchema.safeParse(it).success));\n return;\n }\n const file = fileSchema.safeParse(parsed);\n if (!file.success) {\n return;\n }\n if (file.data.sessionId !== (sessionId ?? null)) {\n // Another session owns this list: drop any stale in-memory items so\n // they can't leak into the resumed session, but leave the file alone.\n this.replace([]);\n return;\n }\n this.replace(file.data.items);\n } catch {\n // corrupted file: keep current state\n }\n }\n\n // Atomic write (tmp + rename): a crash or failed save can never leave a\n // truncated todos.json behind. An empty or fully completed list has no\n // resume value — rehydrating it would only restore finished work — so the\n // file is deleted instead of persisted.\n async save(cwd: string): Promise<void> {\n if (!persistGuard()) {\n return;\n }\n const items = this.list();\n const file = fileFor(cwd);\n if (items.length === 0 || items.every((it) => it.status === \"done\")) {\n await rm(file, { force: true });\n return;\n }\n await mkdir(path.join(cwd, \".star\"), { recursive: true });\n const tmp = `${file}.tmp`;\n await writeFile(tmp, `${JSON.stringify({ sessionId: boundSessionId, items }, null, 2)}\\n`);\n await rename(tmp, file);\n }\n}\n\nconst SYMBOLS: Record<TodoItem[\"status\"], string> = {\n pending: \"[ ]\",\n in_progress: \"[~]\",\n done: \"[x]\",\n};\n\nexport function formatTodos(items: TodoItem[]): string {\n if (items.length === 0) {\n return \"No todos.\";\n }\n const lines = items.map((it) => `${SYMBOLS[it.status]} ${it.id}. ${it.title}`);\n const width = Math.max(...lines.map((l) => l.length));\n return lines\n .map((line, i) => {\n const status = items[i]?.status;\n return status === \"pending\" ? line : `${line.padEnd(width + 4)}(${status})`;\n })\n .join(\"\\n\");\n}\n\nconst defaultStore = new TodoStore();\n\n// Full parsed list from a todo_write args payload, or null when malformed.\n// The REPL uses this to mirror the todo list next to the input box.\nexport function parseTodoArgs(args: unknown): TodoItem[] | null {\n const parsed = writeSchema.safeParse(args);\n return parsed.success ? parsed.data.todos : null;\n}\n\n// Titles of unfinished items from a todo_write args payload; [] when the\n// payload is malformed. Used by the agent loop to spot turns that end while\n// the model's own todo list still has open work.\nexport function pendingTodoTitles(args: unknown): string[] {\n return (parseTodoArgs(args) ?? []).filter((t) => t.status !== \"done\").map((t) => t.title);\n}\n\n// Load the persisted todo list for a cwd into the default store and return\n// it. Called only when the user explicitly continues a project (session\n// resume) — a fresh session never rehydrates stale todos on its own, and a\n// file owned by a different session id leaves the store empty.\nexport async function loadTodos(cwd: string, sessionId?: string): Promise<TodoItem[]> {\n await defaultStore.load(cwd, sessionId);\n return defaultStore.list();\n}\n\n// Clear the in-memory list without touching .star/todos.json — a new session\n// starts clean even when a previous session's list is still on disk.\nexport function resetTodos(): void {\n defaultStore.replace([]);\n}\n\nexport function createTodoTools(store: TodoStore = defaultStore): Tool[] {\n const todoWrite: Tool<typeof writeSchema> = {\n name: \"todo_write\",\n description:\n \"Replace the current todo list with the given items. Each item has an id, title, and status (pending, in_progress, done).\",\n permission: \"read\",\n parameters: writeSchema,\n async execute(args, ctx) {\n const parsed = writeSchema.safeParse(args);\n if (!parsed.success) {\n return { content: `Invalid todos: ${parsed.error.message}`, isError: true };\n }\n // Roll the in-memory list back when the save fails so memory and disk\n // never diverge silently.\n const previous = store.list();\n store.replace(parsed.data.todos);\n try {\n await store.save(ctx.cwd);\n } catch (err) {\n store.replace(previous);\n return { content: `Failed to save todos: ${(err as Error).message}`, isError: true };\n }\n const items = store.list();\n const done = items.filter((it) => it.status === \"done\").length;\n // The caller just supplied the list and the REPL mirrors it live, so a\n // compact confirmation is enough — echoing the whole list back would be\n // re-billed as prompt tokens on every following request.\n return { content: `Todo list updated (${done}/${items.length} done).` };\n },\n };\n\n const todoRead: Tool<typeof readSchema> = {\n name: \"todo_read\",\n description: \"Read the current todo list.\",\n permission: \"read\",\n parameters: readSchema,\n async execute() {\n // In-memory only: disk is hydrated explicitly on session resume, so a\n // fresh session's todo_read never resurrects the previous list.\n return { content: formatTodos(store.list()) };\n },\n };\n\n return [todoWrite, todoRead];\n}\n","import { execFile } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { readFile, unlink, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { MAX_IMAGE_DIMENSION, probeImageDimensions } from \"./image\";\nimport type { ImageInput } from \"./messages\";\n\nexport const RESIZE_TIMEOUT_MS = 10_000;\n\nconst execFileAsync = promisify(execFile);\n\nexport interface ResizeCommand {\n command: string;\n args: string[];\n}\n\nexport function windowsResizeScript(\n inputFile: string,\n outputFile: string,\n maxDimension: number,\n): string {\n const input = inputFile.replace(/'/g, \"''\");\n const output = outputFile.replace(/'/g, \"''\");\n return [\n \"Add-Type -AssemblyName System.Drawing\",\n `$img = [System.Drawing.Image]::FromFile('${input}')`,\n `$scale = [Math]::Min(1.0, ${maxDimension} / [Math]::Max($img.Width, $img.Height))`,\n \"$w = [int][Math]::Max(1, [Math]::Round($img.Width * $scale))\",\n \"$h = [int][Math]::Max(1, [Math]::Round($img.Height * $scale))\",\n \"$bmp = New-Object System.Drawing.Bitmap($w, $h)\",\n \"$g = [System.Drawing.Graphics]::FromImage($bmp)\",\n \"$g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic\",\n \"$g.DrawImage($img, 0, 0, $w, $h)\",\n `$bmp.Save('${output}', [System.Drawing.Imaging.ImageFormat]::Png)`,\n \"$g.Dispose(); $bmp.Dispose(); $img.Dispose()\",\n ].join(\"; \");\n}\n\n// Platform-native resize command, or null when no tool is available. All\n// variants keep the aspect ratio and bound the longest side to maxDimension;\n// the output is always PNG.\nexport function resizeImagePlan(\n platform: NodeJS.Platform,\n inputFile: string,\n outputFile: string,\n maxDimension: number,\n tools: { hasConvert?: boolean; hasFfmpeg?: boolean } = {},\n): ResizeCommand | null {\n if (platform === \"win32\") {\n return {\n command: \"powershell.exe\",\n args: [\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-Command\",\n windowsResizeScript(inputFile, outputFile, maxDimension),\n ],\n };\n }\n if (platform === \"darwin\") {\n return {\n command: \"sips\",\n args: [\"-Z\", String(maxDimension), inputFile, \"--out\", outputFile],\n };\n }\n if (platform === \"linux\") {\n if (tools.hasConvert) {\n return {\n command: \"convert\",\n // -auto-orient keeps EXIF-rotated photos upright after the resize\n // strips their orientation metadata.\n args: [\n inputFile,\n \"-auto-orient\",\n \"-resize\",\n `${maxDimension}x${maxDimension}>`,\n outputFile,\n ],\n };\n }\n if (tools.hasFfmpeg) {\n return {\n command: \"ffmpeg\",\n args: [\n \"-y\",\n \"-i\",\n inputFile,\n \"-vf\",\n `scale='if(gt(iw,ih),min(iw,${maxDimension}),-1)':'if(gt(iw,ih),-1,min(ih,${maxDimension}))'`,\n outputFile,\n ],\n };\n }\n return null;\n }\n return null;\n}\n\nasync function commandExists(command: string): Promise<boolean> {\n const checker = process.platform === \"win32\" ? \"where\" : \"which\";\n try {\n await execFileAsync(checker, [command], { timeout: RESIZE_TIMEOUT_MS });\n return true;\n } catch {\n return false;\n }\n}\n\nexport interface DownsampledImage {\n image: ImageInput;\n /** The source exceeded MAX_IMAGE_DIMENSION on its longest side. */\n oversized: boolean;\n /** The image was actually resized to fit; false when no platform tool was available or resizing failed. */\n resized: boolean;\n}\n\n// Downsamples an image whose longest side exceeds MAX_IMAGE_DIMENSION using\n// platform-native tools (no npm image dependency). Undimensionable or small\n// images pass through untouched; when resizing is impossible the original is\n// kept with oversized=true so the caller can warn instead of failing silently.\nexport async function downsampleImageIfNeeded(image: ImageInput): Promise<DownsampledImage> {\n const buf = Buffer.from(image.data, \"base64\");\n const dims = probeImageDimensions(buf);\n if (!dims || Math.max(dims.width, dims.height) <= MAX_IMAGE_DIMENSION) {\n return { image, oversized: false, resized: false };\n }\n const inputFile = path.join(os.tmpdir(), `star-image-${randomUUID()}.src`);\n const outputFile = path.join(os.tmpdir(), `star-image-${randomUUID()}.png`);\n try {\n const [hasConvert, hasFfmpeg] =\n process.platform === \"linux\"\n ? await Promise.all([commandExists(\"convert\"), commandExists(\"ffmpeg\")])\n : [false, false];\n const plan = resizeImagePlan(process.platform, inputFile, outputFile, MAX_IMAGE_DIMENSION, {\n hasConvert,\n hasFfmpeg,\n });\n if (!plan) return { image, oversized: true, resized: false };\n await writeFile(inputFile, buf);\n await execFileAsync(plan.command, plan.args, { timeout: RESIZE_TIMEOUT_MS });\n const out = await readFile(outputFile).catch(() => null);\n if (!out || out.length === 0) return { image, oversized: true, resized: false };\n return {\n image: { path: image.path, mimeType: \"image/png\", data: out.toString(\"base64\") },\n oversized: true,\n resized: true,\n };\n } catch {\n return { image, oversized: true, resized: false };\n } finally {\n await unlink(inputFile).catch(() => {});\n await unlink(outputFile).catch(() => {});\n }\n}\n","const SENSITIVE_BASENAMES = new Set([\n \".env\",\n \"id_rsa\",\n \"id_dsa\",\n \"id_ecdsa\",\n \"id_ed25519\",\n \".npmrc\",\n \".netrc\",\n \".pgpass\",\n]);\n\nconst SENSITIVE_SUFFIXES = [\".pem\", \".key\", \".p12\", \".pfx\"];\n\nconst SENSITIVE_ENV_EXEMPTIONS = new Set([\".env.example\", \".env.sample\", \".env.template\"]);\n\n// Additional likely-secret globs from the global config ([permissions]\n// sensitive = [...]). The set is replaced wholesale on every config load, so\n// an entry removed from the config stops protecting, and a project config\n// can never add one (its [permissions] table is sandboxed away). Matching\n// uses the same single-* glob semantics as permission rules, case-insensitively,\n// against both the basename and the full normalized path (\"/\" separators).\nconst extraPatterns: string[] = [];\n\nexport function registerSensitivePatterns(patterns: readonly string[]): void {\n extraPatterns.length = 0;\n extraPatterns.push(...patterns.map((p) => p.trim()).filter((p) => p.length > 0));\n}\n\nfunction globToRegExp(pattern: string): RegExp {\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\*/g, \".*\");\n return new RegExp(`^${escaped}$`, \"i\");\n}\n\nfunction pathSegments(p: string): string[] {\n return p\n .replace(/\\\\/g, \"/\")\n .split(\"/\")\n .filter(Boolean)\n .map((s) => s.toLowerCase());\n}\n\n// Single source of truth for \"this file likely holds secrets, don't let the\n// model read it into context or overwrite it\". Matches on the basename, with\n// two directory-aware rules for well-known credential locations. Templates\n// and example env files are explicitly safe unless a configured glob says\n// otherwise.\nexport function isSensitivePath(p: string): boolean {\n const segments = pathSegments(p);\n const base = segments[segments.length - 1] ?? \"\";\n const normalized = p.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n for (const pattern of extraPatterns) {\n const re = globToRegExp(pattern);\n if (re.test(normalized) || re.test(base)) {\n return true;\n }\n }\n if (SENSITIVE_ENV_EXEMPTIONS.has(base)) {\n return false;\n }\n if (base === \".env\" || base.startsWith(\".env.\")) {\n return true;\n }\n if (SENSITIVE_BASENAMES.has(base)) {\n return true;\n }\n if (SENSITIVE_SUFFIXES.some((suffix) => base.endsWith(suffix))) {\n return true;\n }\n const dir = segments[segments.length - 2] ?? \"\";\n if (dir === \".aws\" && base === \"credentials\") {\n return true;\n }\n if (dir === \".kube\" && base === \"config\") {\n return true;\n }\n return false;\n}\n","import { readFileSync, statSync } from \"node:fs\";\nimport { readdir, realpath, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport const SKIP_DIRS = new Set([\"node_modules\", \".git\", \"dist\"]);\n\n// Concurrent readdir/stat lanes during a directory walk; large trees are\n// latency-bound on syscalls, so siblings are explored in parallel.\nexport const WALK_CONCURRENCY = 16;\n\nexport interface WalkedFile {\n abs: string;\n rel: string;\n mtimeMs: number;\n // Realpath of the target when the entry was reached through a symlink or\n // junction; lets callers (grep) apply sensitive-path checks to the target\n // too. Undefined for plain files.\n resolved?: string;\n}\n\nexport type IgnorePredicate = (absPath: string, isDir: boolean) => boolean;\n\nexport interface WalkOptions {\n ignore?: IgnorePredicate;\n // Defaults to true. Pass false when the caller does not sort by mtime (e.g.\n // grep) to skip one stat syscall per file; mtimeMs is then reported as 0.\n withMtime?: boolean;\n // Checked before each directory read and inside the entry loop. On abort\n // the walk stops dispatching new work and returns partial results with\n // `aborted: true`.\n signal?: AbortSignal;\n // Symlink targets are followed only while their realpath stays inside this\n // directory (typically the cwd the permission gate approved); targets\n // resolving outside are skipped so a planted link cannot make a walk leak\n // files the gate would have asked about or denied. Omit to follow links\n // with cycle protection only.\n symlinkBoundary?: string;\n // Load nested .starignore files as the walk enters each directory; their\n // patterns apply gitignore-style to that directory's subtree, resolved\n // relative to the directory holding the file. Presence is detected from\n // the parent's readdir listing, so directories without one cost no extra\n // syscall. Inside a symlinked subtree the rules anchor at the link's walk\n // position (paths stay lexical; the file content comes from the target).\n nestedIgnore?: boolean;\n}\n\nexport interface WalkResult {\n files: WalkedFile[];\n aborted: boolean;\n}\n\nfunction createLimiter(concurrency: number) {\n let active = 0;\n const waiting: Array<() => void> = [];\n return async function limit<T>(fn: () => Promise<T>): Promise<T> {\n while (active >= concurrency) {\n await new Promise<void>((resolve) => waiting.push(resolve));\n }\n active += 1;\n try {\n return await fn();\n } finally {\n active -= 1;\n waiting.shift()?.();\n }\n };\n}\n\n// Patterns from one nested .starignore, anchored at the directory holding it.\n// relBase is that directory's walk-relative path (\"\" at the walk root) so\n// entries relativize with a slice instead of path.relative per pattern.\ninterface IgnoreLayer {\n relBase: string;\n patterns: IgnorePattern[];\n}\n\nfunction layerIgnores(layers: IgnoreLayer[], rel: string, isDir: boolean): boolean {\n for (const layer of layers) {\n // A layer only governs its own subtree; entries outside it skip the\n // layer instead of slicing into a bogus relative path.\n const local =\n layer.relBase === \"\"\n ? rel\n : rel.startsWith(`${layer.relBase}/`)\n ? rel.slice(layer.relBase.length + 1)\n : null;\n if (local === null) {\n continue;\n }\n for (const { pattern, dirOnly } of layer.patterns) {\n if (dirOnly && !isDir) {\n continue;\n }\n if (matchesGlob(pattern, local)) {\n return true;\n }\n }\n }\n return false;\n}\n\nexport async function walkFiles(\n root: string,\n skipDirs: Set<string> = SKIP_DIRS,\n options: WalkOptions = {},\n): Promise<WalkResult> {\n const out: WalkedFile[] = [];\n const limit = createLimiter(WALK_CONCURRENCY);\n const signal = options.signal;\n const caseKey = (p: string) => (process.platform === \"win32\" ? p.toLowerCase() : p);\n // Realpaths of directories already entered. Only symlink targets can create\n // a cycle (A↔B, link to an ancestor), so real directories are not tracked.\n const visited = new Set<string>();\n const rootReal = await realpath(root).catch(() => null);\n if (rootReal !== null) {\n visited.add(caseKey(rootReal));\n }\n // Resolved lazily on the first symlink so trees without links pay nothing.\n let boundaryReal: string | null | undefined;\n async function resolveLink(abs: string): Promise<string | null> {\n const real = await limit(() => realpath(abs)).catch(() => null);\n if (real === null) {\n return null;\n }\n const boundary = options.symlinkBoundary;\n if (boundary === undefined) {\n return real;\n }\n if (boundaryReal === undefined) {\n boundaryReal = await limit(() => realpath(boundary)).catch(() => null);\n }\n if (boundaryReal === null) {\n // The approved root cannot be resolved: fail closed, like the walk did\n // before links were followed at all.\n return null;\n }\n const realKey = caseKey(real);\n const boundaryKey = caseKey(boundaryReal);\n return realKey === boundaryKey || realKey.startsWith(`${boundaryKey}${path.sep}`) ? real : null;\n }\n\n async function walkDir(dir: string, relBase: string, parentLayers: IgnoreLayer[]): Promise<void> {\n if (signal?.aborted) {\n return;\n }\n const entries = await limit(() => readdir(dir, { withFileTypes: true })).catch(() => null);\n if (!entries) {\n return;\n }\n let layers = parentLayers;\n if (options.nestedIgnore && entries.some((e) => e.name === IGNORE_FILE)) {\n const patterns = loadIgnorePatterns(dir);\n if (patterns.length > 0) {\n layers = [...parentLayers, { relBase, patterns }];\n }\n }\n const subs: Promise<void>[] = [];\n for (const entry of entries) {\n if (signal?.aborted) {\n break;\n }\n const abs = path.join(dir, entry.name);\n const rel = relBase ? `${relBase}/${entry.name}` : entry.name;\n // For a symlink the Dirent describes the link itself; stat() follows to\n // the target. targetStat is reused for the mtime below.\n let targetStat: Awaited<ReturnType<typeof stat>> | null = null;\n let resolved: string | undefined;\n if (entry.isSymbolicLink()) {\n targetStat = await limit(() => stat(abs)).catch(() => null);\n if (!targetStat || (!targetStat.isDirectory() && !targetStat.isFile())) {\n continue;\n }\n const real = await resolveLink(abs);\n if (real === null) {\n continue;\n }\n resolved = real;\n if (targetStat.isDirectory()) {\n const key = caseKey(real);\n if (visited.has(key)) {\n continue;\n }\n visited.add(key);\n }\n }\n const isDir = entry.isDirectory() || (targetStat?.isDirectory() ?? false);\n const isFile = entry.isFile() || (targetStat?.isFile() ?? false);\n if (isDir) {\n if (skipDirs.has(entry.name) || options.ignore?.(abs, true)) {\n continue;\n }\n if (layers.length > 0 && layerIgnores(layers, rel, true)) {\n continue;\n }\n subs.push(walkDir(abs, rel, layers));\n } else if (isFile) {\n if (options.ignore?.(abs, false)) {\n continue;\n }\n if (layers.length > 0 && layerIgnores(layers, rel, false)) {\n continue;\n }\n if (options.withMtime === false) {\n out.push(\n resolved === undefined ? { abs, rel, mtimeMs: 0 } : { abs, rel, mtimeMs: 0, resolved },\n );\n continue;\n }\n const st = targetStat ?? (await limit(() => stat(abs)).catch(() => null));\n if (st) {\n out.push(\n resolved === undefined\n ? { abs, rel, mtimeMs: st.mtimeMs }\n : { abs, rel, mtimeMs: st.mtimeMs, resolved },\n );\n }\n }\n }\n await Promise.all(subs);\n }\n await walkDir(root, \"\", []);\n // Concurrent pushes arrive in nondeterministic order; sort so grep's match\n // order and truncation cut are stable from run to run.\n out.sort((a, b) => (a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0));\n return { files: out, aborted: signal?.aborted ?? false };\n}\n\nconst REGEX_SPECIALS = /[.+^${}()|[\\]\\\\]/g;\n\nfunction escapeRe(c: string): string {\n return c.replace(REGEX_SPECIALS, \"\\\\$&\");\n}\n\n// Supports *, **, ?, {a,b} braces (nested one level) and [abc] / [!abc]\n// character classes. Consecutive `**/` segments are collapsed into a single\n// (?:[^/]+/)* so adjacent repetitions cannot produce exponential backtracking.\nexport function globToRegExp(pattern: string): RegExp {\n let i = 0;\n\n function parseClass(): string {\n // i points at \"[\"\n let j = i + 1;\n let negate = false;\n if (pattern.charAt(j) === \"!\") {\n negate = true;\n j += 1;\n }\n let body = \"\";\n // a \"]\" right after \"[\" or \"[!\" is a literal member of the class\n if (pattern.charAt(j) === \"]\") {\n body += \"\\\\]\";\n j += 1;\n }\n while (j < pattern.length && pattern.charAt(j) !== \"]\") {\n const ch = pattern.charAt(j);\n body += ch === \"\\\\\" ? \"\\\\\\\\\" : ch;\n j += 1;\n }\n if (j < pattern.length) {\n i = j + 1;\n // a leading ^ would negate the regex class; escape it\n if (body.startsWith(\"^\")) body = `\\\\${body}`;\n return `[${negate ? \"^\" : \"\"}${body}]`;\n }\n // unterminated \"[\": match it literally\n i += 1;\n return \"\\\\[\";\n }\n\n function parseSegment(depth: number): { re: string; end: string } {\n let re = \"\";\n while (i < pattern.length) {\n const c = pattern.charAt(i);\n if (depth > 0 && (c === \",\" || c === \"}\")) {\n return { re, end: c };\n }\n if (c === \"*\") {\n if (pattern.charAt(i + 1) === \"*\") {\n i += 2;\n if (pattern.charAt(i) === \"/\") {\n i += 1;\n re += \"(?:[^/]+/)*\";\n while (pattern.startsWith(\"**/\", i)) {\n i += 3;\n }\n } else {\n re += \".*\";\n }\n } else {\n re += \"[^/]*\";\n i += 1;\n }\n continue;\n }\n if (c === \"?\") {\n re += \"[^/]\";\n i += 1;\n continue;\n }\n if (c === \"[\") {\n re += parseClass();\n continue;\n }\n if (c === \"{\" && depth < 2) {\n const close = braceEnd(i);\n if (close === -1) {\n re += \"\\\\{\";\n i += 1;\n continue;\n }\n i += 1;\n const alts: string[] = [];\n for (;;) {\n const part = parseSegment(depth + 1);\n alts.push(part.re);\n if (part.end !== \",\") break;\n i += 1;\n }\n i += 1; // consume \"}\"\n re += `(?:${alts.join(\"|\")})`;\n continue;\n }\n re += escapeRe(c);\n i += 1;\n }\n return { re, end: \"\" };\n }\n\n function braceEnd(from: number): number {\n let depth = 0;\n for (let j = from; j < pattern.length; j++) {\n const ch = pattern.charAt(j);\n if (ch === \"{\") depth += 1;\n else if (ch === \"}\") {\n depth -= 1;\n if (depth === 0) return j;\n }\n }\n return -1;\n }\n\n const { re } = parseSegment(0);\n return new RegExp(`^${re}$`);\n}\n\nexport function matchesGlob(pattern: string, relPath: string): boolean {\n const normalized = pattern.replace(/\\\\/g, \"/\");\n if (!normalized.includes(\"/\")) {\n const base = relPath.split(\"/\").pop() ?? relPath;\n return globToRegExp(normalized).test(base);\n }\n return globToRegExp(normalized).test(relPath);\n}\n\nexport const IGNORE_FILE = \".starignore\";\n\nexport interface IgnorePattern {\n pattern: string;\n dirOnly: boolean;\n}\n\nconst ignoreCache = new Map<string, { mtimeMs: number; patterns: IgnorePattern[] }>();\n\nfunction parseIgnorePatterns(body: string): IgnorePattern[] {\n const out: IgnorePattern[] = [];\n for (const raw of body.split(\"\\n\")) {\n const line = raw.trim();\n if (!line || line.startsWith(\"#\")) {\n continue;\n }\n if (line.endsWith(\"/\")) {\n const pattern = line.slice(0, -1);\n if (pattern) {\n out.push({ pattern, dirOnly: true });\n }\n } else {\n out.push({ pattern: line, dirOnly: false });\n }\n }\n return out;\n}\n\n// Reads <dir>/.starignore. Cached by file path and re-read only when the\n// mtime changes, mirroring readProjectMemory in agent/project-memory.ts.\nexport function loadIgnorePatterns(dir: string): IgnorePattern[] {\n const file = path.join(dir, IGNORE_FILE);\n let mtimeMs: number;\n try {\n mtimeMs = statSync(file).mtimeMs;\n } catch {\n return [];\n }\n const hit = ignoreCache.get(file);\n if (hit && hit.mtimeMs === mtimeMs) {\n return hit.patterns;\n }\n let patterns: IgnorePattern[] = [];\n try {\n patterns = parseIgnorePatterns(readFileSync(file, \"utf8\"));\n } catch {\n patterns = [];\n }\n ignoreCache.set(file, { mtimeMs, patterns });\n return patterns;\n}\n\n// Builds an ignore predicate from <cwd>/.starignore, or undefined when there\n// are no patterns so callers keep a zero-overhead path. Paths are matched\n// relative to cwd with forward slashes; \"!\" negation is not supported.\nexport function createIgnorePredicate(cwd: string): IgnorePredicate | undefined {\n const patterns = loadIgnorePatterns(cwd);\n if (patterns.length === 0) {\n return undefined;\n }\n const root = path.resolve(cwd);\n return (absPath, isDir) => {\n const rel = path.relative(root, absPath).split(path.sep).join(\"/\");\n if (!rel || rel.startsWith(\"..\")) {\n return false;\n }\n for (const { pattern, dirOnly } of patterns) {\n if (dirOnly && !isDir) {\n continue;\n }\n if (matchesGlob(pattern, rel)) {\n return true;\n }\n }\n return false;\n };\n}\n\nexport { isSensitivePath } from \"../../core/sensitive\";\n","import dns from \"node:dns\";\nimport { z } from \"zod\";\nimport type { Tool } from \"../types\";\n\nconst DEFAULT_MAX_CHARS = 20000;\nconst MAX_BYTES = 2 * 1024 * 1024;\nconst TIMEOUT_MS = 30000;\nconst MAX_REDIRECTS = 5;\nconst MAX_ANNOTATED_LINKS = 50;\nconst PDF_MAGIC = [0x25, 0x50, 0x44, 0x46]; // \"%PDF\"\n\nconst schema = z.object({\n url: z.string().describe(\"The http(s) URL to fetch\"),\n maxChars: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Maximum characters of text to return (default 20000)\"),\n});\n\n// Resolves a hostname to its IP addresses; injectable so tests (and embedders)\n// never touch real DNS. Defaults to the system resolver.\nexport type WebFetchResolver = (hostname: string) => Promise<string[]>;\n\nexport interface WebFetchToolOptions {\n resolver?: WebFetchResolver;\n fetchImpl?: typeof fetch;\n // Overrides the config-wired default (setWebFetchAllowPrivateHosts).\n allowPrivateHosts?: boolean;\n}\n\nconst defaultResolver: WebFetchResolver = async (hostname) => {\n const results = await dns.promises.lookup(hostname, { all: true });\n return results.map((result) => result.address);\n};\n\n// Wired from loadConfig (webFetchAllowPrivateHosts, global config only — the\n// project-config sandbox cannot set it). When false, every fetched URL's host\n// is resolved and private/reserved addresses are refused, per redirect hop.\nlet configuredAllowPrivateHosts = false;\n\nexport function setWebFetchAllowPrivateHosts(allow: boolean): void {\n configuredAllowPrivateHosts = allow;\n}\n\nfunction isPrivateIpv4(ip: string): boolean {\n const parts = ip.split(\".\").map(Number);\n if (parts.length !== 4 || parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) {\n return false;\n }\n const [a = 0, b = 0] = parts;\n return (\n a === 0 ||\n a === 10 ||\n a === 127 ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && b === 168) ||\n (a === 169 && b === 254)\n );\n}\n\nfunction isPrivateAddress(address: string): boolean {\n let ip = address.toLowerCase();\n if (ip.startsWith(\"::ffff:\")) ip = ip.slice(\"::ffff:\".length);\n if (ip.includes(\".\")) return isPrivateIpv4(ip);\n if (ip === \"::1\" || ip === \"::\") return true;\n // fc00::/7 (ULA) and fe80::/10 (link-local).\n if (ip.startsWith(\"fc\") || ip.startsWith(\"fd\")) return true;\n if (/^fe[89ab]/.test(ip)) return true;\n return false;\n}\n\nfunction isLocalHostname(hostname: string): boolean {\n const h = hostname.toLowerCase().replace(/\\.$/, \"\");\n return (\n h === \"localhost\" || h.endsWith(\".localhost\") || h.endsWith(\".local\") || h.endsWith(\".internal\")\n );\n}\n\n// Returns a refusal message when the URL's host is local/private/reserved,\n// null when it is safe to connect. DNS answers are checked too, so a public\n// hostname that resolves to 169.254.169.254 is refused like a literal IP.\nasync function privateHostRefusal(url: URL, resolve: WebFetchResolver): Promise<string | null> {\n const hostname = url.hostname.replace(/^\\[|\\]$/g, \"\");\n if (isLocalHostname(hostname)) {\n return `\"${hostname}\" is a local/internal hostname`;\n }\n let addresses: string[];\n try {\n addresses = await resolve(hostname);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return `could not resolve \"${hostname}\" (${message})`;\n }\n const blocked = addresses.find(isPrivateAddress);\n if (blocked !== undefined) {\n return `\"${hostname}\" resolves to a private/reserved address (${blocked})`;\n }\n if (addresses.length === 0) {\n return `could not resolve \"${hostname}\"`;\n }\n return null;\n}\n\n// String.fromCodePoint throws a RangeError on code points past U+10FFFF;\n// keep the original entity text for those instead of dying mid-page.\nfunction safeCodePoint(n: number, raw: string): string {\n if (!Number.isInteger(n) || n < 0 || n > 0x10ffff) {\n return raw;\n }\n return String.fromCodePoint(n);\n}\n\nexport function decodeEntities(s: string): string {\n return s\n .replace(/&#(\\d+);/g, (m, n) => safeCodePoint(Number(n), m))\n .replace(/&#x([0-9a-fA-F]+);/gi, (m, n) => safeCodePoint(Number.parseInt(n, 16), m))\n .replace(/&(quot|lt|gt|nbsp);/g, (m) => {\n switch (m) {\n case \"&quot;\":\n return '\"';\n case \"&lt;\":\n return \"<\";\n case \"&gt;\":\n return \">\";\n case \"&nbsp;\":\n return \" \";\n default:\n return m;\n }\n })\n .replace(/&amp;/g, \"&\");\n}\n\n// Link URLs are kept inline as \"anchor text (absolute-url)\" so the model can\n// follow them; resolved against the page base, capped so a link-heavy page\n// cannot drown the text in URLs. Anchor text is returned raw (entities\n// intact) — the pipeline's single decodeEntities pass decodes it later.\nfunction annotateLinks(html: string, base: string | undefined): string {\n let annotated = 0;\n return html.replace(\n /<a\\b[^>]*?\\shref\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')[^>]*>([\\s\\S]*?)<\\/a\\s*>/gi,\n (_match, doubleQuoted, singleQuoted, inner) => {\n const rawText = String(inner).replace(/<[^>]+>/g, \" \");\n const href = decodeEntities(String(doubleQuoted ?? singleQuoted ?? \"\")).trim();\n if (annotated >= MAX_ANNOTATED_LINKS || href === \"\" || href.startsWith(\"#\")) {\n return rawText;\n }\n let resolved: URL;\n try {\n resolved = new URL(href, base);\n } catch {\n return rawText;\n }\n if (resolved.protocol !== \"http:\" && resolved.protocol !== \"https:\") {\n return rawText;\n }\n const text = decodeEntities(rawText).replace(/\\s+/g, \" \").trim();\n if (text === \"\") {\n annotated += 1;\n return resolved.href;\n }\n if (text === resolved.href || text === href) {\n return rawText;\n }\n annotated += 1;\n return `${rawText} (${resolved.href})`;\n },\n );\n}\n\nfunction htmlToText(html: string, base?: string): string {\n const noBlocks = html.replace(/<(script|style|noscript|template)\\b[^>]*>[\\s\\S]*?<\\/\\1>/gi, \" \");\n const withLinks = annotateLinks(noBlocks, base);\n const withBreaks = withLinks.replace(\n /<br\\s*\\/?>|<\\/(p|div|li|tr|h[1-6]|section|article|header|footer|blockquote|pre)>/gi,\n \"\\n\",\n );\n const stripped = decodeEntities(withBreaks.replace(/<[^>]+>/g, \" \"));\n return stripped\n .split(\"\\n\")\n .map((line) => line.replace(/\\s+/g, \" \").trim())\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\n// content-type may declare a charset (gbk, shift_jis, big5, euc-kr, ...);\n// Node ships full-icu so TextDecoder covers the WHATWG label set. A missing\n// or unknown label falls back to utf-8 — a bad charset must never fail the\n// fetch.\nfunction charsetDecoder(contentTypeHeader: string): InstanceType<typeof TextDecoder> {\n const match = contentTypeHeader.match(/charset\\s*=\\s*\"?([^\\s\";]+)\"?/i);\n const label = match?.[1]?.toLowerCase() ?? \"utf-8\";\n if (label === \"utf-8\" || label === \"utf8\") {\n return new TextDecoder(\"utf-8\");\n }\n try {\n return new TextDecoder(label);\n } catch {\n return new TextDecoder(\"utf-8\");\n }\n}\n\nasync function readBody(\n res: Response,\n maxChars: number,\n decoder: InstanceType<typeof TextDecoder>,\n): Promise<string> {\n if (!res.body) {\n return \"\";\n }\n const reader = res.body.getReader();\n let bytes = 0;\n let text = \"\";\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n bytes += value.byteLength;\n text += decoder.decode(value, { stream: true });\n if (bytes >= MAX_BYTES || text.length >= maxChars * 2) {\n break;\n }\n }\n text += decoder.decode();\n } finally {\n reader.cancel().catch(() => {});\n }\n return text;\n}\n\n// Reads at most n bytes from the response (then cancels) for magic-byte\n// sniffing of bodies we would otherwise reject unread.\nasync function readPrefix(res: Response, n: number): Promise<Uint8Array> {\n if (!res.body) {\n return new Uint8Array(0);\n }\n const reader = res.body.getReader();\n const out = new Uint8Array(n);\n let total = 0;\n try {\n while (total < n) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n const take = Math.min(value.byteLength, n - total);\n out.set(value.subarray(0, take), total);\n total += take;\n }\n } finally {\n reader.cancel().catch(() => {});\n }\n return out.subarray(0, total);\n}\n\nfunction isPdfPrefix(bytes: Uint8Array): boolean {\n return PDF_MAGIC.every((b, i) => bytes[i] === b);\n}\n\n// Tool-result error (not an exception): the model reads this and switches\n// strategy instead of retrying the fetch.\nfunction pdfError(url: URL): { content: string; isError: true } {\n return {\n content: `ERROR: PDF is not supported by web_fetch: ${url.href} is a PDF document. Do not retry this URL — use an HTML alternative (landing page, abstract, or HTML version) or a different source instead.`,\n isError: true,\n };\n}\n\nfunction truncate(s: string, maxChars: number): string {\n if (s.length <= maxChars) {\n return s;\n }\n return `${s.slice(0, maxChars)}\\n[truncated, showing first ${maxChars} of ${s.length} chars]`;\n}\n\nexport function createWebFetchTool(options: WebFetchToolOptions = {}): Tool<typeof schema> {\n const resolve = options.resolver ?? defaultResolver;\n const fetchImpl = options.fetchImpl ?? ((...args: Parameters<typeof fetch>) => fetch(...args));\n const allowPrivate = () => options.allowPrivateHosts ?? configuredAllowPrivateHosts;\n return {\n name: \"web_fetch\",\n description:\n \"Fetch a URL over http(s) and return its contents as readable text. HTML pages are converted to plain text (tags, scripts and styles removed; links kept inline as `anchor text (absolute-url)`). The charset declared in the response content-type is honored (GBK, Shift_JIS, Big5, etc.). PDF documents are not supported and return a clear error — do not retry them with this tool. Output is truncated to maxChars (default 20000). Private/reserved addresses (loopback, RFC1918, link-local incl. 169.254.169.254, localhost/.local/.internal names) are refused by default, on redirect hops too — the global config key webFetchAllowPrivateHosts lifts that for trusted intranet use.\",\n permission: \"read\",\n parameters: schema,\n async execute(args, ctx) {\n let url: URL;\n try {\n url = new URL(args.url);\n } catch {\n return { content: `ERROR: invalid URL: ${args.url}`, isError: true };\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n return {\n content: `ERROR: only http(s) URLs are supported, got: ${url.protocol}`,\n isError: true,\n };\n }\n if (!allowPrivate()) {\n const refusal = await privateHostRefusal(url, resolve);\n if (refusal !== null) {\n return { content: `ERROR: refused to fetch ${args.url}: ${refusal}`, isError: true };\n }\n }\n const maxChars = args.maxChars ?? DEFAULT_MAX_CHARS;\n const timeoutSignal = AbortSignal.timeout(TIMEOUT_MS);\n const signal = ctx.abortSignal\n ? AbortSignal.any([timeoutSignal, ctx.abortSignal])\n : timeoutSignal;\n // Redirects are followed manually so every hop's host is re-validated;\n // a public page must not bounce the tool into a metadata endpoint.\n let res: Response | undefined;\n let current = url;\n for (let hops = 0; ; hops += 1) {\n try {\n res = await fetchImpl(current, { signal, redirect: \"manual\" });\n } catch (err) {\n if (ctx.abortSignal?.aborted) {\n return { content: \"ERROR: request aborted\", isError: true };\n }\n if (timeoutSignal.aborted) {\n return {\n content: `ERROR: request timed out after ${TIMEOUT_MS / 1000}s`,\n isError: true,\n };\n }\n const message = err instanceof Error ? err.message : String(err);\n return { content: `ERROR: failed to fetch ${current.href}: ${message}`, isError: true };\n }\n const location = res.headers.get(\"location\");\n if (res.status >= 300 && res.status < 400 && location && hops < MAX_REDIRECTS) {\n await res.body?.cancel().catch(() => {});\n let next: URL;\n try {\n next = new URL(location, current);\n } catch {\n return { content: `ERROR: invalid redirect Location: ${location}`, isError: true };\n }\n if (next.protocol !== \"http:\" && next.protocol !== \"https:\") {\n return {\n content: `ERROR: redirect to non-http(s) URL refused: ${next.protocol}`,\n isError: true,\n };\n }\n if (!allowPrivate()) {\n const refusal = await privateHostRefusal(next, resolve);\n if (refusal !== null) {\n return {\n content: `ERROR: refused redirect to ${next.href}: ${refusal}`,\n isError: true,\n };\n }\n }\n current = next;\n continue;\n }\n break;\n }\n if (!res.ok) {\n await res.body?.cancel().catch(() => {});\n return {\n content: `ERROR: HTTP ${res.status} ${res.statusText} for ${current.href}`,\n isError: true,\n };\n }\n const contentTypeHeader = res.headers.get(\"content-type\") ?? \"text/plain\";\n const [rawType = \"\"] = contentTypeHeader.split(\";\");\n const contentType = rawType.trim().toLowerCase();\n const isHtml = contentType === \"text/html\" || contentType === \"application/xhtml+xml\";\n const isText =\n contentType.startsWith(\"text/\") ||\n contentType === \"application/json\" ||\n contentType === \"application/xml\" ||\n contentType.endsWith(\"+json\") ||\n contentType.endsWith(\"+xml\");\n if (contentType === \"application/pdf\") {\n await res.body?.cancel().catch(() => {});\n return pdfError(current);\n }\n if (!isHtml && !isText) {\n // Some servers mislabel PDFs; sniff the magic bytes so the model gets\n // the actionable PDF error instead of a generic type rejection.\n const prefix = await readPrefix(res, PDF_MAGIC.length);\n if (isPdfPrefix(prefix)) {\n return pdfError(current);\n }\n return {\n content: `ERROR: unsupported content type: ${contentType} (${current.href})`,\n isError: true,\n };\n }\n let body: string;\n try {\n body = await readBody(res, maxChars, charsetDecoder(contentTypeHeader));\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { content: `ERROR: failed to read response body: ${message}`, isError: true };\n }\n if (body.startsWith(\"%PDF\")) {\n return pdfError(current);\n }\n const text = isHtml ? htmlToText(body, current.href) : body;\n return { content: truncate(text, maxChars) };\n },\n };\n}\n\nexport const webFetchTool: Tool<typeof schema> = createWebFetchTool();\n"],"mappings":";;;AAAA,SAAS,oBAAoB;AA6B7B,IAAM,uBAAuB;AAOtB,IAAM,mBAAN,cAA+B,aAAa;AAAA,EACzC,UAAU,oBAAI,IAA6B;AAAA,EAC3C,UAAU;AAAA,EAElB,MACE,KACA,MACmB;AACnB,UAAM,KAAK,SAAS,EAAE,KAAK,OAAO;AAClC,UAAM,MAAuB;AAAA,MAC3B;AAAA,MACA,QAAQ,KAAK;AAAA,MACb,aAAa,KAAK;AAAA,MAClB,QAAQ;AAAA,MACR,WAAW,KAAK,IAAI;AAAA,MACpB,QAAQ;AAAA,MACR,YAAY,IAAI,gBAAgB;AAAA,MAChC,UAAU;AAAA,IACZ;AACA,SAAK,QAAQ,IAAI,IAAI,GAAG;AACxB,SAAK,IAAI,IAAI,WAAW,MAAM,EAC3B,KAAK,CAAC,WAAW,KAAK,OAAO,KAAK,aAAa,MAAM,CAAC,EACtD;AAAA,MAAM,CAAC,UACN,KAAK,OAAO,KAAK,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,IACnF;AACF,SAAK,WAAW,GAAG;AACnB,WAAO,KAAK,SAAS,GAAG;AAAA,EAC1B;AAAA,EAEA,IAAI,IAA2C;AAC7C,UAAM,MAAM,KAAK,QAAQ,IAAI,EAAE;AAC/B,WAAO,MAAM,KAAK,SAAS,GAAG,IAAI;AAAA,EACpC;AAAA,EAEA,OAA4B;AAC1B,WAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,EAAE,IAAI,CAAC,QAAQ,KAAK,SAAS,GAAG,CAAC;AAAA,EACnE;AAAA,EAEA,eAAuB;AACrB,QAAI,QAAQ;AACZ,eAAW,OAAO,KAAK,QAAQ,OAAO,GAAG;AACvC,UAAI,IAAI,WAAW,UAAW,UAAS;AAAA,IACzC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,KAAK,IAA2C;AAC9C,UAAM,MAAM,KAAK,QAAQ,IAAI,EAAE;AAC/B,QAAI,CAAC,OAAO,IAAI,WAAW,UAAW,QAAO;AAC7C,QAAI,WAAW,MAAM;AACrB,SAAK,OAAO,KAAK,WAAW,IAAI,UAAU,kBAAkB;AAC5D,WAAO,KAAK,SAAS,GAAG;AAAA,EAC1B;AAAA,EAEA,UAA+B;AAC7B,UAAM,SAA8B,CAAC;AACrC,eAAW,OAAO,KAAK,QAAQ,OAAO,GAAG;AACvC,UAAI,IAAI,WAAW,WAAW;AAC5B,YAAI,WAAW,MAAM;AACrB,aAAK,OAAO,KAAK,WAAW,IAAI,UAAU,sBAAsB;AAChE,eAAO,KAAK,KAAK,SAAS,GAAG,CAAC;AAAA,MAChC;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,qBAA0C;AACxC,UAAM,UAA+B,CAAC;AACtC,eAAW,OAAO,KAAK,QAAQ,OAAO,GAAG;AACvC,UAAI,IAAI,WAAW,aAAa,CAAC,IAAI,UAAU;AAC7C,YAAI,WAAW;AACf,gBAAQ,KAAK,KAAK,SAAS,GAAG,CAAC;AAAA,MACjC;AAAA,IACF;AACA,SAAK,cAAc;AACnB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC5B,QAAI,WAAW;AACf,eAAW,OAAO,KAAK,QAAQ,OAAO,GAAG;AACvC,UAAI,IAAI,WAAW,UAAW,aAAY;AAAA,IAC5C;AACA,QAAI,SAAS,WAAW;AACxB,QAAI,UAAU,EAAG;AACjB,eAAW,OAAO,KAAK,QAAQ,OAAO,GAAG;AACvC,UAAI,UAAU,EAAG;AACjB,UAAI,IAAI,WAAW,aAAa,IAAI,UAAU;AAC5C,aAAK,QAAQ,OAAO,IAAI,EAAE;AAC1B,kBAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,OAAO,KAAsB,QAAyB,QAAsB;AAClF,QAAI,IAAI,WAAW,UAAW;AAC9B,QAAI,SAAS;AACb,QAAI,SAAS;AACb,QAAI,UAAU,KAAK,IAAI;AACvB,SAAK,WAAW,GAAG;AAAA,EACrB;AAAA,EAEQ,WAAW,KAA4B;AAC7C,SAAK,KAAK,UAAU,KAAK,SAAS,GAAG,CAAC;AAAA,EACxC;AAAA,EAEQ,SAAS,KAAyC;AACxD,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,QAAQ,IAAI;AAAA,MACZ,aAAa,IAAI;AAAA,MACjB,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,MACf,SAAS,IAAI;AAAA,MACb,QAAQ,IAAI;AAAA,IACd;AAAA,EACF;AACF;AAEO,IAAM,oBAAoB,IAAI,iBAAiB;AAO/C,SAAS,sBAAsB,MAAiC;AACrE,QAAM,QAAQ,KAAK,cAAc,KAAK,KAAK,WAAW,KAAK;AAC3D,MAAI,KAAK,WAAW,WAAW;AAC7B,WAAO,uBAAuB,KAAK,EAAE,WAAW,KAAK;AAAA,EACvD;AACA,MAAI,OAAO,uBAAuB,KAAK,EAAE,IAAI,KAAK,MAAM,GAAG,KAAK;AAChE,MAAI,KAAK,WAAW,UAAU;AAC5B,UAAM,UAAU,KAAK,OAAO,MAAM,IAAI,EAAE,CAAC,KAAK,IAAI,QAAQ,wBAAwB,EAAE;AACpF,QAAI,QAAQ;AACV,cAAQ,WAAM,OAAO,SAAS,MAAM,GAAG,OAAO,MAAM,GAAG,GAAG,CAAC,WAAM,MAAM;AAAA,IACzE;AAAA,EACF;AACA,SAAO;AACT;;;AC/KO,SAAS,eAAe,OAAoC;AACjE,QAAM,OAAQ,MAAqC;AACnD,SAAO,OAAO,SAAS,YAAY,KAAK,SAAS,IAAI,OAAO;AAC9D;;;ACAO,IAAM,sBAAsB;AAE5B,IAAM,iCAAiC,IAAI,OAAO;AAClD,IAAM,4BAA4B;AAClC,IAAM,gCAAgC;AAO7C,IAAM,gBAAgB,CAAC,KAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,EAAI;AAErE,SAAS,SAAS,KAAqC;AACrD,MAAI,IAAI,SAAS,GAAI,QAAO;AAC5B,WAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,QAAI,IAAI,CAAC,MAAM,cAAc,CAAC,EAAG,QAAO;AAAA,EAC1C;AACA,MAAI,IAAI,SAAS,SAAS,IAAI,EAAE,MAAM,OAAQ,QAAO;AACrD,SAAO,EAAE,OAAO,IAAI,aAAa,EAAE,GAAG,QAAQ,IAAI,aAAa,EAAE,EAAE;AACrE;AAGA,IAAM,mBAAmB,oBAAI,IAAI;AAAA,EAC/B;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAC1E,CAAC;AAED,SAAS,UAAU,KAAqC;AACtD,MAAI,IAAI,SAAS,KAAK,IAAI,CAAC,MAAM,OAAQ,IAAI,CAAC,MAAM,IAAM,QAAO;AACjE,MAAI,SAAS;AACb,SAAO,SAAS,IAAI,IAAI,QAAQ;AAC9B,QAAI,IAAI,MAAM,MAAM,IAAM,QAAO;AACjC,UAAM,SAAS,IAAI,SAAS,CAAC;AAC7B,QAAI,WAAW,OAAW,QAAO;AACjC,QAAI,WAAW,OAAQ,WAAW,KAAS,UAAU,OAAQ,UAAU,KAAO;AAC5E,gBAAU;AACV;AAAA,IACF;AACA,UAAM,SAAS,IAAI,aAAa,SAAS,CAAC;AAC1C,QAAI,SAAS,EAAG,QAAO;AACvB,QAAI,iBAAiB,IAAI,MAAM,GAAG;AAChC,UAAI,SAAS,IAAI,IAAI,OAAQ,QAAO;AACpC,aAAO,EAAE,QAAQ,IAAI,aAAa,SAAS,CAAC,GAAG,OAAO,IAAI,aAAa,SAAS,CAAC,EAAE;AAAA,IACrF;AACA,cAAU,IAAI;AAAA,EAChB;AACA,SAAO;AACT;AAEA,SAAS,SAAS,KAAqC;AACrD,MAAI,IAAI,SAAS,GAAI,QAAO;AAC5B,QAAM,YAAY,IAAI,SAAS,SAAS,GAAG,CAAC;AAC5C,MAAI,cAAc,YAAY,cAAc,SAAU,QAAO;AAC7D,SAAO,EAAE,OAAO,IAAI,aAAa,CAAC,GAAG,QAAQ,IAAI,aAAa,CAAC,EAAE;AACnE;AAEA,SAAS,UAAU,KAAqC;AACtD,MAAI,IAAI,SAAS,GAAI,QAAO;AAC5B,MAAI,IAAI,SAAS,SAAS,GAAG,CAAC,MAAM,UAAU,IAAI,SAAS,SAAS,GAAG,EAAE,MAAM,QAAQ;AACrF,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,IAAI,SAAS,SAAS,IAAI,EAAE;AAC1C,MAAI,UAAU,QAAQ;AACpB,WAAO,EAAE,OAAO,IAAI,IAAI,WAAW,IAAI,CAAC,GAAG,QAAQ,IAAI,IAAI,WAAW,IAAI,CAAC,EAAE;AAAA,EAC/E;AACA,MAAI,UAAU,QAAQ;AACpB,WAAO;AAAA,MACL,OAAO,IAAI,aAAa,EAAE,IAAI;AAAA,MAC9B,QAAQ,IAAI,aAAa,EAAE,IAAI;AAAA,IACjC;AAAA,EACF;AACA,MAAI,UAAU,QAAQ;AACpB,QAAI,IAAI,EAAE,MAAM,GAAM,QAAO;AAC7B,UAAM,KAAK,IAAI,EAAE;AACjB,UAAM,KAAK,IAAI,EAAE;AACjB,UAAM,KAAK,IAAI,EAAE;AACjB,UAAM,KAAK,IAAI,EAAE;AACjB,WAAO;AAAA,MACL,OAAO,MAAO,KAAK,OAAS,IAAK;AAAA,MACjC,QAAQ,MAAO,KAAK,OAAS,KAAO,MAAM,KAAO,KAAK,QAAS;AAAA,IACjE;AAAA,EACF;AACA,SAAO;AACT;AAIO,SAAS,qBAAqB,KAAqC;AACxE,SAAO,SAAS,GAAG,KAAK,UAAU,GAAG,KAAK,SAAS,GAAG,KAAK,UAAU,GAAG;AAC1E;AAOA,IAAM,sBACJ;AAEK,SAAS,sBAAsB,OAAiC;AACrE,QAAM,SAAS,MAAM;AACrB,MAAI,WAAW,UAAa,SAAS,OAAO,UAAU,IAAK,QAAO;AAClE,QAAM,OAAO,CAAC,MAAM,SAAS,eAAe,KAAK,CAAC,EAC/C,OAAO,CAAC,SAAyB,OAAO,SAAS,QAAQ,EACzD,KAAK,IAAI;AACZ,SAAO,SAAS,KAAK,IAAI,KAAK,oBAAoB,KAAK,IAAI;AAC7D;AAOA,IAAM,8BACJ;AAEK,SAAS,yBAAyB,OAAiC;AACxE,QAAM,SAAS,MAAM;AACrB,MAAI,WAAW,UAAa,SAAS,OAAO,UAAU,IAAK,QAAO;AAClE,QAAM,OAAO,CAAC,MAAM,SAAS,eAAe,KAAK,CAAC,EAC/C,OAAO,CAAC,SAAyB,OAAO,SAAS,QAAQ,EACzD,KAAK,IAAI;AACZ,SAAO,2BAA2B,KAAK,IAAI,KAAK,4BAA4B,KAAK,IAAI;AACvF;AAEA,SAAS,eAAe,OAA+B;AACrD,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,SAAS,MAAM,WAAW,OAAO,IAAI,MAAM,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI;AACjF,QAAI;AACF,aAAO,OAAO,KAAK,QAAQ,QAAQ;AAAA,IACrC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,iBAAiB,WAAY,QAAO,OAAO,KAAK,KAAK;AACzD,SAAO;AACT;AAEO,SAAS,qBAAqB,KAAsB;AACzD,QAAM,OAAO,qBAAqB,GAAG;AACrC,MAAI,KAAM,QAAO,KAAK,IAAI,KAAK,OAAO,KAAK,MAAM,IAAI;AACrD,SAAO,IAAI,SAAS;AACtB;AAKO,SAAS,qBAAqB,UAGnC;AACA,MAAI,UAAU;AACd,QAAM,SAAS,SAAS,IAAI,CAAC,YAAY;AACvC,QAAI,CAAC,MAAM,QAAQ,QAAQ,OAAO,EAAG,QAAO;AAC5C,QAAI,UAAU;AACd,UAAM,UAAU,QAAQ,QAAQ,IAAI,CAAC,SAAS;AAC5C,UAAI,KAAK,SAAS,QAAS,QAAO;AAClC,YAAM,QAAQ,eAAe,KAAK,KAAK;AACvC,UAAI,CAAC,SAAS,CAAC,qBAAqB,KAAK,EAAG,QAAO;AACnD,gBAAU;AACV;AACA,aAAO,EAAE,MAAM,QAAiB,MAAM,0BAA0B;AAAA,IAClE,CAAC;AACD,WAAO,UAAW,EAAE,GAAG,SAAS,QAAQ,IAAoB;AAAA,EAC9D,CAAC;AACD,SAAO,EAAE,UAAU,UAAU,IAAI,SAAS,UAAU,QAAQ;AAC9D;AAKO,SAAS,eAAe,UAG7B;AACA,MAAI,UAAU;AACd,QAAM,SAAS,SAAS,IAAI,CAAC,YAAY;AACvC,QAAI,CAAC,MAAM,QAAQ,QAAQ,OAAO,EAAG,QAAO;AAC5C,QAAI,UAAU;AACd,UAAM,UAAU,QAAQ,QAAQ,IAAI,CAAC,SAAS;AAC5C,UAAI,KAAK,SAAS,QAAS,QAAO;AAClC,gBAAU;AACV;AACA,aAAO,EAAE,MAAM,QAAiB,MAAM,8BAA8B;AAAA,IACtE,CAAC;AACD,WAAO,UAAW,EAAE,GAAG,SAAS,QAAQ,IAAoB;AAAA,EAC9D,CAAC;AACD,SAAO,EAAE,UAAU,UAAU,IAAI,SAAS,UAAU,QAAQ;AAC9D;;;ACnMA,OAAO,QAAQ;AACf,OAAO,UAAU;AAEV,SAAS,WAAmB;AACjC,SAAO,QAAQ,IAAI,aAAa,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW;AACrE;AAEO,SAAS,mBAA2B;AACzC,SAAO,KAAK,KAAK,SAAS,GAAG,aAAa;AAC5C;AAEO,SAAS,cAAsB;AACpC,SAAO,KAAK,KAAK,SAAS,GAAG,UAAU;AACzC;AAEO,SAAS,iBAAyB;AACvC,SAAO,KAAK,KAAK,SAAS,GAAG,WAAW;AAC1C;AAKO,SAAS,cAAsB;AACpC,SAAO,KAAK,KAAK,SAAS,GAAG,WAAW;AAC1C;AAIO,SAAS,cAAsB;AACpC,SAAO,KAAK,KAAK,SAAS,GAAG,MAAM;AACrC;AAEO,SAAS,kBAAkB,KAAqB;AACrD,SAAO,KAAK,KAAK,KAAK,SAAS,aAAa;AAC9C;;;AClCA,SAA4B,SAAAA,cAAa;AACzC,SAAS,gBAAAC,qBAAoB;;;ACD7B,SAA4B,aAAa;AACzC,SAAS,kBAAkB;AAC3B,OAAOC,WAAU;AACjB,SAAS,SAAS;;;ACHlB,OAAO,QAAQ;AAGf,IAAM,kBAAkB;AAIjB,SAAS,gBAAgB,SAAyC;AACvE,QAAM,OAA+B,CAAC;AACtC,aAAW,WAAW,QAAQ,MAAM,OAAO,GAAG;AAC5C,UAAM,OAAO,QAAQ,KAAK;AAC1B,QAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,EAAG;AACnC,UAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,QAAI,MAAM,EAAG;AACb,UAAM,MAAM,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK;AACnC,QAAI,CAAC,gBAAgB,KAAK,GAAG,EAAG;AAChC,QAAI,QAAQ,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK;AACpC,QACE,MAAM,UAAU,MACd,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,KAC1C,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,IAC9C;AACA,cAAQ,MAAM,MAAM,GAAG,EAAE;AAAA,IAC3B;AACA,SAAK,GAAG,IAAI;AAAA,EACd;AACA,SAAO;AACT;AAOA,IAAM,gBAAgB,oBAAI,IAAY;AAE/B,SAAS,iBAAsC;AACpD,SAAO;AACT;AAKO,SAAS,YAAY,WAAmB,YAAY,GAAS;AAClE,MAAI;AACJ,MAAI;AACF,cAAU,GAAG,aAAa,UAAU,MAAM;AAAA,EAC5C,QAAQ;AACN;AAAA,EACF;AACA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,gBAAgB,OAAO,CAAC,GAAG;AACnE,kBAAc,IAAI,GAAG;AACrB,QAAI,QAAQ,IAAI,GAAG,MAAM,QAAW;AAClC,cAAQ,IAAI,GAAG,IAAI;AAAA,IACrB;AAAA,EACF;AACF;AAKO,SAAS,iBAAiB,SAAiB,KAAa,OAAuB;AACpF,QAAM,QAAQ,GAAG,GAAG,IAAI,MAAM,QAAQ,WAAW,EAAE,CAAC;AACpD,QAAM,UAAU,IAAI,OAAO,QAAQ,GAAG,OAAO;AAC7C,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,SAAO,MAAM,SAAS,MAAM,MAAM,MAAM,SAAS,CAAC,KAAK,IAAI,KAAK,MAAM,GAAI,OAAM,IAAI;AACpF,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,QAAI,QAAQ,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG;AAChC,YAAM,CAAC,IAAI;AACX,aAAO,GAAG,MAAM,KAAK,IAAI,CAAC;AAAA;AAAA,IAC5B;AAAA,EACF;AACA,QAAM,KAAK,KAAK;AAChB,SAAO,GAAG,MAAM,KAAK,IAAI,CAAC;AAAA;AAC5B;;;ADlEO,IAAM,aAAa;AAC1B,IAAM,cAAc,aAAa;AACjC,IAAM,kBAAkB;AACxB,IAAM,cAAc;AAQpB,SAAS,WAAW,KAAa,SAAmD;AAClF,aAAW,QAAQ,QAAQ,IAAI,QAAQ,IAAI,MAAMC,MAAK,SAAS,GAAG;AAChE,QAAI,CAAC,OAAO,UAAU,GAAG,GAAG;AAC1B;AAAA,IACF;AACA,UAAM,OAAOA,MAAK,KAAK,KAAK,GAAG;AAC/B,QAAI,WAAW,IAAI,GAAG;AACpB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,uBAAkC;AACzC,MAAI,QAAQ,aAAa,SAAS;AAChC,WAAO,EAAE,OAAO,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,OAAO,KAAK;AAAA,EAC5D;AACA,QAAM,YAAY,CAAC,QAAgB,gCAAgC,KAAK,IAAI,KAAK,CAAC;AAClF,QAAM,WAAW,WAAW,YAAY,SAAS;AACjD,MAAI,UAAU;AACZ,WAAO,EAAE,OAAO,UAAU,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,OAAO,OAAO;AAAA,EAClE;AACA,QAAM,SAAS,WAAW,SAAS;AACnC,MAAI,QAAQ;AACV,UAAM,UAAUA,MAAK,KAAKA,MAAK,QAAQA,MAAK,QAAQ,MAAM,CAAC,GAAG,OAAO,UAAU;AAC/E,QAAI,WAAW,OAAO,GAAG;AACvB,aAAO,EAAE,OAAO,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,OAAO,OAAO;AAAA,IACjE;AAAA,EACF;AAGA,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,QAAQ;AAAA,IACjB,QAAQ,IAAI;AAAA,IACZ,QAAQ,IAAI,mBAAmB;AAAA,IAC/B,QAAQ,IAAI;AAAA,EACd,GAAG;AACD,QAAI,CAAC,KAAM;AACX,UAAM,YAAYA,MAAK,KAAK,MAAM,OAAO,OAAO,UAAU;AAC1D,UAAM,MAAM,UAAU,YAAY;AAClC,QAAI,KAAK,IAAI,GAAG,EAAG;AACnB,SAAK,IAAI,GAAG;AACZ,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO,EAAE,OAAO,WAAW,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,OAAO,OAAO;AAAA,IACnE;AAAA,EACF;AACA,QAAM,UAAU,QAAQ,IAAI,WAAW;AACvC,SAAO,EAAE,OAAO,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,MAAM,MAAM,CAAC,GAAG,OAAO,MAAM;AAC5E;AAGA,IAAI,aAA+B;AAE5B,SAAS,eAA0B;AACxC,MAAI,CAAC,YAAY;AACf,iBAAa,qBAAqB;AAAA,EACpC;AACA,SAAO;AACT;AAEA,IAAM,SAAS,EAAE,OAAO;AAAA,EACtB,SAAS,EAAE,OAAO,EAAE,SAAS,0BAA0B;AAAA,EACvD,SAAS,EACN,OAAO,EACP,SAAS,EACT,IAAI,WAAW,EACf,SAAS,EACT,SAAS,2CAA2C;AAAA,EACvD,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAAA,EACxF,mBAAmB,EAChB,QAAQ,EACR,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ,CAAC;AAEM,SAAS,SAAS,OAA2B;AAClD,MAAI,QAAQ,aAAa,SAAS;AAChC,QAAI,MAAM,KAAK;AACb,YAAM,YAAY,CAAC,QAAQ,OAAO,MAAM,GAAG,GAAG,MAAM,IAAI,GAAG;AAAA,QACzD,aAAa;AAAA,QACb,OAAO;AAAA,MACT,CAAC,EAAE,MAAM;AAAA,IACX;AACA;AAAA,EACF;AAGA,MAAI,MAAM,KAAK;AACb,QAAI;AACF,cAAQ,KAAK,CAAC,MAAM,KAAK,SAAS;AAClC;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACA,QAAM,KAAK,SAAS;AACtB;AAOA,IAAM,gBAAN,MAAoB;AAAA,EACV,OAAe,OAAO,MAAM,CAAC;AAAA,EAC7B,OAAe,OAAO,MAAM,CAAC;AAAA,EAC7B,UAAU;AAAA,EACV,WAAW;AAAA,EAEnB,OAAO,OAAqB;AAC1B,QAAI,CAAC,KAAK,UAAU;AAClB,WAAK,OAAO,KAAK,KAAK,WAAW,IAAI,QAAQ,OAAO,OAAO,CAAC,KAAK,MAAM,KAAK,CAAC;AAC7E,UAAI,KAAK,KAAK,UAAU,YAAY;AAClC;AAAA,MACF;AACA,WAAK,WAAW;AAChB,WAAK,OAAO,KAAK,KAAK,SAAS,WAAW;AAC1C,WAAK,OAAO,KAAK,KAAK,SAAS,GAAG,WAAW;AAC7C,UAAI,KAAK,KAAK,SAAS,aAAa;AAClC,aAAK,UAAU,KAAK,KAAK,SAAS;AAClC,aAAK,OAAO,KAAK,KAAK,SAAS,KAAK,KAAK,SAAS,WAAW;AAAA,MAC/D;AACA;AAAA,IACF;AACA,SAAK,OAAO,KAAK,KAAK,WAAW,IAAI,QAAQ,OAAO,OAAO,CAAC,KAAK,MAAM,KAAK,CAAC;AAC7E,QAAI,KAAK,KAAK,SAAS,aAAa;AAClC,WAAK,WAAW,KAAK,KAAK,SAAS;AACnC,WAAK,OAAO,KAAK,KAAK,SAAS,KAAK,KAAK,SAAS,WAAW;AAAA,IAC/D;AAAA,EACF;AAAA,EAEA,WAAmB;AACjB,QAAI,CAAC,KAAK,UAAU;AAClB,aAAO,aAAa,KAAK,IAAI;AAAA,IAC/B;AAGA,UAAM,OAAO,aAAa,YAAY,KAAK,IAAI,CAAC;AAChD,UAAM,OAAO,aAAa,cAAc,KAAK,IAAI,CAAC;AAClD,WAAO,GAAG,IAAI;AAAA,OAAU,KAAK,OAAO;AAAA,EAA0B,IAAI;AAAA,EACpE;AACF;AAKO,SAAS,YAAY,KAAqB;AAC/C,MAAI,OAAO;AACX,MAAI,IAAI,IAAI;AACZ,SAAO,IAAI,KAAK,OAAO,OAAO,IAAI,IAAI,CAAC,KAAK,KAAK,SAAU,KAAM;AAC/D;AACA;AAAA,EACF;AACA,MAAI,MAAM,EAAG,QAAO;AACpB,QAAM,OAAO,IAAI,IAAI,CAAC,KAAK;AAC3B,MAAI,OAAO,QAAS,OAAO,SAAU,IAAM,QAAO;AAClD,QAAM,SAAS,OAAO,MAAO,IAAI,OAAO,MAAO,IAAI;AACnD,SAAO,OAAO,SAAS,IAAI,SAAS,GAAG,IAAI,CAAC,IAAI;AAClD;AAIO,SAAS,cAAc,KAAqB;AACjD,MAAI,IAAI;AACR,SAAO,IAAI,IAAI,UAAU,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,SAAU,IAAM;AACnE,SAAO,MAAM,IAAI,MAAM,IAAI,SAAS,CAAC;AACvC;AAMO,SAAS,aAAa,KAAa,WAA4B,QAAQ,UAAkB;AAC9F,QAAM,OAAO,IAAI,SAAS,MAAM;AAChC,MAAI,aAAa,WAAW,KAAK,SAAS,QAAG,GAAG;AAC9C,QAAI;AACF,aAAO,IAAI,YAAY,KAAK,EAAE,OAAO,GAAG;AAAA,IAC1C,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,WAA8B;AAC5C,QAAM,MAAM,EAAE,GAAG,QAAQ,IAAI;AAC7B,aAAW,OAAO,eAAe,GAAG;AAClC,WAAO,IAAI,GAAG;AAAA,EAChB;AACA,SAAO;AACT;AAEO,IAAM,WAAgC;AAAA,EAC3C,MAAM;AAAA,EACN,aACE;AAAA,EACF,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,QAAQ,MAAM,KAAK;AACjB,QAAI,KAAK,mBAAmB;AAC1B,YAAM,OAAO,mBAAmB,MAAM;AAAA,QACpC,SAAS,KAAK;AAAA,QACd,aAAa,KAAK;AAAA,QAClB,KAAK,IAAI;AAAA,QACT,gBAAgB,KAAK;AAAA,QACrB,SAAS,IAAI,WAAW;AAAA,QACxB,KAAK,SAAS;AAAA,MAChB,CAAC;AACD,aAAO,QAAQ,QAAQ;AAAA,QACrB,SAAS,4BAA4B,KAAK,EAAE;AAAA,WAAc,KAAK,OAAO;AAAA,eAAkB,KAAK,eAAe,QAAQ;AAAA,MACtH,CAAC;AAAA,IACH;AACA,UAAM,iBAAiB,KAAK,IAAI,KAAK,WAAW,iBAAiB,WAAW;AAC5E,UAAM,OAAO,aAAa;AAC1B,WAAO,IAAI,QAAoB,CAAC,YAAY;AAC1C,YAAM,QAAQ,MAAM,KAAK,OAAO,KAAK,KAAK,KAAK,OAAO,GAAG;AAAA,QACvD,KAAK,IAAI;AAAA,QACT,KAAK,SAAS;AAAA,QACd,aAAa;AAAA,QACb,UAAU,QAAQ,aAAa;AAAA;AAAA;AAAA,QAG/B,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,MAClC,CAAC;AACD,YAAM,SAAS,IAAI,cAAc;AACjC,UAAI,UAAU;AACd,YAAM,SAAS,CAAC,WAAuB;AACrC,YAAI,SAAS;AACX;AAAA,QACF;AACA,kBAAU;AACV,qBAAa,KAAK;AAClB,YAAI,aAAa,oBAAoB,SAAS,OAAO;AACrD,gBAAQ,MAAM;AAAA,MAChB;AACA,YAAM,UAAU,MAAM;AACpB,iBAAS,KAAK;AACd,eAAO,EAAE,SAAS,GAAG,MAAM;AAAA,kBAAqB,SAAS,KAAK,CAAC;AAAA,MACjE;AACA,YAAM,QAAQ,WAAW,MAAM;AAC7B,iBAAS,KAAK;AACd,eAAO;AAAA,UACL,SAAS,GAAG,MAAM;AAAA,0BAA6B,cAAc;AAAA,UAC7D,SAAS;AAAA,QACX,CAAC;AAAA,MACH,GAAG,iBAAiB,GAAI;AACxB,YAAM,MAAM;AACZ,YAAM,OAAO,GAAG,QAAQ,CAAC,MAAc,OAAO,OAAO,CAAC,CAAC;AACvD,YAAM,OAAO,GAAG,QAAQ,CAAC,MAAc,OAAO,OAAO,CAAC,CAAC;AACvD,YAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,eAAO,EAAE,SAAS,0BAA0B,KAAK,KAAK,MAAM,IAAI,OAAO,IAAI,SAAS,KAAK,CAAC;AAAA,MAC5F,CAAC;AACD,UAAI,aAAa,iBAAiB,SAAS,OAAO;AAClD,YAAM,GAAG,SAAS,CAAC,SAAS;AAC1B,cAAM,UAAU,OAAO,SAAS,EAAE,QAAQ,QAAQ,EAAE;AACpD,YAAI,SAAS,GAAG;AACd,iBAAO,EAAE,SAAS,WAAW,cAAc,CAAC;AAC5C;AAAA,QACF;AACA,eAAO;AAAA,UACL,SAAS,GAAG,OAAO,GAAG,UAAU,OAAO,EAAE,cAAc,QAAQ,SAAS;AAAA,UACxE,SAAS;AAAA,QACX,CAAC;AAAA,MACH,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AACF;;;AD9QO,IAAM,kBAAkB;AACxB,IAAM,uBAAuB;AAI7B,IAAMC,wBAAuB;AAEpC,IAAM,mBAAmB;AAiClB,IAAM,cAAN,cAA0BC,cAAa;AAAA,EACpC,UAAU,oBAAI,IAAwB;AAAA,EACtC,MAAM;AAAA,EAEd,MAAM,MAAsC;AAC1C,UAAM,KAAK,QAAQ,EAAE,KAAK,GAAG;AAC7B,UAAM,OAAO,aAAa;AAC1B,UAAM,QAAQC,OAAM,KAAK,OAAO,KAAK,KAAK,KAAK,OAAO,GAAG;AAAA,MACvD,KAAK,KAAK;AAAA,MACV,KAAK,KAAK,OAAO,SAAS;AAAA,MAC1B,aAAa;AAAA;AAAA;AAAA,MAGb,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA;AAAA;AAAA,MAGhC,UAAU,QAAQ,aAAa;AAAA,IACjC,CAAC;AACD,UAAM,MAAkB;AAAA,MACtB;AAAA,MACA,SAAS,KAAK,WAAW;AAAA,MACzB,SAAS,KAAK;AAAA,MACd,aAAa,KAAK;AAAA,MAClB,QAAQ;AAAA,MACR,WAAW,KAAK,IAAI;AAAA,MACpB,cAAc,CAAC;AAAA,MACf,aAAa;AAAA,MACb,WAAW;AAAA,MACX;AAAA,IACF;AACA,SAAK,QAAQ,IAAI,IAAI,GAAG;AAExB,UAAM,SAAS,CAAC,MAAc;AAC5B,UAAI,aAAa,KAAK,CAAC;AACvB,UAAI,eAAe,EAAE;AACrB,UAAI,IAAI,cAAc,iBAAiB;AACrC,cAAM,SAAS,OAAO,OAAO,IAAI,cAAc,IAAI,WAAW;AAC9D,cAAM,OAAO,cAAc,OAAO,SAAS,OAAO,SAAS,eAAe,CAAC;AAC3E,YAAI,eAAe,CAAC,IAAI;AACxB,YAAI,cAAc,KAAK;AACvB,YAAI,YAAY;AAAA,MAClB;AAAA,IACF;AACA,UAAM,OAAO,GAAG,QAAQ,MAAM;AAC9B,UAAM,OAAO,GAAG,QAAQ,MAAM;AAC9B,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,aAAO,OAAO,KAAK,0BAA0B,KAAK,KAAK,MAAM,IAAI,OAAO;AAAA,CAAI,CAAC;AAC7E,UAAI,IAAI,WAAW,WAAW;AAC5B,aAAK,OAAO,KAAK,UAAU,IAAI;AAAA,MACjC;AAAA,IACF,CAAC;AACD,UAAM,GAAG,SAAS,CAAC,SAAS;AAC1B,UAAI,IAAI,WAAW,UAAW;AAC9B,WAAK,OAAO,KAAK,SAAS,IAAI,cAAc,UAAU,IAAI;AAAA,IAC5D,CAAC;AAED,UAAM,iBAAiB,KAAK,kBAAkB;AAC9C,QAAI,QAAQ,WAAW,MAAM,KAAK,UAAU,KAAK,WAAW,GAAG,iBAAiB,GAAI;AACpF,QAAI,MAAM,MAAM;AAEhB,SAAK,WAAW,GAAG;AACnB,WAAO,KAAK,SAAS,GAAG;AAAA,EAC1B;AAAA,EAEA,IAAI,IAAsC;AACxC,UAAM,MAAM,KAAK,QAAQ,IAAI,EAAE;AAC/B,WAAO,MAAM,KAAK,SAAS,GAAG,IAAI;AAAA,EACpC;AAAA,EAEA,OAAuB;AACrB,WAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,EAAE,IAAI,CAAC,QAAQ,KAAK,SAAS,GAAG,CAAC;AAAA,EACnE;AAAA,EAEA,eAAuB;AACrB,QAAI,QAAQ;AACZ,eAAW,OAAO,KAAK,QAAQ,OAAO,GAAG;AACvC,UAAI,IAAI,WAAW,UAAW,UAAS;AAAA,IACzC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,KAAK,IAAsC;AACzC,UAAM,MAAM,KAAK,QAAQ,IAAI,EAAE;AAC/B,QAAI,CAAC,OAAO,IAAI,WAAW,UAAW,QAAO;AAC7C,SAAK,UAAU,KAAK,SAAS;AAC7B,WAAO,KAAK,SAAS,GAAG;AAAA,EAC1B;AAAA,EAEA,UAA0B;AACxB,UAAM,SAAyB,CAAC;AAChC,eAAW,OAAO,KAAK,QAAQ,OAAO,GAAG;AACvC,UAAI,IAAI,WAAW,WAAW;AAC5B,aAAK,UAAU,KAAK,SAAS;AAC7B,eAAO,KAAK,KAAK,SAAS,GAAG,CAAC;AAAA,MAChC;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,KAAiB,QAA0B;AAC3D,aAAS,IAAI,KAAK;AAClB,SAAK,OAAO,KAAK,QAAQ,IAAI;AAAA,EAC/B;AAAA,EAEQ,OAAO,KAAiB,QAAoB,UAA+B;AACjF,QAAI,IAAI,MAAO,cAAa,IAAI,KAAK;AACrC,QAAI,SAAS;AACb,QAAI,WAAW;AACf,QAAI,UAAU,KAAK,IAAI;AACvB,SAAK,cAAc;AACnB,SAAK,WAAW,GAAG;AAAA,EACrB;AAAA,EAEQ,gBAAsB;AAC5B,UAAM,WAAW,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,EAAE,OAAO,CAAC,QAAQ,IAAI,WAAW,SAAS;AACpF,QAAI,SAAS,UAAUF,sBAAsB;AAC7C,aAAS,KAAK,CAAC,GAAG,OAAO,EAAE,WAAW,MAAM,EAAE,WAAW,EAAE;AAC3D,eAAW,OAAO,SAAS,MAAM,GAAG,SAAS,SAASA,qBAAoB,GAAG;AAC3E,WAAK,QAAQ,OAAO,IAAI,EAAE;AAAA,IAC5B;AAAA,EACF;AAAA,EAEQ,WAAW,KAAuB;AACxC,SAAK,KAAK,UAAU,KAAK,SAAS,GAAG,CAAC;AAAA,EACxC;AAAA,EAEQ,SAAS,KAA+B;AAC9C,UAAM,SAAS,aAAa,OAAO,OAAO,IAAI,cAAc,IAAI,WAAW,CAAC;AAC5E,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,SAAS,IAAI;AAAA,MACb,SAAS,IAAI;AAAA,MACb,aAAa,IAAI;AAAA,MACjB,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,MACf,SAAS,IAAI;AAAA,MACb,UAAU,IAAI;AAAA,MACd,QAAQ,IAAI,YAAY,mBAAmB,SAAS;AAAA,IACtD;AAAA,EACF;AACF;AAEO,IAAM,qBAAqB,IAAI,YAAY;;;AG3MlD,SAAS,kBAAkB;AAC3B,SAAS,OAAO,UAAU,IAAI,MAAM,iBAAiB;AACrD,OAAOG,SAAQ;AACf,OAAOC,WAAU;AAoDjB,IAAM,gBAAgB;AACf,IAAM,6BAA6B,IAAI,OAAO;AAErD,IAAM,QAAwB,CAAC;AAC/B,IAAI,aAAa;AACjB,IAAI,yBAAyB;AAC7B,IAAI,gBAAgB;AACpB,IAAI,QAA8B;AAMlC,IAAM,WAAWA,MAAK;AAAA,EACpBD,IAAG,OAAO;AAAA,EACV,sBAAsB,QAAQ,GAAG,IAAI,WAAW,EAAE,MAAM,GAAG,CAAC,CAAC;AAC/D;AAEA,SAAS,YAAY,GAAoB;AACvC,SAAO,EAAE,WAAW,WAAWC,MAAK,GAAG;AACzC;AAEA,eAAe,aAAa,UAAuC;AACjE,MAAI,SAAS,YAAY,QAAQ,SAAS,YAAa;AACvD,MAAI;AACF,UAAM,OAAOA,MAAK,KAAK,UAAU,GAAG,SAAS,EAAE,OAAO;AACtD,UAAM,MAAM,UAAU,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAGtD,UAAM,UAAU,MAAM,SAAS,SAAS,EAAE,MAAM,IAAM,CAAC;AACvD,aAAS,cAAc;AACvB,aAAS,UAAU;AAAA,EACrB,QAAQ;AAAA,EAER;AACF;AAKA,eAAe,aAAa,UAAuC;AACjE,MAAI,SAAS,eAAe,YAAY,SAAS,WAAW,GAAG;AAC7D,UAAM,GAAG,SAAS,aAAa,EAAE,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAChE;AACF;AAEO,SAAS,iBAAiB,MAAkC;AACjE,UAAQ;AACV;AAEO,SAAS,iBAAyB;AACvC,SAAO,EAAE;AACX;AAKO,SAAS,UAAU,eAAe,IAAY;AACnD,2BAAyB;AACzB,SAAO,EAAE;AACX;AAEO,SAAS,iBAAyB;AACvC,SAAO;AACT;AAEO,SAAS,0BAAkC;AAChD,SAAO;AACT;AAEA,SAAS,QAAQ,UAAuC;AACtD,SAAO,SAAS,SAAS;AAC3B;AAEA,eAAsB,gBACpB,UACA,UACA,iBACuB;AACvB,MAAI,UAAkC;AACtC,MAAI,UAAU;AACd,MAAI,kBAAkB;AACtB,QAAM,KAAK,MAAM,KAAK,QAAQ,EAAE,MAAM,MAAM,IAAI;AAChD,MAAI,IAAI,OAAO,GAAG;AAChB,cAAU;AACV,QAAI,GAAG,OAAO,4BAA4B;AACxC,wBAAkB;AAAA,IACpB,OAAO;AACL,UAAI;AAGF,kBAAU,MAAM,SAAS,QAAQ;AAAA,MACnC,QAAQ;AAEN,kBAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACA,SAAO;AAAA,IACL,IAAI,eAAe;AAAA,IACnB,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,iBAAiB,mBAAmB;AAAA,IACpC;AAAA,IACA,WAAW,KAAK,IAAI;AAAA,IACpB,MAAM,iBAAiB,QAAQ;AAAA,IAC/B,cAAc,iBAAiB,gBAAgB;AAAA,IAC/C,OAAO,iBAAiB,SAAS;AAAA,EACnC;AACF;AAEA,eAAsB,aAAa,UAAuC;AACxE,QAAM,KAAK,QAAQ;AACnB,MAAI;AAGF,UAAM,OAAO,SAAS,QAAQ;AAAA,EAChC,SAAS,KAAK;AAGZ,QAAI,QAAQ,IAAI,YAAY;AAC1B,cAAQ,MAAM,iDAAiD,GAAG;AAAA,IACpE;AAAA,EACF;AACA,QAAM,aAAa,QAAQ;AAC3B,MAAI,MAAM,SAAS,eAAe;AAChC,UAAM,UAAU,MAAM,OAAO,GAAG,MAAM,SAAS,aAAa;AAC5D,eAAW,OAAO,QAAS,OAAM,aAAa,GAAG;AACjD,UAAM,gBAAgB,QAAQ,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAAA,EAChD;AACF;AAEA,eAAe,gBAAgB,KAA8B;AAC3D,MAAI,IAAI,SAAS,EAAG,OAAM,OAAO,WAAW,GAAG;AACjD;AAMO,SAAS,gBAAgC;AAC9C,SAAO,CAAC,GAAG,KAAK;AAClB;AAEO,SAAS,iBAAuB;AACrC,aAAW,YAAY,MAAO,MAAK,aAAa,QAAQ;AACxD,QAAM,SAAS;AACjB;AAKO,SAAS,iBAAiB,WAAiC;AAChE,aAAW,YAAY,MAAO,MAAK,aAAa,QAAQ;AACxD,QAAM,SAAS;AACf,QAAM,KAAK,GAAG,UAAU,MAAM,CAAC,aAAa,CAAC;AAC7C,aAAW,YAAY,OAAO;AAC5B,QAAI,SAAS,KAAK,cAAe,iBAAgB,SAAS;AAAA,EAC5D;AACF;AAEA,eAAe,eAAe,UAAyD;AACrF,MAAI,SAAS,YAAY,KAAM,QAAO,SAAS;AAC/C,MAAI,CAAC,SAAS,YAAa,QAAO;AAClC,MAAI;AAGF,WAAO,MAAM,SAAS,SAAS,WAAW;AAAA,EAC5C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,OAAO,UAAyC;AAC7D,MAAI,SAAS,SAAS;AACpB,QAAI,SAAS,iBAAiB;AAC5B,aAAO,WAAW,SAAS,IAAI;AAAA,IACjC;AACA,UAAM,UAAU,MAAM,eAAe,QAAQ;AAC7C,QAAI,YAAY,MAAM;AACpB,aAAO,WAAW,SAAS,IAAI;AAAA,IACjC;AACA,UAAM,MAAMC,MAAK,QAAQ,SAAS,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5D,UAAM,UAAU,SAAS,MAAM,OAAO;AACtC,WAAO,YAAY,SAAS,IAAI,wBAAwB,SAAS,QAAQ;AAAA,EAC3E;AACA,QAAM,GAAG,SAAS,MAAM,EAAE,OAAO,KAAK,CAAC;AACvC,SAAO,WAAW,SAAS,IAAI,6BAA6B,SAAS,QAAQ;AAC/E;AAqBO,SAAS,kBAAkB,MAAc,kBAAkB,OAAuB;AACvF,SAAO,MACJ;AAAA,IACC,CAAC,aAAa,SAAS,SAAS,SAAS,mBAAmB,QAAQ,QAAQ,MAAM;AAAA,EACpF,EACC,QAAQ;AACb;AAMA,eAAsB,uBAAuB,UAAgD;AAC3F,QAAM,UAAU,MAAM,eAAe,QAAQ;AAC7C,MAAI,YAAY,KAAM,QAAO;AAC7B,SAAO,OAAO,YAAY,WAAW,UAAU,QAAQ,SAAS,MAAM;AACxE;AAMA,eAAsB,kBAAkB,MAAc,kBAAkB,OAA0B;AAChG,QAAM,UAA0B,CAAC;AACjC,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,UAAM,WAAW,MAAM,CAAC;AACxB,QAAI,YAAY,SAAS,SAAS,SAAS,mBAAmB,QAAQ,QAAQ,MAAM,SAAS;AAC3F,cAAQ,KAAK,QAAQ;AAAA,IACvB;AAAA,EACF;AACA,QAAM,WAAqB,CAAC;AAC5B,QAAM,cAAwB,CAAC;AAC/B,aAAW,YAAY,SAAS;AAC9B,QAAI;AACF,eAAS,KAAK,MAAM,OAAO,QAAQ,CAAC;AACpC,YAAM,MAAM,MAAM,QAAQ,QAAQ;AAClC,UAAI,OAAO,EAAG,OAAM,OAAO,KAAK,CAAC;AACjC,kBAAY,KAAK,SAAS,EAAE;AAC5B,YAAM,aAAa,QAAQ;AAAA,IAC7B,SAAS,KAAK;AACZ,eAAS;AAAA,QACP,oBAAoB,SAAS,IAAI,KAAM,IAAc,OAAO;AAAA,MAC9D;AAAA,IACF;AAAA,EACF;AACA,QAAM,gBAAgB,WAAW;AACjC,SAAO;AACT;AAMA,eAAsB,kBAAkB,MAAc,kBAAkB,OAAwB;AAC9F,QAAM,UAAoB,CAAC;AAC3B,QAAM,mBAAmC,CAAC;AAC1C,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,UAAM,WAAW,MAAM,CAAC;AACxB,QAAI,YAAY,SAAS,SAAS,SAAS,mBAAmB,QAAQ,QAAQ,MAAM,SAAS;AAC3F,cAAQ,KAAK,SAAS,EAAE;AACxB,uBAAiB,KAAK,QAAQ;AAC9B,YAAM,OAAO,GAAG,CAAC;AAAA,IACnB;AAAA,EACF;AACA,aAAW,YAAY,iBAAkB,OAAM,aAAa,QAAQ;AACpE,QAAM,gBAAgB,OAAO;AAC7B,SAAO,QAAQ;AACjB;AAeA,eAAsB,iBACpB,IACA,kBAAkB,OACY;AAC9B,MAAI,CAAC,MAAM,KAAK,CAAC,aAAa,SAAS,OAAO,EAAE,EAAG,QAAO;AAC1D,QAAM,UAA0B,CAAC;AACjC,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,UAAM,WAAW,MAAM,CAAC;AACxB,QAAI,YAAY,SAAS,MAAM,OAAO,mBAAmB,QAAQ,QAAQ,MAAM,SAAS;AACtF,cAAQ,KAAK,QAAQ;AAAA,IACvB;AAAA,EACF;AACA,QAAM,WAAqB,CAAC;AAC5B,QAAM,cAAwB,CAAC;AAC/B,MAAI,eAA8B;AAClC,aAAW,YAAY,SAAS;AAC9B,QAAI;AACF,eAAS,KAAK,MAAM,OAAO,QAAQ,CAAC;AACpC,YAAM,MAAM,MAAM,QAAQ,QAAQ;AAClC,UAAI,OAAO,EAAG,OAAM,OAAO,KAAK,CAAC;AACjC,kBAAY,KAAK,SAAS,EAAE;AAC5B,YAAM,aAAa,QAAQ;AAC3B,UAAI,SAAS,gBAAgB,GAAG;AAC9B,uBACE,iBAAiB,OACb,SAAS,eACT,KAAK,IAAI,cAAc,SAAS,YAAY;AAAA,MACpD;AAAA,IACF,SAAS,KAAK;AACZ,eAAS;AAAA,QACP,oBAAoB,SAAS,IAAI,KAAM,IAAc,OAAO;AAAA,MAC9D;AAAA,IACF;AAAA,EACF;AACA,QAAM,gBAAgB,WAAW;AACjC,SAAO,EAAE,UAAU,aAAa;AAClC;;;AC/XA,SAAS,SAAAC,QAAO,YAAAC,WAAU,QAAQ,MAAAC,KAAI,aAAAC,kBAAiB;AACvD,OAAOC,WAAU;AACjB,SAAS,KAAAC,UAAS;AASlB,IAAM,iBAAiBA,GAAE,OAAO;AAAA,EAC9B,IAAIA,GAAE,OAAO,EAAE,IAAI;AAAA,EACnB,OAAOA,GAAE,OAAO;AAAA,EAChB,QAAQA,GAAE,KAAK,CAAC,WAAW,eAAe,MAAM,CAAC;AACnD,CAAC;AAED,IAAM,aAAaA,GAAE,OAAO;AAAA,EAC1B,WAAWA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,OAAOA,GAAE,MAAM,cAAc;AAC/B,CAAC;AAED,IAAM,cAAcA,GAAE,OAAO;AAAA,EAC3B,OAAOA,GAAE,MAAM,cAAc,EAAE,SAAS,+CAA+C;AACzF,CAAC;AAED,IAAM,aAAaA,GAAE,OAAO,CAAC,CAAC;AAE9B,SAAS,QAAQ,KAAqB;AACpC,SAAOD,MAAK,KAAK,KAAK,SAAS,YAAY;AAC7C;AAIA,IAAI,eAA8B,MAAM;AAEjC,SAAS,oBAAoB,OAA4B;AAC9D,iBAAe;AACjB;AAOA,IAAI,iBAAgC;AAE7B,SAAS,eAAe,IAAyB;AACtD,mBAAiB;AACnB;AAEO,IAAM,YAAN,MAAgB;AAAA,EACb,QAAQ,oBAAI,IAAsB;AAAA,EAE1C,OAAmB;AACjB,WAAO,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC;AAAA,EAChC;AAAA,EAEA,QAAQ,OAAyB;AAC/B,SAAK,MAAM,MAAM;AACjB,eAAW,QAAQ,OAAO;AACxB,WAAK,MAAM,IAAI,KAAK,IAAI,IAAI;AAAA,IAC9B;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,KAAa,WAAmC;AACzD,QAAI;AACJ,QAAI;AACF,YAAM,MAAMH,UAAS,QAAQ,GAAG,GAAG,MAAM;AAAA,IAC3C,QAAQ;AACN;AAAA,IACF;AACA,QAAI;AACF,YAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,UAAI,MAAM,QAAQ,MAAM,GAAG;AAIzB,aAAK,QAAQ,OAAO,OAAO,CAAC,OAAO,eAAe,UAAU,EAAE,EAAE,OAAO,CAAC;AACxE;AAAA,MACF;AACA,YAAM,OAAO,WAAW,UAAU,MAAM;AACxC,UAAI,CAAC,KAAK,SAAS;AACjB;AAAA,MACF;AACA,UAAI,KAAK,KAAK,eAAe,aAAa,OAAO;AAG/C,aAAK,QAAQ,CAAC,CAAC;AACf;AAAA,MACF;AACA,WAAK,QAAQ,KAAK,KAAK,KAAK;AAAA,IAC9B,QAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,KAAK,KAA4B;AACrC,QAAI,CAAC,aAAa,GAAG;AACnB;AAAA,IACF;AACA,UAAM,QAAQ,KAAK,KAAK;AACxB,UAAM,OAAO,QAAQ,GAAG;AACxB,QAAI,MAAM,WAAW,KAAK,MAAM,MAAM,CAAC,OAAO,GAAG,WAAW,MAAM,GAAG;AACnE,YAAMC,IAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAC9B;AAAA,IACF;AACA,UAAMF,OAAMI,MAAK,KAAK,KAAK,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AACxD,UAAM,MAAM,GAAG,IAAI;AACnB,UAAMD,WAAU,KAAK,GAAG,KAAK,UAAU,EAAE,WAAW,gBAAgB,MAAM,GAAG,MAAM,CAAC,CAAC;AAAA,CAAI;AACzF,UAAM,OAAO,KAAK,IAAI;AAAA,EACxB;AACF;AAEA,IAAM,UAA8C;AAAA,EAClD,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM;AACR;AAEO,SAAS,YAAY,OAA2B;AACrD,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,MAAM,IAAI,CAAC,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC,IAAI,GAAG,EAAE,KAAK,GAAG,KAAK,EAAE;AAC7E,QAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;AACpD,SAAO,MACJ,IAAI,CAAC,MAAM,MAAM;AAChB,UAAM,SAAS,MAAM,CAAC,GAAG;AACzB,WAAO,WAAW,YAAY,OAAO,GAAG,KAAK,OAAO,QAAQ,CAAC,CAAC,IAAI,MAAM;AAAA,EAC1E,CAAC,EACA,KAAK,IAAI;AACd;AAEA,IAAM,eAAe,IAAI,UAAU;AAI5B,SAAS,cAAc,MAAkC;AAC9D,QAAM,SAAS,YAAY,UAAU,IAAI;AACzC,SAAO,OAAO,UAAU,OAAO,KAAK,QAAQ;AAC9C;AAKO,SAAS,kBAAkB,MAAyB;AACzD,UAAQ,cAAc,IAAI,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK;AAC1F;AAMA,eAAsB,UAAU,KAAa,WAAyC;AACpF,QAAM,aAAa,KAAK,KAAK,SAAS;AACtC,SAAO,aAAa,KAAK;AAC3B;AAIO,SAAS,aAAmB;AACjC,eAAa,QAAQ,CAAC,CAAC;AACzB;AAEO,SAAS,gBAAgB,QAAmB,cAAsB;AACvE,QAAM,YAAsC;AAAA,IAC1C,MAAM;AAAA,IACN,aACE;AAAA,IACF,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,MAAM,QAAQ,MAAM,KAAK;AACvB,YAAM,SAAS,YAAY,UAAU,IAAI;AACzC,UAAI,CAAC,OAAO,SAAS;AACnB,eAAO,EAAE,SAAS,kBAAkB,OAAO,MAAM,OAAO,IAAI,SAAS,KAAK;AAAA,MAC5E;AAGA,YAAM,WAAW,MAAM,KAAK;AAC5B,YAAM,QAAQ,OAAO,KAAK,KAAK;AAC/B,UAAI;AACF,cAAM,MAAM,KAAK,IAAI,GAAG;AAAA,MAC1B,SAAS,KAAK;AACZ,cAAM,QAAQ,QAAQ;AACtB,eAAO,EAAE,SAAS,yBAA0B,IAAc,OAAO,IAAI,SAAS,KAAK;AAAA,MACrF;AACA,YAAM,QAAQ,MAAM,KAAK;AACzB,YAAM,OAAO,MAAM,OAAO,CAAC,OAAO,GAAG,WAAW,MAAM,EAAE;AAIxD,aAAO,EAAE,SAAS,sBAAsB,IAAI,IAAI,MAAM,MAAM,UAAU;AAAA,IACxE;AAAA,EACF;AAEA,QAAM,WAAoC;AAAA,IACxC,MAAM;AAAA,IACN,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,MAAM,UAAU;AAGd,aAAO,EAAE,SAAS,YAAY,MAAM,KAAK,CAAC,EAAE;AAAA,IAC9C;AAAA,EACF;AAEA,SAAO,CAAC,WAAW,QAAQ;AAC7B;;;ACrNA,SAAS,gBAAgB;AACzB,SAAS,cAAAG,mBAAkB;AAC3B,SAAS,YAAAC,WAAU,QAAQ,aAAAC,kBAAiB;AAC5C,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,iBAAiB;AAInB,IAAM,oBAAoB;AAEjC,IAAM,gBAAgB,UAAU,QAAQ;AAOjC,SAAS,oBACd,WACA,YACA,cACQ;AACR,QAAM,QAAQ,UAAU,QAAQ,MAAM,IAAI;AAC1C,QAAM,SAAS,WAAW,QAAQ,MAAM,IAAI;AAC5C,SAAO;AAAA,IACL;AAAA,IACA,4CAA4C,KAAK;AAAA,IACjD,6BAA6B,YAAY;AAAA,IACzC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,MAAM;AAAA,IACpB;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAKO,SAAS,gBACd,UACA,WACA,YACA,cACA,QAAuD,CAAC,GAClC;AACtB,MAAI,aAAa,SAAS;AACxB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA,oBAAoB,WAAW,YAAY,YAAY;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AACA,MAAI,aAAa,UAAU;AACzB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM,CAAC,MAAM,OAAO,YAAY,GAAG,WAAW,SAAS,UAAU;AAAA,IACnE;AAAA,EACF;AACA,MAAI,aAAa,SAAS;AACxB,QAAI,MAAM,YAAY;AACpB,aAAO;AAAA,QACL,SAAS;AAAA;AAAA;AAAA,QAGT,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,UACA,GAAG,YAAY,IAAI,YAAY;AAAA,UAC/B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,QAAI,MAAM,WAAW;AACnB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,8BAA8B,YAAY,kCAAkC,YAAY;AAAA,UACxF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,eAAe,cAAc,SAAmC;AAC9D,QAAM,UAAU,QAAQ,aAAa,UAAU,UAAU;AACzD,MAAI;AACF,UAAM,cAAc,SAAS,CAAC,OAAO,GAAG,EAAE,SAAS,kBAAkB,CAAC;AACtE,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAcA,eAAsB,wBAAwB,OAA8C;AAC1F,QAAM,MAAM,OAAO,KAAK,MAAM,MAAM,QAAQ;AAC5C,QAAM,OAAO,qBAAqB,GAAG;AACrC,MAAI,CAAC,QAAQ,KAAK,IAAI,KAAK,OAAO,KAAK,MAAM,KAAK,qBAAqB;AACrE,WAAO,EAAE,OAAO,WAAW,OAAO,SAAS,MAAM;AAAA,EACnD;AACA,QAAM,YAAYC,MAAK,KAAKC,IAAG,OAAO,GAAG,cAAcC,YAAW,CAAC,MAAM;AACzE,QAAM,aAAaF,MAAK,KAAKC,IAAG,OAAO,GAAG,cAAcC,YAAW,CAAC,MAAM;AAC1E,MAAI;AACF,UAAM,CAAC,YAAY,SAAS,IAC1B,QAAQ,aAAa,UACjB,MAAM,QAAQ,IAAI,CAAC,cAAc,SAAS,GAAG,cAAc,QAAQ,CAAC,CAAC,IACrE,CAAC,OAAO,KAAK;AACnB,UAAM,OAAO,gBAAgB,QAAQ,UAAU,WAAW,YAAY,qBAAqB;AAAA,MACzF;AAAA,MACA;AAAA,IACF,CAAC;AACD,QAAI,CAAC,KAAM,QAAO,EAAE,OAAO,WAAW,MAAM,SAAS,MAAM;AAC3D,UAAMC,WAAU,WAAW,GAAG;AAC9B,UAAM,cAAc,KAAK,SAAS,KAAK,MAAM,EAAE,SAAS,kBAAkB,CAAC;AAC3E,UAAM,MAAM,MAAMC,UAAS,UAAU,EAAE,MAAM,MAAM,IAAI;AACvD,QAAI,CAAC,OAAO,IAAI,WAAW,EAAG,QAAO,EAAE,OAAO,WAAW,MAAM,SAAS,MAAM;AAC9E,WAAO;AAAA,MACL,OAAO,EAAE,MAAM,MAAM,MAAM,UAAU,aAAa,MAAM,IAAI,SAAS,QAAQ,EAAE;AAAA,MAC/E,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,OAAO,WAAW,MAAM,SAAS,MAAM;AAAA,EAClD,UAAE;AACA,UAAM,OAAO,SAAS,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AACtC,UAAM,OAAO,UAAU,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACzC;AACF;;;AC3JA,IAAM,sBAAsB,oBAAI,IAAI;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,qBAAqB,CAAC,QAAQ,QAAQ,QAAQ,MAAM;AAE1D,IAAM,2BAA2B,oBAAI,IAAI,CAAC,gBAAgB,eAAe,eAAe,CAAC;AAQzF,IAAM,gBAA0B,CAAC;AAE1B,SAAS,0BAA0B,UAAmC;AAC3E,gBAAc,SAAS;AACvB,gBAAc,KAAK,GAAG,SAAS,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AACjF;AAEA,SAAS,aAAa,SAAyB;AAC7C,QAAM,UAAU,QAAQ,QAAQ,sBAAsB,MAAM,EAAE,QAAQ,OAAO,IAAI;AACjF,SAAO,IAAI,OAAO,IAAI,OAAO,KAAK,GAAG;AACvC;AAEA,SAAS,aAAa,GAAqB;AACzC,SAAO,EACJ,QAAQ,OAAO,GAAG,EAClB,MAAM,GAAG,EACT,OAAO,OAAO,EACd,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;AAC/B;AAOO,SAAS,gBAAgB,GAAoB;AAClD,QAAM,WAAW,aAAa,CAAC;AAC/B,QAAM,OAAO,SAAS,SAAS,SAAS,CAAC,KAAK;AAC9C,QAAM,aAAa,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,QAAQ,EAAE;AAC3D,aAAW,WAAW,eAAe;AACnC,UAAM,KAAK,aAAa,OAAO;AAC/B,QAAI,GAAG,KAAK,UAAU,KAAK,GAAG,KAAK,IAAI,GAAG;AACxC,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,yBAAyB,IAAI,IAAI,GAAG;AACtC,WAAO;AAAA,EACT;AACA,MAAI,SAAS,UAAU,KAAK,WAAW,OAAO,GAAG;AAC/C,WAAO;AAAA,EACT;AACA,MAAI,oBAAoB,IAAI,IAAI,GAAG;AACjC,WAAO;AAAA,EACT;AACA,MAAI,mBAAmB,KAAK,CAAC,WAAW,KAAK,SAAS,MAAM,CAAC,GAAG;AAC9D,WAAO;AAAA,EACT;AACA,QAAM,MAAM,SAAS,SAAS,SAAS,CAAC,KAAK;AAC7C,MAAI,QAAQ,UAAU,SAAS,eAAe;AAC5C,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,WAAW,SAAS,UAAU;AACxC,WAAO;AAAA,EACT;AACA,SAAO;AACT;;;AC5EA,SAAS,cAAc,gBAAgB;AACvC,SAAS,SAAS,UAAU,QAAAC,aAAY;AACxC,OAAOC,WAAU;AAEV,IAAM,YAAY,oBAAI,IAAI,CAAC,gBAAgB,QAAQ,MAAM,CAAC;AAI1D,IAAM,mBAAmB;AA2ChC,SAAS,cAAc,aAAqB;AAC1C,MAAI,SAAS;AACb,QAAM,UAA6B,CAAC;AACpC,SAAO,eAAe,MAAS,IAAkC;AAC/D,WAAO,UAAU,aAAa;AAC5B,YAAM,IAAI,QAAc,CAAC,YAAY,QAAQ,KAAK,OAAO,CAAC;AAAA,IAC5D;AACA,cAAU;AACV,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,UAAE;AACA,gBAAU;AACV,cAAQ,MAAM,IAAI;AAAA,IACpB;AAAA,EACF;AACF;AAUA,SAAS,aAAa,QAAuB,KAAa,OAAyB;AACjF,aAAW,SAAS,QAAQ;AAG1B,UAAM,QACJ,MAAM,YAAY,KACd,MACA,IAAI,WAAW,GAAG,MAAM,OAAO,GAAG,IAChC,IAAI,MAAM,MAAM,QAAQ,SAAS,CAAC,IAClC;AACR,QAAI,UAAU,MAAM;AAClB;AAAA,IACF;AACA,eAAW,EAAE,SAAS,QAAQ,KAAK,MAAM,UAAU;AACjD,UAAI,WAAW,CAAC,OAAO;AACrB;AAAA,MACF;AACA,UAAI,YAAY,SAAS,KAAK,GAAG;AAC/B,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAsB,UACpB,MACA,WAAwB,WACxB,UAAuB,CAAC,GACH;AACrB,QAAM,MAAoB,CAAC;AAC3B,QAAM,QAAQ,cAAc,gBAAgB;AAC5C,QAAM,SAAS,QAAQ;AACvB,QAAM,UAAU,CAAC,MAAe,QAAQ,aAAa,UAAU,EAAE,YAAY,IAAI;AAGjF,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,WAAW,MAAM,SAAS,IAAI,EAAE,MAAM,MAAM,IAAI;AACtD,MAAI,aAAa,MAAM;AACrB,YAAQ,IAAI,QAAQ,QAAQ,CAAC;AAAA,EAC/B;AAEA,MAAI;AACJ,iBAAe,YAAY,KAAqC;AAC9D,UAAM,OAAO,MAAM,MAAM,MAAM,SAAS,GAAG,CAAC,EAAE,MAAM,MAAM,IAAI;AAC9D,QAAI,SAAS,MAAM;AACjB,aAAO;AAAA,IACT;AACA,UAAM,WAAW,QAAQ;AACzB,QAAI,aAAa,QAAW;AAC1B,aAAO;AAAA,IACT;AACA,QAAI,iBAAiB,QAAW;AAC9B,qBAAe,MAAM,MAAM,MAAM,SAAS,QAAQ,CAAC,EAAE,MAAM,MAAM,IAAI;AAAA,IACvE;AACA,QAAI,iBAAiB,MAAM;AAGzB,aAAO;AAAA,IACT;AACA,UAAM,UAAU,QAAQ,IAAI;AAC5B,UAAM,cAAc,QAAQ,YAAY;AACxC,WAAO,YAAY,eAAe,QAAQ,WAAW,GAAG,WAAW,GAAGA,MAAK,GAAG,EAAE,IAAI,OAAO;AAAA,EAC7F;AAEA,iBAAe,QAAQ,KAAa,SAAiB,cAA4C;AAC/F,QAAI,QAAQ,SAAS;AACnB;AAAA,IACF;AACA,UAAM,UAAU,MAAM,MAAM,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC,CAAC,EAAE,MAAM,MAAM,IAAI;AACzF,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AACA,QAAI,SAAS;AACb,QAAI,QAAQ,gBAAgB,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW,GAAG;AACvE,YAAM,WAAW,mBAAmB,GAAG;AACvC,UAAI,SAAS,SAAS,GAAG;AACvB,iBAAS,CAAC,GAAG,cAAc,EAAE,SAAS,SAAS,CAAC;AAAA,MAClD;AAAA,IACF;AACA,UAAM,OAAwB,CAAC;AAC/B,eAAW,SAAS,SAAS;AAC3B,UAAI,QAAQ,SAAS;AACnB;AAAA,MACF;AACA,YAAM,MAAMA,MAAK,KAAK,KAAK,MAAM,IAAI;AACrC,YAAM,MAAM,UAAU,GAAG,OAAO,IAAI,MAAM,IAAI,KAAK,MAAM;AAGzD,UAAI,aAAsD;AAC1D,UAAI;AACJ,UAAI,MAAM,eAAe,GAAG;AAC1B,qBAAa,MAAM,MAAM,MAAMD,MAAK,GAAG,CAAC,EAAE,MAAM,MAAM,IAAI;AAC1D,YAAI,CAAC,cAAe,CAAC,WAAW,YAAY,KAAK,CAAC,WAAW,OAAO,GAAI;AACtE;AAAA,QACF;AACA,cAAM,OAAO,MAAM,YAAY,GAAG;AAClC,YAAI,SAAS,MAAM;AACjB;AAAA,QACF;AACA,mBAAW;AACX,YAAI,WAAW,YAAY,GAAG;AAC5B,gBAAM,MAAM,QAAQ,IAAI;AACxB,cAAI,QAAQ,IAAI,GAAG,GAAG;AACpB;AAAA,UACF;AACA,kBAAQ,IAAI,GAAG;AAAA,QACjB;AAAA,MACF;AACA,YAAM,QAAQ,MAAM,YAAY,MAAM,YAAY,YAAY,KAAK;AACnE,YAAM,SAAS,MAAM,OAAO,MAAM,YAAY,OAAO,KAAK;AAC1D,UAAI,OAAO;AACT,YAAI,SAAS,IAAI,MAAM,IAAI,KAAK,QAAQ,SAAS,KAAK,IAAI,GAAG;AAC3D;AAAA,QACF;AACA,YAAI,OAAO,SAAS,KAAK,aAAa,QAAQ,KAAK,IAAI,GAAG;AACxD;AAAA,QACF;AACA,aAAK,KAAK,QAAQ,KAAK,KAAK,MAAM,CAAC;AAAA,MACrC,WAAW,QAAQ;AACjB,YAAI,QAAQ,SAAS,KAAK,KAAK,GAAG;AAChC;AAAA,QACF;AACA,YAAI,OAAO,SAAS,KAAK,aAAa,QAAQ,KAAK,KAAK,GAAG;AACzD;AAAA,QACF;AACA,YAAI,QAAQ,cAAc,OAAO;AAC/B,cAAI;AAAA,YACF,aAAa,SAAY,EAAE,KAAK,KAAK,SAAS,EAAE,IAAI,EAAE,KAAK,KAAK,SAAS,GAAG,SAAS;AAAA,UACvF;AACA;AAAA,QACF;AACA,cAAM,KAAK,cAAe,MAAM,MAAM,MAAMA,MAAK,GAAG,CAAC,EAAE,MAAM,MAAM,IAAI;AACvE,YAAI,IAAI;AACN,cAAI;AAAA,YACF,aAAa,SACT,EAAE,KAAK,KAAK,SAAS,GAAG,QAAQ,IAChC,EAAE,KAAK,KAAK,SAAS,GAAG,SAAS,SAAS;AAAA,UAChD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,UAAM,QAAQ,IAAI,IAAI;AAAA,EACxB;AACA,QAAM,QAAQ,MAAM,IAAI,CAAC,CAAC;AAG1B,MAAI,KAAK,CAAC,GAAG,MAAO,EAAE,MAAM,EAAE,MAAM,KAAK,EAAE,MAAM,EAAE,MAAM,IAAI,CAAE;AAC/D,SAAO,EAAE,OAAO,KAAK,SAAS,QAAQ,WAAW,MAAM;AACzD;AAEA,IAAM,iBAAiB;AAEvB,SAAS,SAAS,GAAmB;AACnC,SAAO,EAAE,QAAQ,gBAAgB,MAAM;AACzC;AAKO,SAASE,cAAa,SAAyB;AACpD,MAAI,IAAI;AAER,WAAS,aAAqB;AAE5B,QAAI,IAAI,IAAI;AACZ,QAAI,SAAS;AACb,QAAI,QAAQ,OAAO,CAAC,MAAM,KAAK;AAC7B,eAAS;AACT,WAAK;AAAA,IACP;AACA,QAAI,OAAO;AAEX,QAAI,QAAQ,OAAO,CAAC,MAAM,KAAK;AAC7B,cAAQ;AACR,WAAK;AAAA,IACP;AACA,WAAO,IAAI,QAAQ,UAAU,QAAQ,OAAO,CAAC,MAAM,KAAK;AACtD,YAAM,KAAK,QAAQ,OAAO,CAAC;AAC3B,cAAQ,OAAO,OAAO,SAAS;AAC/B,WAAK;AAAA,IACP;AACA,QAAI,IAAI,QAAQ,QAAQ;AACtB,UAAI,IAAI;AAER,UAAI,KAAK,WAAW,GAAG,EAAG,QAAO,KAAK,IAAI;AAC1C,aAAO,IAAI,SAAS,MAAM,EAAE,GAAG,IAAI;AAAA,IACrC;AAEA,SAAK;AACL,WAAO;AAAA,EACT;AAEA,WAAS,aAAa,OAA4C;AAChE,QAAIC,MAAK;AACT,WAAO,IAAI,QAAQ,QAAQ;AACzB,YAAM,IAAI,QAAQ,OAAO,CAAC;AAC1B,UAAI,QAAQ,MAAM,MAAM,OAAO,MAAM,MAAM;AACzC,eAAO,EAAE,IAAAA,KAAI,KAAK,EAAE;AAAA,MACtB;AACA,UAAI,MAAM,KAAK;AACb,YAAI,QAAQ,OAAO,IAAI,CAAC,MAAM,KAAK;AACjC,eAAK;AACL,cAAI,QAAQ,OAAO,CAAC,MAAM,KAAK;AAC7B,iBAAK;AACL,YAAAA,OAAM;AACN,mBAAO,QAAQ,WAAW,OAAO,CAAC,GAAG;AACnC,mBAAK;AAAA,YACP;AAAA,UACF,OAAO;AACL,YAAAA,OAAM;AAAA,UACR;AAAA,QACF,OAAO;AACL,UAAAA,OAAM;AACN,eAAK;AAAA,QACP;AACA;AAAA,MACF;AACA,UAAI,MAAM,KAAK;AACb,QAAAA,OAAM;AACN,aAAK;AACL;AAAA,MACF;AACA,UAAI,MAAM,KAAK;AACb,QAAAA,OAAM,WAAW;AACjB;AAAA,MACF;AACA,UAAI,MAAM,OAAO,QAAQ,GAAG;AAC1B,cAAM,QAAQ,SAAS,CAAC;AACxB,YAAI,UAAU,IAAI;AAChB,UAAAA,OAAM;AACN,eAAK;AACL;AAAA,QACF;AACA,aAAK;AACL,cAAM,OAAiB,CAAC;AACxB,mBAAS;AACP,gBAAM,OAAO,aAAa,QAAQ,CAAC;AACnC,eAAK,KAAK,KAAK,EAAE;AACjB,cAAI,KAAK,QAAQ,IAAK;AACtB,eAAK;AAAA,QACP;AACA,aAAK;AACL,QAAAA,OAAM,MAAM,KAAK,KAAK,GAAG,CAAC;AAC1B;AAAA,MACF;AACA,MAAAA,OAAM,SAAS,CAAC;AAChB,WAAK;AAAA,IACP;AACA,WAAO,EAAE,IAAAA,KAAI,KAAK,GAAG;AAAA,EACvB;AAEA,WAAS,SAAS,MAAsB;AACtC,QAAI,QAAQ;AACZ,aAAS,IAAI,MAAM,IAAI,QAAQ,QAAQ,KAAK;AAC1C,YAAM,KAAK,QAAQ,OAAO,CAAC;AAC3B,UAAI,OAAO,IAAK,UAAS;AAAA,eAChB,OAAO,KAAK;AACnB,iBAAS;AACT,YAAI,UAAU,EAAG,QAAO;AAAA,MAC1B;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,EAAE,GAAG,IAAI,aAAa,CAAC;AAC7B,SAAO,IAAI,OAAO,IAAI,EAAE,GAAG;AAC7B;AAEO,SAAS,YAAY,SAAiB,SAA0B;AACrE,QAAM,aAAa,QAAQ,QAAQ,OAAO,GAAG;AAC7C,MAAI,CAAC,WAAW,SAAS,GAAG,GAAG;AAC7B,UAAM,OAAO,QAAQ,MAAM,GAAG,EAAE,IAAI,KAAK;AACzC,WAAOD,cAAa,UAAU,EAAE,KAAK,IAAI;AAAA,EAC3C;AACA,SAAOA,cAAa,UAAU,EAAE,KAAK,OAAO;AAC9C;AAEO,IAAM,cAAc;AAO3B,IAAM,cAAc,oBAAI,IAA4D;AAEpF,SAAS,oBAAoB,MAA+B;AAC1D,QAAM,MAAuB,CAAC;AAC9B,aAAW,OAAO,KAAK,MAAM,IAAI,GAAG;AAClC,UAAM,OAAO,IAAI,KAAK;AACtB,QAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,GAAG;AACjC;AAAA,IACF;AACA,QAAI,KAAK,SAAS,GAAG,GAAG;AACtB,YAAM,UAAU,KAAK,MAAM,GAAG,EAAE;AAChC,UAAI,SAAS;AACX,YAAI,KAAK,EAAE,SAAS,SAAS,KAAK,CAAC;AAAA,MACrC;AAAA,IACF,OAAO;AACL,UAAI,KAAK,EAAE,SAAS,MAAM,SAAS,MAAM,CAAC;AAAA,IAC5C;AAAA,EACF;AACA,SAAO;AACT;AAIO,SAAS,mBAAmB,KAA8B;AAC/D,QAAM,OAAOD,MAAK,KAAK,KAAK,WAAW;AACvC,MAAI;AACJ,MAAI;AACF,cAAU,SAAS,IAAI,EAAE;AAAA,EAC3B,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,MAAM,YAAY,IAAI,IAAI;AAChC,MAAI,OAAO,IAAI,YAAY,SAAS;AAClC,WAAO,IAAI;AAAA,EACb;AACA,MAAI,WAA4B,CAAC;AACjC,MAAI;AACF,eAAW,oBAAoB,aAAa,MAAM,MAAM,CAAC;AAAA,EAC3D,QAAQ;AACN,eAAW,CAAC;AAAA,EACd;AACA,cAAY,IAAI,MAAM,EAAE,SAAS,SAAS,CAAC;AAC3C,SAAO;AACT;AAKO,SAAS,sBAAsB,KAA0C;AAC9E,QAAM,WAAW,mBAAmB,GAAG;AACvC,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO;AAAA,EACT;AACA,QAAM,OAAOA,MAAK,QAAQ,GAAG;AAC7B,SAAO,CAAC,SAAS,UAAU;AACzB,UAAM,MAAMA,MAAK,SAAS,MAAM,OAAO,EAAE,MAAMA,MAAK,GAAG,EAAE,KAAK,GAAG;AACjE,QAAI,CAAC,OAAO,IAAI,WAAW,IAAI,GAAG;AAChC,aAAO;AAAA,IACT;AACA,eAAW,EAAE,SAAS,QAAQ,KAAK,UAAU;AAC3C,UAAI,WAAW,CAAC,OAAO;AACrB;AAAA,MACF;AACA,UAAI,YAAY,SAAS,GAAG,GAAG;AAC7B,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;;;AC9aA,OAAO,SAAS;AAChB,SAAS,KAAAG,UAAS;AAGlB,IAAM,oBAAoB;AAC1B,IAAM,YAAY,IAAI,OAAO;AAC7B,IAAM,aAAa;AACnB,IAAM,gBAAgB;AACtB,IAAM,sBAAsB;AAC5B,IAAM,YAAY,CAAC,IAAM,IAAM,IAAM,EAAI;AAEzC,IAAMC,UAASD,GAAE,OAAO;AAAA,EACtB,KAAKA,GAAE,OAAO,EAAE,SAAS,0BAA0B;AAAA,EACnD,UAAUA,GACP,OAAO,EACP,IAAI,EACJ,SAAS,EACT,SAAS,EACT,SAAS,sDAAsD;AACpE,CAAC;AAaD,IAAM,kBAAoC,OAAO,aAAa;AAC5D,QAAM,UAAU,MAAM,IAAI,SAAS,OAAO,UAAU,EAAE,KAAK,KAAK,CAAC;AACjE,SAAO,QAAQ,IAAI,CAAC,WAAW,OAAO,OAAO;AAC/C;AAKA,IAAI,8BAA8B;AAE3B,SAAS,6BAA6B,OAAsB;AACjE,gCAA8B;AAChC;AAEA,SAAS,cAAc,IAAqB;AAC1C,QAAM,QAAQ,GAAG,MAAM,GAAG,EAAE,IAAI,MAAM;AACtC,MAAI,MAAM,WAAW,KAAK,MAAM,KAAK,CAAC,MAAM,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,KAAK,IAAI,GAAG,GAAG;AACrF,WAAO;AAAA,EACT;AACA,QAAM,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI;AACvB,SACE,MAAM,KACN,MAAM,MACN,MAAM,OACL,MAAM,OAAO,KAAK,MAAM,KAAK,MAC7B,MAAM,OAAO,MAAM,OACnB,MAAM,OAAO,MAAM;AAExB;AAEA,SAAS,iBAAiB,SAA0B;AAClD,MAAI,KAAK,QAAQ,YAAY;AAC7B,MAAI,GAAG,WAAW,SAAS,EAAG,MAAK,GAAG,MAAM,UAAU,MAAM;AAC5D,MAAI,GAAG,SAAS,GAAG,EAAG,QAAO,cAAc,EAAE;AAC7C,MAAI,OAAO,SAAS,OAAO,KAAM,QAAO;AAExC,MAAI,GAAG,WAAW,IAAI,KAAK,GAAG,WAAW,IAAI,EAAG,QAAO;AACvD,MAAI,YAAY,KAAK,EAAE,EAAG,QAAO;AACjC,SAAO;AACT;AAEA,SAAS,gBAAgB,UAA2B;AAClD,QAAM,IAAI,SAAS,YAAY,EAAE,QAAQ,OAAO,EAAE;AAClD,SACE,MAAM,eAAe,EAAE,SAAS,YAAY,KAAK,EAAE,SAAS,QAAQ,KAAK,EAAE,SAAS,WAAW;AAEnG;AAKA,eAAe,mBAAmB,KAAU,SAAmD;AAC7F,QAAM,WAAW,IAAI,SAAS,QAAQ,YAAY,EAAE;AACpD,MAAI,gBAAgB,QAAQ,GAAG;AAC7B,WAAO,IAAI,QAAQ;AAAA,EACrB;AACA,MAAI;AACJ,MAAI;AACF,gBAAY,MAAM,QAAQ,QAAQ;AAAA,EACpC,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAO,sBAAsB,QAAQ,MAAM,OAAO;AAAA,EACpD;AACA,QAAM,UAAU,UAAU,KAAK,gBAAgB;AAC/C,MAAI,YAAY,QAAW;AACzB,WAAO,IAAI,QAAQ,6CAA6C,OAAO;AAAA,EACzE;AACA,MAAI,UAAU,WAAW,GAAG;AAC1B,WAAO,sBAAsB,QAAQ;AAAA,EACvC;AACA,SAAO;AACT;AAIA,SAAS,cAAc,GAAW,KAAqB;AACrD,MAAI,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,KAAK,IAAI,SAAU;AACjD,WAAO;AAAA,EACT;AACA,SAAO,OAAO,cAAc,CAAC;AAC/B;AAEO,SAAS,eAAe,GAAmB;AAChD,SAAO,EACJ,QAAQ,aAAa,CAAC,GAAG,MAAM,cAAc,OAAO,CAAC,GAAG,CAAC,CAAC,EAC1D,QAAQ,wBAAwB,CAAC,GAAG,MAAM,cAAc,OAAO,SAAS,GAAG,EAAE,GAAG,CAAC,CAAC,EAClF,QAAQ,wBAAwB,CAAC,MAAM;AACtC,YAAQ,GAAG;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF,CAAC,EACA,QAAQ,UAAU,GAAG;AAC1B;AAMA,SAAS,cAAc,MAAc,MAAkC;AACrE,MAAI,YAAY;AAChB,SAAO,KAAK;AAAA,IACV;AAAA,IACA,CAAC,QAAQ,cAAc,cAAc,UAAU;AAC7C,YAAM,UAAU,OAAO,KAAK,EAAE,QAAQ,YAAY,GAAG;AACrD,YAAM,OAAO,eAAe,OAAO,gBAAgB,gBAAgB,EAAE,CAAC,EAAE,KAAK;AAC7E,UAAI,aAAa,uBAAuB,SAAS,MAAM,KAAK,WAAW,GAAG,GAAG;AAC3E,eAAO;AAAA,MACT;AACA,UAAI;AACJ,UAAI;AACF,mBAAW,IAAI,IAAI,MAAM,IAAI;AAAA,MAC/B,QAAQ;AACN,eAAO;AAAA,MACT;AACA,UAAI,SAAS,aAAa,WAAW,SAAS,aAAa,UAAU;AACnE,eAAO;AAAA,MACT;AACA,YAAM,OAAO,eAAe,OAAO,EAAE,QAAQ,QAAQ,GAAG,EAAE,KAAK;AAC/D,UAAI,SAAS,IAAI;AACf,qBAAa;AACb,eAAO,SAAS;AAAA,MAClB;AACA,UAAI,SAAS,SAAS,QAAQ,SAAS,MAAM;AAC3C,eAAO;AAAA,MACT;AACA,mBAAa;AACb,aAAO,GAAG,OAAO,KAAK,SAAS,IAAI;AAAA,IACrC;AAAA,EACF;AACF;AAEA,SAAS,WAAW,MAAc,MAAuB;AACvD,QAAM,WAAW,KAAK,QAAQ,6DAA6D,GAAG;AAC9F,QAAM,YAAY,cAAc,UAAU,IAAI;AAC9C,QAAM,aAAa,UAAU;AAAA,IAC3B;AAAA,IACA;AAAA,EACF;AACA,QAAM,WAAW,eAAe,WAAW,QAAQ,YAAY,GAAG,CAAC;AACnE,SAAO,SACJ,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,KAAK,QAAQ,QAAQ,GAAG,EAAE,KAAK,CAAC,EAC9C,KAAK,IAAI,EACT,QAAQ,WAAW,MAAM,EACzB,KAAK;AACV;AAMA,SAAS,eAAe,mBAA6D;AACnF,QAAM,QAAQ,kBAAkB,MAAM,+BAA+B;AACrE,QAAM,QAAQ,QAAQ,CAAC,GAAG,YAAY,KAAK;AAC3C,MAAI,UAAU,WAAW,UAAU,QAAQ;AACzC,WAAO,IAAI,YAAY,OAAO;AAAA,EAChC;AACA,MAAI;AACF,WAAO,IAAI,YAAY,KAAK;AAAA,EAC9B,QAAQ;AACN,WAAO,IAAI,YAAY,OAAO;AAAA,EAChC;AACF;AAEA,eAAe,SACb,KACA,UACA,SACiB;AACjB,MAAI,CAAC,IAAI,MAAM;AACb,WAAO;AAAA,EACT;AACA,QAAM,SAAS,IAAI,KAAK,UAAU;AAClC,MAAI,QAAQ;AACZ,MAAI,OAAO;AACX,MAAI;AACF,eAAS;AACP,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,MAAM;AACR;AAAA,MACF;AACA,eAAS,MAAM;AACf,cAAQ,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAC9C,UAAI,SAAS,aAAa,KAAK,UAAU,WAAW,GAAG;AACrD;AAAA,MACF;AAAA,IACF;AACA,YAAQ,QAAQ,OAAO;AAAA,EACzB,UAAE;AACA,WAAO,OAAO,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAChC;AACA,SAAO;AACT;AAIA,eAAe,WAAW,KAAe,GAAgC;AACvE,MAAI,CAAC,IAAI,MAAM;AACb,WAAO,IAAI,WAAW,CAAC;AAAA,EACzB;AACA,QAAM,SAAS,IAAI,KAAK,UAAU;AAClC,QAAM,MAAM,IAAI,WAAW,CAAC;AAC5B,MAAI,QAAQ;AACZ,MAAI;AACF,WAAO,QAAQ,GAAG;AAChB,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,MAAM;AACR;AAAA,MACF;AACA,YAAM,OAAO,KAAK,IAAI,MAAM,YAAY,IAAI,KAAK;AACjD,UAAI,IAAI,MAAM,SAAS,GAAG,IAAI,GAAG,KAAK;AACtC,eAAS;AAAA,IACX;AAAA,EACF,UAAE;AACA,WAAO,OAAO,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAChC;AACA,SAAO,IAAI,SAAS,GAAG,KAAK;AAC9B;AAEA,SAAS,YAAY,OAA4B;AAC/C,SAAO,UAAU,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;AACjD;AAIA,SAAS,SAAS,KAA8C;AAC9D,SAAO;AAAA,IACL,SAAS,6CAA6C,IAAI,IAAI;AAAA,IAC9D,SAAS;AAAA,EACX;AACF;AAEA,SAAS,SAAS,GAAW,UAA0B;AACrD,MAAI,EAAE,UAAU,UAAU;AACxB,WAAO;AAAA,EACT;AACA,SAAO,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC;AAAA,4BAA+B,QAAQ,OAAO,EAAE,MAAM;AACtF;AAEO,SAAS,mBAAmB,UAA+B,CAAC,GAAwB;AACzF,QAAM,UAAU,QAAQ,YAAY;AACpC,QAAM,YAAY,QAAQ,cAAc,IAAI,SAAmC,MAAM,GAAG,IAAI;AAC5F,QAAM,eAAe,MAAM,QAAQ,qBAAqB;AACxD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aACE;AAAA,IACF,YAAY;AAAA,IACZ,YAAYC;AAAA,IACZ,MAAM,QAAQ,MAAM,KAAK;AACvB,UAAI;AACJ,UAAI;AACF,cAAM,IAAI,IAAI,KAAK,GAAG;AAAA,MACxB,QAAQ;AACN,eAAO,EAAE,SAAS,uBAAuB,KAAK,GAAG,IAAI,SAAS,KAAK;AAAA,MACrE;AACA,UAAI,IAAI,aAAa,WAAW,IAAI,aAAa,UAAU;AACzD,eAAO;AAAA,UACL,SAAS,gDAAgD,IAAI,QAAQ;AAAA,UACrE,SAAS;AAAA,QACX;AAAA,MACF;AACA,UAAI,CAAC,aAAa,GAAG;AACnB,cAAM,UAAU,MAAM,mBAAmB,KAAK,OAAO;AACrD,YAAI,YAAY,MAAM;AACpB,iBAAO,EAAE,SAAS,2BAA2B,KAAK,GAAG,KAAK,OAAO,IAAI,SAAS,KAAK;AAAA,QACrF;AAAA,MACF;AACA,YAAM,WAAW,KAAK,YAAY;AAClC,YAAM,gBAAgB,YAAY,QAAQ,UAAU;AACpD,YAAM,SAAS,IAAI,cACf,YAAY,IAAI,CAAC,eAAe,IAAI,WAAW,CAAC,IAChD;AAGJ,UAAI;AACJ,UAAI,UAAU;AACd,eAAS,OAAO,KAAK,QAAQ,GAAG;AAC9B,YAAI;AACF,gBAAM,MAAM,UAAU,SAAS,EAAE,QAAQ,UAAU,SAAS,CAAC;AAAA,QAC/D,SAAS,KAAK;AACZ,cAAI,IAAI,aAAa,SAAS;AAC5B,mBAAO,EAAE,SAAS,0BAA0B,SAAS,KAAK;AAAA,UAC5D;AACA,cAAI,cAAc,SAAS;AACzB,mBAAO;AAAA,cACL,SAAS,kCAAkC,aAAa,GAAI;AAAA,cAC5D,SAAS;AAAA,YACX;AAAA,UACF;AACA,gBAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,iBAAO,EAAE,SAAS,0BAA0B,QAAQ,IAAI,KAAK,OAAO,IAAI,SAAS,KAAK;AAAA,QACxF;AACA,cAAM,WAAW,IAAI,QAAQ,IAAI,UAAU;AAC3C,YAAI,IAAI,UAAU,OAAO,IAAI,SAAS,OAAO,YAAY,OAAO,eAAe;AAC7E,gBAAM,IAAI,MAAM,OAAO,EAAE,MAAM,MAAM;AAAA,UAAC,CAAC;AACvC,cAAI;AACJ,cAAI;AACF,mBAAO,IAAI,IAAI,UAAU,OAAO;AAAA,UAClC,QAAQ;AACN,mBAAO,EAAE,SAAS,qCAAqC,QAAQ,IAAI,SAAS,KAAK;AAAA,UACnF;AACA,cAAI,KAAK,aAAa,WAAW,KAAK,aAAa,UAAU;AAC3D,mBAAO;AAAA,cACL,SAAS,+CAA+C,KAAK,QAAQ;AAAA,cACrE,SAAS;AAAA,YACX;AAAA,UACF;AACA,cAAI,CAAC,aAAa,GAAG;AACnB,kBAAM,UAAU,MAAM,mBAAmB,MAAM,OAAO;AACtD,gBAAI,YAAY,MAAM;AACpB,qBAAO;AAAA,gBACL,SAAS,8BAA8B,KAAK,IAAI,KAAK,OAAO;AAAA,gBAC5D,SAAS;AAAA,cACX;AAAA,YACF;AAAA,UACF;AACA,oBAAU;AACV;AAAA,QACF;AACA;AAAA,MACF;AACA,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,OAAO,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AACvC,eAAO;AAAA,UACL,SAAS,eAAe,IAAI,MAAM,IAAI,IAAI,UAAU,QAAQ,QAAQ,IAAI;AAAA,UACxE,SAAS;AAAA,QACX;AAAA,MACF;AACA,YAAM,oBAAoB,IAAI,QAAQ,IAAI,cAAc,KAAK;AAC7D,YAAM,CAAC,UAAU,EAAE,IAAI,kBAAkB,MAAM,GAAG;AAClD,YAAM,cAAc,QAAQ,KAAK,EAAE,YAAY;AAC/C,YAAM,SAAS,gBAAgB,eAAe,gBAAgB;AAC9D,YAAM,SACJ,YAAY,WAAW,OAAO,KAC9B,gBAAgB,sBAChB,gBAAgB,qBAChB,YAAY,SAAS,OAAO,KAC5B,YAAY,SAAS,MAAM;AAC7B,UAAI,gBAAgB,mBAAmB;AACrC,cAAM,IAAI,MAAM,OAAO,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AACvC,eAAO,SAAS,OAAO;AAAA,MACzB;AACA,UAAI,CAAC,UAAU,CAAC,QAAQ;AAGtB,cAAM,SAAS,MAAM,WAAW,KAAK,UAAU,MAAM;AACrD,YAAI,YAAY,MAAM,GAAG;AACvB,iBAAO,SAAS,OAAO;AAAA,QACzB;AACA,eAAO;AAAA,UACL,SAAS,oCAAoC,WAAW,KAAK,QAAQ,IAAI;AAAA,UACzE,SAAS;AAAA,QACX;AAAA,MACF;AACA,UAAI;AACJ,UAAI;AACF,eAAO,MAAM,SAAS,KAAK,UAAU,eAAe,iBAAiB,CAAC;AAAA,MACxE,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,eAAO,EAAE,SAAS,wCAAwC,OAAO,IAAI,SAAS,KAAK;AAAA,MACrF;AACA,UAAI,KAAK,WAAW,MAAM,GAAG;AAC3B,eAAO,SAAS,OAAO;AAAA,MACzB;AACA,YAAM,OAAO,SAAS,WAAW,MAAM,QAAQ,IAAI,IAAI;AACvD,aAAO,EAAE,SAAS,SAAS,MAAM,QAAQ,EAAE;AAAA,IAC7C;AAAA,EACF;AACF;AAEO,IAAM,eAAoC,mBAAmB;","names":["spawn","EventEmitter","path","path","MAX_FINISHED_RECORDS","EventEmitter","spawn","os","path","path","mkdir","readFile","rm","writeFile","path","z","randomUUID","readFile","writeFile","os","path","path","os","randomUUID","writeFile","readFile","stat","path","globToRegExp","re","z","schema"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/messages.ts","../src/config/schema.ts","../src/snapshot/git-tree.ts"],"sourcesContent":["import type { CoreMessage, ToolCallPart } from \"ai\";\n\nexport type { CoreMessage };\n\n// Side-band metadata the model never sees. It rides next to a persisted\n// message (the __starMeta jsonl key) so turn-boundary scans can tell a real\n// user turn from a loop-injected user message:\n// - \"nudge\": auto-continue / resume-after-cutoff steering injected mid-turn\n// - \"bg-report\": a finished background subagent's report, delivered mid-turn\n// - \"tool-image\": images a tool (read_image/screenshot) attached mid-turn\nexport interface MessageMeta {\n synthetic?: \"nudge\" | \"bg-report\" | \"tool-image\";\n // Set by context/elision.ts when the message's bulky content (a stale tool\n // result, an old attached image) was replaced with a placeholder to free\n // the window — idempotency marker and debugging aid; the model never sees it.\n elided?: boolean;\n}\n\nexport interface StarMessage {\n message: CoreMessage;\n meta?: MessageMeta;\n}\n\nexport function toStarMessage(message: CoreMessage | StarMessage): StarMessage {\n return \"role\" in message ? { message } : message;\n}\n\nexport function toCoreMessages(messages: readonly StarMessage[]): CoreMessage[] {\n return messages.map((star) => star.message);\n}\n\n// A synthetic user message belongs to the turn that precedes it: it is never\n// a turn boundary for /undo, /rewind or compaction turn splitting.\nexport function isSyntheticUserMessage(star: StarMessage): boolean {\n return star.message.role === \"user\" && star.meta?.synthetic !== undefined;\n}\n\n// Plain-text content of a message: string content as-is; for array content,\n// the text parts joined with a space (tool calls, images and tool results\n// contribute nothing). Shared by the agent loop (/redo labels) and the CLI\n// (history rendering, conversation copy) so the extraction rule lives once.\nexport function coreMessageText(message: CoreMessage | undefined): string {\n if (!message) return \"\";\n const content = message.content;\n if (typeof content === \"string\") return content;\n if (Array.isArray(content)) {\n return content\n .filter((part) => part.type === \"text\")\n .map((part) => (\"text\" in part ? part.text : \"\"))\n .join(\" \");\n }\n return \"\";\n}\n\nexport interface ImageInput {\n path: string;\n mimeType: string;\n /** Base64-encoded image bytes. */\n data: string;\n}\n\nexport interface MultimodalInput {\n text: string;\n images: ImageInput[];\n}\n\nexport type ChatInput = string | MultimodalInput;\n\nexport interface ConversationState {\n messages: CoreMessage[];\n}\n\n// A tool call that has no real execution result must still be closed with a\n// synthetic tool message, or the stored history can no longer be sent to the\n// API. Three scenarios, three wordings:\n// - MISSING_TOOL_RESULT_TEXT: reconcileToolCalls/reconcileStarMessages repair\n// a persisted history whose session ended before the results were written\n// (crash, kill, older version) — the call may never have run at all.\n// - INTERRUPTED_TOOL_RESULT_TEXT: the user pressed Esc while the call was\n// still streaming in or executing (agent loop abort paths).\n// - UNFINISHED_TOOL_RESULT_TEXT: execution stopped for any other reason\n// (e.g. an error mid-batch) before the call produced a result.\nexport const MISSING_TOOL_RESULT_TEXT = \"tool result missing (session was interrupted)\";\nexport const INTERRUPTED_TOOL_RESULT_TEXT = \"Tool execution interrupted by user.\";\nexport const UNFINISHED_TOOL_RESULT_TEXT =\n \"Tool execution interrupted before a result was produced.\";\n\nexport function reconcileToolCalls(messages: CoreMessage[]): CoreMessage[] {\n const result: CoreMessage[] = [];\n let pending: { toolCallId: string; toolName: string }[] = [];\n // Non-tool messages that arrived while a tool-call batch was still open\n // (e.g. tool-attached image messages persisted by a version that\n // interleaved them between sibling tool results) are held back and emitted\n // once the batch closes: strict providers reject a user message sitting\n // inside the tool-result block (\"an assistant message with 'tool_calls'\n // must be followed by tool messages\"), and flushing synthetic fillers at\n // that point would also duplicate the real results that follow.\n let deferred: CoreMessage[] = [];\n\n const flushPending = () => {\n for (const call of pending) {\n result.push({\n role: \"tool\",\n content: [\n {\n type: \"tool-result\",\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n result: MISSING_TOOL_RESULT_TEXT,\n },\n ],\n });\n }\n pending = [];\n result.push(...deferred);\n deferred = [];\n };\n\n for (const message of messages) {\n if (message.role === \"assistant\" && Array.isArray(message.content)) {\n flushPending();\n pending = message.content\n .filter((part): part is ToolCallPart => part.type === \"tool-call\")\n .map((part) => ({ toolCallId: part.toolCallId, toolName: part.toolName }));\n result.push(message);\n } else if (message.role === \"tool\" && Array.isArray(message.content) && pending.length > 0) {\n const answered = new Set(message.content.map((part) => part.toolCallId));\n pending = pending.filter((call) => !answered.has(call.toolCallId));\n result.push(message);\n if (pending.length === 0) {\n result.push(...deferred);\n deferred = [];\n }\n } else if (pending.length > 0) {\n deferred.push(message);\n } else {\n result.push(message);\n }\n }\n flushPending();\n return result;\n}\n\n// StarMessage counterpart of reconcileToolCalls: same repair, preserving the\n// side-band meta of every surviving message. Synthetic filler results carry\n// no meta.\nexport function reconcileStarMessages(messages: readonly StarMessage[]): StarMessage[] {\n const result: StarMessage[] = [];\n let pending: { toolCallId: string; toolName: string }[] = [];\n let deferred: StarMessage[] = [];\n\n const flushPending = () => {\n for (const call of pending) {\n result.push({\n message: {\n role: \"tool\",\n content: [\n {\n type: \"tool-result\",\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n result: MISSING_TOOL_RESULT_TEXT,\n },\n ],\n },\n });\n }\n pending = [];\n result.push(...deferred);\n deferred = [];\n };\n\n for (const star of messages) {\n const message = star.message;\n if (message.role === \"assistant\" && Array.isArray(message.content)) {\n flushPending();\n pending = message.content\n .filter((part): part is ToolCallPart => part.type === \"tool-call\")\n .map((part) => ({ toolCallId: part.toolCallId, toolName: part.toolName }));\n result.push(star);\n } else if (message.role === \"tool\" && Array.isArray(message.content) && pending.length > 0) {\n const answered = new Set(message.content.map((part) => part.toolCallId));\n pending = pending.filter((call) => !answered.has(call.toolCallId));\n result.push(star);\n if (pending.length === 0) {\n result.push(...deferred);\n deferred = [];\n }\n } else if (pending.length > 0) {\n deferred.push(star);\n } else {\n result.push(star);\n }\n }\n flushPending();\n return result;\n}\n\n// Removes the last conversation turn: the final user message and everything\n// after it (assistant text, tool calls and results belonging to that turn).\n// Synthetic user messages (auto-continue nudges, background reports, tool\n// images) belong to the turn they sit in, so the scan skips them and the\n// boundary lands on the real user message that opened the turn. A leading\n// system message is never touched. Returns the trimmed list and how many\n// messages were dropped (0 = nothing to retract).\nexport function retractLastTurn(messages: StarMessage[]): {\n messages: StarMessage[];\n removed: number;\n} {\n for (let i = messages.length - 1; i >= 0; i--) {\n const star = messages[i];\n if (star && star.message.role === \"user\" && !isSyntheticUserMessage(star)) {\n return { messages: messages.slice(0, i), removed: messages.length - i };\n }\n }\n return { messages, removed: 0 };\n}\n","import { z } from \"zod\";\n\nexport const ProviderConfigSchema = z.object({\n name: z.string(),\n // openai-responses talks to the Responses API (/responses) instead of chat\n // completions — the wire format codex uses, and the native API of\n // reasoning-model relays that otherwise hide reasoning behind empty chat\n // completions. streamChat sends strictSchemas:false + store:false with it.\n protocol: z\n .enum([\"openai-compatible\", \"anthropic\", \"openai-responses\"])\n .default(\"openai-compatible\"),\n baseURL: z.string(),\n apiKeyEnv: z.string().optional(),\n apiKey: z.string().optional(),\n headers: z.record(z.string()).optional(),\n});\n\nexport const ModelConfigSchema = z.object({\n name: z.string(),\n provider: z.string(),\n model: z.string(),\n maxTokens: z.number().int().positive().optional(),\n // Per-model context window; overrides the top-level contextMaxTokens for\n // compaction and the status bar when set.\n contextMaxTokens: z.number().int().positive().optional(),\n // Reasoning intensity for thinking models, forwarded verbatim as\n // OpenAI-style reasoning_effort on every request (llm/provider.ts\n // reasoningEffortMetadata) — level naming varies by model/provider\n // (low|medium|high are common; some add minimal/max/xhigh/none), so any\n // non-empty string is accepted and the server validates it. Unset = the\n // request carries no effort field and the server-side default applies.\n // Anthropic maps effort to a thinking budget instead — ignored there.\n reasoningEffort: z.string().min(1).optional(),\n // Sampling temperature forwarded on every request. Unset = the AI SDK's\n // own default applies (ai@4 sends 0 — it does NOT omit the field). Some\n // endpoints reject anything but one explicit value (e.g. kimi-for-coding:\n // \"invalid temperature: only 1 is allowed\") — set it here. No upper\n // bound: ranges vary by provider and the server validates.\n temperature: z.number().min(0).optional(),\n // Per-model overrides of the top-level stream watchdog timeouts (unset =\n // the global values apply). Raise streamIdleTimeoutSec for relays that\n // buffer long generations and stall mid-stream between flushes.\n streamIdleTimeoutSec: z.number().positive().optional(),\n streamFirstChunkTimeoutSec: z.number().positive().optional(),\n // Text-only models: set false so image-producing tools (read_image,\n // screenshot) decline with a text error instead of attaching images the\n // endpoint would reject with an unrecoverable 4xx. Unset = images allowed.\n vision: z.boolean().optional(),\n // Cost estimation needs BOTH prices (USD per 1M tokens); with only one set\n // the model is treated as unpriced.\n promptPrice: z.number().nonnegative().optional(),\n completionPrice: z.number().nonnegative().optional(),\n // Optional price (USD per 1M tokens) for cache-read prompt tokens. When\n // unset, OpenAI-style cached tokens bill at promptPrice and Anthropic-style\n // cache reads are not billed — set this for accurate cache pricing.\n cacheReadPrice: z.number().nonnegative().optional(),\n});\n\nexport const PermissionsConfigSchema = z.object({\n allow: z.array(z.string()).default([]),\n deny: z.array(z.string()).default([]),\n // Rules that force a confirmation prompt in auto mode (same tool(glob)\n // syntax as allow/deny) — e.g. ask = [\"bash(git push *)\"]. Priority:\n // deny > ask > allow > mode default. Only meaningful in auto mode (ask\n // mode already prompts; readonly/plan are unaffected; yolo bypasses all).\n ask: z.array(z.string()).default([]),\n // Extra likely-secret file globs extending core/sensitive.ts's hardcoded\n // list (same single-* glob semantics as rules, matched case-insensitively\n // against the basename and the full normalized path). Global config only —\n // the project-config sandbox drops the whole [permissions] table.\n sensitive: z.array(z.string()).default([]),\n});\n\nexport const HookConfigSchema = z.object({\n event: z.enum([\"PreToolUse\", \"PostToolUse\", \"Stop\"]),\n matcher: z\n .string()\n .refine(\n (pattern) => {\n try {\n new RegExp(pattern);\n return true;\n } catch {\n return false;\n }\n },\n { message: \"invalid regular expression\" },\n )\n .optional(),\n command: z.string().min(1),\n timeoutSec: z.number().positive().default(30),\n});\n\nexport const ConfigSchema = z.object({\n defaultModel: z.string().default(\"\"),\n // Optional [[models]] name for cheap auxiliary LLM calls (compaction\n // summaries, session titles, turn completion checks) — Claude Code routes\n // these to a Haiku-class model. Unset = the active main model does them.\n smallModel: z.string().optional(),\n permissionMode: z.enum([\"auto\", \"ask\", \"readonly\", \"yolo\", \"plan\"]).default(\"ask\"),\n providers: z.array(ProviderConfigSchema).default([]),\n models: z.array(ModelConfigSchema).default([]),\n // Cap on model round-trips (\"steps\") per turn between progress checkpoints.\n // One step is a streamed reply plus every tool call it asked for — stream\n // retries and auto-continue nudges do not consume steps. Reaching the cap\n // while tools still execute resets the budget with a notice; the turn stops\n // only when no tool ran since the last checkpoint (the doom-loop\n // signature). 0 disables the cap entirely (opencode/codex have none).\n maxSteps: z.number().int().min(0).default(100),\n contextMaxTokens: z.number().int().positive().default(100_000),\n // Token threshold at which the agent loop auto-compacts the history.\n // Defaults to the effective context window (compact only when full); set a\n // lower value to compact earlier and keep headroom. Manual /compact ignores\n // this — it always compacts.\n compactThresholdTokens: z.number().int().positive().optional(),\n // Seconds without any stream part before a stalled response is ended\n // gracefully (some relays never send the terminal chunks). Applies once\n // content has started streaming; the wait for the first content part gets\n // a longer allowance (control/metadata and reasoning parts don't count as\n // content).\n streamIdleTimeoutSec: z.number().positive().default(60),\n // Seconds to wait for the very first content part before giving up; slow\n // thinking-model endpoints can stay silent for minutes after accepting\n // the request.\n streamFirstChunkTimeoutSec: z.number().positive().default(300),\n // Extra attempts per model request when a stream fails transiently\n // (network error, 429/5xx, idle watchdog cutoff) or comes back empty, so a\n // relay hiccup does not silently end a half-finished turn. 0 disables.\n // The wait between attempts honors Retry-After headers and otherwise backs\n // off exponentially with jitter (llm/retry.ts); retries after a\n // timeout/empty failure also scale both stream timeouts up (attempt number\n // ×, capped at 3x) since the relay is likely overloaded. Default 5 matches\n // opencode/Codex retry budgets and rides out flaky relays (中转站) better\n // than a minimal budget.\n streamMaxRetries: z.number().int().min(0).default(5),\n // How many consecutive unproductive replies may be nudged before the turn\n // is handed back: a text-only reply that announces pending work (\"我将…\",\n // \"I will…\") or answers a nudge with more words, or an empty reply that\n // survived the stream retry budget (steered with a nudge — a changed\n // request — since identical resends already failed). Tool calls reset the\n // count, so a productive task keeps going; exhaustion surfaces a notice\n // instead of stopping silently. Never fires in plan mode. 0 disables.\n maxAutoContinues: z.number().int().min(0).default(2),\n contextCompaction: z.enum([\"summary\", \"truncate\"]).default(\"summary\"),\n // Optional per-session dollar budget; when set, the REPL stops the session\n // once the estimated cost for the session reaches this amount.\n sessionBudgetUsd: z.number().positive().optional(),\n // Terminal bell (REPL only): ring when a turn takes longer than\n // notifyBellThresholdSec, and when a background task finishes.\n // STAR_NO_NOTIFY=1 disables without touching the config.\n notifyBell: z.boolean().default(true),\n notifyBellThresholdSec: z.number().positive().default(10),\n permissions: PermissionsConfigSchema.default({ allow: [], deny: [] }),\n hooks: z.array(HookConfigSchema).default([]),\n // Consecutive identical tool calls (same tool + same arguments) allowed\n // before the loop refuses to execute the repeat and tells the model to\n // change approach — a guard against agents stuck retrying a failing call\n // (opencode's doom_loop). 0 disables.\n doomLoopThreshold: z.number().int().min(0).default(3),\n // Track the whole working tree with an internal git repo (outside the\n // user's .git) at every turn boundary, so /undo and /redo can restore\n // changes made by ANY tool (bash included), not just write_file/edit_file.\n // Falls back silently to per-file snapshots when git is unavailable.\n gitSnapshots: z.boolean().default(true),\n // web_fetch SSRF guard escape hatch: by default target hosts are resolved\n // before connecting and private/reserved ranges are refused (loopback,\n // RFC1918, link-local incl. the cloud metadata address 169.254.169.254,\n // ULA, and localhost/.local/.internal names; redirect hops re-checked).\n // true disables the guard for intranet use. Global config only — not in\n // the project-config sandbox whitelist.\n webFetchAllowPrivateHosts: z.boolean().default(false),\n});\n\nexport type ProviderConfig = z.infer<typeof ProviderConfigSchema>;\nexport type ModelConfig = z.infer<typeof ModelConfigSchema>;\nexport type PermissionsConfig = z.infer<typeof PermissionsConfigSchema>;\nexport type HookConfig = z.infer<typeof HookConfigSchema>;\nexport type StarConfig = z.infer<typeof ConfigSchema>;\n\n// Effective context window for a model: its own contextMaxTokens when set,\n// otherwise the top-level config.contextMaxTokens.\nexport function contextWindowTokens(config: StarConfig, modelName: string): number {\n return (\n config.models.find((m) => m.name === modelName)?.contextMaxTokens ?? config.contextMaxTokens\n );\n}\n\n// Token threshold that triggers automatic compaction in the agent loop:\n// config.compactThresholdTokens when set, clamped to the window so a stale\n// value (e.g. after switching to a smaller model) never pushes compaction\n// past it; otherwise the window itself (current behavior).\nexport function resolveCompactThreshold(config: StarConfig, windowTokens: number): number {\n return Math.min(config.compactThresholdTokens ?? windowTokens, windowTokens);\n}\n\nexport interface CliOverrides {\n model?: string;\n permissionMode?: \"auto\" | \"ask\" | \"readonly\" | \"yolo\" | \"plan\";\n}\n","import { execFile } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport { existsSync } from \"node:fs\";\nimport { mkdir, readdir, rm, writeFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { gitTreesDir } from \"../config/paths\";\nimport { isSensitivePath } from \"../core/sensitive\";\n\nconst GIT_TIMEOUT_MS = 10_000;\n// gc repacks the object store and legitimately takes longer than the budget\n// every other git invocation gets; it runs rarely, so it can afford to wait.\nconst GC_TIMEOUT_MS = 60_000;\nconst GIT_MAX_BUFFER = 8 * 1024 * 1024;\n// `git gc --auto` runs after this many successful tree captures per repo\n// (each capture adds a tree plus blobs, and nothing else ever prunes them).\nconst GC_INTERVAL = 50;\n// The agent loop calls trackTree at the start of every turn; without a\n// negative cache a directory whose tracking fails (huge tree hitting the git\n// timeout, unreadable repo) would burn that timeout on every single turn.\nconst TRACK_FAILURE_TTL_MS = 5 * 60 * 1000;\n// Every captured tree is pinned under refs/star/<timestamp>-<random> so a\n// `gc --auto` prune (default gc.pruneExpire of two weeks) can never drop a\n// live /undo target. The namespace is trimmed to the newest this many refs,\n// matching the per-file snapshot stack cap.\nconst MAX_PINNED_REFS = 50;\nconst PINNED_REF_PREFIX = \"refs/star/\";\n\n// Likely-secret files that must never enter the internal snapshot repos,\n// pinned into each repo's info/exclude so `git add -A` skips them even in\n// projects with no .gitignore and `git clean -fd` on restore leaves them\n// alone. Mirrors isSensitivePath (src/core/sensitive.ts), including the\n// template exemptions.\nconst SENSITIVE_EXCLUDE_PATTERNS = [\n \".env\",\n \".env.*\",\n \"!.env.example\",\n \"!.env.sample\",\n \"!.env.template\",\n \"id_rsa\",\n \"id_dsa\",\n \"id_ecdsa\",\n \"id_ed25519\",\n \".npmrc\",\n \".netrc\",\n \".pgpass\",\n \"*.pem\",\n \"*.key\",\n \"*.p12\",\n \"*.pfx\",\n \"**/.aws/credentials\",\n \"**/.kube/config\",\n];\n\n// Forced config for every invocation: no CRLF translation (a file captured\n// with LF endings must restore byte-identically even on a machine whose\n// global config sets core.autocrlf=true) and no LFS filters rewriting blob\n// contents. Command-line -c beats repo/global/system config, so repos\n// created before this fix are covered too.\nconst GIT_CONFIG_ARGS = [\n \"-c\",\n \"core.autocrlf=false\",\n \"-c\",\n \"core.eol=native\",\n \"-c\",\n \"filter.lfs.process=\",\n \"-c\",\n \"filter.lfs.smudge=\",\n \"-c\",\n \"filter.lfs.clean=\",\n];\n\n// The child gets the parent environment minus every GIT_* variable (matched\n// case-insensitively: Windows env keys are). A star launched from a git hook\n// or rebase inherits GIT_DIR/GIT_INDEX_FILE pointing at the user's repo, and\n// GIT_INDEX_FILE redirects index writes even past an explicit --git-dir.\nfunction gitEnv(): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = {};\n for (const [key, value] of Object.entries(process.env)) {\n if (value !== undefined && !key.toUpperCase().startsWith(\"GIT_\")) env[key] = value;\n }\n return env;\n}\n\n// Every git invocation goes through here: array arguments (never a shell), a\n// per-call timeout, and any failure — git missing, repo corrupt, timeout —\n// resolves null so the snapshot feature degrades silently instead of breaking\n// the agent loop.\nfunction runGit(args: string[], timeoutMs = GIT_TIMEOUT_MS): Promise<string | null> {\n return new Promise((resolve) => {\n execFile(\n \"git\",\n [...GIT_CONFIG_ARGS, ...args],\n { timeout: timeoutMs, maxBuffer: GIT_MAX_BUFFER, encoding: \"utf8\", env: gitEnv() },\n (error, stdout) => {\n resolve(error ? null : stdout.trim());\n },\n );\n });\n}\n\nlet gitAvailable: boolean | null = null;\n\n// Expiry timestamps (Date.now) of the per-directory failure negative cache.\nconst trackFailureUntil = new Map<string, number>();\n// Successful captures per repo since process start, driving the periodic gc.\nconst trackCounts = new Map<string, number>();\n// Repos whose index was already scrubbed of excluded entries this process.\nconst scrubbedRepos = new Set<string>();\n\nexport async function isGitAvailable(): Promise<boolean> {\n if (gitAvailable === null) {\n gitAvailable = (await runGit([\"--version\"])) !== null;\n }\n return gitAvailable;\n}\n\n// Test hook: drop the cached availability probe so a mocked execFile takes\n// effect without reloading the module.\nexport function resetGitTreeAvailability(): void {\n gitAvailable = null;\n}\n\n// Test hook: drop every piece of cached state (availability probe, failure\n// negative cache, gc counters, scrub bookkeeping, capture chains).\nexport function resetGitTreeCaches(): void {\n gitAvailable = null;\n trackFailureUntil.clear();\n trackCounts.clear();\n scrubbedRepos.clear();\n trackChains.clear();\n}\n\n// One internal bare repo per project directory, named by the resolved path so\n// two directories never share a tree store.\nexport function treeRepoDir(cwd: string): string {\n const key = Buffer.from(path.resolve(cwd), \"utf8\").toString(\"base64url\");\n return path.join(gitTreesDir(), key);\n}\n\n// Removes a project's internal snapshot repo. Called when the project's last\n// session is cleared: the captured trees only make sense as /undo//redo\n// targets for session turns, and with no session left they are unreachable\n// disk usage. In-memory bookkeeping goes too, or a later session in the same\n// process would trust a failure cache / gc counter for a repo that no longer\n// exists.\nexport async function removeTreeRepo(cwd: string): Promise<void> {\n const dir = treeRepoDir(cwd);\n trackFailureUntil.delete(path.resolve(cwd));\n trackCounts.delete(dir);\n scrubbedRepos.delete(dir);\n await rm(dir, { recursive: true, force: true }).catch(() => {});\n}\n\n// Deletes every tree repo whose owning project has no surviving session.\n// remainingCwds are the cwd values of the sessions still on disk; repos are\n// keyed by resolved path (see treeRepoDir), so a basename compare keeps\n// exactly those.\nexport async function pruneTreeReposExcept(remainingCwds: Iterable<string>): Promise<void> {\n let entries: string[];\n try {\n entries = await readdir(gitTreesDir());\n } catch {\n return;\n }\n const keep = new Set<string>();\n for (const cwd of remainingCwds) keep.add(path.basename(treeRepoDir(cwd)));\n await Promise.all(\n entries\n .filter((entry) => !keep.has(entry))\n .map((entry) =>\n rm(path.join(gitTreesDir(), entry), { recursive: true, force: true }).catch(() => {}),\n ),\n );\n}\n\n// Snapshotting the home directory or a filesystem root would track an entire\n// machine — refuse rather than let a stray session there index everything.\nfunction isGuardedDir(cwd: string): boolean {\n const resolved = path.resolve(cwd);\n const home = path.resolve(os.homedir());\n const same = (a: string, b: string) =>\n process.platform === \"win32\" ? a.toLowerCase() === b.toLowerCase() : a === b;\n return same(resolved, home) || same(resolved, path.parse(resolved).root);\n}\n\nasync function ensureTreeRepo(cwd: string): Promise<string | null> {\n const dir = treeRepoDir(cwd);\n try {\n // Owner-only like the session directories: tree objects hold file\n // contents the user never committed anywhere else.\n await mkdir(dir, { recursive: true, mode: 0o700 });\n if (!existsSync(path.join(dir, \"HEAD\"))) {\n const out = await runGit([\"init\", \"--bare\", dir]);\n if (out === null) return null;\n // Belt and braces for fresh repos; runGit's forced -c flags already\n // cover repos created before the repo-local setting existed.\n await runGit([`--git-dir=${dir}`, \"config\", \"core.autocrlf\", \"false\"]);\n }\n // Rewritten on every call: repos created before the exclusion existed\n // pick it up here without any migration step.\n await mkdir(path.join(dir, \"info\"), { recursive: true });\n await writeFile(\n path.join(dir, \"info\", \"exclude\"),\n `${SENSITIVE_EXCLUDE_PATTERNS.join(\"\\n\")}\\n`,\n \"utf8\",\n );\n } catch {\n return null;\n }\n return dir;\n}\n\nfunction repoArgs(dir: string, cwd: string): string[] {\n return [`--git-dir=${dir}`, `--work-tree=${cwd}`];\n}\n\n// Drops index entries that match the exclude rules. Ignore patterns only keep\n// untracked files out of `git add`; a path already in the index — tracked by\n// a pre-exclusion star, or put back by read-tree of an old tree — stays until\n// explicitly removed, so both trackTree and restoreTree scrub it.\nasync function untrackExcluded(dir: string, cwd: string): Promise<boolean> {\n const base = repoArgs(dir, cwd);\n const listed = await runGit([...base, \"ls-files\", \"-z\", \"-c\", \"-i\", \"--exclude-standard\"]);\n if (listed === null) return false;\n const files = listed.split(\"\\0\").filter(Boolean);\n if (files.length === 0) return true;\n const removed = await runGit([\n ...base,\n \"rm\",\n \"--cached\",\n \"--ignore-unmatch\",\n \"-q\",\n \"--\",\n ...files.map((file) => `:(literal)${file}`),\n ]);\n return removed !== null;\n}\n\n// Captures the whole working tree (any change — write_file, edit_file, bash)\n// as a git tree object and returns its hash. The work tree's own .git is\n// skipped by git automatically; ignored files (node_modules) and excluded\n// likely-secret files never enter the tree, which is exactly what the restore\n// side relies on. A recent failure makes further attempts no-ops until\n// TRACK_FAILURE_TTL_MS has passed.\nexport function trackTree(cwd: string): Promise<string | null> {\n // Serialize captures per directory: the agent loop starts a turn's capture\n // concurrently and a quick second turn (or the /undo path) can arrive while\n // it still runs — two overlapping captures would race on the internal\n // repo's index.lock, and the loser's spurious failure would poison the\n // negative cache for five minutes. trackTreeUncached never rejects, so the\n // chain cannot wedge; each call still captures its own tree, in order.\n const key = path.resolve(cwd);\n const next = (trackChains.get(key) ?? Promise.resolve(null)).then(() => trackTreeGuarded(cwd));\n trackChains.set(key, next);\n return next;\n}\n\n// Per-cwd promise chains serializing trackTree (see above). Values stay\n// settled promises between captures; the map is keyed by directory, so it\n// stays tiny.\nconst trackChains = new Map<string, Promise<string | null>>();\n\nasync function trackTreeGuarded(cwd: string): Promise<string | null> {\n if (isGuardedDir(cwd)) return null;\n const key = path.resolve(cwd);\n const failedUntil = trackFailureUntil.get(key);\n if (failedUntil !== undefined && Date.now() < failedUntil) return null;\n const tree = await trackTreeUncached(cwd);\n if (tree === null) {\n trackFailureUntil.set(key, Date.now() + TRACK_FAILURE_TTL_MS);\n return null;\n }\n trackFailureUntil.delete(key);\n return tree;\n}\n\n// Pins a captured tree under refs/star/<timestamp>-<random> so gc --auto\n// never prunes a live /undo target, then trims the namespace to the newest\n// MAX_PINNED_REFS refs: for-each-ref sorts by refname, and equal-length\n// millisecond timestamps make that order chronological. Pure housekeeping —\n// a failure here never fails the capture.\nasync function pinTree(base: string[], tree: string): Promise<void> {\n const ref = `${PINNED_REF_PREFIX}${Date.now()}-${randomBytes(4).toString(\"hex\")}`;\n if ((await runGit([...base, \"update-ref\", ref, tree])) === null) return;\n const listed = await runGit([...base, \"for-each-ref\", \"--format=%(refname)\", PINNED_REF_PREFIX]);\n if (listed === null) return;\n const refs = listed.split(\"\\n\").filter(Boolean);\n const stale = refs.slice(0, Math.max(0, refs.length - MAX_PINNED_REFS));\n await Promise.all(stale.map((name) => runGit([...base, \"update-ref\", \"-d\", name])));\n}\n\nasync function trackTreeUncached(cwd: string): Promise<string | null> {\n if (!(await isGitAvailable())) return null;\n const dir = await ensureTreeRepo(cwd);\n if (!dir) return null;\n const base = repoArgs(dir, cwd);\n if (!scrubbedRepos.has(dir)) {\n if (!(await untrackExcluded(dir, cwd))) return null;\n scrubbedRepos.add(dir);\n }\n if ((await runGit([...base, \"add\", \"-A\"])) === null) return null;\n const tree = await runGit([...base, \"write-tree\"]);\n if (tree === null) return null;\n await pinTree(base, tree);\n const count = (trackCounts.get(dir) ?? 0) + 1;\n trackCounts.set(dir, count);\n if (count % GC_INTERVAL === 0) {\n // Pure housekeeping: the result is ignored so a gc failure never fails\n // the turn, matching the module's silent-degradation style.\n await runGit([`--git-dir=${dir}`, \"gc\", \"--auto\"], GC_TIMEOUT_MS);\n }\n return tree;\n}\n\n// Restores the working tree to a captured tree: index reset to the tree,\n// every index file checked out over the work tree, then untracked\n// (post-snapshot) files cleaned. `clean` keeps ignored files, so dependencies\n// and excluded secrets survive. checkout-index -a only writes paths present\n// in the index, so scrubbing excluded entries after read-tree guarantees a\n// tree captured before the exclusion existed still cannot overwrite the\n// user's current .env (or delete it via clean, which also skips it).\nexport async function restoreTree(cwd: string, hash: string): Promise<boolean> {\n if (isGuardedDir(cwd)) return false;\n if (!(await isGitAvailable())) return false;\n const dir = await ensureTreeRepo(cwd);\n if (!dir) return false;\n const base = repoArgs(dir, cwd);\n if ((await runGit([...base, \"read-tree\", hash])) === null) return false;\n if (!(await untrackExcluded(dir, cwd))) return false;\n if ((await runGit([...base, \"checkout-index\", \"-f\", \"-a\"])) === null) return false;\n if ((await runGit([...base, \"clean\", \"-fd\"])) === null) return false;\n return true;\n}\n\n// Read-only list of files restoring a tree would touch: tracked paths that\n// differ from the tree, plus untracked (non-ignored) paths the clean step\n// would remove. Backs the /undo and /redo previews.\nexport async function diffTreeNames(cwd: string, hash: string): Promise<string[] | null> {\n if (!(await isGitAvailable())) return null;\n const dir = treeRepoDir(cwd);\n if (!existsSync(path.join(dir, \"HEAD\"))) return null;\n const base = repoArgs(dir, cwd);\n const changed = await runGit([...base, \"diff\", \"--name-only\", hash]);\n const untracked = await runGit([...base, \"ls-files\", \"--others\", \"--exclude-standard\"]);\n if (changed === null || untracked === null) return null;\n const names = new Set<string>();\n for (const line of [...changed.split(\"\\n\"), ...untracked.split(\"\\n\")]) {\n // Restore never touches excluded likely-secret paths (trees captured\n // before the exclusion may still list them); keep the preview honest.\n if (line !== \"\" && !isSensitivePath(line)) names.add(line);\n }\n return [...names];\n}\n"],"mappings":"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","names":[]}