slopie 1.6.2 → 1.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +2 -0
- package/README.md +9 -5
- package/bin/audit-splash.js +71 -0
- package/bin/potsherd.js +2 -0
- package/bin/slopie.js +2 -0
- package/dist/audit-native-worker.js +1112 -0
- package/dist/audit-native-worker.js.map +7 -0
- package/dist/potsherd.js +21386 -20105
- package/dist/potsherd.js.map +4 -4
- package/licenses/ccusage-MIT.txt +21 -0
- package/package.json +4 -6
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../core/src/analytics/scan-pool.ts", "../../core/src/analytics/extract.ts", "../../core/src/markers.ts", "../../core/src/redact.ts", "../../core/src/theme.ts", "../../core/src/redact-rules.ts", "../../core/src/redact-elide.ts", "../../core/src/analytics/source.ts", "../src/audit-native-worker.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Reads history files on a bounded pool of worker threads. Each worker runs\n * `extractFile` on one file at a time and posts the facts back; the caller\n * aggregates. Without a worker entry (tests, unusual installs) the same code\n * runs inline.\n */\nimport os from 'node:os';\nimport {Worker, parentPort} from 'node:worker_threads';\nimport {extractFile, type NativeHarness, type SourceFacts} from './extract.js';\n\nexport interface ScanJob {\n file: string;\n harness: NativeHarness;\n /** Bytes, used to schedule the largest files first. */\n size: number;\n}\n\nexport type ScanResult = {job: ScanJob; facts: SourceFacts} | {job: ScanJob; error: string};\n\nexport interface ScanExecutor {\n /** Calls `onResult` once per job, in completion order. */\n scan(jobs: readonly ScanJob[], onResult: (result: ScanResult) => void, signal: AbortSignal): Promise<void>;\n close(): Promise<void>;\n}\n\nconst errorCode = (error: unknown) => (error instanceof Error && /^[a-z][a-z0-9_]{0,63}$/.test(error.message) ? error.message : 'source_unreadable');\n\nfunction runInline(job: ScanJob): ScanResult {\n try { return {job, facts: extractFile(job.file, job.harness)}; } catch (error) { return {job, error: errorCode(error)}; }\n}\n\n/** In-process executor: yields between files so progress events can flow. */\nexport const inlineScanExecutor: ScanExecutor = {\n async scan(jobs, onResult, signal) {\n for (const job of jobs) {\n if (signal.aborted) return;\n onResult(runInline(job));\n await new Promise(resolve => setImmediate(resolve));\n }\n },\n async close() {},\n};\n\nexport function defaultThreads(): number {\n const forced = Number(process.env['SLOPIE_AUDIT_THREADS']);\n if (Number.isInteger(forced) && forced > 0) return Math.min(forced, 16);\n return Math.max(1, Math.min(8, os.availableParallelism() - 1));\n}\n\n/** Worker-thread executor. Threads start lazily and are reused across scans. */\nexport function createScanPool(entry: URL | string, threads = defaultThreads()): ScanExecutor {\n let workers: Worker[] = [];\n let closed = false;\n const start = (count: number) => {\n while (workers.length < count) workers.push(new Worker(entry, {stdout: false, stderr: false, resourceLimits: {maxOldGenerationSizeMb: 512}}));\n };\n return {\n async scan(jobs, onResult, signal) {\n if (closed) throw new Error('scan_pool_closed');\n const queue = [...jobs].sort((a, b) => b.size - a.size);\n if (!queue.length) return;\n start(Math.min(threads, queue.length));\n await new Promise<void>(resolve => {\n let active = 0, done = false;\n const finish = () => { if (!done) { done = true; signal.removeEventListener('abort', finish); resolve(); } };\n signal.addEventListener('abort', finish, {once: true});\n const feed = (worker: Worker) => {\n if (done) return;\n if (signal.aborted || !queue.length) { if (active === 0) finish(); return; }\n const job = queue.shift()!;\n active++;\n const onMessage = (message: {facts?: SourceFacts; error?: string}) => {\n cleanup();\n active--;\n if (!done) onResult(message.facts ? {job, facts: message.facts} : {job, error: message.error ?? 'source_unreadable'});\n feed(worker);\n };\n const onError = () => {\n cleanup();\n active--;\n workers = workers.filter(w => w !== worker);\n // A crashed thread loses only its file; read it here instead.\n if (!done) onResult(runInline(job));\n if (!workers.length && queue.length) { for (const next of queue.splice(0)) if (!done) onResult(runInline(next)); }\n if (active === 0 && !queue.length) finish();\n };\n const cleanup = () => { worker.off('message', onMessage); worker.off('error', onError); };\n worker.on('message', onMessage);\n worker.once('error', onError);\n worker.postMessage(job);\n };\n for (const worker of workers.slice(0, Math.min(workers.length, queue.length))) feed(worker);\n });\n },\n async close() {\n closed = true;\n const all = workers;\n workers = [];\n await Promise.all(all.map(w => w.terminate().catch(() => 0)));\n },\n };\n}\n\n/** Worker entry point. */\nexport function runScanWorker(): void {\n if (!parentPort) throw new Error('scan_worker_requires_parent');\n const port = parentPort;\n port.on('message', (job: ScanJob) => {\n try { port.postMessage({facts: extractFile(job.file, job.harness)}); } catch (error) { port.postMessage({error: errorCode(error)}); }\n });\n}\n", "/**\n * Single-pass extraction of audit facts from one native history file.\n *\n * Only two things are kept from a transcript:\n * - usage metadata of assistant responses (model, token counts, time), and\n * - the text of user-role messages (redacted), classified as human or not.\n *\n * Assistant bodies, tool calls/outputs, images and reasoning are never parsed:\n * lines are classified from a short byte prefix (or a byte search) and only the\n * interesting ones go through JSON.parse.\n *\n * The usage rules follow ccusage 20.x; the prompt rules are documented in\n * `classifyText` and next to each harness below.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport {hasExclusionMarker} from '../markers.js';\nimport {clean} from './source.js';\nimport type {AuditHarness} from './contracts.js';\n\nexport const FACTS_VERSION = 4;\n\nexport type NativeHarness = Exclude<AuditHarness, 'opencode'>;\n\nexport interface UsageFact {\n /** De-duplication key (see `aggregate.ts`); null when the record has no stable identity. */\n key: string | null;\n at: number | null;\n model: string | null;\n provider: string | null;\n /** Uncached input tokens. */\n input: number;\n /** Output tokens, including reasoning. */\n output: number;\n cacheRead: number;\n /** All cache-creation tokens (5m + 1h). */\n cacheWrite: number;\n cacheWrite1h: number;\n reasoning: number;\n /** True when the model was not recorded and had to be inferred. */\n inferredModel?: boolean;\n /** Claude: written by a subagent / sidechain. */\n sidechain?: boolean;\n /** Claude: the record had no requestId. */\n requestless?: boolean;\n /** Claude: message id + session for sidechain-replay detection. */\n replayKey?: string;\n /** Recorded service tier: Claude `usage.speed`, Codex `thread_settings.service_tier`. */\n tier?: 'fast' | 'standard';\n /** Codex remote compaction request (token_usage_record). */\n compaction?: boolean;\n}\n\nexport type PromptKind = 'human' | 'command' | 'answer' | 'excluded';\n\nexport interface PromptFact {\n key: string;\n at: number | null;\n project: string | null;\n /** Redacted text, capped; empty unless the prompt is human. */\n text: string;\n kind: PromptKind;\n /** Why a user-role message is not a human prompt. */\n reason: string | null;\n /** Model whose response the user was reacting to. */\n before: string | null;\n beforeProvider: string | null;\n /** Model that answered this prompt. */\n after: string | null;\n afterProvider: string | null;\n}\n\nexport interface SourceFacts {\n v: number;\n harness: AuditHarness;\n file: string;\n size: number;\n mtimeMs: number;\n sessionId: string;\n /** Claude: all session ids seen on user records of a top-level transcript. */\n sessionIds?: string[];\n parentId: string | null;\n child: boolean;\n project: string | null;\n title: string | null;\n /** Non-interactive session (codex exec / sdk). */\n automated: boolean;\n /** Contains a potsherd exclusion marker: its text is never kept. */\n excluded: boolean;\n /** Codex: session_meta timestamp, used to order parents before children. */\n startedAt: string | null;\n firstAt: number | null;\n lastAt: number | null;\n usage: UsageFact[];\n prompts: PromptFact[];\n malformed: number;\n}\n\nconst MAX_PROMPT_CHARS = 4000;\n\ntype Rec = Record<string, unknown>;\nconst isRec = (v: unknown): v is Rec => v !== null && typeof v === 'object' && !Array.isArray(v);\nconst str = (v: unknown): string | null => (typeof v === 'string' && v.length > 0 ? v : null);\nconst int = (v: unknown): number => (typeof v === 'number' && Number.isFinite(v) && v > 0 ? Math.floor(v) : 0);\nfunction time(v: unknown): number | null {\n if (typeof v === 'string') {\n const t = Date.parse(v);\n return Number.isFinite(t) ? t : null;\n }\n if (typeof v === 'number' && Number.isFinite(v)) return v < 1e11 ? v * 1000 : v;\n return null;\n}\n\nfunction textOf(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n const parts: string[] = [];\n for (const block of content) if (isRec(block) && typeof block.text === 'string') parts.push(block.text);\n return parts.join('\\n');\n}\n\n/* ------------------------------------------------------- prompt rules --- */\n\n/** Text a harness injects into the user role. Anything starting with these is not a prompt. */\nconst INJECTED_PREFIXES: readonly [string, string][] = [\n ['<task-notification>', 'task_notification'],\n ['<local-command-stdout>', 'local_command_output'], ['<local-command-stderr>', 'local_command_output'],\n ['<local-command-caveat>', 'local_command_output'],\n ['[Request interrupted', 'interrupt_marker'], ['<turn_aborted>', 'interrupt_marker'],\n ['<environment_context>', 'environment_context'], ['# AGENTS.md instructions', 'agents_md'],\n ['<user_instructions>', 'agents_md'], ['<external_codex_apps', 'app_state'],\n ['<recommended_plugins>', 'app_state'], ['<heartbeat>', 'automation'],\n ['The following is the Codex agent history', 'guardian_review'],\n ['You have new hive inbox message', 'automation'],\n ['Another Claude session sent a message', 'cross_session_message'],\n ['This session is being continued from a previous conversation', 'compaction_summary'],\n ['Your claude.ai usage limit has reset', 'auto_continuation'],\n ['Base directory for this skill:', 'skill_expansion'],\n];\nconst SYSTEM_REMINDER = /<system-reminder>[\\s\\S]*?<\\/system-reminder>/g;\n\n/** Classifies the text of a user-role message. Returns the kind, a reason and the cleaned text. */\nexport function classifyText(raw: string): {kind: PromptKind; reason: string | null; text: string} {\n const text = (raw.includes('<system-reminder>') ? raw.replace(SYSTEM_REMINDER, '') : raw).trim();\n if (!text) return {kind: 'excluded', reason: 'empty', text};\n for (const [prefix, reason] of INJECTED_PREFIXES) if (text.startsWith(prefix)) return {kind: 'excluded', reason, text};\n if (text.startsWith('<command-name>') || text.startsWith('<command-message>')) return {kind: 'command', reason: 'slash_command', text};\n if (text.startsWith('<send_user_message_question_reply>')) return {kind: 'answer', reason: 'question_answer', text};\n return {kind: 'human', reason: null, text};\n}\n\nconst CODEX_STRIP = [/<in-app-browser-context[\\s\\S]*?<\\/in-app-browser-context>/g, /<image name=[^>]*>\\s*<\\/image>/g, /<\\/?image[^>]*>/g];\n/** Codex desktop wraps the typed request: '# Files mentioned by the user: ... ## My request: <typed>'. */\nfunction cleanCodexText(raw: string): string {\n let t = raw;\n if (t.includes('<')) for (const rx of CODEX_STRIP) t = t.replace(rx, '');\n const at = t.indexOf('## My request');\n if (at >= 0) {\n const rest = t.slice(at + '## My request'.length), colon = rest.indexOf(':');\n t = colon >= 0 ? rest.slice(colon + 1) : rest;\n } else if (t.trimStart().startsWith('# Files mentioned by the user')) t = '';\n return t.trim();\n}\n\n/* ----------------------------------------------------------- builder --- */\n\ninterface Builder {\n facts: SourceFacts;\n currentModel: string | null;\n currentProvider: string | null;\n /** Prompts waiting for the model that answers them. */\n pending: PromptFact[];\n seenPrompts: Set<string>;\n}\n\nfunction makeBuilder(file: string, harness: NativeHarness, size: number, mtimeMs: number): Builder {\n return {\n facts: {\n v: FACTS_VERSION, harness, file, size, mtimeMs,\n sessionId: path.basename(file, '.jsonl'), parentId: null, child: false, project: null, title: null,\n automated: false, excluded: false, startedAt: null, firstAt: null, lastAt: null, usage: [], prompts: [], malformed: 0,\n },\n currentModel: null, currentProvider: null, pending: [], seenPrompts: new Set(),\n };\n}\n\nfunction touch(b: Builder, at: number | null): void {\n if (at === null) return;\n if (b.facts.firstAt === null || at < b.facts.firstAt) b.facts.firstAt = at;\n if (b.facts.lastAt === null || at > b.facts.lastAt) b.facts.lastAt = at;\n}\n\nfunction answered(b: Builder, model: string | null, provider: string | null): void {\n if (!model) return;\n for (const p of b.pending) { p.after = model; p.afterProvider = provider; }\n b.pending.length = 0;\n b.currentModel = model;\n b.currentProvider = provider;\n}\n\nfunction addUsage(b: Builder, u: UsageFact): void {\n b.facts.usage.push(u);\n touch(b, u.at);\n answered(b, u.model, u.provider);\n}\n\nfunction addPrompt(b: Builder, key: string, at: number | null, project: string | null, kind: PromptKind, reason: string | null, text: string): void {\n if (b.seenPrompts.has(key)) return;\n b.seenPrompts.add(key);\n if (kind === 'human' && hasExclusionMarker(text)) b.facts.excluded = true;\n const prompt: PromptFact = {\n key, at, project, kind, reason,\n text: kind === 'human' ? clean(text.length > MAX_PROMPT_CHARS ? text.slice(0, MAX_PROMPT_CHARS) : text) : '',\n before: b.currentModel, beforeProvider: b.currentProvider, after: null, afterProvider: null,\n };\n b.facts.prompts.push(prompt);\n if (kind === 'human') b.pending.push(prompt);\n touch(b, at);\n}\n\n/* ------------------------------------------------------------ Claude --- */\n\nconst SUBAGENT_DIR = `${path.sep}subagents${path.sep}`;\n\nfunction claudeLine(b: Builder, line: Buffer, subagent: boolean, sessions: Set<string>): void {\n const assistant = line.includes('\"type\":\"assistant\"');\n if (assistant ? !line.includes('\"usage\"') : (subagent || !line.includes('\"type\":\"user\"'))) {\n // Titles are tiny records; everything else here is irrelevant.\n if (line.length > 4096 || (!line.includes('-title\"') && !line.includes('\"type\":\"summary\"'))) return;\n }\n let r: Rec;\n try { r = JSON.parse(line.toString('utf8')) as Rec; } catch { b.facts.malformed++; return; }\n if (!isRec(r)) return;\n const f = b.facts;\n if (r.type === 'summary' || r.type === 'custom-title' || r.type === 'ai-title') {\n const title = str(r.customTitle) ?? str(r.aiTitle) ?? str(r.title) ?? str(r.summary);\n if (title && (r.type !== 'summary' || !f.title)) f.title = title;\n return;\n }\n if (typeof r.cwd === 'string' && !f.project) f.project = r.cwd;\n const sid = str(r.sessionId);\n if (sid) {\n if (subagent) { f.parentId = sid; f.child = true; f.sessionId = `${sid}:${path.basename(f.file, '.jsonl')}`; }\n else if (!sessions.size) f.sessionId = sid;\n }\n const m = isRec(r.message) ? r.message : {};\n const at = time(r.timestamp);\n if (r.type === 'assistant') {\n const u = m.usage;\n if (!isRec(u)) return;\n const model = str(m.model);\n if (model === '<synthetic>') return;\n const cc = isRec(u.cache_creation) ? u.cache_creation : null;\n const write = cc ? int(cc.ephemeral_5m_input_tokens) + int(cc.ephemeral_1h_input_tokens) : int(u.cache_creation_input_tokens);\n const id = str(m.id), request = str(r.requestId), session = sid ?? f.file;\n const fact: UsageFact = {\n key: id ? (request ? `c:${id}:${request}` : `c:${id}::${session}:${String(r.timestamp)}`) : null,\n at, model, provider: null,\n input: int(u.input_tokens), output: int(u.output_tokens), cacheRead: int(u.cache_read_input_tokens),\n cacheWrite: write, cacheWrite1h: cc ? int(cc.ephemeral_1h_input_tokens) : 0, reasoning: 0,\n };\n if (r.isSidechain === true) fact.sidechain = true;\n if (!request) fact.requestless = true;\n if (id) fact.replayKey = `${id}:${session}`;\n if (u.speed === 'fast') fact.tier = 'fast';\n addUsage(b, fact);\n return;\n }\n if (r.type !== 'user' || r.isSidechain === true || subagent) return;\n const content = m.content;\n if (Array.isArray(content) && content.some(block => isRec(block) && block.type === 'tool_result')) return;\n if (sid) sessions.add(sid);\n const origin = isRec(r.origin) ? str(r.origin.kind) : null;\n const flagged = r.isMeta === true ? 'is_meta'\n : r.isCompactSummary === true ? 'compaction_summary'\n : origin && origin !== 'human' ? `origin_${origin}`\n : r.entrypoint === 'sdk-cli' ? 'headless_claude_p'\n : r.promptSource === 'system' ? 'prompt_source_system'\n : null;\n let {kind, reason, text} = classifyText(textOf(content));\n if (flagged) { kind = 'excluded'; reason = flagged; }\n else if (reason === 'empty' && origin === 'human' && Array.isArray(content) && content.some(block => isRec(block) && block.type === 'image')) {\n kind = 'human'; reason = null; text = '[image]';\n }\n addPrompt(b, str(r.uuid) ?? `${f.sessionId}:${String(r.timestamp)}`, at, str(r.cwd) ?? f.project, kind, reason, text);\n}\n\n/* ------------------------------------------------------------- Codex --- */\n\ninterface PendingCodexPrompt {key: string | null; at: number | null; text: string; imported: boolean; project: string | null}\ninterface CodexState {\n lineNo: number;\n previousTotal: string | null;\n tier: 'fast' | 'standard' | undefined;\n imported: boolean;\n records: Map<string, {at: number | null; usage: Rec}>;\n compacted: Set<string>;\n deltas: Set<string>;\n itemPrompts: PendingCodexPrompt[];\n responsePrompts: PendingCodexPrompt[];\n}\n\nconst U_KEYS = ['input_tokens', 'cached_input_tokens', 'output_tokens', 'reasoning_output_tokens', 'total_tokens'] as const;\nconst tuple = (u: unknown): number[] | null => (isRec(u) ? U_KEYS.map(k => int(u[k])) : null);\n\n/** Codex records its review helper as `codex-auto-review`; bill it as the helper model of that date. */\nconst AUTO_REVIEW_MODELS: readonly [string, string][] = [\n ['2026-07-30', 'gpt-5.6-luna'], ['2026-03-05', 'gpt-5.4'], ['2026-02-05', 'gpt-5.3-codex'],\n ['2025-12-11', 'gpt-5.2-codex'], ['2025-11-13', 'gpt-5.1-codex'], ['2025-09-15', 'gpt-5-codex'],\n];\nfunction codexModel(model: string | null, timestamp: string): {model: string; inferred: boolean} {\n if (model === 'codex-auto-review') {\n const day = timestamp.slice(0, 10);\n return {model: AUTO_REVIEW_MODELS.find(([since]) => day >= since)?.[1] ?? 'gpt-5', inferred: true};\n }\n return model ? {model, inferred: false} : {model: 'gpt-5', inferred: true};\n}\n\nfunction codexFact(b: Builder, s: CodexState, at: number | null, timestamp: string, d: readonly number[], key: string | null): UsageFact {\n const input = d[0]!, output = d[2]!, reasoning = d[3]!, cached = Math.min(d[1]!, input);\n const {model, inferred} = codexModel(b.currentModel, timestamp);\n const fact: UsageFact = {key, at, model, provider: 'openai', input: input - cached, output, cacheRead: cached, cacheWrite: 0, cacheWrite1h: 0, reasoning};\n if (inferred) fact.inferredModel = true;\n if (s.tier) fact.tier = s.tier;\n return fact;\n}\n\n/** Whether a Codex line (judged by its first bytes) can matter; everything else is skipped unread. */\nfunction codexWanted(head: string, lineNo: number): boolean {\n return (lineNo === 0 && head.includes('session_meta')) || head.includes('\"token_count\"') || head.includes('\"turn_context\"')\n || head.includes('thread_settings_applied') || head.includes('\"task_started\"') || head.includes('\"token_usage_record\"')\n || head.includes('\"type\":\"compacted\"') || (head.includes('\"UserMessage\"') && head.includes('item_completed'))\n || (head.includes('\"role\":\"user\"') && head.includes('\"response_item\"')) || (head.includes('\"event_msg\"') && head.includes('\"type\":\"user_message\"'));\n}\n\nfunction codexLine(b: Builder, line: Buffer, s: CodexState): void {\n const n = s.lineNo++;\n const head = line.toString(\"latin1\", 0, Math.min(line.length, 260));\n const f = b.facts;\n const parse = (): Rec | null => {\n try { return JSON.parse(line.toString('utf8')) as Rec; } catch { f.malformed++; return null; }\n };\n if (n === 0 && head.includes('session_meta')) {\n const r = parse();\n if (!r) return;\n const p = isRec(r.payload) ? r.payload : {};\n f.sessionId = str(p.id) ?? str(p.session_id) ?? f.sessionId;\n f.project = str(p.cwd);\n f.startedAt = str(p.timestamp) ?? str(r.timestamp);\n const source = p.source;\n if (isRec(source) && 'subagent' in source) {\n f.child = true;\n const sub = isRec(source.subagent) ? source.subagent : {};\n const spawn = isRec(sub.thread_spawn) ? sub.thread_spawn : {};\n f.parentId = str(spawn.parent_thread_id) ?? str(p.parent_thread_id) ?? str(p.forked_from_id);\n }\n if (source === 'exec' || p.originator === 'codex_exec' || p.originator === 'codex_sdk_ts') f.automated = true;\n touch(b, time(r.timestamp));\n return;\n }\n if (head.includes('\"token_count\"')) {\n const r = parse();\n const p = r && isRec(r.payload) ? r.payload : null;\n const info = p && isRec(p.info) ? p.info : null;\n if (!r || !info) return;\n const total = tuple(info.total_token_usage), last = tuple(info.last_token_usage);\n const totalSig = total ? total.join(',') : null;\n const advanced = totalSig === null || totalSig !== s.previousTotal;\n let d: number[] | null = null;\n if (last && advanced) d = last;\n else if (total) {\n const prev = s.previousTotal ? s.previousTotal.split(',').map(Number) : [0, 0, 0, 0, 0];\n d = total.map((v, i) => Math.max(0, v - prev[i]!));\n }\n if (totalSig) s.previousTotal = totalSig;\n if (!d || !(d[0]! || d[1]! || d[2]! || d[3]!)) return;\n s.deltas.add(d.join(','));\n // Counters replayed into another file repeat exactly; count each fingerprint once.\n const key = totalSig ? `x:${totalSig}|${last ? last.join(',') : ''}` : null;\n addUsage(b, codexFact(b, s, time(r.timestamp), String(r.timestamp ?? ''), d, key));\n return;\n }\n if (head.includes('\"turn_context\"')) {\n const p = parse()?.payload;\n if (isRec(p)) { b.currentModel = str(p.model) ?? b.currentModel; if (typeof p.cwd === 'string') f.project = p.cwd; }\n return;\n }\n if (head.includes('thread_settings_applied')) {\n const p = parse()?.payload;\n const settings = isRec(p) && isRec(p.thread_settings) ? p.thread_settings : null;\n if (settings && 'service_tier' in settings) {\n const v = settings.service_tier;\n s.tier = v === 'priority' || v === 'fast' ? 'fast' : v === 'default' || v === 'standard' ? 'standard' : undefined;\n }\n return;\n }\n if (head.includes('\"task_started\"')) {\n const p = parse()?.payload;\n s.imported = isRec(p) && String(p.turn_id ?? '').startsWith('external-import');\n return;\n }\n if (head.includes('\"UserMessage\"') && head.includes('item_completed')) {\n const r = parse();\n const p = r && isRec(r.payload) ? r.payload : null;\n if (!r || !p) return;\n const item = isRec(p.item) ? p.item : {};\n s.itemPrompts.push({key: str(item.id), at: time(r.timestamp), text: textOf(item.content),\n imported: s.imported || String(p.turn_id ?? '').startsWith('external-import'), project: f.project});\n return;\n }\n if (head.includes('\"role\":\"user\"') && head.includes('\"response_item\"')) {\n const r = parse();\n if (!r) return;\n const p = isRec(r.payload) ? r.payload : {};\n s.responsePrompts.push({key: null, at: time(r.timestamp), text: textOf(p.content), imported: s.imported, project: f.project});\n return;\n }\n if (head.includes('\"event_msg\"') && line.subarray(0, 400).includes('\"type\":\"user_message\"')) {\n const r = parse();\n if (!r) return;\n const p = isRec(r.payload) ? r.payload : {};\n s.itemPrompts.push({key: null, at: time(r.timestamp), text: typeof p.message === 'string' ? p.message : '', imported: s.imported, project: f.project});\n return;\n }\n if (head.includes('\"token_usage_record\"')) {\n const r = parse();\n const p = r && isRec(r.payload) ? r.payload : null;\n const id = p ? str(p.response_id) : null;\n if (r && p && id && isRec(p.usage)) s.records.set(id, {at: time(r.timestamp), usage: p.usage});\n return;\n }\n if (head.includes('\"type\":\"compacted\"')) {\n // The marker carries the whole replacement history; only its own id matters.\n const at = line.lastIndexOf('\"compaction_response_id\":\"');\n if (at >= 0) {\n const start = at + 26, end = line.indexOf(34, start);\n if (end > start) s.compacted.add(line.toString(\"latin1\", start, end));\n }\n }\n}\n\nfunction finishCodex(b: Builder, s: CodexState): void {\n const f = b.facts;\n // Remote compaction requests: linked to a compacted marker and not already a token_count delta.\n for (const id of s.compacted) {\n const record = s.records.get(id);\n const d = record ? tuple(record.usage) : null;\n if (!record || !d || !(d[0]! || d[1]! || d[2]! || d[3]!) || s.deltas.has(d.join(','))) continue;\n const fact = codexFact(b, s, record.at, record.at ? new Date(record.at).toISOString() : '', d, `codex-compaction:${id}`);\n fact.compaction = true;\n f.usage.push(fact);\n touch(b, record.at);\n }\n // Current Codex marks typed input with item_completed/UserMessage (older: event_msg/user_message);\n // response_item role=user also carries injected context and is only a fallback.\n const list = s.itemPrompts.length ? s.itemPrompts : s.responsePrompts;\n const reason = f.child ? 'subagent_parent_written' : f.automated ? 'programmatic_exec_session' : null;\n const seen = new Set<string>();\n list.forEach((p, i) => {\n if (p.key) { if (seen.has(p.key)) return; seen.add(p.key); }\n const key = `codex:${f.sessionId}:${p.key ?? i}`;\n if (reason) return addPrompt(b, key, p.at, p.project, 'excluded', reason, '');\n if (p.imported) return addPrompt(b, key, p.at, p.project, 'excluded', 'imported_from_claude_transcript', '');\n const c = classifyText(cleanCodexText(p.text));\n addPrompt(b, key, p.at, p.project, c.kind, c.reason, c.text);\n });\n // Attribute prompts to the models around them, by time.\n const answers = f.usage.filter(u => u.at !== null).sort((x, y) => x.at! - y.at!);\n let j = 0, previous: string | null = null;\n for (const p of [...f.prompts].sort((x, y) => (x.at ?? 0) - (y.at ?? 0))) {\n while (j < answers.length && answers[j]!.at! <= (p.at ?? 0)) previous = answers[j++]!.model;\n p.before = previous; p.beforeProvider = previous ? 'openai' : null;\n p.after = answers[j]?.model ?? null; p.afterProvider = p.after ? 'openai' : null;\n }\n}\n\n/* ---------------------------------------------------------------- pi --- */\n\nfunction piLine(b: Builder, line: Buffer): void {\n let r: Rec;\n try { r = JSON.parse(line.toString('utf8')) as Rec; } catch { b.facts.malformed++; return; }\n const f = b.facts, at = time(r.timestamp);\n if (r.type === 'session') {\n f.sessionId = str(r.id) ?? f.sessionId;\n f.project = str(r.cwd);\n touch(b, at);\n return;\n }\n if (r.type === 'model_change') {\n b.currentModel = str(r.modelId) ?? b.currentModel;\n b.currentProvider = str(r.provider) ?? b.currentProvider;\n return;\n }\n if (r.type !== 'message' || !isRec(r.message)) return;\n const m = r.message;\n if (m.role === 'assistant' && isRec(m.usage)) {\n const u = m.usage;\n const fact: UsageFact = {\n key: `pi:${f.sessionId}:${String(r.id)}`, at, model: str(m.model) ?? b.currentModel, provider: str(m.provider) ?? b.currentProvider,\n input: int(u.input), output: int(u.output), cacheRead: int(u.cacheRead), cacheWrite: int(u.cacheWrite), cacheWrite1h: 0, reasoning: 0,\n };\n if (fact.input + fact.output + fact.cacheRead + fact.cacheWrite > 0) addUsage(b, fact);\n else answered(b, fact.model, fact.provider);\n return;\n }\n if (m.role === 'user') {\n const c = classifyText(textOf(m.content));\n addPrompt(b, `pi:${f.sessionId}:${String(r.id)}`, at, f.project, c.kind, c.reason, c.text);\n }\n}\n\n/* ------------------------------------------------------------ driver --- */\n\nconst CHUNK = 4 * 1024 * 1024;\n/** Lines longer than this are skipped without being materialized. */\nconst MAX_LINE = 256 * 1024 * 1024;\n\nlet sharedBuffer: Buffer | null = null;\n\n/** Reads one file once, in large chunks, and returns its facts. */\nexport function extractFile(file: string, harness: NativeHarness): SourceFacts {\n const fd = fs.openSync(file, 'r');\n try {\n const stat = fs.fstatSync(fd);\n const b = makeBuilder(file, harness, stat.size, stat.mtimeMs);\n const codex: CodexState = {lineNo: 0, previousTotal: null, tier: undefined, imported: false,\n records: new Map(), compacted: new Set(), deltas: new Set(), itemPrompts: [], responsePrompts: []};\n const subagent = file.includes(SUBAGENT_DIR), sessions = new Set<string>();\n const onLine = harness === 'claude' ? (line: Buffer) => claudeLine(b, line, subagent, sessions)\n : harness === 'codex' ? (line: Buffer) => codexLine(b, line, codex)\n : (line: Buffer) => piLine(b, line);\n // One read buffer per thread, reused across files (it only grows for very long lines).\n let buffer = sharedBuffer ??= Buffer.allocUnsafe(CHUNK);\n let filled = 0, position = 0, skipping = false;\n for (;;) {\n if (filled === buffer.length) {\n // A line longer than the buffer: drop it unread unless its head says we need it.\n if (harness === 'codex' && !codexWanted(buffer.toString('latin1', 0, Math.min(filled, 400)), codex.lineNo)) { skipping = true; filled = 0; codex.lineNo++; }\n else if (buffer.length >= MAX_LINE) { skipping = true; filled = 0; b.facts.malformed++; }\n else { const bigger = Buffer.allocUnsafe(buffer.length * 2); buffer.copy(bigger, 0, 0, filled); buffer = bigger; if (bigger.length <= 4 * CHUNK) sharedBuffer = bigger; }\n }\n const n = fs.readSync(fd, buffer, filled, buffer.length - filled, position);\n position += n;\n const end = filled + n;\n let start = 0;\n for (;;) {\n const nl = buffer.indexOf(10, start);\n if (nl < 0 || nl >= end) break;\n if (skipping) skipping = false;\n else if (nl > start) onLine(buffer.subarray(start, nl));\n start = nl + 1;\n }\n if (n === 0) {\n if (!skipping && start < end) onLine(buffer.subarray(start, end));\n break;\n }\n buffer.copyWithin(0, start, end);\n filled = end - start;\n }\n if (harness === 'codex') finishCodex(b, codex);\n if (harness === 'claude' && !subagent && sessions.size) b.facts.sessionIds = [...sessions];\n if (b.facts.excluded) for (const p of b.facts.prompts) p.text = '';\n return b.facts;\n } finally {\n fs.closeSync(fd);\n }\n}\n\n/* ------------------------------------------------- Claude history.jsonl --- */\n\nexport interface HistoryEntry {\n sessionId: string | null;\n at: number | null;\n project: string | null;\n kind: PromptKind;\n text: string;\n}\n\n/**\n * `~/.claude/history.jsonl` is Claude Code's log of what the user typed. It is\n * used only for sessions whose transcript is gone: `display` starting with `/`\n * is a slash command, anything else a human prompt.\n */\nexport function extractHistory(file: string): HistoryEntry[] {\n let raw: string;\n try { raw = fs.readFileSync(file, 'utf8'); } catch { return []; }\n const out: HistoryEntry[] = [];\n for (const line of raw.split('\\n')) {\n if (!line) continue;\n let r: Rec;\n try { r = JSON.parse(line) as Rec; } catch { continue; }\n const display = typeof r.display === 'string' ? r.display.trim() : '';\n if (!display) continue;\n const command = display.startsWith('/');\n out.push({\n sessionId: str(r.sessionId), at: time(r.timestamp), project: str(r.project),\n kind: command ? 'command' : 'human',\n text: command ? '' : clean(display.length > MAX_PROMPT_CHARS ? display.slice(0, MAX_PROMPT_CHARS) : display),\n });\n }\n return out;\n}\n", "/**\n * Opt-out markers: text that means \"do not index this conversation\".\n *\n * Ported from obra/episodic-memory@1075769 `src/constants.ts` (the summarizer\n * content marker, verbatim) and `src/sync.ts:7-11` (the exclusion list)\n * (MIT, (c) 2025 Jesse Vincent).\n *\n * Two of the three markers are upstream's own product strings and are kept\n * **verbatim on purpose**: a user who has been running episodic-memory has\n * these markers sitting in real transcripts already, and potsherd must honour\n * the opt-out they represent rather than re-indexing what they excluded.\n * potsherd's own marker is added alongside.\n */\n\n/** Upstream's summarizer prompt contains this, so its own runs never index. */\nexport const SUMMARIZER_CONTEXT_MARKER =\n 'Context: This summary will be shown in a list to help users and Claude choose which conversations are relevant';\n\n/** potsherd's equivalent, for the card writer in phase 2. */\nexport const POTSHERD_CARD_MARKER =\n '<INSTRUCTIONS-TO-POTSHERD>DO NOT INDEX THIS CHAT</INSTRUCTIONS-TO-POTSHERD>';\n\nexport const EXCLUSION_MARKERS: readonly string[] = [\n '<INSTRUCTIONS-TO-EPISODIC-MEMORY>DO NOT INDEX THIS CHAT</INSTRUCTIONS-TO-EPISODIC-MEMORY>',\n 'Only use NO_INSIGHTS_FOUND',\n SUMMARIZER_CONTEXT_MARKER,\n POTSHERD_CARD_MARKER,\n];\n\n/** True when a transcript's text opts out of indexing. */\nexport function hasExclusionMarker(text: string): boolean {\n return EXCLUSION_MARKERS.some((marker) => text.includes(marker));\n}\n", "import { createHash } from 'node:crypto';\nimport type { Exchange, ExchangeToolCall } from './adapters/types.js';\nimport { Theme } from './theme.js';\nimport type { Row } from './render.js';\nimport * as fmt from './format.js';\nimport {\n ALLOW_SPANS,\n RULES,\n SECRET_TYPES,\n type Rule,\n type SecretType,\n} from './redact-rules.js';\n\nexport type { SecretType } from './redact-rules.js';\n// The pre-redaction pass. Not redaction \u2014 it removes binary bulk that carries\n// no meaning for retrieval \u2014 but it runs immediately before this module on the\n// same strings, so it is re-exported here and the two are read together.\nexport {\n elideBinary,\n elideExchange,\n emptyElisions,\n addElisions,\n MIN_PAYLOAD,\n ELISION_RE,\n type Elisions,\n} from './redact-elide.js';\nexport {\n RULES,\n SECRET_TYPES,\n shannonEntropy,\n ENTROPY_MIN_LENGTH,\n ENTROPY_THRESHOLD,\n nameLooksLikeSecret,\n valueLooksLikeSecret,\n type Rule,\n} from './redact-rules.js';\n\n/**\n * L2 \u2014 redaction. `plans/03-ARCHITECTURE.md` \u00A75 and \u00A711.\n *\n * This module decides whether a user's secrets can ever leave their machine.\n * Nothing downstream of it \u2014 the index, cards, ask, graft \u2014 is allowed to see\n * an unredacted string, and every string that reaches a model passes through\n * here first. A false negative is a leaked credential; a false positive is one\n * unreadable token in a search result. The rules are tuned accordingly.\n *\n * Pure and synchronous by contract: no I/O, no network, no module state that\n * survives a call. It runs over the whole corpus at index time, so it also has\n * to be fast \u2014 see `tests/redact.test.ts` for the measured 10 MB throughput.\n *\n * ## The mask\n *\n * \u2039redacted:<type>:<sha8>\u203A e.g. \u2039redacted:aws:cccc2222\u203A\n *\n * `sha8` is the first 8 hex characters of sha256(secret), so the *same* secret\n * always produces the *same* mask: the index stays searchable by shape (\"that\n * session where the same aws key appeared\") and never by value. Two different\n * secrets colliding needs a 32-bit sha256 prefix collision.\n *\n * The guillemets are deliberate: `\u2039` U+2039 and `\u203A` U+203A cannot appear in a\n * shell token, a json key or a base64 blob, so the mask can never be confused\n * for content and can never be pasted back into a command by accident.\n *\n * fts5 tokenisation, measured against the bundled sqlite (default `unicode61`\n * tokenizer): `\u2039redacted:aws:cccc2222\u203A` indexes as the three tokens\n * `redacted`, `aws`, `cccc2222` \u2014 both guillemets and both colons are\n * separators. So `find \"cccc2222\"` locates every exchange that leaked that one\n * secret, `find \"redacted AND aws\"` locates every exchange that leaked an aws\n * key, and the phrase query `\"redacted:aws:cccc2222\"` also works. The mask\n * costs three tokens per hit in the index and nothing else.\n *\n * ## What redaction does NOT touch\n *\n * The archive copy (L3) is byte-exact and unredacted: it is the user's own file\n * on the user's own disk. Only the *index* is redacted. There is no\n * `--no-redact` flag (`03` \u00A711) and adding one is not a feature request.\n */\n\n/** A masked secret. `sha8` identifies it without revealing it. */\nexport interface RedactionHit {\n type: SecretType;\n /** Rule id that claimed it \u2014 `doctor` and false-positive triage. */\n rule: string;\n /** First 8 hex chars of sha256(secret). */\n sha8: string;\n /** Offset of the secret in the *input* text. */\n start: number;\n /** Length of the secret in the input text. */\n length: number;\n}\n\nexport interface RedactionResult {\n text: string;\n hits: RedactionHit[];\n}\n\n/** Counts by type, for `doctor`. */\nexport interface RedactionCounts {\n total: number;\n byType: Record<SecretType, number>;\n}\n\nconst OPEN = '\u2039';\nconst CLOSE = '\u203A';\n\n/** Matches potsherd's own mask, so a second pass recognises its output. */\nexport const MASK_RE = new RegExp(`${OPEN}redacted:[a-z-]+:[0-9a-f]{8}${CLOSE}`, 'g');\n\n/** The first 8 hex characters of sha256(secret). */\nexport function secretDigest(secret: string): string {\n return createHash('sha256').update(secret, 'utf8').digest('hex').slice(0, 8);\n}\n\n/** `\u2039redacted:aws:cccc2222\u203A` */\nexport function maskFor(type: SecretType, secret: string): string {\n return `${OPEN}redacted:${type}:${secretDigest(secret)}${CLOSE}`;\n}\n\n/** Does this text already contain a potsherd mask? */\nexport function containsMask(text: string): boolean {\n return new RegExp(MASK_RE.source).test(text);\n}\n\ninterface Span {\n start: number;\n end: number;\n}\n\n/**\n * Mask every secret in `text`.\n *\n * Rules are applied in `RULES` order and the first rule to claim a span keeps\n * it, which is why the table is ordered specific \u2192 generic \u2192 entropy: a jwt is\n * reported as a jwt and not as three high-entropy tokens, and the base64 body\n * of a private key is reported as one private key and not as forty.\n *\n * Idempotent: existing masks are protected spans, so `redact(redact(x).text)`\n * returns the same text with no further hits.\n */\nexport function redact(text: string): RedactionResult {\n if (typeof text !== 'string' || text.length === 0) return { text: text ?? '', hits: [] };\n\n // Claimed characters. One byte per input character: an O(1) overlap test that\n // stays honest when 40 rules each propose overlapping spans.\n const claimed = new Uint8Array(text.length);\n const spans: Array<Span & { rule: Rule; value: string }> = [];\n\n // Allowlisted regions (data URIs, SRI hashes) and this module's own masks are\n // claimed before any rule runs, so nothing can match inside them.\n for (const span of protectedSpans(text)) claim(claimed, span);\n\n for (const rule of RULES) {\n for (const m of rule.scan(text)) {\n if (m.start < 0 || m.end > text.length || m.end <= m.start) continue;\n if (isClaimed(claimed, m)) continue;\n claim(claimed, m);\n spans.push({ start: m.start, end: m.end, rule, value: m.value });\n }\n }\n\n if (spans.length === 0) return { text, hits: [] };\n spans.sort((a, b) => a.start - b.start);\n\n const out: string[] = [];\n const hits: RedactionHit[] = [];\n let cursor = 0;\n for (const s of spans) {\n out.push(text.slice(cursor, s.start));\n const sha8 = secretDigest(s.value);\n out.push(`${OPEN}redacted:${s.rule.type}:${sha8}${CLOSE}`);\n hits.push({ type: s.rule.type, rule: s.rule.id, sha8, start: s.start, length: s.end - s.start });\n cursor = s.end;\n }\n out.push(text.slice(cursor));\n return { text: out.join(''), hits };\n}\n\n/** Just the text, for the many callers that do not care what was masked. */\nexport function redactText(text: string): string {\n return redact(text).text;\n}\n\n/**\n * Redact one exchange in place of its unredacted self: `user_text`,\n * `assistant_text` and every `tool_calls.input` / `.result` (`03` \u00A75). Returns\n * a new object; the input is never mutated, because the caller may still be\n * writing the byte-exact archive copy from it.\n */\nexport function redactExchange(ex: Exchange): { exchange: Exchange; hits: RedactionHit[] } {\n const hits: RedactionHit[] = [];\n const one = (s: string): string => {\n const r = redact(s);\n if (r.hits.length) hits.push(...r.hits);\n return r.text;\n };\n\n const userText = one(ex.userText);\n const assistantText = one(ex.assistantText);\n const toolCalls: ExchangeToolCall[] = ex.toolCalls.map((tc) => {\n const next: ExchangeToolCall = { ...tc, input: one(tc.input) };\n if (tc.result !== undefined) next.result = one(tc.result);\n return next;\n });\n\n return {\n exchange: { ...ex, userText, assistantText, toolCalls, redacted: ex.redacted || hits.length > 0 },\n hits,\n };\n}\n\n// ---------------------------------------------------------------- counting\n\nexport function emptyCounts(): RedactionCounts {\n const byType = {} as Record<SecretType, number>;\n for (const t of SECRET_TYPES) byType[t] = 0;\n return { total: 0, byType };\n}\n\n/** Tally hits by type. `doctor` sums these across the whole index. */\nexport function tally(hits: Iterable<RedactionHit>, into: RedactionCounts = emptyCounts()): RedactionCounts {\n for (const h of hits) {\n into.byType[h.type] = (into.byType[h.type] ?? 0) + 1;\n into.total++;\n }\n return into;\n}\n\nexport function addCounts(a: RedactionCounts, b: RedactionCounts): RedactionCounts {\n const out = emptyCounts();\n out.total = a.total + b.total;\n for (const t of SECRET_TYPES) out.byType[t] = (a.byType[t] ?? 0) + (b.byType[t] ?? 0);\n return out;\n}\n\n/** `{ total, aws: 3, jwt: 12 }` \u2014 the `--json` shape for `doctor`. */\nexport function countsJson(c: RedactionCounts): Record<string, number> {\n const out: Record<string, number> = { total: c.total };\n for (const t of SECRET_TYPES) if ((c.byType[t] ?? 0) > 0) out[t] = c.byType[t] ?? 0;\n return out;\n}\n\n/**\n * The `doctor` line (`03` \u00A75: \"`potsherd doctor` reports redaction counts by\n * type\"), in the house style of `render.ts`:\n *\n * secrets masked 1,284 jwt 512 \u00B7 entropy 402 \u00B7 aws 12\n *\n * The note lists types by count, biggest first, and elides rather than wraps \u2014\n * `render.ts` never wraps a row. Wired into the doctor card by T1.5; kept here\n * so the counts and the way they are said stay in one file.\n */\nexport function redactionRow(\n c: RedactionCounts,\n t: Theme = new Theme(),\n noteWidth = 43,\n): Row {\n const parts = SECRET_TYPES.filter((k) => (c.byType[k] ?? 0) > 0)\n .sort((a, b) => (c.byType[b] ?? 0) - (c.byType[a] ?? 0))\n .map((k) => `${k} ${fmt.num(c.byType[k] ?? 0)}`);\n const note = parts.length === 0\n ? 'nothing matched \u2014 index holds no secrets'\n : fmt.joinFit(parts, noteWidth, ` ${t.mid} `, t.ellip);\n return {\n label: 'secrets masked',\n value: fmt.num(c.total),\n note,\n tone: c.total > 0 ? 'ok' : 'dim',\n };\n}\n\n/** One plain line, for logs and for `doctor` in a non-card context. */\nexport function redactionLine(c: RedactionCounts, t: Theme = new Theme()): string {\n const row = redactionRow(c, t, Math.max(20, t.width - 30));\n return `${row.label.padEnd(22)}${(row.value ?? '').padStart(7)} ${t.dim(row.note ?? '')}`;\n}\n\n// ---------------------------------------------------------------- internals\n\nfunction protectedSpans(text: string): Span[] {\n const spans: Span[] = [];\n const patterns = [MASK_RE, ...ALLOW_SPANS];\n for (const p of patterns) {\n const rx = new RegExp(p.source, p.flags.includes('g') ? p.flags : p.flags + 'g');\n let m: RegExpExecArray | null;\n while ((m = rx.exec(text)) !== null) {\n if (m[0].length === 0) { rx.lastIndex++; continue; }\n spans.push({ start: m.index, end: m.index + m[0].length });\n }\n }\n return spans;\n}\n\nfunction isClaimed(claimed: Uint8Array, s: Span): boolean {\n for (let i = s.start; i < s.end; i++) if (claimed[i]) return true;\n return false;\n}\n\nfunction claim(claimed: Uint8Array, s: Span): void {\n const end = Math.min(s.end, claimed.length);\n for (let i = Math.max(0, s.start); i < end; i++) claimed[i] = 1;\n}\n\n/** Legacy projections have no immutable text-hash contract; mask on public read.\n * Canonical evidence/notes must instead refuse an unsafe old immutable value.\n */\nexport function redactLegacyProjection<T>(value:T):T{\n const visit=(input:unknown):{value:unknown;changed:boolean}=>{\n if(typeof input==='string'){const clean=redact(input).text;return {value:clean,changed:clean!==input};}\n if(Array.isArray(input)){const items=input.map(visit);return {value:items.map(item=>item.value),changed:items.some(item=>item.changed)};}\n if(input&&typeof input==='object'&&Object.getPrototypeOf(input)===Object.prototype){const output:Record<string,unknown>={};let changed=false;for(const [key,child] of Object.entries(input)){const result=visit(child);output[key]=result.value;changed ||= result.changed;}if(changed){if('redacted' in output)output.redacted=true;if('text' in output&&'match' in output)output.match=null;output.redactionRefreshed=true;}return {value:output,changed};}\n return {value:input,changed:false};\n };return visit(value).value as T;\n}\n", "import process from 'node:process';\n\n/**\n * The terminal design system, in code (plans/05-SHAREABLE-EXPERIENCE.md).\n *\n * Rules this module enforces so no verb can break them:\n * - never more than three colours on screen: accent, warn, ok. Everything\n * else is `dim` or default.\n * - NO_COLOR and --no-color are honoured; a non-TTY stdout is never coloured.\n * - --ascii replaces every non-ASCII glyph, so the output survives a terminal\n * with no unicode font and a screenshot taken on Windows. Two mechanisms,\n * because one was demonstrably forgettable: {@link Theme.g} picks the\n * fallback where the glyph is chosen, and {@link Theme.asciiLine} folds\n * whatever still got through \u2014 an em dash typed into a note, a `\u00B7` an\n * adapter wrote, an emoji in someone's own prompt. `render.ts` runs every\n * line it emits through the fold, so no verb can opt out by forgetting.\n */\n\nexport interface ThemeOptions {\n color?: boolean;\n ascii?: boolean;\n width?: number;\n}\n\nconst CSI = '\\u001b[';\n\nexport class Theme {\n readonly color: boolean;\n readonly ascii: boolean;\n readonly width: number;\n\n constructor(opts: ThemeOptions = {}) {\n this.color = opts.color ?? detectColor();\n this.ascii = opts.ascii ?? detectAscii();\n this.width = opts.width ?? detectWidth();\n }\n\n private wrap(code: string, s: string): string {\n return this.color ? `${CSI}${code}m${s}${CSI}0m` : s;\n }\n\n /** The single most important number on the screen. Exactly one per card. */\n accent(s: string): string { return this.wrap('38;5;209', s); }\n /** Urgency: something is about to be lost. */\n warn(s: string): string { return this.wrap('38;5;214', s); }\n /** Done, saved, safe. */\n ok(s: string): string { return this.wrap('38;5;71', s); }\n /** Secondary text: units, notes, sources. */\n dim(s: string): string { return this.wrap('2', s); }\n bold(s: string): string { return this.wrap('1', s); }\n\n /** Pick a glyph, with an ASCII fallback for --ascii / non-unicode terminals. */\n g(unicode: string, fallback: string): string {\n return this.ascii ? fallback : unicode;\n }\n\n get arrow(): string { return this.g('\u2192', '->'); }\n get mid(): string { return this.g('\u00B7', '.'); }\n get sep(): string { return this.g('\u00B7', '|'); }\n get ellip(): string { return this.g('\u2026', '...'); }\n get star(): string { return this.g('\u2605', '*'); }\n get le(): string { return this.g('\u2264', '<='); }\n get bullet(): string { return this.g('\u2022', '-'); }\n /** The em dash that stands in for \"no number here\". */\n get dash(): string { return this.g('\u2014', '-'); }\n\n /**\n * Fold one rendered line to pure ASCII, when `--ascii` is on.\n *\n * Applied at the very end, after the line has been fitted to the terminal\n * width, so **every substitution is exactly one character wide or narrower**.\n * That is the whole design constraint: `\u2026` \u2192 `...` here would push a line\n * that just fitted 80 columns to 82. The three-character ellipsis comes from\n * {@link ellip}, which the width arithmetic sees; this is only the net.\n */\n asciiLine(s: string): string {\n return this.ascii ? toAscii(s) : s;\n }\n\n /** Visible width, ignoring ANSI escapes. */\n static len(s: string): number {\n return stripAnsi(s).length;\n }\n}\n\n/**\n * The glyphs the design system uses, and their one-character ASCII stand-ins.\n * Anything not listed is decomposed (`\u00E9` \u2192 `e`) and, failing that, becomes a\n * single `?` \u2014 never silently dropped, because a vanished character is a\n * vanished fact.\n */\nconst ASCII_FOLD: Record<string, string> = {\n '\u2026': '.', '\u00B7': '.', '\u2022': '*', '\u2027': '.', '\u2219': '.',\n '\u2014': '-', '\u2013': '-', '\u2012': '-', '\u2212': '-', '\u2015': '-',\n '\u2192': '>', '\u21D2': '>', '\u2190': '<', '\u21D0': '<', '\u2191': '^', '\u2193': 'v',\n '\u2264': '<', '\u2265': '>', '\u2260': '!', '\u00B1': '~', '\u00D7': 'x', '\u00F7': '/',\n '\u2605': '*', '\u2606': '*', '\u2713': 'v', '\u2714': 'v', '\u2717': 'x', '\u2718': 'x',\n '\u2039': '<', '\u203A': '>', '\u00AB': '<', '\u00BB': '>',\n '\u201C': '\"', '\u201D': '\"', '\u201E': '\"', '\u2018': \"'\", '\u2019': \"'\", '\u201A': \"'\",\n '\u2588': '#', '\u2587': '#', '\u2593': '#', '\u2592': '+', '\u2591': '.', '\u25A0': '#', '\u25A1': '.',\n '\u2502': '|', '\u2500': '-', '\u250C': '+', '\u2510': '+', '\u2514': '+', '\u2518': '+', '\u251C': '+',\n '\u2524': '+', '\u252C': '+', '\u2534': '+', '\u253C': '+', '\u00B0': 'o', '\u00A7': 'S', '\u00B6': 'P',\n '\\u00a0': ' ', '\\u2007': ' ', '\\u2009': ' ', '\\u202f': ' ', '\\ufeff': '',\n};\n\n/**\n * Every non-ASCII code point replaced by one that is, without ever making the\n * string longer (measured in characters, which is what a terminal column is).\n * Surrogate pairs count as the one character they display as.\n */\nexport function toAscii(s: string): string {\n // eslint-disable-next-line no-control-regex\n if (!/[^\\x00-\\x7F]/.test(s)) return s;\n let out = '';\n for (const ch of s) {\n if (ch.charCodeAt(0) < 128 && ch.length === 1) { out += ch; continue; }\n const mapped = ASCII_FOLD[ch];\n if (mapped !== undefined) { out += mapped; continue; }\n // `\u00E9` is `e` plus a combining acute; drop the accent and keep the letter.\n const bare = ch.normalize('NFD').replace(/[\\u0300-\\u036f]/g, '');\n // eslint-disable-next-line no-control-regex\n out += bare.length === 1 && !/[^\\x00-\\x7F]/.test(bare) ? bare : '?';\n }\n return out;\n}\n\nconst ANSI_RE = new RegExp('\\\\u001b\\\\[[0-9;]*m', 'g');\n\nexport function stripAnsi(s: string): string {\n return s.replace(ANSI_RE, '');\n}\n\nfunction detectColor(): boolean {\n if (process.env.NO_COLOR !== undefined && process.env.NO_COLOR !== '') return false;\n if (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== '0') return true;\n if (process.env.TERM === 'dumb') return false;\n return Boolean(process.stdout.isTTY);\n}\n\nfunction detectAscii(): boolean {\n if (process.env.POTSHERD_ASCII === '1') return true;\n const enc = `${process.env.LC_ALL ?? process.env.LC_CTYPE ?? process.env.LANG ?? ''}`;\n if (enc && !/utf-?8/i.test(enc)) return true;\n return false;\n}\n\nfunction detectWidth(): number {\n const cols = process.stdout.columns;\n if (!cols || cols < 40) return 80;\n return Math.min(cols, 100);\n}\n", "/**\n * L2 redaction \u2014 the rule table.\n *\n * ## Provenance (read this before adding a rule)\n *\n * `plans/03-ARCHITECTURE.md` \u00A75 and the reuse table in\n * `plans/02-STRATEGY-AND-VIRALITY.md` are explicit: **port rules, do not write\n * regexes from memory**. Every pattern below is a port of a published,\n * MIT-licensed detection rule, and each rule names its upstream rule id in\n * `source` and in the comment above it.\n *\n * Upstreams:\n *\n * gitleaks \u2014 https://github.com/gitleaks/gitleaks\n * rule pack: `cmd/generate/config/rules/*.go`, published as\n * `config/gitleaks.toml`.\n * Copyright (c) 2019 Zachary Rice. Licensed under the MIT License.\n *\n * secretlint \u2014 https://github.com/secretlint/secretlint\n * rule pack: `@secretlint/secretlint-rule-preset-recommend`, which bundles\n * `-rule-aws`, `-rule-gcp`, `-rule-github`, `-rule-slack`, `-rule-npm`,\n * `-rule-privatekey`, `-rule-basicauth`, `-rule-sendgrid`, `-rule-shopify`.\n * Copyright (c) 2020 Secretlint. Licensed under the MIT License.\n *\n * Neither project's *runtime* is a dependency. secretlint's engine is async,\n * plugin-loading and file-oriented; this module has to be pure, synchronous and\n * cheap enough to run over the whole corpus during `potsherd index`. What is\n * taken is the rule knowledge \u2014 the part that must not be invented. See\n * the header of every rule below. The `NOTICE` entry for both packs lands with\n * T1.5, when this module is first wired into the index.\n *\n * ## What a rule is\n *\n * A scanner over a string returning the spans to mask, a `type` that appears in\n * the mask, and a stable `id`. Rules are applied in array order and the first\n * rule to claim a span wins (see `redact.ts`), so the order here is the\n * precedence order: key material, vendor tokens, credentials in urls, the\n * generic `KEY=` assignment, entropy last.\n *\n * `type` deliberately names a *family* (`aws`, `github`) and not the rule\n * (`aws-access-key-id`): it is user-visible inside the mask, so it stays short,\n * stable and legible in a search result. The rule id rides along on the hit,\n * for `doctor` and for debugging a false positive.\n */\n\n/** The family name that appears in `\u2039redacted:<type>:<sha8>\u203A`. */\nexport type SecretType =\n | 'aws'\n | 'gcp'\n | 'github'\n | 'slack'\n | 'stripe'\n | 'openai'\n | 'anthropic'\n | 'npm'\n | 'jwt'\n | 'private-key'\n | 'basic-auth'\n | 'generic'\n | 'entropy';\n\n/** Every type, in the order `doctor` reports them. */\nexport const SECRET_TYPES: readonly SecretType[] = [\n 'aws',\n 'gcp',\n 'github',\n 'slack',\n 'stripe',\n 'openai',\n 'anthropic',\n 'npm',\n 'jwt',\n 'private-key',\n 'basic-auth',\n 'generic',\n 'entropy',\n] as const;\n\n/** One span of text a rule wants masked. */\nexport interface RuleMatch {\n start: number;\n end: number;\n value: string;\n}\n\nexport interface Rule {\n /** Stable id, reported on the hit and used in tests. */\n id: string;\n type: SecretType;\n /** Upstream rule this was ported from, with its licence. */\n source: string;\n /** Pure: same input, same output, no state carried between calls. */\n scan(text: string): RuleMatch[];\n}\n\n// ---------------------------------------------------------------- entropy\n\n/**\n * Shannon entropy in bits per character. `03` \u00A75 fixes the threshold at 4.5\n * over tokens of \u2265 20 characters.\n *\n * Worth knowing before either number is tuned: entropy measured on a sample of\n * length n cannot exceed log2(n), so a 20-character token can never reach 4.5\n * (log2(20) = 4.32) and nothing shorter than 23 characters can trip this rule\n * at all. That is why 40-char hex git shas (alphabet cap 4.0) and uuids pass\n * through untouched \u2014 the threshold in `03` is self-limiting by design.\n */\nexport function shannonEntropy(s: string): number {\n const n = s.length;\n if (n === 0) return 0;\n const counts = new Map<string, number>();\n for (const ch of s) counts.set(ch, (counts.get(ch) ?? 0) + 1);\n let h = 0;\n for (const c of counts.values()) {\n const p = c / n;\n h -= p * Math.log2(p);\n }\n return h;\n}\n\nexport const ENTROPY_MIN_LENGTH = 20;\nexport const ENTROPY_THRESHOLD = 4.5;\n\n// ---------------------------------------------------------------- allowlists\n\n/**\n * Spans that are never a secret and must not be scanned at all. Ported in\n * spirit from gitleaks' `[[rules.allowlist]] regexes` and secretlint's `allows`\n * option (both MIT).\n *\n * A base64 data URI is the worst false positive in a coding transcript: an\n * inlined PNG is tens of kilobytes of maximum-entropy base64 that no rule could\n * tell from a key. It is excluded by construction rather than by lowering a\n * threshold that would then start missing real keys.\n */\nexport const ALLOW_SPANS: RegExp[] = [\n // data: URIs \u2014 `data:image/png;base64,iVBORw0KGgo\u2026`\n /\\bdata:[a-zA-Z0-9!#$&^_.+-]*(?:\\/[a-zA-Z0-9!#$&^_.+-]*)?(?:;[a-zA-Z0-9-]+=[^;,\\s]*)*;base64,[A-Za-z0-9+/=]{20,}(?:\\s+[A-Za-z0-9+/=]{20,})*/g,\n // Subresource Integrity \u2014 `integrity=\"sha384-oqVuAfXRKap7fdgcCY5uykM6+R9\u2026\"`\n /\\bsha(?:256|384|512)-[A-Za-z0-9+/]{20,}={0,3}/g,\n];\n\nconst UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;\nconst HEX_RE = /^[0-9a-fA-F]+$/;\nconst NUMERIC_RE = /^[0-9_+-]+$/;\n\n// -------------------------------------------------- entropy-rule exclusions\n//\n// Each of these answers a false-positive class *measured* on the reference\n// corpus by `scripts/redaction-benchmark.mjs`, and each is named and commented\n// so a future reader can audit \u2014 or delete \u2014 one of them on its own merits.\n//\n// The load-bearing property of all of them: they apply **only to a bare token\n// the entropy rule found lying in the text with no context**. A value that is\n// assigned to a secret-named key, sent in an `Authorization` header or put in\n// a url's userinfo is claimed by a rule that runs earlier and never consults\n// this list. So \"exclude uuids\" cannot let `API_KEY=<uuid>` through, and\n// \"exclude `toolu_\u2026`\" cannot let `ANTHROPIC_API_KEY=toolu_\u2026` through. Both are\n// planted cases in `tests/fixtures/secrets/planted.jsonl`.\n\n/**\n * **E1 \u2014 minted object ids.** Model apis and agent harnesses stamp every\n * message, request, tool use and run with a random id under a fixed prefix.\n * They are the densest high-entropy shape in an agent transcript by an order\n * of magnitude \u2014 `toolu_014ZfaccgxvcYNpcTLq8qvkR` and its kin \u2014 and none of\n * them is a credential: they are handed out in api *responses*, they appear in\n * public logs, and they authorise nothing.\n *\n * Prefixes, and who mints them:\n * toolu_ srvtoolu_ msg_ req_ anthropic messages api\n * chatcmpl- fc_ call_ run_ step_\n * thread_ asst_ resp_ evt_ openai\n * exec- exec_ agent- sess_ session_ agent harnesses (claude code, codex)\n * task_ job_ turn_ conv_ snapshot- harness bookkeeping\n *\n * Deliberately *not* here: `sk_`, `rk_`, `ghp_`, `xoxb-`, `npm_`, `AKIA`,\n * `sk-ant-`, `tvly-` \u2014 every one of those prefixes means the opposite.\n */\nconst MINTED_ID_RE =\n /^(?:toolu|srvtoolu|msg|req|resp|call|fc|run|step|thread|asst|evt|sess|session|exec|job|task|turn|conv|agent|snapshot|chatcmpl)[-_]/i;\n\n/**\n * **E5 \u2014 ULIDs.** 26 characters of Crockford base32, the first 10 encoding a\n * millisecond timestamp. log2(26) = 4.70, so a ULID can and does clear the 4.5\n * bar. Sortable ids, not secrets; the alphabet excludes I, L, O and U on\n * purpose, which is what makes the shape recognisable.\n */\nconst ULID_RE = /^[0-7][0-9ABCDEFGHJKMNPQRSTVWXYZ]{25}$/;\n\n/**\n * **E6 \u2014 prose wearing a token's clothes.** The single largest surviving false\n * positive on the reference corpus once images were elided: url slugs.\n *\n * \u2026%2Fy-combinator_reducto-has-just-raised-a-75m-series-b-activity-7383909473913880576-ba7M\n *\n * That is 87 characters over a wide alphabet, so its Shannon entropy is 4.9 \u2014\n * comfortably \"random\" by the measure in `03` \u00A75 \u2014 and it is a LinkedIn\n * permalink. Shannon entropy on a single short sample cannot tell *random*\n * from *varied*, and English is very varied.\n *\n * What separates them is **runs**. A base64 or base62 credential draws each\n * character independently, so a run of 7 consecutive same-case letters has\n * probability \u2248 (26/64)^7 \u2248 1 in 900 per position; two disjoint such runs in\n * one 40-character token is well under 1%. English words are nothing but such\n * runs \u2014 `combinator`, `activity`, `whatsapp`, `killmonger`.\n *\n * So: two runs of \u2265 7 same-case letters, or one run of \u2265 12, and the token is\n * text. Both numbers were checked against the corpus rather than chosen: at\n * \u2265 7/\u00D72 every tavily key, every planted secret and every base64 fixture value\n * survives (their longest same-case run is 4), and the slug class disappears.\n */\nconst SAMECASE_RUN_MIN = 7;\nconst SAMECASE_RUNS_NEEDED = 2;\nconst SAMECASE_RUN_ALONE = 12;\n\nfunction looksLikeProse(token: string): boolean {\n let long = 0;\n for (const run of token.match(/[a-z]+|[A-Z]+/g) ?? []) {\n if (run.length >= SAMECASE_RUN_ALONE) return true;\n if (run.length >= SAMECASE_RUN_MIN) long += 1;\n }\n return long >= SAMECASE_RUNS_NEEDED;\n}\n\n/**\n * **E7 \u2014 alphabet constants.** `0123456789ABCDEFGHJKMNPQRSTVWXYZ` is the\n * Crockford base32 table; every base32/base58/base62 encoder in every codebase\n * declares one, and with no repeated character it scores a perfect log2(n)\n * entropy \u2014 the highest possible.\n *\n * The test is exactly \"no character occurs twice\". Drawing 24 characters from\n * a 62-character alphabet without a single repeat happens with probability\n * \u2248 1%, and from base64url's 64 with probability \u2248 1.4%; combined with the\n * 24-character floor and with the fact that a credential in any real context\n * is claimed by an earlier rule, this is a safe trade for a class that is\n * otherwise permanently unmaskable-looking.\n */\nfunction looksLikeAlphabetConstant(token: string): boolean {\n return token.length >= 24 && new Set(token).size === token.length;\n}\n\n/**\n * Placeholder values. Ported from gitleaks' `stopwords` list and secretlint's\n * dummy-value handling, trimmed to the ones that actually occur in coding\n * transcripts. A value matching this is documentation, not a credential.\n */\nconst PLACEHOLDER_RE =\n /(?:^|[^a-z0-9])(?:x{3,}|your|my[-_]?(?:key|token|secret|pass)|example|sample|dummy|fake|mock|placeholder|change[-_]?(?:me|it|this)|replace[-_]?me|insert|todo|fixme|redacted|hidden|omitted|elided|not[-_]?real|no[-_]?such|hunter2|password|passwd|secret|token|apikey|api[-_]key|abcdef|123456|s3cret|letmein|foobar|lorem|ipsum)(?:$|[^a-z0-9])/i;\n\n/** `${VAR}`, `{{var}}`, `%(var)s`, `<%= x %>`, `<your-key>`, `$VAR` \u2026 */\nconst INTERPOLATION_RE = /\\$\\{|\\{\\{|%\\(|<%|^\\$[A-Za-z_(]|^<|^\\{|\\}$|^%[A-Za-z(]/;\n\n/** A reference to a value rather than the value: `process.env.X`, `cfg.token`. */\nconst REFERENCE_RE =\n /^(?:process\\.env|import\\.meta\\.env|os\\.environ|System\\.getenv|Deno\\.env|env|ENV|config|conf|cfg|settings|opts|options|args|argv|params|props|state|data|input|payload|body|req|res|ctx|context|self|this|that|obj|row|item|user|account|creds|credentials|secrets|vault|store|keychain)\\b\\s*[.[(]/;\n\n/** A bare word with no digits \u2014 `hashedPassword`, `MY_SECRET`, `undefined`. */\nconst WORDY_IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z_$]*$/;\n\n/**\n * A dotted identifier chain \u2014 `crypto.randomBytes`, `formData.password`,\n * `settings.auth.token`. Code reaching for a value, not the value. Base64,\n * base64url and hex credentials never contain a `.`, and the ones that do\n * (jwts) have a rule of their own that runs first.\n */\nconst DOTTED_CHAIN_RE = /^[A-Za-z_$][A-Za-z0-9_$]*(?:\\.[A-Za-z_$][A-Za-z0-9_$]*)+$/;\n\n/** A url or a filesystem path, not a credential. */\nconst PATHISH_RE = /^(?:[a-z][a-z0-9+.-]*:\\/\\/|\\.{0,2}\\/|~\\/|[A-Za-z]:\\\\)/;\n\n/** Syntax that means the capture ran into code, not into a credential. */\nconst CODEY_RE = /[<>{}[\\]()|\\\\;,&?!*\"'`]/;\n\n/** The mask itself, so a second pass recognises its own output. */\nexport const MASK_FRAGMENT_RE = /\u2039redacted:/;\n\n/** English words ending in a keyword that never name a secret. */\nconst NAME_STOPWORDS = new Set([\n 'monkey',\n 'donkey',\n 'turkey',\n 'whiskey',\n 'jockey',\n 'hockey',\n 'lackey',\n 'mickey',\n 'malarkey',\n 'hotkey',\n 'sortkey',\n 'oauth',\n]);\n\n/**\n * Does this identifier name a secret? `apiKey`, `AWS_SECRET_ACCESS_KEY`,\n * `db.password`, `client_secret` yes; `tokenizer`, `keyboard`, `monkey` no.\n *\n * Ported from the keyword half of gitleaks' `generic-api-key` and tightened:\n * that rule allows the keyword anywhere inside a 50-character identifier, which\n * flags every `tokenizer:` and `keyboardShortcut:` line in a transcript. Here\n * the keyword has to be the identifier's last segment, splitting on `_ - . $`\n * and on camelCase humps.\n */\nexport function nameLooksLikeSecret(name: string): boolean {\n const segments = name\n .split(/[^A-Za-z0-9]+/)\n .flatMap((part) => part.split(/(?<=[a-z0-9])(?=[A-Z])/))\n .filter(Boolean);\n const last = segments[segments.length - 1];\n if (!last) return false;\n const lower = last.toLowerCase();\n if (NAME_STOPWORDS.has(lower)) return false;\n return /(?:key|token|secret|password|passwd|pwd|credential|auth)s?$/.test(lower);\n}\n\n/**\n * Is this assignment's value plausibly a real credential?\n *\n * The generic `KEY=` rule is where false positives come from, because every\n * codebase assigns to something called `token`. Everything rejected here is\n * something transcripts are full of: env lookups, template interpolations,\n * references to another variable, documentation placeholders, type\n * annotations, version numbers, paths.\n */\nexport function valueLooksLikeSecret(value: string): boolean {\n const v = value.trim();\n if (v.length < 10 || v.length > 200) return false;\n if (/\\s/.test(v)) return false;\n if (MASK_FRAGMENT_RE.test(v)) return false;\n if (CODEY_RE.test(v)) return false;\n if (INTERPOLATION_RE.test(v)) return false;\n if (REFERENCE_RE.test(v)) return false;\n if (DOTTED_CHAIN_RE.test(v)) return false;\n if (PATHISH_RE.test(v)) return false;\n if (NUMERIC_RE.test(v)) return false;\n if (PLACEHOLDER_RE.test(v)) return false;\n // A bare word with no digits is a variable name or a sentence fragment, not\n // a 32-character credential.\n if (WORDY_IDENTIFIER_RE.test(v) && shannonEntropy(v) < 4.0) return false;\n // Filler: `****`, `------`, `0000000000`.\n if (new Set(v).size <= 2) return false;\n // gitleaks' generic-api-key requires 3.5 bits on the captured value. 3.2\n // here: potsherd masks a token in an index rather than failing a build, so a\n // marginal hit costs one unreadable word in a search result and a marginal\n // miss costs a leaked credential.\n if (shannonEntropy(v) < 3.2) return false;\n return true;\n}\n\n/**\n * Entropy-rule allowlist: shapes that are high-entropy but never secret.\n *\n * Every `return false` is an exclusion rule with a letter, defined above with\n * the class it was measured against. Read the block comment there before\n * adding, removing or relaxing one.\n *\n * This is consulted **only** by the bare-token entropy rule. It is not part of\n * `valueLooksLikeSecret`, so nothing here can excuse a value that appears in a\n * credential-shaped position.\n */\nexport function entropyCandidateAllowed(token: string): boolean {\n if (token.length < ENTROPY_MIN_LENGTH) return false;\n // E2 \u2014 uuids. Session ids, request ids, trace ids. Capped at 4.0 bits by\n // their alphabet anyway; excluded explicitly so the intent is readable.\n if (UUID_RE.test(token)) return false;\n // E2b \u2014 a uuid under a prefix: `exec-<uuid>`, `agent-<uuid>`, `snapshot-\u2026`.\n if (UUID_RE.test(token.replace(/^[A-Za-z][A-Za-z0-9]{0,15}[-_]/, ''))) return false;\n // E3 \u2014 hex digests: git sha1s, sha256 sums, md5s, content hashes.\n if (HEX_RE.test(token)) return false;\n // E4 \u2014 a hex digest under a prefix (`sha256-\u2026` without the SRI base64 body,\n // `blake3-\u2026`, `md5-\u2026`). The base64 SRI form is an ALLOW_SPAN, above.\n if (/^(?:sha1|sha256|sha384|sha512|md5|blake3|crc32|xxh64)[-_:]/i.test(token)) return false;\n if (NUMERIC_RE.test(token)) return false;\n if (MASK_FRAGMENT_RE.test(token)) return false;\n if (PLACEHOLDER_RE.test(token)) return false;\n if (MINTED_ID_RE.test(token)) return false; // E1\n if (ULID_RE.test(token)) return false; // E5\n if (looksLikeProse(token)) return false; // E6\n if (looksLikeAlphabetConstant(token)) return false; // E7\n return true;\n}\n\n// ---------------------------------------------------------------- machinery\n\ninterface RegexRuleOptions {\n /** Capture group holding the bytes to mask. 0 = the whole match. */\n group?: number;\n /** Reject a candidate; returning false leaves the text untouched. */\n validate?: (value: string, m: RegExpExecArray) => boolean;\n}\n\n/** Build a scanning rule from one global regex. */\nfunction regexRule(\n id: string,\n type: SecretType,\n source: string,\n re: RegExp,\n opts: RegexRuleOptions = {},\n): Rule {\n const group = opts.group ?? 0;\n return {\n id,\n type,\n source,\n scan(text: string): RuleMatch[] {\n const out: RuleMatch[] = [];\n // A fresh regex per scan: a module-level `lastIndex` is shared state and\n // this module promises purity.\n const rx = new RegExp(re.source, re.flags.includes('g') ? re.flags : re.flags + 'g');\n let m: RegExpExecArray | null;\n while ((m = rx.exec(text)) !== null) {\n if (m[0].length === 0) { rx.lastIndex++; continue; }\n const value = group === 0 ? m[0] : m[group];\n if (value === undefined || value.length === 0) continue;\n if (opts.validate && !opts.validate(value, m)) continue;\n const offset = group === 0 ? 0 : m[0].indexOf(value);\n if (offset < 0) continue;\n out.push({ start: m.index + offset, end: m.index + offset + value.length, value });\n }\n return out;\n },\n };\n}\n\n// ---------------------------------------------------------------- the rules\n\n/**\n * The generic `(KEY|TOKEN|SECRET|PASSWORD)\\s*[=:]` rule from `03` \u00A75, given the\n * shape of gitleaks' `generic-api-key`.\n *\n * It is written as a scanner rather than one regex for two reasons. Speed: a\n * pattern of the form `identifier{0,48}(keyword)[:=]` has to be retried at\n * every letter of a 10 MB corpus, while anchoring on the literal keyword lets\n * the engine skip. Correctness: only the *value* is masked, never the name, so\n * `TOKEN=\u2039redacted:generic:\u2026\u203A` still tells a reader which variable leaked.\n */\nconst GENERIC_ANCHOR =\n /(?:key|token|secret|password|passwd|pwd|credential|auth)s?[\"']?[ \\t]*(?::=|=>|[:=])/gi;\n\nconst GENERIC_VALUE =\n // The bare alternative stops at a backslash on purpose: transcripts carry\n // json-escaped text, where `TOKEN=abc\\nNEXT=\u2026` is one line and the `\\n` is\n // two literal characters. Without this the capture swallows the rest of the\n // record and the value is thrown out as code.\n /[ \\t]*(?:\"([^\"\\r\\n]{10,200})\"|'([^'\\r\\n]{10,200})'|`([^`\\r\\n]{10,200})`|([^\\s\"'`,;)\\](}>\\\\]{10,200}))/y;\n\nconst NAME_CHAR_RE = /[A-Za-z0-9_$.-]/;\n\nconst genericAssignmentRule: Rule = {\n id: 'generic-assignment',\n type: 'generic',\n source: '03 \u00A75, given the shape of gitleaks generic-api-key (MIT)',\n scan(text: string): RuleMatch[] {\n const out: RuleMatch[] = [];\n const anchor = new RegExp(GENERIC_ANCHOR.source, GENERIC_ANCHOR.flags);\n let m: RegExpExecArray | null;\n while ((m = anchor.exec(text)) !== null) {\n const opEnd = m.index + m[0].length;\n const opLen = /(?::=|=>)$/.test(m[0]) ? 2 : 1;\n const before = text[opEnd - opLen - 1] ?? '';\n // `==`, `===`, `!=`, `<=`, `>=`, `+=` are comparisons and compound\n // assignment, not `TOKEN=\u2026`.\n if (text[opEnd] === '=' || (opLen === 1 && /[!<>+\\-*/%&|^=]/.test(before))) continue;\n\n // Walk back over the identifier the keyword ends: `AWS_SECRET_ACCESS_KEY`.\n const keywordEnd = m.index + m[0].replace(/[\"']?[ \\t]*(?::=|=>|[:=])$/, '').length;\n let nameStart = m.index;\n while (nameStart > 0 && NAME_CHAR_RE.test(text[nameStart - 1] ?? '')) nameStart--;\n const name = text.slice(nameStart, keywordEnd);\n if (!nameLooksLikeSecret(name)) continue;\n\n GENERIC_VALUE.lastIndex = opEnd;\n const v = GENERIC_VALUE.exec(text);\n if (!v) continue;\n const value = v[1] ?? v[2] ?? v[3] ?? v[4];\n if (value === undefined || !valueLooksLikeSecret(value)) continue;\n const start = v.index + v[0].lastIndexOf(value);\n out.push({ start, end: start + value.length, value });\n anchor.lastIndex = start + value.length;\n }\n return out;\n },\n};\n\n/** Explicit socket-auth command arguments are credential context, not hashes. */\nconst socketAuthRule:Rule={\n id:'socket-auth-argument',type:'generic',source:'03 \u00A75 credential context',\n scan(text:string):RuleMatch[]{const out:RuleMatch[]=[];const anchor=/(?:^|[\\s\"'`])--socket-auth[\"']?(?:[ \\t]*=[ \\t]*|[ \\t]+)/gi;let match:RegExpExecArray|null;\n while((match=anchor.exec(text))!==null){GENERIC_VALUE.lastIndex=anchor.lastIndex;const found=GENERIC_VALUE.exec(text);if(!found)continue;const value=found[1]??found[2]??found[3]??found[4];if(value===undefined||!valueLooksLikeSecret(value))continue;const start=found.index+found[0].lastIndexOf(value);out.push({start,end:start+value.length,value});anchor.lastIndex=start+value.length;}\n return out;}\n};\n\n/**\n * `Authorization: Bearer <token>` \u2014 a credential *context* rather than a\n * credential *shape*.\n *\n * The rule exists to make the entropy exclusions above safe to hold. They all\n * assume that a value in a position that shouts \"credential\" is claimed by an\n * earlier rule; `KEY=`, `?token=` and `https://user:pw@` were already covered,\n * and the http authorization header was the hole. It matters more now than it\n * did before T1.4b: a `Bearer toolu_\u2026`-shaped token is excluded by E1 as a\n * bare token, and must not be excluded here.\n *\n * The name is not masked, only the credential, so a redacted transcript still\n * says which scheme was used.\n */\nconst BEARER_RE =\n /(?:^|[^A-Za-z0-9_-])(?:Bearer|Token|ApiKey|Api-Key|Basic|DPoP)[ \\t]+([A-Za-z0-9+/._~-]{16,512}={0,2})/g;\n\nconst bearerRule: Rule = regexRule(\n 'authorization-bearer',\n 'generic',\n '03 \u00A75 (credential context), given the shape of gitleaks generic-api-key (MIT)',\n BEARER_RE,\n {\n group: 1,\n // Same value screen as the `KEY=` rule: `Bearer ${token}`, `Bearer <TOKEN>`\n // and `Bearer your-token-here` are documentation, not credentials.\n validate: (value) => valueLooksLikeSecret(value),\n },\n);\n\n/**\n * The entropy rule from `03` \u00A75 \u2014 \u2265 4.5 bits over tokens of \u2265 20 characters \u2014\n * written as a scanner rather than as one regex.\n *\n * `03` fixes the threshold and the length floor and both are honoured\n * unchanged. What T1.4b changed is **what counts as a token**, because that\n * was never specified and the naive reading was doing the damage:\n *\n * 1. `=` is base64 *padding*, so it is only a token character at the end.\n * Before, `ANTHROPIC_VERTEX_PROJECT_ID=example-project` was one\n * \"token\" \u2014 a variable glued to its value \u2014 and scored 4.7.\n * 2. `/` and `.` remain separators, so a long path or a dotted hostname is\n * not one token. (Unchanged from T1.4; the reasoning is that a real\n * base64 secret containing `/` still leaves a \u2265 23-character run, and 23\n * is the shortest string that can reach 4.5 bits at all.)\n * 3. Transcripts are json-inside-json, so the text often carries a *literal*\n * backslash-n where a newline belongs. `\\ntvly-dev-\u2026` used to yield the\n * token `ntvly-dev-\u2026` \u2014 the same secret with a different first character,\n * and therefore a different sha8 and a different mask. Escape residue is\n * trimmed, which is a correctness fix for the \"same secret, same mask\"\n * guarantee and not only a cosmetic one.\n * 4. Percent-escape residue (`%2F` leaving `2F\u2026`) and leading/trailing\n * separators are trimmed for the same reason.\n *\n * Shape exclusions live in {@link entropyCandidateAllowed}.\n */\nconst ENTROPY_TOKEN = /[A-Za-z0-9+_-]{20,}={0,2}/g;\n\n/** Characters a `\\x` json escape leaves glued to the front of a token. */\nconst ESCAPE_LETTERS = 'ntrbfv';\n\nconst highEntropyRule: Rule = {\n id: 'high-entropy-token',\n type: 'entropy',\n source: '03 \u00A75 (4.5 bits / 20 chars) + gitleaks entropy allowlists (MIT)',\n scan(text: string): RuleMatch[] {\n const out: RuleMatch[] = [];\n const rx = new RegExp(ENTROPY_TOKEN.source, ENTROPY_TOKEN.flags);\n let m: RegExpExecArray | null;\n while ((m = rx.exec(text)) !== null) {\n let start = m.index;\n let value = m[0];\n // `\\n`, `\\t`, `\\r` written literally by an outer json encoder.\n if (start > 0 && text[start - 1] === '\\\\' && ESCAPE_LETTERS.includes(value[0] ?? '')) {\n start += 1;\n value = value.slice(1);\n }\n // `%2F`, `%3D` \u2014 the `%` is a separator, but the two hex digits that\n // complete the escape are not part of the token that follows it.\n if (start > 0 && text[start - 1] === '%' && /^[0-9A-Fa-f]{2}/.test(value)) {\n start += 2;\n value = value.slice(2);\n }\n // Leading and trailing separators belong to the surrounding text.\n const lead = value.length - value.replace(/^[-_+]+/, '').length;\n start += lead;\n value = value.slice(lead).replace(/[-_+]+$/, '');\n\n if (value.length < ENTROPY_MIN_LENGTH) continue;\n if (!entropyCandidateAllowed(value)) continue;\n if (shannonEntropy(value) < ENTROPY_THRESHOLD) continue;\n out.push({ start, end: start + value.length, value });\n }\n return out;\n },\n};\n\nexport const RULES: Rule[] = [\n // ---- key material ------------------------------------------------------\n regexRule(\n // gitleaks `private-key`; secretlint `@secretlint/secretlint-rule-privatekey`.\n // The whole block is masked, header to footer: the base64 body alone would\n // otherwise be shredded into a dozen separate entropy hits.\n 'private-key-block',\n 'private-key',\n 'gitleaks private-key / secretlint-rule-privatekey (MIT)',\n /-----BEGIN[ A-Z0-9]{0,40}PRIVATE KEY(?: BLOCK)?-----[\\s\\S]{0,200000}?-----END[ A-Z0-9]{0,40}PRIVATE KEY(?: BLOCK)?-----/g,\n ),\n regexRule(\n // gitleaks `private-key`, unterminated variant: a transcript often quotes\n // the header and the first body lines and then elides the rest.\n //\n // The body is taken line by line and only where a line is \u2265 20 characters\n // of pure base64, so an unterminated header followed by prose masks the\n // header alone instead of swallowing the sentence after it. The separator\n // class carries the backslash so json-escaped `\\n` works, and it is\n // disjoint from the base64 class, which is what keeps this linear.\n 'private-key-header',\n 'private-key',\n 'gitleaks private-key (MIT)',\n /-----BEGIN[ A-Z0-9]{0,40}PRIVATE KEY(?: BLOCK)?-----(?:[\\r\\n \\t\\\\]+[A-Za-z0-9+/=]{20,})*/g,\n ),\n regexRule(\n // gitleaks `jwt`: three base64url segments, the first two starting `ey`\n // (the base64 of `{\"`). Session transcripts are full of these.\n 'jwt',\n 'jwt',\n 'gitleaks jwt (MIT)',\n /\\bey[A-Za-z0-9_-]{17,}\\.ey[A-Za-z0-9_-]{17,}\\.[A-Za-z0-9_-]{10,}={0,2}/g,\n ),\n\n // ---- vendor tokens -----------------------------------------------------\n regexRule(\n // gitleaks `aws-access-token`; secretlint-rule-aws `AWSAccessKeyID`.\n 'aws-access-key-id',\n 'aws',\n 'gitleaks aws-access-token / secretlint-rule-aws (MIT)',\n /\\b(?:A3T[A-Z0-9]|AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}\\b/g,\n ),\n regexRule(\n // secretlint-rule-aws `AWSSecretAccessKey`: a 40-character base64 value\n // bound to an AWS-flavoured name. Typed `aws` and placed before the generic\n // rule so `doctor` reports it as what it is.\n 'aws-secret-access-key',\n 'aws',\n 'secretlint-rule-aws AWSSecretAccessKey (MIT)',\n /aws[_.-]?(?:secret|access)[_.-]?(?:access[_.-]?)?key(?:[_.-]?id)?[\"'\\s]{0,4}[:=][\"'\\s]{0,4}([A-Za-z0-9/+=]{40})(?![A-Za-z0-9/+=])/gi,\n { group: 1 },\n ),\n regexRule(\n // gitleaks `gcp-api-key`; secretlint-rule-gcp `GCPApiKey`.\n 'gcp-api-key',\n 'gcp',\n 'gitleaks gcp-api-key / secretlint-rule-gcp (MIT)',\n /\\bAIza[0-9A-Za-z_-]{35}\\b/g,\n ),\n regexRule(\n // gitleaks `gcp-oauth-client-secret`.\n 'gcp-oauth-client-secret',\n 'gcp',\n 'gitleaks gcp-oauth-client-secret (MIT)',\n /\\bGOCSPX-[a-zA-Z0-9_-]{28}\\b/g,\n ),\n regexRule(\n // gitleaks `github-pat` / `-oauth` / `-app-token` / `-refresh-token`;\n // secretlint-rule-github. ghp_ user, gho_ oauth, ghu_/ghs_ app, ghr_ refresh.\n 'github-token',\n 'github',\n 'gitleaks github-pat / secretlint-rule-github (MIT)',\n /\\bgh[pousr]_[A-Za-z0-9]{36,255}\\b/g,\n ),\n regexRule(\n // gitleaks `github-fine-grained-pat`.\n 'github-fine-grained-pat',\n 'github',\n 'gitleaks github-fine-grained-pat (MIT)',\n /\\bgithub_pat_[0-9a-zA-Z_]{82}\\b/g,\n ),\n regexRule(\n // gitleaks `slack-bot-token` / `-user-token` / `-app-token`;\n // secretlint-rule-slack.\n 'slack-token',\n 'slack',\n 'gitleaks slack-bot-token / secretlint-rule-slack (MIT)',\n /\\bxox[abprs]-[0-9a-zA-Z-]{10,72}\\b/g,\n ),\n regexRule(\n // gitleaks `slack-webhook-url`; secretlint-rule-slack `SlackWebhook`.\n 'slack-webhook',\n 'slack',\n 'gitleaks slack-webhook-url (MIT)',\n /https:\\/\\/hooks\\.slack\\.com\\/(?:services|workflows|triggers)\\/[A-Za-z0-9+/]{6,}\\/[A-Za-z0-9+/]{6,}\\/[A-Za-z0-9+/]{6,}/g,\n ),\n regexRule(\n // gitleaks `stripe-access-token`. `pk_` publishable keys are public by\n // design and are deliberately not matched.\n 'stripe-key',\n 'stripe',\n 'gitleaks stripe-access-token (MIT)',\n /\\b(?:sk|rk)_(?:test|live|prod)_[A-Za-z0-9]{10,99}\\b/g,\n ),\n regexRule(\n // gitleaks `anthropic-api-key`. Must precede the openai rules: both start\n // `sk-`, and whichever rule claims the span first wins.\n 'anthropic-api-key',\n 'anthropic',\n 'gitleaks anthropic-api-key (MIT)',\n /\\bsk-ant-(?:api|admin)[0-9]{2}-[A-Za-z0-9_-]{80,120}\\b/g,\n ),\n regexRule(\n // gitleaks `openai-api-key`: the `T3BlbkFJ` infix is the base64 of\n // \"OpenAI\" and is what makes this rule safe to run over prose.\n 'openai-api-key-project',\n 'openai',\n 'gitleaks openai-api-key (MIT)',\n /\\bsk-(?:proj-|svcacct-|admin-)?[A-Za-z0-9_-]{20,}T3BlbkFJ[A-Za-z0-9_-]{20,}\\b/g,\n ),\n regexRule(\n // gitleaks `openai-api-key`, legacy 48-character form with no infix.\n 'openai-api-key-legacy',\n 'openai',\n 'gitleaks openai-api-key (MIT)',\n /\\bsk-[A-Za-z0-9]{48}\\b/g,\n ),\n regexRule(\n // gitleaks `npm-access-token`; secretlint-rule-npm.\n 'npm-access-token',\n 'npm',\n 'gitleaks npm-access-token / secretlint-rule-npm (MIT)',\n /\\bnpm_[A-Za-z0-9]{36}\\b/g,\n ),\n\n // ---- credentials in urls -----------------------------------------------\n regexRule(\n // gitleaks `authenticated-url`; secretlint `-rule-basicauth`. Only the\n // password is masked: `postgres://app:\u2039redacted:basic-auth:\u2026\u203A@db:5432/x`\n // still says which host and which user, which is the point of an index that\n // stays searchable by shape.\n 'basic-auth-url',\n 'basic-auth',\n 'gitleaks authenticated-url / secretlint-rule-basicauth (MIT)',\n // The user half is `{0,64}`: `redis://:hunter2@host` has an empty user and\n // is exactly as leaked as the two-part form.\n /\\b[a-zA-Z][a-zA-Z0-9+.-]{1,20}:\\/\\/[^\\s:@/]{0,64}:([^\\s:@/]{1,128})@/g,\n {\n group: 1,\n validate: (value) => {\n if (INTERPOLATION_RE.test(value)) return false;\n if (PLACEHOLDER_RE.test(value)) return false;\n if (MASK_FRAGMENT_RE.test(value)) return false;\n if (/^(?:pass|pw|user|admin|root|test|guest|\\*+|x+|\\d{1,4})$/i.test(value)) return false;\n return true;\n },\n },\n ),\n\n // ---- credential context (03 \u00A75) ----------------------------------------\n bearerRule,\n genericAssignmentRule,\n socketAuthRule,\n\n // ---- entropy (03 \u00A75) ---------------------------------------------------\n // Last, so that anything a named rule understands is reported under its real\n // type and a bare token is the fallback rather than the default.\n highEntropyRule,\n];\n", "/**\n * Binary elision \u2014 the pass that runs **before** the redactor.\n *\n * ## Why this exists\n *\n * T1.4 landed the redactor and `potsherd index --full` promptly reported\n * *165,088 secrets masked* over 1,406 exchanges. Measuring where they were\n * (`scripts/redaction-benchmark.mjs`) settled it: 98.6% were in `tool_calls`,\n * and the single worst tool call was a **589 KB base64 JPEG** pasted into a\n * `Read` result, which alone produced 5,660 entropy hits.\n *\n * That is not a miscalibrated entropy rule. It is a picture. No secret scanner\n * ever written can look at 589 KB of base64 and tell you which 40 characters\n * of it are a credential, because none of them are \u2014 it is a JPEG. The fix is\n * not to make the detector blinder; it is to stop feeding it images.\n *\n * The codex adapter already established the convention for exactly this\n * problem (`adapters/codex.ts`, \"Trap 3\"): a binary payload becomes a short\n * marker, `\u2039elided:image/png:109362 bytes\u203A`. Its regex only recognises\n * `data:\u2026;base64,\u2026` URIs, which is the shape codex writes. Claude Code writes\n * the Anthropic content-block shape instead \u2014\n *\n * [{\"type\":\"image\",\"source\":{\"type\":\"base64\",\"data\":\"/9j/4AAQ\u2026\",\"media_type\":\"image/jpeg\"}}]\n *\n * \u2014 which that regex never sees. This module generalises the convention to\n * both shapes, plus to a bare base64 run that announces itself with a file\n * magic, and is called from `ingest.ts` on the way into the store, before\n * `redactExchange`.\n *\n * ## Why it is safe to run before redaction\n *\n * Every shape elided here is identified by something a *credential never has*:\n * a `data:` URI scheme, an image content-block wrapper, or the base64 of a\n * file-format magic number. A 40-character AWS secret is not a JPEG header,\n * and the {@link MIN_PAYLOAD} floor of 512 base64 characters is far above any\n * credential that has ever been issued. PEM private-key bodies are longer than\n * that but begin `MII\u2026` / `b3Blb\u2026` and carry a `-----BEGIN` header, so they\n * match no magic here and reach the redactor intact \u2014 `tests/redact.test.ts`\n * holds that case.\n *\n * The marker uses the same guillemets as the mask (`\u2039` U+2039, `\u203A` U+203A) and\n * for the same reason: those characters cannot occur in base64, in a shell\n * token or in a json key, so an elision can never be confused for content and\n * a second pass recognises its own output.\n *\n * Elision is **not** redaction and does not pretend to be. It removes bulk\n * that carries no meaning for retrieval; the redactor still runs afterwards\n * over everything that survives, and there is still no `--no-redact` flag.\n */\n\nimport type { Exchange, ExchangeToolCall } from './adapters/types.js';\n\n/** What one pass threw away, so a caller can say it out loud. */\nexport interface Elisions {\n /** Binary payloads replaced by a marker. */\n binaryParts: number;\n /** Characters dropped. */\n charsElided: number;\n}\n\nexport function emptyElisions(): Elisions {\n return { binaryParts: 0, charsElided: 0 };\n}\n\nexport function addElisions(a: Elisions, b: Elisions): Elisions {\n return {\n binaryParts: a.binaryParts + b.binaryParts,\n charsElided: a.charsElided + b.charsElided,\n };\n}\n\n/**\n * The shortest base64 run this module will elide, in characters. 512 base64\n * characters is 384 bytes.\n *\n * Chosen to sit far above every credential shape in `redact-rules.ts` \u2014 the\n * longest is an anthropic key at ~120 characters \u2014 so raising this floor can\n * never be the reason a secret is missed. A \"binary payload\" smaller than this\n * is not worth a marker anyway.\n */\nexport const MIN_PAYLOAD = 512;\n\n/** Matches this module's own marker, so a second pass is a no-op. */\nexport const ELISION_RE = /\u2039elided:[^\u203A]{1,120}\u203A/g;\n\n/**\n * base64 of the first bytes of a file format, longest prefix first.\n *\n * A base64 payload that starts with one of these *is* that file: base64 is a\n * fixed 3-bytes-to-4-characters encoding with no header of its own, so the\n * leading characters are a faithful transcription of the leading bytes, which\n * for every format below is a magic number the format spec mandates.\n *\n * Only formats that actually turn up pasted into an agent transcript are here.\n * Adding one is cheap; guessing one is not, so each names the bytes it encodes.\n */\nconst BASE64_MAGIC: Array<[prefix: string, mime: string, bytes: string]> = [\n ['iVBORw0KGgo', 'image/png', '89 50 4E 47 0D 0A 1A 0A'],\n ['/9j/', 'image/jpeg', 'FF D8 FF (JFIF/Exif SOI)'],\n ['R0lGODdh', 'image/gif', 'GIF87a'],\n ['R0lGODlh', 'image/gif', 'GIF89a'],\n ['Qk0', 'image/bmp', '42 4D (\"BM\")'],\n ['SUkqAA', 'image/tiff', '49 49 2A 00 (little-endian TIFF)'],\n ['TU0AK', 'image/tiff', '4D 4D 00 2A (big-endian TIFF)'],\n ['AAABAA', 'image/x-icon', '00 00 01 00 (ICO)'],\n ['UklGR', 'application/octet-stream', '52 49 46 46 (\"RIFF\" \u2014 webp/wav/avi)'],\n ['JVBERi0', 'application/pdf', '25 50 44 46 2D (\"%PDF-\")'],\n ['UEsDB', 'application/zip', '50 4B 03 04 (\"PK\\\\x03\\\\x04\" \u2014 zip/docx/xlsx)'],\n ['H4sI', 'application/gzip', '1F 8B 08'],\n ['SUQz', 'audio/mpeg', '49 44 33 (\"ID3\")'],\n ['T2dnU', 'application/ogg', '4F 67 67 53 (\"OggS\")'],\n ['d09GRg', 'font/woff', '77 4F 46 46 (\"wOFF\")'],\n ['d09GMg', 'font/woff2', '77 4F 46 32 (\"wOF2\")'],\n ['AAEAAA', 'font/ttf', '00 01 00 00 (TrueType)'],\n ['T1RUTw', 'font/otf', '4F 54 54 4F (\"OTTO\")'],\n];\n\n/** A payload character: base64, plus the `\\/` a json encoder writes for `/`. */\nconst B64_RUN = `(?:[A-Za-z0-9+/=]|\\\\\\\\/)`;\n\n/**\n * A payload, possibly hard-wrapped.\n *\n * Whitespace is tolerated *only between two long chunks*. Allowing it as a\n * plain payload character is the obvious way to write this and it is wrong:\n * `<900 characters of png>\\ndone` is then a single run, and the elision eats\n * the word after the payload. Requiring a continuation to be \u2265 40 characters\n * means prose on the next line ends the run instead of joining it.\n */\nconst B64_BODY = `${B64_RUN}{40,}(?:[\\\\r\\\\n \\\\t]+${B64_RUN}{40,})*`;\n\n/**\n * `data:image/png;base64,\u2026` \u2014 the shape the codex adapter already elides,\n * kept identical here so the two passes agree and the second is a no-op.\n */\nconst DATA_URI = new RegExp(\n `data:([a-zA-Z0-9.+-]+/[a-zA-Z0-9.+-]+)?(?:;[a-zA-Z0-9.+=-]+)*;base64,${B64_BODY}`,\n 'g',\n);\n\n/**\n * The Anthropic / OpenAI image **content block**, as the parser stringifies it\n * into `tool_calls.result`:\n *\n * {\"type\":\"image\",\"source\":{\"type\":\"base64\",\"data\":\"/9j/4AAQ\u2026\",\"media_type\":\"image/jpeg\"}}\n *\n * `media_type` may sit either side of `data` (both orders occur in the real\n * corpus), so the mime is recovered by a second look rather than by trying to\n * write one regex that matches both. The payload key is the union of the keys\n * the shapes in the wild use: `data` (anthropic), `image_data`, `b64_json`\n * (openai images), `base64`.\n *\n * The `\"base64\"` / `media_type` context is *required*: without it this would\n * be \"elide any long string called data\", which would swallow a base64\n * credential posted in a json body. That case is a planted fixture.\n */\nconst CONTENT_BLOCK = new RegExp(\n `\"(?:data|image_data|b64_json|base64)\"\\\\s*:\\\\s*\"(${B64_BODY})\"`,\n 'g',\n);\n\n/** `\"media_type\":\"image/jpeg\"`, `\"mimeType\": \"image/png\"`, `\"type\":\"base64\"`. */\nconst MEDIA_HINT = /\"(?:media_?type|mime_?type|mimeType)\"\\s*:\\s*\"([a-zA-Z0-9.+-]+\\/[a-zA-Z0-9.+-]+)\"|\"type\"\\s*:\\s*\"base64\"/gi;\n\n/** How far either side of a payload a media hint still counts as its own. */\nconst HINT_WINDOW = 200;\n\n/**\n * A bare base64 run that announces itself with a file magic \u2014 a payload pasted\n * without any wrapper at all, which is what `cat image.png | base64` in a Bash\n * tool result looks like.\n *\n * Anchored on the magic prefixes rather than on \"any base64 character\", which\n * is not a nicety: a `{512,}` quantifier over an unanchored character class\n * is retried at every letter of the corpus, and the corpus is 78 MB. With the\n * literal prefixes up front the engine skips to the next possible start.\n */\nconst MAGIC_PREFIXES = BASE64_MAGIC.map(([p]) => p.replace(/[.*+?^${}()|[\\]\\\\/]/g, '\\\\$&')).join('|');\nconst BARE_RUN = new RegExp(\n `(?<![A-Za-z0-9+/=])(?:${MAGIC_PREFIXES})${B64_BODY}`,\n 'g',\n);\n\ninterface Span {\n start: number;\n end: number;\n mime: string;\n}\n\n/** Strip json escapes and whitespace to get at the payload's real leading bytes. */\nfunction magicOf(payload: string): string | undefined {\n const head = payload.slice(0, 32).replace(/\\\\\\//g, '/').replace(/\\s+/g, '');\n for (const [prefix, mime] of BASE64_MAGIC) if (head.startsWith(prefix)) return mime;\n return undefined;\n}\n\n/** The mime named nearest to `[start,end)`, if the window holds a hint. */\nfunction mimeNear(text: string, start: number, end: number): string | undefined {\n const from = Math.max(0, start - HINT_WINDOW);\n const window = text.slice(from, start) + text.slice(end, Math.min(text.length, end + HINT_WINDOW));\n const rx = new RegExp(MEDIA_HINT.source, MEDIA_HINT.flags);\n let m: RegExpExecArray | null;\n let sawBase64 = false;\n while ((m = rx.exec(window)) !== null) {\n if (m[1]) return m[1];\n sawBase64 = true;\n }\n return sawBase64 ? 'application/octet-stream' : undefined;\n}\n\nfunction collect(re: RegExp, text: string, group: number, decide: (payload: string, start: number, end: number) => string | undefined, out: Span[]): void {\n const rx = new RegExp(re.source, re.flags);\n let m: RegExpExecArray | null;\n while ((m = rx.exec(text)) !== null) {\n const payload = group === 0 ? m[0] : m[group];\n if (payload === undefined || payload.length === 0) { rx.lastIndex += 1; continue; }\n const start = m.index + (group === 0 ? 0 : m[0].indexOf(payload));\n const end = start + payload.length;\n const mime = decide(payload, start, end);\n if (mime === undefined) continue;\n out.push({ start, end, mime });\n }\n}\n\n/**\n * Replace every binary payload in `text` with `\u2039elided:<mime>:<n> bytes\u203A`.\n *\n * Idempotent: the marker contains no base64 run of its own, so a second call\n * finds nothing and returns the same string.\n */\nexport function elideBinary(text: string, tally: Elisions = emptyElisions()): string {\n if (typeof text !== 'string' || text.length < MIN_PAYLOAD) return text ?? '';\n // Cheap reject. Every shape below needs a long unbroken base64 run, and\n // that needs at least this much alphabet. Skipping here is what keeps the\n // pass off the 99% of transcript text that is prose and code.\n if (!/[A-Za-z0-9+/]{64}/.test(text)) return text;\n\n const spans: Span[] = [];\n // The regexes only require 40 payload characters so that a hard-wrapped\n // payload still matches as one run; the MIN_PAYLOAD floor is applied here,\n // to the whole run.\n const big = (payload: string) => payload.length >= MIN_PAYLOAD;\n // 1. data: URIs \u2014 mime is in the URI itself.\n collect(DATA_URI, text, 0, (payload) => {\n if (!big(payload)) return undefined;\n const m = /^data:([a-zA-Z0-9.+-]+\\/[a-zA-Z0-9.+-]+)?/.exec(payload);\n return m?.[1] ?? 'application/octet-stream';\n }, spans);\n // 2. image content blocks \u2014 the wrapper must say `base64` or name a mime,\n // otherwise a long value that merely happens to be called `data` is left\n // for the redactor to judge.\n collect(CONTENT_BLOCK, text, 1, (payload, start, end) => {\n if (!big(payload)) return undefined;\n const hint = mimeNear(text, start, end);\n if (hint === undefined) return undefined;\n return magicOf(payload) ?? hint;\n }, spans);\n // 3. bare runs that carry a file magic.\n collect(BARE_RUN, text, 0, (payload) => (big(payload) ? magicOf(payload) : undefined), spans);\n\n if (spans.length === 0) return text;\n\n // First span wins on overlap: a data: URI inside a content block is elided\n // once, as the URI, not twice.\n spans.sort((a, b) => a.start - b.start || b.end - a.end);\n const out: string[] = [];\n let cursor = 0;\n for (const s of spans) {\n if (s.start < cursor) continue;\n out.push(text.slice(cursor, s.start));\n const n = s.end - s.start;\n out.push(`\u2039elided:${s.mime}:${n} bytes\u203A`);\n tally.binaryParts += 1;\n tally.charsElided += n;\n cursor = s.end;\n }\n out.push(text.slice(cursor));\n return out.join('');\n}\n\n/**\n * {@link elideBinary} over every field of an exchange that `03` \u00A75 redacts.\n * Returns a new object; the input is never mutated, because the caller may\n * still be writing the byte-exact archive copy from it.\n */\nexport function elideExchange(ex: Exchange): { exchange: Exchange; elisions: Elisions } {\n const elisions = emptyElisions();\n const one = (s: string): string => elideBinary(s, elisions);\n\n const userText = one(ex.userText);\n const assistantText = one(ex.assistantText);\n const toolCalls: ExchangeToolCall[] = ex.toolCalls.map((tc) => {\n const next: ExchangeToolCall = { ...tc, input: one(tc.input) };\n if (tc.result !== undefined) next.result = one(tc.result);\n return next;\n });\n\n if (elisions.binaryParts === 0) return { exchange: ex, elisions };\n return { exchange: { ...ex, userText, assistantText, toolCalls }, elisions };\n}\n", "import fs from 'node:fs';\nimport {createHash} from 'node:crypto';\nimport {redact} from '../redact.js';\nimport {elideBinary} from '../redact-elide.js';\n\nexport const digest=(value:string|Buffer):string=>createHash('sha256').update(value).digest('hex');\nexport const clean=(text:string):string=>redact(elideBinary(text)).text;\nexport const clock=(value:unknown):string|null=>typeof value==='string'&&Number.isFinite(Date.parse(value))?new Date(value).toISOString():null;\nexport interface NativeEvent {observed?:boolean;languageEligible?:boolean;key:string;rawStart:number;rawEnd:number;role:'user';text:string;eventAt:string|null;project:string|null;origin:'claude_prompt_id'|'codex_human_marker'|'pi_user_projection'|'opencode_user_projection'|'unknown';eligible:boolean;excluded:string|null;identity:string;evidenceStart?:number;nativeRecordId?:string;recordCommitment?:string;declaredOrigin?:string;}\nexport interface NativeFacts {nativeId:string;project:string|null;parent:string|null;child:boolean;title:string|null;events:NativeEvent[];gaps:string[];hash:string;consumed:number;bytes:Buffer;}\n\n/** A size check and a bounded fd read, with identity rechecked after reading. */\nexport function boundedBytes(file:string,max:number):Buffer {\n const fd=fs.openSync(file,'r');\n try{const before=fs.fstatSync(fd);if(!before.isFile()||before.size>max)throw new Error('source_bytes_limit');\n const bytes=Buffer.alloc(before.size);let offset=0;\n while(offset<bytes.length){const n=fs.readSync(fd,bytes,offset,bytes.length-offset,offset);if(!n)throw new Error('source_changed');offset+=n;}\n const after=fs.fstatSync(fd);if(before.size!==after.size||before.mtimeMs!==after.mtimeMs||before.ctimeMs!==after.ctimeMs||before.dev!==after.dev||before.ino!==after.ino)throw new Error('source_changed');\n return bytes;\n }finally{fs.closeSync(fd);}\n}\n", "import {runScanWorker} from '../../core/src/analytics/scan-pool.js';\nrunScanWorker();\n"],
|
|
5
|
+
"mappings": ";;;;AAOA,SAAQ,QAAQ,kBAAiB;;;ACOjC,OAAO,QAAQ;AACf,OAAO,UAAU;;;ACAV,IAAM,4BACX;AAGK,IAAM,uBACX;AAEK,IAAM,oBAAuC;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,SAAS,mBAAmB,MAAuB;AACxD,SAAO,kBAAkB,KAAK,CAAC,WAAW,KAAK,SAAS,MAAM,CAAC;AACjE;;;AChCA,SAAS,kBAAkB;;;AC8H3B,IAAM,UAAU,IAAI,OAAO,sBAAsB,GAAG;;;ACnB7C,SAAS,eAAe,GAAmB;AAChD,QAAM,IAAI,EAAE;AACZ,MAAI,MAAM,EAAG,QAAO;AACpB,QAAM,SAAS,oBAAI,IAAoB;AACvC,aAAW,MAAM,EAAG,QAAO,IAAI,KAAK,OAAO,IAAI,EAAE,KAAK,KAAK,CAAC;AAC5D,MAAI,IAAI;AACR,aAAW,KAAK,OAAO,OAAO,GAAG;AAC/B,UAAM,IAAI,IAAI;AACd,SAAK,IAAI,KAAK,KAAK,CAAC;AAAA,EACtB;AACA,SAAO;AACT;AAEO,IAAM,qBAAqB;AAC3B,IAAM,oBAAoB;AAc1B,IAAM,cAAwB;AAAA;AAAA,EAEnC;AAAA;AAAA,EAEA;AACF;AAEA,IAAM,UAAU;AAChB,IAAM,SAAS;AACf,IAAM,aAAa;AAkCnB,IAAM,eACJ;AAQF,IAAM,UAAU;AAwBhB,IAAM,mBAAmB;AACzB,IAAM,uBAAuB;AAC7B,IAAM,qBAAqB;AAE3B,SAAS,eAAe,OAAwB;AAC9C,MAAI,OAAO;AACX,aAAW,OAAO,MAAM,MAAM,gBAAgB,KAAK,CAAC,GAAG;AACrD,QAAI,IAAI,UAAU,mBAAoB,QAAO;AAC7C,QAAI,IAAI,UAAU,iBAAkB,SAAQ;AAAA,EAC9C;AACA,SAAO,QAAQ;AACjB;AAeA,SAAS,0BAA0B,OAAwB;AACzD,SAAO,MAAM,UAAU,MAAM,IAAI,IAAI,KAAK,EAAE,SAAS,MAAM;AAC7D;AAOA,IAAM,iBACJ;AAGF,IAAM,mBAAmB;AAGzB,IAAM,eACJ;AAGF,IAAM,sBAAsB;AAQ5B,IAAM,kBAAkB;AAGxB,IAAM,aAAa;AAGnB,IAAM,WAAW;AAGV,IAAM,mBAAmB;AAGhC,IAAM,iBAAiB,oBAAI,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAYM,SAAS,oBAAoB,MAAuB;AACzD,QAAM,WAAW,KACd,MAAM,eAAe,EACrB,QAAQ,CAAC,SAAS,KAAK,MAAM,wBAAwB,CAAC,EACtD,OAAO,OAAO;AACjB,QAAM,OAAO,SAAS,SAAS,SAAS,CAAC;AACzC,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAQ,KAAK,YAAY;AAC/B,MAAI,eAAe,IAAI,KAAK,EAAG,QAAO;AACtC,SAAO,8DAA8D,KAAK,KAAK;AACjF;AAWO,SAAS,qBAAqB,OAAwB;AAC3D,QAAM,IAAI,MAAM,KAAK;AACrB,MAAI,EAAE,SAAS,MAAM,EAAE,SAAS,IAAK,QAAO;AAC5C,MAAI,KAAK,KAAK,CAAC,EAAG,QAAO;AACzB,MAAI,iBAAiB,KAAK,CAAC,EAAG,QAAO;AACrC,MAAI,SAAS,KAAK,CAAC,EAAG,QAAO;AAC7B,MAAI,iBAAiB,KAAK,CAAC,EAAG,QAAO;AACrC,MAAI,aAAa,KAAK,CAAC,EAAG,QAAO;AACjC,MAAI,gBAAgB,KAAK,CAAC,EAAG,QAAO;AACpC,MAAI,WAAW,KAAK,CAAC,EAAG,QAAO;AAC/B,MAAI,WAAW,KAAK,CAAC,EAAG,QAAO;AAC/B,MAAI,eAAe,KAAK,CAAC,EAAG,QAAO;AAGnC,MAAI,oBAAoB,KAAK,CAAC,KAAK,eAAe,CAAC,IAAI,EAAK,QAAO;AAEnE,MAAI,IAAI,IAAI,CAAC,EAAE,QAAQ,EAAG,QAAO;AAKjC,MAAI,eAAe,CAAC,IAAI,IAAK,QAAO;AACpC,SAAO;AACT;AAaO,SAAS,wBAAwB,OAAwB;AAC9D,MAAI,MAAM,SAAS,mBAAoB,QAAO;AAG9C,MAAI,QAAQ,KAAK,KAAK,EAAG,QAAO;AAEhC,MAAI,QAAQ,KAAK,MAAM,QAAQ,kCAAkC,EAAE,CAAC,EAAG,QAAO;AAE9E,MAAI,OAAO,KAAK,KAAK,EAAG,QAAO;AAG/B,MAAI,8DAA8D,KAAK,KAAK,EAAG,QAAO;AACtF,MAAI,WAAW,KAAK,KAAK,EAAG,QAAO;AACnC,MAAI,iBAAiB,KAAK,KAAK,EAAG,QAAO;AACzC,MAAI,eAAe,KAAK,KAAK,EAAG,QAAO;AACvC,MAAI,aAAa,KAAK,KAAK,EAAG,QAAO;AACrC,MAAI,QAAQ,KAAK,KAAK,EAAG,QAAO;AAChC,MAAI,eAAe,KAAK,EAAG,QAAO;AAClC,MAAI,0BAA0B,KAAK,EAAG,QAAO;AAC7C,SAAO;AACT;AAYA,SAAS,UACP,IACA,MACA,QACA,IACA,OAAyB,CAAC,GACpB;AACN,QAAM,QAAQ,KAAK,SAAS;AAC5B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK,MAA2B;AAC9B,YAAM,MAAmB,CAAC;AAG1B,YAAM,KAAK,IAAI,OAAO,GAAG,QAAQ,GAAG,MAAM,SAAS,GAAG,IAAI,GAAG,QAAQ,GAAG,QAAQ,GAAG;AACnF,UAAI;AACJ,cAAQ,IAAI,GAAG,KAAK,IAAI,OAAO,MAAM;AACnC,YAAI,EAAE,CAAC,EAAE,WAAW,GAAG;AAAE,aAAG;AAAa;AAAA,QAAU;AACnD,cAAM,QAAQ,UAAU,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK;AAC1C,YAAI,UAAU,UAAa,MAAM,WAAW,EAAG;AAC/C,YAAI,KAAK,YAAY,CAAC,KAAK,SAAS,OAAO,CAAC,EAAG;AAC/C,cAAM,SAAS,UAAU,IAAI,IAAI,EAAE,CAAC,EAAE,QAAQ,KAAK;AACnD,YAAI,SAAS,EAAG;AAChB,YAAI,KAAK,EAAE,OAAO,EAAE,QAAQ,QAAQ,KAAK,EAAE,QAAQ,SAAS,MAAM,QAAQ,MAAM,CAAC;AAAA,MACnF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAcA,IAAM,iBACJ;AAEF,IAAM;AAAA;AAAA;AAAA;AAAA;AAAA,EAKJ;AAAA;AAEF,IAAM,eAAe;AAErB,IAAM,wBAA8B;AAAA,EAClC,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK,MAA2B;AAC9B,UAAM,MAAmB,CAAC;AAC1B,UAAM,SAAS,IAAI,OAAO,eAAe,QAAQ,eAAe,KAAK;AACrE,QAAI;AACJ,YAAQ,IAAI,OAAO,KAAK,IAAI,OAAO,MAAM;AACvC,YAAM,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE;AAC7B,YAAM,QAAQ,aAAa,KAAK,EAAE,CAAC,CAAC,IAAI,IAAI;AAC5C,YAAM,SAAS,KAAK,QAAQ,QAAQ,CAAC,KAAK;AAG1C,UAAI,KAAK,KAAK,MAAM,OAAQ,UAAU,KAAK,kBAAkB,KAAK,MAAM,EAAI;AAG5E,YAAM,aAAa,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,8BAA8B,EAAE,EAAE;AAC5E,UAAI,YAAY,EAAE;AAClB,aAAO,YAAY,KAAK,aAAa,KAAK,KAAK,YAAY,CAAC,KAAK,EAAE,EAAG;AACtE,YAAM,OAAO,KAAK,MAAM,WAAW,UAAU;AAC7C,UAAI,CAAC,oBAAoB,IAAI,EAAG;AAEhC,oBAAc,YAAY;AAC1B,YAAM,IAAI,cAAc,KAAK,IAAI;AACjC,UAAI,CAAC,EAAG;AACR,YAAM,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC;AACzC,UAAI,UAAU,UAAa,CAAC,qBAAqB,KAAK,EAAG;AACzD,YAAM,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,YAAY,KAAK;AAC9C,UAAI,KAAK,EAAE,OAAO,KAAK,QAAQ,MAAM,QAAQ,MAAM,CAAC;AACpD,aAAO,YAAY,QAAQ,MAAM;AAAA,IACnC;AACA,WAAO;AAAA,EACT;AACF;AAGA,IAAM,iBAAoB;AAAA,EACzB,IAAG;AAAA,EAAuB,MAAK;AAAA,EAAU,QAAO;AAAA,EAChD,KAAK,MAAwB;AAAC,UAAM,MAAgB,CAAC;AAAE,UAAM,SAAO;AAA4D,QAAI;AACnI,YAAO,QAAM,OAAO,KAAK,IAAI,OAAK,MAAK;AAAC,oBAAc,YAAU,OAAO;AAAU,YAAM,QAAM,cAAc,KAAK,IAAI;AAAE,UAAG,CAAC,MAAM;AAAS,YAAM,QAAM,MAAM,CAAC,KAAG,MAAM,CAAC,KAAG,MAAM,CAAC,KAAG,MAAM,CAAC;AAAE,UAAG,UAAQ,UAAW,CAAC,qBAAqB,KAAK,EAAE;AAAS,YAAM,QAAM,MAAM,QAAM,MAAM,CAAC,EAAE,YAAY,KAAK;AAAE,UAAI,KAAK,EAAC,OAAM,KAAI,QAAM,MAAM,QAAO,MAAK,CAAC;AAAE,aAAO,YAAU,QAAM,MAAM;AAAA,IAAO;AAC/X,WAAO;AAAA,EAAI;AACb;AAgBA,IAAM,YACJ;AAEF,IAAM,aAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,IACE,OAAO;AAAA;AAAA;AAAA,IAGP,UAAU,CAAC,UAAU,qBAAqB,KAAK;AAAA,EACjD;AACF;AA4BA,IAAM,gBAAgB;AAGtB,IAAM,iBAAiB;AAEvB,IAAM,kBAAwB;AAAA,EAC5B,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK,MAA2B;AAC9B,UAAM,MAAmB,CAAC;AAC1B,UAAM,KAAK,IAAI,OAAO,cAAc,QAAQ,cAAc,KAAK;AAC/D,QAAI;AACJ,YAAQ,IAAI,GAAG,KAAK,IAAI,OAAO,MAAM;AACnC,UAAI,QAAQ,EAAE;AACd,UAAI,QAAQ,EAAE,CAAC;AAEf,UAAI,QAAQ,KAAK,KAAK,QAAQ,CAAC,MAAM,QAAQ,eAAe,SAAS,MAAM,CAAC,KAAK,EAAE,GAAG;AACpF,iBAAS;AACT,gBAAQ,MAAM,MAAM,CAAC;AAAA,MACvB;AAGA,UAAI,QAAQ,KAAK,KAAK,QAAQ,CAAC,MAAM,OAAO,kBAAkB,KAAK,KAAK,GAAG;AACzE,iBAAS;AACT,gBAAQ,MAAM,MAAM,CAAC;AAAA,MACvB;AAEA,YAAM,OAAO,MAAM,SAAS,MAAM,QAAQ,WAAW,EAAE,EAAE;AACzD,eAAS;AACT,cAAQ,MAAM,MAAM,IAAI,EAAE,QAAQ,WAAW,EAAE;AAE/C,UAAI,MAAM,SAAS,mBAAoB;AACvC,UAAI,CAAC,wBAAwB,KAAK,EAAG;AACrC,UAAI,eAAe,KAAK,IAAI,kBAAmB;AAC/C,UAAI,KAAK,EAAE,OAAO,KAAK,QAAQ,MAAM,QAAQ,MAAM,CAAC;AAAA,IACtD;AACA,WAAO;AAAA,EACT;AACF;AAEO,IAAM,QAAgB;AAAA;AAAA,EAE3B;AAAA;AAAA;AAAA;AAAA,IAIE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA;AAAA;AAAA,IAGE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA;AAAA;AAAA,IAEE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA;AAAA;AAAA;AAAA,IAIE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,EAAE,OAAO,EAAE;AAAA,EACb;AAAA,EACA;AAAA;AAAA,IAEE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA;AAAA,IAEE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA;AAAA;AAAA,IAGE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA;AAAA,IAEE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA;AAAA;AAAA,IAGE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA;AAAA,IAEE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA;AAAA;AAAA,IAGE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA;AAAA;AAAA,IAGE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA;AAAA;AAAA,IAGE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA;AAAA,IAEE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA;AAAA,IAEE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKE;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA,IAGA;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,UAAU,CAAC,UAAU;AACnB,YAAI,iBAAiB,KAAK,KAAK,EAAG,QAAO;AACzC,YAAI,eAAe,KAAK,KAAK,EAAG,QAAO;AACvC,YAAI,iBAAiB,KAAK,KAAK,EAAG,QAAO;AACzC,YAAI,2DAA2D,KAAK,KAAK,EAAG,QAAO;AACnF,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA,EAKA;AACF;;;ACxrBO,SAAS,gBAA0B;AACxC,SAAO,EAAE,aAAa,GAAG,aAAa,EAAE;AAC1C;AAkBO,IAAM,cAAc;AAgB3B,IAAM,eAAqE;AAAA,EACzE,CAAC,eAAe,aAAa,yBAAyB;AAAA,EACtD,CAAC,QAAQ,cAAc,0BAA0B;AAAA,EACjD,CAAC,YAAY,aAAa,QAAQ;AAAA,EAClC,CAAC,YAAY,aAAa,QAAQ;AAAA,EAClC,CAAC,OAAO,aAAa,cAAc;AAAA,EACnC,CAAC,UAAU,cAAc,kCAAkC;AAAA,EAC3D,CAAC,SAAS,cAAc,+BAA+B;AAAA,EACvD,CAAC,UAAU,gBAAgB,mBAAmB;AAAA,EAC9C,CAAC,SAAS,4BAA4B,0CAAqC;AAAA,EAC3E,CAAC,WAAW,mBAAmB,0BAA0B;AAAA,EACzD,CAAC,SAAS,mBAAmB,mDAA8C;AAAA,EAC3E,CAAC,QAAQ,oBAAoB,UAAU;AAAA,EACvC,CAAC,QAAQ,cAAc,kBAAkB;AAAA,EACzC,CAAC,SAAS,mBAAmB,sBAAsB;AAAA,EACnD,CAAC,UAAU,aAAa,sBAAsB;AAAA,EAC9C,CAAC,UAAU,cAAc,sBAAsB;AAAA,EAC/C,CAAC,UAAU,YAAY,wBAAwB;AAAA,EAC/C,CAAC,UAAU,YAAY,sBAAsB;AAC/C;AAGA,IAAM,UAAU;AAWhB,IAAM,WAAW,GAAG,OAAO,wBAAwB,OAAO;AAM1D,IAAM,WAAW,IAAI;AAAA,EACnB,wEAAwE,QAAQ;AAAA,EAChF;AACF;AAkBA,IAAM,gBAAgB,IAAI;AAAA,EACxB,mDAAmD,QAAQ;AAAA,EAC3D;AACF;AAGA,IAAM,aAAa;AAGnB,IAAM,cAAc;AAYpB,IAAM,iBAAiB,aAAa,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,wBAAwB,MAAM,CAAC,EAAE,KAAK,GAAG;AACpG,IAAM,WAAW,IAAI;AAAA,EACnB,yBAAyB,cAAc,IAAI,QAAQ;AAAA,EACnD;AACF;AASA,SAAS,QAAQ,SAAqC;AACpD,QAAM,OAAO,QAAQ,MAAM,GAAG,EAAE,EAAE,QAAQ,SAAS,GAAG,EAAE,QAAQ,QAAQ,EAAE;AAC1E,aAAW,CAAC,QAAQ,IAAI,KAAK,aAAc,KAAI,KAAK,WAAW,MAAM,EAAG,QAAO;AAC/E,SAAO;AACT;AAGA,SAAS,SAAS,MAAc,OAAe,KAAiC;AAC9E,QAAM,OAAO,KAAK,IAAI,GAAG,QAAQ,WAAW;AAC5C,QAAM,SAAS,KAAK,MAAM,MAAM,KAAK,IAAI,KAAK,MAAM,KAAK,KAAK,IAAI,KAAK,QAAQ,MAAM,WAAW,CAAC;AACjG,QAAM,KAAK,IAAI,OAAO,WAAW,QAAQ,WAAW,KAAK;AACzD,MAAI;AACJ,MAAI,YAAY;AAChB,UAAQ,IAAI,GAAG,KAAK,MAAM,OAAO,MAAM;AACrC,QAAI,EAAE,CAAC,EAAG,QAAO,EAAE,CAAC;AACpB,gBAAY;AAAA,EACd;AACA,SAAO,YAAY,6BAA6B;AAClD;AAEA,SAAS,QAAQ,IAAY,MAAc,OAAe,QAA6E,KAAmB;AACxJ,QAAM,KAAK,IAAI,OAAO,GAAG,QAAQ,GAAG,KAAK;AACzC,MAAI;AACJ,UAAQ,IAAI,GAAG,KAAK,IAAI,OAAO,MAAM;AACnC,UAAM,UAAU,UAAU,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK;AAC5C,QAAI,YAAY,UAAa,QAAQ,WAAW,GAAG;AAAE,SAAG,aAAa;AAAG;AAAA,IAAU;AAClF,UAAM,QAAQ,EAAE,SAAS,UAAU,IAAI,IAAI,EAAE,CAAC,EAAE,QAAQ,OAAO;AAC/D,UAAM,MAAM,QAAQ,QAAQ;AAC5B,UAAM,OAAO,OAAO,SAAS,OAAO,GAAG;AACvC,QAAI,SAAS,OAAW;AACxB,QAAI,KAAK,EAAE,OAAO,KAAK,KAAK,CAAC;AAAA,EAC/B;AACF;AAQO,SAAS,YAAY,MAAc,QAAkB,cAAc,GAAW;AACnF,MAAI,OAAO,SAAS,YAAY,KAAK,SAAS,YAAa,QAAO,QAAQ;AAI1E,MAAI,CAAC,oBAAoB,KAAK,IAAI,EAAG,QAAO;AAE5C,QAAM,QAAgB,CAAC;AAIvB,QAAM,MAAM,CAAC,YAAoB,QAAQ,UAAU;AAEnD,UAAQ,UAAU,MAAM,GAAG,CAAC,YAAY;AACtC,QAAI,CAAC,IAAI,OAAO,EAAG,QAAO;AAC1B,UAAM,IAAI,4CAA4C,KAAK,OAAO;AAClE,WAAO,IAAI,CAAC,KAAK;AAAA,EACnB,GAAG,KAAK;AAIR,UAAQ,eAAe,MAAM,GAAG,CAAC,SAAS,OAAO,QAAQ;AACvD,QAAI,CAAC,IAAI,OAAO,EAAG,QAAO;AAC1B,UAAM,OAAO,SAAS,MAAM,OAAO,GAAG;AACtC,QAAI,SAAS,OAAW,QAAO;AAC/B,WAAO,QAAQ,OAAO,KAAK;AAAA,EAC7B,GAAG,KAAK;AAER,UAAQ,UAAU,MAAM,GAAG,CAAC,YAAa,IAAI,OAAO,IAAI,QAAQ,OAAO,IAAI,QAAY,KAAK;AAE5F,MAAI,MAAM,WAAW,EAAG,QAAO;AAI/B,QAAM,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG;AACvD,QAAM,MAAgB,CAAC;AACvB,MAAI,SAAS;AACb,aAAW,KAAK,OAAO;AACrB,QAAI,EAAE,QAAQ,OAAQ;AACtB,QAAI,KAAK,KAAK,MAAM,QAAQ,EAAE,KAAK,CAAC;AACpC,UAAM,IAAI,EAAE,MAAM,EAAE;AACpB,QAAI,KAAK,gBAAW,EAAE,IAAI,IAAI,CAAC,cAAS;AACxC,UAAM,eAAe;AACrB,UAAM,eAAe;AACrB,aAAS,EAAE;AAAA,EACb;AACA,MAAI,KAAK,KAAK,MAAM,MAAM,CAAC;AAC3B,SAAO,IAAI,KAAK,EAAE;AACpB;;;AHhLA,IAAM,OAAO;AACb,IAAM,QAAQ;AAGP,IAAM,UAAU,IAAI,OAAO,GAAG,IAAI,+BAA+B,KAAK,IAAI,GAAG;AAG7E,SAAS,aAAa,QAAwB;AACnD,SAAO,WAAW,QAAQ,EAAE,OAAO,QAAQ,MAAM,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC;AAC7E;AA4BO,SAAS,OAAO,MAA+B;AACpD,MAAI,OAAO,SAAS,YAAY,KAAK,WAAW,EAAG,QAAO,EAAE,MAAM,QAAQ,IAAI,MAAM,CAAC,EAAE;AAIvF,QAAM,UAAU,IAAI,WAAW,KAAK,MAAM;AAC1C,QAAM,QAAqD,CAAC;AAI5D,aAAW,QAAQ,eAAe,IAAI,EAAG,OAAM,SAAS,IAAI;AAE5D,aAAW,QAAQ,OAAO;AACxB,eAAW,KAAK,KAAK,KAAK,IAAI,GAAG;AAC/B,UAAI,EAAE,QAAQ,KAAK,EAAE,MAAM,KAAK,UAAU,EAAE,OAAO,EAAE,MAAO;AAC5D,UAAI,UAAU,SAAS,CAAC,EAAG;AAC3B,YAAM,SAAS,CAAC;AAChB,YAAM,KAAK,EAAE,OAAO,EAAE,OAAO,KAAK,EAAE,KAAK,MAAM,OAAO,EAAE,MAAM,CAAC;AAAA,IACjE;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,EAAG,QAAO,EAAE,MAAM,MAAM,CAAC,EAAE;AAChD,QAAM,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAEtC,QAAM,MAAgB,CAAC;AACvB,QAAM,OAAuB,CAAC;AAC9B,MAAI,SAAS;AACb,aAAW,KAAK,OAAO;AACrB,QAAI,KAAK,KAAK,MAAM,QAAQ,EAAE,KAAK,CAAC;AACpC,UAAM,OAAO,aAAa,EAAE,KAAK;AACjC,QAAI,KAAK,GAAG,IAAI,YAAY,EAAE,KAAK,IAAI,IAAI,IAAI,GAAG,KAAK,EAAE;AACzD,SAAK,KAAK,EAAE,MAAM,EAAE,KAAK,MAAM,MAAM,EAAE,KAAK,IAAI,MAAM,OAAO,EAAE,OAAO,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;AAC/F,aAAS,EAAE;AAAA,EACb;AACA,MAAI,KAAK,KAAK,MAAM,MAAM,CAAC;AAC3B,SAAO,EAAE,MAAM,IAAI,KAAK,EAAE,GAAG,KAAK;AACpC;AAuGA,SAAS,eAAe,MAAsB;AAC5C,QAAM,QAAgB,CAAC;AACvB,QAAM,WAAW,CAAC,SAAS,GAAG,WAAW;AACzC,aAAW,KAAK,UAAU;AACxB,UAAM,KAAK,IAAI,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,GAAG,IAAI,EAAE,QAAQ,EAAE,QAAQ,GAAG;AAC/E,QAAI;AACJ,YAAQ,IAAI,GAAG,KAAK,IAAI,OAAO,MAAM;AACnC,UAAI,EAAE,CAAC,EAAE,WAAW,GAAG;AAAE,WAAG;AAAa;AAAA,MAAU;AACnD,YAAM,KAAK,EAAE,OAAO,EAAE,OAAO,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,CAAC;AAAA,IAC3D;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,UAAU,SAAqB,GAAkB;AACxD,WAAS,IAAI,EAAE,OAAO,IAAI,EAAE,KAAK,IAAK,KAAI,QAAQ,CAAC,EAAG,QAAO;AAC7D,SAAO;AACT;AAEA,SAAS,MAAM,SAAqB,GAAe;AACjD,QAAM,MAAM,KAAK,IAAI,EAAE,KAAK,QAAQ,MAAM;AAC1C,WAAS,IAAI,KAAK,IAAI,GAAG,EAAE,KAAK,GAAG,IAAI,KAAK,IAAK,SAAQ,CAAC,IAAI;AAChE;;;AItSO,IAAM,QAAM,CAAC,SAAqB,OAAO,YAAY,IAAI,CAAC,EAAE;;;ANc5D,IAAM,gBAAgB;AA8E7B,IAAM,mBAAmB;AAGzB,IAAM,QAAQ,CAAC,MAAyB,MAAM,QAAQ,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC;AAC/F,IAAM,MAAM,CAAC,MAA+B,OAAO,MAAM,YAAY,EAAE,SAAS,IAAI,IAAI;AACxF,IAAM,MAAM,CAAC,MAAwB,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,KAAK,IAAI,IAAI,KAAK,MAAM,CAAC,IAAI;AAC5G,SAAS,KAAK,GAA2B;AACvC,MAAI,OAAO,MAAM,UAAU;AACzB,UAAM,IAAI,KAAK,MAAM,CAAC;AACtB,WAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAAA,EAClC;AACA,MAAI,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,EAAG,QAAO,IAAI,OAAO,IAAI,MAAO;AAC9E,SAAO;AACT;AAEA,SAAS,OAAO,SAA0B;AACxC,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,QAAS,KAAI,MAAM,KAAK,KAAK,OAAO,MAAM,SAAS,SAAU,OAAM,KAAK,MAAM,IAAI;AACtG,SAAO,MAAM,KAAK,IAAI;AACxB;AAKA,IAAM,oBAAiD;AAAA,EACrD,CAAC,uBAAuB,mBAAmB;AAAA,EAC3C,CAAC,0BAA0B,sBAAsB;AAAA,EAAG,CAAC,0BAA0B,sBAAsB;AAAA,EACrG,CAAC,0BAA0B,sBAAsB;AAAA,EACjD,CAAC,wBAAwB,kBAAkB;AAAA,EAAG,CAAC,kBAAkB,kBAAkB;AAAA,EACnF,CAAC,yBAAyB,qBAAqB;AAAA,EAAG,CAAC,4BAA4B,WAAW;AAAA,EAC1F,CAAC,uBAAuB,WAAW;AAAA,EAAG,CAAC,wBAAwB,WAAW;AAAA,EAC1E,CAAC,yBAAyB,WAAW;AAAA,EAAG,CAAC,eAAe,YAAY;AAAA,EACpE,CAAC,4CAA4C,iBAAiB;AAAA,EAC9D,CAAC,mCAAmC,YAAY;AAAA,EAChD,CAAC,yCAAyC,uBAAuB;AAAA,EACjE,CAAC,gEAAgE,oBAAoB;AAAA,EACrF,CAAC,wCAAwC,mBAAmB;AAAA,EAC5D,CAAC,kCAAkC,iBAAiB;AACtD;AACA,IAAM,kBAAkB;AAGjB,SAAS,aAAa,KAAsE;AACjG,QAAM,QAAQ,IAAI,SAAS,mBAAmB,IAAI,IAAI,QAAQ,iBAAiB,EAAE,IAAI,KAAK,KAAK;AAC/F,MAAI,CAAC,KAAM,QAAO,EAAC,MAAM,YAAY,QAAQ,SAAS,KAAI;AAC1D,aAAW,CAAC,QAAQ,MAAM,KAAK,kBAAmB,KAAI,KAAK,WAAW,MAAM,EAAG,QAAO,EAAC,MAAM,YAAY,QAAQ,KAAI;AACrH,MAAI,KAAK,WAAW,gBAAgB,KAAK,KAAK,WAAW,mBAAmB,EAAG,QAAO,EAAC,MAAM,WAAW,QAAQ,iBAAiB,KAAI;AACrI,MAAI,KAAK,WAAW,oCAAoC,EAAG,QAAO,EAAC,MAAM,UAAU,QAAQ,mBAAmB,KAAI;AAClH,SAAO,EAAC,MAAM,SAAS,QAAQ,MAAM,KAAI;AAC3C;AAEA,IAAM,cAAc,CAAC,8DAA8D,mCAAmC,kBAAkB;AAExI,SAAS,eAAe,KAAqB;AAC3C,MAAI,IAAI;AACR,MAAI,EAAE,SAAS,GAAG,EAAG,YAAW,MAAM,YAAa,KAAI,EAAE,QAAQ,IAAI,EAAE;AACvE,QAAM,KAAK,EAAE,QAAQ,eAAe;AACpC,MAAI,MAAM,GAAG;AACX,UAAM,OAAO,EAAE,MAAM,KAAK,gBAAgB,MAAM,GAAG,QAAQ,KAAK,QAAQ,GAAG;AAC3E,QAAI,SAAS,IAAI,KAAK,MAAM,QAAQ,CAAC,IAAI;AAAA,EAC3C,WAAW,EAAE,UAAU,EAAE,WAAW,+BAA+B,EAAG,KAAI;AAC1E,SAAO,EAAE,KAAK;AAChB;AAaA,SAAS,YAAY,MAAc,SAAwB,MAAc,SAA0B;AACjG,SAAO;AAAA,IACL,OAAO;AAAA,MACL,GAAG;AAAA,MAAe;AAAA,MAAS;AAAA,MAAM;AAAA,MAAM;AAAA,MACvC,WAAW,KAAK,SAAS,MAAM,QAAQ;AAAA,MAAG,UAAU;AAAA,MAAM,OAAO;AAAA,MAAO,SAAS;AAAA,MAAM,OAAO;AAAA,MAC9F,WAAW;AAAA,MAAO,UAAU;AAAA,MAAO,WAAW;AAAA,MAAM,SAAS;AAAA,MAAM,QAAQ;AAAA,MAAM,OAAO,CAAC;AAAA,MAAG,SAAS,CAAC;AAAA,MAAG,WAAW;AAAA,IACtH;AAAA,IACA,cAAc;AAAA,IAAM,iBAAiB;AAAA,IAAM,SAAS,CAAC;AAAA,IAAG,aAAa,oBAAI,IAAI;AAAA,EAC/E;AACF;AAEA,SAAS,MAAM,GAAY,IAAyB;AAClD,MAAI,OAAO,KAAM;AACjB,MAAI,EAAE,MAAM,YAAY,QAAQ,KAAK,EAAE,MAAM,QAAS,GAAE,MAAM,UAAU;AACxE,MAAI,EAAE,MAAM,WAAW,QAAQ,KAAK,EAAE,MAAM,OAAQ,GAAE,MAAM,SAAS;AACvE;AAEA,SAAS,SAAS,GAAY,OAAsB,UAA+B;AACjF,MAAI,CAAC,MAAO;AACZ,aAAW,KAAK,EAAE,SAAS;AAAE,MAAE,QAAQ;AAAO,MAAE,gBAAgB;AAAA,EAAU;AAC1E,IAAE,QAAQ,SAAS;AACnB,IAAE,eAAe;AACjB,IAAE,kBAAkB;AACtB;AAEA,SAAS,SAAS,GAAY,GAAoB;AAChD,IAAE,MAAM,MAAM,KAAK,CAAC;AACpB,QAAM,GAAG,EAAE,EAAE;AACb,WAAS,GAAG,EAAE,OAAO,EAAE,QAAQ;AACjC;AAEA,SAAS,UAAU,GAAY,KAAa,IAAmB,SAAwB,MAAkB,QAAuB,MAAoB;AAClJ,MAAI,EAAE,YAAY,IAAI,GAAG,EAAG;AAC5B,IAAE,YAAY,IAAI,GAAG;AACrB,MAAI,SAAS,WAAW,mBAAmB,IAAI,EAAG,GAAE,MAAM,WAAW;AACrE,QAAM,SAAqB;AAAA,IACzB;AAAA,IAAK;AAAA,IAAI;AAAA,IAAS;AAAA,IAAM;AAAA,IACxB,MAAM,SAAS,UAAU,MAAM,KAAK,SAAS,mBAAmB,KAAK,MAAM,GAAG,gBAAgB,IAAI,IAAI,IAAI;AAAA,IAC1G,QAAQ,EAAE;AAAA,IAAc,gBAAgB,EAAE;AAAA,IAAiB,OAAO;AAAA,IAAM,eAAe;AAAA,EACzF;AACA,IAAE,MAAM,QAAQ,KAAK,MAAM;AAC3B,MAAI,SAAS,QAAS,GAAE,QAAQ,KAAK,MAAM;AAC3C,QAAM,GAAG,EAAE;AACb;AAIA,IAAM,eAAe,GAAG,KAAK,GAAG,YAAY,KAAK,GAAG;AAEpD,SAAS,WAAW,GAAY,MAAc,UAAmB,UAA6B;AAC5F,QAAM,YAAY,KAAK,SAAS,oBAAoB;AACpD,MAAI,YAAY,CAAC,KAAK,SAAS,SAAS,IAAK,YAAY,CAAC,KAAK,SAAS,eAAe,GAAI;AAEzF,QAAI,KAAK,SAAS,QAAS,CAAC,KAAK,SAAS,SAAS,KAAK,CAAC,KAAK,SAAS,kBAAkB,EAAI;AAAA,EAC/F;AACA,MAAI;AACJ,MAAI;AAAE,QAAI,KAAK,MAAM,KAAK,SAAS,MAAM,CAAC;AAAA,EAAU,QAAQ;AAAE,MAAE,MAAM;AAAa;AAAA,EAAQ;AAC3F,MAAI,CAAC,MAAM,CAAC,EAAG;AACf,QAAM,IAAI,EAAE;AACZ,MAAI,EAAE,SAAS,aAAa,EAAE,SAAS,kBAAkB,EAAE,SAAS,YAAY;AAC9E,UAAM,QAAQ,IAAI,EAAE,WAAW,KAAK,IAAI,EAAE,OAAO,KAAK,IAAI,EAAE,KAAK,KAAK,IAAI,EAAE,OAAO;AACnF,QAAI,UAAU,EAAE,SAAS,aAAa,CAAC,EAAE,OAAQ,GAAE,QAAQ;AAC3D;AAAA,EACF;AACA,MAAI,OAAO,EAAE,QAAQ,YAAY,CAAC,EAAE,QAAS,GAAE,UAAU,EAAE;AAC3D,QAAM,MAAM,IAAI,EAAE,SAAS;AAC3B,MAAI,KAAK;AACP,QAAI,UAAU;AAAE,QAAE,WAAW;AAAK,QAAE,QAAQ;AAAM,QAAE,YAAY,GAAG,GAAG,IAAI,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAAA,IAAI,WACpG,CAAC,SAAS,KAAM,GAAE,YAAY;AAAA,EACzC;AACA,QAAM,IAAI,MAAM,EAAE,OAAO,IAAI,EAAE,UAAU,CAAC;AAC1C,QAAM,KAAK,KAAK,EAAE,SAAS;AAC3B,MAAI,EAAE,SAAS,aAAa;AAC1B,UAAM,IAAI,EAAE;AACZ,QAAI,CAAC,MAAM,CAAC,EAAG;AACf,UAAM,QAAQ,IAAI,EAAE,KAAK;AACzB,QAAI,UAAU,cAAe;AAC7B,UAAM,KAAK,MAAM,EAAE,cAAc,IAAI,EAAE,iBAAiB;AACxD,UAAM,QAAQ,KAAK,IAAI,GAAG,yBAAyB,IAAI,IAAI,GAAG,yBAAyB,IAAI,IAAI,EAAE,2BAA2B;AAC5H,UAAM,KAAK,IAAI,EAAE,EAAE,GAAG,UAAU,IAAI,EAAE,SAAS,GAAG,UAAU,OAAO,EAAE;AACrE,UAAM,OAAkB;AAAA,MACtB,KAAK,KAAM,UAAU,KAAK,EAAE,IAAI,OAAO,KAAK,KAAK,EAAE,KAAK,OAAO,IAAI,OAAO,EAAE,SAAS,CAAC,KAAM;AAAA,MAC5F;AAAA,MAAI;AAAA,MAAO,UAAU;AAAA,MACrB,OAAO,IAAI,EAAE,YAAY;AAAA,MAAG,QAAQ,IAAI,EAAE,aAAa;AAAA,MAAG,WAAW,IAAI,EAAE,uBAAuB;AAAA,MAClG,YAAY;AAAA,MAAO,cAAc,KAAK,IAAI,GAAG,yBAAyB,IAAI;AAAA,MAAG,WAAW;AAAA,IAC1F;AACA,QAAI,EAAE,gBAAgB,KAAM,MAAK,YAAY;AAC7C,QAAI,CAAC,QAAS,MAAK,cAAc;AACjC,QAAI,GAAI,MAAK,YAAY,GAAG,EAAE,IAAI,OAAO;AACzC,QAAI,EAAE,UAAU,OAAQ,MAAK,OAAO;AACpC,aAAS,GAAG,IAAI;AAChB;AAAA,EACF;AACA,MAAI,EAAE,SAAS,UAAU,EAAE,gBAAgB,QAAQ,SAAU;AAC7D,QAAM,UAAU,EAAE;AAClB,MAAI,MAAM,QAAQ,OAAO,KAAK,QAAQ,KAAK,WAAS,MAAM,KAAK,KAAK,MAAM,SAAS,aAAa,EAAG;AACnG,MAAI,IAAK,UAAS,IAAI,GAAG;AACzB,QAAM,SAAS,MAAM,EAAE,MAAM,IAAI,IAAI,EAAE,OAAO,IAAI,IAAI;AACtD,QAAM,UAAU,EAAE,WAAW,OAAO,YAChC,EAAE,qBAAqB,OAAO,uBAC9B,UAAU,WAAW,UAAU,UAAU,MAAM,KAC/C,EAAE,eAAe,YAAY,sBAC7B,EAAE,iBAAiB,WAAW,yBAC9B;AACJ,MAAI,EAAC,MAAM,QAAQ,KAAI,IAAI,aAAa,OAAO,OAAO,CAAC;AACvD,MAAI,SAAS;AAAE,WAAO;AAAY,aAAS;AAAA,EAAS,WAC3C,WAAW,WAAW,WAAW,WAAW,MAAM,QAAQ,OAAO,KAAK,QAAQ,KAAK,WAAS,MAAM,KAAK,KAAK,MAAM,SAAS,OAAO,GAAG;AAC5I,WAAO;AAAS,aAAS;AAAM,WAAO;AAAA,EACxC;AACA,YAAU,GAAG,IAAI,EAAE,IAAI,KAAK,GAAG,EAAE,SAAS,IAAI,OAAO,EAAE,SAAS,CAAC,IAAI,IAAI,IAAI,EAAE,GAAG,KAAK,EAAE,SAAS,MAAM,QAAQ,IAAI;AACtH;AAiBA,IAAM,SAAS,CAAC,gBAAgB,uBAAuB,iBAAiB,2BAA2B,cAAc;AACjH,IAAM,QAAQ,CAAC,MAAiC,MAAM,CAAC,IAAI,OAAO,IAAI,OAAK,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI;AAGxF,IAAM,qBAAkD;AAAA,EACtD,CAAC,cAAc,cAAc;AAAA,EAAG,CAAC,cAAc,SAAS;AAAA,EAAG,CAAC,cAAc,eAAe;AAAA,EACzF,CAAC,cAAc,eAAe;AAAA,EAAG,CAAC,cAAc,eAAe;AAAA,EAAG,CAAC,cAAc,aAAa;AAChG;AACA,SAAS,WAAW,OAAsB,WAAuD;AAC/F,MAAI,UAAU,qBAAqB;AACjC,UAAM,MAAM,UAAU,MAAM,GAAG,EAAE;AACjC,WAAO,EAAC,OAAO,mBAAmB,KAAK,CAAC,CAAC,KAAK,MAAM,OAAO,KAAK,IAAI,CAAC,KAAK,SAAS,UAAU,KAAI;AAAA,EACnG;AACA,SAAO,QAAQ,EAAC,OAAO,UAAU,MAAK,IAAI,EAAC,OAAO,SAAS,UAAU,KAAI;AAC3E;AAEA,SAAS,UAAU,GAAY,GAAe,IAAmB,WAAmB,GAAsB,KAA+B;AACvI,QAAM,QAAQ,EAAE,CAAC,GAAI,SAAS,EAAE,CAAC,GAAI,YAAY,EAAE,CAAC,GAAI,SAAS,KAAK,IAAI,EAAE,CAAC,GAAI,KAAK;AACtF,QAAM,EAAC,OAAO,SAAQ,IAAI,WAAW,EAAE,cAAc,SAAS;AAC9D,QAAM,OAAkB,EAAC,KAAK,IAAI,OAAO,UAAU,UAAU,OAAO,QAAQ,QAAQ,QAAQ,WAAW,QAAQ,YAAY,GAAG,cAAc,GAAG,UAAS;AACxJ,MAAI,SAAU,MAAK,gBAAgB;AACnC,MAAI,EAAE,KAAM,MAAK,OAAO,EAAE;AAC1B,SAAO;AACT;AAGA,SAAS,YAAY,MAAc,QAAyB;AAC1D,SAAQ,WAAW,KAAK,KAAK,SAAS,cAAc,KAAM,KAAK,SAAS,eAAe,KAAK,KAAK,SAAS,gBAAgB,KACrH,KAAK,SAAS,yBAAyB,KAAK,KAAK,SAAS,gBAAgB,KAAK,KAAK,SAAS,sBAAsB,KACnH,KAAK,SAAS,oBAAoB,KAAM,KAAK,SAAS,eAAe,KAAK,KAAK,SAAS,gBAAgB,KACvG,KAAK,SAAS,eAAe,KAAK,KAAK,SAAS,iBAAiB,KAAO,KAAK,SAAS,aAAa,KAAK,KAAK,SAAS,uBAAuB;AACrJ;AAEA,SAAS,UAAU,GAAY,MAAc,GAAqB;AAChE,QAAM,IAAI,EAAE;AACZ,QAAM,OAAO,KAAK,SAAS,UAAU,GAAG,KAAK,IAAI,KAAK,QAAQ,GAAG,CAAC;AAClE,QAAM,IAAI,EAAE;AACZ,QAAM,QAAQ,MAAkB;AAC9B,QAAI;AAAE,aAAO,KAAK,MAAM,KAAK,SAAS,MAAM,CAAC;AAAA,IAAU,QAAQ;AAAE,QAAE;AAAa,aAAO;AAAA,IAAM;AAAA,EAC/F;AACA,MAAI,MAAM,KAAK,KAAK,SAAS,cAAc,GAAG;AAC5C,UAAM,IAAI,MAAM;AAChB,QAAI,CAAC,EAAG;AACR,UAAM,IAAI,MAAM,EAAE,OAAO,IAAI,EAAE,UAAU,CAAC;AAC1C,MAAE,YAAY,IAAI,EAAE,EAAE,KAAK,IAAI,EAAE,UAAU,KAAK,EAAE;AAClD,MAAE,UAAU,IAAI,EAAE,GAAG;AACrB,MAAE,YAAY,IAAI,EAAE,SAAS,KAAK,IAAI,EAAE,SAAS;AACjD,UAAM,SAAS,EAAE;AACjB,QAAI,MAAM,MAAM,KAAK,cAAc,QAAQ;AACzC,QAAE,QAAQ;AACV,YAAM,MAAM,MAAM,OAAO,QAAQ,IAAI,OAAO,WAAW,CAAC;AACxD,YAAM,QAAQ,MAAM,IAAI,YAAY,IAAI,IAAI,eAAe,CAAC;AAC5D,QAAE,WAAW,IAAI,MAAM,gBAAgB,KAAK,IAAI,EAAE,gBAAgB,KAAK,IAAI,EAAE,cAAc;AAAA,IAC7F;AACA,QAAI,WAAW,UAAU,EAAE,eAAe,gBAAgB,EAAE,eAAe,eAAgB,GAAE,YAAY;AACzG,UAAM,GAAG,KAAK,EAAE,SAAS,CAAC;AAC1B;AAAA,EACF;AACA,MAAI,KAAK,SAAS,eAAe,GAAG;AAClC,UAAM,IAAI,MAAM;AAChB,UAAM,IAAI,KAAK,MAAM,EAAE,OAAO,IAAI,EAAE,UAAU;AAC9C,UAAM,OAAO,KAAK,MAAM,EAAE,IAAI,IAAI,EAAE,OAAO;AAC3C,QAAI,CAAC,KAAK,CAAC,KAAM;AACjB,UAAM,QAAQ,MAAM,KAAK,iBAAiB,GAAG,OAAO,MAAM,KAAK,gBAAgB;AAC/E,UAAM,WAAW,QAAQ,MAAM,KAAK,GAAG,IAAI;AAC3C,UAAM,WAAW,aAAa,QAAQ,aAAa,EAAE;AACrD,QAAI,IAAqB;AACzB,QAAI,QAAQ,SAAU,KAAI;AAAA,aACjB,OAAO;AACd,YAAM,OAAO,EAAE,gBAAgB,EAAE,cAAc,MAAM,GAAG,EAAE,IAAI,MAAM,IAAI,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC;AACtF,UAAI,MAAM,IAAI,CAAC,GAAG,MAAM,KAAK,IAAI,GAAG,IAAI,KAAK,CAAC,CAAE,CAAC;AAAA,IACnD;AACA,QAAI,SAAU,GAAE,gBAAgB;AAChC,QAAI,CAAC,KAAK,EAAE,EAAE,CAAC,KAAM,EAAE,CAAC,KAAM,EAAE,CAAC,KAAM,EAAE,CAAC,GAAK;AAC/C,MAAE,OAAO,IAAI,EAAE,KAAK,GAAG,CAAC;AAExB,UAAM,MAAM,WAAW,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,GAAG,IAAI,EAAE,KAAK;AACvE,aAAS,GAAG,UAAU,GAAG,GAAG,KAAK,EAAE,SAAS,GAAG,OAAO,EAAE,aAAa,EAAE,GAAG,GAAG,GAAG,CAAC;AACjF;AAAA,EACF;AACA,MAAI,KAAK,SAAS,gBAAgB,GAAG;AACnC,UAAM,IAAI,MAAM,GAAG;AACnB,QAAI,MAAM,CAAC,GAAG;AAAE,QAAE,eAAe,IAAI,EAAE,KAAK,KAAK,EAAE;AAAc,UAAI,OAAO,EAAE,QAAQ,SAAU,GAAE,UAAU,EAAE;AAAA,IAAK;AACnH;AAAA,EACF;AACA,MAAI,KAAK,SAAS,yBAAyB,GAAG;AAC5C,UAAM,IAAI,MAAM,GAAG;AACnB,UAAM,WAAW,MAAM,CAAC,KAAK,MAAM,EAAE,eAAe,IAAI,EAAE,kBAAkB;AAC5E,QAAI,YAAY,kBAAkB,UAAU;AAC1C,YAAM,IAAI,SAAS;AACnB,QAAE,OAAO,MAAM,cAAc,MAAM,SAAS,SAAS,MAAM,aAAa,MAAM,aAAa,aAAa;AAAA,IAC1G;AACA;AAAA,EACF;AACA,MAAI,KAAK,SAAS,gBAAgB,GAAG;AACnC,UAAM,IAAI,MAAM,GAAG;AACnB,MAAE,WAAW,MAAM,CAAC,KAAK,OAAO,EAAE,WAAW,EAAE,EAAE,WAAW,iBAAiB;AAC7E;AAAA,EACF;AACA,MAAI,KAAK,SAAS,eAAe,KAAK,KAAK,SAAS,gBAAgB,GAAG;AACrE,UAAM,IAAI,MAAM;AAChB,UAAM,IAAI,KAAK,MAAM,EAAE,OAAO,IAAI,EAAE,UAAU;AAC9C,QAAI,CAAC,KAAK,CAAC,EAAG;AACd,UAAM,OAAO,MAAM,EAAE,IAAI,IAAI,EAAE,OAAO,CAAC;AACvC,MAAE,YAAY,KAAK;AAAA,MAAC,KAAK,IAAI,KAAK,EAAE;AAAA,MAAG,IAAI,KAAK,EAAE,SAAS;AAAA,MAAG,MAAM,OAAO,KAAK,OAAO;AAAA,MACrF,UAAU,EAAE,YAAY,OAAO,EAAE,WAAW,EAAE,EAAE,WAAW,iBAAiB;AAAA,MAAG,SAAS,EAAE;AAAA,IAAO,CAAC;AACpG;AAAA,EACF;AACA,MAAI,KAAK,SAAS,eAAe,KAAK,KAAK,SAAS,iBAAiB,GAAG;AACtE,UAAM,IAAI,MAAM;AAChB,QAAI,CAAC,EAAG;AACR,UAAM,IAAI,MAAM,EAAE,OAAO,IAAI,EAAE,UAAU,CAAC;AAC1C,MAAE,gBAAgB,KAAK,EAAC,KAAK,MAAM,IAAI,KAAK,EAAE,SAAS,GAAG,MAAM,OAAO,EAAE,OAAO,GAAG,UAAU,EAAE,UAAU,SAAS,EAAE,QAAO,CAAC;AAC5H;AAAA,EACF;AACA,MAAI,KAAK,SAAS,aAAa,KAAK,KAAK,SAAS,GAAG,GAAG,EAAE,SAAS,uBAAuB,GAAG;AAC3F,UAAM,IAAI,MAAM;AAChB,QAAI,CAAC,EAAG;AACR,UAAM,IAAI,MAAM,EAAE,OAAO,IAAI,EAAE,UAAU,CAAC;AAC1C,MAAE,YAAY,KAAK,EAAC,KAAK,MAAM,IAAI,KAAK,EAAE,SAAS,GAAG,MAAM,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU,IAAI,UAAU,EAAE,UAAU,SAAS,EAAE,QAAO,CAAC;AACrJ;AAAA,EACF;AACA,MAAI,KAAK,SAAS,sBAAsB,GAAG;AACzC,UAAM,IAAI,MAAM;AAChB,UAAM,IAAI,KAAK,MAAM,EAAE,OAAO,IAAI,EAAE,UAAU;AAC9C,UAAM,KAAK,IAAI,IAAI,EAAE,WAAW,IAAI;AACpC,QAAI,KAAK,KAAK,MAAM,MAAM,EAAE,KAAK,EAAG,GAAE,QAAQ,IAAI,IAAI,EAAC,IAAI,KAAK,EAAE,SAAS,GAAG,OAAO,EAAE,MAAK,CAAC;AAC7F;AAAA,EACF;AACA,MAAI,KAAK,SAAS,oBAAoB,GAAG;AAEvC,UAAM,KAAK,KAAK,YAAY,4BAA4B;AACxD,QAAI,MAAM,GAAG;AACX,YAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,QAAQ,IAAI,KAAK;AACnD,UAAI,MAAM,MAAO,GAAE,UAAU,IAAI,KAAK,SAAS,UAAU,OAAO,GAAG,CAAC;AAAA,IACtE;AAAA,EACF;AACF;AAEA,SAAS,YAAY,GAAY,GAAqB;AACpD,QAAM,IAAI,EAAE;AAEZ,aAAW,MAAM,EAAE,WAAW;AAC5B,UAAM,SAAS,EAAE,QAAQ,IAAI,EAAE;AAC/B,UAAM,IAAI,SAAS,MAAM,OAAO,KAAK,IAAI;AACzC,QAAI,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC,KAAM,EAAE,CAAC,KAAM,EAAE,CAAC,KAAM,EAAE,CAAC,MAAO,EAAE,OAAO,IAAI,EAAE,KAAK,GAAG,CAAC,EAAG;AACvF,UAAM,OAAO,UAAU,GAAG,GAAG,OAAO,IAAI,OAAO,KAAK,IAAI,KAAK,OAAO,EAAE,EAAE,YAAY,IAAI,IAAI,GAAG,oBAAoB,EAAE,EAAE;AACvH,SAAK,aAAa;AAClB,MAAE,MAAM,KAAK,IAAI;AACjB,UAAM,GAAG,OAAO,EAAE;AAAA,EACpB;AAGA,QAAM,OAAO,EAAE,YAAY,SAAS,EAAE,cAAc,EAAE;AACtD,QAAM,SAAS,EAAE,QAAQ,4BAA4B,EAAE,YAAY,8BAA8B;AACjG,QAAM,OAAO,oBAAI,IAAY;AAC7B,OAAK,QAAQ,CAAC,GAAG,MAAM;AACrB,QAAI,EAAE,KAAK;AAAE,UAAI,KAAK,IAAI,EAAE,GAAG,EAAG;AAAQ,WAAK,IAAI,EAAE,GAAG;AAAA,IAAG;AAC3D,UAAM,MAAM,SAAS,EAAE,SAAS,IAAI,EAAE,OAAO,CAAC;AAC9C,QAAI,OAAQ,QAAO,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,SAAS,YAAY,QAAQ,EAAE;AAC5E,QAAI,EAAE,SAAU,QAAO,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,SAAS,YAAY,mCAAmC,EAAE;AAC3G,UAAM,IAAI,aAAa,eAAe,EAAE,IAAI,CAAC;AAC7C,cAAU,GAAG,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI;AAAA,EAC7D,CAAC;AAED,QAAM,UAAU,EAAE,MAAM,OAAO,OAAK,EAAE,OAAO,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,KAAM,EAAE,EAAG;AAC/E,MAAI,IAAI,GAAG,WAA0B;AACrC,aAAW,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,OAAO,EAAE,MAAM,MAAM,EAAE,MAAM,EAAE,GAAG;AACxE,WAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,EAAG,OAAQ,EAAE,MAAM,GAAI,YAAW,QAAQ,GAAG,EAAG;AACtF,MAAE,SAAS;AAAU,MAAE,iBAAiB,WAAW,WAAW;AAC9D,MAAE,QAAQ,QAAQ,CAAC,GAAG,SAAS;AAAM,MAAE,gBAAgB,EAAE,QAAQ,WAAW;AAAA,EAC9E;AACF;AAIA,SAAS,OAAO,GAAY,MAAoB;AAC9C,MAAI;AACJ,MAAI;AAAE,QAAI,KAAK,MAAM,KAAK,SAAS,MAAM,CAAC;AAAA,EAAU,QAAQ;AAAE,MAAE,MAAM;AAAa;AAAA,EAAQ;AAC3F,QAAM,IAAI,EAAE,OAAO,KAAK,KAAK,EAAE,SAAS;AACxC,MAAI,EAAE,SAAS,WAAW;AACxB,MAAE,YAAY,IAAI,EAAE,EAAE,KAAK,EAAE;AAC7B,MAAE,UAAU,IAAI,EAAE,GAAG;AACrB,UAAM,GAAG,EAAE;AACX;AAAA,EACF;AACA,MAAI,EAAE,SAAS,gBAAgB;AAC7B,MAAE,eAAe,IAAI,EAAE,OAAO,KAAK,EAAE;AACrC,MAAE,kBAAkB,IAAI,EAAE,QAAQ,KAAK,EAAE;AACzC;AAAA,EACF;AACA,MAAI,EAAE,SAAS,aAAa,CAAC,MAAM,EAAE,OAAO,EAAG;AAC/C,QAAM,IAAI,EAAE;AACZ,MAAI,EAAE,SAAS,eAAe,MAAM,EAAE,KAAK,GAAG;AAC5C,UAAM,IAAI,EAAE;AACZ,UAAM,OAAkB;AAAA,MACtB,KAAK,MAAM,EAAE,SAAS,IAAI,OAAO,EAAE,EAAE,CAAC;AAAA,MAAI;AAAA,MAAI,OAAO,IAAI,EAAE,KAAK,KAAK,EAAE;AAAA,MAAc,UAAU,IAAI,EAAE,QAAQ,KAAK,EAAE;AAAA,MACpH,OAAO,IAAI,EAAE,KAAK;AAAA,MAAG,QAAQ,IAAI,EAAE,MAAM;AAAA,MAAG,WAAW,IAAI,EAAE,SAAS;AAAA,MAAG,YAAY,IAAI,EAAE,UAAU;AAAA,MAAG,cAAc;AAAA,MAAG,WAAW;AAAA,IACtI;AACA,QAAI,KAAK,QAAQ,KAAK,SAAS,KAAK,YAAY,KAAK,aAAa,EAAG,UAAS,GAAG,IAAI;AAAA,QAChF,UAAS,GAAG,KAAK,OAAO,KAAK,QAAQ;AAC1C;AAAA,EACF;AACA,MAAI,EAAE,SAAS,QAAQ;AACrB,UAAM,IAAI,aAAa,OAAO,EAAE,OAAO,CAAC;AACxC,cAAU,GAAG,MAAM,EAAE,SAAS,IAAI,OAAO,EAAE,EAAE,CAAC,IAAI,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI;AAAA,EAC3F;AACF;AAIA,IAAM,QAAQ,IAAI,OAAO;AAEzB,IAAM,WAAW,MAAM,OAAO;AAE9B,IAAI,eAA8B;AAG3B,SAAS,YAAY,MAAc,SAAqC;AAC7E,QAAM,KAAK,GAAG,SAAS,MAAM,GAAG;AAChC,MAAI;AACF,UAAM,OAAO,GAAG,UAAU,EAAE;AAC5B,UAAM,IAAI,YAAY,MAAM,SAAS,KAAK,MAAM,KAAK,OAAO;AAC5D,UAAM,QAAoB;AAAA,MAAC,QAAQ;AAAA,MAAG,eAAe;AAAA,MAAM,MAAM;AAAA,MAAW,UAAU;AAAA,MACpF,SAAS,oBAAI,IAAI;AAAA,MAAG,WAAW,oBAAI,IAAI;AAAA,MAAG,QAAQ,oBAAI,IAAI;AAAA,MAAG,aAAa,CAAC;AAAA,MAAG,iBAAiB,CAAC;AAAA,IAAC;AACnG,UAAM,WAAW,KAAK,SAAS,YAAY,GAAG,WAAW,oBAAI,IAAY;AACzE,UAAM,SAAS,YAAY,WAAW,CAAC,SAAiB,WAAW,GAAG,MAAM,UAAU,QAAQ,IAC1F,YAAY,UAAU,CAAC,SAAiB,UAAU,GAAG,MAAM,KAAK,IAChE,CAAC,SAAiB,OAAO,GAAG,IAAI;AAEpC,QAAI,SAAS,iBAAiB,OAAO,YAAY,KAAK;AACtD,QAAI,SAAS,GAAG,WAAW,GAAG,WAAW;AACzC,eAAS;AACP,UAAI,WAAW,OAAO,QAAQ;AAE5B,YAAI,YAAY,WAAW,CAAC,YAAY,OAAO,SAAS,UAAU,GAAG,KAAK,IAAI,QAAQ,GAAG,CAAC,GAAG,MAAM,MAAM,GAAG;AAAE,qBAAW;AAAM,mBAAS;AAAG,gBAAM;AAAA,QAAU,WAClJ,OAAO,UAAU,UAAU;AAAE,qBAAW;AAAM,mBAAS;AAAG,YAAE,MAAM;AAAA,QAAa,OACnF;AAAE,gBAAM,SAAS,OAAO,YAAY,OAAO,SAAS,CAAC;AAAG,iBAAO,KAAK,QAAQ,GAAG,GAAG,MAAM;AAAG,mBAAS;AAAQ,cAAI,OAAO,UAAU,IAAI,MAAO,gBAAe;AAAA,QAAQ;AAAA,MAC1K;AACA,YAAM,IAAI,GAAG,SAAS,IAAI,QAAQ,QAAQ,OAAO,SAAS,QAAQ,QAAQ;AAC1E,kBAAY;AACZ,YAAM,MAAM,SAAS;AACrB,UAAI,QAAQ;AACZ,iBAAS;AACP,cAAM,KAAK,OAAO,QAAQ,IAAI,KAAK;AACnC,YAAI,KAAK,KAAK,MAAM,IAAK;AACzB,YAAI,SAAU,YAAW;AAAA,iBAChB,KAAK,MAAO,QAAO,OAAO,SAAS,OAAO,EAAE,CAAC;AACtD,gBAAQ,KAAK;AAAA,MACf;AACA,UAAI,MAAM,GAAG;AACX,YAAI,CAAC,YAAY,QAAQ,IAAK,QAAO,OAAO,SAAS,OAAO,GAAG,CAAC;AAChE;AAAA,MACF;AACA,aAAO,WAAW,GAAG,OAAO,GAAG;AAC/B,eAAS,MAAM;AAAA,IACjB;AACA,QAAI,YAAY,QAAS,aAAY,GAAG,KAAK;AAC7C,QAAI,YAAY,YAAY,CAAC,YAAY,SAAS,KAAM,GAAE,MAAM,aAAa,CAAC,GAAG,QAAQ;AACzF,QAAI,EAAE,MAAM,SAAU,YAAW,KAAK,EAAE,MAAM,QAAS,GAAE,OAAO;AAChE,WAAO,EAAE;AAAA,EACX,UAAE;AACA,OAAG,UAAU,EAAE;AAAA,EACjB;AACF;;;AD7hBA,IAAM,YAAY,CAAC,UAAoB,iBAAiB,SAAS,yBAAyB,KAAK,MAAM,OAAO,IAAI,MAAM,UAAU;AA+EzH,SAAS,gBAAsB;AACpC,MAAI,CAAC,WAAY,OAAM,IAAI,MAAM,6BAA6B;AAC9D,QAAM,OAAO;AACb,OAAK,GAAG,WAAW,CAAC,QAAiB;AACnC,QAAI;AAAE,WAAK,YAAY,EAAC,OAAO,YAAY,IAAI,MAAM,IAAI,OAAO,EAAC,CAAC;AAAA,IAAG,SAAS,OAAO;AAAE,WAAK,YAAY,EAAC,OAAO,UAAU,KAAK,EAAC,CAAC;AAAA,IAAG;AAAA,EACtI,CAAC;AACH;;;AQ7GA,cAAc;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|