sessionpipe 0.13.2 → 0.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +100 -21
- package/dist/cli.js.map +3 -3
- package/dist/control/cli.d.ts.map +1 -1
- package/dist/control/daemon.d.ts +12 -0
- package/dist/control/daemon.d.ts.map +1 -1
- package/dist/control/pair.d.ts +6 -0
- package/dist/control/pair.d.ts.map +1 -1
- package/dist/control/route.d.ts +11 -3
- package/dist/control/route.d.ts.map +1 -1
- package/dist/hook.js +1 -1
- package/dist/index.js +27 -2
- package/dist/index.js.map +3 -3
- package/dist/worker.js +27 -2
- package/dist/worker.js.map +3 -3
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/http-sink.ts", "../src/run.ts", "../../core/src/paths.ts", "../../core/src/adapters/claude-code.ts", "../../core/src/readers/files.ts", "../../core/src/readers/git.ts", "../../core/src/adapters/codex-rollout.ts", "../../core/src/readers/transcripts.ts", "../../core/src/adapters/claude-shaped.ts", "../../core/src/adapters/headless.ts", "../../core/src/launched-by.ts", "../../core/src/adapters/antigravity.ts", "../../core/src/adapters/claude-code-control.ts", "../../core/src/control/verify.ts", "../../core/src/adapters/codex.ts", "../../core/src/adapters/codex-config.ts", "../../core/src/adapters/devin.ts", "../../core/src/readers/sqlite.ts", "../../core/src/adapters/gemini-cli.ts", "../../core/src/adapters/index.ts", "../../core/src/config.ts", "../../core/src/ulid.ts", "../../core/src/events.ts", "../../core/src/outbox.ts", "../../core/src/privacy/pii.ts", "../../core/src/privacy/secrets.ts", "../../core/src/privacy/filter.ts", "../../core/src/short-id.ts", "../../core/src/sinks/file.ts", "../../core/src/sinks/stdout.ts", "../src/control/aliases.ts", "../src/secrets.ts", "../src/control/store.ts", "../src/control/local.ts", "../src/fold.ts"],
|
|
4
|
-
"sourcesContent": ["// SPDX-License-Identifier: Apache-2.0\n// The HTTP sink (spec/HTTP.md \u00A72): POST {events} with the protocol header, a bearer\n// token and optional Standard Webhooks signature; errors map to what the sender\n// does next. The cursor moves only on 202. Retry/backoff is the worker's cadence\n// (each hook run drains the backlog); this class reports, it does not sleep.\nimport { createHash, createHmac } from \"node:crypto\";\nimport type { DeliveryResult, Event, Sink, SinkConfig, Tier } from \"@sessionpipe/core\";\n\nconst TIMEOUT_MS = 15_000;\n\n/** The Standard Webhooks message id (HTTP.md \u00A72): the same batch sent again carries\n * the same id, so a receiver that dedupes on webhook-id sees a retry as one message.\n * Derived from the events' own ids, which the outbox keeps across retries. */\nexport function webhookId(events: Pick<Event, \"id\">[]): string {\n return `msg_${createHash(\"sha256\")\n .update(events.map((e) => e.id).join(\",\"))\n .digest(\"base64url\")\n .slice(0, 32)}`;\n}\n\n/** HTTP.md \u00A72 429: wait as `retry-after` says, seconds or an HTTP date (RFC 9110\n * \u00A710.2.3); a minute when the header is missing or unreadable. */\nexport function retryAfterMs(header: string | null, now = Date.now()): number {\n if (header === null || header.trim() === \"\") return 60_000;\n const secs = Number(header);\n if (Number.isFinite(secs)) return Math.max(0, secs) * 1000;\n const at = Date.parse(header);\n return Number.isFinite(at) ? Math.max(0, at - now) : 60_000;\n}\n\nexport class HttpSink implements Sink {\n readonly name: string;\n readonly pii: boolean;\n private readonly url: string;\n constructor(\n private readonly cfg: SinkConfig,\n readonly tier: Tier,\n ) {\n this.name = cfg.name;\n this.pii = !!cfg.pii;\n this.url = cfg.url.replace(/\\/$/, \"\");\n }\n\n private eventsUrl(): string {\n // The endpoint from the well-known file is stored beside the url at `sink add`;\n // until then the spec's default path.\n const ep = (this.cfg as { endpoints?: { events?: string } }).endpoints?.events ?? \"/api/sessionpipe/v1/events\";\n return /^https?:\\/\\//.test(ep) ? ep : this.url + ep;\n }\n\n async send(events: Event[]): Promise<DeliveryResult> {\n const body = JSON.stringify({ events });\n const headers: Record<string, string> = { \"content-type\": \"application/json\", \"sessionpipe-protocol\": \"1\" };\n if (this.cfg.token) headers.authorization = `Bearer ${this.cfg.token}`;\n if (this.cfg.secret?.startsWith(\"whsec_\")) {\n const id = webhookId(events);\n const ts = Math.floor(Date.now() / 1000);\n const key = Buffer.from(this.cfg.secret.slice(6), \"base64\");\n const sig = createHmac(\"sha256\", key).update(`${id}.${ts}.${body}`).digest(\"base64\");\n headers[\"webhook-id\"] = id;\n headers[\"webhook-timestamp\"] = String(ts);\n headers[\"webhook-signature\"] = `v1,${sig}`;\n }\n const ctl = new AbortController();\n const t = setTimeout(() => ctl.abort(), TIMEOUT_MS);\n try {\n const r = await fetch(this.eventsUrl(), { method: \"POST\", headers, body, signal: ctl.signal });\n const text = await r.text();\n let j: Record<string, unknown> = {};\n try {\n j = text ? (JSON.parse(text) as Record<string, unknown>) : {};\n } catch {}\n if (r.status === 202 || r.status === 200) {\n return {\n ok: true,\n acknowledged: events.length,\n accepted: Number(j.accepted ?? events.length),\n duplicates: Number(j.duplicates ?? 0),\n rejected: Array.isArray(j.rejected) ? (j.rejected as { id: string; reason: string }[]) : [],\n ...(typeof j.max_tier === \"number\" ? { max_tier: j.max_tier as Tier } : {}),\n status: r.status,\n };\n }\n const err = typeof j.reason === \"string\" ? j.reason : text.slice(0, 200);\n if (r.status === 400) return { ok: false, acknowledged: 0, status: 400, error: err, action: \"drop\" };\n if (r.status === 401) return { ok: false, acknowledged: 0, status: 401, error: err, action: \"pause\" };\n if (r.status === 403)\n return {\n ok: false,\n acknowledged: 0,\n status: 403,\n error: err,\n action: \"lower-tier\",\n ...(typeof j.max_tier === \"number\" ? { max_tier: j.max_tier as Tier } : {}),\n };\n if (r.status === 413) return { ok: false, acknowledged: 0, status: 413, error: err, action: \"split\" };\n if (r.status === 429)\n return {\n ok: false,\n acknowledged: 0,\n status: 429,\n error: err,\n action: \"retry\",\n retryAfterMs: retryAfterMs(r.headers.get(\"retry-after\")),\n };\n return { ok: false, acknowledged: 0, status: r.status, error: err, action: \"retry\" };\n } catch (e) {\n return { ok: false, acknowledged: 0, error: String((e as Error).message ?? e), action: \"retry\" };\n } finally {\n clearTimeout(t);\n }\n }\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// What a worker run does, as a function so tests and `sessionpipe replay` share it.\nimport {\n appendFileSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n renameSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport {\n adapterByName,\n Cursors,\n capTail,\n claudeControl,\n type DataOf,\n type DeliveryResult,\n type Event,\n type FactsState,\n FileSink,\n filterEvent,\n gitFacts,\n isInjected,\n machineName,\n makeEvent,\n Outbox,\n type OutboxRef,\n processAlive,\n readConfig,\n readHook,\n redactDeep,\n type Session,\n type Sink,\n type SinkConfig,\n StdoutSink,\n safeId,\n shortId,\n stateDir,\n type TranscriptRead,\n takeLock,\n takeNamedLock,\n tilde,\n writeConfig,\n} from \"@sessionpipe/core\";\nimport { aliasFile, readAliases, receiverIdOf } from \"./control/aliases.js\";\nimport { ask, socketPath } from \"./control/local.js\";\nimport { sendTool, ToolClock, type ToolsMode, toolsMode } from \"./fold.js\";\nimport { HttpSink } from \"./http-sink.js\";\nimport { sinkWithSecrets } from \"./secrets.js\";\n\nasync function tellDaemon(\n state: string,\n job: Job,\n session: string,\n cwd: string | undefined,\n transcript?: string,\n): Promise<void> {\n const sock = socketPath(state);\n if (process.platform !== \"win32\" && !existsSync(sock)) return;\n // The session's own permission mode, where its payload names one (CONTROL.md \u00A76, The\n // session's mode): the daemon records it per session, and an in-place lane and a\n // relaunch's narrowing both rest on it. Read from the top-level field, never matched\n // out of the payload's text, so a tool input that quotes one cannot set it.\n let mode: string | null = null;\n try {\n mode = claudeControl.hookMode(JSON.parse(job.stdin));\n } catch {}\n await ask(\n sock,\n {\n op: \"event\",\n session: `${job.harness}:${session}`,\n event: job.event,\n ...(cwd ? { cwd } : {}),\n ...(transcript ? { transcript } : {}),\n ...(mode ? { mode } : {}),\n },\n 300,\n );\n}\n\ndeclare const __SESSIONPIPE_VERSION__: string;\nconst VERSION = typeof __SESSIONPIPE_VERSION__ === \"string\" ? __SESSIONPIPE_VERSION__ : \"0.0.0\";\n\nexport interface Job {\n harness: string;\n event: string;\n argv: string[];\n stdin: string;\n cwd: string;\n env: Record<string, string>;\n ppid?: number;\n at: number;\n /** How many times the session's lock held this job out (a fresh worker starts for\n * it each time, up to MAX_TRIES; after that a sweep picks it up). */\n tries?: number;\n}\n\nexport const MAX_TRIES = 10;\n\n/** A job file the worker will pick up: random suffix, 0600, in a 0700 dir. */\nexport function enqueueJob(state: string, job: Job): string {\n const jobs = jobsDir(state);\n mkdirSync(jobs, { recursive: true, mode: 0o700 });\n const f = path.join(jobs, `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`);\n writeFileSync(f, JSON.stringify(job), { mode: 0o600 });\n return f;\n}\n\n/** A job is claimed by renaming its file `<file>.<pid>.running`: of two workers that\n * race for it, one rename wins. The claim stays until the job's events are in the\n * outbox (or it went back to the queue), so a worker its deadline ended mid-run\n * leaves it behind for a sweep to give back (ground rule 4: no event lost). */\nexport function claimJob(file: string): { job: Job; file: string } | null {\n const claimed = `${file}.${process.pid}.running`;\n try {\n renameSync(file, claimed);\n } catch {\n return null;\n }\n try {\n return { job: JSON.parse(readFileSync(claimed, \"utf8\")) as Job, file: claimed };\n } catch {\n releaseJob(claimed);\n return null;\n }\n}\nexport function releaseJob(claimed: string | undefined): void {\n if (!claimed) return;\n try {\n unlinkSync(claimed);\n } catch {}\n}\n\n/** Jobs another worker left behind: a claim whose worker is gone (or older than any\n * worker lives, `abandonedMs`) goes back to the queue, then every queued job older\n * than `olderThanMs` (younger ones belong to the worker their hook just spawned)\n * is claimed and run. Every worker sweeps before it exits. */\nexport async function sweepJobs(\n state: string,\n olderThanMs = 3000,\n limit = 20,\n abandonedMs = 60_000,\n o: DrainLife = {},\n): Promise<number> {\n const jobs = jobsDir(state);\n let names: string[] = [];\n try {\n names = readdirSync(jobs);\n } catch {\n return 0;\n }\n const queued = names.filter((n) => n.endsWith(\".json\"));\n for (const n of names) {\n const m = /^(.*\\.json)\\.(\\d+)\\.running$/.exec(n);\n if (!m) continue;\n const f = path.join(jobs, n);\n try {\n if (processAlive(Number(m[2])) && Date.now() - statSync(f).mtimeMs < abandonedMs) continue;\n renameSync(f, path.join(jobs, m[1] as string));\n queued.push(m[1] as string);\n } catch {}\n }\n let ran = 0;\n // The jobs' events go to the outbox one by one and the sinks drain once at the end:\n // a drain per job would wait out the machine's pace (PACE_MS) once per job. Each job\n // that appended something marks flush.more, so a drain already running sends it in\n // its next pass instead of leaving it behind the rest of the sweep. A job whose\n // session is busy waits SWEEP_LOCK_WAIT_MS, not a hook's 6 s, and goes back to the\n // queue; no job starts once the worker's life is too short for the drain after it.\n for (const n of queued.sort().slice(0, limit)) {\n if (o.deadline !== undefined && Date.now() + SWEEP_LOCK_WAIT_MS + PASS_BUDGET_MS > o.deadline) break;\n const f = path.join(jobs, n);\n try {\n if (Date.now() - statSync(f).mtimeMs < olderThanMs) continue;\n } catch {\n continue;\n }\n const c = claimJob(f);\n if (!c) continue;\n try {\n const r = await runJob(c.job, { state, claimed: c.file, drain: false, lockWaitMs: SWEEP_LOCK_WAIT_MS });\n if (r.events.length) markMore(state);\n } catch {\n } finally {\n releaseJob(c.file);\n }\n ran++;\n }\n if (ran) await drain(state, readConfig().sinks, new Outbox(state), undefined, o);\n return ran;\n}\n\nexport function log(state: string, line: string): void {\n try {\n mkdirSync(state, { recursive: true, mode: 0o700 });\n const f = path.join(state, \"log\");\n try {\n if (statSync(f).size > 1024 * 1024) renameSync(f, `${f}.1`);\n } catch {}\n appendFileSync(f, `${new Date().toISOString()} ${line}\\n`);\n } catch {}\n}\n\n/** <state>/facts/<harness>/<session>.json \u2014 what the machine learned about a session. */\nexport function factsState(state: string): FactsState {\n const file = (h: string, s: string) => path.join(state, \"facts\", safeId(h), `${safeId(s)}.json`);\n return {\n get(h, s) {\n try {\n return JSON.parse(readFileSync(file(h, s), \"utf8\")) as Record<string, unknown>;\n } catch {\n return {};\n }\n },\n save(h, s, patch) {\n const cur = this.get(h, s);\n const next = { ...cur, ...patch };\n if (JSON.stringify(next) === JSON.stringify(cur)) return;\n mkdirSync(path.dirname(file(h, s)), { recursive: true, mode: 0o700 });\n writeFileSync(file(h, s), JSON.stringify(next), { mode: 0o600 });\n },\n };\n}\n\nexport function buildSinks(sinks: SinkConfig[], o: { locked?: (name: string) => void } = {}): Sink[] {\n const out: Sink[] = [];\n for (const raw of sinks) {\n if (raw.paused) continue;\n // A token kept in a keychain is read now; one this process can't read (a keychain\n // locked in an ssh session) leaves the sink's events waiting, never sent unsigned.\n const s = sinkWithSecrets(raw);\n if (!s) {\n o.locked?.(raw.name);\n continue;\n }\n // Math.trunc: a tier of 1.5 that an older `sink add --tier 1.5` stored sends as 1.\n // `|| 0`: a sink with no tier (a hand edit) sends at 0, not as a `tier: null` no\n // receiver takes.\n const tier = Math.trunc(Math.min(Number(s.tier) || 0, s.max_tier ?? 3)) as 0 | 1 | 2 | 3;\n if (s.url === \"stdout\") out.push(new StdoutSink(s.name, tier, !!s.pii));\n else if (s.url.startsWith(\"file:\"))\n out.push(new FileSink(s.name, s.url.slice(5).replace(/^~(?=\\/|$)/, os.homedir()), tier, !!s.pii));\n else if (/^https?:\\/\\//.test(s.url)) out.push(new HttpSink(s, tier));\n }\n return out;\n}\n\n/** How often a running turn's words go out again (turn.progress), at most. */\nexport const PROGRESS_MS = 20_000;\n\n/** The turn still running in a transcript read from the delivery cursor: the last\n * turn with a reply so far, or a prompt nothing has answered yet (the hook's own\n * copy when the transcript doesn't hold it yet). `base` is the number the cursor's\n * first turn will get, so a turn keeps the number its turn.transcript will carry.\n * The reply keeps its end when it is long: where the turn is now. */\nexport function openTurn(\n read: TranscriptRead,\n o: { base: number; prompt?: string | undefined; at: number },\n): { turn: number; user: string; assistant: string; at: number; injected?: true } | null {\n const last = read.turns[read.turns.length - 1];\n const fresh = (user: string, at: number, injected?: true) => ({\n turn: o.base + read.turns.length,\n user: user.slice(0, MAX_TURN_SIDE),\n assistant: \"\",\n at,\n ...(injected ? { injected: true as const } : {}),\n });\n // A turn still running says the same about its ask as the finished one will: the board\n // would otherwise show the person's relayed words as typed here until the turn ends.\n if (read.open) return fresh(read.open.user, read.open.at, read.open.injected);\n // The prompt the hook just reported, unless the transcript already has its reply.\n // Text the person never typed (a task notification, another session's message) opens\n // no turn: the readers skip it, so that turn would never be delivered, and its\n // reading would stand in for the turn that is running until the next one lands.\n const prompt = o.prompt?.trim();\n if (prompt && !isInjected(prompt) && !(last && last.user.includes(prompt.slice(0, 200)))) return fresh(prompt, o.at);\n if (!last) return null;\n return {\n turn: o.base + read.turns.length - 1,\n user: last.user.slice(0, MAX_TURN_SIDE),\n assistant: capTail(last.assistant, MAX_TURN_SIDE),\n ...(last.injected ? { injected: true as const } : {}),\n at: last.at,\n };\n}\nconst MAX_TURN_SIDE = 20_000;\n/** What session.attended is read from, the desktop app's link included, and session.launched_by\n * and delegated_by (hook.ts picks all five): the job's own, never the worker's. */\nconst JOB_OWN_ENV = [\n \"CLAUDE_CODE_SESSION_ATTENDED\",\n \"CLAUDE_CODE_HOST_SESSION_ID\",\n \"SESSIONPIPE_ASKED_BY\",\n \"SESSIONPIPE_CONTROL_JOB\",\n \"SESSIONPIPE_LAUNCHED_BY\",\n];\n\n/** Run one hook job end to end: events \u2192 outbox \u2192 sinks. Returns what happened.\n * `claimed` is the job file this run holds: released once the events are in the\n * outbox or the job is back in the queue (`requeued` names its new file). */\nexport async function runJob(\n job: Job,\n opts: {\n state?: string;\n now?: number;\n claimed?: string;\n lockWaitMs?: number;\n /** false: the events go to the outbox and the sinks are left for the caller to drain. */\n drain?: boolean;\n /** The drain's clock (pacing); tests stand in a simulated one. */\n clock?: Clock;\n } & DrainLife = {},\n): Promise<{ events: Event[]; delivered: Record<string, number>; requeued?: string }> {\n const state = opts.state ?? stateDir();\n const cfg = readConfig();\n const adapter = adapterByName(job.harness);\n // A hooks file can outlive the build that wrote it (an update to a sessionpipe without\n // that adapter, a hand-written entry): without this line the events are dropped in\n // silence, with nothing on the machine to say so.\n if (!adapter) {\n log(state, `${job.harness} ${job.event}: no adapter for harness \"${job.harness}\"; event dropped`);\n return { events: [], delivered: {} };\n }\n // Who attends a session, and who launched it, is the job's own word, never the worker's: a\n // worker another session's hook spawned (sweepJobs) carries that session's. A key the job\n // lacks stays absent.\n const env: NodeJS.ProcessEnv = { ...process.env };\n for (const k of JOB_OWN_ENV) delete env[k];\n const r = readHook(adapter, { argv: job.argv, stdin: job.stdin, env: { ...env, ...job.env }, cwd: job.cwd });\n // The adapter did not claim the payload (another harness ran our entry out of a\n // shared hooks file, an event name it does not map, a payload with no session id).\n // Dropped either way, but never in silence: this is where a wrongly-named event\n // lands, and the log is the only thing on the machine that can say so.\n if (!r || !r.session.id) {\n let keys: string[] = [];\n try {\n // Some hooks pass JSON in argv instead of stdin. Only the top-level names\n // belong in this default log: values can contain prompts, paths or secrets.\n const payload: unknown = JSON.parse(job.argv.find((a) => a.startsWith(\"{\")) ?? job.stdin);\n if (payload && typeof payload === \"object\" && !Array.isArray(payload)) keys = Object.keys(payload);\n } catch {}\n log(\n state,\n `${job.harness} ${job.event}: the ${job.harness} adapter did not claim this payload; event dropped; keys: ${keys.join(\", \") || \"(none)\"}`,\n );\n return { events: [], delivered: {} };\n }\n // A session the control daemon started goes by the id its start command named, not\n // the one the harness picked (CONTROL.md \u00A76 `start`; control/aliases.ts): its events\n // say so, and so does everything below. The adapter's own reading of the session\n // (facts, a remote URL) keeps the harness's id.\n const id =\n receiverIdOf(readAliases(aliasFile(path.join(state, \"control\"))), job.harness, r.session.id) ?? r.session.id;\n const claudeEnd = job.harness === \"claude-code\" && r.events.some((e) => e.type === \"session.ended\");\n // The control daemon learns each session's turn state and folder from its events (a\n // message for a session mid-turn waits for its Stop; a pending permission prompt\n // closes when the tool runs). A local round trip, and nothing when no daemon runs.\n if (!claudeEnd) await tellDaemon(state, job, id, r.session.cwd, r.transcript);\n const outbox = new Outbox(state);\n const ref = { harness: job.harness, session: id };\n // Hooks of one session fire close together (parallel subagents, issue #9): poll\n // for the lock, often enough that its siblings can't keep taking it in between,\n // before giving the job back to the queue.\n let release = takeLock(state, ref);\n const lockWait = opts.lockWaitMs ?? LOCK_WAIT_MS;\n for (const until = Date.now() + lockWait; !release && Date.now() < until; ) {\n await new Promise((res) => setTimeout(res, 50));\n release = takeLock(state, ref);\n }\n if (!release) {\n log(state, `${job.harness} ${job.event}: session ${id} locked for ${lockWait / 1000} s, re-queued`);\n const requeued = enqueueJob(state, { ...job, tries: (job.tries ?? 0) + 1 });\n releaseJob(opts.claimed);\n return { events: [], delivered: {}, requeued };\n }\n const events: Event[] = [];\n try {\n const facts = factsState(state);\n const mem = facts.get(job.harness, id);\n // `claude mcp list` fires SessionEnd with a fresh id but no SessionStart or\n // transcript. Check under the session lock, before creating facts or a seq:\n // any existing outbox, facts or transcript is enough to preserve a real end.\n if (\n claudeEnd &&\n !existsSync(outbox.file(ref)) &&\n Object.keys(mem).length === 0 &&\n !(r.transcript && existsSync(r.transcript))\n ) {\n releaseJob(opts.claimed);\n return { events: [], delivered: {} };\n }\n if (claudeEnd) await tellDaemon(state, job, id, r.session.cwd, r.transcript);\n // Heartbeat-class events read no facts (they fire every tool call); the rest refresh them.\n const heavy = !/^(PreToolUse|PostToolUse|PostToolUseFailure|BeforeTool|AfterTool|PostInvocation)$/.test(job.event);\n let learned: Partial<Session> = {};\n if (heavy) {\n // The hook's environment, not the worker's: a swept job may be run by a worker\n // another harness's hook spawned, whose env names none of this harness's homes.\n const hookEnv = { ...process.env, ...job.env };\n const f = adapter.facts(r.session, r.transcript, r.hints ?? {}, facts, hookEnv);\n // A name only the harness's own service knows (claude.ai's): a round trip with the\n // person's sign-in, so only when they turned it on (`sessionpipe titles on`).\n if (cfg.remote_titles && adapter.remoteFacts)\n Object.assign(f, await adapter.remoteFacts(r.session, f, r.transcript, facts, hookEnv));\n const { started_at: _s, last_at: _l, first_ask: _fa, harness_version, ...rest } = f;\n learned = rest;\n if (harness_version) facts.save(job.harness, id, { harness_version });\n facts.save(job.harness, id, { session: { ...(mem.session as object), ...learned } });\n // Antigravity's transcript never says which folder a conversation runs in, and\n // Devin's payload names none either (only DEVIN_PROJECT_DIR in the hook's env);\n // the hooks do. Kept raw (this file is the machine's own, 0600), so the control\n // daemon still knows where to resume it after a restart.\n if (CWD_FROM_HOOKS.has(job.harness) && r.session.cwd && path.isAbsolute(r.session.cwd))\n facts.save(job.harness, id, { cwd: r.session.cwd });\n }\n const known = (mem.session as Partial<Session> | undefined) ?? {};\n const cwdRaw = learned.cwd ?? r.session.cwd ?? known.cwd;\n const git = heavy ? gitFacts(cwdRaw) : {};\n const session: Session = {\n ...known,\n ...learned,\n ...git,\n ...(learned.branch ? { branch: learned.branch } : {}),\n id,\n seq: 0,\n machine: machineName(cfg, os.hostname()),\n ...(cwdRaw ? { cwd: tilde(cwdRaw) } : {}),\n ...(r.session.model ? { model: r.session.model } : {}),\n ...(r.session.launched_by ? { launched_by: r.session.launched_by } : {}),\n ...(r.session.delegated_by ? { delegated_by: r.session.delegated_by } : {}),\n };\n const version = (facts.get(job.harness, id).harness_version as string | undefined) ?? undefined;\n for (const a of r.events) {\n const e = makeEvent(a, {\n harness: { name: job.harness, ...(version ? { version } : {}) },\n session: { ...session, seq: outbox.nextSeq(ref) },\n now: opts.now ?? job.at,\n });\n events.push(e);\n }\n const wantsTranscript = cfg.sinks.some((s) => !s.paused && Math.min(s.tier, s.max_tier ?? 3) >= 2);\n // Tier 2, while a turn runs: what it has said so far, so a receiver can show a long\n // turn before it ends. On the prompt, then at most every PROGRESS_MS on a tool call,\n // and only when it changed. The cursor stays where it is: turn.transcript still\n // delivers the whole turn when it ends.\n const ending = r.events.some((e) => e.type === \"turn.ended\" || e.type === \"session.ended\");\n const starting = r.events.some((e) => e.type === \"turn.started\");\n // A message sent while a turn runs is absorbed by that turn, and Claude Code reports\n // it as a prompt with the running turn's own id. It is not a new turn: where it\n // belongs is the transcript's to say, so its text is no reading of its own.\n const startedId = r.events.find((e) => e.type === \"turn.started\")?.data.turn_id;\n const absorbed = starting && typeof startedId === \"string\" && facts.get(job.harness, id).open_turn === startedId;\n if (wantsTranscript && r.transcript && !ending && (starting || r.events.some((e) => e.type === \"tool.ended\"))) {\n const cur = facts.get(job.harness, id);\n const at = opts.now ?? job.at;\n if (starting || at - (Number(cur.progress_at) || 0) >= PROGRESS_MS) {\n const p = openTurn(adapter.readTranscript(r.transcript, Number(cur.transcript_line) || 0), {\n base: Number(cur.transcript_turns) || 0,\n prompt: !absorbed && typeof r.hints?.prompt === \"string\" ? r.hints.prompt : undefined,\n at,\n });\n const sig = p && `${p.turn}:${p.user.length}:${p.assistant.length}`;\n if (p && (starting || sig !== cur.progress_sig)) {\n events.push(\n makeEvent(\n { type: \"turn.progress\", harnessEvent: job.event, data: { ...p, at: new Date(p.at).toISOString() } },\n {\n harness: { name: job.harness, ...(version ? { version } : {}) },\n session: { ...session, seq: outbox.nextSeq(ref) },\n now: at,\n },\n ),\n );\n facts.save(job.harness, id, { progress_at: at, progress_sig: sig });\n }\n }\n }\n // The turn running now, by its prompt's id, until it ends (for `absorbed`).\n if (starting && typeof startedId === \"string\" && !absorbed) facts.save(job.harness, id, { open_turn: startedId });\n if (ending) facts.save(job.harness, id, { open_turn: null });\n // Tier 2: after a turn ends, the transcript's new turns become turn.transcript events.\n if (\n wantsTranscript &&\n r.transcript &&\n r.events.some((e) => e.type === \"turn.ended\" || e.type === \"session.ended\")\n ) {\n const cur = facts.get(job.harness, id);\n const fromLine = Number(cur.transcript_line) || 0;\n let turnNo = Number(cur.transcript_turns) || 0;\n const read = adapter.readTranscript(r.transcript, fromLine);\n for (const t of read.turns) {\n events.push(\n makeEvent(\n {\n type: \"turn.transcript\",\n harnessEvent: job.event,\n data: {\n turn: turnNo++,\n user: t.user,\n assistant: t.assistant,\n at: new Date(t.at).toISOString(),\n ...(t.injected ? { injected: true as const } : {}),\n },\n },\n {\n harness: { name: job.harness, ...(version ? { version } : {}) },\n session: { ...session, seq: outbox.nextSeq(ref) },\n now: opts.now ?? job.at,\n },\n ),\n );\n }\n if (read.turns.length) facts.save(job.harness, id, { transcript_line: read.line, transcript_turns: turnNo });\n }\n // Tier 1: the account's limits, when the turn left new numbers in the transcript\n // (Codex's rollout; Claude Code's come from its status line, through fromHook).\n if (\n adapter.limits &&\n r.transcript &&\n ending &&\n cfg.sinks.some((s) => !s.paused && Math.min(s.tier, s.max_tier ?? 3) >= 1)\n ) {\n const limits = adapter.limits(r.transcript);\n const sig = limits && JSON.stringify(limits);\n if (limits && sig !== facts.get(job.harness, id).limits_sig) {\n events.push(\n makeEvent(\n { type: \"limits.observed\", harnessEvent: job.event, data: limits },\n {\n harness: { name: job.harness, ...(version ? { version } : {}) },\n session: { ...session, seq: outbox.nextSeq(ref) },\n now: opts.now ?? job.at,\n },\n ),\n );\n facts.save(job.harness, id, { limits_sig: sig });\n }\n }\n // At rest the outbox holds redacted strings (advisory GHSA-8f6f-c3c9-j5p6): the\n // tier-3 fields stay, with their secrets already gone; filterEvent runs the same\n // idempotent pass again per sink.\n outbox.append(\n ref,\n events.map((e) => ({ ...e, session: redactDeep(e.session), data: redactDeep(e.data) })),\n );\n releaseJob(opts.claimed);\n log(\n state,\n `${job.harness} ${job.event}: ${events.map((e) => e.type).join(\",\") || \"nothing\"} (${shortId(job.harness, id)})`,\n );\n } finally {\n release();\n }\n // The session's lock covered the append; the sinks drain without it (the outbox\n // reader leaves a line still being written), so a sibling hook isn't held out for\n // a round trip. A prune holds the `flush` lock, so it goes first: a worker that\n // found the lock taken meanwhile left its events and a `flush.more` mark, and this\n // drain sends them.\n if (Math.random() < 0.02) outbox.prune(cfg.keep_days ?? 30);\n const delivered =\n opts.drain === false\n ? {}\n : ((await drain(state, cfg.sinks, outbox, ref, {\n ...(opts.clock ? { clock: opts.clock } : {}),\n ...(opts.deadline !== undefined ? { deadline: opts.deadline } : {}),\n ...(opts.handoff ? { handoff: opts.handoff } : {}),\n })) ?? {});\n return { events, delivered };\n}\n\n/** One event for a session with no hook behind it: the pool's `limits.observed`, when\n * the daemon read an account's numbers off a request the session made (pool/index.ts\n * `onLimits`). It goes the way a hook's events go \u2014 only with a tier-1 sink, the\n * session's known facts for `session` (its own account_id included), redacted at rest,\n * under the session's lock, then the sinks drain \u2014 so a receiver can't tell it from\n * the status line's. A hook holding the session's lock (its events are being written\n * right then) is waited for, up to `lockWaitMs`; past that the numbers are dropped\n * with a line in the log, never in silence (the live proof lost a turn's switch that\n * way). Null: no sink wants it, or the session stayed busy. */\nexport async function appendObserved(\n state: string,\n harness: string,\n id: string,\n data: DataOf<\"limits.observed\">,\n opts: { now?: number; lockWaitMs?: number } = {},\n): Promise<Event | null> {\n const cfg = readConfig();\n if (!cfg.sinks.some((s) => !s.paused && Math.min(s.tier, s.max_tier ?? 3) >= 1)) return null;\n const outbox = new Outbox(state);\n const ref = { harness, session: id };\n let release = takeLock(state, ref);\n for (const until = Date.now() + (opts.lockWaitMs ?? OBSERVED_LOCK_WAIT_MS); !release && Date.now() < until; ) {\n await new Promise((res) => setTimeout(res, 50));\n release = takeLock(state, ref);\n }\n if (!release) {\n log(state, `${harness} limits.observed for ${shortId(harness, id)} dropped: the session is busy`);\n return null;\n }\n const now = opts.now ?? Date.now();\n let e: Event;\n try {\n const facts = factsState(state);\n const mem = facts.get(harness, id);\n const known = (mem.session as Partial<Session> | undefined) ?? {};\n const version = mem.harness_version as string | undefined;\n e = makeEvent(\n { type: \"limits.observed\", harnessEvent: \"pool\", data },\n {\n harness: { name: harness, ...(version ? { version } : {}) },\n session: { ...known, id, seq: outbox.nextSeq(ref), machine: machineName(cfg, os.hostname()) },\n now,\n },\n );\n outbox.append(ref, [{ ...e, session: redactDeep(e.session), data: redactDeep(e.data) }]);\n } finally {\n release();\n }\n await drain(state, cfg.sinks, outbox, ref);\n return e;\n}\n\n/** What the drain reads the time from and waits with; tests stand in a simulated one. */\nexport interface Clock {\n now(): number;\n sleep(ms: number): Promise<void>;\n}\nconst REAL_CLOCK: Clock = { now: () => Date.now(), sleep: (ms) => new Promise((r) => setTimeout(r, ms)) };\n\n/** A pass of the drain starts at least this long after the machine's last request to\n * a receiver (issue #208): under a few hundred subagents a receiver gets one request\n * a second with everything that piled up, not one per hook. A lone event goes at once. */\nexport const PACE_MS = 1000;\n/** <state>/locks/flush.sent: when this machine last sent a request to a receiver\n * (epoch ms), written as the request leaves. Every worker reads the same file. */\nconst sentFile = (state: string): string => path.join(state, \"locks\", \"flush.sent\");\nfunction lastSent(state: string): number {\n try {\n // Unreadable text is NaN, and a NaN wait waits for nothing.\n return Number(readFileSync(sentFile(state), \"utf8\"));\n } catch {\n return 0;\n }\n}\nfunction noteSent(state: string, at: number): void {\n try {\n mkdirSync(path.dirname(sentFile(state)), { recursive: true, mode: 0o700 });\n writeFileSync(sentFile(state), String(at), { mode: 0o600 });\n } catch {}\n}\n\n/** Whether a receiver (an HTTP sink not paused and not in backoff) has outbox bytes\n * past its cursor: the pass that follows may send it a request. Bytes it will skip\n * (a thinned tool event) count too; reading them is the pass's job. */\nfunction waitingForReceiver(state: string, sinkConfigs: SinkConfig[], outbox: Outbox, now: number): boolean {\n const receivers = sinkConfigs.filter(\n (s) => !s.paused && /^https?:\\/\\//.test(s.url) && !((readBackoff(state, s.name)?.until ?? 0) > now),\n );\n if (!receivers.length) return false;\n const refs = outbox.sessions();\n for (const s of receivers) {\n const cursors = new Cursors(state, s.name);\n const c = cursors.read();\n for (const ref of refs) {\n try {\n if (statSync(outbox.file(ref)).size > (c[cursors.key(ref)] ?? 0)) return true;\n } catch {}\n }\n }\n return false;\n}\n\n/** How long a worker's life must still run for a pass of the drain to start: the\n * pace's wait (\u2264 PACE_MS) and a round trip. A worker that has less leaves the work\n * marked (flush.more) and hands it on (DrainLife.handoff) rather than be ended by its\n * deadline mid-pass, holding events nobody else knows to send. */\nexport const PASS_BUDGET_MS = 3000;\n/** How long a swept job waits for its session's lock before it goes back to the queue. */\nconst SWEEP_LOCK_WAIT_MS = 1000;\n\n/** A worker's remaining life, for the drain and the sweep. Both unset: no limit. */\nexport interface DrainLife {\n /** When the process will be ended (epoch ms, on the drain's clock). */\n deadline?: number;\n /** Called, with the flush lock released, when work is left marked for another\n * worker because this one's life is too short for it (worker.ts starts a drainer). */\n handoff?: () => void;\n}\n\nconst moreFile = (state: string): string => path.join(state, \"locks\", \"flush.more\");\n/** Tell the worker draining now (if any) that the outbox has more for it. */\nfunction markMore(state: string): void {\n try {\n mkdirSync(path.dirname(moreFile(state)), { recursive: true, mode: 0o700 });\n writeFileSync(moreFile(state), \"\", { mode: 0o600 });\n } catch {}\n}\n\n/** One worker drains a machine's sinks at a time (the `flush` lock). Another whose\n * events are in the outbox meanwhile marks that there is more and leaves them to\n * it (null: nothing sent by this one). The one draining looks for the mark after\n * each pass, once it has let the lock go, so events appended during a pass go out\n * in its next pass or with the worker that appended them, never wait for a later\n * hook. Under a flood of hook events this is one uploader sending full batches,\n * not one per event, all refused together (mordast, 2026-10-14: 828 refusals in\n * two minutes, 5 192 jobs queued). A pass that would follow the machine's last\n * request by less than PACE_MS first waits out the rest, holding the lock: the\n * workers that arrive meanwhile only mark that there is more, and the pass sends\n * everything they appended. A pass starts only while the worker's life has\n * PASS_BUDGET_MS left; otherwise the mark stays and the work is handed on. */\nexport async function drain(\n state: string,\n sinkConfigs: SinkConfig[],\n outbox: Outbox,\n first?: { harness: string; session: string },\n o: { clock?: Clock } & DrainLife = {},\n): Promise<Record<string, number> | null> {\n const clock = o.clock ?? REAL_CLOCK;\n const more = moreFile(state);\n let release = takeNamedLock(state, \"flush\");\n if (!release) {\n markMore(state);\n release = takeNamedLock(state, \"flush\");\n if (!release) return null;\n }\n const delivered: Record<string, number> = {};\n for (;;) {\n if (o.deadline !== undefined && clock.now() + PASS_BUDGET_MS > o.deadline) {\n markMore(state);\n release();\n o.handoff?.();\n return delivered;\n }\n try {\n // Never more than PACE_MS: a stamp in the future (a clock set back) can't park it.\n // A pass with nothing for a receiver (a file sink's events, a cursor already at\n // the end) doesn't wait at all.\n const wait = Math.min(PACE_MS, lastSent(state) + PACE_MS - clock.now());\n if (wait > 0 && waitingForReceiver(state, sinkConfigs, outbox, clock.now())) await clock.sleep(wait);\n try {\n unlinkSync(more);\n } catch {}\n for (const [k, v] of Object.entries(await flush(state, sinkConfigs, outbox, first, { clock })))\n delivered[k] = (delivered[k] ?? 0) + v;\n } finally {\n release();\n }\n if (!existsSync(more)) return delivered;\n release = takeNamedLock(state, \"flush\");\n if (!release) return delivered;\n }\n}\n\n/** HTTP.md \u00A72: a sink that answered 429 isn't tried again before its `retry-after`;\n * one that was unreachable (5xx, a timeout) waits 2 s \u00B7 10 s \u00B7 60 s \u00B7 5 min \u00B7 30 min,\n * then every 30 min. Kept in <state>/backoff/<sink>.json, so every worker on the\n * machine waits, not only the one that was refused. Cleared by the next batch the\n * sink takes. */\nexport interface Backoff {\n until: number;\n failures: number;\n why: string;\n}\nconst BACKOFF_MS = [2_000, 10_000, 60_000, 5 * 60_000, 30 * 60_000];\nconst backoffFile = (state: string, sink: string): string => path.join(state, \"backoff\", `${safeId(sink)}.json`);\nexport function readBackoff(state: string, sink: string): Backoff | null {\n try {\n const b = JSON.parse(readFileSync(backoffFile(state, sink), \"utf8\")) as Backoff;\n return typeof b.until === \"number\" ? b : null;\n } catch {\n return null;\n }\n}\nfunction noteFailure(state: string, sink: string, res: DeliveryResult, prev: Backoff | null): Backoff {\n const failures = (prev?.failures ?? 0) + 1;\n const ladder = BACKOFF_MS[Math.min(failures, BACKOFF_MS.length) - 1] as number;\n const wait = Number.isFinite(res.retryAfterMs) ? (res.retryAfterMs as number) : ladder;\n const b = { until: Date.now() + wait, failures, why: `${res.status ?? \"\"} ${res.error ?? \"\"}`.trim() };\n const f = backoffFile(state, sink);\n try {\n mkdirSync(path.dirname(f), { recursive: true, mode: 0o700 });\n writeFileSync(f, JSON.stringify(b), { mode: 0o600 });\n } catch {}\n return b;\n}\nfunction clearBackoff(state: string, sink: string): void {\n try {\n unlinkSync(backoffFile(state, sink));\n } catch {}\n}\n\n/** How long a job waits for its session's lock before it goes back to the queue. */\nconst LOCK_WAIT_MS = 6000;\n/** How long the pool's limits.observed waits for a hook that holds the session. */\nconst OBSERVED_LOCK_WAIT_MS = 2000;\n/** Harnesses whose own files do not name the session's folder: the hooks' cwd is kept. */\nconst CWD_FROM_HOOKS = new Set([\"antigravity\", \"devin\"]);\n\nconst BATCH = 50;\nconst MAX_PER_RUN = 500;\n\n/** Drain every sink from its cursor: this session first, then any backlog. Events\n * from many sessions are packed into one batch of up to BATCH, so a first\n * install's 400-session backfill is nine POSTs, not four hundred (the receiver's\n * write limit ended the first replay at 278 events, 2026-09-28). A cursor moves\n * only after its batch was acknowledged; a refused batch stops the sink for this\n * run and the next worker starts from the same cursors. The sinks drain at once:\n * the session's lock is held for the slowest round trip, not the sum of them (a\n * verdict's config write is synchronous, so two sinks' never interleave). */\nexport async function flush(\n state: string,\n sinkConfigs: SinkConfig[],\n outbox: Outbox,\n first?: { harness: string; session: string },\n o: {\n /** Try a sink in backoff anyway (`sessionpipe replay`). */ force?: boolean;\n /** Stamps the machine's last request (drain's pace). */ clock?: Clock;\n /** Counts, per sink, the tool events it was configured not to be sent (`replay`). */\n thinned?: Record<string, number>;\n } = {},\n): Promise<Record<string, number>> {\n const delivered: Record<string, number> = {};\n const clock = o.clock ?? REAL_CLOCK;\n const sinks = buildSinks(sinkConfigs, {\n locked: (name) => log(state, `sink ${name}: its token is in a keychain this process can't read; left for later`),\n });\n const modes = new Map(sinkConfigs.map((s) => [s.name, toolsMode(s)]));\n const refs = outbox.sessions();\n if (first) {\n const k = `${first.harness}/${safeId(first.session)}`;\n refs.sort((a, b) => (`${a.harness}/${a.session}` === k ? -1 : `${b.harness}/${b.session}` === k ? 1 : 0));\n }\n // A session file this process can't read (a 0600 file a hook left as another user\n // under the same HOME, EACCES; a folder named <id>.jsonl, EISDIR on macOS and Linux,\n // while Windows stats a folder at 0 bytes and reads it as empty) is skipped with a\n // line in the log, and the other sessions still go out to every sink; before, the\n // error threw out of `flush`, so no sink took the sessions listed after that file,\n // and nothing was logged. Its cursors stay where they were, so a file readable again\n // is taken from there on the next drain. Logged once per flush pass, not once per\n // sink (the sinks drain at once), and again on every pass while the file stays\n // unreadable. Only a system error (one with a code) is a skip; anything else is a\n // bug and throws as before.\n const unreadable = new Set<string>();\n const readOrSkip = (ref: OutboxRef, offset: number, room: number): { events: Event[]; offset: number } | null => {\n try {\n return outbox.read(ref, offset, room);\n } catch (e) {\n const code = (e as { code?: unknown }).code;\n if (typeof code !== \"string\") throw e;\n const f = outbox.file(ref);\n if (!unreadable.has(f)) {\n unreadable.add(f);\n log(state, `drain: can't read ${f} (${code}); skipped, its cursors stay`);\n }\n return null;\n }\n };\n const drainSink = async (sink: Sink): Promise<void> => {\n let backoff = readBackoff(state, sink.name);\n if (backoff && !o.force && backoff.until > Date.now()) {\n delivered[sink.name] = 0;\n return;\n }\n const cursors = new Cursors(state, sink.name);\n const c = cursors.read();\n // Issue #208: the tool events this sink is not sent are skipped here, the cursor\n // moving over them. The clock moves with the cursor, only once a batch went\n // through, so a refused batch's tool event is chosen again next time.\n const mode = modes.get(sink.name) as ToolsMode; // buildSinks names each sink after its config\n const tools = new ToolClock(state, sink.name);\n let clockNow = mode === \"all\" ? {} : tools.read();\n let clockNext = { ...clockNow };\n let sent = 0;\n let stalled = false;\n // Pull filtered events session by session into one batch; remember where each\n // session's offset would land if the batch goes through.\n let batch: Event[] = [];\n let pending: Record<string, number> = {};\n // HTTP.md \u00A72 413: halve the batch and try again; one event too large on its own\n // is dropped and logged. A half the receiver drops (400) is dropped and logged on\n // its own, and the other half is still sent: the 400 answered that half, not the\n // batch. A half that is refused for another reason stops the run like any\n // refusal: the cursor stays, and the halves that went through are sent again\n // next time (the receiver counts them as duplicates).\n const deliver = async (events: Event[]): Promise<DeliveryResult> => {\n if (sink instanceof HttpSink) noteSent(state, clock.now());\n const res = await sink.send(events);\n if (res.ok || res.action !== \"split\") return res;\n if (events.length === 1) {\n const e = events[0] as Event;\n log(state, `sink ${sink.name}: 413 ${res.error ?? \"\"} \u2192 event ${e.id} (${e.type}) is too large alone, dropped`);\n return { ok: true, acknowledged: 0, accepted: 0, duplicates: 0, rejected: [{ id: e.id, reason: \"too_large\" }] };\n }\n const part = async (some: Event[]): Promise<DeliveryResult> => {\n const r = await deliver(some);\n if (r.ok || r.action !== \"drop\") return r;\n log(\n state,\n `sink ${sink.name}: ${r.status ?? \"\"} ${r.error ?? \"\"} \u2192 drop, ${some.length} of ${events.length} events`,\n );\n const rejected = some.map((e) => ({ id: e.id, reason: \"dropped\" }));\n return { ok: true, acknowledged: 0, accepted: 0, duplicates: 0, rejected };\n };\n const half = Math.ceil(events.length / 2);\n const a = await part(events.slice(0, half));\n if (!a.ok) return a;\n const b = await part(events.slice(half));\n if (!b.ok) return b;\n return {\n ok: true,\n acknowledged: a.acknowledged + b.acknowledged,\n accepted: (a.accepted ?? 0) + (b.accepted ?? 0),\n duplicates: (a.duplicates ?? 0) + (b.duplicates ?? 0),\n rejected: [...(a.rejected ?? []), ...(b.rejected ?? [])],\n };\n };\n const commitClock = (): void => {\n if (mode === \"all\" || JSON.stringify(clockNext) === JSON.stringify(clockNow)) return;\n tools.write(clockNext);\n clockNow = { ...clockNext };\n };\n const send = async (): Promise<boolean> => {\n if (!batch.length) {\n Object.assign(c, pending);\n pending = {};\n cursors.write(c);\n commitClock();\n return true;\n }\n const res = await deliver(batch);\n if (!res.ok) {\n const action = res.action ?? \"retry\";\n let wait = \"\";\n if (action === \"retry\") {\n backoff = noteFailure(state, sink.name, res, backoff);\n wait = `, waits ${Math.round((backoff.until - Date.now()) / 1000)} s`;\n }\n log(state, `sink ${sink.name}: ${res.status ?? \"\"} ${res.error ?? \"\"} \u2192 ${action}${wait}`);\n if (res.action === \"pause\" || res.action === \"lower-tier\") applySinkVerdict(sink.name, res);\n if (res.action !== \"drop\") {\n batch = [];\n pending = {};\n clockNext = { ...clockNow };\n return false;\n }\n } else if (backoff) {\n clearBackoff(state, sink.name);\n backoff = null;\n }\n sent += batch.length;\n Object.assign(c, pending);\n cursors.write(c);\n commitClock();\n batch = [];\n pending = {};\n return true;\n };\n for (const ref of refs) {\n if (stalled || sent >= MAX_PER_RUN) break;\n let offset = pending[cursors.key(ref)] ?? c[cursors.key(ref)] ?? 0;\n for (;;) {\n const room = BATCH - batch.length;\n const read = readOrSkip(ref, offset, room);\n if (!read) break;\n const { events, offset: next } = read;\n if (!events.length) break;\n for (const e of events) {\n const f = filterEvent(e, sink.tier, sink.pii, { home: os.homedir() });\n if (!f) continue;\n if (sendTool(f, mode, clockNext, cursors.key(ref))) batch.push(f);\n else if (o.thinned) o.thinned[sink.name] = (o.thinned[sink.name] ?? 0) + 1;\n }\n offset = next;\n pending[cursors.key(ref)] = offset;\n if (batch.length >= BATCH) {\n if (!(await send())) {\n stalled = true;\n break;\n }\n if (sent >= MAX_PER_RUN) break;\n }\n }\n }\n if (!stalled && (batch.length || Object.keys(pending).length)) if (!(await send())) stalled = true;\n delivered[sink.name] = sent;\n };\n await Promise.all(sinks.map(drainSink));\n return delivered;\n}\n\nfunction applySinkVerdict(name: string, res: { action?: string; max_tier?: number; error?: string }): void {\n try {\n const cfg = readConfig();\n const s = cfg.sinks.find((x) => x.name === name);\n if (!s) return;\n if (res.action === \"pause\") s.paused = `${new Date().toISOString()} ${res.error ?? \"unauthorized\"}`;\n if (res.action === \"lower-tier\" && typeof res.max_tier === \"number\") s.max_tier = res.max_tier as 0 | 1 | 2 | 3;\n writeConfig(cfg);\n } catch {}\n}\n\nexport { VERSION };\nexport const versionLine = (): string => `sessionpipe ${VERSION}`;\nexport const jobsDir = (state: string): string => path.join(state, \"jobs\");\nexport const hasJobs = (state: string): boolean => existsSync(jobsDir(state));\n", "// SPDX-License-Identifier: Apache-2.0\n// Where sessionpipe keeps its files (spec: the client's Files section).\n// config ~/.config/sessionpipe/config.json SESSIONPIPE_CONFIG\n// state ~/.local/state/sessionpipe/ SESSIONPIPE_STATE\n// Windows %APPDATA%\\sessionpipe\\ and %LOCALAPPDATA%\\sessionpipe\\\nimport os from \"node:os\";\nimport path from \"node:path\";\n\n/** The home directory, read when asked: a test that sets HOME after importing core\n * (or a process whose HOME changes) gets the new one, never one cached at load. */\nexport const homeDir = (): string => os.homedir();\n\nexport function configFile(env: NodeJS.ProcessEnv = process.env): string {\n if (env.SESSIONPIPE_CONFIG) return path.resolve(env.SESSIONPIPE_CONFIG);\n if (process.platform === \"win32\")\n return path.join(env.APPDATA || path.join(homeDir(), \"AppData\", \"Roaming\"), \"sessionpipe\", \"config.json\");\n return path.join(env.XDG_CONFIG_HOME || path.join(homeDir(), \".config\"), \"sessionpipe\", \"config.json\");\n}\n\nexport function stateDir(env: NodeJS.ProcessEnv = process.env): string {\n if (env.SESSIONPIPE_STATE) return path.resolve(env.SESSIONPIPE_STATE);\n if (process.platform === \"win32\")\n return path.join(env.LOCALAPPDATA || path.join(homeDir(), \"AppData\", \"Local\"), \"sessionpipe\");\n return path.join(env.XDG_STATE_HOME || path.join(homeDir(), \".local\", \"state\"), \"sessionpipe\");\n}\n\n/** `~` for the home directory, as the session block writes paths. */\nexport function tilde(p: string, home: string = homeDir()): string {\n if (!p) return p;\n if (p === home) return \"~\";\n return p.startsWith(home + path.sep) || p.startsWith(`${home}/`) ? `~${p.slice(home.length)}` : p;\n}\n\n/** The other way: `~`, `~/\u2026` (or `~\\\u2026`) under the home directory, as a control `start`'s\n * `cwd` may name a folder an event reported (CONTROL.md \u00A73.1). Anything else, `~user`\n * included, is returned as it is. */\nexport function untilde(p: string, home: string = homeDir()): string {\n if (p === \"~\") return home;\n return /^~[\\\\/]/.test(p) ? path.join(home, p.slice(2)) : p;\n}\n\n/** A file-name-safe id. */\nexport const safeId = (s: string): string =>\n String(s)\n .replace(/[^A-Za-z0-9_.-]/g, \"_\")\n .slice(0, 160);\n", "// SPDX-License-Identifier: Apache-2.0\n// Claude Code (Anthropic) \u2014 docs: https://code.claude.com/docs/en/hooks\n// Config: settings.json in EVERY config dir (~/.claude, $CLAUDE_CONFIG_DIR, ~/.claude-*):\n// one per account; hooks written to one never fire for the others. Reads only the\n// hook's stdin, the transcript it names, the per-pid session registry and the config\n// dir's .claude.json account (its id; its email only for control's pair and hello) \u2014\n// or, with no login there, the API its settings.json names (that one key, no token).\n// The one exception is the person's to make: with `remote_titles` on, the sign-in\n// token is read to ask claude.ai for a session's name, and sent nowhere else.\n// Ported from spacesheep-cli lib/sessions.js.\n\nimport { execFileSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { userInfo } from \"node:os\";\nimport path from \"node:path\";\nimport { homeDir } from \"../paths.js\";\nimport { ends, parseLines, recentFiles, scanLines } from \"../readers/files.js\";\nimport { gitFacts } from \"../readers/git.js\";\nimport { isInjected, readClaude } from \"../readers/transcripts.js\";\nimport type { DataOf, HarnessAccount } from \"../types.js\";\nimport {\n formatJson,\n installClaudeShaped,\n installedClaudeShaped,\n OURS,\n readJson,\n readJsonFile,\n uninstallClaudeShaped,\n writeBack,\n} from \"./claude-shaped.js\";\nimport type {\n Adapter,\n BackfillRow,\n FactsState,\n HookCommand,\n HookInput,\n HookResult,\n InstallOptions,\n InstallReport,\n SessionFacts,\n} from \"./types.js\";\n\nconst NAME = \"claude-code\";\nexport const CLAUDE_EVENTS = [\n \"SessionStart\",\n \"UserPromptSubmit\",\n \"PreToolUse\",\n \"PostToolUse\",\n \"PostToolUseFailure\",\n \"PermissionRequest\",\n \"Notification\",\n \"Stop\",\n \"SubagentStart\",\n \"SubagentStop\",\n \"PreCompact\",\n \"PostCompact\",\n \"SessionEnd\",\n \"StopFailure\",\n] as const;\n/** With no sink above tier 0 only the heartbeat needs a tool event: one, not three (issue #12). */\nconst LEAN_SKIP = new Set([\"PreToolUse\", \"PostToolUseFailure\"]);\nconst eventsFor = (lean?: boolean) => (lean ? CLAUDE_EVENTS.filter((e) => !LEAN_SKIP.has(e)) : [...CLAUDE_EVENTS]);\nconst OPTS = {\n matcherFor: (e: string) => (/Tool|Permission/.test(e) ? \"\" : undefined),\n timeout: 5,\n // The hook waits up to 125 s for a verified control answer, and only when control\n // is paired on this machine; everywhere else it exits in milliseconds as before.\n timeoutFor: (e: string) => (e === \"PermissionRequest\" ? 130 : undefined),\n};\nconst DESKTOP_ID = /^local_[A-Za-z0-9-]{1,64}$/;\n\n/** Every Claude Code config dir on this machine. `home` follows the env's HOME, so a\n * caller with its own env (a test) never reaches the real ~/.claude: the install test\n * once rewrote a real settings.json with its fake hook paths on every run. */\nexport function claudeDirs(env: NodeJS.ProcessEnv = process.env, home = env.HOME || homeDir()): string[] {\n const out: string[] = [];\n const add = (d?: string) => {\n if (!d) return;\n const r = path.resolve(d);\n if (!out.includes(r)) out.push(r);\n };\n add(path.join(home, \".claude\"));\n add(env.CLAUDE_CONFIG_DIR);\n try {\n for (const e of readdirSync(home, { withFileTypes: true })) {\n if (!e.isDirectory() || !/^\\.claude[-_.]/.test(e.name)) continue;\n const d = path.join(home, e.name);\n if (existsSync(path.join(d, \"projects\")) || existsSync(path.join(d, \"settings.json\"))) add(d);\n }\n } catch {}\n return out.filter((d, i) => i === 0 || existsSync(d));\n}\nconst settingsIn = (dir: string) => path.join(dir, \"settings.json\");\nconst configDirOf = (transcript: string) => path.dirname(path.dirname(path.dirname(transcript)));\n\nconst validTool = (n: unknown): n is string => typeof n === \"string\" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);\nconst endReason = (r: unknown): \"clear\" | \"logout\" | \"exit\" | \"other\" =>\n r === \"clear\" || r === \"logout\" || r === \"exit\" ? r : r === \"prompt_input_exit\" ? \"exit\" : \"other\";\nconst notifKind = (t: unknown): \"permission\" | \"question\" | \"elicitation\" | \"idle\" | \"error\" =>\n t === \"permission_prompt\"\n ? \"permission\"\n : t === \"idle_prompt\"\n ? \"idle\"\n : t === \"elicitation_dialog\"\n ? \"elicitation\"\n : \"question\";\n\nexport const claudeCode: Adapter = {\n name: NAME,\n events: CLAUDE_EVENTS,\n detect: (env = process.env) => claudeDirs(env).some((d) => existsSync(d)),\n configFiles: (env = process.env) => claudeDirs(env).map(settingsIn),\n install: (cmd, env = process.env, opts: InstallOptions = {}) =>\n claudeDirs(env).flatMap((d) => {\n const hooks = installClaudeShaped(settingsIn(d), NAME, eventsFor(opts.lean), cmd, OPTS);\n // A file the hooks could not go into (not JSON, or refused) gets the one report, theirs.\n const line =\n opts.statusLine === undefined || hooks.skipped ? null : statusLine(settingsIn(d), opts.statusLine, cmd);\n return line ? [hooks, line] : [hooks];\n }),\n uninstall: (env = process.env, opts: InstallOptions = {}) =>\n claudeDirs(env).map((d) => {\n const line = statusLine(settingsIn(d), false);\n const hooks = uninstallClaudeShaped(settingsIn(d), CLAUDE_EVENTS, {\n created: opts.created?.includes(settingsIn(d)),\n });\n // A status line taken out, then the hooks' write refused: \"not touched\" would be\n // false of the file, so the one report says both.\n if (line?.changed && !line.skipped && hooks.skipped)\n return { ...hooks, note: `status line removed, hooks not: ${hooks.note?.replace(/; not touched$/, \"\")}` };\n return line && !hooks.changed && !hooks.skipped ? line : hooks;\n }),\n installed: (cmd, env = process.env, opts: InstallOptions = {}) =>\n claudeDirs(env).map((d) => ({\n file: settingsIn(d),\n state: installedClaudeShaped(settingsIn(d), NAME, eventsFor(opts.lean), cmd, OPTS),\n })),\n\n fromHook(input: HookInput): HookResult | null {\n const event = input.argv[1] ?? \"\";\n let s: Record<string, unknown> = {};\n try {\n // A stdin that is JSON but not an object (`null`, a number) is no payload.\n const j: unknown = input.stdin ? JSON.parse(input.stdin) : {};\n if (j && typeof j === \"object\" && !Array.isArray(j)) s = j as Record<string, unknown>;\n } catch {}\n const id = (s.session_id ?? s.sessionId) as string | undefined;\n if (!id) return null;\n if (notClaudeCode(String(id), s)) return null;\n const session: HookResult[\"session\"] = { id: String(id), cwd: typeof s.cwd === \"string\" ? s.cwd : input.cwd };\n if (typeof s.model === \"string\") session.model = s.model;\n if (typeof s.agent_id === \"string\" && event.startsWith(\"Subagent\") === false && s.agent_id)\n session.parent_id = null;\n const turn_id = typeof s.prompt_id === \"string\" ? s.prompt_id : undefined;\n const tool = validTool(s.tool_name) ? s.tool_name : undefined;\n const call_id = typeof s.tool_use_id === \"string\" ? s.tool_use_id : undefined;\n const ms = typeof s.duration_ms === \"number\" ? Math.round(s.duration_ms) : undefined;\n const agent_id = typeof s.agent_id === \"string\" ? s.agent_id : undefined;\n // PermissionRequest carries no tool_use_id (recorded 2026-09-28): the id is a hash\n // of the prompt, the tool and its input, so the same prompt yields the same id and\n // a later control answer can name it.\n const permissionId = () =>\n `perm-${createHash(\"sha256\")\n .update(`${turn_id ?? \"\"}|${tool ?? \"\"}|${JSON.stringify(s.tool_input ?? null)}`)\n .digest(\"hex\")\n .slice(0, 16)}`;\n const ev = (type: string, data: Record<string, unknown>) => ({ type, data: prune(data), harnessEvent: event });\n const events: HookResult[\"events\"] = [];\n switch (event) {\n case \"SessionStart\":\n events.push(ev(\"session.started\", { source: pickSource(s.source ?? s.trigger) }));\n break;\n case \"UserPromptSubmit\": {\n // Only a string prompt is read; anything else is left unread rather than guessed at.\n const prompt = typeof s.prompt === \"string\" ? s.prompt : undefined;\n events.push(\n ev(\"turn.started\", {\n turn_id,\n prompt_chars: prompt === undefined ? undefined : [...prompt].length,\n // Claude Code's hook schema documents `source`\n // (user | sdk | system | loop_wakeup | schedule_wakeup | poll_event) and says a\n // payload may omit it while the field rolls out; 2.1.304's payload has no such\n // key at all. Forwarded verbatim, shape-guarded only: the list is the harness's\n // to extend.\n submitted_by: harnessWord(s.source),\n // One of Claude Code's own prompt wrappers: the prompt is a block the harness\n // wrote, so it is not the person's words. Absent is unknown, never \"the person\" \u2014\n // and `true` is never \"no person caused this turn\", because a Talk or\n // `sessionpipe wait` delivery hands the person's own message over inside a\n // <task-notification> (PROTOCOL.md \u00A74).\n injected: prompt !== undefined && injectedBlock(prompt) ? true : undefined,\n }),\n );\n break;\n }\n case \"PreToolUse\":\n if (tool) events.push(ev(\"tool.started\", { tool, call_id, turn_id, agent_id, input: s.tool_input }));\n break;\n case \"PostToolUse\":\n if (tool)\n events.push(\n ev(\"tool.ended\", {\n tool,\n call_id,\n turn_id,\n agent_id,\n ms,\n ok: true,\n input: s.tool_input,\n output: s.tool_response,\n }),\n );\n break;\n case \"PostToolUseFailure\":\n if (tool)\n events.push(\n ev(\"tool.ended\", {\n tool,\n call_id,\n turn_id,\n agent_id,\n ms,\n ok: false,\n error: errText(s.error ?? s.tool_response),\n input: s.tool_input,\n output: s.tool_response,\n }),\n );\n break;\n case \"PermissionRequest\":\n events.push(\n ev(\"attention.needed\", {\n attention_id: call_id ?? permissionId(),\n kind: \"permission\",\n tool,\n message: tool ? `${tool}${describe(s.tool_input)}` : undefined,\n agent_id,\n }),\n );\n break;\n case \"Notification\":\n events.push(\n ev(\"attention.needed\", {\n attention_id: `notif-${Date.now()}`,\n kind: notifKind(s.notification_type),\n message: typeof s.message === \"string\" ? s.message.slice(0, 200) : undefined,\n agent_id,\n }),\n );\n break;\n case \"Stop\":\n // In-flight background work (shell commands, monitors, subagents, workflows)\n // that will wake the session when it finishes: a paused session, not a done one\n // (Claude Code's `background_tasks`, in every Stop recorded since 2.1.258). A count, never what they run.\n events.push(ev(\"turn.ended\", { turn_id, reason: \"stop\", background: backgroundCount(s.background_tasks) }));\n break;\n case \"StopFailure\":\n // The turn ended on an API error (rate_limit, overloaded, authentication_failed,\n // server_error \u2026); the payload key is `error` (issue #10, recorded 2026-09-28).\n events.push(\n ev(\"turn.ended\", {\n turn_id,\n reason: \"error\",\n error: typeof s.error === \"string\" ? s.error.slice(0, 200) : undefined,\n }),\n );\n break;\n case \"SubagentStart\":\n events.push(\n ev(\"subagent.started\", {\n agent_id: String(s.agent_id ?? \"\"),\n agent_type: typeof s.agent_type === \"string\" ? s.agent_type : undefined,\n }),\n );\n break;\n case \"SubagentStop\":\n // The same list, with this subagent still in it: the count once it is out, so a\n // receiver holding the session as waiting on its work sees the count fall (#153).\n events.push(\n ev(\"subagent.ended\", {\n agent_id: String(s.agent_id ?? \"\"),\n agent_type: typeof s.agent_type === \"string\" ? s.agent_type : undefined,\n background: backgroundCount(s.background_tasks, String(s.agent_id ?? \"\")),\n }),\n );\n break;\n case \"PreCompact\":\n events.push(ev(\"context.compacted\", { trigger: s.trigger === \"manual\" ? \"manual\" : \"auto\" }));\n break;\n case \"PostCompact\":\n break; // PreCompact already said it; a second event would double-count\n case \"SessionEnd\":\n events.push(ev(\"session.ended\", { reason: endReason(s.reason) }));\n break;\n case \"StatusLine\": {\n // Not a hook: our status line command (install --status-line), which hook.ts\n // hands on only when the limits changed.\n const limits = claudeLimits(s.rate_limits);\n if (!limits) return null;\n events.push(ev(\"limits.observed\", limits));\n break;\n }\n default:\n return null;\n }\n const hints: Record<string, unknown> = {};\n // The prompt, for the turn's first turn.progress: the transcript may not hold it\n // yet when the worker runs. Never an event field of its own (tier 2 carries it).\n if (event === \"UserPromptSubmit\" && typeof s.prompt === \"string\") hints.prompt = s.prompt;\n const host = input.env.CLAUDE_CODE_HOST_SESSION_ID;\n if (host && DESKTOP_ID.test(host)) hints.desktop_id = host;\n // Whether a person attends the session, in Claude Code's own word (\"1\" or \"0\" in a hook's\n // environment); a turn the control daemon runs on the person's signed message is theirs.\n // facts() turns it into session.attended once it knows whether the session has a link.\n const attended = input.env.CLAUDE_CODE_SESSION_ATTENDED;\n if (attended === \"1\" || attended === \"0\") hints.attended = attended === \"1\" || !!input.env.SESSIONPIPE_CONTROL_JOB;\n // The registry file is found while the process tree stands (the detached\n // worker's parent is gone by the time it runs). Heartbeat-class events skip it.\n if (event !== \"PreToolUse\" && event !== \"PostToolUse\") hints.claude_pid = claudePid(input.env);\n const r: HookResult = { session, events, hints };\n if (typeof s.transcript_path === \"string\") r.transcript = s.transcript_path;\n return r;\n },\n\n readTranscript(file, fromLine) {\n let text = \"\";\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return { turns: [], line: fromLine };\n }\n return readClaude(text.split(\"\\n\"), fromLine);\n },\n\n facts(session, transcript, hints, state): SessionFacts {\n const out: SessionFacts = {};\n if (transcript) Object.assign(out, claudeFacts(transcript, session.id, hints, state));\n const cwd = out.cwd ?? session.cwd;\n Object.assign(out, gitFacts(cwd), out.branch ? { branch: out.branch } : {});\n if (!out.url && typeof hints.desktop_id === \"string\") out.url = `claude://code/continue/${hints.desktop_id}`;\n // A Remote Control session says \"0\" though a person drives it from claude.ai: it runs as a\n // headless child of `claude remote-control`. Its link is what tells it from a `claude -p`;\n // CLAUDE_CODE_ENVIRONMENT_KIND cannot, since a `claude -p` started inside a Remote Control\n // session inherits \"bridge\", while the registry entry it writes has no bridge id of its own.\n if (typeof hints.attended === \"boolean\") out.attended = hints.attended || !!out.url;\n return out;\n },\n\n remoteFacts: (session, local, transcript, state, env = process.env) =>\n claudeRemoteFacts(session.id, local, transcript, state, env),\n\n backfill(sinceMs, env = process.env): BackfillRow[] {\n const rows: BackfillRow[] = [];\n const files: string[] = [];\n for (const d of claudeDirs(env))\n files.push(...recentFiles(path.join(d, \"projects\"), 1, sinceMs, (n) => /^[0-9a-f-]{36}\\.jsonl$/.test(n)));\n const noState: FactsState = { get: () => ({}), save: () => {} };\n for (const f of files) {\n const id = path.basename(f, \".jsonl\");\n const facts = claudeFacts(f, id, {}, noState);\n if (!facts.started_at || !facts.last_at) continue;\n const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;\n const cwd = rest.cwd;\n rows.push({\n session: { id, ...rest, ...gitFacts(cwd), ...(rest.branch ? { branch: rest.branch } : {}) },\n started_at,\n last_at,\n transcript: f,\n });\n }\n return rows;\n },\n};\n\n/** sessionpipe's own waiter (CONTROL.md \u00A78), which a session runs as a background command\n * to stay reachable: it ends only when a message arrives, so it is not work the session\n * waits on. Matched by its command, whatever path or shim runs it. */\nconst WAITER_RE = /(?:^|[\\s\"'`/\\\\])sessionpipe(?:\\.\\w+)?[\"']?\\s+wait(?:\\s|$)/;\n\n/** How much background work Claude Code lists that will wake the session when it ends\n * (`background_tasks`, in every Stop and SubagentStop since 2.1.258): a count, never what\n * it runs. Not counted: sessionpipe's own waiter, and the subagent whose own stop this is,\n * which Claude Code still lists as running (issue #153). Undefined when the harness\n * doesn't say. */\nexport function backgroundCount(tasks: unknown, ending?: string): number | undefined {\n if (!Array.isArray(tasks)) return undefined;\n return tasks.filter((t) => {\n const task = t as { id?: unknown; command?: unknown } | null;\n if (ending !== undefined && task?.id === ending) return false;\n return !(typeof task?.command === \"string\" && WAITER_RE.test(task.command));\n }).length;\n}\n\n/** Claude Code runs a status line only on events (a new assistant message, a window's\n * reset), and the hook sends at most one change a minute, so a turn's last numbers\n * would wait for the next prompt. The timer runs it again while the session is idle. */\nconst LINE_REFRESH_S = 60;\n\n/** Claude Code hands an account's limits only to its status line command\n * (https://code.claude.com/docs/en/statusline), so with `--status-line` ours is that\n * command. Someone else's status line is never replaced. Null: nothing to report. */\nfunction statusLine(file: string, want: boolean, cmd?: HookCommand): InstallReport | null {\n const f = readJsonFile(file);\n if (!f.ok) return null; // the hooks' report already says why\n const cur = f.value.statusLine as { command?: unknown; refreshInterval?: unknown } | undefined;\n const ours = typeof cur?.command === \"string\" && OURS.test(cur.command);\n const command = cmd?.([NAME, \"StatusLine\"]).command;\n if (want && cur && !ours)\n return { file, changed: false, skipped: true, note: \"a status line of your own is set; limits are not read\" };\n if (want ? ours && cur?.command === command && cur?.refreshInterval === LINE_REFRESH_S : !ours) return null;\n if (want) f.value.statusLine = { type: \"command\", command, refreshInterval: LINE_REFRESH_S };\n else delete f.value.statusLine;\n return (\n writeBack(file, formatJson(f.value, f), f.existed) ?? {\n file,\n changed: true,\n note: want ? \"status line written (account limits)\" : \"status line removed\",\n }\n );\n}\n\n/** Epoch seconds as RFC 3339; anything that is not a plausible time is no time. */\nconst epochIso = (t: unknown): string | undefined =>\n typeof t === \"number\" && t > 0 && t < 1e11 ? new Date(t * 1000).toISOString() : undefined;\n\nconst WINDOW_MINUTES: Record<string, number> = { five_hour: 300, seven_day: 10_080 };\n\n/** The status line's `rate_limits`: `five_hour`, `seven_day` and, behind a gateway,\n * `spend_limit`, each with `used_percentage` and `resets_at` in epoch seconds. Only\n * Pro and Max accounts (or a gateway) have them, and only after a session's first\n * response; a window drops out once it resets. */\nexport function claudeLimits(rl: unknown): DataOf<\"limits.observed\"> | null {\n if (!rl || typeof rl !== \"object\" || Array.isArray(rl)) return null;\n const windows: DataOf<\"limits.observed\">[\"windows\"] = [];\n for (const [id, w] of Object.entries(rl as Record<string, { used_percentage?: unknown; resets_at?: unknown }>)) {\n if (!/^[a-z0-9_]{1,64}$/.test(id) || typeof w?.used_percentage !== \"number\" || w.used_percentage < 0) continue;\n windows.push(\n prune({\n id,\n used_percent: w.used_percentage,\n window_minutes: WINDOW_MINUTES[id],\n resets_at: epochIso(w.resets_at),\n }),\n );\n }\n return windows.length ? { windows: windows.slice(0, 8) } : null;\n}\n\nfunction prune<T extends Record<string, unknown>>(o: T): T {\n for (const k of Object.keys(o)) if (o[k] === undefined) delete o[k];\n return o;\n}\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n/** Another harness reading this settings file, not Claude Code. Several harnesses\n * import `~/.claude/settings.json` hooks, so our entries there can be run by a tool\n * that has its own adapter: it would arrive under the wrong name, with ids that mean\n * nothing to a Claude Code reader. Every Claude Code payload names a transcript and a\n * UUID session (all 21 recorded fixtures do); a payload with neither is somebody else's. */\nexport function notClaudeCode(id: string, s: Record<string, unknown>): boolean {\n return typeof s.transcript_path !== \"string\" && !UUID.test(id);\n}\nfunction pickSource(v: unknown): string {\n return typeof v === \"string\" && [\"startup\", \"resume\", \"clear\", \"compact\", \"fork\"].includes(v) ? v : \"unknown\";\n}\n/** One of the harness's own words for who submitted a prompt, forwarded as is (PROTOCOL.md\n * \u00A74, `turn.started.submitted_by`). Not an enum of ours: a value we have never seen is\n * still carried, so the guard is the shape the spec allows \u2014 a bare word of at most 40\n * characters, which is all a receiver may show or group by. Tier 0 is not redacted\n * (PRIVACY.md \u00A72), so that shape is the only guard there is. */\nconst harnessWord = (v: unknown): string | undefined =>\n typeof v === \"string\" && /^[A-Za-z0-9][A-Za-z0-9_.-]{0,39}$/.test(v) ? v : undefined;\n/** Claude Code's own prompt wrappers, as a CLOSED list: a task notification, a peer message,\n * a system reminder, a local command's output, a slash command's expansion \u2014 with or\n * without a closing tag, after trimming. Narrower than the reader's `isInjected` on\n * purpose, because the two directions do not cost the same. A wrapper we have not recorded\n * yet reads as unknown and a receiver keeps the turn, which is what it does today; a shape\n * test wide enough to catch every wrapper also fires on a person who pasted nothing but an\n * SVG, an HTML page or a JSX element, and a recency board acting on that would drop one of\n * the person's own turns. Dropping one record from a transcript costs little, which is why\n * the reader's test stays wide; ADAPTERS.md (Claude Code \u00B7 Quirks) says why they differ. */\nconst WRAPPER =\n /^<(?:task-notification|cross-session-message|system-reminder|local-command-stdout|command-[a-z-]+)[\\s/>]/i;\nconst injectedBlock = (prompt: string): boolean => WRAPPER.test(prompt.trim());\nfunction errText(v: unknown): string | undefined {\n if (typeof v === \"string\") return v.slice(0, 2000);\n if (v && typeof v === \"object\") {\n const e = v as { error?: unknown; message?: unknown };\n if (typeof e.error === \"string\") return e.error.slice(0, 2000);\n if (typeof e.message === \"string\") return e.message.slice(0, 2000);\n }\n return undefined;\n}\nfunction describe(input: unknown): string {\n if (!input || typeof input !== \"object\") return \"\";\n const i = input as Record<string, unknown>;\n const v = i.command ?? i.file_path ?? i.path ?? i.url ?? i.pattern;\n return typeof v === \"string\" ? ` ${v.slice(0, 120)}` : \"\";\n}\n\n// --- facts: title, url, model, account, from Claude Code's own files ------------------\n\nconst NAME_RANK: Record<string, \"custom\" | \"harness\"> = {\n user: \"custom\",\n auto: \"harness\",\n hook: \"harness\",\n peer: \"harness\",\n};\ninterface Registry {\n sessionId: string | null;\n name: string | null;\n name_source: \"custom\" | \"harness\" | null;\n bridge: string | null;\n}\nfunction registryEntry(j: unknown): Registry | null {\n if (!j || typeof j !== \"object\") return null;\n const r = j as { sessionId?: unknown; name?: unknown; nameSource?: string; bridgeSessionId?: unknown };\n const source = r.nameSource ? NAME_RANK[r.nameSource] : undefined;\n return {\n sessionId: r.sessionId ? String(r.sessionId) : null,\n name: source && typeof r.name === \"string\" && r.name.trim() ? r.name.trim() : null,\n name_source: source ?? null,\n bridge: typeof r.bridgeSessionId === \"string\" ? r.bridgeSessionId : null,\n };\n}\nconst liveCache = new Map<string, Map<string, Registry>>();\nfunction claudeLive(dir: string): Map<string, Registry> {\n const hit = liveCache.get(dir);\n if (hit) return hit;\n const map = new Map<string, Registry>();\n let names: string[] = [];\n try {\n names = readdirSync(path.join(dir, \"sessions\")).filter((n) => n.endsWith(\".json\"));\n } catch {}\n for (const n of names) {\n try {\n const e = registryEntry(JSON.parse(readFileSync(path.join(dir, \"sessions\", n), \"utf8\")));\n if (e?.sessionId) map.set(e.sessionId, e);\n } catch {}\n }\n liveCache.set(dir, map);\n return map;\n}\nfunction claudeLiveByPid(dir: string, pid: unknown): Registry | null {\n if (!pid) return null;\n try {\n return registryEntry(JSON.parse(readFileSync(path.join(dir, \"sessions\", `${pid}.json`), \"utf8\")));\n } catch {\n return null;\n }\n}\n/** The Claude Code process that ran this hook: the nearest ancestor with a registry\n * file (hooks run under a shell, so usually the parent's parent). */\nexport function claudePid(env: NodeJS.ProcessEnv = process.env): number | null {\n try {\n const dirs = claudeDirs(env);\n const has = (pid: number) => dirs.some((d) => existsSync(path.join(d, \"sessions\", `${pid}.json`)));\n let pid = process.ppid;\n if (has(pid)) return pid;\n const parent = parentOf();\n for (let hop = 0; hop < 6 && pid > 1; hop++) {\n pid = parent(pid);\n if (pid > 1 && has(pid)) return pid;\n }\n } catch {}\n return null;\n}\nfunction parentOf(): (pid: number) => number {\n if (process.platform === \"linux\") {\n return (pid) => {\n try {\n const st = readFileSync(`/proc/${pid}/stat`, \"utf8\");\n return Number(st.slice(st.lastIndexOf(\")\") + 2).split(\" \")[1]) || 0;\n } catch {\n return 0;\n }\n };\n }\n const table = new Map<number, number>();\n try {\n const out = execFileSync(\"ps\", [\"-axo\", \"pid=,ppid=\"], {\n encoding: \"utf8\",\n timeout: 2000,\n maxBuffer: 8 * 1024 * 1024,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n for (const l of out.split(\"\\n\")) {\n const m = /^\\s*(\\d+)\\s+(\\d+)/.exec(l);\n if (m) table.set(Number(m[1]), Number(m[2]));\n }\n } catch {}\n return (pid) => table.get(pid) || 0;\n}\nconst EMAIL_RE = /^[^@\\s]{1,64}@[^@\\s]{1,189}$/;\n/** Who a config dir is signed in as, from its .claude.json (the default dir keeps it\n * at ~/.claude.json): the account id, and the email it signs in with. A dir with no\n * claude.ai login that runs on another API is that API's account (apiAccount). */\nfunction claudeLogin(dir: string, home = homeDir()): { id: string; email?: string; name?: string } | null {\n const files = [path.join(dir, \".claude.json\")];\n if (path.resolve(dir) === path.join(home, \".claude\")) files.unshift(path.join(home, \".claude.json\"));\n for (const f of files) {\n try {\n const a = (\n JSON.parse(readFileSync(f, \"utf8\")) as { oauthAccount?: { accountUuid?: unknown; emailAddress?: unknown } }\n ).oauthAccount;\n if (typeof a?.accountUuid === \"string\" && a.accountUuid) {\n const email = typeof a.emailAddress === \"string\" && EMAIL_RE.test(a.emailAddress) ? a.emailAddress : undefined;\n return { id: a.accountUuid.slice(0, 128), ...(email ? { email } : {}) };\n }\n } catch {}\n }\n return apiAccount(dir);\n}\n/** Anthropic's own API: a key for it is an Anthropic account no local file names. */\nconst ANTHROPIC_HOST = /(^|\\.)anthropic\\.com$/i;\n/** A config dir whose settings send Claude Code to another API (`env.ANTHROPIC_BASE_URL`:\n * Kimi on a Modal endpoint, GLM on Z.ai) has no claude.ai login, so its account is the\n * endpoint: `api-` and 16 hex of the SHA-256 of its origin for events (the same on every\n * machine that uses it), and its host as the name control's pair and hello give it.\n * Only that one key is read; the tokens and headers beside it never are. */\nfunction apiAccount(dir: string): { id: string; name: string } | null {\n const base = (readJson(settingsIn(dir)) as { env?: { ANTHROPIC_BASE_URL?: unknown } }).env?.ANTHROPIC_BASE_URL;\n if (typeof base !== \"string\") return null;\n let u: URL;\n try {\n u = new URL(base.trim());\n } catch {\n return null;\n }\n if ((u.protocol !== \"https:\" && u.protocol !== \"http:\") || !u.hostname || ANTHROPIC_HOST.test(u.hostname))\n return null;\n // The origin alone: a path or a user:password@ can carry a key.\n const origin = `${u.protocol}//${u.host}`;\n return { id: `api-${createHash(\"sha256\").update(origin).digest(\"hex\").slice(0, 16)}`, name: u.host };\n}\nconst accountCache = new Map<string, string | null>();\n/** The account id for `session.account_id`: never the email. Events go to every sink,\n * a team board included; the email (or an API's host) reaches only a receiver the\n * person paired with, in control's pair and hello (claudeAccounts). */\nfunction claudeAccount(dir: string): string | null {\n const hit = accountCache.get(dir);\n if (hit !== undefined) return hit;\n const acct = claudeLogin(dir)?.id ?? null;\n accountCache.set(dir, acct);\n return acct;\n}\n/** Every account signed in across these config dirs, read fresh (a person signs in to\n * another account without restarting anything), for control's pair and hello. */\nexport function claudeAccounts(dirs: string[], home = homeDir()): HarnessAccount[] {\n const out: HarnessAccount[] = [];\n for (const d of dirs) {\n const l = claudeLogin(d, home);\n if (!l) continue;\n const seen = out.find((a) => a.id === l.id);\n if (!seen) out.push({ harness: NAME, ...l });\n else if (!seen.email && l.email) seen.email = l.email;\n }\n return out;\n}\nconst LINK_RE = /^https:\\/\\/claude\\.ai\\/code\\/session_[A-Za-z0-9]+$/;\nfunction transcriptLink(records: Record<string, unknown>[]): string | null {\n for (let i = records.length - 1; i >= 0; i--) {\n const j = records[i] as { type?: string; subtype?: string; url?: unknown; bridgeSessionId?: unknown };\n if (j.type === \"system\" && j.subtype === \"bridge_status\" && typeof j.url === \"string\" && LINK_RE.test(j.url))\n return j.url;\n if (j.type === \"bridge-session\" && typeof j.bridgeSessionId === \"string\") {\n const m = /^(?:cse|session)_([A-Za-z0-9]+)$/.exec(j.bridgeSessionId);\n if (m) return `https://claude.ai/code/session_${m[1]}`;\n }\n }\n return null;\n}\n/** A first ask as the Claude Code app titles an untitled session: whitespace\n * collapsed, whole up to 50 characters, else its first 50 and \"...\". */\nexport function appTitle(text: string): string {\n const t = String(text).replace(/\\s+/g, \" \").trim();\n return t.length > 50 ? `${t.slice(0, 50)}...` : t;\n}\nfunction askText(j: Record<string, unknown> | null): string | null {\n if (!j || j.type !== \"user\" || j.isMeta || j.isSidechain) return null;\n const origin = j.origin as { kind?: string } | undefined;\n if (origin?.kind && origin.kind !== \"human\") return null;\n const c = (j.message as { content?: unknown } | undefined)?.content;\n const text = (\n typeof c === \"string\"\n ? c\n : Array.isArray(c)\n ? c\n .filter((b) => b && b.type === \"text\" && typeof b.text === \"string\")\n .map((b) => b.text as string)\n .join(\"\\n\")\n : \"\"\n ).trim();\n if (!text || isInjected(text)) return null;\n return text;\n}\nfunction firstAsk(records: Record<string, unknown>[]): string | null {\n for (const j of records) {\n const t = askText(j);\n if (t) return appTitle(t);\n }\n return null;\n}\nfunction scanTitles(file: string, from: number, found: { custom?: string | null; ai?: string | null }) {\n const out = { custom: found.custom ?? null, ai: found.ai ?? null, upto: from };\n let size = 0;\n try {\n size = statSync(file).size;\n } catch {\n return out;\n }\n if (size < from) {\n out.upto = 0;\n out.custom = out.ai = null;\n from = 0;\n }\n // Only lines carrying a title record are parsed; the rest of the new bytes are skipped.\n let pos = 0;\n scanLines<null>(\n file,\n (l) => {\n pos += Buffer.byteLength(l, \"utf8\") + 1;\n if (pos <= from || l.indexOf('-title\"') < 0) return null;\n try {\n const j = JSON.parse(l) as { type?: string; customTitle?: string; aiTitle?: string };\n if (j.type === \"custom-title\" && j.customTitle) out.custom = j.customTitle;\n if (j.type === \"ai-title\" && j.aiTitle) out.ai = j.aiTitle;\n } catch {}\n return null;\n },\n 512 * 1024 * 1024,\n );\n out.upto = size;\n return out;\n}\n\n/** A session's title and where it came from, best first: /rename (registry \"user\"\n * name or custom-title) \u2192 Claude Code's own title (registry \"auto\" name or\n * ai-title) \u2192 the first ask, marked first-ask so a real title later replaces it. */\nexport function claudeFacts(\n file: string,\n sessionId: string,\n hints: Record<string, unknown>,\n state: FactsState,\n): SessionFacts {\n const { head, tail } = ends(file, 64 * 1024, 256 * 1024);\n const H = parseLines(head);\n const T = parseLines(tail);\n const out: SessionFacts = {};\n const id = sessionId || path.basename(file, \".jsonl\");\n const mem = state.get(NAME, id);\n let custom: string | null = null;\n let ai: string | null = null;\n for (const j of H.concat(T)) {\n if (j.type === \"custom-title\" && typeof j.customTitle === \"string\") custom = j.customTitle;\n if (j.type === \"ai-title\" && typeof j.aiTitle === \"string\") ai = j.aiTitle;\n }\n let size = 0;\n try {\n size = statSync(file).size;\n } catch {}\n if (!custom && !ai && size > 64 * 1024 + 256 * 1024) {\n const s = scanTitles(file, Number(mem.scan_upto) || 0, {\n custom: mem.scan_custom as string | null,\n ai: mem.scan_ai as string | null,\n });\n custom = s.custom;\n ai = s.ai;\n state.save(NAME, id, { scan_upto: s.upto, scan_custom: s.custom, scan_ai: s.ai });\n }\n const dir = configDirOf(file);\n const live = claudeLive(dir).get(id) ?? claudeLiveByPid(dir, hints.claude_pid);\n if (live?.name) state.save(NAME, id, { name: live.name, name_source: live.name_source });\n const bridge =\n live?.bridge && /^session_[A-Za-z0-9]+$/.test(live.bridge) ? `https://claude.ai/code/${live.bridge}` : null;\n const url = bridge ?? transcriptLink(T) ?? transcriptLink(H) ?? (typeof mem.url === \"string\" ? mem.url : null);\n if (url) {\n out.url = url;\n state.save(NAME, id, { url });\n }\n const name = live?.name\n ? { title: live.name, source: live.name_source as \"custom\" | \"harness\" }\n : typeof mem.name === \"string\"\n ? { title: mem.name, source: mem.name_source as \"custom\" | \"harness\" }\n : null;\n const pick =\n (name && name.source === \"custom\" && name) ||\n (custom && { title: custom, source: \"custom\" as const }) ||\n name ||\n (ai && { title: ai, source: \"harness\" as const });\n let ask = typeof mem.first_ask === \"string\" ? mem.first_ask : firstAsk(H);\n if (!ask && size > 64 * 1024 && !mem.first_ask_none) {\n ask = scanLines<string>(file, (l) => {\n try {\n const t = askText(JSON.parse(l) as Record<string, unknown>);\n return t ? appTitle(t) : null;\n } catch {\n return null;\n }\n });\n if (!ask && size > 16 * 1024 * 1024) state.save(NAME, id, { first_ask_none: true });\n }\n if (ask) {\n out.first_ask = ask;\n if (!mem.first_ask) state.save(NAME, id, { first_ask: ask });\n }\n if (pick) {\n out.title = String(pick.title).slice(0, 120);\n out.title_source = pick.source;\n } else if (ask) {\n out.title = ask;\n out.title_source = \"first-ask\";\n }\n const acct = claudeAccount(dir);\n if (acct) out.account_id = acct;\n for (let i = T.length - 1; i >= 0; i--) {\n const m = (T[i] as { message?: { model?: string } }).message?.model;\n if (T[i]?.type === \"assistant\" && m && m !== \"<synthetic>\") {\n out.model = m;\n break;\n }\n }\n const meta = [...T].reverse().find((j) => j.version) ?? H.find((j) => j.version);\n if (meta?.version) out.harness_version = String(meta.version);\n const times = H.concat(T)\n .map((j) => Date.parse(String(j.timestamp ?? \"\")))\n .filter((n) => n > 0);\n if (times.length) {\n out.started_at = Math.min(...times);\n out.last_at = Math.max(...times);\n }\n const withCwd = H.find((j) => typeof j.cwd === \"string\");\n if (withCwd) {\n out.cwd = withCwd.cwd as string;\n if (typeof withCwd.gitBranch === \"string\" && withCwd.gitBranch !== \"HEAD\") out.branch = withCwd.gitBranch;\n }\n return out;\n}\n\n// --- claude.ai's name for a session, only when the person turned it on ------------------\n\n/** claude.ai names a Remote Control session itself, seconds after its first message, and\n * never tells the machine: no file here holds that name, so the Claude app shows one\n * and every local reader another. With `remote_titles` on, the worker asks claude.ai the\n * way Claude Code reads a session. Again after a minute while it has no name yet, then\n * every half hour: a rename in the Claude app reaches the transcript as a custom-title,\n * which outranks this anyway. */\nconst REMOTE_RETRY_MS = 60_000;\nconst REMOTE_REFRESH_MS = 30 * 60_000;\nconst CLAUDE_API = \"https://api.anthropic.com\";\nconst KEYCHAIN = \"Claude Code-credentials\";\n/** The worker holds the session's lock while it asks, and a sibling hook waits 6 s for it. */\nconst REMOTE_TIMEOUT_MS = 3000;\nconst KEYCHAIN_TIMEOUT_MS = 2000;\n/** A session is a few kB; anything past this is not one. */\nconst REMOTE_MAX_BYTES = 1 << 20;\n/** A name as Claude Code itself cleans one: control and bidirectional characters become\n * spaces, runs of space one, the ends trimmed. */\nconst cleanName = (s: string) =>\n s\n .replace(/[\\p{Cc}\\u061C\\u200E\\u200F\\u202A-\\u202E\\u2066-\\u2069]/gu, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n\n/** claude.ai's name for a session with a claude.ai link, unless the person named it\n * here: `{title, title_source: \"harness\"}`, or nothing. */\nexport async function claudeRemoteFacts(\n id: string,\n local: SessionFacts,\n transcript: string | undefined,\n state: FactsState,\n env: NodeJS.ProcessEnv,\n now = Date.now(),\n): Promise<Partial<SessionFacts>> {\n if (!transcript || !local.url || !LINK_RE.test(local.url) || local.title_source === \"custom\") return {};\n const mem = state.get(NAME, id);\n let title = typeof mem.remote_title === \"string\" ? mem.remote_title : null;\n if (now - (Number(mem.remote_at) || 0) >= (title ? REMOTE_REFRESH_MS : REMOTE_RETRY_MS)) {\n // Marked before the call: a claude.ai that doesn't answer isn't asked on every hook.\n state.save(NAME, id, { remote_at: now });\n const token = claudeAiToken(configDirOf(transcript), env, now);\n const got = token ? await claudeAiTitle(local.url.slice(local.url.lastIndexOf(\"/\") + 1), token) : undefined;\n if (got !== undefined) {\n title = got?.slice(0, 200) ?? null;\n state.save(NAME, id, { remote_title: title });\n }\n }\n return title ? { title: title.slice(0, 120), title_source: \"harness\" } : {};\n}\n\n/** The keychain items Claude Code keeps a config dir's sign-in under: `Claude\n * Code-credentials`, and for a CLAUDE_CONFIG_DIR, `-` and 8 hex of the SHA-256 of that\n * path as it was set (tried as the hook's env names it, then as the dir itself). */\nfunction keychainServices(dir: string, env: NodeJS.ProcessEnv): string[] {\n const named = (p: string) =>\n `${KEYCHAIN}-${createHash(\"sha256\").update(p.normalize(\"NFC\")).digest(\"hex\").slice(0, 8)}`;\n const out = new Set<string>();\n if (env.CLAUDE_CONFIG_DIR && path.resolve(env.CLAUDE_CONFIG_DIR) === dir) out.add(named(env.CLAUDE_CONFIG_DIR));\n if (dir === path.join(env.HOME || homeDir(), \".claude\")) out.add(KEYCHAIN);\n out.add(named(dir));\n return [...out];\n}\n\n/** A token this process already read, by config dir, until a minute before it expires:\n * `sessionpipe titles on` asks about every recent session, one `security` each would be\n * slow. In memory only, for the life of the process. */\nconst tokenCache = new Map<string, { token: string; expires: number }>();\n/** The access token Claude Code signed in to `dir` with: from the macOS keychain, else\n * from `<dir>/.credentials.json`. Never written anywhere, never logged. One that has\n * expired is not refreshed, since that would rotate Claude Code's own login: the lookup\n * waits for Claude Code to refresh it. */\nexport function claudeAiToken(dir: string, env: NodeJS.ProcessEnv, now: number): string | null {\n const hit = tokenCache.get(dir);\n if (hit && hit.expires > now + 60_000) return hit.token;\n const raw: string[] = [];\n if (process.platform === \"darwin\")\n for (const service of keychainServices(dir, env)) {\n try {\n const user = env.USER || userInfo().username;\n const account = /^[\\w.-]+$/.test(user) ? user : \"claude-code-user\";\n raw.push(\n execFileSync(\"security\", [\"find-generic-password\", \"-a\", account, \"-w\", \"-s\", service], {\n encoding: \"utf8\",\n timeout: KEYCHAIN_TIMEOUT_MS,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }),\n );\n break;\n } catch {}\n }\n try {\n raw.push(readFileSync(path.join(dir, \".credentials.json\"), \"utf8\"));\n } catch {}\n for (const r of raw) {\n try {\n // Claude Code writes the item as hex (`-X`); `security -w` prints it back as text,\n // or as that hex where it isn't printable.\n const t = r.trim();\n const o = (\n JSON.parse(/^(?:[0-9a-f]{2})+$/i.test(t) ? Buffer.from(t, \"hex\").toString(\"utf8\") : t) as {\n claudeAiOauth?: { accessToken?: unknown; expiresAt?: unknown };\n }\n ).claudeAiOauth;\n if (typeof o?.accessToken !== \"string\" || !o.accessToken) continue;\n const expires = typeof o.expiresAt === \"number\" ? o.expiresAt : Number.POSITIVE_INFINITY;\n if (expires <= now + 60_000) continue;\n tokenCache.set(dir, { token: o.accessToken, expires });\n return o.accessToken;\n } catch {}\n }\n return null;\n}\n\ninterface RemoteSession {\n id?: unknown;\n title?: unknown;\n}\n/** claude.ai's name for the session `bridge` (`session_\u2026`), from the endpoint Claude Code\n * reads a session from: a string, null when it has none yet, undefined when it couldn't\n * be asked or wouldn't say (offline, a 401 or 404, an answer about another session), so\n * the last answer stands. The token goes to api.anthropic.com alone: a redirect is\n * refused, never followed. */\nexport async function claudeAiTitle(bridge: string, token: string): Promise<string | null | undefined> {\n try {\n const r = await fetch(`${CLAUDE_API}/v1/code/sessions/${bridge}`, {\n headers: { Authorization: `Bearer ${token}`, \"anthropic-version\": \"2023-06-01\", \"User-Agent\": \"sessionpipe\" },\n redirect: \"error\",\n signal: AbortSignal.timeout(REMOTE_TIMEOUT_MS),\n });\n if (r.status !== 200 || Number(r.headers.get(\"content-length\")) > REMOTE_MAX_BYTES) return undefined;\n const text = await r.text();\n if (text.length > REMOTE_MAX_BYTES) return undefined;\n // claude.ai answers `{response_shape: {\u2026}}` (conformance/remote/claude-code); Claude Code\n // also reads `session`, and a bare session is taken too. Asked as `session_\u2026`, it names\n // the session `cse_\u2026`: the same one.\n const j = JSON.parse(text) as (RemoteSession & { response_shape?: RemoteSession; session?: RemoteSession }) | null;\n const s = j?.response_shape ?? j?.session ?? j;\n const bare = (x: string) => x.replace(/^(?:session|cse)_/, \"\");\n if (typeof s?.id !== \"string\" || bare(s.id) !== bare(bridge)) return undefined;\n return (typeof s.title === \"string\" && cleanName(s.title)) || null;\n } catch {\n return undefined;\n }\n}\n\n/** For tests: each hook runs in a fresh process; a test that edits the registry mid-run clears the cache. */\nexport const _resetClaudeCaches = (): void => {\n liveCache.clear();\n accountCache.clear();\n tokenCache.clear();\n};\n", "// SPDX-License-Identifier: Apache-2.0\n// Reading the ends of large files, JSON lines, and recent files under a root.\nimport { closeSync, fstatSync, openSync, readdirSync, readSync, statSync } from \"node:fs\";\nimport path from \"node:path\";\n\n/** The first and last bytes of a file, as lines \u2014 a transcript can be tens of MB. */\nexport function ends(file: string, headBytes: number, tailBytes: number): { head: string[]; tail: string[] } {\n let fd: number | undefined;\n try {\n fd = openSync(file, \"r\");\n const size = fstatSync(fd).size;\n const read = (pos: number, len: number) => {\n const b = Buffer.alloc(len);\n const n = readSync(fd as number, b, 0, len, pos);\n return b.subarray(0, n).toString(\"utf8\");\n };\n const head = read(0, Math.min(size, headBytes));\n const tail = size > headBytes ? read(Math.max(0, size - tailBytes), Math.min(size, tailBytes)) : head;\n return { head: head.split(\"\\n\").slice(0, -1), tail: tail.split(\"\\n\").slice(size > headBytes ? 1 : 0) };\n } catch {\n return { head: [], tail: [] };\n } finally {\n if (fd !== undefined)\n try {\n closeSync(fd);\n } catch {}\n }\n}\n\nexport const parseLines = (lines: string[]): Record<string, unknown>[] =>\n lines\n .map((l) => {\n try {\n return JSON.parse(l) as Record<string, unknown>;\n } catch {\n return null;\n }\n })\n .filter((x): x is Record<string, unknown> => !!x);\n\n/** Read line by line up to `cap` bytes, stopping when `visit` returns a value. */\nexport function scanLines<T>(file: string, visit: (line: string) => T | null, cap = 16 * 1024 * 1024): T | null {\n let fd: number | undefined;\n try {\n fd = openSync(file, \"r\");\n const buf = Buffer.alloc(1024 * 1024);\n let pos = 0;\n let rest: Buffer = Buffer.alloc(0);\n while (pos < cap) {\n const n = readSync(fd, buf, 0, buf.length, pos);\n if (!n) break;\n pos += n;\n let chunk = Buffer.concat([rest, buf.subarray(0, n)]);\n let nl = chunk.indexOf(10);\n while (nl >= 0) {\n const r = visit(chunk.subarray(0, nl).toString(\"utf8\"));\n if (r !== null) return r;\n chunk = chunk.subarray(nl + 1);\n nl = chunk.indexOf(10);\n }\n rest = chunk;\n }\n } catch {\n } finally {\n if (fd !== undefined)\n try {\n closeSync(fd);\n } catch {}\n }\n return null;\n}\n\nexport function recentFiles(root: string, depth: number, sinceMs: number, match: (name: string) => boolean): string[] {\n const out: string[] = [];\n const walk = (dir: string, d: number) => {\n let names: import(\"node:fs\").Dirent[] = [];\n try {\n names = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of names) {\n const p = path.join(dir, e.name);\n if (e.isDirectory()) {\n if (d < depth) walk(p, d + 1);\n continue;\n }\n if (!match(e.name)) continue;\n try {\n if (statSync(p).mtimeMs >= sinceMs) out.push(p);\n } catch {}\n }\n };\n walk(root, 0);\n return out;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// The repo and branch of a folder, from git itself; credentials stripped from the\n// remote. Cached per folder for the life of the process (a worker run).\nimport { execFileSync } from \"node:child_process\";\n\nconst cache = new Map<string, { repo?: string; branch?: string }>();\n\nexport function gitFacts(cwd: string | undefined): { repo?: string; branch?: string } {\n if (!cwd) return {};\n const hit = cache.get(cwd);\n if (hit) return hit;\n const run = (args: string[]) => {\n try {\n return execFileSync(\"git\", [\"-C\", cwd, ...args], {\n encoding: \"utf8\",\n timeout: 1500,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n } catch {\n return \"\";\n }\n };\n const out: { repo?: string; branch?: string } = {};\n const repo = run([\"remote\", \"get-url\", \"origin\"]);\n if (repo) out.repo = stripCredentials(repo);\n const branch = run([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]);\n if (branch && branch !== \"HEAD\") out.branch = branch;\n cache.set(cwd, out);\n return out;\n}\n\n/** `https://user:token@host/x` \u2192 `https://host/x`; `git@github.com:a/b.git` \u2192 `github.com/a/b`. */\nexport function stripCredentials(url: string): string {\n // The ssh forms first: the `git@` they are known by is what the credential strip removes.\n const ssh = /^(?:ssh:\\/\\/)?git@([^:/]+)[:/](.+?)(?:\\.git)?$/.exec(url);\n if (ssh) return `${ssh[1]}/${ssh[2]}`;\n return url\n .replace(/\\/\\/[^/@]*@/, \"//\")\n .replace(/^https?:\\/\\//, \"\")\n .replace(/\\.git$/, \"\");\n}\n", "// SPDX-License-Identifier: Apache-2.0\n\n/** A thread another thread started \u2014 a subagent, a review \u2014 names a `source.subagent`\n * in its own metadata (the rollout's first record). A fork the person made carries\n * only `forked_from_id`, and is a session of its own. */\nexport function codexSubagent(meta: Record<string, unknown> | null | undefined): boolean {\n if (meta?.type !== \"session_meta\") return false;\n const p = meta.payload as { source?: { subagent?: unknown } } | undefined;\n return !!p?.source?.subagent;\n}\n\n/** A fork starts with its own metadata, then replays the parent's history. Only\n * an event naming the new thread establishes where its own records begin. */\nexport function codexReplayBoundary(meta: Record<string, unknown> | null | undefined) {\n if (meta?.type !== \"session_meta\") return undefined;\n const p = meta.payload as { id?: unknown; forked_from_id?: unknown; source?: { subagent?: unknown } } | undefined;\n if (!p || (!p.forked_from_id && !p.source?.subagent)) return undefined;\n return (record: Record<string, unknown> | null) =>\n typeof p.id === \"string\" &&\n !!p.id &&\n record?.type === \"event_msg\" &&\n (record.payload as { thread_id?: unknown } | undefined)?.thread_id === p.id;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Transcript readers: complete turns (a person's message + everything the assistant\n// said before the next one), each with the line it ends on, and the line the\n// cursor may advance to once every turn is delivered. Ported from spacesheep-cli\n// lib/memory.js. One reader per harness file format; nothing else is vendor code.\nimport { codexReplayBoundary } from \"../adapters/codex-rollout.js\";\nimport type { TranscriptRead } from \"../adapters/types.js\";\n\ninterface Msg {\n role: \"user\" | \"assistant\";\n text: string;\n at: number;\n line: number;\n /** This ask reached the session from somewhere other than its own keyboard: another\n * local session handed it over, or a channel pushed it, which are the two ways a\n * control daemon reaches a session in place (spec/CONTROL.md \u00A76.3, \u00A78). The words are\n * the person's; the arrival was not. */\n injected?: true;\n}\n\nexport function pairTurns(msgs: Msg[], endLine: number): TranscriptRead {\n const turns: {\n user: string;\n assistant: string;\n at: number;\n startLine: number;\n endLine: number;\n injected?: true;\n }[] = [];\n let cur: (typeof turns)[number] | null = null;\n for (const m of msgs) {\n if (m.role === \"user\") {\n // Never overwrite a prompt that has no reply yet: in an agentic turn the reply\n // comes after tool calls, and a second user record is not a new question.\n if (cur && !cur.assistant) {\n cur.user += `\\n\\n${m.text}`;\n continue;\n }\n if (cur) turns.push(cur);\n cur = {\n user: m.text,\n assistant: \"\",\n at: m.at,\n startLine: m.line,\n endLine: m.line,\n ...(m.injected ? { injected: true as const } : {}),\n };\n } else {\n if (!cur) cur = { user: \"\", assistant: \"\", at: m.at, startLine: m.line, endLine: m.line };\n cur.assistant += (cur.assistant ? \"\\n\\n\" : \"\") + m.text;\n cur.endLine = m.line;\n }\n }\n if (cur?.assistant) turns.push(cur);\n // A user message with no reply yet stays unsent: the cursor stops before it.\n const line = cur && !cur.assistant ? cur.startLine : endLine;\n const out: TranscriptRead = {\n turns: turns.map((t) => ({\n user: t.user,\n assistant: t.assistant,\n at: t.at,\n endLine: t.endLine,\n ...(t.injected ? { injected: true as const } : {}),\n })),\n line,\n };\n if (cur && !cur.assistant && cur.user)\n out.open = { user: cur.user, at: cur.at, ...(cur.injected ? { injected: true as const } : {}) };\n return out;\n}\n\n/** Injected context the person never typed: <tag>\u2026</tag> wrappers (the tag may carry\n * attributes, as Claude Code's `<cross-session-message from=\"\u2026\">` does), HTML comments,\n * slash-command expansions. */\nexport const isInjected = (text: string): boolean =>\n (/^<[a-z_-]+(?:\\s[^>]*)?>/i.test(text) && /<\\/[a-z_-]+>\\s*$/i.test(text)) ||\n /^<!--\\s/.test(text) ||\n /^<command-/.test(text);\n\nconst parse = (raw: string): Record<string, unknown> | null => {\n try {\n return JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return null;\n }\n};\n/** The line after the last whole one. A trailing line that does not parse has no\n * newline yet (a write in progress): the cursor stops before it, or the prompt in\n * it would never be read. */\nconst endOf = (lines: string[]) => {\n const last = lines[lines.length - 1];\n if (last === undefined) return 0;\n return last === \"\" || parse(last) === null ? lines.length - 1 : lines.length;\n};\nconst textParts = (c: unknown, kinds: string[]): string => {\n if (typeof c === \"string\") return c;\n if (!Array.isArray(c)) return \"\";\n return c\n .filter(\n (b) =>\n b &&\n typeof b === \"object\" &&\n kinds.includes((b as { type?: string }).type ?? \"\") &&\n typeof (b as { text?: unknown }).text === \"string\",\n )\n .map((b) => (b as { text: string }).text)\n .join(\"\\n\");\n};\n\n/** An origin whose message is somebody's words rather than the harness talking to\n * itself: `human` is the person at the keyboard, `peer` is a message another local\n * session handed this one \u2014 which is how a control daemon reaches a session in place,\n * so the words in it are the person's too (spec/CONTROL.md \u00A76.3). Older versions wrote\n * no origin at all, which also means the person. */\nconst SPOKEN = new Set([\"human\", \"peer\"]);\n\n/** Claude Code's own preamble around a message from another session: a first line\n * introducing it, and a closing paragraph telling the model what a peer message is and\n * isn't. Neither is anything the person typed. */\nconst PEER_INTRO = /sent a message[^\\n]*:$/;\nconst PEER_OUTRO = \"\\n\\nThis came from another \";\n/** The envelope the sender wraps its text in (cli control/inbox.ts). */\nconst ENVELOPE = /^<cross-session-message\\b[^>]*>\\n([\\s\\S]*)\\n<\\/cross-session-message>$/;\n\n/** What the person actually typed, out of a peer row. Claude Code records the parsed\n * body on the origin when the sender declared its permission class; otherwise the row\n * holds the preamble and the message together, and the preamble is cut off here. A row\n * read without this is a turn whose ask is the harness's own words. */\nexport function peerAsk(origin: unknown, text: string): string {\n const body = (origin as { body?: unknown } | undefined)?.body;\n let t = typeof body === \"string\" && body.trim() ? body : text;\n if (t !== body) {\n const nl = t.indexOf(\"\\n\");\n if (nl > 0 && PEER_INTRO.test(t.slice(0, nl))) t = t.slice(nl + 1);\n const outro = t.indexOf(PEER_OUTRO);\n if (outro > 0) t = t.slice(0, outro);\n }\n return (ENVELOPE.exec(t.trim())?.[1] ?? t).trim();\n}\n\n/** A message sent while a turn was running, absorbed by that turn. Claude Code writes it\n * as an attachment, never as a `user` record: {type:\"attachment\",\n * attachment:{type:\"queued_command\", prompt, commandMode:\"prompt\", origin:{kind:\"human\"}}}.\n * A message from another session is the same record with `origin.kind: \"peer\"` and the\n * text already on its own; a task notification shares it with another `commandMode`.\n * Older versions wrote no origin. */\nconst queuedPrompt = (a: unknown): string => {\n if (!a || typeof a !== \"object\") return \"\";\n const q = a as { type?: unknown; commandMode?: unknown; origin?: { kind?: string }; prompt?: unknown };\n if (q.type !== \"queued_command\" || (q.commandMode ?? \"prompt\") !== \"prompt\") return \"\";\n // Older versions wrote no origin at all, which means the person at the keyboard.\n const kind = q.origin?.kind ?? \"human\";\n if (!SPOKEN.has(kind)) return \"\";\n const text = textParts(q.prompt, [\"text\"]).trim();\n return kind === \"peer\" ? peerAsk(q.origin, text) : text;\n};\n\n/** A channel's message (Claude Code `--channels`: Telegram, sessionpipe's own) as Claude\n * Code records it: {type:\"user\", isMeta:true, origin:{kind:\"channel\", server}, content:\n * `<channel source=\"\u2026\" \u2026>\\n\u2026\\n</channel>`}. The words inside are the person's, sent from\n * elsewhere: the turn's ask. Anything else in that record is nobody's words. */\nconst channelText = (text: string): string => /^<channel\\b[^>]*>([\\s\\S]*)<\\/channel>$/.exec(text)?.[1]?.trim() ?? \"\";\n\n/** Claude Code / Droid: {type:\"user\"|\"assistant\", message:{content}, timestamp, isMeta, isSidechain, origin},\n * plus the person's mid-turn messages (queuedPrompt) and messages from a channel (channelText). */\nexport function readClaude(lines: string[], from: number): TranscriptRead {\n const msgs: Msg[] = [];\n for (let i = from; i < lines.length; i++) {\n const j = lines[i] ? parse(lines[i] as string) : null;\n if (!j || j.isSidechain) continue;\n const origin = j.origin as { kind?: string } | undefined;\n // A message another session handed this one in place is the person's words reaching\n // the session: a turn's ask. Claude Code marks the row `isMeta` because the preamble\n // is the harness's; the ask is not. Two mechanisms deliver one: a channel (#161,\n // `origin.kind === \"channel\"`, the words wrapped in `<channel>`) and the inbox (#150,\n // `origin.kind === \"peer\"`, a framed message `peerAsk` unwraps).\n const channel = j.type === \"user\" && origin?.kind === \"channel\";\n const peer = origin?.kind === \"peer\";\n if (j.type === \"user\" && (j.isCompactSummary || j.repeatsCompactSummary)) continue;\n if (j.type === \"user\" && !channel && !peer && (j.isMeta || (origin?.kind && origin.kind !== \"human\"))) continue;\n const at = Date.parse(String(j.timestamp ?? \"\")) || Date.now();\n const c = (j.message as { content?: unknown } | undefined)?.content;\n if (channel) {\n // Like a peer's: the words are the person's, the arrival was not the keyboard's.\n const text = channelText(textParts(c, [\"text\"]).trim());\n if (text) msgs.push({ role: \"user\", text, at, line: i, injected: true });\n } else if (j.type === \"user\") {\n const raw = textParts(c, [\"text\"]).trim();\n const text = peer ? peerAsk(origin, raw) : raw;\n // The tag test applies to a peer row like any other. `peerAsk` has taken the\n // envelope off already, so what is left is the message itself \u2014 and any local\n // program of this uid can write one, so a body shaped like harness markup must not\n // become a turn whose ask is that markup, published as something the person typed.\n if (!text || isInjected(text)) continue;\n msgs.push({ role: \"user\", text, at, line: i, ...(peer ? { injected: true as const } : {}) });\n } else if (j.type === \"assistant\" && Array.isArray(c)) {\n const text = textParts(c, [\"text\"]).trim();\n if (text) msgs.push({ role: \"assistant\", text, at, line: i });\n } else if (j.type === \"attachment\") {\n const text = queuedPrompt(j.attachment);\n if (text && !isInjected(text)) msgs.push({ role: \"user\", text, at, line: i });\n }\n }\n return pairTurns(msgs, endOf(lines));\n}\n\n/** What Codex writes into a session as a \"user\" message that the person never typed:\n * `<environment_context>` and the like, and the AGENTS.md it read (`# AGENTS.md\n * instructions for <folder>` followed by `<INSTRUCTIONS>`). */\nexport function codexInjected(text: string): boolean {\n return /^<[a-z_-]+>/i.test(text) || /^# AGENTS\\.md instructions\\b/i.test(text);\n}\n\n/** Codex rollout: {type:\"response_item\", payload:{type:\"message\", role, content:[{type:\"input_text\"|\"output_text\", text}]}}. */\nexport function readCodex(lines: string[], from: number): TranscriptRead {\n const msgs: Msg[] = [];\n const boundary = codexReplayBoundary(parse(lines.find((line) => line.trim()) ?? \"\"));\n // Find the boundary from the header even when resuming inside the replay.\n // Keep absolute line numbers: the delivery cursor refers to the original file.\n const start = boundary ? lines.findIndex((line) => boundary(parse(line))) : 0;\n if (start < 0) return pairTurns(msgs, endOf(lines));\n for (let i = Math.max(from, start); i < lines.length; i++) {\n const j = lines[i] ? parse(lines[i] as string) : null;\n if (!j || j.type !== \"response_item\") continue;\n const p = j.payload as { type?: string; role?: string; content?: unknown } | undefined;\n if (!p || p.type !== \"message\") continue;\n const at = Date.parse(String(j.timestamp ?? \"\")) || Date.now();\n const text = textParts(p.content, [\"input_text\", \"output_text\"]).trim();\n if (!text) continue;\n if (p.role === \"user\") {\n if (codexInjected(text)) continue; // Codex's own injections, not the person's words\n msgs.push({ role: \"user\", text, at, line: i });\n } else if (p.role === \"assistant\") msgs.push({ role: \"assistant\", text, at, line: i });\n }\n return pairTurns(msgs, endOf(lines));\n}\n\n/** Gemini CLI chat log: {type:\"user\"|\"gemini\", content|message|text, timestamp}. */\nexport function readGemini(lines: string[], from: number): TranscriptRead {\n const msgs: Msg[] = [];\n for (let i = from; i < lines.length; i++) {\n const j = lines[i] ? parse(lines[i] as string) : null;\n if (!j) continue;\n const role = j.type === \"user\" ? \"user\" : j.type === \"gemini\" || j.type === \"model\" ? \"assistant\" : null;\n if (!role) continue;\n const raw = j.content ?? j.message ?? j.text;\n const text = (typeof raw === \"string\" ? raw : textParts(raw, [\"text\"])).trim();\n if (!text) continue;\n const at = Date.parse(String(j.timestamp ?? \"\")) || Date.now();\n msgs.push({ role, text, at, line: i });\n }\n return pairTurns(msgs, endOf(lines));\n}\n\n/** Antigravity: one step per line, type USER_INPUT | PLANNER_RESPONSE, content with <USER_REQUEST> wrapping. */\nexport function readAntigravity(lines: string[], from: number): TranscriptRead {\n const msgs: Msg[] = [];\n for (let i = from; i < lines.length; i++) {\n const j = lines[i] ? parse(lines[i] as string) : null;\n if (!j || typeof j.content !== \"string\") continue;\n const at = Date.parse(String(j.created_at ?? j.timestamp ?? \"\")) || Date.now();\n if (j.type === \"USER_INPUT\") {\n const m = /<USER_REQUEST>([\\s\\S]*?)(?:<\\/USER_REQUEST>|$)/.exec(j.content);\n const text = (m ? (m[1] as string) : j.content).trim();\n if (text) msgs.push({ role: \"user\", text, at, line: i });\n } else if (j.type === \"PLANNER_RESPONSE\") {\n const text = j.content.trim();\n if (text) msgs.push({ role: \"assistant\", text, at, line: i });\n }\n }\n return pairTurns(msgs, endOf(lines));\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// The hooks file shape Claude Code introduced and Codex, Droid and (with\n// `version: 1`) Cursor and Copilot reuse: {\"hooks\":{\"<Event>\":[{\"matcher\"?,\n// \"hooks\":[{\"type\":\"command\",\"command\",\"timeout\"}]}]}}. Ours is one entry per\n// event; a re-install replaces a stale entry of ours (an old node path) and never\n// touches anyone else's.\n//\n// Two rules from issue #8: a file that is not plain JSON (a comment, a trailing\n// comma, a write in progress) is never written back \u2014 the harness is skipped and\n// the report says why; and a file is written back in its own shape (indent, final\n// newline, a byte-order mark) so uninstall leaves it byte-identical. A file that did not exist before\n// install is reported `created`, and uninstall deletes it once it is empty again.\nimport { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { HookCommand, InstallReport } from \"./types.js\";\n\nexport interface ClaudeShapedOptions {\n /** Which events take a `matcher` (tool and permission events). */\n matcherFor: (event: string) => string | undefined;\n /** Seconds (milliseconds for Gemini CLI). */\n timeout: number;\n /** A longer timeout for one event (Claude Code's PermissionRequest, which may wait\n * for a verified control answer: spec/CONTROL.md \u00A76). */\n timeoutFor?: (event: string) => number | undefined;\n /** Extra top-level keys the file must carry (Cursor/Copilot: version: 1). */\n top?: Record<string, unknown>;\n}\n\n/** A command of ours: a path that contains \"sessionpipe\" and ends in hook.js (or in\n * sessionpipe-hook, the launcher that outlives a node upgrade), then a harness and an event. */\nexport const OURS = /(?:sessionpipe[^\"\\n]*[\\\\/]hook\\.js|[\\\\/]sessionpipe-hook)\"?\\s+[a-z][a-z-]*\\s+[A-Za-z]+\\s*$/;\nexport const isOurs = (h: { hooks?: { command?: string }[] }): boolean =>\n Array.isArray(h?.hooks) && h.hooks.some((x) => typeof x.command === \"string\" && OURS.test(x.command));\n\nexport type JsonFile =\n | {\n ok: true;\n existed: true;\n value: Record<string, unknown>;\n raw: string;\n indent: string;\n eol: string;\n finalNewline: boolean;\n bom: boolean;\n }\n | {\n ok: true;\n existed: false;\n value: Record<string, unknown>;\n raw: \"\";\n indent: \" \";\n eol: \"\\n\";\n finalNewline: true;\n bom: false;\n }\n | { ok: false; existed: true; reason: string };\n\n/** Read a JSON config file with its shape. Never guesses: a parse failure is a\n * refusal to touch the file, not an empty object. */\nexport function readJsonFile(file: string): JsonFile {\n let raw: string;\n try {\n raw = readFileSync(file, \"utf8\");\n } catch {\n return { ok: true, existed: false, value: {}, raw: \"\", indent: \" \", eol: \"\\n\", finalNewline: true, bom: false };\n }\n // A UTF-8 byte-order mark (PowerShell 5's UTF8 encoding, older Windows editors): JSON.parse\n // refuses it, the harnesses read past it. Kept, and written back, like the line ends.\n const bom = raw.charCodeAt(0) === 0xfeff;\n if (bom) raw = raw.slice(1);\n let value: unknown;\n try {\n value = JSON.parse(raw);\n } catch {\n const hint = /^\\s*\\/\\/|\\/\\*/m.test(raw) ? \"not plain JSON (it has comments)\" : \"not plain JSON\";\n return { ok: false, existed: true, reason: hint };\n }\n if (!value || typeof value !== \"object\" || Array.isArray(value))\n return { ok: false, existed: true, reason: \"not a JSON object\" };\n const eol = raw.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const m = /^( +|\\t+)\\S/m.exec(raw);\n const indent = m?.[1] ?? \" \";\n return {\n ok: true,\n existed: true,\n value: value as Record<string, unknown>,\n raw,\n indent,\n eol,\n finalNewline: /\\r?\\n$/.test(raw),\n bom,\n };\n}\n\n/** Serialise in the file's own shape. */\nexport function formatJson(\n value: unknown,\n f: { indent: string; eol: string; finalNewline: boolean; bom?: boolean },\n): string {\n const text = JSON.stringify(value, null, f.indent).replace(/\\n/g, f.eol);\n return (f.bom ? \"\\uFEFF\" : \"\") + (f.finalNewline ? text + f.eol : text);\n}\n\n/** Back-compat helper for callers that only read. */\nexport function readJson(file: string): Record<string, unknown> {\n const f = readJsonFile(file);\n return f.ok ? f.value : {};\n}\n\n/** The report for a config file the system would not let us change, with the error's\n * code, so `install` and `uninstall` go on to the next file instead of ending on this\n * one. Node opens a file for truncation before it writes, so an error from the open\n * (EACCES; EPERM, which Windows gives a read-only file; EROFS) or from making its\n * folder left the file as it was, and so does a delete refused; the note says so. Only\n * an error from the write itself (ENOSPC, EIO) can have left the file cut short, and\n * that note says to check it instead. */\nfunction refused(\n file: string,\n e: unknown,\n what: \"write\" | \"remove\",\n existed: boolean,\n): InstallReport & { note: string } {\n const { code, syscall } = e as NodeJS.ErrnoException;\n const why = `can't ${what} it (${code ?? String(e)})`;\n if (what === \"remove\" || syscall === \"open\" || syscall === \"mkdir\")\n return { file, changed: false, skipped: true, note: `${why}; not touched` };\n return {\n file,\n changed: true,\n skipped: true,\n ...(existed ? {} : { created: true }),\n note: `${why} part way; check the file before the harness reads it`,\n };\n}\n\n/** Write a config file back, its folder made first. Null once written; otherwise the\n * report for the refusal (see `refused`), which the caller returns in place of its own.\n * `existed` is whether the file was there before: a file cut short is still one\n * sessionpipe made, and the config remembers it as `created`. */\nexport function writeBack(file: string, text: string, existed = true): InstallReport | null {\n try {\n mkdirSync(path.dirname(file), { recursive: true });\n writeFileSync(file, text);\n return null;\n } catch (e) {\n return refused(file, e, \"write\", existed);\n }\n}\n\n/** Remove a config file sessionpipe created and uninstall has emptied. A delete needs\n * the folder's leave where a write needs only the file's, so a delete refused (a\n * ~/.claude owned by root, a folder ACL on Windows) writes the file back empty instead:\n * our hooks come out either way, and the note says what stayed. */\nexport function removeEmptied(file: string, empty: string): InstallReport {\n try {\n unlinkSync(file);\n return { file, changed: true, note: \"removed (sessionpipe created it)\" };\n } catch (e) {\n const bad = refused(file, e, \"remove\", true);\n return (\n writeBack(file, empty) ?? {\n file,\n changed: true,\n note: `emptied; ${bad.note.replace(/; not touched$/, \"\")}, so the empty file stays`,\n }\n );\n }\n}\n\nfunction entryFor(event: string, harness: string, cmd: HookCommand, o: ClaudeShapedOptions) {\n const matcher = o.matcherFor(event);\n return {\n ...(matcher !== undefined ? { matcher } : {}),\n hooks: [{ type: \"command\", command: cmd([harness, event]).command, timeout: o.timeoutFor?.(event) ?? o.timeout }],\n };\n}\n\nexport function installClaudeShaped(\n file: string,\n harness: string,\n events: readonly string[],\n cmd: HookCommand,\n o: ClaudeShapedOptions,\n): InstallReport {\n const f = readJsonFile(file);\n if (!f.ok) return { file, changed: false, skipped: true, note: `${f.reason}; not touched` };\n const settings = f.value;\n const hooks = (settings.hooks && typeof settings.hooks === \"object\" ? settings.hooks : {}) as Record<\n string,\n unknown[]\n >;\n let changed = false;\n const want = new Set(events);\n // Events we no longer hook (a lean install) lose their entry of ours.\n for (const ev of Object.keys(hooks)) {\n if (want.has(ev)) continue;\n const list = Array.isArray(hooks[ev]) ? hooks[ev] : [];\n const kept = list.filter((h) => !isOurs(h as never));\n if (kept.length !== list.length) {\n changed = true;\n if (kept.length) hooks[ev] = kept;\n else delete hooks[ev];\n }\n }\n for (const ev of events) {\n const list = Array.isArray(hooks[ev]) ? hooks[ev] : [];\n const entry = entryFor(ev, harness, cmd, o);\n const mine = list.filter((h) => isOurs(h as never));\n if (mine.length === 1 && JSON.stringify(mine[0]) === JSON.stringify(entry)) continue;\n hooks[ev] = list.filter((h) => !isOurs(h as never)).concat([entry]);\n changed = true;\n }\n if (o.top)\n for (const [k, v] of Object.entries(o.top))\n if (settings[k] !== v) {\n settings[k] = v;\n changed = true;\n }\n if (changed) {\n settings.hooks = hooks;\n const bad = writeBack(file, formatJson(settings, f), f.existed);\n if (bad) return bad;\n }\n return { file, changed, created: !f.existed && changed };\n}\n\nexport function uninstallClaudeShaped(\n file: string,\n events: readonly string[],\n opts: { created?: boolean | undefined } = {},\n): InstallReport {\n if (!existsSync(file)) return { file, changed: false };\n const f = readJsonFile(file);\n if (!f.ok) return { file, changed: false, skipped: true, note: `${f.reason}; not touched` };\n const settings = f.value;\n const hooks = settings.hooks as Record<string, unknown[]> | undefined;\n if (!hooks || typeof hooks !== \"object\") return { file, changed: false };\n let changed = false;\n for (const ev of [...events, ...Object.keys(hooks)]) {\n const list = Array.isArray(hooks[ev]) ? hooks[ev] : [];\n const kept = list.filter((h) => !isOurs(h as never));\n if (kept.length !== list.length) {\n changed = true;\n if (kept.length) hooks[ev] = kept;\n else delete hooks[ev];\n }\n }\n if (!changed) return { file, changed: false };\n if (!Object.keys(hooks).length) delete settings.hooks;\n if (opts.created && !Object.keys(settings).length) return removeEmptied(file, formatJson(settings, f));\n return writeBack(file, formatJson(settings, f)) ?? { file, changed: true };\n}\n\nexport function installedClaudeShaped(\n file: string,\n harness: string,\n events: readonly string[],\n cmd: HookCommand,\n o: ClaudeShapedOptions,\n): \"current\" | \"stale\" | \"missing\" {\n const f = readJsonFile(file);\n const hooks = (f.ok ? (f.value.hooks ?? {}) : {}) as Record<string, unknown[]>;\n let present = 0;\n let current = 0;\n for (const ev of events) {\n const list = Array.isArray(hooks[ev]) ? hooks[ev] : [];\n const mine = list.filter((h) => isOurs(h as never));\n if (mine.length) present++;\n if (mine.length === 1 && JSON.stringify(mine[0]) === JSON.stringify(entryFor(ev, harness, cmd, o))) current++;\n }\n // An entry of ours on an event outside the wanted set (a lean install after a full one) is stale.\n for (const ev of Object.keys(hooks))\n if (!events.includes(ev) && (Array.isArray(hooks[ev]) ? hooks[ev] : []).some((h) => isOurs(h as never)))\n return \"stale\";\n if (!present) return \"missing\";\n return current === events.length ? \"current\" : \"stale\";\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// One headless agent turn as a child process (spec/CONTROL.md \u00A76): the spawner the\n// control daemon runs every harness through, and the shape each harness's reading of\n// its own output takes. Which binary, which flags and how the answer is read are the\n// harness's own (`<harness>-control.ts`); what is the same for all of them is here.\nimport { spawn } from \"node:child_process\";\nimport { accessSync, constants, readFileSync, statSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { withoutLineage } from \"../launched-by.js\";\n\n/** A headless turn still running after this is stopped. */\nexport const JOB_TIMEOUT_MS = 30 * 60_000;\nconst OUT_CAP = 1024 * 1024;\n\n/** How a turn the daemon runs may act (spec/CONTROL.md \u00A72), chosen at the machine's own\n * terminal: safe, the harness's auto mode, or inherit (no permission flag, so the\n * harness starts in its own default on this machine). */\nexport type Mode = \"safe\" | \"auto\" | \"inherit\";\n\nexport function isExe(p: string): boolean {\n try {\n if (!statSync(p).isFile()) return false;\n accessSync(p, constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/** A harness's CLI: the override the person set (`SESSIONPIPE_<NAME>`), else the first\n * of `names` on PATH, else the first of `dirs` that holds one. A launchd or systemd\n * service starts with a short PATH, so the installers' own folders are looked in too. */\nexport function findBinary(o: {\n override?: string | undefined;\n names: string[];\n dirs: string[];\n env?: NodeJS.ProcessEnv;\n}): string | null {\n if (o.override && isExe(o.override)) return o.override;\n for (const d of String(o.env?.PATH || \"\").split(path.delimiter)) {\n if (!d || !path.isAbsolute(d)) continue;\n for (const n of o.names) if (isExe(path.join(d, n))) return path.join(d, n);\n }\n for (const d of o.dirs) for (const n of o.names) if (isExe(path.join(d, n))) return path.join(d, n);\n return null;\n}\n\n/** The script an npm-style `.cmd` shim runs: the one `\"%dp0%\\\\\u2026\"` or `\"%~dp0\\\\\u2026\"` word\n * before `%*` (npm's cmd-shim, old and new; pnpm's is the same shape), beside the shim.\n * Null when the file is no such shim, or names nothing that is there. */\nexport function shimTarget(shim: string): string | null {\n let text = \"\";\n try {\n text = readFileSync(shim, \"utf8\");\n } catch {\n return null;\n }\n const m = /\"%(?:~dp0|dp0%)\\\\([^\"]+)\"\\s+%\\*/.exec(text);\n if (!m) return null;\n const target = path.resolve(path.dirname(shim), (m[1] as string).split(\"\\\\\").join(path.sep));\n try {\n return statSync(target).isFile() ? target : null;\n } catch {\n return null;\n }\n}\n\n/** `bin` and `args` as spawn takes them with no shell. Node refuses to spawn a `.cmd` or\n * `.bat` without one (CVE-2024-27980, since 20.12), and on Windows an `npm install -g`\n * leaves exactly such a shim (`claude.cmd`, `codex.cmd`); a shell would hand a prompt's\n * every character to cmd.exe, so the shim's own script runs under this node instead.\n * Null for a launcher script that names no script to run (issue #165). */\nexport function spawnable(bin: string, args: string[]): { file: string; args: string[] } | null {\n if (!/\\.(cmd|bat)$/i.test(bin)) return { file: bin, args };\n const target = shimTarget(bin);\n return target ? { file: process.execPath, args: [target, ...args] } : null;\n}\n\n/** Why a launcher script can't be run, for the person. */\nexport const shimNote = (bin: string) =>\n `${path.basename(bin)} is a launcher script that names no program to run; point the harness's override (SESSIONPIPE_CLAUDE, SESSIONPIPE_CODEX, \u2026) at the program itself`;\n\n/** As `spawnable`, for a probe whose arguments are fixed words (`--help`, `auth status`):\n * a launcher script that can't be read runs through a shell, as one quoted command\n * (arguments beside `shell: true` are deprecated from node 24, DEP0190). */\nexport function probe(bin: string, args: string[]): { file: string; args: string[]; shell: boolean } {\n const s = spawnable(bin, args);\n return s ? { ...s, shell: false } : { file: [`\"${bin}\"`, ...args].join(\" \"), args: [], shell: true };\n}\n\n/** The env every headless turn runs with: SESSIONPIPE_CONTROL_JOB tells the turn's own\n * hooks that the daemon runs it, so its Stop hook never takes a message meant for a\n * person's session (cli/src/hook.ts); and none of the lineage this process inherited\n * (a daemon started from inside an asked session), so a child carries only what its\n * caller adds after this (CONTROL.md \u00A713). */\nexport function jobEnv(env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {\n return { ...withoutLineage(env), SESSIONPIPE_CONTROL_JOB: \"1\" };\n}\n\n/** Whether a harness is signed in, in its own words. `loggedIn: null` means it can't\n * say (too old, or it didn't answer): never a reason to refuse. */\nexport interface Login {\n loggedIn: boolean | null;\n /** claude.ai, an API key, a long-lived token\u2026 as the harness names it. */\n method?: string;\n subscription?: string;\n}\n\nexport interface RunResult {\n error?: string;\n code?: number | null;\n signal?: NodeJS.Signals | null;\n stdout?: string | null;\n tooBig?: boolean;\n stderr?: string;\n timedOut?: boolean;\n}\n\nexport interface RunOptions {\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeoutMs?: number;\n /** Streaming: each stdout line as it arrives. Only the lines `keep` names are kept\n * for the RunResult (none by default), so a long stream never hits the cap. */\n onLine?: (line: string) => void;\n keep?: (line: string) => boolean;\n}\n\n/** Run one headless turn in its own process group, stdin closed (Codex waits on an open\n * one), holding at most 1 MB of stdout. */\nexport function runHeadless(bin: string, args: string[], o: RunOptions): Promise<RunResult> {\n return new Promise((resolve) => {\n let out: Buffer[] = [];\n let outLen = 0;\n let tooBig = false;\n let errTail = \"\";\n let timedOut = false;\n let settled = false;\n let child: import(\"node:child_process\").ChildProcess;\n const c = spawnable(bin, args);\n if (!c) {\n resolve({ error: shimNote(bin) });\n return;\n }\n try {\n child = spawn(c.file, c.args, {\n cwd: o.cwd,\n env: o.env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n detached: process.platform !== \"win32\",\n windowsHide: true,\n });\n } catch (e) {\n resolve({ error: (e as Error).message });\n return;\n }\n const kill = (sig: NodeJS.Signals) => {\n try {\n if (process.platform !== \"win32\" && child.pid) process.kill(-child.pid, sig);\n else child.kill(sig);\n } catch {\n try {\n child.kill(sig);\n } catch {}\n }\n };\n const keep = (line: string) => {\n if (!o.keep?.(line)) return;\n out = [Buffer.from(line)];\n outLen = line.length;\n };\n let partial = \"\";\n child.stdout?.on(\"data\", (b: Buffer) => {\n if (o.onLine) {\n const chunk = partial + b.toString(\"utf8\");\n const lines = chunk.split(\"\\n\");\n partial = lines.pop() as string;\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n o.onLine(line);\n } catch {}\n keep(line);\n }\n if (partial.length > OUT_CAP) partial = \"\";\n return;\n }\n if (tooBig) return;\n if (outLen + b.length > OUT_CAP) {\n tooBig = true;\n out = [];\n return;\n }\n out.push(b);\n outLen += b.length;\n });\n child.stderr?.on(\"data\", (b: Buffer) => {\n errTail = (errTail + b.toString(\"utf8\")).slice(-4000);\n });\n const timer = setTimeout(() => {\n timedOut = true;\n kill(\"SIGTERM\");\n setTimeout(() => kill(\"SIGKILL\"), 10_000).unref();\n }, o.timeoutMs ?? JOB_TIMEOUT_MS);\n const result = (code: number | null, signal: NodeJS.Signals | null): RunResult => ({\n code,\n signal,\n stdout: tooBig ? null : Buffer.concat(out).toString(\"utf8\"),\n tooBig,\n stderr: errTail,\n timedOut,\n });\n const finish = (r: RunResult) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(r);\n };\n child.on(\"error\", (e) => finish({ error: e.message }));\n // A tool the agent left running can hold stdout open after the harness exits.\n child.on(\"exit\", (code, signal) => setTimeout(() => finish(result(code, signal)), 2000).unref());\n child.on(\"close\", (code, signal) => {\n // The last line may end without a newline: it is often the result itself.\n if (o.onLine && partial.trim()) {\n try {\n o.onLine(partial);\n } catch {}\n keep(partial);\n partial = \"\";\n }\n finish(result(code, signal));\n });\n });\n}\n\n/** A harness's output read as it streams: the session it names, the answer so far, and\n * the outcome once the process is gone. One per run. */\nexport interface Reading {\n /** One stdout line. True when the answer so far changed. */\n feed(line: string): boolean;\n /** The session id the harness revealed (it picks its own), or null so far. */\n readonly session: string | null;\n /** The answer so far, whole. */\n readonly text: string;\n /** How many lines were fed. */\n readonly lines: number;\n outcome(res: RunResult): { failed: string } | { reply: string };\n}\n\n/** The last few lines a harness wrote to stderr, on one line. */\nexport function stderrTail(stderr: string | undefined, n = 3): string {\n return String(stderr || \"\")\n .trim()\n .split(\"\\n\")\n .slice(-n)\n .join(\" \")\n .trim();\n}\n\n/** What a run that wasn't there to ask says about the actions it was refused. */\nexport function deniedNote(names: string[], count: number): string {\n if (!count) return \"\";\n return `\\n\\n(Not allowed on this machine, since nobody was there to approve: ${names.join(\", \") || `${count} action(s)`}.)`;\n}\n\n/** A process that failed before or without its answer, in words. */\nexport function runFailure(label: string, res: RunResult): string | null {\n if (res.error) return `couldn't start ${label}: ${res.error}`;\n if (res.timedOut) return `${label} was still working after 30 minutes, so it was stopped`;\n return null;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// The two session fields every harness shares and no hook payload carries: who started\n// this session on purpose, and which session it answers. Both come from the environment\n// the session was started in, and the hook carries them along (PROTOCOL.md \u00A73).\nimport type { Adapter, HookInput, HookResult } from \"./adapters/types.js\";\n\n/** PROTOCOL.md \u00A73 `launched_by`: a launcher that starts a session for another one (a\n * script running `claude -p`) names itself here as `<harness>:<id>`, and the hook\n * carries it along. */\nexport const LAUNCHED_BY_ENV = \"SESSIONPIPE_LAUNCHED_BY\";\n/** PROTOCOL.md \u00A73 `delegated_by`: the control daemon that starts or resumes a session\n * for another session's ask (CONTROL.md \u00A713) or a handoff (\u00A76) names that session\n * here, and the child's hooks stamp it on every event. */\nexport const DELEGATED_BY_ENV = \"SESSIONPIPE_ASKED_BY\";\n/** CONTROL.md \u00A713: the asks a session is nested in, comma-joined, read by the next ask. */\nexport const ASK_CHAIN_ENV = \"SESSIONPIPE_ASK_CHAIN\";\n/** The three lineage variables. They are inherited like any environment, so anything\n * that starts a further session resets them (PROTOCOL.md \u00A73, CONTROL.md \u00A713); the\n * daemon most of all, which starts sessions for receivers and askers alike and must\n * not stamp its own shell's lineage on them. */\nexport const LINEAGE_ENV = [LAUNCHED_BY_ENV, DELEGATED_BY_ENV, ASK_CHAIN_ENV] as const;\nconst SESSION_REF = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;\n\n/** `env` without the lineage variables: what a child that is nobody's gets. */\nexport function withoutLineage(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const e = { ...env };\n for (const k of LINEAGE_ENV) delete e[k];\n return e;\n}\n\n/** The `<harness>:<id>` a variable names, or nothing when it is absent or malformed. */\nfunction refIn(env: Record<string, string | undefined>, name: string): string | undefined {\n const v = env[name]?.trim();\n return v && SESSION_REF.test(v) ? v : undefined;\n}\n\n/** The launcher the environment names, or nothing when it is absent or malformed. */\nexport function launchedBy(env: Record<string, string | undefined>): string | undefined {\n return refIn(env, LAUNCHED_BY_ENV);\n}\n\n/** The session this one was started or resumed for, or nothing when the environment\n * doesn't say or says it badly. */\nexport function delegatedBy(env: Record<string, string | undefined>): string | undefined {\n return refIn(env, DELEGATED_BY_ENV);\n}\n\n/** An adapter's reading of a hook, plus what every harness shares: the launcher and the\n * asker, unless the harness gave the session a parent of its own (a subagent's lineage\n * is its parent, which carries both) or the value names the session itself (a\n * launcher that forgot to reset it). */\nexport function readHook(adapter: Adapter, input: HookInput): HookResult | null {\n const r = adapter.fromHook(input);\n if (r && !r.session.parent_id) {\n const self = `${adapter.name}:${r.session.id}`;\n const launcher = launchedBy(input.env);\n if (launcher && launcher !== self) r.session.launched_by = launcher;\n const asker = delegatedBy(input.env);\n if (asker && asker !== self) r.session.delegated_by = asker;\n }\n return r;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Antigravity (Google) \u2014 docs: https://antigravity.google/docs/hooks\n// Config: ~/.gemini/config/hooks.json \u2014 top-level keys are hook NAMES, ours is\n// \"sessionpipe\". stdin is camelCase with NO event name (it rides in argv). A hook's\n// stdout is its answer; non-JSON is a deny. Timeouts in seconds. No session end\n// exists. Ported from spacesheep-cli lib/sessions.js (facts, remote url, projects).\n//\n// No PreToolUse. Its answer must carry a `decision` (allow / deny / ask / force_ask),\n// none of which means \"carry on\": `{}` is read as a deny with no reason, which blocked\n// every tool call on every machine 0.1\u20130.6.0 was installed on (2026-10-04, Antigravity\n// 2.19.1), and `allow` would wave calls past the person's own review settings. So the\n// tool events come from PostToolUse alone, which carries no tool name (only `stepIdx`\n// and `error`): it is a \"still working\" beat.\nimport { existsSync, readdirSync, readFileSync, realpathSync, statSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { homeDir } from \"../paths.js\";\nimport { ends, parseLines } from \"../readers/files.js\";\nimport { readAntigravity } from \"../readers/transcripts.js\";\nimport { formatJson, readJsonFile, removeEmptied, writeBack } from \"./claude-shaped.js\";\nimport type {\n Adapter,\n BackfillRow,\n HookCommand,\n HookInput,\n HookResult,\n InstallOptions,\n InstallReport,\n SessionFacts,\n} from \"./types.js\";\n\nconst NAME = \"antigravity\";\nconst HOOK_NAME = \"sessionpipe\";\nexport const AG_EVENTS = [\"PreInvocation\", \"PostInvocation\", \"PostToolUse\", \"Stop\"] as const;\n/** What a PreToolUse entry an older install left behind gets, until a restarted\n * conversation reads the rewritten hooks.json: the person's own prompt, with why. The\n * hook (cli/src/hook.ts) and the launcher (cli/src/launcher.ts) print the same line. */\nexport const AG_STALE_PRETOOL =\n '{\"decision\":\"ask\",\"reason\":\"sessionpipe: an outdated PreToolUse hook is still loaded. Restart this conversation (or run sessionpipe install) to drop it.\"}\\n';\n/** Antigravity's answer for one event: `{}` is \"carry on\" for every event we register. */\nexport const antigravityAnswer = (event: string): string => (event === \"PreToolUse\" ? AG_STALE_PRETOOL : \"{}\\n\");\nconst gemini = (env: NodeJS.ProcessEnv) => env.GEMINI_CLI_HOME || path.join(homeDir(), \".gemini\");\nconst hooksFile = (env: NodeJS.ProcessEnv) => path.join(gemini(env), \"config\", \"hooks.json\");\nconst projectsDir = (env: NodeJS.ProcessEnv) => path.join(gemini(env), \"config\", \"projects\");\nconst dataDirs = (env: NodeJS.ProcessEnv) =>\n [\"antigravity\", \"antigravity-cli\", \"antigravity-ide\"].map((d) => path.join(gemini(env), d));\nconst ID_RE = /^[A-Za-z0-9_-]{1,128}$/;\nconst validTool = (n: unknown): n is string => typeof n === \"string\" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);\n\n// One tool event already, so a lean install is the same entry.\nfunction entry(cmd: HookCommand) {\n const handler = (e: string) => ({ type: \"command\", command: cmd([NAME, e]).command, timeout: 5 });\n const out: Record<string, unknown> = { enabled: true };\n for (const e of AG_EVENTS) out[e] = /Tool/.test(e) ? [{ matcher: \"*\", hooks: [handler(e)] }] : [handler(e)];\n return out;\n}\n\nexport const antigravity: Adapter = {\n name: NAME,\n events: AG_EVENTS,\n detect: (env = process.env) =>\n existsSync(path.join(gemini(env), \"config\")) || dataDirs(env).some((d) => existsSync(d)),\n configFiles: (env = process.env) => [hooksFile(env)],\n install(cmd, env = process.env): InstallReport[] {\n const file = hooksFile(env);\n const f = readJsonFile(file);\n if (!f.ok) return [{ file, changed: false, skipped: true, note: `${f.reason}; not touched` }];\n const hooks = f.value;\n const e = entry(cmd);\n if (JSON.stringify(hooks[HOOK_NAME]) === JSON.stringify(e)) return [{ file, changed: false }];\n hooks[HOOK_NAME] = e;\n return [\n writeBack(file, formatJson(hooks, f), f.existed) ?? {\n file,\n changed: true,\n created: !f.existed,\n note: \"hooks.json is read when a conversation starts; open ones report after a restart\",\n },\n ];\n },\n uninstall(env = process.env, opts: InstallOptions = {}): InstallReport[] {\n const file = hooksFile(env);\n if (!existsSync(file)) return [{ file, changed: false }];\n const f = readJsonFile(file);\n if (!f.ok) return [{ file, changed: false, skipped: true, note: `${f.reason}; not touched` }];\n const hooks = f.value;\n if (!(HOOK_NAME in hooks)) return [{ file, changed: false }];\n delete hooks[HOOK_NAME];\n if (opts.created?.includes(file) && !Object.keys(hooks).length) return [removeEmptied(file, formatJson(hooks, f))];\n return [writeBack(file, formatJson(hooks, f)) ?? { file, changed: true }];\n },\n installed(cmd, env = process.env) {\n const file = hooksFile(env);\n const f = readJsonFile(file);\n const have = f.ok ? f.value[HOOK_NAME] : undefined;\n return [\n {\n file,\n state: !have ? \"missing\" : JSON.stringify(have) === JSON.stringify(entry(cmd)) ? \"current\" : \"stale\",\n },\n ];\n },\n\n fromHook(input: HookInput): HookResult | null {\n const event = input.argv[1] ?? \"\";\n let s: Record<string, unknown> = {};\n try {\n // A stdin that is JSON but not an object (`null`, a number) is no payload.\n const j: unknown = input.stdin ? JSON.parse(input.stdin) : {};\n if (j && typeof j === \"object\" && !Array.isArray(j)) s = j as Record<string, unknown>;\n } catch {}\n const id = (s.conversationId ?? s.session_id ?? s.sessionId) as string | undefined;\n const stdout = antigravityAnswer(event);\n if (!id) return { session: { id: \"\" }, events: [], stdout };\n const workspaces = agWorkspaces(s.workspacePaths);\n const session: HookResult[\"session\"] = { id: String(id), cwd: workspaces[0] ?? input.cwd };\n if (typeof s.modelName === \"string\" && s.modelName !== \"auto\") session.model = s.modelName;\n const toolCall = s.toolCall as { name?: unknown; args?: unknown } | undefined;\n const tool = validTool(toolCall?.name) ? toolCall?.name : validTool(s.toolName) ? s.toolName : undefined;\n const ev = (type: string, data: Record<string, unknown>) => ({ type, data: prune(data), harnessEvent: event });\n const events: HookResult[\"events\"] = [];\n switch (event) {\n case \"PreInvocation\":\n // Fires before every model call of an execution; only the first is the person's ask.\n if (Number(s.invocationNum) === 0 || s.invocationNum === undefined) {\n events.push(ev(\"session.started\", { source: \"unknown\" }));\n events.push(ev(\"turn.started\", {}));\n } else events.push(ev(\"session.heartbeat\", {}));\n break;\n case \"PostInvocation\":\n break;\n case \"PreToolUse\":\n if (tool) events.push(ev(\"tool.started\", { tool, input: toolCall?.args }));\n break;\n case \"PostToolUse\":\n // Antigravity names no tool here; a beat keeps a long run of tools \"working\".\n if (!tool) events.push(ev(\"session.heartbeat\", {}));\n else\n events.push(\n ev(\"tool.ended\", {\n tool,\n ok: !s.error,\n error: typeof s.error === \"string\" ? s.error.slice(0, 2000) : undefined,\n input: toolCall?.args,\n }),\n );\n break;\n case \"Stop\":\n events.push(ev(\"turn.ended\", { reason: s.error ? \"error\" : \"stop\" }));\n break;\n default:\n return { session, events: [], stdout };\n }\n const r: HookResult = { session, events, stdout, hints: { workspaces } };\n if (typeof s.transcriptPath === \"string\") r.transcript = s.transcriptPath;\n return r;\n },\n\n readTranscript(file, fromLine) {\n let text = \"\";\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return { turns: [], line: fromLine };\n }\n return readAntigravity(text.split(\"\\n\"), fromLine);\n },\n\n facts(session, transcript, hints): SessionFacts {\n if (!transcript) return {};\n return antigravityFacts(transcript, {\n conversation: session.id,\n workspaces: (hints.workspaces as string[] | undefined) ?? (session.cwd ? [session.cwd] : []),\n env: process.env,\n });\n },\n\n backfill(sinceMs, env = process.env): BackfillRow[] {\n const rows: BackfillRow[] = [];\n for (const data of dataDirs(env)) {\n let ids: string[] = [];\n try {\n ids = readdirSync(path.join(data, \"brain\")).filter((n) => /^[0-9a-f-]{36}$/i.test(n));\n } catch {}\n for (const id of ids) {\n const f = path.join(data, \"brain\", id, \".system_generated\", \"logs\", \"transcript.jsonl\");\n try {\n if (statSync(f).mtimeMs < sinceMs) continue;\n } catch {\n continue;\n }\n const facts = antigravityFacts(f, { conversation: id, workspaces: [], env });\n if (!facts.started_at || !facts.last_at) continue;\n const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;\n rows.push({ session: { id, ...rest }, started_at, last_at });\n }\n }\n return rows;\n },\n};\n\nfunction prune<T extends Record<string, unknown>>(o: T): T {\n for (const k of Object.keys(o)) if (o[k] === undefined) delete o[k];\n return o;\n}\n\n/** A folder as Antigravity writes it (a file:// URI or a path) \u2192 an absolute path, or null. */\nexport function agPath(p: unknown): string | null {\n if (typeof p !== \"string\" || !p) return null;\n let s = p;\n if (/^file:/i.test(s)) {\n try {\n s = decodeURIComponent(new URL(s).pathname);\n } catch {\n return null;\n }\n if (process.platform === \"win32\" && /^\\/[A-Za-z]:/.test(s)) s = s.slice(1);\n } else if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(s)) return null;\n if (!path.isAbsolute(s)) return null;\n return path.resolve(s);\n}\nexport const agWorkspaces = (paths: unknown): string[] =>\n (Array.isArray(paths) ? paths : [])\n .map(agPath)\n .filter((p): p is string => !!p)\n .filter((p, i, a) => a.indexOf(p) === i);\n\nfunction agVariants(p: string): string[] {\n const out = [p];\n try {\n const r = realpathSync.native(p);\n if (r !== p) out.push(r);\n } catch {}\n return out;\n}\nfunction agProjects(dir: string): { id: string; folders: string[] }[] {\n const out: { id: string; folders: string[] }[] = [];\n let names: string[] = [];\n try {\n names = readdirSync(dir).filter((n) => n.endsWith(\".json\"));\n } catch {\n return out;\n }\n for (const n of names) {\n const id = n.slice(0, -5);\n if (!ID_RE.test(id)) continue;\n let j: { projectResources?: { resources?: { folderUri?: unknown; gitFolder?: { folderUri?: unknown } }[] } };\n try {\n j = JSON.parse(readFileSync(path.join(dir, n), \"utf8\"));\n } catch {\n continue;\n }\n const folders: string[] = [];\n // A resource is one of the kinds in Antigravity's project.proto: a plain folder is\n // `{ folderUri }`, a git checkout `{ gitFolder: { folderUri, defaultBranch, allowWrite } }`\n // (what 2.23.1 writes for a folder under git); google3 and remote resources name no folder.\n for (const r of j?.projectResources?.resources ?? []) {\n const f = agPath(r?.folderUri ?? r?.gitFolder?.folderUri);\n if (f) folders.push(...agVariants(f));\n }\n if (folders.length) out.push({ id, folders });\n }\n return out;\n}\n/** The project whose folder is the workspace or contains it; the deepest wins. */\nexport function agMatchProject(projects: { id: string; folders: string[] }[], workspaces: string[]): string | null {\n const ws: string[] = [];\n for (const w of workspaces) {\n const p = agPath(w);\n if (p) ws.push(...agVariants(p));\n }\n let best: string | null = null;\n let bestLen = -1;\n for (const pr of projects)\n for (const f of pr.folders) {\n const within = ws.some((w) => w === f || w.startsWith(f.endsWith(path.sep) ? f : f + path.sep));\n if (within && f.length > bestLen) {\n best = pr.id;\n bestLen = f.length;\n }\n }\n return best;\n}\n\n/** Antigravity's web remote: https://antigravity.google.com/r/<installation uuid>-v2, with\n * ?p=c/<conversation>?section=<project> when the project is known. */\nexport function antigravityRemoteUrl(\n transcript: string,\n opts: { conversation?: string; workspaces?: string[]; env: NodeJS.ProcessEnv; projectsDir?: string },\n): string | null {\n const i = transcript.indexOf(`${path.sep}brain${path.sep}`);\n const dirs = (i > 0 ? [transcript.slice(0, i)] : []).concat(dataDirs(opts.env));\n for (const d of dirs) {\n try {\n const m = /installation_uuid:\\s*\"([0-9a-f-]{36})\"/i.exec(\n readFileSync(path.join(d, \"antigravity_state.pbtxt\"), \"utf8\"),\n );\n if (!m) continue;\n const base = `https://antigravity.google.com/r/${m[1]}-v2`;\n const conv = opts.conversation ?? \"\";\n if (!ID_RE.test(conv) || !opts.workspaces?.length) return base;\n const project = agMatchProject(agProjects(opts.projectsDir ?? projectsDir(opts.env)), opts.workspaces);\n return project ? `${base}?p=${encodeURIComponent(`c/${conv}?section=${project}`)}` : base;\n } catch {}\n }\n return null;\n}\n\nexport function antigravityFacts(\n file: string,\n opts: { conversation: string; workspaces: string[]; env: NodeJS.ProcessEnv },\n): SessionFacts {\n const { head, tail } = ends(file, 128 * 1024, 64 * 1024);\n const H = parseLines(head);\n const T = parseLines(tail);\n const out: SessionFacts = {};\n const url = antigravityRemoteUrl(file, opts);\n if (url) out.url = url;\n for (const j of H) {\n if (j.type !== \"USER_INPUT\" || typeof j.content !== \"string\") continue;\n const m = /<USER_REQUEST>([\\s\\S]*?)(?:<\\/USER_REQUEST>|$)/.exec(j.content);\n const text = (m ? (m[1] as string) : j.content).trim();\n const line = (text.split(\"\\n\").find((l) => l.trim()) ?? \"\").trim();\n if (!line || /^<[a-z_-]+>/i.test(line)) continue;\n out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\\s+\\S*$/, \"\")}\u2026` : line;\n out.title_source = \"first-ask\";\n break;\n }\n const times = H.concat(T)\n .map((j) => Date.parse(String(j.created_at ?? j.timestamp ?? \"\")))\n .filter((n) => n > 0);\n if (times.length) {\n out.started_at = Math.min(...times);\n out.last_at = Math.max(...times);\n }\n return out;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Claude Code (Anthropic) \u2014 control delivery (spec/CONTROL.md \u00A76). Claude Code on\n// this machine, as the control daemon needs it: which `claude` to\n// run and with which permission flags, where a session's transcript is and which\n// folder it runs in, whether a live process holds it, and running one headless\n// turn (`claude -p --resume`, `--fork-session`, `--session-id`). Ported from the\n// spacesheep CLI's machine listener (lib/machine.js, 1.22), which ran it in\n// production first.\nimport { execFile, execFileSync } from \"node:child_process\";\nimport { closeSync, openSync, readdirSync, readFileSync, readSync, statSync } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { levelRank, type RunLevel, START_PERMISSIONS, type StartPermission } from \"../control/verify.js\";\nimport { withoutLineage } from \"../launched-by.js\";\nimport {\n isExe,\n JOB_TIMEOUT_MS,\n type Login,\n type Mode,\n probe,\n type RunOptions,\n type RunResult,\n runHeadless,\n} from \"./headless.js\";\n\nexport { JOB_TIMEOUT_MS, type Login, type Mode, type RunResult };\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n/** A transcript someone else wrote this recently is in use. */\nconst LIVE_WRITE_MS = 90_000;\n\n/** Dotted versions by number, a missing part as 0: 1.9.0 < 1.10 = 1.10.0. */\nexport const cmpVersion = (a: string, b: string) => {\n const pa = a.split(\".\").map(Number);\n const pb = b.split(\".\").map(Number);\n for (let i = 0; i < Math.max(pa.length, pb.length); i++)\n if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);\n return 0;\n};\n\n/** The `claude` to run: on PATH, else where the installers put it, else the newest\n * copy the desktop app ships. Null when this machine has none. */\nexport function findClaude(env: NodeJS.ProcessEnv = process.env, home = os.homedir()): string | null {\n if (env.SESSIONPIPE_CLAUDE && isExe(env.SESSIONPIPE_CLAUDE)) return env.SESSIONPIPE_CLAUDE;\n const names = process.platform === \"win32\" ? [\"claude.exe\", \"claude.cmd\"] : [\"claude\"];\n for (const d of String(env.PATH || \"\").split(path.delimiter)) {\n if (!d || !path.isAbsolute(d)) continue;\n for (const n of names) if (isExe(path.join(d, n))) return path.join(d, n);\n }\n for (const p of [\n path.join(home, \".claude\", \"local\", \"claude\"),\n path.join(home, \".local\", \"bin\", \"claude\"),\n \"/opt/homebrew/bin/claude\",\n \"/usr/local/bin/claude\",\n ])\n if (isExe(p)) return p;\n const app = path.join(home, \"Library\", \"Application Support\", \"Claude\", \"claude-code\");\n let versions: string[] = [];\n try {\n versions = readdirSync(app)\n .filter((n) => /^\\d+(\\.\\d+)*$/.test(n))\n .sort(cmpVersion)\n .reverse();\n } catch {}\n for (const v of versions) {\n const p = path.join(app, v, \"claude.app\", \"Contents\", \"MacOS\", \"claude\");\n if (isExe(p)) return p;\n }\n return null;\n}\n\n/** One option's text in `claude --help`: its line and the deeper-indented lines under it. */\nfunction optionBlock(help: string, flag: string): string | null {\n const lines = help.split(\"\\n\");\n const re = new RegExp(`^\\\\s{0,4}(?:-\\\\w, )?${flag.replace(/-/g, \"\\\\-\")}(?![\\\\w-])`);\n const start = lines.findIndex((l) => re.test(l));\n if (start < 0) return null;\n const out = [lines[start] as string];\n for (let i = start + 1; i < lines.length && lines[i]?.trim() && !/^\\s{0,4}-/.test(lines[i] as string); i++)\n out.push(lines[i] as string);\n return out.join(\" \");\n}\n\nexport interface Caps {\n hasMode: boolean;\n modes: string[];\n promptsNone: boolean;\n fork: boolean;\n /** `--output-format stream-json` with `--include-partial-messages`: the answer can be\n * forwarded as it is written. Optional so older callers' literals still type. */\n stream?: boolean;\n /** Why `claude --help` couldn't be read, when it couldn't: it timed out (a machine still\n * starting up), or failed without listing `--permission-mode`. Such caps say nothing\n * about this Claude Code's modes, so they are never read as \"it has none\". */\n unread?: string;\n}\n\nexport function parseCaps(help: string): Caps {\n const pm = optionBlock(help, \"--permission-mode\");\n const pp = optionBlock(help, \"--permission-prompts\");\n return {\n hasMode: !!pm,\n modes: pm ? [...pm.matchAll(/\"([A-Za-z]+)\"/g)].map((m) => m[1] as string) : [],\n promptsNone: !!pp && /\"none\"/.test(pp),\n fork: !!optionBlock(help, \"--fork-session\"),\n stream: !!optionBlock(help, \"--include-partial-messages\"),\n };\n}\n\n/** The `--permission-mode` each level (CONTROL.md \u00A73.1) is, where it is one mode. */\nconst LEVEL_MODE: Record<Exclude<StartPermission, \"safe\">, string> = {\n read: \"plan\",\n edits: \"acceptEdits\",\n auto: \"auto\",\n};\n\n/** Permission flags for a level (a machine's mode is one: safe, auto, inherit). Read:\n * plan mode. Safe: `dontAsk` (nothing that needs approval runs). Edits: acceptEdits.\n * Auto: Claude Code's own auto mode. Inherit: none, so the turn starts in the mode\n * Claude Code picks by itself here, its settings' `permissions.defaultMode` (a resume\n * too: Claude Code never restores a session's earlier mode). A level this Claude Code\n * has no mode for is a refusal, never another level, and so is every level but inherit\n * while its --help can't be read (safe would otherwise run with no flag at all). Never\n * bypassPermissions \u2014 no path here writes it. */\nexport function modeFlags(level: RunLevel, caps: Caps): { args: string[] } | { error: string } {\n if (level === \"inherit\") return { args: [] };\n if (caps.unread)\n return { error: `this machine couldn't read its Claude Code's options (${caps.unread}); try again in a minute` };\n if (level === \"auto\" && !caps.modes.includes(\"auto\"))\n return { error: \"this machine is in auto mode, but its Claude Code has no auto permission mode\" };\n if (level !== \"safe\") {\n const m = LEVEL_MODE[level];\n if (!caps.modes.includes(m)) return { error: `this machine's Claude Code has no ${m} permission mode` };\n return { args: [\"--permission-mode\", m] };\n }\n if (caps.modes.includes(\"dontAsk\")) return { args: [\"--permission-mode\", \"dontAsk\"] };\n const args: string[] = [];\n if (caps.hasMode)\n args.push(\n \"--permission-mode\",\n caps.modes.includes(\"manual\") && !caps.modes.includes(\"default\") ? \"manual\" : \"default\",\n );\n if (caps.promptsNone) args.push(\"--permission-prompts\", \"none\");\n return { args };\n}\n\n/** The widest level a start may name on a machine in `mode`: the mode itself, and safe\n * on an inherit machine, whose own default sessionpipe can't rank, so a message there may\n * only narrow to a level that never runs anything needing approval. */\nexport const ceilingLevel = (mode: Mode): StartPermission => (mode === \"inherit\" ? \"safe\" : mode);\n\n/** The levels a start may ask Claude Code for on a machine in `mode`: never above its\n * ceiling, and each only where `claude --help` lists the mode it maps to (safe always).\n * None while that --help couldn't be read: an offered safe would be taken and then run\n * with no permission flag at all. */\nexport function offeredLevels(mode: Mode, caps: Caps): StartPermission[] {\n if (caps.unread) return [];\n return START_PERMISSIONS.filter(\n (l) => levelRank(l) <= levelRank(ceilingLevel(mode)) && (l === \"safe\" || caps.modes.includes(LEVEL_MODE[l])),\n );\n}\n\n/** What a start that names no level gets: what the machine's mode means for Claude Code. */\nexport const defaultLevel = (mode: Mode): RunLevel => mode;\n\n/** How a headless run prints: streamed where this Claude Code can, else one JSON at the end. */\nexport function outputArgs(caps: { stream?: boolean }): string[] {\n return caps.stream\n ? [\"--output-format\", \"stream-json\", \"--verbose\", \"--include-partial-messages\"]\n : [\"--output-format\", \"json\"];\n}\n\n/** One headless `claude -p` run: a new session (`session`), or the next message of one\n * (`resume`, optionally as a fork). The model is its own argument after `--model`, and\n * a prompt that starts with a dash gets a leading space, so it is never read as a flag. */\nexport function claudeArgs(o: {\n caps: Caps;\n level: RunLevel;\n prompt: string;\n session?: string;\n resume?: string;\n fork?: boolean;\n model?: string;\n allowedTools?: string[];\n}): { args: string[] } | { error: string } {\n const mf = modeFlags(o.level, o.caps);\n if (\"error\" in mf) return mf;\n const where = o.resume\n ? [\"--resume\", o.resume, ...(o.fork ? [\"--fork-session\"] : [])]\n : [\"--session-id\", o.session as string];\n return {\n args: [\n \"-p\",\n ...outputArgs(o.caps),\n ...mf.args,\n ...(o.model ? [\"--model\", o.model] : []),\n ...(o.allowedTools?.length ? [`--allowedTools=${o.allowedTools.join(\",\")}`] : []),\n ...where,\n o.prompt.startsWith(\"-\") ? ` ${o.prompt}` : o.prompt,\n ],\n };\n}\n\n/** The env a headless turn runs with: ours, minus the markers that make Claude Code\n * think it runs inside another session and minus any lineage this process inherited\n * (jobEnv), with CLAUDE_CONFIG_DIR naming the account. */\nexport function claudeEnv(configDir?: string, env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {\n const e = withoutLineage(env);\n for (const k of [\n \"CLAUDECODE\",\n \"CLAUDE_CODE_SESSION_ID\",\n \"CLAUDE_CODE_HOST_SESSION_ID\",\n \"CLAUDE_CODE_ENTRYPOINT\",\n \"CLAUDE_CODE_SESSION_ATTENDED\",\n \"CLAUDE_CODE_SSE_PORT\",\n ])\n delete e[k];\n e.SESSIONPIPE_CONTROL_JOB = \"1\";\n if (configDir !== undefined) {\n if (path.resolve(configDir) === path.resolve(os.homedir(), \".claude\")) delete e.CLAUDE_CONFIG_DIR;\n else e.CLAUDE_CONFIG_DIR = configDir;\n }\n return e;\n}\n\n/** Whether Claude Code is signed in for one account, in Claude Code's own words\n * (`claude auth status --json`). `loggedIn: null` means this Claude Code can't say\n * (too old for `auth status`, or it didn't answer): never a reason to refuse. */\nexport function parseAuthStatus(stdout: string | null | undefined): Login {\n const text = String(stdout || \"\").trim();\n const start = text.indexOf(\"{\");\n if (start < 0) return { loggedIn: null };\n try {\n const j = JSON.parse(text.slice(start)) as Record<string, unknown>;\n if (typeof j.loggedIn !== \"boolean\") return { loggedIn: null };\n return {\n loggedIn: j.loggedIn,\n ...(typeof j.authMethod === \"string\" ? { method: j.authMethod } : {}),\n ...(typeof j.subscriptionType === \"string\" ? { subscription: j.subscriptionType } : {}),\n };\n } catch {\n return { loggedIn: null };\n }\n}\n\n/** Ask Claude Code whether the account in `configDir` (the default one when omitted)\n * is signed in, from this process \u2014 which is the point: a login kept in the macOS\n * keychain is readable from the screen's own session and not from a background\n * service on a Mac nobody is logged in to, or from an ssh shell, so a headless turn\n * started there dies with \"please run /login\". */\nexport function claudeLogin(bin: string, configDir?: string, env: NodeJS.ProcessEnv = process.env): Promise<Login> {\n // Through `probe`: a .cmd shim (an npm install on Windows) can't be spawned bare, and\n // execFile's synchronous throw would reject this promise and take doctor down with it.\n const c = probe(bin, [\"auth\", \"status\", \"--json\"]);\n return new Promise((resolve) => {\n execFile(\n c.file,\n c.args,\n { encoding: \"utf8\", timeout: 20_000, env: claudeEnv(configDir, env), maxBuffer: 256 * 1024, shell: c.shell },\n // `auth status` may exit non-zero when signed out; its JSON still says so.\n (_err, stdout) => resolve(parseAuthStatus(stdout)),\n );\n });\n}\n\n/** The command that signs one account in, quoted for a sentence. */\nexport function loginCommand(configDir: string | undefined): string {\n const home = os.homedir();\n const isDefault = !configDir || path.resolve(configDir) === path.resolve(home, \".claude\");\n return isDefault ? \"`claude auth login`\" : `\\`CLAUDE_CONFIG_DIR=${configDir.replace(home, \"~\")} claude auth login\\``;\n}\n\n/** What to tell the person when an account isn't signed in for the daemon. */\nexport function loginHint(configDir: string | undefined, platform: NodeJS.Platform = process.platform): string {\n const shown = configDir ? configDir.replace(os.homedir(), \"~\") : \"~/.claude\";\n const login = loginCommand(configDir);\n if (platform === \"darwin\")\n return `Claude Code (${shown}) isn't signed in as this machine's background service sees it. On a Mac its login lives in the login keychain, which a background service can read only while someone is logged in at the screen: log in at the Mac, or run ${login} there.`;\n return `Claude Code (${shown}) isn't signed in on this machine: run ${login} there.`;\n}\n\n/** What `claude --help` lists. A run that fails still counts when it printed its\n * `--permission-mode` line; one that didn't (a timeout on a machine still starting up,\n * most often) is `unread`, never a Claude Code with no modes. */\nexport function detectCaps(bin: string, timeoutMs = 30_000): Caps {\n const c = probe(bin, [\"--help\"]); // a claude.cmd shim can't be spawned bare (#165)\n try {\n return parseCaps(\n execFileSync(c.file, c.args, {\n encoding: \"utf8\",\n timeout: timeoutMs,\n env: claudeEnv(),\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n maxBuffer: 4 * 1024 * 1024,\n shell: c.shell,\n }),\n );\n } catch (e) {\n const err = e as { stdout?: string; code?: string };\n const caps = parseCaps(String(err?.stdout || \"\"));\n if (caps.hasMode) return caps;\n const why = err?.code === \"ETIMEDOUT\" ? `timed out after ${timeoutMs / 1000} s` : \"failed\";\n return { ...caps, unread: `\\`${path.basename(bin)} --help\\` ${why}` };\n }\n}\n\nexport interface Transcript {\n file: string;\n configDir: string;\n project: string;\n}\n\n/** <config dir>/projects/<folder>/<session>.jsonl in one Claude Code config dir. */\nfunction transcriptIn(session: string, dir: string): Transcript | null {\n let projects: import(\"node:fs\").Dirent[] = [];\n try {\n projects = readdirSync(path.join(dir, \"projects\"), { withFileTypes: true });\n } catch {\n return null;\n }\n for (const p of projects) {\n if (!p.isDirectory()) continue;\n const file = path.join(dir, \"projects\", p.name, `${session}.jsonl`);\n try {\n if (statSync(file).isFile()) return { file, configDir: dir, project: p.name };\n } catch {}\n }\n return null;\n}\n\n/** <config dir>/projects/<folder>/<session>.jsonl in any Claude Code config dir. */\nexport function findTranscript(session: string, dirs: readonly string[]): Transcript | null {\n if (!UUID_RE.test(session)) return null;\n for (const dir of dirs) {\n const t = transcriptIn(session, dir);\n if (t) return t;\n }\n return null;\n}\n\n/** Every config dir that holds this session's transcript, in the order given. One\n * session id can be filed in several accounts: a transcript copied between homes, the\n * same id resumed under another account. There were six such ids on one Mac in\n * October 2026, and picking the first \u2014 which is what reading one home does \u2014 put a\n * message into an account that was signed out. */\nexport function findTranscripts(session: string, dirs: readonly string[]): Transcript[] {\n if (!UUID_RE.test(session)) return [];\n const out: Transcript[] = [];\n for (const dir of dirs) {\n const t = transcriptIn(session, dir);\n if (t) out.push(t);\n }\n return out;\n}\n\n/** Which Claude Code account a session belongs to, when more than one has its\n * transcript: the one whose own `sessions/` says a live process holds it. `ambiguous`\n * is a refusal rather than a guess \u2014 it names every account that has the transcript,\n * and in `live` the ones holding it open, which is none of them or more than one. Both\n * are named by their directory name alone, since an ack travels to the receiver. Null\n * when no account has the session at all. */\nexport type HomeChoice = { transcript: Transcript } | { ambiguous: string[]; live: string[] };\n\nconst homeName = (t: Transcript) => path.basename(t.configDir);\n\nexport function resolveHome(session: string, dirs: readonly string[]): HomeChoice | null {\n const all = findTranscripts(session, dirs);\n if (!all.length) return null;\n if (all.length === 1) return { transcript: all[0] as Transcript };\n const live = all.filter((t) => liveRecords(session, t.configDir).length > 0);\n if (live.length === 1) return { transcript: live[0] as Transcript };\n return {\n ambiguous: [...new Set(all.map(homeName))].sort(),\n live: [...new Set(live.map(homeName))].sort(),\n };\n}\n\nconst projectName = (cwd: string) => cwd.replace(/[^a-zA-Z0-9]/g, \"-\");\n\n/** The folder a session runs in, from its transcript's own records (never from a\n * command): the `cwd` whose project name is the folder the transcript is filed\n * under, else the first `cwd`. Streams; a line over 2 MB is skipped. */\nexport function sessionFolder(t: Transcript, cap = 8 * 1024 * 1024): string | null {\n let first: string | null = null;\n let count = 0;\n let fd: number | undefined;\n const look = (line: Buffer): string | null => {\n if (line.indexOf('\"cwd\"') < 0) return null;\n let j: { cwd?: unknown };\n try {\n j = JSON.parse(line.toString(\"utf8\"));\n } catch {\n return null;\n }\n if (!j || typeof j.cwd !== \"string\" || !path.isAbsolute(j.cwd)) return null;\n if (projectName(j.cwd) === t.project) return j.cwd;\n first ??= j.cwd;\n count++;\n return null;\n };\n try {\n fd = openSync(t.file, \"r\");\n const buf = Buffer.alloc(256 * 1024);\n let pos = 0;\n let rest = Buffer.alloc(0);\n let skipping = false;\n while (pos < cap && count < 50) {\n const n = readSync(fd, buf, 0, buf.length, pos);\n if (!n) {\n if (!skipping && rest.length) {\n const hit = look(rest);\n if (hit) return hit;\n }\n break;\n }\n pos += n;\n let chunk = rest.length ? Buffer.concat([rest, buf.subarray(0, n)]) : buf.subarray(0, n);\n let nl = chunk.indexOf(10);\n while (nl >= 0) {\n const line = chunk.subarray(0, nl);\n chunk = chunk.subarray(nl + 1);\n if (skipping) skipping = false;\n else {\n const hit = look(line);\n if (hit) return hit;\n }\n nl = chunk.indexOf(10);\n }\n if (chunk.length > 2 * 1024 * 1024) {\n skipping = true;\n rest = Buffer.alloc(0);\n } else rest = skipping ? Buffer.alloc(0) : Buffer.from(chunk);\n }\n } catch {\n } finally {\n if (fd !== undefined)\n try {\n closeSync(fd);\n } catch {}\n }\n return first;\n}\n\nfunction pidAlive(pid: number): boolean {\n if (!Number.isSafeInteger(pid) || pid <= 1) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch (e) {\n return (e as { code?: string }).code === \"EPERM\";\n }\n}\n\n/** One live Claude Code process's registry row, `<config>/sessions/<pid>.json`, as the\n * in-place `inbox` lane needs it (spec/CONTROL.md \u00A76.3). Every session that registers\n * files one \u2014 a terminal window, the desktop app and a claude.ai bridge worker alike,\n * all of them `kind: \"interactive\"`; a `-p` one-shot's row is not one of those. */\nexport interface LiveRecord {\n /** The config dir this row was read from: the lane never crosses accounts. */\n configDir: string;\n file: string;\n pid: number;\n /** Claude Code's own local inbox for this process, or null when it published none. */\n socket: string | null;\n /** 1 is the only inbox protocol this code speaks; anything else is refused. */\n peerProtocol: number | null;\n /** The process's start time as the registry spells it (UTC), so a recycled pid can be\n * told from the process that filed the row. */\n procStart: string | null;\n status: string | null;\n /** `interactive` for a session the harness holds open for the person. */\n kind: string | null;\n entrypoint: string | null;\n /** `<pid>.<sha>.key`, 0600: where the inbox's auth token lives (needed off macOS). */\n keyFile: string | null;\n}\n\nconst registryRow = (file: string, configDir: string, j: Record<string, unknown>): LiveRecord => {\n const pid = Number(j.pid ?? path.basename(file).slice(0, -5));\n const str = (v: unknown) => (typeof v === \"string\" && v ? v : null);\n let keyFile: string | null = null;\n try {\n // Claude Code's own spelling: `<pid>.<64 hex>.key`, 0600, beside the registry row.\n const want = new RegExp(`^${pid}\\\\.[0-9a-f]{64}\\\\.key$`);\n const hit = readdirSync(path.dirname(file)).find((n) => want.test(n));\n if (hit) keyFile = path.join(path.dirname(file), hit);\n } catch {}\n return {\n configDir,\n file,\n pid,\n socket: str(j.messagingSocketPath),\n peerProtocol: typeof j.peerProtocol === \"number\" ? j.peerProtocol : null,\n procStart: str(j.procStart),\n status: str(j.status),\n kind: str(j.kind),\n entrypoint: str(j.entrypoint),\n keyFile,\n };\n};\n\n/** Every live registry row for this session, across the config dirs given. A row whose\n * pid is gone is not one: the file outlives the process. */\nexport function liveRecords(session: string, dirs: string | readonly string[]): LiveRecord[] {\n const out: LiveRecord[] = [];\n for (const dir of typeof dirs === \"string\" ? [dirs] : dirs) {\n let names: string[] = [];\n try {\n names = readdirSync(path.join(dir, \"sessions\")).filter((n) => /^\\d+\\.json$/.test(n));\n } catch {\n continue;\n }\n for (const n of names) {\n const file = path.join(dir, \"sessions\", n);\n let j: Record<string, unknown>;\n try {\n j = JSON.parse(readFileSync(file, \"utf8\")) as Record<string, unknown>;\n } catch {\n continue;\n }\n if (String(j.sessionId) !== session) continue;\n const row = registryRow(file, dir, j);\n if (pidAlive(row.pid)) out.push(row);\n }\n }\n return out;\n}\n\n/** The ONE live process that holds this session, or null. Two rows \u2014 two accounts, or\n * two processes in one \u2014 is a refusal rather than a guess: a lane that writes into a\n * live session has to know which process it is writing to (spec/CONTROL.md \u00A76.3). */\nexport function liveRecord(session: string, dirs: string | readonly string[]): LiveRecord | null {\n const all = liveRecords(session, dirs);\n return all.length === 1 ? (all[0] as LiveRecord) : null;\n}\n\n/** A Claude Code process holds this session right now: its registry file,\n * <config>/sessions/<pid>.json, names it and the pid is alive (interactive and\n * Agent SDK sessions both file one). */\nexport function liveProcess(session: string, configDir: string): boolean {\n return liveRecords(session, configDir).length > 0;\n}\n\n/** Claude Code's own permission modes, by how much a turn may do in them without being\n * asked about (its own order, 2.1.310). `plan` writes nothing at all. `default`,\n * `manual` and `dontAsk` share a step: none of them lets anything through that needs\n * approval \u2014 the first two ask the person, `dontAsk` refuses instead. `acceptEdits`\n * writes files; `auto` runs most things; `bypassPermissions` asks about nothing.\n *\n * Used to tell one mode from another and to compare two, never to pick one. -1 is \"not\n * a mode this code knows\", which compares below everything. */\nexport const MODE_RANK: Record<string, number> = {\n plan: 0,\n default: 1,\n manual: 1,\n dontAsk: 1,\n acceptEdits: 2,\n auto: 3,\n bypassPermissions: 4,\n};\nexport const CLAUDE_MODES = Object.keys(MODE_RANK);\nexport const modeRank = (m: string | null | undefined): number => (m && m in MODE_RANK ? (MODE_RANK[m] as number) : -1);\n\n/** The permission mode a hook payload reports for its own session, or null. Claude Code\n * writes `permission_mode` on every hook payload; `permissionMode` is the spelling its\n * transcript records use, and is read too so one reader serves both.\n *\n * This is the only place a mode the person chose at the keyboard can be read from: a\n * session's registry row doesn't carry it, and the transcript names it only on turns\n * that happen to record one. hook.ts repeats the shape test, because it imports nothing\n * but node builtins. */\nexport function hookMode(payload: unknown): string | null {\n if (!payload || typeof payload !== \"object\") return null;\n const p = payload as { permission_mode?: unknown; permissionMode?: unknown };\n const v = typeof p.permission_mode === \"string\" ? p.permission_mode : p.permissionMode;\n return typeof v === \"string\" && CLAUDE_MODES.includes(v) ? v : null;\n}\n\n/** The permission class Claude Code's own inbox checks a sender's declared class\n * against (2.1.310): `bypassPermissions` is `bypass`; a mode that prompts before it\n * acts is `prompting`. `plan` is neither \u2014 its class there depends on a capability and\n * a TTY this code cannot see \u2014 and nor is `auto`, nor a mode nothing has recorded, so\n * those get no in-place lane at all (spec/CONTROL.md \u00A76.3). */\nexport function peerClass(mode: string | null | undefined): \"bypass\" | \"prompting\" | null {\n if (mode === \"bypassPermissions\") return \"bypass\";\n if (mode === \"default\" || mode === \"manual\" || mode === \"dontAsk\" || mode === \"acceptEdits\") return \"prompting\";\n return null;\n}\n\n/** One Claude Code account's permission settings, user scope only\n * (`<config dir>/settings.json`): a folder's own settings may tighten these but never\n * loosen them, and a managed policy may tighten them further. Only the three keys\n * something here acts on.\n *\n * Reading user scope alone can overstate how wide a home is, never understate it, and\n * everything that reads this treats a wider answer as a reason NOT to act (the inbox\n * lane needs the machine's class to equal the session's own; a narrowing is re-asserted\n * only where the home's default is at least as wide). */\nexport interface HomeSettings {\n /** `permissions.defaultMode`: what a session with no flag starts in. Null: not said,\n * or the file could not be read. */\n defaultMode: string | null;\n /** `disableAutoMode: \"disable\"`: asking for auto mode quietly gives Manual instead. */\n disableAutoMode: boolean;\n /** `disableBypassPermissionsMode: \"disable\"`: this home cannot be in bypass at all. */\n disableBypass: boolean;\n}\n\nexport const NO_HOME_SETTINGS: HomeSettings = { defaultMode: null, disableAutoMode: false, disableBypass: false };\n\nexport function homeSettings(configDir: string | undefined, home = os.homedir()): HomeSettings {\n const file = path.join(configDir ?? path.join(home, \".claude\"), \"settings.json\");\n let j: Record<string, unknown>;\n try {\n j = JSON.parse(readFileSync(file, \"utf8\")) as Record<string, unknown>;\n } catch {\n return { ...NO_HOME_SETTINGS };\n }\n if (!j || typeof j !== \"object\" || Array.isArray(j)) return { ...NO_HOME_SETTINGS };\n const p = (j.permissions && typeof j.permissions === \"object\" ? j.permissions : {}) as Record<string, unknown>;\n // Both spellings: Claude Code reads these two under `permissions`, and settings\n // written by hand (and its own older docs) put them at the top level.\n const off = (k: string) => j[k] === \"disable\" || p[k] === \"disable\";\n return {\n defaultMode: typeof p.defaultMode === \"string\" && CLAUDE_MODES.includes(p.defaultMode) ? p.defaultMode : null,\n disableAutoMode: off(\"disableAutoMode\"),\n disableBypass: off(\"disableBypassPermissionsMode\"),\n };\n}\n\n/** The `--permission-mode` a relaunch of ONE session re-asserts, or null for no flag at\n * all (spec/CONTROL.md \u00A76 `prompt`). Only a narrowing the session itself chose, and\n * only where it is one:\n *\n * - `plan` always: nothing this harness has is narrower, so it cannot widen anything;\n * - `dontAsk` and `acceptEdits` where the account's own default is at least as wide\n * (`MODE_RANK`), so a re-assertion is never wider than what the machine itself does\n * with no flag. Settings that can't be read mean it isn't re-asserted;\n * - everything else \u2014 `default`, `manual`, `auto`, `bypassPermissions`, nothing\n * recorded \u2014 is no flag, and the account's own default decides.\n *\n * The default it compares against is the account's own (`permissions.defaultMode`, user\n * scope). A folder's settings or a managed policy may narrow that further, invisibly\n * here; both narrow what the re-asserted mode can actually do too, so the comparison\n * errs on the side of asking.\n *\n * It never returns `auto` or `bypassPermissions`: the two values a setting can quietly\n * turn into something else, and the two this code must never write. `auto` is the\n * incident this came from \u2014 it asks for a mode that `disableAutoMode` turns into Manual,\n * so a session that recorded it gets no flag and the account's default instead.\n *\n * No flag is not neutral. On a fleet whose accounts default to bypass it is the widest\n * outcome there is, which is why a narrowing the person chose has to be said again. */\nexport function relaunchMode(recorded: string | null | undefined, home: HomeSettings): string | null {\n if (recorded === \"plan\") return recorded;\n if ((recorded === \"dontAsk\" || recorded === \"acceptEdits\") && modeRank(home.defaultMode) >= modeRank(recorded))\n return recorded;\n return null;\n}\n\n/** Written in the last 90 s, and not by a job of ours. */\nexport function recentlyWritten(file: string, ourLastEnd?: number, now = Date.now()): boolean {\n let m: number;\n try {\n m = statSync(file).mtimeMs;\n } catch {\n return false;\n }\n if (now - m > LIVE_WRITE_MS) return false;\n return !(ourLastEnd && m <= ourLastEnd + 2000);\n}\n\n/** Run one headless turn (headless.ts). Streaming (`--output-format stream-json`), only\n * the `result` line is kept for the RunResult unless the caller says otherwise. */\nexport function runClaude(bin: string, args: string[], o: RunOptions): Promise<RunResult> {\n return runHeadless(bin, args, { ...o, keep: o.keep ?? ((line) => line.includes('\"type\":\"result\"')) });\n}\n\nexport interface ClaudeResult {\n type?: string;\n result?: unknown;\n is_error?: boolean;\n subtype?: string;\n session_id?: unknown;\n permission_denials?: { tool_name?: unknown }[];\n}\n\n/** `--output-format json`: one result object, or an array whose last `result` entry it is. */\nexport function parseResult(stdout: string | null | undefined): ClaudeResult | null {\n const s = String(stdout || \"\").trim();\n if (!s) return null;\n const pick = (j: unknown): ClaudeResult | null => {\n if (Array.isArray(j)) {\n for (let i = j.length - 1; i >= 0; i--) if (j[i]?.type === \"result\") return j[i];\n return null;\n }\n return j && typeof j === \"object\" ? (j as ClaudeResult) : null;\n };\n try {\n return pick(JSON.parse(s));\n } catch {}\n const lines = s.split(\"\\n\");\n for (let i = lines.length - 1; i >= 0; i--) {\n try {\n const j = pick(JSON.parse(lines[i] as string));\n if (j && (j.type === \"result\" || \"result\" in j)) return j;\n } catch {}\n }\n return null;\n}\n\nexport const isUuid = (s: unknown): s is string => typeof s === \"string\" && UUID_RE.test(s);\n", "// SPDX-License-Identifier: Apache-2.0\n// The one verifier for signed control commands (spec/CONTROL.md \u00A74\u2013\u00A75). A machine\n// runs a command only when this says yes; a receiver MAY run it too, as an early\n// word, so a bad command is refused where the person can see why.\n//\n// WebCrypto only (globalThis.crypto.subtle), no node: imports, no clock of its own\n// (`now` is passed in): the same file runs in a Cloudflare Worker, in a browser and\n// in Node \u2265 20, and conformance/control-vectors/ pins its answers.\n//\n// Every signature covers the EXACT bytes as transmitted (`cmd`, `grant.str`): the\n// strings are parsed to read their fields and never re-serialized to verify.\n\n/** One passkey tap vouches for a day key for at most this long. */\nexport const GRANT_MAX_MS = 24 * 3600_000;\n/** Clock slack between the signing device, the receiver and the machine. */\nexport const SKEW_MS = 5 * 60_000;\n/** The machine judges a command by when it was SIGNED: a message sent while the lid\n * was shut still runs when the machine wakes, up to a day later. */\nexport const MACHINE_MAX_AGE_MS = 24 * 3600_000;\n/** A receiver's early check is stricter: a command is carried within ten minutes of\n * being signed, or not at all. */\nexport const RECEIVER_MAX_AGE_MS = 10 * 60_000;\n/** How long a machine remembers a nonce: longer than MACHINE_MAX_AGE_MS + SKEW_MS. */\nexport const NONCE_TTL_MS = 25 * 3600_000;\n/** The whole command string, in UTF-16 code units (what JSON.parse sees). */\nexport const CMD_MAX = 48_000;\n/** A prompt's text, in characters. */\nexport const TEXT_MAX = 20_000;\n/** A permission answer's note. */\nexport const NOTE_MAX = 2_000;\n/** A grant string. */\nexport const GRANT_STR_MAX = 2_000;\n/** Attachments on a `start` or a `prompt` (CONTROL.md \u00A73.1): at most this many files, */\nexport const FILES_MAX = 10;\n/** each at most this many bytes, */\nexport const FILE_SIZE_MAX = 26_214_400;\n/** together at most this many, */\nexport const FILES_TOTAL_MAX = 52_428_800;\n/** with a name of at most this many characters (after NFC) */\nexport const FILE_NAME_MAX = 200;\n/** and a media type of at most this many. */\nexport const FILE_TYPE_MAX = 100;\n/** A start's `model` (CONTROL.md \u00A73.1): at most this many characters, */\nexport const MODEL_MAX = 100;\n/** in this alphabet, never starting with a dash, so it can never read as a flag. */\nexport const MODEL_RE = /^[A-Za-z0-9][A-Za-z0-9._:/@[\\]-]*$/;\n/** The levels a start may ask for (CONTROL.md \u00A73.1), narrowest first. Closed: no level\n * is wider than `auto`, and none turns a harness's approval off. */\nexport const START_PERMISSIONS = [\"read\", \"safe\", \"edits\", \"auto\"] as const;\nexport type StartPermission = (typeof START_PERMISSIONS)[number];\n/** What a run is started with: a level, or `inherit` (CONTROL.md \u00A72) \u2014 no permission\n * flag at all, so the harness starts in its own default on the machine. Only a machine\n * the person put in inherit mode runs that way; a command never names it. */\nexport type RunLevel = StartPermission | \"inherit\";\n\n/** Where a level sits, narrowest (0) to widest; -1 for anything that isn't one. */\nexport const levelRank = (l: unknown): number => START_PERMISSIONS.indexOf(l as StartPermission);\n/** The narrower of two levels: what a later run of a session gets, its recorded level\n * against what the machine's mode allows now. */\nexport const narrower = (a: StartPermission, b: StartPermission): StartPermission =>\n levelRank(a) <= levelRank(b) ? a : b;\n\nexport const COMMAND_TYPE = \"sessionpipe.control\";\nexport const GRANT_TYPE = \"sessionpipe.grant\";\nexport const CONTROL_KINDS = [\"prompt\", \"permission.answer\", \"cancel\", \"start\", \"key.add\", \"pool\"] as const;\n/** The session a `key.add` names: the machine's own key store, not a coding session. */\nexport const KEY_STORE_SESSION = \"sessionpipe:keys\";\n/** The session a `pool` names: the machine's account pool (CONTROL.md \u00A76 `pool`). */\nexport const POOL_SESSION = \"sessionpipe:pool\";\nexport type ControlKind = (typeof CONTROL_KINDS)[number];\n/** What a `pool` command may ask (CONTROL.md \u00A76 `pool`). Closed: an action added later\n * is refused by every machine that predates it. */\nexport const POOL_ACTIONS = [\n \"on\",\n \"off\",\n \"enable\",\n \"disable\",\n \"remove\",\n \"order\",\n \"refresh\",\n \"login.begin\",\n \"login.complete\",\n] as const;\nexport type PoolAction = (typeof POOL_ACTIONS)[number];\n/** A pooled account's id (or a pending sign-in's): what `session.account_id` carries. */\nexport const POOL_ACCOUNT_RE = /^[A-Za-z0-9._:-]{1,128}$/;\n/** An `order` names at most this many accounts: the pool holds no more. */\nexport const POOL_ORDER_MAX = 20;\n/** The pasted `code#state` of a sign-in, in characters, on one line. */\nexport const POOL_CODE_MAX = 512;\n\n/** COSE algorithm ids a trusted key may use. */\nexport const ES256 = -7;\nexport const RS256 = -257;\n\n/** The kinds of key a machine may trust (CONTROL.md \u00A72): a WebAuthn passkey, or a\n * device key an app holds in hardware where it has no passkey to offer. */\nexport const KEY_TYPES = [\"passkey\", \"device\"] as const;\nexport type KeyType = (typeof KEY_TYPES)[number];\n\n/** A key enrolled on the machine: a passkey's credential id, COSE alg and SPKI\n * (base64url); a device key also says so (`type`), and what its app declared about\n * it (`store`, `uv`). The machine verifies by its own record, never by a message. */\nexport interface TrustedKey {\n id: string;\n alg: number;\n spki: string;\n /** Present only on a device key (CONTROL.md \u00A72 \"Device keys\"). */\n type?: \"device\";\n store?: \"hardware\";\n uv?: \"each-use\";\n}\n/** A WebAuthn assertion: credential id, authenticatorData, clientDataJSON, signature (base64url). */\nexport interface Assertion {\n cred: string;\n ad: string;\n cd: string;\n sig: string;\n}\n/** A device key's signature (CONTROL.md \u00A73.2): its id and 64 bytes r\u2016s over deviceMessage(). */\nexport interface DeviceSignature {\n cred: string;\n sig: string;\n}\n/** The day's grant: a passkey's assertion over sha256(str), where str names the day key. */\nexport interface GrantBundle extends Assertion {\n str: string;\n}\n/** The day's grant from a device key: its signature over deviceMessage(\"grant\", rp, sha256(str)). */\nexport interface DeviceGrant extends DeviceSignature {\n str: string;\n}\n/** What a receiver carries for one command (control.json minus the receiver's envelope). */\nexport interface SignedCommand {\n cmd: string;\n csig?: string | null;\n grant?: GrantBundle | DeviceGrant | null;\n confirm?: Assertion | DeviceSignature | null;\n}\nexport interface Grant {\n v: 1;\n t: typeof GRANT_TYPE;\n pub: string;\n iat: number;\n exp: number;\n rp: string;\n}\n/** One file attached to a `start`: the bytes live on the receiver, named by their hash. */\nexport interface ControlFile {\n /** The file name the machine writes (NFC). */\n name: string;\n /** Media type, `type/subtype`. */\n type: string;\n /** Bytes. */\n size: number;\n /** SHA-256 of the bytes, base64url without padding (43 characters). */\n sha256: string;\n}\nexport interface ControlCommand {\n v: 1;\n t: typeof COMMAND_TYPE;\n machine: string;\n kind: ControlKind;\n /** `<harness>:<id>`, e.g. `claude-code:8c132906-\u2026`. */\n session: string;\n text?: string;\n for?: string;\n decision?: \"allow\" | \"deny\";\n note?: string;\n cwd?: string;\n /** key.add only: the key to trust from now on, as the receiver holds it (a passkey,\n * or a device key with its `type`, `store` and `uv`). */\n key?: TrustedKey;\n /** key.add only: what the person calls the device (\"iPhone\"), for `control keys`. */\n name?: string;\n /** start, prompt: files to download, verify and write into the session's folder. */\n files?: ControlFile[];\n /** start only: the level the new session runs at (never above the machine's mode). */\n permission?: StartPermission;\n /** start only: the model the harness runs, as the harness names it. */\n model?: string;\n /** pool only (session `sessionpipe:pool`): what to do with the account pool. */\n pool_action?: PoolAction;\n /** pool: the account (enable, disable, remove, refresh), or the pending sign-in's id\n * (login.complete). */\n pool_account?: string;\n /** pool, order: every account of the pool once, in the order wanted. */\n pool_order?: string[];\n /** pool, login.complete: the `code#state` the person pasted back. */\n pool_code?: string;\n /** start, prompt: `<harness>:<id>` of a session on the same machine that gets the\n * answer as a message once the turn ends (CONTROL.md \u00A76, a handoff). */\n reply_to?: string;\n nonce: string;\n iat: number;\n}\n\n/** Why a command was refused. Stable: conformance vectors and acks name these. */\nexport type RefusalCode =\n | \"malformed\"\n | \"not_a_command\"\n | \"wrong_machine\"\n | \"unknown_kind\"\n | \"bad_session\"\n | \"bad_fields\"\n | \"bad_nonce\"\n | \"too_old\"\n | \"future\"\n | \"unsigned\"\n | \"untrusted_key\"\n | \"bad_assertion\"\n | \"bad_grant\"\n | \"outside_grant\"\n | \"bad_csig\"\n | \"replay\"\n | \"nonce_store\";\n\nexport type Verdict =\n | { ok: true; cmd: ControlCommand; via: \"grant\" | \"confirm\"; key: string }\n | { ok: false; code: RefusalCode; why: string };\n\nexport interface VerifyOptions {\n /** This machine's id; a command for another machine is refused. */\n machine: string;\n /** The receiver's WebAuthn rp id the keys were enrolled for (well-known control.signing.rp_id). */\n rpId: string;\n /** The keys enrolled on this machine (or, for a receiver's early check, the account's). */\n trusted: TrustedKey[];\n /** Epoch ms. */\n now: number;\n /** MACHINE_MAX_AGE_MS on a machine (default), RECEIVER_MAX_AGE_MS on a receiver. */\n maxAgeMs?: number;\n /** Called once, last, only for a command whose signatures all verified: answer\n * true when the nonce was seen before, otherwise record it (durably) and answer\n * false. A throw refuses the command (`nonce_store`): never run what could not\n * be remembered. Omit it on a receiver's early check. */\n seenNonce?: (nonce: string) => boolean | Promise<boolean>;\n}\n\nconst NONCE_RE = /^[A-Za-z0-9_-]{22,64}$/;\nconst MACHINE_RE = /^m_[A-Za-z0-9_-]{16,40}$/;\nconst HARNESS_RE = /^[a-z][a-z0-9-]{0,39}$/;\nconst SESSION_ID_RE = /^[A-Za-z0-9._:-]{1,128}$/;\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst B64URL_RE = /^[A-Za-z0-9_-]*$/;\nconst SHA256_RE = /^[A-Za-z0-9_-]{43}$/;\nconst MEDIA_TYPE_RE = /^[a-z0-9][a-z0-9!#$&^_.+-]*\\/[a-z0-9][a-z0-9!#$&^_.+-]*$/i;\n// biome-ignore lint/suspicious/noControlCharactersInRegex: control characters are exactly what it rejects\nconst BAD_NAME_CHAR_RE = /[/\\\\\\x00-\\x1f\\x7f]/;\nconst UP = 0x01;\nconst UV = 0x04;\n\n/** The fields each kind carries besides the common ones (CONTROL.md \u00A73.1). The set is\n * closed: a field it doesn't list is `bad_fields`, so a field added later fails closed\n * on a machine that predates it instead of being ignored. */\nconst COMMON_FIELDS: readonly string[] = [\"v\", \"t\", \"machine\", \"kind\", \"session\", \"nonce\", \"iat\", \"note\"];\nconst FIELDS: Record<ControlKind, readonly string[]> = {\n prompt: [\"text\", \"files\", \"reply_to\"],\n \"permission.answer\": [\"for\", \"decision\"],\n cancel: [],\n start: [\"text\", \"cwd\", \"files\", \"permission\", \"model\", \"reply_to\"],\n \"key.add\": [\"key\", \"name\"],\n pool: [\"pool_action\", \"pool_account\", \"pool_order\", \"pool_code\"],\n};\n/** The fields only a `pool` carries. */\nconst POOL_FIELDS = FIELDS.pool;\n\n// --- encodings ------------------------------------------------------------------\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder();\n\nexport function b64url(bytes: Uint8Array | ArrayBuffer): string {\n const u = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let s = \"\";\n for (const b of u) s += String.fromCharCode(b);\n return btoa(s).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n/** base64url without padding, strictly; null for anything else. */\nexport function fromB64url(s: unknown): Uint8Array | null {\n if (typeof s !== \"string\" || !B64URL_RE.test(s) || s.length % 4 === 1) return null;\n try {\n const bin = atob(s.replace(/-/g, \"+\").replace(/_/g, \"/\") + \"===\".slice((s.length + 3) % 4));\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n } catch {\n return null;\n }\n}\n\nexport async function sha256(data: string | Uint8Array): Promise<Uint8Array> {\n const bytes = typeof data === \"string\" ? enc.encode(data) : data;\n return new Uint8Array(await crypto.subtle.digest(\"SHA-256\", bytes as Uint8Array<ArrayBuffer>));\n}\n\nfunction concat(a: Uint8Array, b: Uint8Array): Uint8Array {\n const out = new Uint8Array(a.length + b.length);\n out.set(a, 0);\n out.set(b, a.length);\n return out;\n}\n\n/** Equal, compared whole: a mismatch costs the same as a match. */\nexport function sameBytes(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n let d = 0;\n for (let i = 0; i < a.length; i++) d |= (a[i] as number) ^ (b[i] as number);\n return d === 0;\n}\n\n/** A DER ECDSA signature (what WebAuthn returns) as the 64-byte r\u2016s WebCrypto verifies.\n * Strict: every length must match the bytes, so one signature has one encoding (a\n * P-256 signature is at most 72 bytes, so the short length form is the only one). */\nexport function derToRaw(der: Uint8Array): Uint8Array | null {\n if (der[0] !== 0x30 || der.length < 8 || der[1] !== der.length - 2) return null;\n let i = 2;\n const part = (): Uint8Array | null => {\n if (der[i] !== 0x02) return null;\n const len = der[i + 1] as number;\n if (len < 1 || len > 33) return null;\n let v = der.slice(i + 2, i + 2 + len);\n if (v.length !== len) return null;\n // Minimal integers only: no needless leading zero, and never negative.\n if ((v[0] as number) & 0x80) return null;\n if (len > 1 && v[0] === 0 && !((v[1] as number) & 0x80)) return null;\n i += 2 + len;\n while (v.length > 32 && v[0] === 0) v = v.slice(1);\n if (v.length > 32) return null;\n const out = new Uint8Array(32);\n out.set(v, 32 - v.length);\n return out;\n };\n const r = part();\n const s = part();\n return r && s && i === der.length ? concat(r, s) : null;\n}\n\nconst u8 = (b: Uint8Array) => b as Uint8Array<ArrayBuffer>;\n/** WebCrypto's key type, named without the DOM lib (Workers, Node and browsers all have it). */\ntype CryptoKey = Awaited<ReturnType<typeof crypto.subtle.importKey>>;\n\nasync function importKey(key: TrustedKey): Promise<CryptoKey | null> {\n const spki = fromB64url(key.spki);\n if (!spki?.length) return null;\n try {\n if (key.alg === ES256)\n return await crypto.subtle.importKey(\"spki\", u8(spki), { name: \"ECDSA\", namedCurve: \"P-256\" }, false, [\"verify\"]);\n if (key.alg === RS256) {\n const k = await crypto.subtle.importKey(\"spki\", u8(spki), { name: \"RSASSA-PKCS1-v1_5\", hash: \"SHA-256\" }, false, [\n \"verify\",\n ]);\n const bits = (k.algorithm as { modulusLength?: number }).modulusLength as number; // every RSA key has one\n return bits >= 2048 ? k : null;\n }\n } catch {}\n return null;\n}\n\nconst PASSKEY_FIELDS = new Set([\"id\", \"alg\", \"spki\"]);\nconst DEVICE_FIELDS = new Set([\"id\", \"alg\", \"spki\", \"type\", \"store\", \"uv\"]);\n/** The only fields a device signature has (`str` only in a grant). */\nexport const DEVICE_SIG_FIELDS = [\"cred\", \"sig\", \"str\"] as const;\n\n/** A device key's id: the SHA-256 of its SPKI bytes, base64url without padding. */\nexport async function deviceKeyId(spki: string): Promise<string> {\n return b64url(await sha256(fromB64url(spki) ?? new Uint8Array()));\n}\n\n/** A device key's declared rules, ignoring any bookkeeping fields a store keeps beside\n * them (a name, when it was added): ES256, hardware, verified each use, named by its\n * SPKI's hash, and a P-256 key WebCrypto can import. */\nasync function deviceRecordValid(key: TrustedKey): Promise<boolean> {\n if (key.type !== \"device\" || key.alg !== ES256 || key.store !== \"hardware\" || key.uv !== \"each-use\") return false;\n if (typeof key.id !== \"string\" || !SHA256_RE.test(key.id) || key.id !== (await deviceKeyId(key.spki))) return false;\n return !!(await importKey(key));\n}\n\n/** Can this key be enrolled at all? A machine refuses to trust a key it could never\n * verify with. The key's fields are closed: a passkey is `id`, `alg`, `spki` and\n * nothing else; a device key adds exactly `type`, `store` and `uv` (CONTROL.md \u00A72),\n * so a kind added later is refused by every machine that predates it. */\nexport async function keyUsable(key: TrustedKey): Promise<boolean> {\n if (!key || typeof key !== \"object\" || Array.isArray(key)) return false;\n const fields = Object.keys(key).filter((k) => (key as unknown as Record<string, unknown>)[k] !== undefined);\n if (\"type\" in key && key.type !== undefined) {\n if (!fields.every((f) => DEVICE_FIELDS.has(f))) return false;\n return deviceRecordValid(key);\n }\n if (!fields.every((f) => PASSKEY_FIELDS.has(f))) return false;\n return (\n typeof key.id === \"string\" &&\n /^[A-Za-z0-9_-]{16,1400}$/.test(key.id) &&\n (key.alg === ES256 || key.alg === RS256) &&\n !!(await importKey(key))\n );\n}\n\n// --- WebAuthn -------------------------------------------------------------------\n\n/** A WebAuthn origin a key may have been used from: https on the rp id or a subdomain;\n * for rp id `localhost`, plain http on any port too. A bare origin, as a browser writes it. */\nexport function originAllowed(origin: unknown, rpId: string): boolean {\n if (typeof origin !== \"string\" || !rpId) return false;\n let u: URL;\n try {\n u = new URL(origin);\n } catch {\n return false;\n }\n if (u.origin !== origin) return false;\n if (u.hostname !== rpId && !u.hostname.endsWith(`.${rpId}`)) return false;\n if (u.protocol === \"https:\") return true;\n return u.protocol === \"http:\" && rpId === \"localhost\";\n}\n\ntype Check = { ok: true } | { ok: false; why: string };\nconst no = (why: string): Check => ({ ok: false, why });\n\n// --- device keys (CONTROL.md \u00A73.2) ------------------------------------------------\n\n/** The first line of every message a device key signs. */\nexport const DEVICE_CONTEXT = \"sessionpipe.device.v1\";\n/** What a device signature is for: one use can never answer for another. */\nexport type DevicePurpose = \"grant\" | \"confirm\" | \"pair\";\n\n/** The exact bytes a device key signs: the context, the purpose, the rp id as the\n * machine recorded it, and the base64url (no padding) of the 32-byte challenge a\n * passkey would sign for that purpose, joined by single line feeds, none at the end. */\nexport function deviceMessage(purpose: DevicePurpose, rpId: string, challenge: Uint8Array): Uint8Array {\n return enc.encode(`${DEVICE_CONTEXT}\\n${purpose}\\n${rpId}\\n${b64url(challenge)}`);\n}\n\n/** One device signature over `challenge` for `purpose`, made with the device key `key`. */\nasync function verifyDeviceSignature(\n challenge: Uint8Array,\n a: object,\n key: TrustedKey,\n rpId: string,\n purpose: DevicePurpose,\n): Promise<Check> {\n const sig = a as Record<string, unknown>;\n for (const f of Object.keys(sig)) {\n if (f === \"str\" && purpose === \"grant\") continue;\n if (f !== \"cred\" && f !== \"sig\") return no(`a device key's signature carries no ${f}`);\n }\n if (sig.cred !== key.id) return no(\"signed with a key this machine doesn't trust\");\n if (!(await deviceRecordValid(key))) return no(\"a trusted key is unusable\");\n const raw = fromB64url(sig.sig);\n if (raw?.length !== 64) return no(\"a device key's signature is 64 bytes, r\u2016s\");\n const pub = await importKey(key);\n /* v8 ignore next -- deviceRecordValid imported it already */\n if (!pub) return no(\"a trusted key is unusable\");\n let good = false;\n try {\n good = await crypto.subtle.verify(\n { name: \"ECDSA\", hash: \"SHA-256\" },\n pub,\n u8(raw),\n u8(deviceMessage(purpose, rpId, challenge)),\n );\n } catch {\n /* v8 ignore next -- WebCrypto answers false for a bad signature; a throw is a broken runtime */\n good = false;\n }\n return good ? { ok: true } : no(\"the device key's signature doesn't verify\");\n}\n\n/** One signature over `challenge` by `key`, chosen from the machine's own record of\n * the key, never from the signature: a device key's signature for `purpose`\n * (CONTROL.md \u00A73.2), or a passkey assertion made by a verified person. */\nexport async function verifyAssertion(\n challenge: Uint8Array,\n a: Assertion | DeviceSignature | null | undefined,\n key: TrustedKey,\n rpId: string,\n purpose: DevicePurpose,\n): Promise<Check> {\n if (!a || typeof a !== \"object\") return no(\"no passkey signature\");\n if (key.type !== undefined) {\n if (key.type !== \"device\") return no(\"a trusted key is unusable\");\n return verifyDeviceSignature(challenge, a, key, rpId, purpose);\n }\n return verifyPasskeyAssertion(challenge, a as Assertion, key, rpId);\n}\n\nasync function verifyPasskeyAssertion(\n challenge: Uint8Array,\n a: Assertion,\n key: TrustedKey,\n rpId: string,\n): Promise<Check> {\n if (a.cred !== key.id) return no(\"signed with a key this machine doesn't trust\");\n const cdBytes = fromB64url(a.cd);\n const ad = fromB64url(a.ad);\n const sig = fromB64url(a.sig);\n if (!cdBytes || !ad || !sig?.length) return no(\"the passkey signature is malformed\");\n let cd: { type?: unknown; challenge?: unknown; origin?: unknown; crossOrigin?: unknown };\n try {\n cd = JSON.parse(dec.decode(cdBytes));\n } catch {\n return no(\"the passkey signature is malformed\");\n }\n if (!cd || typeof cd !== \"object\") return no(\"the passkey signature is malformed\");\n if (cd.type !== \"webauthn.get\") return no(\"the passkey answered a different question\");\n if (cd.challenge !== b64url(challenge)) return no(\"the passkey signed something else\");\n if (!originAllowed(cd.origin, rpId)) return no(`the passkey was used on another site, not ${rpId}`);\n if (cd.crossOrigin === true) return no(\"the passkey was used inside another site's frame\");\n if (ad.length < 37) return no(\"the passkey signature is malformed\");\n if (!sameBytes(ad.slice(0, 32), await sha256(rpId))) return no(`the passkey belongs to another site, not ${rpId}`);\n const flags = ad[32] as number;\n if (!(flags & UP) || !(flags & UV)) return no(\"the passkey signed without verifying the person\");\n const pub = await importKey(key);\n if (!pub) return no(\"a trusted key is unusable\");\n const signed = concat(ad, await sha256(cdBytes));\n let good = false;\n try {\n if (key.alg === ES256) {\n const raw = derToRaw(sig);\n good = !!raw && (await crypto.subtle.verify({ name: \"ECDSA\", hash: \"SHA-256\" }, pub, u8(raw), u8(signed)));\n } else {\n good = await crypto.subtle.verify({ name: \"RSASSA-PKCS1-v1_5\" }, pub, u8(sig), u8(signed));\n }\n } catch {\n good = false;\n }\n return good ? { ok: true } : no(\"the passkey signature doesn't verify\");\n}\n\n/** What a passkey signs to enroll on a machine: bound to that machine and that pairing code. */\nexport function pairChallenge(machine: string, code: string): Promise<Uint8Array> {\n return sha256(`sessionpipe.pair:${machine}:${code}`);\n}\n\n/** The enrollment proof: the new key's assertion over pairChallenge(machine, code).\n * Only after this verifies does the key go into the machine's trust store. */\nexport async function verifyEnrollment(o: {\n machine: string;\n code: string;\n key: TrustedKey;\n proof: Assertion | DeviceSignature;\n rpId: string;\n}): Promise<Check> {\n if (!(await keyUsable(o.key))) return no(\"the key is in a shape this machine can't use\");\n return verifyAssertion(await pairChallenge(o.machine, o.code), o.proof, o.key, o.rpId, \"pair\");\n}\n\n// --- the command ------------------------------------------------------------------\n\ntype Parsed = { ok: true; cmd: ControlCommand } | { ok: false; code: RefusalCode; why: string };\nconst bad = (code: RefusalCode, why: string): { ok: false; code: RefusalCode; why: string } => ({\n ok: false,\n code,\n why,\n});\nconst isNum = (n: unknown): n is number => typeof n === \"number\" && Number.isFinite(n);\n\n/** The session id rule per harness (ADAPTERS.md): Claude Code ids are UUIDs. */\nexport function sessionIdOk(harness: string, id: string): boolean {\n if (!SESSION_ID_RE.test(id)) return false;\n if (harness === \"claude-code\") return UUID_RE.test(id);\n return true;\n}\n\n/** Split `<harness>:<id>`; null when either half breaks its rule. */\nexport function parseSessionRef(ref: unknown): { harness: string; id: string } | null {\n if (typeof ref !== \"string\") return null;\n const i = ref.indexOf(\":\");\n if (i <= 0) return null;\n const harness = ref.slice(0, i);\n const id = ref.slice(i + 1);\n return HARNESS_RE.test(harness) && sessionIdOk(harness, id) ? { harness, id } : null;\n}\n\n/** Read the command's fields from the exact string. Structure only; no clock, no keys. */\nexport function parseCommand(str: unknown): Parsed {\n if (typeof str !== \"string\" || !str || str.length > CMD_MAX)\n return bad(\"malformed\", \"the command is missing or too long\");\n let c: Record<string, unknown>;\n try {\n c = JSON.parse(str);\n } catch {\n return bad(\"malformed\", \"the command is not JSON\");\n }\n if (!c || typeof c !== \"object\" || Array.isArray(c)) return bad(\"malformed\", \"the command is not an object\");\n if (c.v !== 1 || c.t !== COMMAND_TYPE) return bad(\"not_a_command\", \"not a sessionpipe control command\");\n if (typeof c.machine !== \"string\" || !MACHINE_RE.test(c.machine)) return bad(\"malformed\", \"which machine?\");\n if (!CONTROL_KINDS.includes(c.kind as ControlKind))\n return bad(\"unknown_kind\", \"the command asks for something unknown\");\n const ref = parseSessionRef(c.session);\n if (!ref) return bad(\"bad_session\", \"which session?\");\n if (typeof c.nonce !== \"string\" || !NONCE_RE.test(c.nonce))\n return bad(\"bad_nonce\", \"the nonce is missing or malformed\");\n if (!isNum(c.iat) || !Number.isInteger(c.iat) || c.iat <= 0)\n return bad(\"malformed\", \"the command has no signing time\");\n const has = (k: string) => c[k] !== undefined;\n const text = () => typeof c.text === \"string\" && c.text.trim().length > 0 && c.text.length <= TEXT_MAX;\n // A handoff's reply_to (CONTROL.md \u00A76): a session ref by the adapter's rule, never the\n // command's own session. Whether that session is on this machine is the daemon's to say.\n const replyToProblem = (): string | null => {\n if (!has(\"reply_to\")) return null;\n if (!parseSessionRef(c.reply_to)) return \"a reply_to names a session on this machine, <harness>:<id>\";\n if (c.reply_to === c.session) return \"a reply_to can't name the command's own session\";\n return null;\n };\n switch (c.kind as ControlKind) {\n case \"prompt\": {\n if (!text()) return bad(\"bad_fields\", `a prompt needs text, at most ${TEXT_MAX} characters`);\n if (has(\"for\") || has(\"decision\") || has(\"cwd\"))\n return bad(\"bad_fields\", \"a prompt carries text, files and a reply_to\");\n if (has(\"files\")) {\n const why = filesProblem(c.files);\n if (why) return bad(\"bad_fields\", why);\n }\n const why = replyToProblem();\n if (why) return bad(\"bad_fields\", why);\n break;\n }\n case \"start\": {\n if (!text()) return bad(\"bad_fields\", `a new session needs text, at most ${TEXT_MAX} characters`);\n if (typeof c.cwd !== \"string\" || !(isAbsolute(c.cwd) || HOME_RELATIVE_RE.test(c.cwd)) || c.cwd.length > 1024)\n return bad(\"bad_fields\", \"a new session needs an absolute folder, or one under ~\");\n if (has(\"for\") || has(\"decision\"))\n return bad(\"bad_fields\", \"a start carries text, cwd, files, a permission, a model and a reply_to\");\n if (has(\"files\")) {\n const why = filesProblem(c.files);\n if (why) return bad(\"bad_fields\", why);\n }\n const why = replyToProblem();\n if (why) return bad(\"bad_fields\", why);\n if (has(\"permission\") && levelRank(c.permission) < 0)\n return bad(\"bad_fields\", `a permission is one of ${START_PERMISSIONS.join(\", \")}`);\n if (has(\"model\") && (typeof c.model !== \"string\" || c.model.length > MODEL_MAX || !MODEL_RE.test(c.model)))\n return bad(\"bad_fields\", `a model is 1 to ${MODEL_MAX} letters, digits and ._:/@[]-, not starting with a dash`);\n break;\n }\n case \"permission.answer\":\n if (typeof c.for !== \"string\" || !c.for || c.for.length > 128) return bad(\"bad_fields\", \"which attention?\");\n if (c.decision !== \"allow\" && c.decision !== \"deny\") return bad(\"bad_fields\", \"allow or deny?\");\n if (has(\"text\") || has(\"cwd\")) return bad(\"bad_fields\", \"a permission answer carries for, decision and note\");\n break;\n case \"cancel\":\n if (has(\"text\") || has(\"for\") || has(\"decision\") || has(\"cwd\"))\n return bad(\"bad_fields\", \"a cancel carries nothing\");\n break;\n case \"key.add\": {\n // A device the machine already trusts vouches for a new one (CONTROL.md \u00A72): the\n // new key's id, algorithm and public key (and, for a device key, its type, store\n // and uv) are inside the signed bytes.\n if (c.session !== KEY_STORE_SESSION) return bad(\"bad_session\", `a key.add names ${KEY_STORE_SESSION}`);\n const k = c.key as Record<string, unknown> | undefined;\n const b64 = (v: unknown, min: number, max: number) =>\n typeof v === \"string\" && v.length >= min && v.length <= max && /^[A-Za-z0-9_-]+$/.test(v);\n if (\n !k ||\n typeof k !== \"object\" ||\n Array.isArray(k) ||\n !b64(k.id, 16, 1400) ||\n (k.alg !== -7 && k.alg !== -257) ||\n !b64(k.spki, 40, 2000)\n )\n return bad(\"bad_fields\", \"a key.add carries the key: id, alg (-7 or -257) and spki\");\n if (k.type !== undefined) {\n if (k.type !== \"device\") return bad(\"bad_fields\", \"a key's type is device, or absent for a passkey\");\n if (k.alg !== ES256 || k.store !== \"hardware\" || k.uv !== \"each-use\" || !SHA256_RE.test(k.id as string))\n return bad(\"bad_fields\", \"a device key is ES256, store hardware, uv each-use, named by its SPKI's hash\");\n if (Object.keys(k).some((f) => !DEVICE_FIELDS.has(f)))\n return bad(\"bad_fields\", \"a device key carries id, alg, spki, type, store and uv\");\n } else if (Object.keys(k).some((f) => !PASSKEY_FIELDS.has(f)))\n return bad(\"bad_fields\", \"a passkey carries id, alg and spki: store and uv belong to a device key\");\n if (has(\"name\") && (typeof c.name !== \"string\" || !c.name.trim() || c.name.length > 80))\n return bad(\"bad_fields\", \"a device name is text, at most 80 characters\");\n if (has(\"text\") || has(\"for\") || has(\"decision\") || has(\"cwd\"))\n return bad(\"bad_fields\", \"a key.add carries the key and a name\");\n break;\n }\n case \"pool\": {\n if (c.session !== POOL_SESSION) return bad(\"bad_session\", `a pool command names ${POOL_SESSION}`);\n const why = poolProblem(c);\n if (why) return bad(\"bad_fields\", why);\n break;\n }\n }\n if (c.kind !== \"key.add\" && (has(\"key\") || has(\"name\"))) return bad(\"bad_fields\", \"only a key.add carries a key\");\n if (c.kind !== \"pool\" && POOL_FIELDS.some(has)) return bad(\"bad_fields\", \"only a pool command carries pool fields\");\n if (c.kind !== \"start\" && c.kind !== \"prompt\" && has(\"files\"))\n return bad(\"bad_fields\", \"only a start or a prompt carries files\");\n if (c.kind !== \"start\" && c.kind !== \"prompt\" && has(\"reply_to\"))\n return bad(\"bad_fields\", \"only a start or a prompt carries a reply_to\");\n if (c.kind !== \"start\" && (has(\"permission\") || has(\"model\")))\n return bad(\"bad_fields\", \"only a start carries a model or a permission\");\n const allowed = FIELDS[c.kind as ControlKind];\n for (const k of Object.keys(c))\n if (!COMMON_FIELDS.includes(k) && !allowed.includes(k))\n return bad(\"bad_fields\", `the command carries a field its kind doesn't have: ${k.slice(0, 40)}`);\n if (has(\"note\") && (typeof c.note !== \"string\" || c.note.length > NOTE_MAX))\n return bad(\"bad_fields\", `a note is text, at most ${NOTE_MAX} characters`);\n return { ok: true, cmd: c as unknown as ControlCommand };\n}\n\n/** Why a `pool` command's fields break the rules (CONTROL.md \u00A76 `pool`), or null when\n * they don't. Each action takes exactly its own fields: an account where the action\n * names one, the whole order for `order`, the pasted code for `login.complete`. The\n * machine still checks the account exists and the order is complete (`refused`). */\nexport function poolProblem(c: Record<string, unknown>): string | null {\n const action = c.pool_action;\n if (!POOL_ACTIONS.includes(action as PoolAction))\n return `a pool command's action is one of ${POOL_ACTIONS.join(\", \")}`;\n const a = action as PoolAction;\n const account = c.pool_account !== undefined;\n const order = c.pool_order !== undefined;\n const code = c.pool_code !== undefined;\n const needsAccount = a === \"enable\" || a === \"disable\" || a === \"remove\" || a === \"login.complete\";\n if (needsAccount && !account) return `${a} names an account`;\n if (account && !needsAccount && a !== \"refresh\") return `${a} names no account`;\n if (account && (typeof c.pool_account !== \"string\" || !POOL_ACCOUNT_RE.test(c.pool_account)))\n return \"an account id is 1 to 128 letters, digits and ._:-\";\n if (order !== (a === \"order\")) return a === \"order\" ? \"order carries the whole order\" : `${a} carries no order`;\n if (order) {\n const o = c.pool_order;\n if (!Array.isArray(o) || o.length < 1 || o.length > POOL_ORDER_MAX)\n return `an order is a list of 1 to ${POOL_ORDER_MAX} account ids`;\n if (!o.every((id) => typeof id === \"string\" && POOL_ACCOUNT_RE.test(id)))\n return \"an account id is 1 to 128 letters, digits and ._:-\";\n if (new Set(o).size !== o.length) return \"an order names each account once\";\n }\n if (code !== (a === \"login.complete\"))\n return a === \"login.complete\" ? \"login.complete carries the pasted code\" : `${a} carries no code`;\n if (\n code &&\n (typeof c.pool_code !== \"string\" ||\n !c.pool_code ||\n c.pool_code.length > POOL_CODE_MAX ||\n /[\\r\\n]/.test(c.pool_code))\n )\n return `a code is 1 to ${POOL_CODE_MAX} characters on one line`;\n return null;\n}\n\n/** Why a start's or a prompt's `files` breaks the rules (CONTROL.md \u00A73.1), or null when it doesn't. */\nexport function filesProblem(files: unknown): string | null {\n if (!Array.isArray(files) || files.length < 1 || files.length > FILES_MAX)\n return `files is a list of 1 to ${FILES_MAX} files`;\n const seen = new Set<string>();\n let total = 0;\n for (const f of files as unknown[]) {\n if (!f || typeof f !== \"object\" || Array.isArray(f)) return \"each file is an object\";\n const { name, type, size, sha256: hash } = f as Record<string, unknown>;\n const why = fileNameProblem(name);\n if (why) return why;\n const key = (name as string).normalize(\"NFC\").toLowerCase();\n if (seen.has(key)) return \"two files have the same name\";\n seen.add(key);\n if (typeof type !== \"string\" || type.length > FILE_TYPE_MAX || !MEDIA_TYPE_RE.test(type))\n return \"a file's type isn't a media type\";\n if (!isNum(size) || !Number.isInteger(size) || size < 1 || size > FILE_SIZE_MAX)\n return `a file is 1 byte to ${FILE_SIZE_MAX / 1_048_576} MiB`;\n total += size;\n if (typeof hash !== \"string\" || !SHA256_RE.test(hash)) return \"a file's sha256 isn't 43 base64url characters\";\n }\n if (total > FILES_TOTAL_MAX) return `the files add up to more than ${FILES_TOTAL_MAX / 1_048_576} MiB`;\n return null;\n}\n\n/** A file name the machine can write inside the session's folder and nowhere else. */\nfunction fileNameProblem(name: unknown): string | null {\n if (typeof name !== \"string\") return \"a file has no name\";\n const n = name.normalize(\"NFC\");\n if (n.length < 1 || n.length > FILE_NAME_MAX) return `a file name is 1 to ${FILE_NAME_MAX} characters`;\n if (BAD_NAME_CHAR_RE.test(n)) return \"a file name has a slash, a backslash or a control character\";\n if (n.startsWith(\".\")) return \"a file name can't start with a dot\";\n return null;\n}\n\n/** A `start`'s `cwd` under the machine's home: `~` alone, or `~/\u2026` (`~\\\u2026`). Never `~user`. */\nexport const HOME_RELATIVE_RE = /^~(?:[\\\\/]|$)/;\n\nfunction isAbsolute(p: string): boolean {\n return p.startsWith(\"/\") || /^[A-Za-z]:[\\\\/]/.test(p);\n}\n\n/** The grant's fields, read from its exact string. */\nexport function parseGrant(str: unknown, rpId: string): { ok: true; grant: Grant } | { ok: false; why: string } {\n if (typeof str !== \"string\" || !str || str.length > GRANT_STR_MAX)\n return { ok: false, why: \"the grant is missing or too long\" };\n let g: Record<string, unknown>;\n try {\n g = JSON.parse(str);\n } catch {\n return { ok: false, why: \"the grant is not JSON\" };\n }\n if (!g || typeof g !== \"object\" || Array.isArray(g)) return { ok: false, why: \"the grant is not an object\" };\n if (g.v !== 1 || g.t !== GRANT_TYPE) return { ok: false, why: \"not a sessionpipe grant\" };\n if (g.rp !== rpId) return { ok: false, why: `the grant is for another site, not ${rpId}` };\n if (typeof g.pub !== \"string\" || !fromB64url(g.pub)?.length || !isNum(g.iat) || !isNum(g.exp))\n return { ok: false, why: \"the grant is missing parts\" };\n if (g.exp <= g.iat) return { ok: false, why: \"the grant ends before it starts\" };\n if (g.exp - g.iat > GRANT_MAX_MS + SKEW_MS) return { ok: false, why: \"the grant lasts longer than a day\" };\n return { ok: true, grant: g as unknown as Grant };\n}\n\nasync function verifyCsig(cmd: string, csig: unknown, pub: string): Promise<boolean> {\n const sig = fromB64url(csig);\n const spki = fromB64url(pub);\n if (sig?.length !== 64 || !spki) return false;\n try {\n const key = await crypto.subtle.importKey(\"spki\", u8(spki), { name: \"ECDSA\", namedCurve: \"P-256\" }, false, [\n \"verify\",\n ]);\n return await crypto.subtle.verify({ name: \"ECDSA\", hash: \"SHA-256\" }, key, u8(sig), u8(enc.encode(cmd)));\n } catch {\n return false;\n }\n}\n\n/**\n * Is this a command this machine may run? The checks, in the spec's order\n * (CONTROL.md \u00A75): the exact bytes parse; the command is for this machine; it is\n * signed \u2014 a grant from a trusted key plus the day key's signature, or a confirm\n * over sha256(cmd); it was signed within the age window; its nonce is new.\n * The folder and open-attention checks that follow are the daemon's (they read\n * the machine, not the command).\n */\nexport async function verifyCommand(s: SignedCommand | null | undefined, o: VerifyOptions): Promise<Verdict> {\n if (!s || typeof s !== \"object\") return bad(\"malformed\", \"nothing to verify\");\n const p = parseCommand(s.cmd);\n if (!p.ok) return p;\n const c = p.cmd;\n if (c.machine !== o.machine) return bad(\"wrong_machine\", \"the command is for another machine\");\n\n let via: \"grant\" | \"confirm\" | null = null;\n let keyId = \"\";\n let refusal: { code: RefusalCode; why: string } | null = null;\n if (s.grant && s.csig) {\n const r = await checkGrantPath(s, c, o);\n if (r.ok) {\n via = \"grant\";\n keyId = r.key;\n } else refusal = r;\n }\n if (!via && s.confirm) {\n const key = o.trusted.find((k) => k && k.id === s.confirm?.cred);\n if (!key) refusal = { code: \"untrusted_key\", why: \"confirmed with a key this machine doesn't trust\" };\n else {\n const a = await verifyAssertion(await sha256(s.cmd), s.confirm, key, o.rpId, \"confirm\");\n if (a.ok) {\n via = \"confirm\";\n keyId = key.id;\n } else refusal = { code: \"bad_assertion\", why: a.why };\n }\n }\n if (!via) {\n if (refusal) return bad(refusal.code, refusal.why);\n return bad(\"unsigned\", \"the command isn't signed\");\n }\n // The key a key.add brings must be one this machine could ever verify with: an\n // SPKI it can import for that alg, and every rule of a device key (CONTROL.md \u00A72).\n if (c.kind === \"key.add\" && !(await keyUsable(c.key as TrustedKey)))\n return bad(\"bad_fields\", \"the key a key.add brings is in a shape this machine can't use\");\n if (c.iat > o.now + SKEW_MS) return bad(\"future\", \"the command is dated in the future (is this clock right?)\");\n if (o.now - c.iat > (o.maxAgeMs ?? MACHINE_MAX_AGE_MS)) return bad(\"too_old\", \"the command was signed too long ago\");\n if (o.seenNonce) {\n let seen: boolean;\n try {\n seen = await o.seenNonce(c.nonce);\n } catch {\n return bad(\"nonce_store\", \"the nonce couldn't be recorded, so the command didn't run\");\n }\n if (seen) return bad(\"replay\", \"this command was already used once\");\n }\n return { ok: true, cmd: c, via, key: keyId };\n}\n\nasync function checkGrantPath(\n s: SignedCommand,\n c: ControlCommand,\n o: VerifyOptions,\n): Promise<{ ok: true; key: string } | { ok: false; code: RefusalCode; why: string }> {\n const bundle = s.grant as GrantBundle | DeviceGrant;\n const g = parseGrant(bundle.str, o.rpId);\n if (!g.ok) return bad(\"bad_grant\", g.why);\n const key = o.trusted.find((k) => k && k.id === bundle.cred);\n if (!key) return bad(\"untrusted_key\", \"the grant was signed with a key this machine doesn't trust\");\n const a = await verifyAssertion(await sha256(bundle.str), bundle, key, o.rpId, \"grant\");\n if (!a.ok) return bad(\"bad_assertion\", a.why);\n if (!(await verifyCsig(s.cmd, s.csig, g.grant.pub)))\n return bad(\"bad_csig\", \"the command's signature doesn't verify \u2014 it was changed after it was signed\");\n if (c.iat < g.grant.iat - SKEW_MS || c.iat > g.grant.exp)\n return bad(\"outside_grant\", \"the command was signed outside its grant's window\");\n return { ok: true, key: key.id };\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Codex CLI (OpenAI) \u2014 docs: https://learn.chatgpt.com/docs/hooks\n// Config: ~/.codex/hooks.json (Claude-shaped, 12 events, timeouts in seconds) AND\n// `notify` at the top level of config.toml as the fallback when hooks are off or\n// not yet trusted (non-managed hooks need a /hooks review in the CLI). An existing\n// notify (Codex Computer Use) is chained through --previous-notify, never replaced.\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { homeDir } from \"../paths.js\";\nimport { ends, parseLines, recentFiles, scanLines } from \"../readers/files.js\";\nimport { codexInjected, readCodex } from \"../readers/transcripts.js\";\nimport type { DataOf } from \"../types.js\";\nimport { installClaudeShaped, installedClaudeShaped, uninstallClaudeShaped, writeBack } from \"./claude-shaped.js\";\nimport * as toml from \"./codex-config.js\";\nimport { codexReplayBoundary, codexSubagent } from \"./codex-rollout.js\";\nimport type {\n Adapter,\n BackfillRow,\n HookCommand,\n HookInput,\n HookResult,\n InstalledReport,\n InstallOptions,\n InstallReport,\n SessionFacts,\n} from \"./types.js\";\n\nconst NAME = \"codex\";\nexport const CODEX_EVENTS = [\n \"SessionStart\",\n \"UserPromptSubmit\",\n \"PreToolUse\",\n \"PostToolUse\",\n \"PermissionRequest\",\n \"Stop\",\n \"Interrupt\",\n \"SubagentStart\",\n \"SubagentStop\",\n \"PreCompact\",\n \"PostCompact\",\n \"SessionEnd\",\n] as const;\nconst OPTS = { matcherFor: (e: string) => (/Tool|Permission/.test(e) ? \"\" : undefined), timeout: 5 };\nconst eventsFor = (lean?: boolean) => (lean ? CODEX_EVENTS.filter((e) => e !== \"PreToolUse\") : [...CODEX_EVENTS]);\n\nconst codexHome = (env: NodeJS.ProcessEnv) => env.CODEX_HOME || path.join(homeDir(), \".codex\");\nconst hooksFile = (env: NodeJS.ProcessEnv) => path.join(codexHome(env), \"hooks.json\");\nconst configFile = (env: NodeJS.ProcessEnv) => path.join(codexHome(env), \"config.toml\");\nconst sessionsDir = (env: NodeJS.ProcessEnv) => path.join(codexHome(env), \"sessions\");\n\nconst validTool = (n: unknown): n is string => typeof n === \"string\" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);\nconst endReason = (r: unknown): \"clear\" | \"logout\" | \"exit\" | \"other\" =>\n r === \"clear\" || r === \"logout\" || r === \"exit\" ? r : \"other\";\n\n/** What config.toml without our notify line means, said with every refusal of it. */\nconst UNTIL_APPROVED =\n \"Codex will report nothing until you approve the hooks: start `codex` and accept the hook review\";\n\nfunction installNotify(cmd: HookCommand, env: NodeJS.ProcessEnv) {\n const file = configFile(env);\n let text = \"\";\n let existed = true;\n try {\n text = readFileSync(file, \"utf8\");\n } catch (e) {\n // Absent only when the system says so: a file that is there but can't be read (0200,\n // root's) is still the person's, never `created` for uninstall to delete.\n existed = (e as NodeJS.ErrnoException).code !== \"ENOENT\";\n }\n const argv = cmd([NAME, \"notify\"]).argv;\n const line = `notify = ${JSON.stringify(argv)}`;\n const n = toml.readNotify(text);\n /** The file written, with this note; or the refusal's report in its place. */\n const write = (next: string, note: string): InstallReport => {\n const bad = writeBack(file, next, existed);\n return bad ? { ...bad, note: `${bad.note}. ${UNTIL_APPROVED}` } : { file, changed: true, note };\n };\n if (n && !n.other) {\n if (!n.misplaced && JSON.stringify(n.argv) === JSON.stringify(argv))\n return { file, changed: false, note: \"notify current\" };\n return write(\n toml.withNotify(text, line),\n n.misplaced\n ? \"notify moved to the top level (it sat inside a [table], where Codex never reads it)\"\n : \"notify updated\",\n );\n }\n if (n?.other) {\n const prev = toml.previousNotify(n.argv);\n if (toml.isComputerUse(n.argv)) {\n if (prev && toml.OURS.test(JSON.stringify(prev))) {\n if (JSON.stringify(prev) === JSON.stringify(argv))\n return { file, changed: false, note: \"notify current (after Codex Computer Use)\" };\n }\n if (!prev || toml.OURS.test(JSON.stringify(prev)))\n return write(\n toml.replaceNotify(text, n, `notify = ${toml.withPreviousRaw(n.raw, argv)}`),\n \"chained after Codex Computer Use (--previous-notify)\",\n );\n }\n return {\n file,\n changed: false,\n skipped: true,\n note: `notify belongs to ${(n.argv?.[0] ?? n.raw).replace(/^.*\\//, \"\").slice(0, 40)}; left alone. ${UNTIL_APPROVED}`,\n };\n }\n return write(toml.withNotify(text, line), \"notify set at the top level\");\n}\n\nfunction uninstallNotify(env: NodeJS.ProcessEnv): InstallReport {\n const file = configFile(env);\n let text: string;\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return { file, changed: false };\n }\n const n = toml.readNotify(text);\n if (!n) return { file, changed: false };\n if (!n.other) return writeBack(file, toml.withoutNotify(text)) ?? { file, changed: true };\n const prev = toml.previousNotify(n.argv);\n if (toml.isComputerUse(n.argv) && prev && toml.OURS.test(JSON.stringify(prev)))\n return (\n writeBack(file, toml.replaceNotify(text, n, `notify = ${toml.stripPreviousRaw(n.raw)}`)) ?? {\n file,\n changed: true,\n }\n );\n return { file, changed: false };\n}\n\n/** The rollout file of one thread: `<CODEX_HOME>/sessions/YYYY/MM/DD/rollout-<time>-<thread\n * id>.jsonl`, newest day first. Null for an id that isn't a thread id's shape, so a\n * short or odd one never matches some other session's file. */\nexport function findRollout(threadId: string, env: NodeJS.ProcessEnv = process.env): string | null {\n if (!/^[A-Za-z0-9-]{8,128}$/.test(threadId)) return null;\n let best: string | null = null;\n const walk = (dir: string, depth: number) => {\n let names: string[] = [];\n try {\n names = readdirSync(dir);\n } catch {\n return;\n }\n for (const n of names.sort().reverse()) {\n const p = path.join(dir, n);\n if (n.endsWith(`-${threadId}.jsonl`)) {\n best = p;\n return;\n }\n if (depth < 3) {\n walk(p, depth + 1);\n if (best) return;\n }\n }\n };\n walk(sessionsDir(env), 0);\n return best;\n}\n\n/** A rollout's first record, its own session_meta. About 50 KB (it carries the base\n * instructions), so it is read on its own rather than from a sampled head. */\nfunction rolloutMeta(file: string): Record<string, unknown> | null {\n return scanLines(file, (line) => parseLines([line])[0] ?? {});\n}\n\n/** A rollout named for a thread other than `id` (`rollout-<time>-<thread id>.jsonl`).\n * A name of any other shape says nothing, so it is not taken for another thread's. */\nfunction ofAnotherThread(file: string, id: string): boolean {\n const other = threadOf(file);\n return !!other && other !== id.toLowerCase();\n}\n/** The thread a rollout is named for, lowercased; null for a name of any other shape. */\nfunction threadOf(file: string): string | null {\n const m = /-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\\.jsonl(?:\\.zst)?$/i.exec(file);\n return m?.[1] ? m[1].toLowerCase() : null;\n}\n\nexport const codex: Adapter = {\n name: NAME,\n events: CODEX_EVENTS,\n detect: (env = process.env) => existsSync(codexHome(env)),\n configFiles: (env = process.env) => [hooksFile(env), configFile(env)],\n install: (cmd, env = process.env, opts: InstallOptions = {}) => [\n installClaudeShaped(hooksFile(env), NAME, eventsFor(opts.lean), cmd, OPTS),\n installNotify(cmd, env),\n ],\n uninstall: (env = process.env, opts: InstallOptions = {}) => [\n uninstallClaudeShaped(hooksFile(env), CODEX_EVENTS, { created: opts.created?.includes(hooksFile(env)) }),\n uninstallNotify(env),\n ],\n installed: (cmd, env = process.env, opts: InstallOptions = {}) => {\n const out: InstalledReport[] = [\n {\n file: hooksFile(env),\n state: installedClaudeShaped(hooksFile(env), NAME, eventsFor(opts.lean), cmd, OPTS),\n note: \"Codex runs non-managed hooks only after the review in `codex` (/hooks); until then nothing is reported unless the notify fallback is ours\",\n },\n ];\n let text = \"\";\n try {\n text = readFileSync(configFile(env), \"utf8\");\n } catch {}\n const n = toml.readNotify(text);\n const argv = cmd([NAME, \"notify\"]).argv;\n const prev = n ? toml.previousNotify(n.argv) : null;\n const ours = n && (!n.other || (prev && JSON.stringify(prev) === JSON.stringify(argv)));\n out.push({\n file: configFile(env),\n state:\n !n || (n.other && !ours)\n ? \"missing\"\n : n.misplaced\n ? \"misplaced\"\n : JSON.stringify(n.argv) === JSON.stringify(argv) || (prev && JSON.stringify(prev) === JSON.stringify(argv))\n ? \"current\"\n : \"stale\",\n });\n return out;\n },\n\n fromHook(input: HookInput): HookResult | null {\n const event = input.argv[1] ?? \"\";\n let s: Record<string, unknown> = {};\n if (event === \"notify\") {\n // Codex's notify passes one JSON argument: {\"type\":\"agent-turn-complete\",\"thread-id\":\u2026,\"turn-id\":\u2026,\"cwd\":\u2026}\n const raw = input.argv.find((a) => a.startsWith(\"{\")) ?? input.stdin;\n try {\n const j: unknown = raw ? JSON.parse(raw) : {};\n if (j && typeof j === \"object\" && !Array.isArray(j)) s = j as Record<string, unknown>;\n } catch {}\n const id = (s[\"thread-id\"] ?? s.thread_id ?? s[\"session-id\"] ?? s.session_id) as string | undefined;\n if (!id) return null;\n // Notify names the thread whose turn ended. Without a rollout it is an ephemeral\n // thread, kept nowhere (the hooks' `transcript_path: null` below). A subagent's\n // thread belongs to its parent, whose hooks already report it: SubagentStart and\n // SubagentStop name it as `agent_id`. Neither is a session of its own.\n const file = findRollout(String(id), input.env as NodeJS.ProcessEnv);\n if (!file || codexSubagent(rolloutMeta(file))) return null;\n const r: HookResult = {\n session: { id: String(id), cwd: typeof s.cwd === \"string\" ? s.cwd : input.cwd },\n events: [\n {\n type: \"turn.ended\",\n data: { reason: \"stop\", ...(typeof s[\"turn-id\"] === \"string\" ? { turn_id: s[\"turn-id\"] } : {}) },\n harnessEvent: \"notify\",\n },\n ],\n hints: {},\n transcript: file,\n };\n return r;\n }\n try {\n // A stdin that is JSON but not an object (`null`, a number) is no payload.\n const j: unknown = input.stdin ? JSON.parse(input.stdin) : {};\n if (j && typeof j === \"object\" && !Array.isArray(j)) s = j as Record<string, unknown>;\n } catch {}\n const id = (s.session_id ?? s.thread_id ?? s.conversation_id ?? s[\"thread-id\"] ?? s[\"session-id\"]) as\n | string\n | undefined;\n if (!id) return null;\n // `transcript_path: null` is an ephemeral thread (recorded on codex-cli 0.160.0):\n // Codex Desktop's own background threads (ambient suggestions and their safety\n // review) and `codex exec --ephemeral`. Codex keeps nothing of one, so there is no\n // session to name, read or resume.\n if (s.transcript_path === null) return null;\n const session: HookResult[\"session\"] = { id: String(id), cwd: typeof s.cwd === \"string\" ? s.cwd : input.cwd };\n if (typeof s.model === \"string\") session.model = s.model;\n const turn_id = typeof s.turn_id === \"string\" ? s.turn_id : undefined;\n const tool = validTool(s.tool_name) ? s.tool_name : undefined;\n const call_id = typeof s.tool_use_id === \"string\" ? s.tool_use_id : undefined;\n // A subagent's hooks carry the parent's session_id (ADAPTERS.md, Threads): its own\n // id where the payload names it, else the thread its rollout is named for. Without\n // it a subagent's tool calls read as the main thread's (issue #208: a sink that\n // thins subagents' tool events would be sent every one).\n const thread = typeof s.transcript_path === \"string\" ? threadOf(s.transcript_path) : null;\n const agent_id =\n typeof s.agent_id === \"string\" && s.agent_id\n ? s.agent_id\n : typeof s.subagent_id === \"string\" && s.subagent_id\n ? s.subagent_id\n : thread && thread !== String(id).toLowerCase()\n ? thread\n : undefined;\n const ev = (type: string, data: Record<string, unknown>) => ({ type, data: prune(data), harnessEvent: event });\n const events: HookResult[\"events\"] = [];\n switch (event) {\n case \"SessionStart\":\n events.push(\n ev(\"session.started\", {\n source:\n typeof s.source === \"string\" && [\"startup\", \"resume\", \"clear\", \"compact\", \"fork\"].includes(s.source)\n ? s.source\n : \"unknown\",\n }),\n );\n break;\n case \"UserPromptSubmit\":\n events.push(\n ev(\"turn.started\", {\n turn_id,\n prompt_chars: typeof s.prompt === \"string\" ? [...s.prompt].length : undefined,\n }),\n );\n break;\n case \"PreToolUse\":\n if (tool) events.push(ev(\"tool.started\", { tool, call_id, turn_id, agent_id, input: s.tool_input }));\n break;\n case \"PostToolUse\":\n if (tool)\n events.push(\n ev(\"tool.ended\", {\n tool,\n call_id,\n turn_id,\n agent_id,\n ok: true,\n input: s.tool_input,\n output: s.tool_response,\n }),\n );\n break;\n case \"PermissionRequest\":\n events.push(\n ev(\"attention.needed\", {\n attention_id: call_id ?? `perm-${Date.now()}`,\n kind: \"permission\",\n tool,\n agent_id,\n }),\n );\n break;\n case \"Stop\":\n events.push(ev(\"turn.ended\", { turn_id, reason: \"stop\" }));\n break;\n case \"Interrupt\":\n events.push(ev(\"turn.ended\", { turn_id, reason: \"interrupt\" }));\n break;\n case \"SubagentStart\":\n events.push(\n ev(\"subagent.started\", {\n agent_id: String(s.agent_id ?? s.subagent_id ?? \"\"),\n agent_type: typeof s.agent_type === \"string\" ? s.agent_type : undefined,\n }),\n );\n break;\n case \"SubagentStop\":\n events.push(\n ev(\"subagent.ended\", {\n agent_id: String(s.agent_id ?? s.subagent_id ?? \"\"),\n agent_type: typeof s.agent_type === \"string\" ? s.agent_type : undefined,\n }),\n );\n break;\n case \"PreCompact\":\n events.push(ev(\"context.compacted\", { trigger: s.trigger === \"manual\" ? \"manual\" : \"auto\" }));\n break;\n case \"PostCompact\":\n break;\n case \"SessionEnd\":\n events.push(ev(\"session.ended\", { reason: endReason(s.reason) }));\n break;\n default:\n return null;\n }\n const r: HookResult = { session, events, hints: {} };\n // Inside a subagent Codex sends the parent's session_id with the subagent's own\n // rollout (its SubagentStart, its tool calls): that file is not this session's.\n if (typeof s.transcript_path === \"string\" && !ofAnotherThread(s.transcript_path, String(id)))\n r.transcript = s.transcript_path;\n return r;\n },\n\n readTranscript(file, fromLine) {\n let text = \"\";\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return { turns: [], line: fromLine };\n }\n return readCodex(text.split(\"\\n\"), fromLine);\n },\n\n facts(session, transcript): SessionFacts {\n const out: SessionFacts = {};\n if (transcript) Object.assign(out, codexFacts(transcript));\n return out;\n },\n\n backfill(sinceMs, env = process.env): BackfillRow[] {\n const rows: BackfillRow[] = [];\n for (const f of recentFiles(sessionsDir(env), 3, sinceMs, (n) => /^rollout-.*\\.jsonl$/.test(n))) {\n const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\\.jsonl$/i.exec(path.basename(f));\n if (!m?.[1]) continue;\n // A subagent's rollout is part of its parent's session, as in fromHook's notify.\n if (codexSubagent(rolloutMeta(f))) continue;\n const facts = codexFacts(f);\n if (!facts.started_at || !facts.last_at) continue;\n const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;\n rows.push({ session: { id: m[1], ...rest }, started_at, last_at });\n }\n return rows;\n },\n\n limits: (transcript) => codexLimits(transcript),\n};\n\n/** Epoch seconds as RFC 3339; anything that is not a plausible time is no time. */\nconst epochIso = (t: unknown): string | undefined =>\n typeof t === \"number\" && t > 0 && t < 1e11 ? new Date(t * 1000).toISOString() : undefined;\n\nfunction prune<T extends Record<string, unknown>>(o: T): T {\n for (const k of Object.keys(o)) if (o[k] === undefined) delete o[k];\n return o;\n}\n\n/** The account's limits from the last `token_count` that carries them (recorded\n * 2026-10-21, codex-cli 0.163): `rate_limits.primary` / `secondary`, each with\n * `used_percent`, `window_minutes` and `resets_at` in epoch seconds, and `plan_type`.\n * Every turn writes one, so the tail is enough. Codex keeps one snapshot and overwrites\n * it with whichever bucket the server named last (`limit_id`: `codex`, or a model's own\n * such as `codex_other`), so only the account's `codex` bucket is read, never another's\n * windows under its names. */\nexport function codexLimits(file: string): DataOf<\"limits.observed\"> | null {\n const { tail } = ends(file, 0, 256 * 1024);\n for (const r of parseLines(tail).reverse()) {\n const p = r.payload as { type?: unknown; rate_limits?: unknown } | undefined;\n if (r.type !== \"event_msg\" || p?.type !== \"token_count\") continue;\n if (!p.rate_limits || typeof p.rate_limits !== \"object\") continue;\n const rl = p.rate_limits as Record<string, unknown>;\n if (rl.limit_id != null && rl.limit_id !== \"codex\") continue;\n const windows: DataOf<\"limits.observed\">[\"windows\"] = [];\n for (const id of [\"primary\", \"secondary\"]) {\n const w = rl[id] as { used_percent?: unknown; window_minutes?: unknown; resets_at?: unknown } | null;\n if (typeof w?.used_percent !== \"number\" || w.used_percent < 0) continue;\n windows.push(\n prune({\n id,\n used_percent: w.used_percent,\n window_minutes:\n Number.isInteger(w.window_minutes) && Number(w.window_minutes) > 0 ? Number(w.window_minutes) : undefined,\n resets_at: epochIso(w.resets_at),\n }),\n );\n }\n if (!windows.length) return null;\n return prune({ windows, plan: typeof rl.plan_type === \"string\" ? rl.plan_type.slice(0, 64) : undefined });\n }\n return null;\n}\n\n/** Codex keeps no title; its first real ask stands in. A fork's replay is not its ask. */\nexport function codexFacts(file: string): SessionFacts {\n const { head, tail } = ends(file, 128 * 1024, 64 * 1024);\n let H = parseLines(head);\n let T = parseLines(tail);\n const out: SessionFacts = {};\n const meta = H.find((j) => j.type === \"session_meta\" && j.payload);\n const boundary = codexReplayBoundary(meta);\n if (meta?.payload) {\n const p = meta.payload as {\n cwd?: string;\n cli_version?: string;\n git?: { branch?: string; repository_url?: string };\n };\n if (p.cwd) out.cwd = p.cwd;\n if (p.cli_version) out.harness_version = String(p.cli_version);\n if (p.git?.branch) out.branch = p.git.branch;\n if (p.git?.repository_url) out.repo = String(p.git.repository_url).replace(/\\/\\/[^/@]*@/, \"//\");\n }\n if (boundary) {\n const h = H.findIndex(boundary);\n const t = T.findIndex(boundary);\n H = h < 0 ? [] : H.slice(h);\n // The samples can overlap, so trim each one at the boundary independently.\n // If both miss it, scan the gap before trusting the tail: it could still be\n // parent replay. Stop as soon as the boundary is found, without loading the file.\n const found =\n h >= 0 ||\n t >= 0 ||\n scanLines(file, (line) => (boundary(parseLines([line])[0] ?? null) ? true : null), Number.POSITIVE_INFINITY);\n T = t >= 0 ? T.slice(t) : found ? T : [];\n }\n const ctx = H.concat(T)\n .reverse()\n .find((j) => j.type === \"turn_context\" && (j.payload as { model?: string } | undefined)?.model) as\n | { payload?: { model?: string } }\n | undefined;\n if (ctx?.payload?.model) out.model = ctx.payload.model;\n for (const j of H) {\n const p = j.payload as { type?: string; role?: string; content?: { type?: string; text?: string }[] } | undefined;\n if (j.type !== \"response_item\" || !p || p.type !== \"message\" || p.role !== \"user\") continue;\n const text = (p.content ?? [])\n .filter((b) => b && b.type === \"input_text\" && typeof b.text === \"string\")\n .map((b) => b.text as string)\n .join(\"\\n\")\n .trim();\n if (!text || codexInjected(text)) continue;\n const line = text.split(\"\\n\")[0] as string; // trimmed, so its first line is its first non-blank one\n out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\\s+\\S*$/, \"\")}\u2026` : line;\n out.title_source = \"first-ask\";\n break;\n }\n const times = (boundary && meta ? [meta, ...H, ...T] : H.concat(T))\n .map((j) => Date.parse(String(j.timestamp ?? \"\")))\n .filter((n) => n > 0);\n if (times.length) {\n out.started_at = Math.min(...times);\n out.last_at = Math.max(...times);\n }\n return out;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Codex's ~/.codex/config.toml, as far as the notify fallback needs it: its one\n// top-level `notify` command. Codex reads `notify` only at the top level \u2014 before\n// the first [table] \u2014 and Codex itself appends a [projects.\"<path>\"] table for every\n// folder it trusts, so a line appended at the end lands inside one and never runs.\n// Ported from spacesheep-cli lib/codex-config.js. Pure text in, text out.\n\nconst NOTIFY_LINE = /^[ \\t]*notify[ \\t]*=[ \\t]*(\\[.*\\])[ \\t]*(?:#.*)?(?=\\r?$)/m;\nconst NOTIFY_KEY = /^[ \\t]*notify[ \\t]*=/m;\n/** A notify that runs sessionpipe's hook directly (or through its launcher). */\nexport const OURS = /(?:hook\\.js|sessionpipe-hook)\",\\s*\"codex\",\\s*\"notify\"/;\n\nfunction scan(line: string, st: { depth: number; str: string | null }): void {\n let i = 0;\n while (i < line.length) {\n if (st.str) {\n const j = line.indexOf(st.str, i);\n if (j < 0) return;\n i = j + 3;\n st.str = null;\n continue;\n }\n const ch = line[i];\n if (ch === \"#\") return;\n if (line.startsWith('\"\"\"', i) || line.startsWith(\"'''\", i)) {\n st.str = line.slice(i, i + 3);\n i += 3;\n continue;\n }\n if (ch === '\"') {\n i++;\n while (i < line.length && line[i] !== '\"') i += line[i] === \"\\\\\" ? 2 : 1;\n i++;\n continue;\n }\n if (ch === \"'\") {\n const j = line.indexOf(\"'\", i + 1);\n i = j < 0 ? line.length : j + 1;\n continue;\n }\n if (ch === \"[\") st.depth++;\n else if (ch === \"]\") st.depth = Math.max(0, st.depth - 1);\n i++;\n }\n}\n\n/** Offset of the first table header, or -1. */\nexport function firstTableAt(text: string): number {\n const st = { depth: 0, str: null as string | null };\n let pos = 0;\n for (const line of text.split(\"\\n\")) {\n if (!st.str && st.depth === 0 && /^[ \\t]*\\[/.test(line)) return pos;\n scan(line, st);\n pos += line.length + 1;\n }\n return -1;\n}\n\nfunction split(text: string) {\n const at = firstTableAt(text);\n return at < 0 ? { head: text, tail: \"\", at: text.length } : { head: text.slice(0, at), tail: text.slice(at), at };\n}\n\nexport function parseArgv(raw: string): string[] | null {\n try {\n const a = JSON.parse(raw) as unknown;\n return Array.isArray(a) && a.every((x) => typeof x === \"string\") ? (a as string[]) : null;\n } catch {\n return null;\n }\n}\n\nexport interface Notify {\n raw: string;\n argv: string[] | null;\n index: number;\n length: number;\n misplaced: boolean;\n other: boolean;\n}\n\n/** notify as Codex sees it; `misplaced` for a line of ours inside a table. */\nexport function readNotify(text: string): Notify | null {\n const { head, tail, at } = split(text);\n const m = NOTIFY_LINE.exec(head);\n if (m) {\n const raw = m[1] as string;\n return { raw, argv: parseArgv(raw), index: m.index, length: m[0].length, misplaced: false, other: !OURS.test(raw) };\n }\n const k = NOTIFY_KEY.exec(head);\n if (k) {\n const rest = head.slice(k.index).split(\"\\n\")[0] as string;\n return {\n raw: rest.replace(NOTIFY_KEY, \"\").trim(),\n argv: null,\n index: k.index,\n length: 0,\n misplaced: false,\n other: true,\n };\n }\n const t = NOTIFY_LINE.exec(tail);\n if (t) {\n const raw = t[1] as string;\n if (OURS.test(raw))\n return { raw, argv: parseArgv(raw), index: at + t.index, length: t[0].length, misplaced: true, other: false };\n }\n return null;\n}\n\nfunction dropLine(text: string, index: number, length: number): string {\n return text.slice(0, index) + text.slice(index + length).replace(/^\\r?\\n/, \"\");\n}\n\n/** Put `line` at the top level: before the first table, or at the end of a file that has\n * none. The line takes the file's own line end, so a CRLF file stays one. */\nexport function insertTopLevel(text: string, line: string): string {\n const { head, tail } = split(text);\n // The line joins the top-level section, so its line end is that section's own; a head\n // with no line end of its own takes the file's.\n const body = head.replace(/\\s*$/, \"\");\n const eol = (body.includes(\"\\n\") ? body : text).includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const top = head.trim() ? head.replace(/\\s*$/, eol) : \"\";\n if (!tail) return top + line + eol;\n return `${top + line}${eol}${eol}${tail}`;\n}\n\n/** The config with `line` as its notify, for a config whose notify is ours or absent. */\nexport function withNotify(text: string, line: string): string {\n const n = readNotify(text);\n if (n && !n.misplaced && !n.other) return text.slice(0, n.index) + line + text.slice(n.index + n.length);\n if (n?.misplaced) return insertTopLevel(dropLine(text, n.index, n.length), line);\n return insertTopLevel(text, line);\n}\n\n/** The config with its notify line `n` (as readNotify found it in `text`) replaced by `line`. */\nexport function replaceNotify(text: string, n: Notify, line: string): string {\n return text.slice(0, n.index) + line + text.slice(n.index + n.length);\n}\n\n/** The config with our top-level or misplaced notify removed. */\nexport function withoutNotify(text: string): string {\n const n = readNotify(text);\n if (!n || n.other || !n.length) return text;\n return dropLine(text, n.index, n.length);\n}\n\n// Codex Computer Use takes the notify slot itself and keeps the command it found in\n// its own argv: [client, \"turn-ended\", \"--previous-notify\", \"<that command as JSON>\"].\nconst PREVIOUS = \"--previous-notify\";\nexport function previousNotify(argv: string[] | null): string[] | null {\n if (!Array.isArray(argv)) return null;\n const i = argv.indexOf(PREVIOUS);\n return i >= 0 && typeof argv[i + 1] === \"string\" ? parseArgv(argv[i + 1] as string) : null;\n}\nexport function withPrevious(argv: string[], prev: string[] | null): string[] {\n const out = argv.slice();\n const i = out.indexOf(PREVIOUS);\n if (i >= 0) out.splice(i, 2);\n if (prev) out.push(PREVIOUS, JSON.stringify(prev));\n return out;\n}\n/** The raw notify value with its --previous-notify pair removed, formatting kept. */\nexport function stripPreviousRaw(raw: string): string {\n return raw.replace(/,\\s*\"--previous-notify\",\\s*\"(?:[^\"\\\\]|\\\\.)*\"\\s*(?=\\])/, \"\");\n}\n/** The raw notify value with `prev` as its --previous-notify, formatting of the rest kept. */\nexport function withPreviousRaw(raw: string, prev: string[]): string {\n const base = stripPreviousRaw(raw).replace(/\\s*\\]\\s*$/, \"\");\n return `${base}, \"--previous-notify\", ${JSON.stringify(JSON.stringify(prev))}]`;\n}\nexport const isComputerUse = (argv: string[] | null): boolean =>\n Array.isArray(argv) && typeof argv[0] === \"string\" && /(^|\\/)SkyComputerUseClient$/.test(argv[0]);\n", "// SPDX-License-Identifier: Apache-2.0\n// Devin CLI (Cognition) \u2014 docs: https://docs.devin.ai/cli/extensibility/hooks/overview\n// Config: the `hooks` key of the user config file \u2014 `~/.config/devin/config.json`\n// (`$XDG_CONFIG_HOME/devin/config.json` when that is set), `%APPDATA%\\devin\\config.json`\n// on Windows. Claude-shaped, matcher a REGEX on the tool name, timeouts in SECONDS.\n// That file is the only user-level location Devin reads hooks from; `hooks.v1.json` is\n// project-level only.\n//\n// Devin accepts exactly eight event names and rejects the WHOLE file on one it does not\n// know (\"unknown variant `Notification`, expected one of \u2026\"), which silently disables\n// every hook in it. DEVIN_EVENTS is that set, and nothing else may be added to it.\n//\n// The payload carries no `cwd`, no `transcript_path` and no `model`: the folder comes\n// from DEVIN_PROJECT_DIR in the hook's environment, and the transcript, the model, the\n// title and the times come from Devin's own session store \u2014 one SQLite file under the\n// data directory, read only, through readers/sqlite.ts. A session's transcript is\n// addressed as \"<database>#<session id>\", because readTranscript is given a file.\n//\n// stdout stays empty for every event: Devin reads a decision from stdout, and silence\n// plus exit 0 is \"carry on\" (printing nothing, printing `{}`, exiting 1 and blowing the\n// timeout were all measured as non-blocking; only exit 2 or an explicit\n// {\"decision\":\"block\"} blocks a tool).\nimport { createHash } from \"node:crypto\";\nimport { existsSync, readlinkSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { homeDir } from \"../paths.js\";\nimport { querySqlite } from \"../readers/sqlite.js\";\nimport { pairTurns } from \"../readers/transcripts.js\";\nimport { installClaudeShaped, installedClaudeShaped, readJson, uninstallClaudeShaped } from \"./claude-shaped.js\";\nimport type {\n Adapter,\n BackfillRow,\n HookInput,\n HookResult,\n InstallOptions,\n SessionFacts,\n TranscriptRead,\n} from \"./types.js\";\n\nconst NAME = \"devin\";\n/** The eight event names Devin accepts. One it does not know makes it drop the whole\n * hooks file, so this list is exact, not a superset \u2014 see devin.test.ts. */\nexport const DEVIN_EVENTS = [\n \"SessionStart\",\n \"UserPromptSubmit\",\n \"PreToolUse\",\n \"PostToolUse\",\n \"PermissionRequest\",\n \"Stop\",\n \"PostCompaction\",\n \"SessionEnd\",\n] as const;\nconst OPTS = { matcherFor: (e: string) => (/Tool|Permission/.test(e) ? \"\" : undefined), timeout: 5 };\nconst eventsFor = (lean?: boolean) => (lean ? DEVIN_EVENTS.filter((e) => e !== \"PreToolUse\") : [...DEVIN_EVENTS]);\nconst INSTALL_NOTE =\n \"Devin drops a whole hooks file on one event name it doesn't know; `/hooks` in `devin` lists what loaded\";\nconst CLAUDE_NOTE =\n \"Devin also reads ~/.claude/settings.json hooks (read_config_from.claude); a Claude-only event name there makes it ignore that file whole\";\n\nconst deadFileNote = (bad: string) =>\n `Devin loads NO hooks from this file: \"${bad}\" is not one of its event names and it rejects the file whole \u2014 remove that key (\\`/hooks\\` in \\`devin\\` lists what loaded)`;\n\n/** The first `hooks` key in the file that Devin does not accept, if there is one.\n * One such key disables every hook in the file, ours and any other tool's. */\nexport function foreignEvent(file: string): string | undefined {\n const hooks = readJson(file).hooks;\n if (!hooks || typeof hooks !== \"object\" || Array.isArray(hooks)) return undefined;\n const ok = DEVIN_EVENTS as readonly string[];\n return Object.keys(hooks).find((k) => !ok.includes(k));\n}\n\nconst homeOf = (env: NodeJS.ProcessEnv) => env.HOME || homeDir();\nconst appData = (env: NodeJS.ProcessEnv) => env.APPDATA || path.join(homeOf(env), \"AppData\", \"Roaming\");\n/** `~/.config/devin`, `$XDG_CONFIG_HOME/devin`, or `%APPDATA%\\devin`. */\nexport const devinConfigDir = (env: NodeJS.ProcessEnv = process.env): string =>\n process.platform === \"win32\"\n ? path.join(appData(env), \"devin\")\n : path.join(env.XDG_CONFIG_HOME || path.join(homeOf(env), \".config\"), \"devin\");\nconst configFile = (env: NodeJS.ProcessEnv) => path.join(devinConfigDir(env), \"config.json\");\n/** `~/.local/share/devin/cli`, `$XDG_DATA_HOME/devin/cli`, or `%APPDATA%\\devin\\cli`. */\nexport const devinDataDir = (env: NodeJS.ProcessEnv = process.env): string =>\n process.platform === \"win32\"\n ? path.join(appData(env), \"devin\", \"cli\")\n : path.join(env.XDG_DATA_HOME || path.join(homeOf(env), \".local\", \"share\"), \"devin\", \"cli\");\nconst sessionsDb = (env: NodeJS.ProcessEnv) => path.join(devinDataDir(env), \"sessions.db\");\n\nconst SESSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;\nconst validTool = (n: unknown): n is string => typeof n === \"string\" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);\nconst endReason = (r: unknown): \"clear\" | \"logout\" | \"exit\" | \"other\" =>\n r === \"clear\" || r === \"logout\" || r === \"exit\" ? r : \"other\";\nconst pickSource = (v: unknown): string =>\n typeof v === \"string\" && [\"startup\", \"resume\", \"clear\", \"compact\", \"fork\"].includes(v) ? v : \"unknown\";\nfunction errText(v: unknown): string | undefined {\n if (typeof v === \"string\" && v) return v.slice(0, 2000);\n if (v && typeof v === \"object\") {\n const e = v as { message?: unknown; error?: unknown };\n if (typeof e.message === \"string\" && e.message) return e.message.slice(0, 2000);\n if (typeof e.error === \"string\" && e.error) return e.error.slice(0, 2000);\n }\n return undefined;\n}\nconst cut = (s: string, n: number): string => (s.length > n ? `${s.slice(0, n).replace(/\\s+\\S*$/, \"\")}\u2026` : s);\n\nexport const devin: Adapter = {\n name: NAME,\n events: DEVIN_EVENTS,\n detect: (env = process.env) => existsSync(devinConfigDir(env)) || existsSync(devinDataDir(env)),\n configFiles: (env = process.env) => [configFile(env)],\n install: (cmd, env = process.env, opts: InstallOptions = {}) => {\n const file = configFile(env);\n const r = installClaudeShaped(file, NAME, eventsFor(opts.lean), cmd, OPTS);\n if (r.skipped) return [r];\n const bad = foreignEvent(file);\n return [{ ...r, note: bad ? deadFileNote(bad) : INSTALL_NOTE }];\n },\n uninstall: (env = process.env, opts: InstallOptions = {}) => [\n uninstallClaudeShaped(configFile(env), DEVIN_EVENTS, { created: opts.created?.includes(configFile(env)) }),\n ],\n installed: (cmd, env = process.env, opts: InstallOptions = {}) => {\n const file = configFile(env);\n const state = installedClaudeShaped(file, NAME, eventsFor(opts.lean), cmd, OPTS);\n // Our entries can be present and current and still report nothing: one event name\n // Devin does not know makes it drop the whole file, ours with it. `status` has to\n // say so, or it tells the person reporting is on while it is dead.\n const bad = state === \"missing\" ? undefined : foreignEvent(file);\n return [{ file, state: bad ? \"inactive\" : state, note: bad ? deadFileNote(bad) : CLAUDE_NOTE }];\n },\n\n fromHook(input: HookInput): HookResult | null {\n const event = input.argv[1] ?? \"\";\n let s: Record<string, unknown> = {};\n try {\n // A stdin that is JSON but not an object (`null`, a number) is no payload.\n const j: unknown = input.stdin ? JSON.parse(input.stdin) : {};\n if (j && typeof j === \"object\" && !Array.isArray(j)) s = j as Record<string, unknown>;\n } catch {}\n const id = typeof s.session_id === \"string\" ? s.session_id : \"\";\n if (!id) return null;\n // Nothing in the payload names the folder; DEVIN_PROJECT_DIR is the only thing that does.\n const session: HookResult[\"session\"] = { id, cwd: input.env.DEVIN_PROJECT_DIR ?? input.cwd };\n const turn_id = typeof s.prompt_id === \"string\" ? s.prompt_id : undefined;\n const tool = validTool(s.tool_name) ? s.tool_name : undefined;\n const call_id = typeof s.tool_use_id === \"string\" ? s.tool_use_id : undefined;\n // Every recorded payload has an object here, but the field is free-form: a Devin\n // version or an MCP-style tool could send a bare string, and then there is no\n // `success` and no `output` to read \u2014 the whole value IS the output.\n const response =\n s.tool_response && typeof s.tool_response === \"object\" && !Array.isArray(s.tool_response)\n ? (s.tool_response as { success?: unknown; output?: unknown; error?: unknown })\n : undefined;\n // The same prompt must always name the same attention (CONTROL.md \u00A76). Devin's\n // PermissionRequest does carry tool_use_id; the hash is the fallback for one that\n // doesn't, and is the bytes ADAPTERS.md's common conventions prescribe.\n const permissionId = () =>\n `perm-${createHash(\"sha256\")\n .update(`${turn_id ?? \"\"}|${tool ?? \"\"}|${JSON.stringify(s.tool_input ?? null)}`)\n .digest(\"hex\")\n .slice(0, 16)}`;\n const ev = (type: string, data: Record<string, unknown>) => ({ type, data: prune(data), harnessEvent: event });\n const events: HookResult[\"events\"] = [];\n switch (event) {\n case \"SessionStart\":\n events.push(ev(\"session.started\", { source: pickSource(s.source) }));\n break;\n case \"UserPromptSubmit\":\n events.push(\n ev(\"turn.started\", {\n turn_id,\n prompt_chars: typeof s.prompt === \"string\" ? [...s.prompt].length : undefined,\n }),\n );\n break;\n case \"PreToolUse\":\n if (tool) events.push(ev(\"tool.started\", { tool, call_id, turn_id, input: s.tool_input }));\n break;\n case \"PostToolUse\":\n // Devin is the one harness that says so outright: tool_response.success.\n if (tool)\n events.push(\n ev(\"tool.ended\", {\n tool,\n call_id,\n turn_id,\n // `ok` is required on tool.ended (PROTOCOL.md \u00A7events), so a response\n // that carries no success flag at all reads as \"nothing said it failed\".\n ok: response?.success !== false,\n error: errText(response?.error),\n input: s.tool_input,\n output: response ? response.output : s.tool_response,\n }),\n );\n break;\n case \"PermissionRequest\":\n events.push(ev(\"attention.needed\", { attention_id: call_id ?? permissionId(), kind: \"permission\", tool }));\n break;\n case \"Stop\":\n events.push(ev(\"turn.ended\", { turn_id, reason: \"stop\" }));\n break;\n case \"PostCompaction\":\n // Devin has no pre-compaction event; the payload carries only the summary.\n events.push(ev(\"context.compacted\", { trigger: \"auto\" }));\n break;\n case \"SessionEnd\":\n events.push(ev(\"session.ended\", { reason: endReason(s.reason) }));\n break;\n default:\n return null;\n }\n return {\n session,\n events,\n hints: {},\n transcript: transcriptRef(sessionsDb(input.env as NodeJS.ProcessEnv), id),\n };\n },\n\n readTranscript(file, fromLine) {\n const ref = splitRef(file);\n if (!ref) return { turns: [], line: fromLine };\n return readDevinChain(ref.db, ref.session, fromLine);\n },\n\n facts(session, transcript, _hints, _state, env = process.env): SessionFacts {\n const out: SessionFacts = {};\n const version = devinVersion(env);\n if (version) out.harness_version = version;\n const account = devinAccount(env);\n if (account) out.account_id = account;\n const ref = splitRef(transcript) ?? { db: sessionsDb(env), session: session.id };\n if (!SESSION_ID.test(ref.session)) return out;\n const rows = querySqlite(ref.db, SESSION_SQL, [ref.session]);\n const row = rows?.[0];\n if (!row) return out;\n Object.assign(out, sessionFacts(row));\n return out;\n },\n\n backfill(sinceMs, env = process.env): BackfillRow[] {\n const rows = querySqlite(sessionsDb(env), BACKFILL_SQL, [\"text\", Math.floor(sinceMs / 1000)]);\n if (!rows) return [];\n const out: BackfillRow[] = [];\n for (const row of rows) {\n const id = String(row.id); // text, the query says\n if (!SESSION_ID.test(id)) continue;\n const { started_at, last_at, ...rest } = sessionFacts(row);\n if (!started_at || !last_at) continue;\n out.push({ session: { id, ...rest }, started_at, last_at });\n }\n return out;\n },\n};\n\nfunction prune<T extends Record<string, unknown>>(o: T): T {\n for (const k of Object.keys(o)) if (o[k] === undefined) delete o[k];\n return o;\n}\n\n// --- the session store ---------------------------------------------------------------\n\n/** `<database>#<session id>`: readTranscript is given a file, and Devin keeps every\n * session in one database, so the session has to ride in the string. */\nexport const transcriptRef = (db: string, session: string): string => `${db}#${session}`;\nexport function splitRef(ref: string | undefined): { db: string; session: string } | null {\n if (!ref) return null;\n const at = ref.lastIndexOf(\"#\");\n if (at <= 0) return null;\n const db = ref.slice(0, at);\n const session = ref.slice(at + 1);\n return db && SESSION_ID.test(session) ? { db, session } : null;\n}\n\n/** `message_nodes` is a forest: it keeps abandoned branches too (27 nodes for a\n * two-turn session). The transcript is the one chain that ends at\n * `sessions.main_chain_id`, walked up through `parent_node_id` \u2014 never every node. */\n/** The walk stops at the cursor (`m.node_id >= ?`): everything below it has been read\n * already, and without that bound every Stop pulled `chat_message` for every ancestor\n * \u2014 the root of a real chain carries a 17 kB system prompt and four more system\n * blocks, re-read on every event, and past 32 MB the child and CLI engines give up\n * and return nothing. The cap keeps the NEWEST rows (`DESC`), re-sorted below: an\n * ascending cap would have kept the oldest and delivered no new turn ever again.\n * `UNION`, not `UNION ALL`: SQLite queues a row only if it has not queued the same row\n * before, so a chain that loops back on itself (a node its own parent, two that name\n * each other) ends at the first node it reaches twice. Under `UNION ALL` it recursed\n * for ever, and on the in-process engine nothing can interrupt that: the worker spins,\n * holds the session's lock and delivers nothing. */\nconst CHAIN_LIMIT = 4000;\nexport const CHAIN_SQL = `WITH RECURSIVE chain(node_id, parent_node_id, chat_message, created_at) AS (\n SELECT node_id, parent_node_id, chat_message, created_at FROM message_nodes\n WHERE session_id = ? AND node_id = (SELECT main_chain_id FROM sessions WHERE id = ?)\n UNION\n SELECT m.node_id, m.parent_node_id, m.chat_message, m.created_at FROM message_nodes m\n JOIN chain c ON m.node_id = c.parent_node_id AND m.session_id = ? AND m.node_id >= ?\n)\nSELECT node_id, chat_message, created_at FROM chain ORDER BY node_id DESC LIMIT ${CHAIN_LIMIT}`;\n\n/** Times are unix SECONDS in this store. The first ask comes from `prompt_history`,\n * because `sessions.title` holds whatever the titler last produced \u2014 a backticked\n * command, or a raw `functions.Shell:0{\u2026}` mid-turn. */\nconst SESSION_FIELDS = `s.model, s.working_directory, s.created_at, s.last_activity_at,\n (SELECT p.content FROM prompt_history p WHERE p.session_id = s.id AND p.is_shell = 0\n ORDER BY p.timestamp ASC, p.id ASC LIMIT 1) AS first_ask`;\nconst SESSION_SQL = `SELECT ${SESSION_FIELDS} FROM sessions s WHERE s.id = ? LIMIT 1`;\n// typeof(id) = 'text' (the literal rides as a parameter: the CLI engine takes no quote\n// in the SQL): an id a session ref could carry. A blob id comes back from the sqlite3\n// CLI as a string shaped by its version, so the database says what it is.\nconst BACKFILL_SQL = `SELECT s.id, ${SESSION_FIELDS} FROM sessions s\n WHERE s.hidden = 0 AND typeof(s.id) = ? AND s.last_activity_at >= ?\n ORDER BY s.last_activity_at DESC LIMIT 500`;\n\nfunction sessionFacts(row: Record<string, unknown>): SessionFacts {\n const out: SessionFacts = {};\n if (typeof row.model === \"string\" && row.model) out.model = row.model;\n if (typeof row.working_directory === \"string\" && path.isAbsolute(row.working_directory))\n out.cwd = row.working_directory;\n const started = Number(row.created_at);\n const last = Number(row.last_activity_at);\n if (Number.isFinite(started) && started > 0) out.started_at = Math.round(started * 1000);\n if (Number.isFinite(last) && last > 0) out.last_at = Math.round(last * 1000);\n if (typeof row.first_ask === \"string\") {\n const line = row.first_ask.split(\"\\n\").find((l) => l.trim()) ?? \"\";\n if (line.trim()) {\n out.title = cut(line.trim(), 80);\n out.title_source = \"first-ask\";\n }\n }\n return out;\n}\n\ninterface ChatMessage {\n role?: unknown;\n content?: unknown;\n metadata?: { is_user_input?: unknown; created_at?: unknown } | null;\n}\n\n/** The canonical chain as turns. `node_id` is the cursor: it rises along the chain and\n * is stable across a resume, so a later read starts where the last one stopped.\n * `role: \"system\"` is Cognition's own prompt (and the skills manifest, and the\n * generated system_info block), never the person's words \u2014 it is skipped at every\n * tier, as is `role: \"tool\"`. */\nexport function readDevinChain(db: string, session: string, fromLine: number): TranscriptRead {\n if (!SESSION_ID.test(session)) return { turns: [], line: fromLine };\n const floor = Number.isFinite(fromLine) && fromLine > 0 ? Math.floor(fromLine) : 0;\n const rows = querySqlite(db, CHAIN_SQL, [session, session, session, floor]);\n if (!rows?.length) return { turns: [], line: fromLine };\n const msgs: { role: \"user\" | \"assistant\"; text: string; at: number; line: number }[] = [];\n let end = fromLine;\n for (const row of rows) {\n const node = Number(row.node_id);\n if (!Number.isFinite(node)) continue;\n if (node + 1 > end) end = node + 1;\n if (node < fromLine) continue;\n let j: ChatMessage;\n try {\n j = JSON.parse(String(row.chat_message ?? \"\")) as ChatMessage;\n } catch {\n continue;\n }\n const text = typeof j.content === \"string\" ? j.content.trim() : \"\";\n if (!text) continue;\n // The message's own time, not the row's. `message_nodes.created_at` is when the row\n // batch was last WRITTEN: Devin rewrites it on every save of the chain, so every\n // node of a chain usually carries one identical value, and it is not even monotonic\n // along the chain. Measured on a live store 2026-10-08: a two-turn session had all\n // four of its chain nodes at 23:33:10 while their own `metadata.created_at` read\n // 23:32:29, :34, 23:33:08 and :10 (turn 1 reported 41 s late, both turns at the same\n // instant); one node's row time was rewritten 12 s later between two reads; and a\n // descendant's row time was 27 s EARLIER than its ancestor's. The column is only the\n // fallback for a row whose message has no time of its own.\n const secs = Number(row.created_at);\n const at =\n Date.parse(String(j.metadata?.created_at ?? \"\")) ||\n (Number.isFinite(secs) && secs > 0 ? Math.round(secs * 1000) : 0) ||\n Date.now();\n if (j.role === \"user\" && j.metadata?.is_user_input === true) msgs.push({ role: \"user\", text, at, line: node });\n else if (j.role === \"assistant\") msgs.push({ role: \"assistant\", text, at, line: node });\n }\n // The rows come newest first (the cap keeps the newest); a transcript runs the other way.\n msgs.sort((a, b) => a.line - b.line);\n return pairTurns(msgs, end);\n}\n\n/** `<data dir>/_versions/current` is a symlink to the running version's folder. */\nexport function devinVersion(env: NodeJS.ProcessEnv = process.env): string | undefined {\n try {\n const target = readlinkSync(path.join(devinDataDir(env), \"_versions\", \"current\"));\n const name = path.basename(target);\n return /^[0-9][0-9A-Za-z.+-]{0,63}$/.test(name) ? name : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** The organisation the CLI is signed in to, from the user config \u2014 the only account id\n * Devin keeps in a plain local file. Never the email, and never credentials.toml. */\nexport function devinAccount(env: NodeJS.ProcessEnv = process.env): string | undefined {\n const d = readJson(configFile(env)).devin as { org_id?: unknown } | undefined;\n const id = d && typeof d === \"object\" ? d.org_id : undefined;\n return typeof id === \"string\" && /^[A-Za-z0-9_.:-]{1,128}$/.test(id) ? id : undefined;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Read-only SQLite, for a harness that keeps its sessions in a database instead of a\n// JSONL file. Three ways in, tried in order, because `node:sqlite` does not exist on\n// Node 20 (the floor this package builds for) and is behind --experimental-sqlite on\n// 22.5\u201322.12:\n// \"node\" in this process, through createRequire \u2014 no static import, so the bundle\n// still loads on a Node without the module;\n// \"child\" one `node --experimental-sqlite -e \u2026` run through execFileSync: no shell,\n// arguments passed as argv, a short timeout;\n// \"cli\" the `sqlite3` binary, when it is on PATH and every parameter is a plain\n// scalar that can be written as a literal.\n// Every path opens the file read-only, is bounded in time, and returns null instead of\n// throwing: a database that is busy, missing, locked or unreadable costs the caller\n// nothing but an empty read.\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\n\nexport type SqliteParam = string | number;\nexport type SqliteRow = Record<string, unknown>;\nexport type SqliteEngine = \"auto\" | \"node\" | \"child\" | \"cli\";\n\nexport interface SqliteOptions {\n /** Busy timeout and the child's wall clock, in ms (default 2000). */\n timeoutMs?: number;\n /** Force one way in; \"auto\" tries node, then child, then cli (default). */\n engine?: SqliteEngine;\n /** Largest reply a child or the CLI may print (default 32 MB). */\n maxBuffer?: number;\n}\n\ninterface Statement {\n all(...params: SqliteParam[]): unknown[];\n}\ninterface Db {\n prepare(sql: string): Statement;\n exec(sql: string): void;\n close(): void;\n}\ntype DbCtor = new (path: string, options?: Record<string, unknown>) => Db;\n\n/** `node:sqlite` is still experimental, and Node says so on stderr the first time it is\n * used. A reader has no business writing to a harness's terminal, so that one warning\n * is swallowed while the module is loaded and used \u2014 nothing else is, and the original\n * emitter is back before this returns. (The child path passes --no-warnings instead.)\n * Exported for its test: Node emits that warning once per process. */\nexport function quiet<T>(fn: () => T): T {\n const original = process.emitWarning;\n try {\n process.emitWarning = ((warning: unknown, ...rest: unknown[]) => {\n const kind = typeof rest[0] === \"string\" ? rest[0] : (rest[0] as { type?: string } | undefined)?.type;\n if (kind === \"ExperimentalWarning\" && /sqlite/i.test(String(warning))) return;\n (original as (...a: unknown[]) => void).call(process, warning, ...rest);\n }) as typeof process.emitWarning;\n return fn();\n } finally {\n process.emitWarning = original;\n }\n}\n\nlet ctorCache: DbCtor | null | undefined;\n/** `node:sqlite` if this Node has it; null if it does not (Node 20, or 22.x unflagged). */\nfunction dbCtor(): DbCtor | null {\n if (ctorCache !== undefined) return ctorCache;\n ctorCache = null;\n try {\n const req = createRequire(import.meta.url);\n const m = quiet(() => req(\"node:sqlite\")) as { DatabaseSync?: unknown };\n if (typeof m?.DatabaseSync === \"function\") ctorCache = m.DatabaseSync as DbCtor;\n } catch {}\n return ctorCache;\n}\n\n/** SQLite gives back strings, numbers, null, BigInt and Uint8Array; the protocol takes JSON. */\nfunction plain(row: unknown): SqliteRow | null {\n if (!row || typeof row !== \"object\") return null;\n const out: SqliteRow = {};\n for (const [k, v] of Object.entries(row as SqliteRow)) {\n if (typeof v === \"bigint\") out[k] = Number(v);\n else if (v instanceof Uint8Array) out[k] = null;\n else out[k] = v;\n }\n return out;\n}\nconst rows = (v: unknown): SqliteRow[] | null => {\n if (!Array.isArray(v)) return null;\n const out: SqliteRow[] = [];\n for (const r of v) {\n const p = plain(r);\n if (p) out.push(p);\n }\n return out;\n};\n\nfunction viaNode(file: string, sql: string, params: SqliteParam[], o: Required<SqliteOptions>): SqliteRow[] | null {\n const C = dbCtor();\n if (!C) return null;\n let db: Db;\n try {\n db = quiet(() => new C(file, { readOnly: true, timeout: o.timeoutMs }));\n } catch {\n return null;\n }\n try {\n try {\n db.exec(`PRAGMA busy_timeout = ${Math.round(o.timeoutMs)}`);\n } catch {}\n return quiet(() => rows(db.prepare(sql).all(...params)));\n } catch {\n return null;\n } finally {\n try {\n db.close();\n } catch {}\n }\n}\n\n/** Runs in a child Node, with the file, the SQL, the parameters and the timeout as argv. */\nconst CHILD = `const { DatabaseSync } = require(\"node:sqlite\");\nconst [, file, sql, params, ms] = process.argv;\nconst db = new DatabaseSync(file, { readOnly: true, timeout: Number(ms) });\ntry { db.exec(\"PRAGMA busy_timeout = \" + Number(ms)); } catch {}\nconst out = db.prepare(sql).all(...JSON.parse(params));\ndb.close();\nprocess.stdout.write(JSON.stringify(out));`;\n\nfunction viaChild(file: string, sql: string, params: SqliteParam[], o: Required<SqliteOptions>): SqliteRow[] | null {\n try {\n const out = execFileSync(\n process.execPath,\n [\"--experimental-sqlite\", \"--no-warnings\", \"-e\", CHILD, file, sql, JSON.stringify(params), String(o.timeoutMs)],\n {\n encoding: \"utf8\",\n timeout: o.timeoutMs + 3000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n maxBuffer: o.maxBuffer,\n windowsHide: true,\n },\n );\n return rows(JSON.parse(out || \"null\"));\n } catch {\n return null;\n }\n}\n\n/** A parameter safe to write into SQL as a literal: no quote, no backslash, no newline. */\nconst SAFE = /^[A-Za-z0-9 ._:@/+-]{0,256}$/;\nexport function inlineParams(sql: string, params: SqliteParam[]): string | null {\n if (sql.includes(\"'\")) return null; // a literal in the SQL would make the `?` count a guess\n const holes = sql.split(\"?\").length - 1;\n if (holes !== params.length) return null;\n let i = 0;\n let bad = false;\n const out = sql.replace(/\\?/g, () => {\n const p = params[i++];\n if (typeof p === \"number\") {\n if (!Number.isFinite(p)) {\n bad = true;\n return \"NULL\";\n }\n return String(p);\n }\n if (typeof p !== \"string\" || !SAFE.test(p)) {\n bad = true;\n return \"NULL\";\n }\n return `'${p}'`;\n });\n return bad ? null : out;\n}\n\nfunction viaCli(file: string, sql: string, params: SqliteParam[], o: Required<SqliteOptions>): SqliteRow[] | null {\n const text = inlineParams(sql, params);\n if (!text) return null;\n try {\n const out = execFileSync(\"sqlite3\", [\"-readonly\", \"-json\", file, text], {\n encoding: \"utf8\",\n timeout: o.timeoutMs + 3000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n maxBuffer: o.maxBuffer,\n windowsHide: true,\n }).trim();\n if (!out) return [];\n return rows(JSON.parse(out));\n } catch {\n return null;\n }\n}\n\n/** One read-only query. Null means \"could not read it\", never an exception. */\nexport function querySqlite(\n file: string,\n sql: string,\n params: SqliteParam[] = [],\n opts: SqliteOptions = {},\n): SqliteRow[] | null {\n const o: Required<SqliteOptions> = {\n timeoutMs: opts.timeoutMs ?? 2000,\n engine: opts.engine ?? \"auto\",\n maxBuffer: opts.maxBuffer ?? 32 * 1024 * 1024,\n };\n if (!file) return null;\n try {\n if (!existsSync(file)) return null;\n } catch {\n return null;\n }\n const ways =\n o.engine === \"auto\"\n ? ([viaNode, viaChild, viaCli] as const)\n : o.engine === \"node\"\n ? ([viaNode] as const)\n : o.engine === \"child\"\n ? ([viaChild] as const)\n : ([viaCli] as const);\n for (const way of ways) {\n const r = way(file, sql, params, o);\n if (r) return r;\n }\n return null;\n}\n\n/** Is `node:sqlite` in this process? (Tests skip what they cannot set up.) */\nexport const sqliteInProcess = (): boolean => dbCtor() !== null;\n", "// SPDX-License-Identifier: Apache-2.0\n// Gemini CLI (Google) \u2014 docs: https://geminicli.com/docs/hooks/reference\n// Config: ~/.gemini/settings.json `hooks` key, same array-of-groups shape as Claude\n// Code but timeouts in MILLISECONDS and a `name` per handler. stdout must be JSON or\n// empty. Its own telemetry defaults logPrompts on; the installer says so once.\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { homeDir } from \"../paths.js\";\nimport { ends, parseLines } from \"../readers/files.js\";\nimport { readGemini } from \"../readers/transcripts.js\";\nimport { installClaudeShaped, installedClaudeShaped, readJson, uninstallClaudeShaped } from \"./claude-shaped.js\";\nimport { findBinary, probe } from \"./headless.js\";\nimport type { Adapter, HookInput, HookResult, InstallOptions, SessionFacts } from \"./types.js\";\n\nconst NAME = \"gemini-cli\";\nexport const GEMINI_EVENTS = [\n \"SessionStart\",\n \"BeforeAgent\",\n \"BeforeTool\",\n \"AfterTool\",\n \"Notification\",\n \"AfterAgent\",\n \"PreCompress\",\n \"SessionEnd\",\n] as const;\nconst OPTS = { matcherFor: (e: string) => (/Tool/.test(e) ? \".*\" : undefined), timeout: 5000 };\nconst geminiDir = (env: NodeJS.ProcessEnv) => env.GEMINI_CLI_HOME || path.join(homeDir(), \".gemini\");\nconst settings = (env: NodeJS.ProcessEnv) => path.join(geminiDir(env), \"settings.json\");\nconst validTool = (n: unknown): n is string => typeof n === \"string\" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);\nconst eventsFor = (lean?: boolean) => (lean ? GEMINI_EVENTS.filter((e) => e !== \"BeforeTool\") : [...GEMINI_EVENTS]);\n\n/** `gemini --version`. On Windows the CLI is npm's gemini.cmd shim, which node refuses to\n * spawn bare, so there it is looked up on PATH and run as `probe` says (the shim's own\n * script under this node, else through a shell). */\nexport function geminiVersion(\n platform: NodeJS.Platform = process.platform,\n env: NodeJS.ProcessEnv = process.env,\n): string {\n const win = platform === \"win32\";\n const bin = win\n ? findBinary({ names: [\"gemini.exe\", \"gemini.cmd\"], dirs: [path.dirname(process.execPath)], env })\n : \"gemini\";\n if (!bin) throw new Error(\"no gemini on PATH\");\n const c = probe(bin, [\"--version\"]);\n return execFileSync(c.file, c.args, {\n encoding: \"utf8\",\n timeout: 4000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n env,\n shell: c.shell,\n }).trim();\n}\n\n/** Before 0.46 the hooks system is off unless tools.enableHooks is true (issue #11). */\nexport function geminiHooksActive(settingsFile: string): { active: boolean; note?: string } {\n const tools = readJson(settingsFile).tools as { enableHooks?: unknown } | undefined;\n if (tools?.enableHooks === true) return { active: true };\n if (tools?.enableHooks === false)\n return { active: false, note: \"inactive: tools.enableHooks is false in settings.json\" };\n let version = \"\";\n try {\n version = geminiVersion();\n } catch {\n return {\n active: true,\n note: \"could not run `gemini --version`; hooks need tools.enableHooks on versions before 0.46\",\n };\n }\n const m = /(\\d+)\\.(\\d+)/.exec(version);\n if (m && (Number(m[1]) > 0 || Number(m[2]) >= 46)) return { active: true };\n return {\n active: false,\n note: `inactive: Gemini CLI ${version || \"?\"} runs hooks only with \"tools\": {\"enableHooks\": true} in settings.json`,\n };\n}\n\nexport const geminiCli: Adapter = {\n name: NAME,\n events: GEMINI_EVENTS,\n // ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.\n detect: (env = process.env) => existsSync(settings(env)) || existsSync(path.join(geminiDir(env), \"tmp\")),\n configFiles: (env = process.env) => [settings(env)],\n install: (cmd, env = process.env, opts: InstallOptions = {}) => {\n const r = installClaudeShaped(settings(env), NAME, eventsFor(opts.lean), cmd, OPTS);\n const a = geminiHooksActive(settings(env));\n return [a.note && !r.skipped ? { ...r, note: [r.note, a.note].filter(Boolean).join(\"; \") } : r];\n },\n uninstall: (env = process.env, opts: InstallOptions = {}) => [\n uninstallClaudeShaped(settings(env), GEMINI_EVENTS, { created: opts.created?.includes(settings(env)) }),\n ],\n installed: (cmd, env = process.env, opts: InstallOptions = {}) => {\n const state = installedClaudeShaped(settings(env), NAME, eventsFor(opts.lean), cmd, OPTS);\n const a = geminiHooksActive(settings(env));\n return [\n {\n file: settings(env),\n state: state === \"current\" && !a.active ? \"inactive\" : state,\n ...(a.note ? { note: a.note } : {}),\n },\n ];\n },\n\n fromHook(input: HookInput): HookResult | null {\n const event = input.argv[1] ?? \"\";\n let s: Record<string, unknown> = {};\n try {\n // A stdin that is JSON but not an object (`null`, a number) is no payload.\n const j: unknown = input.stdin ? JSON.parse(input.stdin) : {};\n if (j && typeof j === \"object\" && !Array.isArray(j)) s = j as Record<string, unknown>;\n } catch {}\n const id = s.session_id as string | undefined;\n if (!id) return null;\n const session: HookResult[\"session\"] = { id: String(id), cwd: typeof s.cwd === \"string\" ? s.cwd : input.cwd };\n const tool = validTool(s.tool_name) ? s.tool_name : undefined;\n const ev = (type: string, data: Record<string, unknown>) => ({ type, data: prune(data), harnessEvent: event });\n const events: HookResult[\"events\"] = [];\n switch (event) {\n case \"SessionStart\":\n events.push(\n ev(\"session.started\", {\n source:\n typeof s.source === \"string\" && [\"startup\", \"resume\", \"clear\"].includes(s.source) ? s.source : \"unknown\",\n }),\n );\n break;\n case \"BeforeAgent\":\n events.push(\n ev(\"turn.started\", { prompt_chars: typeof s.prompt === \"string\" ? [...s.prompt].length : undefined }),\n );\n break;\n case \"BeforeTool\":\n if (tool) events.push(ev(\"tool.started\", { tool, input: s.tool_input }));\n break;\n case \"AfterTool\": {\n const resp = s.tool_response as { error?: unknown; llmContent?: unknown } | undefined;\n const err =\n resp && typeof resp === \"object\" && resp.error\n ? String(typeof resp.error === \"string\" ? resp.error : JSON.stringify(resp.error)).slice(0, 2000)\n : undefined;\n if (tool)\n events.push(\n ev(\"tool.ended\", {\n tool,\n ok: !err,\n error: err,\n input: s.tool_input,\n output: resp?.llmContent ?? s.tool_response,\n }),\n );\n break;\n }\n case \"Notification\":\n events.push(\n ev(\"attention.needed\", {\n attention_id: `notif-${Date.now()}`,\n kind: s.notification_type === \"ToolPermission\" ? \"permission\" : \"question\",\n message: typeof s.message === \"string\" ? s.message.slice(0, 200) : undefined,\n }),\n );\n break;\n case \"AfterAgent\":\n events.push(ev(\"turn.ended\", { reason: \"stop\" }));\n break;\n case \"PreCompress\":\n events.push(ev(\"context.compacted\", { trigger: s.trigger === \"manual\" ? \"manual\" : \"auto\" }));\n break;\n case \"SessionEnd\":\n events.push(\n ev(\"session.ended\", {\n reason:\n s.reason === \"clear\" || s.reason === \"logout\" || s.reason === \"exit\"\n ? s.reason\n : s.reason === \"prompt_input_exit\"\n ? \"exit\"\n : \"other\",\n }),\n );\n break;\n default:\n return null;\n }\n const r: HookResult = { session, events, hints: {} };\n if (typeof s.transcript_path === \"string\") r.transcript = s.transcript_path;\n return r;\n },\n\n readTranscript(file, fromLine) {\n let text = \"\";\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return { turns: [], line: fromLine };\n }\n return readGemini(text.split(\"\\n\"), fromLine);\n },\n\n facts(_session, transcript): SessionFacts {\n const out: SessionFacts = {};\n if (!transcript) return out;\n const { head, tail } = ends(transcript, 64 * 1024, 64 * 1024);\n const H = parseLines(head);\n const T = parseLines(tail);\n const first = H.find((j) => j.type === \"user\");\n const raw = first ? (first.content ?? first.message ?? first.text) : undefined;\n const text = typeof raw === \"string\" ? raw.trim() : \"\";\n if (text) {\n const line = text.split(\"\\n\")[0] as string; // trimmed, so its first line is its first non-blank one\n out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\\s+\\S*$/, \"\")}\u2026` : line;\n out.title_source = \"first-ask\";\n }\n const times = H.concat(T)\n .map((j) => Date.parse(String(j.timestamp ?? \"\")))\n .filter((n) => n > 0);\n if (times.length) {\n out.started_at = Math.min(...times);\n out.last_at = Math.max(...times);\n }\n return out;\n },\n\n backfill: () => [], // chat logs are keyed by a project hash, not a session id we can name; the first hook of a resumed session reports it\n};\n\nfunction prune<T extends Record<string, unknown>>(o: T): T {\n for (const k of Object.keys(o)) if (o[k] === undefined) delete o[k];\n return o;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// The registry: the only place that lists vendors. A new harness is one file here\n// plus its fixtures and its row in spec/ADAPTERS.md.\nimport { acpHarnesses } from \"./acp-control.js\";\nimport { antigravity } from \"./antigravity.js\";\nimport { findAgy } from \"./antigravity-control.js\";\nimport { claudeCode } from \"./claude-code.js\";\nimport { findClaude } from \"./claude-code-control.js\";\nimport { codex } from \"./codex.js\";\nimport { findCodex } from \"./codex-control.js\";\nimport { devin } from \"./devin.js\";\nimport { geminiCli } from \"./gemini-cli.js\";\nimport type { Adapter } from \"./types.js\";\n\nexport const ADAPTERS: readonly Adapter[] = [claudeCode, codex, geminiCli, antigravity, devin];\nexport const adapterByName = (name: string): Adapter | undefined => ADAPTERS.find((a) => a.name === name);\nexport * as acpControl from \"./acp-control.js\";\nexport { AG_STALE_PRETOOL, antigravityAnswer } from \"./antigravity.js\";\nexport * as antigravityControl from \"./antigravity-control.js\";\nexport { claudeAccounts, claudeDirs, notClaudeCode } from \"./claude-code.js\";\nexport * as claudeControl from \"./claude-code-control.js\";\nexport * as codexControl from \"./codex-control.js\";\nexport { DEVIN_EVENTS, devinConfigDir, devinDataDir, readDevinChain } from \"./devin.js\";\nexport {\n deniedNote,\n JOB_TIMEOUT_MS,\n jobEnv,\n type Login,\n type Reading,\n type RunOptions,\n type RunResult,\n runHeadless,\n} from \"./headless.js\";\n\n/** How many harnesses a pairing request or a hello lists at most (control-pair.json,\n * control-hello.json). */\nexport const HELLO_HARNESSES_MAX = 20;\n\n/** The harnesses a control daemon here can drive headlessly (CONTROL.md \u00A76): each whose\n * CLI is installed on this machine, and every ACP agent that is. What `control pair`\n * and every hello advertise: the three with their own drivers first, then the ACP\n * agents in the registry's order, at most HELLO_HARNESSES_MAX of them. */\nexport function drivableHarnesses(env: NodeJS.ProcessEnv = process.env): string[] {\n return [\n ...(findClaude(env) ? [\"claude-code\"] : []),\n ...(findCodex(env) ? [\"codex\"] : []),\n ...(findAgy(env) ? [\"antigravity\"] : []),\n ...acpHarnesses(env),\n ].slice(0, HELLO_HARNESSES_MAX);\n}\nexport type {\n Adapter,\n AdapterEvent,\n BackfillRow,\n FactsState,\n HookCommand,\n HookInput,\n HookResult,\n InstallReport,\n SessionFacts,\n TranscriptRead,\n Turn,\n} from \"./types.js\";\nexport { antigravity, claudeCode, codex, devin, geminiCli };\n", "// SPDX-License-Identifier: Apache-2.0\n// ~/.config/sessionpipe/config.json, mode 0600: the machine's name, the sinks with\n// their tier and token, per-harness switches, the update check, remote titles.\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { configFile, tilde } from \"./paths.js\";\nimport type { Tier } from \"./types.js\";\n\nexport interface SinkConfig {\n name: string;\n /** https://\u2026, file:PATH, stdout, otlp:URL */\n url: string;\n tier: Tier;\n pii?: boolean;\n /** @deprecated Never set: control is per machine (spec/CONTROL.md \u00A72). Kept so an\n * older config still reads. */\n control?: boolean;\n token?: string;\n /** whsec_\u2026 for Standard Webhooks signatures. */\n secret?: string;\n /** The token (and secret) live in this store instead of in this file; the client\n * reads them from it when it sends. */\n token_in?: \"keychain\" | \"secret-service\";\n /** Learned from the receiver's well-known file (`sink add`, `connect`; read again by a\n * worker once a day): its tier cap, and the tool events it asked for. */\n max_tier?: Tier;\n receiver_tools?: \"all\" | \"main\" | \"beat\";\n well_known_at?: string;\n /** When a worker last tried to read it again, whether or not the read went through. */\n well_known_checked_at?: string;\n paused?: string;\n /** Which tool events this sink is sent (issue #208): `all`; `main`, a subagent's at\n * most one per session per minute; `beat`, every tool event at most one per session\n * per minute. Unset: what the receiver's well-known file asked for (`receiver_tools`),\n * else `all`, so no receiver's view changes unless it asked. The outbox keeps every\n * event either way. */\n tools?: \"all\" | \"main\" | \"beat\";\n}\n\nexport interface Config {\n machine?: string;\n sinks: SinkConfig[];\n /** `status_line`: Claude Code's status line is ours (install --status-line). */\n harnesses: Record<string, { enabled?: boolean; created?: string[]; status_line?: boolean }>;\n update_check?: boolean;\n keep_days?: number;\n /** Ask a harness's own service for a session's name (an adapter's `remoteFacts`):\n * claude.ai's, for Claude Code. Off unless the person turns it on (`sessionpipe\n * titles on`): it means reading the harness's sign-in token on this machine. */\n remote_titles?: boolean;\n}\n\nexport const DEFAULT_CONFIG: Config = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };\n\nexport function readConfig(file: string = configFile()): Config {\n try {\n const j = JSON.parse(readFileSync(file, \"utf8\")) as Partial<Config>;\n return {\n ...DEFAULT_CONFIG,\n ...j,\n sinks: Array.isArray(j.sinks) ? j.sinks : [],\n harnesses: j.harnesses && typeof j.harnesses === \"object\" ? j.harnesses : {},\n };\n } catch {\n return { ...DEFAULT_CONFIG, sinks: [], harnesses: {} };\n }\n}\n\n/** Why `file` cannot be read as a config, or undefined when it can: missing, blank, or a\n * JSON object. readConfig reads such a file as the defaults, so hooks go on. */\nfunction configProblem(file: string): string | undefined {\n let text: string;\n try {\n text = readFileSync(file, \"utf8\");\n } catch (e) {\n // Every error readFileSync throws carries a Node error code.\n const code = (e as NodeJS.ErrnoException).code;\n return code === \"ENOENT\" ? undefined : `could not be read (${code})`;\n }\n if (!text.trim()) return undefined;\n if (text.charCodeAt(0) === 0xfeff) return \"starts with a byte-order mark (save it as UTF-8 without one)\";\n try {\n const j: unknown = JSON.parse(text);\n return j && typeof j === \"object\" && !Array.isArray(j) ? undefined : \"is not a JSON object\";\n } catch (e) {\n // Where, never V8's excerpt of the text: that can be part of a sink's token.\n const m = (e as Error).message;\n const at = (/line \\d+ column \\d+/.exec(m) ?? /position \\d+/.exec(m))?.[0];\n return `is not valid JSON${at ? ` at ${at}` : \"\"}`;\n }\n}\n\n/** Throw if `file` holds something readConfig could not read: writing the defaults over a\n * hand edit's typo would lose the machine's name and every sink with its token. A\n * command with an effect before its save calls this first, so it stops before any. */\nexport function assertConfigWritable(file: string = configFile()): void {\n const problem = configProblem(file);\n if (problem)\n throw new Error(\n `${tilde(file)} ${problem}, so it was not written over: that would lose its machine name and sinks. Fix it, or move it aside to start again, then run this again.`,\n );\n}\n\nexport function writeConfig(c: Config, file: string = configFile()): void {\n assertConfigWritable(file);\n mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });\n writeFileSync(file, `${JSON.stringify(c, null, 2)}\\n`, { mode: 0o600 });\n if (process.platform !== \"win32\" && existsSync(file)) {\n try {\n chmodSync(file, 0o600);\n } catch {}\n }\n}\n\n/** The machine's name: what the person configured, else the hostname without a\n * local suffix. Never guessed from anything else (a hand-typed name once linked 64\n * versions to nothing). */\nexport function machineName(c: Config, hostname: string): string {\n if (c.machine) return String(c.machine).slice(0, 80);\n return hostname.replace(/\\.(local|lan|home)$/i, \"\");\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// ULID: 48-bit ms timestamp + 80 random bits, Crockford base32. Monotonic within a\n// process so two events in the same millisecond still sort in emit order.\nimport { randomBytes } from \"node:crypto\";\n\nconst ALPHABET = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\nlet lastTime = 0;\nlet lastRand: number[] = [];\n\nfunction encodeTime(t: number): string {\n let out = \"\";\n let n = t;\n for (let i = 0; i < 10; i++) {\n out = ALPHABET[n % 32] + out;\n n = Math.floor(n / 32);\n }\n return out;\n}\n\nexport function ulid(now: number = Date.now()): string {\n if (now === lastTime) {\n // increment the random part\n for (let i = lastRand.length - 1; i >= 0; i--) {\n const v = (lastRand[i] as number) + 1;\n if (v < 32) {\n lastRand[i] = v;\n break;\n }\n lastRand[i] = 0;\n }\n } else {\n lastTime = now;\n lastRand = Array.from(randomBytes(16), (b) => b % 32);\n }\n return encodeTime(now) + lastRand.map((v) => ALPHABET[v]).join(\"\");\n}\n\nexport const isUlid = (s: string): boolean => /^[0-9A-HJKMNP-TV-Z]{26}$/.test(s);\n", "// SPDX-License-Identifier: Apache-2.0\n// From an adapter's output to a protocol event: id, time, the full session block\n// as known now, and the raw (tier 3) data. Filtering per sink happens later.\nimport type { AdapterEvent } from \"./adapters/types.js\";\nimport type { Event, Session } from \"./types.js\";\nimport { ulid } from \"./ulid.js\";\n\nexport interface EventContext {\n harness: { name: string; version?: string };\n session: Session;\n now?: number;\n}\n\nexport function makeEvent(a: AdapterEvent, ctx: EventContext): Event {\n const now = ctx.now ?? Date.now();\n const harness: Event[\"harness\"] = { name: ctx.harness.name, event: a.harnessEvent };\n if (ctx.harness.version) harness.version = ctx.harness.version;\n return {\n protocol: 1,\n id: ulid(now),\n type: a.type,\n time: new Date(now).toISOString(),\n tier: 3,\n harness,\n session: ctx.session,\n privacy: { rulesets: [], pii: false },\n data: a.data,\n };\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// The outbox: append-only JSONL per (harness, session) under\n// <state>/outbox/<harness>/<session>.jsonl, a per-session seq counter, a per-sink\n// cursor that moves only when the sink acknowledged, and a per-session lock so two\n// workers never interleave. Never lose, never duplicate.\n\nimport { randomBytes } from \"node:crypto\";\nimport {\n appendFileSync,\n closeSync,\n existsSync,\n mkdirSync,\n openSync,\n readdirSync,\n readFileSync,\n readSync,\n renameSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport path from \"node:path\";\nimport { safeId } from \"./paths.js\";\nimport type { Event } from \"./types.js\";\n\nexport interface OutboxRef {\n harness: string;\n session: string;\n}\n\nconst READ_CHUNK = 1024 * 1024;\nconst READ_MAX = 8 * 1024 * 1024;\n\nexport class Outbox {\n constructor(readonly root: string) {}\n\n dir(harness: string): string {\n return path.join(this.root, \"outbox\", safeId(harness));\n }\n file(ref: OutboxRef): string {\n return path.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);\n }\n private seqFile(ref: OutboxRef): string {\n return path.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);\n }\n\n /** The next seq for a session, persisted (survives a hibernating machine and a\n * restart; an in-memory counter would restart at 0 and collide). */\n nextSeq(ref: OutboxRef): number {\n const f = this.seqFile(ref);\n let n = -1;\n try {\n const t = readFileSync(f, \"utf8\").trim();\n if (/^\\d+$/.test(t)) n = Number(t);\n } catch {}\n // A missing or unreadable counter (a crash between truncate and write leaves it\n // empty) with an existing outbox: recover from the file's last line.\n if (n < 0) n = this.lastSeqInFile(ref) + 1;\n mkdirSync(path.dirname(f), { recursive: true, mode: 0o700 });\n writeFileSync(f, String(n + 1), { mode: 0o600 });\n return n;\n }\n\n private lastSeqInFile(ref: OutboxRef): number {\n const f = this.file(ref);\n if (!existsSync(f)) return -1;\n const lines = readFileSync(f, \"utf8\").trim().split(\"\\n\");\n for (let i = lines.length - 1; i >= 0; i--) {\n try {\n const j = JSON.parse(lines[i] as string) as Event;\n if (typeof j.session?.seq === \"number\") return j.session.seq;\n } catch {}\n }\n return -1;\n }\n\n append(ref: OutboxRef, events: Event[]): void {\n if (!events.length) return;\n mkdirSync(this.dir(ref.harness), { recursive: true, mode: 0o700 });\n appendFileSync(this.file(ref), `${events.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`, { mode: 0o600 });\n }\n\n /** Every session file, newest first. */\n sessions(): OutboxRef[] {\n const out: { ref: OutboxRef; mtime: number }[] = [];\n const base = path.join(this.root, \"outbox\");\n if (!existsSync(base)) return [];\n for (const h of readdirSync(base)) {\n const d = path.join(base, h);\n let names: string[] = [];\n try {\n names = readdirSync(d).filter((n) => n.endsWith(\".jsonl\"));\n } catch {\n continue;\n }\n for (const n of names) {\n try {\n out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync(path.join(d, n)).mtimeMs });\n } catch {}\n }\n }\n return out.sort((a, b) => b.mtime - a.mtime).map((x) => x.ref);\n }\n\n /** Read events after byte offset `from`; returns them and the new offset. Lines\n * are read whole; a partial trailing line (a write in progress) is left. A read\n * takes READ_CHUNK bytes at a time, not the rest of the file: a session file grows\n * to tens of MB and a drain takes it in batches of 50. It reads on until the batch\n * is full or the file ends, so lines that parse to nothing never stop a drain; one\n * line longer than the chunk gets a larger one, up to READ_MAX. */\n read(ref: OutboxRef, from: number, maxEvents = 500): { events: Event[]; offset: number } {\n const f = this.file(ref);\n if (!existsSync(f)) return { events: [], offset: from };\n const size = statSync(f).size;\n if (from >= size) return { events: [], offset: from };\n const fd = openSync(f, \"r\");\n try {\n const events: Event[] = [];\n let offset = from;\n let chunk = READ_CHUNK;\n while (events.length < maxEvents && offset < size) {\n const buf = Buffer.alloc(Math.min(size - offset, chunk));\n const n = readSync(fd, buf, 0, buf.length, offset);\n const text = buf.subarray(0, n).toString(\"utf8\");\n let pos = 0;\n let used = 0;\n while (events.length < maxEvents) {\n const nl = text.indexOf(\"\\n\", pos);\n if (nl < 0) break;\n const line = text.slice(pos, nl);\n pos = nl + 1;\n used += Buffer.byteLength(line, \"utf8\") + 1;\n if (!line.trim()) continue;\n try {\n events.push(JSON.parse(line) as Event);\n } catch {}\n }\n offset += used;\n if (used) continue;\n // No whole line in this chunk: a line longer than it, or a write in progress.\n if (offset + n >= size || chunk >= READ_MAX) break;\n chunk *= 2;\n }\n return { events, offset };\n } finally {\n closeSync(fd);\n }\n }\n\n /** Files older than `days` are pruned, and each sink's cursor into them. The seq\n * counter stays: a session resumed after the window counts on from where it was,\n * so a receiver's (harness, session, seq) dedup doesn't take its new events for\n * ones it already has, and the sinks read the new file from its start.\n * It all happens under the drain's `flush` lock, since a drain holds the whole\n * cursor map and writes it back: a file and its cursor keys go together, so a\n * session resumed in between can't keep a key past its new file's end. While a\n * drain runs, nothing is pruned (0); the next prune does it. It never throws: it\n * is a cleanup, and a hook's drain comes after it. */\n prune(days: number, now = Date.now()): number {\n let release: (() => void) | null = null;\n try {\n release = takeNamedLock(this.root, \"flush\");\n } catch {}\n if (!release) return 0;\n let n = 0;\n try {\n const cutoff = now - days * 86_400_000;\n const aged = this.sessions().filter((ref) => {\n try {\n return statSync(this.file(ref)).mtimeMs < cutoff;\n } catch {\n return false;\n }\n });\n // The keys go first: a worker that dies or fails to write here leaves a file\n // with no cursor, which a sink sends again (a receiver's dedup takes it), never\n // a cursor past the end of a file a resume starts. Each file is checked again\n // right before it goes: one a hook appended to meanwhile stays.\n this.dropStaleCursors(new Set(aged.map((ref) => `${ref.harness}/${ref.session}`)));\n for (const ref of aged) {\n const f = this.file(ref);\n try {\n if (statSync(f).mtimeMs < cutoff) {\n unlinkSync(f);\n n++;\n }\n } catch {}\n }\n } catch {\n } finally {\n release();\n }\n return n;\n }\n\n /** A sink's cursor into a session file about to be pruned (`gone`), or one already\n * missing (an earlier prune, an older release, by hand) is removed. The caller\n * holds the `flush` lock. */\n private dropStaleCursors(gone: Set<string>): void {\n let names: string[] = [];\n try {\n names = readdirSync(path.join(this.root, \"cursors\")).filter((n) => n.endsWith(\".json\"));\n } catch {}\n const stale = (c: Record<string, number>): string[] =>\n Object.keys(c).filter((k) => {\n if (gone.has(k)) return true;\n const i = k.indexOf(\"/\");\n return !existsSync(this.file({ harness: k.slice(0, i), session: k.slice(i + 1) }));\n });\n for (const n of names) {\n const s = new Cursors(this.root, n.slice(0, -5));\n const c = s.read();\n const keys = stale(c);\n if (!keys.length) continue;\n for (const k of keys) delete c[k];\n s.write(c);\n }\n }\n\n delete(ref: OutboxRef): void {\n // The cursor keys go before the files: a death between leaves the old file with\n // no cursor, so the next drain sends it whole and the person, who sees no \"removed\"\n // line, runs forget again; the other way round leaves a cursor past the end of\n // the file a session that goes on starts, and nothing says so.\n this.dropCursors(ref);\n for (const f of [this.file(ref), this.seqFile(ref)]) {\n try {\n unlinkSync(f);\n } catch {}\n }\n }\n\n /** The key every sink's cursor holds for a session is removed with the session's\n * file: the key alone pointed into the old file, and a session that went on (still\n * running, or resumed later) had its new file's first events unsent until it grew\n * past that offset. Every file under <state>/cursors is taken (a sink since taken\n * out of the config still has one; one that doesn't parse reads as empty and is\n * left), by the key a drain writes (the file's own name, `safeId`) and by the ref\n * as given. The caller holds the drain's `flush` lock: a drain holds the whole\n * cursor map and writes it back, so a key dropped under it would come back. */\n private dropCursors(ref: OutboxRef): void {\n const keys = new Set([`${ref.harness}/${ref.session}`, `${safeId(ref.harness)}/${safeId(ref.session)}`]);\n let names: string[] = [];\n try {\n names = readdirSync(path.join(this.root, \"cursors\")).filter((n) => n.endsWith(\".json\"));\n } catch {\n return;\n }\n for (const n of names) {\n const s = new Cursors(this.root, n.slice(0, -5));\n const c = s.read();\n const had = Object.keys(c).filter((k) => keys.has(k));\n if (!had.length) continue;\n for (const k of had) delete c[k];\n s.write(c);\n }\n }\n}\n\n/** Per-sink cursor: byte offset per session file, moved only on a 2xx. */\nexport class Cursors {\n constructor(\n readonly root: string,\n readonly sink: string,\n ) {}\n private file(): string {\n return path.join(this.root, \"cursors\", `${safeId(this.sink)}.json`);\n }\n /** The map; empty when the file is missing, cut off mid-write, or holds anything\n * but an object (`null`, a list), so a drain never indexes into those. */\n read(): Record<string, number> {\n try {\n const c = JSON.parse(readFileSync(this.file(), \"utf8\")) as unknown;\n return c && typeof c === \"object\" && !Array.isArray(c) ? (c as Record<string, number>) : {};\n } catch {\n return {};\n }\n }\n write(c: Record<string, number>): void {\n const f = this.file();\n mkdirSync(path.dirname(f), { recursive: true, mode: 0o700 });\n const tmp = `${f}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify(c), { mode: 0o600 });\n renameSync(tmp, f);\n }\n key(ref: OutboxRef): string {\n return `${ref.harness}/${ref.session}`;\n }\n}\n\n/** A lock per session so two workers never interleave. Stale once the worker that\n * took it is gone (its 15 s deadline ends it without a release), or after 5 min. */\nexport function takeLock(root: string, ref: OutboxRef, staleMs = 5 * 60_000): (() => void) | null {\n return takeNamedLock(root, `${safeId(ref.harness)}-${safeId(ref.session)}`, staleMs);\n}\n\n/** A named lock under <root>/locks, held by one process at a time: the file names\n * its pid and a nonce. Stale once that process is gone, or after `staleMs`. A stale\n * lock is taken over by renaming it aside first: of two workers that both judged it\n * stale, one rename wins, and one that moved a fresh lock instead (another worker's,\n * made in between) puts it back and stands down. The release deletes the file only\n * while it is still this taker's, so a lock taken over can't be wiped by its old\n * holder. Two holders at once used to mean two drains reading the same cursors and\n * sending the same batch (issue #208's one-request-a-second pace rests on one). */\nexport function takeNamedLock(root: string, name: string, staleMs = 5 * 60_000): (() => void) | null {\n const dir = path.join(root, \"locks\");\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n const f = path.join(dir, `${name}.lock`);\n const nonce = randomBytes(8).toString(\"hex\");\n const mine = `${process.pid} ${nonce}`;\n let body: string | null = null;\n try {\n body = readFileSync(f, \"utf8\");\n const stale = Date.now() - statSync(f).mtimeMs > staleMs || !processAlive(Number(body.split(\" \")[0]));\n if (!stale) return null;\n } catch {\n body = null;\n }\n if (body !== null) {\n const aside = `${f}.${process.pid}.${nonce}.stale`;\n try {\n renameSync(f, aside);\n } catch {\n return null;\n }\n // The aside name is this call's own: nobody else reads or removes it.\n const moved = readFileSync(aside, \"utf8\");\n unlinkSync(aside);\n if (moved !== body) {\n try {\n writeFileSync(f, moved, { flag: \"wx\" });\n } catch {}\n return null;\n }\n }\n try {\n writeFileSync(f, mine, { flag: \"wx\" });\n } catch {\n return null;\n }\n return () => {\n try {\n if (readFileSync(f, \"utf8\") === mine) unlinkSync(f);\n } catch {}\n };\n}\n\n/** `takeNamedLock`, tried again every `everyMs` until `forMs` have passed: for a\n * command whose work goes under a lock a worker holds for seconds (a drain). null\n * once the time is up. */\nexport async function waitNamedLock(\n root: string,\n name: string,\n forMs: number,\n everyMs = 100,\n): Promise<(() => void) | null> {\n for (let waited = 0; ; waited += everyMs) {\n const release = takeNamedLock(root, name);\n if (release || waited >= forMs) return release;\n await new Promise((r) => setTimeout(r, everyMs));\n }\n}\n\n/** Whether the process a lock or a claimed job names is still running. A pid that\n * can't be read counts as running: the age rule decides then. */\nexport function processAlive(pid: number): boolean {\n if (!Number.isInteger(pid) || pid <= 0) return true;\n try {\n process.kill(pid, 0);\n return true;\n } catch (e) {\n return (e as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// pii@1 \u2014 an opt-in second pass that reduces identifiers (spec/PRIVACY.md \u00A72). It\n// does not promise anonymity; it removes the obvious.\nimport { createHash } from \"node:crypto\";\n\nexport const RULESET_PII = \"pii@1\";\n\nconst EMAIL = /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/g;\n/** E.164 and common national forms: an optional +, 7\u201315 digits with separators,\n * optional area-code parentheses. A version number (22.23.1) or an IP never matches:\n * the run must not sit between dots, and needs \u2265 7 digits. An ISO date (2026-10-31)\n * has a phone's shape, so it is tried first (group 1) and kept, but only when nothing\n * after it could carry the run on: no letter or digit, and no separator then two digits\n * (a phone's group is two to four; one digit, as in \"2026-10-31 5 files\", never is),\n * unless that is a time's hour (\" 12:\", 00\u201323), and not right after a \")\" or a \"+\"\n * with a country code: a phone's prefix this pass could not take (\"x(415) 2026-10-31\",\n * \"[phone]+1 2026-10-31\") would take the date on the next one. Any other date is read\n * as before. The date may follow a `+` (a diff's added line), so a second pass over\n * \"[phone]+2026-10-31\" keeps it too. */\nconst PHONE =\n /(?<![\\w.])(?:(?<!\\)[ .-]?|\\+\\d{1,3}[ .-]?)(\\+?(?:19|20)\\d{2}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\\d|3[01]))(?:(?= (?:[01]?\\d|2[0-3]):)|(?!\\w|[ .-]\\d\\d))|(?:\\+\\d{1,3}[ .-]?)?(?:\\(\\d{2,4}\\)[ .-]?)?\\d{2,4}(?:[ .-]?\\d{2,4}){1,3}(?!\\w))/g;\n\nfunction looksPhone(s: string): boolean {\n const digits = s.replace(/\\D/g, \"\");\n if (digits.length < 7 || digits.length > 15) return false;\n // \"port 8891, pr 12345\": a bare run with no separator or + is a number, not a phone.\n if (!/[+() .-]/.test(s)) return false;\n // \"22.23.1\", \"185.199.108.153\": dots alone are a version or an address.\n if (!/[+() -]/.test(s)) return false;\n return true;\n}\n\n/** Replace emails and phone numbers, and write `home` as `~`. */\nexport function reducePii(text: string, home?: string): string {\n if (!text) return text;\n let out = text.replace(EMAIL, \"[email]\");\n out = out.replace(PHONE, (m, date?: string) => (!date && looksPhone(m) ? \"[phone]\" : m));\n if (home) {\n const h = home.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n out = out.replace(new RegExp(`(?<![A-Za-z0-9_])${h}(?=/|$|\\\\s|[\"'\\`])`, \"g\"), \"~\");\n }\n return out;\n}\n\n/** The machine's name under pii@1: the first 8 hex of its SHA-256. */\nexport function hashMachine(name: string): string {\n return createHash(\"sha256\").update(name).digest(\"hex\").slice(0, 8);\n}\n\n/** Apply pii@1 to every string in a value; `home` becomes `~`. */\nexport function reducePiiDeep<T>(v: T, home?: string): T {\n if (typeof v === \"string\") return reducePii(v, home) as T;\n if (Array.isArray(v)) return v.map((x) => reducePiiDeep(x, home)) as T;\n if (v && typeof v === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [k, x] of Object.entries(v as Record<string, unknown>)) out[k] = reducePiiDeep(x, home);\n return out as T;\n }\n return v;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// secrets@1 \u2014 credentials out of every string, on the person's machine, before an\n// event reaches the outbox. Ported from spacesheep-cli lib/redact.js (Michael\n// Makarov, MIT \u2192 Apache-2.0, see NOTICE). The ruleset is defined in\n// spec/PRIVACY.md \u00A72 and pinned by conformance/redaction/secrets.json.\n//\n// Every pattern is linear: bounded repetition, no nested quantifiers. The\n// adversarial vectors (password_ \u00D7 5000, a 50 000-space run) pin that.\n\nexport const RULESET_SECRETS = \"secrets@1\";\nexport const REDACTED = \"[redacted]\";\n\n/** Token formats that are a credential by their shape alone. Each is anchored on a\n * vendor prefix, so a commit sha or a UUID never matches. */\nconst TOKEN_PATTERNS: RegExp[] = [\n /-----BEGIN [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----[\\s\\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----|$)/g,\n /\\b(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}\\b/g, // AWS access key id\n /\\bgh[pousr]_[A-Za-z0-9]{30,}\\b/g, // GitHub\n /\\bgithub_pat_[A-Za-z0-9_]{30,}\\b/g,\n /\\bglpat-[A-Za-z0-9_-]{20,}\\b/g, // GitLab\n /\\bxox[abposre]-[A-Za-z0-9-]{10,}\\b/g, // Slack\n /\\bhttps:\\/\\/hooks\\.slack\\.com\\/services\\/[A-Za-z0-9/_-]+/g,\n /\\bhttps:\\/\\/(?:ptb\\.|canary\\.)?discord(?:app)?\\.com\\/api\\/webhooks\\/[0-9]+\\/[A-Za-z0-9_-]+/g,\n /\\bsk-(?:ant-|proj-|svcacct-|admin-|or-v1-)?[A-Za-z0-9_-]{20,}\\b/g, // Anthropic, OpenAI, DeepSeek, OpenRouter\n /\\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\\b/g, // Stripe secret / restricted\n /\\bwhsec_[A-Za-z0-9+/=]{20,}/g, // Stripe / Svix / Standard Webhooks secret\n /\\bAIza[0-9A-Za-z_-]{35}\\b/g, // Google API key\n /\\bya29\\.[0-9A-Za-z_-]{20,}/g, // Google OAuth access token\n /\\b1\\/\\/0[0-9A-Za-z_-]{30,}/g, // Google OAuth refresh token\n /\\bGOCSPX-[0-9A-Za-z_-]{20,}/g, // Google OAuth client secret\n /\\bxai-[A-Za-z0-9]{30,}\\b/g,\n /\\bgsk_[A-Za-z0-9]{30,}\\b/g, // Groq\n /\\bpplx-[A-Za-z0-9]{30,}\\b/g, // Perplexity\n /\\bhf_[A-Za-z0-9]{30,}\\b/g, // Hugging Face\n /\\bpcsk_[A-Za-z0-9_]{30,}\\b/g, // Pinecone\n /\\bnpm_[A-Za-z0-9]{30,}\\b/g,\n /\\bpypi-[A-Za-z0-9_-]{50,}/g,\n /\\bSG\\.[A-Za-z0-9_-]{16,}\\.[A-Za-z0-9_-]{16,}/g, // SendGrid\n /\\bshp(?:at|ss|ca|pa)_[a-fA-F0-9]{32}\\b/g, // Shopify\n /\\bdop_v1_[a-f0-9]{64}\\b/g, // DigitalOcean\n /\\b(?:lin_api|lin_oauth)_[A-Za-z0-9]{30,}\\b/g, // Linear\n /\\b(?:ntn|secret)_[A-Za-z0-9]{40,}\\b/g, // Notion\n /\\bsbp_[a-f0-9]{40}\\b/g, // Supabase\n /\\bfigd_[A-Za-z0-9_-]{30,}/g, // Figma\n /\\bdp\\.(?:pt|st|sa|ct)\\.[A-Za-z0-9]{30,}/g, // Doppler\n /\\bgrn_[A-Za-z0-9_-]{20,}/g, // Granola\n /\\bss_[a-f0-9]{32,}\\b/g, // spacesheep API keys\n /\\b[0-9]{8,10}:AA[A-Za-z0-9_-]{33}\\b/g, // Telegram bot token\n /\\beyJ[A-Za-z0-9_-]{10,}\\.eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}/g, // JWT\n];\n/** `scheme://user:password@host` \u2014 the password goes, the user and host stay. */\nconst URL_CREDENTIALS = /\\b([a-z][a-z0-9+.-]{1,20}:\\/\\/[^\\s:/@'\"`<>]{1,100}:)([^\\s@/'\"`<>]{1,200})(@)/gi;\n/** `Authorization: Bearer \u2026` / `Basic \u2026`, and a bare `Bearer <token>` in a curl line. */\nconst AUTH_HEADER = /\\b((?:Bearer|Basic|Token)\\s+)([A-Za-z0-9._~+/=-]{16,})/g;\n// The words that name a secret, in prose. `key` alone is too common (\"the key\n// point\"), so it only counts with a qualifier.\nconst SECRET_WORD =\n \"(?:pass(?:word|wd|phrase|code)?s?|pwd|secret(?:[ _-]?key)?s?|client[ _-]?secret|api[ _-]?keys?|apikeys?|access[ _-]?keys?|access[ _-]?tokens?|auth[ _-]?tokens?|refresh[ _-]?tokens?|private[ _-]?keys?|tokens?|credentials?|creds)\";\n/** Strict form: a NAME, an explicit separator (`=`, `:`, `=>`), a value. The name is\n * one flat run of identifier characters judged in code (isSecretName), never by a\n * nested pattern. Only a secret name goes on to consume its value \u2014 otherwise\n * `https:` would swallow the `?token=\u2026` behind it. */\nconst NAME_SEP = /(?<![A-Za-z0-9_])([A-Za-z][A-Za-z0-9_-]{1,79})([\"']?\\s{0,3}(?::=|=>|=|:)\\s{0,3})/g;\nconst VALUE = /\"([^\"\\n]{1,300})\"|'([^'\\n]{1,300})'|`([^`\\n]{1,300})`|([^\\s\"'`,;&)}\\]]{1,300})/y;\n\nfunction redactAssignments(text: string): string {\n let out = \"\";\n let last = 0;\n NAME_SEP.lastIndex = 0;\n for (let m = NAME_SEP.exec(text); m; m = NAME_SEP.exec(text)) {\n const name = m[1] as string;\n if (!isSecretName(name)) continue;\n VALUE.lastIndex = NAME_SEP.lastIndex;\n const v = VALUE.exec(text);\n if (!v) continue;\n const value = (v[1] ?? v[2] ?? v[3] ?? v[4]) as string;\n NAME_SEP.lastIndex = VALUE.lastIndex;\n if (isPlaceholder(value) || value.includes(REDACTED)) continue;\n // `max_tokens: 4096`, `input_tokens=12k` \u2014 a token COUNT, not a token.\n if (/tokens?$/i.test(name) && /^\\d+(?:[.,]\\d+)*[km]?$/i.test(value)) continue;\n const q = v[1] != null ? '\"' : v[2] != null ? \"'\" : v[3] != null ? \"`\" : \"\";\n out += text.slice(last, v.index) + q + REDACTED + q;\n last = VALUE.lastIndex;\n }\n return last ? out + text.slice(last) : text;\n}\n\n/** Is this identifier the name of a secret? `DB_PASSWORD`, `apiKey`, `client-secret`,\n * `AWS_SECRET_ACCESS_KEY`, `GITHUB_TOKEN`; an env-style `STRIPE_KEY` too. Not\n * `password_hash`, `tokenizer`, `sort_key`. */\nexport function isSecretName(name: string): boolean {\n if (/^[A-Z0-9_]+_KEY$/.test(name)) return true;\n const flat = name.toLowerCase().replace(/[_-]/g, \"\");\n return /(?:pass(?:word|wd|phrase|code)|pwd|secret|secretkey|secretaccesskey|clientsecret|apikey|accesskey|accesstoken|authtoken|refreshtoken|privatekey|token|credential|creds)s?$/.test(\n flat,\n );\n}\n\n/** Loose form: prose. \"password is Hunter2!\", \"the token was ghx\u2026\". Only a value\n * that looks generated counts, so \"the password is wrong\" passes untouched.\n * Whitespace runs are bounded so a long run can't make the match quadratic. */\nconst PROSE = new RegExp(\n `(\\\\b${SECRET_WORD}(?:[ \\\\t]{1,3}(?:is|was|=|:|to|as|of|for[ \\\\t]{1,3}\\\\S{1,40}[ \\\\t]{1,3}is))?[ \\\\t]{1,3})([\"'\\`]?)([^\\\\s\"'\\`]{6,200})\\\\2`,\n \"gi\",\n);\n\n/** A value that is plainly not the secret itself: a type, a variable, a template, an\n * already-redacted marker, an env lookup. */\nfunction isPlaceholder(v: string): boolean {\n const s = v.replace(/[.,;:!?]+$/, \"\");\n if (!s) return true;\n if (\n /^(?:string|number|boolean|bool|str|int|any|unknown|null|undefined|none|nil|true|false|optional|required|text|varchar|password|secret|token|env|string\\[\\]|object|bytes)$/i.test(\n s,\n )\n )\n return true;\n if (/^\\[?redacted\\]?$/i.test(s) || /^\\*+$/.test(s) || /^x{3,}$/i.test(s) || /^\\.{3}$|^\u2026$/.test(s)) return true;\n if (/^[<{$%]/.test(s)) return true; // <your-key>, ${VAR}, {{var}}, $VAR, %VAR%\n if (\n /^(?:process\\.env|env|os\\.environ|c\\.env|this\\.env|import\\.meta\\.env|getenv|config|settings|secrets|args|opts|options|req|request|body|params|input|data|user|self|this)\\b/i.test(\n s,\n )\n )\n return true;\n if (/^[A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*)+$/.test(s)) return true; // a member path: form.password\n if (/[(]/.test(s)) return true; // a call: hash(pw), getSecret()\n if (/^(?:your|my|the|a|an|some|new|old)[-_ ]/i.test(s)) return true; // your-api-key\n return false;\n}\n\n/** Does a prose value look like something generated, rather than an English word? */\nfunction looksSecret(v: string): boolean {\n const s = v.replace(/[.,;:!?)]+$/, \"\");\n if (s.length < 6) return false;\n if (isPlaceholder(s)) return false;\n if (/^https?:\\/\\//i.test(s)) return false; // a link, handled by URL_CREDENTIALS if it carries one\n if (/^[a-z]+(?:[-'][a-z]+)*$/.test(s)) return false; // a lowercase word\n if (/^[A-Z][a-z]+$/.test(s)) return false; // a capitalised word\n if (/^\\d+(?:[.,]\\d+)*[a-z%]*$/i.test(s)) return false; // a count: 4096, 128k, 3.5, 50%\n if (/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/.test(s)) return false; // an env var's NAME\n const kinds = [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/].filter((r) => r.test(s)).length;\n return kinds >= 3 || (/[0-9]/.test(s) && /[A-Za-z]/.test(s));\n}\n\n/** Redact every credential in `text`. Returns the same string when there is none. */\nexport function redactSecrets(text: string): string {\n if (!text) return text;\n let out = text;\n for (const re of TOKEN_PATTERNS) out = out.replace(re, REDACTED);\n out = out.replace(URL_CREDENTIALS, (_m, head: string, _pw: string, at: string) => `${head}${REDACTED}${at}`);\n out = out.replace(AUTH_HEADER, (_m, head: string) => `${head}${REDACTED}`);\n out = redactAssignments(out);\n out = out.replace(PROSE, (m, head: string, q: string, v: string) =>\n !looksSecret(v) || v.includes(REDACTED) ? m : `${head}${q}${REDACTED}${q}`,\n );\n return out;\n}\n\n/** Did redaction change anything? */\nexport function hasSecret(text: string): boolean {\n return !!text && redactSecrets(text) !== text;\n}\n\n/** Redact every string in a JSON-able value (keys are left alone). */\nexport function redactDeep<T>(v: T): T {\n if (typeof v === \"string\") return redactSecrets(v) as T;\n if (Array.isArray(v)) return v.map(redactDeep) as T;\n if (v && typeof v === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [k, x] of Object.entries(v as Record<string, unknown>)) out[k] = redactDeep(x);\n return out as T;\n }\n return v;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// filter(event, tier, pii): the pure function every sink runs an event through.\n// Removes by ALLOWLIST per type and tier (spec/PRIVACY.md \u00A71), redacts every string\n// above tier 0 with secrets@1, and runs pii@1 when asked. Returns null when the\n// type is not carried at the tier. The conformance runner runs the same function.\nimport type { Event, Tier } from \"../types.js\";\nimport { hashMachine, RULESET_PII, reducePiiDeep } from \"./pii.js\";\nimport { RULESET_SECRETS, redactDeep } from \"./secrets.js\";\n\n/** The lowest tier that carries each known type (kept in step with schema/v1.ts). */\nexport const TYPE_TIER: Record<string, Tier> = {\n \"session.started\": 0,\n \"session.heartbeat\": 0,\n \"session.ended\": 0,\n \"session.backfill\": 0,\n \"session.forgotten\": 0,\n \"turn.started\": 0,\n \"turn.ended\": 0,\n \"turn.transcript\": 2,\n \"turn.progress\": 2,\n \"tool.started\": 1,\n \"tool.ended\": 1,\n \"files.changed\": 1,\n \"attention.needed\": 0,\n \"attention.cleared\": 0,\n \"subagent.started\": 1,\n \"subagent.ended\": 1,\n \"context.compacted\": 1,\n \"limits.observed\": 1,\n \"delegation.started\": 0,\n \"delegation.ended\": 0,\n native: 3,\n};\n\n/** data keys allowed per type at tier N (cumulative: tier 1 includes tier 0's). */\nconst DATA_ALLOW: Record<string, Partial<Record<Tier, string[]>>> = {\n \"session.started\": { 0: [\"source\"] },\n \"session.heartbeat\": { 0: [] },\n \"session.ended\": { 0: [\"reason\"] },\n \"session.backfill\": { 0: [\"started_at\", \"last_at\", \"turns\"] },\n \"session.forgotten\": { 0: [] },\n \"turn.started\": { 0: [\"turn_id\", \"submitted_by\", \"injected\"], 1: [\"prompt_chars\"] },\n \"turn.ended\": { 0: [\"turn_id\", \"reason\", \"ms\", \"background\"], 1: [\"error\"] },\n \"turn.transcript\": { 2: [\"turn\", \"user\", \"assistant\", \"at\", \"injected\"] },\n \"turn.progress\": { 2: [\"turn\", \"user\", \"assistant\", \"at\", \"injected\"] },\n \"tool.started\": { 1: [\"tool\", \"call_id\", \"turn_id\", \"agent_id\"], 3: [\"input\"] },\n \"tool.ended\": { 1: [\"tool\", \"call_id\", \"turn_id\", \"agent_id\", \"ms\", \"ok\", \"error\"], 3: [\"input\", \"output\"] },\n \"files.changed\": { 1: [\"paths\"] },\n \"attention.needed\": { 0: [\"attention_id\", \"kind\"], 1: [\"tool\", \"message\", \"agent_id\"] },\n \"attention.cleared\": { 0: [\"attention_id\", \"how\"] },\n \"subagent.started\": { 1: [\"agent_id\", \"agent_type\"] },\n \"subagent.ended\": { 1: [\"agent_id\", \"agent_type\", \"background\"] },\n \"context.compacted\": { 1: [\"trigger\"] },\n \"limits.observed\": { 1: [\"windows\", \"plan\", \"account_id\"] },\n \"delegation.started\": { 0: [\"ask_id\", \"via\", \"harness\", \"to\"], 1: [\"text\"] },\n \"delegation.ended\": { 0: [\"ask_id\", \"to\", \"ok\", \"ms\", \"error\", \"relayed\"], 2: [\"reply\"] },\n};\n\nconst SESSION_ALLOW = [\n \"id\",\n \"seq\",\n \"machine\",\n \"cwd\",\n \"repo\",\n \"branch\",\n \"model\",\n \"title\",\n \"title_source\",\n \"url\",\n \"account_id\",\n \"attended\",\n \"parent_id\",\n \"launched_by\",\n \"delegated_by\",\n];\nconst MAX_SIDE = 20_000;\nconst MAX_IO = 64 * 1024;\n\nfunction allowedKeys(type: string, tier: Tier): string[] | null {\n const table = DATA_ALLOW[type];\n if (!table) return null; // unknown type: kept whole (receivers keep extra keys), redacted\n const out: string[] = [];\n for (let t = 0; t <= tier; t++) for (const k of table[t as Tier] ?? []) out.push(k);\n return out;\n}\n\nfunction cap(v: unknown, max: number): unknown {\n if (typeof v === \"string\") return v.length > max ? `${v.slice(0, max)}\u2026` : v;\n if (v && typeof v === \"object\") {\n const s = JSON.stringify(v);\n return s.length > max ? `${s.slice(0, max)}\u2026` : v;\n }\n return v;\n}\n\n/** At most `max` characters, keeping the end: an ellipsis, then the last max \u2212 1. */\nexport function capTail(v: string, max: number): string {\n return v.length > max ? `\u2026${v.slice(v.length - max + 1)}` : v;\n}\n\nexport interface FilterOptions {\n /** The person's home directory, for pii@1's `~` rewrite. */\n home?: string;\n}\n\n/** The event as a sink at `tier` may see it, or null when the type is above the tier. */\nexport function filterEvent(event: Event, tier: Tier, pii: boolean, opts: FilterOptions = {}): Event | null {\n const minTier = TYPE_TIER[event.type];\n if (minTier !== undefined && minTier > tier) return null;\n\n const session: Record<string, unknown> = {};\n for (const k of SESSION_ALLOW)\n if (event.session[k as keyof typeof event.session] !== undefined)\n session[k] = event.session[k as keyof typeof event.session];\n\n let data: Record<string, unknown> = {};\n const keys = allowedKeys(event.type, tier);\n if (keys === null) data = { ...event.data };\n else for (const k of keys) if (event.data[k] !== undefined) data[k] = event.data[k];\n if (typeof data.user === \"string\") data.user = cap(data.user, MAX_SIDE);\n // A turn still running is read for where it is now: its reply keeps the end.\n if (typeof data.assistant === \"string\")\n data.assistant = event.type === \"turn.progress\" ? capTail(data.assistant, MAX_SIDE) : cap(data.assistant, MAX_SIDE);\n if (data.input !== undefined) data.input = cap(data.input, MAX_IO);\n if (data.output !== undefined) data.output = cap(data.output, MAX_IO);\n if (typeof data.message === \"string\") data.message = cap(data.message, 200);\n // What an ask asked, and what came back (PROTOCOL.md \u00A74 delegation.*): the daemon cuts\n // them too, but the cap is this function's promise.\n if (event.type === \"delegation.started\" && typeof data.text === \"string\") data.text = cap(data.text, 200);\n if (event.type === \"delegation.ended\" && typeof data.reply === \"string\") data.reply = cap(data.reply, MAX_SIDE);\n\n const rulesets: string[] = [];\n let out: Event = { ...event, tier, session: session as Event[\"session\"], data, privacy: { rulesets, pii } };\n if (tier > 0) {\n // Redact, then the caps above already applied: a cut before redaction could leave half a key.\n out = { ...out, session: redactDeep(out.session), data: redactDeep(out.data) };\n rulesets.push(RULESET_SECRETS);\n }\n if (pii) {\n const s = reducePiiDeep(out.session, opts.home) as Record<string, unknown>;\n if (typeof s.machine === \"string\") s.machine = hashMachine(s.machine);\n delete s.account_id;\n const d = reducePiiDeep(out.data, opts.home) as Record<string, unknown>;\n // limits.observed names the account its numbers belong to: the same identifier\n // pii@1 drops from the session block, so it goes the same way.\n delete d.account_id;\n out = { ...out, session: s as Event[\"session\"], data: d };\n rulesets.push(RULESET_PII);\n }\n out.privacy = { rulesets, pii };\n return out;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// A session's short id (spec/PROTOCOL.md \u00A73): the first 8 hex digits of\n// sha256(\"sessionpipe.session:\" + harness + \":\" + id). What a person reads in a log\n// line, a notification, a search box; derived, never sent. Not a prefix of the id\n// itself: Codex ids are time-ordered (UUIDv7), and two sessions started in the same\n// minute share their first eight characters.\nimport { createHash } from \"node:crypto\";\n\nexport function shortId(harness: string, id: string): string {\n return createHash(\"sha256\").update(`sessionpipe.session:${harness}:${id}`).digest(\"hex\").slice(0, 8);\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// file:PATH \u2014 append filtered events as JSONL. The zero-server sink.\nimport { appendFileSync, mkdirSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { Event, Tier } from \"../types.js\";\nimport type { DeliveryResult, Sink } from \"./types.js\";\n\nexport class FileSink implements Sink {\n constructor(\n readonly name: string,\n readonly file: string,\n readonly tier: Tier,\n readonly pii: boolean,\n ) {}\n async send(events: Event[]): Promise<DeliveryResult> {\n try {\n mkdirSync(path.dirname(this.file), { recursive: true });\n appendFileSync(this.file, `${events.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`);\n return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };\n } catch (e) {\n return { ok: false, acknowledged: 0, error: String((e as Error).message), action: \"retry\" };\n }\n }\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// stdout \u2014 one JSON line per event to a writable (the worker's stdout is ignored\n// when spawned by a hook, so this sink is for `sessionpipe tail` and tests).\nimport type { Event, Tier } from \"../types.js\";\nimport type { DeliveryResult, Sink } from \"./types.js\";\n\nexport class StdoutSink implements Sink {\n constructor(\n readonly name: string,\n readonly tier: Tier,\n readonly pii: boolean,\n private readonly write: (line: string) => void = (l) => process.stdout.write(`${l}\\n`),\n ) {}\n async send(events: Event[]): Promise<DeliveryResult> {\n try {\n for (const e of events) this.write(JSON.stringify(e));\n return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };\n } catch (e) {\n // A closed pipe (`sessionpipe tail | head`): a result, like every other sink.\n return { ok: false, acknowledged: 0, error: String((e as Error).message ?? e), action: \"retry\" };\n }\n }\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Sessions the daemon started under the command's name (spec/CONTROL.md \u00A76 `start`).\n// Codex and Antigravity pick a new session's id themselves, but a `start` names the\n// session (`codex:<id>`), and that name is what the person's receiver knows it by. So\n// the moment a run reveals the harness's own id, the daemon records it here:\n//\n// state/control/aliases.json {\"codex:<command's id>\": {\"id\": \"<harness's id>\",\n// \"cwd\": \"<folder>\", \"at\": \"<iso>\",\n// \"permission\": \"<level>\", \"model\": \"<id>\"}, \u2026} (0600)\n//\n// and everything the machine reports uses the command's id: the daemon's acks, a later\n// prompt (resumed under the harness's id), and the hook events, which the worker renames\n// before it builds them (run.ts). Small, so it is read whole wherever it is needed.\nimport { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { type StartOptions, startOptionsOf } from \"./store.js\";\n\nexport interface Alias {\n /** The harness's own session id. */\n id: string;\n /** The folder the session runs in (the start's). */\n cwd: string;\n at: string;\n /** What the start ran with (CONTROL.md \u00A76, Options); absent for an alias recorded\n * before 0.10.0, whose later runs keep the machine's mode. */\n permission?: StartOptions[\"permission\"];\n model?: string;\n}\nexport type Aliases = Record<string, Alias>;\n\nexport const aliasFile = (controlDir: string): string => path.join(controlDir, \"aliases.json\");\n\nexport function readAliases(file: string): Aliases {\n try {\n const j = JSON.parse(readFileSync(file, \"utf8\")) as unknown;\n if (!j || typeof j !== \"object\" || Array.isArray(j)) return {};\n const out: Aliases = {};\n for (const [k, v] of Object.entries(j as Record<string, unknown>)) {\n const a = v as Partial<Alias> | null;\n if (a && typeof a.id === \"string\" && a.id && typeof a.cwd === \"string\")\n out[k] = { id: a.id, cwd: a.cwd, at: typeof a.at === \"string\" ? a.at : \"\", ...startOptionsOf(a) };\n }\n return out;\n } catch {\n return {};\n }\n}\n\n/** Written whole and renamed into place, 0600 in a 0700 folder. */\nexport function writeAliases(file: string, all: Aliases): void {\n mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });\n const tmp = `${file}.${process.pid}.tmp`;\n writeFileSync(tmp, `${JSON.stringify(all, null, 2)}\\n`, { mode: 0o600 });\n renameSync(tmp, file);\n try {\n chmodSync(file, 0o600);\n } catch {}\n}\n\n/** The command's id a harness's own session goes by, or null when it was never renamed. */\nexport function receiverIdOf(all: Aliases, harness: string, id: string): string | null {\n const prefix = `${harness}:`;\n for (const [k, a] of Object.entries(all)) if (a.id === id && k.startsWith(prefix)) return k.slice(prefix.length);\n return null;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Where this machine's secrets live: each sink's token (and Webhooks secret) and each\n// paired receiver's machine token. In the operating system's keychain when this person\n// has one unlocked \u2014 the macOS Keychain, or the Secret Service (GNOME Keyring, KWallet)\n// in a Linux desktop session \u2014 and otherwise in config.json / control.json at mode\n// 0600, which is all a headless server has (the same as ~/.ssh). A secret in a store\n// leaves `token_in` in the file and no token.\n//\n// What a store protects against, plainly: a dotfiles repo, a backup, a synced\n// ~/.config, a `cat` over a shoulder. Not a program running as you: `security` hands an\n// item it wrote back without asking, which is also how the hook's worker reads it.\n\nimport { spawnSync } from \"node:child_process\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { configFile, readConfig, type SinkConfig, writeConfig } from \"@sessionpipe/core\";\n\nexport type SecretStore = \"keychain\" | \"secret-service\";\nexport type StoreName = SecretStore | \"file\";\n\nconst SERVICE = \"sessionpipe\";\n\nexport type Runner = (cmd: string, args: string[], input?: string) => { status: number | null; stdout: string };\nexport const defaultRunner: Runner = (cmd, args, input) => {\n const r = spawnSync(cmd, args, {\n input: input ?? \"\",\n encoding: \"utf8\",\n timeout: 8000,\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n });\n return { status: r.error ? null : r.status, stdout: r.stdout ?? \"\" };\n};\n\n/** A sink's item. The machine has one config, so a sink's name is enough \u2014 except\n * where SESSIONPIPE_CONFIG points at another (a second profile, a test): then the\n * item names that config too, so two configs' sinks of one name never share a key. */\nexport const sinkAccount = (name: string, what: \"token\" | \"secret\" = \"token\"): string => {\n const scope = process.env.SESSIONPIPE_CONFIG\n ? `@${createHash(\"sha256\").update(configFile()).digest(\"hex\").slice(0, 8)}`\n : \"\";\n return `sink:${name}:${what}${scope}`;\n};\nexport const controlAccount = (machine: string): string => `control:${machine}`;\n\n/** `security -i` reads its commands from stdin, so the value never sits in argv (and in\n * `ps`). Its parser takes double-quoted words: a value with a quote, a backslash or a\n * space isn't stored there and stays in the file. */\nconst KC_SAFE = /^[\\x21\\x23-\\x5b\\x5d-\\x7e]{1,4096}$/;\nconst NAME_SAFE = /^[\\x20\\x21\\x23-\\x5b\\x5d-\\x7e]{1,200}$/;\n\nexport function secretGet(store: SecretStore, account: string, run: Runner = defaultRunner): string | null {\n const r =\n store === \"keychain\"\n ? run(\"security\", [\"find-generic-password\", \"-s\", SERVICE, \"-a\", account, \"-w\"])\n : run(\"secret-tool\", [\"lookup\", \"service\", SERVICE, \"account\", account]);\n if (r.status !== 0) return null;\n const v = r.stdout.replace(/\\r?\\n$/, \"\");\n return v || null;\n}\n\nexport function secretSet(store: SecretStore, account: string, value: string, run: Runner = defaultRunner): boolean {\n if (store === \"keychain\") {\n if (!KC_SAFE.test(value) || !NAME_SAFE.test(account)) return false;\n run(\n \"security\",\n [\"-i\"],\n `add-generic-password -U -s ${SERVICE} -a \"${account}\" -l \"${SERVICE} ${account}\" -w \"${value}\"\\n`,\n );\n } else {\n run(\"secret-tool\", [\"store\", \"--label\", `${SERVICE} ${account}`, \"service\", SERVICE, \"account\", account], value);\n }\n // Trust only a read-back: `security -i` exits 0 whatever its commands did.\n return secretGet(store, account, run) === value;\n}\n\nexport function secretDel(store: SecretStore, account: string, run: Runner = defaultRunner): void {\n if (store === \"keychain\") run(\"security\", [\"delete-generic-password\", \"-s\", SERVICE, \"-a\", account]);\n else run(\"secret-tool\", [\"clear\", \"service\", SERVICE, \"account\", account]);\n}\n\n/** Can this process write, read back and delete an item in the store right now? */\nexport function probe(store: SecretStore, run: Runner = defaultRunner): boolean {\n const account = `probe:${randomBytes(6).toString(\"hex\")}`;\n const ok = secretSet(store, account, randomBytes(12).toString(\"base64url\"), run);\n secretDel(store, account, run);\n return ok;\n}\n\n/** The best place for secrets on this machine, from this session. SESSIONPIPE_SECRETS\n * (file | keychain | secret-service) overrides. The Secret Service is used only in a\n * desktop session: on a server its keyring is locked whenever nobody is logged in,\n * which is exactly when the daemon needs its token. */\nexport function bestStore(\n env: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n run: Runner = defaultRunner,\n): StoreName {\n const want = env.SESSIONPIPE_SECRETS;\n if (want === \"file\") return \"file\";\n const candidates: SecretStore[] =\n want === \"keychain\" || want === \"secret-service\"\n ? [want]\n : platform === \"darwin\"\n ? [\"keychain\"]\n : platform === \"linux\" && env.DBUS_SESSION_BUS_ADDRESS && (env.DISPLAY || env.WAYLAND_DISPLAY)\n ? [\"secret-service\"]\n : [];\n for (const s of candidates) if (probe(s, run)) return s;\n return \"file\";\n}\n\nexport const storeLabel = (s: StoreName | undefined, platform: NodeJS.Platform = process.platform): string =>\n s === \"keychain\"\n ? \"the macOS Keychain\"\n : s === \"secret-service\"\n ? \"the Secret Service keyring\"\n : platform === \"win32\"\n ? \"a file under your profile\"\n : \"a 0600 file\";\n\n/** What keeps a key in the config file private: a mode on POSIX; on Windows, where modes\n * mean nothing, the profile folder's own permissions. */\nexport const fileKept = (platform: NodeJS.Platform = process.platform): string =>\n platform === \"win32\" ? \"your profile's permissions\" : \"0600\";\n\n/** `connect`'s line for keys that stay in the file. */\nexport function keysFileNote(\n platform: NodeJS.Platform = process.platform,\n env: NodeJS.ProcessEnv = process.env,\n): string {\n if (platform === \"win32\") return \"under your profile's permissions (Windows has no keychain sessionpipe uses)\";\n return `at 0600 \u2014 no unlocked keychain in this session${env.SSH_CONNECTION ? \" (an ssh shell)\" : \"\"}`;\n}\n\n/** The sink with its token and secret filled in from its store: each item the store\n * holds, since a sink has a token, a Webhooks secret, or both (`sink add --secret`\n * with no `--token` signs and sends no bearer). Null when the store can't be read\n * from this process right now (a keychain locked in an ssh session): the caller\n * leaves the events in the outbox rather than sending them unsigned. One item and not\n * the other is not that: the store has just answered a read from this process, so it\n * is readable right now, and the item it didn't give is one it doesn't hold. A locked\n * store fails both reads. */\nexport function sinkWithSecrets(s: SinkConfig, run: Runner = defaultRunner): SinkConfig | null {\n if (!s.token_in) return s;\n const token = secretGet(s.token_in, sinkAccount(s.name), run);\n const secret = secretGet(s.token_in, sinkAccount(s.name, \"secret\"), run);\n if (!token && !secret) return null;\n return { ...s, ...(token ? { token } : {}), ...(secret ? { secret } : {}) };\n}\n\n/** Store a sink's secrets in `to` (or back in the file) and save the config. */\nexport function moveSinkSecrets(\n to: StoreName,\n o: { run?: Runner; file?: string } = {},\n): { moved: string[]; kept: string[] } {\n const run = o.run ?? defaultRunner;\n const cfg = o.file ? readConfig(o.file) : readConfig();\n const moved: string[] = [];\n const kept: string[] = [];\n for (const s of cfg.sinks) {\n const from = s.token_in;\n if ((from ?? \"file\") === to) continue;\n const full = sinkWithSecrets(s, run);\n if (!full) {\n kept.push(s.name);\n continue;\n }\n if (!full.token && !full.secret) continue;\n if (to === \"file\") {\n // From a store, so with a token, a secret or both (sinkWithSecrets is null with\n // neither); a sink already in the file never gets here.\n if (full.token) s.token = full.token;\n if (full.secret) s.secret = full.secret;\n delete s.token_in;\n } else {\n const ok =\n (!full.token || secretSet(to, sinkAccount(s.name), full.token, run)) &&\n (!full.secret || secretSet(to, sinkAccount(s.name, \"secret\"), full.secret, run));\n if (!ok) {\n secretDel(to, sinkAccount(s.name), run);\n secretDel(to, sinkAccount(s.name, \"secret\"), run);\n kept.push(s.name);\n continue;\n }\n delete s.token;\n delete s.secret;\n s.token_in = to;\n }\n moved.push(s.name);\n if (from) {\n // Written first, then the file says where; only then is the old copy dropped.\n if (o.file) writeConfig(cfg, o.file);\n else writeConfig(cfg);\n secretDel(from, sinkAccount(s.name), run);\n secretDel(from, sinkAccount(s.name, \"secret\"), run);\n }\n }\n if (o.file) writeConfig(cfg, o.file);\n else writeConfig(cfg);\n return { moved, kept };\n}\n\n/** Forget a sink's secrets wherever they are (sink remove, uninstall). */\nexport function dropSinkSecrets(s: SinkConfig, run: Runner = defaultRunner): void {\n if (!s.token_in) return;\n secretDel(s.token_in, sinkAccount(s.name), run);\n secretDel(s.token_in, sinkAccount(s.name, \"secret\"), run);\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// What the control daemon keeps on disk:\n// config/control.json the receivers this machine is paired with: machine id,\n// the machine's own token, the enrolled keys, folders, mode (0600)\n// state/control/nonces.jsonl every verified nonce for 25 h, fsync'd before a\n// command runs (a failed write refuses it)\n// state/control/acks.jsonl acks not yet accepted by their receiver\n// state/control/state.json forks: which copy a message to a session goes to, and\n// the options each session the daemon started runs with\nimport {\n appendFileSync,\n chmodSync,\n closeSync,\n existsSync,\n fsyncSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n writeFileSync,\n} from \"node:fs\";\nimport path from \"node:path\";\nimport { configFile, stateDir } from \"@sessionpipe/core\";\nimport { levelRank, MODEL_RE, NONCE_TTL_MS, type RunLevel, type TrustedKey } from \"@sessionpipe/core/control\";\nimport {\n controlAccount,\n defaultRunner,\n type Runner,\n type SecretStore,\n type StoreName,\n secretDel,\n secretGet,\n secretSet,\n} from \"../secrets.js\";\n\nexport interface PairedReceiver {\n /** The receiver's origin, as the person typed it. */\n url: string;\n /** Absolute control endpoint (from the well-known file). */\n control: string;\n rpId: string;\n machine: string;\n /** The machine's own bearer (polls, acks, hello, off): opens nothing else. Empty\n * when it lives in a store this process can't read right now. */\n token: string;\n /** The token lives in this store, not in control.json (secrets.ts). */\n token_in?: SecretStore;\n keys: (TrustedKey & { name?: string; added_at: string })[];\n paired_at: string;\n}\n\n/** How sessions run when a message arrives (CONTROL.md \u00A72): the person's choice at\n * this machine, never a receiver's. */\nexport const MODES = [\"safe\", \"auto\", \"inherit\"] as const;\nexport type ControlMode = (typeof MODES)[number];\nexport const parseMode = (m: unknown): ControlMode | null =>\n (MODES as readonly unknown[]).includes(m) ? (m as ControlMode) : null;\n\nexport interface ControlConfig {\n name: string;\n /** Folders sessions may run in; compared on whole path segments, real paths. */\n folders: string[];\n mode: ControlMode;\n receivers: PairedReceiver[];\n /** Caps this machine keeps whatever a receiver sends (defaults in daemon.ts,\n * LIMITS): new sessions running at once and per hour, headless Claude Code runs\n * at once, copies of sessions made in a day. */\n limits?: { start_concurrent?: number; start_per_hour?: number; headless?: number; copies_per_day?: number };\n /** Whether a session here may ask another agent through the daemon (CONTROL.md \u00A713).\n * On unless `sessionpipe ask off` set it false; the daemon says which in its hello. */\n ask?: boolean;\n}\n\n/** `ask` as the config means it: on when unsaid. */\nexport const askOn = (c: Pick<ControlConfig, \"ask\">): boolean => c.ask !== false;\n\nexport const controlFile = (env: NodeJS.ProcessEnv = process.env) =>\n path.join(path.dirname(configFile(env)), \"control.json\");\nexport const controlState = (env: NodeJS.ProcessEnv = process.env) => path.join(stateDir(env), \"control\");\n\nexport function writePrivate(file: string, value: unknown): void {\n mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });\n const tmp = `${file}.${process.pid}.tmp`;\n writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\\n`, { mode: 0o600 });\n renameSync(tmp, file);\n try {\n chmodSync(file, 0o600);\n } catch {}\n}\n\nexport function readControl(env: NodeJS.ProcessEnv = process.env, run: Runner = defaultRunner): ControlConfig | null {\n try {\n const j = JSON.parse(readFileSync(controlFile(env), \"utf8\")) as ControlConfig;\n if (!j || !Array.isArray(j.receivers)) return null;\n return {\n name: j.name || \"machine\",\n folders: j.folders ?? [],\n mode: parseMode(j.mode) ?? \"safe\",\n receivers: j.receivers.map((r) =>\n r.token_in ? { ...r, token: secretGet(r.token_in, controlAccount(r.machine), run) ?? \"\" } : r,\n ),\n ...(j.limits ? { limits: j.limits } : {}),\n ...(typeof j.ask === \"boolean\" ? { ask: j.ask } : {}),\n };\n } catch {\n return null;\n }\n}\n\n/** How many receivers this machine is paired with, from control.json alone (no\n * keychain read: `status` shouldn't ask one). */\nexport function pairedCount(env: NodeJS.ProcessEnv = process.env): number {\n try {\n const j = JSON.parse(readFileSync(controlFile(env), \"utf8\")) as { receivers?: unknown };\n return Array.isArray(j.receivers) ? j.receivers.length : 0;\n } catch {\n return 0;\n }\n}\n\n/** A token kept in a store is never written back into the file. */\nexport const writeControl = (c: ControlConfig, env: NodeJS.ProcessEnv = process.env) =>\n writePrivate(controlFile(env), {\n ...c,\n receivers: c.receivers.map((r) => {\n if (!r.token_in) return r;\n const { token: _t, ...rest } = r;\n return rest;\n }),\n });\n\n/** Move every paired receiver's token into `to` (or back into control.json). A token\n * this process can't read is left where it is. */\nexport function moveControlSecrets(\n to: StoreName,\n o: { env?: NodeJS.ProcessEnv; run?: Runner } = {},\n): { moved: string[]; kept: string[] } {\n const run = o.run ?? defaultRunner;\n const cfg = readControl(o.env, run);\n const moved: string[] = [];\n const kept: string[] = [];\n if (!cfg) return { moved, kept };\n const drop: { store: SecretStore; machine: string }[] = [];\n for (const r of cfg.receivers) {\n if ((r.token_in ?? \"file\") === to) continue;\n if (!r.token) {\n kept.push(r.url);\n continue;\n }\n if (to !== \"file\" && !secretSet(to, controlAccount(r.machine), r.token, run)) {\n secretDel(to, controlAccount(r.machine), run);\n kept.push(r.url);\n continue;\n }\n if (r.token_in) drop.push({ store: r.token_in, machine: r.machine });\n if (to === \"file\") delete r.token_in;\n else r.token_in = to;\n moved.push(r.url);\n }\n writeControl(cfg, o.env);\n for (const d of drop) secretDel(d.store, controlAccount(d.machine), run);\n return { moved, kept };\n}\n\n/** Forget a receiver's token wherever it is (control off). */\nexport function dropControlSecret(r: PairedReceiver, run: Runner = defaultRunner): void {\n if (r.token_in) secretDel(r.token_in, controlAccount(r.machine), run);\n}\n\n/** Nonces the machine has run, for NONCE_TTL_MS. `seen()` records a new nonce\n * durably BEFORE it answers false; a write that fails throws, and the verifier\n * refuses the command (`nonce_store`). */\nexport class NonceStore {\n private readonly seenAt = new Map<string, number>();\n constructor(\n private readonly file: string,\n private readonly now: () => number = Date.now,\n ) {\n let raw = \"\";\n try {\n raw = readFileSync(file, \"utf8\");\n } catch {}\n const cut = this.now() - NONCE_TTL_MS;\n for (const line of raw.split(\"\\n\")) {\n if (!line) continue;\n try {\n const { n, t } = JSON.parse(line) as { n: string; t: number };\n if (typeof n === \"string\" && t > cut) this.seenAt.set(n, t);\n } catch {}\n }\n this.compact();\n }\n\n seen = (nonce: string): boolean => {\n const t = this.seenAt.get(nonce);\n if (t !== undefined && t > this.now() - NONCE_TTL_MS) return true;\n const at = this.now();\n mkdirSync(path.dirname(this.file), { recursive: true, mode: 0o700 });\n const fd = openSync(this.file, \"a\", 0o600);\n try {\n appendFileSync(fd, `${JSON.stringify({ n: nonce, t: at })}\\n`);\n fsyncSync(fd);\n } finally {\n closeSync(fd);\n }\n this.seenAt.set(nonce, at);\n return false;\n };\n\n /** Rewrite the file with only the live window (on start, and daily). */\n compact(): void {\n const cut = this.now() - NONCE_TTL_MS;\n for (const [n, t] of this.seenAt) if (t <= cut) this.seenAt.delete(n);\n if (!existsSync(this.file) && !this.seenAt.size) return;\n const body = [...this.seenAt].map(([n, t]) => JSON.stringify({ n, t })).join(\"\\n\");\n mkdirSync(path.dirname(this.file), { recursive: true, mode: 0o700 });\n writeFileSync(`${this.file}.tmp`, body ? `${body}\\n` : \"\", { mode: 0o600 });\n renameSync(`${this.file}.tmp`, this.file);\n }\n\n get size(): number {\n return this.seenAt.size;\n }\n}\n\nexport interface Ack {\n receiver: string;\n id: string;\n outcome: \"taken\" | \"delivered\" | \"expired\" | \"unsupported\" | \"failed\" | \"refused\";\n at: string;\n mode?: string;\n session?: string;\n code?: string;\n detail?: string;\n reply?: string;\n /** A start's taken and delivered: what it ran with (CONTROL.md \u00A76, Options). */\n applied?: StartOptions;\n /** This answer came from a COPY of the session the command named, not from it: the\n * person's words went somewhere other than the conversation they were looking at,\n * and they are owed that fact (CONTROL.md \u00A76 `fork`). */\n copy_of?: string;\n}\n\n/** What a session the daemon started runs with: its level and, when one was asked for,\n * its model. Recorded at the start; every later run of the session reads it. */\nexport interface StartOptions {\n /** A level, or `inherit`: started on an inherit machine with none named. */\n permission: RunLevel;\n model?: string;\n}\n\n/** A recorded entry as read back from disk, or null when it isn't one. */\nexport function startOptionsOf(v: unknown): StartOptions | null {\n const o = v as { permission?: unknown; model?: unknown } | null;\n if (!o || typeof o !== \"object\" || (levelRank(o.permission) < 0 && o.permission !== \"inherit\")) return null;\n return {\n permission: o.permission as RunLevel,\n ...(typeof o.model === \"string\" && MODEL_RE.test(o.model) ? { model: o.model } : {}),\n };\n}\n\n/** Acks not yet accepted: appended before they are sent, rewritten when sent, so a\n * daemon restart re-sends rather than forgets (every message is acked exactly once\n * with a final outcome; a duplicate is tolerated by the receiver). */\nexport class AckOutbox {\n constructor(private readonly file: string) {}\n all(): Ack[] {\n try {\n return readFileSync(this.file, \"utf8\")\n .split(\"\\n\")\n .filter(Boolean)\n .map((l) => JSON.parse(l) as Ack);\n } catch {\n return [];\n }\n }\n add(a: Ack): void {\n mkdirSync(path.dirname(this.file), { recursive: true, mode: 0o700 });\n appendFileSync(this.file, `${JSON.stringify(a)}\\n`, { mode: 0o600 });\n }\n keep(left: Ack[]): void {\n mkdirSync(path.dirname(this.file), { recursive: true, mode: 0o700 });\n writeFileSync(`${this.file}.tmp`, left.map((a) => JSON.stringify(a)).join(\"\\n\") + (left.length ? \"\\n\" : \"\"), {\n mode: 0o600,\n });\n renameSync(`${this.file}.tmp`, this.file);\n }\n}\n\n/** A permission mode as a hook payload spells it (core's CLAUDE_MODES is the list the\n * daemon acts on; this is only the shape a stored value has to keep). */\nconst MODE_SHAPE = /^[A-Za-z]{1,40}$/;\n\n/** How long a message written into a live session's own inbox keeps that lane shut for\n * it (CONTROL.md \u00A76.3): Claude Code holds an unmatched message that long before\n * dropping it, and a hold can be released, so a second write inside the window would\n * mean the person's words arriving twice. It outlives the daemon that wrote it, so a\n * restart \u2014 an update installs one \u2014 cannot turn a resend into a second frame. */\nexport const HOLD_TTL_MS = 300_000;\n\n/** A copy the daemon made because it could not reach the original (CONTROL.md \u00A76\n * `fork`), and what the person is owed about it: which session it is, when it was made\n * and why there is one at all. Daemons up to 0.10.0 wrote the id alone. */\nexport interface Copy {\n id: string;\n at: string;\n /** In the machine's own words, for the ack and the board: why a copy, not the session. */\n reason: string;\n}\n\n/** The id alone, as 0.10.0 and earlier wrote it, reads as a copy with nothing else known. */\nconst asCopy = (v: unknown): Copy | null => {\n if (typeof v === \"string\" && v) return { id: v, at: \"\", reason: \"\" };\n if (!v || typeof v !== \"object\") return null;\n const c = v as { id?: unknown; at?: unknown; reason?: unknown };\n if (typeof c.id !== \"string\" || !c.id) return null;\n return { id: c.id, at: typeof c.at === \"string\" ? c.at : \"\", reason: typeof c.reason === \"string\" ? c.reason : \"\" };\n};\n\n/** A message written into a session's own inbox that it has not taken yet\n * (CONTROL.md \u00A76.3), by the session it was written to. */\nexport interface Parked {\n /** When it was written, ISO-8601. Older than HOLD_TTL_MS is no longer a hold. */\n at: string;\n /** The message's id, so a log or a later ack can name the one that is waiting. */\n id: string;\n}\n\nexport interface DaemonState {\n /** Original session \u2192 the copy its messages go to. */\n copies: Record<string, Copy>;\n /** Claude Code sessions the daemon started \u2192 what they run with (Codex and\n * Antigravity keep theirs in the alias entry, aliases.ts). */\n started: Record<string, StartOptions>;\n /** Session ref \u2192 the permission mode its own hooks last reported (CONTROL.md \u00A76, The\n * session's mode). The daemon's own record, in its own state: no Claude Code home is\n * written to. Insertion order is the age order, which is how the oldest are dropped. */\n modes: Record<string, string>;\n /** Session ref \u2192 a message written into its own inbox that it has not taken. Read back\n * pruned: only the ones still inside the hold window. */\n parked: Record<string, Parked>;\n}\n\nexport function readState(file: string, now: number = Date.now()): DaemonState {\n const copies: Record<string, Copy> = {};\n const started: Record<string, StartOptions> = {};\n const modes: Record<string, string> = {};\n const parked: Record<string, Parked> = {};\n try {\n const j = JSON.parse(readFileSync(file, \"utf8\")) as {\n copies?: unknown;\n copies_v2?: unknown;\n started?: unknown;\n modes?: unknown;\n parked?: unknown;\n };\n if (j.started && typeof j.started === \"object\")\n for (const [k, v] of Object.entries(j.started)) {\n const o = startOptionsOf(v);\n if (o) started[k] = o;\n }\n if (j.modes && typeof j.modes === \"object\")\n for (const [k, v] of Object.entries(j.modes)) if (typeof v === \"string\" && MODE_SHAPE.test(v)) modes[k] = v;\n // A hold whose window has closed is not one: the session can no longer be handed\n // that message, so the lane is open again.\n if (j.parked && typeof j.parked === \"object\")\n for (const [k, v] of Object.entries(j.parked as Record<string, unknown>)) {\n const p = v as { at?: unknown; id?: unknown };\n const at = typeof p?.at === \"string\" ? Date.parse(p.at) : Number.NaN;\n if (Number.isFinite(at) && now - at <= HOLD_TTL_MS && typeof p.id === \"string\")\n parked[k] = { at: p.at as string, id: p.id };\n }\n // The richer shape when it is there, the plain id map when it isn't.\n const from = j.copies_v2 && typeof j.copies_v2 === \"object\" ? j.copies_v2 : j.copies;\n if (from && typeof from === \"object\")\n for (const [k, v] of Object.entries(from as Record<string, unknown>)) {\n const c = asCopy(v);\n if (c) copies[k] = c;\n }\n } catch {}\n return { copies, started, modes, parked };\n}\n\n/** Both shapes of `copies`, every time. A daemon up to 0.10.0 reads `copies` as a map of\n * plain session ids and uses what it finds there as a `--resume` target, so writing\n * only the record would make every message to a session with a copy resume\n * `[object Object]` after a rollback \u2014 and a fleet keeps the previous build precisely\n * so it can roll back. */\nexport const writeState = (file: string, s: DaemonState) =>\n writePrivate(file, {\n ...s,\n copies: Object.fromEntries(Object.entries(s.copies).map(([k, c]) => [k, c.id])),\n copies_v2: s.copies,\n });\n", "// SPDX-License-Identifier: Apache-2.0\n// The daemon's local socket (spec/CONTROL.md \u00A78, \u00A713): how the waiter, the Stop and\n// PermissionRequest hooks, the worker and `sessionpipe ask` talk to the one daemon on\n// this machine. Line-delimited JSON, one request per connection; every request but an\n// ask gets one reply, an ask gets a few in order on the same connection. A Unix socket\n// in the state directory (0600, in a 0700 dir); a per-user named pipe on Windows. Only\n// node: builtins \u2014 the hook bundle imports this.\nimport net from \"node:net\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nexport type LocalRequest =\n /** A waiter parks until a message for its session arrives. `channel`: the waiter is\n * `sessionpipe channel`, which pushes the message into the session rather than handing\n * it to a turn that asked for it (CONTROL.md \u00A78). */\n | { op: \"wait\"; session: string; pid?: number; channel?: boolean }\n /** A Stop hook asks for a queued message (turn mode). `mode` is the session's own\n * permission mode, as its payload reports it (CONTROL.md \u00A76, The session's mode). */\n | { op: \"stop\"; session: string; active: boolean; mode?: string }\n /** A PermissionRequest hook parks until a verified answer, or until it's released. */\n | { op: \"permission\"; session: string; attention: string; tool?: string }\n /** The worker reports a hook event: turn state, attention closure, and the session's\n * own permission mode where its payload named one. */\n | { op: \"event\"; session: string; event: string; transcript?: string; cwd?: string; mode?: string }\n /** `sessionpipe control status`. */\n | { op: \"status\" }\n /** `sessionpipe update` installed a new copy: restart onto it once nothing is in hand. */\n | { op: \"restart\" }\n /** `sessionpipe pool \u2026` while the daemon runs: the daemon holds the pool's engine\n * (pins, credentials, the listener), so a change goes through it. `status` reads;\n * the rest are CONTROL.md \u00A76's actions, plus `import` (reads this machine's own\n * Claude Code logins, which no receiver may ask for). */\n | { op: \"pool\"; action: PoolOp; account?: string; order?: string[]; code?: string }\n /** `sessionpipe ask` (CONTROL.md \u00A713): a session here hands a task to another agent on\n * this machine and gets the answer back on this connection. */\n | AskRequest;\n\nexport type PoolOp =\n | \"status\"\n | \"on\"\n | \"off\"\n | \"enable\"\n | \"disable\"\n | \"remove\"\n | \"order\"\n | \"refresh\"\n | \"login.begin\"\n | \"login.complete\"\n | \"import\";\n\nexport interface AskRequest {\n op: \"ask\";\n /** `<harness>:<id>` of the asking session, when known: the events go on it. */\n from?: string;\n /** The agent to ask. */\n harness: string;\n /** An existing `<harness>:<id>` to continue instead of a new session. */\n session?: string;\n /** 1\u201320 000 characters, not all whitespace. */\n text: string;\n /** Absolute; the caller's folder by default. */\n cwd: string;\n /** Every ancestor ref, outermost first (from env SESSIONPIPE_ASK_CHAIN). */\n chain: string[];\n /** Narrower than the machine's mode, never wider. */\n permission?: \"read\" | \"safe\";\n model?: string;\n pid?: number;\n}\n\nexport type LocalReply =\n | { op: \"message\"; text: string; id: string }\n | { op: \"block\"; reason: string; id: string }\n | { op: \"decision\"; behavior: \"allow\" | \"deny\"; message?: string; id: string }\n | { op: \"none\" }\n /** A newer waiter for the same session took over; this one stops. */\n | { op: \"replaced\" }\n | { op: \"ok\" }\n | { op: \"status\"; status: unknown }\n /** A pool op's answer: the ack it would earn (CONTROL.md \u00A76) and the pool as it now\n * stands (`status`: the fa\u00E7ade's PoolStatus; `detail`: the summary line, or a\n * login.begin's \"<pending id> <URL>\"). */\n | {\n op: \"pool\";\n outcome: \"delivered\" | \"refused\" | \"failed\" | \"unsupported\";\n code?: string;\n detail: string;\n status?: unknown;\n /** `import`: what was added and what was left out (control/pool.ts Imported). */\n imported?: unknown;\n }\n // An ask's replies, in order (askMany): `asked`, any `progress`, then exactly one of\n // `answer`, `refused` and `failed`, after which the daemon closes the connection.\n /** The run began; `session` once it is known. */\n | { op: \"asked\"; ask_id: string; session?: string }\n /** The answer so far, whole, with a rising seq. */\n | { op: \"progress\"; text: string; seq: number }\n | { op: \"answer\"; ask_id: string; session: string; text: string; ms: number }\n /** ask_off \u00B7 harness \u00B7 folder \u00B7 depth \u00B7 loop \u00B7 start_limit \u00B7 bad_fields */\n | { op: \"refused\"; code: string; detail: string }\n | { op: \"failed\"; detail: string };\n\n/** The replies that end an ask. */\nexport type AskEnd = Extract<LocalReply, { op: \"answer\" | \"refused\" | \"failed\" }>;\n\n/** Where the daemon listens. `SESSIONPIPE_SOCKET` overrides (tests). */\nexport function socketPath(state: string, env: NodeJS.ProcessEnv = process.env): string {\n if (env.SESSIONPIPE_SOCKET) return env.SESSIONPIPE_SOCKET;\n if (process.platform === \"win32\") return `\\\\\\\\.\\\\pipe\\\\sessionpipe-control-${os.userInfo().username}`;\n return path.join(state, \"control.sock\");\n}\n\n/** One request, one reply. `timeoutMs` bounds the whole exchange; null on any\n * failure (no daemon, a timeout, a garbled reply) \u2014 callers treat that as \"none\". */\nexport function ask(sock: string, req: LocalRequest, timeoutMs: number): Promise<LocalReply | null> {\n return new Promise((resolve) => {\n let buf = \"\";\n let done = false;\n const s = net.connect(sock);\n const finish = (r: LocalReply | null) => {\n if (done) return;\n done = true;\n clearTimeout(t);\n s.destroy();\n resolve(r);\n };\n const t = setTimeout(() => finish(null), timeoutMs);\n s.on(\"connect\", () => s.write(`${JSON.stringify(req)}\\n`));\n s.on(\"data\", (d) => {\n buf += d.toString(\"utf8\");\n const i = buf.indexOf(\"\\n\");\n if (i < 0) return;\n try {\n finish(JSON.parse(buf.slice(0, i)) as LocalReply);\n } catch {\n finish(null);\n }\n });\n s.on(\"error\", () => finish(null));\n s.on(\"close\", () => finish(null));\n });\n}\n\n/** One request, several replies on the same connection (an ask): `onReply` sees each one\n * in order, the promise settles with the one that ends the exchange. `timeoutMs` bounds\n * the whole exchange; null when it ends without an end \u2014 no daemon, a timeout, a garbled\n * line, a connection that closed mid-run. */\nexport function askMany(\n sock: string,\n req: LocalRequest,\n onReply: (r: LocalReply) => void,\n timeoutMs: number,\n): Promise<AskEnd | null> {\n return new Promise((resolve) => {\n let buf = \"\";\n let done = false;\n const s = net.connect(sock);\n const finish = (r: AskEnd | null) => {\n if (done) return;\n done = true;\n clearTimeout(t);\n s.destroy();\n resolve(r);\n };\n const t = setTimeout(() => finish(null), timeoutMs);\n s.on(\"connect\", () => s.write(`${JSON.stringify(req)}\\n`));\n s.on(\"data\", (d) => {\n buf += d.toString(\"utf8\");\n for (let i = buf.indexOf(\"\\n\"); i >= 0 && !done; i = buf.indexOf(\"\\n\")) {\n const line = buf.slice(0, i);\n buf = buf.slice(i + 1);\n let r: LocalReply;\n try {\n r = JSON.parse(line) as LocalReply;\n } catch {\n finish(null);\n return;\n }\n onReply(r);\n if (r.op === \"answer\" || r.op === \"refused\" || r.op === \"failed\") finish(r);\n }\n });\n s.on(\"error\", () => finish(null));\n s.on(\"close\", () => finish(null));\n });\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Fewer tool events per sink (issue #208). A Claude Code workflow with a few hundred\n// subagents fires two hooks per tool call, and a receiver that counts requests per\n// machine ran out of them by 2 a.m. The outbox keeps every event; a sink's `tools`\n// setting says which of them it is sent:\n// all every tool.started / tool.ended (the default: a receiver's view never changes\n// unless it asked, or the sink's owner chose)\n// main the main thread's tool events go live; a subagent's (data.agent_id set) are\n// thinned (what a receiver that meters requests asks for in its well-known\n// file, `tools: \"main\"`)\n// beat every tool event of a session is thinned\n// A thinned stream is sent tool.ended only, and of those at most one per session per\n// minute, by the events' own time: tool.ended is what a receiver reads as \"a tool ran\"\n// (spacesheep reads nothing from tool.started), so the one event kept is never one it\n// throws away. One more is always sent: the asking thread's first tool.ended after its\n// attention.needed, the event that shows the prompt was answered (PROTOCOL.md \u00A74,\n// attention.cleared). Only tool events are thinned: turns, attention, subagents, files\n// and limits go as they come.\nimport { mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { type Event, type SinkConfig, safeId } from \"@sessionpipe/core\";\n\nexport type ToolsMode = \"all\" | \"main\" | \"beat\";\nexport const TOOLS_MODES: readonly ToolsMode[] = [\"all\", \"main\", \"beat\"];\n\n/** One tool event per this window, per session, when a sink thins them. */\nexport const TOOL_FOLD_MS = 60_000;\n/** A session whose last tool event sent is this much older than the newest one's is\n * dropped from the clock file: it stays small on a machine that runs for months. */\nconst KEEP_MS = 24 * 60 * 60_000;\n\nexport function isToolsMode(v: unknown): v is ToolsMode {\n return typeof v === \"string\" && (TOOLS_MODES as readonly string[]).includes(v);\n}\n\n/** What a sink is sent: its owner's own setting, else what the receiver's well-known\n * file asked for, else every tool event. Never thinned on nobody's say-so: a receiver\n * that reads a subagent's tool calls (sessionpipe-gui does) keeps them on an upgrade. */\nexport function toolsMode(s: Pick<SinkConfig, \"url\" | \"tools\" | \"receiver_tools\">): ToolsMode {\n if (isToolsMode(s.tools)) return s.tools;\n if (isToolsMode(s.receiver_tools)) return s.receiver_tools;\n return \"all\";\n}\n\nconst agentOf = (e: Event): string => (typeof e.data.agent_id === \"string\" ? e.data.agent_id : \"\");\n/** Whether a sink in `mode` thins the tool events of the thread `agent` (\"\" = main). */\nconst thins = (mode: ToolsMode, agent: string): boolean => mode === \"beat\" || (mode === \"main\" && agent !== \"\");\n/** The clock entry that says a thread of `key` is waiting on a prompt: its next\n * tool.ended goes whatever the window says. */\nconst askKey = (key: string, agent: string): string => `${key}\\u0000ask\\u0000${agent}`;\n\n/** Whether a sink in `mode` is sent `e`. `clock` holds, per session (`key`), the time\n * of the last tool event it was sent, and moves when this one is. Times are the\n * events' own, so a backlog thins the same way it would have live. An event a little\n * out of time order (hooks reach the outbox in lock order, not hook order) falls in\n * the window like any other; only a jump back by a whole window or more (a clock set\n * back) sends and restarts it. */\nexport function sendTool(e: Event, mode: ToolsMode, clock: Record<string, number>, key: string): boolean {\n if (mode === \"all\") return true;\n const agent = agentOf(e);\n const t = Date.parse(e.time);\n if (e.type === \"attention.needed\") {\n if (thins(mode, agent)) clock[askKey(key, agent)] = Number.isFinite(t) ? t : 0;\n return true;\n }\n if ((e.type !== \"tool.started\" && e.type !== \"tool.ended\") || !thins(mode, agent)) return true;\n if (e.type === \"tool.started\") return false;\n if (!Number.isFinite(t)) return true;\n const ask = askKey(key, agent);\n // Only a tool.ended timed at or after the prompt answers it.\n const asked = clock[ask];\n if (asked !== undefined && t >= asked) {\n delete clock[ask];\n clock[key] = t;\n return true;\n }\n const last = clock[key];\n if (last !== undefined && t > last - TOOL_FOLD_MS && t < last + TOOL_FOLD_MS) return false;\n clock[key] = t;\n return true;\n}\n\n/** <state>/folds/<sink>.json: the per-session clock `sendTool` reads, beside the\n * sink's cursors. Written with them, after a batch was acknowledged, so a refused\n * batch's tool event is not taken for sent. */\nexport class ToolClock {\n constructor(\n readonly root: string,\n readonly sink: string,\n ) {}\n private file(): string {\n return path.join(this.root, \"folds\", `${safeId(this.sink)}.json`);\n }\n read(): Record<string, number> {\n try {\n const j = JSON.parse(readFileSync(this.file(), \"utf8\")) as unknown;\n return j && typeof j === \"object\" && !Array.isArray(j) ? (j as Record<string, number>) : {};\n } catch {\n return {};\n }\n }\n write(c: Record<string, number>): void {\n const newest = Math.max(0, ...Object.values(c).filter(Number.isFinite));\n const kept: Record<string, number> = {};\n for (const [k, v] of Object.entries(c)) if (Number.isFinite(v) && v >= newest - KEEP_MS) kept[k] = v;\n const f = this.file();\n mkdirSync(path.dirname(f), { recursive: true, mode: 0o700 });\n const tmp = `${f}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify(kept), { mode: 0o600 });\n renameSync(tmp, f);\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;AAKA,SAAS,YAAY,kBAAkB;AAGvC,IAAM,aAAa;AAKZ,SAAS,UAAU,QAAqC;AAC7D,SAAO,OAAO,WAAW,QAAQ,EAC9B,OAAO,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,GAAG,CAAC,EACxC,OAAO,WAAW,EAClB,MAAM,GAAG,EAAE,CAAC;AACjB;AAIO,SAAS,aAAa,QAAuB,MAAM,KAAK,IAAI,GAAW;AAC5E,MAAI,WAAW,QAAQ,OAAO,KAAK,MAAM,GAAI,QAAO;AACpD,QAAM,OAAO,OAAO,MAAM;AAC1B,MAAI,OAAO,SAAS,IAAI,EAAG,QAAO,KAAK,IAAI,GAAG,IAAI,IAAI;AACtD,QAAM,KAAK,KAAK,MAAM,MAAM;AAC5B,SAAO,OAAO,SAAS,EAAE,IAAI,KAAK,IAAI,GAAG,KAAK,GAAG,IAAI;AACvD;AAEO,IAAM,WAAN,MAA+B;AAAA,EAIpC,YACmB,KACR,MACT;AAFiB;AACR;AAET,SAAK,OAAO,IAAI;AAChB,SAAK,MAAM,CAAC,CAAC,IAAI;AACjB,SAAK,MAAM,IAAI,IAAI,QAAQ,OAAO,EAAE;AAAA,EACtC;AAAA,EANmB;AAAA,EACR;AAAA,EALF;AAAA,EACA;AAAA,EACQ;AAAA,EAUT,YAAoB;AAG1B,UAAM,KAAM,KAAK,IAA4C,WAAW,UAAU;AAClF,WAAO,eAAe,KAAK,EAAE,IAAI,KAAK,KAAK,MAAM;AAAA,EACnD;AAAA,EAEA,MAAM,KAAK,QAA0C;AACnD,UAAM,OAAO,KAAK,UAAU,EAAE,OAAO,CAAC;AACtC,UAAM,UAAkC,EAAE,gBAAgB,oBAAoB,wBAAwB,IAAI;AAC1G,QAAI,KAAK,IAAI,MAAO,SAAQ,gBAAgB,UAAU,KAAK,IAAI,KAAK;AACpE,QAAI,KAAK,IAAI,QAAQ,WAAW,QAAQ,GAAG;AACzC,YAAM,KAAK,UAAU,MAAM;AAC3B,YAAM,KAAK,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACvC,YAAM,MAAM,OAAO,KAAK,KAAK,IAAI,OAAO,MAAM,CAAC,GAAG,QAAQ;AAC1D,YAAM,MAAM,WAAW,UAAU,GAAG,EAAE,OAAO,GAAG,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,EAAE,OAAO,QAAQ;AACnF,cAAQ,YAAY,IAAI;AACxB,cAAQ,mBAAmB,IAAI,OAAO,EAAE;AACxC,cAAQ,mBAAmB,IAAI,MAAM,GAAG;AAAA,IAC1C;AACA,UAAM,MAAM,IAAI,gBAAgB;AAChC,UAAM,IAAI,WAAW,MAAM,IAAI,MAAM,GAAG,UAAU;AAClD,QAAI;AACF,YAAM,IAAI,MAAM,MAAM,KAAK,UAAU,GAAG,EAAE,QAAQ,QAAQ,SAAS,MAAM,QAAQ,IAAI,OAAO,CAAC;AAC7F,YAAM,OAAO,MAAM,EAAE,KAAK;AAC1B,UAAI,IAA6B,CAAC;AAClC,UAAI;AACF,YAAI,OAAQ,KAAK,MAAM,IAAI,IAAgC,CAAC;AAAA,MAC9D,QAAQ;AAAA,MAAC;AACT,UAAI,EAAE,WAAW,OAAO,EAAE,WAAW,KAAK;AACxC,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,cAAc,OAAO;AAAA,UACrB,UAAU,OAAO,EAAE,YAAY,OAAO,MAAM;AAAA,UAC5C,YAAY,OAAO,EAAE,cAAc,CAAC;AAAA,UACpC,UAAU,MAAM,QAAQ,EAAE,QAAQ,IAAK,EAAE,WAAgD,CAAC;AAAA,UAC1F,GAAI,OAAO,EAAE,aAAa,WAAW,EAAE,UAAU,EAAE,SAAiB,IAAI,CAAC;AAAA,UACzE,QAAQ,EAAE;AAAA,QACZ;AAAA,MACF;AACA,YAAM,MAAM,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS,KAAK,MAAM,GAAG,GAAG;AACvE,UAAI,EAAE,WAAW,IAAK,QAAO,EAAE,IAAI,OAAO,cAAc,GAAG,QAAQ,KAAK,OAAO,KAAK,QAAQ,OAAO;AACnG,UAAI,EAAE,WAAW,IAAK,QAAO,EAAE,IAAI,OAAO,cAAc,GAAG,QAAQ,KAAK,OAAO,KAAK,QAAQ,QAAQ;AACpG,UAAI,EAAE,WAAW;AACf,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,cAAc;AAAA,UACd,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,GAAI,OAAO,EAAE,aAAa,WAAW,EAAE,UAAU,EAAE,SAAiB,IAAI,CAAC;AAAA,QAC3E;AACF,UAAI,EAAE,WAAW,IAAK,QAAO,EAAE,IAAI,OAAO,cAAc,GAAG,QAAQ,KAAK,OAAO,KAAK,QAAQ,QAAQ;AACpG,UAAI,EAAE,WAAW;AACf,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,cAAc;AAAA,UACd,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,cAAc,aAAa,EAAE,QAAQ,IAAI,aAAa,CAAC;AAAA,QACzD;AACF,aAAO,EAAE,IAAI,OAAO,cAAc,GAAG,QAAQ,EAAE,QAAQ,OAAO,KAAK,QAAQ,QAAQ;AAAA,IACrF,SAAS,GAAG;AACV,aAAO,EAAE,IAAI,OAAO,cAAc,GAAG,OAAO,OAAQ,EAAY,WAAW,CAAC,GAAG,QAAQ,QAAQ;AAAA,IACjG,UAAE;AACA,mBAAa,CAAC;AAAA,IAChB;AAAA,EACF;AACF;;;AC9GA;AAAA,EACE,kBAAAA;AAAA,EACA,cAAAC;AAAA,EACA,aAAAC;AAAA,EACA,eAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,cAAAC;AAAA,EACA,YAAAC;AAAA,EACA,cAAAC;AAAA,EACA,iBAAAC;AAAA,OACK;AACP,OAAOC,SAAQ;AACf,OAAOC,YAAU;;;ACTjB,OAAO,QAAQ;AACf,OAAO,UAAU;AAIV,IAAM,UAAU,MAAc,GAAG,QAAQ;AAEzC,SAAS,WAAW,MAAyB,QAAQ,KAAa;AACvE,MAAI,IAAI,mBAAoB,QAAO,KAAK,QAAQ,IAAI,kBAAkB;AACtE,MAAI,QAAQ,aAAa;AACvB,WAAO,KAAK,KAAK,IAAI,WAAW,KAAK,KAAK,QAAQ,GAAG,WAAW,SAAS,GAAG,eAAe,aAAa;AAC1G,SAAO,KAAK,KAAK,IAAI,mBAAmB,KAAK,KAAK,QAAQ,GAAG,SAAS,GAAG,eAAe,aAAa;AACvG;AAEO,SAAS,SAAS,MAAyB,QAAQ,KAAa;AACrE,MAAI,IAAI,kBAAmB,QAAO,KAAK,QAAQ,IAAI,iBAAiB;AACpE,MAAI,QAAQ,aAAa;AACvB,WAAO,KAAK,KAAK,IAAI,gBAAgB,KAAK,KAAK,QAAQ,GAAG,WAAW,OAAO,GAAG,aAAa;AAC9F,SAAO,KAAK,KAAK,IAAI,kBAAkB,KAAK,KAAK,QAAQ,GAAG,UAAU,OAAO,GAAG,aAAa;AAC/F;AAGO,SAAS,MAAM,GAAW,OAAe,QAAQ,GAAW;AACjE,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,MAAM,KAAM,QAAO;AACvB,SAAO,EAAE,WAAW,OAAO,KAAK,GAAG,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,KAAK;AAClG;AAWO,IAAM,SAAS,CAAC,MACrB,OAAO,CAAC,EACL,QAAQ,oBAAoB,GAAG,EAC/B,MAAM,GAAG,GAAG;;;AClCjB,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,cAAAC,aAAY,eAAAC,cAAa,gBAAAC,eAAc,YAAAC,iBAAgB;AAChE,SAAS,gBAAgB;AACzB,OAAOC,WAAU;;;ACbjB,SAAS,WAAW,WAAW,UAAU,aAAa,UAAU,gBAAgB;AAChF,OAAOC,WAAU;AAGV,SAAS,KAAK,MAAc,WAAmB,WAAuD;AAC3G,MAAI;AACJ,MAAI;AACF,SAAK,SAAS,MAAM,GAAG;AACvB,UAAM,OAAO,UAAU,EAAE,EAAE;AAC3B,UAAM,OAAO,CAAC,KAAa,QAAgB;AACzC,YAAM,IAAI,OAAO,MAAM,GAAG;AAC1B,YAAM,IAAI,SAAS,IAAc,GAAG,GAAG,KAAK,GAAG;AAC/C,aAAO,EAAE,SAAS,GAAG,CAAC,EAAE,SAAS,MAAM;AAAA,IACzC;AACA,UAAM,OAAO,KAAK,GAAG,KAAK,IAAI,MAAM,SAAS,CAAC;AAC9C,UAAM,OAAO,OAAO,YAAY,KAAK,KAAK,IAAI,GAAG,OAAO,SAAS,GAAG,KAAK,IAAI,MAAM,SAAS,CAAC,IAAI;AACjG,WAAO,EAAE,MAAM,KAAK,MAAM,IAAI,EAAE,MAAM,GAAG,EAAE,GAAG,MAAM,KAAK,MAAM,IAAI,EAAE,MAAM,OAAO,YAAY,IAAI,CAAC,EAAE;AAAA,EACvG,QAAQ;AACN,WAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,EAAE;AAAA,EAC9B,UAAE;AACA,QAAI,OAAO;AACT,UAAI;AACF,kBAAU,EAAE;AAAA,MACd,QAAQ;AAAA,MAAC;AAAA,EACb;AACF;AAEO,IAAM,aAAa,CAAC,UACzB,MACG,IAAI,CAAC,MAAM;AACV,MAAI;AACF,WAAO,KAAK,MAAM,CAAC;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACF,CAAC,EACA,OAAO,CAAC,MAAoC,CAAC,CAAC,CAAC;AAG7C,SAAS,UAAa,MAAc,OAAmCC,OAAM,KAAK,OAAO,MAAgB;AAC9G,MAAI;AACJ,MAAI;AACF,SAAK,SAAS,MAAM,GAAG;AACvB,UAAM,MAAM,OAAO,MAAM,OAAO,IAAI;AACpC,QAAI,MAAM;AACV,QAAI,OAAe,OAAO,MAAM,CAAC;AACjC,WAAO,MAAMA,MAAK;AAChB,YAAM,IAAI,SAAS,IAAI,KAAK,GAAG,IAAI,QAAQ,GAAG;AAC9C,UAAI,CAAC,EAAG;AACR,aAAO;AACP,UAAI,QAAQ,OAAO,OAAO,CAAC,MAAM,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;AACpD,UAAI,KAAK,MAAM,QAAQ,EAAE;AACzB,aAAO,MAAM,GAAG;AACd,cAAM,IAAI,MAAM,MAAM,SAAS,GAAG,EAAE,EAAE,SAAS,MAAM,CAAC;AACtD,YAAI,MAAM,KAAM,QAAO;AACvB,gBAAQ,MAAM,SAAS,KAAK,CAAC;AAC7B,aAAK,MAAM,QAAQ,EAAE;AAAA,MACvB;AACA,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EACR,UAAE;AACA,QAAI,OAAO;AACT,UAAI;AACF,kBAAU,EAAE;AAAA,MACd,QAAQ;AAAA,MAAC;AAAA,EACb;AACA,SAAO;AACT;AAEO,SAAS,YAAY,MAAc,OAAe,SAAiB,OAA4C;AACpH,QAAM,MAAgB,CAAC;AACvB,QAAM,OAAO,CAAC,KAAa,MAAc;AACvC,QAAI,QAAoC,CAAC;AACzC,QAAI;AACF,cAAQ,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,IAClD,QAAQ;AACN;AAAA,IACF;AACA,eAAW,KAAK,OAAO;AACrB,YAAM,IAAID,MAAK,KAAK,KAAK,EAAE,IAAI;AAC/B,UAAI,EAAE,YAAY,GAAG;AACnB,YAAI,IAAI,MAAO,MAAK,GAAG,IAAI,CAAC;AAC5B;AAAA,MACF;AACA,UAAI,CAAC,MAAM,EAAE,IAAI,EAAG;AACpB,UAAI;AACF,YAAI,SAAS,CAAC,EAAE,WAAW,QAAS,KAAI,KAAK,CAAC;AAAA,MAChD,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF;AACA,OAAK,MAAM,CAAC;AACZ,SAAO;AACT;;;AC5FA,SAAS,oBAAoB;AAE7B,IAAM,QAAQ,oBAAI,IAAgD;AAE3D,SAAS,SAAS,KAA6D;AACpF,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,QAAM,MAAM,MAAM,IAAI,GAAG;AACzB,MAAI,IAAK,QAAO;AAChB,QAAM,MAAM,CAAC,SAAmB;AAC9B,QAAI;AACF,aAAO,aAAa,OAAO,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG;AAAA,QAC/C,UAAU;AAAA,QACV,SAAS;AAAA,QACT,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,MACpC,CAAC,EAAE,KAAK;AAAA,IACV,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,MAA0C,CAAC;AACjD,QAAM,OAAO,IAAI,CAAC,UAAU,WAAW,QAAQ,CAAC;AAChD,MAAI,KAAM,KAAI,OAAO,iBAAiB,IAAI;AAC1C,QAAM,SAAS,IAAI,CAAC,aAAa,gBAAgB,MAAM,CAAC;AACxD,MAAI,UAAU,WAAW,OAAQ,KAAI,SAAS;AAC9C,QAAM,IAAI,KAAK,GAAG;AAClB,SAAO;AACT;AAGO,SAAS,iBAAiB,KAAqB;AAEpD,QAAM,MAAM,iDAAiD,KAAK,GAAG;AACrE,MAAI,IAAK,QAAO,GAAG,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;AACnC,SAAO,IACJ,QAAQ,eAAe,IAAI,EAC3B,QAAQ,gBAAgB,EAAE,EAC1B,QAAQ,UAAU,EAAE;AACzB;;;ACnCO,SAAS,cAAc,MAA2D;AACvF,MAAI,MAAM,SAAS,eAAgB,QAAO;AAC1C,QAAM,IAAI,KAAK;AACf,SAAO,CAAC,CAAC,GAAG,QAAQ;AACtB;AAIO,SAAS,oBAAoB,MAAkD;AACpF,MAAI,MAAM,SAAS,eAAgB,QAAO;AAC1C,QAAM,IAAI,KAAK;AACf,MAAI,CAAC,KAAM,CAAC,EAAE,kBAAkB,CAAC,EAAE,QAAQ,SAAW,QAAO;AAC7D,SAAO,CAAC,WACN,OAAO,EAAE,OAAO,YAChB,CAAC,CAAC,EAAE,MACJ,QAAQ,SAAS,eAChB,OAAO,SAAiD,cAAc,EAAE;AAC7E;;;ACFO,SAAS,UAAU,MAAa,SAAiC;AACtE,QAAM,QAOA,CAAC;AACP,MAAI,MAAqC;AACzC,aAAW,KAAK,MAAM;AACpB,QAAI,EAAE,SAAS,QAAQ;AAGrB,UAAI,OAAO,CAAC,IAAI,WAAW;AACzB,YAAI,QAAQ;AAAA;AAAA,EAAO,EAAE,IAAI;AACzB;AAAA,MACF;AACA,UAAI,IAAK,OAAM,KAAK,GAAG;AACvB,YAAM;AAAA,QACJ,MAAM,EAAE;AAAA,QACR,WAAW;AAAA,QACX,IAAI,EAAE;AAAA,QACN,WAAW,EAAE;AAAA,QACb,SAAS,EAAE;AAAA,QACX,GAAI,EAAE,WAAW,EAAE,UAAU,KAAc,IAAI,CAAC;AAAA,MAClD;AAAA,IACF,OAAO;AACL,UAAI,CAAC,IAAK,OAAM,EAAE,MAAM,IAAI,WAAW,IAAI,IAAI,EAAE,IAAI,WAAW,EAAE,MAAM,SAAS,EAAE,KAAK;AACxF,UAAI,cAAc,IAAI,YAAY,SAAS,MAAM,EAAE;AACnD,UAAI,UAAU,EAAE;AAAA,IAClB;AAAA,EACF;AACA,MAAI,KAAK,UAAW,OAAM,KAAK,GAAG;AAElC,QAAM,OAAO,OAAO,CAAC,IAAI,YAAY,IAAI,YAAY;AACrD,QAAM,MAAsB;AAAA,IAC1B,OAAO,MAAM,IAAI,CAAC,OAAO;AAAA,MACvB,MAAM,EAAE;AAAA,MACR,WAAW,EAAE;AAAA,MACb,IAAI,EAAE;AAAA,MACN,SAAS,EAAE;AAAA,MACX,GAAI,EAAE,WAAW,EAAE,UAAU,KAAc,IAAI,CAAC;AAAA,IAClD,EAAE;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,CAAC,IAAI,aAAa,IAAI;AAC/B,QAAI,OAAO,EAAE,MAAM,IAAI,MAAM,IAAI,IAAI,IAAI,GAAI,IAAI,WAAW,EAAE,UAAU,KAAc,IAAI,CAAC,EAAG;AAChG,SAAO;AACT;AAKO,IAAM,aAAa,CAAC,SACxB,2BAA2B,KAAK,IAAI,KAAK,oBAAoB,KAAK,IAAI,KACvE,UAAU,KAAK,IAAI,KACnB,aAAa,KAAK,IAAI;AAExB,IAAM,QAAQ,CAAC,QAAgD;AAC7D,MAAI;AACF,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAIA,IAAM,QAAQ,CAAC,UAAoB;AACjC,QAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,MAAI,SAAS,OAAW,QAAO;AAC/B,SAAO,SAAS,MAAM,MAAM,IAAI,MAAM,OAAO,MAAM,SAAS,IAAI,MAAM;AACxE;AACA,IAAM,YAAY,CAAC,GAAY,UAA4B;AACzD,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,MAAI,CAAC,MAAM,QAAQ,CAAC,EAAG,QAAO;AAC9B,SAAO,EACJ;AAAA,IACC,CAAC,MACC,KACA,OAAO,MAAM,YACb,MAAM,SAAU,EAAwB,QAAQ,EAAE,KAClD,OAAQ,EAAyB,SAAS;AAAA,EAC9C,EACC,IAAI,CAAC,MAAO,EAAuB,IAAI,EACvC,KAAK,IAAI;AACd;AAOA,IAAM,SAAS,oBAAI,IAAI,CAAC,SAAS,MAAM,CAAC;AAKxC,IAAM,aAAa;AACnB,IAAM,aAAa;AAEnB,IAAM,WAAW;AAMV,SAAS,QAAQ,QAAiB,MAAsB;AAC7D,QAAM,OAAQ,QAA2C;AACzD,MAAI,IAAI,OAAO,SAAS,YAAY,KAAK,KAAK,IAAI,OAAO;AACzD,MAAI,MAAM,MAAM;AACd,UAAM,KAAK,EAAE,QAAQ,IAAI;AACzB,QAAI,KAAK,KAAK,WAAW,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC,EAAG,KAAI,EAAE,MAAM,KAAK,CAAC;AACjE,UAAM,QAAQ,EAAE,QAAQ,UAAU;AAClC,QAAI,QAAQ,EAAG,KAAI,EAAE,MAAM,GAAG,KAAK;AAAA,EACrC;AACA,UAAQ,SAAS,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK;AAClD;AAQA,IAAM,eAAe,CAAC,MAAuB;AAC3C,MAAI,CAAC,KAAK,OAAO,MAAM,SAAU,QAAO;AACxC,QAAM,IAAI;AACV,MAAI,EAAE,SAAS,qBAAqB,EAAE,eAAe,cAAc,SAAU,QAAO;AAEpF,QAAM,OAAO,EAAE,QAAQ,QAAQ;AAC/B,MAAI,CAAC,OAAO,IAAI,IAAI,EAAG,QAAO;AAC9B,QAAM,OAAO,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK;AAChD,SAAO,SAAS,SAAS,QAAQ,EAAE,QAAQ,IAAI,IAAI;AACrD;AAMA,IAAM,cAAc,CAAC,SAAyB,yCAAyC,KAAK,IAAI,IAAI,CAAC,GAAG,KAAK,KAAK;AAI3G,SAAS,WAAW,OAAiB,MAA8B;AACxE,QAAM,OAAc,CAAC;AACrB,WAAS,IAAI,MAAM,IAAI,MAAM,QAAQ,KAAK;AACxC,UAAM,IAAI,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,CAAW,IAAI;AACjD,QAAI,CAAC,KAAK,EAAE,YAAa;AACzB,UAAM,SAAS,EAAE;AAMjB,UAAM,UAAU,EAAE,SAAS,UAAU,QAAQ,SAAS;AACtD,UAAM,OAAO,QAAQ,SAAS;AAC9B,QAAI,EAAE,SAAS,WAAW,EAAE,oBAAoB,EAAE,uBAAwB;AAC1E,QAAI,EAAE,SAAS,UAAU,CAAC,WAAW,CAAC,SAAS,EAAE,UAAW,QAAQ,QAAQ,OAAO,SAAS,SAAW;AACvG,UAAM,KAAK,KAAK,MAAM,OAAO,EAAE,aAAa,EAAE,CAAC,KAAK,KAAK,IAAI;AAC7D,UAAM,IAAK,EAAE,SAA+C;AAC5D,QAAI,SAAS;AAEX,YAAM,OAAO,YAAY,UAAU,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC;AACtD,UAAI,KAAM,MAAK,KAAK,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,GAAG,UAAU,KAAK,CAAC;AAAA,IACzE,WAAW,EAAE,SAAS,QAAQ;AAC5B,YAAM,MAAM,UAAU,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK;AACxC,YAAM,OAAO,OAAO,QAAQ,QAAQ,GAAG,IAAI;AAK3C,UAAI,CAAC,QAAQ,WAAW,IAAI,EAAG;AAC/B,WAAK,KAAK,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,GAAG,GAAI,OAAO,EAAE,UAAU,KAAc,IAAI,CAAC,EAAG,CAAC;AAAA,IAC7F,WAAW,EAAE,SAAS,eAAe,MAAM,QAAQ,CAAC,GAAG;AACrD,YAAM,OAAO,UAAU,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK;AACzC,UAAI,KAAM,MAAK,KAAK,EAAE,MAAM,aAAa,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,IAC9D,WAAW,EAAE,SAAS,cAAc;AAClC,YAAM,OAAO,aAAa,EAAE,UAAU;AACtC,UAAI,QAAQ,CAAC,WAAW,IAAI,EAAG,MAAK,KAAK,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,IAC9E;AAAA,EACF;AACA,SAAO,UAAU,MAAM,MAAM,KAAK,CAAC;AACrC;AAKO,SAAS,cAAc,MAAuB;AACnD,SAAO,eAAe,KAAK,IAAI,KAAK,gCAAgC,KAAK,IAAI;AAC/E;AAGO,SAAS,UAAU,OAAiB,MAA8B;AACvE,QAAM,OAAc,CAAC;AACrB,QAAM,WAAW,oBAAoB,MAAM,MAAM,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC;AAGnF,QAAM,QAAQ,WAAW,MAAM,UAAU,CAAC,SAAS,SAAS,MAAM,IAAI,CAAC,CAAC,IAAI;AAC5E,MAAI,QAAQ,EAAG,QAAO,UAAU,MAAM,MAAM,KAAK,CAAC;AAClD,WAAS,IAAI,KAAK,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,QAAQ,KAAK;AACzD,UAAM,IAAI,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,CAAW,IAAI;AACjD,QAAI,CAAC,KAAK,EAAE,SAAS,gBAAiB;AACtC,UAAM,IAAI,EAAE;AACZ,QAAI,CAAC,KAAK,EAAE,SAAS,UAAW;AAChC,UAAM,KAAK,KAAK,MAAM,OAAO,EAAE,aAAa,EAAE,CAAC,KAAK,KAAK,IAAI;AAC7D,UAAM,OAAO,UAAU,EAAE,SAAS,CAAC,cAAc,aAAa,CAAC,EAAE,KAAK;AACtE,QAAI,CAAC,KAAM;AACX,QAAI,EAAE,SAAS,QAAQ;AACrB,UAAI,cAAc,IAAI,EAAG;AACzB,WAAK,KAAK,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,IAC/C,WAAW,EAAE,SAAS,YAAa,MAAK,KAAK,EAAE,MAAM,aAAa,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,EACvF;AACA,SAAO,UAAU,MAAM,MAAM,KAAK,CAAC;AACrC;AAGO,SAAS,WAAW,OAAiB,MAA8B;AACxE,QAAM,OAAc,CAAC;AACrB,WAAS,IAAI,MAAM,IAAI,MAAM,QAAQ,KAAK;AACxC,UAAM,IAAI,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,CAAW,IAAI;AACjD,QAAI,CAAC,EAAG;AACR,UAAM,OAAO,EAAE,SAAS,SAAS,SAAS,EAAE,SAAS,YAAY,EAAE,SAAS,UAAU,cAAc;AACpG,QAAI,CAAC,KAAM;AACX,UAAM,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE;AACxC,UAAM,QAAQ,OAAO,QAAQ,WAAW,MAAM,UAAU,KAAK,CAAC,MAAM,CAAC,GAAG,KAAK;AAC7E,QAAI,CAAC,KAAM;AACX,UAAM,KAAK,KAAK,MAAM,OAAO,EAAE,aAAa,EAAE,CAAC,KAAK,KAAK,IAAI;AAC7D,SAAK,KAAK,EAAE,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,EACvC;AACA,SAAO,UAAU,MAAM,MAAM,KAAK,CAAC;AACrC;AAGO,SAAS,gBAAgB,OAAiB,MAA8B;AAC7E,QAAM,OAAc,CAAC;AACrB,WAAS,IAAI,MAAM,IAAI,MAAM,QAAQ,KAAK;AACxC,UAAM,IAAI,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,CAAW,IAAI;AACjD,QAAI,CAAC,KAAK,OAAO,EAAE,YAAY,SAAU;AACzC,UAAM,KAAK,KAAK,MAAM,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC,KAAK,KAAK,IAAI;AAC7E,QAAI,EAAE,SAAS,cAAc;AAC3B,YAAM,IAAI,iDAAiD,KAAK,EAAE,OAAO;AACzE,YAAM,QAAQ,IAAK,EAAE,CAAC,IAAe,EAAE,SAAS,KAAK;AACrD,UAAI,KAAM,MAAK,KAAK,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,IACzD,WAAW,EAAE,SAAS,oBAAoB;AACxC,YAAM,OAAO,EAAE,QAAQ,KAAK;AAC5B,UAAI,KAAM,MAAK,KAAK,EAAE,MAAM,aAAa,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,IAC9D;AAAA,EACF;AACA,SAAO,UAAU,MAAM,MAAM,KAAK,CAAC;AACrC;;;ACnQA,SAAS,YAAY,WAAW,cAAc,YAAY,qBAAqB;AAC/E,OAAOE,WAAU;AAiBV,IAAM,OAAO;AACb,IAAM,SAAS,CAAC,MACrB,MAAM,QAAQ,GAAG,KAAK,KAAK,EAAE,MAAM,KAAK,CAAC,MAAM,OAAO,EAAE,YAAY,YAAY,KAAK,KAAK,EAAE,OAAO,CAAC;AA2B/F,SAAS,aAAa,MAAwB;AACnD,MAAI;AACJ,MAAI;AACF,UAAM,aAAa,MAAM,MAAM;AAAA,EACjC,QAAQ;AACN,WAAO,EAAE,IAAI,MAAM,SAAS,OAAO,OAAO,CAAC,GAAG,KAAK,IAAI,QAAQ,MAAM,KAAK,MAAM,cAAc,MAAM,KAAK,MAAM;AAAA,EACjH;AAGA,QAAM,MAAM,IAAI,WAAW,CAAC,MAAM;AAClC,MAAI,IAAK,OAAM,IAAI,MAAM,CAAC;AAC1B,MAAI;AACJ,MAAI;AACF,YAAQ,KAAK,MAAM,GAAG;AAAA,EACxB,QAAQ;AACN,UAAM,OAAO,iBAAiB,KAAK,GAAG,IAAI,qCAAqC;AAC/E,WAAO,EAAE,IAAI,OAAO,SAAS,MAAM,QAAQ,KAAK;AAAA,EAClD;AACA,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK;AAC5D,WAAO,EAAE,IAAI,OAAO,SAAS,MAAM,QAAQ,oBAAoB;AACjE,QAAM,MAAM,IAAI,SAAS,MAAM,IAAI,SAAS;AAC5C,QAAM,IAAI,eAAe,KAAK,GAAG;AACjC,QAAM,SAAS,IAAI,CAAC,KAAK;AACzB,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,SAAS,KAAK,GAAG;AAAA,IAC/B;AAAA,EACF;AACF;AAGO,SAAS,WACd,OACA,GACQ;AACR,QAAM,OAAO,KAAK,UAAU,OAAO,MAAM,EAAE,MAAM,EAAE,QAAQ,OAAO,EAAE,GAAG;AACvE,UAAQ,EAAE,MAAM,WAAW,OAAO,EAAE,eAAe,OAAO,EAAE,MAAM;AACpE;AAGO,SAAS,SAAS,MAAuC;AAC9D,QAAM,IAAI,aAAa,IAAI;AAC3B,SAAO,EAAE,KAAK,EAAE,QAAQ,CAAC;AAC3B;AASA,SAAS,QACP,MACA,GACA,MACA,SACkC;AAClC,QAAM,EAAE,MAAM,QAAQ,IAAI;AAC1B,QAAM,MAAM,SAAS,IAAI,QAAQ,QAAQ,OAAO,CAAC,CAAC;AAClD,MAAI,SAAS,YAAY,YAAY,UAAU,YAAY;AACzD,WAAO,EAAE,MAAM,SAAS,OAAO,SAAS,MAAM,MAAM,GAAG,GAAG,gBAAgB;AAC5E,SAAO;AAAA,IACL;AAAA,IACA,SAAS;AAAA,IACT,SAAS;AAAA,IACT,GAAI,UAAU,CAAC,IAAI,EAAE,SAAS,KAAK;AAAA,IACnC,MAAM,GAAG,GAAG;AAAA,EACd;AACF;AAMO,SAAS,UAAU,MAAc,MAAc,UAAU,MAA4B;AAC1F,MAAI;AACF,cAAUA,MAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACjD,kBAAc,MAAM,IAAI;AACxB,WAAO;AAAA,EACT,SAAS,GAAG;AACV,WAAO,QAAQ,MAAM,GAAG,SAAS,OAAO;AAAA,EAC1C;AACF;AAMO,SAAS,cAAc,MAAc,OAA8B;AACxE,MAAI;AACF,eAAW,IAAI;AACf,WAAO,EAAE,MAAM,SAAS,MAAM,MAAM,mCAAmC;AAAA,EACzE,SAAS,GAAG;AACV,UAAM,MAAM,QAAQ,MAAM,GAAG,UAAU,IAAI;AAC3C,WACE,UAAU,MAAM,KAAK,KAAK;AAAA,MACxB;AAAA,MACA,SAAS;AAAA,MACT,MAAM,YAAY,IAAI,KAAK,QAAQ,kBAAkB,EAAE,CAAC;AAAA,IAC1D;AAAA,EAEJ;AACF;AAEA,SAAS,SAAS,OAAe,SAAiB,KAAkB,GAAwB;AAC1F,QAAM,UAAU,EAAE,WAAW,KAAK;AAClC,SAAO;AAAA,IACL,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC3C,OAAO,CAAC,EAAE,MAAM,WAAW,SAAS,IAAI,CAAC,SAAS,KAAK,CAAC,EAAE,SAAS,SAAS,EAAE,aAAa,KAAK,KAAK,EAAE,QAAQ,CAAC;AAAA,EAClH;AACF;AAEO,SAAS,oBACd,MACA,SACA,QACA,KACA,GACe;AACf,QAAM,IAAI,aAAa,IAAI;AAC3B,MAAI,CAAC,EAAE,GAAI,QAAO,EAAE,MAAM,SAAS,OAAO,SAAS,MAAM,MAAM,GAAG,EAAE,MAAM,gBAAgB;AAC1F,QAAMC,YAAW,EAAE;AACnB,QAAM,QAASA,UAAS,SAAS,OAAOA,UAAS,UAAU,WAAWA,UAAS,QAAQ,CAAC;AAIxF,MAAI,UAAU;AACd,QAAM,OAAO,IAAI,IAAI,MAAM;AAE3B,aAAW,MAAM,OAAO,KAAK,KAAK,GAAG;AACnC,QAAI,KAAK,IAAI,EAAE,EAAG;AAClB,UAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,CAAC,IAAI,MAAM,EAAE,IAAI,CAAC;AACrD,UAAM,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,OAAO,CAAU,CAAC;AACnD,QAAI,KAAK,WAAW,KAAK,QAAQ;AAC/B,gBAAU;AACV,UAAI,KAAK,OAAQ,OAAM,EAAE,IAAI;AAAA,UACxB,QAAO,MAAM,EAAE;AAAA,IACtB;AAAA,EACF;AACA,aAAW,MAAM,QAAQ;AACvB,UAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,CAAC,IAAI,MAAM,EAAE,IAAI,CAAC;AACrD,UAAMC,SAAQ,SAAS,IAAI,SAAS,KAAK,CAAC;AAC1C,UAAM,OAAO,KAAK,OAAO,CAAC,MAAM,OAAO,CAAU,CAAC;AAClD,QAAI,KAAK,WAAW,KAAK,KAAK,UAAU,KAAK,CAAC,CAAC,MAAM,KAAK,UAAUA,MAAK,EAAG;AAC5E,UAAM,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,OAAO,CAAU,CAAC,EAAE,OAAO,CAACA,MAAK,CAAC;AAClE,cAAU;AAAA,EACZ;AACA,MAAI,EAAE;AACJ,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,EAAE,GAAG;AACvC,UAAID,UAAS,CAAC,MAAM,GAAG;AACrB,QAAAA,UAAS,CAAC,IAAI;AACd,kBAAU;AAAA,MACZ;AAAA;AACJ,MAAI,SAAS;AACX,IAAAA,UAAS,QAAQ;AACjB,UAAM,MAAM,UAAU,MAAM,WAAWA,WAAU,CAAC,GAAG,EAAE,OAAO;AAC9D,QAAI,IAAK,QAAO;AAAA,EAClB;AACA,SAAO,EAAE,MAAM,SAAS,SAAS,CAAC,EAAE,WAAW,QAAQ;AACzD;AAEO,SAAS,sBACd,MACA,QACA,OAA0C,CAAC,GAC5B;AACf,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO,EAAE,MAAM,SAAS,MAAM;AACrD,QAAM,IAAI,aAAa,IAAI;AAC3B,MAAI,CAAC,EAAE,GAAI,QAAO,EAAE,MAAM,SAAS,OAAO,SAAS,MAAM,MAAM,GAAG,EAAE,MAAM,gBAAgB;AAC1F,QAAMA,YAAW,EAAE;AACnB,QAAM,QAAQA,UAAS;AACvB,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO,EAAE,MAAM,SAAS,MAAM;AACvE,MAAI,UAAU;AACd,aAAW,MAAM,CAAC,GAAG,QAAQ,GAAG,OAAO,KAAK,KAAK,CAAC,GAAG;AACnD,UAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,CAAC,IAAI,MAAM,EAAE,IAAI,CAAC;AACrD,UAAM,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,OAAO,CAAU,CAAC;AACnD,QAAI,KAAK,WAAW,KAAK,QAAQ;AAC/B,gBAAU;AACV,UAAI,KAAK,OAAQ,OAAM,EAAE,IAAI;AAAA,UACxB,QAAO,MAAM,EAAE;AAAA,IACtB;AAAA,EACF;AACA,MAAI,CAAC,QAAS,QAAO,EAAE,MAAM,SAAS,MAAM;AAC5C,MAAI,CAAC,OAAO,KAAK,KAAK,EAAE,OAAQ,QAAOA,UAAS;AAChD,MAAI,KAAK,WAAW,CAAC,OAAO,KAAKA,SAAQ,EAAE,OAAQ,QAAO,cAAc,MAAM,WAAWA,WAAU,CAAC,CAAC;AACrG,SAAO,UAAU,MAAM,WAAWA,WAAU,CAAC,CAAC,KAAK,EAAE,MAAM,SAAS,KAAK;AAC3E;AAEO,SAAS,sBACd,MACA,SACA,QACA,KACA,GACiC;AACjC,QAAM,IAAI,aAAa,IAAI;AAC3B,QAAM,QAAS,EAAE,KAAM,EAAE,MAAM,SAAS,CAAC,IAAK,CAAC;AAC/C,MAAI,UAAU;AACd,MAAI,UAAU;AACd,aAAW,MAAM,QAAQ;AACvB,UAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,CAAC,IAAI,MAAM,EAAE,IAAI,CAAC;AACrD,UAAM,OAAO,KAAK,OAAO,CAAC,MAAM,OAAO,CAAU,CAAC;AAClD,QAAI,KAAK,OAAQ;AACjB,QAAI,KAAK,WAAW,KAAK,KAAK,UAAU,KAAK,CAAC,CAAC,MAAM,KAAK,UAAU,SAAS,IAAI,SAAS,KAAK,CAAC,CAAC,EAAG;AAAA,EACtG;AAEA,aAAW,MAAM,OAAO,KAAK,KAAK;AAChC,QAAI,CAAC,OAAO,SAAS,EAAE,MAAM,MAAM,QAAQ,MAAM,EAAE,CAAC,IAAI,MAAM,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,OAAO,CAAU,CAAC;AACpG,aAAO;AACX,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,YAAY,OAAO,SAAS,YAAY;AACjD;;;ALzOA,IAAM,OAAO;AACN,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,YAAY,oBAAI,IAAI,CAAC,cAAc,oBAAoB,CAAC;AAC9D,IAAM,YAAY,CAAC,SAAoB,OAAO,cAAc,OAAO,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,aAAa;AAChH,IAAM,OAAO;AAAA,EACX,YAAY,CAAC,MAAe,kBAAkB,KAAK,CAAC,IAAI,KAAK;AAAA,EAC7D,SAAS;AAAA;AAAA;AAAA,EAGT,YAAY,CAAC,MAAe,MAAM,sBAAsB,MAAM;AAChE;AACA,IAAM,aAAa;AAKZ,SAAS,WAAW,MAAyB,QAAQ,KAAK,OAAO,IAAI,QAAQ,QAAQ,GAAa;AACvG,QAAM,MAAgB,CAAC;AACvB,QAAM,MAAM,CAAC,MAAe;AAC1B,QAAI,CAAC,EAAG;AACR,UAAM,IAAIE,MAAK,QAAQ,CAAC;AACxB,QAAI,CAAC,IAAI,SAAS,CAAC,EAAG,KAAI,KAAK,CAAC;AAAA,EAClC;AACA,MAAIA,MAAK,KAAK,MAAM,SAAS,CAAC;AAC9B,MAAI,IAAI,iBAAiB;AACzB,MAAI;AACF,eAAW,KAAKC,aAAY,MAAM,EAAE,eAAe,KAAK,CAAC,GAAG;AAC1D,UAAI,CAAC,EAAE,YAAY,KAAK,CAAC,iBAAiB,KAAK,EAAE,IAAI,EAAG;AACxD,YAAM,IAAID,MAAK,KAAK,MAAM,EAAE,IAAI;AAChC,UAAIE,YAAWF,MAAK,KAAK,GAAG,UAAU,CAAC,KAAKE,YAAWF,MAAK,KAAK,GAAG,eAAe,CAAC,EAAG,KAAI,CAAC;AAAA,IAC9F;AAAA,EACF,QAAQ;AAAA,EAAC;AACT,SAAO,IAAI,OAAO,CAAC,GAAG,MAAM,MAAM,KAAKE,YAAW,CAAC,CAAC;AACtD;AACA,IAAM,aAAa,CAAC,QAAgBF,MAAK,KAAK,KAAK,eAAe;AAClE,IAAM,cAAc,CAAC,eAAuBA,MAAK,QAAQA,MAAK,QAAQA,MAAK,QAAQ,UAAU,CAAC,CAAC;AAE/F,IAAM,YAAY,CAAC,MAA4B,OAAO,MAAM,YAAY,2BAA2B,KAAK,CAAC;AACzG,IAAM,YAAY,CAAC,MACjB,MAAM,WAAW,MAAM,YAAY,MAAM,SAAS,IAAI,MAAM,sBAAsB,SAAS;AAC7F,IAAM,YAAY,CAAC,MACjB,MAAM,sBACF,eACA,MAAM,gBACJ,SACA,MAAM,uBACJ,gBACA;AAEH,IAAM,aAAsB;AAAA,EACjC,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ,CAAC,MAAM,QAAQ,QAAQ,WAAW,GAAG,EAAE,KAAK,CAAC,MAAME,YAAW,CAAC,CAAC;AAAA,EACxE,aAAa,CAAC,MAAM,QAAQ,QAAQ,WAAW,GAAG,EAAE,IAAI,UAAU;AAAA,EAClE,SAAS,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MACxD,WAAW,GAAG,EAAE,QAAQ,CAAC,MAAM;AAC7B,UAAM,QAAQ,oBAAoB,WAAW,CAAC,GAAG,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI;AAEtF,UAAM,OACJ,KAAK,eAAe,UAAa,MAAM,UAAU,OAAO,WAAW,WAAW,CAAC,GAAG,KAAK,YAAY,GAAG;AACxG,WAAO,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK;AAAA,EACtC,CAAC;AAAA,EACH,WAAW,CAAC,MAAM,QAAQ,KAAK,OAAuB,CAAC,MACrD,WAAW,GAAG,EAAE,IAAI,CAAC,MAAM;AACzB,UAAM,OAAO,WAAW,WAAW,CAAC,GAAG,KAAK;AAC5C,UAAM,QAAQ,sBAAsB,WAAW,CAAC,GAAG,eAAe;AAAA,MAChE,SAAS,KAAK,SAAS,SAAS,WAAW,CAAC,CAAC;AAAA,IAC/C,CAAC;AAGD,QAAI,MAAM,WAAW,CAAC,KAAK,WAAW,MAAM;AAC1C,aAAO,EAAE,GAAG,OAAO,MAAM,mCAAmC,MAAM,MAAM,QAAQ,kBAAkB,EAAE,CAAC,GAAG;AAC1G,WAAO,QAAQ,CAAC,MAAM,WAAW,CAAC,MAAM,UAAU,OAAO;AAAA,EAC3D,CAAC;AAAA,EACH,WAAW,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAC1D,WAAW,GAAG,EAAE,IAAI,CAAC,OAAO;AAAA,IAC1B,MAAM,WAAW,CAAC;AAAA,IAClB,OAAO,sBAAsB,WAAW,CAAC,GAAG,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI;AAAA,EACnF,EAAE;AAAA,EAEJ,SAAS,OAAqC;AAC5C,UAAM,QAAQ,MAAM,KAAK,CAAC,KAAK;AAC/B,QAAI,IAA6B,CAAC;AAClC,QAAI;AAEF,YAAM,IAAa,MAAM,QAAQ,KAAK,MAAM,MAAM,KAAK,IAAI,CAAC;AAC5D,UAAI,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,EAAG,KAAI;AAAA,IAC3D,QAAQ;AAAA,IAAC;AACT,UAAM,KAAM,EAAE,cAAc,EAAE;AAC9B,QAAI,CAAC,GAAI,QAAO;AAChB,QAAI,cAAc,OAAO,EAAE,GAAG,CAAC,EAAG,QAAO;AACzC,UAAM,UAAiC,EAAE,IAAI,OAAO,EAAE,GAAG,KAAK,OAAO,EAAE,QAAQ,WAAW,EAAE,MAAM,MAAM,IAAI;AAC5G,QAAI,OAAO,EAAE,UAAU,SAAU,SAAQ,QAAQ,EAAE;AACnD,QAAI,OAAO,EAAE,aAAa,YAAY,MAAM,WAAW,UAAU,MAAM,SAAS,EAAE;AAChF,cAAQ,YAAY;AACtB,UAAM,UAAU,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY;AAChE,UAAM,OAAO,UAAU,EAAE,SAAS,IAAI,EAAE,YAAY;AACpD,UAAM,UAAU,OAAO,EAAE,gBAAgB,WAAW,EAAE,cAAc;AACpE,UAAM,KAAK,OAAO,EAAE,gBAAgB,WAAW,KAAK,MAAM,EAAE,WAAW,IAAI;AAC3E,UAAM,WAAW,OAAO,EAAE,aAAa,WAAW,EAAE,WAAW;AAI/D,UAAM,eAAe,MACnB,QAAQC,YAAW,QAAQ,EACxB,OAAO,GAAG,WAAW,EAAE,IAAI,QAAQ,EAAE,IAAI,KAAK,UAAU,EAAE,cAAc,IAAI,CAAC,EAAE,EAC/E,OAAO,KAAK,EACZ,MAAM,GAAG,EAAE,CAAC;AACjB,UAAM,KAAK,CAAC,MAAc,UAAmC,EAAE,MAAM,MAAM,MAAM,IAAI,GAAG,cAAc,MAAM;AAC5G,UAAM,SAA+B,CAAC;AACtC,YAAQ,OAAO;AAAA,MACb,KAAK;AACH,eAAO,KAAK,GAAG,mBAAmB,EAAE,QAAQ,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;AAChF;AAAA,MACF,KAAK,oBAAoB;AAEvB,cAAM,SAAS,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS;AACzD,eAAO;AAAA,UACL,GAAG,gBAAgB;AAAA,YACjB;AAAA,YACA,cAAc,WAAW,SAAY,SAAY,CAAC,GAAG,MAAM,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAM7D,cAAc,YAAY,EAAE,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAMlC,UAAU,WAAW,UAAa,cAAc,MAAM,IAAI,OAAO;AAAA,UACnE,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAAA,MACA,KAAK;AACH,YAAI,KAAM,QAAO,KAAK,GAAG,gBAAgB,EAAE,MAAM,SAAS,SAAS,UAAU,OAAO,EAAE,WAAW,CAAC,CAAC;AACnG;AAAA,MACF,KAAK;AACH,YAAI;AACF,iBAAO;AAAA,YACL,GAAG,cAAc;AAAA,cACf;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,IAAI;AAAA,cACJ,OAAO,EAAE;AAAA,cACT,QAAQ,EAAE;AAAA,YACZ,CAAC;AAAA,UACH;AACF;AAAA,MACF,KAAK;AACH,YAAI;AACF,iBAAO;AAAA,YACL,GAAG,cAAc;AAAA,cACf;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,IAAI;AAAA,cACJ,OAAO,QAAQ,EAAE,SAAS,EAAE,aAAa;AAAA,cACzC,OAAO,EAAE;AAAA,cACT,QAAQ,EAAE;AAAA,YACZ,CAAC;AAAA,UACH;AACF;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,oBAAoB;AAAA,YACrB,cAAc,WAAW,aAAa;AAAA,YACtC,MAAM;AAAA,YACN;AAAA,YACA,SAAS,OAAO,GAAG,IAAI,GAAG,SAAS,EAAE,UAAU,CAAC,KAAK;AAAA,YACrD;AAAA,UACF,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,oBAAoB;AAAA,YACrB,cAAc,SAAS,KAAK,IAAI,CAAC;AAAA,YACjC,MAAM,UAAU,EAAE,iBAAiB;AAAA,YACnC,SAAS,OAAO,EAAE,YAAY,WAAW,EAAE,QAAQ,MAAM,GAAG,GAAG,IAAI;AAAA,YACnE;AAAA,UACF,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AAIH,eAAO,KAAK,GAAG,cAAc,EAAE,SAAS,QAAQ,QAAQ,YAAY,gBAAgB,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAC1G;AAAA,MACF,KAAK;AAGH,eAAO;AAAA,UACL,GAAG,cAAc;AAAA,YACf;AAAA,YACA,QAAQ;AAAA,YACR,OAAO,OAAO,EAAE,UAAU,WAAW,EAAE,MAAM,MAAM,GAAG,GAAG,IAAI;AAAA,UAC/D,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,oBAAoB;AAAA,YACrB,UAAU,OAAO,EAAE,YAAY,EAAE;AAAA,YACjC,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;AAAA,UAChE,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AAGH,eAAO;AAAA,UACL,GAAG,kBAAkB;AAAA,YACnB,UAAU,OAAO,EAAE,YAAY,EAAE;AAAA,YACjC,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;AAAA,YAC9D,YAAY,gBAAgB,EAAE,kBAAkB,OAAO,EAAE,YAAY,EAAE,CAAC;AAAA,UAC1E,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,qBAAqB,EAAE,SAAS,EAAE,YAAY,WAAW,WAAW,OAAO,CAAC,CAAC;AAC5F;AAAA,MACF,KAAK;AACH;AAAA;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,iBAAiB,EAAE,QAAQ,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;AAChE;AAAA,MACF,KAAK,cAAc;AAGjB,cAAM,SAAS,aAAa,EAAE,WAAW;AACzC,YAAI,CAAC,OAAQ,QAAO;AACpB,eAAO,KAAK,GAAG,mBAAmB,MAAM,CAAC;AACzC;AAAA,MACF;AAAA,MACA;AACE,eAAO;AAAA,IACX;AACA,UAAM,QAAiC,CAAC;AAGxC,QAAI,UAAU,sBAAsB,OAAO,EAAE,WAAW,SAAU,OAAM,SAAS,EAAE;AACnF,UAAM,OAAO,MAAM,IAAI;AACvB,QAAI,QAAQ,WAAW,KAAK,IAAI,EAAG,OAAM,aAAa;AAItD,UAAM,WAAW,MAAM,IAAI;AAC3B,QAAI,aAAa,OAAO,aAAa,IAAK,OAAM,WAAW,aAAa,OAAO,CAAC,CAAC,MAAM,IAAI;AAG3F,QAAI,UAAU,gBAAgB,UAAU,cAAe,OAAM,aAAa,UAAU,MAAM,GAAG;AAC7F,UAAM,IAAgB,EAAE,SAAS,QAAQ,MAAM;AAC/C,QAAI,OAAO,EAAE,oBAAoB,SAAU,GAAE,aAAa,EAAE;AAC5D,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,MAAM,UAAU;AAC7B,QAAI,OAAO;AACX,QAAI;AACF,aAAOC,cAAa,MAAM,MAAM;AAAA,IAClC,QAAQ;AACN,aAAO,EAAE,OAAO,CAAC,GAAG,MAAM,SAAS;AAAA,IACrC;AACA,WAAO,WAAW,KAAK,MAAM,IAAI,GAAG,QAAQ;AAAA,EAC9C;AAAA,EAEA,MAAM,SAAS,YAAY,OAAO,OAAqB;AACrD,UAAM,MAAoB,CAAC;AAC3B,QAAI,WAAY,QAAO,OAAO,KAAK,YAAY,YAAY,QAAQ,IAAI,OAAO,KAAK,CAAC;AACpF,UAAM,MAAM,IAAI,OAAO,QAAQ;AAC/B,WAAO,OAAO,KAAK,SAAS,GAAG,GAAG,IAAI,SAAS,EAAE,QAAQ,IAAI,OAAO,IAAI,CAAC,CAAC;AAC1E,QAAI,CAAC,IAAI,OAAO,OAAO,MAAM,eAAe,SAAU,KAAI,MAAM,0BAA0B,MAAM,UAAU;AAK1G,QAAI,OAAO,MAAM,aAAa,UAAW,KAAI,WAAW,MAAM,YAAY,CAAC,CAAC,IAAI;AAChF,WAAO;AAAA,EACT;AAAA,EAEA,aAAa,CAAC,SAAS,OAAO,YAAY,OAAO,MAAM,QAAQ,QAC7D,kBAAkB,QAAQ,IAAI,OAAO,YAAY,OAAO,GAAG;AAAA,EAE7D,SAAS,SAAS,MAAM,QAAQ,KAAoB;AAClD,UAAMC,QAAsB,CAAC;AAC7B,UAAM,QAAkB,CAAC;AACzB,eAAW,KAAK,WAAW,GAAG;AAC5B,YAAM,KAAK,GAAG,YAAYL,MAAK,KAAK,GAAG,UAAU,GAAG,GAAG,SAAS,CAAC,MAAM,yBAAyB,KAAK,CAAC,CAAC,CAAC;AAC1G,UAAM,UAAsB,EAAE,KAAK,OAAO,CAAC,IAAI,MAAM,MAAM;AAAA,IAAC,EAAE;AAC9D,eAAW,KAAK,OAAO;AACrB,YAAM,KAAKA,MAAK,SAAS,GAAG,QAAQ;AACpC,YAAM,QAAQ,YAAY,GAAG,IAAI,CAAC,GAAG,OAAO;AAC5C,UAAI,CAAC,MAAM,cAAc,CAAC,MAAM,QAAS;AACzC,YAAM,EAAE,YAAY,SAAS,WAAW,KAAK,iBAAiB,KAAK,GAAG,KAAK,IAAI;AAC/E,YAAM,MAAM,KAAK;AACjB,MAAAK,MAAK,KAAK;AAAA,QACR,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG,GAAG,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC,EAAG;AAAA,QAC1F;AAAA,QACA;AAAA,QACA,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AACA,WAAOA;AAAA,EACT;AACF;AAKA,IAAM,YAAY;AAOX,SAAS,gBAAgB,OAAgB,QAAqC;AACnF,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO;AAClC,SAAO,MAAM,OAAO,CAAC,MAAM;AACzB,UAAM,OAAO;AACb,QAAI,WAAW,UAAa,MAAM,OAAO,OAAQ,QAAO;AACxD,WAAO,EAAE,OAAO,MAAM,YAAY,YAAY,UAAU,KAAK,KAAK,OAAO;AAAA,EAC3E,CAAC,EAAE;AACL;AAKA,IAAM,iBAAiB;AAKvB,SAAS,WAAW,MAAc,MAAe,KAAyC;AACxF,QAAM,IAAI,aAAa,IAAI;AAC3B,MAAI,CAAC,EAAE,GAAI,QAAO;AAClB,QAAM,MAAM,EAAE,MAAM;AACpB,QAAM,OAAO,OAAO,KAAK,YAAY,YAAY,KAAK,KAAK,IAAI,OAAO;AACtE,QAAM,UAAU,MAAM,CAAC,MAAM,YAAY,CAAC,EAAE;AAC5C,MAAI,QAAQ,OAAO,CAAC;AAClB,WAAO,EAAE,MAAM,SAAS,OAAO,SAAS,MAAM,MAAM,wDAAwD;AAC9G,MAAI,OAAO,QAAQ,KAAK,YAAY,WAAW,KAAK,oBAAoB,iBAAiB,CAAC,KAAM,QAAO;AACvG,MAAI,KAAM,GAAE,MAAM,aAAa,EAAE,MAAM,WAAW,SAAS,iBAAiB,eAAe;AAAA,MACtF,QAAO,EAAE,MAAM;AACpB,SACE,UAAU,MAAM,WAAW,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,KAAK;AAAA,IACpD;AAAA,IACA,SAAS;AAAA,IACT,MAAM,OAAO,yCAAyC;AAAA,EACxD;AAEJ;AAGA,IAAM,WAAW,CAAC,MAChB,OAAO,MAAM,YAAY,IAAI,KAAK,IAAI,OAAO,IAAI,KAAK,IAAI,GAAI,EAAE,YAAY,IAAI;AAElF,IAAM,iBAAyC,EAAE,WAAW,KAAK,WAAW,MAAO;AAM5E,SAAS,aAAa,IAA+C;AAC1E,MAAI,CAAC,MAAM,OAAO,OAAO,YAAY,MAAM,QAAQ,EAAE,EAAG,QAAO;AAC/D,QAAM,UAAgD,CAAC;AACvD,aAAW,CAAC,IAAI,CAAC,KAAK,OAAO,QAAQ,EAAwE,GAAG;AAC9G,QAAI,CAAC,oBAAoB,KAAK,EAAE,KAAK,OAAO,GAAG,oBAAoB,YAAY,EAAE,kBAAkB,EAAG;AACtG,YAAQ;AAAA,MACN,MAAM;AAAA,QACJ;AAAA,QACA,cAAc,EAAE;AAAA,QAChB,gBAAgB,eAAe,EAAE;AAAA,QACjC,WAAW,SAAS,EAAE,SAAS;AAAA,MACjC,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,QAAQ,SAAS,EAAE,SAAS,QAAQ,MAAM,GAAG,CAAC,EAAE,IAAI;AAC7D;AAEA,SAAS,MAAyC,GAAS;AACzD,aAAW,KAAK,OAAO,KAAK,CAAC,EAAG,KAAI,EAAE,CAAC,MAAM,OAAW,QAAO,EAAE,CAAC;AAClE,SAAO;AACT;AACA,IAAM,OAAO;AAMN,SAAS,cAAc,IAAY,GAAqC;AAC7E,SAAO,OAAO,EAAE,oBAAoB,YAAY,CAAC,KAAK,KAAK,EAAE;AAC/D;AACA,SAAS,WAAW,GAAoB;AACtC,SAAO,OAAO,MAAM,YAAY,CAAC,WAAW,UAAU,SAAS,WAAW,MAAM,EAAE,SAAS,CAAC,IAAI,IAAI;AACtG;AAMA,IAAM,cAAc,CAAC,MACnB,OAAO,MAAM,YAAY,oCAAoC,KAAK,CAAC,IAAI,IAAI;AAU7E,IAAM,UACJ;AACF,IAAM,gBAAgB,CAAC,WAA4B,QAAQ,KAAK,OAAO,KAAK,CAAC;AAC7E,SAAS,QAAQ,GAAgC;AAC/C,MAAI,OAAO,MAAM,SAAU,QAAO,EAAE,MAAM,GAAG,GAAI;AACjD,MAAI,KAAK,OAAO,MAAM,UAAU;AAC9B,UAAM,IAAI;AACV,QAAI,OAAO,EAAE,UAAU,SAAU,QAAO,EAAE,MAAM,MAAM,GAAG,GAAI;AAC7D,QAAI,OAAO,EAAE,YAAY,SAAU,QAAO,EAAE,QAAQ,MAAM,GAAG,GAAI;AAAA,EACnE;AACA,SAAO;AACT;AACA,SAAS,SAAS,OAAwB;AACxC,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,IAAI;AACV,QAAM,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,OAAO,EAAE;AAC3D,SAAO,OAAO,MAAM,WAAW,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,KAAK;AACzD;AAIA,IAAM,YAAkD;AAAA,EACtD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAOA,SAAS,cAAc,GAA6B;AAClD,MAAI,CAAC,KAAK,OAAO,MAAM,SAAU,QAAO;AACxC,QAAM,IAAI;AACV,QAAM,SAAS,EAAE,aAAa,UAAU,EAAE,UAAU,IAAI;AACxD,SAAO;AAAA,IACL,WAAW,EAAE,YAAY,OAAO,EAAE,SAAS,IAAI;AAAA,IAC/C,MAAM,UAAU,OAAO,EAAE,SAAS,YAAY,EAAE,KAAK,KAAK,IAAI,EAAE,KAAK,KAAK,IAAI;AAAA,IAC9E,aAAa,UAAU;AAAA,IACvB,QAAQ,OAAO,EAAE,oBAAoB,WAAW,EAAE,kBAAkB;AAAA,EACtE;AACF;AACA,IAAM,YAAY,oBAAI,IAAmC;AACzD,SAAS,WAAW,KAAoC;AACtD,QAAM,MAAM,UAAU,IAAI,GAAG;AAC7B,MAAI,IAAK,QAAO;AAChB,QAAM,MAAM,oBAAI,IAAsB;AACtC,MAAI,QAAkB,CAAC;AACvB,MAAI;AACF,YAAQJ,aAAYD,MAAK,KAAK,KAAK,UAAU,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AAAA,EACnF,QAAQ;AAAA,EAAC;AACT,aAAW,KAAK,OAAO;AACrB,QAAI;AACF,YAAM,IAAI,cAAc,KAAK,MAAMI,cAAaJ,MAAK,KAAK,KAAK,YAAY,CAAC,GAAG,MAAM,CAAC,CAAC;AACvF,UAAI,GAAG,UAAW,KAAI,IAAI,EAAE,WAAW,CAAC;AAAA,IAC1C,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,YAAU,IAAI,KAAK,GAAG;AACtB,SAAO;AACT;AACA,SAAS,gBAAgB,KAAa,KAA+B;AACnE,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI;AACF,WAAO,cAAc,KAAK,MAAMI,cAAaJ,MAAK,KAAK,KAAK,YAAY,GAAG,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC;AAAA,EAClG,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,UAAU,MAAyB,QAAQ,KAAoB;AAC7E,MAAI;AACF,UAAM,OAAO,WAAW,GAAG;AAC3B,UAAM,MAAM,CAACM,SAAgB,KAAK,KAAK,CAAC,MAAMJ,YAAWF,MAAK,KAAK,GAAG,YAAY,GAAGM,IAAG,OAAO,CAAC,CAAC;AACjG,QAAI,MAAM,QAAQ;AAClB,QAAI,IAAI,GAAG,EAAG,QAAO;AACrB,UAAM,SAAS,SAAS;AACxB,aAAS,MAAM,GAAG,MAAM,KAAK,MAAM,GAAG,OAAO;AAC3C,YAAM,OAAO,GAAG;AAChB,UAAI,MAAM,KAAK,IAAI,GAAG,EAAG,QAAO;AAAA,IAClC;AAAA,EACF,QAAQ;AAAA,EAAC;AACT,SAAO;AACT;AACA,SAAS,WAAoC;AAC3C,MAAI,QAAQ,aAAa,SAAS;AAChC,WAAO,CAAC,QAAQ;AACd,UAAI;AACF,cAAM,KAAKF,cAAa,SAAS,GAAG,SAAS,MAAM;AACnD,eAAO,OAAO,GAAG,MAAM,GAAG,YAAY,GAAG,IAAI,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK;AAAA,MACpE,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,QAAM,QAAQ,oBAAI,IAAoB;AACtC,MAAI;AACF,UAAM,MAAMG,cAAa,MAAM,CAAC,QAAQ,YAAY,GAAG;AAAA,MACrD,UAAU;AAAA,MACV,SAAS;AAAA,MACT,WAAW,IAAI,OAAO;AAAA,MACtB,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,IACpC,CAAC;AACD,eAAW,KAAK,IAAI,MAAM,IAAI,GAAG;AAC/B,YAAM,IAAI,oBAAoB,KAAK,CAAC;AACpC,UAAI,EAAG,OAAM,IAAI,OAAO,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC;AAAA,IAC7C;AAAA,EACF,QAAQ;AAAA,EAAC;AACT,SAAO,CAAC,QAAQ,MAAM,IAAI,GAAG,KAAK;AACpC;AACA,IAAM,WAAW;AAIjB,SAAS,YAAY,KAAa,OAAO,QAAQ,GAAyD;AACxG,QAAM,QAAQ,CAACP,MAAK,KAAK,KAAK,cAAc,CAAC;AAC7C,MAAIA,MAAK,QAAQ,GAAG,MAAMA,MAAK,KAAK,MAAM,SAAS,EAAG,OAAM,QAAQA,MAAK,KAAK,MAAM,cAAc,CAAC;AACnG,aAAW,KAAK,OAAO;AACrB,QAAI;AACF,YAAM,IACJ,KAAK,MAAMI,cAAa,GAAG,MAAM,CAAC,EAClC;AACF,UAAI,OAAO,GAAG,gBAAgB,YAAY,EAAE,aAAa;AACvD,cAAM,QAAQ,OAAO,EAAE,iBAAiB,YAAY,SAAS,KAAK,EAAE,YAAY,IAAI,EAAE,eAAe;AACrG,eAAO,EAAE,IAAI,EAAE,YAAY,MAAM,GAAG,GAAG,GAAG,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,EAAG;AAAA,MACxE;AAAA,IACF,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,SAAO,WAAW,GAAG;AACvB;AAEA,IAAM,iBAAiB;AAMvB,SAAS,WAAW,KAAkD;AACpE,QAAM,OAAQ,SAAS,WAAW,GAAG,CAAC,EAAiD,KAAK;AAC5F,MAAI,OAAO,SAAS,SAAU,QAAO;AACrC,MAAI;AACJ,MAAI;AACF,QAAI,IAAI,IAAI,KAAK,KAAK,CAAC;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAK,EAAE,aAAa,YAAY,EAAE,aAAa,WAAY,CAAC,EAAE,YAAY,eAAe,KAAK,EAAE,QAAQ;AACtG,WAAO;AAET,QAAM,SAAS,GAAG,EAAE,QAAQ,KAAK,EAAE,IAAI;AACvC,SAAO,EAAE,IAAI,OAAOD,YAAW,QAAQ,EAAE,OAAO,MAAM,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC,IAAI,MAAM,EAAE,KAAK;AACrG;AACA,IAAM,eAAe,oBAAI,IAA2B;AAIpD,SAAS,cAAc,KAA4B;AACjD,QAAM,MAAM,aAAa,IAAI,GAAG;AAChC,MAAI,QAAQ,OAAW,QAAO;AAC9B,QAAM,OAAO,YAAY,GAAG,GAAG,MAAM;AACrC,eAAa,IAAI,KAAK,IAAI;AAC1B,SAAO;AACT;AAcA,IAAM,UAAU;AAChB,SAAS,eAAe,SAAmD;AACzE,WAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC5C,UAAM,IAAI,QAAQ,CAAC;AACnB,QAAI,EAAE,SAAS,YAAY,EAAE,YAAY,mBAAmB,OAAO,EAAE,QAAQ,YAAY,QAAQ,KAAK,EAAE,GAAG;AACzG,aAAO,EAAE;AACX,QAAI,EAAE,SAAS,oBAAoB,OAAO,EAAE,oBAAoB,UAAU;AACxE,YAAM,IAAI,mCAAmC,KAAK,EAAE,eAAe;AACnE,UAAI,EAAG,QAAO,kCAAkC,EAAE,CAAC,CAAC;AAAA,IACtD;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,SAAS,MAAsB;AAC7C,QAAM,IAAI,OAAO,IAAI,EAAE,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACjD,SAAO,EAAE,SAAS,KAAK,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,QAAQ;AAClD;AACA,SAAS,QAAQ,GAAkD;AACjE,MAAI,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,UAAU,EAAE,YAAa,QAAO;AACjE,QAAM,SAAS,EAAE;AACjB,MAAI,QAAQ,QAAQ,OAAO,SAAS,QAAS,QAAO;AACpD,QAAM,IAAK,EAAE,SAA+C;AAC5D,QAAM,QACJ,OAAO,MAAM,WACT,IACA,MAAM,QAAQ,CAAC,IACb,EACG,OAAO,CAAC,MAAM,KAAK,EAAE,SAAS,UAAU,OAAO,EAAE,SAAS,QAAQ,EAClE,IAAI,CAAC,MAAM,EAAE,IAAc,EAC3B,KAAK,IAAI,IACZ,IACN,KAAK;AACP,MAAI,CAAC,QAAQ,WAAW,IAAI,EAAG,QAAO;AACtC,SAAO;AACT;AACA,SAAS,SAAS,SAAmD;AACnE,aAAW,KAAK,SAAS;AACvB,UAAM,IAAI,QAAQ,CAAC;AACnB,QAAI,EAAG,QAAO,SAAS,CAAC;AAAA,EAC1B;AACA,SAAO;AACT;AACA,SAAS,WAAW,MAAc,MAAc,OAAuD;AACrG,QAAM,MAAM,EAAE,QAAQ,MAAM,UAAU,MAAM,IAAI,MAAM,MAAM,MAAM,MAAM,KAAK;AAC7E,MAAI,OAAO;AACX,MAAI;AACF,WAAOK,UAAS,IAAI,EAAE;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,OAAO,MAAM;AACf,QAAI,OAAO;AACX,QAAI,SAAS,IAAI,KAAK;AACtB,WAAO;AAAA,EACT;AAEA,MAAI,MAAM;AACV;AAAA,IACE;AAAA,IACA,CAAC,MAAM;AACL,aAAO,OAAO,WAAW,GAAG,MAAM,IAAI;AACtC,UAAI,OAAO,QAAQ,EAAE,QAAQ,SAAS,IAAI,EAAG,QAAO;AACpD,UAAI;AACF,cAAM,IAAI,KAAK,MAAM,CAAC;AACtB,YAAI,EAAE,SAAS,kBAAkB,EAAE,YAAa,KAAI,SAAS,EAAE;AAC/D,YAAI,EAAE,SAAS,cAAc,EAAE,QAAS,KAAI,KAAK,EAAE;AAAA,MACrD,QAAQ;AAAA,MAAC;AACT,aAAO;AAAA,IACT;AAAA,IACA,MAAM,OAAO;AAAA,EACf;AACA,MAAI,OAAO;AACX,SAAO;AACT;AAKO,SAAS,YACd,MACA,WACA,OACA,OACc;AACd,QAAM,EAAE,MAAM,KAAK,IAAI,KAAK,MAAM,KAAK,MAAM,MAAM,IAAI;AACvD,QAAM,IAAI,WAAW,IAAI;AACzB,QAAM,IAAI,WAAW,IAAI;AACzB,QAAM,MAAoB,CAAC;AAC3B,QAAM,KAAK,aAAaC,MAAK,SAAS,MAAM,QAAQ;AACpD,QAAM,MAAM,MAAM,IAAI,MAAM,EAAE;AAC9B,MAAI,SAAwB;AAC5B,MAAI,KAAoB;AACxB,aAAW,KAAK,EAAE,OAAO,CAAC,GAAG;AAC3B,QAAI,EAAE,SAAS,kBAAkB,OAAO,EAAE,gBAAgB,SAAU,UAAS,EAAE;AAC/E,QAAI,EAAE,SAAS,cAAc,OAAO,EAAE,YAAY,SAAU,MAAK,EAAE;AAAA,EACrE;AACA,MAAI,OAAO;AACX,MAAI;AACF,WAAOD,UAAS,IAAI,EAAE;AAAA,EACxB,QAAQ;AAAA,EAAC;AACT,MAAI,CAAC,UAAU,CAAC,MAAM,OAAO,KAAK,OAAO,MAAM,MAAM;AACnD,UAAM,IAAI,WAAW,MAAM,OAAO,IAAI,SAAS,KAAK,GAAG;AAAA,MACrD,QAAQ,IAAI;AAAA,MACZ,IAAI,IAAI;AAAA,IACV,CAAC;AACD,aAAS,EAAE;AACX,SAAK,EAAE;AACP,UAAM,KAAK,MAAM,IAAI,EAAE,WAAW,EAAE,MAAM,aAAa,EAAE,QAAQ,SAAS,EAAE,GAAG,CAAC;AAAA,EAClF;AACA,QAAM,MAAM,YAAY,IAAI;AAC5B,QAAM,OAAO,WAAW,GAAG,EAAE,IAAI,EAAE,KAAK,gBAAgB,KAAK,MAAM,UAAU;AAC7E,MAAI,MAAM,KAAM,OAAM,KAAK,MAAM,IAAI,EAAE,MAAM,KAAK,MAAM,aAAa,KAAK,YAAY,CAAC;AACvF,QAAM,SACJ,MAAM,UAAU,yBAAyB,KAAK,KAAK,MAAM,IAAI,0BAA0B,KAAK,MAAM,KAAK;AACzG,QAAM,MAAM,UAAU,eAAe,CAAC,KAAK,eAAe,CAAC,MAAM,OAAO,IAAI,QAAQ,WAAW,IAAI,MAAM;AACzG,MAAI,KAAK;AACP,QAAI,MAAM;AACV,UAAM,KAAK,MAAM,IAAI,EAAE,IAAI,CAAC;AAAA,EAC9B;AACA,QAAM,OAAO,MAAM,OACf,EAAE,OAAO,KAAK,MAAM,QAAQ,KAAK,YAAoC,IACrE,OAAO,IAAI,SAAS,WAClB,EAAE,OAAO,IAAI,MAAM,QAAQ,IAAI,YAAoC,IACnE;AACN,QAAM,OACH,QAAQ,KAAK,WAAW,YAAY,QACpC,UAAU,EAAE,OAAO,QAAQ,QAAQ,SAAkB,KACtD,QACC,MAAM,EAAE,OAAO,IAAI,QAAQ,UAAmB;AACjD,MAAIE,OAAM,OAAO,IAAI,cAAc,WAAW,IAAI,YAAY,SAAS,CAAC;AACxE,MAAI,CAACA,QAAO,OAAO,KAAK,QAAQ,CAAC,IAAI,gBAAgB;AACnD,IAAAA,OAAM,UAAkB,MAAM,CAAC,MAAM;AACnC,UAAI;AACF,cAAM,IAAI,QAAQ,KAAK,MAAM,CAAC,CAA4B;AAC1D,eAAO,IAAI,SAAS,CAAC,IAAI;AAAA,MAC3B,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AACD,QAAI,CAACA,QAAO,OAAO,KAAK,OAAO,KAAM,OAAM,KAAK,MAAM,IAAI,EAAE,gBAAgB,KAAK,CAAC;AAAA,EACpF;AACA,MAAIA,MAAK;AACP,QAAI,YAAYA;AAChB,QAAI,CAAC,IAAI,UAAW,OAAM,KAAK,MAAM,IAAI,EAAE,WAAWA,KAAI,CAAC;AAAA,EAC7D;AACA,MAAI,MAAM;AACR,QAAI,QAAQ,OAAO,KAAK,KAAK,EAAE,MAAM,GAAG,GAAG;AAC3C,QAAI,eAAe,KAAK;AAAA,EAC1B,WAAWA,MAAK;AACd,QAAI,QAAQA;AACZ,QAAI,eAAe;AAAA,EACrB;AACA,QAAM,OAAO,cAAc,GAAG;AAC9B,MAAI,KAAM,KAAI,aAAa;AAC3B,WAAS,IAAI,EAAE,SAAS,GAAG,KAAK,GAAG,KAAK;AACtC,UAAM,IAAK,EAAE,CAAC,EAAuC,SAAS;AAC9D,QAAI,EAAE,CAAC,GAAG,SAAS,eAAe,KAAK,MAAM,eAAe;AAC1D,UAAI,QAAQ;AACZ;AAAA,IACF;AAAA,EACF;AACA,QAAM,OAAO,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO;AAC/E,MAAI,MAAM,QAAS,KAAI,kBAAkB,OAAO,KAAK,OAAO;AAC5D,QAAM,QAAQ,EAAE,OAAO,CAAC,EACrB,IAAI,CAAC,MAAM,KAAK,MAAM,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC,EAChD,OAAO,CAAC,MAAM,IAAI,CAAC;AACtB,MAAI,MAAM,QAAQ;AAChB,QAAI,aAAa,KAAK,IAAI,GAAG,KAAK;AAClC,QAAI,UAAU,KAAK,IAAI,GAAG,KAAK;AAAA,EACjC;AACA,QAAM,UAAU,EAAE,KAAK,CAAC,MAAM,OAAO,EAAE,QAAQ,QAAQ;AACvD,MAAI,SAAS;AACX,QAAI,MAAM,QAAQ;AAClB,QAAI,OAAO,QAAQ,cAAc,YAAY,QAAQ,cAAc,OAAQ,KAAI,SAAS,QAAQ;AAAA,EAClG;AACA,SAAO;AACT;AAUA,IAAM,kBAAkB;AACxB,IAAM,oBAAoB,KAAK;AAC/B,IAAM,aAAa;AACnB,IAAM,WAAW;AAEjB,IAAM,oBAAoB;AAC1B,IAAM,sBAAsB;AAE5B,IAAM,mBAAmB,KAAK;AAG9B,IAAM,YAAY,CAAC,MACjB,EACG,QAAQ,0DAA0D,GAAG,EACrE,QAAQ,QAAQ,GAAG,EACnB,KAAK;AAIV,eAAsB,kBACpB,IACA,OACA,YACA,OACA,KACA,MAAM,KAAK,IAAI,GACiB;AAChC,MAAI,CAAC,cAAc,CAAC,MAAM,OAAO,CAAC,QAAQ,KAAK,MAAM,GAAG,KAAK,MAAM,iBAAiB,SAAU,QAAO,CAAC;AACtG,QAAM,MAAM,MAAM,IAAI,MAAM,EAAE;AAC9B,MAAI,QAAQ,OAAO,IAAI,iBAAiB,WAAW,IAAI,eAAe;AACtE,MAAI,OAAO,OAAO,IAAI,SAAS,KAAK,OAAO,QAAQ,oBAAoB,kBAAkB;AAEvF,UAAM,KAAK,MAAM,IAAI,EAAE,WAAW,IAAI,CAAC;AACvC,UAAM,QAAQ,cAAc,YAAY,UAAU,GAAG,KAAK,GAAG;AAC7D,UAAM,MAAM,QAAQ,MAAM,cAAc,MAAM,IAAI,MAAM,MAAM,IAAI,YAAY,GAAG,IAAI,CAAC,GAAG,KAAK,IAAI;AAClG,QAAI,QAAQ,QAAW;AACrB,cAAQ,KAAK,MAAM,GAAG,GAAG,KAAK;AAC9B,YAAM,KAAK,MAAM,IAAI,EAAE,cAAc,MAAM,CAAC;AAAA,IAC9C;AAAA,EACF;AACA,SAAO,QAAQ,EAAE,OAAO,MAAM,MAAM,GAAG,GAAG,GAAG,cAAc,UAAU,IAAI,CAAC;AAC5E;AAKA,SAAS,iBAAiB,KAAa,KAAkC;AACvE,QAAM,QAAQ,CAAC,MACb,GAAG,QAAQ,IAAIC,YAAW,QAAQ,EAAE,OAAO,EAAE,UAAU,KAAK,CAAC,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1F,QAAM,MAAM,oBAAI,IAAY;AAC5B,MAAI,IAAI,qBAAqBF,MAAK,QAAQ,IAAI,iBAAiB,MAAM,IAAK,KAAI,IAAI,MAAM,IAAI,iBAAiB,CAAC;AAC9G,MAAI,QAAQA,MAAK,KAAK,IAAI,QAAQ,QAAQ,GAAG,SAAS,EAAG,KAAI,IAAI,QAAQ;AACzE,MAAI,IAAI,MAAM,GAAG,CAAC;AAClB,SAAO,CAAC,GAAG,GAAG;AAChB;AAKA,IAAM,aAAa,oBAAI,IAAgD;AAKhE,SAAS,cAAc,KAAa,KAAwB,KAA4B;AAC7F,QAAM,MAAM,WAAW,IAAI,GAAG;AAC9B,MAAI,OAAO,IAAI,UAAU,MAAM,IAAQ,QAAO,IAAI;AAClD,QAAM,MAAgB,CAAC;AACvB,MAAI,QAAQ,aAAa;AACvB,eAAW,WAAW,iBAAiB,KAAK,GAAG,GAAG;AAChD,UAAI;AACF,cAAM,OAAO,IAAI,QAAQ,SAAS,EAAE;AACpC,cAAM,UAAU,YAAY,KAAK,IAAI,IAAI,OAAO;AAChD,YAAI;AAAA,UACFG,cAAa,YAAY,CAAC,yBAAyB,MAAM,SAAS,MAAM,MAAM,OAAO,GAAG;AAAA,YACtF,UAAU;AAAA,YACV,SAAS;AAAA,YACT,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,UACpC,CAAC;AAAA,QACH;AACA;AAAA,MACF,QAAQ;AAAA,MAAC;AAAA,IACX;AACF,MAAI;AACF,QAAI,KAAKC,cAAaJ,MAAK,KAAK,KAAK,mBAAmB,GAAG,MAAM,CAAC;AAAA,EACpE,QAAQ;AAAA,EAAC;AACT,aAAW,KAAK,KAAK;AACnB,QAAI;AAGF,YAAM,IAAI,EAAE,KAAK;AACjB,YAAM,IACJ,KAAK,MAAM,sBAAsB,KAAK,CAAC,IAAI,OAAO,KAAK,GAAG,KAAK,EAAE,SAAS,MAAM,IAAI,CAAC,EAGrF;AACF,UAAI,OAAO,GAAG,gBAAgB,YAAY,CAAC,EAAE,YAAa;AAC1D,YAAM,UAAU,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY,OAAO;AACvE,UAAI,WAAW,MAAM,IAAQ;AAC7B,iBAAW,IAAI,KAAK,EAAE,OAAO,EAAE,aAAa,QAAQ,CAAC;AACrD,aAAO,EAAE;AAAA,IACX,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,SAAO;AACT;AAWA,eAAsB,cAAc,QAAgB,OAAmD;AACrG,MAAI;AACF,UAAM,IAAI,MAAM,MAAM,GAAG,UAAU,qBAAqB,MAAM,IAAI;AAAA,MAChE,SAAS,EAAE,eAAe,UAAU,KAAK,IAAI,qBAAqB,cAAc,cAAc,cAAc;AAAA,MAC5G,UAAU;AAAA,MACV,QAAQ,YAAY,QAAQ,iBAAiB;AAAA,IAC/C,CAAC;AACD,QAAI,EAAE,WAAW,OAAO,OAAO,EAAE,QAAQ,IAAI,gBAAgB,CAAC,IAAI,iBAAkB,QAAO;AAC3F,UAAM,OAAO,MAAM,EAAE,KAAK;AAC1B,QAAI,KAAK,SAAS,iBAAkB,QAAO;AAI3C,UAAM,IAAI,KAAK,MAAM,IAAI;AACzB,UAAM,IAAI,GAAG,kBAAkB,GAAG,WAAW;AAC7C,UAAM,OAAO,CAAC,MAAc,EAAE,QAAQ,qBAAqB,EAAE;AAC7D,QAAI,OAAO,GAAG,OAAO,YAAY,KAAK,EAAE,EAAE,MAAM,KAAK,MAAM,EAAG,QAAO;AACrE,WAAQ,OAAO,EAAE,UAAU,YAAY,UAAU,EAAE,KAAK,KAAM;AAAA,EAChE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AMl9BA,SAAS,aAAa;AACtB,SAAS,YAAY,WAAW,gBAAAK,eAAc,YAAAC,iBAAgB;AAC9D,OAAOC,WAAU;;;ACEV,IAAM,kBAAkB;AAIxB,IAAM,mBAAmB;AAEzB,IAAM,gBAAgB;AAKtB,IAAM,cAAc,CAAC,iBAAiB,kBAAkB,aAAa;AAC5E,IAAM,cAAc;AAGb,SAAS,eAAe,KAA2C;AACxE,QAAM,IAAI,EAAE,GAAG,IAAI;AACnB,aAAW,KAAK,YAAa,QAAO,EAAE,CAAC;AACvC,SAAO;AACT;AAGA,SAAS,MAAM,KAAyC,MAAkC;AACxF,QAAM,IAAI,IAAI,IAAI,GAAG,KAAK;AAC1B,SAAO,KAAK,YAAY,KAAK,CAAC,IAAI,IAAI;AACxC;AAGO,SAAS,WAAW,KAA6D;AACtF,SAAO,MAAM,KAAK,eAAe;AACnC;AAIO,SAAS,YAAY,KAA6D;AACvF,SAAO,MAAM,KAAK,gBAAgB;AACpC;AAMO,SAAS,SAAS,SAAkB,OAAqC;AAC9E,QAAM,IAAI,QAAQ,SAAS,KAAK;AAChC,MAAI,KAAK,CAAC,EAAE,QAAQ,WAAW;AAC7B,UAAM,OAAO,GAAG,QAAQ,IAAI,IAAI,EAAE,QAAQ,EAAE;AAC5C,UAAM,WAAW,WAAW,MAAM,GAAG;AACrC,QAAI,YAAY,aAAa,KAAM,GAAE,QAAQ,cAAc;AAC3D,UAAM,QAAQ,YAAY,MAAM,GAAG;AACnC,QAAI,SAAS,UAAU,KAAM,GAAE,QAAQ,eAAe;AAAA,EACxD;AACA,SAAO;AACT;;;ADlDO,IAAM,iBAAiB,KAAK;AACnC,IAAM,UAAU,OAAO;AAOhB,SAAS,MAAM,GAAoB;AACxC,MAAI;AACF,QAAI,CAACC,UAAS,CAAC,EAAE,OAAO,EAAG,QAAO;AAClC,eAAW,GAAG,UAAU,IAAI;AAC5B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,WAAW,GAKT;AAChB,MAAI,EAAE,YAAY,MAAM,EAAE,QAAQ,EAAG,QAAO,EAAE;AAC9C,aAAW,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,EAAE,MAAMC,MAAK,SAAS,GAAG;AAC/D,QAAI,CAAC,KAAK,CAACA,MAAK,WAAW,CAAC,EAAG;AAC/B,eAAW,KAAK,EAAE,MAAO,KAAI,MAAMA,MAAK,KAAK,GAAG,CAAC,CAAC,EAAG,QAAOA,MAAK,KAAK,GAAG,CAAC;AAAA,EAC5E;AACA,aAAW,KAAK,EAAE,KAAM,YAAW,KAAK,EAAE,MAAO,KAAI,MAAMA,MAAK,KAAK,GAAG,CAAC,CAAC,EAAG,QAAOA,MAAK,KAAK,GAAG,CAAC;AAClG,SAAO;AACT;AAKO,SAAS,WAAW,MAA6B;AACtD,MAAI,OAAO;AACX,MAAI;AACF,WAAOC,cAAa,MAAM,MAAM;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,IAAI,kCAAkC,KAAK,IAAI;AACrD,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,SAASD,MAAK,QAAQA,MAAK,QAAQ,IAAI,GAAI,EAAE,CAAC,EAAa,MAAM,IAAI,EAAE,KAAKA,MAAK,GAAG,CAAC;AAC3F,MAAI;AACF,WAAOD,UAAS,MAAM,EAAE,OAAO,IAAI,SAAS;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOO,SAAS,UAAU,KAAa,MAAyD;AAC9F,MAAI,CAAC,gBAAgB,KAAK,GAAG,EAAG,QAAO,EAAE,MAAM,KAAK,KAAK;AACzD,QAAM,SAAS,WAAW,GAAG;AAC7B,SAAO,SAAS,EAAE,MAAM,QAAQ,UAAU,MAAM,CAAC,QAAQ,GAAG,IAAI,EAAE,IAAI;AACxE;AAGO,IAAM,WAAW,CAAC,QACvB,GAAGC,MAAK,SAAS,GAAG,CAAC;AAKhB,SAAS,MAAM,KAAa,MAAkE;AACnG,QAAM,IAAI,UAAU,KAAK,IAAI;AAC7B,SAAO,IAAI,EAAE,GAAG,GAAG,OAAO,MAAM,IAAI,EAAE,MAAM,CAAC,IAAI,GAAG,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG,GAAG,MAAM,CAAC,GAAG,OAAO,KAAK;AACrG;AA0CO,SAAS,YAAY,KAAa,MAAgB,GAAmC;AAC1F,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,MAAgB,CAAC;AACrB,QAAI,SAAS;AACb,QAAI,SAAS;AACb,QAAI,UAAU;AACd,QAAI,WAAW;AACf,QAAI,UAAU;AACd,QAAI;AACJ,UAAM,IAAI,UAAU,KAAK,IAAI;AAC7B,QAAI,CAAC,GAAG;AACN,cAAQ,EAAE,OAAO,SAAS,GAAG,EAAE,CAAC;AAChC;AAAA,IACF;AACA,QAAI;AACF,cAAQ,MAAM,EAAE,MAAM,EAAE,MAAM;AAAA,QAC5B,KAAK,EAAE;AAAA,QACP,KAAK,EAAE;AAAA,QACP,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,QAChC,UAAU,QAAQ,aAAa;AAAA,QAC/B,aAAa;AAAA,MACf,CAAC;AAAA,IACH,SAAS,GAAG;AACV,cAAQ,EAAE,OAAQ,EAAY,QAAQ,CAAC;AACvC;AAAA,IACF;AACA,UAAM,OAAO,CAAC,QAAwB;AACpC,UAAI;AACF,YAAI,QAAQ,aAAa,WAAW,MAAM,IAAK,SAAQ,KAAK,CAAC,MAAM,KAAK,GAAG;AAAA,YACtE,OAAM,KAAK,GAAG;AAAA,MACrB,QAAQ;AACN,YAAI;AACF,gBAAM,KAAK,GAAG;AAAA,QAChB,QAAQ;AAAA,QAAC;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO,CAAC,SAAiB;AAC7B,UAAI,CAAC,EAAE,OAAO,IAAI,EAAG;AACrB,YAAM,CAAC,OAAO,KAAK,IAAI,CAAC;AACxB,eAAS,KAAK;AAAA,IAChB;AACA,QAAI,UAAU;AACd,UAAM,QAAQ,GAAG,QAAQ,CAAC,MAAc;AACtC,UAAI,EAAE,QAAQ;AACZ,cAAM,QAAQ,UAAU,EAAE,SAAS,MAAM;AACzC,cAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,kBAAU,MAAM,IAAI;AACpB,mBAAW,QAAQ,OAAO;AACxB,cAAI,CAAC,KAAK,KAAK,EAAG;AAClB,cAAI;AACF,cAAE,OAAO,IAAI;AAAA,UACf,QAAQ;AAAA,UAAC;AACT,eAAK,IAAI;AAAA,QACX;AACA,YAAI,QAAQ,SAAS,QAAS,WAAU;AACxC;AAAA,MACF;AACA,UAAI,OAAQ;AACZ,UAAI,SAAS,EAAE,SAAS,SAAS;AAC/B,iBAAS;AACT,cAAM,CAAC;AACP;AAAA,MACF;AACA,UAAI,KAAK,CAAC;AACV,gBAAU,EAAE;AAAA,IACd,CAAC;AACD,UAAM,QAAQ,GAAG,QAAQ,CAAC,MAAc;AACtC,iBAAW,UAAU,EAAE,SAAS,MAAM,GAAG,MAAM,IAAK;AAAA,IACtD,CAAC;AACD,UAAM,QAAQ,WAAW,MAAM;AAC7B,iBAAW;AACX,WAAK,SAAS;AACd,iBAAW,MAAM,KAAK,SAAS,GAAG,GAAM,EAAE,MAAM;AAAA,IAClD,GAAG,EAAE,aAAa,cAAc;AAChC,UAAM,SAAS,CAAC,MAAqB,YAA8C;AAAA,MACjF;AAAA,MACA;AAAA,MACA,QAAQ,SAAS,OAAO,OAAO,OAAO,GAAG,EAAE,SAAS,MAAM;AAAA,MAC1D;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,IACF;AACA,UAAM,SAAS,CAAC,MAAiB;AAC/B,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,KAAK;AAClB,cAAQ,CAAC;AAAA,IACX;AACA,UAAM,GAAG,SAAS,CAAC,MAAM,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;AAErD,UAAM,GAAG,QAAQ,CAAC,MAAM,WAAW,WAAW,MAAM,OAAO,OAAO,MAAM,MAAM,CAAC,GAAG,GAAI,EAAE,MAAM,CAAC;AAC/F,UAAM,GAAG,SAAS,CAAC,MAAM,WAAW;AAElC,UAAI,EAAE,UAAU,QAAQ,KAAK,GAAG;AAC9B,YAAI;AACF,YAAE,OAAO,OAAO;AAAA,QAClB,QAAQ;AAAA,QAAC;AACT,aAAK,OAAO;AACZ,kBAAU;AAAA,MACZ;AACA,aAAO,OAAO,MAAM,MAAM,CAAC;AAAA,IAC7B,CAAC;AAAA,EACH,CAAC;AACH;;;AE5NA,SAAS,cAAAE,aAAY,eAAAC,cAAa,gBAAAC,eAAc,cAAc,YAAAC,iBAAgB;AAC9E,OAAOC,WAAU;AAgBjB,IAAMC,QAAO;AACb,IAAM,YAAY;AACX,IAAM,YAAY,CAAC,iBAAiB,kBAAkB,eAAe,MAAM;AAI3E,IAAM,mBACX;AAEK,IAAM,oBAAoB,CAAC,UAA2B,UAAU,eAAe,mBAAmB;AACzG,IAAM,SAAS,CAAC,QAA2B,IAAI,mBAAmBC,MAAK,KAAK,QAAQ,GAAG,SAAS;AAChG,IAAM,YAAY,CAAC,QAA2BA,MAAK,KAAK,OAAO,GAAG,GAAG,UAAU,YAAY;AAC3F,IAAM,cAAc,CAAC,QAA2BA,MAAK,KAAK,OAAO,GAAG,GAAG,UAAU,UAAU;AAC3F,IAAM,WAAW,CAAC,QAChB,CAAC,eAAe,mBAAmB,iBAAiB,EAAE,IAAI,CAAC,MAAMA,MAAK,KAAK,OAAO,GAAG,GAAG,CAAC,CAAC;AAC5F,IAAM,QAAQ;AACd,IAAMC,aAAY,CAAC,MAA4B,OAAO,MAAM,YAAY,2BAA2B,KAAK,CAAC;AAGzG,SAAS,MAAM,KAAkB;AAC/B,QAAM,UAAU,CAAC,OAAe,EAAE,MAAM,WAAW,SAAS,IAAI,CAACF,OAAM,CAAC,CAAC,EAAE,SAAS,SAAS,EAAE;AAC/F,QAAM,MAA+B,EAAE,SAAS,KAAK;AACrD,aAAW,KAAK,UAAW,KAAI,CAAC,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC1G,SAAO;AACT;AAEO,IAAM,cAAuB;AAAA,EAClC,MAAMA;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ,CAAC,MAAM,QAAQ,QACrBG,YAAWF,MAAK,KAAK,OAAO,GAAG,GAAG,QAAQ,CAAC,KAAK,SAAS,GAAG,EAAE,KAAK,CAAC,MAAME,YAAW,CAAC,CAAC;AAAA,EACzF,aAAa,CAAC,MAAM,QAAQ,QAAQ,CAAC,UAAU,GAAG,CAAC;AAAA,EACnD,QAAQ,KAAK,MAAM,QAAQ,KAAsB;AAC/C,UAAM,OAAO,UAAU,GAAG;AAC1B,UAAM,IAAI,aAAa,IAAI;AAC3B,QAAI,CAAC,EAAE,GAAI,QAAO,CAAC,EAAE,MAAM,SAAS,OAAO,SAAS,MAAM,MAAM,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC5F,UAAM,QAAQ,EAAE;AAChB,UAAM,IAAI,MAAM,GAAG;AACnB,QAAI,KAAK,UAAU,MAAM,SAAS,CAAC,MAAM,KAAK,UAAU,CAAC,EAAG,QAAO,CAAC,EAAE,MAAM,SAAS,MAAM,CAAC;AAC5F,UAAM,SAAS,IAAI;AACnB,WAAO;AAAA,MACL,UAAU,MAAM,WAAW,OAAO,CAAC,GAAG,EAAE,OAAO,KAAK;AAAA,QAClD;AAAA,QACA,SAAS;AAAA,QACT,SAAS,CAAC,EAAE;AAAA,QACZ,MAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA,EACA,UAAU,MAAM,QAAQ,KAAK,OAAuB,CAAC,GAAoB;AACvE,UAAM,OAAO,UAAU,GAAG;AAC1B,QAAI,CAACA,YAAW,IAAI,EAAG,QAAO,CAAC,EAAE,MAAM,SAAS,MAAM,CAAC;AACvD,UAAM,IAAI,aAAa,IAAI;AAC3B,QAAI,CAAC,EAAE,GAAI,QAAO,CAAC,EAAE,MAAM,SAAS,OAAO,SAAS,MAAM,MAAM,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC5F,UAAM,QAAQ,EAAE;AAChB,QAAI,EAAE,aAAa,OAAQ,QAAO,CAAC,EAAE,MAAM,SAAS,MAAM,CAAC;AAC3D,WAAO,MAAM,SAAS;AACtB,QAAI,KAAK,SAAS,SAAS,IAAI,KAAK,CAAC,OAAO,KAAK,KAAK,EAAE,OAAQ,QAAO,CAAC,cAAc,MAAM,WAAW,OAAO,CAAC,CAAC,CAAC;AACjH,WAAO,CAAC,UAAU,MAAM,WAAW,OAAO,CAAC,CAAC,KAAK,EAAE,MAAM,SAAS,KAAK,CAAC;AAAA,EAC1E;AAAA,EACA,UAAU,KAAK,MAAM,QAAQ,KAAK;AAChC,UAAM,OAAO,UAAU,GAAG;AAC1B,UAAM,IAAI,aAAa,IAAI;AAC3B,UAAM,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,IAAI;AACzC,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,OAAO,CAAC,OAAO,YAAY,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,MAAM,GAAG,CAAC,IAAI,YAAY;AAAA,MAC/F;AAAA,IACF;AAAA,EACF;AAAA,EAEA,SAAS,OAAqC;AAC5C,UAAM,QAAQ,MAAM,KAAK,CAAC,KAAK;AAC/B,QAAI,IAA6B,CAAC;AAClC,QAAI;AAEF,YAAM,IAAa,MAAM,QAAQ,KAAK,MAAM,MAAM,KAAK,IAAI,CAAC;AAC5D,UAAI,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,EAAG,KAAI;AAAA,IAC3D,QAAQ;AAAA,IAAC;AACT,UAAM,KAAM,EAAE,kBAAkB,EAAE,cAAc,EAAE;AAClD,UAAM,SAAS,kBAAkB,KAAK;AACtC,QAAI,CAAC,GAAI,QAAO,EAAE,SAAS,EAAE,IAAI,GAAG,GAAG,QAAQ,CAAC,GAAG,OAAO;AAC1D,UAAM,aAAa,aAAa,EAAE,cAAc;AAChD,UAAM,UAAiC,EAAE,IAAI,OAAO,EAAE,GAAG,KAAK,WAAW,CAAC,KAAK,MAAM,IAAI;AACzF,QAAI,OAAO,EAAE,cAAc,YAAY,EAAE,cAAc,OAAQ,SAAQ,QAAQ,EAAE;AACjF,UAAM,WAAW,EAAE;AACnB,UAAM,OAAOD,WAAU,UAAU,IAAI,IAAI,UAAU,OAAOA,WAAU,EAAE,QAAQ,IAAI,EAAE,WAAW;AAC/F,UAAM,KAAK,CAAC,MAAc,UAAmC,EAAE,MAAM,MAAME,OAAM,IAAI,GAAG,cAAc,MAAM;AAC5G,UAAM,SAA+B,CAAC;AACtC,YAAQ,OAAO;AAAA,MACb,KAAK;AAEH,YAAI,OAAO,EAAE,aAAa,MAAM,KAAK,EAAE,kBAAkB,QAAW;AAClE,iBAAO,KAAK,GAAG,mBAAmB,EAAE,QAAQ,UAAU,CAAC,CAAC;AACxD,iBAAO,KAAK,GAAG,gBAAgB,CAAC,CAAC,CAAC;AAAA,QACpC,MAAO,QAAO,KAAK,GAAG,qBAAqB,CAAC,CAAC,CAAC;AAC9C;AAAA,MACF,KAAK;AACH;AAAA,MACF,KAAK;AACH,YAAI,KAAM,QAAO,KAAK,GAAG,gBAAgB,EAAE,MAAM,OAAO,UAAU,KAAK,CAAC,CAAC;AACzE;AAAA,MACF,KAAK;AAEH,YAAI,CAAC,KAAM,QAAO,KAAK,GAAG,qBAAqB,CAAC,CAAC,CAAC;AAAA;AAEhD,iBAAO;AAAA,YACL,GAAG,cAAc;AAAA,cACf;AAAA,cACA,IAAI,CAAC,EAAE;AAAA,cACP,OAAO,OAAO,EAAE,UAAU,WAAW,EAAE,MAAM,MAAM,GAAG,GAAI,IAAI;AAAA,cAC9D,OAAO,UAAU;AAAA,YACnB,CAAC;AAAA,UACH;AACF;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,cAAc,EAAE,QAAQ,EAAE,QAAQ,UAAU,OAAO,CAAC,CAAC;AACpE;AAAA,MACF;AACE,eAAO,EAAE,SAAS,QAAQ,CAAC,GAAG,OAAO;AAAA,IACzC;AACA,UAAM,IAAgB,EAAE,SAAS,QAAQ,QAAQ,OAAO,EAAE,WAAW,EAAE;AACvE,QAAI,OAAO,EAAE,mBAAmB,SAAU,GAAE,aAAa,EAAE;AAC3D,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,MAAM,UAAU;AAC7B,QAAI,OAAO;AACX,QAAI;AACF,aAAOC,cAAa,MAAM,MAAM;AAAA,IAClC,QAAQ;AACN,aAAO,EAAE,OAAO,CAAC,GAAG,MAAM,SAAS;AAAA,IACrC;AACA,WAAO,gBAAgB,KAAK,MAAM,IAAI,GAAG,QAAQ;AAAA,EACnD;AAAA,EAEA,MAAM,SAAS,YAAY,OAAqB;AAC9C,QAAI,CAAC,WAAY,QAAO,CAAC;AACzB,WAAO,iBAAiB,YAAY;AAAA,MAClC,cAAc,QAAQ;AAAA,MACtB,YAAa,MAAM,eAAwC,QAAQ,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC;AAAA,MAC1F,KAAK,QAAQ;AAAA,IACf,CAAC;AAAA,EACH;AAAA,EAEA,SAAS,SAAS,MAAM,QAAQ,KAAoB;AAClD,UAAMC,QAAsB,CAAC;AAC7B,eAAW,QAAQ,SAAS,GAAG,GAAG;AAChC,UAAI,MAAgB,CAAC;AACrB,UAAI;AACF,cAAMC,aAAYN,MAAK,KAAK,MAAM,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,mBAAmB,KAAK,CAAC,CAAC;AAAA,MACtF,QAAQ;AAAA,MAAC;AACT,iBAAW,MAAM,KAAK;AACpB,cAAM,IAAIA,MAAK,KAAK,MAAM,SAAS,IAAI,qBAAqB,QAAQ,kBAAkB;AACtF,YAAI;AACF,cAAIO,UAAS,CAAC,EAAE,UAAU,QAAS;AAAA,QACrC,QAAQ;AACN;AAAA,QACF;AACA,cAAM,QAAQ,iBAAiB,GAAG,EAAE,cAAc,IAAI,YAAY,CAAC,GAAG,IAAI,CAAC;AAC3E,YAAI,CAAC,MAAM,cAAc,CAAC,MAAM,QAAS;AACzC,cAAM,EAAE,YAAY,SAAS,WAAW,KAAK,iBAAiB,KAAK,GAAG,KAAK,IAAI;AAC/E,QAAAF,MAAK,KAAK,EAAE,SAAS,EAAE,IAAI,GAAG,KAAK,GAAG,YAAY,QAAQ,CAAC;AAAA,MAC7D;AAAA,IACF;AACA,WAAOA;AAAA,EACT;AACF;AAEA,SAASF,OAAyC,GAAS;AACzD,aAAW,KAAK,OAAO,KAAK,CAAC,EAAG,KAAI,EAAE,CAAC,MAAM,OAAW,QAAO,EAAE,CAAC;AAClE,SAAO;AACT;AAGO,SAAS,OAAO,GAA2B;AAChD,MAAI,OAAO,MAAM,YAAY,CAAC,EAAG,QAAO;AACxC,MAAI,IAAI;AACR,MAAI,UAAU,KAAK,CAAC,GAAG;AACrB,QAAI;AACF,UAAI,mBAAmB,IAAI,IAAI,CAAC,EAAE,QAAQ;AAAA,IAC5C,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,QAAQ,aAAa,WAAW,eAAe,KAAK,CAAC,EAAG,KAAI,EAAE,MAAM,CAAC;AAAA,EAC3E,WAAW,2BAA2B,KAAK,CAAC,EAAG,QAAO;AACtD,MAAI,CAACH,MAAK,WAAW,CAAC,EAAG,QAAO;AAChC,SAAOA,MAAK,QAAQ,CAAC;AACvB;AACO,IAAM,eAAe,CAAC,WAC1B,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,GAC9B,IAAI,MAAM,EACV,OAAO,CAAC,MAAmB,CAAC,CAAC,CAAC,EAC9B,OAAO,CAAC,GAAG,GAAG,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC;AAE3C,SAAS,WAAW,GAAqB;AACvC,QAAM,MAAM,CAAC,CAAC;AACd,MAAI;AACF,UAAM,IAAI,aAAa,OAAO,CAAC;AAC/B,QAAI,MAAM,EAAG,KAAI,KAAK,CAAC;AAAA,EACzB,QAAQ;AAAA,EAAC;AACT,SAAO;AACT;AACA,SAAS,WAAW,KAAkD;AACpE,QAAM,MAA2C,CAAC;AAClD,MAAI,QAAkB,CAAC;AACvB,MAAI;AACF,YAAQM,aAAY,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AAAA,EAC5D,QAAQ;AACN,WAAO;AAAA,EACT;AACA,aAAW,KAAK,OAAO;AACrB,UAAM,KAAK,EAAE,MAAM,GAAG,EAAE;AACxB,QAAI,CAAC,MAAM,KAAK,EAAE,EAAG;AACrB,QAAI;AACJ,QAAI;AACF,UAAI,KAAK,MAAMF,cAAaJ,MAAK,KAAK,KAAK,CAAC,GAAG,MAAM,CAAC;AAAA,IACxD,QAAQ;AACN;AAAA,IACF;AACA,UAAM,UAAoB,CAAC;AAI3B,eAAW,KAAK,GAAG,kBAAkB,aAAa,CAAC,GAAG;AACpD,YAAM,IAAI,OAAO,GAAG,aAAa,GAAG,WAAW,SAAS;AACxD,UAAI,EAAG,SAAQ,KAAK,GAAG,WAAW,CAAC,CAAC;AAAA,IACtC;AACA,QAAI,QAAQ,OAAQ,KAAI,KAAK,EAAE,IAAI,QAAQ,CAAC;AAAA,EAC9C;AACA,SAAO;AACT;AAEO,SAAS,eAAe,UAA+C,YAAqC;AACjH,QAAM,KAAe,CAAC;AACtB,aAAW,KAAK,YAAY;AAC1B,UAAM,IAAI,OAAO,CAAC;AAClB,QAAI,EAAG,IAAG,KAAK,GAAG,WAAW,CAAC,CAAC;AAAA,EACjC;AACA,MAAI,OAAsB;AAC1B,MAAI,UAAU;AACd,aAAW,MAAM;AACf,eAAW,KAAK,GAAG,SAAS;AAC1B,YAAM,SAAS,GAAG,KAAK,CAAC,MAAM,MAAM,KAAK,EAAE,WAAW,EAAE,SAASA,MAAK,GAAG,IAAI,IAAI,IAAIA,MAAK,GAAG,CAAC;AAC9F,UAAI,UAAU,EAAE,SAAS,SAAS;AAChC,eAAO,GAAG;AACV,kBAAU,EAAE;AAAA,MACd;AAAA,IACF;AACF,SAAO;AACT;AAIO,SAAS,qBACd,YACA,MACe;AACf,QAAM,IAAI,WAAW,QAAQ,GAAGA,MAAK,GAAG,QAAQA,MAAK,GAAG,EAAE;AAC1D,QAAM,QAAQ,IAAI,IAAI,CAAC,WAAW,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,SAAS,KAAK,GAAG,CAAC;AAC9E,aAAW,KAAK,MAAM;AACpB,QAAI;AACF,YAAM,IAAI,0CAA0C;AAAA,QAClDI,cAAaJ,MAAK,KAAK,GAAG,yBAAyB,GAAG,MAAM;AAAA,MAC9D;AACA,UAAI,CAAC,EAAG;AACR,YAAM,OAAO,oCAAoC,EAAE,CAAC,CAAC;AACrD,YAAM,OAAO,KAAK,gBAAgB;AAClC,UAAI,CAAC,MAAM,KAAK,IAAI,KAAK,CAAC,KAAK,YAAY,OAAQ,QAAO;AAC1D,YAAM,UAAU,eAAe,WAAW,KAAK,eAAe,YAAY,KAAK,GAAG,CAAC,GAAG,KAAK,UAAU;AACrG,aAAO,UAAU,GAAG,IAAI,MAAM,mBAAmB,KAAK,IAAI,YAAY,OAAO,EAAE,CAAC,KAAK;AAAA,IACvF,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,SAAO;AACT;AAEO,SAAS,iBACd,MACA,MACc;AACd,QAAM,EAAE,MAAM,KAAK,IAAI,KAAK,MAAM,MAAM,MAAM,KAAK,IAAI;AACvD,QAAM,IAAI,WAAW,IAAI;AACzB,QAAM,IAAI,WAAW,IAAI;AACzB,QAAM,MAAoB,CAAC;AAC3B,QAAM,MAAM,qBAAqB,MAAM,IAAI;AAC3C,MAAI,IAAK,KAAI,MAAM;AACnB,aAAW,KAAK,GAAG;AACjB,QAAI,EAAE,SAAS,gBAAgB,OAAO,EAAE,YAAY,SAAU;AAC9D,UAAM,IAAI,iDAAiD,KAAK,EAAE,OAAO;AACzE,UAAM,QAAQ,IAAK,EAAE,CAAC,IAAe,EAAE,SAAS,KAAK;AACrD,UAAM,QAAQ,KAAK,MAAM,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,IAAI,KAAK;AACjE,QAAI,CAAC,QAAQ,eAAe,KAAK,IAAI,EAAG;AACxC,QAAI,QAAQ,KAAK,SAAS,KAAK,GAAG,KAAK,MAAM,GAAG,EAAE,EAAE,QAAQ,WAAW,EAAE,CAAC,WAAM;AAChF,QAAI,eAAe;AACnB;AAAA,EACF;AACA,QAAM,QAAQ,EAAE,OAAO,CAAC,EACrB,IAAI,CAAC,MAAM,KAAK,MAAM,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC,CAAC,EAChE,OAAO,CAAC,MAAM,IAAI,CAAC;AACtB,MAAI,MAAM,QAAQ;AAChB,QAAI,aAAa,KAAK,IAAI,GAAG,KAAK;AAClC,QAAI,UAAU,KAAK,IAAI,GAAG,KAAK;AAAA,EACjC;AACA,SAAO;AACT;;;AC/UA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAAQ;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,SAAS,UAAU,gBAAAC,qBAAoB;AACvC,SAAS,aAAAC,YAAW,YAAAC,WAAU,eAAAC,cAAa,gBAAAC,eAAc,YAAAC,WAAU,YAAAC,iBAAgB;AACnF,OAAOC,SAAQ;AACf,OAAOC,WAAU;;;ACEV,IAAM,eAAe,KAAK;AAE1B,IAAM,UAAU,IAAI;AAGpB,IAAM,qBAAqB,KAAK;AAGhC,IAAM,sBAAsB,KAAK;AAEjC,IAAM,eAAe,KAAK;AAsB1B,IAAM,WAAW;AAGjB,IAAM,oBAAoB,CAAC,QAAQ,QAAQ,SAAS,MAAM;AAQ1D,IAAM,YAAY,CAAC,MAAuB,kBAAkB,QAAQ,CAAoB;AAwM/F,IAAM,SAAiD;AAAA,EACrD,QAAQ,CAAC,QAAQ,SAAS,UAAU;AAAA,EACpC,qBAAqB,CAAC,OAAO,UAAU;AAAA,EACvC,QAAQ,CAAC;AAAA,EACT,OAAO,CAAC,QAAQ,OAAO,SAAS,cAAc,SAAS,UAAU;AAAA,EACjE,WAAW,CAAC,OAAO,MAAM;AAAA,EACzB,MAAM,CAAC,eAAe,gBAAgB,cAAc,WAAW;AACjE;AAEA,IAAM,cAAc,OAAO;AAI3B,IAAM,MAAM,IAAI,YAAY;AAC5B,IAAM,MAAM,IAAI,YAAY;;;ADnP5B,IAAM,UAAU;AAEhB,IAAM,gBAAgB;AAGf,IAAM,aAAa,CAAC,GAAW,MAAc;AAClD,QAAM,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM;AAClC,QAAM,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM;AAClC,WAAS,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,QAAQ,GAAG,MAAM,GAAG;AAClD,SAAK,GAAG,CAAC,KAAK,QAAQ,GAAG,CAAC,KAAK,GAAI,SAAQ,GAAG,CAAC,KAAK,MAAM,GAAG,CAAC,KAAK;AACrE,SAAO;AACT;AAIO,SAAS,WAAW,MAAyB,QAAQ,KAAK,OAAOC,IAAG,QAAQ,GAAkB;AACnG,MAAI,IAAI,sBAAsB,MAAM,IAAI,kBAAkB,EAAG,QAAO,IAAI;AACxE,QAAM,QAAQ,QAAQ,aAAa,UAAU,CAAC,cAAc,YAAY,IAAI,CAAC,QAAQ;AACrF,aAAW,KAAK,OAAO,IAAI,QAAQ,EAAE,EAAE,MAAMC,MAAK,SAAS,GAAG;AAC5D,QAAI,CAAC,KAAK,CAACA,MAAK,WAAW,CAAC,EAAG;AAC/B,eAAW,KAAK,MAAO,KAAI,MAAMA,MAAK,KAAK,GAAG,CAAC,CAAC,EAAG,QAAOA,MAAK,KAAK,GAAG,CAAC;AAAA,EAC1E;AACA,aAAW,KAAK;AAAA,IACdA,MAAK,KAAK,MAAM,WAAW,SAAS,QAAQ;AAAA,IAC5CA,MAAK,KAAK,MAAM,UAAU,OAAO,QAAQ;AAAA,IACzC;AAAA,IACA;AAAA,EACF;AACE,QAAI,MAAM,CAAC,EAAG,QAAO;AACvB,QAAM,MAAMA,MAAK,KAAK,MAAM,WAAW,uBAAuB,UAAU,aAAa;AACrF,MAAI,WAAqB,CAAC;AAC1B,MAAI;AACF,eAAWC,aAAY,GAAG,EACvB,OAAO,CAAC,MAAM,gBAAgB,KAAK,CAAC,CAAC,EACrC,KAAK,UAAU,EACf,QAAQ;AAAA,EACb,QAAQ;AAAA,EAAC;AACT,aAAW,KAAK,UAAU;AACxB,UAAM,IAAID,MAAK,KAAK,KAAK,GAAG,cAAc,YAAY,SAAS,QAAQ;AACvE,QAAI,MAAM,CAAC,EAAG,QAAO;AAAA,EACvB;AACA,SAAO;AACT;AAGA,SAAS,YAAY,MAAc,MAA6B;AAC9D,QAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,QAAM,KAAK,IAAI,OAAO,uBAAuB,KAAK,QAAQ,MAAM,KAAK,CAAC,YAAY;AAClF,QAAM,QAAQ,MAAM,UAAU,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;AAC/C,MAAI,QAAQ,EAAG,QAAO;AACtB,QAAM,MAAM,CAAC,MAAM,KAAK,CAAW;AACnC,WAAS,IAAI,QAAQ,GAAG,IAAI,MAAM,UAAU,MAAM,CAAC,GAAG,KAAK,KAAK,CAAC,YAAY,KAAK,MAAM,CAAC,CAAW,GAAG;AACrG,QAAI,KAAK,MAAM,CAAC,CAAW;AAC7B,SAAO,IAAI,KAAK,GAAG;AACrB;AAgBO,SAAS,UAAU,MAAoB;AAC5C,QAAM,KAAK,YAAY,MAAM,mBAAmB;AAChD,QAAM,KAAK,YAAY,MAAM,sBAAsB;AACnD,SAAO;AAAA,IACL,SAAS,CAAC,CAAC;AAAA,IACX,OAAO,KAAK,CAAC,GAAG,GAAG,SAAS,gBAAgB,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAW,IAAI,CAAC;AAAA,IAC7E,aAAa,CAAC,CAAC,MAAM,SAAS,KAAK,EAAE;AAAA,IACrC,MAAM,CAAC,CAAC,YAAY,MAAM,gBAAgB;AAAA,IAC1C,QAAQ,CAAC,CAAC,YAAY,MAAM,4BAA4B;AAAA,EAC1D;AACF;AAGA,IAAM,aAA+D;AAAA,EACnE,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AACR;AAUO,SAAS,UAAU,OAAiB,MAAoD;AAC7F,MAAI,UAAU,UAAW,QAAO,EAAE,MAAM,CAAC,EAAE;AAC3C,MAAI,KAAK;AACP,WAAO,EAAE,OAAO,yDAAyD,KAAK,MAAM,2BAA2B;AACjH,MAAI,UAAU,UAAU,CAAC,KAAK,MAAM,SAAS,MAAM;AACjD,WAAO,EAAE,OAAO,gFAAgF;AAClG,MAAI,UAAU,QAAQ;AACpB,UAAM,IAAI,WAAW,KAAK;AAC1B,QAAI,CAAC,KAAK,MAAM,SAAS,CAAC,EAAG,QAAO,EAAE,OAAO,qCAAqC,CAAC,mBAAmB;AACtG,WAAO,EAAE,MAAM,CAAC,qBAAqB,CAAC,EAAE;AAAA,EAC1C;AACA,MAAI,KAAK,MAAM,SAAS,SAAS,EAAG,QAAO,EAAE,MAAM,CAAC,qBAAqB,SAAS,EAAE;AACpF,QAAM,OAAiB,CAAC;AACxB,MAAI,KAAK;AACP,SAAK;AAAA,MACH;AAAA,MACA,KAAK,MAAM,SAAS,QAAQ,KAAK,CAAC,KAAK,MAAM,SAAS,SAAS,IAAI,WAAW;AAAA,IAChF;AACF,MAAI,KAAK,YAAa,MAAK,KAAK,wBAAwB,MAAM;AAC9D,SAAO,EAAE,KAAK;AAChB;AAKO,IAAM,eAAe,CAAC,SAAiC,SAAS,YAAY,SAAS;AAMrF,SAAS,cAAc,MAAY,MAA+B;AACvE,MAAI,KAAK,OAAQ,QAAO,CAAC;AACzB,SAAO,kBAAkB;AAAA,IACvB,CAAC,MAAM,UAAU,CAAC,KAAK,UAAU,aAAa,IAAI,CAAC,MAAM,MAAM,UAAU,KAAK,MAAM,SAAS,WAAW,CAAC,CAAC;AAAA,EAC5G;AACF;AAGO,IAAM,eAAe,CAAC,SAAyB;AAG/C,SAAS,WAAW,MAAsC;AAC/D,SAAO,KAAK,SACR,CAAC,mBAAmB,eAAe,aAAa,4BAA4B,IAC5E,CAAC,mBAAmB,MAAM;AAChC;AAKO,SAAS,WAAW,GASgB;AACzC,QAAM,KAAK,UAAU,EAAE,OAAO,EAAE,IAAI;AACpC,MAAI,WAAW,GAAI,QAAO;AAC1B,QAAM,QAAQ,EAAE,SACZ,CAAC,YAAY,EAAE,QAAQ,GAAI,EAAE,OAAO,CAAC,gBAAgB,IAAI,CAAC,CAAE,IAC5D,CAAC,gBAAgB,EAAE,OAAiB;AACxC,SAAO;AAAA,IACL,MAAM;AAAA,MACJ;AAAA,MACA,GAAG,WAAW,EAAE,IAAI;AAAA,MACpB,GAAG,GAAG;AAAA,MACN,GAAI,EAAE,QAAQ,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC;AAAA,MACtC,GAAI,EAAE,cAAc,SAAS,CAAC,kBAAkB,EAAE,aAAa,KAAK,GAAG,CAAC,EAAE,IAAI,CAAC;AAAA,MAC/E,GAAG;AAAA,MACH,EAAE,OAAO,WAAW,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,EAAE;AAAA,IAChD;AAAA,EACF;AACF;AAKO,SAAS,UAAU,WAAoB,MAAyB,QAAQ,KAAwB;AACrG,QAAM,IAAI,eAAe,GAAG;AAC5B,aAAW,KAAK;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACE,WAAO,EAAE,CAAC;AACZ,IAAE,0BAA0B;AAC5B,MAAI,cAAc,QAAW;AAC3B,QAAIA,MAAK,QAAQ,SAAS,MAAMA,MAAK,QAAQD,IAAG,QAAQ,GAAG,SAAS,EAAG,QAAO,EAAE;AAAA,QAC3E,GAAE,oBAAoB;AAAA,EAC7B;AACA,SAAO;AACT;AAKO,SAAS,gBAAgB,QAA0C;AACxE,QAAM,OAAO,OAAO,UAAU,EAAE,EAAE,KAAK;AACvC,QAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,MAAI,QAAQ,EAAG,QAAO,EAAE,UAAU,KAAK;AACvC,MAAI;AACF,UAAM,IAAI,KAAK,MAAM,KAAK,MAAM,KAAK,CAAC;AACtC,QAAI,OAAO,EAAE,aAAa,UAAW,QAAO,EAAE,UAAU,KAAK;AAC7D,WAAO;AAAA,MACL,UAAU,EAAE;AAAA,MACZ,GAAI,OAAO,EAAE,eAAe,WAAW,EAAE,QAAQ,EAAE,WAAW,IAAI,CAAC;AAAA,MACnE,GAAI,OAAO,EAAE,qBAAqB,WAAW,EAAE,cAAc,EAAE,iBAAiB,IAAI,CAAC;AAAA,IACvF;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,UAAU,KAAK;AAAA,EAC1B;AACF;AAOO,SAASG,aAAY,KAAa,WAAoB,MAAyB,QAAQ,KAAqB;AAGjH,QAAM,IAAI,MAAM,KAAK,CAAC,QAAQ,UAAU,QAAQ,CAAC;AACjD,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B;AAAA,MACE,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE,UAAU,QAAQ,SAAS,KAAQ,KAAK,UAAU,WAAW,GAAG,GAAG,WAAW,MAAM,MAAM,OAAO,EAAE,MAAM;AAAA;AAAA,MAE3G,CAAC,MAAM,WAAW,QAAQ,gBAAgB,MAAM,CAAC;AAAA,IACnD;AAAA,EACF,CAAC;AACH;AAGO,SAAS,aAAa,WAAuC;AAClE,QAAM,OAAOH,IAAG,QAAQ;AACxB,QAAM,YAAY,CAAC,aAAaC,MAAK,QAAQ,SAAS,MAAMA,MAAK,QAAQ,MAAM,SAAS;AACxF,SAAO,YAAY,wBAAwB,uBAAuB,UAAU,QAAQ,MAAM,GAAG,CAAC;AAChG;AAGO,SAAS,UAAU,WAA+B,WAA4B,QAAQ,UAAkB;AAC7G,QAAM,QAAQ,YAAY,UAAU,QAAQD,IAAG,QAAQ,GAAG,GAAG,IAAI;AACjE,QAAM,QAAQ,aAAa,SAAS;AACpC,MAAI,aAAa;AACf,WAAO,gBAAgB,KAAK,gOAAgO,KAAK;AACnQ,SAAO,gBAAgB,KAAK,0CAA0C,KAAK;AAC7E;AAKO,SAAS,WAAW,KAAa,YAAY,KAAc;AAChE,QAAM,IAAI,MAAM,KAAK,CAAC,QAAQ,CAAC;AAC/B,MAAI;AACF,WAAO;AAAA,MACLI,cAAa,EAAE,MAAM,EAAE,MAAM;AAAA,QAC3B,UAAU;AAAA,QACV,SAAS;AAAA,QACT,KAAK,UAAU;AAAA,QACf,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,QAChC,WAAW,IAAI,OAAO;AAAA,QACtB,OAAO,EAAE;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF,SAAS,GAAG;AACV,UAAM,MAAM;AACZ,UAAM,OAAO,UAAU,OAAO,KAAK,UAAU,EAAE,CAAC;AAChD,QAAI,KAAK,QAAS,QAAO;AACzB,UAAM,MAAM,KAAK,SAAS,cAAc,mBAAmB,YAAY,GAAI,OAAO;AAClF,WAAO,EAAE,GAAG,MAAM,QAAQ,KAAKH,MAAK,SAAS,GAAG,CAAC,aAAa,GAAG,GAAG;AAAA,EACtE;AACF;AASA,SAAS,aAAa,SAAiB,KAAgC;AACrE,MAAI,WAAuC,CAAC;AAC5C,MAAI;AACF,eAAWC,aAAYD,MAAK,KAAK,KAAK,UAAU,GAAG,EAAE,eAAe,KAAK,CAAC;AAAA,EAC5E,QAAQ;AACN,WAAO;AAAA,EACT;AACA,aAAW,KAAK,UAAU;AACxB,QAAI,CAAC,EAAE,YAAY,EAAG;AACtB,UAAM,OAAOA,MAAK,KAAK,KAAK,YAAY,EAAE,MAAM,GAAG,OAAO,QAAQ;AAClE,QAAI;AACF,UAAII,UAAS,IAAI,EAAE,OAAO,EAAG,QAAO,EAAE,MAAM,WAAW,KAAK,SAAS,EAAE,KAAK;AAAA,IAC9E,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,SAAO;AACT;AAGO,SAAS,eAAe,SAAiB,MAA4C;AAC1F,MAAI,CAAC,QAAQ,KAAK,OAAO,EAAG,QAAO;AACnC,aAAW,OAAO,MAAM;AACtB,UAAM,IAAI,aAAa,SAAS,GAAG;AACnC,QAAI,EAAG,QAAO;AAAA,EAChB;AACA,SAAO;AACT;AAOO,SAAS,gBAAgB,SAAiB,MAAuC;AACtF,MAAI,CAAC,QAAQ,KAAK,OAAO,EAAG,QAAO,CAAC;AACpC,QAAM,MAAoB,CAAC;AAC3B,aAAW,OAAO,MAAM;AACtB,UAAM,IAAI,aAAa,SAAS,GAAG;AACnC,QAAI,EAAG,KAAI,KAAK,CAAC;AAAA,EACnB;AACA,SAAO;AACT;AAUA,IAAM,WAAW,CAAC,MAAkBJ,MAAK,SAAS,EAAE,SAAS;AAEtD,SAAS,YAAY,SAAiB,MAA4C;AACvF,QAAM,MAAM,gBAAgB,SAAS,IAAI;AACzC,MAAI,CAAC,IAAI,OAAQ,QAAO;AACxB,MAAI,IAAI,WAAW,EAAG,QAAO,EAAE,YAAY,IAAI,CAAC,EAAgB;AAChE,QAAM,OAAO,IAAI,OAAO,CAAC,MAAM,YAAY,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;AAC3E,MAAI,KAAK,WAAW,EAAG,QAAO,EAAE,YAAY,KAAK,CAAC,EAAgB;AAClE,SAAO;AAAA,IACL,WAAW,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,QAAQ,CAAC,CAAC,EAAE,KAAK;AAAA,IAChD,MAAM,CAAC,GAAG,IAAI,IAAI,KAAK,IAAI,QAAQ,CAAC,CAAC,EAAE,KAAK;AAAA,EAC9C;AACF;AAEA,IAAM,cAAc,CAAC,QAAgB,IAAI,QAAQ,iBAAiB,GAAG;AAK9D,SAAS,cAAc,GAAeK,OAAM,IAAI,OAAO,MAAqB;AACjF,MAAI,QAAuB;AAC3B,MAAI,QAAQ;AACZ,MAAI;AACJ,QAAM,OAAO,CAAC,SAAgC;AAC5C,QAAI,KAAK,QAAQ,OAAO,IAAI,EAAG,QAAO;AACtC,QAAI;AACJ,QAAI;AACF,UAAI,KAAK,MAAM,KAAK,SAAS,MAAM,CAAC;AAAA,IACtC,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,CAAC,KAAK,OAAO,EAAE,QAAQ,YAAY,CAACL,MAAK,WAAW,EAAE,GAAG,EAAG,QAAO;AACvE,QAAI,YAAY,EAAE,GAAG,MAAM,EAAE,QAAS,QAAO,EAAE;AAC/C,cAAU,EAAE;AACZ;AACA,WAAO;AAAA,EACT;AACA,MAAI;AACF,SAAKM,UAAS,EAAE,MAAM,GAAG;AACzB,UAAM,MAAM,OAAO,MAAM,MAAM,IAAI;AACnC,QAAI,MAAM;AACV,QAAI,OAAO,OAAO,MAAM,CAAC;AACzB,QAAI,WAAW;AACf,WAAO,MAAMD,QAAO,QAAQ,IAAI;AAC9B,YAAM,IAAIE,UAAS,IAAI,KAAK,GAAG,IAAI,QAAQ,GAAG;AAC9C,UAAI,CAAC,GAAG;AACN,YAAI,CAAC,YAAY,KAAK,QAAQ;AAC5B,gBAAM,MAAM,KAAK,IAAI;AACrB,cAAI,IAAK,QAAO;AAAA,QAClB;AACA;AAAA,MACF;AACA,aAAO;AACP,UAAI,QAAQ,KAAK,SAAS,OAAO,OAAO,CAAC,MAAM,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,SAAS,GAAG,CAAC;AACvF,UAAI,KAAK,MAAM,QAAQ,EAAE;AACzB,aAAO,MAAM,GAAG;AACd,cAAM,OAAO,MAAM,SAAS,GAAG,EAAE;AACjC,gBAAQ,MAAM,SAAS,KAAK,CAAC;AAC7B,YAAI,SAAU,YAAW;AAAA,aACpB;AACH,gBAAM,MAAM,KAAK,IAAI;AACrB,cAAI,IAAK,QAAO;AAAA,QAClB;AACA,aAAK,MAAM,QAAQ,EAAE;AAAA,MACvB;AACA,UAAI,MAAM,SAAS,IAAI,OAAO,MAAM;AAClC,mBAAW;AACX,eAAO,OAAO,MAAM,CAAC;AAAA,MACvB,MAAO,QAAO,WAAW,OAAO,MAAM,CAAC,IAAI,OAAO,KAAK,KAAK;AAAA,IAC9D;AAAA,EACF,QAAQ;AAAA,EACR,UAAE;AACA,QAAI,OAAO;AACT,UAAI;AACF,QAAAC,WAAU,EAAE;AAAA,MACd,QAAQ;AAAA,MAAC;AAAA,EACb;AACA,SAAO;AACT;AAEA,SAAS,SAAS,KAAsB;AACtC,MAAI,CAAC,OAAO,cAAc,GAAG,KAAK,OAAO,EAAG,QAAO;AACnD,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,SAAS,GAAG;AACV,WAAQ,EAAwB,SAAS;AAAA,EAC3C;AACF;AA0BA,IAAM,cAAc,CAAC,MAAc,WAAmB,MAA2C;AAC/F,QAAM,MAAM,OAAO,EAAE,OAAOR,MAAK,SAAS,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC;AAC5D,QAAM,MAAM,CAAC,MAAgB,OAAO,MAAM,YAAY,IAAI,IAAI;AAC9D,MAAI,UAAyB;AAC7B,MAAI;AAEF,UAAM,OAAO,IAAI,OAAO,IAAI,GAAG,wBAAwB;AACvD,UAAM,MAAMC,aAAYD,MAAK,QAAQ,IAAI,CAAC,EAAE,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC;AACpE,QAAI,IAAK,WAAUA,MAAK,KAAKA,MAAK,QAAQ,IAAI,GAAG,GAAG;AAAA,EACtD,QAAQ;AAAA,EAAC;AACT,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,IAAI,EAAE,mBAAmB;AAAA,IACjC,cAAc,OAAO,EAAE,iBAAiB,WAAW,EAAE,eAAe;AAAA,IACpE,WAAW,IAAI,EAAE,SAAS;AAAA,IAC1B,QAAQ,IAAI,EAAE,MAAM;AAAA,IACpB,MAAM,IAAI,EAAE,IAAI;AAAA,IAChB,YAAY,IAAI,EAAE,UAAU;AAAA,IAC5B;AAAA,EACF;AACF;AAIO,SAAS,YAAY,SAAiB,MAAgD;AAC3F,QAAM,MAAoB,CAAC;AAC3B,aAAW,OAAO,OAAO,SAAS,WAAW,CAAC,IAAI,IAAI,MAAM;AAC1D,QAAI,QAAkB,CAAC;AACvB,QAAI;AACF,cAAQC,aAAYD,MAAK,KAAK,KAAK,UAAU,CAAC,EAAE,OAAO,CAAC,MAAM,cAAc,KAAK,CAAC,CAAC;AAAA,IACrF,QAAQ;AACN;AAAA,IACF;AACA,eAAW,KAAK,OAAO;AACrB,YAAM,OAAOA,MAAK,KAAK,KAAK,YAAY,CAAC;AACzC,UAAI;AACJ,UAAI;AACF,YAAI,KAAK,MAAMS,cAAa,MAAM,MAAM,CAAC;AAAA,MAC3C,QAAQ;AACN;AAAA,MACF;AACA,UAAI,OAAO,EAAE,SAAS,MAAM,QAAS;AACrC,YAAM,MAAM,YAAY,MAAM,KAAK,CAAC;AACpC,UAAI,SAAS,IAAI,GAAG,EAAG,KAAI,KAAK,GAAG;AAAA,IACrC;AAAA,EACF;AACA,SAAO;AACT;AAKO,SAAS,WAAW,SAAiB,MAAqD;AAC/F,QAAM,MAAM,YAAY,SAAS,IAAI;AACrC,SAAO,IAAI,WAAW,IAAK,IAAI,CAAC,IAAmB;AACrD;AAKO,SAAS,YAAY,SAAiB,WAA4B;AACvE,SAAO,YAAY,SAAS,SAAS,EAAE,SAAS;AAClD;AAUO,IAAM,YAAoC;AAAA,EAC/C,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM;AAAA,EACN,mBAAmB;AACrB;AACO,IAAM,eAAe,OAAO,KAAK,SAAS;AAC1C,IAAM,WAAW,CAAC,MAA0C,KAAK,KAAK,YAAa,UAAU,CAAC,IAAe;AAU7G,SAAS,SAAS,SAAiC;AACxD,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAM,IAAI;AACV,QAAM,IAAI,OAAO,EAAE,oBAAoB,WAAW,EAAE,kBAAkB,EAAE;AACxE,SAAO,OAAO,MAAM,YAAY,aAAa,SAAS,CAAC,IAAI,IAAI;AACjE;AAOO,SAAS,UAAU,MAAgE;AACxF,MAAI,SAAS,oBAAqB,QAAO;AACzC,MAAI,SAAS,aAAa,SAAS,YAAY,SAAS,aAAa,SAAS,cAAe,QAAO;AACpG,SAAO;AACT;AAqBO,IAAM,mBAAiC,EAAE,aAAa,MAAM,iBAAiB,OAAO,eAAe,MAAM;AAEzG,SAAS,aAAa,WAA+B,OAAOV,IAAG,QAAQ,GAAiB;AAC7F,QAAM,OAAOC,MAAK,KAAK,aAAaA,MAAK,KAAK,MAAM,SAAS,GAAG,eAAe;AAC/E,MAAI;AACJ,MAAI;AACF,QAAI,KAAK,MAAMS,cAAa,MAAM,MAAM,CAAC;AAAA,EAC3C,QAAQ;AACN,WAAO,EAAE,GAAG,iBAAiB;AAAA,EAC/B;AACA,MAAI,CAAC,KAAK,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,EAAG,QAAO,EAAE,GAAG,iBAAiB;AAClF,QAAM,IAAK,EAAE,eAAe,OAAO,EAAE,gBAAgB,WAAW,EAAE,cAAc,CAAC;AAGjF,QAAM,MAAM,CAAC,MAAc,EAAE,CAAC,MAAM,aAAa,EAAE,CAAC,MAAM;AAC1D,SAAO;AAAA,IACL,aAAa,OAAO,EAAE,gBAAgB,YAAY,aAAa,SAAS,EAAE,WAAW,IAAI,EAAE,cAAc;AAAA,IACzG,iBAAiB,IAAI,iBAAiB;AAAA,IACtC,eAAe,IAAI,8BAA8B;AAAA,EACnD;AACF;AAyBO,SAAS,aAAa,UAAqC,MAAmC;AACnG,MAAI,aAAa,OAAQ,QAAO;AAChC,OAAK,aAAa,aAAa,aAAa,kBAAkB,SAAS,KAAK,WAAW,KAAK,SAAS,QAAQ;AAC3G,WAAO;AACT,SAAO;AACT;AAGO,SAAS,gBAAgB,MAAc,YAAqB,MAAM,KAAK,IAAI,GAAY;AAC5F,MAAI;AACJ,MAAI;AACF,QAAIL,UAAS,IAAI,EAAE;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,MAAM,IAAI,cAAe,QAAO;AACpC,SAAO,EAAE,cAAc,KAAK,aAAa;AAC3C;AAIO,SAAS,UAAU,KAAa,MAAgB,GAAmC;AACxF,SAAO,YAAY,KAAK,MAAM,EAAE,GAAG,GAAG,MAAM,EAAE,SAAS,CAAC,SAAS,KAAK,SAAS,iBAAiB,GAAG,CAAC;AACtG;AAYO,SAAS,YAAY,QAAwD;AAClF,QAAM,IAAI,OAAO,UAAU,EAAE,EAAE,KAAK;AACpC,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,OAAO,CAAC,MAAoC;AAChD,QAAI,MAAM,QAAQ,CAAC,GAAG;AACpB,eAAS,IAAI,EAAE,SAAS,GAAG,KAAK,GAAG,IAAK,KAAI,EAAE,CAAC,GAAG,SAAS,SAAU,QAAO,EAAE,CAAC;AAC/E,aAAO;AAAA,IACT;AACA,WAAO,KAAK,OAAO,MAAM,WAAY,IAAqB;AAAA,EAC5D;AACA,MAAI;AACF,WAAO,KAAK,KAAK,MAAM,CAAC,CAAC;AAAA,EAC3B,QAAQ;AAAA,EAAC;AACT,QAAM,QAAQ,EAAE,MAAM,IAAI;AAC1B,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,QAAI;AACF,YAAM,IAAI,KAAK,KAAK,MAAM,MAAM,CAAC,CAAW,CAAC;AAC7C,UAAI,MAAM,EAAE,SAAS,YAAY,YAAY,GAAI,QAAO;AAAA,IAC1D,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,SAAO;AACT;AAEO,IAAM,SAAS,CAAC,MAA4B,OAAO,MAAM,YAAY,QAAQ,KAAK,CAAC;;;AEjsB1F,SAAS,cAAAM,aAAY,eAAAC,cAAa,gBAAAC,qBAAoB;AACtD,OAAOC,WAAU;;;ACAjB,IAAM,cAAc;AACpB,IAAM,aAAa;AAEZ,IAAMC,QAAO;AAEpB,SAAS,KAAK,MAAc,IAAiD;AAC3E,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,QAAQ;AACtB,QAAI,GAAG,KAAK;AACV,YAAM,IAAI,KAAK,QAAQ,GAAG,KAAK,CAAC;AAChC,UAAI,IAAI,EAAG;AACX,UAAI,IAAI;AACR,SAAG,MAAM;AACT;AAAA,IACF;AACA,UAAM,KAAK,KAAK,CAAC;AACjB,QAAI,OAAO,IAAK;AAChB,QAAI,KAAK,WAAW,OAAO,CAAC,KAAK,KAAK,WAAW,OAAO,CAAC,GAAG;AAC1D,SAAG,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAC5B,WAAK;AACL;AAAA,IACF;AACA,QAAI,OAAO,KAAK;AACd;AACA,aAAO,IAAI,KAAK,UAAU,KAAK,CAAC,MAAM,IAAK,MAAK,KAAK,CAAC,MAAM,OAAO,IAAI;AACvE;AACA;AAAA,IACF;AACA,QAAI,OAAO,KAAK;AACd,YAAM,IAAI,KAAK,QAAQ,KAAK,IAAI,CAAC;AACjC,UAAI,IAAI,IAAI,KAAK,SAAS,IAAI;AAC9B;AAAA,IACF;AACA,QAAI,OAAO,IAAK,IAAG;AAAA,aACV,OAAO,IAAK,IAAG,QAAQ,KAAK,IAAI,GAAG,GAAG,QAAQ,CAAC;AACxD;AAAA,EACF;AACF;AAGO,SAAS,aAAa,MAAsB;AACjD,QAAM,KAAK,EAAE,OAAO,GAAG,KAAK,KAAsB;AAClD,MAAI,MAAM;AACV,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,QAAI,CAAC,GAAG,OAAO,GAAG,UAAU,KAAK,YAAY,KAAK,IAAI,EAAG,QAAO;AAChE,SAAK,MAAM,EAAE;AACb,WAAO,KAAK,SAAS;AAAA,EACvB;AACA,SAAO;AACT;AAEA,SAAS,MAAM,MAAc;AAC3B,QAAM,KAAK,aAAa,IAAI;AAC5B,SAAO,KAAK,IAAI,EAAE,MAAM,MAAM,MAAM,IAAI,IAAI,KAAK,OAAO,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,EAAE,GAAG,MAAM,KAAK,MAAM,EAAE,GAAG,GAAG;AAClH;AAEO,SAAS,UAAU,KAA8B;AACtD,MAAI;AACF,UAAM,IAAI,KAAK,MAAM,GAAG;AACxB,WAAO,MAAM,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,OAAO,MAAM,QAAQ,IAAK,IAAiB;AAAA,EACvF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYO,SAAS,WAAW,MAA6B;AACtD,QAAM,EAAE,MAAM,MAAM,GAAG,IAAI,MAAM,IAAI;AACrC,QAAM,IAAI,YAAY,KAAK,IAAI;AAC/B,MAAI,GAAG;AACL,UAAM,MAAM,EAAE,CAAC;AACf,WAAO,EAAE,KAAK,MAAM,UAAU,GAAG,GAAG,OAAO,EAAE,OAAO,QAAQ,EAAE,CAAC,EAAE,QAAQ,WAAW,OAAO,OAAO,CAACA,MAAK,KAAK,GAAG,EAAE;AAAA,EACpH;AACA,QAAM,IAAI,WAAW,KAAK,IAAI;AAC9B,MAAI,GAAG;AACL,UAAM,OAAO,KAAK,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC;AAC9C,WAAO;AAAA,MACL,KAAK,KAAK,QAAQ,YAAY,EAAE,EAAE,KAAK;AAAA,MACvC,MAAM;AAAA,MACN,OAAO,EAAE;AAAA,MACT,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,OAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,IAAI,YAAY,KAAK,IAAI;AAC/B,MAAI,GAAG;AACL,UAAM,MAAM,EAAE,CAAC;AACf,QAAIA,MAAK,KAAK,GAAG;AACf,aAAO,EAAE,KAAK,MAAM,UAAU,GAAG,GAAG,OAAO,KAAK,EAAE,OAAO,QAAQ,EAAE,CAAC,EAAE,QAAQ,WAAW,MAAM,OAAO,MAAM;AAAA,EAChH;AACA,SAAO;AACT;AAEA,SAAS,SAAS,MAAc,OAAe,QAAwB;AACrE,SAAO,KAAK,MAAM,GAAG,KAAK,IAAI,KAAK,MAAM,QAAQ,MAAM,EAAE,QAAQ,UAAU,EAAE;AAC/E;AAIO,SAAS,eAAe,MAAc,MAAsB;AACjE,QAAM,EAAE,MAAM,KAAK,IAAI,MAAM,IAAI;AAGjC,QAAM,OAAO,KAAK,QAAQ,QAAQ,EAAE;AACpC,QAAM,OAAO,KAAK,SAAS,IAAI,IAAI,OAAO,MAAM,SAAS,MAAM,IAAI,SAAS;AAC5E,QAAM,MAAM,KAAK,KAAK,IAAI,KAAK,QAAQ,QAAQ,GAAG,IAAI;AACtD,MAAI,CAAC,KAAM,QAAO,MAAM,OAAO;AAC/B,SAAO,GAAG,MAAM,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI;AACzC;AAGO,SAAS,WAAW,MAAc,MAAsB;AAC7D,QAAM,IAAI,WAAW,IAAI;AACzB,MAAI,KAAK,CAAC,EAAE,aAAa,CAAC,EAAE,MAAO,QAAO,KAAK,MAAM,GAAG,EAAE,KAAK,IAAI,OAAO,KAAK,MAAM,EAAE,QAAQ,EAAE,MAAM;AACvG,MAAI,GAAG,UAAW,QAAO,eAAe,SAAS,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;AAC/E,SAAO,eAAe,MAAM,IAAI;AAClC;AAGO,SAAS,cAAc,MAAc,GAAW,MAAsB;AAC3E,SAAO,KAAK,MAAM,GAAG,EAAE,KAAK,IAAI,OAAO,KAAK,MAAM,EAAE,QAAQ,EAAE,MAAM;AACtE;AAGO,SAAS,cAAc,MAAsB;AAClD,QAAM,IAAI,WAAW,IAAI;AACzB,MAAI,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,OAAQ,QAAO;AACvC,SAAO,SAAS,MAAM,EAAE,OAAO,EAAE,MAAM;AACzC;AAIA,IAAM,WAAW;AACV,SAAS,eAAe,MAAwC;AACrE,MAAI,CAAC,MAAM,QAAQ,IAAI,EAAG,QAAO;AACjC,QAAM,IAAI,KAAK,QAAQ,QAAQ;AAC/B,SAAO,KAAK,KAAK,OAAO,KAAK,IAAI,CAAC,MAAM,WAAW,UAAU,KAAK,IAAI,CAAC,CAAW,IAAI;AACxF;AASO,SAAS,iBAAiB,KAAqB;AACpD,SAAO,IAAI,QAAQ,yDAAyD,EAAE;AAChF;AAEO,SAAS,gBAAgB,KAAa,MAAwB;AACnE,QAAM,OAAO,iBAAiB,GAAG,EAAE,QAAQ,aAAa,EAAE;AAC1D,SAAO,GAAG,IAAI,0BAA0B,KAAK,UAAU,KAAK,UAAU,IAAI,CAAC,CAAC;AAC9E;AACO,IAAM,gBAAgB,CAAC,SAC5B,MAAM,QAAQ,IAAI,KAAK,OAAO,KAAK,CAAC,MAAM,YAAY,8BAA8B,KAAK,KAAK,CAAC,CAAC;;;ADjJlG,IAAMC,QAAO;AACN,IAAM,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAMC,QAAO,EAAE,YAAY,CAAC,MAAe,kBAAkB,KAAK,CAAC,IAAI,KAAK,QAAY,SAAS,EAAE;AACnG,IAAMC,aAAY,CAAC,SAAoB,OAAO,aAAa,OAAO,CAAC,MAAM,MAAM,YAAY,IAAI,CAAC,GAAG,YAAY;AAE/G,IAAM,YAAY,CAAC,QAA2B,IAAI,cAAcC,MAAK,KAAK,QAAQ,GAAG,QAAQ;AAC7F,IAAMC,aAAY,CAAC,QAA2BD,MAAK,KAAK,UAAU,GAAG,GAAG,YAAY;AACpF,IAAME,cAAa,CAAC,QAA2BF,MAAK,KAAK,UAAU,GAAG,GAAG,aAAa;AACtF,IAAM,cAAc,CAAC,QAA2BA,MAAK,KAAK,UAAU,GAAG,GAAG,UAAU;AAEpF,IAAMG,aAAY,CAAC,MAA4B,OAAO,MAAM,YAAY,2BAA2B,KAAK,CAAC;AACzG,IAAMC,aAAY,CAAC,MACjB,MAAM,WAAW,MAAM,YAAY,MAAM,SAAS,IAAI;AAGxD,IAAM,iBACJ;AAEF,SAAS,cAAc,KAAkB,KAAwB;AAC/D,QAAM,OAAOF,YAAW,GAAG;AAC3B,MAAI,OAAO;AACX,MAAI,UAAU;AACd,MAAI;AACF,WAAOG,cAAa,MAAM,MAAM;AAAA,EAClC,SAAS,GAAG;AAGV,cAAW,EAA4B,SAAS;AAAA,EAClD;AACA,QAAM,OAAO,IAAI,CAACR,OAAM,QAAQ,CAAC,EAAE;AACnC,QAAM,OAAO,YAAY,KAAK,UAAU,IAAI,CAAC;AAC7C,QAAM,IAAS,WAAW,IAAI;AAE9B,QAAM,QAAQ,CAAC,MAAc,SAAgC;AAC3D,UAAM,MAAM,UAAU,MAAM,MAAM,OAAO;AACzC,WAAO,MAAM,EAAE,GAAG,KAAK,MAAM,GAAG,IAAI,IAAI,KAAK,cAAc,GAAG,IAAI,EAAE,MAAM,SAAS,MAAM,KAAK;AAAA,EAChG;AACA,MAAI,KAAK,CAAC,EAAE,OAAO;AACjB,QAAI,CAAC,EAAE,aAAa,KAAK,UAAU,EAAE,IAAI,MAAM,KAAK,UAAU,IAAI;AAChE,aAAO,EAAE,MAAM,SAAS,OAAO,MAAM,iBAAiB;AACxD,WAAO;AAAA,MACA,WAAW,MAAM,IAAI;AAAA,MAC1B,EAAE,YACE,wFACA;AAAA,IACN;AAAA,EACF;AACA,MAAI,GAAG,OAAO;AACZ,UAAM,OAAY,eAAe,EAAE,IAAI;AACvC,QAAS,cAAc,EAAE,IAAI,GAAG;AAC9B,UAAI,QAAaS,MAAK,KAAK,KAAK,UAAU,IAAI,CAAC,GAAG;AAChD,YAAI,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,IAAI;AAC9C,iBAAO,EAAE,MAAM,SAAS,OAAO,MAAM,4CAA4C;AAAA,MACrF;AACA,UAAI,CAAC,QAAaA,MAAK,KAAK,KAAK,UAAU,IAAI,CAAC;AAC9C,eAAO;AAAA,UACA,cAAc,MAAM,GAAG,YAAiB,gBAAgB,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,UAC3E;AAAA,QACF;AAAA,IACJ;AACA,WAAO;AAAA,MACL;AAAA,MACA,SAAS;AAAA,MACT,SAAS;AAAA,MACT,MAAM,sBAAsB,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK,QAAQ,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC,iBAAiB,cAAc;AAAA,IACpH;AAAA,EACF;AACA,SAAO,MAAW,WAAW,MAAM,IAAI,GAAG,6BAA6B;AACzE;AAEA,SAAS,gBAAgB,KAAuC;AAC9D,QAAM,OAAOJ,YAAW,GAAG;AAC3B,MAAI;AACJ,MAAI;AACF,WAAOG,cAAa,MAAM,MAAM;AAAA,EAClC,QAAQ;AACN,WAAO,EAAE,MAAM,SAAS,MAAM;AAAA,EAChC;AACA,QAAM,IAAS,WAAW,IAAI;AAC9B,MAAI,CAAC,EAAG,QAAO,EAAE,MAAM,SAAS,MAAM;AACtC,MAAI,CAAC,EAAE,MAAO,QAAO,UAAU,MAAW,cAAc,IAAI,CAAC,KAAK,EAAE,MAAM,SAAS,KAAK;AACxF,QAAM,OAAY,eAAe,EAAE,IAAI;AACvC,MAAS,cAAc,EAAE,IAAI,KAAK,QAAaC,MAAK,KAAK,KAAK,UAAU,IAAI,CAAC;AAC3E,WACE,UAAU,MAAW,cAAc,MAAM,GAAG,YAAiB,iBAAiB,EAAE,GAAG,CAAC,EAAE,CAAC,KAAK;AAAA,MAC1F;AAAA,MACA,SAAS;AAAA,IACX;AAEJ,SAAO,EAAE,MAAM,SAAS,MAAM;AAChC;AAKO,SAAS,YAAY,UAAkB,MAAyB,QAAQ,KAAoB;AACjG,MAAI,CAAC,wBAAwB,KAAK,QAAQ,EAAG,QAAO;AACpD,MAAI,OAAsB;AAC1B,QAAM,OAAO,CAAC,KAAa,UAAkB;AAC3C,QAAI,QAAkB,CAAC;AACvB,QAAI;AACF,cAAQC,aAAY,GAAG;AAAA,IACzB,QAAQ;AACN;AAAA,IACF;AACA,eAAW,KAAK,MAAM,KAAK,EAAE,QAAQ,GAAG;AACtC,YAAM,IAAIP,MAAK,KAAK,KAAK,CAAC;AAC1B,UAAI,EAAE,SAAS,IAAI,QAAQ,QAAQ,GAAG;AACpC,eAAO;AACP;AAAA,MACF;AACA,UAAI,QAAQ,GAAG;AACb,aAAK,GAAG,QAAQ,CAAC;AACjB,YAAI,KAAM;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACA,OAAK,YAAY,GAAG,GAAG,CAAC;AACxB,SAAO;AACT;AAIA,SAAS,YAAY,MAA8C;AACjE,SAAO,UAAU,MAAM,CAAC,SAAS,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;AAC9D;AAIA,SAAS,gBAAgB,MAAc,IAAqB;AAC1D,QAAM,QAAQ,SAAS,IAAI;AAC3B,SAAO,CAAC,CAAC,SAAS,UAAU,GAAG,YAAY;AAC7C;AAEA,SAAS,SAAS,MAA6B;AAC7C,QAAM,IAAI,qFAAqF,KAAK,IAAI;AACxG,SAAO,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,YAAY,IAAI;AACvC;AAEO,IAAM,QAAiB;AAAA,EAC5B,MAAMH;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ,CAAC,MAAM,QAAQ,QAAQW,YAAW,UAAU,GAAG,CAAC;AAAA,EACxD,aAAa,CAAC,MAAM,QAAQ,QAAQ,CAACP,WAAU,GAAG,GAAGC,YAAW,GAAG,CAAC;AAAA,EACpE,SAAS,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAAA,IAC9D,oBAAoBD,WAAU,GAAG,GAAGJ,OAAME,WAAU,KAAK,IAAI,GAAG,KAAKD,KAAI;AAAA,IACzE,cAAc,KAAK,GAAG;AAAA,EACxB;AAAA,EACA,WAAW,CAAC,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAAA,IAC3D,sBAAsBG,WAAU,GAAG,GAAG,cAAc,EAAE,SAAS,KAAK,SAAS,SAASA,WAAU,GAAG,CAAC,EAAE,CAAC;AAAA,IACvG,gBAAgB,GAAG;AAAA,EACrB;AAAA,EACA,WAAW,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAChE,UAAM,MAAyB;AAAA,MAC7B;AAAA,QACE,MAAMA,WAAU,GAAG;AAAA,QACnB,OAAO,sBAAsBA,WAAU,GAAG,GAAGJ,OAAME,WAAU,KAAK,IAAI,GAAG,KAAKD,KAAI;AAAA,QAClF,MAAM;AAAA,MACR;AAAA,IACF;AACA,QAAI,OAAO;AACX,QAAI;AACF,aAAOO,cAAaH,YAAW,GAAG,GAAG,MAAM;AAAA,IAC7C,QAAQ;AAAA,IAAC;AACT,UAAM,IAAS,WAAW,IAAI;AAC9B,UAAM,OAAO,IAAI,CAACL,OAAM,QAAQ,CAAC,EAAE;AACnC,UAAM,OAAO,IAAS,eAAe,EAAE,IAAI,IAAI;AAC/C,UAAM,OAAO,MAAM,CAAC,EAAE,SAAU,QAAQ,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,IAAI;AACpF,QAAI,KAAK;AAAA,MACP,MAAMK,YAAW,GAAG;AAAA,MACpB,OACE,CAAC,KAAM,EAAE,SAAS,CAAC,OACf,YACA,EAAE,YACA,cACA,KAAK,UAAU,EAAE,IAAI,MAAM,KAAK,UAAU,IAAI,KAAM,QAAQ,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,IAAI,IACtG,YACA;AAAA,IACZ,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,SAAS,OAAqC;AAC5C,UAAM,QAAQ,MAAM,KAAK,CAAC,KAAK;AAC/B,QAAI,IAA6B,CAAC;AAClC,QAAI,UAAU,UAAU;AAEtB,YAAM,MAAM,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,CAAC,KAAK,MAAM;AAC/D,UAAI;AACF,cAAM,IAAa,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAC5C,YAAI,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,EAAG,KAAI;AAAA,MAC3D,QAAQ;AAAA,MAAC;AACT,YAAMO,MAAM,EAAE,WAAW,KAAK,EAAE,aAAa,EAAE,YAAY,KAAK,EAAE;AAClE,UAAI,CAACA,IAAI,QAAO;AAKhB,YAAM,OAAO,YAAY,OAAOA,GAAE,GAAG,MAAM,GAAwB;AACnE,UAAI,CAAC,QAAQ,cAAc,YAAY,IAAI,CAAC,EAAG,QAAO;AACtD,YAAMC,KAAgB;AAAA,QACpB,SAAS,EAAE,IAAI,OAAOD,GAAE,GAAG,KAAK,OAAO,EAAE,QAAQ,WAAW,EAAE,MAAM,MAAM,IAAI;AAAA,QAC9E,QAAQ;AAAA,UACN;AAAA,YACE,MAAM;AAAA,YACN,MAAM,EAAE,QAAQ,QAAQ,GAAI,OAAO,EAAE,SAAS,MAAM,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,EAAG;AAAA,YAC/F,cAAc;AAAA,UAChB;AAAA,QACF;AAAA,QACA,OAAO,CAAC;AAAA,QACR,YAAY;AAAA,MACd;AACA,aAAOC;AAAA,IACT;AACA,QAAI;AAEF,YAAM,IAAa,MAAM,QAAQ,KAAK,MAAM,MAAM,KAAK,IAAI,CAAC;AAC5D,UAAI,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,EAAG,KAAI;AAAA,IAC3D,QAAQ;AAAA,IAAC;AACT,UAAM,KAAM,EAAE,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,KAAK,EAAE,YAAY;AAGhG,QAAI,CAAC,GAAI,QAAO;AAKhB,QAAI,EAAE,oBAAoB,KAAM,QAAO;AACvC,UAAM,UAAiC,EAAE,IAAI,OAAO,EAAE,GAAG,KAAK,OAAO,EAAE,QAAQ,WAAW,EAAE,MAAM,MAAM,IAAI;AAC5G,QAAI,OAAO,EAAE,UAAU,SAAU,SAAQ,QAAQ,EAAE;AACnD,UAAM,UAAU,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU;AAC5D,UAAM,OAAOP,WAAU,EAAE,SAAS,IAAI,EAAE,YAAY;AACpD,UAAM,UAAU,OAAO,EAAE,gBAAgB,WAAW,EAAE,cAAc;AAKpE,UAAM,SAAS,OAAO,EAAE,oBAAoB,WAAW,SAAS,EAAE,eAAe,IAAI;AACrF,UAAM,WACJ,OAAO,EAAE,aAAa,YAAY,EAAE,WAChC,EAAE,WACF,OAAO,EAAE,gBAAgB,YAAY,EAAE,cACrC,EAAE,cACF,UAAU,WAAW,OAAO,EAAE,EAAE,YAAY,IAC1C,SACA;AACV,UAAM,KAAK,CAAC,MAAc,UAAmC,EAAE,MAAM,MAAMQ,OAAM,IAAI,GAAG,cAAc,MAAM;AAC5G,UAAM,SAA+B,CAAC;AACtC,YAAQ,OAAO;AAAA,MACb,KAAK;AACH,eAAO;AAAA,UACL,GAAG,mBAAmB;AAAA,YACpB,QACE,OAAO,EAAE,WAAW,YAAY,CAAC,WAAW,UAAU,SAAS,WAAW,MAAM,EAAE,SAAS,EAAE,MAAM,IAC/F,EAAE,SACF;AAAA,UACR,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,gBAAgB;AAAA,YACjB;AAAA,YACA,cAAc,OAAO,EAAE,WAAW,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS;AAAA,UACtE,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,YAAI,KAAM,QAAO,KAAK,GAAG,gBAAgB,EAAE,MAAM,SAAS,SAAS,UAAU,OAAO,EAAE,WAAW,CAAC,CAAC;AACnG;AAAA,MACF,KAAK;AACH,YAAI;AACF,iBAAO;AAAA,YACL,GAAG,cAAc;AAAA,cACf;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,IAAI;AAAA,cACJ,OAAO,EAAE;AAAA,cACT,QAAQ,EAAE;AAAA,YACZ,CAAC;AAAA,UACH;AACF;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,oBAAoB;AAAA,YACrB,cAAc,WAAW,QAAQ,KAAK,IAAI,CAAC;AAAA,YAC3C,MAAM;AAAA,YACN;AAAA,YACA;AAAA,UACF,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,cAAc,EAAE,SAAS,QAAQ,OAAO,CAAC,CAAC;AACzD;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,cAAc,EAAE,SAAS,QAAQ,YAAY,CAAC,CAAC;AAC9D;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,oBAAoB;AAAA,YACrB,UAAU,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE;AAAA,YAClD,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;AAAA,UAChE,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,kBAAkB;AAAA,YACnB,UAAU,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE;AAAA,YAClD,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;AAAA,UAChE,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,qBAAqB,EAAE,SAAS,EAAE,YAAY,WAAW,WAAW,OAAO,CAAC,CAAC;AAC5F;AAAA,MACF,KAAK;AACH;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,iBAAiB,EAAE,QAAQP,WAAU,EAAE,MAAM,EAAE,CAAC,CAAC;AAChE;AAAA,MACF;AACE,eAAO;AAAA,IACX;AACA,UAAM,IAAgB,EAAE,SAAS,QAAQ,OAAO,CAAC,EAAE;AAGnD,QAAI,OAAO,EAAE,oBAAoB,YAAY,CAAC,gBAAgB,EAAE,iBAAiB,OAAO,EAAE,CAAC;AACzF,QAAE,aAAa,EAAE;AACnB,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,MAAM,UAAU;AAC7B,QAAI,OAAO;AACX,QAAI;AACF,aAAOC,cAAa,MAAM,MAAM;AAAA,IAClC,QAAQ;AACN,aAAO,EAAE,OAAO,CAAC,GAAG,MAAM,SAAS;AAAA,IACrC;AACA,WAAO,UAAU,KAAK,MAAM,IAAI,GAAG,QAAQ;AAAA,EAC7C;AAAA,EAEA,MAAM,SAAS,YAA0B;AACvC,UAAM,MAAoB,CAAC;AAC3B,QAAI,WAAY,QAAO,OAAO,KAAK,WAAW,UAAU,CAAC;AACzD,WAAO;AAAA,EACT;AAAA,EAEA,SAAS,SAAS,MAAM,QAAQ,KAAoB;AAClD,UAAMO,QAAsB,CAAC;AAC7B,eAAW,KAAK,YAAY,YAAY,GAAG,GAAG,GAAG,SAAS,CAAC,MAAM,sBAAsB,KAAK,CAAC,CAAC,GAAG;AAC/F,YAAM,IAAI,wCAAwC,KAAKZ,MAAK,SAAS,CAAC,CAAC;AACvE,UAAI,CAAC,IAAI,CAAC,EAAG;AAEb,UAAI,cAAc,YAAY,CAAC,CAAC,EAAG;AACnC,YAAM,QAAQ,WAAW,CAAC;AAC1B,UAAI,CAAC,MAAM,cAAc,CAAC,MAAM,QAAS;AACzC,YAAM,EAAE,YAAY,SAAS,WAAW,KAAK,iBAAiB,KAAK,GAAG,KAAK,IAAI;AAC/E,MAAAY,MAAK,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,GAAG,GAAG,KAAK,GAAG,YAAY,QAAQ,CAAC;AAAA,IACnE;AACA,WAAOA;AAAA,EACT;AAAA,EAEA,QAAQ,CAAC,eAAe,YAAY,UAAU;AAChD;AAGA,IAAMC,YAAW,CAAC,MAChB,OAAO,MAAM,YAAY,IAAI,KAAK,IAAI,OAAO,IAAI,KAAK,IAAI,GAAI,EAAE,YAAY,IAAI;AAElF,SAASF,OAAyC,GAAS;AACzD,aAAW,KAAK,OAAO,KAAK,CAAC,EAAG,KAAI,EAAE,CAAC,MAAM,OAAW,QAAO,EAAE,CAAC;AAClE,SAAO;AACT;AASO,SAAS,YAAY,MAAgD;AAC1E,QAAM,EAAE,KAAK,IAAI,KAAK,MAAM,GAAG,MAAM,IAAI;AACzC,aAAW,KAAK,WAAW,IAAI,EAAE,QAAQ,GAAG;AAC1C,UAAM,IAAI,EAAE;AACZ,QAAI,EAAE,SAAS,eAAe,GAAG,SAAS,cAAe;AACzD,QAAI,CAAC,EAAE,eAAe,OAAO,EAAE,gBAAgB,SAAU;AACzD,UAAM,KAAK,EAAE;AACb,QAAI,GAAG,YAAY,QAAQ,GAAG,aAAa,QAAS;AACpD,UAAM,UAAgD,CAAC;AACvD,eAAW,MAAM,CAAC,WAAW,WAAW,GAAG;AACzC,YAAM,IAAI,GAAG,EAAE;AACf,UAAI,OAAO,GAAG,iBAAiB,YAAY,EAAE,eAAe,EAAG;AAC/D,cAAQ;AAAA,QACNA,OAAM;AAAA,UACJ;AAAA,UACA,cAAc,EAAE;AAAA,UAChB,gBACE,OAAO,UAAU,EAAE,cAAc,KAAK,OAAO,EAAE,cAAc,IAAI,IAAI,OAAO,EAAE,cAAc,IAAI;AAAA,UAClG,WAAWE,UAAS,EAAE,SAAS;AAAA,QACjC,CAAC;AAAA,MACH;AAAA,IACF;AACA,QAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,WAAOF,OAAM,EAAE,SAAS,MAAM,OAAO,GAAG,cAAc,WAAW,GAAG,UAAU,MAAM,GAAG,EAAE,IAAI,OAAU,CAAC;AAAA,EAC1G;AACA,SAAO;AACT;AAGO,SAAS,WAAW,MAA4B;AACrD,QAAM,EAAE,MAAM,KAAK,IAAI,KAAK,MAAM,MAAM,MAAM,KAAK,IAAI;AACvD,MAAI,IAAI,WAAW,IAAI;AACvB,MAAI,IAAI,WAAW,IAAI;AACvB,QAAM,MAAoB,CAAC;AAC3B,QAAM,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,OAAO;AACjE,QAAM,WAAW,oBAAoB,IAAI;AACzC,MAAI,MAAM,SAAS;AACjB,UAAM,IAAI,KAAK;AAKf,QAAI,EAAE,IAAK,KAAI,MAAM,EAAE;AACvB,QAAI,EAAE,YAAa,KAAI,kBAAkB,OAAO,EAAE,WAAW;AAC7D,QAAI,EAAE,KAAK,OAAQ,KAAI,SAAS,EAAE,IAAI;AACtC,QAAI,EAAE,KAAK,eAAgB,KAAI,OAAO,OAAO,EAAE,IAAI,cAAc,EAAE,QAAQ,eAAe,IAAI;AAAA,EAChG;AACA,MAAI,UAAU;AACZ,UAAM,IAAI,EAAE,UAAU,QAAQ;AAC9B,UAAM,IAAI,EAAE,UAAU,QAAQ;AAC9B,QAAI,IAAI,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;AAI1B,UAAM,QACJ,KAAK,KACL,KAAK,KACL,UAAU,MAAM,CAAC,SAAU,SAAS,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,IAAI,OAAO,MAAO,OAAO,iBAAiB;AAC7G,QAAI,KAAK,IAAI,EAAE,MAAM,CAAC,IAAI,QAAQ,IAAI,CAAC;AAAA,EACzC;AACA,QAAM,MAAM,EAAE,OAAO,CAAC,EACnB,QAAQ,EACR,KAAK,CAAC,MAAM,EAAE,SAAS,kBAAmB,EAAE,SAA4C,KAAK;AAGhG,MAAI,KAAK,SAAS,MAAO,KAAI,QAAQ,IAAI,QAAQ;AACjD,aAAW,KAAK,GAAG;AACjB,UAAM,IAAI,EAAE;AACZ,QAAI,EAAE,SAAS,mBAAmB,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,SAAS,OAAQ;AACnF,UAAM,QAAQ,EAAE,WAAW,CAAC,GACzB,OAAO,CAAC,MAAM,KAAK,EAAE,SAAS,gBAAgB,OAAO,EAAE,SAAS,QAAQ,EACxE,IAAI,CAAC,MAAM,EAAE,IAAc,EAC3B,KAAK,IAAI,EACT,KAAK;AACR,QAAI,CAAC,QAAQ,cAAc,IAAI,EAAG;AAClC,UAAM,OAAO,KAAK,MAAM,IAAI,EAAE,CAAC;AAC/B,QAAI,QAAQ,KAAK,SAAS,KAAK,GAAG,KAAK,MAAM,GAAG,EAAE,EAAE,QAAQ,WAAW,EAAE,CAAC,WAAM;AAChF,QAAI,eAAe;AACnB;AAAA,EACF;AACA,QAAM,SAAS,YAAY,OAAO,CAAC,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,GAC9D,IAAI,CAAC,MAAM,KAAK,MAAM,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC,EAChD,OAAO,CAAC,MAAM,IAAI,CAAC;AACtB,MAAI,MAAM,QAAQ;AAChB,QAAI,aAAa,KAAK,IAAI,GAAG,KAAK;AAClC,QAAI,UAAU,KAAK,IAAI,GAAG,KAAK;AAAA,EACjC;AACA,SAAO;AACT;;;AE3eA,SAAS,cAAAG,mBAAkB;AAC3B,SAAS,cAAAC,aAAY,oBAAoB;AACzC,OAAOC,WAAU;;;ACVjB,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,qBAAqB;AA8BvB,SAAS,MAAS,IAAgB;AACvC,QAAM,WAAW,QAAQ;AACzB,MAAI;AACF,YAAQ,eAAe,CAAC,YAAqB,SAAoB;AAC/D,YAAM,OAAO,OAAO,KAAK,CAAC,MAAM,WAAW,KAAK,CAAC,IAAK,KAAK,CAAC,GAAqC;AACjG,UAAI,SAAS,yBAAyB,UAAU,KAAK,OAAO,OAAO,CAAC,EAAG;AACvE,MAAC,SAAuC,KAAK,SAAS,SAAS,GAAG,IAAI;AAAA,IACxE;AACA,WAAO,GAAG;AAAA,EACZ,UAAE;AACA,YAAQ,cAAc;AAAA,EACxB;AACF;AAEA,IAAI;AAEJ,SAAS,SAAwB;AAC/B,MAAI,cAAc,OAAW,QAAO;AACpC,cAAY;AACZ,MAAI;AACF,UAAM,MAAM,cAAc,YAAY,GAAG;AACzC,UAAM,IAAI,MAAM,MAAM,IAAI,aAAa,CAAC;AACxC,QAAI,OAAO,GAAG,iBAAiB,WAAY,aAAY,EAAE;AAAA,EAC3D,QAAQ;AAAA,EAAC;AACT,SAAO;AACT;AAGA,SAAS,MAAM,KAAgC;AAC7C,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,MAAiB,CAAC;AACxB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAgB,GAAG;AACrD,QAAI,OAAO,MAAM,SAAU,KAAI,CAAC,IAAI,OAAO,CAAC;AAAA,aACnC,aAAa,WAAY,KAAI,CAAC,IAAI;AAAA,QACtC,KAAI,CAAC,IAAI;AAAA,EAChB;AACA,SAAO;AACT;AACA,IAAM,OAAO,CAAC,MAAmC;AAC/C,MAAI,CAAC,MAAM,QAAQ,CAAC,EAAG,QAAO;AAC9B,QAAM,MAAmB,CAAC;AAC1B,aAAW,KAAK,GAAG;AACjB,UAAM,IAAI,MAAM,CAAC;AACjB,QAAI,EAAG,KAAI,KAAK,CAAC;AAAA,EACnB;AACA,SAAO;AACT;AAEA,SAAS,QAAQ,MAAc,KAAa,QAAuB,GAAgD;AACjH,QAAM,IAAI,OAAO;AACjB,MAAI,CAAC,EAAG,QAAO;AACf,MAAI;AACJ,MAAI;AACF,SAAK,MAAM,MAAM,IAAI,EAAE,MAAM,EAAE,UAAU,MAAM,SAAS,EAAE,UAAU,CAAC,CAAC;AAAA,EACxE,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI;AACF,QAAI;AACF,SAAG,KAAK,yBAAyB,KAAK,MAAM,EAAE,SAAS,CAAC,EAAE;AAAA,IAC5D,QAAQ;AAAA,IAAC;AACT,WAAO,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;AAAA,EACzD,QAAQ;AACN,WAAO;AAAA,EACT,UAAE;AACA,QAAI;AACF,SAAG,MAAM;AAAA,IACX,QAAQ;AAAA,IAAC;AAAA,EACX;AACF;AAGA,IAAM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQd,SAAS,SAAS,MAAc,KAAa,QAAuB,GAAgD;AAClH,MAAI;AACF,UAAM,MAAMD;AAAA,MACV,QAAQ;AAAA,MACR,CAAC,yBAAyB,iBAAiB,MAAM,OAAO,MAAM,KAAK,KAAK,UAAU,MAAM,GAAG,OAAO,EAAE,SAAS,CAAC;AAAA,MAC9G;AAAA,QACE,UAAU;AAAA,QACV,SAAS,EAAE,YAAY;AAAA,QACvB,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,QAClC,WAAW,EAAE;AAAA,QACb,aAAa;AAAA,MACf;AAAA,IACF;AACA,WAAO,KAAK,KAAK,MAAM,OAAO,MAAM,CAAC;AAAA,EACvC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,IAAM,OAAO;AACN,SAAS,aAAa,KAAa,QAAsC;AAC9E,MAAI,IAAI,SAAS,GAAG,EAAG,QAAO;AAC9B,QAAM,QAAQ,IAAI,MAAM,GAAG,EAAE,SAAS;AACtC,MAAI,UAAU,OAAO,OAAQ,QAAO;AACpC,MAAI,IAAI;AACR,MAAI,MAAM;AACV,QAAM,MAAM,IAAI,QAAQ,OAAO,MAAM;AACnC,UAAM,IAAI,OAAO,GAAG;AACpB,QAAI,OAAO,MAAM,UAAU;AACzB,UAAI,CAAC,OAAO,SAAS,CAAC,GAAG;AACvB,cAAM;AACN,eAAO;AAAA,MACT;AACA,aAAO,OAAO,CAAC;AAAA,IACjB;AACA,QAAI,OAAO,MAAM,YAAY,CAAC,KAAK,KAAK,CAAC,GAAG;AAC1C,YAAM;AACN,aAAO;AAAA,IACT;AACA,WAAO,IAAI,CAAC;AAAA,EACd,CAAC;AACD,SAAO,MAAM,OAAO;AACtB;AAEA,SAAS,OAAO,MAAc,KAAa,QAAuB,GAAgD;AAChH,QAAM,OAAO,aAAa,KAAK,MAAM;AACrC,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI;AACF,UAAM,MAAMA,cAAa,WAAW,CAAC,aAAa,SAAS,MAAM,IAAI,GAAG;AAAA,MACtE,UAAU;AAAA,MACV,SAAS,EAAE,YAAY;AAAA,MACvB,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,MAClC,WAAW,EAAE;AAAA,MACb,aAAa;AAAA,IACf,CAAC,EAAE,KAAK;AACR,QAAI,CAAC,IAAK,QAAO,CAAC;AAClB,WAAO,KAAK,KAAK,MAAM,GAAG,CAAC;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,YACd,MACA,KACA,SAAwB,CAAC,GACzB,OAAsB,CAAC,GACH;AACpB,QAAM,IAA6B;AAAA,IACjC,WAAW,KAAK,aAAa;AAAA,IAC7B,QAAQ,KAAK,UAAU;AAAA,IACvB,WAAW,KAAK,aAAa,KAAK,OAAO;AAAA,EAC3C;AACA,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI;AACF,QAAI,CAACC,YAAW,IAAI,EAAG,QAAO;AAAA,EAChC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,OACJ,EAAE,WAAW,SACR,CAAC,SAAS,UAAU,MAAM,IAC3B,EAAE,WAAW,SACV,CAAC,OAAO,IACT,EAAE,WAAW,UACV,CAAC,QAAQ,IACT,CAAC,MAAM;AAClB,aAAW,OAAO,MAAM;AACtB,UAAM,IAAI,IAAI,MAAM,KAAK,QAAQ,CAAC;AAClC,QAAI,EAAG,QAAO;AAAA,EAChB;AACA,SAAO;AACT;;;ADrLA,IAAMC,QAAO;AAGN,IAAM,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAMC,QAAO,EAAE,YAAY,CAAC,MAAe,kBAAkB,KAAK,CAAC,IAAI,KAAK,QAAY,SAAS,EAAE;AACnG,IAAMC,aAAY,CAAC,SAAoB,OAAO,aAAa,OAAO,CAAC,MAAM,MAAM,YAAY,IAAI,CAAC,GAAG,YAAY;AAC/G,IAAM,eACJ;AACF,IAAM,cACJ;AAEF,IAAM,eAAe,CAAC,QACpB,yCAAyC,GAAG;AAIvC,SAAS,aAAa,MAAkC;AAC7D,QAAM,QAAQ,SAAS,IAAI,EAAE;AAC7B,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,QAAM,KAAK;AACX,SAAO,OAAO,KAAK,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;AACvD;AAEA,IAAM,SAAS,CAAC,QAA2B,IAAI,QAAQ,QAAQ;AAC/D,IAAM,UAAU,CAAC,QAA2B,IAAI,WAAWC,MAAK,KAAK,OAAO,GAAG,GAAG,WAAW,SAAS;AAE/F,IAAM,iBAAiB,CAAC,MAAyB,QAAQ,QAC9D,QAAQ,aAAa,UACjBA,MAAK,KAAK,QAAQ,GAAG,GAAG,OAAO,IAC/BA,MAAK,KAAK,IAAI,mBAAmBA,MAAK,KAAK,OAAO,GAAG,GAAG,SAAS,GAAG,OAAO;AACjF,IAAMC,cAAa,CAAC,QAA2BD,MAAK,KAAK,eAAe,GAAG,GAAG,aAAa;AAEpF,IAAM,eAAe,CAAC,MAAyB,QAAQ,QAC5D,QAAQ,aAAa,UACjBA,MAAK,KAAK,QAAQ,GAAG,GAAG,SAAS,KAAK,IACtCA,MAAK,KAAK,IAAI,iBAAiBA,MAAK,KAAK,OAAO,GAAG,GAAG,UAAU,OAAO,GAAG,SAAS,KAAK;AAC9F,IAAM,aAAa,CAAC,QAA2BA,MAAK,KAAK,aAAa,GAAG,GAAG,aAAa;AAEzF,IAAM,aAAa;AACnB,IAAME,aAAY,CAAC,MAA4B,OAAO,MAAM,YAAY,2BAA2B,KAAK,CAAC;AACzG,IAAMC,aAAY,CAAC,MACjB,MAAM,WAAW,MAAM,YAAY,MAAM,SAAS,IAAI;AACxD,IAAMC,cAAa,CAAC,MAClB,OAAO,MAAM,YAAY,CAAC,WAAW,UAAU,SAAS,WAAW,MAAM,EAAE,SAAS,CAAC,IAAI,IAAI;AAC/F,SAASC,SAAQ,GAAgC;AAC/C,MAAI,OAAO,MAAM,YAAY,EAAG,QAAO,EAAE,MAAM,GAAG,GAAI;AACtD,MAAI,KAAK,OAAO,MAAM,UAAU;AAC9B,UAAM,IAAI;AACV,QAAI,OAAO,EAAE,YAAY,YAAY,EAAE,QAAS,QAAO,EAAE,QAAQ,MAAM,GAAG,GAAI;AAC9E,QAAI,OAAO,EAAE,UAAU,YAAY,EAAE,MAAO,QAAO,EAAE,MAAM,MAAM,GAAG,GAAI;AAAA,EAC1E;AACA,SAAO;AACT;AACA,IAAM,MAAM,CAAC,GAAW,MAAuB,EAAE,SAAS,IAAI,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,QAAQ,WAAW,EAAE,CAAC,WAAM;AAEpG,IAAM,QAAiB;AAAA,EAC5B,MAAMR;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ,CAAC,MAAM,QAAQ,QAAQS,YAAW,eAAe,GAAG,CAAC,KAAKA,YAAW,aAAa,GAAG,CAAC;AAAA,EAC9F,aAAa,CAAC,MAAM,QAAQ,QAAQ,CAACL,YAAW,GAAG,CAAC;AAAA,EACpD,SAAS,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAC9D,UAAM,OAAOA,YAAW,GAAG;AAC3B,UAAM,IAAI,oBAAoB,MAAMJ,OAAME,WAAU,KAAK,IAAI,GAAG,KAAKD,KAAI;AACzE,QAAI,EAAE,QAAS,QAAO,CAAC,CAAC;AACxB,UAAM,MAAM,aAAa,IAAI;AAC7B,WAAO,CAAC,EAAE,GAAG,GAAG,MAAM,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC;AAAA,EAChE;AAAA,EACA,WAAW,CAAC,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAAA,IAC3D,sBAAsBG,YAAW,GAAG,GAAG,cAAc,EAAE,SAAS,KAAK,SAAS,SAASA,YAAW,GAAG,CAAC,EAAE,CAAC;AAAA,EAC3G;AAAA,EACA,WAAW,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAChE,UAAM,OAAOA,YAAW,GAAG;AAC3B,UAAM,QAAQ,sBAAsB,MAAMJ,OAAME,WAAU,KAAK,IAAI,GAAG,KAAKD,KAAI;AAI/E,UAAM,MAAM,UAAU,YAAY,SAAY,aAAa,IAAI;AAC/D,WAAO,CAAC,EAAE,MAAM,OAAO,MAAM,aAAa,OAAO,MAAM,MAAM,aAAa,GAAG,IAAI,YAAY,CAAC;AAAA,EAChG;AAAA,EAEA,SAAS,OAAqC;AAC5C,UAAM,QAAQ,MAAM,KAAK,CAAC,KAAK;AAC/B,QAAI,IAA6B,CAAC;AAClC,QAAI;AAEF,YAAM,IAAa,MAAM,QAAQ,KAAK,MAAM,MAAM,KAAK,IAAI,CAAC;AAC5D,UAAI,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,EAAG,KAAI;AAAA,IAC3D,QAAQ;AAAA,IAAC;AACT,UAAM,KAAK,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;AAC7D,QAAI,CAAC,GAAI,QAAO;AAEhB,UAAM,UAAiC,EAAE,IAAI,KAAK,MAAM,IAAI,qBAAqB,MAAM,IAAI;AAC3F,UAAM,UAAU,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY;AAChE,UAAM,OAAOI,WAAU,EAAE,SAAS,IAAI,EAAE,YAAY;AACpD,UAAM,UAAU,OAAO,EAAE,gBAAgB,WAAW,EAAE,cAAc;AAIpE,UAAM,WACJ,EAAE,iBAAiB,OAAO,EAAE,kBAAkB,YAAY,CAAC,MAAM,QAAQ,EAAE,aAAa,IACnF,EAAE,gBACH;AAIN,UAAM,eAAe,MACnB,QAAQK,YAAW,QAAQ,EACxB,OAAO,GAAG,WAAW,EAAE,IAAI,QAAQ,EAAE,IAAI,KAAK,UAAU,EAAE,cAAc,IAAI,CAAC,EAAE,EAC/E,OAAO,KAAK,EACZ,MAAM,GAAG,EAAE,CAAC;AACjB,UAAM,KAAK,CAAC,MAAc,UAAmC,EAAE,MAAM,MAAMC,OAAM,IAAI,GAAG,cAAc,MAAM;AAC5G,UAAM,SAA+B,CAAC;AACtC,YAAQ,OAAO;AAAA,MACb,KAAK;AACH,eAAO,KAAK,GAAG,mBAAmB,EAAE,QAAQJ,YAAW,EAAE,MAAM,EAAE,CAAC,CAAC;AACnE;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,gBAAgB;AAAA,YACjB;AAAA,YACA,cAAc,OAAO,EAAE,WAAW,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS;AAAA,UACtE,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,YAAI,KAAM,QAAO,KAAK,GAAG,gBAAgB,EAAE,MAAM,SAAS,SAAS,OAAO,EAAE,WAAW,CAAC,CAAC;AACzF;AAAA,MACF,KAAK;AAEH,YAAI;AACF,iBAAO;AAAA,YACL,GAAG,cAAc;AAAA,cACf;AAAA,cACA;AAAA,cACA;AAAA;AAAA;AAAA,cAGA,IAAI,UAAU,YAAY;AAAA,cAC1B,OAAOC,SAAQ,UAAU,KAAK;AAAA,cAC9B,OAAO,EAAE;AAAA,cACT,QAAQ,WAAW,SAAS,SAAS,EAAE;AAAA,YACzC,CAAC;AAAA,UACH;AACF;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,oBAAoB,EAAE,cAAc,WAAW,aAAa,GAAG,MAAM,cAAc,KAAK,CAAC,CAAC;AACzG;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,cAAc,EAAE,SAAS,QAAQ,OAAO,CAAC,CAAC;AACzD;AAAA,MACF,KAAK;AAEH,eAAO,KAAK,GAAG,qBAAqB,EAAE,SAAS,OAAO,CAAC,CAAC;AACxD;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,iBAAiB,EAAE,QAAQF,WAAU,EAAE,MAAM,EAAE,CAAC,CAAC;AAChE;AAAA,MACF;AACE,eAAO;AAAA,IACX;AACA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,OAAO,CAAC;AAAA,MACR,YAAY,cAAc,WAAW,MAAM,GAAwB,GAAG,EAAE;AAAA,IAC1E;AAAA,EACF;AAAA,EAEA,eAAe,MAAM,UAAU;AAC7B,UAAM,MAAM,SAAS,IAAI;AACzB,QAAI,CAAC,IAAK,QAAO,EAAE,OAAO,CAAC,GAAG,MAAM,SAAS;AAC7C,WAAO,eAAe,IAAI,IAAI,IAAI,SAAS,QAAQ;AAAA,EACrD;AAAA,EAEA,MAAM,SAAS,YAAY,QAAQ,QAAQ,MAAM,QAAQ,KAAmB;AAC1E,UAAM,MAAoB,CAAC;AAC3B,UAAM,UAAU,aAAa,GAAG;AAChC,QAAI,QAAS,KAAI,kBAAkB;AACnC,UAAM,UAAU,aAAa,GAAG;AAChC,QAAI,QAAS,KAAI,aAAa;AAC9B,UAAM,MAAM,SAAS,UAAU,KAAK,EAAE,IAAI,WAAW,GAAG,GAAG,SAAS,QAAQ,GAAG;AAC/E,QAAI,CAAC,WAAW,KAAK,IAAI,OAAO,EAAG,QAAO;AAC1C,UAAMM,QAAO,YAAY,IAAI,IAAI,aAAa,CAAC,IAAI,OAAO,CAAC;AAC3D,UAAM,MAAMA,QAAO,CAAC;AACpB,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO,OAAO,KAAK,aAAa,GAAG,CAAC;AACpC,WAAO;AAAA,EACT;AAAA,EAEA,SAAS,SAAS,MAAM,QAAQ,KAAoB;AAClD,UAAMA,QAAO,YAAY,WAAW,GAAG,GAAG,cAAc,CAAC,QAAQ,KAAK,MAAM,UAAU,GAAI,CAAC,CAAC;AAC5F,QAAI,CAACA,MAAM,QAAO,CAAC;AACnB,UAAM,MAAqB,CAAC;AAC5B,eAAW,OAAOA,OAAM;AACtB,YAAM,KAAK,OAAO,IAAI,EAAE;AACxB,UAAI,CAAC,WAAW,KAAK,EAAE,EAAG;AAC1B,YAAM,EAAE,YAAY,SAAS,GAAG,KAAK,IAAI,aAAa,GAAG;AACzD,UAAI,CAAC,cAAc,CAAC,QAAS;AAC7B,UAAI,KAAK,EAAE,SAAS,EAAE,IAAI,GAAG,KAAK,GAAG,YAAY,QAAQ,CAAC;AAAA,IAC5D;AACA,WAAO;AAAA,EACT;AACF;AAEA,SAASD,OAAyC,GAAS;AACzD,aAAW,KAAK,OAAO,KAAK,CAAC,EAAG,KAAI,EAAE,CAAC,MAAM,OAAW,QAAO,EAAE,CAAC;AAClE,SAAO;AACT;AAMO,IAAM,gBAAgB,CAAC,IAAY,YAA4B,GAAG,EAAE,IAAI,OAAO;AAC/E,SAAS,SAAS,KAAiE;AACxF,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,KAAK,IAAI,YAAY,GAAG;AAC9B,MAAI,MAAM,EAAG,QAAO;AACpB,QAAM,KAAK,IAAI,MAAM,GAAG,EAAE;AAC1B,QAAM,UAAU,IAAI,MAAM,KAAK,CAAC;AAChC,SAAO,MAAM,WAAW,KAAK,OAAO,IAAI,EAAE,IAAI,QAAQ,IAAI;AAC5D;AAgBA,IAAM,cAAc;AACb,IAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kFAOyD,WAAW;AAK7F,IAAM,iBAAiB;AAAA;AAAA;AAGvB,IAAM,cAAc,UAAU,cAAc;AAI5C,IAAM,eAAe,gBAAgB,cAAc;AAAA;AAAA;AAInD,SAAS,aAAa,KAA4C;AAChE,QAAM,MAAoB,CAAC;AAC3B,MAAI,OAAO,IAAI,UAAU,YAAY,IAAI,MAAO,KAAI,QAAQ,IAAI;AAChE,MAAI,OAAO,IAAI,sBAAsB,YAAYR,MAAK,WAAW,IAAI,iBAAiB;AACpF,QAAI,MAAM,IAAI;AAChB,QAAM,UAAU,OAAO,IAAI,UAAU;AACrC,QAAM,OAAO,OAAO,IAAI,gBAAgB;AACxC,MAAI,OAAO,SAAS,OAAO,KAAK,UAAU,EAAG,KAAI,aAAa,KAAK,MAAM,UAAU,GAAI;AACvF,MAAI,OAAO,SAAS,IAAI,KAAK,OAAO,EAAG,KAAI,UAAU,KAAK,MAAM,OAAO,GAAI;AAC3E,MAAI,OAAO,IAAI,cAAc,UAAU;AACrC,UAAM,OAAO,IAAI,UAAU,MAAM,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK;AAChE,QAAI,KAAK,KAAK,GAAG;AACf,UAAI,QAAQ,IAAI,KAAK,KAAK,GAAG,EAAE;AAC/B,UAAI,eAAe;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;AAaO,SAAS,eAAe,IAAY,SAAiB,UAAkC;AAC5F,MAAI,CAAC,WAAW,KAAK,OAAO,EAAG,QAAO,EAAE,OAAO,CAAC,GAAG,MAAM,SAAS;AAClE,QAAM,QAAQ,OAAO,SAAS,QAAQ,KAAK,WAAW,IAAI,KAAK,MAAM,QAAQ,IAAI;AACjF,QAAMS,QAAO,YAAY,IAAI,WAAW,CAAC,SAAS,SAAS,SAAS,KAAK,CAAC;AAC1E,MAAI,CAACA,OAAM,OAAQ,QAAO,EAAE,OAAO,CAAC,GAAG,MAAM,SAAS;AACtD,QAAM,OAAiF,CAAC;AACxF,MAAI,MAAM;AACV,aAAW,OAAOA,OAAM;AACtB,UAAM,OAAO,OAAO,IAAI,OAAO;AAC/B,QAAI,CAAC,OAAO,SAAS,IAAI,EAAG;AAC5B,QAAI,OAAO,IAAI,IAAK,OAAM,OAAO;AACjC,QAAI,OAAO,SAAU;AACrB,QAAI;AACJ,QAAI;AACF,UAAI,KAAK,MAAM,OAAO,IAAI,gBAAgB,EAAE,CAAC;AAAA,IAC/C,QAAQ;AACN;AAAA,IACF;AACA,UAAM,OAAO,OAAO,EAAE,YAAY,WAAW,EAAE,QAAQ,KAAK,IAAI;AAChE,QAAI,CAAC,KAAM;AAUX,UAAM,OAAO,OAAO,IAAI,UAAU;AAClC,UAAM,KACJ,KAAK,MAAM,OAAO,EAAE,UAAU,cAAc,EAAE,CAAC,MAC9C,OAAO,SAAS,IAAI,KAAK,OAAO,IAAI,KAAK,MAAM,OAAO,GAAI,IAAI,MAC/D,KAAK,IAAI;AACX,QAAI,EAAE,SAAS,UAAU,EAAE,UAAU,kBAAkB,KAAM,MAAK,KAAK,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,KAAK,CAAC;AAAA,aACpG,EAAE,SAAS,YAAa,MAAK,KAAK,EAAE,MAAM,aAAa,MAAM,IAAI,MAAM,KAAK,CAAC;AAAA,EACxF;AAEA,OAAK,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI;AACnC,SAAO,UAAU,MAAM,GAAG;AAC5B;AAGO,SAAS,aAAa,MAAyB,QAAQ,KAAyB;AACrF,MAAI;AACF,UAAM,SAAS,aAAaT,MAAK,KAAK,aAAa,GAAG,GAAG,aAAa,SAAS,CAAC;AAChF,UAAM,OAAOA,MAAK,SAAS,MAAM;AACjC,WAAO,8BAA8B,KAAK,IAAI,IAAI,OAAO;AAAA,EAC3D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAIO,SAAS,aAAa,MAAyB,QAAQ,KAAyB;AACrF,QAAM,IAAI,SAASC,YAAW,GAAG,CAAC,EAAE;AACpC,QAAM,KAAK,KAAK,OAAO,MAAM,WAAW,EAAE,SAAS;AACnD,SAAO,OAAO,OAAO,YAAY,2BAA2B,KAAK,EAAE,IAAI,KAAK;AAC9E;;;AEzYA,SAAS,gBAAAS,qBAAoB;AAC7B,SAAS,cAAAC,aAAY,gBAAAC,qBAAoB;AACzC,OAAOC,YAAU;AAQjB,IAAMC,QAAO;AACN,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAMC,QAAO,EAAE,YAAY,CAAC,MAAe,OAAO,KAAK,CAAC,IAAI,OAAO,QAAY,SAAS,IAAK;AAC7F,IAAM,YAAY,CAAC,QAA2B,IAAI,mBAAmBC,OAAK,KAAK,QAAQ,GAAG,SAAS;AACnG,IAAM,WAAW,CAAC,QAA2BA,OAAK,KAAK,UAAU,GAAG,GAAG,eAAe;AACtF,IAAMC,aAAY,CAAC,MAA4B,OAAO,MAAM,YAAY,2BAA2B,KAAK,CAAC;AACzG,IAAMC,aAAY,CAAC,SAAoB,OAAO,cAAc,OAAO,CAAC,MAAM,MAAM,YAAY,IAAI,CAAC,GAAG,aAAa;AAK1G,SAAS,cACd,WAA4B,QAAQ,UACpC,MAAyB,QAAQ,KACzB;AACR,QAAM,MAAM,aAAa;AACzB,QAAM,MAAM,MACR,WAAW,EAAE,OAAO,CAAC,cAAc,YAAY,GAAG,MAAM,CAACF,OAAK,QAAQ,QAAQ,QAAQ,CAAC,GAAG,IAAI,CAAC,IAC/F;AACJ,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,mBAAmB;AAC7C,QAAM,IAAI,MAAM,KAAK,CAAC,WAAW,CAAC;AAClC,SAAOG,cAAa,EAAE,MAAM,EAAE,MAAM;AAAA,IAClC,UAAU;AAAA,IACV,SAAS;AAAA,IACT,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,IAClC;AAAA,IACA,OAAO,EAAE;AAAA,EACX,CAAC,EAAE,KAAK;AACV;AAGO,SAAS,kBAAkB,cAA0D;AAC1F,QAAM,QAAQ,SAAS,YAAY,EAAE;AACrC,MAAI,OAAO,gBAAgB,KAAM,QAAO,EAAE,QAAQ,KAAK;AACvD,MAAI,OAAO,gBAAgB;AACzB,WAAO,EAAE,QAAQ,OAAO,MAAM,wDAAwD;AACxF,MAAI,UAAU;AACd,MAAI;AACF,cAAU,cAAc;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,MAAM;AAAA,IACR;AAAA,EACF;AACA,QAAM,IAAI,eAAe,KAAK,OAAO;AACrC,MAAI,MAAM,OAAO,EAAE,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC,KAAK,IAAK,QAAO,EAAE,QAAQ,KAAK;AACzE,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,MAAM,wBAAwB,WAAW,GAAG;AAAA,EAC9C;AACF;AAEO,IAAM,YAAqB;AAAA,EAChC,MAAML;AAAA,EACN,QAAQ;AAAA;AAAA,EAER,QAAQ,CAAC,MAAM,QAAQ,QAAQM,YAAW,SAAS,GAAG,CAAC,KAAKA,YAAWJ,OAAK,KAAK,UAAU,GAAG,GAAG,KAAK,CAAC;AAAA,EACvG,aAAa,CAAC,MAAM,QAAQ,QAAQ,CAAC,SAAS,GAAG,CAAC;AAAA,EAClD,SAAS,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAC9D,UAAM,IAAI,oBAAoB,SAAS,GAAG,GAAGF,OAAMI,WAAU,KAAK,IAAI,GAAG,KAAKH,KAAI;AAClF,UAAM,IAAI,kBAAkB,SAAS,GAAG,CAAC;AACzC,WAAO,CAAC,EAAE,QAAQ,CAAC,EAAE,UAAU,EAAE,GAAG,GAAG,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI,EAAE,IAAI,CAAC;AAAA,EAChG;AAAA,EACA,WAAW,CAAC,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAAA,IAC3D,sBAAsB,SAAS,GAAG,GAAG,eAAe,EAAE,SAAS,KAAK,SAAS,SAAS,SAAS,GAAG,CAAC,EAAE,CAAC;AAAA,EACxG;AAAA,EACA,WAAW,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAChE,UAAM,QAAQ,sBAAsB,SAAS,GAAG,GAAGD,OAAMI,WAAU,KAAK,IAAI,GAAG,KAAKH,KAAI;AACxF,UAAM,IAAI,kBAAkB,SAAS,GAAG,CAAC;AACzC,WAAO;AAAA,MACL;AAAA,QACE,MAAM,SAAS,GAAG;AAAA,QAClB,OAAO,UAAU,aAAa,CAAC,EAAE,SAAS,aAAa;AAAA,QACvD,GAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,SAAS,OAAqC;AAC5C,UAAM,QAAQ,MAAM,KAAK,CAAC,KAAK;AAC/B,QAAI,IAA6B,CAAC;AAClC,QAAI;AAEF,YAAM,IAAa,MAAM,QAAQ,KAAK,MAAM,MAAM,KAAK,IAAI,CAAC;AAC5D,UAAI,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,EAAG,KAAI;AAAA,IAC3D,QAAQ;AAAA,IAAC;AACT,UAAM,KAAK,EAAE;AACb,QAAI,CAAC,GAAI,QAAO;AAChB,UAAM,UAAiC,EAAE,IAAI,OAAO,EAAE,GAAG,KAAK,OAAO,EAAE,QAAQ,WAAW,EAAE,MAAM,MAAM,IAAI;AAC5G,UAAM,OAAOE,WAAU,EAAE,SAAS,IAAI,EAAE,YAAY;AACpD,UAAM,KAAK,CAAC,MAAc,UAAmC,EAAE,MAAM,MAAMI,OAAM,IAAI,GAAG,cAAc,MAAM;AAC5G,UAAM,SAA+B,CAAC;AACtC,YAAQ,OAAO;AAAA,MACb,KAAK;AACH,eAAO;AAAA,UACL,GAAG,mBAAmB;AAAA,YACpB,QACE,OAAO,EAAE,WAAW,YAAY,CAAC,WAAW,UAAU,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,EAAE,SAAS;AAAA,UACnG,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,gBAAgB,EAAE,cAAc,OAAO,EAAE,WAAW,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,OAAU,CAAC;AAAA,QACtG;AACA;AAAA,MACF,KAAK;AACH,YAAI,KAAM,QAAO,KAAK,GAAG,gBAAgB,EAAE,MAAM,OAAO,EAAE,WAAW,CAAC,CAAC;AACvE;AAAA,MACF,KAAK,aAAa;AAChB,cAAM,OAAO,EAAE;AACf,cAAM,MACJ,QAAQ,OAAO,SAAS,YAAY,KAAK,QACrC,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAK,UAAU,KAAK,KAAK,CAAC,EAAE,MAAM,GAAG,GAAI,IAC9F;AACN,YAAI;AACF,iBAAO;AAAA,YACL,GAAG,cAAc;AAAA,cACf;AAAA,cACA,IAAI,CAAC;AAAA,cACL,OAAO;AAAA,cACP,OAAO,EAAE;AAAA,cACT,QAAQ,MAAM,cAAc,EAAE;AAAA,YAChC,CAAC;AAAA,UACH;AACF;AAAA,MACF;AAAA,MACA,KAAK;AACH,eAAO;AAAA,UACL,GAAG,oBAAoB;AAAA,YACrB,cAAc,SAAS,KAAK,IAAI,CAAC;AAAA,YACjC,MAAM,EAAE,sBAAsB,mBAAmB,eAAe;AAAA,YAChE,SAAS,OAAO,EAAE,YAAY,WAAW,EAAE,QAAQ,MAAM,GAAG,GAAG,IAAI;AAAA,UACrE,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,cAAc,EAAE,QAAQ,OAAO,CAAC,CAAC;AAChD;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,qBAAqB,EAAE,SAAS,EAAE,YAAY,WAAW,WAAW,OAAO,CAAC,CAAC;AAC5F;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,iBAAiB;AAAA,YAClB,QACE,EAAE,WAAW,WAAW,EAAE,WAAW,YAAY,EAAE,WAAW,SAC1D,EAAE,SACF,EAAE,WAAW,sBACX,SACA;AAAA,UACV,CAAC;AAAA,QACH;AACA;AAAA,MACF;AACE,eAAO;AAAA,IACX;AACA,UAAM,IAAgB,EAAE,SAAS,QAAQ,OAAO,CAAC,EAAE;AACnD,QAAI,OAAO,EAAE,oBAAoB,SAAU,GAAE,aAAa,EAAE;AAC5D,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,MAAM,UAAU;AAC7B,QAAI,OAAO;AACX,QAAI;AACF,aAAOC,cAAa,MAAM,MAAM;AAAA,IAClC,QAAQ;AACN,aAAO,EAAE,OAAO,CAAC,GAAG,MAAM,SAAS;AAAA,IACrC;AACA,WAAO,WAAW,KAAK,MAAM,IAAI,GAAG,QAAQ;AAAA,EAC9C;AAAA,EAEA,MAAM,UAAU,YAA0B;AACxC,UAAM,MAAoB,CAAC;AAC3B,QAAI,CAAC,WAAY,QAAO;AACxB,UAAM,EAAE,MAAM,KAAK,IAAI,KAAK,YAAY,KAAK,MAAM,KAAK,IAAI;AAC5D,UAAM,IAAI,WAAW,IAAI;AACzB,UAAM,IAAI,WAAW,IAAI;AACzB,UAAM,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM;AAC7C,UAAM,MAAM,QAAS,MAAM,WAAW,MAAM,WAAW,MAAM,OAAQ;AACrE,UAAM,OAAO,OAAO,QAAQ,WAAW,IAAI,KAAK,IAAI;AACpD,QAAI,MAAM;AACR,YAAM,OAAO,KAAK,MAAM,IAAI,EAAE,CAAC;AAC/B,UAAI,QAAQ,KAAK,SAAS,KAAK,GAAG,KAAK,MAAM,GAAG,EAAE,EAAE,QAAQ,WAAW,EAAE,CAAC,WAAM;AAChF,UAAI,eAAe;AAAA,IACrB;AACA,UAAM,QAAQ,EAAE,OAAO,CAAC,EACrB,IAAI,CAAC,MAAM,KAAK,MAAM,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC,EAChD,OAAO,CAAC,MAAM,IAAI,CAAC;AACtB,QAAI,MAAM,QAAQ;AAChB,UAAI,aAAa,KAAK,IAAI,GAAG,KAAK;AAClC,UAAI,UAAU,KAAK,IAAI,GAAG,KAAK;AAAA,IACjC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,MAAM,CAAC;AAAA;AACnB;AAEA,SAASD,OAAyC,GAAS;AACzD,aAAW,KAAK,OAAO,KAAK,CAAC,EAAG,KAAI,EAAE,CAAC,MAAM,OAAW,QAAO,EAAE,CAAC;AAClE,SAAO;AACT;;;ACrNO,IAAM,WAA+B,CAAC,YAAY,OAAO,WAAW,aAAa,KAAK;AACtF,IAAM,gBAAgB,CAAC,SAAsC,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;;;ACZxG,SAAS,WAAW,cAAAE,aAAY,aAAAC,YAAW,gBAAAC,eAAc,iBAAAC,sBAAqB;AAC9E,OAAOC,YAAU;AAgDV,IAAM,iBAAyB,EAAE,OAAO,CAAC,GAAG,WAAW,CAAC,GAAG,cAAc,MAAM,WAAW,GAAG;AAE7F,SAAS,WAAW,OAAe,WAAW,GAAW;AAC9D,MAAI;AACF,UAAM,IAAI,KAAK,MAAMC,cAAa,MAAM,MAAM,CAAC;AAC/C,WAAO;AAAA,MACL,GAAG;AAAA,MACH,GAAG;AAAA,MACH,OAAO,MAAM,QAAQ,EAAE,KAAK,IAAI,EAAE,QAAQ,CAAC;AAAA,MAC3C,WAAW,EAAE,aAAa,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY,CAAC;AAAA,IAC7E;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,GAAG,gBAAgB,OAAO,CAAC,GAAG,WAAW,CAAC,EAAE;AAAA,EACvD;AACF;AAIA,SAAS,cAAc,MAAkC;AACvD,MAAI;AACJ,MAAI;AACF,WAAOA,cAAa,MAAM,MAAM;AAAA,EAClC,SAAS,GAAG;AAEV,UAAM,OAAQ,EAA4B;AAC1C,WAAO,SAAS,WAAW,SAAY,sBAAsB,IAAI;AAAA,EACnE;AACA,MAAI,CAAC,KAAK,KAAK,EAAG,QAAO;AACzB,MAAI,KAAK,WAAW,CAAC,MAAM,MAAQ,QAAO;AAC1C,MAAI;AACF,UAAM,IAAa,KAAK,MAAM,IAAI;AAClC,WAAO,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,IAAI,SAAY;AAAA,EACvE,SAAS,GAAG;AAEV,UAAM,IAAK,EAAY;AACvB,UAAM,MAAM,sBAAsB,KAAK,CAAC,KAAK,eAAe,KAAK,CAAC,KAAK,CAAC;AACxE,WAAO,oBAAoB,KAAK,OAAO,EAAE,KAAK,EAAE;AAAA,EAClD;AACF;AAKO,SAAS,qBAAqB,OAAe,WAAW,GAAS;AACtE,QAAM,UAAU,cAAc,IAAI;AAClC,MAAI;AACF,UAAM,IAAI;AAAA,MACR,GAAG,MAAM,IAAI,CAAC,IAAI,OAAO;AAAA,IAC3B;AACJ;AAEO,SAAS,YAAY,GAAW,OAAe,WAAW,GAAS;AACxE,uBAAqB,IAAI;AACzB,EAAAC,WAAUC,OAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC9D,EAAAC,eAAc,MAAM,GAAG,KAAK,UAAU,GAAG,MAAM,CAAC,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AACtE,MAAI,QAAQ,aAAa,WAAWC,YAAW,IAAI,GAAG;AACpD,QAAI;AACF,gBAAU,MAAM,GAAK;AAAA,IACvB,QAAQ;AAAA,IAAC;AAAA,EACX;AACF;AAKO,SAAS,YAAY,GAAW,UAA0B;AAC/D,MAAI,EAAE,QAAS,QAAO,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,EAAE;AACnD,SAAO,SAAS,QAAQ,wBAAwB,EAAE;AACpD;;;ACrHA,SAAS,mBAAmB;AAE5B,IAAM,WAAW;AACjB,IAAI,WAAW;AACf,IAAI,WAAqB,CAAC;AAE1B,SAAS,WAAW,GAAmB;AACrC,MAAI,MAAM;AACV,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,UAAM,SAAS,IAAI,EAAE,IAAI;AACzB,QAAI,KAAK,MAAM,IAAI,EAAE;AAAA,EACvB;AACA,SAAO;AACT;AAEO,SAAS,KAAK,MAAc,KAAK,IAAI,GAAW;AACrD,MAAI,QAAQ,UAAU;AAEpB,aAAS,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AAC7C,YAAM,IAAK,SAAS,CAAC,IAAe;AACpC,UAAI,IAAI,IAAI;AACV,iBAAS,CAAC,IAAI;AACd;AAAA,MACF;AACA,eAAS,CAAC,IAAI;AAAA,IAChB;AAAA,EACF,OAAO;AACL,eAAW;AACX,eAAW,MAAM,KAAK,YAAY,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;AAAA,EACtD;AACA,SAAO,WAAW,GAAG,IAAI,SAAS,IAAI,CAAC,MAAM,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE;AACnE;;;ACtBO,SAAS,UAAU,GAAiB,KAA0B;AACnE,QAAM,MAAM,IAAI,OAAO,KAAK,IAAI;AAChC,QAAM,UAA4B,EAAE,MAAM,IAAI,QAAQ,MAAM,OAAO,EAAE,aAAa;AAClF,MAAI,IAAI,QAAQ,QAAS,SAAQ,UAAU,IAAI,QAAQ;AACvD,SAAO;AAAA,IACL,UAAU;AAAA,IACV,IAAI,KAAK,GAAG;AAAA,IACZ,MAAM,EAAE;AAAA,IACR,MAAM,IAAI,KAAK,GAAG,EAAE,YAAY;AAAA,IAChC,MAAM;AAAA,IACN;AAAA,IACA,SAAS,IAAI;AAAA,IACb,SAAS,EAAE,UAAU,CAAC,GAAG,KAAK,MAAM;AAAA,IACpC,MAAM,EAAE;AAAA,EACV;AACF;;;ACtBA,SAAS,eAAAC,oBAAmB;AAC5B;AAAA,EACE;AAAA,EACA,aAAAC;AAAA,EACA,cAAAC;AAAA,EACA,aAAAC;AAAA,EACA,YAAAC;AAAA,EACA,eAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,YAAAC;AAAA,EACA;AAAA,EACA,YAAAC;AAAA,EACA,cAAAC;AAAA,EACA,iBAAAC;AAAA,OACK;AACP,OAAOC,YAAU;AASjB,IAAM,aAAa,OAAO;AAC1B,IAAM,WAAW,IAAI,OAAO;AAErB,IAAM,SAAN,MAAa;AAAA,EAClB,YAAqB,MAAc;AAAd;AAAA,EAAe;AAAA,EAAf;AAAA,EAErB,IAAI,SAAyB;AAC3B,WAAOC,OAAK,KAAK,KAAK,MAAM,UAAU,OAAO,OAAO,CAAC;AAAA,EACvD;AAAA,EACA,KAAK,KAAwB;AAC3B,WAAOA,OAAK,KAAK,KAAK,IAAI,IAAI,OAAO,GAAG,GAAG,OAAO,IAAI,OAAO,CAAC,QAAQ;AAAA,EACxE;AAAA,EACQ,QAAQ,KAAwB;AACtC,WAAOA,OAAK,KAAK,KAAK,IAAI,IAAI,OAAO,GAAG,GAAG,OAAO,IAAI,OAAO,CAAC,MAAM;AAAA,EACtE;AAAA;AAAA;AAAA,EAIA,QAAQ,KAAwB;AAC9B,UAAM,IAAI,KAAK,QAAQ,GAAG;AAC1B,QAAI,IAAI;AACR,QAAI;AACF,YAAM,IAAIC,cAAa,GAAG,MAAM,EAAE,KAAK;AACvC,UAAI,QAAQ,KAAK,CAAC,EAAG,KAAI,OAAO,CAAC;AAAA,IACnC,QAAQ;AAAA,IAAC;AAGT,QAAI,IAAI,EAAG,KAAI,KAAK,cAAc,GAAG,IAAI;AACzC,IAAAC,WAAUF,OAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3D,IAAAG,eAAc,GAAG,OAAO,IAAI,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AAC/C,WAAO;AAAA,EACT;AAAA,EAEQ,cAAc,KAAwB;AAC5C,UAAM,IAAI,KAAK,KAAK,GAAG;AACvB,QAAI,CAACC,YAAW,CAAC,EAAG,QAAO;AAC3B,UAAM,QAAQH,cAAa,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI;AACvD,aAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,UAAI;AACF,cAAM,IAAI,KAAK,MAAM,MAAM,CAAC,CAAW;AACvC,YAAI,OAAO,EAAE,SAAS,QAAQ,SAAU,QAAO,EAAE,QAAQ;AAAA,MAC3D,QAAQ;AAAA,MAAC;AAAA,IACX;AACA,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,KAAgB,QAAuB;AAC5C,QAAI,CAAC,OAAO,OAAQ;AACpB,IAAAC,WAAU,KAAK,IAAI,IAAI,OAAO,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACjE,mBAAe,KAAK,KAAK,GAAG,GAAG,GAAG,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AAAA,EACxG;AAAA;AAAA,EAGA,WAAwB;AACtB,UAAM,MAA2C,CAAC;AAClD,UAAM,OAAOF,OAAK,KAAK,KAAK,MAAM,QAAQ;AAC1C,QAAI,CAACI,YAAW,IAAI,EAAG,QAAO,CAAC;AAC/B,eAAW,KAAKC,aAAY,IAAI,GAAG;AACjC,YAAM,IAAIL,OAAK,KAAK,MAAM,CAAC;AAC3B,UAAI,QAAkB,CAAC;AACvB,UAAI;AACF,gBAAQK,aAAY,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,QAAQ,CAAC;AAAA,MAC3D,QAAQ;AACN;AAAA,MACF;AACA,iBAAW,KAAK,OAAO;AACrB,YAAI;AACF,cAAI,KAAK,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,OAAOC,UAASN,OAAK,KAAK,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC;AAAA,QACrG,QAAQ;AAAA,QAAC;AAAA,MACX;AAAA,IACF;AACA,WAAO,IAAI,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,KAAK,KAAgB,MAAc,YAAY,KAA0C;AACvF,UAAM,IAAI,KAAK,KAAK,GAAG;AACvB,QAAI,CAACI,YAAW,CAAC,EAAG,QAAO,EAAE,QAAQ,CAAC,GAAG,QAAQ,KAAK;AACtD,UAAM,OAAOE,UAAS,CAAC,EAAE;AACzB,QAAI,QAAQ,KAAM,QAAO,EAAE,QAAQ,CAAC,GAAG,QAAQ,KAAK;AACpD,UAAM,KAAKC,UAAS,GAAG,GAAG;AAC1B,QAAI;AACF,YAAM,SAAkB,CAAC;AACzB,UAAI,SAAS;AACb,UAAI,QAAQ;AACZ,aAAO,OAAO,SAAS,aAAa,SAAS,MAAM;AACjD,cAAM,MAAM,OAAO,MAAM,KAAK,IAAI,OAAO,QAAQ,KAAK,CAAC;AACvD,cAAM,IAAIC,UAAS,IAAI,KAAK,GAAG,IAAI,QAAQ,MAAM;AACjD,cAAM,OAAO,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,MAAM;AAC/C,YAAI,MAAM;AACV,YAAI,OAAO;AACX,eAAO,OAAO,SAAS,WAAW;AAChC,gBAAM,KAAK,KAAK,QAAQ,MAAM,GAAG;AACjC,cAAI,KAAK,EAAG;AACZ,gBAAM,OAAO,KAAK,MAAM,KAAK,EAAE;AAC/B,gBAAM,KAAK;AACX,kBAAQ,OAAO,WAAW,MAAM,MAAM,IAAI;AAC1C,cAAI,CAAC,KAAK,KAAK,EAAG;AAClB,cAAI;AACF,mBAAO,KAAK,KAAK,MAAM,IAAI,CAAU;AAAA,UACvC,QAAQ;AAAA,UAAC;AAAA,QACX;AACA,kBAAU;AACV,YAAI,KAAM;AAEV,YAAI,SAAS,KAAK,QAAQ,SAAS,SAAU;AAC7C,iBAAS;AAAA,MACX;AACA,aAAO,EAAE,QAAQ,OAAO;AAAA,IAC1B,UAAE;AACA,MAAAC,WAAU,EAAE;AAAA,IACd;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,MAAc,MAAM,KAAK,IAAI,GAAW;AAC5C,QAAI,UAA+B;AACnC,QAAI;AACF,gBAAU,cAAc,KAAK,MAAM,OAAO;AAAA,IAC5C,QAAQ;AAAA,IAAC;AACT,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI,IAAI;AACR,QAAI;AACF,YAAM,SAAS,MAAM,OAAO;AAC5B,YAAM,OAAO,KAAK,SAAS,EAAE,OAAO,CAAC,QAAQ;AAC3C,YAAI;AACF,iBAAOH,UAAS,KAAK,KAAK,GAAG,CAAC,EAAE,UAAU;AAAA,QAC5C,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF,CAAC;AAKD,WAAK,iBAAiB,IAAI,IAAI,KAAK,IAAI,CAAC,QAAQ,GAAG,IAAI,OAAO,IAAI,IAAI,OAAO,EAAE,CAAC,CAAC;AACjF,iBAAW,OAAO,MAAM;AACtB,cAAM,IAAI,KAAK,KAAK,GAAG;AACvB,YAAI;AACF,cAAIA,UAAS,CAAC,EAAE,UAAU,QAAQ;AAChC,YAAAI,YAAW,CAAC;AACZ;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAAC;AAAA,MACX;AAAA,IACF,QAAQ;AAAA,IACR,UAAE;AACA,cAAQ;AAAA,IACV;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,MAAyB;AAChD,QAAI,QAAkB,CAAC;AACvB,QAAI;AACF,cAAQL,aAAYL,OAAK,KAAK,KAAK,MAAM,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AAAA,IACxF,QAAQ;AAAA,IAAC;AACT,UAAM,QAAQ,CAAC,MACb,OAAO,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM;AAC3B,UAAI,KAAK,IAAI,CAAC,EAAG,QAAO;AACxB,YAAM,IAAI,EAAE,QAAQ,GAAG;AACvB,aAAO,CAACI,YAAW,KAAK,KAAK,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,EAAE,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC;AAAA,IACnF,CAAC;AACH,eAAW,KAAK,OAAO;AACrB,YAAM,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAE,MAAM,GAAG,EAAE,CAAC;AAC/C,YAAM,IAAI,EAAE,KAAK;AACjB,YAAM,OAAO,MAAM,CAAC;AACpB,UAAI,CAAC,KAAK,OAAQ;AAClB,iBAAW,KAAK,KAAM,QAAO,EAAE,CAAC;AAChC,QAAE,MAAM,CAAC;AAAA,IACX;AAAA,EACF;AAAA,EAEA,OAAO,KAAsB;AAK3B,SAAK,YAAY,GAAG;AACpB,eAAW,KAAK,CAAC,KAAK,KAAK,GAAG,GAAG,KAAK,QAAQ,GAAG,CAAC,GAAG;AACnD,UAAI;AACF,QAAAM,YAAW,CAAC;AAAA,MACd,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,YAAY,KAAsB;AACxC,UAAM,OAAO,oBAAI,IAAI,CAAC,GAAG,IAAI,OAAO,IAAI,IAAI,OAAO,IAAI,GAAG,OAAO,IAAI,OAAO,CAAC,IAAI,OAAO,IAAI,OAAO,CAAC,EAAE,CAAC;AACvG,QAAI,QAAkB,CAAC;AACvB,QAAI;AACF,cAAQL,aAAYL,OAAK,KAAK,KAAK,MAAM,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AAAA,IACxF,QAAQ;AACN;AAAA,IACF;AACA,eAAW,KAAK,OAAO;AACrB,YAAM,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAE,MAAM,GAAG,EAAE,CAAC;AAC/C,YAAM,IAAI,EAAE,KAAK;AACjB,YAAM,MAAM,OAAO,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC;AACpD,UAAI,CAAC,IAAI,OAAQ;AACjB,iBAAW,KAAK,IAAK,QAAO,EAAE,CAAC;AAC/B,QAAE,MAAM,CAAC;AAAA,IACX;AAAA,EACF;AACF;AAGO,IAAM,UAAN,MAAc;AAAA,EACnB,YACW,MACA,MACT;AAFS;AACA;AAAA,EACR;AAAA,EAFQ;AAAA,EACA;AAAA,EAEH,OAAe;AACrB,WAAOA,OAAK,KAAK,KAAK,MAAM,WAAW,GAAG,OAAO,KAAK,IAAI,CAAC,OAAO;AAAA,EACpE;AAAA;AAAA;AAAA,EAGA,OAA+B;AAC7B,QAAI;AACF,YAAM,IAAI,KAAK,MAAMC,cAAa,KAAK,KAAK,GAAG,MAAM,CAAC;AACtD,aAAO,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,IAAK,IAA+B,CAAC;AAAA,IAC5F,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EACA,MAAM,GAAiC;AACrC,UAAM,IAAI,KAAK,KAAK;AACpB,IAAAC,WAAUF,OAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3D,UAAM,MAAM,GAAG,CAAC,IAAI,QAAQ,GAAG;AAC/B,IAAAG,eAAc,KAAK,KAAK,UAAU,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AACrD,eAAW,KAAK,CAAC;AAAA,EACnB;AAAA,EACA,IAAI,KAAwB;AAC1B,WAAO,GAAG,IAAI,OAAO,IAAI,IAAI,OAAO;AAAA,EACtC;AACF;AAIO,SAAS,SAAS,MAAc,KAAgB,UAAU,IAAI,KAA6B;AAChG,SAAO,cAAc,MAAM,GAAG,OAAO,IAAI,OAAO,CAAC,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,OAAO;AACrF;AAUO,SAAS,cAAc,MAAc,MAAc,UAAU,IAAI,KAA6B;AACnG,QAAM,MAAMH,OAAK,KAAK,MAAM,OAAO;AACnC,EAAAE,WAAU,KAAK,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC/C,QAAM,IAAIF,OAAK,KAAK,KAAK,GAAG,IAAI,OAAO;AACvC,QAAM,QAAQW,aAAY,CAAC,EAAE,SAAS,KAAK;AAC3C,QAAM,OAAO,GAAG,QAAQ,GAAG,IAAI,KAAK;AACpC,MAAI,OAAsB;AAC1B,MAAI;AACF,WAAOV,cAAa,GAAG,MAAM;AAC7B,UAAM,QAAQ,KAAK,IAAI,IAAIK,UAAS,CAAC,EAAE,UAAU,WAAW,CAAC,aAAa,OAAO,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC;AACpG,QAAI,CAAC,MAAO,QAAO;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,SAAS,MAAM;AACjB,UAAM,QAAQ,GAAG,CAAC,IAAI,QAAQ,GAAG,IAAI,KAAK;AAC1C,QAAI;AACF,iBAAW,GAAG,KAAK;AAAA,IACrB,QAAQ;AACN,aAAO;AAAA,IACT;AAEA,UAAM,QAAQL,cAAa,OAAO,MAAM;AACxC,IAAAS,YAAW,KAAK;AAChB,QAAI,UAAU,MAAM;AAClB,UAAI;AACF,QAAAP,eAAc,GAAG,OAAO,EAAE,MAAM,KAAK,CAAC;AAAA,MACxC,QAAQ;AAAA,MAAC;AACT,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI;AACF,IAAAA,eAAc,GAAG,MAAM,EAAE,MAAM,KAAK,CAAC;AAAA,EACvC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,MAAM;AACX,QAAI;AACF,UAAIF,cAAa,GAAG,MAAM,MAAM,KAAM,CAAAS,YAAW,CAAC;AAAA,IACpD,QAAQ;AAAA,IAAC;AAAA,EACX;AACF;AAoBO,SAAS,aAAa,KAAsB;AACjD,MAAI,CAAC,OAAO,UAAU,GAAG,KAAK,OAAO,EAAG,QAAO;AAC/C,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,SAAS,GAAG;AACV,WAAQ,EAA4B,SAAS;AAAA,EAC/C;AACF;;;AClXA,SAAS,cAAAE,mBAAkB;AAEpB,IAAM,cAAc;AAE3B,IAAM,QAAQ;AAYd,IAAM,QACJ;AAEF,SAAS,WAAW,GAAoB;AACtC,QAAM,SAAS,EAAE,QAAQ,OAAO,EAAE;AAClC,MAAI,OAAO,SAAS,KAAK,OAAO,SAAS,GAAI,QAAO;AAEpD,MAAI,CAAC,WAAW,KAAK,CAAC,EAAG,QAAO;AAEhC,MAAI,CAAC,UAAU,KAAK,CAAC,EAAG,QAAO;AAC/B,SAAO;AACT;AAGO,SAAS,UAAU,MAAc,MAAuB;AAC7D,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,MAAM,KAAK,QAAQ,OAAO,SAAS;AACvC,QAAM,IAAI,QAAQ,OAAO,CAAC,GAAG,SAAmB,CAAC,QAAQ,WAAW,CAAC,IAAI,YAAY,CAAE;AACvF,MAAI,MAAM;AACR,UAAM,IAAI,KAAK,QAAQ,uBAAuB,MAAM;AACpD,UAAM,IAAI,QAAQ,IAAI,OAAO,oBAAoB,CAAC,sBAAsB,GAAG,GAAG,GAAG;AAAA,EACnF;AACA,SAAO;AACT;AAGO,SAAS,YAAY,MAAsB;AAChD,SAAOA,YAAW,QAAQ,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC;AACnE;AAGO,SAAS,cAAiB,GAAM,MAAkB;AACvD,MAAI,OAAO,MAAM,SAAU,QAAO,UAAU,GAAG,IAAI;AACnD,MAAI,MAAM,QAAQ,CAAC,EAAG,QAAO,EAAE,IAAI,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AAChE,MAAI,KAAK,OAAO,MAAM,UAAU;AAC9B,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,CAA4B,EAAG,KAAI,CAAC,IAAI,cAAc,GAAG,IAAI;AACjG,WAAO;AAAA,EACT;AACA,SAAO;AACT;;;AClDO,IAAM,kBAAkB;AACxB,IAAM,WAAW;AAIxB,IAAM,iBAA2B;AAAA,EAC/B;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAEA,IAAM,kBAAkB;AAExB,IAAM,cAAc;AAGpB,IAAM,cACJ;AAKF,IAAM,WAAW;AACjB,IAAM,QAAQ;AAEd,SAAS,kBAAkB,MAAsB;AAC/C,MAAI,MAAM;AACV,MAAI,OAAO;AACX,WAAS,YAAY;AACrB,WAAS,IAAI,SAAS,KAAK,IAAI,GAAG,GAAG,IAAI,SAAS,KAAK,IAAI,GAAG;AAC5D,UAAM,OAAO,EAAE,CAAC;AAChB,QAAI,CAAC,aAAa,IAAI,EAAG;AACzB,UAAM,YAAY,SAAS;AAC3B,UAAM,IAAI,MAAM,KAAK,IAAI;AACzB,QAAI,CAAC,EAAG;AACR,UAAM,QAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC;AAC1C,aAAS,YAAY,MAAM;AAC3B,QAAI,cAAc,KAAK,KAAK,MAAM,SAAS,QAAQ,EAAG;AAEtD,QAAI,YAAY,KAAK,IAAI,KAAK,0BAA0B,KAAK,KAAK,EAAG;AACrE,UAAM,IAAI,EAAE,CAAC,KAAK,OAAO,MAAM,EAAE,CAAC,KAAK,OAAO,MAAM,EAAE,CAAC,KAAK,OAAO,MAAM;AACzE,WAAO,KAAK,MAAM,MAAM,EAAE,KAAK,IAAI,IAAI,WAAW;AAClD,WAAO,MAAM;AAAA,EACf;AACA,SAAO,OAAO,MAAM,KAAK,MAAM,IAAI,IAAI;AACzC;AAKO,SAAS,aAAa,MAAuB;AAClD,MAAI,mBAAmB,KAAK,IAAI,EAAG,QAAO;AAC1C,QAAM,OAAO,KAAK,YAAY,EAAE,QAAQ,SAAS,EAAE;AACnD,SAAO,6KAA6K;AAAA,IAClL;AAAA,EACF;AACF;AAKA,IAAM,QAAQ,IAAI;AAAA,EAChB,OAAO,WAAW;AAAA,EAClB;AACF;AAIA,SAAS,cAAc,GAAoB;AACzC,QAAM,IAAI,EAAE,QAAQ,cAAc,EAAE;AACpC,MAAI,CAAC,EAAG,QAAO;AACf,MACE,4KAA4K;AAAA,IAC1K;AAAA,EACF;AAEA,WAAO;AACT,MAAI,oBAAoB,KAAK,CAAC,KAAK,QAAQ,KAAK,CAAC,KAAK,WAAW,KAAK,CAAC,KAAK,cAAc,KAAK,CAAC,EAAG,QAAO;AAC1G,MAAI,UAAU,KAAK,CAAC,EAAG,QAAO;AAC9B,MACE,6KAA6K;AAAA,IAC3K;AAAA,EACF;AAEA,WAAO;AACT,MAAI,4CAA4C,KAAK,CAAC,EAAG,QAAO;AAChE,MAAI,MAAM,KAAK,CAAC,EAAG,QAAO;AAC1B,MAAI,2CAA2C,KAAK,CAAC,EAAG,QAAO;AAC/D,SAAO;AACT;AAGA,SAAS,YAAY,GAAoB;AACvC,QAAM,IAAI,EAAE,QAAQ,eAAe,EAAE;AACrC,MAAI,EAAE,SAAS,EAAG,QAAO;AACzB,MAAI,cAAc,CAAC,EAAG,QAAO;AAC7B,MAAI,gBAAgB,KAAK,CAAC,EAAG,QAAO;AACpC,MAAI,0BAA0B,KAAK,CAAC,EAAG,QAAO;AAC9C,MAAI,gBAAgB,KAAK,CAAC,EAAG,QAAO;AACpC,MAAI,4BAA4B,KAAK,CAAC,EAAG,QAAO;AAChD,MAAI,kCAAkC,KAAK,CAAC,EAAG,QAAO;AACtD,QAAM,QAAQ,CAAC,SAAS,SAAS,SAAS,cAAc,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE;AACnF,SAAO,SAAS,KAAM,QAAQ,KAAK,CAAC,KAAK,WAAW,KAAK,CAAC;AAC5D;AAGO,SAAS,cAAc,MAAsB;AAClD,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,MAAM;AACV,aAAW,MAAM,eAAgB,OAAM,IAAI,QAAQ,IAAI,QAAQ;AAC/D,QAAM,IAAI,QAAQ,iBAAiB,CAAC,IAAI,MAAc,KAAa,OAAe,GAAG,IAAI,GAAG,QAAQ,GAAG,EAAE,EAAE;AAC3G,QAAM,IAAI,QAAQ,aAAa,CAAC,IAAI,SAAiB,GAAG,IAAI,GAAG,QAAQ,EAAE;AACzE,QAAM,kBAAkB,GAAG;AAC3B,QAAM,IAAI;AAAA,IAAQ;AAAA,IAAO,CAAC,GAAG,MAAc,GAAW,MACpD,CAAC,YAAY,CAAC,KAAK,EAAE,SAAS,QAAQ,IAAI,IAAI,GAAG,IAAI,GAAG,CAAC,GAAG,QAAQ,GAAG,CAAC;AAAA,EAC1E;AACA,SAAO;AACT;AAQO,SAAS,WAAc,GAAS;AACrC,MAAI,OAAO,MAAM,SAAU,QAAO,cAAc,CAAC;AACjD,MAAI,MAAM,QAAQ,CAAC,EAAG,QAAO,EAAE,IAAI,UAAU;AAC7C,MAAI,KAAK,OAAO,MAAM,UAAU;AAC9B,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,CAA4B,EAAG,KAAI,CAAC,IAAI,WAAW,CAAC;AACxF,WAAO;AAAA,EACT;AACA,SAAO;AACT;;;ACpKO,IAAM,YAAkC;AAAA,EAC7C,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,QAAQ;AACV;AAGA,IAAM,aAA8D;AAAA,EAClE,mBAAmB,EAAE,GAAG,CAAC,QAAQ,EAAE;AAAA,EACnC,qBAAqB,EAAE,GAAG,CAAC,EAAE;AAAA,EAC7B,iBAAiB,EAAE,GAAG,CAAC,QAAQ,EAAE;AAAA,EACjC,oBAAoB,EAAE,GAAG,CAAC,cAAc,WAAW,OAAO,EAAE;AAAA,EAC5D,qBAAqB,EAAE,GAAG,CAAC,EAAE;AAAA,EAC7B,gBAAgB,EAAE,GAAG,CAAC,WAAW,gBAAgB,UAAU,GAAG,GAAG,CAAC,cAAc,EAAE;AAAA,EAClF,cAAc,EAAE,GAAG,CAAC,WAAW,UAAU,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,EAAE;AAAA,EAC3E,mBAAmB,EAAE,GAAG,CAAC,QAAQ,QAAQ,aAAa,MAAM,UAAU,EAAE;AAAA,EACxE,iBAAiB,EAAE,GAAG,CAAC,QAAQ,QAAQ,aAAa,MAAM,UAAU,EAAE;AAAA,EACtE,gBAAgB,EAAE,GAAG,CAAC,QAAQ,WAAW,WAAW,UAAU,GAAG,GAAG,CAAC,OAAO,EAAE;AAAA,EAC9E,cAAc,EAAE,GAAG,CAAC,QAAQ,WAAW,WAAW,YAAY,MAAM,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,QAAQ,EAAE;AAAA,EAC3G,iBAAiB,EAAE,GAAG,CAAC,OAAO,EAAE;AAAA,EAChC,oBAAoB,EAAE,GAAG,CAAC,gBAAgB,MAAM,GAAG,GAAG,CAAC,QAAQ,WAAW,UAAU,EAAE;AAAA,EACtF,qBAAqB,EAAE,GAAG,CAAC,gBAAgB,KAAK,EAAE;AAAA,EAClD,oBAAoB,EAAE,GAAG,CAAC,YAAY,YAAY,EAAE;AAAA,EACpD,kBAAkB,EAAE,GAAG,CAAC,YAAY,cAAc,YAAY,EAAE;AAAA,EAChE,qBAAqB,EAAE,GAAG,CAAC,SAAS,EAAE;AAAA,EACtC,mBAAmB,EAAE,GAAG,CAAC,WAAW,QAAQ,YAAY,EAAE;AAAA,EAC1D,sBAAsB,EAAE,GAAG,CAAC,UAAU,OAAO,WAAW,IAAI,GAAG,GAAG,CAAC,MAAM,EAAE;AAAA,EAC3E,oBAAoB,EAAE,GAAG,CAAC,UAAU,MAAM,MAAM,MAAM,SAAS,SAAS,GAAG,GAAG,CAAC,OAAO,EAAE;AAC1F;AAEA,IAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAM,WAAW;AACjB,IAAM,SAAS,KAAK;AAEpB,SAAS,YAAY,MAAc,MAA6B;AAC9D,QAAM,QAAQ,WAAW,IAAI;AAC7B,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,MAAgB,CAAC;AACvB,WAAS,IAAI,GAAG,KAAK,MAAM,IAAK,YAAW,KAAK,MAAM,CAAS,KAAK,CAAC,EAAG,KAAI,KAAK,CAAC;AAClF,SAAO;AACT;AAEA,SAAS,IAAI,GAAY,KAAsB;AAC7C,MAAI,OAAO,MAAM,SAAU,QAAO,EAAE,SAAS,MAAM,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,WAAM;AAC3E,MAAI,KAAK,OAAO,MAAM,UAAU;AAC9B,UAAM,IAAI,KAAK,UAAU,CAAC;AAC1B,WAAO,EAAE,SAAS,MAAM,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,WAAM;AAAA,EAClD;AACA,SAAO;AACT;AAGO,SAAS,QAAQ,GAAW,KAAqB;AACtD,SAAO,EAAE,SAAS,MAAM,SAAI,EAAE,MAAM,EAAE,SAAS,MAAM,CAAC,CAAC,KAAK;AAC9D;AAQO,SAAS,YAAY,OAAc,MAAY,KAAc,OAAsB,CAAC,GAAiB;AAC1G,QAAM,UAAU,UAAU,MAAM,IAAI;AACpC,MAAI,YAAY,UAAa,UAAU,KAAM,QAAO;AAEpD,QAAM,UAAmC,CAAC;AAC1C,aAAW,KAAK;AACd,QAAI,MAAM,QAAQ,CAA+B,MAAM;AACrD,cAAQ,CAAC,IAAI,MAAM,QAAQ,CAA+B;AAE9D,MAAI,OAAgC,CAAC;AACrC,QAAM,OAAO,YAAY,MAAM,MAAM,IAAI;AACzC,MAAI,SAAS,KAAM,QAAO,EAAE,GAAG,MAAM,KAAK;AAAA,MACrC,YAAW,KAAK,KAAM,KAAI,MAAM,KAAK,CAAC,MAAM,OAAW,MAAK,CAAC,IAAI,MAAM,KAAK,CAAC;AAClF,MAAI,OAAO,KAAK,SAAS,SAAU,MAAK,OAAO,IAAI,KAAK,MAAM,QAAQ;AAEtE,MAAI,OAAO,KAAK,cAAc;AAC5B,SAAK,YAAY,MAAM,SAAS,kBAAkB,QAAQ,KAAK,WAAW,QAAQ,IAAI,IAAI,KAAK,WAAW,QAAQ;AACpH,MAAI,KAAK,UAAU,OAAW,MAAK,QAAQ,IAAI,KAAK,OAAO,MAAM;AACjE,MAAI,KAAK,WAAW,OAAW,MAAK,SAAS,IAAI,KAAK,QAAQ,MAAM;AACpE,MAAI,OAAO,KAAK,YAAY,SAAU,MAAK,UAAU,IAAI,KAAK,SAAS,GAAG;AAG1E,MAAI,MAAM,SAAS,wBAAwB,OAAO,KAAK,SAAS,SAAU,MAAK,OAAO,IAAI,KAAK,MAAM,GAAG;AACxG,MAAI,MAAM,SAAS,sBAAsB,OAAO,KAAK,UAAU,SAAU,MAAK,QAAQ,IAAI,KAAK,OAAO,QAAQ;AAE9G,QAAM,WAAqB,CAAC;AAC5B,MAAI,MAAa,EAAE,GAAG,OAAO,MAAM,SAAsC,MAAM,SAAS,EAAE,UAAU,IAAI,EAAE;AAC1G,MAAI,OAAO,GAAG;AAEZ,UAAM,EAAE,GAAG,KAAK,SAAS,WAAW,IAAI,OAAO,GAAG,MAAM,WAAW,IAAI,IAAI,EAAE;AAC7E,aAAS,KAAK,eAAe;AAAA,EAC/B;AACA,MAAI,KAAK;AACP,UAAM,IAAI,cAAc,IAAI,SAAS,KAAK,IAAI;AAC9C,QAAI,OAAO,EAAE,YAAY,SAAU,GAAE,UAAU,YAAY,EAAE,OAAO;AACpE,WAAO,EAAE;AACT,UAAM,IAAI,cAAc,IAAI,MAAM,KAAK,IAAI;AAG3C,WAAO,EAAE;AACT,UAAM,EAAE,GAAG,KAAK,SAAS,GAAuB,MAAM,EAAE;AACxD,aAAS,KAAK,WAAW;AAAA,EAC3B;AACA,MAAI,UAAU,EAAE,UAAU,IAAI;AAC9B,SAAO;AACT;;;ACjJA,SAAS,cAAAC,mBAAkB;AAEpB,SAAS,QAAQ,SAAiB,IAAoB;AAC3D,SAAOA,YAAW,QAAQ,EAAE,OAAO,uBAAuB,OAAO,IAAI,EAAE,EAAE,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC;AACrG;;;ACRA,SAAS,kBAAAC,iBAAgB,aAAAC,kBAAiB;AAC1C,OAAOC,YAAU;AAIV,IAAM,WAAN,MAA+B;AAAA,EACpC,YACW,MACA,MACA,MACA,KACT;AAJS;AACA;AACA;AACA;AAAA,EACR;AAAA,EAJQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEX,MAAM,KAAK,QAA0C;AACnD,QAAI;AACF,MAAAD,WAAUC,OAAK,QAAQ,KAAK,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACtD,MAAAF,gBAAe,KAAK,MAAM,GAAG,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,CAAI;AAChF,aAAO,EAAE,IAAI,MAAM,cAAc,OAAO,QAAQ,UAAU,OAAO,QAAQ,YAAY,GAAG,UAAU,CAAC,EAAE;AAAA,IACvG,SAAS,GAAG;AACV,aAAO,EAAE,IAAI,OAAO,cAAc,GAAG,OAAO,OAAQ,EAAY,OAAO,GAAG,QAAQ,QAAQ;AAAA,IAC5F;AAAA,EACF;AACF;;;ACjBO,IAAM,aAAN,MAAiC;AAAA,EACtC,YACW,MACA,MACA,KACQ,QAAgC,CAAC,MAAM,QAAQ,OAAO,MAAM,GAAG,CAAC;AAAA,CAAI,GACrF;AAJS;AACA;AACA;AACQ;AAAA,EAChB;AAAA,EAJQ;AAAA,EACA;AAAA,EACA;AAAA,EACQ;AAAA,EAEnB,MAAM,KAAK,QAA0C;AACnD,QAAI;AACF,iBAAW,KAAK,OAAQ,MAAK,MAAM,KAAK,UAAU,CAAC,CAAC;AACpD,aAAO,EAAE,IAAI,MAAM,cAAc,OAAO,QAAQ,UAAU,OAAO,QAAQ,YAAY,GAAG,UAAU,CAAC,EAAE;AAAA,IACvG,SAAS,GAAG;AAEV,aAAO,EAAE,IAAI,OAAO,cAAc,GAAG,OAAO,OAAQ,EAAY,WAAW,CAAC,GAAG,QAAQ,QAAQ;AAAA,IACjG;AAAA,EACF;AACF;;;ACTA,SAAS,aAAAG,YAAW,aAAAC,YAAW,gBAAAC,gBAAc,cAAAC,aAAY,iBAAAC,sBAAqB;AAC9E,OAAOC,YAAU;;;ACFjB,SAAS,iBAAiB;AAC1B,SAAS,cAAAC,aAAY,eAAAC,oBAAmB;AAMxC,IAAM,UAAU;AAGT,IAAM,gBAAwB,CAAC,KAAK,MAAM,UAAU;AACzD,QAAM,IAAI,UAAU,KAAK,MAAM;AAAA,IAC7B,OAAO,SAAS;AAAA,IAChB,UAAU;AAAA,IACV,SAAS;AAAA,IACT,OAAO,CAAC,QAAQ,QAAQ,QAAQ;AAAA,EAClC,CAAC;AACD,SAAO,EAAE,QAAQ,EAAE,QAAQ,OAAO,EAAE,QAAQ,QAAQ,EAAE,UAAU,GAAG;AACrE;AAKO,IAAM,cAAc,CAAC,MAAc,OAA2B,YAAoB;AACvF,QAAM,QAAQ,QAAQ,IAAI,qBACtB,IAAIC,YAAW,QAAQ,EAAE,OAAO,WAAW,CAAC,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,KACvE;AACJ,SAAO,QAAQ,IAAI,IAAI,IAAI,GAAG,KAAK;AACrC;AASO,SAAS,UAAU,OAAoB,SAAiB,MAAc,eAA8B;AACzG,QAAM,IACJ,UAAU,aACN,IAAI,YAAY,CAAC,yBAAyB,MAAM,SAAS,MAAM,SAAS,IAAI,CAAC,IAC7E,IAAI,eAAe,CAAC,UAAU,WAAW,SAAS,WAAW,OAAO,CAAC;AAC3E,MAAI,EAAE,WAAW,EAAG,QAAO;AAC3B,QAAM,IAAI,EAAE,OAAO,QAAQ,UAAU,EAAE;AACvC,SAAO,KAAK;AACd;AAoFO,SAAS,gBAAgB,GAAe,MAAc,eAAkC;AAC7F,MAAI,CAAC,EAAE,SAAU,QAAO;AACxB,QAAM,QAAQ,UAAU,EAAE,UAAU,YAAY,EAAE,IAAI,GAAG,GAAG;AAC5D,QAAM,SAAS,UAAU,EAAE,UAAU,YAAY,EAAE,MAAM,QAAQ,GAAG,GAAG;AACvE,MAAI,CAAC,SAAS,CAAC,OAAQ,QAAO;AAC9B,SAAO,EAAE,GAAG,GAAG,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,GAAI,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC,EAAG;AAC5E;;;ACyGO,SAAS,eAAe,GAAiC;AAC9D,QAAM,IAAI;AACV,MAAI,CAAC,KAAK,OAAO,MAAM,YAAa,UAAU,EAAE,UAAU,IAAI,KAAK,EAAE,eAAe,UAAY,QAAO;AACvG,SAAO;AAAA,IACL,YAAY,EAAE;AAAA,IACd,GAAI,OAAO,EAAE,UAAU,YAAY,SAAS,KAAK,EAAE,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAAA,EACpF;AACF;;;AFrOO,IAAM,YAAY,CAAC,eAA+BC,OAAK,KAAK,YAAY,cAAc;AAEtF,SAAS,YAAY,MAAuB;AACjD,MAAI;AACF,UAAM,IAAI,KAAK,MAAMC,eAAa,MAAM,MAAM,CAAC;AAC/C,QAAI,CAAC,KAAK,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,EAAG,QAAO,CAAC;AAC7D,UAAM,MAAe,CAAC;AACtB,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,CAA4B,GAAG;AACjE,YAAM,IAAI;AACV,UAAI,KAAK,OAAO,EAAE,OAAO,YAAY,EAAE,MAAM,OAAO,EAAE,QAAQ;AAC5D,YAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,KAAK,IAAI,OAAO,EAAE,OAAO,WAAW,EAAE,KAAK,IAAI,GAAG,eAAe,CAAC,EAAE;AAAA,IACpG;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAcO,SAAS,aAAa,KAAc,SAAiB,IAA2B;AACrF,QAAM,SAAS,GAAG,OAAO;AACzB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAG,EAAG,KAAI,EAAE,OAAO,MAAM,EAAE,WAAW,MAAM,EAAG,QAAO,EAAE,MAAM,OAAO,MAAM;AAC/G,SAAO;AACT;;;AGzDA,OAAO,SAAS;AAChB,OAAOC,SAAQ;AACf,OAAOC,YAAU;AAiGV,SAAS,WAAW,OAAe,MAAyB,QAAQ,KAAa;AACtF,MAAI,IAAI,mBAAoB,QAAO,IAAI;AACvC,MAAI,QAAQ,aAAa,QAAS,QAAO,oCAAoCD,IAAG,SAAS,EAAE,QAAQ;AACnG,SAAOC,OAAK,KAAK,OAAO,cAAc;AACxC;AAIO,SAAS,IAAI,MAAc,KAAmB,WAA+C;AAClG,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,MAAM;AACV,QAAI,OAAO;AACX,UAAM,IAAI,IAAI,QAAQ,IAAI;AAC1B,UAAM,SAAS,CAAC,MAAyB;AACvC,UAAI,KAAM;AACV,aAAO;AACP,mBAAa,CAAC;AACd,QAAE,QAAQ;AACV,cAAQ,CAAC;AAAA,IACX;AACA,UAAM,IAAI,WAAW,MAAM,OAAO,IAAI,GAAG,SAAS;AAClD,MAAE,GAAG,WAAW,MAAM,EAAE,MAAM,GAAG,KAAK,UAAU,GAAG,CAAC;AAAA,CAAI,CAAC;AACzD,MAAE,GAAG,QAAQ,CAAC,MAAM;AAClB,aAAO,EAAE,SAAS,MAAM;AACxB,YAAM,IAAI,IAAI,QAAQ,IAAI;AAC1B,UAAI,IAAI,EAAG;AACX,UAAI;AACF,eAAO,KAAK,MAAM,IAAI,MAAM,GAAG,CAAC,CAAC,CAAe;AAAA,MAClD,QAAQ;AACN,eAAO,IAAI;AAAA,MACb;AAAA,IACF,CAAC;AACD,MAAE,GAAG,SAAS,MAAM,OAAO,IAAI,CAAC;AAChC,MAAE,GAAG,SAAS,MAAM,OAAO,IAAI,CAAC;AAAA,EAClC,CAAC;AACH;;;AC3HA,SAAS,aAAAC,YAAW,gBAAAC,gBAAc,cAAAC,aAAY,iBAAAC,sBAAqB;AACnE,OAAOC,YAAU;AAIV,IAAM,cAAoC,CAAC,OAAO,QAAQ,MAAM;AAGhE,IAAM,eAAe;AAG5B,IAAM,UAAU,KAAK,KAAK;AAEnB,SAAS,YAAY,GAA4B;AACtD,SAAO,OAAO,MAAM,YAAa,YAAkC,SAAS,CAAC;AAC/E;AAKO,SAAS,UAAU,GAAoE;AAC5F,MAAI,YAAY,EAAE,KAAK,EAAG,QAAO,EAAE;AACnC,MAAI,YAAY,EAAE,cAAc,EAAG,QAAO,EAAE;AAC5C,SAAO;AACT;AAEA,IAAM,UAAU,CAAC,MAAsB,OAAO,EAAE,KAAK,aAAa,WAAW,EAAE,KAAK,WAAW;AAE/F,IAAM,QAAQ,CAAC,MAAiB,UAA2B,SAAS,UAAW,SAAS,UAAU,UAAU;AAG5G,IAAM,SAAS,CAAC,KAAa,UAA0B,GAAG,GAAG,UAAkB,KAAK;AAQ7E,SAAS,SAAS,GAAU,MAAiB,OAA+B,KAAsB;AACvG,MAAI,SAAS,MAAO,QAAO;AAC3B,QAAM,QAAQ,QAAQ,CAAC;AACvB,QAAM,IAAI,KAAK,MAAM,EAAE,IAAI;AAC3B,MAAI,EAAE,SAAS,oBAAoB;AACjC,QAAI,MAAM,MAAM,KAAK,EAAG,OAAM,OAAO,KAAK,KAAK,CAAC,IAAI,OAAO,SAAS,CAAC,IAAI,IAAI;AAC7E,WAAO;AAAA,EACT;AACA,MAAK,EAAE,SAAS,kBAAkB,EAAE,SAAS,gBAAiB,CAAC,MAAM,MAAM,KAAK,EAAG,QAAO;AAC1F,MAAI,EAAE,SAAS,eAAgB,QAAO;AACtC,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,QAAMC,OAAM,OAAO,KAAK,KAAK;AAE7B,QAAM,QAAQ,MAAMA,IAAG;AACvB,MAAI,UAAU,UAAa,KAAK,OAAO;AACrC,WAAO,MAAMA,IAAG;AAChB,UAAM,GAAG,IAAI;AACb,WAAO;AAAA,EACT;AACA,QAAM,OAAO,MAAM,GAAG;AACtB,MAAI,SAAS,UAAa,IAAI,OAAO,gBAAgB,IAAI,OAAO,aAAc,QAAO;AACrF,QAAM,GAAG,IAAI;AACb,SAAO;AACT;AAKO,IAAM,YAAN,MAAgB;AAAA,EACrB,YACW,MACA,MACT;AAFS;AACA;AAAA,EACR;AAAA,EAFQ;AAAA,EACA;AAAA,EAEH,OAAe;AACrB,WAAOC,OAAK,KAAK,KAAK,MAAM,SAAS,GAAG,OAAO,KAAK,IAAI,CAAC,OAAO;AAAA,EAClE;AAAA,EACA,OAA+B;AAC7B,QAAI;AACF,YAAM,IAAI,KAAK,MAAMC,eAAa,KAAK,KAAK,GAAG,MAAM,CAAC;AACtD,aAAO,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,IAAK,IAA+B,CAAC;AAAA,IAC5F,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EACA,MAAM,GAAiC;AACrC,UAAM,SAAS,KAAK,IAAI,GAAG,GAAG,OAAO,OAAO,CAAC,EAAE,OAAO,OAAO,QAAQ,CAAC;AACtE,UAAM,OAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,CAAC,EAAG,KAAI,OAAO,SAAS,CAAC,KAAK,KAAK,SAAS,QAAS,MAAK,CAAC,IAAI;AACnG,UAAM,IAAI,KAAK,KAAK;AACpB,IAAAC,WAAUF,OAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3D,UAAM,MAAM,GAAG,CAAC,IAAI,QAAQ,GAAG;AAC/B,IAAAG,eAAc,KAAK,KAAK,UAAU,IAAI,GAAG,EAAE,MAAM,IAAM,CAAC;AACxD,IAAAC,YAAW,KAAK,CAAC;AAAA,EACnB;AACF;;;AjCxDA,eAAe,WACb,OACA,KACA,SACA,KACA,YACe;AACf,QAAM,OAAO,WAAW,KAAK;AAC7B,MAAI,QAAQ,aAAa,WAAW,CAACC,aAAW,IAAI,EAAG;AAKvD,MAAI,OAAsB;AAC1B,MAAI;AACF,WAAO,4BAAc,SAAS,KAAK,MAAM,IAAI,KAAK,CAAC;AAAA,EACrD,QAAQ;AAAA,EAAC;AACT,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,SAAS,GAAG,IAAI,OAAO,IAAI,OAAO;AAAA,MAClC,OAAO,IAAI;AAAA,MACX,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,MACrB,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,MACnC,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AACF;AAsBO,SAAS,WAAW,OAAe,KAAkB;AAC1D,QAAM,OAAO,QAAQ,KAAK;AAC1B,EAAAC,WAAU,MAAM,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAChD,QAAM,IAAIC,OAAK,KAAK,MAAM,GAAG,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO;AACrG,EAAAC,eAAc,GAAG,KAAK,UAAU,GAAG,GAAG,EAAE,MAAM,IAAM,CAAC;AACrD,SAAO;AACT;AAoBO,SAAS,WAAW,SAAmC;AAC5D,MAAI,CAAC,QAAS;AACd,MAAI;AACF,IAAAC,YAAW,OAAO;AAAA,EACpB,QAAQ;AAAA,EAAC;AACX;AA6DO,SAAS,IAAI,OAAe,MAAoB;AACrD,MAAI;AACF,IAAAC,WAAU,OAAO,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACjD,UAAM,IAAIC,OAAK,KAAK,OAAO,KAAK;AAChC,QAAI;AACF,UAAIC,UAAS,CAAC,EAAE,OAAO,OAAO,KAAM,CAAAC,YAAW,GAAG,GAAG,CAAC,IAAI;AAAA,IAC5D,QAAQ;AAAA,IAAC;AACT,IAAAC,gBAAe,GAAG,IAAG,oBAAI,KAAK,GAAE,YAAY,CAAC,IAAI,IAAI;AAAA,CAAI;AAAA,EAC3D,QAAQ;AAAA,EAAC;AACX;AAGO,SAAS,WAAW,OAA2B;AACpD,QAAM,OAAO,CAAC,GAAW,MAAcH,OAAK,KAAK,OAAO,SAAS,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,CAAC,OAAO;AAC/F,SAAO;AAAA,IACL,IAAI,GAAG,GAAG;AACR,UAAI;AACF,eAAO,KAAK,MAAMI,eAAa,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC;AAAA,MACpD,QAAQ;AACN,eAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,IACA,KAAK,GAAG,GAAG,OAAO;AAChB,YAAM,MAAM,KAAK,IAAI,GAAG,CAAC;AACzB,YAAM,OAAO,EAAE,GAAG,KAAK,GAAG,MAAM;AAChC,UAAI,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,GAAG,EAAG;AAClD,MAAAL,WAAUC,OAAK,QAAQ,KAAK,GAAG,CAAC,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACpE,MAAAK,eAAc,KAAK,GAAG,CAAC,GAAG,KAAK,UAAU,IAAI,GAAG,EAAE,MAAM,IAAM,CAAC;AAAA,IACjE;AAAA,EACF;AACF;AAEO,SAAS,WAAW,OAAqB,IAAyC,CAAC,GAAW;AACnG,QAAM,MAAc,CAAC;AACrB,aAAW,OAAO,OAAO;AACvB,QAAI,IAAI,OAAQ;AAGhB,UAAM,IAAI,gBAAgB,GAAG;AAC7B,QAAI,CAAC,GAAG;AACN,QAAE,SAAS,IAAI,IAAI;AACnB;AAAA,IACF;AAIA,UAAM,OAAO,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,IAAI,KAAK,GAAG,EAAE,YAAY,CAAC,CAAC;AACtE,QAAI,EAAE,QAAQ,SAAU,KAAI,KAAK,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC;AAAA,aAC7D,EAAE,IAAI,WAAW,OAAO;AAC/B,UAAI,KAAK,IAAI,SAAS,EAAE,MAAM,EAAE,IAAI,MAAM,CAAC,EAAE,QAAQ,cAAcC,IAAG,QAAQ,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC;AAAA,aACzF,eAAe,KAAK,EAAE,GAAG,EAAG,KAAI,KAAK,IAAI,SAAS,GAAG,IAAI,CAAC;AAAA,EACrE;AACA,SAAO;AACT;AAGO,IAAM,cAAc;AAOpB,SAAS,SACd,MACA,GACuF;AACvF,QAAM,OAAO,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC;AAC7C,QAAM,QAAQ,CAAC,MAAc,IAAY,cAAqB;AAAA,IAC5D,MAAM,EAAE,OAAO,KAAK,MAAM;AAAA,IAC1B,MAAM,KAAK,MAAM,GAAG,aAAa;AAAA,IACjC,WAAW;AAAA,IACX;AAAA,IACA,GAAI,WAAW,EAAE,UAAU,KAAc,IAAI,CAAC;AAAA,EAChD;AAGA,MAAI,KAAK,KAAM,QAAO,MAAM,KAAK,KAAK,MAAM,KAAK,KAAK,IAAI,KAAK,KAAK,QAAQ;AAK5E,QAAM,SAAS,EAAE,QAAQ,KAAK;AAC9B,MAAI,UAAU,CAAC,WAAW,MAAM,KAAK,EAAE,QAAQ,KAAK,KAAK,SAAS,OAAO,MAAM,GAAG,GAAG,CAAC,GAAI,QAAO,MAAM,QAAQ,EAAE,EAAE;AACnH,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO;AAAA,IACL,MAAM,EAAE,OAAO,KAAK,MAAM,SAAS;AAAA,IACnC,MAAM,KAAK,KAAK,MAAM,GAAG,aAAa;AAAA,IACtC,WAAW,QAAQ,KAAK,WAAW,aAAa;AAAA,IAChD,GAAI,KAAK,WAAW,EAAE,UAAU,KAAc,IAAI,CAAC;AAAA,IACnD,IAAI,KAAK;AAAA,EACX;AACF;AACA,IAAM,gBAAgB;AAGtB,IAAM,cAAc;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKA,eAAsB,OACpB,KACA,OASgB,CAAC,GACmE;AACpF,QAAM,QAAQ,KAAK,SAAS,SAAS;AACrC,QAAM,MAAM,WAAW;AACvB,QAAM,UAAU,cAAc,IAAI,OAAO;AAIzC,MAAI,CAAC,SAAS;AACZ,QAAI,OAAO,GAAG,IAAI,OAAO,IAAI,IAAI,KAAK,6BAA6B,IAAI,OAAO,kBAAkB;AAChG,WAAO,EAAE,QAAQ,CAAC,GAAG,WAAW,CAAC,EAAE;AAAA,EACrC;AAIA,QAAM,MAAyB,EAAE,GAAG,QAAQ,IAAI;AAChD,aAAW,KAAK,YAAa,QAAO,IAAI,CAAC;AACzC,QAAM,IAAI,SAAS,SAAS,EAAE,MAAM,IAAI,MAAM,OAAO,IAAI,OAAO,KAAK,EAAE,GAAG,KAAK,GAAG,IAAI,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC;AAK3G,MAAI,CAAC,KAAK,CAAC,EAAE,QAAQ,IAAI;AACvB,QAAI,OAAiB,CAAC;AACtB,QAAI;AAGF,YAAM,UAAmB,KAAK,MAAM,IAAI,KAAK,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,CAAC,KAAK,IAAI,KAAK;AACxF,UAAI,WAAW,OAAO,YAAY,YAAY,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO,OAAO,KAAK,OAAO;AAAA,IACnG,QAAQ;AAAA,IAAC;AACT;AAAA,MACE;AAAA,MACA,GAAG,IAAI,OAAO,IAAI,IAAI,KAAK,SAAS,IAAI,OAAO,6DAA6D,KAAK,KAAK,IAAI,KAAK,QAAQ;AAAA,IACzI;AACA,WAAO,EAAE,QAAQ,CAAC,GAAG,WAAW,CAAC,EAAE;AAAA,EACrC;AAKA,QAAM,KACJ,aAAa,YAAY,UAAUN,OAAK,KAAK,OAAO,SAAS,CAAC,CAAC,GAAG,IAAI,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ;AAC5G,QAAM,YAAY,IAAI,YAAY,iBAAiB,EAAE,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,eAAe;AAIlG,MAAI,CAAC,UAAW,OAAM,WAAW,OAAO,KAAK,IAAI,EAAE,QAAQ,KAAK,EAAE,UAAU;AAC5E,QAAM,SAAS,IAAI,OAAO,KAAK;AAC/B,QAAM,MAAM,EAAE,SAAS,IAAI,SAAS,SAAS,GAAG;AAIhD,MAAI,UAAU,SAAS,OAAO,GAAG;AACjC,QAAM,WAAW,KAAK,cAAc;AACpC,aAAW,QAAQ,KAAK,IAAI,IAAI,UAAU,CAAC,WAAW,KAAK,IAAI,IAAI,SAAS;AAC1E,UAAM,IAAI,QAAQ,CAAC,QAAQ,WAAW,KAAK,EAAE,CAAC;AAC9C,cAAU,SAAS,OAAO,GAAG;AAAA,EAC/B;AACA,MAAI,CAAC,SAAS;AACZ,QAAI,OAAO,GAAG,IAAI,OAAO,IAAI,IAAI,KAAK,aAAa,EAAE,eAAe,WAAW,GAAI,eAAe;AAClG,UAAM,WAAW,WAAW,OAAO,EAAE,GAAG,KAAK,QAAQ,IAAI,SAAS,KAAK,EAAE,CAAC;AAC1E,eAAW,KAAK,OAAO;AACvB,WAAO,EAAE,QAAQ,CAAC,GAAG,WAAW,CAAC,GAAG,SAAS;AAAA,EAC/C;AACA,QAAM,SAAkB,CAAC;AACzB,MAAI;AACF,UAAM,QAAQ,WAAW,KAAK;AAC9B,UAAM,MAAM,MAAM,IAAI,IAAI,SAAS,EAAE;AAIrC,QACE,aACA,CAACO,aAAW,OAAO,KAAK,GAAG,CAAC,KAC5B,OAAO,KAAK,GAAG,EAAE,WAAW,KAC5B,EAAE,EAAE,cAAcA,aAAW,EAAE,UAAU,IACzC;AACA,iBAAW,KAAK,OAAO;AACvB,aAAO,EAAE,QAAQ,CAAC,GAAG,WAAW,CAAC,EAAE;AAAA,IACrC;AACA,QAAI,UAAW,OAAM,WAAW,OAAO,KAAK,IAAI,EAAE,QAAQ,KAAK,EAAE,UAAU;AAE3E,UAAM,QAAQ,CAAC,oFAAoF,KAAK,IAAI,KAAK;AACjH,QAAI,UAA4B,CAAC;AACjC,QAAI,OAAO;AAGT,YAAM,UAAU,EAAE,GAAG,QAAQ,KAAK,GAAG,IAAI,IAAI;AAC7C,YAAM,IAAI,QAAQ,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC,GAAG,OAAO,OAAO;AAG9E,UAAI,IAAI,iBAAiB,QAAQ;AAC/B,eAAO,OAAO,GAAG,MAAM,QAAQ,YAAY,EAAE,SAAS,GAAG,EAAE,YAAY,OAAO,OAAO,CAAC;AACxF,YAAM,EAAE,YAAY,IAAI,SAAS,IAAI,WAAW,KAAK,iBAAiB,GAAG,KAAK,IAAI;AAClF,gBAAU;AACV,UAAI,gBAAiB,OAAM,KAAK,IAAI,SAAS,IAAI,EAAE,gBAAgB,CAAC;AACpE,YAAM,KAAK,IAAI,SAAS,IAAI,EAAE,SAAS,EAAE,GAAI,IAAI,SAAoB,GAAG,QAAQ,EAAE,CAAC;AAKnF,UAAI,eAAe,IAAI,IAAI,OAAO,KAAK,EAAE,QAAQ,OAAOP,OAAK,WAAW,EAAE,QAAQ,GAAG;AACnF,cAAM,KAAK,IAAI,SAAS,IAAI,EAAE,KAAK,EAAE,QAAQ,IAAI,CAAC;AAAA,IACtD;AACA,UAAM,QAAS,IAAI,WAA4C,CAAC;AAChE,UAAM,SAAS,QAAQ,OAAO,EAAE,QAAQ,OAAO,MAAM;AACrD,UAAM,MAAM,QAAQ,SAAS,MAAM,IAAI,CAAC;AACxC,UAAM,UAAmB;AAAA,MACvB,GAAG;AAAA,MACH,GAAG;AAAA,MACH,GAAG;AAAA,MACH,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,MACnD;AAAA,MACA,KAAK;AAAA,MACL,SAAS,YAAY,KAAKM,IAAG,SAAS,CAAC;AAAA,MACvC,GAAI,SAAS,EAAE,KAAK,MAAM,MAAM,EAAE,IAAI,CAAC;AAAA,MACvC,GAAI,EAAE,QAAQ,QAAQ,EAAE,OAAO,EAAE,QAAQ,MAAM,IAAI,CAAC;AAAA,MACpD,GAAI,EAAE,QAAQ,cAAc,EAAE,aAAa,EAAE,QAAQ,YAAY,IAAI,CAAC;AAAA,MACtE,GAAI,EAAE,QAAQ,eAAe,EAAE,cAAc,EAAE,QAAQ,aAAa,IAAI,CAAC;AAAA,IAC3E;AACA,UAAM,UAAW,MAAM,IAAI,IAAI,SAAS,EAAE,EAAE,mBAA0C;AACtF,eAAW,KAAK,EAAE,QAAQ;AACxB,YAAM,IAAI,UAAU,GAAG;AAAA,QACrB,SAAS,EAAE,MAAM,IAAI,SAAS,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AAAA,QAC9D,SAAS,EAAE,GAAG,SAAS,KAAK,OAAO,QAAQ,GAAG,EAAE;AAAA,QAChD,KAAK,KAAK,OAAO,IAAI;AAAA,MACvB,CAAC;AACD,aAAO,KAAK,CAAC;AAAA,IACf;AACA,UAAM,kBAAkB,IAAI,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,UAAU,KAAK,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC;AAKjG,UAAM,SAAS,EAAE,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,SAAS,eAAe;AACzF,UAAM,WAAW,EAAE,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,cAAc;AAI/D,UAAM,YAAY,EAAE,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,cAAc,GAAG,KAAK;AACxE,UAAM,WAAW,YAAY,OAAO,cAAc,YAAY,MAAM,IAAI,IAAI,SAAS,EAAE,EAAE,cAAc;AACvG,QAAI,mBAAmB,EAAE,cAAc,CAAC,WAAW,YAAY,EAAE,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,YAAY,IAAI;AAC7G,YAAM,MAAM,MAAM,IAAI,IAAI,SAAS,EAAE;AACrC,YAAM,KAAK,KAAK,OAAO,IAAI;AAC3B,UAAI,YAAY,MAAM,OAAO,IAAI,WAAW,KAAK,MAAM,aAAa;AAClE,cAAM,IAAI,SAAS,QAAQ,eAAe,EAAE,YAAY,OAAO,IAAI,eAAe,KAAK,CAAC,GAAG;AAAA,UACzF,MAAM,OAAO,IAAI,gBAAgB,KAAK;AAAA,UACtC,QAAQ,CAAC,YAAY,OAAO,EAAE,OAAO,WAAW,WAAW,EAAE,MAAM,SAAS;AAAA,UAC5E;AAAA,QACF,CAAC;AACD,cAAM,MAAM,KAAK,GAAG,EAAE,IAAI,IAAI,EAAE,KAAK,MAAM,IAAI,EAAE,UAAU,MAAM;AACjE,YAAI,MAAM,YAAY,QAAQ,IAAI,eAAe;AAC/C,iBAAO;AAAA,YACL;AAAA,cACE,EAAE,MAAM,iBAAiB,cAAc,IAAI,OAAO,MAAM,EAAE,GAAG,GAAG,IAAI,IAAI,KAAK,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE;AAAA,cACnG;AAAA,gBACE,SAAS,EAAE,MAAM,IAAI,SAAS,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AAAA,gBAC9D,SAAS,EAAE,GAAG,SAAS,KAAK,OAAO,QAAQ,GAAG,EAAE;AAAA,gBAChD,KAAK;AAAA,cACP;AAAA,YACF;AAAA,UACF;AACA,gBAAM,KAAK,IAAI,SAAS,IAAI,EAAE,aAAa,IAAI,cAAc,IAAI,CAAC;AAAA,QACpE;AAAA,MACF;AAAA,IACF;AAEA,QAAI,YAAY,OAAO,cAAc,YAAY,CAAC,SAAU,OAAM,KAAK,IAAI,SAAS,IAAI,EAAE,WAAW,UAAU,CAAC;AAChH,QAAI,OAAQ,OAAM,KAAK,IAAI,SAAS,IAAI,EAAE,WAAW,KAAK,CAAC;AAE3D,QACE,mBACA,EAAE,cACF,EAAE,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,SAAS,eAAe,GAC1E;AACA,YAAM,MAAM,MAAM,IAAI,IAAI,SAAS,EAAE;AACrC,YAAM,WAAW,OAAO,IAAI,eAAe,KAAK;AAChD,UAAI,SAAS,OAAO,IAAI,gBAAgB,KAAK;AAC7C,YAAM,OAAO,QAAQ,eAAe,EAAE,YAAY,QAAQ;AAC1D,iBAAW,KAAK,KAAK,OAAO;AAC1B,eAAO;AAAA,UACL;AAAA,YACE;AAAA,cACE,MAAM;AAAA,cACN,cAAc,IAAI;AAAA,cAClB,MAAM;AAAA,gBACJ,MAAM;AAAA,gBACN,MAAM,EAAE;AAAA,gBACR,WAAW,EAAE;AAAA,gBACb,IAAI,IAAI,KAAK,EAAE,EAAE,EAAE,YAAY;AAAA,gBAC/B,GAAI,EAAE,WAAW,EAAE,UAAU,KAAc,IAAI,CAAC;AAAA,cAClD;AAAA,YACF;AAAA,YACA;AAAA,cACE,SAAS,EAAE,MAAM,IAAI,SAAS,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AAAA,cAC9D,SAAS,EAAE,GAAG,SAAS,KAAK,OAAO,QAAQ,GAAG,EAAE;AAAA,cAChD,KAAK,KAAK,OAAO,IAAI;AAAA,YACvB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,UAAI,KAAK,MAAM,OAAQ,OAAM,KAAK,IAAI,SAAS,IAAI,EAAE,iBAAiB,KAAK,MAAM,kBAAkB,OAAO,CAAC;AAAA,IAC7G;AAGA,QACE,QAAQ,UACR,EAAE,cACF,UACA,IAAI,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,UAAU,KAAK,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,GACzE;AACA,YAAM,SAAS,QAAQ,OAAO,EAAE,UAAU;AAC1C,YAAM,MAAM,UAAU,KAAK,UAAU,MAAM;AAC3C,UAAI,UAAU,QAAQ,MAAM,IAAI,IAAI,SAAS,EAAE,EAAE,YAAY;AAC3D,eAAO;AAAA,UACL;AAAA,YACE,EAAE,MAAM,mBAAmB,cAAc,IAAI,OAAO,MAAM,OAAO;AAAA,YACjE;AAAA,cACE,SAAS,EAAE,MAAM,IAAI,SAAS,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AAAA,cAC9D,SAAS,EAAE,GAAG,SAAS,KAAK,OAAO,QAAQ,GAAG,EAAE;AAAA,cAChD,KAAK,KAAK,OAAO,IAAI;AAAA,YACvB;AAAA,UACF;AAAA,QACF;AACA,cAAM,KAAK,IAAI,SAAS,IAAI,EAAE,YAAY,IAAI,CAAC;AAAA,MACjD;AAAA,IACF;AAIA,WAAO;AAAA,MACL;AAAA,MACA,OAAO,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,WAAW,EAAE,OAAO,GAAG,MAAM,WAAW,EAAE,IAAI,EAAE,EAAE;AAAA,IACxF;AACA,eAAW,KAAK,OAAO;AACvB;AAAA,MACE;AAAA,MACA,GAAG,IAAI,OAAO,IAAI,IAAI,KAAK,KAAK,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,GAAG,KAAK,SAAS,KAAK,QAAQ,IAAI,SAAS,EAAE,CAAC;AAAA,IAC/G;AAAA,EACF,UAAE;AACA,YAAQ;AAAA,EACV;AAMA,MAAI,KAAK,OAAO,IAAI,KAAM,QAAO,MAAM,IAAI,aAAa,EAAE;AAC1D,QAAM,YACJ,KAAK,UAAU,QACX,CAAC,IACC,MAAM,MAAM,OAAO,IAAI,OAAO,QAAQ,KAAK;AAAA,IAC3C,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,IAC1C,GAAI,KAAK,aAAa,SAAY,EAAE,UAAU,KAAK,SAAS,IAAI,CAAC;AAAA,IACjE,GAAI,KAAK,UAAU,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,EAClD,CAAC,KAAM,CAAC;AACd,SAAO,EAAE,QAAQ,UAAU;AAC7B;AA2DA,IAAM,aAAoB,EAAE,KAAK,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,OAAO,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC,EAAE;AAKjG,IAAM,UAAU;AAGvB,IAAM,WAAW,CAAC,UAA0BE,OAAK,KAAK,OAAO,SAAS,YAAY;AAClF,SAAS,SAAS,OAAuB;AACvC,MAAI;AAEF,WAAO,OAAOC,eAAa,SAAS,KAAK,GAAG,MAAM,CAAC;AAAA,EACrD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AACA,SAAS,SAAS,OAAe,IAAkB;AACjD,MAAI;AACF,IAAAC,WAAUF,OAAK,QAAQ,SAAS,KAAK,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACzE,IAAAG,eAAc,SAAS,KAAK,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,IAAM,CAAC;AAAA,EAC5D,QAAQ;AAAA,EAAC;AACX;AAKA,SAAS,mBAAmB,OAAe,aAA2B,QAAgB,KAAsB;AAC1G,QAAM,YAAY,YAAY;AAAA,IAC5B,CAAC,MAAM,CAAC,EAAE,UAAU,eAAe,KAAK,EAAE,GAAG,KAAK,GAAG,YAAY,OAAO,EAAE,IAAI,GAAG,SAAS,KAAK;AAAA,EACjG;AACA,MAAI,CAAC,UAAU,OAAQ,QAAO;AAC9B,QAAM,OAAO,OAAO,SAAS;AAC7B,aAAW,KAAK,WAAW;AACzB,UAAM,UAAU,IAAI,QAAQ,OAAO,EAAE,IAAI;AACzC,UAAM,IAAI,QAAQ,KAAK;AACvB,eAAW,OAAO,MAAM;AACtB,UAAI;AACF,YAAIC,UAAS,OAAO,KAAK,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,IAAI,GAAG,CAAC,KAAK,GAAI,QAAO;AAAA,MAC3E,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF;AACA,SAAO;AACT;AAMO,IAAM,iBAAiB;AAa9B,IAAM,WAAW,CAAC,UAA0BC,OAAK,KAAK,OAAO,SAAS,YAAY;AAElF,SAAS,SAAS,OAAqB;AACrC,MAAI;AACF,IAAAC,WAAUD,OAAK,QAAQ,SAAS,KAAK,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACzE,IAAAE,eAAc,SAAS,KAAK,GAAG,IAAI,EAAE,MAAM,IAAM,CAAC;AAAA,EACpD,QAAQ;AAAA,EAAC;AACX;AAcA,eAAsB,MACpB,OACA,aACA,QACA,OACA,IAAmC,CAAC,GACI;AACxC,QAAM,QAAQ,EAAE,SAAS;AACzB,QAAM,OAAO,SAAS,KAAK;AAC3B,MAAI,UAAU,cAAc,OAAO,OAAO;AAC1C,MAAI,CAAC,SAAS;AACZ,aAAS,KAAK;AACd,cAAU,cAAc,OAAO,OAAO;AACtC,QAAI,CAAC,QAAS,QAAO;AAAA,EACvB;AACA,QAAM,YAAoC,CAAC;AAC3C,aAAS;AACP,QAAI,EAAE,aAAa,UAAa,MAAM,IAAI,IAAI,iBAAiB,EAAE,UAAU;AACzE,eAAS,KAAK;AACd,cAAQ;AACR,QAAE,UAAU;AACZ,aAAO;AAAA,IACT;AACA,QAAI;AAIF,YAAM,OAAO,KAAK,IAAI,SAAS,SAAS,KAAK,IAAI,UAAU,MAAM,IAAI,CAAC;AACtE,UAAI,OAAO,KAAK,mBAAmB,OAAO,aAAa,QAAQ,MAAM,IAAI,CAAC,EAAG,OAAM,MAAM,MAAM,IAAI;AACnG,UAAI;AACF,QAAAC,YAAW,IAAI;AAAA,MACjB,QAAQ;AAAA,MAAC;AACT,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,MAAM,OAAO,aAAa,QAAQ,OAAO,EAAE,MAAM,CAAC,CAAC;AAC3F,kBAAU,CAAC,KAAK,UAAU,CAAC,KAAK,KAAK;AAAA,IACzC,UAAE;AACA,cAAQ;AAAA,IACV;AACA,QAAI,CAACC,aAAW,IAAI,EAAG,QAAO;AAC9B,cAAU,cAAc,OAAO,OAAO;AACtC,QAAI,CAAC,QAAS,QAAO;AAAA,EACvB;AACF;AAYA,IAAM,aAAa,CAAC,KAAO,KAAQ,KAAQ,IAAI,KAAQ,KAAK,GAAM;AAClE,IAAM,cAAc,CAAC,OAAe,SAAyBJ,OAAK,KAAK,OAAO,WAAW,GAAG,OAAO,IAAI,CAAC,OAAO;AACxG,SAAS,YAAY,OAAe,MAA8B;AACvE,MAAI;AACF,UAAM,IAAI,KAAK,MAAMK,eAAa,YAAY,OAAO,IAAI,GAAG,MAAM,CAAC;AACnE,WAAO,OAAO,EAAE,UAAU,WAAW,IAAI;AAAA,EAC3C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AACA,SAAS,YAAY,OAAe,MAAc,KAAqB,MAA+B;AACpG,QAAM,YAAY,MAAM,YAAY,KAAK;AACzC,QAAM,SAAS,WAAW,KAAK,IAAI,UAAU,WAAW,MAAM,IAAI,CAAC;AACnE,QAAM,OAAO,OAAO,SAAS,IAAI,YAAY,IAAK,IAAI,eAA0B;AAChF,QAAM,IAAI,EAAE,OAAO,KAAK,IAAI,IAAI,MAAM,UAAU,KAAK,GAAG,IAAI,UAAU,EAAE,IAAI,IAAI,SAAS,EAAE,GAAG,KAAK,EAAE;AACrG,QAAM,IAAI,YAAY,OAAO,IAAI;AACjC,MAAI;AACF,IAAAJ,WAAUD,OAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3D,IAAAE,eAAc,GAAG,KAAK,UAAU,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AAAA,EACrD,QAAQ;AAAA,EAAC;AACT,SAAO;AACT;AACA,SAAS,aAAa,OAAe,MAAoB;AACvD,MAAI;AACF,IAAAC,YAAW,YAAY,OAAO,IAAI,CAAC;AAAA,EACrC,QAAQ;AAAA,EAAC;AACX;AAGA,IAAM,eAAe;AAIrB,IAAM,iBAAiB,oBAAI,IAAI,CAAC,eAAe,OAAO,CAAC;AAEvD,IAAM,QAAQ;AACd,IAAM,cAAc;AAUpB,eAAsB,MACpB,OACA,aACA,QACA,OACA,IAKI,CAAC,GAC4B;AACjC,QAAM,YAAoC,CAAC;AAC3C,QAAM,QAAQ,EAAE,SAAS;AACzB,QAAM,QAAQ,WAAW,aAAa;AAAA,IACpC,QAAQ,CAAC,SAAS,IAAI,OAAO,QAAQ,IAAI,sEAAsE;AAAA,EACjH,CAAC;AACD,QAAM,QAAQ,IAAI,IAAI,YAAY,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,UAAU,CAAC,CAAC,CAAC,CAAC;AACpE,QAAM,OAAO,OAAO,SAAS;AAC7B,MAAI,OAAO;AACT,UAAM,IAAI,GAAG,MAAM,OAAO,IAAI,OAAO,MAAM,OAAO,CAAC;AACnD,SAAK,KAAK,CAAC,GAAG,MAAO,GAAG,EAAE,OAAO,IAAI,EAAE,OAAO,OAAO,IAAI,KAAK,GAAG,EAAE,OAAO,IAAI,EAAE,OAAO,OAAO,IAAI,IAAI,CAAE;AAAA,EAC1G;AAWA,QAAM,aAAa,oBAAI,IAAY;AACnC,QAAM,aAAa,CAAC,KAAgB,QAAgB,SAA6D;AAC/G,QAAI;AACF,aAAO,OAAO,KAAK,KAAK,QAAQ,IAAI;AAAA,IACtC,SAAS,GAAG;AACV,YAAM,OAAQ,EAAyB;AACvC,UAAI,OAAO,SAAS,SAAU,OAAM;AACpC,YAAM,IAAI,OAAO,KAAK,GAAG;AACzB,UAAI,CAAC,WAAW,IAAI,CAAC,GAAG;AACtB,mBAAW,IAAI,CAAC;AAChB,YAAI,OAAO,qBAAqB,CAAC,KAAK,IAAI,8BAA8B;AAAA,MAC1E;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,YAAY,OAAO,SAA8B;AACrD,QAAI,UAAU,YAAY,OAAO,KAAK,IAAI;AAC1C,QAAI,WAAW,CAAC,EAAE,SAAS,QAAQ,QAAQ,KAAK,IAAI,GAAG;AACrD,gBAAU,KAAK,IAAI,IAAI;AACvB;AAAA,IACF;AACA,UAAM,UAAU,IAAI,QAAQ,OAAO,KAAK,IAAI;AAC5C,UAAM,IAAI,QAAQ,KAAK;AAIvB,UAAM,OAAO,MAAM,IAAI,KAAK,IAAI;AAChC,UAAM,QAAQ,IAAI,UAAU,OAAO,KAAK,IAAI;AAC5C,QAAI,WAAW,SAAS,QAAQ,CAAC,IAAI,MAAM,KAAK;AAChD,QAAI,YAAY,EAAE,GAAG,SAAS;AAC9B,QAAI,OAAO;AACX,QAAI,UAAU;AAGd,QAAI,QAAiB,CAAC;AACtB,QAAI,UAAkC,CAAC;AAOvC,UAAM,UAAU,OAAO,WAA6C;AAClE,UAAI,gBAAgB,SAAU,UAAS,OAAO,MAAM,IAAI,CAAC;AACzD,YAAM,MAAM,MAAM,KAAK,KAAK,MAAM;AAClC,UAAI,IAAI,MAAM,IAAI,WAAW,QAAS,QAAO;AAC7C,UAAI,OAAO,WAAW,GAAG;AACvB,cAAM,IAAI,OAAO,CAAC;AAClB,YAAI,OAAO,QAAQ,KAAK,IAAI,SAAS,IAAI,SAAS,EAAE,iBAAY,EAAE,EAAE,KAAK,EAAE,IAAI,+BAA+B;AAC9G,eAAO,EAAE,IAAI,MAAM,cAAc,GAAG,UAAU,GAAG,YAAY,GAAG,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,QAAQ,YAAY,CAAC,EAAE;AAAA,MAChH;AACA,YAAM,OAAO,OAAO,SAA2C;AAC7D,cAAM,IAAI,MAAM,QAAQ,IAAI;AAC5B,YAAI,EAAE,MAAM,EAAE,WAAW,OAAQ,QAAO;AACxC;AAAA,UACE;AAAA,UACA,QAAQ,KAAK,IAAI,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,iBAAY,KAAK,MAAM,OAAO,OAAO,MAAM;AAAA,QAClG;AACA,cAAM,WAAW,KAAK,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,QAAQ,UAAU,EAAE;AAClE,eAAO,EAAE,IAAI,MAAM,cAAc,GAAG,UAAU,GAAG,YAAY,GAAG,SAAS;AAAA,MAC3E;AACA,YAAM,OAAO,KAAK,KAAK,OAAO,SAAS,CAAC;AACxC,YAAM,IAAI,MAAM,KAAK,OAAO,MAAM,GAAG,IAAI,CAAC;AAC1C,UAAI,CAAC,EAAE,GAAI,QAAO;AAClB,YAAM,IAAI,MAAM,KAAK,OAAO,MAAM,IAAI,CAAC;AACvC,UAAI,CAAC,EAAE,GAAI,QAAO;AAClB,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,cAAc,EAAE,eAAe,EAAE;AAAA,QACjC,WAAW,EAAE,YAAY,MAAM,EAAE,YAAY;AAAA,QAC7C,aAAa,EAAE,cAAc,MAAM,EAAE,cAAc;AAAA,QACnD,UAAU,CAAC,GAAI,EAAE,YAAY,CAAC,GAAI,GAAI,EAAE,YAAY,CAAC,CAAE;AAAA,MACzD;AAAA,IACF;AACA,UAAM,cAAc,MAAY;AAC9B,UAAI,SAAS,SAAS,KAAK,UAAU,SAAS,MAAM,KAAK,UAAU,QAAQ,EAAG;AAC9E,YAAM,MAAM,SAAS;AACrB,iBAAW,EAAE,GAAG,UAAU;AAAA,IAC5B;AACA,UAAM,OAAO,YAA8B;AACzC,UAAI,CAAC,MAAM,QAAQ;AACjB,eAAO,OAAO,GAAG,OAAO;AACxB,kBAAU,CAAC;AACX,gBAAQ,MAAM,CAAC;AACf,oBAAY;AACZ,eAAO;AAAA,MACT;AACA,YAAM,MAAM,MAAM,QAAQ,KAAK;AAC/B,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,SAAS,IAAI,UAAU;AAC7B,YAAI,OAAO;AACX,YAAI,WAAW,SAAS;AACtB,oBAAU,YAAY,OAAO,KAAK,MAAM,KAAK,OAAO;AACpD,iBAAO,WAAW,KAAK,OAAO,QAAQ,QAAQ,KAAK,IAAI,KAAK,GAAI,CAAC;AAAA,QACnE;AACA,YAAI,OAAO,QAAQ,KAAK,IAAI,KAAK,IAAI,UAAU,EAAE,IAAI,IAAI,SAAS,EAAE,WAAM,MAAM,GAAG,IAAI,EAAE;AACzF,YAAI,IAAI,WAAW,WAAW,IAAI,WAAW,aAAc,kBAAiB,KAAK,MAAM,GAAG;AAC1F,YAAI,IAAI,WAAW,QAAQ;AACzB,kBAAQ,CAAC;AACT,oBAAU,CAAC;AACX,sBAAY,EAAE,GAAG,SAAS;AAC1B,iBAAO;AAAA,QACT;AAAA,MACF,WAAW,SAAS;AAClB,qBAAa,OAAO,KAAK,IAAI;AAC7B,kBAAU;AAAA,MACZ;AACA,cAAQ,MAAM;AACd,aAAO,OAAO,GAAG,OAAO;AACxB,cAAQ,MAAM,CAAC;AACf,kBAAY;AACZ,cAAQ,CAAC;AACT,gBAAU,CAAC;AACX,aAAO;AAAA,IACT;AACA,eAAW,OAAO,MAAM;AACtB,UAAI,WAAW,QAAQ,YAAa;AACpC,UAAI,SAAS,QAAQ,QAAQ,IAAI,GAAG,CAAC,KAAK,EAAE,QAAQ,IAAI,GAAG,CAAC,KAAK;AACjE,iBAAS;AACP,cAAM,OAAO,QAAQ,MAAM;AAC3B,cAAM,OAAO,WAAW,KAAK,QAAQ,IAAI;AACzC,YAAI,CAAC,KAAM;AACX,cAAM,EAAE,QAAQ,QAAQ,KAAK,IAAI;AACjC,YAAI,CAAC,OAAO,OAAQ;AACpB,mBAAW,KAAK,QAAQ;AACtB,gBAAM,IAAI,YAAY,GAAG,KAAK,MAAM,KAAK,KAAK,EAAE,MAAMG,IAAG,QAAQ,EAAE,CAAC;AACpE,cAAI,CAAC,EAAG;AACR,cAAI,SAAS,GAAG,MAAM,WAAW,QAAQ,IAAI,GAAG,CAAC,EAAG,OAAM,KAAK,CAAC;AAAA,mBACvD,EAAE,QAAS,GAAE,QAAQ,KAAK,IAAI,KAAK,EAAE,QAAQ,KAAK,IAAI,KAAK,KAAK;AAAA,QAC3E;AACA,iBAAS;AACT,gBAAQ,QAAQ,IAAI,GAAG,CAAC,IAAI;AAC5B,YAAI,MAAM,UAAU,OAAO;AACzB,cAAI,CAAE,MAAM,KAAK,GAAI;AACnB,sBAAU;AACV;AAAA,UACF;AACA,cAAI,QAAQ,YAAa;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,YAAY,MAAM,UAAU,OAAO,KAAK,OAAO,EAAE;AAAS,UAAI,CAAE,MAAM,KAAK,EAAI,WAAU;AAAA;AAC9F,cAAU,KAAK,IAAI,IAAI;AAAA,EACzB;AACA,QAAM,QAAQ,IAAI,MAAM,IAAI,SAAS,CAAC;AACtC,SAAO;AACT;AAEA,SAAS,iBAAiB,MAAc,KAAmE;AACzG,MAAI;AACF,UAAM,MAAM,WAAW;AACvB,UAAM,IAAI,IAAI,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AAC/C,QAAI,CAAC,EAAG;AACR,QAAI,IAAI,WAAW,QAAS,GAAE,SAAS,IAAG,oBAAI,KAAK,GAAE,YAAY,CAAC,IAAI,IAAI,SAAS,cAAc;AACjG,QAAI,IAAI,WAAW,gBAAgB,OAAO,IAAI,aAAa,SAAU,GAAE,WAAW,IAAI;AACtF,gBAAY,GAAG;AAAA,EACjB,QAAQ;AAAA,EAAC;AACX;AAIO,IAAM,UAAU,CAAC,UAA0BC,OAAK,KAAK,OAAO,MAAM;",
|
|
6
|
-
"names": ["appendFileSync", "existsSync", "mkdirSync", "readdirSync", "readFileSync", "renameSync", "statSync", "unlinkSync", "writeFileSync", "os", "path", "execFileSync", "createHash", "existsSync", "readdirSync", "readFileSync", "statSync", "path", "path", "cap", "path", "settings", "entry", "path", "readdirSync", "existsSync", "createHash", "readFileSync", "rows", "pid", "execFileSync", "statSync", "path", "ask", "createHash", "execFileSync", "readFileSync", "readFileSync", "statSync", "path", "statSync", "path", "readFileSync", "existsSync", "readdirSync", "readFileSync", "statSync", "path", "NAME", "path", "validTool", "existsSync", "prune", "readFileSync", "rows", "readdirSync", "statSync", "claudeLogin", "execFileSync", "closeSync", "openSync", "readdirSync", "readFileSync", "readSync", "statSync", "os", "path", "os", "
|
|
4
|
+
"sourcesContent": ["// SPDX-License-Identifier: Apache-2.0\n// The HTTP sink (spec/HTTP.md \u00A72): POST {events} with the protocol header, a bearer\n// token and optional Standard Webhooks signature; errors map to what the sender\n// does next. The cursor moves only on 202. Retry/backoff is the worker's cadence\n// (each hook run drains the backlog); this class reports, it does not sleep.\nimport { createHash, createHmac } from \"node:crypto\";\nimport type { DeliveryResult, Event, Sink, SinkConfig, Tier } from \"@sessionpipe/core\";\n\nconst TIMEOUT_MS = 15_000;\n\n/** The Standard Webhooks message id (HTTP.md \u00A72): the same batch sent again carries\n * the same id, so a receiver that dedupes on webhook-id sees a retry as one message.\n * Derived from the events' own ids, which the outbox keeps across retries. */\nexport function webhookId(events: Pick<Event, \"id\">[]): string {\n return `msg_${createHash(\"sha256\")\n .update(events.map((e) => e.id).join(\",\"))\n .digest(\"base64url\")\n .slice(0, 32)}`;\n}\n\n/** HTTP.md \u00A72 429: wait as `retry-after` says, seconds or an HTTP date (RFC 9110\n * \u00A710.2.3); a minute when the header is missing or unreadable. */\nexport function retryAfterMs(header: string | null, now = Date.now()): number {\n if (header === null || header.trim() === \"\") return 60_000;\n const secs = Number(header);\n if (Number.isFinite(secs)) return Math.max(0, secs) * 1000;\n const at = Date.parse(header);\n return Number.isFinite(at) ? Math.max(0, at - now) : 60_000;\n}\n\nexport class HttpSink implements Sink {\n readonly name: string;\n readonly pii: boolean;\n private readonly url: string;\n constructor(\n private readonly cfg: SinkConfig,\n readonly tier: Tier,\n ) {\n this.name = cfg.name;\n this.pii = !!cfg.pii;\n this.url = cfg.url.replace(/\\/$/, \"\");\n }\n\n private eventsUrl(): string {\n // The endpoint from the well-known file is stored beside the url at `sink add`;\n // until then the spec's default path.\n const ep = (this.cfg as { endpoints?: { events?: string } }).endpoints?.events ?? \"/api/sessionpipe/v1/events\";\n return /^https?:\\/\\//.test(ep) ? ep : this.url + ep;\n }\n\n async send(events: Event[]): Promise<DeliveryResult> {\n const body = JSON.stringify({ events });\n const headers: Record<string, string> = { \"content-type\": \"application/json\", \"sessionpipe-protocol\": \"1\" };\n if (this.cfg.token) headers.authorization = `Bearer ${this.cfg.token}`;\n if (this.cfg.secret?.startsWith(\"whsec_\")) {\n const id = webhookId(events);\n const ts = Math.floor(Date.now() / 1000);\n const key = Buffer.from(this.cfg.secret.slice(6), \"base64\");\n const sig = createHmac(\"sha256\", key).update(`${id}.${ts}.${body}`).digest(\"base64\");\n headers[\"webhook-id\"] = id;\n headers[\"webhook-timestamp\"] = String(ts);\n headers[\"webhook-signature\"] = `v1,${sig}`;\n }\n const ctl = new AbortController();\n const t = setTimeout(() => ctl.abort(), TIMEOUT_MS);\n try {\n const r = await fetch(this.eventsUrl(), { method: \"POST\", headers, body, signal: ctl.signal });\n const text = await r.text();\n let j: Record<string, unknown> = {};\n try {\n j = text ? (JSON.parse(text) as Record<string, unknown>) : {};\n } catch {}\n if (r.status === 202 || r.status === 200) {\n return {\n ok: true,\n acknowledged: events.length,\n accepted: Number(j.accepted ?? events.length),\n duplicates: Number(j.duplicates ?? 0),\n rejected: Array.isArray(j.rejected) ? (j.rejected as { id: string; reason: string }[]) : [],\n ...(typeof j.max_tier === \"number\" ? { max_tier: j.max_tier as Tier } : {}),\n status: r.status,\n };\n }\n const err = typeof j.reason === \"string\" ? j.reason : text.slice(0, 200);\n if (r.status === 400) return { ok: false, acknowledged: 0, status: 400, error: err, action: \"drop\" };\n if (r.status === 401) return { ok: false, acknowledged: 0, status: 401, error: err, action: \"pause\" };\n if (r.status === 403)\n return {\n ok: false,\n acknowledged: 0,\n status: 403,\n error: err,\n action: \"lower-tier\",\n ...(typeof j.max_tier === \"number\" ? { max_tier: j.max_tier as Tier } : {}),\n };\n if (r.status === 413) return { ok: false, acknowledged: 0, status: 413, error: err, action: \"split\" };\n if (r.status === 429)\n return {\n ok: false,\n acknowledged: 0,\n status: 429,\n error: err,\n action: \"retry\",\n retryAfterMs: retryAfterMs(r.headers.get(\"retry-after\")),\n };\n return { ok: false, acknowledged: 0, status: r.status, error: err, action: \"retry\" };\n } catch (e) {\n return { ok: false, acknowledged: 0, error: String((e as Error).message ?? e), action: \"retry\" };\n } finally {\n clearTimeout(t);\n }\n }\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// What a worker run does, as a function so tests and `sessionpipe replay` share it.\nimport {\n appendFileSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n renameSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport {\n adapterByName,\n Cursors,\n capTail,\n claudeControl,\n type DataOf,\n type DeliveryResult,\n type Event,\n type FactsState,\n FileSink,\n filterEvent,\n gitFacts,\n isInjected,\n machineName,\n makeEvent,\n Outbox,\n type OutboxRef,\n processAlive,\n readConfig,\n readHook,\n redactDeep,\n type Session,\n type Sink,\n type SinkConfig,\n StdoutSink,\n safeId,\n shortId,\n stateDir,\n type TranscriptRead,\n takeLock,\n takeNamedLock,\n tilde,\n writeConfig,\n} from \"@sessionpipe/core\";\nimport { aliasFile, readAliases, receiverIdOf } from \"./control/aliases.js\";\nimport { ask, socketPath } from \"./control/local.js\";\nimport { sendTool, ToolClock, type ToolsMode, toolsMode } from \"./fold.js\";\nimport { HttpSink } from \"./http-sink.js\";\nimport { sinkWithSecrets } from \"./secrets.js\";\n\nasync function tellDaemon(\n state: string,\n job: Job,\n session: string,\n cwd: string | undefined,\n transcript?: string,\n): Promise<void> {\n const sock = socketPath(state);\n if (process.platform !== \"win32\" && !existsSync(sock)) return;\n // The session's own permission mode, where its payload names one (CONTROL.md \u00A76, The\n // session's mode): the daemon records it per session, and an in-place lane and a\n // relaunch's narrowing both rest on it. Read from the top-level field, never matched\n // out of the payload's text, so a tool input that quotes one cannot set it.\n let mode: string | null = null;\n try {\n mode = claudeControl.hookMode(JSON.parse(job.stdin));\n } catch {}\n await ask(\n sock,\n {\n op: \"event\",\n session: `${job.harness}:${session}`,\n event: job.event,\n ...(cwd ? { cwd } : {}),\n ...(transcript ? { transcript } : {}),\n ...(mode ? { mode } : {}),\n },\n 300,\n );\n}\n\ndeclare const __SESSIONPIPE_VERSION__: string;\nconst VERSION = typeof __SESSIONPIPE_VERSION__ === \"string\" ? __SESSIONPIPE_VERSION__ : \"0.0.0\";\n\nexport interface Job {\n harness: string;\n event: string;\n argv: string[];\n stdin: string;\n cwd: string;\n env: Record<string, string>;\n ppid?: number;\n at: number;\n /** How many times the session's lock held this job out (a fresh worker starts for\n * it each time, up to MAX_TRIES; after that a sweep picks it up). */\n tries?: number;\n}\n\nexport const MAX_TRIES = 10;\n\n/** A job file the worker will pick up: random suffix, 0600, in a 0700 dir. */\nexport function enqueueJob(state: string, job: Job): string {\n const jobs = jobsDir(state);\n mkdirSync(jobs, { recursive: true, mode: 0o700 });\n const f = path.join(jobs, `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`);\n writeFileSync(f, JSON.stringify(job), { mode: 0o600 });\n return f;\n}\n\n/** A job is claimed by renaming its file `<file>.<pid>.running`: of two workers that\n * race for it, one rename wins. The claim stays until the job's events are in the\n * outbox (or it went back to the queue), so a worker its deadline ended mid-run\n * leaves it behind for a sweep to give back (ground rule 4: no event lost). */\nexport function claimJob(file: string): { job: Job; file: string } | null {\n const claimed = `${file}.${process.pid}.running`;\n try {\n renameSync(file, claimed);\n } catch {\n return null;\n }\n try {\n return { job: JSON.parse(readFileSync(claimed, \"utf8\")) as Job, file: claimed };\n } catch {\n releaseJob(claimed);\n return null;\n }\n}\nexport function releaseJob(claimed: string | undefined): void {\n if (!claimed) return;\n try {\n unlinkSync(claimed);\n } catch {}\n}\n\n/** Jobs another worker left behind: a claim whose worker is gone (or older than any\n * worker lives, `abandonedMs`) goes back to the queue, then every queued job older\n * than `olderThanMs` (younger ones belong to the worker their hook just spawned)\n * is claimed and run. Every worker sweeps before it exits. */\nexport async function sweepJobs(\n state: string,\n olderThanMs = 3000,\n limit = 20,\n abandonedMs = 60_000,\n o: DrainLife = {},\n): Promise<number> {\n const jobs = jobsDir(state);\n let names: string[] = [];\n try {\n names = readdirSync(jobs);\n } catch {\n return 0;\n }\n const queued = names.filter((n) => n.endsWith(\".json\"));\n for (const n of names) {\n const m = /^(.*\\.json)\\.(\\d+)\\.running$/.exec(n);\n if (!m) continue;\n const f = path.join(jobs, n);\n try {\n if (processAlive(Number(m[2])) && Date.now() - statSync(f).mtimeMs < abandonedMs) continue;\n renameSync(f, path.join(jobs, m[1] as string));\n queued.push(m[1] as string);\n } catch {}\n }\n let ran = 0;\n // The jobs' events go to the outbox one by one and the sinks drain once at the end:\n // a drain per job would wait out the machine's pace (PACE_MS) once per job. Each job\n // that appended something marks flush.more, so a drain already running sends it in\n // its next pass instead of leaving it behind the rest of the sweep. A job whose\n // session is busy waits SWEEP_LOCK_WAIT_MS, not a hook's 6 s, and goes back to the\n // queue; no job starts once the worker's life is too short for the drain after it.\n for (const n of queued.sort().slice(0, limit)) {\n if (o.deadline !== undefined && Date.now() + SWEEP_LOCK_WAIT_MS + PASS_BUDGET_MS > o.deadline) break;\n const f = path.join(jobs, n);\n try {\n if (Date.now() - statSync(f).mtimeMs < olderThanMs) continue;\n } catch {\n continue;\n }\n const c = claimJob(f);\n if (!c) continue;\n try {\n const r = await runJob(c.job, { state, claimed: c.file, drain: false, lockWaitMs: SWEEP_LOCK_WAIT_MS });\n if (r.events.length) markMore(state);\n } catch {\n } finally {\n releaseJob(c.file);\n }\n ran++;\n }\n if (ran) await drain(state, readConfig().sinks, new Outbox(state), undefined, o);\n return ran;\n}\n\nexport function log(state: string, line: string): void {\n try {\n mkdirSync(state, { recursive: true, mode: 0o700 });\n const f = path.join(state, \"log\");\n try {\n if (statSync(f).size > 1024 * 1024) renameSync(f, `${f}.1`);\n } catch {}\n appendFileSync(f, `${new Date().toISOString()} ${line}\\n`);\n } catch {}\n}\n\n/** <state>/facts/<harness>/<session>.json \u2014 what the machine learned about a session. */\nexport function factsState(state: string): FactsState {\n const file = (h: string, s: string) => path.join(state, \"facts\", safeId(h), `${safeId(s)}.json`);\n return {\n get(h, s) {\n try {\n return JSON.parse(readFileSync(file(h, s), \"utf8\")) as Record<string, unknown>;\n } catch {\n return {};\n }\n },\n save(h, s, patch) {\n const cur = this.get(h, s);\n const next = { ...cur, ...patch };\n if (JSON.stringify(next) === JSON.stringify(cur)) return;\n mkdirSync(path.dirname(file(h, s)), { recursive: true, mode: 0o700 });\n writeFileSync(file(h, s), JSON.stringify(next), { mode: 0o600 });\n },\n };\n}\n\nexport function buildSinks(sinks: SinkConfig[], o: { locked?: (name: string) => void } = {}): Sink[] {\n const out: Sink[] = [];\n for (const raw of sinks) {\n if (raw.paused) continue;\n // A token kept in a keychain is read now; one this process can't read (a keychain\n // locked in an ssh session) leaves the sink's events waiting, never sent unsigned.\n const s = sinkWithSecrets(raw);\n if (!s) {\n o.locked?.(raw.name);\n continue;\n }\n // Math.trunc: a tier of 1.5 that an older `sink add --tier 1.5` stored sends as 1.\n // `|| 0`: a sink with no tier (a hand edit) sends at 0, not as a `tier: null` no\n // receiver takes.\n const tier = Math.trunc(Math.min(Number(s.tier) || 0, s.max_tier ?? 3)) as 0 | 1 | 2 | 3;\n if (s.url === \"stdout\") out.push(new StdoutSink(s.name, tier, !!s.pii));\n else if (s.url.startsWith(\"file:\"))\n out.push(new FileSink(s.name, s.url.slice(5).replace(/^~(?=\\/|$)/, os.homedir()), tier, !!s.pii));\n else if (/^https?:\\/\\//.test(s.url)) out.push(new HttpSink(s, tier));\n }\n return out;\n}\n\n/** How often a running turn's words go out again (turn.progress), at most. */\nexport const PROGRESS_MS = 20_000;\n\n/** The turn still running in a transcript read from the delivery cursor: the last\n * turn with a reply so far, or a prompt nothing has answered yet (the hook's own\n * copy when the transcript doesn't hold it yet). `base` is the number the cursor's\n * first turn will get, so a turn keeps the number its turn.transcript will carry.\n * The reply keeps its end when it is long: where the turn is now. */\nexport function openTurn(\n read: TranscriptRead,\n o: { base: number; prompt?: string | undefined; at: number },\n): { turn: number; user: string; assistant: string; at: number; injected?: true } | null {\n const last = read.turns[read.turns.length - 1];\n const fresh = (user: string, at: number, injected?: true) => ({\n turn: o.base + read.turns.length,\n user: user.slice(0, MAX_TURN_SIDE),\n assistant: \"\",\n at,\n ...(injected ? { injected: true as const } : {}),\n });\n // A turn still running says the same about its ask as the finished one will: the board\n // would otherwise show the person's relayed words as typed here until the turn ends.\n if (read.open) return fresh(read.open.user, read.open.at, read.open.injected);\n // The prompt the hook just reported, unless the transcript already has its reply.\n // Text the person never typed (a task notification, another session's message) opens\n // no turn: the readers skip it, so that turn would never be delivered, and its\n // reading would stand in for the turn that is running until the next one lands.\n const prompt = o.prompt?.trim();\n if (prompt && !isInjected(prompt) && !(last && last.user.includes(prompt.slice(0, 200)))) return fresh(prompt, o.at);\n if (!last) return null;\n return {\n turn: o.base + read.turns.length - 1,\n user: last.user.slice(0, MAX_TURN_SIDE),\n assistant: capTail(last.assistant, MAX_TURN_SIDE),\n ...(last.injected ? { injected: true as const } : {}),\n at: last.at,\n };\n}\nconst MAX_TURN_SIDE = 20_000;\n/** What session.attended is read from, the desktop app's link included, and session.launched_by\n * and delegated_by (hook.ts picks all five): the job's own, never the worker's. */\nconst JOB_OWN_ENV = [\n \"CLAUDE_CODE_SESSION_ATTENDED\",\n \"CLAUDE_CODE_HOST_SESSION_ID\",\n \"SESSIONPIPE_ASKED_BY\",\n \"SESSIONPIPE_CONTROL_JOB\",\n \"SESSIONPIPE_LAUNCHED_BY\",\n];\n\n/** Run one hook job end to end: events \u2192 outbox \u2192 sinks. Returns what happened.\n * `claimed` is the job file this run holds: released once the events are in the\n * outbox or the job is back in the queue (`requeued` names its new file). */\nexport async function runJob(\n job: Job,\n opts: {\n state?: string;\n now?: number;\n claimed?: string;\n lockWaitMs?: number;\n /** false: the events go to the outbox and the sinks are left for the caller to drain. */\n drain?: boolean;\n /** The drain's clock (pacing); tests stand in a simulated one. */\n clock?: Clock;\n } & DrainLife = {},\n): Promise<{ events: Event[]; delivered: Record<string, number>; requeued?: string }> {\n const state = opts.state ?? stateDir();\n const cfg = readConfig();\n const adapter = adapterByName(job.harness);\n // A hooks file can outlive the build that wrote it (an update to a sessionpipe without\n // that adapter, a hand-written entry): without this line the events are dropped in\n // silence, with nothing on the machine to say so.\n if (!adapter) {\n log(state, `${job.harness} ${job.event}: no adapter for harness \"${job.harness}\"; event dropped`);\n return { events: [], delivered: {} };\n }\n // Who attends a session, and who launched it, is the job's own word, never the worker's: a\n // worker another session's hook spawned (sweepJobs) carries that session's. A key the job\n // lacks stays absent.\n const env: NodeJS.ProcessEnv = { ...process.env };\n for (const k of JOB_OWN_ENV) delete env[k];\n const r = readHook(adapter, { argv: job.argv, stdin: job.stdin, env: { ...env, ...job.env }, cwd: job.cwd });\n // The adapter did not claim the payload (another harness ran our entry out of a\n // shared hooks file, an event name it does not map, a payload with no session id).\n // Dropped either way, but never in silence: this is where a wrongly-named event\n // lands, and the log is the only thing on the machine that can say so.\n if (!r || !r.session.id) {\n let keys: string[] = [];\n try {\n // Some hooks pass JSON in argv instead of stdin. Only the top-level names\n // belong in this default log: values can contain prompts, paths or secrets.\n const payload: unknown = JSON.parse(job.argv.find((a) => a.startsWith(\"{\")) ?? job.stdin);\n if (payload && typeof payload === \"object\" && !Array.isArray(payload)) keys = Object.keys(payload);\n } catch {}\n log(\n state,\n `${job.harness} ${job.event}: the ${job.harness} adapter did not claim this payload; event dropped; keys: ${keys.join(\", \") || \"(none)\"}`,\n );\n return { events: [], delivered: {} };\n }\n // A session the control daemon started goes by the id its start command named, not\n // the one the harness picked (CONTROL.md \u00A76 `start`; control/aliases.ts): its events\n // say so, and so does everything below. The adapter's own reading of the session\n // (facts, a remote URL) keeps the harness's id.\n const id =\n receiverIdOf(readAliases(aliasFile(path.join(state, \"control\"))), job.harness, r.session.id) ?? r.session.id;\n const claudeEnd = job.harness === \"claude-code\" && r.events.some((e) => e.type === \"session.ended\");\n // The control daemon learns each session's turn state and folder from its events (a\n // message for a session mid-turn waits for its Stop; a pending permission prompt\n // closes when the tool runs). A local round trip, and nothing when no daemon runs.\n if (!claudeEnd) await tellDaemon(state, job, id, r.session.cwd, r.transcript);\n const outbox = new Outbox(state);\n const ref = { harness: job.harness, session: id };\n // Hooks of one session fire close together (parallel subagents, issue #9): poll\n // for the lock, often enough that its siblings can't keep taking it in between,\n // before giving the job back to the queue.\n let release = takeLock(state, ref);\n const lockWait = opts.lockWaitMs ?? LOCK_WAIT_MS;\n for (const until = Date.now() + lockWait; !release && Date.now() < until; ) {\n await new Promise((res) => setTimeout(res, 50));\n release = takeLock(state, ref);\n }\n if (!release) {\n log(state, `${job.harness} ${job.event}: session ${id} locked for ${lockWait / 1000} s, re-queued`);\n const requeued = enqueueJob(state, { ...job, tries: (job.tries ?? 0) + 1 });\n releaseJob(opts.claimed);\n return { events: [], delivered: {}, requeued };\n }\n const events: Event[] = [];\n try {\n const facts = factsState(state);\n const mem = facts.get(job.harness, id);\n // `claude mcp list` fires SessionEnd with a fresh id but no SessionStart or\n // transcript. Check under the session lock, before creating facts or a seq:\n // any existing outbox, facts or transcript is enough to preserve a real end.\n if (\n claudeEnd &&\n !existsSync(outbox.file(ref)) &&\n Object.keys(mem).length === 0 &&\n !(r.transcript && existsSync(r.transcript))\n ) {\n releaseJob(opts.claimed);\n return { events: [], delivered: {} };\n }\n if (claudeEnd) await tellDaemon(state, job, id, r.session.cwd, r.transcript);\n // Heartbeat-class events read no facts (they fire every tool call); the rest refresh them.\n const heavy = !/^(PreToolUse|PostToolUse|PostToolUseFailure|BeforeTool|AfterTool|PostInvocation)$/.test(job.event);\n let learned: Partial<Session> = {};\n if (heavy) {\n // The hook's environment, not the worker's: a swept job may be run by a worker\n // another harness's hook spawned, whose env names none of this harness's homes.\n const hookEnv = { ...process.env, ...job.env };\n const f = adapter.facts(r.session, r.transcript, r.hints ?? {}, facts, hookEnv);\n // A name only the harness's own service knows (claude.ai's): a round trip with the\n // person's sign-in, so only when they turned it on (`sessionpipe titles on`).\n if (cfg.remote_titles && adapter.remoteFacts)\n Object.assign(f, await adapter.remoteFacts(r.session, f, r.transcript, facts, hookEnv));\n const { started_at: _s, last_at: _l, first_ask: _fa, harness_version, ...rest } = f;\n learned = rest;\n if (harness_version) facts.save(job.harness, id, { harness_version });\n facts.save(job.harness, id, { session: { ...(mem.session as object), ...learned } });\n // Antigravity's transcript never says which folder a conversation runs in, and\n // Devin's payload names none either (only DEVIN_PROJECT_DIR in the hook's env);\n // the hooks do. Kept raw (this file is the machine's own, 0600), so the control\n // daemon still knows where to resume it after a restart.\n if (CWD_FROM_HOOKS.has(job.harness) && r.session.cwd && path.isAbsolute(r.session.cwd))\n facts.save(job.harness, id, { cwd: r.session.cwd });\n }\n const known = (mem.session as Partial<Session> | undefined) ?? {};\n const cwdRaw = learned.cwd ?? r.session.cwd ?? known.cwd;\n const git = heavy ? gitFacts(cwdRaw) : {};\n const session: Session = {\n ...known,\n ...learned,\n ...git,\n ...(learned.branch ? { branch: learned.branch } : {}),\n id,\n seq: 0,\n machine: machineName(cfg, os.hostname()),\n ...(cwdRaw ? { cwd: tilde(cwdRaw) } : {}),\n ...(r.session.model ? { model: r.session.model } : {}),\n ...(r.session.launched_by ? { launched_by: r.session.launched_by } : {}),\n ...(r.session.delegated_by ? { delegated_by: r.session.delegated_by } : {}),\n };\n const version = (facts.get(job.harness, id).harness_version as string | undefined) ?? undefined;\n for (const a of r.events) {\n const e = makeEvent(a, {\n harness: { name: job.harness, ...(version ? { version } : {}) },\n session: { ...session, seq: outbox.nextSeq(ref) },\n now: opts.now ?? job.at,\n });\n events.push(e);\n }\n const wantsTranscript = cfg.sinks.some((s) => !s.paused && Math.min(s.tier, s.max_tier ?? 3) >= 2);\n // Tier 2, while a turn runs: what it has said so far, so a receiver can show a long\n // turn before it ends. On the prompt, then at most every PROGRESS_MS on a tool call,\n // and only when it changed. The cursor stays where it is: turn.transcript still\n // delivers the whole turn when it ends.\n const ending = r.events.some((e) => e.type === \"turn.ended\" || e.type === \"session.ended\");\n const starting = r.events.some((e) => e.type === \"turn.started\");\n // A message sent while a turn runs is absorbed by that turn, and Claude Code reports\n // it as a prompt with the running turn's own id. It is not a new turn: where it\n // belongs is the transcript's to say, so its text is no reading of its own.\n const startedId = r.events.find((e) => e.type === \"turn.started\")?.data.turn_id;\n const absorbed = starting && typeof startedId === \"string\" && facts.get(job.harness, id).open_turn === startedId;\n if (wantsTranscript && r.transcript && !ending && (starting || r.events.some((e) => e.type === \"tool.ended\"))) {\n const cur = facts.get(job.harness, id);\n const at = opts.now ?? job.at;\n if (starting || at - (Number(cur.progress_at) || 0) >= PROGRESS_MS) {\n const p = openTurn(adapter.readTranscript(r.transcript, Number(cur.transcript_line) || 0), {\n base: Number(cur.transcript_turns) || 0,\n prompt: !absorbed && typeof r.hints?.prompt === \"string\" ? r.hints.prompt : undefined,\n at,\n });\n const sig = p && `${p.turn}:${p.user.length}:${p.assistant.length}`;\n if (p && (starting || sig !== cur.progress_sig)) {\n events.push(\n makeEvent(\n { type: \"turn.progress\", harnessEvent: job.event, data: { ...p, at: new Date(p.at).toISOString() } },\n {\n harness: { name: job.harness, ...(version ? { version } : {}) },\n session: { ...session, seq: outbox.nextSeq(ref) },\n now: at,\n },\n ),\n );\n facts.save(job.harness, id, { progress_at: at, progress_sig: sig });\n }\n }\n }\n // The turn running now, by its prompt's id, until it ends (for `absorbed`).\n if (starting && typeof startedId === \"string\" && !absorbed) facts.save(job.harness, id, { open_turn: startedId });\n if (ending) facts.save(job.harness, id, { open_turn: null });\n // Tier 2: after a turn ends, the transcript's new turns become turn.transcript events.\n if (\n wantsTranscript &&\n r.transcript &&\n r.events.some((e) => e.type === \"turn.ended\" || e.type === \"session.ended\")\n ) {\n const cur = facts.get(job.harness, id);\n const fromLine = Number(cur.transcript_line) || 0;\n let turnNo = Number(cur.transcript_turns) || 0;\n const read = adapter.readTranscript(r.transcript, fromLine);\n for (const t of read.turns) {\n events.push(\n makeEvent(\n {\n type: \"turn.transcript\",\n harnessEvent: job.event,\n data: {\n turn: turnNo++,\n user: t.user,\n assistant: t.assistant,\n at: new Date(t.at).toISOString(),\n ...(t.injected ? { injected: true as const } : {}),\n },\n },\n {\n harness: { name: job.harness, ...(version ? { version } : {}) },\n session: { ...session, seq: outbox.nextSeq(ref) },\n now: opts.now ?? job.at,\n },\n ),\n );\n }\n if (read.turns.length) facts.save(job.harness, id, { transcript_line: read.line, transcript_turns: turnNo });\n }\n // Tier 1: the account's limits, when the turn left new numbers in the transcript\n // (Codex's rollout; Claude Code's come from its status line, through fromHook).\n if (\n adapter.limits &&\n r.transcript &&\n ending &&\n cfg.sinks.some((s) => !s.paused && Math.min(s.tier, s.max_tier ?? 3) >= 1)\n ) {\n const limits = adapter.limits(r.transcript);\n const sig = limits && JSON.stringify(limits);\n if (limits && sig !== facts.get(job.harness, id).limits_sig) {\n events.push(\n makeEvent(\n { type: \"limits.observed\", harnessEvent: job.event, data: limits },\n {\n harness: { name: job.harness, ...(version ? { version } : {}) },\n session: { ...session, seq: outbox.nextSeq(ref) },\n now: opts.now ?? job.at,\n },\n ),\n );\n facts.save(job.harness, id, { limits_sig: sig });\n }\n }\n // At rest the outbox holds redacted strings (advisory GHSA-8f6f-c3c9-j5p6): the\n // tier-3 fields stay, with their secrets already gone; filterEvent runs the same\n // idempotent pass again per sink.\n outbox.append(\n ref,\n events.map((e) => ({ ...e, session: redactDeep(e.session), data: redactDeep(e.data) })),\n );\n releaseJob(opts.claimed);\n log(\n state,\n `${job.harness} ${job.event}: ${events.map((e) => e.type).join(\",\") || \"nothing\"} (${shortId(job.harness, id)})`,\n );\n } finally {\n release();\n }\n // The session's lock covered the append; the sinks drain without it (the outbox\n // reader leaves a line still being written), so a sibling hook isn't held out for\n // a round trip. A prune holds the `flush` lock, so it goes first: a worker that\n // found the lock taken meanwhile left its events and a `flush.more` mark, and this\n // drain sends them.\n if (Math.random() < 0.02) outbox.prune(cfg.keep_days ?? 30);\n const delivered =\n opts.drain === false\n ? {}\n : ((await drain(state, cfg.sinks, outbox, ref, {\n ...(opts.clock ? { clock: opts.clock } : {}),\n ...(opts.deadline !== undefined ? { deadline: opts.deadline } : {}),\n ...(opts.handoff ? { handoff: opts.handoff } : {}),\n })) ?? {});\n return { events, delivered };\n}\n\n/** One event for a session with no hook behind it: the pool's `limits.observed`, when\n * the daemon read an account's numbers off a request the session made (pool/index.ts\n * `onLimits`). It goes the way a hook's events go \u2014 only with a tier-1 sink, the\n * session's known facts for `session` (its own account_id included), redacted at rest,\n * under the session's lock, then the sinks drain \u2014 so a receiver can't tell it from\n * the status line's. A hook holding the session's lock (its events are being written\n * right then) is waited for, up to `lockWaitMs`; past that the numbers are dropped\n * with a line in the log, never in silence (the live proof lost a turn's switch that\n * way). Null: no sink wants it, or the session stayed busy. */\nexport async function appendObserved(\n state: string,\n harness: string,\n id: string,\n data: DataOf<\"limits.observed\">,\n opts: { now?: number; lockWaitMs?: number } = {},\n): Promise<Event | null> {\n const cfg = readConfig();\n if (!cfg.sinks.some((s) => !s.paused && Math.min(s.tier, s.max_tier ?? 3) >= 1)) return null;\n const outbox = new Outbox(state);\n const ref = { harness, session: id };\n let release = takeLock(state, ref);\n for (const until = Date.now() + (opts.lockWaitMs ?? OBSERVED_LOCK_WAIT_MS); !release && Date.now() < until; ) {\n await new Promise((res) => setTimeout(res, 50));\n release = takeLock(state, ref);\n }\n if (!release) {\n log(state, `${harness} limits.observed for ${shortId(harness, id)} dropped: the session is busy`);\n return null;\n }\n const now = opts.now ?? Date.now();\n let e: Event;\n try {\n const facts = factsState(state);\n const mem = facts.get(harness, id);\n const known = (mem.session as Partial<Session> | undefined) ?? {};\n const version = mem.harness_version as string | undefined;\n e = makeEvent(\n { type: \"limits.observed\", harnessEvent: \"pool\", data },\n {\n harness: { name: harness, ...(version ? { version } : {}) },\n session: { ...known, id, seq: outbox.nextSeq(ref), machine: machineName(cfg, os.hostname()) },\n now,\n },\n );\n outbox.append(ref, [{ ...e, session: redactDeep(e.session), data: redactDeep(e.data) }]);\n } finally {\n release();\n }\n await drain(state, cfg.sinks, outbox, ref);\n return e;\n}\n\n/** What the drain reads the time from and waits with; tests stand in a simulated one. */\nexport interface Clock {\n now(): number;\n sleep(ms: number): Promise<void>;\n}\nconst REAL_CLOCK: Clock = { now: () => Date.now(), sleep: (ms) => new Promise((r) => setTimeout(r, ms)) };\n\n/** A pass of the drain starts at least this long after the machine's last request to\n * a receiver (issue #208): under a few hundred subagents a receiver gets one request\n * a second with everything that piled up, not one per hook. A lone event goes at once. */\nexport const PACE_MS = 1000;\n/** <state>/locks/flush.sent: when this machine last sent a request to a receiver\n * (epoch ms), written as the request leaves. Every worker reads the same file. */\nconst sentFile = (state: string): string => path.join(state, \"locks\", \"flush.sent\");\nfunction lastSent(state: string): number {\n try {\n // Unreadable text is NaN, and a NaN wait waits for nothing.\n return Number(readFileSync(sentFile(state), \"utf8\"));\n } catch {\n return 0;\n }\n}\nfunction noteSent(state: string, at: number): void {\n try {\n mkdirSync(path.dirname(sentFile(state)), { recursive: true, mode: 0o700 });\n writeFileSync(sentFile(state), String(at), { mode: 0o600 });\n } catch {}\n}\n\n/** Whether a receiver (an HTTP sink not paused and not in backoff) has outbox bytes\n * past its cursor: the pass that follows may send it a request. Bytes it will skip\n * (a thinned tool event) count too; reading them is the pass's job. */\nfunction waitingForReceiver(state: string, sinkConfigs: SinkConfig[], outbox: Outbox, now: number): boolean {\n const receivers = sinkConfigs.filter(\n (s) => !s.paused && /^https?:\\/\\//.test(s.url) && !((readBackoff(state, s.name)?.until ?? 0) > now),\n );\n if (!receivers.length) return false;\n const refs = outbox.sessions();\n for (const s of receivers) {\n const cursors = new Cursors(state, s.name);\n const c = cursors.read();\n for (const ref of refs) {\n try {\n if (statSync(outbox.file(ref)).size > (c[cursors.key(ref)] ?? 0)) return true;\n } catch {}\n }\n }\n return false;\n}\n\n/** How long a worker's life must still run for a pass of the drain to start: the\n * pace's wait (\u2264 PACE_MS) and a round trip. A worker that has less leaves the work\n * marked (flush.more) and hands it on (DrainLife.handoff) rather than be ended by its\n * deadline mid-pass, holding events nobody else knows to send. */\nexport const PASS_BUDGET_MS = 3000;\n/** How long a swept job waits for its session's lock before it goes back to the queue. */\nconst SWEEP_LOCK_WAIT_MS = 1000;\n\n/** A worker's remaining life, for the drain and the sweep. Both unset: no limit. */\nexport interface DrainLife {\n /** When the process will be ended (epoch ms, on the drain's clock). */\n deadline?: number;\n /** Called, with the flush lock released, when work is left marked for another\n * worker because this one's life is too short for it (worker.ts starts a drainer). */\n handoff?: () => void;\n}\n\nconst moreFile = (state: string): string => path.join(state, \"locks\", \"flush.more\");\n/** Tell the worker draining now (if any) that the outbox has more for it. */\nfunction markMore(state: string): void {\n try {\n mkdirSync(path.dirname(moreFile(state)), { recursive: true, mode: 0o700 });\n writeFileSync(moreFile(state), \"\", { mode: 0o600 });\n } catch {}\n}\n\n/** One worker drains a machine's sinks at a time (the `flush` lock). Another whose\n * events are in the outbox meanwhile marks that there is more and leaves them to\n * it (null: nothing sent by this one). The one draining looks for the mark after\n * each pass, once it has let the lock go, so events appended during a pass go out\n * in its next pass or with the worker that appended them, never wait for a later\n * hook. Under a flood of hook events this is one uploader sending full batches,\n * not one per event, all refused together (mordast, 2026-10-14: 828 refusals in\n * two minutes, 5 192 jobs queued). A pass that would follow the machine's last\n * request by less than PACE_MS first waits out the rest, holding the lock: the\n * workers that arrive meanwhile only mark that there is more, and the pass sends\n * everything they appended. A pass starts only while the worker's life has\n * PASS_BUDGET_MS left; otherwise the mark stays and the work is handed on. */\nexport async function drain(\n state: string,\n sinkConfigs: SinkConfig[],\n outbox: Outbox,\n first?: { harness: string; session: string },\n o: { clock?: Clock } & DrainLife = {},\n): Promise<Record<string, number> | null> {\n const clock = o.clock ?? REAL_CLOCK;\n const more = moreFile(state);\n let release = takeNamedLock(state, \"flush\");\n if (!release) {\n markMore(state);\n release = takeNamedLock(state, \"flush\");\n if (!release) return null;\n }\n const delivered: Record<string, number> = {};\n for (;;) {\n if (o.deadline !== undefined && clock.now() + PASS_BUDGET_MS > o.deadline) {\n markMore(state);\n release();\n o.handoff?.();\n return delivered;\n }\n try {\n // Never more than PACE_MS: a stamp in the future (a clock set back) can't park it.\n // A pass with nothing for a receiver (a file sink's events, a cursor already at\n // the end) doesn't wait at all.\n const wait = Math.min(PACE_MS, lastSent(state) + PACE_MS - clock.now());\n if (wait > 0 && waitingForReceiver(state, sinkConfigs, outbox, clock.now())) await clock.sleep(wait);\n try {\n unlinkSync(more);\n } catch {}\n for (const [k, v] of Object.entries(await flush(state, sinkConfigs, outbox, first, { clock })))\n delivered[k] = (delivered[k] ?? 0) + v;\n } finally {\n release();\n }\n if (!existsSync(more)) return delivered;\n release = takeNamedLock(state, \"flush\");\n if (!release) return delivered;\n }\n}\n\n/** HTTP.md \u00A72: a sink that answered 429 isn't tried again before its `retry-after`;\n * one that was unreachable (5xx, a timeout) waits 2 s \u00B7 10 s \u00B7 60 s \u00B7 5 min \u00B7 30 min,\n * then every 30 min. Kept in <state>/backoff/<sink>.json, so every worker on the\n * machine waits, not only the one that was refused. Cleared by the next batch the\n * sink takes. */\nexport interface Backoff {\n until: number;\n failures: number;\n why: string;\n}\nconst BACKOFF_MS = [2_000, 10_000, 60_000, 5 * 60_000, 30 * 60_000];\nconst backoffFile = (state: string, sink: string): string => path.join(state, \"backoff\", `${safeId(sink)}.json`);\nexport function readBackoff(state: string, sink: string): Backoff | null {\n try {\n const b = JSON.parse(readFileSync(backoffFile(state, sink), \"utf8\")) as Backoff;\n return typeof b.until === \"number\" ? b : null;\n } catch {\n return null;\n }\n}\nfunction noteFailure(state: string, sink: string, res: DeliveryResult, prev: Backoff | null): Backoff {\n const failures = (prev?.failures ?? 0) + 1;\n const ladder = BACKOFF_MS[Math.min(failures, BACKOFF_MS.length) - 1] as number;\n const wait = Number.isFinite(res.retryAfterMs) ? (res.retryAfterMs as number) : ladder;\n const b = { until: Date.now() + wait, failures, why: `${res.status ?? \"\"} ${res.error ?? \"\"}`.trim() };\n const f = backoffFile(state, sink);\n try {\n mkdirSync(path.dirname(f), { recursive: true, mode: 0o700 });\n writeFileSync(f, JSON.stringify(b), { mode: 0o600 });\n } catch {}\n return b;\n}\nfunction clearBackoff(state: string, sink: string): void {\n try {\n unlinkSync(backoffFile(state, sink));\n } catch {}\n}\n\n/** How long a job waits for its session's lock before it goes back to the queue. */\nconst LOCK_WAIT_MS = 6000;\n/** How long the pool's limits.observed waits for a hook that holds the session. */\nconst OBSERVED_LOCK_WAIT_MS = 2000;\n/** Harnesses whose own files do not name the session's folder: the hooks' cwd is kept. */\nconst CWD_FROM_HOOKS = new Set([\"antigravity\", \"devin\"]);\n\nconst BATCH = 50;\nconst MAX_PER_RUN = 500;\n\n/** Drain every sink from its cursor: this session first, then any backlog. Events\n * from many sessions are packed into one batch of up to BATCH, so a first\n * install's 400-session backfill is nine POSTs, not four hundred (the receiver's\n * write limit ended the first replay at 278 events, 2026-09-28). A cursor moves\n * only after its batch was acknowledged; a refused batch stops the sink for this\n * run and the next worker starts from the same cursors. The sinks drain at once:\n * the session's lock is held for the slowest round trip, not the sum of them (a\n * verdict's config write is synchronous, so two sinks' never interleave). */\nexport async function flush(\n state: string,\n sinkConfigs: SinkConfig[],\n outbox: Outbox,\n first?: { harness: string; session: string },\n o: {\n /** Try a sink in backoff anyway (`sessionpipe replay`). */ force?: boolean;\n /** Stamps the machine's last request (drain's pace). */ clock?: Clock;\n /** Counts, per sink, the tool events it was configured not to be sent (`replay`). */\n thinned?: Record<string, number>;\n } = {},\n): Promise<Record<string, number>> {\n const delivered: Record<string, number> = {};\n const clock = o.clock ?? REAL_CLOCK;\n const sinks = buildSinks(sinkConfigs, {\n locked: (name) => log(state, `sink ${name}: its token is in a keychain this process can't read; left for later`),\n });\n const modes = new Map(sinkConfigs.map((s) => [s.name, toolsMode(s)]));\n const refs = outbox.sessions();\n if (first) {\n const k = `${first.harness}/${safeId(first.session)}`;\n refs.sort((a, b) => (`${a.harness}/${a.session}` === k ? -1 : `${b.harness}/${b.session}` === k ? 1 : 0));\n }\n // A session file this process can't read (a 0600 file a hook left as another user\n // under the same HOME, EACCES; a folder named <id>.jsonl, EISDIR on macOS and Linux,\n // while Windows stats a folder at 0 bytes and reads it as empty) is skipped with a\n // line in the log, and the other sessions still go out to every sink; before, the\n // error threw out of `flush`, so no sink took the sessions listed after that file,\n // and nothing was logged. Its cursors stay where they were, so a file readable again\n // is taken from there on the next drain. Logged once per flush pass, not once per\n // sink (the sinks drain at once), and again on every pass while the file stays\n // unreadable. Only a system error (one with a code) is a skip; anything else is a\n // bug and throws as before.\n const unreadable = new Set<string>();\n const readOrSkip = (ref: OutboxRef, offset: number, room: number): { events: Event[]; offset: number } | null => {\n try {\n return outbox.read(ref, offset, room);\n } catch (e) {\n const code = (e as { code?: unknown }).code;\n if (typeof code !== \"string\") throw e;\n const f = outbox.file(ref);\n if (!unreadable.has(f)) {\n unreadable.add(f);\n log(state, `drain: can't read ${f} (${code}); skipped, its cursors stay`);\n }\n return null;\n }\n };\n const drainSink = async (sink: Sink): Promise<void> => {\n let backoff = readBackoff(state, sink.name);\n if (backoff && !o.force && backoff.until > Date.now()) {\n delivered[sink.name] = 0;\n return;\n }\n const cursors = new Cursors(state, sink.name);\n const c = cursors.read();\n // Issue #208: the tool events this sink is not sent are skipped here, the cursor\n // moving over them. The clock moves with the cursor, only once a batch went\n // through, so a refused batch's tool event is chosen again next time.\n const mode = modes.get(sink.name) as ToolsMode; // buildSinks names each sink after its config\n const tools = new ToolClock(state, sink.name);\n let clockNow = mode === \"all\" ? {} : tools.read();\n let clockNext = { ...clockNow };\n let sent = 0;\n let stalled = false;\n // Pull filtered events session by session into one batch; remember where each\n // session's offset would land if the batch goes through.\n let batch: Event[] = [];\n let pending: Record<string, number> = {};\n // HTTP.md \u00A72 413: halve the batch and try again; one event too large on its own\n // is dropped and logged. A half the receiver drops (400) is dropped and logged on\n // its own, and the other half is still sent: the 400 answered that half, not the\n // batch. A half that is refused for another reason stops the run like any\n // refusal: the cursor stays, and the halves that went through are sent again\n // next time (the receiver counts them as duplicates).\n const deliver = async (events: Event[]): Promise<DeliveryResult> => {\n if (sink instanceof HttpSink) noteSent(state, clock.now());\n const res = await sink.send(events);\n if (res.ok || res.action !== \"split\") return res;\n if (events.length === 1) {\n const e = events[0] as Event;\n log(state, `sink ${sink.name}: 413 ${res.error ?? \"\"} \u2192 event ${e.id} (${e.type}) is too large alone, dropped`);\n return { ok: true, acknowledged: 0, accepted: 0, duplicates: 0, rejected: [{ id: e.id, reason: \"too_large\" }] };\n }\n const part = async (some: Event[]): Promise<DeliveryResult> => {\n const r = await deliver(some);\n if (r.ok || r.action !== \"drop\") return r;\n log(\n state,\n `sink ${sink.name}: ${r.status ?? \"\"} ${r.error ?? \"\"} \u2192 drop, ${some.length} of ${events.length} events`,\n );\n const rejected = some.map((e) => ({ id: e.id, reason: \"dropped\" }));\n return { ok: true, acknowledged: 0, accepted: 0, duplicates: 0, rejected };\n };\n const half = Math.ceil(events.length / 2);\n const a = await part(events.slice(0, half));\n if (!a.ok) return a;\n const b = await part(events.slice(half));\n if (!b.ok) return b;\n return {\n ok: true,\n acknowledged: a.acknowledged + b.acknowledged,\n accepted: (a.accepted ?? 0) + (b.accepted ?? 0),\n duplicates: (a.duplicates ?? 0) + (b.duplicates ?? 0),\n rejected: [...(a.rejected ?? []), ...(b.rejected ?? [])],\n };\n };\n const commitClock = (): void => {\n if (mode === \"all\" || JSON.stringify(clockNext) === JSON.stringify(clockNow)) return;\n tools.write(clockNext);\n clockNow = { ...clockNext };\n };\n const send = async (): Promise<boolean> => {\n if (!batch.length) {\n Object.assign(c, pending);\n pending = {};\n cursors.write(c);\n commitClock();\n return true;\n }\n const res = await deliver(batch);\n if (!res.ok) {\n const action = res.action ?? \"retry\";\n let wait = \"\";\n if (action === \"retry\") {\n backoff = noteFailure(state, sink.name, res, backoff);\n wait = `, waits ${Math.round((backoff.until - Date.now()) / 1000)} s`;\n }\n log(state, `sink ${sink.name}: ${res.status ?? \"\"} ${res.error ?? \"\"} \u2192 ${action}${wait}`);\n if (res.action === \"pause\" || res.action === \"lower-tier\") applySinkVerdict(sink.name, res);\n if (res.action !== \"drop\") {\n batch = [];\n pending = {};\n clockNext = { ...clockNow };\n return false;\n }\n } else if (backoff) {\n clearBackoff(state, sink.name);\n backoff = null;\n }\n sent += batch.length;\n Object.assign(c, pending);\n cursors.write(c);\n commitClock();\n batch = [];\n pending = {};\n return true;\n };\n for (const ref of refs) {\n if (stalled || sent >= MAX_PER_RUN) break;\n let offset = pending[cursors.key(ref)] ?? c[cursors.key(ref)] ?? 0;\n for (;;) {\n const room = BATCH - batch.length;\n const read = readOrSkip(ref, offset, room);\n if (!read) break;\n const { events, offset: next } = read;\n if (!events.length) break;\n for (const e of events) {\n const f = filterEvent(e, sink.tier, sink.pii, { home: os.homedir() });\n if (!f) continue;\n if (sendTool(f, mode, clockNext, cursors.key(ref))) batch.push(f);\n else if (o.thinned) o.thinned[sink.name] = (o.thinned[sink.name] ?? 0) + 1;\n }\n offset = next;\n pending[cursors.key(ref)] = offset;\n if (batch.length >= BATCH) {\n if (!(await send())) {\n stalled = true;\n break;\n }\n if (sent >= MAX_PER_RUN) break;\n }\n }\n }\n if (!stalled && (batch.length || Object.keys(pending).length)) if (!(await send())) stalled = true;\n delivered[sink.name] = sent;\n };\n await Promise.all(sinks.map(drainSink));\n return delivered;\n}\n\nfunction applySinkVerdict(name: string, res: { action?: string; max_tier?: number; error?: string }): void {\n try {\n const cfg = readConfig();\n const s = cfg.sinks.find((x) => x.name === name);\n if (!s) return;\n if (res.action === \"pause\") s.paused = `${new Date().toISOString()} ${res.error ?? \"unauthorized\"}`;\n if (res.action === \"lower-tier\" && typeof res.max_tier === \"number\") s.max_tier = res.max_tier as 0 | 1 | 2 | 3;\n writeConfig(cfg);\n } catch {}\n}\n\nexport { VERSION };\nexport const versionLine = (): string => `sessionpipe ${VERSION}`;\nexport const jobsDir = (state: string): string => path.join(state, \"jobs\");\nexport const hasJobs = (state: string): boolean => existsSync(jobsDir(state));\n", "// SPDX-License-Identifier: Apache-2.0\n// Where sessionpipe keeps its files (spec: the client's Files section).\n// config ~/.config/sessionpipe/config.json SESSIONPIPE_CONFIG\n// state ~/.local/state/sessionpipe/ SESSIONPIPE_STATE\n// Windows %APPDATA%\\sessionpipe\\ and %LOCALAPPDATA%\\sessionpipe\\\nimport os from \"node:os\";\nimport path from \"node:path\";\n\n/** The home directory, read when asked: a test that sets HOME after importing core\n * (or a process whose HOME changes) gets the new one, never one cached at load. */\nexport const homeDir = (): string => os.homedir();\n\nexport function configFile(env: NodeJS.ProcessEnv = process.env): string {\n if (env.SESSIONPIPE_CONFIG) return path.resolve(env.SESSIONPIPE_CONFIG);\n if (process.platform === \"win32\")\n return path.join(env.APPDATA || path.join(homeDir(), \"AppData\", \"Roaming\"), \"sessionpipe\", \"config.json\");\n return path.join(env.XDG_CONFIG_HOME || path.join(homeDir(), \".config\"), \"sessionpipe\", \"config.json\");\n}\n\nexport function stateDir(env: NodeJS.ProcessEnv = process.env): string {\n if (env.SESSIONPIPE_STATE) return path.resolve(env.SESSIONPIPE_STATE);\n if (process.platform === \"win32\")\n return path.join(env.LOCALAPPDATA || path.join(homeDir(), \"AppData\", \"Local\"), \"sessionpipe\");\n return path.join(env.XDG_STATE_HOME || path.join(homeDir(), \".local\", \"state\"), \"sessionpipe\");\n}\n\n/** `~` for the home directory, as the session block writes paths. */\nexport function tilde(p: string, home: string = homeDir()): string {\n if (!p) return p;\n if (p === home) return \"~\";\n return p.startsWith(home + path.sep) || p.startsWith(`${home}/`) ? `~${p.slice(home.length)}` : p;\n}\n\n/** The other way: `~`, `~/\u2026` (or `~\\\u2026`) under the home directory, as a control `start`'s\n * `cwd` may name a folder an event reported (CONTROL.md \u00A73.1). Anything else, `~user`\n * included, is returned as it is. */\nexport function untilde(p: string, home: string = homeDir()): string {\n if (p === \"~\") return home;\n return /^~[\\\\/]/.test(p) ? path.join(home, p.slice(2)) : p;\n}\n\n/** A file-name-safe id. */\nexport const safeId = (s: string): string =>\n String(s)\n .replace(/[^A-Za-z0-9_.-]/g, \"_\")\n .slice(0, 160);\n", "// SPDX-License-Identifier: Apache-2.0\n// Claude Code (Anthropic) \u2014 docs: https://code.claude.com/docs/en/hooks\n// Config: settings.json in EVERY config dir (~/.claude, $CLAUDE_CONFIG_DIR, ~/.claude-*):\n// one per account; hooks written to one never fire for the others. Reads only the\n// hook's stdin, the transcript it names, the per-pid session registry and the config\n// dir's .claude.json account (its id; its email only for control's pair and hello) \u2014\n// or, with no login there, the API its settings.json names (that one key, no token).\n// The one exception is the person's to make: with `remote_titles` on, the sign-in\n// token is read to ask claude.ai for a session's name, and sent nowhere else.\n// Ported from spacesheep-cli lib/sessions.js.\n\nimport { execFileSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { userInfo } from \"node:os\";\nimport path from \"node:path\";\nimport { homeDir } from \"../paths.js\";\nimport { ends, parseLines, recentFiles, scanLines } from \"../readers/files.js\";\nimport { gitFacts } from \"../readers/git.js\";\nimport { isInjected, readClaude } from \"../readers/transcripts.js\";\nimport type { DataOf, HarnessAccount } from \"../types.js\";\nimport {\n formatJson,\n installClaudeShaped,\n installedClaudeShaped,\n OURS,\n readJson,\n readJsonFile,\n uninstallClaudeShaped,\n writeBack,\n} from \"./claude-shaped.js\";\nimport type {\n Adapter,\n BackfillRow,\n FactsState,\n HookCommand,\n HookInput,\n HookResult,\n InstallOptions,\n InstallReport,\n SessionFacts,\n} from \"./types.js\";\n\nconst NAME = \"claude-code\";\nexport const CLAUDE_EVENTS = [\n \"SessionStart\",\n \"UserPromptSubmit\",\n \"PreToolUse\",\n \"PostToolUse\",\n \"PostToolUseFailure\",\n \"PermissionRequest\",\n \"Notification\",\n \"Stop\",\n \"SubagentStart\",\n \"SubagentStop\",\n \"PreCompact\",\n \"PostCompact\",\n \"SessionEnd\",\n \"StopFailure\",\n] as const;\n/** With no sink above tier 0 only the heartbeat needs a tool event: one, not three (issue #12). */\nconst LEAN_SKIP = new Set([\"PreToolUse\", \"PostToolUseFailure\"]);\nconst eventsFor = (lean?: boolean) => (lean ? CLAUDE_EVENTS.filter((e) => !LEAN_SKIP.has(e)) : [...CLAUDE_EVENTS]);\nconst OPTS = {\n matcherFor: (e: string) => (/Tool|Permission/.test(e) ? \"\" : undefined),\n timeout: 5,\n // The hook waits up to 125 s for a verified control answer, and only when control\n // is paired on this machine; everywhere else it exits in milliseconds as before.\n timeoutFor: (e: string) => (e === \"PermissionRequest\" ? 130 : undefined),\n};\nconst DESKTOP_ID = /^local_[A-Za-z0-9-]{1,64}$/;\n\n/** Every Claude Code config dir on this machine. `home` follows the env's HOME, so a\n * caller with its own env (a test) never reaches the real ~/.claude: the install test\n * once rewrote a real settings.json with its fake hook paths on every run. */\nexport function claudeDirs(env: NodeJS.ProcessEnv = process.env, home = env.HOME || homeDir()): string[] {\n const out: string[] = [];\n const add = (d?: string) => {\n if (!d) return;\n const r = path.resolve(d);\n if (!out.includes(r)) out.push(r);\n };\n add(path.join(home, \".claude\"));\n add(env.CLAUDE_CONFIG_DIR);\n try {\n for (const e of readdirSync(home, { withFileTypes: true })) {\n if (!e.isDirectory() || !/^\\.claude[-_.]/.test(e.name)) continue;\n const d = path.join(home, e.name);\n if (existsSync(path.join(d, \"projects\")) || existsSync(path.join(d, \"settings.json\"))) add(d);\n }\n } catch {}\n return out.filter((d, i) => i === 0 || existsSync(d));\n}\nconst settingsIn = (dir: string) => path.join(dir, \"settings.json\");\nconst configDirOf = (transcript: string) => path.dirname(path.dirname(path.dirname(transcript)));\n\nconst validTool = (n: unknown): n is string => typeof n === \"string\" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);\nconst endReason = (r: unknown): \"clear\" | \"logout\" | \"exit\" | \"other\" =>\n r === \"clear\" || r === \"logout\" || r === \"exit\" ? r : r === \"prompt_input_exit\" ? \"exit\" : \"other\";\nconst notifKind = (t: unknown): \"permission\" | \"question\" | \"elicitation\" | \"idle\" | \"error\" =>\n t === \"permission_prompt\"\n ? \"permission\"\n : t === \"idle_prompt\"\n ? \"idle\"\n : t === \"elicitation_dialog\"\n ? \"elicitation\"\n : \"question\";\n\nexport const claudeCode: Adapter = {\n name: NAME,\n events: CLAUDE_EVENTS,\n detect: (env = process.env) => claudeDirs(env).some((d) => existsSync(d)),\n configFiles: (env = process.env) => claudeDirs(env).map(settingsIn),\n install: (cmd, env = process.env, opts: InstallOptions = {}) =>\n claudeDirs(env).flatMap((d) => {\n const hooks = installClaudeShaped(settingsIn(d), NAME, eventsFor(opts.lean), cmd, OPTS);\n // A file the hooks could not go into (not JSON, or refused) gets the one report, theirs.\n const line =\n opts.statusLine === undefined || hooks.skipped ? null : statusLine(settingsIn(d), opts.statusLine, cmd);\n return line ? [hooks, line] : [hooks];\n }),\n uninstall: (env = process.env, opts: InstallOptions = {}) =>\n claudeDirs(env).map((d) => {\n const line = statusLine(settingsIn(d), false);\n const hooks = uninstallClaudeShaped(settingsIn(d), CLAUDE_EVENTS, {\n created: opts.created?.includes(settingsIn(d)),\n });\n // A status line taken out, then the hooks' write refused: \"not touched\" would be\n // false of the file, so the one report says both.\n if (line?.changed && !line.skipped && hooks.skipped)\n return { ...hooks, note: `status line removed, hooks not: ${hooks.note?.replace(/; not touched$/, \"\")}` };\n return line && !hooks.changed && !hooks.skipped ? line : hooks;\n }),\n installed: (cmd, env = process.env, opts: InstallOptions = {}) =>\n claudeDirs(env).map((d) => ({\n file: settingsIn(d),\n state: installedClaudeShaped(settingsIn(d), NAME, eventsFor(opts.lean), cmd, OPTS),\n })),\n\n fromHook(input: HookInput): HookResult | null {\n const event = input.argv[1] ?? \"\";\n let s: Record<string, unknown> = {};\n try {\n // A stdin that is JSON but not an object (`null`, a number) is no payload.\n const j: unknown = input.stdin ? JSON.parse(input.stdin) : {};\n if (j && typeof j === \"object\" && !Array.isArray(j)) s = j as Record<string, unknown>;\n } catch {}\n const id = (s.session_id ?? s.sessionId) as string | undefined;\n if (!id) return null;\n if (notClaudeCode(String(id), s)) return null;\n const session: HookResult[\"session\"] = { id: String(id), cwd: typeof s.cwd === \"string\" ? s.cwd : input.cwd };\n if (typeof s.model === \"string\") session.model = s.model;\n if (typeof s.agent_id === \"string\" && event.startsWith(\"Subagent\") === false && s.agent_id)\n session.parent_id = null;\n const turn_id = typeof s.prompt_id === \"string\" ? s.prompt_id : undefined;\n const tool = validTool(s.tool_name) ? s.tool_name : undefined;\n const call_id = typeof s.tool_use_id === \"string\" ? s.tool_use_id : undefined;\n const ms = typeof s.duration_ms === \"number\" ? Math.round(s.duration_ms) : undefined;\n const agent_id = typeof s.agent_id === \"string\" ? s.agent_id : undefined;\n // PermissionRequest carries no tool_use_id (recorded 2026-09-28): the id is a hash\n // of the prompt, the tool and its input, so the same prompt yields the same id and\n // a later control answer can name it.\n const permissionId = () =>\n `perm-${createHash(\"sha256\")\n .update(`${turn_id ?? \"\"}|${tool ?? \"\"}|${JSON.stringify(s.tool_input ?? null)}`)\n .digest(\"hex\")\n .slice(0, 16)}`;\n const ev = (type: string, data: Record<string, unknown>) => ({ type, data: prune(data), harnessEvent: event });\n const events: HookResult[\"events\"] = [];\n switch (event) {\n case \"SessionStart\":\n events.push(ev(\"session.started\", { source: pickSource(s.source ?? s.trigger) }));\n break;\n case \"UserPromptSubmit\": {\n // Only a string prompt is read; anything else is left unread rather than guessed at.\n const prompt = typeof s.prompt === \"string\" ? s.prompt : undefined;\n events.push(\n ev(\"turn.started\", {\n turn_id,\n prompt_chars: prompt === undefined ? undefined : [...prompt].length,\n // Claude Code's hook schema documents `source`\n // (user | sdk | system | loop_wakeup | schedule_wakeup | poll_event) and says a\n // payload may omit it while the field rolls out; 2.1.304's payload has no such\n // key at all. Forwarded verbatim, shape-guarded only: the list is the harness's\n // to extend.\n submitted_by: harnessWord(s.source),\n // One of Claude Code's own prompt wrappers: the prompt is a block the harness\n // wrote, so it is not the person's words. Absent is unknown, never \"the person\" \u2014\n // and `true` is never \"no person caused this turn\", because a Talk or\n // `sessionpipe wait` delivery hands the person's own message over inside a\n // <task-notification> (PROTOCOL.md \u00A74).\n injected: prompt !== undefined && injectedBlock(prompt) ? true : undefined,\n }),\n );\n break;\n }\n case \"PreToolUse\":\n if (tool) events.push(ev(\"tool.started\", { tool, call_id, turn_id, agent_id, input: s.tool_input }));\n break;\n case \"PostToolUse\":\n if (tool)\n events.push(\n ev(\"tool.ended\", {\n tool,\n call_id,\n turn_id,\n agent_id,\n ms,\n ok: true,\n input: s.tool_input,\n output: s.tool_response,\n }),\n );\n break;\n case \"PostToolUseFailure\":\n if (tool)\n events.push(\n ev(\"tool.ended\", {\n tool,\n call_id,\n turn_id,\n agent_id,\n ms,\n ok: false,\n error: errText(s.error ?? s.tool_response),\n input: s.tool_input,\n output: s.tool_response,\n }),\n );\n break;\n case \"PermissionRequest\":\n events.push(\n ev(\"attention.needed\", {\n attention_id: call_id ?? permissionId(),\n kind: \"permission\",\n tool,\n message: tool ? `${tool}${describe(s.tool_input)}` : undefined,\n agent_id,\n }),\n );\n break;\n case \"Notification\":\n events.push(\n ev(\"attention.needed\", {\n attention_id: `notif-${Date.now()}`,\n kind: notifKind(s.notification_type),\n message: typeof s.message === \"string\" ? s.message.slice(0, 200) : undefined,\n agent_id,\n }),\n );\n break;\n case \"Stop\":\n // In-flight background work (shell commands, monitors, subagents, workflows)\n // that will wake the session when it finishes: a paused session, not a done one\n // (Claude Code's `background_tasks`, in every Stop recorded since 2.1.258). A count, never what they run.\n events.push(ev(\"turn.ended\", { turn_id, reason: \"stop\", background: backgroundCount(s.background_tasks) }));\n break;\n case \"StopFailure\":\n // The turn ended on an API error (rate_limit, overloaded, authentication_failed,\n // server_error \u2026); the payload key is `error` (issue #10, recorded 2026-09-28).\n events.push(\n ev(\"turn.ended\", {\n turn_id,\n reason: \"error\",\n error: typeof s.error === \"string\" ? s.error.slice(0, 200) : undefined,\n }),\n );\n break;\n case \"SubagentStart\":\n events.push(\n ev(\"subagent.started\", {\n agent_id: String(s.agent_id ?? \"\"),\n agent_type: typeof s.agent_type === \"string\" ? s.agent_type : undefined,\n }),\n );\n break;\n case \"SubagentStop\":\n // The same list, with this subagent still in it: the count once it is out, so a\n // receiver holding the session as waiting on its work sees the count fall (#153).\n events.push(\n ev(\"subagent.ended\", {\n agent_id: String(s.agent_id ?? \"\"),\n agent_type: typeof s.agent_type === \"string\" ? s.agent_type : undefined,\n background: backgroundCount(s.background_tasks, String(s.agent_id ?? \"\")),\n }),\n );\n break;\n case \"PreCompact\":\n events.push(ev(\"context.compacted\", { trigger: s.trigger === \"manual\" ? \"manual\" : \"auto\" }));\n break;\n case \"PostCompact\":\n break; // PreCompact already said it; a second event would double-count\n case \"SessionEnd\":\n events.push(ev(\"session.ended\", { reason: endReason(s.reason) }));\n break;\n case \"StatusLine\": {\n // Not a hook: our status line command (install --status-line), which hook.ts\n // hands on only when the limits changed.\n const limits = claudeLimits(s.rate_limits);\n if (!limits) return null;\n events.push(ev(\"limits.observed\", limits));\n break;\n }\n default:\n return null;\n }\n const hints: Record<string, unknown> = {};\n // The prompt, for the turn's first turn.progress: the transcript may not hold it\n // yet when the worker runs. Never an event field of its own (tier 2 carries it).\n if (event === \"UserPromptSubmit\" && typeof s.prompt === \"string\") hints.prompt = s.prompt;\n const host = input.env.CLAUDE_CODE_HOST_SESSION_ID;\n if (host && DESKTOP_ID.test(host)) hints.desktop_id = host;\n // Whether a person attends the session, in Claude Code's own word (\"1\" or \"0\" in a hook's\n // environment); a turn the control daemon runs on the person's signed message is theirs.\n // facts() turns it into session.attended once it knows whether the session has a link.\n const attended = input.env.CLAUDE_CODE_SESSION_ATTENDED;\n if (attended === \"1\" || attended === \"0\") hints.attended = attended === \"1\" || !!input.env.SESSIONPIPE_CONTROL_JOB;\n // The registry file is found while the process tree stands (the detached\n // worker's parent is gone by the time it runs). Heartbeat-class events skip it.\n if (event !== \"PreToolUse\" && event !== \"PostToolUse\") hints.claude_pid = claudePid(input.env);\n const r: HookResult = { session, events, hints };\n if (typeof s.transcript_path === \"string\") r.transcript = s.transcript_path;\n return r;\n },\n\n readTranscript(file, fromLine) {\n let text = \"\";\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return { turns: [], line: fromLine };\n }\n return readClaude(text.split(\"\\n\"), fromLine);\n },\n\n facts(session, transcript, hints, state): SessionFacts {\n const out: SessionFacts = {};\n if (transcript) Object.assign(out, claudeFacts(transcript, session.id, hints, state));\n const cwd = out.cwd ?? session.cwd;\n Object.assign(out, gitFacts(cwd), out.branch ? { branch: out.branch } : {});\n if (!out.url && typeof hints.desktop_id === \"string\") out.url = `claude://code/continue/${hints.desktop_id}`;\n // A Remote Control session says \"0\" though a person drives it from claude.ai: it runs as a\n // headless child of `claude remote-control`. Its link is what tells it from a `claude -p`;\n // CLAUDE_CODE_ENVIRONMENT_KIND cannot, since a `claude -p` started inside a Remote Control\n // session inherits \"bridge\", while the registry entry it writes has no bridge id of its own.\n if (typeof hints.attended === \"boolean\") out.attended = hints.attended || !!out.url;\n return out;\n },\n\n remoteFacts: (session, local, transcript, state, env = process.env) =>\n claudeRemoteFacts(session.id, local, transcript, state, env),\n\n backfill(sinceMs, env = process.env): BackfillRow[] {\n const rows: BackfillRow[] = [];\n const files: string[] = [];\n for (const d of claudeDirs(env))\n files.push(...recentFiles(path.join(d, \"projects\"), 1, sinceMs, (n) => /^[0-9a-f-]{36}\\.jsonl$/.test(n)));\n const noState: FactsState = { get: () => ({}), save: () => {} };\n for (const f of files) {\n const id = path.basename(f, \".jsonl\");\n const facts = claudeFacts(f, id, {}, noState);\n if (!facts.started_at || !facts.last_at) continue;\n const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;\n const cwd = rest.cwd;\n rows.push({\n session: { id, ...rest, ...gitFacts(cwd), ...(rest.branch ? { branch: rest.branch } : {}) },\n started_at,\n last_at,\n transcript: f,\n });\n }\n return rows;\n },\n};\n\n/** sessionpipe's own waiter (CONTROL.md \u00A78), which a session runs as a background command\n * to stay reachable: it ends only when a message arrives, so it is not work the session\n * waits on. Matched by its command, whatever path or shim runs it. */\nconst WAITER_RE = /(?:^|[\\s\"'`/\\\\])sessionpipe(?:\\.\\w+)?[\"']?\\s+wait(?:\\s|$)/;\n\n/** How much background work Claude Code lists that will wake the session when it ends\n * (`background_tasks`, in every Stop and SubagentStop since 2.1.258): a count, never what\n * it runs. Not counted: sessionpipe's own waiter, and the subagent whose own stop this is,\n * which Claude Code still lists as running (issue #153). Undefined when the harness\n * doesn't say. */\nexport function backgroundCount(tasks: unknown, ending?: string): number | undefined {\n if (!Array.isArray(tasks)) return undefined;\n return tasks.filter((t) => {\n const task = t as { id?: unknown; command?: unknown } | null;\n if (ending !== undefined && task?.id === ending) return false;\n return !(typeof task?.command === \"string\" && WAITER_RE.test(task.command));\n }).length;\n}\n\n/** Claude Code runs a status line only on events (a new assistant message, a window's\n * reset), and the hook sends at most one change a minute, so a turn's last numbers\n * would wait for the next prompt. The timer runs it again while the session is idle. */\nconst LINE_REFRESH_S = 60;\n\n/** Claude Code hands an account's limits only to its status line command\n * (https://code.claude.com/docs/en/statusline), so with `--status-line` ours is that\n * command. Someone else's status line is never replaced. Null: nothing to report. */\nfunction statusLine(file: string, want: boolean, cmd?: HookCommand): InstallReport | null {\n const f = readJsonFile(file);\n if (!f.ok) return null; // the hooks' report already says why\n const cur = f.value.statusLine as { command?: unknown; refreshInterval?: unknown } | undefined;\n const ours = typeof cur?.command === \"string\" && OURS.test(cur.command);\n const command = cmd?.([NAME, \"StatusLine\"]).command;\n if (want && cur && !ours)\n return { file, changed: false, skipped: true, note: \"a status line of your own is set; limits are not read\" };\n if (want ? ours && cur?.command === command && cur?.refreshInterval === LINE_REFRESH_S : !ours) return null;\n if (want) f.value.statusLine = { type: \"command\", command, refreshInterval: LINE_REFRESH_S };\n else delete f.value.statusLine;\n return (\n writeBack(file, formatJson(f.value, f), f.existed) ?? {\n file,\n changed: true,\n note: want ? \"status line written (account limits)\" : \"status line removed\",\n }\n );\n}\n\n/** Epoch seconds as RFC 3339; anything that is not a plausible time is no time. */\nconst epochIso = (t: unknown): string | undefined =>\n typeof t === \"number\" && t > 0 && t < 1e11 ? new Date(t * 1000).toISOString() : undefined;\n\nconst WINDOW_MINUTES: Record<string, number> = { five_hour: 300, seven_day: 10_080 };\n\n/** The status line's `rate_limits`: `five_hour`, `seven_day` and, behind a gateway,\n * `spend_limit`, each with `used_percentage` and `resets_at` in epoch seconds. Only\n * Pro and Max accounts (or a gateway) have them, and only after a session's first\n * response; a window drops out once it resets. */\nexport function claudeLimits(rl: unknown): DataOf<\"limits.observed\"> | null {\n if (!rl || typeof rl !== \"object\" || Array.isArray(rl)) return null;\n const windows: DataOf<\"limits.observed\">[\"windows\"] = [];\n for (const [id, w] of Object.entries(rl as Record<string, { used_percentage?: unknown; resets_at?: unknown }>)) {\n if (!/^[a-z0-9_]{1,64}$/.test(id) || typeof w?.used_percentage !== \"number\" || w.used_percentage < 0) continue;\n windows.push(\n prune({\n id,\n used_percent: w.used_percentage,\n window_minutes: WINDOW_MINUTES[id],\n resets_at: epochIso(w.resets_at),\n }),\n );\n }\n return windows.length ? { windows: windows.slice(0, 8) } : null;\n}\n\nfunction prune<T extends Record<string, unknown>>(o: T): T {\n for (const k of Object.keys(o)) if (o[k] === undefined) delete o[k];\n return o;\n}\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n/** Another harness reading this settings file, not Claude Code. Several harnesses\n * import `~/.claude/settings.json` hooks, so our entries there can be run by a tool\n * that has its own adapter: it would arrive under the wrong name, with ids that mean\n * nothing to a Claude Code reader. Every Claude Code payload names a transcript and a\n * UUID session (all 21 recorded fixtures do); a payload with neither is somebody else's. */\nexport function notClaudeCode(id: string, s: Record<string, unknown>): boolean {\n return typeof s.transcript_path !== \"string\" && !UUID.test(id);\n}\nfunction pickSource(v: unknown): string {\n return typeof v === \"string\" && [\"startup\", \"resume\", \"clear\", \"compact\", \"fork\"].includes(v) ? v : \"unknown\";\n}\n/** One of the harness's own words for who submitted a prompt, forwarded as is (PROTOCOL.md\n * \u00A74, `turn.started.submitted_by`). Not an enum of ours: a value we have never seen is\n * still carried, so the guard is the shape the spec allows \u2014 a bare word of at most 40\n * characters, which is all a receiver may show or group by. Tier 0 is not redacted\n * (PRIVACY.md \u00A72), so that shape is the only guard there is. */\nconst harnessWord = (v: unknown): string | undefined =>\n typeof v === \"string\" && /^[A-Za-z0-9][A-Za-z0-9_.-]{0,39}$/.test(v) ? v : undefined;\n/** Claude Code's own prompt wrappers, as a CLOSED list: a task notification, a peer message,\n * a system reminder, a local command's output, a slash command's expansion \u2014 with or\n * without a closing tag, after trimming. Narrower than the reader's `isInjected` on\n * purpose, because the two directions do not cost the same. A wrapper we have not recorded\n * yet reads as unknown and a receiver keeps the turn, which is what it does today; a shape\n * test wide enough to catch every wrapper also fires on a person who pasted nothing but an\n * SVG, an HTML page or a JSX element, and a recency board acting on that would drop one of\n * the person's own turns. Dropping one record from a transcript costs little, which is why\n * the reader's test stays wide; ADAPTERS.md (Claude Code \u00B7 Quirks) says why they differ. */\nconst WRAPPER =\n /^<(?:task-notification|cross-session-message|system-reminder|local-command-stdout|command-[a-z-]+)[\\s/>]/i;\nconst injectedBlock = (prompt: string): boolean => WRAPPER.test(prompt.trim());\nfunction errText(v: unknown): string | undefined {\n if (typeof v === \"string\") return v.slice(0, 2000);\n if (v && typeof v === \"object\") {\n const e = v as { error?: unknown; message?: unknown };\n if (typeof e.error === \"string\") return e.error.slice(0, 2000);\n if (typeof e.message === \"string\") return e.message.slice(0, 2000);\n }\n return undefined;\n}\nfunction describe(input: unknown): string {\n if (!input || typeof input !== \"object\") return \"\";\n const i = input as Record<string, unknown>;\n const v = i.command ?? i.file_path ?? i.path ?? i.url ?? i.pattern;\n return typeof v === \"string\" ? ` ${v.slice(0, 120)}` : \"\";\n}\n\n// --- facts: title, url, model, account, from Claude Code's own files ------------------\n\nconst NAME_RANK: Record<string, \"custom\" | \"harness\"> = {\n user: \"custom\",\n auto: \"harness\",\n hook: \"harness\",\n peer: \"harness\",\n};\ninterface Registry {\n sessionId: string | null;\n name: string | null;\n name_source: \"custom\" | \"harness\" | null;\n bridge: string | null;\n}\nfunction registryEntry(j: unknown): Registry | null {\n if (!j || typeof j !== \"object\") return null;\n const r = j as { sessionId?: unknown; name?: unknown; nameSource?: string; bridgeSessionId?: unknown };\n const source = r.nameSource ? NAME_RANK[r.nameSource] : undefined;\n return {\n sessionId: r.sessionId ? String(r.sessionId) : null,\n name: source && typeof r.name === \"string\" && r.name.trim() ? r.name.trim() : null,\n name_source: source ?? null,\n bridge: typeof r.bridgeSessionId === \"string\" ? r.bridgeSessionId : null,\n };\n}\nconst liveCache = new Map<string, Map<string, Registry>>();\nfunction claudeLive(dir: string): Map<string, Registry> {\n const hit = liveCache.get(dir);\n if (hit) return hit;\n const map = new Map<string, Registry>();\n let names: string[] = [];\n try {\n names = readdirSync(path.join(dir, \"sessions\")).filter((n) => n.endsWith(\".json\"));\n } catch {}\n for (const n of names) {\n try {\n const e = registryEntry(JSON.parse(readFileSync(path.join(dir, \"sessions\", n), \"utf8\")));\n if (e?.sessionId) map.set(e.sessionId, e);\n } catch {}\n }\n liveCache.set(dir, map);\n return map;\n}\nfunction claudeLiveByPid(dir: string, pid: unknown): Registry | null {\n if (!pid) return null;\n try {\n return registryEntry(JSON.parse(readFileSync(path.join(dir, \"sessions\", `${pid}.json`), \"utf8\")));\n } catch {\n return null;\n }\n}\n/** The Claude Code process that ran this hook: the nearest ancestor with a registry\n * file (hooks run under a shell, so usually the parent's parent). */\nexport function claudePid(env: NodeJS.ProcessEnv = process.env): number | null {\n try {\n const dirs = claudeDirs(env);\n const has = (pid: number) => dirs.some((d) => existsSync(path.join(d, \"sessions\", `${pid}.json`)));\n let pid = process.ppid;\n if (has(pid)) return pid;\n const parent = parentOf();\n for (let hop = 0; hop < 6 && pid > 1; hop++) {\n pid = parent(pid);\n if (pid > 1 && has(pid)) return pid;\n }\n } catch {}\n return null;\n}\nfunction parentOf(): (pid: number) => number {\n if (process.platform === \"linux\") {\n return (pid) => {\n try {\n const st = readFileSync(`/proc/${pid}/stat`, \"utf8\");\n return Number(st.slice(st.lastIndexOf(\")\") + 2).split(\" \")[1]) || 0;\n } catch {\n return 0;\n }\n };\n }\n const table = new Map<number, number>();\n try {\n const out = execFileSync(\"ps\", [\"-axo\", \"pid=,ppid=\"], {\n encoding: \"utf8\",\n timeout: 2000,\n maxBuffer: 8 * 1024 * 1024,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n for (const l of out.split(\"\\n\")) {\n const m = /^\\s*(\\d+)\\s+(\\d+)/.exec(l);\n if (m) table.set(Number(m[1]), Number(m[2]));\n }\n } catch {}\n return (pid) => table.get(pid) || 0;\n}\nconst EMAIL_RE = /^[^@\\s]{1,64}@[^@\\s]{1,189}$/;\n/** Who a config dir is signed in as, from its .claude.json (the default dir keeps it\n * at ~/.claude.json): the account id, and the email it signs in with. A dir with no\n * claude.ai login that runs on another API is that API's account (apiAccount). */\nfunction claudeLogin(dir: string, home = homeDir()): { id: string; email?: string; name?: string } | null {\n const files = [path.join(dir, \".claude.json\")];\n if (path.resolve(dir) === path.join(home, \".claude\")) files.unshift(path.join(home, \".claude.json\"));\n for (const f of files) {\n try {\n const a = (\n JSON.parse(readFileSync(f, \"utf8\")) as { oauthAccount?: { accountUuid?: unknown; emailAddress?: unknown } }\n ).oauthAccount;\n if (typeof a?.accountUuid === \"string\" && a.accountUuid) {\n const email = typeof a.emailAddress === \"string\" && EMAIL_RE.test(a.emailAddress) ? a.emailAddress : undefined;\n return { id: a.accountUuid.slice(0, 128), ...(email ? { email } : {}) };\n }\n } catch {}\n }\n return apiAccount(dir);\n}\n/** Anthropic's own API: a key for it is an Anthropic account no local file names. */\nconst ANTHROPIC_HOST = /(^|\\.)anthropic\\.com$/i;\n/** A config dir whose settings send Claude Code to another API (`env.ANTHROPIC_BASE_URL`:\n * Kimi on a Modal endpoint, GLM on Z.ai) has no claude.ai login, so its account is the\n * endpoint: `api-` and 16 hex of the SHA-256 of its origin for events (the same on every\n * machine that uses it), and its host as the name control's pair and hello give it.\n * Only that one key is read; the tokens and headers beside it never are. */\nfunction apiAccount(dir: string): { id: string; name: string } | null {\n const base = (readJson(settingsIn(dir)) as { env?: { ANTHROPIC_BASE_URL?: unknown } }).env?.ANTHROPIC_BASE_URL;\n if (typeof base !== \"string\") return null;\n let u: URL;\n try {\n u = new URL(base.trim());\n } catch {\n return null;\n }\n if ((u.protocol !== \"https:\" && u.protocol !== \"http:\") || !u.hostname || ANTHROPIC_HOST.test(u.hostname))\n return null;\n // The origin alone: a path or a user:password@ can carry a key.\n const origin = `${u.protocol}//${u.host}`;\n return { id: `api-${createHash(\"sha256\").update(origin).digest(\"hex\").slice(0, 16)}`, name: u.host };\n}\nconst accountCache = new Map<string, string | null>();\n/** The account id for `session.account_id`: never the email. Events go to every sink,\n * a team board included; the email (or an API's host) reaches only a receiver the\n * person paired with, in control's pair and hello (claudeAccounts). */\nfunction claudeAccount(dir: string): string | null {\n const hit = accountCache.get(dir);\n if (hit !== undefined) return hit;\n const acct = claudeLogin(dir)?.id ?? null;\n accountCache.set(dir, acct);\n return acct;\n}\n/** Every account signed in across these config dirs, read fresh (a person signs in to\n * another account without restarting anything), for control's pair and hello. */\nexport function claudeAccounts(dirs: string[], home = homeDir()): HarnessAccount[] {\n const out: HarnessAccount[] = [];\n for (const d of dirs) {\n const l = claudeLogin(d, home);\n if (!l) continue;\n const seen = out.find((a) => a.id === l.id);\n if (!seen) out.push({ harness: NAME, ...l });\n else if (!seen.email && l.email) seen.email = l.email;\n }\n return out;\n}\n/** Each config dir with the account it is signed in as, for whoever asks the harness a\n * question per dir (control's hello asks `claude auth status` for each). Dirs naming\n * nobody are left out. */\nexport function claudeAccountDirs(dirs: string[], home = homeDir()): { id: string; dir: string }[] {\n const out: { id: string; dir: string }[] = [];\n for (const d of dirs) {\n const l = claudeLogin(d, home);\n if (l) out.push({ id: l.id, dir: d });\n }\n return out;\n}\nconst LINK_RE = /^https:\\/\\/claude\\.ai\\/code\\/session_[A-Za-z0-9]+$/;\nfunction transcriptLink(records: Record<string, unknown>[]): string | null {\n for (let i = records.length - 1; i >= 0; i--) {\n const j = records[i] as { type?: string; subtype?: string; url?: unknown; bridgeSessionId?: unknown };\n if (j.type === \"system\" && j.subtype === \"bridge_status\" && typeof j.url === \"string\" && LINK_RE.test(j.url))\n return j.url;\n if (j.type === \"bridge-session\" && typeof j.bridgeSessionId === \"string\") {\n const m = /^(?:cse|session)_([A-Za-z0-9]+)$/.exec(j.bridgeSessionId);\n if (m) return `https://claude.ai/code/session_${m[1]}`;\n }\n }\n return null;\n}\n/** A first ask as the Claude Code app titles an untitled session: whitespace\n * collapsed, whole up to 50 characters, else its first 50 and \"...\". */\nexport function appTitle(text: string): string {\n const t = String(text).replace(/\\s+/g, \" \").trim();\n return t.length > 50 ? `${t.slice(0, 50)}...` : t;\n}\nfunction askText(j: Record<string, unknown> | null): string | null {\n if (!j || j.type !== \"user\" || j.isMeta || j.isSidechain) return null;\n const origin = j.origin as { kind?: string } | undefined;\n if (origin?.kind && origin.kind !== \"human\") return null;\n const c = (j.message as { content?: unknown } | undefined)?.content;\n const text = (\n typeof c === \"string\"\n ? c\n : Array.isArray(c)\n ? c\n .filter((b) => b && b.type === \"text\" && typeof b.text === \"string\")\n .map((b) => b.text as string)\n .join(\"\\n\")\n : \"\"\n ).trim();\n if (!text || isInjected(text)) return null;\n return text;\n}\nfunction firstAsk(records: Record<string, unknown>[]): string | null {\n for (const j of records) {\n const t = askText(j);\n if (t) return appTitle(t);\n }\n return null;\n}\nfunction scanTitles(file: string, from: number, found: { custom?: string | null; ai?: string | null }) {\n const out = { custom: found.custom ?? null, ai: found.ai ?? null, upto: from };\n let size = 0;\n try {\n size = statSync(file).size;\n } catch {\n return out;\n }\n if (size < from) {\n out.upto = 0;\n out.custom = out.ai = null;\n from = 0;\n }\n // Only lines carrying a title record are parsed; the rest of the new bytes are skipped.\n let pos = 0;\n scanLines<null>(\n file,\n (l) => {\n pos += Buffer.byteLength(l, \"utf8\") + 1;\n if (pos <= from || l.indexOf('-title\"') < 0) return null;\n try {\n const j = JSON.parse(l) as { type?: string; customTitle?: string; aiTitle?: string };\n if (j.type === \"custom-title\" && j.customTitle) out.custom = j.customTitle;\n if (j.type === \"ai-title\" && j.aiTitle) out.ai = j.aiTitle;\n } catch {}\n return null;\n },\n 512 * 1024 * 1024,\n );\n out.upto = size;\n return out;\n}\n\n/** A session's title and where it came from, best first: /rename (registry \"user\"\n * name or custom-title) \u2192 Claude Code's own title (registry \"auto\" name or\n * ai-title) \u2192 the first ask, marked first-ask so a real title later replaces it. */\nexport function claudeFacts(\n file: string,\n sessionId: string,\n hints: Record<string, unknown>,\n state: FactsState,\n): SessionFacts {\n const { head, tail } = ends(file, 64 * 1024, 256 * 1024);\n const H = parseLines(head);\n const T = parseLines(tail);\n const out: SessionFacts = {};\n const id = sessionId || path.basename(file, \".jsonl\");\n const mem = state.get(NAME, id);\n let custom: string | null = null;\n let ai: string | null = null;\n for (const j of H.concat(T)) {\n if (j.type === \"custom-title\" && typeof j.customTitle === \"string\") custom = j.customTitle;\n if (j.type === \"ai-title\" && typeof j.aiTitle === \"string\") ai = j.aiTitle;\n }\n let size = 0;\n try {\n size = statSync(file).size;\n } catch {}\n if (!custom && !ai && size > 64 * 1024 + 256 * 1024) {\n const s = scanTitles(file, Number(mem.scan_upto) || 0, {\n custom: mem.scan_custom as string | null,\n ai: mem.scan_ai as string | null,\n });\n custom = s.custom;\n ai = s.ai;\n state.save(NAME, id, { scan_upto: s.upto, scan_custom: s.custom, scan_ai: s.ai });\n }\n const dir = configDirOf(file);\n const live = claudeLive(dir).get(id) ?? claudeLiveByPid(dir, hints.claude_pid);\n if (live?.name) state.save(NAME, id, { name: live.name, name_source: live.name_source });\n const bridge =\n live?.bridge && /^session_[A-Za-z0-9]+$/.test(live.bridge) ? `https://claude.ai/code/${live.bridge}` : null;\n const url = bridge ?? transcriptLink(T) ?? transcriptLink(H) ?? (typeof mem.url === \"string\" ? mem.url : null);\n if (url) {\n out.url = url;\n state.save(NAME, id, { url });\n }\n const name = live?.name\n ? { title: live.name, source: live.name_source as \"custom\" | \"harness\" }\n : typeof mem.name === \"string\"\n ? { title: mem.name, source: mem.name_source as \"custom\" | \"harness\" }\n : null;\n const pick =\n (name && name.source === \"custom\" && name) ||\n (custom && { title: custom, source: \"custom\" as const }) ||\n name ||\n (ai && { title: ai, source: \"harness\" as const });\n let ask = typeof mem.first_ask === \"string\" ? mem.first_ask : firstAsk(H);\n if (!ask && size > 64 * 1024 && !mem.first_ask_none) {\n ask = scanLines<string>(file, (l) => {\n try {\n const t = askText(JSON.parse(l) as Record<string, unknown>);\n return t ? appTitle(t) : null;\n } catch {\n return null;\n }\n });\n if (!ask && size > 16 * 1024 * 1024) state.save(NAME, id, { first_ask_none: true });\n }\n if (ask) {\n out.first_ask = ask;\n if (!mem.first_ask) state.save(NAME, id, { first_ask: ask });\n }\n if (pick) {\n out.title = String(pick.title).slice(0, 120);\n out.title_source = pick.source;\n } else if (ask) {\n out.title = ask;\n out.title_source = \"first-ask\";\n }\n const acct = claudeAccount(dir);\n if (acct) out.account_id = acct;\n for (let i = T.length - 1; i >= 0; i--) {\n const m = (T[i] as { message?: { model?: string } }).message?.model;\n if (T[i]?.type === \"assistant\" && m && m !== \"<synthetic>\") {\n out.model = m;\n break;\n }\n }\n const meta = [...T].reverse().find((j) => j.version) ?? H.find((j) => j.version);\n if (meta?.version) out.harness_version = String(meta.version);\n const times = H.concat(T)\n .map((j) => Date.parse(String(j.timestamp ?? \"\")))\n .filter((n) => n > 0);\n if (times.length) {\n out.started_at = Math.min(...times);\n out.last_at = Math.max(...times);\n }\n const withCwd = H.find((j) => typeof j.cwd === \"string\");\n if (withCwd) {\n out.cwd = withCwd.cwd as string;\n if (typeof withCwd.gitBranch === \"string\" && withCwd.gitBranch !== \"HEAD\") out.branch = withCwd.gitBranch;\n }\n return out;\n}\n\n// --- claude.ai's name for a session, only when the person turned it on ------------------\n\n/** claude.ai names a Remote Control session itself, seconds after its first message, and\n * never tells the machine: no file here holds that name, so the Claude app shows one\n * and every local reader another. With `remote_titles` on, the worker asks claude.ai the\n * way Claude Code reads a session. Again after a minute while it has no name yet, then\n * every half hour: a rename in the Claude app reaches the transcript as a custom-title,\n * which outranks this anyway. */\nconst REMOTE_RETRY_MS = 60_000;\nconst REMOTE_REFRESH_MS = 30 * 60_000;\nconst CLAUDE_API = \"https://api.anthropic.com\";\nconst KEYCHAIN = \"Claude Code-credentials\";\n/** The worker holds the session's lock while it asks, and a sibling hook waits 6 s for it. */\nconst REMOTE_TIMEOUT_MS = 3000;\nconst KEYCHAIN_TIMEOUT_MS = 2000;\n/** A session is a few kB; anything past this is not one. */\nconst REMOTE_MAX_BYTES = 1 << 20;\n/** A name as Claude Code itself cleans one: control and bidirectional characters become\n * spaces, runs of space one, the ends trimmed. */\nconst cleanName = (s: string) =>\n s\n .replace(/[\\p{Cc}\\u061C\\u200E\\u200F\\u202A-\\u202E\\u2066-\\u2069]/gu, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n\n/** claude.ai's name for a session with a claude.ai link, unless the person named it\n * here: `{title, title_source: \"harness\"}`, or nothing. */\nexport async function claudeRemoteFacts(\n id: string,\n local: SessionFacts,\n transcript: string | undefined,\n state: FactsState,\n env: NodeJS.ProcessEnv,\n now = Date.now(),\n): Promise<Partial<SessionFacts>> {\n if (!transcript || !local.url || !LINK_RE.test(local.url) || local.title_source === \"custom\") return {};\n const mem = state.get(NAME, id);\n let title = typeof mem.remote_title === \"string\" ? mem.remote_title : null;\n if (now - (Number(mem.remote_at) || 0) >= (title ? REMOTE_REFRESH_MS : REMOTE_RETRY_MS)) {\n // Marked before the call: a claude.ai that doesn't answer isn't asked on every hook.\n state.save(NAME, id, { remote_at: now });\n const token = claudeAiToken(configDirOf(transcript), env, now);\n const got = token ? await claudeAiTitle(local.url.slice(local.url.lastIndexOf(\"/\") + 1), token) : undefined;\n if (got !== undefined) {\n title = got?.slice(0, 200) ?? null;\n state.save(NAME, id, { remote_title: title });\n }\n }\n return title ? { title: title.slice(0, 120), title_source: \"harness\" } : {};\n}\n\n/** The keychain items Claude Code keeps a config dir's sign-in under: `Claude\n * Code-credentials`, and for a CLAUDE_CONFIG_DIR, `-` and 8 hex of the SHA-256 of that\n * path as it was set (tried as the hook's env names it, then as the dir itself). */\nfunction keychainServices(dir: string, env: NodeJS.ProcessEnv): string[] {\n const named = (p: string) =>\n `${KEYCHAIN}-${createHash(\"sha256\").update(p.normalize(\"NFC\")).digest(\"hex\").slice(0, 8)}`;\n const out = new Set<string>();\n if (env.CLAUDE_CONFIG_DIR && path.resolve(env.CLAUDE_CONFIG_DIR) === dir) out.add(named(env.CLAUDE_CONFIG_DIR));\n if (dir === path.join(env.HOME || homeDir(), \".claude\")) out.add(KEYCHAIN);\n out.add(named(dir));\n return [...out];\n}\n\n/** A token this process already read, by config dir, until a minute before it expires:\n * `sessionpipe titles on` asks about every recent session, one `security` each would be\n * slow. In memory only, for the life of the process. */\nconst tokenCache = new Map<string, { token: string; expires: number }>();\n/** The access token Claude Code signed in to `dir` with: from the macOS keychain, else\n * from `<dir>/.credentials.json`. Never written anywhere, never logged. One that has\n * expired is not refreshed, since that would rotate Claude Code's own login: the lookup\n * waits for Claude Code to refresh it. */\nexport function claudeAiToken(dir: string, env: NodeJS.ProcessEnv, now: number): string | null {\n const hit = tokenCache.get(dir);\n if (hit && hit.expires > now + 60_000) return hit.token;\n const raw: string[] = [];\n if (process.platform === \"darwin\")\n for (const service of keychainServices(dir, env)) {\n try {\n const user = env.USER || userInfo().username;\n const account = /^[\\w.-]+$/.test(user) ? user : \"claude-code-user\";\n raw.push(\n execFileSync(\"security\", [\"find-generic-password\", \"-a\", account, \"-w\", \"-s\", service], {\n encoding: \"utf8\",\n timeout: KEYCHAIN_TIMEOUT_MS,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }),\n );\n break;\n } catch {}\n }\n try {\n raw.push(readFileSync(path.join(dir, \".credentials.json\"), \"utf8\"));\n } catch {}\n for (const r of raw) {\n try {\n // Claude Code writes the item as hex (`-X`); `security -w` prints it back as text,\n // or as that hex where it isn't printable.\n const t = r.trim();\n const o = (\n JSON.parse(/^(?:[0-9a-f]{2})+$/i.test(t) ? Buffer.from(t, \"hex\").toString(\"utf8\") : t) as {\n claudeAiOauth?: { accessToken?: unknown; expiresAt?: unknown };\n }\n ).claudeAiOauth;\n if (typeof o?.accessToken !== \"string\" || !o.accessToken) continue;\n const expires = typeof o.expiresAt === \"number\" ? o.expiresAt : Number.POSITIVE_INFINITY;\n if (expires <= now + 60_000) continue;\n tokenCache.set(dir, { token: o.accessToken, expires });\n return o.accessToken;\n } catch {}\n }\n return null;\n}\n\ninterface RemoteSession {\n id?: unknown;\n title?: unknown;\n}\n/** claude.ai's name for the session `bridge` (`session_\u2026`), from the endpoint Claude Code\n * reads a session from: a string, null when it has none yet, undefined when it couldn't\n * be asked or wouldn't say (offline, a 401 or 404, an answer about another session), so\n * the last answer stands. The token goes to api.anthropic.com alone: a redirect is\n * refused, never followed. */\nexport async function claudeAiTitle(bridge: string, token: string): Promise<string | null | undefined> {\n try {\n const r = await fetch(`${CLAUDE_API}/v1/code/sessions/${bridge}`, {\n headers: { Authorization: `Bearer ${token}`, \"anthropic-version\": \"2023-06-01\", \"User-Agent\": \"sessionpipe\" },\n redirect: \"error\",\n signal: AbortSignal.timeout(REMOTE_TIMEOUT_MS),\n });\n if (r.status !== 200 || Number(r.headers.get(\"content-length\")) > REMOTE_MAX_BYTES) return undefined;\n const text = await r.text();\n if (text.length > REMOTE_MAX_BYTES) return undefined;\n // claude.ai answers `{response_shape: {\u2026}}` (conformance/remote/claude-code); Claude Code\n // also reads `session`, and a bare session is taken too. Asked as `session_\u2026`, it names\n // the session `cse_\u2026`: the same one.\n const j = JSON.parse(text) as (RemoteSession & { response_shape?: RemoteSession; session?: RemoteSession }) | null;\n const s = j?.response_shape ?? j?.session ?? j;\n const bare = (x: string) => x.replace(/^(?:session|cse)_/, \"\");\n if (typeof s?.id !== \"string\" || bare(s.id) !== bare(bridge)) return undefined;\n return (typeof s.title === \"string\" && cleanName(s.title)) || null;\n } catch {\n return undefined;\n }\n}\n\n/** For tests: each hook runs in a fresh process; a test that edits the registry mid-run clears the cache. */\nexport const _resetClaudeCaches = (): void => {\n liveCache.clear();\n accountCache.clear();\n tokenCache.clear();\n};\n", "// SPDX-License-Identifier: Apache-2.0\n// Reading the ends of large files, JSON lines, and recent files under a root.\nimport { closeSync, fstatSync, openSync, readdirSync, readSync, statSync } from \"node:fs\";\nimport path from \"node:path\";\n\n/** The first and last bytes of a file, as lines \u2014 a transcript can be tens of MB. */\nexport function ends(file: string, headBytes: number, tailBytes: number): { head: string[]; tail: string[] } {\n let fd: number | undefined;\n try {\n fd = openSync(file, \"r\");\n const size = fstatSync(fd).size;\n const read = (pos: number, len: number) => {\n const b = Buffer.alloc(len);\n const n = readSync(fd as number, b, 0, len, pos);\n return b.subarray(0, n).toString(\"utf8\");\n };\n const head = read(0, Math.min(size, headBytes));\n const tail = size > headBytes ? read(Math.max(0, size - tailBytes), Math.min(size, tailBytes)) : head;\n return { head: head.split(\"\\n\").slice(0, -1), tail: tail.split(\"\\n\").slice(size > headBytes ? 1 : 0) };\n } catch {\n return { head: [], tail: [] };\n } finally {\n if (fd !== undefined)\n try {\n closeSync(fd);\n } catch {}\n }\n}\n\nexport const parseLines = (lines: string[]): Record<string, unknown>[] =>\n lines\n .map((l) => {\n try {\n return JSON.parse(l) as Record<string, unknown>;\n } catch {\n return null;\n }\n })\n .filter((x): x is Record<string, unknown> => !!x);\n\n/** Read line by line up to `cap` bytes, stopping when `visit` returns a value. */\nexport function scanLines<T>(file: string, visit: (line: string) => T | null, cap = 16 * 1024 * 1024): T | null {\n let fd: number | undefined;\n try {\n fd = openSync(file, \"r\");\n const buf = Buffer.alloc(1024 * 1024);\n let pos = 0;\n let rest: Buffer = Buffer.alloc(0);\n while (pos < cap) {\n const n = readSync(fd, buf, 0, buf.length, pos);\n if (!n) break;\n pos += n;\n let chunk = Buffer.concat([rest, buf.subarray(0, n)]);\n let nl = chunk.indexOf(10);\n while (nl >= 0) {\n const r = visit(chunk.subarray(0, nl).toString(\"utf8\"));\n if (r !== null) return r;\n chunk = chunk.subarray(nl + 1);\n nl = chunk.indexOf(10);\n }\n rest = chunk;\n }\n } catch {\n } finally {\n if (fd !== undefined)\n try {\n closeSync(fd);\n } catch {}\n }\n return null;\n}\n\nexport function recentFiles(root: string, depth: number, sinceMs: number, match: (name: string) => boolean): string[] {\n const out: string[] = [];\n const walk = (dir: string, d: number) => {\n let names: import(\"node:fs\").Dirent[] = [];\n try {\n names = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of names) {\n const p = path.join(dir, e.name);\n if (e.isDirectory()) {\n if (d < depth) walk(p, d + 1);\n continue;\n }\n if (!match(e.name)) continue;\n try {\n if (statSync(p).mtimeMs >= sinceMs) out.push(p);\n } catch {}\n }\n };\n walk(root, 0);\n return out;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// The repo and branch of a folder, from git itself; credentials stripped from the\n// remote. Cached per folder for the life of the process (a worker run).\nimport { execFileSync } from \"node:child_process\";\n\nconst cache = new Map<string, { repo?: string; branch?: string }>();\n\nexport function gitFacts(cwd: string | undefined): { repo?: string; branch?: string } {\n if (!cwd) return {};\n const hit = cache.get(cwd);\n if (hit) return hit;\n const run = (args: string[]) => {\n try {\n return execFileSync(\"git\", [\"-C\", cwd, ...args], {\n encoding: \"utf8\",\n timeout: 1500,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n } catch {\n return \"\";\n }\n };\n const out: { repo?: string; branch?: string } = {};\n const repo = run([\"remote\", \"get-url\", \"origin\"]);\n if (repo) out.repo = stripCredentials(repo);\n const branch = run([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]);\n if (branch && branch !== \"HEAD\") out.branch = branch;\n cache.set(cwd, out);\n return out;\n}\n\n/** `https://user:token@host/x` \u2192 `https://host/x`; `git@github.com:a/b.git` \u2192 `github.com/a/b`. */\nexport function stripCredentials(url: string): string {\n // The ssh forms first: the `git@` they are known by is what the credential strip removes.\n const ssh = /^(?:ssh:\\/\\/)?git@([^:/]+)[:/](.+?)(?:\\.git)?$/.exec(url);\n if (ssh) return `${ssh[1]}/${ssh[2]}`;\n return url\n .replace(/\\/\\/[^/@]*@/, \"//\")\n .replace(/^https?:\\/\\//, \"\")\n .replace(/\\.git$/, \"\");\n}\n", "// SPDX-License-Identifier: Apache-2.0\n\n/** A thread another thread started \u2014 a subagent, a review \u2014 names a `source.subagent`\n * in its own metadata (the rollout's first record). A fork the person made carries\n * only `forked_from_id`, and is a session of its own. */\nexport function codexSubagent(meta: Record<string, unknown> | null | undefined): boolean {\n if (meta?.type !== \"session_meta\") return false;\n const p = meta.payload as { source?: { subagent?: unknown } } | undefined;\n return !!p?.source?.subagent;\n}\n\n/** A fork starts with its own metadata, then replays the parent's history. Only\n * an event naming the new thread establishes where its own records begin. */\nexport function codexReplayBoundary(meta: Record<string, unknown> | null | undefined) {\n if (meta?.type !== \"session_meta\") return undefined;\n const p = meta.payload as { id?: unknown; forked_from_id?: unknown; source?: { subagent?: unknown } } | undefined;\n if (!p || (!p.forked_from_id && !p.source?.subagent)) return undefined;\n return (record: Record<string, unknown> | null) =>\n typeof p.id === \"string\" &&\n !!p.id &&\n record?.type === \"event_msg\" &&\n (record.payload as { thread_id?: unknown } | undefined)?.thread_id === p.id;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Transcript readers: complete turns (a person's message + everything the assistant\n// said before the next one), each with the line it ends on, and the line the\n// cursor may advance to once every turn is delivered. Ported from spacesheep-cli\n// lib/memory.js. One reader per harness file format; nothing else is vendor code.\nimport { codexReplayBoundary } from \"../adapters/codex-rollout.js\";\nimport type { TranscriptRead } from \"../adapters/types.js\";\n\ninterface Msg {\n role: \"user\" | \"assistant\";\n text: string;\n at: number;\n line: number;\n /** This ask reached the session from somewhere other than its own keyboard: another\n * local session handed it over, or a channel pushed it, which are the two ways a\n * control daemon reaches a session in place (spec/CONTROL.md \u00A76.3, \u00A78). The words are\n * the person's; the arrival was not. */\n injected?: true;\n}\n\nexport function pairTurns(msgs: Msg[], endLine: number): TranscriptRead {\n const turns: {\n user: string;\n assistant: string;\n at: number;\n startLine: number;\n endLine: number;\n injected?: true;\n }[] = [];\n let cur: (typeof turns)[number] | null = null;\n for (const m of msgs) {\n if (m.role === \"user\") {\n // Never overwrite a prompt that has no reply yet: in an agentic turn the reply\n // comes after tool calls, and a second user record is not a new question.\n if (cur && !cur.assistant) {\n cur.user += `\\n\\n${m.text}`;\n continue;\n }\n if (cur) turns.push(cur);\n cur = {\n user: m.text,\n assistant: \"\",\n at: m.at,\n startLine: m.line,\n endLine: m.line,\n ...(m.injected ? { injected: true as const } : {}),\n };\n } else {\n if (!cur) cur = { user: \"\", assistant: \"\", at: m.at, startLine: m.line, endLine: m.line };\n cur.assistant += (cur.assistant ? \"\\n\\n\" : \"\") + m.text;\n cur.endLine = m.line;\n }\n }\n if (cur?.assistant) turns.push(cur);\n // A user message with no reply yet stays unsent: the cursor stops before it.\n const line = cur && !cur.assistant ? cur.startLine : endLine;\n const out: TranscriptRead = {\n turns: turns.map((t) => ({\n user: t.user,\n assistant: t.assistant,\n at: t.at,\n endLine: t.endLine,\n ...(t.injected ? { injected: true as const } : {}),\n })),\n line,\n };\n if (cur && !cur.assistant && cur.user)\n out.open = { user: cur.user, at: cur.at, ...(cur.injected ? { injected: true as const } : {}) };\n return out;\n}\n\n/** Injected context the person never typed: <tag>\u2026</tag> wrappers (the tag may carry\n * attributes, as Claude Code's `<cross-session-message from=\"\u2026\">` does), HTML comments,\n * slash-command expansions. */\nexport const isInjected = (text: string): boolean =>\n (/^<[a-z_-]+(?:\\s[^>]*)?>/i.test(text) && /<\\/[a-z_-]+>\\s*$/i.test(text)) ||\n /^<!--\\s/.test(text) ||\n /^<command-/.test(text);\n\nconst parse = (raw: string): Record<string, unknown> | null => {\n try {\n return JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return null;\n }\n};\n/** The line after the last whole one. A trailing line that does not parse has no\n * newline yet (a write in progress): the cursor stops before it, or the prompt in\n * it would never be read. */\nconst endOf = (lines: string[]) => {\n const last = lines[lines.length - 1];\n if (last === undefined) return 0;\n return last === \"\" || parse(last) === null ? lines.length - 1 : lines.length;\n};\nconst textParts = (c: unknown, kinds: string[]): string => {\n if (typeof c === \"string\") return c;\n if (!Array.isArray(c)) return \"\";\n return c\n .filter(\n (b) =>\n b &&\n typeof b === \"object\" &&\n kinds.includes((b as { type?: string }).type ?? \"\") &&\n typeof (b as { text?: unknown }).text === \"string\",\n )\n .map((b) => (b as { text: string }).text)\n .join(\"\\n\");\n};\n\n/** An origin whose message is somebody's words rather than the harness talking to\n * itself: `human` is the person at the keyboard, `peer` is a message another local\n * session handed this one \u2014 which is how a control daemon reaches a session in place,\n * so the words in it are the person's too (spec/CONTROL.md \u00A76.3). Older versions wrote\n * no origin at all, which also means the person. */\nconst SPOKEN = new Set([\"human\", \"peer\"]);\n\n/** Claude Code's own preamble around a message from another session: a first line\n * introducing it, and a closing paragraph telling the model what a peer message is and\n * isn't. Neither is anything the person typed. */\nconst PEER_INTRO = /sent a message[^\\n]*:$/;\nconst PEER_OUTRO = \"\\n\\nThis came from another \";\n/** The envelope the sender wraps its text in (cli control/inbox.ts). */\nconst ENVELOPE = /^<cross-session-message\\b[^>]*>\\n([\\s\\S]*)\\n<\\/cross-session-message>$/;\n\n/** What the person actually typed, out of a peer row. Claude Code records the parsed\n * body on the origin when the sender declared its permission class; otherwise the row\n * holds the preamble and the message together, and the preamble is cut off here. A row\n * read without this is a turn whose ask is the harness's own words. */\nexport function peerAsk(origin: unknown, text: string): string {\n const body = (origin as { body?: unknown } | undefined)?.body;\n let t = typeof body === \"string\" && body.trim() ? body : text;\n if (t !== body) {\n const nl = t.indexOf(\"\\n\");\n if (nl > 0 && PEER_INTRO.test(t.slice(0, nl))) t = t.slice(nl + 1);\n const outro = t.indexOf(PEER_OUTRO);\n if (outro > 0) t = t.slice(0, outro);\n }\n return (ENVELOPE.exec(t.trim())?.[1] ?? t).trim();\n}\n\n/** A message sent while a turn was running, absorbed by that turn. Claude Code writes it\n * as an attachment, never as a `user` record: {type:\"attachment\",\n * attachment:{type:\"queued_command\", prompt, commandMode:\"prompt\", origin:{kind:\"human\"}}}.\n * A message from another session is the same record with `origin.kind: \"peer\"` and the\n * text already on its own; a task notification shares it with another `commandMode`.\n * Older versions wrote no origin. */\nconst queuedPrompt = (a: unknown): string => {\n if (!a || typeof a !== \"object\") return \"\";\n const q = a as { type?: unknown; commandMode?: unknown; origin?: { kind?: string }; prompt?: unknown };\n if (q.type !== \"queued_command\" || (q.commandMode ?? \"prompt\") !== \"prompt\") return \"\";\n // Older versions wrote no origin at all, which means the person at the keyboard.\n const kind = q.origin?.kind ?? \"human\";\n if (!SPOKEN.has(kind)) return \"\";\n const text = textParts(q.prompt, [\"text\"]).trim();\n return kind === \"peer\" ? peerAsk(q.origin, text) : text;\n};\n\n/** A channel's message (Claude Code `--channels`: Telegram, sessionpipe's own) as Claude\n * Code records it: {type:\"user\", isMeta:true, origin:{kind:\"channel\", server}, content:\n * `<channel source=\"\u2026\" \u2026>\\n\u2026\\n</channel>`}. The words inside are the person's, sent from\n * elsewhere: the turn's ask. Anything else in that record is nobody's words. */\nconst channelText = (text: string): string => /^<channel\\b[^>]*>([\\s\\S]*)<\\/channel>$/.exec(text)?.[1]?.trim() ?? \"\";\n\n/** Claude Code / Droid: {type:\"user\"|\"assistant\", message:{content}, timestamp, isMeta, isSidechain, origin},\n * plus the person's mid-turn messages (queuedPrompt) and messages from a channel (channelText). */\nexport function readClaude(lines: string[], from: number): TranscriptRead {\n const msgs: Msg[] = [];\n for (let i = from; i < lines.length; i++) {\n const j = lines[i] ? parse(lines[i] as string) : null;\n if (!j || j.isSidechain) continue;\n const origin = j.origin as { kind?: string } | undefined;\n // A message another session handed this one in place is the person's words reaching\n // the session: a turn's ask. Claude Code marks the row `isMeta` because the preamble\n // is the harness's; the ask is not. Two mechanisms deliver one: a channel (#161,\n // `origin.kind === \"channel\"`, the words wrapped in `<channel>`) and the inbox (#150,\n // `origin.kind === \"peer\"`, a framed message `peerAsk` unwraps).\n const channel = j.type === \"user\" && origin?.kind === \"channel\";\n const peer = origin?.kind === \"peer\";\n if (j.type === \"user\" && (j.isCompactSummary || j.repeatsCompactSummary)) continue;\n if (j.type === \"user\" && !channel && !peer && (j.isMeta || (origin?.kind && origin.kind !== \"human\"))) continue;\n const at = Date.parse(String(j.timestamp ?? \"\")) || Date.now();\n const c = (j.message as { content?: unknown } | undefined)?.content;\n if (channel) {\n // Like a peer's: the words are the person's, the arrival was not the keyboard's.\n const text = channelText(textParts(c, [\"text\"]).trim());\n if (text) msgs.push({ role: \"user\", text, at, line: i, injected: true });\n } else if (j.type === \"user\") {\n const raw = textParts(c, [\"text\"]).trim();\n const text = peer ? peerAsk(origin, raw) : raw;\n // The tag test applies to a peer row like any other. `peerAsk` has taken the\n // envelope off already, so what is left is the message itself \u2014 and any local\n // program of this uid can write one, so a body shaped like harness markup must not\n // become a turn whose ask is that markup, published as something the person typed.\n if (!text || isInjected(text)) continue;\n msgs.push({ role: \"user\", text, at, line: i, ...(peer ? { injected: true as const } : {}) });\n } else if (j.type === \"assistant\" && Array.isArray(c)) {\n const text = textParts(c, [\"text\"]).trim();\n if (text) msgs.push({ role: \"assistant\", text, at, line: i });\n } else if (j.type === \"attachment\") {\n const text = queuedPrompt(j.attachment);\n if (text && !isInjected(text)) msgs.push({ role: \"user\", text, at, line: i });\n }\n }\n return pairTurns(msgs, endOf(lines));\n}\n\n/** What Codex writes into a session as a \"user\" message that the person never typed:\n * `<environment_context>` and the like, and the AGENTS.md it read (`# AGENTS.md\n * instructions for <folder>` followed by `<INSTRUCTIONS>`). */\nexport function codexInjected(text: string): boolean {\n return /^<[a-z_-]+>/i.test(text) || /^# AGENTS\\.md instructions\\b/i.test(text);\n}\n\n/** Codex rollout: {type:\"response_item\", payload:{type:\"message\", role, content:[{type:\"input_text\"|\"output_text\", text}]}}. */\nexport function readCodex(lines: string[], from: number): TranscriptRead {\n const msgs: Msg[] = [];\n const boundary = codexReplayBoundary(parse(lines.find((line) => line.trim()) ?? \"\"));\n // Find the boundary from the header even when resuming inside the replay.\n // Keep absolute line numbers: the delivery cursor refers to the original file.\n const start = boundary ? lines.findIndex((line) => boundary(parse(line))) : 0;\n if (start < 0) return pairTurns(msgs, endOf(lines));\n for (let i = Math.max(from, start); i < lines.length; i++) {\n const j = lines[i] ? parse(lines[i] as string) : null;\n if (!j || j.type !== \"response_item\") continue;\n const p = j.payload as { type?: string; role?: string; content?: unknown } | undefined;\n if (!p || p.type !== \"message\") continue;\n const at = Date.parse(String(j.timestamp ?? \"\")) || Date.now();\n const text = textParts(p.content, [\"input_text\", \"output_text\"]).trim();\n if (!text) continue;\n if (p.role === \"user\") {\n if (codexInjected(text)) continue; // Codex's own injections, not the person's words\n msgs.push({ role: \"user\", text, at, line: i });\n } else if (p.role === \"assistant\") msgs.push({ role: \"assistant\", text, at, line: i });\n }\n return pairTurns(msgs, endOf(lines));\n}\n\n/** Gemini CLI chat log: {type:\"user\"|\"gemini\", content|message|text, timestamp}. */\nexport function readGemini(lines: string[], from: number): TranscriptRead {\n const msgs: Msg[] = [];\n for (let i = from; i < lines.length; i++) {\n const j = lines[i] ? parse(lines[i] as string) : null;\n if (!j) continue;\n const role = j.type === \"user\" ? \"user\" : j.type === \"gemini\" || j.type === \"model\" ? \"assistant\" : null;\n if (!role) continue;\n const raw = j.content ?? j.message ?? j.text;\n const text = (typeof raw === \"string\" ? raw : textParts(raw, [\"text\"])).trim();\n if (!text) continue;\n const at = Date.parse(String(j.timestamp ?? \"\")) || Date.now();\n msgs.push({ role, text, at, line: i });\n }\n return pairTurns(msgs, endOf(lines));\n}\n\n/** Antigravity: one step per line, type USER_INPUT | PLANNER_RESPONSE, content with <USER_REQUEST> wrapping. */\nexport function readAntigravity(lines: string[], from: number): TranscriptRead {\n const msgs: Msg[] = [];\n for (let i = from; i < lines.length; i++) {\n const j = lines[i] ? parse(lines[i] as string) : null;\n if (!j || typeof j.content !== \"string\") continue;\n const at = Date.parse(String(j.created_at ?? j.timestamp ?? \"\")) || Date.now();\n if (j.type === \"USER_INPUT\") {\n const m = /<USER_REQUEST>([\\s\\S]*?)(?:<\\/USER_REQUEST>|$)/.exec(j.content);\n const text = (m ? (m[1] as string) : j.content).trim();\n if (text) msgs.push({ role: \"user\", text, at, line: i });\n } else if (j.type === \"PLANNER_RESPONSE\") {\n const text = j.content.trim();\n if (text) msgs.push({ role: \"assistant\", text, at, line: i });\n }\n }\n return pairTurns(msgs, endOf(lines));\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// The hooks file shape Claude Code introduced and Codex, Droid and (with\n// `version: 1`) Cursor and Copilot reuse: {\"hooks\":{\"<Event>\":[{\"matcher\"?,\n// \"hooks\":[{\"type\":\"command\",\"command\",\"timeout\"}]}]}}. Ours is one entry per\n// event; a re-install replaces a stale entry of ours (an old node path) and never\n// touches anyone else's.\n//\n// Two rules from issue #8: a file that is not plain JSON (a comment, a trailing\n// comma, a write in progress) is never written back \u2014 the harness is skipped and\n// the report says why; and a file is written back in its own shape (indent, final\n// newline, a byte-order mark) so uninstall leaves it byte-identical. A file that did not exist before\n// install is reported `created`, and uninstall deletes it once it is empty again.\nimport { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { HookCommand, InstallReport } from \"./types.js\";\n\nexport interface ClaudeShapedOptions {\n /** Which events take a `matcher` (tool and permission events). */\n matcherFor: (event: string) => string | undefined;\n /** Seconds (milliseconds for Gemini CLI). */\n timeout: number;\n /** A longer timeout for one event (Claude Code's PermissionRequest, which may wait\n * for a verified control answer: spec/CONTROL.md \u00A76). */\n timeoutFor?: (event: string) => number | undefined;\n /** Extra top-level keys the file must carry (Cursor/Copilot: version: 1). */\n top?: Record<string, unknown>;\n}\n\n/** A command of ours: a path that contains \"sessionpipe\" and ends in hook.js (or in\n * sessionpipe-hook, the launcher that outlives a node upgrade), then a harness and an event. */\nexport const OURS = /(?:sessionpipe[^\"\\n]*[\\\\/]hook\\.js|[\\\\/]sessionpipe-hook)\"?\\s+[a-z][a-z-]*\\s+[A-Za-z]+\\s*$/;\nexport const isOurs = (h: { hooks?: { command?: string }[] }): boolean =>\n Array.isArray(h?.hooks) && h.hooks.some((x) => typeof x.command === \"string\" && OURS.test(x.command));\n\nexport type JsonFile =\n | {\n ok: true;\n existed: true;\n value: Record<string, unknown>;\n raw: string;\n indent: string;\n eol: string;\n finalNewline: boolean;\n bom: boolean;\n }\n | {\n ok: true;\n existed: false;\n value: Record<string, unknown>;\n raw: \"\";\n indent: \" \";\n eol: \"\\n\";\n finalNewline: true;\n bom: false;\n }\n | { ok: false; existed: true; reason: string };\n\n/** Read a JSON config file with its shape. Never guesses: a parse failure is a\n * refusal to touch the file, not an empty object. */\nexport function readJsonFile(file: string): JsonFile {\n let raw: string;\n try {\n raw = readFileSync(file, \"utf8\");\n } catch {\n return { ok: true, existed: false, value: {}, raw: \"\", indent: \" \", eol: \"\\n\", finalNewline: true, bom: false };\n }\n // A UTF-8 byte-order mark (PowerShell 5's UTF8 encoding, older Windows editors): JSON.parse\n // refuses it, the harnesses read past it. Kept, and written back, like the line ends.\n const bom = raw.charCodeAt(0) === 0xfeff;\n if (bom) raw = raw.slice(1);\n let value: unknown;\n try {\n value = JSON.parse(raw);\n } catch {\n const hint = /^\\s*\\/\\/|\\/\\*/m.test(raw) ? \"not plain JSON (it has comments)\" : \"not plain JSON\";\n return { ok: false, existed: true, reason: hint };\n }\n if (!value || typeof value !== \"object\" || Array.isArray(value))\n return { ok: false, existed: true, reason: \"not a JSON object\" };\n const eol = raw.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const m = /^( +|\\t+)\\S/m.exec(raw);\n const indent = m?.[1] ?? \" \";\n return {\n ok: true,\n existed: true,\n value: value as Record<string, unknown>,\n raw,\n indent,\n eol,\n finalNewline: /\\r?\\n$/.test(raw),\n bom,\n };\n}\n\n/** Serialise in the file's own shape. */\nexport function formatJson(\n value: unknown,\n f: { indent: string; eol: string; finalNewline: boolean; bom?: boolean },\n): string {\n const text = JSON.stringify(value, null, f.indent).replace(/\\n/g, f.eol);\n return (f.bom ? \"\\uFEFF\" : \"\") + (f.finalNewline ? text + f.eol : text);\n}\n\n/** Back-compat helper for callers that only read. */\nexport function readJson(file: string): Record<string, unknown> {\n const f = readJsonFile(file);\n return f.ok ? f.value : {};\n}\n\n/** The report for a config file the system would not let us change, with the error's\n * code, so `install` and `uninstall` go on to the next file instead of ending on this\n * one. Node opens a file for truncation before it writes, so an error from the open\n * (EACCES; EPERM, which Windows gives a read-only file; EROFS) or from making its\n * folder left the file as it was, and so does a delete refused; the note says so. Only\n * an error from the write itself (ENOSPC, EIO) can have left the file cut short, and\n * that note says to check it instead. */\nfunction refused(\n file: string,\n e: unknown,\n what: \"write\" | \"remove\",\n existed: boolean,\n): InstallReport & { note: string } {\n const { code, syscall } = e as NodeJS.ErrnoException;\n const why = `can't ${what} it (${code ?? String(e)})`;\n if (what === \"remove\" || syscall === \"open\" || syscall === \"mkdir\")\n return { file, changed: false, skipped: true, note: `${why}; not touched` };\n return {\n file,\n changed: true,\n skipped: true,\n ...(existed ? {} : { created: true }),\n note: `${why} part way; check the file before the harness reads it`,\n };\n}\n\n/** Write a config file back, its folder made first. Null once written; otherwise the\n * report for the refusal (see `refused`), which the caller returns in place of its own.\n * `existed` is whether the file was there before: a file cut short is still one\n * sessionpipe made, and the config remembers it as `created`. */\nexport function writeBack(file: string, text: string, existed = true): InstallReport | null {\n try {\n mkdirSync(path.dirname(file), { recursive: true });\n writeFileSync(file, text);\n return null;\n } catch (e) {\n return refused(file, e, \"write\", existed);\n }\n}\n\n/** Remove a config file sessionpipe created and uninstall has emptied. A delete needs\n * the folder's leave where a write needs only the file's, so a delete refused (a\n * ~/.claude owned by root, a folder ACL on Windows) writes the file back empty instead:\n * our hooks come out either way, and the note says what stayed. */\nexport function removeEmptied(file: string, empty: string): InstallReport {\n try {\n unlinkSync(file);\n return { file, changed: true, note: \"removed (sessionpipe created it)\" };\n } catch (e) {\n const bad = refused(file, e, \"remove\", true);\n return (\n writeBack(file, empty) ?? {\n file,\n changed: true,\n note: `emptied; ${bad.note.replace(/; not touched$/, \"\")}, so the empty file stays`,\n }\n );\n }\n}\n\nfunction entryFor(event: string, harness: string, cmd: HookCommand, o: ClaudeShapedOptions) {\n const matcher = o.matcherFor(event);\n return {\n ...(matcher !== undefined ? { matcher } : {}),\n hooks: [{ type: \"command\", command: cmd([harness, event]).command, timeout: o.timeoutFor?.(event) ?? o.timeout }],\n };\n}\n\nexport function installClaudeShaped(\n file: string,\n harness: string,\n events: readonly string[],\n cmd: HookCommand,\n o: ClaudeShapedOptions,\n): InstallReport {\n const f = readJsonFile(file);\n if (!f.ok) return { file, changed: false, skipped: true, note: `${f.reason}; not touched` };\n const settings = f.value;\n const hooks = (settings.hooks && typeof settings.hooks === \"object\" ? settings.hooks : {}) as Record<\n string,\n unknown[]\n >;\n let changed = false;\n const want = new Set(events);\n // Events we no longer hook (a lean install) lose their entry of ours.\n for (const ev of Object.keys(hooks)) {\n if (want.has(ev)) continue;\n const list = Array.isArray(hooks[ev]) ? hooks[ev] : [];\n const kept = list.filter((h) => !isOurs(h as never));\n if (kept.length !== list.length) {\n changed = true;\n if (kept.length) hooks[ev] = kept;\n else delete hooks[ev];\n }\n }\n for (const ev of events) {\n const list = Array.isArray(hooks[ev]) ? hooks[ev] : [];\n const entry = entryFor(ev, harness, cmd, o);\n const mine = list.filter((h) => isOurs(h as never));\n if (mine.length === 1 && JSON.stringify(mine[0]) === JSON.stringify(entry)) continue;\n hooks[ev] = list.filter((h) => !isOurs(h as never)).concat([entry]);\n changed = true;\n }\n if (o.top)\n for (const [k, v] of Object.entries(o.top))\n if (settings[k] !== v) {\n settings[k] = v;\n changed = true;\n }\n if (changed) {\n settings.hooks = hooks;\n const bad = writeBack(file, formatJson(settings, f), f.existed);\n if (bad) return bad;\n }\n return { file, changed, created: !f.existed && changed };\n}\n\nexport function uninstallClaudeShaped(\n file: string,\n events: readonly string[],\n opts: { created?: boolean | undefined } = {},\n): InstallReport {\n if (!existsSync(file)) return { file, changed: false };\n const f = readJsonFile(file);\n if (!f.ok) return { file, changed: false, skipped: true, note: `${f.reason}; not touched` };\n const settings = f.value;\n const hooks = settings.hooks as Record<string, unknown[]> | undefined;\n if (!hooks || typeof hooks !== \"object\") return { file, changed: false };\n let changed = false;\n for (const ev of [...events, ...Object.keys(hooks)]) {\n const list = Array.isArray(hooks[ev]) ? hooks[ev] : [];\n const kept = list.filter((h) => !isOurs(h as never));\n if (kept.length !== list.length) {\n changed = true;\n if (kept.length) hooks[ev] = kept;\n else delete hooks[ev];\n }\n }\n if (!changed) return { file, changed: false };\n if (!Object.keys(hooks).length) delete settings.hooks;\n if (opts.created && !Object.keys(settings).length) return removeEmptied(file, formatJson(settings, f));\n return writeBack(file, formatJson(settings, f)) ?? { file, changed: true };\n}\n\nexport function installedClaudeShaped(\n file: string,\n harness: string,\n events: readonly string[],\n cmd: HookCommand,\n o: ClaudeShapedOptions,\n): \"current\" | \"stale\" | \"missing\" {\n const f = readJsonFile(file);\n const hooks = (f.ok ? (f.value.hooks ?? {}) : {}) as Record<string, unknown[]>;\n let present = 0;\n let current = 0;\n for (const ev of events) {\n const list = Array.isArray(hooks[ev]) ? hooks[ev] : [];\n const mine = list.filter((h) => isOurs(h as never));\n if (mine.length) present++;\n if (mine.length === 1 && JSON.stringify(mine[0]) === JSON.stringify(entryFor(ev, harness, cmd, o))) current++;\n }\n // An entry of ours on an event outside the wanted set (a lean install after a full one) is stale.\n for (const ev of Object.keys(hooks))\n if (!events.includes(ev) && (Array.isArray(hooks[ev]) ? hooks[ev] : []).some((h) => isOurs(h as never)))\n return \"stale\";\n if (!present) return \"missing\";\n return current === events.length ? \"current\" : \"stale\";\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// One headless agent turn as a child process (spec/CONTROL.md \u00A76): the spawner the\n// control daemon runs every harness through, and the shape each harness's reading of\n// its own output takes. Which binary, which flags and how the answer is read are the\n// harness's own (`<harness>-control.ts`); what is the same for all of them is here.\nimport { spawn } from \"node:child_process\";\nimport { accessSync, constants, readFileSync, statSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { withoutLineage } from \"../launched-by.js\";\n\n/** A headless turn still running after this is stopped. */\nexport const JOB_TIMEOUT_MS = 30 * 60_000;\nconst OUT_CAP = 1024 * 1024;\n\n/** How a turn the daemon runs may act (spec/CONTROL.md \u00A72), chosen at the machine's own\n * terminal: safe, the harness's auto mode, or inherit (no permission flag, so the\n * harness starts in its own default on this machine). */\nexport type Mode = \"safe\" | \"auto\" | \"inherit\";\n\nexport function isExe(p: string): boolean {\n try {\n if (!statSync(p).isFile()) return false;\n accessSync(p, constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/** A harness's CLI: the override the person set (`SESSIONPIPE_<NAME>`), else the first\n * of `names` on PATH, else the first of `dirs` that holds one. A launchd or systemd\n * service starts with a short PATH, so the installers' own folders are looked in too. */\nexport function findBinary(o: {\n override?: string | undefined;\n names: string[];\n dirs: string[];\n env?: NodeJS.ProcessEnv;\n}): string | null {\n if (o.override && isExe(o.override)) return o.override;\n for (const d of String(o.env?.PATH || \"\").split(path.delimiter)) {\n if (!d || !path.isAbsolute(d)) continue;\n for (const n of o.names) if (isExe(path.join(d, n))) return path.join(d, n);\n }\n for (const d of o.dirs) for (const n of o.names) if (isExe(path.join(d, n))) return path.join(d, n);\n return null;\n}\n\n/** The script an npm-style `.cmd` shim runs: the one `\"%dp0%\\\\\u2026\"` or `\"%~dp0\\\\\u2026\"` word\n * before `%*` (npm's cmd-shim, old and new; pnpm's is the same shape), beside the shim.\n * Null when the file is no such shim, or names nothing that is there. */\nexport function shimTarget(shim: string): string | null {\n let text = \"\";\n try {\n text = readFileSync(shim, \"utf8\");\n } catch {\n return null;\n }\n const m = /\"%(?:~dp0|dp0%)\\\\([^\"]+)\"\\s+%\\*/.exec(text);\n if (!m) return null;\n const target = path.resolve(path.dirname(shim), (m[1] as string).split(\"\\\\\").join(path.sep));\n try {\n return statSync(target).isFile() ? target : null;\n } catch {\n return null;\n }\n}\n\n/** `bin` and `args` as spawn takes them with no shell. Node refuses to spawn a `.cmd` or\n * `.bat` without one (CVE-2024-27980, since 20.12), and on Windows an `npm install -g`\n * leaves exactly such a shim (`claude.cmd`, `codex.cmd`); a shell would hand a prompt's\n * every character to cmd.exe, so the shim's own script runs under this node instead.\n * Null for a launcher script that names no script to run (issue #165). */\nexport function spawnable(bin: string, args: string[]): { file: string; args: string[] } | null {\n if (!/\\.(cmd|bat)$/i.test(bin)) return { file: bin, args };\n const target = shimTarget(bin);\n return target ? { file: process.execPath, args: [target, ...args] } : null;\n}\n\n/** Why a launcher script can't be run, for the person. */\nexport const shimNote = (bin: string) =>\n `${path.basename(bin)} is a launcher script that names no program to run; point the harness's override (SESSIONPIPE_CLAUDE, SESSIONPIPE_CODEX, \u2026) at the program itself`;\n\n/** As `spawnable`, for a probe whose arguments are fixed words (`--help`, `auth status`):\n * a launcher script that can't be read runs through a shell, as one quoted command\n * (arguments beside `shell: true` are deprecated from node 24, DEP0190). */\nexport function probe(bin: string, args: string[]): { file: string; args: string[]; shell: boolean } {\n const s = spawnable(bin, args);\n return s ? { ...s, shell: false } : { file: [`\"${bin}\"`, ...args].join(\" \"), args: [], shell: true };\n}\n\n/** The env every headless turn runs with: SESSIONPIPE_CONTROL_JOB tells the turn's own\n * hooks that the daemon runs it, so its Stop hook never takes a message meant for a\n * person's session (cli/src/hook.ts); and none of the lineage this process inherited\n * (a daemon started from inside an asked session), so a child carries only what its\n * caller adds after this (CONTROL.md \u00A713). */\nexport function jobEnv(env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {\n return { ...withoutLineage(env), SESSIONPIPE_CONTROL_JOB: \"1\" };\n}\n\n/** Whether a harness is signed in, in its own words. `loggedIn: null` means it can't\n * say (too old, or it didn't answer): never a reason to refuse. */\nexport interface Login {\n loggedIn: boolean | null;\n /** claude.ai, an API key, a long-lived token\u2026 as the harness names it. */\n method?: string;\n subscription?: string;\n}\n\nexport interface RunResult {\n error?: string;\n code?: number | null;\n signal?: NodeJS.Signals | null;\n stdout?: string | null;\n tooBig?: boolean;\n stderr?: string;\n timedOut?: boolean;\n}\n\nexport interface RunOptions {\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeoutMs?: number;\n /** Streaming: each stdout line as it arrives. Only the lines `keep` names are kept\n * for the RunResult (none by default), so a long stream never hits the cap. */\n onLine?: (line: string) => void;\n keep?: (line: string) => boolean;\n}\n\n/** Run one headless turn in its own process group, stdin closed (Codex waits on an open\n * one), holding at most 1 MB of stdout. */\nexport function runHeadless(bin: string, args: string[], o: RunOptions): Promise<RunResult> {\n return new Promise((resolve) => {\n let out: Buffer[] = [];\n let outLen = 0;\n let tooBig = false;\n let errTail = \"\";\n let timedOut = false;\n let settled = false;\n let child: import(\"node:child_process\").ChildProcess;\n const c = spawnable(bin, args);\n if (!c) {\n resolve({ error: shimNote(bin) });\n return;\n }\n try {\n child = spawn(c.file, c.args, {\n cwd: o.cwd,\n env: o.env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n detached: process.platform !== \"win32\",\n windowsHide: true,\n });\n } catch (e) {\n resolve({ error: (e as Error).message });\n return;\n }\n const kill = (sig: NodeJS.Signals) => {\n try {\n if (process.platform !== \"win32\" && child.pid) process.kill(-child.pid, sig);\n else child.kill(sig);\n } catch {\n try {\n child.kill(sig);\n } catch {}\n }\n };\n const keep = (line: string) => {\n if (!o.keep?.(line)) return;\n out = [Buffer.from(line)];\n outLen = line.length;\n };\n let partial = \"\";\n child.stdout?.on(\"data\", (b: Buffer) => {\n if (o.onLine) {\n const chunk = partial + b.toString(\"utf8\");\n const lines = chunk.split(\"\\n\");\n partial = lines.pop() as string;\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n o.onLine(line);\n } catch {}\n keep(line);\n }\n if (partial.length > OUT_CAP) partial = \"\";\n return;\n }\n if (tooBig) return;\n if (outLen + b.length > OUT_CAP) {\n tooBig = true;\n out = [];\n return;\n }\n out.push(b);\n outLen += b.length;\n });\n child.stderr?.on(\"data\", (b: Buffer) => {\n errTail = (errTail + b.toString(\"utf8\")).slice(-4000);\n });\n const timer = setTimeout(() => {\n timedOut = true;\n kill(\"SIGTERM\");\n setTimeout(() => kill(\"SIGKILL\"), 10_000).unref();\n }, o.timeoutMs ?? JOB_TIMEOUT_MS);\n const result = (code: number | null, signal: NodeJS.Signals | null): RunResult => ({\n code,\n signal,\n stdout: tooBig ? null : Buffer.concat(out).toString(\"utf8\"),\n tooBig,\n stderr: errTail,\n timedOut,\n });\n const finish = (r: RunResult) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(r);\n };\n child.on(\"error\", (e) => finish({ error: e.message }));\n // A tool the agent left running can hold stdout open after the harness exits.\n child.on(\"exit\", (code, signal) => setTimeout(() => finish(result(code, signal)), 2000).unref());\n child.on(\"close\", (code, signal) => {\n // The last line may end without a newline: it is often the result itself.\n if (o.onLine && partial.trim()) {\n try {\n o.onLine(partial);\n } catch {}\n keep(partial);\n partial = \"\";\n }\n finish(result(code, signal));\n });\n });\n}\n\n/** A harness's output read as it streams: the session it names, the answer so far, and\n * the outcome once the process is gone. One per run. */\nexport interface Reading {\n /** One stdout line. True when the answer so far changed. */\n feed(line: string): boolean;\n /** The session id the harness revealed (it picks its own), or null so far. */\n readonly session: string | null;\n /** The answer so far, whole. */\n readonly text: string;\n /** How many lines were fed. */\n readonly lines: number;\n outcome(res: RunResult): { failed: string } | { reply: string };\n}\n\n/** The last few lines a harness wrote to stderr, on one line. */\nexport function stderrTail(stderr: string | undefined, n = 3): string {\n return String(stderr || \"\")\n .trim()\n .split(\"\\n\")\n .slice(-n)\n .join(\" \")\n .trim();\n}\n\n/** What a run that wasn't there to ask says about the actions it was refused. */\nexport function deniedNote(names: string[], count: number): string {\n if (!count) return \"\";\n return `\\n\\n(Not allowed on this machine, since nobody was there to approve: ${names.join(\", \") || `${count} action(s)`}.)`;\n}\n\n/** A process that failed before or without its answer, in words. */\nexport function runFailure(label: string, res: RunResult): string | null {\n if (res.error) return `couldn't start ${label}: ${res.error}`;\n if (res.timedOut) return `${label} was still working after 30 minutes, so it was stopped`;\n return null;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// The two session fields every harness shares and no hook payload carries: who started\n// this session on purpose, and which session it answers. Both come from the environment\n// the session was started in, and the hook carries them along (PROTOCOL.md \u00A73).\nimport type { Adapter, HookInput, HookResult } from \"./adapters/types.js\";\n\n/** PROTOCOL.md \u00A73 `launched_by`: a launcher that starts a session for another one (a\n * script running `claude -p`) names itself here as `<harness>:<id>`, and the hook\n * carries it along. */\nexport const LAUNCHED_BY_ENV = \"SESSIONPIPE_LAUNCHED_BY\";\n/** PROTOCOL.md \u00A73 `delegated_by`: the control daemon that starts or resumes a session\n * for another session's ask (CONTROL.md \u00A713) or a handoff (\u00A76) names that session\n * here, and the child's hooks stamp it on every event. */\nexport const DELEGATED_BY_ENV = \"SESSIONPIPE_ASKED_BY\";\n/** CONTROL.md \u00A713: the asks a session is nested in, comma-joined, read by the next ask. */\nexport const ASK_CHAIN_ENV = \"SESSIONPIPE_ASK_CHAIN\";\n/** The three lineage variables. They are inherited like any environment, so anything\n * that starts a further session resets them (PROTOCOL.md \u00A73, CONTROL.md \u00A713); the\n * daemon most of all, which starts sessions for receivers and askers alike and must\n * not stamp its own shell's lineage on them. */\nexport const LINEAGE_ENV = [LAUNCHED_BY_ENV, DELEGATED_BY_ENV, ASK_CHAIN_ENV] as const;\nconst SESSION_REF = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;\n\n/** `env` without the lineage variables: what a child that is nobody's gets. */\nexport function withoutLineage(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const e = { ...env };\n for (const k of LINEAGE_ENV) delete e[k];\n return e;\n}\n\n/** The `<harness>:<id>` a variable names, or nothing when it is absent or malformed. */\nfunction refIn(env: Record<string, string | undefined>, name: string): string | undefined {\n const v = env[name]?.trim();\n return v && SESSION_REF.test(v) ? v : undefined;\n}\n\n/** The launcher the environment names, or nothing when it is absent or malformed. */\nexport function launchedBy(env: Record<string, string | undefined>): string | undefined {\n return refIn(env, LAUNCHED_BY_ENV);\n}\n\n/** The session this one was started or resumed for, or nothing when the environment\n * doesn't say or says it badly. */\nexport function delegatedBy(env: Record<string, string | undefined>): string | undefined {\n return refIn(env, DELEGATED_BY_ENV);\n}\n\n/** An adapter's reading of a hook, plus what every harness shares: the launcher and the\n * asker, unless the harness gave the session a parent of its own (a subagent's lineage\n * is its parent, which carries both) or the value names the session itself (a\n * launcher that forgot to reset it). */\nexport function readHook(adapter: Adapter, input: HookInput): HookResult | null {\n const r = adapter.fromHook(input);\n if (r && !r.session.parent_id) {\n const self = `${adapter.name}:${r.session.id}`;\n const launcher = launchedBy(input.env);\n if (launcher && launcher !== self) r.session.launched_by = launcher;\n const asker = delegatedBy(input.env);\n if (asker && asker !== self) r.session.delegated_by = asker;\n }\n return r;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Antigravity (Google) \u2014 docs: https://antigravity.google/docs/hooks\n// Config: ~/.gemini/config/hooks.json \u2014 top-level keys are hook NAMES, ours is\n// \"sessionpipe\". stdin is camelCase with NO event name (it rides in argv). A hook's\n// stdout is its answer; non-JSON is a deny. Timeouts in seconds. No session end\n// exists. Ported from spacesheep-cli lib/sessions.js (facts, remote url, projects).\n//\n// No PreToolUse. Its answer must carry a `decision` (allow / deny / ask / force_ask),\n// none of which means \"carry on\": `{}` is read as a deny with no reason, which blocked\n// every tool call on every machine 0.1\u20130.6.0 was installed on (2026-10-04, Antigravity\n// 2.19.1), and `allow` would wave calls past the person's own review settings. So the\n// tool events come from PostToolUse alone, which carries no tool name (only `stepIdx`\n// and `error`): it is a \"still working\" beat.\nimport { existsSync, readdirSync, readFileSync, realpathSync, statSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { homeDir } from \"../paths.js\";\nimport { ends, parseLines } from \"../readers/files.js\";\nimport { readAntigravity } from \"../readers/transcripts.js\";\nimport { formatJson, readJsonFile, removeEmptied, writeBack } from \"./claude-shaped.js\";\nimport type {\n Adapter,\n BackfillRow,\n HookCommand,\n HookInput,\n HookResult,\n InstallOptions,\n InstallReport,\n SessionFacts,\n} from \"./types.js\";\n\nconst NAME = \"antigravity\";\nconst HOOK_NAME = \"sessionpipe\";\nexport const AG_EVENTS = [\"PreInvocation\", \"PostInvocation\", \"PostToolUse\", \"Stop\"] as const;\n/** What a PreToolUse entry an older install left behind gets, until a restarted\n * conversation reads the rewritten hooks.json: the person's own prompt, with why. The\n * hook (cli/src/hook.ts) and the launcher (cli/src/launcher.ts) print the same line. */\nexport const AG_STALE_PRETOOL =\n '{\"decision\":\"ask\",\"reason\":\"sessionpipe: an outdated PreToolUse hook is still loaded. Restart this conversation (or run sessionpipe install) to drop it.\"}\\n';\n/** Antigravity's answer for one event: `{}` is \"carry on\" for every event we register. */\nexport const antigravityAnswer = (event: string): string => (event === \"PreToolUse\" ? AG_STALE_PRETOOL : \"{}\\n\");\nconst gemini = (env: NodeJS.ProcessEnv) => env.GEMINI_CLI_HOME || path.join(homeDir(), \".gemini\");\nconst hooksFile = (env: NodeJS.ProcessEnv) => path.join(gemini(env), \"config\", \"hooks.json\");\nconst projectsDir = (env: NodeJS.ProcessEnv) => path.join(gemini(env), \"config\", \"projects\");\nconst dataDirs = (env: NodeJS.ProcessEnv) =>\n [\"antigravity\", \"antigravity-cli\", \"antigravity-ide\"].map((d) => path.join(gemini(env), d));\nconst ID_RE = /^[A-Za-z0-9_-]{1,128}$/;\nconst validTool = (n: unknown): n is string => typeof n === \"string\" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);\n\n// One tool event already, so a lean install is the same entry.\nfunction entry(cmd: HookCommand) {\n const handler = (e: string) => ({ type: \"command\", command: cmd([NAME, e]).command, timeout: 5 });\n const out: Record<string, unknown> = { enabled: true };\n for (const e of AG_EVENTS) out[e] = /Tool/.test(e) ? [{ matcher: \"*\", hooks: [handler(e)] }] : [handler(e)];\n return out;\n}\n\nexport const antigravity: Adapter = {\n name: NAME,\n events: AG_EVENTS,\n detect: (env = process.env) =>\n existsSync(path.join(gemini(env), \"config\")) || dataDirs(env).some((d) => existsSync(d)),\n configFiles: (env = process.env) => [hooksFile(env)],\n install(cmd, env = process.env): InstallReport[] {\n const file = hooksFile(env);\n const f = readJsonFile(file);\n if (!f.ok) return [{ file, changed: false, skipped: true, note: `${f.reason}; not touched` }];\n const hooks = f.value;\n const e = entry(cmd);\n if (JSON.stringify(hooks[HOOK_NAME]) === JSON.stringify(e)) return [{ file, changed: false }];\n hooks[HOOK_NAME] = e;\n return [\n writeBack(file, formatJson(hooks, f), f.existed) ?? {\n file,\n changed: true,\n created: !f.existed,\n note: \"hooks.json is read when a conversation starts; open ones report after a restart\",\n },\n ];\n },\n uninstall(env = process.env, opts: InstallOptions = {}): InstallReport[] {\n const file = hooksFile(env);\n if (!existsSync(file)) return [{ file, changed: false }];\n const f = readJsonFile(file);\n if (!f.ok) return [{ file, changed: false, skipped: true, note: `${f.reason}; not touched` }];\n const hooks = f.value;\n if (!(HOOK_NAME in hooks)) return [{ file, changed: false }];\n delete hooks[HOOK_NAME];\n if (opts.created?.includes(file) && !Object.keys(hooks).length) return [removeEmptied(file, formatJson(hooks, f))];\n return [writeBack(file, formatJson(hooks, f)) ?? { file, changed: true }];\n },\n installed(cmd, env = process.env) {\n const file = hooksFile(env);\n const f = readJsonFile(file);\n const have = f.ok ? f.value[HOOK_NAME] : undefined;\n return [\n {\n file,\n state: !have ? \"missing\" : JSON.stringify(have) === JSON.stringify(entry(cmd)) ? \"current\" : \"stale\",\n },\n ];\n },\n\n fromHook(input: HookInput): HookResult | null {\n const event = input.argv[1] ?? \"\";\n let s: Record<string, unknown> = {};\n try {\n // A stdin that is JSON but not an object (`null`, a number) is no payload.\n const j: unknown = input.stdin ? JSON.parse(input.stdin) : {};\n if (j && typeof j === \"object\" && !Array.isArray(j)) s = j as Record<string, unknown>;\n } catch {}\n const id = (s.conversationId ?? s.session_id ?? s.sessionId) as string | undefined;\n const stdout = antigravityAnswer(event);\n if (!id) return { session: { id: \"\" }, events: [], stdout };\n const workspaces = agWorkspaces(s.workspacePaths);\n const session: HookResult[\"session\"] = { id: String(id), cwd: workspaces[0] ?? input.cwd };\n if (typeof s.modelName === \"string\" && s.modelName !== \"auto\") session.model = s.modelName;\n const toolCall = s.toolCall as { name?: unknown; args?: unknown } | undefined;\n const tool = validTool(toolCall?.name) ? toolCall?.name : validTool(s.toolName) ? s.toolName : undefined;\n const ev = (type: string, data: Record<string, unknown>) => ({ type, data: prune(data), harnessEvent: event });\n const events: HookResult[\"events\"] = [];\n switch (event) {\n case \"PreInvocation\":\n // Fires before every model call of an execution; only the first is the person's ask.\n if (Number(s.invocationNum) === 0 || s.invocationNum === undefined) {\n events.push(ev(\"session.started\", { source: \"unknown\" }));\n events.push(ev(\"turn.started\", {}));\n } else events.push(ev(\"session.heartbeat\", {}));\n break;\n case \"PostInvocation\":\n break;\n case \"PreToolUse\":\n if (tool) events.push(ev(\"tool.started\", { tool, input: toolCall?.args }));\n break;\n case \"PostToolUse\":\n // Antigravity names no tool here; a beat keeps a long run of tools \"working\".\n if (!tool) events.push(ev(\"session.heartbeat\", {}));\n else\n events.push(\n ev(\"tool.ended\", {\n tool,\n ok: !s.error,\n error: typeof s.error === \"string\" ? s.error.slice(0, 2000) : undefined,\n input: toolCall?.args,\n }),\n );\n break;\n case \"Stop\":\n events.push(ev(\"turn.ended\", { reason: s.error ? \"error\" : \"stop\" }));\n break;\n default:\n return { session, events: [], stdout };\n }\n const r: HookResult = { session, events, stdout, hints: { workspaces } };\n if (typeof s.transcriptPath === \"string\") r.transcript = s.transcriptPath;\n return r;\n },\n\n readTranscript(file, fromLine) {\n let text = \"\";\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return { turns: [], line: fromLine };\n }\n return readAntigravity(text.split(\"\\n\"), fromLine);\n },\n\n facts(session, transcript, hints): SessionFacts {\n if (!transcript) return {};\n return antigravityFacts(transcript, {\n conversation: session.id,\n workspaces: (hints.workspaces as string[] | undefined) ?? (session.cwd ? [session.cwd] : []),\n env: process.env,\n });\n },\n\n backfill(sinceMs, env = process.env): BackfillRow[] {\n const rows: BackfillRow[] = [];\n for (const data of dataDirs(env)) {\n let ids: string[] = [];\n try {\n ids = readdirSync(path.join(data, \"brain\")).filter((n) => /^[0-9a-f-]{36}$/i.test(n));\n } catch {}\n for (const id of ids) {\n const f = path.join(data, \"brain\", id, \".system_generated\", \"logs\", \"transcript.jsonl\");\n try {\n if (statSync(f).mtimeMs < sinceMs) continue;\n } catch {\n continue;\n }\n const facts = antigravityFacts(f, { conversation: id, workspaces: [], env });\n if (!facts.started_at || !facts.last_at) continue;\n const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;\n rows.push({ session: { id, ...rest }, started_at, last_at });\n }\n }\n return rows;\n },\n};\n\nfunction prune<T extends Record<string, unknown>>(o: T): T {\n for (const k of Object.keys(o)) if (o[k] === undefined) delete o[k];\n return o;\n}\n\n/** A folder as Antigravity writes it (a file:// URI or a path) \u2192 an absolute path, or null. */\nexport function agPath(p: unknown): string | null {\n if (typeof p !== \"string\" || !p) return null;\n let s = p;\n if (/^file:/i.test(s)) {\n try {\n s = decodeURIComponent(new URL(s).pathname);\n } catch {\n return null;\n }\n if (process.platform === \"win32\" && /^\\/[A-Za-z]:/.test(s)) s = s.slice(1);\n } else if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(s)) return null;\n if (!path.isAbsolute(s)) return null;\n return path.resolve(s);\n}\nexport const agWorkspaces = (paths: unknown): string[] =>\n (Array.isArray(paths) ? paths : [])\n .map(agPath)\n .filter((p): p is string => !!p)\n .filter((p, i, a) => a.indexOf(p) === i);\n\nfunction agVariants(p: string): string[] {\n const out = [p];\n try {\n const r = realpathSync.native(p);\n if (r !== p) out.push(r);\n } catch {}\n return out;\n}\nfunction agProjects(dir: string): { id: string; folders: string[] }[] {\n const out: { id: string; folders: string[] }[] = [];\n let names: string[] = [];\n try {\n names = readdirSync(dir).filter((n) => n.endsWith(\".json\"));\n } catch {\n return out;\n }\n for (const n of names) {\n const id = n.slice(0, -5);\n if (!ID_RE.test(id)) continue;\n let j: { projectResources?: { resources?: { folderUri?: unknown; gitFolder?: { folderUri?: unknown } }[] } };\n try {\n j = JSON.parse(readFileSync(path.join(dir, n), \"utf8\"));\n } catch {\n continue;\n }\n const folders: string[] = [];\n // A resource is one of the kinds in Antigravity's project.proto: a plain folder is\n // `{ folderUri }`, a git checkout `{ gitFolder: { folderUri, defaultBranch, allowWrite } }`\n // (what 2.23.1 writes for a folder under git); google3 and remote resources name no folder.\n for (const r of j?.projectResources?.resources ?? []) {\n const f = agPath(r?.folderUri ?? r?.gitFolder?.folderUri);\n if (f) folders.push(...agVariants(f));\n }\n if (folders.length) out.push({ id, folders });\n }\n return out;\n}\n/** The project whose folder is the workspace or contains it; the deepest wins. */\nexport function agMatchProject(projects: { id: string; folders: string[] }[], workspaces: string[]): string | null {\n const ws: string[] = [];\n for (const w of workspaces) {\n const p = agPath(w);\n if (p) ws.push(...agVariants(p));\n }\n let best: string | null = null;\n let bestLen = -1;\n for (const pr of projects)\n for (const f of pr.folders) {\n const within = ws.some((w) => w === f || w.startsWith(f.endsWith(path.sep) ? f : f + path.sep));\n if (within && f.length > bestLen) {\n best = pr.id;\n bestLen = f.length;\n }\n }\n return best;\n}\n\n/** Antigravity's web remote: https://antigravity.google.com/r/<installation uuid>-v2, with\n * ?p=c/<conversation>?section=<project> when the project is known. */\nexport function antigravityRemoteUrl(\n transcript: string,\n opts: { conversation?: string; workspaces?: string[]; env: NodeJS.ProcessEnv; projectsDir?: string },\n): string | null {\n const i = transcript.indexOf(`${path.sep}brain${path.sep}`);\n const dirs = (i > 0 ? [transcript.slice(0, i)] : []).concat(dataDirs(opts.env));\n for (const d of dirs) {\n try {\n const m = /installation_uuid:\\s*\"([0-9a-f-]{36})\"/i.exec(\n readFileSync(path.join(d, \"antigravity_state.pbtxt\"), \"utf8\"),\n );\n if (!m) continue;\n const base = `https://antigravity.google.com/r/${m[1]}-v2`;\n const conv = opts.conversation ?? \"\";\n if (!ID_RE.test(conv) || !opts.workspaces?.length) return base;\n const project = agMatchProject(agProjects(opts.projectsDir ?? projectsDir(opts.env)), opts.workspaces);\n return project ? `${base}?p=${encodeURIComponent(`c/${conv}?section=${project}`)}` : base;\n } catch {}\n }\n return null;\n}\n\nexport function antigravityFacts(\n file: string,\n opts: { conversation: string; workspaces: string[]; env: NodeJS.ProcessEnv },\n): SessionFacts {\n const { head, tail } = ends(file, 128 * 1024, 64 * 1024);\n const H = parseLines(head);\n const T = parseLines(tail);\n const out: SessionFacts = {};\n const url = antigravityRemoteUrl(file, opts);\n if (url) out.url = url;\n for (const j of H) {\n if (j.type !== \"USER_INPUT\" || typeof j.content !== \"string\") continue;\n const m = /<USER_REQUEST>([\\s\\S]*?)(?:<\\/USER_REQUEST>|$)/.exec(j.content);\n const text = (m ? (m[1] as string) : j.content).trim();\n const line = (text.split(\"\\n\").find((l) => l.trim()) ?? \"\").trim();\n if (!line || /^<[a-z_-]+>/i.test(line)) continue;\n out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\\s+\\S*$/, \"\")}\u2026` : line;\n out.title_source = \"first-ask\";\n break;\n }\n const times = H.concat(T)\n .map((j) => Date.parse(String(j.created_at ?? j.timestamp ?? \"\")))\n .filter((n) => n > 0);\n if (times.length) {\n out.started_at = Math.min(...times);\n out.last_at = Math.max(...times);\n }\n return out;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Claude Code (Anthropic) \u2014 control delivery (spec/CONTROL.md \u00A76). Claude Code on\n// this machine, as the control daemon needs it: which `claude` to\n// run and with which permission flags, where a session's transcript is and which\n// folder it runs in, whether a live process holds it, and running one headless\n// turn (`claude -p --resume`, `--fork-session`, `--session-id`). Ported from the\n// spacesheep CLI's machine listener (lib/machine.js, 1.22), which ran it in\n// production first.\nimport { execFile, execFileSync } from \"node:child_process\";\nimport { closeSync, openSync, readdirSync, readFileSync, readSync, statSync } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { levelRank, type RunLevel, START_PERMISSIONS, type StartPermission } from \"../control/verify.js\";\nimport { withoutLineage } from \"../launched-by.js\";\nimport {\n isExe,\n JOB_TIMEOUT_MS,\n type Login,\n type Mode,\n probe,\n type RunOptions,\n type RunResult,\n runHeadless,\n} from \"./headless.js\";\n\nexport { JOB_TIMEOUT_MS, type Login, type Mode, type RunResult };\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n/** A transcript someone else wrote this recently is in use. */\nconst LIVE_WRITE_MS = 90_000;\n\n/** Dotted versions by number, a missing part as 0: 1.9.0 < 1.10 = 1.10.0. */\nexport const cmpVersion = (a: string, b: string) => {\n const pa = a.split(\".\").map(Number);\n const pb = b.split(\".\").map(Number);\n for (let i = 0; i < Math.max(pa.length, pb.length); i++)\n if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);\n return 0;\n};\n\n/** The `claude` to run: on PATH, else where the installers put it, else the newest\n * copy the desktop app ships. Null when this machine has none. */\n/** The bin folders of the node version managers (nvm, fnm, volta, asdf), newest node\n * first within each, in the folders they use by default or the ones their variables name. */\nexport function versionManagerBins(env: NodeJS.ProcessEnv = process.env, home = os.homedir()): string[] {\n const out: string[] = [];\n const versions = (dir: string, tail: string[]): void => {\n let names: string[] = [];\n try {\n names = readdirSync(dir).filter((n) => /^v?\\d+(\\.\\d+)*$/.test(n));\n } catch {\n return;\n }\n for (const n of names.sort((a, b) => cmpVersion(a.replace(/^v/, \"\"), b.replace(/^v/, \"\"))).reverse())\n out.push(path.join(dir, n, ...tail));\n };\n versions(path.join(env.NVM_DIR || path.join(home, \".nvm\"), \"versions\", \"node\"), [\"bin\"]);\n const fnmDirs = env.FNM_DIR\n ? [env.FNM_DIR]\n : [\n path.join(home, \"Library\", \"Application Support\", \"fnm\"),\n path.join(home, \".local\", \"share\", \"fnm\"),\n path.join(home, \".fnm\"),\n ];\n for (const d of fnmDirs) versions(path.join(d, \"node-versions\"), [\"installation\", \"bin\"]);\n out.push(path.join(env.VOLTA_HOME || path.join(home, \".volta\"), \"bin\"));\n versions(path.join(env.ASDF_DATA_DIR || path.join(home, \".asdf\"), \"installs\", \"nodejs\"), [\"bin\"]);\n return out;\n}\n\nexport function findClaude(env: NodeJS.ProcessEnv = process.env, home = os.homedir()): string | null {\n if (env.SESSIONPIPE_CLAUDE && isExe(env.SESSIONPIPE_CLAUDE)) return env.SESSIONPIPE_CLAUDE;\n const names = process.platform === \"win32\" ? [\"claude.exe\", \"claude.cmd\"] : [\"claude\"];\n for (const d of String(env.PATH || \"\").split(path.delimiter)) {\n if (!d || !path.isAbsolute(d)) continue;\n for (const n of names) if (isExe(path.join(d, n))) return path.join(d, n);\n }\n for (const p of [\n path.join(home, \".claude\", \"local\", \"claude\"),\n path.join(home, \".local\", \"bin\", \"claude\"),\n \"/opt/homebrew/bin/claude\",\n \"/usr/local/bin/claude\",\n ])\n if (isExe(p)) return p;\n // A node version manager's folders: launchd and systemd start the daemon with a bare\n // PATH, and a `claude` that `npm install -g` put under nvm was never found that way\n // (2026-11-05: \"can't copy it (no --fork-session)\" for a Claude Code that had the flag).\n for (const d of versionManagerBins(env, home)) if (isExe(path.join(d, \"claude\"))) return path.join(d, \"claude\");\n const app = path.join(home, \"Library\", \"Application Support\", \"Claude\", \"claude-code\");\n let versions: string[] = [];\n try {\n versions = readdirSync(app)\n .filter((n) => /^\\d+(\\.\\d+)*$/.test(n))\n .sort(cmpVersion)\n .reverse();\n } catch {}\n for (const v of versions) {\n const p = path.join(app, v, \"claude.app\", \"Contents\", \"MacOS\", \"claude\");\n if (isExe(p)) return p;\n }\n return null;\n}\n\n/** One option's text in `claude --help`: its line and the deeper-indented lines under it. */\nfunction optionBlock(help: string, flag: string): string | null {\n const lines = help.split(\"\\n\");\n const re = new RegExp(`^\\\\s{0,4}(?:-\\\\w, )?${flag.replace(/-/g, \"\\\\-\")}(?![\\\\w-])`);\n const start = lines.findIndex((l) => re.test(l));\n if (start < 0) return null;\n const out = [lines[start] as string];\n for (let i = start + 1; i < lines.length && lines[i]?.trim() && !/^\\s{0,4}-/.test(lines[i] as string); i++)\n out.push(lines[i] as string);\n return out.join(\" \");\n}\n\nexport interface Caps {\n hasMode: boolean;\n modes: string[];\n promptsNone: boolean;\n fork: boolean;\n /** `--output-format stream-json` with `--include-partial-messages`: the answer can be\n * forwarded as it is written. Optional so older callers' literals still type. */\n stream?: boolean;\n /** Why `claude --help` couldn't be read, when it couldn't: it timed out (a machine still\n * starting up), or failed without listing `--permission-mode`. Such caps say nothing\n * about this Claude Code's modes, so they are never read as \"it has none\". */\n unread?: string;\n}\n\nexport function parseCaps(help: string): Caps {\n const pm = optionBlock(help, \"--permission-mode\");\n const pp = optionBlock(help, \"--permission-prompts\");\n return {\n hasMode: !!pm,\n modes: pm ? [...pm.matchAll(/\"([A-Za-z]+)\"/g)].map((m) => m[1] as string) : [],\n promptsNone: !!pp && /\"none\"/.test(pp),\n fork: !!optionBlock(help, \"--fork-session\"),\n stream: !!optionBlock(help, \"--include-partial-messages\"),\n };\n}\n\n/** The `--permission-mode` each level (CONTROL.md \u00A73.1) is, where it is one mode. */\nconst LEVEL_MODE: Record<Exclude<StartPermission, \"safe\">, string> = {\n read: \"plan\",\n edits: \"acceptEdits\",\n auto: \"auto\",\n};\n\n/** Permission flags for a level (a machine's mode is one: safe, auto, inherit). Read:\n * plan mode. Safe: `dontAsk` (nothing that needs approval runs). Edits: acceptEdits.\n * Auto: Claude Code's own auto mode. Inherit: none, so the turn starts in the mode\n * Claude Code picks by itself here, its settings' `permissions.defaultMode` (a resume\n * too: Claude Code never restores a session's earlier mode). A level this Claude Code\n * has no mode for is a refusal, never another level, and so is every level but inherit\n * while its --help can't be read (safe would otherwise run with no flag at all). Never\n * bypassPermissions \u2014 no path here writes it. */\nexport function modeFlags(level: RunLevel, caps: Caps): { args: string[] } | { error: string } {\n if (level === \"inherit\") return { args: [] };\n if (caps.unread)\n return { error: `this machine couldn't read its Claude Code's options (${caps.unread}); try again in a minute` };\n if (level === \"auto\" && !caps.modes.includes(\"auto\"))\n return { error: \"this machine is in auto mode, but its Claude Code has no auto permission mode\" };\n if (level !== \"safe\") {\n const m = LEVEL_MODE[level];\n if (!caps.modes.includes(m)) return { error: `this machine's Claude Code has no ${m} permission mode` };\n return { args: [\"--permission-mode\", m] };\n }\n if (caps.modes.includes(\"dontAsk\")) return { args: [\"--permission-mode\", \"dontAsk\"] };\n const args: string[] = [];\n if (caps.hasMode)\n args.push(\n \"--permission-mode\",\n caps.modes.includes(\"manual\") && !caps.modes.includes(\"default\") ? \"manual\" : \"default\",\n );\n if (caps.promptsNone) args.push(\"--permission-prompts\", \"none\");\n return { args };\n}\n\n/** The widest level a start may name on a machine in `mode`: the mode itself, and safe\n * on an inherit machine, whose own default sessionpipe can't rank, so a message there may\n * only narrow to a level that never runs anything needing approval. */\nexport const ceilingLevel = (mode: Mode): StartPermission => (mode === \"inherit\" ? \"safe\" : mode);\n\n/** The levels a start may ask Claude Code for on a machine in `mode`: never above its\n * ceiling, and each only where `claude --help` lists the mode it maps to (safe always).\n * None while that --help couldn't be read: an offered safe would be taken and then run\n * with no permission flag at all. */\nexport function offeredLevels(mode: Mode, caps: Caps): StartPermission[] {\n if (caps.unread) return [];\n return START_PERMISSIONS.filter(\n (l) => levelRank(l) <= levelRank(ceilingLevel(mode)) && (l === \"safe\" || caps.modes.includes(LEVEL_MODE[l])),\n );\n}\n\n/** What a start that names no level gets: what the machine's mode means for Claude Code. */\nexport const defaultLevel = (mode: Mode): RunLevel => mode;\n\n/** How a headless run prints: streamed where this Claude Code can, else one JSON at the end. */\nexport function outputArgs(caps: { stream?: boolean }): string[] {\n return caps.stream\n ? [\"--output-format\", \"stream-json\", \"--verbose\", \"--include-partial-messages\"]\n : [\"--output-format\", \"json\"];\n}\n\n/** One headless `claude -p` run: a new session (`session`), or the next message of one\n * (`resume`, optionally as a fork). The model is its own argument after `--model`, and\n * a prompt that starts with a dash gets a leading space, so it is never read as a flag. */\nexport function claudeArgs(o: {\n caps: Caps;\n level: RunLevel;\n prompt: string;\n session?: string;\n resume?: string;\n fork?: boolean;\n model?: string;\n allowedTools?: string[];\n}): { args: string[] } | { error: string } {\n const mf = modeFlags(o.level, o.caps);\n if (\"error\" in mf) return mf;\n const where = o.resume\n ? [\"--resume\", o.resume, ...(o.fork ? [\"--fork-session\"] : [])]\n : [\"--session-id\", o.session as string];\n return {\n args: [\n \"-p\",\n ...outputArgs(o.caps),\n ...mf.args,\n ...(o.model ? [\"--model\", o.model] : []),\n ...(o.allowedTools?.length ? [`--allowedTools=${o.allowedTools.join(\",\")}`] : []),\n ...where,\n o.prompt.startsWith(\"-\") ? ` ${o.prompt}` : o.prompt,\n ],\n };\n}\n\n/** The env a headless turn runs with: ours, minus the markers that make Claude Code\n * think it runs inside another session and minus any lineage this process inherited\n * (jobEnv), with CLAUDE_CONFIG_DIR naming the account. */\nexport function claudeEnv(configDir?: string, env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {\n const e = withoutLineage(env);\n for (const k of [\n \"CLAUDECODE\",\n \"CLAUDE_CODE_SESSION_ID\",\n \"CLAUDE_CODE_HOST_SESSION_ID\",\n \"CLAUDE_CODE_ENTRYPOINT\",\n \"CLAUDE_CODE_SESSION_ATTENDED\",\n \"CLAUDE_CODE_SSE_PORT\",\n ])\n delete e[k];\n e.SESSIONPIPE_CONTROL_JOB = \"1\";\n if (configDir !== undefined) {\n if (path.resolve(configDir) === path.resolve(os.homedir(), \".claude\")) delete e.CLAUDE_CONFIG_DIR;\n else e.CLAUDE_CONFIG_DIR = configDir;\n }\n return e;\n}\n\n/** Whether Claude Code is signed in for one account, in Claude Code's own words\n * (`claude auth status --json`). `loggedIn: null` means this Claude Code can't say\n * (too old for `auth status`, or it didn't answer): never a reason to refuse. */\nexport function parseAuthStatus(stdout: string | null | undefined): Login {\n const text = String(stdout || \"\").trim();\n const start = text.indexOf(\"{\");\n if (start < 0) return { loggedIn: null };\n try {\n const j = JSON.parse(text.slice(start)) as Record<string, unknown>;\n if (typeof j.loggedIn !== \"boolean\") return { loggedIn: null };\n return {\n loggedIn: j.loggedIn,\n ...(typeof j.authMethod === \"string\" ? { method: j.authMethod } : {}),\n ...(typeof j.subscriptionType === \"string\" ? { subscription: j.subscriptionType } : {}),\n };\n } catch {\n return { loggedIn: null };\n }\n}\n\n/** Ask Claude Code whether the account in `configDir` (the default one when omitted)\n * is signed in, from this process \u2014 which is the point: a login kept in the macOS\n * keychain is readable from the screen's own session and not from a background\n * service on a Mac nobody is logged in to, or from an ssh shell, so a headless turn\n * started there dies with \"please run /login\". */\nexport function claudeLogin(bin: string, configDir?: string, env: NodeJS.ProcessEnv = process.env): Promise<Login> {\n // Through `probe`: a .cmd shim (an npm install on Windows) can't be spawned bare, and\n // execFile's synchronous throw would reject this promise and take doctor down with it.\n const c = probe(bin, [\"auth\", \"status\", \"--json\"]);\n return new Promise((resolve) => {\n execFile(\n c.file,\n c.args,\n { encoding: \"utf8\", timeout: 20_000, env: claudeEnv(configDir, env), maxBuffer: 256 * 1024, shell: c.shell },\n // `auth status` may exit non-zero when signed out; its JSON still says so.\n (_err, stdout) => resolve(parseAuthStatus(stdout)),\n );\n });\n}\n\n/** The command that signs one account in, quoted for a sentence. */\nexport function loginCommand(configDir: string | undefined): string {\n const home = os.homedir();\n const isDefault = !configDir || path.resolve(configDir) === path.resolve(home, \".claude\");\n return isDefault ? \"`claude auth login`\" : `\\`CLAUDE_CONFIG_DIR=${configDir.replace(home, \"~\")} claude auth login\\``;\n}\n\n/** What to tell the person when an account isn't signed in for the daemon. */\nexport function loginHint(configDir: string | undefined, platform: NodeJS.Platform = process.platform): string {\n const shown = configDir ? configDir.replace(os.homedir(), \"~\") : \"~/.claude\";\n const login = loginCommand(configDir);\n if (platform === \"darwin\")\n return `Claude Code (${shown}) isn't signed in as this machine's background service sees it. On a Mac its login lives in the login keychain, which a background service can read only while someone is logged in at the screen: log in at the Mac, or run ${login} there.`;\n return `Claude Code (${shown}) isn't signed in on this machine: run ${login} there.`;\n}\n\n/** What `claude --help` lists. A run that fails still counts when it printed its\n * `--permission-mode` line; one that didn't (a timeout on a machine still starting up,\n * most often) is `unread`, never a Claude Code with no modes. */\nexport function detectCaps(bin: string, timeoutMs = 30_000): Caps {\n const c = probe(bin, [\"--help\"]); // a claude.cmd shim can't be spawned bare (#165)\n try {\n return parseCaps(\n execFileSync(c.file, c.args, {\n encoding: \"utf8\",\n timeout: timeoutMs,\n env: claudeEnv(),\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n maxBuffer: 4 * 1024 * 1024,\n shell: c.shell,\n }),\n );\n } catch (e) {\n const err = e as { stdout?: string; code?: string };\n const caps = parseCaps(String(err?.stdout || \"\"));\n if (caps.hasMode) return caps;\n const why = err?.code === \"ETIMEDOUT\" ? `timed out after ${timeoutMs / 1000} s` : \"failed\";\n return { ...caps, unread: `\\`${path.basename(bin)} --help\\` ${why}` };\n }\n}\n\nexport interface Transcript {\n file: string;\n configDir: string;\n project: string;\n}\n\n/** <config dir>/projects/<folder>/<session>.jsonl in one Claude Code config dir. */\nfunction transcriptIn(session: string, dir: string): Transcript | null {\n let projects: import(\"node:fs\").Dirent[] = [];\n try {\n projects = readdirSync(path.join(dir, \"projects\"), { withFileTypes: true });\n } catch {\n return null;\n }\n for (const p of projects) {\n if (!p.isDirectory()) continue;\n const file = path.join(dir, \"projects\", p.name, `${session}.jsonl`);\n try {\n if (statSync(file).isFile()) return { file, configDir: dir, project: p.name };\n } catch {}\n }\n return null;\n}\n\n/** <config dir>/projects/<folder>/<session>.jsonl in any Claude Code config dir. */\nexport function findTranscript(session: string, dirs: readonly string[]): Transcript | null {\n if (!UUID_RE.test(session)) return null;\n for (const dir of dirs) {\n const t = transcriptIn(session, dir);\n if (t) return t;\n }\n return null;\n}\n\n/** Every config dir that holds this session's transcript, in the order given. One\n * session id can be filed in several accounts: a transcript copied between homes, the\n * same id resumed under another account. There were six such ids on one Mac in\n * October 2026, and picking the first \u2014 which is what reading one home does \u2014 put a\n * message into an account that was signed out. */\nexport function findTranscripts(session: string, dirs: readonly string[]): Transcript[] {\n if (!UUID_RE.test(session)) return [];\n const out: Transcript[] = [];\n for (const dir of dirs) {\n const t = transcriptIn(session, dir);\n if (t) out.push(t);\n }\n return out;\n}\n\n/** Which Claude Code account a session belongs to, when more than one has its\n * transcript: the one whose own `sessions/` says a live process holds it. `ambiguous`\n * is a refusal rather than a guess \u2014 it names every account that has the transcript,\n * and in `live` the ones holding it open, which is none of them or more than one. Both\n * are named by their directory name alone, since an ack travels to the receiver. Null\n * when no account has the session at all. */\nexport type HomeChoice = { transcript: Transcript } | { ambiguous: string[]; live: string[] };\n\nconst homeName = (t: Transcript) => path.basename(t.configDir);\n\nexport function resolveHome(session: string, dirs: readonly string[]): HomeChoice | null {\n const all = findTranscripts(session, dirs);\n if (!all.length) return null;\n if (all.length === 1) return { transcript: all[0] as Transcript };\n const live = all.filter((t) => liveRecords(session, t.configDir).length > 0);\n if (live.length === 1) return { transcript: live[0] as Transcript };\n return {\n ambiguous: [...new Set(all.map(homeName))].sort(),\n live: [...new Set(live.map(homeName))].sort(),\n };\n}\n\nconst projectName = (cwd: string) => cwd.replace(/[^a-zA-Z0-9]/g, \"-\");\n\n/** The folder a session runs in, from its transcript's own records (never from a\n * command): the `cwd` whose project name is the folder the transcript is filed\n * under, else the first `cwd`. Streams; a line over 2 MB is skipped. */\nexport function sessionFolder(t: Transcript, cap = 8 * 1024 * 1024): string | null {\n let first: string | null = null;\n let count = 0;\n let fd: number | undefined;\n const look = (line: Buffer): string | null => {\n if (line.indexOf('\"cwd\"') < 0) return null;\n let j: { cwd?: unknown };\n try {\n j = JSON.parse(line.toString(\"utf8\"));\n } catch {\n return null;\n }\n if (!j || typeof j.cwd !== \"string\" || !path.isAbsolute(j.cwd)) return null;\n if (projectName(j.cwd) === t.project) return j.cwd;\n first ??= j.cwd;\n count++;\n return null;\n };\n try {\n fd = openSync(t.file, \"r\");\n const buf = Buffer.alloc(256 * 1024);\n let pos = 0;\n let rest = Buffer.alloc(0);\n let skipping = false;\n while (pos < cap && count < 50) {\n const n = readSync(fd, buf, 0, buf.length, pos);\n if (!n) {\n if (!skipping && rest.length) {\n const hit = look(rest);\n if (hit) return hit;\n }\n break;\n }\n pos += n;\n let chunk = rest.length ? Buffer.concat([rest, buf.subarray(0, n)]) : buf.subarray(0, n);\n let nl = chunk.indexOf(10);\n while (nl >= 0) {\n const line = chunk.subarray(0, nl);\n chunk = chunk.subarray(nl + 1);\n if (skipping) skipping = false;\n else {\n const hit = look(line);\n if (hit) return hit;\n }\n nl = chunk.indexOf(10);\n }\n if (chunk.length > 2 * 1024 * 1024) {\n skipping = true;\n rest = Buffer.alloc(0);\n } else rest = skipping ? Buffer.alloc(0) : Buffer.from(chunk);\n }\n } catch {\n } finally {\n if (fd !== undefined)\n try {\n closeSync(fd);\n } catch {}\n }\n return first;\n}\n\nfunction pidAlive(pid: number): boolean {\n if (!Number.isSafeInteger(pid) || pid <= 1) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch (e) {\n return (e as { code?: string }).code === \"EPERM\";\n }\n}\n\n/** One live Claude Code process's registry row, `<config>/sessions/<pid>.json`, as the\n * in-place `inbox` lane needs it (spec/CONTROL.md \u00A76.3). Every session that registers\n * files one \u2014 a terminal window, the desktop app and a claude.ai bridge worker alike,\n * all of them `kind: \"interactive\"`; a `-p` one-shot's row is not one of those. */\nexport interface LiveRecord {\n /** The config dir this row was read from: the lane never crosses accounts. */\n configDir: string;\n file: string;\n pid: number;\n /** Claude Code's own local inbox for this process, or null when it published none. */\n socket: string | null;\n /** 1 is the only inbox protocol this code speaks; anything else is refused. */\n peerProtocol: number | null;\n /** The process's start time as the registry spells it (UTC), so a recycled pid can be\n * told from the process that filed the row. */\n procStart: string | null;\n status: string | null;\n /** `interactive` for a session the harness holds open for the person. */\n kind: string | null;\n entrypoint: string | null;\n /** `<pid>.<sha>.key`, 0600: where the inbox's auth token lives (needed off macOS). */\n keyFile: string | null;\n}\n\nconst registryRow = (file: string, configDir: string, j: Record<string, unknown>): LiveRecord => {\n const pid = Number(j.pid ?? path.basename(file).slice(0, -5));\n const str = (v: unknown) => (typeof v === \"string\" && v ? v : null);\n let keyFile: string | null = null;\n try {\n // Claude Code's own spelling: `<pid>.<64 hex>.key`, 0600, beside the registry row.\n const want = new RegExp(`^${pid}\\\\.[0-9a-f]{64}\\\\.key$`);\n const hit = readdirSync(path.dirname(file)).find((n) => want.test(n));\n if (hit) keyFile = path.join(path.dirname(file), hit);\n } catch {}\n return {\n configDir,\n file,\n pid,\n socket: str(j.messagingSocketPath),\n peerProtocol: typeof j.peerProtocol === \"number\" ? j.peerProtocol : null,\n procStart: str(j.procStart),\n status: str(j.status),\n kind: str(j.kind),\n entrypoint: str(j.entrypoint),\n keyFile,\n };\n};\n\n/** Every live registry row for this session, across the config dirs given. A row whose\n * pid is gone is not one: the file outlives the process. */\nexport function liveRecords(session: string, dirs: string | readonly string[]): LiveRecord[] {\n const out: LiveRecord[] = [];\n for (const dir of typeof dirs === \"string\" ? [dirs] : dirs) {\n let names: string[] = [];\n try {\n names = readdirSync(path.join(dir, \"sessions\")).filter((n) => /^\\d+\\.json$/.test(n));\n } catch {\n continue;\n }\n for (const n of names) {\n const file = path.join(dir, \"sessions\", n);\n let j: Record<string, unknown>;\n try {\n j = JSON.parse(readFileSync(file, \"utf8\")) as Record<string, unknown>;\n } catch {\n continue;\n }\n if (String(j.sessionId) !== session) continue;\n const row = registryRow(file, dir, j);\n if (pidAlive(row.pid)) out.push(row);\n }\n }\n return out;\n}\n\n/** The ONE live process that holds this session, or null. Two rows \u2014 two accounts, or\n * two processes in one \u2014 is a refusal rather than a guess: a lane that writes into a\n * live session has to know which process it is writing to (spec/CONTROL.md \u00A76.3). */\nexport function liveRecord(session: string, dirs: string | readonly string[]): LiveRecord | null {\n const all = liveRecords(session, dirs);\n return all.length === 1 ? (all[0] as LiveRecord) : null;\n}\n\n/** A Claude Code process holds this session right now: its registry file,\n * <config>/sessions/<pid>.json, names it and the pid is alive (interactive and\n * Agent SDK sessions both file one). */\nexport function liveProcess(session: string, configDir: string): boolean {\n return liveRecords(session, configDir).length > 0;\n}\n\n/** Claude Code's own permission modes, by how much a turn may do in them without being\n * asked about (its own order, 2.1.310). `plan` writes nothing at all. `default`,\n * `manual` and `dontAsk` share a step: none of them lets anything through that needs\n * approval \u2014 the first two ask the person, `dontAsk` refuses instead. `acceptEdits`\n * writes files; `auto` runs most things; `bypassPermissions` asks about nothing.\n *\n * Used to tell one mode from another and to compare two, never to pick one. -1 is \"not\n * a mode this code knows\", which compares below everything. */\nexport const MODE_RANK: Record<string, number> = {\n plan: 0,\n default: 1,\n manual: 1,\n dontAsk: 1,\n acceptEdits: 2,\n auto: 3,\n bypassPermissions: 4,\n};\nexport const CLAUDE_MODES = Object.keys(MODE_RANK);\nexport const modeRank = (m: string | null | undefined): number => (m && m in MODE_RANK ? (MODE_RANK[m] as number) : -1);\n\n/** The permission mode a hook payload reports for its own session, or null. Claude Code\n * writes `permission_mode` on every hook payload; `permissionMode` is the spelling its\n * transcript records use, and is read too so one reader serves both.\n *\n * This is the only place a mode the person chose at the keyboard can be read from: a\n * session's registry row doesn't carry it, and the transcript names it only on turns\n * that happen to record one. hook.ts repeats the shape test, because it imports nothing\n * but node builtins. */\nexport function hookMode(payload: unknown): string | null {\n if (!payload || typeof payload !== \"object\") return null;\n const p = payload as { permission_mode?: unknown; permissionMode?: unknown };\n const v = typeof p.permission_mode === \"string\" ? p.permission_mode : p.permissionMode;\n return typeof v === \"string\" && CLAUDE_MODES.includes(v) ? v : null;\n}\n\n/** The permission class Claude Code's own inbox checks a sender's declared class\n * against (2.1.310): `bypassPermissions` is `bypass`; a mode that prompts before it\n * acts is `prompting`. `plan` is neither \u2014 its class there depends on a capability and\n * a TTY this code cannot see \u2014 and nor is `auto`, nor a mode nothing has recorded, so\n * those get no in-place lane at all (spec/CONTROL.md \u00A76.3). */\nexport function peerClass(mode: string | null | undefined): \"bypass\" | \"prompting\" | null {\n if (mode === \"bypassPermissions\") return \"bypass\";\n if (mode === \"default\" || mode === \"manual\" || mode === \"dontAsk\" || mode === \"acceptEdits\") return \"prompting\";\n return null;\n}\n\n/** One Claude Code account's permission settings, user scope only\n * (`<config dir>/settings.json`): a folder's own settings may tighten these but never\n * loosen them, and a managed policy may tighten them further. Only the three keys\n * something here acts on.\n *\n * Reading user scope alone can overstate how wide a home is, never understate it, and\n * everything that reads this treats a wider answer as a reason NOT to act (the inbox\n * lane needs the machine's class to equal the session's own; a narrowing is re-asserted\n * only where the home's default is at least as wide). */\nexport interface HomeSettings {\n /** `permissions.defaultMode`: what a session with no flag starts in. Null: not said,\n * or the file could not be read. */\n defaultMode: string | null;\n /** `disableAutoMode: \"disable\"`: asking for auto mode quietly gives Manual instead. */\n disableAutoMode: boolean;\n /** `disableBypassPermissionsMode: \"disable\"`: this home cannot be in bypass at all. */\n disableBypass: boolean;\n}\n\nexport const NO_HOME_SETTINGS: HomeSettings = { defaultMode: null, disableAutoMode: false, disableBypass: false };\n\nexport function homeSettings(configDir: string | undefined, home = os.homedir()): HomeSettings {\n const file = path.join(configDir ?? path.join(home, \".claude\"), \"settings.json\");\n let j: Record<string, unknown>;\n try {\n j = JSON.parse(readFileSync(file, \"utf8\")) as Record<string, unknown>;\n } catch {\n return { ...NO_HOME_SETTINGS };\n }\n if (!j || typeof j !== \"object\" || Array.isArray(j)) return { ...NO_HOME_SETTINGS };\n const p = (j.permissions && typeof j.permissions === \"object\" ? j.permissions : {}) as Record<string, unknown>;\n // Both spellings: Claude Code reads these two under `permissions`, and settings\n // written by hand (and its own older docs) put them at the top level.\n const off = (k: string) => j[k] === \"disable\" || p[k] === \"disable\";\n return {\n defaultMode: typeof p.defaultMode === \"string\" && CLAUDE_MODES.includes(p.defaultMode) ? p.defaultMode : null,\n disableAutoMode: off(\"disableAutoMode\"),\n disableBypass: off(\"disableBypassPermissionsMode\"),\n };\n}\n\n/** The `--permission-mode` a relaunch of ONE session re-asserts, or null for no flag at\n * all (spec/CONTROL.md \u00A76 `prompt`). Only a narrowing the session itself chose, and\n * only where it is one:\n *\n * - `plan` always: nothing this harness has is narrower, so it cannot widen anything;\n * - `dontAsk` and `acceptEdits` where the account's own default is at least as wide\n * (`MODE_RANK`), so a re-assertion is never wider than what the machine itself does\n * with no flag. Settings that can't be read mean it isn't re-asserted;\n * - everything else \u2014 `default`, `manual`, `auto`, `bypassPermissions`, nothing\n * recorded \u2014 is no flag, and the account's own default decides.\n *\n * The default it compares against is the account's own (`permissions.defaultMode`, user\n * scope). A folder's settings or a managed policy may narrow that further, invisibly\n * here; both narrow what the re-asserted mode can actually do too, so the comparison\n * errs on the side of asking.\n *\n * It never returns `auto` or `bypassPermissions`: the two values a setting can quietly\n * turn into something else, and the two this code must never write. `auto` is the\n * incident this came from \u2014 it asks for a mode that `disableAutoMode` turns into Manual,\n * so a session that recorded it gets no flag and the account's default instead.\n *\n * No flag is not neutral. On a fleet whose accounts default to bypass it is the widest\n * outcome there is, which is why a narrowing the person chose has to be said again. */\nexport function relaunchMode(recorded: string | null | undefined, home: HomeSettings): string | null {\n if (recorded === \"plan\") return recorded;\n if ((recorded === \"dontAsk\" || recorded === \"acceptEdits\") && modeRank(home.defaultMode) >= modeRank(recorded))\n return recorded;\n return null;\n}\n\n/** Written in the last 90 s, and not by a job of ours. */\nexport function recentlyWritten(file: string, ourLastEnd?: number, now = Date.now()): boolean {\n let m: number;\n try {\n m = statSync(file).mtimeMs;\n } catch {\n return false;\n }\n if (now - m > LIVE_WRITE_MS) return false;\n return !(ourLastEnd && m <= ourLastEnd + 2000);\n}\n\n/** Run one headless turn (headless.ts). Streaming (`--output-format stream-json`), only\n * the `result` line is kept for the RunResult unless the caller says otherwise. */\nexport function runClaude(bin: string, args: string[], o: RunOptions): Promise<RunResult> {\n return runHeadless(bin, args, { ...o, keep: o.keep ?? ((line) => line.includes('\"type\":\"result\"')) });\n}\n\nexport interface ClaudeResult {\n type?: string;\n result?: unknown;\n is_error?: boolean;\n subtype?: string;\n session_id?: unknown;\n permission_denials?: { tool_name?: unknown }[];\n}\n\n/** `--output-format json`: one result object, or an array whose last `result` entry it is. */\nexport function parseResult(stdout: string | null | undefined): ClaudeResult | null {\n const s = String(stdout || \"\").trim();\n if (!s) return null;\n const pick = (j: unknown): ClaudeResult | null => {\n if (Array.isArray(j)) {\n for (let i = j.length - 1; i >= 0; i--) if (j[i]?.type === \"result\") return j[i];\n return null;\n }\n return j && typeof j === \"object\" ? (j as ClaudeResult) : null;\n };\n try {\n return pick(JSON.parse(s));\n } catch {}\n const lines = s.split(\"\\n\");\n for (let i = lines.length - 1; i >= 0; i--) {\n try {\n const j = pick(JSON.parse(lines[i] as string));\n if (j && (j.type === \"result\" || \"result\" in j)) return j;\n } catch {}\n }\n return null;\n}\n\nexport const isUuid = (s: unknown): s is string => typeof s === \"string\" && UUID_RE.test(s);\n", "// SPDX-License-Identifier: Apache-2.0\n// The one verifier for signed control commands (spec/CONTROL.md \u00A74\u2013\u00A75). A machine\n// runs a command only when this says yes; a receiver MAY run it too, as an early\n// word, so a bad command is refused where the person can see why.\n//\n// WebCrypto only (globalThis.crypto.subtle), no node: imports, no clock of its own\n// (`now` is passed in): the same file runs in a Cloudflare Worker, in a browser and\n// in Node \u2265 20, and conformance/control-vectors/ pins its answers.\n//\n// Every signature covers the EXACT bytes as transmitted (`cmd`, `grant.str`): the\n// strings are parsed to read their fields and never re-serialized to verify.\n\n/** One passkey tap vouches for a day key for at most this long. */\nexport const GRANT_MAX_MS = 24 * 3600_000;\n/** Clock slack between the signing device, the receiver and the machine. */\nexport const SKEW_MS = 5 * 60_000;\n/** The machine judges a command by when it was SIGNED: a message sent while the lid\n * was shut still runs when the machine wakes, up to a day later. */\nexport const MACHINE_MAX_AGE_MS = 24 * 3600_000;\n/** A receiver's early check is stricter: a command is carried within ten minutes of\n * being signed, or not at all. */\nexport const RECEIVER_MAX_AGE_MS = 10 * 60_000;\n/** How long a machine remembers a nonce: longer than MACHINE_MAX_AGE_MS + SKEW_MS. */\nexport const NONCE_TTL_MS = 25 * 3600_000;\n/** The whole command string, in UTF-16 code units (what JSON.parse sees). */\nexport const CMD_MAX = 48_000;\n/** A prompt's text, in characters. */\nexport const TEXT_MAX = 20_000;\n/** A permission answer's note. */\nexport const NOTE_MAX = 2_000;\n/** A grant string. */\nexport const GRANT_STR_MAX = 2_000;\n/** Attachments on a `start` or a `prompt` (CONTROL.md \u00A73.1): at most this many files, */\nexport const FILES_MAX = 10;\n/** each at most this many bytes, */\nexport const FILE_SIZE_MAX = 26_214_400;\n/** together at most this many, */\nexport const FILES_TOTAL_MAX = 52_428_800;\n/** with a name of at most this many characters (after NFC) */\nexport const FILE_NAME_MAX = 200;\n/** and a media type of at most this many. */\nexport const FILE_TYPE_MAX = 100;\n/** A start's `model` (CONTROL.md \u00A73.1): at most this many characters, */\nexport const MODEL_MAX = 100;\n/** in this alphabet, never starting with a dash, so it can never read as a flag. */\nexport const MODEL_RE = /^[A-Za-z0-9][A-Za-z0-9._:/@[\\]-]*$/;\n/** The levels a start may ask for (CONTROL.md \u00A73.1), narrowest first. Closed: no level\n * is wider than `auto`, and none turns a harness's approval off. */\nexport const START_PERMISSIONS = [\"read\", \"safe\", \"edits\", \"auto\"] as const;\nexport type StartPermission = (typeof START_PERMISSIONS)[number];\n/** What a run is started with: a level, or `inherit` (CONTROL.md \u00A72) \u2014 no permission\n * flag at all, so the harness starts in its own default on the machine. Only a machine\n * the person put in inherit mode runs that way; a command never names it. */\nexport type RunLevel = StartPermission | \"inherit\";\n\n/** Where a level sits, narrowest (0) to widest; -1 for anything that isn't one. */\nexport const levelRank = (l: unknown): number => START_PERMISSIONS.indexOf(l as StartPermission);\n/** The narrower of two levels: what a later run of a session gets, its recorded level\n * against what the machine's mode allows now. */\nexport const narrower = (a: StartPermission, b: StartPermission): StartPermission =>\n levelRank(a) <= levelRank(b) ? a : b;\n\nexport const COMMAND_TYPE = \"sessionpipe.control\";\nexport const GRANT_TYPE = \"sessionpipe.grant\";\nexport const CONTROL_KINDS = [\"prompt\", \"permission.answer\", \"cancel\", \"start\", \"key.add\", \"pool\"] as const;\n/** The session a `key.add` names: the machine's own key store, not a coding session. */\nexport const KEY_STORE_SESSION = \"sessionpipe:keys\";\n/** The session a `pool` names: the machine's account pool (CONTROL.md \u00A76 `pool`). */\nexport const POOL_SESSION = \"sessionpipe:pool\";\nexport type ControlKind = (typeof CONTROL_KINDS)[number];\n/** What a `pool` command may ask (CONTROL.md \u00A76 `pool`). Closed: an action added later\n * is refused by every machine that predates it. */\nexport const POOL_ACTIONS = [\n \"on\",\n \"off\",\n \"enable\",\n \"disable\",\n \"remove\",\n \"order\",\n \"refresh\",\n \"login.begin\",\n \"login.complete\",\n] as const;\nexport type PoolAction = (typeof POOL_ACTIONS)[number];\n/** A pooled account's id (or a pending sign-in's): what `session.account_id` carries. */\nexport const POOL_ACCOUNT_RE = /^[A-Za-z0-9._:-]{1,128}$/;\n/** An `order` names at most this many accounts: the pool holds no more. */\nexport const POOL_ORDER_MAX = 20;\n/** The pasted `code#state` of a sign-in, in characters, on one line. */\nexport const POOL_CODE_MAX = 512;\n\n/** COSE algorithm ids a trusted key may use. */\nexport const ES256 = -7;\nexport const RS256 = -257;\n\n/** The kinds of key a machine may trust (CONTROL.md \u00A72): a WebAuthn passkey, or a\n * device key an app holds in hardware where it has no passkey to offer. */\nexport const KEY_TYPES = [\"passkey\", \"device\"] as const;\nexport type KeyType = (typeof KEY_TYPES)[number];\n\n/** A key enrolled on the machine: a passkey's credential id, COSE alg and SPKI\n * (base64url); a device key also says so (`type`), and what its app declared about\n * it (`store`, `uv`). The machine verifies by its own record, never by a message. */\nexport interface TrustedKey {\n id: string;\n alg: number;\n spki: string;\n /** Present only on a device key (CONTROL.md \u00A72 \"Device keys\"). */\n type?: \"device\";\n store?: \"hardware\";\n uv?: \"each-use\";\n}\n/** A WebAuthn assertion: credential id, authenticatorData, clientDataJSON, signature (base64url). */\nexport interface Assertion {\n cred: string;\n ad: string;\n cd: string;\n sig: string;\n}\n/** A device key's signature (CONTROL.md \u00A73.2): its id and 64 bytes r\u2016s over deviceMessage(). */\nexport interface DeviceSignature {\n cred: string;\n sig: string;\n}\n/** The day's grant: a passkey's assertion over sha256(str), where str names the day key. */\nexport interface GrantBundle extends Assertion {\n str: string;\n}\n/** The day's grant from a device key: its signature over deviceMessage(\"grant\", rp, sha256(str)). */\nexport interface DeviceGrant extends DeviceSignature {\n str: string;\n}\n/** What a receiver carries for one command (control.json minus the receiver's envelope). */\nexport interface SignedCommand {\n cmd: string;\n csig?: string | null;\n grant?: GrantBundle | DeviceGrant | null;\n confirm?: Assertion | DeviceSignature | null;\n}\nexport interface Grant {\n v: 1;\n t: typeof GRANT_TYPE;\n pub: string;\n iat: number;\n exp: number;\n rp: string;\n}\n/** One file attached to a `start`: the bytes live on the receiver, named by their hash. */\nexport interface ControlFile {\n /** The file name the machine writes (NFC). */\n name: string;\n /** Media type, `type/subtype`. */\n type: string;\n /** Bytes. */\n size: number;\n /** SHA-256 of the bytes, base64url without padding (43 characters). */\n sha256: string;\n}\nexport interface ControlCommand {\n v: 1;\n t: typeof COMMAND_TYPE;\n machine: string;\n kind: ControlKind;\n /** `<harness>:<id>`, e.g. `claude-code:8c132906-\u2026`. */\n session: string;\n text?: string;\n for?: string;\n decision?: \"allow\" | \"deny\";\n note?: string;\n cwd?: string;\n /** key.add only: the key to trust from now on, as the receiver holds it (a passkey,\n * or a device key with its `type`, `store` and `uv`). */\n key?: TrustedKey;\n /** key.add only: what the person calls the device (\"iPhone\"), for `control keys`. */\n name?: string;\n /** start, prompt: files to download, verify and write into the session's folder. */\n files?: ControlFile[];\n /** start only: the level the new session runs at (never above the machine's mode). */\n permission?: StartPermission;\n /** start only: the model the harness runs, as the harness names it. */\n model?: string;\n /** pool only (session `sessionpipe:pool`): what to do with the account pool. */\n pool_action?: PoolAction;\n /** pool: the account (enable, disable, remove, refresh), or the pending sign-in's id\n * (login.complete). */\n pool_account?: string;\n /** pool, order: every account of the pool once, in the order wanted. */\n pool_order?: string[];\n /** pool, login.complete: the `code#state` the person pasted back. */\n pool_code?: string;\n /** start, prompt: `<harness>:<id>` of a session on the same machine that gets the\n * answer as a message once the turn ends (CONTROL.md \u00A76, a handoff). */\n reply_to?: string;\n nonce: string;\n iat: number;\n}\n\n/** Why a command was refused. Stable: conformance vectors and acks name these. */\nexport type RefusalCode =\n | \"malformed\"\n | \"not_a_command\"\n | \"wrong_machine\"\n | \"unknown_kind\"\n | \"bad_session\"\n | \"bad_fields\"\n | \"bad_nonce\"\n | \"too_old\"\n | \"future\"\n | \"unsigned\"\n | \"untrusted_key\"\n | \"bad_assertion\"\n | \"bad_grant\"\n | \"outside_grant\"\n | \"bad_csig\"\n | \"replay\"\n | \"nonce_store\";\n\nexport type Verdict =\n | { ok: true; cmd: ControlCommand; via: \"grant\" | \"confirm\"; key: string }\n | { ok: false; code: RefusalCode; why: string };\n\nexport interface VerifyOptions {\n /** This machine's id; a command for another machine is refused. */\n machine: string;\n /** The receiver's WebAuthn rp id the keys were enrolled for (well-known control.signing.rp_id). */\n rpId: string;\n /** The keys enrolled on this machine (or, for a receiver's early check, the account's). */\n trusted: TrustedKey[];\n /** Epoch ms. */\n now: number;\n /** MACHINE_MAX_AGE_MS on a machine (default), RECEIVER_MAX_AGE_MS on a receiver. */\n maxAgeMs?: number;\n /** Called once, last, only for a command whose signatures all verified: answer\n * true when the nonce was seen before, otherwise record it (durably) and answer\n * false. A throw refuses the command (`nonce_store`): never run what could not\n * be remembered. Omit it on a receiver's early check. */\n seenNonce?: (nonce: string) => boolean | Promise<boolean>;\n}\n\nconst NONCE_RE = /^[A-Za-z0-9_-]{22,64}$/;\nconst MACHINE_RE = /^m_[A-Za-z0-9_-]{16,40}$/;\nconst HARNESS_RE = /^[a-z][a-z0-9-]{0,39}$/;\nconst SESSION_ID_RE = /^[A-Za-z0-9._:-]{1,128}$/;\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst B64URL_RE = /^[A-Za-z0-9_-]*$/;\nconst SHA256_RE = /^[A-Za-z0-9_-]{43}$/;\nconst MEDIA_TYPE_RE = /^[a-z0-9][a-z0-9!#$&^_.+-]*\\/[a-z0-9][a-z0-9!#$&^_.+-]*$/i;\n// biome-ignore lint/suspicious/noControlCharactersInRegex: control characters are exactly what it rejects\nconst BAD_NAME_CHAR_RE = /[/\\\\\\x00-\\x1f\\x7f]/;\nconst UP = 0x01;\nconst UV = 0x04;\n\n/** The fields each kind carries besides the common ones (CONTROL.md \u00A73.1). The set is\n * closed: a field it doesn't list is `bad_fields`, so a field added later fails closed\n * on a machine that predates it instead of being ignored. */\nconst COMMON_FIELDS: readonly string[] = [\"v\", \"t\", \"machine\", \"kind\", \"session\", \"nonce\", \"iat\", \"note\"];\nconst FIELDS: Record<ControlKind, readonly string[]> = {\n prompt: [\"text\", \"files\", \"reply_to\"],\n \"permission.answer\": [\"for\", \"decision\"],\n cancel: [],\n start: [\"text\", \"cwd\", \"files\", \"permission\", \"model\", \"reply_to\"],\n \"key.add\": [\"key\", \"name\"],\n pool: [\"pool_action\", \"pool_account\", \"pool_order\", \"pool_code\"],\n};\n/** The fields only a `pool` carries. */\nconst POOL_FIELDS = FIELDS.pool;\n\n// --- encodings ------------------------------------------------------------------\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder();\n\nexport function b64url(bytes: Uint8Array | ArrayBuffer): string {\n const u = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let s = \"\";\n for (const b of u) s += String.fromCharCode(b);\n return btoa(s).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n/** base64url without padding, strictly; null for anything else. */\nexport function fromB64url(s: unknown): Uint8Array | null {\n if (typeof s !== \"string\" || !B64URL_RE.test(s) || s.length % 4 === 1) return null;\n try {\n const bin = atob(s.replace(/-/g, \"+\").replace(/_/g, \"/\") + \"===\".slice((s.length + 3) % 4));\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n } catch {\n return null;\n }\n}\n\nexport async function sha256(data: string | Uint8Array): Promise<Uint8Array> {\n const bytes = typeof data === \"string\" ? enc.encode(data) : data;\n return new Uint8Array(await crypto.subtle.digest(\"SHA-256\", bytes as Uint8Array<ArrayBuffer>));\n}\n\nfunction concat(a: Uint8Array, b: Uint8Array): Uint8Array {\n const out = new Uint8Array(a.length + b.length);\n out.set(a, 0);\n out.set(b, a.length);\n return out;\n}\n\n/** Equal, compared whole: a mismatch costs the same as a match. */\nexport function sameBytes(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n let d = 0;\n for (let i = 0; i < a.length; i++) d |= (a[i] as number) ^ (b[i] as number);\n return d === 0;\n}\n\n/** A DER ECDSA signature (what WebAuthn returns) as the 64-byte r\u2016s WebCrypto verifies.\n * Strict: every length must match the bytes, so one signature has one encoding (a\n * P-256 signature is at most 72 bytes, so the short length form is the only one). */\nexport function derToRaw(der: Uint8Array): Uint8Array | null {\n if (der[0] !== 0x30 || der.length < 8 || der[1] !== der.length - 2) return null;\n let i = 2;\n const part = (): Uint8Array | null => {\n if (der[i] !== 0x02) return null;\n const len = der[i + 1] as number;\n if (len < 1 || len > 33) return null;\n let v = der.slice(i + 2, i + 2 + len);\n if (v.length !== len) return null;\n // Minimal integers only: no needless leading zero, and never negative.\n if ((v[0] as number) & 0x80) return null;\n if (len > 1 && v[0] === 0 && !((v[1] as number) & 0x80)) return null;\n i += 2 + len;\n while (v.length > 32 && v[0] === 0) v = v.slice(1);\n if (v.length > 32) return null;\n const out = new Uint8Array(32);\n out.set(v, 32 - v.length);\n return out;\n };\n const r = part();\n const s = part();\n return r && s && i === der.length ? concat(r, s) : null;\n}\n\nconst u8 = (b: Uint8Array) => b as Uint8Array<ArrayBuffer>;\n/** WebCrypto's key type, named without the DOM lib (Workers, Node and browsers all have it). */\ntype CryptoKey = Awaited<ReturnType<typeof crypto.subtle.importKey>>;\n\nasync function importKey(key: TrustedKey): Promise<CryptoKey | null> {\n const spki = fromB64url(key.spki);\n if (!spki?.length) return null;\n try {\n if (key.alg === ES256)\n return await crypto.subtle.importKey(\"spki\", u8(spki), { name: \"ECDSA\", namedCurve: \"P-256\" }, false, [\"verify\"]);\n if (key.alg === RS256) {\n const k = await crypto.subtle.importKey(\"spki\", u8(spki), { name: \"RSASSA-PKCS1-v1_5\", hash: \"SHA-256\" }, false, [\n \"verify\",\n ]);\n const bits = (k.algorithm as { modulusLength?: number }).modulusLength as number; // every RSA key has one\n return bits >= 2048 ? k : null;\n }\n } catch {}\n return null;\n}\n\nconst PASSKEY_FIELDS = new Set([\"id\", \"alg\", \"spki\"]);\nconst DEVICE_FIELDS = new Set([\"id\", \"alg\", \"spki\", \"type\", \"store\", \"uv\"]);\n/** The only fields a device signature has (`str` only in a grant). */\nexport const DEVICE_SIG_FIELDS = [\"cred\", \"sig\", \"str\"] as const;\n\n/** A device key's id: the SHA-256 of its SPKI bytes, base64url without padding. */\nexport async function deviceKeyId(spki: string): Promise<string> {\n return b64url(await sha256(fromB64url(spki) ?? new Uint8Array()));\n}\n\n/** A device key's declared rules, ignoring any bookkeeping fields a store keeps beside\n * them (a name, when it was added): ES256, hardware, verified each use, named by its\n * SPKI's hash, and a P-256 key WebCrypto can import. */\nasync function deviceRecordValid(key: TrustedKey): Promise<boolean> {\n if (key.type !== \"device\" || key.alg !== ES256 || key.store !== \"hardware\" || key.uv !== \"each-use\") return false;\n if (typeof key.id !== \"string\" || !SHA256_RE.test(key.id) || key.id !== (await deviceKeyId(key.spki))) return false;\n return !!(await importKey(key));\n}\n\n/** Can this key be enrolled at all? A machine refuses to trust a key it could never\n * verify with. The key's fields are closed: a passkey is `id`, `alg`, `spki` and\n * nothing else; a device key adds exactly `type`, `store` and `uv` (CONTROL.md \u00A72),\n * so a kind added later is refused by every machine that predates it. */\nexport async function keyUsable(key: TrustedKey): Promise<boolean> {\n if (!key || typeof key !== \"object\" || Array.isArray(key)) return false;\n const fields = Object.keys(key).filter((k) => (key as unknown as Record<string, unknown>)[k] !== undefined);\n if (\"type\" in key && key.type !== undefined) {\n if (!fields.every((f) => DEVICE_FIELDS.has(f))) return false;\n return deviceRecordValid(key);\n }\n if (!fields.every((f) => PASSKEY_FIELDS.has(f))) return false;\n return (\n typeof key.id === \"string\" &&\n /^[A-Za-z0-9_-]{16,1400}$/.test(key.id) &&\n (key.alg === ES256 || key.alg === RS256) &&\n !!(await importKey(key))\n );\n}\n\n// --- WebAuthn -------------------------------------------------------------------\n\n/** A WebAuthn origin a key may have been used from: https on the rp id or a subdomain;\n * for rp id `localhost`, plain http on any port too. A bare origin, as a browser writes it. */\nexport function originAllowed(origin: unknown, rpId: string): boolean {\n if (typeof origin !== \"string\" || !rpId) return false;\n let u: URL;\n try {\n u = new URL(origin);\n } catch {\n return false;\n }\n if (u.origin !== origin) return false;\n if (u.hostname !== rpId && !u.hostname.endsWith(`.${rpId}`)) return false;\n if (u.protocol === \"https:\") return true;\n return u.protocol === \"http:\" && rpId === \"localhost\";\n}\n\ntype Check = { ok: true } | { ok: false; why: string };\nconst no = (why: string): Check => ({ ok: false, why });\n\n// --- device keys (CONTROL.md \u00A73.2) ------------------------------------------------\n\n/** The first line of every message a device key signs. */\nexport const DEVICE_CONTEXT = \"sessionpipe.device.v1\";\n/** What a device signature is for: one use can never answer for another. */\nexport type DevicePurpose = \"grant\" | \"confirm\" | \"pair\";\n\n/** The exact bytes a device key signs: the context, the purpose, the rp id as the\n * machine recorded it, and the base64url (no padding) of the 32-byte challenge a\n * passkey would sign for that purpose, joined by single line feeds, none at the end. */\nexport function deviceMessage(purpose: DevicePurpose, rpId: string, challenge: Uint8Array): Uint8Array {\n return enc.encode(`${DEVICE_CONTEXT}\\n${purpose}\\n${rpId}\\n${b64url(challenge)}`);\n}\n\n/** One device signature over `challenge` for `purpose`, made with the device key `key`. */\nasync function verifyDeviceSignature(\n challenge: Uint8Array,\n a: object,\n key: TrustedKey,\n rpId: string,\n purpose: DevicePurpose,\n): Promise<Check> {\n const sig = a as Record<string, unknown>;\n for (const f of Object.keys(sig)) {\n if (f === \"str\" && purpose === \"grant\") continue;\n if (f !== \"cred\" && f !== \"sig\") return no(`a device key's signature carries no ${f}`);\n }\n if (sig.cred !== key.id) return no(\"signed with a key this machine doesn't trust\");\n if (!(await deviceRecordValid(key))) return no(\"a trusted key is unusable\");\n const raw = fromB64url(sig.sig);\n if (raw?.length !== 64) return no(\"a device key's signature is 64 bytes, r\u2016s\");\n const pub = await importKey(key);\n /* v8 ignore next -- deviceRecordValid imported it already */\n if (!pub) return no(\"a trusted key is unusable\");\n let good = false;\n try {\n good = await crypto.subtle.verify(\n { name: \"ECDSA\", hash: \"SHA-256\" },\n pub,\n u8(raw),\n u8(deviceMessage(purpose, rpId, challenge)),\n );\n } catch {\n /* v8 ignore next -- WebCrypto answers false for a bad signature; a throw is a broken runtime */\n good = false;\n }\n return good ? { ok: true } : no(\"the device key's signature doesn't verify\");\n}\n\n/** One signature over `challenge` by `key`, chosen from the machine's own record of\n * the key, never from the signature: a device key's signature for `purpose`\n * (CONTROL.md \u00A73.2), or a passkey assertion made by a verified person. */\nexport async function verifyAssertion(\n challenge: Uint8Array,\n a: Assertion | DeviceSignature | null | undefined,\n key: TrustedKey,\n rpId: string,\n purpose: DevicePurpose,\n): Promise<Check> {\n if (!a || typeof a !== \"object\") return no(\"no passkey signature\");\n if (key.type !== undefined) {\n if (key.type !== \"device\") return no(\"a trusted key is unusable\");\n return verifyDeviceSignature(challenge, a, key, rpId, purpose);\n }\n return verifyPasskeyAssertion(challenge, a as Assertion, key, rpId);\n}\n\nasync function verifyPasskeyAssertion(\n challenge: Uint8Array,\n a: Assertion,\n key: TrustedKey,\n rpId: string,\n): Promise<Check> {\n if (a.cred !== key.id) return no(\"signed with a key this machine doesn't trust\");\n const cdBytes = fromB64url(a.cd);\n const ad = fromB64url(a.ad);\n const sig = fromB64url(a.sig);\n if (!cdBytes || !ad || !sig?.length) return no(\"the passkey signature is malformed\");\n let cd: { type?: unknown; challenge?: unknown; origin?: unknown; crossOrigin?: unknown };\n try {\n cd = JSON.parse(dec.decode(cdBytes));\n } catch {\n return no(\"the passkey signature is malformed\");\n }\n if (!cd || typeof cd !== \"object\") return no(\"the passkey signature is malformed\");\n if (cd.type !== \"webauthn.get\") return no(\"the passkey answered a different question\");\n if (cd.challenge !== b64url(challenge)) return no(\"the passkey signed something else\");\n if (!originAllowed(cd.origin, rpId)) return no(`the passkey was used on another site, not ${rpId}`);\n if (cd.crossOrigin === true) return no(\"the passkey was used inside another site's frame\");\n if (ad.length < 37) return no(\"the passkey signature is malformed\");\n if (!sameBytes(ad.slice(0, 32), await sha256(rpId))) return no(`the passkey belongs to another site, not ${rpId}`);\n const flags = ad[32] as number;\n if (!(flags & UP) || !(flags & UV)) return no(\"the passkey signed without verifying the person\");\n const pub = await importKey(key);\n if (!pub) return no(\"a trusted key is unusable\");\n const signed = concat(ad, await sha256(cdBytes));\n let good = false;\n try {\n if (key.alg === ES256) {\n const raw = derToRaw(sig);\n good = !!raw && (await crypto.subtle.verify({ name: \"ECDSA\", hash: \"SHA-256\" }, pub, u8(raw), u8(signed)));\n } else {\n good = await crypto.subtle.verify({ name: \"RSASSA-PKCS1-v1_5\" }, pub, u8(sig), u8(signed));\n }\n } catch {\n good = false;\n }\n return good ? { ok: true } : no(\"the passkey signature doesn't verify\");\n}\n\n/** What a passkey signs to enroll on a machine: bound to that machine and that pairing code. */\nexport function pairChallenge(machine: string, code: string): Promise<Uint8Array> {\n return sha256(`sessionpipe.pair:${machine}:${code}`);\n}\n\n/** The enrollment proof: the new key's assertion over pairChallenge(machine, code).\n * Only after this verifies does the key go into the machine's trust store. */\nexport async function verifyEnrollment(o: {\n machine: string;\n code: string;\n key: TrustedKey;\n proof: Assertion | DeviceSignature;\n rpId: string;\n}): Promise<Check> {\n if (!(await keyUsable(o.key))) return no(\"the key is in a shape this machine can't use\");\n return verifyAssertion(await pairChallenge(o.machine, o.code), o.proof, o.key, o.rpId, \"pair\");\n}\n\n// --- the command ------------------------------------------------------------------\n\ntype Parsed = { ok: true; cmd: ControlCommand } | { ok: false; code: RefusalCode; why: string };\nconst bad = (code: RefusalCode, why: string): { ok: false; code: RefusalCode; why: string } => ({\n ok: false,\n code,\n why,\n});\nconst isNum = (n: unknown): n is number => typeof n === \"number\" && Number.isFinite(n);\n\n/** The session id rule per harness (ADAPTERS.md): Claude Code ids are UUIDs. */\nexport function sessionIdOk(harness: string, id: string): boolean {\n if (!SESSION_ID_RE.test(id)) return false;\n if (harness === \"claude-code\") return UUID_RE.test(id);\n return true;\n}\n\n/** Split `<harness>:<id>`; null when either half breaks its rule. */\nexport function parseSessionRef(ref: unknown): { harness: string; id: string } | null {\n if (typeof ref !== \"string\") return null;\n const i = ref.indexOf(\":\");\n if (i <= 0) return null;\n const harness = ref.slice(0, i);\n const id = ref.slice(i + 1);\n return HARNESS_RE.test(harness) && sessionIdOk(harness, id) ? { harness, id } : null;\n}\n\n/** Read the command's fields from the exact string. Structure only; no clock, no keys. */\nexport function parseCommand(str: unknown): Parsed {\n if (typeof str !== \"string\" || !str || str.length > CMD_MAX)\n return bad(\"malformed\", \"the command is missing or too long\");\n let c: Record<string, unknown>;\n try {\n c = JSON.parse(str);\n } catch {\n return bad(\"malformed\", \"the command is not JSON\");\n }\n if (!c || typeof c !== \"object\" || Array.isArray(c)) return bad(\"malformed\", \"the command is not an object\");\n if (c.v !== 1 || c.t !== COMMAND_TYPE) return bad(\"not_a_command\", \"not a sessionpipe control command\");\n if (typeof c.machine !== \"string\" || !MACHINE_RE.test(c.machine)) return bad(\"malformed\", \"which machine?\");\n if (!CONTROL_KINDS.includes(c.kind as ControlKind))\n return bad(\"unknown_kind\", \"the command asks for something unknown\");\n const ref = parseSessionRef(c.session);\n if (!ref) return bad(\"bad_session\", \"which session?\");\n if (typeof c.nonce !== \"string\" || !NONCE_RE.test(c.nonce))\n return bad(\"bad_nonce\", \"the nonce is missing or malformed\");\n if (!isNum(c.iat) || !Number.isInteger(c.iat) || c.iat <= 0)\n return bad(\"malformed\", \"the command has no signing time\");\n const has = (k: string) => c[k] !== undefined;\n const text = () => typeof c.text === \"string\" && c.text.trim().length > 0 && c.text.length <= TEXT_MAX;\n // A handoff's reply_to (CONTROL.md \u00A76): a session ref by the adapter's rule, never the\n // command's own session. Whether that session is on this machine is the daemon's to say.\n const replyToProblem = (): string | null => {\n if (!has(\"reply_to\")) return null;\n if (!parseSessionRef(c.reply_to)) return \"a reply_to names a session on this machine, <harness>:<id>\";\n if (c.reply_to === c.session) return \"a reply_to can't name the command's own session\";\n return null;\n };\n switch (c.kind as ControlKind) {\n case \"prompt\": {\n if (!text()) return bad(\"bad_fields\", `a prompt needs text, at most ${TEXT_MAX} characters`);\n if (has(\"for\") || has(\"decision\") || has(\"cwd\"))\n return bad(\"bad_fields\", \"a prompt carries text, files and a reply_to\");\n if (has(\"files\")) {\n const why = filesProblem(c.files);\n if (why) return bad(\"bad_fields\", why);\n }\n const why = replyToProblem();\n if (why) return bad(\"bad_fields\", why);\n break;\n }\n case \"start\": {\n if (!text()) return bad(\"bad_fields\", `a new session needs text, at most ${TEXT_MAX} characters`);\n if (typeof c.cwd !== \"string\" || !(isAbsolute(c.cwd) || HOME_RELATIVE_RE.test(c.cwd)) || c.cwd.length > 1024)\n return bad(\"bad_fields\", \"a new session needs an absolute folder, or one under ~\");\n if (has(\"for\") || has(\"decision\"))\n return bad(\"bad_fields\", \"a start carries text, cwd, files, a permission, a model and a reply_to\");\n if (has(\"files\")) {\n const why = filesProblem(c.files);\n if (why) return bad(\"bad_fields\", why);\n }\n const why = replyToProblem();\n if (why) return bad(\"bad_fields\", why);\n if (has(\"permission\") && levelRank(c.permission) < 0)\n return bad(\"bad_fields\", `a permission is one of ${START_PERMISSIONS.join(\", \")}`);\n if (has(\"model\") && (typeof c.model !== \"string\" || c.model.length > MODEL_MAX || !MODEL_RE.test(c.model)))\n return bad(\"bad_fields\", `a model is 1 to ${MODEL_MAX} letters, digits and ._:/@[]-, not starting with a dash`);\n break;\n }\n case \"permission.answer\":\n if (typeof c.for !== \"string\" || !c.for || c.for.length > 128) return bad(\"bad_fields\", \"which attention?\");\n if (c.decision !== \"allow\" && c.decision !== \"deny\") return bad(\"bad_fields\", \"allow or deny?\");\n if (has(\"text\") || has(\"cwd\")) return bad(\"bad_fields\", \"a permission answer carries for, decision and note\");\n break;\n case \"cancel\":\n if (has(\"text\") || has(\"for\") || has(\"decision\") || has(\"cwd\"))\n return bad(\"bad_fields\", \"a cancel carries nothing\");\n break;\n case \"key.add\": {\n // A device the machine already trusts vouches for a new one (CONTROL.md \u00A72): the\n // new key's id, algorithm and public key (and, for a device key, its type, store\n // and uv) are inside the signed bytes.\n if (c.session !== KEY_STORE_SESSION) return bad(\"bad_session\", `a key.add names ${KEY_STORE_SESSION}`);\n const k = c.key as Record<string, unknown> | undefined;\n const b64 = (v: unknown, min: number, max: number) =>\n typeof v === \"string\" && v.length >= min && v.length <= max && /^[A-Za-z0-9_-]+$/.test(v);\n if (\n !k ||\n typeof k !== \"object\" ||\n Array.isArray(k) ||\n !b64(k.id, 16, 1400) ||\n (k.alg !== -7 && k.alg !== -257) ||\n !b64(k.spki, 40, 2000)\n )\n return bad(\"bad_fields\", \"a key.add carries the key: id, alg (-7 or -257) and spki\");\n if (k.type !== undefined) {\n if (k.type !== \"device\") return bad(\"bad_fields\", \"a key's type is device, or absent for a passkey\");\n if (k.alg !== ES256 || k.store !== \"hardware\" || k.uv !== \"each-use\" || !SHA256_RE.test(k.id as string))\n return bad(\"bad_fields\", \"a device key is ES256, store hardware, uv each-use, named by its SPKI's hash\");\n if (Object.keys(k).some((f) => !DEVICE_FIELDS.has(f)))\n return bad(\"bad_fields\", \"a device key carries id, alg, spki, type, store and uv\");\n } else if (Object.keys(k).some((f) => !PASSKEY_FIELDS.has(f)))\n return bad(\"bad_fields\", \"a passkey carries id, alg and spki: store and uv belong to a device key\");\n if (has(\"name\") && (typeof c.name !== \"string\" || !c.name.trim() || c.name.length > 80))\n return bad(\"bad_fields\", \"a device name is text, at most 80 characters\");\n if (has(\"text\") || has(\"for\") || has(\"decision\") || has(\"cwd\"))\n return bad(\"bad_fields\", \"a key.add carries the key and a name\");\n break;\n }\n case \"pool\": {\n if (c.session !== POOL_SESSION) return bad(\"bad_session\", `a pool command names ${POOL_SESSION}`);\n const why = poolProblem(c);\n if (why) return bad(\"bad_fields\", why);\n break;\n }\n }\n if (c.kind !== \"key.add\" && (has(\"key\") || has(\"name\"))) return bad(\"bad_fields\", \"only a key.add carries a key\");\n if (c.kind !== \"pool\" && POOL_FIELDS.some(has)) return bad(\"bad_fields\", \"only a pool command carries pool fields\");\n if (c.kind !== \"start\" && c.kind !== \"prompt\" && has(\"files\"))\n return bad(\"bad_fields\", \"only a start or a prompt carries files\");\n if (c.kind !== \"start\" && c.kind !== \"prompt\" && has(\"reply_to\"))\n return bad(\"bad_fields\", \"only a start or a prompt carries a reply_to\");\n if (c.kind !== \"start\" && (has(\"permission\") || has(\"model\")))\n return bad(\"bad_fields\", \"only a start carries a model or a permission\");\n const allowed = FIELDS[c.kind as ControlKind];\n for (const k of Object.keys(c))\n if (!COMMON_FIELDS.includes(k) && !allowed.includes(k))\n return bad(\"bad_fields\", `the command carries a field its kind doesn't have: ${k.slice(0, 40)}`);\n if (has(\"note\") && (typeof c.note !== \"string\" || c.note.length > NOTE_MAX))\n return bad(\"bad_fields\", `a note is text, at most ${NOTE_MAX} characters`);\n return { ok: true, cmd: c as unknown as ControlCommand };\n}\n\n/** Why a `pool` command's fields break the rules (CONTROL.md \u00A76 `pool`), or null when\n * they don't. Each action takes exactly its own fields: an account where the action\n * names one, the whole order for `order`, the pasted code for `login.complete`. The\n * machine still checks the account exists and the order is complete (`refused`). */\nexport function poolProblem(c: Record<string, unknown>): string | null {\n const action = c.pool_action;\n if (!POOL_ACTIONS.includes(action as PoolAction))\n return `a pool command's action is one of ${POOL_ACTIONS.join(\", \")}`;\n const a = action as PoolAction;\n const account = c.pool_account !== undefined;\n const order = c.pool_order !== undefined;\n const code = c.pool_code !== undefined;\n const needsAccount = a === \"enable\" || a === \"disable\" || a === \"remove\" || a === \"login.complete\";\n if (needsAccount && !account) return `${a} names an account`;\n if (account && !needsAccount && a !== \"refresh\") return `${a} names no account`;\n if (account && (typeof c.pool_account !== \"string\" || !POOL_ACCOUNT_RE.test(c.pool_account)))\n return \"an account id is 1 to 128 letters, digits and ._:-\";\n if (order !== (a === \"order\")) return a === \"order\" ? \"order carries the whole order\" : `${a} carries no order`;\n if (order) {\n const o = c.pool_order;\n if (!Array.isArray(o) || o.length < 1 || o.length > POOL_ORDER_MAX)\n return `an order is a list of 1 to ${POOL_ORDER_MAX} account ids`;\n if (!o.every((id) => typeof id === \"string\" && POOL_ACCOUNT_RE.test(id)))\n return \"an account id is 1 to 128 letters, digits and ._:-\";\n if (new Set(o).size !== o.length) return \"an order names each account once\";\n }\n if (code !== (a === \"login.complete\"))\n return a === \"login.complete\" ? \"login.complete carries the pasted code\" : `${a} carries no code`;\n if (\n code &&\n (typeof c.pool_code !== \"string\" ||\n !c.pool_code ||\n c.pool_code.length > POOL_CODE_MAX ||\n /[\\r\\n]/.test(c.pool_code))\n )\n return `a code is 1 to ${POOL_CODE_MAX} characters on one line`;\n return null;\n}\n\n/** Why a start's or a prompt's `files` breaks the rules (CONTROL.md \u00A73.1), or null when it doesn't. */\nexport function filesProblem(files: unknown): string | null {\n if (!Array.isArray(files) || files.length < 1 || files.length > FILES_MAX)\n return `files is a list of 1 to ${FILES_MAX} files`;\n const seen = new Set<string>();\n let total = 0;\n for (const f of files as unknown[]) {\n if (!f || typeof f !== \"object\" || Array.isArray(f)) return \"each file is an object\";\n const { name, type, size, sha256: hash } = f as Record<string, unknown>;\n const why = fileNameProblem(name);\n if (why) return why;\n const key = (name as string).normalize(\"NFC\").toLowerCase();\n if (seen.has(key)) return \"two files have the same name\";\n seen.add(key);\n if (typeof type !== \"string\" || type.length > FILE_TYPE_MAX || !MEDIA_TYPE_RE.test(type))\n return \"a file's type isn't a media type\";\n if (!isNum(size) || !Number.isInteger(size) || size < 1 || size > FILE_SIZE_MAX)\n return `a file is 1 byte to ${FILE_SIZE_MAX / 1_048_576} MiB`;\n total += size;\n if (typeof hash !== \"string\" || !SHA256_RE.test(hash)) return \"a file's sha256 isn't 43 base64url characters\";\n }\n if (total > FILES_TOTAL_MAX) return `the files add up to more than ${FILES_TOTAL_MAX / 1_048_576} MiB`;\n return null;\n}\n\n/** A file name the machine can write inside the session's folder and nowhere else. */\nfunction fileNameProblem(name: unknown): string | null {\n if (typeof name !== \"string\") return \"a file has no name\";\n const n = name.normalize(\"NFC\");\n if (n.length < 1 || n.length > FILE_NAME_MAX) return `a file name is 1 to ${FILE_NAME_MAX} characters`;\n if (BAD_NAME_CHAR_RE.test(n)) return \"a file name has a slash, a backslash or a control character\";\n if (n.startsWith(\".\")) return \"a file name can't start with a dot\";\n return null;\n}\n\n/** A `start`'s `cwd` under the machine's home: `~` alone, or `~/\u2026` (`~\\\u2026`). Never `~user`. */\nexport const HOME_RELATIVE_RE = /^~(?:[\\\\/]|$)/;\n\nfunction isAbsolute(p: string): boolean {\n return p.startsWith(\"/\") || /^[A-Za-z]:[\\\\/]/.test(p);\n}\n\n/** The grant's fields, read from its exact string. */\nexport function parseGrant(str: unknown, rpId: string): { ok: true; grant: Grant } | { ok: false; why: string } {\n if (typeof str !== \"string\" || !str || str.length > GRANT_STR_MAX)\n return { ok: false, why: \"the grant is missing or too long\" };\n let g: Record<string, unknown>;\n try {\n g = JSON.parse(str);\n } catch {\n return { ok: false, why: \"the grant is not JSON\" };\n }\n if (!g || typeof g !== \"object\" || Array.isArray(g)) return { ok: false, why: \"the grant is not an object\" };\n if (g.v !== 1 || g.t !== GRANT_TYPE) return { ok: false, why: \"not a sessionpipe grant\" };\n if (g.rp !== rpId) return { ok: false, why: `the grant is for another site, not ${rpId}` };\n if (typeof g.pub !== \"string\" || !fromB64url(g.pub)?.length || !isNum(g.iat) || !isNum(g.exp))\n return { ok: false, why: \"the grant is missing parts\" };\n if (g.exp <= g.iat) return { ok: false, why: \"the grant ends before it starts\" };\n if (g.exp - g.iat > GRANT_MAX_MS + SKEW_MS) return { ok: false, why: \"the grant lasts longer than a day\" };\n return { ok: true, grant: g as unknown as Grant };\n}\n\nasync function verifyCsig(cmd: string, csig: unknown, pub: string): Promise<boolean> {\n const sig = fromB64url(csig);\n const spki = fromB64url(pub);\n if (sig?.length !== 64 || !spki) return false;\n try {\n const key = await crypto.subtle.importKey(\"spki\", u8(spki), { name: \"ECDSA\", namedCurve: \"P-256\" }, false, [\n \"verify\",\n ]);\n return await crypto.subtle.verify({ name: \"ECDSA\", hash: \"SHA-256\" }, key, u8(sig), u8(enc.encode(cmd)));\n } catch {\n return false;\n }\n}\n\n/**\n * Is this a command this machine may run? The checks, in the spec's order\n * (CONTROL.md \u00A75): the exact bytes parse; the command is for this machine; it is\n * signed \u2014 a grant from a trusted key plus the day key's signature, or a confirm\n * over sha256(cmd); it was signed within the age window; its nonce is new.\n * The folder and open-attention checks that follow are the daemon's (they read\n * the machine, not the command).\n */\nexport async function verifyCommand(s: SignedCommand | null | undefined, o: VerifyOptions): Promise<Verdict> {\n if (!s || typeof s !== \"object\") return bad(\"malformed\", \"nothing to verify\");\n const p = parseCommand(s.cmd);\n if (!p.ok) return p;\n const c = p.cmd;\n if (c.machine !== o.machine) return bad(\"wrong_machine\", \"the command is for another machine\");\n\n let via: \"grant\" | \"confirm\" | null = null;\n let keyId = \"\";\n let refusal: { code: RefusalCode; why: string } | null = null;\n if (s.grant && s.csig) {\n const r = await checkGrantPath(s, c, o);\n if (r.ok) {\n via = \"grant\";\n keyId = r.key;\n } else refusal = r;\n }\n if (!via && s.confirm) {\n const key = o.trusted.find((k) => k && k.id === s.confirm?.cred);\n if (!key) refusal = { code: \"untrusted_key\", why: \"confirmed with a key this machine doesn't trust\" };\n else {\n const a = await verifyAssertion(await sha256(s.cmd), s.confirm, key, o.rpId, \"confirm\");\n if (a.ok) {\n via = \"confirm\";\n keyId = key.id;\n } else refusal = { code: \"bad_assertion\", why: a.why };\n }\n }\n if (!via) {\n if (refusal) return bad(refusal.code, refusal.why);\n return bad(\"unsigned\", \"the command isn't signed\");\n }\n // The key a key.add brings must be one this machine could ever verify with: an\n // SPKI it can import for that alg, and every rule of a device key (CONTROL.md \u00A72).\n if (c.kind === \"key.add\" && !(await keyUsable(c.key as TrustedKey)))\n return bad(\"bad_fields\", \"the key a key.add brings is in a shape this machine can't use\");\n if (c.iat > o.now + SKEW_MS) return bad(\"future\", \"the command is dated in the future (is this clock right?)\");\n if (o.now - c.iat > (o.maxAgeMs ?? MACHINE_MAX_AGE_MS)) return bad(\"too_old\", \"the command was signed too long ago\");\n if (o.seenNonce) {\n let seen: boolean;\n try {\n seen = await o.seenNonce(c.nonce);\n } catch {\n return bad(\"nonce_store\", \"the nonce couldn't be recorded, so the command didn't run\");\n }\n if (seen) return bad(\"replay\", \"this command was already used once\");\n }\n return { ok: true, cmd: c, via, key: keyId };\n}\n\nasync function checkGrantPath(\n s: SignedCommand,\n c: ControlCommand,\n o: VerifyOptions,\n): Promise<{ ok: true; key: string } | { ok: false; code: RefusalCode; why: string }> {\n const bundle = s.grant as GrantBundle | DeviceGrant;\n const g = parseGrant(bundle.str, o.rpId);\n if (!g.ok) return bad(\"bad_grant\", g.why);\n const key = o.trusted.find((k) => k && k.id === bundle.cred);\n if (!key) return bad(\"untrusted_key\", \"the grant was signed with a key this machine doesn't trust\");\n const a = await verifyAssertion(await sha256(bundle.str), bundle, key, o.rpId, \"grant\");\n if (!a.ok) return bad(\"bad_assertion\", a.why);\n if (!(await verifyCsig(s.cmd, s.csig, g.grant.pub)))\n return bad(\"bad_csig\", \"the command's signature doesn't verify \u2014 it was changed after it was signed\");\n if (c.iat < g.grant.iat - SKEW_MS || c.iat > g.grant.exp)\n return bad(\"outside_grant\", \"the command was signed outside its grant's window\");\n return { ok: true, key: key.id };\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Codex CLI (OpenAI) \u2014 docs: https://learn.chatgpt.com/docs/hooks\n// Config: ~/.codex/hooks.json (Claude-shaped, 12 events, timeouts in seconds) AND\n// `notify` at the top level of config.toml as the fallback when hooks are off or\n// not yet trusted (non-managed hooks need a /hooks review in the CLI). An existing\n// notify (Codex Computer Use) is chained through --previous-notify, never replaced.\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { homeDir } from \"../paths.js\";\nimport { ends, parseLines, recentFiles, scanLines } from \"../readers/files.js\";\nimport { codexInjected, readCodex } from \"../readers/transcripts.js\";\nimport type { DataOf } from \"../types.js\";\nimport { installClaudeShaped, installedClaudeShaped, uninstallClaudeShaped, writeBack } from \"./claude-shaped.js\";\nimport * as toml from \"./codex-config.js\";\nimport { codexReplayBoundary, codexSubagent } from \"./codex-rollout.js\";\nimport type {\n Adapter,\n BackfillRow,\n HookCommand,\n HookInput,\n HookResult,\n InstalledReport,\n InstallOptions,\n InstallReport,\n SessionFacts,\n} from \"./types.js\";\n\nconst NAME = \"codex\";\nexport const CODEX_EVENTS = [\n \"SessionStart\",\n \"UserPromptSubmit\",\n \"PreToolUse\",\n \"PostToolUse\",\n \"PermissionRequest\",\n \"Stop\",\n \"Interrupt\",\n \"SubagentStart\",\n \"SubagentStop\",\n \"PreCompact\",\n \"PostCompact\",\n \"SessionEnd\",\n] as const;\nconst OPTS = { matcherFor: (e: string) => (/Tool|Permission/.test(e) ? \"\" : undefined), timeout: 5 };\nconst eventsFor = (lean?: boolean) => (lean ? CODEX_EVENTS.filter((e) => e !== \"PreToolUse\") : [...CODEX_EVENTS]);\n\nconst codexHome = (env: NodeJS.ProcessEnv) => env.CODEX_HOME || path.join(homeDir(), \".codex\");\nconst hooksFile = (env: NodeJS.ProcessEnv) => path.join(codexHome(env), \"hooks.json\");\nconst configFile = (env: NodeJS.ProcessEnv) => path.join(codexHome(env), \"config.toml\");\nconst sessionsDir = (env: NodeJS.ProcessEnv) => path.join(codexHome(env), \"sessions\");\n\nconst validTool = (n: unknown): n is string => typeof n === \"string\" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);\nconst endReason = (r: unknown): \"clear\" | \"logout\" | \"exit\" | \"other\" =>\n r === \"clear\" || r === \"logout\" || r === \"exit\" ? r : \"other\";\n\n/** What config.toml without our notify line means, said with every refusal of it. */\nconst UNTIL_APPROVED =\n \"Codex will report nothing until you approve the hooks: start `codex` and accept the hook review\";\n\nfunction installNotify(cmd: HookCommand, env: NodeJS.ProcessEnv) {\n const file = configFile(env);\n let text = \"\";\n let existed = true;\n try {\n text = readFileSync(file, \"utf8\");\n } catch (e) {\n // Absent only when the system says so: a file that is there but can't be read (0200,\n // root's) is still the person's, never `created` for uninstall to delete.\n existed = (e as NodeJS.ErrnoException).code !== \"ENOENT\";\n }\n const argv = cmd([NAME, \"notify\"]).argv;\n const line = `notify = ${JSON.stringify(argv)}`;\n const n = toml.readNotify(text);\n /** The file written, with this note; or the refusal's report in its place. */\n const write = (next: string, note: string): InstallReport => {\n const bad = writeBack(file, next, existed);\n return bad ? { ...bad, note: `${bad.note}. ${UNTIL_APPROVED}` } : { file, changed: true, note };\n };\n if (n && !n.other) {\n if (!n.misplaced && JSON.stringify(n.argv) === JSON.stringify(argv))\n return { file, changed: false, note: \"notify current\" };\n return write(\n toml.withNotify(text, line),\n n.misplaced\n ? \"notify moved to the top level (it sat inside a [table], where Codex never reads it)\"\n : \"notify updated\",\n );\n }\n if (n?.other) {\n const prev = toml.previousNotify(n.argv);\n if (toml.isComputerUse(n.argv)) {\n if (prev && toml.OURS.test(JSON.stringify(prev))) {\n if (JSON.stringify(prev) === JSON.stringify(argv))\n return { file, changed: false, note: \"notify current (after Codex Computer Use)\" };\n }\n if (!prev || toml.OURS.test(JSON.stringify(prev)))\n return write(\n toml.replaceNotify(text, n, `notify = ${toml.withPreviousRaw(n.raw, argv)}`),\n \"chained after Codex Computer Use (--previous-notify)\",\n );\n }\n return {\n file,\n changed: false,\n skipped: true,\n note: `notify belongs to ${(n.argv?.[0] ?? n.raw).replace(/^.*\\//, \"\").slice(0, 40)}; left alone. ${UNTIL_APPROVED}`,\n };\n }\n return write(toml.withNotify(text, line), \"notify set at the top level\");\n}\n\nfunction uninstallNotify(env: NodeJS.ProcessEnv): InstallReport {\n const file = configFile(env);\n let text: string;\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return { file, changed: false };\n }\n const n = toml.readNotify(text);\n if (!n) return { file, changed: false };\n if (!n.other) return writeBack(file, toml.withoutNotify(text)) ?? { file, changed: true };\n const prev = toml.previousNotify(n.argv);\n if (toml.isComputerUse(n.argv) && prev && toml.OURS.test(JSON.stringify(prev)))\n return (\n writeBack(file, toml.replaceNotify(text, n, `notify = ${toml.stripPreviousRaw(n.raw)}`)) ?? {\n file,\n changed: true,\n }\n );\n return { file, changed: false };\n}\n\n/** The rollout file of one thread: `<CODEX_HOME>/sessions/YYYY/MM/DD/rollout-<time>-<thread\n * id>.jsonl`, newest day first. Null for an id that isn't a thread id's shape, so a\n * short or odd one never matches some other session's file. */\nexport function findRollout(threadId: string, env: NodeJS.ProcessEnv = process.env): string | null {\n if (!/^[A-Za-z0-9-]{8,128}$/.test(threadId)) return null;\n let best: string | null = null;\n const walk = (dir: string, depth: number) => {\n let names: string[] = [];\n try {\n names = readdirSync(dir);\n } catch {\n return;\n }\n for (const n of names.sort().reverse()) {\n const p = path.join(dir, n);\n if (n.endsWith(`-${threadId}.jsonl`)) {\n best = p;\n return;\n }\n if (depth < 3) {\n walk(p, depth + 1);\n if (best) return;\n }\n }\n };\n walk(sessionsDir(env), 0);\n return best;\n}\n\n/** A rollout's first record, its own session_meta. About 50 KB (it carries the base\n * instructions), so it is read on its own rather than from a sampled head. */\nfunction rolloutMeta(file: string): Record<string, unknown> | null {\n return scanLines(file, (line) => parseLines([line])[0] ?? {});\n}\n\n/** A rollout named for a thread other than `id` (`rollout-<time>-<thread id>.jsonl`).\n * A name of any other shape says nothing, so it is not taken for another thread's. */\nfunction ofAnotherThread(file: string, id: string): boolean {\n const other = threadOf(file);\n return !!other && other !== id.toLowerCase();\n}\n/** The thread a rollout is named for, lowercased; null for a name of any other shape. */\nfunction threadOf(file: string): string | null {\n const m = /-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\\.jsonl(?:\\.zst)?$/i.exec(file);\n return m?.[1] ? m[1].toLowerCase() : null;\n}\n\nexport const codex: Adapter = {\n name: NAME,\n events: CODEX_EVENTS,\n detect: (env = process.env) => existsSync(codexHome(env)),\n configFiles: (env = process.env) => [hooksFile(env), configFile(env)],\n install: (cmd, env = process.env, opts: InstallOptions = {}) => [\n installClaudeShaped(hooksFile(env), NAME, eventsFor(opts.lean), cmd, OPTS),\n installNotify(cmd, env),\n ],\n uninstall: (env = process.env, opts: InstallOptions = {}) => [\n uninstallClaudeShaped(hooksFile(env), CODEX_EVENTS, { created: opts.created?.includes(hooksFile(env)) }),\n uninstallNotify(env),\n ],\n installed: (cmd, env = process.env, opts: InstallOptions = {}) => {\n const out: InstalledReport[] = [\n {\n file: hooksFile(env),\n state: installedClaudeShaped(hooksFile(env), NAME, eventsFor(opts.lean), cmd, OPTS),\n note: \"Codex runs non-managed hooks only after the review in `codex` (/hooks); until then nothing is reported unless the notify fallback is ours\",\n },\n ];\n let text = \"\";\n try {\n text = readFileSync(configFile(env), \"utf8\");\n } catch {}\n const n = toml.readNotify(text);\n const argv = cmd([NAME, \"notify\"]).argv;\n const prev = n ? toml.previousNotify(n.argv) : null;\n const ours = n && (!n.other || (prev && JSON.stringify(prev) === JSON.stringify(argv)));\n out.push({\n file: configFile(env),\n state:\n !n || (n.other && !ours)\n ? \"missing\"\n : n.misplaced\n ? \"misplaced\"\n : JSON.stringify(n.argv) === JSON.stringify(argv) || (prev && JSON.stringify(prev) === JSON.stringify(argv))\n ? \"current\"\n : \"stale\",\n });\n return out;\n },\n\n fromHook(input: HookInput): HookResult | null {\n const event = input.argv[1] ?? \"\";\n let s: Record<string, unknown> = {};\n if (event === \"notify\") {\n // Codex's notify passes one JSON argument: {\"type\":\"agent-turn-complete\",\"thread-id\":\u2026,\"turn-id\":\u2026,\"cwd\":\u2026}\n const raw = input.argv.find((a) => a.startsWith(\"{\")) ?? input.stdin;\n try {\n const j: unknown = raw ? JSON.parse(raw) : {};\n if (j && typeof j === \"object\" && !Array.isArray(j)) s = j as Record<string, unknown>;\n } catch {}\n const id = (s[\"thread-id\"] ?? s.thread_id ?? s[\"session-id\"] ?? s.session_id) as string | undefined;\n if (!id) return null;\n // Notify names the thread whose turn ended. Without a rollout it is an ephemeral\n // thread, kept nowhere (the hooks' `transcript_path: null` below). A subagent's\n // thread belongs to its parent, whose hooks already report it: SubagentStart and\n // SubagentStop name it as `agent_id`. Neither is a session of its own.\n const file = findRollout(String(id), input.env as NodeJS.ProcessEnv);\n if (!file || codexSubagent(rolloutMeta(file))) return null;\n const r: HookResult = {\n session: { id: String(id), cwd: typeof s.cwd === \"string\" ? s.cwd : input.cwd },\n events: [\n {\n type: \"turn.ended\",\n data: { reason: \"stop\", ...(typeof s[\"turn-id\"] === \"string\" ? { turn_id: s[\"turn-id\"] } : {}) },\n harnessEvent: \"notify\",\n },\n ],\n hints: {},\n transcript: file,\n };\n return r;\n }\n try {\n // A stdin that is JSON but not an object (`null`, a number) is no payload.\n const j: unknown = input.stdin ? JSON.parse(input.stdin) : {};\n if (j && typeof j === \"object\" && !Array.isArray(j)) s = j as Record<string, unknown>;\n } catch {}\n const id = (s.session_id ?? s.thread_id ?? s.conversation_id ?? s[\"thread-id\"] ?? s[\"session-id\"]) as\n | string\n | undefined;\n if (!id) return null;\n // `transcript_path: null` is an ephemeral thread (recorded on codex-cli 0.160.0):\n // Codex Desktop's own background threads (ambient suggestions and their safety\n // review) and `codex exec --ephemeral`. Codex keeps nothing of one, so there is no\n // session to name, read or resume.\n if (s.transcript_path === null) return null;\n const session: HookResult[\"session\"] = { id: String(id), cwd: typeof s.cwd === \"string\" ? s.cwd : input.cwd };\n if (typeof s.model === \"string\") session.model = s.model;\n const turn_id = typeof s.turn_id === \"string\" ? s.turn_id : undefined;\n const tool = validTool(s.tool_name) ? s.tool_name : undefined;\n const call_id = typeof s.tool_use_id === \"string\" ? s.tool_use_id : undefined;\n // A subagent's hooks carry the parent's session_id (ADAPTERS.md, Threads): its own\n // id where the payload names it, else the thread its rollout is named for. Without\n // it a subagent's tool calls read as the main thread's (issue #208: a sink that\n // thins subagents' tool events would be sent every one).\n const thread = typeof s.transcript_path === \"string\" ? threadOf(s.transcript_path) : null;\n const agent_id =\n typeof s.agent_id === \"string\" && s.agent_id\n ? s.agent_id\n : typeof s.subagent_id === \"string\" && s.subagent_id\n ? s.subagent_id\n : thread && thread !== String(id).toLowerCase()\n ? thread\n : undefined;\n const ev = (type: string, data: Record<string, unknown>) => ({ type, data: prune(data), harnessEvent: event });\n const events: HookResult[\"events\"] = [];\n switch (event) {\n case \"SessionStart\":\n events.push(\n ev(\"session.started\", {\n source:\n typeof s.source === \"string\" && [\"startup\", \"resume\", \"clear\", \"compact\", \"fork\"].includes(s.source)\n ? s.source\n : \"unknown\",\n }),\n );\n break;\n case \"UserPromptSubmit\":\n events.push(\n ev(\"turn.started\", {\n turn_id,\n prompt_chars: typeof s.prompt === \"string\" ? [...s.prompt].length : undefined,\n }),\n );\n break;\n case \"PreToolUse\":\n if (tool) events.push(ev(\"tool.started\", { tool, call_id, turn_id, agent_id, input: s.tool_input }));\n break;\n case \"PostToolUse\":\n if (tool)\n events.push(\n ev(\"tool.ended\", {\n tool,\n call_id,\n turn_id,\n agent_id,\n ok: true,\n input: s.tool_input,\n output: s.tool_response,\n }),\n );\n break;\n case \"PermissionRequest\":\n events.push(\n ev(\"attention.needed\", {\n attention_id: call_id ?? `perm-${Date.now()}`,\n kind: \"permission\",\n tool,\n agent_id,\n }),\n );\n break;\n case \"Stop\":\n events.push(ev(\"turn.ended\", { turn_id, reason: \"stop\" }));\n break;\n case \"Interrupt\":\n events.push(ev(\"turn.ended\", { turn_id, reason: \"interrupt\" }));\n break;\n case \"SubagentStart\":\n events.push(\n ev(\"subagent.started\", {\n agent_id: String(s.agent_id ?? s.subagent_id ?? \"\"),\n agent_type: typeof s.agent_type === \"string\" ? s.agent_type : undefined,\n }),\n );\n break;\n case \"SubagentStop\":\n events.push(\n ev(\"subagent.ended\", {\n agent_id: String(s.agent_id ?? s.subagent_id ?? \"\"),\n agent_type: typeof s.agent_type === \"string\" ? s.agent_type : undefined,\n }),\n );\n break;\n case \"PreCompact\":\n events.push(ev(\"context.compacted\", { trigger: s.trigger === \"manual\" ? \"manual\" : \"auto\" }));\n break;\n case \"PostCompact\":\n break;\n case \"SessionEnd\":\n events.push(ev(\"session.ended\", { reason: endReason(s.reason) }));\n break;\n default:\n return null;\n }\n const r: HookResult = { session, events, hints: {} };\n // Inside a subagent Codex sends the parent's session_id with the subagent's own\n // rollout (its SubagentStart, its tool calls): that file is not this session's.\n if (typeof s.transcript_path === \"string\" && !ofAnotherThread(s.transcript_path, String(id)))\n r.transcript = s.transcript_path;\n return r;\n },\n\n readTranscript(file, fromLine) {\n let text = \"\";\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return { turns: [], line: fromLine };\n }\n return readCodex(text.split(\"\\n\"), fromLine);\n },\n\n facts(session, transcript): SessionFacts {\n const out: SessionFacts = {};\n if (transcript) Object.assign(out, codexFacts(transcript));\n return out;\n },\n\n backfill(sinceMs, env = process.env): BackfillRow[] {\n const rows: BackfillRow[] = [];\n for (const f of recentFiles(sessionsDir(env), 3, sinceMs, (n) => /^rollout-.*\\.jsonl$/.test(n))) {\n const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\\.jsonl$/i.exec(path.basename(f));\n if (!m?.[1]) continue;\n // A subagent's rollout is part of its parent's session, as in fromHook's notify.\n if (codexSubagent(rolloutMeta(f))) continue;\n const facts = codexFacts(f);\n if (!facts.started_at || !facts.last_at) continue;\n const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;\n rows.push({ session: { id: m[1], ...rest }, started_at, last_at });\n }\n return rows;\n },\n\n limits: (transcript) => codexLimits(transcript),\n};\n\n/** Epoch seconds as RFC 3339; anything that is not a plausible time is no time. */\nconst epochIso = (t: unknown): string | undefined =>\n typeof t === \"number\" && t > 0 && t < 1e11 ? new Date(t * 1000).toISOString() : undefined;\n\nfunction prune<T extends Record<string, unknown>>(o: T): T {\n for (const k of Object.keys(o)) if (o[k] === undefined) delete o[k];\n return o;\n}\n\n/** The account's limits from the last `token_count` that carries them (recorded\n * 2026-10-21, codex-cli 0.163): `rate_limits.primary` / `secondary`, each with\n * `used_percent`, `window_minutes` and `resets_at` in epoch seconds, and `plan_type`.\n * Every turn writes one, so the tail is enough. Codex keeps one snapshot and overwrites\n * it with whichever bucket the server named last (`limit_id`: `codex`, or a model's own\n * such as `codex_other`), so only the account's `codex` bucket is read, never another's\n * windows under its names. */\nexport function codexLimits(file: string): DataOf<\"limits.observed\"> | null {\n const { tail } = ends(file, 0, 256 * 1024);\n for (const r of parseLines(tail).reverse()) {\n const p = r.payload as { type?: unknown; rate_limits?: unknown } | undefined;\n if (r.type !== \"event_msg\" || p?.type !== \"token_count\") continue;\n if (!p.rate_limits || typeof p.rate_limits !== \"object\") continue;\n const rl = p.rate_limits as Record<string, unknown>;\n if (rl.limit_id != null && rl.limit_id !== \"codex\") continue;\n const windows: DataOf<\"limits.observed\">[\"windows\"] = [];\n for (const id of [\"primary\", \"secondary\"]) {\n const w = rl[id] as { used_percent?: unknown; window_minutes?: unknown; resets_at?: unknown } | null;\n if (typeof w?.used_percent !== \"number\" || w.used_percent < 0) continue;\n windows.push(\n prune({\n id,\n used_percent: w.used_percent,\n window_minutes:\n Number.isInteger(w.window_minutes) && Number(w.window_minutes) > 0 ? Number(w.window_minutes) : undefined,\n resets_at: epochIso(w.resets_at),\n }),\n );\n }\n if (!windows.length) return null;\n return prune({ windows, plan: typeof rl.plan_type === \"string\" ? rl.plan_type.slice(0, 64) : undefined });\n }\n return null;\n}\n\n/** Codex keeps no title; its first real ask stands in. A fork's replay is not its ask. */\nexport function codexFacts(file: string): SessionFacts {\n const { head, tail } = ends(file, 128 * 1024, 64 * 1024);\n let H = parseLines(head);\n let T = parseLines(tail);\n const out: SessionFacts = {};\n const meta = H.find((j) => j.type === \"session_meta\" && j.payload);\n const boundary = codexReplayBoundary(meta);\n if (meta?.payload) {\n const p = meta.payload as {\n cwd?: string;\n cli_version?: string;\n git?: { branch?: string; repository_url?: string };\n };\n if (p.cwd) out.cwd = p.cwd;\n if (p.cli_version) out.harness_version = String(p.cli_version);\n if (p.git?.branch) out.branch = p.git.branch;\n if (p.git?.repository_url) out.repo = String(p.git.repository_url).replace(/\\/\\/[^/@]*@/, \"//\");\n }\n if (boundary) {\n const h = H.findIndex(boundary);\n const t = T.findIndex(boundary);\n H = h < 0 ? [] : H.slice(h);\n // The samples can overlap, so trim each one at the boundary independently.\n // If both miss it, scan the gap before trusting the tail: it could still be\n // parent replay. Stop as soon as the boundary is found, without loading the file.\n const found =\n h >= 0 ||\n t >= 0 ||\n scanLines(file, (line) => (boundary(parseLines([line])[0] ?? null) ? true : null), Number.POSITIVE_INFINITY);\n T = t >= 0 ? T.slice(t) : found ? T : [];\n }\n const ctx = H.concat(T)\n .reverse()\n .find((j) => j.type === \"turn_context\" && (j.payload as { model?: string } | undefined)?.model) as\n | { payload?: { model?: string } }\n | undefined;\n if (ctx?.payload?.model) out.model = ctx.payload.model;\n for (const j of H) {\n const p = j.payload as { type?: string; role?: string; content?: { type?: string; text?: string }[] } | undefined;\n if (j.type !== \"response_item\" || !p || p.type !== \"message\" || p.role !== \"user\") continue;\n const text = (p.content ?? [])\n .filter((b) => b && b.type === \"input_text\" && typeof b.text === \"string\")\n .map((b) => b.text as string)\n .join(\"\\n\")\n .trim();\n if (!text || codexInjected(text)) continue;\n const line = text.split(\"\\n\")[0] as string; // trimmed, so its first line is its first non-blank one\n out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\\s+\\S*$/, \"\")}\u2026` : line;\n out.title_source = \"first-ask\";\n break;\n }\n const times = (boundary && meta ? [meta, ...H, ...T] : H.concat(T))\n .map((j) => Date.parse(String(j.timestamp ?? \"\")))\n .filter((n) => n > 0);\n if (times.length) {\n out.started_at = Math.min(...times);\n out.last_at = Math.max(...times);\n }\n return out;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Codex's ~/.codex/config.toml, as far as the notify fallback needs it: its one\n// top-level `notify` command. Codex reads `notify` only at the top level \u2014 before\n// the first [table] \u2014 and Codex itself appends a [projects.\"<path>\"] table for every\n// folder it trusts, so a line appended at the end lands inside one and never runs.\n// Ported from spacesheep-cli lib/codex-config.js. Pure text in, text out.\n\nconst NOTIFY_LINE = /^[ \\t]*notify[ \\t]*=[ \\t]*(\\[.*\\])[ \\t]*(?:#.*)?(?=\\r?$)/m;\nconst NOTIFY_KEY = /^[ \\t]*notify[ \\t]*=/m;\n/** A notify that runs sessionpipe's hook directly (or through its launcher). */\nexport const OURS = /(?:hook\\.js|sessionpipe-hook)\",\\s*\"codex\",\\s*\"notify\"/;\n\nfunction scan(line: string, st: { depth: number; str: string | null }): void {\n let i = 0;\n while (i < line.length) {\n if (st.str) {\n const j = line.indexOf(st.str, i);\n if (j < 0) return;\n i = j + 3;\n st.str = null;\n continue;\n }\n const ch = line[i];\n if (ch === \"#\") return;\n if (line.startsWith('\"\"\"', i) || line.startsWith(\"'''\", i)) {\n st.str = line.slice(i, i + 3);\n i += 3;\n continue;\n }\n if (ch === '\"') {\n i++;\n while (i < line.length && line[i] !== '\"') i += line[i] === \"\\\\\" ? 2 : 1;\n i++;\n continue;\n }\n if (ch === \"'\") {\n const j = line.indexOf(\"'\", i + 1);\n i = j < 0 ? line.length : j + 1;\n continue;\n }\n if (ch === \"[\") st.depth++;\n else if (ch === \"]\") st.depth = Math.max(0, st.depth - 1);\n i++;\n }\n}\n\n/** Offset of the first table header, or -1. */\nexport function firstTableAt(text: string): number {\n const st = { depth: 0, str: null as string | null };\n let pos = 0;\n for (const line of text.split(\"\\n\")) {\n if (!st.str && st.depth === 0 && /^[ \\t]*\\[/.test(line)) return pos;\n scan(line, st);\n pos += line.length + 1;\n }\n return -1;\n}\n\nfunction split(text: string) {\n const at = firstTableAt(text);\n return at < 0 ? { head: text, tail: \"\", at: text.length } : { head: text.slice(0, at), tail: text.slice(at), at };\n}\n\nexport function parseArgv(raw: string): string[] | null {\n try {\n const a = JSON.parse(raw) as unknown;\n return Array.isArray(a) && a.every((x) => typeof x === \"string\") ? (a as string[]) : null;\n } catch {\n return null;\n }\n}\n\nexport interface Notify {\n raw: string;\n argv: string[] | null;\n index: number;\n length: number;\n misplaced: boolean;\n other: boolean;\n}\n\n/** notify as Codex sees it; `misplaced` for a line of ours inside a table. */\nexport function readNotify(text: string): Notify | null {\n const { head, tail, at } = split(text);\n const m = NOTIFY_LINE.exec(head);\n if (m) {\n const raw = m[1] as string;\n return { raw, argv: parseArgv(raw), index: m.index, length: m[0].length, misplaced: false, other: !OURS.test(raw) };\n }\n const k = NOTIFY_KEY.exec(head);\n if (k) {\n const rest = head.slice(k.index).split(\"\\n\")[0] as string;\n return {\n raw: rest.replace(NOTIFY_KEY, \"\").trim(),\n argv: null,\n index: k.index,\n length: 0,\n misplaced: false,\n other: true,\n };\n }\n const t = NOTIFY_LINE.exec(tail);\n if (t) {\n const raw = t[1] as string;\n if (OURS.test(raw))\n return { raw, argv: parseArgv(raw), index: at + t.index, length: t[0].length, misplaced: true, other: false };\n }\n return null;\n}\n\nfunction dropLine(text: string, index: number, length: number): string {\n return text.slice(0, index) + text.slice(index + length).replace(/^\\r?\\n/, \"\");\n}\n\n/** Put `line` at the top level: before the first table, or at the end of a file that has\n * none. The line takes the file's own line end, so a CRLF file stays one. */\nexport function insertTopLevel(text: string, line: string): string {\n const { head, tail } = split(text);\n // The line joins the top-level section, so its line end is that section's own; a head\n // with no line end of its own takes the file's.\n const body = head.replace(/\\s*$/, \"\");\n const eol = (body.includes(\"\\n\") ? body : text).includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const top = head.trim() ? head.replace(/\\s*$/, eol) : \"\";\n if (!tail) return top + line + eol;\n return `${top + line}${eol}${eol}${tail}`;\n}\n\n/** The config with `line` as its notify, for a config whose notify is ours or absent. */\nexport function withNotify(text: string, line: string): string {\n const n = readNotify(text);\n if (n && !n.misplaced && !n.other) return text.slice(0, n.index) + line + text.slice(n.index + n.length);\n if (n?.misplaced) return insertTopLevel(dropLine(text, n.index, n.length), line);\n return insertTopLevel(text, line);\n}\n\n/** The config with its notify line `n` (as readNotify found it in `text`) replaced by `line`. */\nexport function replaceNotify(text: string, n: Notify, line: string): string {\n return text.slice(0, n.index) + line + text.slice(n.index + n.length);\n}\n\n/** The config with our top-level or misplaced notify removed. */\nexport function withoutNotify(text: string): string {\n const n = readNotify(text);\n if (!n || n.other || !n.length) return text;\n return dropLine(text, n.index, n.length);\n}\n\n// Codex Computer Use takes the notify slot itself and keeps the command it found in\n// its own argv: [client, \"turn-ended\", \"--previous-notify\", \"<that command as JSON>\"].\nconst PREVIOUS = \"--previous-notify\";\nexport function previousNotify(argv: string[] | null): string[] | null {\n if (!Array.isArray(argv)) return null;\n const i = argv.indexOf(PREVIOUS);\n return i >= 0 && typeof argv[i + 1] === \"string\" ? parseArgv(argv[i + 1] as string) : null;\n}\nexport function withPrevious(argv: string[], prev: string[] | null): string[] {\n const out = argv.slice();\n const i = out.indexOf(PREVIOUS);\n if (i >= 0) out.splice(i, 2);\n if (prev) out.push(PREVIOUS, JSON.stringify(prev));\n return out;\n}\n/** The raw notify value with its --previous-notify pair removed, formatting kept. */\nexport function stripPreviousRaw(raw: string): string {\n return raw.replace(/,\\s*\"--previous-notify\",\\s*\"(?:[^\"\\\\]|\\\\.)*\"\\s*(?=\\])/, \"\");\n}\n/** The raw notify value with `prev` as its --previous-notify, formatting of the rest kept. */\nexport function withPreviousRaw(raw: string, prev: string[]): string {\n const base = stripPreviousRaw(raw).replace(/\\s*\\]\\s*$/, \"\");\n return `${base}, \"--previous-notify\", ${JSON.stringify(JSON.stringify(prev))}]`;\n}\nexport const isComputerUse = (argv: string[] | null): boolean =>\n Array.isArray(argv) && typeof argv[0] === \"string\" && /(^|\\/)SkyComputerUseClient$/.test(argv[0]);\n", "// SPDX-License-Identifier: Apache-2.0\n// Devin CLI (Cognition) \u2014 docs: https://docs.devin.ai/cli/extensibility/hooks/overview\n// Config: the `hooks` key of the user config file \u2014 `~/.config/devin/config.json`\n// (`$XDG_CONFIG_HOME/devin/config.json` when that is set), `%APPDATA%\\devin\\config.json`\n// on Windows. Claude-shaped, matcher a REGEX on the tool name, timeouts in SECONDS.\n// That file is the only user-level location Devin reads hooks from; `hooks.v1.json` is\n// project-level only.\n//\n// Devin accepts exactly eight event names and rejects the WHOLE file on one it does not\n// know (\"unknown variant `Notification`, expected one of \u2026\"), which silently disables\n// every hook in it. DEVIN_EVENTS is that set, and nothing else may be added to it.\n//\n// The payload carries no `cwd`, no `transcript_path` and no `model`: the folder comes\n// from DEVIN_PROJECT_DIR in the hook's environment, and the transcript, the model, the\n// title and the times come from Devin's own session store \u2014 one SQLite file under the\n// data directory, read only, through readers/sqlite.ts. A session's transcript is\n// addressed as \"<database>#<session id>\", because readTranscript is given a file.\n//\n// stdout stays empty for every event: Devin reads a decision from stdout, and silence\n// plus exit 0 is \"carry on\" (printing nothing, printing `{}`, exiting 1 and blowing the\n// timeout were all measured as non-blocking; only exit 2 or an explicit\n// {\"decision\":\"block\"} blocks a tool).\nimport { createHash } from \"node:crypto\";\nimport { existsSync, readlinkSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { homeDir } from \"../paths.js\";\nimport { querySqlite } from \"../readers/sqlite.js\";\nimport { pairTurns } from \"../readers/transcripts.js\";\nimport { installClaudeShaped, installedClaudeShaped, readJson, uninstallClaudeShaped } from \"./claude-shaped.js\";\nimport type {\n Adapter,\n BackfillRow,\n HookInput,\n HookResult,\n InstallOptions,\n SessionFacts,\n TranscriptRead,\n} from \"./types.js\";\n\nconst NAME = \"devin\";\n/** The eight event names Devin accepts. One it does not know makes it drop the whole\n * hooks file, so this list is exact, not a superset \u2014 see devin.test.ts. */\nexport const DEVIN_EVENTS = [\n \"SessionStart\",\n \"UserPromptSubmit\",\n \"PreToolUse\",\n \"PostToolUse\",\n \"PermissionRequest\",\n \"Stop\",\n \"PostCompaction\",\n \"SessionEnd\",\n] as const;\nconst OPTS = { matcherFor: (e: string) => (/Tool|Permission/.test(e) ? \"\" : undefined), timeout: 5 };\nconst eventsFor = (lean?: boolean) => (lean ? DEVIN_EVENTS.filter((e) => e !== \"PreToolUse\") : [...DEVIN_EVENTS]);\nconst INSTALL_NOTE =\n \"Devin drops a whole hooks file on one event name it doesn't know; `/hooks` in `devin` lists what loaded\";\nconst CLAUDE_NOTE =\n \"Devin also reads ~/.claude/settings.json hooks (read_config_from.claude); a Claude-only event name there makes it ignore that file whole\";\n\nconst deadFileNote = (bad: string) =>\n `Devin loads NO hooks from this file: \"${bad}\" is not one of its event names and it rejects the file whole \u2014 remove that key (\\`/hooks\\` in \\`devin\\` lists what loaded)`;\n\n/** The first `hooks` key in the file that Devin does not accept, if there is one.\n * One such key disables every hook in the file, ours and any other tool's. */\nexport function foreignEvent(file: string): string | undefined {\n const hooks = readJson(file).hooks;\n if (!hooks || typeof hooks !== \"object\" || Array.isArray(hooks)) return undefined;\n const ok = DEVIN_EVENTS as readonly string[];\n return Object.keys(hooks).find((k) => !ok.includes(k));\n}\n\nconst homeOf = (env: NodeJS.ProcessEnv) => env.HOME || homeDir();\nconst appData = (env: NodeJS.ProcessEnv) => env.APPDATA || path.join(homeOf(env), \"AppData\", \"Roaming\");\n/** `~/.config/devin`, `$XDG_CONFIG_HOME/devin`, or `%APPDATA%\\devin`. */\nexport const devinConfigDir = (env: NodeJS.ProcessEnv = process.env): string =>\n process.platform === \"win32\"\n ? path.join(appData(env), \"devin\")\n : path.join(env.XDG_CONFIG_HOME || path.join(homeOf(env), \".config\"), \"devin\");\nconst configFile = (env: NodeJS.ProcessEnv) => path.join(devinConfigDir(env), \"config.json\");\n/** `~/.local/share/devin/cli`, `$XDG_DATA_HOME/devin/cli`, or `%APPDATA%\\devin\\cli`. */\nexport const devinDataDir = (env: NodeJS.ProcessEnv = process.env): string =>\n process.platform === \"win32\"\n ? path.join(appData(env), \"devin\", \"cli\")\n : path.join(env.XDG_DATA_HOME || path.join(homeOf(env), \".local\", \"share\"), \"devin\", \"cli\");\nconst sessionsDb = (env: NodeJS.ProcessEnv) => path.join(devinDataDir(env), \"sessions.db\");\n\nconst SESSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;\nconst validTool = (n: unknown): n is string => typeof n === \"string\" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);\nconst endReason = (r: unknown): \"clear\" | \"logout\" | \"exit\" | \"other\" =>\n r === \"clear\" || r === \"logout\" || r === \"exit\" ? r : \"other\";\nconst pickSource = (v: unknown): string =>\n typeof v === \"string\" && [\"startup\", \"resume\", \"clear\", \"compact\", \"fork\"].includes(v) ? v : \"unknown\";\nfunction errText(v: unknown): string | undefined {\n if (typeof v === \"string\" && v) return v.slice(0, 2000);\n if (v && typeof v === \"object\") {\n const e = v as { message?: unknown; error?: unknown };\n if (typeof e.message === \"string\" && e.message) return e.message.slice(0, 2000);\n if (typeof e.error === \"string\" && e.error) return e.error.slice(0, 2000);\n }\n return undefined;\n}\nconst cut = (s: string, n: number): string => (s.length > n ? `${s.slice(0, n).replace(/\\s+\\S*$/, \"\")}\u2026` : s);\n\nexport const devin: Adapter = {\n name: NAME,\n events: DEVIN_EVENTS,\n detect: (env = process.env) => existsSync(devinConfigDir(env)) || existsSync(devinDataDir(env)),\n configFiles: (env = process.env) => [configFile(env)],\n install: (cmd, env = process.env, opts: InstallOptions = {}) => {\n const file = configFile(env);\n const r = installClaudeShaped(file, NAME, eventsFor(opts.lean), cmd, OPTS);\n if (r.skipped) return [r];\n const bad = foreignEvent(file);\n return [{ ...r, note: bad ? deadFileNote(bad) : INSTALL_NOTE }];\n },\n uninstall: (env = process.env, opts: InstallOptions = {}) => [\n uninstallClaudeShaped(configFile(env), DEVIN_EVENTS, { created: opts.created?.includes(configFile(env)) }),\n ],\n installed: (cmd, env = process.env, opts: InstallOptions = {}) => {\n const file = configFile(env);\n const state = installedClaudeShaped(file, NAME, eventsFor(opts.lean), cmd, OPTS);\n // Our entries can be present and current and still report nothing: one event name\n // Devin does not know makes it drop the whole file, ours with it. `status` has to\n // say so, or it tells the person reporting is on while it is dead.\n const bad = state === \"missing\" ? undefined : foreignEvent(file);\n return [{ file, state: bad ? \"inactive\" : state, note: bad ? deadFileNote(bad) : CLAUDE_NOTE }];\n },\n\n fromHook(input: HookInput): HookResult | null {\n const event = input.argv[1] ?? \"\";\n let s: Record<string, unknown> = {};\n try {\n // A stdin that is JSON but not an object (`null`, a number) is no payload.\n const j: unknown = input.stdin ? JSON.parse(input.stdin) : {};\n if (j && typeof j === \"object\" && !Array.isArray(j)) s = j as Record<string, unknown>;\n } catch {}\n const id = typeof s.session_id === \"string\" ? s.session_id : \"\";\n if (!id) return null;\n // Nothing in the payload names the folder; DEVIN_PROJECT_DIR is the only thing that does.\n const session: HookResult[\"session\"] = { id, cwd: input.env.DEVIN_PROJECT_DIR ?? input.cwd };\n const turn_id = typeof s.prompt_id === \"string\" ? s.prompt_id : undefined;\n const tool = validTool(s.tool_name) ? s.tool_name : undefined;\n const call_id = typeof s.tool_use_id === \"string\" ? s.tool_use_id : undefined;\n // Every recorded payload has an object here, but the field is free-form: a Devin\n // version or an MCP-style tool could send a bare string, and then there is no\n // `success` and no `output` to read \u2014 the whole value IS the output.\n const response =\n s.tool_response && typeof s.tool_response === \"object\" && !Array.isArray(s.tool_response)\n ? (s.tool_response as { success?: unknown; output?: unknown; error?: unknown })\n : undefined;\n // The same prompt must always name the same attention (CONTROL.md \u00A76). Devin's\n // PermissionRequest does carry tool_use_id; the hash is the fallback for one that\n // doesn't, and is the bytes ADAPTERS.md's common conventions prescribe.\n const permissionId = () =>\n `perm-${createHash(\"sha256\")\n .update(`${turn_id ?? \"\"}|${tool ?? \"\"}|${JSON.stringify(s.tool_input ?? null)}`)\n .digest(\"hex\")\n .slice(0, 16)}`;\n const ev = (type: string, data: Record<string, unknown>) => ({ type, data: prune(data), harnessEvent: event });\n const events: HookResult[\"events\"] = [];\n switch (event) {\n case \"SessionStart\":\n events.push(ev(\"session.started\", { source: pickSource(s.source) }));\n break;\n case \"UserPromptSubmit\":\n events.push(\n ev(\"turn.started\", {\n turn_id,\n prompt_chars: typeof s.prompt === \"string\" ? [...s.prompt].length : undefined,\n }),\n );\n break;\n case \"PreToolUse\":\n if (tool) events.push(ev(\"tool.started\", { tool, call_id, turn_id, input: s.tool_input }));\n break;\n case \"PostToolUse\":\n // Devin is the one harness that says so outright: tool_response.success.\n if (tool)\n events.push(\n ev(\"tool.ended\", {\n tool,\n call_id,\n turn_id,\n // `ok` is required on tool.ended (PROTOCOL.md \u00A7events), so a response\n // that carries no success flag at all reads as \"nothing said it failed\".\n ok: response?.success !== false,\n error: errText(response?.error),\n input: s.tool_input,\n output: response ? response.output : s.tool_response,\n }),\n );\n break;\n case \"PermissionRequest\":\n events.push(ev(\"attention.needed\", { attention_id: call_id ?? permissionId(), kind: \"permission\", tool }));\n break;\n case \"Stop\":\n events.push(ev(\"turn.ended\", { turn_id, reason: \"stop\" }));\n break;\n case \"PostCompaction\":\n // Devin has no pre-compaction event; the payload carries only the summary.\n events.push(ev(\"context.compacted\", { trigger: \"auto\" }));\n break;\n case \"SessionEnd\":\n events.push(ev(\"session.ended\", { reason: endReason(s.reason) }));\n break;\n default:\n return null;\n }\n return {\n session,\n events,\n hints: {},\n transcript: transcriptRef(sessionsDb(input.env as NodeJS.ProcessEnv), id),\n };\n },\n\n readTranscript(file, fromLine) {\n const ref = splitRef(file);\n if (!ref) return { turns: [], line: fromLine };\n return readDevinChain(ref.db, ref.session, fromLine);\n },\n\n facts(session, transcript, _hints, _state, env = process.env): SessionFacts {\n const out: SessionFacts = {};\n const version = devinVersion(env);\n if (version) out.harness_version = version;\n const account = devinAccount(env);\n if (account) out.account_id = account;\n const ref = splitRef(transcript) ?? { db: sessionsDb(env), session: session.id };\n if (!SESSION_ID.test(ref.session)) return out;\n const rows = querySqlite(ref.db, SESSION_SQL, [ref.session]);\n const row = rows?.[0];\n if (!row) return out;\n Object.assign(out, sessionFacts(row));\n return out;\n },\n\n backfill(sinceMs, env = process.env): BackfillRow[] {\n const rows = querySqlite(sessionsDb(env), BACKFILL_SQL, [\"text\", Math.floor(sinceMs / 1000)]);\n if (!rows) return [];\n const out: BackfillRow[] = [];\n for (const row of rows) {\n const id = String(row.id); // text, the query says\n if (!SESSION_ID.test(id)) continue;\n const { started_at, last_at, ...rest } = sessionFacts(row);\n if (!started_at || !last_at) continue;\n out.push({ session: { id, ...rest }, started_at, last_at });\n }\n return out;\n },\n};\n\nfunction prune<T extends Record<string, unknown>>(o: T): T {\n for (const k of Object.keys(o)) if (o[k] === undefined) delete o[k];\n return o;\n}\n\n// --- the session store ---------------------------------------------------------------\n\n/** `<database>#<session id>`: readTranscript is given a file, and Devin keeps every\n * session in one database, so the session has to ride in the string. */\nexport const transcriptRef = (db: string, session: string): string => `${db}#${session}`;\nexport function splitRef(ref: string | undefined): { db: string; session: string } | null {\n if (!ref) return null;\n const at = ref.lastIndexOf(\"#\");\n if (at <= 0) return null;\n const db = ref.slice(0, at);\n const session = ref.slice(at + 1);\n return db && SESSION_ID.test(session) ? { db, session } : null;\n}\n\n/** `message_nodes` is a forest: it keeps abandoned branches too (27 nodes for a\n * two-turn session). The transcript is the one chain that ends at\n * `sessions.main_chain_id`, walked up through `parent_node_id` \u2014 never every node. */\n/** The walk stops at the cursor (`m.node_id >= ?`): everything below it has been read\n * already, and without that bound every Stop pulled `chat_message` for every ancestor\n * \u2014 the root of a real chain carries a 17 kB system prompt and four more system\n * blocks, re-read on every event, and past 32 MB the child and CLI engines give up\n * and return nothing. The cap keeps the NEWEST rows (`DESC`), re-sorted below: an\n * ascending cap would have kept the oldest and delivered no new turn ever again.\n * `UNION`, not `UNION ALL`: SQLite queues a row only if it has not queued the same row\n * before, so a chain that loops back on itself (a node its own parent, two that name\n * each other) ends at the first node it reaches twice. Under `UNION ALL` it recursed\n * for ever, and on the in-process engine nothing can interrupt that: the worker spins,\n * holds the session's lock and delivers nothing. */\nconst CHAIN_LIMIT = 4000;\nexport const CHAIN_SQL = `WITH RECURSIVE chain(node_id, parent_node_id, chat_message, created_at) AS (\n SELECT node_id, parent_node_id, chat_message, created_at FROM message_nodes\n WHERE session_id = ? AND node_id = (SELECT main_chain_id FROM sessions WHERE id = ?)\n UNION\n SELECT m.node_id, m.parent_node_id, m.chat_message, m.created_at FROM message_nodes m\n JOIN chain c ON m.node_id = c.parent_node_id AND m.session_id = ? AND m.node_id >= ?\n)\nSELECT node_id, chat_message, created_at FROM chain ORDER BY node_id DESC LIMIT ${CHAIN_LIMIT}`;\n\n/** Times are unix SECONDS in this store. The first ask comes from `prompt_history`,\n * because `sessions.title` holds whatever the titler last produced \u2014 a backticked\n * command, or a raw `functions.Shell:0{\u2026}` mid-turn. */\nconst SESSION_FIELDS = `s.model, s.working_directory, s.created_at, s.last_activity_at,\n (SELECT p.content FROM prompt_history p WHERE p.session_id = s.id AND p.is_shell = 0\n ORDER BY p.timestamp ASC, p.id ASC LIMIT 1) AS first_ask`;\nconst SESSION_SQL = `SELECT ${SESSION_FIELDS} FROM sessions s WHERE s.id = ? LIMIT 1`;\n// typeof(id) = 'text' (the literal rides as a parameter: the CLI engine takes no quote\n// in the SQL): an id a session ref could carry. A blob id comes back from the sqlite3\n// CLI as a string shaped by its version, so the database says what it is.\nconst BACKFILL_SQL = `SELECT s.id, ${SESSION_FIELDS} FROM sessions s\n WHERE s.hidden = 0 AND typeof(s.id) = ? AND s.last_activity_at >= ?\n ORDER BY s.last_activity_at DESC LIMIT 500`;\n\nfunction sessionFacts(row: Record<string, unknown>): SessionFacts {\n const out: SessionFacts = {};\n if (typeof row.model === \"string\" && row.model) out.model = row.model;\n if (typeof row.working_directory === \"string\" && path.isAbsolute(row.working_directory))\n out.cwd = row.working_directory;\n const started = Number(row.created_at);\n const last = Number(row.last_activity_at);\n if (Number.isFinite(started) && started > 0) out.started_at = Math.round(started * 1000);\n if (Number.isFinite(last) && last > 0) out.last_at = Math.round(last * 1000);\n if (typeof row.first_ask === \"string\") {\n const line = row.first_ask.split(\"\\n\").find((l) => l.trim()) ?? \"\";\n if (line.trim()) {\n out.title = cut(line.trim(), 80);\n out.title_source = \"first-ask\";\n }\n }\n return out;\n}\n\ninterface ChatMessage {\n role?: unknown;\n content?: unknown;\n metadata?: { is_user_input?: unknown; created_at?: unknown } | null;\n}\n\n/** The canonical chain as turns. `node_id` is the cursor: it rises along the chain and\n * is stable across a resume, so a later read starts where the last one stopped.\n * `role: \"system\"` is Cognition's own prompt (and the skills manifest, and the\n * generated system_info block), never the person's words \u2014 it is skipped at every\n * tier, as is `role: \"tool\"`. */\nexport function readDevinChain(db: string, session: string, fromLine: number): TranscriptRead {\n if (!SESSION_ID.test(session)) return { turns: [], line: fromLine };\n const floor = Number.isFinite(fromLine) && fromLine > 0 ? Math.floor(fromLine) : 0;\n const rows = querySqlite(db, CHAIN_SQL, [session, session, session, floor]);\n if (!rows?.length) return { turns: [], line: fromLine };\n const msgs: { role: \"user\" | \"assistant\"; text: string; at: number; line: number }[] = [];\n let end = fromLine;\n for (const row of rows) {\n const node = Number(row.node_id);\n if (!Number.isFinite(node)) continue;\n if (node + 1 > end) end = node + 1;\n if (node < fromLine) continue;\n let j: ChatMessage;\n try {\n j = JSON.parse(String(row.chat_message ?? \"\")) as ChatMessage;\n } catch {\n continue;\n }\n const text = typeof j.content === \"string\" ? j.content.trim() : \"\";\n if (!text) continue;\n // The message's own time, not the row's. `message_nodes.created_at` is when the row\n // batch was last WRITTEN: Devin rewrites it on every save of the chain, so every\n // node of a chain usually carries one identical value, and it is not even monotonic\n // along the chain. Measured on a live store 2026-10-08: a two-turn session had all\n // four of its chain nodes at 23:33:10 while their own `metadata.created_at` read\n // 23:32:29, :34, 23:33:08 and :10 (turn 1 reported 41 s late, both turns at the same\n // instant); one node's row time was rewritten 12 s later between two reads; and a\n // descendant's row time was 27 s EARLIER than its ancestor's. The column is only the\n // fallback for a row whose message has no time of its own.\n const secs = Number(row.created_at);\n const at =\n Date.parse(String(j.metadata?.created_at ?? \"\")) ||\n (Number.isFinite(secs) && secs > 0 ? Math.round(secs * 1000) : 0) ||\n Date.now();\n if (j.role === \"user\" && j.metadata?.is_user_input === true) msgs.push({ role: \"user\", text, at, line: node });\n else if (j.role === \"assistant\") msgs.push({ role: \"assistant\", text, at, line: node });\n }\n // The rows come newest first (the cap keeps the newest); a transcript runs the other way.\n msgs.sort((a, b) => a.line - b.line);\n return pairTurns(msgs, end);\n}\n\n/** `<data dir>/_versions/current` is a symlink to the running version's folder. */\nexport function devinVersion(env: NodeJS.ProcessEnv = process.env): string | undefined {\n try {\n const target = readlinkSync(path.join(devinDataDir(env), \"_versions\", \"current\"));\n const name = path.basename(target);\n return /^[0-9][0-9A-Za-z.+-]{0,63}$/.test(name) ? name : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** The organisation the CLI is signed in to, from the user config \u2014 the only account id\n * Devin keeps in a plain local file. Never the email, and never credentials.toml. */\nexport function devinAccount(env: NodeJS.ProcessEnv = process.env): string | undefined {\n const d = readJson(configFile(env)).devin as { org_id?: unknown } | undefined;\n const id = d && typeof d === \"object\" ? d.org_id : undefined;\n return typeof id === \"string\" && /^[A-Za-z0-9_.:-]{1,128}$/.test(id) ? id : undefined;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Read-only SQLite, for a harness that keeps its sessions in a database instead of a\n// JSONL file. Three ways in, tried in order, because `node:sqlite` does not exist on\n// Node 20 (the floor this package builds for) and is behind --experimental-sqlite on\n// 22.5\u201322.12:\n// \"node\" in this process, through createRequire \u2014 no static import, so the bundle\n// still loads on a Node without the module;\n// \"child\" one `node --experimental-sqlite -e \u2026` run through execFileSync: no shell,\n// arguments passed as argv, a short timeout;\n// \"cli\" the `sqlite3` binary, when it is on PATH and every parameter is a plain\n// scalar that can be written as a literal.\n// Every path opens the file read-only, is bounded in time, and returns null instead of\n// throwing: a database that is busy, missing, locked or unreadable costs the caller\n// nothing but an empty read.\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\n\nexport type SqliteParam = string | number;\nexport type SqliteRow = Record<string, unknown>;\nexport type SqliteEngine = \"auto\" | \"node\" | \"child\" | \"cli\";\n\nexport interface SqliteOptions {\n /** Busy timeout and the child's wall clock, in ms (default 2000). */\n timeoutMs?: number;\n /** Force one way in; \"auto\" tries node, then child, then cli (default). */\n engine?: SqliteEngine;\n /** Largest reply a child or the CLI may print (default 32 MB). */\n maxBuffer?: number;\n}\n\ninterface Statement {\n all(...params: SqliteParam[]): unknown[];\n}\ninterface Db {\n prepare(sql: string): Statement;\n exec(sql: string): void;\n close(): void;\n}\ntype DbCtor = new (path: string, options?: Record<string, unknown>) => Db;\n\n/** `node:sqlite` is still experimental, and Node says so on stderr the first time it is\n * used. A reader has no business writing to a harness's terminal, so that one warning\n * is swallowed while the module is loaded and used \u2014 nothing else is, and the original\n * emitter is back before this returns. (The child path passes --no-warnings instead.)\n * Exported for its test: Node emits that warning once per process. */\nexport function quiet<T>(fn: () => T): T {\n const original = process.emitWarning;\n try {\n process.emitWarning = ((warning: unknown, ...rest: unknown[]) => {\n const kind = typeof rest[0] === \"string\" ? rest[0] : (rest[0] as { type?: string } | undefined)?.type;\n if (kind === \"ExperimentalWarning\" && /sqlite/i.test(String(warning))) return;\n (original as (...a: unknown[]) => void).call(process, warning, ...rest);\n }) as typeof process.emitWarning;\n return fn();\n } finally {\n process.emitWarning = original;\n }\n}\n\nlet ctorCache: DbCtor | null | undefined;\n/** `node:sqlite` if this Node has it; null if it does not (Node 20, or 22.x unflagged). */\nfunction dbCtor(): DbCtor | null {\n if (ctorCache !== undefined) return ctorCache;\n ctorCache = null;\n try {\n const req = createRequire(import.meta.url);\n const m = quiet(() => req(\"node:sqlite\")) as { DatabaseSync?: unknown };\n if (typeof m?.DatabaseSync === \"function\") ctorCache = m.DatabaseSync as DbCtor;\n } catch {}\n return ctorCache;\n}\n\n/** SQLite gives back strings, numbers, null, BigInt and Uint8Array; the protocol takes JSON. */\nfunction plain(row: unknown): SqliteRow | null {\n if (!row || typeof row !== \"object\") return null;\n const out: SqliteRow = {};\n for (const [k, v] of Object.entries(row as SqliteRow)) {\n if (typeof v === \"bigint\") out[k] = Number(v);\n else if (v instanceof Uint8Array) out[k] = null;\n else out[k] = v;\n }\n return out;\n}\nconst rows = (v: unknown): SqliteRow[] | null => {\n if (!Array.isArray(v)) return null;\n const out: SqliteRow[] = [];\n for (const r of v) {\n const p = plain(r);\n if (p) out.push(p);\n }\n return out;\n};\n\nfunction viaNode(file: string, sql: string, params: SqliteParam[], o: Required<SqliteOptions>): SqliteRow[] | null {\n const C = dbCtor();\n if (!C) return null;\n let db: Db;\n try {\n db = quiet(() => new C(file, { readOnly: true, timeout: o.timeoutMs }));\n } catch {\n return null;\n }\n try {\n try {\n db.exec(`PRAGMA busy_timeout = ${Math.round(o.timeoutMs)}`);\n } catch {}\n return quiet(() => rows(db.prepare(sql).all(...params)));\n } catch {\n return null;\n } finally {\n try {\n db.close();\n } catch {}\n }\n}\n\n/** Runs in a child Node, with the file, the SQL, the parameters and the timeout as argv. */\nconst CHILD = `const { DatabaseSync } = require(\"node:sqlite\");\nconst [, file, sql, params, ms] = process.argv;\nconst db = new DatabaseSync(file, { readOnly: true, timeout: Number(ms) });\ntry { db.exec(\"PRAGMA busy_timeout = \" + Number(ms)); } catch {}\nconst out = db.prepare(sql).all(...JSON.parse(params));\ndb.close();\nprocess.stdout.write(JSON.stringify(out));`;\n\nfunction viaChild(file: string, sql: string, params: SqliteParam[], o: Required<SqliteOptions>): SqliteRow[] | null {\n try {\n const out = execFileSync(\n process.execPath,\n [\"--experimental-sqlite\", \"--no-warnings\", \"-e\", CHILD, file, sql, JSON.stringify(params), String(o.timeoutMs)],\n {\n encoding: \"utf8\",\n timeout: o.timeoutMs + 3000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n maxBuffer: o.maxBuffer,\n windowsHide: true,\n },\n );\n return rows(JSON.parse(out || \"null\"));\n } catch {\n return null;\n }\n}\n\n/** A parameter safe to write into SQL as a literal: no quote, no backslash, no newline. */\nconst SAFE = /^[A-Za-z0-9 ._:@/+-]{0,256}$/;\nexport function inlineParams(sql: string, params: SqliteParam[]): string | null {\n if (sql.includes(\"'\")) return null; // a literal in the SQL would make the `?` count a guess\n const holes = sql.split(\"?\").length - 1;\n if (holes !== params.length) return null;\n let i = 0;\n let bad = false;\n const out = sql.replace(/\\?/g, () => {\n const p = params[i++];\n if (typeof p === \"number\") {\n if (!Number.isFinite(p)) {\n bad = true;\n return \"NULL\";\n }\n return String(p);\n }\n if (typeof p !== \"string\" || !SAFE.test(p)) {\n bad = true;\n return \"NULL\";\n }\n return `'${p}'`;\n });\n return bad ? null : out;\n}\n\nfunction viaCli(file: string, sql: string, params: SqliteParam[], o: Required<SqliteOptions>): SqliteRow[] | null {\n const text = inlineParams(sql, params);\n if (!text) return null;\n try {\n const out = execFileSync(\"sqlite3\", [\"-readonly\", \"-json\", file, text], {\n encoding: \"utf8\",\n timeout: o.timeoutMs + 3000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n maxBuffer: o.maxBuffer,\n windowsHide: true,\n }).trim();\n if (!out) return [];\n return rows(JSON.parse(out));\n } catch {\n return null;\n }\n}\n\n/** One read-only query. Null means \"could not read it\", never an exception. */\nexport function querySqlite(\n file: string,\n sql: string,\n params: SqliteParam[] = [],\n opts: SqliteOptions = {},\n): SqliteRow[] | null {\n const o: Required<SqliteOptions> = {\n timeoutMs: opts.timeoutMs ?? 2000,\n engine: opts.engine ?? \"auto\",\n maxBuffer: opts.maxBuffer ?? 32 * 1024 * 1024,\n };\n if (!file) return null;\n try {\n if (!existsSync(file)) return null;\n } catch {\n return null;\n }\n const ways =\n o.engine === \"auto\"\n ? ([viaNode, viaChild, viaCli] as const)\n : o.engine === \"node\"\n ? ([viaNode] as const)\n : o.engine === \"child\"\n ? ([viaChild] as const)\n : ([viaCli] as const);\n for (const way of ways) {\n const r = way(file, sql, params, o);\n if (r) return r;\n }\n return null;\n}\n\n/** Is `node:sqlite` in this process? (Tests skip what they cannot set up.) */\nexport const sqliteInProcess = (): boolean => dbCtor() !== null;\n", "// SPDX-License-Identifier: Apache-2.0\n// Gemini CLI (Google) \u2014 docs: https://geminicli.com/docs/hooks/reference\n// Config: ~/.gemini/settings.json `hooks` key, same array-of-groups shape as Claude\n// Code but timeouts in MILLISECONDS and a `name` per handler. stdout must be JSON or\n// empty. Its own telemetry defaults logPrompts on; the installer says so once.\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { homeDir } from \"../paths.js\";\nimport { ends, parseLines } from \"../readers/files.js\";\nimport { readGemini } from \"../readers/transcripts.js\";\nimport { installClaudeShaped, installedClaudeShaped, readJson, uninstallClaudeShaped } from \"./claude-shaped.js\";\nimport { findBinary, probe } from \"./headless.js\";\nimport type { Adapter, HookInput, HookResult, InstallOptions, SessionFacts } from \"./types.js\";\n\nconst NAME = \"gemini-cli\";\nexport const GEMINI_EVENTS = [\n \"SessionStart\",\n \"BeforeAgent\",\n \"BeforeTool\",\n \"AfterTool\",\n \"Notification\",\n \"AfterAgent\",\n \"PreCompress\",\n \"SessionEnd\",\n] as const;\nconst OPTS = { matcherFor: (e: string) => (/Tool/.test(e) ? \".*\" : undefined), timeout: 5000 };\nconst geminiDir = (env: NodeJS.ProcessEnv) => env.GEMINI_CLI_HOME || path.join(homeDir(), \".gemini\");\nconst settings = (env: NodeJS.ProcessEnv) => path.join(geminiDir(env), \"settings.json\");\nconst validTool = (n: unknown): n is string => typeof n === \"string\" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);\nconst eventsFor = (lean?: boolean) => (lean ? GEMINI_EVENTS.filter((e) => e !== \"BeforeTool\") : [...GEMINI_EVENTS]);\n\n/** `gemini --version`. On Windows the CLI is npm's gemini.cmd shim, which node refuses to\n * spawn bare, so there it is looked up on PATH and run as `probe` says (the shim's own\n * script under this node, else through a shell). */\nexport function geminiVersion(\n platform: NodeJS.Platform = process.platform,\n env: NodeJS.ProcessEnv = process.env,\n): string {\n const win = platform === \"win32\";\n const bin = win\n ? findBinary({ names: [\"gemini.exe\", \"gemini.cmd\"], dirs: [path.dirname(process.execPath)], env })\n : \"gemini\";\n if (!bin) throw new Error(\"no gemini on PATH\");\n const c = probe(bin, [\"--version\"]);\n return execFileSync(c.file, c.args, {\n encoding: \"utf8\",\n timeout: 4000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n env,\n shell: c.shell,\n }).trim();\n}\n\n/** Before 0.46 the hooks system is off unless tools.enableHooks is true (issue #11). */\nexport function geminiHooksActive(settingsFile: string): { active: boolean; note?: string } {\n const tools = readJson(settingsFile).tools as { enableHooks?: unknown } | undefined;\n if (tools?.enableHooks === true) return { active: true };\n if (tools?.enableHooks === false)\n return { active: false, note: \"inactive: tools.enableHooks is false in settings.json\" };\n let version = \"\";\n try {\n version = geminiVersion();\n } catch {\n return {\n active: true,\n note: \"could not run `gemini --version`; hooks need tools.enableHooks on versions before 0.46\",\n };\n }\n const m = /(\\d+)\\.(\\d+)/.exec(version);\n if (m && (Number(m[1]) > 0 || Number(m[2]) >= 46)) return { active: true };\n return {\n active: false,\n note: `inactive: Gemini CLI ${version || \"?\"} runs hooks only with \"tools\": {\"enableHooks\": true} in settings.json`,\n };\n}\n\nexport const geminiCli: Adapter = {\n name: NAME,\n events: GEMINI_EVENTS,\n // ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.\n detect: (env = process.env) => existsSync(settings(env)) || existsSync(path.join(geminiDir(env), \"tmp\")),\n configFiles: (env = process.env) => [settings(env)],\n install: (cmd, env = process.env, opts: InstallOptions = {}) => {\n const r = installClaudeShaped(settings(env), NAME, eventsFor(opts.lean), cmd, OPTS);\n const a = geminiHooksActive(settings(env));\n return [a.note && !r.skipped ? { ...r, note: [r.note, a.note].filter(Boolean).join(\"; \") } : r];\n },\n uninstall: (env = process.env, opts: InstallOptions = {}) => [\n uninstallClaudeShaped(settings(env), GEMINI_EVENTS, { created: opts.created?.includes(settings(env)) }),\n ],\n installed: (cmd, env = process.env, opts: InstallOptions = {}) => {\n const state = installedClaudeShaped(settings(env), NAME, eventsFor(opts.lean), cmd, OPTS);\n const a = geminiHooksActive(settings(env));\n return [\n {\n file: settings(env),\n state: state === \"current\" && !a.active ? \"inactive\" : state,\n ...(a.note ? { note: a.note } : {}),\n },\n ];\n },\n\n fromHook(input: HookInput): HookResult | null {\n const event = input.argv[1] ?? \"\";\n let s: Record<string, unknown> = {};\n try {\n // A stdin that is JSON but not an object (`null`, a number) is no payload.\n const j: unknown = input.stdin ? JSON.parse(input.stdin) : {};\n if (j && typeof j === \"object\" && !Array.isArray(j)) s = j as Record<string, unknown>;\n } catch {}\n const id = s.session_id as string | undefined;\n if (!id) return null;\n const session: HookResult[\"session\"] = { id: String(id), cwd: typeof s.cwd === \"string\" ? s.cwd : input.cwd };\n const tool = validTool(s.tool_name) ? s.tool_name : undefined;\n const ev = (type: string, data: Record<string, unknown>) => ({ type, data: prune(data), harnessEvent: event });\n const events: HookResult[\"events\"] = [];\n switch (event) {\n case \"SessionStart\":\n events.push(\n ev(\"session.started\", {\n source:\n typeof s.source === \"string\" && [\"startup\", \"resume\", \"clear\"].includes(s.source) ? s.source : \"unknown\",\n }),\n );\n break;\n case \"BeforeAgent\":\n events.push(\n ev(\"turn.started\", { prompt_chars: typeof s.prompt === \"string\" ? [...s.prompt].length : undefined }),\n );\n break;\n case \"BeforeTool\":\n if (tool) events.push(ev(\"tool.started\", { tool, input: s.tool_input }));\n break;\n case \"AfterTool\": {\n const resp = s.tool_response as { error?: unknown; llmContent?: unknown } | undefined;\n const err =\n resp && typeof resp === \"object\" && resp.error\n ? String(typeof resp.error === \"string\" ? resp.error : JSON.stringify(resp.error)).slice(0, 2000)\n : undefined;\n if (tool)\n events.push(\n ev(\"tool.ended\", {\n tool,\n ok: !err,\n error: err,\n input: s.tool_input,\n output: resp?.llmContent ?? s.tool_response,\n }),\n );\n break;\n }\n case \"Notification\":\n events.push(\n ev(\"attention.needed\", {\n attention_id: `notif-${Date.now()}`,\n kind: s.notification_type === \"ToolPermission\" ? \"permission\" : \"question\",\n message: typeof s.message === \"string\" ? s.message.slice(0, 200) : undefined,\n }),\n );\n break;\n case \"AfterAgent\":\n events.push(ev(\"turn.ended\", { reason: \"stop\" }));\n break;\n case \"PreCompress\":\n events.push(ev(\"context.compacted\", { trigger: s.trigger === \"manual\" ? \"manual\" : \"auto\" }));\n break;\n case \"SessionEnd\":\n events.push(\n ev(\"session.ended\", {\n reason:\n s.reason === \"clear\" || s.reason === \"logout\" || s.reason === \"exit\"\n ? s.reason\n : s.reason === \"prompt_input_exit\"\n ? \"exit\"\n : \"other\",\n }),\n );\n break;\n default:\n return null;\n }\n const r: HookResult = { session, events, hints: {} };\n if (typeof s.transcript_path === \"string\") r.transcript = s.transcript_path;\n return r;\n },\n\n readTranscript(file, fromLine) {\n let text = \"\";\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return { turns: [], line: fromLine };\n }\n return readGemini(text.split(\"\\n\"), fromLine);\n },\n\n facts(_session, transcript): SessionFacts {\n const out: SessionFacts = {};\n if (!transcript) return out;\n const { head, tail } = ends(transcript, 64 * 1024, 64 * 1024);\n const H = parseLines(head);\n const T = parseLines(tail);\n const first = H.find((j) => j.type === \"user\");\n const raw = first ? (first.content ?? first.message ?? first.text) : undefined;\n const text = typeof raw === \"string\" ? raw.trim() : \"\";\n if (text) {\n const line = text.split(\"\\n\")[0] as string; // trimmed, so its first line is its first non-blank one\n out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\\s+\\S*$/, \"\")}\u2026` : line;\n out.title_source = \"first-ask\";\n }\n const times = H.concat(T)\n .map((j) => Date.parse(String(j.timestamp ?? \"\")))\n .filter((n) => n > 0);\n if (times.length) {\n out.started_at = Math.min(...times);\n out.last_at = Math.max(...times);\n }\n return out;\n },\n\n backfill: () => [], // chat logs are keyed by a project hash, not a session id we can name; the first hook of a resumed session reports it\n};\n\nfunction prune<T extends Record<string, unknown>>(o: T): T {\n for (const k of Object.keys(o)) if (o[k] === undefined) delete o[k];\n return o;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// The registry: the only place that lists vendors. A new harness is one file here\n// plus its fixtures and its row in spec/ADAPTERS.md.\nimport { acpHarnesses } from \"./acp-control.js\";\nimport { antigravity } from \"./antigravity.js\";\nimport { findAgy } from \"./antigravity-control.js\";\nimport { claudeCode } from \"./claude-code.js\";\nimport { findClaude } from \"./claude-code-control.js\";\nimport { codex } from \"./codex.js\";\nimport { findCodex } from \"./codex-control.js\";\nimport { devin } from \"./devin.js\";\nimport { geminiCli } from \"./gemini-cli.js\";\nimport type { Adapter } from \"./types.js\";\n\nexport const ADAPTERS: readonly Adapter[] = [claudeCode, codex, geminiCli, antigravity, devin];\nexport const adapterByName = (name: string): Adapter | undefined => ADAPTERS.find((a) => a.name === name);\nexport * as acpControl from \"./acp-control.js\";\nexport { AG_STALE_PRETOOL, antigravityAnswer } from \"./antigravity.js\";\nexport * as antigravityControl from \"./antigravity-control.js\";\nexport { claudeAccountDirs, claudeAccounts, claudeDirs, notClaudeCode } from \"./claude-code.js\";\nexport * as claudeControl from \"./claude-code-control.js\";\nexport * as codexControl from \"./codex-control.js\";\nexport { DEVIN_EVENTS, devinConfigDir, devinDataDir, readDevinChain } from \"./devin.js\";\nexport {\n deniedNote,\n JOB_TIMEOUT_MS,\n jobEnv,\n type Login,\n type Reading,\n type RunOptions,\n type RunResult,\n runHeadless,\n} from \"./headless.js\";\n\n/** How many harnesses a pairing request or a hello lists at most (control-pair.json,\n * control-hello.json). */\nexport const HELLO_HARNESSES_MAX = 20;\n\n/** The harnesses a control daemon here can drive headlessly (CONTROL.md \u00A76): each whose\n * CLI is installed on this machine, and every ACP agent that is. What `control pair`\n * and every hello advertise: the three with their own drivers first, then the ACP\n * agents in the registry's order, at most HELLO_HARNESSES_MAX of them. */\nexport function drivableHarnesses(env: NodeJS.ProcessEnv = process.env): string[] {\n return [\n ...(findClaude(env) ? [\"claude-code\"] : []),\n ...(findCodex(env) ? [\"codex\"] : []),\n ...(findAgy(env) ? [\"antigravity\"] : []),\n ...acpHarnesses(env),\n ].slice(0, HELLO_HARNESSES_MAX);\n}\nexport type {\n Adapter,\n AdapterEvent,\n BackfillRow,\n FactsState,\n HookCommand,\n HookInput,\n HookResult,\n InstallReport,\n SessionFacts,\n TranscriptRead,\n Turn,\n} from \"./types.js\";\nexport { antigravity, claudeCode, codex, devin, geminiCli };\n", "// SPDX-License-Identifier: Apache-2.0\n// ~/.config/sessionpipe/config.json, mode 0600: the machine's name, the sinks with\n// their tier and token, per-harness switches, the update check, remote titles.\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { configFile, tilde } from \"./paths.js\";\nimport type { Tier } from \"./types.js\";\n\nexport interface SinkConfig {\n name: string;\n /** https://\u2026, file:PATH, stdout, otlp:URL */\n url: string;\n tier: Tier;\n pii?: boolean;\n /** @deprecated Never set: control is per machine (spec/CONTROL.md \u00A72). Kept so an\n * older config still reads. */\n control?: boolean;\n token?: string;\n /** whsec_\u2026 for Standard Webhooks signatures. */\n secret?: string;\n /** The token (and secret) live in this store instead of in this file; the client\n * reads them from it when it sends. */\n token_in?: \"keychain\" | \"secret-service\";\n /** Learned from the receiver's well-known file (`sink add`, `connect`; read again by a\n * worker once a day): its tier cap, and the tool events it asked for. */\n max_tier?: Tier;\n receiver_tools?: \"all\" | \"main\" | \"beat\";\n well_known_at?: string;\n /** When a worker last tried to read it again, whether or not the read went through. */\n well_known_checked_at?: string;\n paused?: string;\n /** Which tool events this sink is sent (issue #208): `all`; `main`, a subagent's at\n * most one per session per minute; `beat`, every tool event at most one per session\n * per minute. Unset: what the receiver's well-known file asked for (`receiver_tools`),\n * else `all`, so no receiver's view changes unless it asked. The outbox keeps every\n * event either way. */\n tools?: \"all\" | \"main\" | \"beat\";\n}\n\nexport interface Config {\n machine?: string;\n sinks: SinkConfig[];\n /** `status_line`: Claude Code's status line is ours (install --status-line). */\n harnesses: Record<string, { enabled?: boolean; created?: string[]; status_line?: boolean }>;\n update_check?: boolean;\n keep_days?: number;\n /** Ask a harness's own service for a session's name (an adapter's `remoteFacts`):\n * claude.ai's, for Claude Code. Off unless the person turns it on (`sessionpipe\n * titles on`): it means reading the harness's sign-in token on this machine. */\n remote_titles?: boolean;\n}\n\nexport const DEFAULT_CONFIG: Config = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };\n\nexport function readConfig(file: string = configFile()): Config {\n try {\n const j = JSON.parse(readFileSync(file, \"utf8\")) as Partial<Config>;\n return {\n ...DEFAULT_CONFIG,\n ...j,\n sinks: Array.isArray(j.sinks) ? j.sinks : [],\n harnesses: j.harnesses && typeof j.harnesses === \"object\" ? j.harnesses : {},\n };\n } catch {\n return { ...DEFAULT_CONFIG, sinks: [], harnesses: {} };\n }\n}\n\n/** Why `file` cannot be read as a config, or undefined when it can: missing, blank, or a\n * JSON object. readConfig reads such a file as the defaults, so hooks go on. */\nfunction configProblem(file: string): string | undefined {\n let text: string;\n try {\n text = readFileSync(file, \"utf8\");\n } catch (e) {\n // Every error readFileSync throws carries a Node error code.\n const code = (e as NodeJS.ErrnoException).code;\n return code === \"ENOENT\" ? undefined : `could not be read (${code})`;\n }\n if (!text.trim()) return undefined;\n if (text.charCodeAt(0) === 0xfeff) return \"starts with a byte-order mark (save it as UTF-8 without one)\";\n try {\n const j: unknown = JSON.parse(text);\n return j && typeof j === \"object\" && !Array.isArray(j) ? undefined : \"is not a JSON object\";\n } catch (e) {\n // Where, never V8's excerpt of the text: that can be part of a sink's token.\n const m = (e as Error).message;\n const at = (/line \\d+ column \\d+/.exec(m) ?? /position \\d+/.exec(m))?.[0];\n return `is not valid JSON${at ? ` at ${at}` : \"\"}`;\n }\n}\n\n/** Throw if `file` holds something readConfig could not read: writing the defaults over a\n * hand edit's typo would lose the machine's name and every sink with its token. A\n * command with an effect before its save calls this first, so it stops before any. */\nexport function assertConfigWritable(file: string = configFile()): void {\n const problem = configProblem(file);\n if (problem)\n throw new Error(\n `${tilde(file)} ${problem}, so it was not written over: that would lose its machine name and sinks. Fix it, or move it aside to start again, then run this again.`,\n );\n}\n\nexport function writeConfig(c: Config, file: string = configFile()): void {\n assertConfigWritable(file);\n mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });\n writeFileSync(file, `${JSON.stringify(c, null, 2)}\\n`, { mode: 0o600 });\n if (process.platform !== \"win32\" && existsSync(file)) {\n try {\n chmodSync(file, 0o600);\n } catch {}\n }\n}\n\n/** The machine's name: what the person configured, else the hostname without a\n * local suffix. Never guessed from anything else (a hand-typed name once linked 64\n * versions to nothing). */\nexport function machineName(c: Config, hostname: string): string {\n if (c.machine) return String(c.machine).slice(0, 80);\n return hostname.replace(/\\.(local|lan|home)$/i, \"\");\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// ULID: 48-bit ms timestamp + 80 random bits, Crockford base32. Monotonic within a\n// process so two events in the same millisecond still sort in emit order.\nimport { randomBytes } from \"node:crypto\";\n\nconst ALPHABET = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\nlet lastTime = 0;\nlet lastRand: number[] = [];\n\nfunction encodeTime(t: number): string {\n let out = \"\";\n let n = t;\n for (let i = 0; i < 10; i++) {\n out = ALPHABET[n % 32] + out;\n n = Math.floor(n / 32);\n }\n return out;\n}\n\nexport function ulid(now: number = Date.now()): string {\n if (now === lastTime) {\n // increment the random part\n for (let i = lastRand.length - 1; i >= 0; i--) {\n const v = (lastRand[i] as number) + 1;\n if (v < 32) {\n lastRand[i] = v;\n break;\n }\n lastRand[i] = 0;\n }\n } else {\n lastTime = now;\n lastRand = Array.from(randomBytes(16), (b) => b % 32);\n }\n return encodeTime(now) + lastRand.map((v) => ALPHABET[v]).join(\"\");\n}\n\nexport const isUlid = (s: string): boolean => /^[0-9A-HJKMNP-TV-Z]{26}$/.test(s);\n", "// SPDX-License-Identifier: Apache-2.0\n// From an adapter's output to a protocol event: id, time, the full session block\n// as known now, and the raw (tier 3) data. Filtering per sink happens later.\nimport type { AdapterEvent } from \"./adapters/types.js\";\nimport type { Event, Session } from \"./types.js\";\nimport { ulid } from \"./ulid.js\";\n\nexport interface EventContext {\n harness: { name: string; version?: string };\n session: Session;\n now?: number;\n}\n\nexport function makeEvent(a: AdapterEvent, ctx: EventContext): Event {\n const now = ctx.now ?? Date.now();\n const harness: Event[\"harness\"] = { name: ctx.harness.name, event: a.harnessEvent };\n if (ctx.harness.version) harness.version = ctx.harness.version;\n return {\n protocol: 1,\n id: ulid(now),\n type: a.type,\n time: new Date(now).toISOString(),\n tier: 3,\n harness,\n session: ctx.session,\n privacy: { rulesets: [], pii: false },\n data: a.data,\n };\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// The outbox: append-only JSONL per (harness, session) under\n// <state>/outbox/<harness>/<session>.jsonl, a per-session seq counter, a per-sink\n// cursor that moves only when the sink acknowledged, and a per-session lock so two\n// workers never interleave. Never lose, never duplicate.\n\nimport { randomBytes } from \"node:crypto\";\nimport {\n appendFileSync,\n closeSync,\n existsSync,\n mkdirSync,\n openSync,\n readdirSync,\n readFileSync,\n readSync,\n renameSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport path from \"node:path\";\nimport { safeId } from \"./paths.js\";\nimport type { Event } from \"./types.js\";\n\nexport interface OutboxRef {\n harness: string;\n session: string;\n}\n\nconst READ_CHUNK = 1024 * 1024;\nconst READ_MAX = 8 * 1024 * 1024;\n\nexport class Outbox {\n constructor(readonly root: string) {}\n\n dir(harness: string): string {\n return path.join(this.root, \"outbox\", safeId(harness));\n }\n file(ref: OutboxRef): string {\n return path.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);\n }\n private seqFile(ref: OutboxRef): string {\n return path.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);\n }\n\n /** The next seq for a session, persisted (survives a hibernating machine and a\n * restart; an in-memory counter would restart at 0 and collide). */\n nextSeq(ref: OutboxRef): number {\n const f = this.seqFile(ref);\n let n = -1;\n try {\n const t = readFileSync(f, \"utf8\").trim();\n if (/^\\d+$/.test(t)) n = Number(t);\n } catch {}\n // A missing or unreadable counter (a crash between truncate and write leaves it\n // empty) with an existing outbox: recover from the file's last line.\n if (n < 0) n = this.lastSeqInFile(ref) + 1;\n mkdirSync(path.dirname(f), { recursive: true, mode: 0o700 });\n writeFileSync(f, String(n + 1), { mode: 0o600 });\n return n;\n }\n\n private lastSeqInFile(ref: OutboxRef): number {\n const f = this.file(ref);\n if (!existsSync(f)) return -1;\n const lines = readFileSync(f, \"utf8\").trim().split(\"\\n\");\n for (let i = lines.length - 1; i >= 0; i--) {\n try {\n const j = JSON.parse(lines[i] as string) as Event;\n if (typeof j.session?.seq === \"number\") return j.session.seq;\n } catch {}\n }\n return -1;\n }\n\n append(ref: OutboxRef, events: Event[]): void {\n if (!events.length) return;\n mkdirSync(this.dir(ref.harness), { recursive: true, mode: 0o700 });\n appendFileSync(this.file(ref), `${events.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`, { mode: 0o600 });\n }\n\n /** Every session file, newest first. */\n sessions(): OutboxRef[] {\n const out: { ref: OutboxRef; mtime: number }[] = [];\n const base = path.join(this.root, \"outbox\");\n if (!existsSync(base)) return [];\n for (const h of readdirSync(base)) {\n const d = path.join(base, h);\n let names: string[] = [];\n try {\n names = readdirSync(d).filter((n) => n.endsWith(\".jsonl\"));\n } catch {\n continue;\n }\n for (const n of names) {\n try {\n out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync(path.join(d, n)).mtimeMs });\n } catch {}\n }\n }\n return out.sort((a, b) => b.mtime - a.mtime).map((x) => x.ref);\n }\n\n /** Read events after byte offset `from`; returns them and the new offset. Lines\n * are read whole; a partial trailing line (a write in progress) is left. A read\n * takes READ_CHUNK bytes at a time, not the rest of the file: a session file grows\n * to tens of MB and a drain takes it in batches of 50. It reads on until the batch\n * is full or the file ends, so lines that parse to nothing never stop a drain; one\n * line longer than the chunk gets a larger one, up to READ_MAX. */\n read(ref: OutboxRef, from: number, maxEvents = 500): { events: Event[]; offset: number } {\n const f = this.file(ref);\n if (!existsSync(f)) return { events: [], offset: from };\n const size = statSync(f).size;\n if (from >= size) return { events: [], offset: from };\n const fd = openSync(f, \"r\");\n try {\n const events: Event[] = [];\n let offset = from;\n let chunk = READ_CHUNK;\n while (events.length < maxEvents && offset < size) {\n const buf = Buffer.alloc(Math.min(size - offset, chunk));\n const n = readSync(fd, buf, 0, buf.length, offset);\n const text = buf.subarray(0, n).toString(\"utf8\");\n let pos = 0;\n let used = 0;\n while (events.length < maxEvents) {\n const nl = text.indexOf(\"\\n\", pos);\n if (nl < 0) break;\n const line = text.slice(pos, nl);\n pos = nl + 1;\n used += Buffer.byteLength(line, \"utf8\") + 1;\n if (!line.trim()) continue;\n try {\n events.push(JSON.parse(line) as Event);\n } catch {}\n }\n offset += used;\n if (used) continue;\n // No whole line in this chunk: a line longer than it, or a write in progress.\n if (offset + n >= size || chunk >= READ_MAX) break;\n chunk *= 2;\n }\n return { events, offset };\n } finally {\n closeSync(fd);\n }\n }\n\n /** Files older than `days` are pruned, and each sink's cursor into them. The seq\n * counter stays: a session resumed after the window counts on from where it was,\n * so a receiver's (harness, session, seq) dedup doesn't take its new events for\n * ones it already has, and the sinks read the new file from its start.\n * It all happens under the drain's `flush` lock, since a drain holds the whole\n * cursor map and writes it back: a file and its cursor keys go together, so a\n * session resumed in between can't keep a key past its new file's end. While a\n * drain runs, nothing is pruned (0); the next prune does it. It never throws: it\n * is a cleanup, and a hook's drain comes after it. */\n prune(days: number, now = Date.now()): number {\n let release: (() => void) | null = null;\n try {\n release = takeNamedLock(this.root, \"flush\");\n } catch {}\n if (!release) return 0;\n let n = 0;\n try {\n const cutoff = now - days * 86_400_000;\n const aged = this.sessions().filter((ref) => {\n try {\n return statSync(this.file(ref)).mtimeMs < cutoff;\n } catch {\n return false;\n }\n });\n // The keys go first: a worker that dies or fails to write here leaves a file\n // with no cursor, which a sink sends again (a receiver's dedup takes it), never\n // a cursor past the end of a file a resume starts. Each file is checked again\n // right before it goes: one a hook appended to meanwhile stays.\n this.dropStaleCursors(new Set(aged.map((ref) => `${ref.harness}/${ref.session}`)));\n for (const ref of aged) {\n const f = this.file(ref);\n try {\n if (statSync(f).mtimeMs < cutoff) {\n unlinkSync(f);\n n++;\n }\n } catch {}\n }\n } catch {\n } finally {\n release();\n }\n return n;\n }\n\n /** A sink's cursor into a session file about to be pruned (`gone`), or one already\n * missing (an earlier prune, an older release, by hand) is removed. The caller\n * holds the `flush` lock. */\n private dropStaleCursors(gone: Set<string>): void {\n let names: string[] = [];\n try {\n names = readdirSync(path.join(this.root, \"cursors\")).filter((n) => n.endsWith(\".json\"));\n } catch {}\n const stale = (c: Record<string, number>): string[] =>\n Object.keys(c).filter((k) => {\n if (gone.has(k)) return true;\n const i = k.indexOf(\"/\");\n return !existsSync(this.file({ harness: k.slice(0, i), session: k.slice(i + 1) }));\n });\n for (const n of names) {\n const s = new Cursors(this.root, n.slice(0, -5));\n const c = s.read();\n const keys = stale(c);\n if (!keys.length) continue;\n for (const k of keys) delete c[k];\n s.write(c);\n }\n }\n\n delete(ref: OutboxRef): void {\n // The cursor keys go before the files: a death between leaves the old file with\n // no cursor, so the next drain sends it whole and the person, who sees no \"removed\"\n // line, runs forget again; the other way round leaves a cursor past the end of\n // the file a session that goes on starts, and nothing says so.\n this.dropCursors(ref);\n for (const f of [this.file(ref), this.seqFile(ref)]) {\n try {\n unlinkSync(f);\n } catch {}\n }\n }\n\n /** The key every sink's cursor holds for a session is removed with the session's\n * file: the key alone pointed into the old file, and a session that went on (still\n * running, or resumed later) had its new file's first events unsent until it grew\n * past that offset. Every file under <state>/cursors is taken (a sink since taken\n * out of the config still has one; one that doesn't parse reads as empty and is\n * left), by the key a drain writes (the file's own name, `safeId`) and by the ref\n * as given. The caller holds the drain's `flush` lock: a drain holds the whole\n * cursor map and writes it back, so a key dropped under it would come back. */\n private dropCursors(ref: OutboxRef): void {\n const keys = new Set([`${ref.harness}/${ref.session}`, `${safeId(ref.harness)}/${safeId(ref.session)}`]);\n let names: string[] = [];\n try {\n names = readdirSync(path.join(this.root, \"cursors\")).filter((n) => n.endsWith(\".json\"));\n } catch {\n return;\n }\n for (const n of names) {\n const s = new Cursors(this.root, n.slice(0, -5));\n const c = s.read();\n const had = Object.keys(c).filter((k) => keys.has(k));\n if (!had.length) continue;\n for (const k of had) delete c[k];\n s.write(c);\n }\n }\n}\n\n/** Per-sink cursor: byte offset per session file, moved only on a 2xx. */\nexport class Cursors {\n constructor(\n readonly root: string,\n readonly sink: string,\n ) {}\n private file(): string {\n return path.join(this.root, \"cursors\", `${safeId(this.sink)}.json`);\n }\n /** The map; empty when the file is missing, cut off mid-write, or holds anything\n * but an object (`null`, a list), so a drain never indexes into those. */\n read(): Record<string, number> {\n try {\n const c = JSON.parse(readFileSync(this.file(), \"utf8\")) as unknown;\n return c && typeof c === \"object\" && !Array.isArray(c) ? (c as Record<string, number>) : {};\n } catch {\n return {};\n }\n }\n write(c: Record<string, number>): void {\n const f = this.file();\n mkdirSync(path.dirname(f), { recursive: true, mode: 0o700 });\n const tmp = `${f}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify(c), { mode: 0o600 });\n renameSync(tmp, f);\n }\n key(ref: OutboxRef): string {\n return `${ref.harness}/${ref.session}`;\n }\n}\n\n/** A lock per session so two workers never interleave. Stale once the worker that\n * took it is gone (its 15 s deadline ends it without a release), or after 5 min. */\nexport function takeLock(root: string, ref: OutboxRef, staleMs = 5 * 60_000): (() => void) | null {\n return takeNamedLock(root, `${safeId(ref.harness)}-${safeId(ref.session)}`, staleMs);\n}\n\n/** A named lock under <root>/locks, held by one process at a time: the file names\n * its pid and a nonce. Stale once that process is gone, or after `staleMs`. A stale\n * lock is taken over by renaming it aside first: of two workers that both judged it\n * stale, one rename wins, and one that moved a fresh lock instead (another worker's,\n * made in between) puts it back and stands down. The release deletes the file only\n * while it is still this taker's, so a lock taken over can't be wiped by its old\n * holder. Two holders at once used to mean two drains reading the same cursors and\n * sending the same batch (issue #208's one-request-a-second pace rests on one). */\nexport function takeNamedLock(root: string, name: string, staleMs = 5 * 60_000): (() => void) | null {\n const dir = path.join(root, \"locks\");\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n const f = path.join(dir, `${name}.lock`);\n const nonce = randomBytes(8).toString(\"hex\");\n const mine = `${process.pid} ${nonce}`;\n let body: string | null = null;\n try {\n body = readFileSync(f, \"utf8\");\n const stale = Date.now() - statSync(f).mtimeMs > staleMs || !processAlive(Number(body.split(\" \")[0]));\n if (!stale) return null;\n } catch {\n body = null;\n }\n if (body !== null) {\n const aside = `${f}.${process.pid}.${nonce}.stale`;\n try {\n renameSync(f, aside);\n } catch {\n return null;\n }\n // The aside name is this call's own: nobody else reads or removes it.\n const moved = readFileSync(aside, \"utf8\");\n unlinkSync(aside);\n if (moved !== body) {\n try {\n writeFileSync(f, moved, { flag: \"wx\" });\n } catch {}\n return null;\n }\n }\n try {\n writeFileSync(f, mine, { flag: \"wx\" });\n } catch {\n return null;\n }\n return () => {\n try {\n if (readFileSync(f, \"utf8\") === mine) unlinkSync(f);\n } catch {}\n };\n}\n\n/** `takeNamedLock`, tried again every `everyMs` until `forMs` have passed: for a\n * command whose work goes under a lock a worker holds for seconds (a drain). null\n * once the time is up. */\nexport async function waitNamedLock(\n root: string,\n name: string,\n forMs: number,\n everyMs = 100,\n): Promise<(() => void) | null> {\n for (let waited = 0; ; waited += everyMs) {\n const release = takeNamedLock(root, name);\n if (release || waited >= forMs) return release;\n await new Promise((r) => setTimeout(r, everyMs));\n }\n}\n\n/** Whether the process a lock or a claimed job names is still running. A pid that\n * can't be read counts as running: the age rule decides then. */\nexport function processAlive(pid: number): boolean {\n if (!Number.isInteger(pid) || pid <= 0) return true;\n try {\n process.kill(pid, 0);\n return true;\n } catch (e) {\n return (e as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// pii@1 \u2014 an opt-in second pass that reduces identifiers (spec/PRIVACY.md \u00A72). It\n// does not promise anonymity; it removes the obvious.\nimport { createHash } from \"node:crypto\";\n\nexport const RULESET_PII = \"pii@1\";\n\nconst EMAIL = /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/g;\n/** E.164 and common national forms: an optional +, 7\u201315 digits with separators,\n * optional area-code parentheses. A version number (22.23.1) or an IP never matches:\n * the run must not sit between dots, and needs \u2265 7 digits. An ISO date (2026-10-31)\n * has a phone's shape, so it is tried first (group 1) and kept, but only when nothing\n * after it could carry the run on: no letter or digit, and no separator then two digits\n * (a phone's group is two to four; one digit, as in \"2026-10-31 5 files\", never is),\n * unless that is a time's hour (\" 12:\", 00\u201323), and not right after a \")\" or a \"+\"\n * with a country code: a phone's prefix this pass could not take (\"x(415) 2026-10-31\",\n * \"[phone]+1 2026-10-31\") would take the date on the next one. Any other date is read\n * as before. The date may follow a `+` (a diff's added line), so a second pass over\n * \"[phone]+2026-10-31\" keeps it too. */\nconst PHONE =\n /(?<![\\w.])(?:(?<!\\)[ .-]?|\\+\\d{1,3}[ .-]?)(\\+?(?:19|20)\\d{2}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\\d|3[01]))(?:(?= (?:[01]?\\d|2[0-3]):)|(?!\\w|[ .-]\\d\\d))|(?:\\+\\d{1,3}[ .-]?)?(?:\\(\\d{2,4}\\)[ .-]?)?\\d{2,4}(?:[ .-]?\\d{2,4}){1,3}(?!\\w))/g;\n\nfunction looksPhone(s: string): boolean {\n const digits = s.replace(/\\D/g, \"\");\n if (digits.length < 7 || digits.length > 15) return false;\n // \"port 8891, pr 12345\": a bare run with no separator or + is a number, not a phone.\n if (!/[+() .-]/.test(s)) return false;\n // \"22.23.1\", \"185.199.108.153\": dots alone are a version or an address.\n if (!/[+() -]/.test(s)) return false;\n return true;\n}\n\n/** Replace emails and phone numbers, and write `home` as `~`. */\nexport function reducePii(text: string, home?: string): string {\n if (!text) return text;\n let out = text.replace(EMAIL, \"[email]\");\n out = out.replace(PHONE, (m, date?: string) => (!date && looksPhone(m) ? \"[phone]\" : m));\n if (home) {\n const h = home.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n out = out.replace(new RegExp(`(?<![A-Za-z0-9_])${h}(?=/|$|\\\\s|[\"'\\`])`, \"g\"), \"~\");\n }\n return out;\n}\n\n/** The machine's name under pii@1: the first 8 hex of its SHA-256. */\nexport function hashMachine(name: string): string {\n return createHash(\"sha256\").update(name).digest(\"hex\").slice(0, 8);\n}\n\n/** Apply pii@1 to every string in a value; `home` becomes `~`. */\nexport function reducePiiDeep<T>(v: T, home?: string): T {\n if (typeof v === \"string\") return reducePii(v, home) as T;\n if (Array.isArray(v)) return v.map((x) => reducePiiDeep(x, home)) as T;\n if (v && typeof v === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [k, x] of Object.entries(v as Record<string, unknown>)) out[k] = reducePiiDeep(x, home);\n return out as T;\n }\n return v;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// secrets@1 \u2014 credentials out of every string, on the person's machine, before an\n// event reaches the outbox. Ported from spacesheep-cli lib/redact.js (Michael\n// Makarov, MIT \u2192 Apache-2.0, see NOTICE). The ruleset is defined in\n// spec/PRIVACY.md \u00A72 and pinned by conformance/redaction/secrets.json.\n//\n// Every pattern is linear: bounded repetition, no nested quantifiers. The\n// adversarial vectors (password_ \u00D7 5000, a 50 000-space run) pin that.\n\nexport const RULESET_SECRETS = \"secrets@1\";\nexport const REDACTED = \"[redacted]\";\n\n/** Token formats that are a credential by their shape alone. Each is anchored on a\n * vendor prefix, so a commit sha or a UUID never matches. */\nconst TOKEN_PATTERNS: RegExp[] = [\n /-----BEGIN [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----[\\s\\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----|$)/g,\n /\\b(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}\\b/g, // AWS access key id\n /\\bgh[pousr]_[A-Za-z0-9]{30,}\\b/g, // GitHub\n /\\bgithub_pat_[A-Za-z0-9_]{30,}\\b/g,\n /\\bglpat-[A-Za-z0-9_-]{20,}\\b/g, // GitLab\n /\\bxox[abposre]-[A-Za-z0-9-]{10,}\\b/g, // Slack\n /\\bhttps:\\/\\/hooks\\.slack\\.com\\/services\\/[A-Za-z0-9/_-]+/g,\n /\\bhttps:\\/\\/(?:ptb\\.|canary\\.)?discord(?:app)?\\.com\\/api\\/webhooks\\/[0-9]+\\/[A-Za-z0-9_-]+/g,\n /\\bsk-(?:ant-|proj-|svcacct-|admin-|or-v1-)?[A-Za-z0-9_-]{20,}\\b/g, // Anthropic, OpenAI, DeepSeek, OpenRouter\n /\\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\\b/g, // Stripe secret / restricted\n /\\bwhsec_[A-Za-z0-9+/=]{20,}/g, // Stripe / Svix / Standard Webhooks secret\n /\\bAIza[0-9A-Za-z_-]{35}\\b/g, // Google API key\n /\\bya29\\.[0-9A-Za-z_-]{20,}/g, // Google OAuth access token\n /\\b1\\/\\/0[0-9A-Za-z_-]{30,}/g, // Google OAuth refresh token\n /\\bGOCSPX-[0-9A-Za-z_-]{20,}/g, // Google OAuth client secret\n /\\bxai-[A-Za-z0-9]{30,}\\b/g,\n /\\bgsk_[A-Za-z0-9]{30,}\\b/g, // Groq\n /\\bpplx-[A-Za-z0-9]{30,}\\b/g, // Perplexity\n /\\bhf_[A-Za-z0-9]{30,}\\b/g, // Hugging Face\n /\\bpcsk_[A-Za-z0-9_]{30,}\\b/g, // Pinecone\n /\\bnpm_[A-Za-z0-9]{30,}\\b/g,\n /\\bpypi-[A-Za-z0-9_-]{50,}/g,\n /\\bSG\\.[A-Za-z0-9_-]{16,}\\.[A-Za-z0-9_-]{16,}/g, // SendGrid\n /\\bshp(?:at|ss|ca|pa)_[a-fA-F0-9]{32}\\b/g, // Shopify\n /\\bdop_v1_[a-f0-9]{64}\\b/g, // DigitalOcean\n /\\b(?:lin_api|lin_oauth)_[A-Za-z0-9]{30,}\\b/g, // Linear\n /\\b(?:ntn|secret)_[A-Za-z0-9]{40,}\\b/g, // Notion\n /\\bsbp_[a-f0-9]{40}\\b/g, // Supabase\n /\\bfigd_[A-Za-z0-9_-]{30,}/g, // Figma\n /\\bdp\\.(?:pt|st|sa|ct)\\.[A-Za-z0-9]{30,}/g, // Doppler\n /\\bgrn_[A-Za-z0-9_-]{20,}/g, // Granola\n /\\bss_[a-f0-9]{32,}\\b/g, // spacesheep API keys\n /\\b[0-9]{8,10}:AA[A-Za-z0-9_-]{33}\\b/g, // Telegram bot token\n /\\beyJ[A-Za-z0-9_-]{10,}\\.eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}/g, // JWT\n];\n/** `scheme://user:password@host` \u2014 the password goes, the user and host stay. */\nconst URL_CREDENTIALS = /\\b([a-z][a-z0-9+.-]{1,20}:\\/\\/[^\\s:/@'\"`<>]{1,100}:)([^\\s@/'\"`<>]{1,200})(@)/gi;\n/** `Authorization: Bearer \u2026` / `Basic \u2026`, and a bare `Bearer <token>` in a curl line. */\nconst AUTH_HEADER = /\\b((?:Bearer|Basic|Token)\\s+)([A-Za-z0-9._~+/=-]{16,})/g;\n// The words that name a secret, in prose. `key` alone is too common (\"the key\n// point\"), so it only counts with a qualifier.\nconst SECRET_WORD =\n \"(?:pass(?:word|wd|phrase|code)?s?|pwd|secret(?:[ _-]?key)?s?|client[ _-]?secret|api[ _-]?keys?|apikeys?|access[ _-]?keys?|access[ _-]?tokens?|auth[ _-]?tokens?|refresh[ _-]?tokens?|private[ _-]?keys?|tokens?|credentials?|creds)\";\n/** Strict form: a NAME, an explicit separator (`=`, `:`, `=>`), a value. The name is\n * one flat run of identifier characters judged in code (isSecretName), never by a\n * nested pattern. Only a secret name goes on to consume its value \u2014 otherwise\n * `https:` would swallow the `?token=\u2026` behind it. */\nconst NAME_SEP = /(?<![A-Za-z0-9_])([A-Za-z][A-Za-z0-9_-]{1,79})([\"']?\\s{0,3}(?::=|=>|=|:)\\s{0,3})/g;\nconst VALUE = /\"([^\"\\n]{1,300})\"|'([^'\\n]{1,300})'|`([^`\\n]{1,300})`|([^\\s\"'`,;&)}\\]]{1,300})/y;\n\nfunction redactAssignments(text: string): string {\n let out = \"\";\n let last = 0;\n NAME_SEP.lastIndex = 0;\n for (let m = NAME_SEP.exec(text); m; m = NAME_SEP.exec(text)) {\n const name = m[1] as string;\n if (!isSecretName(name)) continue;\n VALUE.lastIndex = NAME_SEP.lastIndex;\n const v = VALUE.exec(text);\n if (!v) continue;\n const value = (v[1] ?? v[2] ?? v[3] ?? v[4]) as string;\n NAME_SEP.lastIndex = VALUE.lastIndex;\n if (isPlaceholder(value) || value.includes(REDACTED)) continue;\n // `max_tokens: 4096`, `input_tokens=12k` \u2014 a token COUNT, not a token.\n if (/tokens?$/i.test(name) && /^\\d+(?:[.,]\\d+)*[km]?$/i.test(value)) continue;\n const q = v[1] != null ? '\"' : v[2] != null ? \"'\" : v[3] != null ? \"`\" : \"\";\n out += text.slice(last, v.index) + q + REDACTED + q;\n last = VALUE.lastIndex;\n }\n return last ? out + text.slice(last) : text;\n}\n\n/** Is this identifier the name of a secret? `DB_PASSWORD`, `apiKey`, `client-secret`,\n * `AWS_SECRET_ACCESS_KEY`, `GITHUB_TOKEN`; an env-style `STRIPE_KEY` too. Not\n * `password_hash`, `tokenizer`, `sort_key`. */\nexport function isSecretName(name: string): boolean {\n if (/^[A-Z0-9_]+_KEY$/.test(name)) return true;\n const flat = name.toLowerCase().replace(/[_-]/g, \"\");\n return /(?:pass(?:word|wd|phrase|code)|pwd|secret|secretkey|secretaccesskey|clientsecret|apikey|accesskey|accesstoken|authtoken|refreshtoken|privatekey|token|credential|creds)s?$/.test(\n flat,\n );\n}\n\n/** Loose form: prose. \"password is Hunter2!\", \"the token was ghx\u2026\". Only a value\n * that looks generated counts, so \"the password is wrong\" passes untouched.\n * Whitespace runs are bounded so a long run can't make the match quadratic. */\nconst PROSE = new RegExp(\n `(\\\\b${SECRET_WORD}(?:[ \\\\t]{1,3}(?:is|was|=|:|to|as|of|for[ \\\\t]{1,3}\\\\S{1,40}[ \\\\t]{1,3}is))?[ \\\\t]{1,3})([\"'\\`]?)([^\\\\s\"'\\`]{6,200})\\\\2`,\n \"gi\",\n);\n\n/** A value that is plainly not the secret itself: a type, a variable, a template, an\n * already-redacted marker, an env lookup. */\nfunction isPlaceholder(v: string): boolean {\n const s = v.replace(/[.,;:!?]+$/, \"\");\n if (!s) return true;\n if (\n /^(?:string|number|boolean|bool|str|int|any|unknown|null|undefined|none|nil|true|false|optional|required|text|varchar|password|secret|token|env|string\\[\\]|object|bytes)$/i.test(\n s,\n )\n )\n return true;\n if (/^\\[?redacted\\]?$/i.test(s) || /^\\*+$/.test(s) || /^x{3,}$/i.test(s) || /^\\.{3}$|^\u2026$/.test(s)) return true;\n if (/^[<{$%]/.test(s)) return true; // <your-key>, ${VAR}, {{var}}, $VAR, %VAR%\n if (\n /^(?:process\\.env|env|os\\.environ|c\\.env|this\\.env|import\\.meta\\.env|getenv|config|settings|secrets|args|opts|options|req|request|body|params|input|data|user|self|this)\\b/i.test(\n s,\n )\n )\n return true;\n if (/^[A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*)+$/.test(s)) return true; // a member path: form.password\n if (/[(]/.test(s)) return true; // a call: hash(pw), getSecret()\n if (/^(?:your|my|the|a|an|some|new|old)[-_ ]/i.test(s)) return true; // your-api-key\n return false;\n}\n\n/** Does a prose value look like something generated, rather than an English word? */\nfunction looksSecret(v: string): boolean {\n const s = v.replace(/[.,;:!?)]+$/, \"\");\n if (s.length < 6) return false;\n if (isPlaceholder(s)) return false;\n if (/^https?:\\/\\//i.test(s)) return false; // a link, handled by URL_CREDENTIALS if it carries one\n if (/^[a-z]+(?:[-'][a-z]+)*$/.test(s)) return false; // a lowercase word\n if (/^[A-Z][a-z]+$/.test(s)) return false; // a capitalised word\n if (/^\\d+(?:[.,]\\d+)*[a-z%]*$/i.test(s)) return false; // a count: 4096, 128k, 3.5, 50%\n if (/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/.test(s)) return false; // an env var's NAME\n const kinds = [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/].filter((r) => r.test(s)).length;\n return kinds >= 3 || (/[0-9]/.test(s) && /[A-Za-z]/.test(s));\n}\n\n/** Redact every credential in `text`. Returns the same string when there is none. */\nexport function redactSecrets(text: string): string {\n if (!text) return text;\n let out = text;\n for (const re of TOKEN_PATTERNS) out = out.replace(re, REDACTED);\n out = out.replace(URL_CREDENTIALS, (_m, head: string, _pw: string, at: string) => `${head}${REDACTED}${at}`);\n out = out.replace(AUTH_HEADER, (_m, head: string) => `${head}${REDACTED}`);\n out = redactAssignments(out);\n out = out.replace(PROSE, (m, head: string, q: string, v: string) =>\n !looksSecret(v) || v.includes(REDACTED) ? m : `${head}${q}${REDACTED}${q}`,\n );\n return out;\n}\n\n/** Did redaction change anything? */\nexport function hasSecret(text: string): boolean {\n return !!text && redactSecrets(text) !== text;\n}\n\n/** Redact every string in a JSON-able value (keys are left alone). */\nexport function redactDeep<T>(v: T): T {\n if (typeof v === \"string\") return redactSecrets(v) as T;\n if (Array.isArray(v)) return v.map(redactDeep) as T;\n if (v && typeof v === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [k, x] of Object.entries(v as Record<string, unknown>)) out[k] = redactDeep(x);\n return out as T;\n }\n return v;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// filter(event, tier, pii): the pure function every sink runs an event through.\n// Removes by ALLOWLIST per type and tier (spec/PRIVACY.md \u00A71), redacts every string\n// above tier 0 with secrets@1, and runs pii@1 when asked. Returns null when the\n// type is not carried at the tier. The conformance runner runs the same function.\nimport type { Event, Tier } from \"../types.js\";\nimport { hashMachine, RULESET_PII, reducePiiDeep } from \"./pii.js\";\nimport { RULESET_SECRETS, redactDeep } from \"./secrets.js\";\n\n/** The lowest tier that carries each known type (kept in step with schema/v1.ts). */\nexport const TYPE_TIER: Record<string, Tier> = {\n \"session.started\": 0,\n \"session.heartbeat\": 0,\n \"session.ended\": 0,\n \"session.backfill\": 0,\n \"session.forgotten\": 0,\n \"turn.started\": 0,\n \"turn.ended\": 0,\n \"turn.transcript\": 2,\n \"turn.progress\": 2,\n \"tool.started\": 1,\n \"tool.ended\": 1,\n \"files.changed\": 1,\n \"attention.needed\": 0,\n \"attention.cleared\": 0,\n \"subagent.started\": 1,\n \"subagent.ended\": 1,\n \"context.compacted\": 1,\n \"limits.observed\": 1,\n \"delegation.started\": 0,\n \"delegation.ended\": 0,\n native: 3,\n};\n\n/** data keys allowed per type at tier N (cumulative: tier 1 includes tier 0's). */\nconst DATA_ALLOW: Record<string, Partial<Record<Tier, string[]>>> = {\n \"session.started\": { 0: [\"source\"] },\n \"session.heartbeat\": { 0: [] },\n \"session.ended\": { 0: [\"reason\"] },\n \"session.backfill\": { 0: [\"started_at\", \"last_at\", \"turns\"] },\n \"session.forgotten\": { 0: [] },\n \"turn.started\": { 0: [\"turn_id\", \"submitted_by\", \"injected\"], 1: [\"prompt_chars\"] },\n \"turn.ended\": { 0: [\"turn_id\", \"reason\", \"ms\", \"background\"], 1: [\"error\"] },\n \"turn.transcript\": { 2: [\"turn\", \"user\", \"assistant\", \"at\", \"injected\"] },\n \"turn.progress\": { 2: [\"turn\", \"user\", \"assistant\", \"at\", \"injected\"] },\n \"tool.started\": { 1: [\"tool\", \"call_id\", \"turn_id\", \"agent_id\"], 3: [\"input\"] },\n \"tool.ended\": { 1: [\"tool\", \"call_id\", \"turn_id\", \"agent_id\", \"ms\", \"ok\", \"error\"], 3: [\"input\", \"output\"] },\n \"files.changed\": { 1: [\"paths\"] },\n \"attention.needed\": { 0: [\"attention_id\", \"kind\"], 1: [\"tool\", \"message\", \"agent_id\"] },\n \"attention.cleared\": { 0: [\"attention_id\", \"how\"] },\n \"subagent.started\": { 1: [\"agent_id\", \"agent_type\"] },\n \"subagent.ended\": { 1: [\"agent_id\", \"agent_type\", \"background\"] },\n \"context.compacted\": { 1: [\"trigger\"] },\n \"limits.observed\": { 1: [\"windows\", \"plan\", \"account_id\"] },\n \"delegation.started\": { 0: [\"ask_id\", \"via\", \"harness\", \"to\"], 1: [\"text\"] },\n \"delegation.ended\": { 0: [\"ask_id\", \"to\", \"ok\", \"ms\", \"error\", \"relayed\"], 2: [\"reply\"] },\n};\n\nconst SESSION_ALLOW = [\n \"id\",\n \"seq\",\n \"machine\",\n \"cwd\",\n \"repo\",\n \"branch\",\n \"model\",\n \"title\",\n \"title_source\",\n \"url\",\n \"account_id\",\n \"attended\",\n \"parent_id\",\n \"launched_by\",\n \"delegated_by\",\n];\nconst MAX_SIDE = 20_000;\nconst MAX_IO = 64 * 1024;\n\nfunction allowedKeys(type: string, tier: Tier): string[] | null {\n const table = DATA_ALLOW[type];\n if (!table) return null; // unknown type: kept whole (receivers keep extra keys), redacted\n const out: string[] = [];\n for (let t = 0; t <= tier; t++) for (const k of table[t as Tier] ?? []) out.push(k);\n return out;\n}\n\nfunction cap(v: unknown, max: number): unknown {\n if (typeof v === \"string\") return v.length > max ? `${v.slice(0, max)}\u2026` : v;\n if (v && typeof v === \"object\") {\n const s = JSON.stringify(v);\n return s.length > max ? `${s.slice(0, max)}\u2026` : v;\n }\n return v;\n}\n\n/** At most `max` characters, keeping the end: an ellipsis, then the last max \u2212 1. */\nexport function capTail(v: string, max: number): string {\n return v.length > max ? `\u2026${v.slice(v.length - max + 1)}` : v;\n}\n\nexport interface FilterOptions {\n /** The person's home directory, for pii@1's `~` rewrite. */\n home?: string;\n}\n\n/** The event as a sink at `tier` may see it, or null when the type is above the tier. */\nexport function filterEvent(event: Event, tier: Tier, pii: boolean, opts: FilterOptions = {}): Event | null {\n const minTier = TYPE_TIER[event.type];\n if (minTier !== undefined && minTier > tier) return null;\n\n const session: Record<string, unknown> = {};\n for (const k of SESSION_ALLOW)\n if (event.session[k as keyof typeof event.session] !== undefined)\n session[k] = event.session[k as keyof typeof event.session];\n\n let data: Record<string, unknown> = {};\n const keys = allowedKeys(event.type, tier);\n if (keys === null) data = { ...event.data };\n else for (const k of keys) if (event.data[k] !== undefined) data[k] = event.data[k];\n if (typeof data.user === \"string\") data.user = cap(data.user, MAX_SIDE);\n // A turn still running is read for where it is now: its reply keeps the end.\n if (typeof data.assistant === \"string\")\n data.assistant = event.type === \"turn.progress\" ? capTail(data.assistant, MAX_SIDE) : cap(data.assistant, MAX_SIDE);\n if (data.input !== undefined) data.input = cap(data.input, MAX_IO);\n if (data.output !== undefined) data.output = cap(data.output, MAX_IO);\n if (typeof data.message === \"string\") data.message = cap(data.message, 200);\n // What an ask asked, and what came back (PROTOCOL.md \u00A74 delegation.*): the daemon cuts\n // them too, but the cap is this function's promise.\n if (event.type === \"delegation.started\" && typeof data.text === \"string\") data.text = cap(data.text, 200);\n if (event.type === \"delegation.ended\" && typeof data.reply === \"string\") data.reply = cap(data.reply, MAX_SIDE);\n\n const rulesets: string[] = [];\n let out: Event = { ...event, tier, session: session as Event[\"session\"], data, privacy: { rulesets, pii } };\n if (tier > 0) {\n // Redact, then the caps above already applied: a cut before redaction could leave half a key.\n out = { ...out, session: redactDeep(out.session), data: redactDeep(out.data) };\n rulesets.push(RULESET_SECRETS);\n }\n if (pii) {\n const s = reducePiiDeep(out.session, opts.home) as Record<string, unknown>;\n if (typeof s.machine === \"string\") s.machine = hashMachine(s.machine);\n delete s.account_id;\n const d = reducePiiDeep(out.data, opts.home) as Record<string, unknown>;\n // limits.observed names the account its numbers belong to: the same identifier\n // pii@1 drops from the session block, so it goes the same way.\n delete d.account_id;\n out = { ...out, session: s as Event[\"session\"], data: d };\n rulesets.push(RULESET_PII);\n }\n out.privacy = { rulesets, pii };\n return out;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// A session's short id (spec/PROTOCOL.md \u00A73): the first 8 hex digits of\n// sha256(\"sessionpipe.session:\" + harness + \":\" + id). What a person reads in a log\n// line, a notification, a search box; derived, never sent. Not a prefix of the id\n// itself: Codex ids are time-ordered (UUIDv7), and two sessions started in the same\n// minute share their first eight characters.\nimport { createHash } from \"node:crypto\";\n\nexport function shortId(harness: string, id: string): string {\n return createHash(\"sha256\").update(`sessionpipe.session:${harness}:${id}`).digest(\"hex\").slice(0, 8);\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// file:PATH \u2014 append filtered events as JSONL. The zero-server sink.\nimport { appendFileSync, mkdirSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { Event, Tier } from \"../types.js\";\nimport type { DeliveryResult, Sink } from \"./types.js\";\n\nexport class FileSink implements Sink {\n constructor(\n readonly name: string,\n readonly file: string,\n readonly tier: Tier,\n readonly pii: boolean,\n ) {}\n async send(events: Event[]): Promise<DeliveryResult> {\n try {\n mkdirSync(path.dirname(this.file), { recursive: true });\n appendFileSync(this.file, `${events.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`);\n return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };\n } catch (e) {\n return { ok: false, acknowledged: 0, error: String((e as Error).message), action: \"retry\" };\n }\n }\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// stdout \u2014 one JSON line per event to a writable (the worker's stdout is ignored\n// when spawned by a hook, so this sink is for `sessionpipe tail` and tests).\nimport type { Event, Tier } from \"../types.js\";\nimport type { DeliveryResult, Sink } from \"./types.js\";\n\nexport class StdoutSink implements Sink {\n constructor(\n readonly name: string,\n readonly tier: Tier,\n readonly pii: boolean,\n private readonly write: (line: string) => void = (l) => process.stdout.write(`${l}\\n`),\n ) {}\n async send(events: Event[]): Promise<DeliveryResult> {\n try {\n for (const e of events) this.write(JSON.stringify(e));\n return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };\n } catch (e) {\n // A closed pipe (`sessionpipe tail | head`): a result, like every other sink.\n return { ok: false, acknowledged: 0, error: String((e as Error).message ?? e), action: \"retry\" };\n }\n }\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Sessions the daemon started under the command's name (spec/CONTROL.md \u00A76 `start`).\n// Codex and Antigravity pick a new session's id themselves, but a `start` names the\n// session (`codex:<id>`), and that name is what the person's receiver knows it by. So\n// the moment a run reveals the harness's own id, the daemon records it here:\n//\n// state/control/aliases.json {\"codex:<command's id>\": {\"id\": \"<harness's id>\",\n// \"cwd\": \"<folder>\", \"at\": \"<iso>\",\n// \"permission\": \"<level>\", \"model\": \"<id>\"}, \u2026} (0600)\n//\n// and everything the machine reports uses the command's id: the daemon's acks, a later\n// prompt (resumed under the harness's id), and the hook events, which the worker renames\n// before it builds them (run.ts). Small, so it is read whole wherever it is needed.\nimport { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { type StartOptions, startOptionsOf } from \"./store.js\";\n\nexport interface Alias {\n /** The harness's own session id. */\n id: string;\n /** The folder the session runs in (the start's). */\n cwd: string;\n at: string;\n /** What the start ran with (CONTROL.md \u00A76, Options); absent for an alias recorded\n * before 0.10.0, whose later runs keep the machine's mode. */\n permission?: StartOptions[\"permission\"];\n model?: string;\n}\nexport type Aliases = Record<string, Alias>;\n\nexport const aliasFile = (controlDir: string): string => path.join(controlDir, \"aliases.json\");\n\nexport function readAliases(file: string): Aliases {\n try {\n const j = JSON.parse(readFileSync(file, \"utf8\")) as unknown;\n if (!j || typeof j !== \"object\" || Array.isArray(j)) return {};\n const out: Aliases = {};\n for (const [k, v] of Object.entries(j as Record<string, unknown>)) {\n const a = v as Partial<Alias> | null;\n if (a && typeof a.id === \"string\" && a.id && typeof a.cwd === \"string\")\n out[k] = { id: a.id, cwd: a.cwd, at: typeof a.at === \"string\" ? a.at : \"\", ...startOptionsOf(a) };\n }\n return out;\n } catch {\n return {};\n }\n}\n\n/** Written whole and renamed into place, 0600 in a 0700 folder. */\nexport function writeAliases(file: string, all: Aliases): void {\n mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });\n const tmp = `${file}.${process.pid}.tmp`;\n writeFileSync(tmp, `${JSON.stringify(all, null, 2)}\\n`, { mode: 0o600 });\n renameSync(tmp, file);\n try {\n chmodSync(file, 0o600);\n } catch {}\n}\n\n/** The command's id a harness's own session goes by, or null when it was never renamed. */\nexport function receiverIdOf(all: Aliases, harness: string, id: string): string | null {\n const prefix = `${harness}:`;\n for (const [k, a] of Object.entries(all)) if (a.id === id && k.startsWith(prefix)) return k.slice(prefix.length);\n return null;\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Where this machine's secrets live: each sink's token (and Webhooks secret) and each\n// paired receiver's machine token. In the operating system's keychain when this person\n// has one unlocked \u2014 the macOS Keychain, or the Secret Service (GNOME Keyring, KWallet)\n// in a Linux desktop session \u2014 and otherwise in config.json / control.json at mode\n// 0600, which is all a headless server has (the same as ~/.ssh). A secret in a store\n// leaves `token_in` in the file and no token.\n//\n// What a store protects against, plainly: a dotfiles repo, a backup, a synced\n// ~/.config, a `cat` over a shoulder. Not a program running as you: `security` hands an\n// item it wrote back without asking, which is also how the hook's worker reads it.\n\nimport { spawnSync } from \"node:child_process\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { configFile, readConfig, type SinkConfig, writeConfig } from \"@sessionpipe/core\";\n\nexport type SecretStore = \"keychain\" | \"secret-service\";\nexport type StoreName = SecretStore | \"file\";\n\nconst SERVICE = \"sessionpipe\";\n\nexport type Runner = (cmd: string, args: string[], input?: string) => { status: number | null; stdout: string };\nexport const defaultRunner: Runner = (cmd, args, input) => {\n const r = spawnSync(cmd, args, {\n input: input ?? \"\",\n encoding: \"utf8\",\n timeout: 8000,\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n });\n return { status: r.error ? null : r.status, stdout: r.stdout ?? \"\" };\n};\n\n/** A sink's item. The machine has one config, so a sink's name is enough \u2014 except\n * where SESSIONPIPE_CONFIG points at another (a second profile, a test): then the\n * item names that config too, so two configs' sinks of one name never share a key. */\nexport const sinkAccount = (name: string, what: \"token\" | \"secret\" = \"token\"): string => {\n const scope = process.env.SESSIONPIPE_CONFIG\n ? `@${createHash(\"sha256\").update(configFile()).digest(\"hex\").slice(0, 8)}`\n : \"\";\n return `sink:${name}:${what}${scope}`;\n};\nexport const controlAccount = (machine: string): string => `control:${machine}`;\n\n/** `security -i` reads its commands from stdin, so the value never sits in argv (and in\n * `ps`). Its parser takes double-quoted words: a value with a quote, a backslash or a\n * space isn't stored there and stays in the file. */\nconst KC_SAFE = /^[\\x21\\x23-\\x5b\\x5d-\\x7e]{1,4096}$/;\nconst NAME_SAFE = /^[\\x20\\x21\\x23-\\x5b\\x5d-\\x7e]{1,200}$/;\n\nexport function secretGet(store: SecretStore, account: string, run: Runner = defaultRunner): string | null {\n const r =\n store === \"keychain\"\n ? run(\"security\", [\"find-generic-password\", \"-s\", SERVICE, \"-a\", account, \"-w\"])\n : run(\"secret-tool\", [\"lookup\", \"service\", SERVICE, \"account\", account]);\n if (r.status !== 0) return null;\n const v = r.stdout.replace(/\\r?\\n$/, \"\");\n return v || null;\n}\n\nexport function secretSet(store: SecretStore, account: string, value: string, run: Runner = defaultRunner): boolean {\n if (store === \"keychain\") {\n if (!KC_SAFE.test(value) || !NAME_SAFE.test(account)) return false;\n run(\n \"security\",\n [\"-i\"],\n `add-generic-password -U -s ${SERVICE} -a \"${account}\" -l \"${SERVICE} ${account}\" -w \"${value}\"\\n`,\n );\n } else {\n run(\"secret-tool\", [\"store\", \"--label\", `${SERVICE} ${account}`, \"service\", SERVICE, \"account\", account], value);\n }\n // Trust only a read-back: `security -i` exits 0 whatever its commands did.\n return secretGet(store, account, run) === value;\n}\n\nexport function secretDel(store: SecretStore, account: string, run: Runner = defaultRunner): void {\n if (store === \"keychain\") run(\"security\", [\"delete-generic-password\", \"-s\", SERVICE, \"-a\", account]);\n else run(\"secret-tool\", [\"clear\", \"service\", SERVICE, \"account\", account]);\n}\n\n/** Can this process write, read back and delete an item in the store right now? */\nexport function probe(store: SecretStore, run: Runner = defaultRunner): boolean {\n const account = `probe:${randomBytes(6).toString(\"hex\")}`;\n const ok = secretSet(store, account, randomBytes(12).toString(\"base64url\"), run);\n secretDel(store, account, run);\n return ok;\n}\n\n/** The best place for secrets on this machine, from this session. SESSIONPIPE_SECRETS\n * (file | keychain | secret-service) overrides. The Secret Service is used only in a\n * desktop session: on a server its keyring is locked whenever nobody is logged in,\n * which is exactly when the daemon needs its token. */\nexport function bestStore(\n env: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n run: Runner = defaultRunner,\n): StoreName {\n const want = env.SESSIONPIPE_SECRETS;\n if (want === \"file\") return \"file\";\n const candidates: SecretStore[] =\n want === \"keychain\" || want === \"secret-service\"\n ? [want]\n : platform === \"darwin\"\n ? [\"keychain\"]\n : platform === \"linux\" && env.DBUS_SESSION_BUS_ADDRESS && (env.DISPLAY || env.WAYLAND_DISPLAY)\n ? [\"secret-service\"]\n : [];\n for (const s of candidates) if (probe(s, run)) return s;\n return \"file\";\n}\n\nexport const storeLabel = (s: StoreName | undefined, platform: NodeJS.Platform = process.platform): string =>\n s === \"keychain\"\n ? \"the macOS Keychain\"\n : s === \"secret-service\"\n ? \"the Secret Service keyring\"\n : platform === \"win32\"\n ? \"a file under your profile\"\n : \"a 0600 file\";\n\n/** What keeps a key in the config file private: a mode on POSIX; on Windows, where modes\n * mean nothing, the profile folder's own permissions. */\nexport const fileKept = (platform: NodeJS.Platform = process.platform): string =>\n platform === \"win32\" ? \"your profile's permissions\" : \"0600\";\n\n/** `connect`'s line for keys that stay in the file. */\nexport function keysFileNote(\n platform: NodeJS.Platform = process.platform,\n env: NodeJS.ProcessEnv = process.env,\n): string {\n if (platform === \"win32\") return \"under your profile's permissions (Windows has no keychain sessionpipe uses)\";\n return `at 0600 \u2014 no unlocked keychain in this session${env.SSH_CONNECTION ? \" (an ssh shell)\" : \"\"}`;\n}\n\n/** The sink with its token and secret filled in from its store: each item the store\n * holds, since a sink has a token, a Webhooks secret, or both (`sink add --secret`\n * with no `--token` signs and sends no bearer). Null when the store can't be read\n * from this process right now (a keychain locked in an ssh session): the caller\n * leaves the events in the outbox rather than sending them unsigned. One item and not\n * the other is not that: the store has just answered a read from this process, so it\n * is readable right now, and the item it didn't give is one it doesn't hold. A locked\n * store fails both reads. */\nexport function sinkWithSecrets(s: SinkConfig, run: Runner = defaultRunner): SinkConfig | null {\n if (!s.token_in) return s;\n const token = secretGet(s.token_in, sinkAccount(s.name), run);\n const secret = secretGet(s.token_in, sinkAccount(s.name, \"secret\"), run);\n if (!token && !secret) return null;\n return { ...s, ...(token ? { token } : {}), ...(secret ? { secret } : {}) };\n}\n\n/** Store a sink's secrets in `to` (or back in the file) and save the config. */\nexport function moveSinkSecrets(\n to: StoreName,\n o: { run?: Runner; file?: string } = {},\n): { moved: string[]; kept: string[] } {\n const run = o.run ?? defaultRunner;\n const cfg = o.file ? readConfig(o.file) : readConfig();\n const moved: string[] = [];\n const kept: string[] = [];\n for (const s of cfg.sinks) {\n const from = s.token_in;\n if ((from ?? \"file\") === to) continue;\n const full = sinkWithSecrets(s, run);\n if (!full) {\n kept.push(s.name);\n continue;\n }\n if (!full.token && !full.secret) continue;\n if (to === \"file\") {\n // From a store, so with a token, a secret or both (sinkWithSecrets is null with\n // neither); a sink already in the file never gets here.\n if (full.token) s.token = full.token;\n if (full.secret) s.secret = full.secret;\n delete s.token_in;\n } else {\n const ok =\n (!full.token || secretSet(to, sinkAccount(s.name), full.token, run)) &&\n (!full.secret || secretSet(to, sinkAccount(s.name, \"secret\"), full.secret, run));\n if (!ok) {\n secretDel(to, sinkAccount(s.name), run);\n secretDel(to, sinkAccount(s.name, \"secret\"), run);\n kept.push(s.name);\n continue;\n }\n delete s.token;\n delete s.secret;\n s.token_in = to;\n }\n moved.push(s.name);\n if (from) {\n // Written first, then the file says where; only then is the old copy dropped.\n if (o.file) writeConfig(cfg, o.file);\n else writeConfig(cfg);\n secretDel(from, sinkAccount(s.name), run);\n secretDel(from, sinkAccount(s.name, \"secret\"), run);\n }\n }\n if (o.file) writeConfig(cfg, o.file);\n else writeConfig(cfg);\n return { moved, kept };\n}\n\n/** Forget a sink's secrets wherever they are (sink remove, uninstall). */\nexport function dropSinkSecrets(s: SinkConfig, run: Runner = defaultRunner): void {\n if (!s.token_in) return;\n secretDel(s.token_in, sinkAccount(s.name), run);\n secretDel(s.token_in, sinkAccount(s.name, \"secret\"), run);\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// What the control daemon keeps on disk:\n// config/control.json the receivers this machine is paired with: machine id,\n// the machine's own token, the enrolled keys, folders, mode (0600)\n// state/control/nonces.jsonl every verified nonce for 25 h, fsync'd before a\n// command runs (a failed write refuses it)\n// state/control/acks.jsonl acks not yet accepted by their receiver\n// state/control/state.json forks: which copy a message to a session goes to, and\n// the options each session the daemon started runs with\nimport {\n appendFileSync,\n chmodSync,\n closeSync,\n existsSync,\n fsyncSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n writeFileSync,\n} from \"node:fs\";\nimport path from \"node:path\";\nimport { configFile, stateDir } from \"@sessionpipe/core\";\nimport { levelRank, MODEL_RE, NONCE_TTL_MS, type RunLevel, type TrustedKey } from \"@sessionpipe/core/control\";\nimport {\n controlAccount,\n defaultRunner,\n type Runner,\n type SecretStore,\n type StoreName,\n secretDel,\n secretGet,\n secretSet,\n} from \"../secrets.js\";\n\nexport interface PairedReceiver {\n /** The receiver's origin, as the person typed it. */\n url: string;\n /** Absolute control endpoint (from the well-known file). */\n control: string;\n rpId: string;\n machine: string;\n /** The machine's own bearer (polls, acks, hello, off): opens nothing else. Empty\n * when it lives in a store this process can't read right now. */\n token: string;\n /** The token lives in this store, not in control.json (secrets.ts). */\n token_in?: SecretStore;\n keys: (TrustedKey & { name?: string; added_at: string })[];\n paired_at: string;\n}\n\n/** How sessions run when a message arrives (CONTROL.md \u00A72): the person's choice at\n * this machine, never a receiver's. */\nexport const MODES = [\"safe\", \"auto\", \"inherit\"] as const;\nexport type ControlMode = (typeof MODES)[number];\nexport const parseMode = (m: unknown): ControlMode | null =>\n (MODES as readonly unknown[]).includes(m) ? (m as ControlMode) : null;\n\nexport interface ControlConfig {\n name: string;\n /** Folders sessions may run in; compared on whole path segments, real paths. */\n folders: string[];\n mode: ControlMode;\n receivers: PairedReceiver[];\n /** Caps this machine keeps whatever a receiver sends (defaults in daemon.ts,\n * LIMITS): new sessions running at once and per hour, headless Claude Code runs\n * at once, copies of sessions made in a day. */\n limits?: { start_concurrent?: number; start_per_hour?: number; headless?: number; copies_per_day?: number };\n /** Whether a session here may ask another agent through the daemon (CONTROL.md \u00A713).\n * On unless `sessionpipe ask off` set it false; the daemon says which in its hello. */\n ask?: boolean;\n}\n\n/** `ask` as the config means it: on when unsaid. */\nexport const askOn = (c: Pick<ControlConfig, \"ask\">): boolean => c.ask !== false;\n\nexport const controlFile = (env: NodeJS.ProcessEnv = process.env) =>\n path.join(path.dirname(configFile(env)), \"control.json\");\nexport const controlState = (env: NodeJS.ProcessEnv = process.env) => path.join(stateDir(env), \"control\");\n\nexport function writePrivate(file: string, value: unknown): void {\n mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });\n const tmp = `${file}.${process.pid}.tmp`;\n writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\\n`, { mode: 0o600 });\n renameSync(tmp, file);\n try {\n chmodSync(file, 0o600);\n } catch {}\n}\n\nexport function readControl(env: NodeJS.ProcessEnv = process.env, run: Runner = defaultRunner): ControlConfig | null {\n try {\n const j = JSON.parse(readFileSync(controlFile(env), \"utf8\")) as ControlConfig;\n if (!j || !Array.isArray(j.receivers)) return null;\n return {\n name: j.name || \"machine\",\n folders: j.folders ?? [],\n mode: parseMode(j.mode) ?? \"safe\",\n receivers: j.receivers.map((r) =>\n r.token_in ? { ...r, token: secretGet(r.token_in, controlAccount(r.machine), run) ?? \"\" } : r,\n ),\n ...(j.limits ? { limits: j.limits } : {}),\n ...(typeof j.ask === \"boolean\" ? { ask: j.ask } : {}),\n };\n } catch {\n return null;\n }\n}\n\n/** How many receivers this machine is paired with, from control.json alone (no\n * keychain read: `status` shouldn't ask one). */\nexport function pairedCount(env: NodeJS.ProcessEnv = process.env): number {\n try {\n const j = JSON.parse(readFileSync(controlFile(env), \"utf8\")) as { receivers?: unknown };\n return Array.isArray(j.receivers) ? j.receivers.length : 0;\n } catch {\n return 0;\n }\n}\n\n/** A token kept in a store is never written back into the file. */\nexport const writeControl = (c: ControlConfig, env: NodeJS.ProcessEnv = process.env) =>\n writePrivate(controlFile(env), {\n ...c,\n receivers: c.receivers.map((r) => {\n if (!r.token_in) return r;\n const { token: _t, ...rest } = r;\n return rest;\n }),\n });\n\n/** Move every paired receiver's token into `to` (or back into control.json). A token\n * this process can't read is left where it is. */\nexport function moveControlSecrets(\n to: StoreName,\n o: { env?: NodeJS.ProcessEnv; run?: Runner } = {},\n): { moved: string[]; kept: string[] } {\n const run = o.run ?? defaultRunner;\n const cfg = readControl(o.env, run);\n const moved: string[] = [];\n const kept: string[] = [];\n if (!cfg) return { moved, kept };\n const drop: { store: SecretStore; machine: string }[] = [];\n for (const r of cfg.receivers) {\n if ((r.token_in ?? \"file\") === to) continue;\n if (!r.token) {\n kept.push(r.url);\n continue;\n }\n if (to !== \"file\" && !secretSet(to, controlAccount(r.machine), r.token, run)) {\n secretDel(to, controlAccount(r.machine), run);\n kept.push(r.url);\n continue;\n }\n if (r.token_in) drop.push({ store: r.token_in, machine: r.machine });\n if (to === \"file\") delete r.token_in;\n else r.token_in = to;\n moved.push(r.url);\n }\n writeControl(cfg, o.env);\n for (const d of drop) secretDel(d.store, controlAccount(d.machine), run);\n return { moved, kept };\n}\n\n/** Forget a receiver's token wherever it is (control off). */\nexport function dropControlSecret(r: PairedReceiver, run: Runner = defaultRunner): void {\n if (r.token_in) secretDel(r.token_in, controlAccount(r.machine), run);\n}\n\n/** Nonces the machine has run, for NONCE_TTL_MS. `seen()` records a new nonce\n * durably BEFORE it answers false; a write that fails throws, and the verifier\n * refuses the command (`nonce_store`). */\nexport class NonceStore {\n private readonly seenAt = new Map<string, number>();\n constructor(\n private readonly file: string,\n private readonly now: () => number = Date.now,\n ) {\n let raw = \"\";\n try {\n raw = readFileSync(file, \"utf8\");\n } catch {}\n const cut = this.now() - NONCE_TTL_MS;\n for (const line of raw.split(\"\\n\")) {\n if (!line) continue;\n try {\n const { n, t } = JSON.parse(line) as { n: string; t: number };\n if (typeof n === \"string\" && t > cut) this.seenAt.set(n, t);\n } catch {}\n }\n this.compact();\n }\n\n seen = (nonce: string): boolean => {\n const t = this.seenAt.get(nonce);\n if (t !== undefined && t > this.now() - NONCE_TTL_MS) return true;\n const at = this.now();\n mkdirSync(path.dirname(this.file), { recursive: true, mode: 0o700 });\n const fd = openSync(this.file, \"a\", 0o600);\n try {\n appendFileSync(fd, `${JSON.stringify({ n: nonce, t: at })}\\n`);\n fsyncSync(fd);\n } finally {\n closeSync(fd);\n }\n this.seenAt.set(nonce, at);\n return false;\n };\n\n /** Rewrite the file with only the live window (on start, and daily). */\n compact(): void {\n const cut = this.now() - NONCE_TTL_MS;\n for (const [n, t] of this.seenAt) if (t <= cut) this.seenAt.delete(n);\n if (!existsSync(this.file) && !this.seenAt.size) return;\n const body = [...this.seenAt].map(([n, t]) => JSON.stringify({ n, t })).join(\"\\n\");\n mkdirSync(path.dirname(this.file), { recursive: true, mode: 0o700 });\n writeFileSync(`${this.file}.tmp`, body ? `${body}\\n` : \"\", { mode: 0o600 });\n renameSync(`${this.file}.tmp`, this.file);\n }\n\n get size(): number {\n return this.seenAt.size;\n }\n}\n\nexport interface Ack {\n receiver: string;\n id: string;\n outcome: \"taken\" | \"delivered\" | \"expired\" | \"unsupported\" | \"failed\" | \"refused\";\n at: string;\n mode?: string;\n session?: string;\n code?: string;\n detail?: string;\n reply?: string;\n /** A start's taken and delivered: what it ran with (CONTROL.md \u00A76, Options). */\n applied?: StartOptions;\n /** This answer came from a COPY of the session the command named, not from it: the\n * person's words went somewhere other than the conversation they were looking at,\n * and they are owed that fact (CONTROL.md \u00A76 `fork`). */\n copy_of?: string;\n}\n\n/** What a session the daemon started runs with: its level and, when one was asked for,\n * its model. Recorded at the start; every later run of the session reads it. */\nexport interface StartOptions {\n /** A level, or `inherit`: started on an inherit machine with none named. */\n permission: RunLevel;\n model?: string;\n}\n\n/** A recorded entry as read back from disk, or null when it isn't one. */\nexport function startOptionsOf(v: unknown): StartOptions | null {\n const o = v as { permission?: unknown; model?: unknown } | null;\n if (!o || typeof o !== \"object\" || (levelRank(o.permission) < 0 && o.permission !== \"inherit\")) return null;\n return {\n permission: o.permission as RunLevel,\n ...(typeof o.model === \"string\" && MODEL_RE.test(o.model) ? { model: o.model } : {}),\n };\n}\n\n/** Acks not yet accepted: appended before they are sent, rewritten when sent, so a\n * daemon restart re-sends rather than forgets (every message is acked exactly once\n * with a final outcome; a duplicate is tolerated by the receiver). */\nexport class AckOutbox {\n constructor(private readonly file: string) {}\n all(): Ack[] {\n try {\n return readFileSync(this.file, \"utf8\")\n .split(\"\\n\")\n .filter(Boolean)\n .map((l) => JSON.parse(l) as Ack);\n } catch {\n return [];\n }\n }\n add(a: Ack): void {\n mkdirSync(path.dirname(this.file), { recursive: true, mode: 0o700 });\n appendFileSync(this.file, `${JSON.stringify(a)}\\n`, { mode: 0o600 });\n }\n keep(left: Ack[]): void {\n mkdirSync(path.dirname(this.file), { recursive: true, mode: 0o700 });\n writeFileSync(`${this.file}.tmp`, left.map((a) => JSON.stringify(a)).join(\"\\n\") + (left.length ? \"\\n\" : \"\"), {\n mode: 0o600,\n });\n renameSync(`${this.file}.tmp`, this.file);\n }\n}\n\n/** A permission mode as a hook payload spells it (core's CLAUDE_MODES is the list the\n * daemon acts on; this is only the shape a stored value has to keep). */\nconst MODE_SHAPE = /^[A-Za-z]{1,40}$/;\n\n/** How long a message written into a live session's own inbox keeps that lane shut for\n * it (CONTROL.md \u00A76.3): Claude Code holds an unmatched message that long before\n * dropping it, and a hold can be released, so a second write inside the window would\n * mean the person's words arriving twice. It outlives the daemon that wrote it, so a\n * restart \u2014 an update installs one \u2014 cannot turn a resend into a second frame. */\nexport const HOLD_TTL_MS = 300_000;\n\n/** A copy the daemon made because it could not reach the original (CONTROL.md \u00A76\n * `fork`), and what the person is owed about it: which session it is, when it was made\n * and why there is one at all. Daemons up to 0.10.0 wrote the id alone. */\nexport interface Copy {\n id: string;\n at: string;\n /** In the machine's own words, for the ack and the board: why a copy, not the session. */\n reason: string;\n}\n\n/** The id alone, as 0.10.0 and earlier wrote it, reads as a copy with nothing else known. */\nconst asCopy = (v: unknown): Copy | null => {\n if (typeof v === \"string\" && v) return { id: v, at: \"\", reason: \"\" };\n if (!v || typeof v !== \"object\") return null;\n const c = v as { id?: unknown; at?: unknown; reason?: unknown };\n if (typeof c.id !== \"string\" || !c.id) return null;\n return { id: c.id, at: typeof c.at === \"string\" ? c.at : \"\", reason: typeof c.reason === \"string\" ? c.reason : \"\" };\n};\n\n/** A message written into a session's own inbox that it has not taken yet\n * (CONTROL.md \u00A76.3), by the session it was written to. */\nexport interface Parked {\n /** When it was written, ISO-8601. Older than HOLD_TTL_MS is no longer a hold. */\n at: string;\n /** The message's id, so a log or a later ack can name the one that is waiting. */\n id: string;\n}\n\nexport interface DaemonState {\n /** Original session \u2192 the copy its messages go to. */\n copies: Record<string, Copy>;\n /** Claude Code sessions the daemon started \u2192 what they run with (Codex and\n * Antigravity keep theirs in the alias entry, aliases.ts). */\n started: Record<string, StartOptions>;\n /** Session ref \u2192 the permission mode its own hooks last reported (CONTROL.md \u00A76, The\n * session's mode). The daemon's own record, in its own state: no Claude Code home is\n * written to. Insertion order is the age order, which is how the oldest are dropped. */\n modes: Record<string, string>;\n /** Session ref \u2192 a message written into its own inbox that it has not taken. Read back\n * pruned: only the ones still inside the hold window. */\n parked: Record<string, Parked>;\n}\n\nexport function readState(file: string, now: number = Date.now()): DaemonState {\n const copies: Record<string, Copy> = {};\n const started: Record<string, StartOptions> = {};\n const modes: Record<string, string> = {};\n const parked: Record<string, Parked> = {};\n try {\n const j = JSON.parse(readFileSync(file, \"utf8\")) as {\n copies?: unknown;\n copies_v2?: unknown;\n started?: unknown;\n modes?: unknown;\n parked?: unknown;\n };\n if (j.started && typeof j.started === \"object\")\n for (const [k, v] of Object.entries(j.started)) {\n const o = startOptionsOf(v);\n if (o) started[k] = o;\n }\n if (j.modes && typeof j.modes === \"object\")\n for (const [k, v] of Object.entries(j.modes)) if (typeof v === \"string\" && MODE_SHAPE.test(v)) modes[k] = v;\n // A hold whose window has closed is not one: the session can no longer be handed\n // that message, so the lane is open again.\n if (j.parked && typeof j.parked === \"object\")\n for (const [k, v] of Object.entries(j.parked as Record<string, unknown>)) {\n const p = v as { at?: unknown; id?: unknown };\n const at = typeof p?.at === \"string\" ? Date.parse(p.at) : Number.NaN;\n if (Number.isFinite(at) && now - at <= HOLD_TTL_MS && typeof p.id === \"string\")\n parked[k] = { at: p.at as string, id: p.id };\n }\n // The richer shape when it is there, the plain id map when it isn't.\n const from = j.copies_v2 && typeof j.copies_v2 === \"object\" ? j.copies_v2 : j.copies;\n if (from && typeof from === \"object\")\n for (const [k, v] of Object.entries(from as Record<string, unknown>)) {\n const c = asCopy(v);\n if (c) copies[k] = c;\n }\n } catch {}\n return { copies, started, modes, parked };\n}\n\n/** Both shapes of `copies`, every time. A daemon up to 0.10.0 reads `copies` as a map of\n * plain session ids and uses what it finds there as a `--resume` target, so writing\n * only the record would make every message to a session with a copy resume\n * `[object Object]` after a rollback \u2014 and a fleet keeps the previous build precisely\n * so it can roll back. */\nexport const writeState = (file: string, s: DaemonState) =>\n writePrivate(file, {\n ...s,\n copies: Object.fromEntries(Object.entries(s.copies).map(([k, c]) => [k, c.id])),\n copies_v2: s.copies,\n });\n", "// SPDX-License-Identifier: Apache-2.0\n// The daemon's local socket (spec/CONTROL.md \u00A78, \u00A713): how the waiter, the Stop and\n// PermissionRequest hooks, the worker and `sessionpipe ask` talk to the one daemon on\n// this machine. Line-delimited JSON, one request per connection; every request but an\n// ask gets one reply, an ask gets a few in order on the same connection. A Unix socket\n// in the state directory (0600, in a 0700 dir); a per-user named pipe on Windows. Only\n// node: builtins \u2014 the hook bundle imports this.\nimport net from \"node:net\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nexport type LocalRequest =\n /** A waiter parks until a message for its session arrives. `channel`: the waiter is\n * `sessionpipe channel`, which pushes the message into the session rather than handing\n * it to a turn that asked for it (CONTROL.md \u00A78). */\n | { op: \"wait\"; session: string; pid?: number; channel?: boolean }\n /** A Stop hook asks for a queued message (turn mode). `mode` is the session's own\n * permission mode, as its payload reports it (CONTROL.md \u00A76, The session's mode). */\n | { op: \"stop\"; session: string; active: boolean; mode?: string }\n /** A PermissionRequest hook parks until a verified answer, or until it's released. */\n | { op: \"permission\"; session: string; attention: string; tool?: string }\n /** The worker reports a hook event: turn state, attention closure, and the session's\n * own permission mode where its payload named one. */\n | { op: \"event\"; session: string; event: string; transcript?: string; cwd?: string; mode?: string }\n /** `sessionpipe control status`. */\n | { op: \"status\" }\n /** `sessionpipe update` installed a new copy: restart onto it once nothing is in hand. */\n | { op: \"restart\" }\n /** `sessionpipe pool \u2026` while the daemon runs: the daemon holds the pool's engine\n * (pins, credentials, the listener), so a change goes through it. `status` reads;\n * the rest are CONTROL.md \u00A76's actions, plus `import` (reads this machine's own\n * Claude Code logins, which no receiver may ask for). */\n | { op: \"pool\"; action: PoolOp; account?: string; order?: string[]; code?: string }\n /** `sessionpipe ask` (CONTROL.md \u00A713): a session here hands a task to another agent on\n * this machine and gets the answer back on this connection. */\n | AskRequest;\n\nexport type PoolOp =\n | \"status\"\n | \"on\"\n | \"off\"\n | \"enable\"\n | \"disable\"\n | \"remove\"\n | \"order\"\n | \"refresh\"\n | \"login.begin\"\n | \"login.complete\"\n | \"import\";\n\nexport interface AskRequest {\n op: \"ask\";\n /** `<harness>:<id>` of the asking session, when known: the events go on it. */\n from?: string;\n /** The agent to ask. */\n harness: string;\n /** An existing `<harness>:<id>` to continue instead of a new session. */\n session?: string;\n /** 1\u201320 000 characters, not all whitespace. */\n text: string;\n /** Absolute; the caller's folder by default. */\n cwd: string;\n /** Every ancestor ref, outermost first (from env SESSIONPIPE_ASK_CHAIN). */\n chain: string[];\n /** Narrower than the machine's mode, never wider. */\n permission?: \"read\" | \"safe\";\n model?: string;\n pid?: number;\n}\n\nexport type LocalReply =\n | { op: \"message\"; text: string; id: string }\n | { op: \"block\"; reason: string; id: string }\n | { op: \"decision\"; behavior: \"allow\" | \"deny\"; message?: string; id: string }\n | { op: \"none\" }\n /** A newer waiter for the same session took over; this one stops. */\n | { op: \"replaced\" }\n | { op: \"ok\" }\n | { op: \"status\"; status: unknown }\n /** A pool op's answer: the ack it would earn (CONTROL.md \u00A76) and the pool as it now\n * stands (`status`: the fa\u00E7ade's PoolStatus; `detail`: the summary line, or a\n * login.begin's \"<pending id> <URL>\"). */\n | {\n op: \"pool\";\n outcome: \"delivered\" | \"refused\" | \"failed\" | \"unsupported\";\n code?: string;\n detail: string;\n status?: unknown;\n /** `import`: what was added and what was left out (control/pool.ts Imported). */\n imported?: unknown;\n }\n // An ask's replies, in order (askMany): `asked`, any `progress`, then exactly one of\n // `answer`, `refused` and `failed`, after which the daemon closes the connection.\n /** The run began; `session` once it is known. */\n | { op: \"asked\"; ask_id: string; session?: string }\n /** The answer so far, whole, with a rising seq. */\n | { op: \"progress\"; text: string; seq: number }\n | { op: \"answer\"; ask_id: string; session: string; text: string; ms: number }\n /** ask_off \u00B7 harness \u00B7 folder \u00B7 depth \u00B7 loop \u00B7 start_limit \u00B7 bad_fields */\n | { op: \"refused\"; code: string; detail: string }\n | { op: \"failed\"; detail: string };\n\n/** The replies that end an ask. */\nexport type AskEnd = Extract<LocalReply, { op: \"answer\" | \"refused\" | \"failed\" }>;\n\n/** Where the daemon listens. `SESSIONPIPE_SOCKET` overrides (tests). */\nexport function socketPath(state: string, env: NodeJS.ProcessEnv = process.env): string {\n if (env.SESSIONPIPE_SOCKET) return env.SESSIONPIPE_SOCKET;\n if (process.platform === \"win32\") return `\\\\\\\\.\\\\pipe\\\\sessionpipe-control-${os.userInfo().username}`;\n return path.join(state, \"control.sock\");\n}\n\n/** One request, one reply. `timeoutMs` bounds the whole exchange; null on any\n * failure (no daemon, a timeout, a garbled reply) \u2014 callers treat that as \"none\". */\nexport function ask(sock: string, req: LocalRequest, timeoutMs: number): Promise<LocalReply | null> {\n return new Promise((resolve) => {\n let buf = \"\";\n let done = false;\n const s = net.connect(sock);\n const finish = (r: LocalReply | null) => {\n if (done) return;\n done = true;\n clearTimeout(t);\n s.destroy();\n resolve(r);\n };\n const t = setTimeout(() => finish(null), timeoutMs);\n s.on(\"connect\", () => s.write(`${JSON.stringify(req)}\\n`));\n s.on(\"data\", (d) => {\n buf += d.toString(\"utf8\");\n const i = buf.indexOf(\"\\n\");\n if (i < 0) return;\n try {\n finish(JSON.parse(buf.slice(0, i)) as LocalReply);\n } catch {\n finish(null);\n }\n });\n s.on(\"error\", () => finish(null));\n s.on(\"close\", () => finish(null));\n });\n}\n\n/** One request, several replies on the same connection (an ask): `onReply` sees each one\n * in order, the promise settles with the one that ends the exchange. `timeoutMs` bounds\n * the whole exchange; null when it ends without an end \u2014 no daemon, a timeout, a garbled\n * line, a connection that closed mid-run. */\nexport function askMany(\n sock: string,\n req: LocalRequest,\n onReply: (r: LocalReply) => void,\n timeoutMs: number,\n): Promise<AskEnd | null> {\n return new Promise((resolve) => {\n let buf = \"\";\n let done = false;\n const s = net.connect(sock);\n const finish = (r: AskEnd | null) => {\n if (done) return;\n done = true;\n clearTimeout(t);\n s.destroy();\n resolve(r);\n };\n const t = setTimeout(() => finish(null), timeoutMs);\n s.on(\"connect\", () => s.write(`${JSON.stringify(req)}\\n`));\n s.on(\"data\", (d) => {\n buf += d.toString(\"utf8\");\n for (let i = buf.indexOf(\"\\n\"); i >= 0 && !done; i = buf.indexOf(\"\\n\")) {\n const line = buf.slice(0, i);\n buf = buf.slice(i + 1);\n let r: LocalReply;\n try {\n r = JSON.parse(line) as LocalReply;\n } catch {\n finish(null);\n return;\n }\n onReply(r);\n if (r.op === \"answer\" || r.op === \"refused\" || r.op === \"failed\") finish(r);\n }\n });\n s.on(\"error\", () => finish(null));\n s.on(\"close\", () => finish(null));\n });\n}\n", "// SPDX-License-Identifier: Apache-2.0\n// Fewer tool events per sink (issue #208). A Claude Code workflow with a few hundred\n// subagents fires two hooks per tool call, and a receiver that counts requests per\n// machine ran out of them by 2 a.m. The outbox keeps every event; a sink's `tools`\n// setting says which of them it is sent:\n// all every tool.started / tool.ended (the default: a receiver's view never changes\n// unless it asked, or the sink's owner chose)\n// main the main thread's tool events go live; a subagent's (data.agent_id set) are\n// thinned (what a receiver that meters requests asks for in its well-known\n// file, `tools: \"main\"`)\n// beat every tool event of a session is thinned\n// A thinned stream is sent tool.ended only, and of those at most one per session per\n// minute, by the events' own time: tool.ended is what a receiver reads as \"a tool ran\"\n// (spacesheep reads nothing from tool.started), so the one event kept is never one it\n// throws away. One more is always sent: the asking thread's first tool.ended after its\n// attention.needed, the event that shows the prompt was answered (PROTOCOL.md \u00A74,\n// attention.cleared). Only tool events are thinned: turns, attention, subagents, files\n// and limits go as they come.\nimport { mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { type Event, type SinkConfig, safeId } from \"@sessionpipe/core\";\n\nexport type ToolsMode = \"all\" | \"main\" | \"beat\";\nexport const TOOLS_MODES: readonly ToolsMode[] = [\"all\", \"main\", \"beat\"];\n\n/** One tool event per this window, per session, when a sink thins them. */\nexport const TOOL_FOLD_MS = 60_000;\n/** A session whose last tool event sent is this much older than the newest one's is\n * dropped from the clock file: it stays small on a machine that runs for months. */\nconst KEEP_MS = 24 * 60 * 60_000;\n\nexport function isToolsMode(v: unknown): v is ToolsMode {\n return typeof v === \"string\" && (TOOLS_MODES as readonly string[]).includes(v);\n}\n\n/** What a sink is sent: its owner's own setting, else what the receiver's well-known\n * file asked for, else every tool event. Never thinned on nobody's say-so: a receiver\n * that reads a subagent's tool calls (sessionpipe-gui does) keeps them on an upgrade. */\nexport function toolsMode(s: Pick<SinkConfig, \"url\" | \"tools\" | \"receiver_tools\">): ToolsMode {\n if (isToolsMode(s.tools)) return s.tools;\n if (isToolsMode(s.receiver_tools)) return s.receiver_tools;\n return \"all\";\n}\n\nconst agentOf = (e: Event): string => (typeof e.data.agent_id === \"string\" ? e.data.agent_id : \"\");\n/** Whether a sink in `mode` thins the tool events of the thread `agent` (\"\" = main). */\nconst thins = (mode: ToolsMode, agent: string): boolean => mode === \"beat\" || (mode === \"main\" && agent !== \"\");\n/** The clock entry that says a thread of `key` is waiting on a prompt: its next\n * tool.ended goes whatever the window says. */\nconst askKey = (key: string, agent: string): string => `${key}\\u0000ask\\u0000${agent}`;\n\n/** Whether a sink in `mode` is sent `e`. `clock` holds, per session (`key`), the time\n * of the last tool event it was sent, and moves when this one is. Times are the\n * events' own, so a backlog thins the same way it would have live. An event a little\n * out of time order (hooks reach the outbox in lock order, not hook order) falls in\n * the window like any other; only a jump back by a whole window or more (a clock set\n * back) sends and restarts it. */\nexport function sendTool(e: Event, mode: ToolsMode, clock: Record<string, number>, key: string): boolean {\n if (mode === \"all\") return true;\n const agent = agentOf(e);\n const t = Date.parse(e.time);\n if (e.type === \"attention.needed\") {\n if (thins(mode, agent)) clock[askKey(key, agent)] = Number.isFinite(t) ? t : 0;\n return true;\n }\n if ((e.type !== \"tool.started\" && e.type !== \"tool.ended\") || !thins(mode, agent)) return true;\n if (e.type === \"tool.started\") return false;\n if (!Number.isFinite(t)) return true;\n const ask = askKey(key, agent);\n // Only a tool.ended timed at or after the prompt answers it.\n const asked = clock[ask];\n if (asked !== undefined && t >= asked) {\n delete clock[ask];\n clock[key] = t;\n return true;\n }\n const last = clock[key];\n if (last !== undefined && t > last - TOOL_FOLD_MS && t < last + TOOL_FOLD_MS) return false;\n clock[key] = t;\n return true;\n}\n\n/** <state>/folds/<sink>.json: the per-session clock `sendTool` reads, beside the\n * sink's cursors. Written with them, after a batch was acknowledged, so a refused\n * batch's tool event is not taken for sent. */\nexport class ToolClock {\n constructor(\n readonly root: string,\n readonly sink: string,\n ) {}\n private file(): string {\n return path.join(this.root, \"folds\", `${safeId(this.sink)}.json`);\n }\n read(): Record<string, number> {\n try {\n const j = JSON.parse(readFileSync(this.file(), \"utf8\")) as unknown;\n return j && typeof j === \"object\" && !Array.isArray(j) ? (j as Record<string, number>) : {};\n } catch {\n return {};\n }\n }\n write(c: Record<string, number>): void {\n const newest = Math.max(0, ...Object.values(c).filter(Number.isFinite));\n const kept: Record<string, number> = {};\n for (const [k, v] of Object.entries(c)) if (Number.isFinite(v) && v >= newest - KEEP_MS) kept[k] = v;\n const f = this.file();\n mkdirSync(path.dirname(f), { recursive: true, mode: 0o700 });\n const tmp = `${f}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify(kept), { mode: 0o600 });\n renameSync(tmp, f);\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;AAKA,SAAS,YAAY,kBAAkB;AAGvC,IAAM,aAAa;AAKZ,SAAS,UAAU,QAAqC;AAC7D,SAAO,OAAO,WAAW,QAAQ,EAC9B,OAAO,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,GAAG,CAAC,EACxC,OAAO,WAAW,EAClB,MAAM,GAAG,EAAE,CAAC;AACjB;AAIO,SAAS,aAAa,QAAuB,MAAM,KAAK,IAAI,GAAW;AAC5E,MAAI,WAAW,QAAQ,OAAO,KAAK,MAAM,GAAI,QAAO;AACpD,QAAM,OAAO,OAAO,MAAM;AAC1B,MAAI,OAAO,SAAS,IAAI,EAAG,QAAO,KAAK,IAAI,GAAG,IAAI,IAAI;AACtD,QAAM,KAAK,KAAK,MAAM,MAAM;AAC5B,SAAO,OAAO,SAAS,EAAE,IAAI,KAAK,IAAI,GAAG,KAAK,GAAG,IAAI;AACvD;AAEO,IAAM,WAAN,MAA+B;AAAA,EAIpC,YACmB,KACR,MACT;AAFiB;AACR;AAET,SAAK,OAAO,IAAI;AAChB,SAAK,MAAM,CAAC,CAAC,IAAI;AACjB,SAAK,MAAM,IAAI,IAAI,QAAQ,OAAO,EAAE;AAAA,EACtC;AAAA,EANmB;AAAA,EACR;AAAA,EALF;AAAA,EACA;AAAA,EACQ;AAAA,EAUT,YAAoB;AAG1B,UAAM,KAAM,KAAK,IAA4C,WAAW,UAAU;AAClF,WAAO,eAAe,KAAK,EAAE,IAAI,KAAK,KAAK,MAAM;AAAA,EACnD;AAAA,EAEA,MAAM,KAAK,QAA0C;AACnD,UAAM,OAAO,KAAK,UAAU,EAAE,OAAO,CAAC;AACtC,UAAM,UAAkC,EAAE,gBAAgB,oBAAoB,wBAAwB,IAAI;AAC1G,QAAI,KAAK,IAAI,MAAO,SAAQ,gBAAgB,UAAU,KAAK,IAAI,KAAK;AACpE,QAAI,KAAK,IAAI,QAAQ,WAAW,QAAQ,GAAG;AACzC,YAAM,KAAK,UAAU,MAAM;AAC3B,YAAM,KAAK,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACvC,YAAM,MAAM,OAAO,KAAK,KAAK,IAAI,OAAO,MAAM,CAAC,GAAG,QAAQ;AAC1D,YAAM,MAAM,WAAW,UAAU,GAAG,EAAE,OAAO,GAAG,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,EAAE,OAAO,QAAQ;AACnF,cAAQ,YAAY,IAAI;AACxB,cAAQ,mBAAmB,IAAI,OAAO,EAAE;AACxC,cAAQ,mBAAmB,IAAI,MAAM,GAAG;AAAA,IAC1C;AACA,UAAM,MAAM,IAAI,gBAAgB;AAChC,UAAM,IAAI,WAAW,MAAM,IAAI,MAAM,GAAG,UAAU;AAClD,QAAI;AACF,YAAM,IAAI,MAAM,MAAM,KAAK,UAAU,GAAG,EAAE,QAAQ,QAAQ,SAAS,MAAM,QAAQ,IAAI,OAAO,CAAC;AAC7F,YAAM,OAAO,MAAM,EAAE,KAAK;AAC1B,UAAI,IAA6B,CAAC;AAClC,UAAI;AACF,YAAI,OAAQ,KAAK,MAAM,IAAI,IAAgC,CAAC;AAAA,MAC9D,QAAQ;AAAA,MAAC;AACT,UAAI,EAAE,WAAW,OAAO,EAAE,WAAW,KAAK;AACxC,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,cAAc,OAAO;AAAA,UACrB,UAAU,OAAO,EAAE,YAAY,OAAO,MAAM;AAAA,UAC5C,YAAY,OAAO,EAAE,cAAc,CAAC;AAAA,UACpC,UAAU,MAAM,QAAQ,EAAE,QAAQ,IAAK,EAAE,WAAgD,CAAC;AAAA,UAC1F,GAAI,OAAO,EAAE,aAAa,WAAW,EAAE,UAAU,EAAE,SAAiB,IAAI,CAAC;AAAA,UACzE,QAAQ,EAAE;AAAA,QACZ;AAAA,MACF;AACA,YAAM,MAAM,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS,KAAK,MAAM,GAAG,GAAG;AACvE,UAAI,EAAE,WAAW,IAAK,QAAO,EAAE,IAAI,OAAO,cAAc,GAAG,QAAQ,KAAK,OAAO,KAAK,QAAQ,OAAO;AACnG,UAAI,EAAE,WAAW,IAAK,QAAO,EAAE,IAAI,OAAO,cAAc,GAAG,QAAQ,KAAK,OAAO,KAAK,QAAQ,QAAQ;AACpG,UAAI,EAAE,WAAW;AACf,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,cAAc;AAAA,UACd,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,GAAI,OAAO,EAAE,aAAa,WAAW,EAAE,UAAU,EAAE,SAAiB,IAAI,CAAC;AAAA,QAC3E;AACF,UAAI,EAAE,WAAW,IAAK,QAAO,EAAE,IAAI,OAAO,cAAc,GAAG,QAAQ,KAAK,OAAO,KAAK,QAAQ,QAAQ;AACpG,UAAI,EAAE,WAAW;AACf,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,cAAc;AAAA,UACd,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,cAAc,aAAa,EAAE,QAAQ,IAAI,aAAa,CAAC;AAAA,QACzD;AACF,aAAO,EAAE,IAAI,OAAO,cAAc,GAAG,QAAQ,EAAE,QAAQ,OAAO,KAAK,QAAQ,QAAQ;AAAA,IACrF,SAAS,GAAG;AACV,aAAO,EAAE,IAAI,OAAO,cAAc,GAAG,OAAO,OAAQ,EAAY,WAAW,CAAC,GAAG,QAAQ,QAAQ;AAAA,IACjG,UAAE;AACA,mBAAa,CAAC;AAAA,IAChB;AAAA,EACF;AACF;;;AC9GA;AAAA,EACE,kBAAAA;AAAA,EACA,cAAAC;AAAA,EACA,aAAAC;AAAA,EACA,eAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,cAAAC;AAAA,EACA,YAAAC;AAAA,EACA,cAAAC;AAAA,EACA,iBAAAC;AAAA,OACK;AACP,OAAOC,SAAQ;AACf,OAAOC,YAAU;;;ACTjB,OAAO,QAAQ;AACf,OAAO,UAAU;AAIV,IAAM,UAAU,MAAc,GAAG,QAAQ;AAEzC,SAAS,WAAW,MAAyB,QAAQ,KAAa;AACvE,MAAI,IAAI,mBAAoB,QAAO,KAAK,QAAQ,IAAI,kBAAkB;AACtE,MAAI,QAAQ,aAAa;AACvB,WAAO,KAAK,KAAK,IAAI,WAAW,KAAK,KAAK,QAAQ,GAAG,WAAW,SAAS,GAAG,eAAe,aAAa;AAC1G,SAAO,KAAK,KAAK,IAAI,mBAAmB,KAAK,KAAK,QAAQ,GAAG,SAAS,GAAG,eAAe,aAAa;AACvG;AAEO,SAAS,SAAS,MAAyB,QAAQ,KAAa;AACrE,MAAI,IAAI,kBAAmB,QAAO,KAAK,QAAQ,IAAI,iBAAiB;AACpE,MAAI,QAAQ,aAAa;AACvB,WAAO,KAAK,KAAK,IAAI,gBAAgB,KAAK,KAAK,QAAQ,GAAG,WAAW,OAAO,GAAG,aAAa;AAC9F,SAAO,KAAK,KAAK,IAAI,kBAAkB,KAAK,KAAK,QAAQ,GAAG,UAAU,OAAO,GAAG,aAAa;AAC/F;AAGO,SAAS,MAAM,GAAW,OAAe,QAAQ,GAAW;AACjE,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,MAAM,KAAM,QAAO;AACvB,SAAO,EAAE,WAAW,OAAO,KAAK,GAAG,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,KAAK;AAClG;AAWO,IAAM,SAAS,CAAC,MACrB,OAAO,CAAC,EACL,QAAQ,oBAAoB,GAAG,EAC/B,MAAM,GAAG,GAAG;;;AClCjB,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,cAAAC,aAAY,eAAAC,cAAa,gBAAAC,eAAc,YAAAC,iBAAgB;AAChE,SAAS,gBAAgB;AACzB,OAAOC,WAAU;;;ACbjB,SAAS,WAAW,WAAW,UAAU,aAAa,UAAU,gBAAgB;AAChF,OAAOC,WAAU;AAGV,SAAS,KAAK,MAAc,WAAmB,WAAuD;AAC3G,MAAI;AACJ,MAAI;AACF,SAAK,SAAS,MAAM,GAAG;AACvB,UAAM,OAAO,UAAU,EAAE,EAAE;AAC3B,UAAM,OAAO,CAAC,KAAa,QAAgB;AACzC,YAAM,IAAI,OAAO,MAAM,GAAG;AAC1B,YAAM,IAAI,SAAS,IAAc,GAAG,GAAG,KAAK,GAAG;AAC/C,aAAO,EAAE,SAAS,GAAG,CAAC,EAAE,SAAS,MAAM;AAAA,IACzC;AACA,UAAM,OAAO,KAAK,GAAG,KAAK,IAAI,MAAM,SAAS,CAAC;AAC9C,UAAM,OAAO,OAAO,YAAY,KAAK,KAAK,IAAI,GAAG,OAAO,SAAS,GAAG,KAAK,IAAI,MAAM,SAAS,CAAC,IAAI;AACjG,WAAO,EAAE,MAAM,KAAK,MAAM,IAAI,EAAE,MAAM,GAAG,EAAE,GAAG,MAAM,KAAK,MAAM,IAAI,EAAE,MAAM,OAAO,YAAY,IAAI,CAAC,EAAE;AAAA,EACvG,QAAQ;AACN,WAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,EAAE;AAAA,EAC9B,UAAE;AACA,QAAI,OAAO;AACT,UAAI;AACF,kBAAU,EAAE;AAAA,MACd,QAAQ;AAAA,MAAC;AAAA,EACb;AACF;AAEO,IAAM,aAAa,CAAC,UACzB,MACG,IAAI,CAAC,MAAM;AACV,MAAI;AACF,WAAO,KAAK,MAAM,CAAC;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACF,CAAC,EACA,OAAO,CAAC,MAAoC,CAAC,CAAC,CAAC;AAG7C,SAAS,UAAa,MAAc,OAAmCC,OAAM,KAAK,OAAO,MAAgB;AAC9G,MAAI;AACJ,MAAI;AACF,SAAK,SAAS,MAAM,GAAG;AACvB,UAAM,MAAM,OAAO,MAAM,OAAO,IAAI;AACpC,QAAI,MAAM;AACV,QAAI,OAAe,OAAO,MAAM,CAAC;AACjC,WAAO,MAAMA,MAAK;AAChB,YAAM,IAAI,SAAS,IAAI,KAAK,GAAG,IAAI,QAAQ,GAAG;AAC9C,UAAI,CAAC,EAAG;AACR,aAAO;AACP,UAAI,QAAQ,OAAO,OAAO,CAAC,MAAM,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;AACpD,UAAI,KAAK,MAAM,QAAQ,EAAE;AACzB,aAAO,MAAM,GAAG;AACd,cAAM,IAAI,MAAM,MAAM,SAAS,GAAG,EAAE,EAAE,SAAS,MAAM,CAAC;AACtD,YAAI,MAAM,KAAM,QAAO;AACvB,gBAAQ,MAAM,SAAS,KAAK,CAAC;AAC7B,aAAK,MAAM,QAAQ,EAAE;AAAA,MACvB;AACA,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EACR,UAAE;AACA,QAAI,OAAO;AACT,UAAI;AACF,kBAAU,EAAE;AAAA,MACd,QAAQ;AAAA,MAAC;AAAA,EACb;AACA,SAAO;AACT;AAEO,SAAS,YAAY,MAAc,OAAe,SAAiB,OAA4C;AACpH,QAAM,MAAgB,CAAC;AACvB,QAAM,OAAO,CAAC,KAAa,MAAc;AACvC,QAAI,QAAoC,CAAC;AACzC,QAAI;AACF,cAAQ,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,IAClD,QAAQ;AACN;AAAA,IACF;AACA,eAAW,KAAK,OAAO;AACrB,YAAM,IAAID,MAAK,KAAK,KAAK,EAAE,IAAI;AAC/B,UAAI,EAAE,YAAY,GAAG;AACnB,YAAI,IAAI,MAAO,MAAK,GAAG,IAAI,CAAC;AAC5B;AAAA,MACF;AACA,UAAI,CAAC,MAAM,EAAE,IAAI,EAAG;AACpB,UAAI;AACF,YAAI,SAAS,CAAC,EAAE,WAAW,QAAS,KAAI,KAAK,CAAC;AAAA,MAChD,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF;AACA,OAAK,MAAM,CAAC;AACZ,SAAO;AACT;;;AC5FA,SAAS,oBAAoB;AAE7B,IAAM,QAAQ,oBAAI,IAAgD;AAE3D,SAAS,SAAS,KAA6D;AACpF,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,QAAM,MAAM,MAAM,IAAI,GAAG;AACzB,MAAI,IAAK,QAAO;AAChB,QAAM,MAAM,CAAC,SAAmB;AAC9B,QAAI;AACF,aAAO,aAAa,OAAO,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG;AAAA,QAC/C,UAAU;AAAA,QACV,SAAS;AAAA,QACT,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,MACpC,CAAC,EAAE,KAAK;AAAA,IACV,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,MAA0C,CAAC;AACjD,QAAM,OAAO,IAAI,CAAC,UAAU,WAAW,QAAQ,CAAC;AAChD,MAAI,KAAM,KAAI,OAAO,iBAAiB,IAAI;AAC1C,QAAM,SAAS,IAAI,CAAC,aAAa,gBAAgB,MAAM,CAAC;AACxD,MAAI,UAAU,WAAW,OAAQ,KAAI,SAAS;AAC9C,QAAM,IAAI,KAAK,GAAG;AAClB,SAAO;AACT;AAGO,SAAS,iBAAiB,KAAqB;AAEpD,QAAM,MAAM,iDAAiD,KAAK,GAAG;AACrE,MAAI,IAAK,QAAO,GAAG,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;AACnC,SAAO,IACJ,QAAQ,eAAe,IAAI,EAC3B,QAAQ,gBAAgB,EAAE,EAC1B,QAAQ,UAAU,EAAE;AACzB;;;ACnCO,SAAS,cAAc,MAA2D;AACvF,MAAI,MAAM,SAAS,eAAgB,QAAO;AAC1C,QAAM,IAAI,KAAK;AACf,SAAO,CAAC,CAAC,GAAG,QAAQ;AACtB;AAIO,SAAS,oBAAoB,MAAkD;AACpF,MAAI,MAAM,SAAS,eAAgB,QAAO;AAC1C,QAAM,IAAI,KAAK;AACf,MAAI,CAAC,KAAM,CAAC,EAAE,kBAAkB,CAAC,EAAE,QAAQ,SAAW,QAAO;AAC7D,SAAO,CAAC,WACN,OAAO,EAAE,OAAO,YAChB,CAAC,CAAC,EAAE,MACJ,QAAQ,SAAS,eAChB,OAAO,SAAiD,cAAc,EAAE;AAC7E;;;ACFO,SAAS,UAAU,MAAa,SAAiC;AACtE,QAAM,QAOA,CAAC;AACP,MAAI,MAAqC;AACzC,aAAW,KAAK,MAAM;AACpB,QAAI,EAAE,SAAS,QAAQ;AAGrB,UAAI,OAAO,CAAC,IAAI,WAAW;AACzB,YAAI,QAAQ;AAAA;AAAA,EAAO,EAAE,IAAI;AACzB;AAAA,MACF;AACA,UAAI,IAAK,OAAM,KAAK,GAAG;AACvB,YAAM;AAAA,QACJ,MAAM,EAAE;AAAA,QACR,WAAW;AAAA,QACX,IAAI,EAAE;AAAA,QACN,WAAW,EAAE;AAAA,QACb,SAAS,EAAE;AAAA,QACX,GAAI,EAAE,WAAW,EAAE,UAAU,KAAc,IAAI,CAAC;AAAA,MAClD;AAAA,IACF,OAAO;AACL,UAAI,CAAC,IAAK,OAAM,EAAE,MAAM,IAAI,WAAW,IAAI,IAAI,EAAE,IAAI,WAAW,EAAE,MAAM,SAAS,EAAE,KAAK;AACxF,UAAI,cAAc,IAAI,YAAY,SAAS,MAAM,EAAE;AACnD,UAAI,UAAU,EAAE;AAAA,IAClB;AAAA,EACF;AACA,MAAI,KAAK,UAAW,OAAM,KAAK,GAAG;AAElC,QAAM,OAAO,OAAO,CAAC,IAAI,YAAY,IAAI,YAAY;AACrD,QAAM,MAAsB;AAAA,IAC1B,OAAO,MAAM,IAAI,CAAC,OAAO;AAAA,MACvB,MAAM,EAAE;AAAA,MACR,WAAW,EAAE;AAAA,MACb,IAAI,EAAE;AAAA,MACN,SAAS,EAAE;AAAA,MACX,GAAI,EAAE,WAAW,EAAE,UAAU,KAAc,IAAI,CAAC;AAAA,IAClD,EAAE;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,CAAC,IAAI,aAAa,IAAI;AAC/B,QAAI,OAAO,EAAE,MAAM,IAAI,MAAM,IAAI,IAAI,IAAI,GAAI,IAAI,WAAW,EAAE,UAAU,KAAc,IAAI,CAAC,EAAG;AAChG,SAAO;AACT;AAKO,IAAM,aAAa,CAAC,SACxB,2BAA2B,KAAK,IAAI,KAAK,oBAAoB,KAAK,IAAI,KACvE,UAAU,KAAK,IAAI,KACnB,aAAa,KAAK,IAAI;AAExB,IAAM,QAAQ,CAAC,QAAgD;AAC7D,MAAI;AACF,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAIA,IAAM,QAAQ,CAAC,UAAoB;AACjC,QAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,MAAI,SAAS,OAAW,QAAO;AAC/B,SAAO,SAAS,MAAM,MAAM,IAAI,MAAM,OAAO,MAAM,SAAS,IAAI,MAAM;AACxE;AACA,IAAM,YAAY,CAAC,GAAY,UAA4B;AACzD,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,MAAI,CAAC,MAAM,QAAQ,CAAC,EAAG,QAAO;AAC9B,SAAO,EACJ;AAAA,IACC,CAAC,MACC,KACA,OAAO,MAAM,YACb,MAAM,SAAU,EAAwB,QAAQ,EAAE,KAClD,OAAQ,EAAyB,SAAS;AAAA,EAC9C,EACC,IAAI,CAAC,MAAO,EAAuB,IAAI,EACvC,KAAK,IAAI;AACd;AAOA,IAAM,SAAS,oBAAI,IAAI,CAAC,SAAS,MAAM,CAAC;AAKxC,IAAM,aAAa;AACnB,IAAM,aAAa;AAEnB,IAAM,WAAW;AAMV,SAAS,QAAQ,QAAiB,MAAsB;AAC7D,QAAM,OAAQ,QAA2C;AACzD,MAAI,IAAI,OAAO,SAAS,YAAY,KAAK,KAAK,IAAI,OAAO;AACzD,MAAI,MAAM,MAAM;AACd,UAAM,KAAK,EAAE,QAAQ,IAAI;AACzB,QAAI,KAAK,KAAK,WAAW,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC,EAAG,KAAI,EAAE,MAAM,KAAK,CAAC;AACjE,UAAM,QAAQ,EAAE,QAAQ,UAAU;AAClC,QAAI,QAAQ,EAAG,KAAI,EAAE,MAAM,GAAG,KAAK;AAAA,EACrC;AACA,UAAQ,SAAS,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK;AAClD;AAQA,IAAM,eAAe,CAAC,MAAuB;AAC3C,MAAI,CAAC,KAAK,OAAO,MAAM,SAAU,QAAO;AACxC,QAAM,IAAI;AACV,MAAI,EAAE,SAAS,qBAAqB,EAAE,eAAe,cAAc,SAAU,QAAO;AAEpF,QAAM,OAAO,EAAE,QAAQ,QAAQ;AAC/B,MAAI,CAAC,OAAO,IAAI,IAAI,EAAG,QAAO;AAC9B,QAAM,OAAO,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK;AAChD,SAAO,SAAS,SAAS,QAAQ,EAAE,QAAQ,IAAI,IAAI;AACrD;AAMA,IAAM,cAAc,CAAC,SAAyB,yCAAyC,KAAK,IAAI,IAAI,CAAC,GAAG,KAAK,KAAK;AAI3G,SAAS,WAAW,OAAiB,MAA8B;AACxE,QAAM,OAAc,CAAC;AACrB,WAAS,IAAI,MAAM,IAAI,MAAM,QAAQ,KAAK;AACxC,UAAM,IAAI,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,CAAW,IAAI;AACjD,QAAI,CAAC,KAAK,EAAE,YAAa;AACzB,UAAM,SAAS,EAAE;AAMjB,UAAM,UAAU,EAAE,SAAS,UAAU,QAAQ,SAAS;AACtD,UAAM,OAAO,QAAQ,SAAS;AAC9B,QAAI,EAAE,SAAS,WAAW,EAAE,oBAAoB,EAAE,uBAAwB;AAC1E,QAAI,EAAE,SAAS,UAAU,CAAC,WAAW,CAAC,SAAS,EAAE,UAAW,QAAQ,QAAQ,OAAO,SAAS,SAAW;AACvG,UAAM,KAAK,KAAK,MAAM,OAAO,EAAE,aAAa,EAAE,CAAC,KAAK,KAAK,IAAI;AAC7D,UAAM,IAAK,EAAE,SAA+C;AAC5D,QAAI,SAAS;AAEX,YAAM,OAAO,YAAY,UAAU,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC;AACtD,UAAI,KAAM,MAAK,KAAK,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,GAAG,UAAU,KAAK,CAAC;AAAA,IACzE,WAAW,EAAE,SAAS,QAAQ;AAC5B,YAAM,MAAM,UAAU,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK;AACxC,YAAM,OAAO,OAAO,QAAQ,QAAQ,GAAG,IAAI;AAK3C,UAAI,CAAC,QAAQ,WAAW,IAAI,EAAG;AAC/B,WAAK,KAAK,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,GAAG,GAAI,OAAO,EAAE,UAAU,KAAc,IAAI,CAAC,EAAG,CAAC;AAAA,IAC7F,WAAW,EAAE,SAAS,eAAe,MAAM,QAAQ,CAAC,GAAG;AACrD,YAAM,OAAO,UAAU,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK;AACzC,UAAI,KAAM,MAAK,KAAK,EAAE,MAAM,aAAa,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,IAC9D,WAAW,EAAE,SAAS,cAAc;AAClC,YAAM,OAAO,aAAa,EAAE,UAAU;AACtC,UAAI,QAAQ,CAAC,WAAW,IAAI,EAAG,MAAK,KAAK,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,IAC9E;AAAA,EACF;AACA,SAAO,UAAU,MAAM,MAAM,KAAK,CAAC;AACrC;AAKO,SAAS,cAAc,MAAuB;AACnD,SAAO,eAAe,KAAK,IAAI,KAAK,gCAAgC,KAAK,IAAI;AAC/E;AAGO,SAAS,UAAU,OAAiB,MAA8B;AACvE,QAAM,OAAc,CAAC;AACrB,QAAM,WAAW,oBAAoB,MAAM,MAAM,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC;AAGnF,QAAM,QAAQ,WAAW,MAAM,UAAU,CAAC,SAAS,SAAS,MAAM,IAAI,CAAC,CAAC,IAAI;AAC5E,MAAI,QAAQ,EAAG,QAAO,UAAU,MAAM,MAAM,KAAK,CAAC;AAClD,WAAS,IAAI,KAAK,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,QAAQ,KAAK;AACzD,UAAM,IAAI,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,CAAW,IAAI;AACjD,QAAI,CAAC,KAAK,EAAE,SAAS,gBAAiB;AACtC,UAAM,IAAI,EAAE;AACZ,QAAI,CAAC,KAAK,EAAE,SAAS,UAAW;AAChC,UAAM,KAAK,KAAK,MAAM,OAAO,EAAE,aAAa,EAAE,CAAC,KAAK,KAAK,IAAI;AAC7D,UAAM,OAAO,UAAU,EAAE,SAAS,CAAC,cAAc,aAAa,CAAC,EAAE,KAAK;AACtE,QAAI,CAAC,KAAM;AACX,QAAI,EAAE,SAAS,QAAQ;AACrB,UAAI,cAAc,IAAI,EAAG;AACzB,WAAK,KAAK,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,IAC/C,WAAW,EAAE,SAAS,YAAa,MAAK,KAAK,EAAE,MAAM,aAAa,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,EACvF;AACA,SAAO,UAAU,MAAM,MAAM,KAAK,CAAC;AACrC;AAGO,SAAS,WAAW,OAAiB,MAA8B;AACxE,QAAM,OAAc,CAAC;AACrB,WAAS,IAAI,MAAM,IAAI,MAAM,QAAQ,KAAK;AACxC,UAAM,IAAI,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,CAAW,IAAI;AACjD,QAAI,CAAC,EAAG;AACR,UAAM,OAAO,EAAE,SAAS,SAAS,SAAS,EAAE,SAAS,YAAY,EAAE,SAAS,UAAU,cAAc;AACpG,QAAI,CAAC,KAAM;AACX,UAAM,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE;AACxC,UAAM,QAAQ,OAAO,QAAQ,WAAW,MAAM,UAAU,KAAK,CAAC,MAAM,CAAC,GAAG,KAAK;AAC7E,QAAI,CAAC,KAAM;AACX,UAAM,KAAK,KAAK,MAAM,OAAO,EAAE,aAAa,EAAE,CAAC,KAAK,KAAK,IAAI;AAC7D,SAAK,KAAK,EAAE,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,EACvC;AACA,SAAO,UAAU,MAAM,MAAM,KAAK,CAAC;AACrC;AAGO,SAAS,gBAAgB,OAAiB,MAA8B;AAC7E,QAAM,OAAc,CAAC;AACrB,WAAS,IAAI,MAAM,IAAI,MAAM,QAAQ,KAAK;AACxC,UAAM,IAAI,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,CAAW,IAAI;AACjD,QAAI,CAAC,KAAK,OAAO,EAAE,YAAY,SAAU;AACzC,UAAM,KAAK,KAAK,MAAM,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC,KAAK,KAAK,IAAI;AAC7E,QAAI,EAAE,SAAS,cAAc;AAC3B,YAAM,IAAI,iDAAiD,KAAK,EAAE,OAAO;AACzE,YAAM,QAAQ,IAAK,EAAE,CAAC,IAAe,EAAE,SAAS,KAAK;AACrD,UAAI,KAAM,MAAK,KAAK,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,IACzD,WAAW,EAAE,SAAS,oBAAoB;AACxC,YAAM,OAAO,EAAE,QAAQ,KAAK;AAC5B,UAAI,KAAM,MAAK,KAAK,EAAE,MAAM,aAAa,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,IAC9D;AAAA,EACF;AACA,SAAO,UAAU,MAAM,MAAM,KAAK,CAAC;AACrC;;;ACnQA,SAAS,YAAY,WAAW,cAAc,YAAY,qBAAqB;AAC/E,OAAOE,WAAU;AAiBV,IAAM,OAAO;AACb,IAAM,SAAS,CAAC,MACrB,MAAM,QAAQ,GAAG,KAAK,KAAK,EAAE,MAAM,KAAK,CAAC,MAAM,OAAO,EAAE,YAAY,YAAY,KAAK,KAAK,EAAE,OAAO,CAAC;AA2B/F,SAAS,aAAa,MAAwB;AACnD,MAAI;AACJ,MAAI;AACF,UAAM,aAAa,MAAM,MAAM;AAAA,EACjC,QAAQ;AACN,WAAO,EAAE,IAAI,MAAM,SAAS,OAAO,OAAO,CAAC,GAAG,KAAK,IAAI,QAAQ,MAAM,KAAK,MAAM,cAAc,MAAM,KAAK,MAAM;AAAA,EACjH;AAGA,QAAM,MAAM,IAAI,WAAW,CAAC,MAAM;AAClC,MAAI,IAAK,OAAM,IAAI,MAAM,CAAC;AAC1B,MAAI;AACJ,MAAI;AACF,YAAQ,KAAK,MAAM,GAAG;AAAA,EACxB,QAAQ;AACN,UAAM,OAAO,iBAAiB,KAAK,GAAG,IAAI,qCAAqC;AAC/E,WAAO,EAAE,IAAI,OAAO,SAAS,MAAM,QAAQ,KAAK;AAAA,EAClD;AACA,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK;AAC5D,WAAO,EAAE,IAAI,OAAO,SAAS,MAAM,QAAQ,oBAAoB;AACjE,QAAM,MAAM,IAAI,SAAS,MAAM,IAAI,SAAS;AAC5C,QAAM,IAAI,eAAe,KAAK,GAAG;AACjC,QAAM,SAAS,IAAI,CAAC,KAAK;AACzB,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,SAAS,KAAK,GAAG;AAAA,IAC/B;AAAA,EACF;AACF;AAGO,SAAS,WACd,OACA,GACQ;AACR,QAAM,OAAO,KAAK,UAAU,OAAO,MAAM,EAAE,MAAM,EAAE,QAAQ,OAAO,EAAE,GAAG;AACvE,UAAQ,EAAE,MAAM,WAAW,OAAO,EAAE,eAAe,OAAO,EAAE,MAAM;AACpE;AAGO,SAAS,SAAS,MAAuC;AAC9D,QAAM,IAAI,aAAa,IAAI;AAC3B,SAAO,EAAE,KAAK,EAAE,QAAQ,CAAC;AAC3B;AASA,SAAS,QACP,MACA,GACA,MACA,SACkC;AAClC,QAAM,EAAE,MAAM,QAAQ,IAAI;AAC1B,QAAM,MAAM,SAAS,IAAI,QAAQ,QAAQ,OAAO,CAAC,CAAC;AAClD,MAAI,SAAS,YAAY,YAAY,UAAU,YAAY;AACzD,WAAO,EAAE,MAAM,SAAS,OAAO,SAAS,MAAM,MAAM,GAAG,GAAG,gBAAgB;AAC5E,SAAO;AAAA,IACL;AAAA,IACA,SAAS;AAAA,IACT,SAAS;AAAA,IACT,GAAI,UAAU,CAAC,IAAI,EAAE,SAAS,KAAK;AAAA,IACnC,MAAM,GAAG,GAAG;AAAA,EACd;AACF;AAMO,SAAS,UAAU,MAAc,MAAc,UAAU,MAA4B;AAC1F,MAAI;AACF,cAAUA,MAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACjD,kBAAc,MAAM,IAAI;AACxB,WAAO;AAAA,EACT,SAAS,GAAG;AACV,WAAO,QAAQ,MAAM,GAAG,SAAS,OAAO;AAAA,EAC1C;AACF;AAMO,SAAS,cAAc,MAAc,OAA8B;AACxE,MAAI;AACF,eAAW,IAAI;AACf,WAAO,EAAE,MAAM,SAAS,MAAM,MAAM,mCAAmC;AAAA,EACzE,SAAS,GAAG;AACV,UAAM,MAAM,QAAQ,MAAM,GAAG,UAAU,IAAI;AAC3C,WACE,UAAU,MAAM,KAAK,KAAK;AAAA,MACxB;AAAA,MACA,SAAS;AAAA,MACT,MAAM,YAAY,IAAI,KAAK,QAAQ,kBAAkB,EAAE,CAAC;AAAA,IAC1D;AAAA,EAEJ;AACF;AAEA,SAAS,SAAS,OAAe,SAAiB,KAAkB,GAAwB;AAC1F,QAAM,UAAU,EAAE,WAAW,KAAK;AAClC,SAAO;AAAA,IACL,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC3C,OAAO,CAAC,EAAE,MAAM,WAAW,SAAS,IAAI,CAAC,SAAS,KAAK,CAAC,EAAE,SAAS,SAAS,EAAE,aAAa,KAAK,KAAK,EAAE,QAAQ,CAAC;AAAA,EAClH;AACF;AAEO,SAAS,oBACd,MACA,SACA,QACA,KACA,GACe;AACf,QAAM,IAAI,aAAa,IAAI;AAC3B,MAAI,CAAC,EAAE,GAAI,QAAO,EAAE,MAAM,SAAS,OAAO,SAAS,MAAM,MAAM,GAAG,EAAE,MAAM,gBAAgB;AAC1F,QAAMC,YAAW,EAAE;AACnB,QAAM,QAASA,UAAS,SAAS,OAAOA,UAAS,UAAU,WAAWA,UAAS,QAAQ,CAAC;AAIxF,MAAI,UAAU;AACd,QAAM,OAAO,IAAI,IAAI,MAAM;AAE3B,aAAW,MAAM,OAAO,KAAK,KAAK,GAAG;AACnC,QAAI,KAAK,IAAI,EAAE,EAAG;AAClB,UAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,CAAC,IAAI,MAAM,EAAE,IAAI,CAAC;AACrD,UAAM,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,OAAO,CAAU,CAAC;AACnD,QAAI,KAAK,WAAW,KAAK,QAAQ;AAC/B,gBAAU;AACV,UAAI,KAAK,OAAQ,OAAM,EAAE,IAAI;AAAA,UACxB,QAAO,MAAM,EAAE;AAAA,IACtB;AAAA,EACF;AACA,aAAW,MAAM,QAAQ;AACvB,UAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,CAAC,IAAI,MAAM,EAAE,IAAI,CAAC;AACrD,UAAMC,SAAQ,SAAS,IAAI,SAAS,KAAK,CAAC;AAC1C,UAAM,OAAO,KAAK,OAAO,CAAC,MAAM,OAAO,CAAU,CAAC;AAClD,QAAI,KAAK,WAAW,KAAK,KAAK,UAAU,KAAK,CAAC,CAAC,MAAM,KAAK,UAAUA,MAAK,EAAG;AAC5E,UAAM,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,OAAO,CAAU,CAAC,EAAE,OAAO,CAACA,MAAK,CAAC;AAClE,cAAU;AAAA,EACZ;AACA,MAAI,EAAE;AACJ,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,EAAE,GAAG;AACvC,UAAID,UAAS,CAAC,MAAM,GAAG;AACrB,QAAAA,UAAS,CAAC,IAAI;AACd,kBAAU;AAAA,MACZ;AAAA;AACJ,MAAI,SAAS;AACX,IAAAA,UAAS,QAAQ;AACjB,UAAM,MAAM,UAAU,MAAM,WAAWA,WAAU,CAAC,GAAG,EAAE,OAAO;AAC9D,QAAI,IAAK,QAAO;AAAA,EAClB;AACA,SAAO,EAAE,MAAM,SAAS,SAAS,CAAC,EAAE,WAAW,QAAQ;AACzD;AAEO,SAAS,sBACd,MACA,QACA,OAA0C,CAAC,GAC5B;AACf,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO,EAAE,MAAM,SAAS,MAAM;AACrD,QAAM,IAAI,aAAa,IAAI;AAC3B,MAAI,CAAC,EAAE,GAAI,QAAO,EAAE,MAAM,SAAS,OAAO,SAAS,MAAM,MAAM,GAAG,EAAE,MAAM,gBAAgB;AAC1F,QAAMA,YAAW,EAAE;AACnB,QAAM,QAAQA,UAAS;AACvB,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO,EAAE,MAAM,SAAS,MAAM;AACvE,MAAI,UAAU;AACd,aAAW,MAAM,CAAC,GAAG,QAAQ,GAAG,OAAO,KAAK,KAAK,CAAC,GAAG;AACnD,UAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,CAAC,IAAI,MAAM,EAAE,IAAI,CAAC;AACrD,UAAM,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,OAAO,CAAU,CAAC;AACnD,QAAI,KAAK,WAAW,KAAK,QAAQ;AAC/B,gBAAU;AACV,UAAI,KAAK,OAAQ,OAAM,EAAE,IAAI;AAAA,UACxB,QAAO,MAAM,EAAE;AAAA,IACtB;AAAA,EACF;AACA,MAAI,CAAC,QAAS,QAAO,EAAE,MAAM,SAAS,MAAM;AAC5C,MAAI,CAAC,OAAO,KAAK,KAAK,EAAE,OAAQ,QAAOA,UAAS;AAChD,MAAI,KAAK,WAAW,CAAC,OAAO,KAAKA,SAAQ,EAAE,OAAQ,QAAO,cAAc,MAAM,WAAWA,WAAU,CAAC,CAAC;AACrG,SAAO,UAAU,MAAM,WAAWA,WAAU,CAAC,CAAC,KAAK,EAAE,MAAM,SAAS,KAAK;AAC3E;AAEO,SAAS,sBACd,MACA,SACA,QACA,KACA,GACiC;AACjC,QAAM,IAAI,aAAa,IAAI;AAC3B,QAAM,QAAS,EAAE,KAAM,EAAE,MAAM,SAAS,CAAC,IAAK,CAAC;AAC/C,MAAI,UAAU;AACd,MAAI,UAAU;AACd,aAAW,MAAM,QAAQ;AACvB,UAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,CAAC,IAAI,MAAM,EAAE,IAAI,CAAC;AACrD,UAAM,OAAO,KAAK,OAAO,CAAC,MAAM,OAAO,CAAU,CAAC;AAClD,QAAI,KAAK,OAAQ;AACjB,QAAI,KAAK,WAAW,KAAK,KAAK,UAAU,KAAK,CAAC,CAAC,MAAM,KAAK,UAAU,SAAS,IAAI,SAAS,KAAK,CAAC,CAAC,EAAG;AAAA,EACtG;AAEA,aAAW,MAAM,OAAO,KAAK,KAAK;AAChC,QAAI,CAAC,OAAO,SAAS,EAAE,MAAM,MAAM,QAAQ,MAAM,EAAE,CAAC,IAAI,MAAM,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,OAAO,CAAU,CAAC;AACpG,aAAO;AACX,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,YAAY,OAAO,SAAS,YAAY;AACjD;;;ALzOA,IAAM,OAAO;AACN,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,YAAY,oBAAI,IAAI,CAAC,cAAc,oBAAoB,CAAC;AAC9D,IAAM,YAAY,CAAC,SAAoB,OAAO,cAAc,OAAO,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,aAAa;AAChH,IAAM,OAAO;AAAA,EACX,YAAY,CAAC,MAAe,kBAAkB,KAAK,CAAC,IAAI,KAAK;AAAA,EAC7D,SAAS;AAAA;AAAA;AAAA,EAGT,YAAY,CAAC,MAAe,MAAM,sBAAsB,MAAM;AAChE;AACA,IAAM,aAAa;AAKZ,SAAS,WAAW,MAAyB,QAAQ,KAAK,OAAO,IAAI,QAAQ,QAAQ,GAAa;AACvG,QAAM,MAAgB,CAAC;AACvB,QAAM,MAAM,CAAC,MAAe;AAC1B,QAAI,CAAC,EAAG;AACR,UAAM,IAAIE,MAAK,QAAQ,CAAC;AACxB,QAAI,CAAC,IAAI,SAAS,CAAC,EAAG,KAAI,KAAK,CAAC;AAAA,EAClC;AACA,MAAIA,MAAK,KAAK,MAAM,SAAS,CAAC;AAC9B,MAAI,IAAI,iBAAiB;AACzB,MAAI;AACF,eAAW,KAAKC,aAAY,MAAM,EAAE,eAAe,KAAK,CAAC,GAAG;AAC1D,UAAI,CAAC,EAAE,YAAY,KAAK,CAAC,iBAAiB,KAAK,EAAE,IAAI,EAAG;AACxD,YAAM,IAAID,MAAK,KAAK,MAAM,EAAE,IAAI;AAChC,UAAIE,YAAWF,MAAK,KAAK,GAAG,UAAU,CAAC,KAAKE,YAAWF,MAAK,KAAK,GAAG,eAAe,CAAC,EAAG,KAAI,CAAC;AAAA,IAC9F;AAAA,EACF,QAAQ;AAAA,EAAC;AACT,SAAO,IAAI,OAAO,CAAC,GAAG,MAAM,MAAM,KAAKE,YAAW,CAAC,CAAC;AACtD;AACA,IAAM,aAAa,CAAC,QAAgBF,MAAK,KAAK,KAAK,eAAe;AAClE,IAAM,cAAc,CAAC,eAAuBA,MAAK,QAAQA,MAAK,QAAQA,MAAK,QAAQ,UAAU,CAAC,CAAC;AAE/F,IAAM,YAAY,CAAC,MAA4B,OAAO,MAAM,YAAY,2BAA2B,KAAK,CAAC;AACzG,IAAM,YAAY,CAAC,MACjB,MAAM,WAAW,MAAM,YAAY,MAAM,SAAS,IAAI,MAAM,sBAAsB,SAAS;AAC7F,IAAM,YAAY,CAAC,MACjB,MAAM,sBACF,eACA,MAAM,gBACJ,SACA,MAAM,uBACJ,gBACA;AAEH,IAAM,aAAsB;AAAA,EACjC,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ,CAAC,MAAM,QAAQ,QAAQ,WAAW,GAAG,EAAE,KAAK,CAAC,MAAME,YAAW,CAAC,CAAC;AAAA,EACxE,aAAa,CAAC,MAAM,QAAQ,QAAQ,WAAW,GAAG,EAAE,IAAI,UAAU;AAAA,EAClE,SAAS,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MACxD,WAAW,GAAG,EAAE,QAAQ,CAAC,MAAM;AAC7B,UAAM,QAAQ,oBAAoB,WAAW,CAAC,GAAG,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI;AAEtF,UAAM,OACJ,KAAK,eAAe,UAAa,MAAM,UAAU,OAAO,WAAW,WAAW,CAAC,GAAG,KAAK,YAAY,GAAG;AACxG,WAAO,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK;AAAA,EACtC,CAAC;AAAA,EACH,WAAW,CAAC,MAAM,QAAQ,KAAK,OAAuB,CAAC,MACrD,WAAW,GAAG,EAAE,IAAI,CAAC,MAAM;AACzB,UAAM,OAAO,WAAW,WAAW,CAAC,GAAG,KAAK;AAC5C,UAAM,QAAQ,sBAAsB,WAAW,CAAC,GAAG,eAAe;AAAA,MAChE,SAAS,KAAK,SAAS,SAAS,WAAW,CAAC,CAAC;AAAA,IAC/C,CAAC;AAGD,QAAI,MAAM,WAAW,CAAC,KAAK,WAAW,MAAM;AAC1C,aAAO,EAAE,GAAG,OAAO,MAAM,mCAAmC,MAAM,MAAM,QAAQ,kBAAkB,EAAE,CAAC,GAAG;AAC1G,WAAO,QAAQ,CAAC,MAAM,WAAW,CAAC,MAAM,UAAU,OAAO;AAAA,EAC3D,CAAC;AAAA,EACH,WAAW,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAC1D,WAAW,GAAG,EAAE,IAAI,CAAC,OAAO;AAAA,IAC1B,MAAM,WAAW,CAAC;AAAA,IAClB,OAAO,sBAAsB,WAAW,CAAC,GAAG,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI;AAAA,EACnF,EAAE;AAAA,EAEJ,SAAS,OAAqC;AAC5C,UAAM,QAAQ,MAAM,KAAK,CAAC,KAAK;AAC/B,QAAI,IAA6B,CAAC;AAClC,QAAI;AAEF,YAAM,IAAa,MAAM,QAAQ,KAAK,MAAM,MAAM,KAAK,IAAI,CAAC;AAC5D,UAAI,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,EAAG,KAAI;AAAA,IAC3D,QAAQ;AAAA,IAAC;AACT,UAAM,KAAM,EAAE,cAAc,EAAE;AAC9B,QAAI,CAAC,GAAI,QAAO;AAChB,QAAI,cAAc,OAAO,EAAE,GAAG,CAAC,EAAG,QAAO;AACzC,UAAM,UAAiC,EAAE,IAAI,OAAO,EAAE,GAAG,KAAK,OAAO,EAAE,QAAQ,WAAW,EAAE,MAAM,MAAM,IAAI;AAC5G,QAAI,OAAO,EAAE,UAAU,SAAU,SAAQ,QAAQ,EAAE;AACnD,QAAI,OAAO,EAAE,aAAa,YAAY,MAAM,WAAW,UAAU,MAAM,SAAS,EAAE;AAChF,cAAQ,YAAY;AACtB,UAAM,UAAU,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY;AAChE,UAAM,OAAO,UAAU,EAAE,SAAS,IAAI,EAAE,YAAY;AACpD,UAAM,UAAU,OAAO,EAAE,gBAAgB,WAAW,EAAE,cAAc;AACpE,UAAM,KAAK,OAAO,EAAE,gBAAgB,WAAW,KAAK,MAAM,EAAE,WAAW,IAAI;AAC3E,UAAM,WAAW,OAAO,EAAE,aAAa,WAAW,EAAE,WAAW;AAI/D,UAAM,eAAe,MACnB,QAAQC,YAAW,QAAQ,EACxB,OAAO,GAAG,WAAW,EAAE,IAAI,QAAQ,EAAE,IAAI,KAAK,UAAU,EAAE,cAAc,IAAI,CAAC,EAAE,EAC/E,OAAO,KAAK,EACZ,MAAM,GAAG,EAAE,CAAC;AACjB,UAAM,KAAK,CAAC,MAAc,UAAmC,EAAE,MAAM,MAAM,MAAM,IAAI,GAAG,cAAc,MAAM;AAC5G,UAAM,SAA+B,CAAC;AACtC,YAAQ,OAAO;AAAA,MACb,KAAK;AACH,eAAO,KAAK,GAAG,mBAAmB,EAAE,QAAQ,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;AAChF;AAAA,MACF,KAAK,oBAAoB;AAEvB,cAAM,SAAS,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS;AACzD,eAAO;AAAA,UACL,GAAG,gBAAgB;AAAA,YACjB;AAAA,YACA,cAAc,WAAW,SAAY,SAAY,CAAC,GAAG,MAAM,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAM7D,cAAc,YAAY,EAAE,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAMlC,UAAU,WAAW,UAAa,cAAc,MAAM,IAAI,OAAO;AAAA,UACnE,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAAA,MACA,KAAK;AACH,YAAI,KAAM,QAAO,KAAK,GAAG,gBAAgB,EAAE,MAAM,SAAS,SAAS,UAAU,OAAO,EAAE,WAAW,CAAC,CAAC;AACnG;AAAA,MACF,KAAK;AACH,YAAI;AACF,iBAAO;AAAA,YACL,GAAG,cAAc;AAAA,cACf;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,IAAI;AAAA,cACJ,OAAO,EAAE;AAAA,cACT,QAAQ,EAAE;AAAA,YACZ,CAAC;AAAA,UACH;AACF;AAAA,MACF,KAAK;AACH,YAAI;AACF,iBAAO;AAAA,YACL,GAAG,cAAc;AAAA,cACf;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,IAAI;AAAA,cACJ,OAAO,QAAQ,EAAE,SAAS,EAAE,aAAa;AAAA,cACzC,OAAO,EAAE;AAAA,cACT,QAAQ,EAAE;AAAA,YACZ,CAAC;AAAA,UACH;AACF;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,oBAAoB;AAAA,YACrB,cAAc,WAAW,aAAa;AAAA,YACtC,MAAM;AAAA,YACN;AAAA,YACA,SAAS,OAAO,GAAG,IAAI,GAAG,SAAS,EAAE,UAAU,CAAC,KAAK;AAAA,YACrD;AAAA,UACF,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,oBAAoB;AAAA,YACrB,cAAc,SAAS,KAAK,IAAI,CAAC;AAAA,YACjC,MAAM,UAAU,EAAE,iBAAiB;AAAA,YACnC,SAAS,OAAO,EAAE,YAAY,WAAW,EAAE,QAAQ,MAAM,GAAG,GAAG,IAAI;AAAA,YACnE;AAAA,UACF,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AAIH,eAAO,KAAK,GAAG,cAAc,EAAE,SAAS,QAAQ,QAAQ,YAAY,gBAAgB,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAC1G;AAAA,MACF,KAAK;AAGH,eAAO;AAAA,UACL,GAAG,cAAc;AAAA,YACf;AAAA,YACA,QAAQ;AAAA,YACR,OAAO,OAAO,EAAE,UAAU,WAAW,EAAE,MAAM,MAAM,GAAG,GAAG,IAAI;AAAA,UAC/D,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,oBAAoB;AAAA,YACrB,UAAU,OAAO,EAAE,YAAY,EAAE;AAAA,YACjC,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;AAAA,UAChE,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AAGH,eAAO;AAAA,UACL,GAAG,kBAAkB;AAAA,YACnB,UAAU,OAAO,EAAE,YAAY,EAAE;AAAA,YACjC,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;AAAA,YAC9D,YAAY,gBAAgB,EAAE,kBAAkB,OAAO,EAAE,YAAY,EAAE,CAAC;AAAA,UAC1E,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,qBAAqB,EAAE,SAAS,EAAE,YAAY,WAAW,WAAW,OAAO,CAAC,CAAC;AAC5F;AAAA,MACF,KAAK;AACH;AAAA;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,iBAAiB,EAAE,QAAQ,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;AAChE;AAAA,MACF,KAAK,cAAc;AAGjB,cAAM,SAAS,aAAa,EAAE,WAAW;AACzC,YAAI,CAAC,OAAQ,QAAO;AACpB,eAAO,KAAK,GAAG,mBAAmB,MAAM,CAAC;AACzC;AAAA,MACF;AAAA,MACA;AACE,eAAO;AAAA,IACX;AACA,UAAM,QAAiC,CAAC;AAGxC,QAAI,UAAU,sBAAsB,OAAO,EAAE,WAAW,SAAU,OAAM,SAAS,EAAE;AACnF,UAAM,OAAO,MAAM,IAAI;AACvB,QAAI,QAAQ,WAAW,KAAK,IAAI,EAAG,OAAM,aAAa;AAItD,UAAM,WAAW,MAAM,IAAI;AAC3B,QAAI,aAAa,OAAO,aAAa,IAAK,OAAM,WAAW,aAAa,OAAO,CAAC,CAAC,MAAM,IAAI;AAG3F,QAAI,UAAU,gBAAgB,UAAU,cAAe,OAAM,aAAa,UAAU,MAAM,GAAG;AAC7F,UAAM,IAAgB,EAAE,SAAS,QAAQ,MAAM;AAC/C,QAAI,OAAO,EAAE,oBAAoB,SAAU,GAAE,aAAa,EAAE;AAC5D,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,MAAM,UAAU;AAC7B,QAAI,OAAO;AACX,QAAI;AACF,aAAOC,cAAa,MAAM,MAAM;AAAA,IAClC,QAAQ;AACN,aAAO,EAAE,OAAO,CAAC,GAAG,MAAM,SAAS;AAAA,IACrC;AACA,WAAO,WAAW,KAAK,MAAM,IAAI,GAAG,QAAQ;AAAA,EAC9C;AAAA,EAEA,MAAM,SAAS,YAAY,OAAO,OAAqB;AACrD,UAAM,MAAoB,CAAC;AAC3B,QAAI,WAAY,QAAO,OAAO,KAAK,YAAY,YAAY,QAAQ,IAAI,OAAO,KAAK,CAAC;AACpF,UAAM,MAAM,IAAI,OAAO,QAAQ;AAC/B,WAAO,OAAO,KAAK,SAAS,GAAG,GAAG,IAAI,SAAS,EAAE,QAAQ,IAAI,OAAO,IAAI,CAAC,CAAC;AAC1E,QAAI,CAAC,IAAI,OAAO,OAAO,MAAM,eAAe,SAAU,KAAI,MAAM,0BAA0B,MAAM,UAAU;AAK1G,QAAI,OAAO,MAAM,aAAa,UAAW,KAAI,WAAW,MAAM,YAAY,CAAC,CAAC,IAAI;AAChF,WAAO;AAAA,EACT;AAAA,EAEA,aAAa,CAAC,SAAS,OAAO,YAAY,OAAO,MAAM,QAAQ,QAC7D,kBAAkB,QAAQ,IAAI,OAAO,YAAY,OAAO,GAAG;AAAA,EAE7D,SAAS,SAAS,MAAM,QAAQ,KAAoB;AAClD,UAAMC,QAAsB,CAAC;AAC7B,UAAM,QAAkB,CAAC;AACzB,eAAW,KAAK,WAAW,GAAG;AAC5B,YAAM,KAAK,GAAG,YAAYL,MAAK,KAAK,GAAG,UAAU,GAAG,GAAG,SAAS,CAAC,MAAM,yBAAyB,KAAK,CAAC,CAAC,CAAC;AAC1G,UAAM,UAAsB,EAAE,KAAK,OAAO,CAAC,IAAI,MAAM,MAAM;AAAA,IAAC,EAAE;AAC9D,eAAW,KAAK,OAAO;AACrB,YAAM,KAAKA,MAAK,SAAS,GAAG,QAAQ;AACpC,YAAM,QAAQ,YAAY,GAAG,IAAI,CAAC,GAAG,OAAO;AAC5C,UAAI,CAAC,MAAM,cAAc,CAAC,MAAM,QAAS;AACzC,YAAM,EAAE,YAAY,SAAS,WAAW,KAAK,iBAAiB,KAAK,GAAG,KAAK,IAAI;AAC/E,YAAM,MAAM,KAAK;AACjB,MAAAK,MAAK,KAAK;AAAA,QACR,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG,GAAG,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC,EAAG;AAAA,QAC1F;AAAA,QACA;AAAA,QACA,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AACA,WAAOA;AAAA,EACT;AACF;AAKA,IAAM,YAAY;AAOX,SAAS,gBAAgB,OAAgB,QAAqC;AACnF,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO;AAClC,SAAO,MAAM,OAAO,CAAC,MAAM;AACzB,UAAM,OAAO;AACb,QAAI,WAAW,UAAa,MAAM,OAAO,OAAQ,QAAO;AACxD,WAAO,EAAE,OAAO,MAAM,YAAY,YAAY,UAAU,KAAK,KAAK,OAAO;AAAA,EAC3E,CAAC,EAAE;AACL;AAKA,IAAM,iBAAiB;AAKvB,SAAS,WAAW,MAAc,MAAe,KAAyC;AACxF,QAAM,IAAI,aAAa,IAAI;AAC3B,MAAI,CAAC,EAAE,GAAI,QAAO;AAClB,QAAM,MAAM,EAAE,MAAM;AACpB,QAAM,OAAO,OAAO,KAAK,YAAY,YAAY,KAAK,KAAK,IAAI,OAAO;AACtE,QAAM,UAAU,MAAM,CAAC,MAAM,YAAY,CAAC,EAAE;AAC5C,MAAI,QAAQ,OAAO,CAAC;AAClB,WAAO,EAAE,MAAM,SAAS,OAAO,SAAS,MAAM,MAAM,wDAAwD;AAC9G,MAAI,OAAO,QAAQ,KAAK,YAAY,WAAW,KAAK,oBAAoB,iBAAiB,CAAC,KAAM,QAAO;AACvG,MAAI,KAAM,GAAE,MAAM,aAAa,EAAE,MAAM,WAAW,SAAS,iBAAiB,eAAe;AAAA,MACtF,QAAO,EAAE,MAAM;AACpB,SACE,UAAU,MAAM,WAAW,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,KAAK;AAAA,IACpD;AAAA,IACA,SAAS;AAAA,IACT,MAAM,OAAO,yCAAyC;AAAA,EACxD;AAEJ;AAGA,IAAM,WAAW,CAAC,MAChB,OAAO,MAAM,YAAY,IAAI,KAAK,IAAI,OAAO,IAAI,KAAK,IAAI,GAAI,EAAE,YAAY,IAAI;AAElF,IAAM,iBAAyC,EAAE,WAAW,KAAK,WAAW,MAAO;AAM5E,SAAS,aAAa,IAA+C;AAC1E,MAAI,CAAC,MAAM,OAAO,OAAO,YAAY,MAAM,QAAQ,EAAE,EAAG,QAAO;AAC/D,QAAM,UAAgD,CAAC;AACvD,aAAW,CAAC,IAAI,CAAC,KAAK,OAAO,QAAQ,EAAwE,GAAG;AAC9G,QAAI,CAAC,oBAAoB,KAAK,EAAE,KAAK,OAAO,GAAG,oBAAoB,YAAY,EAAE,kBAAkB,EAAG;AACtG,YAAQ;AAAA,MACN,MAAM;AAAA,QACJ;AAAA,QACA,cAAc,EAAE;AAAA,QAChB,gBAAgB,eAAe,EAAE;AAAA,QACjC,WAAW,SAAS,EAAE,SAAS;AAAA,MACjC,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,QAAQ,SAAS,EAAE,SAAS,QAAQ,MAAM,GAAG,CAAC,EAAE,IAAI;AAC7D;AAEA,SAAS,MAAyC,GAAS;AACzD,aAAW,KAAK,OAAO,KAAK,CAAC,EAAG,KAAI,EAAE,CAAC,MAAM,OAAW,QAAO,EAAE,CAAC;AAClE,SAAO;AACT;AACA,IAAM,OAAO;AAMN,SAAS,cAAc,IAAY,GAAqC;AAC7E,SAAO,OAAO,EAAE,oBAAoB,YAAY,CAAC,KAAK,KAAK,EAAE;AAC/D;AACA,SAAS,WAAW,GAAoB;AACtC,SAAO,OAAO,MAAM,YAAY,CAAC,WAAW,UAAU,SAAS,WAAW,MAAM,EAAE,SAAS,CAAC,IAAI,IAAI;AACtG;AAMA,IAAM,cAAc,CAAC,MACnB,OAAO,MAAM,YAAY,oCAAoC,KAAK,CAAC,IAAI,IAAI;AAU7E,IAAM,UACJ;AACF,IAAM,gBAAgB,CAAC,WAA4B,QAAQ,KAAK,OAAO,KAAK,CAAC;AAC7E,SAAS,QAAQ,GAAgC;AAC/C,MAAI,OAAO,MAAM,SAAU,QAAO,EAAE,MAAM,GAAG,GAAI;AACjD,MAAI,KAAK,OAAO,MAAM,UAAU;AAC9B,UAAM,IAAI;AACV,QAAI,OAAO,EAAE,UAAU,SAAU,QAAO,EAAE,MAAM,MAAM,GAAG,GAAI;AAC7D,QAAI,OAAO,EAAE,YAAY,SAAU,QAAO,EAAE,QAAQ,MAAM,GAAG,GAAI;AAAA,EACnE;AACA,SAAO;AACT;AACA,SAAS,SAAS,OAAwB;AACxC,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,IAAI;AACV,QAAM,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,OAAO,EAAE;AAC3D,SAAO,OAAO,MAAM,WAAW,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,KAAK;AACzD;AAIA,IAAM,YAAkD;AAAA,EACtD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAOA,SAAS,cAAc,GAA6B;AAClD,MAAI,CAAC,KAAK,OAAO,MAAM,SAAU,QAAO;AACxC,QAAM,IAAI;AACV,QAAM,SAAS,EAAE,aAAa,UAAU,EAAE,UAAU,IAAI;AACxD,SAAO;AAAA,IACL,WAAW,EAAE,YAAY,OAAO,EAAE,SAAS,IAAI;AAAA,IAC/C,MAAM,UAAU,OAAO,EAAE,SAAS,YAAY,EAAE,KAAK,KAAK,IAAI,EAAE,KAAK,KAAK,IAAI;AAAA,IAC9E,aAAa,UAAU;AAAA,IACvB,QAAQ,OAAO,EAAE,oBAAoB,WAAW,EAAE,kBAAkB;AAAA,EACtE;AACF;AACA,IAAM,YAAY,oBAAI,IAAmC;AACzD,SAAS,WAAW,KAAoC;AACtD,QAAM,MAAM,UAAU,IAAI,GAAG;AAC7B,MAAI,IAAK,QAAO;AAChB,QAAM,MAAM,oBAAI,IAAsB;AACtC,MAAI,QAAkB,CAAC;AACvB,MAAI;AACF,YAAQJ,aAAYD,MAAK,KAAK,KAAK,UAAU,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AAAA,EACnF,QAAQ;AAAA,EAAC;AACT,aAAW,KAAK,OAAO;AACrB,QAAI;AACF,YAAM,IAAI,cAAc,KAAK,MAAMI,cAAaJ,MAAK,KAAK,KAAK,YAAY,CAAC,GAAG,MAAM,CAAC,CAAC;AACvF,UAAI,GAAG,UAAW,KAAI,IAAI,EAAE,WAAW,CAAC;AAAA,IAC1C,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,YAAU,IAAI,KAAK,GAAG;AACtB,SAAO;AACT;AACA,SAAS,gBAAgB,KAAa,KAA+B;AACnE,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI;AACF,WAAO,cAAc,KAAK,MAAMI,cAAaJ,MAAK,KAAK,KAAK,YAAY,GAAG,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC;AAAA,EAClG,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,UAAU,MAAyB,QAAQ,KAAoB;AAC7E,MAAI;AACF,UAAM,OAAO,WAAW,GAAG;AAC3B,UAAM,MAAM,CAACM,SAAgB,KAAK,KAAK,CAAC,MAAMJ,YAAWF,MAAK,KAAK,GAAG,YAAY,GAAGM,IAAG,OAAO,CAAC,CAAC;AACjG,QAAI,MAAM,QAAQ;AAClB,QAAI,IAAI,GAAG,EAAG,QAAO;AACrB,UAAM,SAAS,SAAS;AACxB,aAAS,MAAM,GAAG,MAAM,KAAK,MAAM,GAAG,OAAO;AAC3C,YAAM,OAAO,GAAG;AAChB,UAAI,MAAM,KAAK,IAAI,GAAG,EAAG,QAAO;AAAA,IAClC;AAAA,EACF,QAAQ;AAAA,EAAC;AACT,SAAO;AACT;AACA,SAAS,WAAoC;AAC3C,MAAI,QAAQ,aAAa,SAAS;AAChC,WAAO,CAAC,QAAQ;AACd,UAAI;AACF,cAAM,KAAKF,cAAa,SAAS,GAAG,SAAS,MAAM;AACnD,eAAO,OAAO,GAAG,MAAM,GAAG,YAAY,GAAG,IAAI,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK;AAAA,MACpE,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,QAAM,QAAQ,oBAAI,IAAoB;AACtC,MAAI;AACF,UAAM,MAAMG,cAAa,MAAM,CAAC,QAAQ,YAAY,GAAG;AAAA,MACrD,UAAU;AAAA,MACV,SAAS;AAAA,MACT,WAAW,IAAI,OAAO;AAAA,MACtB,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,IACpC,CAAC;AACD,eAAW,KAAK,IAAI,MAAM,IAAI,GAAG;AAC/B,YAAM,IAAI,oBAAoB,KAAK,CAAC;AACpC,UAAI,EAAG,OAAM,IAAI,OAAO,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC;AAAA,IAC7C;AAAA,EACF,QAAQ;AAAA,EAAC;AACT,SAAO,CAAC,QAAQ,MAAM,IAAI,GAAG,KAAK;AACpC;AACA,IAAM,WAAW;AAIjB,SAAS,YAAY,KAAa,OAAO,QAAQ,GAAyD;AACxG,QAAM,QAAQ,CAACP,MAAK,KAAK,KAAK,cAAc,CAAC;AAC7C,MAAIA,MAAK,QAAQ,GAAG,MAAMA,MAAK,KAAK,MAAM,SAAS,EAAG,OAAM,QAAQA,MAAK,KAAK,MAAM,cAAc,CAAC;AACnG,aAAW,KAAK,OAAO;AACrB,QAAI;AACF,YAAM,IACJ,KAAK,MAAMI,cAAa,GAAG,MAAM,CAAC,EAClC;AACF,UAAI,OAAO,GAAG,gBAAgB,YAAY,EAAE,aAAa;AACvD,cAAM,QAAQ,OAAO,EAAE,iBAAiB,YAAY,SAAS,KAAK,EAAE,YAAY,IAAI,EAAE,eAAe;AACrG,eAAO,EAAE,IAAI,EAAE,YAAY,MAAM,GAAG,GAAG,GAAG,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,EAAG;AAAA,MACxE;AAAA,IACF,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,SAAO,WAAW,GAAG;AACvB;AAEA,IAAM,iBAAiB;AAMvB,SAAS,WAAW,KAAkD;AACpE,QAAM,OAAQ,SAAS,WAAW,GAAG,CAAC,EAAiD,KAAK;AAC5F,MAAI,OAAO,SAAS,SAAU,QAAO;AACrC,MAAI;AACJ,MAAI;AACF,QAAI,IAAI,IAAI,KAAK,KAAK,CAAC;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAK,EAAE,aAAa,YAAY,EAAE,aAAa,WAAY,CAAC,EAAE,YAAY,eAAe,KAAK,EAAE,QAAQ;AACtG,WAAO;AAET,QAAM,SAAS,GAAG,EAAE,QAAQ,KAAK,EAAE,IAAI;AACvC,SAAO,EAAE,IAAI,OAAOD,YAAW,QAAQ,EAAE,OAAO,MAAM,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC,IAAI,MAAM,EAAE,KAAK;AACrG;AACA,IAAM,eAAe,oBAAI,IAA2B;AAIpD,SAAS,cAAc,KAA4B;AACjD,QAAM,MAAM,aAAa,IAAI,GAAG;AAChC,MAAI,QAAQ,OAAW,QAAO;AAC9B,QAAM,OAAO,YAAY,GAAG,GAAG,MAAM;AACrC,eAAa,IAAI,KAAK,IAAI;AAC1B,SAAO;AACT;AAyBA,IAAM,UAAU;AAChB,SAAS,eAAe,SAAmD;AACzE,WAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC5C,UAAM,IAAI,QAAQ,CAAC;AACnB,QAAI,EAAE,SAAS,YAAY,EAAE,YAAY,mBAAmB,OAAO,EAAE,QAAQ,YAAY,QAAQ,KAAK,EAAE,GAAG;AACzG,aAAO,EAAE;AACX,QAAI,EAAE,SAAS,oBAAoB,OAAO,EAAE,oBAAoB,UAAU;AACxE,YAAM,IAAI,mCAAmC,KAAK,EAAE,eAAe;AACnE,UAAI,EAAG,QAAO,kCAAkC,EAAE,CAAC,CAAC;AAAA,IACtD;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,SAAS,MAAsB;AAC7C,QAAM,IAAI,OAAO,IAAI,EAAE,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACjD,SAAO,EAAE,SAAS,KAAK,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,QAAQ;AAClD;AACA,SAAS,QAAQ,GAAkD;AACjE,MAAI,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,UAAU,EAAE,YAAa,QAAO;AACjE,QAAM,SAAS,EAAE;AACjB,MAAI,QAAQ,QAAQ,OAAO,SAAS,QAAS,QAAO;AACpD,QAAM,IAAK,EAAE,SAA+C;AAC5D,QAAM,QACJ,OAAO,MAAM,WACT,IACA,MAAM,QAAQ,CAAC,IACb,EACG,OAAO,CAAC,MAAM,KAAK,EAAE,SAAS,UAAU,OAAO,EAAE,SAAS,QAAQ,EAClE,IAAI,CAAC,MAAM,EAAE,IAAc,EAC3B,KAAK,IAAI,IACZ,IACN,KAAK;AACP,MAAI,CAAC,QAAQ,WAAW,IAAI,EAAG,QAAO;AACtC,SAAO;AACT;AACA,SAAS,SAAS,SAAmD;AACnE,aAAW,KAAK,SAAS;AACvB,UAAM,IAAI,QAAQ,CAAC;AACnB,QAAI,EAAG,QAAO,SAAS,CAAC;AAAA,EAC1B;AACA,SAAO;AACT;AACA,SAAS,WAAW,MAAc,MAAc,OAAuD;AACrG,QAAM,MAAM,EAAE,QAAQ,MAAM,UAAU,MAAM,IAAI,MAAM,MAAM,MAAM,MAAM,KAAK;AAC7E,MAAI,OAAO;AACX,MAAI;AACF,WAAOK,UAAS,IAAI,EAAE;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,OAAO,MAAM;AACf,QAAI,OAAO;AACX,QAAI,SAAS,IAAI,KAAK;AACtB,WAAO;AAAA,EACT;AAEA,MAAI,MAAM;AACV;AAAA,IACE;AAAA,IACA,CAAC,MAAM;AACL,aAAO,OAAO,WAAW,GAAG,MAAM,IAAI;AACtC,UAAI,OAAO,QAAQ,EAAE,QAAQ,SAAS,IAAI,EAAG,QAAO;AACpD,UAAI;AACF,cAAM,IAAI,KAAK,MAAM,CAAC;AACtB,YAAI,EAAE,SAAS,kBAAkB,EAAE,YAAa,KAAI,SAAS,EAAE;AAC/D,YAAI,EAAE,SAAS,cAAc,EAAE,QAAS,KAAI,KAAK,EAAE;AAAA,MACrD,QAAQ;AAAA,MAAC;AACT,aAAO;AAAA,IACT;AAAA,IACA,MAAM,OAAO;AAAA,EACf;AACA,MAAI,OAAO;AACX,SAAO;AACT;AAKO,SAAS,YACd,MACA,WACA,OACA,OACc;AACd,QAAM,EAAE,MAAM,KAAK,IAAI,KAAK,MAAM,KAAK,MAAM,MAAM,IAAI;AACvD,QAAM,IAAI,WAAW,IAAI;AACzB,QAAM,IAAI,WAAW,IAAI;AACzB,QAAM,MAAoB,CAAC;AAC3B,QAAM,KAAK,aAAaC,MAAK,SAAS,MAAM,QAAQ;AACpD,QAAM,MAAM,MAAM,IAAI,MAAM,EAAE;AAC9B,MAAI,SAAwB;AAC5B,MAAI,KAAoB;AACxB,aAAW,KAAK,EAAE,OAAO,CAAC,GAAG;AAC3B,QAAI,EAAE,SAAS,kBAAkB,OAAO,EAAE,gBAAgB,SAAU,UAAS,EAAE;AAC/E,QAAI,EAAE,SAAS,cAAc,OAAO,EAAE,YAAY,SAAU,MAAK,EAAE;AAAA,EACrE;AACA,MAAI,OAAO;AACX,MAAI;AACF,WAAOD,UAAS,IAAI,EAAE;AAAA,EACxB,QAAQ;AAAA,EAAC;AACT,MAAI,CAAC,UAAU,CAAC,MAAM,OAAO,KAAK,OAAO,MAAM,MAAM;AACnD,UAAM,IAAI,WAAW,MAAM,OAAO,IAAI,SAAS,KAAK,GAAG;AAAA,MACrD,QAAQ,IAAI;AAAA,MACZ,IAAI,IAAI;AAAA,IACV,CAAC;AACD,aAAS,EAAE;AACX,SAAK,EAAE;AACP,UAAM,KAAK,MAAM,IAAI,EAAE,WAAW,EAAE,MAAM,aAAa,EAAE,QAAQ,SAAS,EAAE,GAAG,CAAC;AAAA,EAClF;AACA,QAAM,MAAM,YAAY,IAAI;AAC5B,QAAM,OAAO,WAAW,GAAG,EAAE,IAAI,EAAE,KAAK,gBAAgB,KAAK,MAAM,UAAU;AAC7E,MAAI,MAAM,KAAM,OAAM,KAAK,MAAM,IAAI,EAAE,MAAM,KAAK,MAAM,aAAa,KAAK,YAAY,CAAC;AACvF,QAAM,SACJ,MAAM,UAAU,yBAAyB,KAAK,KAAK,MAAM,IAAI,0BAA0B,KAAK,MAAM,KAAK;AACzG,QAAM,MAAM,UAAU,eAAe,CAAC,KAAK,eAAe,CAAC,MAAM,OAAO,IAAI,QAAQ,WAAW,IAAI,MAAM;AACzG,MAAI,KAAK;AACP,QAAI,MAAM;AACV,UAAM,KAAK,MAAM,IAAI,EAAE,IAAI,CAAC;AAAA,EAC9B;AACA,QAAM,OAAO,MAAM,OACf,EAAE,OAAO,KAAK,MAAM,QAAQ,KAAK,YAAoC,IACrE,OAAO,IAAI,SAAS,WAClB,EAAE,OAAO,IAAI,MAAM,QAAQ,IAAI,YAAoC,IACnE;AACN,QAAM,OACH,QAAQ,KAAK,WAAW,YAAY,QACpC,UAAU,EAAE,OAAO,QAAQ,QAAQ,SAAkB,KACtD,QACC,MAAM,EAAE,OAAO,IAAI,QAAQ,UAAmB;AACjD,MAAIE,OAAM,OAAO,IAAI,cAAc,WAAW,IAAI,YAAY,SAAS,CAAC;AACxE,MAAI,CAACA,QAAO,OAAO,KAAK,QAAQ,CAAC,IAAI,gBAAgB;AACnD,IAAAA,OAAM,UAAkB,MAAM,CAAC,MAAM;AACnC,UAAI;AACF,cAAM,IAAI,QAAQ,KAAK,MAAM,CAAC,CAA4B;AAC1D,eAAO,IAAI,SAAS,CAAC,IAAI;AAAA,MAC3B,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AACD,QAAI,CAACA,QAAO,OAAO,KAAK,OAAO,KAAM,OAAM,KAAK,MAAM,IAAI,EAAE,gBAAgB,KAAK,CAAC;AAAA,EACpF;AACA,MAAIA,MAAK;AACP,QAAI,YAAYA;AAChB,QAAI,CAAC,IAAI,UAAW,OAAM,KAAK,MAAM,IAAI,EAAE,WAAWA,KAAI,CAAC;AAAA,EAC7D;AACA,MAAI,MAAM;AACR,QAAI,QAAQ,OAAO,KAAK,KAAK,EAAE,MAAM,GAAG,GAAG;AAC3C,QAAI,eAAe,KAAK;AAAA,EAC1B,WAAWA,MAAK;AACd,QAAI,QAAQA;AACZ,QAAI,eAAe;AAAA,EACrB;AACA,QAAM,OAAO,cAAc,GAAG;AAC9B,MAAI,KAAM,KAAI,aAAa;AAC3B,WAAS,IAAI,EAAE,SAAS,GAAG,KAAK,GAAG,KAAK;AACtC,UAAM,IAAK,EAAE,CAAC,EAAuC,SAAS;AAC9D,QAAI,EAAE,CAAC,GAAG,SAAS,eAAe,KAAK,MAAM,eAAe;AAC1D,UAAI,QAAQ;AACZ;AAAA,IACF;AAAA,EACF;AACA,QAAM,OAAO,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO;AAC/E,MAAI,MAAM,QAAS,KAAI,kBAAkB,OAAO,KAAK,OAAO;AAC5D,QAAM,QAAQ,EAAE,OAAO,CAAC,EACrB,IAAI,CAAC,MAAM,KAAK,MAAM,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC,EAChD,OAAO,CAAC,MAAM,IAAI,CAAC;AACtB,MAAI,MAAM,QAAQ;AAChB,QAAI,aAAa,KAAK,IAAI,GAAG,KAAK;AAClC,QAAI,UAAU,KAAK,IAAI,GAAG,KAAK;AAAA,EACjC;AACA,QAAM,UAAU,EAAE,KAAK,CAAC,MAAM,OAAO,EAAE,QAAQ,QAAQ;AACvD,MAAI,SAAS;AACX,QAAI,MAAM,QAAQ;AAClB,QAAI,OAAO,QAAQ,cAAc,YAAY,QAAQ,cAAc,OAAQ,KAAI,SAAS,QAAQ;AAAA,EAClG;AACA,SAAO;AACT;AAUA,IAAM,kBAAkB;AACxB,IAAM,oBAAoB,KAAK;AAC/B,IAAM,aAAa;AACnB,IAAM,WAAW;AAEjB,IAAM,oBAAoB;AAC1B,IAAM,sBAAsB;AAE5B,IAAM,mBAAmB,KAAK;AAG9B,IAAM,YAAY,CAAC,MACjB,EACG,QAAQ,0DAA0D,GAAG,EACrE,QAAQ,QAAQ,GAAG,EACnB,KAAK;AAIV,eAAsB,kBACpB,IACA,OACA,YACA,OACA,KACA,MAAM,KAAK,IAAI,GACiB;AAChC,MAAI,CAAC,cAAc,CAAC,MAAM,OAAO,CAAC,QAAQ,KAAK,MAAM,GAAG,KAAK,MAAM,iBAAiB,SAAU,QAAO,CAAC;AACtG,QAAM,MAAM,MAAM,IAAI,MAAM,EAAE;AAC9B,MAAI,QAAQ,OAAO,IAAI,iBAAiB,WAAW,IAAI,eAAe;AACtE,MAAI,OAAO,OAAO,IAAI,SAAS,KAAK,OAAO,QAAQ,oBAAoB,kBAAkB;AAEvF,UAAM,KAAK,MAAM,IAAI,EAAE,WAAW,IAAI,CAAC;AACvC,UAAM,QAAQ,cAAc,YAAY,UAAU,GAAG,KAAK,GAAG;AAC7D,UAAM,MAAM,QAAQ,MAAM,cAAc,MAAM,IAAI,MAAM,MAAM,IAAI,YAAY,GAAG,IAAI,CAAC,GAAG,KAAK,IAAI;AAClG,QAAI,QAAQ,QAAW;AACrB,cAAQ,KAAK,MAAM,GAAG,GAAG,KAAK;AAC9B,YAAM,KAAK,MAAM,IAAI,EAAE,cAAc,MAAM,CAAC;AAAA,IAC9C;AAAA,EACF;AACA,SAAO,QAAQ,EAAE,OAAO,MAAM,MAAM,GAAG,GAAG,GAAG,cAAc,UAAU,IAAI,CAAC;AAC5E;AAKA,SAAS,iBAAiB,KAAa,KAAkC;AACvE,QAAM,QAAQ,CAAC,MACb,GAAG,QAAQ,IAAIC,YAAW,QAAQ,EAAE,OAAO,EAAE,UAAU,KAAK,CAAC,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1F,QAAM,MAAM,oBAAI,IAAY;AAC5B,MAAI,IAAI,qBAAqBF,MAAK,QAAQ,IAAI,iBAAiB,MAAM,IAAK,KAAI,IAAI,MAAM,IAAI,iBAAiB,CAAC;AAC9G,MAAI,QAAQA,MAAK,KAAK,IAAI,QAAQ,QAAQ,GAAG,SAAS,EAAG,KAAI,IAAI,QAAQ;AACzE,MAAI,IAAI,MAAM,GAAG,CAAC;AAClB,SAAO,CAAC,GAAG,GAAG;AAChB;AAKA,IAAM,aAAa,oBAAI,IAAgD;AAKhE,SAAS,cAAc,KAAa,KAAwB,KAA4B;AAC7F,QAAM,MAAM,WAAW,IAAI,GAAG;AAC9B,MAAI,OAAO,IAAI,UAAU,MAAM,IAAQ,QAAO,IAAI;AAClD,QAAM,MAAgB,CAAC;AACvB,MAAI,QAAQ,aAAa;AACvB,eAAW,WAAW,iBAAiB,KAAK,GAAG,GAAG;AAChD,UAAI;AACF,cAAM,OAAO,IAAI,QAAQ,SAAS,EAAE;AACpC,cAAM,UAAU,YAAY,KAAK,IAAI,IAAI,OAAO;AAChD,YAAI;AAAA,UACFG,cAAa,YAAY,CAAC,yBAAyB,MAAM,SAAS,MAAM,MAAM,OAAO,GAAG;AAAA,YACtF,UAAU;AAAA,YACV,SAAS;AAAA,YACT,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,UACpC,CAAC;AAAA,QACH;AACA;AAAA,MACF,QAAQ;AAAA,MAAC;AAAA,IACX;AACF,MAAI;AACF,QAAI,KAAKC,cAAaJ,MAAK,KAAK,KAAK,mBAAmB,GAAG,MAAM,CAAC;AAAA,EACpE,QAAQ;AAAA,EAAC;AACT,aAAW,KAAK,KAAK;AACnB,QAAI;AAGF,YAAM,IAAI,EAAE,KAAK;AACjB,YAAM,IACJ,KAAK,MAAM,sBAAsB,KAAK,CAAC,IAAI,OAAO,KAAK,GAAG,KAAK,EAAE,SAAS,MAAM,IAAI,CAAC,EAGrF;AACF,UAAI,OAAO,GAAG,gBAAgB,YAAY,CAAC,EAAE,YAAa;AAC1D,YAAM,UAAU,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY,OAAO;AACvE,UAAI,WAAW,MAAM,IAAQ;AAC7B,iBAAW,IAAI,KAAK,EAAE,OAAO,EAAE,aAAa,QAAQ,CAAC;AACrD,aAAO,EAAE;AAAA,IACX,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,SAAO;AACT;AAWA,eAAsB,cAAc,QAAgB,OAAmD;AACrG,MAAI;AACF,UAAM,IAAI,MAAM,MAAM,GAAG,UAAU,qBAAqB,MAAM,IAAI;AAAA,MAChE,SAAS,EAAE,eAAe,UAAU,KAAK,IAAI,qBAAqB,cAAc,cAAc,cAAc;AAAA,MAC5G,UAAU;AAAA,MACV,QAAQ,YAAY,QAAQ,iBAAiB;AAAA,IAC/C,CAAC;AACD,QAAI,EAAE,WAAW,OAAO,OAAO,EAAE,QAAQ,IAAI,gBAAgB,CAAC,IAAI,iBAAkB,QAAO;AAC3F,UAAM,OAAO,MAAM,EAAE,KAAK;AAC1B,QAAI,KAAK,SAAS,iBAAkB,QAAO;AAI3C,UAAM,IAAI,KAAK,MAAM,IAAI;AACzB,UAAM,IAAI,GAAG,kBAAkB,GAAG,WAAW;AAC7C,UAAM,OAAO,CAAC,MAAc,EAAE,QAAQ,qBAAqB,EAAE;AAC7D,QAAI,OAAO,GAAG,OAAO,YAAY,KAAK,EAAE,EAAE,MAAM,KAAK,MAAM,EAAG,QAAO;AACrE,WAAQ,OAAO,EAAE,UAAU,YAAY,UAAU,EAAE,KAAK,KAAM;AAAA,EAChE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AM79BA,SAAS,aAAa;AACtB,SAAS,YAAY,WAAW,gBAAAK,eAAc,YAAAC,iBAAgB;AAC9D,OAAOC,WAAU;;;ACEV,IAAM,kBAAkB;AAIxB,IAAM,mBAAmB;AAEzB,IAAM,gBAAgB;AAKtB,IAAM,cAAc,CAAC,iBAAiB,kBAAkB,aAAa;AAC5E,IAAM,cAAc;AAGb,SAAS,eAAe,KAA2C;AACxE,QAAM,IAAI,EAAE,GAAG,IAAI;AACnB,aAAW,KAAK,YAAa,QAAO,EAAE,CAAC;AACvC,SAAO;AACT;AAGA,SAAS,MAAM,KAAyC,MAAkC;AACxF,QAAM,IAAI,IAAI,IAAI,GAAG,KAAK;AAC1B,SAAO,KAAK,YAAY,KAAK,CAAC,IAAI,IAAI;AACxC;AAGO,SAAS,WAAW,KAA6D;AACtF,SAAO,MAAM,KAAK,eAAe;AACnC;AAIO,SAAS,YAAY,KAA6D;AACvF,SAAO,MAAM,KAAK,gBAAgB;AACpC;AAMO,SAAS,SAAS,SAAkB,OAAqC;AAC9E,QAAM,IAAI,QAAQ,SAAS,KAAK;AAChC,MAAI,KAAK,CAAC,EAAE,QAAQ,WAAW;AAC7B,UAAM,OAAO,GAAG,QAAQ,IAAI,IAAI,EAAE,QAAQ,EAAE;AAC5C,UAAM,WAAW,WAAW,MAAM,GAAG;AACrC,QAAI,YAAY,aAAa,KAAM,GAAE,QAAQ,cAAc;AAC3D,UAAM,QAAQ,YAAY,MAAM,GAAG;AACnC,QAAI,SAAS,UAAU,KAAM,GAAE,QAAQ,eAAe;AAAA,EACxD;AACA,SAAO;AACT;;;ADlDO,IAAM,iBAAiB,KAAK;AACnC,IAAM,UAAU,OAAO;AAOhB,SAAS,MAAM,GAAoB;AACxC,MAAI;AACF,QAAI,CAACC,UAAS,CAAC,EAAE,OAAO,EAAG,QAAO;AAClC,eAAW,GAAG,UAAU,IAAI;AAC5B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,WAAW,GAKT;AAChB,MAAI,EAAE,YAAY,MAAM,EAAE,QAAQ,EAAG,QAAO,EAAE;AAC9C,aAAW,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,EAAE,MAAMC,MAAK,SAAS,GAAG;AAC/D,QAAI,CAAC,KAAK,CAACA,MAAK,WAAW,CAAC,EAAG;AAC/B,eAAW,KAAK,EAAE,MAAO,KAAI,MAAMA,MAAK,KAAK,GAAG,CAAC,CAAC,EAAG,QAAOA,MAAK,KAAK,GAAG,CAAC;AAAA,EAC5E;AACA,aAAW,KAAK,EAAE,KAAM,YAAW,KAAK,EAAE,MAAO,KAAI,MAAMA,MAAK,KAAK,GAAG,CAAC,CAAC,EAAG,QAAOA,MAAK,KAAK,GAAG,CAAC;AAClG,SAAO;AACT;AAKO,SAAS,WAAW,MAA6B;AACtD,MAAI,OAAO;AACX,MAAI;AACF,WAAOC,cAAa,MAAM,MAAM;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,IAAI,kCAAkC,KAAK,IAAI;AACrD,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,SAASD,MAAK,QAAQA,MAAK,QAAQ,IAAI,GAAI,EAAE,CAAC,EAAa,MAAM,IAAI,EAAE,KAAKA,MAAK,GAAG,CAAC;AAC3F,MAAI;AACF,WAAOD,UAAS,MAAM,EAAE,OAAO,IAAI,SAAS;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOO,SAAS,UAAU,KAAa,MAAyD;AAC9F,MAAI,CAAC,gBAAgB,KAAK,GAAG,EAAG,QAAO,EAAE,MAAM,KAAK,KAAK;AACzD,QAAM,SAAS,WAAW,GAAG;AAC7B,SAAO,SAAS,EAAE,MAAM,QAAQ,UAAU,MAAM,CAAC,QAAQ,GAAG,IAAI,EAAE,IAAI;AACxE;AAGO,IAAM,WAAW,CAAC,QACvB,GAAGC,MAAK,SAAS,GAAG,CAAC;AAKhB,SAAS,MAAM,KAAa,MAAkE;AACnG,QAAM,IAAI,UAAU,KAAK,IAAI;AAC7B,SAAO,IAAI,EAAE,GAAG,GAAG,OAAO,MAAM,IAAI,EAAE,MAAM,CAAC,IAAI,GAAG,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG,GAAG,MAAM,CAAC,GAAG,OAAO,KAAK;AACrG;AA0CO,SAAS,YAAY,KAAa,MAAgB,GAAmC;AAC1F,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,MAAgB,CAAC;AACrB,QAAI,SAAS;AACb,QAAI,SAAS;AACb,QAAI,UAAU;AACd,QAAI,WAAW;AACf,QAAI,UAAU;AACd,QAAI;AACJ,UAAM,IAAI,UAAU,KAAK,IAAI;AAC7B,QAAI,CAAC,GAAG;AACN,cAAQ,EAAE,OAAO,SAAS,GAAG,EAAE,CAAC;AAChC;AAAA,IACF;AACA,QAAI;AACF,cAAQ,MAAM,EAAE,MAAM,EAAE,MAAM;AAAA,QAC5B,KAAK,EAAE;AAAA,QACP,KAAK,EAAE;AAAA,QACP,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,QAChC,UAAU,QAAQ,aAAa;AAAA,QAC/B,aAAa;AAAA,MACf,CAAC;AAAA,IACH,SAAS,GAAG;AACV,cAAQ,EAAE,OAAQ,EAAY,QAAQ,CAAC;AACvC;AAAA,IACF;AACA,UAAM,OAAO,CAAC,QAAwB;AACpC,UAAI;AACF,YAAI,QAAQ,aAAa,WAAW,MAAM,IAAK,SAAQ,KAAK,CAAC,MAAM,KAAK,GAAG;AAAA,YACtE,OAAM,KAAK,GAAG;AAAA,MACrB,QAAQ;AACN,YAAI;AACF,gBAAM,KAAK,GAAG;AAAA,QAChB,QAAQ;AAAA,QAAC;AAAA,MACX;AAAA,IACF;AACA,UAAM,OAAO,CAAC,SAAiB;AAC7B,UAAI,CAAC,EAAE,OAAO,IAAI,EAAG;AACrB,YAAM,CAAC,OAAO,KAAK,IAAI,CAAC;AACxB,eAAS,KAAK;AAAA,IAChB;AACA,QAAI,UAAU;AACd,UAAM,QAAQ,GAAG,QAAQ,CAAC,MAAc;AACtC,UAAI,EAAE,QAAQ;AACZ,cAAM,QAAQ,UAAU,EAAE,SAAS,MAAM;AACzC,cAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,kBAAU,MAAM,IAAI;AACpB,mBAAW,QAAQ,OAAO;AACxB,cAAI,CAAC,KAAK,KAAK,EAAG;AAClB,cAAI;AACF,cAAE,OAAO,IAAI;AAAA,UACf,QAAQ;AAAA,UAAC;AACT,eAAK,IAAI;AAAA,QACX;AACA,YAAI,QAAQ,SAAS,QAAS,WAAU;AACxC;AAAA,MACF;AACA,UAAI,OAAQ;AACZ,UAAI,SAAS,EAAE,SAAS,SAAS;AAC/B,iBAAS;AACT,cAAM,CAAC;AACP;AAAA,MACF;AACA,UAAI,KAAK,CAAC;AACV,gBAAU,EAAE;AAAA,IACd,CAAC;AACD,UAAM,QAAQ,GAAG,QAAQ,CAAC,MAAc;AACtC,iBAAW,UAAU,EAAE,SAAS,MAAM,GAAG,MAAM,IAAK;AAAA,IACtD,CAAC;AACD,UAAM,QAAQ,WAAW,MAAM;AAC7B,iBAAW;AACX,WAAK,SAAS;AACd,iBAAW,MAAM,KAAK,SAAS,GAAG,GAAM,EAAE,MAAM;AAAA,IAClD,GAAG,EAAE,aAAa,cAAc;AAChC,UAAM,SAAS,CAAC,MAAqB,YAA8C;AAAA,MACjF;AAAA,MACA;AAAA,MACA,QAAQ,SAAS,OAAO,OAAO,OAAO,GAAG,EAAE,SAAS,MAAM;AAAA,MAC1D;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,IACF;AACA,UAAM,SAAS,CAAC,MAAiB;AAC/B,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,KAAK;AAClB,cAAQ,CAAC;AAAA,IACX;AACA,UAAM,GAAG,SAAS,CAAC,MAAM,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;AAErD,UAAM,GAAG,QAAQ,CAAC,MAAM,WAAW,WAAW,MAAM,OAAO,OAAO,MAAM,MAAM,CAAC,GAAG,GAAI,EAAE,MAAM,CAAC;AAC/F,UAAM,GAAG,SAAS,CAAC,MAAM,WAAW;AAElC,UAAI,EAAE,UAAU,QAAQ,KAAK,GAAG;AAC9B,YAAI;AACF,YAAE,OAAO,OAAO;AAAA,QAClB,QAAQ;AAAA,QAAC;AACT,aAAK,OAAO;AACZ,kBAAU;AAAA,MACZ;AACA,aAAO,OAAO,MAAM,MAAM,CAAC;AAAA,IAC7B,CAAC;AAAA,EACH,CAAC;AACH;;;AE5NA,SAAS,cAAAE,aAAY,eAAAC,cAAa,gBAAAC,eAAc,cAAc,YAAAC,iBAAgB;AAC9E,OAAOC,WAAU;AAgBjB,IAAMC,QAAO;AACb,IAAM,YAAY;AACX,IAAM,YAAY,CAAC,iBAAiB,kBAAkB,eAAe,MAAM;AAI3E,IAAM,mBACX;AAEK,IAAM,oBAAoB,CAAC,UAA2B,UAAU,eAAe,mBAAmB;AACzG,IAAM,SAAS,CAAC,QAA2B,IAAI,mBAAmBC,MAAK,KAAK,QAAQ,GAAG,SAAS;AAChG,IAAM,YAAY,CAAC,QAA2BA,MAAK,KAAK,OAAO,GAAG,GAAG,UAAU,YAAY;AAC3F,IAAM,cAAc,CAAC,QAA2BA,MAAK,KAAK,OAAO,GAAG,GAAG,UAAU,UAAU;AAC3F,IAAM,WAAW,CAAC,QAChB,CAAC,eAAe,mBAAmB,iBAAiB,EAAE,IAAI,CAAC,MAAMA,MAAK,KAAK,OAAO,GAAG,GAAG,CAAC,CAAC;AAC5F,IAAM,QAAQ;AACd,IAAMC,aAAY,CAAC,MAA4B,OAAO,MAAM,YAAY,2BAA2B,KAAK,CAAC;AAGzG,SAAS,MAAM,KAAkB;AAC/B,QAAM,UAAU,CAAC,OAAe,EAAE,MAAM,WAAW,SAAS,IAAI,CAACF,OAAM,CAAC,CAAC,EAAE,SAAS,SAAS,EAAE;AAC/F,QAAM,MAA+B,EAAE,SAAS,KAAK;AACrD,aAAW,KAAK,UAAW,KAAI,CAAC,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC1G,SAAO;AACT;AAEO,IAAM,cAAuB;AAAA,EAClC,MAAMA;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ,CAAC,MAAM,QAAQ,QACrBG,YAAWF,MAAK,KAAK,OAAO,GAAG,GAAG,QAAQ,CAAC,KAAK,SAAS,GAAG,EAAE,KAAK,CAAC,MAAME,YAAW,CAAC,CAAC;AAAA,EACzF,aAAa,CAAC,MAAM,QAAQ,QAAQ,CAAC,UAAU,GAAG,CAAC;AAAA,EACnD,QAAQ,KAAK,MAAM,QAAQ,KAAsB;AAC/C,UAAM,OAAO,UAAU,GAAG;AAC1B,UAAM,IAAI,aAAa,IAAI;AAC3B,QAAI,CAAC,EAAE,GAAI,QAAO,CAAC,EAAE,MAAM,SAAS,OAAO,SAAS,MAAM,MAAM,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC5F,UAAM,QAAQ,EAAE;AAChB,UAAM,IAAI,MAAM,GAAG;AACnB,QAAI,KAAK,UAAU,MAAM,SAAS,CAAC,MAAM,KAAK,UAAU,CAAC,EAAG,QAAO,CAAC,EAAE,MAAM,SAAS,MAAM,CAAC;AAC5F,UAAM,SAAS,IAAI;AACnB,WAAO;AAAA,MACL,UAAU,MAAM,WAAW,OAAO,CAAC,GAAG,EAAE,OAAO,KAAK;AAAA,QAClD;AAAA,QACA,SAAS;AAAA,QACT,SAAS,CAAC,EAAE;AAAA,QACZ,MAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA,EACA,UAAU,MAAM,QAAQ,KAAK,OAAuB,CAAC,GAAoB;AACvE,UAAM,OAAO,UAAU,GAAG;AAC1B,QAAI,CAACA,YAAW,IAAI,EAAG,QAAO,CAAC,EAAE,MAAM,SAAS,MAAM,CAAC;AACvD,UAAM,IAAI,aAAa,IAAI;AAC3B,QAAI,CAAC,EAAE,GAAI,QAAO,CAAC,EAAE,MAAM,SAAS,OAAO,SAAS,MAAM,MAAM,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC5F,UAAM,QAAQ,EAAE;AAChB,QAAI,EAAE,aAAa,OAAQ,QAAO,CAAC,EAAE,MAAM,SAAS,MAAM,CAAC;AAC3D,WAAO,MAAM,SAAS;AACtB,QAAI,KAAK,SAAS,SAAS,IAAI,KAAK,CAAC,OAAO,KAAK,KAAK,EAAE,OAAQ,QAAO,CAAC,cAAc,MAAM,WAAW,OAAO,CAAC,CAAC,CAAC;AACjH,WAAO,CAAC,UAAU,MAAM,WAAW,OAAO,CAAC,CAAC,KAAK,EAAE,MAAM,SAAS,KAAK,CAAC;AAAA,EAC1E;AAAA,EACA,UAAU,KAAK,MAAM,QAAQ,KAAK;AAChC,UAAM,OAAO,UAAU,GAAG;AAC1B,UAAM,IAAI,aAAa,IAAI;AAC3B,UAAM,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,IAAI;AACzC,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,OAAO,CAAC,OAAO,YAAY,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,MAAM,GAAG,CAAC,IAAI,YAAY;AAAA,MAC/F;AAAA,IACF;AAAA,EACF;AAAA,EAEA,SAAS,OAAqC;AAC5C,UAAM,QAAQ,MAAM,KAAK,CAAC,KAAK;AAC/B,QAAI,IAA6B,CAAC;AAClC,QAAI;AAEF,YAAM,IAAa,MAAM,QAAQ,KAAK,MAAM,MAAM,KAAK,IAAI,CAAC;AAC5D,UAAI,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,EAAG,KAAI;AAAA,IAC3D,QAAQ;AAAA,IAAC;AACT,UAAM,KAAM,EAAE,kBAAkB,EAAE,cAAc,EAAE;AAClD,UAAM,SAAS,kBAAkB,KAAK;AACtC,QAAI,CAAC,GAAI,QAAO,EAAE,SAAS,EAAE,IAAI,GAAG,GAAG,QAAQ,CAAC,GAAG,OAAO;AAC1D,UAAM,aAAa,aAAa,EAAE,cAAc;AAChD,UAAM,UAAiC,EAAE,IAAI,OAAO,EAAE,GAAG,KAAK,WAAW,CAAC,KAAK,MAAM,IAAI;AACzF,QAAI,OAAO,EAAE,cAAc,YAAY,EAAE,cAAc,OAAQ,SAAQ,QAAQ,EAAE;AACjF,UAAM,WAAW,EAAE;AACnB,UAAM,OAAOD,WAAU,UAAU,IAAI,IAAI,UAAU,OAAOA,WAAU,EAAE,QAAQ,IAAI,EAAE,WAAW;AAC/F,UAAM,KAAK,CAAC,MAAc,UAAmC,EAAE,MAAM,MAAME,OAAM,IAAI,GAAG,cAAc,MAAM;AAC5G,UAAM,SAA+B,CAAC;AACtC,YAAQ,OAAO;AAAA,MACb,KAAK;AAEH,YAAI,OAAO,EAAE,aAAa,MAAM,KAAK,EAAE,kBAAkB,QAAW;AAClE,iBAAO,KAAK,GAAG,mBAAmB,EAAE,QAAQ,UAAU,CAAC,CAAC;AACxD,iBAAO,KAAK,GAAG,gBAAgB,CAAC,CAAC,CAAC;AAAA,QACpC,MAAO,QAAO,KAAK,GAAG,qBAAqB,CAAC,CAAC,CAAC;AAC9C;AAAA,MACF,KAAK;AACH;AAAA,MACF,KAAK;AACH,YAAI,KAAM,QAAO,KAAK,GAAG,gBAAgB,EAAE,MAAM,OAAO,UAAU,KAAK,CAAC,CAAC;AACzE;AAAA,MACF,KAAK;AAEH,YAAI,CAAC,KAAM,QAAO,KAAK,GAAG,qBAAqB,CAAC,CAAC,CAAC;AAAA;AAEhD,iBAAO;AAAA,YACL,GAAG,cAAc;AAAA,cACf;AAAA,cACA,IAAI,CAAC,EAAE;AAAA,cACP,OAAO,OAAO,EAAE,UAAU,WAAW,EAAE,MAAM,MAAM,GAAG,GAAI,IAAI;AAAA,cAC9D,OAAO,UAAU;AAAA,YACnB,CAAC;AAAA,UACH;AACF;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,cAAc,EAAE,QAAQ,EAAE,QAAQ,UAAU,OAAO,CAAC,CAAC;AACpE;AAAA,MACF;AACE,eAAO,EAAE,SAAS,QAAQ,CAAC,GAAG,OAAO;AAAA,IACzC;AACA,UAAM,IAAgB,EAAE,SAAS,QAAQ,QAAQ,OAAO,EAAE,WAAW,EAAE;AACvE,QAAI,OAAO,EAAE,mBAAmB,SAAU,GAAE,aAAa,EAAE;AAC3D,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,MAAM,UAAU;AAC7B,QAAI,OAAO;AACX,QAAI;AACF,aAAOC,cAAa,MAAM,MAAM;AAAA,IAClC,QAAQ;AACN,aAAO,EAAE,OAAO,CAAC,GAAG,MAAM,SAAS;AAAA,IACrC;AACA,WAAO,gBAAgB,KAAK,MAAM,IAAI,GAAG,QAAQ;AAAA,EACnD;AAAA,EAEA,MAAM,SAAS,YAAY,OAAqB;AAC9C,QAAI,CAAC,WAAY,QAAO,CAAC;AACzB,WAAO,iBAAiB,YAAY;AAAA,MAClC,cAAc,QAAQ;AAAA,MACtB,YAAa,MAAM,eAAwC,QAAQ,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC;AAAA,MAC1F,KAAK,QAAQ;AAAA,IACf,CAAC;AAAA,EACH;AAAA,EAEA,SAAS,SAAS,MAAM,QAAQ,KAAoB;AAClD,UAAMC,QAAsB,CAAC;AAC7B,eAAW,QAAQ,SAAS,GAAG,GAAG;AAChC,UAAI,MAAgB,CAAC;AACrB,UAAI;AACF,cAAMC,aAAYN,MAAK,KAAK,MAAM,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,mBAAmB,KAAK,CAAC,CAAC;AAAA,MACtF,QAAQ;AAAA,MAAC;AACT,iBAAW,MAAM,KAAK;AACpB,cAAM,IAAIA,MAAK,KAAK,MAAM,SAAS,IAAI,qBAAqB,QAAQ,kBAAkB;AACtF,YAAI;AACF,cAAIO,UAAS,CAAC,EAAE,UAAU,QAAS;AAAA,QACrC,QAAQ;AACN;AAAA,QACF;AACA,cAAM,QAAQ,iBAAiB,GAAG,EAAE,cAAc,IAAI,YAAY,CAAC,GAAG,IAAI,CAAC;AAC3E,YAAI,CAAC,MAAM,cAAc,CAAC,MAAM,QAAS;AACzC,cAAM,EAAE,YAAY,SAAS,WAAW,KAAK,iBAAiB,KAAK,GAAG,KAAK,IAAI;AAC/E,QAAAF,MAAK,KAAK,EAAE,SAAS,EAAE,IAAI,GAAG,KAAK,GAAG,YAAY,QAAQ,CAAC;AAAA,MAC7D;AAAA,IACF;AACA,WAAOA;AAAA,EACT;AACF;AAEA,SAASF,OAAyC,GAAS;AACzD,aAAW,KAAK,OAAO,KAAK,CAAC,EAAG,KAAI,EAAE,CAAC,MAAM,OAAW,QAAO,EAAE,CAAC;AAClE,SAAO;AACT;AAGO,SAAS,OAAO,GAA2B;AAChD,MAAI,OAAO,MAAM,YAAY,CAAC,EAAG,QAAO;AACxC,MAAI,IAAI;AACR,MAAI,UAAU,KAAK,CAAC,GAAG;AACrB,QAAI;AACF,UAAI,mBAAmB,IAAI,IAAI,CAAC,EAAE,QAAQ;AAAA,IAC5C,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,QAAQ,aAAa,WAAW,eAAe,KAAK,CAAC,EAAG,KAAI,EAAE,MAAM,CAAC;AAAA,EAC3E,WAAW,2BAA2B,KAAK,CAAC,EAAG,QAAO;AACtD,MAAI,CAACH,MAAK,WAAW,CAAC,EAAG,QAAO;AAChC,SAAOA,MAAK,QAAQ,CAAC;AACvB;AACO,IAAM,eAAe,CAAC,WAC1B,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,GAC9B,IAAI,MAAM,EACV,OAAO,CAAC,MAAmB,CAAC,CAAC,CAAC,EAC9B,OAAO,CAAC,GAAG,GAAG,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC;AAE3C,SAAS,WAAW,GAAqB;AACvC,QAAM,MAAM,CAAC,CAAC;AACd,MAAI;AACF,UAAM,IAAI,aAAa,OAAO,CAAC;AAC/B,QAAI,MAAM,EAAG,KAAI,KAAK,CAAC;AAAA,EACzB,QAAQ;AAAA,EAAC;AACT,SAAO;AACT;AACA,SAAS,WAAW,KAAkD;AACpE,QAAM,MAA2C,CAAC;AAClD,MAAI,QAAkB,CAAC;AACvB,MAAI;AACF,YAAQM,aAAY,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AAAA,EAC5D,QAAQ;AACN,WAAO;AAAA,EACT;AACA,aAAW,KAAK,OAAO;AACrB,UAAM,KAAK,EAAE,MAAM,GAAG,EAAE;AACxB,QAAI,CAAC,MAAM,KAAK,EAAE,EAAG;AACrB,QAAI;AACJ,QAAI;AACF,UAAI,KAAK,MAAMF,cAAaJ,MAAK,KAAK,KAAK,CAAC,GAAG,MAAM,CAAC;AAAA,IACxD,QAAQ;AACN;AAAA,IACF;AACA,UAAM,UAAoB,CAAC;AAI3B,eAAW,KAAK,GAAG,kBAAkB,aAAa,CAAC,GAAG;AACpD,YAAM,IAAI,OAAO,GAAG,aAAa,GAAG,WAAW,SAAS;AACxD,UAAI,EAAG,SAAQ,KAAK,GAAG,WAAW,CAAC,CAAC;AAAA,IACtC;AACA,QAAI,QAAQ,OAAQ,KAAI,KAAK,EAAE,IAAI,QAAQ,CAAC;AAAA,EAC9C;AACA,SAAO;AACT;AAEO,SAAS,eAAe,UAA+C,YAAqC;AACjH,QAAM,KAAe,CAAC;AACtB,aAAW,KAAK,YAAY;AAC1B,UAAM,IAAI,OAAO,CAAC;AAClB,QAAI,EAAG,IAAG,KAAK,GAAG,WAAW,CAAC,CAAC;AAAA,EACjC;AACA,MAAI,OAAsB;AAC1B,MAAI,UAAU;AACd,aAAW,MAAM;AACf,eAAW,KAAK,GAAG,SAAS;AAC1B,YAAM,SAAS,GAAG,KAAK,CAAC,MAAM,MAAM,KAAK,EAAE,WAAW,EAAE,SAASA,MAAK,GAAG,IAAI,IAAI,IAAIA,MAAK,GAAG,CAAC;AAC9F,UAAI,UAAU,EAAE,SAAS,SAAS;AAChC,eAAO,GAAG;AACV,kBAAU,EAAE;AAAA,MACd;AAAA,IACF;AACF,SAAO;AACT;AAIO,SAAS,qBACd,YACA,MACe;AACf,QAAM,IAAI,WAAW,QAAQ,GAAGA,MAAK,GAAG,QAAQA,MAAK,GAAG,EAAE;AAC1D,QAAM,QAAQ,IAAI,IAAI,CAAC,WAAW,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,SAAS,KAAK,GAAG,CAAC;AAC9E,aAAW,KAAK,MAAM;AACpB,QAAI;AACF,YAAM,IAAI,0CAA0C;AAAA,QAClDI,cAAaJ,MAAK,KAAK,GAAG,yBAAyB,GAAG,MAAM;AAAA,MAC9D;AACA,UAAI,CAAC,EAAG;AACR,YAAM,OAAO,oCAAoC,EAAE,CAAC,CAAC;AACrD,YAAM,OAAO,KAAK,gBAAgB;AAClC,UAAI,CAAC,MAAM,KAAK,IAAI,KAAK,CAAC,KAAK,YAAY,OAAQ,QAAO;AAC1D,YAAM,UAAU,eAAe,WAAW,KAAK,eAAe,YAAY,KAAK,GAAG,CAAC,GAAG,KAAK,UAAU;AACrG,aAAO,UAAU,GAAG,IAAI,MAAM,mBAAmB,KAAK,IAAI,YAAY,OAAO,EAAE,CAAC,KAAK;AAAA,IACvF,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,SAAO;AACT;AAEO,SAAS,iBACd,MACA,MACc;AACd,QAAM,EAAE,MAAM,KAAK,IAAI,KAAK,MAAM,MAAM,MAAM,KAAK,IAAI;AACvD,QAAM,IAAI,WAAW,IAAI;AACzB,QAAM,IAAI,WAAW,IAAI;AACzB,QAAM,MAAoB,CAAC;AAC3B,QAAM,MAAM,qBAAqB,MAAM,IAAI;AAC3C,MAAI,IAAK,KAAI,MAAM;AACnB,aAAW,KAAK,GAAG;AACjB,QAAI,EAAE,SAAS,gBAAgB,OAAO,EAAE,YAAY,SAAU;AAC9D,UAAM,IAAI,iDAAiD,KAAK,EAAE,OAAO;AACzE,UAAM,QAAQ,IAAK,EAAE,CAAC,IAAe,EAAE,SAAS,KAAK;AACrD,UAAM,QAAQ,KAAK,MAAM,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,IAAI,KAAK;AACjE,QAAI,CAAC,QAAQ,eAAe,KAAK,IAAI,EAAG;AACxC,QAAI,QAAQ,KAAK,SAAS,KAAK,GAAG,KAAK,MAAM,GAAG,EAAE,EAAE,QAAQ,WAAW,EAAE,CAAC,WAAM;AAChF,QAAI,eAAe;AACnB;AAAA,EACF;AACA,QAAM,QAAQ,EAAE,OAAO,CAAC,EACrB,IAAI,CAAC,MAAM,KAAK,MAAM,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC,CAAC,EAChE,OAAO,CAAC,MAAM,IAAI,CAAC;AACtB,MAAI,MAAM,QAAQ;AAChB,QAAI,aAAa,KAAK,IAAI,GAAG,KAAK;AAClC,QAAI,UAAU,KAAK,IAAI,GAAG,KAAK;AAAA,EACjC;AACA,SAAO;AACT;;;AC/UA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAAQ;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,SAAS,UAAU,gBAAAC,qBAAoB;AACvC,SAAS,aAAAC,YAAW,YAAAC,WAAU,eAAAC,cAAa,gBAAAC,eAAc,YAAAC,WAAU,YAAAC,iBAAgB;AACnF,OAAOC,SAAQ;AACf,OAAOC,WAAU;;;ACEV,IAAM,eAAe,KAAK;AAE1B,IAAM,UAAU,IAAI;AAGpB,IAAM,qBAAqB,KAAK;AAGhC,IAAM,sBAAsB,KAAK;AAEjC,IAAM,eAAe,KAAK;AAsB1B,IAAM,WAAW;AAGjB,IAAM,oBAAoB,CAAC,QAAQ,QAAQ,SAAS,MAAM;AAQ1D,IAAM,YAAY,CAAC,MAAuB,kBAAkB,QAAQ,CAAoB;AAwM/F,IAAM,SAAiD;AAAA,EACrD,QAAQ,CAAC,QAAQ,SAAS,UAAU;AAAA,EACpC,qBAAqB,CAAC,OAAO,UAAU;AAAA,EACvC,QAAQ,CAAC;AAAA,EACT,OAAO,CAAC,QAAQ,OAAO,SAAS,cAAc,SAAS,UAAU;AAAA,EACjE,WAAW,CAAC,OAAO,MAAM;AAAA,EACzB,MAAM,CAAC,eAAe,gBAAgB,cAAc,WAAW;AACjE;AAEA,IAAM,cAAc,OAAO;AAI3B,IAAM,MAAM,IAAI,YAAY;AAC5B,IAAM,MAAM,IAAI,YAAY;;;ADnP5B,IAAM,UAAU;AAEhB,IAAM,gBAAgB;AAGf,IAAM,aAAa,CAAC,GAAW,MAAc;AAClD,QAAM,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM;AAClC,QAAM,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM;AAClC,WAAS,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,QAAQ,GAAG,MAAM,GAAG;AAClD,SAAK,GAAG,CAAC,KAAK,QAAQ,GAAG,CAAC,KAAK,GAAI,SAAQ,GAAG,CAAC,KAAK,MAAM,GAAG,CAAC,KAAK;AACrE,SAAO;AACT;AAMO,SAAS,mBAAmB,MAAyB,QAAQ,KAAK,OAAOC,IAAG,QAAQ,GAAa;AACtG,QAAM,MAAgB,CAAC;AACvB,QAAM,WAAW,CAAC,KAAa,SAAyB;AACtD,QAAI,QAAkB,CAAC;AACvB,QAAI;AACF,cAAQC,aAAY,GAAG,EAAE,OAAO,CAAC,MAAM,kBAAkB,KAAK,CAAC,CAAC;AAAA,IAClE,QAAQ;AACN;AAAA,IACF;AACA,eAAW,KAAK,MAAM,KAAK,CAAC,GAAG,MAAM,WAAW,EAAE,QAAQ,MAAM,EAAE,GAAG,EAAE,QAAQ,MAAM,EAAE,CAAC,CAAC,EAAE,QAAQ;AACjG,UAAI,KAAKC,MAAK,KAAK,KAAK,GAAG,GAAG,IAAI,CAAC;AAAA,EACvC;AACA,WAASA,MAAK,KAAK,IAAI,WAAWA,MAAK,KAAK,MAAM,MAAM,GAAG,YAAY,MAAM,GAAG,CAAC,KAAK,CAAC;AACvF,QAAM,UAAU,IAAI,UAChB,CAAC,IAAI,OAAO,IACZ;AAAA,IACEA,MAAK,KAAK,MAAM,WAAW,uBAAuB,KAAK;AAAA,IACvDA,MAAK,KAAK,MAAM,UAAU,SAAS,KAAK;AAAA,IACxCA,MAAK,KAAK,MAAM,MAAM;AAAA,EACxB;AACJ,aAAW,KAAK,QAAS,UAASA,MAAK,KAAK,GAAG,eAAe,GAAG,CAAC,gBAAgB,KAAK,CAAC;AACxF,MAAI,KAAKA,MAAK,KAAK,IAAI,cAAcA,MAAK,KAAK,MAAM,QAAQ,GAAG,KAAK,CAAC;AACtE,WAASA,MAAK,KAAK,IAAI,iBAAiBA,MAAK,KAAK,MAAM,OAAO,GAAG,YAAY,QAAQ,GAAG,CAAC,KAAK,CAAC;AAChG,SAAO;AACT;AAEO,SAAS,WAAW,MAAyB,QAAQ,KAAK,OAAOF,IAAG,QAAQ,GAAkB;AACnG,MAAI,IAAI,sBAAsB,MAAM,IAAI,kBAAkB,EAAG,QAAO,IAAI;AACxE,QAAM,QAAQ,QAAQ,aAAa,UAAU,CAAC,cAAc,YAAY,IAAI,CAAC,QAAQ;AACrF,aAAW,KAAK,OAAO,IAAI,QAAQ,EAAE,EAAE,MAAME,MAAK,SAAS,GAAG;AAC5D,QAAI,CAAC,KAAK,CAACA,MAAK,WAAW,CAAC,EAAG;AAC/B,eAAW,KAAK,MAAO,KAAI,MAAMA,MAAK,KAAK,GAAG,CAAC,CAAC,EAAG,QAAOA,MAAK,KAAK,GAAG,CAAC;AAAA,EAC1E;AACA,aAAW,KAAK;AAAA,IACdA,MAAK,KAAK,MAAM,WAAW,SAAS,QAAQ;AAAA,IAC5CA,MAAK,KAAK,MAAM,UAAU,OAAO,QAAQ;AAAA,IACzC;AAAA,IACA;AAAA,EACF;AACE,QAAI,MAAM,CAAC,EAAG,QAAO;AAIvB,aAAW,KAAK,mBAAmB,KAAK,IAAI,EAAG,KAAI,MAAMA,MAAK,KAAK,GAAG,QAAQ,CAAC,EAAG,QAAOA,MAAK,KAAK,GAAG,QAAQ;AAC9G,QAAM,MAAMA,MAAK,KAAK,MAAM,WAAW,uBAAuB,UAAU,aAAa;AACrF,MAAI,WAAqB,CAAC;AAC1B,MAAI;AACF,eAAWD,aAAY,GAAG,EACvB,OAAO,CAAC,MAAM,gBAAgB,KAAK,CAAC,CAAC,EACrC,KAAK,UAAU,EACf,QAAQ;AAAA,EACb,QAAQ;AAAA,EAAC;AACT,aAAW,KAAK,UAAU;AACxB,UAAM,IAAIC,MAAK,KAAK,KAAK,GAAG,cAAc,YAAY,SAAS,QAAQ;AACvE,QAAI,MAAM,CAAC,EAAG,QAAO;AAAA,EACvB;AACA,SAAO;AACT;AAGA,SAAS,YAAY,MAAc,MAA6B;AAC9D,QAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,QAAM,KAAK,IAAI,OAAO,uBAAuB,KAAK,QAAQ,MAAM,KAAK,CAAC,YAAY;AAClF,QAAM,QAAQ,MAAM,UAAU,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;AAC/C,MAAI,QAAQ,EAAG,QAAO;AACtB,QAAM,MAAM,CAAC,MAAM,KAAK,CAAW;AACnC,WAAS,IAAI,QAAQ,GAAG,IAAI,MAAM,UAAU,MAAM,CAAC,GAAG,KAAK,KAAK,CAAC,YAAY,KAAK,MAAM,CAAC,CAAW,GAAG;AACrG,QAAI,KAAK,MAAM,CAAC,CAAW;AAC7B,SAAO,IAAI,KAAK,GAAG;AACrB;AAgBO,SAAS,UAAU,MAAoB;AAC5C,QAAM,KAAK,YAAY,MAAM,mBAAmB;AAChD,QAAM,KAAK,YAAY,MAAM,sBAAsB;AACnD,SAAO;AAAA,IACL,SAAS,CAAC,CAAC;AAAA,IACX,OAAO,KAAK,CAAC,GAAG,GAAG,SAAS,gBAAgB,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAW,IAAI,CAAC;AAAA,IAC7E,aAAa,CAAC,CAAC,MAAM,SAAS,KAAK,EAAE;AAAA,IACrC,MAAM,CAAC,CAAC,YAAY,MAAM,gBAAgB;AAAA,IAC1C,QAAQ,CAAC,CAAC,YAAY,MAAM,4BAA4B;AAAA,EAC1D;AACF;AAGA,IAAM,aAA+D;AAAA,EACnE,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AACR;AAUO,SAAS,UAAU,OAAiB,MAAoD;AAC7F,MAAI,UAAU,UAAW,QAAO,EAAE,MAAM,CAAC,EAAE;AAC3C,MAAI,KAAK;AACP,WAAO,EAAE,OAAO,yDAAyD,KAAK,MAAM,2BAA2B;AACjH,MAAI,UAAU,UAAU,CAAC,KAAK,MAAM,SAAS,MAAM;AACjD,WAAO,EAAE,OAAO,gFAAgF;AAClG,MAAI,UAAU,QAAQ;AACpB,UAAM,IAAI,WAAW,KAAK;AAC1B,QAAI,CAAC,KAAK,MAAM,SAAS,CAAC,EAAG,QAAO,EAAE,OAAO,qCAAqC,CAAC,mBAAmB;AACtG,WAAO,EAAE,MAAM,CAAC,qBAAqB,CAAC,EAAE;AAAA,EAC1C;AACA,MAAI,KAAK,MAAM,SAAS,SAAS,EAAG,QAAO,EAAE,MAAM,CAAC,qBAAqB,SAAS,EAAE;AACpF,QAAM,OAAiB,CAAC;AACxB,MAAI,KAAK;AACP,SAAK;AAAA,MACH;AAAA,MACA,KAAK,MAAM,SAAS,QAAQ,KAAK,CAAC,KAAK,MAAM,SAAS,SAAS,IAAI,WAAW;AAAA,IAChF;AACF,MAAI,KAAK,YAAa,MAAK,KAAK,wBAAwB,MAAM;AAC9D,SAAO,EAAE,KAAK;AAChB;AAKO,IAAM,eAAe,CAAC,SAAiC,SAAS,YAAY,SAAS;AAMrF,SAAS,cAAc,MAAY,MAA+B;AACvE,MAAI,KAAK,OAAQ,QAAO,CAAC;AACzB,SAAO,kBAAkB;AAAA,IACvB,CAAC,MAAM,UAAU,CAAC,KAAK,UAAU,aAAa,IAAI,CAAC,MAAM,MAAM,UAAU,KAAK,MAAM,SAAS,WAAW,CAAC,CAAC;AAAA,EAC5G;AACF;AAGO,IAAM,eAAe,CAAC,SAAyB;AAG/C,SAAS,WAAW,MAAsC;AAC/D,SAAO,KAAK,SACR,CAAC,mBAAmB,eAAe,aAAa,4BAA4B,IAC5E,CAAC,mBAAmB,MAAM;AAChC;AAKO,SAAS,WAAW,GASgB;AACzC,QAAM,KAAK,UAAU,EAAE,OAAO,EAAE,IAAI;AACpC,MAAI,WAAW,GAAI,QAAO;AAC1B,QAAM,QAAQ,EAAE,SACZ,CAAC,YAAY,EAAE,QAAQ,GAAI,EAAE,OAAO,CAAC,gBAAgB,IAAI,CAAC,CAAE,IAC5D,CAAC,gBAAgB,EAAE,OAAiB;AACxC,SAAO;AAAA,IACL,MAAM;AAAA,MACJ;AAAA,MACA,GAAG,WAAW,EAAE,IAAI;AAAA,MACpB,GAAG,GAAG;AAAA,MACN,GAAI,EAAE,QAAQ,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC;AAAA,MACtC,GAAI,EAAE,cAAc,SAAS,CAAC,kBAAkB,EAAE,aAAa,KAAK,GAAG,CAAC,EAAE,IAAI,CAAC;AAAA,MAC/E,GAAG;AAAA,MACH,EAAE,OAAO,WAAW,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,EAAE;AAAA,IAChD;AAAA,EACF;AACF;AAKO,SAAS,UAAU,WAAoB,MAAyB,QAAQ,KAAwB;AACrG,QAAM,IAAI,eAAe,GAAG;AAC5B,aAAW,KAAK;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACE,WAAO,EAAE,CAAC;AACZ,IAAE,0BAA0B;AAC5B,MAAI,cAAc,QAAW;AAC3B,QAAIA,MAAK,QAAQ,SAAS,MAAMA,MAAK,QAAQF,IAAG,QAAQ,GAAG,SAAS,EAAG,QAAO,EAAE;AAAA,QAC3E,GAAE,oBAAoB;AAAA,EAC7B;AACA,SAAO;AACT;AAKO,SAAS,gBAAgB,QAA0C;AACxE,QAAM,OAAO,OAAO,UAAU,EAAE,EAAE,KAAK;AACvC,QAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,MAAI,QAAQ,EAAG,QAAO,EAAE,UAAU,KAAK;AACvC,MAAI;AACF,UAAM,IAAI,KAAK,MAAM,KAAK,MAAM,KAAK,CAAC;AACtC,QAAI,OAAO,EAAE,aAAa,UAAW,QAAO,EAAE,UAAU,KAAK;AAC7D,WAAO;AAAA,MACL,UAAU,EAAE;AAAA,MACZ,GAAI,OAAO,EAAE,eAAe,WAAW,EAAE,QAAQ,EAAE,WAAW,IAAI,CAAC;AAAA,MACnE,GAAI,OAAO,EAAE,qBAAqB,WAAW,EAAE,cAAc,EAAE,iBAAiB,IAAI,CAAC;AAAA,IACvF;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,UAAU,KAAK;AAAA,EAC1B;AACF;AAOO,SAASG,aAAY,KAAa,WAAoB,MAAyB,QAAQ,KAAqB;AAGjH,QAAM,IAAI,MAAM,KAAK,CAAC,QAAQ,UAAU,QAAQ,CAAC;AACjD,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B;AAAA,MACE,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE,UAAU,QAAQ,SAAS,KAAQ,KAAK,UAAU,WAAW,GAAG,GAAG,WAAW,MAAM,MAAM,OAAO,EAAE,MAAM;AAAA;AAAA,MAE3G,CAAC,MAAM,WAAW,QAAQ,gBAAgB,MAAM,CAAC;AAAA,IACnD;AAAA,EACF,CAAC;AACH;AAGO,SAAS,aAAa,WAAuC;AAClE,QAAM,OAAOH,IAAG,QAAQ;AACxB,QAAM,YAAY,CAAC,aAAaE,MAAK,QAAQ,SAAS,MAAMA,MAAK,QAAQ,MAAM,SAAS;AACxF,SAAO,YAAY,wBAAwB,uBAAuB,UAAU,QAAQ,MAAM,GAAG,CAAC;AAChG;AAGO,SAAS,UAAU,WAA+B,WAA4B,QAAQ,UAAkB;AAC7G,QAAM,QAAQ,YAAY,UAAU,QAAQF,IAAG,QAAQ,GAAG,GAAG,IAAI;AACjE,QAAM,QAAQ,aAAa,SAAS;AACpC,MAAI,aAAa;AACf,WAAO,gBAAgB,KAAK,gOAAgO,KAAK;AACnQ,SAAO,gBAAgB,KAAK,0CAA0C,KAAK;AAC7E;AAKO,SAAS,WAAW,KAAa,YAAY,KAAc;AAChE,QAAM,IAAI,MAAM,KAAK,CAAC,QAAQ,CAAC;AAC/B,MAAI;AACF,WAAO;AAAA,MACLI,cAAa,EAAE,MAAM,EAAE,MAAM;AAAA,QAC3B,UAAU;AAAA,QACV,SAAS;AAAA,QACT,KAAK,UAAU;AAAA,QACf,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,QAChC,WAAW,IAAI,OAAO;AAAA,QACtB,OAAO,EAAE;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF,SAAS,GAAG;AACV,UAAM,MAAM;AACZ,UAAM,OAAO,UAAU,OAAO,KAAK,UAAU,EAAE,CAAC;AAChD,QAAI,KAAK,QAAS,QAAO;AACzB,UAAM,MAAM,KAAK,SAAS,cAAc,mBAAmB,YAAY,GAAI,OAAO;AAClF,WAAO,EAAE,GAAG,MAAM,QAAQ,KAAKF,MAAK,SAAS,GAAG,CAAC,aAAa,GAAG,GAAG;AAAA,EACtE;AACF;AASA,SAAS,aAAa,SAAiB,KAAgC;AACrE,MAAI,WAAuC,CAAC;AAC5C,MAAI;AACF,eAAWD,aAAYC,MAAK,KAAK,KAAK,UAAU,GAAG,EAAE,eAAe,KAAK,CAAC;AAAA,EAC5E,QAAQ;AACN,WAAO;AAAA,EACT;AACA,aAAW,KAAK,UAAU;AACxB,QAAI,CAAC,EAAE,YAAY,EAAG;AACtB,UAAM,OAAOA,MAAK,KAAK,KAAK,YAAY,EAAE,MAAM,GAAG,OAAO,QAAQ;AAClE,QAAI;AACF,UAAIG,UAAS,IAAI,EAAE,OAAO,EAAG,QAAO,EAAE,MAAM,WAAW,KAAK,SAAS,EAAE,KAAK;AAAA,IAC9E,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,SAAO;AACT;AAGO,SAAS,eAAe,SAAiB,MAA4C;AAC1F,MAAI,CAAC,QAAQ,KAAK,OAAO,EAAG,QAAO;AACnC,aAAW,OAAO,MAAM;AACtB,UAAM,IAAI,aAAa,SAAS,GAAG;AACnC,QAAI,EAAG,QAAO;AAAA,EAChB;AACA,SAAO;AACT;AAOO,SAAS,gBAAgB,SAAiB,MAAuC;AACtF,MAAI,CAAC,QAAQ,KAAK,OAAO,EAAG,QAAO,CAAC;AACpC,QAAM,MAAoB,CAAC;AAC3B,aAAW,OAAO,MAAM;AACtB,UAAM,IAAI,aAAa,SAAS,GAAG;AACnC,QAAI,EAAG,KAAI,KAAK,CAAC;AAAA,EACnB;AACA,SAAO;AACT;AAUA,IAAM,WAAW,CAAC,MAAkBH,MAAK,SAAS,EAAE,SAAS;AAEtD,SAAS,YAAY,SAAiB,MAA4C;AACvF,QAAM,MAAM,gBAAgB,SAAS,IAAI;AACzC,MAAI,CAAC,IAAI,OAAQ,QAAO;AACxB,MAAI,IAAI,WAAW,EAAG,QAAO,EAAE,YAAY,IAAI,CAAC,EAAgB;AAChE,QAAM,OAAO,IAAI,OAAO,CAAC,MAAM,YAAY,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;AAC3E,MAAI,KAAK,WAAW,EAAG,QAAO,EAAE,YAAY,KAAK,CAAC,EAAgB;AAClE,SAAO;AAAA,IACL,WAAW,CAAC,GAAG,IAAI,IAAI,IAAI,IAAI,QAAQ,CAAC,CAAC,EAAE,KAAK;AAAA,IAChD,MAAM,CAAC,GAAG,IAAI,IAAI,KAAK,IAAI,QAAQ,CAAC,CAAC,EAAE,KAAK;AAAA,EAC9C;AACF;AAEA,IAAM,cAAc,CAAC,QAAgB,IAAI,QAAQ,iBAAiB,GAAG;AAK9D,SAAS,cAAc,GAAeI,OAAM,IAAI,OAAO,MAAqB;AACjF,MAAI,QAAuB;AAC3B,MAAI,QAAQ;AACZ,MAAI;AACJ,QAAM,OAAO,CAAC,SAAgC;AAC5C,QAAI,KAAK,QAAQ,OAAO,IAAI,EAAG,QAAO;AACtC,QAAI;AACJ,QAAI;AACF,UAAI,KAAK,MAAM,KAAK,SAAS,MAAM,CAAC;AAAA,IACtC,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,CAAC,KAAK,OAAO,EAAE,QAAQ,YAAY,CAACJ,MAAK,WAAW,EAAE,GAAG,EAAG,QAAO;AACvE,QAAI,YAAY,EAAE,GAAG,MAAM,EAAE,QAAS,QAAO,EAAE;AAC/C,cAAU,EAAE;AACZ;AACA,WAAO;AAAA,EACT;AACA,MAAI;AACF,SAAKK,UAAS,EAAE,MAAM,GAAG;AACzB,UAAM,MAAM,OAAO,MAAM,MAAM,IAAI;AACnC,QAAI,MAAM;AACV,QAAI,OAAO,OAAO,MAAM,CAAC;AACzB,QAAI,WAAW;AACf,WAAO,MAAMD,QAAO,QAAQ,IAAI;AAC9B,YAAM,IAAIE,UAAS,IAAI,KAAK,GAAG,IAAI,QAAQ,GAAG;AAC9C,UAAI,CAAC,GAAG;AACN,YAAI,CAAC,YAAY,KAAK,QAAQ;AAC5B,gBAAM,MAAM,KAAK,IAAI;AACrB,cAAI,IAAK,QAAO;AAAA,QAClB;AACA;AAAA,MACF;AACA,aAAO;AACP,UAAI,QAAQ,KAAK,SAAS,OAAO,OAAO,CAAC,MAAM,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,SAAS,GAAG,CAAC;AACvF,UAAI,KAAK,MAAM,QAAQ,EAAE;AACzB,aAAO,MAAM,GAAG;AACd,cAAM,OAAO,MAAM,SAAS,GAAG,EAAE;AACjC,gBAAQ,MAAM,SAAS,KAAK,CAAC;AAC7B,YAAI,SAAU,YAAW;AAAA,aACpB;AACH,gBAAM,MAAM,KAAK,IAAI;AACrB,cAAI,IAAK,QAAO;AAAA,QAClB;AACA,aAAK,MAAM,QAAQ,EAAE;AAAA,MACvB;AACA,UAAI,MAAM,SAAS,IAAI,OAAO,MAAM;AAClC,mBAAW;AACX,eAAO,OAAO,MAAM,CAAC;AAAA,MACvB,MAAO,QAAO,WAAW,OAAO,MAAM,CAAC,IAAI,OAAO,KAAK,KAAK;AAAA,IAC9D;AAAA,EACF,QAAQ;AAAA,EACR,UAAE;AACA,QAAI,OAAO;AACT,UAAI;AACF,QAAAC,WAAU,EAAE;AAAA,MACd,QAAQ;AAAA,MAAC;AAAA,EACb;AACA,SAAO;AACT;AAEA,SAAS,SAAS,KAAsB;AACtC,MAAI,CAAC,OAAO,cAAc,GAAG,KAAK,OAAO,EAAG,QAAO;AACnD,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,SAAS,GAAG;AACV,WAAQ,EAAwB,SAAS;AAAA,EAC3C;AACF;AA0BA,IAAM,cAAc,CAAC,MAAc,WAAmB,MAA2C;AAC/F,QAAM,MAAM,OAAO,EAAE,OAAOP,MAAK,SAAS,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC;AAC5D,QAAM,MAAM,CAAC,MAAgB,OAAO,MAAM,YAAY,IAAI,IAAI;AAC9D,MAAI,UAAyB;AAC7B,MAAI;AAEF,UAAM,OAAO,IAAI,OAAO,IAAI,GAAG,wBAAwB;AACvD,UAAM,MAAMD,aAAYC,MAAK,QAAQ,IAAI,CAAC,EAAE,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC;AACpE,QAAI,IAAK,WAAUA,MAAK,KAAKA,MAAK,QAAQ,IAAI,GAAG,GAAG;AAAA,EACtD,QAAQ;AAAA,EAAC;AACT,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,IAAI,EAAE,mBAAmB;AAAA,IACjC,cAAc,OAAO,EAAE,iBAAiB,WAAW,EAAE,eAAe;AAAA,IACpE,WAAW,IAAI,EAAE,SAAS;AAAA,IAC1B,QAAQ,IAAI,EAAE,MAAM;AAAA,IACpB,MAAM,IAAI,EAAE,IAAI;AAAA,IAChB,YAAY,IAAI,EAAE,UAAU;AAAA,IAC5B;AAAA,EACF;AACF;AAIO,SAAS,YAAY,SAAiB,MAAgD;AAC3F,QAAM,MAAoB,CAAC;AAC3B,aAAW,OAAO,OAAO,SAAS,WAAW,CAAC,IAAI,IAAI,MAAM;AAC1D,QAAI,QAAkB,CAAC;AACvB,QAAI;AACF,cAAQD,aAAYC,MAAK,KAAK,KAAK,UAAU,CAAC,EAAE,OAAO,CAAC,MAAM,cAAc,KAAK,CAAC,CAAC;AAAA,IACrF,QAAQ;AACN;AAAA,IACF;AACA,eAAW,KAAK,OAAO;AACrB,YAAM,OAAOA,MAAK,KAAK,KAAK,YAAY,CAAC;AACzC,UAAI;AACJ,UAAI;AACF,YAAI,KAAK,MAAMQ,cAAa,MAAM,MAAM,CAAC;AAAA,MAC3C,QAAQ;AACN;AAAA,MACF;AACA,UAAI,OAAO,EAAE,SAAS,MAAM,QAAS;AACrC,YAAM,MAAM,YAAY,MAAM,KAAK,CAAC;AACpC,UAAI,SAAS,IAAI,GAAG,EAAG,KAAI,KAAK,GAAG;AAAA,IACrC;AAAA,EACF;AACA,SAAO;AACT;AAKO,SAAS,WAAW,SAAiB,MAAqD;AAC/F,QAAM,MAAM,YAAY,SAAS,IAAI;AACrC,SAAO,IAAI,WAAW,IAAK,IAAI,CAAC,IAAmB;AACrD;AAKO,SAAS,YAAY,SAAiB,WAA4B;AACvE,SAAO,YAAY,SAAS,SAAS,EAAE,SAAS;AAClD;AAUO,IAAM,YAAoC;AAAA,EAC/C,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM;AAAA,EACN,mBAAmB;AACrB;AACO,IAAM,eAAe,OAAO,KAAK,SAAS;AAC1C,IAAM,WAAW,CAAC,MAA0C,KAAK,KAAK,YAAa,UAAU,CAAC,IAAe;AAU7G,SAAS,SAAS,SAAiC;AACxD,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAM,IAAI;AACV,QAAM,IAAI,OAAO,EAAE,oBAAoB,WAAW,EAAE,kBAAkB,EAAE;AACxE,SAAO,OAAO,MAAM,YAAY,aAAa,SAAS,CAAC,IAAI,IAAI;AACjE;AAOO,SAAS,UAAU,MAAgE;AACxF,MAAI,SAAS,oBAAqB,QAAO;AACzC,MAAI,SAAS,aAAa,SAAS,YAAY,SAAS,aAAa,SAAS,cAAe,QAAO;AACpG,SAAO;AACT;AAqBO,IAAM,mBAAiC,EAAE,aAAa,MAAM,iBAAiB,OAAO,eAAe,MAAM;AAEzG,SAAS,aAAa,WAA+B,OAAOV,IAAG,QAAQ,GAAiB;AAC7F,QAAM,OAAOE,MAAK,KAAK,aAAaA,MAAK,KAAK,MAAM,SAAS,GAAG,eAAe;AAC/E,MAAI;AACJ,MAAI;AACF,QAAI,KAAK,MAAMQ,cAAa,MAAM,MAAM,CAAC;AAAA,EAC3C,QAAQ;AACN,WAAO,EAAE,GAAG,iBAAiB;AAAA,EAC/B;AACA,MAAI,CAAC,KAAK,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,EAAG,QAAO,EAAE,GAAG,iBAAiB;AAClF,QAAM,IAAK,EAAE,eAAe,OAAO,EAAE,gBAAgB,WAAW,EAAE,cAAc,CAAC;AAGjF,QAAM,MAAM,CAAC,MAAc,EAAE,CAAC,MAAM,aAAa,EAAE,CAAC,MAAM;AAC1D,SAAO;AAAA,IACL,aAAa,OAAO,EAAE,gBAAgB,YAAY,aAAa,SAAS,EAAE,WAAW,IAAI,EAAE,cAAc;AAAA,IACzG,iBAAiB,IAAI,iBAAiB;AAAA,IACtC,eAAe,IAAI,8BAA8B;AAAA,EACnD;AACF;AAyBO,SAAS,aAAa,UAAqC,MAAmC;AACnG,MAAI,aAAa,OAAQ,QAAO;AAChC,OAAK,aAAa,aAAa,aAAa,kBAAkB,SAAS,KAAK,WAAW,KAAK,SAAS,QAAQ;AAC3G,WAAO;AACT,SAAO;AACT;AAGO,SAAS,gBAAgB,MAAc,YAAqB,MAAM,KAAK,IAAI,GAAY;AAC5F,MAAI;AACJ,MAAI;AACF,QAAIL,UAAS,IAAI,EAAE;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,MAAM,IAAI,cAAe,QAAO;AACpC,SAAO,EAAE,cAAc,KAAK,aAAa;AAC3C;AAIO,SAAS,UAAU,KAAa,MAAgB,GAAmC;AACxF,SAAO,YAAY,KAAK,MAAM,EAAE,GAAG,GAAG,MAAM,EAAE,SAAS,CAAC,SAAS,KAAK,SAAS,iBAAiB,GAAG,CAAC;AACtG;AAYO,SAAS,YAAY,QAAwD;AAClF,QAAM,IAAI,OAAO,UAAU,EAAE,EAAE,KAAK;AACpC,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,OAAO,CAAC,MAAoC;AAChD,QAAI,MAAM,QAAQ,CAAC,GAAG;AACpB,eAAS,IAAI,EAAE,SAAS,GAAG,KAAK,GAAG,IAAK,KAAI,EAAE,CAAC,GAAG,SAAS,SAAU,QAAO,EAAE,CAAC;AAC/E,aAAO;AAAA,IACT;AACA,WAAO,KAAK,OAAO,MAAM,WAAY,IAAqB;AAAA,EAC5D;AACA,MAAI;AACF,WAAO,KAAK,KAAK,MAAM,CAAC,CAAC;AAAA,EAC3B,QAAQ;AAAA,EAAC;AACT,QAAM,QAAQ,EAAE,MAAM,IAAI;AAC1B,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,QAAI;AACF,YAAM,IAAI,KAAK,KAAK,MAAM,MAAM,CAAC,CAAW,CAAC;AAC7C,UAAI,MAAM,EAAE,SAAS,YAAY,YAAY,GAAI,QAAO;AAAA,IAC1D,QAAQ;AAAA,IAAC;AAAA,EACX;AACA,SAAO;AACT;AAEO,IAAM,SAAS,CAAC,MAA4B,OAAO,MAAM,YAAY,QAAQ,KAAK,CAAC;;;AEjuB1F,SAAS,cAAAM,aAAY,eAAAC,cAAa,gBAAAC,qBAAoB;AACtD,OAAOC,WAAU;;;ACAjB,IAAM,cAAc;AACpB,IAAM,aAAa;AAEZ,IAAMC,QAAO;AAEpB,SAAS,KAAK,MAAc,IAAiD;AAC3E,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,QAAQ;AACtB,QAAI,GAAG,KAAK;AACV,YAAM,IAAI,KAAK,QAAQ,GAAG,KAAK,CAAC;AAChC,UAAI,IAAI,EAAG;AACX,UAAI,IAAI;AACR,SAAG,MAAM;AACT;AAAA,IACF;AACA,UAAM,KAAK,KAAK,CAAC;AACjB,QAAI,OAAO,IAAK;AAChB,QAAI,KAAK,WAAW,OAAO,CAAC,KAAK,KAAK,WAAW,OAAO,CAAC,GAAG;AAC1D,SAAG,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAC5B,WAAK;AACL;AAAA,IACF;AACA,QAAI,OAAO,KAAK;AACd;AACA,aAAO,IAAI,KAAK,UAAU,KAAK,CAAC,MAAM,IAAK,MAAK,KAAK,CAAC,MAAM,OAAO,IAAI;AACvE;AACA;AAAA,IACF;AACA,QAAI,OAAO,KAAK;AACd,YAAM,IAAI,KAAK,QAAQ,KAAK,IAAI,CAAC;AACjC,UAAI,IAAI,IAAI,KAAK,SAAS,IAAI;AAC9B;AAAA,IACF;AACA,QAAI,OAAO,IAAK,IAAG;AAAA,aACV,OAAO,IAAK,IAAG,QAAQ,KAAK,IAAI,GAAG,GAAG,QAAQ,CAAC;AACxD;AAAA,EACF;AACF;AAGO,SAAS,aAAa,MAAsB;AACjD,QAAM,KAAK,EAAE,OAAO,GAAG,KAAK,KAAsB;AAClD,MAAI,MAAM;AACV,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,QAAI,CAAC,GAAG,OAAO,GAAG,UAAU,KAAK,YAAY,KAAK,IAAI,EAAG,QAAO;AAChE,SAAK,MAAM,EAAE;AACb,WAAO,KAAK,SAAS;AAAA,EACvB;AACA,SAAO;AACT;AAEA,SAAS,MAAM,MAAc;AAC3B,QAAM,KAAK,aAAa,IAAI;AAC5B,SAAO,KAAK,IAAI,EAAE,MAAM,MAAM,MAAM,IAAI,IAAI,KAAK,OAAO,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,EAAE,GAAG,MAAM,KAAK,MAAM,EAAE,GAAG,GAAG;AAClH;AAEO,SAAS,UAAU,KAA8B;AACtD,MAAI;AACF,UAAM,IAAI,KAAK,MAAM,GAAG;AACxB,WAAO,MAAM,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,OAAO,MAAM,QAAQ,IAAK,IAAiB;AAAA,EACvF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYO,SAAS,WAAW,MAA6B;AACtD,QAAM,EAAE,MAAM,MAAM,GAAG,IAAI,MAAM,IAAI;AACrC,QAAM,IAAI,YAAY,KAAK,IAAI;AAC/B,MAAI,GAAG;AACL,UAAM,MAAM,EAAE,CAAC;AACf,WAAO,EAAE,KAAK,MAAM,UAAU,GAAG,GAAG,OAAO,EAAE,OAAO,QAAQ,EAAE,CAAC,EAAE,QAAQ,WAAW,OAAO,OAAO,CAACA,MAAK,KAAK,GAAG,EAAE;AAAA,EACpH;AACA,QAAM,IAAI,WAAW,KAAK,IAAI;AAC9B,MAAI,GAAG;AACL,UAAM,OAAO,KAAK,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC;AAC9C,WAAO;AAAA,MACL,KAAK,KAAK,QAAQ,YAAY,EAAE,EAAE,KAAK;AAAA,MACvC,MAAM;AAAA,MACN,OAAO,EAAE;AAAA,MACT,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,OAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,IAAI,YAAY,KAAK,IAAI;AAC/B,MAAI,GAAG;AACL,UAAM,MAAM,EAAE,CAAC;AACf,QAAIA,MAAK,KAAK,GAAG;AACf,aAAO,EAAE,KAAK,MAAM,UAAU,GAAG,GAAG,OAAO,KAAK,EAAE,OAAO,QAAQ,EAAE,CAAC,EAAE,QAAQ,WAAW,MAAM,OAAO,MAAM;AAAA,EAChH;AACA,SAAO;AACT;AAEA,SAAS,SAAS,MAAc,OAAe,QAAwB;AACrE,SAAO,KAAK,MAAM,GAAG,KAAK,IAAI,KAAK,MAAM,QAAQ,MAAM,EAAE,QAAQ,UAAU,EAAE;AAC/E;AAIO,SAAS,eAAe,MAAc,MAAsB;AACjE,QAAM,EAAE,MAAM,KAAK,IAAI,MAAM,IAAI;AAGjC,QAAM,OAAO,KAAK,QAAQ,QAAQ,EAAE;AACpC,QAAM,OAAO,KAAK,SAAS,IAAI,IAAI,OAAO,MAAM,SAAS,MAAM,IAAI,SAAS;AAC5E,QAAM,MAAM,KAAK,KAAK,IAAI,KAAK,QAAQ,QAAQ,GAAG,IAAI;AACtD,MAAI,CAAC,KAAM,QAAO,MAAM,OAAO;AAC/B,SAAO,GAAG,MAAM,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI;AACzC;AAGO,SAAS,WAAW,MAAc,MAAsB;AAC7D,QAAM,IAAI,WAAW,IAAI;AACzB,MAAI,KAAK,CAAC,EAAE,aAAa,CAAC,EAAE,MAAO,QAAO,KAAK,MAAM,GAAG,EAAE,KAAK,IAAI,OAAO,KAAK,MAAM,EAAE,QAAQ,EAAE,MAAM;AACvG,MAAI,GAAG,UAAW,QAAO,eAAe,SAAS,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;AAC/E,SAAO,eAAe,MAAM,IAAI;AAClC;AAGO,SAAS,cAAc,MAAc,GAAW,MAAsB;AAC3E,SAAO,KAAK,MAAM,GAAG,EAAE,KAAK,IAAI,OAAO,KAAK,MAAM,EAAE,QAAQ,EAAE,MAAM;AACtE;AAGO,SAAS,cAAc,MAAsB;AAClD,QAAM,IAAI,WAAW,IAAI;AACzB,MAAI,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,OAAQ,QAAO;AACvC,SAAO,SAAS,MAAM,EAAE,OAAO,EAAE,MAAM;AACzC;AAIA,IAAM,WAAW;AACV,SAAS,eAAe,MAAwC;AACrE,MAAI,CAAC,MAAM,QAAQ,IAAI,EAAG,QAAO;AACjC,QAAM,IAAI,KAAK,QAAQ,QAAQ;AAC/B,SAAO,KAAK,KAAK,OAAO,KAAK,IAAI,CAAC,MAAM,WAAW,UAAU,KAAK,IAAI,CAAC,CAAW,IAAI;AACxF;AASO,SAAS,iBAAiB,KAAqB;AACpD,SAAO,IAAI,QAAQ,yDAAyD,EAAE;AAChF;AAEO,SAAS,gBAAgB,KAAa,MAAwB;AACnE,QAAM,OAAO,iBAAiB,GAAG,EAAE,QAAQ,aAAa,EAAE;AAC1D,SAAO,GAAG,IAAI,0BAA0B,KAAK,UAAU,KAAK,UAAU,IAAI,CAAC,CAAC;AAC9E;AACO,IAAM,gBAAgB,CAAC,SAC5B,MAAM,QAAQ,IAAI,KAAK,OAAO,KAAK,CAAC,MAAM,YAAY,8BAA8B,KAAK,KAAK,CAAC,CAAC;;;ADjJlG,IAAMC,QAAO;AACN,IAAM,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAMC,QAAO,EAAE,YAAY,CAAC,MAAe,kBAAkB,KAAK,CAAC,IAAI,KAAK,QAAY,SAAS,EAAE;AACnG,IAAMC,aAAY,CAAC,SAAoB,OAAO,aAAa,OAAO,CAAC,MAAM,MAAM,YAAY,IAAI,CAAC,GAAG,YAAY;AAE/G,IAAM,YAAY,CAAC,QAA2B,IAAI,cAAcC,MAAK,KAAK,QAAQ,GAAG,QAAQ;AAC7F,IAAMC,aAAY,CAAC,QAA2BD,MAAK,KAAK,UAAU,GAAG,GAAG,YAAY;AACpF,IAAME,cAAa,CAAC,QAA2BF,MAAK,KAAK,UAAU,GAAG,GAAG,aAAa;AACtF,IAAM,cAAc,CAAC,QAA2BA,MAAK,KAAK,UAAU,GAAG,GAAG,UAAU;AAEpF,IAAMG,aAAY,CAAC,MAA4B,OAAO,MAAM,YAAY,2BAA2B,KAAK,CAAC;AACzG,IAAMC,aAAY,CAAC,MACjB,MAAM,WAAW,MAAM,YAAY,MAAM,SAAS,IAAI;AAGxD,IAAM,iBACJ;AAEF,SAAS,cAAc,KAAkB,KAAwB;AAC/D,QAAM,OAAOF,YAAW,GAAG;AAC3B,MAAI,OAAO;AACX,MAAI,UAAU;AACd,MAAI;AACF,WAAOG,cAAa,MAAM,MAAM;AAAA,EAClC,SAAS,GAAG;AAGV,cAAW,EAA4B,SAAS;AAAA,EAClD;AACA,QAAM,OAAO,IAAI,CAACR,OAAM,QAAQ,CAAC,EAAE;AACnC,QAAM,OAAO,YAAY,KAAK,UAAU,IAAI,CAAC;AAC7C,QAAM,IAAS,WAAW,IAAI;AAE9B,QAAM,QAAQ,CAAC,MAAc,SAAgC;AAC3D,UAAM,MAAM,UAAU,MAAM,MAAM,OAAO;AACzC,WAAO,MAAM,EAAE,GAAG,KAAK,MAAM,GAAG,IAAI,IAAI,KAAK,cAAc,GAAG,IAAI,EAAE,MAAM,SAAS,MAAM,KAAK;AAAA,EAChG;AACA,MAAI,KAAK,CAAC,EAAE,OAAO;AACjB,QAAI,CAAC,EAAE,aAAa,KAAK,UAAU,EAAE,IAAI,MAAM,KAAK,UAAU,IAAI;AAChE,aAAO,EAAE,MAAM,SAAS,OAAO,MAAM,iBAAiB;AACxD,WAAO;AAAA,MACA,WAAW,MAAM,IAAI;AAAA,MAC1B,EAAE,YACE,wFACA;AAAA,IACN;AAAA,EACF;AACA,MAAI,GAAG,OAAO;AACZ,UAAM,OAAY,eAAe,EAAE,IAAI;AACvC,QAAS,cAAc,EAAE,IAAI,GAAG;AAC9B,UAAI,QAAaS,MAAK,KAAK,KAAK,UAAU,IAAI,CAAC,GAAG;AAChD,YAAI,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,IAAI;AAC9C,iBAAO,EAAE,MAAM,SAAS,OAAO,MAAM,4CAA4C;AAAA,MACrF;AACA,UAAI,CAAC,QAAaA,MAAK,KAAK,KAAK,UAAU,IAAI,CAAC;AAC9C,eAAO;AAAA,UACA,cAAc,MAAM,GAAG,YAAiB,gBAAgB,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,UAC3E;AAAA,QACF;AAAA,IACJ;AACA,WAAO;AAAA,MACL;AAAA,MACA,SAAS;AAAA,MACT,SAAS;AAAA,MACT,MAAM,sBAAsB,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK,QAAQ,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC,iBAAiB,cAAc;AAAA,IACpH;AAAA,EACF;AACA,SAAO,MAAW,WAAW,MAAM,IAAI,GAAG,6BAA6B;AACzE;AAEA,SAAS,gBAAgB,KAAuC;AAC9D,QAAM,OAAOJ,YAAW,GAAG;AAC3B,MAAI;AACJ,MAAI;AACF,WAAOG,cAAa,MAAM,MAAM;AAAA,EAClC,QAAQ;AACN,WAAO,EAAE,MAAM,SAAS,MAAM;AAAA,EAChC;AACA,QAAM,IAAS,WAAW,IAAI;AAC9B,MAAI,CAAC,EAAG,QAAO,EAAE,MAAM,SAAS,MAAM;AACtC,MAAI,CAAC,EAAE,MAAO,QAAO,UAAU,MAAW,cAAc,IAAI,CAAC,KAAK,EAAE,MAAM,SAAS,KAAK;AACxF,QAAM,OAAY,eAAe,EAAE,IAAI;AACvC,MAAS,cAAc,EAAE,IAAI,KAAK,QAAaC,MAAK,KAAK,KAAK,UAAU,IAAI,CAAC;AAC3E,WACE,UAAU,MAAW,cAAc,MAAM,GAAG,YAAiB,iBAAiB,EAAE,GAAG,CAAC,EAAE,CAAC,KAAK;AAAA,MAC1F;AAAA,MACA,SAAS;AAAA,IACX;AAEJ,SAAO,EAAE,MAAM,SAAS,MAAM;AAChC;AAKO,SAAS,YAAY,UAAkB,MAAyB,QAAQ,KAAoB;AACjG,MAAI,CAAC,wBAAwB,KAAK,QAAQ,EAAG,QAAO;AACpD,MAAI,OAAsB;AAC1B,QAAM,OAAO,CAAC,KAAa,UAAkB;AAC3C,QAAI,QAAkB,CAAC;AACvB,QAAI;AACF,cAAQC,aAAY,GAAG;AAAA,IACzB,QAAQ;AACN;AAAA,IACF;AACA,eAAW,KAAK,MAAM,KAAK,EAAE,QAAQ,GAAG;AACtC,YAAM,IAAIP,MAAK,KAAK,KAAK,CAAC;AAC1B,UAAI,EAAE,SAAS,IAAI,QAAQ,QAAQ,GAAG;AACpC,eAAO;AACP;AAAA,MACF;AACA,UAAI,QAAQ,GAAG;AACb,aAAK,GAAG,QAAQ,CAAC;AACjB,YAAI,KAAM;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACA,OAAK,YAAY,GAAG,GAAG,CAAC;AACxB,SAAO;AACT;AAIA,SAAS,YAAY,MAA8C;AACjE,SAAO,UAAU,MAAM,CAAC,SAAS,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;AAC9D;AAIA,SAAS,gBAAgB,MAAc,IAAqB;AAC1D,QAAM,QAAQ,SAAS,IAAI;AAC3B,SAAO,CAAC,CAAC,SAAS,UAAU,GAAG,YAAY;AAC7C;AAEA,SAAS,SAAS,MAA6B;AAC7C,QAAM,IAAI,qFAAqF,KAAK,IAAI;AACxG,SAAO,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,YAAY,IAAI;AACvC;AAEO,IAAM,QAAiB;AAAA,EAC5B,MAAMH;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ,CAAC,MAAM,QAAQ,QAAQW,YAAW,UAAU,GAAG,CAAC;AAAA,EACxD,aAAa,CAAC,MAAM,QAAQ,QAAQ,CAACP,WAAU,GAAG,GAAGC,YAAW,GAAG,CAAC;AAAA,EACpE,SAAS,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAAA,IAC9D,oBAAoBD,WAAU,GAAG,GAAGJ,OAAME,WAAU,KAAK,IAAI,GAAG,KAAKD,KAAI;AAAA,IACzE,cAAc,KAAK,GAAG;AAAA,EACxB;AAAA,EACA,WAAW,CAAC,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAAA,IAC3D,sBAAsBG,WAAU,GAAG,GAAG,cAAc,EAAE,SAAS,KAAK,SAAS,SAASA,WAAU,GAAG,CAAC,EAAE,CAAC;AAAA,IACvG,gBAAgB,GAAG;AAAA,EACrB;AAAA,EACA,WAAW,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAChE,UAAM,MAAyB;AAAA,MAC7B;AAAA,QACE,MAAMA,WAAU,GAAG;AAAA,QACnB,OAAO,sBAAsBA,WAAU,GAAG,GAAGJ,OAAME,WAAU,KAAK,IAAI,GAAG,KAAKD,KAAI;AAAA,QAClF,MAAM;AAAA,MACR;AAAA,IACF;AACA,QAAI,OAAO;AACX,QAAI;AACF,aAAOO,cAAaH,YAAW,GAAG,GAAG,MAAM;AAAA,IAC7C,QAAQ;AAAA,IAAC;AACT,UAAM,IAAS,WAAW,IAAI;AAC9B,UAAM,OAAO,IAAI,CAACL,OAAM,QAAQ,CAAC,EAAE;AACnC,UAAM,OAAO,IAAS,eAAe,EAAE,IAAI,IAAI;AAC/C,UAAM,OAAO,MAAM,CAAC,EAAE,SAAU,QAAQ,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,IAAI;AACpF,QAAI,KAAK;AAAA,MACP,MAAMK,YAAW,GAAG;AAAA,MACpB,OACE,CAAC,KAAM,EAAE,SAAS,CAAC,OACf,YACA,EAAE,YACA,cACA,KAAK,UAAU,EAAE,IAAI,MAAM,KAAK,UAAU,IAAI,KAAM,QAAQ,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,IAAI,IACtG,YACA;AAAA,IACZ,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,SAAS,OAAqC;AAC5C,UAAM,QAAQ,MAAM,KAAK,CAAC,KAAK;AAC/B,QAAI,IAA6B,CAAC;AAClC,QAAI,UAAU,UAAU;AAEtB,YAAM,MAAM,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,CAAC,KAAK,MAAM;AAC/D,UAAI;AACF,cAAM,IAAa,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAC5C,YAAI,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,EAAG,KAAI;AAAA,MAC3D,QAAQ;AAAA,MAAC;AACT,YAAMO,MAAM,EAAE,WAAW,KAAK,EAAE,aAAa,EAAE,YAAY,KAAK,EAAE;AAClE,UAAI,CAACA,IAAI,QAAO;AAKhB,YAAM,OAAO,YAAY,OAAOA,GAAE,GAAG,MAAM,GAAwB;AACnE,UAAI,CAAC,QAAQ,cAAc,YAAY,IAAI,CAAC,EAAG,QAAO;AACtD,YAAMC,KAAgB;AAAA,QACpB,SAAS,EAAE,IAAI,OAAOD,GAAE,GAAG,KAAK,OAAO,EAAE,QAAQ,WAAW,EAAE,MAAM,MAAM,IAAI;AAAA,QAC9E,QAAQ;AAAA,UACN;AAAA,YACE,MAAM;AAAA,YACN,MAAM,EAAE,QAAQ,QAAQ,GAAI,OAAO,EAAE,SAAS,MAAM,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,EAAG;AAAA,YAC/F,cAAc;AAAA,UAChB;AAAA,QACF;AAAA,QACA,OAAO,CAAC;AAAA,QACR,YAAY;AAAA,MACd;AACA,aAAOC;AAAA,IACT;AACA,QAAI;AAEF,YAAM,IAAa,MAAM,QAAQ,KAAK,MAAM,MAAM,KAAK,IAAI,CAAC;AAC5D,UAAI,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,EAAG,KAAI;AAAA,IAC3D,QAAQ;AAAA,IAAC;AACT,UAAM,KAAM,EAAE,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,KAAK,EAAE,YAAY;AAGhG,QAAI,CAAC,GAAI,QAAO;AAKhB,QAAI,EAAE,oBAAoB,KAAM,QAAO;AACvC,UAAM,UAAiC,EAAE,IAAI,OAAO,EAAE,GAAG,KAAK,OAAO,EAAE,QAAQ,WAAW,EAAE,MAAM,MAAM,IAAI;AAC5G,QAAI,OAAO,EAAE,UAAU,SAAU,SAAQ,QAAQ,EAAE;AACnD,UAAM,UAAU,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU;AAC5D,UAAM,OAAOP,WAAU,EAAE,SAAS,IAAI,EAAE,YAAY;AACpD,UAAM,UAAU,OAAO,EAAE,gBAAgB,WAAW,EAAE,cAAc;AAKpE,UAAM,SAAS,OAAO,EAAE,oBAAoB,WAAW,SAAS,EAAE,eAAe,IAAI;AACrF,UAAM,WACJ,OAAO,EAAE,aAAa,YAAY,EAAE,WAChC,EAAE,WACF,OAAO,EAAE,gBAAgB,YAAY,EAAE,cACrC,EAAE,cACF,UAAU,WAAW,OAAO,EAAE,EAAE,YAAY,IAC1C,SACA;AACV,UAAM,KAAK,CAAC,MAAc,UAAmC,EAAE,MAAM,MAAMQ,OAAM,IAAI,GAAG,cAAc,MAAM;AAC5G,UAAM,SAA+B,CAAC;AACtC,YAAQ,OAAO;AAAA,MACb,KAAK;AACH,eAAO;AAAA,UACL,GAAG,mBAAmB;AAAA,YACpB,QACE,OAAO,EAAE,WAAW,YAAY,CAAC,WAAW,UAAU,SAAS,WAAW,MAAM,EAAE,SAAS,EAAE,MAAM,IAC/F,EAAE,SACF;AAAA,UACR,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,gBAAgB;AAAA,YACjB;AAAA,YACA,cAAc,OAAO,EAAE,WAAW,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS;AAAA,UACtE,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,YAAI,KAAM,QAAO,KAAK,GAAG,gBAAgB,EAAE,MAAM,SAAS,SAAS,UAAU,OAAO,EAAE,WAAW,CAAC,CAAC;AACnG;AAAA,MACF,KAAK;AACH,YAAI;AACF,iBAAO;AAAA,YACL,GAAG,cAAc;AAAA,cACf;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,IAAI;AAAA,cACJ,OAAO,EAAE;AAAA,cACT,QAAQ,EAAE;AAAA,YACZ,CAAC;AAAA,UACH;AACF;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,oBAAoB;AAAA,YACrB,cAAc,WAAW,QAAQ,KAAK,IAAI,CAAC;AAAA,YAC3C,MAAM;AAAA,YACN;AAAA,YACA;AAAA,UACF,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,cAAc,EAAE,SAAS,QAAQ,OAAO,CAAC,CAAC;AACzD;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,cAAc,EAAE,SAAS,QAAQ,YAAY,CAAC,CAAC;AAC9D;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,oBAAoB;AAAA,YACrB,UAAU,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE;AAAA,YAClD,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;AAAA,UAChE,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,kBAAkB;AAAA,YACnB,UAAU,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE;AAAA,YAClD,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;AAAA,UAChE,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,qBAAqB,EAAE,SAAS,EAAE,YAAY,WAAW,WAAW,OAAO,CAAC,CAAC;AAC5F;AAAA,MACF,KAAK;AACH;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,iBAAiB,EAAE,QAAQP,WAAU,EAAE,MAAM,EAAE,CAAC,CAAC;AAChE;AAAA,MACF;AACE,eAAO;AAAA,IACX;AACA,UAAM,IAAgB,EAAE,SAAS,QAAQ,OAAO,CAAC,EAAE;AAGnD,QAAI,OAAO,EAAE,oBAAoB,YAAY,CAAC,gBAAgB,EAAE,iBAAiB,OAAO,EAAE,CAAC;AACzF,QAAE,aAAa,EAAE;AACnB,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,MAAM,UAAU;AAC7B,QAAI,OAAO;AACX,QAAI;AACF,aAAOC,cAAa,MAAM,MAAM;AAAA,IAClC,QAAQ;AACN,aAAO,EAAE,OAAO,CAAC,GAAG,MAAM,SAAS;AAAA,IACrC;AACA,WAAO,UAAU,KAAK,MAAM,IAAI,GAAG,QAAQ;AAAA,EAC7C;AAAA,EAEA,MAAM,SAAS,YAA0B;AACvC,UAAM,MAAoB,CAAC;AAC3B,QAAI,WAAY,QAAO,OAAO,KAAK,WAAW,UAAU,CAAC;AACzD,WAAO;AAAA,EACT;AAAA,EAEA,SAAS,SAAS,MAAM,QAAQ,KAAoB;AAClD,UAAMO,QAAsB,CAAC;AAC7B,eAAW,KAAK,YAAY,YAAY,GAAG,GAAG,GAAG,SAAS,CAAC,MAAM,sBAAsB,KAAK,CAAC,CAAC,GAAG;AAC/F,YAAM,IAAI,wCAAwC,KAAKZ,MAAK,SAAS,CAAC,CAAC;AACvE,UAAI,CAAC,IAAI,CAAC,EAAG;AAEb,UAAI,cAAc,YAAY,CAAC,CAAC,EAAG;AACnC,YAAM,QAAQ,WAAW,CAAC;AAC1B,UAAI,CAAC,MAAM,cAAc,CAAC,MAAM,QAAS;AACzC,YAAM,EAAE,YAAY,SAAS,WAAW,KAAK,iBAAiB,KAAK,GAAG,KAAK,IAAI;AAC/E,MAAAY,MAAK,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,GAAG,GAAG,KAAK,GAAG,YAAY,QAAQ,CAAC;AAAA,IACnE;AACA,WAAOA;AAAA,EACT;AAAA,EAEA,QAAQ,CAAC,eAAe,YAAY,UAAU;AAChD;AAGA,IAAMC,YAAW,CAAC,MAChB,OAAO,MAAM,YAAY,IAAI,KAAK,IAAI,OAAO,IAAI,KAAK,IAAI,GAAI,EAAE,YAAY,IAAI;AAElF,SAASF,OAAyC,GAAS;AACzD,aAAW,KAAK,OAAO,KAAK,CAAC,EAAG,KAAI,EAAE,CAAC,MAAM,OAAW,QAAO,EAAE,CAAC;AAClE,SAAO;AACT;AASO,SAAS,YAAY,MAAgD;AAC1E,QAAM,EAAE,KAAK,IAAI,KAAK,MAAM,GAAG,MAAM,IAAI;AACzC,aAAW,KAAK,WAAW,IAAI,EAAE,QAAQ,GAAG;AAC1C,UAAM,IAAI,EAAE;AACZ,QAAI,EAAE,SAAS,eAAe,GAAG,SAAS,cAAe;AACzD,QAAI,CAAC,EAAE,eAAe,OAAO,EAAE,gBAAgB,SAAU;AACzD,UAAM,KAAK,EAAE;AACb,QAAI,GAAG,YAAY,QAAQ,GAAG,aAAa,QAAS;AACpD,UAAM,UAAgD,CAAC;AACvD,eAAW,MAAM,CAAC,WAAW,WAAW,GAAG;AACzC,YAAM,IAAI,GAAG,EAAE;AACf,UAAI,OAAO,GAAG,iBAAiB,YAAY,EAAE,eAAe,EAAG;AAC/D,cAAQ;AAAA,QACNA,OAAM;AAAA,UACJ;AAAA,UACA,cAAc,EAAE;AAAA,UAChB,gBACE,OAAO,UAAU,EAAE,cAAc,KAAK,OAAO,EAAE,cAAc,IAAI,IAAI,OAAO,EAAE,cAAc,IAAI;AAAA,UAClG,WAAWE,UAAS,EAAE,SAAS;AAAA,QACjC,CAAC;AAAA,MACH;AAAA,IACF;AACA,QAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,WAAOF,OAAM,EAAE,SAAS,MAAM,OAAO,GAAG,cAAc,WAAW,GAAG,UAAU,MAAM,GAAG,EAAE,IAAI,OAAU,CAAC;AAAA,EAC1G;AACA,SAAO;AACT;AAGO,SAAS,WAAW,MAA4B;AACrD,QAAM,EAAE,MAAM,KAAK,IAAI,KAAK,MAAM,MAAM,MAAM,KAAK,IAAI;AACvD,MAAI,IAAI,WAAW,IAAI;AACvB,MAAI,IAAI,WAAW,IAAI;AACvB,QAAM,MAAoB,CAAC;AAC3B,QAAM,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,OAAO;AACjE,QAAM,WAAW,oBAAoB,IAAI;AACzC,MAAI,MAAM,SAAS;AACjB,UAAM,IAAI,KAAK;AAKf,QAAI,EAAE,IAAK,KAAI,MAAM,EAAE;AACvB,QAAI,EAAE,YAAa,KAAI,kBAAkB,OAAO,EAAE,WAAW;AAC7D,QAAI,EAAE,KAAK,OAAQ,KAAI,SAAS,EAAE,IAAI;AACtC,QAAI,EAAE,KAAK,eAAgB,KAAI,OAAO,OAAO,EAAE,IAAI,cAAc,EAAE,QAAQ,eAAe,IAAI;AAAA,EAChG;AACA,MAAI,UAAU;AACZ,UAAM,IAAI,EAAE,UAAU,QAAQ;AAC9B,UAAM,IAAI,EAAE,UAAU,QAAQ;AAC9B,QAAI,IAAI,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;AAI1B,UAAM,QACJ,KAAK,KACL,KAAK,KACL,UAAU,MAAM,CAAC,SAAU,SAAS,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,IAAI,OAAO,MAAO,OAAO,iBAAiB;AAC7G,QAAI,KAAK,IAAI,EAAE,MAAM,CAAC,IAAI,QAAQ,IAAI,CAAC;AAAA,EACzC;AACA,QAAM,MAAM,EAAE,OAAO,CAAC,EACnB,QAAQ,EACR,KAAK,CAAC,MAAM,EAAE,SAAS,kBAAmB,EAAE,SAA4C,KAAK;AAGhG,MAAI,KAAK,SAAS,MAAO,KAAI,QAAQ,IAAI,QAAQ;AACjD,aAAW,KAAK,GAAG;AACjB,UAAM,IAAI,EAAE;AACZ,QAAI,EAAE,SAAS,mBAAmB,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,SAAS,OAAQ;AACnF,UAAM,QAAQ,EAAE,WAAW,CAAC,GACzB,OAAO,CAAC,MAAM,KAAK,EAAE,SAAS,gBAAgB,OAAO,EAAE,SAAS,QAAQ,EACxE,IAAI,CAAC,MAAM,EAAE,IAAc,EAC3B,KAAK,IAAI,EACT,KAAK;AACR,QAAI,CAAC,QAAQ,cAAc,IAAI,EAAG;AAClC,UAAM,OAAO,KAAK,MAAM,IAAI,EAAE,CAAC;AAC/B,QAAI,QAAQ,KAAK,SAAS,KAAK,GAAG,KAAK,MAAM,GAAG,EAAE,EAAE,QAAQ,WAAW,EAAE,CAAC,WAAM;AAChF,QAAI,eAAe;AACnB;AAAA,EACF;AACA,QAAM,SAAS,YAAY,OAAO,CAAC,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,GAC9D,IAAI,CAAC,MAAM,KAAK,MAAM,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC,EAChD,OAAO,CAAC,MAAM,IAAI,CAAC;AACtB,MAAI,MAAM,QAAQ;AAChB,QAAI,aAAa,KAAK,IAAI,GAAG,KAAK;AAClC,QAAI,UAAU,KAAK,IAAI,GAAG,KAAK;AAAA,EACjC;AACA,SAAO;AACT;;;AE3eA,SAAS,cAAAG,mBAAkB;AAC3B,SAAS,cAAAC,aAAY,oBAAoB;AACzC,OAAOC,WAAU;;;ACVjB,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,qBAAqB;AA8BvB,SAAS,MAAS,IAAgB;AACvC,QAAM,WAAW,QAAQ;AACzB,MAAI;AACF,YAAQ,eAAe,CAAC,YAAqB,SAAoB;AAC/D,YAAM,OAAO,OAAO,KAAK,CAAC,MAAM,WAAW,KAAK,CAAC,IAAK,KAAK,CAAC,GAAqC;AACjG,UAAI,SAAS,yBAAyB,UAAU,KAAK,OAAO,OAAO,CAAC,EAAG;AACvE,MAAC,SAAuC,KAAK,SAAS,SAAS,GAAG,IAAI;AAAA,IACxE;AACA,WAAO,GAAG;AAAA,EACZ,UAAE;AACA,YAAQ,cAAc;AAAA,EACxB;AACF;AAEA,IAAI;AAEJ,SAAS,SAAwB;AAC/B,MAAI,cAAc,OAAW,QAAO;AACpC,cAAY;AACZ,MAAI;AACF,UAAM,MAAM,cAAc,YAAY,GAAG;AACzC,UAAM,IAAI,MAAM,MAAM,IAAI,aAAa,CAAC;AACxC,QAAI,OAAO,GAAG,iBAAiB,WAAY,aAAY,EAAE;AAAA,EAC3D,QAAQ;AAAA,EAAC;AACT,SAAO;AACT;AAGA,SAAS,MAAM,KAAgC;AAC7C,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,MAAiB,CAAC;AACxB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAgB,GAAG;AACrD,QAAI,OAAO,MAAM,SAAU,KAAI,CAAC,IAAI,OAAO,CAAC;AAAA,aACnC,aAAa,WAAY,KAAI,CAAC,IAAI;AAAA,QACtC,KAAI,CAAC,IAAI;AAAA,EAChB;AACA,SAAO;AACT;AACA,IAAM,OAAO,CAAC,MAAmC;AAC/C,MAAI,CAAC,MAAM,QAAQ,CAAC,EAAG,QAAO;AAC9B,QAAM,MAAmB,CAAC;AAC1B,aAAW,KAAK,GAAG;AACjB,UAAM,IAAI,MAAM,CAAC;AACjB,QAAI,EAAG,KAAI,KAAK,CAAC;AAAA,EACnB;AACA,SAAO;AACT;AAEA,SAAS,QAAQ,MAAc,KAAa,QAAuB,GAAgD;AACjH,QAAM,IAAI,OAAO;AACjB,MAAI,CAAC,EAAG,QAAO;AACf,MAAI;AACJ,MAAI;AACF,SAAK,MAAM,MAAM,IAAI,EAAE,MAAM,EAAE,UAAU,MAAM,SAAS,EAAE,UAAU,CAAC,CAAC;AAAA,EACxE,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI;AACF,QAAI;AACF,SAAG,KAAK,yBAAyB,KAAK,MAAM,EAAE,SAAS,CAAC,EAAE;AAAA,IAC5D,QAAQ;AAAA,IAAC;AACT,WAAO,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;AAAA,EACzD,QAAQ;AACN,WAAO;AAAA,EACT,UAAE;AACA,QAAI;AACF,SAAG,MAAM;AAAA,IACX,QAAQ;AAAA,IAAC;AAAA,EACX;AACF;AAGA,IAAM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQd,SAAS,SAAS,MAAc,KAAa,QAAuB,GAAgD;AAClH,MAAI;AACF,UAAM,MAAMD;AAAA,MACV,QAAQ;AAAA,MACR,CAAC,yBAAyB,iBAAiB,MAAM,OAAO,MAAM,KAAK,KAAK,UAAU,MAAM,GAAG,OAAO,EAAE,SAAS,CAAC;AAAA,MAC9G;AAAA,QACE,UAAU;AAAA,QACV,SAAS,EAAE,YAAY;AAAA,QACvB,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,QAClC,WAAW,EAAE;AAAA,QACb,aAAa;AAAA,MACf;AAAA,IACF;AACA,WAAO,KAAK,KAAK,MAAM,OAAO,MAAM,CAAC;AAAA,EACvC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,IAAM,OAAO;AACN,SAAS,aAAa,KAAa,QAAsC;AAC9E,MAAI,IAAI,SAAS,GAAG,EAAG,QAAO;AAC9B,QAAM,QAAQ,IAAI,MAAM,GAAG,EAAE,SAAS;AACtC,MAAI,UAAU,OAAO,OAAQ,QAAO;AACpC,MAAI,IAAI;AACR,MAAI,MAAM;AACV,QAAM,MAAM,IAAI,QAAQ,OAAO,MAAM;AACnC,UAAM,IAAI,OAAO,GAAG;AACpB,QAAI,OAAO,MAAM,UAAU;AACzB,UAAI,CAAC,OAAO,SAAS,CAAC,GAAG;AACvB,cAAM;AACN,eAAO;AAAA,MACT;AACA,aAAO,OAAO,CAAC;AAAA,IACjB;AACA,QAAI,OAAO,MAAM,YAAY,CAAC,KAAK,KAAK,CAAC,GAAG;AAC1C,YAAM;AACN,aAAO;AAAA,IACT;AACA,WAAO,IAAI,CAAC;AAAA,EACd,CAAC;AACD,SAAO,MAAM,OAAO;AACtB;AAEA,SAAS,OAAO,MAAc,KAAa,QAAuB,GAAgD;AAChH,QAAM,OAAO,aAAa,KAAK,MAAM;AACrC,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI;AACF,UAAM,MAAMA,cAAa,WAAW,CAAC,aAAa,SAAS,MAAM,IAAI,GAAG;AAAA,MACtE,UAAU;AAAA,MACV,SAAS,EAAE,YAAY;AAAA,MACvB,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,MAClC,WAAW,EAAE;AAAA,MACb,aAAa;AAAA,IACf,CAAC,EAAE,KAAK;AACR,QAAI,CAAC,IAAK,QAAO,CAAC;AAClB,WAAO,KAAK,KAAK,MAAM,GAAG,CAAC;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,YACd,MACA,KACA,SAAwB,CAAC,GACzB,OAAsB,CAAC,GACH;AACpB,QAAM,IAA6B;AAAA,IACjC,WAAW,KAAK,aAAa;AAAA,IAC7B,QAAQ,KAAK,UAAU;AAAA,IACvB,WAAW,KAAK,aAAa,KAAK,OAAO;AAAA,EAC3C;AACA,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI;AACF,QAAI,CAACC,YAAW,IAAI,EAAG,QAAO;AAAA,EAChC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,OACJ,EAAE,WAAW,SACR,CAAC,SAAS,UAAU,MAAM,IAC3B,EAAE,WAAW,SACV,CAAC,OAAO,IACT,EAAE,WAAW,UACV,CAAC,QAAQ,IACT,CAAC,MAAM;AAClB,aAAW,OAAO,MAAM;AACtB,UAAM,IAAI,IAAI,MAAM,KAAK,QAAQ,CAAC;AAClC,QAAI,EAAG,QAAO;AAAA,EAChB;AACA,SAAO;AACT;;;ADrLA,IAAMC,QAAO;AAGN,IAAM,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAMC,QAAO,EAAE,YAAY,CAAC,MAAe,kBAAkB,KAAK,CAAC,IAAI,KAAK,QAAY,SAAS,EAAE;AACnG,IAAMC,aAAY,CAAC,SAAoB,OAAO,aAAa,OAAO,CAAC,MAAM,MAAM,YAAY,IAAI,CAAC,GAAG,YAAY;AAC/G,IAAM,eACJ;AACF,IAAM,cACJ;AAEF,IAAM,eAAe,CAAC,QACpB,yCAAyC,GAAG;AAIvC,SAAS,aAAa,MAAkC;AAC7D,QAAM,QAAQ,SAAS,IAAI,EAAE;AAC7B,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,QAAM,KAAK;AACX,SAAO,OAAO,KAAK,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;AACvD;AAEA,IAAM,SAAS,CAAC,QAA2B,IAAI,QAAQ,QAAQ;AAC/D,IAAM,UAAU,CAAC,QAA2B,IAAI,WAAWC,MAAK,KAAK,OAAO,GAAG,GAAG,WAAW,SAAS;AAE/F,IAAM,iBAAiB,CAAC,MAAyB,QAAQ,QAC9D,QAAQ,aAAa,UACjBA,MAAK,KAAK,QAAQ,GAAG,GAAG,OAAO,IAC/BA,MAAK,KAAK,IAAI,mBAAmBA,MAAK,KAAK,OAAO,GAAG,GAAG,SAAS,GAAG,OAAO;AACjF,IAAMC,cAAa,CAAC,QAA2BD,MAAK,KAAK,eAAe,GAAG,GAAG,aAAa;AAEpF,IAAM,eAAe,CAAC,MAAyB,QAAQ,QAC5D,QAAQ,aAAa,UACjBA,MAAK,KAAK,QAAQ,GAAG,GAAG,SAAS,KAAK,IACtCA,MAAK,KAAK,IAAI,iBAAiBA,MAAK,KAAK,OAAO,GAAG,GAAG,UAAU,OAAO,GAAG,SAAS,KAAK;AAC9F,IAAM,aAAa,CAAC,QAA2BA,MAAK,KAAK,aAAa,GAAG,GAAG,aAAa;AAEzF,IAAM,aAAa;AACnB,IAAME,aAAY,CAAC,MAA4B,OAAO,MAAM,YAAY,2BAA2B,KAAK,CAAC;AACzG,IAAMC,aAAY,CAAC,MACjB,MAAM,WAAW,MAAM,YAAY,MAAM,SAAS,IAAI;AACxD,IAAMC,cAAa,CAAC,MAClB,OAAO,MAAM,YAAY,CAAC,WAAW,UAAU,SAAS,WAAW,MAAM,EAAE,SAAS,CAAC,IAAI,IAAI;AAC/F,SAASC,SAAQ,GAAgC;AAC/C,MAAI,OAAO,MAAM,YAAY,EAAG,QAAO,EAAE,MAAM,GAAG,GAAI;AACtD,MAAI,KAAK,OAAO,MAAM,UAAU;AAC9B,UAAM,IAAI;AACV,QAAI,OAAO,EAAE,YAAY,YAAY,EAAE,QAAS,QAAO,EAAE,QAAQ,MAAM,GAAG,GAAI;AAC9E,QAAI,OAAO,EAAE,UAAU,YAAY,EAAE,MAAO,QAAO,EAAE,MAAM,MAAM,GAAG,GAAI;AAAA,EAC1E;AACA,SAAO;AACT;AACA,IAAM,MAAM,CAAC,GAAW,MAAuB,EAAE,SAAS,IAAI,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,QAAQ,WAAW,EAAE,CAAC,WAAM;AAEpG,IAAM,QAAiB;AAAA,EAC5B,MAAMR;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ,CAAC,MAAM,QAAQ,QAAQS,YAAW,eAAe,GAAG,CAAC,KAAKA,YAAW,aAAa,GAAG,CAAC;AAAA,EAC9F,aAAa,CAAC,MAAM,QAAQ,QAAQ,CAACL,YAAW,GAAG,CAAC;AAAA,EACpD,SAAS,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAC9D,UAAM,OAAOA,YAAW,GAAG;AAC3B,UAAM,IAAI,oBAAoB,MAAMJ,OAAME,WAAU,KAAK,IAAI,GAAG,KAAKD,KAAI;AACzE,QAAI,EAAE,QAAS,QAAO,CAAC,CAAC;AACxB,UAAM,MAAM,aAAa,IAAI;AAC7B,WAAO,CAAC,EAAE,GAAG,GAAG,MAAM,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC;AAAA,EAChE;AAAA,EACA,WAAW,CAAC,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAAA,IAC3D,sBAAsBG,YAAW,GAAG,GAAG,cAAc,EAAE,SAAS,KAAK,SAAS,SAASA,YAAW,GAAG,CAAC,EAAE,CAAC;AAAA,EAC3G;AAAA,EACA,WAAW,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAChE,UAAM,OAAOA,YAAW,GAAG;AAC3B,UAAM,QAAQ,sBAAsB,MAAMJ,OAAME,WAAU,KAAK,IAAI,GAAG,KAAKD,KAAI;AAI/E,UAAM,MAAM,UAAU,YAAY,SAAY,aAAa,IAAI;AAC/D,WAAO,CAAC,EAAE,MAAM,OAAO,MAAM,aAAa,OAAO,MAAM,MAAM,aAAa,GAAG,IAAI,YAAY,CAAC;AAAA,EAChG;AAAA,EAEA,SAAS,OAAqC;AAC5C,UAAM,QAAQ,MAAM,KAAK,CAAC,KAAK;AAC/B,QAAI,IAA6B,CAAC;AAClC,QAAI;AAEF,YAAM,IAAa,MAAM,QAAQ,KAAK,MAAM,MAAM,KAAK,IAAI,CAAC;AAC5D,UAAI,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,EAAG,KAAI;AAAA,IAC3D,QAAQ;AAAA,IAAC;AACT,UAAM,KAAK,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;AAC7D,QAAI,CAAC,GAAI,QAAO;AAEhB,UAAM,UAAiC,EAAE,IAAI,KAAK,MAAM,IAAI,qBAAqB,MAAM,IAAI;AAC3F,UAAM,UAAU,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY;AAChE,UAAM,OAAOI,WAAU,EAAE,SAAS,IAAI,EAAE,YAAY;AACpD,UAAM,UAAU,OAAO,EAAE,gBAAgB,WAAW,EAAE,cAAc;AAIpE,UAAM,WACJ,EAAE,iBAAiB,OAAO,EAAE,kBAAkB,YAAY,CAAC,MAAM,QAAQ,EAAE,aAAa,IACnF,EAAE,gBACH;AAIN,UAAM,eAAe,MACnB,QAAQK,YAAW,QAAQ,EACxB,OAAO,GAAG,WAAW,EAAE,IAAI,QAAQ,EAAE,IAAI,KAAK,UAAU,EAAE,cAAc,IAAI,CAAC,EAAE,EAC/E,OAAO,KAAK,EACZ,MAAM,GAAG,EAAE,CAAC;AACjB,UAAM,KAAK,CAAC,MAAc,UAAmC,EAAE,MAAM,MAAMC,OAAM,IAAI,GAAG,cAAc,MAAM;AAC5G,UAAM,SAA+B,CAAC;AACtC,YAAQ,OAAO;AAAA,MACb,KAAK;AACH,eAAO,KAAK,GAAG,mBAAmB,EAAE,QAAQJ,YAAW,EAAE,MAAM,EAAE,CAAC,CAAC;AACnE;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,gBAAgB;AAAA,YACjB;AAAA,YACA,cAAc,OAAO,EAAE,WAAW,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS;AAAA,UACtE,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,YAAI,KAAM,QAAO,KAAK,GAAG,gBAAgB,EAAE,MAAM,SAAS,SAAS,OAAO,EAAE,WAAW,CAAC,CAAC;AACzF;AAAA,MACF,KAAK;AAEH,YAAI;AACF,iBAAO;AAAA,YACL,GAAG,cAAc;AAAA,cACf;AAAA,cACA;AAAA,cACA;AAAA;AAAA;AAAA,cAGA,IAAI,UAAU,YAAY;AAAA,cAC1B,OAAOC,SAAQ,UAAU,KAAK;AAAA,cAC9B,OAAO,EAAE;AAAA,cACT,QAAQ,WAAW,SAAS,SAAS,EAAE;AAAA,YACzC,CAAC;AAAA,UACH;AACF;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,oBAAoB,EAAE,cAAc,WAAW,aAAa,GAAG,MAAM,cAAc,KAAK,CAAC,CAAC;AACzG;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,cAAc,EAAE,SAAS,QAAQ,OAAO,CAAC,CAAC;AACzD;AAAA,MACF,KAAK;AAEH,eAAO,KAAK,GAAG,qBAAqB,EAAE,SAAS,OAAO,CAAC,CAAC;AACxD;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,iBAAiB,EAAE,QAAQF,WAAU,EAAE,MAAM,EAAE,CAAC,CAAC;AAChE;AAAA,MACF;AACE,eAAO;AAAA,IACX;AACA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,OAAO,CAAC;AAAA,MACR,YAAY,cAAc,WAAW,MAAM,GAAwB,GAAG,EAAE;AAAA,IAC1E;AAAA,EACF;AAAA,EAEA,eAAe,MAAM,UAAU;AAC7B,UAAM,MAAM,SAAS,IAAI;AACzB,QAAI,CAAC,IAAK,QAAO,EAAE,OAAO,CAAC,GAAG,MAAM,SAAS;AAC7C,WAAO,eAAe,IAAI,IAAI,IAAI,SAAS,QAAQ;AAAA,EACrD;AAAA,EAEA,MAAM,SAAS,YAAY,QAAQ,QAAQ,MAAM,QAAQ,KAAmB;AAC1E,UAAM,MAAoB,CAAC;AAC3B,UAAM,UAAU,aAAa,GAAG;AAChC,QAAI,QAAS,KAAI,kBAAkB;AACnC,UAAM,UAAU,aAAa,GAAG;AAChC,QAAI,QAAS,KAAI,aAAa;AAC9B,UAAM,MAAM,SAAS,UAAU,KAAK,EAAE,IAAI,WAAW,GAAG,GAAG,SAAS,QAAQ,GAAG;AAC/E,QAAI,CAAC,WAAW,KAAK,IAAI,OAAO,EAAG,QAAO;AAC1C,UAAMM,QAAO,YAAY,IAAI,IAAI,aAAa,CAAC,IAAI,OAAO,CAAC;AAC3D,UAAM,MAAMA,QAAO,CAAC;AACpB,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO,OAAO,KAAK,aAAa,GAAG,CAAC;AACpC,WAAO;AAAA,EACT;AAAA,EAEA,SAAS,SAAS,MAAM,QAAQ,KAAoB;AAClD,UAAMA,QAAO,YAAY,WAAW,GAAG,GAAG,cAAc,CAAC,QAAQ,KAAK,MAAM,UAAU,GAAI,CAAC,CAAC;AAC5F,QAAI,CAACA,MAAM,QAAO,CAAC;AACnB,UAAM,MAAqB,CAAC;AAC5B,eAAW,OAAOA,OAAM;AACtB,YAAM,KAAK,OAAO,IAAI,EAAE;AACxB,UAAI,CAAC,WAAW,KAAK,EAAE,EAAG;AAC1B,YAAM,EAAE,YAAY,SAAS,GAAG,KAAK,IAAI,aAAa,GAAG;AACzD,UAAI,CAAC,cAAc,CAAC,QAAS;AAC7B,UAAI,KAAK,EAAE,SAAS,EAAE,IAAI,GAAG,KAAK,GAAG,YAAY,QAAQ,CAAC;AAAA,IAC5D;AACA,WAAO;AAAA,EACT;AACF;AAEA,SAASD,OAAyC,GAAS;AACzD,aAAW,KAAK,OAAO,KAAK,CAAC,EAAG,KAAI,EAAE,CAAC,MAAM,OAAW,QAAO,EAAE,CAAC;AAClE,SAAO;AACT;AAMO,IAAM,gBAAgB,CAAC,IAAY,YAA4B,GAAG,EAAE,IAAI,OAAO;AAC/E,SAAS,SAAS,KAAiE;AACxF,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,KAAK,IAAI,YAAY,GAAG;AAC9B,MAAI,MAAM,EAAG,QAAO;AACpB,QAAM,KAAK,IAAI,MAAM,GAAG,EAAE;AAC1B,QAAM,UAAU,IAAI,MAAM,KAAK,CAAC;AAChC,SAAO,MAAM,WAAW,KAAK,OAAO,IAAI,EAAE,IAAI,QAAQ,IAAI;AAC5D;AAgBA,IAAM,cAAc;AACb,IAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kFAOyD,WAAW;AAK7F,IAAM,iBAAiB;AAAA;AAAA;AAGvB,IAAM,cAAc,UAAU,cAAc;AAI5C,IAAM,eAAe,gBAAgB,cAAc;AAAA;AAAA;AAInD,SAAS,aAAa,KAA4C;AAChE,QAAM,MAAoB,CAAC;AAC3B,MAAI,OAAO,IAAI,UAAU,YAAY,IAAI,MAAO,KAAI,QAAQ,IAAI;AAChE,MAAI,OAAO,IAAI,sBAAsB,YAAYR,MAAK,WAAW,IAAI,iBAAiB;AACpF,QAAI,MAAM,IAAI;AAChB,QAAM,UAAU,OAAO,IAAI,UAAU;AACrC,QAAM,OAAO,OAAO,IAAI,gBAAgB;AACxC,MAAI,OAAO,SAAS,OAAO,KAAK,UAAU,EAAG,KAAI,aAAa,KAAK,MAAM,UAAU,GAAI;AACvF,MAAI,OAAO,SAAS,IAAI,KAAK,OAAO,EAAG,KAAI,UAAU,KAAK,MAAM,OAAO,GAAI;AAC3E,MAAI,OAAO,IAAI,cAAc,UAAU;AACrC,UAAM,OAAO,IAAI,UAAU,MAAM,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK;AAChE,QAAI,KAAK,KAAK,GAAG;AACf,UAAI,QAAQ,IAAI,KAAK,KAAK,GAAG,EAAE;AAC/B,UAAI,eAAe;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;AAaO,SAAS,eAAe,IAAY,SAAiB,UAAkC;AAC5F,MAAI,CAAC,WAAW,KAAK,OAAO,EAAG,QAAO,EAAE,OAAO,CAAC,GAAG,MAAM,SAAS;AAClE,QAAM,QAAQ,OAAO,SAAS,QAAQ,KAAK,WAAW,IAAI,KAAK,MAAM,QAAQ,IAAI;AACjF,QAAMS,QAAO,YAAY,IAAI,WAAW,CAAC,SAAS,SAAS,SAAS,KAAK,CAAC;AAC1E,MAAI,CAACA,OAAM,OAAQ,QAAO,EAAE,OAAO,CAAC,GAAG,MAAM,SAAS;AACtD,QAAM,OAAiF,CAAC;AACxF,MAAI,MAAM;AACV,aAAW,OAAOA,OAAM;AACtB,UAAM,OAAO,OAAO,IAAI,OAAO;AAC/B,QAAI,CAAC,OAAO,SAAS,IAAI,EAAG;AAC5B,QAAI,OAAO,IAAI,IAAK,OAAM,OAAO;AACjC,QAAI,OAAO,SAAU;AACrB,QAAI;AACJ,QAAI;AACF,UAAI,KAAK,MAAM,OAAO,IAAI,gBAAgB,EAAE,CAAC;AAAA,IAC/C,QAAQ;AACN;AAAA,IACF;AACA,UAAM,OAAO,OAAO,EAAE,YAAY,WAAW,EAAE,QAAQ,KAAK,IAAI;AAChE,QAAI,CAAC,KAAM;AAUX,UAAM,OAAO,OAAO,IAAI,UAAU;AAClC,UAAM,KACJ,KAAK,MAAM,OAAO,EAAE,UAAU,cAAc,EAAE,CAAC,MAC9C,OAAO,SAAS,IAAI,KAAK,OAAO,IAAI,KAAK,MAAM,OAAO,GAAI,IAAI,MAC/D,KAAK,IAAI;AACX,QAAI,EAAE,SAAS,UAAU,EAAE,UAAU,kBAAkB,KAAM,MAAK,KAAK,EAAE,MAAM,QAAQ,MAAM,IAAI,MAAM,KAAK,CAAC;AAAA,aACpG,EAAE,SAAS,YAAa,MAAK,KAAK,EAAE,MAAM,aAAa,MAAM,IAAI,MAAM,KAAK,CAAC;AAAA,EACxF;AAEA,OAAK,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI;AACnC,SAAO,UAAU,MAAM,GAAG;AAC5B;AAGO,SAAS,aAAa,MAAyB,QAAQ,KAAyB;AACrF,MAAI;AACF,UAAM,SAAS,aAAaT,MAAK,KAAK,aAAa,GAAG,GAAG,aAAa,SAAS,CAAC;AAChF,UAAM,OAAOA,MAAK,SAAS,MAAM;AACjC,WAAO,8BAA8B,KAAK,IAAI,IAAI,OAAO;AAAA,EAC3D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAIO,SAAS,aAAa,MAAyB,QAAQ,KAAyB;AACrF,QAAM,IAAI,SAASC,YAAW,GAAG,CAAC,EAAE;AACpC,QAAM,KAAK,KAAK,OAAO,MAAM,WAAW,EAAE,SAAS;AACnD,SAAO,OAAO,OAAO,YAAY,2BAA2B,KAAK,EAAE,IAAI,KAAK;AAC9E;;;AEzYA,SAAS,gBAAAS,qBAAoB;AAC7B,SAAS,cAAAC,aAAY,gBAAAC,qBAAoB;AACzC,OAAOC,YAAU;AAQjB,IAAMC,QAAO;AACN,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAMC,QAAO,EAAE,YAAY,CAAC,MAAe,OAAO,KAAK,CAAC,IAAI,OAAO,QAAY,SAAS,IAAK;AAC7F,IAAM,YAAY,CAAC,QAA2B,IAAI,mBAAmBC,OAAK,KAAK,QAAQ,GAAG,SAAS;AACnG,IAAM,WAAW,CAAC,QAA2BA,OAAK,KAAK,UAAU,GAAG,GAAG,eAAe;AACtF,IAAMC,aAAY,CAAC,MAA4B,OAAO,MAAM,YAAY,2BAA2B,KAAK,CAAC;AACzG,IAAMC,aAAY,CAAC,SAAoB,OAAO,cAAc,OAAO,CAAC,MAAM,MAAM,YAAY,IAAI,CAAC,GAAG,aAAa;AAK1G,SAAS,cACd,WAA4B,QAAQ,UACpC,MAAyB,QAAQ,KACzB;AACR,QAAM,MAAM,aAAa;AACzB,QAAM,MAAM,MACR,WAAW,EAAE,OAAO,CAAC,cAAc,YAAY,GAAG,MAAM,CAACF,OAAK,QAAQ,QAAQ,QAAQ,CAAC,GAAG,IAAI,CAAC,IAC/F;AACJ,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,mBAAmB;AAC7C,QAAM,IAAI,MAAM,KAAK,CAAC,WAAW,CAAC;AAClC,SAAOG,cAAa,EAAE,MAAM,EAAE,MAAM;AAAA,IAClC,UAAU;AAAA,IACV,SAAS;AAAA,IACT,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,IAClC;AAAA,IACA,OAAO,EAAE;AAAA,EACX,CAAC,EAAE,KAAK;AACV;AAGO,SAAS,kBAAkB,cAA0D;AAC1F,QAAM,QAAQ,SAAS,YAAY,EAAE;AACrC,MAAI,OAAO,gBAAgB,KAAM,QAAO,EAAE,QAAQ,KAAK;AACvD,MAAI,OAAO,gBAAgB;AACzB,WAAO,EAAE,QAAQ,OAAO,MAAM,wDAAwD;AACxF,MAAI,UAAU;AACd,MAAI;AACF,cAAU,cAAc;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,MAAM;AAAA,IACR;AAAA,EACF;AACA,QAAM,IAAI,eAAe,KAAK,OAAO;AACrC,MAAI,MAAM,OAAO,EAAE,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC,KAAK,IAAK,QAAO,EAAE,QAAQ,KAAK;AACzE,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,MAAM,wBAAwB,WAAW,GAAG;AAAA,EAC9C;AACF;AAEO,IAAM,YAAqB;AAAA,EAChC,MAAML;AAAA,EACN,QAAQ;AAAA;AAAA,EAER,QAAQ,CAAC,MAAM,QAAQ,QAAQM,YAAW,SAAS,GAAG,CAAC,KAAKA,YAAWJ,OAAK,KAAK,UAAU,GAAG,GAAG,KAAK,CAAC;AAAA,EACvG,aAAa,CAAC,MAAM,QAAQ,QAAQ,CAAC,SAAS,GAAG,CAAC;AAAA,EAClD,SAAS,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAC9D,UAAM,IAAI,oBAAoB,SAAS,GAAG,GAAGF,OAAMI,WAAU,KAAK,IAAI,GAAG,KAAKH,KAAI;AAClF,UAAM,IAAI,kBAAkB,SAAS,GAAG,CAAC;AACzC,WAAO,CAAC,EAAE,QAAQ,CAAC,EAAE,UAAU,EAAE,GAAG,GAAG,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI,EAAE,IAAI,CAAC;AAAA,EAChG;AAAA,EACA,WAAW,CAAC,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAAA,IAC3D,sBAAsB,SAAS,GAAG,GAAG,eAAe,EAAE,SAAS,KAAK,SAAS,SAAS,SAAS,GAAG,CAAC,EAAE,CAAC;AAAA,EACxG;AAAA,EACA,WAAW,CAAC,KAAK,MAAM,QAAQ,KAAK,OAAuB,CAAC,MAAM;AAChE,UAAM,QAAQ,sBAAsB,SAAS,GAAG,GAAGD,OAAMI,WAAU,KAAK,IAAI,GAAG,KAAKH,KAAI;AACxF,UAAM,IAAI,kBAAkB,SAAS,GAAG,CAAC;AACzC,WAAO;AAAA,MACL;AAAA,QACE,MAAM,SAAS,GAAG;AAAA,QAClB,OAAO,UAAU,aAAa,CAAC,EAAE,SAAS,aAAa;AAAA,QACvD,GAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,SAAS,OAAqC;AAC5C,UAAM,QAAQ,MAAM,KAAK,CAAC,KAAK;AAC/B,QAAI,IAA6B,CAAC;AAClC,QAAI;AAEF,YAAM,IAAa,MAAM,QAAQ,KAAK,MAAM,MAAM,KAAK,IAAI,CAAC;AAC5D,UAAI,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,EAAG,KAAI;AAAA,IAC3D,QAAQ;AAAA,IAAC;AACT,UAAM,KAAK,EAAE;AACb,QAAI,CAAC,GAAI,QAAO;AAChB,UAAM,UAAiC,EAAE,IAAI,OAAO,EAAE,GAAG,KAAK,OAAO,EAAE,QAAQ,WAAW,EAAE,MAAM,MAAM,IAAI;AAC5G,UAAM,OAAOE,WAAU,EAAE,SAAS,IAAI,EAAE,YAAY;AACpD,UAAM,KAAK,CAAC,MAAc,UAAmC,EAAE,MAAM,MAAMI,OAAM,IAAI,GAAG,cAAc,MAAM;AAC5G,UAAM,SAA+B,CAAC;AACtC,YAAQ,OAAO;AAAA,MACb,KAAK;AACH,eAAO;AAAA,UACL,GAAG,mBAAmB;AAAA,YACpB,QACE,OAAO,EAAE,WAAW,YAAY,CAAC,WAAW,UAAU,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,EAAE,SAAS;AAAA,UACnG,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,gBAAgB,EAAE,cAAc,OAAO,EAAE,WAAW,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,OAAU,CAAC;AAAA,QACtG;AACA;AAAA,MACF,KAAK;AACH,YAAI,KAAM,QAAO,KAAK,GAAG,gBAAgB,EAAE,MAAM,OAAO,EAAE,WAAW,CAAC,CAAC;AACvE;AAAA,MACF,KAAK,aAAa;AAChB,cAAM,OAAO,EAAE;AACf,cAAM,MACJ,QAAQ,OAAO,SAAS,YAAY,KAAK,QACrC,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAK,UAAU,KAAK,KAAK,CAAC,EAAE,MAAM,GAAG,GAAI,IAC9F;AACN,YAAI;AACF,iBAAO;AAAA,YACL,GAAG,cAAc;AAAA,cACf;AAAA,cACA,IAAI,CAAC;AAAA,cACL,OAAO;AAAA,cACP,OAAO,EAAE;AAAA,cACT,QAAQ,MAAM,cAAc,EAAE;AAAA,YAChC,CAAC;AAAA,UACH;AACF;AAAA,MACF;AAAA,MACA,KAAK;AACH,eAAO;AAAA,UACL,GAAG,oBAAoB;AAAA,YACrB,cAAc,SAAS,KAAK,IAAI,CAAC;AAAA,YACjC,MAAM,EAAE,sBAAsB,mBAAmB,eAAe;AAAA,YAChE,SAAS,OAAO,EAAE,YAAY,WAAW,EAAE,QAAQ,MAAM,GAAG,GAAG,IAAI;AAAA,UACrE,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,cAAc,EAAE,QAAQ,OAAO,CAAC,CAAC;AAChD;AAAA,MACF,KAAK;AACH,eAAO,KAAK,GAAG,qBAAqB,EAAE,SAAS,EAAE,YAAY,WAAW,WAAW,OAAO,CAAC,CAAC;AAC5F;AAAA,MACF,KAAK;AACH,eAAO;AAAA,UACL,GAAG,iBAAiB;AAAA,YAClB,QACE,EAAE,WAAW,WAAW,EAAE,WAAW,YAAY,EAAE,WAAW,SAC1D,EAAE,SACF,EAAE,WAAW,sBACX,SACA;AAAA,UACV,CAAC;AAAA,QACH;AACA;AAAA,MACF;AACE,eAAO;AAAA,IACX;AACA,UAAM,IAAgB,EAAE,SAAS,QAAQ,OAAO,CAAC,EAAE;AACnD,QAAI,OAAO,EAAE,oBAAoB,SAAU,GAAE,aAAa,EAAE;AAC5D,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,MAAM,UAAU;AAC7B,QAAI,OAAO;AACX,QAAI;AACF,aAAOC,cAAa,MAAM,MAAM;AAAA,IAClC,QAAQ;AACN,aAAO,EAAE,OAAO,CAAC,GAAG,MAAM,SAAS;AAAA,IACrC;AACA,WAAO,WAAW,KAAK,MAAM,IAAI,GAAG,QAAQ;AAAA,EAC9C;AAAA,EAEA,MAAM,UAAU,YAA0B;AACxC,UAAM,MAAoB,CAAC;AAC3B,QAAI,CAAC,WAAY,QAAO;AACxB,UAAM,EAAE,MAAM,KAAK,IAAI,KAAK,YAAY,KAAK,MAAM,KAAK,IAAI;AAC5D,UAAM,IAAI,WAAW,IAAI;AACzB,UAAM,IAAI,WAAW,IAAI;AACzB,UAAM,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM;AAC7C,UAAM,MAAM,QAAS,MAAM,WAAW,MAAM,WAAW,MAAM,OAAQ;AACrE,UAAM,OAAO,OAAO,QAAQ,WAAW,IAAI,KAAK,IAAI;AACpD,QAAI,MAAM;AACR,YAAM,OAAO,KAAK,MAAM,IAAI,EAAE,CAAC;AAC/B,UAAI,QAAQ,KAAK,SAAS,KAAK,GAAG,KAAK,MAAM,GAAG,EAAE,EAAE,QAAQ,WAAW,EAAE,CAAC,WAAM;AAChF,UAAI,eAAe;AAAA,IACrB;AACA,UAAM,QAAQ,EAAE,OAAO,CAAC,EACrB,IAAI,CAAC,MAAM,KAAK,MAAM,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC,EAChD,OAAO,CAAC,MAAM,IAAI,CAAC;AACtB,QAAI,MAAM,QAAQ;AAChB,UAAI,aAAa,KAAK,IAAI,GAAG,KAAK;AAClC,UAAI,UAAU,KAAK,IAAI,GAAG,KAAK;AAAA,IACjC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,MAAM,CAAC;AAAA;AACnB;AAEA,SAASD,OAAyC,GAAS;AACzD,aAAW,KAAK,OAAO,KAAK,CAAC,EAAG,KAAI,EAAE,CAAC,MAAM,OAAW,QAAO,EAAE,CAAC;AAClE,SAAO;AACT;;;ACrNO,IAAM,WAA+B,CAAC,YAAY,OAAO,WAAW,aAAa,KAAK;AACtF,IAAM,gBAAgB,CAAC,SAAsC,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;;;ACZxG,SAAS,WAAW,cAAAE,aAAY,aAAAC,YAAW,gBAAAC,eAAc,iBAAAC,sBAAqB;AAC9E,OAAOC,YAAU;AAgDV,IAAM,iBAAyB,EAAE,OAAO,CAAC,GAAG,WAAW,CAAC,GAAG,cAAc,MAAM,WAAW,GAAG;AAE7F,SAAS,WAAW,OAAe,WAAW,GAAW;AAC9D,MAAI;AACF,UAAM,IAAI,KAAK,MAAMC,cAAa,MAAM,MAAM,CAAC;AAC/C,WAAO;AAAA,MACL,GAAG;AAAA,MACH,GAAG;AAAA,MACH,OAAO,MAAM,QAAQ,EAAE,KAAK,IAAI,EAAE,QAAQ,CAAC;AAAA,MAC3C,WAAW,EAAE,aAAa,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY,CAAC;AAAA,IAC7E;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,GAAG,gBAAgB,OAAO,CAAC,GAAG,WAAW,CAAC,EAAE;AAAA,EACvD;AACF;AAIA,SAAS,cAAc,MAAkC;AACvD,MAAI;AACJ,MAAI;AACF,WAAOA,cAAa,MAAM,MAAM;AAAA,EAClC,SAAS,GAAG;AAEV,UAAM,OAAQ,EAA4B;AAC1C,WAAO,SAAS,WAAW,SAAY,sBAAsB,IAAI;AAAA,EACnE;AACA,MAAI,CAAC,KAAK,KAAK,EAAG,QAAO;AACzB,MAAI,KAAK,WAAW,CAAC,MAAM,MAAQ,QAAO;AAC1C,MAAI;AACF,UAAM,IAAa,KAAK,MAAM,IAAI;AAClC,WAAO,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,IAAI,SAAY;AAAA,EACvE,SAAS,GAAG;AAEV,UAAM,IAAK,EAAY;AACvB,UAAM,MAAM,sBAAsB,KAAK,CAAC,KAAK,eAAe,KAAK,CAAC,KAAK,CAAC;AACxE,WAAO,oBAAoB,KAAK,OAAO,EAAE,KAAK,EAAE;AAAA,EAClD;AACF;AAKO,SAAS,qBAAqB,OAAe,WAAW,GAAS;AACtE,QAAM,UAAU,cAAc,IAAI;AAClC,MAAI;AACF,UAAM,IAAI;AAAA,MACR,GAAG,MAAM,IAAI,CAAC,IAAI,OAAO;AAAA,IAC3B;AACJ;AAEO,SAAS,YAAY,GAAW,OAAe,WAAW,GAAS;AACxE,uBAAqB,IAAI;AACzB,EAAAC,WAAUC,OAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC9D,EAAAC,eAAc,MAAM,GAAG,KAAK,UAAU,GAAG,MAAM,CAAC,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AACtE,MAAI,QAAQ,aAAa,WAAWC,YAAW,IAAI,GAAG;AACpD,QAAI;AACF,gBAAU,MAAM,GAAK;AAAA,IACvB,QAAQ;AAAA,IAAC;AAAA,EACX;AACF;AAKO,SAAS,YAAY,GAAW,UAA0B;AAC/D,MAAI,EAAE,QAAS,QAAO,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,EAAE;AACnD,SAAO,SAAS,QAAQ,wBAAwB,EAAE;AACpD;;;ACrHA,SAAS,mBAAmB;AAE5B,IAAM,WAAW;AACjB,IAAI,WAAW;AACf,IAAI,WAAqB,CAAC;AAE1B,SAAS,WAAW,GAAmB;AACrC,MAAI,MAAM;AACV,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,UAAM,SAAS,IAAI,EAAE,IAAI;AACzB,QAAI,KAAK,MAAM,IAAI,EAAE;AAAA,EACvB;AACA,SAAO;AACT;AAEO,SAAS,KAAK,MAAc,KAAK,IAAI,GAAW;AACrD,MAAI,QAAQ,UAAU;AAEpB,aAAS,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AAC7C,YAAM,IAAK,SAAS,CAAC,IAAe;AACpC,UAAI,IAAI,IAAI;AACV,iBAAS,CAAC,IAAI;AACd;AAAA,MACF;AACA,eAAS,CAAC,IAAI;AAAA,IAChB;AAAA,EACF,OAAO;AACL,eAAW;AACX,eAAW,MAAM,KAAK,YAAY,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;AAAA,EACtD;AACA,SAAO,WAAW,GAAG,IAAI,SAAS,IAAI,CAAC,MAAM,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE;AACnE;;;ACtBO,SAAS,UAAU,GAAiB,KAA0B;AACnE,QAAM,MAAM,IAAI,OAAO,KAAK,IAAI;AAChC,QAAM,UAA4B,EAAE,MAAM,IAAI,QAAQ,MAAM,OAAO,EAAE,aAAa;AAClF,MAAI,IAAI,QAAQ,QAAS,SAAQ,UAAU,IAAI,QAAQ;AACvD,SAAO;AAAA,IACL,UAAU;AAAA,IACV,IAAI,KAAK,GAAG;AAAA,IACZ,MAAM,EAAE;AAAA,IACR,MAAM,IAAI,KAAK,GAAG,EAAE,YAAY;AAAA,IAChC,MAAM;AAAA,IACN;AAAA,IACA,SAAS,IAAI;AAAA,IACb,SAAS,EAAE,UAAU,CAAC,GAAG,KAAK,MAAM;AAAA,IACpC,MAAM,EAAE;AAAA,EACV;AACF;;;ACtBA,SAAS,eAAAC,oBAAmB;AAC5B;AAAA,EACE;AAAA,EACA,aAAAC;AAAA,EACA,cAAAC;AAAA,EACA,aAAAC;AAAA,EACA,YAAAC;AAAA,EACA,eAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,YAAAC;AAAA,EACA;AAAA,EACA,YAAAC;AAAA,EACA,cAAAC;AAAA,EACA,iBAAAC;AAAA,OACK;AACP,OAAOC,YAAU;AASjB,IAAM,aAAa,OAAO;AAC1B,IAAM,WAAW,IAAI,OAAO;AAErB,IAAM,SAAN,MAAa;AAAA,EAClB,YAAqB,MAAc;AAAd;AAAA,EAAe;AAAA,EAAf;AAAA,EAErB,IAAI,SAAyB;AAC3B,WAAOC,OAAK,KAAK,KAAK,MAAM,UAAU,OAAO,OAAO,CAAC;AAAA,EACvD;AAAA,EACA,KAAK,KAAwB;AAC3B,WAAOA,OAAK,KAAK,KAAK,IAAI,IAAI,OAAO,GAAG,GAAG,OAAO,IAAI,OAAO,CAAC,QAAQ;AAAA,EACxE;AAAA,EACQ,QAAQ,KAAwB;AACtC,WAAOA,OAAK,KAAK,KAAK,IAAI,IAAI,OAAO,GAAG,GAAG,OAAO,IAAI,OAAO,CAAC,MAAM;AAAA,EACtE;AAAA;AAAA;AAAA,EAIA,QAAQ,KAAwB;AAC9B,UAAM,IAAI,KAAK,QAAQ,GAAG;AAC1B,QAAI,IAAI;AACR,QAAI;AACF,YAAM,IAAIC,cAAa,GAAG,MAAM,EAAE,KAAK;AACvC,UAAI,QAAQ,KAAK,CAAC,EAAG,KAAI,OAAO,CAAC;AAAA,IACnC,QAAQ;AAAA,IAAC;AAGT,QAAI,IAAI,EAAG,KAAI,KAAK,cAAc,GAAG,IAAI;AACzC,IAAAC,WAAUF,OAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3D,IAAAG,eAAc,GAAG,OAAO,IAAI,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AAC/C,WAAO;AAAA,EACT;AAAA,EAEQ,cAAc,KAAwB;AAC5C,UAAM,IAAI,KAAK,KAAK,GAAG;AACvB,QAAI,CAACC,YAAW,CAAC,EAAG,QAAO;AAC3B,UAAM,QAAQH,cAAa,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI;AACvD,aAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,UAAI;AACF,cAAM,IAAI,KAAK,MAAM,MAAM,CAAC,CAAW;AACvC,YAAI,OAAO,EAAE,SAAS,QAAQ,SAAU,QAAO,EAAE,QAAQ;AAAA,MAC3D,QAAQ;AAAA,MAAC;AAAA,IACX;AACA,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,KAAgB,QAAuB;AAC5C,QAAI,CAAC,OAAO,OAAQ;AACpB,IAAAC,WAAU,KAAK,IAAI,IAAI,OAAO,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACjE,mBAAe,KAAK,KAAK,GAAG,GAAG,GAAG,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AAAA,EACxG;AAAA;AAAA,EAGA,WAAwB;AACtB,UAAM,MAA2C,CAAC;AAClD,UAAM,OAAOF,OAAK,KAAK,KAAK,MAAM,QAAQ;AAC1C,QAAI,CAACI,YAAW,IAAI,EAAG,QAAO,CAAC;AAC/B,eAAW,KAAKC,aAAY,IAAI,GAAG;AACjC,YAAM,IAAIL,OAAK,KAAK,MAAM,CAAC;AAC3B,UAAI,QAAkB,CAAC;AACvB,UAAI;AACF,gBAAQK,aAAY,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,QAAQ,CAAC;AAAA,MAC3D,QAAQ;AACN;AAAA,MACF;AACA,iBAAW,KAAK,OAAO;AACrB,YAAI;AACF,cAAI,KAAK,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,OAAOC,UAASN,OAAK,KAAK,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC;AAAA,QACrG,QAAQ;AAAA,QAAC;AAAA,MACX;AAAA,IACF;AACA,WAAO,IAAI,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,KAAK,KAAgB,MAAc,YAAY,KAA0C;AACvF,UAAM,IAAI,KAAK,KAAK,GAAG;AACvB,QAAI,CAACI,YAAW,CAAC,EAAG,QAAO,EAAE,QAAQ,CAAC,GAAG,QAAQ,KAAK;AACtD,UAAM,OAAOE,UAAS,CAAC,EAAE;AACzB,QAAI,QAAQ,KAAM,QAAO,EAAE,QAAQ,CAAC,GAAG,QAAQ,KAAK;AACpD,UAAM,KAAKC,UAAS,GAAG,GAAG;AAC1B,QAAI;AACF,YAAM,SAAkB,CAAC;AACzB,UAAI,SAAS;AACb,UAAI,QAAQ;AACZ,aAAO,OAAO,SAAS,aAAa,SAAS,MAAM;AACjD,cAAM,MAAM,OAAO,MAAM,KAAK,IAAI,OAAO,QAAQ,KAAK,CAAC;AACvD,cAAM,IAAIC,UAAS,IAAI,KAAK,GAAG,IAAI,QAAQ,MAAM;AACjD,cAAM,OAAO,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,MAAM;AAC/C,YAAI,MAAM;AACV,YAAI,OAAO;AACX,eAAO,OAAO,SAAS,WAAW;AAChC,gBAAM,KAAK,KAAK,QAAQ,MAAM,GAAG;AACjC,cAAI,KAAK,EAAG;AACZ,gBAAM,OAAO,KAAK,MAAM,KAAK,EAAE;AAC/B,gBAAM,KAAK;AACX,kBAAQ,OAAO,WAAW,MAAM,MAAM,IAAI;AAC1C,cAAI,CAAC,KAAK,KAAK,EAAG;AAClB,cAAI;AACF,mBAAO,KAAK,KAAK,MAAM,IAAI,CAAU;AAAA,UACvC,QAAQ;AAAA,UAAC;AAAA,QACX;AACA,kBAAU;AACV,YAAI,KAAM;AAEV,YAAI,SAAS,KAAK,QAAQ,SAAS,SAAU;AAC7C,iBAAS;AAAA,MACX;AACA,aAAO,EAAE,QAAQ,OAAO;AAAA,IAC1B,UAAE;AACA,MAAAC,WAAU,EAAE;AAAA,IACd;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,MAAc,MAAM,KAAK,IAAI,GAAW;AAC5C,QAAI,UAA+B;AACnC,QAAI;AACF,gBAAU,cAAc,KAAK,MAAM,OAAO;AAAA,IAC5C,QAAQ;AAAA,IAAC;AACT,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI,IAAI;AACR,QAAI;AACF,YAAM,SAAS,MAAM,OAAO;AAC5B,YAAM,OAAO,KAAK,SAAS,EAAE,OAAO,CAAC,QAAQ;AAC3C,YAAI;AACF,iBAAOH,UAAS,KAAK,KAAK,GAAG,CAAC,EAAE,UAAU;AAAA,QAC5C,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF,CAAC;AAKD,WAAK,iBAAiB,IAAI,IAAI,KAAK,IAAI,CAAC,QAAQ,GAAG,IAAI,OAAO,IAAI,IAAI,OAAO,EAAE,CAAC,CAAC;AACjF,iBAAW,OAAO,MAAM;AACtB,cAAM,IAAI,KAAK,KAAK,GAAG;AACvB,YAAI;AACF,cAAIA,UAAS,CAAC,EAAE,UAAU,QAAQ;AAChC,YAAAI,YAAW,CAAC;AACZ;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAAC;AAAA,MACX;AAAA,IACF,QAAQ;AAAA,IACR,UAAE;AACA,cAAQ;AAAA,IACV;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,MAAyB;AAChD,QAAI,QAAkB,CAAC;AACvB,QAAI;AACF,cAAQL,aAAYL,OAAK,KAAK,KAAK,MAAM,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AAAA,IACxF,QAAQ;AAAA,IAAC;AACT,UAAM,QAAQ,CAAC,MACb,OAAO,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM;AAC3B,UAAI,KAAK,IAAI,CAAC,EAAG,QAAO;AACxB,YAAM,IAAI,EAAE,QAAQ,GAAG;AACvB,aAAO,CAACI,YAAW,KAAK,KAAK,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,EAAE,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC;AAAA,IACnF,CAAC;AACH,eAAW,KAAK,OAAO;AACrB,YAAM,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAE,MAAM,GAAG,EAAE,CAAC;AAC/C,YAAM,IAAI,EAAE,KAAK;AACjB,YAAM,OAAO,MAAM,CAAC;AACpB,UAAI,CAAC,KAAK,OAAQ;AAClB,iBAAW,KAAK,KAAM,QAAO,EAAE,CAAC;AAChC,QAAE,MAAM,CAAC;AAAA,IACX;AAAA,EACF;AAAA,EAEA,OAAO,KAAsB;AAK3B,SAAK,YAAY,GAAG;AACpB,eAAW,KAAK,CAAC,KAAK,KAAK,GAAG,GAAG,KAAK,QAAQ,GAAG,CAAC,GAAG;AACnD,UAAI;AACF,QAAAM,YAAW,CAAC;AAAA,MACd,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,YAAY,KAAsB;AACxC,UAAM,OAAO,oBAAI,IAAI,CAAC,GAAG,IAAI,OAAO,IAAI,IAAI,OAAO,IAAI,GAAG,OAAO,IAAI,OAAO,CAAC,IAAI,OAAO,IAAI,OAAO,CAAC,EAAE,CAAC;AACvG,QAAI,QAAkB,CAAC;AACvB,QAAI;AACF,cAAQL,aAAYL,OAAK,KAAK,KAAK,MAAM,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC;AAAA,IACxF,QAAQ;AACN;AAAA,IACF;AACA,eAAW,KAAK,OAAO;AACrB,YAAM,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAE,MAAM,GAAG,EAAE,CAAC;AAC/C,YAAM,IAAI,EAAE,KAAK;AACjB,YAAM,MAAM,OAAO,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC;AACpD,UAAI,CAAC,IAAI,OAAQ;AACjB,iBAAW,KAAK,IAAK,QAAO,EAAE,CAAC;AAC/B,QAAE,MAAM,CAAC;AAAA,IACX;AAAA,EACF;AACF;AAGO,IAAM,UAAN,MAAc;AAAA,EACnB,YACW,MACA,MACT;AAFS;AACA;AAAA,EACR;AAAA,EAFQ;AAAA,EACA;AAAA,EAEH,OAAe;AACrB,WAAOA,OAAK,KAAK,KAAK,MAAM,WAAW,GAAG,OAAO,KAAK,IAAI,CAAC,OAAO;AAAA,EACpE;AAAA;AAAA;AAAA,EAGA,OAA+B;AAC7B,QAAI;AACF,YAAM,IAAI,KAAK,MAAMC,cAAa,KAAK,KAAK,GAAG,MAAM,CAAC;AACtD,aAAO,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,IAAK,IAA+B,CAAC;AAAA,IAC5F,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EACA,MAAM,GAAiC;AACrC,UAAM,IAAI,KAAK,KAAK;AACpB,IAAAC,WAAUF,OAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3D,UAAM,MAAM,GAAG,CAAC,IAAI,QAAQ,GAAG;AAC/B,IAAAG,eAAc,KAAK,KAAK,UAAU,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AACrD,eAAW,KAAK,CAAC;AAAA,EACnB;AAAA,EACA,IAAI,KAAwB;AAC1B,WAAO,GAAG,IAAI,OAAO,IAAI,IAAI,OAAO;AAAA,EACtC;AACF;AAIO,SAAS,SAAS,MAAc,KAAgB,UAAU,IAAI,KAA6B;AAChG,SAAO,cAAc,MAAM,GAAG,OAAO,IAAI,OAAO,CAAC,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,OAAO;AACrF;AAUO,SAAS,cAAc,MAAc,MAAc,UAAU,IAAI,KAA6B;AACnG,QAAM,MAAMH,OAAK,KAAK,MAAM,OAAO;AACnC,EAAAE,WAAU,KAAK,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC/C,QAAM,IAAIF,OAAK,KAAK,KAAK,GAAG,IAAI,OAAO;AACvC,QAAM,QAAQW,aAAY,CAAC,EAAE,SAAS,KAAK;AAC3C,QAAM,OAAO,GAAG,QAAQ,GAAG,IAAI,KAAK;AACpC,MAAI,OAAsB;AAC1B,MAAI;AACF,WAAOV,cAAa,GAAG,MAAM;AAC7B,UAAM,QAAQ,KAAK,IAAI,IAAIK,UAAS,CAAC,EAAE,UAAU,WAAW,CAAC,aAAa,OAAO,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC;AACpG,QAAI,CAAC,MAAO,QAAO;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,SAAS,MAAM;AACjB,UAAM,QAAQ,GAAG,CAAC,IAAI,QAAQ,GAAG,IAAI,KAAK;AAC1C,QAAI;AACF,iBAAW,GAAG,KAAK;AAAA,IACrB,QAAQ;AACN,aAAO;AAAA,IACT;AAEA,UAAM,QAAQL,cAAa,OAAO,MAAM;AACxC,IAAAS,YAAW,KAAK;AAChB,QAAI,UAAU,MAAM;AAClB,UAAI;AACF,QAAAP,eAAc,GAAG,OAAO,EAAE,MAAM,KAAK,CAAC;AAAA,MACxC,QAAQ;AAAA,MAAC;AACT,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI;AACF,IAAAA,eAAc,GAAG,MAAM,EAAE,MAAM,KAAK,CAAC;AAAA,EACvC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,MAAM;AACX,QAAI;AACF,UAAIF,cAAa,GAAG,MAAM,MAAM,KAAM,CAAAS,YAAW,CAAC;AAAA,IACpD,QAAQ;AAAA,IAAC;AAAA,EACX;AACF;AAoBO,SAAS,aAAa,KAAsB;AACjD,MAAI,CAAC,OAAO,UAAU,GAAG,KAAK,OAAO,EAAG,QAAO;AAC/C,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,SAAS,GAAG;AACV,WAAQ,EAA4B,SAAS;AAAA,EAC/C;AACF;;;AClXA,SAAS,cAAAE,mBAAkB;AAEpB,IAAM,cAAc;AAE3B,IAAM,QAAQ;AAYd,IAAM,QACJ;AAEF,SAAS,WAAW,GAAoB;AACtC,QAAM,SAAS,EAAE,QAAQ,OAAO,EAAE;AAClC,MAAI,OAAO,SAAS,KAAK,OAAO,SAAS,GAAI,QAAO;AAEpD,MAAI,CAAC,WAAW,KAAK,CAAC,EAAG,QAAO;AAEhC,MAAI,CAAC,UAAU,KAAK,CAAC,EAAG,QAAO;AAC/B,SAAO;AACT;AAGO,SAAS,UAAU,MAAc,MAAuB;AAC7D,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,MAAM,KAAK,QAAQ,OAAO,SAAS;AACvC,QAAM,IAAI,QAAQ,OAAO,CAAC,GAAG,SAAmB,CAAC,QAAQ,WAAW,CAAC,IAAI,YAAY,CAAE;AACvF,MAAI,MAAM;AACR,UAAM,IAAI,KAAK,QAAQ,uBAAuB,MAAM;AACpD,UAAM,IAAI,QAAQ,IAAI,OAAO,oBAAoB,CAAC,sBAAsB,GAAG,GAAG,GAAG;AAAA,EACnF;AACA,SAAO;AACT;AAGO,SAAS,YAAY,MAAsB;AAChD,SAAOA,YAAW,QAAQ,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC;AACnE;AAGO,SAAS,cAAiB,GAAM,MAAkB;AACvD,MAAI,OAAO,MAAM,SAAU,QAAO,UAAU,GAAG,IAAI;AACnD,MAAI,MAAM,QAAQ,CAAC,EAAG,QAAO,EAAE,IAAI,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AAChE,MAAI,KAAK,OAAO,MAAM,UAAU;AAC9B,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,CAA4B,EAAG,KAAI,CAAC,IAAI,cAAc,GAAG,IAAI;AACjG,WAAO;AAAA,EACT;AACA,SAAO;AACT;;;AClDO,IAAM,kBAAkB;AACxB,IAAM,WAAW;AAIxB,IAAM,iBAA2B;AAAA,EAC/B;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAEA,IAAM,kBAAkB;AAExB,IAAM,cAAc;AAGpB,IAAM,cACJ;AAKF,IAAM,WAAW;AACjB,IAAM,QAAQ;AAEd,SAAS,kBAAkB,MAAsB;AAC/C,MAAI,MAAM;AACV,MAAI,OAAO;AACX,WAAS,YAAY;AACrB,WAAS,IAAI,SAAS,KAAK,IAAI,GAAG,GAAG,IAAI,SAAS,KAAK,IAAI,GAAG;AAC5D,UAAM,OAAO,EAAE,CAAC;AAChB,QAAI,CAAC,aAAa,IAAI,EAAG;AACzB,UAAM,YAAY,SAAS;AAC3B,UAAM,IAAI,MAAM,KAAK,IAAI;AACzB,QAAI,CAAC,EAAG;AACR,UAAM,QAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC;AAC1C,aAAS,YAAY,MAAM;AAC3B,QAAI,cAAc,KAAK,KAAK,MAAM,SAAS,QAAQ,EAAG;AAEtD,QAAI,YAAY,KAAK,IAAI,KAAK,0BAA0B,KAAK,KAAK,EAAG;AACrE,UAAM,IAAI,EAAE,CAAC,KAAK,OAAO,MAAM,EAAE,CAAC,KAAK,OAAO,MAAM,EAAE,CAAC,KAAK,OAAO,MAAM;AACzE,WAAO,KAAK,MAAM,MAAM,EAAE,KAAK,IAAI,IAAI,WAAW;AAClD,WAAO,MAAM;AAAA,EACf;AACA,SAAO,OAAO,MAAM,KAAK,MAAM,IAAI,IAAI;AACzC;AAKO,SAAS,aAAa,MAAuB;AAClD,MAAI,mBAAmB,KAAK,IAAI,EAAG,QAAO;AAC1C,QAAM,OAAO,KAAK,YAAY,EAAE,QAAQ,SAAS,EAAE;AACnD,SAAO,6KAA6K;AAAA,IAClL;AAAA,EACF;AACF;AAKA,IAAM,QAAQ,IAAI;AAAA,EAChB,OAAO,WAAW;AAAA,EAClB;AACF;AAIA,SAAS,cAAc,GAAoB;AACzC,QAAM,IAAI,EAAE,QAAQ,cAAc,EAAE;AACpC,MAAI,CAAC,EAAG,QAAO;AACf,MACE,4KAA4K;AAAA,IAC1K;AAAA,EACF;AAEA,WAAO;AACT,MAAI,oBAAoB,KAAK,CAAC,KAAK,QAAQ,KAAK,CAAC,KAAK,WAAW,KAAK,CAAC,KAAK,cAAc,KAAK,CAAC,EAAG,QAAO;AAC1G,MAAI,UAAU,KAAK,CAAC,EAAG,QAAO;AAC9B,MACE,6KAA6K;AAAA,IAC3K;AAAA,EACF;AAEA,WAAO;AACT,MAAI,4CAA4C,KAAK,CAAC,EAAG,QAAO;AAChE,MAAI,MAAM,KAAK,CAAC,EAAG,QAAO;AAC1B,MAAI,2CAA2C,KAAK,CAAC,EAAG,QAAO;AAC/D,SAAO;AACT;AAGA,SAAS,YAAY,GAAoB;AACvC,QAAM,IAAI,EAAE,QAAQ,eAAe,EAAE;AACrC,MAAI,EAAE,SAAS,EAAG,QAAO;AACzB,MAAI,cAAc,CAAC,EAAG,QAAO;AAC7B,MAAI,gBAAgB,KAAK,CAAC,EAAG,QAAO;AACpC,MAAI,0BAA0B,KAAK,CAAC,EAAG,QAAO;AAC9C,MAAI,gBAAgB,KAAK,CAAC,EAAG,QAAO;AACpC,MAAI,4BAA4B,KAAK,CAAC,EAAG,QAAO;AAChD,MAAI,kCAAkC,KAAK,CAAC,EAAG,QAAO;AACtD,QAAM,QAAQ,CAAC,SAAS,SAAS,SAAS,cAAc,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE;AACnF,SAAO,SAAS,KAAM,QAAQ,KAAK,CAAC,KAAK,WAAW,KAAK,CAAC;AAC5D;AAGO,SAAS,cAAc,MAAsB;AAClD,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,MAAM;AACV,aAAW,MAAM,eAAgB,OAAM,IAAI,QAAQ,IAAI,QAAQ;AAC/D,QAAM,IAAI,QAAQ,iBAAiB,CAAC,IAAI,MAAc,KAAa,OAAe,GAAG,IAAI,GAAG,QAAQ,GAAG,EAAE,EAAE;AAC3G,QAAM,IAAI,QAAQ,aAAa,CAAC,IAAI,SAAiB,GAAG,IAAI,GAAG,QAAQ,EAAE;AACzE,QAAM,kBAAkB,GAAG;AAC3B,QAAM,IAAI;AAAA,IAAQ;AAAA,IAAO,CAAC,GAAG,MAAc,GAAW,MACpD,CAAC,YAAY,CAAC,KAAK,EAAE,SAAS,QAAQ,IAAI,IAAI,GAAG,IAAI,GAAG,CAAC,GAAG,QAAQ,GAAG,CAAC;AAAA,EAC1E;AACA,SAAO;AACT;AAQO,SAAS,WAAc,GAAS;AACrC,MAAI,OAAO,MAAM,SAAU,QAAO,cAAc,CAAC;AACjD,MAAI,MAAM,QAAQ,CAAC,EAAG,QAAO,EAAE,IAAI,UAAU;AAC7C,MAAI,KAAK,OAAO,MAAM,UAAU;AAC9B,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,CAA4B,EAAG,KAAI,CAAC,IAAI,WAAW,CAAC;AACxF,WAAO;AAAA,EACT;AACA,SAAO;AACT;;;ACpKO,IAAM,YAAkC;AAAA,EAC7C,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,QAAQ;AACV;AAGA,IAAM,aAA8D;AAAA,EAClE,mBAAmB,EAAE,GAAG,CAAC,QAAQ,EAAE;AAAA,EACnC,qBAAqB,EAAE,GAAG,CAAC,EAAE;AAAA,EAC7B,iBAAiB,EAAE,GAAG,CAAC,QAAQ,EAAE;AAAA,EACjC,oBAAoB,EAAE,GAAG,CAAC,cAAc,WAAW,OAAO,EAAE;AAAA,EAC5D,qBAAqB,EAAE,GAAG,CAAC,EAAE;AAAA,EAC7B,gBAAgB,EAAE,GAAG,CAAC,WAAW,gBAAgB,UAAU,GAAG,GAAG,CAAC,cAAc,EAAE;AAAA,EAClF,cAAc,EAAE,GAAG,CAAC,WAAW,UAAU,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,EAAE;AAAA,EAC3E,mBAAmB,EAAE,GAAG,CAAC,QAAQ,QAAQ,aAAa,MAAM,UAAU,EAAE;AAAA,EACxE,iBAAiB,EAAE,GAAG,CAAC,QAAQ,QAAQ,aAAa,MAAM,UAAU,EAAE;AAAA,EACtE,gBAAgB,EAAE,GAAG,CAAC,QAAQ,WAAW,WAAW,UAAU,GAAG,GAAG,CAAC,OAAO,EAAE;AAAA,EAC9E,cAAc,EAAE,GAAG,CAAC,QAAQ,WAAW,WAAW,YAAY,MAAM,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,QAAQ,EAAE;AAAA,EAC3G,iBAAiB,EAAE,GAAG,CAAC,OAAO,EAAE;AAAA,EAChC,oBAAoB,EAAE,GAAG,CAAC,gBAAgB,MAAM,GAAG,GAAG,CAAC,QAAQ,WAAW,UAAU,EAAE;AAAA,EACtF,qBAAqB,EAAE,GAAG,CAAC,gBAAgB,KAAK,EAAE;AAAA,EAClD,oBAAoB,EAAE,GAAG,CAAC,YAAY,YAAY,EAAE;AAAA,EACpD,kBAAkB,EAAE,GAAG,CAAC,YAAY,cAAc,YAAY,EAAE;AAAA,EAChE,qBAAqB,EAAE,GAAG,CAAC,SAAS,EAAE;AAAA,EACtC,mBAAmB,EAAE,GAAG,CAAC,WAAW,QAAQ,YAAY,EAAE;AAAA,EAC1D,sBAAsB,EAAE,GAAG,CAAC,UAAU,OAAO,WAAW,IAAI,GAAG,GAAG,CAAC,MAAM,EAAE;AAAA,EAC3E,oBAAoB,EAAE,GAAG,CAAC,UAAU,MAAM,MAAM,MAAM,SAAS,SAAS,GAAG,GAAG,CAAC,OAAO,EAAE;AAC1F;AAEA,IAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAM,WAAW;AACjB,IAAM,SAAS,KAAK;AAEpB,SAAS,YAAY,MAAc,MAA6B;AAC9D,QAAM,QAAQ,WAAW,IAAI;AAC7B,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,MAAgB,CAAC;AACvB,WAAS,IAAI,GAAG,KAAK,MAAM,IAAK,YAAW,KAAK,MAAM,CAAS,KAAK,CAAC,EAAG,KAAI,KAAK,CAAC;AAClF,SAAO;AACT;AAEA,SAAS,IAAI,GAAY,KAAsB;AAC7C,MAAI,OAAO,MAAM,SAAU,QAAO,EAAE,SAAS,MAAM,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,WAAM;AAC3E,MAAI,KAAK,OAAO,MAAM,UAAU;AAC9B,UAAM,IAAI,KAAK,UAAU,CAAC;AAC1B,WAAO,EAAE,SAAS,MAAM,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,WAAM;AAAA,EAClD;AACA,SAAO;AACT;AAGO,SAAS,QAAQ,GAAW,KAAqB;AACtD,SAAO,EAAE,SAAS,MAAM,SAAI,EAAE,MAAM,EAAE,SAAS,MAAM,CAAC,CAAC,KAAK;AAC9D;AAQO,SAAS,YAAY,OAAc,MAAY,KAAc,OAAsB,CAAC,GAAiB;AAC1G,QAAM,UAAU,UAAU,MAAM,IAAI;AACpC,MAAI,YAAY,UAAa,UAAU,KAAM,QAAO;AAEpD,QAAM,UAAmC,CAAC;AAC1C,aAAW,KAAK;AACd,QAAI,MAAM,QAAQ,CAA+B,MAAM;AACrD,cAAQ,CAAC,IAAI,MAAM,QAAQ,CAA+B;AAE9D,MAAI,OAAgC,CAAC;AACrC,QAAM,OAAO,YAAY,MAAM,MAAM,IAAI;AACzC,MAAI,SAAS,KAAM,QAAO,EAAE,GAAG,MAAM,KAAK;AAAA,MACrC,YAAW,KAAK,KAAM,KAAI,MAAM,KAAK,CAAC,MAAM,OAAW,MAAK,CAAC,IAAI,MAAM,KAAK,CAAC;AAClF,MAAI,OAAO,KAAK,SAAS,SAAU,MAAK,OAAO,IAAI,KAAK,MAAM,QAAQ;AAEtE,MAAI,OAAO,KAAK,cAAc;AAC5B,SAAK,YAAY,MAAM,SAAS,kBAAkB,QAAQ,KAAK,WAAW,QAAQ,IAAI,IAAI,KAAK,WAAW,QAAQ;AACpH,MAAI,KAAK,UAAU,OAAW,MAAK,QAAQ,IAAI,KAAK,OAAO,MAAM;AACjE,MAAI,KAAK,WAAW,OAAW,MAAK,SAAS,IAAI,KAAK,QAAQ,MAAM;AACpE,MAAI,OAAO,KAAK,YAAY,SAAU,MAAK,UAAU,IAAI,KAAK,SAAS,GAAG;AAG1E,MAAI,MAAM,SAAS,wBAAwB,OAAO,KAAK,SAAS,SAAU,MAAK,OAAO,IAAI,KAAK,MAAM,GAAG;AACxG,MAAI,MAAM,SAAS,sBAAsB,OAAO,KAAK,UAAU,SAAU,MAAK,QAAQ,IAAI,KAAK,OAAO,QAAQ;AAE9G,QAAM,WAAqB,CAAC;AAC5B,MAAI,MAAa,EAAE,GAAG,OAAO,MAAM,SAAsC,MAAM,SAAS,EAAE,UAAU,IAAI,EAAE;AAC1G,MAAI,OAAO,GAAG;AAEZ,UAAM,EAAE,GAAG,KAAK,SAAS,WAAW,IAAI,OAAO,GAAG,MAAM,WAAW,IAAI,IAAI,EAAE;AAC7E,aAAS,KAAK,eAAe;AAAA,EAC/B;AACA,MAAI,KAAK;AACP,UAAM,IAAI,cAAc,IAAI,SAAS,KAAK,IAAI;AAC9C,QAAI,OAAO,EAAE,YAAY,SAAU,GAAE,UAAU,YAAY,EAAE,OAAO;AACpE,WAAO,EAAE;AACT,UAAM,IAAI,cAAc,IAAI,MAAM,KAAK,IAAI;AAG3C,WAAO,EAAE;AACT,UAAM,EAAE,GAAG,KAAK,SAAS,GAAuB,MAAM,EAAE;AACxD,aAAS,KAAK,WAAW;AAAA,EAC3B;AACA,MAAI,UAAU,EAAE,UAAU,IAAI;AAC9B,SAAO;AACT;;;ACjJA,SAAS,cAAAC,mBAAkB;AAEpB,SAAS,QAAQ,SAAiB,IAAoB;AAC3D,SAAOA,YAAW,QAAQ,EAAE,OAAO,uBAAuB,OAAO,IAAI,EAAE,EAAE,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC;AACrG;;;ACRA,SAAS,kBAAAC,iBAAgB,aAAAC,kBAAiB;AAC1C,OAAOC,YAAU;AAIV,IAAM,WAAN,MAA+B;AAAA,EACpC,YACW,MACA,MACA,MACA,KACT;AAJS;AACA;AACA;AACA;AAAA,EACR;AAAA,EAJQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEX,MAAM,KAAK,QAA0C;AACnD,QAAI;AACF,MAAAD,WAAUC,OAAK,QAAQ,KAAK,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACtD,MAAAF,gBAAe,KAAK,MAAM,GAAG,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,CAAI;AAChF,aAAO,EAAE,IAAI,MAAM,cAAc,OAAO,QAAQ,UAAU,OAAO,QAAQ,YAAY,GAAG,UAAU,CAAC,EAAE;AAAA,IACvG,SAAS,GAAG;AACV,aAAO,EAAE,IAAI,OAAO,cAAc,GAAG,OAAO,OAAQ,EAAY,OAAO,GAAG,QAAQ,QAAQ;AAAA,IAC5F;AAAA,EACF;AACF;;;ACjBO,IAAM,aAAN,MAAiC;AAAA,EACtC,YACW,MACA,MACA,KACQ,QAAgC,CAAC,MAAM,QAAQ,OAAO,MAAM,GAAG,CAAC;AAAA,CAAI,GACrF;AAJS;AACA;AACA;AACQ;AAAA,EAChB;AAAA,EAJQ;AAAA,EACA;AAAA,EACA;AAAA,EACQ;AAAA,EAEnB,MAAM,KAAK,QAA0C;AACnD,QAAI;AACF,iBAAW,KAAK,OAAQ,MAAK,MAAM,KAAK,UAAU,CAAC,CAAC;AACpD,aAAO,EAAE,IAAI,MAAM,cAAc,OAAO,QAAQ,UAAU,OAAO,QAAQ,YAAY,GAAG,UAAU,CAAC,EAAE;AAAA,IACvG,SAAS,GAAG;AAEV,aAAO,EAAE,IAAI,OAAO,cAAc,GAAG,OAAO,OAAQ,EAAY,WAAW,CAAC,GAAG,QAAQ,QAAQ;AAAA,IACjG;AAAA,EACF;AACF;;;ACTA,SAAS,aAAAG,YAAW,aAAAC,YAAW,gBAAAC,gBAAc,cAAAC,aAAY,iBAAAC,sBAAqB;AAC9E,OAAOC,YAAU;;;ACFjB,SAAS,iBAAiB;AAC1B,SAAS,cAAAC,aAAY,eAAAC,oBAAmB;AAMxC,IAAM,UAAU;AAGT,IAAM,gBAAwB,CAAC,KAAK,MAAM,UAAU;AACzD,QAAM,IAAI,UAAU,KAAK,MAAM;AAAA,IAC7B,OAAO,SAAS;AAAA,IAChB,UAAU;AAAA,IACV,SAAS;AAAA,IACT,OAAO,CAAC,QAAQ,QAAQ,QAAQ;AAAA,EAClC,CAAC;AACD,SAAO,EAAE,QAAQ,EAAE,QAAQ,OAAO,EAAE,QAAQ,QAAQ,EAAE,UAAU,GAAG;AACrE;AAKO,IAAM,cAAc,CAAC,MAAc,OAA2B,YAAoB;AACvF,QAAM,QAAQ,QAAQ,IAAI,qBACtB,IAAIC,YAAW,QAAQ,EAAE,OAAO,WAAW,CAAC,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,KACvE;AACJ,SAAO,QAAQ,IAAI,IAAI,IAAI,GAAG,KAAK;AACrC;AASO,SAAS,UAAU,OAAoB,SAAiB,MAAc,eAA8B;AACzG,QAAM,IACJ,UAAU,aACN,IAAI,YAAY,CAAC,yBAAyB,MAAM,SAAS,MAAM,SAAS,IAAI,CAAC,IAC7E,IAAI,eAAe,CAAC,UAAU,WAAW,SAAS,WAAW,OAAO,CAAC;AAC3E,MAAI,EAAE,WAAW,EAAG,QAAO;AAC3B,QAAM,IAAI,EAAE,OAAO,QAAQ,UAAU,EAAE;AACvC,SAAO,KAAK;AACd;AAoFO,SAAS,gBAAgB,GAAe,MAAc,eAAkC;AAC7F,MAAI,CAAC,EAAE,SAAU,QAAO;AACxB,QAAM,QAAQ,UAAU,EAAE,UAAU,YAAY,EAAE,IAAI,GAAG,GAAG;AAC5D,QAAM,SAAS,UAAU,EAAE,UAAU,YAAY,EAAE,MAAM,QAAQ,GAAG,GAAG;AACvE,MAAI,CAAC,SAAS,CAAC,OAAQ,QAAO;AAC9B,SAAO,EAAE,GAAG,GAAG,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,GAAI,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC,EAAG;AAC5E;;;ACyGO,SAAS,eAAe,GAAiC;AAC9D,QAAM,IAAI;AACV,MAAI,CAAC,KAAK,OAAO,MAAM,YAAa,UAAU,EAAE,UAAU,IAAI,KAAK,EAAE,eAAe,UAAY,QAAO;AACvG,SAAO;AAAA,IACL,YAAY,EAAE;AAAA,IACd,GAAI,OAAO,EAAE,UAAU,YAAY,SAAS,KAAK,EAAE,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAAA,EACpF;AACF;;;AFrOO,IAAM,YAAY,CAAC,eAA+BC,OAAK,KAAK,YAAY,cAAc;AAEtF,SAAS,YAAY,MAAuB;AACjD,MAAI;AACF,UAAM,IAAI,KAAK,MAAMC,eAAa,MAAM,MAAM,CAAC;AAC/C,QAAI,CAAC,KAAK,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,EAAG,QAAO,CAAC;AAC7D,UAAM,MAAe,CAAC;AACtB,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,CAA4B,GAAG;AACjE,YAAM,IAAI;AACV,UAAI,KAAK,OAAO,EAAE,OAAO,YAAY,EAAE,MAAM,OAAO,EAAE,QAAQ;AAC5D,YAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,KAAK,IAAI,OAAO,EAAE,OAAO,WAAW,EAAE,KAAK,IAAI,GAAG,eAAe,CAAC,EAAE;AAAA,IACpG;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAcO,SAAS,aAAa,KAAc,SAAiB,IAA2B;AACrF,QAAM,SAAS,GAAG,OAAO;AACzB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAG,EAAG,KAAI,EAAE,OAAO,MAAM,EAAE,WAAW,MAAM,EAAG,QAAO,EAAE,MAAM,OAAO,MAAM;AAC/G,SAAO;AACT;;;AGzDA,OAAO,SAAS;AAChB,OAAOC,SAAQ;AACf,OAAOC,YAAU;AAiGV,SAAS,WAAW,OAAe,MAAyB,QAAQ,KAAa;AACtF,MAAI,IAAI,mBAAoB,QAAO,IAAI;AACvC,MAAI,QAAQ,aAAa,QAAS,QAAO,oCAAoCD,IAAG,SAAS,EAAE,QAAQ;AACnG,SAAOC,OAAK,KAAK,OAAO,cAAc;AACxC;AAIO,SAAS,IAAI,MAAc,KAAmB,WAA+C;AAClG,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,MAAM;AACV,QAAI,OAAO;AACX,UAAM,IAAI,IAAI,QAAQ,IAAI;AAC1B,UAAM,SAAS,CAAC,MAAyB;AACvC,UAAI,KAAM;AACV,aAAO;AACP,mBAAa,CAAC;AACd,QAAE,QAAQ;AACV,cAAQ,CAAC;AAAA,IACX;AACA,UAAM,IAAI,WAAW,MAAM,OAAO,IAAI,GAAG,SAAS;AAClD,MAAE,GAAG,WAAW,MAAM,EAAE,MAAM,GAAG,KAAK,UAAU,GAAG,CAAC;AAAA,CAAI,CAAC;AACzD,MAAE,GAAG,QAAQ,CAAC,MAAM;AAClB,aAAO,EAAE,SAAS,MAAM;AACxB,YAAM,IAAI,IAAI,QAAQ,IAAI;AAC1B,UAAI,IAAI,EAAG;AACX,UAAI;AACF,eAAO,KAAK,MAAM,IAAI,MAAM,GAAG,CAAC,CAAC,CAAe;AAAA,MAClD,QAAQ;AACN,eAAO,IAAI;AAAA,MACb;AAAA,IACF,CAAC;AACD,MAAE,GAAG,SAAS,MAAM,OAAO,IAAI,CAAC;AAChC,MAAE,GAAG,SAAS,MAAM,OAAO,IAAI,CAAC;AAAA,EAClC,CAAC;AACH;;;AC3HA,SAAS,aAAAC,YAAW,gBAAAC,gBAAc,cAAAC,aAAY,iBAAAC,sBAAqB;AACnE,OAAOC,YAAU;AAIV,IAAM,cAAoC,CAAC,OAAO,QAAQ,MAAM;AAGhE,IAAM,eAAe;AAG5B,IAAM,UAAU,KAAK,KAAK;AAEnB,SAAS,YAAY,GAA4B;AACtD,SAAO,OAAO,MAAM,YAAa,YAAkC,SAAS,CAAC;AAC/E;AAKO,SAAS,UAAU,GAAoE;AAC5F,MAAI,YAAY,EAAE,KAAK,EAAG,QAAO,EAAE;AACnC,MAAI,YAAY,EAAE,cAAc,EAAG,QAAO,EAAE;AAC5C,SAAO;AACT;AAEA,IAAM,UAAU,CAAC,MAAsB,OAAO,EAAE,KAAK,aAAa,WAAW,EAAE,KAAK,WAAW;AAE/F,IAAM,QAAQ,CAAC,MAAiB,UAA2B,SAAS,UAAW,SAAS,UAAU,UAAU;AAG5G,IAAM,SAAS,CAAC,KAAa,UAA0B,GAAG,GAAG,UAAkB,KAAK;AAQ7E,SAAS,SAAS,GAAU,MAAiB,OAA+B,KAAsB;AACvG,MAAI,SAAS,MAAO,QAAO;AAC3B,QAAM,QAAQ,QAAQ,CAAC;AACvB,QAAM,IAAI,KAAK,MAAM,EAAE,IAAI;AAC3B,MAAI,EAAE,SAAS,oBAAoB;AACjC,QAAI,MAAM,MAAM,KAAK,EAAG,OAAM,OAAO,KAAK,KAAK,CAAC,IAAI,OAAO,SAAS,CAAC,IAAI,IAAI;AAC7E,WAAO;AAAA,EACT;AACA,MAAK,EAAE,SAAS,kBAAkB,EAAE,SAAS,gBAAiB,CAAC,MAAM,MAAM,KAAK,EAAG,QAAO;AAC1F,MAAI,EAAE,SAAS,eAAgB,QAAO;AACtC,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,QAAMC,OAAM,OAAO,KAAK,KAAK;AAE7B,QAAM,QAAQ,MAAMA,IAAG;AACvB,MAAI,UAAU,UAAa,KAAK,OAAO;AACrC,WAAO,MAAMA,IAAG;AAChB,UAAM,GAAG,IAAI;AACb,WAAO;AAAA,EACT;AACA,QAAM,OAAO,MAAM,GAAG;AACtB,MAAI,SAAS,UAAa,IAAI,OAAO,gBAAgB,IAAI,OAAO,aAAc,QAAO;AACrF,QAAM,GAAG,IAAI;AACb,SAAO;AACT;AAKO,IAAM,YAAN,MAAgB;AAAA,EACrB,YACW,MACA,MACT;AAFS;AACA;AAAA,EACR;AAAA,EAFQ;AAAA,EACA;AAAA,EAEH,OAAe;AACrB,WAAOC,OAAK,KAAK,KAAK,MAAM,SAAS,GAAG,OAAO,KAAK,IAAI,CAAC,OAAO;AAAA,EAClE;AAAA,EACA,OAA+B;AAC7B,QAAI;AACF,YAAM,IAAI,KAAK,MAAMC,eAAa,KAAK,KAAK,GAAG,MAAM,CAAC;AACtD,aAAO,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,IAAK,IAA+B,CAAC;AAAA,IAC5F,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EACA,MAAM,GAAiC;AACrC,UAAM,SAAS,KAAK,IAAI,GAAG,GAAG,OAAO,OAAO,CAAC,EAAE,OAAO,OAAO,QAAQ,CAAC;AACtE,UAAM,OAA+B,CAAC;AACtC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,CAAC,EAAG,KAAI,OAAO,SAAS,CAAC,KAAK,KAAK,SAAS,QAAS,MAAK,CAAC,IAAI;AACnG,UAAM,IAAI,KAAK,KAAK;AACpB,IAAAC,WAAUF,OAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3D,UAAM,MAAM,GAAG,CAAC,IAAI,QAAQ,GAAG;AAC/B,IAAAG,eAAc,KAAK,KAAK,UAAU,IAAI,GAAG,EAAE,MAAM,IAAM,CAAC;AACxD,IAAAC,YAAW,KAAK,CAAC;AAAA,EACnB;AACF;;;AjCxDA,eAAe,WACb,OACA,KACA,SACA,KACA,YACe;AACf,QAAM,OAAO,WAAW,KAAK;AAC7B,MAAI,QAAQ,aAAa,WAAW,CAACC,aAAW,IAAI,EAAG;AAKvD,MAAI,OAAsB;AAC1B,MAAI;AACF,WAAO,4BAAc,SAAS,KAAK,MAAM,IAAI,KAAK,CAAC;AAAA,EACrD,QAAQ;AAAA,EAAC;AACT,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,SAAS,GAAG,IAAI,OAAO,IAAI,OAAO;AAAA,MAClC,OAAO,IAAI;AAAA,MACX,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,MACrB,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,MACnC,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AACF;AAsBO,SAAS,WAAW,OAAe,KAAkB;AAC1D,QAAM,OAAO,QAAQ,KAAK;AAC1B,EAAAC,WAAU,MAAM,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAChD,QAAM,IAAIC,OAAK,KAAK,MAAM,GAAG,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO;AACrG,EAAAC,eAAc,GAAG,KAAK,UAAU,GAAG,GAAG,EAAE,MAAM,IAAM,CAAC;AACrD,SAAO;AACT;AAoBO,SAAS,WAAW,SAAmC;AAC5D,MAAI,CAAC,QAAS;AACd,MAAI;AACF,IAAAC,YAAW,OAAO;AAAA,EACpB,QAAQ;AAAA,EAAC;AACX;AA6DO,SAAS,IAAI,OAAe,MAAoB;AACrD,MAAI;AACF,IAAAC,WAAU,OAAO,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACjD,UAAM,IAAIC,OAAK,KAAK,OAAO,KAAK;AAChC,QAAI;AACF,UAAIC,UAAS,CAAC,EAAE,OAAO,OAAO,KAAM,CAAAC,YAAW,GAAG,GAAG,CAAC,IAAI;AAAA,IAC5D,QAAQ;AAAA,IAAC;AACT,IAAAC,gBAAe,GAAG,IAAG,oBAAI,KAAK,GAAE,YAAY,CAAC,IAAI,IAAI;AAAA,CAAI;AAAA,EAC3D,QAAQ;AAAA,EAAC;AACX;AAGO,SAAS,WAAW,OAA2B;AACpD,QAAM,OAAO,CAAC,GAAW,MAAcH,OAAK,KAAK,OAAO,SAAS,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,CAAC,OAAO;AAC/F,SAAO;AAAA,IACL,IAAI,GAAG,GAAG;AACR,UAAI;AACF,eAAO,KAAK,MAAMI,eAAa,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC;AAAA,MACpD,QAAQ;AACN,eAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,IACA,KAAK,GAAG,GAAG,OAAO;AAChB,YAAM,MAAM,KAAK,IAAI,GAAG,CAAC;AACzB,YAAM,OAAO,EAAE,GAAG,KAAK,GAAG,MAAM;AAChC,UAAI,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,GAAG,EAAG;AAClD,MAAAL,WAAUC,OAAK,QAAQ,KAAK,GAAG,CAAC,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACpE,MAAAK,eAAc,KAAK,GAAG,CAAC,GAAG,KAAK,UAAU,IAAI,GAAG,EAAE,MAAM,IAAM,CAAC;AAAA,IACjE;AAAA,EACF;AACF;AAEO,SAAS,WAAW,OAAqB,IAAyC,CAAC,GAAW;AACnG,QAAM,MAAc,CAAC;AACrB,aAAW,OAAO,OAAO;AACvB,QAAI,IAAI,OAAQ;AAGhB,UAAM,IAAI,gBAAgB,GAAG;AAC7B,QAAI,CAAC,GAAG;AACN,QAAE,SAAS,IAAI,IAAI;AACnB;AAAA,IACF;AAIA,UAAM,OAAO,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,IAAI,KAAK,GAAG,EAAE,YAAY,CAAC,CAAC;AACtE,QAAI,EAAE,QAAQ,SAAU,KAAI,KAAK,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC;AAAA,aAC7D,EAAE,IAAI,WAAW,OAAO;AAC/B,UAAI,KAAK,IAAI,SAAS,EAAE,MAAM,EAAE,IAAI,MAAM,CAAC,EAAE,QAAQ,cAAcC,IAAG,QAAQ,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC;AAAA,aACzF,eAAe,KAAK,EAAE,GAAG,EAAG,KAAI,KAAK,IAAI,SAAS,GAAG,IAAI,CAAC;AAAA,EACrE;AACA,SAAO;AACT;AAGO,IAAM,cAAc;AAOpB,SAAS,SACd,MACA,GACuF;AACvF,QAAM,OAAO,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC;AAC7C,QAAM,QAAQ,CAAC,MAAc,IAAY,cAAqB;AAAA,IAC5D,MAAM,EAAE,OAAO,KAAK,MAAM;AAAA,IAC1B,MAAM,KAAK,MAAM,GAAG,aAAa;AAAA,IACjC,WAAW;AAAA,IACX;AAAA,IACA,GAAI,WAAW,EAAE,UAAU,KAAc,IAAI,CAAC;AAAA,EAChD;AAGA,MAAI,KAAK,KAAM,QAAO,MAAM,KAAK,KAAK,MAAM,KAAK,KAAK,IAAI,KAAK,KAAK,QAAQ;AAK5E,QAAM,SAAS,EAAE,QAAQ,KAAK;AAC9B,MAAI,UAAU,CAAC,WAAW,MAAM,KAAK,EAAE,QAAQ,KAAK,KAAK,SAAS,OAAO,MAAM,GAAG,GAAG,CAAC,GAAI,QAAO,MAAM,QAAQ,EAAE,EAAE;AACnH,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO;AAAA,IACL,MAAM,EAAE,OAAO,KAAK,MAAM,SAAS;AAAA,IACnC,MAAM,KAAK,KAAK,MAAM,GAAG,aAAa;AAAA,IACtC,WAAW,QAAQ,KAAK,WAAW,aAAa;AAAA,IAChD,GAAI,KAAK,WAAW,EAAE,UAAU,KAAc,IAAI,CAAC;AAAA,IACnD,IAAI,KAAK;AAAA,EACX;AACF;AACA,IAAM,gBAAgB;AAGtB,IAAM,cAAc;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKA,eAAsB,OACpB,KACA,OASgB,CAAC,GACmE;AACpF,QAAM,QAAQ,KAAK,SAAS,SAAS;AACrC,QAAM,MAAM,WAAW;AACvB,QAAM,UAAU,cAAc,IAAI,OAAO;AAIzC,MAAI,CAAC,SAAS;AACZ,QAAI,OAAO,GAAG,IAAI,OAAO,IAAI,IAAI,KAAK,6BAA6B,IAAI,OAAO,kBAAkB;AAChG,WAAO,EAAE,QAAQ,CAAC,GAAG,WAAW,CAAC,EAAE;AAAA,EACrC;AAIA,QAAM,MAAyB,EAAE,GAAG,QAAQ,IAAI;AAChD,aAAW,KAAK,YAAa,QAAO,IAAI,CAAC;AACzC,QAAM,IAAI,SAAS,SAAS,EAAE,MAAM,IAAI,MAAM,OAAO,IAAI,OAAO,KAAK,EAAE,GAAG,KAAK,GAAG,IAAI,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC;AAK3G,MAAI,CAAC,KAAK,CAAC,EAAE,QAAQ,IAAI;AACvB,QAAI,OAAiB,CAAC;AACtB,QAAI;AAGF,YAAM,UAAmB,KAAK,MAAM,IAAI,KAAK,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,CAAC,KAAK,IAAI,KAAK;AACxF,UAAI,WAAW,OAAO,YAAY,YAAY,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO,OAAO,KAAK,OAAO;AAAA,IACnG,QAAQ;AAAA,IAAC;AACT;AAAA,MACE;AAAA,MACA,GAAG,IAAI,OAAO,IAAI,IAAI,KAAK,SAAS,IAAI,OAAO,6DAA6D,KAAK,KAAK,IAAI,KAAK,QAAQ;AAAA,IACzI;AACA,WAAO,EAAE,QAAQ,CAAC,GAAG,WAAW,CAAC,EAAE;AAAA,EACrC;AAKA,QAAM,KACJ,aAAa,YAAY,UAAUN,OAAK,KAAK,OAAO,SAAS,CAAC,CAAC,GAAG,IAAI,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ;AAC5G,QAAM,YAAY,IAAI,YAAY,iBAAiB,EAAE,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,eAAe;AAIlG,MAAI,CAAC,UAAW,OAAM,WAAW,OAAO,KAAK,IAAI,EAAE,QAAQ,KAAK,EAAE,UAAU;AAC5E,QAAM,SAAS,IAAI,OAAO,KAAK;AAC/B,QAAM,MAAM,EAAE,SAAS,IAAI,SAAS,SAAS,GAAG;AAIhD,MAAI,UAAU,SAAS,OAAO,GAAG;AACjC,QAAM,WAAW,KAAK,cAAc;AACpC,aAAW,QAAQ,KAAK,IAAI,IAAI,UAAU,CAAC,WAAW,KAAK,IAAI,IAAI,SAAS;AAC1E,UAAM,IAAI,QAAQ,CAAC,QAAQ,WAAW,KAAK,EAAE,CAAC;AAC9C,cAAU,SAAS,OAAO,GAAG;AAAA,EAC/B;AACA,MAAI,CAAC,SAAS;AACZ,QAAI,OAAO,GAAG,IAAI,OAAO,IAAI,IAAI,KAAK,aAAa,EAAE,eAAe,WAAW,GAAI,eAAe;AAClG,UAAM,WAAW,WAAW,OAAO,EAAE,GAAG,KAAK,QAAQ,IAAI,SAAS,KAAK,EAAE,CAAC;AAC1E,eAAW,KAAK,OAAO;AACvB,WAAO,EAAE,QAAQ,CAAC,GAAG,WAAW,CAAC,GAAG,SAAS;AAAA,EAC/C;AACA,QAAM,SAAkB,CAAC;AACzB,MAAI;AACF,UAAM,QAAQ,WAAW,KAAK;AAC9B,UAAM,MAAM,MAAM,IAAI,IAAI,SAAS,EAAE;AAIrC,QACE,aACA,CAACO,aAAW,OAAO,KAAK,GAAG,CAAC,KAC5B,OAAO,KAAK,GAAG,EAAE,WAAW,KAC5B,EAAE,EAAE,cAAcA,aAAW,EAAE,UAAU,IACzC;AACA,iBAAW,KAAK,OAAO;AACvB,aAAO,EAAE,QAAQ,CAAC,GAAG,WAAW,CAAC,EAAE;AAAA,IACrC;AACA,QAAI,UAAW,OAAM,WAAW,OAAO,KAAK,IAAI,EAAE,QAAQ,KAAK,EAAE,UAAU;AAE3E,UAAM,QAAQ,CAAC,oFAAoF,KAAK,IAAI,KAAK;AACjH,QAAI,UAA4B,CAAC;AACjC,QAAI,OAAO;AAGT,YAAM,UAAU,EAAE,GAAG,QAAQ,KAAK,GAAG,IAAI,IAAI;AAC7C,YAAM,IAAI,QAAQ,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC,GAAG,OAAO,OAAO;AAG9E,UAAI,IAAI,iBAAiB,QAAQ;AAC/B,eAAO,OAAO,GAAG,MAAM,QAAQ,YAAY,EAAE,SAAS,GAAG,EAAE,YAAY,OAAO,OAAO,CAAC;AACxF,YAAM,EAAE,YAAY,IAAI,SAAS,IAAI,WAAW,KAAK,iBAAiB,GAAG,KAAK,IAAI;AAClF,gBAAU;AACV,UAAI,gBAAiB,OAAM,KAAK,IAAI,SAAS,IAAI,EAAE,gBAAgB,CAAC;AACpE,YAAM,KAAK,IAAI,SAAS,IAAI,EAAE,SAAS,EAAE,GAAI,IAAI,SAAoB,GAAG,QAAQ,EAAE,CAAC;AAKnF,UAAI,eAAe,IAAI,IAAI,OAAO,KAAK,EAAE,QAAQ,OAAOP,OAAK,WAAW,EAAE,QAAQ,GAAG;AACnF,cAAM,KAAK,IAAI,SAAS,IAAI,EAAE,KAAK,EAAE,QAAQ,IAAI,CAAC;AAAA,IACtD;AACA,UAAM,QAAS,IAAI,WAA4C,CAAC;AAChE,UAAM,SAAS,QAAQ,OAAO,EAAE,QAAQ,OAAO,MAAM;AACrD,UAAM,MAAM,QAAQ,SAAS,MAAM,IAAI,CAAC;AACxC,UAAM,UAAmB;AAAA,MACvB,GAAG;AAAA,MACH,GAAG;AAAA,MACH,GAAG;AAAA,MACH,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,MACnD;AAAA,MACA,KAAK;AAAA,MACL,SAAS,YAAY,KAAKM,IAAG,SAAS,CAAC;AAAA,MACvC,GAAI,SAAS,EAAE,KAAK,MAAM,MAAM,EAAE,IAAI,CAAC;AAAA,MACvC,GAAI,EAAE,QAAQ,QAAQ,EAAE,OAAO,EAAE,QAAQ,MAAM,IAAI,CAAC;AAAA,MACpD,GAAI,EAAE,QAAQ,cAAc,EAAE,aAAa,EAAE,QAAQ,YAAY,IAAI,CAAC;AAAA,MACtE,GAAI,EAAE,QAAQ,eAAe,EAAE,cAAc,EAAE,QAAQ,aAAa,IAAI,CAAC;AAAA,IAC3E;AACA,UAAM,UAAW,MAAM,IAAI,IAAI,SAAS,EAAE,EAAE,mBAA0C;AACtF,eAAW,KAAK,EAAE,QAAQ;AACxB,YAAM,IAAI,UAAU,GAAG;AAAA,QACrB,SAAS,EAAE,MAAM,IAAI,SAAS,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AAAA,QAC9D,SAAS,EAAE,GAAG,SAAS,KAAK,OAAO,QAAQ,GAAG,EAAE;AAAA,QAChD,KAAK,KAAK,OAAO,IAAI;AAAA,MACvB,CAAC;AACD,aAAO,KAAK,CAAC;AAAA,IACf;AACA,UAAM,kBAAkB,IAAI,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,UAAU,KAAK,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC;AAKjG,UAAM,SAAS,EAAE,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,SAAS,eAAe;AACzF,UAAM,WAAW,EAAE,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,cAAc;AAI/D,UAAM,YAAY,EAAE,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,cAAc,GAAG,KAAK;AACxE,UAAM,WAAW,YAAY,OAAO,cAAc,YAAY,MAAM,IAAI,IAAI,SAAS,EAAE,EAAE,cAAc;AACvG,QAAI,mBAAmB,EAAE,cAAc,CAAC,WAAW,YAAY,EAAE,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,YAAY,IAAI;AAC7G,YAAM,MAAM,MAAM,IAAI,IAAI,SAAS,EAAE;AACrC,YAAM,KAAK,KAAK,OAAO,IAAI;AAC3B,UAAI,YAAY,MAAM,OAAO,IAAI,WAAW,KAAK,MAAM,aAAa;AAClE,cAAM,IAAI,SAAS,QAAQ,eAAe,EAAE,YAAY,OAAO,IAAI,eAAe,KAAK,CAAC,GAAG;AAAA,UACzF,MAAM,OAAO,IAAI,gBAAgB,KAAK;AAAA,UACtC,QAAQ,CAAC,YAAY,OAAO,EAAE,OAAO,WAAW,WAAW,EAAE,MAAM,SAAS;AAAA,UAC5E;AAAA,QACF,CAAC;AACD,cAAM,MAAM,KAAK,GAAG,EAAE,IAAI,IAAI,EAAE,KAAK,MAAM,IAAI,EAAE,UAAU,MAAM;AACjE,YAAI,MAAM,YAAY,QAAQ,IAAI,eAAe;AAC/C,iBAAO;AAAA,YACL;AAAA,cACE,EAAE,MAAM,iBAAiB,cAAc,IAAI,OAAO,MAAM,EAAE,GAAG,GAAG,IAAI,IAAI,KAAK,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE;AAAA,cACnG;AAAA,gBACE,SAAS,EAAE,MAAM,IAAI,SAAS,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AAAA,gBAC9D,SAAS,EAAE,GAAG,SAAS,KAAK,OAAO,QAAQ,GAAG,EAAE;AAAA,gBAChD,KAAK;AAAA,cACP;AAAA,YACF;AAAA,UACF;AACA,gBAAM,KAAK,IAAI,SAAS,IAAI,EAAE,aAAa,IAAI,cAAc,IAAI,CAAC;AAAA,QACpE;AAAA,MACF;AAAA,IACF;AAEA,QAAI,YAAY,OAAO,cAAc,YAAY,CAAC,SAAU,OAAM,KAAK,IAAI,SAAS,IAAI,EAAE,WAAW,UAAU,CAAC;AAChH,QAAI,OAAQ,OAAM,KAAK,IAAI,SAAS,IAAI,EAAE,WAAW,KAAK,CAAC;AAE3D,QACE,mBACA,EAAE,cACF,EAAE,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,SAAS,eAAe,GAC1E;AACA,YAAM,MAAM,MAAM,IAAI,IAAI,SAAS,EAAE;AACrC,YAAM,WAAW,OAAO,IAAI,eAAe,KAAK;AAChD,UAAI,SAAS,OAAO,IAAI,gBAAgB,KAAK;AAC7C,YAAM,OAAO,QAAQ,eAAe,EAAE,YAAY,QAAQ;AAC1D,iBAAW,KAAK,KAAK,OAAO;AAC1B,eAAO;AAAA,UACL;AAAA,YACE;AAAA,cACE,MAAM;AAAA,cACN,cAAc,IAAI;AAAA,cAClB,MAAM;AAAA,gBACJ,MAAM;AAAA,gBACN,MAAM,EAAE;AAAA,gBACR,WAAW,EAAE;AAAA,gBACb,IAAI,IAAI,KAAK,EAAE,EAAE,EAAE,YAAY;AAAA,gBAC/B,GAAI,EAAE,WAAW,EAAE,UAAU,KAAc,IAAI,CAAC;AAAA,cAClD;AAAA,YACF;AAAA,YACA;AAAA,cACE,SAAS,EAAE,MAAM,IAAI,SAAS,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AAAA,cAC9D,SAAS,EAAE,GAAG,SAAS,KAAK,OAAO,QAAQ,GAAG,EAAE;AAAA,cAChD,KAAK,KAAK,OAAO,IAAI;AAAA,YACvB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,UAAI,KAAK,MAAM,OAAQ,OAAM,KAAK,IAAI,SAAS,IAAI,EAAE,iBAAiB,KAAK,MAAM,kBAAkB,OAAO,CAAC;AAAA,IAC7G;AAGA,QACE,QAAQ,UACR,EAAE,cACF,UACA,IAAI,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,UAAU,KAAK,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,GACzE;AACA,YAAM,SAAS,QAAQ,OAAO,EAAE,UAAU;AAC1C,YAAM,MAAM,UAAU,KAAK,UAAU,MAAM;AAC3C,UAAI,UAAU,QAAQ,MAAM,IAAI,IAAI,SAAS,EAAE,EAAE,YAAY;AAC3D,eAAO;AAAA,UACL;AAAA,YACE,EAAE,MAAM,mBAAmB,cAAc,IAAI,OAAO,MAAM,OAAO;AAAA,YACjE;AAAA,cACE,SAAS,EAAE,MAAM,IAAI,SAAS,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AAAA,cAC9D,SAAS,EAAE,GAAG,SAAS,KAAK,OAAO,QAAQ,GAAG,EAAE;AAAA,cAChD,KAAK,KAAK,OAAO,IAAI;AAAA,YACvB;AAAA,UACF;AAAA,QACF;AACA,cAAM,KAAK,IAAI,SAAS,IAAI,EAAE,YAAY,IAAI,CAAC;AAAA,MACjD;AAAA,IACF;AAIA,WAAO;AAAA,MACL;AAAA,MACA,OAAO,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,WAAW,EAAE,OAAO,GAAG,MAAM,WAAW,EAAE,IAAI,EAAE,EAAE;AAAA,IACxF;AACA,eAAW,KAAK,OAAO;AACvB;AAAA,MACE;AAAA,MACA,GAAG,IAAI,OAAO,IAAI,IAAI,KAAK,KAAK,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,GAAG,KAAK,SAAS,KAAK,QAAQ,IAAI,SAAS,EAAE,CAAC;AAAA,IAC/G;AAAA,EACF,UAAE;AACA,YAAQ;AAAA,EACV;AAMA,MAAI,KAAK,OAAO,IAAI,KAAM,QAAO,MAAM,IAAI,aAAa,EAAE;AAC1D,QAAM,YACJ,KAAK,UAAU,QACX,CAAC,IACC,MAAM,MAAM,OAAO,IAAI,OAAO,QAAQ,KAAK;AAAA,IAC3C,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,IAC1C,GAAI,KAAK,aAAa,SAAY,EAAE,UAAU,KAAK,SAAS,IAAI,CAAC;AAAA,IACjE,GAAI,KAAK,UAAU,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,EAClD,CAAC,KAAM,CAAC;AACd,SAAO,EAAE,QAAQ,UAAU;AAC7B;AA2DA,IAAM,aAAoB,EAAE,KAAK,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,OAAO,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC,EAAE;AAKjG,IAAM,UAAU;AAGvB,IAAM,WAAW,CAAC,UAA0BE,OAAK,KAAK,OAAO,SAAS,YAAY;AAClF,SAAS,SAAS,OAAuB;AACvC,MAAI;AAEF,WAAO,OAAOC,eAAa,SAAS,KAAK,GAAG,MAAM,CAAC;AAAA,EACrD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AACA,SAAS,SAAS,OAAe,IAAkB;AACjD,MAAI;AACF,IAAAC,WAAUF,OAAK,QAAQ,SAAS,KAAK,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACzE,IAAAG,eAAc,SAAS,KAAK,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,IAAM,CAAC;AAAA,EAC5D,QAAQ;AAAA,EAAC;AACX;AAKA,SAAS,mBAAmB,OAAe,aAA2B,QAAgB,KAAsB;AAC1G,QAAM,YAAY,YAAY;AAAA,IAC5B,CAAC,MAAM,CAAC,EAAE,UAAU,eAAe,KAAK,EAAE,GAAG,KAAK,GAAG,YAAY,OAAO,EAAE,IAAI,GAAG,SAAS,KAAK;AAAA,EACjG;AACA,MAAI,CAAC,UAAU,OAAQ,QAAO;AAC9B,QAAM,OAAO,OAAO,SAAS;AAC7B,aAAW,KAAK,WAAW;AACzB,UAAM,UAAU,IAAI,QAAQ,OAAO,EAAE,IAAI;AACzC,UAAM,IAAI,QAAQ,KAAK;AACvB,eAAW,OAAO,MAAM;AACtB,UAAI;AACF,YAAIC,UAAS,OAAO,KAAK,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,IAAI,GAAG,CAAC,KAAK,GAAI,QAAO;AAAA,MAC3E,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF;AACA,SAAO;AACT;AAMO,IAAM,iBAAiB;AAa9B,IAAM,WAAW,CAAC,UAA0BC,OAAK,KAAK,OAAO,SAAS,YAAY;AAElF,SAAS,SAAS,OAAqB;AACrC,MAAI;AACF,IAAAC,WAAUD,OAAK,QAAQ,SAAS,KAAK,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACzE,IAAAE,eAAc,SAAS,KAAK,GAAG,IAAI,EAAE,MAAM,IAAM,CAAC;AAAA,EACpD,QAAQ;AAAA,EAAC;AACX;AAcA,eAAsB,MACpB,OACA,aACA,QACA,OACA,IAAmC,CAAC,GACI;AACxC,QAAM,QAAQ,EAAE,SAAS;AACzB,QAAM,OAAO,SAAS,KAAK;AAC3B,MAAI,UAAU,cAAc,OAAO,OAAO;AAC1C,MAAI,CAAC,SAAS;AACZ,aAAS,KAAK;AACd,cAAU,cAAc,OAAO,OAAO;AACtC,QAAI,CAAC,QAAS,QAAO;AAAA,EACvB;AACA,QAAM,YAAoC,CAAC;AAC3C,aAAS;AACP,QAAI,EAAE,aAAa,UAAa,MAAM,IAAI,IAAI,iBAAiB,EAAE,UAAU;AACzE,eAAS,KAAK;AACd,cAAQ;AACR,QAAE,UAAU;AACZ,aAAO;AAAA,IACT;AACA,QAAI;AAIF,YAAM,OAAO,KAAK,IAAI,SAAS,SAAS,KAAK,IAAI,UAAU,MAAM,IAAI,CAAC;AACtE,UAAI,OAAO,KAAK,mBAAmB,OAAO,aAAa,QAAQ,MAAM,IAAI,CAAC,EAAG,OAAM,MAAM,MAAM,IAAI;AACnG,UAAI;AACF,QAAAC,YAAW,IAAI;AAAA,MACjB,QAAQ;AAAA,MAAC;AACT,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,MAAM,OAAO,aAAa,QAAQ,OAAO,EAAE,MAAM,CAAC,CAAC;AAC3F,kBAAU,CAAC,KAAK,UAAU,CAAC,KAAK,KAAK;AAAA,IACzC,UAAE;AACA,cAAQ;AAAA,IACV;AACA,QAAI,CAACC,aAAW,IAAI,EAAG,QAAO;AAC9B,cAAU,cAAc,OAAO,OAAO;AACtC,QAAI,CAAC,QAAS,QAAO;AAAA,EACvB;AACF;AAYA,IAAM,aAAa,CAAC,KAAO,KAAQ,KAAQ,IAAI,KAAQ,KAAK,GAAM;AAClE,IAAM,cAAc,CAAC,OAAe,SAAyBJ,OAAK,KAAK,OAAO,WAAW,GAAG,OAAO,IAAI,CAAC,OAAO;AACxG,SAAS,YAAY,OAAe,MAA8B;AACvE,MAAI;AACF,UAAM,IAAI,KAAK,MAAMK,eAAa,YAAY,OAAO,IAAI,GAAG,MAAM,CAAC;AACnE,WAAO,OAAO,EAAE,UAAU,WAAW,IAAI;AAAA,EAC3C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AACA,SAAS,YAAY,OAAe,MAAc,KAAqB,MAA+B;AACpG,QAAM,YAAY,MAAM,YAAY,KAAK;AACzC,QAAM,SAAS,WAAW,KAAK,IAAI,UAAU,WAAW,MAAM,IAAI,CAAC;AACnE,QAAM,OAAO,OAAO,SAAS,IAAI,YAAY,IAAK,IAAI,eAA0B;AAChF,QAAM,IAAI,EAAE,OAAO,KAAK,IAAI,IAAI,MAAM,UAAU,KAAK,GAAG,IAAI,UAAU,EAAE,IAAI,IAAI,SAAS,EAAE,GAAG,KAAK,EAAE;AACrG,QAAM,IAAI,YAAY,OAAO,IAAI;AACjC,MAAI;AACF,IAAAJ,WAAUD,OAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC3D,IAAAE,eAAc,GAAG,KAAK,UAAU,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AAAA,EACrD,QAAQ;AAAA,EAAC;AACT,SAAO;AACT;AACA,SAAS,aAAa,OAAe,MAAoB;AACvD,MAAI;AACF,IAAAC,YAAW,YAAY,OAAO,IAAI,CAAC;AAAA,EACrC,QAAQ;AAAA,EAAC;AACX;AAGA,IAAM,eAAe;AAIrB,IAAM,iBAAiB,oBAAI,IAAI,CAAC,eAAe,OAAO,CAAC;AAEvD,IAAM,QAAQ;AACd,IAAM,cAAc;AAUpB,eAAsB,MACpB,OACA,aACA,QACA,OACA,IAKI,CAAC,GAC4B;AACjC,QAAM,YAAoC,CAAC;AAC3C,QAAM,QAAQ,EAAE,SAAS;AACzB,QAAM,QAAQ,WAAW,aAAa;AAAA,IACpC,QAAQ,CAAC,SAAS,IAAI,OAAO,QAAQ,IAAI,sEAAsE;AAAA,EACjH,CAAC;AACD,QAAM,QAAQ,IAAI,IAAI,YAAY,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,UAAU,CAAC,CAAC,CAAC,CAAC;AACpE,QAAM,OAAO,OAAO,SAAS;AAC7B,MAAI,OAAO;AACT,UAAM,IAAI,GAAG,MAAM,OAAO,IAAI,OAAO,MAAM,OAAO,CAAC;AACnD,SAAK,KAAK,CAAC,GAAG,MAAO,GAAG,EAAE,OAAO,IAAI,EAAE,OAAO,OAAO,IAAI,KAAK,GAAG,EAAE,OAAO,IAAI,EAAE,OAAO,OAAO,IAAI,IAAI,CAAE;AAAA,EAC1G;AAWA,QAAM,aAAa,oBAAI,IAAY;AACnC,QAAM,aAAa,CAAC,KAAgB,QAAgB,SAA6D;AAC/G,QAAI;AACF,aAAO,OAAO,KAAK,KAAK,QAAQ,IAAI;AAAA,IACtC,SAAS,GAAG;AACV,YAAM,OAAQ,EAAyB;AACvC,UAAI,OAAO,SAAS,SAAU,OAAM;AACpC,YAAM,IAAI,OAAO,KAAK,GAAG;AACzB,UAAI,CAAC,WAAW,IAAI,CAAC,GAAG;AACtB,mBAAW,IAAI,CAAC;AAChB,YAAI,OAAO,qBAAqB,CAAC,KAAK,IAAI,8BAA8B;AAAA,MAC1E;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,QAAM,YAAY,OAAO,SAA8B;AACrD,QAAI,UAAU,YAAY,OAAO,KAAK,IAAI;AAC1C,QAAI,WAAW,CAAC,EAAE,SAAS,QAAQ,QAAQ,KAAK,IAAI,GAAG;AACrD,gBAAU,KAAK,IAAI,IAAI;AACvB;AAAA,IACF;AACA,UAAM,UAAU,IAAI,QAAQ,OAAO,KAAK,IAAI;AAC5C,UAAM,IAAI,QAAQ,KAAK;AAIvB,UAAM,OAAO,MAAM,IAAI,KAAK,IAAI;AAChC,UAAM,QAAQ,IAAI,UAAU,OAAO,KAAK,IAAI;AAC5C,QAAI,WAAW,SAAS,QAAQ,CAAC,IAAI,MAAM,KAAK;AAChD,QAAI,YAAY,EAAE,GAAG,SAAS;AAC9B,QAAI,OAAO;AACX,QAAI,UAAU;AAGd,QAAI,QAAiB,CAAC;AACtB,QAAI,UAAkC,CAAC;AAOvC,UAAM,UAAU,OAAO,WAA6C;AAClE,UAAI,gBAAgB,SAAU,UAAS,OAAO,MAAM,IAAI,CAAC;AACzD,YAAM,MAAM,MAAM,KAAK,KAAK,MAAM;AAClC,UAAI,IAAI,MAAM,IAAI,WAAW,QAAS,QAAO;AAC7C,UAAI,OAAO,WAAW,GAAG;AACvB,cAAM,IAAI,OAAO,CAAC;AAClB,YAAI,OAAO,QAAQ,KAAK,IAAI,SAAS,IAAI,SAAS,EAAE,iBAAY,EAAE,EAAE,KAAK,EAAE,IAAI,+BAA+B;AAC9G,eAAO,EAAE,IAAI,MAAM,cAAc,GAAG,UAAU,GAAG,YAAY,GAAG,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,QAAQ,YAAY,CAAC,EAAE;AAAA,MAChH;AACA,YAAM,OAAO,OAAO,SAA2C;AAC7D,cAAM,IAAI,MAAM,QAAQ,IAAI;AAC5B,YAAI,EAAE,MAAM,EAAE,WAAW,OAAQ,QAAO;AACxC;AAAA,UACE;AAAA,UACA,QAAQ,KAAK,IAAI,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,iBAAY,KAAK,MAAM,OAAO,OAAO,MAAM;AAAA,QAClG;AACA,cAAM,WAAW,KAAK,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,QAAQ,UAAU,EAAE;AAClE,eAAO,EAAE,IAAI,MAAM,cAAc,GAAG,UAAU,GAAG,YAAY,GAAG,SAAS;AAAA,MAC3E;AACA,YAAM,OAAO,KAAK,KAAK,OAAO,SAAS,CAAC;AACxC,YAAM,IAAI,MAAM,KAAK,OAAO,MAAM,GAAG,IAAI,CAAC;AAC1C,UAAI,CAAC,EAAE,GAAI,QAAO;AAClB,YAAM,IAAI,MAAM,KAAK,OAAO,MAAM,IAAI,CAAC;AACvC,UAAI,CAAC,EAAE,GAAI,QAAO;AAClB,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,cAAc,EAAE,eAAe,EAAE;AAAA,QACjC,WAAW,EAAE,YAAY,MAAM,EAAE,YAAY;AAAA,QAC7C,aAAa,EAAE,cAAc,MAAM,EAAE,cAAc;AAAA,QACnD,UAAU,CAAC,GAAI,EAAE,YAAY,CAAC,GAAI,GAAI,EAAE,YAAY,CAAC,CAAE;AAAA,MACzD;AAAA,IACF;AACA,UAAM,cAAc,MAAY;AAC9B,UAAI,SAAS,SAAS,KAAK,UAAU,SAAS,MAAM,KAAK,UAAU,QAAQ,EAAG;AAC9E,YAAM,MAAM,SAAS;AACrB,iBAAW,EAAE,GAAG,UAAU;AAAA,IAC5B;AACA,UAAM,OAAO,YAA8B;AACzC,UAAI,CAAC,MAAM,QAAQ;AACjB,eAAO,OAAO,GAAG,OAAO;AACxB,kBAAU,CAAC;AACX,gBAAQ,MAAM,CAAC;AACf,oBAAY;AACZ,eAAO;AAAA,MACT;AACA,YAAM,MAAM,MAAM,QAAQ,KAAK;AAC/B,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,SAAS,IAAI,UAAU;AAC7B,YAAI,OAAO;AACX,YAAI,WAAW,SAAS;AACtB,oBAAU,YAAY,OAAO,KAAK,MAAM,KAAK,OAAO;AACpD,iBAAO,WAAW,KAAK,OAAO,QAAQ,QAAQ,KAAK,IAAI,KAAK,GAAI,CAAC;AAAA,QACnE;AACA,YAAI,OAAO,QAAQ,KAAK,IAAI,KAAK,IAAI,UAAU,EAAE,IAAI,IAAI,SAAS,EAAE,WAAM,MAAM,GAAG,IAAI,EAAE;AACzF,YAAI,IAAI,WAAW,WAAW,IAAI,WAAW,aAAc,kBAAiB,KAAK,MAAM,GAAG;AAC1F,YAAI,IAAI,WAAW,QAAQ;AACzB,kBAAQ,CAAC;AACT,oBAAU,CAAC;AACX,sBAAY,EAAE,GAAG,SAAS;AAC1B,iBAAO;AAAA,QACT;AAAA,MACF,WAAW,SAAS;AAClB,qBAAa,OAAO,KAAK,IAAI;AAC7B,kBAAU;AAAA,MACZ;AACA,cAAQ,MAAM;AACd,aAAO,OAAO,GAAG,OAAO;AACxB,cAAQ,MAAM,CAAC;AACf,kBAAY;AACZ,cAAQ,CAAC;AACT,gBAAU,CAAC;AACX,aAAO;AAAA,IACT;AACA,eAAW,OAAO,MAAM;AACtB,UAAI,WAAW,QAAQ,YAAa;AACpC,UAAI,SAAS,QAAQ,QAAQ,IAAI,GAAG,CAAC,KAAK,EAAE,QAAQ,IAAI,GAAG,CAAC,KAAK;AACjE,iBAAS;AACP,cAAM,OAAO,QAAQ,MAAM;AAC3B,cAAM,OAAO,WAAW,KAAK,QAAQ,IAAI;AACzC,YAAI,CAAC,KAAM;AACX,cAAM,EAAE,QAAQ,QAAQ,KAAK,IAAI;AACjC,YAAI,CAAC,OAAO,OAAQ;AACpB,mBAAW,KAAK,QAAQ;AACtB,gBAAM,IAAI,YAAY,GAAG,KAAK,MAAM,KAAK,KAAK,EAAE,MAAMG,IAAG,QAAQ,EAAE,CAAC;AACpE,cAAI,CAAC,EAAG;AACR,cAAI,SAAS,GAAG,MAAM,WAAW,QAAQ,IAAI,GAAG,CAAC,EAAG,OAAM,KAAK,CAAC;AAAA,mBACvD,EAAE,QAAS,GAAE,QAAQ,KAAK,IAAI,KAAK,EAAE,QAAQ,KAAK,IAAI,KAAK,KAAK;AAAA,QAC3E;AACA,iBAAS;AACT,gBAAQ,QAAQ,IAAI,GAAG,CAAC,IAAI;AAC5B,YAAI,MAAM,UAAU,OAAO;AACzB,cAAI,CAAE,MAAM,KAAK,GAAI;AACnB,sBAAU;AACV;AAAA,UACF;AACA,cAAI,QAAQ,YAAa;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,YAAY,MAAM,UAAU,OAAO,KAAK,OAAO,EAAE;AAAS,UAAI,CAAE,MAAM,KAAK,EAAI,WAAU;AAAA;AAC9F,cAAU,KAAK,IAAI,IAAI;AAAA,EACzB;AACA,QAAM,QAAQ,IAAI,MAAM,IAAI,SAAS,CAAC;AACtC,SAAO;AACT;AAEA,SAAS,iBAAiB,MAAc,KAAmE;AACzG,MAAI;AACF,UAAM,MAAM,WAAW;AACvB,UAAM,IAAI,IAAI,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AAC/C,QAAI,CAAC,EAAG;AACR,QAAI,IAAI,WAAW,QAAS,GAAE,SAAS,IAAG,oBAAI,KAAK,GAAE,YAAY,CAAC,IAAI,IAAI,SAAS,cAAc;AACjG,QAAI,IAAI,WAAW,gBAAgB,OAAO,IAAI,aAAa,SAAU,GAAE,WAAW,IAAI;AACtF,gBAAY,GAAG;AAAA,EACjB,QAAQ;AAAA,EAAC;AACX;AAIO,IAAM,UAAU,CAAC,UAA0BC,OAAK,KAAK,OAAO,MAAM;",
|
|
6
|
+
"names": ["appendFileSync", "existsSync", "mkdirSync", "readdirSync", "readFileSync", "renameSync", "statSync", "unlinkSync", "writeFileSync", "os", "path", "execFileSync", "createHash", "existsSync", "readdirSync", "readFileSync", "statSync", "path", "path", "cap", "path", "settings", "entry", "path", "readdirSync", "existsSync", "createHash", "readFileSync", "rows", "pid", "execFileSync", "statSync", "path", "ask", "createHash", "execFileSync", "readFileSync", "readFileSync", "statSync", "path", "statSync", "path", "readFileSync", "existsSync", "readdirSync", "readFileSync", "statSync", "path", "NAME", "path", "validTool", "existsSync", "prune", "readFileSync", "rows", "readdirSync", "statSync", "claudeLogin", "execFileSync", "closeSync", "openSync", "readdirSync", "readFileSync", "readSync", "statSync", "os", "path", "os", "readdirSync", "path", "claudeLogin", "execFileSync", "statSync", "cap", "openSync", "readSync", "closeSync", "readFileSync", "existsSync", "readdirSync", "readFileSync", "path", "OURS", "NAME", "OPTS", "eventsFor", "path", "hooksFile", "configFile", "validTool", "endReason", "readFileSync", "OURS", "readdirSync", "existsSync", "id", "r", "prune", "rows", "epochIso", "createHash", "existsSync", "path", "execFileSync", "existsSync", "NAME", "OPTS", "eventsFor", "path", "configFile", "validTool", "endReason", "pickSource", "errText", "existsSync", "createHash", "prune", "rows", "execFileSync", "existsSync", "readFileSync", "path", "NAME", "OPTS", "path", "validTool", "eventsFor", "execFileSync", "existsSync", "prune", "readFileSync", "existsSync", "mkdirSync", "readFileSync", "writeFileSync", "path", "readFileSync", "mkdirSync", "path", "writeFileSync", "existsSync", "randomBytes", "closeSync", "existsSync", "mkdirSync", "openSync", "readdirSync", "readFileSync", "readSync", "statSync", "unlinkSync", "writeFileSync", "path", "path", "readFileSync", "mkdirSync", "writeFileSync", "existsSync", "readdirSync", "statSync", "openSync", "readSync", "closeSync", "unlinkSync", "randomBytes", "createHash", "createHash", "appendFileSync", "mkdirSync", "path", "chmodSync", "mkdirSync", "readFileSync", "renameSync", "writeFileSync", "path", "createHash", "randomBytes", "createHash", "path", "readFileSync", "os", "path", "mkdirSync", "readFileSync", "renameSync", "writeFileSync", "path", "ask", "path", "readFileSync", "mkdirSync", "writeFileSync", "renameSync", "existsSync", "mkdirSync", "path", "writeFileSync", "unlinkSync", "mkdirSync", "path", "statSync", "renameSync", "appendFileSync", "readFileSync", "writeFileSync", "os", "existsSync", "path", "readFileSync", "mkdirSync", "writeFileSync", "statSync", "path", "mkdirSync", "writeFileSync", "unlinkSync", "existsSync", "readFileSync", "os", "path"]
|
|
7
7
|
}
|