@khalilgharbaoui/opencode-claude-code-plugin 0.27.1 → 0.27.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +6 -64
- package/dist/index.js +772 -732
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/claude-code-language-model.ts","../src/logger.ts","../src/todo-ledger.ts","../src/tool-mapping.ts","../src/host-tools.ts","../src/account-failover.ts","../src/accounts.ts","../src/cli-events.ts","../src/plan-mode-question.ts","../src/cli-version.ts","../src/session-manager.ts","../src/proxy-mcp.ts","../src/tmp.ts","../src/proxy-broker.ts","../src/compression-store.ts","../src/side-question.ts","../src/btw-command.ts","../src/startup-diagnostics.ts","../src/mcp-bridge.ts","../src/runtime-status.ts","../src/doctor.ts","../src/turn-stats.ts","../src/message-builder.ts","../src/agent-models.ts","../src/models.ts","../src/skill-bridge.ts","../src/claude-session-wrapper.ts","../src/claude-session-bun.ts","../src/cleanup-stale.ts","../src/v2-client.ts","../src/v2.ts","../src/index.ts"],"sourcesContent":["import type {\n LanguageModelV3,\n LanguageModelV3CallOptions,\n LanguageModelV3Content,\n LanguageModelV3FinishReason,\n LanguageModelV3StreamPart,\n LanguageModelV3Usage,\n SharedV3Warning,\n} from \"@ai-sdk/provider\"\nimport { generateId } from \"@ai-sdk/provider-utils\"\nimport type {\n ClaudeCodeConfig,\n ControlRequestBehavior,\n ClaudeStreamMessage,\n ReasoningEffort,\n} from \"./types.js\"\nimport { mapTool, isWebSearchTool, isWebSearchHandledByCli } from \"./tool-mapping.js\"\nimport { createHostToolPartTranslator, translateStreamForHost } from \"./host-tools.js\"\nimport { applyTaskCreateToolResult } from \"./todo-ledger.js\"\nimport {\n getClaudeUserMessage,\n shouldStripContextReminders,\n} from \"./message-builder.js\"\nimport { resolveAgentEffort, resolveAgentModel } from \"./agent-models.js\"\nimport { parseSideQuestion, requestSideQuestion, collectSideQuestionHistory, SIDE_QUESTION_USAGE, type SideQuestionResult } from \"./side-question.js\"\nimport { BTW_NO_SESSION_MESSAGE, registerAsideSink, takeSideQuestionAnswer } from \"./btw-command.js\"\nimport {\n describeResultFailure,\n formatResultFailureNote,\n formatStreamTimeoutNote,\n isRateLimitRejected,\n parseRateLimitEvent,\n reportCompactBoundary,\n reportConversationReset,\n reportRateLimitEvent,\n reportSystemInit,\n} from \"./cli-events.js\"\nimport {\n DEFAULT_ACCOUNT,\n accountConfigDirPath,\n normalizeAccountName,\n} from \"./accounts.js\"\nimport {\n accountBlockKind,\n buildFailoverContinuationPrompt,\n consumeAccountFailoverAnswer,\n createAccountFailoverQuestionCall,\n describeAccountBlock,\n failoverCandidates,\n failoverUntil,\n formatAccountBlockNote,\n formatFailoverNote,\n formatFailoverStopNote,\n isAccountFailoverQuestionActive,\n isAccountLimitError,\n resolveFailoverSpawn,\n setAccountOverride,\n type AccountBlockKind,\n type FailoverSpawn,\n} from \"./account-failover.js\"\nimport { DOCTOR_COMMAND, buildDoctorReport, parseDoctorCommand } from \"./doctor.js\"\nimport {\n extractTurnStats,\n formatTurnStatsBlock,\n turnStatsLogPayload,\n} from \"./turn-stats.js\"\nimport { resolveSkillPluginDirs } from \"./skill-bridge.js\"\nimport { parseModelId } from \"./models.js\"\nimport {\n QUESTION_TOOL_NAME,\n consumeExitPlanModeQuestionResult,\n createExitPlanModeQuestionCall,\n isPlanModeQuestionActive,\n type QuestionToolCall,\n} from \"./plan-mode-question.js\"\nimport { bridgeOpencodeMcp, type RuntimeMcpStatus } from \"./mcp-bridge.js\"\nimport {\n getRuntimeMcpStatus,\n fetchOpencodeToolList,\n fetchSessionParentId,\n type OpencodeToolListItem,\n resolveSpawnCwdForSession,\n fetchSessionRunState,\n settleSessionRunState,\n} from \"./runtime-status.js\"\nimport {\n getActiveProcess,\n setActiveProcess,\n spawnClaudeProcess,\n buildCliArgs,\n setClaudeSessionId,\n getClaudeSessionId,\n deleteClaudeSessionId,\n deleteActiveProcess,\n deleteActiveProcessAndWait,\n respawnActiveProcess,\n resolveIdleProcessTimeoutMs,\n scheduleIdleProcessEviction,\n noteTurnStarted,\n isTurnInFlight,\n interruptTurn,\n takeUnattendedLines,\n describeChildCrash,\n claudeSpawnEnv,\n isClaudeThinkingDisabled,\n sessionKey,\n effortSessionKey,\n invalidateOtherEffortSessions,\n describeSessionKey,\n} from \"./session-manager.js\"\nimport { spawnInteractiveProcess } from \"./claude-session-wrapper.js\"\nimport {\n clearCompression,\n consumeCompressionRestart,\n getCompressionSummary,\n storeCompressionSummary,\n} from \"./compression-store.js\"\nimport { log } from \"./logger.js\"\nimport { detectCliVersion } from \"./cli-version.js\"\nimport {\n createProxyMcpServer,\n resolveDisallowedTools,\n resolveProxyOpencodeToolDefs,\n resolveMcpProxyToolDefs,\n DEFAULT_PROXY_TOOLS,\n overlayTaskProxyDescription,\n overlayQuestionProxyDescription,\n filterQuestionProxyByOpencodeSupport,\n PROXY_TOOL_PREFIX,\n TASK_BATCH_TOOL_NAME,\n taskBatchTasks,\n taskBatchChildToolCallId,\n formatTaskBatchResults,\n setProxyDeadlineGuard,\n type McpProxyToolResolution,\n type ModelToolEntry,\n type ProxyMcpServer,\n type ProxyToolCall,\n type ProxyToolDef,\n type ProxyToolInterceptor,\n type ProxyToolResult,\n} from \"./proxy-mcp.js\"\nimport {\n findPendingProxyCall,\n getPendingProxyCalls,\n isPendingProxyCallChannelClosed,\n markPendingProxyCallEmitted,\n onPendingProxyCall,\n queuePendingProxyCall,\n rejectAllPendingProxyCallsForSession,\n rejectPendingProxyCallById,\n resolvePendingProxyCallById,\n type PendingProxyCall,\n} from \"./proxy-broker.js\"\nimport { readFileSync, writeFileSync } from \"node:fs\"\nimport { unlink } from \"node:fs/promises\"\nimport { homedir, tmpdir } from \"node:os\"\nimport { randomUUID } from \"node:crypto\"\nimport { dirname, join } from \"node:path\"\n\n/**\n * Whether opencode is still serving a proxied call whose deadline just passed.\n * Only a call opencode was actually handed (`emitted`), in a session opencode\n * positively reports `busy`, is kept: that is a permission prompt still open\n * or the tool itself still running. Anything else, including `unknown` (no\n * SDK client, no status route, the no-affinity `default` session), ends at\n * the deadline exactly as before.\n */\nexport async function isProxyCallStillServed(callId: string): Promise<boolean> {\n const pending = findPendingProxyCall(callId)\n if (!pending || pending.emitted !== true) return false\n const session = describeSessionKey(pending.sessionKey).session\n return (await fetchSessionRunState(session)) === \"busy\"\n}\n\nsetProxyDeadlineGuard(({ callId }) => isProxyCallStillServed(callId))\n\n/**\n * Default model used for opencode `/compact`. Haiku 4.5 is fast\n * (~150 tok/s), has a hard 8k output cap that bounds latency, and is a\n * strong structured summarizer. Override per-project via the\n * `compactionModel` provider setting in opencode.json / opencode.jsonc,\n * or per-run via the `CLAUDE_CODE_COMPACTION_MODEL` env var (env wins).\n */\nexport const DEFAULT_COMPACTION_MODEL = \"claude-haiku-4-5\"\n\n/**\n * Pick the model used to handle /compact. Precedence:\n * 1. `CLAUDE_CODE_COMPACTION_MODEL` env var (per-process override)\n * 2. `configured` argument (the `compactionModel` provider setting)\n * 3. `DEFAULT_COMPACTION_MODEL`\n *\n * Exported as a free function so it can be unit-tested without\n * instantiating the language model class.\n */\nexport function resolveCompactionModel(configured?: string): string {\n const env = process.env.CLAUDE_CODE_COMPACTION_MODEL?.trim()\n if (env) return env\n const trimmed = configured?.trim()\n if (trimmed) return trimmed\n return DEFAULT_COMPACTION_MODEL\n}\n\n/**\n * Resolve the session affinity token for a given LLM call. The affinity\n * token is part of the session key in session-manager so two different\n * opencode sessions sharing the same cwd+model still get separate Claude\n * CLI processes.\n *\n * Priority:\n * 1. `x-session-affinity` request header (primary — opencode sets it for\n * third-party providers in packages/opencode/src/session/llm.ts).\n * 2. `opencodeSessionID` inside `providerOptions` (injected by the\n * `chat.params` hook in index.ts). Covers cases where the header is\n * absent: provider switch mid-session, title synthesis paths, older\n * opencode versions. opencode wraps `output.options` under the\n * providerID before passing it to the language model, so we look up\n * both the configured provider key and the canonical `\"claude-code\"`.\n * 3. `\"default\"` — safe fallback when neither source is available.\n *\n * Exported as a free function so it can be unit-tested without\n * instantiating the language model class.\n */\nexport function resolveSessionAffinity(\n headers: Record<string, string | undefined> | undefined,\n providerOptions: Record<string, unknown> | undefined,\n providerKey: string,\n): string {\n if (headers) {\n for (const key of Object.keys(headers)) {\n if (key.toLowerCase() === \"x-session-affinity\") {\n const v = headers[key]\n if (typeof v === \"string\" && v.length > 0) return v\n }\n }\n }\n if (providerOptions) {\n const bag =\n (providerOptions as any)[providerKey] ??\n (providerOptions as any)[\"claude-code\"]\n const sid = bag?.opencodeSessionID\n if (typeof sid === \"string\" && sid.length > 0) return sid\n }\n return \"default\"\n}\n\n/**\n * The opencode agent this call runs for, which is how compaction and title\n * calls are told apart from ordinary turns.\n *\n * 1. `opencodeAgent` in providerOptions, written by V1's `chat.params`\n * hook. Checked first so opencode 1.x behaves exactly as it always has.\n * 2. The `x-opencode-agent` request header, written by the V2 entrypoint's\n * `model.request` hook (src/v2.ts), which can set headers but not\n * provider options.\n */\nexport function resolveOpencodeAgent(\n headers: Record<string, string | undefined> | undefined,\n providerOptions: Record<string, unknown> | undefined,\n providerKey: string,\n): string | undefined {\n if (providerOptions) {\n const bag =\n (providerOptions as any)[providerKey] ??\n (providerOptions as any)[\"claude-code\"]\n const agent = bag?.opencodeAgent\n if (typeof agent === \"string\") return agent\n }\n if (headers) {\n for (const key of Object.keys(headers)) {\n if (key.toLowerCase() === \"x-opencode-agent\") {\n const value = headers[key]\n if (typeof value === \"string\" && value.length > 0) return value\n }\n }\n }\n return undefined\n}\n\n/** An `AbortSignal.reason` as loggable text: its name and message, or its type. */\nexport function describeAbortReason(reason: unknown): string {\n if (reason === undefined) return \"undefined\"\n if (reason instanceof Error) return `${reason.name}: ${reason.message}`\n if (typeof reason === \"string\") return reason\n try {\n return JSON.stringify(reason) ?? typeof reason\n } catch {\n return typeof reason\n }\n}\n\n/**\n * Stream delta types we handle explicitly. `signature_delta` is listed as\n * known-and-silent: it carries encrypted thinking-block signatures that\n * are opaque to clients (the server uses them to reconstitute thinking\n * across turns), so there's nothing for us to do but ignore it.\n */\nconst KNOWN_DELTA_TYPES = new Set([\n \"thinking_delta\",\n \"text_delta\",\n \"input_json_delta\",\n \"signature_delta\",\n])\n\n/**\n * True if the prompt has any user-side content after the last assistant\n * message (text, tool_result, or any user role entry). False when the\n * prompt ends with an assistant message and there is nothing for Claude\n * to respond to — opencode sometimes iterates the agent loop one more\n * time after a turn naturally completed; without short-circuiting we'd\n * spawn Claude CLI on an empty turn and the model would reply with a\n * stub like \"Did you mean to send a message?\".\n */\nexport function hasNewUserContent(\n prompt: LanguageModelV3CallOptions[\"prompt\"],\n): boolean {\n for (let i = prompt.length - 1; i >= 0; i--) {\n const msg = prompt[i]\n if (msg.role === \"assistant\") return false\n // Tool-result turns from opencode's outer loop arrive in `tool`-role\n // messages (AI SDK V3 shape). Treat any tool-result part as new\n // content so the short-circuit doesn't drop turns where opencode is\n // delivering the result for a still-pending proxy MCP call — letting\n // that fire `stop` is what was forcing the user to press \"continue\".\n if (msg.role === \"tool\") {\n const content: any = msg.content\n if (Array.isArray(content)) {\n for (const part of content as any[]) {\n if (part?.type === \"tool-result\") return true\n }\n }\n continue\n }\n if (msg.role !== \"user\") continue\n const content: any = msg.content\n if (typeof content === \"string\") {\n if (content.trim()) return true\n continue\n }\n if (Array.isArray(content)) {\n for (const part of content as any[]) {\n if (part.type === \"text\" && part.text && part.text.trim()) return true\n if (part.type === \"tool-result\") return true\n // Image/file-only user turns count as new input — without this the\n // short-circuit drops them as if the turn were empty.\n if (part.type === \"image\" || part.type === \"file\") return true\n }\n }\n }\n return false\n}\n\nconst AUTO_CONTINUE_MAX_ATTEMPTS = 8\nconst AUTO_CONTINUE_MAX_ELAPSED_MS = 10 * 60 * 1000\nconst AUTO_CONTINUE_NO_PROGRESS_LIMIT = 2\nconst PROXY_RESULT_BOUNDARY_GRACE_MS = 250\n// How long a turn that lost its child waits for that child's exit status\n// before reporting the crash without one.\nconst CHILD_EXIT_STATUS_GRACE_MS = 250\n\nconst AUTO_CONTINUE_PROMPT =\n \"Continue the task from where you stopped. Do not summarize; keep working until the requested task is complete, you need clarification, or you hit a real blocker.\"\n\n/** One per-turn snapshot of opencode's live tool registry. */\ninterface LiveToolInfo {\n /** False when nothing answered (no SDK client, fetch failed). */\n resolved: boolean\n taskDescription: string | undefined\n questionDescription: string | undefined\n hasQuestion: boolean\n /**\n * The raw registry entries behind the fields above, so `proxyOpencodeTools`\n * can be resolved from the same single fetch rather than a second one.\n */\n items?: OpencodeToolListItem[]\n}\n\ninterface AutoContinueState {\n enabled: boolean | \"smart\" | undefined\n attempts: number\n startedAt: number\n noProgressCount: number\n lastSignature?: string\n aborted?: boolean\n /**\n * Latched true once AskUserQuestion is rendered this turn. Auto-continue\n * must never fire afterwards: the model has handed control to the operator\n * and is waiting for a real reply. Without this, a short trailing text after\n * the question (one that doesn't trip looksLikeQuestion) would let the turn\n * look \"incomplete\", and the auto-continue nudge would make the model\n * proceed on its own — which the operator sees as the question being\n * answered/cancelled without them ever interacting.\n */\n sawAskUserQuestion?: boolean\n}\n\ninterface AutoContinueSnapshot {\n text: string\n /**\n * Text of the most recent assistant text block only. Used for final-answer\n * detection so mid-task narration like \"Implementing now. Updated the\n * search index.\" in an earlier block doesn't trip the keyword regex.\n */\n lastVisibleText: string\n hadReasoning: boolean\n hadToolActivity: boolean\n hadProxyActivity: boolean\n isError?: boolean\n /**\n * Protocol-level stop signal from the Claude API (forwarded by Claude\n * CLI). When present and non-empty, we trust it as authoritative — the\n * model itself signaled why the turn ended (`end_turn`, `max_tokens`,\n * `stop_sequence`, `refusal`, `pause_turn`, `tool_use`, etc.) — and stop\n * without running the keyword regex. The heuristic only runs as a\n * fallback when `stop_reason` is missing (older CLI versions, abrupt\n * termination).\n */\n stopReason?: string | null\n now?: number\n}\n\n/**\n * A compaction turn must never be nudged to continue. `AUTO_CONTINUE_PROMPT`\n * says \"Do not summarize; keep working\", the exact inverse of what `/compact`\n * is for, and continuation reopens the same stream rather than closing it, so\n * the non-summary text would land inside what opencode stores as the session\n * summary. This was unreachable while every `stop_reason` ended the turn;\n * truncation-continue made a summary that hits the output cap reach it.\n * Exported so the wiring is testable, since the state itself is built inline\n * in `doStream`.\n */\nexport function autoContinueEnabledFor(\n compactionMode: boolean,\n configured: boolean | \"smart\" | undefined,\n): boolean | \"smart\" | undefined {\n return compactionMode ? false : configured\n}\n\n/**\n * Stop reasons that mean \"cut off\", not \"done\". Anthropic sends `max_tokens`;\n * `max_output_tokens` is accepted as a defensive alias so a rename upstream\n * degrades to today's behaviour rather than silently mis-reading a real stop.\n */\nfunction isTruncationStopReason(stopReason: string): boolean {\n return stopReason === \"max_tokens\" || stopReason === \"max_output_tokens\"\n}\n\ninterface AutoContinueDecision {\n continue: boolean\n reason: string\n}\n\nfunction normalizeVisibleText(text: string): string {\n return text.replace(/\\s+/g, \" \").trim()\n}\n\n/** Tool names that mean \"ask the human a question\" (CLI casing variants). */\nexport function isAskUserQuestionTool(name: string | undefined): boolean {\n if (!name) return false\n const n = name.toLowerCase()\n return n === \"askuserquestion\" || n === \"ask_user_question\"\n}\n\n/**\n * Deny message returned to the model when it invokes AskUserQuestion.\n *\n * AskUserQuestion is denied (see controlRequestBehaviorForTool) so the\n * headless CLI cannot self-answer against an empty TTY. The question is\n * already rendered to the operator by formatAskUserQuestion, so this text\n * tells the model to stop and wait — unconditionally. Earlier versions\n * offered an \"if this is non-interactive, proceed with a reasonable guess\"\n * escape hatch, but the model could not reliably tell interactive opencode\n * from a headless run and routinely took it, so questions appeared to be\n * skipped (issue #8). Stopping is the correct default for opencode; a\n * headless run simply ends the turn with the question as its final output.\n */\nconst ASK_USER_QUESTION_DENY_MESSAGE =\n \"Your question and its options have already been presented to the\" +\n \" operator verbatim. This is NOT a cancellation or a refusal — the\" +\n \" operator simply has not answered yet. Stop now: end your turn without\" +\n \" calling any more tools and without answering the question yourself. Do\" +\n \" not say the question was cancelled, skipped, or declined, and do not\" +\n \" guess, assume, or proceed on their behalf. Wait for the operator's\" +\n \" reply, which arrives as the next user message.\"\n\n/** Build the deny message for an auto-denied control request. */\nexport function denyMessageForTool(\n toolName: string | undefined,\n configuredDenyMessage?: string,\n): string {\n if (isAskUserQuestionTool(toolName)) return ASK_USER_QUESTION_DENY_MESSAGE\n return (\n configuredDenyMessage ??\n `Denied by opencode-claude-code policy for tool ${toolName}`\n )\n}\n\n/**\n * Render Claude Code's `AskUserQuestion` tool input as visible markdown.\n *\n * This is the fallback path used when the `Question` proxy is off or the\n * opencode build lacks the `question` registry entry. When the proxy is\n * enabled, `AskUserQuestion` is disabled via `--disallowedTools` and the\n * model calls `mcp__opencode_proxy__question` instead (opencode's native\n * `question` tool renders the TUI form). Here, the question + every\n * option is rendered as readable assistant text and the user answers in\n * the next turn — same approach as the `ExitPlanMode` handling. The\n * previous behavior collapsed the whole payload to a single faint\n * `_Asking: <q>_` line, dropping all options and any question past the\n * first.\n */\nfunction formatAskUserQuestion(input: Record<string, unknown>): string {\n const anyInput = input as any\n const questions: any[] = Array.isArray(anyInput?.questions)\n ? anyInput.questions\n : []\n\n if (questions.length === 0) {\n const single = anyInput?.question ?? anyInput?.text\n const q =\n typeof single === \"string\" && single.trim() ? single.trim() : \"Question?\"\n return `\\n\\n**${q}**\\n\\n_Reply with your answer to continue._\\n\\n`\n }\n\n const out: string[] = [\"\\n\\n\"]\n const multiQ = questions.length > 1\n questions.forEach((q, i) => {\n const text =\n (typeof q?.question === \"string\" && q.question.trim()) ||\n (typeof q?.text === \"string\" && q.text.trim()) ||\n \"Question?\"\n const header =\n typeof q?.header === \"string\" && q.header.trim() ? q.header.trim() : \"\"\n out.push(`**${multiQ ? `${i + 1}. ` : \"\"}${text}**`)\n if (header) out.push(` _(${header})_`)\n out.push(\"\\n\\n\")\n\n const options: any[] = Array.isArray(q?.options) ? q.options : []\n options.forEach((opt, j) => {\n const label =\n (typeof opt?.label === \"string\" && opt.label.trim()) ||\n (typeof opt === \"string\" && opt.trim()) ||\n `Option ${j + 1}`\n const desc =\n typeof opt?.description === \"string\" && opt.description.trim()\n ? ` — ${opt.description.trim()}`\n : \"\"\n out.push(`${j + 1}. **${label}**${desc}\\n`)\n })\n\n out.push(\n q?.multiSelect === true\n ? \"\\n_Select one or more — reply with the numbers or labels._\\n\\n\"\n : \"\\n_Reply with your choice (the number or label)._\\n\\n\",\n )\n })\n return out.join(\"\")\n}\n\nfunction looksLikeQuestion(text: string): boolean {\n const normalized = normalizeVisibleText(text).toLowerCase()\n if (!normalized) return false\n // v0.4.10 tweak 5a: '?' anywhere in the last block, not just trailing.\n // Catches long answers that pose a question mid-text then list options\n // and end with a period. FP risk on inline code (`result?.value`) is\n // accepted — cost is one extra \"continue\" press, in the safe direction.\n if (normalized.includes(\"?\")) return true\n // v0.4.11 additions: ready when you are / standing by / i'll stand by /\n // let me know when. These are awaiting-input idioms with no '?'. The\n // \"standing by\" addition has historical significance — it's the exact\n // stub phrase Claude CLI emits on empty turns that commit 49345e3 was\n // designed to suppress at the message-builder layer. This adds a second\n // line of defense at the model-output layer for cases where the model\n // organically produces the same idiom.\n //\n // v0.4.12 additions: over to you / your turn / all yours / let me know\n // how / i'm here. Defensive coverage of soft-proceed idioms in the\n // model's vocabulary. \"i'm here\" has the highest FP risk (\"I'm here to\n // help with X\" is a conversational opener) but cost of FP is one extra\n // continue press — safe direction.\n return /\\b(please confirm|can you confirm|should i|would you like|do you want|which option|choose|pick one|need your|need you to|what would you like|let me know if|let me know whether|let me know what|let me know when|let me know how|if you'?d like|if you want to|tell me if|tell me which|tell me whether|say (?:go|yes|no)|push back|sign off|sounds? (?:good|right)|your call|your move|your turn|over to you|all yours|up to you|ready to (?:ship|go|proceed|merge)|ready (?:when|whenever|once|if) you|standing by|i'?ll stand ?by|i'?m here|happy to (?:ship|go|proceed|merge))\\b/.test(normalized)\n}\n\nfunction looksLikeBlocker(text: string): boolean {\n const normalized = normalizeVisibleText(text).toLowerCase()\n if (!normalized) return false\n // v0.4.10 tweak 3: 'needs your' / 'needs you to' / 'action required'\n // are intent-equivalent to 'requires your' but use the verb-with-s form.\n return /\\b(blocked|blocker|cannot proceed|can't proceed|unable to proceed|need clarification|need more information|permission denied|failed and needs|requires your|needs your|needs you to|action required|manual step|required from you)\\b/.test(normalized)\n}\n\nfunction looksLikeFinalAnswer(text: string): boolean {\n const normalized = normalizeVisibleText(text).toLowerCase()\n if (looksLikeQuestion(normalized) || looksLikeBlocker(normalized)) return false\n // v0.4.15: strong-completion phrases bypass the 30-char length floor.\n // These are unambiguous end-of-turn signals at any text length — even\n // a short standalone \"We're done.\" should stop.\n if (/\\b(we'?re done|we are done|all done|all set)\\b/.test(normalized)) {\n return true\n }\n // v0.4.10 tweak 4: floor lowered 40 → 30 chars. Catches short clean\n // completions like \"Task is now completely done. Pushed.\" (36 chars)\n // while keeping a buffer against ambiguous short narration.\n if (normalized.length < 30) return false\n // v0.4.15: keyword list extended with deploy/ship verbs the model\n // routinely uses at turn end (shipped, deployed, merged, tagged, live,\n // pinned). FP risk highest on \"live\" — \"live data\" mid-turn could match\n // — but cost of FP is one extra continue press, safe direction.\n return /\\b(done|completed|fixed|implemented|verified|published|released|sent|delivered|updated|shipped|deployed|merged|tagged|live|pinned)\\b/.test(normalized) ||\n // v0.4.15: also accept present-tense \"tests pass\" / \"checks pass\".\n // Real fire 03:31 ended in \"78/78 tests pass\" — past-tense-only regex\n // missed it.\n /\\b(checks?|tests?) (?:pass|passes|passed)\\b/.test(normalized) ||\n /\\b(summary|what changed|verification)\\b/.test(normalized)\n}\n\nfunction continuationSignature(snapshot: AutoContinueSnapshot): string {\n const text = normalizeVisibleText(snapshot.text).slice(-500)\n return JSON.stringify({\n text,\n reasoning: snapshot.hadReasoning,\n tools: snapshot.hadToolActivity,\n proxy: snapshot.hadProxyActivity,\n })\n}\n\nexport function shouldAutoContinueIncompleteTurn(\n state: AutoContinueState,\n snapshot: AutoContinueSnapshot,\n): AutoContinueDecision {\n if (state.enabled === false) return { continue: false, reason: \"disabled\" }\n if (snapshot.isError) return { continue: false, reason: \"error\" }\n if (state.aborted) return { continue: false, reason: \"aborted\" }\n // Once the model asked the operator a question this turn, never nudge it to\n // continue — it is waiting for a reply, not stalled. Latched so it holds\n // even when the trailing text after the question doesn't read as a question.\n if (state.sawAskUserQuestion) return { continue: false, reason: \"question\" }\n // v0.4.17: trust ANY protocol-level stop_reason as authoritative. If\n // Claude CLI emitted a stop_reason value at all, the model has signaled\n // a stop — honor it without consulting the keyword heuristic. The\n // heuristic only runs as a fallback when stop_reason is missing (older\n // CLI versions / edge cases). Maps snake_case → kebab-case for reason\n // label consistency with other reasons.\n if (snapshot.stopReason) {\n // ...with one exception, which is the narrow half of @JWebCoder's PR #15\n // worth keeping. Truncation is the single stop_reason that does NOT mean\n // the model finished: the response hit the output cap mid-sentence. The\n // old guard read it as a stop, so a cut-off answer was silently accepted\n // as complete. Falling through to the keyword heuristic below would not\n // fix it either, because a truncated prose answer has no tool or\n // reasoning activity and would die at the `no-activity` gate. So\n // truncation is authoritative in the opposite direction: continue, still\n // bounded by the attempt and elapsed rails. PR #15 itself deleted the\n // whole guard, which would have handed every turn back to the regex that\n // v0.4.17 deliberately demoted; that is why it was closed.\n if (isTruncationStopReason(snapshot.stopReason)) {\n if (state.attempts >= AUTO_CONTINUE_MAX_ATTEMPTS) {\n return { continue: false, reason: \"max-attempts\" }\n }\n const truncatedAt = snapshot.now ?? Date.now()\n if (truncatedAt - state.startedAt > AUTO_CONTINUE_MAX_ELAPSED_MS) {\n return { continue: false, reason: \"max-elapsed\" }\n }\n return { continue: true, reason: \"truncated\" }\n }\n return {\n continue: false,\n reason: snapshot.stopReason.replace(/_/g, \"-\"),\n }\n }\n if (state.attempts >= AUTO_CONTINUE_MAX_ATTEMPTS) {\n return { continue: false, reason: \"max-attempts\" }\n }\n const now = snapshot.now ?? Date.now()\n if (now - state.startedAt > AUTO_CONTINUE_MAX_ELAPSED_MS) {\n return { continue: false, reason: \"max-elapsed\" }\n }\n\n const text = normalizeVisibleText(snapshot.text)\n const lastText = normalizeVisibleText(snapshot.lastVisibleText)\n if (looksLikeQuestion(text)) return { continue: false, reason: \"question\" }\n if (looksLikeBlocker(text)) return { continue: false, reason: \"blocker\" }\n // Final-answer detection runs on the most recent text block only. Earlier\n // blocks may contain mid-task narration that would false-positive the\n // keyword regex; the model's actual \"I'm done\" sentence is in the last\n // block before result/end_turn.\n if (looksLikeFinalAnswer(lastText)) {\n return { continue: false, reason: \"final-answer\" }\n }\n\n const hadActivity =\n snapshot.hadReasoning || snapshot.hadToolActivity || snapshot.hadProxyActivity\n if (!hadActivity) return { continue: false, reason: \"no-activity\" }\n\n const signature = continuationSignature(snapshot)\n const noProgress = signature === state.lastSignature\n if (noProgress && state.noProgressCount + 1 >= AUTO_CONTINUE_NO_PROGRESS_LIMIT) {\n return { continue: false, reason: \"no-progress\" }\n }\n\n if (!text) {\n return { continue: true, reason: \"activity-without-visible-answer\" }\n }\n\n return { continue: true, reason: \"non-final-progress\" }\n}\n\nfunction makeAutoContinueMessage(): string {\n return JSON.stringify({\n type: \"user\",\n message: {\n role: \"user\",\n content: [{ type: \"text\", text: AUTO_CONTINUE_PROMPT }],\n },\n })\n}\n\n/**\n * A proxy result whose HTTP reply channel Claude already abandoned cannot\n * go back as a `tool_result` (the CLI closed that tool_use with a timeout\n * error). Hand it over as a user message that names the call instead.\n */\nexport function makeLateProxyResultMessage(\n entries: Array<{ call: PendingProxyCall; result: ProxyToolResult }>,\n): string {\n const sections = entries.map(({ call, result }) => {\n const failed = result.kind === \"error\" || result.isError === true\n const body = result.kind === \"error\" ? result.message : result.text\n return (\n `Your earlier \\`${call.toolName}\\` tool call (id ${call.toolCallId})` +\n ` has ${failed ? \"failed\" : \"completed\"}, but delivery or continuation was interrupted.` +\n ` Treat the following as its ${failed ? \"error\" : \"result\"} and continue from there;` +\n ` do not re-run it.\\n\\n${body}`\n )\n })\n return JSON.stringify({\n type: \"user\",\n message: {\n role: \"user\",\n content: [{ type: \"text\", text: sections.join(\"\\n\\n---\\n\\n\") }],\n },\n })\n}\n\nfunction readPromptFileIfPresent(path: string): string | undefined {\n try {\n const content = readFileSync(path, \"utf8\").trim()\n return content || undefined\n } catch {\n return undefined\n }\n}\n\nfunction nearestWorkspaceAgentsPrompt(cwd: string): string | undefined {\n let dir = cwd\n while (true) {\n const content = readPromptFileIfPresent(join(dir, \"AGENTS.md\"))\n if (content) return content\n const parent = dirname(dir)\n if (parent === dir) return undefined\n dir = parent\n }\n}\n\nconst AGENTS_MAINTENANCE_HINT = `## Keeping AGENTS.md up to date\n\nWhen you complete a task, phase, or to-do item that is listed in AGENTS.md, update the file\nimmediately after the work is done — mark it ✅, check it off, or remove it. Do this inside\nthe same turn so the next session does not repeat work that is already finished.`\n\nconst MULTI_STEP_TASK_HINT = `## Continuing through multi-step tasks\n\nopencode requires the user to press \"continue\" after each turn ends. When a\ntask has multiple steps, do them all in one turn — chain tool calls rather\nthan pausing for user confirmation between subtasks. End the turn only\nwhen the task is done, you need clarification on intent, or you hit a real\nblocker. The user can interrupt or abort at any time; turn endings should\nmark meaningful checkpoints, not every completed substep.`\n\n/**\n * Appended to the system prompt whenever the `task` proxy tool is\n * enabled. Live sessions (2026-07-04) showed models resolving opencode's\n * \"call the task tool with subagent: X\" mention hint to Claude Code's\n * native TaskCreate: haiku created a todo and narrated a dispatch that\n * never happened; sonnet probed TaskCreate's schema before recovering.\n * The proxy tool can also be deferred behind ToolSearch, in which case\n * \"the task tool\" is invisible while TaskCreate is not. Name the exact\n * tool, the recovery path, and the failure mode.\n */\nexport const SUBAGENT_DISPATCH_HINT = `## opencode subagents\n\nSubagent dispatch in this environment goes through exactly two tools: \\`mcp__opencode_proxy__task\\` for one subagent and \\`mcp__opencode_proxy__task_batch\\` for two or more at once.\n\n- Two or more independent subagents in one response: make ONE \\`mcp__opencode_proxy__task_batch\\` call with a \\`tasks\\` array (each item is a normal task input). Claude Code runs MCP calls one at a time, so several \\`mcp__opencode_proxy__task\\` calls in the same response run serially; \\`task_batch\\` runs them concurrently in opencode and returns every result together, labelled in order.\n- When the user mentions \\`@<agent>\\` or an instruction says \"call the task tool with subagent: <name>\", call \\`mcp__opencode_proxy__task\\` with \\`subagent_type: \"<name>\"\\`.\n- If that tool is not in your visible tool list it is deferred — load it with ToolSearch (\\`select:mcp__opencode_proxy__task\\`), then call it.\n- Claude Code's built-in TaskCreate/TaskUpdate/TaskList manage a local todo list. They cannot dispatch subagents; creating a task there runs nothing. Never report a subagent as dispatched unless \\`mcp__opencode_proxy__task\\` returned its result.\n- Do not verify a subagent's existence by searching config files — the tool's description lists the available agent types, and invalid types fail fast with a clear error.`\n\n/**\n * Appended to the system prompt whenever the `question` proxy tool is\n * enabled. Live testing (2026-07-05, haiku) showed the model's reasoning\n * correctly identified `mcp__opencode_proxy__question` as the tool to use,\n * but then emitted a tool call for bare `question` — stripping the MCP\n * prefix. opencode's AI SDK bridge has no bare `question` tool, so the\n * call rendered as `⚙ invalid`. Same near-miss pattern the task proxy\n * hit (TaskCreate vs mcp__opencode_proxy__task); the fix is the same:\n * name the exact tool in the system prompt so the model doesn't\n * abbreviate.\n */\nexport const QUESTION_PROXY_HINT = `## Asking the operator questions\n\nStructured questions in this environment go through exactly one tool: \\`mcp__opencode_proxy__question\\`.\n\n- When you need to ask the operator a question with options, call \\`mcp__opencode_proxy__question\\` with a \\`questions\\` array (each item has \\`question\\`, \\`header\\`, \\`options\\` of \\`{label, description}\\`, and optional \\`multiple\\`).\n- If that tool is not in your visible tool list it is deferred — load it with ToolSearch (\\`select:mcp__opencode_proxy__question\\`), then call it by its FULL name.\n- Do NOT call bare \\`question\\` — that is not a tool. Always use the full \\`mcp__opencode_proxy__question\\` name when invoking it.\n- Claude Code's built-in \\`AskUserQuestion\\` is disabled in this environment; the proxy is the only way to ask structured questions.`\n\n/**\n * Prepended to every appended system prompt so Claude knows which\n * context-management tools exist in the Claude CLI runtime versus a\n * direct API provider. DCP and similar plugins forward compress/distill/\n * prune instructions via system.transform; those reach us through\n * extractSystemMessages, but the tools themselves are not available in\n * the CLI environment. Without this note Claude wastes thinking cycles\n * searching for tools that don't exist.\n */\nconst CLAUDE_CLI_CONTEXT_NOTE = `## Runtime environment: Claude Code CLI\n\nYou are running via the Claude Code CLI (not a direct API call). This affects context management:\n\n- The \\`compress\\` tool is NOT available. Do not attempt to call it.\n- The \\`distill\\`, \\`prune\\`, and \\`extract\\` tools are NOT available.\n- Context window management is handled automatically by Claude CLI's own session history.\n- Ignore any system instructions that tell you to call \\`compress\\` — they are intended for direct API providers, not this environment.\n- DCP context injections (AGENTS.md, dynamic state) arrive via the system prompt and are already applied.`\n\n/**\n * Replaces the note above when `compress` is in the resolved proxy list.\n * The full MCP name is spelled out for the same reason the question proxy\n * hint spells its own out: models strip the prefix and call bare\n * `compress`, which opencode renders as `⚙ invalid`.\n */\nconst CLAUDE_CLI_COMPRESS_NOTE = `## Runtime environment: Claude Code CLI\n\nYou are running via the Claude Code CLI (not a direct API call). This affects context management:\n\n- To compress context, call \\`mcp__opencode_proxy__compress\\` with a \\`summary\\` argument. Use that exact full name.\n- The reset happens at the start of your NEXT turn: this Claude Code session is discarded and a fresh one starts with your summary as its only prior context. Keep working normally after the call.\n- Everything outside the summary is gone after the reset — tool output, files you read, and the earlier conversation are not replayed. Write the summary as the authoritative record.\n- The \\`distill\\`, \\`prune\\`, and \\`extract\\` tools are NOT available.\n- DCP context injections (AGENTS.md, dynamic state) arrive via the system prompt and are already applied.`\n\n/**\n * Used when opencode's own `compress` tool is forwarded through the proxy\n * (`proxyOpencodeTools: [\"compress\"]`) instead of the plugin's in-process\n * one. The two shrink different windows and the difference has to be said\n * out loud: opencode's rewrites opencode's transcript, so the live Claude\n * Code session keeps everything it already had. A model told otherwise\n * would assume detail it can still see had been discarded.\n */\nconst CLAUDE_CLI_OPENCODE_COMPRESS_NOTE = `## Runtime environment: Claude Code CLI\n\nYou are running via the Claude Code CLI (not a direct API call). This affects context management:\n\n- To compress context, call \\`mcp__opencode_proxy__compress\\`. Use that exact full name. It runs opencode's own \\`compress\\` tool, which is what a \"MAX CONTEXT LIMIT REACHED\" reminder is asking you to do.\n- It compresses opencode's stored conversation, NOT this Claude Code session. Your current session keeps the context it already has, so do not assume earlier detail is gone after the call.\n- The \\`distill\\`, \\`prune\\`, and \\`extract\\` tools are NOT available.\n- DCP context injections (AGENTS.md, dynamic state) arrive via the system prompt and are already applied.`\n\n/**\n * Extract text content from all `system`-role messages in the prompt.\n * Standard API providers forward these as the `system` parameter; for\n * Claude CLI, the only equivalent path is --append-system-prompt-file.\n * Plugins like opencode-dcp inject AGENTS.md and other context via\n * system-role messages and would otherwise be silently dropped.\n */\nfunction extractSystemMessages(\n prompt: LanguageModelV3CallOptions[\"prompt\"],\n): string[] {\n const out: string[] = []\n for (const msg of prompt) {\n if (msg.role !== \"system\") continue\n if (typeof msg.content === \"string\") {\n if (msg.content.trim()) out.push(msg.content.trim())\n } else if (Array.isArray(msg.content)) {\n for (const part of msg.content as any[]) {\n if (\n part?.type === \"text\" &&\n typeof part.text === \"string\" &&\n part.text.trim()\n ) {\n out.push(part.text.trim())\n }\n }\n }\n }\n return out\n}\n\nexport interface AppendedSystemPromptOptions {\n /** True when the plugin's own `compress` def is in the proxy list. */\n compressEnabled?: boolean\n /** True when opencode's `compress` tool is forwarded through the proxy. */\n opencodeCompressEnabled?: boolean\n /** Summary from a previous `compress` call, if this key has one. */\n compressionSummary?: string\n}\n\nexport function buildAppendedSystemPrompt(\n cwd: string,\n includeMultiStepHint = true,\n extraSystemContent: string[] = [],\n options: AppendedSystemPromptOptions = {},\n): string | undefined {\n const parts: string[] = []\n // First, so it reads as prior context for everything that follows.\n if (options.compressionSummary?.trim()) {\n parts.push(\n `## Summary of earlier work (context was compressed)\\n\\n${options.compressionSummary.trim()}`,\n )\n }\n // The plugin's own compress wins when both are somehow live, matching the\n // def-level precedence in resolveProxyOpencodeToolDefs: it is the one that\n // holds the name, so it is the one the model would reach.\n parts.push(\n options.compressEnabled\n ? CLAUDE_CLI_COMPRESS_NOTE\n : options.opencodeCompressEnabled\n ? CLAUDE_CLI_OPENCODE_COMPRESS_NOTE\n : CLAUDE_CLI_CONTEXT_NOTE,\n )\n for (const s of extraSystemContent) {\n if (s.trim()) parts.push(s.trim())\n }\n const configRoot =\n process.env.XDG_CONFIG_HOME ?? join(homedir(), \".config\")\n const globalAgents = readPromptFileIfPresent(join(configRoot, \"opencode\", \"AGENTS.md\"))\n const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd)\n\n // opencode already forwards AGENTS.md inside its own system prompt\n // (`extraSystemContent`, under an \"Instructions from:\" header), so a\n // disk-read copy would reach the model twice. Only push ours when the\n // forwarded text does not already contain it. No match (formatting drift,\n // or the interactive path, which forwards nothing) keeps the old behaviour,\n // so AGENTS.md is never lost. (Dedup by @HeikoAtGitHub, 25260a4.)\n const forwarded = extraSystemContent.join(\"\\n\\n\")\n const pushGlobal = !!globalAgents && !forwarded.includes(globalAgents)\n const pushWorkspace =\n !!workspaceAgents && workspaceAgents !== globalAgents &&\n !forwarded.includes(workspaceAgents)\n if (pushGlobal) parts.push(globalAgents)\n if (pushWorkspace) parts.push(workspaceAgents)\n if (pushGlobal || pushWorkspace) parts.push(AGENTS_MAINTENANCE_HINT)\n if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT)\n\n const content = parts.join(\"\\n\\n\")\n if (!content) return undefined\n\n const path = join(tmpdir(), `opencode-cc-sys-${randomUUID()}.md`)\n try {\n writeFileSync(path, content, \"utf8\")\n return path\n } catch (err) {\n log.warn(\"failed to write system prompt file\", { error: String(err) })\n return undefined\n }\n}\n\n/**\n * Human-readable explanations for the CLI's `fast_mode_disabled_reason` codes,\n * so a downgrade tells the user what to do instead of leaking an enum.\n */\nconst FAST_MODE_REASONS: Record<string, string> = {\n sdk_opt_in_required:\n \"the CLI did not receive the headless opt-in (--settings). This is a plugin bug, please report it\",\n extra_usage_disabled:\n \"your account has usage credits turned off. Run /usage-credits in an interactive `claude` session to enable them\",\n free: \"fast mode requires a paid subscription or purchased credits\",\n preference: \"fast mode is turned off for your organization\",\n model_not_allowed:\n \"this model is not in your organization's allowed models\",\n not_first_party:\n \"fast mode only works against the Anthropic API directly, not Bedrock / Vertex / Foundry\",\n network_error: \"the CLI could not reach Anthropic to check availability\",\n disabled_by_env: \"CLAUDE_CODE_DISABLE_FAST_MODE is set in the environment\",\n pending: \"the CLI is still checking availability\",\n}\n\n/** Reasons already surfaced this process, so a persistent block warns once. */\nconst warnedFastModeReasons = new Set<string>()\n\n/** Test-only. */\nexport function _resetFastModeWarnings(): void {\n warnedFastModeReasons.clear()\n}\n\n/**\n * Report what actually happened to a fast-mode request.\n *\n * Fast mode fails soft: an ineligible account or a rate-limit cooldown drops\n * back to standard speed with no error. That silence is the problem worth\n * solving here: the fast model ids advertise fast pricing in opencode's picker,\n * so a downgrade the user cannot see means the picker is lying about cost for\n * every subsequent turn.\n *\n * A hard block is therefore a WARN, which this codebase routes to the TUI\n * unconditionally (NOTICE only surfaces in debug mode, which would defeat the\n * purpose). It is deduped per reason per process because the blocking\n * conditions are account-level and would otherwise repeat on every respawn.\n * Cooldown stays quieter: it is transient and clears on its own.\n */\nexport function reportFastModeState(\n msg: ClaudeStreamMessage,\n requested: boolean,\n): void {\n const state = msg.fast_mode_state\n if (!state) return\n\n if (!requested) {\n // Nothing was asked for. Only interesting at debug level.\n log.debug(\"fast mode state\", { state })\n return\n }\n\n if (state === \"on\") {\n log.info(\"fast mode active\", { state })\n return\n }\n\n const reason = msg.fast_mode_disabled_reason\n if (state === \"cooldown\") {\n log.notice(\n \"fast mode is in cooldown after a rate limit; this turn runs at standard speed and is billed at standard Opus rates, not the fast price shown in the model picker.\",\n { state, reason: reason ?? null },\n )\n return\n }\n\n const key = reason ?? \"unknown\"\n const explanation = reason ? FAST_MODE_REASONS[reason] : undefined\n const message = `fast mode was requested but is off${\n explanation ? `: ${explanation}` : reason ? ` (${reason})` : \"\"\n }. Turns run at standard speed and are billed at standard Opus rates, not the fast price shown in the model picker. Switch to the non-fast model id to make the picker's price accurate.`\n\n if (warnedFastModeReasons.has(key)) {\n log.debug(message, { state, reason: reason ?? null })\n return\n }\n warnedFastModeReasons.add(key)\n log.warn(message, { state, reason: reason ?? null })\n}\n\nexport class ClaudeCodeLanguageModel implements LanguageModelV3 {\n readonly specificationVersion = \"v3\"\n readonly modelId: string\n private readonly config: ClaudeCodeConfig\n\n constructor(modelId: string, config: ClaudeCodeConfig) {\n this.modelId = modelId\n this.config = config\n }\n\n readonly supportedUrls: Record<string, RegExp[]> = {}\n\n get provider(): string {\n return this.config.provider\n }\n\n private toUsage(rawUsage?: ClaudeStreamMessage[\"usage\"]): LanguageModelV3Usage {\n // Prefer the last iteration's counters over cumulative totals.\n // CLI usage is the sum across all internal tool-use iterations;\n // using it directly inflates context size and triggers premature compaction.\n const iter = rawUsage?.iterations\n const effective = iter?.length ? iter[iter.length - 1] : rawUsage\n // Claude CLI reports input_tokens as non-cached input only.\n // OpenCode expects total = noCache + cacheRead + cacheWrite.\n const noCache = effective?.input_tokens ?? 0\n const cacheRead = effective?.cache_read_input_tokens ?? 0\n const cacheWrite = effective?.cache_creation_input_tokens ?? 0\n return {\n inputTokens: {\n total: noCache + cacheRead + cacheWrite,\n noCache,\n cacheRead: cacheRead || undefined,\n cacheWrite: cacheWrite || undefined,\n },\n outputTokens: {\n total: effective?.output_tokens,\n text: effective?.output_tokens,\n reasoning: undefined,\n },\n raw: rawUsage as any,\n }\n }\n\n private toFinishReason(\n reason: \"stop\" | \"tool-calls\" | \"error\" = \"stop\",\n ): LanguageModelV3FinishReason {\n return {\n unified: reason,\n raw: reason,\n }\n }\n\n /**\n * Whether this call only names the session, which gets the synthetic stub\n * rather than a `claude` spawn. opencode 1.x sends a title request with no\n * tools, and that is the whole test there. opencode 2 sends its tool set\n * along with it (measured on 2.0.11: `scope: \"tools\"`, agent `title`), so\n * every new V2 session paid for a second `claude` process just to title\n * itself; for a V2 model the request kind, carried as the `title` agent,\n * decides instead.\n */\n private isTitleRequest(\n scope: \"tools\" | \"no-tools\",\n options: LanguageModelV3CallOptions,\n ): boolean {\n if (scope === \"no-tools\") return true\n return this.config.hostApi === \"v2\" && this.getOpencodeAgent(options) === \"title\"\n }\n\n private requestScope(options: { tools?: unknown }): \"tools\" | \"no-tools\" {\n const tools = options?.tools\n if (Array.isArray(tools)) return \"tools\"\n if (tools && typeof tools === \"object\") {\n return Object.keys(tools as Record<string, unknown>).length > 0\n ? \"tools\"\n : \"no-tools\"\n }\n return \"no-tools\"\n }\n\n /**\n * Build the combined `--mcp-config` list and return both the list and the\n * hash of the bridged opencode MCP block (or null when bridging is off /\n * yields nothing). The hash is used to detect mid-session config changes\n * and respawn the underlying claude process.\n *\n * `runtimeStatus` is a snapshot of opencode's `client.mcp.status()`. When\n * provided it overlays opencode's UI-toggled state on top of disk config\n * so `/mcps` toggles propagate without a config file write.\n */\n private effectiveMcpConfig(\n cwd: string,\n proxyConfigPath?: string,\n runtimeStatus?: RuntimeMcpStatus,\n excludeServers?: ReadonlySet<string>,\n ): {\n paths: string[]\n bridgedHash: string | null\n allEnabledServerNames: string[]\n } {\n const paths = Array.isArray(this.config.mcpConfig)\n ? this.config.mcpConfig.slice()\n : this.config.mcpConfig\n ? [this.config.mcpConfig]\n : []\n let bridgedHash: string | null = null\n let allEnabledServerNames: string[] = []\n if (this.config.bridgeOpencodeMcp !== false) {\n const bridged = bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers)\n if (bridged) {\n if (bridged.path) paths.push(bridged.path)\n bridgedHash = bridged.hash\n allEnabledServerNames = bridged.allEnabledServerNames\n }\n }\n if (proxyConfigPath) paths.push(proxyConfigPath)\n return { paths, bridgedHash, allEnabledServerNames }\n }\n\n /** Resolve ProxyToolDef[] for the configured proxyTools names. */\n private resolvedProxyTools(): ProxyToolDef[] | null {\n const names = this.config.proxyTools\n if (!names || names.length === 0) return null\n const defsByName = new Map(\n DEFAULT_PROXY_TOOLS.map((t) => [t.name.toLowerCase(), t]),\n )\n const picked: ProxyToolDef[] = []\n const seen = new Set<string>()\n const unknown: string[] = []\n const pick = (def: ProxyToolDef) => {\n if (seen.has(def.name)) return\n seen.add(def.name)\n picked.push(def)\n }\n for (const n of names) {\n const def = defsByName.get(String(n).toLowerCase())\n if (!def) {\n unknown.push(String(n))\n continue\n }\n pick(def)\n // `task_batch` rides along with `task`: it is the same dispatch path for\n // two or more subagents at once (TASK_BATCH_PROXY_NOTE), and a\n // `proxyTools` list that names `Task` should not have to know it exists.\n if (def.name === \"task\") {\n const batch = defsByName.get(TASK_BATCH_TOOL_NAME)\n if (batch) pick(batch)\n }\n }\n // A typo used to vanish here. Silence is the wrong response: unknown\n // names are not proxied, so the matching Claude built-in stays enabled\n // and unmediated, and if *every* name is unknown the whole turn runs\n // with no proxy at all (issue #26).\n if (unknown.length > 0) {\n const known = [...defsByName.keys()].join(\", \")\n if (picked.length === 0) {\n log.warn(\n \"no proxyTools entry was recognised; nothing will be proxied this turn\",\n { unknown, known },\n )\n } else {\n log.warn(\"ignoring unknown proxyTools entries\", { unknown, known })\n }\n }\n return picked.length > 0 ? picked : null\n }\n\n /**\n * Resolve ProxyToolDef[] for opencode's MCP-backed tools so they go\n * through the in-process proxy instead of being bridged into Claude CLI's\n * `--mcp-config`. Routing through the proxy keeps a single execution site\n * (opencode), so the call is permission-prompted and rendered as an\n * opencode tool call.\n *\n * Opt-in (`proxyOpencodeMcpTools: true`) and off by default. It used to\n * default to true while finding nothing, because it discovered tools via\n * `client.tool.list()`, which enumerates opencode's `ToolRegistry` and not\n * the MCP tools merged into the model's tool set afterwards. Discovery now\n * reads that merged set, the `tools` array opencode passes `doStream`, so\n * the option does what it says. Turning it on by default at the same time\n * would have silently moved every existing user's MCP traffic off the\n * working direct bridge, so the default went to false instead: today's\n * behaviour is preserved exactly and crossing over is the operator's call.\n *\n * Returns null when the feature is off or nothing matched, which leaves\n * every server on the direct bridge.\n */\n private resolvedProxyMcpTools(\n allEnabledServerNames: string[],\n modelTools: readonly ModelToolEntry[] | undefined,\n taken?: ReadonlySet<string>,\n ): McpProxyToolResolution | null {\n if (this.config.proxyOpencodeMcpTools !== true) return null\n if (this.config.bridgeOpencodeMcp === false) return null\n if (allEnabledServerNames.length === 0) return null\n\n const resolution = resolveMcpProxyToolDefs({\n serverNames: allEnabledServerNames,\n tools: modelTools,\n taken,\n })\n if (resolution.defs.length === 0) {\n // WARN, not NOTICE: only warn and error are alwaysStderr in\n // src/logger.ts, so a NOTICE would be invisible to the very operator\n // who opted in and is entitled to know their MCP calls are still\n // going direct, and so still are not permission-prompted by opencode.\n log.warn(\n \"proxyOpencodeMcpTools is on but no MCP tool was found in opencode's\" +\n \" tool set; those servers stay on the direct bridge this spawn\",\n { servers: allEnabledServerNames, modelTools: modelTools?.length ?? 0 },\n )\n return null\n }\n log.debug(\"routing opencode MCP tools through the proxy\", {\n servers: [...resolution.coveredServers],\n tools: resolution.defs.map((def) => def.name),\n })\n return resolution\n }\n\n /**\n * Live tool info derived from a single `client.tool.list()` fetch:\n *\n * - `taskDescription`: opencode's `task` tool description exactly as the\n * registry renders it for native models, including the \"Available\n * agent types\" list. Overlaid onto the static `task` proxy def so\n * Claude sees the same subagent catalog native models see, instead\n * of hunting through config files.\n * - `questionDescription` / `hasQuestion`: opencode's `question` tool\n * description and whether the registry has the entry at all. Older\n * builds lack it, in which case a `mcp__opencode_proxy__question`\n * call resolves to `⚙ invalid`; the version gate drops the def.\n *\n * Returns undefined/false when the SDK client is unavailable (direct\n * AI-SDK use, tests) so the static defs stand. `resolved` distinguishes\n * \"the registry answered and has no `question` entry\" from \"nobody\n * answered\": only the former is a real version-gate signal.\n */\n private async fetchLiveToolInfo(): Promise<LiveToolInfo> {\n const items = await fetchOpencodeToolList(\n this.config.provider,\n this.modelId,\n this.config.cwd,\n )\n const question = items?.find((item) => item.id === \"question\")\n return {\n resolved: items !== undefined,\n taskDescription: items?.find((item) => item.id === \"task\")?.description,\n questionDescription: question?.description,\n hasQuestion: !!question,\n items,\n }\n }\n\n /**\n * Whether dcp-style context reminders should be stripped from this turn's\n * messages. Config-only and synchronous, so it can be answered before the\n * spawn block resolves anything: `userMsg` is built well ahead of it.\n */\n private stripContextRemindersEnabled(): boolean {\n return shouldStripContextReminders({\n enabled: this.config.stripContextReminders,\n proxyTools: this.config.proxyTools,\n proxyOpencodeTools: this.config.proxyOpencodeTools,\n })\n }\n\n /**\n * Arguments the skill bridge needs beyond `cwd` / `cliPath`: which\n * `CLAUDE_CONFIG_DIR` this spawn reads its native skills from, and whether\n * to drop the ones it already loads. A failover moves the spawn to another\n * account, and therefore to that account's config dir.\n */\n private skillBridgeSpawn(failover: FailoverSpawn): {\n configDir: string | undefined\n skipNative: boolean\n } {\n return {\n configDir:\n failover.failedOver && failover.target\n ? accountConfigDirPath(failover.target)\n : this.config.configDir,\n skipNative: this.config.bridgeSkipNativeSkills !== false,\n }\n }\n\n /** Share one lazy registry request within a turn without making it stale. */\n private createLiveToolInfoLoader(): () => Promise<LiveToolInfo> {\n let pending: Promise<LiveToolInfo> | undefined\n return () => {\n pending ??= this.fetchLiveToolInfo()\n return pending\n }\n }\n\n /**\n * Whether the ExitPlanMode approval bridge is live for this turn: the\n * operator opted in AND opencode's registry actually has the `question`\n * tool. Without the registry entry the emitted tool-call would render as\n * `⚙ invalid` and wedge the turn, so the plugin keeps the text path.\n */\n private async resolvePlanModeQuestion(\n compactionMode: boolean,\n loadLiveToolInfo = () => this.fetchLiveToolInfo(),\n ): Promise<boolean> {\n if (compactionMode || this.config.planModeQuestion !== true) return false\n const info = await loadLiveToolInfo()\n const active = isPlanModeQuestionActive({\n configured: this.config.planModeQuestion,\n opencodeHasQuestion: info.hasQuestion,\n compactionMode,\n })\n if (!active) {\n // Same reasoning as the question proxy's version-gate log: a silent\n // fallback to the text path looks from the outside like the setting\n // was ignored.\n log.info(\"plan-mode question gate\", {\n opencodeHasQuestion: info.hasQuestion,\n registryResolved: info.resolved,\n active,\n })\n }\n return active\n }\n\n /**\n * Create a proxy MCP server for a single active Claude process/session.\n * The process lifecycle owns the server lifecycle via session-manager.\n */\n private async ensureProxyServer(\n tools: ProxyToolDef[],\n sessionKeyForCalls: string,\n // Whether the `compress` in `tools` is the PLUGIN's def rather than\n // opencode's forwarded one. Keying the interceptor on the name alone\n // would answer a forwarded `compress` in-process and opencode would\n // never see the call: the same name, the wrong tool, silently. The\n // caller knows which list the def came from, so it decides.\n interceptCompress: boolean,\n ): Promise<ProxyMcpServer> {\n const timeoutOverrides = this.config.proxyToolTimeoutMs\n const interceptors = new Map<string, ProxyToolInterceptor>()\n if (interceptCompress && tools.some((t) => t.name === \"compress\")) {\n interceptors.set(\"compress\", (input) => {\n const summary = typeof input.summary === \"string\" ? input.summary.trim() : \"\"\n if (!summary) {\n return {\n kind: \"error\",\n message:\n \"compress needs a non-empty `summary`: it becomes the only\" +\n \" prior context after the reset. Nothing was compressed.\",\n }\n }\n storeCompressionSummary(sessionKeyForCalls, summary)\n log.info(\"compress stored summary; session resets next turn\", {\n sessionKey: sessionKeyForCalls,\n summaryLength: summary.length,\n })\n return {\n kind: \"text\",\n text:\n \"Summary stored. Finish this turn as normal; the next turn starts\" +\n \" a fresh Claude Code session with this summary as its only prior\" +\n \" context.\",\n }\n })\n }\n const srv = await createProxyMcpServer(tools, timeoutOverrides, interceptors)\n srv.calls.on(\"call\", (call: ProxyToolCall) => {\n queuePendingProxyCall(sessionKeyForCalls, call, timeoutOverrides)\n })\n return srv\n }\n\n private extractPendingProxyResult(\n prompt: LanguageModelV3CallOptions[\"prompt\"],\n toolCallId: string,\n ): ProxyToolResult | null {\n for (let i = prompt.length - 1; i >= 0; i--) {\n const msg = prompt[i]\n if (msg.role !== \"tool\" || !Array.isArray(msg.content)) continue\n\n for (const part of msg.content) {\n if (part.type !== \"tool-result\" || part.toolCallId !== toolCallId) continue\n\n const output = part.output as any\n if (!output || typeof output !== \"object\") {\n return {\n kind: \"text\",\n text: String(output ?? \"\"),\n }\n }\n\n if (output.type === \"text\") {\n return {\n kind: \"text\",\n text: String(output.value ?? \"\"),\n }\n }\n\n if (output.type === \"json\") {\n return {\n kind: \"text\",\n text: JSON.stringify(output.value),\n }\n }\n\n if (output.type === \"content\" && Array.isArray(output.value)) {\n const text = output.value\n .filter((v: any) => v?.type === \"text\" && typeof v.text === \"string\")\n .map((v: any) => v.text)\n .join(\"\\n\")\n return {\n kind: \"text\",\n text,\n }\n }\n\n return {\n kind: \"text\",\n text: JSON.stringify(output),\n }\n }\n }\n\n return null\n }\n\n /**\n * The result opencode produced for a pending proxy call, if the prompt\n * carries it. For `task_batch` that means every child's result gathered\n * back onto the parent: opencode runs the children in one step and hands\n * all their results to the next call together, so a partial set is not\n * expected. If it ever happens the batch still resolves, with the gap\n * named in the text, because leaving the parent pending would send this\n * turn down the fresh-envelope path and reject the call as orphaned.\n */\n private extractPendingProxyResultForCall(\n prompt: LanguageModelV3CallOptions[\"prompt\"],\n call: PendingProxyCall,\n ): ProxyToolResult | null {\n if (call.toolName !== TASK_BATCH_TOOL_NAME) {\n return this.extractPendingProxyResult(prompt, call.toolCallId)\n }\n const tasks = taskBatchTasks(call.input)\n if (tasks.length === 0) {\n return { kind: \"error\", message: \"task_batch input is not a list of task objects\" }\n }\n const children = tasks.map((task, index) => ({\n task,\n result: this.extractPendingProxyResult(\n prompt,\n taskBatchChildToolCallId(call.toolCallId, index),\n ),\n }))\n const answered = children.filter((child) => child.result !== null).length\n if (answered === 0) return null\n if (answered < children.length) {\n log.warn(\"task_batch resolving with child results missing\", {\n toolCallId: call.toolCallId,\n answered,\n total: children.length,\n })\n }\n return formatTaskBatchResults(children)\n }\n\n /**\n * Resolve the session affinity token for this LLM call. Delegates to the\n * exported `resolveSessionAffinity` helper so the logic is unit-testable.\n * Priority:\n * 1. `x-session-affinity` request header (primary).\n * 2. `opencodeSessionID` in providerOptions (chat.params hook fallback —\n * covers provider switches mid-session and title synthesis paths\n * where the header is absent).\n * 3. `\"default\"`.\n */\n private sessionAffinity(\n options: LanguageModelV3CallOptions,\n ): string {\n const headers = (options as any)?.headers as\n | Record<string, string | undefined>\n | undefined\n return resolveSessionAffinity(\n headers,\n options.providerOptions as Record<string, unknown> | undefined,\n this.config.provider,\n )\n }\n\n private controlRequestBehaviorForTool(toolName: string): ControlRequestBehavior {\n const configured = this.config.controlRequestToolBehaviors\n if (configured && toolName) {\n const direct = configured[toolName] ?? configured[toolName.toLowerCase()]\n if (direct === \"allow\" || direct === \"deny\") return direct\n\n const lower = toolName.toLowerCase()\n for (const [key, behavior] of Object.entries(configured)) {\n if (key.toLowerCase() === lower && (behavior === \"allow\" || behavior === \"deny\")) {\n return behavior\n }\n }\n }\n\n // AskUserQuestion must never be auto-allowed. Allowing it lets the\n // Claude CLI resolve its own question internally — in headless mode\n // there is no TTY, so the CLI fabricates/empties the answer and the\n // model proceeds on a guess. Deny so the CLI cannot self-answer; the\n // tool_use is still streamed and rendered to the opencode user by\n // formatAskUserQuestion, and the turn stops for a real reply. An\n // explicit controlRequestToolBehaviors entry above can still override.\n if (isAskUserQuestionTool(toolName)) return \"deny\"\n\n return this.config.controlRequestBehavior ?? \"allow\"\n }\n\n private writeControlResponse(\n proc: import(\"child_process\").ChildProcess,\n requestId: string,\n response?: Record<string, unknown>,\n ): void {\n const payload = {\n type: \"control_response\",\n response: {\n subtype: \"success\",\n request_id: requestId,\n response,\n },\n }\n\n try {\n proc.stdin?.write(JSON.stringify(payload) + \"\\n\")\n } catch (error) {\n log.warn(\"failed to write control response\", {\n requestId,\n error: error instanceof Error ? error.message : String(error),\n })\n }\n }\n\n /**\n * Handle Claude stream-json control requests (`can_use_tool`, etc.) and\n * respond via stdin with a matching `control_response`.\n */\n private handleControlRequest(\n msg: ClaudeStreamMessage,\n proc: import(\"child_process\").ChildProcess,\n ): boolean {\n if (msg.type !== \"control_request\") return false\n const requestId = msg.request_id\n const request = msg.request\n if (!requestId || !request?.subtype) return false\n\n if (request.subtype === \"can_use_tool\") {\n const toolName = request.tool_name ?? \"unknown\"\n const behavior = this.controlRequestBehaviorForTool(toolName)\n\n if (behavior === \"allow\") {\n this.writeControlResponse(proc, requestId, {\n behavior: \"allow\",\n updatedInput: request.input ?? {},\n toolUseID: request.tool_use_id,\n })\n log.info(\"control request auto-allowed\", {\n requestId,\n toolName,\n })\n } else {\n const denyMessage = denyMessageForTool(\n toolName,\n this.config.controlRequestDenyMessage,\n )\n this.writeControlResponse(proc, requestId, {\n behavior: \"deny\",\n message: denyMessage,\n toolUseID: request.tool_use_id,\n })\n log.info(\"control request auto-denied\", {\n requestId,\n toolName,\n })\n }\n\n return true\n }\n\n // For control request subtypes we don't actively handle yet, acknowledge\n // with an empty success so the CLI stream does not stall.\n this.writeControlResponse(proc, requestId, {})\n log.debug(\"control request acknowledged\", {\n requestId,\n subtype: request.subtype,\n })\n return true\n }\n\n private getReasoningEffort(\n providerOptions?: LanguageModelV3CallOptions[\"providerOptions\"],\n ): ReasoningEffort | undefined {\n if (!providerOptions) return undefined\n const ownKey = this.config.provider\n const bag =\n (providerOptions as any)[ownKey] ??\n (providerOptions as any)[\"claude-code\"]\n const effort = bag?.reasoningEffort\n const valid: ReasoningEffort[] = [\n \"minimal\",\n \"low\",\n \"medium\",\n \"high\",\n \"xhigh\",\n \"max\",\n ]\n return valid.includes(effort) ? effort : undefined\n }\n\n private getOpencodeAgent(options: LanguageModelV3CallOptions): string | undefined {\n return resolveOpencodeAgent(\n (options as any)?.headers as Record<string, string | undefined> | undefined,\n options.providerOptions as Record<string, unknown> | undefined,\n this.config.provider,\n )\n }\n\n private isCompactionCall(\n options: LanguageModelV3CallOptions,\n ): boolean {\n return this.getOpencodeAgent(options) === \"compaction\"\n }\n\n /**\n * Pick the model used to handle /compact. Precedence:\n * 1. `CLAUDE_CODE_COMPACTION_MODEL` env var (per-process override)\n * 2. `compactionModel` provider setting (opencode.json / .jsonc)\n * 3. Built-in default (claude-haiku-4-5)\n */\n private resolveCompactionModel(): string {\n return resolveCompactionModel(this.config.compactionModel)\n }\n\n private thinkingCliOptions(): {\n thinking?: \"enabled\"\n thinkingDisplay?: \"summarized\"\n } {\n if (isClaudeThinkingDisabled()) return {}\n\n return {\n thinking: \"enabled\",\n thinkingDisplay:\n process.env.CLAUDE_CODE_SHOW_THINKING_SUMMARIES === undefined\n ? \"summarized\"\n : undefined,\n }\n }\n\n private latestUserText(\n prompt: LanguageModelV3CallOptions[\"prompt\"],\n ): string {\n for (let i = prompt.length - 1; i >= 0; i--) {\n const msg = prompt[i]\n if (msg.role !== \"user\") continue\n\n if (typeof msg.content === \"string\") {\n return String(msg.content).trim()\n }\n\n if (Array.isArray(msg.content)) {\n const text = (msg.content as any[])\n .filter((part) => part.type === \"text\" && typeof part.text === \"string\")\n .map((part: any) => String(part.text).trim())\n .filter(Boolean)\n .join(\" \")\n if (text) return text\n }\n }\n\n return \"\"\n }\n\n private synthesizeTitle(\n prompt: LanguageModelV3CallOptions[\"prompt\"],\n ): string {\n const source = this.latestUserText(prompt)\n .replace(/\\s+/g, \" \")\n .replace(/[^\\p{L}\\p{N}\\s-]/gu, \" \")\n .trim()\n\n if (!source) return \"New Session\"\n\n const stop = new Set([\n \"a\",\n \"an\",\n \"the\",\n \"and\",\n \"or\",\n \"but\",\n \"to\",\n \"for\",\n \"of\",\n \"in\",\n \"on\",\n \"at\",\n \"with\",\n \"can\",\n \"could\",\n \"would\",\n \"should\",\n \"please\",\n \"hi\",\n \"hello\",\n \"hey\",\n \"there\",\n \"you\",\n \"your\",\n \"this\",\n \"that\",\n \"is\",\n \"are\",\n \"was\",\n \"were\",\n \"be\",\n \"do\",\n \"does\",\n \"did\",\n \"summarize\",\n \"summary\",\n \"project\",\n ])\n\n const words = source\n .split(\" \")\n .map((word) => word.trim())\n .filter(Boolean)\n .filter((word) => !stop.has(word.toLowerCase()))\n\n const picked = (words.length > 0 ? words : source.split(\" \").filter(Boolean))\n .slice(0, 6)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \")\n\n return picked || \"New Session\"\n }\n\n private async doGenerateViaStream(\n options: LanguageModelV3CallOptions,\n ): Promise<Awaited<ReturnType<LanguageModelV3[\"doGenerate\"]>>> {\n const result = await this.doStream(options)\n const reader = result.stream.getReader()\n\n let text = \"\"\n let reasoning = \"\"\n const toolCalls: LanguageModelV3Content[] = []\n let finishReason = this.toFinishReason(\"stop\")\n let usage: LanguageModelV3Usage = this.toUsage()\n let providerMetadata: any\n\n while (true) {\n const { value, done } = await reader.read()\n if (done) break\n\n switch ((value as any).type) {\n case \"text-delta\":\n text += (value as any).delta ?? \"\"\n break\n case \"reasoning-delta\":\n reasoning += (value as any).delta ?? \"\"\n break\n case \"tool-call\":\n toolCalls.push({\n type: \"tool-call\",\n toolCallId: (value as any).toolCallId,\n toolName: (value as any).toolName,\n input: (value as any).input,\n providerExecuted: (value as any).providerExecuted,\n } as any)\n break\n case \"finish\":\n finishReason = (value as any).finishReason ?? finishReason\n usage = (value as any).usage ?? usage\n providerMetadata = (value as any).providerMetadata ?? providerMetadata\n break\n }\n }\n\n const content: LanguageModelV3Content[] = []\n if (reasoning) {\n content.push({ type: \"reasoning\", text: reasoning } as any)\n }\n if (text) {\n content.push({ type: \"text\", text, providerMetadata } as any)\n }\n content.push(...toolCalls)\n\n return {\n content,\n finishReason,\n usage,\n request: result.request,\n response: {\n id: generateId(),\n timestamp: new Date(),\n modelId: this.modelId,\n },\n providerMetadata,\n warnings: [],\n }\n }\n\n async doGenerate(\n options: LanguageModelV3CallOptions,\n ): Promise<Awaited<ReturnType<LanguageModelV3[\"doGenerate\"]>>> {\n const result = await this.doGenerateForHost(options)\n if (this.config.hostApi !== \"v2\") return result\n const translate = createHostToolPartTranslator(\"v2\")\n return {\n ...result,\n content: result.content.flatMap((part) => {\n const next = translate(part as any)\n return next ? [next as unknown as LanguageModelV3Content] : []\n }),\n }\n }\n\n private async doGenerateForHost(\n options: LanguageModelV3CallOptions,\n ): Promise<Awaited<ReturnType<LanguageModelV3[\"doGenerate\"]>>> {\n if (!this.isCompactionCall(options) && this.requestScope(options as any) !== \"no-tools\" && parseSideQuestion(options.prompt)) {\n return this.doGenerateViaStream(options)\n }\n const warnings: SharedV3Warning[] = []\n const scope = this.requestScope(options as any)\n const affinity = this.sessionAffinity(options)\n const cwd = await resolveSpawnCwdForSession(this.config.cwd, affinity)\n // An agent may run on a different model than the one opencode routed here\n // (see agent-models.ts). The session key must carry the effective model or\n // an overridden agent shares a claude process with its caller.\n const effectiveModelId = resolveAgentModel(\n this.getOpencodeAgent(options),\n this.modelId,\n )\n const reasoningEffort = resolveAgentEffort(\n this.getOpencodeAgent(options),\n this.getReasoningEffort(options.providerOptions),\n ) as ReasoningEffort | undefined\n // Keep effort invalidation inside one agent/provider, even when callers\n // share a model and opencode session (for example switching agents).\n const baseKey = sessionKey(\n cwd,\n `${effectiveModelId}::${scope}::${affinity}::context=${JSON.stringify([this.config.provider, this.getOpencodeAgent(options) ?? null])}`,\n )\n const sk = effortSessionKey(baseKey, reasoningEffort)\n\n // When selective proxying is enabled, doGenerate must not bypass the\n // proxy path. Reuse doStream and aggregate its events so proxied tools\n // still route through opencode permissions/execution. Same for\n // opencode MCP proxying — doStream is the only path that wires up the\n // proxy server with the dynamically-discovered MCP tool defs.\n const compactionMode = this.isCompactionCall(options)\n\n if (\n scope === \"tools\" &&\n (this.resolvedProxyTools() ||\n (this.config.proxyOpencodeMcpTools === true &&\n this.config.bridgeOpencodeMcp !== false))\n ) {\n return this.doGenerateViaStream(options)\n }\n\n // Route compaction through doStream so it gets the lean spawn path,\n // model override, and rich transcript handling. Aggregating a stream\n // for doGenerate matches what doGenerateViaStream already does for\n // proxy tools.\n if (compactionMode) {\n return this.doGenerateViaStream(options)\n }\n\n if (this.isTitleRequest(scope, options)) {\n log.info(\"doGenerate no-tools title stub\", {\n compactionMode,\n opencodeAgent: this.getOpencodeAgent(options),\n providerOptionsKeys: options.providerOptions\n ? Object.keys(options.providerOptions)\n : [],\n })\n const text = this.synthesizeTitle(options.prompt)\n return {\n content: [{ type: \"text\", text }] as any,\n finishReason: this.toFinishReason(\"stop\"),\n usage: this.toUsage({ input_tokens: 0, output_tokens: 0 }),\n request: { body: { text: \"\" } },\n response: {\n id: generateId(),\n timestamp: new Date(),\n modelId: this.modelId,\n },\n providerMetadata: {\n \"claude-code\": {\n synthetic: true,\n path: \"no-tools\",\n },\n },\n warnings,\n }\n }\n\n // Short-circuit when opencode iterates the agent loop one more time\n // after a turn already finished. The prompt ends with an assistant\n // message and has no fresh user input — spawning Claude here would\n // just produce a stub like \"No input received. Standing by\".\n if (!hasNewUserContent(options.prompt)) {\n log.info(\"doGenerate short-circuit: no new user content\")\n return {\n content: [],\n finishReason: this.toFinishReason(\"stop\"),\n usage: this.toUsage({ input_tokens: 0, output_tokens: 0 }),\n request: { body: { text: \"\" } },\n response: {\n id: generateId(),\n timestamp: new Date(),\n modelId: this.modelId,\n },\n providerMetadata: {\n \"claude-code\": { synthetic: true, path: \"no-new-user-content\" },\n },\n warnings,\n }\n }\n\n invalidateOtherEffortSessions(baseKey, reasoningEffort)\n\n const hasPriorConversation =\n options.prompt.filter((m) => m.role === \"user\" || m.role === \"assistant\")\n .length > 1\n\n // New session — clear any stale state from a previous session.\n // A compression summary is scoped to one conversation, so this is the\n // one place it is dropped: the compress restart itself calls\n // deleteClaudeSessionId, and clearing there would wipe the summary\n // just before the fresh spawn reads it.\n if (!hasPriorConversation) {\n deleteClaudeSessionId(sk)\n deleteActiveProcess(sk)\n clearCompression(sk)\n }\n\n const hasExistingSession = !!getClaudeSessionId(sk)\n const includeHistoryContext = !hasExistingSession && hasPriorConversation\n\n const userMsg =\n consumeExitPlanModeQuestionResult(sk, options.prompt as any) ??\n // doGenerate has no proxy wiring, so this process issued no tool calls\n // at all: every tool result reaching it belongs to opencode and must be\n // rendered as text rather than an orphaned `tool_result` (issue #29).\n getClaudeUserMessage(options.prompt, includeHistoryContext, {\n cliToolCallIds: new Set<string>(),\n stripContextReminders: this.stripContextRemindersEnabled(),\n })\n\n // The same account override doStream applies, with no dialog of its own:\n // a title or no-tools call must not ask anything, but it must follow the\n // account the conversation was moved to, or it bills the limited one.\n const failover = await resolveFailoverSpawn({\n account: this.config.account ?? DEFAULT_ACCOUNT,\n baseCliPath: this.config.baseCliPath ?? this.config.cliPath,\n cliPath: this.config.cliPath,\n modelId: effectiveModelId,\n })\n const cliPath = failover.cliPath\n\n // doGenerate always spawns a fresh process, never reuse session ID.\n // Pre-fetch opencode's MCP runtime status so the bridge overlays\n // UI-toggled state on top of disk config.\n const [runtimeStatus, cliVersion, planModeQuestionActive] = await Promise.all([\n getRuntimeMcpStatus(),\n detectCliVersion(cliPath),\n this.resolvePlanModeQuestion(compactionMode),\n ])\n const systemPromptFile = buildAppendedSystemPrompt(\n cwd,\n this.config.multiStepContinuation !== false,\n extractSystemMessages(options.prompt),\n // doGenerate has no proxy wiring, so `compress` is not callable here.\n // An existing summary still carries: it is this key's prior context.\n { compressEnabled: false, compressionSummary: getCompressionSummary(sk) },\n )\n const { model: spawnModelId, fast: fastMode } = parseModelId(failover.modelId)\n // The same skill bridge as doStream's spawn: Claude's Skill tool is the\n // only way a Claude-routed turn can load an opencode skill, on this path\n // as much as on the streaming one.\n const skillPluginDirs = await resolveSkillPluginDirs({\n cwd,\n cliPath,\n enabled: this.config.bridgeOpencodeSkills === true,\n ...this.skillBridgeSpawn(failover),\n })\n const cliArgs = buildCliArgs({\n sessionKey: sk,\n skipPermissions: this.config.skipPermissions !== false,\n includeSessionId: false,\n model: spawnModelId,\n permissionMode: this.config.permissionMode,\n mcpConfig: this.effectiveMcpConfig(cwd, undefined, runtimeStatus).paths,\n strictMcpConfig: this.config.strictMcpConfig,\n disallowedTools:\n this.config.webSearch === \"disabled\" ? [\"WebSearch\"] : undefined,\n appendSystemPromptFile: systemPromptFile,\n pluginDirs: skillPluginDirs,\n ...this.thinkingCliOptions(),\n fastMode,\n cliVersion,\n })\n\n log.info(\"doGenerate starting\", {\n cwd,\n model: effectiveModelId,\n requestedModel: this.modelId,\n textLength: userMsg.length,\n includeHistoryContext,\n })\n\n const { spawn } = await import(\"node:child_process\")\n const { createInterface } = await import(\"node:readline\")\n\n const proc = spawn(cliPath, cliArgs, {\n cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: claudeSpawnEnv({\n ignoreAnthropicApiKey: this.config.ignoreAnthropicApiKey,\n effort: reasoningEffort,\n }),\n shell: process.platform === \"win32\",\n })\n\n if (systemPromptFile) {\n proc.on(\"exit\", () => {\n void unlink(systemPromptFile).catch(() => {})\n })\n }\n\n const rl = createInterface({ input: proc.stdout! })\n\n let responseText = \"\"\n let thinkingText = \"\"\n let resultMeta: {\n sessionId?: string\n costUsd?: number\n durationMs?: number\n durationApiMs?: number\n numTurns?: number\n usage?: ClaudeStreamMessage[\"usage\"]\n modelUsage?: ClaudeStreamMessage[\"modelUsage\"]\n permissionDenials?: ClaudeStreamMessage[\"permission_denials\"]\n } = {}\n const toolCalls: Array<{ id: string; name: string; args: unknown }> = []\n // Streaming tool_use entries keyed by content-block index. We accumulate\n // partial_json chunks here instead of trying to JSON.parse each chunk\n // independently, and flush to `toolCalls` at content_block_stop. The\n // previous code indexed `toolCalls` by `msg.index` directly, which is\n // wrong whenever non-tool blocks (text, thinking) precede a tool_use.\n const toolCallStreams = new Map<\n number,\n { id: string; name: string; inputJson: string }\n >()\n\n // Set true once we observe a `stream_event` envelope. When on, the\n // top-level `assistant` message is a duplicate of content already\n // accumulated via the inner content_block_* events — skip it.\n let gotPartialEvents = false\n\n const result = await new Promise<\n typeof resultMeta & {\n text: string\n thinking: string\n toolCalls: typeof toolCalls\n }\n >((resolve, reject) => {\n const cleanup = () => {\n try {\n if (!proc.killed && proc.exitCode === null) proc.kill()\n } catch {}\n }\n\n rl.on(\"line\", (line) => {\n if (!line.trim()) return\n try {\n const outer: ClaudeStreamMessage = JSON.parse(line)\n\n // Unwrap stream_event envelope (--include-partial-messages).\n // Inner event uses the same content_block_* / message_* shape.\n const msg: ClaudeStreamMessage =\n outer.type === \"stream_event\" && outer.event\n ? { ...outer.event, session_id: outer.session_id }\n : outer\n\n if (outer.type === \"stream_event\") {\n gotPartialEvents = true\n }\n\n if (this.handleControlRequest(msg, proc)) {\n return\n }\n\n if (msg.type === \"system\" && msg.subtype === \"init\") {\n if (msg.session_id) {\n setClaudeSessionId(sk, msg.session_id)\n }\n reportFastModeState(msg, fastMode)\n reportSystemInit(msg, {\n ignoreAnthropicApiKey: this.config.ignoreAnthropicApiKey,\n })\n }\n\n if (msg.type === \"rate_limit_event\") {\n reportRateLimitEvent(msg)\n return\n }\n\n if (\n msg.type === \"assistant\" &&\n msg.message?.content &&\n !gotPartialEvents\n ) {\n for (const block of msg.message.content) {\n if (block.type === \"text\" && block.text) {\n responseText += block.text\n }\n if (block.type === \"thinking\" && block.thinking) {\n thinkingText += block.thinking\n }\n if (block.type === \"tool_use\" && block.id && block.name) {\n if (isAskUserQuestionTool(block.name)) {\n // Render the full question + options as visible text so\n // the user can actually see and answer it.\n const parsedInput = (block.input ?? {}) as Record<\n string,\n unknown\n >\n responseText += formatAskUserQuestion(parsedInput)\n continue\n }\n\n if (block.name === \"ExitPlanMode\") {\n const parsedInput = (block.input ?? {}) as Record<\n string,\n unknown\n >\n const plan = (parsedInput?.plan as string) || \"\"\n if (planModeQuestionActive) {\n const questionCall = createExitPlanModeQuestionCall(\n sk,\n block.id,\n plan,\n )\n responseText += questionCall.text\n toolCalls.push({\n id: questionCall.toolCallId,\n name: questionCall.toolName,\n args: questionCall.input,\n })\n continue\n }\n responseText += `\\n\\n${plan}\\n\\n---\\n**Do you want to proceed with this plan?** (yes/no)\\n`\n continue\n }\n\n toolCalls.push({\n id: block.id,\n name: block.name,\n args: block.input ?? {},\n })\n }\n }\n }\n\n if (\n msg.type === \"content_block_start\" &&\n msg.content_block &&\n msg.index !== undefined\n ) {\n if (\n msg.content_block.type === \"tool_use\" &&\n msg.content_block.id &&\n msg.content_block.name\n ) {\n toolCallStreams.set(msg.index, {\n id: msg.content_block.id,\n name: msg.content_block.name,\n inputJson: \"\",\n })\n }\n }\n\n if (\n msg.type === \"content_block_delta\" &&\n msg.delta &&\n msg.index !== undefined\n ) {\n if (msg.delta.type === \"text_delta\" && msg.delta.text) {\n responseText += msg.delta.text\n }\n if (msg.delta.type === \"thinking_delta\" && msg.delta.thinking) {\n thinkingText += msg.delta.thinking\n }\n if (\n msg.delta.type === \"input_json_delta\" &&\n msg.delta.partial_json\n ) {\n const tc = toolCallStreams.get(msg.index)\n if (tc) tc.inputJson += msg.delta.partial_json\n }\n }\n\n if (msg.type === \"content_block_stop\" && msg.index !== undefined) {\n const tc = toolCallStreams.get(msg.index)\n if (tc) {\n let args: unknown = {}\n try {\n args = tc.inputJson ? JSON.parse(tc.inputJson) : {}\n } catch (err) {\n log.warn(\"tool input JSON parse failed\", {\n name: tc.name,\n error: String(err),\n })\n }\n if (tc.name === \"ExitPlanMode\" && planModeQuestionActive) {\n const parsedInput = args as Record<string, unknown>\n const plan = (parsedInput?.plan as string) || \"\"\n const questionCall = createExitPlanModeQuestionCall(sk, tc.id, plan)\n responseText += questionCall.text\n toolCalls.push({\n id: questionCall.toolCallId,\n name: questionCall.toolName,\n args: questionCall.input,\n })\n } else {\n toolCalls.push({ id: tc.id, name: tc.name, args })\n }\n toolCallStreams.delete(msg.index)\n }\n }\n\n if (msg.type === \"result\") {\n if (msg.session_id) {\n setClaudeSessionId(sk, msg.session_id)\n }\n\n // Some CLI failures only surface user-readable text on the final\n // `result` message (without prior assistant text blocks). Preserve\n // that so callers don't receive an empty response.\n if (\n !responseText &&\n msg.is_error &&\n typeof msg.result === \"string\" &&\n msg.result.trim().length > 0\n ) {\n responseText = msg.result\n }\n\n resultMeta = {\n sessionId: msg.session_id,\n costUsd: msg.total_cost_usd,\n durationMs: msg.duration_ms,\n durationApiMs: msg.duration_api_ms,\n numTurns: msg.num_turns,\n usage: msg.usage,\n modelUsage: msg.modelUsage,\n permissionDenials: msg.permission_denials?.map((denial) => ({\n tool_name: denial.tool_name,\n tool_use_id: denial.tool_use_id,\n })),\n }\n log.info(\"conversation result\", {\n sessionId: msg.session_id,\n isError: msg.is_error,\n subtype: msg.subtype,\n ...turnStatsLogPayload(extractTurnStats(msg)),\n })\n cleanup()\n resolve({\n ...resultMeta,\n text: responseText,\n thinking: thinkingText,\n toolCalls,\n })\n }\n } catch {\n // Ignore non-JSON lines\n }\n })\n\n rl.on(\"close\", () => {\n cleanup()\n resolve({\n ...resultMeta,\n text: responseText,\n thinking: thinkingText,\n toolCalls,\n })\n })\n\n proc.on(\"error\", (err) => {\n log.error(\"process error\", { error: err.message })\n cleanup()\n reject(err)\n })\n\n proc.stderr?.on(\"data\", (data: Buffer) => {\n log.debug(\"stderr\", { data: data.toString().slice(0, 200) })\n })\n\n proc.stdin?.write(userMsg + \"\\n\")\n })\n\n const content: LanguageModelV3Content[] = []\n\n if (result.thinking) {\n content.push({\n type: \"reasoning\",\n text: result.thinking,\n } as any)\n }\n\n if (result.text) {\n content.push({\n type: \"text\",\n text: result.text,\n providerMetadata: {\n \"claude-code\": {\n sessionId: result.sessionId ?? null,\n costUsd: result.costUsd ?? null,\n durationMs: result.durationMs ?? null,\n },\n ...(typeof result.usage?.cache_creation_input_tokens === \"number\"\n ? {\n anthropic: {\n cacheCreationInputTokens:\n result.usage.cache_creation_input_tokens,\n },\n }\n : {}),\n },\n })\n }\n\n for (const tc of result.toolCalls) {\n if (tc.name === QUESTION_TOOL_NAME) {\n content.push({\n type: \"tool-call\",\n toolCallId: tc.id,\n toolName: tc.name,\n input: JSON.stringify(tc.args),\n providerExecuted: false,\n } as any)\n continue\n }\n\n const {\n name: mappedName,\n input: mappedInput,\n executed,\n skip,\n } = mapTool(tc.name, tc.args, {\n webSearch: this.config.webSearch,\n sessionId: getClaudeSessionId(sk),\n toolUseId: tc.id,\n })\n if (skip) continue\n content.push({\n type: \"tool-call\",\n toolCallId: tc.id,\n toolName: mappedName,\n input: JSON.stringify(mappedInput),\n providerExecuted: executed,\n } as any)\n }\n\n const usage = this.toUsage(result.usage)\n\n return {\n content,\n // Claude CLI's `result` message normally signals a fully-completed turn:\n // tools have already been executed internally and final assistant text\n // has been produced. ExitPlanMode is the exception: we surface it as\n // opencode's native question tool so the outer loop must run that tool.\n finishReason: this.toFinishReason(\n result.toolCalls.some((tc) => tc.name === QUESTION_TOOL_NAME)\n ? \"tool-calls\"\n : \"stop\",\n ),\n usage,\n request: { body: { text: userMsg } },\n response: {\n id: result.sessionId ?? generateId(),\n timestamp: new Date(),\n modelId: this.modelId,\n },\n providerMetadata: {\n \"claude-code\": {\n sessionId: result.sessionId ?? null,\n costUsd: result.costUsd ?? null,\n durationMs: result.durationMs ?? null,\n },\n ...(typeof result.usage?.cache_creation_input_tokens === \"number\"\n ? {\n anthropic: {\n cacheCreationInputTokens:\n result.usage.cache_creation_input_tokens,\n },\n }\n : {}),\n },\n warnings,\n }\n }\n\n /**\n * Tool parts leave in opencode 1.x's vocabulary; on opencode 2.x they are\n * renamed at this one edge (src/host-tools.ts). On V1 the stream is\n * returned untouched.\n */\n async doStream(\n options: LanguageModelV3CallOptions,\n ): Promise<Awaited<ReturnType<LanguageModelV3[\"doStream\"]>>> {\n const result = await this.doStreamForHost(options)\n return {\n ...result,\n stream: translateStreamForHost(result.stream as any, this.config.hostApi ?? \"v1\") as any,\n }\n }\n\n private async doStreamForHost(\n options: LanguageModelV3CallOptions,\n ): Promise<Awaited<ReturnType<LanguageModelV3[\"doStream\"]>>> {\n const warnings: SharedV3Warning[] = []\n const skipPermissions = this.config.skipPermissions !== false\n const scope = this.requestScope(options as any)\n const affinity = this.sessionAffinity(options)\n const cwd = await resolveSpawnCwdForSession(this.config.cwd, affinity)\n const compactionMode = this.isCompactionCall(options)\n // Use a separate session key for compaction so its short-lived spawn\n // never collides with the main conversation's claude process.\n const effectiveModelId = compactionMode\n ? this.resolveCompactionModel()\n : resolveAgentModel(\n this.getOpencodeAgent(options),\n this.modelId,\n )\n // Compaction skips request/agent effort overrides; other calls key on it.\n const reasoningEffort = compactionMode\n ? undefined\n : (resolveAgentEffort(\n this.getOpencodeAgent(options),\n this.getReasoningEffort(options.providerOptions),\n ) as ReasoningEffort | undefined)\n const baseKey = sessionKey(\n cwd,\n `${effectiveModelId}::${scope}::${affinity}::context=${JSON.stringify([this.config.provider, this.getOpencodeAgent(options) ?? null])}`,\n )\n const sk = compactionMode\n ? sessionKey(cwd, `${effectiveModelId}::compaction::${affinity}`)\n : effortSessionKey(baseKey, reasoningEffort)\n const toUsage = this.toUsage.bind(this)\n const toFinishReason = this.toFinishReason.bind(this)\n const handleControlRequest = this.handleControlRequest.bind(this)\n const flagOn = (v: string | undefined) =>\n v !== undefined &&\n ![\"\", \"0\", \"false\", \"no\", \"off\"].includes(v.trim().toLowerCase())\n // Interactive (subscription) transport: drive the claude TUI over Bun's\n // native ConPTY + JSONL tail instead of headless `--print` stream-json.\n // Prefer the provider option (config-driven, reliable in the GUI app where\n // process env vars are not inherited); fall back to the env var. Self-healing:\n // if Bun.Terminal is unavailable (e.g. not under Bun), use the headless path.\n const interactivePref =\n this.config.interactive ??\n flagOn(process.env.CLAUDE_CODE_INTERACTIVE_TRANSPORT)\n const useInteractive =\n interactivePref && typeof (globalThis as any).Bun?.Terminal === \"function\"\n const interactiveBypassRequested =\n this.config.interactiveBypass ??\n flagOn(process.env.CLAUDE_CODE_INTERACTIVE_BYPASS)\n\n // Account failover. When a previous turn hit this account's usage limit\n // and the operator picked another account, every turn from then on spawns\n // that account's wrapper instead, until the limit's reset time. The\n // override is keyed on the ACCOUNT, so it covers every session running on\n // it, subagents included. Resolved here, before anything reads `cliPath`.\n //\n // Excluded for the interactive transport, which drives a TUI over a PTY\n // with no proxy server: nothing in that path can show the form or replay\n // the conversation, so it keeps the plain rate-limit error.\n const sourceAccount = normalizeAccountName(\n this.config.account ?? DEFAULT_ACCOUNT,\n )\n const baseCliPath = this.config.baseCliPath ?? this.config.cliPath\n let failover: FailoverSpawn =\n useInteractive || compactionMode\n ? { cliPath: this.config.cliPath, modelId: effectiveModelId, failedOver: false }\n : await resolveFailoverSpawn({\n account: sourceAccount,\n baseCliPath,\n cliPath: this.config.cliPath,\n modelId: effectiveModelId,\n })\n let cliPath = failover.cliPath\n\n // Tagged onto the process each turn so the /btw command hook, which only\n // knows the opencode session id, can find it and ask it early\n // (btw-command.ts).\n const asideTransportRef = { cliPath, interactive: !!useInteractive }\n\n // `/claude-code-doctor` is answered here, by the plugin, with no CLI\n // inference at all: everything in the report is already in this process.\n // Same shape as the aside branch below, and the exchange is stripped from\n // rebuilt transcripts the same way a `/btw` pair is.\n const doctor =\n !compactionMode && scope !== \"no-tools\" ? parseDoctorCommand(options.prompt) : null\n if (doctor) {\n const doctorOptions = {\n cliPath,\n interactive: !!useInteractive,\n turnStats: this.config.turnStats === true,\n }\n const stream = new ReadableStream<LanguageModelV3StreamPart>({\n async start(controller) {\n controller.enqueue({ type: \"stream-start\", warnings })\n try {\n const text = await buildDoctorReport(doctorOptions)\n const id = generateId()\n controller.enqueue({ type: \"text-start\", id })\n controller.enqueue({ type: \"text-delta\", id, delta: text })\n controller.enqueue({ type: \"text-end\", id })\n controller.enqueue({\n type: \"finish\",\n finishReason: toFinishReason(\"stop\"),\n usage: toUsage({ input_tokens: 0, output_tokens: 0 }),\n providerMetadata: {\n \"claude-code\": { path: \"doctor\", synthetic: true, usageUnavailable: true },\n },\n })\n } catch (error) {\n controller.enqueue({ type: \"error\", error })\n } finally {\n controller.close()\n }\n },\n })\n return { stream, request: { body: { text: `/${DOCTOR_COMMAND}` } } }\n }\n\n const aside = !compactionMode && scope !== \"no-tools\" ? parseSideQuestion(options.prompt) : null\n if (aside) {\n // `/btw` is an ordinary user message in this conversation, so opencode\n // keeps the exchange, but it is answered over the CLI's side_question\n // control channel, never as a turn. The command hook normally sent the\n // question ahead, while the previous turn was still streaming, and its\n // answer is taken here; otherwise the process is idle now and is asked\n // directly. Earlier asides in this conversation ride along as history.\n const active = getActiveProcess(sk)\n const early = aside.question ? takeSideQuestionAnswer(affinity, aside.question) : undefined\n const history = collectSideQuestionHistory(options.prompt)\n const answerAside = async (): Promise<SideQuestionResult> => {\n if (!aside.question) return { response: SIDE_QUESTION_USAGE, synthetic: true }\n if (early) {\n try {\n return await early\n } catch (error) {\n log.info(\"btw: early answer failed, asking the idle process\", { error: String(error) })\n }\n }\n if (!active) return { response: BTW_NO_SESSION_MESSAGE, synthetic: true }\n return requestSideQuestion(active, aside.question, {\n cliVersion: await detectCliVersion(cliPath),\n interactive: useInteractive,\n abortSignal: options.abortSignal,\n ...(history.length ? { history } : {}),\n })\n }\n const stream = new ReadableStream<LanguageModelV3StreamPart>({\n async start(controller) {\n controller.enqueue({ type: \"stream-start\", warnings })\n try {\n const answer = await answerAside()\n const id = generateId()\n controller.enqueue({ type: \"text-start\", id })\n controller.enqueue({ type: \"text-delta\", id, delta: answer.response })\n controller.enqueue({ type: \"text-end\", id })\n controller.enqueue({\n type: \"finish\",\n finishReason: toFinishReason(\"stop\"),\n usage: toUsage({}),\n providerMetadata: { \"claude-code\": { path: \"side-question\", synthetic: answer.synthetic, usageUnavailable: true } },\n })\n } catch (error) {\n controller.enqueue({ type: \"error\", error })\n } finally {\n controller.close()\n }\n },\n })\n return { stream, request: { body: { text: aside.question } } }\n }\n\n if (this.isTitleRequest(scope, options) && !compactionMode) {\n log.info(\"doStream no-tools title stub\", {\n compactionMode,\n opencodeAgent: this.getOpencodeAgent(options),\n providerOptionsKeys: options.providerOptions\n ? Object.keys(options.providerOptions)\n : [],\n })\n const text = this.synthesizeTitle(options.prompt)\n const textId = generateId()\n const stream = new ReadableStream<LanguageModelV3StreamPart>({\n start(controller) {\n controller.enqueue({ type: \"stream-start\", warnings })\n controller.enqueue({ type: \"text-start\", id: textId } as any)\n controller.enqueue({\n type: \"text-delta\",\n id: textId,\n delta: text,\n })\n controller.enqueue({ type: \"text-end\", id: textId })\n controller.enqueue({\n type: \"finish\",\n finishReason: toFinishReason(\"stop\"),\n usage: toUsage({ input_tokens: 0, output_tokens: 0 }),\n providerMetadata: {\n \"claude-code\": {\n synthetic: true,\n path: \"no-tools\",\n },\n },\n })\n controller.close()\n },\n })\n\n return {\n stream,\n request: { body: { text: \"\" } },\n }\n }\n\n // Short-circuit when opencode iterates the agent loop one more time\n // after a turn already finished. The prompt ends with an assistant\n // message and has no fresh user input — spawning Claude here would\n // just produce a stub like \"No input received. Standing by\".\n if (!hasNewUserContent(options.prompt)) {\n log.info(\"doStream short-circuit: no new user content\")\n const stream = new ReadableStream<LanguageModelV3StreamPart>({\n start(controller) {\n controller.enqueue({ type: \"stream-start\", warnings })\n controller.enqueue({\n type: \"finish\",\n finishReason: toFinishReason(\"stop\"),\n usage: toUsage({ input_tokens: 0, output_tokens: 0 }),\n providerMetadata: {\n \"claude-code\": { synthetic: true, path: \"no-new-user-content\" },\n },\n })\n controller.close()\n },\n })\n return { stream, request: { body: { text: \"\" } } }\n }\n\n if (!compactionMode) invalidateOtherEffortSessions(baseKey, reasoningEffort)\n\n const hasPriorConversation =\n options.prompt.filter((m) => m.role === \"user\" || m.role === \"assistant\")\n .length > 1\n\n // New session — clear any stale state from a previous session.\n // A compression summary is scoped to one conversation, so this is the\n // one place it is dropped: the compress restart itself calls\n // deleteClaudeSessionId, and clearing there would wipe the summary\n // just before the fresh spawn reads it.\n if (!hasPriorConversation) {\n deleteClaudeSessionId(sk)\n deleteActiveProcess(sk)\n clearCompression(sk)\n }\n\n // The operator's answer to a failover form this session asked on an\n // earlier turn. Consumed before the session/process state below is read,\n // because a switch changes which account those belong to.\n const failoverAnswer =\n compactionMode || useInteractive\n ? null\n : consumeAccountFailoverAnswer(sk, options.prompt as any, {\n sourceAccount,\n candidates: failoverCandidates(this.config.failoverAccounts, sourceAccount),\n })\n\n if (failoverAnswer?.kind === \"stop\") {\n // Dismissed, answered `stop`, or answered with something that is not\n // one of the offered accounts. End the turn the way the rate-limit\n // error ends it today: no CLI inference, nothing spawned.\n log.warn(\"account failover declined; ending the turn\", {\n sessionKey: sk,\n account: sourceAccount,\n reason: failoverAnswer.reason,\n })\n const note = formatFailoverStopNote(failoverAnswer.reason)\n const stream = new ReadableStream<LanguageModelV3StreamPart>({\n start(controller) {\n controller.enqueue({ type: \"stream-start\", warnings })\n const id = generateId()\n controller.enqueue({ type: \"text-start\", id } as any)\n controller.enqueue({ type: \"text-delta\", id, delta: note })\n controller.enqueue({ type: \"text-end\", id })\n controller.enqueue({\n type: \"error\",\n error: new Error(\n `Claude account \"${sourceAccount}\" is out of usage and no other account was picked.`,\n ),\n })\n controller.enqueue({\n type: \"finish\",\n finishReason: { unified: \"error\" as const, raw: \"account_limit\" },\n usage: toUsage({ input_tokens: 0, output_tokens: 0 }),\n providerMetadata: {\n \"claude-code\": {\n path: \"account-failover-stop\",\n synthetic: true,\n usageUnavailable: true,\n },\n },\n })\n controller.close()\n },\n })\n return { stream, request: { body: { text: \"\" } } }\n }\n\n // The pick applies from this turn on, so re-resolve before the spawn\n // reads anything: this turn is the one that continues the task.\n let failoverNote: string | null = null\n if (failoverAnswer?.kind === \"switch\") {\n setAccountOverride(\n failoverAnswer.sourceAccount,\n failoverAnswer.target,\n failoverUntil(failoverAnswer.resetsAt),\n )\n failover = await resolveFailoverSpawn({\n account: sourceAccount,\n baseCliPath,\n cliPath: this.config.cliPath,\n modelId: effectiveModelId,\n })\n cliPath = failover.cliPath\n asideTransportRef.cliPath = cliPath\n failoverNote = formatFailoverNote({\n sourceAccount: failoverAnswer.sourceAccount,\n target: failoverAnswer.target,\n resetsAt: failoverAnswer.resetsAt,\n })\n }\n\n // A live process belongs to the account it was spawned with, and its\n // Claude transcript lives under that account's config dir, so neither can\n // follow the conversation across a switch. Dropping both here (before\n // `includeHistoryContext` is computed) is what turns the switch into a\n // fresh session with the thread replayed, and it is equally what switches\n // back once the override expires. The `?.cliPath &&` guard keeps the\n // interactive shim, which carries no path, out of it.\n const processForAccount = getActiveProcess(sk)\n if (\n !compactionMode &&\n !useInteractive &&\n processForAccount?.cliPath &&\n processForAccount.cliPath !== cliPath\n ) {\n log.notice(\"claude process belongs to another account; starting fresh\", {\n sessionKey: sk,\n was: processForAccount.cliPath,\n now: cliPath,\n failedOver: failover.failedOver,\n })\n deleteActiveProcess(sk)\n deleteClaudeSessionId(sk)\n }\n\n const hasExistingSession = !!getClaudeSessionId(sk)\n const hasActiveProcess = !!getActiveProcess(sk)\n let includeHistoryContext =\n !hasExistingSession && !hasActiveProcess && hasPriorConversation\n // A fresh session on the other account holds none of this conversation,\n // so the replay is not optional on a switch the way it is on a normal turn.\n if (failoverAnswer?.kind === \"switch\" && hasPriorConversation) {\n includeHistoryContext = true\n }\n\n // `effectiveModelId` stays intact for session keys, logs, and metadata;\n // only the name handed to the CLI gets the `-fast` marker stripped, and\n // (on a failover) the `@account` suffix the other account's wrapper would\n // not recognise.\n const { model: spawnModelId, fast: fastMode } = parseModelId(failover.modelId)\n\n const exitPlanModeQuestionResult = compactionMode\n ? null\n : consumeExitPlanModeQuestionResult(sk, options.prompt as any)\n if (exitPlanModeQuestionResult) {\n // The whole user message for this turn is the `tool_result` for the\n // pending ExitPlanMode call, so say so: an operator looking at a turn\n // that carries none of their typed text needs the reason in the log.\n log.info(\"sending plan approval decision to claude\", { sk })\n }\n // Read before the envelope is built, and used by it: only these ids were\n // issued by this CLI process, so only these may be sent back as\n // `tool_result` blocks (issue #29).\n const previousPendingProxyCalls = compactionMode\n ? []\n : getPendingProxyCalls(sk)\n // On a switch the dialog comes out of the transcript and a short note\n // telling the fresh session to carry on goes in as the current message.\n const effectivePrompt =\n failoverAnswer?.kind === \"switch\"\n ? buildFailoverContinuationPrompt(options.prompt, failoverAnswer.target)\n : options.prompt\n const userMsg =\n exitPlanModeQuestionResult ??\n getClaudeUserMessage(effectivePrompt, includeHistoryContext, {\n compactionMode,\n cliToolCallIds: new Set(previousPendingProxyCalls.map((c) => c.toolCallId)),\n stripContextReminders: this.stripContextRemindersEnabled(),\n })\n const resolvedProxy = compactionMode ? null : this.resolvedProxyTools()\n const loadLiveToolInfo = this.createLiveToolInfoLoader()\n // Resolved here, not inside the stream body: the ExitPlanMode branches\n // run in a synchronous line handler and a reused process never reaches\n // the spawn block where the registry snapshot is otherwise taken.\n const planModeQuestionActive = await this.resolvePlanModeQuestion(\n compactionMode,\n loadLiveToolInfo,\n )\n const self = this\n\n const previousPendingProxyMatches: Array<{\n call: PendingProxyCall\n result: ProxyToolResult | null\n }> = previousPendingProxyCalls.map((call) => ({\n call,\n result: this.extractPendingProxyResultForCall(options.prompt, call),\n }))\n const hasMatchedPendingResults = previousPendingProxyMatches.some(\n (m) => m.result !== null,\n )\n\n // Pre-fetch opencode's MCP runtime status before constructing the\n // ReadableStream so the sync hot-reload check and async setup() see\n // the same overlay snapshot. One in-process call per turn — cheap;\n // the SDK client routes through `Server.app.fetch` (no socket).\n // Detect the Claude CLI version in parallel so the spawn can decide\n // which optional flags it supports without crashing older binaries.\n const [runtimeStatus, cliVersion] = await Promise.all([\n compactionMode ? Promise.resolve(undefined) : getRuntimeMcpStatus(),\n detectCliVersion(cliPath),\n ])\n\n // Whether a usage limit on this account should end the turn with the\n // switch form. Resolved here, in the prologue, for the same reason the\n // plan-mode gate is: the `result` branch that needs the answer runs in a\n // synchronous line handler. The candidate check comes first so a\n // single-account install never pays for the two lookups behind it.\n const failoverAccounts = failoverCandidates(\n this.config.failoverAccounts,\n sourceAccount,\n )\n const failoverAskActive =\n failoverAccounts.length > 0 &&\n this.config.accountFailover !== \"off\" &&\n !compactionMode &&\n !useInteractive &&\n isAccountFailoverQuestionActive({\n configured: this.config.accountFailover,\n candidates: failoverAccounts,\n opencodeHasQuestion: (await loadLiveToolInfo()).hasQuestion,\n compactionMode,\n interactive: !!useInteractive,\n // A subagent follows its parent's account for free, because the\n // override is account-scoped. Asking it would put a form in a session\n // the operator is usually not even looking at.\n childSession: !!(await fetchSessionParentId(affinity)),\n })\n\n log.info(\"doStream starting\", {\n cwd,\n model: effectiveModelId,\n textLength: userMsg.length,\n includeHistoryContext,\n hasActiveProcess,\n reasoningEffort,\n proxyTools: resolvedProxy?.map((t) => t.name) ?? null,\n compactionMode,\n scope,\n opencodeAgent: this.getOpencodeAgent(options),\n providerOptionsKeys: options.providerOptions\n ? Object.keys(options.providerOptions)\n : [],\n })\n\n const stream = new ReadableStream<LanguageModelV3StreamPart>({\n start(controller) {\n // Compaction is a one-shot call. Don't reuse any cached process\n // from a prior compaction — each /compact gets a fresh spawn so\n // the new transcript isn't appended to a stale claude session.\n if (compactionMode) {\n deleteActiveProcess(sk)\n deleteClaudeSessionId(sk)\n }\n\n // A compress call lands mid-turn, when the child is still streaming,\n // so the reset it asks for happens here instead: drop the child and\n // its session id, and the spawn below starts clean. `userMsg` and\n // `includeHistoryContext` were resolved above while the session\n // still existed, so the fresh process is given only this turn's\n // message — the summary in its system prompt is the whole of its\n // prior context, exactly as the tool promised.\n //\n // Not while this turn carries results for the live child: evicting\n // it would send a tool_result to a process that never issued the\n // matching tool_use. The mark survives to the next turn.\n if (!compactionMode && !hasMatchedPendingResults && consumeCompressionRestart(sk)) {\n deleteActiveProcess(sk)\n deleteClaudeSessionId(sk)\n log.info(\"compress reset: dropped claude process and session id\", {\n sessionKey: sk,\n })\n }\n\n let activeProcess = getActiveProcess(sk)\n let proc: import(\"child_process\").ChildProcess\n let lineEmitter: import(\"events\").EventEmitter\n let cliArgs: string[]\n let proxyServer: ProxyMcpServer | null = activeProcess?.proxyServer ?? null\n\n const setup = async () => {\n // Wait for the old owner to exit before resuming its session ID in\n // the replacement, so two processes never append to one transcript.\n if (\n !compactionMode &&\n activeProcess &&\n self.config.hotReloadMcp !== false &&\n self.config.bridgeOpencodeMcp !== false\n ) {\n const probe = self.effectiveMcpConfig(cwd, undefined, runtimeStatus!)\n const previousHash = activeProcess.mcpHash ?? null\n if (previousHash !== probe.bridgedHash) {\n if (previousPendingProxyCalls.length > 0) {\n log.info(\"deferring MCP hot reload until proxy calls resolve\", {\n sk,\n previousHash,\n currentHash: probe.bridgedHash,\n pendingCalls: previousPendingProxyCalls.length,\n })\n } else {\n log.info(\"opencode MCP config changed, respawning claude\", {\n sk,\n previousHash,\n currentHash: probe.bridgedHash,\n })\n await deleteActiveProcessAndWait(sk)\n activeProcess = undefined\n proxyServer = null\n }\n }\n }\n\n if (useInteractive && !compactionMode) {\n // Interactive Bun-ConPTY transport. Reuse the live session if one\n // exists for this key; else spawn a new interactive claude. The\n // wrapper conforms to ActiveProcess, so reuse/eviction/hot-reload\n // and the whole emission body below work unchanged.\n const mcp = self.effectiveMcpConfig(cwd, undefined, runtimeStatus!)\n if (activeProcess) {\n proc = activeProcess.proc\n lineEmitter = activeProcess.lineEmitter\n log.debug(\"reusing active interactive session\", { sk })\n } else {\n // MCP wildcards are always derived from the live bridge config;\n // the built-in tool list is overridable via interactiveAllowTools.\n const allow = [\n ...mcp.allEnabledServerNames.map((n) => `mcp__${n}__*`),\n \"mcp__opencode_proxy__*\",\n ...(self.config.interactiveAllowTools ?? [\n \"Bash\",\n \"Edit\",\n \"Write\",\n \"Read\",\n \"WebFetch\",\n ]),\n ]\n const systemPromptFile =\n self.config.interactiveSystemPrompt === false\n ? undefined\n : buildAppendedSystemPrompt(\n cwd,\n self.config.multiStepContinuation !== false,\n // Do not forward opencode's own system prompt into the\n // interactive TUI. Live subscription-account testing\n // showed that large forwarded payload can trigger Claude\n // Code's third-party-app usage gate, while our static\n // CLI/AGENTS/continuation prompt remains safe.\n )\n if (self.config.interactiveSystemPrompt === false) {\n log.warn(\n \"interactive system prompt disabled; opencode agent prompts will not be appended\",\n )\n }\n if (interactiveBypassRequested) {\n log.warn(\n \"interactiveBypass ignored: Claude Code prompts for bypassPermissions confirmation in the interactive TUI\",\n )\n }\n // Same skill bridge as the headless spawn: the TUI's native\n // Skill tool reads `--plugin-dir` too, and the flag probe\n // keeps it off a CLI that does not know the flag.\n const skillPluginDirs = await resolveSkillPluginDirs({\n cwd,\n cliPath,\n enabled: self.config.bridgeOpencodeSkills === true,\n ...self.skillBridgeSpawn(failover),\n })\n const ap = spawnInteractiveProcess({\n cwd,\n cliPath,\n configDir: self.config.configDir,\n model: spawnModelId,\n fastMode,\n mcpConfigPaths: mcp.paths,\n pluginDirs: skillPluginDirs,\n permissionsAllow: allow,\n systemPromptFile,\n ignoreAnthropicApiKey: self.config.ignoreAnthropicApiKey,\n effort: reasoningEffort,\n })\n ap.mcpHash = mcp.bridgedHash\n setActiveProcess(sk, ap)\n proc = ap.proc\n lineEmitter = ap.lineEmitter\n activeProcess = ap\n log.info(\"spawned interactive claude session\", {\n sk,\n cliPath,\n configDir: self.config.configDir,\n model: effectiveModelId,\n })\n }\n } else {\n let spawnSystemPromptFile: string | undefined\n let spawnProxyServer: ProxyMcpServer | null = null\n let spawnMcpHash: string | null = null\n\n if (compactionMode) {\n // Compaction takes a lean spawn: no MCP servers, no proxy, no\n // appended system prompt, no disallowed-tools list. The model\n // is asked for text output only on a single turn — all the\n // normal tool wiring is pure overhead and adds latency.\n // Explicitly opt out of `--resume` so a stale id can never\n // resume into the lean spawn.\n cliArgs = buildCliArgs({\n sessionKey: sk,\n skipPermissions,\n includeSessionId: false,\n model: spawnModelId,\n permissionMode: self.config.permissionMode,\n fastMode,\n cliVersion,\n })\n } else {\n // First pass: discover which opencode MCP servers would be\n // bridged. We use this to decide which ones to re-route through\n // the proxy instead. No --mcp-config path is consumed here;\n // it's recomputed below with the exclusion set in place.\n const discovery = self.effectiveMcpConfig(\n cwd,\n undefined,\n runtimeStatus!,\n )\n\n // Fetch the proxy MCP tools (one ProxyToolDef per opencode\n // MCP-bridged tool). If discovery returns nothing or the SDK\n // is unreachable, this is null and we fall back to direct\n // bridging.\n const mcpResolution = self.resolvedProxyMcpTools(\n discovery.allEnabledServerNames,\n options.tools as readonly ModelToolEntry[] | undefined,\n new Set((resolvedProxy ?? []).map((def) => def.name)),\n )\n const proxyMcpTools = mcpResolution?.defs ?? null\n // Exclude only the servers a def was actually built for. Excluding\n // every enabled server, as this did while the resolution was always\n // null, would strand a server whose tools were not in the model's\n // tool set: dropped from `--mcp-config` and absent from the proxy,\n // so reachable by neither route.\n const excludeServers: ReadonlySet<string> | undefined = mcpResolution\n ? mcpResolution.coveredServers\n : undefined\n\n // Overlay opencode's live tool info onto the static proxy defs.\n // Both the `task` description (with the \"Available agent types\"\n // list, so the model sees which subagents exist instead of\n // grepping configs) and the `question` version gate (older\n // opencode builds lack the `question` registry entry; the def\n // must be dropped or a forwarded call renders `⚙ invalid`)\n // derive from a single tool-list fetch. Spawn-time only, like\n // the rest of this block; a reused process keeps its defs.\n const taskProxyEnabled =\n resolvedProxy?.some((t) => t.name === \"task\") ?? false\n const questionProxyEnabled =\n resolvedProxy?.some((t) => t.name === \"question\") ?? false\n // `proxyOpencodeTools` reads its defs out of the same registry\n // snapshot, so it joins the condition instead of fetching again.\n const opencodeToolsRequested =\n (self.config.proxyOpencodeTools?.length ?? 0) > 0\n log.debug(\"opencode tool forwarding gate\", {\n requested: self.config.proxyOpencodeTools ?? null,\n opencodeToolsRequested,\n })\n const liveToolInfo =\n taskProxyEnabled || questionProxyEnabled || opencodeToolsRequested\n ? await loadLiveToolInfo()\n : {\n resolved: false,\n taskDescription: undefined,\n questionDescription: undefined,\n hasQuestion: false,\n }\n let enrichedProxy = resolvedProxy\n if (enrichedProxy && taskProxyEnabled) {\n enrichedProxy = overlayTaskProxyDescription(\n enrichedProxy,\n liveToolInfo.taskDescription,\n )\n // Whether the model will see opencode's agent list is the\n // difference between a dispatch and an \"Unknown agent type\"\n // guess, so say so out loud.\n log.info(\"task proxy description overlay\", {\n applied: Boolean(liveToolInfo.taskDescription),\n liveDescriptionLength: liveToolInfo.taskDescription?.length ?? 0,\n listsAgentTypes: Boolean(\n liveToolInfo.taskDescription?.includes(\n \"Available agent types\",\n ),\n ),\n })\n }\n if (enrichedProxy && questionProxyEnabled) {\n // When the version gate is about to drop the def\n // (`hasQuestion === false`) the live description is moot,\n // so only overlay when the entry actually exists.\n enrichedProxy = overlayQuestionProxyDescription(\n enrichedProxy,\n liveToolInfo.hasQuestion\n ? liveToolInfo.questionDescription\n : undefined,\n )\n enrichedProxy = filterQuestionProxyByOpencodeSupport(\n enrichedProxy,\n liveToolInfo.hasQuestion,\n )\n // Same reasoning as the task overlay log: when the gate drops\n // the def the model silently falls back to the deny/markdown\n // path, which looks from the outside like the feature is off.\n log.info(\"question proxy version gate\", {\n opencodeHasQuestion: liveToolInfo.hasQuestion,\n kept: liveToolInfo.hasQuestion,\n })\n }\n\n // Combine the static proxy defs with any MCP-bridged proxy\n // tools. Guard against the empty case: a version gate can\n // drop every configured def (e.g. `proxyTools: [\"Question\"]`\n // on an opencode build that lacks the `question` registry\n // entry), and spinning up an MCP server with zero tools is\n // wasteful and wrong shape.\n // Opencode tools that belong to no MCP server are invisible to\n // resolvedProxyMcpTools, so an explicitly named one is resolved\n // here. It goes into the same combined list, which means the same\n // broker path, and therefore the same abort / orphan-sweep /\n // session-delete / child-exit release as every other proxy call.\n // Last in `taken`, so a static def or an MCP tool keeps a\n // contested name (`compress`) and this one is dropped with a\n // warning rather than shadowing it.\n const opencodeToolDefs = resolveProxyOpencodeToolDefs({\n requested: self.config.proxyOpencodeTools,\n items: liveToolInfo.items,\n taken: new Set(\n [...(enrichedProxy ?? []), ...(proxyMcpTools ?? [])].map(\n (t) => t.name,\n ),\n ),\n })\n if (opencodeToolDefs.length > 0) {\n log.info(\"forwarding opencode tools through the proxy\", {\n tools: opencodeToolDefs.map((t) => t.name),\n })\n }\n\n const combinedList = [\n ...(enrichedProxy ?? []),\n ...(proxyMcpTools ?? []),\n ...opencodeToolDefs,\n ]\n const combinedProxyTools: ProxyToolDef[] | null =\n combinedList.length > 0 ? combinedList : null\n\n const pluginCompressEnabled =\n enrichedProxy?.some((t) => t.name === \"compress\") ?? false\n\n if (!proxyServer && combinedProxyTools) {\n proxyServer = await self.ensureProxyServer(\n combinedProxyTools,\n sk,\n pluginCompressEnabled,\n )\n }\n\n // Whether the question proxy actually survived the version\n // gate (post-filter). Used to decide whether to inject the\n // QUESTION_PROXY_HINT — if the gate dropped the def, the\n // model must fall back to AskUserQuestion (the deny/markdown\n // path) and must NOT be told to call a proxy tool that does\n // not exist.\n const questionProxyActive =\n enrichedProxy?.some((t) => t.name === \"question\") ?? false\n\n // Compute disallowed flags from the POST-FILTER proxy list\n // (enrichedProxy), not the pre-filter one (resolvedProxy).\n // When the version gate drops `question` on an older opencode\n // build, AskUserQuestion must NOT be added to\n // --disallowedTools — otherwise the native tool is disabled\n // while the proxy replacement is absent, leaving the model\n // with no way to ask questions at all (neither proxy nor the\n // deny/markdown fallback path fires).\n const allDisallowed = resolveDisallowedTools({\n proxyTools: enrichedProxy,\n extraDisallowedTools: self.config.extraDisallowedTools,\n disableWebSearch: self.config.webSearch === \"disabled\",\n })\n const mcp = self.effectiveMcpConfig(\n cwd,\n proxyServer?.configPath(),\n runtimeStatus!,\n excludeServers,\n )\n const systemPromptFile = activeProcess\n ? undefined\n : buildAppendedSystemPrompt(\n cwd,\n self.config.multiStepContinuation !== false,\n [\n ...extractSystemMessages(options.prompt),\n ...(taskProxyEnabled ? [SUBAGENT_DISPATCH_HINT] : []),\n ...(questionProxyActive ? [QUESTION_PROXY_HINT] : []),\n ],\n {\n compressEnabled: pluginCompressEnabled,\n opencodeCompressEnabled: opencodeToolDefs.some(\n (t) => t.name === \"compress\",\n ),\n compressionSummary: getCompressionSummary(sk),\n },\n )\n // Skill bridge (@broskees): stage opencode skills as a\n // session-scoped --plugin-dir so Claude's Skill tool can run them.\n // Opt-in via `bridgeOpencodeSkills: true`; the bundled skill is\n // staged either way.\n const skillPluginDirs = await resolveSkillPluginDirs({\n cwd,\n cliPath,\n enabled: self.config.bridgeOpencodeSkills === true,\n ...self.skillBridgeSpawn(failover),\n })\n cliArgs = buildCliArgs({\n sessionKey: sk,\n skipPermissions,\n model: spawnModelId,\n permissionMode: self.config.permissionMode,\n mcpConfig: mcp.paths,\n strictMcpConfig: self.config.strictMcpConfig,\n disallowedTools: allDisallowed.length > 0 ? allDisallowed : undefined,\n appendSystemPromptFile: systemPromptFile,\n pluginDirs: skillPluginDirs,\n ...self.thinkingCliOptions(),\n fastMode,\n cliVersion,\n })\n spawnSystemPromptFile = systemPromptFile\n spawnProxyServer = proxyServer\n spawnMcpHash = mcp.bridgedHash\n }\n\n if (activeProcess && !compactionMode) {\n proc = activeProcess.proc\n lineEmitter = activeProcess.lineEmitter\n log.debug(\"reusing active process\", { sk })\n } else {\n const ap = spawnClaudeProcess(\n cliPath,\n cliArgs,\n cwd,\n sk,\n spawnProxyServer,\n spawnMcpHash,\n spawnSystemPromptFile,\n self.config.ignoreAnthropicApiKey,\n reasoningEffort,\n )\n proc = ap.proc\n lineEmitter = ap.lineEmitter\n activeProcess = ap\n }\n }\n\n // The CLI serves one turn at a time. If the previous one is still\n // running (the user aborted it, or it ended on our inactivity\n // fallback rather than a real `result`), stop it before this turn\n // attaches any listeners; otherwise its tail streams into us and its\n // `result` closes us before our own answer arrives. Skipped for\n // tool-result turns: there the CLI is deliberately parked inside a\n // proxy MCP call waiting for the result we are about to deliver.\n if (activeProcess && !hasMatchedPendingResults && isTurnInFlight(activeProcess)) {\n log.warn(\"previous turn still in flight; interrupting it\", { sk })\n const idle = await interruptTurn(activeProcess)\n if (!idle) {\n log.warn(\"previous turn did not stop in time; this turn may see stale output\", { sk })\n }\n }\n\n controller.enqueue({ type: \"stream-start\", warnings })\n\n let currentTextId: string | null = null\n const textBlockIndices = new Set<number>()\n\n const startTextBlock = (): string => {\n if (currentTextId) {\n controller.enqueue({ type: \"text-end\", id: currentTextId })\n }\n const id = generateId()\n currentTextId = id\n controller.enqueue({ type: \"text-start\", id } as any)\n return id\n }\n\n const endTextBlock = (): void => {\n if (currentTextId) {\n controller.enqueue({ type: \"text-end\", id: currentTextId })\n currentTextId = null\n }\n }\n\n // Its own text part, led by FAILOVER_MARKER, so a later transcript\n // rebuild strips it exactly: it was never Claude's output.\n if (failoverNote) {\n controller.enqueue({\n type: \"text-delta\",\n id: startTextBlock(),\n delta: failoverNote,\n })\n endTextBlock()\n }\n\n const reasoningIds = new Map<number, string>()\n const reasoningStarted = new Map<number, boolean>()\n let hadThinkingTextFromStream = false\n\n let turnCompleted = false\n let controllerClosed = false\n // Buffered terminal results belong to the previous CLI turn.\n let unattendedTurnEnded = false\n let watchdogMessage = userMsg\n let pendingProxyUnsubscribe: (() => void) | null = null\n let asideSinkUnregister: (() => void) | null = null\n let resultFallbackTimer: ReturnType<typeof setTimeout> | null = null\n let pendingResultCompletion: (() => void) | null = null\n let hasReceivedContent = false\n let hasReceivedProgress = false\n let visibleTextSinceContinue = \"\"\n let lastVisibleTextSinceContinue = \"\"\n let hadReasoningSinceContinue = false\n let hadToolActivitySinceContinue = false\n let hadProxyActivitySinceContinue = false\n // v0.4.16: protocol-level stop signal captured from Claude CLI's\n // stream. Set by either the `message_delta` partial event or the\n // top-level `assistant` message, whichever arrives first.\n let lastStopReason: string | null = null\n const autoContinueState: AutoContinueState = {\n enabled: autoContinueEnabledFor(\n compactionMode,\n self.config.autoContinueIncompleteTurns,\n ),\n attempts: 0,\n startedAt: Date.now(),\n noProgressCount: 0,\n }\n\n const clearFallbackTimer = () => {\n if (resultFallbackTimer) {\n clearTimeout(resultFallbackTimer)\n resultFallbackTimer = null\n }\n }\n\n // Wire-inactivity watchdog. Resets on every line received from the\n // CLI; only fires if the CLI has emitted content and then gone\n // silent on stdout for `delayMs` without sending a `result`. The\n // previous design armed this on every text content_block_stop,\n // which killed legitimate mid-turn think pauses (most visibly\n // with sonnet between text-end and the next tool_use_start).\n // Tunable for reproduces and for the regression test, the same seam\n // CLAUDE_CODE_START_WATCHDOG_MS gives the start watchdog below.\n const RESULT_FALLBACK_MS = (() => {\n const env = process.env.CLAUDE_CODE_RESULT_FALLBACK_MS\n const parsed = env ? Number.parseInt(env, 10) : NaN\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 60_000\n })()\n const startResultFallback = (delayMs = RESULT_FALLBACK_MS) => {\n clearFallbackTimer()\n if ((!hasReceivedContent && !hasReceivedProgress) || controllerClosed) return\n resultFallbackTimer = setTimeout(() => {\n if (controllerClosed) return\n log.warn(\"result fallback timer fired — closing stream without result event\", {\n delayMs,\n })\n // Closing on a log line alone left the operator with a reply that\n // just stopped. An abort is exempt: they asked for it, and the\n // short grace period there is not a silent CLI.\n if (!autoContinueState.aborted) {\n controller.enqueue({\n type: \"text-delta\",\n id: startTextBlock(),\n delta: formatStreamTimeoutNote(delayMs),\n })\n endTextBlock()\n }\n closeHandler()\n }, delayMs)\n }\n\n // Start watchdog: complementary to the inactivity watchdog above.\n // That one only arms once content has arrived; this one covers the\n // gap the other explicitly skips — a reused process that produces\n // NO stdout at all after a fresh-turn envelope write. Seen after a\n // very long proxy-blocked tool call resumed successfully (the child\n // stays silent on stdout). On first fire we respawn the child with\n // --session-id to resume the conversation transparently; on a\n // second fire (respawn also silent) we end the turn cleanly so the\n // next opencode turn spawns fresh. Tunable via env for reproduces.\n const START_WATCHDOG_MS = (() => {\n const env = process.env.CLAUDE_CODE_START_WATCHDOG_MS\n const parsed = env ? Number.parseInt(env, 10) : NaN\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 90_000\n })()\n let startWatchdog: ReturnType<typeof setTimeout> | null = null\n let respawnAttempted = false\n const clearStartWatchdog = () => {\n if (startWatchdog) {\n clearTimeout(startWatchdog)\n startWatchdog = null\n }\n }\n const onStartWatchdogFire = () => {\n startWatchdog = null\n if (controllerClosed || hasReceivedContent || hasReceivedProgress) return\n if (respawnAttempted) {\n log.error(\n \"claude process still silent after respawn; ending turn\",\n { sessionKey: sk },\n )\n deleteActiveProcess(sk)\n deleteClaudeSessionId(sk)\n controllerClosed = true\n cleanupTurn()\n controller.enqueue({\n type: \"error\",\n error: new Error(\n \"Claude process produced no output after the envelope write (start watchdog timeout).\",\n ),\n })\n try {\n controller.close()\n } catch {}\n return\n }\n respawnAttempted = true\n log.warn(\n \"no stdout after envelope write; respawning claude process to resume conversation\",\n { sessionKey: sk, startWatchdogMs: START_WATCHDOG_MS },\n )\n lineEmitter.off(\"line\", lineHandler)\n lineEmitter.off(\"close\", closeHandler)\n proc.off(\"error\", procErrorHandler)\n const newAp = respawnActiveProcess(\n sk,\n cliPath,\n cliArgs,\n cwd,\n self.config.ignoreAnthropicApiKey,\n )\n if (!newAp) {\n log.error(\n \"no active process to respawn (start watchdog); ending turn\",\n { sessionKey: sk },\n )\n controllerClosed = true\n cleanupTurn()\n controller.enqueue({\n type: \"error\",\n error: new Error(\n \"No active claude process to respawn after start watchdog timeout.\",\n ),\n })\n try {\n controller.close()\n } catch {}\n return\n }\n proc = newAp.proc\n lineEmitter = newAp.lineEmitter\n activeProcess = newAp\n lineEmitter.on(\"line\", lineHandler)\n lineEmitter.on(\"close\", closeHandler)\n proc.on(\"error\", procErrorHandler)\n try {\n if (!deliverPendingCompletions(true)) {\n noteTurnStarted(newAp)\n proc.stdin?.write(watchdogMessage + \"\\n\")\n }\n log.debug(\"re-sent user message after respawn\", {\n textLength: watchdogMessage.length,\n })\n } catch (err) {\n log.error(\"failed to re-send envelope after respawn\", {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n armStartWatchdog()\n }\n const armStartWatchdog = () => {\n clearStartWatchdog()\n if (controllerClosed) return\n startWatchdog = setTimeout(onStartWatchdogFire, START_WATCHDOG_MS)\n }\n\n // Both buffered/live terminal boundaries and respawn consume through\n // this path. Open-channel results remain available for a later close.\n const deliverPendingCompletions = (force = false): boolean => {\n const pending = activeProcess?.pendingProxyCompletions\n const entries = [...(pending?.values() ?? [])].filter(\n (entry) => force || entry.recoveryRequired || isPendingProxyCallChannelClosed(entry.call),\n )\n if (entries.length === 0) return false\n endTextBlock()\n watchdogMessage = makeLateProxyResultMessage(entries)\n // This write asks the CLI for work like any fresh envelope, so\n // abort, LRU eviction and the idle timer must see it as busy.\n if (activeProcess) noteTurnStarted(activeProcess)\n proc.stdin!.write(watchdogMessage + \"\\n\")\n for (const { call } of entries) pending!.delete(call.toolCallId)\n log.warn(\"delivering proxy results after interrupted continuation\", {\n sessionKey: sk,\n toolCallIds: entries.map(({ call }) => call.toolCallId),\n respawn: force,\n })\n gotPartialEvents = false\n hasReceivedContent = false\n hasReceivedProgress = false\n turnCompleted = false\n resetAutoContinueWindow()\n clearFallbackTimer()\n armStartWatchdog()\n return true\n }\n\n const toolCallMap = new Map<\n number,\n { id: string; name: string; inputJson: string; started: boolean }\n >()\n // Tool calls the plugin reported as providerExecuted:false — opencode\n // will run these itself and emit its own tool-result, so we must NOT\n // forward Claude CLI's tool_result for them (would short-circuit\n // opencode's execute).\n const skipResultForIds = new Set<string>()\n const toolCallsById = new Map<\n string,\n { id: string; name: string; input: unknown }\n >()\n\n let resultMeta: {\n sessionId?: string\n costUsd?: number\n durationMs?: number\n durationApiMs?: number\n numTurns?: number\n usage?: ClaudeStreamMessage[\"usage\"]\n modelUsage?: ClaudeStreamMessage[\"modelUsage\"]\n permissionDenials?: ClaudeStreamMessage[\"permission_denials\"]\n } = {}\n\n // Subtype of a failing `result`, so the finish below reports the\n // turn as an error instead of a clean stop.\n let resultFailure: string | undefined\n\n // Set only by a REJECTED rate-limit event or by one of the two\n // known account-limit error texts, never by a generic failure: a\n // transient error must not open a form that moves the billing.\n let accountLimitHit: { resetsAt?: number; window?: string } | null = null\n // The account itself cannot serve (an expired login, a billing\n // problem), from the `error` kind on the CLI's own failure reply.\n let accountBlock: AccountBlockKind | null = null\n\n // Batched drain so claude CLI's parallel tool_use blocks (e.g. two\n // bash calls in one assistant message) end up in a single\n // tool-calls finish event. Without this, the broker would reject\n // every overlapping call and claude would see spurious tool errors.\n const drainBuffer: PendingProxyCall[] = []\n let drainTimer: ReturnType<typeof setTimeout> | null = null\n const DRAIN_QUIET_MS = 100\n\n const finishWithToolCalls = (calls: PendingProxyCall[]) => {\n if (controllerClosed) return\n if (calls.length === 0) return\n const enqueueToolCall = (\n toolCallId: string,\n toolName: string,\n input: Record<string, unknown>,\n ) => {\n controller.enqueue({\n type: \"tool-input-start\",\n id: toolCallId,\n toolName,\n } as any)\n controller.enqueue({\n type: \"tool-call\",\n toolCallId,\n toolName,\n input: JSON.stringify(input),\n providerExecuted: false,\n } as any)\n skipResultForIds.add(toolCallId)\n }\n for (const call of calls) {\n if (call.toolName === TASK_BATCH_TOOL_NAME) {\n // One MCP call from the CLI becomes N opencode `task` calls in\n // this single tool boundary, which is what makes them run at the\n // same time: the CLI serialises MCP calls, opencode runs the\n // tool calls of one step concurrently. Their results are\n // gathered back onto the parent id in\n // extractPendingProxyResultForCall.\n for (const [index, task] of taskBatchTasks(call.input).entries()) {\n enqueueToolCall(\n taskBatchChildToolCallId(call.toolCallId, index),\n \"task\",\n task,\n )\n }\n skipResultForIds.add(call.toolCallId)\n } else {\n enqueueToolCall(call.toolCallId, call.toolName, call.input)\n }\n markPendingProxyCallEmitted(call.toolCallId)\n }\n controller.enqueue({\n type: \"finish\",\n finishReason: toFinishReason(\"tool-calls\"),\n usage: toUsage(resultMeta.usage),\n providerMetadata: {\n \"claude-code\": resultMeta,\n },\n })\n controllerClosed = true\n cleanupTurn()\n try {\n controller.close()\n } catch {}\n }\n\n /**\n * End the turn on a synthetic call to opencode's native `question`\n * tool. opencode runs the tool, and the operator's answer arrives on\n * the NEXT doStream as a `tool-result` carrying this same id, which\n * is what keeps the whole exchange inside one opencode turn. Shared\n * by the plan-mode approval bridge and the account-failover form.\n */\n const finishWithQuestionCall = (call: QuestionToolCall) => {\n if (controllerClosed) return\n endTextBlock()\n controller.enqueue({\n type: \"tool-input-start\",\n id: call.toolCallId,\n toolName: call.toolName,\n providerExecuted: false,\n } as any)\n controller.enqueue({\n type: \"tool-call\",\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n input: JSON.stringify(call.input),\n providerExecuted: false,\n } as any)\n controller.enqueue({\n type: \"finish\",\n finishReason: toFinishReason(\"tool-calls\"),\n usage: toUsage(resultMeta.usage),\n providerMetadata: {\n \"claude-code\": resultMeta,\n },\n })\n controllerClosed = true\n cleanupTurn()\n try {\n controller.close()\n } catch {}\n }\n\n const drainNow = () => {\n if (drainTimer) {\n clearTimeout(drainTimer)\n drainTimer = null\n }\n if (drainBuffer.length === 0) return\n if (controllerClosed) return\n const batch = drainBuffer.splice(0, drainBuffer.length)\n log.info(\"draining pending proxy calls into stream finish\", {\n sessionKey: sk,\n count: batch.length,\n toolCallIds: batch.map((c) => c.toolCallId),\n })\n finishWithToolCalls(batch)\n }\n\n const settleResultBoundary = () => {\n drainTimer = null\n const completeResult = pendingResultCompletion\n pendingResultCompletion = null\n if (!completeResult || controllerClosed) return\n if (drainBuffer.length > 0) {\n drainNow()\n return\n }\n completeResult()\n }\n\n const scheduleResultBoundary = (\n completeResult: () => void,\n delayMs: number,\n ) => {\n pendingResultCompletion = completeResult\n if (drainTimer) clearTimeout(drainTimer)\n drainTimer = setTimeout(settleResultBoundary, delayMs)\n }\n\n const noteResultBoundaryCall = (): boolean => {\n if (!pendingResultCompletion) return false\n if (drainTimer) clearTimeout(drainTimer)\n drainTimer = setTimeout(settleResultBoundary, DRAIN_QUIET_MS)\n return true\n }\n\n const noteVisibleText = (text: string) => {\n visibleTextSinceContinue += text\n lastVisibleTextSinceContinue += text\n }\n\n const resetLastVisibleTextBlock = () => {\n lastVisibleTextSinceContinue = \"\"\n }\n\n const noteReasoning = () => {\n hadReasoningSinceContinue = true\n }\n\n const noteToolActivity = () => {\n hadToolActivitySinceContinue = true\n }\n\n const noteProxyActivity = () => {\n hadProxyActivitySinceContinue = true\n }\n\n const resetAutoContinueWindow = () => {\n visibleTextSinceContinue = \"\"\n lastVisibleTextSinceContinue = \"\"\n hadReasoningSinceContinue = false\n hadToolActivitySinceContinue = false\n hadProxyActivitySinceContinue = false\n lastStopReason = null\n }\n\n const completeResult = (msg: ClaudeStreamMessage) => {\n if (controllerClosed) return\n // The socket may have closed after the tool-result prompt was matched,\n // or while the result-boundary grace timer was running.\n if (deliverPendingCompletions()) {\n if (drainBuffer.length > 0) drainNow()\n return\n }\n if (drainBuffer.length > 0) {\n drainNow()\n return\n }\n\n const pendingSiblings = getPendingProxyCalls(sk)\n if (pendingSiblings.length > 0) {\n log.info(\"leaving parallel proxy calls pending at result boundary\", {\n sessionKey: sk,\n count: pendingSiblings.length,\n })\n }\n\n activeProcess?.pendingProxyCompletions?.clear()\n\n // This account is out of usage. Rather than finish as an error the\n // operator can only act on by editing config, end the turn on a\n // form listing the other configured accounts. Leaving it unanswered\n // waits and costs nothing; every answer that is not one of those\n // accounts comes back as a `stop` and ends the turn as before.\n // Only a turn that failed: a limit event on a turn that was served\n // is information, and replacing its answer with this form would\n // throw the answer away.\n if ((accountLimitHit || accountBlock) && failoverAskActive && msg.is_error === true) {\n const call = createAccountFailoverQuestionCall(sk, {\n sourceAccount,\n candidates: failoverAccounts,\n resetsAt: accountLimitHit?.resetsAt,\n window: accountLimitHit?.window,\n reason: accountLimitHit || !accountBlock ? undefined : describeAccountBlock(accountBlock),\n })\n log.warn(\n `Claude account \"${sourceAccount}\" ${\n accountLimitHit || !accountBlock ? \"is out of usage\" : `cannot serve (${accountBlock})`\n }; asking which account to continue on.`,\n {\n sessionKey: sk,\n candidates: failoverAccounts,\n toolCallId: call.toolCallId,\n resetsAt: accountLimitHit?.resetsAt ?? null,\n },\n )\n finishWithQuestionCall(call)\n return\n }\n\n const autoDecision = shouldAutoContinueIncompleteTurn(\n autoContinueState,\n {\n text: visibleTextSinceContinue,\n lastVisibleText: lastVisibleTextSinceContinue,\n hadReasoning: hadReasoningSinceContinue,\n hadToolActivity: hadToolActivitySinceContinue,\n hadProxyActivity: hadProxyActivitySinceContinue,\n isError: msg.is_error,\n stopReason: lastStopReason,\n },\n )\n if (autoDecision.continue) {\n const signature = continuationSignature({\n text: visibleTextSinceContinue,\n lastVisibleText: lastVisibleTextSinceContinue,\n hadReasoning: hadReasoningSinceContinue,\n hadToolActivity: hadToolActivitySinceContinue,\n hadProxyActivity: hadProxyActivitySinceContinue,\n isError: msg.is_error,\n })\n autoContinueState.noProgressCount =\n signature === autoContinueState.lastSignature\n ? autoContinueState.noProgressCount + 1\n : 0\n autoContinueState.lastSignature = signature\n autoContinueState.attempts++\n log.notice(\"auto-continuing incomplete claude result\", {\n sessionKey: sk,\n reason: autoDecision.reason,\n attempts: autoContinueState.attempts,\n textLength: visibleTextSinceContinue.length,\n lastTextLength: lastVisibleTextSinceContinue.length,\n hadReasoning: hadReasoningSinceContinue,\n hadToolActivity: hadToolActivitySinceContinue,\n hadProxyActivity: hadProxyActivitySinceContinue,\n })\n turnCompleted = false\n resetAutoContinueWindow()\n // The `result` just consumed marked the CLI idle; this puts it back to work.\n if (activeProcess) noteTurnStarted(activeProcess)\n proc.stdin?.write(makeAutoContinueMessage() + \"\\n\")\n return\n }\n log.notice(\"auto-continuation stopped\", {\n sessionKey: sk,\n reason: autoDecision.reason,\n stopReason: lastStopReason,\n attempts: autoContinueState.attempts,\n textLength: visibleTextSinceContinue.length,\n lastTextLength: lastVisibleTextSinceContinue.length,\n hadReasoning: hadReasoningSinceContinue,\n hadToolActivity: hadToolActivitySinceContinue,\n hadProxyActivity: hadProxyActivitySinceContinue,\n })\n\n for (const [idx, reasoningId] of reasoningIds) {\n if (reasoningStarted.get(idx)) {\n controller.enqueue({\n type: \"reasoning-end\",\n id: reasoningId,\n } as any)\n }\n }\n\n controller.enqueue({\n type: \"finish\",\n finishReason: resultFailure\n ? { unified: \"error\" as const, raw: resultFailure }\n : toFinishReason(\"stop\"),\n usage: toUsage(msg.usage),\n providerMetadata: {\n \"claude-code\": {\n ...resultMeta,\n ...(resultFailure ? { resultSubtype: resultFailure } : {}),\n ...(compactionMode\n ? { compactionModel: effectiveModelId }\n : {}),\n },\n ...(typeof msg.usage?.cache_creation_input_tokens === \"number\"\n ? {\n anthropic: {\n cacheCreationInputTokens:\n msg.usage.cache_creation_input_tokens,\n },\n }\n : {}),\n },\n })\n\n controllerClosed = true\n cleanupTurn()\n if (!useInteractive && !compactionMode) {\n scheduleIdleProcessEviction(\n sk,\n resolveIdleProcessTimeoutMs(self.config.idleProcessTimeoutMs),\n )\n }\n\n try {\n controller.close()\n } catch {}\n }\n\n // Set true once we observe a `stream_event` envelope. When on, the\n // top-level `assistant` message is a duplicate of what we already\n // streamed via content_block_* deltas — skip its content.\n let gotPartialEvents = false\n\n const lineHandler = (line: string) => {\n if (!line.trim()) return\n if (controllerClosed) return\n\n // Any line from the CLI counts as activity — reset the inactivity\n // watchdog so mid-turn pauses between blocks don't get killed.\n startResultFallback()\n\n try {\n const outer: ClaudeStreamMessage = JSON.parse(line)\n\n // Unwrap stream_event envelope (--include-partial-messages).\n // Inner event uses the same content_block_* / message_* shape.\n const msg: ClaudeStreamMessage =\n outer.type === \"stream_event\" && outer.event\n ? { ...outer.event, session_id: outer.session_id }\n : outer\n\n const modelProgress =\n (msg.type === \"assistant\" && !!msg.message?.content?.length) ||\n (msg.type === \"content_block_start\" && msg.content_block?.type === \"tool_use\") ||\n (msg.type === \"content_block_delta\" &&\n ((msg.delta?.type === \"text_delta\" && !!msg.delta.text) ||\n (msg.delta?.type === \"thinking_delta\" && !!msg.delta.thinking)))\n if (modelProgress) {\n hasReceivedProgress = true\n clearStartWatchdog()\n startResultFallback()\n }\n\n if (outer.type === \"stream_event\") {\n gotPartialEvents = true\n }\n\n if (handleControlRequest(msg, proc)) {\n return\n }\n\n log.debug(\"stream message\", {\n type: msg.type,\n subtype: msg.subtype,\n })\n\n // Handle system init\n if (msg.type === \"system\" && msg.subtype === \"init\") {\n if (msg.session_id) {\n setClaudeSessionId(sk, msg.session_id)\n log.info(\"session initialized\", {\n claudeSessionId: msg.session_id,\n })\n }\n reportFastModeState(msg, fastMode)\n reportSystemInit(msg, {\n ignoreAnthropicApiKey: self.config.ignoreAnthropicApiKey,\n })\n }\n\n // The CLI compacted its own context. Nothing else tells the user\n // that everything before this point is now a summary.\n if (msg.type === \"system\" && msg.subtype === \"compact_boundary\") {\n const note = reportCompactBoundary(msg)\n if (note) {\n controller.enqueue({ type: \"text-delta\", id: startTextBlock(), delta: note })\n endTextBlock()\n }\n }\n\n // Claude Code started a new conversation (`/clear`, plan-mode\n // exit). Content-block indices restart with it, so nothing keyed\n // by index may survive: a stale `toolCallMap` entry re-emits a\n // finished tool call on the new conversation's first block, the\n // failure fixed on 2026-09-06. The Claude session id needs nothing\n // here; the `system/init` that follows carries the new one.\n if (msg.type === \"conversation_reset\") {\n const note = reportConversationReset(msg)\n if (note) {\n toolCallMap.clear()\n reasoningIds.clear()\n reasoningStarted.clear()\n textBlockIndices.clear()\n controller.enqueue({ type: \"text-delta\", id: startTextBlock(), delta: note })\n endTextBlock()\n }\n return\n }\n\n // Not returned from: the reply's own text still renders below.\n const block = accountBlockKind(msg)\n if (block) accountBlock = block\n\n // A rejection is why the turn is about to fail. Put it in the\n // transcript so the reason does not live only in a log file that\n // is off by default.\n if (msg.type === \"rate_limit_event\") {\n // Parsed separately from the reporter, which dedupes per\n // process and returns null on a repeat: the second rejection in\n // a session is still a rejection this turn has to act on.\n const info = parseRateLimitEvent(msg)\n if (info && isRateLimitRejected(info)) {\n accountLimitHit = {\n resetsAt: info.resetsAt ?? info.overageResetsAt,\n window: info.rateLimitType,\n }\n }\n const note = reportRateLimitEvent(msg)\n if (note) {\n controller.enqueue({ type: \"text-delta\", id: startTextBlock(), delta: note })\n endTextBlock()\n }\n return\n }\n\n // content_block_start\n if (\n msg.type === \"content_block_start\" &&\n msg.content_block &&\n msg.index !== undefined\n ) {\n const block = msg.content_block\n const idx = msg.index\n\n if (block.type === \"thinking\") {\n noteReasoning()\n const reasoningId = generateId()\n reasoningIds.set(idx, reasoningId)\n }\n\n if (block.type === \"text\") {\n textBlockIndices.add(idx)\n // New text block — clear last-block buffer so final-answer\n // detection only considers this block's contents, not earlier\n // mid-task narration.\n resetLastVisibleTextBlock()\n if (block.text) {\n if (!currentTextId) startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: currentTextId!,\n delta: block.text,\n })\n noteVisibleText(block.text)\n hasReceivedContent = true\n }\n }\n\n if (block.type === \"tool_use\" && block.id && block.name) {\n noteToolActivity()\n const entry = {\n id: block.id,\n name: block.name,\n inputJson: \"\",\n started: false,\n }\n toolCallMap.set(idx, entry)\n\n if (\n block.name !== \"AskUserQuestion\" &&\n block.name !== \"ask_user_question\" &&\n block.name !== \"ExitPlanMode\" &&\n !block.name.startsWith(PROXY_TOOL_PREFIX)\n ) {\n const { name: mappedName, skip, executed } = mapTool(\n block.name,\n undefined,\n {\n webSearch: self.config.webSearch,\n sessionId: getClaudeSessionId(sk),\n toolUseId: block.id,\n },\n )\n if (!skip) {\n entry.started = true\n controller.enqueue({\n type: \"tool-input-start\",\n id: block.id,\n toolName: mappedName,\n providerExecuted: executed,\n } as any)\n log.info(\"tool started\", {\n name: block.name,\n mappedName,\n id: block.id,\n })\n }\n }\n }\n }\n\n // content_block_delta\n if (\n msg.type === \"content_block_delta\" &&\n msg.delta &&\n msg.index !== undefined\n ) {\n const delta = msg.delta\n const idx = msg.index\n\n if (delta.type === \"thinking_delta\" && delta.thinking) {\n noteReasoning()\n hadThinkingTextFromStream = true\n const reasoningId = reasoningIds.get(idx)\n if (reasoningId) {\n if (!reasoningStarted.get(idx)) {\n controller.enqueue({\n type: \"reasoning-start\",\n id: reasoningId,\n } as any)\n reasoningStarted.set(idx, true)\n }\n controller.enqueue({\n type: \"reasoning-delta\",\n id: reasoningId,\n delta: delta.thinking,\n } as any)\n }\n }\n\n if (delta.type === \"text_delta\" && delta.text) {\n if (!currentTextId) startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: currentTextId!,\n delta: delta.text,\n })\n noteVisibleText(delta.text)\n hasReceivedContent = true\n }\n\n if (delta.type === \"input_json_delta\" && delta.partial_json) {\n const tc = toolCallMap.get(idx)\n if (tc) {\n tc.inputJson += delta.partial_json\n // Only forward deltas for tool calls whose tool-input-start\n // was actually emitted. Skipped tools (CLAUDE_INTERNAL_TOOLS,\n // TaskCreate/TaskUpdate, CLI-internal WebSearch, AskUserQuestion,\n // ExitPlanMode, proxy tools) never get a named start part, so\n // forwarding their deltas makes opencode's AI SDK bridge fall\n // back to a nameless pending part rendered as `⚙ unknown`.\n if (tc.started) {\n controller.enqueue({\n type: \"tool-input-delta\",\n id: tc.id,\n delta: delta.partial_json,\n } as any)\n }\n }\n }\n\n if (!KNOWN_DELTA_TYPES.has(delta.type)) {\n log.debug(\"unrecognized content_block_delta type\", {\n type: delta.type,\n idx,\n keys: Object.keys(delta),\n })\n }\n }\n\n // content_block_stop\n if (\n msg.type === \"content_block_stop\" &&\n msg.index !== undefined\n ) {\n const idx = msg.index\n\n const reasoningId = reasoningIds.get(idx)\n if (reasoningId && reasoningStarted.get(idx)) {\n controller.enqueue({\n type: \"reasoning-end\",\n id: reasoningId,\n } as any)\n reasoningStarted.delete(idx)\n }\n\n if (textBlockIndices.has(idx)) {\n endTextBlock()\n textBlockIndices.delete(idx)\n }\n\n const tc = toolCallMap.get(idx)\n if (tc) {\n // Block indices restart at 0 on every assistant message, and a\n // turn can hold several (tool_use -> tool_result -> answer).\n // Without this delete the entry outlives its message, so the\n // next message's block at the same index re-emits a tool-call\n // for an id opencode already completed. That second part never\n // gets a result, opencode aborts it at stream end, and a\n // subagent's `task` call reports \"Tool execution aborted\"\n // even though the child answered correctly.\n toolCallMap.delete(idx)\n let parsedInput: any = {}\n try {\n parsedInput = JSON.parse(tc.inputJson || \"{}\")\n } catch {}\n\n if (isAskUserQuestionTool(tc.name)) {\n // Latch: the model handed control to the operator. Block any\n // auto-continue nudge for the rest of the turn so it can't\n // proceed on its own before the operator replies.\n autoContinueState.sawAskUserQuestion = true\n const askId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: askId,\n delta: formatAskUserQuestion(parsedInput),\n })\n endTextBlock()\n } else if (tc.name === \"ExitPlanMode\") {\n const plan = (parsedInput?.plan as string) || \"\"\n\n if (planModeQuestionActive) {\n // Approval bridge: render the plan, then hand the\n // yes/no back to opencode's own `question` tool and end\n // the turn on \"tool-calls\" so the outer loop runs it.\n const questionCall = createExitPlanModeQuestionCall(\n sk,\n tc.id,\n plan,\n )\n const planId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: planId,\n delta: questionCall.text,\n })\n finishWithQuestionCall(questionCall)\n return\n }\n\n const planId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: planId,\n delta: `\\n\\n${plan}\\n\\n---\\n**Do you want to proceed with this plan?** (yes/no)\\n`,\n })\n endTextBlock()\n } else if (\n isWebSearchTool(tc.name) &&\n isWebSearchHandledByCli(self.config.webSearch)\n ) {\n // Claude CLI runs WebSearch internally. Forwarding the\n // \"WebSearch\" tool-call part would render an invalid tool\n // row in opencode (no registry entry), so show the query\n // as a text line instead. The result stays CLI-internal.\n const query =\n typeof parsedInput?.query === \"string\"\n ? parsedInput.query\n : JSON.stringify(parsedInput)\n const searchId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: searchId,\n delta: `\\n> **Web search:** ${query}\\n`,\n })\n endTextBlock()\n } else if (tc.name.startsWith(PROXY_TOOL_PREFIX)) {\n noteProxyActivity()\n log.debug(\"ignoring proxy tool_use block; broker handles it\", {\n name: tc.name,\n id: tc.id,\n })\n } else {\n const {\n name: mappedName,\n input: mappedInput,\n executed,\n skip,\n } = mapTool(tc.name, parsedInput, {\n webSearch: self.config.webSearch,\n sessionId: getClaudeSessionId(sk),\n toolUseId: tc.id,\n })\n\n if (!skip) {\n toolCallsById.set(tc.id, {\n id: tc.id,\n name: tc.name,\n input: parsedInput,\n })\n if (!executed) skipResultForIds.add(tc.id)\n\n controller.enqueue({\n type: \"tool-call\",\n toolCallId: tc.id,\n toolName: mappedName,\n input: JSON.stringify(mappedInput),\n providerExecuted: executed,\n } as any)\n }\n log.info(\"tool call complete\", {\n name: tc.name,\n mappedName,\n id: tc.id,\n executed,\n })\n }\n }\n }\n\n // Capture protocol-level stop_reason from the streaming\n // `message_delta` event (sent right before the final\n // `message_stop`). Any non-empty value is the source-of-truth\n // for why the turn ended — used to bypass the keyword heuristic.\n if (\n gotPartialEvents &&\n msg.type === \"message_delta\" &&\n typeof (msg as any).delta?.stop_reason === \"string\"\n ) {\n lastStopReason = (msg as any).delta.stop_reason\n }\n\n // assistant message (complete, not streaming).\n // When --include-partial-messages is on, this is a duplicate of\n // what we already streamed via content_block_* events. Skip it\n // for content, but still capture stop_reason from it for the\n // non-partial path.\n if (\n msg.type === \"assistant\" &&\n msg.message &&\n typeof (msg.message as any).stop_reason === \"string\"\n ) {\n lastStopReason = (msg.message as any).stop_reason\n }\n // Fallback: extract thinking from the complete assistant\n // message. opus-4-7's CLI strips thinking_delta from stream\n // events but may include thinking in the final message.\n if (\n msg.type === \"assistant\" &&\n msg.message?.content &&\n gotPartialEvents\n ) {\n const thinkingBlocks = (msg.message.content as any[]).filter(\n (b) => b.type === \"thinking\",\n )\n if (thinkingBlocks.length > 0) {\n log.info(\"assistant message thinking blocks\", {\n count: thinkingBlocks.length,\n hasText: thinkingBlocks.some(\n (b) => typeof b.thinking === \"string\" && b.thinking.length > 0,\n ),\n hadStreamThinking: hadThinkingTextFromStream,\n })\n if (!hadThinkingTextFromStream) {\n for (const block of thinkingBlocks) {\n if (block.thinking && block.thinking.length > 0) {\n noteReasoning()\n hadThinkingTextFromStream = true\n const thinkingId = generateId()\n controller.enqueue({\n type: \"reasoning-start\",\n id: thinkingId,\n } as any)\n controller.enqueue({\n type: \"reasoning-delta\",\n id: thinkingId,\n delta: block.thinking,\n } as any)\n controller.enqueue({\n type: \"reasoning-end\",\n id: thinkingId,\n } as any)\n }\n }\n }\n }\n }\n if (\n msg.type === \"assistant\" &&\n msg.message?.content &&\n !gotPartialEvents\n ) {\n const hasText = msg.message.content.some(\n (b: any) => b.type === \"text\" && b.text,\n )\n const hasToolUse = msg.message.content.some(\n (b: any) => b.type === \"tool_use\",\n )\n\n if (hasText) {\n hasReceivedContent = true\n }\n\n if (hasText && !hasToolUse) {\n startResultFallback()\n }\n if (hasToolUse) {\n clearFallbackTimer()\n }\n\n for (const block of msg.message.content) {\n if (block.type === \"text\" && block.text) {\n // New text block — keep only this block's text in the\n // last-block buffer for final-answer detection.\n resetLastVisibleTextBlock()\n const blockId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: blockId,\n delta: block.text,\n })\n endTextBlock()\n noteVisibleText(block.text)\n hasReceivedContent = true\n }\n\n if (block.type === \"thinking\" && block.thinking) {\n noteReasoning()\n const thinkingId = generateId()\n controller.enqueue({\n type: \"reasoning-start\",\n id: thinkingId,\n } as any)\n controller.enqueue({\n type: \"reasoning-delta\",\n id: thinkingId,\n delta: block.thinking,\n } as any)\n controller.enqueue({\n type: \"reasoning-end\",\n id: thinkingId,\n } as any)\n }\n\n if (block.type === \"tool_use\" && block.id && block.name) {\n noteToolActivity()\n const parsedInput = (block.input ?? {}) as Record<\n string,\n unknown\n >\n\n if (isAskUserQuestionTool(block.name)) {\n const askId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: askId,\n delta: formatAskUserQuestion(parsedInput),\n })\n endTextBlock()\n } else if (block.name === \"ExitPlanMode\") {\n const plan = (parsedInput?.plan as string) || \"\"\n\n if (planModeQuestionActive) {\n const questionCall = createExitPlanModeQuestionCall(\n sk,\n block.id,\n plan,\n )\n const planId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: planId,\n delta: questionCall.text,\n })\n finishWithQuestionCall(questionCall)\n return\n }\n\n const planId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: planId,\n delta: `\\n\\n${plan}\\n\\n---\\n**Do you want to proceed with this plan?** (yes/no)\\n`,\n })\n endTextBlock()\n } else if (\n isWebSearchTool(block.name) &&\n isWebSearchHandledByCli(self.config.webSearch)\n ) {\n // CLI-internal WebSearch: render the query as text and\n // drop the call/result parts (no opencode registry entry\n // for \"WebSearch\" — would render as an invalid tool row).\n toolCallsById.delete(block.id)\n const query =\n typeof parsedInput?.query === \"string\"\n ? parsedInput.query\n : JSON.stringify(parsedInput)\n const searchId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: searchId,\n delta: `\\n> **Web search:** ${query}\\n`,\n })\n endTextBlock()\n } else if (block.name.startsWith(PROXY_TOOL_PREFIX)) {\n noteProxyActivity()\n log.debug(\"ignoring proxy tool_use from assistant message\", {\n name: block.name,\n id: block.id,\n })\n } else {\n const {\n name: mappedName,\n input: mappedInput,\n executed,\n skip,\n } = mapTool(block.name, parsedInput, {\n webSearch: self.config.webSearch,\n sessionId: getClaudeSessionId(sk),\n toolUseId: block.id,\n })\n\n if (!skip) {\n toolCallsById.set(block.id, {\n id: block.id,\n name: block.name,\n input: parsedInput,\n })\n if (!executed) skipResultForIds.add(block.id)\n controller.enqueue({\n type: \"tool-input-start\",\n id: block.id,\n toolName: mappedName,\n providerExecuted: executed,\n } as any)\n controller.enqueue({\n type: \"tool-call\",\n toolCallId: block.id,\n toolName: mappedName,\n input: JSON.stringify(mappedInput),\n providerExecuted: executed,\n } as any)\n }\n log.info(\"tool_use from assistant message\", {\n name: block.name,\n mappedName,\n id: block.id,\n executed,\n })\n }\n }\n\n if (block.type === \"tool_result\") {\n log.debug(\"tool_result\", {\n toolUseId: block.tool_use_id,\n })\n }\n }\n }\n\n // user message (tool results from Claude CLI)\n if (msg.type === \"user\" && msg.message?.content) {\n for (const block of msg.message.content) {\n if (block.type === \"tool_result\" && block.tool_use_id) {\n if (skipResultForIds.has(block.tool_use_id)) {\n log.debug(\"skipping tool-result (opencode runs it)\", {\n toolUseId: block.tool_use_id,\n })\n continue\n }\n\n let resultText = \"\"\n if (typeof block.content === \"string\") {\n resultText = block.content\n } else if (Array.isArray(block.content)) {\n resultText = block.content\n .filter(\n (\n c,\n ): c is { type: string; text: string } =>\n c.type === \"text\" &&\n typeof c.text === \"string\",\n )\n .map((c) => c.text)\n .join(\"\\n\")\n }\n\n // Ledger hook: commit pending TaskCreate to opencode's todo\n // panel via a synthetic todowrite emission. Pass-through —\n // returns null for non-TaskCreate ids, so cheap and silent.\n const claudeSessionId = getClaudeSessionId(sk)\n if (claudeSessionId) {\n const list = applyTaskCreateToolResult(\n claudeSessionId,\n block.tool_use_id,\n resultText,\n )\n if (list) {\n const synthId = `todowrite_${block.tool_use_id}`\n controller.enqueue({\n type: \"tool-input-start\",\n id: synthId,\n toolName: \"todowrite\",\n providerExecuted: false,\n } as any)\n controller.enqueue({\n type: \"tool-call\",\n toolCallId: synthId,\n toolName: \"todowrite\",\n input: JSON.stringify({\n todos: list.map((t) => ({\n id: t.id,\n content: t.content,\n status: t.status,\n priority: \"medium\",\n })),\n }),\n providerExecuted: false,\n } as any)\n noteToolActivity()\n }\n }\n\n const toolCall = toolCallsById.get(block.tool_use_id)\n if (toolCall) {\n // A CLI-executed tool that failed carries `is_error`. The\n // AI SDK turns a `tool-result` with `isError` into a\n // `tool-error` part, which is what makes opencode render\n // the row as failed; without the flag every failed CLI\n // tool was forwarded as a success whose output happened\n // to be an error message.\n const isError = block.is_error === true\n controller.enqueue({\n type: \"tool-result\",\n toolCallId: block.tool_use_id,\n toolName: toolCall.name,\n result: {\n output: resultText,\n title: toolCall.name,\n metadata: isError ? { error: true } : {},\n },\n ...(isError ? { isError: true } : {}),\n providerExecuted: true,\n } as any)\n noteToolActivity()\n log.info(\"tool result emitted\", {\n toolUseId: block.tool_use_id,\n name: toolCall.name,\n isError,\n })\n toolCallsById.delete(block.tool_use_id)\n }\n }\n }\n }\n\n // result - end of conversation turn\n if (msg.type === \"result\") {\n clearFallbackTimer()\n\n if (msg.session_id) {\n setClaudeSessionId(sk, msg.session_id)\n }\n\n if (deliverPendingCompletions()) {\n // Finish the abandoned turn before submitting its late result.\n // Otherwise this result could close the stream for the new turn.\n return\n }\n\n // Some CLI failures only include user-readable text in\n // `result.result` (no prior assistant text blocks). Emit it so\n // opencode users don't see a blank turn.\n if (\n !currentTextId &&\n msg.is_error &&\n typeof msg.result === \"string\" &&\n msg.result.trim().length > 0\n ) {\n const errId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: errId,\n delta: msg.result,\n })\n }\n\n // The other half of the limit signal: some rejections only ever\n // reach us as the error text of the terminal result.\n if (\n !accountLimitHit &&\n msg.is_error &&\n isAccountLimitError({\n resultText: typeof msg.result === \"string\" ? msg.result : null,\n })\n ) {\n accountLimitHit = {}\n }\n\n // Say which account and what to run. Without this the only\n // thing on screen was the CLI's \"Failed to authenticate: OAuth\n // session expired\", which names neither.\n if (accountBlock && msg.is_error) {\n // The CLI labels this result `success` with `is_error: true`,\n // so nothing else marks the turn failed; without this it\n // finished as an ordinary `stop` with the error as its answer.\n resultFailure ??= accountBlock\n const offeringSwitch = failoverAskActive\n controller.enqueue({\n type: \"text-delta\",\n id: startTextBlock(),\n delta: formatAccountBlockNote({\n kind: accountBlock,\n account: sourceAccount,\n configDir: self.config.configDir,\n offeringSwitch,\n }),\n })\n endTextBlock()\n log.warn(`Claude account \"${sourceAccount}\" cannot serve requests`, {\n sessionKey: sk,\n kind: accountBlock,\n offeringSwitch,\n })\n }\n\n // A non-`success` subtype is a failed turn. Name it in the\n // transcript and finish as an error, rather than letting it be\n // recorded as an ordinary reply with the subtype only in a\n // debug log line.\n const failure = describeResultFailure(msg)\n if (failure) {\n resultFailure = msg.subtype\n controller.enqueue({\n type: \"text-delta\",\n id: startTextBlock(),\n delta: formatResultFailureNote(failure),\n })\n log.warn(failure, { sessionKey: sk, subtype: msg.subtype })\n }\n\n const turnStats = extractTurnStats(msg)\n resultMeta = {\n sessionId: msg.session_id,\n costUsd: msg.total_cost_usd,\n durationMs: msg.duration_ms,\n durationApiMs: msg.duration_api_ms,\n numTurns: msg.num_turns,\n usage: msg.usage,\n modelUsage: msg.modelUsage,\n // Names and ids only: a denial's `tool_input` can be a whole\n // file write payload and has no business in metadata.\n permissionDenials: msg.permission_denials?.map((denial) => ({\n tool_name: denial.tool_name,\n tool_use_id: denial.tool_use_id,\n })),\n }\n\n // Logged whatever `turnStats` is set to: the footer is a\n // display preference, the numbers are diagnostics.\n log.info(\"conversation result\", {\n sessionId: msg.session_id,\n numTurns: msg.num_turns,\n isError: msg.is_error,\n subtype: msg.subtype,\n ...turnStatsLogPayload(turnStats),\n })\n\n // Never on a compaction turn (the footer would be appended to\n // what opencode stores as the summary) and never on a failed\n // one (the error is the thing to read, not the bill).\n if (self.config.turnStats && !compactionMode && !msg.is_error && !failure) {\n const footer = formatTurnStatsBlock(turnStats)\n if (footer) {\n controller.enqueue({\n type: \"text-delta\",\n id: startTextBlock(),\n delta: footer,\n })\n }\n }\n\n turnCompleted = true\n\n endTextBlock()\n\n const shouldDeferResult =\n !msg.is_error &&\n !autoContinueState.aborted &&\n !autoContinueState.sawAskUserQuestion\n\n if (drainBuffer.length > 0 && shouldDeferResult) {\n log.info(\n \"waiting for parallel proxy calls at turn-result boundary\",\n {\n sessionKey: sk,\n count: drainBuffer.length,\n },\n )\n scheduleResultBoundary(\n () => completeResult(msg),\n DRAIN_QUIET_MS,\n )\n return\n }\n\n if (\n drainBuffer.length === 0 &&\n hadProxyActivitySinceContinue &&\n shouldDeferResult\n ) {\n log.info(\n \"waiting for delayed proxy call at turn-result boundary\",\n {\n sessionKey: sk,\n graceMs: PROXY_RESULT_BOUNDARY_GRACE_MS,\n },\n )\n scheduleResultBoundary(\n () => completeResult(msg),\n PROXY_RESULT_BOUNDARY_GRACE_MS,\n )\n return\n }\n\n completeResult(msg)\n }\n } catch (e) {\n log.debug(\"failed to parse line\", {\n error:\n e instanceof Error ? e.message : String(e),\n })\n }\n }\n\n const closeHandler = () => {\n log.debug(\"readline closed\")\n if (controllerClosed) return\n // Claude CLI's stdio is gone. The proxy-mcp HTTP requests that\n // backed any pending tool calls have no one to answer them now —\n // reject so the handlers return errors rather than hang.\n if (drainBuffer.length > 0 || getPendingProxyCalls(sk).length > 0) {\n rejectAllPendingProxyCallsForSession(\n sk,\n new Error(\n \"Claude CLI subprocess closed before pending tool calls were resolved\",\n ),\n )\n drainBuffer.length = 0\n }\n // A close without a terminal `result` means the child died mid-turn.\n // Reporting that as `stop` with empty usage made a crashed CLI look\n // like a short but successful answer. An abort is not a crash: the\n // operator asked for it, and the CLI may exit before its interrupt\n // result lands.\n const crashed = !turnCompleted && !autoContinueState.aborted\n controllerClosed = true\n cleanupTurn()\n endTextBlock()\n\n const finishClose = (\n exitCode: number | null,\n signal: NodeJS.Signals | null,\n ) => {\n if (crashed) {\n log.warn(\"claude process closed without a result\", {\n sessionKey: sk,\n exitCode,\n signal,\n stderrBytes: activeProcess?.lastStderr?.length ?? 0,\n })\n controller.enqueue({\n type: \"error\",\n error: new Error(\n describeChildCrash(exitCode, signal, activeProcess?.lastStderr),\n ),\n })\n }\n controller.enqueue({\n type: \"finish\",\n finishReason: toFinishReason(crashed ? \"error\" : \"stop\"),\n usage: toUsage(),\n providerMetadata: {\n \"claude-code\": {\n ...resultMeta,\n ...(compactionMode\n ? { compactionModel: effectiveModelId }\n : {}),\n },\n },\n })\n try {\n controller.close()\n } catch {}\n }\n\n // stdout usually reaches EOF a tick before the child's `exit` event,\n // so the status that explains the crash is not known yet here. The\n // turn is over either way; wait briefly for it rather than report a\n // bare \"closed its output\". Bounded, and only on the crash path.\n if (crashed && proc.exitCode === null && proc.signalCode === null) {\n let reported = false\n const report = (\n exitCode: number | null,\n signal: NodeJS.Signals | null,\n ) => {\n if (reported) return\n reported = true\n clearTimeout(exitGrace)\n proc.off(\"exit\", onExit)\n finishClose(exitCode, signal)\n }\n const onExit = (code: number | null, signal: NodeJS.Signals | null) =>\n report(code, signal)\n const exitGrace = setTimeout(\n () => report(proc.exitCode, proc.signalCode),\n CHILD_EXIT_STATUS_GRACE_MS,\n )\n proc.once(\"exit\", onExit)\n return\n }\n finishClose(proc.exitCode, proc.signalCode)\n }\n\n // Centralised per-turn teardown. Every exit path funnels through here\n // so we don't accumulate listeners across turns on a reused process.\n let cleanedUp = false\n const cleanupTurn = () => {\n if (cleanedUp) return\n cleanedUp = true\n clearFallbackTimer()\n pendingResultCompletion = null\n clearStartWatchdog()\n if (drainTimer) {\n clearTimeout(drainTimer)\n drainTimer = null\n }\n lineEmitter.off(\"line\", lineHandler)\n lineEmitter.off(\"close\", closeHandler)\n pendingProxyUnsubscribe?.()\n pendingProxyUnsubscribe = null\n asideSinkUnregister?.()\n asideSinkUnregister = null\n proc.off(\"error\", procErrorHandler)\n }\n\n const procErrorHandler = (err: Error) => {\n log.error(\"process error\", { error: err.message })\n deleteActiveProcess(sk)\n deleteClaudeSessionId(sk)\n if (controllerClosed) return\n // Subprocess failure invalidates every pending HTTP-bound tool\n // call for this session. Reject them so proxy-mcp returns errors\n // to Claude rather than letting the sockets stall.\n if (drainBuffer.length > 0 || getPendingProxyCalls(sk).length > 0) {\n rejectAllPendingProxyCallsForSession(\n sk,\n new Error(\n `Claude CLI subprocess error: ${err.message}`,\n ),\n )\n drainBuffer.length = 0\n }\n controllerClosed = true\n cleanupTurn()\n controller.enqueue({ type: \"error\", error: err })\n try {\n controller.close()\n } catch {}\n }\n\n // Whatever the child said while no turn was listening comes first:\n // the operator gets to see it, and a turn that already ended on the\n // CLI's side is known before this one decides what to send.\n if (activeProcess) {\n const unattended = takeUnattendedLines(activeProcess)\n if (unattended.lines.length > 0 || unattended.dropped > 0) {\n log.notice(\"replaying stdout the child emitted between turns\", {\n sessionKey: sk,\n lines: unattended.lines.length,\n dropped: unattended.dropped,\n })\n // Render narration only. Replaying actionable events could execute\n // old tools or close this new stream on a stale approval/result.\n let partialText = false\n {\n if (unattended.dropped > 0) {\n const id = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id,\n delta: `> _${unattended.dropped} lines of output emitted between turns were dropped._\\n\\n`,\n })\n }\n for (const line of unattended.lines) {\n try {\n const outer: ClaudeStreamMessage = JSON.parse(line)\n const msg = outer.type === \"stream_event\" && outer.event ? outer.event : outer\n let text = \"\"\n if (msg.type === \"content_block_delta\" && msg.delta?.type === \"text_delta\") {\n text = msg.delta.text ?? \"\"\n partialText = true\n } else if (msg.type === \"assistant\") {\n if (!partialText) text = (msg.message?.content ?? []).filter((part) => part.type === \"text\").map((part) => part.text ?? \"\").join(\"\")\n partialText = false\n } else if (msg.type === \"result\") {\n unattendedTurnEnded = true\n for (const entry of activeProcess.pendingProxyCompletions?.values() ?? []) {\n if (isPendingProxyCallChannelClosed(entry.call)) entry.recoveryRequired = true\n }\n if (outer.session_id) setClaudeSessionId(sk, outer.session_id)\n if (msg.is_error && msg.result) text = msg.result\n }\n if (text) controller.enqueue({ type: \"text-delta\", id: currentTextId ?? startTextBlock(), delta: text })\n } catch { /* Ignore incomplete or malformed buffered lines. */ }\n }\n }\n endTextBlock()\n // Replayed lines are history, not liveness: the watchdogs below\n // must judge the child on what it does from here on.\n clearFallbackTimer()\n hasReceivedContent = false\n }\n }\n\n if (activeProcess && !compactionMode) {\n activeProcess.opencodeSessionID = affinity\n activeProcess.asideTransport = asideTransportRef\n }\n if (!compactionMode) {\n // Lets a `/btw` answered while this turn runs land in the turn's own\n // reply instead of a toast (btw-command.ts). Its own text block, so\n // the marker stays at the start of a part and the block can be\n // stripped exactly when a transcript is rebuilt.\n asideSinkUnregister = registerAsideSink(affinity, (text) => {\n if (controllerClosed) return false\n const asideId = startTextBlock()\n controller.enqueue({ type: \"text-delta\", id: asideId, delta: text })\n endTextBlock()\n return true\n })\n }\n lineEmitter.on(\"line\", lineHandler)\n lineEmitter.on(\"close\", closeHandler)\n\n pendingProxyUnsubscribe = onPendingProxyCall(sk, (call) => {\n if (controllerClosed) {\n // Stream already closed (we already drained). Late arrival —\n // reject immediately so the proxy-mcp HTTP request returns\n // instead of hanging until its 10-min timeout.\n log.warn(\n \"pending proxy call arrived after stream close; rejecting\",\n {\n sessionKey: sk,\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n },\n )\n rejectPendingProxyCallById(\n call.toolCallId,\n new Error(\n `Pending proxy call '${call.toolName}' arrived after the stream was already closed`,\n ),\n )\n return\n }\n log.info(\"received pending proxy call for session\", {\n sessionKey: sk,\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n })\n noteProxyActivity()\n noteToolActivity()\n drainBuffer.push(call)\n if (noteResultBoundaryCall()) return\n if (drainTimer) clearTimeout(drainTimer)\n drainTimer = setTimeout(drainNow, DRAIN_QUIET_MS)\n })\n\n proc.on(\"error\", procErrorHandler)\n\n // On abort, keep process alive for next message\n if (options.abortSignal) {\n // Proxy calls this turn handed to opencode will never get a result\n // once the operator aborts: opencode stops its tool runs with the\n // turn. Release them now, so the CLI's parked requests return and\n // nothing waits for the next message to find out. Late-result\n // recovery is untouched: it holds results that already arrived.\n const releaseAbandonedProxyCalls = (reason: string) => {\n if (drainBuffer.length === 0 && getPendingProxyCalls(sk).length === 0) return\n rejectAllPendingProxyCallsForSession(sk, new Error(reason))\n drainBuffer.length = 0\n }\n options.abortSignal.addEventListener(\"abort\", () => {\n autoContinueState.aborted = true\n if (turnCompleted || controllerClosed) {\n // This stream already ended on a proxy tool boundary. An abort\n // here is NOT necessarily the operator: opencode 1.18.32 aborts\n // the signal of every step that ends in tool calls, about a\n // second after the finish, while it runs the tool (measured:\n // 348 of 938 proxied calls on 2026-09-23, and every call in a\n // plugin-only scratch config). Releasing on that rejected calls\n // that were working, told Claude \"the user doesn't want to\n // proceed\", and pushed each result into the next turn as text.\n // The abort reason is the same `AbortError` either way, so\n // opencode's session status decides: still busy means it is\n // running the tool, idle means the operator stopped the turn.\n // Unknown keeps the call, which at worst leaves a real abort\n // waiting for the next message, as it did before release-on-\n // abort existed.\n const stoppedProcess = activeProcess\n if (\n stoppedProcess &&\n stoppedProcess.lineEmitter.listenerCount(\"line\") === 0 &&\n getPendingProxyCalls(sk).length > 0\n ) {\n const reason = describeAbortReason(options.abortSignal?.reason)\n void settleSessionRunState(affinity).then((stopped) => {\n // Re-checked after the wait: a later turn that attached in\n // the meantime owns these calls now.\n const stillParked =\n stoppedProcess.lineEmitter.listenerCount(\"line\") === 0 &&\n getPendingProxyCalls(sk).length > 0\n // Only a positive `busy` keeps the call. `unknown` (no SDK\n // client, no status route, a failed read) releases exactly\n // as it did before this check existed, so a build that\n // cannot ask is never left worse off.\n if (stopped === \"busy\" || !stillParked) {\n log.debug(\"abort at a tool boundary while opencode is still running the turn; keeping pending calls\", {\n sk,\n session: stopped,\n stillParked,\n reason,\n })\n return\n }\n log.info(\"abort between proxy tool boundaries; releasing pending calls\", { sk, reason })\n void interruptTurn(stoppedProcess).then((idle) => {\n log.info(\"interrupt sent for aborted turn\", { sk, idle })\n })\n releaseAbandonedProxyCalls(\n \"Provider stream was aborted while opencode was running its proxy tool calls\",\n )\n })\n }\n return\n }\n\n // Stop the CLI's turn, not just our end of the stream: it would\n // otherwise run the abandoned turn to completion, billing tokens\n // and executing tools, with its late output landing in the next\n // turn. The process itself stays alive for the next message.\n if (activeProcess) {\n void interruptTurn(activeProcess).then((idle) => {\n log.info(\"interrupt sent for aborted turn\", { sk, idle })\n })\n }\n\n if (!hasReceivedContent) {\n log.info(\n \"abort signal received before content, closing stream immediately\",\n { cwd },\n )\n releaseAbandonedProxyCalls(\n \"Provider stream was aborted before pending proxy calls were emitted\",\n )\n controllerClosed = true\n cleanupTurn()\n try {\n controller.close()\n } catch {}\n return\n }\n\n log.info(\n \"abort signal received mid-turn, starting grace period\",\n { cwd },\n )\n releaseAbandonedProxyCalls(\n \"Provider stream was aborted while proxy tool calls were pending\",\n )\n // Abort grace period — short, since the user already asked to stop.\n startResultFallback(5_000)\n })\n }\n\n if (hasMatchedPendingResults) {\n // Tool-result turn: the prompt carries opencode's results for the\n // proxy tool calls we drained on the previous turn. Resolve each\n // matched call (claude CLI's HTTP handlers wake up and continue).\n // Parallel tools may complete in separate opencode turns. Keep\n // unmatched siblings pending until their own result, an explicit\n // abort/new user turn, or the proxy deadline.\n for (const { call, result } of previousPendingProxyMatches) {\n if (result) {\n const channelClosed = isPendingProxyCallChannelClosed(call)\n log.info(\"resolving pending proxy call from tool result prompt\", {\n sessionKey: sk,\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n channelClosed,\n })\n const completions = (activeProcess!.pendingProxyCompletions ??= new Map())\n if (!completions.has(call.toolCallId)) {\n completions.set(call.toolCallId, {\n call,\n result,\n recoveryRequired: channelClosed || unattendedTurnEnded,\n })\n }\n // With a closed channel this only clears the broker entry;\n // proxy-mcp drops the write and the result travels below.\n resolvePendingProxyCallById(call.toolCallId, result)\n } else {\n log.info(\n \"leaving unmatched parallel proxy call pending\",\n {\n sessionKey: sk,\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n },\n )\n }\n }\n\n if (unattendedTurnEnded) deliverPendingCompletions()\n\n // Calls queued while no turn was attached were never handed to\n // opencode; the child is blocked on them right now.\n const unemitted = getPendingProxyCalls(sk).filter(\n (call) => !call.emitted,\n )\n if (unemitted.length > 0) {\n log.notice(\"draining proxy calls queued between turns\", {\n sessionKey: sk,\n toolCallIds: unemitted.map((call) => call.toolCallId),\n })\n drainBuffer.push(...unemitted)\n drainNow()\n return\n }\n\n if (getPendingProxyCalls(sk).length === 0) {\n armStartWatchdog()\n }\n return\n }\n\n // No pending calls had matching tool-results. If any pending calls\n // are still hanging around from a prior turn, reject them so the\n // HTTP handlers in proxy-mcp don't sit blocked forever while we\n // proceed with a brand new user message.\n if (previousPendingProxyCalls.length > 0) {\n for (const call of previousPendingProxyCalls) {\n rejectPendingProxyCallById(\n call.toolCallId,\n new Error(\n `Pending proxy call '${call.toolName}' (${call.toolCallId}) was orphaned by a new user turn; rejecting`,\n ),\n )\n }\n }\n\n // Send the user message for a fresh turn.\n if (activeProcess) noteTurnStarted(activeProcess)\n proc.stdin?.write(userMsg + \"\\n\")\n log.debug(\"sent user message\", { textLength: userMsg.length })\n // Arm the start watchdog so a reused child that goes silent after\n // the envelope write (seen after a long proxy-blocked tool call)\n // is respawned with --session-id instead of hanging the turn.\n armStartWatchdog()\n }\n\n void setup().catch((err) => {\n log.error(\"failed to set up doStream\", {\n error: err instanceof Error ? err.message : String(err),\n })\n controller.enqueue({\n type: \"error\",\n error: err instanceof Error ? err : new Error(String(err)),\n })\n try {\n controller.close()\n } catch {}\n })\n },\n cancel() {\n // Consumer cancelled the stream\n },\n })\n\n return {\n stream,\n request: { body: { text: userMsg } },\n response: { headers: {} },\n }\n }\n}\n","import { appendFileSync, mkdirSync, renameSync, statSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { dirname, join } from \"node:path\"\n\nexport type LogLevel = \"debug\" | \"info\" | \"notice\" | \"warn\" | \"error\"\nexport type LogMode = \"silent\" | \"debug\"\n\nexport interface LoggerConfig {\n file: boolean\n dir: string | null\n mode: LogMode\n level: LogLevel\n}\n\nconst LEVEL_RANK: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n notice: 2,\n warn: 3,\n error: 4,\n}\n\nconst MAX_LOG_BYTES = 5 * 1024 * 1024 // 5 MB\nconst DEFAULT_DIR = join(homedir(), \".local\", \"share\", \"opencode-claude-code\")\n\nconst DEFAULT_CONFIG: LoggerConfig = {\n file: false,\n dir: null,\n mode: \"silent\",\n level: \"info\",\n}\n\nfunction parseBoolEnv(v: string | undefined): boolean | undefined {\n if (v == null) return undefined\n const s = v.toLowerCase().trim()\n if (s === \"\") return undefined\n if (s === \"0\" || s === \"false\" || s === \"no\" || s === \"off\") return false\n return true\n}\n\nfunction parseLevelEnv(v: string | undefined): LogLevel | undefined {\n if (v == null) return undefined\n const s = v.toLowerCase().trim()\n if (s === \"\") return undefined\n if (s === \"debug\" || s === \"info\" || s === \"notice\" || s === \"warn\" || s === \"error\") {\n return s\n }\n return undefined\n}\n\nfunction parseModeFromDebugEnv(v: string | undefined): LogMode | undefined {\n if (v == null || v === \"\") return undefined\n return v.includes(\"opencode-claude-code\") ? \"debug\" : undefined\n}\n\nfunction withEnvOverrides(base: LoggerConfig): LoggerConfig {\n const result: LoggerConfig = { ...base }\n const envFile = parseBoolEnv(process.env.OPENCODE_CLAUDE_CODE_LOG_FILE)\n if (envFile !== undefined) result.file = envFile\n const envDir = process.env.OPENCODE_CLAUDE_CODE_LOG_DIR\n if (envDir !== undefined && envDir !== \"\") result.dir = envDir\n const envMode = parseModeFromDebugEnv(process.env.DEBUG)\n if (envMode !== undefined) result.mode = envMode\n const envLevel = parseLevelEnv(process.env.OPENCODE_CLAUDE_CODE_LOG_LEVEL)\n if (envLevel !== undefined) result.level = envLevel\n return result\n}\n\nlet activeConfig: LoggerConfig = withEnvOverrides(DEFAULT_CONFIG)\nlet fileLoggingDisabled = false\n\n/**\n * Configure the logger from plugin settings. Env vars override the supplied\n * config when explicitly set, so a developer can flip behavior for a single\n * process without editing opencode.jsonc.\n *\n * `OPENCODE_CLAUDE_CODE_LOG_FILE` → `file` (1/true/on/yes vs 0/false/no/off)\n * `OPENCODE_CLAUDE_CODE_LOG_DIR` → `dir`\n * `DEBUG=opencode-claude-code` → `mode: \"debug\"`\n * `OPENCODE_CLAUDE_CODE_LOG_LEVEL` → `level` (debug | info | notice | warn | error)\n */\nexport function configureLogger(input: Partial<LoggerConfig>): void {\n const merged: LoggerConfig = { ...DEFAULT_CONFIG, ...input }\n activeConfig = withEnvOverrides(merged)\n fileLoggingDisabled = false\n}\n\nexport function getLoggerConfig(): LoggerConfig {\n return { ...activeConfig }\n}\n\n/** Test-only helper. Resets to defaults+env so tests are deterministic. */\nexport function _resetLoggerForTests(): void {\n activeConfig = withEnvOverrides(DEFAULT_CONFIG)\n fileLoggingDisabled = false\n}\n\nfunction resolvedLogFile(): string {\n return join(activeConfig.dir ?? DEFAULT_DIR, \"plugin.log\")\n}\n\nfunction rotateIfNeeded(logFile: string): void {\n try {\n const stat = statSync(logFile)\n if (stat.size > MAX_LOG_BYTES) {\n renameSync(logFile, `${logFile}.1`)\n }\n } catch {\n // file does not exist yet — nothing to rotate\n }\n}\n\nfunction writeToFile(line: string): void {\n if (!activeConfig.file) return\n if (fileLoggingDisabled) return\n try {\n const logFile = resolvedLogFile()\n mkdirSync(dirname(logFile), { recursive: true })\n rotateIfNeeded(logFile)\n appendFileSync(logFile, line + \"\\n\", \"utf8\")\n } catch {\n // Disable on first failure to avoid spamming errors on a read-only FS.\n fileLoggingDisabled = true\n }\n}\n\nfunction fmt(level: string, msg: string, data?: Record<string, unknown>): string {\n const ts = new Date().toISOString()\n const base = `[${ts}] [opencode-claude-code] ${level}: ${msg}`\n if (data && Object.keys(data).length > 0) {\n return `${base} ${JSON.stringify(data)}`\n }\n return base\n}\n\nfunction shouldEmit(level: LogLevel): boolean {\n return LEVEL_RANK[level] >= LEVEL_RANK[activeConfig.level]\n}\n\nfunction shouldTui(level: LogLevel): boolean {\n // warn/error are alwaysStderr: a developer who passes the level threshold\n // should still see real problems in the TUI regardless of mode. Below-\n // threshold entries are filtered earlier by shouldEmit().\n if (level === \"warn\" || level === \"error\") return true\n return activeConfig.mode === \"debug\"\n}\n\nfunction emit(level: LogLevel, msg: string, data?: Record<string, unknown>): void {\n if (!shouldEmit(level)) return\n const line = fmt(level.toUpperCase(), msg, data)\n if (shouldTui(level)) {\n console.error(line)\n }\n writeToFile(line)\n}\n\nexport const log = {\n debug(msg: string, data?: Record<string, unknown>) {\n emit(\"debug\", msg, data)\n },\n info(msg: string, data?: Record<string, unknown>) {\n emit(\"info\", msg, data)\n },\n notice(msg: string, data?: Record<string, unknown>) {\n emit(\"notice\", msg, data)\n },\n warn(msg: string, data?: Record<string, unknown>) {\n emit(\"warn\", msg, data)\n },\n error(msg: string, data?: Record<string, unknown>) {\n emit(\"error\", msg, data)\n },\n}\n","import { log } from \"./logger.js\"\n\nexport type TodoStatus = \"pending\" | \"in_progress\" | \"completed\"\n\nexport interface TodoEntry {\n id: string\n content: string\n status: TodoStatus\n}\n\ninterface PendingCreate {\n subject: string\n createdAt: number\n}\n\ninterface SessionLedger {\n todos: Map<string, TodoEntry>\n pendingCreates: Map<string, PendingCreate>\n}\n\nconst ledgers = new Map<string, SessionLedger>()\n\nconst PENDING_CREATE_TTL_MS = 60_000\n/**\n * A ledger is normally released with its Claude session id, but a session\n * that is never deleted (a long-lived `opencode serve` hopping projects, a\n * CLI session whose id the plugin never sees again) leaves one behind. Same\n * insertion-order cap as the compression store; a todo list belonging to a\n * session that old is not going to be written to opencode again.\n */\nexport const MAX_LEDGER_SESSIONS = 64\nconst TASK_CREATED_PATTERN = /Task\\s*#?\\s*(\\d+)\\s+created/i\nconst VALID_STATUSES: ReadonlySet<TodoStatus> = new Set([\"pending\", \"in_progress\", \"completed\"])\n\nfunction getOrCreate(sessionId: string): SessionLedger {\n let ledger = ledgers.get(sessionId)\n if (!ledger) {\n ledger = { todos: new Map(), pendingCreates: new Map() }\n ledgers.set(sessionId, ledger)\n capLedgers()\n }\n return ledger\n}\n\nfunction capLedgers(): void {\n while (ledgers.size > MAX_LEDGER_SESSIONS) {\n const oldest = ledgers.keys().next()\n if (oldest.done) break\n ledgers.delete(oldest.value)\n log.info(\"todo ledger evicted oldest session\", { sessionId: oldest.value })\n }\n}\n\nfunction prunePending(ledger: SessionLedger): void {\n const cutoff = Date.now() - PENDING_CREATE_TTL_MS\n for (const [id, pending] of ledger.pendingCreates) {\n if (pending.createdAt < cutoff) ledger.pendingCreates.delete(id)\n }\n}\n\nfunction materialize(ledger: SessionLedger): TodoEntry[] {\n return Array.from(ledger.todos.values())\n}\n\nfunction resolveSubject(input: { subject?: unknown; description?: unknown } | undefined): string {\n const subject = typeof input?.subject === \"string\" ? input.subject.trim() : \"\"\n if (subject) return subject\n const description = typeof input?.description === \"string\" ? input.description.trim() : \"\"\n if (description) return description\n return \"(no subject)\"\n}\n\nexport function applyTaskCreateToolUse(\n sessionId: string,\n toolUseId: string,\n input: { subject?: unknown; description?: unknown } | undefined,\n): void {\n if (!sessionId || !toolUseId) return\n const ledger = getOrCreate(sessionId)\n prunePending(ledger)\n ledger.pendingCreates.set(toolUseId, {\n subject: resolveSubject(input),\n createdAt: Date.now(),\n })\n}\n\nexport function applyTaskCreateToolResult(\n sessionId: string,\n toolUseId: string,\n resultText: string,\n): TodoEntry[] | null {\n if (!sessionId || !toolUseId) return null\n const ledger = ledgers.get(sessionId)\n if (!ledger) return null\n const pending = ledger.pendingCreates.get(toolUseId)\n if (!pending) return null\n ledger.pendingCreates.delete(toolUseId)\n const match = typeof resultText === \"string\" ? resultText.match(TASK_CREATED_PATTERN) : null\n if (!match) {\n log.debug(\"TaskCreate result did not match expected format\", { sessionId, toolUseId, resultText })\n return null\n }\n const claudeId = match[1]\n if (ledger.todos.has(claudeId)) {\n log.debug(\"TaskCreate result for already-known claude id; overwriting\", { sessionId, claudeId })\n }\n ledger.todos.set(claudeId, { id: claudeId, content: pending.subject, status: \"pending\" })\n return materialize(ledger)\n}\n\nexport function applyTaskUpdate(\n sessionId: string,\n input: { taskId?: unknown; subject?: unknown; status?: unknown } | undefined,\n): TodoEntry[] | null {\n if (!sessionId) return null\n const taskId = typeof input?.taskId === \"string\" ? input.taskId : null\n if (!taskId) return null\n const ledger = ledgers.get(sessionId)\n if (!ledger) return null\n const entry = ledger.todos.get(taskId)\n if (!entry) {\n log.debug(\"TaskUpdate for unknown task id\", { sessionId, taskId })\n return null\n }\n if (input?.status === \"deleted\") {\n ledger.todos.delete(taskId)\n return materialize(ledger)\n }\n if (typeof input?.status === \"string\" && VALID_STATUSES.has(input.status as TodoStatus)) {\n entry.status = input.status as TodoStatus\n }\n if (typeof input?.subject === \"string\" && input.subject.trim().length > 0) {\n entry.content = input.subject.trim()\n }\n return materialize(ledger)\n}\n\nexport function clearLedger(sessionId: string): void {\n if (!sessionId) return\n ledgers.delete(sessionId)\n}\n\nexport function getLedger(sessionId: string): TodoEntry[] {\n const ledger = ledgers.get(sessionId)\n if (!ledger) return []\n return materialize(ledger)\n}\n\nexport function _resetAllLedgersForTests(): void {\n ledgers.clear()\n}\n","import { log } from \"./logger.js\"\nimport { applyTaskCreateToolUse, applyTaskUpdate, type TodoEntry } from \"./todo-ledger.js\"\nimport type { WebSearchRouting } from \"./types.js\"\n\nexport interface MapToolOptions {\n webSearch?: WebSearchRouting\n sessionId?: string\n toolUseId?: string\n}\n\n/** Claude CLI's built-in web search tool (name varies by CLI version). */\nexport function isWebSearchTool(name: string): boolean {\n return name === \"WebSearch\" || name === \"web_search\"\n}\n\n/**\n * True when WebSearch runs inside Claude CLI (default) rather than being\n * forwarded to an opencode tool. In that case the tool-call part must not\n * reach opencode — \"WebSearch\" has no registry entry there and renders as\n * an invalid tool row. Callers show the query as a text line instead.\n */\nexport function isWebSearchHandledByCli(route?: WebSearchRouting): boolean {\n return !route || route === \"claude\" || route === \"disabled\"\n}\n\n/**\n * Map Claude CLI tool input (snake_case) to OpenCode tool input (camelCase)\n */\nfunction mapToolInput(name: string, input: any): any {\n if (!input) return input\n\n switch (name) {\n case \"Write\":\n return {\n filePath: input.file_path ?? input.filePath,\n content: input.content,\n }\n case \"Edit\":\n return {\n filePath: input.file_path ?? input.filePath,\n oldString: input.old_string ?? input.oldString,\n newString: input.new_string ?? input.newString,\n replaceAll: input.replace_all ?? input.replaceAll,\n }\n case \"Read\":\n return {\n filePath: input.file_path ?? input.filePath,\n offset: input.offset,\n limit: input.limit,\n }\n case \"Bash\":\n return {\n command: input.command,\n description:\n input.description ||\n `Execute: ${String(input.command || \"\").slice(0, 50)}${String(input.command || \"\").length > 50 ? \"...\" : \"\"}`,\n timeout: input.timeout,\n }\n case \"NotebookEdit\":\n return {\n notebookPath: input.notebook_path ?? input.notebookPath,\n cellNumber: input.cell_number ?? input.cellNumber,\n newSource: input.new_source ?? input.newSource,\n cellType: input.cell_type ?? input.cellType,\n editMode: input.edit_mode ?? input.editMode,\n }\n case \"Glob\":\n return {\n pattern: input.pattern,\n path: input.path,\n }\n case \"Grep\":\n return {\n pattern: input.pattern,\n path: input.path,\n include: input.include,\n }\n case \"TodoWrite\":\n if (Array.isArray(input.todos)) {\n const mappedTodos = input.todos.map((todo: any, index: number) => ({\n content: todo.content,\n status: todo.status || \"pending\",\n priority: todo.priority || \"medium\",\n id: todo.id || `todo_${Date.now()}_${index}`,\n }))\n return { todos: mappedTodos }\n }\n return input\n default:\n return input\n }\n}\n\n// Tools that Claude CLI executes internally but we report to opencode for UI display\nconst OPENCODE_HANDLED_TOOLS = new Set([\n \"Edit\",\n \"Write\",\n \"Bash\",\n \"NotebookEdit\",\n \"Read\",\n \"Glob\",\n \"Grep\",\n])\n\n// Claude CLI internal tools that should not be forwarded to opencode.\n// These are part of Claude Code's own system and have no opencode equivalent.\n// Tools the Claude CLI emits for its own internal bookkeeping (sub-agents,\n// task tracking, search). opencode has no matching tool registry entry, so\n// forwarding them surfaces as `⚙ invalid` rows in the UI. Skip them.\n// TaskOutput is intentionally NOT here — it has an explicit bash-echo mapping\n// below so the result stays visible.\nconst CLAUDE_INTERNAL_TOOLS = new Set([\n \"ToolSearch\",\n \"Agent\",\n \"AskFollowupQuestion\",\n \"TaskList\",\n \"TaskGet\",\n \"TaskStop\",\n])\n\n/**\n * Wrap model-controlled text as one shell single-quoted word.\n *\n * `TaskOutput` is displayed by running a real `bash` call, so its payload\n * reaches a shell. Double quotes are not enough: inside them `$(…)`,\n * backticks and `${…}` still expand, so `TaskOutput({content: \"X$(id -u)Y\"})`\n * executed `id` while the operator saw a command that read like a print\n * (issue #27). Single quotes suppress every expansion; the only character\n * needing care is `'` itself, closed and reopened around an escaped one.\n */\nexport function singleQuoteForShell(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`\n}\n\nfunction emitTodoWrite(todos: TodoEntry[]) {\n return {\n name: \"todowrite\",\n input: {\n todos: todos.map((todo) => ({\n id: todo.id,\n content: todo.content,\n status: todo.status,\n priority: \"medium\",\n })),\n },\n executed: false,\n }\n}\n\nexport function mapTool(\n name: string,\n input?: any,\n opts?: MapToolOptions,\n): { name: string; input?: any; executed: boolean; skip?: boolean } {\n // Claude CLI internal tools — skip entirely\n if (CLAUDE_INTERNAL_TOOLS.has(name)) {\n log.debug(\"skipping Claude CLI internal tool\", { name })\n return { name, input, executed: true, skip: true }\n }\n\n // TaskCreate: stash subject keyed by tool_use_id; emission happens on tool_result.\n // Without sessionId+toolUseId we cannot maintain the ledger, so fall back to skip\n // (preserves old behavior for callers that haven't been threaded yet).\n if (name === \"TaskCreate\") {\n if (opts?.sessionId && opts?.toolUseId) {\n applyTaskCreateToolUse(opts.sessionId, opts.toolUseId, input)\n }\n return { name, input, executed: true, skip: true }\n }\n\n // TaskUpdate: mutate ledger and emit full list as opencode todowrite. Without\n // sessionId, fall back to skip. Unknown task ids return null from the ledger\n // and we drop the event.\n if (name === \"TaskUpdate\") {\n if (opts?.sessionId) {\n const list = applyTaskUpdate(opts.sessionId, input)\n if (list !== null) return emitTodoWrite(list)\n }\n return { name, input, executed: true, skip: true }\n }\n\n // Plan mode tools\n if (name === \"EnterPlanMode\") return { name: \"plan_enter\", input: {}, executed: false }\n if (name === \"ExitPlanMode\") return { name: \"plan_exit\", input, executed: false }\n\n // TodoWrite needs opencode to run it locally so Todo.Service (and the UI\n // widget backed by it) gets populated. Reporting as provider-executed would\n // short-circuit opencode's own execute and leave the todo panel empty.\n if (name === \"TodoWrite\") {\n const mappedInput = mapToolInput(name, input)\n return { name: \"todowrite\", input: mappedInput, executed: false }\n }\n\n // WebSearch — routing controlled by config.webSearch\n if (isWebSearchTool(name)) {\n const mappedInput = input?.query ? { query: input.query } : input\n const route = opts?.webSearch\n if (route && route !== \"claude\" && route !== \"disabled\") {\n log.debug(\"routing WebSearch to opencode tool\", { target: route, mappedInput })\n return { name: route, input: mappedInput, executed: false }\n }\n // Claude CLI runs WebSearch internally; \"WebSearch\" has no opencode\n // registry entry, so forwarding the tool-call part surfaces a\n // \"Model tried to call unavailable tool\" invalid row in opencode.\n // Skip the part — callers render the query as a text line instead.\n log.debug(\"WebSearch executed by Claude CLI\", { mappedInput })\n return { name: \"WebSearch\", input: mappedInput, executed: true, skip: true }\n }\n\n // TaskOutput -> bash printf\n if (name === \"TaskOutput\") {\n if (!input) return { name: \"bash\", executed: false }\n const output = input?.content || input?.output || JSON.stringify(input)\n return {\n name: \"bash\",\n input: {\n command: `printf '%s\\\\n' ${singleQuoteForShell(`TASK OUTPUT: ${String(output)}`)}`,\n description: \"Displaying task output\",\n },\n executed: false,\n }\n }\n\n // Third-party MCP tools: mcp__<server>__<tool> -> <server>_<tool>.\n // Marked provider-executed because Claude CLI runs these internally via\n // its own --mcp-config; the tool-result is already in the stream. If we\n // reported executed:false, opencode would look up the tool in its own\n // registry, fail to find it, and emit an `invalid` tool error that\n // shadows the real result.\n //\n // Our own proxy tools (`mcp__opencode_proxy__*`) are filtered out by\n // callers before reaching here, so this branch only ever sees user MCP\n // servers configured in Claude CLI's settings.\n if (name.startsWith(\"mcp__\")) {\n const parts = name.slice(5).split(\"__\")\n if (parts.length >= 2) {\n const serverName = parts[0]\n const toolName = parts.slice(1).join(\"_\")\n const openCodeName = `${serverName}_${toolName}`\n log.debug(\"mapping MCP tool\", { original: name, mapped: openCodeName })\n return { name: openCodeName, input, executed: true }\n }\n }\n\n // Tools executed by Claude CLI internally - map to lowercase for opencode\n if (OPENCODE_HANDLED_TOOLS.has(name)) {\n const mappedInput = mapToolInput(name, input)\n const openCodeName = name.toLowerCase()\n log.debug(\"mapping CLI-executed tool\", { name, openCodeName })\n return { name: openCodeName, input: mappedInput, executed: true }\n }\n\n // Unknown tools - treated as provider-executed\n return { name, input, executed: true }\n}\n","/**\n * Tool names and inputs in the shape the opencode host expects.\n *\n * Every tool call this plugin hands opencode, whether a proxied call opencode\n * must run (`bash`, `task`, ...) or a Claude CLI tool it only renders\n * (`read`, `glob`, ...), is built in opencode 1.x's vocabulary. opencode 2.x\n * renamed several tools and fields, and rejects a name it does not know with\n * `No tool named \"bash\" is currently available` (measured on 2.0.11). So on V2,\n * and only on V2, the stream is rewritten once at its edge rather than at each\n * of the dozen places a tool part is built. On V1 nothing here runs.\n *\n * The V2 table was read off `@opencode/core@2.0.11`'s own tool definitions\n * (`dist/tool/plugin/*.js`): each tool's registered `name` and input schema.\n */\n\n/**\n * Which opencode major a model serves. Carried by each model's config\n * (`ClaudeCodeConfig.hostApi`), never held process-wide: opencode 1.18 calls a\n * dual export's `setup` too, so a global flipped there turned V1's tool calls\n * into V2 names and V1 rejected every one (measured on 1.18.32).\n */\nexport type HostToolDialect = \"v1\" | \"v2\"\n\ntype Input = Record<string, unknown>\n\ninterface HostTool {\n name: string\n /** Absent when the input already matches, so its deltas can stream as-is. */\n input?: (input: Input) => Input\n}\n\n/** Drops keys whose value is undefined, so optional V2 fields stay optional. */\nfunction defined(input: Input): Input {\n return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined))\n}\n\n/**\n * V1 name to its V2 counterpart. `null` means V2 has no such tool: the part is\n * dropped, because an unknown name is an error there, not a harmless row.\n */\nconst V2_TOOLS: Record<string, HostTool | null> = {\n bash: {\n name: \"shell\",\n // V2's shell takes no `description`; `workdir`, `timeout` and\n // `background` keep their names.\n input: (input) =>\n defined({\n command: input.command,\n workdir: input.workdir,\n timeout: input.timeout,\n background: input.background,\n }),\n },\n edit: {\n name: \"edit\",\n input: (input) =>\n defined({\n path: input.filePath ?? input.path,\n oldString: input.oldString,\n newString: input.newString,\n replaceAll: input.replaceAll,\n }),\n },\n write: {\n name: \"write\",\n input: (input) => defined({ path: input.filePath ?? input.path, content: input.content }),\n },\n read: {\n name: \"read\",\n input: (input) =>\n defined({ path: input.filePath ?? input.path, offset: input.offset, limit: input.limit }),\n },\n glob: { name: \"glob\" },\n grep: { name: \"grep\" },\n webfetch: { name: \"webfetch\" },\n websearch: { name: \"websearch\" },\n question: { name: \"question\" },\n task: {\n name: \"subagent\",\n input: (input) =>\n defined({\n agent: input.subagent_type ?? input.agent,\n description: input.description,\n prompt: input.prompt,\n sessionID: input.task_id ?? input.sessionID,\n background: input.background,\n }),\n },\n todowrite: null,\n plan_enter: null,\n plan_exit: null,\n notebookedit: null,\n}\n\n/**\n * The host's name and input for a tool call, `null` to drop it, or the call\n * unchanged when the host needs no translation or the tool is not in the table.\n */\nexport function translateToolForHost(\n name: string,\n input: Input,\n dialect: HostToolDialect = \"v1\",\n): { name: string; input: Input } | null {\n if (dialect !== \"v2\") return { name, input }\n const tool = V2_TOOLS[name]\n if (tool === null) return null\n if (tool === undefined) return { name, input }\n return { name: tool.name, input: tool.input ? tool.input(input) : input }\n}\n\ntype StreamPart = { type: string; [key: string]: unknown }\n\nfunction parseInput(raw: unknown): Input {\n if (typeof raw !== \"string\") return (raw as Input) ?? {}\n try {\n const parsed = JSON.parse(raw)\n return parsed && typeof parsed === \"object\" ? (parsed as Input) : {}\n } catch {\n return {}\n }\n}\n\n/**\n * A per-stream rewriter for tool parts. Stateful because opencode ties a\n * tool's start, deltas, call and result together by id, so a tool that is\n * renamed or dropped at its start must be treated the same way to the end.\n * Returns `null` for a part to drop.\n */\nexport function createHostToolPartTranslator(\n dialect: HostToolDialect = \"v1\",\n): (part: StreamPart) => StreamPart | null {\n if (dialect !== \"v2\") return (part) => part\n\n const dropped = new Set<string>()\n const translated = new Map<string, HostTool>()\n\n const classify = (id: string, name: unknown): HostTool | null | undefined => {\n if (dropped.has(id)) return null\n const known = translated.get(id)\n if (known) return known\n if (typeof name !== \"string\") return undefined\n const tool = V2_TOOLS[name]\n if (tool === null) {\n dropped.add(id)\n return null\n }\n if (tool) translated.set(id, tool)\n return tool\n }\n\n return (part) => {\n switch (part.type) {\n case \"tool-input-start\": {\n const tool = classify(String(part.id), part.toolName)\n if (tool === null) return null\n return tool ? { ...part, toolName: tool.name } : part\n }\n case \"tool-input-delta\": {\n const tool = classify(String(part.id), undefined)\n if (tool === null) return null\n // A translated input would stream the V1 keys and then arrive with\n // the V2 ones; opencode takes the final `tool-call` input anyway.\n return tool?.input ? null : part\n }\n case \"tool-input-end\": {\n return classify(String(part.id), undefined) === null ? null : part\n }\n case \"tool-call\": {\n const tool = classify(String(part.toolCallId), part.toolName)\n if (tool === null) return null\n if (!tool) return part\n const input = tool.input ? tool.input(parseInput(part.input)) : parseInput(part.input)\n return {\n ...part,\n toolName: tool.name,\n input: typeof part.input === \"string\" ? JSON.stringify(input) : input,\n }\n }\n case \"tool-result\": {\n const tool = classify(String(part.toolCallId), part.toolName)\n if (tool === null) return null\n return tool ? { ...part, toolName: tool.name } : part\n }\n default:\n return part\n }\n }\n}\n\n/** Applies the translator to a whole stream; the identity on V1. */\nexport function translateStreamForHost<T extends StreamPart>(\n stream: ReadableStream<T>,\n dialect: HostToolDialect = \"v1\",\n): ReadableStream<T> {\n if (dialect !== \"v2\") return stream\n const translate = createHostToolPartTranslator(dialect)\n return stream.pipeThrough(\n new TransformStream<T, T>({\n transform(part, controller) {\n const next = translate(part)\n if (next) controller.enqueue(next as T)\n },\n }),\n )\n}\n","import { homedir } from \"node:os\"\nimport type { LanguageModelV3 } from \"@ai-sdk/provider\"\nimport {\n DEFAULT_ACCOUNT,\n ensureAccountRuntime,\n normalizeAccountName,\n} from \"./accounts.js\"\nimport {\n formatResetsAt,\n isRateLimitRejected,\n resetsAtToMs,\n type RateLimitInfo,\n} from \"./cli-events.js\"\nimport { log } from \"./logger.js\"\nimport {\n QUESTION_TOOL_NAME,\n collectAnswerStrings,\n unwrapToolOutput,\n type QuestionToolCall,\n} from \"./plan-mode-question.js\"\n\n/**\n * Account failover.\n *\n * When the account a conversation is running on is out of usage, the turn\n * fails and the only remedy is the operator's: wait, pay, or move to another\n * account. The last one is the only one the plugin can help with, because the\n * accounts are already configured and each is just another `CLAUDE_CONFIG_DIR`\n * behind a wrapper script (`src/accounts.ts`).\n *\n * So the limit ends the turn with a form instead of an error: one option per\n * other configured account, plus `stop`. The form is opencode's own `question`\n * tool, reached exactly the way the plan-mode bridge reaches it, which means\n * the answer arrives on the next `doStream` call as a `tool-result` and the\n * switch happens inside the same opencode turn, with no new user message.\n * Leaving it unanswered waits, and waiting costs nothing.\n *\n * Three things about the design are deliberate and load-bearing:\n *\n * 1. **The override is scoped to the limited ACCOUNT, not the session.** A\n * rate limit is a property of the account, so one pick governs every\n * session running on it, and a subagent follows its parent for free\n * without needing its own form (child sessions are never asked).\n * 2. **A switch is always a fresh Claude session with the conversation\n * replayed.** Transcripts live under the account's own config dir, so\n * `--resume` can never cross accounts. The caller drops the active process\n * and the stored Claude session id, which makes `includeHistoryContext`\n * true and rebuilds the thread from opencode's prompt.\n * 3. **The `@account` suffix must come off the model id.** `parseModelId`\n * keeps it on purpose because the source account's own wrapper strips it,\n * but a failover spawn goes through a DIFFERENT wrapper (or the bare\n * binary for `default`), which would pass `--model claude-opus-5@appical`\n * straight to a CLI that rejects it.\n */\n\ntype Prompt = Parameters<LanguageModelV3[\"doGenerate\"]>[0][\"prompt\"]\n\n/** Leading text of the `▌` note the plugin writes when a switch happens. */\nexport const FAILOVER_MARKER = \"▌ **account failover:**\"\n\n/**\n * Prefix of every synthetic `question` tool-call id this module mints. The\n * transcript rebuild keys on it to drop the dialog, so a replayed history\n * never hands Claude a form it never saw.\n */\nexport const ACCOUNT_FAILOVER_TOOL_CALL_PREFIX = \"account_failover_\"\n\n// Escaped rather than a literal NUL byte: one raw \\0 anywhere in the file\n// makes git treat this TypeScript source as binary, so it has no diff, no\n// line-level merge and no review. Same string value, text file.\nconst KEY_SEPARATOR = \"\\u0000\"\n\n/** The option that ends the turn instead of switching. */\nexport const STOP_ANSWER = \"stop\"\n\n/**\n * The only error texts that count as \"this account is out of usage\".\n *\n * Deliberately not \"any 4xx\" and not \"any error\": a transient network failure\n * or a bad flag must never open a form that moves where the billing lands.\n * Both strings are the ones observed in production and recorded in AGENTS.md;\n * the apostrophe class covers the straight and curly forms.\n */\nexport const ACCOUNT_LIMIT_PATTERNS: RegExp[] = [\n /third-party apps now draw from your extra usage/i,\n /you[’'`]?ve hit your individual spend limit/i,\n]\n\n/** Leading text of the `▌` note naming an account that cannot serve requests. */\nexport const ACCOUNT_BLOCK_MARKER = \"▌ **claude account:**\"\n\n/**\n * The CLI's failure kinds that belong to the ACCOUNT rather than to this\n * request, each with what the operator can do about it. Read off the\n * `error` enum on the assistant message in Claude Code 2.1.280's own schema;\n * `rate_limit` is left out because the limit path above already covers it,\n * and the request-level kinds (`invalid_request`, `server_error`, ...) are\n * left out because another account would fail the same way.\n *\n * `authentication_failed` is the one measured in production: on 2026-09-23\n * the `appical` login expired, every turn on it failed in about 40 ms, and all\n * the operator saw was the CLI's own reply \"Failed to authenticate: OAuth\n * session expired and could not be refreshed\", with no hint of which account\n * or what to run.\n */\nconst ACCOUNT_BLOCKS = {\n authentication_failed: { what: \"is not logged in (its login expired or was revoked)\", login: true },\n oauth_org_not_allowed: { what: \"belongs to an organization that does not allow this login\", login: false },\n account_on_hold: { what: \"is on hold\", login: false },\n verification_required: { what: \"needs to be verified at claude.ai\", login: false },\n billing_error: { what: \"has a billing problem\", login: false },\n} as const\n\nexport type AccountBlockKind = keyof typeof ACCOUNT_BLOCKS\n\n/** The account-level failure an assistant message reports, if any. */\nexport function accountBlockKind(msg: { type?: string; error?: unknown }): AccountBlockKind | null {\n if (msg.type !== \"assistant\" || typeof msg.error !== \"string\") return null\n return Object.prototype.hasOwnProperty.call(ACCOUNT_BLOCKS, msg.error)\n ? (msg.error as AccountBlockKind)\n : null\n}\n\n/** What the switch form says about the account, in place of \"out of usage\". */\nexport function describeAccountBlock(kind: AccountBlockKind): string {\n return ACCOUNT_BLOCKS[kind].what\n}\n\n/**\n * The command that logs this account in again. An account provider is only a\n * `CLAUDE_CONFIG_DIR` (the wrapper script sets nothing else), so naming the\n * directory is the whole instruction, and it works without the wrapper.\n */\nexport function loginCommandFor(configDir: string | undefined, home = homedir()): string {\n if (!configDir) return \"claude auth login\"\n const shown = configDir === home || configDir.startsWith(`${home}/`) ? `~${configDir.slice(home.length)}` : configDir\n return `CLAUDE_CONFIG_DIR=${shown} claude auth login`\n}\n\nexport function formatAccountBlockNote(input: {\n kind: AccountBlockKind\n account: string\n configDir?: string\n offeringSwitch: boolean\n}): string {\n const block = ACCOUNT_BLOCKS[input.kind]\n const account = normalizeAccountName(input.account || DEFAULT_ACCOUNT)\n const fix = block.login\n ? `Log in again with \\`${loginCommandFor(input.configDir)}\\`, then resend your message.`\n : \"Check the account at claude.ai, then resend your message.\"\n const then = input.offeringSwitch ? \" Or pick another account below.\" : \"\"\n return `\\n${ACCOUNT_BLOCK_MARKER} the Claude account \"${account}\" ${block.what}. ${fix}${then}\\n`\n}\n\nexport function isAccountLimitError(input: {\n rateLimit?: RateLimitInfo | null\n resultText?: string | null\n}): boolean {\n if (input.rateLimit && isRateLimitRejected(input.rateLimit)) return true\n const text = input.resultText\n if (typeof text !== \"string\" || text.length === 0) return false\n return ACCOUNT_LIMIT_PATTERNS.some((pattern) => pattern.test(text))\n}\n\n// ---------------------------------------------------------------------------\n// The override store: which account replaces which, and until when\n// ---------------------------------------------------------------------------\n\ninterface AccountOverride {\n target: string\n /** Epoch ms the limit resets at, or undefined for \"until opencode restarts\". */\n until?: number\n}\n\nconst accountOverrides = new Map<string, AccountOverride>()\n\n/** Test-only. */\nexport function _resetAccountOverrides(): void {\n accountOverrides.clear()\n}\n\nexport function setAccountOverride(\n source: string,\n target: string,\n until?: number,\n now = Date.now(),\n): void {\n const from = normalizeAccountName(source || DEFAULT_ACCOUNT)\n const to = normalizeAccountName(target)\n if (!to || to === from) return\n // A reset time that is not in the future would expire the override on the\n // very next read, so the switch the operator just asked for would be\n // undone before it ran and the turn would re-hit the same limit. Clock\n // skew and a stale `resetsAt` both produce that, so anything not ahead of\n // now degrades to \"until opencode restarts\" rather than to nothing.\n if (until !== undefined && until <= now) {\n log.notice(\"ignoring a failover reset time that is not in the future\", {\n source: from,\n target: to,\n until,\n })\n until = undefined\n }\n accountOverrides.set(from, { target: to, until })\n log.warn(\n `Claude account \"${from}\" is out of usage; this and every other session on it now runs on \"${to}\"${\n until ? ` until ${new Date(until).toISOString()}` : \" until opencode restarts\"\n }.`,\n { source: from, target: to, until: until ?? null },\n )\n}\n\n/**\n * The account to run on instead of `source`, or undefined when there is no\n * override. An expired one is deleted here (and logged once, because the\n * deletion is what silently sends the next turn back to the original account\n * and replays the conversation again).\n */\nexport function resolveAccountOverride(\n source: string,\n now = Date.now(),\n): string | undefined {\n const from = normalizeAccountName(source || DEFAULT_ACCOUNT)\n const entry = accountOverrides.get(from)\n if (!entry) return undefined\n if (entry.until !== undefined && entry.until <= now) {\n accountOverrides.delete(from)\n log.notice(\n `Claude account \"${from}\" should have usage again; switching back from \"${entry.target}\".`,\n { source: from, target: entry.target, until: entry.until },\n )\n return undefined\n }\n return entry.target\n}\n\nexport function clearAccountOverride(source: string): void {\n accountOverrides.delete(normalizeAccountName(source || DEFAULT_ACCOUNT))\n}\n\n/** Read-only view for `/claude-code-doctor` and tests. */\nexport function snapshotAccountOverrides(): Array<{\n source: string\n target: string\n until?: number\n}> {\n return [...accountOverrides.entries()].map(([source, entry]) => ({\n source,\n target: entry.target,\n ...(entry.until === undefined ? {} : { until: entry.until }),\n }))\n}\n\n// ---------------------------------------------------------------------------\n// Resolving the spawn\n// ---------------------------------------------------------------------------\n\n/** `claude-opus-5@appical` -> `claude-opus-5`. See the module note (3). */\nexport function stripAccountSuffix(modelId: string): string {\n const at = modelId.indexOf(\"@\")\n return at === -1 ? modelId : modelId.slice(0, at)\n}\n\nexport interface FailoverSpawn {\n cliPath: string\n modelId: string\n target?: string\n failedOver: boolean\n}\n\n/**\n * The CLI path and model id this turn should actually spawn with. Without an\n * override the inputs come back untouched, which is what keeps every\n * single-account install on exactly today's code path.\n */\nexport async function resolveFailoverSpawn(input: {\n account: string | undefined\n baseCliPath: string\n cliPath: string\n modelId: string\n now?: number\n}): Promise<FailoverSpawn> {\n const unchanged: FailoverSpawn = {\n cliPath: input.cliPath,\n modelId: input.modelId,\n failedOver: false,\n }\n const source = normalizeAccountName(input.account || DEFAULT_ACCOUNT)\n const target = resolveAccountOverride(source, input.now)\n if (!target) return unchanged\n\n try {\n const cliPath =\n target === DEFAULT_ACCOUNT\n ? input.baseCliPath\n : (await ensureAccountRuntime(target, input.baseCliPath)).cliPath\n return {\n cliPath,\n modelId: stripAccountSuffix(input.modelId),\n target,\n failedOver: true,\n }\n } catch (err) {\n // A wrapper we cannot write is not a reason to spawn nothing: fall back\n // to the limited account and let its own error speak, rather than\n // spawning a path that does not exist.\n log.error(\"failed to prepare the failover account runtime; staying put\", {\n source,\n target,\n error: String(err),\n })\n return unchanged\n }\n}\n\n// ---------------------------------------------------------------------------\n// The form\n// ---------------------------------------------------------------------------\n\n/**\n * On by default whenever more than one account is configured: the operator's\n * pick is the consent, and with no other account there is nothing to offer.\n * Never on a compaction turn (its answer would have nowhere to go), never on\n * the interactive transport (a TUI stdin and no proxy server), never in a\n * child session (a subagent follows its parent's account for free), and never\n * without opencode's `question` entry, where the emitted call renders as\n * `⚙ invalid` and wedges the turn.\n */\nexport function isAccountFailoverQuestionActive(input: {\n configured: \"ask\" | \"off\" | undefined\n candidates: readonly string[]\n opencodeHasQuestion: boolean\n compactionMode: boolean\n interactive: boolean\n childSession: boolean\n}): boolean {\n if (input.compactionMode) return false\n if (input.interactive) return false\n if (input.childSession) return false\n if (input.configured === \"off\") return false\n if (input.candidates.length === 0) return false\n return input.opencodeHasQuestion\n}\n\n/** Every configured account except the one that just hit its limit. */\nexport function failoverCandidates(\n accounts: readonly string[] | undefined,\n source: string,\n): string[] {\n const from = normalizeAccountName(source || DEFAULT_ACCOUNT)\n const out: string[] = []\n for (const raw of accounts ?? []) {\n const name = normalizeAccountName(String(raw))\n if (!name || name === from || out.includes(name)) continue\n out.push(name)\n }\n return out\n}\n\ninterface PendingFailoverQuestion {\n sourceAccount: string\n candidates: string[]\n resetsAt?: number\n /** The question as asked, needed to read opencode's answer sentence. */\n question?: string\n}\n\nconst pendingQuestions = new Map<string, PendingFailoverQuestion>()\n\nfunction pendingKey(sessionKey: string, toolCallId: string): string {\n return `${sessionKey}${KEY_SEPARATOR}${toolCallId}`\n}\n\n/**\n * Called from `deleteClaudeSessionId`, the one destructive session boundary.\n * A pending id that outlives its session would route the next answer at a\n * dialog nobody can act on.\n */\nexport function clearAccountFailoverQuestions(sessionKey: string): void {\n const prefix = `${sessionKey}${KEY_SEPARATOR}`\n for (const key of pendingQuestions.keys()) {\n if (key.startsWith(prefix)) pendingQuestions.delete(key)\n }\n}\n\nfunction describeReset(resetsAt: number | undefined): string | undefined {\n return formatResetsAt(resetsAt)\n}\n\nexport function createAccountFailoverQuestionCall(\n sessionKey: string,\n input: {\n sourceAccount: string\n candidates: readonly string[]\n resetsAt?: number\n window?: string\n /**\n * Why the account cannot serve, when it is not a usage limit\n * (`describeAccountBlock`). Replaces \"is out of usage\" in the question.\n */\n reason?: string\n },\n toolCallId = `${ACCOUNT_FAILOVER_TOOL_CALL_PREFIX}${Math.random()\n .toString(36)\n .slice(2, 10)}`,\n): QuestionToolCall {\n const source = normalizeAccountName(input.sourceAccount || DEFAULT_ACCOUNT)\n const candidates = input.candidates.map((c) => normalizeAccountName(c))\n const resets = describeReset(input.resetsAt)\n const until = resets ?? \"opencode restarts\"\n\n const question = input.reason\n ? `The Claude account \"${source}\" ${input.reason}. Continue this task on another configured account? Leaving this unanswered waits, at no cost.`\n : [\n `The Claude account \"${source}\" is out of usage`,\n input.window ? ` in ${input.window}` : \"\",\n resets ? `, which resets at ${resets}` : \"\",\n \". Continue this task on another configured account? Leaving this unanswered waits, at no cost.\",\n ].join(\"\")\n\n pendingQuestions.set(pendingKey(sessionKey, toolCallId), {\n sourceAccount: source,\n candidates: [...candidates],\n resetsAt: input.resetsAt,\n question,\n })\n\n return {\n toolCallId,\n toolName: QUESTION_TOOL_NAME,\n input: {\n questions: [\n {\n header: \"Account limit\",\n question,\n options: [\n ...candidates.map((candidate) => ({\n label: candidate,\n description: `Run on \"${candidate}\" until ${until}. The conversation is replayed as a fresh Claude session (a session cannot resume across accounts), and any MCP server configured only in \"${source}\"'s Claude profile will be missing.`,\n })),\n {\n label: STOP_ANSWER,\n description: \"End this turn now and leave the account as it is.\",\n },\n ],\n multiple: false,\n custom: true,\n },\n ],\n },\n text: \"\",\n }\n}\n\nexport type AccountFailoverAnswer =\n | { kind: \"switch\"; target: string; sourceAccount: string; resetsAt?: number }\n | { kind: \"stop\"; reason: string }\n\nfunction isDenied(output: unknown): output is { reason?: unknown } {\n return (\n !!output &&\n typeof output === \"object\" &&\n (output as { denied?: unknown }).denied === true\n )\n}\n\nfunction classify(\n pending: PendingFailoverQuestion,\n part: any,\n): AccountFailoverAnswer {\n const output = unwrapToolOutput(part)\n if (isDenied(output)) {\n return {\n kind: \"stop\",\n reason: String((output as { reason?: unknown }).reason ?? \"question rejected\"),\n }\n }\n // A dismissed form reaches the model as a failed tool call (\"The user\n // dismissed this question\"), not as an answer that happens to be unknown.\n const outputType = part?.output?.type\n if (outputType === \"error-text\" || outputType === \"error-json\") {\n return { kind: \"stop\", reason: String(output ?? \"the form was dismissed\") }\n }\n\n const answers = collectAnswerStrings(output, pending.question)\n .map((answer) => answer.trim())\n .filter(Boolean)\n if (answers.length === 0) return { kind: \"stop\", reason: \"no answer\" }\n\n const picked = normalizeAccountName(answers[0])\n if (picked === STOP_ANSWER) {\n return { kind: \"stop\", reason: \"the operator chose to stop\" }\n }\n const target = pending.candidates.find((candidate) => candidate === picked)\n if (!target) {\n return { kind: \"stop\", reason: `unrecognised answer \"${answers[0]}\"` }\n }\n return {\n kind: \"switch\",\n target,\n sourceAccount: pending.sourceAccount,\n resetsAt: pending.resetsAt,\n }\n}\n\n/**\n * Take the operator's answer to a failover form out of this turn's prompt.\n * Anything that is not one of the offered accounts, including a dismissal and\n * unrecognised custom text, is a `stop`: the turn then ends the way the\n * rate-limit error ends it today.\n */\nexport function consumeAccountFailoverAnswer(\n sessionKey: string,\n prompt: Array<{ role: string; content?: unknown }>,\n /**\n * The form this model would offer now, for an answer whose form was asked\n * by an earlier opencode process. The pending entry lives in memory, so a\n * restart between the form and the answer lost it and the pick was replayed\n * to Claude as stray text (measured 2026-09-23). Only the newest message is\n * read with it, so an old answer further up the history never fires again.\n */\n fallback?: { sourceAccount: string; candidates: readonly string[] },\n): AccountFailoverAnswer | null {\n for (let i = prompt.length - 1; i >= 0; i--) {\n const msg = prompt[i]\n if (!Array.isArray(msg.content)) continue\n\n for (const part of msg.content as any[]) {\n if (part?.type !== \"tool-result\" || typeof part.toolCallId !== \"string\") {\n continue\n }\n const key = pendingKey(sessionKey, part.toolCallId)\n const pending = pendingQuestions.get(key)\n if (!pending) continue\n\n pendingQuestions.delete(key)\n return classify(pending, part)\n }\n }\n\n const last = prompt[prompt.length - 1]\n if (fallback && fallback.candidates.length > 0 && Array.isArray(last?.content)) {\n const orphan = (last.content as any[]).find(\n (part) =>\n part?.type === \"tool-result\" &&\n typeof part.toolCallId === \"string\" &&\n part.toolCallId.startsWith(ACCOUNT_FAILOVER_TOOL_CALL_PREFIX),\n )\n if (orphan) {\n return classify(\n {\n sourceAccount: normalizeAccountName(fallback.sourceAccount || DEFAULT_ACCOUNT),\n candidates: fallback.candidates.map((c) => normalizeAccountName(c)),\n },\n orphan,\n )\n }\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Transcript handling\n// ---------------------------------------------------------------------------\n\nexport function formatFailoverNote(input: {\n sourceAccount: string\n target: string\n resetsAt?: number\n}): string {\n const resets = describeReset(input.resetsAt)\n return `\\n${FAILOVER_MARKER} \"${input.sourceAccount}\" is out of usage, so this conversation continues on \"${\n input.target\n }\" ${\n resets ? `until ${resets}` : \"until opencode restarts\"\n }. Claude cannot resume a session across accounts, so the thread is being replayed into a fresh one.\\n`\n}\n\nexport function formatFailoverStopNote(reason: string): string {\n return `\\n${FAILOVER_MARKER} Staying on this account (${reason}). The turn ends here; the usage limit is unchanged.\\n`\n}\n\nfunction isFailoverPart(part: any): boolean {\n if (!part || typeof part.toolCallId !== \"string\") return false\n if (part.type !== \"tool-call\" && part.type !== \"tool-result\") return false\n return part.toolCallId.startsWith(ACCOUNT_FAILOVER_TOOL_CALL_PREFIX)\n}\n\n/**\n * Remove the failover dialog from a transcript: the synthetic `question`\n * tool-call and the `tool-result` carrying the answer. Claude never issued\n * that call and never saw that result, so replaying either would hand a\n * fresh session a conversation it cannot make sense of. Messages left with no\n * content at all are dropped rather than replayed empty.\n */\nexport function stripAccountFailoverParts(prompt: Prompt): Prompt {\n let changed = false\n const out = [] as unknown as Prompt\n\n for (const message of prompt) {\n const content = (message as { content?: unknown }).content\n if (!Array.isArray(content) || !content.some(isFailoverPart)) {\n out.push(message)\n continue\n }\n changed = true\n const kept = content.filter((part: any) => !isFailoverPart(part))\n if (kept.length === 0) continue\n out.push({ ...message, content: kept } as typeof message)\n }\n\n return changed ? out : prompt\n}\n\nexport function failoverContinuationText(target: string): string {\n return [\n `The Claude account this conversation was running on hit its usage limit, so it has been moved to the \"${target}\" account and you are now in a fresh Claude session.`,\n \"The conversation so far is above. Continue the task from where it stopped: do not start over, do not re-plan, and do not repeat work that is already done.\",\n \"Do not mention the account switch unless you are asked about it.\",\n ].join(\" \")\n}\n\n/**\n * The prompt to replay into the target account: the conversation with the\n * failover dialog removed, plus one user message telling the fresh session\n * what happened and to carry on.\n */\nexport function buildFailoverContinuationPrompt(\n prompt: Prompt,\n target: string,\n): Prompt {\n const stripped = stripAccountFailoverParts(prompt)\n return [\n ...stripped,\n {\n role: \"user\",\n content: [{ type: \"text\", text: failoverContinuationText(target) }],\n },\n ] as Prompt\n}\n\n/** Epoch ms a limit resets at, from whichever field the CLI filled in. */\nexport function failoverUntil(\n resetsAt: number | undefined,\n): number | undefined {\n return resetsAtToMs(resetsAt)\n}\n","import { chmod, lstat, mkdir, readlink, symlink, writeFile } from \"node:fs/promises\"\nimport path from \"node:path\"\nimport { log } from \"./logger.js\"\n\nexport const BASE_PROVIDER_ID = \"claude-code\"\nexport const DEFAULT_ACCOUNT = \"default\"\n\nconst SHARED_CAPABILITY_ITEMS = [\n \"CLAUDE.md\",\n \"settings.json\",\n \"skills\",\n \"agents\",\n \"commands\",\n \"plugins\",\n]\n\nexport function normalizeAccountName(account: string): string {\n return account\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n}\n\nexport function resolveAccounts(value: unknown): string[] | null {\n if (!Array.isArray(value)) return null\n\n const accounts = value\n .map((account) => normalizeAccountName(String(account)))\n .filter(Boolean)\n\n return Array.from(new Set([DEFAULT_ACCOUNT, ...accounts]))\n}\n\nexport function accountProviderId(account: string): string {\n return `${BASE_PROVIDER_ID}-${normalizeAccountName(account)}`\n}\n\nexport function accountDisplayName(account: string): string {\n return `Claude Code (${titleizeAccount(account)})`\n}\n\nexport function accountModelSuffix(account: string): string | undefined {\n const normalized = normalizeAccountName(account)\n return normalized === DEFAULT_ACCOUNT ? undefined : normalized\n}\n\nexport function accountConfigDir(account: string): string | undefined {\n const normalized = normalizeAccountName(account)\n\n if (!normalized || normalized === DEFAULT_ACCOUNT) return undefined\n\n return `~/.claude-${normalized}`\n}\n\n/**\n * Absolute `CLAUDE_CONFIG_DIR` for an account, or undefined for the default\n * account, which leaves the CLI on its own `~/.claude`. Used where a caller\n * needs to know what the *spawn* will read (skills, plugins) rather than to\n * set the variable itself.\n */\nexport function accountConfigDirPath(account: string): string | undefined {\n const dir = accountConfigDir(account)\n return dir ? expandHome(dir) : undefined\n}\n\nexport function expandHome(value: string): string {\n const home = process.env.HOME ?? process.env.USERPROFILE\n\n if (value === \"~\") return home ?? value\n\n if (value.startsWith(\"~/\") || value.startsWith(\"~\\\\\")) {\n return home ? path.join(home, value.slice(2)) : value\n }\n\n return value\n}\n\nexport async function ensureAccountRuntime(\n account: string,\n baseCliPath: string,\n): Promise<{ cliPath: string; configDir?: string }> {\n const configDir = accountConfigDir(account)\n\n if (!configDir) return { cliPath: baseCliPath }\n\n const expandedConfigDir = expandHome(configDir)\n await mkdir(expandedConfigDir, { recursive: true })\n\n try {\n await ensureSharedCapabilities(expandedConfigDir)\n } catch (err) {\n log.warn(\"failed to symlink shared capabilities; continuing anyway\", {\n account,\n configDir: expandedConfigDir,\n error: String(err),\n })\n }\n\n const cliPath = await writeAccountWrapper(\n normalizeAccountName(account),\n baseCliPath,\n expandedConfigDir,\n )\n\n return { cliPath, configDir: expandedConfigDir }\n}\n\nasync function ensureSharedCapabilities(targetRoot: string): Promise<void> {\n const sourceRoot = expandHome(\"~/.claude\")\n\n for (const item of SHARED_CAPABILITY_ITEMS) {\n await ensureSharedCapabilityItem(sourceRoot, targetRoot, item)\n }\n}\n\nasync function ensureSharedCapabilityItem(\n sourceRoot: string,\n targetRoot: string,\n item: string,\n): Promise<void> {\n const source = path.join(sourceRoot, item)\n const target = path.join(targetRoot, item)\n\n let sourceStat\n try {\n sourceStat = await lstat(source)\n } catch {\n return\n }\n\n try {\n const targetStat = await lstat(target)\n\n if (targetStat.isSymbolicLink()) {\n const current = await readlink(target)\n const resolvedCurrent = path.resolve(path.dirname(target), current)\n const resolvedSource = path.resolve(source)\n\n if (resolvedCurrent === resolvedSource) return\n }\n\n log.warn(\"shared Claude capability already exists; leaving untouched\", {\n item,\n target,\n source,\n })\n\n return\n } catch {\n // Missing target is expected.\n }\n\n const type = sourceStat.isDirectory()\n ? process.platform === \"win32\"\n ? \"junction\"\n : \"dir\"\n : \"file\"\n\n await symlink(source, target, type)\n}\n\nasync function writeAccountWrapper(\n account: string,\n baseCliPath: string,\n configDir: string,\n): Promise<string> {\n const cacheRoot = path.join(\n process.env.XDG_CACHE_HOME ?? expandHome(\"~/.cache\"),\n \"opencode-claude-code-plugin\",\n )\n const wrapperPath = path.join(cacheRoot, `claude-${account}`)\n const suffix = `@${account}`\n\n await mkdir(cacheRoot, { recursive: true })\n\n const script = `#!/usr/bin/env bash\nset -euo pipefail\n\nargs=()\nwhile [[ $# -gt 0 ]]; do\n if [[ \"$1\" == \"--model\" && $# -ge 2 ]]; then\n model=\"$2\"\n if [[ \"$model\" == *${shellDoubleQuote(suffix)} ]]; then\n model=\"\\${model%${shellDoubleQuote(suffix)}}\"\n fi\n args+=(\"$1\" \"$model\")\n shift 2\n else\n args+=(\"$1\")\n shift\n fi\ndone\n\nexport CLAUDE_CONFIG_DIR=${shellSingleQuote(configDir)}\nexec ${shellSingleQuote(baseCliPath)} \"\\${args[@]}\"\n`\n\n await writeFile(wrapperPath, script, \"utf8\")\n await chmod(wrapperPath, 0o755)\n\n return wrapperPath\n}\n\nfunction shellSingleQuote(value: string): string {\n return `'${value.replace(/'/g, `'\"'\"'`)}'`\n}\n\nfunction shellDoubleQuote(value: string): string {\n return value.replace(/[$`\"\\\\]/g, \"\\\\$&\")\n}\n\nfunction titleizeAccount(account: string): string {\n return normalizeAccountName(account)\n .split(\"-\")\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\" \")\n}\n","import { log } from \"./logger.js\"\nimport type { ClaudeStreamMessage } from \"./types.js\"\n\n/**\n * Claude CLI stream events the plugin used to drop on the floor.\n *\n * Every shape below was read out of the CLI's own zod schemas in the installed\n * bundle (`rg -a` over `~/.local/share/claude/versions/<v>`) on 2.1.263, not\n * guessed, but they are still parsed defensively: a future CLI may rename a\n * field, and a diagnostic that throws is worse than one that stays quiet.\n *\n * Levels follow the rule the rest of this codebase uses. `src/logger.ts` routes\n * only WARN and ERROR to stderr unconditionally, so anything the user has to\n * see without turning on debug logging is either a WARN or a `▌` line written\n * into the transcript. Everything else is INFO or NOTICE for the file log.\n */\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n}\n\nfunction str(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined\n}\n\nfunction num(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined\n}\n\n// ---------------------------------------------------------------------------\n// rate_limit_event\n// ---------------------------------------------------------------------------\n\n/**\n * `{type:\"rate_limit_event\", rate_limit_info:{...}}`, emitted whenever the\n * CLI's view of the account's limits changes. `status` is the plan window,\n * `overageStatus` is paid extra usage on top of it; either can be `rejected`\n * on its own, so both are read.\n */\nexport interface RateLimitInfo {\n status?: string\n rateLimitType?: string\n resetsAt?: number\n utilization?: number\n isUsingOverage?: boolean\n overageStatus?: string\n overageResetsAt?: number\n overageDisabledReason?: string\n}\n\nexport const RATE_LIMIT_MARKER = \"▌ **rate limit:**\"\n\n/**\n * What the operator can actually do about it. Same four levers the billing\n * note in AGENTS.md lists, which is where this wording comes from: none of\n * them is something the plugin may do on its own.\n */\nexport const RATE_LIMIT_ACTION =\n \"Enable extra usage or add credits on the account, wait for the window to reset, switch account, org or plan, or authenticate with an API key.\"\n\nconst OVERAGE_DISABLED_REASONS: Record<string, string> = {\n overage_not_provisioned: \"extra usage is not set up on this account\",\n org_level_disabled: \"extra usage is disabled for your organization\",\n org_level_disabled_until: \"extra usage is disabled for your organization for now\",\n out_of_credits: \"the account's usage credits are spent\",\n seat_tier_level_disabled: \"your seat tier does not allow extra usage\",\n member_level_disabled: \"extra usage is disabled for your member account\",\n seat_tier_zero_credit_limit: \"your seat tier has a zero credit limit\",\n group_zero_credit_limit: \"your group has a zero credit limit\",\n member_zero_credit_limit: \"your member account has a zero credit limit\",\n org_service_level_disabled: \"your organization's service level does not include extra usage\",\n no_limits_configured: \"no usage limits are configured for this account\",\n fetch_error: \"the CLI could not read the account's usage limits\",\n}\n\nconst RATE_LIMIT_WINDOWS: Record<string, string> = {\n five_hour: \"the 5-hour window\",\n seven_day: \"the 7-day window\",\n seven_day_opus: \"the 7-day Opus window\",\n seven_day_sonnet: \"the 7-day Sonnet window\",\n seven_day_overage_included: \"the 7-day extra-usage window\",\n overage: \"extra usage\",\n}\n\nexport function parseRateLimitEvent(msg: ClaudeStreamMessage): RateLimitInfo | null {\n if (msg.type !== \"rate_limit_event\") return null\n const info = (msg as { rate_limit_info?: unknown }).rate_limit_info\n if (!isRecord(info)) return null\n return {\n status: str(info.status),\n rateLimitType: str(info.rateLimitType),\n resetsAt: num(info.resetsAt),\n utilization: num(info.utilization),\n isUsingOverage: info.isUsingOverage === true,\n overageStatus: str(info.overageStatus),\n overageResetsAt: num(info.overageResetsAt),\n overageDisabledReason: str(info.overageDisabledReason),\n }\n}\n\n/** The CLI sends unix seconds; tolerate milliseconds rather than mean 1970. */\nexport function resetsAtToMs(resetsAt: number | undefined): number | undefined {\n if (resetsAt === undefined || !Number.isFinite(resetsAt)) return undefined\n return resetsAt < 1e12 ? resetsAt * 1000 : resetsAt\n}\n\nexport function formatResetsAt(resetsAt: number | undefined): string | undefined {\n const ms = resetsAtToMs(resetsAt)\n if (ms === undefined) return undefined\n const date = new Date(ms)\n return Number.isNaN(date.getTime()) ? undefined : date.toISOString()\n}\n\n/** Dedup identity: one warning per (window, overage status, reason) per process. */\nexport function rateLimitKey(info: RateLimitInfo): string {\n return [\n info.status ?? \"?\",\n info.rateLimitType ?? \"?\",\n info.overageStatus ?? \"?\",\n info.overageDisabledReason ?? \"?\",\n ].join(\"|\")\n}\n\n/**\n * Whether the CLI refused this request. `status` is the request's own verdict\n * and wins whenever it is present: `overageStatus: \"rejected\"` on its own only\n * says paid extra usage is unavailable, which is the normal steady state for\n * an org with extra usage disabled. Measured on CLI 2.1.280 (2026-09-23): an\n * event of `{status: \"allowed\", overageStatus: \"rejected\",\n * overageDisabledReason: \"org_level_disabled\"}` arrived on a turn that was\n * served and answered. Treating that as a rejection printed a false \"rejected\n * this request\" line and, worse, set account failover off on a successful turn.\n * An overage rejection still counts when no status came with it.\n */\nexport function isRateLimitRejected(info: RateLimitInfo): boolean {\n if (info.status === \"rejected\") return true\n if (info.status === \"allowed\" || info.status === \"allowed_warning\") return false\n return info.overageStatus === \"rejected\"\n}\n\nexport interface RateLimitReport {\n key: string\n level: \"warn\" | \"notice\" | \"info\"\n message: string\n /** Text to put in the transcript, or null when this is log-only. */\n transcript: string | null\n}\n\n/**\n * A rejection is the only state the user has to act on, so it is the only one\n * that gets a WARN and a transcript line. A warning state is a NOTICE: it is\n * real but not yet blocking, and a per-turn TUI bubble for \"you are at 82%\"\n * would train people to ignore the blocking one.\n */\nexport function describeRateLimit(info: RateLimitInfo): RateLimitReport | null {\n if (!info.status && !info.overageStatus) return null\n const window = info.rateLimitType ? RATE_LIMIT_WINDOWS[info.rateLimitType] ?? info.rateLimitType : undefined\n const resets = formatResetsAt(info.resetsAt)\n const overageResets = formatResetsAt(info.overageResetsAt)\n const key = rateLimitKey(info)\n\n if (isRateLimitRejected(info)) {\n const parts: string[] = []\n parts.push(\n info.status === \"rejected\"\n ? `Claude Code rejected this request: you are out of usage${window ? ` in ${window}` : \"\"}.`\n : \"Claude Code rejected this request: paid extra usage is not available on this account.\",\n )\n const reason = info.overageDisabledReason\n ? OVERAGE_DISABLED_REASONS[info.overageDisabledReason] ?? info.overageDisabledReason\n : undefined\n if (reason) parts.push(`Extra usage is unavailable because ${reason}.`)\n const resetAt = resets ?? overageResets\n if (resetAt) parts.push(`Resets at ${resetAt}.`)\n parts.push(RATE_LIMIT_ACTION)\n const message = parts.join(\" \")\n return { key, level: \"warn\", message, transcript: `\\n${RATE_LIMIT_MARKER} ${message}\\n` }\n }\n\n if (info.status === \"allowed_warning\" || info.overageStatus === \"allowed_warning\") {\n const used =\n info.utilization === undefined ? \"\" : ` (${Math.round(info.utilization * 100)}% used)`\n return {\n key,\n level: \"notice\",\n message: `Approaching the usage limit${window ? ` for ${window}` : \"\"}${used}${\n resets ? `, resets at ${resets}` : \"\"\n }.`,\n transcript: null,\n }\n }\n\n return {\n key,\n level: \"info\",\n message: `usage limits updated${window ? ` for ${window}` : \"\"}`,\n transcript: null,\n }\n}\n\nconst reportedRateLimits = new Set<string>()\n\n/** Test-only. */\nexport function _resetRateLimitReports(): void {\n reportedRateLimits.clear()\n}\n\n/**\n * Logs the event and returns the transcript line to enqueue, if any. Deduped\n * per identity per process: the CLI re-emits the same rejection on every\n * subsequent request, and a repeated WARN would bury the first one.\n */\nexport function reportRateLimitEvent(msg: ClaudeStreamMessage): string | null {\n const info = parseRateLimitEvent(msg)\n if (!info) return null\n const report = describeRateLimit(info)\n if (!report) return null\n const data: Record<string, unknown> = { ...info }\n if (reportedRateLimits.has(report.key)) {\n log.debug(report.message, data)\n return null\n }\n reportedRateLimits.add(report.key)\n log[report.level](report.message, data)\n return report.transcript\n}\n\n// ---------------------------------------------------------------------------\n// system / init\n// ---------------------------------------------------------------------------\n\nexport interface SystemInitInfo {\n apiKeySource?: string\n permissionMode?: string\n model?: string\n cliVersion?: string\n toolCount: number\n mcpServers: Array<{ name: string; status: string }>\n}\n\n/**\n * Credential sources that mean the CLI authenticated with an API key rather\n * than the logged-in subscription, so the turn bills pay-as-you-go against the\n * Console account and never touches the plan's Agent SDK credit. `oauth` is\n * the subscription and `none` is no credential at all; everything else in the\n * CLI's enum is a key from some scope.\n */\nexport const API_KEY_SOURCES = new Set([\n \"ANTHROPIC_API_KEY\",\n \"apiKeyHelper\",\n \"/login managed key\",\n \"user\",\n \"project\",\n \"org\",\n \"temporary\",\n])\n\nexport function parseSystemInit(msg: ClaudeStreamMessage): SystemInitInfo | null {\n if (msg.type !== \"system\" || msg.subtype !== \"init\") return null\n const raw = msg as unknown as Record<string, unknown>\n const servers: Array<{ name: string; status: string }> = []\n if (Array.isArray(raw.mcp_servers)) {\n for (const entry of raw.mcp_servers) {\n if (!isRecord(entry)) continue\n servers.push({ name: str(entry.name) ?? \"unknown\", status: str(entry.status) ?? \"unknown\" })\n }\n }\n return {\n apiKeySource: str(raw.apiKeySource),\n permissionMode: str(raw.permissionMode),\n model: str(raw.model),\n cliVersion: str(raw.claude_code_version),\n toolCount: Array.isArray(raw.tools) ? raw.tools.length : 0,\n mcpServers: servers,\n }\n}\n\n/**\n * The warning text for an API-key session, or null when there is nothing to\n * say. Kept pure and separate from the dedup so both halves are testable.\n *\n * `ignoreAnthropicApiKey` strips the env vars from the spawn, so a key still\n * in effect after that came from the CLI's own settings and the option is not\n * the fix; saying so is the whole point of reading this field rather than\n * `process.env`, which only sees one of the two ways a key gets in.\n */\nexport function apiKeySourceWarning(\n apiKeySource: string | undefined,\n ignoreAnthropicApiKey: boolean | undefined,\n): string | null {\n if (!apiKeySource || !API_KEY_SOURCES.has(apiKeySource)) return null\n const base = `Claude Code authenticated with an API key (apiKeySource: ${apiKeySource}), so these turns bill as pay-as-you-go API usage instead of your subscription's Agent SDK credit.`\n return ignoreAnthropicApiKey\n ? `${base} \\`ignoreAnthropicApiKey\\` is already on, so the key is not coming from the environment: check the CLI's own settings (\\`claude config\\`) or an \\`apiKeyHelper\\`.`\n : `${base} Set the provider option \\`ignoreAnthropicApiKey: true\\` to strip the key from spawns and fall back to the stored subscription auth.`\n}\n\nconst warnedApiKeySources = new Set<string>()\nconst warnedMcpFailures = new Set<string>()\n\n/** Test-only. */\nexport function _resetSystemInitReports(): void {\n warnedApiKeySources.clear()\n warnedMcpFailures.clear()\n}\n\n/**\n * Log the CLI's own view of the session it just started, and warn about the\n * two things in it a user has to act on: a credential that changes who gets\n * billed, and an MCP server that did not come up (the model simply will not\n * have those tools, with no other sign of it).\n */\nexport function reportSystemInit(\n msg: ClaudeStreamMessage,\n options: { ignoreAnthropicApiKey?: boolean } = {},\n): void {\n const info = parseSystemInit(msg)\n if (!info) return\n log.info(\"claude session init\", {\n apiKeySource: info.apiKeySource ?? null,\n permissionMode: info.permissionMode ?? null,\n model: info.model ?? null,\n cliVersion: info.cliVersion ?? null,\n tools: info.toolCount,\n mcpServers: info.mcpServers,\n })\n\n for (const server of info.mcpServers) {\n if (server.status === \"connected\") continue\n const key = `${server.name}:${server.status}`\n const message = `MCP server \"${server.name}\" is ${server.status} in Claude Code; its tools are not available to the model this session.`\n if (warnedMcpFailures.has(key)) {\n log.debug(message, { server: server.name, status: server.status })\n continue\n }\n warnedMcpFailures.add(key)\n log.warn(message, { server: server.name, status: server.status })\n }\n\n const apiKeyMessage = apiKeySourceWarning(info.apiKeySource, options.ignoreAnthropicApiKey)\n if (!apiKeyMessage) return\n const key = `${info.apiKeySource}:${options.ignoreAnthropicApiKey ? \"ignored\" : \"passed\"}`\n if (warnedApiKeySources.has(key)) {\n log.debug(apiKeyMessage, { apiKeySource: info.apiKeySource })\n return\n }\n warnedApiKeySources.add(key)\n log.warn(apiKeyMessage, { apiKeySource: info.apiKeySource })\n}\n\n// ---------------------------------------------------------------------------\n// system / compact_boundary\n// ---------------------------------------------------------------------------\n\nexport const COMPACT_BOUNDARY_MARKER = \"▌ **context compacted:**\"\n\nexport interface CompactBoundary {\n trigger: string\n preTokens?: number\n postTokens?: number\n}\n\n/**\n * The CLI compacted its own context mid-conversation. Nothing in opencode\n * shows this today, so a conversation can silently lose everything before the\n * boundary and the next answer just looks forgetful.\n *\n * Field name confirmed on CLI 2.1.263: the stream schema emits\n * `compact_metadata`, while the CLI's own transcript reader uses\n * `compactMetadata`. Both are read, because it costs one line and the two\n * spellings genuinely coexist inside the binary.\n */\nexport function parseCompactBoundary(msg: ClaudeStreamMessage): CompactBoundary | null {\n if (msg.type !== \"system\" || msg.subtype !== \"compact_boundary\") return null\n const raw = msg as unknown as Record<string, unknown>\n const meta = isRecord(raw.compact_metadata)\n ? raw.compact_metadata\n : isRecord(raw.compactMetadata)\n ? raw.compactMetadata\n : undefined\n return {\n trigger: str(meta?.trigger) ?? \"unknown\",\n preTokens: num(meta?.pre_tokens) ?? num(meta?.preTokens),\n postTokens: num(meta?.post_tokens) ?? num(meta?.postTokens),\n }\n}\n\nexport function formatCompactBoundaryNote(boundary: CompactBoundary): string {\n const how = boundary.trigger === \"auto\" ? \"on its own\" : `on a ${boundary.trigger} request`\n const sizes =\n boundary.preTokens !== undefined && boundary.postTokens !== undefined\n ? ` (${boundary.preTokens.toLocaleString(\"en-US\")} tokens to ${boundary.postTokens.toLocaleString(\"en-US\")})`\n : \"\"\n return `\\n${COMPACT_BOUNDARY_MARKER} Claude Code compacted its own context ${how}${sizes}. Earlier detail in this conversation is now a summary.\\n`\n}\n\n/** Logs the boundary and returns the transcript note, or null when not one. */\nexport function reportCompactBoundary(msg: ClaudeStreamMessage): string | null {\n const boundary = parseCompactBoundary(msg)\n if (!boundary) return null\n log.notice(\"claude code compacted its own context\", {\n trigger: boundary.trigger,\n preTokens: boundary.preTokens ?? null,\n postTokens: boundary.postTokens ?? null,\n })\n return formatCompactBoundaryNote(boundary)\n}\n\n// ---------------------------------------------------------------------------\n// conversation_reset\n// ---------------------------------------------------------------------------\n\nexport const CONVERSATION_RESET_MARKER = \"▌ **claude code reset:**\"\n\nexport interface ConversationReset {\n newConversationId: string\n /** The session the reset ended; the next `system/init` carries the new one. */\n previousSessionId?: string\n}\n\n/**\n * Claude Code threw its conversation away and started a new one. Schema from\n * Claude Code 2.1.280: `{type: \"conversation_reset\", new_conversation_id,\n * uuid, session_id}`, emitted by `/clear`, plan-mode exit and fresh-session\n * flows. Measured the same day by driving the real CLI: an ordinary turn emits\n * none, and `/clear` emits one carrying the OLD `session_id`, followed by a\n * `system/init` with a new session id that differs from `new_conversation_id`,\n * after which Claude could not recall the conversation. A frame without a\n * string `new_conversation_id` is ignored, as the CLI's own adapter drops it.\n */\nexport function parseConversationReset(msg: ClaudeStreamMessage): ConversationReset | null {\n if (msg.type !== \"conversation_reset\") return null\n const newConversationId = str(msg.new_conversation_id)\n if (!newConversationId) return null\n return { newConversationId, previousSessionId: str(msg.session_id) }\n}\n\n/**\n * Deliberately a note and not a history replay: every known trigger is a\n * clear the user or Claude Code asked for, and replaying opencode's transcript\n * on the next message would silently undo it.\n */\nexport function formatConversationResetNote(): string {\n return `\\n${CONVERSATION_RESET_MARKER} Claude Code cleared its conversation, so from here on Claude does not see the earlier messages this chat still shows. Start a new opencode session for a clean slate, or restate what matters.\\n`\n}\n\n/** Logs the reset and returns the transcript note, or null when not one. */\nexport function reportConversationReset(msg: ClaudeStreamMessage): string | null {\n const reset = parseConversationReset(msg)\n if (!reset) return null\n log.notice(\"claude code reset its conversation\", {\n newConversationId: reset.newConversationId,\n previousSessionId: reset.previousSessionId ?? null,\n })\n return formatConversationResetNote()\n}\n\n// ---------------------------------------------------------------------------\n// result subtype\n// ---------------------------------------------------------------------------\n\nexport const RESULT_ERROR_MARKER = \"▌ **claude code error:**\"\n\nconst RESULT_SUBTYPES: Record<string, string> = {\n error_max_turns: \"it hit its internal turn limit before finishing\",\n error_during_execution: \"it failed while running the turn\",\n error_max_budget_usd: \"it hit the configured spend limit for the turn\",\n error_max_structured_output_retries: \"it could not produce valid structured output\",\n}\n\n/**\n * A `result` whose subtype is not `success` is a failed turn, and until now it\n * finished as a clean `stop`: opencode recorded it as a normal reply and the\n * only trace of the subtype was a debug log line.\n *\n * This is the with-result case only. A CLI that dies without emitting a\n * `result` at all is a different failure, handled elsewhere.\n */\nexport function describeResultFailure(msg: ClaudeStreamMessage): string | null {\n if (msg.type !== \"result\") return null\n const subtype = msg.subtype\n if (!subtype || subtype === \"success\") return null\n const explanation = RESULT_SUBTYPES[subtype]\n return explanation\n ? `Claude Code ended the turn with \\`${subtype}\\`: ${explanation}.`\n : `Claude Code ended the turn with \\`${subtype}\\`.`\n}\n\nexport function formatResultFailureNote(message: string): string {\n return `\\n${RESULT_ERROR_MARKER} ${message}\\n`\n}\n\n// ---------------------------------------------------------------------------\n// stdout silence after content\n// ---------------------------------------------------------------------------\n\nexport const STREAM_TIMEOUT_MARKER = \"▌ **stream timeout:**\"\n\n/**\n * The inactivity watchdog in `doStream` closes a turn whose CLI produced\n * output and then stopped talking without sending a `result`. That decision\n * was log-only, so the operator saw a reply that simply stopped mid-thought\n * with nothing saying why. This is the note that says it.\n */\nexport function formatStreamTimeoutNote(silenceMs: number): string {\n const seconds = Math.max(1, Math.round(silenceMs / 1000))\n return (\n `\\n${STREAM_TIMEOUT_MARKER} The Claude Code CLI produced output and then went ` +\n `silent for ${seconds}s without finishing the turn, so it was closed without a ` +\n \"result. The answer above may be incomplete.\\n\"\n )\n}\n","export const QUESTION_TOOL_NAME = \"question\"\n\nexport const APPROVED_EXIT_PLAN_MODE_MESSAGE =\n \"User has approved your plan. You can now start coding. Start with updating your todo list if applicable.\"\n\nconst REJECTED_EXIT_PLAN_MODE_PREFIX =\n \"The user doesn't want to proceed with this tool use. The tool use was rejected. To tell you how to proceed, the user said:\"\n\nconst PLAN_MODE_APPROVAL_QUESTION = \"Do you want to proceed with this plan?\"\nconst OPENCODE_QUESTION_RESULT_PREFIX = \"User has answered your questions: \"\nconst OPENCODE_QUESTION_RESULT_SUFFIX =\n \". You can now continue with the user's answers in mind.\"\n/** What opencode writes for a question the operator left blank. */\nconst OPENCODE_UNANSWERED = \"Unanswered\"\n\nconst KEY_SEPARATOR = \"\\u0000\"\n\n/**\n * A synthetic call to opencode's native `question` tool, emitted so the turn\n * ends on `tool-calls` and the operator's answer arrives on the next\n * `doStream` as a `tool-result` with the same id. Shared with the account\n * failover form (`src/account-failover.ts`), which uses the identical\n * mechanism for a different question.\n */\nexport interface QuestionToolCall {\n toolCallId: string\n toolName: typeof QUESTION_TOOL_NAME\n input: {\n questions: Array<{\n header: string\n question: string\n options: Array<{ label: string; description: string }>\n multiple: boolean\n custom: boolean\n }>\n }\n text: string\n}\n\nexport type ExitPlanModeQuestionCall = QuestionToolCall\n\n/**\n * Whether to bridge `ExitPlanMode` into opencode's native `question` tool\n * this turn.\n *\n * Opt-in (`planModeQuestion`) because the bridge is dormant on the headless\n * transport: `--print` offers the model no `ExitPlanMode` tool at all\n * (measured on CLI 2.1.258), so there is nothing to key on and the model asks\n * for approval in prose instead. opencode's question form itself is fine; the\n * older claim here that it never rendered (anomalyco/opencode#36604) was\n * retracted on 2026-09-06, and both the native form and the `question` proxy\n * were verified round-tripping. Gated on the live registry because emitting a\n * `question` tool-call on a build without that entry renders `⚙ invalid` and\n * wedges the turn. Never bridged during compaction: that turn is text-only and\n * its answer would have nowhere to go.\n */\nexport function isPlanModeQuestionActive(input: {\n configured: boolean | undefined\n opencodeHasQuestion: boolean\n compactionMode: boolean\n}): boolean {\n if (input.compactionMode) return false\n if (input.configured !== true) return false\n return input.opencodeHasQuestion\n}\n\nconst pendingQuestions = new Map<string, string>()\n\nfunction pendingKey(sessionKey: string, questionToolCallId: string): string {\n return `${sessionKey}${KEY_SEPARATOR}${questionToolCallId}`\n}\n\nexport function clearExitPlanModeQuestions(sessionKey: string): void {\n const prefix = `${sessionKey}${KEY_SEPARATOR}`\n for (const key of pendingQuestions.keys()) {\n if (key.startsWith(prefix)) pendingQuestions.delete(key)\n }\n}\n\nexport function hasExitPlanModeQuestions(sessionKey: string): boolean {\n const prefix = `${sessionKey}${KEY_SEPARATOR}`\n return [...pendingQuestions.keys()].some((key) => key.startsWith(prefix))\n}\n\nexport function createExitPlanModeQuestionCall(\n sessionKey: string,\n exitPlanModeToolUseId: string,\n plan: string,\n questionToolCallId = `exit_plan_question_${exitPlanModeToolUseId}`,\n): ExitPlanModeQuestionCall {\n pendingQuestions.set(pendingKey(sessionKey, questionToolCallId), exitPlanModeToolUseId)\n\n return {\n toolCallId: questionToolCallId,\n toolName: QUESTION_TOOL_NAME,\n input: {\n questions: [\n {\n header: \"Plan approval\",\n question: PLAN_MODE_APPROVAL_QUESTION,\n options: [\n { label: \"yes\", description: \"\" },\n { label: \"no\", description: \"\" },\n ],\n multiple: false,\n custom: true,\n },\n ],\n },\n text: plan ? `\\n\\n${plan}\\n` : \"\\n\\n\",\n }\n}\n\nfunction buildToolResultMessage(input: {\n toolUseId: string\n approved: boolean\n feedback: string\n}): string {\n return JSON.stringify({\n type: \"user\",\n message: {\n role: \"user\",\n content: [\n input.approved\n ? {\n type: \"tool_result\",\n tool_use_id: input.toolUseId,\n content: APPROVED_EXIT_PLAN_MODE_MESSAGE,\n }\n : {\n type: \"tool_result\",\n tool_use_id: input.toolUseId,\n content: `${REJECTED_EXIT_PLAN_MODE_PREFIX}\\n${input.feedback || \"no\"}`,\n is_error: true,\n },\n ],\n },\n })\n}\n\nfunction tryParseJson(text: string): unknown {\n try {\n return JSON.parse(text)\n } catch {\n return text\n }\n}\n\n/**\n * Pull the operator's answer out of whatever shape opencode wrapped the\n * `question` tool result in. Exported because the account failover form reads\n * the same results through the same tool; a second copy of this would drift.\n */\nexport function unwrapToolOutput(part: any): unknown {\n const output = part?.output ?? part?.result\n if (typeof output === \"string\") return tryParseJson(output)\n if (!output || typeof output !== \"object\") return output\n\n switch (output.type) {\n case \"json\":\n case \"error-json\":\n return output.value\n case \"text\":\n case \"error-text\":\n return tryParseJson(String(output.value ?? \"\"))\n case \"execution-denied\":\n return {\n denied: true,\n reason: String(output.reason ?? \"question rejected\"),\n }\n case \"content\":\n return Array.isArray(output.value)\n ? output.value\n .map((item: any) => {\n if (item?.type === \"text\") return item.text\n return JSON.stringify(item)\n })\n .join(\"\\n\")\n : output.value\n default:\n return output\n }\n}\n\n/**\n * opencode's `question` tool does not return the answers, it returns one\n * sentence: `User has answered your questions: \"<question>\"=\"<answer>\". You\n * can now continue with the user's answers in mind.` (read out of the 1.18.32\n * binary; several answers to one question are joined with \", \", and a blank\n * one is written as `Unanswered`). The question is ours, so it is matched\n * whole: the account-failover question carries quotes of its own, and a split\n * on quotes cuts the answer in the wrong place. This used to recognise the\n * plan-approval question only, so every failover pick arrived as the whole\n * sentence and was refused as unrecognised. Without a known question, the last\n * `\"=\"` is the split, which is right for any single-question form.\n */\nfunction unwrapOpencodeQuestionResult(value: string, question?: string): string {\n if (\n !value.startsWith(OPENCODE_QUESTION_RESULT_PREFIX) ||\n !value.endsWith(OPENCODE_QUESTION_RESULT_SUFFIX)\n ) {\n return value\n }\n const body = value.slice(\n OPENCODE_QUESTION_RESULT_PREFIX.length,\n value.length - OPENCODE_QUESTION_RESULT_SUFFIX.length,\n )\n if (!body.startsWith('\"') || !body.endsWith('\"')) return value\n\n let answer: string | undefined\n const head = question === undefined ? undefined : `\"${question}\"=\"`\n if (head && body.startsWith(head) && body.length > head.length) {\n answer = body.slice(head.length, -1)\n } else {\n const split = body.lastIndexOf('\"=\"')\n if (split > 0) answer = body.slice(split + 3, -1)\n }\n if (answer === undefined) return value\n return answer === OPENCODE_UNANSWERED ? \"\" : answer\n}\n\n/**\n * Flatten an unwrapped `question` result into the answer strings it holds.\n * `question` is the text the form asked, when the caller knows it; see\n * `unwrapOpencodeQuestionResult` for why it matters.\n */\nexport function collectAnswerStrings(value: unknown, question?: string): string[] {\n if (typeof value === \"string\") return [unwrapOpencodeQuestionResult(value, question)]\n if (Array.isArray(value)) return value.flatMap((item) => collectAnswerStrings(item, question))\n if (!value || typeof value !== \"object\") return []\n\n const obj = value as Record<string, unknown>\n if (obj.denied === true) return [String(obj.reason ?? \"question rejected\")]\n\n for (const key of [\"answers\", \"answer\", \"selected\", \"selection\", \"value\"]) {\n if (key in obj) return collectAnswerStrings(obj[key], question)\n }\n\n return []\n}\n\nfunction classifyQuestionResult(part: any): { approved: boolean; feedback: string } {\n const output = unwrapToolOutput(part)\n const answers = collectAnswerStrings(output, PLAN_MODE_APPROVAL_QUESTION)\n .map((answer) => answer.trim())\n .filter(Boolean)\n\n if (answers.length === 1 && answers[0].toLowerCase() === \"yes\") {\n return { approved: true, feedback: \"\" }\n }\n\n return {\n approved: false,\n feedback: answers.length > 0 ? answers.join(\"\\n\") : \"no\",\n }\n}\n\nexport function consumeExitPlanModeQuestionResult(\n sessionKey: string,\n prompt: Array<{ role: string; content?: unknown }>,\n): string | null {\n for (let i = prompt.length - 1; i >= 0; i--) {\n const msg = prompt[i]\n if (!Array.isArray(msg.content)) continue\n\n for (const part of msg.content as any[]) {\n if (part?.type !== \"tool-result\" || typeof part.toolCallId !== \"string\") {\n continue\n }\n\n const key = pendingKey(sessionKey, part.toolCallId)\n const exitPlanModeToolUseId = pendingQuestions.get(key)\n if (!exitPlanModeToolUseId) continue\n\n pendingQuestions.delete(key)\n const result = classifyQuestionResult(part)\n return buildToolResultMessage({\n toolUseId: exitPlanModeToolUseId,\n approved: result.approved,\n feedback: result.feedback,\n })\n }\n }\n\n return null\n}\n","import { execFile } from \"node:child_process\"\nimport { promisify } from \"node:util\"\nimport { log } from \"./logger.js\"\n\nconst execFileAsync = promisify(execFile)\n\nexport interface CliVersion {\n major: number\n minor: number\n patch: number\n raw: string\n}\n\nconst cache = new Map<string, Promise<CliVersion | null>>()\n\n/**\n * Env vars set on every `claude` child so the binary we detected stays the\n * binary we run.\n *\n * `detectCliVersion` resolves once per cliPath and caches that answer for the\n * life of the opencode process, and several flags are gated on it:\n * `--thinking-display summarized`, `--plugin-dir`, and fast mode via\n * `--settings`. If the CLI autoupdates underneath a long-running opencode the\n * cached version stops describing the binary actually being spawned, so a gated\n * flag can be passed to a CLI that rejects it or withheld from one that\n * supports it. A binary swapped mid-session is a plain correctness hazard\n * besides.\n *\n * Both names were read out of the Claude Code 2.1.263 bundle rather than\n * assumed (`rg -a` over the Mach-O, the technique AGENTS.md records for the CLI\n * stream events). `DISABLE_AUTOUPDATER` is read as an update blocker, and\n * `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` both suppresses non-essential\n * network traffic and counts as a second, independent update blocker.\n * Anthropic's own runner sets `DISABLE_AUTOUPDATER: \"1\"` on the children it\n * spawns, which is the same use we are putting it to here.\n */\nexport const CLI_HYGIENE_ENV_VARS = [\n \"DISABLE_AUTOUPDATER\",\n \"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC\",\n] as const\n\n/**\n * The hygiene vars that are missing from `inherited`, each set to \"1\".\n *\n * Only ever fills a gap: a var the user exported themselves is left exactly as\n * they set it, including an empty string, which both vars read as off. That is\n * the same rule the thinking vars follow in `claudeSpawnEnv`, and it is the\n * escape hatch for anyone who deliberately wants the autoupdater, so this needs\n * no provider option of its own.\n */\nexport function cliHygieneEnv(\n inherited: Record<string, string | undefined> = process.env,\n): Record<string, string> {\n const filled: Record<string, string> = {}\n for (const name of CLI_HYGIENE_ENV_VARS) {\n if (inherited[name] === undefined) filled[name] = \"1\"\n }\n return filled\n}\n\n/**\n * Run `claude --version` once per cliPath and parse the leading semver.\n * Returns null on any failure (binary missing, unparseable output, etc.)\n * so callers can fall back to the most conservative flag set.\n */\nexport function detectCliVersion(cliPath: string): Promise<CliVersion | null> {\n const cached = cache.get(cliPath)\n if (cached) return cached\n const promise = (async (): Promise<CliVersion | null> => {\n try {\n const { stdout } = await execFileAsync(cliPath, [\"--version\"], {\n timeout: 5000,\n })\n const match = /(\\d+)\\.(\\d+)\\.(\\d+)/.exec(stdout.trim())\n if (!match) {\n log.warn(\"claude --version output unparseable\", { stdout: stdout.trim() })\n return null\n }\n const v: CliVersion = {\n major: Number(match[1]),\n minor: Number(match[2]),\n patch: Number(match[3]),\n raw: stdout.trim(),\n }\n log.info(\"detected claude cli version\", { cliPath, version: v.raw })\n if (!cliSupportsThinkingDisplay(v)) {\n log.notice(\n \"claude cli < 2.1.142 detected; Opus 4.7 thinking summaries unavailable. Run `npm i -g @anthropic-ai/claude-code` to upgrade.\",\n { version: v.raw },\n )\n }\n return v\n } catch (err) {\n log.warn(\"failed to detect claude cli version\", {\n cliPath,\n error: err instanceof Error ? err.message : String(err),\n })\n return null\n }\n })()\n cache.set(cliPath, promise)\n return promise\n}\n\nfunction gte(v: CliVersion, target: { major: number; minor: number; patch: number }): boolean {\n if (v.major !== target.major) return v.major > target.major\n if (v.minor !== target.minor) return v.minor > target.minor\n return v.patch >= target.patch\n}\n\n/**\n * `--thinking-display` was introduced in Claude Code 2.1.142 alongside\n * Opus 4.7's \"omitted by default\" thinking behavior. Older CLIs reject\n * the flag with a parse error, so we gate it. Unknown version → return\n * false so we don't risk crashing the spawn.\n */\nexport function cliSupportsThinkingDisplay(v: CliVersion | null): boolean {\n if (!v) return false\n return gte(v, { major: 2, minor: 1, patch: 142 })\n}\n\n/**\n * Fast mode's headless opt-in. In print mode the CLI reports\n * `fast_mode_disabled_reason: \"sdk_opt_in_required\"` unless the *flag* settings\n * layer carries `fastMode: true`, which only `--settings` populates (there is\n * no `--fast` flag, and no fast-mode model name the CLI still accepts).\n *\n * 2.1.220 is the floor because it is the oldest binary the opt-in path was\n * confirmed present in, not because 2.1.219 is known to lack it. An unknown\n * settings key is ignored rather than fatal, so the downside of gating too\n * high is only that fast mode stays off.\n */\nexport function cliSupportsFastMode(v: CliVersion | null): boolean {\n if (!v) return false\n return gte(v, { major: 2, minor: 1, patch: 220 })\n}\n\n/** 2.1.258 is the oldest verified side_question control protocol, not its introduction date. */\nexport function cliSupportsSideQuestion(v: CliVersion | null): boolean {\n if (!v) return false\n return gte(v, { major: 2, minor: 1, patch: 258 })\n}\n\n/**\n * `--thinking` has been part of Claude Code's CLI since the 2.x line.\n * We require a detected 2.0.0+ before passing it; unknown version → skip\n * to avoid crashing a pre-flag binary. Anyone on the 1.x line should\n * upgrade.\n */\nexport function cliSupportsThinking(v: CliVersion | null): boolean {\n if (!v) return false\n return gte(v, { major: 2, minor: 0, patch: 0 })\n}\n\n/** For tests. */\nconst flagSupport = new Map<string, Promise<boolean>>()\n\n/**\n * Probe whether the binary's own `--help` mentions a flag. For flags with no\n * published version marker (`--plugin-dir`), where an invented semver\n * threshold would be a guess. One `--help` spawn per cliPath+flag, cached for\n * the process lifetime. Any failure is false, so the caller skips the flag\n * rather than risking a parse error on spawn. (From @broskees' 68ed142.)\n */\nexport function detectCliSupportsFlag(cliPath: string, flag: string): Promise<boolean> {\n const key = `${cliPath}\\x00${flag}`\n const cached = flagSupport.get(key)\n if (cached) return cached\n const promise = (async (): Promise<boolean> => {\n try {\n const execution = execFileAsync(cliPath, [\"--help\"], {\n timeout: 5000,\n killSignal: \"SIGKILL\",\n maxBuffer: 4 * 1024 * 1024,\n })\n // A wrapper may wait for stdin EOF even when asked for help.\n execution.child.stdin?.end()\n const { stdout } = await execution\n return stdout.includes(flag)\n } catch (err) {\n log.warn(\"failed to probe claude cli flag support\", {\n cliPath,\n flag,\n error: err instanceof Error ? err.message : String(err),\n })\n return false\n }\n })()\n flagSupport.set(key, promise)\n return promise\n}\n\nexport function _clearCache(): void {\n flagSupport.clear()\n cache.clear()\n}\n","import { spawn, type ChildProcess } from \"node:child_process\"\nimport { createInterface } from \"node:readline\"\nimport { randomUUID } from \"node:crypto\"\nimport { EventEmitter } from \"node:events\"\nimport { unlink } from \"node:fs/promises\"\nimport { log } from \"./logger.js\"\nimport { SERVER_CLOSED_MESSAGE, type ProxyMcpServer, type ProxyToolResult } from \"./proxy-mcp.js\"\nimport {\n getPendingProxyCalls,\n rejectAllPendingProxyCallsForSession,\n type PendingProxyCall,\n} from \"./proxy-broker.js\"\nimport { clearLedger } from \"./todo-ledger.js\"\nimport { clearExitPlanModeQuestions, hasExitPlanModeQuestions } from \"./plan-mode-question.js\"\nimport { clearAccountFailoverQuestions } from \"./account-failover.js\"\nimport { clearCompression } from \"./compression-store.js\"\nimport {\n cliHygieneEnv,\n cliSupportsFastMode,\n cliSupportsThinking,\n cliSupportsThinkingDisplay,\n type CliVersion,\n} from \"./cli-version.js\"\nimport type { ReasoningEffort } from \"./types.js\"\nimport { dispatchSideQuestionResponse, isSideQuestionPending } from \"./side-question.js\"\n\nexport interface ActiveProcess {\n proc: ChildProcess\n lineEmitter: EventEmitter\n proxyServer?: ProxyMcpServer | null\n /**\n * Hash of the bridged opencode MCP config the process was spawned with.\n * `null` when the bridge produced nothing (no MCP servers). `undefined`\n * when the bridge was disabled. Used to detect mid-session config drift\n * and force a respawn.\n */\n mcpHash?: string | null\n /** Temp file holding `--append-system-prompt-file` content; unlinked on exit. */\n systemPromptFile?: string\n /** Effort the process was spawned with, so a respawn keeps it. */\n effort?: ReasoningEffort\n /** When the child was spawned, so `/claude-code-doctor` can report its age. */\n startedAt?: number\n /**\n * The binary this child was spawned with. Account failover compares it\n * against the path the current turn resolves to: a difference means the\n * conversation has moved to another account, and the process plus its\n * Claude session id have to go because a transcript cannot resume across\n * accounts. Absent on the interactive shim, which never fails over.\n */\n cliPath?: string\n cliArgs?: string[]\n // Retain resolved calls until continuation settles, including late channel closure.\n pendingProxyCompletions?: Map<string, {\n call: PendingProxyCall\n result: ProxyToolResult\n recoveryRequired: boolean\n }>\n /**\n * stdout lines the child emitted while no turn had a line listener\n * attached (between opencode turns). Bounded; see `bufferUnattendedLine`.\n * Absent on the interactive shim, which has no unattended window.\n */\n unattendedLines?: string[]\n /** Lines evicted from `unattendedLines` because the cap was hit. */\n unattendedDropped?: number\n /**\n * opencode session this process last served, tagged by doStream each turn.\n * `/btw` runs from a command hook that only knows the session id, so this is\n * how it finds the process to ask (see `findActiveProcessBySessionId`).\n */\n opencodeSessionID?: string\n /** What the /btw command hook needs to send a side question to this process early. */\n asideTransport?: { cliPath: string; interactive: boolean }\n /**\n * True from a stdin write that asks the CLI for work until its terminal\n * `result` line, whether or not a turn is still listening. Set by\n * `noteTurnStarted`, cleared by `noteTurnLine` (see `interruptTurn`).\n */\n turnInFlight?: boolean\n turnIdleWaiters?: Array<() => void>\n /**\n * Tail of what the child last wrote to stderr, capped at\n * `STDERR_RETAIN_BYTES` with the newest bytes kept. Often the only record\n * of why a child died when it closed without emitting a terminal `result`\n * line; see `describeChildCrash`.\n */\n lastStderr?: string\n}\n\n/** Most recently used process serving an opencode session id, if any. */\nexport function findActiveProcessBySessionId(sessionID: string): ActiveProcess | undefined {\n let found: ActiveProcess | undefined\n // Map order is LRU (see `touch`), so the last match is the freshest.\n for (const ap of activeProcesses.values()) {\n if (ap.opencodeSessionID === sessionID) found = ap\n }\n return found\n}\n\n// A child normally only speaks while a doStream turn is listening. The one\n// exception is a turn that ended on the CLI's side while opencode was still\n// waiting on a proxy call (Claude's MCP client gave up on the request and\n// the model carried on alone). Keep what it said so the next turn can show\n// it instead of losing it; cap it so a runaway child cannot grow the heap.\nconst UNATTENDED_LINE_CAP = 500\nconst UNATTENDED_BYTE_CAP = 2 * 1024 * 1024\n\nexport function bufferUnattendedLine(ap: ActiveProcess, line: string): void {\n const lines = (ap.unattendedLines ??= [])\n lines.push(line)\n let bytes = 0\n for (const kept of lines) bytes += Buffer.byteLength(kept)\n while (\n lines.length > 0 &&\n (lines.length > UNATTENDED_LINE_CAP || bytes > UNATTENDED_BYTE_CAP)\n ) {\n bytes -= Buffer.byteLength(lines.shift()!)\n ap.unattendedDropped = (ap.unattendedDropped ?? 0) + 1\n }\n}\n\n/** Hand over and clear everything the child said while nobody listened. */\nexport function takeUnattendedLines(ap: ActiveProcess): {\n lines: string[]\n dropped: number\n} {\n const lines = ap.unattendedLines ?? []\n const dropped = ap.unattendedDropped ?? 0\n ap.unattendedLines = []\n ap.unattendedDropped = 0\n return { lines, dropped }\n}\n\n// The CLI writes its own diagnostics to stderr, which is where the reason a\n// child died is usually the only thing on record. Keep the tail so a turn\n// that ends with the child gone can say why; bounded, newest bytes win.\nconst STDERR_RETAIN_BYTES = 2 * 1024\n\nexport function retainStderr(ap: ActiveProcess, chunk: string): void {\n ap.lastStderr = ((ap.lastStderr ?? \"\") + chunk).slice(-STDERR_RETAIN_BYTES)\n}\n\n/**\n * One line (plus the stderr tail) explaining a child that closed its stdio\n * without emitting a terminal `result`. Before this the turn simply finished\n * with reason `stop` and empty usage, so a crashed CLI read as a short but\n * successful answer.\n */\nexport function describeChildCrash(\n exitCode: number | null | undefined,\n signal: NodeJS.Signals | null | undefined,\n lastStderr: string | undefined,\n): string {\n const how = signal\n ? `was killed by ${signal}`\n : typeof exitCode === \"number\"\n ? `exited with code ${exitCode}`\n : \"closed its output\"\n const tail = lastStderr?.trim()\n return (\n `The Claude Code CLI ${how} before finishing this turn (no result was emitted), ` +\n \"so the answer above may be incomplete.\" +\n (tail ? `\\n\\nLast stderr from the CLI:\\n${tail}` : \"\")\n )\n}\n\n// One active CLI process per session key. Keyed by a composite\n// (cwd + model + opencode session-affinity) so two chats don't race.\n// Iteration order is insertion order, which we refresh on access to\n// make this a poor-man's LRU; see `touch()` below.\nconst activeProcesses = new Map<string, ActiveProcess>()\nconst claudeSessions = new Map<string, string>()\n// Idle-eviction timers keyed like `activeProcesses` (idle timeout by\n// @bernardofortes, absorbed from a5f723a).\nconst idleEvictionTimers = new Map<string, ReturnType<typeof setTimeout>>()\nconst MAX_IDLE_TIMEOUT_MS = 2_147_483_647\n\n/**\n * Idle eviction is off unless `idleProcessTimeoutMs` is set: an unset option\n * resolves to 0, which arms no timer, so workers live until LRU eviction as\n * they always have. PR #36 (@broskees) proposed 30 minutes by default; that\n * was reverted at merge because it changes when a resumed chat pays for a\n * fresh `--resume` spawn, which is the user's call. An idle `claude --print`\n * holds roughly 250 MB resident, so setting it is worth documenting, not\n * imposing. The Claude session id survives eviction either way.\n */\nexport const DEFAULT_IDLE_PROCESS_TIMEOUT_MS = 0\n\n/** The idle timeout a caller-facing option resolves to: unset means the default. */\nexport function resolveIdleProcessTimeoutMs(configured: number | undefined): number {\n return configured === undefined ? DEFAULT_IDLE_PROCESS_TIMEOUT_MS : configured\n}\n\n// Cap on live CLI subprocesses. Session-affinity-keyed entries accumulate\n// one-per-chat, so an unbounded map would leak processes as users open new\n// chats. This caps at a reasonable working-set and evicts the oldest idle\n// one, never a process that is mid-turn. Kept at 16: PR #36 proposed 8 on\n// the assumption that a default idle timer does the real work, and that\n// default was not adopted, so the cap is still the only bound.\nexport const MAX_ACTIVE_PROCESSES = 16\nconst PROCESS_EXIT_TIMEOUT_MS = 1_500\nconst PROCESS_FORCE_EXIT_TIMEOUT_MS = 500\n/** Same wording the attached turn's close handler uses, so one log line\n * shape covers a child that died mid-turn and one that died between turns. */\nexport const CHILD_EXITED_MESSAGE =\n \"Claude CLI subprocess closed before pending tool calls were resolved\"\n\nfunction envFlagEnabled(value: string | undefined): boolean {\n if (value === undefined) return false\n const normalized = value.trim().toLowerCase()\n if (!normalized) return false\n return ![\"0\", \"false\", \"no\", \"off\"].includes(normalized)\n}\n\nexport function isClaudeThinkingDisabled(): boolean {\n return (\n envFlagEnabled(process.env.CLAUDE_CODE_DISABLE_THINKING) ||\n envFlagEnabled(process.env.CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING)\n )\n}\n\n/**\n * The CLI's effort vocabulary is low | medium | high | xhigh | max. `minimal`\n * is this provider's own lowest step with no CLI counterpart, so it lands on\n * `low`.\n */\nexport function cliEffortLevel(effort: ReasoningEffort): string {\n return effort === \"minimal\" ? \"low\" : effort\n}\n\nexport function claudeSpawnEnv(opts?: {\n ignoreAnthropicApiKey?: boolean\n /** Reasoning effort for this spawn; wins over a shell-level override. */\n effort?: ReasoningEffort\n}): Record<string, string | undefined> {\n const env: Record<string, string | undefined> = {\n ...process.env,\n TERM: \"xterm-256color\",\n // Pin the child to the binary whose version we detected, and keep it off\n // non-essential network calls. Fills gaps only, so an explicit shell value\n // survives: see `cliHygieneEnv` for why the version has to hold still.\n ...cliHygieneEnv(),\n }\n\n // Effort travels as CLAUDE_CODE_EFFORT_LEVEL, which the CLI treats as the\n // session-wide override (it beats settings.json and `/effort`). An env var\n // rather than `--effort` because a CLI too old to know it ignores it\n // instead of refusing to start. Unlike the thinking vars below, an explicit\n // effort from the request wins over the shell: the variant picker and an\n // agent's `reasoningEffort` are per-request choices, a shell export is not.\n if (opts?.effort) {\n env.CLAUDE_CODE_EFFORT_LEVEL = cliEffortLevel(opts.effort)\n }\n\n // Force subscription auth: with an API key in the env, Claude Code bills\n // pay-as-you-go (Console) instead of the logged-in plan, bypassing the\n // Agent SDK credit. Opt-in via `ignoreAnthropicApiKey`.\n if (opts?.ignoreAnthropicApiKey) {\n delete env.ANTHROPIC_API_KEY\n delete env.ANTHROPIC_AUTH_TOKEN\n }\n\n // Default-on thinking summaries for opus-4-7 (which omits thinking by\n // default on the CLI side). Any var the user has explicitly set in their\n // shell is passed through untouched; the plugin only fills in the default.\n if (\n !isClaudeThinkingDisabled() &&\n process.env.CLAUDE_CODE_SHOW_THINKING_SUMMARIES === undefined\n ) {\n env.CLAUDE_CODE_SHOW_THINKING_SUMMARIES = \"1\"\n }\n\n return env\n}\n\nfunction touch(key: string): void {\n const existing = activeProcesses.get(key)\n if (existing) {\n activeProcesses.delete(key)\n activeProcesses.set(key, existing)\n }\n}\n\n/**\n * Make room for a new child, but never by killing one that is mid-turn.\n * Insertion order is LRU, so the first idle entry is the oldest safe victim.\n * Evicting an in-flight process truncates that turn silently: its readline\n * closes, the close handler finishes the stream, and the operator sees a\n * half-written answer with no error. When every process is busy we exceed the\n * cap for now rather than kill live work; the next spawn tries again.\n */\nexport function evictIfNeeded(): void {\n while (activeProcesses.size >= MAX_ACTIVE_PROCESSES) {\n let victimKey: string | undefined\n for (const [key, ap] of activeProcesses) {\n if (!isTurnInFlight(ap)) {\n victimKey = key\n break\n }\n }\n if (!victimKey) {\n log.warn(\"every claude process is mid-turn; skipping LRU eviction\", {\n active: activeProcesses.size,\n cap: MAX_ACTIVE_PROCESSES,\n })\n return\n }\n log.info(\"evicting LRU claude process\", { sessionKey: victimKey })\n deleteActiveProcess(victimKey)\n }\n}\n\n// Turn lifecycle and interrupt (from @broskees' 68ed142, adapted).\n//\n// The Claude CLI runs one turn per process. Closing the opencode-side stream\n// tells it nothing: before this, an abort only detached our listeners and the\n// CLI ran the abandoned turn to completion (Joseph Roberts measured ~7,500\n// extra characters generated after abort on a haiku probe), kept billing, kept\n// running tools, and its late output landed in whatever turn came next, whose\n// own stream was then closed early by the stale `result`. The CLI answers a\n// stream-json `control_request` of subtype `interrupt` by aborting the turn\n// and emitting a terminal `result`, normally within milliseconds.\n\nconst TURN_INTERRUPT_TIMEOUT_MS = 5_000\n\n/** Cheap pre-filter before JSON.parse, since every CLI stdout line hits this. */\nfunction isTerminalResultLine(line: string): boolean {\n if (!line.includes('\"result\"')) return false\n try {\n return (JSON.parse(line) as { type?: string }).type === \"result\"\n } catch {\n return false\n }\n}\n\nfunction settleTurn(ap: ActiveProcess): void {\n ap.turnInFlight = false\n const waiters = ap.turnIdleWaiters ?? []\n ap.turnIdleWaiters = []\n for (const wake of waiters) wake()\n}\n\n/** Call immediately before any stdin write that asks the CLI to do work. */\nexport function noteTurnStarted(ap: ActiveProcess): void {\n // The interactive transport never reports through `noteTurnLine`, so a flag\n // set there would never clear.\n if (ap.asideTransport?.interactive) return\n ap.turnInFlight = true\n}\n\n/**\n * Feed every CLI stdout line here, independent of whichever turn currently\n * owns the stream: a `result` that lands after its turn detached (the abort\n * case) must still mark the CLI idle rather than leak into the next turn.\n */\nexport function noteTurnLine(ap: ActiveProcess, line: string): void {\n if (!ap.turnInFlight) return\n if (isTerminalResultLine(line)) settleTurn(ap)\n}\n\nexport function isTurnInFlight(ap: ActiveProcess): boolean {\n return ap.turnInFlight === true\n}\n\n/** Resolves true once the CLI is idle, false if it stayed busy past the timeout. */\nexport function awaitTurnIdle(ap: ActiveProcess, timeoutMs: number): Promise<boolean> {\n if (!ap.turnInFlight) return Promise.resolve(true)\n return new Promise((resolve) => {\n const wake = () => {\n clearTimeout(timer)\n resolve(true)\n }\n const timer = setTimeout(() => {\n const waiters = ap.turnIdleWaiters ?? []\n const at = waiters.indexOf(wake)\n if (at >= 0) waiters.splice(at, 1)\n resolve(false)\n }, timeoutMs)\n ;(ap.turnIdleWaiters ??= []).push(wake)\n })\n}\n\n/** Ask the CLI to abandon the in-flight turn, and wait for it to say it did. */\nexport function interruptTurn(\n ap: ActiveProcess,\n timeoutMs = TURN_INTERRUPT_TIMEOUT_MS,\n): Promise<boolean> {\n if (!ap.turnInFlight) return Promise.resolve(true)\n const stdin = ap.proc.stdin\n if (ap.asideTransport?.interactive || !stdin || !stdin.writable) {\n // A TUI stdin would type the JSON in as text. Wait the turn out instead.\n log.notice(\"cannot interrupt this transport; waiting for the turn to end\")\n return awaitTurnIdle(ap, timeoutMs)\n }\n try {\n stdin.write(\n JSON.stringify({\n type: \"control_request\",\n request_id: randomUUID(),\n request: { subtype: \"interrupt\" },\n }) + \"\\n\",\n )\n } catch (error) {\n log.warn(\"failed to write interrupt control request\", {\n error: error instanceof Error ? error.message : String(error),\n })\n return Promise.resolve(false)\n }\n return awaitTurnIdle(ap, timeoutMs)\n}\n\nfunction cancelIdleProcessEviction(key: string): void {\n const timer = idleEvictionTimers.get(key)\n if (!timer) return\n clearTimeout(timer)\n idleEvictionTimers.delete(key)\n}\n\nexport function getActiveProcess(key: string): ActiveProcess | undefined {\n const ap = activeProcesses.get(key)\n if (ap) {\n cancelIdleProcessEviction(key)\n touch(key)\n }\n return ap\n}\n\nexport function setActiveProcess(key: string, ap: ActiveProcess): void {\n cancelIdleProcessEviction(key)\n activeProcesses.set(key, ap)\n}\n\n/**\n * Evict a headless Claude worker after a completed turn has stayed idle.\n * Armed by the turn's `completeResult`, so the clock starts when a turn\n * finishes, not when the child was spawned. Reusing the worker through\n * `getActiveProcess` cancels the timer. The Claude session id is\n * intentionally retained so the next turn can continue the same conversation\n * via `--resume`. A process found mid-turn when the timer fires (a recovered\n * continuation, an auto-continue, a late tool result) is not evicted; the\n * timer is re-armed instead, the same rule the LRU cap follows.\n */\nexport function scheduleIdleProcessEviction(\n key: string,\n timeoutMs: number | undefined,\n): void {\n cancelIdleProcessEviction(key)\n if (\n typeof timeoutMs !== \"number\" ||\n !Number.isFinite(timeoutMs) ||\n timeoutMs <= 0 ||\n timeoutMs > MAX_IDLE_TIMEOUT_MS\n ) {\n return\n }\n\n const scheduledProcess = activeProcesses.get(key)\n if (!scheduledProcess) return\n\n const timer = setTimeout(() => {\n idleEvictionTimers.delete(key)\n if (activeProcesses.get(key) !== scheduledProcess) return\n if (isTurnInFlight(scheduledProcess)) {\n log.info(\"idle timer found a turn in flight; re-arming\", { sessionKey: key, timeoutMs })\n scheduleIdleProcessEviction(key, timeoutMs)\n return\n }\n log.info(\"evicting idle claude process\", { sessionKey: key, timeoutMs })\n deleteActiveProcess(key)\n }, timeoutMs)\n timer.unref()\n idleEvictionTimers.set(key, timer)\n}\n\n/** Whether an idle-eviction timer is armed for the key (read-only, for tests). */\nexport function isIdleProcessEvictionScheduled(key: string): boolean {\n return idleEvictionTimers.has(key)\n}\n\nfunction detachActiveProcess(key: string): ActiveProcess | undefined {\n cancelIdleProcessEviction(key)\n const ap = activeProcesses.get(key)\n if (!ap) return undefined\n activeProcesses.delete(key)\n if (ap.proxyServer) {\n void ap.proxyServer.close()\n // The server's close already answered every open HTTP request with an\n // error; the broker's entries for them can never be resolved to anyone\n // now, and a `task` call has no deadline that would otherwise reap them.\n rejectAllPendingProxyCallsForSession(key, new Error(SERVER_CLOSED_MESSAGE))\n }\n return ap\n}\n\n/**\n * Release everything this plugin holds for one opencode session that was\n * deleted: its live `claude` children (any model, effort, or compaction\n * spawn), the remembered Claude session ids, and per-session state. Unlike\n * idle eviction this is a real deletion, so nothing is kept for a resume.\n * The `\"default\"` affinity is the shared bucket used when no session id is\n * known and is deliberately never matched. Returns the released keys.\n */\nexport function deleteActiveProcessesForSession(sessionID: string): string[] {\n if (!sessionID || sessionID === \"default\") return []\n const released: string[] = []\n for (const [key, ap] of [...activeProcesses]) {\n const owned =\n ap.opencodeSessionID === sessionID || describeSessionKey(key).session === sessionID\n if (!owned) continue\n log.info(\"releasing claude process for deleted session\", { sessionKey: key, sessionID })\n void deleteActiveProcessAndWait(key)\n released.push(key)\n }\n // Session ids and per-session state can outlive their process (idle\n // eviction keeps them for `--resume`); a deleted session never resumes.\n for (const key of [...claudeSessions.keys()]) {\n if (describeSessionKey(key).session !== sessionID) continue\n deleteClaudeSessionId(key)\n clearCompression(key)\n if (!released.includes(key)) released.push(key)\n }\n return released\n}\n\n/**\n * Synchronous best-effort sweep for host process exit. Node does not kill\n * children on exit, so without this a hard opencode shutdown reparents every\n * live `claude` to init. Must stay sync: `process.on(\"exit\")` runs no async\n * work. Session ids are left alone; the process is going away with them.\n */\nexport function killAllActiveProcesses(): string[] {\n const keys = [...activeProcesses.keys()]\n for (const key of keys) deleteActiveProcess(key)\n return keys\n}\n\nlet processExitCleanupWired = false\n\n/**\n * Arm `killAllActiveProcesses` for host process exit, once per process. The\n * plugin entry can run more than once (tests, account expansion), and each\n * run must not add another `exit` listener. Returns whether this call armed it.\n */\nexport function ensureProcessExitCleanup(): boolean {\n if (processExitCleanupWired) return false\n processExitCleanupWired = true\n process.once(\"exit\", () => {\n killAllActiveProcesses()\n })\n return true\n}\n\nexport function deleteActiveProcess(key: string): void {\n const ap = detachActiveProcess(key)\n ap?.proc.kill()\n}\n\nfunction hasProcessExited(proc: ChildProcess): boolean {\n return proc.exitCode !== null || proc.signalCode !== null\n}\n\nfunction waitForProcessExit(\n proc: ChildProcess,\n timeoutMs: number,\n): Promise<boolean> {\n if (hasProcessExited(proc)) return Promise.resolve(true)\n\n return new Promise((resolve) => {\n const onExit = () => {\n clearTimeout(timer)\n resolve(true)\n }\n const timer = setTimeout(() => {\n proc.off(\"exit\", onExit)\n resolve(hasProcessExited(proc))\n }, timeoutMs)\n proc.once(\"exit\", onExit)\n })\n}\n\nexport async function deleteActiveProcessAndWait(\n key: string,\n options: {\n exitTimeoutMs?: number\n forceExitTimeoutMs?: number\n } = {},\n): Promise<boolean> {\n const ap = detachActiveProcess(key)\n if (!ap || hasProcessExited(ap.proc)) return true\n\n const gracefulExit = waitForProcessExit(\n ap.proc,\n options.exitTimeoutMs ?? PROCESS_EXIT_TIMEOUT_MS,\n )\n ap.proc.kill()\n if (await gracefulExit) return true\n\n const forcedExit = waitForProcessExit(\n ap.proc,\n options.forceExitTimeoutMs ?? PROCESS_FORCE_EXIT_TIMEOUT_MS,\n )\n ap.proc.kill(\"SIGKILL\")\n if (await forcedExit) return true\n\n log.warn(\"claude process did not exit; starting a fresh session\", {\n sessionKey: key,\n })\n deleteClaudeSessionId(key)\n return false\n}\n\nexport function getClaudeSessionId(key: string): string | undefined {\n return claudeSessions.get(key)\n}\n\n/**\n * A Claude session id outlives its process on purpose, so nothing in the\n * ordinary lifecycle ever removes one: under a long-lived `opencode serve`\n * that hops projects and models this map only grows, and each entry pins a\n * todo ledger with it. The cap is the same shape as\n * `MAX_COMPRESSION_ENTRIES`, and it is generous because the cost of getting\n * it wrong is a conversation that silently restarts.\n */\nexport const MAX_CLAUDE_SESSION_ENTRIES = 64\n\n/**\n * A key with a live process, a proxied call still in the air or an unanswered\n * plan-mode question is doing work that the id is part of; dropping it would\n * strand that work on a session the next turn no longer resumes. Read from\n * the map directly rather than through `getActiveProcess`: that one refreshes\n * LRU order and cancels idle timers, which a scan must not do.\n */\nfunction claudeSessionIsBusy(key: string): boolean {\n if (activeProcesses.has(key)) return true\n return getPendingProxyCalls(key).length > 0 || hasExitPlanModeQuestions(key)\n}\n\n/**\n * Shed the least recently used idle sessions. When every key is busy this\n * evicts nothing and the map runs over the cap for a while, the same rule\n * `evictIfNeeded` follows: exceeding a cap briefly is cheaper than cutting a\n * conversation that is still running.\n */\nfunction capClaudeSessions(): void {\n for (const key of [...claudeSessions.keys()]) {\n if (claudeSessions.size <= MAX_CLAUDE_SESSION_ENTRIES) return\n if (claudeSessionIsBusy(key)) continue\n log.info(\"claude session cap reached; releasing an idle session\", {\n sessionKey: key,\n size: claudeSessions.size,\n cap: MAX_CLAUDE_SESSION_ENTRIES,\n })\n // Through the central release so the todo ledger and any pending\n // plan-mode question go with it rather than outliving the id.\n deleteClaudeSessionId(key)\n }\n}\n\nexport function setClaudeSessionId(key: string, sessionId: string): void {\n // Re-inserting moves the key to the back, so the cap sheds the conversation\n // that has been quiet longest rather than the one that started first.\n claudeSessions.delete(key)\n claudeSessions.set(key, sessionId)\n capClaudeSessions()\n}\n\nexport function deleteClaudeSessionId(key: string): void {\n clearExitPlanModeQuestions(key)\n clearAccountFailoverQuestions(key)\n const claudeSessionId = claudeSessions.get(key)\n if (claudeSessionId) clearLedger(claudeSessionId)\n claudeSessions.delete(key)\n}\n\nexport function effortSessionKey(baseKey: string, effort?: ReasoningEffort): string {\n return effort ? `${baseKey}::effort=${effort}` : baseKey\n}\n\n/** Retire sibling effort sessions before deciding whether to replay history. */\nexport function invalidateOtherEffortSessions(\n baseKey: string,\n effort?: ReasoningEffort,\n): void {\n const levels: (ReasoningEffort | undefined)[] = [\n undefined, \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\",\n ]\n const staleKeys = levels\n .filter((level) => level !== effort)\n .map((level) => effortSessionKey(baseKey, level))\n\n // Refuse the transition atomically. Tool results and recovery completions\n // still belong to the old process; they must finish at its original effort.\n for (const key of staleKeys) {\n const active = activeProcesses.get(key)\n if (\n getPendingProxyCalls(key).length ||\n hasExitPlanModeQuestions(key) ||\n active?.pendingProxyCompletions?.size ||\n (active && (active.lineEmitter.listenerCount(\"line\") > 0 || isSideQuestionPending(active)))\n ) {\n throw new Error(\n \"Cannot change reasoning effort while the previous effort session has pending work. Finish that work at its original effort first.\",\n )\n }\n }\n for (const key of staleKeys) {\n deleteActiveProcess(key)\n deleteClaudeSessionId(key)\n clearCompression(key)\n }\n}\n\nexport function spawnClaudeProcess(\n cliPath: string,\n cliArgs: string[],\n cwd: string,\n sessionKey: string,\n proxyServer?: ProxyMcpServer | null,\n mcpHash?: string | null,\n systemPromptFile?: string,\n ignoreAnthropicApiKey?: boolean,\n effort?: ReasoningEffort,\n): ActiveProcess {\n evictIfNeeded()\n log.info(\"spawning new claude process\", {\n cliPath,\n cliArgs,\n cwd,\n sessionKey,\n effort,\n })\n\n const proc = spawn(cliPath, cliArgs, {\n cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: claudeSpawnEnv({ ignoreAnthropicApiKey, effort }),\n shell: process.platform === \"win32\",\n })\n\n const lineEmitter = new EventEmitter()\n\n const ap: ActiveProcess = {\n proc,\n lineEmitter,\n proxyServer: proxyServer ?? null,\n mcpHash,\n systemPromptFile,\n effort,\n startedAt: Date.now(),\n cliPath,\n cliArgs: [...cliArgs],\n unattendedLines: [],\n unattendedDropped: 0,\n }\n\n const rl = createInterface({ input: proc.stdout! })\n rl.on(\"line\", (line: string) => {\n if (dispatchSideQuestionResponse(ap, line)) return\n noteTurnLine(ap, line)\n if (lineEmitter.listenerCount(\"line\") === 0) {\n bufferUnattendedLine(ap, line)\n return\n }\n lineEmitter.emit(\"line\", line)\n })\n rl.on(\"close\", () => {\n settleTurn(ap)\n lineEmitter.emit(\"close\")\n })\n cancelIdleProcessEviction(sessionKey)\n activeProcesses.set(sessionKey, ap)\n\n // Baseline 'error' listener so Node doesn't throw when the process emits\n // an error between stream turns (no per-stream listener attached then).\n proc.on(\"error\", (err) => {\n log.error(\"claude process error\", { sessionKey, error: err.message })\n })\n\n // Same baseline for the child's stdin, which is a separate emitter. Every\n // write that asks the CLI for work (fresh envelope, auto-continue, the\n // watchdog re-send, the interrupt request) can land after the child died,\n // and an unhandled 'error' on a stream throws inside opencode's own\n // process. Ending the turn is not this handler's job: the child is gone,\n // so its readline 'close' follows and the turn's close handler reports it\n // (see `describeChildCrash`). Releasing `turnInFlight` is, since no\n // terminal `result` is ever coming for a write that never arrived.\n proc.stdin?.on(\"error\", (err: NodeJS.ErrnoException) => {\n log.warn(\"claude process stdin error\", {\n sessionKey,\n code: err.code,\n error: err.message,\n })\n settleTurn(ap)\n })\n\n proc.on(\"exit\", (code, signal) => {\n log.info(\"claude process exited\", { code, signal, sessionKey })\n void proxyServer?.close()\n if (systemPromptFile) {\n void unlink(systemPromptFile).catch(() => {})\n }\n const ownsSessionKey = activeProcesses.get(sessionKey) === ap\n if (ownsSessionKey) {\n cancelIdleProcessEviction(sessionKey)\n activeProcesses.delete(sessionKey)\n // The child is the only thing that could still consume these calls'\n // results. A turn that is attached rejects them from its own close\n // handler; this covers a child that dies between turns, which no\n // deadline would otherwise reap now that `task` has none.\n if (getPendingProxyCalls(sessionKey).length > 0) {\n rejectAllPendingProxyCallsForSession(\n sessionKey,\n new Error(CHILD_EXITED_MESSAGE),\n )\n }\n }\n if (ownsSessionKey && code !== 0 && code !== null) {\n log.info(\"process exited with error, clearing session\", {\n code,\n sessionKey,\n })\n claudeSessions.delete(sessionKey)\n }\n })\n\n proc.stderr?.on(\"data\", (data: Buffer) => {\n const stderr = data.toString()\n log.debug(\"stderr\", { data: stderr.slice(0, 200) })\n retainStderr(ap, stderr)\n\n // \"No conversation found with session ID: <uuid>\" is what `--resume`\n // prints for a purged transcript — note the lowercase \"session ID\",\n // which the capitalized match below does not catch.\n if (\n stderr.includes(\"No conversation found\") ||\n (stderr.includes(\"Session ID\") &&\n (stderr.includes(\"already in use\") ||\n stderr.includes(\"not found\") ||\n stderr.includes(\"invalid\")))\n ) {\n if (activeProcesses.get(sessionKey) === ap) {\n log.warn(\"claude session ID error, clearing session\", {\n sessionKey,\n error: stderr.slice(0, 200),\n })\n claudeSessions.delete(sessionKey)\n } else {\n log.debug(\"ignoring session ID error from stale claude process\", {\n sessionKey,\n })\n }\n }\n })\n\n return ap\n}\n\n/**\n * Append `--resume <id>` to an already-built args vector when a Claude\n * conversation id is known for the session and the args don't already carry\n * a session flag. Used by `respawnActiveProcess` to resume the conversation\n * in a fresh child without rebuilding the whole (version-gated) args vector.\n * `--resume`, not `--session-id`: the latter means \"create a NEW session\n * with this UUID\" and the CLI rejects it with \"Session ID ... is already in\n * use\" whenever a transcript exists on disk — which is exactly the state a\n * mid-conversation respawn is in. If the wedged child died before writing\n * any transcript, `--resume` fails with \"No conversation found with session\n * ID\", which the stderr recovery matcher already catches (fresh-session\n * fallback).\n */\nexport function appendResumeIfNeeded(\n sessionKey: string,\n cliArgs: string[],\n): string[] {\n if (cliArgs.includes(\"--resume\") || cliArgs.includes(\"--session-id\")) {\n return cliArgs\n }\n const sid = claudeSessions.get(sessionKey)\n if (!sid) return cliArgs\n return [...cliArgs, \"--resume\", sid]\n}\n\n/**\n * Replace a wedged reused process with a fresh one, resuming the same\n * Claude conversation. Used by the doStream start-watchdog when a reused\n * process produces no stdout within a grace window after a fresh-turn\n * envelope write — observed after a very long proxy-blocked tool call\n * (e.g. a multi-minute `task` subagent). Before the per-tool proxy timeout\n * fix this was masked because the flat 10-minute ceiling ended the turn\n * first; now that the task proxy blocks and returns successfully, resuming\n * a reused child after such a long wait can leave it silent on stdout.\n *\n * Reuses the existing proxy server, system-prompt file, and MCP hash (their\n * handles are already baked into `cliArgs`' `--mcp-config`/append-prompt\n * paths), so this only swaps the child process. The old child's exit\n * handler is silenced before kill so it doesn't close the proxy server we\n * are reusing; the new child gets its own exit handler from\n * `spawnClaudeProcess`. `claudeSessions` is left intact so the respawn can\n * add `--resume` (see `appendResumeIfNeeded`).\n *\n * The respawn happens in the middle of the same logical turn, and the caller\n * re-sends that turn's envelope at once. Turn state lives on the\n * `ActiveProcess`, so the replacement inherits the old process's in-flight\n * marker (@broskees' b719497); without that handoff abort, LRU eviction, the\n * idle timer and the next turn's quiesce all mistake the busy replacement\n * for an idle process.\n *\n * Returns the new `ActiveProcess`, or `undefined` if there was no active\n * process for the key (caller should treat that as \"nothing to respawn\").\n */\nexport function respawnActiveProcess(\n sessionKey: string,\n cliPath: string,\n cliArgs: string[],\n cwd: string,\n ignoreAnthropicApiKey?: boolean,\n): ActiveProcess | undefined {\n const old = activeProcesses.get(sessionKey)\n if (!old) return undefined\n const turnWasInFlight = isTurnInFlight(old)\n activeProcesses.delete(sessionKey)\n // Silence the old exit handler so it doesn't close the proxy server,\n // unlink the system-prompt file, or touch claudeSessions on its way out\n // — those handles are reused by the new child. spawnClaudeProcess wires\n // a fresh exit handler for the respawned child.\n old.proc.removeAllListeners(\"exit\")\n try {\n old.proc.kill()\n } catch {}\n const replacement = spawnClaudeProcess(\n cliPath,\n appendResumeIfNeeded(sessionKey, old.cliArgs ?? cliArgs),\n cwd,\n sessionKey,\n old.proxyServer,\n old.mcpHash,\n old.systemPromptFile,\n ignoreAnthropicApiKey,\n old.effort,\n )\n replacement.pendingProxyCompletions = old.pendingProxyCompletions\n delete old.pendingProxyCompletions\n if (turnWasInFlight) noteTurnStarted(replacement)\n return replacement\n}\n\nexport function buildCliArgs(opts: {\n sessionKey: string\n skipPermissions: boolean\n includeSessionId?: boolean\n model?: string\n permissionMode?: string\n mcpConfig?: string | string[]\n strictMcpConfig?: boolean\n disallowedTools?: string[]\n appendSystemPromptFile?: string\n /** `--plugin-dir` values (skill bridge), one flag per directory. */\n pluginDirs?: string[]\n thinking?: \"enabled\" | \"disabled\"\n thinkingDisplay?: \"summarized\" | \"omitted\"\n fastMode?: boolean\n cliVersion?: CliVersion | null\n}): string[] {\n const {\n sessionKey,\n skipPermissions,\n includeSessionId = true,\n model,\n permissionMode,\n mcpConfig,\n strictMcpConfig,\n disallowedTools,\n appendSystemPromptFile,\n pluginDirs,\n thinking,\n thinkingDisplay,\n fastMode,\n cliVersion,\n } = opts\n const args = [\n \"--print\",\n \"--output-format\",\n \"stream-json\",\n \"--input-format\",\n \"stream-json\",\n \"--include-partial-messages\",\n \"--verbose\",\n ]\n\n if (model) {\n args.push(\"--model\", model)\n }\n\n if (permissionMode) {\n args.push(\"--permission-mode\", permissionMode)\n }\n\n // `--session-id` means \"create a NEW session with this UUID\" and the CLI\n // exits with \"Session ID ... is already in use\" whenever a transcript for\n // that ID already exists on disk. Continuing an existing session requires\n // `--resume` (which keeps the same session ID in print mode).\n if (includeSessionId) {\n const sessionId = claudeSessions.get(sessionKey)\n if (sessionId && !activeProcesses.has(sessionKey)) {\n args.push(\"--resume\", sessionId)\n }\n }\n\n if (mcpConfig) {\n const configs = Array.isArray(mcpConfig) ? mcpConfig : [mcpConfig]\n const filtered = configs.filter((c) => typeof c === \"string\" && c.length > 0)\n if (filtered.length > 0) {\n args.push(\"--mcp-config\", ...filtered)\n }\n }\n\n if (strictMcpConfig) {\n args.push(\"--strict-mcp-config\")\n }\n\n if (disallowedTools && disallowedTools.length > 0) {\n args.push(\"--disallowedTools\", ...disallowedTools)\n }\n\n // `--thinking` is only present from Claude Code 2.x onward; gate so\n // pre-2.x binaries don't crash with a parse error. Unknown version →\n // skip (the spawn still works, the user just doesn't get extended\n // thinking until they upgrade).\n if (thinking && cliSupportsThinking(cliVersion ?? null)) {\n args.push(\"--thinking\", thinking)\n }\n\n // `--thinking-display` was added in Claude Code 2.1.142. Older CLIs\n // reject it with a parse error, so gate on detected version. When\n // version is unknown (detection failed), be conservative and skip.\n if (thinkingDisplay && cliSupportsThinkingDisplay(cliVersion ?? null)) {\n args.push(\"--thinking-display\", thinkingDisplay)\n }\n\n if (appendSystemPromptFile) {\n args.push(\"--append-system-prompt-file\", appendSystemPromptFile)\n }\n for (const dir of pluginDirs ?? []) {\n args.push(\"--plugin-dir\", dir)\n }\n\n // Fast mode's only headless opt-in. `--settings` feeds the CLI's\n // `flagSettings` layer, which is the one its SDK gate checks; a `fastMode`\n // in the user's own settings.json is NOT enough for a `--print` run.\n // Built as one object so later flag-settings keys merge here instead of\n // adding a second `--settings` (the CLI takes the flag once).\n if (fastMode && cliSupportsFastMode(cliVersion ?? null)) {\n args.push(\"--settings\", JSON.stringify({ fastMode: true }))\n }\n\n // Plan mode is a capability restriction, not a prompt policy, and the CLI\n // lets `--dangerously-skip-permissions` override it outright: measured on\n // 2.1.258, a plan-mode run carrying both flags wrote a file on request\n // without prompting, while the same run without the skip flag refused and\n // created nothing. Since `skipPermissions` defaults to true, passing both\n // is the common case, so anyone asking for plan mode was silently getting\n // full write access. Plan mode must never permit edits, so it wins here.\n // Every other `permissionMode` value governs prompting, which is exactly\n // what the skip flag is for, so those still pass both.\n if (skipPermissions && permissionMode !== \"plan\") {\n args.push(\"--dangerously-skip-permissions\")\n }\n\n return args\n}\n\n/**\n * Build a session key that includes both cwd and model,\n * so different models get separate processes.\n */\nexport function sessionKey(cwd: string, modelId: string): string {\n return `${cwd}::${modelId}`\n}\n\n/**\n * Pull the readable parts back out of a session key for the doctor report.\n * The key is `<cwd>::<model>::<scope>::<affinity>::context=[...]` with an\n * optional `::effort=<level>` tail, and the compaction variant is\n * `<cwd>::<model>::compaction::<affinity>`, so the model and the opencode\n * session id sit at the same two positions either way.\n */\nexport function describeSessionKey(key: string): {\n cwd: string\n model: string\n session: string\n compaction: boolean\n} {\n const parts = key.split(\"::\")\n return {\n cwd: parts[0] ?? \"unknown\",\n model: parts[1] ?? \"unknown\",\n session: parts[3] ?? \"unknown\",\n compaction: parts[2] === \"compaction\",\n }\n}\n\n/** One live `claude` child, flattened for `/claude-code-doctor`. */\nexport interface ActiveProcessSnapshot {\n sessionKey: string\n session: string\n model: string\n compaction: boolean\n pid?: number\n inFlight: boolean\n ageMs?: number\n effort?: ReasoningEffort\n attached: boolean\n proxyUrl?: string\n /**\n * Tail of the child's stderr, when something upstream of this module is\n * recording one. Read through an optional property so the doctor works\n * whether or not that field exists on the running build.\n */\n lastStderr?: string\n}\n\n/**\n * Every live child, oldest-used first (the map is LRU). Read-only; nothing\n * here touches eviction or the process's own listeners.\n */\nexport function snapshotActiveProcesses(now = Date.now()): ActiveProcessSnapshot[] {\n const out: ActiveProcessSnapshot[] = []\n for (const [key, ap] of activeProcesses) {\n const described = describeSessionKey(key)\n const lastStderr = (ap as { lastStderr?: unknown }).lastStderr\n out.push({\n sessionKey: key,\n session: ap.opencodeSessionID ?? described.session,\n model: described.model,\n compaction: described.compaction,\n pid: ap.proc.pid,\n inFlight: ap.turnInFlight === true,\n ageMs: ap.startedAt === undefined ? undefined : Math.max(0, now - ap.startedAt),\n effort: ap.effort,\n attached: ap.lineEmitter.listenerCount(\"line\") > 0,\n proxyUrl: ap.proxyServer?.url,\n lastStderr: typeof lastStderr === \"string\" ? lastStderr : undefined,\n })\n }\n return out\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from \"node:http\"\nimport type { AddressInfo } from \"node:net\"\nimport * as fs from \"node:fs\"\nimport * as path from \"node:path\"\nimport * as crypto from \"node:crypto\"\nimport { EventEmitter } from \"node:events\"\nimport { log } from \"./logger.js\"\nimport { pluginTmpDir } from \"./tmp.js\"\n\n/**\n * Minimal MCP HTTP server embedded in-process. Exposes a set of \"proxy\"\n * tools (Bash, Edit, Write, etc.) that Claude CLI calls when its built-in\n * equivalents are disabled via --disallowedTools. Our handler blocks until\n * an external broker resolves the call, then responds to Claude.\n *\n * Wire protocol: JSON-RPC 2.0 over plain HTTP POST to `/mcp`. Protocol\n * methods (`initialize`, `tools/list`) and calls answered in-process get a\n * single JSON reply. A broker-backed `tools/call` can block for as long as\n * opencode takes to run the tool, so its reply is streamed: SSE when the\n * client accepts it, otherwise a chunked JSON body whose headers go out at\n * once and which carries keepalive whitespace until the result is ready\n * (see `openEventStream` / `openJsonStream`).\n */\n\nexport interface ProxyMcpServer {\n url: string\n serverName: string\n tools: ProxyToolDef[]\n /** Per-server bearer secret. Minted on start, handed to Claude via the\n * `headers` block of the generated MCP config, and required on every\n * request. Exposed so callers (and tests) can authenticate; MUST NOT be\n * logged or placed in the URL. */\n authToken: string\n /** Fires when Claude invokes one of our proxy tools. The handler resolves\n * the returned pending call once a result is available. */\n calls: EventEmitter\n /** Write `--mcp-config <path>`-compatible scratch file and return its path. */\n configPath(): string\n /**\n * Ids of the `tools/call` requests this server is still holding open.\n * Read-only. An entry leaves this list only when its promise settles, so\n * it is the direct evidence that a lifecycle event released the HTTP side\n * of a call and not just the broker's entry for it.\n */\n pendingCallIds(): string[]\n close(): Promise<void>\n}\n\nexport interface ProxyToolDef {\n /** Raw name as seen by Claude once proxied: the MCP exposed tool name. */\n name: string\n description: string\n inputSchema: Record<string, unknown>\n}\n\n/**\n * Liveness of the HTTP reply channel behind one proxy call. Shared by\n * reference between proxy-mcp (which flips `closed` when Claude's request\n * goes away) and the broker / language model (which read it before\n * answering), so the two never need to import each other.\n */\nexport interface ProxyCallChannel {\n closed: boolean\n}\n\nexport interface ProxyToolCall {\n id: string\n toolName: string\n input: Record<string, unknown>\n resolve: (result: ProxyToolResult) => void\n reject: (err: Error) => void\n /** Absent for calls built by hand in tests; treated as open. */\n channel?: ProxyCallChannel\n}\n\n/**\n * Keep unanswered HTTP calls active independently of the tool deadline.\n * A held call timed out before delivery on CLI 2.1.258; with immediate\n * headers and these comments, the same 390-second hold completed. The same\n * cadence drives the whitespace keepalive of a JSON-only reply: both must\n * stay well under the ~300 s header/body timers in the CLI's HTTP client.\n */\nexport const PROXY_KEEPALIVE_MS = 15_000\n\n/** True when the client advertised `text/event-stream` in Accept. */\nexport function acceptsEventStream(acceptHeader: unknown): boolean {\n return (\n typeof acceptHeader === \"string\" &&\n acceptHeader.toLowerCase().includes(\"text/event-stream\")\n )\n}\n\nexport type ProxyToolResult =\n | { kind: \"text\"; text: string; isError?: boolean }\n | { kind: \"error\"; message: string }\n\n/**\n * Handler that answers a `tools/call` inside this process instead of\n * queueing it for opencode. Used by tools that act on plugin state rather\n * than on the workspace (currently only `compress`), so they never reach\n * the broker, never block on a human, and have no deadline.\n */\nexport type ProxyToolInterceptor = (\n input: Record<string, unknown>,\n) => Promise<ProxyToolResult> | ProxyToolResult\n\nexport const SERVER_CLOSED_MESSAGE = \"proxy MCP server closed\"\n\n/** Rejections that fire on normal lifecycle transitions: AFK-permission\n * timeouts, orphan rejections at turn boundaries, stream aborts, and server\n * close while its owning Claude process exits or is replaced. None are\n * user-actionable — file-log them at NOTICE. Anything else stays WARN so\n * genuine bugs remain visible in the TUI. */\nexport function isExpectedCleanupError(message: string): boolean {\n return (\n (message.includes(\"timed out after\") &&\n message.includes(\"waiting for opencode to resolve\")) ||\n message.includes(\"rejecting as orphaned\") ||\n message.includes(\"was orphaned by a new user turn\") ||\n message.includes(\"stream was aborted\") ||\n message.includes(SERVER_CLOSED_MESSAGE)\n )\n}\n\nconst PROTOCOL_VERSION = \"2024-11-05\"\nconst SERVER_NAME = \"opencode_proxy\"\nexport const PROXY_TOOL_PREFIX = `mcp__${SERVER_NAME}__`\n\n// Flat fallback cap on how long a proxy tool call may wait for opencode to\n// resolve it. Matches Claude CLI's hard upper bound for Bash (10 min). The\n// effective deadline is resolved per tool — see `resolveProxyCallTimeoutMs`.\nexport const PROXY_DEFAULT_TIMEOUT_MS = 10 * 60 * 1000\n\n/** A resolved deadline of 0 means the call waits until a lifecycle event\n * releases it: a result, an abort, the next user turn's orphan sweep, the\n * child closing, or the proxy server closing with its process. */\nexport const PROXY_NO_DEADLINE_MS = 0\n\n// Per-tool default deadlines, keyed by lowercase proxy tool name. `task` and\n// `task_batch` dispatch opencode subagents, and the wall clock is the wrong\n// unit for those: a 10-min flat ceiling fired mid-subagent and dropped the\n// late result on the floor (@jknlsn, live session ses_0cfc0da6, 2026-07-05),\n// and a 60-min one did the same to any subagent that ran longer (@broskees'\n// dd494a8). So they carry no deadline at all: an abandoned task call is\n// released by the same lifecycle events that already release every other\n// call, and a positive `proxyToolTimeoutMs` override restores a backstop.\n//\n// `question` blocks on a human reading a TUI form, so the flat ceiling is\n// the wrong unit entirely: a question posed just before the operator steps\n// away would be rejected mid-answer. 30 min is jknlsn's original figure and\n// matches the \"prefer fewer, high-signal questions\" guidance in the def.\nexport const PROXY_PER_TOOL_DEFAULT_TIMEOUT_MS: Record<string, number> = {\n task: PROXY_NO_DEADLINE_MS,\n task_batch: PROXY_NO_DEADLINE_MS, // same reasoning: it IS task calls\n question: 30 * 60 * 1000, // 30 min\n}\n\n// Node's setTimeout delay is a signed 32-bit int; values above 2^31-1 ms\n// (~24.85 days) trigger TimeoutOverflowWarning and fire at ~1ms instead.\n// Clamp absurd overrides / input.timeouts so a misconfigured deadline\n// can't collapse to \"fires immediately\".\nexport const MAX_PROXY_TIMEOUT_MS = 2 ** 31 - 1\n\n/**\n * Resolve the proxy deadline for a tool call. Layers, most-specific last:\n * 1. flat default (`PROXY_DEFAULT_TIMEOUT_MS`, 10 min)\n * 2. per-tool default (`PROXY_PER_TOOL_DEFAULT_TIMEOUT_MS`; `task` and\n * `task_batch` have none)\n * 3. user override via `proxyToolTimeoutMs` config (case-insensitive key).\n * A positive value replaces the default, `0` disables the deadline for\n * that tool, and a negative or non-finite value is ignored.\n * 4. for `bash`, the call's own `input.timeout` -- the proxy must never\n * undercut a build the caller explicitly asked to run long. The bash\n * proxy def advertises a `timeout` field; before this fix the proxy\n * ignored it and killed the call at the flat ceiling anyway. It only\n * ever raises, so it also turns a disabled bash deadline back into one.\n * Finally clamped to `MAX_PROXY_TIMEOUT_MS` to stay within Node's timer range.\n * Returns `PROXY_NO_DEADLINE_MS` (0) when the call has no deadline; callers\n * must not arm a timer for that value.\n */\nexport function resolveProxyCallTimeoutMs(\n toolName: string,\n input: Record<string, unknown> | undefined,\n overrides: Record<string, number> | undefined,\n): number {\n const key = toolName.toLowerCase()\n let ms = PROXY_PER_TOOL_DEFAULT_TIMEOUT_MS[key] ?? PROXY_DEFAULT_TIMEOUT_MS\n if (overrides) {\n const ov = lookupCaseInsensitive(overrides, key)\n if (isDeadlineOverride(ov)) ms = ov\n }\n if (key === \"bash\") {\n const requested = input?.timeout\n if (typeof requested === \"number\" && requested > ms) ms = requested\n }\n return Math.min(ms, MAX_PROXY_TIMEOUT_MS)\n}\n\n/** `0` (no deadline) or a positive finite number of milliseconds. */\nfunction isDeadlineOverride(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0\n}\n\nfunction lookupCaseInsensitive(\n map: Record<string, number>,\n key: string,\n): number | undefined {\n if (Object.prototype.hasOwnProperty.call(map, key)) return map[key]\n for (const k of Object.keys(map)) {\n if (k.toLowerCase() === key) return map[k]\n }\n return undefined\n}\n\n/**\n * Client-side abort ceiling written into Claude's `--mcp-config` entry for\n * the proxy server. Without a `timeout` there, Claude CLI's remote-HTTP MCP\n * client aborts each call at its 60-second default even while an opencode\n * subagent is still running (@broskees, PR #18). It must be >= the largest\n * server-side deadline or the client gives up before the broker does, so it\n * tracks the max of every tool's effective deadline: the flat default, the\n * per-tool defaults, and the user's overrides applied on top of them. A tool\n * with no deadline needs the largest value the client accepts, because the\n * CLI rejects `timeout: 0` in the MCP config outright (measured on the fork\n * this came from, @broskees' dd494a8), and this is also Node's timer max.\n * (A bash call raising its own `input.timeout` above this ceiling is a known\n * edge; Claude CLI caps bash at 10 min anyway.)\n */\nexport function resolveProxyClientCeilingMs(\n overrides: Record<string, number> | undefined,\n): number {\n let ms = PROXY_DEFAULT_TIMEOUT_MS\n const consider = (deadlineMs: number): boolean => {\n if (deadlineMs === PROXY_NO_DEADLINE_MS) return true\n if (deadlineMs > ms) ms = deadlineMs\n return false\n }\n for (const [toolName, defaultMs] of Object.entries(PROXY_PER_TOOL_DEFAULT_TIMEOUT_MS)) {\n const override = overrides ? lookupCaseInsensitive(overrides, toolName) : undefined\n if (consider(isDeadlineOverride(override) ? override : defaultMs)) {\n return MAX_PROXY_TIMEOUT_MS\n }\n }\n for (const value of Object.values(overrides ?? {})) {\n if (isDeadlineOverride(value) && consider(value)) return MAX_PROXY_TIMEOUT_MS\n }\n return Math.min(ms, MAX_PROXY_TIMEOUT_MS)\n}\n\n/**\n * Asked when a call's deadline passes. `true` means opencode is still serving\n * the call, so it keeps waiting and is asked about again later.\n *\n * The deadline counted time the operator spent on opencode's permission\n * prompt, and that is where nearly every deadline went: measured 2026-09-23,\n * three proxied `bash` calls sat on an `external_directory` prompt for 24, 34\n * and 10.5 minutes (opencode's own part timings) and each was rejected at 10.\n * Claude then moved on to a new call, and the approval, once given, arrived as\n * a late result that the next turn's orphan sweep read as a new message: the\n * new call was cancelled and Claude was told the user had refused it. opencode\n * reports the session `busy` for as long as a prompt is open (measured on\n * 1.18.32), which is the signal the language model's guard reads.\n *\n * Registered by the language model; with no guard every deadline is final, as\n * it always was, so a bare proxy server and the offline tests are unchanged.\n */\nexport type ProxyDeadlineGuard = (call: { callId: string; toolName: string }) => Promise<boolean>\n\nlet proxyDeadlineGuard: ProxyDeadlineGuard | null = null\n\nexport function setProxyDeadlineGuard(guard: ProxyDeadlineGuard | null): void {\n proxyDeadlineGuard = guard\n}\n\n/** How often a call kept past its deadline is looked at again. */\nexport const PROXY_DEADLINE_RECHECK_MS = 60_000\n\nlet deadlineRecheckMs = PROXY_DEADLINE_RECHECK_MS\n\n/** Test seam: `null` restores the default. */\nexport function _setProxyDeadlineRecheckMs(ms: number | null): void {\n deadlineRecheckMs = ms ?? PROXY_DEADLINE_RECHECK_MS\n}\n\nexport interface ProxyDeadline {\n cancel(): void\n}\n\n/**\n * One call's deadline, shared by the HTTP handler and the broker so the two\n * layers make the same decision. `onExpire` runs only once the guard says\n * opencode is no longer serving the call (or there is no guard). A guard that\n * throws counts as \"not serving\", so a failed lookup can only end a call the\n * way it always ended, never hold one open.\n */\nexport function armProxyDeadline(opts: {\n callId: string\n toolName: string\n deadlineMs: number\n onExpire: () => void\n /** Only one layer logs the extension, so a call does not report twice. */\n logExtension?: boolean\n /** Test seam. */\n recheckMs?: number\n}): ProxyDeadline {\n let cancelled = false\n let extended = false\n let timer: ReturnType<typeof setTimeout> | null = null\n\n const check = async (): Promise<void> => {\n timer = null\n if (cancelled) return\n let keep = false\n const guard = proxyDeadlineGuard\n if (guard) {\n try {\n keep = await guard({ callId: opts.callId, toolName: opts.toolName })\n } catch {\n keep = false\n }\n }\n if (cancelled) return\n if (keep) {\n if (!extended && opts.logExtension) {\n log.warn(\"proxy call past its deadline, but opencode is still serving it; waiting\", {\n callId: opts.callId,\n toolName: opts.toolName,\n deadlineMs: opts.deadlineMs,\n note: \"usually a permission prompt in opencode; the call ends when it is answered, you abort, or the claude process goes\",\n })\n }\n extended = true\n timer = setTimeout(() => void check(), opts.recheckMs ?? deadlineRecheckMs)\n return\n }\n opts.onExpire()\n }\n\n timer = setTimeout(() => void check(), opts.deadlineMs)\n return {\n cancel() {\n cancelled = true\n if (timer) clearTimeout(timer)\n timer = null\n },\n }\n}\n\n/**\n * Build the timeout error surfaced to Claude. Keeps the substrings\n * `\"timed out after\"` and `\"waiting for opencode to resolve\"` that the\n * proxy-mcp catch block classifies as expected cleanup (notice, not warn).\n * For `task` we append guidance: a Task timeout means the subagent may\n * still be running but its result is now unreachable, and the model must\n * neither declare the dispatch failed nor \"schedule a wake-up\" -- that is a\n * Claude Code affordance which cannot fire in this headless/proxy context,\n * so deferring silently drops the work.\n */\nexport function buildProxyTimeoutError(toolName: string, ms: number): Error {\n const key = toolName.toLowerCase()\n const base = `Proxy tool '${toolName}' timed out after ${ms}ms waiting for opencode to resolve the call`\n if (key === \"task\" || key === TASK_BATCH_TOOL_NAME) {\n return new Error(\n base +\n (key === \"task\" ? \" (the subagent).\" : \" (the subagents).\") +\n \" The subagent may still be running but its result\" +\n \" is no longer reachable in this session. Do not declare the dispatch\" +\n \" failed, and do not 'schedule a wake-up' or defer -- that mechanism\" +\n \" does not apply here. If the result is required, re-dispatch or\" +\n \" verify it directly now.\",\n )\n }\n return new Error(base)\n}\n\n/**\n * Disambiguation appended to the `task` proxy def (both the static\n * fallback and the live overlay). Models routinely resolve opencode's\n * \"call the task tool with subagent: X\" mention hint to Claude Code's\n * native TaskCreate (a todo tool) — creating a todo, dispatching nothing,\n * and then narrating a successful dispatch. Others burn turns grepping\n * config files to verify a subagent exists before daring to call it.\n * Both failure modes are addressed here, at the tool the model reads.\n */\nexport const TASK_PROXY_NOTE =\n \"This and task_batch are the ONLY tools that dispatch opencode subagents\" +\n \" (including user @-mentions). Claude Code's built-in TaskCreate/TaskUpdate\" +\n \" manage a local todo list and cannot dispatch subagents. Do not search\" +\n \" config files to verify a subagent type exists: invalid types fail fast\" +\n \" with a clear error. Foreground calls block until the subagent finishes;\" +\n \" set `background` to request opencode's background execution mode. For\" +\n \" two or more independent subagents in one response use task_batch, not\" +\n \" several task calls: those run one after another. Task calls have no\" +\n \" proxy deadline by default: the call waits for the subagent to finish\" +\n \" (a positive proxyToolTimeoutMs override adds a deadline).\"\n\n/**\n * `task_batch`: one MCP call that opencode runs as N parallel `task` calls.\n *\n * Design and first implementation by Joseph Roberts (@broskees) on his fork\n * (68ed142), absorbed here with credit. The limitation it works around is\n * measured, not assumed: Claude Code emits several `mcp__opencode_proxy__*`\n * tool_use blocks in one assistant message but sends the MCP requests one at\n * a time, each only after the previous result (2026-09-06, haiku, two\n * 8-second bash calls: second request arrived 7 ms after the first resolved).\n * So \"call task twice\" is serial by construction, and the only way to get two\n * subagents running at once is a single proxy call that the plugin fans out\n * inside one opencode tool boundary, where opencode executes tool calls\n * concurrently. The children are ordinary `task` calls with ids derived from\n * the parent (`taskBatchChildToolCallId`), and their results are gathered\n * back onto the parent id (`formatTaskBatchResults`) before the CLI sees it.\n */\nexport const TASK_BATCH_TOOL_NAME = \"task_batch\"\n\nexport const TASK_BATCH_PROXY_NOTE =\n \"Use this instead of several task calls in one response: Claude Code runs\" +\n \" MCP tool calls one at a time, so separate task calls run serially even\" +\n \" when emitted together, while one task_batch call fans them out as\" +\n \" parallel opencode task calls. Each task takes the same fields as the\" +\n \" task tool. Results come back in task order, each labelled. Like task it\" +\n \" has no proxy deadline by default (a positive proxyToolTimeoutMs override\" +\n \" adds one).\"\n\nexport const TASK_INPUT_REQUIRED = [\"description\", \"prompt\", \"subagent_type\"]\n\n/** Why a `task_batch` input is unusable, or null when it is fine. */\nexport function taskBatchInputError(input: Record<string, unknown> | undefined): string | null {\n const tasks = input?.tasks\n if (!Array.isArray(tasks) || tasks.length < 2) {\n return \"task_batch requires a `tasks` array with at least two items; use `task` for one subagent\"\n }\n for (const [index, task] of tasks.entries()) {\n if (task === null || typeof task !== \"object\" || Array.isArray(task)) {\n return `task_batch tasks[${index}] must be an object`\n }\n const item = task as Record<string, unknown>\n for (const field of TASK_INPUT_REQUIRED) {\n if (typeof item[field] !== \"string\") {\n return `task_batch tasks[${index}].${field} must be a string`\n }\n }\n }\n return null\n}\n\n/** The batch's task inputs, or [] when the input never passed validation. */\nexport function taskBatchTasks(input: Record<string, unknown> | undefined): Record<string, unknown>[] {\n if (taskBatchInputError(input)) return []\n return input!.tasks as Record<string, unknown>[]\n}\n\n/**\n * Child ids stay derivable from the parent so the next turn can find every\n * child's `tool-result` without extra state. Only `[A-Za-z0-9_-]`: AI SDK\n * bridges normalise other characters and the round trip would not match.\n */\nexport function taskBatchChildToolCallId(parentToolCallId: string, index: number): string {\n return `${parentToolCallId}_task_${index}`\n}\n\n/**\n * One readable result for the parent call. Children are labelled in task\n * order; a child opencode did not answer is said so rather than dropped,\n * since a silent gap would read as a subagent that never ran.\n */\nexport function formatTaskBatchResults(\n children: Array<{ task: Record<string, unknown>; result: ProxyToolResult | null }>,\n): ProxyToolResult {\n const total = children.length\n const sections = children.map(({ task, result }, index) => {\n const label = typeof task.description === \"string\" ? task.description : `task ${index + 1}`\n const agent = typeof task.subagent_type === \"string\" ? ` (${task.subagent_type})` : \"\"\n const header = `## task ${index + 1} of ${total}: ${label}${agent}`\n if (!result) return `${header}\\n[missing] opencode returned no result for this task in the batch`\n if (result.kind === \"error\") return `${header}\\n[error] ${result.message}`\n return `${header}\\n${result.isError ? \"[error] \" : \"\"}${result.text}`\n })\n const failed = children.some(({ result }) => !result || result.kind === \"error\" || result.isError)\n return { kind: \"text\", text: sections.join(\"\\n\\n\"), ...(failed ? { isError: true } : {}) }\n}\n\nconst AGENT_TYPES_HEADING = \"Available agent types\"\n\n/** Longest per-agent blurb we keep; enough to choose, short enough to survive. */\nconst AGENT_BLURB_LIMIT = 140\n\n/**\n * Disambiguation appended to the `question` proxy def. Claude Code ships\n * a built-in `AskUserQuestion` that, when proxied, is disabled via\n * `--disallowedTools`; without an explicit hand-off note models keep\n * reaching for the disabled built-in or fall back to plain text. This\n * states that the proxy is the structured-questions path and summarises\n * the answer shape so the model can act on the result without a second\n * round-trip.\n */\nexport const QUESTION_PROXY_NOTE =\n \"This routes structured questions through opencode's native `question`\" +\n \" tool, which renders a TUI form with the options you provide and\" +\n \" blocks until the operator answers. Claude Code's built-in\" +\n \" AskUserQuestion is disabled in this environment; this proxy is the\" +\n \" ONLY way to ask the operator for a decision or clarification.\" +\n \" Answers come back as arrays of selected labels (set `multiple: true`\" +\n \" to allow more than one). If the operator dismisses the form the call\" +\n \" returns an error — treat that as 'no answer' and stop, do not guess.\" +\n \" Question calls get a 30-minute proxy deadline by default (configurable\" +\n \" via proxyToolTimeoutMs); for long-AFK scenarios prefer fewer,\" +\n \" high-signal questions.\"\n\n/**\n * Disambiguation appended to the `compress` proxy def. Two things the\n * model gets wrong without it: when the reset happens (not mid-turn, so\n * it can keep working after the call), and how much survives it (only\n * the summary, because the fresh spawn is not given the prior transcript).\n */\nexport const COMPRESS_PROXY_NOTE =\n \"The current turn continues normally after this call — finish what you\" +\n \" are doing. The reset happens at the START of the next turn: the\" +\n \" Claude Code session is discarded and a fresh one begins with your\" +\n \" summary as its only prior context. Everything else, including tool\" +\n \" output and files you read, is gone, so write the summary as the\" +\n \" authoritative record. Call this once per compression, when older\" +\n \" resolved work no longer needs full detail.\"\n\n/**\n * Pull *only* the agent-type list out of opencode's live `task` description.\n *\n * jknlsn's original overlaid the whole live description (2.8 KB here) in front\n * of the static def. Live check 2026-07-26 showed that backfires: Claude Code\n * truncates long MCP tool descriptions, and opencode puts the agent list at\n * the *end* (char 2306 of 2858), so the one part the model needs is exactly\n * what gets cut — haiku then guessed `general-purpose`, `default`, and\n * `code-reviewer` (Claude Code's own agent names) and every dispatch failed\n * with \"Unknown agent type\". So: keep the list, drop opencode's preamble\n * (generic delegation advice the model already has), trim each blurb, and let\n * the caller put it first.\n *\n * Returns undefined when the description carries no parsable list, so callers\n * leave the static def alone.\n */\nexport function extractAgentTypeList(\n liveDescription: string | undefined,\n): string | undefined {\n const live = liveDescription?.trim()\n if (!live) return undefined\n const start = live.indexOf(AGENT_TYPES_HEADING)\n if (start === -1) return undefined\n const entries: string[] = []\n for (const raw of live.slice(start).split(\"\\n\")) {\n const match = /^-\\s*([^:]+):\\s*(.+)$/.exec(raw.trim())\n if (!match) continue\n const name = match[1].trim()\n const blurb = match[2].trim()\n entries.push(\n `- ${name}: ${\n blurb.length > AGENT_BLURB_LIMIT\n ? `${blurb.slice(0, AGENT_BLURB_LIMIT).trimEnd()}…`\n : blurb\n }`,\n )\n }\n if (entries.length === 0) return undefined\n return `Valid subagent_type values, from opencode's live registry — anything else fails:\\n${entries.join(\"\\n\")}`\n}\n\n/**\n * Front-load opencode's live agent-type list onto the static `task` proxy def\n * so the model picks a real `subagent_type` instead of guessing a Claude Code\n * name. First, not last: see `extractAgentTypeList` for why position matters.\n * No-op when no list can be extracted (SDK client missing, older opencode) or\n * the `task` def is not among the tools.\n */\nexport function overlayTaskProxyDescription(\n tools: ProxyToolDef[],\n liveDescription: string | undefined,\n): ProxyToolDef[] {\n const agentTypes = extractAgentTypeList(liveDescription)\n if (!agentTypes) return tools\n return tools.map((t) =>\n t.name === \"task\" || t.name === TASK_BATCH_TOOL_NAME\n ? { ...t, description: `${agentTypes}\\n\\n${t.description}` }\n : t,\n )\n}\n\n/**\n * Overlay opencode's live `question` tool description onto the static\n * proxy def, then append the disambiguation note. No-op when the live\n * description is unavailable (older opencode, SDK client missing) — the\n * static def + note stands. Mirrors `overlayTaskProxyDescription`.\n */\nexport function overlayQuestionProxyDescription(\n tools: ProxyToolDef[],\n liveDescription: string | undefined,\n): ProxyToolDef[] {\n const live = liveDescription?.trim()\n if (!live) return tools\n return tools.map((t) =>\n t.name === \"question\"\n ? { ...t, description: `${live}\\n\\n${QUESTION_PROXY_NOTE}` }\n : t,\n )\n}\n\n/**\n * Version gate for the `question` proxy. opencode added a built-in\n * `question` tool (registry id `question`) — on older builds that entry\n * is absent and a forwarded `mcp__opencode_proxy__question` call would\n * resolve to `⚙ invalid` in opencode. Drop the def silently when the\n * live registry does not contain it so the model never sees a dead tool.\n */\nexport function filterQuestionProxyByOpencodeSupport(\n tools: ProxyToolDef[],\n opencodeHasQuestion: boolean,\n): ProxyToolDef[] {\n if (opencodeHasQuestion) return tools\n return tools.filter((t) => t.name !== \"question\")\n}\n\n/** Input fields of one `task`, shared with each `task_batch` item. */\nexport const TASK_INPUT_PROPERTIES = {\n description: {\n type: \"string\",\n description: \"A short (3-5 words) description of the task\",\n },\n prompt: {\n type: \"string\",\n description: \"The task for the agent to perform\",\n },\n subagent_type: {\n type: \"string\",\n description: \"The type of specialized agent to use for this task\",\n },\n task_id: {\n type: \"string\",\n description:\n \"Set this only if you mean to resume a previous task: pass the\" +\n \" prior task_id to continue the same subagent session instead of\" +\n \" creating a fresh one.\",\n },\n command: {\n type: \"string\",\n description: \"The command that triggered this task\",\n },\n background: {\n type: \"boolean\",\n description:\n \"Run the task in the background when supported by opencode\",\n },\n}\n\nexport const DEFAULT_PROXY_TOOLS: ProxyToolDef[] = [\n {\n name: \"bash\",\n description:\n \"Execute a shell command. Routed through opencode's bash tool so\" +\n \" permission prompts flow through opencode's UI.\",\n inputSchema: {\n type: \"object\",\n properties: {\n command: {\n type: \"string\",\n description: \"The shell command to execute.\",\n },\n description: {\n type: \"string\",\n description: \"Short human-readable description of what the command does.\",\n },\n timeout: {\n type: \"number\",\n description: \"Optional timeout in milliseconds.\",\n },\n },\n required: [\"command\"],\n },\n },\n {\n name: \"write\",\n description:\n \"Write a file. Routed through opencode's write tool so permission prompts flow through opencode's UI.\",\n inputSchema: {\n type: \"object\",\n properties: {\n filePath: {\n type: \"string\",\n description: \"The file to write. Absolute paths are preferred.\",\n },\n content: {\n type: \"string\",\n description: \"The full content to write to the file.\",\n },\n },\n required: [\"filePath\", \"content\"],\n },\n },\n {\n name: \"edit\",\n description:\n \"Replace text in an existing file. Routed through opencode's edit tool so permission prompts flow through opencode's UI.\",\n inputSchema: {\n type: \"object\",\n properties: {\n filePath: {\n type: \"string\",\n description: \"The file to edit. Absolute paths are preferred.\",\n },\n oldString: {\n type: \"string\",\n description: \"The exact text to replace.\",\n },\n newString: {\n type: \"string\",\n description: \"The replacement text.\",\n },\n replaceAll: {\n type: \"boolean\",\n description: \"Replace all occurrences instead of just the first one.\",\n },\n },\n required: [\"filePath\", \"oldString\", \"newString\"],\n },\n },\n {\n name: \"webfetch\",\n description:\n \"Fetch content from a URL. Routed through opencode's webfetch tool so\" +\n \" permission prompts flow through opencode's UI. Returns the page\" +\n \" content in the requested format.\",\n inputSchema: {\n type: \"object\",\n properties: {\n url: {\n type: \"string\",\n description: \"The URL to fetch content from. Must start with http:// or https://.\",\n },\n format: {\n type: \"string\",\n enum: [\"text\", \"markdown\", \"html\"],\n description:\n \"The format to return the content in. Defaults to markdown.\",\n },\n timeout: {\n type: \"number\",\n description: \"Optional timeout in seconds (max 120).\",\n },\n },\n required: [\"url\"],\n },\n },\n {\n name: \"task\",\n description:\n \"Launch an opencode subagent to handle a complex multi-step task\" +\n \" autonomously. Routed through opencode's task tool so subagent\" +\n \" orchestration, permission, and lifecycle are handled by opencode.\" +\n \" Use `subagent_type` to pick which configured subagent runs (e.g.\" +\n \" `build`, `general`, `explore`, or any custom subagent declared in\" +\n \" opencode.json). \" +\n TASK_PROXY_NOTE,\n inputSchema: {\n type: \"object\",\n properties: TASK_INPUT_PROPERTIES,\n required: TASK_INPUT_REQUIRED,\n },\n },\n {\n name: TASK_BATCH_TOOL_NAME,\n description:\n \"Launch two or more independent opencode subagents at the same time and\" +\n \" get all their results back together. Put one ordinary task input in\" +\n \" `tasks` for each subagent. \" +\n TASK_BATCH_PROXY_NOTE,\n inputSchema: {\n type: \"object\",\n properties: {\n tasks: {\n type: \"array\",\n minItems: 2,\n description: \"Independent subagent tasks to run concurrently\",\n items: {\n type: \"object\",\n properties: TASK_INPUT_PROPERTIES,\n required: TASK_INPUT_REQUIRED,\n },\n },\n },\n required: [\"tasks\"],\n },\n },\n {\n name: \"question\",\n description:\n \"Ask the operator structured questions with options and receive\" +\n \" their answers back. Routed through opencode's native `question`\" +\n \" tool so the prompt renders as a real TUI form (with options and a\" +\n \" custom-answer field) instead of a plain text turn. Use this when\" +\n \" you need a decision, clarification, or preference from the\" +\n \" operator mid-task. \" +\n QUESTION_PROXY_NOTE,\n inputSchema: {\n type: \"object\",\n properties: {\n questions: {\n type: \"array\",\n description: \"Questions to ask.\",\n items: {\n type: \"object\",\n properties: {\n question: {\n type: \"string\",\n description: \"Complete question.\",\n },\n header: {\n type: \"string\",\n description: \"Very short label (max 30 chars).\",\n },\n options: {\n type: \"array\",\n description: \"Available choices.\",\n items: {\n type: \"object\",\n properties: {\n label: {\n type: \"string\",\n description: \"Display text (1-5 words, concise).\",\n },\n description: {\n type: \"string\",\n description: \"Explanation of choice.\",\n },\n },\n required: [\"label\", \"description\"],\n },\n },\n multiple: {\n type: \"boolean\",\n description:\n \"Allow selecting multiple choices. Defaults to false.\",\n },\n },\n required: [\"question\", \"header\", \"options\"],\n },\n },\n },\n required: [\"questions\"],\n },\n },\n {\n name: \"compress\",\n description:\n \"Replace older conversation detail with a summary you write, then\" +\n \" continue in a fresh Claude Code session. Handled inside the plugin,\" +\n \" so it never prompts the operator. \" +\n COMPRESS_PROXY_NOTE,\n inputSchema: {\n type: \"object\",\n properties: {\n summary: {\n type: \"string\",\n description:\n \"Dense technical summary of the work being compressed: decisions\" +\n \" made, files changed, commands run and their outcomes, and what\" +\n \" is still open. This is the ONLY prior context that survives, so\" +\n \" anything omitted is lost.\",\n },\n },\n required: [\"summary\"],\n },\n },\n]\n\nexport async function createProxyMcpServer(\n tools: ProxyToolDef[] = DEFAULT_PROXY_TOOLS,\n timeoutOverrides?: Record<string, number>,\n interceptors?: Map<string, ProxyToolInterceptor>,\n options: {\n /** Keepalive cadence for streamed replies; a test seam, defaults to `PROXY_KEEPALIVE_MS`. */\n keepaliveMs?: number\n } = {},\n): Promise<ProxyMcpServer> {\n const calls = new EventEmitter()\n const pending = new Map<string, ProxyToolCall>()\n const keepaliveMs = options.keepaliveMs ?? PROXY_KEEPALIVE_MS\n\n // Per-server bearer secret (256 bits). This endpoint executes Bash/Edit/\n // Write through opencode's executor, so an unauthenticated caller on\n // loopback would have arbitrary command execution. The token lives only\n // in this process and in the 0600 MCP config file Claude reads; it is\n // deliberately kept out of the URL, because query strings leak into logs\n // and process listings.\n const authToken = crypto.randomBytes(32).toString(\"hex\")\n const expectedAuth = Buffer.from(`Bearer ${authToken}`)\n // The exact authority we hand to Claude. Set once the ephemeral port is\n // known; compared against the Host header to defeat DNS rebinding.\n let boundAuthority = \"\"\n\n function authOk(req: IncomingMessage): boolean {\n const got = req.headers.authorization\n if (typeof got !== \"string\") return false\n const candidate = Buffer.from(got)\n // timingSafeEqual throws on length mismatch, so length-check first.\n // Length is not secret (the token is fixed-width).\n if (candidate.length !== expectedAuth.length) return false\n return crypto.timingSafeEqual(candidate, expectedAuth)\n }\n\n /**\n * Reject a request without leaving the connection usable.\n *\n * Ending the response alone is not enough. A peer can declare a large\n * Content-Length, send a single byte, take the rejection, and leave the\n * request still arriving — and `server.close()` does not reap connections\n * that are still sending, so a shutdown would hang behind it. Node's\n * default whole-request timeout is five minutes, which is five minutes of\n * a socket held by an unauthenticated caller.\n *\n * `Connection: close` tells Node to close once the response is flushed;\n * destroying the socket on `finish` covers the case where the peer never\n * finishes its body.\n */\n function reject(\n req: IncomingMessage,\n res: ServerResponse,\n statusCode: number,\n reason: string,\n ): void {\n // Every guard below is a measured property of the client we spawn, not a\n // guarantee about future ones. If a later Claude CLI starts sending an\n // Origin header, or a different Content-Type, every proxy call would\n // 403/415 with no other symptom than tools mysteriously not working — so\n // say why, here, once per rejected request. Header VALUES are omitted:\n // this line must never carry the bearer token.\n log.notice(\"proxy-mcp rejected a request\", {\n statusCode,\n reason,\n method: req.method,\n hasAuthorization: typeof req.headers.authorization === \"string\",\n })\n res.statusCode = statusCode\n res.setHeader(\"Connection\", \"close\")\n res.on(\"finish\", () => {\n req.socket?.destroy()\n })\n res.end()\n }\n\n const server = createServer(async (req, res) => {\n if (req.method !== \"POST\" || !req.url?.startsWith(\"/mcp\")) {\n reject(req, res, 404, \"not a POST to /mcp\")\n return\n }\n // Everything below runs BEFORE readBody: an unauthenticated peer must\n // not be able to stream an unbounded body into memory.\n //\n // DNS rebinding: a browser rebound onto this port via an attacker\n // hostname sends that hostname in Host, never the loopback authority we\n // generated. This does NOT block a page posting directly to\n // 127.0.0.1:<port> — such a request carries exactly the expected Host —\n // so it is a rebinding defense specifically, not a browser defense. The\n // Origin and Content-Type guards below, and the token, cover that case.\n if (req.headers.host !== boundAuthority) {\n reject(req, res, 403, \"host header is not the bound authority\")\n return\n }\n // Claude Code 2.1.226 sends no Origin on MCP requests (verified). The MCP\n // transport spec obliges SERVERS to validate Origin; it does not oblige\n // clients to omit it, so this is a measured property of the client we\n // spawn rather than a guarantee about all conforming clients.\n if (req.headers.origin !== undefined) {\n reject(req, res, 403, \"origin header present\")\n return\n }\n // Requiring application/json forces a CORS preflight for cross-origin\n // callers (which then fails), closing the text/plain \"simple request\"\n // bypass that would otherwise allow blind cross-site POSTs.\n const contentType = String(req.headers[\"content-type\"] ?? \"\")\n .split(\";\")[0]\n .trim()\n .toLowerCase()\n if (contentType !== \"application/json\") {\n reject(req, res, 415, \"content-type is not application/json\")\n return\n }\n if (!authOk(req)) {\n reject(req, res, 401, \"missing or invalid bearer token\")\n return\n }\n // Hoist the request id and method so the catch block can echo them\n // in error responses. Without this, a broker rejection (timeout /\n // orphan) on a tools/call lands in the catch with no visible id, and\n // the response goes back with `id: null` which Claude CLI cannot\n // match to the original request. The method is also needed because\n // tools/call errors must be returned as MCP results with isError\n // (not JSON-RPC errors) or Claude CLI rejects them as a \"malformed\n // result that failed schema validation\" (seen live 2026-07-04).\n let requestId: number | string | null = null\n let requestMethod: string | null = null\n // Hoisted for the same reason: once a streamed reply's headers are out,\n // an error must travel down that stream instead of through writeJson\n // (which would try to set headers again and throw inside the catch).\n let reply: ReplyStream | null = null\n try {\n const body = await readBody(req)\n const request = JSON.parse(body) as {\n jsonrpc?: string\n id?: number | string | null\n method?: string\n params?: Record<string, unknown>\n }\n requestId = request?.id ?? null\n requestMethod = typeof request?.method === \"string\" ? request.method : null\n\n if (request?.jsonrpc !== \"2.0\" || typeof request.method !== \"string\") {\n writeJson(res, {\n jsonrpc: \"2.0\",\n id: requestId,\n error: { code: -32600, message: \"Invalid request\" },\n })\n return\n }\n\n log.debug(\"proxy-mcp request\", {\n method: request.method,\n id: request.id,\n })\n\n if (request.method === \"initialize\") {\n writeJson(res, {\n jsonrpc: \"2.0\",\n id: requestId,\n result: {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: { tools: {} },\n serverInfo: {\n name: SERVER_NAME,\n version: \"0.1.0\",\n },\n },\n })\n return\n }\n\n if (request.method === \"notifications/initialized\") {\n res.statusCode = 204\n res.end()\n return\n }\n\n if (request.method === \"tools/list\") {\n writeJson(res, {\n jsonrpc: \"2.0\",\n id: requestId,\n result: {\n tools: tools.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n },\n })\n return\n }\n\n if (request.method === \"tools/call\") {\n const params = request.params ?? {}\n const toolName = String(params.name ?? \"\")\n const input = (params.arguments ?? {}) as Record<string, unknown>\n\n if (!tools.some((t) => t.name === toolName)) {\n // tools/call failures MUST be MCP results with isError, never\n // JSON-RPC error envelopes: Claude CLI validates every tools/call\n // response against the MCP result schema and rejects JSON-RPC\n // errors as malformed (@jknlsn, seen live 2026-07-04).\n writeJson(res, {\n jsonrpc: \"2.0\",\n id: requestId,\n result: {\n content: [{ type: \"text\", text: `Unknown proxy tool: ${toolName}` }],\n isError: true,\n },\n })\n return\n }\n\n if (toolName === TASK_BATCH_TOOL_NAME) {\n const problem = taskBatchInputError(input)\n if (problem) {\n // Same rule as the unknown-tool path: an MCP result with isError,\n // never a JSON-RPC error envelope.\n writeToolCallResult(res, requestId, { kind: \"error\", message: problem })\n return\n }\n }\n\n // Intercepted tools act on plugin state, not on the workspace, so\n // they are answered here and never queued for opencode. The result\n // still goes through the shared MCP envelope below — a JSON-RPC\n // error here would be rejected by Claude CLI exactly like any other\n // tools/call error envelope.\n const interceptor = interceptors?.get(toolName)\n if (interceptor) {\n let intercepted: ProxyToolResult\n try {\n intercepted = await interceptor(input)\n } catch (interceptorError) {\n const message =\n interceptorError instanceof Error\n ? interceptorError.message\n : String(interceptorError)\n log.warn(\"proxy-mcp interceptor failed\", { toolName, error: message })\n intercepted = { kind: \"error\", message }\n }\n writeToolCallResult(res, requestId, intercepted)\n return\n }\n\n const callId = crypto.randomUUID()\n log.info(\"proxy-mcp tool call received\", {\n callId,\n toolName,\n hasInput: input != null,\n sse: acceptsEventStream(req.headers.accept),\n })\n\n // Broker-backed calls can block for as long as a subagent runs. The\n // reply is streamed either way so the client's own HTTP timers never\n // fire on a silent connection: SSE when the client accepts it\n // (headers and a comment now, keepalive comments, the JSON-RPC result\n // as the final event), otherwise a chunked JSON body whose headers go\n // out now and which carries keepalive whitespace until the result.\n // Every guard above has already run, so nothing is flushed for an\n // unauthenticated peer, an unknown tool, or a rejected batch.\n const channel: ProxyCallChannel = { closed: false }\n reply = acceptsEventStream(req.headers.accept)\n ? openEventStream(res, keepaliveMs)\n : openJsonStream(res, keepaliveMs)\n res.once(\"close\", () => {\n reply?.stop()\n if (res.writableFinished) return\n channel.closed = true\n log.notice(\"proxy-mcp client closed a tool call before its result\", {\n callId,\n toolName,\n })\n })\n\n let timer: ProxyDeadline | null = null\n const result = await new Promise<ProxyToolResult>(\n (resolve, reject) => {\n const entry: ProxyToolCall = {\n id: callId,\n toolName,\n input,\n resolve,\n reject,\n channel,\n }\n pending.set(callId, entry)\n const deadlineMs = resolveProxyCallTimeoutMs(\n toolName,\n input,\n timeoutOverrides,\n )\n // No deadline means no timer at all: `setTimeout(fn, 0)` would\n // reject the call on the next tick. The broker applies the same\n // rule to the same resolved value, so the two layers agree.\n if (deadlineMs > PROXY_NO_DEADLINE_MS) {\n // A permission prompt still open in opencode extends it; see\n // `ProxyDeadlineGuard`. The broker logs that, not this layer.\n timer = armProxyDeadline({\n callId,\n toolName,\n deadlineMs,\n onExpire: () => {\n if (!pending.has(callId)) return\n pending.delete(callId)\n log.notice(\"proxy-mcp tool call timed out\", {\n callId,\n toolName,\n deadlineMs,\n })\n reject(buildProxyTimeoutError(toolName, deadlineMs))\n },\n })\n }\n calls.emit(\"call\", entry)\n },\n ).finally(() => {\n timer?.cancel()\n pending.delete(callId)\n })\n\n if (channel.closed) {\n // Nobody is reading. The language model already saw the closed\n // channel and hands the result to Claude another way.\n log.notice(\"proxy-mcp dropping result for a closed tool call\", {\n callId,\n toolName,\n })\n return\n }\n writeToolCallResult(res, requestId, result, reply)\n return\n }\n\n writeJson(res, {\n jsonrpc: \"2.0\",\n id: requestId,\n error: { code: -32601, message: `Unknown method: ${request.method}` },\n })\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n const logFn = isExpectedCleanupError(errorMessage) ? log.notice : log.warn\n logFn(\"proxy-mcp error handling request\", {\n error: errorMessage,\n })\n // Broker rejections (timeouts, orphans, server close) surface here for\n // tools/call requests. Same rule as above: respond with an MCP result\n // carrying isError, never a JSON-RPC error envelope, or Claude CLI\n // rejects the response as schema-invalid.\n if (requestMethod === \"tools/call\") {\n try {\n writeToolCallResult(\n res,\n requestId,\n { kind: \"error\", message: errorMessage },\n reply,\n )\n } catch {\n try {\n res.statusCode = 500\n res.end()\n } catch {}\n }\n return\n }\n try {\n // tools/call already returned above with an MCP result; anything\n // reaching here is a protocol-level method (initialize, tools/list)\n // where a JSON-RPC error is the correct shape.\n writeJson(res, {\n jsonrpc: \"2.0\",\n id: requestId,\n error: {\n code: -32603,\n message: error instanceof Error ? error.message : \"Internal error\",\n },\n })\n } catch {\n try {\n res.statusCode = 500\n res.end()\n } catch {}\n }\n }\n })\n\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject)\n server.listen(0, \"127.0.0.1\", () => {\n server.off(\"error\", reject)\n resolve()\n })\n })\n\n const addr = server.address() as AddressInfo | null\n if (!addr) {\n server.close()\n throw new Error(\"Failed to bind proxy MCP server\")\n }\n\n boundAuthority = `127.0.0.1:${addr.port}`\n const url = `http://${boundAuthority}/mcp`\n\n // NOTE: authToken is deliberately absent from this line and every other\n // log call. The plugin log is written to disk and echoed to the TUI in\n // debug mode; a leaked token there would defeat the whole mechanism.\n log.info(\"proxy-mcp server started\", {\n url,\n tools: tools.map((t) => t.name),\n })\n\n let configFilePath: string | null = null\n\n const api: ProxyMcpServer = {\n url,\n serverName: SERVER_NAME,\n tools,\n authToken,\n calls,\n configPath() {\n if (configFilePath) return configFilePath\n const body = JSON.stringify(\n {\n mcpServers: {\n [SERVER_NAME]: {\n type: \"http\",\n url,\n // Claude CLI replays these headers on every request to this\n // server, which is what lets the handler above reject anyone\n // who did not read this 0600 file.\n headers: { Authorization: `Bearer ${authToken}` },\n timeout: resolveProxyClientCeilingMs(timeoutOverrides),\n },\n },\n },\n null,\n 2,\n )\n const hash = crypto\n .createHash(\"sha256\")\n .update(body)\n .digest(\"hex\")\n .slice(0, 12)\n const outPath = path.join(\n pluginTmpDir(),\n `proxy-${hash}.json`,\n )\n fs.writeFileSync(outPath, body, { encoding: \"utf8\", mode: 0o600 })\n configFilePath = outPath\n return outPath\n },\n pendingCallIds() {\n return [...pending.keys()]\n },\n async close() {\n for (const entry of pending.values()) {\n entry.reject(new Error(SERVER_CLOSED_MESSAGE))\n }\n pending.clear()\n await new Promise<void>((resolve) => {\n server.close(() => resolve())\n })\n if (configFilePath) {\n try {\n fs.unlinkSync(configFilePath)\n } catch {}\n configFilePath = null\n }\n },\n }\n\n return api\n}\n\n/** CLI-ready list of Claude tool names to disable, for each proxied tool. */\nexport function disallowedToolFlags(tools: ProxyToolDef[]): string[] {\n // Map our lowercase MCP tool names to the Claude tool name(s) they replace.\n // `edit` covers both `Edit` and `MultiEdit` because opencode has no\n // MultiEdit equivalent; without disabling MultiEdit, Claude can batch\n // file changes through it and bypass opencode's permission UI.\n // `task` disables Claude CLI's `Agent` tool (its built-in subagent\n // dispatcher) so subagent calls flow through opencode's `task` tool\n // instead — which lets opencode's configured subagent set (`build`,\n // `general`, custom subagents in opencode.json) execute the work\n // under opencode's permission/lifecycle, rather than Claude's\n // internal-only general-purpose / Explore / Plan options.\n const nameMap: Record<string, string[]> = {\n bash: [\"Bash\"],\n read: [\"Read\"],\n write: [\"Write\"],\n edit: [\"Edit\", \"MultiEdit\"],\n glob: [\"Glob\"],\n grep: [\"Grep\"],\n webfetch: [\"WebFetch\"],\n task: [\"Agent\"],\n task_batch: [\"Agent\"],\n // `question` disables Claude Code's built-in `AskUserQuestion` so the\n // structured-questions path flows through opencode's native `question`\n // tool instead — same UI/permission/audit benefits as the other\n // proxies. Without this, the model can call both and the two paths\n // diverge (opencode's form vs the headless deny-and-render fallback).\n question: [\"AskUserQuestion\"],\n }\n const out: string[] = []\n const seen = new Set<string>()\n for (const t of tools) {\n const mapped = nameMap[t.name.toLowerCase()]\n if (!mapped) continue\n for (const claudeTool of mapped) {\n if (seen.has(claudeTool)) continue\n seen.add(claudeTool)\n out.push(claudeTool)\n }\n }\n return out\n}\n\n/**\n * Everything that goes to `--disallowedTools` for one spawn: the built-ins\n * the proxied tools replace, plus the ones the operator named directly.\n *\n * `disallowedToolFlags` can only cover tools the plugin has a proxy for, so\n * a built-in with no equivalent (`NotebookEdit`, and anything Claude Code\n * ships next) is unreachable without `extraDisallowedTools` — issue #26.\n */\nexport function resolveDisallowedTools(options: {\n proxyTools?: ProxyToolDef[] | null\n extraDisallowedTools?: string[]\n disableWebSearch?: boolean\n}): string[] {\n const out: string[] = []\n const seen = new Set<string>()\n const push = (name: string) => {\n const trimmed = name.trim()\n if (!trimmed || seen.has(trimmed)) return\n seen.add(trimmed)\n out.push(trimmed)\n }\n\n for (const name of disallowedToolFlags(options.proxyTools ?? [])) push(name)\n for (const name of options.extraDisallowedTools ?? []) push(String(name))\n if (options.disableWebSearch) push(\"WebSearch\")\n return out\n}\n\n/** The shape of one `client.tool.list()` entry this resolver needs. */\nexport interface OpencodeToolListEntry {\n id: string\n description?: string\n parameters?: unknown\n}\n\n/**\n * Build proxy defs for the opencode tools named in `proxyOpencodeTools`.\n *\n * `resolvedProxyMcpTools` only forwards a tool whose id matches an enabled\n * MCP server (`<server>` or `<server>_<tool>`), so a tool another opencode\n * plugin declares directly matches nothing and is dropped. opencode-dcp's\n * `compress` is the case that motivated this: it is in opencode's registry,\n * dcp tells the model \"you MUST use the `compress` tool now\", and under this\n * provider the model was never offered it. This is the explicit allowlist\n * that forwards such a tool. It is deliberately never automatic: these run\n * inside opencode with the caller's permissions, so which ones cross over is\n * the operator's decision.\n *\n * A name already held by another proxy def wins, and the forwarded entry is\n * dropped with a warning. That is not arbitrary: `ensureProxyServer`\n * registers interceptors by name and an intercepted call is answered\n * in-process, so a forwarded def sharing a name with an intercepted one\n * (`compress` again) could never reach opencode at all. Dropping it loudly\n * is the difference between documented precedence and a silent shadow.\n */\nexport function resolveProxyOpencodeToolDefs(options: {\n requested?: readonly string[]\n items?: readonly OpencodeToolListEntry[]\n taken?: ReadonlySet<string>\n}): ProxyToolDef[] {\n const requested = options.requested ?? []\n if (requested.length === 0) return []\n\n const items = options.items\n if (!items) {\n log.warn(\n \"proxyOpencodeTools is set but opencode's tool registry did not answer;\" +\n \" forwarding nothing this spawn\",\n { requested: requested.map(String) },\n )\n return []\n }\n\n const byLowerId = new Map<string, OpencodeToolListEntry>()\n for (const item of items) {\n const key = item.id.toLowerCase()\n if (!byLowerId.has(key)) byLowerId.set(key, item)\n }\n\n const taken = options.taken ?? new Set<string>()\n const out: ProxyToolDef[] = []\n const seen = new Set<string>()\n const unknown: string[] = []\n const collided: string[] = []\n\n for (const raw of requested) {\n const name = String(raw).trim()\n if (!name) continue\n const item = byLowerId.get(name.toLowerCase())\n if (!item) {\n unknown.push(name)\n continue\n }\n if (taken.has(item.id)) {\n collided.push(item.id)\n continue\n }\n if (seen.has(item.id)) continue\n seen.add(item.id)\n out.push({\n name: item.id,\n description: typeof item.description === \"string\" ? item.description : \"\",\n inputSchema:\n item.parameters && typeof item.parameters === \"object\"\n ? (item.parameters as Record<string, unknown>)\n : { type: \"object\", properties: {} },\n })\n }\n\n // Same reasoning as the `proxyTools` typo warning: an unrecognised name is\n // simply not forwarded, and silence looks from the outside like the option\n // was ignored.\n if (unknown.length > 0) {\n log.warn(\"ignoring unknown proxyOpencodeTools entries\", {\n unknown,\n known: [...byLowerId.values()].map((item) => item.id).join(\", \"),\n })\n }\n if (collided.length > 0) {\n log.warn(\n \"proxyOpencodeTools entry dropped: a proxy tool already holds that name,\" +\n \" and it keeps it\",\n { collided },\n )\n }\n return out\n}\n\n/** One entry of the AI SDK `tools` array opencode hands `doStream`. */\nexport interface ModelToolEntry {\n type?: string\n name?: string\n description?: string\n inputSchema?: unknown\n}\n\n/** What `resolveMcpProxyToolDefs` found, split by the two things callers need. */\nexport interface McpProxyToolResolution {\n /** One def per MCP tool that will be served from the proxy instead. */\n defs: ProxyToolDef[]\n /** Only the servers a def was actually built for. */\n coveredServers: Set<string>\n}\n\n/**\n * Build proxy defs for opencode's MCP-backed tools out of the tool array\n * opencode already passes to `doStream`.\n *\n * The discovery source matters, and it is the whole reason this function\n * exists. The obvious source, `client.tool.list()` behind\n * `/experimental/tool`, enumerates opencode's `ToolRegistry` only: built-ins\n * plus plugin-declared tools. MCP tools are not in that registry on 1.18.31,\n * they are merged into the model's tool set afterwards, so a registry-based\n * match finds nothing however the prefix rule is written. The AI SDK `tools`\n * argument is downstream of that merge, so it is the one place a provider\n * plugin can see them at all.\n *\n * Matching is still by enabled-server prefix, longest name first so\n * `slack_intl_*` resolves to `slack_intl` and not `slack`. That is\n * deliberately narrow: everything else in the array is a built-in or another\n * plugin's tool, and forwarding those wholesale is what the explicit\n * `proxyOpencodeTools` allowlist is for.\n */\nexport function resolveMcpProxyToolDefs(options: {\n serverNames: readonly string[]\n tools?: readonly ModelToolEntry[]\n taken?: ReadonlySet<string>\n}): McpProxyToolResolution {\n const empty: McpProxyToolResolution = { defs: [], coveredServers: new Set() }\n const serverNames = options.serverNames ?? []\n if (serverNames.length === 0) return empty\n\n const tools = options.tools\n if (!tools || tools.length === 0) return empty\n\n const serversByLengthDesc = [...serverNames].sort((a, b) => b.length - a.length)\n const taken = options.taken ?? new Set<string>()\n const defs: ProxyToolDef[] = []\n const coveredServers = new Set<string>()\n const seen = new Set<string>()\n const collided: string[] = []\n\n for (const tool of tools) {\n // opencode only ever puts plain function tools in this array; a\n // provider-defined entry has no opencode executor behind it, so\n // forwarding one would produce a call nothing can answer.\n if (tool?.type !== undefined && tool.type !== \"function\") continue\n const name = typeof tool?.name === \"string\" ? tool.name.trim() : \"\"\n if (!name) continue\n\n const matchedServer = serversByLengthDesc.find(\n (server) => name === server || name.startsWith(`${server}_`),\n )\n if (!matchedServer) continue\n if (seen.has(name)) continue\n if (taken.has(name)) {\n collided.push(name)\n continue\n }\n seen.add(name)\n coveredServers.add(matchedServer)\n defs.push({\n name,\n description: typeof tool.description === \"string\" ? tool.description : \"\",\n inputSchema:\n tool.inputSchema && typeof tool.inputSchema === \"object\"\n ? (tool.inputSchema as Record<string, unknown>)\n : { type: \"object\", properties: {} },\n })\n }\n\n if (collided.length > 0) {\n // WARN, not NOTICE: only warn and error are alwaysStderr in src/logger.ts,\n // and a shadowed MCP tool silently stops being routed through opencode,\n // which is exactly the class of surprise this lane exists to end.\n log.warn(\n \"MCP tool not routed through the proxy: another proxy tool already holds\" +\n \" that name, and it keeps it\",\n { collided },\n )\n }\n return { defs, coveredServers }\n}\n\nfunction readBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = []\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk))\n req.on(\"end\", () => resolve(Buffer.concat(chunks).toString(\"utf8\")))\n req.on(\"error\", reject)\n })\n}\n\n/**\n * The single exit for every `tools/call`, broker-backed or intercepted.\n * Success and failure share one MCP result envelope: a JSON-RPC error for\n * `kind: \"error\"` was rejected by Claude CLI as a \"malformed result that\n * failed schema validation\", so tool failures must surface as\n * `isError: true` instead.\n */\nfunction writeToolCallResult(\n res: ServerResponse,\n requestId: unknown,\n result: ProxyToolResult,\n reply: ReplyStream | null = null,\n): void {\n const text = result.kind === \"error\" ? result.message : result.text\n const isError = result.kind === \"error\" || result.isError === true\n const envelope = {\n jsonrpc: \"2.0\",\n id: requestId ?? null,\n result: {\n content: [{ type: \"text\", text }],\n isError,\n },\n }\n if (reply) {\n reply.finish(envelope)\n return\n }\n writeJson(res, envelope)\n}\n\n/**\n * An in-flight streamed reply whose headers are already on the wire.\n * `finish` writes the JSON-RPC response and ends the body; `stop` only\n * cancels the keepalive, for when the client went away first.\n */\ninterface ReplyStream {\n finish(envelope: unknown): void\n stop(): void\n}\n\n/**\n * Write `ping` every `keepaliveMs` until stopped or the response is gone.\n * Never keeps the host process alive on its own.\n */\nfunction startKeepalive(\n res: ServerResponse,\n keepaliveMs: number,\n ping: string,\n): () => void {\n let timer: ReturnType<typeof setInterval> | null = setInterval(() => {\n if (res.writableEnded || res.destroyed) {\n stop()\n return\n }\n res.write(ping)\n }, keepaliveMs)\n timer.unref?.()\n const stop = () => {\n if (timer) {\n clearInterval(timer)\n timer = null\n }\n }\n return stop\n}\n\n/**\n * SSE reply: the JSON-RPC response goes out as the single `message` event,\n * which is what the MCP Streamable HTTP client expects for a request\n * answered over SSE.\n */\nfunction openEventStream(res: ServerResponse, keepaliveMs: number): ReplyStream {\n res.statusCode = 200\n res.setHeader(\"Content-Type\", \"text/event-stream\")\n res.setHeader(\"Cache-Control\", \"no-cache, no-transform\")\n res.setHeader(\"Connection\", \"keep-alive\")\n res.flushHeaders()\n // Start the response body without waiting for the tool result.\n res.write(\": open\\n\\n\")\n const stop = startKeepalive(res, keepaliveMs, \": keepalive\\n\\n\")\n return {\n stop,\n finish(envelope) {\n stop()\n if (res.writableEnded || res.destroyed) return\n res.end(`event: message\\ndata: ${JSON.stringify(envelope)}\\n\\n`)\n },\n }\n}\n\n/**\n * JSON reply for a client that did not ask for SSE (@broskees' 68ed142,\n * adapted). Headers are flushed at once, which stops the client's header\n * timer, and whitespace is written on the keepalive cadence, which stops its\n * body timer. There is no `Content-Length`, so the body is chunked, and the\n * envelope is written last: whitespace before a JSON value is insignificant\n * (RFC 8259), so the whole body still parses as the one JSON-RPC response,\n * on success and on error alike.\n */\nfunction openJsonStream(res: ServerResponse, keepaliveMs: number): ReplyStream {\n res.statusCode = 200\n res.setHeader(\"Content-Type\", \"application/json\")\n res.setHeader(\"Cache-Control\", \"no-cache, no-transform\")\n res.flushHeaders()\n const stop = startKeepalive(res, keepaliveMs, \" \")\n return {\n stop,\n finish(envelope) {\n stop()\n if (res.writableEnded || res.destroyed) return\n res.end(JSON.stringify(envelope))\n },\n }\n}\n\nfunction writeJson(res: ServerResponse, body: unknown): void {\n if (res.destroyed || res.writableEnded) return\n const payload = JSON.stringify(body)\n res.statusCode = 200\n res.setHeader(\"Content-Type\", \"application/json\")\n res.setHeader(\"Content-Length\", Buffer.byteLength(payload).toString())\n res.end(payload)\n}\n","import * as fs from \"node:fs\"\nimport * as os from \"node:os\"\nimport * as path from \"node:path\"\n\n/**\n * Per-process scratch directory for plugin tmp files (bridged MCP config,\n * proxy server config, etc.). Created lazily on first use and rm'd on\n * normal process exit so we don't leak across runs. PID-isolated so two\n * concurrent opencode processes don't race on the same files.\n *\n * Caveat: `process.on(\"exit\")` does not fire for SIGKILL or unhandled\n * external signals, so abnormal terminations still leak. OS-level tmpdir\n * cleanup (`systemd-tmpfiles`, macOS periodic) handles those eventually.\n */\nconst PLUGIN_TMP_DIR = path.join(\n os.tmpdir(),\n `opencode-claude-code-${process.pid}`,\n)\n\nlet registered = false\n\nexport function pluginTmpDir(): string {\n if (!fs.existsSync(PLUGIN_TMP_DIR)) {\n fs.mkdirSync(PLUGIN_TMP_DIR, { recursive: true })\n }\n if (!registered) {\n registered = true\n process.on(\"exit\", () => {\n try {\n fs.rmSync(PLUGIN_TMP_DIR, { recursive: true, force: true })\n } catch {}\n })\n }\n return PLUGIN_TMP_DIR\n}\n","import { EventEmitter } from \"node:events\"\nimport {\n armProxyDeadline,\n buildProxyTimeoutError,\n PROXY_NO_DEADLINE_MS,\n resolveProxyCallTimeoutMs,\n type ProxyCallChannel,\n type ProxyDeadline,\n type ProxyToolCall,\n type ProxyToolResult,\n} from \"./proxy-mcp.js\"\nimport { log } from \"./logger.js\"\n\nexport interface PendingProxyCall {\n sessionKey: string\n toolCallId: string\n toolName: string\n input: Record<string, unknown>\n /**\n * Liveness of Claude's HTTP request for this call. Once `closed`, a\n * result written to it is lost; the language model then delivers the\n * result as a user message instead. Absent means open.\n */\n channel?: ProxyCallChannel\n /**\n * True once the language model has handed this call to opencode as a\n * tool-call part. A call that is still pending without it was queued\n * while no turn was attached and has to be drained by the next one.\n */\n emitted?: boolean\n}\n\ntype InternalPending = PendingProxyCall & {\n createdAt: number\n /** `PROXY_NO_DEADLINE_MS` (0) when the call has no deadline. */\n deadlineMs: number\n /** Absent when the call has no deadline. */\n timer: ProxyDeadline | null\n /** Stall heartbeat; only armed for calls that have no deadline. */\n stallTimer: ReturnType<typeof setInterval> | null\n /** One-shot \"this is going to run out\" notice; deadline-bearing calls only. */\n deadlineWarnTimer: ReturnType<typeof setTimeout> | null\n resolve(result: ProxyToolResult): void\n reject(error: Error): void\n}\n\n/**\n * How long a call with NO deadline may wait before the broker starts saying\n * so, and how often it repeats afterwards.\n *\n * `task` and `task_batch` have had no deadline since v0.20.0, which is right:\n * every way a call can end is an event the plugin observes, so a wall clock\n * could only ever kill a subagent that was still working. The cost is that a\n * genuinely wedged subagent is now silent forever, with nothing to notice it\n * but the operator. This is the missing half: it never ends a call, it only\n * reports one. Deliberately long, because a real subagent routinely runs\n * minutes and a warning on healthy work is noise. Deadline-bearing calls are\n * not armed at all: their deadline already reports them.\n */\nexport const PROXY_STALL_WARNING_MS = 5 * 60_000\n\n/**\n * Where in a deadline-bearing call's life to say it is going to run out.\n *\n * The heartbeat above deliberately skips these calls, on the reasoning that\n * their deadline already reports them. It does, but only by killing them:\n * the first and last thing you hear is the failure. Measured the hard way on\n * 2026-09-19, when two proxied calls that were still working were rejected at\n * their 10-minute deadline with no prior signal, and the operator had to\n * infer from silence what was happening.\n *\n * So one notice, at 60% of the deadline, saying how long is left. Once, never\n * repeating, because the deadline itself is the next thing that will speak.\n * Calls whose deadline is under `PROXY_DEADLINE_WARNING_MIN_MS` are skipped:\n * on a short deadline the notice and the rejection would arrive together and\n * tell you nothing you are not about to be told anyway.\n */\nexport const PROXY_DEADLINE_WARNING_FRACTION = 0.6\nexport const PROXY_DEADLINE_WARNING_MIN_MS = 60_000\n\n/** Every timer a pending call can hold. Each removal site must use this. */\nfunction clearPendingTimers(pending: InternalPending): void {\n pending.timer?.cancel()\n if (pending.stallTimer) clearInterval(pending.stallTimer)\n if (pending.deadlineWarnTimer) clearTimeout(pending.deadlineWarnTimer)\n}\n\n/** One pending call, flattened for `/claude-code-doctor`. */\nexport interface PendingProxyCallSnapshot {\n sessionKey: string\n toolCallId: string\n toolName: string\n ageMs: number\n deadlineMs: number\n emitted: boolean\n channelClosed: boolean\n}\n\n// Primary index: callId -> pending. Tool call IDs are UUIDs produced by\n// proxy-mcp, so they are globally unique across sessions.\nconst pendingByCallId = new Map<string, InternalPending>()\n// Reverse index: sessionKey -> set of callIds, so the language model can\n// drain or reject every pending call for one Claude subprocess at once.\nconst callIdsBySession = new Map<string, Set<string>>()\n\nconst emitter = new EventEmitter()\n\nfunction eventName(sessionKey: string) {\n return `pending:${sessionKey}`\n}\n\nfunction indexAdd(sessionKey: string, callId: string) {\n let s = callIdsBySession.get(sessionKey)\n if (!s) {\n s = new Set()\n callIdsBySession.set(sessionKey, s)\n }\n s.add(callId)\n}\n\nfunction indexRemove(sessionKey: string, callId: string) {\n const s = callIdsBySession.get(sessionKey)\n if (!s) return\n s.delete(callId)\n if (s.size === 0) callIdsBySession.delete(sessionKey)\n}\n\nexport function onPendingProxyCall(\n sessionKey: string,\n handler: (call: PendingProxyCall) => void,\n): () => void {\n const name = eventName(sessionKey)\n emitter.on(name, handler)\n return () => emitter.off(name, handler)\n}\n\nexport function queuePendingProxyCall(\n sessionKey: string,\n call: ProxyToolCall,\n timeoutOverrides?: Record<string, number>,\n /** Test seam, same shape as `createProxyMcpServer`'s `keepaliveMs`. */\n stallWarningMs: number = PROXY_STALL_WARNING_MS,\n /** Test seam: lower it so a short test deadline still warns. */\n deadlineWarningMinMs: number = PROXY_DEADLINE_WARNING_MIN_MS,\n): PendingProxyCall {\n // Defensive: if this exact callId is somehow already pending (UUID\n // collision or retry storm), replace it cleanly so we never leak two\n // entries for the same id.\n const previous = pendingByCallId.get(call.id)\n if (previous) {\n clearPendingTimers(previous)\n previous.reject(\n new Error(`Replaced pending proxy call ${call.id} with a fresh one`),\n )\n pendingByCallId.delete(call.id)\n indexRemove(previous.sessionKey, call.id)\n }\n\n const deadlineMs = resolveProxyCallTimeoutMs(\n call.toolName,\n call.input,\n timeoutOverrides,\n )\n\n // Same rule as the proxy-mcp handler: a call with no deadline gets no timer\n // (a zero-delay timer would fire on the next tick). It stays pending until\n // a result, an abort, the next turn's orphan sweep, or its process going.\n // A permission prompt still open in opencode extends the deadline instead of\n // ending the call (`ProxyDeadlineGuard` in proxy-mcp.ts): the time an\n // operator spends answering it is not the tool being slow.\n const timer =\n deadlineMs > PROXY_NO_DEADLINE_MS\n ? armProxyDeadline({\n callId: call.id,\n toolName: call.toolName,\n deadlineMs,\n logExtension: true,\n onExpire: () => {\n const current = pendingByCallId.get(call.id)\n if (!current) return\n pendingByCallId.delete(call.id)\n indexRemove(current.sessionKey, call.id)\n clearPendingTimers(current)\n current.reject(buildProxyTimeoutError(call.toolName, deadlineMs))\n log.notice(\"timed out pending proxy call\", {\n sessionKey: current.sessionKey,\n toolCallId: call.id,\n toolName: call.toolName,\n deadlineMs,\n })\n },\n })\n : null\n\n // A call with no deadline has nothing that will ever report it, so it gets\n // a heartbeat instead. WARN on purpose: only warn and error are always on\n // stderr (see `src/logger.ts`), and a NOTICE nobody sees outside debug mode\n // would defeat the point of the line existing at all.\n const stallTimer =\n deadlineMs === PROXY_NO_DEADLINE_MS && stallWarningMs > 0\n ? setInterval(() => {\n const current = pendingByCallId.get(call.id)\n if (!current) return\n log.warn(\"proxy call still waiting, no deadline\", {\n sessionKey: current.sessionKey,\n toolCallId: current.toolCallId,\n toolName: current.toolName,\n waitedMs: Date.now() - current.createdAt,\n emitted: current.emitted === true,\n channelClosed: current.channel?.closed === true,\n note: \"nothing will time this out; it ends when opencode returns a result, you abort, you send another message, or the claude process goes\",\n })\n }, stallWarningMs)\n : null\n // Never hold opencode's process open for a heartbeat.\n stallTimer?.unref?.()\n\n // The other half: a call that DOES have a deadline says so before the\n // deadline takes it, rather than only by dying. Same WARN reasoning, and\n // one-shot, since the rejection is the next thing that will report.\n const warnAtMs = Math.floor(deadlineMs * PROXY_DEADLINE_WARNING_FRACTION)\n const deadlineWarnTimer =\n deadlineMs >= deadlineWarningMinMs && deadlineMs > 0 && warnAtMs > 0\n ? setTimeout(() => {\n const current = pendingByCallId.get(call.id)\n if (!current) return\n const waitedMs = Date.now() - current.createdAt\n log.warn(\"proxy call still waiting, deadline approaching\", {\n sessionKey: current.sessionKey,\n toolCallId: current.toolCallId,\n toolName: current.toolName,\n waitedMs,\n deadlineMs,\n remainingMs: Math.max(0, deadlineMs - waitedMs),\n emitted: current.emitted === true,\n channelClosed: current.channel?.closed === true,\n note: \"it will be rejected when the deadline passes unless opencode is still serving it (a permission prompt is waiting); raise this tool's proxyToolTimeoutMs if the work is legitimately this long\",\n })\n }, warnAtMs)\n : null\n deadlineWarnTimer?.unref?.()\n\n const pending: InternalPending = {\n sessionKey,\n toolCallId: call.id,\n toolName: call.toolName,\n input: call.input,\n channel: call.channel,\n createdAt: Date.now(),\n deadlineMs,\n timer,\n stallTimer,\n deadlineWarnTimer,\n resolve: call.resolve,\n reject: call.reject,\n }\n pendingByCallId.set(call.id, pending)\n indexAdd(sessionKey, call.id)\n emitter.emit(eventName(sessionKey), pending)\n log.info(\"queued pending proxy call\", {\n sessionKey,\n toolCallId: call.id,\n toolName: call.toolName,\n })\n return pending\n}\n\n/** Record that opencode has been given this call as a tool-call part. */\nexport function markPendingProxyCallEmitted(toolCallId: string): void {\n const pending = pendingByCallId.get(toolCallId)\n if (pending) pending.emitted = true\n}\n\n/** True when Claude's request for this call is gone (see `channel`). */\nexport function isPendingProxyCallChannelClosed(\n call: PendingProxyCall,\n): boolean {\n return call.channel?.closed === true\n}\n\n/** One pending call by id, for the deadline guard, which knows only the id. */\nexport function findPendingProxyCall(toolCallId: string): PendingProxyCall | undefined {\n return pendingByCallId.get(toolCallId)\n}\n\nexport function getPendingProxyCalls(sessionKey: string): PendingProxyCall[] {\n const s = callIdsBySession.get(sessionKey)\n if (!s || s.size === 0) return []\n const out: PendingProxyCall[] = []\n for (const id of s) {\n const p = pendingByCallId.get(id)\n if (p) out.push(p)\n }\n return out\n}\n\n/**\n * Every call the broker is currently holding, across all sessions, with how\n * long it has waited and when it gives up. Read-only view for the doctor\n * report; deliberately carries no `input`, since a pending call's arguments\n * can be a whole file's contents.\n */\nexport function snapshotPendingProxyCalls(now = Date.now()): PendingProxyCallSnapshot[] {\n const out: PendingProxyCallSnapshot[] = []\n for (const pending of pendingByCallId.values()) {\n out.push({\n sessionKey: pending.sessionKey,\n toolCallId: pending.toolCallId,\n toolName: pending.toolName,\n ageMs: Math.max(0, now - pending.createdAt),\n deadlineMs: pending.deadlineMs,\n emitted: pending.emitted === true,\n channelClosed: pending.channel?.closed === true,\n })\n }\n return out\n}\n\nexport function resolvePendingProxyCallById(\n toolCallId: string,\n result: ProxyToolResult,\n): boolean {\n const pending = pendingByCallId.get(toolCallId)\n if (!pending) return false\n pendingByCallId.delete(toolCallId)\n indexRemove(pending.sessionKey, toolCallId)\n clearPendingTimers(pending)\n pending.resolve(result)\n log.info(\"resolved pending proxy call\", {\n sessionKey: pending.sessionKey,\n toolCallId: pending.toolCallId,\n toolName: pending.toolName,\n })\n return true\n}\n\nexport function rejectPendingProxyCallById(\n toolCallId: string,\n error: Error,\n): boolean {\n const pending = pendingByCallId.get(toolCallId)\n if (!pending) return false\n pendingByCallId.delete(toolCallId)\n indexRemove(pending.sessionKey, toolCallId)\n clearPendingTimers(pending)\n pending.reject(error)\n // Rejection is the broker's cleanup mechanism — fires on timeouts, orphans,\n // stream closes, etc. None are user-actionable. File-log them at NOTICE so\n // the audit trail is intact; rely on caller sites to decide TUI visibility.\n log.notice(\"rejected pending proxy call\", {\n sessionKey: pending.sessionKey,\n toolCallId: pending.toolCallId,\n toolName: pending.toolName,\n error: error.message,\n })\n return true\n}\n\nexport function rejectAllPendingProxyCallsForSession(\n sessionKey: string,\n error: Error,\n): number {\n const s = callIdsBySession.get(sessionKey)\n if (!s) return 0\n const ids = [...s]\n let count = 0\n for (const id of ids) {\n if (rejectPendingProxyCallById(id, error)) count++\n }\n return count\n}\n","/**\n * Per-session state for the opt-in `compress` proxy tool.\n *\n * Keyed by session key (the same `cwd::modelId::scope::affinity` string\n * session-manager uses). When Claude calls the intercepted `compress` tool\n * the summary is stored here and the session is marked for restart. The\n * next `doStream` turn consumes that mark, evicts the running child and its\n * Claude session id, and the fresh spawn gets the summary prepended to its\n * appended system prompt.\n *\n * The summary deliberately survives `deleteClaudeSessionId()`: the restart\n * path calls it, so clearing there would wipe the summary microseconds\n * before the new spawn reads it (the original fork version did exactly\n * that, which made the whole feature a no-op). It is dropped when a new\n * opencode conversation starts on the same key, and by the entry cap below.\n */\n\nimport { log } from \"./logger.js\"\n\ninterface CompressionState {\n summary: string\n restartPending: boolean\n}\n\n/**\n * Session keys are bounded in practice by workspaces × models, and each\n * entry is one summary string, but a long-lived opencode process that\n * hops workspaces should not accumulate them forever.\n */\nconst MAX_COMPRESSION_ENTRIES = 32\n\nconst compressions = new Map<string, CompressionState>()\n\n/**\n * Record a summary and mark the session for restart. Storing and marking\n * are one event on purpose: a stored summary that never resets the session\n * would silently do nothing.\n */\nexport function storeCompressionSummary(sessionKey: string, summary: string): void {\n compressions.set(sessionKey, { summary, restartPending: true })\n while (compressions.size > MAX_COMPRESSION_ENTRIES) {\n const oldest = compressions.keys().next()\n if (oldest.done) break\n compressions.delete(oldest.value)\n log.info(\"compression store evicted oldest entry\", { sessionKey: oldest.value })\n }\n}\n\nexport function getCompressionSummary(sessionKey: string): string | undefined {\n return compressions.get(sessionKey)?.summary\n}\n\n/**\n * True once per compress call, for the turn that performs the reset. The\n * summary is kept: it is the prior context for every spawn that follows,\n * until a new conversation clears it.\n */\nexport function consumeCompressionRestart(sessionKey: string): boolean {\n const state = compressions.get(sessionKey)\n if (!state?.restartPending) return false\n state.restartPending = false\n return true\n}\n\nexport function clearCompression(sessionKey: string): void {\n compressions.delete(sessionKey)\n}\n","import { randomUUID } from \"node:crypto\"\nimport type { ChildProcess } from \"node:child_process\"\nimport { cliSupportsSideQuestion, type CliVersion } from \"./cli-version.js\"\nimport type { ActiveProcess } from \"./session-manager.js\"\n\ntype SideQuestionProcess = Pick<ActiveProcess, \"proc\" | \"lineEmitter\">\n\nexport interface SideQuestionResult {\n response: string\n synthetic: boolean\n}\n\nexport interface SideQuestionOptions {\n cliVersion: CliVersion | null\n interactive?: boolean\n abortSignal?: AbortSignal\n timeoutMs?: number\n history?: readonly { question: string; response: string }[]\n}\n\nexport interface SideQuestionExchange {\n question: string\n response: string\n}\n\nconst MAX_HISTORY_EXCHANGES = 20\n\nexport const SIDE_QUESTION_USAGE =\n \"Usage: /btw <question>. Ask a side question about the current conversation without adding it to the main context.\"\n\nconst pendingProcesses = new WeakSet<ChildProcess>()\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n}\n\n/**\n * opencode appends its own `<system-reminder>` blocks to the user message, as\n * extra text parts on the same message. They instruct a normal turn and are not\n * part of what the operator typed after `/btw`, so they must not travel with the\n * aside: a plan-mode reminder alone is over 1.5 KB, and measured live it both\n * steered the answer and kept a bare `/btw` from ever looking empty.\n *\n * Blocks are removed wherever they sit rather than by matching a whole part,\n * because a harness may append its own trailing metadata after one (opencode-dcp\n * adds a `<dcp-message-id>` marker), which an end-anchored check would miss.\n */\nconst SYSTEM_REMINDER_BLOCK = /<system-reminder>[\\s\\S]*?<\\/system-reminder>/g\n\nexport function parseSideQuestionContent(content: unknown): { question: string } | null {\n let text: string\n if (typeof content === \"string\") {\n text = content\n } else if (Array.isArray(content)) {\n const parts: string[] = []\n for (const part of content) {\n if (!isRecord(part) || part.type !== \"text\" || typeof part.text !== \"string\") return null\n parts.push(part.text)\n }\n text = parts.join(\"\\n\")\n } else {\n return null\n }\n const match = /^\\/btw(?:\\s+([\\s\\S]*))?$/.exec(text.replace(SYSTEM_REMINDER_BLOCK, \"\").trim())\n return match ? { question: (match[1] ?? \"\").trim() } : null\n}\n\n/** Do not replay a historical /btw during an assistant/tool continuation. */\nexport function parseSideQuestion(\n prompt: readonly { role: string; content: unknown }[],\n): { question: string } | null {\n const latest = prompt.at(-1)\n return latest?.role === \"user\" ? parseSideQuestionContent(latest.content) : null\n}\n\nfunction assistantText(content: unknown): string {\n if (typeof content === \"string\") return content.trim()\n if (!Array.isArray(content)) return \"\"\n const parts: string[] = []\n for (const part of content) {\n if (isRecord(part) && part.type === \"text\" && typeof part.text === \"string\") parts.push(part.text)\n }\n return parts.join(\"\\n\").trim()\n}\n\n/**\n * Earlier `/btw` exchanges in this conversation, oldest first, for the\n * control request's `history` so follow-ups can refer to previous asides.\n * The final user message is the current question and is left out.\n */\nexport function collectSideQuestionHistory(\n prompt: readonly { role: string; content: unknown }[],\n): SideQuestionExchange[] {\n const history: SideQuestionExchange[] = []\n for (let index = 0; index < prompt.length - 1; index++) {\n const message = prompt[index]\n if (message.role !== \"user\") continue\n const aside = parseSideQuestionContent(message.content)\n if (!aside?.question) continue\n const reply = prompt[index + 1]\n if (reply.role !== \"assistant\") continue\n const response = assistantText(reply.content)\n if (!response || response === SIDE_QUESTION_USAGE) continue\n history.push({ question: aside.question, response })\n }\n return history.slice(-MAX_HISTORY_EXCHANGES)\n}\n\nexport function isSideQuestionPending(activeProcess: SideQuestionProcess): boolean {\n return pendingProcesses.has(activeProcess.proc)\n}\n\n/**\n * Call before the normal stdout line/buffer dispatch. Only a response with an\n * active request-ID listener is consumed. Progress and unrelated lines retain\n * their existing routing; the helper never subscribes to the shared `line` event.\n */\nexport function dispatchSideQuestionResponse(\n activeProcess: SideQuestionProcess,\n line: string,\n): boolean {\n if (!pendingProcesses.has(activeProcess.proc)) return false\n let message: unknown\n try {\n message = JSON.parse(line)\n } catch {\n return false\n }\n if (!isRecord(message) || message.type !== \"control_response\") return false\n const response = message.response\n if (!isRecord(response) || typeof response.request_id !== \"string\") return false\n return activeProcess.lineEmitter.emit(`side-question:${response.request_id}`, response)\n}\n\n/**\n * Uses an existing headless process, never a user envelope or a new spawn.\n * The process may be mid-turn: Claude Code answers `side_question` on a\n * separate advisor call while the main loop keeps running (measured live on\n * 2.1.258 with the turn blocked on a held MCP tool). Only one aside per\n * process is in flight at a time; responses are matched by request id ahead\n * of the normal stdout routing, so a streaming turn never sees them.\n */\nexport async function requestSideQuestion(\n activeProcess: SideQuestionProcess,\n question: string,\n options: SideQuestionOptions,\n): Promise<SideQuestionResult> {\n question = question.trim()\n if (!question) return { response: SIDE_QUESTION_USAGE, synthetic: true }\n options.abortSignal?.throwIfAborted()\n const { proc, lineEmitter } = activeProcess\n if (options.interactive || !proc.stdout) {\n throw new Error(\"/btw requires the headless Claude Code transport; interactive sessions are not supported.\")\n }\n if (!cliSupportsSideQuestion(options.cliVersion)) {\n throw new Error(\"/btw requires Claude Code CLI 2.1.258 or newer (the oldest verified version).\")\n }\n if (pendingProcesses.has(proc)) {\n throw new Error(\"Wait for the current /btw to finish before asking another.\")\n }\n const stdin = proc.stdin\n if (proc.killed || proc.exitCode != null || proc.signalCode != null ||\n !stdin || stdin.destroyed || stdin.writableEnded || !stdin.writable) {\n throw new Error(\"/btw requires a live Claude Code session with writable stdin.\")\n }\n const timeoutMs = options.timeoutMs ?? 120_000\n if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2_147_483_647) {\n throw new Error(\"/btw timeoutMs must be a positive 32-bit integer.\")\n }\n const requestId = randomUUID()\n const request = JSON.stringify({\n type: \"control_request\",\n request_id: requestId,\n request: {\n subtype: \"side_question\",\n question,\n ...(options.history === undefined ? {} : { history: options.history }),\n },\n })\n\n pendingProcesses.add(proc)\n return new Promise<SideQuestionResult>((resolve, reject) => {\n const event = `side-question:${requestId}`\n let settled = false\n let sent = false\n let cancelPending = false\n\n const cleanup = (): void => {\n clearTimeout(timer)\n lineEmitter.off(event, onResponse)\n lineEmitter.off(\"close\", onClose)\n lineEmitter.off(\"error\", onError)\n proc.off(\"exit\", onClose)\n proc.off(\"close\", onClose)\n proc.off(\"error\", onError)\n if (!cancelPending) stdin.off(\"error\", onError)\n options.abortSignal?.removeEventListener(\"abort\", onAbort)\n pendingProcesses.delete(proc)\n }\n const fail = (error: unknown, cancel = false): void => {\n if (settled) return\n settled = true\n if (cancel && sent && !stdin.destroyed && !stdin.writableEnded && stdin.writable) {\n try {\n cancelPending = true\n stdin.write(\n JSON.stringify({ type: \"control_cancel_request\", request_id: requestId }) + \"\\n\",\n () => {\n // A failed write emits `error` after its callback. Keep the pipe\n // listener through that event without delaying abort/timeout.\n queueMicrotask(() => stdin.off(\"error\", onError))\n },\n )\n } catch {\n cancelPending = false\n // Preserve the original abort/timeout even if the child has gone away.\n }\n }\n cleanup()\n reject(error)\n }\n const onClose = (): void => fail(new Error(\"Claude Code closed before answering /btw.\"))\n const onError = (error: Error): void => fail(error)\n const onAbort = (): void => fail(\n options.abortSignal?.reason ?? new DOMException(\"/btw was aborted.\", \"AbortError\"),\n true,\n )\n const onResponse = (response: Record<string, unknown>): void => {\n if (settled || response.request_id !== requestId) return\n if (response.subtype === \"error\") {\n fail(new Error(typeof response.error === \"string\" ? response.error : \"Claude Code rejected /btw.\"))\n return\n }\n const result = response.response\n if (response.subtype !== \"success\" || !isRecord(result) ||\n typeof result.response !== \"string\" || typeof result.synthetic !== \"boolean\") {\n fail(new Error(\"Claude Code returned an invalid /btw response.\"))\n return\n }\n settled = true\n cleanup()\n resolve({ response: result.response, synthetic: result.synthetic })\n }\n const timer = setTimeout(() => {\n fail(new Error(`/btw timed out after ${timeoutMs}ms.`), true)\n }, timeoutMs)\n\n lineEmitter.on(event, onResponse)\n lineEmitter.on(\"close\", onClose)\n lineEmitter.on(\"error\", onError)\n proc.on(\"exit\", onClose)\n proc.on(\"close\", onClose)\n proc.on(\"error\", onError)\n stdin.on(\"error\", onError)\n options.abortSignal?.addEventListener(\"abort\", onAbort, { once: true })\n if (options.abortSignal?.aborted) {\n onAbort()\n return\n }\n try {\n sent = true\n stdin.write(request + \"\\n\")\n } catch (error) {\n fail(error)\n }\n })\n}\n","import { detectCliVersion } from \"./cli-version.js\"\nimport { log } from \"./logger.js\"\nimport { getOpencodeClient } from \"./runtime-status.js\"\nimport { findActiveProcessBySessionId, type ActiveProcess } from \"./session-manager.js\"\nimport {\n collectSideQuestionHistory,\n isSideQuestionPending,\n requestSideQuestion,\n SIDE_QUESTION_USAGE,\n type SideQuestionExchange,\n type SideQuestionResult,\n} from \"./side-question.js\"\n\n/**\n * `/btw`: a side question that is answered while the main turn keeps running,\n * and whose exchange is kept in the conversation where it was asked.\n *\n * opencode's TUI sends every slash command to the server the moment it is\n * typed, busy or not (`tui/component/prompt/index.tsx`), so the\n * `command.execute.before` hook fires immediately. The user message the\n * command produces is what gets held back (\"Queued\") until the running turn\n * ends, and opencode's loop then runs it as a step of its own: the loop only\n * exits when the newest assistant message answers the newest user message\n * (`session/prompt.ts`, `lastAssistant.parentID === lastUser.id`).\n *\n * So the hook sends the question to the conversation's live `claude` process\n * as a `side_question` control request right away (Claude Code answers those\n * on a separate advisor call, concurrently with a running turn, from the\n * conversation's context) and remembers the pending answer per session. Where\n * the answer lands then depends on what is open when it arrives:\n * 1. a turn is streaming, so the answer is written into that turn's own\n * reply as its own text block and the `/btw` message is dropped. The\n * operator reads it in place, the moment it is ready, and it stays;\n * 2. nothing is open to write to, so the `/btw` message is held until the\n * turn ends. It then reaches the aside branch in\n * `claude-code-language-model.ts`, which takes the remembered answer and\n * emits it as that message's reply, at no cost;\n * 3. the conversation was idle all along, so the message runs at once and\n * case 2 is all that happens.\n * Every one of those lands in the conversation, so none of them toasts: a\n * toast expires and the operator asked for the answer to stay. The two that\n * remain are the paths where nothing reaches the conversation at all, a bare\n * `/btw` and a turn that never ended, where a toast is the only feedback left.\n * `filterSideQuestionHistory` keeps every `/btw` pair out of Claude's prompt,\n * `INLINE_ASIDE_MARKER` does the same for case 1's block, and the control\n * request never touches Claude's own transcript, so an aside is persisted for\n * the operator only.\n */\n\ntype SdkResult<T = unknown> = Promise<{ data?: T; error?: unknown }>\n\nexport interface BtwToast {\n title?: string\n message: string\n variant: \"info\" | \"success\" | \"warning\" | \"error\"\n duration?: number\n}\n\nexport interface BtwSdkMessage {\n info?: { role?: string }\n parts?: unknown[]\n}\n\nexport interface BtwSdkClient {\n session?: {\n messages?: (options: { path: { id: string } }) => SdkResult<BtwSdkMessage[]>\n /** `GET /session/status`: sessions missing from the map are idle. */\n status?: () => SdkResult<Record<string, { type: string }>>\n }\n tui?: {\n showToast?: (options: { body: BtwToast }) => SdkResult\n }\n}\n\nexport interface BtwCommandInput {\n command: string\n sessionID: string\n arguments: string\n}\n\n/** Every wait the hook can make, so tests do not have to sit through them. */\nexport interface BtwWaitOptions {\n /** How often to re-read opencode's session status. */\n pollMs?: number\n /** Cap on holding the `/btw` message back while a turn runs. */\n timeoutMs?: number\n /** Cap on treating an idle-looking status as not yet registered. */\n settleMs?: number\n /** Cap on waiting for the running turn's `claude` process to be tagged. */\n spawnWaitMs?: number\n /** How often to retry writing the answer into the running turn. */\n inlinePollMs?: number\n /** Cap on waiting for a stream to write the answer into. */\n inlineWaitMs?: number\n}\n\n/** Thrown to make opencode drop the prompt when there is nothing worth keeping. */\nexport class BtwHandledError extends Error {\n override readonly name = \"BtwHandledError\"\n constructor(message = \"/btw was handled by the claude-code plugin; nothing to add to this conversation.\") {\n super(message)\n }\n}\n\nexport const BTW_COMMAND_DESCRIPTION =\n \"Ask a side question in the live Claude Code session without changing its context\"\n\nexport const BTW_NO_SESSION_MESSAGE =\n \"/btw needs a live Claude Code session in this conversation. Send a normal message with a Claude Code model first, then ask again.\"\n\nexport const BTW_INLINE_HANDLED_MESSAGE =\n \"/btw was answered inside the running turn; nothing to add to this conversation.\"\n\nexport const BTW_TURN_TOO_LONG_MESSAGE =\n \"/btw gave up waiting for this turn to end. Ask again once it is over.\"\n\nconst IDLE_POLL_MS = 500\nconst IDLE_WAIT_MAX_MS = 30 * 60_000\n/**\n * How long a single status read is allowed to be wrong. opencode registers\n * the turn a moment after the TUI sends the command, and a session missing\n * from `GET /session/status` reads as idle, so a `/btw` typed inside that gap\n * would decide the conversation is free and let its message queue.\n */\nconst BUSY_SETTLE_MS = 1_500\n/**\n * How long to wait for the turn's `claude` process to appear. doStream tags\n * the process only once it attaches its line listener, which is after the\n * whole spawn path, so the first `/btw` of a conversation regularly arrives\n * before there is anything to ask. Bounded, because the running turn may\n * belong to another provider and then no process is ever coming.\n */\nconst SPAWN_WAIT_MAX_MS = 30_000\n\nconst INLINE_POLL_MS = 200\n/**\n * How long to keep trying to write into the turn. A turn is a run of streams,\n * not one: every proxy tool call ends the current stream and opencode opens\n * the next one with the tool's result, so an answer that arrives inside that\n * gap has nothing to write to yet and has to wait for the next stream.\n */\nconst INLINE_WAIT_MAX_MS = 20_000\n\nconst PENDING_ANSWER_TTL_MS = 10 * 60_000\nconst PENDING_ANSWER_CAP = 32\n\ninterface PendingAnswer {\n question: string\n answer: Promise<SideQuestionResult>\n at: number\n}\n\n/** Answers the hook requested ahead of the queued prompt, one per opencode session. */\nconst pendingAnswers = new Map<string, PendingAnswer>()\n\nexport function rememberSideQuestionAnswer(\n sessionID: string,\n question: string,\n answer: Promise<SideQuestionResult>,\n now = Date.now(),\n): void {\n for (const [id, entry] of pendingAnswers) {\n if (now - entry.at > PENDING_ANSWER_TTL_MS) pendingAnswers.delete(id)\n }\n pendingAnswers.delete(sessionID)\n while (pendingAnswers.size >= PENDING_ANSWER_CAP) {\n const oldest = pendingAnswers.keys().next().value\n if (oldest === undefined) break\n pendingAnswers.delete(oldest)\n }\n pendingAnswers.set(sessionID, { question: question.trim(), answer, at: now })\n}\n\n/**\n * The answer the hook already requested for this session, if it was for this\n * question and is still fresh. Taking it consumes it: a later `/btw` with the\n * same text asks again rather than replaying a stale answer.\n *\n * The question the turn parses may be longer than what the hook saw: a\n * harness can append trailing metadata to the message text (opencode-dcp adds\n * a `<dcp-message-id>` marker), so the hook's question only has to be a prefix.\n * Measured live: an exact match missed, the turn asked again, and the\n * single-flight guard refused it as a second concurrent aside.\n */\nexport function takeSideQuestionAnswer(\n sessionID: string,\n question: string,\n now = Date.now(),\n): Promise<SideQuestionResult> | undefined {\n const entry = pendingAnswers.get(sessionID)\n if (!entry) return undefined\n pendingAnswers.delete(sessionID)\n if (!question.trim().startsWith(entry.question) || now - entry.at > PENDING_ANSWER_TTL_MS) return undefined\n return entry.answer\n}\n\n/** Test seam. */\nexport function clearPendingSideQuestionAnswers(): void {\n pendingAnswers.clear()\n}\n\n/**\n * Header of the block an aside writes into the running turn's own reply, and\n * the marker `message-builder` strips by when a transcript has to be rebuilt\n * for a fresh Claude process. Kept as the first characters of its own text\n * part so the strip is exact rather than a guess at where the block ends.\n */\nexport const INLINE_ASIDE_MARKER = \"▌ **btw:**\"\n\n/**\n * Markers of blocks written before the bar replaced the blockquote. Only the\n * strip reads these: a conversation that already holds an old aside still has\n * to keep it out of a rebuilt transcript.\n */\nexport const LEGACY_INLINE_ASIDE_MARKERS = [\"> **btw:**\"]\n\n/**\n * A literal bar on every line, blank ones included, so the aside reads as one\n * block down its whole height.\n *\n * The obvious alternative, a markdown blockquote, was tried first and is why\n * this exists: opencode renders assistant text with OpenTUI's markdown, which\n * draws a blockquote's left border in the `conceal` scope's colour, not the\n * theme's `markdownBlockQuote`. That border is dim by design and there is no\n * per-block way to change it, so the bar has to be text the plugin emits.\n * Line breaks survive because OpenTUI renders a paragraph from `token.raw`,\n * verbatim, rather than reflowing it.\n */\nfunction barEveryLine(text: string): string {\n return text\n .split(\"\\n\")\n .map((line) => (line.trim() === \"\" ? \"▌\" : `▌ ${line}`))\n .join(\"\\n\")\n}\n\nfunction oneLine(question: string): string {\n return question.replace(/\\s+/g, \" \").trim()\n}\n\nfunction asideHeader(question: string): string {\n return `${INLINE_ASIDE_MARKER} ${oneLine(question)}`\n}\n\nexport function formatInlineAside(question: string, answer: string): string {\n return `\\n${asideHeader(question)}\\n▌\\n${barEveryLine(answer.trim())}\\n`\n}\n\n/**\n * The receipt's trailing note. Past tense, because the block stays in the\n * conversation and an \"answering...\" would read as stale the moment the\n * answer lands.\n */\nexport const INLINE_ASIDE_SENT_NOTE = \"*sent to Claude on the side*\"\n\n/**\n * A receipt written into the running turn the moment the question goes out, so\n * a `/btw` typed mid-turn shows as taken instead of looking swallowed until\n * the answer arrives.\n *\n * It quotes the question back **in full**, which is what the operator asked\n * for: the prompt box clears on submit and no `/btw` message is ever created,\n * so this is the only place the question can be read back. It was briefly\n * capped at 240 characters and that was wrong for the same reason, since a\n * long aside would then be unreadable everywhere. The note goes on its own bar\n * line so the question is never crowded by it.\n *\n * The answer block repeats the question rather than dropping it, because the\n * model keeps streaming its own text between the two and a headerless answer\n * arriving after that reads as orphaned.\n */\nexport function formatInlineAsideAsk(question: string): string {\n return `\\n${asideHeader(question)}\\n▌ ${INLINE_ASIDE_SENT_NOTE}\\n`\n}\n\n/**\n * Writes one finished text block into a stream that is open right now.\n * Returns false when there is nothing to write to, which is the whole reason\n * the held-message path is still here.\n */\nexport type AsideSink = (text: string) => boolean\n\n/** At most one open stream per conversation, so a plain map is enough. */\nconst asideSinks = new Map<string, AsideSink>()\n\nexport function registerAsideSink(sessionID: string, sink: AsideSink): () => void {\n asideSinks.set(sessionID, sink)\n return () => {\n // Only the stream that registered may unregister: a later turn's sink\n // must survive the earlier turn's cleanup.\n if (asideSinks.get(sessionID) === sink) asideSinks.delete(sessionID)\n }\n}\n\nexport function emitAsideInline(sessionID: string, text: string): boolean {\n const sink = asideSinks.get(sessionID)\n if (!sink) return false\n try {\n return sink(text)\n } catch (error) {\n log.debug(\"btw: could not write the aside into the running turn\", { sessionID, error: errorText(error) })\n return false\n }\n}\n\n/** Test seam. */\nexport function clearAsideSinks(): void {\n asideSinks.clear()\n}\n\nexport function showToast(client: BtwSdkClient | null, body: BtwToast): void {\n // Keep the receiver: the SDK's namespace methods read `this._client`, so a\n // detached `const show = client.tui.showToast` throws at call time.\n try {\n void client?.tui?.showToast?.({ body })?.catch((error: unknown) => {\n log.debug(\"btw toast failed\", { error: errorText(error) })\n })\n } catch (error) {\n log.debug(\"btw toast failed\", { error: errorText(error) })\n }\n}\n\n/** A turn is streaming from this process, so its transcript cannot show an answer yet. */\nexport function isProcessBusy(active: Pick<ActiveProcess, \"lineEmitter\">): boolean {\n return active.lineEmitter.listenerCount(\"line\") > 0\n}\n\n/**\n * opencode's own view of the session: `busy` for the whole turn, including\n * the gaps where opencode runs a tool and no stream is attached to the\n * process, which `isProcessBusy` cannot see. `unknown` when the SDK has no\n * status route or it fails.\n */\nexport async function sessionStatus(\n client: BtwSdkClient | null,\n sessionID: string,\n): Promise<\"busy\" | \"idle\" | \"unknown\"> {\n const status = client?.session?.status\n if (!status) return \"unknown\"\n try {\n const result = await status.call(client!.session)\n const entry = result.data?.[sessionID]\n return entry && entry.type !== \"idle\" ? \"busy\" : \"idle\"\n } catch (error) {\n log.debug(\"btw: could not read session status\", { sessionID, error: errorText(error) })\n return \"unknown\"\n }\n}\n\n/**\n * Resolves once the session is no longer busy. Returns false on timeout. A\n * client without a status route resolves at once, since there is nothing to\n * wait on.\n */\nexport async function waitForSessionIdle(\n client: BtwSdkClient | null,\n sessionID: string,\n options: { pollMs?: number; timeoutMs?: number; stop?: () => boolean } = {},\n): Promise<boolean> {\n const pollMs = options.pollMs ?? IDLE_POLL_MS\n const timeoutMs = options.timeoutMs ?? IDLE_WAIT_MAX_MS\n const started = Date.now()\n for (;;) {\n if (options.stop?.()) return true\n if ((await sessionStatus(client, sessionID)) !== \"busy\") return true\n if (Date.now() - started >= timeoutMs) return false\n await new Promise((resolve) => setTimeout(resolve, pollMs))\n }\n}\n\n/**\n * Puts the answer in the conversation while the turn that prompted it is\n * still running, by writing it as its own text block into that turn's live\n * stream. It lands in the assistant reply the operator is already watching:\n * full markdown, scrollable, kept by opencode, and readable long after a\n * toast would have gone.\n *\n * Retries while the conversation stays busy, because a turn is a run of\n * streams rather than one and the gap between two of them is short. Gives up\n * once the turn ends, leaving the message to carry the answer instead.\n */\nexport async function deliverAsideInline(\n client: BtwSdkClient | null,\n sessionID: string,\n text: string,\n options: BtwWaitOptions = {},\n): Promise<boolean> {\n const pollMs = options.inlinePollMs ?? INLINE_POLL_MS\n const timeoutMs = options.inlineWaitMs ?? INLINE_WAIT_MAX_MS\n const started = Date.now()\n for (;;) {\n if (emitAsideInline(sessionID, text)) return true\n if (Date.now() - started >= timeoutMs) return false\n if ((await sessionStatus(client, sessionID)) !== \"busy\") return false\n await new Promise((resolve) => setTimeout(resolve, pollMs))\n }\n}\n\n/**\n * The `claude` process serving this conversation, waiting for it when a turn\n * is already running but has not yet reached the point where doStream tags it\n * (`claude-code-language-model.ts`, where the line listener attaches). That\n * gap is the whole spawn path on a conversation's first turn, and a `/btw`\n * typed inside it used to fall straight through, which is exactly what leaves\n * a \"Queued\" bubble in the transcript: measured live on 2026-09-06, a `/btw`\n * logged \"no live claude process for session\" and the same question 22 s\n * later found one and was answered concurrently.\n */\nexport async function waitForAsideProcess(\n client: BtwSdkClient | null,\n sessionID: string,\n options: BtwWaitOptions = {},\n): Promise<ActiveProcess | undefined> {\n const pollMs = options.pollMs ?? IDLE_POLL_MS\n const settleMs = options.settleMs ?? BUSY_SETTLE_MS\n const spawnWaitMs = options.spawnWaitMs ?? SPAWN_WAIT_MAX_MS\n const started = Date.now()\n for (;;) {\n const active = findActiveProcessBySessionId(sessionID)\n if (active) return active\n const busy = (await sessionStatus(client, sessionID)) === \"busy\"\n const waitedMs = Date.now() - started\n if (!busy && waitedMs >= settleMs) {\n // Nothing is running, so no process is on its way either.\n log.info(\"btw: no live claude process for session, leaving it to the turn\", { sessionID, waitedMs })\n return undefined\n }\n if (busy && waitedMs >= spawnWaitMs) {\n // A turn is running but it never produced a process of ours: it belongs\n // to another provider, or the spawn failed. Do not hold the message for\n // the rest of it.\n log.warn(\"btw: a turn is running but no claude process appeared for it\", { sessionID, waitedMs })\n return undefined\n }\n await new Promise((resolve) => setTimeout(resolve, pollMs))\n }\n}\n\n/**\n * Whether a turn is running, tolerant of the same registration lag: a status\n * read taken the instant `/btw` is typed can still say idle while opencode is\n * starting the turn, and skipping the hold on that reading is what queues the\n * message behind the turn instead of releasing it afterwards.\n */\nexport async function settleSessionBusy(\n client: BtwSdkClient | null,\n sessionID: string,\n active: Pick<ActiveProcess, \"lineEmitter\">,\n options: BtwWaitOptions = {},\n): Promise<boolean> {\n const pollMs = options.pollMs ?? IDLE_POLL_MS\n const settleMs = options.settleMs ?? BUSY_SETTLE_MS\n const started = Date.now()\n for (;;) {\n const status = await sessionStatus(client, sessionID)\n if (status === \"busy\") return true\n // No status route to poll: the process's own stream is all there is.\n if (status === \"unknown\") return isProcessBusy(active)\n if (Date.now() - started >= settleMs) return false\n await new Promise((resolve) => setTimeout(resolve, pollMs))\n }\n}\n\nfunction errorText(error: unknown): string {\n if (error instanceof Error) return error.message\n if (error && typeof error === \"object\" && \"message\" in error && typeof (error as { message: unknown }).message === \"string\") {\n return (error as { message: string }).message\n }\n return String(error)\n}\n\nfunction isTextPart(part: unknown): part is { type: \"text\"; text: string } {\n return (\n part !== null &&\n typeof part === \"object\" &&\n (part as { type?: unknown }).type === \"text\" &&\n typeof (part as { text?: unknown }).text === \"string\"\n )\n}\n\n/**\n * Earlier `/btw` exchanges in this conversation, read back from opencode\n * because the hook runs before the current question exists as a message.\n * Best effort: a follow-up without history still gets an answer, just one\n * that cannot refer to previous asides.\n */\nexport async function fetchAsideHistory(\n client: BtwSdkClient | null,\n sessionID: string,\n question: string,\n): Promise<SideQuestionExchange[]> {\n const messages = client?.session?.messages\n if (!messages) return []\n try {\n const result = await messages.call(client!.session, { path: { id: sessionID } })\n const prompt: { role: string; content: unknown }[] = []\n for (const message of result.data ?? []) {\n const role = message.info?.role\n if (role !== \"user\" && role !== \"assistant\") continue\n prompt.push({ role, content: (message.parts ?? []).filter(isTextPart) })\n }\n // collectSideQuestionHistory skips the final message as the question being\n // asked; stand in for the one opencode has not created yet.\n prompt.push({ role: \"user\", content: `/btw ${question}` })\n return collectSideQuestionHistory(prompt)\n } catch (error) {\n log.debug(\"btw: could not read aside history\", { sessionID, error: errorText(error) })\n return []\n }\n}\n\n/**\n * `command.execute.before` handler for `btw`. Returns normally so opencode\n * creates the `/btw` message in this conversation; throws only when there is\n * nothing to keep (a bare `/btw`, or a turn that never ended).\n *\n * While the session is busy the return is delayed until it is idle. opencode\n * would otherwise queue the message behind the running turn and run it as\n * that turn's next step, which is also the step that carries the results of\n * the tools opencode just ran: answering the aside there would swallow the\n * turn's own continuation (measured live: the main answer never appeared).\n * opencode already keeps the command route open for a queued prompt, so\n * holding it here changes nothing on the wire, and the TUI's call is\n * fire-and-forget.\n *\n * Both waits before that hold exist because a `/btw` typed early in a turn\n * used to be seen as belonging to an idle conversation with no process, and\n * was let through to be queued: `waitForAsideProcess` covers the spawn gap,\n * `settleSessionBusy` covers opencode registering the turn.\n */\nexport async function handleBtwCommand(\n client: BtwSdkClient | null,\n input: BtwCommandInput,\n options: BtwWaitOptions = {},\n): Promise<void> {\n const question = input.arguments.trim()\n if (!question) {\n showToast(client, { title: \"btw\", message: SIDE_QUESTION_USAGE, variant: \"warning\", duration: 6_000 })\n throw new BtwHandledError(\"/btw needs a question.\")\n }\n const active = await waitForAsideProcess(client, input.sessionID, options)\n const transport = active?.asideTransport\n if (!active || !transport) {\n // The message still goes through: the aside branch answers it with an\n // explanation that stays readable in the conversation.\n if (active) log.info(\"btw: process has no aside transport, leaving it to the turn\", { sessionID: input.sessionID })\n return\n }\n let busy = false\n let inlineDone = false\n let markInlineDelivered = (): void => {}\n const inlineDelivered = new Promise<\"inline\">((resolve) => {\n markInlineDelivered = () => {\n inlineDone = true\n resolve(\"inline\")\n }\n })\n if (isSideQuestionPending(active)) {\n // One aside per process at a time. Leave the earlier answer in place for\n // its own message; this one asks when its turn comes.\n busy = await settleSessionBusy(client, input.sessionID, active, options)\n log.info(\"btw: an aside is already in flight, leaving this one to the turn\", { sessionID: input.sessionID, busy })\n } else {\n // Settled alongside the request rather than before it: an aside asked\n // while the conversation is idle must not wait out the settle window\n // before it is even sent.\n const settling = settleSessionBusy(client, input.sessionID, active, options)\n const history = await fetchAsideHistory(client, input.sessionID, question)\n const answer = requestSideQuestion(active, question, {\n cliVersion: await detectCliVersion(transport.cliPath),\n interactive: transport.interactive,\n ...(history.length ? { history } : {}),\n })\n // Handled from this tick on. The settle below can span several timer\n // ticks, and an aside that fails immediately (a dead process, an\n // interactive transport) would otherwise raise an unhandled rejection in\n // the host before the real handlers further down are attached.\n answer.catch(() => undefined)\n rememberSideQuestionAnswer(input.sessionID, question, answer)\n busy = await settling\n log.info(\"btw: aside sent ahead of its message\", {\n sessionID: input.sessionID,\n busy,\n questionLength: question.length,\n history: history.length,\n })\n // Written before the answer exists, so a `/btw` typed mid-turn shows up in\n // the turn straight away rather than looking swallowed until the answer\n // arrives. Only while a turn is running: an idle conversation gets the\n // whole pair as its own message a moment later anyway.\n const asked = busy\n ? deliverAsideInline(client, input.sessionID, formatInlineAsideAsk(question), options).catch(() => false)\n : Promise.resolve(false)\n answer.then(\n async (result) => {\n log.info(\"btw: early answer arrived\", { sessionID: input.sessionID, busy, responseLength: result.response.length })\n if (!busy || result.synthetic) return\n // Awaited, not raced: a receipt that landed after the answer it\n // announces would read backwards. In the common case it was written\n // long before this and the await is already settled.\n await asked\n const inline = await deliverAsideInline(\n client,\n input.sessionID,\n formatInlineAside(question, result.response),\n options,\n )\n if (inline) {\n // The answer is in the conversation already, so the `/btw` message\n // has nothing left to carry. The remembered answer is deliberately\n // left in place: if the drop below does not take, the message\n // replays this answer instead of paying for a second one.\n log.info(\"btw: answer written into the running turn\", { sessionID: input.sessionID })\n markInlineDelivered()\n return\n }\n // Nothing was open to write to. The held `/btw` message carries this\n // same answer into the conversation once the turn ends, which is the\n // durable copy, so there is nothing to announce here.\n log.info(\"btw: no open stream for the answer; the held message will carry it\", {\n sessionID: input.sessionID,\n })\n },\n (error: unknown) => {\n // The message asks again once its turn runs.\n log.warn(\"btw: early aside failed; the message will ask again\", {\n sessionID: input.sessionID,\n error: errorText(error),\n })\n },\n )\n }\n if (!busy) return\n const started = Date.now()\n const outcome = await Promise.race([\n inlineDelivered,\n waitForSessionIdle(client, input.sessionID, { ...options, stop: () => inlineDone }).then((idle) =>\n idle ? (\"idle\" as const) : (\"timeout\" as const),\n ),\n ])\n if (outcome === \"inline\") {\n log.info(\"btw: answered inside the running turn, dropping the /btw message\", {\n sessionID: input.sessionID,\n waitedMs: Date.now() - started,\n })\n throw new BtwHandledError(BTW_INLINE_HANDLED_MESSAGE)\n }\n log.info(\"btw: turn over, releasing the /btw message\", {\n sessionID: input.sessionID,\n idle: outcome === \"idle\",\n waitedMs: Date.now() - started,\n })\n if (outcome === \"timeout\") {\n showToast(client, { title: \"btw\", message: BTW_TURN_TOO_LONG_MESSAGE, variant: \"warning\", duration: 8_000 })\n throw new BtwHandledError(BTW_TURN_TOO_LONG_MESSAGE)\n }\n}\n","import { execFile } from \"node:child_process\"\nimport * as fs from \"node:fs\"\nimport * as path from \"node:path\"\nimport { promisify } from \"node:util\"\nimport { fileURLToPath } from \"node:url\"\n\nimport { detectCliVersion } from \"./cli-version.js\"\nimport { log } from \"./logger.js\"\nimport { mergeOpencodeMcp } from \"./mcp-bridge.js\"\nimport { getOpencodeProjectDirectory, isUsableDirectory } from \"./runtime-status.js\"\n\n/**\n * One compact status block logged once per process, right after providers are\n * registered. Every field here answers a question that previously cost a live\n * debugging session: which plugin build is loaded, whether the Claude CLI is\n * even reachable, which cwd the spawn will use and why, what is proxied, and\n * how many MCP servers the bridge sees. Keep it cheap and never let it throw:\n * diagnostics must not be able to break provider registration.\n */\nexport interface StartupDiagnostics {\n plugin: string\n opencode: string\n claudeCli: { path: string; version: string }\n cwd: { resolved: string; source: CwdSource }\n providers: string[]\n accounts: string[]\n proxyTools: string[]\n mcpServers: string[]\n interactiveTransport: boolean\n /** ExitPlanMode approval routed through opencode's `question` tool. */\n planModeQuestion: boolean\n anthropicApiKeyInEnv: boolean\n}\n\n/** Which branch of `resolveSpawnCwd` a Claude CLI spawn would take right now. */\nexport type CwdSource = \"configured\" | \"process\" | \"captured\" | \"unresolved\"\n\nexport interface DiagnosticsProviderEntry {\n name?: string\n options?: Record<string, unknown>\n}\n\nlet cachedPluginVersion: string | undefined\n\n/** Version of this plugin, read from the package manifest one level up. */\nexport function pluginVersion(): string {\n if (cachedPluginVersion) return cachedPluginVersion\n try {\n const here = path.dirname(fileURLToPath(import.meta.url))\n const raw = fs.readFileSync(path.join(here, \"..\", \"package.json\"), \"utf8\")\n const version = (JSON.parse(raw) as { version?: unknown }).version\n cachedPluginVersion = typeof version === \"string\" ? version : \"unknown\"\n } catch {\n cachedPluginVersion = \"unknown\"\n }\n return cachedPluginVersion\n}\n\n/**\n * Best-effort opencode version from the plugin input. Re-verified on opencode\n * 1.18.5: nothing on the plugin surface carries it. `PluginInput` has no\n * version field, the SDK client's `app` namespace exposes only `log`/`agents`,\n * and the server has no `/version` route. So this probes a couple of plausible\n * shapes for future opencode releases and otherwise returns undefined, leaving\n * the binary probe (`detectOpencodeVersion`) as the fallback. Do not replace it\n * with a `client.app.get()` call — that method does not exist.\n */\nexport function pickOpencodeVersion(input: unknown): string | undefined {\n if (!input || typeof input !== \"object\") return undefined\n const app = (input as { app?: unknown }).app\n if (app && typeof app === \"object\") {\n const version = (app as { version?: unknown }).version\n if (typeof version === \"string\" && version.length > 0) return version\n }\n const direct = (input as { version?: unknown }).version\n if (typeof direct === \"string\" && direct.length > 0) return direct\n return undefined\n}\n\nconst execFileAsync = promisify(execFile)\n\nlet opencodeVersionProbe: Promise<string | undefined> | undefined\n\n/**\n * The plugin runs *inside* opencode's process, so `process.execPath` is the\n * opencode binary itself — asking it for `--version` is the only reliable way\n * to name the version, since the plugin API exposes it nowhere (see\n * `pickOpencodeVersion`). Guarded on the basename: when opencode is run from\n * source (`bun run packages/opencode/src/index.ts`) execPath is the Bun binary,\n * and reporting Bun's version as opencode's would be worse than \"unknown\".\n * Cached, 5s timeout, never throws.\n */\nexport function detectOpencodeVersion(\n execPath: string = process.execPath,\n): Promise<string | undefined> {\n if (opencodeVersionProbe) return opencodeVersionProbe\n opencodeVersionProbe = (async (): Promise<string | undefined> => {\n if (!path.basename(execPath).toLowerCase().includes(\"opencode\")) {\n log.debug(\"skipping opencode version probe: execPath is not opencode\", { execPath })\n return undefined\n }\n try {\n const { stdout } = await execFileAsync(execPath, [\"--version\"], { timeout: 5000 })\n const match = /\\d+\\.\\d+\\.\\d+\\S*/.exec(stdout.trim())\n return match ? match[0] : undefined\n } catch (err) {\n log.debug(\"opencode version probe failed\", {\n execPath,\n error: err instanceof Error ? err.message : String(err),\n })\n return undefined\n }\n })()\n return opencodeVersionProbe\n}\n\n/** Test seam: drop the cached probe so a fresh execPath is honored. */\nexport function resetOpencodeVersionProbe(): void {\n opencodeVersionProbe = undefined\n}\n\n/**\n * Mirror of `resolveSpawnCwd`'s priority order, but reporting *which* branch\n * won. `configured` means `options.cwd` pinned it, `process` is the normal\n * lazy path, `captured` means `process.cwd()` was unusable (macOS GUI launch\n * at `/`) and the captured project directory rescued it — that one is the\n * fingerprint of issue #4.\n */\nexport function describeSpawnCwd(\n configured: unknown,\n live: string = process.cwd(),\n captured: string | undefined = getOpencodeProjectDirectory(),\n): { resolved: string; source: CwdSource } {\n if (typeof configured === \"string\" && configured.length > 0) {\n return { resolved: configured, source: \"configured\" }\n }\n if (isUsableDirectory(live)) return { resolved: live, source: \"process\" }\n if (isUsableDirectory(captured)) return { resolved: captured, source: \"captured\" }\n return { resolved: live, source: \"unresolved\" }\n}\n\nfunction stringList(value: unknown): string[] {\n if (!Array.isArray(value)) return []\n return value.filter((entry): entry is string => typeof entry === \"string\")\n}\n\nfunction firstOption(\n providers: Record<string, DiagnosticsProviderEntry>,\n key: string,\n): unknown {\n for (const entry of Object.values(providers)) {\n const value = entry?.options?.[key]\n if (value !== undefined) return value\n }\n return undefined\n}\n\nexport function collectStartupDiagnostics(\n providers: Record<string, DiagnosticsProviderEntry>,\n opencodeVersion?: string,\n): Omit<StartupDiagnostics, \"claudeCli\"> & { claudeCliPath: string } {\n const accounts: string[] = []\n for (const entry of Object.values(providers)) {\n const account = entry?.options?.account\n if (typeof account === \"string\" && account.length > 0) accounts.push(account)\n }\n\n const cwd = describeSpawnCwd(firstOption(providers, \"cwd\"))\n\n let mcpServers: string[] = []\n try {\n // Disk-only view: opencode's runtime MCP status isn't settled at plugin\n // init (servers are still connecting), so the per-turn overlay is not\n // applied here. This is what the bridge would ship on a cold start.\n mcpServers = mergeOpencodeMcp(cwd.resolved).enabledServerNames\n } catch (err) {\n log.debug(\"startup diagnostics could not read MCP config\", {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n\n return {\n plugin: pluginVersion(),\n opencode: opencodeVersion ?? process.env.OPENCODE_VERSION ?? \"unknown\",\n claudeCliPath: String(firstOption(providers, \"cliPath\") ?? \"claude\"),\n cwd,\n providers: Object.keys(providers),\n accounts,\n proxyTools: stringList(firstOption(providers, \"proxyTools\")),\n mcpServers,\n interactiveTransport:\n firstOption(providers, \"interactive\") === true ||\n process.env.CLAUDE_CODE_INTERACTIVE_TRANSPORT === \"1\",\n planModeQuestion: firstOption(providers, \"planModeQuestion\") === true,\n anthropicApiKeyInEnv: Boolean(\n process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN,\n ),\n }\n}\n\nlet logged = false\nlet lastProviders: Record<string, DiagnosticsProviderEntry> = {}\nlet lastOpencodeVersion: string | undefined\n\n/**\n * The providers the config hook last registered, so `/claude-code-doctor` can\n * re-run `collectStartupDiagnostics` on demand instead of reporting a snapshot\n * frozen at startup. Kept here rather than in the doctor because this is\n * already the module that owns the shape.\n */\nexport function lastDiagnosticsProviders(): Record<string, DiagnosticsProviderEntry> {\n return lastProviders\n}\n\nexport function lastKnownOpencodeVersion(): string | undefined {\n return lastOpencodeVersion\n}\n\n/**\n * Emit the startup block once per process. Fire-and-forget: the Claude CLI\n * version probe is async (`claude --version`, 5s timeout, cached), and a slow\n * or missing binary must never delay provider registration.\n */\nexport function logStartupDiagnostics(\n providers: Record<string, DiagnosticsProviderEntry>,\n opencodeVersion?: string,\n): void {\n // Recorded before the once-per-process guard: an account expansion calls\n // this a second time with the real provider set, and the doctor should read\n // that one rather than the pre-expansion view.\n lastProviders = providers\n if (opencodeVersion) lastOpencodeVersion = opencodeVersion\n if (logged) return\n logged = true\n void (async () => {\n try {\n // Probe the binary only when the plugin input and env gave us nothing,\n // so a future opencode that reports its version costs no spawn.\n const version =\n opencodeVersion ?? process.env.OPENCODE_VERSION ?? (await detectOpencodeVersion())\n const { claudeCliPath, ...rest } = collectStartupDiagnostics(providers, version)\n const cli = await detectCliVersion(claudeCliPath)\n const diagnostics: StartupDiagnostics = {\n ...rest,\n claudeCli: { path: claudeCliPath, version: cli?.raw ?? \"not detected\" },\n }\n log.notice(\"claude-code plugin ready\", { ...diagnostics })\n } catch (err) {\n log.debug(\"startup diagnostics failed\", {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n })()\n}\n\n/** For tests. */\nexport function _resetStartupDiagnostics(): void {\n logged = false\n lastProviders = {}\n lastOpencodeVersion = undefined\n}\n","import * as fs from \"node:fs\"\nimport * as path from \"node:path\"\nimport * as os from \"node:os\"\nimport * as crypto from \"node:crypto\"\nimport {\n parse as parseJsonc,\n printParseErrorCode,\n type ParseError,\n} from \"jsonc-parser\"\nimport { log } from \"./logger.js\"\nimport { pluginTmpDir } from \"./tmp.js\"\n\n/**\n * Bridge opencode's `mcp` config block into a Claude CLI `--mcp-config` file.\n *\n * Opencode core schema (packages/opencode/src/config/mcp.ts):\n * {\n * \"mcp\": {\n * \"name\": {\n * \"type\": \"local\" | \"remote\",\n * \"command\"?: string[], // local\n * \"environment\"?: Record<string,string>,\n * \"url\"?: string, // remote\n * \"headers\"?: Record<string,string>,\n * \"oauth\"?: object | false, // remote — NOT bridged (Claude --mcp-config has no slot)\n * \"timeout\"?: number, // NOT bridged (Claude --mcp-config has no slot)\n * \"enabled\"?: boolean\n * }\n * }\n * }\n *\n * Claude CLI `--mcp-config` schema:\n * {\n * \"mcpServers\": {\n * \"name\": {\n * \"type\": \"stdio\" | \"http\",\n * \"command\"?: string, \"args\"?: string[], \"env\"?: Record<string,string>,\n * \"url\"?: string, \"headers\"?: Record<string,string>\n * }\n * }\n * }\n *\n * Discovery + merge are aligned with opencode core's `loadInstanceState`\n * (packages/opencode/src/config/config.ts). In merge order (last wins),\n * opencode loads:\n *\n * 1. Auth `.well-known` remote configs ← NOT bridged\n * 2. Global: ~/.config/opencode/{config.json,opencode.json,opencode.jsonc}\n * — all three deep-merged, jsonc highest priority\n * 3. OPENCODE_CONFIG env var (single file)\n * 4. Project walk-up: opencode.json[c] in each dir from cwd up to (not past)\n * worktree, both extensions per dir, parent-most first\n * 5. .opencode/ siblings: from cwd up + home dir + OPENCODE_CONFIG_DIR,\n * both extensions per dir, opencode-iteration order (cwd-most first\n * in walk-up — so parent-most `.opencode/` wins, matching upstream)\n * 6. OPENCODE_CONFIG_CONTENT env var (inline JSON) ← NOT bridged\n * 7. Active org remote config ← NOT bridged\n * 8. Managed config dir / macOS MDM ← NOT bridged\n *\n * Sources marked NOT bridged are niche and would require live opencode\n * runtime state (auth tokens, account context, MDM access). Document them\n * here so the gap is explicit; functionality of the common path is intact.\n *\n * Per-server merge is deep-merge (matching opencode's `mergeConfigConcatArrays`\n * → `mergeDeep`), so a project layer can override one field of a global server\n * spec — e.g. `{ \"linear\": { \"enabled\": true } }` lifts global linear's URL.\n */\n\nconst FILE_NAMES = [\"opencode.jsonc\", \"opencode.json\", \"config.json\"] as const\nconst PROJECT_FILE_NAMES = [\"opencode.json\", \"opencode.jsonc\"] as const\n\nfunction fileExists(p: string): boolean {\n try {\n return fs.statSync(p).isFile()\n } catch {\n return false\n }\n}\n\nfunction dirExists(p: string): boolean {\n try {\n return fs.statSync(p).isDirectory()\n } catch {\n return false\n }\n}\n\nfunction readAndParse(file: string): Record<string, unknown> | null {\n try {\n const raw = fs.readFileSync(file, \"utf8\")\n const errors: ParseError[] = []\n const parsed = parseJsonc(raw, errors, { allowTrailingComma: true })\n if (errors.length > 0) {\n const first = errors[0]\n throw new Error(\n `${printParseErrorCode(first.error)} at offset ${first.offset}`,\n )\n }\n return parsed as Record<string, unknown>\n } catch (e) {\n log.warn(\"failed to parse opencode config\", {\n file,\n error: e instanceof Error ? e.message : String(e),\n })\n return null\n }\n}\n\n/**\n * Deep merge two plain-object trees. Arrays and primitives are replaced\n * (not concatenated). Matches the effective behavior of opencode's\n * `mergeDeep` from `remeda` for the MCP block — opencode does not special\n * case array fields inside `mcp.<server>` (its only special case is\n * `instructions`, which is concat-deduped at the config root).\n */\nfunction isPlainObject(x: unknown): x is Record<string, unknown> {\n return typeof x === \"object\" && x !== null && !Array.isArray(x)\n}\n\nfunction deepMerge(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = { ...target }\n for (const [k, v] of Object.entries(source)) {\n if (v === undefined) continue\n const existing = out[k]\n if (isPlainObject(existing) && isPlainObject(v)) {\n out[k] = deepMerge(existing, v)\n } else {\n out[k] = v\n }\n }\n return out\n}\n\n/**\n * Walk up from `start` toward filesystem root (or `stop` if provided),\n * collecting paths where each `target` exists. Mirrors opencode core's\n * `FileSystem.up` (packages/core/src/filesystem.ts): cwd-most first,\n * parent-most last.\n */\nfunction walkUp(opts: {\n start: string\n stop?: string\n targets: readonly string[]\n predicate: (p: string) => boolean\n}): string[] {\n const out: string[] = []\n let current = path.resolve(opts.start)\n while (true) {\n for (const target of opts.targets) {\n const candidate = path.join(current, target)\n if (opts.predicate(candidate)) out.push(candidate)\n }\n if (opts.stop && current === path.resolve(opts.stop)) break\n const parent = path.dirname(current)\n if (parent === current) break\n current = parent\n }\n return out\n}\n\n/**\n * Find the worktree root by walking up from `cwd` looking for a `.git`\n * entry (file or directory — submodules use a file). If no `.git` is\n * found, walk to filesystem root. Honors OPENCODE_WORKTREE override.\n */\nfunction detectWorktree(cwd: string): string | undefined {\n const override = process.env.OPENCODE_WORKTREE\n if (override) return path.resolve(override)\n let current = path.resolve(cwd)\n while (true) {\n const gitPath = path.join(current, \".git\")\n try {\n if (fs.existsSync(gitPath)) return current\n } catch {\n // ignore\n }\n const parent = path.dirname(current)\n if (parent === current) return undefined\n current = parent\n }\n}\n\nfunction globalConfigDir(): string {\n const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), \".config\")\n return path.join(xdg, \"opencode\")\n}\n\n/**\n * Load the merged global config from `~/.config/opencode/`. Mirrors\n * opencode core's `loadGlobal`: deep-merges config.json → opencode.json\n * → opencode.jsonc in that order (jsonc wins).\n */\nfunction loadGlobalConfig(): Record<string, unknown> {\n const dir = globalConfigDir()\n let merged: Record<string, unknown> = {}\n for (const name of FILE_NAMES.slice().reverse()) {\n // FILE_NAMES is jsonc-first; reverse to get config.json-first order.\n const file = path.join(dir, name)\n if (!fileExists(file)) continue\n const parsed = readAndParse(file)\n if (parsed) merged = deepMerge(merged, parsed)\n }\n return merged\n}\n\n/** Load both `opencode.json` and `opencode.jsonc` in `dir`, deep-merged. */\nfunction loadProjectFilesInDir(dir: string): Record<string, unknown> {\n let merged: Record<string, unknown> = {}\n for (const name of PROJECT_FILE_NAMES) {\n const file = path.join(dir, name)\n if (!fileExists(file)) continue\n const parsed = readAndParse(file)\n if (parsed) merged = deepMerge(merged, parsed)\n }\n return merged\n}\n\n/**\n * Build the list of `.opencode/` directories to consider, in opencode core's\n * order (matching `ConfigPaths.directories`):\n * project walk-up (cwd-most first) → home-dir `.opencode/` → OPENCODE_CONFIG_DIR\n */\nfunction dotOpencodeDirs(cwd: string, worktree?: string): string[] {\n const dirs: string[] = []\n const seen = new Set<string>()\n const push = (p: string) => {\n const abs = path.resolve(p)\n if (!seen.has(abs) && dirExists(abs)) {\n seen.add(abs)\n dirs.push(abs)\n }\n }\n\n for (const dir of walkUp({\n start: cwd,\n stop: worktree,\n targets: [\".opencode\"],\n predicate: dirExists,\n })) {\n push(dir)\n }\n\n const home = os.homedir()\n if (home) {\n const homeDot = path.join(home, \".opencode\")\n if (dirExists(homeDot)) push(homeDot)\n }\n\n const envDir = process.env.OPENCODE_CONFIG_DIR\n if (envDir && dirExists(envDir)) push(envDir)\n\n return dirs\n}\n\ninterface OpencodeLocalServer {\n type?: \"local\"\n command?: string[]\n environment?: Record<string, string>\n enabled?: boolean\n}\n\ninterface OpencodeRemoteServer {\n type?: \"remote\"\n url?: string\n headers?: Record<string, string>\n enabled?: boolean\n}\n\ntype OpencodeServer = OpencodeLocalServer | OpencodeRemoteServer | { enabled?: boolean }\n\n/**\n * Substitute opencode's `{env:VAR}` interpolation in a string-keyed record\n * using values from `process.env`. Returns a new object. If the source is\n * not a flat string-valued record, returns it unchanged.\n *\n * Opencode performs this substitution itself when it spawns MCP servers\n * directly, but the spec we read from disk still contains the literal\n * placeholders. Without substituting them here, Claude CLI hands the\n * literal string `{env:FOO}` to the MCP subprocess as the env value, and\n * any server that validates credentials at startup (e.g. slack-mcp-server)\n * crashes before exposing tools. Servers that defer validation to\n * request time (e.g. github-mcp-server) appear to register but every API\n * call 401s.\n */\nfunction substituteEnvPlaceholders(\n source: Record<string, unknown>,\n): Record<string, string> {\n const out: Record<string, string> = {}\n for (const [k, v] of Object.entries(source)) {\n if (typeof v !== \"string\") continue\n out[k] = v.replace(/\\{env:([A-Za-z_][A-Za-z0-9_]*)\\}/g, (_match, name) => {\n const resolved = process.env[name]\n return typeof resolved === \"string\" ? resolved : \"\"\n })\n }\n return out\n}\n\nfunction translateServer(\n name: string,\n spec: Record<string, unknown>,\n): Record<string, unknown> | null {\n if (spec.enabled === false) return null\n\n const type = spec.type\n if (type === \"local\") {\n const cmd = spec.command\n if (!Array.isArray(cmd) || cmd.length === 0) {\n log.warn(\"skipping local MCP server with no command\", { name })\n return null\n }\n const out: Record<string, unknown> = {\n type: \"stdio\",\n command: String(cmd[0]),\n }\n if (cmd.length > 1) out.args = cmd.slice(1).map((s) => String(s))\n if (spec.environment && typeof spec.environment === \"object\") {\n out.env = substituteEnvPlaceholders(\n spec.environment as Record<string, unknown>,\n )\n }\n return out\n }\n\n if (type === \"remote\") {\n if (typeof spec.url !== \"string\" || !spec.url) {\n log.warn(\"skipping remote MCP server with no url\", { name })\n return null\n }\n const out: Record<string, unknown> = {\n type: \"http\",\n url: spec.url,\n }\n if (spec.headers && typeof spec.headers === \"object\") {\n out.headers = substituteEnvPlaceholders(\n spec.headers as Record<string, unknown>,\n )\n }\n return out\n }\n\n log.warn(\"skipping MCP server with unknown type\", {\n name,\n type: type ?? null,\n })\n return null\n}\n\nfunction extractMcpBlock(\n config: Record<string, unknown>,\n): Record<string, OpencodeServer> {\n const mcp = config.mcp\n if (!mcp || typeof mcp !== \"object\" || Array.isArray(mcp)) return {}\n return mcp as Record<string, OpencodeServer>\n}\n\n/**\n * Deep-merge per-server specs from `source` into `target`. Mirrors opencode's\n * `mergeDeep` semantics for the `mcp` record: each server entry is recursively\n * merged so a partial layer (e.g. `{ \"linear\": { \"enabled\": true } }`) can\n * override one field without dropping the rest.\n */\nfunction mergeMcp(\n target: Record<string, OpencodeServer>,\n source: Record<string, OpencodeServer>,\n): Record<string, OpencodeServer> {\n const out: Record<string, OpencodeServer> = { ...target }\n for (const [name, spec] of Object.entries(source)) {\n if (!spec || typeof spec !== \"object\") continue\n const existing = out[name]\n if (existing && typeof existing === \"object\") {\n out[name] = deepMerge(\n existing as Record<string, unknown>,\n spec as Record<string, unknown>,\n ) as OpencodeServer\n } else {\n out[name] = spec\n }\n }\n return out\n}\n\nexport interface BridgedMcp {\n /** Path to the temp file containing the translated `--mcp-config`. */\n path: string\n /** Stable hash of the merged opencode mcp block (pre-translation). */\n hash: string\n /**\n * Names of opencode MCP servers that were bridged into Claude CLI's\n * `--mcp-config`. Excludes any servers passed in `excludeServers`.\n */\n serverNames: string[]\n /**\n * Names of every enabled opencode MCP server after merge + runtime\n * overlay, regardless of whether they ended up bridged or excluded.\n * Callers (e.g. the proxy-tool builder) use this to decide which\n * `<server>_<tool>` IDs in opencode's tool catalog are MCP-origin.\n */\n allEnabledServerNames: string[]\n}\n\n/** Result of merging opencode's MCP config layers + applying runtime overlay. */\nexport interface MergedMcp {\n /** Merged, overlay-applied server specs keyed by opencode server name. */\n servers: Record<string, OpencodeServer>\n /** Server names whose final spec is enabled (or implicitly enabled). */\n enabledServerNames: string[]\n /** Stable hash of the merged (pre-translation) MCP block. */\n hash: string\n}\n\n/**\n * Per-server runtime status from opencode's `client.mcp.status()`. Used as\n * an overlay on top of the on-disk merged config so opencode's UI-toggled\n * state — which lives only in-memory; `connect()`/`disconnect()` never\n * touch disk — propagates to the bridged claude subprocess.\n *\n * Treatment per server:\n * - \"connected\" → force `enabled: true` (mirror opencode)\n * - any other status → force `enabled: false` (don't ship a server\n * opencode can't run; user fixes it in opencode first)\n * - missing entry → leave disk value\n *\n * Omit the overlay and the bridge falls back to disk-only.\n */\nexport type RuntimeMcpStatus = Record<string, string>\n\n/**\n * Read opencode config layers, deep-merge their `mcp` blocks per opencode's\n * own semantics, optionally apply an opencode runtime-status overlay, then\n * translate each server to Claude CLI format, write a scratch file, and\n * return its path + a stable hash. Returns null when no enabled MCP servers\n * remain after the merge + overlay.\n */\nexport function bridgeOpencodeMcp(\n cwd: string,\n runtimeStatus?: RuntimeMcpStatus,\n excludeServers?: ReadonlySet<string>,\n): BridgedMcp | null {\n const {\n servers: merged,\n enabledServerNames: allEnabledServerNames,\n hash,\n } = mergeOpencodeMcp(cwd, runtimeStatus)\n\n // Translate every still-enabled server, skipping any caller has asked us\n // to exclude (because they're being routed through the proxy instead).\n const servers: Record<string, unknown> = {}\n const bridgedServerNames: string[] = []\n for (const [name, spec] of Object.entries(merged)) {\n if (!spec || typeof spec !== \"object\") continue\n if (excludeServers?.has(name)) continue\n const translated = translateServer(name, spec as Record<string, unknown>)\n if (translated) {\n servers[name] = translated\n bridgedServerNames.push(name)\n }\n }\n return finishBridge({\n servers,\n bridgedServerNames,\n allEnabledServerNames,\n hash,\n excludeServers,\n })\n}\n\n/**\n * Every opencode config layer that lives on disk, lowest precedence first:\n * global → `OPENCODE_CONFIG` → project walk-up → `.opencode/` siblings. The\n * order is the whole contract, so the MCP bridge and anything else reading\n * opencode's config (the V2 entrypoint's `accounts` lookup) share it rather\n * than each walking the layers themselves.\n */\nexport function opencodeConfigLayers(cwd: string): Record<string, unknown>[] {\n const worktree = detectWorktree(cwd)\n const layers: Record<string, unknown>[] = []\n\n // Layer 1: global merged\n layers.push(loadGlobalConfig())\n\n // Layer 2: OPENCODE_CONFIG (single file, applied before project walk-up)\n const explicitConfig = process.env.OPENCODE_CONFIG\n if (explicitConfig && fileExists(explicitConfig)) {\n const parsed = readAndParse(explicitConfig)\n if (parsed) layers.push(parsed)\n }\n\n // Layer 3: project walk-up — opencode.json[c] in each dir from cwd to\n // (not past) worktree, both extensions per dir. walkUp returns cwd-most\n // first; collect distinct dirs in that order then reverse for merge so\n // cwd-most wins under last-merge-wins.\n const projectFiles = walkUp({\n start: cwd,\n stop: worktree,\n targets: PROJECT_FILE_NAMES,\n predicate: fileExists,\n })\n const projectDirs: string[] = []\n const seenProjectDirs = new Set<string>()\n for (const f of projectFiles) {\n const d = path.dirname(f)\n if (!seenProjectDirs.has(d)) {\n seenProjectDirs.add(d)\n projectDirs.push(d)\n }\n }\n for (const dir of projectDirs.slice().reverse()) {\n layers.push(loadProjectFilesInDir(dir))\n }\n\n // Layer 4: `.opencode/` siblings — project walk-up then home-dir then\n // OPENCODE_CONFIG_DIR, in that order. Iteration order matches opencode's\n // (cwd-most first within walk-up), so under deep-merge \"later wins\"\n // parent-most `.opencode/` overrides cwd-most. This is upstream's\n // behavior, surprising though it is.\n for (const dir of dotOpencodeDirs(cwd, worktree)) {\n layers.push(loadProjectFilesInDir(dir))\n }\n\n return layers\n}\n\n/** The on-disk opencode config, every layer deep-merged in precedence order. */\nexport function loadMergedOpencodeConfig(cwd: string): Record<string, unknown> {\n let merged: Record<string, unknown> = {}\n for (const layer of opencodeConfigLayers(cwd)) merged = deepMerge(merged, layer)\n return merged\n}\n\n/**\n * Merge opencode's MCP config layers (see `opencodeConfigLayers`), apply the\n * opencode runtime-status overlay, and hash the result. Split out of\n * `bridgeOpencodeMcp` so read-only callers (startup diagnostics) can inspect\n * what would be bridged without translating servers or writing a scratch\n * config file.\n */\nexport function mergeOpencodeMcp(\n cwd: string,\n runtimeStatus?: RuntimeMcpStatus,\n): MergedMcp {\n let merged: Record<string, OpencodeServer> = {}\n for (const layer of opencodeConfigLayers(cwd)) {\n merged = mergeMcp(merged, extractMcpBlock(layer))\n }\n\n // Layer 5: opencode runtime overlay. opencode's `/mcps` UI toggle calls\n // `mcp.connect()` / `mcp.disconnect()` which only mutate in-memory state,\n // never the on-disk config. Without this overlay the bridge can't see\n // those toggles and claude misses servers the user just enabled.\n if (runtimeStatus) {\n for (const name of Object.keys(merged)) {\n const status = runtimeStatus[name]\n if (status === undefined) continue\n const existing = merged[name]\n const base =\n existing && typeof existing === \"object\"\n ? (existing as Record<string, unknown>)\n : {}\n merged[name] = { ...base, enabled: status === \"connected\" } as OpencodeServer\n }\n }\n\n // Compute the set of enabled server names BEFORE exclusion so callers can\n // tell whether a tool ID like `slack_conversations_add_message` came from\n // an opencode MCP server (vs a built-in tool that happens to contain `_`).\n const enabledServerNames: string[] = []\n for (const [name, spec] of Object.entries(merged)) {\n if (!spec || typeof spec !== \"object\") continue\n const enabled = (spec as { enabled?: unknown }).enabled\n if (enabled === false) continue\n enabledServerNames.push(name)\n }\n\n // Hash the pre-exclusion merged block so the hot-reload detector picks up\n // upstream config changes even when every server is excluded.\n const mergedBody = JSON.stringify({ mcpServers: merged }, null, 2)\n const hash = crypto\n .createHash(\"sha256\")\n .update(mergedBody)\n .digest(\"hex\")\n .slice(0, 12)\n\n return { servers: merged, enabledServerNames, hash }\n}\n\n/** Write the translated config (if any) and shape `bridgeOpencodeMcp`'s result. */\nfunction finishBridge(input: {\n servers: Record<string, unknown>\n bridgedServerNames: string[]\n allEnabledServerNames: string[]\n hash: string\n excludeServers?: ReadonlySet<string>\n}): BridgedMcp | null {\n const { servers, bridgedServerNames, allEnabledServerNames, hash, excludeServers } =\n input\n\n if (Object.keys(servers).length === 0) {\n const allEnabledServersExcluded =\n excludeServers &&\n allEnabledServerNames.length > 0 &&\n allEnabledServerNames.every((name) => excludeServers.has(name))\n\n if (!allEnabledServersExcluded) return null\n\n return {\n path: \"\",\n hash,\n serverNames: [],\n allEnabledServerNames,\n }\n }\n\n const body = JSON.stringify({ mcpServers: servers }, null, 2)\n // Content-addressed on purpose. `hash` covers the merged opencode config and\n // NOT `excludeServers`, so two calls that differ only in exclusions share it,\n // and the file is written only when absent. Naming the file after `hash`\n // alone therefore let the first writer win: the hot-reload probe runs first\n // with no exclusions, so the spawn's own exclusions never reached disk and a\n // server being routed through the proxy stayed in the bridged config as well,\n // reachable by both routes. That is the double execution this whole option\n // exists to prevent. The returned `hash` is unchanged, because drift\n // detection still wants to track the config rather than the exclusions.\n const bodyDigest = crypto\n .createHash(\"sha256\")\n .update(body)\n .digest(\"hex\")\n .slice(0, 12)\n const outPath = path.join(\n pluginTmpDir(),\n `mcp-${bodyDigest}.json`,\n )\n try {\n if (!fileExists(outPath)) {\n fs.writeFileSync(outPath, body, { encoding: \"utf8\", mode: 0o600 })\n }\n } catch (e) {\n log.warn(\"failed to write bridged MCP config\", {\n error: e instanceof Error ? e.message : String(e),\n })\n return null\n }\n\n log.info(\"bridged opencode MCP config\", {\n target: outPath,\n hash,\n servers: bridgedServerNames,\n excluded: excludeServers ? Array.from(excludeServers) : [],\n })\n return {\n path: outPath,\n hash,\n serverNames: bridgedServerNames,\n allEnabledServerNames,\n }\n}\n\n// Internal helpers exported for tests only.\nexport const __test = {\n deepMerge,\n mergeMcp,\n translateServer,\n substituteEnvPlaceholders,\n detectWorktree,\n loadGlobalConfig,\n loadProjectFilesInDir,\n dotOpencodeDirs,\n}\n","import type { RuntimeMcpStatus } from \"./mcp-bridge.js\"\nimport { log } from \"./logger.js\"\n\n/**\n * Captured opencode runtime context (SDK client + project directory) from\n * `PluginInput`. Lives in its own module to break the cycle that would\n * otherwise form between `index.ts` and `claude-code-language-model.ts`.\n * Values are `null`/`undefined` until the plugin's `server` factory runs\n * (e.g. early provider lookups, direct AI-SDK use, tests).\n */\ntype OpencodeClient = {\n mcp?: {\n status?: () => Promise<{ data?: unknown; error?: unknown }>\n }\n tool?: {\n list?: (options: {\n query: { provider: string; model: string; directory?: string }\n }) => Promise<{ data?: unknown; error?: unknown }>\n }\n session?: {\n /** `GET /session/{id}` — the returned Session carries `directory`. */\n get?: (options: {\n path: { id: string }\n query?: { directory?: string }\n }) => Promise<{ data?: unknown; error?: unknown }>\n /** `GET /session/status`: sessions missing from the map are idle. */\n status?: () => Promise<{ data?: unknown; error?: unknown }>\n }\n}\n\nlet opencodeClient: OpencodeClient | null = null\n\nexport function setOpencodeClient(client: unknown): void {\n if (client && typeof client === \"object\") {\n opencodeClient = client as OpencodeClient\n }\n}\n\n/**\n * The captured SDK client, untyped: callers narrow to the surface they use\n * (this module's `OpencodeClient` only mirrors the MCP/tool routes).\n */\nexport function getOpencodeClient(): unknown {\n return opencodeClient\n}\n\n/**\n * Captured opencode project directory from `PluginInput.directory` (with\n * `worktree` as secondary signal). Used as a *fallback* at Claude CLI\n * spawn time only when `process.cwd()` is unusable (macOS GUI launches\n * where launchd hands the process `cwd=/`).\n *\n * IMPORTANT: never bake this into provider config (`mergedOptions.cwd`).\n * Doing so freezes the value at plugin init and breaks workspace\n * switching mid-session, because subsequent workspace changes in\n * opencode's UI never get reflected in `this.config.cwd`. See issue #4.\n */\nlet opencodeProjectDirectory: string | undefined\n\nexport function setOpencodeProjectDirectory(dir: string | undefined): void {\n opencodeProjectDirectory = dir\n}\n\nexport function getOpencodeProjectDirectory(): string | undefined {\n return opencodeProjectDirectory\n}\n\nexport function isUsableDirectory(d: unknown): d is string {\n return typeof d === \"string\" && d.length > 1 && d !== \"/\"\n}\n\n/**\n * Resolve the cwd for a Claude CLI subprocess spawn. Priority:\n *\n * 1. Explicit `configured` value (`options.cwd` from `opencode.json`).\n * Users who pinned a directory keep their override unconditionally.\n * 2. The opencode session's own `directory` (resolved per-call from the\n * `x-session-affinity` id via the SDK). Authoritative for\n * `opencode serve` / web-UI mode, where one long-lived server process\n * handles many projects and `process.cwd()` is the server's launch\n * dir — not the session's project. Equals `process.cwd()` in the TUI,\n * so it does not regress that path.\n * 3. Live `process.cwd()` when it's a real directory. Lazy resolution\n * that lets opencode's project-aware behavior (chdir on workspace\n * switch, project-per-shell on terminal launch) flow through.\n * 4. Captured project directory from plugin init. Rescues macOS GUI\n * launches where `process.cwd()` is `/`.\n * 5. Final fallback to `process.cwd()` (returns `/` in the pathological\n * case where neither override nor capture is available).\n */\nexport function resolveSpawnCwd(configured: string | undefined): string {\n return resolveSpawnCwdFrom(\n configured,\n process.cwd(),\n opencodeProjectDirectory,\n )\n}\n\nexport function resolveSpawnCwdFrom(\n configured: string | undefined,\n live: string,\n captured: string | undefined,\n sessionDir?: string,\n): string {\n if (configured) return configured\n if (isUsableDirectory(sessionDir)) return sessionDir\n if (isUsableDirectory(live)) return live\n return captured ?? live\n}\n\n/**\n * Resolve the spawn cwd for a specific opencode session. Looks up the\n * session's `directory` via the SDK (keyed by the `x-session-affinity`\n * id opencode sets on LLM calls) and feeds it into `resolveSpawnCwdFrom`\n * as tier 2. Falls back cleanly to the non-session resolution when the\n * id is absent (\"default\"), no SDK client is captured, or the lookup\n * fails — so the TUI / direct-AI-SDK / test paths are unaffected.\n */\nexport async function resolveSpawnCwdForSession(\n configured: string | undefined,\n sessionID: string | undefined,\n): Promise<string> {\n // An explicit pin wins unconditionally — skip the lookup entirely.\n if (configured) return configured\n const sessionDir = sessionID\n ? await fetchSessionDirectory(sessionID)\n : undefined\n return resolveSpawnCwdFrom(\n configured,\n process.cwd(),\n opencodeProjectDirectory,\n sessionDir,\n )\n}\n\n/**\n * Fetch an opencode session's project directory via `GET /session/{id}`.\n * Returns `undefined` on any failure (no client, \"default\"/empty id,\n * rejected call, malformed response, unusable directory) so callers fall\n * back to `process.cwd()`-based resolution. No caching: a session's\n * directory can change (workspace switch) and the call is a cheap\n * localhost round-trip relative to spawning Claude.\n */\nexport async function fetchSessionDirectory(\n sessionID: string,\n): Promise<string | undefined> {\n if (!sessionID || sessionID === \"default\") return undefined\n const client = opencodeClient\n if (!client?.session?.get) return undefined\n try {\n const res = await client.session.get({ path: { id: sessionID } })\n const data = (res as { data?: unknown }).data\n if (!data || typeof data !== \"object\") return undefined\n const dir = (data as { directory?: unknown }).directory\n return isUsableDirectory(dir) ? dir : undefined\n } catch (err) {\n log.warn(\"failed to fetch opencode session directory\", {\n sessionID,\n error: err instanceof Error ? err.message : String(err),\n })\n return undefined\n }\n}\n\n/**\n * The id of the session that spawned this one, or undefined when it is a\n * top-level session (or the lookup is unavailable). Read off the same\n * `GET /session/{id}` response `fetchSessionDirectory` uses, kept separate\n * because the two are needed at different points in a turn.\n *\n * Account failover is the only caller: a subagent must never be shown the\n * switch form. It follows its parent's account for free, because the override\n * is scoped to the account rather than the session.\n */\nexport async function fetchSessionParentId(\n sessionID: string,\n): Promise<string | undefined> {\n if (!sessionID || sessionID === \"default\") return undefined\n const client = opencodeClient\n if (!client?.session?.get) return undefined\n try {\n const res = await client.session.get({ path: { id: sessionID } })\n const data = (res as { data?: unknown }).data\n if (!data || typeof data !== \"object\") return undefined\n const parentID = (data as { parentID?: unknown }).parentID\n return typeof parentID === \"string\" && parentID.length > 0\n ? parentID\n : undefined\n } catch (err) {\n log.warn(\"failed to fetch opencode session parent\", {\n sessionID,\n error: err instanceof Error ? err.message : String(err),\n })\n return undefined\n }\n}\n\nexport type SessionRunState = \"busy\" | \"idle\" | \"unknown\"\n\n/**\n * Whether opencode still considers this session to be running a turn.\n * `unknown` when there is no client or the route is missing, which every\n * caller must treat as \"do not act\": a wrong guess here is worse than no\n * answer.\n */\nexport async function fetchSessionRunState(\n sessionID: string,\n): Promise<SessionRunState> {\n if (!sessionID || sessionID === \"default\") return \"unknown\"\n const status = opencodeClient?.session?.status\n if (!status) return \"unknown\"\n try {\n const res = await status.call(opencodeClient!.session)\n const data = (res as { data?: unknown }).data\n if (!data || typeof data !== \"object\") return \"unknown\"\n const entry = (data as Record<string, unknown>)[sessionID]\n if (!entry || typeof entry !== \"object\") return \"idle\"\n const type = (entry as { type?: unknown }).type\n return type === \"idle\" ? \"idle\" : \"busy\"\n } catch (err) {\n log.debug(\"failed to read opencode session status\", {\n sessionID,\n error: err instanceof Error ? err.message : String(err),\n })\n return \"unknown\"\n }\n}\n\n/**\n * The session's run state, given a moment to settle. opencode 1.18.32 aborts\n * the provider signal of every step that ends in tool calls, roughly a second\n * after the finish, while it runs the tool; that looks exactly like an\n * operator abort at the signal. The session stays busy through the tool run\n * and goes idle when the turn really stops, so one `busy` reading inside the\n * window is enough to tell them apart, and the poll exists because an abort\n * can arrive before opencode has updated the map.\n */\nexport async function settleSessionRunState(\n sessionID: string,\n options: { pollMs?: number; windowMs?: number } = {},\n): Promise<SessionRunState> {\n const pollMs = options.pollMs ?? 150\n const windowMs = options.windowMs ?? 1_500\n const started = Date.now()\n let last: SessionRunState = \"unknown\"\n for (;;) {\n const state = await fetchSessionRunState(sessionID)\n if (state === \"busy\") return \"busy\"\n if (state !== \"unknown\") last = state\n if (Date.now() - started >= windowMs) return last\n await new Promise((resolve) => setTimeout(resolve, pollMs))\n }\n}\n\n/**\n * Snapshot opencode's current MCP runtime status so the bridge can overlay\n * UI-toggled state on top of disk config. Returns `undefined` on any\n * failure (no client captured, status call rejected, malformed response)\n * so the bridge falls back to disk-only.\n */\nexport async function getRuntimeMcpStatus(): Promise<\n RuntimeMcpStatus | undefined\n> {\n const client = opencodeClient\n if (!client?.mcp?.status) return undefined\n try {\n const res = await client.mcp.status()\n const data = (res as { data?: unknown }).data\n if (!data || typeof data !== \"object\") return undefined\n const out: RuntimeMcpStatus = {}\n for (const [name, entry] of Object.entries(data as Record<string, unknown>)) {\n if (entry && typeof entry === \"object\") {\n const status = (entry as { status?: unknown }).status\n if (typeof status === \"string\") out[name] = status\n }\n }\n return out\n } catch (err) {\n log.warn(\"failed to fetch opencode MCP runtime status\", {\n error: err instanceof Error ? err.message : String(err),\n })\n return undefined\n }\n}\n\nexport interface OpencodeToolListItem {\n id: string\n description: string\n parameters: Record<string, unknown>\n}\n\n/**\n * Fetch opencode's full tool catalog (built-ins + MCP-bridged) with JSON\n * Schema parameters via `client.tool.list()`. The provider/model query\n * narrows the schema variants opencode returns; in practice MCP-origin\n * tool schemas are model-agnostic, so any registered (provider, model)\n * works as the query target. Returns `undefined` on any failure so callers\n * can fall back to direct-bridge behavior.\n */\nexport async function fetchOpencodeToolList(\n provider: string,\n model: string,\n directory?: string,\n): Promise<OpencodeToolListItem[] | undefined> {\n const client = opencodeClient\n if (!client?.tool?.list) return undefined\n try {\n const res = await client.tool.list({\n query: { provider, model, ...(directory ? { directory } : {}) },\n })\n const data = (res as { data?: unknown }).data\n if (!Array.isArray(data)) return undefined\n const out: OpencodeToolListItem[] = []\n for (const entry of data as unknown[]) {\n if (!entry || typeof entry !== \"object\") continue\n const e = entry as Record<string, unknown>\n const id = typeof e.id === \"string\" ? e.id : null\n const description =\n typeof e.description === \"string\" ? e.description : \"\"\n const parameters =\n e.parameters && typeof e.parameters === \"object\"\n ? (e.parameters as Record<string, unknown>)\n : {}\n if (!id) continue\n out.push({ id, description, parameters })\n }\n return out\n } catch (err) {\n log.warn(\"failed to fetch opencode tool list\", {\n provider,\n model,\n error: err instanceof Error ? err.message : String(err),\n })\n return undefined\n }\n}\n","import { detectCliVersion } from \"./cli-version.js\"\nimport { log } from \"./logger.js\"\nimport {\n snapshotPendingProxyCalls,\n type PendingProxyCallSnapshot,\n} from \"./proxy-broker.js\"\nimport {\n snapshotActiveProcesses,\n type ActiveProcessSnapshot,\n} from \"./session-manager.js\"\nimport {\n collectStartupDiagnostics,\n detectOpencodeVersion,\n lastDiagnosticsProviders,\n lastKnownOpencodeVersion,\n type CwdSource,\n} from \"./startup-diagnostics.js\"\n\n/**\n * `/claude-code-doctor`: what the plugin thinks is happening, in the chat,\n * right now.\n *\n * The startup block already answers most of this, but it is logged once per\n * process to a file that is off by default, so in practice nobody sees it. The\n * command is answered by the plugin itself with no CLI inference, following\n * the `/btw` branch in `claude-code-language-model.ts`: the report is emitted\n * as assistant text at zero tokens, and the whole exchange is stripped from\n * any transcript rebuilt for the CLI.\n *\n * The name is `claude-code-doctor`, not `claude-code doctor`: opencode\n * commands are invoked as `/<key>` with everything after the first space taken\n * as `$ARGUMENTS`, so a space in the name would make the second word an\n * argument rather than part of the command.\n *\n * Nothing secret goes in it. Not the proxy bearer token, not the value of\n * `ANTHROPIC_API_KEY`, not the system prompt, not a pending call's arguments.\n */\n\nexport const DOCTOR_COMMAND = \"claude-code-doctor\"\n\nexport const DOCTOR_COMMAND_DESCRIPTION =\n \"Report what the Claude Code plugin sees: versions, cwd, live processes, pending proxy calls\"\n\n/** Leading marker of the report block, so `message-builder` can strip it. */\nexport const DOCTOR_MARKER = \"▌ **claude-code doctor**\"\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n}\n\n/**\n * The same `<system-reminder>` strip `/btw` needs: opencode appends its own\n * reminder blocks as extra text parts on the user message, and without this a\n * bare `/claude-code-doctor` would never look bare.\n */\nconst SYSTEM_REMINDER_BLOCK = /<system-reminder>[\\s\\S]*?<\\/system-reminder>/g\n\nexport function parseDoctorCommandContent(content: unknown): { rest: string } | null {\n let text: string\n if (typeof content === \"string\") {\n text = content\n } else if (Array.isArray(content)) {\n const parts: string[] = []\n for (const part of content) {\n if (!isRecord(part) || part.type !== \"text\" || typeof part.text !== \"string\") return null\n parts.push(part.text)\n }\n text = parts.join(\"\\n\")\n } else {\n return null\n }\n const match = new RegExp(`^/${DOCTOR_COMMAND}(?:\\\\s+([\\\\s\\\\S]*))?$`).exec(\n text.replace(SYSTEM_REMINDER_BLOCK, \"\").trim(),\n )\n return match ? { rest: (match[1] ?? \"\").trim() } : null\n}\n\n/** Only the newest user message, so a historical report is never re-run. */\nexport function parseDoctorCommand(\n prompt: readonly { role: string; content: unknown }[],\n): { rest: string } | null {\n const latest = prompt.at(-1)\n return latest?.role === \"user\" ? parseDoctorCommandContent(latest.content) : null\n}\n\nexport type ProxyAuthCheck =\n | { status: \"ok\"; code: number }\n | { status: \"unsafe\"; code: number }\n | { status: \"unreachable\"; error: string }\n | { status: \"skipped\" }\n\nexport interface DoctorProxyRow {\n url: string\n auth: ProxyAuthCheck\n}\n\nexport interface DoctorReport {\n plugin: string\n opencode: string\n claudeCli: { path: string; version: string }\n cwd: { resolved: string; source: CwdSource }\n providers: string[]\n accounts: string[]\n proxyTools: string[]\n mcpServers: string[]\n transport: \"headless\" | \"interactive\"\n planModeQuestion: boolean\n turnStats: boolean\n anthropicApiKeyInEnv: boolean\n processes: ActiveProcessSnapshot[]\n pendingCalls: PendingProxyCallSnapshot[]\n proxyServers: DoctorProxyRow[]\n}\n\nfunction formatAge(ms: number | undefined): string {\n if (ms === undefined) return \"unknown\"\n if (ms < 1000) return `${Math.round(ms)}ms`\n if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`\n const minutes = Math.floor(ms / 60_000)\n if (minutes < 60) return `${minutes}m`\n return `${Math.floor(minutes / 60)}h ${minutes % 60}m`\n}\n\nfunction list(values: string[]): string {\n return values.length ? values.join(\", \") : \"none\"\n}\n\nfunction describeAuth(auth: ProxyAuthCheck): string {\n switch (auth.status) {\n case \"ok\":\n return `${auth.code}, good`\n case \"unsafe\":\n return `${auth.code}, UNSAFE: an unauthenticated caller was accepted. Restart every opencode window; a window opened before 0.13.2 keeps serving an open port.`\n case \"unreachable\":\n return `could not be checked (${auth.error})`\n case \"skipped\":\n return \"not checked\"\n }\n}\n\n/**\n * Markdown, in one text part, led by `DOCTOR_MARKER`. Pure so a test can pin\n * the whole report against a fixed object; everything live is gathered in\n * `gatherDoctorReport`.\n */\nexport function formatDoctorReport(report: DoctorReport): string {\n const lines: string[] = []\n lines.push(DOCTOR_MARKER)\n lines.push(\"\")\n lines.push(\"| Field | Value |\")\n lines.push(\"|---|---|\")\n lines.push(`| plugin | ${report.plugin} |`)\n lines.push(`| opencode | ${report.opencode} |`)\n lines.push(`| claude CLI | \\`${report.claudeCli.path}\\` (${report.claudeCli.version}) |`)\n lines.push(`| cwd | \\`${report.cwd.resolved}\\` (${report.cwd.source}) |`)\n lines.push(`| providers | ${list(report.providers)} |`)\n lines.push(`| accounts | ${list(report.accounts)} |`)\n lines.push(`| proxyTools | ${list(report.proxyTools)} |`)\n lines.push(`| MCP servers (on disk) | ${list(report.mcpServers)} |`)\n lines.push(`| transport | ${report.transport} |`)\n lines.push(`| planModeQuestion | ${report.planModeQuestion} |`)\n lines.push(`| turnStats | ${report.turnStats} |`)\n lines.push(`| ANTHROPIC_API_KEY in env | ${report.anthropicApiKeyInEnv ? \"yes\" : \"no\"} |`)\n\n lines.push(\"\")\n lines.push(\"**Live `claude` processes**\")\n lines.push(\"\")\n if (report.processes.length === 0) {\n lines.push(\"None. The next message in a Claude Code session spawns one.\")\n } else {\n lines.push(\"| session | model | pid | in flight | age | effort |\")\n lines.push(\"|---|---|---|---|---|---|\")\n for (const proc of report.processes) {\n lines.push(\n `| ${proc.session}${proc.compaction ? \" (compaction)\" : \"\"} | ${proc.model} | ${\n proc.pid ?? \"unknown\"\n } | ${proc.inFlight ? \"yes\" : \"no\"} | ${formatAge(proc.ageMs)} | ${proc.effort ?? \"inherited\"} |`,\n )\n }\n }\n\n lines.push(\"\")\n lines.push(\"**Pending proxy calls**\")\n lines.push(\"\")\n if (report.pendingCalls.length === 0) {\n lines.push(\"None.\")\n } else {\n lines.push(\"| tool | call id | age | deadline |\")\n lines.push(\"|---|---|---|---|\")\n for (const call of report.pendingCalls) {\n // A deadline of 0 is \"none\": task calls wait for the subagent by default.\n const deadline = call.deadlineMs > 0 ? formatAge(call.deadlineMs) : \"none\"\n lines.push(\n `| ${call.toolName} | \\`${call.toolCallId}\\` | ${formatAge(call.ageMs)} | ${deadline} |`,\n )\n }\n }\n\n lines.push(\"\")\n lines.push(\"**Proxy servers**\")\n lines.push(\"\")\n if (report.proxyServers.length === 0) {\n lines.push(\"None running.\")\n } else {\n lines.push(\"| url | unauthenticated `initialize` |\")\n lines.push(\"|---|---|\")\n for (const server of report.proxyServers) {\n lines.push(`| ${server.url} | ${describeAuth(server.auth)} |`)\n }\n }\n\n const stderr = report.processes.filter((proc) => proc.lastStderr)\n if (stderr.length > 0) {\n lines.push(\"\")\n lines.push(\"**Last stderr**\")\n lines.push(\"\")\n for (const proc of stderr) {\n lines.push(`\\`${proc.session}\\`:`)\n lines.push(\"\")\n lines.push(\"```text\")\n lines.push(proc.lastStderr!.trimEnd())\n lines.push(\"```\")\n }\n }\n\n return lines.join(\"\\n\")\n}\n\n/**\n * The security probe from the README: an unauthenticated `initialize` with the\n * right Host, no Origin and a JSON content type must be refused. 401 is the\n * patched behaviour; a 200 means this opencode window predates 0.13.2 and is\n * serving an open loopback port that executes Bash through opencode.\n *\n * Deliberately only `initialize`: a `tools/call` probe would run something.\n */\nexport async function checkProxyAuth(\n url: string,\n fetchImpl: typeof fetch = fetch,\n timeoutMs = 3000,\n): Promise<ProxyAuthCheck> {\n let authority: string\n try {\n authority = new URL(url).host\n } catch {\n return { status: \"skipped\" }\n }\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n const response = await fetchImpl(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", host: authority },\n body: JSON.stringify({ jsonrpc: \"2.0\", id: 0, method: \"initialize\", params: {} }),\n signal: controller.signal,\n })\n // Drain so the socket is not left half-read.\n await response.text().catch(() => \"\")\n return response.status === 401\n ? { status: \"ok\", code: response.status }\n : { status: \"unsafe\", code: response.status }\n } catch (error) {\n return {\n status: \"unreachable\",\n error: error instanceof Error ? error.message : String(error),\n }\n } finally {\n clearTimeout(timer)\n }\n}\n\nexport interface GatherDoctorOptions {\n cliPath: string\n interactive: boolean\n turnStats: boolean\n fetchImpl?: typeof fetch\n}\n\n/** Assemble the live report. Never throws: a broken field reads as unknown. */\nexport async function gatherDoctorReport(\n options: GatherDoctorOptions,\n): Promise<DoctorReport> {\n const providers = lastDiagnosticsProviders()\n const opencodeVersion =\n lastKnownOpencodeVersion() ??\n process.env.OPENCODE_VERSION ??\n (await detectOpencodeVersion().catch(() => undefined))\n const { claudeCliPath, ...base } = collectStartupDiagnostics(providers, opencodeVersion)\n const cliPath = options.cliPath || claudeCliPath\n const cli = await detectCliVersion(cliPath).catch(() => null)\n\n const processes = snapshotActiveProcesses()\n const seen = new Set<string>()\n const proxyServers: DoctorProxyRow[] = []\n for (const proc of processes) {\n if (!proc.proxyUrl || seen.has(proc.proxyUrl)) continue\n seen.add(proc.proxyUrl)\n proxyServers.push({\n url: proc.proxyUrl,\n auth: await checkProxyAuth(proc.proxyUrl, options.fetchImpl ?? fetch),\n })\n }\n\n return {\n plugin: base.plugin,\n opencode: base.opencode,\n claudeCli: { path: cliPath, version: cli?.raw ?? \"not detected\" },\n cwd: base.cwd,\n providers: base.providers,\n accounts: base.accounts,\n proxyTools: base.proxyTools,\n mcpServers: base.mcpServers,\n transport: options.interactive || base.interactiveTransport ? \"interactive\" : \"headless\",\n planModeQuestion: base.planModeQuestion,\n turnStats: options.turnStats,\n anthropicApiKeyInEnv: base.anthropicApiKeyInEnv,\n processes,\n pendingCalls: snapshotPendingProxyCalls(),\n proxyServers,\n }\n}\n\n/** The whole command: gather, format, and never let a failure eat the answer. */\nexport async function buildDoctorReport(options: GatherDoctorOptions): Promise<string> {\n try {\n const report = await gatherDoctorReport(options)\n log.info(\"claude-code doctor report\", {\n plugin: report.plugin,\n opencode: report.opencode,\n cwd: report.cwd,\n processes: report.processes.length,\n pendingCalls: report.pendingCalls.length,\n proxyServers: report.proxyServers.map((server) => server.auth.status),\n })\n return formatDoctorReport(report)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n log.warn(\"claude-code doctor failed to build its report\", { error: message })\n return `${DOCTOR_MARKER}\\n\\nCould not build the report: ${message}`\n }\n}\n","import type { ClaudeStreamMessage } from \"./types.js\"\n\n/**\n * What a finished Claude CLI turn cost, and how much of its input came out of\n * the prompt cache.\n *\n * The CLI already reports all of it on the terminal `result` line, and until\n * now most of it was thrown away: `modelUsage` and `permission_denials` were\n * dropped outright, and the rest only reached `providerMetadata`, where\n * nothing in opencode's UI shows it. The numbers below are always logged at\n * INFO; the one-line footer is opt-in via the `turnStats` provider option,\n * because a cost line under every single reply is a preference, not a default.\n */\nexport interface TurnStats {\n costUsd?: number\n durationMs?: number\n durationApiMs?: number\n numTurns?: number\n inputTokens?: number\n outputTokens?: number\n cacheReadTokens?: number\n cacheWriteTokens?: number\n /** Per-model totals, keyed by model id. Present from CLI 2.1.x on. */\n modelUsage?: Record<string, unknown>\n /** Tool calls the permission layer refused during the turn. */\n permissionDenials?: unknown[]\n}\n\n/**\n * Header of the footer block, and the marker `message-builder` strips by when\n * a transcript is rebuilt for a fresh Claude process. The footer is the\n * plugin's own accounting, never something the model said, so it must not come\n * back as model output on a resume. Kept as the first characters of its own\n * text part so the strip is exact.\n */\nexport const TURN_STATS_MARKER = \"▌ **stats:**\"\n\n/**\n * Usage here is the turn total, not the last iteration `toUsage` prefers.\n * Those two answer different questions: `toUsage` feeds opencode's context\n * gauge, where summing every tool-use iteration would inflate the window and\n * trigger premature compaction, while a cost footer has to match the cost the\n * CLI reports, and that cost is cumulative over the whole turn.\n */\nexport function extractTurnStats(msg: ClaudeStreamMessage): TurnStats {\n const usage = msg.usage\n const stats: TurnStats = {}\n if (typeof msg.total_cost_usd === \"number\") stats.costUsd = msg.total_cost_usd\n if (typeof msg.duration_ms === \"number\") stats.durationMs = msg.duration_ms\n if (typeof msg.duration_api_ms === \"number\") stats.durationApiMs = msg.duration_api_ms\n if (typeof msg.num_turns === \"number\") stats.numTurns = msg.num_turns\n if (typeof usage?.input_tokens === \"number\") stats.inputTokens = usage.input_tokens\n if (typeof usage?.output_tokens === \"number\") stats.outputTokens = usage.output_tokens\n if (typeof usage?.cache_read_input_tokens === \"number\") {\n stats.cacheReadTokens = usage.cache_read_input_tokens\n }\n if (typeof usage?.cache_creation_input_tokens === \"number\") {\n stats.cacheWriteTokens = usage.cache_creation_input_tokens\n }\n if (msg.modelUsage && typeof msg.modelUsage === \"object\") stats.modelUsage = msg.modelUsage\n if (Array.isArray(msg.permission_denials)) stats.permissionDenials = msg.permission_denials\n return stats\n}\n\n/** Dollars, at the precision the number actually carries information at. */\nexport function formatCost(costUsd: number): string {\n if (!Number.isFinite(costUsd) || costUsd < 0) return \"$0.00\"\n return costUsd >= 1 ? `$${costUsd.toFixed(2)}` : `$${costUsd.toFixed(4)}`\n}\n\nexport function formatDuration(durationMs: number): string {\n if (!Number.isFinite(durationMs) || durationMs < 0) return \"0.0 s\"\n if (durationMs < 60_000) return `${(durationMs / 1000).toFixed(1)} s`\n const totalSeconds = Math.round(durationMs / 1000)\n return `${Math.floor(totalSeconds / 60)}m ${totalSeconds % 60}s`\n}\n\nexport function formatTokens(tokens: number): string {\n if (!Number.isFinite(tokens) || tokens < 0) return \"0\"\n if (tokens < 1000) return String(Math.round(tokens))\n if (tokens < 1_000_000) return `${(tokens / 1000).toFixed(1)}k`\n return `${(tokens / 1_000_000).toFixed(1)}M`\n}\n\n/**\n * One compact line, or null when the CLI reported nothing worth a line.\n *\n * Zero-valued cache counters are dropped rather than printed as `0`: a turn\n * with no cache activity should read as short, not as a row of zeroes. Cost,\n * duration and turn count are printed whenever the CLI sent them, including at\n * zero, because a genuinely free turn is information.\n */\nexport function formatTurnStatsLine(stats: TurnStats): string | null {\n const parts: string[] = []\n if (stats.costUsd !== undefined) parts.push(formatCost(stats.costUsd))\n if (stats.durationMs !== undefined) parts.push(formatDuration(stats.durationMs))\n if (stats.numTurns !== undefined) {\n parts.push(`${stats.numTurns} CLI ${stats.numTurns === 1 ? \"turn\" : \"turns\"}`)\n }\n if (stats.inputTokens !== undefined) parts.push(`in ${formatTokens(stats.inputTokens)}`)\n if (stats.outputTokens !== undefined) parts.push(`out ${formatTokens(stats.outputTokens)}`)\n if (stats.cacheReadTokens) parts.push(`cache read ${formatTokens(stats.cacheReadTokens)}`)\n if (stats.cacheWriteTokens) parts.push(`cache write ${formatTokens(stats.cacheWriteTokens)}`)\n if (stats.permissionDenials?.length) {\n const count = stats.permissionDenials.length\n parts.push(`${count} permission ${count === 1 ? \"denial\" : \"denials\"}`)\n }\n if (parts.length === 0) return null\n return `${TURN_STATS_MARKER} ${parts.join(\" · \")}`\n}\n\n/** The footer as its own text part: a leading newline keeps it off the reply's last line. */\nexport function formatTurnStatsBlock(stats: TurnStats): string | null {\n const line = formatTurnStatsLine(stats)\n return line === null ? null : `\\n${line}\\n`\n}\n\n/** Flat payload for the INFO line, which is emitted whether or not the footer is. */\nexport function turnStatsLogPayload(stats: TurnStats): Record<string, unknown> {\n return {\n costUsd: stats.costUsd ?? null,\n durationMs: stats.durationMs ?? null,\n durationApiMs: stats.durationApiMs ?? null,\n numTurns: stats.numTurns ?? null,\n inputTokens: stats.inputTokens ?? null,\n outputTokens: stats.outputTokens ?? null,\n cacheReadTokens: stats.cacheReadTokens ?? null,\n cacheWriteTokens: stats.cacheWriteTokens ?? null,\n modelUsage: stats.modelUsage ?? null,\n permissionDenials: stats.permissionDenials?.length ?? 0,\n }\n}\n","import type { LanguageModelV3 } from \"@ai-sdk/provider\"\nimport {\n ACCOUNT_BLOCK_MARKER,\n FAILOVER_MARKER,\n stripAccountFailoverParts,\n} from \"./account-failover.js\"\nimport { INLINE_ASIDE_MARKER, LEGACY_INLINE_ASIDE_MARKERS } from \"./btw-command.js\"\nimport {\n COMPACT_BOUNDARY_MARKER,\n CONVERSATION_RESET_MARKER,\n RATE_LIMIT_MARKER,\n RESULT_ERROR_MARKER,\n STREAM_TIMEOUT_MARKER,\n} from \"./cli-events.js\"\nimport { DOCTOR_MARKER, parseDoctorCommandContent } from \"./doctor.js\"\nimport { log } from \"./logger.js\"\nimport { parseSideQuestionContent } from \"./side-question.js\"\nimport { TURN_STATS_MARKER } from \"./turn-stats.js\"\n\ntype Prompt = Parameters<LanguageModelV3[\"doGenerate\"]>[0][\"prompt\"]\n\n/**\n * Leading markers of text parts the plugin itself wrote into an assistant\n * reply: the `/btw` aside and its pre-bar form, the turn-stats footer, and the\n * `▌` notes for a CLI compaction, a rate-limit rejection and a failed result\n * subtype. None of them was ever model output or ever in Claude's context, so\n * a transcript rebuilt for a fresh CLI process must not hand any of them back\n * as something Claude said. Each is the first characters of its own text part,\n * which is what makes the strip exact instead of a guess at where a block ends.\n */\nconst PLUGIN_NOTE_MARKERS = [\n INLINE_ASIDE_MARKER,\n ...LEGACY_INLINE_ASIDE_MARKERS,\n TURN_STATS_MARKER,\n COMPACT_BOUNDARY_MARKER,\n CONVERSATION_RESET_MARKER,\n RATE_LIMIT_MARKER,\n RESULT_ERROR_MARKER,\n DOCTOR_MARKER,\n STREAM_TIMEOUT_MARKER,\n FAILOVER_MARKER,\n ACCOUNT_BLOCK_MARKER,\n]\n\nfunction isPluginNote(part: any): boolean {\n if (!part || part.type !== \"text\" || typeof part.text !== \"string\") return false\n const text = part.text.trimStart()\n return PLUGIN_NOTE_MARKERS.some((marker) => text.startsWith(marker))\n}\n\nfunction stripPluginNotes(content: unknown): unknown {\n if (!Array.isArray(content)) return content\n const kept = content.filter((part: any) => !isPluginNote(part))\n return kept.length === content.length ? content : kept\n}\n\n/**\n * Drop every plugin-authored exchange and note from a transcript before it is\n * replayed to the CLI: the `/btw` question with its answer, the\n * `/claude-code-doctor` report with its command, and the `▌` blocks listed in\n * `PLUGIN_NOTE_MARKERS`. Named for the `/btw` case it started as; it is the\n * one place all of them are removed, and it is called from both transcript\n * rebuild paths.\n */\nexport function filterSideQuestionHistory(prompt: Prompt): Prompt {\n // The account-failover form is a synthetic `question` call Claude never\n // issued, answered by a `tool-result` it never saw. It has to come out\n // before anything is replayed, which is on the switch turn by definition.\n prompt = stripAccountFailoverParts(prompt)\n let pluginCommand = false\n const kept = prompt.filter((message) => {\n if (message.role === \"user\") {\n pluginCommand =\n parseSideQuestionContent(message.content) !== null ||\n parseDoctorCommandContent(message.content) !== null\n return !pluginCommand\n }\n return message.role !== \"assistant\" || !pluginCommand\n })\n return kept.map((message) =>\n message.role === \"assistant\" ? ({ ...message, content: stripPluginNotes(message.content) } as typeof message) : message,\n )\n}\n\n/**\n * opencode-dcp anchors its nudges into message text as\n * `<dcp-system-reminder>` blocks (its `lib/messages/inject/utils.ts` appends\n * one to an existing text part, or splices in a synthetic part), and the\n * loudest of them orders the model to \"use the `compress` tool now\". Under\n * this provider that tool is only reachable when the operator forwards it,\n * so otherwise the block is an order that cannot be obeyed, carried by every\n * message it is anchored to.\n *\n * Blocks are removed wherever they sit rather than by matching a whole part,\n * because dcp appends its own `<dcp-message-id>` marker after one and an\n * end-anchored check would miss it. That is the same trap the `/btw`\n * reminder strip hit in production.\n */\nconst DCP_REMINDER_BLOCK =\n /<dcp-system-reminder\\b[^>]*>[\\s\\S]*?<\\/dcp-system-reminder>/gi\n\n/** Remove every dcp reminder block from one piece of text. */\nexport function stripContextReminderBlocks(text: string): string {\n if (!text.includes(\"<dcp-system-reminder\")) return text\n return text.replace(DCP_REMINDER_BLOCK, \"\").replace(/\\n{3,}/g, \"\\n\\n\").trim()\n}\n\n/**\n * Whether this turn should strip those blocks: the operator opted in AND no\n * `compress` tool is being proxied. When compress IS proxied the reminder is\n * satisfiable and must survive, or the model is told to compress by nothing\n * and holds a tool it never learns it needs.\n *\n * Resolved from configuration alone so it is available before the spawn\n * block decides anything. A name that is configured but missing from\n * opencode's registry therefore counts as callable and nothing is stripped,\n * which is the conservative direction: the cost is a reminder that stays.\n */\nexport function shouldStripContextReminders(options: {\n enabled?: boolean\n proxyTools?: readonly string[]\n proxyOpencodeTools?: readonly string[]\n}): boolean {\n if (options.enabled !== true) return false\n const namesCompress = (list?: readonly string[]): boolean =>\n (list ?? []).some((name) => String(name).trim().toLowerCase() === \"compress\")\n return !namesCompress(options.proxyTools) && !namesCompress(options.proxyOpencodeTools)\n}\n\n/**\n * Strip dcp reminder blocks from every user and assistant text part.\n *\n * Emptied parts are kept as empty strings rather than dropped: a nudge is\n * sometimes a message's only text part, and removing the part outright could\n * leave a user message with no content at all, which takes the empty-content\n * sentinel path in `getClaudeUserMessage`. Every consumer here already skips\n * a falsy `text`.\n */\nexport function stripContextReminders(prompt: Prompt): {\n prompt: Prompt\n removed: number\n} {\n let removed = 0\n const countIn = (text: string): number =>\n (text.match(DCP_REMINDER_BLOCK) ?? []).length\n\n const out = prompt.map((message) => {\n if (message.role !== \"user\" && message.role !== \"assistant\") return message\n\n // AI SDK v3 always delivers user/assistant content as a part array, so\n // there is no string form to handle here.\n if (!Array.isArray(message.content)) return message\n\n let touched = false\n const parts = (message.content as any[]).map((part) => {\n if (!part || part.type !== \"text\" || typeof part.text !== \"string\") return part\n const hits = countIn(part.text)\n if (hits === 0) return part\n removed += hits\n touched = true\n return { ...part, text: stripContextReminderBlocks(part.text) }\n })\n return touched ? ({ ...message, content: parts } as typeof message) : message\n })\n\n return removed > 0 ? { prompt: out, removed } : { prompt, removed: 0 }\n}\n\nconst SUPPORTED_IMAGE_TYPES = new Set([\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n])\n\nfunction toImageBlock(part: any): any | null {\n const raw: unknown = part.image ?? part.data ?? part.url ?? part.source?.data\n if (!raw) {\n log.warn(\"file part without data, skipping\")\n return null\n }\n\n let resolvedMediaType: string = part.mediaType || part.mimeType || part.mime || \"\"\n let base64: string | null = null\n\n if (typeof raw === \"string\") {\n if (raw.startsWith(\"data:\")) {\n const match = /^data:([^;,]+)(?:;[^,]*)*(?:;base64)?,(.*)$/s.exec(raw)\n if (!match) {\n log.warn(\"malformed data URI, skipping file part\")\n return null\n }\n resolvedMediaType = resolvedMediaType || match[1]\n base64 = match[2]\n } else if (/^https?:\\/\\//i.test(raw)) {\n log.warn(\"remote URL images are not supported by Claude CLI, skipping\")\n return null\n } else {\n base64 = raw\n }\n } else if (raw instanceof URL) {\n log.warn(\"remote URL images are not supported by Claude CLI, skipping\")\n return null\n } else if (raw instanceof Uint8Array || Buffer.isBuffer(raw)) {\n base64 = Buffer.from(raw as Uint8Array).toString(\"base64\")\n } else {\n log.warn(\"unsupported file part data type\", { dataType: typeof raw })\n return null\n }\n\n if (!resolvedMediaType || !SUPPORTED_IMAGE_TYPES.has(resolvedMediaType)) {\n log.warn(\"unsupported media type for Claude image block, skipping\", {\n mediaType: resolvedMediaType,\n })\n return null\n }\n\n return {\n type: \"image\",\n source: { type: \"base64\", media_type: resolvedMediaType, data: base64 },\n }\n}\n\nfunction getToolResultText(part: any): string {\n const value = part.output ?? part.result\n\n if (typeof value === \"string\") {\n return value\n }\n\n if (!value || typeof value !== \"object\") {\n return JSON.stringify(value)\n }\n\n switch (value.type) {\n case \"text\":\n case \"error-text\":\n return String(value.value)\n case \"json\":\n case \"error-json\":\n return JSON.stringify(value.value)\n case \"execution-denied\":\n return value.reason ? `Execution denied: ${value.reason}` : \"Execution denied\"\n case \"content\":\n return Array.isArray(value.value)\n ? value.value\n .map((item: any) => {\n if (item?.type === \"text\") return item.text\n return JSON.stringify(item)\n })\n .join(\"\\n\")\n : JSON.stringify(value.value)\n default:\n return JSON.stringify(value)\n }\n}\n\n// Compaction-mode caps. These are the only knobs that affect how much\n// transcript content reaches the model when opencode invokes /compact.\n// 180k chars ≈ 60k tokens worst-case — well under Haiku 4.5's 200k window\n// after accounting for system prompt + output budget.\nconst MAX_HISTORY_CHARS = 180_000\nconst MAX_TOOL_RESULT_CHARS = 10_000\nconst MAX_TOOL_INPUT_CHARS = 2_000\n\nfunction clipWithMarker(text: string, max: number): string {\n if (text.length <= max) return text\n return `${text.slice(0, max)}\\n…[truncated ${text.length - max} chars]`\n}\n\nfunction renderToolInput(input: unknown): string {\n let raw: string\n try {\n raw = typeof input === \"string\" ? input : JSON.stringify(input)\n } catch {\n raw = String(input)\n }\n return clipWithMarker(raw, MAX_TOOL_INPUT_CHARS)\n}\n\nfunction renderMessageContentForCompaction(\n msg: any,\n): { text: string; toolResultCount: number } {\n const lines: string[] = []\n let toolResultCount = 0\n\n if (typeof msg.content === \"string\") {\n return { text: msg.content, toolResultCount: 0 }\n }\n\n if (!Array.isArray(msg.content)) {\n return { text: \"\", toolResultCount: 0 }\n }\n\n for (const part of msg.content as any[]) {\n if (!part) continue\n switch (part.type) {\n case \"text\":\n if (part.text) lines.push(part.text)\n break\n case \"tool-call\":\n lines.push(\n `[tool_use:${part.toolName ?? \"unknown\"}(${renderToolInput(part.input)})]`,\n )\n break\n case \"tool-result\":\n toolResultCount++\n lines.push(\n `[tool_result:${part.toolName ?? part.toolCallId ?? \"unknown\"}]\\n${clipWithMarker(\n getToolResultText(part),\n MAX_TOOL_RESULT_CHARS,\n )}`,\n )\n break\n case \"image\":\n lines.push(\n `[image: ${part.mediaType ?? part.mimeType ?? \"unknown\"}]`,\n )\n break\n case \"file\":\n lines.push(\n `[file: ${part.mediaType ?? part.mimeType ?? \"unknown\"}]`,\n )\n break\n case \"reasoning\":\n // Skip reasoning blocks in compaction — they bloat input without\n // helping the summarizer.\n break\n }\n }\n\n return { text: lines.join(\"\\n\"), toolResultCount }\n}\n\n/**\n * Compact conversation history into a context summary.\n *\n * - mode \"fresh-session\" (default): includes user, assistant and tool roles,\n * renders each with the same serializer /compact uses so tool inputs and\n * result bodies survive, then clips each message at 2000 chars. Used when\n * starting a fresh CLI session that lost its prior session id. It used to\n * filter to user/assistant only and reduce tool content to\n * `[Called N tool(s)]` placeholders, which silently dropped subagent\n * output entirely (issue #29).\n * - mode \"compaction\": rich serializer for opencode /compact. Includes\n * tool roles, renders tool_use input and tool_result content (each\n * clipped at MAX_TOOL_RESULT_CHARS), and caps aggregate output at\n * MAX_HISTORY_CHARS by dropping oldest entries first.\n */\nexport function compactConversationHistory(\n prompt: Prompt,\n opts: { mode?: \"fresh-session\" | \"compaction\" } = {},\n): string | null {\n const mode = opts.mode ?? \"fresh-session\"\n prompt = filterSideQuestionHistory(prompt)\n\n if (mode === \"compaction\") {\n return buildCompactionHistory(prompt)\n }\n\n // `tool`-role messages carry the results of everything opencode ran itself,\n // so they belong in the transcript. Filtering them out (issue #29) meant a\n // subagent's whole answer vanished: the assistant message kept a\n // `[Called 1 tool(s): task]` placeholder and the result it referred to was\n // never rendered at all.\n const conversationMessages = prompt.filter(\n (m) => m.role === \"user\" || m.role === \"assistant\" || m.role === \"tool\",\n )\n\n if (conversationMessages.length <= 1) {\n return null\n }\n\n const historyParts: string[] = []\n\n for (let i = 0; i < conversationMessages.length - 1; i++) {\n const msg = conversationMessages[i]\n const role =\n msg.role === \"user\" ? \"User\" : msg.role === \"assistant\" ? \"Assistant\" : \"Tool\"\n\n // Same renderer the /compact transcript uses, so tool inputs and result\n // bodies survive instead of collapsing to counts. This path used to write\n // `[Called N tool(s): ...]` / `[Received N tool result(s)]` and discard\n // every byte of the payload, which is the second half of issue #29.\n const { text } = renderMessageContentForCompaction(msg)\n\n if (text.trim()) {\n const truncated =\n text.length > 2000 ? text.slice(0, 2000) + \"...\" : text\n historyParts.push(`${role}: ${truncated}`)\n }\n }\n\n if (historyParts.length === 0) {\n return null\n }\n\n return historyParts.join(\"\\n\\n\")\n}\n\nfunction buildCompactionHistory(prompt: Prompt): string | null {\n // Iterate newest-first, accumulate up to MAX_HISTORY_CHARS, then reverse\n // to chronological order. Oldest messages get dropped when the budget\n // is exhausted — they are the least relevant for a summary of recent\n // work.\n const entries: string[] = []\n let total = 0\n let totalToolResults = 0\n let droppedOldest = 0\n\n // Skip the trailing user message: opencode's /compact appends the\n // synthesis instruction as the final user turn. The instruction itself\n // is added by getClaudeUserMessage after the transcript block, so we\n // don't want it duplicated inside the transcript.\n const end = prompt.length > 0 && prompt[prompt.length - 1].role === \"user\"\n ? prompt.length - 1\n : prompt.length\n\n for (let i = end - 1; i >= 0; i--) {\n const msg = prompt[i] as any\n const roleLabel =\n msg.role === \"user\"\n ? \"User\"\n : msg.role === \"assistant\"\n ? \"Assistant\"\n : msg.role === \"tool\"\n ? \"Tool\"\n : msg.role\n\n const { text, toolResultCount } = renderMessageContentForCompaction(msg)\n if (!text.trim()) continue\n\n const entry = `${roleLabel}: ${text}`\n if (total + entry.length > MAX_HISTORY_CHARS) {\n droppedOldest = i + 1\n break\n }\n entries.push(entry)\n total += entry.length + 2 // +2 for the \"\\n\\n\" join\n totalToolResults += toolResultCount\n }\n\n if (entries.length === 0) return null\n\n entries.reverse()\n log.info(\"built compaction history\", {\n entries: entries.length,\n chars: total,\n toolResults: totalToolResults,\n droppedOldestBefore: droppedOldest,\n })\n\n return entries.join(\"\\n\\n\")\n}\n\n/**\n * Convert AI SDK prompt into a Claude CLI stream-json user message.\n *\n * `compactionMode` switches behavior for opencode /compact: the prior\n * transcript is rendered with rich tool content (not placeholders) and the\n * wrapper framing tells the model this is the authoritative thread.\n *\n * Reasoning effort is not part of the message. It used to ride here as a\n * thinking keyword (\"(ultrathink)\"), but Claude Code dropped every keyword\n * except that one, so effort now reaches the CLI as CLAUDE_CODE_EFFORT_LEVEL\n * at spawn time (see `claudeSpawnEnv`).\n */\nexport function getClaudeUserMessage(\n prompt: Prompt,\n includeHistoryContext: boolean = false,\n opts: {\n compactionMode?: boolean\n cliToolCallIds?: ReadonlySet<string>\n stripContextReminders?: boolean\n } = {},\n): string {\n const compactionMode = opts.compactionMode === true\n const cliToolCallIds = opts.cliToolCallIds\n const content: any[] = []\n\n // The account-failover form is the plugin's own dialog: Claude never issued\n // that call and never saw its answer. The transcript rebuilds stripped it\n // already; the current message did not, so on the turn after a form the\n // answer reached Claude as a stray `<opencode_tool_result>` (\"The user\n // dismissed this question\"), measured 2026-09-23.\n prompt = stripAccountFailoverParts(prompt)\n\n // Done once here, at the top, so every path below (the current message,\n // the fresh-session rebuild and the /compact transcript) sees the cleaned\n // text without each needing its own flag.\n if (opts.stripContextReminders) {\n const stripped = stripContextReminders(prompt)\n if (stripped.removed > 0) {\n log.info(\"stripped unsatisfiable context reminders\", {\n blocks: stripped.removed,\n })\n prompt = stripped.prompt\n }\n }\n\n /**\n * A `tool_result` block is only meaningful to a resumed CLI session when\n * that session issued the matching `tool_use`. Anything opencode ran on its\n * own behalf (a `subtask: true` command's `task` call, issue #29) has an id\n * the CLI never emitted, so the block is orphaned: Claude cannot resolve it\n * and the payload, which is right there in the envelope, is unreachable.\n * Those are rendered as plain text instead, which keeps the content and\n * loses only the pairing the CLI could not have honoured anyway.\n *\n * `cliToolCallIds` is the set of calls this CLI process is waiting on. When\n * a caller does not supply it we keep the old unconditional block, so a\n * forgotten call site degrades to today's behaviour rather than breaking\n * the proxy round-trip.\n */\n const pushToolResult = (part: any): void => {\n const id = part.toolCallId\n const text = getToolResultText(part)\n if (!cliToolCallIds || cliToolCallIds.has(id)) {\n content.push({ type: \"tool_result\", tool_use_id: id, content: text })\n return\n }\n log.info(\"rendering opencode-side tool result as text\", {\n toolCallId: id,\n toolName: part.toolName,\n chars: text.length,\n })\n content.push({\n type: \"text\",\n text: `<opencode_tool_result tool=\"${part.toolName ?? \"unknown\"}\">\\n${text}\\n</opencode_tool_result>`,\n })\n }\n\n if (compactionMode) {\n const transcript = compactConversationHistory(prompt, {\n mode: \"compaction\",\n })\n if (transcript) {\n log.info(\"including compaction transcript\", {\n historyLength: transcript.length,\n })\n content.push({\n type: \"text\",\n text: `<conversation_transcript>\n${transcript}\n</conversation_transcript>\n\nThe complete prior conversation appears above. The synthesis instructions follow below.\n\n`,\n })\n }\n } else if (includeHistoryContext) {\n const historyContext = compactConversationHistory(prompt)\n if (historyContext) {\n log.info(\"including conversation history context\", {\n historyLength: historyContext.length,\n })\n content.push({\n type: \"text\",\n text: `<conversation_history>\nThe following is a summary of our conversation so far (from a previous session that couldn't be resumed):\n\n${historyContext}\n\n</conversation_history>\n\nNow continuing with the current message:\n\n`,\n })\n }\n }\n\n // Find messages since last assistant message\n const messages: typeof prompt = []\n for (let i = prompt.length - 1; i >= 0; i--) {\n if (prompt[i].role === \"assistant\") break\n messages.unshift(prompt[i])\n }\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n if (parseSideQuestionContent(msg.content) !== null) continue\n if (typeof msg.content === \"string\") {\n const str = msg.content as string\n if (str.trim()) {\n content.push({ type: \"text\", text: str })\n }\n } else if (Array.isArray(msg.content)) {\n for (const part of msg.content as any[]) {\n if (part.type === \"text\") {\n if (part.text && part.text.trim()) {\n content.push({ type: \"text\", text: part.text })\n }\n } else if (part.type === \"file\" || part.type === \"image\") {\n const block = toImageBlock(part)\n if (block) {\n content.push(block)\n } else {\n log.debug(\"skipped non-image file part\", {\n mediaType: part.mediaType,\n })\n }\n } else if (part.type === \"tool-result\") {\n pushToolResult(part)\n }\n }\n }\n } else if (msg.role === \"tool\") {\n // AI SDK V3 delivers tool results in `tool`-role messages, not `user`.\n // Without this branch we'd hit the empty-content sentinel path and\n // send \"(empty)\" to Claude CLI instead of the actual tool result —\n // forcing the user to press \"continue\" between proxy tool calls.\n if (Array.isArray(msg.content)) {\n for (const part of msg.content as any[]) {\n if (part?.type === \"tool-result\") {\n pushToolResult(part)\n }\n }\n }\n }\n }\n\n if (content.length === 0) {\n // CLI rejects a zero-block message with 400, and Anthropic rejects\n // whitespace-only text blocks — so we need a non-whitespace sentinel.\n // \"(empty)\" matches the parenthetical meta-note convention this file\n // already uses for reasoning keywords (\"(think)\", \"(megathink)\", etc.),\n // which the model reads as out-of-band metadata rather than a prompt to\n // continue its previous turn.\n log.warn(\"empty user content; sending sentinel to satisfy CLI\")\n return JSON.stringify({\n type: \"user\",\n message: {\n role: \"user\",\n content: [{ type: \"text\", text: \"(empty)\" }],\n },\n })\n }\n\n return JSON.stringify({\n type: \"user\",\n message: {\n role: \"user\",\n content,\n },\n })\n}\n","/**\n * Per-agent model resolution.\n *\n * opencode's agent config cannot express \"inherit the account, choose the\n * model\". A subagent that omits `model` inherits the invoking agent's WHOLE\n * model string, and one that pins `model` inherits neither half, so pinning\n * Opus also pins the account it was written with. That is the wrong trade on a\n * machine with more than one Claude account: the worker should follow whoever\n * invoked it and still run on the model the job needs.\n *\n * The account is not part of the model id this class sees. It lives in the\n * provider (`claude-code-<account>`), which selects CLAUDE_CONFIG_DIR at spawn\n * time, and in an `@<account>` marker riding on the id for non-default\n * accounts (see `parseModelId` in models.ts). So swapping the model NAME while\n * preserving that marker changes the model and nothing else, which is exactly\n * the gap in the config schema.\n *\n * Declaring it: an agent markdown file says `forceModel: <id>`, or the\n * `defaultSubagentModel` provider option covers every subagent at once.\n * Nothing needs a per-agent entry in opencode.json.\n *\n * The same file can state `reasoningEffort:`, which beats the effort opencode\n * inherited from the caller's picker (see `resolveAgentEffort`). Model and\n * effort together are what a turn costs, so both belong with the agent.\n *\n * Two deliberate silences, because this rewrites what a user's model picker\n * said it would run:\n *\n * - With `defaultSubagentModel` unset there is NO implicit override. An\n * existing setup upgrading the plugin behaves exactly as before, instead\n * of quietly moving somebody's cheap subagent onto an expensive model.\n * - Only agents this plugin discovered are eligible. opencode's built-ins\n * (`explore`, `general`, `compaction`, ...) are never in the registry, so\n * they are never rewritten.\n */\nimport { readFile, readdir } from \"node:fs/promises\"\nimport path from \"node:path\"\nimport { log } from \"./logger.js\"\nimport { defaultModels } from \"./models.js\"\n\n/** Directory names opencode reads agent markdown from, current form first. */\nexport const AGENT_DIR_NAMES = [\"agents\", \"agent\"]\n\n/** Levels the Claude CLI accepts; anything else is refused, not forwarded. */\nconst REASONING_EFFORTS = [\n \"minimal\",\n \"low\",\n \"medium\",\n \"high\",\n \"xhigh\",\n \"max\",\n]\n\nexport type AgentRecord = {\n mode?: string\n /** A fully-qualified `provider/model` the agent pinned for itself. */\n model?: string\n /** Model NAME this agent wants, on whatever account the caller is using. */\n forceModel?: string\n /** Thinking budget this agent wants, whatever the caller's picker says. */\n reasoningEffort?: string\n}\n\nlet registry: Record<string, AgentRecord> = {}\nlet defaultSubagentModel: string | undefined\n\nexport function setAgentRegistry(records: Record<string, AgentRecord>): void {\n registry = records\n}\n\nexport function getAgentRegistry(): Record<string, AgentRecord> {\n return registry\n}\n\n/** `undefined` (the default) means no implicit override for any agent. */\nexport function setDefaultSubagentModel(model: string | undefined): void {\n defaultSubagentModel = model?.trim() || undefined\n}\n\nexport function getDefaultSubagentModel(): string | undefined {\n return defaultSubagentModel\n}\n\nexport function _resetAgentRegistryForTests(): void {\n registry = {}\n defaultSubagentModel = undefined\n}\n\n/** `claude-opus-5-fast@work` -> `@work`; a default-account id has none. */\nfunction accountMarker(modelId: string): string {\n const at = modelId.indexOf(\"@\")\n return at === -1 ? \"\" : modelId.slice(at)\n}\n\nfunction withoutAccountMarker(modelId: string): string {\n const at = modelId.indexOf(\"@\")\n return at === -1 ? modelId : modelId.slice(0, at)\n}\n\n/**\n * The model a request should actually spawn with.\n *\n * Order, first match wins:\n * 1. The agent declared `forceModel`.\n * 2. The agent is a discovered subagent and `defaultSubagentModel` is set.\n * 3. Anything else: the id opencode asked for, untouched.\n *\n * An agent that pinned a full `provider/model` is out of scope entirely:\n * opencode already routed the call to that provider, and second-guessing it\n * here would silently undo a choice the user made explicitly.\n *\n * Fails closed. An id that is not in the model registry is refused and the\n * original kept, because the alternative is spawning the CLI with a `--model`\n * it will reject, on a turn someone is waiting for.\n */\nexport function resolveAgentModel(\n agent: string | undefined,\n modelId: string,\n overrides?: {\n records?: Record<string, AgentRecord>\n defaultSubagentModel?: string\n },\n): string {\n if (!agent) return modelId\n\n const record = (overrides?.records ?? registry)[agent]\n if (!record) return modelId\n if (record.model?.includes(\"/\")) return modelId\n\n const fallback = overrides\n ? overrides.defaultSubagentModel\n : defaultSubagentModel\n const declared = record.forceModel?.trim()\n const wanted =\n declared || (record.mode === \"subagent\" ? fallback : undefined)\n if (!wanted) return modelId\n\n // A `forceModel` carrying its own `@account` would be forcing an account,\n // which is the thing this exists to avoid. Keep the caller's.\n const base = withoutAccountMarker(wanted)\n if (!Object.hasOwn(defaultModels, base)) {\n log.warn(\"agent model override refused: unknown model\", {\n agent,\n wanted: base,\n keeping: modelId,\n })\n return modelId\n }\n\n const resolved = `${base}${accountMarker(modelId)}`\n if (resolved !== modelId) {\n log.debug(\"agent model override\", { agent, from: modelId, to: resolved })\n }\n return resolved\n}\n\n/**\n * The thinking budget a request should actually spawn with.\n *\n * opencode resolves one effort for the whole session (the model picker's\n * selector, or a variant), and a subagent inherits it. That inheritance is\n * wrong in the expensive direction: a caller who picked `max` for their own\n * turn silently hands `max` to every worker it dispatches, so a mechanical\n * lane runs at the most costly setting available and burns a weekly cap that\n * the caller never spent on the work in front of them.\n *\n * An agent that states its own budget wins. Same reasoning as `forceModel`:\n * the declaration lives with the agent, so a file on disk is the whole\n * configuration and the caller's picker stays a choice about the caller.\n *\n * Unknown values are ignored rather than passed on, since the CLI refuses a\n * level it does not recognise and the turn would die at spawn.\n */\nexport function resolveAgentEffort(\n agent: string | undefined,\n inherited: string | undefined,\n overrides?: { records?: Record<string, AgentRecord> },\n): string | undefined {\n if (!agent) return inherited\n\n const record = (overrides?.records ?? registry)[agent]\n const declared = record?.reasoningEffort?.trim()\n if (!declared) return inherited\n\n if (!REASONING_EFFORTS.includes(declared)) {\n log.warn(\"agent effort override refused: unknown level\", {\n agent,\n wanted: declared,\n keeping: inherited,\n })\n return inherited\n }\n\n if (declared !== inherited) {\n log.debug(\"agent effort override\", {\n agent,\n from: inherited,\n to: declared,\n })\n }\n return declared\n}\n\n/**\n * Read the four fields that matter out of an agent markdown file's YAML\n * frontmatter. Hand-parsed rather than pulling a YAML dependency in for four\n * scalars, and deliberately top-level only: `permission:` has nested keys\n * (`bash:`, `edit:`) that must not be mistaken for agent fields.\n */\nexport function parseAgentFrontmatter(text: string): AgentRecord {\n const record: AgentRecord = {}\n if (!text.startsWith(\"---\")) return record\n\n const lines = text.split(/\\r?\\n/)\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i]\n if (line.trim() === \"---\") break\n\n const match = /^([A-Za-z_][A-Za-z0-9_-]*):[ \\t]*(.*)$/.exec(line)\n if (!match) continue\n\n const key = match[1]\n if (\n key !== \"mode\" &&\n key !== \"model\" &&\n key !== \"forceModel\" &&\n key !== \"reasoningEffort\"\n )\n continue\n\n const value = match[2].trim().replace(/^[\"']|[\"']$/g, \"\")\n if (value) record[key] = value\n }\n\n return record\n}\n\n/**\n * Discover agents from markdown on disk. opencode merges these into its own\n * registry, but whether they reach a plugin's config hook is not documented,\n * so they are read directly rather than assumed.\n */\nexport async function readAgentMarkdownRecords(\n directories: string[],\n): Promise<Record<string, AgentRecord>> {\n const records: Record<string, AgentRecord> = {}\n\n for (const directory of directories) {\n let entries: string[]\n try {\n entries = await readdir(directory)\n } catch {\n continue\n }\n\n for (const entry of entries) {\n if (!entry.endsWith(\".md\")) continue\n\n const name = entry.slice(0, -3)\n if (records[name]) continue\n\n try {\n const text = await readFile(path.join(directory, entry), \"utf8\")\n records[name] = parseAgentFrontmatter(text)\n } catch (err) {\n log.debug(\"failed to read agent markdown\", {\n file: path.join(directory, entry),\n error: String(err),\n })\n }\n }\n }\n\n return records\n}\n\n/**\n * Every directory opencode would read agent markdown from, project before\n * global so a project agent of the same name wins, as opencode resolves them.\n */\nexport function agentDirectories(\n home: string | undefined,\n projectDirectory: string | undefined,\n): string[] {\n const directories: string[] = []\n\n if (projectDirectory) {\n for (const name of AGENT_DIR_NAMES) {\n directories.push(path.join(projectDirectory, \".opencode\", name))\n }\n }\n if (home) {\n for (const name of AGENT_DIR_NAMES) {\n directories.push(path.join(home, \".config\", \"opencode\", name))\n }\n }\n\n return directories\n}\n","import type { OpenCodeModel } from \"./opencode-types.js\"\n\nconst PROVIDER_ID = \"claude-code\"\nconst NPM = \"@khalilgharbaoui/opencode-claude-code-plugin\"\n\nconst reasoningVariants: Record<string, Record<string, unknown>> = {\n low: { reasoningEffort: \"low\" },\n medium: { reasoningEffort: \"medium\" },\n high: { reasoningEffort: \"high\" },\n xhigh: { reasoningEffort: \"xhigh\" },\n max: { reasoningEffort: \"max\" },\n}\n\nconst baseCapabilities = {\n temperature: false,\n attachment: true,\n toolcall: true,\n input: { text: true, audio: false, image: true, video: false, pdf: false },\n output: { text: true, audio: false, image: false, video: false, pdf: false },\n interleaved: false as const,\n}\n\nfunction defineModel(opts: {\n id: string\n name: string\n family: string\n reasoning: boolean\n context: number\n output: number\n cost: { input: number; output: number; cacheRead: number; cacheWrite: number }\n releaseDate: string\n // List-price multiplier relative to Haiku (the cheapest model). Derived\n // exactly from published per-token pricing: input AND output ratios both come\n // out to haiku 1, sonnet 3, opus 5 (opus 5.5: 4), fable/mythos 10. Rendered\n // as an `(N×)` suffix so it surfaces in opencode's model picker, which has no\n // dedicated multiplier field.\n // Display-only: model resolution keys off `id`.\n multiplier: number\n status?: OpenCodeModel[\"status\"]\n}): OpenCodeModel {\n return {\n id: opts.id,\n providerID: PROVIDER_ID,\n api: { id: opts.id, url: \"\", npm: NPM },\n name: `${opts.name} (${opts.multiplier}×)`,\n family: opts.family,\n capabilities: { ...baseCapabilities, reasoning: opts.reasoning },\n cost: {\n input: opts.cost.input,\n output: opts.cost.output,\n cache: { read: opts.cost.cacheRead, write: opts.cost.cacheWrite },\n },\n limit: { context: opts.context, output: opts.output },\n status: opts.status ?? \"active\",\n options: {},\n headers: {},\n release_date: opts.releaseDate,\n variants: opts.reasoning ? reasoningVariants : undefined,\n }\n}\n\n// Costs in US dollars per MILLION tokens, matching Anthropic's published\n// pricing verbatim. This is the unit opencode and models.dev use: opencode\n// divides by 1e6 itself when it multiplies a cost by a token count, so writing\n// per-token values here under-reports session cost by exactly 1,000,000x.\n// Compare models.dev's own entry for the same model:\n// `anthropic/claude-haiku-4-5 -> {\"input\": 1, \"output\": 5, \"cache_read\": 0.1,\n// \"cache_write\": 1.25}`.\n//\n// There is no long-context premium to model. Anthropic's pricing page states\n// that Claude 4.6 and later ship the full 1M-token context window at standard\n// pricing (\"a 900k-token request is billed at the same per-token rate as a\n// 9k-token request\"), and caching/batch discounts apply unchanged across it.\n// opencode 1.18.5 added optional `cost.tiers` / `cost.experimentalOver200K`\n// fields for above-200K pricing; they stay unset here deliberately, because a\n// tier would misreport the real price. Re-check only if Anthropic introduces\n// one. Verified against the pricing docs 2026-07-26.\nconst haikuCost = { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 }\nconst sonnetCost = { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }\n// Opus 4.5+ standard pricing is $5/M in, $25/M out (the price cut at 4.5; held\n// through 4.6/4.7/4.8/5). Cache read 0.1x input, cache write 1.25x input.\nconst opusCost = { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }\n// Fable 5 and Mythos 5 are the Mythos-class tier above Opus and share pricing\n// ($10/M in, $50/M out). Cache read/write follow Anthropic's standard 0.1x / 1.25x\n// input ratios (not separately published).\nconst fableCost = { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 }\n// Fable 5.1 and Mythos 5.1 keep the same input/output and cache-write rates,\n// but Anthropic cut cache reads to $0.25/M (one quarter of the 5.0 price).\nconst fable51Cost = { input: 10, output: 50, cacheRead: 0.25, cacheWrite: 12.5 }\n// Fast mode bills the same per-token rates as the Mythos-class tier: $10/M in,\n// $50/M out, cache read 1, cache write 12.5. Not an inference; this is the\n// exact table the CLI itself applies for `speed: \"fast\"` on Opus 4.8 / Opus 5\n// (`{inputTokens: 10, outputTokens: 50, promptCacheWriteTokens: 12.5,\n// promptCacheReadTokens: 1}`). Kept as its own binding rather than reusing\n// `fableCost` so a future divergence in either tier stays a one-line change.\n// Verified against Claude Code 2.1.245, 2026-08-30.\nconst opusFastCost = { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 }\n// Opus 5.5 is the first Opus priced below the $5/$25 line: $4/M in, $20/M\n// out. Cache writes keep the 1.25x input ratio ($5/M) but cache reads are\n// $0.20/M, 0.05x input rather than the usual 0.1x, so this cannot be derived\n// from `opusCost` by scaling. Fast mode is exactly double across the board\n// ($8/M in, $40/M out, cache read 0.4, cache write 10). Both tables are the\n// ones Claude Code bakes in for the model (`tier_4_20_cache_read_0_20` and\n// its dedicated fast-mode entry), matching the 2026-09-22 announcement.\n// Verified against Claude Code 2.1.280, 2026-09-22.\nconst opus55Cost = { input: 4, output: 20, cacheRead: 0.2, cacheWrite: 5 }\nconst opus55FastCost = { input: 8, output: 40, cacheRead: 0.4, cacheWrite: 10 }\n\n/**\n * Convert an OpenCodeModel to the flat config schema that OpenCode's\n * provider.ts config parser expects (model.temperature, model.reasoning,\n * model.cost.cache_read, model.modalities, etc.).\n */\nexport function toConfigModel(model: OpenCodeModel): Record<string, unknown> {\n const inputMods: string[] = []\n const outputMods: string[] = []\n for (const [k, v] of Object.entries(model.capabilities.input)) {\n if (v) inputMods.push(k)\n }\n for (const [k, v] of Object.entries(model.capabilities.output)) {\n if (v) outputMods.push(k)\n }\n\n return {\n id: model.api.id,\n name: model.name,\n status: model.status,\n family: model.family ?? \"\",\n release_date: model.release_date,\n\n temperature: model.capabilities.temperature,\n reasoning: model.capabilities.reasoning,\n attachment: model.capabilities.attachment,\n tool_call: model.capabilities.toolcall,\n modalities: { input: inputMods, output: outputMods },\n\n cost: {\n input: model.cost.input,\n output: model.cost.output,\n cache_read: model.cost.cache.read,\n cache_write: model.cost.cache.write,\n },\n\n limit: model.limit,\n options: model.options,\n headers: model.headers,\n variants: model.variants,\n }\n}\n\nexport const defaultModels: Record<string, OpenCodeModel> = {\n \"claude-haiku-4-5\": defineModel({\n id: \"claude-haiku-4-5\",\n name: \"Claude Haiku 4.5\",\n family: \"haiku\",\n reasoning: false,\n context: 200_000,\n output: 64_000,\n cost: haikuCost,\n multiplier: 1,\n releaseDate: \"2025-10-01\",\n }),\n \"claude-sonnet-4-5\": defineModel({\n id: \"claude-sonnet-4-5\",\n name: \"Claude Sonnet 4.5\",\n family: \"sonnet\",\n reasoning: true,\n context: 200_000,\n output: 64_000,\n cost: sonnetCost,\n multiplier: 3,\n releaseDate: \"2025-09-29\",\n }),\n \"claude-sonnet-4-6\": defineModel({\n id: \"claude-sonnet-4-6\",\n name: \"Claude Sonnet 4.6\",\n family: \"sonnet\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: sonnetCost,\n multiplier: 3,\n releaseDate: \"2025-06-19\",\n }),\n \"claude-sonnet-5\": defineModel({\n id: \"claude-sonnet-5\",\n name: \"Claude Sonnet 5\",\n family: \"sonnet\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: sonnetCost,\n multiplier: 3,\n releaseDate: \"2026-06-30\",\n }),\n \"claude-opus-4-5\": defineModel({\n id: \"claude-opus-4-5\",\n name: \"Claude Opus 4.5\",\n family: \"opus\",\n reasoning: true,\n context: 200_000,\n output: 64_000,\n cost: opusCost,\n multiplier: 5,\n releaseDate: \"2025-11-01\",\n }),\n \"claude-opus-4-6\": defineModel({\n id: \"claude-opus-4-6\",\n name: \"Claude Opus 4.6\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opusCost,\n multiplier: 5,\n releaseDate: \"2025-06-19\",\n }),\n \"claude-opus-4-7\": defineModel({\n id: \"claude-opus-4-7\",\n name: \"Claude Opus 4.7\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opusCost,\n multiplier: 5,\n releaseDate: \"2025-07-16\",\n }),\n \"claude-opus-4-8\": defineModel({\n id: \"claude-opus-4-8\",\n name: \"Claude Opus 4.8\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opusCost,\n multiplier: 5,\n releaseDate: \"2026-05-28\",\n }),\n // Fast mode. The `-fast` suffix is OUR marker, not a model name Anthropic\n // serves: `parseModelId` strips it before `--model` and turns it into\n // `--settings {\"fastMode\":true}` on the spawn. Retired `-fast` model strings\n // (`claude-opus-4-6-fast`) are a different thing and are not registered here.\n //\n // Only Opus 4.8, Opus 5 and Opus 5.5 qualify: the CLI gates fast mode on\n // the resolved model name containing `opus-4-8` or `opus-5` (which\n // `claude-opus-5-5` satisfies too, and 2.1.280's model catalog lists\n // `fast_mode` for it outright), so registering a fast entry for any other\n // model would produce a picker option that silently runs at standard speed\n // while displaying the fast price.\n \"claude-opus-4-8-fast\": defineModel({\n id: \"claude-opus-4-8-fast\",\n name: \"Claude Opus 4.8 Fast\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opusFastCost,\n multiplier: 10,\n releaseDate: \"2026-05-28\",\n }),\n \"claude-opus-5\": defineModel({\n id: \"claude-opus-5\",\n name: \"Claude Opus 5\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opusCost,\n multiplier: 5,\n releaseDate: \"2026-07-24\",\n }),\n \"claude-opus-5-fast\": defineModel({\n id: \"claude-opus-5-fast\",\n name: \"Claude Opus 5 Fast\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opusFastCost,\n multiplier: 10,\n releaseDate: \"2026-07-24\",\n }),\n // Opus 5.5 needs Claude Code 2.1.280+: the API rejects it from an older CLI\n // with a 400 naming that floor, which the plugin surfaces as a turn error.\n // Thinking cannot be switched off for it (the CLI's catalog marks it\n // `rejects_disabled_thinking`); the plugin never asks for that, so nothing\n // here has to special-case it.\n \"claude-opus-5-5\": defineModel({\n id: \"claude-opus-5-5\",\n name: \"Claude Opus 5.5\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opus55Cost,\n multiplier: 4,\n releaseDate: \"2026-09-22\",\n }),\n \"claude-opus-5-5-fast\": defineModel({\n id: \"claude-opus-5-5-fast\",\n name: \"Claude Opus 5.5 Fast\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opus55FastCost,\n multiplier: 8,\n releaseDate: \"2026-09-22\",\n }),\n \"claude-fable-5\": defineModel({\n id: \"claude-fable-5\",\n name: \"Claude Fable 5\",\n family: \"fable\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: fableCost,\n multiplier: 10,\n releaseDate: \"2026-06-09\",\n }),\n \"claude-fable-5-1\": defineModel({\n id: \"claude-fable-5-1\",\n name: \"Claude Fable 5.1\",\n family: \"fable\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: fable51Cost,\n multiplier: 10,\n releaseDate: \"2026-09-01\",\n }),\n // Mythos 5 and 5.1 share the corresponding Fable models' capabilities and\n // pricing without the safety classifiers; limited availability via Project\n // Glasswing. `claude --model` simply errors for accounts without access, so\n // they are safe to register unconditionally.\n \"claude-mythos-5\": defineModel({\n id: \"claude-mythos-5\",\n name: \"Claude Mythos 5\",\n family: \"mythos\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: fableCost,\n multiplier: 10,\n releaseDate: \"2026-06-09\",\n }),\n \"claude-mythos-5-1\": defineModel({\n id: \"claude-mythos-5-1\",\n name: \"Claude Mythos 5.1\",\n family: \"mythos\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: fable51Cost,\n multiplier: 10,\n releaseDate: \"2026-09-01\",\n }),\n}\n\n/** Marker this plugin appends to build a fast-mode model id. See below. */\nconst FAST_SUFFIX = \"-fast\"\n\n/**\n * Split an opencode model id into the name the Claude CLI actually accepts\n * and whether fast mode was requested.\n *\n * Two suffixes can ride on one id and they are NOT interchangeable:\n *\n * claude-opus-5-fast@work\n * \\_____________/\\___/\\__/\n * CLI model ours accounts.ts's\n *\n * `@work` must survive: the per-account wrapper script strips it at spawn\n * time to pick a CLAUDE_CONFIG_DIR. `-fast` must not: the CLI has no such\n * model (`claude-opus-4-6-fast` is retired and `claude-opus-4-7-fast` errors\n * outright), so it becomes `--settings {\"fastMode\":true}` instead.\n *\n * The `defaultModels` lookup is the guard against a false positive. Only ids\n * we registered are treated as fast markers, so a user-defined model that\n * happens to end in `-fast` is passed through untouched rather than being\n * silently rewritten into a model name that does not exist.\n */\nexport function parseModelId(modelId: string): { model: string; fast: boolean } {\n const at = modelId.indexOf(\"@\")\n const base = at === -1 ? modelId : modelId.slice(0, at)\n const account = at === -1 ? \"\" : modelId.slice(at)\n\n if (!base.endsWith(FAST_SUFFIX)) return { model: modelId, fast: false }\n if (!Object.hasOwn(defaultModels, base)) return { model: modelId, fast: false }\n\n return { model: base.slice(0, -FAST_SUFFIX.length) + account, fast: true }\n}\n","import * as crypto from \"node:crypto\"\nimport * as fs from \"node:fs\"\nimport * as os from \"node:os\"\nimport * as path from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { expandHome } from \"./accounts.js\"\nimport { detectCliSupportsFlag } from \"./cli-version.js\"\nimport { log } from \"./logger.js\"\nimport { pluginTmpDir } from \"./tmp.js\"\n\n/**\n * Bridge opencode skills into Claude Code's native Skill tool.\n *\n * Written by Joseph Roberts (@broskees) on his fork, commit 68ed142, and\n * absorbed here with light edits. Opt-in via `bridgeOpencodeSkills`; see\n * README for why it is off by default upstream.\n *\n * opencode and Claude Code use the same on-disk skill format, a\n * `<name>/SKILL.md` file whose YAML frontmatter carries `name` and\n * `description`, but they read from overlapping, not identical, roots. So an\n * opencode skill the CLI cannot see produces `Unknown skill`, and a skill in\n * a root they *share* reaches one session twice.\n *\n * Fix: assemble a throwaway Claude Code *plugin* directory whose `skills/`\n * folder links each discovered opencode skill, and hand it to the CLI with\n * `--plugin-dir`. Claude registers them natively as\n * `opencode-skills:<name>`, listed by the Skill tool, invocable, and\n * usable as `/opencode-skills:<name>`.\n *\n * `--plugin-dir` is documented as \"for this session only\", so this never\n * writes into the user's `~/.claude`. The staging dir lives under the\n * per-process tmp dir and is removed on exit with everything else.\n *\n * ## The two root sets, and where they overlap\n *\n * opencode's own loader (read out of the `opencode` binary: `var\n * bA=\".claude\", xA=\".agents\", GA=\"skills/**\\/SKILL.md\"`) reads project\n * `.opencode/`, `.claude/` and `.agents/` walking up from the session\n * directory, the opencode config dirs, and the global `~/.claude/skills` and\n * `~/.agents/skills` (the latter two behind `OPENCODE_DISABLE_EXTERNAL_SKILLS`\n * / `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS`). `skillRoots` mirrors that list, so\n * every skill opencode advertises is one the bridge can actually stage.\n *\n * Claude Code 2.1.x loads `<CLAUDE_CONFIG_DIR>/skills` (`~/.claude/skills` by\n * default), the project's own `.claude/skills`, and the `skills/` folder of\n * every installed plugin. Its managed-settings schema names the first two\n * verbatim: \"Blocked: ~/.claude/{surface}/, .claude/{surface}/ (project)\".\n *\n * `.claude/skills` is therefore in *both* sets, and a skill can also be\n * installed as a Claude plugin and symlinked into `~/.agents/skills`. Either\n * way the model sees the same name and description twice, on every turn.\n * `dropNativelyLoadedSkills` removes that overlap before anything is staged;\n * `bridgeSkipNativeSkills: false` turns it off.\n *\n * Known limitation: the plugin scan reads `installed_plugins.json` and does\n * not consult the CLI's own enable/blocklist state, so a skill belonging to a\n * *disabled* plugin can be treated as natively loaded. Every skip is logged\n * with both paths and its reason, which is the one line to grep when a skill\n * goes missing.\n */\n\n/** Plugin name, and therefore the `<plugin>:<skill>` prefix Claude assigns. */\nexport const SKILL_PLUGIN_NAME = \"opencode-skills\"\n\n/**\n * Skills shipped inside this package, at `<package>/skills/<name>/SKILL.md`.\n * Today that is `claude-code-plugin`, the skill that lets a model configure\n * this plugin from its own reference instead of the README. It reaches the\n * model two ways: `registerBundledSkillPath` adds the directory to opencode's\n * `skills.paths` so opencode lists it for every provider, and\n * `resolveSkillPluginDirs` always stages it as a `--plugin-dir` (the user's\n * own skills stay opt-in) because a Claude-routed turn cannot call opencode's\n * `skill` tool and only sees Claude's native Skill tool.\n *\n * Both `dist/index.js` (built) and `src/skill-bridge.ts` (tsx, tests) sit one\n * level below the package root, so the same relative walk finds it.\n */\nexport function bundledSkillsDir(): string | null {\n try {\n const here = fileURLToPath(import.meta.url)\n const dir = path.resolve(path.dirname(here), \"..\", \"skills\")\n return dirExists(dir) ? dir : null\n } catch {\n return null\n }\n}\n\nexport interface DiscoveredSkill {\n /**\n * The name the skill is known by: its SKILL.md frontmatter `name` when it\n * declares a usable one, and the directory basename otherwise. opencode\n * advertises the declared name, so staging under the basename is what made\n * `Skill(\"defuddle\")` fail for a directory called\n * `obsidian-skills--defuddle`.\n */\n name: string\n /** Absolute path to the skill directory containing SKILL.md. */\n dir: string\n /** `dir` with symlinks resolved: the identity two copies of one skill share. */\n realDir: string\n /** sha256 of SKILL.md, the only part of a skill that reaches the prompt. */\n contentHash: string\n}\n\n/** A skill the Claude Code session loads on its own, with no bridge involved. */\nexport interface NativeSkill extends DiscoveredSkill {\n /**\n * Where Claude reads it from. `user` and `project` skills are registered\n * under their bare name, so a bridged skill of the same name collides with\n * them; `plugin` skills are namespaced `<plugin>:<name>` and can only ever\n * duplicate *content*, never a name.\n */\n scope: \"user\" | \"project\" | \"plugin\"\n}\n\nfunction dirExists(p: string): boolean {\n try {\n return fs.statSync(p).isDirectory()\n } catch {\n return false\n }\n}\n\nfunction realPath(p: string): string {\n try {\n return fs.realpathSync(p)\n } catch {\n return path.resolve(p)\n }\n}\n\nfunction envEnabled(name: string): boolean {\n const value = process.env[name]\n if (value === undefined) return false\n const normalized = value.trim().toLowerCase()\n return normalized !== \"\" && normalized !== \"0\" && normalized !== \"false\"\n}\n\n/**\n * A staged skill becomes a directory name under `skills/`, so a frontmatter\n * `name` has to be a single safe path segment before it can be trusted with\n * one. Anything else falls back to the directory basename, which readdir\n * guarantees is a plain segment.\n */\nconst SAFE_SKILL_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/\n\nconst FRONTMATTER_BLOCK = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/\nconst FRONTMATTER_NAME = /^name:[ \\t]*(.+?)[ \\t]*$/m\n\n/** The `name:` a SKILL.md declares, or null when it declares none we can use. */\nexport function declaredSkillName(source: string): string | null {\n const block = FRONTMATTER_BLOCK.exec(source)\n if (!block) return null\n const line = FRONTMATTER_NAME.exec(block[1]!)\n if (!line) return null\n const name = line[1]!.trim().replace(/^[\"']|[\"']$/g, \"\").trim()\n return SAFE_SKILL_NAME.test(name) ? name : null\n}\n\n/** Read one `<root>/<entry>/SKILL.md`, or null when there is nothing to read. */\nfunction readSkill(root: string, entry: string): DiscoveredSkill | null {\n const dir = path.join(root, entry)\n let source: string\n try {\n source = fs.readFileSync(path.join(dir, \"SKILL.md\"), \"utf8\")\n } catch {\n return null\n }\n return {\n name: declaredSkillName(source) ?? entry,\n dir,\n realDir: realPath(dir),\n contentHash: crypto.createHash(\"sha256\").update(source).digest(\"hex\"),\n }\n}\n\n/**\n * Whether two skills are the same skill: the same directory once symlinks are\n * resolved, or byte-identical SKILL.md content. Either one means bridging both\n * would put the same text in the prompt twice with nothing to choose between.\n */\nexport function sameSkill(a: DiscoveredSkill, b: DiscoveredSkill): boolean {\n return a.realDir === b.realDir || a.contentHash === b.contentHash\n}\n\n/**\n * Skill roots in opencode's own precedence order: project scope nearest-first\n * (`.opencode`, `.claude`, `.agents`), then the home-dir `.opencode`, then\n * `OPENCODE_CONFIG_DIR`, then the global `~/.config/opencode`, and finally the\n * external `~/.claude/skills` and `~/.agents/skills` scans. First occurrence of\n * a given skill name wins, so a project can shadow a global skill and an\n * opencode-managed copy shadows an external one.\n */\nexport function skillRoots(cwd: string): string[] {\n const roots: string[] = []\n const seen = new Set<string>()\n const push = (p: string) => {\n const abs = path.resolve(p)\n if (seen.has(abs)) return\n seen.add(abs)\n if (dirExists(abs)) roots.push(abs)\n }\n\n const home = os.homedir()\n const homeDir = home ? path.resolve(home) : null\n\n let current = path.resolve(cwd)\n while (true) {\n push(path.join(current, \".opencode\", \"skills\"))\n // `~/.claude` and `~/.agents` are the *global* external roots and are\n // pushed below under the env switches opencode honours. A workspace that\n // happens to be the home directory must not smuggle them in ahead of that.\n if (current !== homeDir) {\n push(path.join(current, \".claude\", \"skills\"))\n push(path.join(current, \".agents\", \"skills\"))\n }\n const parent = path.dirname(current)\n if (parent === current) break\n current = parent\n }\n\n if (homeDir) push(path.join(homeDir, \".opencode\", \"skills\"))\n\n // opencode accepts both spellings in its own config dirs (`{skill,skills}`).\n const envDir = process.env.OPENCODE_CONFIG_DIR\n if (envDir) {\n push(path.join(envDir, \"skills\"))\n push(path.join(envDir, \"skill\"))\n }\n\n const xdg = process.env.XDG_CONFIG_HOME ?? (homeDir ? path.join(homeDir, \".config\") : null)\n if (xdg) {\n push(path.join(xdg, \"opencode\", \"skills\"))\n push(path.join(xdg, \"opencode\", \"skill\"))\n }\n\n if (homeDir && !envEnabled(\"OPENCODE_DISABLE_EXTERNAL_SKILLS\")) {\n if (!envEnabled(\"OPENCODE_DISABLE_CLAUDE_CODE_SKILLS\")) {\n push(path.join(homeDir, \".claude\", \"skills\"))\n }\n push(path.join(homeDir, \".agents\", \"skills\"))\n }\n\n return roots\n}\n\n/**\n * Walk one skill root and collect every `<name>/SKILL.md`. Directories\n * without a SKILL.md are skipped silently, opencode ignores them too.\n *\n * A name already claimed by an earlier root keeps its winner, because root\n * order *is* the precedence. When the shadowed copy is a different skill\n * rather than another link to the same one, that is a genuine ambiguity and\n * gets a warning naming both paths instead of vanishing.\n */\nfunction collectSkills(\n root: string,\n claimed: Map<string, DiscoveredSkill>,\n found: DiscoveredSkill[],\n): void {\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(root, { withFileTypes: true })\n } catch {\n return\n }\n for (const entry of entries) {\n // `withFileTypes` reports a symlinked dir as a link, not a dir.\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue\n if (entry.name.startsWith(\".\")) continue\n const skill = readSkill(root, entry.name)\n if (!skill) continue\n const winner = claimed.get(skill.name)\n if (winner) {\n if (!sameSkill(winner, skill)) {\n log.warn(\"two different skills share a name; bridging the one the nearest root claimed\", {\n name: skill.name,\n bridged: winner.dir,\n ignored: skill.dir,\n })\n }\n continue\n }\n claimed.set(skill.name, skill)\n found.push(skill)\n }\n}\n\nconst byName = (a: DiscoveredSkill, b: DiscoveredSkill) => a.name.localeCompare(b.name)\n\nexport function discoverOpencodeSkills(cwd: string): DiscoveredSkill[] {\n const found: DiscoveredSkill[] = []\n const claimed = new Map<string, DiscoveredSkill>()\n for (const root of skillRoots(cwd)) collectSkills(root, claimed, found)\n return found.sort(byName)\n}\n\n/** The skills this package ships (see `bundledSkillsDir`). */\nexport function discoverBundledSkills(): DiscoveredSkill[] {\n const root = bundledSkillsDir()\n if (!root) return []\n const found: DiscoveredSkill[] = []\n collectSkills(root, new Map(), found)\n return found.sort(byName)\n}\n\n// ---------------------------------------------------------------------------\n// What Claude already loads\n// ---------------------------------------------------------------------------\n\n/**\n * The `CLAUDE_CONFIG_DIR` this spawn will run under: the account's own config\n * dir when the provider selected one, else the environment's, else `~/.claude`.\n * Accounts symlink `skills` back to `~/.claude/skills`, so in practice every\n * account resolves to the same directory, but the spawn's own value is the\n * only one guaranteed to.\n */\nexport function claudeConfigDir(configDir?: string): string | null {\n const explicit = configDir ?? process.env.CLAUDE_CONFIG_DIR\n if (explicit) return path.resolve(expandHome(explicit))\n const home = os.homedir()\n return home ? path.resolve(home, \".claude\") : null\n}\n\n/**\n * `skills/` of every plugin `installed_plugins.json` says applies here. A\n * `user`-scope install always does; a `project` / `local` one only when its\n * `projectPath` is the workspace or an ancestor of it.\n */\nfunction installedPluginSkillRoots(configDir: string | null, cwd: string): string[] {\n if (!configDir) return []\n let parsed: unknown\n try {\n parsed = JSON.parse(\n fs.readFileSync(path.join(configDir, \"plugins\", \"installed_plugins.json\"), \"utf8\"),\n )\n } catch {\n return []\n }\n const plugins = (parsed as { plugins?: unknown } | null)?.plugins\n if (!plugins || typeof plugins !== \"object\") return []\n\n const workspace = path.resolve(cwd)\n const roots: string[] = []\n for (const installs of Object.values(plugins as Record<string, unknown>)) {\n if (!Array.isArray(installs)) continue\n for (const install of installs) {\n const entry = install as { installPath?: unknown; scope?: unknown; projectPath?: unknown }\n if (typeof entry?.installPath !== \"string\" || !entry.installPath) continue\n if (entry.scope !== \"user\" && !coversWorkspace(entry.projectPath, workspace)) continue\n roots.push(path.join(entry.installPath, \"skills\"))\n }\n }\n return roots\n}\n\nfunction coversWorkspace(projectPath: unknown, workspace: string): boolean {\n if (typeof projectPath !== \"string\" || !projectPath) return false\n const root = path.resolve(projectPath)\n if (root === workspace) return true\n const rel = path.relative(root, workspace)\n return rel !== \"\" && !rel.startsWith(\"..\") && !path.isAbsolute(rel)\n}\n\n/**\n * Every skill the Claude Code session will load without our help: its user\n * config dir, the project `.claude/skills` directories above the workspace,\n * and each installed plugin's own `skills/`.\n */\nexport function discoverNativeClaudeSkills(opts: {\n cwd: string\n configDir?: string\n}): NativeSkill[] {\n const found: NativeSkill[] = []\n const scanned = new Set<string>()\n const scan = (root: string, scope: NativeSkill[\"scope\"]) => {\n const abs = path.resolve(root)\n if (scanned.has(abs)) return\n scanned.add(abs)\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(abs, { withFileTypes: true })\n } catch {\n return\n }\n for (const entry of entries) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue\n if (entry.name.startsWith(\".\")) continue\n const skill = readSkill(abs, entry.name)\n if (skill) found.push({ ...skill, scope })\n }\n }\n\n const configDir = claudeConfigDir(opts.configDir)\n if (configDir) scan(path.join(configDir, \"skills\"), \"user\")\n\n const home = os.homedir()\n const homeDir = home ? path.resolve(home) : null\n let current = path.resolve(opts.cwd)\n while (true) {\n // The home dir's `.claude/skills` is the user scope, already scanned above\n // under whatever config dir this spawn actually uses.\n if (current !== homeDir) scan(path.join(current, \".claude\", \"skills\"), \"project\")\n const parent = path.dirname(current)\n if (parent === current) break\n current = parent\n }\n\n for (const root of installedPluginSkillRoots(configDir, opts.cwd)) scan(root, \"plugin\")\n\n return found\n}\n\nexport interface SkippedSkill {\n skill: DiscoveredSkill\n native: NativeSkill\n /**\n * `same-copy`: literally the same directory. `same-content`: a byte-identical\n * SKILL.md somewhere else. `name-taken`: a *different* skill Claude registers\n * under this name, so bridging ours would make the name ambiguous.\n */\n reason: \"same-copy\" | \"same-content\" | \"name-taken\"\n}\n\n/**\n * Drop the skills the Claude session already sees. Identity wins over name:\n * an identical copy is dropped whatever Claude loads it from, while a name\n * clash only counts against `user` / `project` skills, because a plugin skill\n * answers to `<plugin>:<name>` and cannot shadow a bridged one.\n *\n * A `name-taken` skip changes which copy answers `Skill(\"<name>\")`, so it is\n * the one case that warns rather than just logging.\n */\nexport function dropNativelyLoadedSkills(\n skills: DiscoveredSkill[],\n native: NativeSkill[],\n): { bridged: DiscoveredSkill[]; skipped: SkippedSkill[] } {\n if (native.length === 0) return { bridged: skills, skipped: [] }\n\n const bridged: DiscoveredSkill[] = []\n const skipped: SkippedSkill[] = []\n for (const skill of skills) {\n const sameCopy = native.find((n) => n.realDir === skill.realDir)\n const sameContent = sameCopy ?? native.find((n) => n.contentHash === skill.contentHash)\n const nameTaken =\n sameContent ?? native.find((n) => n.scope !== \"plugin\" && n.name === skill.name)\n if (!nameTaken) {\n bridged.push(skill)\n continue\n }\n const reason = sameCopy ? \"same-copy\" : sameContent ? \"same-content\" : \"name-taken\"\n skipped.push({ skill, native: nameTaken, reason })\n if (reason === \"name-taken\") {\n log.warn(\"claude already registers a different skill under this name; not bridging ours\", {\n name: skill.name,\n claudeLoads: nameTaken.dir,\n notBridged: skill.dir,\n scope: nameTaken.scope,\n })\n }\n }\n return { bridged, skipped }\n}\n\n// ---------------------------------------------------------------------------\n// Staging\n// ---------------------------------------------------------------------------\n\n/** opencode 2's `Skill.Info`, the fields it requires. */\nexport interface BundledSkillInfo {\n id: string\n name: string\n description?: string\n path: string\n content: string\n}\n\n/**\n * The bundled skills in opencode 2's `Skill.Info` shape. V2 has no config\n * hook to add a `skills.paths` entry to, so the V2 entrypoint registers them\n * through `skill.transform` instead. Parsed the way V2's own skill-file loader\n * does (`@opencode/core@2.0.11`): `name` and `description` from the\n * frontmatter, `content` is the markdown body after it, `path` is the file.\n */\nexport function readBundledSkillInfos(): BundledSkillInfo[] {\n return discoverBundledSkills().flatMap((skill) => {\n const file = path.join(skill.dir, \"SKILL.md\")\n let text: string\n try {\n text = fs.readFileSync(file, \"utf8\")\n } catch {\n return []\n }\n const match = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/.exec(text)\n const frontmatter = match?.[1] ?? \"\"\n const field = (key: string): string | undefined => {\n const line = new RegExp(`^${key}:\\\\s*(.+)$`, \"m\").exec(frontmatter)?.[1]?.trim()\n return line ? line.replace(/^([\"'])(.*)\\1$/, \"$2\") : undefined\n }\n const description = field(\"description\")\n return [\n {\n id: skill.name,\n name: field(\"name\") ?? skill.name,\n ...(description ? { description } : {}),\n path: file,\n content: match ? text.slice(match[0].length) : text,\n },\n ]\n })\n}\n\n/** Link a skill dir into the staging tree, falling back to a copy. */\nfunction linkSkill(source: string, target: string): void {\n try {\n // Windows needs an explicit junction for directory links, and even then\n // only with the right privileges, hence the copy fallback below.\n fs.symlinkSync(source, target, process.platform === \"win32\" ? \"junction\" : \"dir\")\n return\n } catch {\n fs.cpSync(source, target, { recursive: true, dereference: true })\n }\n}\n\nfunction fileExists(p: string): boolean {\n try {\n return fs.statSync(p).isFile()\n } catch {\n return false\n }\n}\n\n/**\n * Materialise the synthetic plugin directory. Returns its path, or null if\n * there are no skills to bridge. The path is keyed by a hash of the\n * resolved skill set, so an unchanged set reuses the existing tree instead\n * of rebuilding it on every spawn.\n */\nexport function buildSkillPluginDir(skills: DiscoveredSkill[]): string | null {\n if (skills.length === 0) return null\n\n const fingerprint = skills.map((s) => `${s.name}\\0${s.dir}`).join(\"\\n\")\n const hash = crypto.createHash(\"sha256\").update(fingerprint).digest(\"hex\").slice(0, 12)\n const root = path.join(pluginTmpDir(), `skills-${hash}`)\n const manifest = path.join(root, \".claude-plugin\", \"plugin.json\")\n\n // Same skill set as a previous spawn in this process, reuse the tree.\n if (fileExists(manifest)) return root\n\n try {\n fs.rmSync(root, { recursive: true, force: true })\n fs.mkdirSync(path.join(root, \".claude-plugin\"), { recursive: true })\n fs.mkdirSync(path.join(root, \"skills\"), { recursive: true })\n fs.writeFileSync(\n manifest,\n JSON.stringify(\n {\n name: SKILL_PLUGIN_NAME,\n description:\n \"Skills discovered from this opencode installation, bridged into Claude Code.\",\n },\n null,\n 2,\n ),\n { encoding: \"utf8\", mode: 0o600 },\n )\n for (const skill of skills) {\n linkSkill(skill.dir, path.join(root, \"skills\", skill.name))\n }\n } catch (err) {\n log.warn(\"failed to stage opencode skill plugin dir\", {\n root,\n error: err instanceof Error ? err.message : String(err),\n })\n return null\n }\n\n return root\n}\n\n/**\n * Add the bundled skills directory to opencode's `skills.paths` (scanned for\n * nested SKILL.md files) so opencode itself lists the\n * skill for every provider and its own `skill` tool can load it. Idempotent;\n * returns whether anything was added.\n */\nexport function registerBundledSkillPath(config: {\n skills?: { paths?: string[]; urls?: string[] }\n}): boolean {\n const dir = bundledSkillsDir()\n if (!dir) return false\n config.skills ??= {}\n const paths = (config.skills.paths ??= [])\n if (paths.some((p) => path.resolve(p) === dir)) return false\n paths.push(dir)\n return true\n}\n\n/**\n * One-call entry point for the spawn sites: discover, drop what Claude already\n * loads, stage, and return the `--plugin-dir` values. The package's own skills\n * are always staged; the user's opencode skills only when `enabled`\n * (`bridgeOpencodeSkills`). A user skill with the same name as a bundled one\n * wins, so it can be overridden. Returns an empty array when the CLI is too old\n * to accept the flag or there is nothing left to stage, so callers can spread\n * the result unconditionally.\n */\nexport async function resolveSkillPluginDirs(opts: {\n cwd: string\n cliPath: string\n enabled: boolean\n /** The spawn's `CLAUDE_CONFIG_DIR`, which decides what Claude loads natively. */\n configDir?: string\n /** `bridgeSkipNativeSkills`; only `false` keeps the duplicates. */\n skipNative?: boolean\n}): Promise<string[]> {\n const user = opts.enabled ? discoverOpencodeSkills(opts.cwd) : []\n const claimed = new Set(user.map((s) => s.name))\n const bundled = discoverBundledSkills().filter((s) => !claimed.has(s.name))\n let skills = [...user, ...bundled].sort(byName)\n\n if (opts.skipNative !== false && skills.length > 0) {\n const native = discoverNativeClaudeSkills({ cwd: opts.cwd, configDir: opts.configDir })\n const { bridged, skipped } = dropNativelyLoadedSkills(skills, native)\n if (skipped.length > 0) {\n log.info(\"skills claude code already loads; left unbridged\", {\n count: skipped.length,\n skipped: skipped.map((s) => ({\n name: s.skill.name,\n reason: s.reason,\n opencode: s.skill.dir,\n claude: s.native.dir,\n })),\n })\n }\n skills = bridged\n }\n if (skills.length === 0) return []\n\n // No published version marks `--plugin-dir`'s arrival, so probe the\n // binary's own help text rather than inventing a semver threshold.\n const supported = await detectCliSupportsFlag(opts.cliPath, \"--plugin-dir\")\n if (!supported) {\n log.notice(\n \"claude cli does not support --plugin-dir; opencode skills will not be bridged. Run `npm i -g @anthropic-ai/claude-code` to upgrade.\",\n { skills: skills.length },\n )\n return []\n }\n\n const dir = buildSkillPluginDir(skills)\n if (!dir) return []\n\n log.info(\"bridged opencode skills into claude\", {\n count: skills.length,\n names: skills.map((s) => s.name),\n bundled: bundled.map((s) => s.name),\n pluginDir: dir,\n })\n return [dir]\n}\n","import { EventEmitter } from \"node:events\"\nimport { unlink } from \"node:fs/promises\"\nimport { ClaudeSession } from \"./claude-session-bun.js\"\nimport { cliEffortLevel, type ActiveProcess } from \"./session-manager.js\"\nimport type { ReasoningEffort } from \"./types.js\"\nimport { log } from \"./logger.js\"\n\nexport interface InteractiveSpawnOptions {\n cwd: string\n /** Claude CLI executable or account wrapper path. */\n cliPath?: string\n /** Claude config root used for JSONL transcripts. */\n configDir?: string\n model?: string\n /** Request Claude Code's fast mode (Opus 4.8 / 5 / 5.5 only). Folded into\n * the single `--settings` payload alongside `permissions`. */\n fastMode?: boolean\n /** Bridged Claude `--mcp-config` file paths (from effectiveMcpConfig). */\n mcpConfigPaths?: string[]\n /** Session-scoped `--plugin-dir` paths (from `resolveSkillPluginDirs`),\n * which expose opencode skills to the TUI's native Skill tool. Already\n * filtered for CLI support, and empty when there is nothing to bridge. */\n pluginDirs?: string[]\n /** permissions.allow rules (e.g. mcp__server__*, Bash, Edit). */\n permissionsAllow?: string[]\n /** Optional permission mode. `bypassPermissions` is ignored for interactive\n * sessions because Claude Code shows a safety confirmation screen first. */\n permissionMode?: string\n /** Temp file for --append-system-prompt-file (parity with the headless\n * spawn; unlinked when the session is killed). */\n systemPromptFile?: string\n /** \"\" = skip CLAUDE.md + ambient settings (fast e2e); null/undefined =\n * normal settings (default — parity with the headless transport). */\n settingSources?: string | null\n /** Strip ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN from the spawn env so the\n * CLI uses subscription auth instead of pay-as-you-go API billing. */\n ignoreAnthropicApiKey?: boolean\n /** Reasoning effort, exported as CLAUDE_CODE_EFFORT_LEVEL for the session. */\n effort?: ReasoningEffort\n}\n\n/**\n * doStream writes stream-json user envelopes to stdin\n * (`{\"type\":\"user\",\"message\":{content:[...]}}`). The interactive TUI expects\n * plain typed text, so decode the envelope: extract the text blocks and drop\n * anything that can't be typed into a terminal (an image block would paste\n * megabytes of base64 into the chat). Tool results are rendered as labeled\n * text so the model still sees the outcome. Non-envelope input (already plain\n * text) passes through verbatim.\n */\nexport function decodeUserEnvelope(chunk: string): string {\n let parsed: any\n try {\n parsed = JSON.parse(chunk)\n } catch {\n return chunk\n }\n if (!parsed || parsed.type !== \"user\" || !parsed.message) return chunk\n const content = parsed.message.content\n if (typeof content === \"string\") return content\n if (!Array.isArray(content)) return chunk\n\n const parts: string[] = []\n let dropped = 0\n for (const block of content) {\n if (block?.type === \"text\" && typeof block.text === \"string\") {\n parts.push(block.text)\n } else if (block?.type === \"tool_result\") {\n const v = block.content\n const text =\n typeof v === \"string\"\n ? v\n : Array.isArray(v)\n ? v\n .map((i: any) => (i?.type === \"text\" ? i.text : \"\"))\n .filter(Boolean)\n .join(\"\\n\")\n : \"\"\n parts.push(\n `[Tool result${block.tool_use_id ? ` ${block.tool_use_id}` : \"\"}]\\n${text}`,\n )\n } else {\n dropped++\n }\n }\n if (dropped > 0) {\n log.warn(\"interactive transport dropped non-text content blocks\", {\n dropped,\n })\n }\n return parts.join(\"\\n\\n\")\n}\n\n/**\n * Adapt a ClaudeSession (interactive Bun ConPTY transport) to the ActiveProcess\n * contract the doStream line handler depends on. The shim's `proc.stdin.write`\n * injects a turn into the live interactive `claude` and re-emits each new JSONL\n * transcript record on `lineEmitter` as a 'line' event, plus a synthetic\n * `{type:'result'}` line on a terminal stop_reason so the existing finish branch\n * (usage + providerMetadata + controller.close) fires unchanged.\n *\n * No node-pty, no node sidecar: runs in-process under opencode's Bun (which\n * bundles a Bun version with native ConPTY). Interactive = subscription billing.\n */\n/**\n * The CLI flags an interactive spawn adds after `ClaudeSession`'s own\n * `--session-id` / `--model` / `--setting-sources`. Exported so the spawn\n * arguments can be checked without a PTY.\n */\nexport function interactiveExtraArgs(opts: InteractiveSpawnOptions): string[] {\n const extraArgs: string[] = []\n if (opts.mcpConfigPaths && opts.mcpConfigPaths.length > 0) {\n extraArgs.push(\n \"--mcp-config\",\n ...opts.mcpConfigPaths,\n \"--strict-mcp-config\",\n )\n }\n // `--plugin-dir` is repeatable and scoped to this session only.\n for (const dir of opts.pluginDirs ?? []) {\n extraArgs.push(\"--plugin-dir\", dir)\n }\n // One `--settings` for the whole flag-settings layer. The CLI accepts the\n // flag once, so pushing a second occurrence would silently drop the first\n // rather than merge it.\n const flagSettings: Record<string, unknown> = {}\n if (opts.permissionsAllow && opts.permissionsAllow.length > 0) {\n flagSettings.permissions = { allow: opts.permissionsAllow }\n }\n if (opts.fastMode) {\n flagSettings.fastMode = true\n }\n if (Object.keys(flagSettings).length > 0) {\n extraArgs.push(\"--settings\", JSON.stringify(flagSettings))\n }\n if (opts.permissionMode === \"bypassPermissions\") {\n log.warn(\n \"interactive permissionMode bypassPermissions ignored: Claude Code prompts for confirmation in the TUI\",\n )\n } else if (opts.permissionMode) {\n extraArgs.push(\"--permission-mode\", opts.permissionMode)\n }\n if (opts.systemPromptFile) {\n extraArgs.push(\"--append-system-prompt-file\", opts.systemPromptFile)\n }\n return extraArgs\n}\n\nexport function spawnInteractiveProcess(\n opts: InteractiveSpawnOptions,\n): ActiveProcess {\n const extraArgs = interactiveExtraArgs(opts)\n\n const session = new ClaudeSession({\n cwd: opts.cwd,\n cliPath: opts.cliPath,\n configDir: opts.configDir,\n model: opts.model,\n // Default null = normal CLAUDE.md + settings load, matching what the\n // headless spawn does. \"\" (skip everything) is for fast e2e runs only.\n settingSources:\n opts.settingSources === undefined ? null : opts.settingSources,\n extraArgs,\n ignoreAnthropicApiKey: opts.ignoreAnthropicApiKey,\n effort: opts.effort ? cliEffortLevel(opts.effort) : undefined,\n })\n log.info(\"prepared interactive claude session\", {\n cwd: opts.cwd,\n cliPath: opts.cliPath ?? \"claude\",\n configDir: session.configDir,\n model: opts.model,\n effort: opts.effort,\n sessionId: session.sessionId,\n jsonlPath: session.jsonlPath,\n })\n\n const lineEmitter = new EventEmitter()\n const errorHandlers = new Set<(err: Error) => void>()\n let startPromise: Promise<void> | null = null\n\n const ensureStarted = (): Promise<void> => {\n if (!startPromise) startPromise = session.start()\n return startPromise\n }\n\n const emitResult = (\n subtype: string,\n isError: boolean,\n result?: string,\n usage?: unknown,\n ): void => {\n lineEmitter.emit(\n \"line\",\n JSON.stringify({\n type: \"result\",\n subtype,\n is_error: isError,\n result,\n session_id: session.sessionId,\n usage: usage ?? {},\n total_cost_usd: null,\n duration_ms: 0,\n }),\n )\n }\n\n const runTurn = (userMsg: string): void => {\n void (async () => {\n try {\n await ensureStarted()\n const { stopReason, usage } = await session.tailTurn(userMsg, (raw) => {\n lineEmitter.emit(\"line\", raw)\n })\n // Synthesize the `result` line the headless transport would have\n // emitted, so doStream's existing finish branch runs verbatim. A turn\n // with no terminal stop_reason (timeout / session exit mid-turn) is\n // reported HONESTLY as an error result — not a clean end_turn — so\n // truncation is visible to the user and to auto-continue.\n const timedOut = !stopReason\n emitResult(\n timedOut ? \"error_during_execution\" : stopReason,\n timedOut,\n timedOut\n ? \"Interactive transport: the turn ended without a terminal stop_reason (turn timeout or claude exit). Output above may be incomplete.\"\n : undefined,\n usage,\n )\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err))\n log.error(\"interactive turn failed\", { error: e.message })\n emitResult(\n \"error_during_execution\",\n true,\n `Interactive transport failed: ${e.message}`,\n )\n if (errorHandlers.size > 0) {\n for (const h of errorHandlers) h(e)\n } else {\n lineEmitter.emit(\"close\")\n }\n }\n })()\n }\n\n // Minimal ChildProcess-shaped shim: only the members doStream/session-manager\n // actually touch (stdin.write, on/off 'error', kill).\n const proc: any = {\n stdin: {\n write(chunk: string): boolean {\n const raw =\n typeof chunk === \"string\" && chunk.endsWith(\"\\n\")\n ? chunk.slice(0, -1)\n : chunk\n // doStream writes stream-json envelopes; the TUI needs plain text.\n runTurn(decodeUserEnvelope(raw))\n return true\n },\n end(): void {},\n },\n stdout: null,\n stderr: null,\n pid: -1,\n killed: false,\n on(event: string, fn: (err: Error) => void): unknown {\n if (event === \"error\") errorHandlers.add(fn)\n return proc\n },\n once(): unknown {\n return proc\n },\n off(event: string, fn: (err: Error) => void): unknown {\n if (event === \"error\") errorHandlers.delete(fn)\n return proc\n },\n kill(): boolean {\n try {\n session.dispose()\n } catch {}\n if (opts.systemPromptFile) {\n void unlink(opts.systemPromptFile).catch(() => {})\n }\n proc.killed = true\n return true\n },\n }\n\n return {\n proc: proc as unknown as ActiveProcess[\"proc\"],\n lineEmitter,\n proxyServer: null,\n mcpHash: undefined,\n systemPromptFile: opts.systemPromptFile,\n }\n}\n","import * as os from \"node:os\"\nimport * as fs from \"node:fs\"\nimport * as path from \"node:path\"\nimport { execFileSync } from \"node:child_process\"\nimport { randomUUID } from \"node:crypto\"\nimport { cliHygieneEnv } from \"./cli-version.js\"\n\n/**\n * Persistent interactive Claude Code session driven over Bun's NATIVE PTY\n * (Bun.spawn `terminal` option = openpty on POSIX, ConPTY on Windows). This is\n * the in-process Bun port of claude-tui-bridge/src/claudeSession.ts: same\n * design, node-pty swapped for Bun's own ConPTY so it runs inside opencode's\n * Bun runtime with NO node sidecar and NO node-pty dependency.\n *\n * - ONE long-lived interactive `claude` process per session (multi-turn),\n * - turns injected by writing into the terminal (bracketed paste + Enter),\n * - replies captured by tailing the session JSONL transcript\n * (<CLAUDE_CONFIG_DIR>/projects/<encoded-cwd>/<session-id>.jsonl) and\n * parsing the assistant records; completion detected by a terminal\n * `stop_reason`.\n *\n * Driving the INTERACTIVE TUI (real TTY) keeps model calls on the subscription\n * billing path (not `claude -p` / Agent SDK, which meter after 2026-06-15).\n */\n\nfunction resolveClaude(cmd = \"claude\"): string {\n if (path.isAbsolute(cmd) && fs.existsSync(cmd)) return cmd\n const viaBun = Bun.which(cmd)\n if (viaBun) return viaBun\n const isWin = os.platform() === \"win32\"\n try {\n const out = execFileSync(isWin ? \"where\" : \"which\", [cmd], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n })\n const first = out\n .split(/\\r?\\n/)\n .map((l) => l.trim())\n .filter(Boolean)\n .find((p) => fs.existsSync(p))\n if (first) return first\n } catch {}\n throw new Error(`Could not resolve command on PATH: ${cmd}`)\n}\n\n/** Claude encodes the absolute cwd into the transcript dir name by replacing\n * EVERY non-alphanumeric char with `-` (no collapsing of runs). Verified on\n * Windows against ~/.claude/projects, e.g.:\n * C:\\code\\my-app -> C--code-my-app\n * C:\\dev\\My Project -> C--dev-My-Project (the space also becomes `-`). */\nexport function encodeCwd(cwd: string): string {\n return path.resolve(cwd).replace(/[^a-zA-Z0-9]/g, \"-\")\n}\n\nexport interface TurnResult {\n text: string\n stopReason: string | null\n usage: any | null\n cacheReadTokens: number\n cacheCreationTokens: number\n ephemeral1hTokens: number\n ephemeral5mTokens: number\n inputTokens: number\n outputTokens: number\n elapsedMs: number\n}\n\nexport interface ClaudeSessionOptions {\n cwd?: string\n /** Claude CLI executable or account wrapper path. */\n cliPath?: string\n /** Claude config root used for JSONL transcripts (defaults to ~/.claude). */\n configDir?: string\n model?: string\n /** '' bypasses CLAUDE.md + user/project/local settings load (fast tests).\n * null/undefined omits the flag entirely (normal settings). */\n settingSources?: string | null\n extraArgs?: string[]\n /** Strip ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN from the spawn env so the\n * CLI uses subscription auth instead of pay-as-you-go API billing. */\n ignoreAnthropicApiKey?: boolean\n /** CLI effort level (low | medium | high | xhigh | max), exported as\n * CLAUDE_CODE_EFFORT_LEVEL so it overrides the account's settings.json. */\n effort?: string\n cols?: number\n rows?: number\n bootMinMs?: number\n bootQuietMs?: number\n bootMaxMs?: number\n pollMs?: number\n turnTimeoutMs?: number\n /** false = plain write(prompt)+Enter; true = wrap in bracketed-paste so\n * multi-line prompts don't submit early. Default true. */\n bracketedPaste?: boolean\n /** Submitting a turn: a large/multi-line bracketed paste collapses into a\n * \"[Pasted text]\" placeholder, and an Enter sent while claude is still\n * ingesting the paste is silently DROPPED — so a single fixed-delay Enter is\n * unreliable and the turn can hang until turnTimeoutMs. Instead: wait\n * submitMinMs, send Enter, then confirm the turn was accepted (a new\n * transcript record appears) within submitConfirmMs; if not, resend Enter,\n * up to submitMaxRetries times. */\n submitMinMs?: number\n submitConfirmMs?: number\n submitMaxRetries?: number\n /** Abort the call (during boot or an in-flight turn): kills the process and\n * rejects with an \"aborted\" error. */\n signal?: AbortSignal\n debug?: boolean\n}\n\n/**\n * Env for the interactive (TUI) child. The headless counterpart is\n * `claudeSpawnEnv` in session-manager.ts; both must apply `cliHygieneEnv`, so\n * this is a named function rather than an object literal inside `Bun.spawn`,\n * which no test can reach without a real PTY.\n */\nexport function interactiveSpawnEnv(opts: {\n configDir: string\n ignoreAnthropicApiKey?: boolean\n effort?: string\n}): Record<string, string | undefined> {\n return {\n ...process.env,\n CLAUDE_CONFIG_DIR: opts.configDir,\n TERM: \"xterm-256color\",\n // Pin the binary so a mid-session autoupdate cannot invalidate the\n // detected version the flag gates read, and skip non-essential traffic.\n // Fills gaps only, so a var the user exported survives untouched.\n ...cliHygieneEnv(),\n ...(opts.ignoreAnthropicApiKey\n ? { ANTHROPIC_API_KEY: undefined, ANTHROPIC_AUTH_TOKEN: undefined }\n : {}),\n ...(opts.effort ? { CLAUDE_CODE_EFFORT_LEVEL: opts.effort } : {}),\n }\n}\n\nconst TERMINAL_STOP = new Set([\"end_turn\", \"stop_sequence\", \"max_tokens\"])\nconst delay = (ms: number) => new Promise<void>((r) => setTimeout(r, ms))\n\nfunction resolveConfigDir(configDir: string | undefined): string {\n const value = configDir ?? process.env.CLAUDE_CONFIG_DIR\n if (!value) return path.join(os.homedir(), \".claude\")\n if (value === \"~\") return os.homedir()\n if (value.startsWith(\"~/\") || value.startsWith(\"~\\\\\")) {\n return path.join(os.homedir(), value.slice(2))\n }\n return path.resolve(value)\n}\n\nexport class ClaudeSession {\n readonly sessionId: string\n readonly cwd: string\n readonly configDir: string\n readonly jsonlPath: string\n raw = \"\"\n\n private proc: BunSubprocess | null = null\n private cursor = 0 // index into transcript split('\\n')\n private lastDataAt = 0\n private exited = false\n private exitCode: number | null = null\n private aborted = false\n private readonly signal?: AbortSignal\n private readonly o: Required<\n Omit<\n ClaudeSessionOptions,\n | \"cliPath\"\n | \"configDir\"\n | \"model\"\n | \"settingSources\"\n | \"extraArgs\"\n | \"signal\"\n | \"ignoreAnthropicApiKey\"\n | \"effort\"\n >\n > &\n Pick<\n ClaudeSessionOptions,\n | \"cliPath\"\n | \"configDir\"\n | \"model\"\n | \"settingSources\"\n | \"extraArgs\"\n | \"ignoreAnthropicApiKey\"\n | \"effort\"\n >\n\n constructor(opts: ClaudeSessionOptions = {}) {\n this.cwd = path.resolve(opts.cwd ?? process.cwd())\n this.configDir = resolveConfigDir(opts.configDir)\n this.signal = opts.signal\n this.sessionId = randomUUID()\n this.jsonlPath = path.join(\n this.configDir,\n \"projects\",\n encodeCwd(this.cwd),\n `${this.sessionId}.jsonl`,\n )\n this.o = {\n cwd: this.cwd,\n cliPath: opts.cliPath,\n configDir: this.configDir,\n model: opts.model,\n settingSources: opts.settingSources,\n extraArgs: opts.extraArgs ?? [],\n ignoreAnthropicApiKey: opts.ignoreAnthropicApiKey,\n effort: opts.effort,\n cols: opts.cols ?? 200,\n rows: opts.rows ?? 50,\n bootMinMs: opts.bootMinMs ?? 3000,\n bootQuietMs: opts.bootQuietMs ?? 1500,\n bootMaxMs: opts.bootMaxMs ?? 25000,\n pollMs: opts.pollMs ?? 250,\n // Agentic turns (tool loops) routinely run for many minutes; a short\n // cap would surface as a mid-task error result. 30 min mirrors the\n // proxy-tool ceiling rather than a chat-reply expectation.\n turnTimeoutMs: opts.turnTimeoutMs ?? 1_800_000,\n bracketedPaste: opts.bracketedPaste ?? true,\n submitMinMs: opts.submitMinMs ?? 200,\n submitConfirmMs: opts.submitConfirmMs ?? 1500,\n submitMaxRetries: opts.submitMaxRetries ?? 8,\n debug: opts.debug ?? false,\n }\n }\n\n async start(): Promise<void> {\n if (this.signal?.aborted) throw new Error(\"aborted before start\")\n this.signal?.addEventListener(\n \"abort\",\n () => {\n this.aborted = true\n this.dispose()\n },\n { once: true },\n )\n const claude = resolveClaude(this.o.cliPath ?? \"claude\")\n const args: string[] = [\"--session-id\", this.sessionId]\n if (this.o.model) args.push(\"--model\", this.o.model)\n if (this.o.settingSources !== null && this.o.settingSources !== undefined) {\n args.push(\"--setting-sources\", this.o.settingSources)\n }\n if (this.o.extraArgs && this.o.extraArgs.length) args.push(...this.o.extraArgs)\n\n if (this.o.debug)\n process.stderr.write(`[session] spawn: ${claude} ${args.join(\" \")}\\n`)\n\n this.lastDataAt = Date.now()\n this.proc = Bun.spawn([claude, ...args], {\n cwd: this.cwd,\n env: interactiveSpawnEnv({\n // The resolved field, not `this.o.configDir`: same value (the\n // constructor copies it in) but typed as always present.\n configDir: this.configDir,\n ignoreAnthropicApiKey: this.o.ignoreAnthropicApiKey,\n effort: this.o.effort,\n }),\n terminal: {\n cols: this.o.cols,\n rows: this.o.rows,\n data: (_term, d) => {\n this.lastDataAt = Date.now()\n const chunk = Buffer.from(d).toString(\"utf8\")\n this.raw += chunk\n if (this.o.debug) process.stdout.write(chunk)\n },\n },\n })\n this.proc.exited\n .then((code) => {\n this.exitCode = typeof code === \"number\" ? code : null\n this.exited = true\n this.proc = null\n })\n .catch(() => {\n this.exited = true\n this.proc = null\n })\n\n await this.waitForBoot()\n this.cursor = this.lineCount()\n }\n\n /** Wait until the TUI has been quiet for bootQuietMs (Ink ready), bounded by\n * bootMinMs..bootMaxMs. */\n private async waitForBoot(): Promise<void> {\n const start = Date.now()\n while (Date.now() - start < this.o.bootMaxMs) {\n await delay(150)\n if (this.aborted) throw new Error(\"aborted during boot\")\n if (this.exited) {\n throw new Error(this.failureMessage(\"claude exited during boot\", true))\n }\n const elapsed = Date.now() - start\n const sinceData = Date.now() - this.lastDataAt\n if (elapsed >= this.o.bootMinMs && sinceData >= this.o.bootQuietMs) return\n }\n }\n\n /** Submit the freshly-injected prompt and confirm the turn was actually\n * accepted. A large bracketed paste collapses into a \"[Pasted text]\"\n * placeholder; an Enter sent while claude is still ingesting the paste is\n * silently dropped, so a single fixed-delay Enter races the paste and can\n * leave the prompt sitting unsubmitted (→ hang until turnTimeoutMs). Send\n * Enter, then poll for transcript growth past the cursor (the turn's records\n * are written on acceptance); resend Enter until accepted or the retry\n * budget is spent. Polling growth (not a blind delay) also stops us from\n * sending a stray Enter once the turn is in flight. */\n private async submitTurn(): Promise<void> {\n await delay(this.o.submitMinMs)\n for (let attempt = 0; attempt < this.o.submitMaxRetries; attempt++) {\n if (this.aborted || this.exited || !this.proc) return\n this.proc.terminal.write(\"\\r\")\n const until = Date.now() + this.o.submitConfirmMs\n while (Date.now() < until) {\n await delay(80)\n if (this.aborted || this.exited) return\n if (this.lineCount() > this.cursor) return // turn accepted\n }\n }\n }\n\n private readRawLines(): string[] {\n try {\n return fs.readFileSync(this.jsonlPath, \"utf8\").split(\"\\n\")\n } catch {\n return []\n }\n }\n\n /** Count of complete lines (split('\\n') minus the trailing/partial element). */\n private lineCount(): number {\n const lines = this.readRawLines()\n return lines.length > 0 ? lines.length - 1 : 0\n }\n\n private rawTail(max = 600): string {\n const clean = this.raw\n // Strip ANSI escape/control sequences before including terminal output in diagnostics.\n .replace(/\\x1B(?:[@-Z\\\\-_]|\\[[0-?]*[ -/]*[@-~])/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim()\n return clean.length > max ? clean.slice(-max) : clean\n }\n\n private failureMessage(reason: string, includeRaw = false): string {\n const parts = [\n `${reason} (sessionId=${this.sessionId}, jsonlPath=${this.jsonlPath}, exitCode=${this.exitCode ?? \"unknown\"})`,\n ]\n if (includeRaw) {\n const tail = this.rawTail()\n if (tail) parts.push(`terminalTail=${JSON.stringify(tail)}`)\n }\n return parts.join(\"; \")\n }\n\n /**\n * Inject a turn into the live session and return the assistant reply once a\n * terminal stop_reason is observed in the transcript.\n */\n async ask(prompt: string, perTurnTimeoutMs?: number): Promise<TurnResult> {\n if (this.aborted) throw new Error(\"aborted\")\n if (!this.proc || this.exited)\n throw new Error(\"session not started or already exited\")\n const timeout = perTurnTimeoutMs ?? this.o.turnTimeoutMs\n const t0 = Date.now()\n\n // Inject. Bracketed paste keeps multi-line prompts from submitting early;\n // submitTurn() then presses Enter and confirms the turn was accepted,\n // resending Enter if the (collapsed) paste swallowed the first one.\n if (this.o.bracketedPaste) {\n this.proc.terminal.write(\"\\x1b[200~\" + prompt + \"\\x1b[201~\")\n } else {\n this.proc.terminal.write(prompt)\n }\n await this.submitTurn()\n\n const collected: string[] = []\n let lastUsage: any = null\n let stopReason: string | null = null\n const deadline = Date.now() + timeout\n\n while (Date.now() < deadline) {\n await delay(this.o.pollMs)\n if (this.aborted) throw new Error(\"aborted mid-turn\")\n const lines = this.readRawLines()\n const lastComplete = lines.length - 1 // exclusive bound; trailing/partial line skipped\n if (lastComplete <= this.cursor) {\n // Drain the transcript before reacting to exit: a final assistant record\n // can be flushed in the same tick the process exits.\n if (this.exited) throw new Error(this.failureMessage(\"claude exited mid-turn\", true))\n continue\n }\n\n for (let i = this.cursor; i < lastComplete; i++) {\n const s = lines[i]\n if (!s || !s.trim()) continue\n let rec: any\n try {\n rec = JSON.parse(s)\n } catch {\n continue\n }\n if (rec.type === \"assistant\" && rec.message) {\n for (const b of rec.message.content ?? []) {\n if (b?.type === \"text\" && typeof b.text === \"string\")\n collected.push(b.text)\n }\n if (rec.message.usage) lastUsage = rec.message.usage\n if (\n rec.message.stop_reason &&\n TERMINAL_STOP.has(rec.message.stop_reason)\n ) {\n stopReason = rec.message.stop_reason\n }\n }\n }\n this.cursor = lastComplete\n if (stopReason) break\n }\n\n if (!stopReason) {\n throw new Error(\n this.failureMessage(\n `turn timed out after ${timeout}ms (no terminal assistant record; collected ${collected.length} text block(s))`,\n ),\n )\n }\n\n const u = lastUsage ?? {}\n return {\n text: collected.join(\"\\n\").trim(),\n stopReason,\n usage: lastUsage,\n cacheReadTokens: u.cache_read_input_tokens ?? 0,\n cacheCreationTokens: u.cache_creation_input_tokens ?? 0,\n ephemeral1hTokens: u.cache_creation?.ephemeral_1h_input_tokens ?? 0,\n ephemeral5mTokens: u.cache_creation?.ephemeral_5m_input_tokens ?? 0,\n inputTokens: u.input_tokens ?? 0,\n outputTokens: u.output_tokens ?? 0,\n elapsedMs: Date.now() - t0,\n }\n }\n\n /**\n * Like ask(), but instead of collecting the reply text it re-emits each NEW\n * raw JSONL transcript line via onLine (verbatim) until a terminal\n * stop_reason. Returns the terminal stop_reason + the last assistant usage.\n * Used by the opencode plugin transport shim, which feeds these raw lines\n * into the existing stream-json line handler unchanged.\n */\n async tailTurn(\n prompt: string,\n onLine: (rawLine: string) => void,\n perTurnTimeoutMs?: number\n ): Promise<{ stopReason: string | null; usage: any | null }> {\n if (this.aborted) throw new Error(\"aborted\")\n if (!this.proc || this.exited)\n throw new Error(\"session not started or already exited\")\n const timeout = perTurnTimeoutMs ?? this.o.turnTimeoutMs\n\n if (this.o.bracketedPaste) {\n this.proc.terminal.write(\"\\x1b[200~\" + prompt + \"\\x1b[201~\")\n } else {\n this.proc.terminal.write(prompt)\n }\n await this.submitTurn()\n\n let lastUsage: any = null\n let totalOutput = 0\n let stopReason: string | null = null\n const deadline = Date.now() + timeout\n\n while (Date.now() < deadline) {\n await delay(this.o.pollMs)\n if (this.aborted) throw new Error(\"aborted mid-turn\")\n const lines = this.readRawLines()\n const lastComplete = lines.length - 1\n if (lastComplete <= this.cursor) {\n // Drain the transcript before reacting to exit: the terminal assistant\n // record can land in the same tick the process exits.\n if (this.exited) {\n throw new Error(this.failureMessage(\"claude exited mid-turn\", true))\n }\n continue\n }\n for (let i = this.cursor; i < lastComplete; i++) {\n const s = lines[i]\n if (!s || !s.trim()) continue\n onLine(s)\n let rec: any\n try {\n rec = JSON.parse(s)\n } catch {\n continue\n }\n if (rec.type === \"assistant\" && rec.message) {\n if (rec.message.usage) {\n lastUsage = rec.message.usage\n totalOutput += rec.message.usage.output_tokens ?? 0\n }\n if (\n rec.message.stop_reason &&\n TERMINAL_STOP.has(rec.message.stop_reason)\n ) {\n stopReason = rec.message.stop_reason\n }\n }\n }\n this.cursor = lastComplete\n if (stopReason) break\n }\n\n // Context (input/cache) = the LAST record's full conversation state; output\n // = SUM across all assistant records this turn (each generation), else\n // multi-record tool turns undercount output. toUsage() prefers\n // iterations[last], so patch that entry's output too.\n let usage: any = lastUsage\n if (lastUsage) {\n usage = { ...lastUsage, output_tokens: totalOutput }\n if (Array.isArray(lastUsage.iterations) && lastUsage.iterations.length > 0) {\n const iters = lastUsage.iterations.map((it: any) => ({ ...it }))\n iters[iters.length - 1] = {\n ...iters[iters.length - 1],\n output_tokens: totalOutput,\n }\n usage.iterations = iters\n }\n }\n if (!stopReason) {\n throw new Error(\n this.failureMessage(\n `turn timed out after ${timeout}ms (no terminal assistant record)`,\n ),\n )\n }\n\n return { stopReason, usage }\n }\n\n dispose(): void {\n if (this.proc) {\n try {\n this.proc.terminal.write(\"\\x03\")\n } catch {}\n try {\n this.proc.kill()\n } catch {}\n try {\n this.proc.terminal.close()\n } catch {}\n }\n this.proc = null\n }\n}\n\n/** One-shot convenience (drop-in for `claude -p`): start, ask, dispose. */\nexport async function askOnce(\n prompt: string,\n opts: ClaudeSessionOptions = {},\n): Promise<TurnResult> {\n const s = new ClaudeSession(opts)\n await s.start()\n try {\n return await s.ask(prompt)\n } finally {\n s.dispose()\n }\n}\n","// Removes a stale unscoped `opencode-claude-code-plugin` install left in\n// opencode's plugin cache by older configs. The unscoped name is a different\n// artifact than this scoped plugin and shadows it when both coexist.\n// Disable with OPENCODE_CLAUDE_CODE_PLUGIN_NO_CLEANUP=1.\n\nimport {\n existsSync,\n readFileSync,\n realpathSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { join, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { log } from \"./logger.js\"\n\nconst STALE_PACKAGE_NAME = \"opencode-claude-code-plugin\"\nconst SUSPECT_DESCRIPTION_TOKEN = \"Claude Code\"\n\nlet alreadyRan = false\n\nfunction candidateCacheRoots(): string[] {\n const xdg = process.env.XDG_CACHE_HOME\n return [\n xdg ? join(xdg, \"opencode\") : null,\n join(homedir(), \".cache\", \"opencode\"),\n join(homedir(), \"Library\", \"Caches\", \"opencode\"),\n ].filter((p): p is string => Boolean(p))\n}\n\nfunction userOpencodeJsonPath(): string {\n const xdgConfig = process.env.XDG_CONFIG_HOME ?? join(homedir(), \".config\")\n return join(xdgConfig, \"opencode\", \"opencode.json\")\n}\n\nfunction userIntendsToUseUnscoped(): boolean {\n const cfg = userOpencodeJsonPath()\n if (!existsSync(cfg)) return false\n try {\n const json = JSON.parse(readFileSync(cfg, \"utf8\"))\n const plugins: unknown = json.plugin\n if (!Array.isArray(plugins)) return false\n return plugins.some(\n (entry) =>\n typeof entry === \"string\" &&\n /^opencode-claude-code-plugin(@[^/]+)?$/.test(entry),\n )\n } catch {\n return false\n }\n}\n\nfunction ourLoadedDir(): string | null {\n try {\n const filePath = fileURLToPath(import.meta.url)\n return realpathSync(resolve(filePath, \"..\", \"..\"))\n } catch {\n return null\n }\n}\n\nexport function cleanupStaleUnscopedInstall(): void {\n if (alreadyRan) return\n alreadyRan = true\n\n if (process.env.OPENCODE_CLAUDE_CODE_PLUGIN_NO_CLEANUP === \"1\") return\n if (userIntendsToUseUnscoped()) return\n\n const ourDir = ourLoadedDir()\n\n for (const cacheRoot of candidateCacheRoots()) {\n try {\n cleanupOne(cacheRoot, ourDir)\n } catch (err) {\n log.warn(\"cleanup-stale: error processing cache root\", {\n cacheRoot,\n error: String(err),\n })\n }\n }\n}\n\nfunction cleanupOne(cacheRoot: string, ourDir: string | null): void {\n if (!existsSync(cacheRoot)) return\n\n const stalePath = join(cacheRoot, \"node_modules\", STALE_PACKAGE_NAME)\n if (!existsSync(stalePath)) return\n\n // Don't self-delete if we are the unscoped install.\n let realStalePath = stalePath\n try {\n realStalePath = realpathSync(stalePath)\n } catch {\n // ignore\n }\n if (ourDir && realStalePath === ourDir) return\n\n // Verify identity before removing.\n const pkgJsonPath = join(stalePath, \"package.json\")\n if (!existsSync(pkgJsonPath)) return\n let pkg: { name?: string; description?: string } = {}\n try {\n pkg = JSON.parse(readFileSync(pkgJsonPath, \"utf8\"))\n } catch {\n return\n }\n if (pkg.name !== STALE_PACKAGE_NAME) return\n if (!pkg.description?.includes(SUSPECT_DESCRIPTION_TOKEN)) return\n\n log.info(\"cleanup-stale: removing unscoped install\", { stalePath })\n try {\n rmSync(stalePath, { recursive: true, force: true })\n } catch (err) {\n log.warn(\"cleanup-stale: rmSync failed\", {\n stalePath,\n error: String(err),\n })\n return\n }\n\n // Drop the dep from the cache root's package.json so opencode's installer\n // doesn't reinstate it on its next pass. Lockfile is left alone; bun\n // reconciles against package.json on the next install.\n const cachePkgJson = join(cacheRoot, \"package.json\")\n if (!existsSync(cachePkgJson)) return\n try {\n const cfg = JSON.parse(readFileSync(cachePkgJson, \"utf8\"))\n if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {\n delete cfg.dependencies[STALE_PACKAGE_NAME]\n writeFileSync(cachePkgJson, JSON.stringify(cfg, null, 2) + \"\\n\")\n log.info(\"cleanup-stale: pruned dep from cache package.json\")\n }\n } catch (err) {\n log.warn(\"cleanup-stale: cache package.json update failed\", {\n error: String(err),\n })\n }\n}\n","import path from \"node:path\"\n\n/**\n * A V1-shaped opencode client over opencode 2's plugin context.\n *\n * Several features ask opencode's SDK client for live state at call time: the\n * MCP runtime overlay (`mcp.status`), the tool registry (`tool.list`, which\n * gates the `question`-based features and feeds the `task` description\n * overlay), and a session's directory and parent (`session.get`, for serve-mode\n * cwd and for keeping account failover out of subagents). Every call site\n * already degrades when there is no client, which is exactly what V2 looked\n * like before this: those features quietly switched off.\n *\n * Rather than teach each call site a second API, this answers the same calls\n * in V1's response shapes (`{ data }` envelopes, V1 field names) from V2's\n * domains. Shapes read off `@opencode/client@2.0.11` `generated/types.d.ts`.\n * Anything V2 cannot answer is left out, so its caller takes its existing\n * no-client path.\n */\n\nexport interface V2ClientContext {\n readonly mcp?: {\n list?: () => Promise<{ data?: Array<{ name?: unknown; status?: { status?: unknown } }> }>\n }\n readonly tool?: {\n list?: () => Promise<ReadonlyArray<{ id?: unknown; name?: unknown; description?: unknown }>>\n }\n readonly session?: {\n get?: (input: { sessionID: string }) => Promise<unknown>\n }\n readonly agent?: {\n list?: () => Promise<{\n data?: Array<{ id?: unknown; name?: unknown; description?: unknown; mode?: unknown; hidden?: unknown }>\n }>\n }\n}\n\n/**\n * The agent list V1 carries inside its `task` description and the `task`\n * proxy overlay extracts (`extractAgentTypeList`). V2's `subagent`\n * description has no such list (measured on 2.0.11: 596 characters, no agent\n * names), which left Claude guessing names, the exact failure AGENTS.md\n * records for V1. Built from `agent.list()`: agents a subagent call may use.\n */\nexport function formatAgentTypeList(\n agents: ReadonlyArray<{ id?: unknown; name?: unknown; description?: unknown; mode?: unknown; hidden?: unknown }>,\n): string | undefined {\n const lines = agents.flatMap((agent) => {\n if (agent.hidden === true || agent.mode === \"primary\") return []\n const name = typeof agent.id === \"string\" ? agent.id : agent.name\n if (typeof name !== \"string\" || name.length === 0) return []\n const blurb = typeof agent.description === \"string\" ? agent.description.trim() : \"\"\n return [`- ${name}: ${blurb || \"(no description)\"}`]\n })\n if (lines.length === 0) return undefined\n return `Available agent types and the tools they have access to:\\n${lines.join(\"\\n\")}`\n}\n\nfunction describe(value: unknown): string {\n if (typeof value === \"string\") return value\n // V2 tools may carry `description` as a thunk. Only a plain string result\n // is usable; anything else (an Effect, a throw) is treated as empty.\n if (typeof value === \"function\") {\n try {\n const result = (value as () => unknown)()\n return typeof result === \"string\" ? result : \"\"\n } catch {\n return \"\"\n }\n }\n return \"\"\n}\n\n/** V2's `SessionInfo` in V1's `Session` terms: `directory` and `parentID`. */\nexport function toV1Session(info: unknown): Record<string, unknown> | undefined {\n if (!info || typeof info !== \"object\") return undefined\n const session = info as {\n parentID?: unknown\n subpath?: unknown\n location?: { directory?: unknown }\n }\n const root = session.location?.directory\n const directory =\n typeof root === \"string\"\n ? typeof session.subpath === \"string\" && session.subpath.length > 0\n ? path.resolve(root, session.subpath)\n : root\n : undefined\n return { ...(info as Record<string, unknown>), directory, parentID: session.parentID }\n}\n\nexport function createV1ClientShim(ctx: V2ClientContext): Record<string, unknown> {\n const client: Record<string, unknown> = {}\n\n const mcpList = ctx.mcp?.list\n if (typeof mcpList === \"function\") {\n client.mcp = {\n // V1: `{ data: { [server]: { status } } }`. V2: `{ data: [{ name, status: { status } }] }`.\n status: async () => {\n const result = await mcpList.call(ctx.mcp)\n const data: Record<string, { status: string }> = {}\n for (const server of result?.data ?? []) {\n const status = server?.status?.status\n if (typeof server?.name === \"string\" && typeof status === \"string\") {\n data[server.name] = { status }\n }\n }\n return { data }\n },\n }\n }\n\n const toolList = ctx.tool?.list\n if (typeof toolList === \"function\") {\n client.tool = {\n // V1: `{ data: [{ id, description, parameters }] }`. V2 returns the tools\n // themselves, with an Effect schema for input rather than JSON Schema, so\n // `parameters` is empty; only `proxyOpencodeTools` reads it.\n list: async () => {\n const tools = await toolList.call(ctx.tool)\n const data = (tools ?? []).flatMap((tool) => {\n const id = typeof tool?.id === \"string\" ? tool.id : tool?.name\n return typeof id === \"string\"\n ? [{ id, description: describe(tool.description), parameters: {} }]\n : []\n })\n // The `task` proxy looks its live description up by V1's id; V2 calls\n // the same tool `subagent`, and its description names no agents.\n const subagent = data.find((tool) => tool.id === \"subagent\")\n if (subagent && !data.some((tool) => tool.id === \"task\")) {\n let agentList: string | undefined\n const agentApi = ctx.agent?.list\n if (typeof agentApi === \"function\") {\n try {\n agentList = formatAgentTypeList((await agentApi.call(ctx.agent))?.data ?? [])\n } catch {\n agentList = undefined\n }\n }\n const description = agentList\n ? `${subagent.description}\\n\\n${agentList}`\n : subagent.description\n data.push({ ...subagent, id: \"task\", description })\n }\n return { data }\n },\n }\n }\n\n const sessionGet = ctx.session?.get\n if (typeof sessionGet === \"function\") {\n client.session = {\n get: async (options: { path: { id: string } }) => ({\n data: toV1Session(await sessionGet.call(ctx.session, { sessionID: options.path.id })),\n }),\n }\n }\n\n return client\n}\n","import type { LanguageModelV3 } from \"@ai-sdk/provider\"\nimport {\n BASE_PROVIDER_ID,\n accountDisplayName,\n accountModelSuffix,\n accountProviderId,\n ensureAccountRuntime,\n resolveAccounts,\n} from \"./accounts.js\"\nimport { log } from \"./logger.js\"\nimport { defaultModels } from \"./models.js\"\nimport type { OpenCodeModel } from \"./opencode-types.js\"\nimport { BTW_COMMAND_DESCRIPTION } from \"./btw-command.js\"\nimport { DOCTOR_COMMAND, DOCTOR_COMMAND_DESCRIPTION } from \"./doctor.js\"\nimport type {\n V2Cleanup,\n V2CommandInvocation,\n V2Context,\n V2Delivery,\n V2Event,\n V2ModelInfo,\n V2ProviderInfo,\n V2Registration,\n V2RequestKind,\n} from \"./opencode-v2-types.js\"\nimport {\n isUsableDirectory,\n setOpencodeClient,\n setOpencodeProjectDirectory,\n} from \"./runtime-status.js\"\nimport { createV1ClientShim, type V2ClientContext } from \"./v2-client.js\"\nimport {\n deleteActiveProcessesForSession,\n ensureProcessExitCleanup,\n} from \"./session-manager.js\"\nimport { readBundledSkillInfos } from \"./skill-bridge.js\"\nimport { logStartupDiagnostics } from \"./startup-diagnostics.js\"\nimport type { ClaudeCodeProviderSettings } from \"./types.js\"\n\n/**\n * opencode 2.x entrypoint. V1 calls the default export's `server()`, V2 calls\n * its `setup(ctx)`; the two never run in the same process, and nothing here\n * translates one API into the other.\n *\n * The provider is split across two V2 surfaces where V1 had one hook:\n * `provider.transform` publishes the metadata (providers and models) and the\n * `aisdk` `sdk` hook hands opencode the object that builds the language model.\n * opencode never imports `package` itself: `AISDK.language` only checks that it\n * carries the `aisdk:` prefix and then asks the `sdk` hooks for the SDK, which\n * is how its own bundled providers work too. So the name below is a label, not\n * something that has to resolve.\n */\nexport const V2_PLUGIN_PACKAGE = \"aisdk:@khalilgharbaoui/opencode-claude-code-plugin\"\n\n/** The same header opencode 1.x sets itself, so the language model needs no V2 branch to read it. */\nexport const SESSION_AFFINITY_HEADER = \"x-session-affinity\"\n\n/**\n * V1 tags the opencode agent into `providerOptions` from `chat.params`. V2's\n * `model.request` hook can only set headers, so the agent travels as one.\n */\nexport const OPENCODE_AGENT_HEADER = \"x-opencode-agent\"\n\n// Keys the plugin itself consumes. They configure the provider set, not a\n// language model, so they are never passed to `createClaudeCode`.\nconst PLUGIN_ONLY_SETTINGS = [\"accounts\", \"defaultSubagentModel\"]\n\n// Keys opencode adds to the SDK options on its own (`prepareOptions` in the\n// V2 aisdk runtime). None of them means anything to the Claude CLI.\nconst OPENCODE_SDK_OPTION_KEYS = [\"fetch\", \"headers\", \"body\", \"name\"]\n\nexport function isClaudeCodeProviderId(providerID: string | undefined): boolean {\n if (typeof providerID !== \"string\") return false\n return providerID === BASE_PROVIDER_ID || providerID.startsWith(`${BASE_PROVIDER_ID}-`)\n}\n\n/**\n * The agent name the language model keys its special paths on. V2 tells the\n * request kind apart from the agent, so a compaction or title call arrives with\n * the session's ordinary agent; map the kind back onto the names V1 used, which\n * are what `isCompactionCall` and the title stub already test for.\n */\nexport function agentForRequest(kind: V2RequestKind, agent: string): string {\n if (kind === \"compaction\") return \"compaction\"\n if (kind === \"title\") return \"title\"\n return agent\n}\n\nfunction releasedAt(date: string | undefined): number {\n const ms = date ? Date.parse(date) : Number.NaN\n return Number.isFinite(ms) ? ms : 0\n}\n\nfunction modalities(flags: Record<string, boolean | undefined>): string[] {\n return Object.entries(flags)\n .filter(([, enabled]) => enabled === true)\n .map(([name]) => name)\n}\n\n/** One registry entry in V2's `Model.Info` shape. */\nexport function toV2Model(\n model: OpenCodeModel,\n providerID: string,\n modelId: string,\n): V2ModelInfo {\n return {\n id: modelId,\n modelID: modelId,\n providerID,\n name: model.name,\n family: model.family,\n capabilities: {\n tools: model.capabilities.toolcall,\n input: modalities(model.capabilities.input),\n output: modalities(model.capabilities.output),\n },\n variants: Object.entries(model.variants ?? {}).map(([id, settings]) => ({\n id,\n settings: { ...settings },\n })),\n time: { released: releasedAt(model.release_date) },\n cost: [\n {\n input: model.cost.input,\n output: model.cost.output,\n cache: { read: model.cost.cache.read, write: model.cost.cache.write },\n },\n ],\n status: model.status ?? \"active\",\n enabled: true,\n limit: { context: model.limit.context, output: model.limit.output },\n package: V2_PLUGIN_PACKAGE,\n }\n}\n\nexport function v2ModelsForProvider(providerID: string, modelSuffix?: string): V2ModelInfo[] {\n return Object.entries(defaultModels).map(([id, model]) =>\n toV2Model(model, providerID, modelSuffix ? `${id}@${modelSuffix}` : id),\n )\n}\n\nexport interface V2ProviderPlan {\n info: V2ProviderInfo\n models: V2ModelInfo[]\n}\n\nfunction stripPluginOnly(settings: Record<string, unknown> | undefined): Record<string, unknown> {\n const result = { ...(settings ?? {}) }\n for (const key of PLUGIN_ONLY_SETTINGS) delete result[key]\n return result\n}\n\n/**\n * The providers to publish, from the `claude-code` settings the operator\n * configured. Mirrors V1's config hook: no `accounts` means one `claude-code`\n * provider, and a list means one `claude-code-<account>` provider per entry\n * with the seed removed. The account wrapper script is not built here because\n * provider transforms are synchronous; `resolveSdkSettings` builds it when the\n * model is first asked for.\n */\nexport function planV2Providers(\n seedSettings: Record<string, unknown> | undefined,\n defaultProxyTools: readonly string[],\n): V2ProviderPlan[] {\n const base: Record<string, unknown> = {\n cliPath: \"claude\",\n proxyTools: [...defaultProxyTools],\n ...stripPluginOnly(seedSettings),\n }\n const accounts = resolveAccounts(seedSettings?.accounts)\n\n if (!accounts) {\n return [\n {\n info: {\n id: BASE_PROVIDER_ID,\n name: \"Claude Code\",\n activation: \"enabled\",\n package: V2_PLUGIN_PACKAGE,\n settings: { ...base, providerID: BASE_PROVIDER_ID },\n },\n models: v2ModelsForProvider(BASE_PROVIDER_ID),\n },\n ]\n }\n\n return accounts.map((account) => {\n const providerID = accountProviderId(account)\n return {\n info: {\n id: providerID,\n name: accountDisplayName(account),\n activation: \"enabled\",\n package: V2_PLUGIN_PACKAGE,\n settings: {\n ...base,\n account,\n // The resolved list, so this account's language model can offer the\n // others when it runs out of usage (src/account-failover.ts).\n failoverAccounts: accounts,\n providerID,\n // Kept separately because the wrapper replaces `cliPath`, and a\n // failover builds another account's wrapper on the same base.\n baseCliPath: String(base.cliPath ?? \"claude\"),\n },\n },\n models: v2ModelsForProvider(providerID, accountModelSuffix(account)),\n }\n })\n}\n\n/**\n * The settings `createClaudeCode` is called with. What opencode hands the hook\n * wins over the planned copy: by then its config plugin has merged the\n * operator's provider settings over ours, and model or variant settings sit on\n * top of that. The planned copy fills what opencode's lacks, which is\n * everything for an account provider the config never names. opencode's own\n * transport keys are removed first. An account provider's wrapper is built\n * here, once per SDK, since it writes a script to disk.\n */\nexport async function resolveSdkSettings(\n providerID: string,\n planned: Record<string, unknown> | undefined,\n eventOptions: Record<string, unknown>,\n): Promise<ClaudeCodeProviderSettings> {\n const fromEvent = { ...eventOptions }\n for (const key of OPENCODE_SDK_OPTION_KEYS) delete fromEvent[key]\n const merged: Record<string, unknown> = {\n ...stripPluginOnly({ ...(planned ?? {}), ...fromEvent }),\n providerID,\n // The only place a model is told it serves opencode 2.\n hostApi: \"v2\",\n }\n\n const account = typeof merged.account === \"string\" ? merged.account : undefined\n if (account) {\n const base = String(merged.baseCliPath ?? merged.cliPath ?? \"claude\")\n const runtime = await ensureAccountRuntime(account, base)\n Object.assign(merged, runtime, { baseCliPath: base })\n }\n\n return merged as ClaudeCodeProviderSettings\n}\n\nfunction plainObject(value: unknown): Record<string, unknown> | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined\n}\n\n/**\n * The `claude-code` settings the operator configured, as the provider set is\n * planned. This cannot come from the provider editor: opencode 2 applies the\n * config file's provider block through its own `opencode.config.provider`\n * plugin, after plugin transforms, so config overrides plugin defaults and a\n * plugin never sees it (measured on 2.0.11: `accounts` was absent from the\n * seed record). So it is read from the same on-disk layers opencode reads.\n *\n * Both config shapes are accepted, because V2 promises to keep reading V1\n * files: V1's `options` and V2's `settings`, the latter winning. The V2-native\n * home for plugin-level settings, the plugin's own `options` in the `plugins`\n * array, wins over both.\n */\nexport function configuredSeedSettings(\n config: Record<string, unknown>,\n pluginOptions: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n const provider = plainObject(plainObject(config.provider)?.[BASE_PROVIDER_ID])\n return {\n ...(plainObject(provider?.options) ?? {}),\n ...(plainObject(provider?.settings) ?? {}),\n ...(pluginOptions ?? {}),\n }\n}\n\n/**\n * Whether `setup` is really running inside opencode 2. opencode 1.18 calls a\n * dual export's `setup` as well as its `server` (measured on 1.18.32), with its\n * own smaller v2-compat context that has no `provider` or `session` domain.\n * Registering anything there would put a second provider path next to the one\n * `server()` already built, so a context without every domain we use, or one\n * that names a 1.x version, gets nothing.\n */\nexport function isOpencodeV2Context(ctx: unknown): ctx is V2Context {\n const context = plainObject(ctx)\n if (!context) return false\n const version = plainObject(context.app)?.version\n if (typeof version === \"string\") {\n const major = Number.parseInt(version.replace(/^v/, \"\"), 10)\n if (Number.isFinite(major) && major < 2) return false\n }\n const hasHook = (domain: unknown, name: \"transform\" | \"hook\") =>\n typeof plainObject(domain)?.[name] === \"function\"\n return (\n hasHook(context.provider, \"transform\") &&\n hasHook(context.aisdk, \"hook\") &&\n hasHook(context.session, \"hook\")\n )\n}\n\n/**\n * What a command sends as the session's next message: the same text V1's\n * config template produces (`/<name> $ARGUMENTS`), so the language model's\n * existing `/btw` and doctor branches answer it unchanged.\n */\nexport function commandPromptText(command: string, args: string | undefined): string {\n const rest = (args ?? \"\").trim()\n return rest ? `/${command} ${rest}` : `/${command}`\n}\n\n/** The session a V2 `session.deleted` event names (`data.sessionID`). */\nexport function deletedSessionIdV2(event: V2Event | undefined): string | undefined {\n if (!event || event.type !== \"session.deleted\") return undefined\n const id = event.data?.sessionID\n return typeof id === \"string\" && id.length > 0 ? id : undefined\n}\n\nasync function releaseDeletedSessions(\n subscribe: (options?: { signal?: AbortSignal }) => AsyncIterable<V2Event>,\n signal: AbortSignal,\n): Promise<void> {\n try {\n for await (const event of subscribe({ signal })) {\n if (signal.aborted) break\n const sessionID = deletedSessionIdV2(event)\n if (!sessionID) continue\n const released = deleteActiveProcessesForSession(sessionID)\n if (released.length > 0) {\n log.info(\"released claude state for deleted session\", { sessionID, released })\n }\n }\n } catch (err) {\n if (!signal.aborted) {\n log.warn(\"opencode 2 event subscription ended; deleted sessions keep their claude process until idle eviction\", {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n}\n\nexport interface V2SetupDeps {\n /** `createClaudeCode`, passed in so this module never imports the entrypoint. */\n createProvider: (settings: ClaudeCodeProviderSettings) => {\n languageModel(modelId: string): LanguageModelV3\n }\n defaultProxyTools: readonly string[]\n /** The merged on-disk opencode config for a directory (`loadMergedOpencodeConfig`). */\n loadConfig: (directory: string) => Record<string, unknown>\n /**\n * V1's registry builder, reused so the same agents are eligible for\n * `forceModel`, `defaultSubagentModel` and `reasoningEffort` on both majors:\n * markdown agents on disk plus the config's `agent` block. Not V2's\n * `agent.list()`, which includes opencode's built-ins; those must never be\n * rewritten (src/agent-models.ts).\n */\n buildAgentRegistry?: (config: Record<string, unknown>) => Promise<void>\n}\n\nexport function createV2Setup(deps: V2SetupDeps): (ctx: V2Context) => Promise<V2Cleanup> {\n return async (ctx) => {\n if (!isOpencodeV2Context(ctx)) {\n log.debug(\"setup called outside opencode 2; leaving it to server()\", {\n version: (ctx as { app?: { version?: unknown } } | undefined)?.app?.version,\n })\n return () => undefined\n }\n ensureProcessExitCleanup()\n // The live-state calls (MCP status, tool registry, session lookups) are\n // answered in V1's shapes, so their callers need no V2 branch.\n setOpencodeClient(createV1ClientShim(ctx as unknown as V2ClientContext))\n const directory = ctx.location?.directory\n setOpencodeProjectDirectory(isUsableDirectory(directory) ? directory : undefined)\n\n if (deps.buildAgentRegistry) {\n try {\n const config = deps.loadConfig(directory ?? process.cwd())\n // V1's builder reads `provider.claude-code.options`; hand it the seed\n // settings in that slot whichever config shape they came from.\n await deps.buildAgentRegistry({\n ...config,\n provider: {\n ...(plainObject(config.provider) ?? {}),\n [BASE_PROVIDER_ID]: {\n options: configuredSeedSettings(config, plainObject(ctx.options)),\n },\n },\n })\n } catch (err) {\n log.warn(\"failed to build the agent registry for opencode 2\", {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n const planned = new Map<string, Record<string, unknown>>()\n const registrations: V2Registration[] = []\n\n registrations.push(\n await ctx.provider.transform((editor) => {\n const seed = editor.get(BASE_PROVIDER_ID)\n // Whatever the editor already holds is plugin-level (ours from an\n // earlier pass, or another plugin's); the operator's config wins.\n const seedSettings = {\n ...(seed?.provider.settings ?? {}),\n ...configuredSeedSettings(\n deps.loadConfig(directory ?? process.cwd()),\n plainObject(ctx.options),\n ),\n }\n const plans = planV2Providers(seedSettings, deps.defaultProxyTools)\n planned.clear()\n\n for (const plan of plans) {\n planned.set(plan.info.id, plan.info.settings ?? {})\n if (editor.get(plan.info.id)) {\n editor.update(plan.info.id, (provider) => {\n provider.name = plan.info.name\n provider.activation = plan.info.activation\n provider.package = plan.info.package\n provider.settings = plan.info.settings\n })\n editor.models.set(plan.info.id, plan.models)\n } else {\n editor.add({ info: plan.info, models: plan.models })\n }\n }\n\n // An account expansion replaces the seed, exactly as V1 deletes it.\n if (seed && !plans.some((plan) => plan.info.id === BASE_PROVIDER_ID)) {\n editor.remove(BASE_PROVIDER_ID)\n }\n\n logStartupDiagnostics(\n Object.fromEntries(\n plans.map((plan) => [plan.info.id, { name: plan.info.name, options: plan.info.settings }]),\n ),\n ctx.app?.version,\n )\n }),\n )\n\n registrations.push(\n await ctx.aisdk.hook(\"sdk\", async (event) => {\n const providerID = event.model.providerID\n if (!isClaudeCodeProviderId(providerID)) return\n const settings = await resolveSdkSettings(providerID, planned.get(providerID), event.options)\n event.sdk = deps.createProvider(settings)\n log.debug(\"v2 sdk created\", { providerID, cliPath: settings.cliPath })\n }),\n )\n\n registrations.push(\n await ctx.session.hook(\"model.request\", (event) => {\n if (!isClaudeCodeProviderId(event.model.providerID)) return\n event.headers[SESSION_AFFINITY_HEADER] = event.sessionID\n event.headers[OPENCODE_AGENT_HEADER] = agentForRequest(event.kind, event.agent)\n }),\n )\n\n // `/btw` and `/claude-code-doctor`. V1 injects templates into the user's\n // config and has to guard against clobbering a command of the same name;\n // here they are real commands, and the config's own commands are applied\n // after plugin transforms, so a user-defined one still wins.\n const prompt = ctx.session.prompt\n if (ctx.command?.transform && typeof prompt === \"function\") {\n const send = (\n input: V2CommandInvocation,\n command: string,\n delivery: V2Delivery,\n ): Promise<void> =>\n prompt\n .call(ctx.session, {\n ...input.prompt,\n sessionID: input.sessionID,\n text: commandPromptText(command, input.prompt.text),\n delivery,\n })\n .then(() => undefined)\n registrations.push(\n await ctx.command.transform((editor) => {\n editor.add({\n name: DOCTOR_COMMAND,\n description: DOCTOR_COMMAND_DESCRIPTION,\n execute: (input) => send(input, DOCTOR_COMMAND, input.delivery),\n })\n // Always queued. A `/btw` steered into a running turn becomes that\n // turn's next step, the one carrying the results of the tools opencode\n // just ran, and answering the aside there swallows the continuation\n // (the failure V1's hold exists to prevent). Queued, the aside branch\n // answers it from the idle process once the turn is over. V1's\n // answer-inside-the-running-turn needs a session-status route V2's\n // plugin session domain does not offer.\n editor.add({\n name: \"btw\",\n description: BTW_COMMAND_DESCRIPTION,\n execute: (input) => send(input, \"btw\", \"queue\"),\n })\n }),\n )\n }\n\n // The bundled `claude-code-plugin` skill, listed by opencode for every\n // provider. V1 adds its directory to the config's `skills.paths`; V2 has no\n // config hook, so it is registered directly. A skill of the same id that\n // opencode already found (the operator's own copy) is left alone.\n const bundledSkills = readBundledSkillInfos()\n if (typeof ctx.skill?.transform === \"function\" && bundledSkills.length > 0) {\n registrations.push(\n await ctx.skill.transform((editor) => {\n const added: string[] = []\n for (const skill of bundledSkills) {\n if (editor.get(skill.id)) continue\n editor.add(skill)\n added.push(skill.id)\n }\n log.debug(\"v2 bundled skills registered\", { added })\n }),\n )\n }\n\n // Release a deleted session's `claude` child, proxy server and session id\n // at once, as V1's `event` hook does.\n const events = new AbortController()\n if (typeof ctx.event?.subscribe === \"function\") {\n void releaseDeletedSessions(ctx.event.subscribe.bind(ctx.event), events.signal)\n }\n\n log.info(\"claude-code plugin set up for opencode 2\", {\n opencode: ctx.app?.version,\n directory,\n })\n\n return async () => {\n events.abort()\n for (const registration of registrations.splice(0)) {\n await registration.dispose().catch(() => undefined)\n }\n }\n }\n}\n","import type { LanguageModelV3 } from \"@ai-sdk/provider\"\nimport { ClaudeCodeLanguageModel } from \"./claude-code-language-model.js\"\nimport { defaultModels, toConfigModel } from \"./models.js\"\nimport type {\n OpenCodeConfig,\n OpenCodeEvent,\n OpenCodeModel,\n OpenCodePlugin,\n OpenCodeProvider,\n} from \"./opencode-types.js\"\nimport type { ClaudeCodeProviderSettings } from \"./types.js\"\nimport {\n BASE_PROVIDER_ID,\n accountDisplayName,\n accountModelSuffix,\n accountProviderId,\n ensureAccountRuntime,\n resolveAccounts,\n} from \"./accounts.js\"\nimport {\n type AgentRecord,\n agentDirectories,\n getDefaultSubagentModel,\n readAgentMarkdownRecords,\n setAgentRegistry,\n setDefaultSubagentModel,\n} from \"./agent-models.js\"\nimport { cleanupStaleUnscopedInstall } from \"./cleanup-stale.js\"\nimport { DOCTOR_COMMAND, DOCTOR_COMMAND_DESCRIPTION } from \"./doctor.js\"\nimport { configureLogger, log } from \"./logger.js\"\nimport {\n BTW_COMMAND_DESCRIPTION,\n handleBtwCommand,\n type BtwSdkClient,\n} from \"./btw-command.js\"\nimport { registerBundledSkillPath } from \"./skill-bridge.js\"\nimport {\n deleteActiveProcessesForSession,\n ensureProcessExitCleanup,\n} from \"./session-manager.js\"\nimport { getOpencodeClient } from \"./runtime-status.js\"\nimport {\n getOpencodeProjectDirectory,\n isUsableDirectory,\n setOpencodeClient,\n setOpencodeProjectDirectory,\n} from \"./runtime-status.js\"\nimport {\n logStartupDiagnostics,\n pickOpencodeVersion,\n type DiagnosticsProviderEntry,\n} from \"./startup-diagnostics.js\"\nimport { loadMergedOpencodeConfig } from \"./mcp-bridge.js\"\nimport { createV2Setup } from \"./v2.js\"\n\nexport interface ClaudeCodeProvider {\n specificationVersion: \"v3\"\n (modelId: string): LanguageModelV3\n languageModel(modelId: string): LanguageModelV3\n}\n\n// Picks the best directory from opencode's plugin context (`directory` /\n// `worktree`). Result is handed to runtime-status so it's available as a\n// *fallback* at spawn time only when `process.cwd()` is unusable (macOS\n// GUI launches at `/`). Never baked into provider config — see #4.\nfunction pickOpencodeDirectory(input: unknown): string | undefined {\n if (!input || typeof input !== \"object\") return undefined\n const ctx = input as { directory?: unknown; worktree?: unknown }\n if (isUsableDirectory(ctx.directory)) return ctx.directory\n if (isUsableDirectory(ctx.worktree)) return ctx.worktree\n return undefined\n}\n\nlet warnedAnthropicApiKey = false\nlet warnedPlanModeNoExit = false\n\n// `Question` is deliberately absent: enabling it disables Claude Code's\n// built-in AskUserQuestion (via --disallowedTools) and replaces the\n// stop-and-wait deny/markdown path with an in-turn blocking form. That is a\n// behavior trade against the issue-#8 guarantee, so it stays opt-in until it\n// has the same live mileage Task had before v0.10.0 flipped it on. Users opt\n// in by listing it in `proxyTools`; see README \"Question proxy tool\".\nexport const DEFAULT_PROXY_TOOL_NAMES = [\n \"Bash\",\n \"Edit\",\n \"Write\",\n \"WebFetch\",\n \"Task\",\n]\n\n/**\n * Registers `/btw` unless the user defined their own. Returns whether the\n * registration is ours: the command hook only intercepts `btw` in that case,\n * so a user-defined command keeps opencode's normal behaviour end to end.\n */\nexport function registerSideQuestionCommand(config: OpenCodeConfig): boolean {\n config.command ??= {}\n if (config.command.btw) return false\n config.command.btw = {\n template: \"/btw $ARGUMENTS\",\n description: BTW_COMMAND_DESCRIPTION,\n }\n return true\n}\n\n/**\n * Registers `/claude-code-doctor` unless the user defined their own command of\n * that name. Unlike `/btw` there is no hook to guard: the command is a plain\n * template and the language model answers the message it produces, so leaving\n * a user definition alone here is the whole guard.\n *\n * The name carries no slash. opencode invokes a command as `/<key>` and takes\n * everything after the first space as `$ARGUMENTS`, so `claude-code doctor`\n * would be the command `claude-code` with the argument `doctor`.\n */\nexport function registerDoctorCommand(config: OpenCodeConfig): boolean {\n config.command ??= {}\n if (config.command[DOCTOR_COMMAND]) return false\n config.command[DOCTOR_COMMAND] = {\n template: `/${DOCTOR_COMMAND} $ARGUMENTS`,\n description: DOCTOR_COMMAND_DESCRIPTION,\n }\n return true\n}\n\nlet ownsSideQuestionCommand = false\n\n// One-time heads-up: an API key in the environment makes Claude Code bill\n// pay-as-you-go (Console) instead of the logged-in Pro/Max subscription, which\n// silently bypasses the Agent SDK plan credit. Surfaced once per process.\nfunction warnIfAnthropicApiKey(ignore: boolean | undefined): void {\n if (warnedAnthropicApiKey) return\n if (!process.env.ANTHROPIC_API_KEY && !process.env.ANTHROPIC_AUTH_TOKEN) return\n warnedAnthropicApiKey = true\n if (ignore) {\n log.warn(\n \"ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN detected; stripping it from claude spawns (ignoreAnthropicApiKey) so requests use your subscription auth, not pay-as-you-go API billing.\",\n )\n } else {\n log.warn(\n \"ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN detected; claude may bill as pay-as-you-go API usage instead of your subscription / Agent SDK credit. Set provider option `ignoreAnthropicApiKey: true` to force subscription auth.\",\n )\n }\n}\n\n// Plan mode is enforced (buildCliArgs drops the skip-permissions flag for it),\n// so the read-only guarantee holds. The cost is that headless Claude Code is\n// not offered an `ExitPlanMode` tool, measured on 2.1.258, so nothing can\n// release plan mode mid-session and approving a plan in chat will not let\n// Claude write. Say so once per process rather than let it look like a hang.\nexport function _resetPlanModeWarningForTests(): void {\n warnedPlanModeNoExit = false\n}\n\nexport function warnIfPlanModeCannotExit(permissionMode: string | undefined): void {\n if (permissionMode !== \"plan\") return\n if (warnedPlanModeNoExit) return\n warnedPlanModeNoExit = true\n log.warn(\n \"permissionMode \\\"plan\\\" is enforced: claude cannot edit files or run commands, and --dangerously-skip-permissions is deliberately not passed so it stays that way. Headless Claude Code is not offered an ExitPlanMode tool, so nothing releases plan mode mid-session; approving a plan in chat does not unlock writes. Leaving plan mode means changing the config and restarting opencode.\",\n { permissionMode, measuredOn: \"claude-code 2.1.258\" },\n )\n}\n\nexport function createClaudeCode(\n settings: ClaudeCodeProviderSettings = {},\n): ClaudeCodeProvider {\n if (settings.logging) {\n configureLogger({\n file: settings.logging.file ?? false,\n dir: settings.logging.dir ?? null,\n mode: settings.logging.mode ?? \"silent\",\n level: settings.logging.level ?? \"info\",\n })\n }\n warnIfAnthropicApiKey(settings.ignoreAnthropicApiKey)\n warnIfPlanModeCannotExit(settings.permissionMode)\n const cliPath =\n settings.cliPath ?? process.env.CLAUDE_CLI_PATH ?? \"claude\"\n const providerName = settings.providerID ?? settings.name ?? \"claude-code\"\n const proxyTools = settings.proxyTools ?? [...DEFAULT_PROXY_TOOL_NAMES]\n\n const createModel = (modelId: string): LanguageModelV3 => {\n return new ClaudeCodeLanguageModel(modelId, {\n provider: providerName,\n cliPath,\n hostApi: settings.hostApi,\n cwd: settings.cwd,\n account: settings.account,\n configDir: settings.configDir,\n failoverAccounts: settings.failoverAccounts,\n baseCliPath: settings.baseCliPath ?? cliPath,\n accountFailover: settings.accountFailover ?? \"ask\",\n providerID: settings.providerID,\n skipPermissions: settings.skipPermissions ?? true,\n permissionMode: settings.permissionMode,\n mcpConfig: settings.mcpConfig,\n strictMcpConfig: settings.strictMcpConfig,\n bridgeOpencodeMcp: settings.bridgeOpencodeMcp ?? true,\n controlRequestBehavior: settings.controlRequestBehavior ?? \"allow\",\n controlRequestToolBehaviors: settings.controlRequestToolBehaviors,\n controlRequestDenyMessage: settings.controlRequestDenyMessage,\n proxyTools,\n proxyOpencodeTools: settings.proxyOpencodeTools,\n stripContextReminders: settings.stripContextReminders === true,\n extraDisallowedTools: settings.extraDisallowedTools,\n proxyToolTimeoutMs: settings.proxyToolTimeoutMs,\n planModeQuestion: settings.planModeQuestion ?? false,\n webSearch: settings.webSearch,\n hotReloadMcp: settings.hotReloadMcp ?? true,\n proxyOpencodeMcpTools: settings.proxyOpencodeMcpTools === true,\n multiStepContinuation: settings.multiStepContinuation ?? true,\n autoContinueIncompleteTurns:\n settings.autoContinueIncompleteTurns ?? \"smart\",\n compactionModel: settings.compactionModel,\n ignoreAnthropicApiKey: settings.ignoreAnthropicApiKey,\n idleProcessTimeoutMs: settings.idleProcessTimeoutMs,\n bridgeOpencodeSkills: settings.bridgeOpencodeSkills === true,\n bridgeSkipNativeSkills: settings.bridgeSkipNativeSkills !== false,\n turnStats: settings.turnStats === true,\n interactive: settings.interactive,\n interactiveBypass: settings.interactiveBypass,\n interactiveAllowTools: settings.interactiveAllowTools,\n interactiveSystemPrompt: settings.interactiveSystemPrompt,\n })\n }\n\n const provider = function (modelId: string) {\n return createModel(modelId)\n } as ClaudeCodeProvider\n\n provider.specificationVersion = \"v3\"\n provider.languageModel = createModel\n\n return provider\n}\n\n// ---------------------------------------------------------------------------\n// OpenCode plugin interface\n// ---------------------------------------------------------------------------\n\nconst PROVIDER_ID = BASE_PROVIDER_ID\nconst PACKAGE_NPM = \"@khalilgharbaoui/opencode-claude-code-plugin\"\n\nfunction pluginEntrypoint(): string {\n return import.meta.url.startsWith(\"file:\") ? import.meta.url : PACKAGE_NPM\n}\n\nfunction cleanProviderOptions(\n options: Record<string, unknown> = {},\n): Record<string, unknown> {\n const result = { ...options }\n delete result.accounts\n // Consumed by the config hook (agent registry), not by the language model.\n delete result.defaultSubagentModel\n return result\n}\n\nfunction defaultModelsForProvider(\n providerModels: OpenCodeProvider[\"models\"],\n providerID = PROVIDER_ID,\n modelSuffix?: string,\n) {\n const models = Object.fromEntries(\n Object.entries(defaultModels).map(([id, model]) => {\n const modelId = modelSuffix ? `${id}@${modelSuffix}` : id\n const existing = providerModels[id] ?? providerModels[modelId]\n return [\n modelId,\n {\n ...model,\n id: modelId,\n providerID,\n api: {\n ...model.api,\n id: modelId,\n npm: existing?.api?.npm ?? model.api.npm,\n url: existing?.api?.url ?? model.api.url,\n },\n },\n ]\n }),\n )\n\n for (const [id, model] of Object.entries(providerModels)) {\n if (!(id in models)) {\n models[id] = {\n ...model,\n providerID,\n }\n }\n }\n\n return models\n}\n\n/**\n * Build models in OpenCode's config schema format (flat properties like\n * `temperature`, `reasoning`, `cost.cache_read`, `modalities`, etc.)\n * so the config-path provider loader parses them correctly.\n */\nexport function configModelsForProvider(\n providerModels: OpenCodeProvider[\"models\"],\n providerID: string,\n modelSuffix?: string,\n): Record<string, Record<string, unknown>> {\n const models: Record<string, Record<string, unknown>> = {}\n\n for (const [id, model] of Object.entries(defaultModels)) {\n const modelId = modelSuffix ? `${id}@${modelSuffix}` : id\n const existing = providerModels[id] ?? providerModels[modelId]\n const existingVariants =\n existing && typeof (existing as { variants?: unknown }).variants === \"object\"\n ? ((existing as { variants?: Record<string, Record<string, unknown>> }).variants ?? {})\n : {}\n const full: OpenCodeModel = {\n ...model,\n id: modelId,\n providerID,\n api: {\n ...model.api,\n id: modelId,\n npm: existing?.api?.npm ?? model.api.npm,\n url: existing?.api?.url ?? model.api.url,\n },\n variants: {\n ...(model.variants ?? {}),\n ...existingVariants,\n },\n }\n models[modelId] = toConfigModel(full)\n }\n\n for (const [id, model] of Object.entries(providerModels)) {\n if (!(id in models)) {\n models[id] = toConfigModel({ ...model, providerID } as OpenCodeModel)\n }\n }\n\n return models\n}\n\nasync function providerConfig(\n existing: {\n name?: string\n npm?: string\n options?: Record<string, unknown>\n models?: Record<string, unknown>\n } | undefined,\n providerID = PROVIDER_ID,\n optionDefaults: Record<string, unknown> = {},\n displayName?: string,\n) {\n const mergedOptions: Record<string, unknown> = {\n cliPath: \"claude\",\n proxyTools: [...DEFAULT_PROXY_TOOL_NAMES],\n ...optionDefaults,\n ...cleanProviderOptions(existing?.options),\n providerID,\n }\n\n const cliPath = String(mergedOptions.cliPath ?? \"claude\")\n const account =\n typeof mergedOptions.account === \"string\" ? mergedOptions.account : undefined\n const runtime = account\n ? await ensureAccountRuntime(account, cliPath)\n : { cliPath }\n\n return {\n name: displayName ?? existing?.name,\n npm: existing?.npm ?? pluginEntrypoint(),\n options: {\n ...mergedOptions,\n ...runtime,\n // The pre-wrapper binary, kept because `runtime` replaces `cliPath`\n // with the account's wrapper and a failover has to build ANOTHER\n // account's wrapper on top of the same base (src/account-failover.ts).\n baseCliPath: cliPath,\n },\n // models is intentionally omitted: both callers overwrite it with\n // configModelsForProvider(), which emits the flat config schema\n // opencode's config-path loader parses (and merges user variants).\n }\n}\n\n/**\n * Narrow opencode's full provider map down to the ones this plugin owns\n * (`claude-code` plus every `claude-code-<account>` expansion) so startup\n * diagnostics never report another provider's options.\n */\nexport function claudeCodeProviders(\n providers: Record<string, DiagnosticsProviderEntry> | undefined,\n): Record<string, DiagnosticsProviderEntry> {\n const out: Record<string, DiagnosticsProviderEntry> = {}\n for (const [id, entry] of Object.entries(providers ?? {})) {\n if (id === PROVIDER_ID || id.startsWith(`${PROVIDER_ID}-`)) out[id] = entry\n }\n return out\n}\n\nasync function expandAccountProviders(config: {\n provider?: Record<\n string,\n {\n name?: string\n npm?: string\n options?: Record<string, unknown>\n models?: Record<string, unknown>\n }\n >\n}): Promise<boolean> {\n const seed = config.provider?.[PROVIDER_ID]\n const accounts = resolveAccounts(seed?.options?.accounts)\n\n if (!accounts) return false\n\n config.provider ??= {}\n\n const seedOptions = cleanProviderOptions(seed?.options)\n let expandedCount = 0\n\n for (const account of accounts) {\n const providerID = accountProviderId(account)\n try {\n const existing = config.provider[providerID]\n const modelSuffix = accountModelSuffix(account)\n\n config.provider[providerID] = {\n ...existing,\n ...(await providerConfig(\n existing,\n providerID,\n {\n ...seedOptions,\n account,\n // The resolved list, so this account's language model can offer\n // the others when it runs out of usage. `accounts` itself stays\n // stripped by cleanProviderOptions.\n failoverAccounts: accounts,\n },\n accountDisplayName(account),\n )),\n models: configModelsForProvider(\n (existing?.models ?? seed?.models ?? {}) as OpenCodeProvider[\"models\"],\n providerID,\n modelSuffix,\n ),\n }\n expandedCount++\n } catch (err) {\n log.error(\"failed to expand account provider\", {\n account,\n providerID,\n error: String(err),\n })\n }\n }\n\n if (expandedCount > 0) {\n delete config.provider[PROVIDER_ID]\n }\n\n return expandedCount > 0\n}\n\n/**\n * Record what every known agent asked for, so `resolveAgentModel` and\n * `resolveAgentEffort` can answer at spawn time without the language model\n * needing to see opencode's config.\n *\n * Runs BEFORE `expandAccountProviders`, which deletes the seed provider entry\n * once it has expanded it: `defaultSubagentModel` has to be read while it is\n * still there.\n *\n * Purely observational. It defines no agents and changes no agent's config;\n * an agent this plugin never heard of is simply absent from the registry,\n * which is what keeps opencode's built-ins out of the override path.\n */\nexport async function buildAgentRegistry(config: OpenCodeConfig): Promise<void> {\n const options = config.provider?.[PROVIDER_ID]?.options\n const configured = options?.defaultSubagentModel\n setDefaultSubagentModel(\n typeof configured === \"string\" ? configured : undefined,\n )\n\n // Markdown agents may or may not reach a plugin's config hook (undocumented\n // either way), so they are read from disk and then overlaid with whatever\n // config does carry, which is authoritative when both describe one agent.\n const records: Record<string, AgentRecord> = await readAgentMarkdownRecords(\n agentDirectories(\n process.env.HOME ?? process.env.USERPROFILE,\n getOpencodeProjectDirectory(),\n ),\n )\n\n for (const [name, agent] of Object.entries(config.agent ?? {})) {\n const bag = (agent.options ?? {}) as Record<string, unknown>\n const pick = (key: string): string | undefined => {\n const value = agent[key] ?? bag[key]\n return typeof value === \"string\" ? value : undefined\n }\n\n records[name] = {\n mode: pick(\"mode\") ?? records[name]?.mode,\n model: pick(\"model\") ?? records[name]?.model,\n forceModel: pick(\"forceModel\") ?? records[name]?.forceModel,\n reasoningEffort:\n pick(\"reasoningEffort\") ?? records[name]?.reasoningEffort,\n }\n }\n\n setAgentRegistry(records)\n log.debug(\"agent registry built\", {\n agents: Object.keys(records).length,\n defaultSubagentModel: getDefaultSubagentModel(),\n })\n}\n\n/**\n * The opencode session id a `session.deleted` bus event names, or undefined\n * for any other event. opencode publishes `{ type, properties: { info } }`\n * under `payload`, and the deleted session's own record is `properties.info`.\n */\nexport function extractDeletedSessionId(event: OpenCodeEvent | undefined): string | undefined {\n const payload = event?.payload ?? event\n if (!payload || payload.type !== \"session.deleted\") return undefined\n const properties = payload.properties as { info?: { id?: unknown } } | undefined\n const id = properties?.info?.id\n return typeof id === \"string\" && id.length > 0 ? id : undefined\n}\n\nconst server: OpenCodePlugin = async (input) => {\n cleanupStaleUnscopedInstall()\n // Retained `claude` children would otherwise outlive a hard opencode exit,\n // reparented to init. Armed once per process however often this runs.\n ensureProcessExitCleanup()\n\n const opencodeVersion = pickOpencodeVersion(input)\n\n // Capture the SDK client so the language model can query opencode's\n // in-memory MCP state per-turn for the runtime overlay. `input` is\n // `unknown` here (kept loose since opencode adds fields over time);\n // narrow defensively.\n if (input && typeof input === \"object\" && \"client\" in input) {\n setOpencodeClient((input as { client?: unknown }).client)\n }\n\n // Capture opencode's project-aware directory as a *fallback* used at\n // Claude CLI spawn time only when `process.cwd()` is unusable. Rescues\n // macOS GUI launches at `/` without freezing the value into provider\n // config, so opencode workspace switches mid-session still take effect.\n // See `resolveSpawnCwd` in runtime-status.ts and issue #4.\n setOpencodeProjectDirectory(pickOpencodeDirectory(input))\n\n return {\n config: async (config) => {\n if (registerSideQuestionCommand(config)) ownsSideQuestionCommand = true\n registerDoctorCommand(config)\n // The bundled `claude-code-plugin` skill: opencode lists it for every\n // provider via skills.paths; the spawn path also stages it as a\n // --plugin-dir so Claude's own Skill tool can load it.\n registerBundledSkillPath(config)\n config.provider ??= {}\n\n await buildAgentRegistry(config)\n\n const expanded = await expandAccountProviders(config)\n if (expanded) {\n logStartupDiagnostics(\n claudeCodeProviders(config.provider),\n opencodeVersion,\n )\n return\n }\n\n const existing = config.provider[PROVIDER_ID]\n config.provider[PROVIDER_ID] = {\n ...existing,\n ...(await providerConfig(existing)),\n models: configModelsForProvider(\n (existing?.models ?? {}) as OpenCodeProvider[\"models\"],\n PROVIDER_ID,\n ),\n }\n logStartupDiagnostics(\n claudeCodeProviders(config.provider),\n opencodeVersion,\n )\n },\n // Only `session.deleted` is acted on. MCP config drift is still detected\n // at turn start by the hot-reload check in `claude-code-language-model.ts`,\n // which respawns claude safely between turns, and eviction on\n // `global.disposed` would kill an in-flight stream and abort the user's\n // current turn. A deleted session has no turn left to abort, and its\n // `claude` child would otherwise linger until the idle timer or LRU\n // pressure took it, with its session id kept for a resume that never comes.\n event: async ({ event }) => {\n const sessionID = extractDeletedSessionId(event)\n if (!sessionID) return\n const released = deleteActiveProcessesForSession(sessionID)\n if (released.length > 0) {\n log.info(\"released claude state for deleted session\", { sessionID, released })\n }\n },\n provider: {\n id: PROVIDER_ID,\n models: async (provider) => defaultModelsForProvider(provider.models),\n },\n // Inject opencode's agent name into providerOptions so the language\n // model can distinguish /compact (and title) calls from normal turns.\n // Without this, every no-tools call looks like a title request and\n // gets short-circuited to a synthetic stub.\n // /btw is asked from here, the moment the command is typed, busy or not.\n // The message itself still goes through: opencode queues it behind the\n // running turn and the aside branch in the language model then answers it\n // from the early answer, so the exchange is kept in this conversation.\n \"command.execute.before\": async (input) => {\n if (input.command !== \"btw\" || !ownsSideQuestionCommand) return\n await handleBtwCommand(getOpencodeClient() as BtwSdkClient | null, input)\n },\n \"chat.params\": async (input, output) => {\n const providerID = input.model?.providerID ?? input.provider?.info?.id\n // The hook fires for every provider opencode is configured with, not\n // just ours — keep this at debug to avoid log spam on non-claude-code\n // calls.\n log.debug(\"chat.params hook fired\", {\n agent: input.agent,\n providerID,\n sessionID: input.sessionID,\n })\n if (typeof providerID !== \"string\") return\n if (providerID !== PROVIDER_ID && !providerID.startsWith(`${PROVIDER_ID}-`)) return\n\n // Inject sessionID BEFORE the agent guard so session isolation works\n // even when input.agent is absent (older opencode, provider-switch\n // edge paths). resolveSessionAffinity reads this as a fallback when\n // the x-session-affinity header is missing.\n if (typeof input.sessionID === \"string\" && input.sessionID.length > 0) {\n output.options ??= {}\n ;(output.options as Record<string, unknown>).opencodeSessionID = input.sessionID\n }\n\n if (!input.agent) return\n // opencode wraps the entire `output.options` bag under the providerID\n // via ProviderTransform.providerOptions(model, options) → { [providerID]: options }\n // before handing it to the language model as `providerOptions`. So we\n // write fields at the TOP LEVEL of output.options, not nested under\n // providerID — otherwise the model sees providerOptions[id][id].opencodeAgent.\n output.options ??= {}\n ;(output.options as Record<string, unknown>).opencodeAgent = input.agent\n log.debug(\"chat.params tagged providerOptions\", {\n agent: input.agent,\n sessionID: input.sessionID,\n providerID,\n })\n },\n }\n}\n\n// One package, both opencode majors: 1.x calls `server()`, 2.x calls\n// `setup(ctx)`. This is the documented dual shape from opencode's V1 migration\n// guide, and V1 has accepted an object entrypoint since 1.18.29. See V2.md.\nexport default {\n id: \"@khalilgharbaoui/opencode-claude-code-plugin\",\n server,\n setup: createV2Setup({\n createProvider: createClaudeCode,\n defaultProxyTools: DEFAULT_PROXY_TOOL_NAMES,\n loadConfig: loadMergedOpencodeConfig,\n buildAgentRegistry: (config) => buildAgentRegistry(config as OpenCodeConfig),\n }),\n}\n\n// ---------------------------------------------------------------------------\n// Re-exports\n// ---------------------------------------------------------------------------\n\nexport { ClaudeCodeLanguageModel } from \"./claude-code-language-model.js\"\nexport { bridgeOpencodeMcp } from \"./mcp-bridge.js\"\nexport {\n type AgentRecord,\n getAgentRegistry,\n getDefaultSubagentModel,\n resolveAgentModel,\n} from \"./agent-models.js\"\nexport { defaultModels } from \"./models.js\"\nexport type {\n ClaudeCodeConfig,\n ClaudeCodeProviderSettings,\n ClaudeStreamMessage,\n} from \"./types.js\"\nexport type { OpenCodeHooks, OpenCodeModel, OpenCodePlugin } from \"./opencode-types.js\"\n"],"mappings":";AASA,SAAS,kBAAkB;;;ACT3B,SAAS,gBAAgB,WAAW,YAAY,gBAAgB;AAChE,SAAS,eAAe;AACxB,SAAS,SAAS,YAAY;AAY9B,IAAM,aAAuC;AAAA,EAC3C,OAAO;AAAA,EACP,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AACT;AAEA,IAAM,gBAAgB,IAAI,OAAO;AACjC,IAAM,cAAc,KAAK,QAAQ,GAAG,UAAU,SAAS,sBAAsB;AAE7E,IAAM,iBAA+B;AAAA,EACnC,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,OAAO;AACT;AAEA,SAAS,aAAa,GAA4C;AAChE,MAAI,KAAK,KAAM,QAAO;AACtB,QAAM,IAAI,EAAE,YAAY,EAAE,KAAK;AAC/B,MAAI,MAAM,GAAI,QAAO;AACrB,MAAI,MAAM,OAAO,MAAM,WAAW,MAAM,QAAQ,MAAM,MAAO,QAAO;AACpE,SAAO;AACT;AAEA,SAAS,cAAc,GAA6C;AAClE,MAAI,KAAK,KAAM,QAAO;AACtB,QAAM,IAAI,EAAE,YAAY,EAAE,KAAK;AAC/B,MAAI,MAAM,GAAI,QAAO;AACrB,MAAI,MAAM,WAAW,MAAM,UAAU,MAAM,YAAY,MAAM,UAAU,MAAM,SAAS;AACpF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,sBAAsB,GAA4C;AACzE,MAAI,KAAK,QAAQ,MAAM,GAAI,QAAO;AAClC,SAAO,EAAE,SAAS,sBAAsB,IAAI,UAAU;AACxD;AAEA,SAAS,iBAAiB,MAAkC;AAC1D,QAAM,SAAuB,EAAE,GAAG,KAAK;AACvC,QAAM,UAAU,aAAa,QAAQ,IAAI,6BAA6B;AACtE,MAAI,YAAY,OAAW,QAAO,OAAO;AACzC,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,WAAW,UAAa,WAAW,GAAI,QAAO,MAAM;AACxD,QAAM,UAAU,sBAAsB,QAAQ,IAAI,KAAK;AACvD,MAAI,YAAY,OAAW,QAAO,OAAO;AACzC,QAAM,WAAW,cAAc,QAAQ,IAAI,8BAA8B;AACzE,MAAI,aAAa,OAAW,QAAO,QAAQ;AAC3C,SAAO;AACT;AAEA,IAAI,eAA6B,iBAAiB,cAAc;AAChE,IAAI,sBAAsB;AAYnB,SAAS,gBAAgB,OAAoC;AAClE,QAAM,SAAuB,EAAE,GAAG,gBAAgB,GAAG,MAAM;AAC3D,iBAAe,iBAAiB,MAAM;AACtC,wBAAsB;AACxB;AAYA,SAAS,kBAA0B;AACjC,SAAO,KAAK,aAAa,OAAO,aAAa,YAAY;AAC3D;AAEA,SAAS,eAAe,SAAuB;AAC7C,MAAI;AACF,UAAM,OAAO,SAAS,OAAO;AAC7B,QAAI,KAAK,OAAO,eAAe;AAC7B,iBAAW,SAAS,GAAG,OAAO,IAAI;AAAA,IACpC;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,YAAY,MAAoB;AACvC,MAAI,CAAC,aAAa,KAAM;AACxB,MAAI,oBAAqB;AACzB,MAAI;AACF,UAAM,UAAU,gBAAgB;AAChC,cAAU,QAAQ,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AAC/C,mBAAe,OAAO;AACtB,mBAAe,SAAS,OAAO,MAAM,MAAM;AAAA,EAC7C,QAAQ;AAEN,0BAAsB;AAAA,EACxB;AACF;AAEA,SAAS,IAAI,OAAe,KAAa,MAAwC;AAC/E,QAAM,MAAK,oBAAI,KAAK,GAAE,YAAY;AAClC,QAAM,OAAO,IAAI,EAAE,4BAA4B,KAAK,KAAK,GAAG;AAC5D,MAAI,QAAQ,OAAO,KAAK,IAAI,EAAE,SAAS,GAAG;AACxC,WAAO,GAAG,IAAI,IAAI,KAAK,UAAU,IAAI,CAAC;AAAA,EACxC;AACA,SAAO;AACT;AAEA,SAAS,WAAW,OAA0B;AAC5C,SAAO,WAAW,KAAK,KAAK,WAAW,aAAa,KAAK;AAC3D;AAEA,SAAS,UAAU,OAA0B;AAI3C,MAAI,UAAU,UAAU,UAAU,QAAS,QAAO;AAClD,SAAO,aAAa,SAAS;AAC/B;AAEA,SAAS,KAAK,OAAiB,KAAa,MAAsC;AAChF,MAAI,CAAC,WAAW,KAAK,EAAG;AACxB,QAAM,OAAO,IAAI,MAAM,YAAY,GAAG,KAAK,IAAI;AAC/C,MAAI,UAAU,KAAK,GAAG;AACpB,YAAQ,MAAM,IAAI;AAAA,EACpB;AACA,cAAY,IAAI;AAClB;AAEO,IAAM,MAAM;AAAA,EACjB,MAAM,KAAa,MAAgC;AACjD,SAAK,SAAS,KAAK,IAAI;AAAA,EACzB;AAAA,EACA,KAAK,KAAa,MAAgC;AAChD,SAAK,QAAQ,KAAK,IAAI;AAAA,EACxB;AAAA,EACA,OAAO,KAAa,MAAgC;AAClD,SAAK,UAAU,KAAK,IAAI;AAAA,EAC1B;AAAA,EACA,KAAK,KAAa,MAAgC;AAChD,SAAK,QAAQ,KAAK,IAAI;AAAA,EACxB;AAAA,EACA,MAAM,KAAa,MAAgC;AACjD,SAAK,SAAS,KAAK,IAAI;AAAA,EACzB;AACF;;;ACxJA,IAAM,UAAU,oBAAI,IAA2B;AAE/C,IAAM,wBAAwB;AAQvB,IAAM,sBAAsB;AACnC,IAAM,uBAAuB;AAC7B,IAAM,iBAA0C,oBAAI,IAAI,CAAC,WAAW,eAAe,WAAW,CAAC;AAE/F,SAAS,YAAY,WAAkC;AACrD,MAAI,SAAS,QAAQ,IAAI,SAAS;AAClC,MAAI,CAAC,QAAQ;AACX,aAAS,EAAE,OAAO,oBAAI,IAAI,GAAG,gBAAgB,oBAAI,IAAI,EAAE;AACvD,YAAQ,IAAI,WAAW,MAAM;AAC7B,eAAW;AAAA,EACb;AACA,SAAO;AACT;AAEA,SAAS,aAAmB;AAC1B,SAAO,QAAQ,OAAO,qBAAqB;AACzC,UAAM,SAAS,QAAQ,KAAK,EAAE,KAAK;AACnC,QAAI,OAAO,KAAM;AACjB,YAAQ,OAAO,OAAO,KAAK;AAC3B,QAAI,KAAK,sCAAsC,EAAE,WAAW,OAAO,MAAM,CAAC;AAAA,EAC5E;AACF;AAEA,SAAS,aAAa,QAA6B;AACjD,QAAM,SAAS,KAAK,IAAI,IAAI;AAC5B,aAAW,CAAC,IAAI,OAAO,KAAK,OAAO,gBAAgB;AACjD,QAAI,QAAQ,YAAY,OAAQ,QAAO,eAAe,OAAO,EAAE;AAAA,EACjE;AACF;AAEA,SAAS,YAAY,QAAoC;AACvD,SAAO,MAAM,KAAK,OAAO,MAAM,OAAO,CAAC;AACzC;AAEA,SAAS,eAAe,OAAyE;AAC/F,QAAM,UAAU,OAAO,OAAO,YAAY,WAAW,MAAM,QAAQ,KAAK,IAAI;AAC5E,MAAI,QAAS,QAAO;AACpB,QAAM,cAAc,OAAO,OAAO,gBAAgB,WAAW,MAAM,YAAY,KAAK,IAAI;AACxF,MAAI,YAAa,QAAO;AACxB,SAAO;AACT;AAEO,SAAS,uBACd,WACA,WACA,OACM;AACN,MAAI,CAAC,aAAa,CAAC,UAAW;AAC9B,QAAM,SAAS,YAAY,SAAS;AACpC,eAAa,MAAM;AACnB,SAAO,eAAe,IAAI,WAAW;AAAA,IACnC,SAAS,eAAe,KAAK;AAAA,IAC7B,WAAW,KAAK,IAAI;AAAA,EACtB,CAAC;AACH;AAEO,SAAS,0BACd,WACA,WACA,YACoB;AACpB,MAAI,CAAC,aAAa,CAAC,UAAW,QAAO;AACrC,QAAM,SAAS,QAAQ,IAAI,SAAS;AACpC,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,OAAO,eAAe,IAAI,SAAS;AACnD,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,eAAe,OAAO,SAAS;AACtC,QAAM,QAAQ,OAAO,eAAe,WAAW,WAAW,MAAM,oBAAoB,IAAI;AACxF,MAAI,CAAC,OAAO;AACV,QAAI,MAAM,mDAAmD,EAAE,WAAW,WAAW,WAAW,CAAC;AACjG,WAAO;AAAA,EACT;AACA,QAAM,WAAW,MAAM,CAAC;AACxB,MAAI,OAAO,MAAM,IAAI,QAAQ,GAAG;AAC9B,QAAI,MAAM,8DAA8D,EAAE,WAAW,SAAS,CAAC;AAAA,EACjG;AACA,SAAO,MAAM,IAAI,UAAU,EAAE,IAAI,UAAU,SAAS,QAAQ,SAAS,QAAQ,UAAU,CAAC;AACxF,SAAO,YAAY,MAAM;AAC3B;AAEO,SAAS,gBACd,WACA,OACoB;AACpB,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,SAAS,OAAO,OAAO,WAAW,WAAW,MAAM,SAAS;AAClE,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,SAAS,QAAQ,IAAI,SAAS;AACpC,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,QAAQ,OAAO,MAAM,IAAI,MAAM;AACrC,MAAI,CAAC,OAAO;AACV,QAAI,MAAM,kCAAkC,EAAE,WAAW,OAAO,CAAC;AACjE,WAAO;AAAA,EACT;AACA,MAAI,OAAO,WAAW,WAAW;AAC/B,WAAO,MAAM,OAAO,MAAM;AAC1B,WAAO,YAAY,MAAM;AAAA,EAC3B;AACA,MAAI,OAAO,OAAO,WAAW,YAAY,eAAe,IAAI,MAAM,MAAoB,GAAG;AACvF,UAAM,SAAS,MAAM;AAAA,EACvB;AACA,MAAI,OAAO,OAAO,YAAY,YAAY,MAAM,QAAQ,KAAK,EAAE,SAAS,GAAG;AACzE,UAAM,UAAU,MAAM,QAAQ,KAAK;AAAA,EACrC;AACA,SAAO,YAAY,MAAM;AAC3B;AAEO,SAAS,YAAY,WAAyB;AACnD,MAAI,CAAC,UAAW;AAChB,UAAQ,OAAO,SAAS;AAC1B;;;ACjIO,SAAS,gBAAgB,MAAuB;AACrD,SAAO,SAAS,eAAe,SAAS;AAC1C;AAQO,SAAS,wBAAwB,OAAmC;AACzE,SAAO,CAAC,SAAS,UAAU,YAAY,UAAU;AACnD;AAKA,SAAS,aAAa,MAAc,OAAiB;AACnD,MAAI,CAAC,MAAO,QAAO;AAEnB,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,QACL,UAAU,MAAM,aAAa,MAAM;AAAA,QACnC,SAAS,MAAM;AAAA,MACjB;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,UAAU,MAAM,aAAa,MAAM;AAAA,QACnC,WAAW,MAAM,cAAc,MAAM;AAAA,QACrC,WAAW,MAAM,cAAc,MAAM;AAAA,QACrC,YAAY,MAAM,eAAe,MAAM;AAAA,MACzC;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,UAAU,MAAM,aAAa,MAAM;AAAA,QACnC,QAAQ,MAAM;AAAA,QACd,OAAO,MAAM;AAAA,MACf;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,SAAS,MAAM;AAAA,QACf,aACE,MAAM,eACN,YAAY,OAAO,MAAM,WAAW,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,OAAO,MAAM,WAAW,EAAE,EAAE,SAAS,KAAK,QAAQ,EAAE;AAAA,QAC7G,SAAS,MAAM;AAAA,MACjB;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,cAAc,MAAM,iBAAiB,MAAM;AAAA,QAC3C,YAAY,MAAM,eAAe,MAAM;AAAA,QACvC,WAAW,MAAM,cAAc,MAAM;AAAA,QACrC,UAAU,MAAM,aAAa,MAAM;AAAA,QACnC,UAAU,MAAM,aAAa,MAAM;AAAA,MACrC;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,SAAS,MAAM;AAAA,QACf,MAAM,MAAM;AAAA,MACd;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,SAAS,MAAM;AAAA,QACf,MAAM,MAAM;AAAA,QACZ,SAAS,MAAM;AAAA,MACjB;AAAA,IACF,KAAK;AACH,UAAI,MAAM,QAAQ,MAAM,KAAK,GAAG;AAC9B,cAAM,cAAc,MAAM,MAAM,IAAI,CAAC,MAAW,WAAmB;AAAA,UACjE,SAAS,KAAK;AAAA,UACd,QAAQ,KAAK,UAAU;AAAA,UACvB,UAAU,KAAK,YAAY;AAAA,UAC3B,IAAI,KAAK,MAAM,QAAQ,KAAK,IAAI,CAAC,IAAI,KAAK;AAAA,QAC5C,EAAE;AACF,eAAO,EAAE,OAAO,YAAY;AAAA,MAC9B;AACA,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAGA,IAAM,yBAAyB,oBAAI,IAAI;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AASD,IAAM,wBAAwB,oBAAI,IAAI;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAYM,SAAS,oBAAoB,OAAuB;AACzD,SAAO,IAAI,MAAM,QAAQ,MAAM,OAAO,CAAC;AACzC;AAEA,SAAS,cAAc,OAAoB;AACzC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,MACL,OAAO,MAAM,IAAI,CAAC,UAAU;AAAA,QAC1B,IAAI,KAAK;AAAA,QACT,SAAS,KAAK;AAAA,QACd,QAAQ,KAAK;AAAA,QACb,UAAU;AAAA,MACZ,EAAE;AAAA,IACJ;AAAA,IACA,UAAU;AAAA,EACZ;AACF;AAEO,SAAS,QACd,MACA,OACA,MACkE;AAElE,MAAI,sBAAsB,IAAI,IAAI,GAAG;AACnC,QAAI,MAAM,qCAAqC,EAAE,KAAK,CAAC;AACvD,WAAO,EAAE,MAAM,OAAO,UAAU,MAAM,MAAM,KAAK;AAAA,EACnD;AAKA,MAAI,SAAS,cAAc;AACzB,QAAI,MAAM,aAAa,MAAM,WAAW;AACtC,6BAAuB,KAAK,WAAW,KAAK,WAAW,KAAK;AAAA,IAC9D;AACA,WAAO,EAAE,MAAM,OAAO,UAAU,MAAM,MAAM,KAAK;AAAA,EACnD;AAKA,MAAI,SAAS,cAAc;AACzB,QAAI,MAAM,WAAW;AACnB,YAAMA,QAAO,gBAAgB,KAAK,WAAW,KAAK;AAClD,UAAIA,UAAS,KAAM,QAAO,cAAcA,KAAI;AAAA,IAC9C;AACA,WAAO,EAAE,MAAM,OAAO,UAAU,MAAM,MAAM,KAAK;AAAA,EACnD;AAGA,MAAI,SAAS,gBAAiB,QAAO,EAAE,MAAM,cAAc,OAAO,CAAC,GAAG,UAAU,MAAM;AACtF,MAAI,SAAS,eAAgB,QAAO,EAAE,MAAM,aAAa,OAAO,UAAU,MAAM;AAKhF,MAAI,SAAS,aAAa;AACxB,UAAM,cAAc,aAAa,MAAM,KAAK;AAC5C,WAAO,EAAE,MAAM,aAAa,OAAO,aAAa,UAAU,MAAM;AAAA,EAClE;AAGA,MAAI,gBAAgB,IAAI,GAAG;AACzB,UAAM,cAAc,OAAO,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI;AAC5D,UAAM,QAAQ,MAAM;AACpB,QAAI,SAAS,UAAU,YAAY,UAAU,YAAY;AACvD,UAAI,MAAM,sCAAsC,EAAE,QAAQ,OAAO,YAAY,CAAC;AAC9E,aAAO,EAAE,MAAM,OAAO,OAAO,aAAa,UAAU,MAAM;AAAA,IAC5D;AAKA,QAAI,MAAM,oCAAoC,EAAE,YAAY,CAAC;AAC7D,WAAO,EAAE,MAAM,aAAa,OAAO,aAAa,UAAU,MAAM,MAAM,KAAK;AAAA,EAC7E;AAGA,MAAI,SAAS,cAAc;AACzB,QAAI,CAAC,MAAO,QAAO,EAAE,MAAM,QAAQ,UAAU,MAAM;AACnD,UAAM,SAAS,OAAO,WAAW,OAAO,UAAU,KAAK,UAAU,KAAK;AACtE,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,QACL,SAAS,kBAAkB,oBAAoB,gBAAgB,OAAO,MAAM,CAAC,EAAE,CAAC;AAAA,QAChF,aAAa;AAAA,MACf;AAAA,MACA,UAAU;AAAA,IACZ;AAAA,EACF;AAYA,MAAI,KAAK,WAAW,OAAO,GAAG;AAC5B,UAAM,QAAQ,KAAK,MAAM,CAAC,EAAE,MAAM,IAAI;AACtC,QAAI,MAAM,UAAU,GAAG;AACrB,YAAM,aAAa,MAAM,CAAC;AAC1B,YAAM,WAAW,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG;AACxC,YAAM,eAAe,GAAG,UAAU,IAAI,QAAQ;AAC9C,UAAI,MAAM,oBAAoB,EAAE,UAAU,MAAM,QAAQ,aAAa,CAAC;AACtE,aAAO,EAAE,MAAM,cAAc,OAAO,UAAU,KAAK;AAAA,IACrD;AAAA,EACF;AAGA,MAAI,uBAAuB,IAAI,IAAI,GAAG;AACpC,UAAM,cAAc,aAAa,MAAM,KAAK;AAC5C,UAAM,eAAe,KAAK,YAAY;AACtC,QAAI,MAAM,6BAA6B,EAAE,MAAM,aAAa,CAAC;AAC7D,WAAO,EAAE,MAAM,cAAc,OAAO,aAAa,UAAU,KAAK;AAAA,EAClE;AAGA,SAAO,EAAE,MAAM,OAAO,UAAU,KAAK;AACvC;;;AC9NA,SAAS,QAAQ,OAAqB;AACpC,SAAO,OAAO,YAAY,OAAO,QAAQ,KAAK,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,UAAU,MAAS,CAAC;AAC5F;AAMA,IAAM,WAA4C;AAAA,EAChD,MAAM;AAAA,IACJ,MAAM;AAAA;AAAA;AAAA,IAGN,OAAO,CAAC,UACN,QAAQ;AAAA,MACN,SAAS,MAAM;AAAA,MACf,SAAS,MAAM;AAAA,MACf,SAAS,MAAM;AAAA,MACf,YAAY,MAAM;AAAA,IACpB,CAAC;AAAA,EACL;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO,CAAC,UACN,QAAQ;AAAA,MACN,MAAM,MAAM,YAAY,MAAM;AAAA,MAC9B,WAAW,MAAM;AAAA,MACjB,WAAW,MAAM;AAAA,MACjB,YAAY,MAAM;AAAA,IACpB,CAAC;AAAA,EACL;AAAA,EACA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO,CAAC,UAAU,QAAQ,EAAE,MAAM,MAAM,YAAY,MAAM,MAAM,SAAS,MAAM,QAAQ,CAAC;AAAA,EAC1F;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO,CAAC,UACN,QAAQ,EAAE,MAAM,MAAM,YAAY,MAAM,MAAM,QAAQ,MAAM,QAAQ,OAAO,MAAM,MAAM,CAAC;AAAA,EAC5F;AAAA,EACA,MAAM,EAAE,MAAM,OAAO;AAAA,EACrB,MAAM,EAAE,MAAM,OAAO;AAAA,EACrB,UAAU,EAAE,MAAM,WAAW;AAAA,EAC7B,WAAW,EAAE,MAAM,YAAY;AAAA,EAC/B,UAAU,EAAE,MAAM,WAAW;AAAA,EAC7B,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO,CAAC,UACN,QAAQ;AAAA,MACN,OAAO,MAAM,iBAAiB,MAAM;AAAA,MACpC,aAAa,MAAM;AAAA,MACnB,QAAQ,MAAM;AAAA,MACd,WAAW,MAAM,WAAW,MAAM;AAAA,MAClC,YAAY,MAAM;AAAA,IACpB,CAAC;AAAA,EACL;AAAA,EACA,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,cAAc;AAChB;AAoBA,SAAS,WAAW,KAAqB;AACvC,MAAI,OAAO,QAAQ,SAAU,QAAQ,OAAiB,CAAC;AACvD,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,WAAO,UAAU,OAAO,WAAW,WAAY,SAAmB,CAAC;AAAA,EACrE,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAQO,SAAS,6BACd,UAA2B,MACc;AACzC,MAAI,YAAY,KAAM,QAAO,CAAC,SAAS;AAEvC,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,aAAa,oBAAI,IAAsB;AAE7C,QAAMC,YAAW,CAAC,IAAY,SAA+C;AAC3E,QAAI,QAAQ,IAAI,EAAE,EAAG,QAAO;AAC5B,UAAM,QAAQ,WAAW,IAAI,EAAE;AAC/B,QAAI,MAAO,QAAO;AAClB,QAAI,OAAO,SAAS,SAAU,QAAO;AACrC,UAAM,OAAO,SAAS,IAAI;AAC1B,QAAI,SAAS,MAAM;AACjB,cAAQ,IAAI,EAAE;AACd,aAAO;AAAA,IACT;AACA,QAAI,KAAM,YAAW,IAAI,IAAI,IAAI;AACjC,WAAO;AAAA,EACT;AAEA,SAAO,CAAC,SAAS;AACf,YAAQ,KAAK,MAAM;AAAA,MACjB,KAAK,oBAAoB;AACvB,cAAM,OAAOA,UAAS,OAAO,KAAK,EAAE,GAAG,KAAK,QAAQ;AACpD,YAAI,SAAS,KAAM,QAAO;AAC1B,eAAO,OAAO,EAAE,GAAG,MAAM,UAAU,KAAK,KAAK,IAAI;AAAA,MACnD;AAAA,MACA,KAAK,oBAAoB;AACvB,cAAM,OAAOA,UAAS,OAAO,KAAK,EAAE,GAAG,MAAS;AAChD,YAAI,SAAS,KAAM,QAAO;AAG1B,eAAO,MAAM,QAAQ,OAAO;AAAA,MAC9B;AAAA,MACA,KAAK,kBAAkB;AACrB,eAAOA,UAAS,OAAO,KAAK,EAAE,GAAG,MAAS,MAAM,OAAO,OAAO;AAAA,MAChE;AAAA,MACA,KAAK,aAAa;AAChB,cAAM,OAAOA,UAAS,OAAO,KAAK,UAAU,GAAG,KAAK,QAAQ;AAC5D,YAAI,SAAS,KAAM,QAAO;AAC1B,YAAI,CAAC,KAAM,QAAO;AAClB,cAAM,QAAQ,KAAK,QAAQ,KAAK,MAAM,WAAW,KAAK,KAAK,CAAC,IAAI,WAAW,KAAK,KAAK;AACrF,eAAO;AAAA,UACL,GAAG;AAAA,UACH,UAAU,KAAK;AAAA,UACf,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,UAAU,KAAK,IAAI;AAAA,QAClE;AAAA,MACF;AAAA,MACA,KAAK,eAAe;AAClB,cAAM,OAAOA,UAAS,OAAO,KAAK,UAAU,GAAG,KAAK,QAAQ;AAC5D,YAAI,SAAS,KAAM,QAAO;AAC1B,eAAO,OAAO,EAAE,GAAG,MAAM,UAAU,KAAK,KAAK,IAAI;AAAA,MACnD;AAAA,MACA;AACE,eAAO;AAAA,IACX;AAAA,EACF;AACF;AAGO,SAAS,uBACd,QACA,UAA2B,MACR;AACnB,MAAI,YAAY,KAAM,QAAO;AAC7B,QAAM,YAAY,6BAA6B,OAAO;AACtD,SAAO,OAAO;AAAA,IACZ,IAAI,gBAAsB;AAAA,MACxB,UAAU,MAAM,YAAY;AAC1B,cAAM,OAAO,UAAU,IAAI;AAC3B,YAAI,KAAM,YAAW,QAAQ,IAAS;AAAA,MACxC;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;AC5MA,SAAS,WAAAC,gBAAe;;;ACAxB,SAAS,OAAO,OAAO,OAAO,UAAU,SAAS,iBAAiB;AAClE,OAAO,UAAU;AAGV,IAAM,mBAAmB;AACzB,IAAM,kBAAkB;AAE/B,IAAM,0BAA0B;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,qBAAqB,SAAyB;AAC5D,SAAO,QACJ,KAAK,EACL,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE;AAC3B;AAEO,SAAS,gBAAgB,OAAiC;AAC/D,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO;AAElC,QAAM,WAAW,MACd,IAAI,CAAC,YAAY,qBAAqB,OAAO,OAAO,CAAC,CAAC,EACtD,OAAO,OAAO;AAEjB,SAAO,MAAM,KAAK,oBAAI,IAAI,CAAC,iBAAiB,GAAG,QAAQ,CAAC,CAAC;AAC3D;AAEO,SAAS,kBAAkB,SAAyB;AACzD,SAAO,GAAG,gBAAgB,IAAI,qBAAqB,OAAO,CAAC;AAC7D;AAEO,SAAS,mBAAmB,SAAyB;AAC1D,SAAO,gBAAgB,gBAAgB,OAAO,CAAC;AACjD;AAEO,SAAS,mBAAmB,SAAqC;AACtE,QAAM,aAAa,qBAAqB,OAAO;AAC/C,SAAO,eAAe,kBAAkB,SAAY;AACtD;AAEO,SAAS,iBAAiB,SAAqC;AACpE,QAAM,aAAa,qBAAqB,OAAO;AAE/C,MAAI,CAAC,cAAc,eAAe,gBAAiB,QAAO;AAE1D,SAAO,aAAa,UAAU;AAChC;AAQO,SAAS,qBAAqB,SAAqC;AACxE,QAAM,MAAM,iBAAiB,OAAO;AACpC,SAAO,MAAM,WAAW,GAAG,IAAI;AACjC;AAEO,SAAS,WAAW,OAAuB;AAChD,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI;AAE7C,MAAI,UAAU,IAAK,QAAO,QAAQ;AAElC,MAAI,MAAM,WAAW,IAAI,KAAK,MAAM,WAAW,KAAK,GAAG;AACrD,WAAO,OAAO,KAAK,KAAK,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI;AAAA,EAClD;AAEA,SAAO;AACT;AAEA,eAAsB,qBACpB,SACA,aACkD;AAClD,QAAM,YAAY,iBAAiB,OAAO;AAE1C,MAAI,CAAC,UAAW,QAAO,EAAE,SAAS,YAAY;AAE9C,QAAM,oBAAoB,WAAW,SAAS;AAC9C,QAAM,MAAM,mBAAmB,EAAE,WAAW,KAAK,CAAC;AAElD,MAAI;AACF,UAAM,yBAAyB,iBAAiB;AAAA,EAClD,SAAS,KAAK;AACZ,QAAI,KAAK,4DAA4D;AAAA,MACnE;AAAA,MACA,WAAW;AAAA,MACX,OAAO,OAAO,GAAG;AAAA,IACnB,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB,qBAAqB,OAAO;AAAA,IAC5B;AAAA,IACA;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,WAAW,kBAAkB;AACjD;AAEA,eAAe,yBAAyB,YAAmC;AACzE,QAAM,aAAa,WAAW,WAAW;AAEzC,aAAW,QAAQ,yBAAyB;AAC1C,UAAM,2BAA2B,YAAY,YAAY,IAAI;AAAA,EAC/D;AACF;AAEA,eAAe,2BACb,YACA,YACA,MACe;AACf,QAAM,SAAS,KAAK,KAAK,YAAY,IAAI;AACzC,QAAM,SAAS,KAAK,KAAK,YAAY,IAAI;AAEzC,MAAI;AACJ,MAAI;AACF,iBAAa,MAAM,MAAM,MAAM;AAAA,EACjC,QAAQ;AACN;AAAA,EACF;AAEA,MAAI;AACF,UAAM,aAAa,MAAM,MAAM,MAAM;AAErC,QAAI,WAAW,eAAe,GAAG;AAC/B,YAAM,UAAU,MAAM,SAAS,MAAM;AACrC,YAAM,kBAAkB,KAAK,QAAQ,KAAK,QAAQ,MAAM,GAAG,OAAO;AAClE,YAAM,iBAAiB,KAAK,QAAQ,MAAM;AAE1C,UAAI,oBAAoB,eAAgB;AAAA,IAC1C;AAEA,QAAI,KAAK,8DAA8D;AAAA,MACrE;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,QAAM,OAAO,WAAW,YAAY,IAChC,QAAQ,aAAa,UACnB,aACA,QACF;AAEJ,QAAM,QAAQ,QAAQ,QAAQ,IAAI;AACpC;AAEA,eAAe,oBACb,SACA,aACA,WACiB;AACjB,QAAM,YAAY,KAAK;AAAA,IACrB,QAAQ,IAAI,kBAAkB,WAAW,UAAU;AAAA,IACnD;AAAA,EACF;AACA,QAAM,cAAc,KAAK,KAAK,WAAW,UAAU,OAAO,EAAE;AAC5D,QAAM,SAAS,IAAI,OAAO;AAE1B,QAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAE1C,QAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAOQ,iBAAiB,MAAM,CAAC;AAAA,wBACzB,iBAAiB,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAUrB,iBAAiB,SAAS,CAAC;AAAA,OAC/C,iBAAiB,WAAW,CAAC;AAAA;AAGlC,QAAM,UAAU,aAAa,QAAQ,MAAM;AAC3C,QAAM,MAAM,aAAa,GAAK;AAE9B,SAAO;AACT;AAEA,SAAS,iBAAiB,OAAuB;AAC/C,SAAO,IAAI,MAAM,QAAQ,MAAM,OAAO,CAAC;AACzC;AAEA,SAAS,iBAAiB,OAAuB;AAC/C,SAAO,MAAM,QAAQ,YAAY,MAAM;AACzC;AAEA,SAAS,gBAAgB,SAAyB;AAChD,SAAO,qBAAqB,OAAO,EAChC,MAAM,GAAG,EACT,OAAO,OAAO,EACd,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,EAC1D,KAAK,GAAG;AACb;;;ACzMA,SAAS,SAAS,OAAkD;AAClE,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,IAAI,OAAoC;AAC/C,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AACjE;AAEA,SAAS,IAAI,OAAoC;AAC/C,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,QAAQ;AACvE;AAuBO,IAAM,oBAAoB;AAO1B,IAAM,oBACX;AAEF,IAAM,2BAAmD;AAAA,EACvD,yBAAyB;AAAA,EACzB,oBAAoB;AAAA,EACpB,0BAA0B;AAAA,EAC1B,gBAAgB;AAAA,EAChB,0BAA0B;AAAA,EAC1B,uBAAuB;AAAA,EACvB,6BAA6B;AAAA,EAC7B,yBAAyB;AAAA,EACzB,0BAA0B;AAAA,EAC1B,4BAA4B;AAAA,EAC5B,sBAAsB;AAAA,EACtB,aAAa;AACf;AAEA,IAAM,qBAA6C;AAAA,EACjD,WAAW;AAAA,EACX,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,4BAA4B;AAAA,EAC5B,SAAS;AACX;AAEO,SAAS,oBAAoB,KAAgD;AAClF,MAAI,IAAI,SAAS,mBAAoB,QAAO;AAC5C,QAAM,OAAQ,IAAsC;AACpD,MAAI,CAAC,SAAS,IAAI,EAAG,QAAO;AAC5B,SAAO;AAAA,IACL,QAAQ,IAAI,KAAK,MAAM;AAAA,IACvB,eAAe,IAAI,KAAK,aAAa;AAAA,IACrC,UAAU,IAAI,KAAK,QAAQ;AAAA,IAC3B,aAAa,IAAI,KAAK,WAAW;AAAA,IACjC,gBAAgB,KAAK,mBAAmB;AAAA,IACxC,eAAe,IAAI,KAAK,aAAa;AAAA,IACrC,iBAAiB,IAAI,KAAK,eAAe;AAAA,IACzC,uBAAuB,IAAI,KAAK,qBAAqB;AAAA,EACvD;AACF;AAGO,SAAS,aAAa,UAAkD;AAC7E,MAAI,aAAa,UAAa,CAAC,OAAO,SAAS,QAAQ,EAAG,QAAO;AACjE,SAAO,WAAW,OAAO,WAAW,MAAO;AAC7C;AAEO,SAAS,eAAe,UAAkD;AAC/E,QAAM,KAAK,aAAa,QAAQ;AAChC,MAAI,OAAO,OAAW,QAAO;AAC7B,QAAM,OAAO,IAAI,KAAK,EAAE;AACxB,SAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,SAAY,KAAK,YAAY;AACrE;AAGO,SAAS,aAAa,MAA6B;AACxD,SAAO;AAAA,IACL,KAAK,UAAU;AAAA,IACf,KAAK,iBAAiB;AAAA,IACtB,KAAK,iBAAiB;AAAA,IACtB,KAAK,yBAAyB;AAAA,EAChC,EAAE,KAAK,GAAG;AACZ;AAaO,SAAS,oBAAoB,MAA8B;AAChE,MAAI,KAAK,WAAW,WAAY,QAAO;AACvC,MAAI,KAAK,WAAW,aAAa,KAAK,WAAW,kBAAmB,QAAO;AAC3E,SAAO,KAAK,kBAAkB;AAChC;AAgBO,SAAS,kBAAkB,MAA6C;AAC7E,MAAI,CAAC,KAAK,UAAU,CAAC,KAAK,cAAe,QAAO;AAChD,QAAM,SAAS,KAAK,gBAAgB,mBAAmB,KAAK,aAAa,KAAK,KAAK,gBAAgB;AACnG,QAAM,SAAS,eAAe,KAAK,QAAQ;AAC3C,QAAM,gBAAgB,eAAe,KAAK,eAAe;AACzD,QAAM,MAAM,aAAa,IAAI;AAE7B,MAAI,oBAAoB,IAAI,GAAG;AAC7B,UAAM,QAAkB,CAAC;AACzB,UAAM;AAAA,MACJ,KAAK,WAAW,aACZ,0DAA0D,SAAS,OAAO,MAAM,KAAK,EAAE,MACvF;AAAA,IACN;AACA,UAAM,SAAS,KAAK,wBAChB,yBAAyB,KAAK,qBAAqB,KAAK,KAAK,wBAC7D;AACJ,QAAI,OAAQ,OAAM,KAAK,sCAAsC,MAAM,GAAG;AACtE,UAAM,UAAU,UAAU;AAC1B,QAAI,QAAS,OAAM,KAAK,aAAa,OAAO,GAAG;AAC/C,UAAM,KAAK,iBAAiB;AAC5B,UAAM,UAAU,MAAM,KAAK,GAAG;AAC9B,WAAO,EAAE,KAAK,OAAO,QAAQ,SAAS,YAAY;AAAA,EAAK,iBAAiB,IAAI,OAAO;AAAA,EAAK;AAAA,EAC1F;AAEA,MAAI,KAAK,WAAW,qBAAqB,KAAK,kBAAkB,mBAAmB;AACjF,UAAM,OACJ,KAAK,gBAAgB,SAAY,KAAK,KAAK,KAAK,MAAM,KAAK,cAAc,GAAG,CAAC;AAC/E,WAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,MACP,SAAS,8BAA8B,SAAS,QAAQ,MAAM,KAAK,EAAE,GAAG,IAAI,GAC1E,SAAS,eAAe,MAAM,KAAK,EACrC;AAAA,MACA,YAAY;AAAA,IACd;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,OAAO;AAAA,IACP,SAAS,uBAAuB,SAAS,QAAQ,MAAM,KAAK,EAAE;AAAA,IAC9D,YAAY;AAAA,EACd;AACF;AAEA,IAAM,qBAAqB,oBAAI,IAAY;AAYpC,SAAS,qBAAqB,KAAyC;AAC5E,QAAM,OAAO,oBAAoB,GAAG;AACpC,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,SAAS,kBAAkB,IAAI;AACrC,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,OAAgC,EAAE,GAAG,KAAK;AAChD,MAAI,mBAAmB,IAAI,OAAO,GAAG,GAAG;AACtC,QAAI,MAAM,OAAO,SAAS,IAAI;AAC9B,WAAO;AAAA,EACT;AACA,qBAAmB,IAAI,OAAO,GAAG;AACjC,MAAI,OAAO,KAAK,EAAE,OAAO,SAAS,IAAI;AACtC,SAAO,OAAO;AAChB;AAsBO,IAAM,kBAAkB,oBAAI,IAAI;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,SAAS,gBAAgB,KAAiD;AAC/E,MAAI,IAAI,SAAS,YAAY,IAAI,YAAY,OAAQ,QAAO;AAC5D,QAAM,MAAM;AACZ,QAAM,UAAmD,CAAC;AAC1D,MAAI,MAAM,QAAQ,IAAI,WAAW,GAAG;AAClC,eAAW,SAAS,IAAI,aAAa;AACnC,UAAI,CAAC,SAAS,KAAK,EAAG;AACtB,cAAQ,KAAK,EAAE,MAAM,IAAI,MAAM,IAAI,KAAK,WAAW,QAAQ,IAAI,MAAM,MAAM,KAAK,UAAU,CAAC;AAAA,IAC7F;AAAA,EACF;AACA,SAAO;AAAA,IACL,cAAc,IAAI,IAAI,YAAY;AAAA,IAClC,gBAAgB,IAAI,IAAI,cAAc;AAAA,IACtC,OAAO,IAAI,IAAI,KAAK;AAAA,IACpB,YAAY,IAAI,IAAI,mBAAmB;AAAA,IACvC,WAAW,MAAM,QAAQ,IAAI,KAAK,IAAI,IAAI,MAAM,SAAS;AAAA,IACzD,YAAY;AAAA,EACd;AACF;AAWO,SAAS,oBACd,cACA,uBACe;AACf,MAAI,CAAC,gBAAgB,CAAC,gBAAgB,IAAI,YAAY,EAAG,QAAO;AAChE,QAAM,OAAO,4DAA4D,YAAY;AACrF,SAAO,wBACH,GAAG,IAAI,sKACP,GAAG,IAAI;AACb;AAEA,IAAM,sBAAsB,oBAAI,IAAY;AAC5C,IAAM,oBAAoB,oBAAI,IAAY;AAcnC,SAAS,iBACd,KACA,UAA+C,CAAC,GAC1C;AACN,QAAM,OAAO,gBAAgB,GAAG;AAChC,MAAI,CAAC,KAAM;AACX,MAAI,KAAK,uBAAuB;AAAA,IAC9B,cAAc,KAAK,gBAAgB;AAAA,IACnC,gBAAgB,KAAK,kBAAkB;AAAA,IACvC,OAAO,KAAK,SAAS;AAAA,IACrB,YAAY,KAAK,cAAc;AAAA,IAC/B,OAAO,KAAK;AAAA,IACZ,YAAY,KAAK;AAAA,EACnB,CAAC;AAED,aAAWC,WAAU,KAAK,YAAY;AACpC,QAAIA,QAAO,WAAW,YAAa;AACnC,UAAMC,OAAM,GAAGD,QAAO,IAAI,IAAIA,QAAO,MAAM;AAC3C,UAAM,UAAU,eAAeA,QAAO,IAAI,QAAQA,QAAO,MAAM;AAC/D,QAAI,kBAAkB,IAAIC,IAAG,GAAG;AAC9B,UAAI,MAAM,SAAS,EAAE,QAAQD,QAAO,MAAM,QAAQA,QAAO,OAAO,CAAC;AACjE;AAAA,IACF;AACA,sBAAkB,IAAIC,IAAG;AACzB,QAAI,KAAK,SAAS,EAAE,QAAQD,QAAO,MAAM,QAAQA,QAAO,OAAO,CAAC;AAAA,EAClE;AAEA,QAAM,gBAAgB,oBAAoB,KAAK,cAAc,QAAQ,qBAAqB;AAC1F,MAAI,CAAC,cAAe;AACpB,QAAM,MAAM,GAAG,KAAK,YAAY,IAAI,QAAQ,wBAAwB,YAAY,QAAQ;AACxF,MAAI,oBAAoB,IAAI,GAAG,GAAG;AAChC,QAAI,MAAM,eAAe,EAAE,cAAc,KAAK,aAAa,CAAC;AAC5D;AAAA,EACF;AACA,sBAAoB,IAAI,GAAG;AAC3B,MAAI,KAAK,eAAe,EAAE,cAAc,KAAK,aAAa,CAAC;AAC7D;AAMO,IAAM,0BAA0B;AAkBhC,SAAS,qBAAqB,KAAkD;AACrF,MAAI,IAAI,SAAS,YAAY,IAAI,YAAY,mBAAoB,QAAO;AACxE,QAAM,MAAM;AACZ,QAAM,OAAO,SAAS,IAAI,gBAAgB,IACtC,IAAI,mBACJ,SAAS,IAAI,eAAe,IAC1B,IAAI,kBACJ;AACN,SAAO;AAAA,IACL,SAAS,IAAI,MAAM,OAAO,KAAK;AAAA,IAC/B,WAAW,IAAI,MAAM,UAAU,KAAK,IAAI,MAAM,SAAS;AAAA,IACvD,YAAY,IAAI,MAAM,WAAW,KAAK,IAAI,MAAM,UAAU;AAAA,EAC5D;AACF;AAEO,SAAS,0BAA0B,UAAmC;AAC3E,QAAM,MAAM,SAAS,YAAY,SAAS,eAAe,QAAQ,SAAS,OAAO;AACjF,QAAM,QACJ,SAAS,cAAc,UAAa,SAAS,eAAe,SACxD,KAAK,SAAS,UAAU,eAAe,OAAO,CAAC,cAAc,SAAS,WAAW,eAAe,OAAO,CAAC,MACxG;AACN,SAAO;AAAA,EAAK,uBAAuB,0CAA0C,GAAG,GAAG,KAAK;AAAA;AAC1F;AAGO,SAAS,sBAAsB,KAAyC;AAC7E,QAAM,WAAW,qBAAqB,GAAG;AACzC,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,OAAO,yCAAyC;AAAA,IAClD,SAAS,SAAS;AAAA,IAClB,WAAW,SAAS,aAAa;AAAA,IACjC,YAAY,SAAS,cAAc;AAAA,EACrC,CAAC;AACD,SAAO,0BAA0B,QAAQ;AAC3C;AAMO,IAAM,4BAA4B;AAkBlC,SAAS,uBAAuB,KAAoD;AACzF,MAAI,IAAI,SAAS,qBAAsB,QAAO;AAC9C,QAAM,oBAAoB,IAAI,IAAI,mBAAmB;AACrD,MAAI,CAAC,kBAAmB,QAAO;AAC/B,SAAO,EAAE,mBAAmB,mBAAmB,IAAI,IAAI,UAAU,EAAE;AACrE;AAOO,SAAS,8BAAsC;AACpD,SAAO;AAAA,EAAK,yBAAyB;AAAA;AACvC;AAGO,SAAS,wBAAwB,KAAyC;AAC/E,QAAM,QAAQ,uBAAuB,GAAG;AACxC,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,OAAO,sCAAsC;AAAA,IAC/C,mBAAmB,MAAM;AAAA,IACzB,mBAAmB,MAAM,qBAAqB;AAAA,EAChD,CAAC;AACD,SAAO,4BAA4B;AACrC;AAMO,IAAM,sBAAsB;AAEnC,IAAM,kBAA0C;AAAA,EAC9C,iBAAiB;AAAA,EACjB,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,qCAAqC;AACvC;AAUO,SAAS,sBAAsB,KAAyC;AAC7E,MAAI,IAAI,SAAS,SAAU,QAAO;AAClC,QAAM,UAAU,IAAI;AACpB,MAAI,CAAC,WAAW,YAAY,UAAW,QAAO;AAC9C,QAAM,cAAc,gBAAgB,OAAO;AAC3C,SAAO,cACH,qCAAqC,OAAO,OAAO,WAAW,MAC9D,qCAAqC,OAAO;AAClD;AAEO,SAAS,wBAAwB,SAAyB;AAC/D,SAAO;AAAA,EAAK,mBAAmB,IAAI,OAAO;AAAA;AAC5C;AAMO,IAAM,wBAAwB;AAQ9B,SAAS,wBAAwB,WAA2B;AACjE,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,GAAI,CAAC;AACxD,SACE;AAAA,EAAK,qBAAqB,iEACZ,OAAO;AAAA;AAGzB;;;AC/fO,IAAM,qBAAqB;AAE3B,IAAM,kCACX;AAEF,IAAM,iCACJ;AAEF,IAAM,8BAA8B;AACpC,IAAM,kCAAkC;AACxC,IAAM,kCACJ;AAEF,IAAM,sBAAsB;AAE5B,IAAM,gBAAgB;AAyCf,SAAS,yBAAyB,OAI7B;AACV,MAAI,MAAM,eAAgB,QAAO;AACjC,MAAI,MAAM,eAAe,KAAM,QAAO;AACtC,SAAO,MAAM;AACf;AAEA,IAAM,mBAAmB,oBAAI,IAAoB;AAEjD,SAAS,WAAWE,aAAoB,oBAAoC;AAC1E,SAAO,GAAGA,WAAU,GAAG,aAAa,GAAG,kBAAkB;AAC3D;AAEO,SAAS,2BAA2BA,aAA0B;AACnE,QAAM,SAAS,GAAGA,WAAU,GAAG,aAAa;AAC5C,aAAW,OAAO,iBAAiB,KAAK,GAAG;AACzC,QAAI,IAAI,WAAW,MAAM,EAAG,kBAAiB,OAAO,GAAG;AAAA,EACzD;AACF;AAEO,SAAS,yBAAyBA,aAA6B;AACpE,QAAM,SAAS,GAAGA,WAAU,GAAG,aAAa;AAC5C,SAAO,CAAC,GAAG,iBAAiB,KAAK,CAAC,EAAE,KAAK,CAAC,QAAQ,IAAI,WAAW,MAAM,CAAC;AAC1E;AAEO,SAAS,+BACdA,aACA,uBACA,MACA,qBAAqB,sBAAsB,qBAAqB,IACtC;AAC1B,mBAAiB,IAAI,WAAWA,aAAY,kBAAkB,GAAG,qBAAqB;AAEtF,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,OAAO;AAAA,MACL,WAAW;AAAA,QACT;AAAA,UACE,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,SAAS;AAAA,YACP,EAAE,OAAO,OAAO,aAAa,GAAG;AAAA,YAChC,EAAE,OAAO,MAAM,aAAa,GAAG;AAAA,UACjC;AAAA,UACA,UAAU;AAAA,UACV,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,OAAO;AAAA;AAAA,EAAO,IAAI;AAAA,IAAO;AAAA,EACjC;AACF;AAEA,SAAS,uBAAuB,OAIrB;AACT,SAAO,KAAK,UAAU;AAAA,IACpB,MAAM;AAAA,IACN,SAAS;AAAA,MACP,MAAM;AAAA,MACN,SAAS;AAAA,QACP,MAAM,WACF;AAAA,UACE,MAAM;AAAA,UACN,aAAa,MAAM;AAAA,UACnB,SAAS;AAAA,QACX,IACA;AAAA,UACE,MAAM;AAAA,UACN,aAAa,MAAM;AAAA,UACnB,SAAS,GAAG,8BAA8B;AAAA,EAAK,MAAM,YAAY,IAAI;AAAA,UACrE,UAAU;AAAA,QACZ;AAAA,MACN;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,SAAS,aAAa,MAAuB;AAC3C,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOO,SAAS,iBAAiB,MAAoB;AACnD,QAAM,SAAS,MAAM,UAAU,MAAM;AACrC,MAAI,OAAO,WAAW,SAAU,QAAO,aAAa,MAAM;AAC1D,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAElD,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK;AAAA,IACL,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AAAA,IACL,KAAK;AACH,aAAO,aAAa,OAAO,OAAO,SAAS,EAAE,CAAC;AAAA,IAChD,KAAK;AACH,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,QAAQ,OAAO,OAAO,UAAU,mBAAmB;AAAA,MACrD;AAAA,IACF,KAAK;AACH,aAAO,MAAM,QAAQ,OAAO,KAAK,IAC7B,OAAO,MACJ,IAAI,CAAC,SAAc;AAClB,YAAI,MAAM,SAAS,OAAQ,QAAO,KAAK;AACvC,eAAO,KAAK,UAAU,IAAI;AAAA,MAC5B,CAAC,EACA,KAAK,IAAI,IACZ,OAAO;AAAA,IACb;AACE,aAAO;AAAA,EACX;AACF;AAcA,SAAS,6BAA6B,OAAe,UAA2B;AAC9E,MACE,CAAC,MAAM,WAAW,+BAA+B,KACjD,CAAC,MAAM,SAAS,+BAA+B,GAC/C;AACA,WAAO;AAAA,EACT;AACA,QAAM,OAAO,MAAM;AAAA,IACjB,gCAAgC;AAAA,IAChC,MAAM,SAAS,gCAAgC;AAAA,EACjD;AACA,MAAI,CAAC,KAAK,WAAW,GAAG,KAAK,CAAC,KAAK,SAAS,GAAG,EAAG,QAAO;AAEzD,MAAI;AACJ,QAAM,OAAO,aAAa,SAAY,SAAY,IAAI,QAAQ;AAC9D,MAAI,QAAQ,KAAK,WAAW,IAAI,KAAK,KAAK,SAAS,KAAK,QAAQ;AAC9D,aAAS,KAAK,MAAM,KAAK,QAAQ,EAAE;AAAA,EACrC,OAAO;AACL,UAAM,QAAQ,KAAK,YAAY,KAAK;AACpC,QAAI,QAAQ,EAAG,UAAS,KAAK,MAAM,QAAQ,GAAG,EAAE;AAAA,EAClD;AACA,MAAI,WAAW,OAAW,QAAO;AACjC,SAAO,WAAW,sBAAsB,KAAK;AAC/C;AAOO,SAAS,qBAAqB,OAAgB,UAA6B;AAChF,MAAI,OAAO,UAAU,SAAU,QAAO,CAAC,6BAA6B,OAAO,QAAQ,CAAC;AACpF,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,QAAQ,CAAC,SAAS,qBAAqB,MAAM,QAAQ,CAAC;AAC7F,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO,CAAC;AAEjD,QAAM,MAAM;AACZ,MAAI,IAAI,WAAW,KAAM,QAAO,CAAC,OAAO,IAAI,UAAU,mBAAmB,CAAC;AAE1E,aAAW,OAAO,CAAC,WAAW,UAAU,YAAY,aAAa,OAAO,GAAG;AACzE,QAAI,OAAO,IAAK,QAAO,qBAAqB,IAAI,GAAG,GAAG,QAAQ;AAAA,EAChE;AAEA,SAAO,CAAC;AACV;AAEA,SAAS,uBAAuB,MAAoD;AAClF,QAAM,SAAS,iBAAiB,IAAI;AACpC,QAAM,UAAU,qBAAqB,QAAQ,2BAA2B,EACrE,IAAI,CAAC,WAAW,OAAO,KAAK,CAAC,EAC7B,OAAO,OAAO;AAEjB,MAAI,QAAQ,WAAW,KAAK,QAAQ,CAAC,EAAE,YAAY,MAAM,OAAO;AAC9D,WAAO,EAAE,UAAU,MAAM,UAAU,GAAG;AAAA,EACxC;AAEA,SAAO;AAAA,IACL,UAAU;AAAA,IACV,UAAU,QAAQ,SAAS,IAAI,QAAQ,KAAK,IAAI,IAAI;AAAA,EACtD;AACF;AAEO,SAAS,kCACdA,aACA,QACe;AACf,WAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,UAAM,MAAM,OAAO,CAAC;AACpB,QAAI,CAAC,MAAM,QAAQ,IAAI,OAAO,EAAG;AAEjC,eAAW,QAAQ,IAAI,SAAkB;AACvC,UAAI,MAAM,SAAS,iBAAiB,OAAO,KAAK,eAAe,UAAU;AACvE;AAAA,MACF;AAEA,YAAM,MAAM,WAAWA,aAAY,KAAK,UAAU;AAClD,YAAM,wBAAwB,iBAAiB,IAAI,GAAG;AACtD,UAAI,CAAC,sBAAuB;AAE5B,uBAAiB,OAAO,GAAG;AAC3B,YAAM,SAAS,uBAAuB,IAAI;AAC1C,aAAO,uBAAuB;AAAA,QAC5B,WAAW;AAAA,QACX,UAAU,OAAO;AAAA,QACjB,UAAU,OAAO;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;;;AHnOO,IAAM,kBAAkB;AAOxB,IAAM,oCAAoC;AAKjD,IAAMC,iBAAgB;AAGf,IAAM,cAAc;AAUpB,IAAM,yBAAmC;AAAA,EAC9C;AAAA,EACA;AACF;AAGO,IAAM,uBAAuB;AAgBpC,IAAM,iBAAiB;AAAA,EACrB,uBAAuB,EAAE,MAAM,uDAAuD,OAAO,KAAK;AAAA,EAClG,uBAAuB,EAAE,MAAM,6DAA6D,OAAO,MAAM;AAAA,EACzG,iBAAiB,EAAE,MAAM,cAAc,OAAO,MAAM;AAAA,EACpD,uBAAuB,EAAE,MAAM,qCAAqC,OAAO,MAAM;AAAA,EACjF,eAAe,EAAE,MAAM,yBAAyB,OAAO,MAAM;AAC/D;AAKO,SAAS,iBAAiB,KAAkE;AACjG,MAAI,IAAI,SAAS,eAAe,OAAO,IAAI,UAAU,SAAU,QAAO;AACtE,SAAO,OAAO,UAAU,eAAe,KAAK,gBAAgB,IAAI,KAAK,IAChE,IAAI,QACL;AACN;AAGO,SAAS,qBAAqB,MAAgC;AACnE,SAAO,eAAe,IAAI,EAAE;AAC9B;AAOO,SAAS,gBAAgB,WAA+B,OAAOC,SAAQ,GAAW;AACvF,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,QAAQ,cAAc,QAAQ,UAAU,WAAW,GAAG,IAAI,GAAG,IAAI,IAAI,UAAU,MAAM,KAAK,MAAM,CAAC,KAAK;AAC5G,SAAO,qBAAqB,KAAK;AACnC;AAEO,SAAS,uBAAuB,OAK5B;AACT,QAAM,QAAQ,eAAe,MAAM,IAAI;AACvC,QAAM,UAAU,qBAAqB,MAAM,WAAW,eAAe;AACrE,QAAM,MAAM,MAAM,QACd,uBAAuB,gBAAgB,MAAM,SAAS,CAAC,kCACvD;AACJ,QAAM,OAAO,MAAM,iBAAiB,oCAAoC;AACxE,SAAO;AAAA,EAAK,oBAAoB,wBAAwB,OAAO,KAAK,MAAM,IAAI,KAAK,GAAG,GAAG,IAAI;AAAA;AAC/F;AAEO,SAAS,oBAAoB,OAGxB;AACV,MAAI,MAAM,aAAa,oBAAoB,MAAM,SAAS,EAAG,QAAO;AACpE,QAAM,OAAO,MAAM;AACnB,MAAI,OAAO,SAAS,YAAY,KAAK,WAAW,EAAG,QAAO;AAC1D,SAAO,uBAAuB,KAAK,CAAC,YAAY,QAAQ,KAAK,IAAI,CAAC;AACpE;AAYA,IAAM,mBAAmB,oBAAI,IAA6B;AAOnD,SAAS,mBACd,QACA,QACA,OACA,MAAM,KAAK,IAAI,GACT;AACN,QAAM,OAAO,qBAAqB,UAAU,eAAe;AAC3D,QAAM,KAAK,qBAAqB,MAAM;AACtC,MAAI,CAAC,MAAM,OAAO,KAAM;AAMxB,MAAI,UAAU,UAAa,SAAS,KAAK;AACvC,QAAI,OAAO,4DAA4D;AAAA,MACrE,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR;AAAA,IACF,CAAC;AACD,YAAQ;AAAA,EACV;AACA,mBAAiB,IAAI,MAAM,EAAE,QAAQ,IAAI,MAAM,CAAC;AAChD,MAAI;AAAA,IACF,mBAAmB,IAAI,sEAAsE,EAAE,IAC7F,QAAQ,UAAU,IAAI,KAAK,KAAK,EAAE,YAAY,CAAC,KAAK,0BACtD;AAAA,IACA,EAAE,QAAQ,MAAM,QAAQ,IAAI,OAAO,SAAS,KAAK;AAAA,EACnD;AACF;AAQO,SAAS,uBACd,QACA,MAAM,KAAK,IAAI,GACK;AACpB,QAAM,OAAO,qBAAqB,UAAU,eAAe;AAC3D,QAAM,QAAQ,iBAAiB,IAAI,IAAI;AACvC,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,MAAM,UAAU,UAAa,MAAM,SAAS,KAAK;AACnD,qBAAiB,OAAO,IAAI;AAC5B,QAAI;AAAA,MACF,mBAAmB,IAAI,mDAAmD,MAAM,MAAM;AAAA,MACtF,EAAE,QAAQ,MAAM,QAAQ,MAAM,QAAQ,OAAO,MAAM,MAAM;AAAA,IAC3D;AACA,WAAO;AAAA,EACT;AACA,SAAO,MAAM;AACf;AAwBO,SAAS,mBAAmB,SAAyB;AAC1D,QAAM,KAAK,QAAQ,QAAQ,GAAG;AAC9B,SAAO,OAAO,KAAK,UAAU,QAAQ,MAAM,GAAG,EAAE;AAClD;AAcA,eAAsB,qBAAqB,OAMhB;AACzB,QAAM,YAA2B;AAAA,IAC/B,SAAS,MAAM;AAAA,IACf,SAAS,MAAM;AAAA,IACf,YAAY;AAAA,EACd;AACA,QAAM,SAAS,qBAAqB,MAAM,WAAW,eAAe;AACpE,QAAM,SAAS,uBAAuB,QAAQ,MAAM,GAAG;AACvD,MAAI,CAAC,OAAQ,QAAO;AAEpB,MAAI;AACF,UAAM,UACJ,WAAW,kBACP,MAAM,eACL,MAAM,qBAAqB,QAAQ,MAAM,WAAW,GAAG;AAC9D,WAAO;AAAA,MACL;AAAA,MACA,SAAS,mBAAmB,MAAM,OAAO;AAAA,MACzC;AAAA,MACA,YAAY;AAAA,IACd;AAAA,EACF,SAAS,KAAK;AAIZ,QAAI,MAAM,+DAA+D;AAAA,MACvE;AAAA,MACA;AAAA,MACA,OAAO,OAAO,GAAG;AAAA,IACnB,CAAC;AACD,WAAO;AAAA,EACT;AACF;AAeO,SAAS,gCAAgC,OAOpC;AACV,MAAI,MAAM,eAAgB,QAAO;AACjC,MAAI,MAAM,YAAa,QAAO;AAC9B,MAAI,MAAM,aAAc,QAAO;AAC/B,MAAI,MAAM,eAAe,MAAO,QAAO;AACvC,MAAI,MAAM,WAAW,WAAW,EAAG,QAAO;AAC1C,SAAO,MAAM;AACf;AAGO,SAAS,mBACd,UACA,QACU;AACV,QAAM,OAAO,qBAAqB,UAAU,eAAe;AAC3D,QAAM,MAAgB,CAAC;AACvB,aAAW,OAAO,YAAY,CAAC,GAAG;AAChC,UAAM,OAAO,qBAAqB,OAAO,GAAG,CAAC;AAC7C,QAAI,CAAC,QAAQ,SAAS,QAAQ,IAAI,SAAS,IAAI,EAAG;AAClD,QAAI,KAAK,IAAI;AAAA,EACf;AACA,SAAO;AACT;AAUA,IAAMC,oBAAmB,oBAAI,IAAqC;AAElE,SAASC,YAAWC,aAAoB,YAA4B;AAClE,SAAO,GAAGA,WAAU,GAAGC,cAAa,GAAG,UAAU;AACnD;AAOO,SAAS,8BAA8BD,aAA0B;AACtE,QAAM,SAAS,GAAGA,WAAU,GAAGC,cAAa;AAC5C,aAAW,OAAOH,kBAAiB,KAAK,GAAG;AACzC,QAAI,IAAI,WAAW,MAAM,EAAG,CAAAA,kBAAiB,OAAO,GAAG;AAAA,EACzD;AACF;AAEA,SAAS,cAAc,UAAkD;AACvE,SAAO,eAAe,QAAQ;AAChC;AAEO,SAAS,kCACdE,aACA,OAWA,aAAa,GAAG,iCAAiC,GAAG,KAAK,OAAO,EAC7D,SAAS,EAAE,EACX,MAAM,GAAG,EAAE,CAAC,IACG;AAClB,QAAM,SAAS,qBAAqB,MAAM,iBAAiB,eAAe;AAC1E,QAAM,aAAa,MAAM,WAAW,IAAI,CAAC,MAAM,qBAAqB,CAAC,CAAC;AACtE,QAAM,SAAS,cAAc,MAAM,QAAQ;AAC3C,QAAM,QAAQ,UAAU;AAExB,QAAM,WAAW,MAAM,SACnB,uBAAuB,MAAM,KAAK,MAAM,MAAM,mGAC9C;AAAA,IACE,uBAAuB,MAAM;AAAA,IAC7B,MAAM,SAAS,OAAO,MAAM,MAAM,KAAK;AAAA,IACvC,SAAS,qBAAqB,MAAM,KAAK;AAAA,IACzC;AAAA,EACF,EAAE,KAAK,EAAE;AAEb,EAAAF,kBAAiB,IAAIC,YAAWC,aAAY,UAAU,GAAG;AAAA,IACvD,eAAe;AAAA,IACf,YAAY,CAAC,GAAG,UAAU;AAAA,IAC1B,UAAU,MAAM;AAAA,IAChB;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA,UAAU;AAAA,IACV,OAAO;AAAA,MACL,WAAW;AAAA,QACT;AAAA,UACE,QAAQ;AAAA,UACR;AAAA,UACA,SAAS;AAAA,YACP,GAAG,WAAW,IAAI,CAAC,eAAe;AAAA,cAChC,OAAO;AAAA,cACP,aAAa,WAAW,SAAS,WAAW,KAAK,8IAA8I,MAAM;AAAA,YACvM,EAAE;AAAA,YACF;AAAA,cACE,OAAO;AAAA,cACP,aAAa;AAAA,YACf;AAAA,UACF;AAAA,UACA,UAAU;AAAA,UACV,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM;AAAA,EACR;AACF;AAMA,SAAS,SAAS,QAAiD;AACjE,SACE,CAAC,CAAC,UACF,OAAO,WAAW,YACjB,OAAgC,WAAW;AAEhD;AAEA,SAAS,SACP,SACA,MACuB;AACvB,QAAM,SAAS,iBAAiB,IAAI;AACpC,MAAI,SAAS,MAAM,GAAG;AACpB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ,OAAQ,OAAgC,UAAU,mBAAmB;AAAA,IAC/E;AAAA,EACF;AAGA,QAAM,aAAa,MAAM,QAAQ;AACjC,MAAI,eAAe,gBAAgB,eAAe,cAAc;AAC9D,WAAO,EAAE,MAAM,QAAQ,QAAQ,OAAO,UAAU,wBAAwB,EAAE;AAAA,EAC5E;AAEA,QAAM,UAAU,qBAAqB,QAAQ,QAAQ,QAAQ,EAC1D,IAAI,CAAC,WAAW,OAAO,KAAK,CAAC,EAC7B,OAAO,OAAO;AACjB,MAAI,QAAQ,WAAW,EAAG,QAAO,EAAE,MAAM,QAAQ,QAAQ,YAAY;AAErE,QAAM,SAAS,qBAAqB,QAAQ,CAAC,CAAC;AAC9C,MAAI,WAAW,aAAa;AAC1B,WAAO,EAAE,MAAM,QAAQ,QAAQ,6BAA6B;AAAA,EAC9D;AACA,QAAM,SAAS,QAAQ,WAAW,KAAK,CAAC,cAAc,cAAc,MAAM;AAC1E,MAAI,CAAC,QAAQ;AACX,WAAO,EAAE,MAAM,QAAQ,QAAQ,wBAAwB,QAAQ,CAAC,CAAC,IAAI;AAAA,EACvE;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,eAAe,QAAQ;AAAA,IACvB,UAAU,QAAQ;AAAA,EACpB;AACF;AAQO,SAAS,6BACdA,aACA,QAQA,UAC8B;AAC9B,WAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,UAAM,MAAM,OAAO,CAAC;AACpB,QAAI,CAAC,MAAM,QAAQ,IAAI,OAAO,EAAG;AAEjC,eAAW,QAAQ,IAAI,SAAkB;AACvC,UAAI,MAAM,SAAS,iBAAiB,OAAO,KAAK,eAAe,UAAU;AACvE;AAAA,MACF;AACA,YAAM,MAAMD,YAAWC,aAAY,KAAK,UAAU;AAClD,YAAM,UAAUF,kBAAiB,IAAI,GAAG;AACxC,UAAI,CAAC,QAAS;AAEd,MAAAA,kBAAiB,OAAO,GAAG;AAC3B,aAAO,SAAS,SAAS,IAAI;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,OAAO,OAAO,OAAO,SAAS,CAAC;AACrC,MAAI,YAAY,SAAS,WAAW,SAAS,KAAK,MAAM,QAAQ,MAAM,OAAO,GAAG;AAC9E,UAAM,SAAU,KAAK,QAAkB;AAAA,MACrC,CAAC,SACC,MAAM,SAAS,iBACf,OAAO,KAAK,eAAe,YAC3B,KAAK,WAAW,WAAW,iCAAiC;AAAA,IAChE;AACA,QAAI,QAAQ;AACV,aAAO;AAAA,QACL;AAAA,UACE,eAAe,qBAAqB,SAAS,iBAAiB,eAAe;AAAA,UAC7E,YAAY,SAAS,WAAW,IAAI,CAAC,MAAM,qBAAqB,CAAC,CAAC;AAAA,QACpE;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,mBAAmB,OAIxB;AACT,QAAM,SAAS,cAAc,MAAM,QAAQ;AAC3C,SAAO;AAAA,EAAK,eAAe,KAAK,MAAM,aAAa,yDACjD,MAAM,MACR,KACE,SAAS,SAAS,MAAM,KAAK,yBAC/B;AAAA;AACF;AAEO,SAAS,uBAAuB,QAAwB;AAC7D,SAAO;AAAA,EAAK,eAAe,6BAA6B,MAAM;AAAA;AAChE;AAEA,SAAS,eAAe,MAAoB;AAC1C,MAAI,CAAC,QAAQ,OAAO,KAAK,eAAe,SAAU,QAAO;AACzD,MAAI,KAAK,SAAS,eAAe,KAAK,SAAS,cAAe,QAAO;AACrE,SAAO,KAAK,WAAW,WAAW,iCAAiC;AACrE;AASO,SAAS,0BAA0B,QAAwB;AAChE,MAAI,UAAU;AACd,QAAM,MAAM,CAAC;AAEb,aAAW,WAAW,QAAQ;AAC5B,UAAM,UAAW,QAAkC;AACnD,QAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,CAAC,QAAQ,KAAK,cAAc,GAAG;AAC5D,UAAI,KAAK,OAAO;AAChB;AAAA,IACF;AACA,cAAU;AACV,UAAM,OAAO,QAAQ,OAAO,CAAC,SAAc,CAAC,eAAe,IAAI,CAAC;AAChE,QAAI,KAAK,WAAW,EAAG;AACvB,QAAI,KAAK,EAAE,GAAG,SAAS,SAAS,KAAK,CAAmB;AAAA,EAC1D;AAEA,SAAO,UAAU,MAAM;AACzB;AAEO,SAAS,yBAAyB,QAAwB;AAC/D,SAAO;AAAA,IACL,yGAAyG,MAAM;AAAA,IAC/G;AAAA,IACA;AAAA,EACF,EAAE,KAAK,GAAG;AACZ;AAOO,SAAS,gCACd,QACA,QACQ;AACR,QAAM,WAAW,0BAA0B,MAAM;AACjD,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,MACE,MAAM;AAAA,MACN,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,yBAAyB,MAAM,EAAE,CAAC;AAAA,IACpE;AAAA,EACF;AACF;AAGO,SAAS,cACd,UACoB;AACpB,SAAO,aAAa,QAAQ;AAC9B;;;AItoBA,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAG1B,IAAM,gBAAgB,UAAU,QAAQ;AASxC,IAAM,QAAQ,oBAAI,IAAwC;AAuBnD,IAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AACF;AAWO,SAAS,cACd,YAAgD,QAAQ,KAChC;AACxB,QAAM,SAAiC,CAAC;AACxC,aAAW,QAAQ,sBAAsB;AACvC,QAAI,UAAU,IAAI,MAAM,OAAW,QAAO,IAAI,IAAI;AAAA,EACpD;AACA,SAAO;AACT;AAOO,SAAS,iBAAiB,SAA6C;AAC5E,QAAM,SAAS,MAAM,IAAI,OAAO;AAChC,MAAI,OAAQ,QAAO;AACnB,QAAM,WAAW,YAAwC;AACvD,QAAI;AACF,YAAM,EAAE,OAAO,IAAI,MAAM,cAAc,SAAS,CAAC,WAAW,GAAG;AAAA,QAC7D,SAAS;AAAA,MACX,CAAC;AACD,YAAM,QAAQ,sBAAsB,KAAK,OAAO,KAAK,CAAC;AACtD,UAAI,CAAC,OAAO;AACV,YAAI,KAAK,uCAAuC,EAAE,QAAQ,OAAO,KAAK,EAAE,CAAC;AACzE,eAAO;AAAA,MACT;AACA,YAAM,IAAgB;AAAA,QACpB,OAAO,OAAO,MAAM,CAAC,CAAC;AAAA,QACtB,OAAO,OAAO,MAAM,CAAC,CAAC;AAAA,QACtB,OAAO,OAAO,MAAM,CAAC,CAAC;AAAA,QACtB,KAAK,OAAO,KAAK;AAAA,MACnB;AACA,UAAI,KAAK,+BAA+B,EAAE,SAAS,SAAS,EAAE,IAAI,CAAC;AACnE,UAAI,CAAC,2BAA2B,CAAC,GAAG;AAClC,YAAI;AAAA,UACF;AAAA,UACA,EAAE,SAAS,EAAE,IAAI;AAAA,QACnB;AAAA,MACF;AACA,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,UAAI,KAAK,uCAAuC;AAAA,QAC9C;AAAA,QACA,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AACD,aAAO;AAAA,IACT;AAAA,EACF,GAAG;AACH,QAAM,IAAI,SAAS,OAAO;AAC1B,SAAO;AACT;AAEA,SAAS,IAAI,GAAe,QAAkE;AAC5F,MAAI,EAAE,UAAU,OAAO,MAAO,QAAO,EAAE,QAAQ,OAAO;AACtD,MAAI,EAAE,UAAU,OAAO,MAAO,QAAO,EAAE,QAAQ,OAAO;AACtD,SAAO,EAAE,SAAS,OAAO;AAC3B;AAQO,SAAS,2BAA2B,GAA+B;AACxE,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,IAAI,GAAG,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,IAAI,CAAC;AAClD;AAaO,SAAS,oBAAoB,GAA+B;AACjE,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,IAAI,GAAG,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,IAAI,CAAC;AAClD;AAGO,SAAS,wBAAwB,GAA+B;AACrE,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,IAAI,GAAG,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,IAAI,CAAC;AAClD;AAQO,SAAS,oBAAoB,GAA+B;AACjE,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,IAAI,GAAG,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,EAAE,CAAC;AAChD;AAGA,IAAM,cAAc,oBAAI,IAA8B;AAS/C,SAAS,sBAAsB,SAAiB,MAAgC;AACrF,QAAM,MAAM,GAAG,OAAO,KAAO,IAAI;AACjC,QAAM,SAAS,YAAY,IAAI,GAAG;AAClC,MAAI,OAAQ,QAAO;AACnB,QAAM,WAAW,YAA8B;AAC7C,QAAI;AACF,YAAM,YAAY,cAAc,SAAS,CAAC,QAAQ,GAAG;AAAA,QACnD,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,WAAW,IAAI,OAAO;AAAA,MACxB,CAAC;AAED,gBAAU,MAAM,OAAO,IAAI;AAC3B,YAAM,EAAE,OAAO,IAAI,MAAM;AACzB,aAAO,OAAO,SAAS,IAAI;AAAA,IAC7B,SAAS,KAAK;AACZ,UAAI,KAAK,2CAA2C;AAAA,QAClD;AAAA,QACA;AAAA,QACA,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AACD,aAAO;AAAA,IACT;AAAA,EACF,GAAG;AACH,cAAY,IAAI,KAAK,OAAO;AAC5B,SAAO;AACT;;;AC9LA,SAAS,aAAgC;AACzC,SAAS,uBAAuB;AAChC,SAAS,cAAAI,mBAAkB;AAC3B,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,cAAc;;;ACJvB,SAAS,oBAA+D;AAExE,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,YAAY,YAAY;AACxB,SAAS,oBAAoB;;;ACL7B,YAAY,QAAQ;AACpB,YAAY,QAAQ;AACpB,YAAYC,WAAU;AAYtB,IAAM,iBAAsB;AAAA,EACvB,UAAO;AAAA,EACV,wBAAwB,QAAQ,GAAG;AACrC;AAEA,IAAI,aAAa;AAEV,SAAS,eAAuB;AACrC,MAAI,CAAI,cAAW,cAAc,GAAG;AAClC,IAAG,aAAU,gBAAgB,EAAE,WAAW,KAAK,CAAC;AAAA,EAClD;AACA,MAAI,CAAC,YAAY;AACf,iBAAa;AACb,YAAQ,GAAG,QAAQ,MAAM;AACvB,UAAI;AACF,QAAG,UAAO,gBAAgB,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,MAC5D,QAAQ;AAAA,MAAC;AAAA,IACX,CAAC;AAAA,EACH;AACA,SAAO;AACT;;;ADgDO,IAAM,qBAAqB;AAG3B,SAAS,mBAAmB,cAAgC;AACjE,SACE,OAAO,iBAAiB,YACxB,aAAa,YAAY,EAAE,SAAS,mBAAmB;AAE3D;AAgBO,IAAM,wBAAwB;AAO9B,SAAS,uBAAuB,SAA0B;AAC/D,SACG,QAAQ,SAAS,iBAAiB,KACjC,QAAQ,SAAS,iCAAiC,KACpD,QAAQ,SAAS,uBAAuB,KACxC,QAAQ,SAAS,iCAAiC,KAClD,QAAQ,SAAS,oBAAoB,KACrC,QAAQ,SAAS,qBAAqB;AAE1C;AAEA,IAAM,mBAAmB;AACzB,IAAM,cAAc;AACb,IAAM,oBAAoB,QAAQ,WAAW;AAK7C,IAAM,2BAA2B,KAAK,KAAK;AAK3C,IAAM,uBAAuB;AAe7B,IAAM,oCAA4D;AAAA,EACvE,MAAM;AAAA,EACN,YAAY;AAAA;AAAA,EACZ,UAAU,KAAK,KAAK;AAAA;AACtB;AAMO,IAAM,uBAAuB,KAAK,KAAK;AAmBvC,SAAS,0BACd,UACA,OACA,WACQ;AACR,QAAM,MAAM,SAAS,YAAY;AACjC,MAAI,KAAK,kCAAkC,GAAG,KAAK;AACnD,MAAI,WAAW;AACb,UAAM,KAAK,sBAAsB,WAAW,GAAG;AAC/C,QAAI,mBAAmB,EAAE,EAAG,MAAK;AAAA,EACnC;AACA,MAAI,QAAQ,QAAQ;AAClB,UAAM,YAAY,OAAO;AACzB,QAAI,OAAO,cAAc,YAAY,YAAY,GAAI,MAAK;AAAA,EAC5D;AACA,SAAO,KAAK,IAAI,IAAI,oBAAoB;AAC1C;AAGA,SAAS,mBAAmB,OAAiC;AAC3D,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KAAK,SAAS;AACzE;AAEA,SAAS,sBACP,KACA,KACoB;AACpB,MAAI,OAAO,UAAU,eAAe,KAAK,KAAK,GAAG,EAAG,QAAO,IAAI,GAAG;AAClE,aAAW,KAAK,OAAO,KAAK,GAAG,GAAG;AAChC,QAAI,EAAE,YAAY,MAAM,IAAK,QAAO,IAAI,CAAC;AAAA,EAC3C;AACA,SAAO;AACT;AAgBO,SAAS,4BACd,WACQ;AACR,MAAI,KAAK;AACT,QAAM,WAAW,CAAC,eAAgC;AAChD,QAAI,eAAe,qBAAsB,QAAO;AAChD,QAAI,aAAa,GAAI,MAAK;AAC1B,WAAO;AAAA,EACT;AACA,aAAW,CAAC,UAAU,SAAS,KAAK,OAAO,QAAQ,iCAAiC,GAAG;AACrF,UAAM,WAAW,YAAY,sBAAsB,WAAW,QAAQ,IAAI;AAC1E,QAAI,SAAS,mBAAmB,QAAQ,IAAI,WAAW,SAAS,GAAG;AACjE,aAAO;AAAA,IACT;AAAA,EACF;AACA,aAAW,SAAS,OAAO,OAAO,aAAa,CAAC,CAAC,GAAG;AAClD,QAAI,mBAAmB,KAAK,KAAK,SAAS,KAAK,EAAG,QAAO;AAAA,EAC3D;AACA,SAAO,KAAK,IAAI,IAAI,oBAAoB;AAC1C;AAqBA,IAAI,qBAAgD;AAE7C,SAAS,sBAAsB,OAAwC;AAC5E,uBAAqB;AACvB;AAGO,IAAM,4BAA4B;AAEzC,IAAI,oBAAoB;AAkBjB,SAAS,iBAAiB,MASf;AAChB,MAAI,YAAY;AAChB,MAAI,WAAW;AACf,MAAI,QAA8C;AAElD,QAAM,QAAQ,YAA2B;AACvC,YAAQ;AACR,QAAI,UAAW;AACf,QAAI,OAAO;AACX,UAAM,QAAQ;AACd,QAAI,OAAO;AACT,UAAI;AACF,eAAO,MAAM,MAAM,EAAE,QAAQ,KAAK,QAAQ,UAAU,KAAK,SAAS,CAAC;AAAA,MACrE,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AACA,QAAI,UAAW;AACf,QAAI,MAAM;AACR,UAAI,CAAC,YAAY,KAAK,cAAc;AAClC,YAAI,KAAK,2EAA2E;AAAA,UAClF,QAAQ,KAAK;AAAA,UACb,UAAU,KAAK;AAAA,UACf,YAAY,KAAK;AAAA,UACjB,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AACA,iBAAW;AACX,cAAQ,WAAW,MAAM,KAAK,MAAM,GAAG,KAAK,aAAa,iBAAiB;AAC1E;AAAA,IACF;AACA,SAAK,SAAS;AAAA,EAChB;AAEA,UAAQ,WAAW,MAAM,KAAK,MAAM,GAAG,KAAK,UAAU;AACtD,SAAO;AAAA,IACL,SAAS;AACP,kBAAY;AACZ,UAAI,MAAO,cAAa,KAAK;AAC7B,cAAQ;AAAA,IACV;AAAA,EACF;AACF;AAYO,SAAS,uBAAuB,UAAkB,IAAmB;AAC1E,QAAM,MAAM,SAAS,YAAY;AACjC,QAAM,OAAO,eAAe,QAAQ,qBAAqB,EAAE;AAC3D,MAAI,QAAQ,UAAU,QAAQ,sBAAsB;AAClD,WAAO,IAAI;AAAA,MACT,QACG,QAAQ,SAAS,qBAAqB,uBACvC;AAAA,IAKJ;AAAA,EACF;AACA,SAAO,IAAI,MAAM,IAAI;AACvB;AAWO,IAAM,kBACX;AA2BK,IAAM,uBAAuB;AAE7B,IAAM,wBACX;AAQK,IAAM,sBAAsB,CAAC,eAAe,UAAU,eAAe;AAGrE,SAAS,oBAAoB,OAA2D;AAC7F,QAAM,QAAQ,OAAO;AACrB,MAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,GAAG;AAC7C,WAAO;AAAA,EACT;AACA,aAAW,CAAC,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG;AAC3C,QAAI,SAAS,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,GAAG;AACpE,aAAO,oBAAoB,KAAK;AAAA,IAClC;AACA,UAAM,OAAO;AACb,eAAW,SAAS,qBAAqB;AACvC,UAAI,OAAO,KAAK,KAAK,MAAM,UAAU;AACnC,eAAO,oBAAoB,KAAK,KAAK,KAAK;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,eAAe,OAAuE;AACpG,MAAI,oBAAoB,KAAK,EAAG,QAAO,CAAC;AACxC,SAAO,MAAO;AAChB;AAOO,SAAS,yBAAyB,kBAA0B,OAAuB;AACxF,SAAO,GAAG,gBAAgB,SAAS,KAAK;AAC1C;AAOO,SAAS,uBACd,UACiB;AACjB,QAAM,QAAQ,SAAS;AACvB,QAAM,WAAW,SAAS,IAAI,CAAC,EAAE,MAAM,OAAO,GAAG,UAAU;AACzD,UAAM,QAAQ,OAAO,KAAK,gBAAgB,WAAW,KAAK,cAAc,QAAQ,QAAQ,CAAC;AACzF,UAAM,QAAQ,OAAO,KAAK,kBAAkB,WAAW,KAAK,KAAK,aAAa,MAAM;AACpF,UAAM,SAAS,WAAW,QAAQ,CAAC,OAAO,KAAK,KAAK,KAAK,GAAG,KAAK;AACjE,QAAI,CAAC,OAAQ,QAAO,GAAG,MAAM;AAAA;AAC7B,QAAI,OAAO,SAAS,QAAS,QAAO,GAAG,MAAM;AAAA,UAAa,OAAO,OAAO;AACxE,WAAO,GAAG,MAAM;AAAA,EAAK,OAAO,UAAU,aAAa,EAAE,GAAG,OAAO,IAAI;AAAA,EACrE,CAAC;AACD,QAAM,SAAS,SAAS,KAAK,CAAC,EAAE,OAAO,MAAM,CAAC,UAAU,OAAO,SAAS,WAAW,OAAO,OAAO;AACjG,SAAO,EAAE,MAAM,QAAQ,MAAM,SAAS,KAAK,MAAM,GAAG,GAAI,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC,EAAG;AAC3F;AAEA,IAAM,sBAAsB;AAG5B,IAAM,oBAAoB;AAWnB,IAAM,sBACX;AAkBK,IAAM,sBACX;AAwBK,SAAS,qBACd,iBACoB;AACpB,QAAM,OAAO,iBAAiB,KAAK;AACnC,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAQ,KAAK,QAAQ,mBAAmB;AAC9C,MAAI,UAAU,GAAI,QAAO;AACzB,QAAM,UAAoB,CAAC;AAC3B,aAAW,OAAO,KAAK,MAAM,KAAK,EAAE,MAAM,IAAI,GAAG;AAC/C,UAAM,QAAQ,wBAAwB,KAAK,IAAI,KAAK,CAAC;AACrD,QAAI,CAAC,MAAO;AACZ,UAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAC3B,UAAM,QAAQ,MAAM,CAAC,EAAE,KAAK;AAC5B,YAAQ;AAAA,MACN,KAAK,IAAI,KACP,MAAM,SAAS,oBACX,GAAG,MAAM,MAAM,GAAG,iBAAiB,EAAE,QAAQ,CAAC,WAC9C,KACN;AAAA,IACF;AAAA,EACF;AACA,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,SAAO;AAAA,EAAqF,QAAQ,KAAK,IAAI,CAAC;AAChH;AASO,SAAS,4BACd,OACA,iBACgB;AAChB,QAAM,aAAa,qBAAqB,eAAe;AACvD,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO,MAAM;AAAA,IAAI,CAAC,MAChB,EAAE,SAAS,UAAU,EAAE,SAAS,uBAC5B,EAAE,GAAG,GAAG,aAAa,GAAG,UAAU;AAAA;AAAA,EAAO,EAAE,WAAW,GAAG,IACzD;AAAA,EACN;AACF;AAQO,SAAS,gCACd,OACA,iBACgB;AAChB,QAAM,OAAO,iBAAiB,KAAK;AACnC,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,MAAM;AAAA,IAAI,CAAC,MAChB,EAAE,SAAS,aACP,EAAE,GAAG,GAAG,aAAa,GAAG,IAAI;AAAA;AAAA,EAAO,mBAAmB,GAAG,IACzD;AAAA,EACN;AACF;AASO,SAAS,qCACd,OACA,qBACgB;AAChB,MAAI,oBAAqB,QAAO;AAChC,SAAO,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU;AAClD;AAGO,IAAM,wBAAwB;AAAA,EACnC,aAAa;AAAA,IACX,MAAM;AAAA,IACN,aAAa;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,EACf;AAAA,EACA,eAAe;AAAA,IACb,MAAM;AAAA,IACN,aAAa;AAAA,EACf;AAAA,EACA,SAAS;AAAA,IACP,MAAM;AAAA,IACN,aACE;AAAA,EAGJ;AAAA,EACA,SAAS;AAAA,IACP,MAAM;AAAA,IACN,aAAa;AAAA,EACf;AAAA,EACA,YAAY;AAAA,IACV,MAAM;AAAA,IACN,aACE;AAAA,EACJ;AACF;AAEO,IAAM,sBAAsC;AAAA,EACjD;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IAEF,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,SAAS;AAAA,UACP,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,aAAa;AAAA,UACX,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,SAAS;AAAA,UACP,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,MACF;AAAA,MACA,UAAU,CAAC,SAAS;AAAA,IACtB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,UAAU;AAAA,UACR,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,SAAS;AAAA,UACP,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,MACF;AAAA,MACA,UAAU,CAAC,YAAY,SAAS;AAAA,IAClC;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IACF,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,UAAU;AAAA,UACR,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,WAAW;AAAA,UACT,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,WAAW;AAAA,UACT,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,YAAY;AAAA,UACV,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,MACF;AAAA,MACA,UAAU,CAAC,YAAY,aAAa,WAAW;AAAA,IACjD;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE;AAAA,IAGF,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,KAAK;AAAA,UACH,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,MAAM,CAAC,QAAQ,YAAY,MAAM;AAAA,UACjC,aACE;AAAA,QACJ;AAAA,QACA,SAAS;AAAA,UACP,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,MACF;AAAA,MACA,UAAU,CAAC,KAAK;AAAA,IAClB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE,wVAMA;AAAA,IACF,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE,2KAGA;AAAA,IACF,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,OAAO;AAAA,UACL,MAAM;AAAA,UACN,UAAU;AAAA,UACV,aAAa;AAAA,UACb,OAAO;AAAA,YACL,MAAM;AAAA,YACN,YAAY;AAAA,YACZ,UAAU;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AAAA,MACA,UAAU,CAAC,OAAO;AAAA,IACpB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE,qVAMA;AAAA,IACF,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,WAAW;AAAA,UACT,MAAM;AAAA,UACN,aAAa;AAAA,UACb,OAAO;AAAA,YACL,MAAM;AAAA,YACN,YAAY;AAAA,cACV,UAAU;AAAA,gBACR,MAAM;AAAA,gBACN,aAAa;AAAA,cACf;AAAA,cACA,QAAQ;AAAA,gBACN,MAAM;AAAA,gBACN,aAAa;AAAA,cACf;AAAA,cACA,SAAS;AAAA,gBACP,MAAM;AAAA,gBACN,aAAa;AAAA,gBACb,OAAO;AAAA,kBACL,MAAM;AAAA,kBACN,YAAY;AAAA,oBACV,OAAO;AAAA,sBACL,MAAM;AAAA,sBACN,aAAa;AAAA,oBACf;AAAA,oBACA,aAAa;AAAA,sBACX,MAAM;AAAA,sBACN,aAAa;AAAA,oBACf;AAAA,kBACF;AAAA,kBACA,UAAU,CAAC,SAAS,aAAa;AAAA,gBACnC;AAAA,cACF;AAAA,cACA,UAAU;AAAA,gBACR,MAAM;AAAA,gBACN,aACE;AAAA,cACJ;AAAA,YACF;AAAA,YACA,UAAU,CAAC,YAAY,UAAU,SAAS;AAAA,UAC5C;AAAA,QACF;AAAA,MACF;AAAA,MACA,UAAU,CAAC,WAAW;AAAA,IACxB;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aACE,4KAGA;AAAA,IACF,aAAa;AAAA,MACX,MAAM;AAAA,MACN,YAAY;AAAA,QACV,SAAS;AAAA,UACP,MAAM;AAAA,UACN,aACE;AAAA,QAIJ;AAAA,MACF;AAAA,MACA,UAAU,CAAC,SAAS;AAAA,IACtB;AAAA,EACF;AACF;AAEA,eAAsB,qBACpB,QAAwB,qBACxB,kBACA,cACA,UAGI,CAAC,GACoB;AACzB,QAAM,QAAQ,IAAI,aAAa;AAC/B,QAAM,UAAU,oBAAI,IAA2B;AAC/C,QAAM,cAAc,QAAQ,eAAe;AAQ3C,QAAM,YAAmB,mBAAY,EAAE,EAAE,SAAS,KAAK;AACvD,QAAM,eAAe,OAAO,KAAK,UAAU,SAAS,EAAE;AAGtD,MAAI,iBAAiB;AAErB,WAAS,OAAO,KAA+B;AAC7C,UAAM,MAAM,IAAI,QAAQ;AACxB,QAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,UAAM,YAAY,OAAO,KAAK,GAAG;AAGjC,QAAI,UAAU,WAAW,aAAa,OAAQ,QAAO;AACrD,WAAc,uBAAgB,WAAW,YAAY;AAAA,EACvD;AAgBA,WAAS,OACP,KACA,KACA,YACA,QACM;AAON,QAAI,OAAO,gCAAgC;AAAA,MACzC;AAAA,MACA;AAAA,MACA,QAAQ,IAAI;AAAA,MACZ,kBAAkB,OAAO,IAAI,QAAQ,kBAAkB;AAAA,IACzD,CAAC;AACD,QAAI,aAAa;AACjB,QAAI,UAAU,cAAc,OAAO;AACnC,QAAI,GAAG,UAAU,MAAM;AACrB,UAAI,QAAQ,QAAQ;AAAA,IACtB,CAAC;AACD,QAAI,IAAI;AAAA,EACV;AAEA,QAAMC,UAAS,aAAa,OAAO,KAAK,QAAQ;AAC9C,QAAI,IAAI,WAAW,UAAU,CAAC,IAAI,KAAK,WAAW,MAAM,GAAG;AACzD,aAAO,KAAK,KAAK,KAAK,oBAAoB;AAC1C;AAAA,IACF;AAUA,QAAI,IAAI,QAAQ,SAAS,gBAAgB;AACvC,aAAO,KAAK,KAAK,KAAK,wCAAwC;AAC9D;AAAA,IACF;AAKA,QAAI,IAAI,QAAQ,WAAW,QAAW;AACpC,aAAO,KAAK,KAAK,KAAK,uBAAuB;AAC7C;AAAA,IACF;AAIA,UAAM,cAAc,OAAO,IAAI,QAAQ,cAAc,KAAK,EAAE,EACzD,MAAM,GAAG,EAAE,CAAC,EACZ,KAAK,EACL,YAAY;AACf,QAAI,gBAAgB,oBAAoB;AACtC,aAAO,KAAK,KAAK,KAAK,sCAAsC;AAC5D;AAAA,IACF;AACA,QAAI,CAAC,OAAO,GAAG,GAAG;AAChB,aAAO,KAAK,KAAK,KAAK,iCAAiC;AACvD;AAAA,IACF;AASA,QAAI,YAAoC;AACxC,QAAI,gBAA+B;AAInC,QAAI,QAA4B;AAChC,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,GAAG;AAC/B,YAAM,UAAU,KAAK,MAAM,IAAI;AAM/B,kBAAY,SAAS,MAAM;AAC3B,sBAAgB,OAAO,SAAS,WAAW,WAAW,QAAQ,SAAS;AAEvE,UAAI,SAAS,YAAY,SAAS,OAAO,QAAQ,WAAW,UAAU;AACpE,kBAAU,KAAK;AAAA,UACb,SAAS;AAAA,UACT,IAAI;AAAA,UACJ,OAAO,EAAE,MAAM,QAAQ,SAAS,kBAAkB;AAAA,QACpD,CAAC;AACD;AAAA,MACF;AAEA,UAAI,MAAM,qBAAqB;AAAA,QAC7B,QAAQ,QAAQ;AAAA,QAChB,IAAI,QAAQ;AAAA,MACd,CAAC;AAED,UAAI,QAAQ,WAAW,cAAc;AACnC,kBAAU,KAAK;AAAA,UACb,SAAS;AAAA,UACT,IAAI;AAAA,UACJ,QAAQ;AAAA,YACN,iBAAiB;AAAA,YACjB,cAAc,EAAE,OAAO,CAAC,EAAE;AAAA,YAC1B,YAAY;AAAA,cACV,MAAM;AAAA,cACN,SAAS;AAAA,YACX;AAAA,UACF;AAAA,QACF,CAAC;AACD;AAAA,MACF;AAEA,UAAI,QAAQ,WAAW,6BAA6B;AAClD,YAAI,aAAa;AACjB,YAAI,IAAI;AACR;AAAA,MACF;AAEA,UAAI,QAAQ,WAAW,cAAc;AACnC,kBAAU,KAAK;AAAA,UACb,SAAS;AAAA,UACT,IAAI;AAAA,UACJ,QAAQ;AAAA,YACN,OAAO,MAAM,IAAI,CAAC,OAAO;AAAA,cACvB,MAAM,EAAE;AAAA,cACR,aAAa,EAAE;AAAA,cACf,aAAa,EAAE;AAAA,YACjB,EAAE;AAAA,UACJ;AAAA,QACF,CAAC;AACD;AAAA,MACF;AAEA,UAAI,QAAQ,WAAW,cAAc;AACnC,cAAM,SAAS,QAAQ,UAAU,CAAC;AAClC,cAAM,WAAW,OAAO,OAAO,QAAQ,EAAE;AACzC,cAAM,QAAS,OAAO,aAAa,CAAC;AAEpC,YAAI,CAAC,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ,GAAG;AAK3C,oBAAU,KAAK;AAAA,YACb,SAAS;AAAA,YACT,IAAI;AAAA,YACJ,QAAQ;AAAA,cACN,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,uBAAuB,QAAQ,GAAG,CAAC;AAAA,cACnE,SAAS;AAAA,YACX;AAAA,UACF,CAAC;AACD;AAAA,QACF;AAEA,YAAI,aAAa,sBAAsB;AACrC,gBAAM,UAAU,oBAAoB,KAAK;AACzC,cAAI,SAAS;AAGX,gCAAoB,KAAK,WAAW,EAAE,MAAM,SAAS,SAAS,QAAQ,CAAC;AACvE;AAAA,UACF;AAAA,QACF;AAOA,cAAM,cAAc,cAAc,IAAI,QAAQ;AAC9C,YAAI,aAAa;AACf,cAAI;AACJ,cAAI;AACF,0BAAc,MAAM,YAAY,KAAK;AAAA,UACvC,SAAS,kBAAkB;AACzB,kBAAM,UACJ,4BAA4B,QACxB,iBAAiB,UACjB,OAAO,gBAAgB;AAC7B,gBAAI,KAAK,gCAAgC,EAAE,UAAU,OAAO,QAAQ,CAAC;AACrE,0BAAc,EAAE,MAAM,SAAS,QAAQ;AAAA,UACzC;AACA,8BAAoB,KAAK,WAAW,WAAW;AAC/C;AAAA,QACF;AAEA,cAAM,SAAgB,kBAAW;AACjC,YAAI,KAAK,gCAAgC;AAAA,UACvC;AAAA,UACA;AAAA,UACA,UAAU,SAAS;AAAA,UACnB,KAAK,mBAAmB,IAAI,QAAQ,MAAM;AAAA,QAC5C,CAAC;AAUD,cAAM,UAA4B,EAAE,QAAQ,MAAM;AAClD,gBAAQ,mBAAmB,IAAI,QAAQ,MAAM,IACzC,gBAAgB,KAAK,WAAW,IAChC,eAAe,KAAK,WAAW;AACnC,YAAI,KAAK,SAAS,MAAM;AACtB,iBAAO,KAAK;AACZ,cAAI,IAAI,iBAAkB;AAC1B,kBAAQ,SAAS;AACjB,cAAI,OAAO,yDAAyD;AAAA,YAClE;AAAA,YACA;AAAA,UACF,CAAC;AAAA,QACH,CAAC;AAED,YAAI,QAA8B;AAClC,cAAM,SAAS,MAAM,IAAI;AAAA,UACvB,CAACC,UAASC,YAAW;AACnB,kBAAM,QAAuB;AAAA,cAC3B,IAAI;AAAA,cACJ;AAAA,cACA;AAAA,cACA,SAAAD;AAAA,cACA,QAAAC;AAAA,cACA;AAAA,YACF;AACA,oBAAQ,IAAI,QAAQ,KAAK;AACzB,kBAAM,aAAa;AAAA,cACjB;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAIA,gBAAI,aAAa,sBAAsB;AAGrC,sBAAQ,iBAAiB;AAAA,gBACvB;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA,UAAU,MAAM;AACd,sBAAI,CAAC,QAAQ,IAAI,MAAM,EAAG;AAC1B,0BAAQ,OAAO,MAAM;AACrB,sBAAI,OAAO,iCAAiC;AAAA,oBAC1C;AAAA,oBACA;AAAA,oBACA;AAAA,kBACF,CAAC;AACD,kBAAAA,QAAO,uBAAuB,UAAU,UAAU,CAAC;AAAA,gBACrD;AAAA,cACF,CAAC;AAAA,YACH;AACA,kBAAM,KAAK,QAAQ,KAAK;AAAA,UAC1B;AAAA,QACF,EAAE,QAAQ,MAAM;AACd,iBAAO,OAAO;AACd,kBAAQ,OAAO,MAAM;AAAA,QACvB,CAAC;AAED,YAAI,QAAQ,QAAQ;AAGlB,cAAI,OAAO,oDAAoD;AAAA,YAC7D;AAAA,YACA;AAAA,UACF,CAAC;AACD;AAAA,QACF;AACA,4BAAoB,KAAK,WAAW,QAAQ,KAAK;AACjD;AAAA,MACF;AAEA,gBAAU,KAAK;AAAA,QACb,SAAS;AAAA,QACT,IAAI;AAAA,QACJ,OAAO,EAAE,MAAM,QAAQ,SAAS,mBAAmB,QAAQ,MAAM,GAAG;AAAA,MACtE,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC1E,YAAM,QAAQ,uBAAuB,YAAY,IAAI,IAAI,SAAS,IAAI;AACtE,YAAM,oCAAoC;AAAA,QACxC,OAAO;AAAA,MACT,CAAC;AAKD,UAAI,kBAAkB,cAAc;AAClC,YAAI;AACF;AAAA,YACE;AAAA,YACA;AAAA,YACA,EAAE,MAAM,SAAS,SAAS,aAAa;AAAA,YACvC;AAAA,UACF;AAAA,QACF,QAAQ;AACN,cAAI;AACF,gBAAI,aAAa;AACjB,gBAAI,IAAI;AAAA,UACV,QAAQ;AAAA,UAAC;AAAA,QACX;AACA;AAAA,MACF;AACA,UAAI;AAIF,kBAAU,KAAK;AAAA,UACb,SAAS;AAAA,UACT,IAAI;AAAA,UACJ,OAAO;AAAA,YACL,MAAM;AAAA,YACN,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UACpD;AAAA,QACF,CAAC;AAAA,MACH,QAAQ;AACN,YAAI;AACF,cAAI,aAAa;AACjB,cAAI,IAAI;AAAA,QACV,QAAQ;AAAA,QAAC;AAAA,MACX;AAAA,IACF;AAAA,EACF,CAAC;AAED,QAAM,IAAI,QAAc,CAACD,UAASC,YAAW;AAC3C,IAAAF,QAAO,KAAK,SAASE,OAAM;AAC3B,IAAAF,QAAO,OAAO,GAAG,aAAa,MAAM;AAClC,MAAAA,QAAO,IAAI,SAASE,OAAM;AAC1B,MAAAD,SAAQ;AAAA,IACV,CAAC;AAAA,EACH,CAAC;AAED,QAAM,OAAOD,QAAO,QAAQ;AAC5B,MAAI,CAAC,MAAM;AACT,IAAAA,QAAO,MAAM;AACb,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACnD;AAEA,mBAAiB,aAAa,KAAK,IAAI;AACvC,QAAM,MAAM,UAAU,cAAc;AAKpC,MAAI,KAAK,4BAA4B;AAAA,IACnC;AAAA,IACA,OAAO,MAAM,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EAChC,CAAC;AAED,MAAI,iBAAgC;AAEpC,QAAM,MAAsB;AAAA,IAC1B;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AACX,UAAI,eAAgB,QAAO;AAC3B,YAAM,OAAO,KAAK;AAAA,QAChB;AAAA,UACE,YAAY;AAAA,YACV,CAAC,WAAW,GAAG;AAAA,cACb,MAAM;AAAA,cACN;AAAA;AAAA;AAAA;AAAA,cAIA,SAAS,EAAE,eAAe,UAAU,SAAS,GAAG;AAAA,cAChD,SAAS,4BAA4B,gBAAgB;AAAA,YACvD;AAAA,UACF;AAAA,QACF;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,YAAM,OACH,kBAAW,QAAQ,EACnB,OAAO,IAAI,EACX,OAAO,KAAK,EACZ,MAAM,GAAG,EAAE;AACd,YAAM,UAAe;AAAA,QACnB,aAAa;AAAA,QACb,SAAS,IAAI;AAAA,MACf;AACA,MAAG,kBAAc,SAAS,MAAM,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AACjE,uBAAiB;AACjB,aAAO;AAAA,IACT;AAAA,IACA,iBAAiB;AACf,aAAO,CAAC,GAAG,QAAQ,KAAK,CAAC;AAAA,IAC3B;AAAA,IACA,MAAM,QAAQ;AACZ,iBAAW,SAAS,QAAQ,OAAO,GAAG;AACpC,cAAM,OAAO,IAAI,MAAM,qBAAqB,CAAC;AAAA,MAC/C;AACA,cAAQ,MAAM;AACd,YAAM,IAAI,QAAc,CAACC,aAAY;AACnC,QAAAD,QAAO,MAAM,MAAMC,SAAQ,CAAC;AAAA,MAC9B,CAAC;AACD,UAAI,gBAAgB;AAClB,YAAI;AACF,UAAG,eAAW,cAAc;AAAA,QAC9B,QAAQ;AAAA,QAAC;AACT,yBAAiB;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAGO,SAAS,oBAAoB,OAAiC;AAWnE,QAAM,UAAoC;AAAA,IACxC,MAAM,CAAC,MAAM;AAAA,IACb,MAAM,CAAC,MAAM;AAAA,IACb,OAAO,CAAC,OAAO;AAAA,IACf,MAAM,CAAC,QAAQ,WAAW;AAAA,IAC1B,MAAM,CAAC,MAAM;AAAA,IACb,MAAM,CAAC,MAAM;AAAA,IACb,UAAU,CAAC,UAAU;AAAA,IACrB,MAAM,CAAC,OAAO;AAAA,IACd,YAAY,CAAC,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMpB,UAAU,CAAC,iBAAiB;AAAA,EAC9B;AACA,QAAM,MAAgB,CAAC;AACvB,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,KAAK,OAAO;AACrB,UAAM,SAAS,QAAQ,EAAE,KAAK,YAAY,CAAC;AAC3C,QAAI,CAAC,OAAQ;AACb,eAAW,cAAc,QAAQ;AAC/B,UAAI,KAAK,IAAI,UAAU,EAAG;AAC1B,WAAK,IAAI,UAAU;AACnB,UAAI,KAAK,UAAU;AAAA,IACrB;AAAA,EACF;AACA,SAAO;AACT;AAUO,SAAS,uBAAuB,SAI1B;AACX,QAAM,MAAgB,CAAC;AACvB,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,OAAO,CAAC,SAAiB;AAC7B,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,CAAC,WAAW,KAAK,IAAI,OAAO,EAAG;AACnC,SAAK,IAAI,OAAO;AAChB,QAAI,KAAK,OAAO;AAAA,EAClB;AAEA,aAAW,QAAQ,oBAAoB,QAAQ,cAAc,CAAC,CAAC,EAAG,MAAK,IAAI;AAC3E,aAAW,QAAQ,QAAQ,wBAAwB,CAAC,EAAG,MAAK,OAAO,IAAI,CAAC;AACxE,MAAI,QAAQ,iBAAkB,MAAK,WAAW;AAC9C,SAAO;AACT;AA6BO,SAAS,6BAA6B,SAI1B;AACjB,QAAM,YAAY,QAAQ,aAAa,CAAC;AACxC,MAAI,UAAU,WAAW,EAAG,QAAO,CAAC;AAEpC,QAAM,QAAQ,QAAQ;AACtB,MAAI,CAAC,OAAO;AACV,QAAI;AAAA,MACF;AAAA,MAEA,EAAE,WAAW,UAAU,IAAI,MAAM,EAAE;AAAA,IACrC;AACA,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,YAAY,oBAAI,IAAmC;AACzD,aAAW,QAAQ,OAAO;AACxB,UAAM,MAAM,KAAK,GAAG,YAAY;AAChC,QAAI,CAAC,UAAU,IAAI,GAAG,EAAG,WAAU,IAAI,KAAK,IAAI;AAAA,EAClD;AAEA,QAAM,QAAQ,QAAQ,SAAS,oBAAI,IAAY;AAC/C,QAAM,MAAsB,CAAC;AAC7B,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,UAAoB,CAAC;AAC3B,QAAM,WAAqB,CAAC;AAE5B,aAAW,OAAO,WAAW;AAC3B,UAAM,OAAO,OAAO,GAAG,EAAE,KAAK;AAC9B,QAAI,CAAC,KAAM;AACX,UAAM,OAAO,UAAU,IAAI,KAAK,YAAY,CAAC;AAC7C,QAAI,CAAC,MAAM;AACT,cAAQ,KAAK,IAAI;AACjB;AAAA,IACF;AACA,QAAI,MAAM,IAAI,KAAK,EAAE,GAAG;AACtB,eAAS,KAAK,KAAK,EAAE;AACrB;AAAA,IACF;AACA,QAAI,KAAK,IAAI,KAAK,EAAE,EAAG;AACvB,SAAK,IAAI,KAAK,EAAE;AAChB,QAAI,KAAK;AAAA,MACP,MAAM,KAAK;AAAA,MACX,aAAa,OAAO,KAAK,gBAAgB,WAAW,KAAK,cAAc;AAAA,MACvE,aACE,KAAK,cAAc,OAAO,KAAK,eAAe,WACzC,KAAK,aACN,EAAE,MAAM,UAAU,YAAY,CAAC,EAAE;AAAA,IACzC,CAAC;AAAA,EACH;AAKA,MAAI,QAAQ,SAAS,GAAG;AACtB,QAAI,KAAK,+CAA+C;AAAA,MACtD;AAAA,MACA,OAAO,CAAC,GAAG,UAAU,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,KAAK,EAAE,EAAE,KAAK,IAAI;AAAA,IACjE,CAAC;AAAA,EACH;AACA,MAAI,SAAS,SAAS,GAAG;AACvB,QAAI;AAAA,MACF;AAAA,MAEA,EAAE,SAAS;AAAA,IACb;AAAA,EACF;AACA,SAAO;AACT;AAqCO,SAAS,wBAAwB,SAIb;AACzB,QAAM,QAAgC,EAAE,MAAM,CAAC,GAAG,gBAAgB,oBAAI,IAAI,EAAE;AAC5E,QAAM,cAAc,QAAQ,eAAe,CAAC;AAC5C,MAAI,YAAY,WAAW,EAAG,QAAO;AAErC,QAAM,QAAQ,QAAQ;AACtB,MAAI,CAAC,SAAS,MAAM,WAAW,EAAG,QAAO;AAEzC,QAAM,sBAAsB,CAAC,GAAG,WAAW,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM;AAC/E,QAAM,QAAQ,QAAQ,SAAS,oBAAI,IAAY;AAC/C,QAAM,OAAuB,CAAC;AAC9B,QAAM,iBAAiB,oBAAI,IAAY;AACvC,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,WAAqB,CAAC;AAE5B,aAAW,QAAQ,OAAO;AAIxB,QAAI,MAAM,SAAS,UAAa,KAAK,SAAS,WAAY;AAC1D,UAAM,OAAO,OAAO,MAAM,SAAS,WAAW,KAAK,KAAK,KAAK,IAAI;AACjE,QAAI,CAAC,KAAM;AAEX,UAAM,gBAAgB,oBAAoB;AAAA,MACxC,CAACD,YAAW,SAASA,WAAU,KAAK,WAAW,GAAGA,OAAM,GAAG;AAAA,IAC7D;AACA,QAAI,CAAC,cAAe;AACpB,QAAI,KAAK,IAAI,IAAI,EAAG;AACpB,QAAI,MAAM,IAAI,IAAI,GAAG;AACnB,eAAS,KAAK,IAAI;AAClB;AAAA,IACF;AACA,SAAK,IAAI,IAAI;AACb,mBAAe,IAAI,aAAa;AAChC,SAAK,KAAK;AAAA,MACR;AAAA,MACA,aAAa,OAAO,KAAK,gBAAgB,WAAW,KAAK,cAAc;AAAA,MACvE,aACE,KAAK,eAAe,OAAO,KAAK,gBAAgB,WAC3C,KAAK,cACN,EAAE,MAAM,UAAU,YAAY,CAAC,EAAE;AAAA,IACzC,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,SAAS,GAAG;AAIvB,QAAI;AAAA,MACF;AAAA,MAEA,EAAE,SAAS;AAAA,IACb;AAAA,EACF;AACA,SAAO,EAAE,MAAM,eAAe;AAChC;AAEA,SAAS,SAAS,KAAuC;AACvD,SAAO,IAAI,QAAQ,CAACC,UAAS,WAAW;AACtC,UAAM,SAAmB,CAAC;AAC1B,QAAI,GAAG,QAAQ,CAAC,UAAkB,OAAO,KAAK,KAAK,CAAC;AACpD,QAAI,GAAG,OAAO,MAAMA,SAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM,CAAC,CAAC;AACnE,QAAI,GAAG,SAAS,MAAM;AAAA,EACxB,CAAC;AACH;AASA,SAAS,oBACP,KACA,WACA,QACA,QAA4B,MACtB;AACN,QAAM,OAAO,OAAO,SAAS,UAAU,OAAO,UAAU,OAAO;AAC/D,QAAM,UAAU,OAAO,SAAS,WAAW,OAAO,YAAY;AAC9D,QAAM,WAAW;AAAA,IACf,SAAS;AAAA,IACT,IAAI,aAAa;AAAA,IACjB,QAAQ;AAAA,MACN,SAAS,CAAC,EAAE,MAAM,QAAQ,KAAK,CAAC;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO;AACT,UAAM,OAAO,QAAQ;AACrB;AAAA,EACF;AACA,YAAU,KAAK,QAAQ;AACzB;AAgBA,SAAS,eACP,KACA,aACA,MACY;AACZ,MAAI,QAA+C,YAAY,MAAM;AACnE,QAAI,IAAI,iBAAiB,IAAI,WAAW;AACtC,WAAK;AACL;AAAA,IACF;AACA,QAAI,MAAM,IAAI;AAAA,EAChB,GAAG,WAAW;AACd,QAAM,QAAQ;AACd,QAAM,OAAO,MAAM;AACjB,QAAI,OAAO;AACT,oBAAc,KAAK;AACnB,cAAQ;AAAA,IACV;AAAA,EACF;AACA,SAAO;AACT;AAOA,SAAS,gBAAgB,KAAqB,aAAkC;AAC9E,MAAI,aAAa;AACjB,MAAI,UAAU,gBAAgB,mBAAmB;AACjD,MAAI,UAAU,iBAAiB,wBAAwB;AACvD,MAAI,UAAU,cAAc,YAAY;AACxC,MAAI,aAAa;AAEjB,MAAI,MAAM,YAAY;AACtB,QAAM,OAAO,eAAe,KAAK,aAAa,iBAAiB;AAC/D,SAAO;AAAA,IACL;AAAA,IACA,OAAO,UAAU;AACf,WAAK;AACL,UAAI,IAAI,iBAAiB,IAAI,UAAW;AACxC,UAAI,IAAI;AAAA,QAAyB,KAAK,UAAU,QAAQ,CAAC;AAAA;AAAA,CAAM;AAAA,IACjE;AAAA,EACF;AACF;AAWA,SAAS,eAAe,KAAqB,aAAkC;AAC7E,MAAI,aAAa;AACjB,MAAI,UAAU,gBAAgB,kBAAkB;AAChD,MAAI,UAAU,iBAAiB,wBAAwB;AACvD,MAAI,aAAa;AACjB,QAAM,OAAO,eAAe,KAAK,aAAa,GAAG;AACjD,SAAO;AAAA,IACL;AAAA,IACA,OAAO,UAAU;AACf,WAAK;AACL,UAAI,IAAI,iBAAiB,IAAI,UAAW;AACxC,UAAI,IAAI,KAAK,UAAU,QAAQ,CAAC;AAAA,IAClC;AAAA,EACF;AACF;AAEA,SAAS,UAAU,KAAqB,MAAqB;AAC3D,MAAI,IAAI,aAAa,IAAI,cAAe;AACxC,QAAM,UAAU,KAAK,UAAU,IAAI;AACnC,MAAI,aAAa;AACjB,MAAI,UAAU,gBAAgB,kBAAkB;AAChD,MAAI,UAAU,kBAAkB,OAAO,WAAW,OAAO,EAAE,SAAS,CAAC;AACrE,MAAI,IAAI,OAAO;AACjB;;;AE9sDA,SAAS,gBAAAE,qBAAoB;AA2DtB,IAAM,yBAAyB,IAAI;AAkBnC,IAAM,kCAAkC;AACxC,IAAM,gCAAgC;AAG7C,SAAS,mBAAmB,SAAgC;AAC1D,UAAQ,OAAO,OAAO;AACtB,MAAI,QAAQ,WAAY,eAAc,QAAQ,UAAU;AACxD,MAAI,QAAQ,kBAAmB,cAAa,QAAQ,iBAAiB;AACvE;AAeA,IAAM,kBAAkB,oBAAI,IAA6B;AAGzD,IAAM,mBAAmB,oBAAI,IAAyB;AAEtD,IAAM,UAAU,IAAIC,cAAa;AAEjC,SAAS,UAAUC,aAAoB;AACrC,SAAO,WAAWA,WAAU;AAC9B;AAEA,SAAS,SAASA,aAAoB,QAAgB;AACpD,MAAI,IAAI,iBAAiB,IAAIA,WAAU;AACvC,MAAI,CAAC,GAAG;AACN,QAAI,oBAAI,IAAI;AACZ,qBAAiB,IAAIA,aAAY,CAAC;AAAA,EACpC;AACA,IAAE,IAAI,MAAM;AACd;AAEA,SAAS,YAAYA,aAAoB,QAAgB;AACvD,QAAM,IAAI,iBAAiB,IAAIA,WAAU;AACzC,MAAI,CAAC,EAAG;AACR,IAAE,OAAO,MAAM;AACf,MAAI,EAAE,SAAS,EAAG,kBAAiB,OAAOA,WAAU;AACtD;AAEO,SAAS,mBACdA,aACA,SACY;AACZ,QAAM,OAAO,UAAUA,WAAU;AACjC,UAAQ,GAAG,MAAM,OAAO;AACxB,SAAO,MAAM,QAAQ,IAAI,MAAM,OAAO;AACxC;AAEO,SAAS,sBACdA,aACA,MACA,kBAEA,iBAAyB,wBAEzB,uBAA+B,+BACb;AAIlB,QAAM,WAAW,gBAAgB,IAAI,KAAK,EAAE;AAC5C,MAAI,UAAU;AACZ,uBAAmB,QAAQ;AAC3B,aAAS;AAAA,MACP,IAAI,MAAM,+BAA+B,KAAK,EAAE,mBAAmB;AAAA,IACrE;AACA,oBAAgB,OAAO,KAAK,EAAE;AAC9B,gBAAY,SAAS,YAAY,KAAK,EAAE;AAAA,EAC1C;AAEA,QAAM,aAAa;AAAA,IACjB,KAAK;AAAA,IACL,KAAK;AAAA,IACL;AAAA,EACF;AAQA,QAAM,QACJ,aAAa,uBACT,iBAAiB;AAAA,IACf,QAAQ,KAAK;AAAA,IACb,UAAU,KAAK;AAAA,IACf;AAAA,IACA,cAAc;AAAA,IACd,UAAU,MAAM;AACd,YAAM,UAAU,gBAAgB,IAAI,KAAK,EAAE;AAC3C,UAAI,CAAC,QAAS;AACd,sBAAgB,OAAO,KAAK,EAAE;AAC9B,kBAAY,QAAQ,YAAY,KAAK,EAAE;AACvC,yBAAmB,OAAO;AAC1B,cAAQ,OAAO,uBAAuB,KAAK,UAAU,UAAU,CAAC;AAChE,UAAI,OAAO,gCAAgC;AAAA,QACzC,YAAY,QAAQ;AAAA,QACpB,YAAY,KAAK;AAAA,QACjB,UAAU,KAAK;AAAA,QACf;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,CAAC,IACD;AAMN,QAAM,aACJ,eAAe,wBAAwB,iBAAiB,IACpD,YAAY,MAAM;AAChB,UAAM,UAAU,gBAAgB,IAAI,KAAK,EAAE;AAC3C,QAAI,CAAC,QAAS;AACd,QAAI,KAAK,yCAAyC;AAAA,MAChD,YAAY,QAAQ;AAAA,MACpB,YAAY,QAAQ;AAAA,MACpB,UAAU,QAAQ;AAAA,MAClB,UAAU,KAAK,IAAI,IAAI,QAAQ;AAAA,MAC/B,SAAS,QAAQ,YAAY;AAAA,MAC7B,eAAe,QAAQ,SAAS,WAAW;AAAA,MAC3C,MAAM;AAAA,IACR,CAAC;AAAA,EACH,GAAG,cAAc,IACjB;AAEN,cAAY,QAAQ;AAKpB,QAAM,WAAW,KAAK,MAAM,aAAa,+BAA+B;AACxE,QAAM,oBACJ,cAAc,wBAAwB,aAAa,KAAK,WAAW,IAC/D,WAAW,MAAM;AACf,UAAM,UAAU,gBAAgB,IAAI,KAAK,EAAE;AAC3C,QAAI,CAAC,QAAS;AACd,UAAM,WAAW,KAAK,IAAI,IAAI,QAAQ;AACtC,QAAI,KAAK,kDAAkD;AAAA,MACzD,YAAY,QAAQ;AAAA,MACpB,YAAY,QAAQ;AAAA,MACpB,UAAU,QAAQ;AAAA,MAClB;AAAA,MACA;AAAA,MACA,aAAa,KAAK,IAAI,GAAG,aAAa,QAAQ;AAAA,MAC9C,SAAS,QAAQ,YAAY;AAAA,MAC7B,eAAe,QAAQ,SAAS,WAAW;AAAA,MAC3C,MAAM;AAAA,IACR,CAAC;AAAA,EACH,GAAG,QAAQ,IACX;AACN,qBAAmB,QAAQ;AAE3B,QAAM,UAA2B;AAAA,IAC/B,YAAAA;AAAA,IACA,YAAY,KAAK;AAAA,IACjB,UAAU,KAAK;AAAA,IACf,OAAO,KAAK;AAAA,IACZ,SAAS,KAAK;AAAA,IACd,WAAW,KAAK,IAAI;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,KAAK;AAAA,IACd,QAAQ,KAAK;AAAA,EACf;AACA,kBAAgB,IAAI,KAAK,IAAI,OAAO;AACpC,WAASA,aAAY,KAAK,EAAE;AAC5B,UAAQ,KAAK,UAAUA,WAAU,GAAG,OAAO;AAC3C,MAAI,KAAK,6BAA6B;AAAA,IACpC,YAAAA;AAAA,IACA,YAAY,KAAK;AAAA,IACjB,UAAU,KAAK;AAAA,EACjB,CAAC;AACD,SAAO;AACT;AAGO,SAAS,4BAA4B,YAA0B;AACpE,QAAM,UAAU,gBAAgB,IAAI,UAAU;AAC9C,MAAI,QAAS,SAAQ,UAAU;AACjC;AAGO,SAAS,gCACd,MACS;AACT,SAAO,KAAK,SAAS,WAAW;AAClC;AAGO,SAAS,qBAAqB,YAAkD;AACrF,SAAO,gBAAgB,IAAI,UAAU;AACvC;AAEO,SAAS,qBAAqBA,aAAwC;AAC3E,QAAM,IAAI,iBAAiB,IAAIA,WAAU;AACzC,MAAI,CAAC,KAAK,EAAE,SAAS,EAAG,QAAO,CAAC;AAChC,QAAM,MAA0B,CAAC;AACjC,aAAW,MAAM,GAAG;AAClB,UAAM,IAAI,gBAAgB,IAAI,EAAE;AAChC,QAAI,EAAG,KAAI,KAAK,CAAC;AAAA,EACnB;AACA,SAAO;AACT;AAQO,SAAS,0BAA0B,MAAM,KAAK,IAAI,GAA+B;AACtF,QAAM,MAAkC,CAAC;AACzC,aAAW,WAAW,gBAAgB,OAAO,GAAG;AAC9C,QAAI,KAAK;AAAA,MACP,YAAY,QAAQ;AAAA,MACpB,YAAY,QAAQ;AAAA,MACpB,UAAU,QAAQ;AAAA,MAClB,OAAO,KAAK,IAAI,GAAG,MAAM,QAAQ,SAAS;AAAA,MAC1C,YAAY,QAAQ;AAAA,MACpB,SAAS,QAAQ,YAAY;AAAA,MAC7B,eAAe,QAAQ,SAAS,WAAW;AAAA,IAC7C,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEO,SAAS,4BACd,YACA,QACS;AACT,QAAM,UAAU,gBAAgB,IAAI,UAAU;AAC9C,MAAI,CAAC,QAAS,QAAO;AACrB,kBAAgB,OAAO,UAAU;AACjC,cAAY,QAAQ,YAAY,UAAU;AAC1C,qBAAmB,OAAO;AAC1B,UAAQ,QAAQ,MAAM;AACtB,MAAI,KAAK,+BAA+B;AAAA,IACtC,YAAY,QAAQ;AAAA,IACpB,YAAY,QAAQ;AAAA,IACpB,UAAU,QAAQ;AAAA,EACpB,CAAC;AACD,SAAO;AACT;AAEO,SAAS,2BACd,YACA,OACS;AACT,QAAM,UAAU,gBAAgB,IAAI,UAAU;AAC9C,MAAI,CAAC,QAAS,QAAO;AACrB,kBAAgB,OAAO,UAAU;AACjC,cAAY,QAAQ,YAAY,UAAU;AAC1C,qBAAmB,OAAO;AAC1B,UAAQ,OAAO,KAAK;AAIpB,MAAI,OAAO,+BAA+B;AAAA,IACxC,YAAY,QAAQ;AAAA,IACpB,YAAY,QAAQ;AAAA,IACpB,UAAU,QAAQ;AAAA,IAClB,OAAO,MAAM;AAAA,EACf,CAAC;AACD,SAAO;AACT;AAEO,SAAS,qCACdA,aACA,OACQ;AACR,QAAM,IAAI,iBAAiB,IAAIA,WAAU;AACzC,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,MAAM,CAAC,GAAG,CAAC;AACjB,MAAI,QAAQ;AACZ,aAAW,MAAM,KAAK;AACpB,QAAI,2BAA2B,IAAI,KAAK,EAAG;AAAA,EAC7C;AACA,SAAO;AACT;;;ACrVA,IAAM,0BAA0B;AAEhC,IAAM,eAAe,oBAAI,IAA8B;AAOhD,SAAS,wBAAwBC,aAAoB,SAAuB;AACjF,eAAa,IAAIA,aAAY,EAAE,SAAS,gBAAgB,KAAK,CAAC;AAC9D,SAAO,aAAa,OAAO,yBAAyB;AAClD,UAAM,SAAS,aAAa,KAAK,EAAE,KAAK;AACxC,QAAI,OAAO,KAAM;AACjB,iBAAa,OAAO,OAAO,KAAK;AAChC,QAAI,KAAK,0CAA0C,EAAE,YAAY,OAAO,MAAM,CAAC;AAAA,EACjF;AACF;AAEO,SAAS,sBAAsBA,aAAwC;AAC5E,SAAO,aAAa,IAAIA,WAAU,GAAG;AACvC;AAOO,SAAS,0BAA0BA,aAA6B;AACrE,QAAM,QAAQ,aAAa,IAAIA,WAAU;AACzC,MAAI,CAAC,OAAO,eAAgB,QAAO;AACnC,QAAM,iBAAiB;AACvB,SAAO;AACT;AAEO,SAAS,iBAAiBA,aAA0B;AACzD,eAAa,OAAOA,WAAU;AAChC;;;AClEA,SAAS,cAAAC,mBAAkB;AAyB3B,IAAM,wBAAwB;AAEvB,IAAM,sBACX;AAEF,IAAM,mBAAmB,oBAAI,QAAsB;AAEnD,SAASC,UAAS,OAAkD;AAClE,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAaA,IAAM,wBAAwB;AAEvB,SAAS,yBAAyB,SAA+C;AACtF,MAAI;AACJ,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO;AAAA,EACT,WAAW,MAAM,QAAQ,OAAO,GAAG;AACjC,UAAM,QAAkB,CAAC;AACzB,eAAW,QAAQ,SAAS;AAC1B,UAAI,CAACA,UAAS,IAAI,KAAK,KAAK,SAAS,UAAU,OAAO,KAAK,SAAS,SAAU,QAAO;AACrF,YAAM,KAAK,KAAK,IAAI;AAAA,IACtB;AACA,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB,OAAO;AACL,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,2BAA2B,KAAK,KAAK,QAAQ,uBAAuB,EAAE,EAAE,KAAK,CAAC;AAC5F,SAAO,QAAQ,EAAE,WAAW,MAAM,CAAC,KAAK,IAAI,KAAK,EAAE,IAAI;AACzD;AAGO,SAAS,kBACd,QAC6B;AAC7B,QAAM,SAAS,OAAO,GAAG,EAAE;AAC3B,SAAO,QAAQ,SAAS,SAAS,yBAAyB,OAAO,OAAO,IAAI;AAC9E;AAEA,SAAS,cAAc,SAA0B;AAC/C,MAAI,OAAO,YAAY,SAAU,QAAO,QAAQ,KAAK;AACrD,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,QAAM,QAAkB,CAAC;AACzB,aAAW,QAAQ,SAAS;AAC1B,QAAIA,UAAS,IAAI,KAAK,KAAK,SAAS,UAAU,OAAO,KAAK,SAAS,SAAU,OAAM,KAAK,KAAK,IAAI;AAAA,EACnG;AACA,SAAO,MAAM,KAAK,IAAI,EAAE,KAAK;AAC/B;AAOO,SAAS,2BACd,QACwB;AACxB,QAAM,UAAkC,CAAC;AACzC,WAAS,QAAQ,GAAG,QAAQ,OAAO,SAAS,GAAG,SAAS;AACtD,UAAM,UAAU,OAAO,KAAK;AAC5B,QAAI,QAAQ,SAAS,OAAQ;AAC7B,UAAM,QAAQ,yBAAyB,QAAQ,OAAO;AACtD,QAAI,CAAC,OAAO,SAAU;AACtB,UAAM,QAAQ,OAAO,QAAQ,CAAC;AAC9B,QAAI,MAAM,SAAS,YAAa;AAChC,UAAM,WAAW,cAAc,MAAM,OAAO;AAC5C,QAAI,CAAC,YAAY,aAAa,oBAAqB;AACnD,YAAQ,KAAK,EAAE,UAAU,MAAM,UAAU,SAAS,CAAC;AAAA,EACrD;AACA,SAAO,QAAQ,MAAM,CAAC,qBAAqB;AAC7C;AAEO,SAAS,sBAAsB,eAA6C;AACjF,SAAO,iBAAiB,IAAI,cAAc,IAAI;AAChD;AAOO,SAAS,6BACd,eACA,MACS;AACT,MAAI,CAAC,iBAAiB,IAAI,cAAc,IAAI,EAAG,QAAO;AACtD,MAAI;AACJ,MAAI;AACF,cAAU,KAAK,MAAM,IAAI;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,CAACA,UAAS,OAAO,KAAK,QAAQ,SAAS,mBAAoB,QAAO;AACtE,QAAM,WAAW,QAAQ;AACzB,MAAI,CAACA,UAAS,QAAQ,KAAK,OAAO,SAAS,eAAe,SAAU,QAAO;AAC3E,SAAO,cAAc,YAAY,KAAK,iBAAiB,SAAS,UAAU,IAAI,QAAQ;AACxF;AAUA,eAAsB,oBACpB,eACA,UACA,SAC6B;AAC7B,aAAW,SAAS,KAAK;AACzB,MAAI,CAAC,SAAU,QAAO,EAAE,UAAU,qBAAqB,WAAW,KAAK;AACvE,UAAQ,aAAa,eAAe;AACpC,QAAM,EAAE,MAAM,YAAY,IAAI;AAC9B,MAAI,QAAQ,eAAe,CAAC,KAAK,QAAQ;AACvC,UAAM,IAAI,MAAM,2FAA2F;AAAA,EAC7G;AACA,MAAI,CAAC,wBAAwB,QAAQ,UAAU,GAAG;AAChD,UAAM,IAAI,MAAM,+EAA+E;AAAA,EACjG;AACA,MAAI,iBAAiB,IAAI,IAAI,GAAG;AAC9B,UAAM,IAAI,MAAM,4DAA4D;AAAA,EAC9E;AACA,QAAM,QAAQ,KAAK;AACnB,MAAI,KAAK,UAAU,KAAK,YAAY,QAAQ,KAAK,cAAc,QAC3D,CAAC,SAAS,MAAM,aAAa,MAAM,iBAAiB,CAAC,MAAM,UAAU;AACvE,UAAM,IAAI,MAAM,+DAA+D;AAAA,EACjF;AACA,QAAM,YAAY,QAAQ,aAAa;AACvC,MAAI,CAAC,OAAO,UAAU,SAAS,KAAK,aAAa,KAAK,YAAY,YAAe;AAC/E,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AACA,QAAM,YAAYC,YAAW;AAC7B,QAAM,UAAU,KAAK,UAAU;AAAA,IAC7B,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,SAAS;AAAA,MACP,SAAS;AAAA,MACT;AAAA,MACA,GAAI,QAAQ,YAAY,SAAY,CAAC,IAAI,EAAE,SAAS,QAAQ,QAAQ;AAAA,IACtE;AAAA,EACF,CAAC;AAED,mBAAiB,IAAI,IAAI;AACzB,SAAO,IAAI,QAA4B,CAACC,UAAS,WAAW;AAC1D,UAAM,QAAQ,iBAAiB,SAAS;AACxC,QAAI,UAAU;AACd,QAAI,OAAO;AACX,QAAI,gBAAgB;AAEpB,UAAM,UAAU,MAAY;AAC1B,mBAAa,KAAK;AAClB,kBAAY,IAAI,OAAO,UAAU;AACjC,kBAAY,IAAI,SAAS,OAAO;AAChC,kBAAY,IAAI,SAAS,OAAO;AAChC,WAAK,IAAI,QAAQ,OAAO;AACxB,WAAK,IAAI,SAAS,OAAO;AACzB,WAAK,IAAI,SAAS,OAAO;AACzB,UAAI,CAAC,cAAe,OAAM,IAAI,SAAS,OAAO;AAC9C,cAAQ,aAAa,oBAAoB,SAAS,OAAO;AACzD,uBAAiB,OAAO,IAAI;AAAA,IAC9B;AACA,UAAM,OAAO,CAAC,OAAgB,SAAS,UAAgB;AACrD,UAAI,QAAS;AACb,gBAAU;AACV,UAAI,UAAU,QAAQ,CAAC,MAAM,aAAa,CAAC,MAAM,iBAAiB,MAAM,UAAU;AAChF,YAAI;AACF,0BAAgB;AAChB,gBAAM;AAAA,YACJ,KAAK,UAAU,EAAE,MAAM,0BAA0B,YAAY,UAAU,CAAC,IAAI;AAAA,YAC5E,MAAM;AAGJ,6BAAe,MAAM,MAAM,IAAI,SAAS,OAAO,CAAC;AAAA,YAClD;AAAA,UACF;AAAA,QACF,QAAQ;AACN,0BAAgB;AAAA,QAElB;AAAA,MACF;AACA,cAAQ;AACR,aAAO,KAAK;AAAA,IACd;AACA,UAAM,UAAU,MAAY,KAAK,IAAI,MAAM,2CAA2C,CAAC;AACvF,UAAM,UAAU,CAAC,UAAuB,KAAK,KAAK;AAClD,UAAM,UAAU,MAAY;AAAA,MAC1B,QAAQ,aAAa,UAAU,IAAI,aAAa,qBAAqB,YAAY;AAAA,MACjF;AAAA,IACF;AACA,UAAM,aAAa,CAAC,aAA4C;AAC9D,UAAI,WAAW,SAAS,eAAe,UAAW;AAClD,UAAI,SAAS,YAAY,SAAS;AAChC,aAAK,IAAI,MAAM,OAAO,SAAS,UAAU,WAAW,SAAS,QAAQ,4BAA4B,CAAC;AAClG;AAAA,MACF;AACA,YAAM,SAAS,SAAS;AACxB,UAAI,SAAS,YAAY,aAAa,CAACF,UAAS,MAAM,KAClD,OAAO,OAAO,aAAa,YAAY,OAAO,OAAO,cAAc,WAAW;AAChF,aAAK,IAAI,MAAM,gDAAgD,CAAC;AAChE;AAAA,MACF;AACA,gBAAU;AACV,cAAQ;AACR,MAAAE,SAAQ,EAAE,UAAU,OAAO,UAAU,WAAW,OAAO,UAAU,CAAC;AAAA,IACpE;AACA,UAAM,QAAQ,WAAW,MAAM;AAC7B,WAAK,IAAI,MAAM,wBAAwB,SAAS,KAAK,GAAG,IAAI;AAAA,IAC9D,GAAG,SAAS;AAEZ,gBAAY,GAAG,OAAO,UAAU;AAChC,gBAAY,GAAG,SAAS,OAAO;AAC/B,gBAAY,GAAG,SAAS,OAAO;AAC/B,SAAK,GAAG,QAAQ,OAAO;AACvB,SAAK,GAAG,SAAS,OAAO;AACxB,SAAK,GAAG,SAAS,OAAO;AACxB,UAAM,GAAG,SAAS,OAAO;AACzB,YAAQ,aAAa,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACtE,QAAI,QAAQ,aAAa,SAAS;AAChC,cAAQ;AACR;AAAA,IACF;AACA,QAAI;AACF,aAAO;AACP,YAAM,MAAM,UAAU,IAAI;AAAA,IAC5B,SAAS,OAAO;AACd,WAAK,KAAK;AAAA,IACZ;AAAA,EACF,CAAC;AACH;;;AL/KO,SAAS,6BAA6B,WAA8C;AACzF,MAAI;AAEJ,aAAW,MAAM,gBAAgB,OAAO,GAAG;AACzC,QAAI,GAAG,sBAAsB,UAAW,SAAQ;AAAA,EAClD;AACA,SAAO;AACT;AAOA,IAAM,sBAAsB;AAC5B,IAAM,sBAAsB,IAAI,OAAO;AAEhC,SAAS,qBAAqB,IAAmB,MAAoB;AAC1E,QAAM,QAAS,GAAG,oBAAoB,CAAC;AACvC,QAAM,KAAK,IAAI;AACf,MAAI,QAAQ;AACZ,aAAW,QAAQ,MAAO,UAAS,OAAO,WAAW,IAAI;AACzD,SACE,MAAM,SAAS,MACd,MAAM,SAAS,uBAAuB,QAAQ,sBAC/C;AACA,aAAS,OAAO,WAAW,MAAM,MAAM,CAAE;AACzC,OAAG,qBAAqB,GAAG,qBAAqB,KAAK;AAAA,EACvD;AACF;AAGO,SAAS,oBAAoB,IAGlC;AACA,QAAM,QAAQ,GAAG,mBAAmB,CAAC;AACrC,QAAM,UAAU,GAAG,qBAAqB;AACxC,KAAG,kBAAkB,CAAC;AACtB,KAAG,oBAAoB;AACvB,SAAO,EAAE,OAAO,QAAQ;AAC1B;AAKA,IAAM,sBAAsB,IAAI;AAEzB,SAAS,aAAa,IAAmB,OAAqB;AACnE,KAAG,eAAe,GAAG,cAAc,MAAM,OAAO,MAAM,CAAC,mBAAmB;AAC5E;AAQO,SAAS,mBACd,UACA,QACA,YACQ;AACR,QAAM,MAAM,SACR,iBAAiB,MAAM,KACvB,OAAO,aAAa,WAClB,oBAAoB,QAAQ,KAC5B;AACN,QAAM,OAAO,YAAY,KAAK;AAC9B,SACE,uBAAuB,GAAG,iGAEzB,OAAO;AAAA;AAAA;AAAA,EAAkC,IAAI,KAAK;AAEvD;AAMA,IAAM,kBAAkB,oBAAI,IAA2B;AACvD,IAAM,iBAAiB,oBAAI,IAAoB;AAG/C,IAAM,qBAAqB,oBAAI,IAA2C;AAC1E,IAAM,sBAAsB;AAWrB,IAAM,kCAAkC;AAGxC,SAAS,4BAA4B,YAAwC;AAClF,SAAO,eAAe,SAAY,kCAAkC;AACtE;AAQO,IAAM,uBAAuB;AACpC,IAAM,0BAA0B;AAChC,IAAM,gCAAgC;AAG/B,IAAM,uBACX;AAEF,SAAS,eAAe,OAAoC;AAC1D,MAAI,UAAU,OAAW,QAAO;AAChC,QAAM,aAAa,MAAM,KAAK,EAAE,YAAY;AAC5C,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO,CAAC,CAAC,KAAK,SAAS,MAAM,KAAK,EAAE,SAAS,UAAU;AACzD;AAEO,SAAS,2BAAoC;AAClD,SACE,eAAe,QAAQ,IAAI,4BAA4B,KACvD,eAAe,QAAQ,IAAI,qCAAqC;AAEpE;AAOO,SAAS,eAAe,QAAiC;AAC9D,SAAO,WAAW,YAAY,QAAQ;AACxC;AAEO,SAAS,eAAe,MAIQ;AACrC,QAAM,MAA0C;AAAA,IAC9C,GAAG,QAAQ;AAAA,IACX,MAAM;AAAA;AAAA;AAAA;AAAA,IAIN,GAAG,cAAc;AAAA,EACnB;AAQA,MAAI,MAAM,QAAQ;AAChB,QAAI,2BAA2B,eAAe,KAAK,MAAM;AAAA,EAC3D;AAKA,MAAI,MAAM,uBAAuB;AAC/B,WAAO,IAAI;AACX,WAAO,IAAI;AAAA,EACb;AAKA,MACE,CAAC,yBAAyB,KAC1B,QAAQ,IAAI,wCAAwC,QACpD;AACA,QAAI,sCAAsC;AAAA,EAC5C;AAEA,SAAO;AACT;AAEA,SAAS,MAAM,KAAmB;AAChC,QAAM,WAAW,gBAAgB,IAAI,GAAG;AACxC,MAAI,UAAU;AACZ,oBAAgB,OAAO,GAAG;AAC1B,oBAAgB,IAAI,KAAK,QAAQ;AAAA,EACnC;AACF;AAUO,SAAS,gBAAsB;AACpC,SAAO,gBAAgB,QAAQ,sBAAsB;AACnD,QAAI;AACJ,eAAW,CAAC,KAAK,EAAE,KAAK,iBAAiB;AACvC,UAAI,CAAC,eAAe,EAAE,GAAG;AACvB,oBAAY;AACZ;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,WAAW;AACd,UAAI,KAAK,2DAA2D;AAAA,QAClE,QAAQ,gBAAgB;AAAA,QACxB,KAAK;AAAA,MACP,CAAC;AACD;AAAA,IACF;AACA,QAAI,KAAK,+BAA+B,EAAE,YAAY,UAAU,CAAC;AACjE,wBAAoB,SAAS;AAAA,EAC/B;AACF;AAaA,IAAM,4BAA4B;AAGlC,SAAS,qBAAqB,MAAuB;AACnD,MAAI,CAAC,KAAK,SAAS,UAAU,EAAG,QAAO;AACvC,MAAI;AACF,WAAQ,KAAK,MAAM,IAAI,EAAwB,SAAS;AAAA,EAC1D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,WAAW,IAAyB;AAC3C,KAAG,eAAe;AAClB,QAAM,UAAU,GAAG,mBAAmB,CAAC;AACvC,KAAG,kBAAkB,CAAC;AACtB,aAAW,QAAQ,QAAS,MAAK;AACnC;AAGO,SAAS,gBAAgB,IAAyB;AAGvD,MAAI,GAAG,gBAAgB,YAAa;AACpC,KAAG,eAAe;AACpB;AAOO,SAAS,aAAa,IAAmB,MAAoB;AAClE,MAAI,CAAC,GAAG,aAAc;AACtB,MAAI,qBAAqB,IAAI,EAAG,YAAW,EAAE;AAC/C;AAEO,SAAS,eAAe,IAA4B;AACzD,SAAO,GAAG,iBAAiB;AAC7B;AAGO,SAAS,cAAc,IAAmB,WAAqC;AACpF,MAAI,CAAC,GAAG,aAAc,QAAO,QAAQ,QAAQ,IAAI;AACjD,SAAO,IAAI,QAAQ,CAACC,aAAY;AAC9B,UAAM,OAAO,MAAM;AACjB,mBAAa,KAAK;AAClB,MAAAA,SAAQ,IAAI;AAAA,IACd;AACA,UAAM,QAAQ,WAAW,MAAM;AAC7B,YAAM,UAAU,GAAG,mBAAmB,CAAC;AACvC,YAAM,KAAK,QAAQ,QAAQ,IAAI;AAC/B,UAAI,MAAM,EAAG,SAAQ,OAAO,IAAI,CAAC;AACjC,MAAAA,SAAQ,KAAK;AAAA,IACf,GAAG,SAAS;AACX,KAAC,GAAG,oBAAoB,CAAC,GAAG,KAAK,IAAI;AAAA,EACxC,CAAC;AACH;AAGO,SAAS,cACd,IACA,YAAY,2BACM;AAClB,MAAI,CAAC,GAAG,aAAc,QAAO,QAAQ,QAAQ,IAAI;AACjD,QAAM,QAAQ,GAAG,KAAK;AACtB,MAAI,GAAG,gBAAgB,eAAe,CAAC,SAAS,CAAC,MAAM,UAAU;AAE/D,QAAI,OAAO,8DAA8D;AACzE,WAAO,cAAc,IAAI,SAAS;AAAA,EACpC;AACA,MAAI;AACF,UAAM;AAAA,MACJ,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,QACN,YAAYC,YAAW;AAAA,QACvB,SAAS,EAAE,SAAS,YAAY;AAAA,MAClC,CAAC,IAAI;AAAA,IACP;AAAA,EACF,SAAS,OAAO;AACd,QAAI,KAAK,6CAA6C;AAAA,MACpD,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAC9D,CAAC;AACD,WAAO,QAAQ,QAAQ,KAAK;AAAA,EAC9B;AACA,SAAO,cAAc,IAAI,SAAS;AACpC;AAEA,SAAS,0BAA0B,KAAmB;AACpD,QAAM,QAAQ,mBAAmB,IAAI,GAAG;AACxC,MAAI,CAAC,MAAO;AACZ,eAAa,KAAK;AAClB,qBAAmB,OAAO,GAAG;AAC/B;AAEO,SAAS,iBAAiB,KAAwC;AACvE,QAAM,KAAK,gBAAgB,IAAI,GAAG;AAClC,MAAI,IAAI;AACN,8BAA0B,GAAG;AAC7B,UAAM,GAAG;AAAA,EACX;AACA,SAAO;AACT;AAEO,SAAS,iBAAiB,KAAa,IAAyB;AACrE,4BAA0B,GAAG;AAC7B,kBAAgB,IAAI,KAAK,EAAE;AAC7B;AAYO,SAAS,4BACd,KACA,WACM;AACN,4BAA0B,GAAG;AAC7B,MACE,OAAO,cAAc,YACrB,CAAC,OAAO,SAAS,SAAS,KAC1B,aAAa,KACb,YAAY,qBACZ;AACA;AAAA,EACF;AAEA,QAAM,mBAAmB,gBAAgB,IAAI,GAAG;AAChD,MAAI,CAAC,iBAAkB;AAEvB,QAAM,QAAQ,WAAW,MAAM;AAC7B,uBAAmB,OAAO,GAAG;AAC7B,QAAI,gBAAgB,IAAI,GAAG,MAAM,iBAAkB;AACnD,QAAI,eAAe,gBAAgB,GAAG;AACpC,UAAI,KAAK,gDAAgD,EAAE,YAAY,KAAK,UAAU,CAAC;AACvF,kCAA4B,KAAK,SAAS;AAC1C;AAAA,IACF;AACA,QAAI,KAAK,gCAAgC,EAAE,YAAY,KAAK,UAAU,CAAC;AACvE,wBAAoB,GAAG;AAAA,EACzB,GAAG,SAAS;AACZ,QAAM,MAAM;AACZ,qBAAmB,IAAI,KAAK,KAAK;AACnC;AAOA,SAAS,oBAAoB,KAAwC;AACnE,4BAA0B,GAAG;AAC7B,QAAM,KAAK,gBAAgB,IAAI,GAAG;AAClC,MAAI,CAAC,GAAI,QAAO;AAChB,kBAAgB,OAAO,GAAG;AAC1B,MAAI,GAAG,aAAa;AAClB,SAAK,GAAG,YAAY,MAAM;AAI1B,yCAAqC,KAAK,IAAI,MAAM,qBAAqB,CAAC;AAAA,EAC5E;AACA,SAAO;AACT;AAUO,SAAS,gCAAgC,WAA6B;AAC3E,MAAI,CAAC,aAAa,cAAc,UAAW,QAAO,CAAC;AACnD,QAAM,WAAqB,CAAC;AAC5B,aAAW,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,eAAe,GAAG;AAC5C,UAAM,QACJ,GAAG,sBAAsB,aAAa,mBAAmB,GAAG,EAAE,YAAY;AAC5E,QAAI,CAAC,MAAO;AACZ,QAAI,KAAK,gDAAgD,EAAE,YAAY,KAAK,UAAU,CAAC;AACvF,SAAK,2BAA2B,GAAG;AACnC,aAAS,KAAK,GAAG;AAAA,EACnB;AAGA,aAAW,OAAO,CAAC,GAAG,eAAe,KAAK,CAAC,GAAG;AAC5C,QAAI,mBAAmB,GAAG,EAAE,YAAY,UAAW;AACnD,0BAAsB,GAAG;AACzB,qBAAiB,GAAG;AACpB,QAAI,CAAC,SAAS,SAAS,GAAG,EAAG,UAAS,KAAK,GAAG;AAAA,EAChD;AACA,SAAO;AACT;AAQO,SAAS,yBAAmC;AACjD,QAAM,OAAO,CAAC,GAAG,gBAAgB,KAAK,CAAC;AACvC,aAAW,OAAO,KAAM,qBAAoB,GAAG;AAC/C,SAAO;AACT;AAEA,IAAI,0BAA0B;AAOvB,SAAS,2BAAoC;AAClD,MAAI,wBAAyB,QAAO;AACpC,4BAA0B;AAC1B,UAAQ,KAAK,QAAQ,MAAM;AACzB,2BAAuB;AAAA,EACzB,CAAC;AACD,SAAO;AACT;AAEO,SAAS,oBAAoB,KAAmB;AACrD,QAAM,KAAK,oBAAoB,GAAG;AAClC,MAAI,KAAK,KAAK;AAChB;AAEA,SAAS,iBAAiB,MAA6B;AACrD,SAAO,KAAK,aAAa,QAAQ,KAAK,eAAe;AACvD;AAEA,SAAS,mBACP,MACA,WACkB;AAClB,MAAI,iBAAiB,IAAI,EAAG,QAAO,QAAQ,QAAQ,IAAI;AAEvD,SAAO,IAAI,QAAQ,CAACC,aAAY;AAC9B,UAAM,SAAS,MAAM;AACnB,mBAAa,KAAK;AAClB,MAAAA,SAAQ,IAAI;AAAA,IACd;AACA,UAAM,QAAQ,WAAW,MAAM;AAC7B,WAAK,IAAI,QAAQ,MAAM;AACvB,MAAAA,SAAQ,iBAAiB,IAAI,CAAC;AAAA,IAChC,GAAG,SAAS;AACZ,SAAK,KAAK,QAAQ,MAAM;AAAA,EAC1B,CAAC;AACH;AAEA,eAAsB,2BACpB,KACA,UAGI,CAAC,GACa;AAClB,QAAM,KAAK,oBAAoB,GAAG;AAClC,MAAI,CAAC,MAAM,iBAAiB,GAAG,IAAI,EAAG,QAAO;AAE7C,QAAM,eAAe;AAAA,IACnB,GAAG;AAAA,IACH,QAAQ,iBAAiB;AAAA,EAC3B;AACA,KAAG,KAAK,KAAK;AACb,MAAI,MAAM,aAAc,QAAO;AAE/B,QAAM,aAAa;AAAA,IACjB,GAAG;AAAA,IACH,QAAQ,sBAAsB;AAAA,EAChC;AACA,KAAG,KAAK,KAAK,SAAS;AACtB,MAAI,MAAM,WAAY,QAAO;AAE7B,MAAI,KAAK,yDAAyD;AAAA,IAChE,YAAY;AAAA,EACd,CAAC;AACD,wBAAsB,GAAG;AACzB,SAAO;AACT;AAEO,SAAS,mBAAmB,KAAiC;AAClE,SAAO,eAAe,IAAI,GAAG;AAC/B;AAUO,IAAM,6BAA6B;AAS1C,SAAS,oBAAoB,KAAsB;AACjD,MAAI,gBAAgB,IAAI,GAAG,EAAG,QAAO;AACrC,SAAO,qBAAqB,GAAG,EAAE,SAAS,KAAK,yBAAyB,GAAG;AAC7E;AAQA,SAAS,oBAA0B;AACjC,aAAW,OAAO,CAAC,GAAG,eAAe,KAAK,CAAC,GAAG;AAC5C,QAAI,eAAe,QAAQ,2BAA4B;AACvD,QAAI,oBAAoB,GAAG,EAAG;AAC9B,QAAI,KAAK,yDAAyD;AAAA,MAChE,YAAY;AAAA,MACZ,MAAM,eAAe;AAAA,MACrB,KAAK;AAAA,IACP,CAAC;AAGD,0BAAsB,GAAG;AAAA,EAC3B;AACF;AAEO,SAAS,mBAAmB,KAAa,WAAyB;AAGvE,iBAAe,OAAO,GAAG;AACzB,iBAAe,IAAI,KAAK,SAAS;AACjC,oBAAkB;AACpB;AAEO,SAAS,sBAAsB,KAAmB;AACvD,6BAA2B,GAAG;AAC9B,gCAA8B,GAAG;AACjC,QAAM,kBAAkB,eAAe,IAAI,GAAG;AAC9C,MAAI,gBAAiB,aAAY,eAAe;AAChD,iBAAe,OAAO,GAAG;AAC3B;AAEO,SAAS,iBAAiB,SAAiB,QAAkC;AAClF,SAAO,SAAS,GAAG,OAAO,YAAY,MAAM,KAAK;AACnD;AAGO,SAAS,8BACd,SACA,QACM;AACN,QAAM,SAA0C;AAAA,IAC9C;AAAA,IAAW;AAAA,IAAW;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAS;AAAA,EAC1D;AACA,QAAM,YAAY,OACf,OAAO,CAAC,UAAU,UAAU,MAAM,EAClC,IAAI,CAAC,UAAU,iBAAiB,SAAS,KAAK,CAAC;AAIlD,aAAW,OAAO,WAAW;AAC3B,UAAM,SAAS,gBAAgB,IAAI,GAAG;AACtC,QACE,qBAAqB,GAAG,EAAE,UAC1B,yBAAyB,GAAG,KAC5B,QAAQ,yBAAyB,QAChC,WAAW,OAAO,YAAY,cAAc,MAAM,IAAI,KAAK,sBAAsB,MAAM,IACxF;AACA,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,aAAW,OAAO,WAAW;AAC3B,wBAAoB,GAAG;AACvB,0BAAsB,GAAG;AACzB,qBAAiB,GAAG;AAAA,EACtB;AACF;AAEO,SAAS,mBACd,SACA,SACA,KACAC,aACA,aACA,SACA,kBACA,uBACA,QACe;AACf,gBAAc;AACd,MAAI,KAAK,+BAA+B;AAAA,IACtC;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAAA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,OAAO,MAAM,SAAS,SAAS;AAAA,IACnC;AAAA,IACA,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,IAC9B,KAAK,eAAe,EAAE,uBAAuB,OAAO,CAAC;AAAA,IACrD,OAAO,QAAQ,aAAa;AAAA,EAC9B,CAAC;AAED,QAAM,cAAc,IAAIC,cAAa;AAErC,QAAM,KAAoB;AAAA,IACxB;AAAA,IACA;AAAA,IACA,aAAa,eAAe;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,KAAK,IAAI;AAAA,IACpB;AAAA,IACA,SAAS,CAAC,GAAG,OAAO;AAAA,IACpB,iBAAiB,CAAC;AAAA,IAClB,mBAAmB;AAAA,EACrB;AAEA,QAAM,KAAK,gBAAgB,EAAE,OAAO,KAAK,OAAQ,CAAC;AAClD,KAAG,GAAG,QAAQ,CAAC,SAAiB;AAC9B,QAAI,6BAA6B,IAAI,IAAI,EAAG;AAC5C,iBAAa,IAAI,IAAI;AACrB,QAAI,YAAY,cAAc,MAAM,MAAM,GAAG;AAC3C,2BAAqB,IAAI,IAAI;AAC7B;AAAA,IACF;AACA,gBAAY,KAAK,QAAQ,IAAI;AAAA,EAC/B,CAAC;AACD,KAAG,GAAG,SAAS,MAAM;AACnB,eAAW,EAAE;AACb,gBAAY,KAAK,OAAO;AAAA,EAC1B,CAAC;AACD,4BAA0BD,WAAU;AACpC,kBAAgB,IAAIA,aAAY,EAAE;AAIlC,OAAK,GAAG,SAAS,CAAC,QAAQ;AACxB,QAAI,MAAM,wBAAwB,EAAE,YAAAA,aAAY,OAAO,IAAI,QAAQ,CAAC;AAAA,EACtE,CAAC;AAUD,OAAK,OAAO,GAAG,SAAS,CAAC,QAA+B;AACtD,QAAI,KAAK,8BAA8B;AAAA,MACrC,YAAAA;AAAA,MACA,MAAM,IAAI;AAAA,MACV,OAAO,IAAI;AAAA,IACb,CAAC;AACD,eAAW,EAAE;AAAA,EACf,CAAC;AAED,OAAK,GAAG,QAAQ,CAAC,MAAM,WAAW;AAChC,QAAI,KAAK,yBAAyB,EAAE,MAAM,QAAQ,YAAAA,YAAW,CAAC;AAC9D,SAAK,aAAa,MAAM;AACxB,QAAI,kBAAkB;AACpB,WAAK,OAAO,gBAAgB,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC9C;AACA,UAAM,iBAAiB,gBAAgB,IAAIA,WAAU,MAAM;AAC3D,QAAI,gBAAgB;AAClB,gCAA0BA,WAAU;AACpC,sBAAgB,OAAOA,WAAU;AAKjC,UAAI,qBAAqBA,WAAU,EAAE,SAAS,GAAG;AAC/C;AAAA,UACEA;AAAA,UACA,IAAI,MAAM,oBAAoB;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AACA,QAAI,kBAAkB,SAAS,KAAK,SAAS,MAAM;AACjD,UAAI,KAAK,+CAA+C;AAAA,QACtD;AAAA,QACA,YAAAA;AAAA,MACF,CAAC;AACD,qBAAe,OAAOA,WAAU;AAAA,IAClC;AAAA,EACF,CAAC;AAED,OAAK,QAAQ,GAAG,QAAQ,CAAC,SAAiB;AACxC,UAAM,SAAS,KAAK,SAAS;AAC7B,QAAI,MAAM,UAAU,EAAE,MAAM,OAAO,MAAM,GAAG,GAAG,EAAE,CAAC;AAClD,iBAAa,IAAI,MAAM;AAKvB,QACE,OAAO,SAAS,uBAAuB,KACtC,OAAO,SAAS,YAAY,MAC1B,OAAO,SAAS,gBAAgB,KAC/B,OAAO,SAAS,WAAW,KAC3B,OAAO,SAAS,SAAS,IAC7B;AACA,UAAI,gBAAgB,IAAIA,WAAU,MAAM,IAAI;AAC1C,YAAI,KAAK,6CAA6C;AAAA,UACpD,YAAAA;AAAA,UACA,OAAO,OAAO,MAAM,GAAG,GAAG;AAAA,QAC5B,CAAC;AACD,uBAAe,OAAOA,WAAU;AAAA,MAClC,OAAO;AACL,YAAI,MAAM,uDAAuD;AAAA,UAC/D,YAAAA;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAeO,SAAS,qBACdA,aACA,SACU;AACV,MAAI,QAAQ,SAAS,UAAU,KAAK,QAAQ,SAAS,cAAc,GAAG;AACpE,WAAO;AAAA,EACT;AACA,QAAM,MAAM,eAAe,IAAIA,WAAU;AACzC,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,CAAC,GAAG,SAAS,YAAY,GAAG;AACrC;AA8BO,SAAS,qBACdA,aACA,SACA,SACA,KACA,uBAC2B;AAC3B,QAAM,MAAM,gBAAgB,IAAIA,WAAU;AAC1C,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,kBAAkB,eAAe,GAAG;AAC1C,kBAAgB,OAAOA,WAAU;AAKjC,MAAI,KAAK,mBAAmB,MAAM;AAClC,MAAI;AACF,QAAI,KAAK,KAAK;AAAA,EAChB,QAAQ;AAAA,EAAC;AACT,QAAM,cAAc;AAAA,IAClB;AAAA,IACA,qBAAqBA,aAAY,IAAI,WAAW,OAAO;AAAA,IACvD;AAAA,IACAA;AAAA,IACA,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ;AAAA,IACA,IAAI;AAAA,EACN;AACA,cAAY,0BAA0B,IAAI;AAC1C,SAAO,IAAI;AACX,MAAI,gBAAiB,iBAAgB,WAAW;AAChD,SAAO;AACT;AAEO,SAAS,aAAa,MAgBhB;AACX,QAAM;AAAA,IACJ,YAAAA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,QAAM,OAAO;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI,OAAO;AACT,SAAK,KAAK,WAAW,KAAK;AAAA,EAC5B;AAEA,MAAI,gBAAgB;AAClB,SAAK,KAAK,qBAAqB,cAAc;AAAA,EAC/C;AAMA,MAAI,kBAAkB;AACpB,UAAM,YAAY,eAAe,IAAIA,WAAU;AAC/C,QAAI,aAAa,CAAC,gBAAgB,IAAIA,WAAU,GAAG;AACjD,WAAK,KAAK,YAAY,SAAS;AAAA,IACjC;AAAA,EACF;AAEA,MAAI,WAAW;AACb,UAAM,UAAU,MAAM,QAAQ,SAAS,IAAI,YAAY,CAAC,SAAS;AACjE,UAAM,WAAW,QAAQ,OAAO,CAAC,MAAM,OAAO,MAAM,YAAY,EAAE,SAAS,CAAC;AAC5E,QAAI,SAAS,SAAS,GAAG;AACvB,WAAK,KAAK,gBAAgB,GAAG,QAAQ;AAAA,IACvC;AAAA,EACF;AAEA,MAAI,iBAAiB;AACnB,SAAK,KAAK,qBAAqB;AAAA,EACjC;AAEA,MAAI,mBAAmB,gBAAgB,SAAS,GAAG;AACjD,SAAK,KAAK,qBAAqB,GAAG,eAAe;AAAA,EACnD;AAMA,MAAI,YAAY,oBAAoB,cAAc,IAAI,GAAG;AACvD,SAAK,KAAK,cAAc,QAAQ;AAAA,EAClC;AAKA,MAAI,mBAAmB,2BAA2B,cAAc,IAAI,GAAG;AACrE,SAAK,KAAK,sBAAsB,eAAe;AAAA,EACjD;AAEA,MAAI,wBAAwB;AAC1B,SAAK,KAAK,+BAA+B,sBAAsB;AAAA,EACjE;AACA,aAAW,OAAO,cAAc,CAAC,GAAG;AAClC,SAAK,KAAK,gBAAgB,GAAG;AAAA,EAC/B;AAOA,MAAI,YAAY,oBAAoB,cAAc,IAAI,GAAG;AACvD,SAAK,KAAK,cAAc,KAAK,UAAU,EAAE,UAAU,KAAK,CAAC,CAAC;AAAA,EAC5D;AAWA,MAAI,mBAAmB,mBAAmB,QAAQ;AAChD,SAAK,KAAK,gCAAgC;AAAA,EAC5C;AAEA,SAAO;AACT;AAMO,SAAS,WAAW,KAAa,SAAyB;AAC/D,SAAO,GAAG,GAAG,KAAK,OAAO;AAC3B;AASO,SAAS,mBAAmB,KAKjC;AACA,QAAM,QAAQ,IAAI,MAAM,IAAI;AAC5B,SAAO;AAAA,IACL,KAAK,MAAM,CAAC,KAAK;AAAA,IACjB,OAAO,MAAM,CAAC,KAAK;AAAA,IACnB,SAAS,MAAM,CAAC,KAAK;AAAA,IACrB,YAAY,MAAM,CAAC,MAAM;AAAA,EAC3B;AACF;AA0BO,SAAS,wBAAwB,MAAM,KAAK,IAAI,GAA4B;AACjF,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,KAAK,EAAE,KAAK,iBAAiB;AACvC,UAAM,YAAY,mBAAmB,GAAG;AACxC,UAAM,aAAc,GAAgC;AACpD,QAAI,KAAK;AAAA,MACP,YAAY;AAAA,MACZ,SAAS,GAAG,qBAAqB,UAAU;AAAA,MAC3C,OAAO,UAAU;AAAA,MACjB,YAAY,UAAU;AAAA,MACtB,KAAK,GAAG,KAAK;AAAA,MACb,UAAU,GAAG,iBAAiB;AAAA,MAC9B,OAAO,GAAG,cAAc,SAAY,SAAY,KAAK,IAAI,GAAG,MAAM,GAAG,SAAS;AAAA,MAC9E,QAAQ,GAAG;AAAA,MACX,UAAU,GAAG,YAAY,cAAc,MAAM,IAAI;AAAA,MACjD,UAAU,GAAG,aAAa;AAAA,MAC1B,YAAY,OAAO,eAAe,WAAW,aAAa;AAAA,IAC5D,CAAC;AAAA,EACH;AACA,SAAO;AACT;;;AMzhCO,IAAM,kBAAN,cAA8B,MAAM;AAAA,EACvB,OAAO;AAAA,EACzB,YAAY,UAAU,oFAAoF;AACxG,UAAM,OAAO;AAAA,EACf;AACF;AAEO,IAAM,0BACX;AAEK,IAAM,yBACX;AAEK,IAAM,6BACX;AAEK,IAAM,4BACX;AAEF,IAAM,eAAe;AACrB,IAAM,mBAAmB,KAAK;AAO9B,IAAM,iBAAiB;AAQvB,IAAM,oBAAoB;AAE1B,IAAM,iBAAiB;AAOvB,IAAM,qBAAqB;AAE3B,IAAM,wBAAwB,KAAK;AACnC,IAAM,qBAAqB;AAS3B,IAAM,iBAAiB,oBAAI,IAA2B;AAE/C,SAAS,2BACd,WACA,UACA,QACA,MAAM,KAAK,IAAI,GACT;AACN,aAAW,CAAC,IAAI,KAAK,KAAK,gBAAgB;AACxC,QAAI,MAAM,MAAM,KAAK,sBAAuB,gBAAe,OAAO,EAAE;AAAA,EACtE;AACA,iBAAe,OAAO,SAAS;AAC/B,SAAO,eAAe,QAAQ,oBAAoB;AAChD,UAAM,SAAS,eAAe,KAAK,EAAE,KAAK,EAAE;AAC5C,QAAI,WAAW,OAAW;AAC1B,mBAAe,OAAO,MAAM;AAAA,EAC9B;AACA,iBAAe,IAAI,WAAW,EAAE,UAAU,SAAS,KAAK,GAAG,QAAQ,IAAI,IAAI,CAAC;AAC9E;AAaO,SAAS,uBACd,WACA,UACA,MAAM,KAAK,IAAI,GAC0B;AACzC,QAAM,QAAQ,eAAe,IAAI,SAAS;AAC1C,MAAI,CAAC,MAAO,QAAO;AACnB,iBAAe,OAAO,SAAS;AAC/B,MAAI,CAAC,SAAS,KAAK,EAAE,WAAW,MAAM,QAAQ,KAAK,MAAM,MAAM,KAAK,sBAAuB,QAAO;AAClG,SAAO,MAAM;AACf;AAaO,IAAM,sBAAsB;AAO5B,IAAM,8BAA8B,CAAC,YAAY;AAcxD,SAAS,aAAa,MAAsB;AAC1C,SAAO,KACJ,MAAM,IAAI,EACV,IAAI,CAAC,SAAU,KAAK,KAAK,MAAM,KAAK,WAAM,UAAK,IAAI,EAAG,EACtD,KAAK,IAAI;AACd;AAEA,SAAS,QAAQ,UAA0B;AACzC,SAAO,SAAS,QAAQ,QAAQ,GAAG,EAAE,KAAK;AAC5C;AAEA,SAAS,YAAY,UAA0B;AAC7C,SAAO,GAAG,mBAAmB,IAAI,QAAQ,QAAQ,CAAC;AACpD;AAEO,SAAS,kBAAkB,UAAkB,QAAwB;AAC1E,SAAO;AAAA,EAAK,YAAY,QAAQ,CAAC;AAAA;AAAA,EAAQ,aAAa,OAAO,KAAK,CAAC,CAAC;AAAA;AACtE;AAOO,IAAM,yBAAyB;AAkB/B,SAAS,qBAAqB,UAA0B;AAC7D,SAAO;AAAA,EAAK,YAAY,QAAQ,CAAC;AAAA,SAAO,sBAAsB;AAAA;AAChE;AAUA,IAAM,aAAa,oBAAI,IAAuB;AAEvC,SAAS,kBAAkB,WAAmB,MAA6B;AAChF,aAAW,IAAI,WAAW,IAAI;AAC9B,SAAO,MAAM;AAGX,QAAI,WAAW,IAAI,SAAS,MAAM,KAAM,YAAW,OAAO,SAAS;AAAA,EACrE;AACF;AAEO,SAAS,gBAAgB,WAAmB,MAAuB;AACxE,QAAM,OAAO,WAAW,IAAI,SAAS;AACrC,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI;AACF,WAAO,KAAK,IAAI;AAAA,EAClB,SAAS,OAAO;AACd,QAAI,MAAM,wDAAwD,EAAE,WAAW,OAAO,UAAU,KAAK,EAAE,CAAC;AACxG,WAAO;AAAA,EACT;AACF;AAOO,SAAS,UAAU,QAA6B,MAAsB;AAG3E,MAAI;AACF,SAAK,QAAQ,KAAK,YAAY,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,UAAmB;AACjE,UAAI,MAAM,oBAAoB,EAAE,OAAO,UAAU,KAAK,EAAE,CAAC;AAAA,IAC3D,CAAC;AAAA,EACH,SAAS,OAAO;AACd,QAAI,MAAM,oBAAoB,EAAE,OAAO,UAAU,KAAK,EAAE,CAAC;AAAA,EAC3D;AACF;AAGO,SAAS,cAAc,QAAqD;AACjF,SAAO,OAAO,YAAY,cAAc,MAAM,IAAI;AACpD;AAQA,eAAsB,cACpB,QACA,WACsC;AACtC,QAAM,SAAS,QAAQ,SAAS;AAChC,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI;AACF,UAAM,SAAS,MAAM,OAAO,KAAK,OAAQ,OAAO;AAChD,UAAM,QAAQ,OAAO,OAAO,SAAS;AACrC,WAAO,SAAS,MAAM,SAAS,SAAS,SAAS;AAAA,EACnD,SAAS,OAAO;AACd,QAAI,MAAM,sCAAsC,EAAE,WAAW,OAAO,UAAU,KAAK,EAAE,CAAC;AACtF,WAAO;AAAA,EACT;AACF;AAOA,eAAsB,mBACpB,QACA,WACA,UAAyE,CAAC,GACxD;AAClB,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,UAAU,KAAK,IAAI;AACzB,aAAS;AACP,QAAI,QAAQ,OAAO,EAAG,QAAO;AAC7B,QAAK,MAAM,cAAc,QAAQ,SAAS,MAAO,OAAQ,QAAO;AAChE,QAAI,KAAK,IAAI,IAAI,WAAW,UAAW,QAAO;AAC9C,UAAM,IAAI,QAAQ,CAACE,aAAY,WAAWA,UAAS,MAAM,CAAC;AAAA,EAC5D;AACF;AAaA,eAAsB,mBACpB,QACA,WACA,MACA,UAA0B,CAAC,GACT;AAClB,QAAM,SAAS,QAAQ,gBAAgB;AACvC,QAAM,YAAY,QAAQ,gBAAgB;AAC1C,QAAM,UAAU,KAAK,IAAI;AACzB,aAAS;AACP,QAAI,gBAAgB,WAAW,IAAI,EAAG,QAAO;AAC7C,QAAI,KAAK,IAAI,IAAI,WAAW,UAAW,QAAO;AAC9C,QAAK,MAAM,cAAc,QAAQ,SAAS,MAAO,OAAQ,QAAO;AAChE,UAAM,IAAI,QAAQ,CAACA,aAAY,WAAWA,UAAS,MAAM,CAAC;AAAA,EAC5D;AACF;AAYA,eAAsB,oBACpB,QACA,WACA,UAA0B,CAAC,GACS;AACpC,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,UAAU,KAAK,IAAI;AACzB,aAAS;AACP,UAAM,SAAS,6BAA6B,SAAS;AACrD,QAAI,OAAQ,QAAO;AACnB,UAAM,OAAQ,MAAM,cAAc,QAAQ,SAAS,MAAO;AAC1D,UAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,QAAI,CAAC,QAAQ,YAAY,UAAU;AAEjC,UAAI,KAAK,mEAAmE,EAAE,WAAW,SAAS,CAAC;AACnG,aAAO;AAAA,IACT;AACA,QAAI,QAAQ,YAAY,aAAa;AAInC,UAAI,KAAK,gEAAgE,EAAE,WAAW,SAAS,CAAC;AAChG,aAAO;AAAA,IACT;AACA,UAAM,IAAI,QAAQ,CAACA,aAAY,WAAWA,UAAS,MAAM,CAAC;AAAA,EAC5D;AACF;AAQA,eAAsB,kBACpB,QACA,WACA,QACA,UAA0B,CAAC,GACT;AAClB,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,UAAU,KAAK,IAAI;AACzB,aAAS;AACP,UAAM,SAAS,MAAM,cAAc,QAAQ,SAAS;AACpD,QAAI,WAAW,OAAQ,QAAO;AAE9B,QAAI,WAAW,UAAW,QAAO,cAAc,MAAM;AACrD,QAAI,KAAK,IAAI,IAAI,WAAW,SAAU,QAAO;AAC7C,UAAM,IAAI,QAAQ,CAACA,aAAY,WAAWA,UAAS,MAAM,CAAC;AAAA,EAC5D;AACF;AAEA,SAAS,UAAU,OAAwB;AACzC,MAAI,iBAAiB,MAAO,QAAO,MAAM;AACzC,MAAI,SAAS,OAAO,UAAU,YAAY,aAAa,SAAS,OAAQ,MAA+B,YAAY,UAAU;AAC3H,WAAQ,MAA8B;AAAA,EACxC;AACA,SAAO,OAAO,KAAK;AACrB;AAEA,SAAS,WAAW,MAAuD;AACzE,SACE,SAAS,QACT,OAAO,SAAS,YACf,KAA4B,SAAS,UACtC,OAAQ,KAA4B,SAAS;AAEjD;AAQA,eAAsB,kBACpB,QACA,WACA,UACiC;AACjC,QAAM,WAAW,QAAQ,SAAS;AAClC,MAAI,CAAC,SAAU,QAAO,CAAC;AACvB,MAAI;AACF,UAAM,SAAS,MAAM,SAAS,KAAK,OAAQ,SAAS,EAAE,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;AAC/E,UAAM,SAA+C,CAAC;AACtD,eAAW,WAAW,OAAO,QAAQ,CAAC,GAAG;AACvC,YAAM,OAAO,QAAQ,MAAM;AAC3B,UAAI,SAAS,UAAU,SAAS,YAAa;AAC7C,aAAO,KAAK,EAAE,MAAM,UAAU,QAAQ,SAAS,CAAC,GAAG,OAAO,UAAU,EAAE,CAAC;AAAA,IACzE;AAGA,WAAO,KAAK,EAAE,MAAM,QAAQ,SAAS,QAAQ,QAAQ,GAAG,CAAC;AACzD,WAAO,2BAA2B,MAAM;AAAA,EAC1C,SAAS,OAAO;AACd,QAAI,MAAM,qCAAqC,EAAE,WAAW,OAAO,UAAU,KAAK,EAAE,CAAC;AACrF,WAAO,CAAC;AAAA,EACV;AACF;AAqBA,eAAsB,iBACpB,QACA,OACA,UAA0B,CAAC,GACZ;AACf,QAAM,WAAW,MAAM,UAAU,KAAK;AACtC,MAAI,CAAC,UAAU;AACb,cAAU,QAAQ,EAAE,OAAO,OAAO,SAAS,qBAAqB,SAAS,WAAW,UAAU,IAAM,CAAC;AACrG,UAAM,IAAI,gBAAgB,wBAAwB;AAAA,EACpD;AACA,QAAM,SAAS,MAAM,oBAAoB,QAAQ,MAAM,WAAW,OAAO;AACzE,QAAM,YAAY,QAAQ;AAC1B,MAAI,CAAC,UAAU,CAAC,WAAW;AAGzB,QAAI,OAAQ,KAAI,KAAK,+DAA+D,EAAE,WAAW,MAAM,UAAU,CAAC;AAClH;AAAA,EACF;AACA,MAAI,OAAO;AACX,MAAI,aAAa;AACjB,MAAI,sBAAsB,MAAY;AAAA,EAAC;AACvC,QAAM,kBAAkB,IAAI,QAAkB,CAACA,aAAY;AACzD,0BAAsB,MAAM;AAC1B,mBAAa;AACb,MAAAA,SAAQ,QAAQ;AAAA,IAClB;AAAA,EACF,CAAC;AACD,MAAI,sBAAsB,MAAM,GAAG;AAGjC,WAAO,MAAM,kBAAkB,QAAQ,MAAM,WAAW,QAAQ,OAAO;AACvE,QAAI,KAAK,oEAAoE,EAAE,WAAW,MAAM,WAAW,KAAK,CAAC;AAAA,EACnH,OAAO;AAIL,UAAM,WAAW,kBAAkB,QAAQ,MAAM,WAAW,QAAQ,OAAO;AAC3E,UAAM,UAAU,MAAM,kBAAkB,QAAQ,MAAM,WAAW,QAAQ;AACzE,UAAM,SAAS,oBAAoB,QAAQ,UAAU;AAAA,MACnD,YAAY,MAAM,iBAAiB,UAAU,OAAO;AAAA,MACpD,aAAa,UAAU;AAAA,MACvB,GAAI,QAAQ,SAAS,EAAE,QAAQ,IAAI,CAAC;AAAA,IACtC,CAAC;AAKD,WAAO,MAAM,MAAM,MAAS;AAC5B,+BAA2B,MAAM,WAAW,UAAU,MAAM;AAC5D,WAAO,MAAM;AACb,QAAI,KAAK,wCAAwC;AAAA,MAC/C,WAAW,MAAM;AAAA,MACjB;AAAA,MACA,gBAAgB,SAAS;AAAA,MACzB,SAAS,QAAQ;AAAA,IACnB,CAAC;AAKD,UAAM,QAAQ,OACV,mBAAmB,QAAQ,MAAM,WAAW,qBAAqB,QAAQ,GAAG,OAAO,EAAE,MAAM,MAAM,KAAK,IACtG,QAAQ,QAAQ,KAAK;AACzB,WAAO;AAAA,MACL,OAAO,WAAW;AAChB,YAAI,KAAK,6BAA6B,EAAE,WAAW,MAAM,WAAW,MAAM,gBAAgB,OAAO,SAAS,OAAO,CAAC;AAClH,YAAI,CAAC,QAAQ,OAAO,UAAW;AAI/B,cAAM;AACN,cAAM,SAAS,MAAM;AAAA,UACnB;AAAA,UACA,MAAM;AAAA,UACN,kBAAkB,UAAU,OAAO,QAAQ;AAAA,UAC3C;AAAA,QACF;AACA,YAAI,QAAQ;AAKV,cAAI,KAAK,6CAA6C,EAAE,WAAW,MAAM,UAAU,CAAC;AACpF,8BAAoB;AACpB;AAAA,QACF;AAIA,YAAI,KAAK,sEAAsE;AAAA,UAC7E,WAAW,MAAM;AAAA,QACnB,CAAC;AAAA,MACH;AAAA,MACA,CAAC,UAAmB;AAElB,YAAI,KAAK,uDAAuD;AAAA,UAC9D,WAAW,MAAM;AAAA,UACjB,OAAO,UAAU,KAAK;AAAA,QACxB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,KAAM;AACX,QAAM,UAAU,KAAK,IAAI;AACzB,QAAM,UAAU,MAAM,QAAQ,KAAK;AAAA,IACjC;AAAA,IACA,mBAAmB,QAAQ,MAAM,WAAW,EAAE,GAAG,SAAS,MAAM,MAAM,WAAW,CAAC,EAAE;AAAA,MAAK,CAAC,SACxF,OAAQ,SAAoB;AAAA,IAC9B;AAAA,EACF,CAAC;AACD,MAAI,YAAY,UAAU;AACxB,QAAI,KAAK,oEAAoE;AAAA,MAC3E,WAAW,MAAM;AAAA,MACjB,UAAU,KAAK,IAAI,IAAI;AAAA,IACzB,CAAC;AACD,UAAM,IAAI,gBAAgB,0BAA0B;AAAA,EACtD;AACA,MAAI,KAAK,8CAA8C;AAAA,IACrD,WAAW,MAAM;AAAA,IACjB,MAAM,YAAY;AAAA,IAClB,UAAU,KAAK,IAAI,IAAI;AAAA,EACzB,CAAC;AACD,MAAI,YAAY,WAAW;AACzB,cAAU,QAAQ,EAAE,OAAO,OAAO,SAAS,2BAA2B,SAAS,WAAW,UAAU,IAAM,CAAC;AAC3G,UAAM,IAAI,gBAAgB,yBAAyB;AAAA,EACrD;AACF;;;AC/oBA,SAAS,YAAAC,iBAAgB;AACzB,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,SAAS,aAAAC,kBAAiB;AAC1B,SAAS,qBAAqB;;;ACJ9B,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,YAAYC,SAAQ;AACpB,YAAYC,aAAY;AACxB;AAAA,EACE,SAAS;AAAA,EACT;AAAA,OAEK;AA4DP,IAAM,aAAa,CAAC,kBAAkB,iBAAiB,aAAa;AACpE,IAAM,qBAAqB,CAAC,iBAAiB,gBAAgB;AAE7D,SAAS,WAAW,GAAoB;AACtC,MAAI;AACF,WAAU,aAAS,CAAC,EAAE,OAAO;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,UAAU,GAAoB;AACrC,MAAI;AACF,WAAU,aAAS,CAAC,EAAE,YAAY;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAa,MAA8C;AAClE,MAAI;AACF,UAAM,MAAS,iBAAa,MAAM,MAAM;AACxC,UAAM,SAAuB,CAAC;AAC9B,UAAM,SAAS,WAAW,KAAK,QAAQ,EAAE,oBAAoB,KAAK,CAAC;AACnE,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,QAAQ,OAAO,CAAC;AACtB,YAAM,IAAI;AAAA,QACR,GAAG,oBAAoB,MAAM,KAAK,CAAC,cAAc,MAAM,MAAM;AAAA,MAC/D;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAS,GAAG;AACV,QAAI,KAAK,mCAAmC;AAAA,MAC1C;AAAA,MACA,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,IAClD,CAAC;AACD,WAAO;AAAA,EACT;AACF;AASA,SAAS,cAAc,GAA0C;AAC/D,SAAO,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ,CAAC;AAChE;AAEA,SAAS,UACP,QACA,QACyB;AACzB,QAAM,MAA+B,EAAE,GAAG,OAAO;AACjD,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC3C,QAAI,MAAM,OAAW;AACrB,UAAM,WAAW,IAAI,CAAC;AACtB,QAAI,cAAc,QAAQ,KAAK,cAAc,CAAC,GAAG;AAC/C,UAAI,CAAC,IAAI,UAAU,UAAU,CAAC;AAAA,IAChC,OAAO;AACL,UAAI,CAAC,IAAI;AAAA,IACX;AAAA,EACF;AACA,SAAO;AACT;AAQA,SAAS,OAAO,MAKH;AACX,QAAM,MAAgB,CAAC;AACvB,MAAI,UAAe,cAAQ,KAAK,KAAK;AACrC,SAAO,MAAM;AACX,eAAW,UAAU,KAAK,SAAS;AACjC,YAAM,YAAiB,WAAK,SAAS,MAAM;AAC3C,UAAI,KAAK,UAAU,SAAS,EAAG,KAAI,KAAK,SAAS;AAAA,IACnD;AACA,QAAI,KAAK,QAAQ,YAAiB,cAAQ,KAAK,IAAI,EAAG;AACtD,UAAM,SAAc,cAAQ,OAAO;AACnC,QAAI,WAAW,QAAS;AACxB,cAAU;AAAA,EACZ;AACA,SAAO;AACT;AAOA,SAAS,eAAe,KAAiC;AACvD,QAAM,WAAW,QAAQ,IAAI;AAC7B,MAAI,SAAU,QAAY,cAAQ,QAAQ;AAC1C,MAAI,UAAe,cAAQ,GAAG;AAC9B,SAAO,MAAM;AACX,UAAM,UAAe,WAAK,SAAS,MAAM;AACzC,QAAI;AACF,UAAO,eAAW,OAAO,EAAG,QAAO;AAAA,IACrC,QAAQ;AAAA,IAER;AACA,UAAM,SAAc,cAAQ,OAAO;AACnC,QAAI,WAAW,QAAS,QAAO;AAC/B,cAAU;AAAA,EACZ;AACF;AAEA,SAAS,kBAA0B;AACjC,QAAM,MAAM,QAAQ,IAAI,mBAAwB,WAAQ,YAAQ,GAAG,SAAS;AAC5E,SAAY,WAAK,KAAK,UAAU;AAClC;AAOA,SAAS,mBAA4C;AACnD,QAAM,MAAM,gBAAgB;AAC5B,MAAI,SAAkC,CAAC;AACvC,aAAW,QAAQ,WAAW,MAAM,EAAE,QAAQ,GAAG;AAE/C,UAAM,OAAY,WAAK,KAAK,IAAI;AAChC,QAAI,CAAC,WAAW,IAAI,EAAG;AACvB,UAAM,SAAS,aAAa,IAAI;AAChC,QAAI,OAAQ,UAAS,UAAU,QAAQ,MAAM;AAAA,EAC/C;AACA,SAAO;AACT;AAGA,SAAS,sBAAsB,KAAsC;AACnE,MAAI,SAAkC,CAAC;AACvC,aAAW,QAAQ,oBAAoB;AACrC,UAAM,OAAY,WAAK,KAAK,IAAI;AAChC,QAAI,CAAC,WAAW,IAAI,EAAG;AACvB,UAAM,SAAS,aAAa,IAAI;AAChC,QAAI,OAAQ,UAAS,UAAU,QAAQ,MAAM;AAAA,EAC/C;AACA,SAAO;AACT;AAOA,SAAS,gBAAgB,KAAa,UAA6B;AACjE,QAAM,OAAiB,CAAC;AACxB,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,OAAO,CAAC,MAAc;AAC1B,UAAM,MAAW,cAAQ,CAAC;AAC1B,QAAI,CAAC,KAAK,IAAI,GAAG,KAAK,UAAU,GAAG,GAAG;AACpC,WAAK,IAAI,GAAG;AACZ,WAAK,KAAK,GAAG;AAAA,IACf;AAAA,EACF;AAEA,aAAW,OAAO,OAAO;AAAA,IACvB,OAAO;AAAA,IACP,MAAM;AAAA,IACN,SAAS,CAAC,WAAW;AAAA,IACrB,WAAW;AAAA,EACb,CAAC,GAAG;AACF,SAAK,GAAG;AAAA,EACV;AAEA,QAAM,OAAU,YAAQ;AACxB,MAAI,MAAM;AACR,UAAM,UAAe,WAAK,MAAM,WAAW;AAC3C,QAAI,UAAU,OAAO,EAAG,MAAK,OAAO;AAAA,EACtC;AAEA,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,UAAU,UAAU,MAAM,EAAG,MAAK,MAAM;AAE5C,SAAO;AACT;AAgCA,SAAS,0BACP,QACwB;AACxB,QAAM,MAA8B,CAAC;AACrC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC3C,QAAI,OAAO,MAAM,SAAU;AAC3B,QAAI,CAAC,IAAI,EAAE,QAAQ,qCAAqC,CAAC,QAAQ,SAAS;AACxE,YAAM,WAAW,QAAQ,IAAI,IAAI;AACjC,aAAO,OAAO,aAAa,WAAW,WAAW;AAAA,IACnD,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,gBACP,MACA,MACgC;AAChC,MAAI,KAAK,YAAY,MAAO,QAAO;AAEnC,QAAM,OAAO,KAAK;AAClB,MAAI,SAAS,SAAS;AACpB,UAAM,MAAM,KAAK;AACjB,QAAI,CAAC,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW,GAAG;AAC3C,UAAI,KAAK,6CAA6C,EAAE,KAAK,CAAC;AAC9D,aAAO;AAAA,IACT;AACA,UAAM,MAA+B;AAAA,MACnC,MAAM;AAAA,MACN,SAAS,OAAO,IAAI,CAAC,CAAC;AAAA,IACxB;AACA,QAAI,IAAI,SAAS,EAAG,KAAI,OAAO,IAAI,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,OAAO,CAAC,CAAC;AAChE,QAAI,KAAK,eAAe,OAAO,KAAK,gBAAgB,UAAU;AAC5D,UAAI,MAAM;AAAA,QACR,KAAK;AAAA,MACP;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,MAAI,SAAS,UAAU;AACrB,QAAI,OAAO,KAAK,QAAQ,YAAY,CAAC,KAAK,KAAK;AAC7C,UAAI,KAAK,0CAA0C,EAAE,KAAK,CAAC;AAC3D,aAAO;AAAA,IACT;AACA,UAAM,MAA+B;AAAA,MACnC,MAAM;AAAA,MACN,KAAK,KAAK;AAAA,IACZ;AACA,QAAI,KAAK,WAAW,OAAO,KAAK,YAAY,UAAU;AACpD,UAAI,UAAU;AAAA,QACZ,KAAK;AAAA,MACP;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,MAAI,KAAK,yCAAyC;AAAA,IAChD;AAAA,IACA,MAAM,QAAQ;AAAA,EAChB,CAAC;AACD,SAAO;AACT;AAEA,SAAS,gBACP,QACgC;AAChC,QAAM,MAAM,OAAO;AACnB,MAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,EAAG,QAAO,CAAC;AACnE,SAAO;AACT;AAQA,SAAS,SACP,QACA,QACgC;AAChC,QAAM,MAAsC,EAAE,GAAG,OAAO;AACxD,aAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,UAAM,WAAW,IAAI,IAAI;AACzB,QAAI,YAAY,OAAO,aAAa,UAAU;AAC5C,UAAI,IAAI,IAAI;AAAA,QACV;AAAA,QACA;AAAA,MACF;AAAA,IACF,OAAO;AACL,UAAI,IAAI,IAAI;AAAA,IACd;AAAA,EACF;AACA,SAAO;AACT;AAsDO,SAAS,kBACd,KACA,eACA,gBACmB;AACnB,QAAM;AAAA,IACJ,SAAS;AAAA,IACT,oBAAoB;AAAA,IACpB;AAAA,EACF,IAAI,iBAAiB,KAAK,aAAa;AAIvC,QAAM,UAAmC,CAAC;AAC1C,QAAM,qBAA+B,CAAC;AACtC,aAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,QAAI,gBAAgB,IAAI,IAAI,EAAG;AAC/B,UAAM,aAAa,gBAAgB,MAAM,IAA+B;AACxE,QAAI,YAAY;AACd,cAAQ,IAAI,IAAI;AAChB,yBAAmB,KAAK,IAAI;AAAA,IAC9B;AAAA,EACF;AACA,SAAO,aAAa;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACH;AASO,SAAS,qBAAqB,KAAwC;AAC3E,QAAM,WAAW,eAAe,GAAG;AACnC,QAAM,SAAoC,CAAC;AAG3C,SAAO,KAAK,iBAAiB,CAAC;AAG9B,QAAM,iBAAiB,QAAQ,IAAI;AACnC,MAAI,kBAAkB,WAAW,cAAc,GAAG;AAChD,UAAM,SAAS,aAAa,cAAc;AAC1C,QAAI,OAAQ,QAAO,KAAK,MAAM;AAAA,EAChC;AAMA,QAAM,eAAe,OAAO;AAAA,IAC1B,OAAO;AAAA,IACP,MAAM;AAAA,IACN,SAAS;AAAA,IACT,WAAW;AAAA,EACb,CAAC;AACD,QAAM,cAAwB,CAAC;AAC/B,QAAM,kBAAkB,oBAAI,IAAY;AACxC,aAAW,KAAK,cAAc;AAC5B,UAAM,IAAS,cAAQ,CAAC;AACxB,QAAI,CAAC,gBAAgB,IAAI,CAAC,GAAG;AAC3B,sBAAgB,IAAI,CAAC;AACrB,kBAAY,KAAK,CAAC;AAAA,IACpB;AAAA,EACF;AACA,aAAW,OAAO,YAAY,MAAM,EAAE,QAAQ,GAAG;AAC/C,WAAO,KAAK,sBAAsB,GAAG,CAAC;AAAA,EACxC;AAOA,aAAW,OAAO,gBAAgB,KAAK,QAAQ,GAAG;AAChD,WAAO,KAAK,sBAAsB,GAAG,CAAC;AAAA,EACxC;AAEA,SAAO;AACT;AAGO,SAAS,yBAAyB,KAAsC;AAC7E,MAAI,SAAkC,CAAC;AACvC,aAAW,SAAS,qBAAqB,GAAG,EAAG,UAAS,UAAU,QAAQ,KAAK;AAC/E,SAAO;AACT;AASO,SAAS,iBACd,KACA,eACW;AACX,MAAI,SAAyC,CAAC;AAC9C,aAAW,SAAS,qBAAqB,GAAG,GAAG;AAC7C,aAAS,SAAS,QAAQ,gBAAgB,KAAK,CAAC;AAAA,EAClD;AAMA,MAAI,eAAe;AACjB,eAAW,QAAQ,OAAO,KAAK,MAAM,GAAG;AACtC,YAAM,SAAS,cAAc,IAAI;AACjC,UAAI,WAAW,OAAW;AAC1B,YAAM,WAAW,OAAO,IAAI;AAC5B,YAAM,OACJ,YAAY,OAAO,aAAa,WAC3B,WACD,CAAC;AACP,aAAO,IAAI,IAAI,EAAE,GAAG,MAAM,SAAS,WAAW,YAAY;AAAA,IAC5D;AAAA,EACF;AAKA,QAAM,qBAA+B,CAAC;AACtC,aAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,UAAM,UAAW,KAA+B;AAChD,QAAI,YAAY,MAAO;AACvB,uBAAmB,KAAK,IAAI;AAAA,EAC9B;AAIA,QAAM,aAAa,KAAK,UAAU,EAAE,YAAY,OAAO,GAAG,MAAM,CAAC;AACjE,QAAM,OACH,mBAAW,QAAQ,EACnB,OAAO,UAAU,EACjB,OAAO,KAAK,EACZ,MAAM,GAAG,EAAE;AAEd,SAAO,EAAE,SAAS,QAAQ,oBAAoB,KAAK;AACrD;AAGA,SAAS,aAAa,OAMA;AACpB,QAAM,EAAE,SAAS,oBAAoB,uBAAuB,MAAM,eAAe,IAC/E;AAEF,MAAI,OAAO,KAAK,OAAO,EAAE,WAAW,GAAG;AACrC,UAAM,4BACJ,kBACA,sBAAsB,SAAS,KAC/B,sBAAsB,MAAM,CAAC,SAAS,eAAe,IAAI,IAAI,CAAC;AAEhE,QAAI,CAAC,0BAA2B,QAAO;AAEvC,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA,aAAa,CAAC;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAEA,QAAM,OAAO,KAAK,UAAU,EAAE,YAAY,QAAQ,GAAG,MAAM,CAAC;AAU5D,QAAM,aACH,mBAAW,QAAQ,EACnB,OAAO,IAAI,EACX,OAAO,KAAK,EACZ,MAAM,GAAG,EAAE;AACd,QAAM,UAAe;AAAA,IACnB,aAAa;AAAA,IACb,OAAO,UAAU;AAAA,EACnB;AACA,MAAI;AACF,QAAI,CAAC,WAAW,OAAO,GAAG;AACxB,MAAG,kBAAc,SAAS,MAAM,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AAAA,IACnE;AAAA,EACF,SAAS,GAAG;AACV,QAAI,KAAK,sCAAsC;AAAA,MAC7C,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,IAClD,CAAC;AACD,WAAO;AAAA,EACT;AAEA,MAAI,KAAK,+BAA+B;AAAA,IACtC,QAAQ;AAAA,IACR;AAAA,IACA,SAAS;AAAA,IACT,UAAU,iBAAiB,MAAM,KAAK,cAAc,IAAI,CAAC;AAAA,EAC3D,CAAC;AACD,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,aAAa;AAAA,IACb;AAAA,EACF;AACF;;;ACpnBA,IAAI,iBAAwC;AAErC,SAAS,kBAAkB,QAAuB;AACvD,MAAI,UAAU,OAAO,WAAW,UAAU;AACxC,qBAAiB;AAAA,EACnB;AACF;AAMO,SAAS,oBAA6B;AAC3C,SAAO;AACT;AAaA,IAAI;AAEG,SAAS,4BAA4B,KAA+B;AACzE,6BAA2B;AAC7B;AAEO,SAAS,8BAAkD;AAChE,SAAO;AACT;AAEO,SAAS,kBAAkB,GAAyB;AACzD,SAAO,OAAO,MAAM,YAAY,EAAE,SAAS,KAAK,MAAM;AACxD;AA6BO,SAAS,oBACd,YACA,MACA,UACA,YACQ;AACR,MAAI,WAAY,QAAO;AACvB,MAAI,kBAAkB,UAAU,EAAG,QAAO;AAC1C,MAAI,kBAAkB,IAAI,EAAG,QAAO;AACpC,SAAO,YAAY;AACrB;AAUA,eAAsB,0BACpB,YACA,WACiB;AAEjB,MAAI,WAAY,QAAO;AACvB,QAAM,aAAa,YACf,MAAM,sBAAsB,SAAS,IACrC;AACJ,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,IAAI;AAAA,IACZ;AAAA,IACA;AAAA,EACF;AACF;AAUA,eAAsB,sBACpB,WAC6B;AAC7B,MAAI,CAAC,aAAa,cAAc,UAAW,QAAO;AAClD,QAAM,SAAS;AACf,MAAI,CAAC,QAAQ,SAAS,IAAK,QAAO;AAClC,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,QAAQ,IAAI,EAAE,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;AAChE,UAAM,OAAQ,IAA2B;AACzC,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,UAAM,MAAO,KAAiC;AAC9C,WAAO,kBAAkB,GAAG,IAAI,MAAM;AAAA,EACxC,SAAS,KAAK;AACZ,QAAI,KAAK,8CAA8C;AAAA,MACrD;AAAA,MACA,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,IACxD,CAAC;AACD,WAAO;AAAA,EACT;AACF;AAYA,eAAsB,qBACpB,WAC6B;AAC7B,MAAI,CAAC,aAAa,cAAc,UAAW,QAAO;AAClD,QAAM,SAAS;AACf,MAAI,CAAC,QAAQ,SAAS,IAAK,QAAO;AAClC,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,QAAQ,IAAI,EAAE,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;AAChE,UAAM,OAAQ,IAA2B;AACzC,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,UAAM,WAAY,KAAgC;AAClD,WAAO,OAAO,aAAa,YAAY,SAAS,SAAS,IACrD,WACA;AAAA,EACN,SAAS,KAAK;AACZ,QAAI,KAAK,2CAA2C;AAAA,MAClD;AAAA,MACA,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,IACxD,CAAC;AACD,WAAO;AAAA,EACT;AACF;AAUA,eAAsB,qBACpB,WAC0B;AAC1B,MAAI,CAAC,aAAa,cAAc,UAAW,QAAO;AAClD,QAAM,SAAS,gBAAgB,SAAS;AACxC,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,KAAK,eAAgB,OAAO;AACrD,UAAM,OAAQ,IAA2B;AACzC,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,UAAM,QAAS,KAAiC,SAAS;AACzD,QAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,UAAM,OAAQ,MAA6B;AAC3C,WAAO,SAAS,SAAS,SAAS;AAAA,EACpC,SAAS,KAAK;AACZ,QAAI,MAAM,0CAA0C;AAAA,MAClD;AAAA,MACA,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,IACxD,CAAC;AACD,WAAO;AAAA,EACT;AACF;AAWA,eAAsB,sBACpB,WACA,UAAkD,CAAC,GACzB;AAC1B,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,UAAU,KAAK,IAAI;AACzB,MAAI,OAAwB;AAC5B,aAAS;AACP,UAAM,QAAQ,MAAM,qBAAqB,SAAS;AAClD,QAAI,UAAU,OAAQ,QAAO;AAC7B,QAAI,UAAU,UAAW,QAAO;AAChC,QAAI,KAAK,IAAI,IAAI,WAAW,SAAU,QAAO;AAC7C,UAAM,IAAI,QAAQ,CAACC,aAAY,WAAWA,UAAS,MAAM,CAAC;AAAA,EAC5D;AACF;AAQA,eAAsB,sBAEpB;AACA,QAAM,SAAS;AACf,MAAI,CAAC,QAAQ,KAAK,OAAQ,QAAO;AACjC,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,IAAI,OAAO;AACpC,UAAM,OAAQ,IAA2B;AACzC,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,UAAM,MAAwB,CAAC;AAC/B,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,IAA+B,GAAG;AAC3E,UAAI,SAAS,OAAO,UAAU,UAAU;AACtC,cAAM,SAAU,MAA+B;AAC/C,YAAI,OAAO,WAAW,SAAU,KAAI,IAAI,IAAI;AAAA,MAC9C;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,KAAK,+CAA+C;AAAA,MACtD,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,IACxD,CAAC;AACD,WAAO;AAAA,EACT;AACF;AAgBA,eAAsB,sBACpB,UACA,OACA,WAC6C;AAC7C,QAAM,SAAS;AACf,MAAI,CAAC,QAAQ,MAAM,KAAM,QAAO;AAChC,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,KAAK,KAAK;AAAA,MACjC,OAAO,EAAE,UAAU,OAAO,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC,EAAG;AAAA,IAChE,CAAC;AACD,UAAM,OAAQ,IAA2B;AACzC,QAAI,CAAC,MAAM,QAAQ,IAAI,EAAG,QAAO;AACjC,UAAM,MAA8B,CAAC;AACrC,eAAW,SAAS,MAAmB;AACrC,UAAI,CAAC,SAAS,OAAO,UAAU,SAAU;AACzC,YAAM,IAAI;AACV,YAAM,KAAK,OAAO,EAAE,OAAO,WAAW,EAAE,KAAK;AAC7C,YAAM,cACJ,OAAO,EAAE,gBAAgB,WAAW,EAAE,cAAc;AACtD,YAAM,aACJ,EAAE,cAAc,OAAO,EAAE,eAAe,WACnC,EAAE,aACH,CAAC;AACP,UAAI,CAAC,GAAI;AACT,UAAI,KAAK,EAAE,IAAI,aAAa,WAAW,CAAC;AAAA,IAC1C;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,KAAK,sCAAsC;AAAA,MAC7C;AAAA,MACA;AAAA,MACA,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,IACxD,CAAC;AACD,WAAO;AAAA,EACT;AACF;;;AFrSA,IAAI;AAGG,SAAS,gBAAwB;AACtC,MAAI,oBAAqB,QAAO;AAChC,MAAI;AACF,UAAM,OAAY,cAAQ,cAAc,YAAY,GAAG,CAAC;AACxD,UAAM,MAAS,iBAAkB,WAAK,MAAM,MAAM,cAAc,GAAG,MAAM;AACzE,UAAM,UAAW,KAAK,MAAM,GAAG,EAA4B;AAC3D,0BAAsB,OAAO,YAAY,WAAW,UAAU;AAAA,EAChE,QAAQ;AACN,0BAAsB;AAAA,EACxB;AACA,SAAO;AACT;AAWO,SAAS,oBAAoB,OAAoC;AACtE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,MAAO,MAA4B;AACzC,MAAI,OAAO,OAAO,QAAQ,UAAU;AAClC,UAAM,UAAW,IAA8B;AAC/C,QAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,EAAG,QAAO;AAAA,EAChE;AACA,QAAM,SAAU,MAAgC;AAChD,MAAI,OAAO,WAAW,YAAY,OAAO,SAAS,EAAG,QAAO;AAC5D,SAAO;AACT;AAEA,IAAMC,iBAAgBC,WAAUC,SAAQ;AAExC,IAAI;AAWG,SAAS,sBACd,WAAmB,QAAQ,UACE;AAC7B,MAAI,qBAAsB,QAAO;AACjC,0BAAwB,YAAyC;AAC/D,QAAI,CAAM,eAAS,QAAQ,EAAE,YAAY,EAAE,SAAS,UAAU,GAAG;AAC/D,UAAI,MAAM,6DAA6D,EAAE,SAAS,CAAC;AACnF,aAAO;AAAA,IACT;AACA,QAAI;AACF,YAAM,EAAE,OAAO,IAAI,MAAMF,eAAc,UAAU,CAAC,WAAW,GAAG,EAAE,SAAS,IAAK,CAAC;AACjF,YAAM,QAAQ,mBAAmB,KAAK,OAAO,KAAK,CAAC;AACnD,aAAO,QAAQ,MAAM,CAAC,IAAI;AAAA,IAC5B,SAAS,KAAK;AACZ,UAAI,MAAM,iCAAiC;AAAA,QACzC;AAAA,QACA,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AACD,aAAO;AAAA,IACT;AAAA,EACF,GAAG;AACH,SAAO;AACT;AAcO,SAAS,iBACd,YACA,OAAe,QAAQ,IAAI,GAC3B,WAA+B,4BAA4B,GAClB;AACzC,MAAI,OAAO,eAAe,YAAY,WAAW,SAAS,GAAG;AAC3D,WAAO,EAAE,UAAU,YAAY,QAAQ,aAAa;AAAA,EACtD;AACA,MAAI,kBAAkB,IAAI,EAAG,QAAO,EAAE,UAAU,MAAM,QAAQ,UAAU;AACxE,MAAI,kBAAkB,QAAQ,EAAG,QAAO,EAAE,UAAU,UAAU,QAAQ,WAAW;AACjF,SAAO,EAAE,UAAU,MAAM,QAAQ,aAAa;AAChD;AAEA,SAAS,WAAW,OAA0B;AAC5C,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO,CAAC;AACnC,SAAO,MAAM,OAAO,CAAC,UAA2B,OAAO,UAAU,QAAQ;AAC3E;AAEA,SAAS,YACP,WACA,KACS;AACT,aAAW,SAAS,OAAO,OAAO,SAAS,GAAG;AAC5C,UAAM,QAAQ,OAAO,UAAU,GAAG;AAClC,QAAI,UAAU,OAAW,QAAO;AAAA,EAClC;AACA,SAAO;AACT;AAEO,SAAS,0BACd,WACA,iBACmE;AACnE,QAAM,WAAqB,CAAC;AAC5B,aAAW,SAAS,OAAO,OAAO,SAAS,GAAG;AAC5C,UAAM,UAAU,OAAO,SAAS;AAChC,QAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,EAAG,UAAS,KAAK,OAAO;AAAA,EAC9E;AAEA,QAAM,MAAM,iBAAiB,YAAY,WAAW,KAAK,CAAC;AAE1D,MAAI,aAAuB,CAAC;AAC5B,MAAI;AAIF,iBAAa,iBAAiB,IAAI,QAAQ,EAAE;AAAA,EAC9C,SAAS,KAAK;AACZ,QAAI,MAAM,iDAAiD;AAAA,MACzD,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,IACxD,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,QAAQ,cAAc;AAAA,IACtB,UAAU,mBAAmB,QAAQ,IAAI,oBAAoB;AAAA,IAC7D,eAAe,OAAO,YAAY,WAAW,SAAS,KAAK,QAAQ;AAAA,IACnE;AAAA,IACA,WAAW,OAAO,KAAK,SAAS;AAAA,IAChC;AAAA,IACA,YAAY,WAAW,YAAY,WAAW,YAAY,CAAC;AAAA,IAC3D;AAAA,IACA,sBACE,YAAY,WAAW,aAAa,MAAM,QAC1C,QAAQ,IAAI,sCAAsC;AAAA,IACpD,kBAAkB,YAAY,WAAW,kBAAkB,MAAM;AAAA,IACjE,sBAAsB;AAAA,MACpB,QAAQ,IAAI,qBAAqB,QAAQ,IAAI;AAAA,IAC/C;AAAA,EACF;AACF;AAEA,IAAI,SAAS;AACb,IAAI,gBAA0D,CAAC;AAC/D,IAAI;AAQG,SAAS,2BAAqE;AACnF,SAAO;AACT;AAEO,SAAS,2BAA+C;AAC7D,SAAO;AACT;AAOO,SAAS,sBACd,WACA,iBACM;AAIN,kBAAgB;AAChB,MAAI,gBAAiB,uBAAsB;AAC3C,MAAI,OAAQ;AACZ,WAAS;AACT,QAAM,YAAY;AAChB,QAAI;AAGF,YAAM,UACJ,mBAAmB,QAAQ,IAAI,oBAAqB,MAAM,sBAAsB;AAClF,YAAM,EAAE,eAAe,GAAG,KAAK,IAAI,0BAA0B,WAAW,OAAO;AAC/E,YAAM,MAAM,MAAM,iBAAiB,aAAa;AAChD,YAAM,cAAkC;AAAA,QACtC,GAAG;AAAA,QACH,WAAW,EAAE,MAAM,eAAe,SAAS,KAAK,OAAO,eAAe;AAAA,MACxE;AACA,UAAI,OAAO,4BAA4B,EAAE,GAAG,YAAY,CAAC;AAAA,IAC3D,SAAS,KAAK;AACZ,UAAI,MAAM,8BAA8B;AAAA,QACtC,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,EACF,GAAG;AACL;;;AGvNO,IAAM,iBAAiB;AAEvB,IAAM,6BACX;AAGK,IAAM,gBAAgB;AAE7B,SAASG,UAAS,OAAkD;AAClE,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAOA,IAAMC,yBAAwB;AAEvB,SAAS,0BAA0B,SAA2C;AACnF,MAAI;AACJ,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO;AAAA,EACT,WAAW,MAAM,QAAQ,OAAO,GAAG;AACjC,UAAM,QAAkB,CAAC;AACzB,eAAW,QAAQ,SAAS;AAC1B,UAAI,CAACD,UAAS,IAAI,KAAK,KAAK,SAAS,UAAU,OAAO,KAAK,SAAS,SAAU,QAAO;AACrF,YAAM,KAAK,KAAK,IAAI;AAAA,IACtB;AACA,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB,OAAO;AACL,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,IAAI,OAAO,KAAK,cAAc,uBAAuB,EAAE;AAAA,IACnE,KAAK,QAAQC,wBAAuB,EAAE,EAAE,KAAK;AAAA,EAC/C;AACA,SAAO,QAAQ,EAAE,OAAO,MAAM,CAAC,KAAK,IAAI,KAAK,EAAE,IAAI;AACrD;AAGO,SAAS,mBACd,QACyB;AACzB,QAAM,SAAS,OAAO,GAAG,EAAE;AAC3B,SAAO,QAAQ,SAAS,SAAS,0BAA0B,OAAO,OAAO,IAAI;AAC/E;AA+BA,SAAS,UAAU,IAAgC;AACjD,MAAI,OAAO,OAAW,QAAO;AAC7B,MAAI,KAAK,IAAM,QAAO,GAAG,KAAK,MAAM,EAAE,CAAC;AACvC,MAAI,KAAK,IAAQ,QAAO,IAAI,KAAK,KAAM,QAAQ,CAAC,CAAC;AACjD,QAAM,UAAU,KAAK,MAAM,KAAK,GAAM;AACtC,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AACnC,SAAO,GAAG,KAAK,MAAM,UAAU,EAAE,CAAC,KAAK,UAAU,EAAE;AACrD;AAEA,SAAS,KAAK,QAA0B;AACtC,SAAO,OAAO,SAAS,OAAO,KAAK,IAAI,IAAI;AAC7C;AAEA,SAAS,aAAa,MAA8B;AAClD,UAAQ,KAAK,QAAQ;AAAA,IACnB,KAAK;AACH,aAAO,GAAG,KAAK,IAAI;AAAA,IACrB,KAAK;AACH,aAAO,GAAG,KAAK,IAAI;AAAA,IACrB,KAAK;AACH,aAAO,yBAAyB,KAAK,KAAK;AAAA,IAC5C,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAOO,SAAS,mBAAmB,QAA8B;AAC/D,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,aAAa;AACxB,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,mBAAmB;AAC9B,QAAM,KAAK,WAAW;AACtB,QAAM,KAAK,cAAc,OAAO,MAAM,IAAI;AAC1C,QAAM,KAAK,gBAAgB,OAAO,QAAQ,IAAI;AAC9C,QAAM,KAAK,oBAAoB,OAAO,UAAU,IAAI,OAAO,OAAO,UAAU,OAAO,KAAK;AACxF,QAAM,KAAK,aAAa,OAAO,IAAI,QAAQ,OAAO,OAAO,IAAI,MAAM,KAAK;AACxE,QAAM,KAAK,iBAAiB,KAAK,OAAO,SAAS,CAAC,IAAI;AACtD,QAAM,KAAK,gBAAgB,KAAK,OAAO,QAAQ,CAAC,IAAI;AACpD,QAAM,KAAK,kBAAkB,KAAK,OAAO,UAAU,CAAC,IAAI;AACxD,QAAM,KAAK,6BAA6B,KAAK,OAAO,UAAU,CAAC,IAAI;AACnE,QAAM,KAAK,iBAAiB,OAAO,SAAS,IAAI;AAChD,QAAM,KAAK,wBAAwB,OAAO,gBAAgB,IAAI;AAC9D,QAAM,KAAK,iBAAiB,OAAO,SAAS,IAAI;AAChD,QAAM,KAAK,gCAAgC,OAAO,uBAAuB,QAAQ,IAAI,IAAI;AAEzF,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,6BAA6B;AACxC,QAAM,KAAK,EAAE;AACb,MAAI,OAAO,UAAU,WAAW,GAAG;AACjC,UAAM,KAAK,6DAA6D;AAAA,EAC1E,OAAO;AACL,UAAM,KAAK,sDAAsD;AACjE,UAAM,KAAK,2BAA2B;AACtC,eAAW,QAAQ,OAAO,WAAW;AACnC,YAAM;AAAA,QACJ,KAAK,KAAK,OAAO,GAAG,KAAK,aAAa,kBAAkB,EAAE,MAAM,KAAK,KAAK,MACxE,KAAK,OAAO,SACd,MAAM,KAAK,WAAW,QAAQ,IAAI,MAAM,UAAU,KAAK,KAAK,CAAC,MAAM,KAAK,UAAU,WAAW;AAAA,MAC/F;AAAA,IACF;AAAA,EACF;AAEA,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,yBAAyB;AACpC,QAAM,KAAK,EAAE;AACb,MAAI,OAAO,aAAa,WAAW,GAAG;AACpC,UAAM,KAAK,OAAO;AAAA,EACpB,OAAO;AACL,UAAM,KAAK,qCAAqC;AAChD,UAAM,KAAK,mBAAmB;AAC9B,eAAW,QAAQ,OAAO,cAAc;AAEtC,YAAM,WAAW,KAAK,aAAa,IAAI,UAAU,KAAK,UAAU,IAAI;AACpE,YAAM;AAAA,QACJ,KAAK,KAAK,QAAQ,QAAQ,KAAK,UAAU,QAAQ,UAAU,KAAK,KAAK,CAAC,MAAM,QAAQ;AAAA,MACtF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,mBAAmB;AAC9B,QAAM,KAAK,EAAE;AACb,MAAI,OAAO,aAAa,WAAW,GAAG;AACpC,UAAM,KAAK,eAAe;AAAA,EAC5B,OAAO;AACL,UAAM,KAAK,wCAAwC;AACnD,UAAM,KAAK,WAAW;AACtB,eAAWC,WAAU,OAAO,cAAc;AACxC,YAAM,KAAK,KAAKA,QAAO,GAAG,MAAM,aAAaA,QAAO,IAAI,CAAC,IAAI;AAAA,IAC/D;AAAA,EACF;AAEA,QAAM,SAAS,OAAO,UAAU,OAAO,CAAC,SAAS,KAAK,UAAU;AAChE,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,iBAAiB;AAC5B,UAAM,KAAK,EAAE;AACb,eAAW,QAAQ,QAAQ;AACzB,YAAM,KAAK,KAAK,KAAK,OAAO,KAAK;AACjC,YAAM,KAAK,EAAE;AACb,YAAM,KAAK,SAAS;AACpB,YAAM,KAAK,KAAK,WAAY,QAAQ,CAAC;AACrC,YAAM,KAAK,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAUA,eAAsB,eACpB,KACA,YAA0B,OAC1B,YAAY,KACa;AACzB,MAAI;AACJ,MAAI;AACF,gBAAY,IAAI,IAAI,GAAG,EAAE;AAAA,EAC3B,QAAQ;AACN,WAAO,EAAE,QAAQ,UAAU;AAAA,EAC7B;AACA,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAC5D,MAAI;AACF,UAAM,WAAW,MAAM,UAAU,KAAK;AAAA,MACpC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oBAAoB,MAAM,UAAU;AAAA,MAC/D,MAAM,KAAK,UAAU,EAAE,SAAS,OAAO,IAAI,GAAG,QAAQ,cAAc,QAAQ,CAAC,EAAE,CAAC;AAAA,MAChF,QAAQ,WAAW;AAAA,IACrB,CAAC;AAED,UAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACpC,WAAO,SAAS,WAAW,MACvB,EAAE,QAAQ,MAAM,MAAM,SAAS,OAAO,IACtC,EAAE,QAAQ,UAAU,MAAM,SAAS,OAAO;AAAA,EAChD,SAAS,OAAO;AACd,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAC9D;AAAA,EACF,UAAE;AACA,iBAAa,KAAK;AAAA,EACpB;AACF;AAUA,eAAsB,mBACpB,SACuB;AACvB,QAAM,YAAY,yBAAyB;AAC3C,QAAM,kBACJ,yBAAyB,KACzB,QAAQ,IAAI,oBACX,MAAM,sBAAsB,EAAE,MAAM,MAAM,MAAS;AACtD,QAAM,EAAE,eAAe,GAAG,KAAK,IAAI,0BAA0B,WAAW,eAAe;AACvF,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,MAAM,MAAM,iBAAiB,OAAO,EAAE,MAAM,MAAM,IAAI;AAE5D,QAAM,YAAY,wBAAwB;AAC1C,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,eAAiC,CAAC;AACxC,aAAW,QAAQ,WAAW;AAC5B,QAAI,CAAC,KAAK,YAAY,KAAK,IAAI,KAAK,QAAQ,EAAG;AAC/C,SAAK,IAAI,KAAK,QAAQ;AACtB,iBAAa,KAAK;AAAA,MAChB,KAAK,KAAK;AAAA,MACV,MAAM,MAAM,eAAe,KAAK,UAAU,QAAQ,aAAa,KAAK;AAAA,IACtE,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,QAAQ,KAAK;AAAA,IACb,UAAU,KAAK;AAAA,IACf,WAAW,EAAE,MAAM,SAAS,SAAS,KAAK,OAAO,eAAe;AAAA,IAChE,KAAK,KAAK;AAAA,IACV,WAAW,KAAK;AAAA,IAChB,UAAU,KAAK;AAAA,IACf,YAAY,KAAK;AAAA,IACjB,YAAY,KAAK;AAAA,IACjB,WAAW,QAAQ,eAAe,KAAK,uBAAuB,gBAAgB;AAAA,IAC9E,kBAAkB,KAAK;AAAA,IACvB,WAAW,QAAQ;AAAA,IACnB,sBAAsB,KAAK;AAAA,IAC3B;AAAA,IACA,cAAc,0BAA0B;AAAA,IACxC;AAAA,EACF;AACF;AAGA,eAAsB,kBAAkB,SAA+C;AACrF,MAAI;AACF,UAAM,SAAS,MAAM,mBAAmB,OAAO;AAC/C,QAAI,KAAK,6BAA6B;AAAA,MACpC,QAAQ,OAAO;AAAA,MACf,UAAU,OAAO;AAAA,MACjB,KAAK,OAAO;AAAA,MACZ,WAAW,OAAO,UAAU;AAAA,MAC5B,cAAc,OAAO,aAAa;AAAA,MAClC,cAAc,OAAO,aAAa,IAAI,CAACA,YAAWA,QAAO,KAAK,MAAM;AAAA,IACtE,CAAC;AACD,WAAO,mBAAmB,MAAM;AAAA,EAClC,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,QAAI,KAAK,iDAAiD,EAAE,OAAO,QAAQ,CAAC;AAC5E,WAAO,GAAG,aAAa;AAAA;AAAA,8BAAmC,OAAO;AAAA,EACnE;AACF;;;ACjTO,IAAM,oBAAoB;AAS1B,SAAS,iBAAiB,KAAqC;AACpE,QAAM,QAAQ,IAAI;AAClB,QAAM,QAAmB,CAAC;AAC1B,MAAI,OAAO,IAAI,mBAAmB,SAAU,OAAM,UAAU,IAAI;AAChE,MAAI,OAAO,IAAI,gBAAgB,SAAU,OAAM,aAAa,IAAI;AAChE,MAAI,OAAO,IAAI,oBAAoB,SAAU,OAAM,gBAAgB,IAAI;AACvE,MAAI,OAAO,IAAI,cAAc,SAAU,OAAM,WAAW,IAAI;AAC5D,MAAI,OAAO,OAAO,iBAAiB,SAAU,OAAM,cAAc,MAAM;AACvE,MAAI,OAAO,OAAO,kBAAkB,SAAU,OAAM,eAAe,MAAM;AACzE,MAAI,OAAO,OAAO,4BAA4B,UAAU;AACtD,UAAM,kBAAkB,MAAM;AAAA,EAChC;AACA,MAAI,OAAO,OAAO,gCAAgC,UAAU;AAC1D,UAAM,mBAAmB,MAAM;AAAA,EACjC;AACA,MAAI,IAAI,cAAc,OAAO,IAAI,eAAe,SAAU,OAAM,aAAa,IAAI;AACjF,MAAI,MAAM,QAAQ,IAAI,kBAAkB,EAAG,OAAM,oBAAoB,IAAI;AACzE,SAAO;AACT;AAGO,SAAS,WAAW,SAAyB;AAClD,MAAI,CAAC,OAAO,SAAS,OAAO,KAAK,UAAU,EAAG,QAAO;AACrD,SAAO,WAAW,IAAI,IAAI,QAAQ,QAAQ,CAAC,CAAC,KAAK,IAAI,QAAQ,QAAQ,CAAC,CAAC;AACzE;AAEO,SAAS,eAAe,YAA4B;AACzD,MAAI,CAAC,OAAO,SAAS,UAAU,KAAK,aAAa,EAAG,QAAO;AAC3D,MAAI,aAAa,IAAQ,QAAO,IAAI,aAAa,KAAM,QAAQ,CAAC,CAAC;AACjE,QAAM,eAAe,KAAK,MAAM,aAAa,GAAI;AACjD,SAAO,GAAG,KAAK,MAAM,eAAe,EAAE,CAAC,KAAK,eAAe,EAAE;AAC/D;AAEO,SAAS,aAAa,QAAwB;AACnD,MAAI,CAAC,OAAO,SAAS,MAAM,KAAK,SAAS,EAAG,QAAO;AACnD,MAAI,SAAS,IAAM,QAAO,OAAO,KAAK,MAAM,MAAM,CAAC;AACnD,MAAI,SAAS,IAAW,QAAO,IAAI,SAAS,KAAM,QAAQ,CAAC,CAAC;AAC5D,SAAO,IAAI,SAAS,KAAW,QAAQ,CAAC,CAAC;AAC3C;AAUO,SAAS,oBAAoB,OAAiC;AACnE,QAAM,QAAkB,CAAC;AACzB,MAAI,MAAM,YAAY,OAAW,OAAM,KAAK,WAAW,MAAM,OAAO,CAAC;AACrE,MAAI,MAAM,eAAe,OAAW,OAAM,KAAK,eAAe,MAAM,UAAU,CAAC;AAC/E,MAAI,MAAM,aAAa,QAAW;AAChC,UAAM,KAAK,GAAG,MAAM,QAAQ,QAAQ,MAAM,aAAa,IAAI,SAAS,OAAO,EAAE;AAAA,EAC/E;AACA,MAAI,MAAM,gBAAgB,OAAW,OAAM,KAAK,MAAM,aAAa,MAAM,WAAW,CAAC,EAAE;AACvF,MAAI,MAAM,iBAAiB,OAAW,OAAM,KAAK,OAAO,aAAa,MAAM,YAAY,CAAC,EAAE;AAC1F,MAAI,MAAM,gBAAiB,OAAM,KAAK,cAAc,aAAa,MAAM,eAAe,CAAC,EAAE;AACzF,MAAI,MAAM,iBAAkB,OAAM,KAAK,eAAe,aAAa,MAAM,gBAAgB,CAAC,EAAE;AAC5F,MAAI,MAAM,mBAAmB,QAAQ;AACnC,UAAM,QAAQ,MAAM,kBAAkB;AACtC,UAAM,KAAK,GAAG,KAAK,eAAe,UAAU,IAAI,WAAW,SAAS,EAAE;AAAA,EACxE;AACA,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO,GAAG,iBAAiB,IAAI,MAAM,KAAK,QAAK,CAAC;AAClD;AAGO,SAAS,qBAAqB,OAAiC;AACpE,QAAM,OAAO,oBAAoB,KAAK;AACtC,SAAO,SAAS,OAAO,OAAO;AAAA,EAAK,IAAI;AAAA;AACzC;AAGO,SAAS,oBAAoB,OAA2C;AAC7E,SAAO;AAAA,IACL,SAAS,MAAM,WAAW;AAAA,IAC1B,YAAY,MAAM,cAAc;AAAA,IAChC,eAAe,MAAM,iBAAiB;AAAA,IACtC,UAAU,MAAM,YAAY;AAAA,IAC5B,aAAa,MAAM,eAAe;AAAA,IAClC,cAAc,MAAM,gBAAgB;AAAA,IACpC,iBAAiB,MAAM,mBAAmB;AAAA,IAC1C,kBAAkB,MAAM,oBAAoB;AAAA,IAC5C,YAAY,MAAM,cAAc;AAAA,IAChC,mBAAmB,MAAM,mBAAmB,UAAU;AAAA,EACxD;AACF;;;ACrGA,IAAM,sBAAsB;AAAA,EAC1B;AAAA,EACA,GAAG;AAAA,EACH;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,aAAa,MAAoB;AACxC,MAAI,CAAC,QAAQ,KAAK,SAAS,UAAU,OAAO,KAAK,SAAS,SAAU,QAAO;AAC3E,QAAM,OAAO,KAAK,KAAK,UAAU;AACjC,SAAO,oBAAoB,KAAK,CAAC,WAAW,KAAK,WAAW,MAAM,CAAC;AACrE;AAEA,SAAS,iBAAiB,SAA2B;AACnD,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,QAAM,OAAO,QAAQ,OAAO,CAAC,SAAc,CAAC,aAAa,IAAI,CAAC;AAC9D,SAAO,KAAK,WAAW,QAAQ,SAAS,UAAU;AACpD;AAUO,SAAS,0BAA0B,QAAwB;AAIhE,WAAS,0BAA0B,MAAM;AACzC,MAAI,gBAAgB;AACpB,QAAM,OAAO,OAAO,OAAO,CAAC,YAAY;AACtC,QAAI,QAAQ,SAAS,QAAQ;AAC3B,sBACE,yBAAyB,QAAQ,OAAO,MAAM,QAC9C,0BAA0B,QAAQ,OAAO,MAAM;AACjD,aAAO,CAAC;AAAA,IACV;AACA,WAAO,QAAQ,SAAS,eAAe,CAAC;AAAA,EAC1C,CAAC;AACD,SAAO,KAAK;AAAA,IAAI,CAAC,YACf,QAAQ,SAAS,cAAe,EAAE,GAAG,SAAS,SAAS,iBAAiB,QAAQ,OAAO,EAAE,IAAuB;AAAA,EAClH;AACF;AAgBA,IAAM,qBACJ;AAGK,SAAS,2BAA2B,MAAsB;AAC/D,MAAI,CAAC,KAAK,SAAS,sBAAsB,EAAG,QAAO;AACnD,SAAO,KAAK,QAAQ,oBAAoB,EAAE,EAAE,QAAQ,WAAW,MAAM,EAAE,KAAK;AAC9E;AAaO,SAAS,4BAA4B,SAIhC;AACV,MAAI,QAAQ,YAAY,KAAM,QAAO;AACrC,QAAM,gBAAgB,CAACC,WACpBA,SAAQ,CAAC,GAAG,KAAK,CAAC,SAAS,OAAO,IAAI,EAAE,KAAK,EAAE,YAAY,MAAM,UAAU;AAC9E,SAAO,CAAC,cAAc,QAAQ,UAAU,KAAK,CAAC,cAAc,QAAQ,kBAAkB;AACxF;AAWO,SAAS,sBAAsB,QAGpC;AACA,MAAI,UAAU;AACd,QAAM,UAAU,CAAC,UACd,KAAK,MAAM,kBAAkB,KAAK,CAAC,GAAG;AAEzC,QAAM,MAAM,OAAO,IAAI,CAAC,YAAY;AAClC,QAAI,QAAQ,SAAS,UAAU,QAAQ,SAAS,YAAa,QAAO;AAIpE,QAAI,CAAC,MAAM,QAAQ,QAAQ,OAAO,EAAG,QAAO;AAE5C,QAAI,UAAU;AACd,UAAM,QAAS,QAAQ,QAAkB,IAAI,CAAC,SAAS;AACrD,UAAI,CAAC,QAAQ,KAAK,SAAS,UAAU,OAAO,KAAK,SAAS,SAAU,QAAO;AAC3E,YAAM,OAAO,QAAQ,KAAK,IAAI;AAC9B,UAAI,SAAS,EAAG,QAAO;AACvB,iBAAW;AACX,gBAAU;AACV,aAAO,EAAE,GAAG,MAAM,MAAM,2BAA2B,KAAK,IAAI,EAAE;AAAA,IAChE,CAAC;AACD,WAAO,UAAW,EAAE,GAAG,SAAS,SAAS,MAAM,IAAuB;AAAA,EACxE,CAAC;AAED,SAAO,UAAU,IAAI,EAAE,QAAQ,KAAK,QAAQ,IAAI,EAAE,QAAQ,SAAS,EAAE;AACvE;AAEA,IAAM,wBAAwB,oBAAI,IAAI;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,aAAa,MAAuB;AAC3C,QAAM,MAAe,KAAK,SAAS,KAAK,QAAQ,KAAK,OAAO,KAAK,QAAQ;AACzE,MAAI,CAAC,KAAK;AACR,QAAI,KAAK,kCAAkC;AAC3C,WAAO;AAAA,EACT;AAEA,MAAI,oBAA4B,KAAK,aAAa,KAAK,YAAY,KAAK,QAAQ;AAChF,MAAI,SAAwB;AAE5B,MAAI,OAAO,QAAQ,UAAU;AAC3B,QAAI,IAAI,WAAW,OAAO,GAAG;AAC3B,YAAM,QAAQ,+CAA+C,KAAK,GAAG;AACrE,UAAI,CAAC,OAAO;AACV,YAAI,KAAK,wCAAwC;AACjD,eAAO;AAAA,MACT;AACA,0BAAoB,qBAAqB,MAAM,CAAC;AAChD,eAAS,MAAM,CAAC;AAAA,IAClB,WAAW,gBAAgB,KAAK,GAAG,GAAG;AACpC,UAAI,KAAK,6DAA6D;AACtE,aAAO;AAAA,IACT,OAAO;AACL,eAAS;AAAA,IACX;AAAA,EACF,WAAW,eAAe,KAAK;AAC7B,QAAI,KAAK,6DAA6D;AACtE,WAAO;AAAA,EACT,WAAW,eAAe,cAAc,OAAO,SAAS,GAAG,GAAG;AAC5D,aAAS,OAAO,KAAK,GAAiB,EAAE,SAAS,QAAQ;AAAA,EAC3D,OAAO;AACL,QAAI,KAAK,mCAAmC,EAAE,UAAU,OAAO,IAAI,CAAC;AACpE,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,qBAAqB,CAAC,sBAAsB,IAAI,iBAAiB,GAAG;AACvE,QAAI,KAAK,2DAA2D;AAAA,MAClE,WAAW;AAAA,IACb,CAAC;AACD,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,EAAE,MAAM,UAAU,YAAY,mBAAmB,MAAM,OAAO;AAAA,EACxE;AACF;AAEA,SAAS,kBAAkB,MAAmB;AAC5C,QAAM,QAAQ,KAAK,UAAU,KAAK;AAElC,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B;AAEA,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AAAA,IACL,KAAK;AACH,aAAO,OAAO,MAAM,KAAK;AAAA,IAC3B,KAAK;AAAA,IACL,KAAK;AACH,aAAO,KAAK,UAAU,MAAM,KAAK;AAAA,IACnC,KAAK;AACH,aAAO,MAAM,SAAS,qBAAqB,MAAM,MAAM,KAAK;AAAA,IAC9D,KAAK;AACH,aAAO,MAAM,QAAQ,MAAM,KAAK,IAC5B,MAAM,MACH,IAAI,CAAC,SAAc;AAClB,YAAI,MAAM,SAAS,OAAQ,QAAO,KAAK;AACvC,eAAO,KAAK,UAAU,IAAI;AAAA,MAC5B,CAAC,EACA,KAAK,IAAI,IACZ,KAAK,UAAU,MAAM,KAAK;AAAA,IAChC;AACE,aAAO,KAAK,UAAU,KAAK;AAAA,EAC/B;AACF;AAMA,IAAM,oBAAoB;AAC1B,IAAM,wBAAwB;AAC9B,IAAM,uBAAuB;AAE7B,SAAS,eAAe,MAAc,KAAqB;AACzD,MAAI,KAAK,UAAU,IAAK,QAAO;AAC/B,SAAO,GAAG,KAAK,MAAM,GAAG,GAAG,CAAC;AAAA,mBAAiB,KAAK,SAAS,GAAG;AAChE;AAEA,SAAS,gBAAgB,OAAwB;AAC/C,MAAI;AACJ,MAAI;AACF,UAAM,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;AAAA,EAChE,QAAQ;AACN,UAAM,OAAO,KAAK;AAAA,EACpB;AACA,SAAO,eAAe,KAAK,oBAAoB;AACjD;AAEA,SAAS,kCACP,KAC2C;AAC3C,QAAM,QAAkB,CAAC;AACzB,MAAI,kBAAkB;AAEtB,MAAI,OAAO,IAAI,YAAY,UAAU;AACnC,WAAO,EAAE,MAAM,IAAI,SAAS,iBAAiB,EAAE;AAAA,EACjD;AAEA,MAAI,CAAC,MAAM,QAAQ,IAAI,OAAO,GAAG;AAC/B,WAAO,EAAE,MAAM,IAAI,iBAAiB,EAAE;AAAA,EACxC;AAEA,aAAW,QAAQ,IAAI,SAAkB;AACvC,QAAI,CAAC,KAAM;AACX,YAAQ,KAAK,MAAM;AAAA,MACjB,KAAK;AACH,YAAI,KAAK,KAAM,OAAM,KAAK,KAAK,IAAI;AACnC;AAAA,MACF,KAAK;AACH,cAAM;AAAA,UACJ,aAAa,KAAK,YAAY,SAAS,IAAI,gBAAgB,KAAK,KAAK,CAAC;AAAA,QACxE;AACA;AAAA,MACF,KAAK;AACH;AACA,cAAM;AAAA,UACJ,gBAAgB,KAAK,YAAY,KAAK,cAAc,SAAS;AAAA,EAAM;AAAA,YACjE,kBAAkB,IAAI;AAAA,YACtB;AAAA,UACF,CAAC;AAAA,QACH;AACA;AAAA,MACF,KAAK;AACH,cAAM;AAAA,UACJ,WAAW,KAAK,aAAa,KAAK,YAAY,SAAS;AAAA,QACzD;AACA;AAAA,MACF,KAAK;AACH,cAAM;AAAA,UACJ,UAAU,KAAK,aAAa,KAAK,YAAY,SAAS;AAAA,QACxD;AACA;AAAA,MACF,KAAK;AAGH;AAAA,IACJ;AAAA,EACF;AAEA,SAAO,EAAE,MAAM,MAAM,KAAK,IAAI,GAAG,gBAAgB;AACnD;AAiBO,SAAS,2BACd,QACA,OAAkD,CAAC,GACpC;AACf,QAAM,OAAO,KAAK,QAAQ;AAC1B,WAAS,0BAA0B,MAAM;AAEzC,MAAI,SAAS,cAAc;AACzB,WAAO,uBAAuB,MAAM;AAAA,EACtC;AAOA,QAAM,uBAAuB,OAAO;AAAA,IAClC,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,SAAS,eAAe,EAAE,SAAS;AAAA,EACnE;AAEA,MAAI,qBAAqB,UAAU,GAAG;AACpC,WAAO;AAAA,EACT;AAEA,QAAM,eAAyB,CAAC;AAEhC,WAAS,IAAI,GAAG,IAAI,qBAAqB,SAAS,GAAG,KAAK;AACxD,UAAM,MAAM,qBAAqB,CAAC;AAClC,UAAM,OACJ,IAAI,SAAS,SAAS,SAAS,IAAI,SAAS,cAAc,cAAc;AAM1E,UAAM,EAAE,KAAK,IAAI,kCAAkC,GAAG;AAEtD,QAAI,KAAK,KAAK,GAAG;AACf,YAAM,YACJ,KAAK,SAAS,MAAO,KAAK,MAAM,GAAG,GAAI,IAAI,QAAQ;AACrD,mBAAa,KAAK,GAAG,IAAI,KAAK,SAAS,EAAE;AAAA,IAC3C;AAAA,EACF;AAEA,MAAI,aAAa,WAAW,GAAG;AAC7B,WAAO;AAAA,EACT;AAEA,SAAO,aAAa,KAAK,MAAM;AACjC;AAEA,SAAS,uBAAuB,QAA+B;AAK7D,QAAM,UAAoB,CAAC;AAC3B,MAAI,QAAQ;AACZ,MAAI,mBAAmB;AACvB,MAAI,gBAAgB;AAMpB,QAAM,MAAM,OAAO,SAAS,KAAK,OAAO,OAAO,SAAS,CAAC,EAAE,SAAS,SAChE,OAAO,SAAS,IAChB,OAAO;AAEX,WAAS,IAAI,MAAM,GAAG,KAAK,GAAG,KAAK;AACjC,UAAM,MAAM,OAAO,CAAC;AACpB,UAAM,YACJ,IAAI,SAAS,SACT,SACA,IAAI,SAAS,cACX,cACA,IAAI,SAAS,SACX,SACA,IAAI;AAEd,UAAM,EAAE,MAAM,gBAAgB,IAAI,kCAAkC,GAAG;AACvE,QAAI,CAAC,KAAK,KAAK,EAAG;AAElB,UAAM,QAAQ,GAAG,SAAS,KAAK,IAAI;AACnC,QAAI,QAAQ,MAAM,SAAS,mBAAmB;AAC5C,sBAAgB,IAAI;AACpB;AAAA,IACF;AACA,YAAQ,KAAK,KAAK;AAClB,aAAS,MAAM,SAAS;AACxB,wBAAoB;AAAA,EACtB;AAEA,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,UAAQ,QAAQ;AAChB,MAAI,KAAK,4BAA4B;AAAA,IACnC,SAAS,QAAQ;AAAA,IACjB,OAAO;AAAA,IACP,aAAa;AAAA,IACb,qBAAqB;AAAA,EACvB,CAAC;AAED,SAAO,QAAQ,KAAK,MAAM;AAC5B;AAcO,SAAS,qBACd,QACA,wBAAiC,OACjC,OAII,CAAC,GACG;AACR,QAAM,iBAAiB,KAAK,mBAAmB;AAC/C,QAAM,iBAAiB,KAAK;AAC5B,QAAM,UAAiB,CAAC;AAOxB,WAAS,0BAA0B,MAAM;AAKzC,MAAI,KAAK,uBAAuB;AAC9B,UAAM,WAAW,sBAAsB,MAAM;AAC7C,QAAI,SAAS,UAAU,GAAG;AACxB,UAAI,KAAK,4CAA4C;AAAA,QACnD,QAAQ,SAAS;AAAA,MACnB,CAAC;AACD,eAAS,SAAS;AAAA,IACpB;AAAA,EACF;AAgBA,QAAM,iBAAiB,CAAC,SAAoB;AAC1C,UAAM,KAAK,KAAK;AAChB,UAAM,OAAO,kBAAkB,IAAI;AACnC,QAAI,CAAC,kBAAkB,eAAe,IAAI,EAAE,GAAG;AAC7C,cAAQ,KAAK,EAAE,MAAM,eAAe,aAAa,IAAI,SAAS,KAAK,CAAC;AACpE;AAAA,IACF;AACA,QAAI,KAAK,+CAA+C;AAAA,MACtD,YAAY;AAAA,MACZ,UAAU,KAAK;AAAA,MACf,OAAO,KAAK;AAAA,IACd,CAAC;AACD,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,MAAM,+BAA+B,KAAK,YAAY,SAAS;AAAA,EAAO,IAAI;AAAA;AAAA,IAC5E,CAAC;AAAA,EACH;AAEA,MAAI,gBAAgB;AAClB,UAAM,aAAa,2BAA2B,QAAQ;AAAA,MACpD,MAAM;AAAA,IACR,CAAC;AACD,QAAI,YAAY;AACd,UAAI,KAAK,mCAAmC;AAAA,QAC1C,eAAe,WAAW;AAAA,MAC5B,CAAC;AACD,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,MAAM;AAAA,EACZ,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMN,CAAC;AAAA,IACH;AAAA,EACF,WAAW,uBAAuB;AAChC,UAAM,iBAAiB,2BAA2B,MAAM;AACxD,QAAI,gBAAgB;AAClB,UAAI,KAAK,0CAA0C;AAAA,QACjD,eAAe,eAAe;AAAA,MAChC,CAAC;AACD,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,MAAM;AAAA;AAAA;AAAA,EAGZ,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOV,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,WAA0B,CAAC;AACjC,WAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,QAAI,OAAO,CAAC,EAAE,SAAS,YAAa;AACpC,aAAS,QAAQ,OAAO,CAAC,CAAC;AAAA,EAC5B;AAEA,aAAW,OAAO,UAAU;AAC1B,QAAI,IAAI,SAAS,QAAQ;AACvB,UAAI,yBAAyB,IAAI,OAAO,MAAM,KAAM;AACpD,UAAI,OAAO,IAAI,YAAY,UAAU;AACnC,cAAMC,OAAM,IAAI;AAChB,YAAIA,KAAI,KAAK,GAAG;AACd,kBAAQ,KAAK,EAAE,MAAM,QAAQ,MAAMA,KAAI,CAAC;AAAA,QAC1C;AAAA,MACF,WAAW,MAAM,QAAQ,IAAI,OAAO,GAAG;AACrC,mBAAW,QAAQ,IAAI,SAAkB;AACvC,cAAI,KAAK,SAAS,QAAQ;AACxB,gBAAI,KAAK,QAAQ,KAAK,KAAK,KAAK,GAAG;AACjC,sBAAQ,KAAK,EAAE,MAAM,QAAQ,MAAM,KAAK,KAAK,CAAC;AAAA,YAChD;AAAA,UACF,WAAW,KAAK,SAAS,UAAU,KAAK,SAAS,SAAS;AACxD,kBAAM,QAAQ,aAAa,IAAI;AAC/B,gBAAI,OAAO;AACT,sBAAQ,KAAK,KAAK;AAAA,YACpB,OAAO;AACL,kBAAI,MAAM,+BAA+B;AAAA,gBACvC,WAAW,KAAK;AAAA,cAClB,CAAC;AAAA,YACH;AAAA,UACF,WAAW,KAAK,SAAS,eAAe;AACtC,2BAAe,IAAI;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAAA,IACF,WAAW,IAAI,SAAS,QAAQ;AAK9B,UAAI,MAAM,QAAQ,IAAI,OAAO,GAAG;AAC9B,mBAAW,QAAQ,IAAI,SAAkB;AACvC,cAAI,MAAM,SAAS,eAAe;AAChC,2BAAe,IAAI;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAQ,WAAW,GAAG;AAOxB,QAAI,KAAK,qDAAqD;AAC9D,WAAO,KAAK,UAAU;AAAA,MACpB,MAAM;AAAA,MACN,SAAS;AAAA,QACP,MAAM;AAAA,QACN,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,UAAU,CAAC;AAAA,MAC7C;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,KAAK,UAAU;AAAA,IACpB,MAAM;AAAA,IACN,SAAS;AAAA,MACP,MAAM;AAAA,MACN;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;ACpmBA,SAAS,UAAU,eAAe;AAClC,OAAOC,WAAU;;;AClCjB,IAAM,cAAc;AACpB,IAAM,MAAM;AAEZ,IAAM,oBAA6D;AAAA,EACjE,KAAK,EAAE,iBAAiB,MAAM;AAAA,EAC9B,QAAQ,EAAE,iBAAiB,SAAS;AAAA,EACpC,MAAM,EAAE,iBAAiB,OAAO;AAAA,EAChC,OAAO,EAAE,iBAAiB,QAAQ;AAAA,EAClC,KAAK,EAAE,iBAAiB,MAAM;AAChC;AAEA,IAAM,mBAAmB;AAAA,EACvB,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,OAAO,EAAE,MAAM,MAAM,OAAO,OAAO,OAAO,MAAM,OAAO,OAAO,KAAK,MAAM;AAAA,EACzE,QAAQ,EAAE,MAAM,MAAM,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK,MAAM;AAAA,EAC3E,aAAa;AACf;AAEA,SAAS,YAAY,MAiBH;AAChB,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,YAAY;AAAA,IACZ,KAAK,EAAE,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI;AAAA,IACtC,MAAM,GAAG,KAAK,IAAI,KAAK,KAAK,UAAU;AAAA,IACtC,QAAQ,KAAK;AAAA,IACb,cAAc,EAAE,GAAG,kBAAkB,WAAW,KAAK,UAAU;AAAA,IAC/D,MAAM;AAAA,MACJ,OAAO,KAAK,KAAK;AAAA,MACjB,QAAQ,KAAK,KAAK;AAAA,MAClB,OAAO,EAAE,MAAM,KAAK,KAAK,WAAW,OAAO,KAAK,KAAK,WAAW;AAAA,IAClE;AAAA,IACA,OAAO,EAAE,SAAS,KAAK,SAAS,QAAQ,KAAK,OAAO;AAAA,IACpD,QAAQ,KAAK,UAAU;AAAA,IACvB,SAAS,CAAC;AAAA,IACV,SAAS,CAAC;AAAA,IACV,cAAc,KAAK;AAAA,IACnB,UAAU,KAAK,YAAY,oBAAoB;AAAA,EACjD;AACF;AAkBA,IAAM,YAAY,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,KAAK,YAAY,KAAK;AAC1E,IAAM,aAAa,EAAE,OAAO,GAAG,QAAQ,IAAI,WAAW,KAAK,YAAY,KAAK;AAG5E,IAAM,WAAW,EAAE,OAAO,GAAG,QAAQ,IAAI,WAAW,KAAK,YAAY,KAAK;AAI1E,IAAM,YAAY,EAAE,OAAO,IAAI,QAAQ,IAAI,WAAW,GAAG,YAAY,KAAK;AAG1E,IAAM,cAAc,EAAE,OAAO,IAAI,QAAQ,IAAI,WAAW,MAAM,YAAY,KAAK;AAQ/E,IAAM,eAAe,EAAE,OAAO,IAAI,QAAQ,IAAI,WAAW,GAAG,YAAY,KAAK;AAS7E,IAAM,aAAa,EAAE,OAAO,GAAG,QAAQ,IAAI,WAAW,KAAK,YAAY,EAAE;AACzE,IAAM,iBAAiB,EAAE,OAAO,GAAG,QAAQ,IAAI,WAAW,KAAK,YAAY,GAAG;AAOvE,SAAS,cAAc,OAA+C;AAC3E,QAAM,YAAsB,CAAC;AAC7B,QAAM,aAAuB,CAAC;AAC9B,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,aAAa,KAAK,GAAG;AAC7D,QAAI,EAAG,WAAU,KAAK,CAAC;AAAA,EACzB;AACA,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,aAAa,MAAM,GAAG;AAC9D,QAAI,EAAG,YAAW,KAAK,CAAC;AAAA,EAC1B;AAEA,SAAO;AAAA,IACL,IAAI,MAAM,IAAI;AAAA,IACd,MAAM,MAAM;AAAA,IACZ,QAAQ,MAAM;AAAA,IACd,QAAQ,MAAM,UAAU;AAAA,IACxB,cAAc,MAAM;AAAA,IAEpB,aAAa,MAAM,aAAa;AAAA,IAChC,WAAW,MAAM,aAAa;AAAA,IAC9B,YAAY,MAAM,aAAa;AAAA,IAC/B,WAAW,MAAM,aAAa;AAAA,IAC9B,YAAY,EAAE,OAAO,WAAW,QAAQ,WAAW;AAAA,IAEnD,MAAM;AAAA,MACJ,OAAO,MAAM,KAAK;AAAA,MAClB,QAAQ,MAAM,KAAK;AAAA,MACnB,YAAY,MAAM,KAAK,MAAM;AAAA,MAC7B,aAAa,MAAM,KAAK,MAAM;AAAA,IAChC;AAAA,IAEA,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,IACf,SAAS,MAAM;AAAA,IACf,UAAU,MAAM;AAAA,EAClB;AACF;AAEO,IAAM,gBAA+C;AAAA,EAC1D,oBAAoB,YAAY;AAAA,IAC9B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA,EACD,qBAAqB,YAAY;AAAA,IAC/B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA,EACD,qBAAqB,YAAY;AAAA,IAC/B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA,EACD,mBAAmB,YAAY;AAAA,IAC7B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA,EACD,mBAAmB,YAAY;AAAA,IAC7B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA,EACD,mBAAmB,YAAY;AAAA,IAC7B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA,EACD,mBAAmB,YAAY;AAAA,IAC7B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA,EACD,mBAAmB,YAAY;AAAA,IAC7B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYD,wBAAwB,YAAY;AAAA,IAClC,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA,EACD,iBAAiB,YAAY;AAAA,IAC3B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA,EACD,sBAAsB,YAAY;AAAA,IAChC,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMD,mBAAmB,YAAY;AAAA,IAC7B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA,EACD,wBAAwB,YAAY;AAAA,IAClC,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA,EACD,kBAAkB,YAAY;AAAA,IAC5B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA,EACD,oBAAoB,YAAY;AAAA,IAC9B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKD,mBAAmB,YAAY;AAAA,IAC7B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AAAA,EACD,qBAAqB,YAAY;AAAA,IAC/B,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,EACf,CAAC;AACH;AAGA,IAAM,cAAc;AAsBb,SAAS,aAAa,SAAmD;AAC9E,QAAM,KAAK,QAAQ,QAAQ,GAAG;AAC9B,QAAM,OAAO,OAAO,KAAK,UAAU,QAAQ,MAAM,GAAG,EAAE;AACtD,QAAM,UAAU,OAAO,KAAK,KAAK,QAAQ,MAAM,EAAE;AAEjD,MAAI,CAAC,KAAK,SAAS,WAAW,EAAG,QAAO,EAAE,OAAO,SAAS,MAAM,MAAM;AACtE,MAAI,CAAC,OAAO,OAAO,eAAe,IAAI,EAAG,QAAO,EAAE,OAAO,SAAS,MAAM,MAAM;AAE9E,SAAO,EAAE,OAAO,KAAK,MAAM,GAAG,CAAC,YAAY,MAAM,IAAI,SAAS,MAAM,KAAK;AAC3E;;;AD/VO,IAAM,kBAAkB,CAAC,UAAU,OAAO;AAGjD,IAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAYA,IAAI,WAAwC,CAAC;AAC7C,IAAI;AAEG,SAAS,iBAAiB,SAA4C;AAC3E,aAAW;AACb;AAEO,SAAS,mBAAgD;AAC9D,SAAO;AACT;AAGO,SAAS,wBAAwB,OAAiC;AACvE,yBAAuB,OAAO,KAAK,KAAK;AAC1C;AAEO,SAAS,0BAA8C;AAC5D,SAAO;AACT;AAQA,SAAS,cAAc,SAAyB;AAC9C,QAAM,KAAK,QAAQ,QAAQ,GAAG;AAC9B,SAAO,OAAO,KAAK,KAAK,QAAQ,MAAM,EAAE;AAC1C;AAEA,SAAS,qBAAqB,SAAyB;AACrD,QAAM,KAAK,QAAQ,QAAQ,GAAG;AAC9B,SAAO,OAAO,KAAK,UAAU,QAAQ,MAAM,GAAG,EAAE;AAClD;AAkBO,SAAS,kBACd,OACA,SACA,WAIQ;AACR,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,UAAU,WAAW,WAAW,UAAU,KAAK;AACrD,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,OAAO,SAAS,GAAG,EAAG,QAAO;AAExC,QAAM,WAAW,YACb,UAAU,uBACV;AACJ,QAAM,WAAW,OAAO,YAAY,KAAK;AACzC,QAAM,SACJ,aAAa,OAAO,SAAS,aAAa,WAAW;AACvD,MAAI,CAAC,OAAQ,QAAO;AAIpB,QAAM,OAAO,qBAAqB,MAAM;AACxC,MAAI,CAAC,OAAO,OAAO,eAAe,IAAI,GAAG;AACvC,QAAI,KAAK,+CAA+C;AAAA,MACtD;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AACD,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,GAAG,IAAI,GAAG,cAAc,OAAO,CAAC;AACjD,MAAI,aAAa,SAAS;AACxB,QAAI,MAAM,wBAAwB,EAAE,OAAO,MAAM,SAAS,IAAI,SAAS,CAAC;AAAA,EAC1E;AACA,SAAO;AACT;AAmBO,SAAS,mBACd,OACA,WACA,WACoB;AACpB,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,UAAU,WAAW,WAAW,UAAU,KAAK;AACrD,QAAM,WAAW,QAAQ,iBAAiB,KAAK;AAC/C,MAAI,CAAC,SAAU,QAAO;AAEtB,MAAI,CAAC,kBAAkB,SAAS,QAAQ,GAAG;AACzC,QAAI,KAAK,gDAAgD;AAAA,MACvD;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AACD,WAAO;AAAA,EACT;AAEA,MAAI,aAAa,WAAW;AAC1B,QAAI,MAAM,yBAAyB;AAAA,MACjC;AAAA,MACA,MAAM;AAAA,MACN,IAAI;AAAA,IACN,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAQO,SAAS,sBAAsB,MAA2B;AAC/D,QAAM,SAAsB,CAAC;AAC7B,MAAI,CAAC,KAAK,WAAW,KAAK,EAAG,QAAO;AAEpC,QAAM,QAAQ,KAAK,MAAM,OAAO;AAChC,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,KAAK,KAAK,MAAM,MAAO;AAE3B,UAAM,QAAQ,yCAAyC,KAAK,IAAI;AAChE,QAAI,CAAC,MAAO;AAEZ,UAAM,MAAM,MAAM,CAAC;AACnB,QACE,QAAQ,UACR,QAAQ,WACR,QAAQ,gBACR,QAAQ;AAER;AAEF,UAAM,QAAQ,MAAM,CAAC,EAAE,KAAK,EAAE,QAAQ,gBAAgB,EAAE;AACxD,QAAI,MAAO,QAAO,GAAG,IAAI;AAAA,EAC3B;AAEA,SAAO;AACT;AAOA,eAAsB,yBACpB,aACsC;AACtC,QAAM,UAAuC,CAAC;AAE9C,aAAW,aAAa,aAAa;AACnC,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,QAAQ,SAAS;AAAA,IACnC,QAAQ;AACN;AAAA,IACF;AAEA,eAAW,SAAS,SAAS;AAC3B,UAAI,CAAC,MAAM,SAAS,KAAK,EAAG;AAE5B,YAAM,OAAO,MAAM,MAAM,GAAG,EAAE;AAC9B,UAAI,QAAQ,IAAI,EAAG;AAEnB,UAAI;AACF,cAAM,OAAO,MAAM,SAASC,MAAK,KAAK,WAAW,KAAK,GAAG,MAAM;AAC/D,gBAAQ,IAAI,IAAI,sBAAsB,IAAI;AAAA,MAC5C,SAAS,KAAK;AACZ,YAAI,MAAM,iCAAiC;AAAA,UACzC,MAAMA,MAAK,KAAK,WAAW,KAAK;AAAA,UAChC,OAAO,OAAO,GAAG;AAAA,QACnB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMO,SAAS,iBACd,MACA,kBACU;AACV,QAAM,cAAwB,CAAC;AAE/B,MAAI,kBAAkB;AACpB,eAAW,QAAQ,iBAAiB;AAClC,kBAAY,KAAKA,MAAK,KAAK,kBAAkB,aAAa,IAAI,CAAC;AAAA,IACjE;AAAA,EACF;AACA,MAAI,MAAM;AACR,eAAW,QAAQ,iBAAiB;AAClC,kBAAY,KAAKA,MAAK,KAAK,MAAM,WAAW,YAAY,IAAI,CAAC;AAAA,IAC/D;AAAA,EACF;AAEA,SAAO;AACT;;;AE1SA,YAAYC,aAAY;AACxB,YAAYC,SAAQ;AACpB,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,SAAS,iBAAAC,sBAAqB;AA0DvB,IAAM,oBAAoB;AAe1B,SAAS,mBAAkC;AAChD,MAAI;AACF,UAAM,OAAOC,eAAc,YAAY,GAAG;AAC1C,UAAM,MAAW,cAAa,cAAQ,IAAI,GAAG,MAAM,QAAQ;AAC3D,WAAOC,WAAU,GAAG,IAAI,MAAM;AAAA,EAChC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AA8BA,SAASA,WAAU,GAAoB;AACrC,MAAI;AACF,WAAU,aAAS,CAAC,EAAE,YAAY;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,SAAS,GAAmB;AACnC,MAAI;AACF,WAAU,iBAAa,CAAC;AAAA,EAC1B,QAAQ;AACN,WAAY,cAAQ,CAAC;AAAA,EACvB;AACF;AAEA,SAAS,WAAW,MAAuB;AACzC,QAAM,QAAQ,QAAQ,IAAI,IAAI;AAC9B,MAAI,UAAU,OAAW,QAAO;AAChC,QAAM,aAAa,MAAM,KAAK,EAAE,YAAY;AAC5C,SAAO,eAAe,MAAM,eAAe,OAAO,eAAe;AACnE;AAQA,IAAM,kBAAkB;AAExB,IAAM,oBAAoB;AAC1B,IAAM,mBAAmB;AAGlB,SAAS,kBAAkB,QAA+B;AAC/D,QAAM,QAAQ,kBAAkB,KAAK,MAAM;AAC3C,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,iBAAiB,KAAK,MAAM,CAAC,CAAE;AAC5C,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,OAAO,KAAK,CAAC,EAAG,KAAK,EAAE,QAAQ,gBAAgB,EAAE,EAAE,KAAK;AAC9D,SAAO,gBAAgB,KAAK,IAAI,IAAI,OAAO;AAC7C;AAGA,SAAS,UAAU,MAAc,OAAuC;AACtE,QAAM,MAAW,WAAK,MAAM,KAAK;AACjC,MAAI;AACJ,MAAI;AACF,aAAY,iBAAkB,WAAK,KAAK,UAAU,GAAG,MAAM;AAAA,EAC7D,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL,MAAM,kBAAkB,MAAM,KAAK;AAAA,IACnC;AAAA,IACA,SAAS,SAAS,GAAG;AAAA,IACrB,aAAoB,mBAAW,QAAQ,EAAE,OAAO,MAAM,EAAE,OAAO,KAAK;AAAA,EACtE;AACF;AAOO,SAAS,UAAU,GAAoB,GAA6B;AACzE,SAAO,EAAE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE;AACxD;AAUO,SAAS,WAAW,KAAuB;AAChD,QAAM,QAAkB,CAAC;AACzB,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,OAAO,CAAC,MAAc;AAC1B,UAAM,MAAW,cAAQ,CAAC;AAC1B,QAAI,KAAK,IAAI,GAAG,EAAG;AACnB,SAAK,IAAI,GAAG;AACZ,QAAIA,WAAU,GAAG,EAAG,OAAM,KAAK,GAAG;AAAA,EACpC;AAEA,QAAM,OAAU,YAAQ;AACxB,QAAM,UAAU,OAAY,cAAQ,IAAI,IAAI;AAE5C,MAAI,UAAe,cAAQ,GAAG;AAC9B,SAAO,MAAM;AACX,SAAU,WAAK,SAAS,aAAa,QAAQ,CAAC;AAI9C,QAAI,YAAY,SAAS;AACvB,WAAU,WAAK,SAAS,WAAW,QAAQ,CAAC;AAC5C,WAAU,WAAK,SAAS,WAAW,QAAQ,CAAC;AAAA,IAC9C;AACA,UAAM,SAAc,cAAQ,OAAO;AACnC,QAAI,WAAW,QAAS;AACxB,cAAU;AAAA,EACZ;AAEA,MAAI,QAAS,MAAU,WAAK,SAAS,aAAa,QAAQ,CAAC;AAG3D,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,QAAQ;AACV,SAAU,WAAK,QAAQ,QAAQ,CAAC;AAChC,SAAU,WAAK,QAAQ,OAAO,CAAC;AAAA,EACjC;AAEA,QAAM,MAAM,QAAQ,IAAI,oBAAoB,UAAe,WAAK,SAAS,SAAS,IAAI;AACtF,MAAI,KAAK;AACP,SAAU,WAAK,KAAK,YAAY,QAAQ,CAAC;AACzC,SAAU,WAAK,KAAK,YAAY,OAAO,CAAC;AAAA,EAC1C;AAEA,MAAI,WAAW,CAAC,WAAW,kCAAkC,GAAG;AAC9D,QAAI,CAAC,WAAW,qCAAqC,GAAG;AACtD,WAAU,WAAK,SAAS,WAAW,QAAQ,CAAC;AAAA,IAC9C;AACA,SAAU,WAAK,SAAS,WAAW,QAAQ,CAAC;AAAA,EAC9C;AAEA,SAAO;AACT;AAWA,SAAS,cACP,MACA,SACA,OACM;AACN,MAAI;AACJ,MAAI;AACF,cAAa,gBAAY,MAAM,EAAE,eAAe,KAAK,CAAC;AAAA,EACxD,QAAQ;AACN;AAAA,EACF;AACA,aAAW,SAAS,SAAS;AAE3B,QAAI,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,eAAe,EAAG;AACrD,QAAI,MAAM,KAAK,WAAW,GAAG,EAAG;AAChC,UAAM,QAAQ,UAAU,MAAM,MAAM,IAAI;AACxC,QAAI,CAAC,MAAO;AACZ,UAAM,SAAS,QAAQ,IAAI,MAAM,IAAI;AACrC,QAAI,QAAQ;AACV,UAAI,CAAC,UAAU,QAAQ,KAAK,GAAG;AAC7B,YAAI,KAAK,gFAAgF;AAAA,UACvF,MAAM,MAAM;AAAA,UACZ,SAAS,OAAO;AAAA,UAChB,SAAS,MAAM;AAAA,QACjB,CAAC;AAAA,MACH;AACA;AAAA,IACF;AACA,YAAQ,IAAI,MAAM,MAAM,KAAK;AAC7B,UAAM,KAAK,KAAK;AAAA,EAClB;AACF;AAEA,IAAM,SAAS,CAAC,GAAoB,MAAuB,EAAE,KAAK,cAAc,EAAE,IAAI;AAE/E,SAAS,uBAAuB,KAAgC;AACrE,QAAM,QAA2B,CAAC;AAClC,QAAM,UAAU,oBAAI,IAA6B;AACjD,aAAW,QAAQ,WAAW,GAAG,EAAG,eAAc,MAAM,SAAS,KAAK;AACtE,SAAO,MAAM,KAAK,MAAM;AAC1B;AAGO,SAAS,wBAA2C;AACzD,QAAM,OAAO,iBAAiB;AAC9B,MAAI,CAAC,KAAM,QAAO,CAAC;AACnB,QAAM,QAA2B,CAAC;AAClC,gBAAc,MAAM,oBAAI,IAAI,GAAG,KAAK;AACpC,SAAO,MAAM,KAAK,MAAM;AAC1B;AAaO,SAAS,gBAAgB,WAAmC;AACjE,QAAM,WAAW,aAAa,QAAQ,IAAI;AAC1C,MAAI,SAAU,QAAY,cAAQ,WAAW,QAAQ,CAAC;AACtD,QAAM,OAAU,YAAQ;AACxB,SAAO,OAAY,cAAQ,MAAM,SAAS,IAAI;AAChD;AAOA,SAAS,0BAA0B,WAA0B,KAAuB;AAClF,MAAI,CAAC,UAAW,QAAO,CAAC;AACxB,MAAI;AACJ,MAAI;AACF,aAAS,KAAK;AAAA,MACT,iBAAkB,WAAK,WAAW,WAAW,wBAAwB,GAAG,MAAM;AAAA,IACnF;AAAA,EACF,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,UAAW,QAAyC;AAC1D,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO,CAAC;AAErD,QAAM,YAAiB,cAAQ,GAAG;AAClC,QAAM,QAAkB,CAAC;AACzB,aAAW,YAAY,OAAO,OAAO,OAAkC,GAAG;AACxE,QAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG;AAC9B,eAAW,WAAW,UAAU;AAC9B,YAAM,QAAQ;AACd,UAAI,OAAO,OAAO,gBAAgB,YAAY,CAAC,MAAM,YAAa;AAClE,UAAI,MAAM,UAAU,UAAU,CAAC,gBAAgB,MAAM,aAAa,SAAS,EAAG;AAC9E,YAAM,KAAU,WAAK,MAAM,aAAa,QAAQ,CAAC;AAAA,IACnD;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,aAAsB,WAA4B;AACzE,MAAI,OAAO,gBAAgB,YAAY,CAAC,YAAa,QAAO;AAC5D,QAAM,OAAY,cAAQ,WAAW;AACrC,MAAI,SAAS,UAAW,QAAO;AAC/B,QAAM,MAAW,eAAS,MAAM,SAAS;AACzC,SAAO,QAAQ,MAAM,CAAC,IAAI,WAAW,IAAI,KAAK,CAAM,iBAAW,GAAG;AACpE;AAOO,SAAS,2BAA2B,MAGzB;AAChB,QAAM,QAAuB,CAAC;AAC9B,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,OAAO,CAAC,MAAc,UAAgC;AAC1D,UAAM,MAAW,cAAQ,IAAI;AAC7B,QAAI,QAAQ,IAAI,GAAG,EAAG;AACtB,YAAQ,IAAI,GAAG;AACf,QAAI;AACJ,QAAI;AACF,gBAAa,gBAAY,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,IACvD,QAAQ;AACN;AAAA,IACF;AACA,eAAW,SAAS,SAAS;AAC3B,UAAI,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,eAAe,EAAG;AACrD,UAAI,MAAM,KAAK,WAAW,GAAG,EAAG;AAChC,YAAM,QAAQ,UAAU,KAAK,MAAM,IAAI;AACvC,UAAI,MAAO,OAAM,KAAK,EAAE,GAAG,OAAO,MAAM,CAAC;AAAA,IAC3C;AAAA,EACF;AAEA,QAAM,YAAY,gBAAgB,KAAK,SAAS;AAChD,MAAI,UAAW,MAAU,WAAK,WAAW,QAAQ,GAAG,MAAM;AAE1D,QAAM,OAAU,YAAQ;AACxB,QAAM,UAAU,OAAY,cAAQ,IAAI,IAAI;AAC5C,MAAI,UAAe,cAAQ,KAAK,GAAG;AACnC,SAAO,MAAM;AAGX,QAAI,YAAY,QAAS,MAAU,WAAK,SAAS,WAAW,QAAQ,GAAG,SAAS;AAChF,UAAM,SAAc,cAAQ,OAAO;AACnC,QAAI,WAAW,QAAS;AACxB,cAAU;AAAA,EACZ;AAEA,aAAW,QAAQ,0BAA0B,WAAW,KAAK,GAAG,EAAG,MAAK,MAAM,QAAQ;AAEtF,SAAO;AACT;AAsBO,SAAS,yBACd,QACA,QACyD;AACzD,MAAI,OAAO,WAAW,EAAG,QAAO,EAAE,SAAS,QAAQ,SAAS,CAAC,EAAE;AAE/D,QAAM,UAA6B,CAAC;AACpC,QAAM,UAA0B,CAAC;AACjC,aAAW,SAAS,QAAQ;AAC1B,UAAM,WAAW,OAAO,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,OAAO;AAC/D,UAAM,cAAc,YAAY,OAAO,KAAK,CAAC,MAAM,EAAE,gBAAgB,MAAM,WAAW;AACtF,UAAM,YACJ,eAAe,OAAO,KAAK,CAAC,MAAM,EAAE,UAAU,YAAY,EAAE,SAAS,MAAM,IAAI;AACjF,QAAI,CAAC,WAAW;AACd,cAAQ,KAAK,KAAK;AAClB;AAAA,IACF;AACA,UAAM,SAAS,WAAW,cAAc,cAAc,iBAAiB;AACvE,YAAQ,KAAK,EAAE,OAAO,QAAQ,WAAW,OAAO,CAAC;AACjD,QAAI,WAAW,cAAc;AAC3B,UAAI,KAAK,iFAAiF;AAAA,QACxF,MAAM,MAAM;AAAA,QACZ,aAAa,UAAU;AAAA,QACvB,YAAY,MAAM;AAAA,QAClB,OAAO,UAAU;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,EAAE,SAAS,QAAQ;AAC5B;AAsBO,SAAS,wBAA4C;AAC1D,SAAO,sBAAsB,EAAE,QAAQ,CAAC,UAAU;AAChD,UAAM,OAAY,WAAK,MAAM,KAAK,UAAU;AAC5C,QAAI;AACJ,QAAI;AACF,aAAU,iBAAa,MAAM,MAAM;AAAA,IACrC,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AACA,UAAM,QAAQ,oCAAoC,KAAK,IAAI;AAC3D,UAAM,cAAc,QAAQ,CAAC,KAAK;AAClC,UAAM,QAAQ,CAAC,QAAoC;AACjD,YAAM,OAAO,IAAI,OAAO,IAAI,GAAG,cAAc,GAAG,EAAE,KAAK,WAAW,IAAI,CAAC,GAAG,KAAK;AAC/E,aAAO,OAAO,KAAK,QAAQ,kBAAkB,IAAI,IAAI;AAAA,IACvD;AACA,UAAM,cAAc,MAAM,aAAa;AACvC,WAAO;AAAA,MACL;AAAA,QACE,IAAI,MAAM;AAAA,QACV,MAAM,MAAM,MAAM,KAAK,MAAM;AAAA,QAC7B,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,QACrC,MAAM;AAAA,QACN,SAAS,QAAQ,KAAK,MAAM,MAAM,CAAC,EAAE,MAAM,IAAI;AAAA,MACjD;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAGA,SAAS,UAAU,QAAgB,QAAsB;AACvD,MAAI;AAGF,IAAG,gBAAY,QAAQ,QAAQ,QAAQ,aAAa,UAAU,aAAa,KAAK;AAChF;AAAA,EACF,QAAQ;AACN,IAAG,WAAO,QAAQ,QAAQ,EAAE,WAAW,MAAM,aAAa,KAAK,CAAC;AAAA,EAClE;AACF;AAEA,SAASC,YAAW,GAAoB;AACtC,MAAI;AACF,WAAU,aAAS,CAAC,EAAE,OAAO;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,oBAAoB,QAA0C;AAC5E,MAAI,OAAO,WAAW,EAAG,QAAO;AAEhC,QAAM,cAAc,OAAO,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,KAAK,EAAE,GAAG,EAAE,EAAE,KAAK,IAAI;AACtE,QAAM,OAAc,mBAAW,QAAQ,EAAE,OAAO,WAAW,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AACtF,QAAM,OAAY,WAAK,aAAa,GAAG,UAAU,IAAI,EAAE;AACvD,QAAM,WAAgB,WAAK,MAAM,kBAAkB,aAAa;AAGhE,MAAIA,YAAW,QAAQ,EAAG,QAAO;AAEjC,MAAI;AACF,IAAG,WAAO,MAAM,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAChD,IAAG,cAAe,WAAK,MAAM,gBAAgB,GAAG,EAAE,WAAW,KAAK,CAAC;AACnE,IAAG,cAAe,WAAK,MAAM,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAC3D,IAAG;AAAA,MACD;AAAA,MACA,KAAK;AAAA,QACH;AAAA,UACE,MAAM;AAAA,UACN,aACE;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,EAAE,UAAU,QAAQ,MAAM,IAAM;AAAA,IAClC;AACA,eAAW,SAAS,QAAQ;AAC1B,gBAAU,MAAM,KAAU,WAAK,MAAM,UAAU,MAAM,IAAI,CAAC;AAAA,IAC5D;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,KAAK,6CAA6C;AAAA,MACpD;AAAA,MACA,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,IACxD,CAAC;AACD,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAQO,SAAS,yBAAyB,QAE7B;AACV,QAAM,MAAM,iBAAiB;AAC7B,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,WAAW,CAAC;AACnB,QAAM,QAAS,OAAO,OAAO,UAAU,CAAC;AACxC,MAAI,MAAM,KAAK,CAAC,MAAW,cAAQ,CAAC,MAAM,GAAG,EAAG,QAAO;AACvD,QAAM,KAAK,GAAG;AACd,SAAO;AACT;AAWA,eAAsB,uBAAuB,MAQvB;AACpB,QAAM,OAAO,KAAK,UAAU,uBAAuB,KAAK,GAAG,IAAI,CAAC;AAChE,QAAM,UAAU,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAC/C,QAAM,UAAU,sBAAsB,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,IAAI,CAAC;AAC1E,MAAI,SAAS,CAAC,GAAG,MAAM,GAAG,OAAO,EAAE,KAAK,MAAM;AAE9C,MAAI,KAAK,eAAe,SAAS,OAAO,SAAS,GAAG;AAClD,UAAM,SAAS,2BAA2B,EAAE,KAAK,KAAK,KAAK,WAAW,KAAK,UAAU,CAAC;AACtF,UAAM,EAAE,SAAS,QAAQ,IAAI,yBAAyB,QAAQ,MAAM;AACpE,QAAI,QAAQ,SAAS,GAAG;AACtB,UAAI,KAAK,oDAAoD;AAAA,QAC3D,OAAO,QAAQ;AAAA,QACf,SAAS,QAAQ,IAAI,CAAC,OAAO;AAAA,UAC3B,MAAM,EAAE,MAAM;AAAA,UACd,QAAQ,EAAE;AAAA,UACV,UAAU,EAAE,MAAM;AAAA,UAClB,QAAQ,EAAE,OAAO;AAAA,QACnB,EAAE;AAAA,MACJ,CAAC;AAAA,IACH;AACA,aAAS;AAAA,EACX;AACA,MAAI,OAAO,WAAW,EAAG,QAAO,CAAC;AAIjC,QAAM,YAAY,MAAM,sBAAsB,KAAK,SAAS,cAAc;AAC1E,MAAI,CAAC,WAAW;AACd,QAAI;AAAA,MACF;AAAA,MACA,EAAE,QAAQ,OAAO,OAAO;AAAA,IAC1B;AACA,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,MAAM,oBAAoB,MAAM;AACtC,MAAI,CAAC,IAAK,QAAO,CAAC;AAElB,MAAI,KAAK,uCAAuC;AAAA,IAC9C,OAAO,OAAO;AAAA,IACd,OAAO,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,IAC/B,SAAS,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,IAClC,WAAW;AAAA,EACb,CAAC;AACD,SAAO,CAAC,GAAG;AACb;;;ACppBA,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,UAAAC,eAAc;;;ACDvB,YAAYC,SAAQ;AACpB,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,SAAS,oBAAoB;AAC7B,SAAS,cAAAC,mBAAkB;AAqB3B,SAAS,cAAc,MAAM,UAAkB;AAC7C,MAAS,iBAAW,GAAG,KAAQ,eAAW,GAAG,EAAG,QAAO;AACvD,QAAM,SAAS,IAAI,MAAM,GAAG;AAC5B,MAAI,OAAQ,QAAO;AACnB,QAAM,QAAW,aAAS,MAAM;AAChC,MAAI;AACF,UAAM,MAAM,aAAa,QAAQ,UAAU,SAAS,CAAC,GAAG,GAAG;AAAA,MACzD,UAAU;AAAA,MACV,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,IACpC,CAAC;AACD,UAAM,QAAQ,IACX,MAAM,OAAO,EACb,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO,EACd,KAAK,CAAC,MAAS,eAAW,CAAC,CAAC;AAC/B,QAAI,MAAO,QAAO;AAAA,EACpB,QAAQ;AAAA,EAAC;AACT,QAAM,IAAI,MAAM,sCAAsC,GAAG,EAAE;AAC7D;AAOO,SAAS,UAAU,KAAqB;AAC7C,SAAY,cAAQ,GAAG,EAAE,QAAQ,iBAAiB,GAAG;AACvD;AAgEO,SAAS,oBAAoB,MAIG;AACrC,SAAO;AAAA,IACL,GAAG,QAAQ;AAAA,IACX,mBAAmB,KAAK;AAAA,IACxB,MAAM;AAAA;AAAA;AAAA;AAAA,IAIN,GAAG,cAAc;AAAA,IACjB,GAAI,KAAK,wBACL,EAAE,mBAAmB,QAAW,sBAAsB,OAAU,IAChE,CAAC;AAAA,IACL,GAAI,KAAK,SAAS,EAAE,0BAA0B,KAAK,OAAO,IAAI,CAAC;AAAA,EACjE;AACF;AAEA,IAAM,gBAAgB,oBAAI,IAAI,CAAC,YAAY,iBAAiB,YAAY,CAAC;AACzE,IAAM,QAAQ,CAAC,OAAe,IAAI,QAAc,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAExE,SAAS,iBAAiB,WAAuC;AAC/D,QAAM,QAAQ,aAAa,QAAQ,IAAI;AACvC,MAAI,CAAC,MAAO,QAAY,WAAQ,YAAQ,GAAG,SAAS;AACpD,MAAI,UAAU,IAAK,QAAU,YAAQ;AACrC,MAAI,MAAM,WAAW,IAAI,KAAK,MAAM,WAAW,KAAK,GAAG;AACrD,WAAY,WAAQ,YAAQ,GAAG,MAAM,MAAM,CAAC,CAAC;AAAA,EAC/C;AACA,SAAY,cAAQ,KAAK;AAC3B;AAEO,IAAM,gBAAN,MAAoB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT,MAAM;AAAA,EAEE,OAA6B;AAAA,EAC7B,SAAS;AAAA;AAAA,EACT,aAAa;AAAA,EACb,SAAS;AAAA,EACT,WAA0B;AAAA,EAC1B,UAAU;AAAA,EACD;AAAA,EACA;AAAA,EAwBjB,YAAY,OAA6B,CAAC,GAAG;AAC3C,SAAK,MAAW,cAAQ,KAAK,OAAO,QAAQ,IAAI,CAAC;AACjD,SAAK,YAAY,iBAAiB,KAAK,SAAS;AAChD,SAAK,SAAS,KAAK;AACnB,SAAK,YAAYC,YAAW;AAC5B,SAAK,YAAiB;AAAA,MACpB,KAAK;AAAA,MACL;AAAA,MACA,UAAU,KAAK,GAAG;AAAA,MAClB,GAAG,KAAK,SAAS;AAAA,IACnB;AACA,SAAK,IAAI;AAAA,MACP,KAAK,KAAK;AAAA,MACV,SAAS,KAAK;AAAA,MACd,WAAW,KAAK;AAAA,MAChB,OAAO,KAAK;AAAA,MACZ,gBAAgB,KAAK;AAAA,MACrB,WAAW,KAAK,aAAa,CAAC;AAAA,MAC9B,uBAAuB,KAAK;AAAA,MAC5B,QAAQ,KAAK;AAAA,MACb,MAAM,KAAK,QAAQ;AAAA,MACnB,MAAM,KAAK,QAAQ;AAAA,MACnB,WAAW,KAAK,aAAa;AAAA,MAC7B,aAAa,KAAK,eAAe;AAAA,MACjC,WAAW,KAAK,aAAa;AAAA,MAC7B,QAAQ,KAAK,UAAU;AAAA;AAAA;AAAA;AAAA,MAIvB,eAAe,KAAK,iBAAiB;AAAA,MACrC,gBAAgB,KAAK,kBAAkB;AAAA,MACvC,aAAa,KAAK,eAAe;AAAA,MACjC,iBAAiB,KAAK,mBAAmB;AAAA,MACzC,kBAAkB,KAAK,oBAAoB;AAAA,MAC3C,OAAO,KAAK,SAAS;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,KAAK,QAAQ,QAAS,OAAM,IAAI,MAAM,sBAAsB;AAChE,SAAK,QAAQ;AAAA,MACX;AAAA,MACA,MAAM;AACJ,aAAK,UAAU;AACf,aAAK,QAAQ;AAAA,MACf;AAAA,MACA,EAAE,MAAM,KAAK;AAAA,IACf;AACA,UAAM,SAAS,cAAc,KAAK,EAAE,WAAW,QAAQ;AACvD,UAAM,OAAiB,CAAC,gBAAgB,KAAK,SAAS;AACtD,QAAI,KAAK,EAAE,MAAO,MAAK,KAAK,WAAW,KAAK,EAAE,KAAK;AACnD,QAAI,KAAK,EAAE,mBAAmB,QAAQ,KAAK,EAAE,mBAAmB,QAAW;AACzE,WAAK,KAAK,qBAAqB,KAAK,EAAE,cAAc;AAAA,IACtD;AACA,QAAI,KAAK,EAAE,aAAa,KAAK,EAAE,UAAU,OAAQ,MAAK,KAAK,GAAG,KAAK,EAAE,SAAS;AAE9E,QAAI,KAAK,EAAE;AACT,cAAQ,OAAO,MAAM,oBAAoB,MAAM,IAAI,KAAK,KAAK,GAAG,CAAC;AAAA,CAAI;AAEvE,SAAK,aAAa,KAAK,IAAI;AAC3B,SAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,GAAG,IAAI,GAAG;AAAA,MACvC,KAAK,KAAK;AAAA,MACV,KAAK,oBAAoB;AAAA;AAAA;AAAA,QAGvB,WAAW,KAAK;AAAA,QAChB,uBAAuB,KAAK,EAAE;AAAA,QAC9B,QAAQ,KAAK,EAAE;AAAA,MACjB,CAAC;AAAA,MACD,UAAU;AAAA,QACR,MAAM,KAAK,EAAE;AAAA,QACb,MAAM,KAAK,EAAE;AAAA,QACb,MAAM,CAAC,OAAO,MAAM;AAClB,eAAK,aAAa,KAAK,IAAI;AAC3B,gBAAM,QAAQ,OAAO,KAAK,CAAC,EAAE,SAAS,MAAM;AAC5C,eAAK,OAAO;AACZ,cAAI,KAAK,EAAE,MAAO,SAAQ,OAAO,MAAM,KAAK;AAAA,QAC9C;AAAA,MACF;AAAA,IACF,CAAC;AACD,SAAK,KAAK,OACP,KAAK,CAAC,SAAS;AACd,WAAK,WAAW,OAAO,SAAS,WAAW,OAAO;AAClD,WAAK,SAAS;AACd,WAAK,OAAO;AAAA,IACd,CAAC,EACA,MAAM,MAAM;AACX,WAAK,SAAS;AACd,WAAK,OAAO;AAAA,IACd,CAAC;AAEH,UAAM,KAAK,YAAY;AACvB,SAAK,SAAS,KAAK,UAAU;AAAA,EAC/B;AAAA;AAAA;AAAA,EAIA,MAAc,cAA6B;AACzC,UAAM,QAAQ,KAAK,IAAI;AACvB,WAAO,KAAK,IAAI,IAAI,QAAQ,KAAK,EAAE,WAAW;AAC5C,YAAM,MAAM,GAAG;AACf,UAAI,KAAK,QAAS,OAAM,IAAI,MAAM,qBAAqB;AACvD,UAAI,KAAK,QAAQ;AACf,cAAM,IAAI,MAAM,KAAK,eAAe,6BAA6B,IAAI,CAAC;AAAA,MACxE;AACA,YAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,YAAM,YAAY,KAAK,IAAI,IAAI,KAAK;AACpC,UAAI,WAAW,KAAK,EAAE,aAAa,aAAa,KAAK,EAAE,YAAa;AAAA,IACtE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAc,aAA4B;AACxC,UAAM,MAAM,KAAK,EAAE,WAAW;AAC9B,aAAS,UAAU,GAAG,UAAU,KAAK,EAAE,kBAAkB,WAAW;AAClE,UAAI,KAAK,WAAW,KAAK,UAAU,CAAC,KAAK,KAAM;AAC/C,WAAK,KAAK,SAAS,MAAM,IAAI;AAC7B,YAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,EAAE;AAClC,aAAO,KAAK,IAAI,IAAI,OAAO;AACzB,cAAM,MAAM,EAAE;AACd,YAAI,KAAK,WAAW,KAAK,OAAQ;AACjC,YAAI,KAAK,UAAU,IAAI,KAAK,OAAQ;AAAA,MACtC;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,eAAyB;AAC/B,QAAI;AACF,aAAU,iBAAa,KAAK,WAAW,MAAM,EAAE,MAAM,IAAI;AAAA,IAC3D,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA;AAAA,EAGQ,YAAoB;AAC1B,UAAM,QAAQ,KAAK,aAAa;AAChC,WAAO,MAAM,SAAS,IAAI,MAAM,SAAS,IAAI;AAAA,EAC/C;AAAA,EAEQ,QAAQ,MAAM,KAAa;AACjC,UAAM,QAAQ,KAAK,IAEhB,QAAQ,0CAA0C,EAAE,EACpD,QAAQ,QAAQ,GAAG,EACnB,KAAK;AACR,WAAO,MAAM,SAAS,MAAM,MAAM,MAAM,CAAC,GAAG,IAAI;AAAA,EAClD;AAAA,EAEQ,eAAe,QAAgB,aAAa,OAAe;AACjE,UAAM,QAAQ;AAAA,MACZ,GAAG,MAAM,eAAe,KAAK,SAAS,eAAe,KAAK,SAAS,cAAc,KAAK,YAAY,SAAS;AAAA,IAC7G;AACA,QAAI,YAAY;AACd,YAAM,OAAO,KAAK,QAAQ;AAC1B,UAAI,KAAM,OAAM,KAAK,gBAAgB,KAAK,UAAU,IAAI,CAAC,EAAE;AAAA,IAC7D;AACA,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,IAAI,QAAgB,kBAAgD;AACxE,QAAI,KAAK,QAAS,OAAM,IAAI,MAAM,SAAS;AAC3C,QAAI,CAAC,KAAK,QAAQ,KAAK;AACrB,YAAM,IAAI,MAAM,uCAAuC;AACzD,UAAM,UAAU,oBAAoB,KAAK,EAAE;AAC3C,UAAM,KAAK,KAAK,IAAI;AAKpB,QAAI,KAAK,EAAE,gBAAgB;AACzB,WAAK,KAAK,SAAS,MAAM,cAAc,SAAS,WAAW;AAAA,IAC7D,OAAO;AACL,WAAK,KAAK,SAAS,MAAM,MAAM;AAAA,IACjC;AACA,UAAM,KAAK,WAAW;AAEtB,UAAM,YAAsB,CAAC;AAC7B,QAAI,YAAiB;AACrB,QAAI,aAA4B;AAChC,UAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,WAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,YAAM,MAAM,KAAK,EAAE,MAAM;AACzB,UAAI,KAAK,QAAS,OAAM,IAAI,MAAM,kBAAkB;AACpD,YAAM,QAAQ,KAAK,aAAa;AAChC,YAAM,eAAe,MAAM,SAAS;AACpC,UAAI,gBAAgB,KAAK,QAAQ;AAG/B,YAAI,KAAK,OAAQ,OAAM,IAAI,MAAM,KAAK,eAAe,0BAA0B,IAAI,CAAC;AACpF;AAAA,MACF;AAEA,eAAS,IAAI,KAAK,QAAQ,IAAI,cAAc,KAAK;AAC/C,cAAM,IAAI,MAAM,CAAC;AACjB,YAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAG;AACrB,YAAI;AACJ,YAAI;AACF,gBAAM,KAAK,MAAM,CAAC;AAAA,QACpB,QAAQ;AACN;AAAA,QACF;AACA,YAAI,IAAI,SAAS,eAAe,IAAI,SAAS;AAC3C,qBAAW,KAAK,IAAI,QAAQ,WAAW,CAAC,GAAG;AACzC,gBAAI,GAAG,SAAS,UAAU,OAAO,EAAE,SAAS;AAC1C,wBAAU,KAAK,EAAE,IAAI;AAAA,UACzB;AACA,cAAI,IAAI,QAAQ,MAAO,aAAY,IAAI,QAAQ;AAC/C,cACE,IAAI,QAAQ,eACZ,cAAc,IAAI,IAAI,QAAQ,WAAW,GACzC;AACA,yBAAa,IAAI,QAAQ;AAAA,UAC3B;AAAA,QACF;AAAA,MACF;AACA,WAAK,SAAS;AACd,UAAI,WAAY;AAAA,IAClB;AAEA,QAAI,CAAC,YAAY;AACf,YAAM,IAAI;AAAA,QACR,KAAK;AAAA,UACH,wBAAwB,OAAO,+CAA+C,UAAU,MAAM;AAAA,QAChG;AAAA,MACF;AAAA,IACF;AAEA,UAAM,IAAI,aAAa,CAAC;AACxB,WAAO;AAAA,MACL,MAAM,UAAU,KAAK,IAAI,EAAE,KAAK;AAAA,MAChC;AAAA,MACA,OAAO;AAAA,MACP,iBAAiB,EAAE,2BAA2B;AAAA,MAC9C,qBAAqB,EAAE,+BAA+B;AAAA,MACtD,mBAAmB,EAAE,gBAAgB,6BAA6B;AAAA,MAClE,mBAAmB,EAAE,gBAAgB,6BAA6B;AAAA,MAClE,aAAa,EAAE,gBAAgB;AAAA,MAC/B,cAAc,EAAE,iBAAiB;AAAA,MACjC,WAAW,KAAK,IAAI,IAAI;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,SACJ,QACA,QACA,kBAC2D;AAC3D,QAAI,KAAK,QAAS,OAAM,IAAI,MAAM,SAAS;AAC3C,QAAI,CAAC,KAAK,QAAQ,KAAK;AACrB,YAAM,IAAI,MAAM,uCAAuC;AACzD,UAAM,UAAU,oBAAoB,KAAK,EAAE;AAE3C,QAAI,KAAK,EAAE,gBAAgB;AACzB,WAAK,KAAK,SAAS,MAAM,cAAc,SAAS,WAAW;AAAA,IAC7D,OAAO;AACL,WAAK,KAAK,SAAS,MAAM,MAAM;AAAA,IACjC;AACA,UAAM,KAAK,WAAW;AAEtB,QAAI,YAAiB;AACrB,QAAI,cAAc;AAClB,QAAI,aAA4B;AAChC,UAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,WAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,YAAM,MAAM,KAAK,EAAE,MAAM;AACzB,UAAI,KAAK,QAAS,OAAM,IAAI,MAAM,kBAAkB;AACpD,YAAM,QAAQ,KAAK,aAAa;AAChC,YAAM,eAAe,MAAM,SAAS;AACpC,UAAI,gBAAgB,KAAK,QAAQ;AAG/B,YAAI,KAAK,QAAQ;AACf,gBAAM,IAAI,MAAM,KAAK,eAAe,0BAA0B,IAAI,CAAC;AAAA,QACrE;AACA;AAAA,MACF;AACA,eAAS,IAAI,KAAK,QAAQ,IAAI,cAAc,KAAK;AAC/C,cAAM,IAAI,MAAM,CAAC;AACjB,YAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAG;AACrB,eAAO,CAAC;AACR,YAAI;AACJ,YAAI;AACF,gBAAM,KAAK,MAAM,CAAC;AAAA,QACpB,QAAQ;AACN;AAAA,QACF;AACA,YAAI,IAAI,SAAS,eAAe,IAAI,SAAS;AAC3C,cAAI,IAAI,QAAQ,OAAO;AACrB,wBAAY,IAAI,QAAQ;AACxB,2BAAe,IAAI,QAAQ,MAAM,iBAAiB;AAAA,UACpD;AACA,cACE,IAAI,QAAQ,eACZ,cAAc,IAAI,IAAI,QAAQ,WAAW,GACzC;AACA,yBAAa,IAAI,QAAQ;AAAA,UAC3B;AAAA,QACF;AAAA,MACF;AACA,WAAK,SAAS;AACd,UAAI,WAAY;AAAA,IAClB;AAMA,QAAI,QAAa;AACjB,QAAI,WAAW;AACb,cAAQ,EAAE,GAAG,WAAW,eAAe,YAAY;AACnD,UAAI,MAAM,QAAQ,UAAU,UAAU,KAAK,UAAU,WAAW,SAAS,GAAG;AAC1E,cAAM,QAAQ,UAAU,WAAW,IAAI,CAAC,QAAa,EAAE,GAAG,GAAG,EAAE;AAC/D,cAAM,MAAM,SAAS,CAAC,IAAI;AAAA,UACxB,GAAG,MAAM,MAAM,SAAS,CAAC;AAAA,UACzB,eAAe;AAAA,QACjB;AACA,cAAM,aAAa;AAAA,MACrB;AAAA,IACF;AACA,QAAI,CAAC,YAAY;AACf,YAAM,IAAI;AAAA,QACR,KAAK;AAAA,UACH,wBAAwB,OAAO;AAAA,QACjC;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,YAAY,MAAM;AAAA,EAC7B;AAAA,EAEA,UAAgB;AACd,QAAI,KAAK,MAAM;AACb,UAAI;AACF,aAAK,KAAK,SAAS,MAAM,GAAM;AAAA,MACjC,QAAQ;AAAA,MAAC;AACT,UAAI;AACF,aAAK,KAAK,KAAK;AAAA,MACjB,QAAQ;AAAA,MAAC;AACT,UAAI;AACF,aAAK,KAAK,SAAS,MAAM;AAAA,MAC3B,QAAQ;AAAA,MAAC;AAAA,IACX;AACA,SAAK,OAAO;AAAA,EACd;AACF;;;ADvfO,SAAS,mBAAmB,OAAuB;AACxD,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,KAAK;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,CAAC,UAAU,OAAO,SAAS,UAAU,CAAC,OAAO,QAAS,QAAO;AACjE,QAAM,UAAU,OAAO,QAAQ;AAC/B,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AAEpC,QAAM,QAAkB,CAAC;AACzB,MAAI,UAAU;AACd,aAAW,SAAS,SAAS;AAC3B,QAAI,OAAO,SAAS,UAAU,OAAO,MAAM,SAAS,UAAU;AAC5D,YAAM,KAAK,MAAM,IAAI;AAAA,IACvB,WAAW,OAAO,SAAS,eAAe;AACxC,YAAM,IAAI,MAAM;AAChB,YAAM,OACJ,OAAO,MAAM,WACT,IACA,MAAM,QAAQ,CAAC,IACb,EACG,IAAI,CAAC,MAAY,GAAG,SAAS,SAAS,EAAE,OAAO,EAAG,EAClD,OAAO,OAAO,EACd,KAAK,IAAI,IACZ;AACR,YAAM;AAAA,QACJ,eAAe,MAAM,cAAc,IAAI,MAAM,WAAW,KAAK,EAAE;AAAA,EAAM,IAAI;AAAA,MAC3E;AAAA,IACF,OAAO;AACL;AAAA,IACF;AAAA,EACF;AACA,MAAI,UAAU,GAAG;AACf,QAAI,KAAK,yDAAyD;AAAA,MAChE;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO,MAAM,KAAK,MAAM;AAC1B;AAkBO,SAAS,qBAAqB,MAAyC;AAC5E,QAAM,YAAsB,CAAC;AAC7B,MAAI,KAAK,kBAAkB,KAAK,eAAe,SAAS,GAAG;AACzD,cAAU;AAAA,MACR;AAAA,MACA,GAAG,KAAK;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,aAAW,OAAO,KAAK,cAAc,CAAC,GAAG;AACvC,cAAU,KAAK,gBAAgB,GAAG;AAAA,EACpC;AAIA,QAAM,eAAwC,CAAC;AAC/C,MAAI,KAAK,oBAAoB,KAAK,iBAAiB,SAAS,GAAG;AAC7D,iBAAa,cAAc,EAAE,OAAO,KAAK,iBAAiB;AAAA,EAC5D;AACA,MAAI,KAAK,UAAU;AACjB,iBAAa,WAAW;AAAA,EAC1B;AACA,MAAI,OAAO,KAAK,YAAY,EAAE,SAAS,GAAG;AACxC,cAAU,KAAK,cAAc,KAAK,UAAU,YAAY,CAAC;AAAA,EAC3D;AACA,MAAI,KAAK,mBAAmB,qBAAqB;AAC/C,QAAI;AAAA,MACF;AAAA,IACF;AAAA,EACF,WAAW,KAAK,gBAAgB;AAC9B,cAAU,KAAK,qBAAqB,KAAK,cAAc;AAAA,EACzD;AACA,MAAI,KAAK,kBAAkB;AACzB,cAAU,KAAK,+BAA+B,KAAK,gBAAgB;AAAA,EACrE;AACA,SAAO;AACT;AAEO,SAAS,wBACd,MACe;AACf,QAAM,YAAY,qBAAqB,IAAI;AAE3C,QAAM,UAAU,IAAI,cAAc;AAAA,IAChC,KAAK,KAAK;AAAA,IACV,SAAS,KAAK;AAAA,IACd,WAAW,KAAK;AAAA,IAChB,OAAO,KAAK;AAAA;AAAA;AAAA,IAGZ,gBACE,KAAK,mBAAmB,SAAY,OAAO,KAAK;AAAA,IAClD;AAAA,IACA,uBAAuB,KAAK;AAAA,IAC5B,QAAQ,KAAK,SAAS,eAAe,KAAK,MAAM,IAAI;AAAA,EACtD,CAAC;AACD,MAAI,KAAK,uCAAuC;AAAA,IAC9C,KAAK,KAAK;AAAA,IACV,SAAS,KAAK,WAAW;AAAA,IACzB,WAAW,QAAQ;AAAA,IACnB,OAAO,KAAK;AAAA,IACZ,QAAQ,KAAK;AAAA,IACb,WAAW,QAAQ;AAAA,IACnB,WAAW,QAAQ;AAAA,EACrB,CAAC;AAED,QAAM,cAAc,IAAIC,cAAa;AACrC,QAAM,gBAAgB,oBAAI,IAA0B;AACpD,MAAI,eAAqC;AAEzC,QAAM,gBAAgB,MAAqB;AACzC,QAAI,CAAC,aAAc,gBAAe,QAAQ,MAAM;AAChD,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,CACjB,SACA,SACA,QACA,UACS;AACT,gBAAY;AAAA,MACV;AAAA,MACA,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,QACN;AAAA,QACA,UAAU;AAAA,QACV;AAAA,QACA,YAAY,QAAQ;AAAA,QACpB,OAAO,SAAS,CAAC;AAAA,QACjB,gBAAgB;AAAA,QAChB,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,UAAU,CAAC,YAA0B;AACzC,UAAM,YAAY;AAChB,UAAI;AACF,cAAM,cAAc;AACpB,cAAM,EAAE,YAAY,MAAM,IAAI,MAAM,QAAQ,SAAS,SAAS,CAAC,QAAQ;AACrE,sBAAY,KAAK,QAAQ,GAAG;AAAA,QAC9B,CAAC;AAMD,cAAM,WAAW,CAAC;AAClB;AAAA,UACE,WAAW,2BAA2B;AAAA,UACtC;AAAA,UACA,WACI,wIACA;AAAA,UACJ;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AACZ,cAAM,IAAI,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAC5D,YAAI,MAAM,2BAA2B,EAAE,OAAO,EAAE,QAAQ,CAAC;AACzD;AAAA,UACE;AAAA,UACA;AAAA,UACA,iCAAiC,EAAE,OAAO;AAAA,QAC5C;AACA,YAAI,cAAc,OAAO,GAAG;AAC1B,qBAAW,KAAK,cAAe,GAAE,CAAC;AAAA,QACpC,OAAO;AACL,sBAAY,KAAK,OAAO;AAAA,QAC1B;AAAA,MACF;AAAA,IACF,GAAG;AAAA,EACL;AAIA,QAAM,OAAY;AAAA,IAChB,OAAO;AAAA,MACL,MAAM,OAAwB;AAC5B,cAAM,MACJ,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,IAC5C,MAAM,MAAM,GAAG,EAAE,IACjB;AAEN,gBAAQ,mBAAmB,GAAG,CAAC;AAC/B,eAAO;AAAA,MACT;AAAA,MACA,MAAY;AAAA,MAAC;AAAA,IACf;AAAA,IACA,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,GAAG,OAAe,IAAmC;AACnD,UAAI,UAAU,QAAS,eAAc,IAAI,EAAE;AAC3C,aAAO;AAAA,IACT;AAAA,IACA,OAAgB;AACd,aAAO;AAAA,IACT;AAAA,IACA,IAAI,OAAe,IAAmC;AACpD,UAAI,UAAU,QAAS,eAAc,OAAO,EAAE;AAC9C,aAAO;AAAA,IACT;AAAA,IACA,OAAgB;AACd,UAAI;AACF,gBAAQ,QAAQ;AAAA,MAClB,QAAQ;AAAA,MAAC;AACT,UAAI,KAAK,kBAAkB;AACzB,aAAKC,QAAO,KAAK,gBAAgB,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MACnD;AACA,WAAK,SAAS;AACd,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,SAAS;AAAA,IACT,kBAAkB,KAAK;AAAA,EACzB;AACF;;;A1B3IA,SAAS,gBAAAC,eAAc,iBAAAC,sBAAqB;AAC5C,SAAS,UAAAC,eAAc;AACvB,SAAS,WAAAC,UAAS,UAAAC,eAAc;AAChC,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAU9B,eAAsB,uBAAuB,QAAkC;AAC7E,QAAM,UAAU,qBAAqB,MAAM;AAC3C,MAAI,CAAC,WAAW,QAAQ,YAAY,KAAM,QAAO;AACjD,QAAM,UAAU,mBAAmB,QAAQ,UAAU,EAAE;AACvD,SAAQ,MAAM,qBAAqB,OAAO,MAAO;AACnD;AAEA,sBAAsB,CAAC,EAAE,OAAO,MAAM,uBAAuB,MAAM,CAAC;AAS7D,IAAM,2BAA2B;AAWjC,SAAS,uBAAuB,YAA6B;AAClE,QAAM,MAAM,QAAQ,IAAI,8BAA8B,KAAK;AAC3D,MAAI,IAAK,QAAO;AAChB,QAAM,UAAU,YAAY,KAAK;AACjC,MAAI,QAAS,QAAO;AACpB,SAAO;AACT;AAsBO,SAAS,uBACd,SACA,iBACA,aACQ;AACR,MAAI,SAAS;AACX,eAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,UAAI,IAAI,YAAY,MAAM,sBAAsB;AAC9C,cAAM,IAAI,QAAQ,GAAG;AACrB,YAAI,OAAO,MAAM,YAAY,EAAE,SAAS,EAAG,QAAO;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AACA,MAAI,iBAAiB;AACnB,UAAM,MACH,gBAAwB,WAAW,KACnC,gBAAwB,aAAa;AACxC,UAAM,MAAM,KAAK;AACjB,QAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,EAAG,QAAO;AAAA,EACxD;AACA,SAAO;AACT;AAYO,SAAS,qBACd,SACA,iBACA,aACoB;AACpB,MAAI,iBAAiB;AACnB,UAAM,MACH,gBAAwB,WAAW,KACnC,gBAAwB,aAAa;AACxC,UAAM,QAAQ,KAAK;AACnB,QAAI,OAAO,UAAU,SAAU,QAAO;AAAA,EACxC;AACA,MAAI,SAAS;AACX,eAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,UAAI,IAAI,YAAY,MAAM,oBAAoB;AAC5C,cAAM,QAAQ,QAAQ,GAAG;AACzB,YAAI,OAAO,UAAU,YAAY,MAAM,SAAS,EAAG,QAAO;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,oBAAoB,QAAyB;AAC3D,MAAI,WAAW,OAAW,QAAO;AACjC,MAAI,kBAAkB,MAAO,QAAO,GAAG,OAAO,IAAI,KAAK,OAAO,OAAO;AACrE,MAAI,OAAO,WAAW,SAAU,QAAO;AACvC,MAAI;AACF,WAAO,KAAK,UAAU,MAAM,KAAK,OAAO;AAAA,EAC1C,QAAQ;AACN,WAAO,OAAO;AAAA,EAChB;AACF;AAQA,IAAM,oBAAoB,oBAAI,IAAI;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAWM,SAAS,kBACd,QACS;AACT,WAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,UAAM,MAAM,OAAO,CAAC;AACpB,QAAI,IAAI,SAAS,YAAa,QAAO;AAMrC,QAAI,IAAI,SAAS,QAAQ;AACvB,YAAMC,WAAe,IAAI;AACzB,UAAI,MAAM,QAAQA,QAAO,GAAG;AAC1B,mBAAW,QAAQA,UAAkB;AACnC,cAAI,MAAM,SAAS,cAAe,QAAO;AAAA,QAC3C;AAAA,MACF;AACA;AAAA,IACF;AACA,QAAI,IAAI,SAAS,OAAQ;AACzB,UAAM,UAAe,IAAI;AACzB,QAAI,OAAO,YAAY,UAAU;AAC/B,UAAI,QAAQ,KAAK,EAAG,QAAO;AAC3B;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,OAAO,GAAG;AAC1B,iBAAW,QAAQ,SAAkB;AACnC,YAAI,KAAK,SAAS,UAAU,KAAK,QAAQ,KAAK,KAAK,KAAK,EAAG,QAAO;AAClE,YAAI,KAAK,SAAS,cAAe,QAAO;AAGxC,YAAI,KAAK,SAAS,WAAW,KAAK,SAAS,OAAQ,QAAO;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,6BAA6B;AACnC,IAAM,+BAA+B,KAAK,KAAK;AAC/C,IAAM,kCAAkC;AACxC,IAAM,iCAAiC;AAGvC,IAAM,6BAA6B;AAEnC,IAAM,uBACJ;AAsEK,SAAS,uBACd,gBACA,YAC+B;AAC/B,SAAO,iBAAiB,QAAQ;AAClC;AAOA,SAAS,uBAAuB,YAA6B;AAC3D,SAAO,eAAe,gBAAgB,eAAe;AACvD;AAOA,SAAS,qBAAqB,MAAsB;AAClD,SAAO,KAAK,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACxC;AAGO,SAAS,sBAAsB,MAAmC;AACvE,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,IAAI,KAAK,YAAY;AAC3B,SAAO,MAAM,qBAAqB,MAAM;AAC1C;AAeA,IAAM,iCACJ;AASK,SAAS,mBACd,UACA,uBACQ;AACR,MAAI,sBAAsB,QAAQ,EAAG,QAAO;AAC5C,SACE,yBACA,kDAAkD,QAAQ;AAE9D;AAgBA,SAAS,sBAAsB,OAAwC;AACrE,QAAM,WAAW;AACjB,QAAM,YAAmB,MAAM,QAAQ,UAAU,SAAS,IACtD,SAAS,YACT,CAAC;AAEL,MAAI,UAAU,WAAW,GAAG;AAC1B,UAAM,SAAS,UAAU,YAAY,UAAU;AAC/C,UAAM,IACJ,OAAO,WAAW,YAAY,OAAO,KAAK,IAAI,OAAO,KAAK,IAAI;AAChE,WAAO;AAAA;AAAA,IAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EACnB;AAEA,QAAM,MAAgB,CAAC,MAAM;AAC7B,QAAM,SAAS,UAAU,SAAS;AAClC,YAAU,QAAQ,CAAC,GAAG,MAAM;AAC1B,UAAM,OACH,OAAO,GAAG,aAAa,YAAY,EAAE,SAAS,KAAK,KACnD,OAAO,GAAG,SAAS,YAAY,EAAE,KAAK,KAAK,KAC5C;AACF,UAAM,SACJ,OAAO,GAAG,WAAW,YAAY,EAAE,OAAO,KAAK,IAAI,EAAE,OAAO,KAAK,IAAI;AACvE,QAAI,KAAK,KAAK,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI;AACnD,QAAI,OAAQ,KAAI,KAAK,MAAM,MAAM,IAAI;AACrC,QAAI,KAAK,MAAM;AAEf,UAAM,UAAiB,MAAM,QAAQ,GAAG,OAAO,IAAI,EAAE,UAAU,CAAC;AAChE,YAAQ,QAAQ,CAAC,KAAK,MAAM;AAC1B,YAAM,QACH,OAAO,KAAK,UAAU,YAAY,IAAI,MAAM,KAAK,KACjD,OAAO,QAAQ,YAAY,IAAI,KAAK,KACrC,UAAU,IAAI,CAAC;AACjB,YAAM,OACJ,OAAO,KAAK,gBAAgB,YAAY,IAAI,YAAY,KAAK,IACzD,WAAM,IAAI,YAAY,KAAK,CAAC,KAC5B;AACN,UAAI,KAAK,GAAG,IAAI,CAAC,OAAO,KAAK,KAAK,IAAI;AAAA,CAAI;AAAA,IAC5C,CAAC;AAED,QAAI;AAAA,MACF,GAAG,gBAAgB,OACf,wEACA;AAAA,IACN;AAAA,EACF,CAAC;AACD,SAAO,IAAI,KAAK,EAAE;AACpB;AAEA,SAAS,kBAAkB,MAAuB;AAChD,QAAM,aAAa,qBAAqB,IAAI,EAAE,YAAY;AAC1D,MAAI,CAAC,WAAY,QAAO;AAKxB,MAAI,WAAW,SAAS,GAAG,EAAG,QAAO;AAcrC,SAAO,wjBAAwjB,KAAK,UAAU;AAChlB;AAEA,SAAS,iBAAiB,MAAuB;AAC/C,QAAM,aAAa,qBAAqB,IAAI,EAAE,YAAY;AAC1D,MAAI,CAAC,WAAY,QAAO;AAGxB,SAAO,uOAAuO,KAAK,UAAU;AAC/P;AAEA,SAAS,qBAAqB,MAAuB;AACnD,QAAM,aAAa,qBAAqB,IAAI,EAAE,YAAY;AAC1D,MAAI,kBAAkB,UAAU,KAAK,iBAAiB,UAAU,EAAG,QAAO;AAI1E,MAAI,iDAAiD,KAAK,UAAU,GAAG;AACrE,WAAO;AAAA,EACT;AAIA,MAAI,WAAW,SAAS,GAAI,QAAO;AAKnC,SAAO,uIAAuI,KAAK,UAAU;AAAA;AAAA;AAAA,EAI3J,8CAA8C,KAAK,UAAU,KAC7D,0CAA0C,KAAK,UAAU;AAC7D;AAEA,SAAS,sBAAsB,UAAwC;AACrE,QAAM,OAAO,qBAAqB,SAAS,IAAI,EAAE,MAAM,IAAI;AAC3D,SAAO,KAAK,UAAU;AAAA,IACpB;AAAA,IACA,WAAW,SAAS;AAAA,IACpB,OAAO,SAAS;AAAA,IAChB,OAAO,SAAS;AAAA,EAClB,CAAC;AACH;AAEO,SAAS,iCACd,OACA,UACsB;AACtB,MAAI,MAAM,YAAY,MAAO,QAAO,EAAE,UAAU,OAAO,QAAQ,WAAW;AAC1E,MAAI,SAAS,QAAS,QAAO,EAAE,UAAU,OAAO,QAAQ,QAAQ;AAChE,MAAI,MAAM,QAAS,QAAO,EAAE,UAAU,OAAO,QAAQ,UAAU;AAI/D,MAAI,MAAM,mBAAoB,QAAO,EAAE,UAAU,OAAO,QAAQ,WAAW;AAO3E,MAAI,SAAS,YAAY;AAYvB,QAAI,uBAAuB,SAAS,UAAU,GAAG;AAC/C,UAAI,MAAM,YAAY,4BAA4B;AAChD,eAAO,EAAE,UAAU,OAAO,QAAQ,eAAe;AAAA,MACnD;AACA,YAAM,cAAc,SAAS,OAAO,KAAK,IAAI;AAC7C,UAAI,cAAc,MAAM,YAAY,8BAA8B;AAChE,eAAO,EAAE,UAAU,OAAO,QAAQ,cAAc;AAAA,MAClD;AACA,aAAO,EAAE,UAAU,MAAM,QAAQ,YAAY;AAAA,IAC/C;AACA,WAAO;AAAA,MACL,UAAU;AAAA,MACV,QAAQ,SAAS,WAAW,QAAQ,MAAM,GAAG;AAAA,IAC/C;AAAA,EACF;AACA,MAAI,MAAM,YAAY,4BAA4B;AAChD,WAAO,EAAE,UAAU,OAAO,QAAQ,eAAe;AAAA,EACnD;AACA,QAAM,MAAM,SAAS,OAAO,KAAK,IAAI;AACrC,MAAI,MAAM,MAAM,YAAY,8BAA8B;AACxD,WAAO,EAAE,UAAU,OAAO,QAAQ,cAAc;AAAA,EAClD;AAEA,QAAM,OAAO,qBAAqB,SAAS,IAAI;AAC/C,QAAM,WAAW,qBAAqB,SAAS,eAAe;AAC9D,MAAI,kBAAkB,IAAI,EAAG,QAAO,EAAE,UAAU,OAAO,QAAQ,WAAW;AAC1E,MAAI,iBAAiB,IAAI,EAAG,QAAO,EAAE,UAAU,OAAO,QAAQ,UAAU;AAKxE,MAAI,qBAAqB,QAAQ,GAAG;AAClC,WAAO,EAAE,UAAU,OAAO,QAAQ,eAAe;AAAA,EACnD;AAEA,QAAM,cACJ,SAAS,gBAAgB,SAAS,mBAAmB,SAAS;AAChE,MAAI,CAAC,YAAa,QAAO,EAAE,UAAU,OAAO,QAAQ,cAAc;AAElE,QAAM,YAAY,sBAAsB,QAAQ;AAChD,QAAM,aAAa,cAAc,MAAM;AACvC,MAAI,cAAc,MAAM,kBAAkB,KAAK,iCAAiC;AAC9E,WAAO,EAAE,UAAU,OAAO,QAAQ,cAAc;AAAA,EAClD;AAEA,MAAI,CAAC,MAAM;AACT,WAAO,EAAE,UAAU,MAAM,QAAQ,kCAAkC;AAAA,EACrE;AAEA,SAAO,EAAE,UAAU,MAAM,QAAQ,qBAAqB;AACxD;AAEA,SAAS,0BAAkC;AACzC,SAAO,KAAK,UAAU;AAAA,IACpB,MAAM;AAAA,IACN,SAAS;AAAA,MACP,MAAM;AAAA,MACN,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,qBAAqB,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AACH;AAOO,SAAS,2BACd,SACQ;AACR,QAAM,WAAW,QAAQ,IAAI,CAAC,EAAE,MAAM,OAAO,MAAM;AACjD,UAAM,SAAS,OAAO,SAAS,WAAW,OAAO,YAAY;AAC7D,UAAM,OAAO,OAAO,SAAS,UAAU,OAAO,UAAU,OAAO;AAC/D,WACE,kBAAkB,KAAK,QAAQ,oBAAoB,KAAK,UAAU,SAC1D,SAAS,WAAW,WAAW,8EACR,SAAS,UAAU,QAAQ;AAAA;AAAA,EACjC,IAAI;AAAA,EAEjC,CAAC;AACD,SAAO,KAAK,UAAU;AAAA,IACpB,MAAM;AAAA,IACN,SAAS;AAAA,MACP,MAAM;AAAA,MACN,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,SAAS,KAAK,aAAa,EAAE,CAAC;AAAA,IAChE;AAAA,EACF,CAAC;AACH;AAEA,SAAS,wBAAwBC,QAAkC;AACjE,MAAI;AACF,UAAM,UAAUT,cAAaS,QAAM,MAAM,EAAE,KAAK;AAChD,WAAO,WAAW;AAAA,EACpB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,6BAA6B,KAAiC;AACrE,MAAI,MAAM;AACV,SAAO,MAAM;AACX,UAAM,UAAU,wBAAwBF,MAAK,KAAK,WAAW,CAAC;AAC9D,QAAI,QAAS,QAAO;AACpB,UAAM,SAASD,SAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK,QAAO;AAC3B,UAAM;AAAA,EACR;AACF;AAEA,IAAM,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAMhC,IAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBtB,IAAM,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqB/B,IAAM,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBnC,IAAM,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBhC,IAAM,2BAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBjC,IAAM,oCAAoC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgB1C,SAAS,sBACP,QACU;AACV,QAAM,MAAgB,CAAC;AACvB,aAAW,OAAO,QAAQ;AACxB,QAAI,IAAI,SAAS,SAAU;AAC3B,QAAI,OAAO,IAAI,YAAY,UAAU;AACnC,UAAI,IAAI,QAAQ,KAAK,EAAG,KAAI,KAAK,IAAI,QAAQ,KAAK,CAAC;AAAA,IACrD,WAAW,MAAM,QAAQ,IAAI,OAAO,GAAG;AACrC,iBAAW,QAAQ,IAAI,SAAkB;AACvC,YACE,MAAM,SAAS,UACf,OAAO,KAAK,SAAS,YACrB,KAAK,KAAK,KAAK,GACf;AACA,cAAI,KAAK,KAAK,KAAK,KAAK,CAAC;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAWO,SAAS,0BACd,KACA,uBAAuB,MACvB,qBAA+B,CAAC,GAChC,UAAuC,CAAC,GACpB;AACpB,QAAM,QAAkB,CAAC;AAEzB,MAAI,QAAQ,oBAAoB,KAAK,GAAG;AACtC,UAAM;AAAA,MACJ;AAAA;AAAA,EAA0D,QAAQ,mBAAmB,KAAK,CAAC;AAAA,IAC7F;AAAA,EACF;AAIA,QAAM;AAAA,IACJ,QAAQ,kBACJ,2BACA,QAAQ,0BACN,oCACA;AAAA,EACR;AACA,aAAW,KAAK,oBAAoB;AAClC,QAAI,EAAE,KAAK,EAAG,OAAM,KAAK,EAAE,KAAK,CAAC;AAAA,EACnC;AACA,QAAM,aACJ,QAAQ,IAAI,mBAAmBC,MAAKJ,SAAQ,GAAG,SAAS;AAC1D,QAAM,eAAe,wBAAwBI,MAAK,YAAY,YAAY,WAAW,CAAC;AACtF,QAAM,kBAAkB,6BAA6B,GAAG;AAQxD,QAAM,YAAY,mBAAmB,KAAK,MAAM;AAChD,QAAM,aAAa,CAAC,CAAC,gBAAgB,CAAC,UAAU,SAAS,YAAY;AACrE,QAAM,gBACJ,CAAC,CAAC,mBAAmB,oBAAoB,gBACzC,CAAC,UAAU,SAAS,eAAe;AACrC,MAAI,WAAY,OAAM,KAAK,YAAY;AACvC,MAAI,cAAe,OAAM,KAAK,eAAe;AAC7C,MAAI,cAAc,cAAe,OAAM,KAAK,uBAAuB;AACnE,MAAI,qBAAsB,OAAM,KAAK,oBAAoB;AAEzD,QAAM,UAAU,MAAM,KAAK,MAAM;AACjC,MAAI,CAAC,QAAS,QAAO;AAErB,QAAME,SAAOF,MAAKH,QAAO,GAAG,mBAAmBC,YAAW,CAAC,KAAK;AAChE,MAAI;AACF,IAAAJ,eAAcQ,QAAM,SAAS,MAAM;AACnC,WAAOA;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,KAAK,sCAAsC,EAAE,OAAO,OAAO,GAAG,EAAE,CAAC;AACrE,WAAO;AAAA,EACT;AACF;AAMA,IAAM,oBAA4C;AAAA,EAChD,qBACE;AAAA,EACF,sBACE;AAAA,EACF,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,mBACE;AAAA,EACF,iBACE;AAAA,EACF,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,SAAS;AACX;AAGA,IAAM,wBAAwB,oBAAI,IAAY;AAsBvC,SAAS,oBACd,KACA,WACM;AACN,QAAM,QAAQ,IAAI;AAClB,MAAI,CAAC,MAAO;AAEZ,MAAI,CAAC,WAAW;AAEd,QAAI,MAAM,mBAAmB,EAAE,MAAM,CAAC;AACtC;AAAA,EACF;AAEA,MAAI,UAAU,MAAM;AAClB,QAAI,KAAK,oBAAoB,EAAE,MAAM,CAAC;AACtC;AAAA,EACF;AAEA,QAAM,SAAS,IAAI;AACnB,MAAI,UAAU,YAAY;AACxB,QAAI;AAAA,MACF;AAAA,MACA,EAAE,OAAO,QAAQ,UAAU,KAAK;AAAA,IAClC;AACA;AAAA,EACF;AAEA,QAAM,MAAM,UAAU;AACtB,QAAM,cAAc,SAAS,kBAAkB,MAAM,IAAI;AACzD,QAAM,UAAU,qCACd,cAAc,KAAK,WAAW,KAAK,SAAS,KAAK,MAAM,MAAM,EAC/D;AAEA,MAAI,sBAAsB,IAAI,GAAG,GAAG;AAClC,QAAI,MAAM,SAAS,EAAE,OAAO,QAAQ,UAAU,KAAK,CAAC;AACpD;AAAA,EACF;AACA,wBAAsB,IAAI,GAAG;AAC7B,MAAI,KAAK,SAAS,EAAE,OAAO,QAAQ,UAAU,KAAK,CAAC;AACrD;AAEO,IAAM,0BAAN,MAAyD;AAAA,EACrD,uBAAuB;AAAA,EACvB;AAAA,EACQ;AAAA,EAEjB,YAAY,SAAiB,QAA0B;AACrD,SAAK,UAAU;AACf,SAAK,SAAS;AAAA,EAChB;AAAA,EAES,gBAA0C,CAAC;AAAA,EAEpD,IAAI,WAAmB;AACrB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEQ,QAAQ,UAA+D;AAI7E,UAAM,OAAO,UAAU;AACvB,UAAM,YAAY,MAAM,SAAS,KAAK,KAAK,SAAS,CAAC,IAAI;AAGzD,UAAM,UAAU,WAAW,gBAAgB;AAC3C,UAAM,YAAY,WAAW,2BAA2B;AACxD,UAAM,aAAa,WAAW,+BAA+B;AAC7D,WAAO;AAAA,MACL,aAAa;AAAA,QACX,OAAO,UAAU,YAAY;AAAA,QAC7B;AAAA,QACA,WAAW,aAAa;AAAA,QACxB,YAAY,cAAc;AAAA,MAC5B;AAAA,MACA,cAAc;AAAA,QACZ,OAAO,WAAW;AAAA,QAClB,MAAM,WAAW;AAAA,QACjB,WAAW;AAAA,MACb;AAAA,MACA,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEQ,eACN,SAA0C,QACb;AAC7B,WAAO;AAAA,MACL,SAAS;AAAA,MACT,KAAK;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,eACN,OACA,SACS;AACT,QAAI,UAAU,WAAY,QAAO;AACjC,WAAO,KAAK,OAAO,YAAY,QAAQ,KAAK,iBAAiB,OAAO,MAAM;AAAA,EAC5E;AAAA,EAEQ,aAAa,SAAoD;AACvE,UAAM,QAAQ,SAAS;AACvB,QAAI,MAAM,QAAQ,KAAK,EAAG,QAAO;AACjC,QAAI,SAAS,OAAO,UAAU,UAAU;AACtC,aAAO,OAAO,KAAK,KAAgC,EAAE,SAAS,IAC1D,UACA;AAAA,IACN;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYQ,mBACN,KACA,iBACA,eACA,gBAKA;AACA,UAAM,QAAQ,MAAM,QAAQ,KAAK,OAAO,SAAS,IAC7C,KAAK,OAAO,UAAU,MAAM,IAC5B,KAAK,OAAO,YACV,CAAC,KAAK,OAAO,SAAS,IACtB,CAAC;AACP,QAAI,cAA6B;AACjC,QAAI,wBAAkC,CAAC;AACvC,QAAI,KAAK,OAAO,sBAAsB,OAAO;AAC3C,YAAM,UAAU,kBAAkB,KAAK,eAAe,cAAc;AACpE,UAAI,SAAS;AACX,YAAI,QAAQ,KAAM,OAAM,KAAK,QAAQ,IAAI;AACzC,sBAAc,QAAQ;AACtB,gCAAwB,QAAQ;AAAA,MAClC;AAAA,IACF;AACA,QAAI,gBAAiB,OAAM,KAAK,eAAe;AAC/C,WAAO,EAAE,OAAO,aAAa,sBAAsB;AAAA,EACrD;AAAA;AAAA,EAGQ,qBAA4C;AAClD,UAAM,QAAQ,KAAK,OAAO;AAC1B,QAAI,CAAC,SAAS,MAAM,WAAW,EAAG,QAAO;AACzC,UAAM,aAAa,IAAI;AAAA,MACrB,oBAAoB,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,YAAY,GAAG,CAAC,CAAC;AAAA,IAC1D;AACA,UAAM,SAAyB,CAAC;AAChC,UAAM,OAAO,oBAAI,IAAY;AAC7B,UAAM,UAAoB,CAAC;AAC3B,UAAM,OAAO,CAAC,QAAsB;AAClC,UAAI,KAAK,IAAI,IAAI,IAAI,EAAG;AACxB,WAAK,IAAI,IAAI,IAAI;AACjB,aAAO,KAAK,GAAG;AAAA,IACjB;AACA,eAAW,KAAK,OAAO;AACrB,YAAM,MAAM,WAAW,IAAI,OAAO,CAAC,EAAE,YAAY,CAAC;AAClD,UAAI,CAAC,KAAK;AACR,gBAAQ,KAAK,OAAO,CAAC,CAAC;AACtB;AAAA,MACF;AACA,WAAK,GAAG;AAIR,UAAI,IAAI,SAAS,QAAQ;AACvB,cAAM,QAAQ,WAAW,IAAI,oBAAoB;AACjD,YAAI,MAAO,MAAK,KAAK;AAAA,MACvB;AAAA,IACF;AAKA,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,QAAQ,CAAC,GAAG,WAAW,KAAK,CAAC,EAAE,KAAK,IAAI;AAC9C,UAAI,OAAO,WAAW,GAAG;AACvB,YAAI;AAAA,UACF;AAAA,UACA,EAAE,SAAS,MAAM;AAAA,QACnB;AAAA,MACF,OAAO;AACL,YAAI,KAAK,uCAAuC,EAAE,SAAS,MAAM,CAAC;AAAA,MACpE;AAAA,IACF;AACA,WAAO,OAAO,SAAS,IAAI,SAAS;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBQ,sBACN,uBACA,YACA,OAC+B;AAC/B,QAAI,KAAK,OAAO,0BAA0B,KAAM,QAAO;AACvD,QAAI,KAAK,OAAO,sBAAsB,MAAO,QAAO;AACpD,QAAI,sBAAsB,WAAW,EAAG,QAAO;AAE/C,UAAM,aAAa,wBAAwB;AAAA,MACzC,aAAa;AAAA,MACb,OAAO;AAAA,MACP;AAAA,IACF,CAAC;AACD,QAAI,WAAW,KAAK,WAAW,GAAG;AAKhC,UAAI;AAAA,QACF;AAAA,QAEA,EAAE,SAAS,uBAAuB,YAAY,YAAY,UAAU,EAAE;AAAA,MACxE;AACA,aAAO;AAAA,IACT;AACA,QAAI,MAAM,gDAAgD;AAAA,MACxD,SAAS,CAAC,GAAG,WAAW,cAAc;AAAA,MACtC,OAAO,WAAW,KAAK,IAAI,CAAC,QAAQ,IAAI,IAAI;AAAA,IAC9C,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAc,oBAA2C;AACvD,UAAM,QAAQ,MAAM;AAAA,MAClB,KAAK,OAAO;AAAA,MACZ,KAAK;AAAA,MACL,KAAK,OAAO;AAAA,IACd;AACA,UAAM,WAAW,OAAO,KAAK,CAAC,SAAS,KAAK,OAAO,UAAU;AAC7D,WAAO;AAAA,MACL,UAAU,UAAU;AAAA,MACpB,iBAAiB,OAAO,KAAK,CAAC,SAAS,KAAK,OAAO,MAAM,GAAG;AAAA,MAC5D,qBAAqB,UAAU;AAAA,MAC/B,aAAa,CAAC,CAAC;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,+BAAwC;AAC9C,WAAO,4BAA4B;AAAA,MACjC,SAAS,KAAK,OAAO;AAAA,MACrB,YAAY,KAAK,OAAO;AAAA,MACxB,oBAAoB,KAAK,OAAO;AAAA,IAClC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,iBAAiB,UAGvB;AACA,WAAO;AAAA,MACL,WACE,SAAS,cAAc,SAAS,SAC5B,qBAAqB,SAAS,MAAM,IACpC,KAAK,OAAO;AAAA,MAClB,YAAY,KAAK,OAAO,2BAA2B;AAAA,IACrD;AAAA,EACF;AAAA;AAAA,EAGQ,2BAAwD;AAC9D,QAAI;AACJ,WAAO,MAAM;AACX,kBAAY,KAAK,kBAAkB;AACnC,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,wBACZ,gBACA,mBAAmB,MAAM,KAAK,kBAAkB,GAC9B;AAClB,QAAI,kBAAkB,KAAK,OAAO,qBAAqB,KAAM,QAAO;AACpE,UAAM,OAAO,MAAM,iBAAiB;AACpC,UAAM,SAAS,yBAAyB;AAAA,MACtC,YAAY,KAAK,OAAO;AAAA,MACxB,qBAAqB,KAAK;AAAA,MAC1B;AAAA,IACF,CAAC;AACD,QAAI,CAAC,QAAQ;AAIX,UAAI,KAAK,2BAA2B;AAAA,QAClC,qBAAqB,KAAK;AAAA,QAC1B,kBAAkB,KAAK;AAAA,QACvB;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,kBACZ,OACA,oBAMA,mBACyB;AACzB,UAAM,mBAAmB,KAAK,OAAO;AACrC,UAAM,eAAe,oBAAI,IAAkC;AAC3D,QAAI,qBAAqB,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU,GAAG;AACjE,mBAAa,IAAI,YAAY,CAAC,UAAU;AACtC,cAAM,UAAU,OAAO,MAAM,YAAY,WAAW,MAAM,QAAQ,KAAK,IAAI;AAC3E,YAAI,CAAC,SAAS;AACZ,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,SACE;AAAA,UAEJ;AAAA,QACF;AACA,gCAAwB,oBAAoB,OAAO;AACnD,YAAI,KAAK,qDAAqD;AAAA,UAC5D,YAAY;AAAA,UACZ,eAAe,QAAQ;AAAA,QACzB,CAAC;AACD,eAAO;AAAA,UACL,MAAM;AAAA,UACN,MACE;AAAA,QAGJ;AAAA,MACF,CAAC;AAAA,IACH;AACA,UAAM,MAAM,MAAM,qBAAqB,OAAO,kBAAkB,YAAY;AAC5E,QAAI,MAAM,GAAG,QAAQ,CAAC,SAAwB;AAC5C,4BAAsB,oBAAoB,MAAM,gBAAgB;AAAA,IAClE,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEQ,0BACN,QACA,YACwB;AACxB,aAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,YAAM,MAAM,OAAO,CAAC;AACpB,UAAI,IAAI,SAAS,UAAU,CAAC,MAAM,QAAQ,IAAI,OAAO,EAAG;AAExD,iBAAW,QAAQ,IAAI,SAAS;AAC9B,YAAI,KAAK,SAAS,iBAAiB,KAAK,eAAe,WAAY;AAEnE,cAAM,SAAS,KAAK;AACpB,YAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,MAAM,OAAO,UAAU,EAAE;AAAA,UAC3B;AAAA,QACF;AAEA,YAAI,OAAO,SAAS,QAAQ;AAC1B,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,MAAM,OAAO,OAAO,SAAS,EAAE;AAAA,UACjC;AAAA,QACF;AAEA,YAAI,OAAO,SAAS,QAAQ;AAC1B,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,MAAM,KAAK,UAAU,OAAO,KAAK;AAAA,UACnC;AAAA,QACF;AAEA,YAAI,OAAO,SAAS,aAAa,MAAM,QAAQ,OAAO,KAAK,GAAG;AAC5D,gBAAM,OAAO,OAAO,MACjB,OAAO,CAAC,MAAW,GAAG,SAAS,UAAU,OAAO,EAAE,SAAS,QAAQ,EACnE,IAAI,CAAC,MAAW,EAAE,IAAI,EACtB,KAAK,IAAI;AACZ,iBAAO;AAAA,YACL,MAAM;AAAA,YACN;AAAA,UACF;AAAA,QACF;AAEA,eAAO;AAAA,UACL,MAAM;AAAA,UACN,MAAM,KAAK,UAAU,MAAM;AAAA,QAC7B;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,iCACN,QACA,MACwB;AACxB,QAAI,KAAK,aAAa,sBAAsB;AAC1C,aAAO,KAAK,0BAA0B,QAAQ,KAAK,UAAU;AAAA,IAC/D;AACA,UAAM,QAAQ,eAAe,KAAK,KAAK;AACvC,QAAI,MAAM,WAAW,GAAG;AACtB,aAAO,EAAE,MAAM,SAAS,SAAS,iDAAiD;AAAA,IACpF;AACA,UAAM,WAAW,MAAM,IAAI,CAAC,MAAM,WAAW;AAAA,MAC3C;AAAA,MACA,QAAQ,KAAK;AAAA,QACX;AAAA,QACA,yBAAyB,KAAK,YAAY,KAAK;AAAA,MACjD;AAAA,IACF,EAAE;AACF,UAAM,WAAW,SAAS,OAAO,CAAC,UAAU,MAAM,WAAW,IAAI,EAAE;AACnE,QAAI,aAAa,EAAG,QAAO;AAC3B,QAAI,WAAW,SAAS,QAAQ;AAC9B,UAAI,KAAK,mDAAmD;AAAA,QAC1D,YAAY,KAAK;AAAA,QACjB;AAAA,QACA,OAAO,SAAS;AAAA,MAClB,CAAC;AAAA,IACH;AACA,WAAO,uBAAuB,QAAQ;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYQ,gBACN,SACQ;AACR,UAAM,UAAW,SAAiB;AAGlC,WAAO;AAAA,MACL;AAAA,MACA,QAAQ;AAAA,MACR,KAAK,OAAO;AAAA,IACd;AAAA,EACF;AAAA,EAEQ,8BAA8B,UAA0C;AAC9E,UAAM,aAAa,KAAK,OAAO;AAC/B,QAAI,cAAc,UAAU;AAC1B,YAAM,SAAS,WAAW,QAAQ,KAAK,WAAW,SAAS,YAAY,CAAC;AACxE,UAAI,WAAW,WAAW,WAAW,OAAQ,QAAO;AAEpD,YAAM,QAAQ,SAAS,YAAY;AACnC,iBAAW,CAAC,KAAK,QAAQ,KAAK,OAAO,QAAQ,UAAU,GAAG;AACxD,YAAI,IAAI,YAAY,MAAM,UAAU,aAAa,WAAW,aAAa,SAAS;AAChF,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AASA,QAAI,sBAAsB,QAAQ,EAAG,QAAO;AAE5C,WAAO,KAAK,OAAO,0BAA0B;AAAA,EAC/C;AAAA,EAEQ,qBACN,MACA,WACA,UACM;AACN,UAAM,UAAU;AAAA,MACd,MAAM;AAAA,MACN,UAAU;AAAA,QACR,SAAS;AAAA,QACT,YAAY;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACF,WAAK,OAAO,MAAM,KAAK,UAAU,OAAO,IAAI,IAAI;AAAA,IAClD,SAAS,OAAO;AACd,UAAI,KAAK,oCAAoC;AAAA,QAC3C;AAAA,QACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,qBACN,KACA,MACS;AACT,QAAI,IAAI,SAAS,kBAAmB,QAAO;AAC3C,UAAM,YAAY,IAAI;AACtB,UAAM,UAAU,IAAI;AACpB,QAAI,CAAC,aAAa,CAAC,SAAS,QAAS,QAAO;AAE5C,QAAI,QAAQ,YAAY,gBAAgB;AACtC,YAAM,WAAW,QAAQ,aAAa;AACtC,YAAM,WAAW,KAAK,8BAA8B,QAAQ;AAE5D,UAAI,aAAa,SAAS;AACxB,aAAK,qBAAqB,MAAM,WAAW;AAAA,UACzC,UAAU;AAAA,UACV,cAAc,QAAQ,SAAS,CAAC;AAAA,UAChC,WAAW,QAAQ;AAAA,QACrB,CAAC;AACD,YAAI,KAAK,gCAAgC;AAAA,UACvC;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH,OAAO;AACL,cAAM,cAAc;AAAA,UAClB;AAAA,UACA,KAAK,OAAO;AAAA,QACd;AACA,aAAK,qBAAqB,MAAM,WAAW;AAAA,UACzC,UAAU;AAAA,UACV,SAAS;AAAA,UACT,WAAW,QAAQ;AAAA,QACrB,CAAC;AACD,YAAI,KAAK,+BAA+B;AAAA,UACtC;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,IACT;AAIA,SAAK,qBAAqB,MAAM,WAAW,CAAC,CAAC;AAC7C,QAAI,MAAM,gCAAgC;AAAA,MACxC;AAAA,MACA,SAAS,QAAQ;AAAA,IACnB,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEQ,mBACN,iBAC6B;AAC7B,QAAI,CAAC,gBAAiB,QAAO;AAC7B,UAAM,SAAS,KAAK,OAAO;AAC3B,UAAM,MACH,gBAAwB,MAAM,KAC9B,gBAAwB,aAAa;AACxC,UAAM,SAAS,KAAK;AACpB,UAAM,QAA2B;AAAA,MAC/B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,WAAO,MAAM,SAAS,MAAM,IAAI,SAAS;AAAA,EAC3C;AAAA,EAEQ,iBAAiB,SAAyD;AAChF,WAAO;AAAA,MACJ,SAAiB;AAAA,MAClB,QAAQ;AAAA,MACR,KAAK,OAAO;AAAA,IACd;AAAA,EACF;AAAA,EAEQ,iBACN,SACS;AACT,WAAO,KAAK,iBAAiB,OAAO,MAAM;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,yBAAiC;AACvC,WAAO,uBAAuB,KAAK,OAAO,eAAe;AAAA,EAC3D;AAAA,EAEQ,qBAGN;AACA,QAAI,yBAAyB,EAAG,QAAO,CAAC;AAExC,WAAO;AAAA,MACL,UAAU;AAAA,MACV,iBACE,QAAQ,IAAI,wCAAwC,SAChD,eACA;AAAA,IACR;AAAA,EACF;AAAA,EAEQ,eACN,QACQ;AACR,aAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,YAAM,MAAM,OAAO,CAAC;AACpB,UAAI,IAAI,SAAS,OAAQ;AAEzB,UAAI,OAAO,IAAI,YAAY,UAAU;AACnC,eAAO,OAAO,IAAI,OAAO,EAAE,KAAK;AAAA,MAClC;AAEA,UAAI,MAAM,QAAQ,IAAI,OAAO,GAAG;AAC9B,cAAM,OAAQ,IAAI,QACf,OAAO,CAAC,SAAS,KAAK,SAAS,UAAU,OAAO,KAAK,SAAS,QAAQ,EACtE,IAAI,CAAC,SAAc,OAAO,KAAK,IAAI,EAAE,KAAK,CAAC,EAC3C,OAAO,OAAO,EACd,KAAK,GAAG;AACX,YAAI,KAAM,QAAO;AAAA,MACnB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,gBACN,QACQ;AACR,UAAM,SAAS,KAAK,eAAe,MAAM,EACtC,QAAQ,QAAQ,GAAG,EACnB,QAAQ,sBAAsB,GAAG,EACjC,KAAK;AAER,QAAI,CAAC,OAAQ,QAAO;AAEpB,UAAM,OAAO,oBAAI,IAAI;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,QAAQ,OACX,MAAM,GAAG,EACT,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,OAAO,EACd,OAAO,CAAC,SAAS,CAAC,KAAK,IAAI,KAAK,YAAY,CAAC,CAAC;AAEjD,UAAM,UAAU,MAAM,SAAS,IAAI,QAAQ,OAAO,MAAM,GAAG,EAAE,OAAO,OAAO,GACxE,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,EAC1D,KAAK,GAAG;AAEX,WAAO,UAAU;AAAA,EACnB;AAAA,EAEA,MAAc,oBACZ,SAC6D;AAC7D,UAAM,SAAS,MAAM,KAAK,SAAS,OAAO;AAC1C,UAAM,SAAS,OAAO,OAAO,UAAU;AAEvC,QAAI,OAAO;AACX,QAAI,YAAY;AAChB,UAAM,YAAsC,CAAC;AAC7C,QAAI,eAAe,KAAK,eAAe,MAAM;AAC7C,QAAI,QAA8B,KAAK,QAAQ;AAC/C,QAAI;AAEJ,WAAO,MAAM;AACX,YAAM,EAAE,OAAO,KAAK,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,KAAM;AAEV,cAAS,MAAc,MAAM;AAAA,QAC3B,KAAK;AACH,kBAAS,MAAc,SAAS;AAChC;AAAA,QACF,KAAK;AACH,uBAAc,MAAc,SAAS;AACrC;AAAA,QACF,KAAK;AACH,oBAAU,KAAK;AAAA,YACb,MAAM;AAAA,YACN,YAAa,MAAc;AAAA,YAC3B,UAAW,MAAc;AAAA,YACzB,OAAQ,MAAc;AAAA,YACtB,kBAAmB,MAAc;AAAA,UACnC,CAAQ;AACR;AAAA,QACF,KAAK;AACH,yBAAgB,MAAc,gBAAgB;AAC9C,kBAAS,MAAc,SAAS;AAChC,6BAAoB,MAAc,oBAAoB;AACtD;AAAA,MACJ;AAAA,IACF;AAEA,UAAM,UAAoC,CAAC;AAC3C,QAAI,WAAW;AACb,cAAQ,KAAK,EAAE,MAAM,aAAa,MAAM,UAAU,CAAQ;AAAA,IAC5D;AACA,QAAI,MAAM;AACR,cAAQ,KAAK,EAAE,MAAM,QAAQ,MAAM,iBAAiB,CAAQ;AAAA,IAC9D;AACA,YAAQ,KAAK,GAAG,SAAS;AAEzB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,UAAU;AAAA,QACR,IAAI,WAAW;AAAA,QACf,WAAW,oBAAI,KAAK;AAAA,QACpB,SAAS,KAAK;AAAA,MAChB;AAAA,MACA;AAAA,MACA,UAAU,CAAC;AAAA,IACb;AAAA,EACF;AAAA,EAEA,MAAM,WACJ,SAC6D;AAC7D,UAAM,SAAS,MAAM,KAAK,kBAAkB,OAAO;AACnD,QAAI,KAAK,OAAO,YAAY,KAAM,QAAO;AACzC,UAAM,YAAY,6BAA6B,IAAI;AACnD,WAAO;AAAA,MACL,GAAG;AAAA,MACH,SAAS,OAAO,QAAQ,QAAQ,CAAC,SAAS;AACxC,cAAM,OAAO,UAAU,IAAW;AAClC,eAAO,OAAO,CAAC,IAAyC,IAAI,CAAC;AAAA,MAC/D,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAc,kBACZ,SAC6D;AAC7D,QAAI,CAAC,KAAK,iBAAiB,OAAO,KAAK,KAAK,aAAa,OAAc,MAAM,cAAc,kBAAkB,QAAQ,MAAM,GAAG;AAC5H,aAAO,KAAK,oBAAoB,OAAO;AAAA,IACzC;AACA,UAAM,WAA8B,CAAC;AACrC,UAAM,QAAQ,KAAK,aAAa,OAAc;AAC9C,UAAM,WAAW,KAAK,gBAAgB,OAAO;AAC7C,UAAM,MAAM,MAAM,0BAA0B,KAAK,OAAO,KAAK,QAAQ;AAIrE,UAAM,mBAAmB;AAAA,MACvB,KAAK,iBAAiB,OAAO;AAAA,MAC7B,KAAK;AAAA,IACP;AACA,UAAM,kBAAkB;AAAA,MACtB,KAAK,iBAAiB,OAAO;AAAA,MAC7B,KAAK,mBAAmB,QAAQ,eAAe;AAAA,IACjD;AAGA,UAAM,UAAU;AAAA,MACd;AAAA,MACA,GAAG,gBAAgB,KAAK,KAAK,KAAK,QAAQ,aAAa,KAAK,UAAU,CAAC,KAAK,OAAO,UAAU,KAAK,iBAAiB,OAAO,KAAK,IAAI,CAAC,CAAC;AAAA,IACvI;AACA,UAAM,KAAK,iBAAiB,SAAS,eAAe;AAOpD,UAAM,iBAAiB,KAAK,iBAAiB,OAAO;AAEpD,QACE,UAAU,YACT,KAAK,mBAAmB,KACtB,KAAK,OAAO,0BAA0B,QACrC,KAAK,OAAO,sBAAsB,QACtC;AACA,aAAO,KAAK,oBAAoB,OAAO;AAAA,IACzC;AAMA,QAAI,gBAAgB;AAClB,aAAO,KAAK,oBAAoB,OAAO;AAAA,IACzC;AAEA,QAAI,KAAK,eAAe,OAAO,OAAO,GAAG;AACvC,UAAI,KAAK,kCAAkC;AAAA,QACzC;AAAA,QACA,eAAe,KAAK,iBAAiB,OAAO;AAAA,QAC5C,qBAAqB,QAAQ,kBACzB,OAAO,KAAK,QAAQ,eAAe,IACnC,CAAC;AAAA,MACP,CAAC;AACD,YAAM,OAAO,KAAK,gBAAgB,QAAQ,MAAM;AAChD,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,KAAK,CAAC;AAAA,QAChC,cAAc,KAAK,eAAe,MAAM;AAAA,QACxC,OAAO,KAAK,QAAQ,EAAE,cAAc,GAAG,eAAe,EAAE,CAAC;AAAA,QACzD,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,EAAE;AAAA,QAC9B,UAAU;AAAA,UACR,IAAI,WAAW;AAAA,UACf,WAAW,oBAAI,KAAK;AAAA,UACpB,SAAS,KAAK;AAAA,QAChB;AAAA,QACA,kBAAkB;AAAA,UAChB,eAAe;AAAA,YACb,WAAW;AAAA,YACX,MAAM;AAAA,UACR;AAAA,QACF;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAMA,QAAI,CAAC,kBAAkB,QAAQ,MAAM,GAAG;AACtC,UAAI,KAAK,+CAA+C;AACxD,aAAO;AAAA,QACL,SAAS,CAAC;AAAA,QACV,cAAc,KAAK,eAAe,MAAM;AAAA,QACxC,OAAO,KAAK,QAAQ,EAAE,cAAc,GAAG,eAAe,EAAE,CAAC;AAAA,QACzD,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,EAAE;AAAA,QAC9B,UAAU;AAAA,UACR,IAAI,WAAW;AAAA,UACf,WAAW,oBAAI,KAAK;AAAA,UACpB,SAAS,KAAK;AAAA,QAChB;AAAA,QACA,kBAAkB;AAAA,UAChB,eAAe,EAAE,WAAW,MAAM,MAAM,sBAAsB;AAAA,QAChE;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,kCAA8B,SAAS,eAAe;AAEtD,UAAM,uBACJ,QAAQ,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,SAAS,WAAW,EACrE,SAAS;AAOd,QAAI,CAAC,sBAAsB;AACzB,4BAAsB,EAAE;AACxB,0BAAoB,EAAE;AACtB,uBAAiB,EAAE;AAAA,IACrB;AAEA,UAAM,qBAAqB,CAAC,CAAC,mBAAmB,EAAE;AAClD,UAAM,wBAAwB,CAAC,sBAAsB;AAErD,UAAM,UACJ,kCAAkC,IAAI,QAAQ,MAAa;AAAA;AAAA;AAAA,IAI3D,qBAAqB,QAAQ,QAAQ,uBAAuB;AAAA,MAC1D,gBAAgB,oBAAI,IAAY;AAAA,MAChC,uBAAuB,KAAK,6BAA6B;AAAA,IAC3D,CAAC;AAKH,UAAM,WAAW,MAAM,qBAAqB;AAAA,MAC1C,SAAS,KAAK,OAAO,WAAW;AAAA,MAChC,aAAa,KAAK,OAAO,eAAe,KAAK,OAAO;AAAA,MACpD,SAAS,KAAK,OAAO;AAAA,MACrB,SAAS;AAAA,IACX,CAAC;AACD,UAAM,UAAU,SAAS;AAKzB,UAAM,CAAC,eAAe,YAAY,sBAAsB,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC5E,oBAAoB;AAAA,MACpB,iBAAiB,OAAO;AAAA,MACxB,KAAK,wBAAwB,cAAc;AAAA,IAC7C,CAAC;AACD,UAAM,mBAAmB;AAAA,MACvB;AAAA,MACA,KAAK,OAAO,0BAA0B;AAAA,MACtC,sBAAsB,QAAQ,MAAM;AAAA;AAAA;AAAA,MAGpC,EAAE,iBAAiB,OAAO,oBAAoB,sBAAsB,EAAE,EAAE;AAAA,IAC1E;AACA,UAAM,EAAE,OAAO,cAAc,MAAM,SAAS,IAAI,aAAa,SAAS,OAAO;AAI7E,UAAM,kBAAkB,MAAM,uBAAuB;AAAA,MACnD;AAAA,MACA;AAAA,MACA,SAAS,KAAK,OAAO,yBAAyB;AAAA,MAC9C,GAAG,KAAK,iBAAiB,QAAQ;AAAA,IACnC,CAAC;AACD,UAAM,UAAU,aAAa;AAAA,MAC3B,YAAY;AAAA,MACZ,iBAAiB,KAAK,OAAO,oBAAoB;AAAA,MACjD,kBAAkB;AAAA,MAClB,OAAO;AAAA,MACP,gBAAgB,KAAK,OAAO;AAAA,MAC5B,WAAW,KAAK,mBAAmB,KAAK,QAAW,aAAa,EAAE;AAAA,MAClE,iBAAiB,KAAK,OAAO;AAAA,MAC7B,iBACE,KAAK,OAAO,cAAc,aAAa,CAAC,WAAW,IAAI;AAAA,MACzD,wBAAwB;AAAA,MACxB,YAAY;AAAA,MACZ,GAAG,KAAK,mBAAmB;AAAA,MAC3B;AAAA,MACA;AAAA,IACF,CAAC;AAED,QAAI,KAAK,uBAAuB;AAAA,MAC9B;AAAA,MACA,OAAO;AAAA,MACP,gBAAgB,KAAK;AAAA,MACrB,YAAY,QAAQ;AAAA,MACpB;AAAA,IACF,CAAC;AAED,UAAM,EAAE,OAAAC,OAAM,IAAI,MAAM,OAAO,eAAoB;AACnD,UAAM,EAAE,iBAAAC,iBAAgB,IAAI,MAAM,OAAO,UAAe;AAExD,UAAM,OAAOD,OAAM,SAAS,SAAS;AAAA,MACnC;AAAA,MACA,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,MAC9B,KAAK,eAAe;AAAA,QAClB,uBAAuB,KAAK,OAAO;AAAA,QACnC,QAAQ;AAAA,MACV,CAAC;AAAA,MACD,OAAO,QAAQ,aAAa;AAAA,IAC9B,CAAC;AAED,QAAI,kBAAkB;AACpB,WAAK,GAAG,QAAQ,MAAM;AACpB,aAAKE,QAAO,gBAAgB,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAC9C,CAAC;AAAA,IACH;AAEA,UAAM,KAAKD,iBAAgB,EAAE,OAAO,KAAK,OAAQ,CAAC;AAElD,QAAI,eAAe;AACnB,QAAI,eAAe;AACnB,QAAI,aASA,CAAC;AACL,UAAM,YAAgE,CAAC;AAMvE,UAAM,kBAAkB,oBAAI,IAG1B;AAKF,QAAI,mBAAmB;AAEvB,UAAM,SAAS,MAAM,IAAI,QAMvB,CAACE,UAAS,WAAW;AACrB,YAAM,UAAU,MAAM;AACpB,YAAI;AACF,cAAI,CAAC,KAAK,UAAU,KAAK,aAAa,KAAM,MAAK,KAAK;AAAA,QACxD,QAAQ;AAAA,QAAC;AAAA,MACX;AAEA,SAAG,GAAG,QAAQ,CAAC,SAAS;AACtB,YAAI,CAAC,KAAK,KAAK,EAAG;AAClB,YAAI;AACF,gBAAM,QAA6B,KAAK,MAAM,IAAI;AAIlD,gBAAM,MACJ,MAAM,SAAS,kBAAkB,MAAM,QACnC,EAAE,GAAG,MAAM,OAAO,YAAY,MAAM,WAAW,IAC/C;AAEN,cAAI,MAAM,SAAS,gBAAgB;AACjC,+BAAmB;AAAA,UACrB;AAEA,cAAI,KAAK,qBAAqB,KAAK,IAAI,GAAG;AACxC;AAAA,UACF;AAEA,cAAI,IAAI,SAAS,YAAY,IAAI,YAAY,QAAQ;AACnD,gBAAI,IAAI,YAAY;AAClB,iCAAmB,IAAI,IAAI,UAAU;AAAA,YACvC;AACA,gCAAoB,KAAK,QAAQ;AACjC,6BAAiB,KAAK;AAAA,cACpB,uBAAuB,KAAK,OAAO;AAAA,YACrC,CAAC;AAAA,UACH;AAEA,cAAI,IAAI,SAAS,oBAAoB;AACnC,iCAAqB,GAAG;AACxB;AAAA,UACF;AAEA,cACE,IAAI,SAAS,eACb,IAAI,SAAS,WACb,CAAC,kBACD;AACA,uBAAW,SAAS,IAAI,QAAQ,SAAS;AACvC,kBAAI,MAAM,SAAS,UAAU,MAAM,MAAM;AACvC,gCAAgB,MAAM;AAAA,cACxB;AACA,kBAAI,MAAM,SAAS,cAAc,MAAM,UAAU;AAC/C,gCAAgB,MAAM;AAAA,cACxB;AACA,kBAAI,MAAM,SAAS,cAAc,MAAM,MAAM,MAAM,MAAM;AACvD,oBAAI,sBAAsB,MAAM,IAAI,GAAG;AAGrC,wBAAM,cAAe,MAAM,SAAS,CAAC;AAIrC,kCAAgB,sBAAsB,WAAW;AACjD;AAAA,gBACF;AAEA,oBAAI,MAAM,SAAS,gBAAgB;AACjC,wBAAM,cAAe,MAAM,SAAS,CAAC;AAIrC,wBAAM,OAAQ,aAAa,QAAmB;AAC9C,sBAAI,wBAAwB;AAC1B,0BAAM,eAAe;AAAA,sBACnB;AAAA,sBACA,MAAM;AAAA,sBACN;AAAA,oBACF;AACA,oCAAgB,aAAa;AAC7B,8BAAU,KAAK;AAAA,sBACb,IAAI,aAAa;AAAA,sBACjB,MAAM,aAAa;AAAA,sBACnB,MAAM,aAAa;AAAA,oBACrB,CAAC;AACD;AAAA,kBACF;AACA,kCAAgB;AAAA;AAAA,EAAO,IAAI;AAAA;AAAA;AAAA;AAAA;AAC3B;AAAA,gBACF;AAEA,0BAAU,KAAK;AAAA,kBACb,IAAI,MAAM;AAAA,kBACV,MAAM,MAAM;AAAA,kBACZ,MAAM,MAAM,SAAS,CAAC;AAAA,gBACxB,CAAC;AAAA,cACH;AAAA,YACF;AAAA,UACF;AAEA,cACE,IAAI,SAAS,yBACb,IAAI,iBACJ,IAAI,UAAU,QACd;AACA,gBACE,IAAI,cAAc,SAAS,cAC3B,IAAI,cAAc,MAClB,IAAI,cAAc,MAClB;AACA,8BAAgB,IAAI,IAAI,OAAO;AAAA,gBAC7B,IAAI,IAAI,cAAc;AAAA,gBACtB,MAAM,IAAI,cAAc;AAAA,gBACxB,WAAW;AAAA,cACb,CAAC;AAAA,YACH;AAAA,UACF;AAEA,cACE,IAAI,SAAS,yBACb,IAAI,SACJ,IAAI,UAAU,QACd;AACA,gBAAI,IAAI,MAAM,SAAS,gBAAgB,IAAI,MAAM,MAAM;AACrD,8BAAgB,IAAI,MAAM;AAAA,YAC5B;AACA,gBAAI,IAAI,MAAM,SAAS,oBAAoB,IAAI,MAAM,UAAU;AAC7D,8BAAgB,IAAI,MAAM;AAAA,YAC5B;AACA,gBACE,IAAI,MAAM,SAAS,sBACnB,IAAI,MAAM,cACV;AACA,oBAAM,KAAK,gBAAgB,IAAI,IAAI,KAAK;AACxC,kBAAI,GAAI,IAAG,aAAa,IAAI,MAAM;AAAA,YACpC;AAAA,UACF;AAEA,cAAI,IAAI,SAAS,wBAAwB,IAAI,UAAU,QAAW;AAChE,kBAAM,KAAK,gBAAgB,IAAI,IAAI,KAAK;AACxC,gBAAI,IAAI;AACN,kBAAI,OAAgB,CAAC;AACrB,kBAAI;AACF,uBAAO,GAAG,YAAY,KAAK,MAAM,GAAG,SAAS,IAAI,CAAC;AAAA,cACpD,SAAS,KAAK;AACZ,oBAAI,KAAK,gCAAgC;AAAA,kBACvC,MAAM,GAAG;AAAA,kBACT,OAAO,OAAO,GAAG;AAAA,gBACnB,CAAC;AAAA,cACH;AACA,kBAAI,GAAG,SAAS,kBAAkB,wBAAwB;AACxD,sBAAM,cAAc;AACpB,sBAAM,OAAQ,aAAa,QAAmB;AAC9C,sBAAM,eAAe,+BAA+B,IAAI,GAAG,IAAI,IAAI;AACnE,gCAAgB,aAAa;AAC7B,0BAAU,KAAK;AAAA,kBACb,IAAI,aAAa;AAAA,kBACjB,MAAM,aAAa;AAAA,kBACnB,MAAM,aAAa;AAAA,gBACrB,CAAC;AAAA,cACH,OAAO;AACL,0BAAU,KAAK,EAAE,IAAI,GAAG,IAAI,MAAM,GAAG,MAAM,KAAK,CAAC;AAAA,cACnD;AACA,8BAAgB,OAAO,IAAI,KAAK;AAAA,YAClC;AAAA,UACF;AAEA,cAAI,IAAI,SAAS,UAAU;AACzB,gBAAI,IAAI,YAAY;AAClB,iCAAmB,IAAI,IAAI,UAAU;AAAA,YACvC;AAKA,gBACE,CAAC,gBACD,IAAI,YACJ,OAAO,IAAI,WAAW,YACtB,IAAI,OAAO,KAAK,EAAE,SAAS,GAC3B;AACA,6BAAe,IAAI;AAAA,YACrB;AAEA,yBAAa;AAAA,cACX,WAAW,IAAI;AAAA,cACf,SAAS,IAAI;AAAA,cACb,YAAY,IAAI;AAAA,cAChB,eAAe,IAAI;AAAA,cACnB,UAAU,IAAI;AAAA,cACd,OAAO,IAAI;AAAA,cACX,YAAY,IAAI;AAAA,cAChB,mBAAmB,IAAI,oBAAoB,IAAI,CAAC,YAAY;AAAA,gBAC1D,WAAW,OAAO;AAAA,gBAClB,aAAa,OAAO;AAAA,cACtB,EAAE;AAAA,YACJ;AACA,gBAAI,KAAK,uBAAuB;AAAA,cAC9B,WAAW,IAAI;AAAA,cACf,SAAS,IAAI;AAAA,cACb,SAAS,IAAI;AAAA,cACb,GAAG,oBAAoB,iBAAiB,GAAG,CAAC;AAAA,YAC9C,CAAC;AACD,oBAAQ;AACR,YAAAA,SAAQ;AAAA,cACN,GAAG;AAAA,cACH,MAAM;AAAA,cACN,UAAU;AAAA,cACV;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF,CAAC;AAED,SAAG,GAAG,SAAS,MAAM;AACnB,gBAAQ;AACR,QAAAA,SAAQ;AAAA,UACN,GAAG;AAAA,UACH,MAAM;AAAA,UACN,UAAU;AAAA,UACV;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAED,WAAK,GAAG,SAAS,CAAC,QAAQ;AACxB,YAAI,MAAM,iBAAiB,EAAE,OAAO,IAAI,QAAQ,CAAC;AACjD,gBAAQ;AACR,eAAO,GAAG;AAAA,MACZ,CAAC;AAED,WAAK,QAAQ,GAAG,QAAQ,CAAC,SAAiB;AACxC,YAAI,MAAM,UAAU,EAAE,MAAM,KAAK,SAAS,EAAE,MAAM,GAAG,GAAG,EAAE,CAAC;AAAA,MAC7D,CAAC;AAED,WAAK,OAAO,MAAM,UAAU,IAAI;AAAA,IAClC,CAAC;AAED,UAAM,UAAoC,CAAC;AAE3C,QAAI,OAAO,UAAU;AACnB,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,MAAM,OAAO;AAAA,MACf,CAAQ;AAAA,IACV;AAEA,QAAI,OAAO,MAAM;AACf,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,MAAM,OAAO;AAAA,QACb,kBAAkB;AAAA,UAChB,eAAe;AAAA,YACb,WAAW,OAAO,aAAa;AAAA,YAC/B,SAAS,OAAO,WAAW;AAAA,YAC3B,YAAY,OAAO,cAAc;AAAA,UACnC;AAAA,UACA,GAAI,OAAO,OAAO,OAAO,gCAAgC,WACrD;AAAA,YACE,WAAW;AAAA,cACT,0BACE,OAAO,MAAM;AAAA,YACjB;AAAA,UACF,IACA,CAAC;AAAA,QACP;AAAA,MACF,CAAC;AAAA,IACH;AAEA,eAAW,MAAM,OAAO,WAAW;AACjC,UAAI,GAAG,SAAS,oBAAoB;AAClC,gBAAQ,KAAK;AAAA,UACX,MAAM;AAAA,UACN,YAAY,GAAG;AAAA,UACf,UAAU,GAAG;AAAA,UACb,OAAO,KAAK,UAAU,GAAG,IAAI;AAAA,UAC7B,kBAAkB;AAAA,QACpB,CAAQ;AACR;AAAA,MACF;AAEA,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,OAAO;AAAA,QACP;AAAA,QACA;AAAA,MACF,IAAI,QAAQ,GAAG,MAAM,GAAG,MAAM;AAAA,QAC5B,WAAW,KAAK,OAAO;AAAA,QACvB,WAAW,mBAAmB,EAAE;AAAA,QAChC,WAAW,GAAG;AAAA,MAChB,CAAC;AACD,UAAI,KAAM;AACV,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,YAAY,GAAG;AAAA,QACf,UAAU;AAAA,QACV,OAAO,KAAK,UAAU,WAAW;AAAA,QACjC,kBAAkB;AAAA,MACpB,CAAQ;AAAA,IACV;AAEA,UAAM,QAAQ,KAAK,QAAQ,OAAO,KAAK;AAEvC,WAAO;AAAA,MACL;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,cAAc,KAAK;AAAA,QACjB,OAAO,UAAU,KAAK,CAAC,OAAO,GAAG,SAAS,kBAAkB,IACxD,eACA;AAAA,MACN;AAAA,MACA;AAAA,MACA,SAAS,EAAE,MAAM,EAAE,MAAM,QAAQ,EAAE;AAAA,MACnC,UAAU;AAAA,QACR,IAAI,OAAO,aAAa,WAAW;AAAA,QACnC,WAAW,oBAAI,KAAK;AAAA,QACpB,SAAS,KAAK;AAAA,MAChB;AAAA,MACA,kBAAkB;AAAA,QAChB,eAAe;AAAA,UACb,WAAW,OAAO,aAAa;AAAA,UAC/B,SAAS,OAAO,WAAW;AAAA,UAC3B,YAAY,OAAO,cAAc;AAAA,QACnC;AAAA,QACA,GAAI,OAAO,OAAO,OAAO,gCAAgC,WACrD;AAAA,UACE,WAAW;AAAA,YACT,0BACE,OAAO,MAAM;AAAA,UACjB;AAAA,QACF,IACA,CAAC;AAAA,MACP;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,SACJ,SAC2D;AAC3D,UAAM,SAAS,MAAM,KAAK,gBAAgB,OAAO;AACjD,WAAO;AAAA,MACL,GAAG;AAAA,MACH,QAAQ,uBAAuB,OAAO,QAAe,KAAK,OAAO,WAAW,IAAI;AAAA,IAClF;AAAA,EACF;AAAA,EAEA,MAAc,gBACZ,SAC2D;AAC3D,UAAM,WAA8B,CAAC;AACrC,UAAM,kBAAkB,KAAK,OAAO,oBAAoB;AACxD,UAAM,QAAQ,KAAK,aAAa,OAAc;AAC9C,UAAM,WAAW,KAAK,gBAAgB,OAAO;AAC7C,UAAM,MAAM,MAAM,0BAA0B,KAAK,OAAO,KAAK,QAAQ;AACrE,UAAM,iBAAiB,KAAK,iBAAiB,OAAO;AAGpD,UAAM,mBAAmB,iBACrB,KAAK,uBAAuB,IAC5B;AAAA,MACE,KAAK,iBAAiB,OAAO;AAAA,MAC7B,KAAK;AAAA,IACP;AAEJ,UAAM,kBAAkB,iBACpB,SACC;AAAA,MACC,KAAK,iBAAiB,OAAO;AAAA,MAC7B,KAAK,mBAAmB,QAAQ,eAAe;AAAA,IACjD;AACJ,UAAM,UAAU;AAAA,MACd;AAAA,MACA,GAAG,gBAAgB,KAAK,KAAK,KAAK,QAAQ,aAAa,KAAK,UAAU,CAAC,KAAK,OAAO,UAAU,KAAK,iBAAiB,OAAO,KAAK,IAAI,CAAC,CAAC;AAAA,IACvI;AACA,UAAM,KAAK,iBACP,WAAW,KAAK,GAAG,gBAAgB,iBAAiB,QAAQ,EAAE,IAC9D,iBAAiB,SAAS,eAAe;AAC7C,UAAM,UAAU,KAAK,QAAQ,KAAK,IAAI;AACtC,UAAM,iBAAiB,KAAK,eAAe,KAAK,IAAI;AACpD,UAAM,uBAAuB,KAAK,qBAAqB,KAAK,IAAI;AAChE,UAAM,SAAS,CAAC,MACd,MAAM,UACN,CAAC,CAAC,IAAI,KAAK,SAAS,MAAM,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,CAAC;AAMlE,UAAM,kBACJ,KAAK,OAAO,eACZ,OAAO,QAAQ,IAAI,iCAAiC;AACtD,UAAM,iBACJ,mBAAmB,OAAQ,WAAmB,KAAK,aAAa;AAClE,UAAM,6BACJ,KAAK,OAAO,qBACZ,OAAO,QAAQ,IAAI,8BAA8B;AAWnD,UAAM,gBAAgB;AAAA,MACpB,KAAK,OAAO,WAAW;AAAA,IACzB;AACA,UAAM,cAAc,KAAK,OAAO,eAAe,KAAK,OAAO;AAC3D,QAAI,WACF,kBAAkB,iBACd,EAAE,SAAS,KAAK,OAAO,SAAS,SAAS,kBAAkB,YAAY,MAAM,IAC7E,MAAM,qBAAqB;AAAA,MACzB,SAAS;AAAA,MACT;AAAA,MACA,SAAS,KAAK,OAAO;AAAA,MACrB,SAAS;AAAA,IACX,CAAC;AACP,QAAI,UAAU,SAAS;AAKvB,UAAM,oBAAoB,EAAE,SAAS,aAAa,CAAC,CAAC,eAAe;AAMnE,UAAM,SACJ,CAAC,kBAAkB,UAAU,aAAa,mBAAmB,QAAQ,MAAM,IAAI;AACjF,QAAI,QAAQ;AACV,YAAM,gBAAgB;AAAA,QACpB;AAAA,QACA,aAAa,CAAC,CAAC;AAAA,QACf,WAAW,KAAK,OAAO,cAAc;AAAA,MACvC;AACA,YAAMC,UAAS,IAAI,eAA0C;AAAA,QAC3D,MAAM,MAAM,YAAY;AACtB,qBAAW,QAAQ,EAAE,MAAM,gBAAgB,SAAS,CAAC;AACrD,cAAI;AACF,kBAAM,OAAO,MAAM,kBAAkB,aAAa;AAClD,kBAAM,KAAK,WAAW;AACtB,uBAAW,QAAQ,EAAE,MAAM,cAAc,GAAG,CAAC;AAC7C,uBAAW,QAAQ,EAAE,MAAM,cAAc,IAAI,OAAO,KAAK,CAAC;AAC1D,uBAAW,QAAQ,EAAE,MAAM,YAAY,GAAG,CAAC;AAC3C,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN,cAAc,eAAe,MAAM;AAAA,cACnC,OAAO,QAAQ,EAAE,cAAc,GAAG,eAAe,EAAE,CAAC;AAAA,cACpD,kBAAkB;AAAA,gBAChB,eAAe,EAAE,MAAM,UAAU,WAAW,MAAM,kBAAkB,KAAK;AAAA,cAC3E;AAAA,YACF,CAAC;AAAA,UACH,SAAS,OAAO;AACd,uBAAW,QAAQ,EAAE,MAAM,SAAS,MAAM,CAAC;AAAA,UAC7C,UAAE;AACA,uBAAW,MAAM;AAAA,UACnB;AAAA,QACF;AAAA,MACF,CAAC;AACD,aAAO,EAAE,QAAAA,SAAQ,SAAS,EAAE,MAAM,EAAE,MAAM,IAAI,cAAc,GAAG,EAAE,EAAE;AAAA,IACrE;AAEA,UAAM,QAAQ,CAAC,kBAAkB,UAAU,aAAa,kBAAkB,QAAQ,MAAM,IAAI;AAC5F,QAAI,OAAO;AAOT,YAAM,SAAS,iBAAiB,EAAE;AAClC,YAAM,QAAQ,MAAM,WAAW,uBAAuB,UAAU,MAAM,QAAQ,IAAI;AAClF,YAAM,UAAU,2BAA2B,QAAQ,MAAM;AACzD,YAAM,cAAc,YAAyC;AAC3D,YAAI,CAAC,MAAM,SAAU,QAAO,EAAE,UAAU,qBAAqB,WAAW,KAAK;AAC7E,YAAI,OAAO;AACT,cAAI;AACF,mBAAO,MAAM;AAAA,UACf,SAAS,OAAO;AACd,gBAAI,KAAK,qDAAqD,EAAE,OAAO,OAAO,KAAK,EAAE,CAAC;AAAA,UACxF;AAAA,QACF;AACA,YAAI,CAAC,OAAQ,QAAO,EAAE,UAAU,wBAAwB,WAAW,KAAK;AACxE,eAAO,oBAAoB,QAAQ,MAAM,UAAU;AAAA,UACjD,YAAY,MAAM,iBAAiB,OAAO;AAAA,UAC1C,aAAa;AAAA,UACb,aAAa,QAAQ;AAAA,UACrB,GAAI,QAAQ,SAAS,EAAE,QAAQ,IAAI,CAAC;AAAA,QACtC,CAAC;AAAA,MACH;AACA,YAAMA,UAAS,IAAI,eAA0C;AAAA,QAC3D,MAAM,MAAM,YAAY;AACtB,qBAAW,QAAQ,EAAE,MAAM,gBAAgB,SAAS,CAAC;AACrD,cAAI;AACF,kBAAM,SAAS,MAAM,YAAY;AACjC,kBAAM,KAAK,WAAW;AACtB,uBAAW,QAAQ,EAAE,MAAM,cAAc,GAAG,CAAC;AAC7C,uBAAW,QAAQ,EAAE,MAAM,cAAc,IAAI,OAAO,OAAO,SAAS,CAAC;AACrE,uBAAW,QAAQ,EAAE,MAAM,YAAY,GAAG,CAAC;AAC3C,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN,cAAc,eAAe,MAAM;AAAA,cACnC,OAAO,QAAQ,CAAC,CAAC;AAAA,cACjB,kBAAkB,EAAE,eAAe,EAAE,MAAM,iBAAiB,WAAW,OAAO,WAAW,kBAAkB,KAAK,EAAE;AAAA,YACpH,CAAC;AAAA,UACH,SAAS,OAAO;AACd,uBAAW,QAAQ,EAAE,MAAM,SAAS,MAAM,CAAC;AAAA,UAC7C,UAAE;AACA,uBAAW,MAAM;AAAA,UACnB;AAAA,QACF;AAAA,MACF,CAAC;AACD,aAAO,EAAE,QAAAA,SAAQ,SAAS,EAAE,MAAM,EAAE,MAAM,MAAM,SAAS,EAAE,EAAE;AAAA,IAC/D;AAEA,QAAI,KAAK,eAAe,OAAO,OAAO,KAAK,CAAC,gBAAgB;AAC1D,UAAI,KAAK,gCAAgC;AAAA,QACvC;AAAA,QACA,eAAe,KAAK,iBAAiB,OAAO;AAAA,QAC5C,qBAAqB,QAAQ,kBACzB,OAAO,KAAK,QAAQ,eAAe,IACnC,CAAC;AAAA,MACP,CAAC;AACD,YAAM,OAAO,KAAK,gBAAgB,QAAQ,MAAM;AAChD,YAAM,SAAS,WAAW;AAC1B,YAAMA,UAAS,IAAI,eAA0C;AAAA,QAC3D,MAAM,YAAY;AAChB,qBAAW,QAAQ,EAAE,MAAM,gBAAgB,SAAS,CAAC;AACrD,qBAAW,QAAQ,EAAE,MAAM,cAAc,IAAI,OAAO,CAAQ;AAC5D,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN,IAAI;AAAA,YACJ,OAAO;AAAA,UACT,CAAC;AACD,qBAAW,QAAQ,EAAE,MAAM,YAAY,IAAI,OAAO,CAAC;AACnD,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN,cAAc,eAAe,MAAM;AAAA,YACnC,OAAO,QAAQ,EAAE,cAAc,GAAG,eAAe,EAAE,CAAC;AAAA,YACpD,kBAAkB;AAAA,cAChB,eAAe;AAAA,gBACb,WAAW;AAAA,gBACX,MAAM;AAAA,cACR;AAAA,YACF;AAAA,UACF,CAAC;AACD,qBAAW,MAAM;AAAA,QACnB;AAAA,MACF,CAAC;AAED,aAAO;AAAA,QACL,QAAAA;AAAA,QACA,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,EAAE;AAAA,MAChC;AAAA,IACF;AAMA,QAAI,CAAC,kBAAkB,QAAQ,MAAM,GAAG;AACtC,UAAI,KAAK,6CAA6C;AACtD,YAAMA,UAAS,IAAI,eAA0C;AAAA,QAC3D,MAAM,YAAY;AAChB,qBAAW,QAAQ,EAAE,MAAM,gBAAgB,SAAS,CAAC;AACrD,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN,cAAc,eAAe,MAAM;AAAA,YACnC,OAAO,QAAQ,EAAE,cAAc,GAAG,eAAe,EAAE,CAAC;AAAA,YACpD,kBAAkB;AAAA,cAChB,eAAe,EAAE,WAAW,MAAM,MAAM,sBAAsB;AAAA,YAChE;AAAA,UACF,CAAC;AACD,qBAAW,MAAM;AAAA,QACnB;AAAA,MACF,CAAC;AACD,aAAO,EAAE,QAAAA,SAAQ,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,EAAE,EAAE;AAAA,IACnD;AAEA,QAAI,CAAC,eAAgB,+BAA8B,SAAS,eAAe;AAE3E,UAAM,uBACJ,QAAQ,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,SAAS,WAAW,EACrE,SAAS;AAOd,QAAI,CAAC,sBAAsB;AACzB,4BAAsB,EAAE;AACxB,0BAAoB,EAAE;AACtB,uBAAiB,EAAE;AAAA,IACrB;AAKA,UAAM,iBACJ,kBAAkB,iBACd,OACA,6BAA6B,IAAI,QAAQ,QAAe;AAAA,MACtD;AAAA,MACA,YAAY,mBAAmB,KAAK,OAAO,kBAAkB,aAAa;AAAA,IAC5E,CAAC;AAEP,QAAI,gBAAgB,SAAS,QAAQ;AAInC,UAAI,KAAK,8CAA8C;AAAA,QACrD,YAAY;AAAA,QACZ,SAAS;AAAA,QACT,QAAQ,eAAe;AAAA,MACzB,CAAC;AACD,YAAM,OAAO,uBAAuB,eAAe,MAAM;AACzD,YAAMA,UAAS,IAAI,eAA0C;AAAA,QAC3D,MAAM,YAAY;AAChB,qBAAW,QAAQ,EAAE,MAAM,gBAAgB,SAAS,CAAC;AACrD,gBAAM,KAAK,WAAW;AACtB,qBAAW,QAAQ,EAAE,MAAM,cAAc,GAAG,CAAQ;AACpD,qBAAW,QAAQ,EAAE,MAAM,cAAc,IAAI,OAAO,KAAK,CAAC;AAC1D,qBAAW,QAAQ,EAAE,MAAM,YAAY,GAAG,CAAC;AAC3C,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN,OAAO,IAAI;AAAA,cACT,mBAAmB,aAAa;AAAA,YAClC;AAAA,UACF,CAAC;AACD,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN,cAAc,EAAE,SAAS,SAAkB,KAAK,gBAAgB;AAAA,YAChE,OAAO,QAAQ,EAAE,cAAc,GAAG,eAAe,EAAE,CAAC;AAAA,YACpD,kBAAkB;AAAA,cAChB,eAAe;AAAA,gBACb,MAAM;AAAA,gBACN,WAAW;AAAA,gBACX,kBAAkB;AAAA,cACpB;AAAA,YACF;AAAA,UACF,CAAC;AACD,qBAAW,MAAM;AAAA,QACnB;AAAA,MACF,CAAC;AACD,aAAO,EAAE,QAAAA,SAAQ,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,EAAE,EAAE;AAAA,IACnD;AAIA,QAAI,eAA8B;AAClC,QAAI,gBAAgB,SAAS,UAAU;AACrC;AAAA,QACE,eAAe;AAAA,QACf,eAAe;AAAA,QACf,cAAc,eAAe,QAAQ;AAAA,MACvC;AACA,iBAAW,MAAM,qBAAqB;AAAA,QACpC,SAAS;AAAA,QACT;AAAA,QACA,SAAS,KAAK,OAAO;AAAA,QACrB,SAAS;AAAA,MACX,CAAC;AACD,gBAAU,SAAS;AACnB,wBAAkB,UAAU;AAC5B,qBAAe,mBAAmB;AAAA,QAChC,eAAe,eAAe;AAAA,QAC9B,QAAQ,eAAe;AAAA,QACvB,UAAU,eAAe;AAAA,MAC3B,CAAC;AAAA,IACH;AASA,UAAM,oBAAoB,iBAAiB,EAAE;AAC7C,QACE,CAAC,kBACD,CAAC,kBACD,mBAAmB,WACnB,kBAAkB,YAAY,SAC9B;AACA,UAAI,OAAO,6DAA6D;AAAA,QACtE,YAAY;AAAA,QACZ,KAAK,kBAAkB;AAAA,QACvB,KAAK;AAAA,QACL,YAAY,SAAS;AAAA,MACvB,CAAC;AACD,0BAAoB,EAAE;AACtB,4BAAsB,EAAE;AAAA,IAC1B;AAEA,UAAM,qBAAqB,CAAC,CAAC,mBAAmB,EAAE;AAClD,UAAM,mBAAmB,CAAC,CAAC,iBAAiB,EAAE;AAC9C,QAAI,wBACF,CAAC,sBAAsB,CAAC,oBAAoB;AAG9C,QAAI,gBAAgB,SAAS,YAAY,sBAAsB;AAC7D,8BAAwB;AAAA,IAC1B;AAMA,UAAM,EAAE,OAAO,cAAc,MAAM,SAAS,IAAI,aAAa,SAAS,OAAO;AAE7E,UAAM,6BAA6B,iBAC/B,OACA,kCAAkC,IAAI,QAAQ,MAAa;AAC/D,QAAI,4BAA4B;AAI9B,UAAI,KAAK,4CAA4C,EAAE,GAAG,CAAC;AAAA,IAC7D;AAIA,UAAM,4BAA4B,iBAC9B,CAAC,IACD,qBAAqB,EAAE;AAG3B,UAAM,kBACJ,gBAAgB,SAAS,WACrB,gCAAgC,QAAQ,QAAQ,eAAe,MAAM,IACrE,QAAQ;AACd,UAAM,UACJ,8BACA,qBAAqB,iBAAiB,uBAAuB;AAAA,MAC3D;AAAA,MACA,gBAAgB,IAAI,IAAI,0BAA0B,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC;AAAA,MAC1E,uBAAuB,KAAK,6BAA6B;AAAA,IAC3D,CAAC;AACH,UAAM,gBAAgB,iBAAiB,OAAO,KAAK,mBAAmB;AACtE,UAAM,mBAAmB,KAAK,yBAAyB;AAIvD,UAAM,yBAAyB,MAAM,KAAK;AAAA,MACxC;AAAA,MACA;AAAA,IACF;AACA,UAAM,OAAO;AAEb,UAAM,8BAGD,0BAA0B,IAAI,CAAC,UAAU;AAAA,MAC5C;AAAA,MACA,QAAQ,KAAK,iCAAiC,QAAQ,QAAQ,IAAI;AAAA,IACpE,EAAE;AACF,UAAM,2BAA2B,4BAA4B;AAAA,MAC3D,CAAC,MAAM,EAAE,WAAW;AAAA,IACtB;AAQA,UAAM,CAAC,eAAe,UAAU,IAAI,MAAM,QAAQ,IAAI;AAAA,MACpD,iBAAiB,QAAQ,QAAQ,MAAS,IAAI,oBAAoB;AAAA,MAClE,iBAAiB,OAAO;AAAA,IAC1B,CAAC;AAOD,UAAM,mBAAmB;AAAA,MACvB,KAAK,OAAO;AAAA,MACZ;AAAA,IACF;AACA,UAAM,oBACJ,iBAAiB,SAAS,KAC1B,KAAK,OAAO,oBAAoB,SAChC,CAAC,kBACD,CAAC,kBACD,gCAAgC;AAAA,MAC9B,YAAY,KAAK,OAAO;AAAA,MACxB,YAAY;AAAA,MACZ,sBAAsB,MAAM,iBAAiB,GAAG;AAAA,MAChD;AAAA,MACA,aAAa,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA,MAIf,cAAc,CAAC,CAAE,MAAM,qBAAqB,QAAQ;AAAA,IACtD,CAAC;AAEH,QAAI,KAAK,qBAAqB;AAAA,MAC5B;AAAA,MACA,OAAO;AAAA,MACP,YAAY,QAAQ;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,eAAe,IAAI,CAAC,MAAM,EAAE,IAAI,KAAK;AAAA,MACjD;AAAA,MACA;AAAA,MACA,eAAe,KAAK,iBAAiB,OAAO;AAAA,MAC5C,qBAAqB,QAAQ,kBACzB,OAAO,KAAK,QAAQ,eAAe,IACnC,CAAC;AAAA,IACP,CAAC;AAED,UAAM,SAAS,IAAI,eAA0C;AAAA,MAC3D,MAAM,YAAY;AAIhB,YAAI,gBAAgB;AAClB,8BAAoB,EAAE;AACtB,gCAAsB,EAAE;AAAA,QAC1B;AAaA,YAAI,CAAC,kBAAkB,CAAC,4BAA4B,0BAA0B,EAAE,GAAG;AACjF,8BAAoB,EAAE;AACtB,gCAAsB,EAAE;AACxB,cAAI,KAAK,yDAAyD;AAAA,YAChE,YAAY;AAAA,UACd,CAAC;AAAA,QACH;AAEA,YAAI,gBAAgB,iBAAiB,EAAE;AACvC,YAAI;AACJ,YAAI;AACJ,YAAI;AACJ,YAAI,cAAqC,eAAe,eAAe;AAEvE,cAAM,QAAQ,YAAY;AAGxB,cACE,CAAC,kBACD,iBACA,KAAK,OAAO,iBAAiB,SAC7B,KAAK,OAAO,sBAAsB,OAClC;AACA,kBAAM,QAAQ,KAAK,mBAAmB,KAAK,QAAW,aAAc;AACpE,kBAAM,eAAe,cAAc,WAAW;AAC9C,gBAAI,iBAAiB,MAAM,aAAa;AACtC,kBAAI,0BAA0B,SAAS,GAAG;AACxC,oBAAI,KAAK,sDAAsD;AAAA,kBAC7D;AAAA,kBACA;AAAA,kBACA,aAAa,MAAM;AAAA,kBACnB,cAAc,0BAA0B;AAAA,gBAC1C,CAAC;AAAA,cACH,OAAO;AACL,oBAAI,KAAK,kDAAkD;AAAA,kBACzD;AAAA,kBACA;AAAA,kBACA,aAAa,MAAM;AAAA,gBACrB,CAAC;AACD,sBAAM,2BAA2B,EAAE;AACnC,gCAAgB;AAChB,8BAAc;AAAA,cAChB;AAAA,YACF;AAAA,UACF;AAEA,cAAI,kBAAkB,CAAC,gBAAgB;AAKrC,kBAAM,MAAM,KAAK,mBAAmB,KAAK,QAAW,aAAc;AAClE,gBAAI,eAAe;AACjB,qBAAO,cAAc;AACrB,4BAAc,cAAc;AAC5B,kBAAI,MAAM,sCAAsC,EAAE,GAAG,CAAC;AAAA,YACxD,OAAO;AAGL,oBAAM,QAAQ;AAAA,gBACZ,GAAG,IAAI,sBAAsB,IAAI,CAAC,MAAM,QAAQ,CAAC,KAAK;AAAA,gBACtD;AAAA,gBACA,GAAI,KAAK,OAAO,yBAAyB;AAAA,kBACvC;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA,gBACF;AAAA,cACF;AACA,oBAAM,mBACJ,KAAK,OAAO,4BAA4B,QACpC,SACA;AAAA,gBACE;AAAA,gBACA,KAAK,OAAO,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAMxC;AACN,kBAAI,KAAK,OAAO,4BAA4B,OAAO;AACjD,oBAAI;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AACA,kBAAI,4BAA4B;AAC9B,oBAAI;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AAIA,oBAAM,kBAAkB,MAAM,uBAAuB;AAAA,gBACnD;AAAA,gBACA;AAAA,gBACA,SAAS,KAAK,OAAO,yBAAyB;AAAA,gBAC9C,GAAG,KAAK,iBAAiB,QAAQ;AAAA,cACnC,CAAC;AACD,oBAAM,KAAK,wBAAwB;AAAA,gBACjC;AAAA,gBACA;AAAA,gBACA,WAAW,KAAK,OAAO;AAAA,gBACvB,OAAO;AAAA,gBACP;AAAA,gBACA,gBAAgB,IAAI;AAAA,gBACpB,YAAY;AAAA,gBACZ,kBAAkB;AAAA,gBAClB;AAAA,gBACA,uBAAuB,KAAK,OAAO;AAAA,gBACnC,QAAQ;AAAA,cACV,CAAC;AACD,iBAAG,UAAU,IAAI;AACjB,+BAAiB,IAAI,EAAE;AACvB,qBAAO,GAAG;AACV,4BAAc,GAAG;AACjB,8BAAgB;AAChB,kBAAI,KAAK,sCAAsC;AAAA,gBAC7C;AAAA,gBACA;AAAA,gBACA,WAAW,KAAK,OAAO;AAAA,gBACvB,OAAO;AAAA,cACT,CAAC;AAAA,YACH;AAAA,UACF,OAAO;AACP,gBAAI;AACJ,gBAAI,mBAA0C;AAC9C,gBAAI,eAA8B;AAElC,gBAAI,gBAAgB;AAOlB,wBAAU,aAAa;AAAA,gBACrB,YAAY;AAAA,gBACZ;AAAA,gBACA,kBAAkB;AAAA,gBAClB,OAAO;AAAA,gBACP,gBAAgB,KAAK,OAAO;AAAA,gBAC5B;AAAA,gBACA;AAAA,cACF,CAAC;AAAA,YACH,OAAO;AAKL,oBAAM,YAAY,KAAK;AAAA,gBACrB;AAAA,gBACA;AAAA,gBACA;AAAA,cACF;AAMA,oBAAM,gBAAgB,KAAK;AAAA,gBACzB,UAAU;AAAA,gBACV,QAAQ;AAAA,gBACR,IAAI,KAAK,iBAAiB,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC;AAAA,cACtD;AACA,oBAAM,gBAAgB,eAAe,QAAQ;AAM7C,oBAAM,iBAAkD,gBACpD,cAAc,iBACd;AAUJ,oBAAM,mBACJ,eAAe,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM,KAAK;AACnD,oBAAM,uBACJ,eAAe,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU,KAAK;AAGvD,oBAAM,0BACH,KAAK,OAAO,oBAAoB,UAAU,KAAK;AAClD,kBAAI,MAAM,iCAAiC;AAAA,gBACzC,WAAW,KAAK,OAAO,sBAAsB;AAAA,gBAC7C;AAAA,cACF,CAAC;AACD,oBAAM,eACJ,oBAAoB,wBAAwB,yBACxC,MAAM,iBAAiB,IACvB;AAAA,gBACE,UAAU;AAAA,gBACV,iBAAiB;AAAA,gBACjB,qBAAqB;AAAA,gBACrB,aAAa;AAAA,cACf;AACN,kBAAI,gBAAgB;AACpB,kBAAI,iBAAiB,kBAAkB;AACrC,gCAAgB;AAAA,kBACd;AAAA,kBACA,aAAa;AAAA,gBACf;AAIA,oBAAI,KAAK,kCAAkC;AAAA,kBACzC,SAAS,QAAQ,aAAa,eAAe;AAAA,kBAC7C,uBAAuB,aAAa,iBAAiB,UAAU;AAAA,kBAC/D,iBAAiB;AAAA,oBACf,aAAa,iBAAiB;AAAA,sBAC5B;AAAA,oBACF;AAAA,kBACF;AAAA,gBACF,CAAC;AAAA,cACH;AACA,kBAAI,iBAAiB,sBAAsB;AAIzC,gCAAgB;AAAA,kBACd;AAAA,kBACA,aAAa,cACT,aAAa,sBACb;AAAA,gBACN;AACA,gCAAgB;AAAA,kBACd;AAAA,kBACA,aAAa;AAAA,gBACf;AAIA,oBAAI,KAAK,+BAA+B;AAAA,kBACtC,qBAAqB,aAAa;AAAA,kBAClC,MAAM,aAAa;AAAA,gBACrB,CAAC;AAAA,cACH;AAgBA,oBAAM,mBAAmB,6BAA6B;AAAA,gBACpD,WAAW,KAAK,OAAO;AAAA,gBACvB,OAAO,aAAa;AAAA,gBACpB,OAAO,IAAI;AAAA,kBACT,CAAC,GAAI,iBAAiB,CAAC,GAAI,GAAI,iBAAiB,CAAC,CAAE,EAAE;AAAA,oBACnD,CAAC,MAAM,EAAE;AAAA,kBACX;AAAA,gBACF;AAAA,cACF,CAAC;AACD,kBAAI,iBAAiB,SAAS,GAAG;AAC/B,oBAAI,KAAK,+CAA+C;AAAA,kBACtD,OAAO,iBAAiB,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,gBAC3C,CAAC;AAAA,cACH;AAEA,oBAAM,eAAe;AAAA,gBACnB,GAAI,iBAAiB,CAAC;AAAA,gBACtB,GAAI,iBAAiB,CAAC;AAAA,gBACtB,GAAG;AAAA,cACL;AACA,oBAAM,qBACJ,aAAa,SAAS,IAAI,eAAe;AAE3C,oBAAM,wBACJ,eAAe,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU,KAAK;AAEvD,kBAAI,CAAC,eAAe,oBAAoB;AACtC,8BAAc,MAAM,KAAK;AAAA,kBACvB;AAAA,kBACA;AAAA,kBACA;AAAA,gBACF;AAAA,cACF;AAQA,oBAAM,sBACJ,eAAe,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU,KAAK;AAUvD,oBAAM,gBAAgB,uBAAuB;AAAA,gBAC3C,YAAY;AAAA,gBACZ,sBAAsB,KAAK,OAAO;AAAA,gBAClC,kBAAkB,KAAK,OAAO,cAAc;AAAA,cAC9C,CAAC;AACD,oBAAM,MAAM,KAAK;AAAA,gBACf;AAAA,gBACA,aAAa,WAAW;AAAA,gBACxB;AAAA,gBACA;AAAA,cACF;AACA,oBAAM,mBAAmB,gBACrB,SACA;AAAA,gBACE;AAAA,gBACA,KAAK,OAAO,0BAA0B;AAAA,gBACtC;AAAA,kBACE,GAAG,sBAAsB,QAAQ,MAAM;AAAA,kBACvC,GAAI,mBAAmB,CAAC,sBAAsB,IAAI,CAAC;AAAA,kBACnD,GAAI,sBAAsB,CAAC,mBAAmB,IAAI,CAAC;AAAA,gBACrD;AAAA,gBACA;AAAA,kBACE,iBAAiB;AAAA,kBACjB,yBAAyB,iBAAiB;AAAA,oBACxC,CAAC,MAAM,EAAE,SAAS;AAAA,kBACpB;AAAA,kBACA,oBAAoB,sBAAsB,EAAE;AAAA,gBAC9C;AAAA,cACF;AAKJ,oBAAM,kBAAkB,MAAM,uBAAuB;AAAA,gBACnD;AAAA,gBACA;AAAA,gBACA,SAAS,KAAK,OAAO,yBAAyB;AAAA,gBAC9C,GAAG,KAAK,iBAAiB,QAAQ;AAAA,cACnC,CAAC;AACD,wBAAU,aAAa;AAAA,gBACrB,YAAY;AAAA,gBACZ;AAAA,gBACA,OAAO;AAAA,gBACP,gBAAgB,KAAK,OAAO;AAAA,gBAC5B,WAAW,IAAI;AAAA,gBACf,iBAAiB,KAAK,OAAO;AAAA,gBAC7B,iBAAiB,cAAc,SAAS,IAAI,gBAAgB;AAAA,gBAC5D,wBAAwB;AAAA,gBACxB,YAAY;AAAA,gBACZ,GAAG,KAAK,mBAAmB;AAAA,gBAC3B;AAAA,gBACA;AAAA,cACF,CAAC;AACD,sCAAwB;AACxB,iCAAmB;AACnB,6BAAe,IAAI;AAAA,YACrB;AAEA,gBAAI,iBAAiB,CAAC,gBAAgB;AACpC,qBAAO,cAAc;AACrB,4BAAc,cAAc;AAC5B,kBAAI,MAAM,0BAA0B,EAAE,GAAG,CAAC;AAAA,YAC5C,OAAO;AACL,oBAAM,KAAK;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA,KAAK,OAAO;AAAA,gBACZ;AAAA,cACF;AACA,qBAAO,GAAG;AACV,4BAAc,GAAG;AACjB,8BAAgB;AAAA,YAClB;AAAA,UACA;AASA,cAAI,iBAAiB,CAAC,4BAA4B,eAAe,aAAa,GAAG;AAC/E,gBAAI,KAAK,kDAAkD,EAAE,GAAG,CAAC;AACjE,kBAAM,OAAO,MAAM,cAAc,aAAa;AAC9C,gBAAI,CAAC,MAAM;AACT,kBAAI,KAAK,sEAAsE,EAAE,GAAG,CAAC;AAAA,YACvF;AAAA,UACF;AAEA,qBAAW,QAAQ,EAAE,MAAM,gBAAgB,SAAS,CAAC;AAErD,cAAI,gBAA+B;AACnC,gBAAM,mBAAmB,oBAAI,IAAY;AAEzC,gBAAM,iBAAiB,MAAc;AACnC,gBAAI,eAAe;AACjB,yBAAW,QAAQ,EAAE,MAAM,YAAY,IAAI,cAAc,CAAC;AAAA,YAC5D;AACA,kBAAM,KAAK,WAAW;AACtB,4BAAgB;AAChB,uBAAW,QAAQ,EAAE,MAAM,cAAc,GAAG,CAAQ;AACpD,mBAAO;AAAA,UACT;AAEA,gBAAM,eAAe,MAAY;AAC/B,gBAAI,eAAe;AACjB,yBAAW,QAAQ,EAAE,MAAM,YAAY,IAAI,cAAc,CAAC;AAC1D,8BAAgB;AAAA,YAClB;AAAA,UACF;AAIA,cAAI,cAAc;AAChB,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN,IAAI,eAAe;AAAA,cACnB,OAAO;AAAA,YACT,CAAC;AACD,yBAAa;AAAA,UACf;AAEA,gBAAM,eAAe,oBAAI,IAAoB;AAC7C,gBAAM,mBAAmB,oBAAI,IAAqB;AAClD,cAAI,4BAA4B;AAEhC,cAAI,gBAAgB;AACpB,cAAI,mBAAmB;AAEvB,cAAI,sBAAsB;AAC1B,cAAI,kBAAkB;AACtB,cAAI,0BAA+C;AACnD,cAAI,sBAA2C;AAC/C,cAAI,sBAA4D;AAChE,cAAI,0BAA+C;AACnD,cAAI,qBAAqB;AACzB,cAAI,sBAAsB;AAC1B,cAAI,2BAA2B;AAC/B,cAAI,+BAA+B;AACnC,cAAI,4BAA4B;AAChC,cAAI,+BAA+B;AACnC,cAAI,gCAAgC;AAIpC,cAAI,iBAAgC;AACpC,gBAAM,oBAAuC;AAAA,YAC3C,SAAS;AAAA,cACP;AAAA,cACA,KAAK,OAAO;AAAA,YACd;AAAA,YACA,UAAU;AAAA,YACV,WAAW,KAAK,IAAI;AAAA,YACpB,iBAAiB;AAAA,UACnB;AAEA,gBAAM,qBAAqB,MAAM;AAC/B,gBAAI,qBAAqB;AACvB,2BAAa,mBAAmB;AAChC,oCAAsB;AAAA,YACxB;AAAA,UACF;AAUA,gBAAM,sBAAsB,MAAM;AAChC,kBAAM,MAAM,QAAQ,IAAI;AACxB,kBAAM,SAAS,MAAM,OAAO,SAAS,KAAK,EAAE,IAAI;AAChD,mBAAO,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,SAAS;AAAA,UAC1D,GAAG;AACH,gBAAM,sBAAsB,CAAC,UAAU,uBAAuB;AAC5D,+BAAmB;AACnB,gBAAK,CAAC,sBAAsB,CAAC,uBAAwB,iBAAkB;AACvE,kCAAsB,WAAW,MAAM;AACrC,kBAAI,iBAAkB;AACtB,kBAAI,KAAK,0EAAqE;AAAA,gBAC5E;AAAA,cACF,CAAC;AAID,kBAAI,CAAC,kBAAkB,SAAS;AAC9B,2BAAW,QAAQ;AAAA,kBACjB,MAAM;AAAA,kBACN,IAAI,eAAe;AAAA,kBACnB,OAAO,wBAAwB,OAAO;AAAA,gBACxC,CAAC;AACD,6BAAa;AAAA,cACf;AACA,2BAAa;AAAA,YACf,GAAG,OAAO;AAAA,UACZ;AAWA,gBAAM,qBAAqB,MAAM;AAC/B,kBAAM,MAAM,QAAQ,IAAI;AACxB,kBAAM,SAAS,MAAM,OAAO,SAAS,KAAK,EAAE,IAAI;AAChD,mBAAO,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,SAAS;AAAA,UAC1D,GAAG;AACH,cAAI,gBAAsD;AAC1D,cAAI,mBAAmB;AACvB,gBAAM,qBAAqB,MAAM;AAC/B,gBAAI,eAAe;AACjB,2BAAa,aAAa;AAC1B,8BAAgB;AAAA,YAClB;AAAA,UACF;AACA,gBAAM,sBAAsB,MAAM;AAChC,4BAAgB;AAChB,gBAAI,oBAAoB,sBAAsB,oBAAqB;AACnE,gBAAI,kBAAkB;AACpB,kBAAI;AAAA,gBACF;AAAA,gBACA,EAAE,YAAY,GAAG;AAAA,cACnB;AACA,kCAAoB,EAAE;AACtB,oCAAsB,EAAE;AACxB,iCAAmB;AACnB,0BAAY;AACZ,yBAAW,QAAQ;AAAA,gBACjB,MAAM;AAAA,gBACN,OAAO,IAAI;AAAA,kBACT;AAAA,gBACF;AAAA,cACF,CAAC;AACD,kBAAI;AACF,2BAAW,MAAM;AAAA,cACnB,QAAQ;AAAA,cAAC;AACT;AAAA,YACF;AACA,+BAAmB;AACnB,gBAAI;AAAA,cACF;AAAA,cACA,EAAE,YAAY,IAAI,iBAAiB,kBAAkB;AAAA,YACvD;AACA,wBAAY,IAAI,QAAQ,WAAW;AACnC,wBAAY,IAAI,SAAS,YAAY;AACrC,iBAAK,IAAI,SAAS,gBAAgB;AAClC,kBAAM,QAAQ;AAAA,cACZ;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,KAAK,OAAO;AAAA,YACd;AACA,gBAAI,CAAC,OAAO;AACV,kBAAI;AAAA,gBACF;AAAA,gBACA,EAAE,YAAY,GAAG;AAAA,cACnB;AACA,iCAAmB;AACnB,0BAAY;AACZ,yBAAW,QAAQ;AAAA,gBACjB,MAAM;AAAA,gBACN,OAAO,IAAI;AAAA,kBACT;AAAA,gBACF;AAAA,cACF,CAAC;AACD,kBAAI;AACF,2BAAW,MAAM;AAAA,cACnB,QAAQ;AAAA,cAAC;AACT;AAAA,YACF;AACA,mBAAO,MAAM;AACb,0BAAc,MAAM;AACpB,4BAAgB;AAChB,wBAAY,GAAG,QAAQ,WAAW;AAClC,wBAAY,GAAG,SAAS,YAAY;AACpC,iBAAK,GAAG,SAAS,gBAAgB;AACjC,gBAAI;AACF,kBAAI,CAAC,0BAA0B,IAAI,GAAG;AACpC,gCAAgB,KAAK;AACrB,qBAAK,OAAO,MAAM,kBAAkB,IAAI;AAAA,cAC1C;AACA,kBAAI,MAAM,sCAAsC;AAAA,gBAC9C,YAAY,gBAAgB;AAAA,cAC9B,CAAC;AAAA,YACH,SAAS,KAAK;AACZ,kBAAI,MAAM,4CAA4C;AAAA,gBACpD,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,cACxD,CAAC;AAAA,YACH;AACA,6BAAiB;AAAA,UACnB;AACA,gBAAM,mBAAmB,MAAM;AAC7B,+BAAmB;AACnB,gBAAI,iBAAkB;AACtB,4BAAgB,WAAW,qBAAqB,iBAAiB;AAAA,UACnE;AAIA,gBAAM,4BAA4B,CAAC,QAAQ,UAAmB;AAC5D,kBAAM,UAAU,eAAe;AAC/B,kBAAM,UAAU,CAAC,GAAI,SAAS,OAAO,KAAK,CAAC,CAAE,EAAE;AAAA,cAC7C,CAAC,UAAU,SAAS,MAAM,oBAAoB,gCAAgC,MAAM,IAAI;AAAA,YAC1F;AACA,gBAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,yBAAa;AACb,8BAAkB,2BAA2B,OAAO;AAGpD,gBAAI,cAAe,iBAAgB,aAAa;AAChD,iBAAK,MAAO,MAAM,kBAAkB,IAAI;AACxC,uBAAW,EAAE,KAAK,KAAK,QAAS,SAAS,OAAO,KAAK,UAAU;AAC/D,gBAAI,KAAK,2DAA2D;AAAA,cAClE,YAAY;AAAA,cACZ,aAAa,QAAQ,IAAI,CAAC,EAAE,KAAK,MAAM,KAAK,UAAU;AAAA,cACtD,SAAS;AAAA,YACX,CAAC;AACD,+BAAmB;AACnB,iCAAqB;AACrB,kCAAsB;AACtB,4BAAgB;AAChB,oCAAwB;AACxB,+BAAmB;AACnB,6BAAiB;AACjB,mBAAO;AAAA,UACT;AAEA,gBAAM,cAAc,oBAAI,IAGtB;AAKF,gBAAM,mBAAmB,oBAAI,IAAY;AACzC,gBAAM,gBAAgB,oBAAI,IAGxB;AAEF,cAAI,aASA,CAAC;AAIL,cAAI;AAKJ,cAAI,kBAAiE;AAGrE,cAAI,eAAwC;AAM9C,gBAAM,cAAkC,CAAC;AACzC,cAAI,aAAmD;AACvD,gBAAM,iBAAiB;AAEvB,gBAAM,sBAAsB,CAAC,UAA8B;AACzD,gBAAI,iBAAkB;AACtB,gBAAI,MAAM,WAAW,EAAG;AACxB,kBAAM,kBAAkB,CACtB,YACA,UACA,UACG;AACH,yBAAW,QAAQ;AAAA,gBACjB,MAAM;AAAA,gBACN,IAAI;AAAA,gBACJ;AAAA,cACF,CAAQ;AACR,yBAAW,QAAQ;AAAA,gBACjB,MAAM;AAAA,gBACN;AAAA,gBACA;AAAA,gBACA,OAAO,KAAK,UAAU,KAAK;AAAA,gBAC3B,kBAAkB;AAAA,cACpB,CAAQ;AACR,+BAAiB,IAAI,UAAU;AAAA,YACjC;AACA,uBAAW,QAAQ,OAAO;AACxB,kBAAI,KAAK,aAAa,sBAAsB;AAO1C,2BAAW,CAAC,OAAO,IAAI,KAAK,eAAe,KAAK,KAAK,EAAE,QAAQ,GAAG;AAChE;AAAA,oBACE,yBAAyB,KAAK,YAAY,KAAK;AAAA,oBAC/C;AAAA,oBACA;AAAA,kBACF;AAAA,gBACF;AACA,iCAAiB,IAAI,KAAK,UAAU;AAAA,cACtC,OAAO;AACL,gCAAgB,KAAK,YAAY,KAAK,UAAU,KAAK,KAAK;AAAA,cAC5D;AACA,0CAA4B,KAAK,UAAU;AAAA,YAC7C;AACA,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN,cAAc,eAAe,YAAY;AAAA,cACzC,OAAO,QAAQ,WAAW,KAAK;AAAA,cAC/B,kBAAkB;AAAA,gBAChB,eAAe;AAAA,cACjB;AAAA,YACF,CAAC;AACD,+BAAmB;AACnB,wBAAY;AACZ,gBAAI;AACF,yBAAW,MAAM;AAAA,YACnB,QAAQ;AAAA,YAAC;AAAA,UACX;AASA,gBAAM,yBAAyB,CAAC,SAA2B;AACzD,gBAAI,iBAAkB;AACtB,yBAAa;AACb,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN,IAAI,KAAK;AAAA,cACT,UAAU,KAAK;AAAA,cACf,kBAAkB;AAAA,YACpB,CAAQ;AACR,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN,YAAY,KAAK;AAAA,cACjB,UAAU,KAAK;AAAA,cACf,OAAO,KAAK,UAAU,KAAK,KAAK;AAAA,cAChC,kBAAkB;AAAA,YACpB,CAAQ;AACR,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN,cAAc,eAAe,YAAY;AAAA,cACzC,OAAO,QAAQ,WAAW,KAAK;AAAA,cAC/B,kBAAkB;AAAA,gBAChB,eAAe;AAAA,cACjB;AAAA,YACF,CAAC;AACD,+BAAmB;AACnB,wBAAY;AACZ,gBAAI;AACF,yBAAW,MAAM;AAAA,YACnB,QAAQ;AAAA,YAAC;AAAA,UACX;AAEA,gBAAM,WAAW,MAAM;AACrB,gBAAI,YAAY;AACd,2BAAa,UAAU;AACvB,2BAAa;AAAA,YACf;AACA,gBAAI,YAAY,WAAW,EAAG;AAC9B,gBAAI,iBAAkB;AACtB,kBAAM,QAAQ,YAAY,OAAO,GAAG,YAAY,MAAM;AACtD,gBAAI,KAAK,mDAAmD;AAAA,cAC1D,YAAY;AAAA,cACZ,OAAO,MAAM;AAAA,cACb,aAAa,MAAM,IAAI,CAAC,MAAM,EAAE,UAAU;AAAA,YAC5C,CAAC;AACD,gCAAoB,KAAK;AAAA,UAC3B;AAEA,gBAAM,uBAAuB,MAAM;AACjC,yBAAa;AACb,kBAAMC,kBAAiB;AACvB,sCAA0B;AAC1B,gBAAI,CAACA,mBAAkB,iBAAkB;AACzC,gBAAI,YAAY,SAAS,GAAG;AAC1B,uBAAS;AACT;AAAA,YACF;AACA,YAAAA,gBAAe;AAAA,UACjB;AAEA,gBAAM,yBAAyB,CAC7BA,iBACA,YACG;AACH,sCAA0BA;AAC1B,gBAAI,WAAY,cAAa,UAAU;AACvC,yBAAa,WAAW,sBAAsB,OAAO;AAAA,UACvD;AAEA,gBAAM,yBAAyB,MAAe;AAC5C,gBAAI,CAAC,wBAAyB,QAAO;AACrC,gBAAI,WAAY,cAAa,UAAU;AACvC,yBAAa,WAAW,sBAAsB,cAAc;AAC5D,mBAAO;AAAA,UACT;AAEA,gBAAM,kBAAkB,CAAC,SAAiB;AACxC,wCAA4B;AAC5B,4CAAgC;AAAA,UAClC;AAEA,gBAAM,4BAA4B,MAAM;AACtC,2CAA+B;AAAA,UACjC;AAEA,gBAAM,gBAAgB,MAAM;AAC1B,wCAA4B;AAAA,UAC9B;AAEA,gBAAM,mBAAmB,MAAM;AAC7B,2CAA+B;AAAA,UACjC;AAEA,gBAAM,oBAAoB,MAAM;AAC9B,4CAAgC;AAAA,UAClC;AAEA,gBAAM,0BAA0B,MAAM;AACpC,uCAA2B;AAC3B,2CAA+B;AAC/B,wCAA4B;AAC5B,2CAA+B;AAC/B,4CAAgC;AAChC,6BAAiB;AAAA,UACnB;AAEA,gBAAM,iBAAiB,CAAC,QAA6B;AACnD,gBAAI,iBAAkB;AAGtB,gBAAI,0BAA0B,GAAG;AAC/B,kBAAI,YAAY,SAAS,EAAG,UAAS;AACrC;AAAA,YACF;AACA,gBAAI,YAAY,SAAS,GAAG;AAC1B,uBAAS;AACT;AAAA,YACF;AAEA,kBAAM,kBAAkB,qBAAqB,EAAE;AAC/C,gBAAI,gBAAgB,SAAS,GAAG;AAC9B,kBAAI,KAAK,2DAA2D;AAAA,gBAClE,YAAY;AAAA,gBACZ,OAAO,gBAAgB;AAAA,cACzB,CAAC;AAAA,YACH;AAEA,2BAAe,yBAAyB,MAAM;AAU9C,iBAAK,mBAAmB,iBAAiB,qBAAqB,IAAI,aAAa,MAAM;AACnF,oBAAM,OAAO,kCAAkC,IAAI;AAAA,gBACjD;AAAA,gBACA,YAAY;AAAA,gBACZ,UAAU,iBAAiB;AAAA,gBAC3B,QAAQ,iBAAiB;AAAA,gBACzB,QAAQ,mBAAmB,CAAC,eAAe,SAAY,qBAAqB,YAAY;AAAA,cAC1F,CAAC;AACD,kBAAI;AAAA,gBACF,mBAAmB,aAAa,KAC9B,mBAAmB,CAAC,eAAe,oBAAoB,iBAAiB,YAAY,GACtF;AAAA,gBACA;AAAA,kBACE,YAAY;AAAA,kBACZ,YAAY;AAAA,kBACZ,YAAY,KAAK;AAAA,kBACjB,UAAU,iBAAiB,YAAY;AAAA,gBACzC;AAAA,cACF;AACA,qCAAuB,IAAI;AAC3B;AAAA,YACF;AAEA,kBAAM,eAAe;AAAA,cACnB;AAAA,cACA;AAAA,gBACE,MAAM;AAAA,gBACN,iBAAiB;AAAA,gBACjB,cAAc;AAAA,gBACd,iBAAiB;AAAA,gBACjB,kBAAkB;AAAA,gBAClB,SAAS,IAAI;AAAA,gBACb,YAAY;AAAA,cACd;AAAA,YACF;AACA,gBAAI,aAAa,UAAU;AACzB,oBAAM,YAAY,sBAAsB;AAAA,gBACtC,MAAM;AAAA,gBACN,iBAAiB;AAAA,gBACjB,cAAc;AAAA,gBACd,iBAAiB;AAAA,gBACjB,kBAAkB;AAAA,gBAClB,SAAS,IAAI;AAAA,cACf,CAAC;AACD,gCAAkB,kBAChB,cAAc,kBAAkB,gBAC5B,kBAAkB,kBAAkB,IACpC;AACN,gCAAkB,gBAAgB;AAClC,gCAAkB;AAClB,kBAAI,OAAO,4CAA4C;AAAA,gBACrD,YAAY;AAAA,gBACZ,QAAQ,aAAa;AAAA,gBACrB,UAAU,kBAAkB;AAAA,gBAC5B,YAAY,yBAAyB;AAAA,gBACrC,gBAAgB,6BAA6B;AAAA,gBAC7C,cAAc;AAAA,gBACd,iBAAiB;AAAA,gBACjB,kBAAkB;AAAA,cACpB,CAAC;AACD,8BAAgB;AAChB,sCAAwB;AAExB,kBAAI,cAAe,iBAAgB,aAAa;AAChD,mBAAK,OAAO,MAAM,wBAAwB,IAAI,IAAI;AAClD;AAAA,YACF;AACA,gBAAI,OAAO,6BAA6B;AAAA,cACtC,YAAY;AAAA,cACZ,QAAQ,aAAa;AAAA,cACrB,YAAY;AAAA,cACZ,UAAU,kBAAkB;AAAA,cAC5B,YAAY,yBAAyB;AAAA,cACrC,gBAAgB,6BAA6B;AAAA,cAC7C,cAAc;AAAA,cACd,iBAAiB;AAAA,cACjB,kBAAkB;AAAA,YACpB,CAAC;AAED,uBAAW,CAAC,KAAK,WAAW,KAAK,cAAc;AAC7C,kBAAI,iBAAiB,IAAI,GAAG,GAAG;AAC7B,2BAAW,QAAQ;AAAA,kBACjB,MAAM;AAAA,kBACN,IAAI;AAAA,gBACN,CAAQ;AAAA,cACV;AAAA,YACF;AAEA,uBAAW,QAAQ;AAAA,cACjB,MAAM;AAAA,cACN,cAAc,gBACV,EAAE,SAAS,SAAkB,KAAK,cAAc,IAChD,eAAe,MAAM;AAAA,cACzB,OAAO,QAAQ,IAAI,KAAK;AAAA,cACxB,kBAAkB;AAAA,gBAChB,eAAe;AAAA,kBACb,GAAG;AAAA,kBACH,GAAI,gBAAgB,EAAE,eAAe,cAAc,IAAI,CAAC;AAAA,kBACxD,GAAI,iBACA,EAAE,iBAAiB,iBAAiB,IACpC,CAAC;AAAA,gBACP;AAAA,gBACA,GAAI,OAAO,IAAI,OAAO,gCAAgC,WAClD;AAAA,kBACE,WAAW;AAAA,oBACT,0BACE,IAAI,MAAM;AAAA,kBACd;AAAA,gBACF,IACA,CAAC;AAAA,cACP;AAAA,YACF,CAAC;AAED,+BAAmB;AACnB,wBAAY;AACZ,gBAAI,CAAC,kBAAkB,CAAC,gBAAgB;AACtC;AAAA,gBACE;AAAA,gBACA,4BAA4B,KAAK,OAAO,oBAAoB;AAAA,cAC9D;AAAA,YACF;AAEA,gBAAI;AACF,yBAAW,MAAM;AAAA,YACnB,QAAQ;AAAA,YAAC;AAAA,UACX;AAKA,cAAI,mBAAmB;AAEvB,gBAAM,cAAc,CAAC,SAAiB;AACpC,gBAAI,CAAC,KAAK,KAAK,EAAG;AAClB,gBAAI,iBAAkB;AAItB,gCAAoB;AAEpB,gBAAI;AACF,oBAAM,QAA6B,KAAK,MAAM,IAAI;AAIlD,oBAAM,MACJ,MAAM,SAAS,kBAAkB,MAAM,QACnC,EAAE,GAAG,MAAM,OAAO,YAAY,MAAM,WAAW,IAC/C;AAEN,oBAAM,gBACH,IAAI,SAAS,eAAe,CAAC,CAAC,IAAI,SAAS,SAAS,UACpD,IAAI,SAAS,yBAAyB,IAAI,eAAe,SAAS,cAClE,IAAI,SAAS,0BACV,IAAI,OAAO,SAAS,gBAAgB,CAAC,CAAC,IAAI,MAAM,QAChD,IAAI,OAAO,SAAS,oBAAoB,CAAC,CAAC,IAAI,MAAM;AAC1D,kBAAI,eAAe;AACjB,sCAAsB;AACtB,mCAAmB;AACnB,oCAAoB;AAAA,cACtB;AAEA,kBAAI,MAAM,SAAS,gBAAgB;AACjC,mCAAmB;AAAA,cACrB;AAEA,kBAAI,qBAAqB,KAAK,IAAI,GAAG;AACnC;AAAA,cACF;AAEA,kBAAI,MAAM,kBAAkB;AAAA,gBAC1B,MAAM,IAAI;AAAA,gBACV,SAAS,IAAI;AAAA,cACf,CAAC;AAGD,kBAAI,IAAI,SAAS,YAAY,IAAI,YAAY,QAAQ;AACnD,oBAAI,IAAI,YAAY;AAClB,qCAAmB,IAAI,IAAI,UAAU;AACrC,sBAAI,KAAK,uBAAuB;AAAA,oBAC9B,iBAAiB,IAAI;AAAA,kBACvB,CAAC;AAAA,gBACH;AACA,oCAAoB,KAAK,QAAQ;AACjC,iCAAiB,KAAK;AAAA,kBACpB,uBAAuB,KAAK,OAAO;AAAA,gBACrC,CAAC;AAAA,cACH;AAIA,kBAAI,IAAI,SAAS,YAAY,IAAI,YAAY,oBAAoB;AAC/D,sBAAM,OAAO,sBAAsB,GAAG;AACtC,oBAAI,MAAM;AACR,6BAAW,QAAQ,EAAE,MAAM,cAAc,IAAI,eAAe,GAAG,OAAO,KAAK,CAAC;AAC5E,+BAAa;AAAA,gBACf;AAAA,cACF;AAQA,kBAAI,IAAI,SAAS,sBAAsB;AACrC,sBAAM,OAAO,wBAAwB,GAAG;AACxC,oBAAI,MAAM;AACR,8BAAY,MAAM;AAClB,+BAAa,MAAM;AACnB,mCAAiB,MAAM;AACvB,mCAAiB,MAAM;AACvB,6BAAW,QAAQ,EAAE,MAAM,cAAc,IAAI,eAAe,GAAG,OAAO,KAAK,CAAC;AAC5E,+BAAa;AAAA,gBACf;AACA;AAAA,cACF;AAGA,oBAAM,QAAQ,iBAAiB,GAAG;AAClC,kBAAI,MAAO,gBAAe;AAK1B,kBAAI,IAAI,SAAS,oBAAoB;AAInC,sBAAM,OAAO,oBAAoB,GAAG;AACpC,oBAAI,QAAQ,oBAAoB,IAAI,GAAG;AACrC,oCAAkB;AAAA,oBAChB,UAAU,KAAK,YAAY,KAAK;AAAA,oBAChC,QAAQ,KAAK;AAAA,kBACf;AAAA,gBACF;AACA,sBAAM,OAAO,qBAAqB,GAAG;AACrC,oBAAI,MAAM;AACR,6BAAW,QAAQ,EAAE,MAAM,cAAc,IAAI,eAAe,GAAG,OAAO,KAAK,CAAC;AAC5E,+BAAa;AAAA,gBACf;AACA;AAAA,cACF;AAGA,kBACE,IAAI,SAAS,yBACb,IAAI,iBACJ,IAAI,UAAU,QACd;AACA,sBAAMC,SAAQ,IAAI;AAClB,sBAAM,MAAM,IAAI;AAEhB,oBAAIA,OAAM,SAAS,YAAY;AAC7B,gCAAc;AACd,wBAAM,cAAc,WAAW;AAC/B,+BAAa,IAAI,KAAK,WAAW;AAAA,gBACnC;AAEA,oBAAIA,OAAM,SAAS,QAAQ;AACzB,mCAAiB,IAAI,GAAG;AAIxB,4CAA0B;AAC1B,sBAAIA,OAAM,MAAM;AACd,wBAAI,CAAC,cAAe,gBAAe;AACnC,+BAAW,QAAQ;AAAA,sBACjB,MAAM;AAAA,sBACN,IAAI;AAAA,sBACJ,OAAOA,OAAM;AAAA,oBACf,CAAC;AACD,oCAAgBA,OAAM,IAAI;AAC1B,yCAAqB;AAAA,kBACvB;AAAA,gBACF;AAEA,oBAAIA,OAAM,SAAS,cAAcA,OAAM,MAAMA,OAAM,MAAM;AACvD,mCAAiB;AACjB,wBAAM,QAAQ;AAAA,oBACZ,IAAIA,OAAM;AAAA,oBACV,MAAMA,OAAM;AAAA,oBACZ,WAAW;AAAA,oBACX,SAAS;AAAA,kBACX;AACA,8BAAY,IAAI,KAAK,KAAK;AAE1B,sBACEA,OAAM,SAAS,qBACfA,OAAM,SAAS,uBACfA,OAAM,SAAS,kBACf,CAACA,OAAM,KAAK,WAAW,iBAAiB,GACxC;AACA,0BAAM,EAAE,MAAM,YAAY,MAAM,SAAS,IAAI;AAAA,sBAC3CA,OAAM;AAAA,sBACN;AAAA,sBACA;AAAA,wBACE,WAAW,KAAK,OAAO;AAAA,wBACvB,WAAW,mBAAmB,EAAE;AAAA,wBAChC,WAAWA,OAAM;AAAA,sBACnB;AAAA,oBACF;AACA,wBAAI,CAAC,MAAM;AACT,4BAAM,UAAU;AAChB,iCAAW,QAAQ;AAAA,wBACjB,MAAM;AAAA,wBACN,IAAIA,OAAM;AAAA,wBACV,UAAU;AAAA,wBACV,kBAAkB;AAAA,sBACpB,CAAQ;AACR,0BAAI,KAAK,gBAAgB;AAAA,wBACvB,MAAMA,OAAM;AAAA,wBACZ;AAAA,wBACA,IAAIA,OAAM;AAAA,sBACZ,CAAC;AAAA,oBACH;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AAGA,kBACE,IAAI,SAAS,yBACb,IAAI,SACJ,IAAI,UAAU,QACd;AACA,sBAAM,QAAQ,IAAI;AAClB,sBAAM,MAAM,IAAI;AAEhB,oBAAI,MAAM,SAAS,oBAAoB,MAAM,UAAU;AACrD,gCAAc;AACd,8CAA4B;AAC5B,wBAAM,cAAc,aAAa,IAAI,GAAG;AACxC,sBAAI,aAAa;AACf,wBAAI,CAAC,iBAAiB,IAAI,GAAG,GAAG;AAC9B,iCAAW,QAAQ;AAAA,wBACjB,MAAM;AAAA,wBACN,IAAI;AAAA,sBACN,CAAQ;AACR,uCAAiB,IAAI,KAAK,IAAI;AAAA,oBAChC;AACA,+BAAW,QAAQ;AAAA,sBACjB,MAAM;AAAA,sBACN,IAAI;AAAA,sBACJ,OAAO,MAAM;AAAA,oBACf,CAAQ;AAAA,kBACV;AAAA,gBACF;AAEA,oBAAI,MAAM,SAAS,gBAAgB,MAAM,MAAM;AAC7C,sBAAI,CAAC,cAAe,gBAAe;AACnC,6BAAW,QAAQ;AAAA,oBACjB,MAAM;AAAA,oBACN,IAAI;AAAA,oBACJ,OAAO,MAAM;AAAA,kBACf,CAAC;AACD,kCAAgB,MAAM,IAAI;AAC1B,uCAAqB;AAAA,gBACvB;AAEA,oBAAI,MAAM,SAAS,sBAAsB,MAAM,cAAc;AAC3D,wBAAM,KAAK,YAAY,IAAI,GAAG;AAC9B,sBAAI,IAAI;AACN,uBAAG,aAAa,MAAM;AAOtB,wBAAI,GAAG,SAAS;AACd,iCAAW,QAAQ;AAAA,wBACjB,MAAM;AAAA,wBACN,IAAI,GAAG;AAAA,wBACP,OAAO,MAAM;AAAA,sBACf,CAAQ;AAAA,oBACV;AAAA,kBACF;AAAA,gBACF;AAEA,oBAAI,CAAC,kBAAkB,IAAI,MAAM,IAAI,GAAG;AACtC,sBAAI,MAAM,yCAAyC;AAAA,oBACjD,MAAM,MAAM;AAAA,oBACZ;AAAA,oBACA,MAAM,OAAO,KAAK,KAAK;AAAA,kBACzB,CAAC;AAAA,gBACH;AAAA,cACF;AAGA,kBACE,IAAI,SAAS,wBACb,IAAI,UAAU,QACd;AACA,sBAAM,MAAM,IAAI;AAEhB,sBAAM,cAAc,aAAa,IAAI,GAAG;AACxC,oBAAI,eAAe,iBAAiB,IAAI,GAAG,GAAG;AAC5C,6BAAW,QAAQ;AAAA,oBACjB,MAAM;AAAA,oBACN,IAAI;AAAA,kBACN,CAAQ;AACR,mCAAiB,OAAO,GAAG;AAAA,gBAC7B;AAEA,oBAAI,iBAAiB,IAAI,GAAG,GAAG;AAC7B,+BAAa;AACb,mCAAiB,OAAO,GAAG;AAAA,gBAC7B;AAEA,sBAAM,KAAK,YAAY,IAAI,GAAG;AAC9B,oBAAI,IAAI;AASN,8BAAY,OAAO,GAAG;AACtB,sBAAI,cAAmB,CAAC;AACxB,sBAAI;AACF,kCAAc,KAAK,MAAM,GAAG,aAAa,IAAI;AAAA,kBAC/C,QAAQ;AAAA,kBAAC;AAET,sBAAI,sBAAsB,GAAG,IAAI,GAAG;AAIlC,sCAAkB,qBAAqB;AACvC,0BAAM,QAAQ,eAAe;AAC7B,+BAAW,QAAQ;AAAA,sBACjB,MAAM;AAAA,sBACN,IAAI;AAAA,sBACJ,OAAO,sBAAsB,WAAW;AAAA,oBAC1C,CAAC;AACD,iCAAa;AAAA,kBACf,WAAW,GAAG,SAAS,gBAAgB;AACrC,0BAAM,OAAQ,aAAa,QAAmB;AAE9C,wBAAI,wBAAwB;AAI1B,4BAAM,eAAe;AAAA,wBACnB;AAAA,wBACA,GAAG;AAAA,wBACH;AAAA,sBACF;AACA,4BAAMC,UAAS,eAAe;AAC9B,iCAAW,QAAQ;AAAA,wBACjB,MAAM;AAAA,wBACN,IAAIA;AAAA,wBACJ,OAAO,aAAa;AAAA,sBACtB,CAAC;AACD,6CAAuB,YAAY;AACnC;AAAA,oBACF;AAEA,0BAAM,SAAS,eAAe;AAC9B,+BAAW,QAAQ;AAAA,sBACjB,MAAM;AAAA,sBACN,IAAI;AAAA,sBACJ,OAAO;AAAA;AAAA,EAAO,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,oBACpB,CAAC;AACD,iCAAa;AAAA,kBACf,WACE,gBAAgB,GAAG,IAAI,KACvB,wBAAwB,KAAK,OAAO,SAAS,GAC7C;AAKA,0BAAM,QACJ,OAAO,aAAa,UAAU,WAC1B,YAAY,QACZ,KAAK,UAAU,WAAW;AAChC,0BAAM,WAAW,eAAe;AAChC,+BAAW,QAAQ;AAAA,sBACjB,MAAM;AAAA,sBACN,IAAI;AAAA,sBACJ,OAAO;AAAA,oBAAuB,KAAK;AAAA;AAAA,oBACrC,CAAC;AACD,iCAAa;AAAA,kBACf,WAAW,GAAG,KAAK,WAAW,iBAAiB,GAAG;AAChD,sCAAkB;AAClB,wBAAI,MAAM,oDAAoD;AAAA,sBAC5D,MAAM,GAAG;AAAA,sBACT,IAAI,GAAG;AAAA,oBACT,CAAC;AAAA,kBACH,OAAO;AACL,0BAAM;AAAA,sBACJ,MAAM;AAAA,sBACN,OAAO;AAAA,sBACP;AAAA,sBACA;AAAA,oBACF,IAAI,QAAQ,GAAG,MAAM,aAAa;AAAA,sBAChC,WAAW,KAAK,OAAO;AAAA,sBACvB,WAAW,mBAAmB,EAAE;AAAA,sBAChC,WAAW,GAAG;AAAA,oBAChB,CAAC;AAED,wBAAI,CAAC,MAAM;AACT,oCAAc,IAAI,GAAG,IAAI;AAAA,wBACvB,IAAI,GAAG;AAAA,wBACP,MAAM,GAAG;AAAA,wBACT,OAAO;AAAA,sBACT,CAAC;AACD,0BAAI,CAAC,SAAU,kBAAiB,IAAI,GAAG,EAAE;AAEzC,iCAAW,QAAQ;AAAA,wBACjB,MAAM;AAAA,wBACN,YAAY,GAAG;AAAA,wBACf,UAAU;AAAA,wBACV,OAAO,KAAK,UAAU,WAAW;AAAA,wBACjC,kBAAkB;AAAA,sBACpB,CAAQ;AAAA,oBACV;AACA,wBAAI,KAAK,sBAAsB;AAAA,sBAC7B,MAAM,GAAG;AAAA,sBACT;AAAA,sBACA,IAAI,GAAG;AAAA,sBACP;AAAA,oBACF,CAAC;AAAA,kBACH;AAAA,gBACF;AAAA,cACF;AAMA,kBACE,oBACA,IAAI,SAAS,mBACb,OAAQ,IAAY,OAAO,gBAAgB,UAC3C;AACA,iCAAkB,IAAY,MAAM;AAAA,cACtC;AAOA,kBACE,IAAI,SAAS,eACb,IAAI,WACJ,OAAQ,IAAI,QAAgB,gBAAgB,UAC5C;AACA,iCAAkB,IAAI,QAAgB;AAAA,cACxC;AAIA,kBACE,IAAI,SAAS,eACb,IAAI,SAAS,WACb,kBACA;AACA,sBAAM,iBAAkB,IAAI,QAAQ,QAAkB;AAAA,kBACpD,CAAC,MAAM,EAAE,SAAS;AAAA,gBACpB;AACA,oBAAI,eAAe,SAAS,GAAG;AAC7B,sBAAI,KAAK,qCAAqC;AAAA,oBAC5C,OAAO,eAAe;AAAA,oBACtB,SAAS,eAAe;AAAA,sBACtB,CAAC,MAAM,OAAO,EAAE,aAAa,YAAY,EAAE,SAAS,SAAS;AAAA,oBAC/D;AAAA,oBACA,mBAAmB;AAAA,kBACrB,CAAC;AACD,sBAAI,CAAC,2BAA2B;AAC9B,+BAAWD,UAAS,gBAAgB;AAClC,0BAAIA,OAAM,YAAYA,OAAM,SAAS,SAAS,GAAG;AAC/C,sCAAc;AACd,oDAA4B;AAC5B,8BAAM,aAAa,WAAW;AAC9B,mCAAW,QAAQ;AAAA,0BACjB,MAAM;AAAA,0BACN,IAAI;AAAA,wBACN,CAAQ;AACR,mCAAW,QAAQ;AAAA,0BACjB,MAAM;AAAA,0BACN,IAAI;AAAA,0BACJ,OAAOA,OAAM;AAAA,wBACf,CAAQ;AACR,mCAAW,QAAQ;AAAA,0BACjB,MAAM;AAAA,0BACN,IAAI;AAAA,wBACN,CAAQ;AAAA,sBACV;AAAA,oBACF;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AACA,kBACE,IAAI,SAAS,eACb,IAAI,SAAS,WACb,CAAC,kBACD;AACA,sBAAM,UAAU,IAAI,QAAQ,QAAQ;AAAA,kBAClC,CAAC,MAAW,EAAE,SAAS,UAAU,EAAE;AAAA,gBACrC;AACA,sBAAM,aAAa,IAAI,QAAQ,QAAQ;AAAA,kBACrC,CAAC,MAAW,EAAE,SAAS;AAAA,gBACzB;AAEA,oBAAI,SAAS;AACX,uCAAqB;AAAA,gBACvB;AAEA,oBAAI,WAAW,CAAC,YAAY;AAC1B,sCAAoB;AAAA,gBACtB;AACA,oBAAI,YAAY;AACd,qCAAmB;AAAA,gBACrB;AAEA,2BAAWA,UAAS,IAAI,QAAQ,SAAS;AACvC,sBAAIA,OAAM,SAAS,UAAUA,OAAM,MAAM;AAGvC,8CAA0B;AAC1B,0BAAM,UAAU,eAAe;AAC/B,+BAAW,QAAQ;AAAA,sBACjB,MAAM;AAAA,sBACN,IAAI;AAAA,sBACJ,OAAOA,OAAM;AAAA,oBACf,CAAC;AACD,iCAAa;AACb,oCAAgBA,OAAM,IAAI;AAC1B,yCAAqB;AAAA,kBACvB;AAEA,sBAAIA,OAAM,SAAS,cAAcA,OAAM,UAAU;AAC/C,kCAAc;AACd,0BAAM,aAAa,WAAW;AAC9B,+BAAW,QAAQ;AAAA,sBACjB,MAAM;AAAA,sBACN,IAAI;AAAA,oBACN,CAAQ;AACR,+BAAW,QAAQ;AAAA,sBACjB,MAAM;AAAA,sBACN,IAAI;AAAA,sBACJ,OAAOA,OAAM;AAAA,oBACf,CAAQ;AACR,+BAAW,QAAQ;AAAA,sBACjB,MAAM;AAAA,sBACN,IAAI;AAAA,oBACN,CAAQ;AAAA,kBACV;AAEA,sBAAIA,OAAM,SAAS,cAAcA,OAAM,MAAMA,OAAM,MAAM;AACvD,qCAAiB;AACjB,0BAAM,cAAeA,OAAM,SAAS,CAAC;AAKrC,wBAAI,sBAAsBA,OAAM,IAAI,GAAG;AACrC,4BAAM,QAAQ,eAAe;AAC7B,iCAAW,QAAQ;AAAA,wBACjB,MAAM;AAAA,wBACN,IAAI;AAAA,wBACJ,OAAO,sBAAsB,WAAW;AAAA,sBAC1C,CAAC;AACD,mCAAa;AAAA,oBACf,WAAWA,OAAM,SAAS,gBAAgB;AACxC,4BAAM,OAAQ,aAAa,QAAmB;AAE9C,0BAAI,wBAAwB;AAC1B,8BAAM,eAAe;AAAA,0BACnB;AAAA,0BACAA,OAAM;AAAA,0BACN;AAAA,wBACF;AACA,8BAAMC,UAAS,eAAe;AAC9B,mCAAW,QAAQ;AAAA,0BACjB,MAAM;AAAA,0BACN,IAAIA;AAAA,0BACJ,OAAO,aAAa;AAAA,wBACtB,CAAC;AACD,+CAAuB,YAAY;AACnC;AAAA,sBACF;AAEA,4BAAM,SAAS,eAAe;AAC9B,iCAAW,QAAQ;AAAA,wBACjB,MAAM;AAAA,wBACN,IAAI;AAAA,wBACJ,OAAO;AAAA;AAAA,EAAO,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,sBACpB,CAAC;AACD,mCAAa;AAAA,oBACf,WACE,gBAAgBD,OAAM,IAAI,KAC1B,wBAAwB,KAAK,OAAO,SAAS,GAC7C;AAIA,oCAAc,OAAOA,OAAM,EAAE;AAC7B,4BAAM,QACJ,OAAO,aAAa,UAAU,WAC1B,YAAY,QACZ,KAAK,UAAU,WAAW;AAChC,4BAAM,WAAW,eAAe;AAChC,iCAAW,QAAQ;AAAA,wBACjB,MAAM;AAAA,wBACN,IAAI;AAAA,wBACJ,OAAO;AAAA,oBAAuB,KAAK;AAAA;AAAA,sBACrC,CAAC;AACD,mCAAa;AAAA,oBACf,WAAWA,OAAM,KAAK,WAAW,iBAAiB,GAAG;AACnD,wCAAkB;AAClB,0BAAI,MAAM,kDAAkD;AAAA,wBAC1D,MAAMA,OAAM;AAAA,wBACZ,IAAIA,OAAM;AAAA,sBACZ,CAAC;AAAA,oBACH,OAAO;AACL,4BAAM;AAAA,wBACJ,MAAM;AAAA,wBACN,OAAO;AAAA,wBACP;AAAA,wBACA;AAAA,sBACF,IAAI,QAAQA,OAAM,MAAM,aAAa;AAAA,wBACnC,WAAW,KAAK,OAAO;AAAA,wBACvB,WAAW,mBAAmB,EAAE;AAAA,wBAChC,WAAWA,OAAM;AAAA,sBACnB,CAAC;AAED,0BAAI,CAAC,MAAM;AACT,sCAAc,IAAIA,OAAM,IAAI;AAAA,0BAC1B,IAAIA,OAAM;AAAA,0BACV,MAAMA,OAAM;AAAA,0BACZ,OAAO;AAAA,wBACT,CAAC;AACD,4BAAI,CAAC,SAAU,kBAAiB,IAAIA,OAAM,EAAE;AAC5C,mCAAW,QAAQ;AAAA,0BACjB,MAAM;AAAA,0BACN,IAAIA,OAAM;AAAA,0BACV,UAAU;AAAA,0BACV,kBAAkB;AAAA,wBACpB,CAAQ;AACR,mCAAW,QAAQ;AAAA,0BACjB,MAAM;AAAA,0BACN,YAAYA,OAAM;AAAA,0BAClB,UAAU;AAAA,0BACV,OAAO,KAAK,UAAU,WAAW;AAAA,0BACjC,kBAAkB;AAAA,wBACpB,CAAQ;AAAA,sBACV;AACA,0BAAI,KAAK,mCAAmC;AAAA,wBAC1C,MAAMA,OAAM;AAAA,wBACZ;AAAA,wBACA,IAAIA,OAAM;AAAA,wBACV;AAAA,sBACF,CAAC;AAAA,oBACH;AAAA,kBACF;AAEA,sBAAIA,OAAM,SAAS,eAAe;AAChC,wBAAI,MAAM,eAAe;AAAA,sBACvB,WAAWA,OAAM;AAAA,oBACnB,CAAC;AAAA,kBACH;AAAA,gBACF;AAAA,cACF;AAGA,kBAAI,IAAI,SAAS,UAAU,IAAI,SAAS,SAAS;AAC/C,2BAAWA,UAAS,IAAI,QAAQ,SAAS;AACvC,sBAAIA,OAAM,SAAS,iBAAiBA,OAAM,aAAa;AACrD,wBAAI,iBAAiB,IAAIA,OAAM,WAAW,GAAG;AAC3C,0BAAI,MAAM,2CAA2C;AAAA,wBACnD,WAAWA,OAAM;AAAA,sBACnB,CAAC;AACD;AAAA,oBACF;AAEA,wBAAI,aAAa;AACjB,wBAAI,OAAOA,OAAM,YAAY,UAAU;AACrC,mCAAaA,OAAM;AAAA,oBACrB,WAAW,MAAM,QAAQA,OAAM,OAAO,GAAG;AACvC,mCAAaA,OAAM,QAChB;AAAA,wBACC,CACE,MAEA,EAAE,SAAS,UACX,OAAO,EAAE,SAAS;AAAA,sBACtB,EACC,IAAI,CAAC,MAAM,EAAE,IAAI,EACjB,KAAK,IAAI;AAAA,oBACd;AAKA,0BAAM,kBAAkB,mBAAmB,EAAE;AAC7C,wBAAI,iBAAiB;AACnB,4BAAME,QAAO;AAAA,wBACX;AAAA,wBACAF,OAAM;AAAA,wBACN;AAAA,sBACF;AACA,0BAAIE,OAAM;AACR,8BAAM,UAAU,aAAaF,OAAM,WAAW;AAC9C,mCAAW,QAAQ;AAAA,0BACjB,MAAM;AAAA,0BACN,IAAI;AAAA,0BACJ,UAAU;AAAA,0BACV,kBAAkB;AAAA,wBACpB,CAAQ;AACR,mCAAW,QAAQ;AAAA,0BACjB,MAAM;AAAA,0BACN,YAAY;AAAA,0BACZ,UAAU;AAAA,0BACV,OAAO,KAAK,UAAU;AAAA,4BACpB,OAAOE,MAAK,IAAI,CAAC,OAAO;AAAA,8BACtB,IAAI,EAAE;AAAA,8BACN,SAAS,EAAE;AAAA,8BACX,QAAQ,EAAE;AAAA,8BACV,UAAU;AAAA,4BACZ,EAAE;AAAA,0BACJ,CAAC;AAAA,0BACD,kBAAkB;AAAA,wBACpB,CAAQ;AACR,yCAAiB;AAAA,sBACnB;AAAA,oBACF;AAEA,0BAAM,WAAW,cAAc,IAAIF,OAAM,WAAW;AACpD,wBAAI,UAAU;AAOZ,4BAAM,UAAUA,OAAM,aAAa;AACnC,iCAAW,QAAQ;AAAA,wBACjB,MAAM;AAAA,wBACN,YAAYA,OAAM;AAAA,wBAClB,UAAU,SAAS;AAAA,wBACnB,QAAQ;AAAA,0BACN,QAAQ;AAAA,0BACR,OAAO,SAAS;AAAA,0BAChB,UAAU,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC;AAAA,wBACzC;AAAA,wBACA,GAAI,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,wBACnC,kBAAkB;AAAA,sBACpB,CAAQ;AACR,uCAAiB;AACjB,0BAAI,KAAK,uBAAuB;AAAA,wBAC9B,WAAWA,OAAM;AAAA,wBACjB,MAAM,SAAS;AAAA,wBACf;AAAA,sBACF,CAAC;AACD,oCAAc,OAAOA,OAAM,WAAW;AAAA,oBACxC;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AAGA,kBAAI,IAAI,SAAS,UAAU;AACzB,mCAAmB;AAEnB,oBAAI,IAAI,YAAY;AAClB,qCAAmB,IAAI,IAAI,UAAU;AAAA,gBACvC;AAEA,oBAAI,0BAA0B,GAAG;AAG/B;AAAA,gBACF;AAKA,oBACE,CAAC,iBACD,IAAI,YACJ,OAAO,IAAI,WAAW,YACtB,IAAI,OAAO,KAAK,EAAE,SAAS,GAC3B;AACA,wBAAM,QAAQ,eAAe;AAC7B,6BAAW,QAAQ;AAAA,oBACjB,MAAM;AAAA,oBACN,IAAI;AAAA,oBACJ,OAAO,IAAI;AAAA,kBACb,CAAC;AAAA,gBACH;AAIA,oBACE,CAAC,mBACD,IAAI,YACJ,oBAAoB;AAAA,kBAClB,YAAY,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS;AAAA,gBAC5D,CAAC,GACD;AACA,oCAAkB,CAAC;AAAA,gBACrB;AAKA,oBAAI,gBAAgB,IAAI,UAAU;AAIhC,oCAAkB;AAClB,wBAAM,iBAAiB;AACvB,6BAAW,QAAQ;AAAA,oBACjB,MAAM;AAAA,oBACN,IAAI,eAAe;AAAA,oBACnB,OAAO,uBAAuB;AAAA,sBAC5B,MAAM;AAAA,sBACN,SAAS;AAAA,sBACT,WAAW,KAAK,OAAO;AAAA,sBACvB;AAAA,oBACF,CAAC;AAAA,kBACH,CAAC;AACD,+BAAa;AACb,sBAAI,KAAK,mBAAmB,aAAa,2BAA2B;AAAA,oBAClE,YAAY;AAAA,oBACZ,MAAM;AAAA,oBACN;AAAA,kBACF,CAAC;AAAA,gBACH;AAMA,sBAAM,UAAU,sBAAsB,GAAG;AACzC,oBAAI,SAAS;AACX,kCAAgB,IAAI;AACpB,6BAAW,QAAQ;AAAA,oBACjB,MAAM;AAAA,oBACN,IAAI,eAAe;AAAA,oBACnB,OAAO,wBAAwB,OAAO;AAAA,kBACxC,CAAC;AACD,sBAAI,KAAK,SAAS,EAAE,YAAY,IAAI,SAAS,IAAI,QAAQ,CAAC;AAAA,gBAC5D;AAEA,sBAAM,YAAY,iBAAiB,GAAG;AACtC,6BAAa;AAAA,kBACX,WAAW,IAAI;AAAA,kBACf,SAAS,IAAI;AAAA,kBACb,YAAY,IAAI;AAAA,kBAChB,eAAe,IAAI;AAAA,kBACnB,UAAU,IAAI;AAAA,kBACd,OAAO,IAAI;AAAA,kBACX,YAAY,IAAI;AAAA;AAAA;AAAA,kBAGhB,mBAAmB,IAAI,oBAAoB,IAAI,CAAC,YAAY;AAAA,oBAC1D,WAAW,OAAO;AAAA,oBAClB,aAAa,OAAO;AAAA,kBACtB,EAAE;AAAA,gBACJ;AAIA,oBAAI,KAAK,uBAAuB;AAAA,kBAC9B,WAAW,IAAI;AAAA,kBACf,UAAU,IAAI;AAAA,kBACd,SAAS,IAAI;AAAA,kBACb,SAAS,IAAI;AAAA,kBACb,GAAG,oBAAoB,SAAS;AAAA,gBAClC,CAAC;AAKD,oBAAI,KAAK,OAAO,aAAa,CAAC,kBAAkB,CAAC,IAAI,YAAY,CAAC,SAAS;AACzE,wBAAM,SAAS,qBAAqB,SAAS;AAC7C,sBAAI,QAAQ;AACV,+BAAW,QAAQ;AAAA,sBACjB,MAAM;AAAA,sBACN,IAAI,eAAe;AAAA,sBACnB,OAAO;AAAA,oBACT,CAAC;AAAA,kBACH;AAAA,gBACF;AAEA,gCAAgB;AAEhB,6BAAa;AAEb,sBAAM,oBACJ,CAAC,IAAI,YACL,CAAC,kBAAkB,WACnB,CAAC,kBAAkB;AAErB,oBAAI,YAAY,SAAS,KAAK,mBAAmB;AAC/C,sBAAI;AAAA,oBACF;AAAA,oBACA;AAAA,sBACE,YAAY;AAAA,sBACZ,OAAO,YAAY;AAAA,oBACrB;AAAA,kBACF;AACA;AAAA,oBACE,MAAM,eAAe,GAAG;AAAA,oBACxB;AAAA,kBACF;AACA;AAAA,gBACF;AAEA,oBACE,YAAY,WAAW,KACvB,iCACA,mBACA;AACA,sBAAI;AAAA,oBACF;AAAA,oBACA;AAAA,sBACE,YAAY;AAAA,sBACZ,SAAS;AAAA,oBACX;AAAA,kBACF;AACA;AAAA,oBACE,MAAM,eAAe,GAAG;AAAA,oBACxB;AAAA,kBACF;AACA;AAAA,gBACF;AAEA,+BAAe,GAAG;AAAA,cACpB;AAAA,YACF,SAAS,GAAG;AACV,kBAAI,MAAM,wBAAwB;AAAA,gBAChC,OACE,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,cAC7C,CAAC;AAAA,YACH;AAAA,UACF;AAEA,gBAAM,eAAe,MAAM;AACzB,gBAAI,MAAM,iBAAiB;AAC3B,gBAAI,iBAAkB;AAItB,gBAAI,YAAY,SAAS,KAAK,qBAAqB,EAAE,EAAE,SAAS,GAAG;AACjE;AAAA,gBACE;AAAA,gBACA,IAAI;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AACA,0BAAY,SAAS;AAAA,YACvB;AAMA,kBAAM,UAAU,CAAC,iBAAiB,CAAC,kBAAkB;AACrD,+BAAmB;AACnB,wBAAY;AACZ,yBAAa;AAEb,kBAAM,cAAc,CAClB,UACA,WACG;AACH,kBAAI,SAAS;AACX,oBAAI,KAAK,0CAA0C;AAAA,kBACjD,YAAY;AAAA,kBACZ;AAAA,kBACA;AAAA,kBACA,aAAa,eAAe,YAAY,UAAU;AAAA,gBACpD,CAAC;AACD,2BAAW,QAAQ;AAAA,kBACjB,MAAM;AAAA,kBACN,OAAO,IAAI;AAAA,oBACT,mBAAmB,UAAU,QAAQ,eAAe,UAAU;AAAA,kBAChE;AAAA,gBACF,CAAC;AAAA,cACH;AACA,yBAAW,QAAQ;AAAA,gBACjB,MAAM;AAAA,gBACN,cAAc,eAAe,UAAU,UAAU,MAAM;AAAA,gBACvD,OAAO,QAAQ;AAAA,gBACf,kBAAkB;AAAA,kBAChB,eAAe;AAAA,oBACb,GAAG;AAAA,oBACH,GAAI,iBACA,EAAE,iBAAiB,iBAAiB,IACpC,CAAC;AAAA,kBACP;AAAA,gBACF;AAAA,cACF,CAAC;AACD,kBAAI;AACF,2BAAW,MAAM;AAAA,cACnB,QAAQ;AAAA,cAAC;AAAA,YACX;AAMA,gBAAI,WAAW,KAAK,aAAa,QAAQ,KAAK,eAAe,MAAM;AACjE,kBAAI,WAAW;AACf,oBAAM,SAAS,CACb,UACA,WACG;AACH,oBAAI,SAAU;AACd,2BAAW;AACX,6BAAa,SAAS;AACtB,qBAAK,IAAI,QAAQ,MAAM;AACvB,4BAAY,UAAU,MAAM;AAAA,cAC9B;AACA,oBAAM,SAAS,CAAC,MAAqB,WACnC,OAAO,MAAM,MAAM;AACrB,oBAAM,YAAY;AAAA,gBAChB,MAAM,OAAO,KAAK,UAAU,KAAK,UAAU;AAAA,gBAC3C;AAAA,cACF;AACA,mBAAK,KAAK,QAAQ,MAAM;AACxB;AAAA,YACF;AACA,wBAAY,KAAK,UAAU,KAAK,UAAU;AAAA,UAC5C;AAIA,cAAI,YAAY;AAChB,gBAAM,cAAc,MAAM;AACxB,gBAAI,UAAW;AACf,wBAAY;AACZ,+BAAmB;AACnB,sCAA0B;AAC1B,+BAAmB;AACnB,gBAAI,YAAY;AACd,2BAAa,UAAU;AACvB,2BAAa;AAAA,YACf;AACA,wBAAY,IAAI,QAAQ,WAAW;AACnC,wBAAY,IAAI,SAAS,YAAY;AACrC,sCAA0B;AAC1B,sCAA0B;AAC1B,kCAAsB;AACtB,kCAAsB;AACtB,iBAAK,IAAI,SAAS,gBAAgB;AAAA,UACpC;AAEA,gBAAM,mBAAmB,CAAC,QAAe;AACvC,gBAAI,MAAM,iBAAiB,EAAE,OAAO,IAAI,QAAQ,CAAC;AACjD,gCAAoB,EAAE;AACtB,kCAAsB,EAAE;AACxB,gBAAI,iBAAkB;AAItB,gBAAI,YAAY,SAAS,KAAK,qBAAqB,EAAE,EAAE,SAAS,GAAG;AACjE;AAAA,gBACE;AAAA,gBACA,IAAI;AAAA,kBACF,gCAAgC,IAAI,OAAO;AAAA,gBAC7C;AAAA,cACF;AACA,0BAAY,SAAS;AAAA,YACvB;AACA,+BAAmB;AACnB,wBAAY;AACZ,uBAAW,QAAQ,EAAE,MAAM,SAAS,OAAO,IAAI,CAAC;AAChD,gBAAI;AACF,yBAAW,MAAM;AAAA,YACnB,QAAQ;AAAA,YAAC;AAAA,UACX;AAKA,cAAI,eAAe;AACjB,kBAAM,aAAa,oBAAoB,aAAa;AACpD,gBAAI,WAAW,MAAM,SAAS,KAAK,WAAW,UAAU,GAAG;AACzD,kBAAI,OAAO,oDAAoD;AAAA,gBAC7D,YAAY;AAAA,gBACZ,OAAO,WAAW,MAAM;AAAA,gBACxB,SAAS,WAAW;AAAA,cACtB,CAAC;AAGD,kBAAI,cAAc;AAClB;AACE,oBAAI,WAAW,UAAU,GAAG;AAC1B,wBAAM,KAAK,eAAe;AAC1B,6BAAW,QAAQ;AAAA,oBACjB,MAAM;AAAA,oBACN;AAAA,oBACA,OAAO,MAAM,WAAW,OAAO;AAAA;AAAA;AAAA,kBACjC,CAAC;AAAA,gBACH;AACA,2BAAW,QAAQ,WAAW,OAAO;AACnC,sBAAI;AACF,0BAAM,QAA6B,KAAK,MAAM,IAAI;AAClD,0BAAM,MAAM,MAAM,SAAS,kBAAkB,MAAM,QAAQ,MAAM,QAAQ;AACzE,wBAAI,OAAO;AACX,wBAAI,IAAI,SAAS,yBAAyB,IAAI,OAAO,SAAS,cAAc;AAC1E,6BAAO,IAAI,MAAM,QAAQ;AACzB,oCAAc;AAAA,oBAChB,WAAW,IAAI,SAAS,aAAa;AACnC,0BAAI,CAAC,YAAa,SAAQ,IAAI,SAAS,WAAW,CAAC,GAAG,OAAO,CAAC,SAAS,KAAK,SAAS,MAAM,EAAE,IAAI,CAAC,SAAS,KAAK,QAAQ,EAAE,EAAE,KAAK,EAAE;AACnI,oCAAc;AAAA,oBAChB,WAAW,IAAI,SAAS,UAAU;AAChC,4CAAsB;AACtB,iCAAW,SAAS,cAAc,yBAAyB,OAAO,KAAK,CAAC,GAAG;AACzE,4BAAI,gCAAgC,MAAM,IAAI,EAAG,OAAM,mBAAmB;AAAA,sBAC5E;AACA,0BAAI,MAAM,WAAY,oBAAmB,IAAI,MAAM,UAAU;AAC7D,0BAAI,IAAI,YAAY,IAAI,OAAQ,QAAO,IAAI;AAAA,oBAC7C;AACA,wBAAI,KAAM,YAAW,QAAQ,EAAE,MAAM,cAAc,IAAI,iBAAiB,eAAe,GAAG,OAAO,KAAK,CAAC;AAAA,kBACzG,QAAQ;AAAA,kBAAuD;AAAA,gBACjE;AAAA,cACF;AACA,2BAAa;AAGb,iCAAmB;AACnB,mCAAqB;AAAA,YACvB;AAAA,UACF;AAEA,cAAI,iBAAiB,CAAC,gBAAgB;AACpC,0BAAc,oBAAoB;AAClC,0BAAc,iBAAiB;AAAA,UACjC;AACA,cAAI,CAAC,gBAAgB;AAKnB,kCAAsB,kBAAkB,UAAU,CAAC,SAAS;AAC1D,kBAAI,iBAAkB,QAAO;AAC7B,oBAAM,UAAU,eAAe;AAC/B,yBAAW,QAAQ,EAAE,MAAM,cAAc,IAAI,SAAS,OAAO,KAAK,CAAC;AACnE,2BAAa;AACb,qBAAO;AAAA,YACT,CAAC;AAAA,UACH;AACA,sBAAY,GAAG,QAAQ,WAAW;AAClC,sBAAY,GAAG,SAAS,YAAY;AAEpC,oCAA0B,mBAAmB,IAAI,CAAC,SAAS;AACzD,gBAAI,kBAAkB;AAIpB,kBAAI;AAAA,gBACF;AAAA,gBACA;AAAA,kBACE,YAAY;AAAA,kBACZ,YAAY,KAAK;AAAA,kBACjB,UAAU,KAAK;AAAA,gBACjB;AAAA,cACF;AACA;AAAA,gBACE,KAAK;AAAA,gBACL,IAAI;AAAA,kBACF,uBAAuB,KAAK,QAAQ;AAAA,gBACtC;AAAA,cACF;AACA;AAAA,YACF;AACA,gBAAI,KAAK,2CAA2C;AAAA,cAClD,YAAY;AAAA,cACZ,YAAY,KAAK;AAAA,cACjB,UAAU,KAAK;AAAA,YACjB,CAAC;AACD,8BAAkB;AAClB,6BAAiB;AACjB,wBAAY,KAAK,IAAI;AACrB,gBAAI,uBAAuB,EAAG;AAC9B,gBAAI,WAAY,cAAa,UAAU;AACvC,yBAAa,WAAW,UAAU,cAAc;AAAA,UAClD,CAAC;AAED,eAAK,GAAG,SAAS,gBAAgB;AAGjC,cAAI,QAAQ,aAAa;AAMvB,kBAAM,6BAA6B,CAAC,WAAmB;AACrD,kBAAI,YAAY,WAAW,KAAK,qBAAqB,EAAE,EAAE,WAAW,EAAG;AACvE,mDAAqC,IAAI,IAAI,MAAM,MAAM,CAAC;AAC1D,0BAAY,SAAS;AAAA,YACvB;AACA,oBAAQ,YAAY,iBAAiB,SAAS,MAAM;AAClD,gCAAkB,UAAU;AAC5B,kBAAI,iBAAiB,kBAAkB;AAerC,sBAAM,iBAAiB;AACvB,oBACE,kBACA,eAAe,YAAY,cAAc,MAAM,MAAM,KACrD,qBAAqB,EAAE,EAAE,SAAS,GAClC;AACA,wBAAM,SAAS,oBAAoB,QAAQ,aAAa,MAAM;AAC9D,uBAAK,sBAAsB,QAAQ,EAAE,KAAK,CAAC,YAAY;AAGrD,0BAAM,cACJ,eAAe,YAAY,cAAc,MAAM,MAAM,KACrD,qBAAqB,EAAE,EAAE,SAAS;AAKpC,wBAAI,YAAY,UAAU,CAAC,aAAa;AACtC,0BAAI,MAAM,4FAA4F;AAAA,wBACpG;AAAA,wBACA,SAAS;AAAA,wBACT;AAAA,wBACA;AAAA,sBACF,CAAC;AACD;AAAA,oBACF;AACA,wBAAI,KAAK,gEAAgE,EAAE,IAAI,OAAO,CAAC;AACvF,yBAAK,cAAc,cAAc,EAAE,KAAK,CAAC,SAAS;AAChD,0BAAI,KAAK,mCAAmC,EAAE,IAAI,KAAK,CAAC;AAAA,oBAC1D,CAAC;AACD;AAAA,sBACE;AAAA,oBACF;AAAA,kBACF,CAAC;AAAA,gBACH;AACA;AAAA,cACF;AAMA,kBAAI,eAAe;AACjB,qBAAK,cAAc,aAAa,EAAE,KAAK,CAAC,SAAS;AAC/C,sBAAI,KAAK,mCAAmC,EAAE,IAAI,KAAK,CAAC;AAAA,gBAC1D,CAAC;AAAA,cACH;AAEA,kBAAI,CAAC,oBAAoB;AACvB,oBAAI;AAAA,kBACF;AAAA,kBACA,EAAE,IAAI;AAAA,gBACR;AACA;AAAA,kBACE;AAAA,gBACF;AACA,mCAAmB;AACnB,4BAAY;AACZ,oBAAI;AACF,6BAAW,MAAM;AAAA,gBACnB,QAAQ;AAAA,gBAAC;AACT;AAAA,cACF;AAEA,kBAAI;AAAA,gBACF;AAAA,gBACA,EAAE,IAAI;AAAA,cACR;AACA;AAAA,gBACE;AAAA,cACF;AAEA,kCAAoB,GAAK;AAAA,YAC3B,CAAC;AAAA,UACH;AAEA,cAAI,0BAA0B;AAO5B,uBAAW,EAAE,MAAM,OAAO,KAAK,6BAA6B;AAC1D,kBAAI,QAAQ;AACV,sBAAM,gBAAgB,gCAAgC,IAAI;AAC1D,oBAAI,KAAK,wDAAwD;AAAA,kBAC/D,YAAY;AAAA,kBACZ,YAAY,KAAK;AAAA,kBACjB,UAAU,KAAK;AAAA,kBACf;AAAA,gBACF,CAAC;AACD,sBAAM,cAAe,cAAe,4BAA4B,oBAAI,IAAI;AACxE,oBAAI,CAAC,YAAY,IAAI,KAAK,UAAU,GAAG;AACrC,8BAAY,IAAI,KAAK,YAAY;AAAA,oBAC/B;AAAA,oBACA;AAAA,oBACA,kBAAkB,iBAAiB;AAAA,kBACrC,CAAC;AAAA,gBACH;AAGA,4CAA4B,KAAK,YAAY,MAAM;AAAA,cACrD,OAAO;AACL,oBAAI;AAAA,kBACF;AAAA,kBACA;AAAA,oBACE,YAAY;AAAA,oBACZ,YAAY,KAAK;AAAA,oBACjB,UAAU,KAAK;AAAA,kBACjB;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAEA,gBAAI,oBAAqB,2BAA0B;AAInD,kBAAM,YAAY,qBAAqB,EAAE,EAAE;AAAA,cACzC,CAAC,SAAS,CAAC,KAAK;AAAA,YAClB;AACA,gBAAI,UAAU,SAAS,GAAG;AACxB,kBAAI,OAAO,6CAA6C;AAAA,gBACtD,YAAY;AAAA,gBACZ,aAAa,UAAU,IAAI,CAAC,SAAS,KAAK,UAAU;AAAA,cACtD,CAAC;AACD,0BAAY,KAAK,GAAG,SAAS;AAC7B,uBAAS;AACT;AAAA,YACF;AAEA,gBAAI,qBAAqB,EAAE,EAAE,WAAW,GAAG;AACzC,+BAAiB;AAAA,YACnB;AACA;AAAA,UACF;AAMA,cAAI,0BAA0B,SAAS,GAAG;AACxC,uBAAW,QAAQ,2BAA2B;AAC5C;AAAA,gBACE,KAAK;AAAA,gBACL,IAAI;AAAA,kBACF,uBAAuB,KAAK,QAAQ,MAAM,KAAK,UAAU;AAAA,gBAC3D;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAGA,cAAI,cAAe,iBAAgB,aAAa;AAChD,eAAK,OAAO,MAAM,UAAU,IAAI;AAChC,cAAI,MAAM,qBAAqB,EAAE,YAAY,QAAQ,OAAO,CAAC;AAI7D,2BAAiB;AAAA,QACjB;AAEA,aAAK,MAAM,EAAE,MAAM,CAAC,QAAQ;AAC1B,cAAI,MAAM,6BAA6B;AAAA,YACrC,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,UACxD,CAAC;AACD,qBAAW,QAAQ;AAAA,YACjB,MAAM;AAAA,YACN,OAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAAA,UAC3D,CAAC;AACD,cAAI;AACF,uBAAW,MAAM;AAAA,UACnB,QAAQ;AAAA,UAAC;AAAA,QACX,CAAC;AAAA,MACH;AAAA,MACA,SAAS;AAAA,MAET;AAAA,IACF,CAAC;AAED,WAAO;AAAA,MACL;AAAA,MACA,SAAS,EAAE,MAAM,EAAE,MAAM,QAAQ,EAAE;AAAA,MACnC,UAAU,EAAE,SAAS,CAAC,EAAE;AAAA,IAC1B;AAAA,EACF;AACF;;;A4BjwKA;AAAA,EACE,cAAAG;AAAA,EACA,gBAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,UAAAC;AAAA,EACA,iBAAAC;AAAA,OACK;AACP,SAAS,WAAAC,gBAAe;AACxB,SAAS,QAAAC,OAAM,WAAAC,gBAAe;AAC9B,SAAS,iBAAAC,sBAAqB;AAG9B,IAAM,qBAAqB;AAC3B,IAAM,4BAA4B;AAElC,IAAI,aAAa;AAEjB,SAAS,sBAAgC;AACvC,QAAM,MAAM,QAAQ,IAAI;AACxB,SAAO;AAAA,IACL,MAAMC,MAAK,KAAK,UAAU,IAAI;AAAA,IAC9BA,MAAKC,SAAQ,GAAG,UAAU,UAAU;AAAA,IACpCD,MAAKC,SAAQ,GAAG,WAAW,UAAU,UAAU;AAAA,EACjD,EAAE,OAAO,CAAC,MAAmB,QAAQ,CAAC,CAAC;AACzC;AAEA,SAAS,uBAA+B;AACtC,QAAM,YAAY,QAAQ,IAAI,mBAAmBD,MAAKC,SAAQ,GAAG,SAAS;AAC1E,SAAOD,MAAK,WAAW,YAAY,eAAe;AACpD;AAEA,SAAS,2BAAoC;AAC3C,QAAM,MAAM,qBAAqB;AACjC,MAAI,CAACE,YAAW,GAAG,EAAG,QAAO;AAC7B,MAAI;AACF,UAAM,OAAO,KAAK,MAAMC,cAAa,KAAK,MAAM,CAAC;AACjD,UAAM,UAAmB,KAAK;AAC9B,QAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,WAAO,QAAQ;AAAA,MACb,CAAC,UACC,OAAO,UAAU,YACjB,yCAAyC,KAAK,KAAK;AAAA,IACvD;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,eAA8B;AACrC,MAAI;AACF,UAAM,WAAWC,eAAc,YAAY,GAAG;AAC9C,WAAOC,cAAaC,SAAQ,UAAU,MAAM,IAAI,CAAC;AAAA,EACnD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,8BAAoC;AAClD,MAAI,WAAY;AAChB,eAAa;AAEb,MAAI,QAAQ,IAAI,2CAA2C,IAAK;AAChE,MAAI,yBAAyB,EAAG;AAEhC,QAAM,SAAS,aAAa;AAE5B,aAAW,aAAa,oBAAoB,GAAG;AAC7C,QAAI;AACF,iBAAW,WAAW,MAAM;AAAA,IAC9B,SAAS,KAAK;AACZ,UAAI,KAAK,8CAA8C;AAAA,QACrD;AAAA,QACA,OAAO,OAAO,GAAG;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,SAAS,WAAW,WAAmB,QAA6B;AAClE,MAAI,CAACJ,YAAW,SAAS,EAAG;AAE5B,QAAM,YAAYF,MAAK,WAAW,gBAAgB,kBAAkB;AACpE,MAAI,CAACE,YAAW,SAAS,EAAG;AAG5B,MAAI,gBAAgB;AACpB,MAAI;AACF,oBAAgBG,cAAa,SAAS;AAAA,EACxC,QAAQ;AAAA,EAER;AACA,MAAI,UAAU,kBAAkB,OAAQ;AAGxC,QAAM,cAAcL,MAAK,WAAW,cAAc;AAClD,MAAI,CAACE,YAAW,WAAW,EAAG;AAC9B,MAAI,MAA+C,CAAC;AACpD,MAAI;AACF,UAAM,KAAK,MAAMC,cAAa,aAAa,MAAM,CAAC;AAAA,EACpD,QAAQ;AACN;AAAA,EACF;AACA,MAAI,IAAI,SAAS,mBAAoB;AACrC,MAAI,CAAC,IAAI,aAAa,SAAS,yBAAyB,EAAG;AAE3D,MAAI,KAAK,4CAA4C,EAAE,UAAU,CAAC;AAClE,MAAI;AACF,IAAAI,QAAO,WAAW,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EACpD,SAAS,KAAK;AACZ,QAAI,KAAK,gCAAgC;AAAA,MACvC;AAAA,MACA,OAAO,OAAO,GAAG;AAAA,IACnB,CAAC;AACD;AAAA,EACF;AAKA,QAAM,eAAeP,MAAK,WAAW,cAAc;AACnD,MAAI,CAACE,YAAW,YAAY,EAAG;AAC/B,MAAI;AACF,UAAM,MAAM,KAAK,MAAMC,cAAa,cAAc,MAAM,CAAC;AACzD,QAAI,KAAK,eAAe,kBAAkB,GAAG;AAC3C,aAAO,IAAI,aAAa,kBAAkB;AAC1C,MAAAK,eAAc,cAAc,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,IAAI;AAC/D,UAAI,KAAK,mDAAmD;AAAA,IAC9D;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,KAAK,mDAAmD;AAAA,MAC1D,OAAO,OAAO,GAAG;AAAA,IACnB,CAAC;AAAA,EACH;AACF;;;AC1IA,OAAOC,WAAU;AA4CV,SAAS,oBACd,QACoB;AACpB,QAAM,QAAQ,OAAO,QAAQ,CAAC,UAAU;AACtC,QAAI,MAAM,WAAW,QAAQ,MAAM,SAAS,UAAW,QAAO,CAAC;AAC/D,UAAM,OAAO,OAAO,MAAM,OAAO,WAAW,MAAM,KAAK,MAAM;AAC7D,QAAI,OAAO,SAAS,YAAY,KAAK,WAAW,EAAG,QAAO,CAAC;AAC3D,UAAM,QAAQ,OAAO,MAAM,gBAAgB,WAAW,MAAM,YAAY,KAAK,IAAI;AACjF,WAAO,CAAC,KAAK,IAAI,KAAK,SAAS,kBAAkB,EAAE;AAAA,EACrD,CAAC;AACD,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO;AAAA,EAA6D,MAAM,KAAK,IAAI,CAAC;AACtF;AAEA,SAAS,SAAS,OAAwB;AACxC,MAAI,OAAO,UAAU,SAAU,QAAO;AAGtC,MAAI,OAAO,UAAU,YAAY;AAC/B,QAAI;AACF,YAAM,SAAU,MAAwB;AACxC,aAAO,OAAO,WAAW,WAAW,SAAS;AAAA,IAC/C,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,YAAY,MAAoD;AAC9E,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,QAAM,UAAU;AAKhB,QAAM,OAAO,QAAQ,UAAU;AAC/B,QAAM,YACJ,OAAO,SAAS,WACZ,OAAO,QAAQ,YAAY,YAAY,QAAQ,QAAQ,SAAS,IAC9DA,MAAK,QAAQ,MAAM,QAAQ,OAAO,IAClC,OACF;AACN,SAAO,EAAE,GAAI,MAAkC,WAAW,UAAU,QAAQ,SAAS;AACvF;AAEO,SAAS,mBAAmB,KAA+C;AAChF,QAAM,SAAkC,CAAC;AAEzC,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,OAAO,YAAY,YAAY;AACjC,WAAO,MAAM;AAAA;AAAA,MAEX,QAAQ,YAAY;AAClB,cAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,GAAG;AACzC,cAAM,OAA2C,CAAC;AAClD,mBAAWC,WAAU,QAAQ,QAAQ,CAAC,GAAG;AACvC,gBAAM,SAASA,SAAQ,QAAQ;AAC/B,cAAI,OAAOA,SAAQ,SAAS,YAAY,OAAO,WAAW,UAAU;AAClE,iBAAKA,QAAO,IAAI,IAAI,EAAE,OAAO;AAAA,UAC/B;AAAA,QACF;AACA,eAAO,EAAE,KAAK;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,IAAI,MAAM;AAC3B,MAAI,OAAO,aAAa,YAAY;AAClC,WAAO,OAAO;AAAA;AAAA;AAAA;AAAA,MAIZ,MAAM,YAAY;AAChB,cAAM,QAAQ,MAAM,SAAS,KAAK,IAAI,IAAI;AAC1C,cAAM,QAAQ,SAAS,CAAC,GAAG,QAAQ,CAAC,SAAS;AAC3C,gBAAM,KAAK,OAAO,MAAM,OAAO,WAAW,KAAK,KAAK,MAAM;AAC1D,iBAAO,OAAO,OAAO,WACjB,CAAC,EAAE,IAAI,aAAa,SAAS,KAAK,WAAW,GAAG,YAAY,CAAC,EAAE,CAAC,IAChE,CAAC;AAAA,QACP,CAAC;AAGD,cAAM,WAAW,KAAK,KAAK,CAAC,SAAS,KAAK,OAAO,UAAU;AAC3D,YAAI,YAAY,CAAC,KAAK,KAAK,CAAC,SAAS,KAAK,OAAO,MAAM,GAAG;AACxD,cAAI;AACJ,gBAAM,WAAW,IAAI,OAAO;AAC5B,cAAI,OAAO,aAAa,YAAY;AAClC,gBAAI;AACF,0BAAY,qBAAqB,MAAM,SAAS,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC,CAAC;AAAA,YAC9E,QAAQ;AACN,0BAAY;AAAA,YACd;AAAA,UACF;AACA,gBAAM,cAAc,YAChB,GAAG,SAAS,WAAW;AAAA;AAAA,EAAO,SAAS,KACvC,SAAS;AACb,eAAK,KAAK,EAAE,GAAG,UAAU,IAAI,QAAQ,YAAY,CAAC;AAAA,QACpD;AACA,eAAO,EAAE,KAAK;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAa,IAAI,SAAS;AAChC,MAAI,OAAO,eAAe,YAAY;AACpC,WAAO,UAAU;AAAA,MACf,KAAK,OAAO,aAAuC;AAAA,QACjD,MAAM,YAAY,MAAM,WAAW,KAAK,IAAI,SAAS,EAAE,WAAW,QAAQ,KAAK,GAAG,CAAC,CAAC;AAAA,MACtF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;AC3GO,IAAM,oBAAoB;AAG1B,IAAM,0BAA0B;AAMhC,IAAM,wBAAwB;AAIrC,IAAM,uBAAuB,CAAC,YAAY,sBAAsB;AAIhE,IAAM,2BAA2B,CAAC,SAAS,WAAW,QAAQ,MAAM;AAE7D,SAAS,uBAAuB,YAAyC;AAC9E,MAAI,OAAO,eAAe,SAAU,QAAO;AAC3C,SAAO,eAAe,oBAAoB,WAAW,WAAW,GAAG,gBAAgB,GAAG;AACxF;AAQO,SAAS,gBAAgB,MAAqB,OAAuB;AAC1E,MAAI,SAAS,aAAc,QAAO;AAClC,MAAI,SAAS,QAAS,QAAO;AAC7B,SAAO;AACT;AAEA,SAAS,WAAW,MAAkC;AACpD,QAAM,KAAK,OAAO,KAAK,MAAM,IAAI,IAAI,OAAO;AAC5C,SAAO,OAAO,SAAS,EAAE,IAAI,KAAK;AACpC;AAEA,SAAS,WAAW,OAAsD;AACxE,SAAO,OAAO,QAAQ,KAAK,EACxB,OAAO,CAAC,CAAC,EAAE,OAAO,MAAM,YAAY,IAAI,EACxC,IAAI,CAAC,CAAC,IAAI,MAAM,IAAI;AACzB;AAGO,SAAS,UACd,OACA,YACA,SACa;AACb,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,SAAS;AAAA,IACT;AAAA,IACA,MAAM,MAAM;AAAA,IACZ,QAAQ,MAAM;AAAA,IACd,cAAc;AAAA,MACZ,OAAO,MAAM,aAAa;AAAA,MAC1B,OAAO,WAAW,MAAM,aAAa,KAAK;AAAA,MAC1C,QAAQ,WAAW,MAAM,aAAa,MAAM;AAAA,IAC9C;AAAA,IACA,UAAU,OAAO,QAAQ,MAAM,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,QAAQ,OAAO;AAAA,MACtE;AAAA,MACA,UAAU,EAAE,GAAG,SAAS;AAAA,IAC1B,EAAE;AAAA,IACF,MAAM,EAAE,UAAU,WAAW,MAAM,YAAY,EAAE;AAAA,IACjD,MAAM;AAAA,MACJ;AAAA,QACE,OAAO,MAAM,KAAK;AAAA,QAClB,QAAQ,MAAM,KAAK;AAAA,QACnB,OAAO,EAAE,MAAM,MAAM,KAAK,MAAM,MAAM,OAAO,MAAM,KAAK,MAAM,MAAM;AAAA,MACtE;AAAA,IACF;AAAA,IACA,QAAQ,MAAM,UAAU;AAAA,IACxB,SAAS;AAAA,IACT,OAAO,EAAE,SAAS,MAAM,MAAM,SAAS,QAAQ,MAAM,MAAM,OAAO;AAAA,IAClE,SAAS;AAAA,EACX;AACF;AAEO,SAAS,oBAAoB,YAAoB,aAAqC;AAC3F,SAAO,OAAO,QAAQ,aAAa,EAAE;AAAA,IAAI,CAAC,CAAC,IAAI,KAAK,MAClD,UAAU,OAAO,YAAY,cAAc,GAAG,EAAE,IAAI,WAAW,KAAK,EAAE;AAAA,EACxE;AACF;AAOA,SAAS,gBAAgB,UAAwE;AAC/F,QAAM,SAAS,EAAE,GAAI,YAAY,CAAC,EAAG;AACrC,aAAW,OAAO,qBAAsB,QAAO,OAAO,GAAG;AACzD,SAAO;AACT;AAUO,SAAS,gBACd,cACA,mBACkB;AAClB,QAAM,OAAgC;AAAA,IACpC,SAAS;AAAA,IACT,YAAY,CAAC,GAAG,iBAAiB;AAAA,IACjC,GAAG,gBAAgB,YAAY;AAAA,EACjC;AACA,QAAM,WAAW,gBAAgB,cAAc,QAAQ;AAEvD,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,UACJ,IAAI;AAAA,UACJ,MAAM;AAAA,UACN,YAAY;AAAA,UACZ,SAAS;AAAA,UACT,UAAU,EAAE,GAAG,MAAM,YAAY,iBAAiB;AAAA,QACpD;AAAA,QACA,QAAQ,oBAAoB,gBAAgB;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AAEA,SAAO,SAAS,IAAI,CAAC,YAAY;AAC/B,UAAM,aAAa,kBAAkB,OAAO;AAC5C,WAAO;AAAA,MACL,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,MAAM,mBAAmB,OAAO;AAAA,QAChC,YAAY;AAAA,QACZ,SAAS;AAAA,QACT,UAAU;AAAA,UACR,GAAG;AAAA,UACH;AAAA;AAAA;AAAA,UAGA,kBAAkB;AAAA,UAClB;AAAA;AAAA;AAAA,UAGA,aAAa,OAAO,KAAK,WAAW,QAAQ;AAAA,QAC9C;AAAA,MACF;AAAA,MACA,QAAQ,oBAAoB,YAAY,mBAAmB,OAAO,CAAC;AAAA,IACrE;AAAA,EACF,CAAC;AACH;AAWA,eAAsB,mBACpB,YACA,SACA,cACqC;AACrC,QAAM,YAAY,EAAE,GAAG,aAAa;AACpC,aAAW,OAAO,yBAA0B,QAAO,UAAU,GAAG;AAChE,QAAM,SAAkC;AAAA,IACtC,GAAG,gBAAgB,EAAE,GAAI,WAAW,CAAC,GAAI,GAAG,UAAU,CAAC;AAAA,IACvD;AAAA;AAAA,IAEA,SAAS;AAAA,EACX;AAEA,QAAM,UAAU,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU;AACtE,MAAI,SAAS;AACX,UAAM,OAAO,OAAO,OAAO,eAAe,OAAO,WAAW,QAAQ;AACpE,UAAM,UAAU,MAAM,qBAAqB,SAAS,IAAI;AACxD,WAAO,OAAO,QAAQ,SAAS,EAAE,aAAa,KAAK,CAAC;AAAA,EACtD;AAEA,SAAO;AACT;AAEA,SAAS,YAAY,OAAqD;AACxE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,IACrE,QACD;AACN;AAeO,SAAS,uBACd,QACA,eACyB;AACzB,QAAM,WAAW,YAAY,YAAY,OAAO,QAAQ,IAAI,gBAAgB,CAAC;AAC7E,SAAO;AAAA,IACL,GAAI,YAAY,UAAU,OAAO,KAAK,CAAC;AAAA,IACvC,GAAI,YAAY,UAAU,QAAQ,KAAK,CAAC;AAAA,IACxC,GAAI,iBAAiB,CAAC;AAAA,EACxB;AACF;AAUO,SAAS,oBAAoB,KAAgC;AAClE,QAAM,UAAU,YAAY,GAAG;AAC/B,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,UAAU,YAAY,QAAQ,GAAG,GAAG;AAC1C,MAAI,OAAO,YAAY,UAAU;AAC/B,UAAM,QAAQ,OAAO,SAAS,QAAQ,QAAQ,MAAM,EAAE,GAAG,EAAE;AAC3D,QAAI,OAAO,SAAS,KAAK,KAAK,QAAQ,EAAG,QAAO;AAAA,EAClD;AACA,QAAM,UAAU,CAAC,QAAiB,SAChC,OAAO,YAAY,MAAM,IAAI,IAAI,MAAM;AACzC,SACE,QAAQ,QAAQ,UAAU,WAAW,KACrC,QAAQ,QAAQ,OAAO,MAAM,KAC7B,QAAQ,QAAQ,SAAS,MAAM;AAEnC;AAOO,SAAS,kBAAkB,SAAiB,MAAkC;AACnF,QAAM,QAAQ,QAAQ,IAAI,KAAK;AAC/B,SAAO,OAAO,IAAI,OAAO,IAAI,IAAI,KAAK,IAAI,OAAO;AACnD;AAGO,SAAS,mBAAmB,OAAgD;AACjF,MAAI,CAAC,SAAS,MAAM,SAAS,kBAAmB,QAAO;AACvD,QAAM,KAAK,MAAM,MAAM;AACvB,SAAO,OAAO,OAAO,YAAY,GAAG,SAAS,IAAI,KAAK;AACxD;AAEA,eAAe,uBACb,WACA,QACe;AACf,MAAI;AACF,qBAAiB,SAAS,UAAU,EAAE,OAAO,CAAC,GAAG;AAC/C,UAAI,OAAO,QAAS;AACpB,YAAM,YAAY,mBAAmB,KAAK;AAC1C,UAAI,CAAC,UAAW;AAChB,YAAM,WAAW,gCAAgC,SAAS;AAC1D,UAAI,SAAS,SAAS,GAAG;AACvB,YAAI,KAAK,6CAA6C,EAAE,WAAW,SAAS,CAAC;AAAA,MAC/E;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,CAAC,OAAO,SAAS;AACnB,UAAI,KAAK,uGAAuG;AAAA,QAC9G,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAoBO,SAAS,cAAc,MAA2D;AACvF,SAAO,OAAO,QAAQ;AACpB,QAAI,CAAC,oBAAoB,GAAG,GAAG;AAC7B,UAAI,MAAM,2DAA2D;AAAA,QACnE,SAAU,KAAqD,KAAK;AAAA,MACtE,CAAC;AACD,aAAO,MAAM;AAAA,IACf;AACA,6BAAyB;AAGzB,sBAAkB,mBAAmB,GAAiC,CAAC;AACvE,UAAM,YAAY,IAAI,UAAU;AAChC,gCAA4B,kBAAkB,SAAS,IAAI,YAAY,MAAS;AAEhF,QAAI,KAAK,oBAAoB;AAC3B,UAAI;AACF,cAAM,SAAS,KAAK,WAAW,aAAa,QAAQ,IAAI,CAAC;AAGzD,cAAM,KAAK,mBAAmB;AAAA,UAC5B,GAAG;AAAA,UACH,UAAU;AAAA,YACR,GAAI,YAAY,OAAO,QAAQ,KAAK,CAAC;AAAA,YACrC,CAAC,gBAAgB,GAAG;AAAA,cAClB,SAAS,uBAAuB,QAAQ,YAAY,IAAI,OAAO,CAAC;AAAA,YAClE;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,YAAI,KAAK,qDAAqD;AAAA,UAC5D,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QACxD,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,UAAU,oBAAI,IAAqC;AACzD,UAAM,gBAAkC,CAAC;AAEzC,kBAAc;AAAA,MACZ,MAAM,IAAI,SAAS,UAAU,CAAC,WAAW;AACvC,cAAM,OAAO,OAAO,IAAI,gBAAgB;AAGxC,cAAM,eAAe;AAAA,UACnB,GAAI,MAAM,SAAS,YAAY,CAAC;AAAA,UAChC,GAAG;AAAA,YACD,KAAK,WAAW,aAAa,QAAQ,IAAI,CAAC;AAAA,YAC1C,YAAY,IAAI,OAAO;AAAA,UACzB;AAAA,QACF;AACA,cAAM,QAAQ,gBAAgB,cAAc,KAAK,iBAAiB;AAClE,gBAAQ,MAAM;AAEd,mBAAW,QAAQ,OAAO;AACxB,kBAAQ,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,YAAY,CAAC,CAAC;AAClD,cAAI,OAAO,IAAI,KAAK,KAAK,EAAE,GAAG;AAC5B,mBAAO,OAAO,KAAK,KAAK,IAAI,CAAC,aAAa;AACxC,uBAAS,OAAO,KAAK,KAAK;AAC1B,uBAAS,aAAa,KAAK,KAAK;AAChC,uBAAS,UAAU,KAAK,KAAK;AAC7B,uBAAS,WAAW,KAAK,KAAK;AAAA,YAChC,CAAC;AACD,mBAAO,OAAO,IAAI,KAAK,KAAK,IAAI,KAAK,MAAM;AAAA,UAC7C,OAAO;AACL,mBAAO,IAAI,EAAE,MAAM,KAAK,MAAM,QAAQ,KAAK,OAAO,CAAC;AAAA,UACrD;AAAA,QACF;AAGA,YAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,SAAS,KAAK,KAAK,OAAO,gBAAgB,GAAG;AACpE,iBAAO,OAAO,gBAAgB;AAAA,QAChC;AAEA;AAAA,UACE,OAAO;AAAA,YACL,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,KAAK,IAAI,EAAE,MAAM,KAAK,KAAK,MAAM,SAAS,KAAK,KAAK,SAAS,CAAC,CAAC;AAAA,UAC3F;AAAA,UACA,IAAI,KAAK;AAAA,QACX;AAAA,MACF,CAAC;AAAA,IACH;AAEA,kBAAc;AAAA,MACZ,MAAM,IAAI,MAAM,KAAK,OAAO,OAAO,UAAU;AAC3C,cAAM,aAAa,MAAM,MAAM;AAC/B,YAAI,CAAC,uBAAuB,UAAU,EAAG;AACzC,cAAM,WAAW,MAAM,mBAAmB,YAAY,QAAQ,IAAI,UAAU,GAAG,MAAM,OAAO;AAC5F,cAAM,MAAM,KAAK,eAAe,QAAQ;AACxC,YAAI,MAAM,kBAAkB,EAAE,YAAY,SAAS,SAAS,QAAQ,CAAC;AAAA,MACvE,CAAC;AAAA,IACH;AAEA,kBAAc;AAAA,MACZ,MAAM,IAAI,QAAQ,KAAK,iBAAiB,CAAC,UAAU;AACjD,YAAI,CAAC,uBAAuB,MAAM,MAAM,UAAU,EAAG;AACrD,cAAM,QAAQ,uBAAuB,IAAI,MAAM;AAC/C,cAAM,QAAQ,qBAAqB,IAAI,gBAAgB,MAAM,MAAM,MAAM,KAAK;AAAA,MAChF,CAAC;AAAA,IACH;AAMA,UAAM,SAAS,IAAI,QAAQ;AAC3B,QAAI,IAAI,SAAS,aAAa,OAAO,WAAW,YAAY;AAC1D,YAAM,OAAO,CACX,OACA,SACA,aAEA,OACG,KAAK,IAAI,SAAS;AAAA,QACjB,GAAG,MAAM;AAAA,QACT,WAAW,MAAM;AAAA,QACjB,MAAM,kBAAkB,SAAS,MAAM,OAAO,IAAI;AAAA,QAClD;AAAA,MACF,CAAC,EACA,KAAK,MAAM,MAAS;AACzB,oBAAc;AAAA,QACZ,MAAM,IAAI,QAAQ,UAAU,CAAC,WAAW;AACtC,iBAAO,IAAI;AAAA,YACT,MAAM;AAAA,YACN,aAAa;AAAA,YACb,SAAS,CAAC,UAAU,KAAK,OAAO,gBAAgB,MAAM,QAAQ;AAAA,UAChE,CAAC;AAQD,iBAAO,IAAI;AAAA,YACT,MAAM;AAAA,YACN,aAAa;AAAA,YACb,SAAS,CAAC,UAAU,KAAK,OAAO,OAAO,OAAO;AAAA,UAChD,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAMA,UAAM,gBAAgB,sBAAsB;AAC5C,QAAI,OAAO,IAAI,OAAO,cAAc,cAAc,cAAc,SAAS,GAAG;AAC1E,oBAAc;AAAA,QACZ,MAAM,IAAI,MAAM,UAAU,CAAC,WAAW;AACpC,gBAAM,QAAkB,CAAC;AACzB,qBAAW,SAAS,eAAe;AACjC,gBAAI,OAAO,IAAI,MAAM,EAAE,EAAG;AAC1B,mBAAO,IAAI,KAAK;AAChB,kBAAM,KAAK,MAAM,EAAE;AAAA,UACrB;AACA,cAAI,MAAM,gCAAgC,EAAE,MAAM,CAAC;AAAA,QACrD,CAAC;AAAA,MACH;AAAA,IACF;AAIA,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,OAAO,IAAI,OAAO,cAAc,YAAY;AAC9C,WAAK,uBAAuB,IAAI,MAAM,UAAU,KAAK,IAAI,KAAK,GAAG,OAAO,MAAM;AAAA,IAChF;AAEA,QAAI,KAAK,4CAA4C;AAAA,MACnD,UAAU,IAAI,KAAK;AAAA,MACnB;AAAA,IACF,CAAC;AAED,WAAO,YAAY;AACjB,aAAO,MAAM;AACb,iBAAW,gBAAgB,cAAc,OAAO,CAAC,GAAG;AAClD,cAAM,aAAa,QAAQ,EAAE,MAAM,MAAM,MAAS;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AACF;;;AC1dA,SAAS,sBAAsB,OAAoC;AACjE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,MAAM;AACZ,MAAI,kBAAkB,IAAI,SAAS,EAAG,QAAO,IAAI;AACjD,MAAI,kBAAkB,IAAI,QAAQ,EAAG,QAAO,IAAI;AAChD,SAAO;AACT;AAEA,IAAI,wBAAwB;AAC5B,IAAI,uBAAuB;AAQpB,IAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,SAAS,4BAA4B,QAAiC;AAC3E,SAAO,YAAY,CAAC;AACpB,MAAI,OAAO,QAAQ,IAAK,QAAO;AAC/B,SAAO,QAAQ,MAAM;AAAA,IACnB,UAAU;AAAA,IACV,aAAa;AAAA,EACf;AACA,SAAO;AACT;AAYO,SAAS,sBAAsB,QAAiC;AACrE,SAAO,YAAY,CAAC;AACpB,MAAI,OAAO,QAAQ,cAAc,EAAG,QAAO;AAC3C,SAAO,QAAQ,cAAc,IAAI;AAAA,IAC/B,UAAU,IAAI,cAAc;AAAA,IAC5B,aAAa;AAAA,EACf;AACA,SAAO;AACT;AAEA,IAAI,0BAA0B;AAK9B,SAAS,sBAAsB,QAAmC;AAChE,MAAI,sBAAuB;AAC3B,MAAI,CAAC,QAAQ,IAAI,qBAAqB,CAAC,QAAQ,IAAI,qBAAsB;AACzE,0BAAwB;AACxB,MAAI,QAAQ;AACV,QAAI;AAAA,MACF;AAAA,IACF;AAAA,EACF,OAAO;AACL,QAAI;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAOO,SAAS,gCAAsC;AACpD,yBAAuB;AACzB;AAEO,SAAS,yBAAyB,gBAA0C;AACjF,MAAI,mBAAmB,OAAQ;AAC/B,MAAI,qBAAsB;AAC1B,yBAAuB;AACvB,MAAI;AAAA,IACF;AAAA,IACA,EAAE,gBAAgB,YAAY,sBAAsB;AAAA,EACtD;AACF;AAEO,SAAS,iBACd,WAAuC,CAAC,GACpB;AACpB,MAAI,SAAS,SAAS;AACpB,oBAAgB;AAAA,MACd,MAAM,SAAS,QAAQ,QAAQ;AAAA,MAC/B,KAAK,SAAS,QAAQ,OAAO;AAAA,MAC7B,MAAM,SAAS,QAAQ,QAAQ;AAAA,MAC/B,OAAO,SAAS,QAAQ,SAAS;AAAA,IACnC,CAAC;AAAA,EACH;AACA,wBAAsB,SAAS,qBAAqB;AACpD,2BAAyB,SAAS,cAAc;AAChD,QAAM,UACJ,SAAS,WAAW,QAAQ,IAAI,mBAAmB;AACrD,QAAM,eAAe,SAAS,cAAc,SAAS,QAAQ;AAC7D,QAAM,aAAa,SAAS,cAAc,CAAC,GAAG,wBAAwB;AAEtE,QAAM,cAAc,CAAC,YAAqC;AACxD,WAAO,IAAI,wBAAwB,SAAS;AAAA,MAC1C,UAAU;AAAA,MACV;AAAA,MACA,SAAS,SAAS;AAAA,MAClB,KAAK,SAAS;AAAA,MACd,SAAS,SAAS;AAAA,MAClB,WAAW,SAAS;AAAA,MACpB,kBAAkB,SAAS;AAAA,MAC3B,aAAa,SAAS,eAAe;AAAA,MACrC,iBAAiB,SAAS,mBAAmB;AAAA,MAC7C,YAAY,SAAS;AAAA,MACrB,iBAAiB,SAAS,mBAAmB;AAAA,MAC7C,gBAAgB,SAAS;AAAA,MACzB,WAAW,SAAS;AAAA,MACpB,iBAAiB,SAAS;AAAA,MAC1B,mBAAmB,SAAS,qBAAqB;AAAA,MACjD,wBAAwB,SAAS,0BAA0B;AAAA,MAC3D,6BAA6B,SAAS;AAAA,MACtC,2BAA2B,SAAS;AAAA,MACpC;AAAA,MACA,oBAAoB,SAAS;AAAA,MAC7B,uBAAuB,SAAS,0BAA0B;AAAA,MAC1D,sBAAsB,SAAS;AAAA,MAC/B,oBAAoB,SAAS;AAAA,MAC7B,kBAAkB,SAAS,oBAAoB;AAAA,MAC/C,WAAW,SAAS;AAAA,MACpB,cAAc,SAAS,gBAAgB;AAAA,MACvC,uBAAuB,SAAS,0BAA0B;AAAA,MAC1D,uBAAuB,SAAS,yBAAyB;AAAA,MACzD,6BACE,SAAS,+BAA+B;AAAA,MAC1C,iBAAiB,SAAS;AAAA,MAC1B,uBAAuB,SAAS;AAAA,MAChC,sBAAsB,SAAS;AAAA,MAC/B,sBAAsB,SAAS,yBAAyB;AAAA,MACxD,wBAAwB,SAAS,2BAA2B;AAAA,MAC5D,WAAW,SAAS,cAAc;AAAA,MAClC,aAAa,SAAS;AAAA,MACtB,mBAAmB,SAAS;AAAA,MAC5B,uBAAuB,SAAS;AAAA,MAChC,yBAAyB,SAAS;AAAA,IACpC,CAAC;AAAA,EACH;AAEA,QAAM,WAAW,SAAU,SAAiB;AAC1C,WAAO,YAAY,OAAO;AAAA,EAC5B;AAEA,WAAS,uBAAuB;AAChC,WAAS,gBAAgB;AAEzB,SAAO;AACT;AAMA,IAAMC,eAAc;AACpB,IAAM,cAAc;AAEpB,SAAS,mBAA2B;AAClC,SAAO,YAAY,IAAI,WAAW,OAAO,IAAI,YAAY,MAAM;AACjE;AAEA,SAAS,qBACP,UAAmC,CAAC,GACX;AACzB,QAAM,SAAS,EAAE,GAAG,QAAQ;AAC5B,SAAO,OAAO;AAEd,SAAO,OAAO;AACd,SAAO;AACT;AAEA,SAAS,yBACP,gBACA,aAAaA,cACb,aACA;AACA,QAAM,SAAS,OAAO;AAAA,IACpB,OAAO,QAAQ,aAAa,EAAE,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;AACjD,YAAM,UAAU,cAAc,GAAG,EAAE,IAAI,WAAW,KAAK;AACvD,YAAM,WAAW,eAAe,EAAE,KAAK,eAAe,OAAO;AAC7D,aAAO;AAAA,QACL;AAAA,QACA;AAAA,UACE,GAAG;AAAA,UACH,IAAI;AAAA,UACJ;AAAA,UACA,KAAK;AAAA,YACH,GAAG,MAAM;AAAA,YACT,IAAI;AAAA,YACJ,KAAK,UAAU,KAAK,OAAO,MAAM,IAAI;AAAA,YACrC,KAAK,UAAU,KAAK,OAAO,MAAM,IAAI;AAAA,UACvC;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,aAAW,CAAC,IAAI,KAAK,KAAK,OAAO,QAAQ,cAAc,GAAG;AACxD,QAAI,EAAE,MAAM,SAAS;AACnB,aAAO,EAAE,IAAI;AAAA,QACX,GAAG;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAOO,SAAS,wBACd,gBACA,YACA,aACyC;AACzC,QAAM,SAAkD,CAAC;AAEzD,aAAW,CAAC,IAAI,KAAK,KAAK,OAAO,QAAQ,aAAa,GAAG;AACvD,UAAM,UAAU,cAAc,GAAG,EAAE,IAAI,WAAW,KAAK;AACvD,UAAM,WAAW,eAAe,EAAE,KAAK,eAAe,OAAO;AAC7D,UAAM,mBACJ,YAAY,OAAQ,SAAoC,aAAa,WAC/D,SAAoE,YAAY,CAAC,IACnF,CAAC;AACP,UAAM,OAAsB;AAAA,MAC1B,GAAG;AAAA,MACH,IAAI;AAAA,MACJ;AAAA,MACA,KAAK;AAAA,QACH,GAAG,MAAM;AAAA,QACT,IAAI;AAAA,QACJ,KAAK,UAAU,KAAK,OAAO,MAAM,IAAI;AAAA,QACrC,KAAK,UAAU,KAAK,OAAO,MAAM,IAAI;AAAA,MACvC;AAAA,MACA,UAAU;AAAA,QACR,GAAI,MAAM,YAAY,CAAC;AAAA,QACvB,GAAG;AAAA,MACL;AAAA,IACF;AACA,WAAO,OAAO,IAAI,cAAc,IAAI;AAAA,EACtC;AAEA,aAAW,CAAC,IAAI,KAAK,KAAK,OAAO,QAAQ,cAAc,GAAG;AACxD,QAAI,EAAE,MAAM,SAAS;AACnB,aAAO,EAAE,IAAI,cAAc,EAAE,GAAG,OAAO,WAAW,CAAkB;AAAA,IACtE;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAe,eACb,UAMA,aAAaA,cACb,iBAA0C,CAAC,GAC3C,aACA;AACA,QAAM,gBAAyC;AAAA,IAC7C,SAAS;AAAA,IACT,YAAY,CAAC,GAAG,wBAAwB;AAAA,IACxC,GAAG;AAAA,IACH,GAAG,qBAAqB,UAAU,OAAO;AAAA,IACzC;AAAA,EACF;AAEA,QAAM,UAAU,OAAO,cAAc,WAAW,QAAQ;AACxD,QAAM,UACJ,OAAO,cAAc,YAAY,WAAW,cAAc,UAAU;AACtE,QAAM,UAAU,UACZ,MAAM,qBAAqB,SAAS,OAAO,IAC3C,EAAE,QAAQ;AAEd,SAAO;AAAA,IACL,MAAM,eAAe,UAAU;AAAA,IAC/B,KAAK,UAAU,OAAO,iBAAiB;AAAA,IACvC,SAAS;AAAA,MACP,GAAG;AAAA,MACH,GAAG;AAAA;AAAA;AAAA;AAAA,MAIH,aAAa;AAAA,IACf;AAAA;AAAA;AAAA;AAAA,EAIF;AACF;AAOO,SAAS,oBACd,WAC0C;AAC1C,QAAM,MAAgD,CAAC;AACvD,aAAW,CAAC,IAAI,KAAK,KAAK,OAAO,QAAQ,aAAa,CAAC,CAAC,GAAG;AACzD,QAAI,OAAOA,gBAAe,GAAG,WAAW,GAAGA,YAAW,GAAG,EAAG,KAAI,EAAE,IAAI;AAAA,EACxE;AACA,SAAO;AACT;AAEA,eAAe,uBAAuB,QAUjB;AACnB,QAAM,OAAO,OAAO,WAAWA,YAAW;AAC1C,QAAM,WAAW,gBAAgB,MAAM,SAAS,QAAQ;AAExD,MAAI,CAAC,SAAU,QAAO;AAEtB,SAAO,aAAa,CAAC;AAErB,QAAM,cAAc,qBAAqB,MAAM,OAAO;AACtD,MAAI,gBAAgB;AAEpB,aAAW,WAAW,UAAU;AAC9B,UAAM,aAAa,kBAAkB,OAAO;AAC5C,QAAI;AACF,YAAM,WAAW,OAAO,SAAS,UAAU;AAC3C,YAAM,cAAc,mBAAmB,OAAO;AAE9C,aAAO,SAAS,UAAU,IAAI;AAAA,QAC5B,GAAG;AAAA,QACH,GAAI,MAAM;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,YACE,GAAG;AAAA,YACH;AAAA;AAAA;AAAA;AAAA,YAIA,kBAAkB;AAAA,UACpB;AAAA,UACA,mBAAmB,OAAO;AAAA,QAC5B;AAAA,QACA,QAAQ;AAAA,UACL,UAAU,UAAU,MAAM,UAAU,CAAC;AAAA,UACtC;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,MAAM,qCAAqC;AAAA,QAC7C;AAAA,QACA;AAAA,QACA,OAAO,OAAO,GAAG;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,gBAAgB,GAAG;AACrB,WAAO,OAAO,SAASA,YAAW;AAAA,EACpC;AAEA,SAAO,gBAAgB;AACzB;AAeA,eAAsB,mBAAmB,QAAuC;AAC9E,QAAM,UAAU,OAAO,WAAWA,YAAW,GAAG;AAChD,QAAM,aAAa,SAAS;AAC5B;AAAA,IACE,OAAO,eAAe,WAAW,aAAa;AAAA,EAChD;AAKA,QAAM,UAAuC,MAAM;AAAA,IACjD;AAAA,MACE,QAAQ,IAAI,QAAQ,QAAQ,IAAI;AAAA,MAChC,4BAA4B;AAAA,IAC9B;AAAA,EACF;AAEA,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,OAAO,SAAS,CAAC,CAAC,GAAG;AAC9D,UAAM,MAAO,MAAM,WAAW,CAAC;AAC/B,UAAM,OAAO,CAAC,QAAoC;AAChD,YAAM,QAAQ,MAAM,GAAG,KAAK,IAAI,GAAG;AACnC,aAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,IAC7C;AAEA,YAAQ,IAAI,IAAI;AAAA,MACd,MAAM,KAAK,MAAM,KAAK,QAAQ,IAAI,GAAG;AAAA,MACrC,OAAO,KAAK,OAAO,KAAK,QAAQ,IAAI,GAAG;AAAA,MACvC,YAAY,KAAK,YAAY,KAAK,QAAQ,IAAI,GAAG;AAAA,MACjD,iBACE,KAAK,iBAAiB,KAAK,QAAQ,IAAI,GAAG;AAAA,IAC9C;AAAA,EACF;AAEA,mBAAiB,OAAO;AACxB,MAAI,MAAM,wBAAwB;AAAA,IAChC,QAAQ,OAAO,KAAK,OAAO,EAAE;AAAA,IAC7B,sBAAsB,wBAAwB;AAAA,EAChD,CAAC;AACH;AAOO,SAAS,wBAAwB,OAAsD;AAC5F,QAAM,UAAU,OAAO,WAAW;AAClC,MAAI,CAAC,WAAW,QAAQ,SAAS,kBAAmB,QAAO;AAC3D,QAAM,aAAa,QAAQ;AAC3B,QAAM,KAAK,YAAY,MAAM;AAC7B,SAAO,OAAO,OAAO,YAAY,GAAG,SAAS,IAAI,KAAK;AACxD;AAEA,IAAM,SAAyB,OAAO,UAAU;AAC9C,8BAA4B;AAG5B,2BAAyB;AAEzB,QAAM,kBAAkB,oBAAoB,KAAK;AAMjD,MAAI,SAAS,OAAO,UAAU,YAAY,YAAY,OAAO;AAC3D,sBAAmB,MAA+B,MAAM;AAAA,EAC1D;AAOA,8BAA4B,sBAAsB,KAAK,CAAC;AAExD,SAAO;AAAA,IACL,QAAQ,OAAO,WAAW;AACxB,UAAI,4BAA4B,MAAM,EAAG,2BAA0B;AACnE,4BAAsB,MAAM;AAI5B,+BAAyB,MAAM;AAC/B,aAAO,aAAa,CAAC;AAErB,YAAM,mBAAmB,MAAM;AAE/B,YAAM,WAAW,MAAM,uBAAuB,MAAM;AACpD,UAAI,UAAU;AACZ;AAAA,UACE,oBAAoB,OAAO,QAAQ;AAAA,UACnC;AAAA,QACF;AACA;AAAA,MACF;AAEA,YAAM,WAAW,OAAO,SAASA,YAAW;AAC5C,aAAO,SAASA,YAAW,IAAI;AAAA,QAC7B,GAAG;AAAA,QACH,GAAI,MAAM,eAAe,QAAQ;AAAA,QACjC,QAAQ;AAAA,UACL,UAAU,UAAU,CAAC;AAAA,UACtBA;AAAA,QACF;AAAA,MACF;AACA;AAAA,QACE,oBAAoB,OAAO,QAAQ;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,OAAO,OAAO,EAAE,MAAM,MAAM;AAC1B,YAAM,YAAY,wBAAwB,KAAK;AAC/C,UAAI,CAAC,UAAW;AAChB,YAAM,WAAW,gCAAgC,SAAS;AAC1D,UAAI,SAAS,SAAS,GAAG;AACvB,YAAI,KAAK,6CAA6C,EAAE,WAAW,SAAS,CAAC;AAAA,MAC/E;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,IAAIA;AAAA,MACJ,QAAQ,OAAO,aAAa,yBAAyB,SAAS,MAAM;AAAA,IACtE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,0BAA0B,OAAOC,WAAU;AACzC,UAAIA,OAAM,YAAY,SAAS,CAAC,wBAAyB;AACzD,YAAM,iBAAiB,kBAAkB,GAA0BA,MAAK;AAAA,IAC1E;AAAA,IACA,eAAe,OAAOA,QAAO,WAAW;AACtC,YAAM,aAAaA,OAAM,OAAO,cAAcA,OAAM,UAAU,MAAM;AAIpE,UAAI,MAAM,0BAA0B;AAAA,QAClC,OAAOA,OAAM;AAAA,QACb;AAAA,QACA,WAAWA,OAAM;AAAA,MACnB,CAAC;AACD,UAAI,OAAO,eAAe,SAAU;AACpC,UAAI,eAAeD,gBAAe,CAAC,WAAW,WAAW,GAAGA,YAAW,GAAG,EAAG;AAM7E,UAAI,OAAOC,OAAM,cAAc,YAAYA,OAAM,UAAU,SAAS,GAAG;AACrE,eAAO,YAAY,CAAC;AACnB,QAAC,OAAO,QAAoC,oBAAoBA,OAAM;AAAA,MACzE;AAEA,UAAI,CAACA,OAAM,MAAO;AAMlB,aAAO,YAAY,CAAC;AACnB,MAAC,OAAO,QAAoC,gBAAgBA,OAAM;AACnE,UAAI,MAAM,sCAAsC;AAAA,QAC9C,OAAOA,OAAM;AAAA,QACb,WAAWA,OAAM;AAAA,QACjB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAKA,IAAO,gBAAQ;AAAA,EACb,IAAI;AAAA,EACJ;AAAA,EACA,OAAO,cAAc;AAAA,IACnB,gBAAgB;AAAA,IAChB,mBAAmB;AAAA,IACnB,YAAY;AAAA,IACZ,oBAAoB,CAAC,WAAW,mBAAmB,MAAwB;AAAA,EAC7E,CAAC;AACH;","names":["list","classify","homedir","server","key","sessionKey","KEY_SEPARATOR","homedir","pendingQuestions","pendingKey","sessionKey","KEY_SEPARATOR","randomUUID","EventEmitter","fs","path","path","server","resolve","reject","EventEmitter","EventEmitter","sessionKey","sessionKey","randomUUID","isRecord","randomUUID","resolve","resolve","randomUUID","resolve","sessionKey","EventEmitter","resolve","execFile","fs","path","promisify","fs","path","os","crypto","resolve","execFileAsync","promisify","execFile","isRecord","SYSTEM_REMINDER_BLOCK","server","list","str","path","path","crypto","fs","os","path","fileURLToPath","fileURLToPath","dirExists","fileExists","EventEmitter","unlink","os","fs","path","randomUUID","randomUUID","EventEmitter","unlink","readFileSync","writeFileSync","unlink","homedir","tmpdir","randomUUID","dirname","join","content","path","spawn","createInterface","unlink","resolve","stream","completeResult","block","planId","list","existsSync","readFileSync","realpathSync","rmSync","writeFileSync","homedir","join","resolve","fileURLToPath","join","homedir","existsSync","readFileSync","fileURLToPath","realpathSync","resolve","rmSync","writeFileSync","path","server","PROVIDER_ID","input"]}
|
|
1
|
+
{"version":3,"sources":["../src/claude-code-language-model.ts","../src/logger.ts","../src/todo-ledger.ts","../src/tool-mapping.ts","../src/host-tools.ts","../src/account-failover.ts","../src/accounts.ts","../src/cli-events.ts","../src/plan-mode-question.ts","../src/cli-version.ts","../src/session-manager.ts","../src/proxy-mcp.ts","../src/tmp.ts","../src/proxy-broker.ts","../src/compression-store.ts","../src/side-question.ts","../src/btw-command.ts","../src/startup-diagnostics.ts","../src/mcp-bridge.ts","../src/runtime-status.ts","../src/doctor.ts","../src/turn-stats.ts","../src/message-builder.ts","../src/agent-models.ts","../src/models.ts","../src/skill-bridge.ts","../src/claude-session-wrapper.ts","../src/claude-session-bun.ts","../src/prompts.ts","../src/auto-continue.ts","../src/ask-user-question.ts","../src/fast-mode.ts","../src/call-options.ts","../src/proxy-results.ts","../src/control-request.ts","../src/spawn-planning.ts","../src/title.ts","../src/usage.ts","../src/cleanup-stale.ts","../src/v2-client.ts","../src/v2.ts","../src/index.ts"],"sourcesContent":["import type {\n LanguageModelV3,\n LanguageModelV3CallOptions,\n LanguageModelV3Content,\n LanguageModelV3FinishReason,\n LanguageModelV3StreamPart,\n LanguageModelV3Usage,\n SharedV3Warning,\n} from \"@ai-sdk/provider\"\nimport { generateId } from \"@ai-sdk/provider-utils\"\nimport type {\n ClaudeCodeConfig,\n ControlRequestBehavior,\n ClaudeStreamMessage,\n ReasoningEffort,\n} from \"./types.js\"\nimport { mapTool, isWebSearchTool, isWebSearchHandledByCli } from \"./tool-mapping.js\"\nimport { createHostToolPartTranslator, translateStreamForHost } from \"./host-tools.js\"\nimport { applyTaskCreateToolResult } from \"./todo-ledger.js\"\nimport {\n getClaudeUserMessage,\n} from \"./message-builder.js\"\nimport { resolveAgentEffort, resolveAgentModel } from \"./agent-models.js\"\nimport { parseSideQuestion, requestSideQuestion, collectSideQuestionHistory, SIDE_QUESTION_USAGE, type SideQuestionResult } from \"./side-question.js\"\nimport { BTW_NO_SESSION_MESSAGE, registerAsideSink, takeSideQuestionAnswer } from \"./btw-command.js\"\nimport {\n describeResultFailure,\n formatResultFailureNote,\n formatStreamTimeoutNote,\n isRateLimitRejected,\n parseRateLimitEvent,\n reportCompactBoundary,\n reportConversationReset,\n reportRateLimitEvent,\n reportSystemInit,\n} from \"./cli-events.js\"\nimport {\n DEFAULT_ACCOUNT,\n normalizeAccountName,\n} from \"./accounts.js\"\nimport {\n accountBlockKind,\n buildFailoverContinuationPrompt,\n consumeAccountFailoverAnswer,\n createAccountFailoverQuestionCall,\n describeAccountBlock,\n failoverCandidates,\n failoverUntil,\n formatAccountBlockNote,\n formatFailoverNote,\n formatFailoverStopNote,\n isAccountFailoverQuestionActive,\n isAccountLimitError,\n resolveFailoverSpawn,\n setAccountOverride,\n type AccountBlockKind,\n type FailoverSpawn,\n} from \"./account-failover.js\"\nimport { DOCTOR_COMMAND, buildDoctorReport, parseDoctorCommand } from \"./doctor.js\"\nimport {\n extractTurnStats,\n formatTurnStatsBlock,\n turnStatsLogPayload,\n} from \"./turn-stats.js\"\nimport { resolveSkillPluginDirs } from \"./skill-bridge.js\"\nimport { parseModelId } from \"./models.js\"\nimport {\n QUESTION_TOOL_NAME,\n consumeExitPlanModeQuestionResult,\n createExitPlanModeQuestionCall,\n type QuestionToolCall,\n} from \"./plan-mode-question.js\"\nimport type { RuntimeMcpStatus } from \"./mcp-bridge.js\"\nimport {\n getRuntimeMcpStatus,\n fetchSessionParentId,\n resolveSpawnCwdForSession,\n fetchSessionRunState,\n settleSessionRunState,\n} from \"./runtime-status.js\"\nimport {\n getActiveProcess,\n setActiveProcess,\n spawnClaudeProcess,\n buildCliArgs,\n setClaudeSessionId,\n getClaudeSessionId,\n deleteClaudeSessionId,\n deleteActiveProcess,\n deleteActiveProcessAndWait,\n respawnActiveProcess,\n resolveIdleProcessTimeoutMs,\n scheduleIdleProcessEviction,\n noteTurnStarted,\n isTurnInFlight,\n interruptTurn,\n takeUnattendedLines,\n describeChildCrash,\n claudeSpawnEnv,\n isClaudeThinkingDisabled,\n sessionKey,\n effortSessionKey,\n invalidateOtherEffortSessions,\n describeSessionKey,\n} from \"./session-manager.js\"\nimport { spawnInteractiveProcess } from \"./claude-session-wrapper.js\"\nimport {\n clearCompression,\n consumeCompressionRestart,\n getCompressionSummary,\n} from \"./compression-store.js\"\nimport { log } from \"./logger.js\"\nimport { detectCliVersion } from \"./cli-version.js\"\nimport {\n resolveDisallowedTools,\n resolveProxyOpencodeToolDefs,\n overlayTaskProxyDescription,\n overlayQuestionProxyDescription,\n filterQuestionProxyByOpencodeSupport,\n PROXY_TOOL_PREFIX,\n TASK_BATCH_TOOL_NAME,\n taskBatchTasks,\n taskBatchChildToolCallId,\n setProxyDeadlineGuard,\n type McpProxyToolResolution,\n type ModelToolEntry,\n type ProxyMcpServer,\n type ProxyToolDef,\n type ProxyToolResult,\n} from \"./proxy-mcp.js\"\nimport {\n findPendingProxyCall,\n getPendingProxyCalls,\n isPendingProxyCallChannelClosed,\n markPendingProxyCallEmitted,\n onPendingProxyCall,\n rejectAllPendingProxyCallsForSession,\n rejectPendingProxyCallById,\n resolvePendingProxyCallById,\n type PendingProxyCall,\n} from \"./proxy-broker.js\"\nimport {\n buildAppendedSystemPrompt,\n extractSystemMessages,\n QUESTION_PROXY_HINT,\n SUBAGENT_DISPATCH_HINT,\n} from \"./prompts.js\"\nimport {\n autoContinueEnabledFor,\n continuationSignature,\n makeAutoContinueMessage,\n shouldAutoContinueIncompleteTurn,\n type AutoContinueState,\n} from \"./auto-continue.js\"\nimport {\n denyMessageForTool,\n formatAskUserQuestion,\n isAskUserQuestionTool,\n} from \"./ask-user-question.js\"\nimport { reportFastModeState } from \"./fast-mode.js\"\nimport {\n describeAbortReason,\n hasNewUserContent,\n resolveCompactionModel,\n resolveOpencodeAgent,\n resolveSessionAffinity,\n} from \"./call-options.js\"\nimport {\n extractPendingProxyResultForCall,\n makeLateProxyResultMessage,\n} from \"./proxy-results.js\"\nimport {\n controlRequestBehaviorForTool,\n handleControlRequest,\n writeControlResponse,\n} from \"./control-request.js\"\nimport {\n createLiveToolInfoLoader,\n effectiveMcpConfig,\n ensureProxyServer,\n fetchLiveToolInfo,\n resolvedProxyMcpTools,\n resolvedProxyTools,\n resolvePlanModeQuestion,\n skillBridgeSpawn,\n stripContextRemindersEnabled,\n type LiveToolInfo,\n} from \"./spawn-planning.js\"\nimport {\n isTitleRequest,\n latestUserText,\n requestScope,\n synthesizeTitle,\n} from \"./title.js\"\nimport { toFinishReason, toUsage } from \"./usage.js\"\nimport { unlink } from \"node:fs/promises\"\n\n// Re-exported so importers that have always reached for these here keep\n// working after the split. The definitions live in the modules named above.\nexport {\n buildAppendedSystemPrompt,\n QUESTION_PROXY_HINT,\n SUBAGENT_DISPATCH_HINT,\n} from \"./prompts.js\"\nexport type { AppendedSystemPromptOptions } from \"./prompts.js\"\nexport {\n autoContinueEnabledFor,\n shouldAutoContinueIncompleteTurn,\n} from \"./auto-continue.js\"\nexport { denyMessageForTool, isAskUserQuestionTool } from \"./ask-user-question.js\"\nexport { reportFastModeState, _resetFastModeWarnings } from \"./fast-mode.js\"\nexport {\n DEFAULT_COMPACTION_MODEL,\n describeAbortReason,\n hasNewUserContent,\n resolveCompactionModel,\n resolveOpencodeAgent,\n resolveSessionAffinity,\n} from \"./call-options.js\"\nexport { makeLateProxyResultMessage } from \"./proxy-results.js\"\n\n/**\n * Whether opencode is still serving a proxied call whose deadline just passed.\n * Only a call opencode was actually handed (`emitted`), in a session opencode\n * positively reports `busy`, is kept: that is a permission prompt still open\n * or the tool itself still running. Anything else, including `unknown` (no\n * SDK client, no status route, the no-affinity `default` session), ends at\n * the deadline exactly as before.\n */\nexport async function isProxyCallStillServed(callId: string): Promise<boolean> {\n const pending = findPendingProxyCall(callId)\n if (!pending || pending.emitted !== true) return false\n const session = describeSessionKey(pending.sessionKey).session\n return (await fetchSessionRunState(session)) === \"busy\"\n}\n\nsetProxyDeadlineGuard(({ callId }) => isProxyCallStillServed(callId))\n\n/**\n * Stream delta types we handle explicitly. `signature_delta` is listed as\n * known-and-silent: it carries encrypted thinking-block signatures that\n * are opaque to clients (the server uses them to reconstitute thinking\n * across turns), so there's nothing for us to do but ignore it.\n */\nconst KNOWN_DELTA_TYPES = new Set([\n \"thinking_delta\",\n \"text_delta\",\n \"input_json_delta\",\n \"signature_delta\",\n])\n\nconst PROXY_RESULT_BOUNDARY_GRACE_MS = 250\n// How long a turn that lost its child waits for that child's exit status\n// before reporting the crash without one.\nconst CHILD_EXIT_STATUS_GRACE_MS = 250\n\nexport class ClaudeCodeLanguageModel implements LanguageModelV3 {\n readonly specificationVersion = \"v3\"\n readonly modelId: string\n private readonly config: ClaudeCodeConfig\n\n constructor(modelId: string, config: ClaudeCodeConfig) {\n this.modelId = modelId\n this.config = config\n }\n\n readonly supportedUrls: Record<string, RegExp[]> = {}\n\n get provider(): string {\n return this.config.provider\n }\n\n private toUsage(rawUsage?: ClaudeStreamMessage[\"usage\"]): LanguageModelV3Usage {\n return toUsage(rawUsage)\n }\n\n private toFinishReason(\n reason: \"stop\" | \"tool-calls\" | \"error\" = \"stop\",\n ): LanguageModelV3FinishReason {\n return toFinishReason(reason)\n }\n\n /**\n * Whether this call only names the session, which gets the synthetic stub\n * rather than a `claude` spawn. See `isTitleRequest` in title.ts.\n */\n private isTitleRequest(\n scope: \"tools\" | \"no-tools\",\n options: LanguageModelV3CallOptions,\n ): boolean {\n return isTitleRequest(this.config, scope, options)\n }\n\n private requestScope(options: { tools?: unknown }): \"tools\" | \"no-tools\" {\n return requestScope(options)\n }\n\n /**\n * Build the combined `--mcp-config` list and return both the list and the\n * hash of the bridged opencode MCP block. See `effectiveMcpConfig` in\n * spawn-planning.ts.\n */\n private effectiveMcpConfig(\n cwd: string,\n proxyConfigPath?: string,\n runtimeStatus?: RuntimeMcpStatus,\n excludeServers?: ReadonlySet<string>,\n ): {\n paths: string[]\n bridgedHash: string | null\n allEnabledServerNames: string[]\n } {\n return effectiveMcpConfig(\n this.config,\n cwd,\n proxyConfigPath,\n runtimeStatus,\n excludeServers,\n )\n }\n\n /** Resolve ProxyToolDef[] for the configured proxyTools names. */\n private resolvedProxyTools(): ProxyToolDef[] | null {\n return resolvedProxyTools(this.config)\n }\n\n /** Resolve ProxyToolDef[] for opencode's MCP-backed tools. */\n private resolvedProxyMcpTools(\n allEnabledServerNames: string[],\n modelTools: readonly ModelToolEntry[] | undefined,\n taken?: ReadonlySet<string>,\n ): McpProxyToolResolution | null {\n return resolvedProxyMcpTools(\n this.config,\n allEnabledServerNames,\n modelTools,\n taken,\n )\n }\n\n /** One `client.tool.list()` fetch, shaped for this turn's gates. */\n private async fetchLiveToolInfo(): Promise<LiveToolInfo> {\n return fetchLiveToolInfo(this.config, this.modelId)\n }\n\n /**\n * Whether dcp-style context reminders should be stripped from this turn's\n * messages. Config-only and synchronous, so it can be answered before the\n * spawn block resolves anything: `userMsg` is built well ahead of it.\n */\n private stripContextRemindersEnabled(): boolean {\n return stripContextRemindersEnabled(this.config)\n }\n\n /**\n * Arguments the skill bridge needs beyond `cwd` / `cliPath`: which\n * `CLAUDE_CONFIG_DIR` this spawn reads its native skills from, and whether\n * to drop the ones it already loads.\n */\n private skillBridgeSpawn(failover: FailoverSpawn): {\n configDir: string | undefined\n skipNative: boolean\n } {\n return skillBridgeSpawn(this.config, failover)\n }\n\n /** Share one lazy registry request within a turn without making it stale. */\n private createLiveToolInfoLoader(): () => Promise<LiveToolInfo> {\n return createLiveToolInfoLoader(this.config, this.modelId)\n }\n\n /**\n * Whether the ExitPlanMode approval bridge is live for this turn: the\n * operator opted in AND opencode's registry actually has the `question`\n * tool. Without the registry entry the emitted tool-call would render as\n * `⚙ invalid` and wedge the turn, so the plugin keeps the text path.\n */\n private async resolvePlanModeQuestion(\n compactionMode: boolean,\n loadLiveToolInfo = () => this.fetchLiveToolInfo(),\n ): Promise<boolean> {\n return resolvePlanModeQuestion(this.config, compactionMode, loadLiveToolInfo)\n }\n\n /**\n * Create a proxy MCP server for a single active Claude process/session.\n * The process lifecycle owns the server lifecycle via session-manager.\n */\n private async ensureProxyServer(\n tools: ProxyToolDef[],\n sessionKeyForCalls: string,\n interceptCompress: boolean,\n ): Promise<ProxyMcpServer> {\n return ensureProxyServer(\n this.config,\n tools,\n sessionKeyForCalls,\n interceptCompress,\n )\n }\n\n /**\n * Resolve the session affinity token for this LLM call. Delegates to the\n * exported `resolveSessionAffinity` helper so the logic is unit-testable.\n * Priority:\n * 1. `x-session-affinity` request header (primary).\n * 2. `opencodeSessionID` in providerOptions (chat.params hook fallback —\n * covers provider switches mid-session and title synthesis paths\n * where the header is absent).\n * 3. `\"default\"`.\n */\n private sessionAffinity(\n options: LanguageModelV3CallOptions,\n ): string {\n const headers = (options as any)?.headers as\n | Record<string, string | undefined>\n | undefined\n return resolveSessionAffinity(\n headers,\n options.providerOptions as Record<string, unknown> | undefined,\n this.config.provider,\n )\n }\n\n private controlRequestBehaviorForTool(toolName: string): ControlRequestBehavior {\n return controlRequestBehaviorForTool(this.config, toolName)\n }\n\n private writeControlResponse(\n proc: import(\"child_process\").ChildProcess,\n requestId: string,\n response?: Record<string, unknown>,\n ): void {\n writeControlResponse(proc, requestId, response)\n }\n\n /**\n * Handle Claude stream-json control requests (`can_use_tool`, etc.) and\n * respond via stdin with a matching `control_response`.\n */\n private handleControlRequest(\n msg: ClaudeStreamMessage,\n proc: import(\"child_process\").ChildProcess,\n ): boolean {\n return handleControlRequest(this.config, msg, proc)\n }\n\n private getReasoningEffort(\n providerOptions?: LanguageModelV3CallOptions[\"providerOptions\"],\n ): ReasoningEffort | undefined {\n if (!providerOptions) return undefined\n const ownKey = this.config.provider\n const bag =\n (providerOptions as any)[ownKey] ??\n (providerOptions as any)[\"claude-code\"]\n const effort = bag?.reasoningEffort\n const valid: ReasoningEffort[] = [\n \"minimal\",\n \"low\",\n \"medium\",\n \"high\",\n \"xhigh\",\n \"max\",\n ]\n return valid.includes(effort) ? effort : undefined\n }\n\n private getOpencodeAgent(options: LanguageModelV3CallOptions): string | undefined {\n return resolveOpencodeAgent(\n (options as any)?.headers as Record<string, string | undefined> | undefined,\n options.providerOptions as Record<string, unknown> | undefined,\n this.config.provider,\n )\n }\n\n private isCompactionCall(\n options: LanguageModelV3CallOptions,\n ): boolean {\n return this.getOpencodeAgent(options) === \"compaction\"\n }\n\n /**\n * Pick the model used to handle /compact. Precedence:\n * 1. `CLAUDE_CODE_COMPACTION_MODEL` env var (per-process override)\n * 2. `compactionModel` provider setting (opencode.json / .jsonc)\n * 3. Built-in default (claude-haiku-4-5)\n */\n private resolveCompactionModel(): string {\n return resolveCompactionModel(this.config.compactionModel)\n }\n\n private thinkingCliOptions(): {\n thinking?: \"enabled\"\n thinkingDisplay?: \"summarized\"\n } {\n if (isClaudeThinkingDisabled()) return {}\n\n return {\n thinking: \"enabled\",\n thinkingDisplay:\n process.env.CLAUDE_CODE_SHOW_THINKING_SUMMARIES === undefined\n ? \"summarized\"\n : undefined,\n }\n }\n\n private latestUserText(\n prompt: LanguageModelV3CallOptions[\"prompt\"],\n ): string {\n return latestUserText(prompt)\n }\n\n private synthesizeTitle(\n prompt: LanguageModelV3CallOptions[\"prompt\"],\n ): string {\n return synthesizeTitle(prompt)\n }\n\n private async doGenerateViaStream(\n options: LanguageModelV3CallOptions,\n ): Promise<Awaited<ReturnType<LanguageModelV3[\"doGenerate\"]>>> {\n const result = await this.doStream(options)\n const reader = result.stream.getReader()\n\n let text = \"\"\n let reasoning = \"\"\n const toolCalls: LanguageModelV3Content[] = []\n let finishReason = this.toFinishReason(\"stop\")\n let usage: LanguageModelV3Usage = this.toUsage()\n let providerMetadata: any\n\n while (true) {\n const { value, done } = await reader.read()\n if (done) break\n\n switch ((value as any).type) {\n case \"text-delta\":\n text += (value as any).delta ?? \"\"\n break\n case \"reasoning-delta\":\n reasoning += (value as any).delta ?? \"\"\n break\n case \"tool-call\":\n toolCalls.push({\n type: \"tool-call\",\n toolCallId: (value as any).toolCallId,\n toolName: (value as any).toolName,\n input: (value as any).input,\n providerExecuted: (value as any).providerExecuted,\n } as any)\n break\n case \"finish\":\n finishReason = (value as any).finishReason ?? finishReason\n usage = (value as any).usage ?? usage\n providerMetadata = (value as any).providerMetadata ?? providerMetadata\n break\n }\n }\n\n const content: LanguageModelV3Content[] = []\n if (reasoning) {\n content.push({ type: \"reasoning\", text: reasoning } as any)\n }\n if (text) {\n content.push({ type: \"text\", text, providerMetadata } as any)\n }\n content.push(...toolCalls)\n\n return {\n content,\n finishReason,\n usage,\n request: result.request,\n response: {\n id: generateId(),\n timestamp: new Date(),\n modelId: this.modelId,\n },\n providerMetadata,\n warnings: [],\n }\n }\n\n async doGenerate(\n options: LanguageModelV3CallOptions,\n ): Promise<Awaited<ReturnType<LanguageModelV3[\"doGenerate\"]>>> {\n const result = await this.doGenerateForHost(options)\n if (this.config.hostApi !== \"v2\") return result\n const translate = createHostToolPartTranslator(\"v2\")\n return {\n ...result,\n content: result.content.flatMap((part) => {\n const next = translate(part as any)\n return next ? [next as unknown as LanguageModelV3Content] : []\n }),\n }\n }\n\n private async doGenerateForHost(\n options: LanguageModelV3CallOptions,\n ): Promise<Awaited<ReturnType<LanguageModelV3[\"doGenerate\"]>>> {\n if (!this.isCompactionCall(options) && this.requestScope(options as any) !== \"no-tools\" && parseSideQuestion(options.prompt)) {\n return this.doGenerateViaStream(options)\n }\n const warnings: SharedV3Warning[] = []\n const scope = this.requestScope(options as any)\n const affinity = this.sessionAffinity(options)\n const cwd = await resolveSpawnCwdForSession(this.config.cwd, affinity)\n // An agent may run on a different model than the one opencode routed here\n // (see agent-models.ts). The session key must carry the effective model or\n // an overridden agent shares a claude process with its caller.\n const effectiveModelId = resolveAgentModel(\n this.getOpencodeAgent(options),\n this.modelId,\n )\n const reasoningEffort = resolveAgentEffort(\n this.getOpencodeAgent(options),\n this.getReasoningEffort(options.providerOptions),\n ) as ReasoningEffort | undefined\n // Keep effort invalidation inside one agent/provider, even when callers\n // share a model and opencode session (for example switching agents).\n const baseKey = sessionKey(\n cwd,\n `${effectiveModelId}::${scope}::${affinity}::context=${JSON.stringify([this.config.provider, this.getOpencodeAgent(options) ?? null])}`,\n )\n const sk = effortSessionKey(baseKey, reasoningEffort)\n\n // When selective proxying is enabled, doGenerate must not bypass the\n // proxy path. Reuse doStream and aggregate its events so proxied tools\n // still route through opencode permissions/execution. Same for\n // opencode MCP proxying — doStream is the only path that wires up the\n // proxy server with the dynamically-discovered MCP tool defs.\n const compactionMode = this.isCompactionCall(options)\n\n if (\n scope === \"tools\" &&\n (this.resolvedProxyTools() ||\n (this.config.proxyOpencodeMcpTools === true &&\n this.config.bridgeOpencodeMcp !== false))\n ) {\n return this.doGenerateViaStream(options)\n }\n\n // Route compaction through doStream so it gets the lean spawn path,\n // model override, and rich transcript handling. Aggregating a stream\n // for doGenerate matches what doGenerateViaStream already does for\n // proxy tools.\n if (compactionMode) {\n return this.doGenerateViaStream(options)\n }\n\n if (this.isTitleRequest(scope, options)) {\n log.info(\"doGenerate no-tools title stub\", {\n compactionMode,\n opencodeAgent: this.getOpencodeAgent(options),\n providerOptionsKeys: options.providerOptions\n ? Object.keys(options.providerOptions)\n : [],\n })\n const text = this.synthesizeTitle(options.prompt)\n return {\n content: [{ type: \"text\", text }] as any,\n finishReason: this.toFinishReason(\"stop\"),\n usage: this.toUsage({ input_tokens: 0, output_tokens: 0 }),\n request: { body: { text: \"\" } },\n response: {\n id: generateId(),\n timestamp: new Date(),\n modelId: this.modelId,\n },\n providerMetadata: {\n \"claude-code\": {\n synthetic: true,\n path: \"no-tools\",\n },\n },\n warnings,\n }\n }\n\n // Short-circuit when opencode iterates the agent loop one more time\n // after a turn already finished. The prompt ends with an assistant\n // message and has no fresh user input — spawning Claude here would\n // just produce a stub like \"No input received. Standing by\".\n if (!hasNewUserContent(options.prompt)) {\n log.info(\"doGenerate short-circuit: no new user content\")\n return {\n content: [],\n finishReason: this.toFinishReason(\"stop\"),\n usage: this.toUsage({ input_tokens: 0, output_tokens: 0 }),\n request: { body: { text: \"\" } },\n response: {\n id: generateId(),\n timestamp: new Date(),\n modelId: this.modelId,\n },\n providerMetadata: {\n \"claude-code\": { synthetic: true, path: \"no-new-user-content\" },\n },\n warnings,\n }\n }\n\n invalidateOtherEffortSessions(baseKey, reasoningEffort)\n\n const hasPriorConversation =\n options.prompt.filter((m) => m.role === \"user\" || m.role === \"assistant\")\n .length > 1\n\n // New session — clear any stale state from a previous session.\n // A compression summary is scoped to one conversation, so this is the\n // one place it is dropped: the compress restart itself calls\n // deleteClaudeSessionId, and clearing there would wipe the summary\n // just before the fresh spawn reads it.\n if (!hasPriorConversation) {\n deleteClaudeSessionId(sk)\n deleteActiveProcess(sk)\n clearCompression(sk)\n }\n\n const hasExistingSession = !!getClaudeSessionId(sk)\n const includeHistoryContext = !hasExistingSession && hasPriorConversation\n\n const userMsg =\n consumeExitPlanModeQuestionResult(sk, options.prompt as any) ??\n // doGenerate has no proxy wiring, so this process issued no tool calls\n // at all: every tool result reaching it belongs to opencode and must be\n // rendered as text rather than an orphaned `tool_result` (issue #29).\n getClaudeUserMessage(options.prompt, includeHistoryContext, {\n cliToolCallIds: new Set<string>(),\n stripContextReminders: this.stripContextRemindersEnabled(),\n })\n\n // The same account override doStream applies, with no dialog of its own:\n // a title or no-tools call must not ask anything, but it must follow the\n // account the conversation was moved to, or it bills the limited one.\n const failover = await resolveFailoverSpawn({\n account: this.config.account ?? DEFAULT_ACCOUNT,\n baseCliPath: this.config.baseCliPath ?? this.config.cliPath,\n cliPath: this.config.cliPath,\n modelId: effectiveModelId,\n })\n const cliPath = failover.cliPath\n\n // doGenerate always spawns a fresh process, never reuse session ID.\n // Pre-fetch opencode's MCP runtime status so the bridge overlays\n // UI-toggled state on top of disk config.\n const [runtimeStatus, cliVersion, planModeQuestionActive] = await Promise.all([\n getRuntimeMcpStatus(),\n detectCliVersion(cliPath),\n this.resolvePlanModeQuestion(compactionMode),\n ])\n const systemPromptFile = buildAppendedSystemPrompt(\n cwd,\n this.config.multiStepContinuation !== false,\n extractSystemMessages(options.prompt),\n // doGenerate has no proxy wiring, so `compress` is not callable here.\n // An existing summary still carries: it is this key's prior context.\n { compressEnabled: false, compressionSummary: getCompressionSummary(sk) },\n )\n const { model: spawnModelId, fast: fastMode } = parseModelId(failover.modelId)\n // The same skill bridge as doStream's spawn: Claude's Skill tool is the\n // only way a Claude-routed turn can load an opencode skill, on this path\n // as much as on the streaming one.\n const skillPluginDirs = await resolveSkillPluginDirs({\n cwd,\n cliPath,\n enabled: this.config.bridgeOpencodeSkills === true,\n ...this.skillBridgeSpawn(failover),\n })\n const cliArgs = buildCliArgs({\n sessionKey: sk,\n skipPermissions: this.config.skipPermissions !== false,\n includeSessionId: false,\n model: spawnModelId,\n permissionMode: this.config.permissionMode,\n mcpConfig: this.effectiveMcpConfig(cwd, undefined, runtimeStatus).paths,\n strictMcpConfig: this.config.strictMcpConfig,\n disallowedTools:\n this.config.webSearch === \"disabled\" ? [\"WebSearch\"] : undefined,\n appendSystemPromptFile: systemPromptFile,\n pluginDirs: skillPluginDirs,\n ...this.thinkingCliOptions(),\n fastMode,\n cliVersion,\n })\n\n log.info(\"doGenerate starting\", {\n cwd,\n model: effectiveModelId,\n requestedModel: this.modelId,\n textLength: userMsg.length,\n includeHistoryContext,\n })\n\n const { spawn } = await import(\"node:child_process\")\n const { createInterface } = await import(\"node:readline\")\n\n const proc = spawn(cliPath, cliArgs, {\n cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: claudeSpawnEnv({\n ignoreAnthropicApiKey: this.config.ignoreAnthropicApiKey,\n effort: reasoningEffort,\n }),\n shell: process.platform === \"win32\",\n })\n\n if (systemPromptFile) {\n proc.on(\"exit\", () => {\n void unlink(systemPromptFile).catch(() => {})\n })\n }\n\n const rl = createInterface({ input: proc.stdout! })\n\n let responseText = \"\"\n let thinkingText = \"\"\n let resultMeta: {\n sessionId?: string\n costUsd?: number\n durationMs?: number\n durationApiMs?: number\n numTurns?: number\n usage?: ClaudeStreamMessage[\"usage\"]\n modelUsage?: ClaudeStreamMessage[\"modelUsage\"]\n permissionDenials?: ClaudeStreamMessage[\"permission_denials\"]\n } = {}\n const toolCalls: Array<{ id: string; name: string; args: unknown }> = []\n // Streaming tool_use entries keyed by content-block index. We accumulate\n // partial_json chunks here instead of trying to JSON.parse each chunk\n // independently, and flush to `toolCalls` at content_block_stop. The\n // previous code indexed `toolCalls` by `msg.index` directly, which is\n // wrong whenever non-tool blocks (text, thinking) precede a tool_use.\n const toolCallStreams = new Map<\n number,\n { id: string; name: string; inputJson: string }\n >()\n\n // Set true once we observe a `stream_event` envelope. When on, the\n // top-level `assistant` message is a duplicate of content already\n // accumulated via the inner content_block_* events — skip it.\n let gotPartialEvents = false\n\n const result = await new Promise<\n typeof resultMeta & {\n text: string\n thinking: string\n toolCalls: typeof toolCalls\n }\n >((resolve, reject) => {\n const cleanup = () => {\n try {\n if (!proc.killed && proc.exitCode === null) proc.kill()\n } catch {}\n }\n\n rl.on(\"line\", (line) => {\n if (!line.trim()) return\n try {\n const outer: ClaudeStreamMessage = JSON.parse(line)\n\n // Unwrap stream_event envelope (--include-partial-messages).\n // Inner event uses the same content_block_* / message_* shape.\n const msg: ClaudeStreamMessage =\n outer.type === \"stream_event\" && outer.event\n ? { ...outer.event, session_id: outer.session_id }\n : outer\n\n if (outer.type === \"stream_event\") {\n gotPartialEvents = true\n }\n\n if (this.handleControlRequest(msg, proc)) {\n return\n }\n\n if (msg.type === \"system\" && msg.subtype === \"init\") {\n if (msg.session_id) {\n setClaudeSessionId(sk, msg.session_id)\n }\n reportFastModeState(msg, fastMode)\n reportSystemInit(msg, {\n ignoreAnthropicApiKey: this.config.ignoreAnthropicApiKey,\n })\n }\n\n if (msg.type === \"rate_limit_event\") {\n reportRateLimitEvent(msg)\n return\n }\n\n if (\n msg.type === \"assistant\" &&\n msg.message?.content &&\n !gotPartialEvents\n ) {\n for (const block of msg.message.content) {\n if (block.type === \"text\" && block.text) {\n responseText += block.text\n }\n if (block.type === \"thinking\" && block.thinking) {\n thinkingText += block.thinking\n }\n if (block.type === \"tool_use\" && block.id && block.name) {\n if (isAskUserQuestionTool(block.name)) {\n // Render the full question + options as visible text so\n // the user can actually see and answer it.\n const parsedInput = (block.input ?? {}) as Record<\n string,\n unknown\n >\n responseText += formatAskUserQuestion(parsedInput)\n continue\n }\n\n if (block.name === \"ExitPlanMode\") {\n const parsedInput = (block.input ?? {}) as Record<\n string,\n unknown\n >\n const plan = (parsedInput?.plan as string) || \"\"\n if (planModeQuestionActive) {\n const questionCall = createExitPlanModeQuestionCall(\n sk,\n block.id,\n plan,\n )\n responseText += questionCall.text\n toolCalls.push({\n id: questionCall.toolCallId,\n name: questionCall.toolName,\n args: questionCall.input,\n })\n continue\n }\n responseText += `\\n\\n${plan}\\n\\n---\\n**Do you want to proceed with this plan?** (yes/no)\\n`\n continue\n }\n\n toolCalls.push({\n id: block.id,\n name: block.name,\n args: block.input ?? {},\n })\n }\n }\n }\n\n if (\n msg.type === \"content_block_start\" &&\n msg.content_block &&\n msg.index !== undefined\n ) {\n if (\n msg.content_block.type === \"tool_use\" &&\n msg.content_block.id &&\n msg.content_block.name\n ) {\n toolCallStreams.set(msg.index, {\n id: msg.content_block.id,\n name: msg.content_block.name,\n inputJson: \"\",\n })\n }\n }\n\n if (\n msg.type === \"content_block_delta\" &&\n msg.delta &&\n msg.index !== undefined\n ) {\n if (msg.delta.type === \"text_delta\" && msg.delta.text) {\n responseText += msg.delta.text\n }\n if (msg.delta.type === \"thinking_delta\" && msg.delta.thinking) {\n thinkingText += msg.delta.thinking\n }\n if (\n msg.delta.type === \"input_json_delta\" &&\n msg.delta.partial_json\n ) {\n const tc = toolCallStreams.get(msg.index)\n if (tc) tc.inputJson += msg.delta.partial_json\n }\n }\n\n if (msg.type === \"content_block_stop\" && msg.index !== undefined) {\n const tc = toolCallStreams.get(msg.index)\n if (tc) {\n let args: unknown = {}\n try {\n args = tc.inputJson ? JSON.parse(tc.inputJson) : {}\n } catch (err) {\n log.warn(\"tool input JSON parse failed\", {\n name: tc.name,\n error: String(err),\n })\n }\n if (tc.name === \"ExitPlanMode\" && planModeQuestionActive) {\n const parsedInput = args as Record<string, unknown>\n const plan = (parsedInput?.plan as string) || \"\"\n const questionCall = createExitPlanModeQuestionCall(sk, tc.id, plan)\n responseText += questionCall.text\n toolCalls.push({\n id: questionCall.toolCallId,\n name: questionCall.toolName,\n args: questionCall.input,\n })\n } else {\n toolCalls.push({ id: tc.id, name: tc.name, args })\n }\n toolCallStreams.delete(msg.index)\n }\n }\n\n if (msg.type === \"result\") {\n if (msg.session_id) {\n setClaudeSessionId(sk, msg.session_id)\n }\n\n // Some CLI failures only surface user-readable text on the final\n // `result` message (without prior assistant text blocks). Preserve\n // that so callers don't receive an empty response.\n if (\n !responseText &&\n msg.is_error &&\n typeof msg.result === \"string\" &&\n msg.result.trim().length > 0\n ) {\n responseText = msg.result\n }\n\n resultMeta = {\n sessionId: msg.session_id,\n costUsd: msg.total_cost_usd,\n durationMs: msg.duration_ms,\n durationApiMs: msg.duration_api_ms,\n numTurns: msg.num_turns,\n usage: msg.usage,\n modelUsage: msg.modelUsage,\n permissionDenials: msg.permission_denials?.map((denial) => ({\n tool_name: denial.tool_name,\n tool_use_id: denial.tool_use_id,\n })),\n }\n log.info(\"conversation result\", {\n sessionId: msg.session_id,\n isError: msg.is_error,\n subtype: msg.subtype,\n ...turnStatsLogPayload(extractTurnStats(msg)),\n })\n cleanup()\n resolve({\n ...resultMeta,\n text: responseText,\n thinking: thinkingText,\n toolCalls,\n })\n }\n } catch {\n // Ignore non-JSON lines\n }\n })\n\n rl.on(\"close\", () => {\n cleanup()\n resolve({\n ...resultMeta,\n text: responseText,\n thinking: thinkingText,\n toolCalls,\n })\n })\n\n proc.on(\"error\", (err) => {\n log.error(\"process error\", { error: err.message })\n cleanup()\n reject(err)\n })\n\n proc.stderr?.on(\"data\", (data: Buffer) => {\n log.debug(\"stderr\", { data: data.toString().slice(0, 200) })\n })\n\n proc.stdin?.write(userMsg + \"\\n\")\n })\n\n const content: LanguageModelV3Content[] = []\n\n if (result.thinking) {\n content.push({\n type: \"reasoning\",\n text: result.thinking,\n } as any)\n }\n\n if (result.text) {\n content.push({\n type: \"text\",\n text: result.text,\n providerMetadata: {\n \"claude-code\": {\n sessionId: result.sessionId ?? null,\n costUsd: result.costUsd ?? null,\n durationMs: result.durationMs ?? null,\n },\n ...(typeof result.usage?.cache_creation_input_tokens === \"number\"\n ? {\n anthropic: {\n cacheCreationInputTokens:\n result.usage.cache_creation_input_tokens,\n },\n }\n : {}),\n },\n })\n }\n\n for (const tc of result.toolCalls) {\n if (tc.name === QUESTION_TOOL_NAME) {\n content.push({\n type: \"tool-call\",\n toolCallId: tc.id,\n toolName: tc.name,\n input: JSON.stringify(tc.args),\n providerExecuted: false,\n } as any)\n continue\n }\n\n const {\n name: mappedName,\n input: mappedInput,\n executed,\n skip,\n } = mapTool(tc.name, tc.args, {\n webSearch: this.config.webSearch,\n sessionId: getClaudeSessionId(sk),\n toolUseId: tc.id,\n })\n if (skip) continue\n content.push({\n type: \"tool-call\",\n toolCallId: tc.id,\n toolName: mappedName,\n input: JSON.stringify(mappedInput),\n providerExecuted: executed,\n } as any)\n }\n\n const usage = this.toUsage(result.usage)\n\n return {\n content,\n // Claude CLI's `result` message normally signals a fully-completed turn:\n // tools have already been executed internally and final assistant text\n // has been produced. ExitPlanMode is the exception: we surface it as\n // opencode's native question tool so the outer loop must run that tool.\n finishReason: this.toFinishReason(\n result.toolCalls.some((tc) => tc.name === QUESTION_TOOL_NAME)\n ? \"tool-calls\"\n : \"stop\",\n ),\n usage,\n request: { body: { text: userMsg } },\n response: {\n id: result.sessionId ?? generateId(),\n timestamp: new Date(),\n modelId: this.modelId,\n },\n providerMetadata: {\n \"claude-code\": {\n sessionId: result.sessionId ?? null,\n costUsd: result.costUsd ?? null,\n durationMs: result.durationMs ?? null,\n },\n ...(typeof result.usage?.cache_creation_input_tokens === \"number\"\n ? {\n anthropic: {\n cacheCreationInputTokens:\n result.usage.cache_creation_input_tokens,\n },\n }\n : {}),\n },\n warnings,\n }\n }\n\n /**\n * Tool parts leave in opencode 1.x's vocabulary; on opencode 2.x they are\n * renamed at this one edge (src/host-tools.ts). On V1 the stream is\n * returned untouched.\n */\n async doStream(\n options: LanguageModelV3CallOptions,\n ): Promise<Awaited<ReturnType<LanguageModelV3[\"doStream\"]>>> {\n const result = await this.doStreamForHost(options)\n return {\n ...result,\n stream: translateStreamForHost(result.stream as any, this.config.hostApi ?? \"v1\") as any,\n }\n }\n\n private async doStreamForHost(\n options: LanguageModelV3CallOptions,\n ): Promise<Awaited<ReturnType<LanguageModelV3[\"doStream\"]>>> {\n const warnings: SharedV3Warning[] = []\n const skipPermissions = this.config.skipPermissions !== false\n const scope = this.requestScope(options as any)\n const affinity = this.sessionAffinity(options)\n const cwd = await resolveSpawnCwdForSession(this.config.cwd, affinity)\n const compactionMode = this.isCompactionCall(options)\n // Use a separate session key for compaction so its short-lived spawn\n // never collides with the main conversation's claude process.\n const effectiveModelId = compactionMode\n ? this.resolveCompactionModel()\n : resolveAgentModel(\n this.getOpencodeAgent(options),\n this.modelId,\n )\n // Compaction skips request/agent effort overrides; other calls key on it.\n const reasoningEffort = compactionMode\n ? undefined\n : (resolveAgentEffort(\n this.getOpencodeAgent(options),\n this.getReasoningEffort(options.providerOptions),\n ) as ReasoningEffort | undefined)\n const baseKey = sessionKey(\n cwd,\n `${effectiveModelId}::${scope}::${affinity}::context=${JSON.stringify([this.config.provider, this.getOpencodeAgent(options) ?? null])}`,\n )\n const sk = compactionMode\n ? sessionKey(cwd, `${effectiveModelId}::compaction::${affinity}`)\n : effortSessionKey(baseKey, reasoningEffort)\n const toUsage = this.toUsage.bind(this)\n const toFinishReason = this.toFinishReason.bind(this)\n const handleControlRequest = this.handleControlRequest.bind(this)\n const flagOn = (v: string | undefined) =>\n v !== undefined &&\n ![\"\", \"0\", \"false\", \"no\", \"off\"].includes(v.trim().toLowerCase())\n // Interactive (subscription) transport: drive the claude TUI over Bun's\n // native ConPTY + JSONL tail instead of headless `--print` stream-json.\n // Prefer the provider option (config-driven, reliable in the GUI app where\n // process env vars are not inherited); fall back to the env var. Self-healing:\n // if Bun.Terminal is unavailable (e.g. not under Bun), use the headless path.\n const interactivePref =\n this.config.interactive ??\n flagOn(process.env.CLAUDE_CODE_INTERACTIVE_TRANSPORT)\n const useInteractive =\n interactivePref && typeof (globalThis as any).Bun?.Terminal === \"function\"\n const interactiveBypassRequested =\n this.config.interactiveBypass ??\n flagOn(process.env.CLAUDE_CODE_INTERACTIVE_BYPASS)\n\n // Account failover. When a previous turn hit this account's usage limit\n // and the operator picked another account, every turn from then on spawns\n // that account's wrapper instead, until the limit's reset time. The\n // override is keyed on the ACCOUNT, so it covers every session running on\n // it, subagents included. Resolved here, before anything reads `cliPath`.\n //\n // Excluded for the interactive transport, which drives a TUI over a PTY\n // with no proxy server: nothing in that path can show the form or replay\n // the conversation, so it keeps the plain rate-limit error.\n const sourceAccount = normalizeAccountName(\n this.config.account ?? DEFAULT_ACCOUNT,\n )\n const baseCliPath = this.config.baseCliPath ?? this.config.cliPath\n let failover: FailoverSpawn =\n useInteractive || compactionMode\n ? { cliPath: this.config.cliPath, modelId: effectiveModelId, failedOver: false }\n : await resolveFailoverSpawn({\n account: sourceAccount,\n baseCliPath,\n cliPath: this.config.cliPath,\n modelId: effectiveModelId,\n })\n let cliPath = failover.cliPath\n\n // Tagged onto the process each turn so the /btw command hook, which only\n // knows the opencode session id, can find it and ask it early\n // (btw-command.ts).\n const asideTransportRef = { cliPath, interactive: !!useInteractive }\n\n // `/claude-code-doctor` is answered here, by the plugin, with no CLI\n // inference at all: everything in the report is already in this process.\n // Same shape as the aside branch below, and the exchange is stripped from\n // rebuilt transcripts the same way a `/btw` pair is.\n const doctor =\n !compactionMode && scope !== \"no-tools\" ? parseDoctorCommand(options.prompt) : null\n if (doctor) {\n const doctorOptions = {\n cliPath,\n interactive: !!useInteractive,\n turnStats: this.config.turnStats === true,\n }\n const stream = new ReadableStream<LanguageModelV3StreamPart>({\n async start(controller) {\n controller.enqueue({ type: \"stream-start\", warnings })\n try {\n const text = await buildDoctorReport(doctorOptions)\n const id = generateId()\n controller.enqueue({ type: \"text-start\", id })\n controller.enqueue({ type: \"text-delta\", id, delta: text })\n controller.enqueue({ type: \"text-end\", id })\n controller.enqueue({\n type: \"finish\",\n finishReason: toFinishReason(\"stop\"),\n usage: toUsage({ input_tokens: 0, output_tokens: 0 }),\n providerMetadata: {\n \"claude-code\": { path: \"doctor\", synthetic: true, usageUnavailable: true },\n },\n })\n } catch (error) {\n controller.enqueue({ type: \"error\", error })\n } finally {\n controller.close()\n }\n },\n })\n return { stream, request: { body: { text: `/${DOCTOR_COMMAND}` } } }\n }\n\n const aside = !compactionMode && scope !== \"no-tools\" ? parseSideQuestion(options.prompt) : null\n if (aside) {\n // `/btw` is an ordinary user message in this conversation, so opencode\n // keeps the exchange, but it is answered over the CLI's side_question\n // control channel, never as a turn. The command hook normally sent the\n // question ahead, while the previous turn was still streaming, and its\n // answer is taken here; otherwise the process is idle now and is asked\n // directly. Earlier asides in this conversation ride along as history.\n const active = getActiveProcess(sk)\n const early = aside.question ? takeSideQuestionAnswer(affinity, aside.question) : undefined\n const history = collectSideQuestionHistory(options.prompt)\n const answerAside = async (): Promise<SideQuestionResult> => {\n if (!aside.question) return { response: SIDE_QUESTION_USAGE, synthetic: true }\n if (early) {\n try {\n return await early\n } catch (error) {\n log.info(\"btw: early answer failed, asking the idle process\", { error: String(error) })\n }\n }\n if (!active) return { response: BTW_NO_SESSION_MESSAGE, synthetic: true }\n return requestSideQuestion(active, aside.question, {\n cliVersion: await detectCliVersion(cliPath),\n interactive: useInteractive,\n abortSignal: options.abortSignal,\n ...(history.length ? { history } : {}),\n })\n }\n const stream = new ReadableStream<LanguageModelV3StreamPart>({\n async start(controller) {\n controller.enqueue({ type: \"stream-start\", warnings })\n try {\n const answer = await answerAside()\n const id = generateId()\n controller.enqueue({ type: \"text-start\", id })\n controller.enqueue({ type: \"text-delta\", id, delta: answer.response })\n controller.enqueue({ type: \"text-end\", id })\n controller.enqueue({\n type: \"finish\",\n finishReason: toFinishReason(\"stop\"),\n usage: toUsage({}),\n providerMetadata: { \"claude-code\": { path: \"side-question\", synthetic: answer.synthetic, usageUnavailable: true } },\n })\n } catch (error) {\n controller.enqueue({ type: \"error\", error })\n } finally {\n controller.close()\n }\n },\n })\n return { stream, request: { body: { text: aside.question } } }\n }\n\n if (this.isTitleRequest(scope, options) && !compactionMode) {\n log.info(\"doStream no-tools title stub\", {\n compactionMode,\n opencodeAgent: this.getOpencodeAgent(options),\n providerOptionsKeys: options.providerOptions\n ? Object.keys(options.providerOptions)\n : [],\n })\n const text = this.synthesizeTitle(options.prompt)\n const textId = generateId()\n const stream = new ReadableStream<LanguageModelV3StreamPart>({\n start(controller) {\n controller.enqueue({ type: \"stream-start\", warnings })\n controller.enqueue({ type: \"text-start\", id: textId } as any)\n controller.enqueue({\n type: \"text-delta\",\n id: textId,\n delta: text,\n })\n controller.enqueue({ type: \"text-end\", id: textId })\n controller.enqueue({\n type: \"finish\",\n finishReason: toFinishReason(\"stop\"),\n usage: toUsage({ input_tokens: 0, output_tokens: 0 }),\n providerMetadata: {\n \"claude-code\": {\n synthetic: true,\n path: \"no-tools\",\n },\n },\n })\n controller.close()\n },\n })\n\n return {\n stream,\n request: { body: { text: \"\" } },\n }\n }\n\n // Short-circuit when opencode iterates the agent loop one more time\n // after a turn already finished. The prompt ends with an assistant\n // message and has no fresh user input — spawning Claude here would\n // just produce a stub like \"No input received. Standing by\".\n if (!hasNewUserContent(options.prompt)) {\n log.info(\"doStream short-circuit: no new user content\")\n const stream = new ReadableStream<LanguageModelV3StreamPart>({\n start(controller) {\n controller.enqueue({ type: \"stream-start\", warnings })\n controller.enqueue({\n type: \"finish\",\n finishReason: toFinishReason(\"stop\"),\n usage: toUsage({ input_tokens: 0, output_tokens: 0 }),\n providerMetadata: {\n \"claude-code\": { synthetic: true, path: \"no-new-user-content\" },\n },\n })\n controller.close()\n },\n })\n return { stream, request: { body: { text: \"\" } } }\n }\n\n if (!compactionMode) invalidateOtherEffortSessions(baseKey, reasoningEffort)\n\n const hasPriorConversation =\n options.prompt.filter((m) => m.role === \"user\" || m.role === \"assistant\")\n .length > 1\n\n // New session — clear any stale state from a previous session.\n // A compression summary is scoped to one conversation, so this is the\n // one place it is dropped: the compress restart itself calls\n // deleteClaudeSessionId, and clearing there would wipe the summary\n // just before the fresh spawn reads it.\n if (!hasPriorConversation) {\n deleteClaudeSessionId(sk)\n deleteActiveProcess(sk)\n clearCompression(sk)\n }\n\n // The operator's answer to a failover form this session asked on an\n // earlier turn. Consumed before the session/process state below is read,\n // because a switch changes which account those belong to.\n const failoverAnswer =\n compactionMode || useInteractive\n ? null\n : consumeAccountFailoverAnswer(sk, options.prompt as any, {\n sourceAccount,\n candidates: failoverCandidates(this.config.failoverAccounts, sourceAccount),\n })\n\n if (failoverAnswer?.kind === \"stop\") {\n // Dismissed, answered `stop`, or answered with something that is not\n // one of the offered accounts. End the turn the way the rate-limit\n // error ends it today: no CLI inference, nothing spawned.\n log.warn(\"account failover declined; ending the turn\", {\n sessionKey: sk,\n account: sourceAccount,\n reason: failoverAnswer.reason,\n })\n const note = formatFailoverStopNote(failoverAnswer.reason)\n const stream = new ReadableStream<LanguageModelV3StreamPart>({\n start(controller) {\n controller.enqueue({ type: \"stream-start\", warnings })\n const id = generateId()\n controller.enqueue({ type: \"text-start\", id } as any)\n controller.enqueue({ type: \"text-delta\", id, delta: note })\n controller.enqueue({ type: \"text-end\", id })\n controller.enqueue({\n type: \"error\",\n error: new Error(\n `Claude account \"${sourceAccount}\" is out of usage and no other account was picked.`,\n ),\n })\n controller.enqueue({\n type: \"finish\",\n finishReason: { unified: \"error\" as const, raw: \"account_limit\" },\n usage: toUsage({ input_tokens: 0, output_tokens: 0 }),\n providerMetadata: {\n \"claude-code\": {\n path: \"account-failover-stop\",\n synthetic: true,\n usageUnavailable: true,\n },\n },\n })\n controller.close()\n },\n })\n return { stream, request: { body: { text: \"\" } } }\n }\n\n // The pick applies from this turn on, so re-resolve before the spawn\n // reads anything: this turn is the one that continues the task.\n let failoverNote: string | null = null\n if (failoverAnswer?.kind === \"switch\") {\n setAccountOverride(\n failoverAnswer.sourceAccount,\n failoverAnswer.target,\n failoverUntil(failoverAnswer.resetsAt),\n )\n failover = await resolveFailoverSpawn({\n account: sourceAccount,\n baseCliPath,\n cliPath: this.config.cliPath,\n modelId: effectiveModelId,\n })\n cliPath = failover.cliPath\n asideTransportRef.cliPath = cliPath\n failoverNote = formatFailoverNote({\n sourceAccount: failoverAnswer.sourceAccount,\n target: failoverAnswer.target,\n resetsAt: failoverAnswer.resetsAt,\n })\n }\n\n // A live process belongs to the account it was spawned with, and its\n // Claude transcript lives under that account's config dir, so neither can\n // follow the conversation across a switch. Dropping both here (before\n // `includeHistoryContext` is computed) is what turns the switch into a\n // fresh session with the thread replayed, and it is equally what switches\n // back once the override expires. The `?.cliPath &&` guard keeps the\n // interactive shim, which carries no path, out of it.\n const processForAccount = getActiveProcess(sk)\n if (\n !compactionMode &&\n !useInteractive &&\n processForAccount?.cliPath &&\n processForAccount.cliPath !== cliPath\n ) {\n log.notice(\"claude process belongs to another account; starting fresh\", {\n sessionKey: sk,\n was: processForAccount.cliPath,\n now: cliPath,\n failedOver: failover.failedOver,\n })\n deleteActiveProcess(sk)\n deleteClaudeSessionId(sk)\n }\n\n const hasExistingSession = !!getClaudeSessionId(sk)\n const hasActiveProcess = !!getActiveProcess(sk)\n let includeHistoryContext =\n !hasExistingSession && !hasActiveProcess && hasPriorConversation\n // A fresh session on the other account holds none of this conversation,\n // so the replay is not optional on a switch the way it is on a normal turn.\n if (failoverAnswer?.kind === \"switch\" && hasPriorConversation) {\n includeHistoryContext = true\n }\n\n // `effectiveModelId` stays intact for session keys, logs, and metadata;\n // only the name handed to the CLI gets the `-fast` marker stripped, and\n // (on a failover) the `@account` suffix the other account's wrapper would\n // not recognise.\n const { model: spawnModelId, fast: fastMode } = parseModelId(failover.modelId)\n\n const exitPlanModeQuestionResult = compactionMode\n ? null\n : consumeExitPlanModeQuestionResult(sk, options.prompt as any)\n if (exitPlanModeQuestionResult) {\n // The whole user message for this turn is the `tool_result` for the\n // pending ExitPlanMode call, so say so: an operator looking at a turn\n // that carries none of their typed text needs the reason in the log.\n log.info(\"sending plan approval decision to claude\", { sk })\n }\n // Read before the envelope is built, and used by it: only these ids were\n // issued by this CLI process, so only these may be sent back as\n // `tool_result` blocks (issue #29).\n const previousPendingProxyCalls = compactionMode\n ? []\n : getPendingProxyCalls(sk)\n // On a switch the dialog comes out of the transcript and a short note\n // telling the fresh session to carry on goes in as the current message.\n const effectivePrompt =\n failoverAnswer?.kind === \"switch\"\n ? buildFailoverContinuationPrompt(options.prompt, failoverAnswer.target)\n : options.prompt\n const userMsg =\n exitPlanModeQuestionResult ??\n getClaudeUserMessage(effectivePrompt, includeHistoryContext, {\n compactionMode,\n cliToolCallIds: new Set(previousPendingProxyCalls.map((c) => c.toolCallId)),\n stripContextReminders: this.stripContextRemindersEnabled(),\n })\n const resolvedProxy = compactionMode ? null : this.resolvedProxyTools()\n const loadLiveToolInfo = this.createLiveToolInfoLoader()\n // Resolved here, not inside the stream body: the ExitPlanMode branches\n // run in a synchronous line handler and a reused process never reaches\n // the spawn block where the registry snapshot is otherwise taken.\n const planModeQuestionActive = await this.resolvePlanModeQuestion(\n compactionMode,\n loadLiveToolInfo,\n )\n const self = this\n\n const previousPendingProxyMatches: Array<{\n call: PendingProxyCall\n result: ProxyToolResult | null\n }> = previousPendingProxyCalls.map((call) => ({\n call,\n result: extractPendingProxyResultForCall(options.prompt, call),\n }))\n const hasMatchedPendingResults = previousPendingProxyMatches.some(\n (m) => m.result !== null,\n )\n\n // Pre-fetch opencode's MCP runtime status before constructing the\n // ReadableStream so the sync hot-reload check and async setup() see\n // the same overlay snapshot. One in-process call per turn — cheap;\n // the SDK client routes through `Server.app.fetch` (no socket).\n // Detect the Claude CLI version in parallel so the spawn can decide\n // which optional flags it supports without crashing older binaries.\n const [runtimeStatus, cliVersion] = await Promise.all([\n compactionMode ? Promise.resolve(undefined) : getRuntimeMcpStatus(),\n detectCliVersion(cliPath),\n ])\n\n // Whether a usage limit on this account should end the turn with the\n // switch form. Resolved here, in the prologue, for the same reason the\n // plan-mode gate is: the `result` branch that needs the answer runs in a\n // synchronous line handler. The candidate check comes first so a\n // single-account install never pays for the two lookups behind it.\n const failoverAccounts = failoverCandidates(\n this.config.failoverAccounts,\n sourceAccount,\n )\n const failoverAskActive =\n failoverAccounts.length > 0 &&\n this.config.accountFailover !== \"off\" &&\n !compactionMode &&\n !useInteractive &&\n isAccountFailoverQuestionActive({\n configured: this.config.accountFailover,\n candidates: failoverAccounts,\n opencodeHasQuestion: (await loadLiveToolInfo()).hasQuestion,\n compactionMode,\n interactive: !!useInteractive,\n // A subagent follows its parent's account for free, because the\n // override is account-scoped. Asking it would put a form in a session\n // the operator is usually not even looking at.\n childSession: !!(await fetchSessionParentId(affinity)),\n })\n\n log.info(\"doStream starting\", {\n cwd,\n model: effectiveModelId,\n textLength: userMsg.length,\n includeHistoryContext,\n hasActiveProcess,\n reasoningEffort,\n proxyTools: resolvedProxy?.map((t) => t.name) ?? null,\n compactionMode,\n scope,\n opencodeAgent: this.getOpencodeAgent(options),\n providerOptionsKeys: options.providerOptions\n ? Object.keys(options.providerOptions)\n : [],\n })\n\n const stream = new ReadableStream<LanguageModelV3StreamPart>({\n start(controller) {\n // Compaction is a one-shot call. Don't reuse any cached process\n // from a prior compaction — each /compact gets a fresh spawn so\n // the new transcript isn't appended to a stale claude session.\n if (compactionMode) {\n deleteActiveProcess(sk)\n deleteClaudeSessionId(sk)\n }\n\n // A compress call lands mid-turn, when the child is still streaming,\n // so the reset it asks for happens here instead: drop the child and\n // its session id, and the spawn below starts clean. `userMsg` and\n // `includeHistoryContext` were resolved above while the session\n // still existed, so the fresh process is given only this turn's\n // message — the summary in its system prompt is the whole of its\n // prior context, exactly as the tool promised.\n //\n // Not while this turn carries results for the live child: evicting\n // it would send a tool_result to a process that never issued the\n // matching tool_use. The mark survives to the next turn.\n if (!compactionMode && !hasMatchedPendingResults && consumeCompressionRestart(sk)) {\n deleteActiveProcess(sk)\n deleteClaudeSessionId(sk)\n log.info(\"compress reset: dropped claude process and session id\", {\n sessionKey: sk,\n })\n }\n\n let activeProcess = getActiveProcess(sk)\n let proc: import(\"child_process\").ChildProcess\n let lineEmitter: import(\"events\").EventEmitter\n let cliArgs: string[]\n let proxyServer: ProxyMcpServer | null = activeProcess?.proxyServer ?? null\n\n const setup = async () => {\n // Wait for the old owner to exit before resuming its session ID in\n // the replacement, so two processes never append to one transcript.\n if (\n !compactionMode &&\n activeProcess &&\n self.config.hotReloadMcp !== false &&\n self.config.bridgeOpencodeMcp !== false\n ) {\n const probe = self.effectiveMcpConfig(cwd, undefined, runtimeStatus!)\n const previousHash = activeProcess.mcpHash ?? null\n if (previousHash !== probe.bridgedHash) {\n if (previousPendingProxyCalls.length > 0) {\n log.info(\"deferring MCP hot reload until proxy calls resolve\", {\n sk,\n previousHash,\n currentHash: probe.bridgedHash,\n pendingCalls: previousPendingProxyCalls.length,\n })\n } else {\n log.info(\"opencode MCP config changed, respawning claude\", {\n sk,\n previousHash,\n currentHash: probe.bridgedHash,\n })\n await deleteActiveProcessAndWait(sk)\n activeProcess = undefined\n proxyServer = null\n }\n }\n }\n\n if (useInteractive && !compactionMode) {\n // Interactive Bun-ConPTY transport. Reuse the live session if one\n // exists for this key; else spawn a new interactive claude. The\n // wrapper conforms to ActiveProcess, so reuse/eviction/hot-reload\n // and the whole emission body below work unchanged.\n const mcp = self.effectiveMcpConfig(cwd, undefined, runtimeStatus!)\n if (activeProcess) {\n proc = activeProcess.proc\n lineEmitter = activeProcess.lineEmitter\n log.debug(\"reusing active interactive session\", { sk })\n } else {\n // MCP wildcards are always derived from the live bridge config;\n // the built-in tool list is overridable via interactiveAllowTools.\n const allow = [\n ...mcp.allEnabledServerNames.map((n) => `mcp__${n}__*`),\n \"mcp__opencode_proxy__*\",\n ...(self.config.interactiveAllowTools ?? [\n \"Bash\",\n \"Edit\",\n \"Write\",\n \"Read\",\n \"WebFetch\",\n ]),\n ]\n const systemPromptFile =\n self.config.interactiveSystemPrompt === false\n ? undefined\n : buildAppendedSystemPrompt(\n cwd,\n self.config.multiStepContinuation !== false,\n // Do not forward opencode's own system prompt into the\n // interactive TUI. Live subscription-account testing\n // showed that large forwarded payload can trigger Claude\n // Code's third-party-app usage gate, while our static\n // CLI/AGENTS/continuation prompt remains safe.\n )\n if (self.config.interactiveSystemPrompt === false) {\n log.warn(\n \"interactive system prompt disabled; opencode agent prompts will not be appended\",\n )\n }\n if (interactiveBypassRequested) {\n log.warn(\n \"interactiveBypass ignored: Claude Code prompts for bypassPermissions confirmation in the interactive TUI\",\n )\n }\n // Same skill bridge as the headless spawn: the TUI's native\n // Skill tool reads `--plugin-dir` too, and the flag probe\n // keeps it off a CLI that does not know the flag.\n const skillPluginDirs = await resolveSkillPluginDirs({\n cwd,\n cliPath,\n enabled: self.config.bridgeOpencodeSkills === true,\n ...self.skillBridgeSpawn(failover),\n })\n const ap = spawnInteractiveProcess({\n cwd,\n cliPath,\n configDir: self.config.configDir,\n model: spawnModelId,\n fastMode,\n mcpConfigPaths: mcp.paths,\n pluginDirs: skillPluginDirs,\n permissionsAllow: allow,\n systemPromptFile,\n ignoreAnthropicApiKey: self.config.ignoreAnthropicApiKey,\n effort: reasoningEffort,\n })\n ap.mcpHash = mcp.bridgedHash\n setActiveProcess(sk, ap)\n proc = ap.proc\n lineEmitter = ap.lineEmitter\n activeProcess = ap\n log.info(\"spawned interactive claude session\", {\n sk,\n cliPath,\n configDir: self.config.configDir,\n model: effectiveModelId,\n })\n }\n } else {\n let spawnSystemPromptFile: string | undefined\n let spawnProxyServer: ProxyMcpServer | null = null\n let spawnMcpHash: string | null = null\n\n if (compactionMode) {\n // Compaction takes a lean spawn: no MCP servers, no proxy, no\n // appended system prompt, no disallowed-tools list. The model\n // is asked for text output only on a single turn — all the\n // normal tool wiring is pure overhead and adds latency.\n // Explicitly opt out of `--resume` so a stale id can never\n // resume into the lean spawn.\n cliArgs = buildCliArgs({\n sessionKey: sk,\n skipPermissions,\n includeSessionId: false,\n model: spawnModelId,\n permissionMode: self.config.permissionMode,\n fastMode,\n cliVersion,\n })\n } else {\n // First pass: discover which opencode MCP servers would be\n // bridged. We use this to decide which ones to re-route through\n // the proxy instead. No --mcp-config path is consumed here;\n // it's recomputed below with the exclusion set in place.\n const discovery = self.effectiveMcpConfig(\n cwd,\n undefined,\n runtimeStatus!,\n )\n\n // Fetch the proxy MCP tools (one ProxyToolDef per opencode\n // MCP-bridged tool). If discovery returns nothing or the SDK\n // is unreachable, this is null and we fall back to direct\n // bridging.\n const mcpResolution = self.resolvedProxyMcpTools(\n discovery.allEnabledServerNames,\n options.tools as readonly ModelToolEntry[] | undefined,\n new Set((resolvedProxy ?? []).map((def) => def.name)),\n )\n const proxyMcpTools = mcpResolution?.defs ?? null\n // Exclude only the servers a def was actually built for. Excluding\n // every enabled server, as this did while the resolution was always\n // null, would strand a server whose tools were not in the model's\n // tool set: dropped from `--mcp-config` and absent from the proxy,\n // so reachable by neither route.\n const excludeServers: ReadonlySet<string> | undefined = mcpResolution\n ? mcpResolution.coveredServers\n : undefined\n\n // Overlay opencode's live tool info onto the static proxy defs.\n // Both the `task` description (with the \"Available agent types\"\n // list, so the model sees which subagents exist instead of\n // grepping configs) and the `question` version gate (older\n // opencode builds lack the `question` registry entry; the def\n // must be dropped or a forwarded call renders `⚙ invalid`)\n // derive from a single tool-list fetch. Spawn-time only, like\n // the rest of this block; a reused process keeps its defs.\n const taskProxyEnabled =\n resolvedProxy?.some((t) => t.name === \"task\") ?? false\n const questionProxyEnabled =\n resolvedProxy?.some((t) => t.name === \"question\") ?? false\n // `proxyOpencodeTools` reads its defs out of the same registry\n // snapshot, so it joins the condition instead of fetching again.\n const opencodeToolsRequested =\n (self.config.proxyOpencodeTools?.length ?? 0) > 0\n log.debug(\"opencode tool forwarding gate\", {\n requested: self.config.proxyOpencodeTools ?? null,\n opencodeToolsRequested,\n })\n const liveToolInfo =\n taskProxyEnabled || questionProxyEnabled || opencodeToolsRequested\n ? await loadLiveToolInfo()\n : {\n resolved: false,\n taskDescription: undefined,\n questionDescription: undefined,\n hasQuestion: false,\n }\n let enrichedProxy = resolvedProxy\n if (enrichedProxy && taskProxyEnabled) {\n enrichedProxy = overlayTaskProxyDescription(\n enrichedProxy,\n liveToolInfo.taskDescription,\n )\n // Whether the model will see opencode's agent list is the\n // difference between a dispatch and an \"Unknown agent type\"\n // guess, so say so out loud.\n log.info(\"task proxy description overlay\", {\n applied: Boolean(liveToolInfo.taskDescription),\n liveDescriptionLength: liveToolInfo.taskDescription?.length ?? 0,\n listsAgentTypes: Boolean(\n liveToolInfo.taskDescription?.includes(\n \"Available agent types\",\n ),\n ),\n })\n }\n if (enrichedProxy && questionProxyEnabled) {\n // When the version gate is about to drop the def\n // (`hasQuestion === false`) the live description is moot,\n // so only overlay when the entry actually exists.\n enrichedProxy = overlayQuestionProxyDescription(\n enrichedProxy,\n liveToolInfo.hasQuestion\n ? liveToolInfo.questionDescription\n : undefined,\n )\n enrichedProxy = filterQuestionProxyByOpencodeSupport(\n enrichedProxy,\n liveToolInfo.hasQuestion,\n )\n // Same reasoning as the task overlay log: when the gate drops\n // the def the model silently falls back to the deny/markdown\n // path, which looks from the outside like the feature is off.\n log.info(\"question proxy version gate\", {\n opencodeHasQuestion: liveToolInfo.hasQuestion,\n kept: liveToolInfo.hasQuestion,\n })\n }\n\n // Combine the static proxy defs with any MCP-bridged proxy\n // tools. Guard against the empty case: a version gate can\n // drop every configured def (e.g. `proxyTools: [\"Question\"]`\n // on an opencode build that lacks the `question` registry\n // entry), and spinning up an MCP server with zero tools is\n // wasteful and wrong shape.\n // Opencode tools that belong to no MCP server are invisible to\n // resolvedProxyMcpTools, so an explicitly named one is resolved\n // here. It goes into the same combined list, which means the same\n // broker path, and therefore the same abort / orphan-sweep /\n // session-delete / child-exit release as every other proxy call.\n // Last in `taken`, so a static def or an MCP tool keeps a\n // contested name (`compress`) and this one is dropped with a\n // warning rather than shadowing it.\n const opencodeToolDefs = resolveProxyOpencodeToolDefs({\n requested: self.config.proxyOpencodeTools,\n items: liveToolInfo.items,\n taken: new Set(\n [...(enrichedProxy ?? []), ...(proxyMcpTools ?? [])].map(\n (t) => t.name,\n ),\n ),\n })\n if (opencodeToolDefs.length > 0) {\n log.info(\"forwarding opencode tools through the proxy\", {\n tools: opencodeToolDefs.map((t) => t.name),\n })\n }\n\n const combinedList = [\n ...(enrichedProxy ?? []),\n ...(proxyMcpTools ?? []),\n ...opencodeToolDefs,\n ]\n const combinedProxyTools: ProxyToolDef[] | null =\n combinedList.length > 0 ? combinedList : null\n\n const pluginCompressEnabled =\n enrichedProxy?.some((t) => t.name === \"compress\") ?? false\n\n if (!proxyServer && combinedProxyTools) {\n proxyServer = await self.ensureProxyServer(\n combinedProxyTools,\n sk,\n pluginCompressEnabled,\n )\n }\n\n // Whether the question proxy actually survived the version\n // gate (post-filter). Used to decide whether to inject the\n // QUESTION_PROXY_HINT — if the gate dropped the def, the\n // model must fall back to AskUserQuestion (the deny/markdown\n // path) and must NOT be told to call a proxy tool that does\n // not exist.\n const questionProxyActive =\n enrichedProxy?.some((t) => t.name === \"question\") ?? false\n\n // Compute disallowed flags from the POST-FILTER proxy list\n // (enrichedProxy), not the pre-filter one (resolvedProxy).\n // When the version gate drops `question` on an older opencode\n // build, AskUserQuestion must NOT be added to\n // --disallowedTools — otherwise the native tool is disabled\n // while the proxy replacement is absent, leaving the model\n // with no way to ask questions at all (neither proxy nor the\n // deny/markdown fallback path fires).\n const allDisallowed = resolveDisallowedTools({\n proxyTools: enrichedProxy,\n extraDisallowedTools: self.config.extraDisallowedTools,\n disableWebSearch: self.config.webSearch === \"disabled\",\n })\n const mcp = self.effectiveMcpConfig(\n cwd,\n proxyServer?.configPath(),\n runtimeStatus!,\n excludeServers,\n )\n const systemPromptFile = activeProcess\n ? undefined\n : buildAppendedSystemPrompt(\n cwd,\n self.config.multiStepContinuation !== false,\n [\n ...extractSystemMessages(options.prompt),\n ...(taskProxyEnabled ? [SUBAGENT_DISPATCH_HINT] : []),\n ...(questionProxyActive ? [QUESTION_PROXY_HINT] : []),\n ],\n {\n compressEnabled: pluginCompressEnabled,\n opencodeCompressEnabled: opencodeToolDefs.some(\n (t) => t.name === \"compress\",\n ),\n compressionSummary: getCompressionSummary(sk),\n },\n )\n // Skill bridge (@broskees): stage opencode skills as a\n // session-scoped --plugin-dir so Claude's Skill tool can run them.\n // Opt-in via `bridgeOpencodeSkills: true`; the bundled skill is\n // staged either way.\n const skillPluginDirs = await resolveSkillPluginDirs({\n cwd,\n cliPath,\n enabled: self.config.bridgeOpencodeSkills === true,\n ...self.skillBridgeSpawn(failover),\n })\n cliArgs = buildCliArgs({\n sessionKey: sk,\n skipPermissions,\n model: spawnModelId,\n permissionMode: self.config.permissionMode,\n mcpConfig: mcp.paths,\n strictMcpConfig: self.config.strictMcpConfig,\n disallowedTools: allDisallowed.length > 0 ? allDisallowed : undefined,\n appendSystemPromptFile: systemPromptFile,\n pluginDirs: skillPluginDirs,\n ...self.thinkingCliOptions(),\n fastMode,\n cliVersion,\n })\n spawnSystemPromptFile = systemPromptFile\n spawnProxyServer = proxyServer\n spawnMcpHash = mcp.bridgedHash\n }\n\n if (activeProcess && !compactionMode) {\n proc = activeProcess.proc\n lineEmitter = activeProcess.lineEmitter\n log.debug(\"reusing active process\", { sk })\n } else {\n const ap = spawnClaudeProcess(\n cliPath,\n cliArgs,\n cwd,\n sk,\n spawnProxyServer,\n spawnMcpHash,\n spawnSystemPromptFile,\n self.config.ignoreAnthropicApiKey,\n reasoningEffort,\n )\n proc = ap.proc\n lineEmitter = ap.lineEmitter\n activeProcess = ap\n }\n }\n\n // The CLI serves one turn at a time. If the previous one is still\n // running (the user aborted it, or it ended on our inactivity\n // fallback rather than a real `result`), stop it before this turn\n // attaches any listeners; otherwise its tail streams into us and its\n // `result` closes us before our own answer arrives. Skipped for\n // tool-result turns: there the CLI is deliberately parked inside a\n // proxy MCP call waiting for the result we are about to deliver.\n if (activeProcess && !hasMatchedPendingResults && isTurnInFlight(activeProcess)) {\n log.warn(\"previous turn still in flight; interrupting it\", { sk })\n const idle = await interruptTurn(activeProcess)\n if (!idle) {\n log.warn(\"previous turn did not stop in time; this turn may see stale output\", { sk })\n }\n }\n\n controller.enqueue({ type: \"stream-start\", warnings })\n\n let currentTextId: string | null = null\n const textBlockIndices = new Set<number>()\n\n const startTextBlock = (): string => {\n if (currentTextId) {\n controller.enqueue({ type: \"text-end\", id: currentTextId })\n }\n const id = generateId()\n currentTextId = id\n controller.enqueue({ type: \"text-start\", id } as any)\n return id\n }\n\n const endTextBlock = (): void => {\n if (currentTextId) {\n controller.enqueue({ type: \"text-end\", id: currentTextId })\n currentTextId = null\n }\n }\n\n // Its own text part, led by FAILOVER_MARKER, so a later transcript\n // rebuild strips it exactly: it was never Claude's output.\n if (failoverNote) {\n controller.enqueue({\n type: \"text-delta\",\n id: startTextBlock(),\n delta: failoverNote,\n })\n endTextBlock()\n }\n\n const reasoningIds = new Map<number, string>()\n const reasoningStarted = new Map<number, boolean>()\n let hadThinkingTextFromStream = false\n\n let turnCompleted = false\n let controllerClosed = false\n // Buffered terminal results belong to the previous CLI turn.\n let unattendedTurnEnded = false\n let watchdogMessage = userMsg\n let pendingProxyUnsubscribe: (() => void) | null = null\n let asideSinkUnregister: (() => void) | null = null\n let resultFallbackTimer: ReturnType<typeof setTimeout> | null = null\n let pendingResultCompletion: (() => void) | null = null\n let hasReceivedContent = false\n let hasReceivedProgress = false\n let visibleTextSinceContinue = \"\"\n let lastVisibleTextSinceContinue = \"\"\n let hadReasoningSinceContinue = false\n let hadToolActivitySinceContinue = false\n let hadProxyActivitySinceContinue = false\n // v0.4.16: protocol-level stop signal captured from Claude CLI's\n // stream. Set by either the `message_delta` partial event or the\n // top-level `assistant` message, whichever arrives first.\n let lastStopReason: string | null = null\n const autoContinueState: AutoContinueState = {\n enabled: autoContinueEnabledFor(\n compactionMode,\n self.config.autoContinueIncompleteTurns,\n ),\n attempts: 0,\n startedAt: Date.now(),\n noProgressCount: 0,\n }\n\n const clearFallbackTimer = () => {\n if (resultFallbackTimer) {\n clearTimeout(resultFallbackTimer)\n resultFallbackTimer = null\n }\n }\n\n // Wire-inactivity watchdog. Resets on every line received from the\n // CLI; only fires if the CLI has emitted content and then gone\n // silent on stdout for `delayMs` without sending a `result`. The\n // previous design armed this on every text content_block_stop,\n // which killed legitimate mid-turn think pauses (most visibly\n // with sonnet between text-end and the next tool_use_start).\n // Tunable for reproduces and for the regression test, the same seam\n // CLAUDE_CODE_START_WATCHDOG_MS gives the start watchdog below.\n const RESULT_FALLBACK_MS = (() => {\n const env = process.env.CLAUDE_CODE_RESULT_FALLBACK_MS\n const parsed = env ? Number.parseInt(env, 10) : NaN\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 60_000\n })()\n const startResultFallback = (delayMs = RESULT_FALLBACK_MS) => {\n clearFallbackTimer()\n if ((!hasReceivedContent && !hasReceivedProgress) || controllerClosed) return\n resultFallbackTimer = setTimeout(() => {\n if (controllerClosed) return\n log.warn(\"result fallback timer fired — closing stream without result event\", {\n delayMs,\n })\n // Closing on a log line alone left the operator with a reply that\n // just stopped. An abort is exempt: they asked for it, and the\n // short grace period there is not a silent CLI.\n if (!autoContinueState.aborted) {\n controller.enqueue({\n type: \"text-delta\",\n id: startTextBlock(),\n delta: formatStreamTimeoutNote(delayMs),\n })\n endTextBlock()\n }\n closeHandler()\n }, delayMs)\n }\n\n // Start watchdog: complementary to the inactivity watchdog above.\n // That one only arms once content has arrived; this one covers the\n // gap the other explicitly skips — a reused process that produces\n // NO stdout at all after a fresh-turn envelope write. Seen after a\n // very long proxy-blocked tool call resumed successfully (the child\n // stays silent on stdout). On first fire we respawn the child with\n // --session-id to resume the conversation transparently; on a\n // second fire (respawn also silent) we end the turn cleanly so the\n // next opencode turn spawns fresh. Tunable via env for reproduces.\n const START_WATCHDOG_MS = (() => {\n const env = process.env.CLAUDE_CODE_START_WATCHDOG_MS\n const parsed = env ? Number.parseInt(env, 10) : NaN\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 90_000\n })()\n let startWatchdog: ReturnType<typeof setTimeout> | null = null\n let respawnAttempted = false\n const clearStartWatchdog = () => {\n if (startWatchdog) {\n clearTimeout(startWatchdog)\n startWatchdog = null\n }\n }\n const onStartWatchdogFire = () => {\n startWatchdog = null\n if (controllerClosed || hasReceivedContent || hasReceivedProgress) return\n if (respawnAttempted) {\n log.error(\n \"claude process still silent after respawn; ending turn\",\n { sessionKey: sk },\n )\n deleteActiveProcess(sk)\n deleteClaudeSessionId(sk)\n controllerClosed = true\n cleanupTurn()\n controller.enqueue({\n type: \"error\",\n error: new Error(\n \"Claude process produced no output after the envelope write (start watchdog timeout).\",\n ),\n })\n try {\n controller.close()\n } catch {}\n return\n }\n respawnAttempted = true\n log.warn(\n \"no stdout after envelope write; respawning claude process to resume conversation\",\n { sessionKey: sk, startWatchdogMs: START_WATCHDOG_MS },\n )\n lineEmitter.off(\"line\", lineHandler)\n lineEmitter.off(\"close\", closeHandler)\n proc.off(\"error\", procErrorHandler)\n const newAp = respawnActiveProcess(\n sk,\n cliPath,\n cliArgs,\n cwd,\n self.config.ignoreAnthropicApiKey,\n )\n if (!newAp) {\n log.error(\n \"no active process to respawn (start watchdog); ending turn\",\n { sessionKey: sk },\n )\n controllerClosed = true\n cleanupTurn()\n controller.enqueue({\n type: \"error\",\n error: new Error(\n \"No active claude process to respawn after start watchdog timeout.\",\n ),\n })\n try {\n controller.close()\n } catch {}\n return\n }\n proc = newAp.proc\n lineEmitter = newAp.lineEmitter\n activeProcess = newAp\n lineEmitter.on(\"line\", lineHandler)\n lineEmitter.on(\"close\", closeHandler)\n proc.on(\"error\", procErrorHandler)\n try {\n if (!deliverPendingCompletions(true)) {\n noteTurnStarted(newAp)\n proc.stdin?.write(watchdogMessage + \"\\n\")\n }\n log.debug(\"re-sent user message after respawn\", {\n textLength: watchdogMessage.length,\n })\n } catch (err) {\n log.error(\"failed to re-send envelope after respawn\", {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n armStartWatchdog()\n }\n const armStartWatchdog = () => {\n clearStartWatchdog()\n if (controllerClosed) return\n startWatchdog = setTimeout(onStartWatchdogFire, START_WATCHDOG_MS)\n }\n\n // Both buffered/live terminal boundaries and respawn consume through\n // this path. Open-channel results remain available for a later close.\n const deliverPendingCompletions = (force = false): boolean => {\n const pending = activeProcess?.pendingProxyCompletions\n const entries = [...(pending?.values() ?? [])].filter(\n (entry) => force || entry.recoveryRequired || isPendingProxyCallChannelClosed(entry.call),\n )\n if (entries.length === 0) return false\n endTextBlock()\n watchdogMessage = makeLateProxyResultMessage(entries)\n // This write asks the CLI for work like any fresh envelope, so\n // abort, LRU eviction and the idle timer must see it as busy.\n if (activeProcess) noteTurnStarted(activeProcess)\n proc.stdin!.write(watchdogMessage + \"\\n\")\n for (const { call } of entries) pending!.delete(call.toolCallId)\n log.warn(\"delivering proxy results after interrupted continuation\", {\n sessionKey: sk,\n toolCallIds: entries.map(({ call }) => call.toolCallId),\n respawn: force,\n })\n gotPartialEvents = false\n hasReceivedContent = false\n hasReceivedProgress = false\n turnCompleted = false\n resetAutoContinueWindow()\n clearFallbackTimer()\n armStartWatchdog()\n return true\n }\n\n const toolCallMap = new Map<\n number,\n { id: string; name: string; inputJson: string; started: boolean }\n >()\n // Tool calls the plugin reported as providerExecuted:false — opencode\n // will run these itself and emit its own tool-result, so we must NOT\n // forward Claude CLI's tool_result for them (would short-circuit\n // opencode's execute).\n const skipResultForIds = new Set<string>()\n const toolCallsById = new Map<\n string,\n { id: string; name: string; input: unknown }\n >()\n\n let resultMeta: {\n sessionId?: string\n costUsd?: number\n durationMs?: number\n durationApiMs?: number\n numTurns?: number\n usage?: ClaudeStreamMessage[\"usage\"]\n modelUsage?: ClaudeStreamMessage[\"modelUsage\"]\n permissionDenials?: ClaudeStreamMessage[\"permission_denials\"]\n } = {}\n\n // Subtype of a failing `result`, so the finish below reports the\n // turn as an error instead of a clean stop.\n let resultFailure: string | undefined\n\n // Set only by a REJECTED rate-limit event or by one of the two\n // known account-limit error texts, never by a generic failure: a\n // transient error must not open a form that moves the billing.\n let accountLimitHit: { resetsAt?: number; window?: string } | null = null\n // The account itself cannot serve (an expired login, a billing\n // problem), from the `error` kind on the CLI's own failure reply.\n let accountBlock: AccountBlockKind | null = null\n\n // Batched drain so claude CLI's parallel tool_use blocks (e.g. two\n // bash calls in one assistant message) end up in a single\n // tool-calls finish event. Without this, the broker would reject\n // every overlapping call and claude would see spurious tool errors.\n const drainBuffer: PendingProxyCall[] = []\n let drainTimer: ReturnType<typeof setTimeout> | null = null\n const DRAIN_QUIET_MS = 100\n\n const finishWithToolCalls = (calls: PendingProxyCall[]) => {\n if (controllerClosed) return\n if (calls.length === 0) return\n const enqueueToolCall = (\n toolCallId: string,\n toolName: string,\n input: Record<string, unknown>,\n ) => {\n controller.enqueue({\n type: \"tool-input-start\",\n id: toolCallId,\n toolName,\n } as any)\n controller.enqueue({\n type: \"tool-call\",\n toolCallId,\n toolName,\n input: JSON.stringify(input),\n providerExecuted: false,\n } as any)\n skipResultForIds.add(toolCallId)\n }\n for (const call of calls) {\n if (call.toolName === TASK_BATCH_TOOL_NAME) {\n // One MCP call from the CLI becomes N opencode `task` calls in\n // this single tool boundary, which is what makes them run at the\n // same time: the CLI serialises MCP calls, opencode runs the\n // tool calls of one step concurrently. Their results are\n // gathered back onto the parent id in\n // extractPendingProxyResultForCall.\n for (const [index, task] of taskBatchTasks(call.input).entries()) {\n enqueueToolCall(\n taskBatchChildToolCallId(call.toolCallId, index),\n \"task\",\n task,\n )\n }\n skipResultForIds.add(call.toolCallId)\n } else {\n enqueueToolCall(call.toolCallId, call.toolName, call.input)\n }\n markPendingProxyCallEmitted(call.toolCallId)\n }\n controller.enqueue({\n type: \"finish\",\n finishReason: toFinishReason(\"tool-calls\"),\n usage: toUsage(resultMeta.usage),\n providerMetadata: {\n \"claude-code\": resultMeta,\n },\n })\n controllerClosed = true\n cleanupTurn()\n try {\n controller.close()\n } catch {}\n }\n\n /**\n * End the turn on a synthetic call to opencode's native `question`\n * tool. opencode runs the tool, and the operator's answer arrives on\n * the NEXT doStream as a `tool-result` carrying this same id, which\n * is what keeps the whole exchange inside one opencode turn. Shared\n * by the plan-mode approval bridge and the account-failover form.\n */\n const finishWithQuestionCall = (call: QuestionToolCall) => {\n if (controllerClosed) return\n endTextBlock()\n controller.enqueue({\n type: \"tool-input-start\",\n id: call.toolCallId,\n toolName: call.toolName,\n providerExecuted: false,\n } as any)\n controller.enqueue({\n type: \"tool-call\",\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n input: JSON.stringify(call.input),\n providerExecuted: false,\n } as any)\n controller.enqueue({\n type: \"finish\",\n finishReason: toFinishReason(\"tool-calls\"),\n usage: toUsage(resultMeta.usage),\n providerMetadata: {\n \"claude-code\": resultMeta,\n },\n })\n controllerClosed = true\n cleanupTurn()\n try {\n controller.close()\n } catch {}\n }\n\n const drainNow = () => {\n if (drainTimer) {\n clearTimeout(drainTimer)\n drainTimer = null\n }\n if (drainBuffer.length === 0) return\n if (controllerClosed) return\n const batch = drainBuffer.splice(0, drainBuffer.length)\n log.info(\"draining pending proxy calls into stream finish\", {\n sessionKey: sk,\n count: batch.length,\n toolCallIds: batch.map((c) => c.toolCallId),\n })\n finishWithToolCalls(batch)\n }\n\n const settleResultBoundary = () => {\n drainTimer = null\n const completeResult = pendingResultCompletion\n pendingResultCompletion = null\n if (!completeResult || controllerClosed) return\n if (drainBuffer.length > 0) {\n drainNow()\n return\n }\n completeResult()\n }\n\n const scheduleResultBoundary = (\n completeResult: () => void,\n delayMs: number,\n ) => {\n pendingResultCompletion = completeResult\n if (drainTimer) clearTimeout(drainTimer)\n drainTimer = setTimeout(settleResultBoundary, delayMs)\n }\n\n const noteResultBoundaryCall = (): boolean => {\n if (!pendingResultCompletion) return false\n if (drainTimer) clearTimeout(drainTimer)\n drainTimer = setTimeout(settleResultBoundary, DRAIN_QUIET_MS)\n return true\n }\n\n const noteVisibleText = (text: string) => {\n visibleTextSinceContinue += text\n lastVisibleTextSinceContinue += text\n }\n\n const resetLastVisibleTextBlock = () => {\n lastVisibleTextSinceContinue = \"\"\n }\n\n const noteReasoning = () => {\n hadReasoningSinceContinue = true\n }\n\n const noteToolActivity = () => {\n hadToolActivitySinceContinue = true\n }\n\n const noteProxyActivity = () => {\n hadProxyActivitySinceContinue = true\n }\n\n const resetAutoContinueWindow = () => {\n visibleTextSinceContinue = \"\"\n lastVisibleTextSinceContinue = \"\"\n hadReasoningSinceContinue = false\n hadToolActivitySinceContinue = false\n hadProxyActivitySinceContinue = false\n lastStopReason = null\n }\n\n const completeResult = (msg: ClaudeStreamMessage) => {\n if (controllerClosed) return\n // The socket may have closed after the tool-result prompt was matched,\n // or while the result-boundary grace timer was running.\n if (deliverPendingCompletions()) {\n if (drainBuffer.length > 0) drainNow()\n return\n }\n if (drainBuffer.length > 0) {\n drainNow()\n return\n }\n\n const pendingSiblings = getPendingProxyCalls(sk)\n if (pendingSiblings.length > 0) {\n log.info(\"leaving parallel proxy calls pending at result boundary\", {\n sessionKey: sk,\n count: pendingSiblings.length,\n })\n }\n\n activeProcess?.pendingProxyCompletions?.clear()\n\n // This account is out of usage. Rather than finish as an error the\n // operator can only act on by editing config, end the turn on a\n // form listing the other configured accounts. Leaving it unanswered\n // waits and costs nothing; every answer that is not one of those\n // accounts comes back as a `stop` and ends the turn as before.\n // Only a turn that failed: a limit event on a turn that was served\n // is information, and replacing its answer with this form would\n // throw the answer away.\n if ((accountLimitHit || accountBlock) && failoverAskActive && msg.is_error === true) {\n const call = createAccountFailoverQuestionCall(sk, {\n sourceAccount,\n candidates: failoverAccounts,\n resetsAt: accountLimitHit?.resetsAt,\n window: accountLimitHit?.window,\n reason: accountLimitHit || !accountBlock ? undefined : describeAccountBlock(accountBlock),\n })\n log.warn(\n `Claude account \"${sourceAccount}\" ${\n accountLimitHit || !accountBlock ? \"is out of usage\" : `cannot serve (${accountBlock})`\n }; asking which account to continue on.`,\n {\n sessionKey: sk,\n candidates: failoverAccounts,\n toolCallId: call.toolCallId,\n resetsAt: accountLimitHit?.resetsAt ?? null,\n },\n )\n finishWithQuestionCall(call)\n return\n }\n\n const autoDecision = shouldAutoContinueIncompleteTurn(\n autoContinueState,\n {\n text: visibleTextSinceContinue,\n lastVisibleText: lastVisibleTextSinceContinue,\n hadReasoning: hadReasoningSinceContinue,\n hadToolActivity: hadToolActivitySinceContinue,\n hadProxyActivity: hadProxyActivitySinceContinue,\n isError: msg.is_error,\n stopReason: lastStopReason,\n },\n )\n if (autoDecision.continue) {\n const signature = continuationSignature({\n text: visibleTextSinceContinue,\n lastVisibleText: lastVisibleTextSinceContinue,\n hadReasoning: hadReasoningSinceContinue,\n hadToolActivity: hadToolActivitySinceContinue,\n hadProxyActivity: hadProxyActivitySinceContinue,\n isError: msg.is_error,\n })\n autoContinueState.noProgressCount =\n signature === autoContinueState.lastSignature\n ? autoContinueState.noProgressCount + 1\n : 0\n autoContinueState.lastSignature = signature\n autoContinueState.attempts++\n log.notice(\"auto-continuing incomplete claude result\", {\n sessionKey: sk,\n reason: autoDecision.reason,\n attempts: autoContinueState.attempts,\n textLength: visibleTextSinceContinue.length,\n lastTextLength: lastVisibleTextSinceContinue.length,\n hadReasoning: hadReasoningSinceContinue,\n hadToolActivity: hadToolActivitySinceContinue,\n hadProxyActivity: hadProxyActivitySinceContinue,\n })\n turnCompleted = false\n resetAutoContinueWindow()\n // The `result` just consumed marked the CLI idle; this puts it back to work.\n if (activeProcess) noteTurnStarted(activeProcess)\n proc.stdin?.write(makeAutoContinueMessage() + \"\\n\")\n return\n }\n log.notice(\"auto-continuation stopped\", {\n sessionKey: sk,\n reason: autoDecision.reason,\n stopReason: lastStopReason,\n attempts: autoContinueState.attempts,\n textLength: visibleTextSinceContinue.length,\n lastTextLength: lastVisibleTextSinceContinue.length,\n hadReasoning: hadReasoningSinceContinue,\n hadToolActivity: hadToolActivitySinceContinue,\n hadProxyActivity: hadProxyActivitySinceContinue,\n })\n\n for (const [idx, reasoningId] of reasoningIds) {\n if (reasoningStarted.get(idx)) {\n controller.enqueue({\n type: \"reasoning-end\",\n id: reasoningId,\n } as any)\n }\n }\n\n controller.enqueue({\n type: \"finish\",\n finishReason: resultFailure\n ? { unified: \"error\" as const, raw: resultFailure }\n : toFinishReason(\"stop\"),\n usage: toUsage(msg.usage),\n providerMetadata: {\n \"claude-code\": {\n ...resultMeta,\n ...(resultFailure ? { resultSubtype: resultFailure } : {}),\n ...(compactionMode\n ? { compactionModel: effectiveModelId }\n : {}),\n },\n ...(typeof msg.usage?.cache_creation_input_tokens === \"number\"\n ? {\n anthropic: {\n cacheCreationInputTokens:\n msg.usage.cache_creation_input_tokens,\n },\n }\n : {}),\n },\n })\n\n controllerClosed = true\n cleanupTurn()\n if (!useInteractive && !compactionMode) {\n scheduleIdleProcessEviction(\n sk,\n resolveIdleProcessTimeoutMs(self.config.idleProcessTimeoutMs),\n )\n }\n\n try {\n controller.close()\n } catch {}\n }\n\n // Set true once we observe a `stream_event` envelope. When on, the\n // top-level `assistant` message is a duplicate of what we already\n // streamed via content_block_* deltas — skip its content.\n let gotPartialEvents = false\n\n const lineHandler = (line: string) => {\n if (!line.trim()) return\n if (controllerClosed) return\n\n // Any line from the CLI counts as activity — reset the inactivity\n // watchdog so mid-turn pauses between blocks don't get killed.\n startResultFallback()\n\n try {\n const outer: ClaudeStreamMessage = JSON.parse(line)\n\n // Unwrap stream_event envelope (--include-partial-messages).\n // Inner event uses the same content_block_* / message_* shape.\n const msg: ClaudeStreamMessage =\n outer.type === \"stream_event\" && outer.event\n ? { ...outer.event, session_id: outer.session_id }\n : outer\n\n const modelProgress =\n (msg.type === \"assistant\" && !!msg.message?.content?.length) ||\n (msg.type === \"content_block_start\" && msg.content_block?.type === \"tool_use\") ||\n (msg.type === \"content_block_delta\" &&\n ((msg.delta?.type === \"text_delta\" && !!msg.delta.text) ||\n (msg.delta?.type === \"thinking_delta\" && !!msg.delta.thinking)))\n if (modelProgress) {\n hasReceivedProgress = true\n clearStartWatchdog()\n startResultFallback()\n }\n\n if (outer.type === \"stream_event\") {\n gotPartialEvents = true\n }\n\n if (handleControlRequest(msg, proc)) {\n return\n }\n\n log.debug(\"stream message\", {\n type: msg.type,\n subtype: msg.subtype,\n })\n\n // Handle system init\n if (msg.type === \"system\" && msg.subtype === \"init\") {\n if (msg.session_id) {\n setClaudeSessionId(sk, msg.session_id)\n log.info(\"session initialized\", {\n claudeSessionId: msg.session_id,\n })\n }\n reportFastModeState(msg, fastMode)\n reportSystemInit(msg, {\n ignoreAnthropicApiKey: self.config.ignoreAnthropicApiKey,\n })\n }\n\n // The CLI compacted its own context. Nothing else tells the user\n // that everything before this point is now a summary.\n if (msg.type === \"system\" && msg.subtype === \"compact_boundary\") {\n const note = reportCompactBoundary(msg)\n if (note) {\n controller.enqueue({ type: \"text-delta\", id: startTextBlock(), delta: note })\n endTextBlock()\n }\n }\n\n // Claude Code started a new conversation (`/clear`, plan-mode\n // exit). Content-block indices restart with it, so nothing keyed\n // by index may survive: a stale `toolCallMap` entry re-emits a\n // finished tool call on the new conversation's first block, the\n // failure fixed on 2026-09-06. The Claude session id needs nothing\n // here; the `system/init` that follows carries the new one.\n if (msg.type === \"conversation_reset\") {\n const note = reportConversationReset(msg)\n if (note) {\n toolCallMap.clear()\n reasoningIds.clear()\n reasoningStarted.clear()\n textBlockIndices.clear()\n controller.enqueue({ type: \"text-delta\", id: startTextBlock(), delta: note })\n endTextBlock()\n }\n return\n }\n\n // Not returned from: the reply's own text still renders below.\n const block = accountBlockKind(msg)\n if (block) accountBlock = block\n\n // A rejection is why the turn is about to fail. Put it in the\n // transcript so the reason does not live only in a log file that\n // is off by default.\n if (msg.type === \"rate_limit_event\") {\n // Parsed separately from the reporter, which dedupes per\n // process and returns null on a repeat: the second rejection in\n // a session is still a rejection this turn has to act on.\n const info = parseRateLimitEvent(msg)\n if (info && isRateLimitRejected(info)) {\n accountLimitHit = {\n resetsAt: info.resetsAt ?? info.overageResetsAt,\n window: info.rateLimitType,\n }\n }\n const note = reportRateLimitEvent(msg)\n if (note) {\n controller.enqueue({ type: \"text-delta\", id: startTextBlock(), delta: note })\n endTextBlock()\n }\n return\n }\n\n // content_block_start\n if (\n msg.type === \"content_block_start\" &&\n msg.content_block &&\n msg.index !== undefined\n ) {\n const block = msg.content_block\n const idx = msg.index\n\n if (block.type === \"thinking\") {\n noteReasoning()\n const reasoningId = generateId()\n reasoningIds.set(idx, reasoningId)\n }\n\n if (block.type === \"text\") {\n textBlockIndices.add(idx)\n // New text block — clear last-block buffer so final-answer\n // detection only considers this block's contents, not earlier\n // mid-task narration.\n resetLastVisibleTextBlock()\n if (block.text) {\n if (!currentTextId) startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: currentTextId!,\n delta: block.text,\n })\n noteVisibleText(block.text)\n hasReceivedContent = true\n }\n }\n\n if (block.type === \"tool_use\" && block.id && block.name) {\n noteToolActivity()\n const entry = {\n id: block.id,\n name: block.name,\n inputJson: \"\",\n started: false,\n }\n toolCallMap.set(idx, entry)\n\n if (\n block.name !== \"AskUserQuestion\" &&\n block.name !== \"ask_user_question\" &&\n block.name !== \"ExitPlanMode\" &&\n !block.name.startsWith(PROXY_TOOL_PREFIX)\n ) {\n const { name: mappedName, skip, executed } = mapTool(\n block.name,\n undefined,\n {\n webSearch: self.config.webSearch,\n sessionId: getClaudeSessionId(sk),\n toolUseId: block.id,\n },\n )\n if (!skip) {\n entry.started = true\n controller.enqueue({\n type: \"tool-input-start\",\n id: block.id,\n toolName: mappedName,\n providerExecuted: executed,\n } as any)\n log.info(\"tool started\", {\n name: block.name,\n mappedName,\n id: block.id,\n })\n }\n }\n }\n }\n\n // content_block_delta\n if (\n msg.type === \"content_block_delta\" &&\n msg.delta &&\n msg.index !== undefined\n ) {\n const delta = msg.delta\n const idx = msg.index\n\n if (delta.type === \"thinking_delta\" && delta.thinking) {\n noteReasoning()\n hadThinkingTextFromStream = true\n const reasoningId = reasoningIds.get(idx)\n if (reasoningId) {\n if (!reasoningStarted.get(idx)) {\n controller.enqueue({\n type: \"reasoning-start\",\n id: reasoningId,\n } as any)\n reasoningStarted.set(idx, true)\n }\n controller.enqueue({\n type: \"reasoning-delta\",\n id: reasoningId,\n delta: delta.thinking,\n } as any)\n }\n }\n\n if (delta.type === \"text_delta\" && delta.text) {\n if (!currentTextId) startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: currentTextId!,\n delta: delta.text,\n })\n noteVisibleText(delta.text)\n hasReceivedContent = true\n }\n\n if (delta.type === \"input_json_delta\" && delta.partial_json) {\n const tc = toolCallMap.get(idx)\n if (tc) {\n tc.inputJson += delta.partial_json\n // Only forward deltas for tool calls whose tool-input-start\n // was actually emitted. Skipped tools (CLAUDE_INTERNAL_TOOLS,\n // TaskCreate/TaskUpdate, CLI-internal WebSearch, AskUserQuestion,\n // ExitPlanMode, proxy tools) never get a named start part, so\n // forwarding their deltas makes opencode's AI SDK bridge fall\n // back to a nameless pending part rendered as `⚙ unknown`.\n if (tc.started) {\n controller.enqueue({\n type: \"tool-input-delta\",\n id: tc.id,\n delta: delta.partial_json,\n } as any)\n }\n }\n }\n\n if (!KNOWN_DELTA_TYPES.has(delta.type)) {\n log.debug(\"unrecognized content_block_delta type\", {\n type: delta.type,\n idx,\n keys: Object.keys(delta),\n })\n }\n }\n\n // content_block_stop\n if (\n msg.type === \"content_block_stop\" &&\n msg.index !== undefined\n ) {\n const idx = msg.index\n\n const reasoningId = reasoningIds.get(idx)\n if (reasoningId && reasoningStarted.get(idx)) {\n controller.enqueue({\n type: \"reasoning-end\",\n id: reasoningId,\n } as any)\n reasoningStarted.delete(idx)\n }\n\n if (textBlockIndices.has(idx)) {\n endTextBlock()\n textBlockIndices.delete(idx)\n }\n\n const tc = toolCallMap.get(idx)\n if (tc) {\n // Block indices restart at 0 on every assistant message, and a\n // turn can hold several (tool_use -> tool_result -> answer).\n // Without this delete the entry outlives its message, so the\n // next message's block at the same index re-emits a tool-call\n // for an id opencode already completed. That second part never\n // gets a result, opencode aborts it at stream end, and a\n // subagent's `task` call reports \"Tool execution aborted\"\n // even though the child answered correctly.\n toolCallMap.delete(idx)\n let parsedInput: any = {}\n try {\n parsedInput = JSON.parse(tc.inputJson || \"{}\")\n } catch {}\n\n if (isAskUserQuestionTool(tc.name)) {\n // Latch: the model handed control to the operator. Block any\n // auto-continue nudge for the rest of the turn so it can't\n // proceed on its own before the operator replies.\n autoContinueState.sawAskUserQuestion = true\n const askId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: askId,\n delta: formatAskUserQuestion(parsedInput),\n })\n endTextBlock()\n } else if (tc.name === \"ExitPlanMode\") {\n const plan = (parsedInput?.plan as string) || \"\"\n\n if (planModeQuestionActive) {\n // Approval bridge: render the plan, then hand the\n // yes/no back to opencode's own `question` tool and end\n // the turn on \"tool-calls\" so the outer loop runs it.\n const questionCall = createExitPlanModeQuestionCall(\n sk,\n tc.id,\n plan,\n )\n const planId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: planId,\n delta: questionCall.text,\n })\n finishWithQuestionCall(questionCall)\n return\n }\n\n const planId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: planId,\n delta: `\\n\\n${plan}\\n\\n---\\n**Do you want to proceed with this plan?** (yes/no)\\n`,\n })\n endTextBlock()\n } else if (\n isWebSearchTool(tc.name) &&\n isWebSearchHandledByCli(self.config.webSearch)\n ) {\n // Claude CLI runs WebSearch internally. Forwarding the\n // \"WebSearch\" tool-call part would render an invalid tool\n // row in opencode (no registry entry), so show the query\n // as a text line instead. The result stays CLI-internal.\n const query =\n typeof parsedInput?.query === \"string\"\n ? parsedInput.query\n : JSON.stringify(parsedInput)\n const searchId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: searchId,\n delta: `\\n> **Web search:** ${query}\\n`,\n })\n endTextBlock()\n } else if (tc.name.startsWith(PROXY_TOOL_PREFIX)) {\n noteProxyActivity()\n log.debug(\"ignoring proxy tool_use block; broker handles it\", {\n name: tc.name,\n id: tc.id,\n })\n } else {\n const {\n name: mappedName,\n input: mappedInput,\n executed,\n skip,\n } = mapTool(tc.name, parsedInput, {\n webSearch: self.config.webSearch,\n sessionId: getClaudeSessionId(sk),\n toolUseId: tc.id,\n })\n\n if (!skip) {\n toolCallsById.set(tc.id, {\n id: tc.id,\n name: tc.name,\n input: parsedInput,\n })\n if (!executed) skipResultForIds.add(tc.id)\n\n controller.enqueue({\n type: \"tool-call\",\n toolCallId: tc.id,\n toolName: mappedName,\n input: JSON.stringify(mappedInput),\n providerExecuted: executed,\n } as any)\n }\n log.info(\"tool call complete\", {\n name: tc.name,\n mappedName,\n id: tc.id,\n executed,\n })\n }\n }\n }\n\n // Capture protocol-level stop_reason from the streaming\n // `message_delta` event (sent right before the final\n // `message_stop`). Any non-empty value is the source-of-truth\n // for why the turn ended — used to bypass the keyword heuristic.\n if (\n gotPartialEvents &&\n msg.type === \"message_delta\" &&\n typeof (msg as any).delta?.stop_reason === \"string\"\n ) {\n lastStopReason = (msg as any).delta.stop_reason\n }\n\n // assistant message (complete, not streaming).\n // When --include-partial-messages is on, this is a duplicate of\n // what we already streamed via content_block_* events. Skip it\n // for content, but still capture stop_reason from it for the\n // non-partial path.\n if (\n msg.type === \"assistant\" &&\n msg.message &&\n typeof (msg.message as any).stop_reason === \"string\"\n ) {\n lastStopReason = (msg.message as any).stop_reason\n }\n // Fallback: extract thinking from the complete assistant\n // message. opus-4-7's CLI strips thinking_delta from stream\n // events but may include thinking in the final message.\n if (\n msg.type === \"assistant\" &&\n msg.message?.content &&\n gotPartialEvents\n ) {\n const thinkingBlocks = (msg.message.content as any[]).filter(\n (b) => b.type === \"thinking\",\n )\n if (thinkingBlocks.length > 0) {\n log.info(\"assistant message thinking blocks\", {\n count: thinkingBlocks.length,\n hasText: thinkingBlocks.some(\n (b) => typeof b.thinking === \"string\" && b.thinking.length > 0,\n ),\n hadStreamThinking: hadThinkingTextFromStream,\n })\n if (!hadThinkingTextFromStream) {\n for (const block of thinkingBlocks) {\n if (block.thinking && block.thinking.length > 0) {\n noteReasoning()\n hadThinkingTextFromStream = true\n const thinkingId = generateId()\n controller.enqueue({\n type: \"reasoning-start\",\n id: thinkingId,\n } as any)\n controller.enqueue({\n type: \"reasoning-delta\",\n id: thinkingId,\n delta: block.thinking,\n } as any)\n controller.enqueue({\n type: \"reasoning-end\",\n id: thinkingId,\n } as any)\n }\n }\n }\n }\n }\n if (\n msg.type === \"assistant\" &&\n msg.message?.content &&\n !gotPartialEvents\n ) {\n const hasText = msg.message.content.some(\n (b: any) => b.type === \"text\" && b.text,\n )\n const hasToolUse = msg.message.content.some(\n (b: any) => b.type === \"tool_use\",\n )\n\n if (hasText) {\n hasReceivedContent = true\n }\n\n if (hasText && !hasToolUse) {\n startResultFallback()\n }\n if (hasToolUse) {\n clearFallbackTimer()\n }\n\n for (const block of msg.message.content) {\n if (block.type === \"text\" && block.text) {\n // New text block — keep only this block's text in the\n // last-block buffer for final-answer detection.\n resetLastVisibleTextBlock()\n const blockId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: blockId,\n delta: block.text,\n })\n endTextBlock()\n noteVisibleText(block.text)\n hasReceivedContent = true\n }\n\n if (block.type === \"thinking\" && block.thinking) {\n noteReasoning()\n const thinkingId = generateId()\n controller.enqueue({\n type: \"reasoning-start\",\n id: thinkingId,\n } as any)\n controller.enqueue({\n type: \"reasoning-delta\",\n id: thinkingId,\n delta: block.thinking,\n } as any)\n controller.enqueue({\n type: \"reasoning-end\",\n id: thinkingId,\n } as any)\n }\n\n if (block.type === \"tool_use\" && block.id && block.name) {\n noteToolActivity()\n const parsedInput = (block.input ?? {}) as Record<\n string,\n unknown\n >\n\n if (isAskUserQuestionTool(block.name)) {\n const askId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: askId,\n delta: formatAskUserQuestion(parsedInput),\n })\n endTextBlock()\n } else if (block.name === \"ExitPlanMode\") {\n const plan = (parsedInput?.plan as string) || \"\"\n\n if (planModeQuestionActive) {\n const questionCall = createExitPlanModeQuestionCall(\n sk,\n block.id,\n plan,\n )\n const planId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: planId,\n delta: questionCall.text,\n })\n finishWithQuestionCall(questionCall)\n return\n }\n\n const planId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: planId,\n delta: `\\n\\n${plan}\\n\\n---\\n**Do you want to proceed with this plan?** (yes/no)\\n`,\n })\n endTextBlock()\n } else if (\n isWebSearchTool(block.name) &&\n isWebSearchHandledByCli(self.config.webSearch)\n ) {\n // CLI-internal WebSearch: render the query as text and\n // drop the call/result parts (no opencode registry entry\n // for \"WebSearch\" — would render as an invalid tool row).\n toolCallsById.delete(block.id)\n const query =\n typeof parsedInput?.query === \"string\"\n ? parsedInput.query\n : JSON.stringify(parsedInput)\n const searchId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: searchId,\n delta: `\\n> **Web search:** ${query}\\n`,\n })\n endTextBlock()\n } else if (block.name.startsWith(PROXY_TOOL_PREFIX)) {\n noteProxyActivity()\n log.debug(\"ignoring proxy tool_use from assistant message\", {\n name: block.name,\n id: block.id,\n })\n } else {\n const {\n name: mappedName,\n input: mappedInput,\n executed,\n skip,\n } = mapTool(block.name, parsedInput, {\n webSearch: self.config.webSearch,\n sessionId: getClaudeSessionId(sk),\n toolUseId: block.id,\n })\n\n if (!skip) {\n toolCallsById.set(block.id, {\n id: block.id,\n name: block.name,\n input: parsedInput,\n })\n if (!executed) skipResultForIds.add(block.id)\n controller.enqueue({\n type: \"tool-input-start\",\n id: block.id,\n toolName: mappedName,\n providerExecuted: executed,\n } as any)\n controller.enqueue({\n type: \"tool-call\",\n toolCallId: block.id,\n toolName: mappedName,\n input: JSON.stringify(mappedInput),\n providerExecuted: executed,\n } as any)\n }\n log.info(\"tool_use from assistant message\", {\n name: block.name,\n mappedName,\n id: block.id,\n executed,\n })\n }\n }\n\n if (block.type === \"tool_result\") {\n log.debug(\"tool_result\", {\n toolUseId: block.tool_use_id,\n })\n }\n }\n }\n\n // user message (tool results from Claude CLI)\n if (msg.type === \"user\" && msg.message?.content) {\n for (const block of msg.message.content) {\n if (block.type === \"tool_result\" && block.tool_use_id) {\n if (skipResultForIds.has(block.tool_use_id)) {\n log.debug(\"skipping tool-result (opencode runs it)\", {\n toolUseId: block.tool_use_id,\n })\n continue\n }\n\n let resultText = \"\"\n if (typeof block.content === \"string\") {\n resultText = block.content\n } else if (Array.isArray(block.content)) {\n resultText = block.content\n .filter(\n (\n c,\n ): c is { type: string; text: string } =>\n c.type === \"text\" &&\n typeof c.text === \"string\",\n )\n .map((c) => c.text)\n .join(\"\\n\")\n }\n\n // Ledger hook: commit pending TaskCreate to opencode's todo\n // panel via a synthetic todowrite emission. Pass-through —\n // returns null for non-TaskCreate ids, so cheap and silent.\n const claudeSessionId = getClaudeSessionId(sk)\n if (claudeSessionId) {\n const list = applyTaskCreateToolResult(\n claudeSessionId,\n block.tool_use_id,\n resultText,\n )\n if (list) {\n const synthId = `todowrite_${block.tool_use_id}`\n controller.enqueue({\n type: \"tool-input-start\",\n id: synthId,\n toolName: \"todowrite\",\n providerExecuted: false,\n } as any)\n controller.enqueue({\n type: \"tool-call\",\n toolCallId: synthId,\n toolName: \"todowrite\",\n input: JSON.stringify({\n todos: list.map((t) => ({\n id: t.id,\n content: t.content,\n status: t.status,\n priority: \"medium\",\n })),\n }),\n providerExecuted: false,\n } as any)\n noteToolActivity()\n }\n }\n\n const toolCall = toolCallsById.get(block.tool_use_id)\n if (toolCall) {\n // A CLI-executed tool that failed carries `is_error`. The\n // AI SDK turns a `tool-result` with `isError` into a\n // `tool-error` part, which is what makes opencode render\n // the row as failed; without the flag every failed CLI\n // tool was forwarded as a success whose output happened\n // to be an error message.\n const isError = block.is_error === true\n controller.enqueue({\n type: \"tool-result\",\n toolCallId: block.tool_use_id,\n toolName: toolCall.name,\n result: {\n output: resultText,\n title: toolCall.name,\n metadata: isError ? { error: true } : {},\n },\n ...(isError ? { isError: true } : {}),\n providerExecuted: true,\n } as any)\n noteToolActivity()\n log.info(\"tool result emitted\", {\n toolUseId: block.tool_use_id,\n name: toolCall.name,\n isError,\n })\n toolCallsById.delete(block.tool_use_id)\n }\n }\n }\n }\n\n // result - end of conversation turn\n if (msg.type === \"result\") {\n clearFallbackTimer()\n\n if (msg.session_id) {\n setClaudeSessionId(sk, msg.session_id)\n }\n\n if (deliverPendingCompletions()) {\n // Finish the abandoned turn before submitting its late result.\n // Otherwise this result could close the stream for the new turn.\n return\n }\n\n // Some CLI failures only include user-readable text in\n // `result.result` (no prior assistant text blocks). Emit it so\n // opencode users don't see a blank turn.\n if (\n !currentTextId &&\n msg.is_error &&\n typeof msg.result === \"string\" &&\n msg.result.trim().length > 0\n ) {\n const errId = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id: errId,\n delta: msg.result,\n })\n }\n\n // The other half of the limit signal: some rejections only ever\n // reach us as the error text of the terminal result.\n if (\n !accountLimitHit &&\n msg.is_error &&\n isAccountLimitError({\n resultText: typeof msg.result === \"string\" ? msg.result : null,\n })\n ) {\n accountLimitHit = {}\n }\n\n // Say which account and what to run. Without this the only\n // thing on screen was the CLI's \"Failed to authenticate: OAuth\n // session expired\", which names neither.\n if (accountBlock && msg.is_error) {\n // The CLI labels this result `success` with `is_error: true`,\n // so nothing else marks the turn failed; without this it\n // finished as an ordinary `stop` with the error as its answer.\n resultFailure ??= accountBlock\n const offeringSwitch = failoverAskActive\n controller.enqueue({\n type: \"text-delta\",\n id: startTextBlock(),\n delta: formatAccountBlockNote({\n kind: accountBlock,\n account: sourceAccount,\n configDir: self.config.configDir,\n offeringSwitch,\n }),\n })\n endTextBlock()\n log.warn(`Claude account \"${sourceAccount}\" cannot serve requests`, {\n sessionKey: sk,\n kind: accountBlock,\n offeringSwitch,\n })\n }\n\n // A non-`success` subtype is a failed turn. Name it in the\n // transcript and finish as an error, rather than letting it be\n // recorded as an ordinary reply with the subtype only in a\n // debug log line.\n const failure = describeResultFailure(msg)\n if (failure) {\n resultFailure = msg.subtype\n controller.enqueue({\n type: \"text-delta\",\n id: startTextBlock(),\n delta: formatResultFailureNote(failure),\n })\n log.warn(failure, { sessionKey: sk, subtype: msg.subtype })\n }\n\n const turnStats = extractTurnStats(msg)\n resultMeta = {\n sessionId: msg.session_id,\n costUsd: msg.total_cost_usd,\n durationMs: msg.duration_ms,\n durationApiMs: msg.duration_api_ms,\n numTurns: msg.num_turns,\n usage: msg.usage,\n modelUsage: msg.modelUsage,\n // Names and ids only: a denial's `tool_input` can be a whole\n // file write payload and has no business in metadata.\n permissionDenials: msg.permission_denials?.map((denial) => ({\n tool_name: denial.tool_name,\n tool_use_id: denial.tool_use_id,\n })),\n }\n\n // Logged whatever `turnStats` is set to: the footer is a\n // display preference, the numbers are diagnostics.\n log.info(\"conversation result\", {\n sessionId: msg.session_id,\n numTurns: msg.num_turns,\n isError: msg.is_error,\n subtype: msg.subtype,\n ...turnStatsLogPayload(turnStats),\n })\n\n // Never on a compaction turn (the footer would be appended to\n // what opencode stores as the summary) and never on a failed\n // one (the error is the thing to read, not the bill).\n if (self.config.turnStats && !compactionMode && !msg.is_error && !failure) {\n const footer = formatTurnStatsBlock(turnStats)\n if (footer) {\n controller.enqueue({\n type: \"text-delta\",\n id: startTextBlock(),\n delta: footer,\n })\n }\n }\n\n turnCompleted = true\n\n endTextBlock()\n\n const shouldDeferResult =\n !msg.is_error &&\n !autoContinueState.aborted &&\n !autoContinueState.sawAskUserQuestion\n\n if (drainBuffer.length > 0 && shouldDeferResult) {\n log.info(\n \"waiting for parallel proxy calls at turn-result boundary\",\n {\n sessionKey: sk,\n count: drainBuffer.length,\n },\n )\n scheduleResultBoundary(\n () => completeResult(msg),\n DRAIN_QUIET_MS,\n )\n return\n }\n\n if (\n drainBuffer.length === 0 &&\n hadProxyActivitySinceContinue &&\n shouldDeferResult\n ) {\n log.info(\n \"waiting for delayed proxy call at turn-result boundary\",\n {\n sessionKey: sk,\n graceMs: PROXY_RESULT_BOUNDARY_GRACE_MS,\n },\n )\n scheduleResultBoundary(\n () => completeResult(msg),\n PROXY_RESULT_BOUNDARY_GRACE_MS,\n )\n return\n }\n\n completeResult(msg)\n }\n } catch (e) {\n log.debug(\"failed to parse line\", {\n error:\n e instanceof Error ? e.message : String(e),\n })\n }\n }\n\n const closeHandler = () => {\n log.debug(\"readline closed\")\n if (controllerClosed) return\n // Claude CLI's stdio is gone. The proxy-mcp HTTP requests that\n // backed any pending tool calls have no one to answer them now —\n // reject so the handlers return errors rather than hang.\n if (drainBuffer.length > 0 || getPendingProxyCalls(sk).length > 0) {\n rejectAllPendingProxyCallsForSession(\n sk,\n new Error(\n \"Claude CLI subprocess closed before pending tool calls were resolved\",\n ),\n )\n drainBuffer.length = 0\n }\n // A close without a terminal `result` means the child died mid-turn.\n // Reporting that as `stop` with empty usage made a crashed CLI look\n // like a short but successful answer. An abort is not a crash: the\n // operator asked for it, and the CLI may exit before its interrupt\n // result lands.\n const crashed = !turnCompleted && !autoContinueState.aborted\n controllerClosed = true\n cleanupTurn()\n endTextBlock()\n\n const finishClose = (\n exitCode: number | null,\n signal: NodeJS.Signals | null,\n ) => {\n if (crashed) {\n log.warn(\"claude process closed without a result\", {\n sessionKey: sk,\n exitCode,\n signal,\n stderrBytes: activeProcess?.lastStderr?.length ?? 0,\n })\n controller.enqueue({\n type: \"error\",\n error: new Error(\n describeChildCrash(exitCode, signal, activeProcess?.lastStderr),\n ),\n })\n }\n controller.enqueue({\n type: \"finish\",\n finishReason: toFinishReason(crashed ? \"error\" : \"stop\"),\n usage: toUsage(),\n providerMetadata: {\n \"claude-code\": {\n ...resultMeta,\n ...(compactionMode\n ? { compactionModel: effectiveModelId }\n : {}),\n },\n },\n })\n try {\n controller.close()\n } catch {}\n }\n\n // stdout usually reaches EOF a tick before the child's `exit` event,\n // so the status that explains the crash is not known yet here. The\n // turn is over either way; wait briefly for it rather than report a\n // bare \"closed its output\". Bounded, and only on the crash path.\n if (crashed && proc.exitCode === null && proc.signalCode === null) {\n let reported = false\n const report = (\n exitCode: number | null,\n signal: NodeJS.Signals | null,\n ) => {\n if (reported) return\n reported = true\n clearTimeout(exitGrace)\n proc.off(\"exit\", onExit)\n finishClose(exitCode, signal)\n }\n const onExit = (code: number | null, signal: NodeJS.Signals | null) =>\n report(code, signal)\n const exitGrace = setTimeout(\n () => report(proc.exitCode, proc.signalCode),\n CHILD_EXIT_STATUS_GRACE_MS,\n )\n proc.once(\"exit\", onExit)\n return\n }\n finishClose(proc.exitCode, proc.signalCode)\n }\n\n // Centralised per-turn teardown. Every exit path funnels through here\n // so we don't accumulate listeners across turns on a reused process.\n let cleanedUp = false\n const cleanupTurn = () => {\n if (cleanedUp) return\n cleanedUp = true\n clearFallbackTimer()\n pendingResultCompletion = null\n clearStartWatchdog()\n if (drainTimer) {\n clearTimeout(drainTimer)\n drainTimer = null\n }\n lineEmitter.off(\"line\", lineHandler)\n lineEmitter.off(\"close\", closeHandler)\n pendingProxyUnsubscribe?.()\n pendingProxyUnsubscribe = null\n asideSinkUnregister?.()\n asideSinkUnregister = null\n proc.off(\"error\", procErrorHandler)\n }\n\n const procErrorHandler = (err: Error) => {\n log.error(\"process error\", { error: err.message })\n deleteActiveProcess(sk)\n deleteClaudeSessionId(sk)\n if (controllerClosed) return\n // Subprocess failure invalidates every pending HTTP-bound tool\n // call for this session. Reject them so proxy-mcp returns errors\n // to Claude rather than letting the sockets stall.\n if (drainBuffer.length > 0 || getPendingProxyCalls(sk).length > 0) {\n rejectAllPendingProxyCallsForSession(\n sk,\n new Error(\n `Claude CLI subprocess error: ${err.message}`,\n ),\n )\n drainBuffer.length = 0\n }\n controllerClosed = true\n cleanupTurn()\n controller.enqueue({ type: \"error\", error: err })\n try {\n controller.close()\n } catch {}\n }\n\n // Whatever the child said while no turn was listening comes first:\n // the operator gets to see it, and a turn that already ended on the\n // CLI's side is known before this one decides what to send.\n if (activeProcess) {\n const unattended = takeUnattendedLines(activeProcess)\n if (unattended.lines.length > 0 || unattended.dropped > 0) {\n log.notice(\"replaying stdout the child emitted between turns\", {\n sessionKey: sk,\n lines: unattended.lines.length,\n dropped: unattended.dropped,\n })\n // Render narration only. Replaying actionable events could execute\n // old tools or close this new stream on a stale approval/result.\n let partialText = false\n {\n if (unattended.dropped > 0) {\n const id = startTextBlock()\n controller.enqueue({\n type: \"text-delta\",\n id,\n delta: `> _${unattended.dropped} lines of output emitted between turns were dropped._\\n\\n`,\n })\n }\n for (const line of unattended.lines) {\n try {\n const outer: ClaudeStreamMessage = JSON.parse(line)\n const msg = outer.type === \"stream_event\" && outer.event ? outer.event : outer\n let text = \"\"\n if (msg.type === \"content_block_delta\" && msg.delta?.type === \"text_delta\") {\n text = msg.delta.text ?? \"\"\n partialText = true\n } else if (msg.type === \"assistant\") {\n if (!partialText) text = (msg.message?.content ?? []).filter((part) => part.type === \"text\").map((part) => part.text ?? \"\").join(\"\")\n partialText = false\n } else if (msg.type === \"result\") {\n unattendedTurnEnded = true\n for (const entry of activeProcess.pendingProxyCompletions?.values() ?? []) {\n if (isPendingProxyCallChannelClosed(entry.call)) entry.recoveryRequired = true\n }\n if (outer.session_id) setClaudeSessionId(sk, outer.session_id)\n if (msg.is_error && msg.result) text = msg.result\n }\n if (text) controller.enqueue({ type: \"text-delta\", id: currentTextId ?? startTextBlock(), delta: text })\n } catch { /* Ignore incomplete or malformed buffered lines. */ }\n }\n }\n endTextBlock()\n // Replayed lines are history, not liveness: the watchdogs below\n // must judge the child on what it does from here on.\n clearFallbackTimer()\n hasReceivedContent = false\n }\n }\n\n if (activeProcess && !compactionMode) {\n activeProcess.opencodeSessionID = affinity\n activeProcess.asideTransport = asideTransportRef\n }\n if (!compactionMode) {\n // Lets a `/btw` answered while this turn runs land in the turn's own\n // reply instead of a toast (btw-command.ts). Its own text block, so\n // the marker stays at the start of a part and the block can be\n // stripped exactly when a transcript is rebuilt.\n asideSinkUnregister = registerAsideSink(affinity, (text) => {\n if (controllerClosed) return false\n const asideId = startTextBlock()\n controller.enqueue({ type: \"text-delta\", id: asideId, delta: text })\n endTextBlock()\n return true\n })\n }\n lineEmitter.on(\"line\", lineHandler)\n lineEmitter.on(\"close\", closeHandler)\n\n pendingProxyUnsubscribe = onPendingProxyCall(sk, (call) => {\n if (controllerClosed) {\n // Stream already closed (we already drained). Late arrival —\n // reject immediately so the proxy-mcp HTTP request returns\n // instead of hanging until its 10-min timeout.\n log.warn(\n \"pending proxy call arrived after stream close; rejecting\",\n {\n sessionKey: sk,\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n },\n )\n rejectPendingProxyCallById(\n call.toolCallId,\n new Error(\n `Pending proxy call '${call.toolName}' arrived after the stream was already closed`,\n ),\n )\n return\n }\n log.info(\"received pending proxy call for session\", {\n sessionKey: sk,\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n })\n noteProxyActivity()\n noteToolActivity()\n drainBuffer.push(call)\n if (noteResultBoundaryCall()) return\n if (drainTimer) clearTimeout(drainTimer)\n drainTimer = setTimeout(drainNow, DRAIN_QUIET_MS)\n })\n\n proc.on(\"error\", procErrorHandler)\n\n // On abort, keep process alive for next message\n if (options.abortSignal) {\n // Proxy calls this turn handed to opencode will never get a result\n // once the operator aborts: opencode stops its tool runs with the\n // turn. Release them now, so the CLI's parked requests return and\n // nothing waits for the next message to find out. Late-result\n // recovery is untouched: it holds results that already arrived.\n const releaseAbandonedProxyCalls = (reason: string) => {\n if (drainBuffer.length === 0 && getPendingProxyCalls(sk).length === 0) return\n rejectAllPendingProxyCallsForSession(sk, new Error(reason))\n drainBuffer.length = 0\n }\n options.abortSignal.addEventListener(\"abort\", () => {\n autoContinueState.aborted = true\n if (turnCompleted || controllerClosed) {\n // This stream already ended on a proxy tool boundary. An abort\n // here is NOT necessarily the operator: opencode 1.18.32 aborts\n // the signal of every step that ends in tool calls, about a\n // second after the finish, while it runs the tool (measured:\n // 348 of 938 proxied calls on 2026-09-23, and every call in a\n // plugin-only scratch config). Releasing on that rejected calls\n // that were working, told Claude \"the user doesn't want to\n // proceed\", and pushed each result into the next turn as text.\n // The abort reason is the same `AbortError` either way, so\n // opencode's session status decides: still busy means it is\n // running the tool, idle means the operator stopped the turn.\n // Unknown keeps the call, which at worst leaves a real abort\n // waiting for the next message, as it did before release-on-\n // abort existed.\n const stoppedProcess = activeProcess\n if (\n stoppedProcess &&\n stoppedProcess.lineEmitter.listenerCount(\"line\") === 0 &&\n getPendingProxyCalls(sk).length > 0\n ) {\n const reason = describeAbortReason(options.abortSignal?.reason)\n void settleSessionRunState(affinity).then((stopped) => {\n // Re-checked after the wait: a later turn that attached in\n // the meantime owns these calls now.\n const stillParked =\n stoppedProcess.lineEmitter.listenerCount(\"line\") === 0 &&\n getPendingProxyCalls(sk).length > 0\n // Only a positive `busy` keeps the call. `unknown` (no SDK\n // client, no status route, a failed read) releases exactly\n // as it did before this check existed, so a build that\n // cannot ask is never left worse off.\n if (stopped === \"busy\" || !stillParked) {\n log.debug(\"abort at a tool boundary while opencode is still running the turn; keeping pending calls\", {\n sk,\n session: stopped,\n stillParked,\n reason,\n })\n return\n }\n log.info(\"abort between proxy tool boundaries; releasing pending calls\", { sk, reason })\n void interruptTurn(stoppedProcess).then((idle) => {\n log.info(\"interrupt sent for aborted turn\", { sk, idle })\n })\n releaseAbandonedProxyCalls(\n \"Provider stream was aborted while opencode was running its proxy tool calls\",\n )\n })\n }\n return\n }\n\n // Stop the CLI's turn, not just our end of the stream: it would\n // otherwise run the abandoned turn to completion, billing tokens\n // and executing tools, with its late output landing in the next\n // turn. The process itself stays alive for the next message.\n if (activeProcess) {\n void interruptTurn(activeProcess).then((idle) => {\n log.info(\"interrupt sent for aborted turn\", { sk, idle })\n })\n }\n\n if (!hasReceivedContent) {\n log.info(\n \"abort signal received before content, closing stream immediately\",\n { cwd },\n )\n releaseAbandonedProxyCalls(\n \"Provider stream was aborted before pending proxy calls were emitted\",\n )\n controllerClosed = true\n cleanupTurn()\n try {\n controller.close()\n } catch {}\n return\n }\n\n log.info(\n \"abort signal received mid-turn, starting grace period\",\n { cwd },\n )\n releaseAbandonedProxyCalls(\n \"Provider stream was aborted while proxy tool calls were pending\",\n )\n // Abort grace period — short, since the user already asked to stop.\n startResultFallback(5_000)\n })\n }\n\n if (hasMatchedPendingResults) {\n // Tool-result turn: the prompt carries opencode's results for the\n // proxy tool calls we drained on the previous turn. Resolve each\n // matched call (claude CLI's HTTP handlers wake up and continue).\n // Parallel tools may complete in separate opencode turns. Keep\n // unmatched siblings pending until their own result, an explicit\n // abort/new user turn, or the proxy deadline.\n for (const { call, result } of previousPendingProxyMatches) {\n if (result) {\n const channelClosed = isPendingProxyCallChannelClosed(call)\n log.info(\"resolving pending proxy call from tool result prompt\", {\n sessionKey: sk,\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n channelClosed,\n })\n const completions = (activeProcess!.pendingProxyCompletions ??= new Map())\n if (!completions.has(call.toolCallId)) {\n completions.set(call.toolCallId, {\n call,\n result,\n recoveryRequired: channelClosed || unattendedTurnEnded,\n })\n }\n // With a closed channel this only clears the broker entry;\n // proxy-mcp drops the write and the result travels below.\n resolvePendingProxyCallById(call.toolCallId, result)\n } else {\n log.info(\n \"leaving unmatched parallel proxy call pending\",\n {\n sessionKey: sk,\n toolCallId: call.toolCallId,\n toolName: call.toolName,\n },\n )\n }\n }\n\n if (unattendedTurnEnded) deliverPendingCompletions()\n\n // Calls queued while no turn was attached were never handed to\n // opencode; the child is blocked on them right now.\n const unemitted = getPendingProxyCalls(sk).filter(\n (call) => !call.emitted,\n )\n if (unemitted.length > 0) {\n log.notice(\"draining proxy calls queued between turns\", {\n sessionKey: sk,\n toolCallIds: unemitted.map((call) => call.toolCallId),\n })\n drainBuffer.push(...unemitted)\n drainNow()\n return\n }\n\n if (getPendingProxyCalls(sk).length === 0) {\n armStartWatchdog()\n }\n return\n }\n\n // No pending calls had matching tool-results. If any pending calls\n // are still hanging around from a prior turn, reject them so the\n // HTTP handlers in proxy-mcp don't sit blocked forever while we\n // proceed with a brand new user message.\n if (previousPendingProxyCalls.length > 0) {\n for (const call of previousPendingProxyCalls) {\n rejectPendingProxyCallById(\n call.toolCallId,\n new Error(\n `Pending proxy call '${call.toolName}' (${call.toolCallId}) was orphaned by a new user turn; rejecting`,\n ),\n )\n }\n }\n\n // Send the user message for a fresh turn.\n if (activeProcess) noteTurnStarted(activeProcess)\n proc.stdin?.write(userMsg + \"\\n\")\n log.debug(\"sent user message\", { textLength: userMsg.length })\n // Arm the start watchdog so a reused child that goes silent after\n // the envelope write (seen after a long proxy-blocked tool call)\n // is respawned with --session-id instead of hanging the turn.\n armStartWatchdog()\n }\n\n void setup().catch((err) => {\n log.error(\"failed to set up doStream\", {\n error: err instanceof Error ? err.message : String(err),\n })\n controller.enqueue({\n type: \"error\",\n error: err instanceof Error ? err : new Error(String(err)),\n })\n try {\n controller.close()\n } catch {}\n })\n },\n cancel() {\n // Consumer cancelled the stream\n },\n })\n\n return {\n stream,\n request: { body: { text: userMsg } },\n response: { headers: {} },\n }\n }\n}\n","import { appendFileSync, mkdirSync, renameSync, statSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { dirname, join } from \"node:path\"\n\nexport type LogLevel = \"debug\" | \"info\" | \"notice\" | \"warn\" | \"error\"\nexport type LogMode = \"silent\" | \"debug\"\n\nexport interface LoggerConfig {\n file: boolean\n dir: string | null\n mode: LogMode\n level: LogLevel\n}\n\nconst LEVEL_RANK: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n notice: 2,\n warn: 3,\n error: 4,\n}\n\nconst MAX_LOG_BYTES = 5 * 1024 * 1024 // 5 MB\nconst DEFAULT_DIR = join(homedir(), \".local\", \"share\", \"opencode-claude-code\")\n\nconst DEFAULT_CONFIG: LoggerConfig = {\n file: false,\n dir: null,\n mode: \"silent\",\n level: \"info\",\n}\n\nfunction parseBoolEnv(v: string | undefined): boolean | undefined {\n if (v == null) return undefined\n const s = v.toLowerCase().trim()\n if (s === \"\") return undefined\n if (s === \"0\" || s === \"false\" || s === \"no\" || s === \"off\") return false\n return true\n}\n\nfunction parseLevelEnv(v: string | undefined): LogLevel | undefined {\n if (v == null) return undefined\n const s = v.toLowerCase().trim()\n if (s === \"\") return undefined\n if (s === \"debug\" || s === \"info\" || s === \"notice\" || s === \"warn\" || s === \"error\") {\n return s\n }\n return undefined\n}\n\nfunction parseModeFromDebugEnv(v: string | undefined): LogMode | undefined {\n if (v == null || v === \"\") return undefined\n return v.includes(\"opencode-claude-code\") ? \"debug\" : undefined\n}\n\nfunction withEnvOverrides(base: LoggerConfig): LoggerConfig {\n const result: LoggerConfig = { ...base }\n const envFile = parseBoolEnv(process.env.OPENCODE_CLAUDE_CODE_LOG_FILE)\n if (envFile !== undefined) result.file = envFile\n const envDir = process.env.OPENCODE_CLAUDE_CODE_LOG_DIR\n if (envDir !== undefined && envDir !== \"\") result.dir = envDir\n const envMode = parseModeFromDebugEnv(process.env.DEBUG)\n if (envMode !== undefined) result.mode = envMode\n const envLevel = parseLevelEnv(process.env.OPENCODE_CLAUDE_CODE_LOG_LEVEL)\n if (envLevel !== undefined) result.level = envLevel\n return result\n}\n\nlet activeConfig: LoggerConfig = withEnvOverrides(DEFAULT_CONFIG)\nlet fileLoggingDisabled = false\n\n/**\n * Configure the logger from plugin settings. Env vars override the supplied\n * config when explicitly set, so a developer can flip behavior for a single\n * process without editing opencode.jsonc.\n *\n * `OPENCODE_CLAUDE_CODE_LOG_FILE` → `file` (1/true/on/yes vs 0/false/no/off)\n * `OPENCODE_CLAUDE_CODE_LOG_DIR` → `dir`\n * `DEBUG=opencode-claude-code` → `mode: \"debug\"`\n * `OPENCODE_CLAUDE_CODE_LOG_LEVEL` → `level` (debug | info | notice | warn | error)\n */\nexport function configureLogger(input: Partial<LoggerConfig>): void {\n const merged: LoggerConfig = { ...DEFAULT_CONFIG, ...input }\n activeConfig = withEnvOverrides(merged)\n fileLoggingDisabled = false\n}\n\nexport function getLoggerConfig(): LoggerConfig {\n return { ...activeConfig }\n}\n\n/** Test-only helper. Resets to defaults+env so tests are deterministic. */\nexport function _resetLoggerForTests(): void {\n activeConfig = withEnvOverrides(DEFAULT_CONFIG)\n fileLoggingDisabled = false\n}\n\nfunction resolvedLogFile(): string {\n return join(activeConfig.dir ?? DEFAULT_DIR, \"plugin.log\")\n}\n\nfunction rotateIfNeeded(logFile: string): void {\n try {\n const stat = statSync(logFile)\n if (stat.size > MAX_LOG_BYTES) {\n renameSync(logFile, `${logFile}.1`)\n }\n } catch {\n // file does not exist yet — nothing to rotate\n }\n}\n\nfunction writeToFile(line: string): void {\n if (!activeConfig.file) return\n if (fileLoggingDisabled) return\n try {\n const logFile = resolvedLogFile()\n mkdirSync(dirname(logFile), { recursive: true })\n rotateIfNeeded(logFile)\n appendFileSync(logFile, line + \"\\n\", \"utf8\")\n } catch {\n // Disable on first failure to avoid spamming errors on a read-only FS.\n fileLoggingDisabled = true\n }\n}\n\nfunction fmt(level: string, msg: string, data?: Record<string, unknown>): string {\n const ts = new Date().toISOString()\n const base = `[${ts}] [opencode-claude-code] ${level}: ${msg}`\n if (data && Object.keys(data).length > 0) {\n return `${base} ${JSON.stringify(data)}`\n }\n return base\n}\n\nfunction shouldEmit(level: LogLevel): boolean {\n return LEVEL_RANK[level] >= LEVEL_RANK[activeConfig.level]\n}\n\nfunction shouldTui(level: LogLevel): boolean {\n // warn/error are alwaysStderr: a developer who passes the level threshold\n // should still see real problems in the TUI regardless of mode. Below-\n // threshold entries are filtered earlier by shouldEmit().\n if (level === \"warn\" || level === \"error\") return true\n return activeConfig.mode === \"debug\"\n}\n\nfunction emit(level: LogLevel, msg: string, data?: Record<string, unknown>): void {\n if (!shouldEmit(level)) return\n const line = fmt(level.toUpperCase(), msg, data)\n if (shouldTui(level)) {\n console.error(line)\n }\n writeToFile(line)\n}\n\nexport const log = {\n debug(msg: string, data?: Record<string, unknown>) {\n emit(\"debug\", msg, data)\n },\n info(msg: string, data?: Record<string, unknown>) {\n emit(\"info\", msg, data)\n },\n notice(msg: string, data?: Record<string, unknown>) {\n emit(\"notice\", msg, data)\n },\n warn(msg: string, data?: Record<string, unknown>) {\n emit(\"warn\", msg, data)\n },\n error(msg: string, data?: Record<string, unknown>) {\n emit(\"error\", msg, data)\n },\n}\n","import { log } from \"./logger.js\"\n\nexport type TodoStatus = \"pending\" | \"in_progress\" | \"completed\"\n\nexport interface TodoEntry {\n id: string\n content: string\n status: TodoStatus\n}\n\ninterface PendingCreate {\n subject: string\n createdAt: number\n}\n\ninterface SessionLedger {\n todos: Map<string, TodoEntry>\n pendingCreates: Map<string, PendingCreate>\n}\n\nconst ledgers = new Map<string, SessionLedger>()\n\nconst PENDING_CREATE_TTL_MS = 60_000\n/**\n * A ledger is normally released with its Claude session id, but a session\n * that is never deleted (a long-lived `opencode serve` hopping projects, a\n * CLI session whose id the plugin never sees again) leaves one behind. Same\n * insertion-order cap as the compression store; a todo list belonging to a\n * session that old is not going to be written to opencode again.\n */\nexport const MAX_LEDGER_SESSIONS = 64\nconst TASK_CREATED_PATTERN = /Task\\s*#?\\s*(\\d+)\\s+created/i\nconst VALID_STATUSES: ReadonlySet<TodoStatus> = new Set([\"pending\", \"in_progress\", \"completed\"])\n\nfunction getOrCreate(sessionId: string): SessionLedger {\n let ledger = ledgers.get(sessionId)\n if (!ledger) {\n ledger = { todos: new Map(), pendingCreates: new Map() }\n ledgers.set(sessionId, ledger)\n capLedgers()\n }\n return ledger\n}\n\nfunction capLedgers(): void {\n while (ledgers.size > MAX_LEDGER_SESSIONS) {\n const oldest = ledgers.keys().next()\n if (oldest.done) break\n ledgers.delete(oldest.value)\n log.info(\"todo ledger evicted oldest session\", { sessionId: oldest.value })\n }\n}\n\nfunction prunePending(ledger: SessionLedger): void {\n const cutoff = Date.now() - PENDING_CREATE_TTL_MS\n for (const [id, pending] of ledger.pendingCreates) {\n if (pending.createdAt < cutoff) ledger.pendingCreates.delete(id)\n }\n}\n\nfunction materialize(ledger: SessionLedger): TodoEntry[] {\n return Array.from(ledger.todos.values())\n}\n\nfunction resolveSubject(input: { subject?: unknown; description?: unknown } | undefined): string {\n const subject = typeof input?.subject === \"string\" ? input.subject.trim() : \"\"\n if (subject) return subject\n const description = typeof input?.description === \"string\" ? input.description.trim() : \"\"\n if (description) return description\n return \"(no subject)\"\n}\n\nexport function applyTaskCreateToolUse(\n sessionId: string,\n toolUseId: string,\n input: { subject?: unknown; description?: unknown } | undefined,\n): void {\n if (!sessionId || !toolUseId) return\n const ledger = getOrCreate(sessionId)\n prunePending(ledger)\n ledger.pendingCreates.set(toolUseId, {\n subject: resolveSubject(input),\n createdAt: Date.now(),\n })\n}\n\nexport function applyTaskCreateToolResult(\n sessionId: string,\n toolUseId: string,\n resultText: string,\n): TodoEntry[] | null {\n if (!sessionId || !toolUseId) return null\n const ledger = ledgers.get(sessionId)\n if (!ledger) return null\n const pending = ledger.pendingCreates.get(toolUseId)\n if (!pending) return null\n ledger.pendingCreates.delete(toolUseId)\n const match = typeof resultText === \"string\" ? resultText.match(TASK_CREATED_PATTERN) : null\n if (!match) {\n log.debug(\"TaskCreate result did not match expected format\", { sessionId, toolUseId, resultText })\n return null\n }\n const claudeId = match[1]\n if (ledger.todos.has(claudeId)) {\n log.debug(\"TaskCreate result for already-known claude id; overwriting\", { sessionId, claudeId })\n }\n ledger.todos.set(claudeId, { id: claudeId, content: pending.subject, status: \"pending\" })\n return materialize(ledger)\n}\n\nexport function applyTaskUpdate(\n sessionId: string,\n input: { taskId?: unknown; subject?: unknown; status?: unknown } | undefined,\n): TodoEntry[] | null {\n if (!sessionId) return null\n const taskId = typeof input?.taskId === \"string\" ? input.taskId : null\n if (!taskId) return null\n const ledger = ledgers.get(sessionId)\n if (!ledger) return null\n const entry = ledger.todos.get(taskId)\n if (!entry) {\n log.debug(\"TaskUpdate for unknown task id\", { sessionId, taskId })\n return null\n }\n if (input?.status === \"deleted\") {\n ledger.todos.delete(taskId)\n return materialize(ledger)\n }\n if (typeof input?.status === \"string\" && VALID_STATUSES.has(input.status as TodoStatus)) {\n entry.status = input.status as TodoStatus\n }\n if (typeof input?.subject === \"string\" && input.subject.trim().length > 0) {\n entry.content = input.subject.trim()\n }\n return materialize(ledger)\n}\n\nexport function clearLedger(sessionId: string): void {\n if (!sessionId) return\n ledgers.delete(sessionId)\n}\n\nexport function getLedger(sessionId: string): TodoEntry[] {\n const ledger = ledgers.get(sessionId)\n if (!ledger) return []\n return materialize(ledger)\n}\n\nexport function _resetAllLedgersForTests(): void {\n ledgers.clear()\n}\n","import { log } from \"./logger.js\"\nimport { applyTaskCreateToolUse, applyTaskUpdate, type TodoEntry } from \"./todo-ledger.js\"\nimport type { WebSearchRouting } from \"./types.js\"\n\nexport interface MapToolOptions {\n webSearch?: WebSearchRouting\n sessionId?: string\n toolUseId?: string\n}\n\n/** Claude CLI's built-in web search tool (name varies by CLI version). */\nexport function isWebSearchTool(name: string): boolean {\n return name === \"WebSearch\" || name === \"web_search\"\n}\n\n/**\n * True when WebSearch runs inside Claude CLI (default) rather than being\n * forwarded to an opencode tool. In that case the tool-call part must not\n * reach opencode — \"WebSearch\" has no registry entry there and renders as\n * an invalid tool row. Callers show the query as a text line instead.\n */\nexport function isWebSearchHandledByCli(route?: WebSearchRouting): boolean {\n return !route || route === \"claude\" || route === \"disabled\"\n}\n\n/**\n * Map Claude CLI tool input (snake_case) to OpenCode tool input (camelCase)\n */\nfunction mapToolInput(name: string, input: any): any {\n if (!input) return input\n\n switch (name) {\n case \"Write\":\n return {\n filePath: input.file_path ?? input.filePath,\n content: input.content,\n }\n case \"Edit\":\n return {\n filePath: input.file_path ?? input.filePath,\n oldString: input.old_string ?? input.oldString,\n newString: input.new_string ?? input.newString,\n replaceAll: input.replace_all ?? input.replaceAll,\n }\n case \"Read\":\n return {\n filePath: input.file_path ?? input.filePath,\n offset: input.offset,\n limit: input.limit,\n }\n case \"Bash\":\n return {\n command: input.command,\n description:\n input.description ||\n `Execute: ${String(input.command || \"\").slice(0, 50)}${String(input.command || \"\").length > 50 ? \"...\" : \"\"}`,\n timeout: input.timeout,\n }\n case \"NotebookEdit\":\n return {\n notebookPath: input.notebook_path ?? input.notebookPath,\n cellNumber: input.cell_number ?? input.cellNumber,\n newSource: input.new_source ?? input.newSource,\n cellType: input.cell_type ?? input.cellType,\n editMode: input.edit_mode ?? input.editMode,\n }\n case \"Glob\":\n return {\n pattern: input.pattern,\n path: input.path,\n }\n case \"Grep\":\n return {\n pattern: input.pattern,\n path: input.path,\n include: input.include,\n }\n case \"TodoWrite\":\n if (Array.isArray(input.todos)) {\n const mappedTodos = input.todos.map((todo: any, index: number) => ({\n content: todo.content,\n status: todo.status || \"pending\",\n priority: todo.priority || \"medium\",\n id: todo.id || `todo_${Date.now()}_${index}`,\n }))\n return { todos: mappedTodos }\n }\n return input\n default:\n return input\n }\n}\n\n// Tools that Claude CLI executes internally but we report to opencode for UI display\nconst OPENCODE_HANDLED_TOOLS = new Set([\n \"Edit\",\n \"Write\",\n \"Bash\",\n \"NotebookEdit\",\n \"Read\",\n \"Glob\",\n \"Grep\",\n])\n\n// Claude CLI internal tools that should not be forwarded to opencode.\n// These are part of Claude Code's own system and have no opencode equivalent.\n// Tools the Claude CLI emits for its own internal bookkeeping (sub-agents,\n// task tracking, search). opencode has no matching tool registry entry, so\n// forwarding them surfaces as `⚙ invalid` rows in the UI. Skip them.\n// TaskOutput is intentionally NOT here — it has an explicit bash-echo mapping\n// below so the result stays visible.\nconst CLAUDE_INTERNAL_TOOLS = new Set([\n \"ToolSearch\",\n \"Agent\",\n \"AskFollowupQuestion\",\n \"TaskList\",\n \"TaskGet\",\n \"TaskStop\",\n])\n\n/**\n * Wrap model-controlled text as one shell single-quoted word.\n *\n * `TaskOutput` is displayed by running a real `bash` call, so its payload\n * reaches a shell. Double quotes are not enough: inside them `$(…)`,\n * backticks and `${…}` still expand, so `TaskOutput({content: \"X$(id -u)Y\"})`\n * executed `id` while the operator saw a command that read like a print\n * (issue #27). Single quotes suppress every expansion; the only character\n * needing care is `'` itself, closed and reopened around an escaped one.\n */\nexport function singleQuoteForShell(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`\n}\n\nfunction emitTodoWrite(todos: TodoEntry[]) {\n return {\n name: \"todowrite\",\n input: {\n todos: todos.map((todo) => ({\n id: todo.id,\n content: todo.content,\n status: todo.status,\n priority: \"medium\",\n })),\n },\n executed: false,\n }\n}\n\nexport function mapTool(\n name: string,\n input?: any,\n opts?: MapToolOptions,\n): { name: string; input?: any; executed: boolean; skip?: boolean } {\n // Claude CLI internal tools — skip entirely\n if (CLAUDE_INTERNAL_TOOLS.has(name)) {\n log.debug(\"skipping Claude CLI internal tool\", { name })\n return { name, input, executed: true, skip: true }\n }\n\n // TaskCreate: stash subject keyed by tool_use_id; emission happens on tool_result.\n // Without sessionId+toolUseId we cannot maintain the ledger, so fall back to skip\n // (preserves old behavior for callers that haven't been threaded yet).\n if (name === \"TaskCreate\") {\n if (opts?.sessionId && opts?.toolUseId) {\n applyTaskCreateToolUse(opts.sessionId, opts.toolUseId, input)\n }\n return { name, input, executed: true, skip: true }\n }\n\n // TaskUpdate: mutate ledger and emit full list as opencode todowrite. Without\n // sessionId, fall back to skip. Unknown task ids return null from the ledger\n // and we drop the event.\n if (name === \"TaskUpdate\") {\n if (opts?.sessionId) {\n const list = applyTaskUpdate(opts.sessionId, input)\n if (list !== null) return emitTodoWrite(list)\n }\n return { name, input, executed: true, skip: true }\n }\n\n // Plan mode tools\n if (name === \"EnterPlanMode\") return { name: \"plan_enter\", input: {}, executed: false }\n if (name === \"ExitPlanMode\") return { name: \"plan_exit\", input, executed: false }\n\n // TodoWrite needs opencode to run it locally so Todo.Service (and the UI\n // widget backed by it) gets populated. Reporting as provider-executed would\n // short-circuit opencode's own execute and leave the todo panel empty.\n if (name === \"TodoWrite\") {\n const mappedInput = mapToolInput(name, input)\n return { name: \"todowrite\", input: mappedInput, executed: false }\n }\n\n // WebSearch — routing controlled by config.webSearch\n if (isWebSearchTool(name)) {\n const mappedInput = input?.query ? { query: input.query } : input\n const route = opts?.webSearch\n if (route && route !== \"claude\" && route !== \"disabled\") {\n log.debug(\"routing WebSearch to opencode tool\", { target: route, mappedInput })\n return { name: route, input: mappedInput, executed: false }\n }\n // Claude CLI runs WebSearch internally; \"WebSearch\" has no opencode\n // registry entry, so forwarding the tool-call part surfaces a\n // \"Model tried to call unavailable tool\" invalid row in opencode.\n // Skip the part — callers render the query as a text line instead.\n log.debug(\"WebSearch executed by Claude CLI\", { mappedInput })\n return { name: \"WebSearch\", input: mappedInput, executed: true, skip: true }\n }\n\n // TaskOutput -> bash printf\n if (name === \"TaskOutput\") {\n if (!input) return { name: \"bash\", executed: false }\n const output = input?.content || input?.output || JSON.stringify(input)\n return {\n name: \"bash\",\n input: {\n command: `printf '%s\\\\n' ${singleQuoteForShell(`TASK OUTPUT: ${String(output)}`)}`,\n description: \"Displaying task output\",\n },\n executed: false,\n }\n }\n\n // Third-party MCP tools: mcp__<server>__<tool> -> <server>_<tool>.\n // Marked provider-executed because Claude CLI runs these internally via\n // its own --mcp-config; the tool-result is already in the stream. If we\n // reported executed:false, opencode would look up the tool in its own\n // registry, fail to find it, and emit an `invalid` tool error that\n // shadows the real result.\n //\n // Our own proxy tools (`mcp__opencode_proxy__*`) are filtered out by\n // callers before reaching here, so this branch only ever sees user MCP\n // servers configured in Claude CLI's settings.\n if (name.startsWith(\"mcp__\")) {\n const parts = name.slice(5).split(\"__\")\n if (parts.length >= 2) {\n const serverName = parts[0]\n const toolName = parts.slice(1).join(\"_\")\n const openCodeName = `${serverName}_${toolName}`\n log.debug(\"mapping MCP tool\", { original: name, mapped: openCodeName })\n return { name: openCodeName, input, executed: true }\n }\n }\n\n // Tools executed by Claude CLI internally - map to lowercase for opencode\n if (OPENCODE_HANDLED_TOOLS.has(name)) {\n const mappedInput = mapToolInput(name, input)\n const openCodeName = name.toLowerCase()\n log.debug(\"mapping CLI-executed tool\", { name, openCodeName })\n return { name: openCodeName, input: mappedInput, executed: true }\n }\n\n // Unknown tools - treated as provider-executed\n return { name, input, executed: true }\n}\n","/**\n * Tool names and inputs in the shape the opencode host expects.\n *\n * Every tool call this plugin hands opencode, whether a proxied call opencode\n * must run (`bash`, `task`, ...) or a Claude CLI tool it only renders\n * (`read`, `glob`, ...), is built in opencode 1.x's vocabulary. opencode 2.x\n * renamed several tools and fields, and rejects a name it does not know with\n * `No tool named \"bash\" is currently available` (measured on 2.0.11). So on V2,\n * and only on V2, the stream is rewritten once at its edge rather than at each\n * of the dozen places a tool part is built. On V1 nothing here runs.\n *\n * The V2 table was read off `@opencode/core@2.0.11`'s own tool definitions\n * (`dist/tool/plugin/*.js`): each tool's registered `name` and input schema.\n */\n\n/**\n * Which opencode major a model serves. Carried by each model's config\n * (`ClaudeCodeConfig.hostApi`), never held process-wide: opencode 1.18 calls a\n * dual export's `setup` too, so a global flipped there turned V1's tool calls\n * into V2 names and V1 rejected every one (measured on 1.18.32).\n */\nexport type HostToolDialect = \"v1\" | \"v2\"\n\ntype Input = Record<string, unknown>\n\ninterface HostTool {\n name: string\n /** Absent when the input already matches, so its deltas can stream as-is. */\n input?: (input: Input) => Input\n}\n\n/** Drops keys whose value is undefined, so optional V2 fields stay optional. */\nfunction defined(input: Input): Input {\n return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined))\n}\n\n/**\n * V1 name to its V2 counterpart. `null` means V2 has no such tool: the part is\n * dropped, because an unknown name is an error there, not a harmless row.\n */\nconst V2_TOOLS: Record<string, HostTool | null> = {\n bash: {\n name: \"shell\",\n // V2's shell takes no `description`; `workdir`, `timeout` and\n // `background` keep their names.\n input: (input) =>\n defined({\n command: input.command,\n workdir: input.workdir,\n timeout: input.timeout,\n background: input.background,\n }),\n },\n edit: {\n name: \"edit\",\n input: (input) =>\n defined({\n path: input.filePath ?? input.path,\n oldString: input.oldString,\n newString: input.newString,\n replaceAll: input.replaceAll,\n }),\n },\n write: {\n name: \"write\",\n input: (input) => defined({ path: input.filePath ?? input.path, content: input.content }),\n },\n read: {\n name: \"read\",\n input: (input) =>\n defined({ path: input.filePath ?? input.path, offset: input.offset, limit: input.limit }),\n },\n glob: { name: \"glob\" },\n grep: { name: \"grep\" },\n webfetch: { name: \"webfetch\" },\n websearch: { name: \"websearch\" },\n question: { name: \"question\" },\n task: {\n name: \"subagent\",\n input: (input) =>\n defined({\n agent: input.subagent_type ?? input.agent,\n description: input.description,\n prompt: input.prompt,\n sessionID: input.task_id ?? input.sessionID,\n background: input.background,\n }),\n },\n todowrite: null,\n plan_enter: null,\n plan_exit: null,\n notebookedit: null,\n}\n\n/**\n * The host's name and input for a tool call, `null` to drop it, or the call\n * unchanged when the host needs no translation or the tool is not in the table.\n */\nexport function translateToolForHost(\n name: string,\n input: Input,\n dialect: HostToolDialect = \"v1\",\n): { name: string; input: Input } | null {\n if (dialect !== \"v2\") return { name, input }\n const tool = V2_TOOLS[name]\n if (tool === null) return null\n if (tool === undefined) return { name, input }\n return { name: tool.name, input: tool.input ? tool.input(input) : input }\n}\n\ntype StreamPart = { type: string; [key: string]: unknown }\n\nfunction parseInput(raw: unknown): Input {\n if (typeof raw !== \"string\") return (raw as Input) ?? {}\n try {\n const parsed = JSON.parse(raw)\n return parsed && typeof parsed === \"object\" ? (parsed as Input) : {}\n } catch {\n return {}\n }\n}\n\n/**\n * A per-stream rewriter for tool parts. Stateful because opencode ties a\n * tool's start, deltas, call and result together by id, so a tool that is\n * renamed or dropped at its start must be treated the same way to the end.\n * Returns `null` for a part to drop.\n */\nexport function createHostToolPartTranslator(\n dialect: HostToolDialect = \"v1\",\n): (part: StreamPart) => StreamPart | null {\n if (dialect !== \"v2\") return (part) => part\n\n const dropped = new Set<string>()\n const translated = new Map<string, HostTool>()\n\n const classify = (id: string, name: unknown): HostTool | null | undefined => {\n if (dropped.has(id)) return null\n const known = translated.get(id)\n if (known) return known\n if (typeof name !== \"string\") return undefined\n const tool = V2_TOOLS[name]\n if (tool === null) {\n dropped.add(id)\n return null\n }\n if (tool) translated.set(id, tool)\n return tool\n }\n\n return (part) => {\n switch (part.type) {\n case \"tool-input-start\": {\n const tool = classify(String(part.id), part.toolName)\n if (tool === null) return null\n return tool ? { ...part, toolName: tool.name } : part\n }\n case \"tool-input-delta\": {\n const tool = classify(String(part.id), undefined)\n if (tool === null) return null\n // A translated input would stream the V1 keys and then arrive with\n // the V2 ones; opencode takes the final `tool-call` input anyway.\n return tool?.input ? null : part\n }\n case \"tool-input-end\": {\n return classify(String(part.id), undefined) === null ? null : part\n }\n case \"tool-call\": {\n const tool = classify(String(part.toolCallId), part.toolName)\n if (tool === null) return null\n if (!tool) return part\n const input = tool.input ? tool.input(parseInput(part.input)) : parseInput(part.input)\n return {\n ...part,\n toolName: tool.name,\n input: typeof part.input === \"string\" ? JSON.stringify(input) : input,\n }\n }\n case \"tool-result\": {\n const tool = classify(String(part.toolCallId), part.toolName)\n if (tool === null) return null\n return tool ? { ...part, toolName: tool.name } : part\n }\n default:\n return part\n }\n }\n}\n\n/** Applies the translator to a whole stream; the identity on V1. */\nexport function translateStreamForHost<T extends StreamPart>(\n stream: ReadableStream<T>,\n dialect: HostToolDialect = \"v1\",\n): ReadableStream<T> {\n if (dialect !== \"v2\") return stream\n const translate = createHostToolPartTranslator(dialect)\n return stream.pipeThrough(\n new TransformStream<T, T>({\n transform(part, controller) {\n const next = translate(part)\n if (next) controller.enqueue(next as T)\n },\n }),\n )\n}\n","import { homedir } from \"node:os\"\nimport type { LanguageModelV3 } from \"@ai-sdk/provider\"\nimport {\n DEFAULT_ACCOUNT,\n ensureAccountRuntime,\n normalizeAccountName,\n} from \"./accounts.js\"\nimport {\n formatResetsAt,\n isRateLimitRejected,\n resetsAtToMs,\n type RateLimitInfo,\n} from \"./cli-events.js\"\nimport { log } from \"./logger.js\"\nimport {\n QUESTION_TOOL_NAME,\n collectAnswerStrings,\n unwrapToolOutput,\n type QuestionToolCall,\n} from \"./plan-mode-question.js\"\n\n/**\n * Account failover.\n *\n * When the account a conversation is running on is out of usage, the turn\n * fails and the only remedy is the operator's: wait, pay, or move to another\n * account. The last one is the only one the plugin can help with, because the\n * accounts are already configured and each is just another `CLAUDE_CONFIG_DIR`\n * behind a wrapper script (`src/accounts.ts`).\n *\n * So the limit ends the turn with a form instead of an error: one option per\n * other configured account, plus `stop`. The form is opencode's own `question`\n * tool, reached exactly the way the plan-mode bridge reaches it, which means\n * the answer arrives on the next `doStream` call as a `tool-result` and the\n * switch happens inside the same opencode turn, with no new user message.\n * Leaving it unanswered waits, and waiting costs nothing.\n *\n * Three things about the design are deliberate and load-bearing:\n *\n * 1. **The override is scoped to the limited ACCOUNT, not the session.** A\n * rate limit is a property of the account, so one pick governs every\n * session running on it, and a subagent follows its parent for free\n * without needing its own form (child sessions are never asked).\n * 2. **A switch is always a fresh Claude session with the conversation\n * replayed.** Transcripts live under the account's own config dir, so\n * `--resume` can never cross accounts. The caller drops the active process\n * and the stored Claude session id, which makes `includeHistoryContext`\n * true and rebuilds the thread from opencode's prompt.\n * 3. **The `@account` suffix must come off the model id.** `parseModelId`\n * keeps it on purpose because the source account's own wrapper strips it,\n * but a failover spawn goes through a DIFFERENT wrapper (or the bare\n * binary for `default`), which would pass `--model claude-opus-5@appical`\n * straight to a CLI that rejects it.\n */\n\ntype Prompt = Parameters<LanguageModelV3[\"doGenerate\"]>[0][\"prompt\"]\n\n/** Leading text of the `▌` note the plugin writes when a switch happens. */\nexport const FAILOVER_MARKER = \"▌ **account failover:**\"\n\n/**\n * Prefix of every synthetic `question` tool-call id this module mints. The\n * transcript rebuild keys on it to drop the dialog, so a replayed history\n * never hands Claude a form it never saw.\n */\nexport const ACCOUNT_FAILOVER_TOOL_CALL_PREFIX = \"account_failover_\"\n\n// Escaped rather than a literal NUL byte: one raw \\0 anywhere in the file\n// makes git treat this TypeScript source as binary, so it has no diff, no\n// line-level merge and no review. Same string value, text file.\nconst KEY_SEPARATOR = \"\\u0000\"\n\n/** The option that ends the turn instead of switching. */\nexport const STOP_ANSWER = \"stop\"\n\n/**\n * The only error texts that count as \"this account is out of usage\".\n *\n * Deliberately not \"any 4xx\" and not \"any error\": a transient network failure\n * or a bad flag must never open a form that moves where the billing lands.\n * Both strings are the ones observed in production and recorded in AGENTS.md;\n * the apostrophe class covers the straight and curly forms.\n */\nexport const ACCOUNT_LIMIT_PATTERNS: RegExp[] = [\n /third-party apps now draw from your extra usage/i,\n /you[’'`]?ve hit your individual spend limit/i,\n]\n\n/** Leading text of the `▌` note naming an account that cannot serve requests. */\nexport const ACCOUNT_BLOCK_MARKER = \"▌ **claude account:**\"\n\n/**\n * The CLI's failure kinds that belong to the ACCOUNT rather than to this\n * request, each with what the operator can do about it. Read off the\n * `error` enum on the assistant message in Claude Code 2.1.280's own schema;\n * `rate_limit` is left out because the limit path above already covers it,\n * and the request-level kinds (`invalid_request`, `server_error`, ...) are\n * left out because another account would fail the same way.\n *\n * `authentication_failed` is the one measured in production: on 2026-09-23\n * the `appical` login expired, every turn on it failed in about 40 ms, and all\n * the operator saw was the CLI's own reply \"Failed to authenticate: OAuth\n * session expired and could not be refreshed\", with no hint of which account\n * or what to run.\n */\nconst ACCOUNT_BLOCKS = {\n authentication_failed: { what: \"is not logged in (its login expired or was revoked)\", login: true },\n oauth_org_not_allowed: { what: \"belongs to an organization that does not allow this login\", login: false },\n account_on_hold: { what: \"is on hold\", login: false },\n verification_required: { what: \"needs to be verified at claude.ai\", login: false },\n billing_error: { what: \"has a billing problem\", login: false },\n} as const\n\nexport type AccountBlockKind = keyof typeof ACCOUNT_BLOCKS\n\n/** The account-level failure an assistant message reports, if any. */\nexport function accountBlockKind(msg: { type?: string; error?: unknown }): AccountBlockKind | null {\n if (msg.type !== \"assistant\" || typeof msg.error !== \"string\") return null\n return Object.prototype.hasOwnProperty.call(ACCOUNT_BLOCKS, msg.error)\n ? (msg.error as AccountBlockKind)\n : null\n}\n\n/** What the switch form says about the account, in place of \"out of usage\". */\nexport function describeAccountBlock(kind: AccountBlockKind): string {\n return ACCOUNT_BLOCKS[kind].what\n}\n\n/**\n * The command that logs this account in again. An account provider is only a\n * `CLAUDE_CONFIG_DIR` (the wrapper script sets nothing else), so naming the\n * directory is the whole instruction, and it works without the wrapper.\n */\nexport function loginCommandFor(configDir: string | undefined, home = homedir()): string {\n if (!configDir) return \"claude auth login\"\n const shown = configDir === home || configDir.startsWith(`${home}/`) ? `~${configDir.slice(home.length)}` : configDir\n return `CLAUDE_CONFIG_DIR=${shown} claude auth login`\n}\n\nexport function formatAccountBlockNote(input: {\n kind: AccountBlockKind\n account: string\n configDir?: string\n offeringSwitch: boolean\n}): string {\n const block = ACCOUNT_BLOCKS[input.kind]\n const account = normalizeAccountName(input.account || DEFAULT_ACCOUNT)\n const fix = block.login\n ? `Log in again with \\`${loginCommandFor(input.configDir)}\\`, then resend your message.`\n : \"Check the account at claude.ai, then resend your message.\"\n const then = input.offeringSwitch ? \" Or pick another account below.\" : \"\"\n return `\\n${ACCOUNT_BLOCK_MARKER} the Claude account \"${account}\" ${block.what}. ${fix}${then}\\n`\n}\n\nexport function isAccountLimitError(input: {\n rateLimit?: RateLimitInfo | null\n resultText?: string | null\n}): boolean {\n if (input.rateLimit && isRateLimitRejected(input.rateLimit)) return true\n const text = input.resultText\n if (typeof text !== \"string\" || text.length === 0) return false\n return ACCOUNT_LIMIT_PATTERNS.some((pattern) => pattern.test(text))\n}\n\n// ---------------------------------------------------------------------------\n// The override store: which account replaces which, and until when\n// ---------------------------------------------------------------------------\n\ninterface AccountOverride {\n target: string\n /** Epoch ms the limit resets at, or undefined for \"until opencode restarts\". */\n until?: number\n}\n\nconst accountOverrides = new Map<string, AccountOverride>()\n\n/** Test-only. */\nexport function _resetAccountOverrides(): void {\n accountOverrides.clear()\n}\n\nexport function setAccountOverride(\n source: string,\n target: string,\n until?: number,\n now = Date.now(),\n): void {\n const from = normalizeAccountName(source || DEFAULT_ACCOUNT)\n const to = normalizeAccountName(target)\n if (!to || to === from) return\n // A reset time that is not in the future would expire the override on the\n // very next read, so the switch the operator just asked for would be\n // undone before it ran and the turn would re-hit the same limit. Clock\n // skew and a stale `resetsAt` both produce that, so anything not ahead of\n // now degrades to \"until opencode restarts\" rather than to nothing.\n if (until !== undefined && until <= now) {\n log.notice(\"ignoring a failover reset time that is not in the future\", {\n source: from,\n target: to,\n until,\n })\n until = undefined\n }\n accountOverrides.set(from, { target: to, until })\n log.warn(\n `Claude account \"${from}\" is out of usage; this and every other session on it now runs on \"${to}\"${\n until ? ` until ${new Date(until).toISOString()}` : \" until opencode restarts\"\n }.`,\n { source: from, target: to, until: until ?? null },\n )\n}\n\n/**\n * The account to run on instead of `source`, or undefined when there is no\n * override. An expired one is deleted here (and logged once, because the\n * deletion is what silently sends the next turn back to the original account\n * and replays the conversation again).\n */\nexport function resolveAccountOverride(\n source: string,\n now = Date.now(),\n): string | undefined {\n const from = normalizeAccountName(source || DEFAULT_ACCOUNT)\n const entry = accountOverrides.get(from)\n if (!entry) return undefined\n if (entry.until !== undefined && entry.until <= now) {\n accountOverrides.delete(from)\n log.notice(\n `Claude account \"${from}\" should have usage again; switching back from \"${entry.target}\".`,\n { source: from, target: entry.target, until: entry.until },\n )\n return undefined\n }\n return entry.target\n}\n\nexport function clearAccountOverride(source: string): void {\n accountOverrides.delete(normalizeAccountName(source || DEFAULT_ACCOUNT))\n}\n\n/** Read-only view for `/claude-code-doctor` and tests. */\nexport function snapshotAccountOverrides(): Array<{\n source: string\n target: string\n until?: number\n}> {\n return [...accountOverrides.entries()].map(([source, entry]) => ({\n source,\n target: entry.target,\n ...(entry.until === undefined ? {} : { until: entry.until }),\n }))\n}\n\n// ---------------------------------------------------------------------------\n// Resolving the spawn\n// ---------------------------------------------------------------------------\n\n/** `claude-opus-5@appical` -> `claude-opus-5`. See the module note (3). */\nexport function stripAccountSuffix(modelId: string): string {\n const at = modelId.indexOf(\"@\")\n return at === -1 ? modelId : modelId.slice(0, at)\n}\n\nexport interface FailoverSpawn {\n cliPath: string\n modelId: string\n target?: string\n failedOver: boolean\n}\n\n/**\n * The CLI path and model id this turn should actually spawn with. Without an\n * override the inputs come back untouched, which is what keeps every\n * single-account install on exactly today's code path.\n */\nexport async function resolveFailoverSpawn(input: {\n account: string | undefined\n baseCliPath: string\n cliPath: string\n modelId: string\n now?: number\n}): Promise<FailoverSpawn> {\n const unchanged: FailoverSpawn = {\n cliPath: input.cliPath,\n modelId: input.modelId,\n failedOver: false,\n }\n const source = normalizeAccountName(input.account || DEFAULT_ACCOUNT)\n const target = resolveAccountOverride(source, input.now)\n if (!target) return unchanged\n\n try {\n const cliPath =\n target === DEFAULT_ACCOUNT\n ? input.baseCliPath\n : (await ensureAccountRuntime(target, input.baseCliPath)).cliPath\n return {\n cliPath,\n modelId: stripAccountSuffix(input.modelId),\n target,\n failedOver: true,\n }\n } catch (err) {\n // A wrapper we cannot write is not a reason to spawn nothing: fall back\n // to the limited account and let its own error speak, rather than\n // spawning a path that does not exist.\n log.error(\"failed to prepare the failover account runtime; staying put\", {\n source,\n target,\n error: String(err),\n })\n return unchanged\n }\n}\n\n// ---------------------------------------------------------------------------\n// The form\n// ---------------------------------------------------------------------------\n\n/**\n * On by default whenever more than one account is configured: the operator's\n * pick is the consent, and with no other account there is nothing to offer.\n * Never on a compaction turn (its answer would have nowhere to go), never on\n * the interactive transport (a TUI stdin and no proxy server), never in a\n * child session (a subagent follows its parent's account for free), and never\n * without opencode's `question` entry, where the emitted call renders as\n * `⚙ invalid` and wedges the turn.\n */\nexport function isAccountFailoverQuestionActive(input: {\n configured: \"ask\" | \"off\" | undefined\n candidates: readonly string[]\n opencodeHasQuestion: boolean\n compactionMode: boolean\n interactive: boolean\n childSession: boolean\n}): boolean {\n if (input.compactionMode) return false\n if (input.interactive) return false\n if (input.childSession) return false\n if (input.configured === \"off\") return false\n if (input.candidates.length === 0) return false\n return input.opencodeHasQuestion\n}\n\n/** Every configured account except the one that just hit its limit. */\nexport function failoverCandidates(\n accounts: readonly string[] | undefined,\n source: string,\n): string[] {\n const from = normalizeAccountName(source || DEFAULT_ACCOUNT)\n const out: string[] = []\n for (const raw of accounts ?? []) {\n const name = normalizeAccountName(String(raw))\n if (!name || name === from || out.includes(name)) continue\n out.push(name)\n }\n return out\n}\n\ninterface PendingFailoverQuestion {\n sourceAccount: string\n candidates: string[]\n resetsAt?: number\n /** The question as asked, needed to read opencode's answer sentence. */\n question?: string\n}\n\nconst pendingQuestions = new Map<string, PendingFailoverQuestion>()\n\nfunction pendingKey(sessionKey: string, toolCallId: string): string {\n return `${sessionKey}${KEY_SEPARATOR}${toolCallId}`\n}\n\n/**\n * Called from `deleteClaudeSessionId`, the one destructive session boundary.\n * A pending id that outlives its session would route the next answer at a\n * dialog nobody can act on.\n */\nexport function clearAccountFailoverQuestions(sessionKey: string): void {\n const prefix = `${sessionKey}${KEY_SEPARATOR}`\n for (const key of pendingQuestions.keys()) {\n if (key.startsWith(prefix)) pendingQuestions.delete(key)\n }\n}\n\nfunction describeReset(resetsAt: number | undefined): string | undefined {\n return formatResetsAt(resetsAt)\n}\n\nexport function createAccountFailoverQuestionCall(\n sessionKey: string,\n input: {\n sourceAccount: string\n candidates: readonly string[]\n resetsAt?: number\n window?: string\n /**\n * Why the account cannot serve, when it is not a usage limit\n * (`describeAccountBlock`). Replaces \"is out of usage\" in the question.\n */\n reason?: string\n },\n toolCallId = `${ACCOUNT_FAILOVER_TOOL_CALL_PREFIX}${Math.random()\n .toString(36)\n .slice(2, 10)}`,\n): QuestionToolCall {\n const source = normalizeAccountName(input.sourceAccount || DEFAULT_ACCOUNT)\n const candidates = input.candidates.map((c) => normalizeAccountName(c))\n const resets = describeReset(input.resetsAt)\n const until = resets ?? \"opencode restarts\"\n\n const question = input.reason\n ? `The Claude account \"${source}\" ${input.reason}. Continue this task on another configured account? Leaving this unanswered waits, at no cost.`\n : [\n `The Claude account \"${source}\" is out of usage`,\n input.window ? ` in ${input.window}` : \"\",\n resets ? `, which resets at ${resets}` : \"\",\n \". Continue this task on another configured account? Leaving this unanswered waits, at no cost.\",\n ].join(\"\")\n\n pendingQuestions.set(pendingKey(sessionKey, toolCallId), {\n sourceAccount: source,\n candidates: [...candidates],\n resetsAt: input.resetsAt,\n question,\n })\n\n return {\n toolCallId,\n toolName: QUESTION_TOOL_NAME,\n input: {\n questions: [\n {\n header: \"Account limit\",\n question,\n options: [\n ...candidates.map((candidate) => ({\n label: candidate,\n description: `Run on \"${candidate}\" until ${until}. The conversation is replayed as a fresh Claude session (a session cannot resume across accounts), and any MCP server configured only in \"${source}\"'s Claude profile will be missing.`,\n })),\n {\n label: STOP_ANSWER,\n description: \"End this turn now and leave the account as it is.\",\n },\n ],\n multiple: false,\n custom: true,\n },\n ],\n },\n text: \"\",\n }\n}\n\nexport type AccountFailoverAnswer =\n | { kind: \"switch\"; target: string; sourceAccount: string; resetsAt?: number }\n | { kind: \"stop\"; reason: string }\n\nfunction isDenied(output: unknown): output is { reason?: unknown } {\n return (\n !!output &&\n typeof output === \"object\" &&\n (output as { denied?: unknown }).denied === true\n )\n}\n\nfunction classify(\n pending: PendingFailoverQuestion,\n part: any,\n): AccountFailoverAnswer {\n const output = unwrapToolOutput(part)\n if (isDenied(output)) {\n return {\n kind: \"stop\",\n reason: String((output as { reason?: unknown }).reason ?? \"question rejected\"),\n }\n }\n // A dismissed form reaches the model as a failed tool call (\"The user\n // dismissed this question\"), not as an answer that happens to be unknown.\n const outputType = part?.output?.type\n if (outputType === \"error-text\" || outputType === \"error-json\") {\n return { kind: \"stop\", reason: String(output ?? \"the form was dismissed\") }\n }\n\n const answers = collectAnswerStrings(output, pending.question)\n .map((answer) => answer.trim())\n .filter(Boolean)\n if (answers.length === 0) return { kind: \"stop\", reason: \"no answer\" }\n\n const picked = normalizeAccountName(answers[0])\n if (picked === STOP_ANSWER) {\n return { kind: \"stop\", reason: \"the operator chose to stop\" }\n }\n const target = pending.candidates.find((candidate) => candidate === picked)\n if (!target) {\n return { kind: \"stop\", reason: `unrecognised answer \"${answers[0]}\"` }\n }\n return {\n kind: \"switch\",\n target,\n sourceAccount: pending.sourceAccount,\n resetsAt: pending.resetsAt,\n }\n}\n\n/**\n * Take the operator's answer to a failover form out of this turn's prompt.\n * Anything that is not one of the offered accounts, including a dismissal and\n * unrecognised custom text, is a `stop`: the turn then ends the way the\n * rate-limit error ends it today.\n */\nexport function consumeAccountFailoverAnswer(\n sessionKey: string,\n prompt: Array<{ role: string; content?: unknown }>,\n /**\n * The form this model would offer now, for an answer whose form was asked\n * by an earlier opencode process. The pending entry lives in memory, so a\n * restart between the form and the answer lost it and the pick was replayed\n * to Claude as stray text (measured 2026-09-23). Only the newest message is\n * read with it, so an old answer further up the history never fires again.\n */\n fallback?: { sourceAccount: string; candidates: readonly string[] },\n): AccountFailoverAnswer | null {\n for (let i = prompt.length - 1; i >= 0; i--) {\n const msg = prompt[i]\n if (!Array.isArray(msg.content)) continue\n\n for (const part of msg.content as any[]) {\n if (part?.type !== \"tool-result\" || typeof part.toolCallId !== \"string\") {\n continue\n }\n const key = pendingKey(sessionKey, part.toolCallId)\n const pending = pendingQuestions.get(key)\n if (!pending) continue\n\n pendingQuestions.delete(key)\n return classify(pending, part)\n }\n }\n\n const last = prompt[prompt.length - 1]\n if (fallback && fallback.candidates.length > 0 && Array.isArray(last?.content)) {\n const orphan = (last.content as any[]).find(\n (part) =>\n part?.type === \"tool-result\" &&\n typeof part.toolCallId === \"string\" &&\n part.toolCallId.startsWith(ACCOUNT_FAILOVER_TOOL_CALL_PREFIX),\n )\n if (orphan) {\n return classify(\n {\n sourceAccount: normalizeAccountName(fallback.sourceAccount || DEFAULT_ACCOUNT),\n candidates: fallback.candidates.map((c) => normalizeAccountName(c)),\n },\n orphan,\n )\n }\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Transcript handling\n// ---------------------------------------------------------------------------\n\nexport function formatFailoverNote(input: {\n sourceAccount: string\n target: string\n resetsAt?: number\n}): string {\n const resets = describeReset(input.resetsAt)\n return `\\n${FAILOVER_MARKER} \"${input.sourceAccount}\" is out of usage, so this conversation continues on \"${\n input.target\n }\" ${\n resets ? `until ${resets}` : \"until opencode restarts\"\n }. Claude cannot resume a session across accounts, so the thread is being replayed into a fresh one.\\n`\n}\n\nexport function formatFailoverStopNote(reason: string): string {\n return `\\n${FAILOVER_MARKER} Staying on this account (${reason}). The turn ends here; the usage limit is unchanged.\\n`\n}\n\nfunction isFailoverPart(part: any): boolean {\n if (!part || typeof part.toolCallId !== \"string\") return false\n if (part.type !== \"tool-call\" && part.type !== \"tool-result\") return false\n return part.toolCallId.startsWith(ACCOUNT_FAILOVER_TOOL_CALL_PREFIX)\n}\n\n/**\n * Remove the failover dialog from a transcript: the synthetic `question`\n * tool-call and the `tool-result` carrying the answer. Claude never issued\n * that call and never saw that result, so replaying either would hand a\n * fresh session a conversation it cannot make sense of. Messages left with no\n * content at all are dropped rather than replayed empty.\n */\nexport function stripAccountFailoverParts(prompt: Prompt): Prompt {\n let changed = false\n const out = [] as unknown as Prompt\n\n for (const message of prompt) {\n const content = (message as { content?: unknown }).content\n if (!Array.isArray(content) || !content.some(isFailoverPart)) {\n out.push(message)\n continue\n }\n changed = true\n const kept = content.filter((part: any) => !isFailoverPart(part))\n if (kept.length === 0) continue\n out.push({ ...message, content: kept } as typeof message)\n }\n\n return changed ? out : prompt\n}\n\nexport function failoverContinuationText(target: string): string {\n return [\n `The Claude account this conversation was running on hit its usage limit, so it has been moved to the \"${target}\" account and you are now in a fresh Claude session.`,\n \"The conversation so far is above. Continue the task from where it stopped: do not start over, do not re-plan, and do not repeat work that is already done.\",\n \"Do not mention the account switch unless you are asked about it.\",\n ].join(\" \")\n}\n\n/**\n * The prompt to replay into the target account: the conversation with the\n * failover dialog removed, plus one user message telling the fresh session\n * what happened and to carry on.\n */\nexport function buildFailoverContinuationPrompt(\n prompt: Prompt,\n target: string,\n): Prompt {\n const stripped = stripAccountFailoverParts(prompt)\n return [\n ...stripped,\n {\n role: \"user\",\n content: [{ type: \"text\", text: failoverContinuationText(target) }],\n },\n ] as Prompt\n}\n\n/** Epoch ms a limit resets at, from whichever field the CLI filled in. */\nexport function failoverUntil(\n resetsAt: number | undefined,\n): number | undefined {\n return resetsAtToMs(resetsAt)\n}\n","import { chmod, lstat, mkdir, readlink, symlink, writeFile } from \"node:fs/promises\"\nimport path from \"node:path\"\nimport { log } from \"./logger.js\"\n\nexport const BASE_PROVIDER_ID = \"claude-code\"\nexport const DEFAULT_ACCOUNT = \"default\"\n\nconst SHARED_CAPABILITY_ITEMS = [\n \"CLAUDE.md\",\n \"settings.json\",\n \"skills\",\n \"agents\",\n \"commands\",\n \"plugins\",\n]\n\nexport function normalizeAccountName(account: string): string {\n return account\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n}\n\nexport function resolveAccounts(value: unknown): string[] | null {\n if (!Array.isArray(value)) return null\n\n const accounts = value\n .map((account) => normalizeAccountName(String(account)))\n .filter(Boolean)\n\n return Array.from(new Set([DEFAULT_ACCOUNT, ...accounts]))\n}\n\nexport function accountProviderId(account: string): string {\n return `${BASE_PROVIDER_ID}-${normalizeAccountName(account)}`\n}\n\nexport function accountDisplayName(account: string): string {\n return `Claude Code (${titleizeAccount(account)})`\n}\n\nexport function accountModelSuffix(account: string): string | undefined {\n const normalized = normalizeAccountName(account)\n return normalized === DEFAULT_ACCOUNT ? undefined : normalized\n}\n\nexport function accountConfigDir(account: string): string | undefined {\n const normalized = normalizeAccountName(account)\n\n if (!normalized || normalized === DEFAULT_ACCOUNT) return undefined\n\n return `~/.claude-${normalized}`\n}\n\n/**\n * Absolute `CLAUDE_CONFIG_DIR` for an account, or undefined for the default\n * account, which leaves the CLI on its own `~/.claude`. Used where a caller\n * needs to know what the *spawn* will read (skills, plugins) rather than to\n * set the variable itself.\n */\nexport function accountConfigDirPath(account: string): string | undefined {\n const dir = accountConfigDir(account)\n return dir ? expandHome(dir) : undefined\n}\n\nexport function expandHome(value: string): string {\n const home = process.env.HOME ?? process.env.USERPROFILE\n\n if (value === \"~\") return home ?? value\n\n if (value.startsWith(\"~/\") || value.startsWith(\"~\\\\\")) {\n return home ? path.join(home, value.slice(2)) : value\n }\n\n return value\n}\n\nexport async function ensureAccountRuntime(\n account: string,\n baseCliPath: string,\n): Promise<{ cliPath: string; configDir?: string }> {\n const configDir = accountConfigDir(account)\n\n if (!configDir) return { cliPath: baseCliPath }\n\n const expandedConfigDir = expandHome(configDir)\n await mkdir(expandedConfigDir, { recursive: true })\n\n try {\n await ensureSharedCapabilities(expandedConfigDir)\n } catch (err) {\n log.warn(\"failed to symlink shared capabilities; continuing anyway\", {\n account,\n configDir: expandedConfigDir,\n error: String(err),\n })\n }\n\n const cliPath = await writeAccountWrapper(\n normalizeAccountName(account),\n baseCliPath,\n expandedConfigDir,\n )\n\n return { cliPath, configDir: expandedConfigDir }\n}\n\nasync function ensureSharedCapabilities(targetRoot: string): Promise<void> {\n const sourceRoot = expandHome(\"~/.claude\")\n\n for (const item of SHARED_CAPABILITY_ITEMS) {\n await ensureSharedCapabilityItem(sourceRoot, targetRoot, item)\n }\n}\n\nasync function ensureSharedCapabilityItem(\n sourceRoot: string,\n targetRoot: string,\n item: string,\n): Promise<void> {\n const source = path.join(sourceRoot, item)\n const target = path.join(targetRoot, item)\n\n let sourceStat\n try {\n sourceStat = await lstat(source)\n } catch {\n return\n }\n\n try {\n const targetStat = await lstat(target)\n\n if (targetStat.isSymbolicLink()) {\n const current = await readlink(target)\n const resolvedCurrent = path.resolve(path.dirname(target), current)\n const resolvedSource = path.resolve(source)\n\n if (resolvedCurrent === resolvedSource) return\n }\n\n log.warn(\"shared Claude capability already exists; leaving untouched\", {\n item,\n target,\n source,\n })\n\n return\n } catch {\n // Missing target is expected.\n }\n\n const type = sourceStat.isDirectory()\n ? process.platform === \"win32\"\n ? \"junction\"\n : \"dir\"\n : \"file\"\n\n await symlink(source, target, type)\n}\n\nasync function writeAccountWrapper(\n account: string,\n baseCliPath: string,\n configDir: string,\n): Promise<string> {\n const cacheRoot = path.join(\n process.env.XDG_CACHE_HOME ?? expandHome(\"~/.cache\"),\n \"opencode-claude-code-plugin\",\n )\n const wrapperPath = path.join(cacheRoot, `claude-${account}`)\n const suffix = `@${account}`\n\n await mkdir(cacheRoot, { recursive: true })\n\n const script = `#!/usr/bin/env bash\nset -euo pipefail\n\nargs=()\nwhile [[ $# -gt 0 ]]; do\n if [[ \"$1\" == \"--model\" && $# -ge 2 ]]; then\n model=\"$2\"\n if [[ \"$model\" == *${shellDoubleQuote(suffix)} ]]; then\n model=\"\\${model%${shellDoubleQuote(suffix)}}\"\n fi\n args+=(\"$1\" \"$model\")\n shift 2\n else\n args+=(\"$1\")\n shift\n fi\ndone\n\nexport CLAUDE_CONFIG_DIR=${shellSingleQuote(configDir)}\n# \\${args[@]+...} keeps an empty array legal under \\`set -u\\` on bash 3.2,\n# which is the bash macOS ships and \\`/usr/bin/env bash\\` usually finds.\nexec ${shellSingleQuote(baseCliPath)} \\${args[@]+\"\\${args[@]}\"}\n`\n\n await writeFile(wrapperPath, script, \"utf8\")\n await chmod(wrapperPath, 0o755)\n\n return wrapperPath\n}\n\nfunction shellSingleQuote(value: string): string {\n return `'${value.replace(/'/g, `'\"'\"'`)}'`\n}\n\nfunction shellDoubleQuote(value: string): string {\n return value.replace(/[$`\"\\\\]/g, \"\\\\$&\")\n}\n\nfunction titleizeAccount(account: string): string {\n return normalizeAccountName(account)\n .split(\"-\")\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\" \")\n}\n","import { log } from \"./logger.js\"\nimport type { ClaudeStreamMessage } from \"./types.js\"\n\n/**\n * Claude CLI stream events the plugin used to drop on the floor.\n *\n * Every shape below was read out of the CLI's own zod schemas in the installed\n * bundle (`rg -a` over `~/.local/share/claude/versions/<v>`) on 2.1.263, not\n * guessed, but they are still parsed defensively: a future CLI may rename a\n * field, and a diagnostic that throws is worse than one that stays quiet.\n *\n * Levels follow the rule the rest of this codebase uses. `src/logger.ts` routes\n * only WARN and ERROR to stderr unconditionally, so anything the user has to\n * see without turning on debug logging is either a WARN or a `▌` line written\n * into the transcript. Everything else is INFO or NOTICE for the file log.\n */\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n}\n\nfunction str(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined\n}\n\nfunction num(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined\n}\n\n// ---------------------------------------------------------------------------\n// rate_limit_event\n// ---------------------------------------------------------------------------\n\n/**\n * `{type:\"rate_limit_event\", rate_limit_info:{...}}`, emitted whenever the\n * CLI's view of the account's limits changes. `status` is the plan window,\n * `overageStatus` is paid extra usage on top of it; either can be `rejected`\n * on its own, so both are read.\n */\nexport interface RateLimitInfo {\n status?: string\n rateLimitType?: string\n resetsAt?: number\n utilization?: number\n isUsingOverage?: boolean\n overageStatus?: string\n overageResetsAt?: number\n overageDisabledReason?: string\n}\n\nexport const RATE_LIMIT_MARKER = \"▌ **rate limit:**\"\n\n/**\n * What the operator can actually do about it. Same four levers the billing\n * note in AGENTS.md lists, which is where this wording comes from: none of\n * them is something the plugin may do on its own.\n */\nexport const RATE_LIMIT_ACTION =\n \"Enable extra usage or add credits on the account, wait for the window to reset, switch account, org or plan, or authenticate with an API key.\"\n\nconst OVERAGE_DISABLED_REASONS: Record<string, string> = {\n overage_not_provisioned: \"extra usage is not set up on this account\",\n org_level_disabled: \"extra usage is disabled for your organization\",\n org_level_disabled_until: \"extra usage is disabled for your organization for now\",\n out_of_credits: \"the account's usage credits are spent\",\n seat_tier_level_disabled: \"your seat tier does not allow extra usage\",\n member_level_disabled: \"extra usage is disabled for your member account\",\n seat_tier_zero_credit_limit: \"your seat tier has a zero credit limit\",\n group_zero_credit_limit: \"your group has a zero credit limit\",\n member_zero_credit_limit: \"your member account has a zero credit limit\",\n org_service_level_disabled: \"your organization's service level does not include extra usage\",\n no_limits_configured: \"no usage limits are configured for this account\",\n fetch_error: \"the CLI could not read the account's usage limits\",\n}\n\nconst RATE_LIMIT_WINDOWS: Record<string, string> = {\n five_hour: \"the 5-hour window\",\n seven_day: \"the 7-day window\",\n seven_day_opus: \"the 7-day Opus window\",\n seven_day_sonnet: \"the 7-day Sonnet window\",\n seven_day_overage_included: \"the 7-day extra-usage window\",\n overage: \"extra usage\",\n}\n\nexport function parseRateLimitEvent(msg: ClaudeStreamMessage): RateLimitInfo | null {\n if (msg.type !== \"rate_limit_event\") return null\n const info = (msg as { rate_limit_info?: unknown }).rate_limit_info\n if (!isRecord(info)) return null\n return {\n status: str(info.status),\n rateLimitType: str(info.rateLimitType),\n resetsAt: num(info.resetsAt),\n utilization: num(info.utilization),\n isUsingOverage: info.isUsingOverage === true,\n overageStatus: str(info.overageStatus),\n overageResetsAt: num(info.overageResetsAt),\n overageDisabledReason: str(info.overageDisabledReason),\n }\n}\n\n/** The CLI sends unix seconds; tolerate milliseconds rather than mean 1970. */\nexport function resetsAtToMs(resetsAt: number | undefined): number | undefined {\n if (resetsAt === undefined || !Number.isFinite(resetsAt)) return undefined\n return resetsAt < 1e12 ? resetsAt * 1000 : resetsAt\n}\n\nexport function formatResetsAt(resetsAt: number | undefined): string | undefined {\n const ms = resetsAtToMs(resetsAt)\n if (ms === undefined) return undefined\n const date = new Date(ms)\n return Number.isNaN(date.getTime()) ? undefined : date.toISOString()\n}\n\n/** Dedup identity: one warning per (window, overage status, reason) per process. */\nexport function rateLimitKey(info: RateLimitInfo): string {\n return [\n info.status ?? \"?\",\n info.rateLimitType ?? \"?\",\n info.overageStatus ?? \"?\",\n info.overageDisabledReason ?? \"?\",\n ].join(\"|\")\n}\n\n/**\n * Whether the CLI refused this request. `status` is the request's own verdict\n * and wins whenever it is present: `overageStatus: \"rejected\"` on its own only\n * says paid extra usage is unavailable, which is the normal steady state for\n * an org with extra usage disabled. Measured on CLI 2.1.280 (2026-09-23): an\n * event of `{status: \"allowed\", overageStatus: \"rejected\",\n * overageDisabledReason: \"org_level_disabled\"}` arrived on a turn that was\n * served and answered. Treating that as a rejection printed a false \"rejected\n * this request\" line and, worse, set account failover off on a successful turn.\n * An overage rejection still counts when no status came with it.\n */\nexport function isRateLimitRejected(info: RateLimitInfo): boolean {\n if (info.status === \"rejected\") return true\n if (info.status === \"allowed\" || info.status === \"allowed_warning\") return false\n return info.overageStatus === \"rejected\"\n}\n\nexport interface RateLimitReport {\n key: string\n level: \"warn\" | \"notice\" | \"info\"\n message: string\n /** Text to put in the transcript, or null when this is log-only. */\n transcript: string | null\n}\n\n/**\n * A rejection is the only state the user has to act on, so it is the only one\n * that gets a WARN and a transcript line. A warning state is a NOTICE: it is\n * real but not yet blocking, and a per-turn TUI bubble for \"you are at 82%\"\n * would train people to ignore the blocking one.\n */\nexport function describeRateLimit(info: RateLimitInfo): RateLimitReport | null {\n if (!info.status && !info.overageStatus) return null\n const window = info.rateLimitType ? RATE_LIMIT_WINDOWS[info.rateLimitType] ?? info.rateLimitType : undefined\n const resets = formatResetsAt(info.resetsAt)\n const overageResets = formatResetsAt(info.overageResetsAt)\n const key = rateLimitKey(info)\n\n if (isRateLimitRejected(info)) {\n const parts: string[] = []\n parts.push(\n info.status === \"rejected\"\n ? `Claude Code rejected this request: you are out of usage${window ? ` in ${window}` : \"\"}.`\n : \"Claude Code rejected this request: paid extra usage is not available on this account.\",\n )\n const reason = info.overageDisabledReason\n ? OVERAGE_DISABLED_REASONS[info.overageDisabledReason] ?? info.overageDisabledReason\n : undefined\n if (reason) parts.push(`Extra usage is unavailable because ${reason}.`)\n const resetAt = resets ?? overageResets\n if (resetAt) parts.push(`Resets at ${resetAt}.`)\n parts.push(RATE_LIMIT_ACTION)\n const message = parts.join(\" \")\n return { key, level: \"warn\", message, transcript: `\\n${RATE_LIMIT_MARKER} ${message}\\n` }\n }\n\n if (info.status === \"allowed_warning\" || info.overageStatus === \"allowed_warning\") {\n const used =\n info.utilization === undefined ? \"\" : ` (${Math.round(info.utilization * 100)}% used)`\n return {\n key,\n level: \"notice\",\n message: `Approaching the usage limit${window ? ` for ${window}` : \"\"}${used}${\n resets ? `, resets at ${resets}` : \"\"\n }.`,\n transcript: null,\n }\n }\n\n return {\n key,\n level: \"info\",\n message: `usage limits updated${window ? ` for ${window}` : \"\"}`,\n transcript: null,\n }\n}\n\nconst reportedRateLimits = new Set<string>()\n\n/** Test-only. */\nexport function _resetRateLimitReports(): void {\n reportedRateLimits.clear()\n}\n\n/**\n * Logs the event and returns the transcript line to enqueue, if any. Deduped\n * per identity per process: the CLI re-emits the same rejection on every\n * subsequent request, and a repeated WARN would bury the first one.\n */\nexport function reportRateLimitEvent(msg: ClaudeStreamMessage): string | null {\n const info = parseRateLimitEvent(msg)\n if (!info) return null\n const report = describeRateLimit(info)\n if (!report) return null\n const data: Record<string, unknown> = { ...info }\n if (reportedRateLimits.has(report.key)) {\n log.debug(report.message, data)\n return null\n }\n reportedRateLimits.add(report.key)\n log[report.level](report.message, data)\n return report.transcript\n}\n\n// ---------------------------------------------------------------------------\n// system / init\n// ---------------------------------------------------------------------------\n\nexport interface SystemInitInfo {\n apiKeySource?: string\n permissionMode?: string\n model?: string\n cliVersion?: string\n toolCount: number\n mcpServers: Array<{ name: string; status: string }>\n}\n\n/**\n * Credential sources that mean the CLI authenticated with an API key rather\n * than the logged-in subscription, so the turn bills pay-as-you-go against the\n * Console account and never touches the plan's Agent SDK credit. `oauth` is\n * the subscription and `none` is no credential at all; everything else in the\n * CLI's enum is a key from some scope.\n */\nexport const API_KEY_SOURCES = new Set([\n \"ANTHROPIC_API_KEY\",\n \"apiKeyHelper\",\n \"/login managed key\",\n \"user\",\n \"project\",\n \"org\",\n \"temporary\",\n])\n\nexport function parseSystemInit(msg: ClaudeStreamMessage): SystemInitInfo | null {\n if (msg.type !== \"system\" || msg.subtype !== \"init\") return null\n const raw = msg as unknown as Record<string, unknown>\n const servers: Array<{ name: string; status: string }> = []\n if (Array.isArray(raw.mcp_servers)) {\n for (const entry of raw.mcp_servers) {\n if (!isRecord(entry)) continue\n servers.push({ name: str(entry.name) ?? \"unknown\", status: str(entry.status) ?? \"unknown\" })\n }\n }\n return {\n apiKeySource: str(raw.apiKeySource),\n permissionMode: str(raw.permissionMode),\n model: str(raw.model),\n cliVersion: str(raw.claude_code_version),\n toolCount: Array.isArray(raw.tools) ? raw.tools.length : 0,\n mcpServers: servers,\n }\n}\n\n/**\n * The warning text for an API-key session, or null when there is nothing to\n * say. Kept pure and separate from the dedup so both halves are testable.\n *\n * `ignoreAnthropicApiKey` strips the env vars from the spawn, so a key still\n * in effect after that came from the CLI's own settings and the option is not\n * the fix; saying so is the whole point of reading this field rather than\n * `process.env`, which only sees one of the two ways a key gets in.\n */\nexport function apiKeySourceWarning(\n apiKeySource: string | undefined,\n ignoreAnthropicApiKey: boolean | undefined,\n): string | null {\n if (!apiKeySource || !API_KEY_SOURCES.has(apiKeySource)) return null\n const base = `Claude Code authenticated with an API key (apiKeySource: ${apiKeySource}), so these turns bill as pay-as-you-go API usage instead of your subscription's Agent SDK credit.`\n return ignoreAnthropicApiKey\n ? `${base} \\`ignoreAnthropicApiKey\\` is already on, so the key is not coming from the environment: check the CLI's own settings (\\`claude config\\`) or an \\`apiKeyHelper\\`.`\n : `${base} Set the provider option \\`ignoreAnthropicApiKey: true\\` to strip the key from spawns and fall back to the stored subscription auth.`\n}\n\nconst warnedApiKeySources = new Set<string>()\nconst warnedMcpFailures = new Set<string>()\n\n/** Test-only. */\nexport function _resetSystemInitReports(): void {\n warnedApiKeySources.clear()\n warnedMcpFailures.clear()\n}\n\n/**\n * Log the CLI's own view of the session it just started, and warn about the\n * two things in it a user has to act on: a credential that changes who gets\n * billed, and an MCP server that did not come up (the model simply will not\n * have those tools, with no other sign of it).\n */\nexport function reportSystemInit(\n msg: ClaudeStreamMessage,\n options: { ignoreAnthropicApiKey?: boolean } = {},\n): void {\n const info = parseSystemInit(msg)\n if (!info) return\n log.info(\"claude session init\", {\n apiKeySource: info.apiKeySource ?? null,\n permissionMode: info.permissionMode ?? null,\n model: info.model ?? null,\n cliVersion: info.cliVersion ?? null,\n tools: info.toolCount,\n mcpServers: info.mcpServers,\n })\n\n for (const server of info.mcpServers) {\n if (server.status === \"connected\") continue\n const key = `${server.name}:${server.status}`\n const message = `MCP server \"${server.name}\" is ${server.status} in Claude Code; its tools are not available to the model this session.`\n if (warnedMcpFailures.has(key)) {\n log.debug(message, { server: server.name, status: server.status })\n continue\n }\n warnedMcpFailures.add(key)\n log.warn(message, { server: server.name, status: server.status })\n }\n\n const apiKeyMessage = apiKeySourceWarning(info.apiKeySource, options.ignoreAnthropicApiKey)\n if (!apiKeyMessage) return\n const key = `${info.apiKeySource}:${options.ignoreAnthropicApiKey ? \"ignored\" : \"passed\"}`\n if (warnedApiKeySources.has(key)) {\n log.debug(apiKeyMessage, { apiKeySource: info.apiKeySource })\n return\n }\n warnedApiKeySources.add(key)\n log.warn(apiKeyMessage, { apiKeySource: info.apiKeySource })\n}\n\n// ---------------------------------------------------------------------------\n// system / compact_boundary\n// ---------------------------------------------------------------------------\n\nexport const COMPACT_BOUNDARY_MARKER = \"▌ **context compacted:**\"\n\nexport interface CompactBoundary {\n trigger: string\n preTokens?: number\n postTokens?: number\n}\n\n/**\n * The CLI compacted its own context mid-conversation. Nothing in opencode\n * shows this today, so a conversation can silently lose everything before the\n * boundary and the next answer just looks forgetful.\n *\n * Field name confirmed on CLI 2.1.263: the stream schema emits\n * `compact_metadata`, while the CLI's own transcript reader uses\n * `compactMetadata`. Both are read, because it costs one line and the two\n * spellings genuinely coexist inside the binary.\n */\nexport function parseCompactBoundary(msg: ClaudeStreamMessage): CompactBoundary | null {\n if (msg.type !== \"system\" || msg.subtype !== \"compact_boundary\") return null\n const raw = msg as unknown as Record<string, unknown>\n const meta = isRecord(raw.compact_metadata)\n ? raw.compact_metadata\n : isRecord(raw.compactMetadata)\n ? raw.compactMetadata\n : undefined\n return {\n trigger: str(meta?.trigger) ?? \"unknown\",\n preTokens: num(meta?.pre_tokens) ?? num(meta?.preTokens),\n postTokens: num(meta?.post_tokens) ?? num(meta?.postTokens),\n }\n}\n\nexport function formatCompactBoundaryNote(boundary: CompactBoundary): string {\n const how = boundary.trigger === \"auto\" ? \"on its own\" : `on a ${boundary.trigger} request`\n const sizes =\n boundary.preTokens !== undefined && boundary.postTokens !== undefined\n ? ` (${boundary.preTokens.toLocaleString(\"en-US\")} tokens to ${boundary.postTokens.toLocaleString(\"en-US\")})`\n : \"\"\n return `\\n${COMPACT_BOUNDARY_MARKER} Claude Code compacted its own context ${how}${sizes}. Earlier detail in this conversation is now a summary.\\n`\n}\n\n/** Logs the boundary and returns the transcript note, or null when not one. */\nexport function reportCompactBoundary(msg: ClaudeStreamMessage): string | null {\n const boundary = parseCompactBoundary(msg)\n if (!boundary) return null\n log.notice(\"claude code compacted its own context\", {\n trigger: boundary.trigger,\n preTokens: boundary.preTokens ?? null,\n postTokens: boundary.postTokens ?? null,\n })\n return formatCompactBoundaryNote(boundary)\n}\n\n// ---------------------------------------------------------------------------\n// conversation_reset\n// ---------------------------------------------------------------------------\n\nexport const CONVERSATION_RESET_MARKER = \"▌ **claude code reset:**\"\n\nexport interface ConversationReset {\n newConversationId: string\n /** The session the reset ended; the next `system/init` carries the new one. */\n previousSessionId?: string\n}\n\n/**\n * Claude Code threw its conversation away and started a new one. Schema from\n * Claude Code 2.1.280: `{type: \"conversation_reset\", new_conversation_id,\n * uuid, session_id}`, emitted by `/clear`, plan-mode exit and fresh-session\n * flows. Measured the same day by driving the real CLI: an ordinary turn emits\n * none, and `/clear` emits one carrying the OLD `session_id`, followed by a\n * `system/init` with a new session id that differs from `new_conversation_id`,\n * after which Claude could not recall the conversation. A frame without a\n * string `new_conversation_id` is ignored, as the CLI's own adapter drops it.\n */\nexport function parseConversationReset(msg: ClaudeStreamMessage): ConversationReset | null {\n if (msg.type !== \"conversation_reset\") return null\n const newConversationId = str(msg.new_conversation_id)\n if (!newConversationId) return null\n return { newConversationId, previousSessionId: str(msg.session_id) }\n}\n\n/**\n * Deliberately a note and not a history replay: every known trigger is a\n * clear the user or Claude Code asked for, and replaying opencode's transcript\n * on the next message would silently undo it.\n */\nexport function formatConversationResetNote(): string {\n return `\\n${CONVERSATION_RESET_MARKER} Claude Code cleared its conversation, so from here on Claude does not see the earlier messages this chat still shows. Start a new opencode session for a clean slate, or restate what matters.\\n`\n}\n\n/** Logs the reset and returns the transcript note, or null when not one. */\nexport function reportConversationReset(msg: ClaudeStreamMessage): string | null {\n const reset = parseConversationReset(msg)\n if (!reset) return null\n log.notice(\"claude code reset its conversation\", {\n newConversationId: reset.newConversationId,\n previousSessionId: reset.previousSessionId ?? null,\n })\n return formatConversationResetNote()\n}\n\n// ---------------------------------------------------------------------------\n// result subtype\n// ---------------------------------------------------------------------------\n\nexport const RESULT_ERROR_MARKER = \"▌ **claude code error:**\"\n\nconst RESULT_SUBTYPES: Record<string, string> = {\n error_max_turns: \"it hit its internal turn limit before finishing\",\n error_during_execution: \"it failed while running the turn\",\n error_max_budget_usd: \"it hit the configured spend limit for the turn\",\n error_max_structured_output_retries: \"it could not produce valid structured output\",\n}\n\n/**\n * A `result` whose subtype is not `success` is a failed turn, and until now it\n * finished as a clean `stop`: opencode recorded it as a normal reply and the\n * only trace of the subtype was a debug log line.\n *\n * This is the with-result case only. A CLI that dies without emitting a\n * `result` at all is a different failure, handled elsewhere.\n */\nexport function describeResultFailure(msg: ClaudeStreamMessage): string | null {\n if (msg.type !== \"result\") return null\n const subtype = msg.subtype\n if (!subtype || subtype === \"success\") return null\n const explanation = RESULT_SUBTYPES[subtype]\n return explanation\n ? `Claude Code ended the turn with \\`${subtype}\\`: ${explanation}.`\n : `Claude Code ended the turn with \\`${subtype}\\`.`\n}\n\nexport function formatResultFailureNote(message: string): string {\n return `\\n${RESULT_ERROR_MARKER} ${message}\\n`\n}\n\n// ---------------------------------------------------------------------------\n// stdout silence after content\n// ---------------------------------------------------------------------------\n\nexport const STREAM_TIMEOUT_MARKER = \"▌ **stream timeout:**\"\n\n/**\n * The inactivity watchdog in `doStream` closes a turn whose CLI produced\n * output and then stopped talking without sending a `result`. That decision\n * was log-only, so the operator saw a reply that simply stopped mid-thought\n * with nothing saying why. This is the note that says it.\n */\nexport function formatStreamTimeoutNote(silenceMs: number): string {\n const seconds = Math.max(1, Math.round(silenceMs / 1000))\n return (\n `\\n${STREAM_TIMEOUT_MARKER} The Claude Code CLI produced output and then went ` +\n `silent for ${seconds}s without finishing the turn, so it was closed without a ` +\n \"result. The answer above may be incomplete.\\n\"\n )\n}\n","export const QUESTION_TOOL_NAME = \"question\"\n\nexport const APPROVED_EXIT_PLAN_MODE_MESSAGE =\n \"User has approved your plan. You can now start coding. Start with updating your todo list if applicable.\"\n\nconst REJECTED_EXIT_PLAN_MODE_PREFIX =\n \"The user doesn't want to proceed with this tool use. The tool use was rejected. To tell you how to proceed, the user said:\"\n\nconst PLAN_MODE_APPROVAL_QUESTION = \"Do you want to proceed with this plan?\"\nconst OPENCODE_QUESTION_RESULT_PREFIX = \"User has answered your questions: \"\nconst OPENCODE_QUESTION_RESULT_SUFFIX =\n \". You can now continue with the user's answers in mind.\"\n/** What opencode writes for a question the operator left blank. */\nconst OPENCODE_UNANSWERED = \"Unanswered\"\n\nconst KEY_SEPARATOR = \"\\u0000\"\n\n/**\n * A synthetic call to opencode's native `question` tool, emitted so the turn\n * ends on `tool-calls` and the operator's answer arrives on the next\n * `doStream` as a `tool-result` with the same id. Shared with the account\n * failover form (`src/account-failover.ts`), which uses the identical\n * mechanism for a different question.\n */\nexport interface QuestionToolCall {\n toolCallId: string\n toolName: typeof QUESTION_TOOL_NAME\n input: {\n questions: Array<{\n header: string\n question: string\n options: Array<{ label: string; description: string }>\n multiple: boolean\n custom: boolean\n }>\n }\n text: string\n}\n\nexport type ExitPlanModeQuestionCall = QuestionToolCall\n\n/**\n * Whether to bridge `ExitPlanMode` into opencode's native `question` tool\n * this turn.\n *\n * Opt-in (`planModeQuestion`) because the bridge is dormant on the headless\n * transport: `--print` offers the model no `ExitPlanMode` tool at all\n * (measured on CLI 2.1.258), so there is nothing to key on and the model asks\n * for approval in prose instead. opencode's question form itself is fine; the\n * older claim here that it never rendered (anomalyco/opencode#36604) was\n * retracted on 2026-09-06, and both the native form and the `question` proxy\n * were verified round-tripping. Gated on the live registry because emitting a\n * `question` tool-call on a build without that entry renders `⚙ invalid` and\n * wedges the turn. Never bridged during compaction: that turn is text-only and\n * its answer would have nowhere to go.\n */\nexport function isPlanModeQuestionActive(input: {\n configured: boolean | undefined\n opencodeHasQuestion: boolean\n compactionMode: boolean\n}): boolean {\n if (input.compactionMode) return false\n if (input.configured !== true) return false\n return input.opencodeHasQuestion\n}\n\nconst pendingQuestions = new Map<string, string>()\n\nfunction pendingKey(sessionKey: string, questionToolCallId: string): string {\n return `${sessionKey}${KEY_SEPARATOR}${questionToolCallId}`\n}\n\nexport function clearExitPlanModeQuestions(sessionKey: string): void {\n const prefix = `${sessionKey}${KEY_SEPARATOR}`\n for (const key of pendingQuestions.keys()) {\n if (key.startsWith(prefix)) pendingQuestions.delete(key)\n }\n}\n\nexport function hasExitPlanModeQuestions(sessionKey: string): boolean {\n const prefix = `${sessionKey}${KEY_SEPARATOR}`\n return [...pendingQuestions.keys()].some((key) => key.startsWith(prefix))\n}\n\nexport function createExitPlanModeQuestionCall(\n sessionKey: string,\n exitPlanModeToolUseId: string,\n plan: string,\n questionToolCallId = `exit_plan_question_${exitPlanModeToolUseId}`,\n): ExitPlanModeQuestionCall {\n pendingQuestions.set(pendingKey(sessionKey, questionToolCallId), exitPlanModeToolUseId)\n\n return {\n toolCallId: questionToolCallId,\n toolName: QUESTION_TOOL_NAME,\n input: {\n questions: [\n {\n header: \"Plan approval\",\n question: PLAN_MODE_APPROVAL_QUESTION,\n options: [\n { label: \"yes\", description: \"\" },\n { label: \"no\", description: \"\" },\n ],\n multiple: false,\n custom: true,\n },\n ],\n },\n text: plan ? `\\n\\n${plan}\\n` : \"\\n\\n\",\n }\n}\n\nfunction buildToolResultMessage(input: {\n toolUseId: string\n approved: boolean\n feedback: string\n}): string {\n return JSON.stringify({\n type: \"user\",\n message: {\n role: \"user\",\n content: [\n input.approved\n ? {\n type: \"tool_result\",\n tool_use_id: input.toolUseId,\n content: APPROVED_EXIT_PLAN_MODE_MESSAGE,\n }\n : {\n type: \"tool_result\",\n tool_use_id: input.toolUseId,\n content: `${REJECTED_EXIT_PLAN_MODE_PREFIX}\\n${input.feedback || \"no\"}`,\n is_error: true,\n },\n ],\n },\n })\n}\n\nfunction tryParseJson(text: string): unknown {\n try {\n return JSON.parse(text)\n } catch {\n return text\n }\n}\n\n/**\n * Pull the operator's answer out of whatever shape opencode wrapped the\n * `question` tool result in. Exported because the account failover form reads\n * the same results through the same tool; a second copy of this would drift.\n */\nexport function unwrapToolOutput(part: any): unknown {\n const output = part?.output ?? part?.result\n if (typeof output === \"string\") return tryParseJson(output)\n if (!output || typeof output !== \"object\") return output\n\n switch (output.type) {\n case \"json\":\n case \"error-json\":\n return output.value\n case \"text\":\n case \"error-text\":\n return tryParseJson(String(output.value ?? \"\"))\n case \"execution-denied\":\n return {\n denied: true,\n reason: String(output.reason ?? \"question rejected\"),\n }\n case \"content\":\n return Array.isArray(output.value)\n ? output.value\n .map((item: any) => {\n if (item?.type === \"text\") return item.text\n return JSON.stringify(item)\n })\n .join(\"\\n\")\n : output.value\n default:\n return output\n }\n}\n\n/**\n * opencode's `question` tool does not return the answers, it returns one\n * sentence: `User has answered your questions: \"<question>\"=\"<answer>\". You\n * can now continue with the user's answers in mind.` (read out of the 1.18.32\n * binary; several answers to one question are joined with \", \", and a blank\n * one is written as `Unanswered`). The question is ours, so it is matched\n * whole: the account-failover question carries quotes of its own, and a split\n * on quotes cuts the answer in the wrong place. This used to recognise the\n * plan-approval question only, so every failover pick arrived as the whole\n * sentence and was refused as unrecognised. Without a known question, the last\n * `\"=\"` is the split, which is right for any single-question form.\n */\nfunction unwrapOpencodeQuestionResult(value: string, question?: string): string {\n if (\n !value.startsWith(OPENCODE_QUESTION_RESULT_PREFIX) ||\n !value.endsWith(OPENCODE_QUESTION_RESULT_SUFFIX)\n ) {\n return value\n }\n const body = value.slice(\n OPENCODE_QUESTION_RESULT_PREFIX.length,\n value.length - OPENCODE_QUESTION_RESULT_SUFFIX.length,\n )\n if (!body.startsWith('\"') || !body.endsWith('\"')) return value\n\n let answer: string | undefined\n const head = question === undefined ? undefined : `\"${question}\"=\"`\n if (head && body.startsWith(head) && body.length > head.length) {\n answer = body.slice(head.length, -1)\n } else {\n const split = body.lastIndexOf('\"=\"')\n if (split > 0) answer = body.slice(split + 3, -1)\n }\n if (answer === undefined) return value\n return answer === OPENCODE_UNANSWERED ? \"\" : answer\n}\n\n/**\n * Flatten an unwrapped `question` result into the answer strings it holds.\n * `question` is the text the form asked, when the caller knows it; see\n * `unwrapOpencodeQuestionResult` for why it matters.\n */\nexport function collectAnswerStrings(value: unknown, question?: string): string[] {\n if (typeof value === \"string\") return [unwrapOpencodeQuestionResult(value, question)]\n if (Array.isArray(value)) return value.flatMap((item) => collectAnswerStrings(item, question))\n if (!value || typeof value !== \"object\") return []\n\n const obj = value as Record<string, unknown>\n if (obj.denied === true) return [String(obj.reason ?? \"question rejected\")]\n\n for (const key of [\"answers\", \"answer\", \"selected\", \"selection\", \"value\"]) {\n if (key in obj) return collectAnswerStrings(obj[key], question)\n }\n\n return []\n}\n\nfunction classifyQuestionResult(part: any): { approved: boolean; feedback: string } {\n const output = unwrapToolOutput(part)\n const answers = collectAnswerStrings(output, PLAN_MODE_APPROVAL_QUESTION)\n .map((answer) => answer.trim())\n .filter(Boolean)\n\n if (answers.length === 1 && answers[0].toLowerCase() === \"yes\") {\n return { approved: true, feedback: \"\" }\n }\n\n return {\n approved: false,\n feedback: answers.length > 0 ? answers.join(\"\\n\") : \"no\",\n }\n}\n\nexport function consumeExitPlanModeQuestionResult(\n sessionKey: string,\n prompt: Array<{ role: string; content?: unknown }>,\n): string | null {\n for (let i = prompt.length - 1; i >= 0; i--) {\n const msg = prompt[i]\n if (!Array.isArray(msg.content)) continue\n\n for (const part of msg.content as any[]) {\n if (part?.type !== \"tool-result\" || typeof part.toolCallId !== \"string\") {\n continue\n }\n\n const key = pendingKey(sessionKey, part.toolCallId)\n const exitPlanModeToolUseId = pendingQuestions.get(key)\n if (!exitPlanModeToolUseId) continue\n\n pendingQuestions.delete(key)\n const result = classifyQuestionResult(part)\n return buildToolResultMessage({\n toolUseId: exitPlanModeToolUseId,\n approved: result.approved,\n feedback: result.feedback,\n })\n }\n }\n\n return null\n}\n","import { execFile } from \"node:child_process\"\nimport { promisify } from \"node:util\"\nimport { log } from \"./logger.js\"\n\nconst execFileAsync = promisify(execFile)\n\nexport interface CliVersion {\n major: number\n minor: number\n patch: number\n raw: string\n}\n\nconst cache = new Map<string, Promise<CliVersion | null>>()\n\n/**\n * Env vars set on every `claude` child so the binary we detected stays the\n * binary we run.\n *\n * `detectCliVersion` resolves once per cliPath and caches that answer for the\n * life of the opencode process, and several flags are gated on it:\n * `--thinking-display summarized`, `--plugin-dir`, and fast mode via\n * `--settings`. If the CLI autoupdates underneath a long-running opencode the\n * cached version stops describing the binary actually being spawned, so a gated\n * flag can be passed to a CLI that rejects it or withheld from one that\n * supports it. A binary swapped mid-session is a plain correctness hazard\n * besides.\n *\n * Both names were read out of the Claude Code 2.1.263 bundle rather than\n * assumed (`rg -a` over the Mach-O, the technique AGENTS.md records for the CLI\n * stream events). `DISABLE_AUTOUPDATER` is read as an update blocker, and\n * `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` both suppresses non-essential\n * network traffic and counts as a second, independent update blocker.\n * Anthropic's own runner sets `DISABLE_AUTOUPDATER: \"1\"` on the children it\n * spawns, which is the same use we are putting it to here.\n */\nexport const CLI_HYGIENE_ENV_VARS = [\n \"DISABLE_AUTOUPDATER\",\n \"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC\",\n] as const\n\n/**\n * The hygiene vars that are missing from `inherited`, each set to \"1\".\n *\n * Only ever fills a gap: a var the user exported themselves is left exactly as\n * they set it, including an empty string, which both vars read as off. That is\n * the same rule the thinking vars follow in `claudeSpawnEnv`, and it is the\n * escape hatch for anyone who deliberately wants the autoupdater, so this needs\n * no provider option of its own.\n */\nexport function cliHygieneEnv(\n inherited: Record<string, string | undefined> = process.env,\n): Record<string, string> {\n const filled: Record<string, string> = {}\n for (const name of CLI_HYGIENE_ENV_VARS) {\n if (inherited[name] === undefined) filled[name] = \"1\"\n }\n return filled\n}\n\n/**\n * Run `claude --version` once per cliPath and parse the leading semver.\n * Returns null on any failure (binary missing, unparseable output, etc.)\n * so callers can fall back to the most conservative flag set.\n */\nexport function detectCliVersion(cliPath: string): Promise<CliVersion | null> {\n const cached = cache.get(cliPath)\n if (cached) return cached\n const promise = (async (): Promise<CliVersion | null> => {\n try {\n const { stdout } = await execFileAsync(cliPath, [\"--version\"], {\n timeout: 5000,\n })\n const match = /(\\d+)\\.(\\d+)\\.(\\d+)/.exec(stdout.trim())\n if (!match) {\n log.warn(\"claude --version output unparseable\", { stdout: stdout.trim() })\n return null\n }\n const v: CliVersion = {\n major: Number(match[1]),\n minor: Number(match[2]),\n patch: Number(match[3]),\n raw: stdout.trim(),\n }\n log.info(\"detected claude cli version\", { cliPath, version: v.raw })\n if (!cliSupportsThinkingDisplay(v)) {\n log.notice(\n \"claude cli < 2.1.142 detected; Opus 4.7 thinking summaries unavailable. Run `npm i -g @anthropic-ai/claude-code` to upgrade.\",\n { version: v.raw },\n )\n }\n return v\n } catch (err) {\n log.warn(\"failed to detect claude cli version\", {\n cliPath,\n error: err instanceof Error ? err.message : String(err),\n })\n return null\n }\n })()\n cache.set(cliPath, promise)\n return promise\n}\n\nfunction gte(v: CliVersion, target: { major: number; minor: number; patch: number }): boolean {\n if (v.major !== target.major) return v.major > target.major\n if (v.minor !== target.minor) return v.minor > target.minor\n return v.patch >= target.patch\n}\n\n/**\n * `--thinking-display` was introduced in Claude Code 2.1.142 alongside\n * Opus 4.7's \"omitted by default\" thinking behavior. Older CLIs reject\n * the flag with a parse error, so we gate it. Unknown version → return\n * false so we don't risk crashing the spawn.\n */\nexport function cliSupportsThinkingDisplay(v: CliVersion | null): boolean {\n if (!v) return false\n return gte(v, { major: 2, minor: 1, patch: 142 })\n}\n\n/**\n * Fast mode's headless opt-in. In print mode the CLI reports\n * `fast_mode_disabled_reason: \"sdk_opt_in_required\"` unless the *flag* settings\n * layer carries `fastMode: true`, which only `--settings` populates (there is\n * no `--fast` flag, and no fast-mode model name the CLI still accepts).\n *\n * 2.1.220 is the floor because it is the oldest binary the opt-in path was\n * confirmed present in, not because 2.1.219 is known to lack it. An unknown\n * settings key is ignored rather than fatal, so the downside of gating too\n * high is only that fast mode stays off.\n */\nexport function cliSupportsFastMode(v: CliVersion | null): boolean {\n if (!v) return false\n return gte(v, { major: 2, minor: 1, patch: 220 })\n}\n\n/** 2.1.258 is the oldest verified side_question control protocol, not its introduction date. */\nexport function cliSupportsSideQuestion(v: CliVersion | null): boolean {\n if (!v) return false\n return gte(v, { major: 2, minor: 1, patch: 258 })\n}\n\n/**\n * `--thinking` has been part of Claude Code's CLI since the 2.x line.\n * We require a detected 2.0.0+ before passing it; unknown version → skip\n * to avoid crashing a pre-flag binary. Anyone on the 1.x line should\n * upgrade.\n */\nexport function cliSupportsThinking(v: CliVersion | null): boolean {\n if (!v) return false\n return gte(v, { major: 2, minor: 0, patch: 0 })\n}\n\n/** For tests. */\nconst flagSupport = new Map<string, Promise<boolean>>()\n\n/**\n * Probe whether the binary's own `--help` mentions a flag. For flags with no\n * published version marker (`--plugin-dir`), where an invented semver\n * threshold would be a guess. One `--help` spawn per cliPath+flag, cached for\n * the process lifetime. Any failure is false, so the caller skips the flag\n * rather than risking a parse error on spawn. (From @broskees' 68ed142.)\n */\nexport function detectCliSupportsFlag(cliPath: string, flag: string): Promise<boolean> {\n const key = `${cliPath}\\x00${flag}`\n const cached = flagSupport.get(key)\n if (cached) return cached\n const promise = (async (): Promise<boolean> => {\n try {\n const execution = execFileAsync(cliPath, [\"--help\"], {\n timeout: 5000,\n killSignal: \"SIGKILL\",\n maxBuffer: 4 * 1024 * 1024,\n })\n // A wrapper may wait for stdin EOF even when asked for help.\n execution.child.stdin?.end()\n const { stdout } = await execution\n return stdout.includes(flag)\n } catch (err) {\n log.warn(\"failed to probe claude cli flag support\", {\n cliPath,\n flag,\n error: err instanceof Error ? err.message : String(err),\n })\n return false\n }\n })()\n flagSupport.set(key, promise)\n return promise\n}\n\nexport function _clearCache(): void {\n flagSupport.clear()\n cache.clear()\n}\n","import { spawn, type ChildProcess } from \"node:child_process\"\nimport { createInterface } from \"node:readline\"\nimport { randomUUID } from \"node:crypto\"\nimport { EventEmitter } from \"node:events\"\nimport { unlink } from \"node:fs/promises\"\nimport { log } from \"./logger.js\"\nimport { SERVER_CLOSED_MESSAGE, type ProxyMcpServer, type ProxyToolResult } from \"./proxy-mcp.js\"\nimport {\n getPendingProxyCalls,\n rejectAllPendingProxyCallsForSession,\n type PendingProxyCall,\n} from \"./proxy-broker.js\"\nimport { clearLedger } from \"./todo-ledger.js\"\nimport { clearExitPlanModeQuestions, hasExitPlanModeQuestions } from \"./plan-mode-question.js\"\nimport { clearAccountFailoverQuestions } from \"./account-failover.js\"\nimport { clearCompression } from \"./compression-store.js\"\nimport {\n cliHygieneEnv,\n cliSupportsFastMode,\n cliSupportsThinking,\n cliSupportsThinkingDisplay,\n type CliVersion,\n} from \"./cli-version.js\"\nimport type { ReasoningEffort } from \"./types.js\"\nimport { dispatchSideQuestionResponse, isSideQuestionPending } from \"./side-question.js\"\n\nexport interface ActiveProcess {\n proc: ChildProcess\n lineEmitter: EventEmitter\n proxyServer?: ProxyMcpServer | null\n /**\n * Hash of the bridged opencode MCP config the process was spawned with.\n * `null` when the bridge produced nothing (no MCP servers). `undefined`\n * when the bridge was disabled. Used to detect mid-session config drift\n * and force a respawn.\n */\n mcpHash?: string | null\n /** Temp file holding `--append-system-prompt-file` content; unlinked on exit. */\n systemPromptFile?: string\n /** Effort the process was spawned with, so a respawn keeps it. */\n effort?: ReasoningEffort\n /** When the child was spawned, so `/claude-code-doctor` can report its age. */\n startedAt?: number\n /**\n * The binary this child was spawned with. Account failover compares it\n * against the path the current turn resolves to: a difference means the\n * conversation has moved to another account, and the process plus its\n * Claude session id have to go because a transcript cannot resume across\n * accounts. Absent on the interactive shim, which never fails over.\n */\n cliPath?: string\n cliArgs?: string[]\n // Retain resolved calls until continuation settles, including late channel closure.\n pendingProxyCompletions?: Map<string, {\n call: PendingProxyCall\n result: ProxyToolResult\n recoveryRequired: boolean\n }>\n /**\n * stdout lines the child emitted while no turn had a line listener\n * attached (between opencode turns). Bounded; see `bufferUnattendedLine`.\n * Absent on the interactive shim, which has no unattended window.\n */\n unattendedLines?: string[]\n /** Lines evicted from `unattendedLines` because the cap was hit. */\n unattendedDropped?: number\n /**\n * opencode session this process last served, tagged by doStream each turn.\n * `/btw` runs from a command hook that only knows the session id, so this is\n * how it finds the process to ask (see `findActiveProcessBySessionId`).\n */\n opencodeSessionID?: string\n /** What the /btw command hook needs to send a side question to this process early. */\n asideTransport?: { cliPath: string; interactive: boolean }\n /**\n * True from a stdin write that asks the CLI for work until its terminal\n * `result` line, whether or not a turn is still listening. Set by\n * `noteTurnStarted`, cleared by `noteTurnLine` (see `interruptTurn`).\n */\n turnInFlight?: boolean\n turnIdleWaiters?: Array<() => void>\n /**\n * Tail of what the child last wrote to stderr, capped at\n * `STDERR_RETAIN_BYTES` with the newest bytes kept. Often the only record\n * of why a child died when it closed without emitting a terminal `result`\n * line; see `describeChildCrash`.\n */\n lastStderr?: string\n}\n\n/** Most recently used process serving an opencode session id, if any. */\nexport function findActiveProcessBySessionId(sessionID: string): ActiveProcess | undefined {\n let found: ActiveProcess | undefined\n // Map order is LRU (see `touch`), so the last match is the freshest.\n for (const ap of activeProcesses.values()) {\n if (ap.opencodeSessionID === sessionID) found = ap\n }\n return found\n}\n\n// A child normally only speaks while a doStream turn is listening. The one\n// exception is a turn that ended on the CLI's side while opencode was still\n// waiting on a proxy call (Claude's MCP client gave up on the request and\n// the model carried on alone). Keep what it said so the next turn can show\n// it instead of losing it; cap it so a runaway child cannot grow the heap.\nconst UNATTENDED_LINE_CAP = 500\nconst UNATTENDED_BYTE_CAP = 2 * 1024 * 1024\n\nexport function bufferUnattendedLine(ap: ActiveProcess, line: string): void {\n const lines = (ap.unattendedLines ??= [])\n lines.push(line)\n let bytes = 0\n for (const kept of lines) bytes += Buffer.byteLength(kept)\n while (\n lines.length > 0 &&\n (lines.length > UNATTENDED_LINE_CAP || bytes > UNATTENDED_BYTE_CAP)\n ) {\n bytes -= Buffer.byteLength(lines.shift()!)\n ap.unattendedDropped = (ap.unattendedDropped ?? 0) + 1\n }\n}\n\n/** Hand over and clear everything the child said while nobody listened. */\nexport function takeUnattendedLines(ap: ActiveProcess): {\n lines: string[]\n dropped: number\n} {\n const lines = ap.unattendedLines ?? []\n const dropped = ap.unattendedDropped ?? 0\n ap.unattendedLines = []\n ap.unattendedDropped = 0\n return { lines, dropped }\n}\n\n// The CLI writes its own diagnostics to stderr, which is where the reason a\n// child died is usually the only thing on record. Keep the tail so a turn\n// that ends with the child gone can say why; bounded, newest bytes win.\nconst STDERR_RETAIN_BYTES = 2 * 1024\n\nexport function retainStderr(ap: ActiveProcess, chunk: string): void {\n ap.lastStderr = ((ap.lastStderr ?? \"\") + chunk).slice(-STDERR_RETAIN_BYTES)\n}\n\n/**\n * One line (plus the stderr tail) explaining a child that closed its stdio\n * without emitting a terminal `result`. Before this the turn simply finished\n * with reason `stop` and empty usage, so a crashed CLI read as a short but\n * successful answer.\n */\nexport function describeChildCrash(\n exitCode: number | null | undefined,\n signal: NodeJS.Signals | null | undefined,\n lastStderr: string | undefined,\n): string {\n const how = signal\n ? `was killed by ${signal}`\n : typeof exitCode === \"number\"\n ? `exited with code ${exitCode}`\n : \"closed its output\"\n const tail = lastStderr?.trim()\n return (\n `The Claude Code CLI ${how} before finishing this turn (no result was emitted), ` +\n \"so the answer above may be incomplete.\" +\n (tail ? `\\n\\nLast stderr from the CLI:\\n${tail}` : \"\")\n )\n}\n\n// One active CLI process per session key. Keyed by a composite\n// (cwd + model + opencode session-affinity) so two chats don't race.\n// Iteration order is insertion order, which we refresh on access to\n// make this a poor-man's LRU; see `touch()` below.\nconst activeProcesses = new Map<string, ActiveProcess>()\nconst claudeSessions = new Map<string, string>()\n// Idle-eviction timers keyed like `activeProcesses` (idle timeout by\n// @bernardofortes, absorbed from a5f723a).\nconst idleEvictionTimers = new Map<string, ReturnType<typeof setTimeout>>()\nconst MAX_IDLE_TIMEOUT_MS = 2_147_483_647\n\n/**\n * Idle eviction is off unless `idleProcessTimeoutMs` is set: an unset option\n * resolves to 0, which arms no timer, so workers live until LRU eviction as\n * they always have. PR #36 (@broskees) proposed 30 minutes by default; that\n * was reverted at merge because it changes when a resumed chat pays for a\n * fresh `--resume` spawn, which is the user's call. An idle `claude --print`\n * holds roughly 250 MB resident, so setting it is worth documenting, not\n * imposing. The Claude session id survives eviction either way.\n */\nexport const DEFAULT_IDLE_PROCESS_TIMEOUT_MS = 0\n\n/** The idle timeout a caller-facing option resolves to: unset means the default. */\nexport function resolveIdleProcessTimeoutMs(configured: number | undefined): number {\n return configured === undefined ? DEFAULT_IDLE_PROCESS_TIMEOUT_MS : configured\n}\n\n// Cap on live CLI subprocesses. Session-affinity-keyed entries accumulate\n// one-per-chat, so an unbounded map would leak processes as users open new\n// chats. This caps at a reasonable working-set and evicts the oldest idle\n// one, never a process that is mid-turn. Kept at 16: PR #36 proposed 8 on\n// the assumption that a default idle timer does the real work, and that\n// default was not adopted, so the cap is still the only bound.\nexport const MAX_ACTIVE_PROCESSES = 16\nconst PROCESS_EXIT_TIMEOUT_MS = 1_500\nconst PROCESS_FORCE_EXIT_TIMEOUT_MS = 500\n/** Same wording the attached turn's close handler uses, so one log line\n * shape covers a child that died mid-turn and one that died between turns. */\nexport const CHILD_EXITED_MESSAGE =\n \"Claude CLI subprocess closed before pending tool calls were resolved\"\n\nfunction envFlagEnabled(value: string | undefined): boolean {\n if (value === undefined) return false\n const normalized = value.trim().toLowerCase()\n if (!normalized) return false\n return ![\"0\", \"false\", \"no\", \"off\"].includes(normalized)\n}\n\nexport function isClaudeThinkingDisabled(): boolean {\n return (\n envFlagEnabled(process.env.CLAUDE_CODE_DISABLE_THINKING) ||\n envFlagEnabled(process.env.CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING)\n )\n}\n\n/**\n * The CLI's effort vocabulary is low | medium | high | xhigh | max. `minimal`\n * is this provider's own lowest step with no CLI counterpart, so it lands on\n * `low`.\n */\nexport function cliEffortLevel(effort: ReasoningEffort): string {\n return effort === \"minimal\" ? \"low\" : effort\n}\n\nexport function claudeSpawnEnv(opts?: {\n ignoreAnthropicApiKey?: boolean\n /** Reasoning effort for this spawn; wins over a shell-level override. */\n effort?: ReasoningEffort\n}): Record<string, string | undefined> {\n const env: Record<string, string | undefined> = {\n ...process.env,\n TERM: \"xterm-256color\",\n // Pin the child to the binary whose version we detected, and keep it off\n // non-essential network calls. Fills gaps only, so an explicit shell value\n // survives: see `cliHygieneEnv` for why the version has to hold still.\n ...cliHygieneEnv(),\n }\n\n // Effort travels as CLAUDE_CODE_EFFORT_LEVEL, which the CLI treats as the\n // session-wide override (it beats settings.json and `/effort`). An env var\n // rather than `--effort` because a CLI too old to know it ignores it\n // instead of refusing to start. Unlike the thinking vars below, an explicit\n // effort from the request wins over the shell: the variant picker and an\n // agent's `reasoningEffort` are per-request choices, a shell export is not.\n if (opts?.effort) {\n env.CLAUDE_CODE_EFFORT_LEVEL = cliEffortLevel(opts.effort)\n }\n\n // Force subscription auth: with an API key in the env, Claude Code bills\n // pay-as-you-go (Console) instead of the logged-in plan, bypassing the\n // Agent SDK credit. Opt-in via `ignoreAnthropicApiKey`.\n if (opts?.ignoreAnthropicApiKey) {\n delete env.ANTHROPIC_API_KEY\n delete env.ANTHROPIC_AUTH_TOKEN\n }\n\n // Default-on thinking summaries for opus-4-7 (which omits thinking by\n // default on the CLI side). Any var the user has explicitly set in their\n // shell is passed through untouched; the plugin only fills in the default.\n if (\n !isClaudeThinkingDisabled() &&\n process.env.CLAUDE_CODE_SHOW_THINKING_SUMMARIES === undefined\n ) {\n env.CLAUDE_CODE_SHOW_THINKING_SUMMARIES = \"1\"\n }\n\n return env\n}\n\nfunction touch(key: string): void {\n const existing = activeProcesses.get(key)\n if (existing) {\n activeProcesses.delete(key)\n activeProcesses.set(key, existing)\n }\n}\n\n/**\n * Make room for a new child, but never by killing one that is mid-turn.\n * Insertion order is LRU, so the first idle entry is the oldest safe victim.\n * Evicting an in-flight process truncates that turn silently: its readline\n * closes, the close handler finishes the stream, and the operator sees a\n * half-written answer with no error. When every process is busy we exceed the\n * cap for now rather than kill live work; the next spawn tries again.\n */\nexport function evictIfNeeded(): void {\n while (activeProcesses.size >= MAX_ACTIVE_PROCESSES) {\n let victimKey: string | undefined\n for (const [key, ap] of activeProcesses) {\n if (!isTurnInFlight(ap)) {\n victimKey = key\n break\n }\n }\n if (!victimKey) {\n log.warn(\"every claude process is mid-turn; skipping LRU eviction\", {\n active: activeProcesses.size,\n cap: MAX_ACTIVE_PROCESSES,\n })\n return\n }\n log.info(\"evicting LRU claude process\", { sessionKey: victimKey })\n deleteActiveProcess(victimKey)\n }\n}\n\n// Turn lifecycle and interrupt (from @broskees' 68ed142, adapted).\n//\n// The Claude CLI runs one turn per process. Closing the opencode-side stream\n// tells it nothing: before this, an abort only detached our listeners and the\n// CLI ran the abandoned turn to completion (Joseph Roberts measured ~7,500\n// extra characters generated after abort on a haiku probe), kept billing, kept\n// running tools, and its late output landed in whatever turn came next, whose\n// own stream was then closed early by the stale `result`. The CLI answers a\n// stream-json `control_request` of subtype `interrupt` by aborting the turn\n// and emitting a terminal `result`, normally within milliseconds.\n\nconst TURN_INTERRUPT_TIMEOUT_MS = 5_000\n\n/** Cheap pre-filter before JSON.parse, since every CLI stdout line hits this. */\nfunction isTerminalResultLine(line: string): boolean {\n if (!line.includes('\"result\"')) return false\n try {\n return (JSON.parse(line) as { type?: string }).type === \"result\"\n } catch {\n return false\n }\n}\n\nfunction settleTurn(ap: ActiveProcess): void {\n ap.turnInFlight = false\n const waiters = ap.turnIdleWaiters ?? []\n ap.turnIdleWaiters = []\n for (const wake of waiters) wake()\n}\n\n/** Call immediately before any stdin write that asks the CLI to do work. */\nexport function noteTurnStarted(ap: ActiveProcess): void {\n // The interactive transport never reports through `noteTurnLine`, so a flag\n // set there would never clear.\n if (ap.asideTransport?.interactive) return\n ap.turnInFlight = true\n}\n\n/**\n * Feed every CLI stdout line here, independent of whichever turn currently\n * owns the stream: a `result` that lands after its turn detached (the abort\n * case) must still mark the CLI idle rather than leak into the next turn.\n */\nexport function noteTurnLine(ap: ActiveProcess, line: string): void {\n if (!ap.turnInFlight) return\n if (isTerminalResultLine(line)) settleTurn(ap)\n}\n\nexport function isTurnInFlight(ap: ActiveProcess): boolean {\n return ap.turnInFlight === true\n}\n\n/** Resolves true once the CLI is idle, false if it stayed busy past the timeout. */\nexport function awaitTurnIdle(ap: ActiveProcess, timeoutMs: number): Promise<boolean> {\n if (!ap.turnInFlight) return Promise.resolve(true)\n return new Promise((resolve) => {\n const wake = () => {\n clearTimeout(timer)\n resolve(true)\n }\n const timer = setTimeout(() => {\n const waiters = ap.turnIdleWaiters ?? []\n const at = waiters.indexOf(wake)\n if (at >= 0) waiters.splice(at, 1)\n resolve(false)\n }, timeoutMs)\n ;(ap.turnIdleWaiters ??= []).push(wake)\n })\n}\n\n/** Ask the CLI to abandon the in-flight turn, and wait for it to say it did. */\nexport function interruptTurn(\n ap: ActiveProcess,\n timeoutMs = TURN_INTERRUPT_TIMEOUT_MS,\n): Promise<boolean> {\n if (!ap.turnInFlight) return Promise.resolve(true)\n const stdin = ap.proc.stdin\n if (ap.asideTransport?.interactive || !stdin || !stdin.writable) {\n // A TUI stdin would type the JSON in as text. Wait the turn out instead.\n log.notice(\"cannot interrupt this transport; waiting for the turn to end\")\n return awaitTurnIdle(ap, timeoutMs)\n }\n try {\n stdin.write(\n JSON.stringify({\n type: \"control_request\",\n request_id: randomUUID(),\n request: { subtype: \"interrupt\" },\n }) + \"\\n\",\n )\n } catch (error) {\n log.warn(\"failed to write interrupt control request\", {\n error: error instanceof Error ? error.message : String(error),\n })\n return Promise.resolve(false)\n }\n return awaitTurnIdle(ap, timeoutMs)\n}\n\nfunction cancelIdleProcessEviction(key: string): void {\n const timer = idleEvictionTimers.get(key)\n if (!timer) return\n clearTimeout(timer)\n idleEvictionTimers.delete(key)\n}\n\nexport function getActiveProcess(key: string): ActiveProcess | undefined {\n const ap = activeProcesses.get(key)\n if (ap) {\n cancelIdleProcessEviction(key)\n touch(key)\n }\n return ap\n}\n\nexport function setActiveProcess(key: string, ap: ActiveProcess): void {\n cancelIdleProcessEviction(key)\n activeProcesses.set(key, ap)\n}\n\n/**\n * Evict a headless Claude worker after a completed turn has stayed idle.\n * Armed by the turn's `completeResult`, so the clock starts when a turn\n * finishes, not when the child was spawned. Reusing the worker through\n * `getActiveProcess` cancels the timer. The Claude session id is\n * intentionally retained so the next turn can continue the same conversation\n * via `--resume`. A process found mid-turn when the timer fires (a recovered\n * continuation, an auto-continue, a late tool result) is not evicted; the\n * timer is re-armed instead, the same rule the LRU cap follows.\n */\nexport function scheduleIdleProcessEviction(\n key: string,\n timeoutMs: number | undefined,\n): void {\n cancelIdleProcessEviction(key)\n if (\n typeof timeoutMs !== \"number\" ||\n !Number.isFinite(timeoutMs) ||\n timeoutMs <= 0 ||\n timeoutMs > MAX_IDLE_TIMEOUT_MS\n ) {\n return\n }\n\n const scheduledProcess = activeProcesses.get(key)\n if (!scheduledProcess) return\n\n const timer = setTimeout(() => {\n idleEvictionTimers.delete(key)\n if (activeProcesses.get(key) !== scheduledProcess) return\n if (isTurnInFlight(scheduledProcess)) {\n log.info(\"idle timer found a turn in flight; re-arming\", { sessionKey: key, timeoutMs })\n scheduleIdleProcessEviction(key, timeoutMs)\n return\n }\n log.info(\"evicting idle claude process\", { sessionKey: key, timeoutMs })\n deleteActiveProcess(key)\n }, timeoutMs)\n timer.unref()\n idleEvictionTimers.set(key, timer)\n}\n\n/** Whether an idle-eviction timer is armed for the key (read-only, for tests). */\nexport function isIdleProcessEvictionScheduled(key: string): boolean {\n return idleEvictionTimers.has(key)\n}\n\nfunction detachActiveProcess(key: string): ActiveProcess | undefined {\n cancelIdleProcessEviction(key)\n const ap = activeProcesses.get(key)\n if (!ap) return undefined\n activeProcesses.delete(key)\n if (ap.proxyServer) {\n void ap.proxyServer.close()\n // The server's close already answered every open HTTP request with an\n // error; the broker's entries for them can never be resolved to anyone\n // now, and a `task` call has no deadline that would otherwise reap them.\n rejectAllPendingProxyCallsForSession(key, new Error(SERVER_CLOSED_MESSAGE))\n }\n return ap\n}\n\n/**\n * Release everything this plugin holds for one opencode session that was\n * deleted: its live `claude` children (any model, effort, or compaction\n * spawn), the remembered Claude session ids, and per-session state. Unlike\n * idle eviction this is a real deletion, so nothing is kept for a resume.\n * The `\"default\"` affinity is the shared bucket used when no session id is\n * known and is deliberately never matched. Returns the released keys.\n */\nexport function deleteActiveProcessesForSession(sessionID: string): string[] {\n if (!sessionID || sessionID === \"default\") return []\n const released: string[] = []\n for (const [key, ap] of [...activeProcesses]) {\n const owned =\n ap.opencodeSessionID === sessionID || describeSessionKey(key).session === sessionID\n if (!owned) continue\n log.info(\"releasing claude process for deleted session\", { sessionKey: key, sessionID })\n void deleteActiveProcessAndWait(key)\n released.push(key)\n }\n // Session ids and per-session state can outlive their process (idle\n // eviction keeps them for `--resume`); a deleted session never resumes.\n for (const key of [...claudeSessions.keys()]) {\n if (describeSessionKey(key).session !== sessionID) continue\n deleteClaudeSessionId(key)\n clearCompression(key)\n if (!released.includes(key)) released.push(key)\n }\n return released\n}\n\n/**\n * Synchronous best-effort sweep for host process exit. Node does not kill\n * children on exit, so without this a hard opencode shutdown reparents every\n * live `claude` to init. Must stay sync: `process.on(\"exit\")` runs no async\n * work. Session ids are left alone; the process is going away with them.\n */\nexport function killAllActiveProcesses(): string[] {\n const keys = [...activeProcesses.keys()]\n for (const key of keys) deleteActiveProcess(key)\n return keys\n}\n\nlet processExitCleanupWired = false\n\n/**\n * Arm `killAllActiveProcesses` for host process exit, once per process. The\n * plugin entry can run more than once (tests, account expansion), and each\n * run must not add another `exit` listener. Returns whether this call armed it.\n */\nexport function ensureProcessExitCleanup(): boolean {\n if (processExitCleanupWired) return false\n processExitCleanupWired = true\n process.once(\"exit\", () => {\n killAllActiveProcesses()\n })\n return true\n}\n\nexport function deleteActiveProcess(key: string): void {\n const ap = detachActiveProcess(key)\n ap?.proc.kill()\n}\n\nfunction hasProcessExited(proc: ChildProcess): boolean {\n return proc.exitCode !== null || proc.signalCode !== null\n}\n\nfunction waitForProcessExit(\n proc: ChildProcess,\n timeoutMs: number,\n): Promise<boolean> {\n if (hasProcessExited(proc)) return Promise.resolve(true)\n\n return new Promise((resolve) => {\n const onExit = () => {\n clearTimeout(timer)\n resolve(true)\n }\n const timer = setTimeout(() => {\n proc.off(\"exit\", onExit)\n resolve(hasProcessExited(proc))\n }, timeoutMs)\n proc.once(\"exit\", onExit)\n })\n}\n\nexport async function deleteActiveProcessAndWait(\n key: string,\n options: {\n exitTimeoutMs?: number\n forceExitTimeoutMs?: number\n } = {},\n): Promise<boolean> {\n const ap = detachActiveProcess(key)\n if (!ap || hasProcessExited(ap.proc)) return true\n\n const gracefulExit = waitForProcessExit(\n ap.proc,\n options.exitTimeoutMs ?? PROCESS_EXIT_TIMEOUT_MS,\n )\n ap.proc.kill()\n if (await gracefulExit) return true\n\n const forcedExit = waitForProcessExit(\n ap.proc,\n options.forceExitTimeoutMs ?? PROCESS_FORCE_EXIT_TIMEOUT_MS,\n )\n ap.proc.kill(\"SIGKILL\")\n if (await forcedExit) return true\n\n log.warn(\"claude process did not exit; starting a fresh session\", {\n sessionKey: key,\n })\n deleteClaudeSessionId(key)\n return false\n}\n\nexport function getClaudeSessionId(key: string): string | undefined {\n return claudeSessions.get(key)\n}\n\n/**\n * A Claude session id outlives its process on purpose, so nothing in the\n * ordinary lifecycle ever removes one: under a long-lived `opencode serve`\n * that hops projects and models this map only grows, and each entry pins a\n * todo ledger with it. The cap is the same shape as\n * `MAX_COMPRESSION_ENTRIES`, and it is generous because the cost of getting\n * it wrong is a conversation that silently restarts.\n */\nexport const MAX_CLAUDE_SESSION_ENTRIES = 64\n\n/**\n * A key with a live process, a proxied call still in the air or an unanswered\n * plan-mode question is doing work that the id is part of; dropping it would\n * strand that work on a session the next turn no longer resumes. Read from\n * the map directly rather than through `getActiveProcess`: that one refreshes\n * LRU order and cancels idle timers, which a scan must not do.\n */\nfunction claudeSessionIsBusy(key: string): boolean {\n if (activeProcesses.has(key)) return true\n return getPendingProxyCalls(key).length > 0 || hasExitPlanModeQuestions(key)\n}\n\n/**\n * Shed the least recently used idle sessions. When every key is busy this\n * evicts nothing and the map runs over the cap for a while, the same rule\n * `evictIfNeeded` follows: exceeding a cap briefly is cheaper than cutting a\n * conversation that is still running.\n */\nfunction capClaudeSessions(): void {\n for (const key of [...claudeSessions.keys()]) {\n if (claudeSessions.size <= MAX_CLAUDE_SESSION_ENTRIES) return\n if (claudeSessionIsBusy(key)) continue\n log.info(\"claude session cap reached; releasing an idle session\", {\n sessionKey: key,\n size: claudeSessions.size,\n cap: MAX_CLAUDE_SESSION_ENTRIES,\n })\n // Through the central release so the todo ledger and any pending\n // plan-mode question go with it rather than outliving the id.\n deleteClaudeSessionId(key)\n }\n}\n\nexport function setClaudeSessionId(key: string, sessionId: string): void {\n // Re-inserting moves the key to the back, so the cap sheds the conversation\n // that has been quiet longest rather than the one that started first.\n claudeSessions.delete(key)\n claudeSessions.set(key, sessionId)\n capClaudeSessions()\n}\n\nexport function deleteClaudeSessionId(key: string): void {\n clearExitPlanModeQuestions(key)\n clearAccountFailoverQuestions(key)\n const claudeSessionId = claudeSessions.get(key)\n if (claudeSessionId) clearLedger(claudeSessionId)\n claudeSessions.delete(key)\n}\n\nexport function effortSessionKey(baseKey: string, effort?: ReasoningEffort): string {\n return effort ? `${baseKey}::effort=${effort}` : baseKey\n}\n\n/** Retire sibling effort sessions before deciding whether to replay history. */\nexport function invalidateOtherEffortSessions(\n baseKey: string,\n effort?: ReasoningEffort,\n): void {\n const levels: (ReasoningEffort | undefined)[] = [\n undefined, \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\",\n ]\n const staleKeys = levels\n .filter((level) => level !== effort)\n .map((level) => effortSessionKey(baseKey, level))\n\n // Refuse the transition atomically. Tool results and recovery completions\n // still belong to the old process; they must finish at its original effort.\n for (const key of staleKeys) {\n const active = activeProcesses.get(key)\n if (\n getPendingProxyCalls(key).length ||\n hasExitPlanModeQuestions(key) ||\n active?.pendingProxyCompletions?.size ||\n (active && (active.lineEmitter.listenerCount(\"line\") > 0 || isSideQuestionPending(active)))\n ) {\n throw new Error(\n \"Cannot change reasoning effort while the previous effort session has pending work. Finish that work at its original effort first.\",\n )\n }\n }\n for (const key of staleKeys) {\n deleteActiveProcess(key)\n deleteClaudeSessionId(key)\n clearCompression(key)\n }\n}\n\nexport function spawnClaudeProcess(\n cliPath: string,\n cliArgs: string[],\n cwd: string,\n sessionKey: string,\n proxyServer?: ProxyMcpServer | null,\n mcpHash?: string | null,\n systemPromptFile?: string,\n ignoreAnthropicApiKey?: boolean,\n effort?: ReasoningEffort,\n): ActiveProcess {\n evictIfNeeded()\n log.info(\"spawning new claude process\", {\n cliPath,\n cliArgs,\n cwd,\n sessionKey,\n effort,\n })\n\n const proc = spawn(cliPath, cliArgs, {\n cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: claudeSpawnEnv({ ignoreAnthropicApiKey, effort }),\n shell: process.platform === \"win32\",\n })\n\n const lineEmitter = new EventEmitter()\n\n const ap: ActiveProcess = {\n proc,\n lineEmitter,\n proxyServer: proxyServer ?? null,\n mcpHash,\n systemPromptFile,\n effort,\n startedAt: Date.now(),\n cliPath,\n cliArgs: [...cliArgs],\n unattendedLines: [],\n unattendedDropped: 0,\n }\n\n const rl = createInterface({ input: proc.stdout! })\n rl.on(\"line\", (line: string) => {\n if (dispatchSideQuestionResponse(ap, line)) return\n noteTurnLine(ap, line)\n if (lineEmitter.listenerCount(\"line\") === 0) {\n bufferUnattendedLine(ap, line)\n return\n }\n lineEmitter.emit(\"line\", line)\n })\n rl.on(\"close\", () => {\n settleTurn(ap)\n lineEmitter.emit(\"close\")\n })\n cancelIdleProcessEviction(sessionKey)\n activeProcesses.set(sessionKey, ap)\n\n // Baseline 'error' listener so Node doesn't throw when the process emits\n // an error between stream turns (no per-stream listener attached then).\n proc.on(\"error\", (err) => {\n log.error(\"claude process error\", { sessionKey, error: err.message })\n })\n\n // Same baseline for the child's stdin, which is a separate emitter. Every\n // write that asks the CLI for work (fresh envelope, auto-continue, the\n // watchdog re-send, the interrupt request) can land after the child died,\n // and an unhandled 'error' on a stream throws inside opencode's own\n // process. Ending the turn is not this handler's job: the child is gone,\n // so its readline 'close' follows and the turn's close handler reports it\n // (see `describeChildCrash`). Releasing `turnInFlight` is, since no\n // terminal `result` is ever coming for a write that never arrived.\n proc.stdin?.on(\"error\", (err: NodeJS.ErrnoException) => {\n log.warn(\"claude process stdin error\", {\n sessionKey,\n code: err.code,\n error: err.message,\n })\n settleTurn(ap)\n })\n\n proc.on(\"exit\", (code, signal) => {\n log.info(\"claude process exited\", { code, signal, sessionKey })\n void proxyServer?.close()\n if (systemPromptFile) {\n void unlink(systemPromptFile).catch(() => {})\n }\n const ownsSessionKey = activeProcesses.get(sessionKey) === ap\n if (ownsSessionKey) {\n cancelIdleProcessEviction(sessionKey)\n activeProcesses.delete(sessionKey)\n // The child is the only thing that could still consume these calls'\n // results. A turn that is attached rejects them from its own close\n // handler; this covers a child that dies between turns, which no\n // deadline would otherwise reap now that `task` has none.\n if (getPendingProxyCalls(sessionKey).length > 0) {\n rejectAllPendingProxyCallsForSession(\n sessionKey,\n new Error(CHILD_EXITED_MESSAGE),\n )\n }\n }\n if (ownsSessionKey && code !== 0 && code !== null) {\n log.info(\"process exited with error, clearing session\", {\n code,\n sessionKey,\n })\n claudeSessions.delete(sessionKey)\n }\n })\n\n proc.stderr?.on(\"data\", (data: Buffer) => {\n const stderr = data.toString()\n log.debug(\"stderr\", { data: stderr.slice(0, 200) })\n retainStderr(ap, stderr)\n\n // \"No conversation found with session ID: <uuid>\" is what `--resume`\n // prints for a purged transcript — note the lowercase \"session ID\",\n // which the capitalized match below does not catch.\n if (\n stderr.includes(\"No conversation found\") ||\n (stderr.includes(\"Session ID\") &&\n (stderr.includes(\"already in use\") ||\n stderr.includes(\"not found\") ||\n stderr.includes(\"invalid\")))\n ) {\n if (activeProcesses.get(sessionKey) === ap) {\n log.warn(\"claude session ID error, clearing session\", {\n sessionKey,\n error: stderr.slice(0, 200),\n })\n claudeSessions.delete(sessionKey)\n } else {\n log.debug(\"ignoring session ID error from stale claude process\", {\n sessionKey,\n })\n }\n }\n })\n\n return ap\n}\n\n/**\n * Append `--resume <id>` to an already-built args vector when a Claude\n * conversation id is known for the session and the args don't already carry\n * a session flag. Used by `respawnActiveProcess` to resume the conversation\n * in a fresh child without rebuilding the whole (version-gated) args vector.\n * `--resume`, not `--session-id`: the latter means \"create a NEW session\n * with this UUID\" and the CLI rejects it with \"Session ID ... is already in\n * use\" whenever a transcript exists on disk — which is exactly the state a\n * mid-conversation respawn is in. If the wedged child died before writing\n * any transcript, `--resume` fails with \"No conversation found with session\n * ID\", which the stderr recovery matcher already catches (fresh-session\n * fallback).\n */\nexport function appendResumeIfNeeded(\n sessionKey: string,\n cliArgs: string[],\n): string[] {\n if (cliArgs.includes(\"--resume\") || cliArgs.includes(\"--session-id\")) {\n return cliArgs\n }\n const sid = claudeSessions.get(sessionKey)\n if (!sid) return cliArgs\n return [...cliArgs, \"--resume\", sid]\n}\n\n/**\n * Replace a wedged reused process with a fresh one, resuming the same\n * Claude conversation. Used by the doStream start-watchdog when a reused\n * process produces no stdout within a grace window after a fresh-turn\n * envelope write — observed after a very long proxy-blocked tool call\n * (e.g. a multi-minute `task` subagent). Before the per-tool proxy timeout\n * fix this was masked because the flat 10-minute ceiling ended the turn\n * first; now that the task proxy blocks and returns successfully, resuming\n * a reused child after such a long wait can leave it silent on stdout.\n *\n * Reuses the existing proxy server, system-prompt file, and MCP hash (their\n * handles are already baked into `cliArgs`' `--mcp-config`/append-prompt\n * paths), so this only swaps the child process. The old child's exit\n * handler is silenced before kill so it doesn't close the proxy server we\n * are reusing; the new child gets its own exit handler from\n * `spawnClaudeProcess`. `claudeSessions` is left intact so the respawn can\n * add `--resume` (see `appendResumeIfNeeded`).\n *\n * The respawn happens in the middle of the same logical turn, and the caller\n * re-sends that turn's envelope at once. Turn state lives on the\n * `ActiveProcess`, so the replacement inherits the old process's in-flight\n * marker (@broskees' b719497); without that handoff abort, LRU eviction, the\n * idle timer and the next turn's quiesce all mistake the busy replacement\n * for an idle process.\n *\n * Returns the new `ActiveProcess`, or `undefined` if there was no active\n * process for the key (caller should treat that as \"nothing to respawn\").\n */\nexport function respawnActiveProcess(\n sessionKey: string,\n cliPath: string,\n cliArgs: string[],\n cwd: string,\n ignoreAnthropicApiKey?: boolean,\n): ActiveProcess | undefined {\n const old = activeProcesses.get(sessionKey)\n if (!old) return undefined\n const turnWasInFlight = isTurnInFlight(old)\n activeProcesses.delete(sessionKey)\n // Silence the old exit handler so it doesn't close the proxy server,\n // unlink the system-prompt file, or touch claudeSessions on its way out\n // — those handles are reused by the new child. spawnClaudeProcess wires\n // a fresh exit handler for the respawned child.\n old.proc.removeAllListeners(\"exit\")\n try {\n old.proc.kill()\n } catch {}\n const replacement = spawnClaudeProcess(\n cliPath,\n appendResumeIfNeeded(sessionKey, old.cliArgs ?? cliArgs),\n cwd,\n sessionKey,\n old.proxyServer,\n old.mcpHash,\n old.systemPromptFile,\n ignoreAnthropicApiKey,\n old.effort,\n )\n replacement.pendingProxyCompletions = old.pendingProxyCompletions\n delete old.pendingProxyCompletions\n if (turnWasInFlight) noteTurnStarted(replacement)\n return replacement\n}\n\nexport function buildCliArgs(opts: {\n sessionKey: string\n skipPermissions: boolean\n includeSessionId?: boolean\n model?: string\n permissionMode?: string\n mcpConfig?: string | string[]\n strictMcpConfig?: boolean\n disallowedTools?: string[]\n appendSystemPromptFile?: string\n /** `--plugin-dir` values (skill bridge), one flag per directory. */\n pluginDirs?: string[]\n thinking?: \"enabled\" | \"disabled\"\n thinkingDisplay?: \"summarized\" | \"omitted\"\n fastMode?: boolean\n cliVersion?: CliVersion | null\n}): string[] {\n const {\n sessionKey,\n skipPermissions,\n includeSessionId = true,\n model,\n permissionMode,\n mcpConfig,\n strictMcpConfig,\n disallowedTools,\n appendSystemPromptFile,\n pluginDirs,\n thinking,\n thinkingDisplay,\n fastMode,\n cliVersion,\n } = opts\n const args = [\n \"--print\",\n \"--output-format\",\n \"stream-json\",\n \"--input-format\",\n \"stream-json\",\n \"--include-partial-messages\",\n \"--verbose\",\n ]\n\n if (model) {\n args.push(\"--model\", model)\n }\n\n if (permissionMode) {\n args.push(\"--permission-mode\", permissionMode)\n }\n\n // `--session-id` means \"create a NEW session with this UUID\" and the CLI\n // exits with \"Session ID ... is already in use\" whenever a transcript for\n // that ID already exists on disk. Continuing an existing session requires\n // `--resume` (which keeps the same session ID in print mode).\n if (includeSessionId) {\n const sessionId = claudeSessions.get(sessionKey)\n if (sessionId && !activeProcesses.has(sessionKey)) {\n args.push(\"--resume\", sessionId)\n }\n }\n\n if (mcpConfig) {\n const configs = Array.isArray(mcpConfig) ? mcpConfig : [mcpConfig]\n const filtered = configs.filter((c) => typeof c === \"string\" && c.length > 0)\n if (filtered.length > 0) {\n args.push(\"--mcp-config\", ...filtered)\n }\n }\n\n if (strictMcpConfig) {\n args.push(\"--strict-mcp-config\")\n }\n\n if (disallowedTools && disallowedTools.length > 0) {\n args.push(\"--disallowedTools\", ...disallowedTools)\n }\n\n // `--thinking` is only present from Claude Code 2.x onward; gate so\n // pre-2.x binaries don't crash with a parse error. Unknown version →\n // skip (the spawn still works, the user just doesn't get extended\n // thinking until they upgrade).\n if (thinking && cliSupportsThinking(cliVersion ?? null)) {\n args.push(\"--thinking\", thinking)\n }\n\n // `--thinking-display` was added in Claude Code 2.1.142. Older CLIs\n // reject it with a parse error, so gate on detected version. When\n // version is unknown (detection failed), be conservative and skip.\n if (thinkingDisplay && cliSupportsThinkingDisplay(cliVersion ?? null)) {\n args.push(\"--thinking-display\", thinkingDisplay)\n }\n\n if (appendSystemPromptFile) {\n args.push(\"--append-system-prompt-file\", appendSystemPromptFile)\n }\n for (const dir of pluginDirs ?? []) {\n args.push(\"--plugin-dir\", dir)\n }\n\n // Fast mode's only headless opt-in. `--settings` feeds the CLI's\n // `flagSettings` layer, which is the one its SDK gate checks; a `fastMode`\n // in the user's own settings.json is NOT enough for a `--print` run.\n // Built as one object so later flag-settings keys merge here instead of\n // adding a second `--settings` (the CLI takes the flag once).\n if (fastMode && cliSupportsFastMode(cliVersion ?? null)) {\n args.push(\"--settings\", JSON.stringify({ fastMode: true }))\n }\n\n // Plan mode is a capability restriction, not a prompt policy, and the CLI\n // lets `--dangerously-skip-permissions` override it outright: measured on\n // 2.1.258, a plan-mode run carrying both flags wrote a file on request\n // without prompting, while the same run without the skip flag refused and\n // created nothing. Since `skipPermissions` defaults to true, passing both\n // is the common case, so anyone asking for plan mode was silently getting\n // full write access. Plan mode must never permit edits, so it wins here.\n // Every other `permissionMode` value governs prompting, which is exactly\n // what the skip flag is for, so those still pass both.\n if (skipPermissions && permissionMode !== \"plan\") {\n args.push(\"--dangerously-skip-permissions\")\n }\n\n return args\n}\n\n/**\n * Build a session key that includes both cwd and model,\n * so different models get separate processes.\n */\nexport function sessionKey(cwd: string, modelId: string): string {\n return `${cwd}::${modelId}`\n}\n\n/**\n * Pull the readable parts back out of a session key for the doctor report.\n * The key is `<cwd>::<model>::<scope>::<affinity>::context=[...]` with an\n * optional `::effort=<level>` tail, and the compaction variant is\n * `<cwd>::<model>::compaction::<affinity>`, so the model and the opencode\n * session id sit at the same two positions either way.\n */\nexport function describeSessionKey(key: string): {\n cwd: string\n model: string\n session: string\n compaction: boolean\n} {\n const parts = key.split(\"::\")\n return {\n cwd: parts[0] ?? \"unknown\",\n model: parts[1] ?? \"unknown\",\n session: parts[3] ?? \"unknown\",\n compaction: parts[2] === \"compaction\",\n }\n}\n\n/** One live `claude` child, flattened for `/claude-code-doctor`. */\nexport interface ActiveProcessSnapshot {\n sessionKey: string\n session: string\n model: string\n compaction: boolean\n pid?: number\n inFlight: boolean\n ageMs?: number\n effort?: ReasoningEffort\n attached: boolean\n proxyUrl?: string\n /**\n * Tail of the child's stderr, when something upstream of this module is\n * recording one. Read through an optional property so the doctor works\n * whether or not that field exists on the running build.\n */\n lastStderr?: string\n}\n\n/**\n * Every live child, oldest-used first (the map is LRU). Read-only; nothing\n * here touches eviction or the process's own listeners.\n */\nexport function snapshotActiveProcesses(now = Date.now()): ActiveProcessSnapshot[] {\n const out: ActiveProcessSnapshot[] = []\n for (const [key, ap] of activeProcesses) {\n const described = describeSessionKey(key)\n const lastStderr = (ap as { lastStderr?: unknown }).lastStderr\n out.push({\n sessionKey: key,\n session: ap.opencodeSessionID ?? described.session,\n model: described.model,\n compaction: described.compaction,\n pid: ap.proc.pid,\n inFlight: ap.turnInFlight === true,\n ageMs: ap.startedAt === undefined ? undefined : Math.max(0, now - ap.startedAt),\n effort: ap.effort,\n attached: ap.lineEmitter.listenerCount(\"line\") > 0,\n proxyUrl: ap.proxyServer?.url,\n lastStderr: typeof lastStderr === \"string\" ? lastStderr : undefined,\n })\n }\n return out\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from \"node:http\"\nimport type { AddressInfo } from \"node:net\"\nimport * as fs from \"node:fs\"\nimport * as path from \"node:path\"\nimport * as crypto from \"node:crypto\"\nimport { EventEmitter } from \"node:events\"\nimport { log } from \"./logger.js\"\nimport { pluginTmpDir } from \"./tmp.js\"\n\n/**\n * Minimal MCP HTTP server embedded in-process. Exposes a set of \"proxy\"\n * tools (Bash, Edit, Write, etc.) that Claude CLI calls when its built-in\n * equivalents are disabled via --disallowedTools. Our handler blocks until\n * an external broker resolves the call, then responds to Claude.\n *\n * Wire protocol: JSON-RPC 2.0 over plain HTTP POST to `/mcp`. Protocol\n * methods (`initialize`, `tools/list`) and calls answered in-process get a\n * single JSON reply. A broker-backed `tools/call` can block for as long as\n * opencode takes to run the tool, so its reply is streamed: SSE when the\n * client accepts it, otherwise a chunked JSON body whose headers go out at\n * once and which carries keepalive whitespace until the result is ready\n * (see `openEventStream` / `openJsonStream`).\n */\n\nexport interface ProxyMcpServer {\n url: string\n serverName: string\n tools: ProxyToolDef[]\n /** Per-server bearer secret. Minted on start, handed to Claude via the\n * `headers` block of the generated MCP config, and required on every\n * request. Exposed so callers (and tests) can authenticate; MUST NOT be\n * logged or placed in the URL. */\n authToken: string\n /** Fires when Claude invokes one of our proxy tools. The handler resolves\n * the returned pending call once a result is available. */\n calls: EventEmitter\n /** Write `--mcp-config <path>`-compatible scratch file and return its path. */\n configPath(): string\n /**\n * Ids of the `tools/call` requests this server is still holding open.\n * Read-only. An entry leaves this list only when its promise settles, so\n * it is the direct evidence that a lifecycle event released the HTTP side\n * of a call and not just the broker's entry for it.\n */\n pendingCallIds(): string[]\n close(): Promise<void>\n}\n\nexport interface ProxyToolDef {\n /** Raw name as seen by Claude once proxied: the MCP exposed tool name. */\n name: string\n description: string\n inputSchema: Record<string, unknown>\n}\n\n/**\n * Liveness of the HTTP reply channel behind one proxy call. Shared by\n * reference between proxy-mcp (which flips `closed` when Claude's request\n * goes away) and the broker / language model (which read it before\n * answering), so the two never need to import each other.\n */\nexport interface ProxyCallChannel {\n closed: boolean\n}\n\nexport interface ProxyToolCall {\n id: string\n toolName: string\n input: Record<string, unknown>\n resolve: (result: ProxyToolResult) => void\n reject: (err: Error) => void\n /** Absent for calls built by hand in tests; treated as open. */\n channel?: ProxyCallChannel\n}\n\n/**\n * Keep unanswered HTTP calls active independently of the tool deadline.\n * A held call timed out before delivery on CLI 2.1.258; with immediate\n * headers and these comments, the same 390-second hold completed. The same\n * cadence drives the whitespace keepalive of a JSON-only reply: both must\n * stay well under the ~300 s header/body timers in the CLI's HTTP client.\n */\nexport const PROXY_KEEPALIVE_MS = 15_000\n\n/** True when the client advertised `text/event-stream` in Accept. */\nexport function acceptsEventStream(acceptHeader: unknown): boolean {\n return (\n typeof acceptHeader === \"string\" &&\n acceptHeader.toLowerCase().includes(\"text/event-stream\")\n )\n}\n\nexport type ProxyToolResult =\n | { kind: \"text\"; text: string; isError?: boolean }\n | { kind: \"error\"; message: string }\n\n/**\n * Handler that answers a `tools/call` inside this process instead of\n * queueing it for opencode. Used by tools that act on plugin state rather\n * than on the workspace (currently only `compress`), so they never reach\n * the broker, never block on a human, and have no deadline.\n */\nexport type ProxyToolInterceptor = (\n input: Record<string, unknown>,\n) => Promise<ProxyToolResult> | ProxyToolResult\n\nexport const SERVER_CLOSED_MESSAGE = \"proxy MCP server closed\"\n\n/** Rejections that fire on normal lifecycle transitions: AFK-permission\n * timeouts, orphan rejections at turn boundaries, stream aborts, and server\n * close while its owning Claude process exits or is replaced. None are\n * user-actionable — file-log them at NOTICE. Anything else stays WARN so\n * genuine bugs remain visible in the TUI. */\nexport function isExpectedCleanupError(message: string): boolean {\n return (\n (message.includes(\"timed out after\") &&\n message.includes(\"waiting for opencode to resolve\")) ||\n message.includes(\"rejecting as orphaned\") ||\n message.includes(\"was orphaned by a new user turn\") ||\n message.includes(\"stream was aborted\") ||\n message.includes(SERVER_CLOSED_MESSAGE)\n )\n}\n\nconst PROTOCOL_VERSION = \"2024-11-05\"\nconst SERVER_NAME = \"opencode_proxy\"\nexport const PROXY_TOOL_PREFIX = `mcp__${SERVER_NAME}__`\n\n// Flat fallback cap on how long a proxy tool call may wait for opencode to\n// resolve it. Matches Claude CLI's hard upper bound for Bash (10 min). The\n// effective deadline is resolved per tool — see `resolveProxyCallTimeoutMs`.\nexport const PROXY_DEFAULT_TIMEOUT_MS = 10 * 60 * 1000\n\n/** A resolved deadline of 0 means the call waits until a lifecycle event\n * releases it: a result, an abort, the next user turn's orphan sweep, the\n * child closing, or the proxy server closing with its process. */\nexport const PROXY_NO_DEADLINE_MS = 0\n\n// Per-tool default deadlines, keyed by lowercase proxy tool name. `task` and\n// `task_batch` dispatch opencode subagents, and the wall clock is the wrong\n// unit for those: a 10-min flat ceiling fired mid-subagent and dropped the\n// late result on the floor (@jknlsn, live session ses_0cfc0da6, 2026-07-05),\n// and a 60-min one did the same to any subagent that ran longer (@broskees'\n// dd494a8). So they carry no deadline at all: an abandoned task call is\n// released by the same lifecycle events that already release every other\n// call, and a positive `proxyToolTimeoutMs` override restores a backstop.\n//\n// `question` blocks on a human reading a TUI form, so the flat ceiling is\n// the wrong unit entirely: a question posed just before the operator steps\n// away would be rejected mid-answer. 30 min is jknlsn's original figure and\n// matches the \"prefer fewer, high-signal questions\" guidance in the def.\nexport const PROXY_PER_TOOL_DEFAULT_TIMEOUT_MS: Record<string, number> = {\n task: PROXY_NO_DEADLINE_MS,\n task_batch: PROXY_NO_DEADLINE_MS, // same reasoning: it IS task calls\n question: 30 * 60 * 1000, // 30 min\n}\n\n// Node's setTimeout delay is a signed 32-bit int; values above 2^31-1 ms\n// (~24.85 days) trigger TimeoutOverflowWarning and fire at ~1ms instead.\n// Clamp absurd overrides / input.timeouts so a misconfigured deadline\n// can't collapse to \"fires immediately\".\nexport const MAX_PROXY_TIMEOUT_MS = 2 ** 31 - 1\n\n/**\n * Resolve the proxy deadline for a tool call. Layers, most-specific last:\n * 1. flat default (`PROXY_DEFAULT_TIMEOUT_MS`, 10 min)\n * 2. per-tool default (`PROXY_PER_TOOL_DEFAULT_TIMEOUT_MS`; `task` and\n * `task_batch` have none)\n * 3. user override via `proxyToolTimeoutMs` config (case-insensitive key).\n * A positive value replaces the default, `0` disables the deadline for\n * that tool, and a negative or non-finite value is ignored.\n * 4. for `bash`, the call's own `input.timeout` -- the proxy must never\n * undercut a build the caller explicitly asked to run long. The bash\n * proxy def advertises a `timeout` field; before this fix the proxy\n * ignored it and killed the call at the flat ceiling anyway. It only\n * ever raises, so it also turns a disabled bash deadline back into one.\n * Finally clamped to `MAX_PROXY_TIMEOUT_MS` to stay within Node's timer range.\n * Returns `PROXY_NO_DEADLINE_MS` (0) when the call has no deadline; callers\n * must not arm a timer for that value.\n */\nexport function resolveProxyCallTimeoutMs(\n toolName: string,\n input: Record<string, unknown> | undefined,\n overrides: Record<string, number> | undefined,\n): number {\n const key = toolName.toLowerCase()\n let ms = PROXY_PER_TOOL_DEFAULT_TIMEOUT_MS[key] ?? PROXY_DEFAULT_TIMEOUT_MS\n if (overrides) {\n const ov = lookupCaseInsensitive(overrides, key)\n if (isDeadlineOverride(ov)) ms = ov\n }\n if (key === \"bash\") {\n const requested = input?.timeout\n if (typeof requested === \"number\" && requested > ms) ms = requested\n }\n return Math.min(ms, MAX_PROXY_TIMEOUT_MS)\n}\n\n/** `0` (no deadline) or a positive finite number of milliseconds. */\nfunction isDeadlineOverride(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0\n}\n\nfunction lookupCaseInsensitive(\n map: Record<string, number>,\n key: string,\n): number | undefined {\n if (Object.prototype.hasOwnProperty.call(map, key)) return map[key]\n for (const k of Object.keys(map)) {\n if (k.toLowerCase() === key) return map[k]\n }\n return undefined\n}\n\n/**\n * Client-side abort ceiling written into Claude's `--mcp-config` entry for\n * the proxy server. Without a `timeout` there, Claude CLI's remote-HTTP MCP\n * client aborts each call at its 60-second default even while an opencode\n * subagent is still running (@broskees, PR #18). It must be >= the largest\n * server-side deadline or the client gives up before the broker does, so it\n * tracks the max of every tool's effective deadline: the flat default, the\n * per-tool defaults, and the user's overrides applied on top of them. A tool\n * with no deadline needs the largest value the client accepts, because the\n * CLI rejects `timeout: 0` in the MCP config outright (measured on the fork\n * this came from, @broskees' dd494a8), and this is also Node's timer max.\n * (A bash call raising its own `input.timeout` above this ceiling is a known\n * edge; Claude CLI caps bash at 10 min anyway.)\n */\nexport function resolveProxyClientCeilingMs(\n overrides: Record<string, number> | undefined,\n): number {\n let ms = PROXY_DEFAULT_TIMEOUT_MS\n const consider = (deadlineMs: number): boolean => {\n if (deadlineMs === PROXY_NO_DEADLINE_MS) return true\n if (deadlineMs > ms) ms = deadlineMs\n return false\n }\n for (const [toolName, defaultMs] of Object.entries(PROXY_PER_TOOL_DEFAULT_TIMEOUT_MS)) {\n const override = overrides ? lookupCaseInsensitive(overrides, toolName) : undefined\n if (consider(isDeadlineOverride(override) ? override : defaultMs)) {\n return MAX_PROXY_TIMEOUT_MS\n }\n }\n for (const value of Object.values(overrides ?? {})) {\n if (isDeadlineOverride(value) && consider(value)) return MAX_PROXY_TIMEOUT_MS\n }\n return Math.min(ms, MAX_PROXY_TIMEOUT_MS)\n}\n\n/**\n * Asked when a call's deadline passes. `true` means opencode is still serving\n * the call, so it keeps waiting and is asked about again later.\n *\n * The deadline counted time the operator spent on opencode's permission\n * prompt, and that is where nearly every deadline went: measured 2026-09-23,\n * three proxied `bash` calls sat on an `external_directory` prompt for 24, 34\n * and 10.5 minutes (opencode's own part timings) and each was rejected at 10.\n * Claude then moved on to a new call, and the approval, once given, arrived as\n * a late result that the next turn's orphan sweep read as a new message: the\n * new call was cancelled and Claude was told the user had refused it. opencode\n * reports the session `busy` for as long as a prompt is open (measured on\n * 1.18.32), which is the signal the language model's guard reads.\n *\n * Registered by the language model; with no guard every deadline is final, as\n * it always was, so a bare proxy server and the offline tests are unchanged.\n */\nexport type ProxyDeadlineGuard = (call: { callId: string; toolName: string }) => Promise<boolean>\n\nlet proxyDeadlineGuard: ProxyDeadlineGuard | null = null\n\nexport function setProxyDeadlineGuard(guard: ProxyDeadlineGuard | null): void {\n proxyDeadlineGuard = guard\n}\n\n/** How often a call kept past its deadline is looked at again. */\nexport const PROXY_DEADLINE_RECHECK_MS = 60_000\n\nlet deadlineRecheckMs = PROXY_DEADLINE_RECHECK_MS\n\n/** Test seam: `null` restores the default. */\nexport function _setProxyDeadlineRecheckMs(ms: number | null): void {\n deadlineRecheckMs = ms ?? PROXY_DEADLINE_RECHECK_MS\n}\n\nexport interface ProxyDeadline {\n cancel(): void\n}\n\n/**\n * One call's deadline, shared by the HTTP handler and the broker so the two\n * layers make the same decision. `onExpire` runs only once the guard says\n * opencode is no longer serving the call (or there is no guard). A guard that\n * throws counts as \"not serving\", so a failed lookup can only end a call the\n * way it always ended, never hold one open.\n */\nexport function armProxyDeadline(opts: {\n callId: string\n toolName: string\n deadlineMs: number\n onExpire: () => void\n /** Only one layer logs the extension, so a call does not report twice. */\n logExtension?: boolean\n /** Test seam. */\n recheckMs?: number\n}): ProxyDeadline {\n let cancelled = false\n let extended = false\n let timer: ReturnType<typeof setTimeout> | null = null\n\n const check = async (): Promise<void> => {\n timer = null\n if (cancelled) return\n let keep = false\n const guard = proxyDeadlineGuard\n if (guard) {\n try {\n keep = await guard({ callId: opts.callId, toolName: opts.toolName })\n } catch {\n keep = false\n }\n }\n if (cancelled) return\n if (keep) {\n if (!extended && opts.logExtension) {\n log.warn(\"proxy call past its deadline, but opencode is still serving it; waiting\", {\n callId: opts.callId,\n toolName: opts.toolName,\n deadlineMs: opts.deadlineMs,\n note: \"usually a permission prompt in opencode; the call ends when it is answered, you abort, or the claude process goes\",\n })\n }\n extended = true\n timer = setTimeout(() => void check(), opts.recheckMs ?? deadlineRecheckMs)\n return\n }\n opts.onExpire()\n }\n\n timer = setTimeout(() => void check(), opts.deadlineMs)\n return {\n cancel() {\n cancelled = true\n if (timer) clearTimeout(timer)\n timer = null\n },\n }\n}\n\n/**\n * Build the timeout error surfaced to Claude. Keeps the substrings\n * `\"timed out after\"` and `\"waiting for opencode to resolve\"` that the\n * proxy-mcp catch block classifies as expected cleanup (notice, not warn).\n * For `task` we append guidance: a Task timeout means the subagent may\n * still be running but its result is now unreachable, and the model must\n * neither declare the dispatch failed nor \"schedule a wake-up\" -- that is a\n * Claude Code affordance which cannot fire in this headless/proxy context,\n * so deferring silently drops the work.\n */\nexport function buildProxyTimeoutError(toolName: string, ms: number): Error {\n const key = toolName.toLowerCase()\n const base = `Proxy tool '${toolName}' timed out after ${ms}ms waiting for opencode to resolve the call`\n if (key === \"task\" || key === TASK_BATCH_TOOL_NAME) {\n return new Error(\n base +\n (key === \"task\" ? \" (the subagent).\" : \" (the subagents).\") +\n \" The subagent may still be running but its result\" +\n \" is no longer reachable in this session. Do not declare the dispatch\" +\n \" failed, and do not 'schedule a wake-up' or defer -- that mechanism\" +\n \" does not apply here. If the result is required, re-dispatch or\" +\n \" verify it directly now.\",\n )\n }\n return new Error(base)\n}\n\n/**\n * Disambiguation appended to the `task` proxy def (both the static\n * fallback and the live overlay). Models routinely resolve opencode's\n * \"call the task tool with subagent: X\" mention hint to Claude Code's\n * native TaskCreate (a todo tool) — creating a todo, dispatching nothing,\n * and then narrating a successful dispatch. Others burn turns grepping\n * config files to verify a subagent exists before daring to call it.\n * Both failure modes are addressed here, at the tool the model reads.\n */\nexport const TASK_PROXY_NOTE =\n \"This and task_batch are the ONLY tools that dispatch opencode subagents\" +\n \" (including user @-mentions). Claude Code's built-in TaskCreate/TaskUpdate\" +\n \" manage a local todo list and cannot dispatch subagents. Do not search\" +\n \" config files to verify a subagent type exists: invalid types fail fast\" +\n \" with a clear error. Foreground calls block until the subagent finishes;\" +\n \" set `background` to request opencode's background execution mode. For\" +\n \" two or more independent subagents in one response use task_batch, not\" +\n \" several task calls: those run one after another. Task calls have no\" +\n \" proxy deadline by default: the call waits for the subagent to finish\" +\n \" (a positive proxyToolTimeoutMs override adds a deadline).\"\n\n/**\n * `task_batch`: one MCP call that opencode runs as N parallel `task` calls.\n *\n * Design and first implementation by Joseph Roberts (@broskees) on his fork\n * (68ed142), absorbed here with credit. The limitation it works around is\n * measured, not assumed: Claude Code emits several `mcp__opencode_proxy__*`\n * tool_use blocks in one assistant message but sends the MCP requests one at\n * a time, each only after the previous result (2026-09-06, haiku, two\n * 8-second bash calls: second request arrived 7 ms after the first resolved).\n * So \"call task twice\" is serial by construction, and the only way to get two\n * subagents running at once is a single proxy call that the plugin fans out\n * inside one opencode tool boundary, where opencode executes tool calls\n * concurrently. The children are ordinary `task` calls with ids derived from\n * the parent (`taskBatchChildToolCallId`), and their results are gathered\n * back onto the parent id (`formatTaskBatchResults`) before the CLI sees it.\n */\nexport const TASK_BATCH_TOOL_NAME = \"task_batch\"\n\nexport const TASK_BATCH_PROXY_NOTE =\n \"Use this instead of several task calls in one response: Claude Code runs\" +\n \" MCP tool calls one at a time, so separate task calls run serially even\" +\n \" when emitted together, while one task_batch call fans them out as\" +\n \" parallel opencode task calls. Each task takes the same fields as the\" +\n \" task tool. Results come back in task order, each labelled. Like task it\" +\n \" has no proxy deadline by default (a positive proxyToolTimeoutMs override\" +\n \" adds one).\"\n\nexport const TASK_INPUT_REQUIRED = [\"description\", \"prompt\", \"subagent_type\"]\n\n/** Why a `task_batch` input is unusable, or null when it is fine. */\nexport function taskBatchInputError(input: Record<string, unknown> | undefined): string | null {\n const tasks = input?.tasks\n if (!Array.isArray(tasks) || tasks.length < 2) {\n return \"task_batch requires a `tasks` array with at least two items; use `task` for one subagent\"\n }\n for (const [index, task] of tasks.entries()) {\n if (task === null || typeof task !== \"object\" || Array.isArray(task)) {\n return `task_batch tasks[${index}] must be an object`\n }\n const item = task as Record<string, unknown>\n for (const field of TASK_INPUT_REQUIRED) {\n if (typeof item[field] !== \"string\") {\n return `task_batch tasks[${index}].${field} must be a string`\n }\n }\n }\n return null\n}\n\n/** The batch's task inputs, or [] when the input never passed validation. */\nexport function taskBatchTasks(input: Record<string, unknown> | undefined): Record<string, unknown>[] {\n if (taskBatchInputError(input)) return []\n return input!.tasks as Record<string, unknown>[]\n}\n\n/**\n * Child ids stay derivable from the parent so the next turn can find every\n * child's `tool-result` without extra state. Only `[A-Za-z0-9_-]`: AI SDK\n * bridges normalise other characters and the round trip would not match.\n */\nexport function taskBatchChildToolCallId(parentToolCallId: string, index: number): string {\n return `${parentToolCallId}_task_${index}`\n}\n\n/**\n * One readable result for the parent call. Children are labelled in task\n * order; a child opencode did not answer is said so rather than dropped,\n * since a silent gap would read as a subagent that never ran.\n */\nexport function formatTaskBatchResults(\n children: Array<{ task: Record<string, unknown>; result: ProxyToolResult | null }>,\n): ProxyToolResult {\n const total = children.length\n const sections = children.map(({ task, result }, index) => {\n const label = typeof task.description === \"string\" ? task.description : `task ${index + 1}`\n const agent = typeof task.subagent_type === \"string\" ? ` (${task.subagent_type})` : \"\"\n const header = `## task ${index + 1} of ${total}: ${label}${agent}`\n if (!result) return `${header}\\n[missing] opencode returned no result for this task in the batch`\n if (result.kind === \"error\") return `${header}\\n[error] ${result.message}`\n return `${header}\\n${result.isError ? \"[error] \" : \"\"}${result.text}`\n })\n const failed = children.some(({ result }) => !result || result.kind === \"error\" || result.isError)\n return { kind: \"text\", text: sections.join(\"\\n\\n\"), ...(failed ? { isError: true } : {}) }\n}\n\nconst AGENT_TYPES_HEADING = \"Available agent types\"\n\n/** Longest per-agent blurb we keep; enough to choose, short enough to survive. */\nconst AGENT_BLURB_LIMIT = 140\n\n/**\n * Disambiguation appended to the `question` proxy def. Claude Code ships\n * a built-in `AskUserQuestion` that, when proxied, is disabled via\n * `--disallowedTools`; without an explicit hand-off note models keep\n * reaching for the disabled built-in or fall back to plain text. This\n * states that the proxy is the structured-questions path and summarises\n * the answer shape so the model can act on the result without a second\n * round-trip.\n */\nexport const QUESTION_PROXY_NOTE =\n \"This routes structured questions through opencode's native `question`\" +\n \" tool, which renders a TUI form with the options you provide and\" +\n \" blocks until the operator answers. Claude Code's built-in\" +\n \" AskUserQuestion is disabled in this environment; this proxy is the\" +\n \" ONLY way to ask the operator for a decision or clarification.\" +\n \" Answers come back as arrays of selected labels (set `multiple: true`\" +\n \" to allow more than one). If the operator dismisses the form the call\" +\n \" returns an error — treat that as 'no answer' and stop, do not guess.\" +\n \" Question calls get a 30-minute proxy deadline by default (configurable\" +\n \" via proxyToolTimeoutMs); for long-AFK scenarios prefer fewer,\" +\n \" high-signal questions.\"\n\n/**\n * Disambiguation appended to the `compress` proxy def. Two things the\n * model gets wrong without it: when the reset happens (not mid-turn, so\n * it can keep working after the call), and how much survives it (only\n * the summary, because the fresh spawn is not given the prior transcript).\n */\nexport const COMPRESS_PROXY_NOTE =\n \"The current turn continues normally after this call — finish what you\" +\n \" are doing. The reset happens at the START of the next turn: the\" +\n \" Claude Code session is discarded and a fresh one begins with your\" +\n \" summary as its only prior context. Everything else, including tool\" +\n \" output and files you read, is gone, so write the summary as the\" +\n \" authoritative record. Call this once per compression, when older\" +\n \" resolved work no longer needs full detail.\"\n\n/**\n * Pull *only* the agent-type list out of opencode's live `task` description.\n *\n * jknlsn's original overlaid the whole live description (2.8 KB here) in front\n * of the static def. Live check 2026-07-26 showed that backfires: Claude Code\n * truncates long MCP tool descriptions, and opencode puts the agent list at\n * the *end* (char 2306 of 2858), so the one part the model needs is exactly\n * what gets cut — haiku then guessed `general-purpose`, `default`, and\n * `code-reviewer` (Claude Code's own agent names) and every dispatch failed\n * with \"Unknown agent type\". So: keep the list, drop opencode's preamble\n * (generic delegation advice the model already has), trim each blurb, and let\n * the caller put it first.\n *\n * Returns undefined when the description carries no parsable list, so callers\n * leave the static def alone.\n */\nexport function extractAgentTypeList(\n liveDescription: string | undefined,\n): string | undefined {\n const live = liveDescription?.trim()\n if (!live) return undefined\n const start = live.indexOf(AGENT_TYPES_HEADING)\n if (start === -1) return undefined\n const entries: string[] = []\n for (const raw of live.slice(start).split(\"\\n\")) {\n const match = /^-\\s*([^:]+):\\s*(.+)$/.exec(raw.trim())\n if (!match) continue\n const name = match[1].trim()\n const blurb = match[2].trim()\n entries.push(\n `- ${name}: ${\n blurb.length > AGENT_BLURB_LIMIT\n ? `${blurb.slice(0, AGENT_BLURB_LIMIT).trimEnd()}…`\n : blurb\n }`,\n )\n }\n if (entries.length === 0) return undefined\n return `Valid subagent_type values, from opencode's live registry — anything else fails:\\n${entries.join(\"\\n\")}`\n}\n\n/**\n * Front-load opencode's live agent-type list onto the static `task` proxy def\n * so the model picks a real `subagent_type` instead of guessing a Claude Code\n * name. First, not last: see `extractAgentTypeList` for why position matters.\n * No-op when no list can be extracted (SDK client missing, older opencode) or\n * the `task` def is not among the tools.\n */\nexport function overlayTaskProxyDescription(\n tools: ProxyToolDef[],\n liveDescription: string | undefined,\n): ProxyToolDef[] {\n const agentTypes = extractAgentTypeList(liveDescription)\n if (!agentTypes) return tools\n return tools.map((t) =>\n t.name === \"task\" || t.name === TASK_BATCH_TOOL_NAME\n ? { ...t, description: `${agentTypes}\\n\\n${t.description}` }\n : t,\n )\n}\n\n/**\n * Overlay opencode's live `question` tool description onto the static\n * proxy def, then append the disambiguation note. No-op when the live\n * description is unavailable (older opencode, SDK client missing) — the\n * static def + note stands. Mirrors `overlayTaskProxyDescription`.\n */\nexport function overlayQuestionProxyDescription(\n tools: ProxyToolDef[],\n liveDescription: string | undefined,\n): ProxyToolDef[] {\n const live = liveDescription?.trim()\n if (!live) return tools\n return tools.map((t) =>\n t.name === \"question\"\n ? { ...t, description: `${live}\\n\\n${QUESTION_PROXY_NOTE}` }\n : t,\n )\n}\n\n/**\n * Version gate for the `question` proxy. opencode added a built-in\n * `question` tool (registry id `question`) — on older builds that entry\n * is absent and a forwarded `mcp__opencode_proxy__question` call would\n * resolve to `⚙ invalid` in opencode. Drop the def silently when the\n * live registry does not contain it so the model never sees a dead tool.\n */\nexport function filterQuestionProxyByOpencodeSupport(\n tools: ProxyToolDef[],\n opencodeHasQuestion: boolean,\n): ProxyToolDef[] {\n if (opencodeHasQuestion) return tools\n return tools.filter((t) => t.name !== \"question\")\n}\n\n/** Input fields of one `task`, shared with each `task_batch` item. */\nexport const TASK_INPUT_PROPERTIES = {\n description: {\n type: \"string\",\n description: \"A short (3-5 words) description of the task\",\n },\n prompt: {\n type: \"string\",\n description: \"The task for the agent to perform\",\n },\n subagent_type: {\n type: \"string\",\n description: \"The type of specialized agent to use for this task\",\n },\n task_id: {\n type: \"string\",\n description:\n \"Set this only if you mean to resume a previous task: pass the\" +\n \" prior task_id to continue the same subagent session instead of\" +\n \" creating a fresh one.\",\n },\n command: {\n type: \"string\",\n description: \"The command that triggered this task\",\n },\n background: {\n type: \"boolean\",\n description:\n \"Run the task in the background when supported by opencode\",\n },\n}\n\nexport const DEFAULT_PROXY_TOOLS: ProxyToolDef[] = [\n {\n name: \"bash\",\n description:\n \"Execute a shell command. Routed through opencode's bash tool so\" +\n \" permission prompts flow through opencode's UI.\",\n inputSchema: {\n type: \"object\",\n properties: {\n command: {\n type: \"string\",\n description: \"The shell command to execute.\",\n },\n description: {\n type: \"string\",\n description: \"Short human-readable description of what the command does.\",\n },\n timeout: {\n type: \"number\",\n description: \"Optional timeout in milliseconds.\",\n },\n },\n required: [\"command\"],\n },\n },\n {\n name: \"write\",\n description:\n \"Write a file. Routed through opencode's write tool so permission prompts flow through opencode's UI.\",\n inputSchema: {\n type: \"object\",\n properties: {\n filePath: {\n type: \"string\",\n description: \"The file to write. Absolute paths are preferred.\",\n },\n content: {\n type: \"string\",\n description: \"The full content to write to the file.\",\n },\n },\n required: [\"filePath\", \"content\"],\n },\n },\n {\n name: \"edit\",\n description:\n \"Replace text in an existing file. Routed through opencode's edit tool so permission prompts flow through opencode's UI.\",\n inputSchema: {\n type: \"object\",\n properties: {\n filePath: {\n type: \"string\",\n description: \"The file to edit. Absolute paths are preferred.\",\n },\n oldString: {\n type: \"string\",\n description: \"The exact text to replace.\",\n },\n newString: {\n type: \"string\",\n description: \"The replacement text.\",\n },\n replaceAll: {\n type: \"boolean\",\n description: \"Replace all occurrences instead of just the first one.\",\n },\n },\n required: [\"filePath\", \"oldString\", \"newString\"],\n },\n },\n {\n name: \"webfetch\",\n description:\n \"Fetch content from a URL. Routed through opencode's webfetch tool so\" +\n \" permission prompts flow through opencode's UI. Returns the page\" +\n \" content in the requested format.\",\n inputSchema: {\n type: \"object\",\n properties: {\n url: {\n type: \"string\",\n description: \"The URL to fetch content from. Must start with http:// or https://.\",\n },\n format: {\n type: \"string\",\n enum: [\"text\", \"markdown\", \"html\"],\n description:\n \"The format to return the content in. Defaults to markdown.\",\n },\n timeout: {\n type: \"number\",\n description: \"Optional timeout in seconds (max 120).\",\n },\n },\n required: [\"url\"],\n },\n },\n {\n name: \"task\",\n description:\n \"Launch an opencode subagent to handle a complex multi-step task\" +\n \" autonomously. Routed through opencode's task tool so subagent\" +\n \" orchestration, permission, and lifecycle are handled by opencode.\" +\n \" Use `subagent_type` to pick which configured subagent runs (e.g.\" +\n \" `build`, `general`, `explore`, or any custom subagent declared in\" +\n \" opencode.json). \" +\n TASK_PROXY_NOTE,\n inputSchema: {\n type: \"object\",\n properties: TASK_INPUT_PROPERTIES,\n required: TASK_INPUT_REQUIRED,\n },\n },\n {\n name: TASK_BATCH_TOOL_NAME,\n description:\n \"Launch two or more independent opencode subagents at the same time and\" +\n \" get all their results back together. Put one ordinary task input in\" +\n \" `tasks` for each subagent. \" +\n TASK_BATCH_PROXY_NOTE,\n inputSchema: {\n type: \"object\",\n properties: {\n tasks: {\n type: \"array\",\n minItems: 2,\n description: \"Independent subagent tasks to run concurrently\",\n items: {\n type: \"object\",\n properties: TASK_INPUT_PROPERTIES,\n required: TASK_INPUT_REQUIRED,\n },\n },\n },\n required: [\"tasks\"],\n },\n },\n {\n name: \"question\",\n description:\n \"Ask the operator structured questions with options and receive\" +\n \" their answers back. Routed through opencode's native `question`\" +\n \" tool so the prompt renders as a real TUI form (with options and a\" +\n \" custom-answer field) instead of a plain text turn. Use this when\" +\n \" you need a decision, clarification, or preference from the\" +\n \" operator mid-task. \" +\n QUESTION_PROXY_NOTE,\n inputSchema: {\n type: \"object\",\n properties: {\n questions: {\n type: \"array\",\n description: \"Questions to ask.\",\n items: {\n type: \"object\",\n properties: {\n question: {\n type: \"string\",\n description: \"Complete question.\",\n },\n header: {\n type: \"string\",\n description: \"Very short label (max 30 chars).\",\n },\n options: {\n type: \"array\",\n description: \"Available choices.\",\n items: {\n type: \"object\",\n properties: {\n label: {\n type: \"string\",\n description: \"Display text (1-5 words, concise).\",\n },\n description: {\n type: \"string\",\n description: \"Explanation of choice.\",\n },\n },\n required: [\"label\", \"description\"],\n },\n },\n multiple: {\n type: \"boolean\",\n description:\n \"Allow selecting multiple choices. Defaults to false.\",\n },\n },\n required: [\"question\", \"header\", \"options\"],\n },\n },\n },\n required: [\"questions\"],\n },\n },\n {\n name: \"compress\",\n description:\n \"Replace older conversation detail with a summary you write, then\" +\n \" continue in a fresh Claude Code session. Handled inside the plugin,\" +\n \" so it never prompts the operator. \" +\n COMPRESS_PROXY_NOTE,\n inputSchema: {\n type: \"object\",\n properties: {\n summary: {\n type: \"string\",\n description:\n \"Dense technical summary of the work being compressed: decisions\" +\n \" made, files changed, commands run and their outcomes, and what\" +\n \" is still open. This is the ONLY prior context that survives, so\" +\n \" anything omitted is lost.\",\n },\n },\n required: [\"summary\"],\n },\n },\n]\n\nexport async function createProxyMcpServer(\n tools: ProxyToolDef[] = DEFAULT_PROXY_TOOLS,\n timeoutOverrides?: Record<string, number>,\n interceptors?: Map<string, ProxyToolInterceptor>,\n options: {\n /** Keepalive cadence for streamed replies; a test seam, defaults to `PROXY_KEEPALIVE_MS`. */\n keepaliveMs?: number\n } = {},\n): Promise<ProxyMcpServer> {\n const calls = new EventEmitter()\n const pending = new Map<string, ProxyToolCall>()\n const keepaliveMs = options.keepaliveMs ?? PROXY_KEEPALIVE_MS\n\n // Per-server bearer secret (256 bits). This endpoint executes Bash/Edit/\n // Write through opencode's executor, so an unauthenticated caller on\n // loopback would have arbitrary command execution. The token lives only\n // in this process and in the 0600 MCP config file Claude reads; it is\n // deliberately kept out of the URL, because query strings leak into logs\n // and process listings.\n const authToken = crypto.randomBytes(32).toString(\"hex\")\n const expectedAuth = Buffer.from(`Bearer ${authToken}`)\n // The exact authority we hand to Claude. Set once the ephemeral port is\n // known; compared against the Host header to defeat DNS rebinding.\n let boundAuthority = \"\"\n\n function authOk(req: IncomingMessage): boolean {\n const got = req.headers.authorization\n if (typeof got !== \"string\") return false\n const candidate = Buffer.from(got)\n // timingSafeEqual throws on length mismatch, so length-check first.\n // Length is not secret (the token is fixed-width).\n if (candidate.length !== expectedAuth.length) return false\n return crypto.timingSafeEqual(candidate, expectedAuth)\n }\n\n /**\n * Reject a request without leaving the connection usable.\n *\n * Ending the response alone is not enough. A peer can declare a large\n * Content-Length, send a single byte, take the rejection, and leave the\n * request still arriving — and `server.close()` does not reap connections\n * that are still sending, so a shutdown would hang behind it. Node's\n * default whole-request timeout is five minutes, which is five minutes of\n * a socket held by an unauthenticated caller.\n *\n * `Connection: close` tells Node to close once the response is flushed;\n * destroying the socket on `finish` covers the case where the peer never\n * finishes its body.\n */\n function reject(\n req: IncomingMessage,\n res: ServerResponse,\n statusCode: number,\n reason: string,\n ): void {\n // Every guard below is a measured property of the client we spawn, not a\n // guarantee about future ones. If a later Claude CLI starts sending an\n // Origin header, or a different Content-Type, every proxy call would\n // 403/415 with no other symptom than tools mysteriously not working — so\n // say why, here, once per rejected request. Header VALUES are omitted:\n // this line must never carry the bearer token.\n log.notice(\"proxy-mcp rejected a request\", {\n statusCode,\n reason,\n method: req.method,\n hasAuthorization: typeof req.headers.authorization === \"string\",\n })\n res.statusCode = statusCode\n res.setHeader(\"Connection\", \"close\")\n res.on(\"finish\", () => {\n req.socket?.destroy()\n })\n res.end()\n }\n\n const server = createServer(async (req, res) => {\n if (req.method !== \"POST\" || !req.url?.startsWith(\"/mcp\")) {\n reject(req, res, 404, \"not a POST to /mcp\")\n return\n }\n // Everything below runs BEFORE readBody: an unauthenticated peer must\n // not be able to stream an unbounded body into memory.\n //\n // DNS rebinding: a browser rebound onto this port via an attacker\n // hostname sends that hostname in Host, never the loopback authority we\n // generated. This does NOT block a page posting directly to\n // 127.0.0.1:<port> — such a request carries exactly the expected Host —\n // so it is a rebinding defense specifically, not a browser defense. The\n // Origin and Content-Type guards below, and the token, cover that case.\n if (req.headers.host !== boundAuthority) {\n reject(req, res, 403, \"host header is not the bound authority\")\n return\n }\n // Claude Code 2.1.226 sends no Origin on MCP requests (verified). The MCP\n // transport spec obliges SERVERS to validate Origin; it does not oblige\n // clients to omit it, so this is a measured property of the client we\n // spawn rather than a guarantee about all conforming clients.\n if (req.headers.origin !== undefined) {\n reject(req, res, 403, \"origin header present\")\n return\n }\n // Requiring application/json forces a CORS preflight for cross-origin\n // callers (which then fails), closing the text/plain \"simple request\"\n // bypass that would otherwise allow blind cross-site POSTs.\n const contentType = String(req.headers[\"content-type\"] ?? \"\")\n .split(\";\")[0]\n .trim()\n .toLowerCase()\n if (contentType !== \"application/json\") {\n reject(req, res, 415, \"content-type is not application/json\")\n return\n }\n if (!authOk(req)) {\n reject(req, res, 401, \"missing or invalid bearer token\")\n return\n }\n // Hoist the request id and method so the catch block can echo them\n // in error responses. Without this, a broker rejection (timeout /\n // orphan) on a tools/call lands in the catch with no visible id, and\n // the response goes back with `id: null` which Claude CLI cannot\n // match to the original request. The method is also needed because\n // tools/call errors must be returned as MCP results with isError\n // (not JSON-RPC errors) or Claude CLI rejects them as a \"malformed\n // result that failed schema validation\" (seen live 2026-07-04).\n let requestId: number | string | null = null\n let requestMethod: string | null = null\n // Hoisted for the same reason: once a streamed reply's headers are out,\n // an error must travel down that stream instead of through writeJson\n // (which would try to set headers again and throw inside the catch).\n let reply: ReplyStream | null = null\n try {\n const body = await readBody(req)\n const request = JSON.parse(body) as {\n jsonrpc?: string\n id?: number | string | null\n method?: string\n params?: Record<string, unknown>\n }\n requestId = request?.id ?? null\n requestMethod = typeof request?.method === \"string\" ? request.method : null\n\n if (request?.jsonrpc !== \"2.0\" || typeof request.method !== \"string\") {\n writeJson(res, {\n jsonrpc: \"2.0\",\n id: requestId,\n error: { code: -32600, message: \"Invalid request\" },\n })\n return\n }\n\n log.debug(\"proxy-mcp request\", {\n method: request.method,\n id: request.id,\n })\n\n if (request.method === \"initialize\") {\n writeJson(res, {\n jsonrpc: \"2.0\",\n id: requestId,\n result: {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: { tools: {} },\n serverInfo: {\n name: SERVER_NAME,\n version: \"0.1.0\",\n },\n },\n })\n return\n }\n\n if (request.method === \"notifications/initialized\") {\n res.statusCode = 204\n res.end()\n return\n }\n\n if (request.method === \"tools/list\") {\n writeJson(res, {\n jsonrpc: \"2.0\",\n id: requestId,\n result: {\n tools: tools.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n },\n })\n return\n }\n\n if (request.method === \"tools/call\") {\n const params = request.params ?? {}\n const toolName = String(params.name ?? \"\")\n const input = (params.arguments ?? {}) as Record<string, unknown>\n\n if (!tools.some((t) => t.name === toolName)) {\n // tools/call failures MUST be MCP results with isError, never\n // JSON-RPC error envelopes: Claude CLI validates every tools/call\n // response against the MCP result schema and rejects JSON-RPC\n // errors as malformed (@jknlsn, seen live 2026-07-04).\n writeJson(res, {\n jsonrpc: \"2.0\",\n id: requestId,\n result: {\n content: [{ type: \"text\", text: `Unknown proxy tool: ${toolName}` }],\n isError: true,\n },\n })\n return\n }\n\n if (toolName === TASK_BATCH_TOOL_NAME) {\n const problem = taskBatchInputError(input)\n if (problem) {\n // Same rule as the unknown-tool path: an MCP result with isError,\n // never a JSON-RPC error envelope.\n writeToolCallResult(res, requestId, { kind: \"error\", message: problem })\n return\n }\n }\n\n // Intercepted tools act on plugin state, not on the workspace, so\n // they are answered here and never queued for opencode. The result\n // still goes through the shared MCP envelope below — a JSON-RPC\n // error here would be rejected by Claude CLI exactly like any other\n // tools/call error envelope.\n const interceptor = interceptors?.get(toolName)\n if (interceptor) {\n let intercepted: ProxyToolResult\n try {\n intercepted = await interceptor(input)\n } catch (interceptorError) {\n const message =\n interceptorError instanceof Error\n ? interceptorError.message\n : String(interceptorError)\n log.warn(\"proxy-mcp interceptor failed\", { toolName, error: message })\n intercepted = { kind: \"error\", message }\n }\n writeToolCallResult(res, requestId, intercepted)\n return\n }\n\n const callId = crypto.randomUUID()\n log.info(\"proxy-mcp tool call received\", {\n callId,\n toolName,\n hasInput: input != null,\n sse: acceptsEventStream(req.headers.accept),\n })\n\n // Broker-backed calls can block for as long as a subagent runs. The\n // reply is streamed either way so the client's own HTTP timers never\n // fire on a silent connection: SSE when the client accepts it\n // (headers and a comment now, keepalive comments, the JSON-RPC result\n // as the final event), otherwise a chunked JSON body whose headers go\n // out now and which carries keepalive whitespace until the result.\n // Every guard above has already run, so nothing is flushed for an\n // unauthenticated peer, an unknown tool, or a rejected batch.\n const channel: ProxyCallChannel = { closed: false }\n reply = acceptsEventStream(req.headers.accept)\n ? openEventStream(res, keepaliveMs)\n : openJsonStream(res, keepaliveMs)\n res.once(\"close\", () => {\n reply?.stop()\n if (res.writableFinished) return\n channel.closed = true\n log.notice(\"proxy-mcp client closed a tool call before its result\", {\n callId,\n toolName,\n })\n })\n\n let timer: ProxyDeadline | null = null\n const result = await new Promise<ProxyToolResult>(\n (resolve, reject) => {\n const entry: ProxyToolCall = {\n id: callId,\n toolName,\n input,\n resolve,\n reject,\n channel,\n }\n pending.set(callId, entry)\n const deadlineMs = resolveProxyCallTimeoutMs(\n toolName,\n input,\n timeoutOverrides,\n )\n // No deadline means no timer at all: `setTimeout(fn, 0)` would\n // reject the call on the next tick. The broker applies the same\n // rule to the same resolved value, so the two layers agree.\n if (deadlineMs > PROXY_NO_DEADLINE_MS) {\n // A permission prompt still open in opencode extends it; see\n // `ProxyDeadlineGuard`. The broker logs that, not this layer.\n timer = armProxyDeadline({\n callId,\n toolName,\n deadlineMs,\n onExpire: () => {\n if (!pending.has(callId)) return\n pending.delete(callId)\n log.notice(\"proxy-mcp tool call timed out\", {\n callId,\n toolName,\n deadlineMs,\n })\n reject(buildProxyTimeoutError(toolName, deadlineMs))\n },\n })\n }\n calls.emit(\"call\", entry)\n },\n ).finally(() => {\n timer?.cancel()\n pending.delete(callId)\n })\n\n if (channel.closed) {\n // Nobody is reading. The language model already saw the closed\n // channel and hands the result to Claude another way.\n log.notice(\"proxy-mcp dropping result for a closed tool call\", {\n callId,\n toolName,\n })\n return\n }\n writeToolCallResult(res, requestId, result, reply)\n return\n }\n\n writeJson(res, {\n jsonrpc: \"2.0\",\n id: requestId,\n error: { code: -32601, message: `Unknown method: ${request.method}` },\n })\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n const logFn = isExpectedCleanupError(errorMessage) ? log.notice : log.warn\n logFn(\"proxy-mcp error handling request\", {\n error: errorMessage,\n })\n // Broker rejections (timeouts, orphans, server close) surface here for\n // tools/call requests. Same rule as above: respond with an MCP result\n // carrying isError, never a JSON-RPC error envelope, or Claude CLI\n // rejects the response as schema-invalid.\n if (requestMethod === \"tools/call\") {\n try {\n writeToolCallResult(\n res,\n requestId,\n { kind: \"error\", message: errorMessage },\n reply,\n )\n } catch {\n try {\n res.statusCode = 500\n res.end()\n } catch {}\n }\n return\n }\n try {\n // tools/call already returned above with an MCP result; anything\n // reaching here is a protocol-level method (initialize, tools/list)\n // where a JSON-RPC error is the correct shape.\n writeJson(res, {\n jsonrpc: \"2.0\",\n id: requestId,\n error: {\n code: -32603,\n message: error instanceof Error ? error.message : \"Internal error\",\n },\n })\n } catch {\n try {\n res.statusCode = 500\n res.end()\n } catch {}\n }\n }\n })\n\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject)\n server.listen(0, \"127.0.0.1\", () => {\n server.off(\"error\", reject)\n resolve()\n })\n })\n\n const addr = server.address() as AddressInfo | null\n if (!addr) {\n server.close()\n throw new Error(\"Failed to bind proxy MCP server\")\n }\n\n boundAuthority = `127.0.0.1:${addr.port}`\n const url = `http://${boundAuthority}/mcp`\n\n // NOTE: authToken is deliberately absent from this line and every other\n // log call. The plugin log is written to disk and echoed to the TUI in\n // debug mode; a leaked token there would defeat the whole mechanism.\n log.info(\"proxy-mcp server started\", {\n url,\n tools: tools.map((t) => t.name),\n })\n\n let configFilePath: string | null = null\n\n const api: ProxyMcpServer = {\n url,\n serverName: SERVER_NAME,\n tools,\n authToken,\n calls,\n configPath() {\n if (configFilePath) return configFilePath\n const body = JSON.stringify(\n {\n mcpServers: {\n [SERVER_NAME]: {\n type: \"http\",\n url,\n // Claude CLI replays these headers on every request to this\n // server, which is what lets the handler above reject anyone\n // who did not read this 0600 file.\n headers: { Authorization: `Bearer ${authToken}` },\n timeout: resolveProxyClientCeilingMs(timeoutOverrides),\n },\n },\n },\n null,\n 2,\n )\n const hash = crypto\n .createHash(\"sha256\")\n .update(body)\n .digest(\"hex\")\n .slice(0, 12)\n const outPath = path.join(\n pluginTmpDir(),\n `proxy-${hash}.json`,\n )\n fs.writeFileSync(outPath, body, { encoding: \"utf8\", mode: 0o600 })\n configFilePath = outPath\n return outPath\n },\n pendingCallIds() {\n return [...pending.keys()]\n },\n async close() {\n for (const entry of pending.values()) {\n entry.reject(new Error(SERVER_CLOSED_MESSAGE))\n }\n pending.clear()\n await new Promise<void>((resolve) => {\n server.close(() => resolve())\n })\n if (configFilePath) {\n try {\n fs.unlinkSync(configFilePath)\n } catch {}\n configFilePath = null\n }\n },\n }\n\n return api\n}\n\n/** CLI-ready list of Claude tool names to disable, for each proxied tool. */\nexport function disallowedToolFlags(tools: ProxyToolDef[]): string[] {\n // Map our lowercase MCP tool names to the Claude tool name(s) they replace.\n // `edit` covers both `Edit` and `MultiEdit` because opencode has no\n // MultiEdit equivalent; without disabling MultiEdit, Claude can batch\n // file changes through it and bypass opencode's permission UI.\n // `task` disables Claude CLI's `Agent` tool (its built-in subagent\n // dispatcher) so subagent calls flow through opencode's `task` tool\n // instead — which lets opencode's configured subagent set (`build`,\n // `general`, custom subagents in opencode.json) execute the work\n // under opencode's permission/lifecycle, rather than Claude's\n // internal-only general-purpose / Explore / Plan options.\n const nameMap: Record<string, string[]> = {\n bash: [\"Bash\"],\n read: [\"Read\"],\n write: [\"Write\"],\n edit: [\"Edit\", \"MultiEdit\"],\n glob: [\"Glob\"],\n grep: [\"Grep\"],\n webfetch: [\"WebFetch\"],\n task: [\"Agent\"],\n task_batch: [\"Agent\"],\n // `question` disables Claude Code's built-in `AskUserQuestion` so the\n // structured-questions path flows through opencode's native `question`\n // tool instead — same UI/permission/audit benefits as the other\n // proxies. Without this, the model can call both and the two paths\n // diverge (opencode's form vs the headless deny-and-render fallback).\n question: [\"AskUserQuestion\"],\n }\n const out: string[] = []\n const seen = new Set<string>()\n for (const t of tools) {\n const mapped = nameMap[t.name.toLowerCase()]\n if (!mapped) continue\n for (const claudeTool of mapped) {\n if (seen.has(claudeTool)) continue\n seen.add(claudeTool)\n out.push(claudeTool)\n }\n }\n return out\n}\n\n/**\n * Everything that goes to `--disallowedTools` for one spawn: the built-ins\n * the proxied tools replace, plus the ones the operator named directly.\n *\n * `disallowedToolFlags` can only cover tools the plugin has a proxy for, so\n * a built-in with no equivalent (`NotebookEdit`, and anything Claude Code\n * ships next) is unreachable without `extraDisallowedTools` — issue #26.\n */\nexport function resolveDisallowedTools(options: {\n proxyTools?: ProxyToolDef[] | null\n extraDisallowedTools?: string[]\n disableWebSearch?: boolean\n}): string[] {\n const out: string[] = []\n const seen = new Set<string>()\n const push = (name: string) => {\n const trimmed = name.trim()\n if (!trimmed || seen.has(trimmed)) return\n seen.add(trimmed)\n out.push(trimmed)\n }\n\n for (const name of disallowedToolFlags(options.proxyTools ?? [])) push(name)\n for (const name of options.extraDisallowedTools ?? []) push(String(name))\n if (options.disableWebSearch) push(\"WebSearch\")\n return out\n}\n\n/** The shape of one `client.tool.list()` entry this resolver needs. */\nexport interface OpencodeToolListEntry {\n id: string\n description?: string\n parameters?: unknown\n}\n\n/**\n * Build proxy defs for the opencode tools named in `proxyOpencodeTools`.\n *\n * `resolvedProxyMcpTools` only forwards a tool whose id matches an enabled\n * MCP server (`<server>` or `<server>_<tool>`), so a tool another opencode\n * plugin declares directly matches nothing and is dropped. opencode-dcp's\n * `compress` is the case that motivated this: it is in opencode's registry,\n * dcp tells the model \"you MUST use the `compress` tool now\", and under this\n * provider the model was never offered it. This is the explicit allowlist\n * that forwards such a tool. It is deliberately never automatic: these run\n * inside opencode with the caller's permissions, so which ones cross over is\n * the operator's decision.\n *\n * A name already held by another proxy def wins, and the forwarded entry is\n * dropped with a warning. That is not arbitrary: `ensureProxyServer`\n * registers interceptors by name and an intercepted call is answered\n * in-process, so a forwarded def sharing a name with an intercepted one\n * (`compress` again) could never reach opencode at all. Dropping it loudly\n * is the difference between documented precedence and a silent shadow.\n */\nexport function resolveProxyOpencodeToolDefs(options: {\n requested?: readonly string[]\n items?: readonly OpencodeToolListEntry[]\n taken?: ReadonlySet<string>\n}): ProxyToolDef[] {\n const requested = options.requested ?? []\n if (requested.length === 0) return []\n\n const items = options.items\n if (!items) {\n log.warn(\n \"proxyOpencodeTools is set but opencode's tool registry did not answer;\" +\n \" forwarding nothing this spawn\",\n { requested: requested.map(String) },\n )\n return []\n }\n\n const byLowerId = new Map<string, OpencodeToolListEntry>()\n for (const item of items) {\n const key = item.id.toLowerCase()\n if (!byLowerId.has(key)) byLowerId.set(key, item)\n }\n\n const taken = options.taken ?? new Set<string>()\n const out: ProxyToolDef[] = []\n const seen = new Set<string>()\n const unknown: string[] = []\n const collided: string[] = []\n\n for (const raw of requested) {\n const name = String(raw).trim()\n if (!name) continue\n const item = byLowerId.get(name.toLowerCase())\n if (!item) {\n unknown.push(name)\n continue\n }\n if (taken.has(item.id)) {\n collided.push(item.id)\n continue\n }\n if (seen.has(item.id)) continue\n seen.add(item.id)\n out.push({\n name: item.id,\n description: typeof item.description === \"string\" ? item.description : \"\",\n inputSchema:\n item.parameters && typeof item.parameters === \"object\"\n ? (item.parameters as Record<string, unknown>)\n : { type: \"object\", properties: {} },\n })\n }\n\n // Same reasoning as the `proxyTools` typo warning: an unrecognised name is\n // simply not forwarded, and silence looks from the outside like the option\n // was ignored.\n if (unknown.length > 0) {\n log.warn(\"ignoring unknown proxyOpencodeTools entries\", {\n unknown,\n known: [...byLowerId.values()].map((item) => item.id).join(\", \"),\n })\n }\n if (collided.length > 0) {\n log.warn(\n \"proxyOpencodeTools entry dropped: a proxy tool already holds that name,\" +\n \" and it keeps it\",\n { collided },\n )\n }\n return out\n}\n\n/** One entry of the AI SDK `tools` array opencode hands `doStream`. */\nexport interface ModelToolEntry {\n type?: string\n name?: string\n description?: string\n inputSchema?: unknown\n}\n\n/** What `resolveMcpProxyToolDefs` found, split by the two things callers need. */\nexport interface McpProxyToolResolution {\n /** One def per MCP tool that will be served from the proxy instead. */\n defs: ProxyToolDef[]\n /** Only the servers a def was actually built for. */\n coveredServers: Set<string>\n}\n\n/**\n * Build proxy defs for opencode's MCP-backed tools out of the tool array\n * opencode already passes to `doStream`.\n *\n * The discovery source matters, and it is the whole reason this function\n * exists. The obvious source, `client.tool.list()` behind\n * `/experimental/tool`, enumerates opencode's `ToolRegistry` only: built-ins\n * plus plugin-declared tools. MCP tools are not in that registry on 1.18.31,\n * they are merged into the model's tool set afterwards, so a registry-based\n * match finds nothing however the prefix rule is written. The AI SDK `tools`\n * argument is downstream of that merge, so it is the one place a provider\n * plugin can see them at all.\n *\n * Matching is still by enabled-server prefix, longest name first so\n * `slack_intl_*` resolves to `slack_intl` and not `slack`. That is\n * deliberately narrow: everything else in the array is a built-in or another\n * plugin's tool, and forwarding those wholesale is what the explicit\n * `proxyOpencodeTools` allowlist is for.\n */\nexport function resolveMcpProxyToolDefs(options: {\n serverNames: readonly string[]\n tools?: readonly ModelToolEntry[]\n taken?: ReadonlySet<string>\n}): McpProxyToolResolution {\n const empty: McpProxyToolResolution = { defs: [], coveredServers: new Set() }\n const serverNames = options.serverNames ?? []\n if (serverNames.length === 0) return empty\n\n const tools = options.tools\n if (!tools || tools.length === 0) return empty\n\n const serversByLengthDesc = [...serverNames].sort((a, b) => b.length - a.length)\n const taken = options.taken ?? new Set<string>()\n const defs: ProxyToolDef[] = []\n const coveredServers = new Set<string>()\n const seen = new Set<string>()\n const collided: string[] = []\n\n for (const tool of tools) {\n // opencode only ever puts plain function tools in this array; a\n // provider-defined entry has no opencode executor behind it, so\n // forwarding one would produce a call nothing can answer.\n if (tool?.type !== undefined && tool.type !== \"function\") continue\n const name = typeof tool?.name === \"string\" ? tool.name.trim() : \"\"\n if (!name) continue\n\n const matchedServer = serversByLengthDesc.find(\n (server) => name === server || name.startsWith(`${server}_`),\n )\n if (!matchedServer) continue\n if (seen.has(name)) continue\n if (taken.has(name)) {\n collided.push(name)\n continue\n }\n seen.add(name)\n coveredServers.add(matchedServer)\n defs.push({\n name,\n description: typeof tool.description === \"string\" ? tool.description : \"\",\n inputSchema:\n tool.inputSchema && typeof tool.inputSchema === \"object\"\n ? (tool.inputSchema as Record<string, unknown>)\n : { type: \"object\", properties: {} },\n })\n }\n\n if (collided.length > 0) {\n // WARN, not NOTICE: only warn and error are alwaysStderr in src/logger.ts,\n // and a shadowed MCP tool silently stops being routed through opencode,\n // which is exactly the class of surprise this lane exists to end.\n log.warn(\n \"MCP tool not routed through the proxy: another proxy tool already holds\" +\n \" that name, and it keeps it\",\n { collided },\n )\n }\n return { defs, coveredServers }\n}\n\nfunction readBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = []\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk))\n req.on(\"end\", () => resolve(Buffer.concat(chunks).toString(\"utf8\")))\n req.on(\"error\", reject)\n })\n}\n\n/**\n * The single exit for every `tools/call`, broker-backed or intercepted.\n * Success and failure share one MCP result envelope: a JSON-RPC error for\n * `kind: \"error\"` was rejected by Claude CLI as a \"malformed result that\n * failed schema validation\", so tool failures must surface as\n * `isError: true` instead.\n */\nfunction writeToolCallResult(\n res: ServerResponse,\n requestId: unknown,\n result: ProxyToolResult,\n reply: ReplyStream | null = null,\n): void {\n const text = result.kind === \"error\" ? result.message : result.text\n const isError = result.kind === \"error\" || result.isError === true\n const envelope = {\n jsonrpc: \"2.0\",\n id: requestId ?? null,\n result: {\n content: [{ type: \"text\", text }],\n isError,\n },\n }\n if (reply) {\n reply.finish(envelope)\n return\n }\n writeJson(res, envelope)\n}\n\n/**\n * An in-flight streamed reply whose headers are already on the wire.\n * `finish` writes the JSON-RPC response and ends the body; `stop` only\n * cancels the keepalive, for when the client went away first.\n */\ninterface ReplyStream {\n finish(envelope: unknown): void\n stop(): void\n}\n\n/**\n * Write `ping` every `keepaliveMs` until stopped or the response is gone.\n * Never keeps the host process alive on its own.\n */\nfunction startKeepalive(\n res: ServerResponse,\n keepaliveMs: number,\n ping: string,\n): () => void {\n let timer: ReturnType<typeof setInterval> | null = setInterval(() => {\n if (res.writableEnded || res.destroyed) {\n stop()\n return\n }\n res.write(ping)\n }, keepaliveMs)\n timer.unref?.()\n const stop = () => {\n if (timer) {\n clearInterval(timer)\n timer = null\n }\n }\n return stop\n}\n\n/**\n * SSE reply: the JSON-RPC response goes out as the single `message` event,\n * which is what the MCP Streamable HTTP client expects for a request\n * answered over SSE.\n */\nfunction openEventStream(res: ServerResponse, keepaliveMs: number): ReplyStream {\n res.statusCode = 200\n res.setHeader(\"Content-Type\", \"text/event-stream\")\n res.setHeader(\"Cache-Control\", \"no-cache, no-transform\")\n res.setHeader(\"Connection\", \"keep-alive\")\n res.flushHeaders()\n // Start the response body without waiting for the tool result.\n res.write(\": open\\n\\n\")\n const stop = startKeepalive(res, keepaliveMs, \": keepalive\\n\\n\")\n return {\n stop,\n finish(envelope) {\n stop()\n if (res.writableEnded || res.destroyed) return\n res.end(`event: message\\ndata: ${JSON.stringify(envelope)}\\n\\n`)\n },\n }\n}\n\n/**\n * JSON reply for a client that did not ask for SSE (@broskees' 68ed142,\n * adapted). Headers are flushed at once, which stops the client's header\n * timer, and whitespace is written on the keepalive cadence, which stops its\n * body timer. There is no `Content-Length`, so the body is chunked, and the\n * envelope is written last: whitespace before a JSON value is insignificant\n * (RFC 8259), so the whole body still parses as the one JSON-RPC response,\n * on success and on error alike.\n */\nfunction openJsonStream(res: ServerResponse, keepaliveMs: number): ReplyStream {\n res.statusCode = 200\n res.setHeader(\"Content-Type\", \"application/json\")\n res.setHeader(\"Cache-Control\", \"no-cache, no-transform\")\n res.flushHeaders()\n const stop = startKeepalive(res, keepaliveMs, \" \")\n return {\n stop,\n finish(envelope) {\n stop()\n if (res.writableEnded || res.destroyed) return\n res.end(JSON.stringify(envelope))\n },\n }\n}\n\nfunction writeJson(res: ServerResponse, body: unknown): void {\n if (res.destroyed || res.writableEnded) return\n const payload = JSON.stringify(body)\n res.statusCode = 200\n res.setHeader(\"Content-Type\", \"application/json\")\n res.setHeader(\"Content-Length\", Buffer.byteLength(payload).toString())\n res.end(payload)\n}\n","import * as fs from \"node:fs\"\nimport * as os from \"node:os\"\nimport * as path from \"node:path\"\n\n/**\n * Per-process scratch directory for plugin tmp files (bridged MCP config,\n * proxy server config, etc.). Created lazily on first use and rm'd on\n * normal process exit so we don't leak across runs. PID-isolated so two\n * concurrent opencode processes don't race on the same files.\n *\n * Caveat: `process.on(\"exit\")` does not fire for SIGKILL or unhandled\n * external signals, so abnormal terminations still leak. OS-level tmpdir\n * cleanup (`systemd-tmpfiles`, macOS periodic) handles those eventually.\n */\nconst PLUGIN_TMP_DIR = path.join(\n os.tmpdir(),\n `opencode-claude-code-${process.pid}`,\n)\n\nlet registered = false\n\nexport function pluginTmpDir(): string {\n if (!fs.existsSync(PLUGIN_TMP_DIR)) {\n fs.mkdirSync(PLUGIN_TMP_DIR, { recursive: true })\n }\n if (!registered) {\n registered = true\n process.on(\"exit\", () => {\n try {\n fs.rmSync(PLUGIN_TMP_DIR, { recursive: true, force: true })\n } catch {}\n })\n }\n return PLUGIN_TMP_DIR\n}\n","import { EventEmitter } from \"node:events\"\nimport {\n armProxyDeadline,\n buildProxyTimeoutError,\n PROXY_NO_DEADLINE_MS,\n resolveProxyCallTimeoutMs,\n type ProxyCallChannel,\n type ProxyDeadline,\n type ProxyToolCall,\n type ProxyToolResult,\n} from \"./proxy-mcp.js\"\nimport { log } from \"./logger.js\"\n\nexport interface PendingProxyCall {\n sessionKey: string\n toolCallId: string\n toolName: string\n input: Record<string, unknown>\n /**\n * Liveness of Claude's HTTP request for this call. Once `closed`, a\n * result written to it is lost; the language model then delivers the\n * result as a user message instead. Absent means open.\n */\n channel?: ProxyCallChannel\n /**\n * True once the language model has handed this call to opencode as a\n * tool-call part. A call that is still pending without it was queued\n * while no turn was attached and has to be drained by the next one.\n */\n emitted?: boolean\n}\n\ntype InternalPending = PendingProxyCall & {\n createdAt: number\n /** `PROXY_NO_DEADLINE_MS` (0) when the call has no deadline. */\n deadlineMs: number\n /** Absent when the call has no deadline. */\n timer: ProxyDeadline | null\n /** Stall heartbeat; only armed for calls that have no deadline. */\n stallTimer: ReturnType<typeof setInterval> | null\n /** One-shot \"this is going to run out\" notice; deadline-bearing calls only. */\n deadlineWarnTimer: ReturnType<typeof setTimeout> | null\n resolve(result: ProxyToolResult): void\n reject(error: Error): void\n}\n\n/**\n * How long a call with NO deadline may wait before the broker starts saying\n * so, and how often it repeats afterwards.\n *\n * `task` and `task_batch` have had no deadline since v0.20.0, which is right:\n * every way a call can end is an event the plugin observes, so a wall clock\n * could only ever kill a subagent that was still working. The cost is that a\n * genuinely wedged subagent is now silent forever, with nothing to notice it\n * but the operator. This is the missing half: it never ends a call, it only\n * reports one. Deliberately long, because a real subagent routinely runs\n * minutes and a warning on healthy work is noise. Deadline-bearing calls are\n * not armed at all: their deadline already reports them.\n */\nexport const PROXY_STALL_WARNING_MS = 5 * 60_000\n\n/**\n * Where in a deadline-bearing call's life to say it is going to run out.\n *\n * The heartbeat above deliberately skips these calls, on the reasoning that\n * their deadline already reports them. It does, but only by killing them:\n * the first and last thing you hear is the failure. Measured the hard way on\n * 2026-09-19, when two proxied calls that were still working were rejected at\n * their 10-minute deadline with no prior signal, and the operator had to\n * infer from silence what was happening.\n *\n * So one notice, at 60% of the deadline, saying how long is left. Once, never\n * repeating, because the deadline itself is the next thing that will speak.\n * Calls whose deadline is under `PROXY_DEADLINE_WARNING_MIN_MS` are skipped:\n * on a short deadline the notice and the rejection would arrive together and\n * tell you nothing you are not about to be told anyway.\n */\nexport const PROXY_DEADLINE_WARNING_FRACTION = 0.6\nexport const PROXY_DEADLINE_WARNING_MIN_MS = 60_000\n\n/** Every timer a pending call can hold. Each removal site must use this. */\nfunction clearPendingTimers(pending: InternalPending): void {\n pending.timer?.cancel()\n if (pending.stallTimer) clearInterval(pending.stallTimer)\n if (pending.deadlineWarnTimer) clearTimeout(pending.deadlineWarnTimer)\n}\n\n/** One pending call, flattened for `/claude-code-doctor`. */\nexport interface PendingProxyCallSnapshot {\n sessionKey: string\n toolCallId: string\n toolName: string\n ageMs: number\n deadlineMs: number\n emitted: boolean\n channelClosed: boolean\n}\n\n// Primary index: callId -> pending. Tool call IDs are UUIDs produced by\n// proxy-mcp, so they are globally unique across sessions.\nconst pendingByCallId = new Map<string, InternalPending>()\n// Reverse index: sessionKey -> set of callIds, so the language model can\n// drain or reject every pending call for one Claude subprocess at once.\nconst callIdsBySession = new Map<string, Set<string>>()\n\nconst emitter = new EventEmitter()\n\nfunction eventName(sessionKey: string) {\n return `pending:${sessionKey}`\n}\n\nfunction indexAdd(sessionKey: string, callId: string) {\n let s = callIdsBySession.get(sessionKey)\n if (!s) {\n s = new Set()\n callIdsBySession.set(sessionKey, s)\n }\n s.add(callId)\n}\n\nfunction indexRemove(sessionKey: string, callId: string) {\n const s = callIdsBySession.get(sessionKey)\n if (!s) return\n s.delete(callId)\n if (s.size === 0) callIdsBySession.delete(sessionKey)\n}\n\nexport function onPendingProxyCall(\n sessionKey: string,\n handler: (call: PendingProxyCall) => void,\n): () => void {\n const name = eventName(sessionKey)\n emitter.on(name, handler)\n return () => emitter.off(name, handler)\n}\n\nexport function queuePendingProxyCall(\n sessionKey: string,\n call: ProxyToolCall,\n timeoutOverrides?: Record<string, number>,\n /** Test seam, same shape as `createProxyMcpServer`'s `keepaliveMs`. */\n stallWarningMs: number = PROXY_STALL_WARNING_MS,\n /** Test seam: lower it so a short test deadline still warns. */\n deadlineWarningMinMs: number = PROXY_DEADLINE_WARNING_MIN_MS,\n): PendingProxyCall {\n // Defensive: if this exact callId is somehow already pending (UUID\n // collision or retry storm), replace it cleanly so we never leak two\n // entries for the same id.\n const previous = pendingByCallId.get(call.id)\n if (previous) {\n clearPendingTimers(previous)\n previous.reject(\n new Error(`Replaced pending proxy call ${call.id} with a fresh one`),\n )\n pendingByCallId.delete(call.id)\n indexRemove(previous.sessionKey, call.id)\n }\n\n const deadlineMs = resolveProxyCallTimeoutMs(\n call.toolName,\n call.input,\n timeoutOverrides,\n )\n\n // Same rule as the proxy-mcp handler: a call with no deadline gets no timer\n // (a zero-delay timer would fire on the next tick). It stays pending until\n // a result, an abort, the next turn's orphan sweep, or its process going.\n // A permission prompt still open in opencode extends the deadline instead of\n // ending the call (`ProxyDeadlineGuard` in proxy-mcp.ts): the time an\n // operator spends answering it is not the tool being slow.\n const timer =\n deadlineMs > PROXY_NO_DEADLINE_MS\n ? armProxyDeadline({\n callId: call.id,\n toolName: call.toolName,\n deadlineMs,\n logExtension: true,\n onExpire: () => {\n const current = pendingByCallId.get(call.id)\n if (!current) return\n pendingByCallId.delete(call.id)\n indexRemove(current.sessionKey, call.id)\n clearPendingTimers(current)\n current.reject(buildProxyTimeoutError(call.toolName, deadlineMs))\n log.notice(\"timed out pending proxy call\", {\n sessionKey: current.sessionKey,\n toolCallId: call.id,\n toolName: call.toolName,\n deadlineMs,\n })\n },\n })\n : null\n\n // A call with no deadline has nothing that will ever report it, so it gets\n // a heartbeat instead. WARN on purpose: only warn and error are always on\n // stderr (see `src/logger.ts`), and a NOTICE nobody sees outside debug mode\n // would defeat the point of the line existing at all.\n const stallTimer =\n deadlineMs === PROXY_NO_DEADLINE_MS && stallWarningMs > 0\n ? setInterval(() => {\n const current = pendingByCallId.get(call.id)\n if (!current) return\n log.warn(\"proxy call still waiting, no deadline\", {\n sessionKey: current.sessionKey,\n toolCallId: current.toolCallId,\n toolName: current.toolName,\n waitedMs: Date.now() - current.createdAt,\n emitted: current.emitted === true,\n channelClosed: current.channel?.closed === true,\n note: \"nothing will time this out; it ends when opencode returns a result, you abort, you send another message, or the claude process goes\",\n })\n }, stallWarningMs)\n : null\n // Never hold opencode's process open for a heartbeat.\n stallTimer?.unref?.()\n\n // The other half: a call that DOES have a deadline says so before the\n // deadline takes it, rather than only by dying. Same WARN reasoning, and\n // one-shot, since the rejection is the next thing that will report.\n const warnAtMs = Math.floor(deadlineMs * PROXY_DEADLINE_WARNING_FRACTION)\n const deadlineWarnTimer =\n deadlineMs >= deadlineWarningMinMs && deadlineMs > 0 && warnAtMs > 0\n ? setTimeout(() => {\n const current = pendingByCallId.get(call.id)\n if (!current) return\n const waitedMs = Date.now() - current.createdAt\n log.warn(\"proxy call still waiting, deadline approaching\", {\n sessionKey: current.sessionKey,\n toolCallId: current.toolCallId,\n toolName: current.toolName,\n waitedMs,\n deadlineMs,\n remainingMs: Math.max(0, deadlineMs - waitedMs),\n emitted: current.emitted === true,\n channelClosed: current.channel?.closed === true,\n note: \"it will be rejected when the deadline passes unless opencode is still serving it (a permission prompt is waiting); raise this tool's proxyToolTimeoutMs if the work is legitimately this long\",\n })\n }, warnAtMs)\n : null\n deadlineWarnTimer?.unref?.()\n\n const pending: InternalPending = {\n sessionKey,\n toolCallId: call.id,\n toolName: call.toolName,\n input: call.input,\n channel: call.channel,\n createdAt: Date.now(),\n deadlineMs,\n timer,\n stallTimer,\n deadlineWarnTimer,\n resolve: call.resolve,\n reject: call.reject,\n }\n pendingByCallId.set(call.id, pending)\n indexAdd(sessionKey, call.id)\n emitter.emit(eventName(sessionKey), pending)\n log.info(\"queued pending proxy call\", {\n sessionKey,\n toolCallId: call.id,\n toolName: call.toolName,\n })\n return pending\n}\n\n/** Record that opencode has been given this call as a tool-call part. */\nexport function markPendingProxyCallEmitted(toolCallId: string): void {\n const pending = pendingByCallId.get(toolCallId)\n if (pending) pending.emitted = true\n}\n\n/** True when Claude's request for this call is gone (see `channel`). */\nexport function isPendingProxyCallChannelClosed(\n call: PendingProxyCall,\n): boolean {\n return call.channel?.closed === true\n}\n\n/** One pending call by id, for the deadline guard, which knows only the id. */\nexport function findPendingProxyCall(toolCallId: string): PendingProxyCall | undefined {\n return pendingByCallId.get(toolCallId)\n}\n\nexport function getPendingProxyCalls(sessionKey: string): PendingProxyCall[] {\n const s = callIdsBySession.get(sessionKey)\n if (!s || s.size === 0) return []\n const out: PendingProxyCall[] = []\n for (const id of s) {\n const p = pendingByCallId.get(id)\n if (p) out.push(p)\n }\n return out\n}\n\n/**\n * Every call the broker is currently holding, across all sessions, with how\n * long it has waited and when it gives up. Read-only view for the doctor\n * report; deliberately carries no `input`, since a pending call's arguments\n * can be a whole file's contents.\n */\nexport function snapshotPendingProxyCalls(now = Date.now()): PendingProxyCallSnapshot[] {\n const out: PendingProxyCallSnapshot[] = []\n for (const pending of pendingByCallId.values()) {\n out.push({\n sessionKey: pending.sessionKey,\n toolCallId: pending.toolCallId,\n toolName: pending.toolName,\n ageMs: Math.max(0, now - pending.createdAt),\n deadlineMs: pending.deadlineMs,\n emitted: pending.emitted === true,\n channelClosed: pending.channel?.closed === true,\n })\n }\n return out\n}\n\nexport function resolvePendingProxyCallById(\n toolCallId: string,\n result: ProxyToolResult,\n): boolean {\n const pending = pendingByCallId.get(toolCallId)\n if (!pending) return false\n pendingByCallId.delete(toolCallId)\n indexRemove(pending.sessionKey, toolCallId)\n clearPendingTimers(pending)\n pending.resolve(result)\n log.info(\"resolved pending proxy call\", {\n sessionKey: pending.sessionKey,\n toolCallId: pending.toolCallId,\n toolName: pending.toolName,\n })\n return true\n}\n\nexport function rejectPendingProxyCallById(\n toolCallId: string,\n error: Error,\n): boolean {\n const pending = pendingByCallId.get(toolCallId)\n if (!pending) return false\n pendingByCallId.delete(toolCallId)\n indexRemove(pending.sessionKey, toolCallId)\n clearPendingTimers(pending)\n pending.reject(error)\n // Rejection is the broker's cleanup mechanism — fires on timeouts, orphans,\n // stream closes, etc. None are user-actionable. File-log them at NOTICE so\n // the audit trail is intact; rely on caller sites to decide TUI visibility.\n log.notice(\"rejected pending proxy call\", {\n sessionKey: pending.sessionKey,\n toolCallId: pending.toolCallId,\n toolName: pending.toolName,\n error: error.message,\n })\n return true\n}\n\nexport function rejectAllPendingProxyCallsForSession(\n sessionKey: string,\n error: Error,\n): number {\n const s = callIdsBySession.get(sessionKey)\n if (!s) return 0\n const ids = [...s]\n let count = 0\n for (const id of ids) {\n if (rejectPendingProxyCallById(id, error)) count++\n }\n return count\n}\n","/**\n * Per-session state for the opt-in `compress` proxy tool.\n *\n * Keyed by session key (the same `cwd::modelId::scope::affinity` string\n * session-manager uses). When Claude calls the intercepted `compress` tool\n * the summary is stored here and the session is marked for restart. The\n * next `doStream` turn consumes that mark, evicts the running child and its\n * Claude session id, and the fresh spawn gets the summary prepended to its\n * appended system prompt.\n *\n * The summary deliberately survives `deleteClaudeSessionId()`: the restart\n * path calls it, so clearing there would wipe the summary microseconds\n * before the new spawn reads it (the original fork version did exactly\n * that, which made the whole feature a no-op). It is dropped when a new\n * opencode conversation starts on the same key, and by the entry cap below.\n */\n\nimport { log } from \"./logger.js\"\n\ninterface CompressionState {\n summary: string\n restartPending: boolean\n}\n\n/**\n * Session keys are bounded in practice by workspaces × models, and each\n * entry is one summary string, but a long-lived opencode process that\n * hops workspaces should not accumulate them forever.\n */\nconst MAX_COMPRESSION_ENTRIES = 32\n\nconst compressions = new Map<string, CompressionState>()\n\n/**\n * Record a summary and mark the session for restart. Storing and marking\n * are one event on purpose: a stored summary that never resets the session\n * would silently do nothing.\n */\nexport function storeCompressionSummary(sessionKey: string, summary: string): void {\n compressions.set(sessionKey, { summary, restartPending: true })\n while (compressions.size > MAX_COMPRESSION_ENTRIES) {\n const oldest = compressions.keys().next()\n if (oldest.done) break\n compressions.delete(oldest.value)\n log.info(\"compression store evicted oldest entry\", { sessionKey: oldest.value })\n }\n}\n\nexport function getCompressionSummary(sessionKey: string): string | undefined {\n return compressions.get(sessionKey)?.summary\n}\n\n/**\n * True once per compress call, for the turn that performs the reset. The\n * summary is kept: it is the prior context for every spawn that follows,\n * until a new conversation clears it.\n */\nexport function consumeCompressionRestart(sessionKey: string): boolean {\n const state = compressions.get(sessionKey)\n if (!state?.restartPending) return false\n state.restartPending = false\n return true\n}\n\nexport function clearCompression(sessionKey: string): void {\n compressions.delete(sessionKey)\n}\n","import { randomUUID } from \"node:crypto\"\nimport type { ChildProcess } from \"node:child_process\"\nimport { cliSupportsSideQuestion, type CliVersion } from \"./cli-version.js\"\nimport type { ActiveProcess } from \"./session-manager.js\"\n\ntype SideQuestionProcess = Pick<ActiveProcess, \"proc\" | \"lineEmitter\">\n\nexport interface SideQuestionResult {\n response: string\n synthetic: boolean\n}\n\nexport interface SideQuestionOptions {\n cliVersion: CliVersion | null\n interactive?: boolean\n abortSignal?: AbortSignal\n timeoutMs?: number\n history?: readonly { question: string; response: string }[]\n}\n\nexport interface SideQuestionExchange {\n question: string\n response: string\n}\n\nconst MAX_HISTORY_EXCHANGES = 20\n\nexport const SIDE_QUESTION_USAGE =\n \"Usage: /btw <question>. Ask a side question about the current conversation without adding it to the main context.\"\n\nconst pendingProcesses = new WeakSet<ChildProcess>()\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n}\n\n/**\n * opencode appends its own `<system-reminder>` blocks to the user message, as\n * extra text parts on the same message. They instruct a normal turn and are not\n * part of what the operator typed after `/btw`, so they must not travel with the\n * aside: a plan-mode reminder alone is over 1.5 KB, and measured live it both\n * steered the answer and kept a bare `/btw` from ever looking empty.\n *\n * Blocks are removed wherever they sit rather than by matching a whole part,\n * because a harness may append its own trailing metadata after one (opencode-dcp\n * adds a `<dcp-message-id>` marker), which an end-anchored check would miss.\n */\nconst SYSTEM_REMINDER_BLOCK = /<system-reminder>[\\s\\S]*?<\\/system-reminder>/g\n\nexport function parseSideQuestionContent(content: unknown): { question: string } | null {\n let text: string\n if (typeof content === \"string\") {\n text = content\n } else if (Array.isArray(content)) {\n const parts: string[] = []\n for (const part of content) {\n if (!isRecord(part) || part.type !== \"text\" || typeof part.text !== \"string\") return null\n parts.push(part.text)\n }\n text = parts.join(\"\\n\")\n } else {\n return null\n }\n const match = /^\\/btw(?:\\s+([\\s\\S]*))?$/.exec(text.replace(SYSTEM_REMINDER_BLOCK, \"\").trim())\n return match ? { question: (match[1] ?? \"\").trim() } : null\n}\n\n/** Do not replay a historical /btw during an assistant/tool continuation. */\nexport function parseSideQuestion(\n prompt: readonly { role: string; content: unknown }[],\n): { question: string } | null {\n const latest = prompt.at(-1)\n return latest?.role === \"user\" ? parseSideQuestionContent(latest.content) : null\n}\n\nfunction assistantText(content: unknown): string {\n if (typeof content === \"string\") return content.trim()\n if (!Array.isArray(content)) return \"\"\n const parts: string[] = []\n for (const part of content) {\n if (isRecord(part) && part.type === \"text\" && typeof part.text === \"string\") parts.push(part.text)\n }\n return parts.join(\"\\n\").trim()\n}\n\n/**\n * Earlier `/btw` exchanges in this conversation, oldest first, for the\n * control request's `history` so follow-ups can refer to previous asides.\n * The final user message is the current question and is left out.\n */\nexport function collectSideQuestionHistory(\n prompt: readonly { role: string; content: unknown }[],\n): SideQuestionExchange[] {\n const history: SideQuestionExchange[] = []\n for (let index = 0; index < prompt.length - 1; index++) {\n const message = prompt[index]\n if (message.role !== \"user\") continue\n const aside = parseSideQuestionContent(message.content)\n if (!aside?.question) continue\n const reply = prompt[index + 1]\n if (reply.role !== \"assistant\") continue\n const response = assistantText(reply.content)\n if (!response || response === SIDE_QUESTION_USAGE) continue\n history.push({ question: aside.question, response })\n }\n return history.slice(-MAX_HISTORY_EXCHANGES)\n}\n\nexport function isSideQuestionPending(activeProcess: SideQuestionProcess): boolean {\n return pendingProcesses.has(activeProcess.proc)\n}\n\n/**\n * Call before the normal stdout line/buffer dispatch. Only a response with an\n * active request-ID listener is consumed. Progress and unrelated lines retain\n * their existing routing; the helper never subscribes to the shared `line` event.\n */\nexport function dispatchSideQuestionResponse(\n activeProcess: SideQuestionProcess,\n line: string,\n): boolean {\n if (!pendingProcesses.has(activeProcess.proc)) return false\n let message: unknown\n try {\n message = JSON.parse(line)\n } catch {\n return false\n }\n if (!isRecord(message) || message.type !== \"control_response\") return false\n const response = message.response\n if (!isRecord(response) || typeof response.request_id !== \"string\") return false\n return activeProcess.lineEmitter.emit(`side-question:${response.request_id}`, response)\n}\n\n/**\n * Uses an existing headless process, never a user envelope or a new spawn.\n * The process may be mid-turn: Claude Code answers `side_question` on a\n * separate advisor call while the main loop keeps running (measured live on\n * 2.1.258 with the turn blocked on a held MCP tool). Only one aside per\n * process is in flight at a time; responses are matched by request id ahead\n * of the normal stdout routing, so a streaming turn never sees them.\n */\nexport async function requestSideQuestion(\n activeProcess: SideQuestionProcess,\n question: string,\n options: SideQuestionOptions,\n): Promise<SideQuestionResult> {\n question = question.trim()\n if (!question) return { response: SIDE_QUESTION_USAGE, synthetic: true }\n options.abortSignal?.throwIfAborted()\n const { proc, lineEmitter } = activeProcess\n if (options.interactive || !proc.stdout) {\n throw new Error(\"/btw requires the headless Claude Code transport; interactive sessions are not supported.\")\n }\n if (!cliSupportsSideQuestion(options.cliVersion)) {\n throw new Error(\"/btw requires Claude Code CLI 2.1.258 or newer (the oldest verified version).\")\n }\n if (pendingProcesses.has(proc)) {\n throw new Error(\"Wait for the current /btw to finish before asking another.\")\n }\n const stdin = proc.stdin\n if (proc.killed || proc.exitCode != null || proc.signalCode != null ||\n !stdin || stdin.destroyed || stdin.writableEnded || !stdin.writable) {\n throw new Error(\"/btw requires a live Claude Code session with writable stdin.\")\n }\n const timeoutMs = options.timeoutMs ?? 120_000\n if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2_147_483_647) {\n throw new Error(\"/btw timeoutMs must be a positive 32-bit integer.\")\n }\n const requestId = randomUUID()\n const request = JSON.stringify({\n type: \"control_request\",\n request_id: requestId,\n request: {\n subtype: \"side_question\",\n question,\n ...(options.history === undefined ? {} : { history: options.history }),\n },\n })\n\n pendingProcesses.add(proc)\n return new Promise<SideQuestionResult>((resolve, reject) => {\n const event = `side-question:${requestId}`\n let settled = false\n let sent = false\n let cancelPending = false\n\n const cleanup = (): void => {\n clearTimeout(timer)\n lineEmitter.off(event, onResponse)\n lineEmitter.off(\"close\", onClose)\n lineEmitter.off(\"error\", onError)\n proc.off(\"exit\", onClose)\n proc.off(\"close\", onClose)\n proc.off(\"error\", onError)\n if (!cancelPending) stdin.off(\"error\", onError)\n options.abortSignal?.removeEventListener(\"abort\", onAbort)\n pendingProcesses.delete(proc)\n }\n const fail = (error: unknown, cancel = false): void => {\n if (settled) return\n settled = true\n if (cancel && sent && !stdin.destroyed && !stdin.writableEnded && stdin.writable) {\n try {\n cancelPending = true\n stdin.write(\n JSON.stringify({ type: \"control_cancel_request\", request_id: requestId }) + \"\\n\",\n () => {\n // A failed write emits `error` after its callback. Keep the pipe\n // listener through that event without delaying abort/timeout.\n queueMicrotask(() => stdin.off(\"error\", onError))\n },\n )\n } catch {\n cancelPending = false\n // Preserve the original abort/timeout even if the child has gone away.\n }\n }\n cleanup()\n reject(error)\n }\n const onClose = (): void => fail(new Error(\"Claude Code closed before answering /btw.\"))\n const onError = (error: Error): void => fail(error)\n const onAbort = (): void => fail(\n options.abortSignal?.reason ?? new DOMException(\"/btw was aborted.\", \"AbortError\"),\n true,\n )\n const onResponse = (response: Record<string, unknown>): void => {\n if (settled || response.request_id !== requestId) return\n if (response.subtype === \"error\") {\n fail(new Error(typeof response.error === \"string\" ? response.error : \"Claude Code rejected /btw.\"))\n return\n }\n const result = response.response\n if (response.subtype !== \"success\" || !isRecord(result) ||\n typeof result.response !== \"string\" || typeof result.synthetic !== \"boolean\") {\n fail(new Error(\"Claude Code returned an invalid /btw response.\"))\n return\n }\n settled = true\n cleanup()\n resolve({ response: result.response, synthetic: result.synthetic })\n }\n const timer = setTimeout(() => {\n fail(new Error(`/btw timed out after ${timeoutMs}ms.`), true)\n }, timeoutMs)\n\n lineEmitter.on(event, onResponse)\n lineEmitter.on(\"close\", onClose)\n lineEmitter.on(\"error\", onError)\n proc.on(\"exit\", onClose)\n proc.on(\"close\", onClose)\n proc.on(\"error\", onError)\n stdin.on(\"error\", onError)\n options.abortSignal?.addEventListener(\"abort\", onAbort, { once: true })\n if (options.abortSignal?.aborted) {\n onAbort()\n return\n }\n try {\n sent = true\n stdin.write(request + \"\\n\")\n } catch (error) {\n fail(error)\n }\n })\n}\n","import { detectCliVersion } from \"./cli-version.js\"\nimport { log } from \"./logger.js\"\nimport { getOpencodeClient } from \"./runtime-status.js\"\nimport { findActiveProcessBySessionId, type ActiveProcess } from \"./session-manager.js\"\nimport {\n collectSideQuestionHistory,\n isSideQuestionPending,\n requestSideQuestion,\n SIDE_QUESTION_USAGE,\n type SideQuestionExchange,\n type SideQuestionResult,\n} from \"./side-question.js\"\n\n/**\n * `/btw`: a side question that is answered while the main turn keeps running,\n * and whose exchange is kept in the conversation where it was asked.\n *\n * opencode's TUI sends every slash command to the server the moment it is\n * typed, busy or not (`tui/component/prompt/index.tsx`), so the\n * `command.execute.before` hook fires immediately. The user message the\n * command produces is what gets held back (\"Queued\") until the running turn\n * ends, and opencode's loop then runs it as a step of its own: the loop only\n * exits when the newest assistant message answers the newest user message\n * (`session/prompt.ts`, `lastAssistant.parentID === lastUser.id`).\n *\n * So the hook sends the question to the conversation's live `claude` process\n * as a `side_question` control request right away (Claude Code answers those\n * on a separate advisor call, concurrently with a running turn, from the\n * conversation's context) and remembers the pending answer per session. Where\n * the answer lands then depends on what is open when it arrives:\n * 1. a turn is streaming, so the answer is written into that turn's own\n * reply as its own text block and the `/btw` message is dropped. The\n * operator reads it in place, the moment it is ready, and it stays;\n * 2. nothing is open to write to, so the `/btw` message is held until the\n * turn ends. It then reaches the aside branch in\n * `claude-code-language-model.ts`, which takes the remembered answer and\n * emits it as that message's reply, at no cost;\n * 3. the conversation was idle all along, so the message runs at once and\n * case 2 is all that happens.\n * Every one of those lands in the conversation, so none of them toasts: a\n * toast expires and the operator asked for the answer to stay. The two that\n * remain are the paths where nothing reaches the conversation at all, a bare\n * `/btw` and a turn that never ended, where a toast is the only feedback left.\n * `filterSideQuestionHistory` keeps every `/btw` pair out of Claude's prompt,\n * `INLINE_ASIDE_MARKER` does the same for case 1's block, and the control\n * request never touches Claude's own transcript, so an aside is persisted for\n * the operator only.\n */\n\ntype SdkResult<T = unknown> = Promise<{ data?: T; error?: unknown }>\n\nexport interface BtwToast {\n title?: string\n message: string\n variant: \"info\" | \"success\" | \"warning\" | \"error\"\n duration?: number\n}\n\nexport interface BtwSdkMessage {\n info?: { role?: string }\n parts?: unknown[]\n}\n\nexport interface BtwSdkClient {\n session?: {\n messages?: (options: { path: { id: string } }) => SdkResult<BtwSdkMessage[]>\n /** `GET /session/status`: sessions missing from the map are idle. */\n status?: () => SdkResult<Record<string, { type: string }>>\n }\n tui?: {\n showToast?: (options: { body: BtwToast }) => SdkResult\n }\n}\n\nexport interface BtwCommandInput {\n command: string\n sessionID: string\n arguments: string\n}\n\n/** Every wait the hook can make, so tests do not have to sit through them. */\nexport interface BtwWaitOptions {\n /** How often to re-read opencode's session status. */\n pollMs?: number\n /** Cap on holding the `/btw` message back while a turn runs. */\n timeoutMs?: number\n /** Cap on treating an idle-looking status as not yet registered. */\n settleMs?: number\n /** Cap on waiting for the running turn's `claude` process to be tagged. */\n spawnWaitMs?: number\n /** How often to retry writing the answer into the running turn. */\n inlinePollMs?: number\n /** Cap on waiting for a stream to write the answer into. */\n inlineWaitMs?: number\n}\n\n/** Thrown to make opencode drop the prompt when there is nothing worth keeping. */\nexport class BtwHandledError extends Error {\n override readonly name = \"BtwHandledError\"\n constructor(message = \"/btw was handled by the claude-code plugin; nothing to add to this conversation.\") {\n super(message)\n }\n}\n\nexport const BTW_COMMAND_DESCRIPTION =\n \"Ask a side question in the live Claude Code session without changing its context\"\n\nexport const BTW_NO_SESSION_MESSAGE =\n \"/btw needs a live Claude Code session in this conversation. Send a normal message with a Claude Code model first, then ask again.\"\n\nexport const BTW_INLINE_HANDLED_MESSAGE =\n \"/btw was answered inside the running turn; nothing to add to this conversation.\"\n\nexport const BTW_TURN_TOO_LONG_MESSAGE =\n \"/btw gave up waiting for this turn to end. Ask again once it is over.\"\n\nconst IDLE_POLL_MS = 500\nconst IDLE_WAIT_MAX_MS = 30 * 60_000\n/**\n * How long a single status read is allowed to be wrong. opencode registers\n * the turn a moment after the TUI sends the command, and a session missing\n * from `GET /session/status` reads as idle, so a `/btw` typed inside that gap\n * would decide the conversation is free and let its message queue.\n */\nconst BUSY_SETTLE_MS = 1_500\n/**\n * How long to wait for the turn's `claude` process to appear. doStream tags\n * the process only once it attaches its line listener, which is after the\n * whole spawn path, so the first `/btw` of a conversation regularly arrives\n * before there is anything to ask. Bounded, because the running turn may\n * belong to another provider and then no process is ever coming.\n */\nconst SPAWN_WAIT_MAX_MS = 30_000\n\nconst INLINE_POLL_MS = 200\n/**\n * How long to keep trying to write into the turn. A turn is a run of streams,\n * not one: every proxy tool call ends the current stream and opencode opens\n * the next one with the tool's result, so an answer that arrives inside that\n * gap has nothing to write to yet and has to wait for the next stream.\n */\nconst INLINE_WAIT_MAX_MS = 20_000\n\nconst PENDING_ANSWER_TTL_MS = 10 * 60_000\nconst PENDING_ANSWER_CAP = 32\n\ninterface PendingAnswer {\n question: string\n answer: Promise<SideQuestionResult>\n at: number\n}\n\n/** Answers the hook requested ahead of the queued prompt, one per opencode session. */\nconst pendingAnswers = new Map<string, PendingAnswer>()\n\nexport function rememberSideQuestionAnswer(\n sessionID: string,\n question: string,\n answer: Promise<SideQuestionResult>,\n now = Date.now(),\n): void {\n for (const [id, entry] of pendingAnswers) {\n if (now - entry.at > PENDING_ANSWER_TTL_MS) pendingAnswers.delete(id)\n }\n pendingAnswers.delete(sessionID)\n while (pendingAnswers.size >= PENDING_ANSWER_CAP) {\n const oldest = pendingAnswers.keys().next().value\n if (oldest === undefined) break\n pendingAnswers.delete(oldest)\n }\n pendingAnswers.set(sessionID, { question: question.trim(), answer, at: now })\n}\n\n/**\n * The answer the hook already requested for this session, if it was for this\n * question and is still fresh. Taking it consumes it: a later `/btw` with the\n * same text asks again rather than replaying a stale answer.\n *\n * The question the turn parses may be longer than what the hook saw: a\n * harness can append trailing metadata to the message text (opencode-dcp adds\n * a `<dcp-message-id>` marker), so the hook's question only has to be a prefix.\n * Measured live: an exact match missed, the turn asked again, and the\n * single-flight guard refused it as a second concurrent aside.\n */\nexport function takeSideQuestionAnswer(\n sessionID: string,\n question: string,\n now = Date.now(),\n): Promise<SideQuestionResult> | undefined {\n const entry = pendingAnswers.get(sessionID)\n if (!entry) return undefined\n pendingAnswers.delete(sessionID)\n if (!question.trim().startsWith(entry.question) || now - entry.at > PENDING_ANSWER_TTL_MS) return undefined\n return entry.answer\n}\n\n/** Test seam. */\nexport function clearPendingSideQuestionAnswers(): void {\n pendingAnswers.clear()\n}\n\n/**\n * Header of the block an aside writes into the running turn's own reply, and\n * the marker `message-builder` strips by when a transcript has to be rebuilt\n * for a fresh Claude process. Kept as the first characters of its own text\n * part so the strip is exact rather than a guess at where the block ends.\n */\nexport const INLINE_ASIDE_MARKER = \"▌ **btw:**\"\n\n/**\n * Markers of blocks written before the bar replaced the blockquote. Only the\n * strip reads these: a conversation that already holds an old aside still has\n * to keep it out of a rebuilt transcript.\n */\nexport const LEGACY_INLINE_ASIDE_MARKERS = [\"> **btw:**\"]\n\n/**\n * A literal bar on every line, blank ones included, so the aside reads as one\n * block down its whole height.\n *\n * The obvious alternative, a markdown blockquote, was tried first and is why\n * this exists: opencode renders assistant text with OpenTUI's markdown, which\n * draws a blockquote's left border in the `conceal` scope's colour, not the\n * theme's `markdownBlockQuote`. That border is dim by design and there is no\n * per-block way to change it, so the bar has to be text the plugin emits.\n * Line breaks survive because OpenTUI renders a paragraph from `token.raw`,\n * verbatim, rather than reflowing it.\n */\nfunction barEveryLine(text: string): string {\n return text\n .split(\"\\n\")\n .map((line) => (line.trim() === \"\" ? \"▌\" : `▌ ${line}`))\n .join(\"\\n\")\n}\n\nfunction oneLine(question: string): string {\n return question.replace(/\\s+/g, \" \").trim()\n}\n\nfunction asideHeader(question: string): string {\n return `${INLINE_ASIDE_MARKER} ${oneLine(question)}`\n}\n\nexport function formatInlineAside(question: string, answer: string): string {\n return `\\n${asideHeader(question)}\\n▌\\n${barEveryLine(answer.trim())}\\n`\n}\n\n/**\n * The receipt's trailing note. Past tense, because the block stays in the\n * conversation and an \"answering...\" would read as stale the moment the\n * answer lands.\n */\nexport const INLINE_ASIDE_SENT_NOTE = \"*sent to Claude on the side*\"\n\n/**\n * A receipt written into the running turn the moment the question goes out, so\n * a `/btw` typed mid-turn shows as taken instead of looking swallowed until\n * the answer arrives.\n *\n * It quotes the question back **in full**, which is what the operator asked\n * for: the prompt box clears on submit and no `/btw` message is ever created,\n * so this is the only place the question can be read back. It was briefly\n * capped at 240 characters and that was wrong for the same reason, since a\n * long aside would then be unreadable everywhere. The note goes on its own bar\n * line so the question is never crowded by it.\n *\n * The answer block repeats the question rather than dropping it, because the\n * model keeps streaming its own text between the two and a headerless answer\n * arriving after that reads as orphaned.\n */\nexport function formatInlineAsideAsk(question: string): string {\n return `\\n${asideHeader(question)}\\n▌ ${INLINE_ASIDE_SENT_NOTE}\\n`\n}\n\n/**\n * Writes one finished text block into a stream that is open right now.\n * Returns false when there is nothing to write to, which is the whole reason\n * the held-message path is still here.\n */\nexport type AsideSink = (text: string) => boolean\n\n/** At most one open stream per conversation, so a plain map is enough. */\nconst asideSinks = new Map<string, AsideSink>()\n\nexport function registerAsideSink(sessionID: string, sink: AsideSink): () => void {\n asideSinks.set(sessionID, sink)\n return () => {\n // Only the stream that registered may unregister: a later turn's sink\n // must survive the earlier turn's cleanup.\n if (asideSinks.get(sessionID) === sink) asideSinks.delete(sessionID)\n }\n}\n\nexport function emitAsideInline(sessionID: string, text: string): boolean {\n const sink = asideSinks.get(sessionID)\n if (!sink) return false\n try {\n return sink(text)\n } catch (error) {\n log.debug(\"btw: could not write the aside into the running turn\", { sessionID, error: errorText(error) })\n return false\n }\n}\n\n/** Test seam. */\nexport function clearAsideSinks(): void {\n asideSinks.clear()\n}\n\nexport function showToast(client: BtwSdkClient | null, body: BtwToast): void {\n // Keep the receiver: the SDK's namespace methods read `this._client`, so a\n // detached `const show = client.tui.showToast` throws at call time.\n try {\n void client?.tui?.showToast?.({ body })?.catch((error: unknown) => {\n log.debug(\"btw toast failed\", { error: errorText(error) })\n })\n } catch (error) {\n log.debug(\"btw toast failed\", { error: errorText(error) })\n }\n}\n\n/** A turn is streaming from this process, so its transcript cannot show an answer yet. */\nexport function isProcessBusy(active: Pick<ActiveProcess, \"lineEmitter\">): boolean {\n return active.lineEmitter.listenerCount(\"line\") > 0\n}\n\n/**\n * opencode's own view of the session: `busy` for the whole turn, including\n * the gaps where opencode runs a tool and no stream is attached to the\n * process, which `isProcessBusy` cannot see. `unknown` when the SDK has no\n * status route or it fails.\n */\nexport async function sessionStatus(\n client: BtwSdkClient | null,\n sessionID: string,\n): Promise<\"busy\" | \"idle\" | \"unknown\"> {\n const status = client?.session?.status\n if (!status) return \"unknown\"\n try {\n const result = await status.call(client!.session)\n const entry = result.data?.[sessionID]\n return entry && entry.type !== \"idle\" ? \"busy\" : \"idle\"\n } catch (error) {\n log.debug(\"btw: could not read session status\", { sessionID, error: errorText(error) })\n return \"unknown\"\n }\n}\n\n/**\n * Resolves once the session is no longer busy. Returns false on timeout. A\n * client without a status route resolves at once, since there is nothing to\n * wait on.\n */\nexport async function waitForSessionIdle(\n client: BtwSdkClient | null,\n sessionID: string,\n options: { pollMs?: number; timeoutMs?: number; stop?: () => boolean } = {},\n): Promise<boolean> {\n const pollMs = options.pollMs ?? IDLE_POLL_MS\n const timeoutMs = options.timeoutMs ?? IDLE_WAIT_MAX_MS\n const started = Date.now()\n for (;;) {\n if (options.stop?.()) return true\n if ((await sessionStatus(client, sessionID)) !== \"busy\") return true\n if (Date.now() - started >= timeoutMs) return false\n await new Promise((resolve) => setTimeout(resolve, pollMs))\n }\n}\n\n/**\n * Puts the answer in the conversation while the turn that prompted it is\n * still running, by writing it as its own text block into that turn's live\n * stream. It lands in the assistant reply the operator is already watching:\n * full markdown, scrollable, kept by opencode, and readable long after a\n * toast would have gone.\n *\n * Retries while the conversation stays busy, because a turn is a run of\n * streams rather than one and the gap between two of them is short. Gives up\n * once the turn ends, leaving the message to carry the answer instead.\n */\nexport async function deliverAsideInline(\n client: BtwSdkClient | null,\n sessionID: string,\n text: string,\n options: BtwWaitOptions = {},\n): Promise<boolean> {\n const pollMs = options.inlinePollMs ?? INLINE_POLL_MS\n const timeoutMs = options.inlineWaitMs ?? INLINE_WAIT_MAX_MS\n const started = Date.now()\n for (;;) {\n if (emitAsideInline(sessionID, text)) return true\n if (Date.now() - started >= timeoutMs) return false\n if ((await sessionStatus(client, sessionID)) !== \"busy\") return false\n await new Promise((resolve) => setTimeout(resolve, pollMs))\n }\n}\n\n/**\n * The `claude` process serving this conversation, waiting for it when a turn\n * is already running but has not yet reached the point where doStream tags it\n * (`claude-code-language-model.ts`, where the line listener attaches). That\n * gap is the whole spawn path on a conversation's first turn, and a `/btw`\n * typed inside it used to fall straight through, which is exactly what leaves\n * a \"Queued\" bubble in the transcript: measured live on 2026-09-06, a `/btw`\n * logged \"no live claude process for session\" and the same question 22 s\n * later found one and was answered concurrently.\n */\nexport async function waitForAsideProcess(\n client: BtwSdkClient | null,\n sessionID: string,\n options: BtwWaitOptions = {},\n): Promise<ActiveProcess | undefined> {\n const pollMs = options.pollMs ?? IDLE_POLL_MS\n const settleMs = options.settleMs ?? BUSY_SETTLE_MS\n const spawnWaitMs = options.spawnWaitMs ?? SPAWN_WAIT_MAX_MS\n const started = Date.now()\n for (;;) {\n const active = findActiveProcessBySessionId(sessionID)\n if (active) return active\n const busy = (await sessionStatus(client, sessionID)) === \"busy\"\n const waitedMs = Date.now() - started\n if (!busy && waitedMs >= settleMs) {\n // Nothing is running, so no process is on its way either.\n log.info(\"btw: no live claude process for session, leaving it to the turn\", { sessionID, waitedMs })\n return undefined\n }\n if (busy && waitedMs >= spawnWaitMs) {\n // A turn is running but it never produced a process of ours: it belongs\n // to another provider, or the spawn failed. Do not hold the message for\n // the rest of it.\n log.warn(\"btw: a turn is running but no claude process appeared for it\", { sessionID, waitedMs })\n return undefined\n }\n await new Promise((resolve) => setTimeout(resolve, pollMs))\n }\n}\n\n/**\n * Whether a turn is running, tolerant of the same registration lag: a status\n * read taken the instant `/btw` is typed can still say idle while opencode is\n * starting the turn, and skipping the hold on that reading is what queues the\n * message behind the turn instead of releasing it afterwards.\n */\nexport async function settleSessionBusy(\n client: BtwSdkClient | null,\n sessionID: string,\n active: Pick<ActiveProcess, \"lineEmitter\">,\n options: BtwWaitOptions = {},\n): Promise<boolean> {\n const pollMs = options.pollMs ?? IDLE_POLL_MS\n const settleMs = options.settleMs ?? BUSY_SETTLE_MS\n const started = Date.now()\n for (;;) {\n const status = await sessionStatus(client, sessionID)\n if (status === \"busy\") return true\n // No status route to poll: the process's own stream is all there is.\n if (status === \"unknown\") return isProcessBusy(active)\n if (Date.now() - started >= settleMs) return false\n await new Promise((resolve) => setTimeout(resolve, pollMs))\n }\n}\n\nfunction errorText(error: unknown): string {\n if (error instanceof Error) return error.message\n if (error && typeof error === \"object\" && \"message\" in error && typeof (error as { message: unknown }).message === \"string\") {\n return (error as { message: string }).message\n }\n return String(error)\n}\n\nfunction isTextPart(part: unknown): part is { type: \"text\"; text: string } {\n return (\n part !== null &&\n typeof part === \"object\" &&\n (part as { type?: unknown }).type === \"text\" &&\n typeof (part as { text?: unknown }).text === \"string\"\n )\n}\n\n/**\n * Earlier `/btw` exchanges in this conversation, read back from opencode\n * because the hook runs before the current question exists as a message.\n * Best effort: a follow-up without history still gets an answer, just one\n * that cannot refer to previous asides.\n */\nexport async function fetchAsideHistory(\n client: BtwSdkClient | null,\n sessionID: string,\n question: string,\n): Promise<SideQuestionExchange[]> {\n const messages = client?.session?.messages\n if (!messages) return []\n try {\n const result = await messages.call(client!.session, { path: { id: sessionID } })\n const prompt: { role: string; content: unknown }[] = []\n for (const message of result.data ?? []) {\n const role = message.info?.role\n if (role !== \"user\" && role !== \"assistant\") continue\n prompt.push({ role, content: (message.parts ?? []).filter(isTextPart) })\n }\n // collectSideQuestionHistory skips the final message as the question being\n // asked; stand in for the one opencode has not created yet.\n prompt.push({ role: \"user\", content: `/btw ${question}` })\n return collectSideQuestionHistory(prompt)\n } catch (error) {\n log.debug(\"btw: could not read aside history\", { sessionID, error: errorText(error) })\n return []\n }\n}\n\n/**\n * `command.execute.before` handler for `btw`. Returns normally so opencode\n * creates the `/btw` message in this conversation; throws only when there is\n * nothing to keep (a bare `/btw`, or a turn that never ended).\n *\n * While the session is busy the return is delayed until it is idle. opencode\n * would otherwise queue the message behind the running turn and run it as\n * that turn's next step, which is also the step that carries the results of\n * the tools opencode just ran: answering the aside there would swallow the\n * turn's own continuation (measured live: the main answer never appeared).\n * opencode already keeps the command route open for a queued prompt, so\n * holding it here changes nothing on the wire, and the TUI's call is\n * fire-and-forget.\n *\n * Both waits before that hold exist because a `/btw` typed early in a turn\n * used to be seen as belonging to an idle conversation with no process, and\n * was let through to be queued: `waitForAsideProcess` covers the spawn gap,\n * `settleSessionBusy` covers opencode registering the turn.\n */\nexport async function handleBtwCommand(\n client: BtwSdkClient | null,\n input: BtwCommandInput,\n options: BtwWaitOptions = {},\n): Promise<void> {\n const question = input.arguments.trim()\n if (!question) {\n showToast(client, { title: \"btw\", message: SIDE_QUESTION_USAGE, variant: \"warning\", duration: 6_000 })\n throw new BtwHandledError(\"/btw needs a question.\")\n }\n const active = await waitForAsideProcess(client, input.sessionID, options)\n const transport = active?.asideTransport\n if (!active || !transport) {\n // The message still goes through: the aside branch answers it with an\n // explanation that stays readable in the conversation.\n if (active) log.info(\"btw: process has no aside transport, leaving it to the turn\", { sessionID: input.sessionID })\n return\n }\n let busy = false\n let inlineDone = false\n let markInlineDelivered = (): void => {}\n const inlineDelivered = new Promise<\"inline\">((resolve) => {\n markInlineDelivered = () => {\n inlineDone = true\n resolve(\"inline\")\n }\n })\n if (isSideQuestionPending(active)) {\n // One aside per process at a time. Leave the earlier answer in place for\n // its own message; this one asks when its turn comes.\n busy = await settleSessionBusy(client, input.sessionID, active, options)\n log.info(\"btw: an aside is already in flight, leaving this one to the turn\", { sessionID: input.sessionID, busy })\n } else {\n // Settled alongside the request rather than before it: an aside asked\n // while the conversation is idle must not wait out the settle window\n // before it is even sent.\n const settling = settleSessionBusy(client, input.sessionID, active, options)\n const history = await fetchAsideHistory(client, input.sessionID, question)\n const answer = requestSideQuestion(active, question, {\n cliVersion: await detectCliVersion(transport.cliPath),\n interactive: transport.interactive,\n ...(history.length ? { history } : {}),\n })\n // Handled from this tick on. The settle below can span several timer\n // ticks, and an aside that fails immediately (a dead process, an\n // interactive transport) would otherwise raise an unhandled rejection in\n // the host before the real handlers further down are attached.\n answer.catch(() => undefined)\n rememberSideQuestionAnswer(input.sessionID, question, answer)\n busy = await settling\n log.info(\"btw: aside sent ahead of its message\", {\n sessionID: input.sessionID,\n busy,\n questionLength: question.length,\n history: history.length,\n })\n // Written before the answer exists, so a `/btw` typed mid-turn shows up in\n // the turn straight away rather than looking swallowed until the answer\n // arrives. Only while a turn is running: an idle conversation gets the\n // whole pair as its own message a moment later anyway.\n const asked = busy\n ? deliverAsideInline(client, input.sessionID, formatInlineAsideAsk(question), options).catch(() => false)\n : Promise.resolve(false)\n answer.then(\n async (result) => {\n log.info(\"btw: early answer arrived\", { sessionID: input.sessionID, busy, responseLength: result.response.length })\n if (!busy || result.synthetic) return\n // Awaited, not raced: a receipt that landed after the answer it\n // announces would read backwards. In the common case it was written\n // long before this and the await is already settled.\n await asked\n const inline = await deliverAsideInline(\n client,\n input.sessionID,\n formatInlineAside(question, result.response),\n options,\n )\n if (inline) {\n // The answer is in the conversation already, so the `/btw` message\n // has nothing left to carry. The remembered answer is deliberately\n // left in place: if the drop below does not take, the message\n // replays this answer instead of paying for a second one.\n log.info(\"btw: answer written into the running turn\", { sessionID: input.sessionID })\n markInlineDelivered()\n return\n }\n // Nothing was open to write to. The held `/btw` message carries this\n // same answer into the conversation once the turn ends, which is the\n // durable copy, so there is nothing to announce here.\n log.info(\"btw: no open stream for the answer; the held message will carry it\", {\n sessionID: input.sessionID,\n })\n },\n (error: unknown) => {\n // The message asks again once its turn runs.\n log.warn(\"btw: early aside failed; the message will ask again\", {\n sessionID: input.sessionID,\n error: errorText(error),\n })\n },\n )\n }\n if (!busy) return\n const started = Date.now()\n const outcome = await Promise.race([\n inlineDelivered,\n waitForSessionIdle(client, input.sessionID, { ...options, stop: () => inlineDone }).then((idle) =>\n idle ? (\"idle\" as const) : (\"timeout\" as const),\n ),\n ])\n if (outcome === \"inline\") {\n log.info(\"btw: answered inside the running turn, dropping the /btw message\", {\n sessionID: input.sessionID,\n waitedMs: Date.now() - started,\n })\n throw new BtwHandledError(BTW_INLINE_HANDLED_MESSAGE)\n }\n log.info(\"btw: turn over, releasing the /btw message\", {\n sessionID: input.sessionID,\n idle: outcome === \"idle\",\n waitedMs: Date.now() - started,\n })\n if (outcome === \"timeout\") {\n showToast(client, { title: \"btw\", message: BTW_TURN_TOO_LONG_MESSAGE, variant: \"warning\", duration: 8_000 })\n throw new BtwHandledError(BTW_TURN_TOO_LONG_MESSAGE)\n }\n}\n","import { execFile } from \"node:child_process\"\nimport * as fs from \"node:fs\"\nimport * as path from \"node:path\"\nimport { promisify } from \"node:util\"\nimport { fileURLToPath } from \"node:url\"\n\nimport { detectCliVersion } from \"./cli-version.js\"\nimport { log } from \"./logger.js\"\nimport { mergeOpencodeMcp } from \"./mcp-bridge.js\"\nimport { getOpencodeProjectDirectory, isUsableDirectory } from \"./runtime-status.js\"\n\n/**\n * One compact status block logged once per process, right after providers are\n * registered. Every field here answers a question that previously cost a live\n * debugging session: which plugin build is loaded, whether the Claude CLI is\n * even reachable, which cwd the spawn will use and why, what is proxied, and\n * how many MCP servers the bridge sees. Keep it cheap and never let it throw:\n * diagnostics must not be able to break provider registration.\n */\nexport interface StartupDiagnostics {\n plugin: string\n opencode: string\n claudeCli: { path: string; version: string }\n cwd: { resolved: string; source: CwdSource }\n providers: string[]\n accounts: string[]\n proxyTools: string[]\n mcpServers: string[]\n interactiveTransport: boolean\n /** ExitPlanMode approval routed through opencode's `question` tool. */\n planModeQuestion: boolean\n anthropicApiKeyInEnv: boolean\n}\n\n/** Which branch of `resolveSpawnCwd` a Claude CLI spawn would take right now. */\nexport type CwdSource = \"configured\" | \"process\" | \"captured\" | \"unresolved\"\n\nexport interface DiagnosticsProviderEntry {\n name?: string\n options?: Record<string, unknown>\n}\n\nlet cachedPluginVersion: string | undefined\n\n/** Version of this plugin, read from the package manifest one level up. */\nexport function pluginVersion(): string {\n if (cachedPluginVersion) return cachedPluginVersion\n try {\n const here = path.dirname(fileURLToPath(import.meta.url))\n const raw = fs.readFileSync(path.join(here, \"..\", \"package.json\"), \"utf8\")\n const version = (JSON.parse(raw) as { version?: unknown }).version\n cachedPluginVersion = typeof version === \"string\" ? version : \"unknown\"\n } catch {\n cachedPluginVersion = \"unknown\"\n }\n return cachedPluginVersion\n}\n\n/**\n * Best-effort opencode version from the plugin input. Re-verified on opencode\n * 1.18.5: nothing on the plugin surface carries it. `PluginInput` has no\n * version field, the SDK client's `app` namespace exposes only `log`/`agents`,\n * and the server has no `/version` route. So this probes a couple of plausible\n * shapes for future opencode releases and otherwise returns undefined, leaving\n * the binary probe (`detectOpencodeVersion`) as the fallback. Do not replace it\n * with a `client.app.get()` call — that method does not exist.\n */\nexport function pickOpencodeVersion(input: unknown): string | undefined {\n if (!input || typeof input !== \"object\") return undefined\n const app = (input as { app?: unknown }).app\n if (app && typeof app === \"object\") {\n const version = (app as { version?: unknown }).version\n if (typeof version === \"string\" && version.length > 0) return version\n }\n const direct = (input as { version?: unknown }).version\n if (typeof direct === \"string\" && direct.length > 0) return direct\n return undefined\n}\n\nconst execFileAsync = promisify(execFile)\n\nlet opencodeVersionProbe: Promise<string | undefined> | undefined\n\n/**\n * The plugin runs *inside* opencode's process, so `process.execPath` is the\n * opencode binary itself — asking it for `--version` is the only reliable way\n * to name the version, since the plugin API exposes it nowhere (see\n * `pickOpencodeVersion`). Guarded on the basename: when opencode is run from\n * source (`bun run packages/opencode/src/index.ts`) execPath is the Bun binary,\n * and reporting Bun's version as opencode's would be worse than \"unknown\".\n * Cached, 5s timeout, never throws.\n */\nexport function detectOpencodeVersion(\n execPath: string = process.execPath,\n): Promise<string | undefined> {\n if (opencodeVersionProbe) return opencodeVersionProbe\n opencodeVersionProbe = (async (): Promise<string | undefined> => {\n if (!path.basename(execPath).toLowerCase().includes(\"opencode\")) {\n log.debug(\"skipping opencode version probe: execPath is not opencode\", { execPath })\n return undefined\n }\n try {\n const { stdout } = await execFileAsync(execPath, [\"--version\"], { timeout: 5000 })\n const match = /\\d+\\.\\d+\\.\\d+\\S*/.exec(stdout.trim())\n return match ? match[0] : undefined\n } catch (err) {\n log.debug(\"opencode version probe failed\", {\n execPath,\n error: err instanceof Error ? err.message : String(err),\n })\n return undefined\n }\n })()\n return opencodeVersionProbe\n}\n\n/** Test seam: drop the cached probe so a fresh execPath is honored. */\nexport function resetOpencodeVersionProbe(): void {\n opencodeVersionProbe = undefined\n}\n\n/**\n * Mirror of `resolveSpawnCwd`'s priority order, but reporting *which* branch\n * won. `configured` means `options.cwd` pinned it, `process` is the normal\n * lazy path, `captured` means `process.cwd()` was unusable (macOS GUI launch\n * at `/`) and the captured project directory rescued it — that one is the\n * fingerprint of issue #4.\n */\nexport function describeSpawnCwd(\n configured: unknown,\n live: string = process.cwd(),\n captured: string | undefined = getOpencodeProjectDirectory(),\n): { resolved: string; source: CwdSource } {\n if (typeof configured === \"string\" && configured.length > 0) {\n return { resolved: configured, source: \"configured\" }\n }\n if (isUsableDirectory(live)) return { resolved: live, source: \"process\" }\n if (isUsableDirectory(captured)) return { resolved: captured, source: \"captured\" }\n return { resolved: live, source: \"unresolved\" }\n}\n\nfunction stringList(value: unknown): string[] {\n if (!Array.isArray(value)) return []\n return value.filter((entry): entry is string => typeof entry === \"string\")\n}\n\nfunction firstOption(\n providers: Record<string, DiagnosticsProviderEntry>,\n key: string,\n): unknown {\n for (const entry of Object.values(providers)) {\n const value = entry?.options?.[key]\n if (value !== undefined) return value\n }\n return undefined\n}\n\nexport function collectStartupDiagnostics(\n providers: Record<string, DiagnosticsProviderEntry>,\n opencodeVersion?: string,\n): Omit<StartupDiagnostics, \"claudeCli\"> & { claudeCliPath: string } {\n const accounts: string[] = []\n for (const entry of Object.values(providers)) {\n const account = entry?.options?.account\n if (typeof account === \"string\" && account.length > 0) accounts.push(account)\n }\n\n const cwd = describeSpawnCwd(firstOption(providers, \"cwd\"))\n\n let mcpServers: string[] = []\n try {\n // Disk-only view: opencode's runtime MCP status isn't settled at plugin\n // init (servers are still connecting), so the per-turn overlay is not\n // applied here. This is what the bridge would ship on a cold start.\n mcpServers = mergeOpencodeMcp(cwd.resolved).enabledServerNames\n } catch (err) {\n log.debug(\"startup diagnostics could not read MCP config\", {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n\n return {\n plugin: pluginVersion(),\n opencode: opencodeVersion ?? process.env.OPENCODE_VERSION ?? \"unknown\",\n claudeCliPath: String(firstOption(providers, \"cliPath\") ?? \"claude\"),\n cwd,\n providers: Object.keys(providers),\n accounts,\n proxyTools: stringList(firstOption(providers, \"proxyTools\")),\n mcpServers,\n interactiveTransport:\n firstOption(providers, \"interactive\") === true ||\n process.env.CLAUDE_CODE_INTERACTIVE_TRANSPORT === \"1\",\n planModeQuestion: firstOption(providers, \"planModeQuestion\") === true,\n anthropicApiKeyInEnv: Boolean(\n process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN,\n ),\n }\n}\n\nlet logged = false\nlet lastProviders: Record<string, DiagnosticsProviderEntry> = {}\nlet lastOpencodeVersion: string | undefined\n\n/**\n * The providers the config hook last registered, so `/claude-code-doctor` can\n * re-run `collectStartupDiagnostics` on demand instead of reporting a snapshot\n * frozen at startup. Kept here rather than in the doctor because this is\n * already the module that owns the shape.\n */\nexport function lastDiagnosticsProviders(): Record<string, DiagnosticsProviderEntry> {\n return lastProviders\n}\n\nexport function lastKnownOpencodeVersion(): string | undefined {\n return lastOpencodeVersion\n}\n\n/**\n * Emit the startup block once per process. Fire-and-forget: the Claude CLI\n * version probe is async (`claude --version`, 5s timeout, cached), and a slow\n * or missing binary must never delay provider registration.\n */\nexport function logStartupDiagnostics(\n providers: Record<string, DiagnosticsProviderEntry>,\n opencodeVersion?: string,\n): void {\n // Recorded before the once-per-process guard: an account expansion calls\n // this a second time with the real provider set, and the doctor should read\n // that one rather than the pre-expansion view.\n lastProviders = providers\n if (opencodeVersion) lastOpencodeVersion = opencodeVersion\n if (logged) return\n logged = true\n void (async () => {\n try {\n // Probe the binary only when the plugin input and env gave us nothing,\n // so a future opencode that reports its version costs no spawn.\n const version =\n opencodeVersion ?? process.env.OPENCODE_VERSION ?? (await detectOpencodeVersion())\n const { claudeCliPath, ...rest } = collectStartupDiagnostics(providers, version)\n const cli = await detectCliVersion(claudeCliPath)\n const diagnostics: StartupDiagnostics = {\n ...rest,\n claudeCli: { path: claudeCliPath, version: cli?.raw ?? \"not detected\" },\n }\n log.notice(\"claude-code plugin ready\", { ...diagnostics })\n } catch (err) {\n log.debug(\"startup diagnostics failed\", {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n })()\n}\n\n/** For tests. */\nexport function _resetStartupDiagnostics(): void {\n logged = false\n lastProviders = {}\n lastOpencodeVersion = undefined\n}\n","import * as fs from \"node:fs\"\nimport * as path from \"node:path\"\nimport * as os from \"node:os\"\nimport * as crypto from \"node:crypto\"\nimport {\n parse as parseJsonc,\n printParseErrorCode,\n type ParseError,\n} from \"jsonc-parser\"\nimport { log } from \"./logger.js\"\nimport { pluginTmpDir } from \"./tmp.js\"\n\n/**\n * Bridge opencode's `mcp` config block into a Claude CLI `--mcp-config` file.\n *\n * Opencode core schema (packages/opencode/src/config/mcp.ts):\n * {\n * \"mcp\": {\n * \"name\": {\n * \"type\": \"local\" | \"remote\",\n * \"command\"?: string[], // local\n * \"environment\"?: Record<string,string>,\n * \"url\"?: string, // remote\n * \"headers\"?: Record<string,string>,\n * \"oauth\"?: object | false, // remote — NOT bridged (Claude --mcp-config has no slot)\n * \"timeout\"?: number, // NOT bridged (Claude --mcp-config has no slot)\n * \"enabled\"?: boolean\n * }\n * }\n * }\n *\n * Claude CLI `--mcp-config` schema:\n * {\n * \"mcpServers\": {\n * \"name\": {\n * \"type\": \"stdio\" | \"http\",\n * \"command\"?: string, \"args\"?: string[], \"env\"?: Record<string,string>,\n * \"url\"?: string, \"headers\"?: Record<string,string>\n * }\n * }\n * }\n *\n * Discovery + merge are aligned with opencode core's `loadInstanceState`\n * (packages/opencode/src/config/config.ts). In merge order (last wins),\n * opencode loads:\n *\n * 1. Auth `.well-known` remote configs ← NOT bridged\n * 2. Global: ~/.config/opencode/{config.json,opencode.json,opencode.jsonc}\n * — all three deep-merged, jsonc highest priority\n * 3. OPENCODE_CONFIG env var (single file)\n * 4. Project walk-up: opencode.json[c] in each dir from cwd up to (not past)\n * worktree, both extensions per dir, parent-most first\n * 5. .opencode/ siblings: from cwd up + home dir + OPENCODE_CONFIG_DIR,\n * both extensions per dir, opencode-iteration order (cwd-most first\n * in walk-up — so parent-most `.opencode/` wins, matching upstream)\n * 6. OPENCODE_CONFIG_CONTENT env var (inline JSON) ← NOT bridged\n * 7. Active org remote config ← NOT bridged\n * 8. Managed config dir / macOS MDM ← NOT bridged\n *\n * Sources marked NOT bridged are niche and would require live opencode\n * runtime state (auth tokens, account context, MDM access). Document them\n * here so the gap is explicit; functionality of the common path is intact.\n *\n * Per-server merge is deep-merge (matching opencode's `mergeConfigConcatArrays`\n * → `mergeDeep`), so a project layer can override one field of a global server\n * spec — e.g. `{ \"linear\": { \"enabled\": true } }` lifts global linear's URL.\n */\n\nconst FILE_NAMES = [\"opencode.jsonc\", \"opencode.json\", \"config.json\"] as const\nconst PROJECT_FILE_NAMES = [\"opencode.json\", \"opencode.jsonc\"] as const\n\nfunction fileExists(p: string): boolean {\n try {\n return fs.statSync(p).isFile()\n } catch {\n return false\n }\n}\n\nfunction dirExists(p: string): boolean {\n try {\n return fs.statSync(p).isDirectory()\n } catch {\n return false\n }\n}\n\nfunction readAndParse(file: string): Record<string, unknown> | null {\n try {\n const raw = fs.readFileSync(file, \"utf8\")\n const errors: ParseError[] = []\n const parsed = parseJsonc(raw, errors, { allowTrailingComma: true })\n if (errors.length > 0) {\n const first = errors[0]\n throw new Error(\n `${printParseErrorCode(first.error)} at offset ${first.offset}`,\n )\n }\n return parsed as Record<string, unknown>\n } catch (e) {\n log.warn(\"failed to parse opencode config\", {\n file,\n error: e instanceof Error ? e.message : String(e),\n })\n return null\n }\n}\n\n/**\n * Deep merge two plain-object trees. Arrays and primitives are replaced\n * (not concatenated). Matches the effective behavior of opencode's\n * `mergeDeep` from `remeda` for the MCP block — opencode does not special\n * case array fields inside `mcp.<server>` (its only special case is\n * `instructions`, which is concat-deduped at the config root).\n */\nfunction isPlainObject(x: unknown): x is Record<string, unknown> {\n return typeof x === \"object\" && x !== null && !Array.isArray(x)\n}\n\nfunction deepMerge(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = { ...target }\n for (const [k, v] of Object.entries(source)) {\n if (v === undefined) continue\n const existing = out[k]\n if (isPlainObject(existing) && isPlainObject(v)) {\n out[k] = deepMerge(existing, v)\n } else {\n out[k] = v\n }\n }\n return out\n}\n\n/**\n * Walk up from `start` toward filesystem root (or `stop` if provided),\n * collecting paths where each `target` exists. Mirrors opencode core's\n * `FileSystem.up` (packages/core/src/filesystem.ts): cwd-most first,\n * parent-most last.\n */\nfunction walkUp(opts: {\n start: string\n stop?: string\n targets: readonly string[]\n predicate: (p: string) => boolean\n}): string[] {\n const out: string[] = []\n let current = path.resolve(opts.start)\n while (true) {\n for (const target of opts.targets) {\n const candidate = path.join(current, target)\n if (opts.predicate(candidate)) out.push(candidate)\n }\n if (opts.stop && current === path.resolve(opts.stop)) break\n const parent = path.dirname(current)\n if (parent === current) break\n current = parent\n }\n return out\n}\n\n/**\n * Find the worktree root by walking up from `cwd` looking for a `.git`\n * entry (file or directory — submodules use a file). If no `.git` is\n * found, walk to filesystem root. Honors OPENCODE_WORKTREE override.\n */\nfunction detectWorktree(cwd: string): string | undefined {\n const override = process.env.OPENCODE_WORKTREE\n if (override) return path.resolve(override)\n let current = path.resolve(cwd)\n while (true) {\n const gitPath = path.join(current, \".git\")\n try {\n if (fs.existsSync(gitPath)) return current\n } catch {\n // ignore\n }\n const parent = path.dirname(current)\n if (parent === current) return undefined\n current = parent\n }\n}\n\nfunction globalConfigDir(): string {\n const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), \".config\")\n return path.join(xdg, \"opencode\")\n}\n\n/**\n * Load the merged global config from `~/.config/opencode/`. Mirrors\n * opencode core's `loadGlobal`: deep-merges config.json → opencode.json\n * → opencode.jsonc in that order (jsonc wins).\n */\nfunction loadGlobalConfig(): Record<string, unknown> {\n const dir = globalConfigDir()\n let merged: Record<string, unknown> = {}\n for (const name of FILE_NAMES.slice().reverse()) {\n // FILE_NAMES is jsonc-first; reverse to get config.json-first order.\n const file = path.join(dir, name)\n if (!fileExists(file)) continue\n const parsed = readAndParse(file)\n if (parsed) merged = deepMerge(merged, parsed)\n }\n return merged\n}\n\n/** Load both `opencode.json` and `opencode.jsonc` in `dir`, deep-merged. */\nfunction loadProjectFilesInDir(dir: string): Record<string, unknown> {\n let merged: Record<string, unknown> = {}\n for (const name of PROJECT_FILE_NAMES) {\n const file = path.join(dir, name)\n if (!fileExists(file)) continue\n const parsed = readAndParse(file)\n if (parsed) merged = deepMerge(merged, parsed)\n }\n return merged\n}\n\n/**\n * Build the list of `.opencode/` directories to consider, in opencode core's\n * order (matching `ConfigPaths.directories`):\n * project walk-up (cwd-most first) → home-dir `.opencode/` → OPENCODE_CONFIG_DIR\n */\nfunction dotOpencodeDirs(cwd: string, worktree?: string): string[] {\n const dirs: string[] = []\n const seen = new Set<string>()\n const push = (p: string) => {\n const abs = path.resolve(p)\n if (!seen.has(abs) && dirExists(abs)) {\n seen.add(abs)\n dirs.push(abs)\n }\n }\n\n for (const dir of walkUp({\n start: cwd,\n stop: worktree,\n targets: [\".opencode\"],\n predicate: dirExists,\n })) {\n push(dir)\n }\n\n const home = os.homedir()\n if (home) {\n const homeDot = path.join(home, \".opencode\")\n if (dirExists(homeDot)) push(homeDot)\n }\n\n const envDir = process.env.OPENCODE_CONFIG_DIR\n if (envDir && dirExists(envDir)) push(envDir)\n\n return dirs\n}\n\ninterface OpencodeLocalServer {\n type?: \"local\"\n command?: string[]\n environment?: Record<string, string>\n enabled?: boolean\n}\n\ninterface OpencodeRemoteServer {\n type?: \"remote\"\n url?: string\n headers?: Record<string, string>\n enabled?: boolean\n}\n\ntype OpencodeServer = OpencodeLocalServer | OpencodeRemoteServer | { enabled?: boolean }\n\n/**\n * Substitute opencode's `{env:VAR}` interpolation in a string-keyed record\n * using values from `process.env`. Returns a new object. If the source is\n * not a flat string-valued record, returns it unchanged.\n *\n * Opencode performs this substitution itself when it spawns MCP servers\n * directly, but the spec we read from disk still contains the literal\n * placeholders. Without substituting them here, Claude CLI hands the\n * literal string `{env:FOO}` to the MCP subprocess as the env value, and\n * any server that validates credentials at startup (e.g. slack-mcp-server)\n * crashes before exposing tools. Servers that defer validation to\n * request time (e.g. github-mcp-server) appear to register but every API\n * call 401s.\n */\nfunction substituteEnvPlaceholders(\n source: Record<string, unknown>,\n): Record<string, string> {\n const out: Record<string, string> = {}\n for (const [k, v] of Object.entries(source)) {\n if (typeof v !== \"string\") continue\n out[k] = v.replace(/\\{env:([A-Za-z_][A-Za-z0-9_]*)\\}/g, (_match, name) => {\n const resolved = process.env[name]\n return typeof resolved === \"string\" ? resolved : \"\"\n })\n }\n return out\n}\n\nfunction translateServer(\n name: string,\n spec: Record<string, unknown>,\n): Record<string, unknown> | null {\n if (spec.enabled === false) return null\n\n const type = spec.type\n if (type === \"local\") {\n const cmd = spec.command\n if (!Array.isArray(cmd) || cmd.length === 0) {\n log.warn(\"skipping local MCP server with no command\", { name })\n return null\n }\n const out: Record<string, unknown> = {\n type: \"stdio\",\n command: String(cmd[0]),\n }\n if (cmd.length > 1) out.args = cmd.slice(1).map((s) => String(s))\n if (spec.environment && typeof spec.environment === \"object\") {\n out.env = substituteEnvPlaceholders(\n spec.environment as Record<string, unknown>,\n )\n }\n return out\n }\n\n if (type === \"remote\") {\n if (typeof spec.url !== \"string\" || !spec.url) {\n log.warn(\"skipping remote MCP server with no url\", { name })\n return null\n }\n const out: Record<string, unknown> = {\n type: \"http\",\n url: spec.url,\n }\n if (spec.headers && typeof spec.headers === \"object\") {\n out.headers = substituteEnvPlaceholders(\n spec.headers as Record<string, unknown>,\n )\n }\n return out\n }\n\n log.warn(\"skipping MCP server with unknown type\", {\n name,\n type: type ?? null,\n })\n return null\n}\n\nfunction extractMcpBlock(\n config: Record<string, unknown>,\n): Record<string, OpencodeServer> {\n const mcp = config.mcp\n if (!mcp || typeof mcp !== \"object\" || Array.isArray(mcp)) return {}\n return mcp as Record<string, OpencodeServer>\n}\n\n/**\n * Deep-merge per-server specs from `source` into `target`. Mirrors opencode's\n * `mergeDeep` semantics for the `mcp` record: each server entry is recursively\n * merged so a partial layer (e.g. `{ \"linear\": { \"enabled\": true } }`) can\n * override one field without dropping the rest.\n */\nfunction mergeMcp(\n target: Record<string, OpencodeServer>,\n source: Record<string, OpencodeServer>,\n): Record<string, OpencodeServer> {\n const out: Record<string, OpencodeServer> = { ...target }\n for (const [name, spec] of Object.entries(source)) {\n if (!spec || typeof spec !== \"object\") continue\n const existing = out[name]\n if (existing && typeof existing === \"object\") {\n out[name] = deepMerge(\n existing as Record<string, unknown>,\n spec as Record<string, unknown>,\n ) as OpencodeServer\n } else {\n out[name] = spec\n }\n }\n return out\n}\n\nexport interface BridgedMcp {\n /** Path to the temp file containing the translated `--mcp-config`. */\n path: string\n /** Stable hash of the merged opencode mcp block (pre-translation). */\n hash: string\n /**\n * Names of opencode MCP servers that were bridged into Claude CLI's\n * `--mcp-config`. Excludes any servers passed in `excludeServers`.\n */\n serverNames: string[]\n /**\n * Names of every enabled opencode MCP server after merge + runtime\n * overlay, regardless of whether they ended up bridged or excluded.\n * Callers (e.g. the proxy-tool builder) use this to decide which\n * `<server>_<tool>` IDs in opencode's tool catalog are MCP-origin.\n */\n allEnabledServerNames: string[]\n}\n\n/** Result of merging opencode's MCP config layers + applying runtime overlay. */\nexport interface MergedMcp {\n /** Merged, overlay-applied server specs keyed by opencode server name. */\n servers: Record<string, OpencodeServer>\n /** Server names whose final spec is enabled (or implicitly enabled). */\n enabledServerNames: string[]\n /** Stable hash of the merged (pre-translation) MCP block. */\n hash: string\n}\n\n/**\n * Per-server runtime status from opencode's `client.mcp.status()`. Used as\n * an overlay on top of the on-disk merged config so opencode's UI-toggled\n * state — which lives only in-memory; `connect()`/`disconnect()` never\n * touch disk — propagates to the bridged claude subprocess.\n *\n * Treatment per server:\n * - \"connected\" → force `enabled: true` (mirror opencode)\n * - any other status → force `enabled: false` (don't ship a server\n * opencode can't run; user fixes it in opencode first)\n * - missing entry → leave disk value\n *\n * Omit the overlay and the bridge falls back to disk-only.\n */\nexport type RuntimeMcpStatus = Record<string, string>\n\n/**\n * Read opencode config layers, deep-merge their `mcp` blocks per opencode's\n * own semantics, optionally apply an opencode runtime-status overlay, then\n * translate each server to Claude CLI format, write a scratch file, and\n * return its path + a stable hash. Returns null when no enabled MCP servers\n * remain after the merge + overlay.\n */\nexport function bridgeOpencodeMcp(\n cwd: string,\n runtimeStatus?: RuntimeMcpStatus,\n excludeServers?: ReadonlySet<string>,\n): BridgedMcp | null {\n const {\n servers: merged,\n enabledServerNames: allEnabledServerNames,\n hash,\n } = mergeOpencodeMcp(cwd, runtimeStatus)\n\n // Translate every still-enabled server, skipping any caller has asked us\n // to exclude (because they're being routed through the proxy instead).\n const servers: Record<string, unknown> = {}\n const bridgedServerNames: string[] = []\n for (const [name, spec] of Object.entries(merged)) {\n if (!spec || typeof spec !== \"object\") continue\n if (excludeServers?.has(name)) continue\n const translated = translateServer(name, spec as Record<string, unknown>)\n if (translated) {\n servers[name] = translated\n bridgedServerNames.push(name)\n }\n }\n return finishBridge({\n servers,\n bridgedServerNames,\n allEnabledServerNames,\n hash,\n excludeServers,\n })\n}\n\n/**\n * Every opencode config layer that lives on disk, lowest precedence first:\n * global → `OPENCODE_CONFIG` → project walk-up → `.opencode/` siblings. The\n * order is the whole contract, so the MCP bridge and anything else reading\n * opencode's config (the V2 entrypoint's `accounts` lookup) share it rather\n * than each walking the layers themselves.\n */\nexport function opencodeConfigLayers(cwd: string): Record<string, unknown>[] {\n const worktree = detectWorktree(cwd)\n const layers: Record<string, unknown>[] = []\n\n // Layer 1: global merged\n layers.push(loadGlobalConfig())\n\n // Layer 2: OPENCODE_CONFIG (single file, applied before project walk-up)\n const explicitConfig = process.env.OPENCODE_CONFIG\n if (explicitConfig && fileExists(explicitConfig)) {\n const parsed = readAndParse(explicitConfig)\n if (parsed) layers.push(parsed)\n }\n\n // Layer 3: project walk-up — opencode.json[c] in each dir from cwd to\n // (not past) worktree, both extensions per dir. walkUp returns cwd-most\n // first; collect distinct dirs in that order then reverse for merge so\n // cwd-most wins under last-merge-wins.\n const projectFiles = walkUp({\n start: cwd,\n stop: worktree,\n targets: PROJECT_FILE_NAMES,\n predicate: fileExists,\n })\n const projectDirs: string[] = []\n const seenProjectDirs = new Set<string>()\n for (const f of projectFiles) {\n const d = path.dirname(f)\n if (!seenProjectDirs.has(d)) {\n seenProjectDirs.add(d)\n projectDirs.push(d)\n }\n }\n for (const dir of projectDirs.slice().reverse()) {\n layers.push(loadProjectFilesInDir(dir))\n }\n\n // Layer 4: `.opencode/` siblings — project walk-up then home-dir then\n // OPENCODE_CONFIG_DIR, in that order. Iteration order matches opencode's\n // (cwd-most first within walk-up), so under deep-merge \"later wins\"\n // parent-most `.opencode/` overrides cwd-most. This is upstream's\n // behavior, surprising though it is.\n for (const dir of dotOpencodeDirs(cwd, worktree)) {\n layers.push(loadProjectFilesInDir(dir))\n }\n\n return layers\n}\n\n/** The on-disk opencode config, every layer deep-merged in precedence order. */\nexport function loadMergedOpencodeConfig(cwd: string): Record<string, unknown> {\n let merged: Record<string, unknown> = {}\n for (const layer of opencodeConfigLayers(cwd)) merged = deepMerge(merged, layer)\n return merged\n}\n\n/**\n * Merge opencode's MCP config layers (see `opencodeConfigLayers`), apply the\n * opencode runtime-status overlay, and hash the result. Split out of\n * `bridgeOpencodeMcp` so read-only callers (startup diagnostics) can inspect\n * what would be bridged without translating servers or writing a scratch\n * config file.\n */\nexport function mergeOpencodeMcp(\n cwd: string,\n runtimeStatus?: RuntimeMcpStatus,\n): MergedMcp {\n let merged: Record<string, OpencodeServer> = {}\n for (const layer of opencodeConfigLayers(cwd)) {\n merged = mergeMcp(merged, extractMcpBlock(layer))\n }\n\n // Layer 5: opencode runtime overlay. opencode's `/mcps` UI toggle calls\n // `mcp.connect()` / `mcp.disconnect()` which only mutate in-memory state,\n // never the on-disk config. Without this overlay the bridge can't see\n // those toggles and claude misses servers the user just enabled.\n if (runtimeStatus) {\n for (const name of Object.keys(merged)) {\n const status = runtimeStatus[name]\n if (status === undefined) continue\n const existing = merged[name]\n const base =\n existing && typeof existing === \"object\"\n ? (existing as Record<string, unknown>)\n : {}\n merged[name] = { ...base, enabled: status === \"connected\" } as OpencodeServer\n }\n }\n\n // Compute the set of enabled server names BEFORE exclusion so callers can\n // tell whether a tool ID like `slack_conversations_add_message` came from\n // an opencode MCP server (vs a built-in tool that happens to contain `_`).\n const enabledServerNames: string[] = []\n for (const [name, spec] of Object.entries(merged)) {\n if (!spec || typeof spec !== \"object\") continue\n const enabled = (spec as { enabled?: unknown }).enabled\n if (enabled === false) continue\n enabledServerNames.push(name)\n }\n\n // Hash the pre-exclusion merged block so the hot-reload detector picks up\n // upstream config changes even when every server is excluded.\n const mergedBody = JSON.stringify({ mcpServers: merged }, null, 2)\n const hash = crypto\n .createHash(\"sha256\")\n .update(mergedBody)\n .digest(\"hex\")\n .slice(0, 12)\n\n return { servers: merged, enabledServerNames, hash }\n}\n\n/** Write the translated config (if any) and shape `bridgeOpencodeMcp`'s result. */\nfunction finishBridge(input: {\n servers: Record<string, unknown>\n bridgedServerNames: string[]\n allEnabledServerNames: string[]\n hash: string\n excludeServers?: ReadonlySet<string>\n}): BridgedMcp | null {\n const { servers, bridgedServerNames, allEnabledServerNames, hash, excludeServers } =\n input\n\n if (Object.keys(servers).length === 0) {\n const allEnabledServersExcluded =\n excludeServers &&\n allEnabledServerNames.length > 0 &&\n allEnabledServerNames.every((name) => excludeServers.has(name))\n\n if (!allEnabledServersExcluded) return null\n\n return {\n path: \"\",\n hash,\n serverNames: [],\n allEnabledServerNames,\n }\n }\n\n const body = JSON.stringify({ mcpServers: servers }, null, 2)\n // Content-addressed on purpose. `hash` covers the merged opencode config and\n // NOT `excludeServers`, so two calls that differ only in exclusions share it,\n // and the file is written only when absent. Naming the file after `hash`\n // alone therefore let the first writer win: the hot-reload probe runs first\n // with no exclusions, so the spawn's own exclusions never reached disk and a\n // server being routed through the proxy stayed in the bridged config as well,\n // reachable by both routes. That is the double execution this whole option\n // exists to prevent. The returned `hash` is unchanged, because drift\n // detection still wants to track the config rather than the exclusions.\n const bodyDigest = crypto\n .createHash(\"sha256\")\n .update(body)\n .digest(\"hex\")\n .slice(0, 12)\n const outPath = path.join(\n pluginTmpDir(),\n `mcp-${bodyDigest}.json`,\n )\n try {\n if (!fileExists(outPath)) {\n fs.writeFileSync(outPath, body, { encoding: \"utf8\", mode: 0o600 })\n }\n } catch (e) {\n log.warn(\"failed to write bridged MCP config\", {\n error: e instanceof Error ? e.message : String(e),\n })\n return null\n }\n\n log.info(\"bridged opencode MCP config\", {\n target: outPath,\n hash,\n servers: bridgedServerNames,\n excluded: excludeServers ? Array.from(excludeServers) : [],\n })\n return {\n path: outPath,\n hash,\n serverNames: bridgedServerNames,\n allEnabledServerNames,\n }\n}\n\n// Internal helpers exported for tests only.\nexport const __test = {\n deepMerge,\n mergeMcp,\n translateServer,\n substituteEnvPlaceholders,\n detectWorktree,\n loadGlobalConfig,\n loadProjectFilesInDir,\n dotOpencodeDirs,\n}\n","import type { RuntimeMcpStatus } from \"./mcp-bridge.js\"\nimport { log } from \"./logger.js\"\n\n/**\n * Captured opencode runtime context (SDK client + project directory) from\n * `PluginInput`. Lives in its own module to break the cycle that would\n * otherwise form between `index.ts` and `claude-code-language-model.ts`.\n * Values are `null`/`undefined` until the plugin's `server` factory runs\n * (e.g. early provider lookups, direct AI-SDK use, tests).\n */\ntype OpencodeClient = {\n mcp?: {\n status?: () => Promise<{ data?: unknown; error?: unknown }>\n }\n tool?: {\n list?: (options: {\n query: { provider: string; model: string; directory?: string }\n }) => Promise<{ data?: unknown; error?: unknown }>\n }\n session?: {\n /** `GET /session/{id}` — the returned Session carries `directory`. */\n get?: (options: {\n path: { id: string }\n query?: { directory?: string }\n }) => Promise<{ data?: unknown; error?: unknown }>\n /** `GET /session/status`: sessions missing from the map are idle. */\n status?: () => Promise<{ data?: unknown; error?: unknown }>\n }\n}\n\nlet opencodeClient: OpencodeClient | null = null\n\nexport function setOpencodeClient(client: unknown): void {\n if (client && typeof client === \"object\") {\n opencodeClient = client as OpencodeClient\n }\n}\n\n/**\n * The captured SDK client, untyped: callers narrow to the surface they use\n * (this module's `OpencodeClient` only mirrors the MCP/tool routes).\n */\nexport function getOpencodeClient(): unknown {\n return opencodeClient\n}\n\n/**\n * Captured opencode project directory from `PluginInput.directory` (with\n * `worktree` as secondary signal). Used as a *fallback* at Claude CLI\n * spawn time only when `process.cwd()` is unusable (macOS GUI launches\n * where launchd hands the process `cwd=/`).\n *\n * IMPORTANT: never bake this into provider config (`mergedOptions.cwd`).\n * Doing so freezes the value at plugin init and breaks workspace\n * switching mid-session, because subsequent workspace changes in\n * opencode's UI never get reflected in `this.config.cwd`. See issue #4.\n */\nlet opencodeProjectDirectory: string | undefined\n\nexport function setOpencodeProjectDirectory(dir: string | undefined): void {\n opencodeProjectDirectory = dir\n}\n\nexport function getOpencodeProjectDirectory(): string | undefined {\n return opencodeProjectDirectory\n}\n\nexport function isUsableDirectory(d: unknown): d is string {\n return typeof d === \"string\" && d.length > 1 && d !== \"/\"\n}\n\n/**\n * Resolve the cwd for a Claude CLI subprocess spawn. Priority:\n *\n * 1. Explicit `configured` value (`options.cwd` from `opencode.json`).\n * Users who pinned a directory keep their override unconditionally.\n * 2. The opencode session's own `directory` (resolved per-call from the\n * `x-session-affinity` id via the SDK). Authoritative for\n * `opencode serve` / web-UI mode, where one long-lived server process\n * handles many projects and `process.cwd()` is the server's launch\n * dir — not the session's project. Equals `process.cwd()` in the TUI,\n * so it does not regress that path.\n * 3. Live `process.cwd()` when it's a real directory. Lazy resolution\n * that lets opencode's project-aware behavior (chdir on workspace\n * switch, project-per-shell on terminal launch) flow through.\n * 4. Captured project directory from plugin init. Rescues macOS GUI\n * launches where `process.cwd()` is `/`.\n * 5. Final fallback to `process.cwd()` (returns `/` in the pathological\n * case where neither override nor capture is available).\n */\nexport function resolveSpawnCwd(configured: string | undefined): string {\n return resolveSpawnCwdFrom(\n configured,\n process.cwd(),\n opencodeProjectDirectory,\n )\n}\n\nexport function resolveSpawnCwdFrom(\n configured: string | undefined,\n live: string,\n captured: string | undefined,\n sessionDir?: string,\n): string {\n if (configured) return configured\n if (isUsableDirectory(sessionDir)) return sessionDir\n if (isUsableDirectory(live)) return live\n return captured ?? live\n}\n\n/**\n * Resolve the spawn cwd for a specific opencode session. Looks up the\n * session's `directory` via the SDK (keyed by the `x-session-affinity`\n * id opencode sets on LLM calls) and feeds it into `resolveSpawnCwdFrom`\n * as tier 2. Falls back cleanly to the non-session resolution when the\n * id is absent (\"default\"), no SDK client is captured, or the lookup\n * fails — so the TUI / direct-AI-SDK / test paths are unaffected.\n */\nexport async function resolveSpawnCwdForSession(\n configured: string | undefined,\n sessionID: string | undefined,\n): Promise<string> {\n // An explicit pin wins unconditionally — skip the lookup entirely.\n if (configured) return configured\n const sessionDir = sessionID\n ? await fetchSessionDirectory(sessionID)\n : undefined\n return resolveSpawnCwdFrom(\n configured,\n process.cwd(),\n opencodeProjectDirectory,\n sessionDir,\n )\n}\n\n/**\n * Fetch an opencode session's project directory via `GET /session/{id}`.\n * Returns `undefined` on any failure (no client, \"default\"/empty id,\n * rejected call, malformed response, unusable directory) so callers fall\n * back to `process.cwd()`-based resolution. No caching: a session's\n * directory can change (workspace switch) and the call is a cheap\n * localhost round-trip relative to spawning Claude.\n */\nexport async function fetchSessionDirectory(\n sessionID: string,\n): Promise<string | undefined> {\n if (!sessionID || sessionID === \"default\") return undefined\n const client = opencodeClient\n if (!client?.session?.get) return undefined\n try {\n const res = await client.session.get({ path: { id: sessionID } })\n const data = (res as { data?: unknown }).data\n if (!data || typeof data !== \"object\") return undefined\n const dir = (data as { directory?: unknown }).directory\n return isUsableDirectory(dir) ? dir : undefined\n } catch (err) {\n log.warn(\"failed to fetch opencode session directory\", {\n sessionID,\n error: err instanceof Error ? err.message : String(err),\n })\n return undefined\n }\n}\n\n/**\n * The id of the session that spawned this one, or undefined when it is a\n * top-level session (or the lookup is unavailable). Read off the same\n * `GET /session/{id}` response `fetchSessionDirectory` uses, kept separate\n * because the two are needed at different points in a turn.\n *\n * Account failover is the only caller: a subagent must never be shown the\n * switch form. It follows its parent's account for free, because the override\n * is scoped to the account rather than the session.\n */\nexport async function fetchSessionParentId(\n sessionID: string,\n): Promise<string | undefined> {\n if (!sessionID || sessionID === \"default\") return undefined\n const client = opencodeClient\n if (!client?.session?.get) return undefined\n try {\n const res = await client.session.get({ path: { id: sessionID } })\n const data = (res as { data?: unknown }).data\n if (!data || typeof data !== \"object\") return undefined\n const parentID = (data as { parentID?: unknown }).parentID\n return typeof parentID === \"string\" && parentID.length > 0\n ? parentID\n : undefined\n } catch (err) {\n log.warn(\"failed to fetch opencode session parent\", {\n sessionID,\n error: err instanceof Error ? err.message : String(err),\n })\n return undefined\n }\n}\n\nexport type SessionRunState = \"busy\" | \"idle\" | \"unknown\"\n\n/**\n * Whether opencode still considers this session to be running a turn.\n * `unknown` when there is no client or the route is missing, which every\n * caller must treat as \"do not act\": a wrong guess here is worse than no\n * answer.\n */\nexport async function fetchSessionRunState(\n sessionID: string,\n): Promise<SessionRunState> {\n if (!sessionID || sessionID === \"default\") return \"unknown\"\n const status = opencodeClient?.session?.status\n if (!status) return \"unknown\"\n try {\n const res = await status.call(opencodeClient!.session)\n const data = (res as { data?: unknown }).data\n if (!data || typeof data !== \"object\") return \"unknown\"\n const entry = (data as Record<string, unknown>)[sessionID]\n if (!entry || typeof entry !== \"object\") return \"idle\"\n const type = (entry as { type?: unknown }).type\n return type === \"idle\" ? \"idle\" : \"busy\"\n } catch (err) {\n log.debug(\"failed to read opencode session status\", {\n sessionID,\n error: err instanceof Error ? err.message : String(err),\n })\n return \"unknown\"\n }\n}\n\n/**\n * The session's run state, given a moment to settle. opencode 1.18.32 aborts\n * the provider signal of every step that ends in tool calls, roughly a second\n * after the finish, while it runs the tool; that looks exactly like an\n * operator abort at the signal. The session stays busy through the tool run\n * and goes idle when the turn really stops, so one `busy` reading inside the\n * window is enough to tell them apart, and the poll exists because an abort\n * can arrive before opencode has updated the map.\n */\nexport async function settleSessionRunState(\n sessionID: string,\n options: { pollMs?: number; windowMs?: number } = {},\n): Promise<SessionRunState> {\n const pollMs = options.pollMs ?? 150\n const windowMs = options.windowMs ?? 1_500\n const started = Date.now()\n let last: SessionRunState = \"unknown\"\n for (;;) {\n const state = await fetchSessionRunState(sessionID)\n if (state === \"busy\") return \"busy\"\n if (state !== \"unknown\") last = state\n if (Date.now() - started >= windowMs) return last\n await new Promise((resolve) => setTimeout(resolve, pollMs))\n }\n}\n\n/**\n * Snapshot opencode's current MCP runtime status so the bridge can overlay\n * UI-toggled state on top of disk config. Returns `undefined` on any\n * failure (no client captured, status call rejected, malformed response)\n * so the bridge falls back to disk-only.\n */\nexport async function getRuntimeMcpStatus(): Promise<\n RuntimeMcpStatus | undefined\n> {\n const client = opencodeClient\n if (!client?.mcp?.status) return undefined\n try {\n const res = await client.mcp.status()\n const data = (res as { data?: unknown }).data\n if (!data || typeof data !== \"object\") return undefined\n const out: RuntimeMcpStatus = {}\n for (const [name, entry] of Object.entries(data as Record<string, unknown>)) {\n if (entry && typeof entry === \"object\") {\n const status = (entry as { status?: unknown }).status\n if (typeof status === \"string\") out[name] = status\n }\n }\n return out\n } catch (err) {\n log.warn(\"failed to fetch opencode MCP runtime status\", {\n error: err instanceof Error ? err.message : String(err),\n })\n return undefined\n }\n}\n\nexport interface OpencodeToolListItem {\n id: string\n description: string\n parameters: Record<string, unknown>\n}\n\n/**\n * Fetch opencode's full tool catalog (built-ins + MCP-bridged) with JSON\n * Schema parameters via `client.tool.list()`. The provider/model query\n * narrows the schema variants opencode returns; in practice MCP-origin\n * tool schemas are model-agnostic, so any registered (provider, model)\n * works as the query target. Returns `undefined` on any failure so callers\n * can fall back to direct-bridge behavior.\n */\nexport async function fetchOpencodeToolList(\n provider: string,\n model: string,\n directory?: string,\n): Promise<OpencodeToolListItem[] | undefined> {\n const client = opencodeClient\n if (!client?.tool?.list) return undefined\n try {\n const res = await client.tool.list({\n query: { provider, model, ...(directory ? { directory } : {}) },\n })\n const data = (res as { data?: unknown }).data\n if (!Array.isArray(data)) return undefined\n const out: OpencodeToolListItem[] = []\n for (const entry of data as unknown[]) {\n if (!entry || typeof entry !== \"object\") continue\n const e = entry as Record<string, unknown>\n const id = typeof e.id === \"string\" ? e.id : null\n const description =\n typeof e.description === \"string\" ? e.description : \"\"\n const parameters =\n e.parameters && typeof e.parameters === \"object\"\n ? (e.parameters as Record<string, unknown>)\n : {}\n if (!id) continue\n out.push({ id, description, parameters })\n }\n return out\n } catch (err) {\n log.warn(\"failed to fetch opencode tool list\", {\n provider,\n model,\n error: err instanceof Error ? err.message : String(err),\n })\n return undefined\n }\n}\n","import { detectCliVersion } from \"./cli-version.js\"\nimport { log } from \"./logger.js\"\nimport {\n snapshotPendingProxyCalls,\n type PendingProxyCallSnapshot,\n} from \"./proxy-broker.js\"\nimport {\n snapshotActiveProcesses,\n type ActiveProcessSnapshot,\n} from \"./session-manager.js\"\nimport {\n collectStartupDiagnostics,\n detectOpencodeVersion,\n lastDiagnosticsProviders,\n lastKnownOpencodeVersion,\n type CwdSource,\n} from \"./startup-diagnostics.js\"\n\n/**\n * `/claude-code-doctor`: what the plugin thinks is happening, in the chat,\n * right now.\n *\n * The startup block already answers most of this, but it is logged once per\n * process to a file that is off by default, so in practice nobody sees it. The\n * command is answered by the plugin itself with no CLI inference, following\n * the `/btw` branch in `claude-code-language-model.ts`: the report is emitted\n * as assistant text at zero tokens, and the whole exchange is stripped from\n * any transcript rebuilt for the CLI.\n *\n * The name is `claude-code-doctor`, not `claude-code doctor`: opencode\n * commands are invoked as `/<key>` with everything after the first space taken\n * as `$ARGUMENTS`, so a space in the name would make the second word an\n * argument rather than part of the command.\n *\n * Nothing secret goes in it. Not the proxy bearer token, not the value of\n * `ANTHROPIC_API_KEY`, not the system prompt, not a pending call's arguments.\n */\n\nexport const DOCTOR_COMMAND = \"claude-code-doctor\"\n\nexport const DOCTOR_COMMAND_DESCRIPTION =\n \"Report what the Claude Code plugin sees: versions, cwd, live processes, pending proxy calls\"\n\n/** Leading marker of the report block, so `message-builder` can strip it. */\nexport const DOCTOR_MARKER = \"▌ **claude-code doctor**\"\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n}\n\n/**\n * The same `<system-reminder>` strip `/btw` needs: opencode appends its own\n * reminder blocks as extra text parts on the user message, and without this a\n * bare `/claude-code-doctor` would never look bare.\n */\nconst SYSTEM_REMINDER_BLOCK = /<system-reminder>[\\s\\S]*?<\\/system-reminder>/g\n\nexport function parseDoctorCommandContent(content: unknown): { rest: string } | null {\n let text: string\n if (typeof content === \"string\") {\n text = content\n } else if (Array.isArray(content)) {\n const parts: string[] = []\n for (const part of content) {\n if (!isRecord(part) || part.type !== \"text\" || typeof part.text !== \"string\") return null\n parts.push(part.text)\n }\n text = parts.join(\"\\n\")\n } else {\n return null\n }\n const match = new RegExp(`^/${DOCTOR_COMMAND}(?:\\\\s+([\\\\s\\\\S]*))?$`).exec(\n text.replace(SYSTEM_REMINDER_BLOCK, \"\").trim(),\n )\n return match ? { rest: (match[1] ?? \"\").trim() } : null\n}\n\n/** Only the newest user message, so a historical report is never re-run. */\nexport function parseDoctorCommand(\n prompt: readonly { role: string; content: unknown }[],\n): { rest: string } | null {\n const latest = prompt.at(-1)\n return latest?.role === \"user\" ? parseDoctorCommandContent(latest.content) : null\n}\n\nexport type ProxyAuthCheck =\n | { status: \"ok\"; code: number }\n | { status: \"unsafe\"; code: number }\n | { status: \"unreachable\"; error: string }\n | { status: \"skipped\" }\n\nexport interface DoctorProxyRow {\n url: string\n auth: ProxyAuthCheck\n}\n\nexport interface DoctorReport {\n plugin: string\n opencode: string\n claudeCli: { path: string; version: string }\n cwd: { resolved: string; source: CwdSource }\n providers: string[]\n accounts: string[]\n proxyTools: string[]\n mcpServers: string[]\n transport: \"headless\" | \"interactive\"\n planModeQuestion: boolean\n turnStats: boolean\n anthropicApiKeyInEnv: boolean\n processes: ActiveProcessSnapshot[]\n pendingCalls: PendingProxyCallSnapshot[]\n proxyServers: DoctorProxyRow[]\n}\n\nfunction formatAge(ms: number | undefined): string {\n if (ms === undefined) return \"unknown\"\n if (ms < 1000) return `${Math.round(ms)}ms`\n if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`\n const minutes = Math.floor(ms / 60_000)\n if (minutes < 60) return `${minutes}m`\n return `${Math.floor(minutes / 60)}h ${minutes % 60}m`\n}\n\nfunction list(values: string[]): string {\n return values.length ? values.join(\", \") : \"none\"\n}\n\nfunction describeAuth(auth: ProxyAuthCheck): string {\n switch (auth.status) {\n case \"ok\":\n return `${auth.code}, good`\n case \"unsafe\":\n return `${auth.code}, UNSAFE: an unauthenticated caller was accepted. Restart every opencode window; a window opened before 0.13.2 keeps serving an open port.`\n case \"unreachable\":\n return `could not be checked (${auth.error})`\n case \"skipped\":\n return \"not checked\"\n }\n}\n\n/**\n * Markdown, in one text part, led by `DOCTOR_MARKER`. Pure so a test can pin\n * the whole report against a fixed object; everything live is gathered in\n * `gatherDoctorReport`.\n */\nexport function formatDoctorReport(report: DoctorReport): string {\n const lines: string[] = []\n lines.push(DOCTOR_MARKER)\n lines.push(\"\")\n lines.push(\"| Field | Value |\")\n lines.push(\"|---|---|\")\n lines.push(`| plugin | ${report.plugin} |`)\n lines.push(`| opencode | ${report.opencode} |`)\n lines.push(`| claude CLI | \\`${report.claudeCli.path}\\` (${report.claudeCli.version}) |`)\n lines.push(`| cwd | \\`${report.cwd.resolved}\\` (${report.cwd.source}) |`)\n lines.push(`| providers | ${list(report.providers)} |`)\n lines.push(`| accounts | ${list(report.accounts)} |`)\n lines.push(`| proxyTools | ${list(report.proxyTools)} |`)\n lines.push(`| MCP servers (on disk) | ${list(report.mcpServers)} |`)\n lines.push(`| transport | ${report.transport} |`)\n lines.push(`| planModeQuestion | ${report.planModeQuestion} |`)\n lines.push(`| turnStats | ${report.turnStats} |`)\n lines.push(`| ANTHROPIC_API_KEY in env | ${report.anthropicApiKeyInEnv ? \"yes\" : \"no\"} |`)\n\n lines.push(\"\")\n lines.push(\"**Live `claude` processes**\")\n lines.push(\"\")\n if (report.processes.length === 0) {\n lines.push(\"None. The next message in a Claude Code session spawns one.\")\n } else {\n lines.push(\"| session | model | pid | in flight | age | effort |\")\n lines.push(\"|---|---|---|---|---|---|\")\n for (const proc of report.processes) {\n lines.push(\n `| ${proc.session}${proc.compaction ? \" (compaction)\" : \"\"} | ${proc.model} | ${\n proc.pid ?? \"unknown\"\n } | ${proc.inFlight ? \"yes\" : \"no\"} | ${formatAge(proc.ageMs)} | ${proc.effort ?? \"inherited\"} |`,\n )\n }\n }\n\n lines.push(\"\")\n lines.push(\"**Pending proxy calls**\")\n lines.push(\"\")\n if (report.pendingCalls.length === 0) {\n lines.push(\"None.\")\n } else {\n lines.push(\"| tool | call id | age | deadline |\")\n lines.push(\"|---|---|---|---|\")\n for (const call of report.pendingCalls) {\n // A deadline of 0 is \"none\": task calls wait for the subagent by default.\n const deadline = call.deadlineMs > 0 ? formatAge(call.deadlineMs) : \"none\"\n lines.push(\n `| ${call.toolName} | \\`${call.toolCallId}\\` | ${formatAge(call.ageMs)} | ${deadline} |`,\n )\n }\n }\n\n lines.push(\"\")\n lines.push(\"**Proxy servers**\")\n lines.push(\"\")\n if (report.proxyServers.length === 0) {\n lines.push(\"None running.\")\n } else {\n lines.push(\"| url | unauthenticated `initialize` |\")\n lines.push(\"|---|---|\")\n for (const server of report.proxyServers) {\n lines.push(`| ${server.url} | ${describeAuth(server.auth)} |`)\n }\n }\n\n const stderr = report.processes.filter((proc) => proc.lastStderr)\n if (stderr.length > 0) {\n lines.push(\"\")\n lines.push(\"**Last stderr**\")\n lines.push(\"\")\n for (const proc of stderr) {\n lines.push(`\\`${proc.session}\\`:`)\n lines.push(\"\")\n lines.push(\"```text\")\n lines.push(proc.lastStderr!.trimEnd())\n lines.push(\"```\")\n }\n }\n\n return lines.join(\"\\n\")\n}\n\n/**\n * The security probe from the README: an unauthenticated `initialize` with the\n * right Host, no Origin and a JSON content type must be refused. 401 is the\n * patched behaviour; a 200 means this opencode window predates 0.13.2 and is\n * serving an open loopback port that executes Bash through opencode.\n *\n * Deliberately only `initialize`: a `tools/call` probe would run something.\n */\nexport async function checkProxyAuth(\n url: string,\n fetchImpl: typeof fetch = fetch,\n timeoutMs = 3000,\n): Promise<ProxyAuthCheck> {\n let authority: string\n try {\n authority = new URL(url).host\n } catch {\n return { status: \"skipped\" }\n }\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n const response = await fetchImpl(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", host: authority },\n body: JSON.stringify({ jsonrpc: \"2.0\", id: 0, method: \"initialize\", params: {} }),\n signal: controller.signal,\n })\n // Drain so the socket is not left half-read.\n await response.text().catch(() => \"\")\n return response.status === 401\n ? { status: \"ok\", code: response.status }\n : { status: \"unsafe\", code: response.status }\n } catch (error) {\n return {\n status: \"unreachable\",\n error: error instanceof Error ? error.message : String(error),\n }\n } finally {\n clearTimeout(timer)\n }\n}\n\nexport interface GatherDoctorOptions {\n cliPath: string\n interactive: boolean\n turnStats: boolean\n fetchImpl?: typeof fetch\n}\n\n/** Assemble the live report. Never throws: a broken field reads as unknown. */\nexport async function gatherDoctorReport(\n options: GatherDoctorOptions,\n): Promise<DoctorReport> {\n const providers = lastDiagnosticsProviders()\n const opencodeVersion =\n lastKnownOpencodeVersion() ??\n process.env.OPENCODE_VERSION ??\n (await detectOpencodeVersion().catch(() => undefined))\n const { claudeCliPath, ...base } = collectStartupDiagnostics(providers, opencodeVersion)\n const cliPath = options.cliPath || claudeCliPath\n const cli = await detectCliVersion(cliPath).catch(() => null)\n\n const processes = snapshotActiveProcesses()\n const seen = new Set<string>()\n const proxyServers: DoctorProxyRow[] = []\n for (const proc of processes) {\n if (!proc.proxyUrl || seen.has(proc.proxyUrl)) continue\n seen.add(proc.proxyUrl)\n proxyServers.push({\n url: proc.proxyUrl,\n auth: await checkProxyAuth(proc.proxyUrl, options.fetchImpl ?? fetch),\n })\n }\n\n return {\n plugin: base.plugin,\n opencode: base.opencode,\n claudeCli: { path: cliPath, version: cli?.raw ?? \"not detected\" },\n cwd: base.cwd,\n providers: base.providers,\n accounts: base.accounts,\n proxyTools: base.proxyTools,\n mcpServers: base.mcpServers,\n transport: options.interactive || base.interactiveTransport ? \"interactive\" : \"headless\",\n planModeQuestion: base.planModeQuestion,\n turnStats: options.turnStats,\n anthropicApiKeyInEnv: base.anthropicApiKeyInEnv,\n processes,\n pendingCalls: snapshotPendingProxyCalls(),\n proxyServers,\n }\n}\n\n/** The whole command: gather, format, and never let a failure eat the answer. */\nexport async function buildDoctorReport(options: GatherDoctorOptions): Promise<string> {\n try {\n const report = await gatherDoctorReport(options)\n log.info(\"claude-code doctor report\", {\n plugin: report.plugin,\n opencode: report.opencode,\n cwd: report.cwd,\n processes: report.processes.length,\n pendingCalls: report.pendingCalls.length,\n proxyServers: report.proxyServers.map((server) => server.auth.status),\n })\n return formatDoctorReport(report)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n log.warn(\"claude-code doctor failed to build its report\", { error: message })\n return `${DOCTOR_MARKER}\\n\\nCould not build the report: ${message}`\n }\n}\n","import type { ClaudeStreamMessage } from \"./types.js\"\n\n/**\n * What a finished Claude CLI turn cost, and how much of its input came out of\n * the prompt cache.\n *\n * The CLI already reports all of it on the terminal `result` line, and until\n * now most of it was thrown away: `modelUsage` and `permission_denials` were\n * dropped outright, and the rest only reached `providerMetadata`, where\n * nothing in opencode's UI shows it. The numbers below are always logged at\n * INFO; the one-line footer is opt-in via the `turnStats` provider option,\n * because a cost line under every single reply is a preference, not a default.\n */\nexport interface TurnStats {\n costUsd?: number\n durationMs?: number\n durationApiMs?: number\n numTurns?: number\n inputTokens?: number\n outputTokens?: number\n cacheReadTokens?: number\n cacheWriteTokens?: number\n /** Per-model totals, keyed by model id. Present from CLI 2.1.x on. */\n modelUsage?: Record<string, unknown>\n /** Tool calls the permission layer refused during the turn. */\n permissionDenials?: unknown[]\n}\n\n/**\n * Header of the footer block, and the marker `message-builder` strips by when\n * a transcript is rebuilt for a fresh Claude process. The footer is the\n * plugin's own accounting, never something the model said, so it must not come\n * back as model output on a resume. Kept as the first characters of its own\n * text part so the strip is exact.\n */\nexport const TURN_STATS_MARKER = \"▌ **stats:**\"\n\n/**\n * Usage here is the turn total, not the last iteration `toUsage` prefers.\n * Those two answer different questions: `toUsage` feeds opencode's context\n * gauge, where summing every tool-use iteration would inflate the window and\n * trigger premature compaction, while a cost footer has to match the cost the\n * CLI reports, and that cost is cumulative over the whole turn.\n */\nexport function extractTurnStats(msg: ClaudeStreamMessage): TurnStats {\n const usage = msg.usage\n const stats: TurnStats = {}\n if (typeof msg.total_cost_usd === \"number\") stats.costUsd = msg.total_cost_usd\n if (typeof msg.duration_ms === \"number\") stats.durationMs = msg.duration_ms\n if (typeof msg.duration_api_ms === \"number\") stats.durationApiMs = msg.duration_api_ms\n if (typeof msg.num_turns === \"number\") stats.numTurns = msg.num_turns\n if (typeof usage?.input_tokens === \"number\") stats.inputTokens = usage.input_tokens\n if (typeof usage?.output_tokens === \"number\") stats.outputTokens = usage.output_tokens\n if (typeof usage?.cache_read_input_tokens === \"number\") {\n stats.cacheReadTokens = usage.cache_read_input_tokens\n }\n if (typeof usage?.cache_creation_input_tokens === \"number\") {\n stats.cacheWriteTokens = usage.cache_creation_input_tokens\n }\n if (msg.modelUsage && typeof msg.modelUsage === \"object\") stats.modelUsage = msg.modelUsage\n if (Array.isArray(msg.permission_denials)) stats.permissionDenials = msg.permission_denials\n return stats\n}\n\n/** Dollars, at the precision the number actually carries information at. */\nexport function formatCost(costUsd: number): string {\n if (!Number.isFinite(costUsd) || costUsd < 0) return \"$0.00\"\n return costUsd >= 1 ? `$${costUsd.toFixed(2)}` : `$${costUsd.toFixed(4)}`\n}\n\nexport function formatDuration(durationMs: number): string {\n if (!Number.isFinite(durationMs) || durationMs < 0) return \"0.0 s\"\n if (durationMs < 60_000) return `${(durationMs / 1000).toFixed(1)} s`\n const totalSeconds = Math.round(durationMs / 1000)\n return `${Math.floor(totalSeconds / 60)}m ${totalSeconds % 60}s`\n}\n\nexport function formatTokens(tokens: number): string {\n if (!Number.isFinite(tokens) || tokens < 0) return \"0\"\n if (tokens < 1000) return String(Math.round(tokens))\n if (tokens < 1_000_000) return `${(tokens / 1000).toFixed(1)}k`\n return `${(tokens / 1_000_000).toFixed(1)}M`\n}\n\n/**\n * One compact line, or null when the CLI reported nothing worth a line.\n *\n * Zero-valued cache counters are dropped rather than printed as `0`: a turn\n * with no cache activity should read as short, not as a row of zeroes. Cost,\n * duration and turn count are printed whenever the CLI sent them, including at\n * zero, because a genuinely free turn is information.\n */\nexport function formatTurnStatsLine(stats: TurnStats): string | null {\n const parts: string[] = []\n if (stats.costUsd !== undefined) parts.push(formatCost(stats.costUsd))\n if (stats.durationMs !== undefined) parts.push(formatDuration(stats.durationMs))\n if (stats.numTurns !== undefined) {\n parts.push(`${stats.numTurns} CLI ${stats.numTurns === 1 ? \"turn\" : \"turns\"}`)\n }\n if (stats.inputTokens !== undefined) parts.push(`in ${formatTokens(stats.inputTokens)}`)\n if (stats.outputTokens !== undefined) parts.push(`out ${formatTokens(stats.outputTokens)}`)\n if (stats.cacheReadTokens) parts.push(`cache read ${formatTokens(stats.cacheReadTokens)}`)\n if (stats.cacheWriteTokens) parts.push(`cache write ${formatTokens(stats.cacheWriteTokens)}`)\n if (stats.permissionDenials?.length) {\n const count = stats.permissionDenials.length\n parts.push(`${count} permission ${count === 1 ? \"denial\" : \"denials\"}`)\n }\n if (parts.length === 0) return null\n return `${TURN_STATS_MARKER} ${parts.join(\" · \")}`\n}\n\n/** The footer as its own text part: a leading newline keeps it off the reply's last line. */\nexport function formatTurnStatsBlock(stats: TurnStats): string | null {\n const line = formatTurnStatsLine(stats)\n return line === null ? null : `\\n${line}\\n`\n}\n\n/** Flat payload for the INFO line, which is emitted whether or not the footer is. */\nexport function turnStatsLogPayload(stats: TurnStats): Record<string, unknown> {\n return {\n costUsd: stats.costUsd ?? null,\n durationMs: stats.durationMs ?? null,\n durationApiMs: stats.durationApiMs ?? null,\n numTurns: stats.numTurns ?? null,\n inputTokens: stats.inputTokens ?? null,\n outputTokens: stats.outputTokens ?? null,\n cacheReadTokens: stats.cacheReadTokens ?? null,\n cacheWriteTokens: stats.cacheWriteTokens ?? null,\n modelUsage: stats.modelUsage ?? null,\n permissionDenials: stats.permissionDenials?.length ?? 0,\n }\n}\n","import type { LanguageModelV3 } from \"@ai-sdk/provider\"\nimport {\n ACCOUNT_BLOCK_MARKER,\n FAILOVER_MARKER,\n stripAccountFailoverParts,\n} from \"./account-failover.js\"\nimport { INLINE_ASIDE_MARKER, LEGACY_INLINE_ASIDE_MARKERS } from \"./btw-command.js\"\nimport {\n COMPACT_BOUNDARY_MARKER,\n CONVERSATION_RESET_MARKER,\n RATE_LIMIT_MARKER,\n RESULT_ERROR_MARKER,\n STREAM_TIMEOUT_MARKER,\n} from \"./cli-events.js\"\nimport { DOCTOR_MARKER, parseDoctorCommandContent } from \"./doctor.js\"\nimport { log } from \"./logger.js\"\nimport { parseSideQuestionContent } from \"./side-question.js\"\nimport { TURN_STATS_MARKER } from \"./turn-stats.js\"\n\ntype Prompt = Parameters<LanguageModelV3[\"doGenerate\"]>[0][\"prompt\"]\n\n/**\n * Leading markers of text parts the plugin itself wrote into an assistant\n * reply: the `/btw` aside and its pre-bar form, the turn-stats footer, and the\n * `▌` notes for a CLI compaction, a rate-limit rejection and a failed result\n * subtype. None of them was ever model output or ever in Claude's context, so\n * a transcript rebuilt for a fresh CLI process must not hand any of them back\n * as something Claude said. Each is the first characters of its own text part,\n * which is what makes the strip exact instead of a guess at where a block ends.\n */\nconst PLUGIN_NOTE_MARKERS = [\n INLINE_ASIDE_MARKER,\n ...LEGACY_INLINE_ASIDE_MARKERS,\n TURN_STATS_MARKER,\n COMPACT_BOUNDARY_MARKER,\n CONVERSATION_RESET_MARKER,\n RATE_LIMIT_MARKER,\n RESULT_ERROR_MARKER,\n DOCTOR_MARKER,\n STREAM_TIMEOUT_MARKER,\n FAILOVER_MARKER,\n ACCOUNT_BLOCK_MARKER,\n]\n\nfunction isPluginNote(part: any): boolean {\n if (!part || part.type !== \"text\" || typeof part.text !== \"string\") return false\n const text = part.text.trimStart()\n return PLUGIN_NOTE_MARKERS.some((marker) => text.startsWith(marker))\n}\n\nfunction stripPluginNotes(content: unknown): unknown {\n if (!Array.isArray(content)) return content\n const kept = content.filter((part: any) => !isPluginNote(part))\n return kept.length === content.length ? content : kept\n}\n\n/**\n * Drop every plugin-authored exchange and note from a transcript before it is\n * replayed to the CLI: the `/btw` question with its answer, the\n * `/claude-code-doctor` report with its command, and the `▌` blocks listed in\n * `PLUGIN_NOTE_MARKERS`. Named for the `/btw` case it started as; it is the\n * one place all of them are removed, and it is called from both transcript\n * rebuild paths.\n */\nexport function filterSideQuestionHistory(prompt: Prompt): Prompt {\n // The account-failover form is a synthetic `question` call Claude never\n // issued, answered by a `tool-result` it never saw. It has to come out\n // before anything is replayed, which is on the switch turn by definition.\n prompt = stripAccountFailoverParts(prompt)\n let pluginCommand = false\n const kept = prompt.filter((message) => {\n if (message.role === \"user\") {\n pluginCommand =\n parseSideQuestionContent(message.content) !== null ||\n parseDoctorCommandContent(message.content) !== null\n return !pluginCommand\n }\n return message.role !== \"assistant\" || !pluginCommand\n })\n return kept.map((message) =>\n message.role === \"assistant\" ? ({ ...message, content: stripPluginNotes(message.content) } as typeof message) : message,\n )\n}\n\n/**\n * opencode-dcp anchors its nudges into message text as\n * `<dcp-system-reminder>` blocks (its `lib/messages/inject/utils.ts` appends\n * one to an existing text part, or splices in a synthetic part), and the\n * loudest of them orders the model to \"use the `compress` tool now\". Under\n * this provider that tool is only reachable when the operator forwards it,\n * so otherwise the block is an order that cannot be obeyed, carried by every\n * message it is anchored to.\n *\n * Blocks are removed wherever they sit rather than by matching a whole part,\n * because dcp appends its own `<dcp-message-id>` marker after one and an\n * end-anchored check would miss it. That is the same trap the `/btw`\n * reminder strip hit in production.\n */\nconst DCP_REMINDER_BLOCK =\n /<dcp-system-reminder\\b[^>]*>[\\s\\S]*?<\\/dcp-system-reminder>/gi\n\n/** Remove every dcp reminder block from one piece of text. */\nexport function stripContextReminderBlocks(text: string): string {\n if (!text.includes(\"<dcp-system-reminder\")) return text\n return text.replace(DCP_REMINDER_BLOCK, \"\").replace(/\\n{3,}/g, \"\\n\\n\").trim()\n}\n\n/**\n * Whether this turn should strip those blocks: the operator opted in AND no\n * `compress` tool is being proxied. When compress IS proxied the reminder is\n * satisfiable and must survive, or the model is told to compress by nothing\n * and holds a tool it never learns it needs.\n *\n * Resolved from configuration alone so it is available before the spawn\n * block decides anything. A name that is configured but missing from\n * opencode's registry therefore counts as callable and nothing is stripped,\n * which is the conservative direction: the cost is a reminder that stays.\n */\nexport function shouldStripContextReminders(options: {\n enabled?: boolean\n proxyTools?: readonly string[]\n proxyOpencodeTools?: readonly string[]\n}): boolean {\n if (options.enabled !== true) return false\n const namesCompress = (list?: readonly string[]): boolean =>\n (list ?? []).some((name) => String(name).trim().toLowerCase() === \"compress\")\n return !namesCompress(options.proxyTools) && !namesCompress(options.proxyOpencodeTools)\n}\n\n/**\n * Strip dcp reminder blocks from every user and assistant text part.\n *\n * Emptied parts are kept as empty strings rather than dropped: a nudge is\n * sometimes a message's only text part, and removing the part outright could\n * leave a user message with no content at all, which takes the empty-content\n * sentinel path in `getClaudeUserMessage`. Every consumer here already skips\n * a falsy `text`.\n */\nexport function stripContextReminders(prompt: Prompt): {\n prompt: Prompt\n removed: number\n} {\n let removed = 0\n const countIn = (text: string): number =>\n (text.match(DCP_REMINDER_BLOCK) ?? []).length\n\n const out = prompt.map((message) => {\n if (message.role !== \"user\" && message.role !== \"assistant\") return message\n\n // AI SDK v3 always delivers user/assistant content as a part array, so\n // there is no string form to handle here.\n if (!Array.isArray(message.content)) return message\n\n let touched = false\n const parts = (message.content as any[]).map((part) => {\n if (!part || part.type !== \"text\" || typeof part.text !== \"string\") return part\n const hits = countIn(part.text)\n if (hits === 0) return part\n removed += hits\n touched = true\n return { ...part, text: stripContextReminderBlocks(part.text) }\n })\n return touched ? ({ ...message, content: parts } as typeof message) : message\n })\n\n return removed > 0 ? { prompt: out, removed } : { prompt, removed: 0 }\n}\n\nconst SUPPORTED_IMAGE_TYPES = new Set([\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n])\n\nfunction toImageBlock(part: any): any | null {\n const raw: unknown = part.image ?? part.data ?? part.url ?? part.source?.data\n if (!raw) {\n log.warn(\"file part without data, skipping\")\n return null\n }\n\n let resolvedMediaType: string = part.mediaType || part.mimeType || part.mime || \"\"\n let base64: string | null = null\n\n if (typeof raw === \"string\") {\n if (raw.startsWith(\"data:\")) {\n const match = /^data:([^;,]+)(?:;[^,]*)*(?:;base64)?,(.*)$/s.exec(raw)\n if (!match) {\n log.warn(\"malformed data URI, skipping file part\")\n return null\n }\n resolvedMediaType = resolvedMediaType || match[1]\n base64 = match[2]\n } else if (/^https?:\\/\\//i.test(raw)) {\n log.warn(\"remote URL images are not supported by Claude CLI, skipping\")\n return null\n } else {\n base64 = raw\n }\n } else if (raw instanceof URL) {\n log.warn(\"remote URL images are not supported by Claude CLI, skipping\")\n return null\n } else if (raw instanceof Uint8Array || Buffer.isBuffer(raw)) {\n base64 = Buffer.from(raw as Uint8Array).toString(\"base64\")\n } else {\n log.warn(\"unsupported file part data type\", { dataType: typeof raw })\n return null\n }\n\n if (!resolvedMediaType || !SUPPORTED_IMAGE_TYPES.has(resolvedMediaType)) {\n log.warn(\"unsupported media type for Claude image block, skipping\", {\n mediaType: resolvedMediaType,\n })\n return null\n }\n\n return {\n type: \"image\",\n source: { type: \"base64\", media_type: resolvedMediaType, data: base64 },\n }\n}\n\nfunction getToolResultText(part: any): string {\n const value = part.output ?? part.result\n\n if (typeof value === \"string\") {\n return value\n }\n\n if (!value || typeof value !== \"object\") {\n return JSON.stringify(value)\n }\n\n switch (value.type) {\n case \"text\":\n case \"error-text\":\n return String(value.value)\n case \"json\":\n case \"error-json\":\n return JSON.stringify(value.value)\n case \"execution-denied\":\n return value.reason ? `Execution denied: ${value.reason}` : \"Execution denied\"\n case \"content\":\n return Array.isArray(value.value)\n ? value.value\n .map((item: any) => {\n if (item?.type === \"text\") return item.text\n return JSON.stringify(item)\n })\n .join(\"\\n\")\n : JSON.stringify(value.value)\n default:\n return JSON.stringify(value)\n }\n}\n\n// Compaction-mode caps. These are the only knobs that affect how much\n// transcript content reaches the model when opencode invokes /compact.\n// 180k chars ≈ 60k tokens worst-case — well under Haiku 4.5's 200k window\n// after accounting for system prompt + output budget.\nconst MAX_HISTORY_CHARS = 180_000\nconst MAX_TOOL_RESULT_CHARS = 10_000\nconst MAX_TOOL_INPUT_CHARS = 2_000\n\nfunction clipWithMarker(text: string, max: number): string {\n if (text.length <= max) return text\n return `${text.slice(0, max)}\\n…[truncated ${text.length - max} chars]`\n}\n\nfunction renderToolInput(input: unknown): string {\n let raw: string\n try {\n raw = typeof input === \"string\" ? input : JSON.stringify(input)\n } catch {\n raw = String(input)\n }\n return clipWithMarker(raw, MAX_TOOL_INPUT_CHARS)\n}\n\nfunction renderMessageContentForCompaction(\n msg: any,\n): { text: string; toolResultCount: number } {\n const lines: string[] = []\n let toolResultCount = 0\n\n if (typeof msg.content === \"string\") {\n return { text: msg.content, toolResultCount: 0 }\n }\n\n if (!Array.isArray(msg.content)) {\n return { text: \"\", toolResultCount: 0 }\n }\n\n for (const part of msg.content as any[]) {\n if (!part) continue\n switch (part.type) {\n case \"text\":\n if (part.text) lines.push(part.text)\n break\n case \"tool-call\":\n lines.push(\n `[tool_use:${part.toolName ?? \"unknown\"}(${renderToolInput(part.input)})]`,\n )\n break\n case \"tool-result\":\n toolResultCount++\n lines.push(\n `[tool_result:${part.toolName ?? part.toolCallId ?? \"unknown\"}]\\n${clipWithMarker(\n getToolResultText(part),\n MAX_TOOL_RESULT_CHARS,\n )}`,\n )\n break\n case \"image\":\n lines.push(\n `[image: ${part.mediaType ?? part.mimeType ?? \"unknown\"}]`,\n )\n break\n case \"file\":\n lines.push(\n `[file: ${part.mediaType ?? part.mimeType ?? \"unknown\"}]`,\n )\n break\n case \"reasoning\":\n // Skip reasoning blocks in compaction — they bloat input without\n // helping the summarizer.\n break\n }\n }\n\n return { text: lines.join(\"\\n\"), toolResultCount }\n}\n\n/**\n * Compact conversation history into a context summary.\n *\n * - mode \"fresh-session\" (default): includes user, assistant and tool roles,\n * renders each with the same serializer /compact uses so tool inputs and\n * result bodies survive, then clips each message at 2000 chars. Used when\n * starting a fresh CLI session that lost its prior session id. It used to\n * filter to user/assistant only and reduce tool content to\n * `[Called N tool(s)]` placeholders, which silently dropped subagent\n * output entirely (issue #29).\n * - mode \"compaction\": rich serializer for opencode /compact. Includes\n * tool roles, renders tool_use input and tool_result content (each\n * clipped at MAX_TOOL_RESULT_CHARS), and caps aggregate output at\n * MAX_HISTORY_CHARS by dropping oldest entries first.\n */\nexport function compactConversationHistory(\n prompt: Prompt,\n opts: { mode?: \"fresh-session\" | \"compaction\" } = {},\n): string | null {\n const mode = opts.mode ?? \"fresh-session\"\n prompt = filterSideQuestionHistory(prompt)\n\n if (mode === \"compaction\") {\n return buildCompactionHistory(prompt)\n }\n\n // `tool`-role messages carry the results of everything opencode ran itself,\n // so they belong in the transcript. Filtering them out (issue #29) meant a\n // subagent's whole answer vanished: the assistant message kept a\n // `[Called 1 tool(s): task]` placeholder and the result it referred to was\n // never rendered at all.\n const conversationMessages = prompt.filter(\n (m) => m.role === \"user\" || m.role === \"assistant\" || m.role === \"tool\",\n )\n\n if (conversationMessages.length <= 1) {\n return null\n }\n\n const historyParts: string[] = []\n\n for (let i = 0; i < conversationMessages.length - 1; i++) {\n const msg = conversationMessages[i]\n const role =\n msg.role === \"user\" ? \"User\" : msg.role === \"assistant\" ? \"Assistant\" : \"Tool\"\n\n // Same renderer the /compact transcript uses, so tool inputs and result\n // bodies survive instead of collapsing to counts. This path used to write\n // `[Called N tool(s): ...]` / `[Received N tool result(s)]` and discard\n // every byte of the payload, which is the second half of issue #29.\n const { text } = renderMessageContentForCompaction(msg)\n\n if (text.trim()) {\n const truncated =\n text.length > 2000 ? text.slice(0, 2000) + \"...\" : text\n historyParts.push(`${role}: ${truncated}`)\n }\n }\n\n if (historyParts.length === 0) {\n return null\n }\n\n return historyParts.join(\"\\n\\n\")\n}\n\nfunction buildCompactionHistory(prompt: Prompt): string | null {\n // Iterate newest-first, accumulate up to MAX_HISTORY_CHARS, then reverse\n // to chronological order. Oldest messages get dropped when the budget\n // is exhausted — they are the least relevant for a summary of recent\n // work.\n const entries: string[] = []\n let total = 0\n let totalToolResults = 0\n let droppedOldest = 0\n\n // Skip the trailing user message: opencode's /compact appends the\n // synthesis instruction as the final user turn. The instruction itself\n // is added by getClaudeUserMessage after the transcript block, so we\n // don't want it duplicated inside the transcript.\n const end = prompt.length > 0 && prompt[prompt.length - 1].role === \"user\"\n ? prompt.length - 1\n : prompt.length\n\n for (let i = end - 1; i >= 0; i--) {\n const msg = prompt[i] as any\n const roleLabel =\n msg.role === \"user\"\n ? \"User\"\n : msg.role === \"assistant\"\n ? \"Assistant\"\n : msg.role === \"tool\"\n ? \"Tool\"\n : msg.role\n\n const { text, toolResultCount } = renderMessageContentForCompaction(msg)\n if (!text.trim()) continue\n\n const entry = `${roleLabel}: ${text}`\n if (total + entry.length > MAX_HISTORY_CHARS) {\n droppedOldest = i + 1\n break\n }\n entries.push(entry)\n total += entry.length + 2 // +2 for the \"\\n\\n\" join\n totalToolResults += toolResultCount\n }\n\n if (entries.length === 0) return null\n\n entries.reverse()\n log.info(\"built compaction history\", {\n entries: entries.length,\n chars: total,\n toolResults: totalToolResults,\n droppedOldestBefore: droppedOldest,\n })\n\n return entries.join(\"\\n\\n\")\n}\n\n/**\n * Convert AI SDK prompt into a Claude CLI stream-json user message.\n *\n * `compactionMode` switches behavior for opencode /compact: the prior\n * transcript is rendered with rich tool content (not placeholders) and the\n * wrapper framing tells the model this is the authoritative thread.\n *\n * Reasoning effort is not part of the message. It used to ride here as a\n * thinking keyword (\"(ultrathink)\"), but Claude Code dropped every keyword\n * except that one, so effort now reaches the CLI as CLAUDE_CODE_EFFORT_LEVEL\n * at spawn time (see `claudeSpawnEnv`).\n */\nexport function getClaudeUserMessage(\n prompt: Prompt,\n includeHistoryContext: boolean = false,\n opts: {\n compactionMode?: boolean\n cliToolCallIds?: ReadonlySet<string>\n stripContextReminders?: boolean\n } = {},\n): string {\n const compactionMode = opts.compactionMode === true\n const cliToolCallIds = opts.cliToolCallIds\n const content: any[] = []\n\n // The account-failover form is the plugin's own dialog: Claude never issued\n // that call and never saw its answer. The transcript rebuilds stripped it\n // already; the current message did not, so on the turn after a form the\n // answer reached Claude as a stray `<opencode_tool_result>` (\"The user\n // dismissed this question\"), measured 2026-09-23.\n prompt = stripAccountFailoverParts(prompt)\n\n // Done once here, at the top, so every path below (the current message,\n // the fresh-session rebuild and the /compact transcript) sees the cleaned\n // text without each needing its own flag.\n if (opts.stripContextReminders) {\n const stripped = stripContextReminders(prompt)\n if (stripped.removed > 0) {\n log.info(\"stripped unsatisfiable context reminders\", {\n blocks: stripped.removed,\n })\n prompt = stripped.prompt\n }\n }\n\n /**\n * A `tool_result` block is only meaningful to a resumed CLI session when\n * that session issued the matching `tool_use`. Anything opencode ran on its\n * own behalf (a `subtask: true` command's `task` call, issue #29) has an id\n * the CLI never emitted, so the block is orphaned: Claude cannot resolve it\n * and the payload, which is right there in the envelope, is unreachable.\n * Those are rendered as plain text instead, which keeps the content and\n * loses only the pairing the CLI could not have honoured anyway.\n *\n * `cliToolCallIds` is the set of calls this CLI process is waiting on. When\n * a caller does not supply it we keep the old unconditional block, so a\n * forgotten call site degrades to today's behaviour rather than breaking\n * the proxy round-trip.\n */\n const pushToolResult = (part: any): void => {\n const id = part.toolCallId\n const text = getToolResultText(part)\n if (!cliToolCallIds || cliToolCallIds.has(id)) {\n content.push({ type: \"tool_result\", tool_use_id: id, content: text })\n return\n }\n log.info(\"rendering opencode-side tool result as text\", {\n toolCallId: id,\n toolName: part.toolName,\n chars: text.length,\n })\n content.push({\n type: \"text\",\n text: `<opencode_tool_result tool=\"${part.toolName ?? \"unknown\"}\">\\n${text}\\n</opencode_tool_result>`,\n })\n }\n\n if (compactionMode) {\n const transcript = compactConversationHistory(prompt, {\n mode: \"compaction\",\n })\n if (transcript) {\n log.info(\"including compaction transcript\", {\n historyLength: transcript.length,\n })\n content.push({\n type: \"text\",\n text: `<conversation_transcript>\n${transcript}\n</conversation_transcript>\n\nThe complete prior conversation appears above. The synthesis instructions follow below.\n\n`,\n })\n }\n } else if (includeHistoryContext) {\n const historyContext = compactConversationHistory(prompt)\n if (historyContext) {\n log.info(\"including conversation history context\", {\n historyLength: historyContext.length,\n })\n content.push({\n type: \"text\",\n text: `<conversation_history>\nThe following is a summary of our conversation so far (from a previous session that couldn't be resumed):\n\n${historyContext}\n\n</conversation_history>\n\nNow continuing with the current message:\n\n`,\n })\n }\n }\n\n // Find messages since last assistant message\n const messages: typeof prompt = []\n for (let i = prompt.length - 1; i >= 0; i--) {\n if (prompt[i].role === \"assistant\") break\n messages.unshift(prompt[i])\n }\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n if (parseSideQuestionContent(msg.content) !== null) continue\n if (typeof msg.content === \"string\") {\n const str = msg.content as string\n if (str.trim()) {\n content.push({ type: \"text\", text: str })\n }\n } else if (Array.isArray(msg.content)) {\n for (const part of msg.content as any[]) {\n if (part.type === \"text\") {\n if (part.text && part.text.trim()) {\n content.push({ type: \"text\", text: part.text })\n }\n } else if (part.type === \"file\" || part.type === \"image\") {\n const block = toImageBlock(part)\n if (block) {\n content.push(block)\n } else {\n log.debug(\"skipped non-image file part\", {\n mediaType: part.mediaType,\n })\n }\n } else if (part.type === \"tool-result\") {\n pushToolResult(part)\n }\n }\n }\n } else if (msg.role === \"tool\") {\n // AI SDK V3 delivers tool results in `tool`-role messages, not `user`.\n // Without this branch we'd hit the empty-content sentinel path and\n // send \"(empty)\" to Claude CLI instead of the actual tool result —\n // forcing the user to press \"continue\" between proxy tool calls.\n if (Array.isArray(msg.content)) {\n for (const part of msg.content as any[]) {\n if (part?.type === \"tool-result\") {\n pushToolResult(part)\n }\n }\n }\n }\n }\n\n if (content.length === 0) {\n // CLI rejects a zero-block message with 400, and Anthropic rejects\n // whitespace-only text blocks — so we need a non-whitespace sentinel.\n // \"(empty)\" matches the parenthetical meta-note convention this file\n // already uses for reasoning keywords (\"(think)\", \"(megathink)\", etc.),\n // which the model reads as out-of-band metadata rather than a prompt to\n // continue its previous turn.\n log.warn(\"empty user content; sending sentinel to satisfy CLI\")\n return JSON.stringify({\n type: \"user\",\n message: {\n role: \"user\",\n content: [{ type: \"text\", text: \"(empty)\" }],\n },\n })\n }\n\n return JSON.stringify({\n type: \"user\",\n message: {\n role: \"user\",\n content,\n },\n })\n}\n","/**\n * Per-agent model resolution.\n *\n * opencode's agent config cannot express \"inherit the account, choose the\n * model\". A subagent that omits `model` inherits the invoking agent's WHOLE\n * model string, and one that pins `model` inherits neither half, so pinning\n * Opus also pins the account it was written with. That is the wrong trade on a\n * machine with more than one Claude account: the worker should follow whoever\n * invoked it and still run on the model the job needs.\n *\n * The account is not part of the model id this class sees. It lives in the\n * provider (`claude-code-<account>`), which selects CLAUDE_CONFIG_DIR at spawn\n * time, and in an `@<account>` marker riding on the id for non-default\n * accounts (see `parseModelId` in models.ts). So swapping the model NAME while\n * preserving that marker changes the model and nothing else, which is exactly\n * the gap in the config schema.\n *\n * Declaring it: an agent markdown file says `forceModel: <id>`, or the\n * `defaultSubagentModel` provider option covers every subagent at once.\n * Nothing needs a per-agent entry in opencode.json.\n *\n * The same file can state `reasoningEffort:`, which beats the effort opencode\n * inherited from the caller's picker (see `resolveAgentEffort`). Model and\n * effort together are what a turn costs, so both belong with the agent.\n *\n * Two deliberate silences, because this rewrites what a user's model picker\n * said it would run:\n *\n * - With `defaultSubagentModel` unset there is NO implicit override. An\n * existing setup upgrading the plugin behaves exactly as before, instead\n * of quietly moving somebody's cheap subagent onto an expensive model.\n * - Only agents this plugin discovered are eligible. opencode's built-ins\n * (`explore`, `general`, `compaction`, ...) are never in the registry, so\n * they are never rewritten.\n */\nimport { readFile, readdir } from \"node:fs/promises\"\nimport path from \"node:path\"\nimport { log } from \"./logger.js\"\nimport { defaultModels } from \"./models.js\"\n\n/** Directory names opencode reads agent markdown from, current form first. */\nexport const AGENT_DIR_NAMES = [\"agents\", \"agent\"]\n\n/** Levels the Claude CLI accepts; anything else is refused, not forwarded. */\nconst REASONING_EFFORTS = [\n \"minimal\",\n \"low\",\n \"medium\",\n \"high\",\n \"xhigh\",\n \"max\",\n]\n\nexport type AgentRecord = {\n mode?: string\n /** A fully-qualified `provider/model` the agent pinned for itself. */\n model?: string\n /** Model NAME this agent wants, on whatever account the caller is using. */\n forceModel?: string\n /** Thinking budget this agent wants, whatever the caller's picker says. */\n reasoningEffort?: string\n}\n\nlet registry: Record<string, AgentRecord> = {}\nlet defaultSubagentModel: string | undefined\n\nexport function setAgentRegistry(records: Record<string, AgentRecord>): void {\n registry = records\n}\n\nexport function getAgentRegistry(): Record<string, AgentRecord> {\n return registry\n}\n\n/** `undefined` (the default) means no implicit override for any agent. */\nexport function setDefaultSubagentModel(model: string | undefined): void {\n defaultSubagentModel = model?.trim() || undefined\n}\n\nexport function getDefaultSubagentModel(): string | undefined {\n return defaultSubagentModel\n}\n\nexport function _resetAgentRegistryForTests(): void {\n registry = {}\n defaultSubagentModel = undefined\n}\n\n/** `claude-opus-5-fast@work` -> `@work`; a default-account id has none. */\nfunction accountMarker(modelId: string): string {\n const at = modelId.indexOf(\"@\")\n return at === -1 ? \"\" : modelId.slice(at)\n}\n\nfunction withoutAccountMarker(modelId: string): string {\n const at = modelId.indexOf(\"@\")\n return at === -1 ? modelId : modelId.slice(0, at)\n}\n\n/**\n * The model a request should actually spawn with.\n *\n * Order, first match wins:\n * 1. The agent declared `forceModel`.\n * 2. The agent is a discovered subagent and `defaultSubagentModel` is set.\n * 3. Anything else: the id opencode asked for, untouched.\n *\n * An agent that pinned a full `provider/model` is out of scope entirely:\n * opencode already routed the call to that provider, and second-guessing it\n * here would silently undo a choice the user made explicitly.\n *\n * Fails closed. An id that is not in the model registry is refused and the\n * original kept, because the alternative is spawning the CLI with a `--model`\n * it will reject, on a turn someone is waiting for.\n */\nexport function resolveAgentModel(\n agent: string | undefined,\n modelId: string,\n overrides?: {\n records?: Record<string, AgentRecord>\n defaultSubagentModel?: string\n },\n): string {\n if (!agent) return modelId\n\n const record = (overrides?.records ?? registry)[agent]\n if (!record) return modelId\n if (record.model?.includes(\"/\")) return modelId\n\n const fallback = overrides\n ? overrides.defaultSubagentModel\n : defaultSubagentModel\n const declared = record.forceModel?.trim()\n const wanted =\n declared || (record.mode === \"subagent\" ? fallback : undefined)\n if (!wanted) return modelId\n\n // A `forceModel` carrying its own `@account` would be forcing an account,\n // which is the thing this exists to avoid. Keep the caller's.\n const base = withoutAccountMarker(wanted)\n if (!Object.hasOwn(defaultModels, base)) {\n log.warn(\"agent model override refused: unknown model\", {\n agent,\n wanted: base,\n keeping: modelId,\n })\n return modelId\n }\n\n const resolved = `${base}${accountMarker(modelId)}`\n if (resolved !== modelId) {\n log.debug(\"agent model override\", { agent, from: modelId, to: resolved })\n }\n return resolved\n}\n\n/**\n * The thinking budget a request should actually spawn with.\n *\n * opencode resolves one effort for the whole session (the model picker's\n * selector, or a variant), and a subagent inherits it. That inheritance is\n * wrong in the expensive direction: a caller who picked `max` for their own\n * turn silently hands `max` to every worker it dispatches, so a mechanical\n * lane runs at the most costly setting available and burns a weekly cap that\n * the caller never spent on the work in front of them.\n *\n * An agent that states its own budget wins. Same reasoning as `forceModel`:\n * the declaration lives with the agent, so a file on disk is the whole\n * configuration and the caller's picker stays a choice about the caller.\n *\n * Unknown values are ignored rather than passed on, since the CLI refuses a\n * level it does not recognise and the turn would die at spawn.\n */\nexport function resolveAgentEffort(\n agent: string | undefined,\n inherited: string | undefined,\n overrides?: { records?: Record<string, AgentRecord> },\n): string | undefined {\n if (!agent) return inherited\n\n const record = (overrides?.records ?? registry)[agent]\n const declared = record?.reasoningEffort?.trim()\n if (!declared) return inherited\n\n if (!REASONING_EFFORTS.includes(declared)) {\n log.warn(\"agent effort override refused: unknown level\", {\n agent,\n wanted: declared,\n keeping: inherited,\n })\n return inherited\n }\n\n if (declared !== inherited) {\n log.debug(\"agent effort override\", {\n agent,\n from: inherited,\n to: declared,\n })\n }\n return declared\n}\n\n/**\n * Read the four fields that matter out of an agent markdown file's YAML\n * frontmatter. Hand-parsed rather than pulling a YAML dependency in for four\n * scalars, and deliberately top-level only: `permission:` has nested keys\n * (`bash:`, `edit:`) that must not be mistaken for agent fields.\n */\nexport function parseAgentFrontmatter(text: string): AgentRecord {\n const record: AgentRecord = {}\n if (!text.startsWith(\"---\")) return record\n\n const lines = text.split(/\\r?\\n/)\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i]\n if (line.trim() === \"---\") break\n\n const match = /^([A-Za-z_][A-Za-z0-9_-]*):[ \\t]*(.*)$/.exec(line)\n if (!match) continue\n\n const key = match[1]\n if (\n key !== \"mode\" &&\n key !== \"model\" &&\n key !== \"forceModel\" &&\n key !== \"reasoningEffort\"\n )\n continue\n\n const value = match[2].trim().replace(/^[\"']|[\"']$/g, \"\")\n if (value) record[key] = value\n }\n\n return record\n}\n\n/**\n * Discover agents from markdown on disk. opencode merges these into its own\n * registry, but whether they reach a plugin's config hook is not documented,\n * so they are read directly rather than assumed.\n */\nexport async function readAgentMarkdownRecords(\n directories: string[],\n): Promise<Record<string, AgentRecord>> {\n const records: Record<string, AgentRecord> = {}\n\n for (const directory of directories) {\n let entries: string[]\n try {\n entries = await readdir(directory)\n } catch {\n continue\n }\n\n for (const entry of entries) {\n if (!entry.endsWith(\".md\")) continue\n\n const name = entry.slice(0, -3)\n if (records[name]) continue\n\n try {\n const text = await readFile(path.join(directory, entry), \"utf8\")\n records[name] = parseAgentFrontmatter(text)\n } catch (err) {\n log.debug(\"failed to read agent markdown\", {\n file: path.join(directory, entry),\n error: String(err),\n })\n }\n }\n }\n\n return records\n}\n\n/**\n * Every directory opencode would read agent markdown from, project before\n * global so a project agent of the same name wins, as opencode resolves them.\n */\nexport function agentDirectories(\n home: string | undefined,\n projectDirectory: string | undefined,\n): string[] {\n const directories: string[] = []\n\n if (projectDirectory) {\n for (const name of AGENT_DIR_NAMES) {\n directories.push(path.join(projectDirectory, \".opencode\", name))\n }\n }\n if (home) {\n for (const name of AGENT_DIR_NAMES) {\n directories.push(path.join(home, \".config\", \"opencode\", name))\n }\n }\n\n return directories\n}\n","import type { OpenCodeModel } from \"./opencode-types.js\"\n\nconst PROVIDER_ID = \"claude-code\"\nconst NPM = \"@khalilgharbaoui/opencode-claude-code-plugin\"\n\nconst reasoningVariants: Record<string, Record<string, unknown>> = {\n low: { reasoningEffort: \"low\" },\n medium: { reasoningEffort: \"medium\" },\n high: { reasoningEffort: \"high\" },\n xhigh: { reasoningEffort: \"xhigh\" },\n max: { reasoningEffort: \"max\" },\n}\n\nconst baseCapabilities = {\n temperature: false,\n attachment: true,\n toolcall: true,\n input: { text: true, audio: false, image: true, video: false, pdf: false },\n output: { text: true, audio: false, image: false, video: false, pdf: false },\n interleaved: false as const,\n}\n\nfunction defineModel(opts: {\n id: string\n name: string\n family: string\n reasoning: boolean\n context: number\n output: number\n cost: { input: number; output: number; cacheRead: number; cacheWrite: number }\n releaseDate: string\n // List-price multiplier relative to Haiku (the cheapest model). Derived\n // exactly from published per-token pricing: input AND output ratios both come\n // out to haiku 1, sonnet 3, opus 5 (opus 5.5: 4), fable/mythos 10. Rendered\n // as an `(N×)` suffix so it surfaces in opencode's model picker, which has no\n // dedicated multiplier field.\n // Display-only: model resolution keys off `id`.\n multiplier: number\n status?: OpenCodeModel[\"status\"]\n}): OpenCodeModel {\n return {\n id: opts.id,\n providerID: PROVIDER_ID,\n api: { id: opts.id, url: \"\", npm: NPM },\n name: `${opts.name} (${opts.multiplier}×)`,\n family: opts.family,\n capabilities: { ...baseCapabilities, reasoning: opts.reasoning },\n cost: {\n input: opts.cost.input,\n output: opts.cost.output,\n cache: { read: opts.cost.cacheRead, write: opts.cost.cacheWrite },\n },\n limit: { context: opts.context, output: opts.output },\n status: opts.status ?? \"active\",\n options: {},\n headers: {},\n release_date: opts.releaseDate,\n variants: opts.reasoning ? reasoningVariants : undefined,\n }\n}\n\n// Costs in US dollars per MILLION tokens, matching Anthropic's published\n// pricing verbatim. This is the unit opencode and models.dev use: opencode\n// divides by 1e6 itself when it multiplies a cost by a token count, so writing\n// per-token values here under-reports session cost by exactly 1,000,000x.\n// Compare models.dev's own entry for the same model:\n// `anthropic/claude-haiku-4-5 -> {\"input\": 1, \"output\": 5, \"cache_read\": 0.1,\n// \"cache_write\": 1.25}`.\n//\n// There is no long-context premium to model. Anthropic's pricing page states\n// that Claude 4.6 and later ship the full 1M-token context window at standard\n// pricing (\"a 900k-token request is billed at the same per-token rate as a\n// 9k-token request\"), and caching/batch discounts apply unchanged across it.\n// opencode 1.18.5 added optional `cost.tiers` / `cost.experimentalOver200K`\n// fields for above-200K pricing; they stay unset here deliberately, because a\n// tier would misreport the real price. Re-check only if Anthropic introduces\n// one. Verified against the pricing docs 2026-07-26.\nconst haikuCost = { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 }\nconst sonnetCost = { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }\n// Opus 4.5+ standard pricing is $5/M in, $25/M out (the price cut at 4.5; held\n// through 4.6/4.7/4.8/5). Cache read 0.1x input, cache write 1.25x input.\nconst opusCost = { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }\n// Fable 5 and Mythos 5 are the Mythos-class tier above Opus and share pricing\n// ($10/M in, $50/M out). Cache read/write follow Anthropic's standard 0.1x / 1.25x\n// input ratios (not separately published).\nconst fableCost = { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 }\n// Fable 5.1 and Mythos 5.1 keep the same input/output and cache-write rates,\n// but Anthropic cut cache reads to $0.25/M (one quarter of the 5.0 price).\nconst fable51Cost = { input: 10, output: 50, cacheRead: 0.25, cacheWrite: 12.5 }\n// Fast mode bills the same per-token rates as the Mythos-class tier: $10/M in,\n// $50/M out, cache read 1, cache write 12.5. Not an inference; this is the\n// exact table the CLI itself applies for `speed: \"fast\"` on Opus 4.8 / Opus 5\n// (`{inputTokens: 10, outputTokens: 50, promptCacheWriteTokens: 12.5,\n// promptCacheReadTokens: 1}`). Kept as its own binding rather than reusing\n// `fableCost` so a future divergence in either tier stays a one-line change.\n// Verified against Claude Code 2.1.245, 2026-08-30.\nconst opusFastCost = { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 }\n// Opus 5.5 is the first Opus priced below the $5/$25 line: $4/M in, $20/M\n// out. Cache writes keep the 1.25x input ratio ($5/M) but cache reads are\n// $0.20/M, 0.05x input rather than the usual 0.1x, so this cannot be derived\n// from `opusCost` by scaling. Fast mode is exactly double across the board\n// ($8/M in, $40/M out, cache read 0.4, cache write 10). Both tables are the\n// ones Claude Code bakes in for the model (`tier_4_20_cache_read_0_20` and\n// its dedicated fast-mode entry), matching the 2026-09-22 announcement.\n// Verified against Claude Code 2.1.280, 2026-09-22.\nconst opus55Cost = { input: 4, output: 20, cacheRead: 0.2, cacheWrite: 5 }\nconst opus55FastCost = { input: 8, output: 40, cacheRead: 0.4, cacheWrite: 10 }\n\n/**\n * Convert an OpenCodeModel to the flat config schema that OpenCode's\n * provider.ts config parser expects (model.temperature, model.reasoning,\n * model.cost.cache_read, model.modalities, etc.).\n */\nexport function toConfigModel(model: OpenCodeModel): Record<string, unknown> {\n const inputMods: string[] = []\n const outputMods: string[] = []\n for (const [k, v] of Object.entries(model.capabilities.input)) {\n if (v) inputMods.push(k)\n }\n for (const [k, v] of Object.entries(model.capabilities.output)) {\n if (v) outputMods.push(k)\n }\n\n return {\n id: model.api.id,\n name: model.name,\n status: model.status,\n family: model.family ?? \"\",\n release_date: model.release_date,\n\n temperature: model.capabilities.temperature,\n reasoning: model.capabilities.reasoning,\n attachment: model.capabilities.attachment,\n tool_call: model.capabilities.toolcall,\n modalities: { input: inputMods, output: outputMods },\n\n cost: {\n input: model.cost.input,\n output: model.cost.output,\n cache_read: model.cost.cache.read,\n cache_write: model.cost.cache.write,\n },\n\n limit: model.limit,\n options: model.options,\n headers: model.headers,\n variants: model.variants,\n }\n}\n\nexport const defaultModels: Record<string, OpenCodeModel> = {\n \"claude-haiku-4-5\": defineModel({\n id: \"claude-haiku-4-5\",\n name: \"Claude Haiku 4.5\",\n family: \"haiku\",\n reasoning: false,\n context: 200_000,\n output: 64_000,\n cost: haikuCost,\n multiplier: 1,\n releaseDate: \"2025-10-01\",\n }),\n \"claude-sonnet-4-5\": defineModel({\n id: \"claude-sonnet-4-5\",\n name: \"Claude Sonnet 4.5\",\n family: \"sonnet\",\n reasoning: true,\n context: 200_000,\n output: 64_000,\n cost: sonnetCost,\n multiplier: 3,\n releaseDate: \"2025-09-29\",\n }),\n \"claude-sonnet-4-6\": defineModel({\n id: \"claude-sonnet-4-6\",\n name: \"Claude Sonnet 4.6\",\n family: \"sonnet\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: sonnetCost,\n multiplier: 3,\n releaseDate: \"2025-06-19\",\n }),\n \"claude-sonnet-5\": defineModel({\n id: \"claude-sonnet-5\",\n name: \"Claude Sonnet 5\",\n family: \"sonnet\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: sonnetCost,\n multiplier: 3,\n releaseDate: \"2026-06-30\",\n }),\n \"claude-opus-4-5\": defineModel({\n id: \"claude-opus-4-5\",\n name: \"Claude Opus 4.5\",\n family: \"opus\",\n reasoning: true,\n context: 200_000,\n output: 64_000,\n cost: opusCost,\n multiplier: 5,\n releaseDate: \"2025-11-01\",\n }),\n \"claude-opus-4-6\": defineModel({\n id: \"claude-opus-4-6\",\n name: \"Claude Opus 4.6\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opusCost,\n multiplier: 5,\n releaseDate: \"2025-06-19\",\n }),\n \"claude-opus-4-7\": defineModel({\n id: \"claude-opus-4-7\",\n name: \"Claude Opus 4.7\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opusCost,\n multiplier: 5,\n releaseDate: \"2025-07-16\",\n }),\n \"claude-opus-4-8\": defineModel({\n id: \"claude-opus-4-8\",\n name: \"Claude Opus 4.8\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opusCost,\n multiplier: 5,\n releaseDate: \"2026-05-28\",\n }),\n // Fast mode. The `-fast` suffix is OUR marker, not a model name Anthropic\n // serves: `parseModelId` strips it before `--model` and turns it into\n // `--settings {\"fastMode\":true}` on the spawn. Retired `-fast` model strings\n // (`claude-opus-4-6-fast`) are a different thing and are not registered here.\n //\n // Only Opus 4.8, Opus 5 and Opus 5.5 qualify: the CLI gates fast mode on\n // the resolved model name containing `opus-4-8` or `opus-5` (which\n // `claude-opus-5-5` satisfies too, and 2.1.280's model catalog lists\n // `fast_mode` for it outright), so registering a fast entry for any other\n // model would produce a picker option that silently runs at standard speed\n // while displaying the fast price.\n \"claude-opus-4-8-fast\": defineModel({\n id: \"claude-opus-4-8-fast\",\n name: \"Claude Opus 4.8 Fast\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opusFastCost,\n multiplier: 10,\n releaseDate: \"2026-05-28\",\n }),\n \"claude-opus-5\": defineModel({\n id: \"claude-opus-5\",\n name: \"Claude Opus 5\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opusCost,\n multiplier: 5,\n releaseDate: \"2026-07-24\",\n }),\n \"claude-opus-5-fast\": defineModel({\n id: \"claude-opus-5-fast\",\n name: \"Claude Opus 5 Fast\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opusFastCost,\n multiplier: 10,\n releaseDate: \"2026-07-24\",\n }),\n // Opus 5.5 needs Claude Code 2.1.280+: the API rejects it from an older CLI\n // with a 400 naming that floor, which the plugin surfaces as a turn error.\n // Thinking cannot be switched off for it (the CLI's catalog marks it\n // `rejects_disabled_thinking`); the plugin never asks for that, so nothing\n // here has to special-case it.\n \"claude-opus-5-5\": defineModel({\n id: \"claude-opus-5-5\",\n name: \"Claude Opus 5.5\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opus55Cost,\n multiplier: 4,\n releaseDate: \"2026-09-22\",\n }),\n \"claude-opus-5-5-fast\": defineModel({\n id: \"claude-opus-5-5-fast\",\n name: \"Claude Opus 5.5 Fast\",\n family: \"opus\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: opus55FastCost,\n multiplier: 8,\n releaseDate: \"2026-09-22\",\n }),\n \"claude-fable-5\": defineModel({\n id: \"claude-fable-5\",\n name: \"Claude Fable 5\",\n family: \"fable\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: fableCost,\n multiplier: 10,\n releaseDate: \"2026-06-09\",\n }),\n \"claude-fable-5-1\": defineModel({\n id: \"claude-fable-5-1\",\n name: \"Claude Fable 5.1\",\n family: \"fable\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: fable51Cost,\n multiplier: 10,\n releaseDate: \"2026-09-01\",\n }),\n // Mythos 5 and 5.1 share the corresponding Fable models' capabilities and\n // pricing without the safety classifiers; limited availability via Project\n // Glasswing. `claude --model` simply errors for accounts without access, so\n // they are safe to register unconditionally.\n \"claude-mythos-5\": defineModel({\n id: \"claude-mythos-5\",\n name: \"Claude Mythos 5\",\n family: \"mythos\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: fableCost,\n multiplier: 10,\n releaseDate: \"2026-06-09\",\n }),\n \"claude-mythos-5-1\": defineModel({\n id: \"claude-mythos-5-1\",\n name: \"Claude Mythos 5.1\",\n family: \"mythos\",\n reasoning: true,\n context: 1_000_000,\n output: 128_000,\n cost: fable51Cost,\n multiplier: 10,\n releaseDate: \"2026-09-01\",\n }),\n}\n\n/** Marker this plugin appends to build a fast-mode model id. See below. */\nconst FAST_SUFFIX = \"-fast\"\n\n/**\n * Split an opencode model id into the name the Claude CLI actually accepts\n * and whether fast mode was requested.\n *\n * Two suffixes can ride on one id and they are NOT interchangeable:\n *\n * claude-opus-5-fast@work\n * \\_____________/\\___/\\__/\n * CLI model ours accounts.ts's\n *\n * `@work` must survive: the per-account wrapper script strips it at spawn\n * time to pick a CLAUDE_CONFIG_DIR. `-fast` must not: the CLI has no such\n * model (`claude-opus-4-6-fast` is retired and `claude-opus-4-7-fast` errors\n * outright), so it becomes `--settings {\"fastMode\":true}` instead.\n *\n * The `defaultModels` lookup is the guard against a false positive. Only ids\n * we registered are treated as fast markers, so a user-defined model that\n * happens to end in `-fast` is passed through untouched rather than being\n * silently rewritten into a model name that does not exist.\n */\nexport function parseModelId(modelId: string): { model: string; fast: boolean } {\n const at = modelId.indexOf(\"@\")\n const base = at === -1 ? modelId : modelId.slice(0, at)\n const account = at === -1 ? \"\" : modelId.slice(at)\n\n if (!base.endsWith(FAST_SUFFIX)) return { model: modelId, fast: false }\n if (!Object.hasOwn(defaultModels, base)) return { model: modelId, fast: false }\n\n return { model: base.slice(0, -FAST_SUFFIX.length) + account, fast: true }\n}\n","import * as crypto from \"node:crypto\"\nimport * as fs from \"node:fs\"\nimport * as os from \"node:os\"\nimport * as path from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { expandHome } from \"./accounts.js\"\nimport { detectCliSupportsFlag } from \"./cli-version.js\"\nimport { log } from \"./logger.js\"\nimport { pluginTmpDir } from \"./tmp.js\"\n\n/**\n * Bridge opencode skills into Claude Code's native Skill tool.\n *\n * Written by Joseph Roberts (@broskees) on his fork, commit 68ed142, and\n * absorbed here with light edits. Opt-in via `bridgeOpencodeSkills`; see\n * README for why it is off by default upstream.\n *\n * opencode and Claude Code use the same on-disk skill format, a\n * `<name>/SKILL.md` file whose YAML frontmatter carries `name` and\n * `description`, but they read from overlapping, not identical, roots. So an\n * opencode skill the CLI cannot see produces `Unknown skill`, and a skill in\n * a root they *share* reaches one session twice.\n *\n * Fix: assemble a throwaway Claude Code *plugin* directory whose `skills/`\n * folder links each discovered opencode skill, and hand it to the CLI with\n * `--plugin-dir`. Claude registers them natively as\n * `opencode-skills:<name>`, listed by the Skill tool, invocable, and\n * usable as `/opencode-skills:<name>`.\n *\n * `--plugin-dir` is documented as \"for this session only\", so this never\n * writes into the user's `~/.claude`. The staging dir lives under the\n * per-process tmp dir and is removed on exit with everything else.\n *\n * ## The two root sets, and where they overlap\n *\n * opencode's own loader (read out of the `opencode` binary: `var\n * bA=\".claude\", xA=\".agents\", GA=\"skills/**\\/SKILL.md\"`) reads project\n * `.opencode/`, `.claude/` and `.agents/` walking up from the session\n * directory, the opencode config dirs, and the global `~/.claude/skills` and\n * `~/.agents/skills` (the latter two behind `OPENCODE_DISABLE_EXTERNAL_SKILLS`\n * / `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS`). `skillRoots` mirrors that list, so\n * every skill opencode advertises is one the bridge can actually stage.\n *\n * Claude Code 2.1.x loads `<CLAUDE_CONFIG_DIR>/skills` (`~/.claude/skills` by\n * default), the project's own `.claude/skills`, and the `skills/` folder of\n * every installed plugin. Its managed-settings schema names the first two\n * verbatim: \"Blocked: ~/.claude/{surface}/, .claude/{surface}/ (project)\".\n *\n * `.claude/skills` is therefore in *both* sets, and a skill can also be\n * installed as a Claude plugin and symlinked into `~/.agents/skills`. Either\n * way the model sees the same name and description twice, on every turn.\n * `dropNativelyLoadedSkills` removes that overlap before anything is staged;\n * `bridgeSkipNativeSkills: false` turns it off.\n *\n * Known limitation: the plugin scan reads `installed_plugins.json` and does\n * not consult the CLI's own enable/blocklist state, so a skill belonging to a\n * *disabled* plugin can be treated as natively loaded. Every skip is logged\n * with both paths and its reason, which is the one line to grep when a skill\n * goes missing.\n */\n\n/** Plugin name, and therefore the `<plugin>:<skill>` prefix Claude assigns. */\nexport const SKILL_PLUGIN_NAME = \"opencode-skills\"\n\n/**\n * Skills shipped inside this package, at `<package>/skills/<name>/SKILL.md`.\n * Today that is `claude-code-plugin`, the skill that lets a model configure\n * this plugin from its own reference instead of the README. It reaches the\n * model two ways: `registerBundledSkillPath` adds the directory to opencode's\n * `skills.paths` so opencode lists it for every provider, and\n * `resolveSkillPluginDirs` always stages it as a `--plugin-dir` (the user's\n * own skills stay opt-in) because a Claude-routed turn cannot call opencode's\n * `skill` tool and only sees Claude's native Skill tool.\n *\n * Both `dist/index.js` (built) and `src/skill-bridge.ts` (tsx, tests) sit one\n * level below the package root, so the same relative walk finds it.\n */\nexport function bundledSkillsDir(): string | null {\n try {\n const here = fileURLToPath(import.meta.url)\n const dir = path.resolve(path.dirname(here), \"..\", \"skills\")\n return dirExists(dir) ? dir : null\n } catch {\n return null\n }\n}\n\nexport interface DiscoveredSkill {\n /**\n * The name the skill is known by: its SKILL.md frontmatter `name` when it\n * declares a usable one, and the directory basename otherwise. opencode\n * advertises the declared name, so staging under the basename is what made\n * `Skill(\"defuddle\")` fail for a directory called\n * `obsidian-skills--defuddle`.\n */\n name: string\n /** Absolute path to the skill directory containing SKILL.md. */\n dir: string\n /** `dir` with symlinks resolved: the identity two copies of one skill share. */\n realDir: string\n /** sha256 of SKILL.md, the only part of a skill that reaches the prompt. */\n contentHash: string\n}\n\n/** A skill the Claude Code session loads on its own, with no bridge involved. */\nexport interface NativeSkill extends DiscoveredSkill {\n /**\n * Where Claude reads it from. `user` and `project` skills are registered\n * under their bare name, so a bridged skill of the same name collides with\n * them; `plugin` skills are namespaced `<plugin>:<name>` and can only ever\n * duplicate *content*, never a name.\n */\n scope: \"user\" | \"project\" | \"plugin\"\n}\n\nfunction dirExists(p: string): boolean {\n try {\n return fs.statSync(p).isDirectory()\n } catch {\n return false\n }\n}\n\nfunction realPath(p: string): string {\n try {\n return fs.realpathSync(p)\n } catch {\n return path.resolve(p)\n }\n}\n\nfunction envEnabled(name: string): boolean {\n const value = process.env[name]\n if (value === undefined) return false\n const normalized = value.trim().toLowerCase()\n return normalized !== \"\" && normalized !== \"0\" && normalized !== \"false\"\n}\n\n/**\n * A staged skill becomes a directory name under `skills/`, so a frontmatter\n * `name` has to be a single safe path segment before it can be trusted with\n * one. Anything else falls back to the directory basename, which readdir\n * guarantees is a plain segment.\n */\nconst SAFE_SKILL_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/\n\nconst FRONTMATTER_BLOCK = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/\nconst FRONTMATTER_NAME = /^name:[ \\t]*(.+?)[ \\t]*$/m\n\n/** The `name:` a SKILL.md declares, or null when it declares none we can use. */\nexport function declaredSkillName(source: string): string | null {\n const block = FRONTMATTER_BLOCK.exec(source)\n if (!block) return null\n const line = FRONTMATTER_NAME.exec(block[1]!)\n if (!line) return null\n const name = line[1]!.trim().replace(/^[\"']|[\"']$/g, \"\").trim()\n return SAFE_SKILL_NAME.test(name) ? name : null\n}\n\n/** Read one `<root>/<entry>/SKILL.md`, or null when there is nothing to read. */\nfunction readSkill(root: string, entry: string): DiscoveredSkill | null {\n const dir = path.join(root, entry)\n let source: string\n try {\n source = fs.readFileSync(path.join(dir, \"SKILL.md\"), \"utf8\")\n } catch {\n return null\n }\n return {\n name: declaredSkillName(source) ?? entry,\n dir,\n realDir: realPath(dir),\n contentHash: crypto.createHash(\"sha256\").update(source).digest(\"hex\"),\n }\n}\n\n/**\n * Whether two skills are the same skill: the same directory once symlinks are\n * resolved, or byte-identical SKILL.md content. Either one means bridging both\n * would put the same text in the prompt twice with nothing to choose between.\n */\nexport function sameSkill(a: DiscoveredSkill, b: DiscoveredSkill): boolean {\n return a.realDir === b.realDir || a.contentHash === b.contentHash\n}\n\n/**\n * Skill roots in opencode's own precedence order: project scope nearest-first\n * (`.opencode`, `.claude`, `.agents`), then the home-dir `.opencode`, then\n * `OPENCODE_CONFIG_DIR`, then the global `~/.config/opencode`, and finally the\n * external `~/.claude/skills` and `~/.agents/skills` scans. First occurrence of\n * a given skill name wins, so a project can shadow a global skill and an\n * opencode-managed copy shadows an external one.\n */\nexport function skillRoots(cwd: string): string[] {\n const roots: string[] = []\n const seen = new Set<string>()\n const push = (p: string) => {\n const abs = path.resolve(p)\n if (seen.has(abs)) return\n seen.add(abs)\n if (dirExists(abs)) roots.push(abs)\n }\n\n const home = os.homedir()\n const homeDir = home ? path.resolve(home) : null\n\n let current = path.resolve(cwd)\n while (true) {\n push(path.join(current, \".opencode\", \"skills\"))\n // `~/.claude` and `~/.agents` are the *global* external roots and are\n // pushed below under the env switches opencode honours. A workspace that\n // happens to be the home directory must not smuggle them in ahead of that.\n if (current !== homeDir) {\n push(path.join(current, \".claude\", \"skills\"))\n push(path.join(current, \".agents\", \"skills\"))\n }\n const parent = path.dirname(current)\n if (parent === current) break\n current = parent\n }\n\n if (homeDir) push(path.join(homeDir, \".opencode\", \"skills\"))\n\n // opencode accepts both spellings in its own config dirs (`{skill,skills}`).\n const envDir = process.env.OPENCODE_CONFIG_DIR\n if (envDir) {\n push(path.join(envDir, \"skills\"))\n push(path.join(envDir, \"skill\"))\n }\n\n const xdg = process.env.XDG_CONFIG_HOME ?? (homeDir ? path.join(homeDir, \".config\") : null)\n if (xdg) {\n push(path.join(xdg, \"opencode\", \"skills\"))\n push(path.join(xdg, \"opencode\", \"skill\"))\n }\n\n if (homeDir && !envEnabled(\"OPENCODE_DISABLE_EXTERNAL_SKILLS\")) {\n if (!envEnabled(\"OPENCODE_DISABLE_CLAUDE_CODE_SKILLS\")) {\n push(path.join(homeDir, \".claude\", \"skills\"))\n }\n push(path.join(homeDir, \".agents\", \"skills\"))\n }\n\n return roots\n}\n\n/**\n * Walk one skill root and collect every `<name>/SKILL.md`. Directories\n * without a SKILL.md are skipped silently, opencode ignores them too.\n *\n * A name already claimed by an earlier root keeps its winner, because root\n * order *is* the precedence. When the shadowed copy is a different skill\n * rather than another link to the same one, that is a genuine ambiguity and\n * gets a warning naming both paths instead of vanishing.\n */\nfunction collectSkills(\n root: string,\n claimed: Map<string, DiscoveredSkill>,\n found: DiscoveredSkill[],\n): void {\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(root, { withFileTypes: true })\n } catch {\n return\n }\n for (const entry of entries) {\n // `withFileTypes` reports a symlinked dir as a link, not a dir.\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue\n if (entry.name.startsWith(\".\")) continue\n const skill = readSkill(root, entry.name)\n if (!skill) continue\n const winner = claimed.get(skill.name)\n if (winner) {\n if (!sameSkill(winner, skill)) {\n log.warn(\"two different skills share a name; bridging the one the nearest root claimed\", {\n name: skill.name,\n bridged: winner.dir,\n ignored: skill.dir,\n })\n }\n continue\n }\n claimed.set(skill.name, skill)\n found.push(skill)\n }\n}\n\nconst byName = (a: DiscoveredSkill, b: DiscoveredSkill) => a.name.localeCompare(b.name)\n\nexport function discoverOpencodeSkills(cwd: string): DiscoveredSkill[] {\n const found: DiscoveredSkill[] = []\n const claimed = new Map<string, DiscoveredSkill>()\n for (const root of skillRoots(cwd)) collectSkills(root, claimed, found)\n return found.sort(byName)\n}\n\n/** The skills this package ships (see `bundledSkillsDir`). */\nexport function discoverBundledSkills(): DiscoveredSkill[] {\n const root = bundledSkillsDir()\n if (!root) return []\n const found: DiscoveredSkill[] = []\n collectSkills(root, new Map(), found)\n return found.sort(byName)\n}\n\n// ---------------------------------------------------------------------------\n// What Claude already loads\n// ---------------------------------------------------------------------------\n\n/**\n * The `CLAUDE_CONFIG_DIR` this spawn will run under: the account's own config\n * dir when the provider selected one, else the environment's, else `~/.claude`.\n * Accounts symlink `skills` back to `~/.claude/skills`, so in practice every\n * account resolves to the same directory, but the spawn's own value is the\n * only one guaranteed to.\n */\nexport function claudeConfigDir(configDir?: string): string | null {\n const explicit = configDir ?? process.env.CLAUDE_CONFIG_DIR\n if (explicit) return path.resolve(expandHome(explicit))\n const home = os.homedir()\n return home ? path.resolve(home, \".claude\") : null\n}\n\n/**\n * `skills/` of every plugin `installed_plugins.json` says applies here. A\n * `user`-scope install always does; a `project` / `local` one only when its\n * `projectPath` is the workspace or an ancestor of it.\n */\nfunction installedPluginSkillRoots(configDir: string | null, cwd: string): string[] {\n if (!configDir) return []\n let parsed: unknown\n try {\n parsed = JSON.parse(\n fs.readFileSync(path.join(configDir, \"plugins\", \"installed_plugins.json\"), \"utf8\"),\n )\n } catch {\n return []\n }\n const plugins = (parsed as { plugins?: unknown } | null)?.plugins\n if (!plugins || typeof plugins !== \"object\") return []\n\n const workspace = path.resolve(cwd)\n const roots: string[] = []\n for (const installs of Object.values(plugins as Record<string, unknown>)) {\n if (!Array.isArray(installs)) continue\n for (const install of installs) {\n const entry = install as { installPath?: unknown; scope?: unknown; projectPath?: unknown }\n if (typeof entry?.installPath !== \"string\" || !entry.installPath) continue\n if (entry.scope !== \"user\" && !coversWorkspace(entry.projectPath, workspace)) continue\n roots.push(path.join(entry.installPath, \"skills\"))\n }\n }\n return roots\n}\n\nfunction coversWorkspace(projectPath: unknown, workspace: string): boolean {\n if (typeof projectPath !== \"string\" || !projectPath) return false\n const root = path.resolve(projectPath)\n if (root === workspace) return true\n const rel = path.relative(root, workspace)\n return rel !== \"\" && !rel.startsWith(\"..\") && !path.isAbsolute(rel)\n}\n\n/**\n * Every skill the Claude Code session will load without our help: its user\n * config dir, the project `.claude/skills` directories above the workspace,\n * and each installed plugin's own `skills/`.\n */\nexport function discoverNativeClaudeSkills(opts: {\n cwd: string\n configDir?: string\n}): NativeSkill[] {\n const found: NativeSkill[] = []\n const scanned = new Set<string>()\n const scan = (root: string, scope: NativeSkill[\"scope\"]) => {\n const abs = path.resolve(root)\n if (scanned.has(abs)) return\n scanned.add(abs)\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(abs, { withFileTypes: true })\n } catch {\n return\n }\n for (const entry of entries) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue\n if (entry.name.startsWith(\".\")) continue\n const skill = readSkill(abs, entry.name)\n if (skill) found.push({ ...skill, scope })\n }\n }\n\n const configDir = claudeConfigDir(opts.configDir)\n if (configDir) scan(path.join(configDir, \"skills\"), \"user\")\n\n const home = os.homedir()\n const homeDir = home ? path.resolve(home) : null\n let current = path.resolve(opts.cwd)\n while (true) {\n // The home dir's `.claude/skills` is the user scope, already scanned above\n // under whatever config dir this spawn actually uses.\n if (current !== homeDir) scan(path.join(current, \".claude\", \"skills\"), \"project\")\n const parent = path.dirname(current)\n if (parent === current) break\n current = parent\n }\n\n for (const root of installedPluginSkillRoots(configDir, opts.cwd)) scan(root, \"plugin\")\n\n return found\n}\n\nexport interface SkippedSkill {\n skill: DiscoveredSkill\n native: NativeSkill\n /**\n * `same-copy`: literally the same directory. `same-content`: a byte-identical\n * SKILL.md somewhere else. `name-taken`: a *different* skill Claude registers\n * under this name, so bridging ours would make the name ambiguous.\n */\n reason: \"same-copy\" | \"same-content\" | \"name-taken\"\n}\n\n/**\n * Drop the skills the Claude session already sees. Identity wins over name:\n * an identical copy is dropped whatever Claude loads it from, while a name\n * clash only counts against `user` / `project` skills, because a plugin skill\n * answers to `<plugin>:<name>` and cannot shadow a bridged one.\n *\n * A `name-taken` skip changes which copy answers `Skill(\"<name>\")`, so it is\n * the one case that warns rather than just logging.\n */\nexport function dropNativelyLoadedSkills(\n skills: DiscoveredSkill[],\n native: NativeSkill[],\n): { bridged: DiscoveredSkill[]; skipped: SkippedSkill[] } {\n if (native.length === 0) return { bridged: skills, skipped: [] }\n\n const bridged: DiscoveredSkill[] = []\n const skipped: SkippedSkill[] = []\n for (const skill of skills) {\n const sameCopy = native.find((n) => n.realDir === skill.realDir)\n const sameContent = sameCopy ?? native.find((n) => n.contentHash === skill.contentHash)\n const nameTaken =\n sameContent ?? native.find((n) => n.scope !== \"plugin\" && n.name === skill.name)\n if (!nameTaken) {\n bridged.push(skill)\n continue\n }\n const reason = sameCopy ? \"same-copy\" : sameContent ? \"same-content\" : \"name-taken\"\n skipped.push({ skill, native: nameTaken, reason })\n if (reason === \"name-taken\") {\n log.warn(\"claude already registers a different skill under this name; not bridging ours\", {\n name: skill.name,\n claudeLoads: nameTaken.dir,\n notBridged: skill.dir,\n scope: nameTaken.scope,\n })\n }\n }\n return { bridged, skipped }\n}\n\n// ---------------------------------------------------------------------------\n// Staging\n// ---------------------------------------------------------------------------\n\n/** opencode 2's `Skill.Info`, the fields it requires. */\nexport interface BundledSkillInfo {\n id: string\n name: string\n description?: string\n path: string\n content: string\n}\n\n/**\n * The bundled skills in opencode 2's `Skill.Info` shape. V2 has no config\n * hook to add a `skills.paths` entry to, so the V2 entrypoint registers them\n * through `skill.transform` instead. Parsed the way V2's own skill-file loader\n * does (`@opencode/core@2.0.11`): `name` and `description` from the\n * frontmatter, `content` is the markdown body after it, `path` is the file.\n */\nexport function readBundledSkillInfos(): BundledSkillInfo[] {\n return discoverBundledSkills().flatMap((skill) => {\n const file = path.join(skill.dir, \"SKILL.md\")\n let text: string\n try {\n text = fs.readFileSync(file, \"utf8\")\n } catch {\n return []\n }\n const match = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/.exec(text)\n const frontmatter = match?.[1] ?? \"\"\n const field = (key: string): string | undefined => {\n const line = new RegExp(`^${key}:\\\\s*(.+)$`, \"m\").exec(frontmatter)?.[1]?.trim()\n return line ? line.replace(/^([\"'])(.*)\\1$/, \"$2\") : undefined\n }\n const description = field(\"description\")\n return [\n {\n id: skill.name,\n name: field(\"name\") ?? skill.name,\n ...(description ? { description } : {}),\n path: file,\n content: match ? text.slice(match[0].length) : text,\n },\n ]\n })\n}\n\n/** Link a skill dir into the staging tree, falling back to a copy. */\nfunction linkSkill(source: string, target: string): void {\n try {\n // Windows needs an explicit junction for directory links, and even then\n // only with the right privileges, hence the copy fallback below.\n fs.symlinkSync(source, target, process.platform === \"win32\" ? \"junction\" : \"dir\")\n return\n } catch {\n fs.cpSync(source, target, { recursive: true, dereference: true })\n }\n}\n\nfunction fileExists(p: string): boolean {\n try {\n return fs.statSync(p).isFile()\n } catch {\n return false\n }\n}\n\n/**\n * Materialise the synthetic plugin directory. Returns its path, or null if\n * there are no skills to bridge. The path is keyed by a hash of the\n * resolved skill set, so an unchanged set reuses the existing tree instead\n * of rebuilding it on every spawn.\n */\nexport function buildSkillPluginDir(skills: DiscoveredSkill[]): string | null {\n if (skills.length === 0) return null\n\n const fingerprint = skills.map((s) => `${s.name}\\0${s.dir}`).join(\"\\n\")\n const hash = crypto.createHash(\"sha256\").update(fingerprint).digest(\"hex\").slice(0, 12)\n const root = path.join(pluginTmpDir(), `skills-${hash}`)\n const manifest = path.join(root, \".claude-plugin\", \"plugin.json\")\n\n // Same skill set as a previous spawn in this process, reuse the tree.\n if (fileExists(manifest)) return root\n\n try {\n fs.rmSync(root, { recursive: true, force: true })\n fs.mkdirSync(path.join(root, \".claude-plugin\"), { recursive: true })\n fs.mkdirSync(path.join(root, \"skills\"), { recursive: true })\n fs.writeFileSync(\n manifest,\n JSON.stringify(\n {\n name: SKILL_PLUGIN_NAME,\n description:\n \"Skills discovered from this opencode installation, bridged into Claude Code.\",\n },\n null,\n 2,\n ),\n { encoding: \"utf8\", mode: 0o600 },\n )\n for (const skill of skills) {\n linkSkill(skill.dir, path.join(root, \"skills\", skill.name))\n }\n } catch (err) {\n log.warn(\"failed to stage opencode skill plugin dir\", {\n root,\n error: err instanceof Error ? err.message : String(err),\n })\n return null\n }\n\n return root\n}\n\n/**\n * Add the bundled skills directory to opencode's `skills.paths` (scanned for\n * nested SKILL.md files) so opencode itself lists the\n * skill for every provider and its own `skill` tool can load it. Idempotent;\n * returns whether anything was added.\n */\nexport function registerBundledSkillPath(config: {\n skills?: { paths?: string[]; urls?: string[] }\n}): boolean {\n const dir = bundledSkillsDir()\n if (!dir) return false\n config.skills ??= {}\n const paths = (config.skills.paths ??= [])\n if (paths.some((p) => path.resolve(p) === dir)) return false\n paths.push(dir)\n return true\n}\n\n/**\n * One-call entry point for the spawn sites: discover, drop what Claude already\n * loads, stage, and return the `--plugin-dir` values. The package's own skills\n * are always staged; the user's opencode skills only when `enabled`\n * (`bridgeOpencodeSkills`). A user skill with the same name as a bundled one\n * wins, so it can be overridden. Returns an empty array when the CLI is too old\n * to accept the flag or there is nothing left to stage, so callers can spread\n * the result unconditionally.\n */\nexport async function resolveSkillPluginDirs(opts: {\n cwd: string\n cliPath: string\n enabled: boolean\n /** The spawn's `CLAUDE_CONFIG_DIR`, which decides what Claude loads natively. */\n configDir?: string\n /** `bridgeSkipNativeSkills`; only `false` keeps the duplicates. */\n skipNative?: boolean\n}): Promise<string[]> {\n const user = opts.enabled ? discoverOpencodeSkills(opts.cwd) : []\n const claimed = new Set(user.map((s) => s.name))\n const bundled = discoverBundledSkills().filter((s) => !claimed.has(s.name))\n let skills = [...user, ...bundled].sort(byName)\n\n if (opts.skipNative !== false && skills.length > 0) {\n const native = discoverNativeClaudeSkills({ cwd: opts.cwd, configDir: opts.configDir })\n const { bridged, skipped } = dropNativelyLoadedSkills(skills, native)\n if (skipped.length > 0) {\n log.info(\"skills claude code already loads; left unbridged\", {\n count: skipped.length,\n skipped: skipped.map((s) => ({\n name: s.skill.name,\n reason: s.reason,\n opencode: s.skill.dir,\n claude: s.native.dir,\n })),\n })\n }\n skills = bridged\n }\n if (skills.length === 0) return []\n\n // No published version marks `--plugin-dir`'s arrival, so probe the\n // binary's own help text rather than inventing a semver threshold.\n const supported = await detectCliSupportsFlag(opts.cliPath, \"--plugin-dir\")\n if (!supported) {\n log.notice(\n \"claude cli does not support --plugin-dir; opencode skills will not be bridged. Run `npm i -g @anthropic-ai/claude-code` to upgrade.\",\n { skills: skills.length },\n )\n return []\n }\n\n const dir = buildSkillPluginDir(skills)\n if (!dir) return []\n\n log.info(\"bridged opencode skills into claude\", {\n count: skills.length,\n names: skills.map((s) => s.name),\n bundled: bundled.map((s) => s.name),\n pluginDir: dir,\n })\n return [dir]\n}\n","import { EventEmitter } from \"node:events\"\nimport { unlink } from \"node:fs/promises\"\nimport { ClaudeSession } from \"./claude-session-bun.js\"\nimport { cliEffortLevel, type ActiveProcess } from \"./session-manager.js\"\nimport type { ReasoningEffort } from \"./types.js\"\nimport { log } from \"./logger.js\"\n\nexport interface InteractiveSpawnOptions {\n cwd: string\n /** Claude CLI executable or account wrapper path. */\n cliPath?: string\n /** Claude config root used for JSONL transcripts. */\n configDir?: string\n model?: string\n /** Request Claude Code's fast mode (Opus 4.8 / 5 / 5.5 only). Folded into\n * the single `--settings` payload alongside `permissions`. */\n fastMode?: boolean\n /** Bridged Claude `--mcp-config` file paths (from effectiveMcpConfig). */\n mcpConfigPaths?: string[]\n /** Session-scoped `--plugin-dir` paths (from `resolveSkillPluginDirs`),\n * which expose opencode skills to the TUI's native Skill tool. Already\n * filtered for CLI support, and empty when there is nothing to bridge. */\n pluginDirs?: string[]\n /** permissions.allow rules (e.g. mcp__server__*, Bash, Edit). */\n permissionsAllow?: string[]\n /** Optional permission mode. `bypassPermissions` is ignored for interactive\n * sessions because Claude Code shows a safety confirmation screen first. */\n permissionMode?: string\n /** Temp file for --append-system-prompt-file (parity with the headless\n * spawn; unlinked when the session is killed). */\n systemPromptFile?: string\n /** \"\" = skip CLAUDE.md + ambient settings (fast e2e); null/undefined =\n * normal settings (default — parity with the headless transport). */\n settingSources?: string | null\n /** Strip ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN from the spawn env so the\n * CLI uses subscription auth instead of pay-as-you-go API billing. */\n ignoreAnthropicApiKey?: boolean\n /** Reasoning effort, exported as CLAUDE_CODE_EFFORT_LEVEL for the session. */\n effort?: ReasoningEffort\n}\n\n/**\n * doStream writes stream-json user envelopes to stdin\n * (`{\"type\":\"user\",\"message\":{content:[...]}}`). The interactive TUI expects\n * plain typed text, so decode the envelope: extract the text blocks and drop\n * anything that can't be typed into a terminal (an image block would paste\n * megabytes of base64 into the chat). Tool results are rendered as labeled\n * text so the model still sees the outcome. Non-envelope input (already plain\n * text) passes through verbatim.\n */\nexport function decodeUserEnvelope(chunk: string): string {\n let parsed: any\n try {\n parsed = JSON.parse(chunk)\n } catch {\n return chunk\n }\n if (!parsed || parsed.type !== \"user\" || !parsed.message) return chunk\n const content = parsed.message.content\n if (typeof content === \"string\") return content\n if (!Array.isArray(content)) return chunk\n\n const parts: string[] = []\n let dropped = 0\n for (const block of content) {\n if (block?.type === \"text\" && typeof block.text === \"string\") {\n parts.push(block.text)\n } else if (block?.type === \"tool_result\") {\n const v = block.content\n const text =\n typeof v === \"string\"\n ? v\n : Array.isArray(v)\n ? v\n .map((i: any) => (i?.type === \"text\" ? i.text : \"\"))\n .filter(Boolean)\n .join(\"\\n\")\n : \"\"\n parts.push(\n `[Tool result${block.tool_use_id ? ` ${block.tool_use_id}` : \"\"}]\\n${text}`,\n )\n } else {\n dropped++\n }\n }\n if (dropped > 0) {\n log.warn(\"interactive transport dropped non-text content blocks\", {\n dropped,\n })\n }\n return parts.join(\"\\n\\n\")\n}\n\n/**\n * Adapt a ClaudeSession (interactive Bun ConPTY transport) to the ActiveProcess\n * contract the doStream line handler depends on. The shim's `proc.stdin.write`\n * injects a turn into the live interactive `claude` and re-emits each new JSONL\n * transcript record on `lineEmitter` as a 'line' event, plus a synthetic\n * `{type:'result'}` line on a terminal stop_reason so the existing finish branch\n * (usage + providerMetadata + controller.close) fires unchanged.\n *\n * No node-pty, no node sidecar: runs in-process under opencode's Bun (which\n * bundles a Bun version with native ConPTY). Interactive = subscription billing.\n */\n/**\n * The CLI flags an interactive spawn adds after `ClaudeSession`'s own\n * `--session-id` / `--model` / `--setting-sources`. Exported so the spawn\n * arguments can be checked without a PTY.\n */\nexport function interactiveExtraArgs(opts: InteractiveSpawnOptions): string[] {\n const extraArgs: string[] = []\n if (opts.mcpConfigPaths && opts.mcpConfigPaths.length > 0) {\n extraArgs.push(\n \"--mcp-config\",\n ...opts.mcpConfigPaths,\n \"--strict-mcp-config\",\n )\n }\n // `--plugin-dir` is repeatable and scoped to this session only.\n for (const dir of opts.pluginDirs ?? []) {\n extraArgs.push(\"--plugin-dir\", dir)\n }\n // One `--settings` for the whole flag-settings layer. The CLI accepts the\n // flag once, so pushing a second occurrence would silently drop the first\n // rather than merge it.\n const flagSettings: Record<string, unknown> = {}\n if (opts.permissionsAllow && opts.permissionsAllow.length > 0) {\n flagSettings.permissions = { allow: opts.permissionsAllow }\n }\n if (opts.fastMode) {\n flagSettings.fastMode = true\n }\n if (Object.keys(flagSettings).length > 0) {\n extraArgs.push(\"--settings\", JSON.stringify(flagSettings))\n }\n if (opts.permissionMode === \"bypassPermissions\") {\n log.warn(\n \"interactive permissionMode bypassPermissions ignored: Claude Code prompts for confirmation in the TUI\",\n )\n } else if (opts.permissionMode) {\n extraArgs.push(\"--permission-mode\", opts.permissionMode)\n }\n if (opts.systemPromptFile) {\n extraArgs.push(\"--append-system-prompt-file\", opts.systemPromptFile)\n }\n return extraArgs\n}\n\nexport function spawnInteractiveProcess(\n opts: InteractiveSpawnOptions,\n): ActiveProcess {\n const extraArgs = interactiveExtraArgs(opts)\n\n const session = new ClaudeSession({\n cwd: opts.cwd,\n cliPath: opts.cliPath,\n configDir: opts.configDir,\n model: opts.model,\n // Default null = normal CLAUDE.md + settings load, matching what the\n // headless spawn does. \"\" (skip everything) is for fast e2e runs only.\n settingSources:\n opts.settingSources === undefined ? null : opts.settingSources,\n extraArgs,\n ignoreAnthropicApiKey: opts.ignoreAnthropicApiKey,\n effort: opts.effort ? cliEffortLevel(opts.effort) : undefined,\n })\n log.info(\"prepared interactive claude session\", {\n cwd: opts.cwd,\n cliPath: opts.cliPath ?? \"claude\",\n configDir: session.configDir,\n model: opts.model,\n effort: opts.effort,\n sessionId: session.sessionId,\n jsonlPath: session.jsonlPath,\n })\n\n const lineEmitter = new EventEmitter()\n const errorHandlers = new Set<(err: Error) => void>()\n let startPromise: Promise<void> | null = null\n\n const ensureStarted = (): Promise<void> => {\n if (!startPromise) startPromise = session.start()\n return startPromise\n }\n\n const emitResult = (\n subtype: string,\n isError: boolean,\n result?: string,\n usage?: unknown,\n ): void => {\n lineEmitter.emit(\n \"line\",\n JSON.stringify({\n type: \"result\",\n subtype,\n is_error: isError,\n result,\n session_id: session.sessionId,\n usage: usage ?? {},\n total_cost_usd: null,\n duration_ms: 0,\n }),\n )\n }\n\n const runTurn = (userMsg: string): void => {\n void (async () => {\n try {\n await ensureStarted()\n const { stopReason, usage } = await session.tailTurn(userMsg, (raw) => {\n lineEmitter.emit(\"line\", raw)\n })\n // Synthesize the `result` line the headless transport would have\n // emitted, so doStream's existing finish branch runs verbatim. A turn\n // with no terminal stop_reason (timeout / session exit mid-turn) is\n // reported HONESTLY as an error result — not a clean end_turn — so\n // truncation is visible to the user and to auto-continue.\n const timedOut = !stopReason\n emitResult(\n timedOut ? \"error_during_execution\" : stopReason,\n timedOut,\n timedOut\n ? \"Interactive transport: the turn ended without a terminal stop_reason (turn timeout or claude exit). Output above may be incomplete.\"\n : undefined,\n usage,\n )\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err))\n log.error(\"interactive turn failed\", { error: e.message })\n emitResult(\n \"error_during_execution\",\n true,\n `Interactive transport failed: ${e.message}`,\n )\n if (errorHandlers.size > 0) {\n for (const h of errorHandlers) h(e)\n } else {\n lineEmitter.emit(\"close\")\n }\n }\n })()\n }\n\n // Minimal ChildProcess-shaped shim: only the members doStream/session-manager\n // actually touch (stdin.write, on/off 'error', kill).\n const proc: any = {\n stdin: {\n write(chunk: string): boolean {\n const raw =\n typeof chunk === \"string\" && chunk.endsWith(\"\\n\")\n ? chunk.slice(0, -1)\n : chunk\n // doStream writes stream-json envelopes; the TUI needs plain text.\n runTurn(decodeUserEnvelope(raw))\n return true\n },\n end(): void {},\n },\n stdout: null,\n stderr: null,\n pid: -1,\n killed: false,\n on(event: string, fn: (err: Error) => void): unknown {\n if (event === \"error\") errorHandlers.add(fn)\n return proc\n },\n once(): unknown {\n return proc\n },\n off(event: string, fn: (err: Error) => void): unknown {\n if (event === \"error\") errorHandlers.delete(fn)\n return proc\n },\n kill(): boolean {\n try {\n session.dispose()\n } catch {}\n if (opts.systemPromptFile) {\n void unlink(opts.systemPromptFile).catch(() => {})\n }\n proc.killed = true\n return true\n },\n }\n\n return {\n proc: proc as unknown as ActiveProcess[\"proc\"],\n lineEmitter,\n proxyServer: null,\n mcpHash: undefined,\n systemPromptFile: opts.systemPromptFile,\n }\n}\n","import * as os from \"node:os\"\nimport * as fs from \"node:fs\"\nimport * as path from \"node:path\"\nimport { execFileSync } from \"node:child_process\"\nimport { randomUUID } from \"node:crypto\"\nimport { cliHygieneEnv } from \"./cli-version.js\"\n\n/**\n * Persistent interactive Claude Code session driven over Bun's NATIVE PTY\n * (Bun.spawn `terminal` option = openpty on POSIX, ConPTY on Windows). This is\n * the in-process Bun port of claude-tui-bridge/src/claudeSession.ts: same\n * design, node-pty swapped for Bun's own ConPTY so it runs inside opencode's\n * Bun runtime with NO node sidecar and NO node-pty dependency.\n *\n * - ONE long-lived interactive `claude` process per session (multi-turn),\n * - turns injected by writing into the terminal (bracketed paste + Enter),\n * - replies captured by tailing the session JSONL transcript\n * (<CLAUDE_CONFIG_DIR>/projects/<encoded-cwd>/<session-id>.jsonl) and\n * parsing the assistant records; completion detected by a terminal\n * `stop_reason`.\n *\n * Driving the INTERACTIVE TUI (real TTY) keeps model calls on the subscription\n * billing path (not `claude -p` / Agent SDK, which meter after 2026-06-15).\n */\n\nfunction resolveClaude(cmd = \"claude\"): string {\n if (path.isAbsolute(cmd) && fs.existsSync(cmd)) return cmd\n const viaBun = Bun.which(cmd)\n if (viaBun) return viaBun\n const isWin = os.platform() === \"win32\"\n try {\n const out = execFileSync(isWin ? \"where\" : \"which\", [cmd], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n })\n const first = out\n .split(/\\r?\\n/)\n .map((l) => l.trim())\n .filter(Boolean)\n .find((p) => fs.existsSync(p))\n if (first) return first\n } catch {}\n throw new Error(`Could not resolve command on PATH: ${cmd}`)\n}\n\n/** Claude encodes the absolute cwd into the transcript dir name by replacing\n * EVERY non-alphanumeric char with `-` (no collapsing of runs). Verified on\n * Windows against ~/.claude/projects, e.g.:\n * C:\\code\\my-app -> C--code-my-app\n * C:\\dev\\My Project -> C--dev-My-Project (the space also becomes `-`). */\nexport function encodeCwd(cwd: string): string {\n return path.resolve(cwd).replace(/[^a-zA-Z0-9]/g, \"-\")\n}\n\nexport interface TurnResult {\n text: string\n stopReason: string | null\n usage: any | null\n cacheReadTokens: number\n cacheCreationTokens: number\n ephemeral1hTokens: number\n ephemeral5mTokens: number\n inputTokens: number\n outputTokens: number\n elapsedMs: number\n}\n\nexport interface ClaudeSessionOptions {\n cwd?: string\n /** Claude CLI executable or account wrapper path. */\n cliPath?: string\n /** Claude config root used for JSONL transcripts (defaults to ~/.claude). */\n configDir?: string\n model?: string\n /** '' bypasses CLAUDE.md + user/project/local settings load (fast tests).\n * null/undefined omits the flag entirely (normal settings). */\n settingSources?: string | null\n extraArgs?: string[]\n /** Strip ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN from the spawn env so the\n * CLI uses subscription auth instead of pay-as-you-go API billing. */\n ignoreAnthropicApiKey?: boolean\n /** CLI effort level (low | medium | high | xhigh | max), exported as\n * CLAUDE_CODE_EFFORT_LEVEL so it overrides the account's settings.json. */\n effort?: string\n cols?: number\n rows?: number\n bootMinMs?: number\n bootQuietMs?: number\n bootMaxMs?: number\n pollMs?: number\n turnTimeoutMs?: number\n /** false = plain write(prompt)+Enter; true = wrap in bracketed-paste so\n * multi-line prompts don't submit early. Default true. */\n bracketedPaste?: boolean\n /** Submitting a turn: a large/multi-line bracketed paste collapses into a\n * \"[Pasted text]\" placeholder, and an Enter sent while claude is still\n * ingesting the paste is silently DROPPED — so a single fixed-delay Enter is\n * unreliable and the turn can hang until turnTimeoutMs. Instead: wait\n * submitMinMs, send Enter, then confirm the turn was accepted (a new\n * transcript record appears) within submitConfirmMs; if not, resend Enter,\n * up to submitMaxRetries times. */\n submitMinMs?: number\n submitConfirmMs?: number\n submitMaxRetries?: number\n /** Abort the call (during boot or an in-flight turn): kills the process and\n * rejects with an \"aborted\" error. */\n signal?: AbortSignal\n debug?: boolean\n}\n\n/**\n * Env for the interactive (TUI) child. The headless counterpart is\n * `claudeSpawnEnv` in session-manager.ts; both must apply `cliHygieneEnv`, so\n * this is a named function rather than an object literal inside `Bun.spawn`,\n * which no test can reach without a real PTY.\n */\nexport function interactiveSpawnEnv(opts: {\n configDir: string\n ignoreAnthropicApiKey?: boolean\n effort?: string\n}): Record<string, string | undefined> {\n return {\n ...process.env,\n CLAUDE_CONFIG_DIR: opts.configDir,\n TERM: \"xterm-256color\",\n // Pin the binary so a mid-session autoupdate cannot invalidate the\n // detected version the flag gates read, and skip non-essential traffic.\n // Fills gaps only, so a var the user exported survives untouched.\n ...cliHygieneEnv(),\n ...(opts.ignoreAnthropicApiKey\n ? { ANTHROPIC_API_KEY: undefined, ANTHROPIC_AUTH_TOKEN: undefined }\n : {}),\n ...(opts.effort ? { CLAUDE_CODE_EFFORT_LEVEL: opts.effort } : {}),\n }\n}\n\nconst TERMINAL_STOP = new Set([\"end_turn\", \"stop_sequence\", \"max_tokens\"])\nconst delay = (ms: number) => new Promise<void>((r) => setTimeout(r, ms))\n\nfunction resolveConfigDir(configDir: string | undefined): string {\n const value = configDir ?? process.env.CLAUDE_CONFIG_DIR\n if (!value) return path.join(os.homedir(), \".claude\")\n if (value === \"~\") return os.homedir()\n if (value.startsWith(\"~/\") || value.startsWith(\"~\\\\\")) {\n return path.join(os.homedir(), value.slice(2))\n }\n return path.resolve(value)\n}\n\nexport class ClaudeSession {\n readonly sessionId: string\n readonly cwd: string\n readonly configDir: string\n readonly jsonlPath: string\n raw = \"\"\n\n private proc: BunSubprocess | null = null\n private cursor = 0 // index into transcript split('\\n')\n private lastDataAt = 0\n private exited = false\n private exitCode: number | null = null\n private aborted = false\n private readonly signal?: AbortSignal\n private readonly o: Required<\n Omit<\n ClaudeSessionOptions,\n | \"cliPath\"\n | \"configDir\"\n | \"model\"\n | \"settingSources\"\n | \"extraArgs\"\n | \"signal\"\n | \"ignoreAnthropicApiKey\"\n | \"effort\"\n >\n > &\n Pick<\n ClaudeSessionOptions,\n | \"cliPath\"\n | \"configDir\"\n | \"model\"\n | \"settingSources\"\n | \"extraArgs\"\n | \"ignoreAnthropicApiKey\"\n | \"effort\"\n >\n\n constructor(opts: ClaudeSessionOptions = {}) {\n this.cwd = path.resolve(opts.cwd ?? process.cwd())\n this.configDir = resolveConfigDir(opts.configDir)\n this.signal = opts.signal\n this.sessionId = randomUUID()\n this.jsonlPath = path.join(\n this.configDir,\n \"projects\",\n encodeCwd(this.cwd),\n `${this.sessionId}.jsonl`,\n )\n this.o = {\n cwd: this.cwd,\n cliPath: opts.cliPath,\n configDir: this.configDir,\n model: opts.model,\n settingSources: opts.settingSources,\n extraArgs: opts.extraArgs ?? [],\n ignoreAnthropicApiKey: opts.ignoreAnthropicApiKey,\n effort: opts.effort,\n cols: opts.cols ?? 200,\n rows: opts.rows ?? 50,\n bootMinMs: opts.bootMinMs ?? 3000,\n bootQuietMs: opts.bootQuietMs ?? 1500,\n bootMaxMs: opts.bootMaxMs ?? 25000,\n pollMs: opts.pollMs ?? 250,\n // Agentic turns (tool loops) routinely run for many minutes; a short\n // cap would surface as a mid-task error result. 30 min mirrors the\n // proxy-tool ceiling rather than a chat-reply expectation.\n turnTimeoutMs: opts.turnTimeoutMs ?? 1_800_000,\n bracketedPaste: opts.bracketedPaste ?? true,\n submitMinMs: opts.submitMinMs ?? 200,\n submitConfirmMs: opts.submitConfirmMs ?? 1500,\n submitMaxRetries: opts.submitMaxRetries ?? 8,\n debug: opts.debug ?? false,\n }\n }\n\n async start(): Promise<void> {\n if (this.signal?.aborted) throw new Error(\"aborted before start\")\n this.signal?.addEventListener(\n \"abort\",\n () => {\n this.aborted = true\n this.dispose()\n },\n { once: true },\n )\n const claude = resolveClaude(this.o.cliPath ?? \"claude\")\n const args: string[] = [\"--session-id\", this.sessionId]\n if (this.o.model) args.push(\"--model\", this.o.model)\n if (this.o.settingSources !== null && this.o.settingSources !== undefined) {\n args.push(\"--setting-sources\", this.o.settingSources)\n }\n if (this.o.extraArgs && this.o.extraArgs.length) args.push(...this.o.extraArgs)\n\n if (this.o.debug)\n process.stderr.write(`[session] spawn: ${claude} ${args.join(\" \")}\\n`)\n\n this.lastDataAt = Date.now()\n this.proc = Bun.spawn([claude, ...args], {\n cwd: this.cwd,\n env: interactiveSpawnEnv({\n // The resolved field, not `this.o.configDir`: same value (the\n // constructor copies it in) but typed as always present.\n configDir: this.configDir,\n ignoreAnthropicApiKey: this.o.ignoreAnthropicApiKey,\n effort: this.o.effort,\n }),\n terminal: {\n cols: this.o.cols,\n rows: this.o.rows,\n data: (_term, d) => {\n this.lastDataAt = Date.now()\n const chunk = Buffer.from(d).toString(\"utf8\")\n this.raw += chunk\n if (this.o.debug) process.stdout.write(chunk)\n },\n },\n })\n this.proc.exited\n .then((code) => {\n this.exitCode = typeof code === \"number\" ? code : null\n this.exited = true\n this.proc = null\n })\n .catch(() => {\n this.exited = true\n this.proc = null\n })\n\n await this.waitForBoot()\n this.cursor = this.lineCount()\n }\n\n /** Wait until the TUI has been quiet for bootQuietMs (Ink ready), bounded by\n * bootMinMs..bootMaxMs. */\n private async waitForBoot(): Promise<void> {\n const start = Date.now()\n while (Date.now() - start < this.o.bootMaxMs) {\n await delay(150)\n if (this.aborted) throw new Error(\"aborted during boot\")\n if (this.exited) {\n throw new Error(this.failureMessage(\"claude exited during boot\", true))\n }\n const elapsed = Date.now() - start\n const sinceData = Date.now() - this.lastDataAt\n if (elapsed >= this.o.bootMinMs && sinceData >= this.o.bootQuietMs) return\n }\n }\n\n /** Submit the freshly-injected prompt and confirm the turn was actually\n * accepted. A large bracketed paste collapses into a \"[Pasted text]\"\n * placeholder; an Enter sent while claude is still ingesting the paste is\n * silently dropped, so a single fixed-delay Enter races the paste and can\n * leave the prompt sitting unsubmitted (→ hang until turnTimeoutMs). Send\n * Enter, then poll for transcript growth past the cursor (the turn's records\n * are written on acceptance); resend Enter until accepted or the retry\n * budget is spent. Polling growth (not a blind delay) also stops us from\n * sending a stray Enter once the turn is in flight. */\n private async submitTurn(): Promise<void> {\n await delay(this.o.submitMinMs)\n for (let attempt = 0; attempt < this.o.submitMaxRetries; attempt++) {\n if (this.aborted || this.exited || !this.proc) return\n this.proc.terminal.write(\"\\r\")\n const until = Date.now() + this.o.submitConfirmMs\n while (Date.now() < until) {\n await delay(80)\n if (this.aborted || this.exited) return\n if (this.lineCount() > this.cursor) return // turn accepted\n }\n }\n }\n\n private readRawLines(): string[] {\n try {\n return fs.readFileSync(this.jsonlPath, \"utf8\").split(\"\\n\")\n } catch {\n return []\n }\n }\n\n /** Count of complete lines (split('\\n') minus the trailing/partial element). */\n private lineCount(): number {\n const lines = this.readRawLines()\n return lines.length > 0 ? lines.length - 1 : 0\n }\n\n private rawTail(max = 600): string {\n const clean = this.raw\n // Strip ANSI escape/control sequences before including terminal output in diagnostics.\n .replace(/\\x1B(?:[@-Z\\\\-_]|\\[[0-?]*[ -/]*[@-~])/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim()\n return clean.length > max ? clean.slice(-max) : clean\n }\n\n private failureMessage(reason: string, includeRaw = false): string {\n const parts = [\n `${reason} (sessionId=${this.sessionId}, jsonlPath=${this.jsonlPath}, exitCode=${this.exitCode ?? \"unknown\"})`,\n ]\n if (includeRaw) {\n const tail = this.rawTail()\n if (tail) parts.push(`terminalTail=${JSON.stringify(tail)}`)\n }\n return parts.join(\"; \")\n }\n\n /**\n * Inject a turn into the live session and return the assistant reply once a\n * terminal stop_reason is observed in the transcript.\n */\n async ask(prompt: string, perTurnTimeoutMs?: number): Promise<TurnResult> {\n if (this.aborted) throw new Error(\"aborted\")\n if (!this.proc || this.exited)\n throw new Error(\"session not started or already exited\")\n const timeout = perTurnTimeoutMs ?? this.o.turnTimeoutMs\n const t0 = Date.now()\n\n // Inject. Bracketed paste keeps multi-line prompts from submitting early;\n // submitTurn() then presses Enter and confirms the turn was accepted,\n // resending Enter if the (collapsed) paste swallowed the first one.\n if (this.o.bracketedPaste) {\n this.proc.terminal.write(\"\\x1b[200~\" + prompt + \"\\x1b[201~\")\n } else {\n this.proc.terminal.write(prompt)\n }\n await this.submitTurn()\n\n const collected: string[] = []\n let lastUsage: any = null\n let stopReason: string | null = null\n const deadline = Date.now() + timeout\n\n while (Date.now() < deadline) {\n await delay(this.o.pollMs)\n if (this.aborted) throw new Error(\"aborted mid-turn\")\n const lines = this.readRawLines()\n const lastComplete = lines.length - 1 // exclusive bound; trailing/partial line skipped\n if (lastComplete <= this.cursor) {\n // Drain the transcript before reacting to exit: a final assistant record\n // can be flushed in the same tick the process exits.\n if (this.exited) throw new Error(this.failureMessage(\"claude exited mid-turn\", true))\n continue\n }\n\n for (let i = this.cursor; i < lastComplete; i++) {\n const s = lines[i]\n if (!s || !s.trim()) continue\n let rec: any\n try {\n rec = JSON.parse(s)\n } catch {\n continue\n }\n if (rec.type === \"assistant\" && rec.message) {\n for (const b of rec.message.content ?? []) {\n if (b?.type === \"text\" && typeof b.text === \"string\")\n collected.push(b.text)\n }\n if (rec.message.usage) lastUsage = rec.message.usage\n if (\n rec.message.stop_reason &&\n TERMINAL_STOP.has(rec.message.stop_reason)\n ) {\n stopReason = rec.message.stop_reason\n }\n }\n }\n this.cursor = lastComplete\n if (stopReason) break\n }\n\n if (!stopReason) {\n throw new Error(\n this.failureMessage(\n `turn timed out after ${timeout}ms (no terminal assistant record; collected ${collected.length} text block(s))`,\n ),\n )\n }\n\n const u = lastUsage ?? {}\n return {\n text: collected.join(\"\\n\").trim(),\n stopReason,\n usage: lastUsage,\n cacheReadTokens: u.cache_read_input_tokens ?? 0,\n cacheCreationTokens: u.cache_creation_input_tokens ?? 0,\n ephemeral1hTokens: u.cache_creation?.ephemeral_1h_input_tokens ?? 0,\n ephemeral5mTokens: u.cache_creation?.ephemeral_5m_input_tokens ?? 0,\n inputTokens: u.input_tokens ?? 0,\n outputTokens: u.output_tokens ?? 0,\n elapsedMs: Date.now() - t0,\n }\n }\n\n /**\n * Like ask(), but instead of collecting the reply text it re-emits each NEW\n * raw JSONL transcript line via onLine (verbatim) until a terminal\n * stop_reason. Returns the terminal stop_reason + the last assistant usage.\n * Used by the opencode plugin transport shim, which feeds these raw lines\n * into the existing stream-json line handler unchanged.\n */\n async tailTurn(\n prompt: string,\n onLine: (rawLine: string) => void,\n perTurnTimeoutMs?: number\n ): Promise<{ stopReason: string | null; usage: any | null }> {\n if (this.aborted) throw new Error(\"aborted\")\n if (!this.proc || this.exited)\n throw new Error(\"session not started or already exited\")\n const timeout = perTurnTimeoutMs ?? this.o.turnTimeoutMs\n\n if (this.o.bracketedPaste) {\n this.proc.terminal.write(\"\\x1b[200~\" + prompt + \"\\x1b[201~\")\n } else {\n this.proc.terminal.write(prompt)\n }\n await this.submitTurn()\n\n let lastUsage: any = null\n let totalOutput = 0\n let stopReason: string | null = null\n const deadline = Date.now() + timeout\n\n while (Date.now() < deadline) {\n await delay(this.o.pollMs)\n if (this.aborted) throw new Error(\"aborted mid-turn\")\n const lines = this.readRawLines()\n const lastComplete = lines.length - 1\n if (lastComplete <= this.cursor) {\n // Drain the transcript before reacting to exit: the terminal assistant\n // record can land in the same tick the process exits.\n if (this.exited) {\n throw new Error(this.failureMessage(\"claude exited mid-turn\", true))\n }\n continue\n }\n for (let i = this.cursor; i < lastComplete; i++) {\n const s = lines[i]\n if (!s || !s.trim()) continue\n onLine(s)\n let rec: any\n try {\n rec = JSON.parse(s)\n } catch {\n continue\n }\n if (rec.type === \"assistant\" && rec.message) {\n if (rec.message.usage) {\n lastUsage = rec.message.usage\n totalOutput += rec.message.usage.output_tokens ?? 0\n }\n if (\n rec.message.stop_reason &&\n TERMINAL_STOP.has(rec.message.stop_reason)\n ) {\n stopReason = rec.message.stop_reason\n }\n }\n }\n this.cursor = lastComplete\n if (stopReason) break\n }\n\n // Context (input/cache) = the LAST record's full conversation state; output\n // = SUM across all assistant records this turn (each generation), else\n // multi-record tool turns undercount output. toUsage() prefers\n // iterations[last], so patch that entry's output too.\n let usage: any = lastUsage\n if (lastUsage) {\n usage = { ...lastUsage, output_tokens: totalOutput }\n if (Array.isArray(lastUsage.iterations) && lastUsage.iterations.length > 0) {\n const iters = lastUsage.iterations.map((it: any) => ({ ...it }))\n iters[iters.length - 1] = {\n ...iters[iters.length - 1],\n output_tokens: totalOutput,\n }\n usage.iterations = iters\n }\n }\n if (!stopReason) {\n throw new Error(\n this.failureMessage(\n `turn timed out after ${timeout}ms (no terminal assistant record)`,\n ),\n )\n }\n\n return { stopReason, usage }\n }\n\n dispose(): void {\n if (this.proc) {\n try {\n this.proc.terminal.write(\"\\x03\")\n } catch {}\n try {\n this.proc.kill()\n } catch {}\n try {\n this.proc.terminal.close()\n } catch {}\n }\n this.proc = null\n }\n}\n\n/** One-shot convenience (drop-in for `claude -p`): start, ask, dispose. */\nexport async function askOnce(\n prompt: string,\n opts: ClaudeSessionOptions = {},\n): Promise<TurnResult> {\n const s = new ClaudeSession(opts)\n await s.start()\n try {\n return await s.ask(prompt)\n } finally {\n s.dispose()\n }\n}\n","/**\n * The text this plugin appends to Claude's system prompt, and the helpers\n * that assemble it.\n *\n * Split out of `claude-code-language-model.ts` verbatim: every constant and\n * function here behaves exactly as it did there, and the language model\n * re-exports the public ones so existing importers are unchanged.\n */\nimport type { LanguageModelV3CallOptions } from \"@ai-sdk/provider\"\nimport { readFileSync, writeFileSync } from \"node:fs\"\nimport { homedir, tmpdir } from \"node:os\"\nimport { randomUUID } from \"node:crypto\"\nimport { dirname, join } from \"node:path\"\nimport { log } from \"./logger.js\"\n\nfunction readPromptFileIfPresent(path: string): string | undefined {\n try {\n const content = readFileSync(path, \"utf8\").trim()\n return content || undefined\n } catch {\n return undefined\n }\n}\n\nfunction nearestWorkspaceAgentsPrompt(cwd: string): string | undefined {\n let dir = cwd\n while (true) {\n const content = readPromptFileIfPresent(join(dir, \"AGENTS.md\"))\n if (content) return content\n const parent = dirname(dir)\n if (parent === dir) return undefined\n dir = parent\n }\n}\n\nconst AGENTS_MAINTENANCE_HINT = `## Keeping AGENTS.md up to date\n\nWhen you complete a task, phase, or to-do item that is listed in AGENTS.md, update the file\nimmediately after the work is done — mark it ✅, check it off, or remove it. Do this inside\nthe same turn so the next session does not repeat work that is already finished.`\n\nconst MULTI_STEP_TASK_HINT = `## Continuing through multi-step tasks\n\nopencode requires the user to press \"continue\" after each turn ends. When a\ntask has multiple steps, do them all in one turn — chain tool calls rather\nthan pausing for user confirmation between subtasks. End the turn only\nwhen the task is done, you need clarification on intent, or you hit a real\nblocker. The user can interrupt or abort at any time; turn endings should\nmark meaningful checkpoints, not every completed substep.`\n\n/**\n * Appended to the system prompt whenever the `task` proxy tool is\n * enabled. Live sessions (2026-07-04) showed models resolving opencode's\n * \"call the task tool with subagent: X\" mention hint to Claude Code's\n * native TaskCreate: haiku created a todo and narrated a dispatch that\n * never happened; sonnet probed TaskCreate's schema before recovering.\n * The proxy tool can also be deferred behind ToolSearch, in which case\n * \"the task tool\" is invisible while TaskCreate is not. Name the exact\n * tool, the recovery path, and the failure mode.\n */\nexport const SUBAGENT_DISPATCH_HINT = `## opencode subagents\n\nSubagent dispatch in this environment goes through exactly two tools: \\`mcp__opencode_proxy__task\\` for one subagent and \\`mcp__opencode_proxy__task_batch\\` for two or more at once.\n\n- Two or more independent subagents in one response: make ONE \\`mcp__opencode_proxy__task_batch\\` call with a \\`tasks\\` array (each item is a normal task input). Claude Code runs MCP calls one at a time, so several \\`mcp__opencode_proxy__task\\` calls in the same response run serially; \\`task_batch\\` runs them concurrently in opencode and returns every result together, labelled in order.\n- When the user mentions \\`@<agent>\\` or an instruction says \"call the task tool with subagent: <name>\", call \\`mcp__opencode_proxy__task\\` with \\`subagent_type: \"<name>\"\\`.\n- If that tool is not in your visible tool list it is deferred — load it with ToolSearch (\\`select:mcp__opencode_proxy__task\\`), then call it.\n- Claude Code's built-in TaskCreate/TaskUpdate/TaskList manage a local todo list. They cannot dispatch subagents; creating a task there runs nothing. Never report a subagent as dispatched unless \\`mcp__opencode_proxy__task\\` returned its result.\n- Do not verify a subagent's existence by searching config files — the tool's description lists the available agent types, and invalid types fail fast with a clear error.`\n\n/**\n * Appended to the system prompt whenever the `question` proxy tool is\n * enabled. Live testing (2026-07-05, haiku) showed the model's reasoning\n * correctly identified `mcp__opencode_proxy__question` as the tool to use,\n * but then emitted a tool call for bare `question` — stripping the MCP\n * prefix. opencode's AI SDK bridge has no bare `question` tool, so the\n * call rendered as `⚙ invalid`. Same near-miss pattern the task proxy\n * hit (TaskCreate vs mcp__opencode_proxy__task); the fix is the same:\n * name the exact tool in the system prompt so the model doesn't\n * abbreviate.\n */\nexport const QUESTION_PROXY_HINT = `## Asking the operator questions\n\nStructured questions in this environment go through exactly one tool: \\`mcp__opencode_proxy__question\\`.\n\n- When you need to ask the operator a question with options, call \\`mcp__opencode_proxy__question\\` with a \\`questions\\` array (each item has \\`question\\`, \\`header\\`, \\`options\\` of \\`{label, description}\\`, and optional \\`multiple\\`).\n- If that tool is not in your visible tool list it is deferred — load it with ToolSearch (\\`select:mcp__opencode_proxy__question\\`), then call it by its FULL name.\n- Do NOT call bare \\`question\\` — that is not a tool. Always use the full \\`mcp__opencode_proxy__question\\` name when invoking it.\n- Claude Code's built-in \\`AskUserQuestion\\` is disabled in this environment; the proxy is the only way to ask structured questions.`\n\n/**\n * Prepended to every appended system prompt so Claude knows which\n * context-management tools exist in the Claude CLI runtime versus a\n * direct API provider. DCP and similar plugins forward compress/distill/\n * prune instructions via system.transform; those reach us through\n * extractSystemMessages, but the tools themselves are not available in\n * the CLI environment. Without this note Claude wastes thinking cycles\n * searching for tools that don't exist.\n */\nconst CLAUDE_CLI_CONTEXT_NOTE = `## Runtime environment: Claude Code CLI\n\nYou are running via the Claude Code CLI (not a direct API call). This affects context management:\n\n- The \\`compress\\` tool is NOT available. Do not attempt to call it.\n- The \\`distill\\`, \\`prune\\`, and \\`extract\\` tools are NOT available.\n- Context window management is handled automatically by Claude CLI's own session history.\n- Ignore any system instructions that tell you to call \\`compress\\` — they are intended for direct API providers, not this environment.\n- DCP context injections (AGENTS.md, dynamic state) arrive via the system prompt and are already applied.`\n\n/**\n * Replaces the note above when `compress` is in the resolved proxy list.\n * The full MCP name is spelled out for the same reason the question proxy\n * hint spells its own out: models strip the prefix and call bare\n * `compress`, which opencode renders as `⚙ invalid`.\n */\nconst CLAUDE_CLI_COMPRESS_NOTE = `## Runtime environment: Claude Code CLI\n\nYou are running via the Claude Code CLI (not a direct API call). This affects context management:\n\n- To compress context, call \\`mcp__opencode_proxy__compress\\` with a \\`summary\\` argument. Use that exact full name.\n- The reset happens at the start of your NEXT turn: this Claude Code session is discarded and a fresh one starts with your summary as its only prior context. Keep working normally after the call.\n- Everything outside the summary is gone after the reset — tool output, files you read, and the earlier conversation are not replayed. Write the summary as the authoritative record.\n- The \\`distill\\`, \\`prune\\`, and \\`extract\\` tools are NOT available.\n- DCP context injections (AGENTS.md, dynamic state) arrive via the system prompt and are already applied.`\n\n/**\n * Used when opencode's own `compress` tool is forwarded through the proxy\n * (`proxyOpencodeTools: [\"compress\"]`) instead of the plugin's in-process\n * one. The two shrink different windows and the difference has to be said\n * out loud: opencode's rewrites opencode's transcript, so the live Claude\n * Code session keeps everything it already had. A model told otherwise\n * would assume detail it can still see had been discarded.\n */\nconst CLAUDE_CLI_OPENCODE_COMPRESS_NOTE = `## Runtime environment: Claude Code CLI\n\nYou are running via the Claude Code CLI (not a direct API call). This affects context management:\n\n- To compress context, call \\`mcp__opencode_proxy__compress\\`. Use that exact full name. It runs opencode's own \\`compress\\` tool, which is what a \"MAX CONTEXT LIMIT REACHED\" reminder is asking you to do.\n- It compresses opencode's stored conversation, NOT this Claude Code session. Your current session keeps the context it already has, so do not assume earlier detail is gone after the call.\n- The \\`distill\\`, \\`prune\\`, and \\`extract\\` tools are NOT available.\n- DCP context injections (AGENTS.md, dynamic state) arrive via the system prompt and are already applied.`\n\n/**\n * Extract text content from all `system`-role messages in the prompt.\n * Standard API providers forward these as the `system` parameter; for\n * Claude CLI, the only equivalent path is --append-system-prompt-file.\n * Plugins like opencode-dcp inject AGENTS.md and other context via\n * system-role messages and would otherwise be silently dropped.\n */\nexport function extractSystemMessages(\n prompt: LanguageModelV3CallOptions[\"prompt\"],\n): string[] {\n const out: string[] = []\n for (const msg of prompt) {\n if (msg.role !== \"system\") continue\n if (typeof msg.content === \"string\") {\n if (msg.content.trim()) out.push(msg.content.trim())\n } else if (Array.isArray(msg.content)) {\n for (const part of msg.content as any[]) {\n if (\n part?.type === \"text\" &&\n typeof part.text === \"string\" &&\n part.text.trim()\n ) {\n out.push(part.text.trim())\n }\n }\n }\n }\n return out\n}\n\nexport interface AppendedSystemPromptOptions {\n /** True when the plugin's own `compress` def is in the proxy list. */\n compressEnabled?: boolean\n /** True when opencode's `compress` tool is forwarded through the proxy. */\n opencodeCompressEnabled?: boolean\n /** Summary from a previous `compress` call, if this key has one. */\n compressionSummary?: string\n}\n\nexport function buildAppendedSystemPrompt(\n cwd: string,\n includeMultiStepHint = true,\n extraSystemContent: string[] = [],\n options: AppendedSystemPromptOptions = {},\n): string | undefined {\n const parts: string[] = []\n // First, so it reads as prior context for everything that follows.\n if (options.compressionSummary?.trim()) {\n parts.push(\n `## Summary of earlier work (context was compressed)\\n\\n${options.compressionSummary.trim()}`,\n )\n }\n // The plugin's own compress wins when both are somehow live, matching the\n // def-level precedence in resolveProxyOpencodeToolDefs: it is the one that\n // holds the name, so it is the one the model would reach.\n parts.push(\n options.compressEnabled\n ? CLAUDE_CLI_COMPRESS_NOTE\n : options.opencodeCompressEnabled\n ? CLAUDE_CLI_OPENCODE_COMPRESS_NOTE\n : CLAUDE_CLI_CONTEXT_NOTE,\n )\n for (const s of extraSystemContent) {\n if (s.trim()) parts.push(s.trim())\n }\n const configRoot =\n process.env.XDG_CONFIG_HOME ?? join(homedir(), \".config\")\n const globalAgents = readPromptFileIfPresent(join(configRoot, \"opencode\", \"AGENTS.md\"))\n const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd)\n\n // opencode already forwards AGENTS.md inside its own system prompt\n // (`extraSystemContent`, under an \"Instructions from:\" header), so a\n // disk-read copy would reach the model twice. Only push ours when the\n // forwarded text does not already contain it. No match (formatting drift,\n // or the interactive path, which forwards nothing) keeps the old behaviour,\n // so AGENTS.md is never lost. (Dedup by @HeikoAtGitHub, 25260a4.)\n const forwarded = extraSystemContent.join(\"\\n\\n\")\n const pushGlobal = !!globalAgents && !forwarded.includes(globalAgents)\n const pushWorkspace =\n !!workspaceAgents && workspaceAgents !== globalAgents &&\n !forwarded.includes(workspaceAgents)\n if (pushGlobal) parts.push(globalAgents)\n if (pushWorkspace) parts.push(workspaceAgents)\n if (pushGlobal || pushWorkspace) parts.push(AGENTS_MAINTENANCE_HINT)\n if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT)\n\n const content = parts.join(\"\\n\\n\")\n if (!content) return undefined\n\n const path = join(tmpdir(), `opencode-cc-sys-${randomUUID()}.md`)\n try {\n writeFileSync(path, content, \"utf8\")\n return path\n } catch (err) {\n log.warn(\"failed to write system prompt file\", { error: String(err) })\n return undefined\n }\n}\n","/**\n * Auto-continue: whether a turn that ended without finishing should be\n * nudged to keep going, and the nudge itself.\n *\n * Split out of `claude-code-language-model.ts` verbatim. The decision is\n * pure (state plus a snapshot of the turn in, a verdict out), which is why\n * it moved first: `shouldAutoContinueIncompleteTurn` is exercised directly\n * by `test-auto-continue.ts` and by the corpus runner under `sim/`.\n */\n\nconst AUTO_CONTINUE_MAX_ATTEMPTS = 8\nconst AUTO_CONTINUE_MAX_ELAPSED_MS = 10 * 60 * 1000\nconst AUTO_CONTINUE_NO_PROGRESS_LIMIT = 2\n\nconst AUTO_CONTINUE_PROMPT =\n \"Continue the task from where you stopped. Do not summarize; keep working until the requested task is complete, you need clarification, or you hit a real blocker.\"\n\nexport interface AutoContinueState {\n enabled: boolean | \"smart\" | undefined\n attempts: number\n startedAt: number\n noProgressCount: number\n lastSignature?: string\n aborted?: boolean\n /**\n * Latched true once AskUserQuestion is rendered this turn. Auto-continue\n * must never fire afterwards: the model has handed control to the operator\n * and is waiting for a real reply. Without this, a short trailing text after\n * the question (one that doesn't trip looksLikeQuestion) would let the turn\n * look \"incomplete\", and the auto-continue nudge would make the model\n * proceed on its own — which the operator sees as the question being\n * answered/cancelled without them ever interacting.\n */\n sawAskUserQuestion?: boolean\n}\n\nexport interface AutoContinueSnapshot {\n text: string\n /**\n * Text of the most recent assistant text block only. Used for final-answer\n * detection so mid-task narration like \"Implementing now. Updated the\n * search index.\" in an earlier block doesn't trip the keyword regex.\n */\n lastVisibleText: string\n hadReasoning: boolean\n hadToolActivity: boolean\n hadProxyActivity: boolean\n isError?: boolean\n /**\n * Protocol-level stop signal from the Claude API (forwarded by Claude\n * CLI). When present and non-empty, we trust it as authoritative — the\n * model itself signaled why the turn ended (`end_turn`, `max_tokens`,\n * `stop_sequence`, `refusal`, `pause_turn`, `tool_use`, etc.) — and stop\n * without running the keyword regex. The heuristic only runs as a\n * fallback when `stop_reason` is missing (older CLI versions, abrupt\n * termination).\n */\n stopReason?: string | null\n now?: number\n}\n\n/**\n * A compaction turn must never be nudged to continue. `AUTO_CONTINUE_PROMPT`\n * says \"Do not summarize; keep working\", the exact inverse of what `/compact`\n * is for, and continuation reopens the same stream rather than closing it, so\n * the non-summary text would land inside what opencode stores as the session\n * summary. This was unreachable while every `stop_reason` ended the turn;\n * truncation-continue made a summary that hits the output cap reach it.\n * Exported so the wiring is testable, since the state itself is built inline\n * in `doStream`.\n */\nexport function autoContinueEnabledFor(\n compactionMode: boolean,\n configured: boolean | \"smart\" | undefined,\n): boolean | \"smart\" | undefined {\n return compactionMode ? false : configured\n}\n\n/**\n * Stop reasons that mean \"cut off\", not \"done\". Anthropic sends `max_tokens`;\n * `max_output_tokens` is accepted as a defensive alias so a rename upstream\n * degrades to today's behaviour rather than silently mis-reading a real stop.\n */\nfunction isTruncationStopReason(stopReason: string): boolean {\n return stopReason === \"max_tokens\" || stopReason === \"max_output_tokens\"\n}\n\nexport interface AutoContinueDecision {\n continue: boolean\n reason: string\n}\n\nfunction normalizeVisibleText(text: string): string {\n return text.replace(/\\s+/g, \" \").trim()\n}\n\nfunction looksLikeQuestion(text: string): boolean {\n const normalized = normalizeVisibleText(text).toLowerCase()\n if (!normalized) return false\n // v0.4.10 tweak 5a: '?' anywhere in the last block, not just trailing.\n // Catches long answers that pose a question mid-text then list options\n // and end with a period. FP risk on inline code (`result?.value`) is\n // accepted — cost is one extra \"continue\" press, in the safe direction.\n if (normalized.includes(\"?\")) return true\n // v0.4.11 additions: ready when you are / standing by / i'll stand by /\n // let me know when. These are awaiting-input idioms with no '?'. The\n // \"standing by\" addition has historical significance — it's the exact\n // stub phrase Claude CLI emits on empty turns that commit 49345e3 was\n // designed to suppress at the message-builder layer. This adds a second\n // line of defense at the model-output layer for cases where the model\n // organically produces the same idiom.\n //\n // v0.4.12 additions: over to you / your turn / all yours / let me know\n // how / i'm here. Defensive coverage of soft-proceed idioms in the\n // model's vocabulary. \"i'm here\" has the highest FP risk (\"I'm here to\n // help with X\" is a conversational opener) but cost of FP is one extra\n // continue press — safe direction.\n return /\\b(please confirm|can you confirm|should i|would you like|do you want|which option|choose|pick one|need your|need you to|what would you like|let me know if|let me know whether|let me know what|let me know when|let me know how|if you'?d like|if you want to|tell me if|tell me which|tell me whether|say (?:go|yes|no)|push back|sign off|sounds? (?:good|right)|your call|your move|your turn|over to you|all yours|up to you|ready to (?:ship|go|proceed|merge)|ready (?:when|whenever|once|if) you|standing by|i'?ll stand ?by|i'?m here|happy to (?:ship|go|proceed|merge))\\b/.test(normalized)\n}\n\nfunction looksLikeBlocker(text: string): boolean {\n const normalized = normalizeVisibleText(text).toLowerCase()\n if (!normalized) return false\n // v0.4.10 tweak 3: 'needs your' / 'needs you to' / 'action required'\n // are intent-equivalent to 'requires your' but use the verb-with-s form.\n return /\\b(blocked|blocker|cannot proceed|can't proceed|unable to proceed|need clarification|need more information|permission denied|failed and needs|requires your|needs your|needs you to|action required|manual step|required from you)\\b/.test(normalized)\n}\n\nfunction looksLikeFinalAnswer(text: string): boolean {\n const normalized = normalizeVisibleText(text).toLowerCase()\n if (looksLikeQuestion(normalized) || looksLikeBlocker(normalized)) return false\n // v0.4.15: strong-completion phrases bypass the 30-char length floor.\n // These are unambiguous end-of-turn signals at any text length — even\n // a short standalone \"We're done.\" should stop.\n if (/\\b(we'?re done|we are done|all done|all set)\\b/.test(normalized)) {\n return true\n }\n // v0.4.10 tweak 4: floor lowered 40 → 30 chars. Catches short clean\n // completions like \"Task is now completely done. Pushed.\" (36 chars)\n // while keeping a buffer against ambiguous short narration.\n if (normalized.length < 30) return false\n // v0.4.15: keyword list extended with deploy/ship verbs the model\n // routinely uses at turn end (shipped, deployed, merged, tagged, live,\n // pinned). FP risk highest on \"live\" — \"live data\" mid-turn could match\n // — but cost of FP is one extra continue press, safe direction.\n return /\\b(done|completed|fixed|implemented|verified|published|released|sent|delivered|updated|shipped|deployed|merged|tagged|live|pinned)\\b/.test(normalized) ||\n // v0.4.15: also accept present-tense \"tests pass\" / \"checks pass\".\n // Real fire 03:31 ended in \"78/78 tests pass\" — past-tense-only regex\n // missed it.\n /\\b(checks?|tests?) (?:pass|passes|passed)\\b/.test(normalized) ||\n /\\b(summary|what changed|verification)\\b/.test(normalized)\n}\n\nexport function continuationSignature(snapshot: AutoContinueSnapshot): string {\n const text = normalizeVisibleText(snapshot.text).slice(-500)\n return JSON.stringify({\n text,\n reasoning: snapshot.hadReasoning,\n tools: snapshot.hadToolActivity,\n proxy: snapshot.hadProxyActivity,\n })\n}\n\nexport function shouldAutoContinueIncompleteTurn(\n state: AutoContinueState,\n snapshot: AutoContinueSnapshot,\n): AutoContinueDecision {\n if (state.enabled === false) return { continue: false, reason: \"disabled\" }\n if (snapshot.isError) return { continue: false, reason: \"error\" }\n if (state.aborted) return { continue: false, reason: \"aborted\" }\n // Once the model asked the operator a question this turn, never nudge it to\n // continue — it is waiting for a reply, not stalled. Latched so it holds\n // even when the trailing text after the question doesn't read as a question.\n if (state.sawAskUserQuestion) return { continue: false, reason: \"question\" }\n // v0.4.17: trust ANY protocol-level stop_reason as authoritative. If\n // Claude CLI emitted a stop_reason value at all, the model has signaled\n // a stop — honor it without consulting the keyword heuristic. The\n // heuristic only runs as a fallback when stop_reason is missing (older\n // CLI versions / edge cases). Maps snake_case → kebab-case for reason\n // label consistency with other reasons.\n if (snapshot.stopReason) {\n // ...with one exception, which is the narrow half of @JWebCoder's PR #15\n // worth keeping. Truncation is the single stop_reason that does NOT mean\n // the model finished: the response hit the output cap mid-sentence. The\n // old guard read it as a stop, so a cut-off answer was silently accepted\n // as complete. Falling through to the keyword heuristic below would not\n // fix it either, because a truncated prose answer has no tool or\n // reasoning activity and would die at the `no-activity` gate. So\n // truncation is authoritative in the opposite direction: continue, still\n // bounded by the attempt and elapsed rails. PR #15 itself deleted the\n // whole guard, which would have handed every turn back to the regex that\n // v0.4.17 deliberately demoted; that is why it was closed.\n if (isTruncationStopReason(snapshot.stopReason)) {\n if (state.attempts >= AUTO_CONTINUE_MAX_ATTEMPTS) {\n return { continue: false, reason: \"max-attempts\" }\n }\n const truncatedAt = snapshot.now ?? Date.now()\n if (truncatedAt - state.startedAt > AUTO_CONTINUE_MAX_ELAPSED_MS) {\n return { continue: false, reason: \"max-elapsed\" }\n }\n return { continue: true, reason: \"truncated\" }\n }\n return {\n continue: false,\n reason: snapshot.stopReason.replace(/_/g, \"-\"),\n }\n }\n if (state.attempts >= AUTO_CONTINUE_MAX_ATTEMPTS) {\n return { continue: false, reason: \"max-attempts\" }\n }\n const now = snapshot.now ?? Date.now()\n if (now - state.startedAt > AUTO_CONTINUE_MAX_ELAPSED_MS) {\n return { continue: false, reason: \"max-elapsed\" }\n }\n\n const text = normalizeVisibleText(snapshot.text)\n const lastText = normalizeVisibleText(snapshot.lastVisibleText)\n if (looksLikeQuestion(text)) return { continue: false, reason: \"question\" }\n if (looksLikeBlocker(text)) return { continue: false, reason: \"blocker\" }\n // Final-answer detection runs on the most recent text block only. Earlier\n // blocks may contain mid-task narration that would false-positive the\n // keyword regex; the model's actual \"I'm done\" sentence is in the last\n // block before result/end_turn.\n if (looksLikeFinalAnswer(lastText)) {\n return { continue: false, reason: \"final-answer\" }\n }\n\n const hadActivity =\n snapshot.hadReasoning || snapshot.hadToolActivity || snapshot.hadProxyActivity\n if (!hadActivity) return { continue: false, reason: \"no-activity\" }\n\n const signature = continuationSignature(snapshot)\n const noProgress = signature === state.lastSignature\n if (noProgress && state.noProgressCount + 1 >= AUTO_CONTINUE_NO_PROGRESS_LIMIT) {\n return { continue: false, reason: \"no-progress\" }\n }\n\n if (!text) {\n return { continue: true, reason: \"activity-without-visible-answer\" }\n }\n\n return { continue: true, reason: \"non-final-progress\" }\n}\n\nexport function makeAutoContinueMessage(): string {\n return JSON.stringify({\n type: \"user\",\n message: {\n role: \"user\",\n content: [{ type: \"text\", text: AUTO_CONTINUE_PROMPT }],\n },\n })\n}\n","/**\n * Claude Code's `AskUserQuestion`: recognising it, rendering it to the\n * operator, and the message the model gets back when it is denied.\n *\n * Split out of `claude-code-language-model.ts` verbatim. The deny path is\n * load-bearing (issue #8): the message must tell the model to stop and wait\n * unconditionally, and must never offer a proceed-anyway escape hatch.\n */\n\n/** Tool names that mean \"ask the human a question\" (CLI casing variants). */\nexport function isAskUserQuestionTool(name: string | undefined): boolean {\n if (!name) return false\n const n = name.toLowerCase()\n return n === \"askuserquestion\" || n === \"ask_user_question\"\n}\n\n/**\n * Deny message returned to the model when it invokes AskUserQuestion.\n *\n * AskUserQuestion is denied (see controlRequestBehaviorForTool) so the\n * headless CLI cannot self-answer against an empty TTY. The question is\n * already rendered to the operator by formatAskUserQuestion, so this text\n * tells the model to stop and wait — unconditionally. Earlier versions\n * offered an \"if this is non-interactive, proceed with a reasonable guess\"\n * escape hatch, but the model could not reliably tell interactive opencode\n * from a headless run and routinely took it, so questions appeared to be\n * skipped (issue #8). Stopping is the correct default for opencode; a\n * headless run simply ends the turn with the question as its final output.\n */\nconst ASK_USER_QUESTION_DENY_MESSAGE =\n \"Your question and its options have already been presented to the\" +\n \" operator verbatim. This is NOT a cancellation or a refusal — the\" +\n \" operator simply has not answered yet. Stop now: end your turn without\" +\n \" calling any more tools and without answering the question yourself. Do\" +\n \" not say the question was cancelled, skipped, or declined, and do not\" +\n \" guess, assume, or proceed on their behalf. Wait for the operator's\" +\n \" reply, which arrives as the next user message.\"\n\n/** Build the deny message for an auto-denied control request. */\nexport function denyMessageForTool(\n toolName: string | undefined,\n configuredDenyMessage?: string,\n): string {\n if (isAskUserQuestionTool(toolName)) return ASK_USER_QUESTION_DENY_MESSAGE\n return (\n configuredDenyMessage ??\n `Denied by opencode-claude-code policy for tool ${toolName}`\n )\n}\n\n/**\n * Render Claude Code's `AskUserQuestion` tool input as visible markdown.\n *\n * This is the fallback path used when the `Question` proxy is off or the\n * opencode build lacks the `question` registry entry. When the proxy is\n * enabled, `AskUserQuestion` is disabled via `--disallowedTools` and the\n * model calls `mcp__opencode_proxy__question` instead (opencode's native\n * `question` tool renders the TUI form). Here, the question + every\n * option is rendered as readable assistant text and the user answers in\n * the next turn — same approach as the `ExitPlanMode` handling. The\n * previous behavior collapsed the whole payload to a single faint\n * `_Asking: <q>_` line, dropping all options and any question past the\n * first.\n */\nexport function formatAskUserQuestion(input: Record<string, unknown>): string {\n const anyInput = input as any\n const questions: any[] = Array.isArray(anyInput?.questions)\n ? anyInput.questions\n : []\n\n if (questions.length === 0) {\n const single = anyInput?.question ?? anyInput?.text\n const q =\n typeof single === \"string\" && single.trim() ? single.trim() : \"Question?\"\n return `\\n\\n**${q}**\\n\\n_Reply with your answer to continue._\\n\\n`\n }\n\n const out: string[] = [\"\\n\\n\"]\n const multiQ = questions.length > 1\n questions.forEach((q, i) => {\n const text =\n (typeof q?.question === \"string\" && q.question.trim()) ||\n (typeof q?.text === \"string\" && q.text.trim()) ||\n \"Question?\"\n const header =\n typeof q?.header === \"string\" && q.header.trim() ? q.header.trim() : \"\"\n out.push(`**${multiQ ? `${i + 1}. ` : \"\"}${text}**`)\n if (header) out.push(` _(${header})_`)\n out.push(\"\\n\\n\")\n\n const options: any[] = Array.isArray(q?.options) ? q.options : []\n options.forEach((opt, j) => {\n const label =\n (typeof opt?.label === \"string\" && opt.label.trim()) ||\n (typeof opt === \"string\" && opt.trim()) ||\n `Option ${j + 1}`\n const desc =\n typeof opt?.description === \"string\" && opt.description.trim()\n ? ` — ${opt.description.trim()}`\n : \"\"\n out.push(`${j + 1}. **${label}**${desc}\\n`)\n })\n\n out.push(\n q?.multiSelect === true\n ? \"\\n_Select one or more — reply with the numbers or labels._\\n\\n\"\n : \"\\n_Reply with your choice (the number or label)._\\n\\n\",\n )\n })\n return out.join(\"\")\n}\n","/**\n * Reporting what actually happened to a fast-mode request.\n *\n * Split out of `claude-code-language-model.ts` verbatim. Fast mode fails\n * soft, so the downgrade has to warn rather than merely notice: the fast\n * model ids advertise fast pricing in opencode's picker, and a silent\n * downgrade leaves that price wrong for every later turn.\n */\nimport type { ClaudeStreamMessage } from \"./types.js\"\nimport { log } from \"./logger.js\"\n\n/**\n * Human-readable explanations for the CLI's `fast_mode_disabled_reason` codes,\n * so a downgrade tells the user what to do instead of leaking an enum.\n */\nconst FAST_MODE_REASONS: Record<string, string> = {\n sdk_opt_in_required:\n \"the CLI did not receive the headless opt-in (--settings). This is a plugin bug, please report it\",\n extra_usage_disabled:\n \"your account has usage credits turned off. Run /usage-credits in an interactive `claude` session to enable them\",\n free: \"fast mode requires a paid subscription or purchased credits\",\n preference: \"fast mode is turned off for your organization\",\n model_not_allowed:\n \"this model is not in your organization's allowed models\",\n not_first_party:\n \"fast mode only works against the Anthropic API directly, not Bedrock / Vertex / Foundry\",\n network_error: \"the CLI could not reach Anthropic to check availability\",\n disabled_by_env: \"CLAUDE_CODE_DISABLE_FAST_MODE is set in the environment\",\n pending: \"the CLI is still checking availability\",\n}\n\n/** Reasons already surfaced this process, so a persistent block warns once. */\nconst warnedFastModeReasons = new Set<string>()\n\n/** Test-only. */\nexport function _resetFastModeWarnings(): void {\n warnedFastModeReasons.clear()\n}\n\n/**\n * Report what actually happened to a fast-mode request.\n *\n * Fast mode fails soft: an ineligible account or a rate-limit cooldown drops\n * back to standard speed with no error. That silence is the problem worth\n * solving here: the fast model ids advertise fast pricing in opencode's picker,\n * so a downgrade the user cannot see means the picker is lying about cost for\n * every subsequent turn.\n *\n * A hard block is therefore a WARN, which this codebase routes to the TUI\n * unconditionally (NOTICE only surfaces in debug mode, which would defeat the\n * purpose). It is deduped per reason per process because the blocking\n * conditions are account-level and would otherwise repeat on every respawn.\n * Cooldown stays quieter: it is transient and clears on its own.\n */\nexport function reportFastModeState(\n msg: ClaudeStreamMessage,\n requested: boolean,\n): void {\n const state = msg.fast_mode_state\n if (!state) return\n\n if (!requested) {\n // Nothing was asked for. Only interesting at debug level.\n log.debug(\"fast mode state\", { state })\n return\n }\n\n if (state === \"on\") {\n log.info(\"fast mode active\", { state })\n return\n }\n\n const reason = msg.fast_mode_disabled_reason\n if (state === \"cooldown\") {\n log.notice(\n \"fast mode is in cooldown after a rate limit; this turn runs at standard speed and is billed at standard Opus rates, not the fast price shown in the model picker.\",\n { state, reason: reason ?? null },\n )\n return\n }\n\n const key = reason ?? \"unknown\"\n const explanation = reason ? FAST_MODE_REASONS[reason] : undefined\n const message = `fast mode was requested but is off${\n explanation ? `: ${explanation}` : reason ? ` (${reason})` : \"\"\n }. Turns run at standard speed and are billed at standard Opus rates, not the fast price shown in the model picker. Switch to the non-fast model id to make the picker's price accurate.`\n\n if (warnedFastModeReasons.has(key)) {\n log.debug(message, { state, reason: reason ?? null })\n return\n }\n warnedFastModeReasons.add(key)\n log.warn(message, { state, reason: reason ?? null })\n}\n","/**\n * Reading a single LLM call: which opencode session and agent it belongs\n * to, which model handles /compact, whether the prompt has anything new in\n * it, and how to describe an abort.\n *\n * Split out of `claude-code-language-model.ts` verbatim. Everything here is\n * pure and free-standing, which is why it was already exported and unit\n * tested from the class's outside.\n */\nimport type { LanguageModelV3CallOptions } from \"@ai-sdk/provider\"\n\n/**\n * Default model used for opencode `/compact`. Haiku 4.5 is fast\n * (~150 tok/s), has a hard 8k output cap that bounds latency, and is a\n * strong structured summarizer. Override per-project via the\n * `compactionModel` provider setting in opencode.json / opencode.jsonc,\n * or per-run via the `CLAUDE_CODE_COMPACTION_MODEL` env var (env wins).\n */\nexport const DEFAULT_COMPACTION_MODEL = \"claude-haiku-4-5\"\n\n/**\n * Pick the model used to handle /compact. Precedence:\n * 1. `CLAUDE_CODE_COMPACTION_MODEL` env var (per-process override)\n * 2. `configured` argument (the `compactionModel` provider setting)\n * 3. `DEFAULT_COMPACTION_MODEL`\n *\n * Exported as a free function so it can be unit-tested without\n * instantiating the language model class.\n */\nexport function resolveCompactionModel(configured?: string): string {\n const env = process.env.CLAUDE_CODE_COMPACTION_MODEL?.trim()\n if (env) return env\n const trimmed = configured?.trim()\n if (trimmed) return trimmed\n return DEFAULT_COMPACTION_MODEL\n}\n\n/**\n * Resolve the session affinity token for a given LLM call. The affinity\n * token is part of the session key in session-manager so two different\n * opencode sessions sharing the same cwd+model still get separate Claude\n * CLI processes.\n *\n * Priority:\n * 1. `x-session-affinity` request header (primary — opencode sets it for\n * third-party providers in packages/opencode/src/session/llm.ts).\n * 2. `opencodeSessionID` inside `providerOptions` (injected by the\n * `chat.params` hook in index.ts). Covers cases where the header is\n * absent: provider switch mid-session, title synthesis paths, older\n * opencode versions. opencode wraps `output.options` under the\n * providerID before passing it to the language model, so we look up\n * both the configured provider key and the canonical `\"claude-code\"`.\n * 3. `\"default\"` — safe fallback when neither source is available.\n *\n * Exported as a free function so it can be unit-tested without\n * instantiating the language model class.\n */\nexport function resolveSessionAffinity(\n headers: Record<string, string | undefined> | undefined,\n providerOptions: Record<string, unknown> | undefined,\n providerKey: string,\n): string {\n if (headers) {\n for (const key of Object.keys(headers)) {\n if (key.toLowerCase() === \"x-session-affinity\") {\n const v = headers[key]\n if (typeof v === \"string\" && v.length > 0) return v\n }\n }\n }\n if (providerOptions) {\n const bag =\n (providerOptions as any)[providerKey] ??\n (providerOptions as any)[\"claude-code\"]\n const sid = bag?.opencodeSessionID\n if (typeof sid === \"string\" && sid.length > 0) return sid\n }\n return \"default\"\n}\n\n/**\n * The opencode agent this call runs for, which is how compaction and title\n * calls are told apart from ordinary turns.\n *\n * 1. `opencodeAgent` in providerOptions, written by V1's `chat.params`\n * hook. Checked first so opencode 1.x behaves exactly as it always has.\n * 2. The `x-opencode-agent` request header, written by the V2 entrypoint's\n * `model.request` hook (src/v2.ts), which can set headers but not\n * provider options.\n */\nexport function resolveOpencodeAgent(\n headers: Record<string, string | undefined> | undefined,\n providerOptions: Record<string, unknown> | undefined,\n providerKey: string,\n): string | undefined {\n if (providerOptions) {\n const bag =\n (providerOptions as any)[providerKey] ??\n (providerOptions as any)[\"claude-code\"]\n const agent = bag?.opencodeAgent\n if (typeof agent === \"string\") return agent\n }\n if (headers) {\n for (const key of Object.keys(headers)) {\n if (key.toLowerCase() === \"x-opencode-agent\") {\n const value = headers[key]\n if (typeof value === \"string\" && value.length > 0) return value\n }\n }\n }\n return undefined\n}\n\n/** An `AbortSignal.reason` as loggable text: its name and message, or its type. */\nexport function describeAbortReason(reason: unknown): string {\n if (reason === undefined) return \"undefined\"\n if (reason instanceof Error) return `${reason.name}: ${reason.message}`\n if (typeof reason === \"string\") return reason\n try {\n return JSON.stringify(reason) ?? typeof reason\n } catch {\n return typeof reason\n }\n}\n\n/**\n * True if the prompt has any user-side content after the last assistant\n * message (text, tool_result, or any user role entry). False when the\n * prompt ends with an assistant message and there is nothing for Claude\n * to respond to — opencode sometimes iterates the agent loop one more\n * time after a turn naturally completed; without short-circuiting we'd\n * spawn Claude CLI on an empty turn and the model would reply with a\n * stub like \"Did you mean to send a message?\".\n */\nexport function hasNewUserContent(\n prompt: LanguageModelV3CallOptions[\"prompt\"],\n): boolean {\n for (let i = prompt.length - 1; i >= 0; i--) {\n const msg = prompt[i]\n if (msg.role === \"assistant\") return false\n // Tool-result turns from opencode's outer loop arrive in `tool`-role\n // messages (AI SDK V3 shape). Treat any tool-result part as new\n // content so the short-circuit doesn't drop turns where opencode is\n // delivering the result for a still-pending proxy MCP call — letting\n // that fire `stop` is what was forcing the user to press \"continue\".\n if (msg.role === \"tool\") {\n const content: any = msg.content\n if (Array.isArray(content)) {\n for (const part of content as any[]) {\n if (part?.type === \"tool-result\") return true\n }\n }\n continue\n }\n if (msg.role !== \"user\") continue\n const content: any = msg.content\n if (typeof content === \"string\") {\n if (content.trim()) return true\n continue\n }\n if (Array.isArray(content)) {\n for (const part of content as any[]) {\n if (part.type === \"text\" && part.text && part.text.trim()) return true\n if (part.type === \"tool-result\") return true\n // Image/file-only user turns count as new input — without this the\n // short-circuit drops them as if the turn were empty.\n if (part.type === \"image\" || part.type === \"file\") return true\n }\n }\n }\n return false\n}\n","/**\n * Reading opencode's answer to a proxied tool call back out of the next\n * prompt, and the fallback message for a result that arrived too late to\n * go back as a `tool_result`.\n *\n * Split out of `claude-code-language-model.ts` verbatim: the two extractors\n * were private methods that never touched `this` beyond calling each other,\n * so they became free functions unchanged.\n */\nimport type { LanguageModelV3CallOptions } from \"@ai-sdk/provider\"\nimport {\n formatTaskBatchResults,\n taskBatchChildToolCallId,\n taskBatchTasks,\n TASK_BATCH_TOOL_NAME,\n type ProxyToolResult,\n} from \"./proxy-mcp.js\"\nimport type { PendingProxyCall } from \"./proxy-broker.js\"\nimport { log } from \"./logger.js\"\n\n/**\n * A proxy result whose HTTP reply channel Claude already abandoned cannot\n * go back as a `tool_result` (the CLI closed that tool_use with a timeout\n * error). Hand it over as a user message that names the call instead.\n */\nexport function makeLateProxyResultMessage(\n entries: Array<{ call: PendingProxyCall; result: ProxyToolResult }>,\n): string {\n const sections = entries.map(({ call, result }) => {\n const failed = result.kind === \"error\" || result.isError === true\n const body = result.kind === \"error\" ? result.message : result.text\n return (\n `Your earlier \\`${call.toolName}\\` tool call (id ${call.toolCallId})` +\n ` has ${failed ? \"failed\" : \"completed\"}, but delivery or continuation was interrupted.` +\n ` Treat the following as its ${failed ? \"error\" : \"result\"} and continue from there;` +\n ` do not re-run it.\\n\\n${body}`\n )\n })\n return JSON.stringify({\n type: \"user\",\n message: {\n role: \"user\",\n content: [{ type: \"text\", text: sections.join(\"\\n\\n---\\n\\n\") }],\n },\n })\n}\n\nexport function extractPendingProxyResult(\n prompt: LanguageModelV3CallOptions[\"prompt\"],\n toolCallId: string,\n): ProxyToolResult | null {\n for (let i = prompt.length - 1; i >= 0; i--) {\n const msg = prompt[i]\n if (msg.role !== \"tool\" || !Array.isArray(msg.content)) continue\n\n for (const part of msg.content) {\n if (part.type !== \"tool-result\" || part.toolCallId !== toolCallId) continue\n\n const output = part.output as any\n if (!output || typeof output !== \"object\") {\n return {\n kind: \"text\",\n text: String(output ?? \"\"),\n }\n }\n\n if (output.type === \"text\") {\n return {\n kind: \"text\",\n text: String(output.value ?? \"\"),\n }\n }\n\n if (output.type === \"json\") {\n return {\n kind: \"text\",\n text: JSON.stringify(output.value),\n }\n }\n\n if (output.type === \"content\" && Array.isArray(output.value)) {\n const text = output.value\n .filter((v: any) => v?.type === \"text\" && typeof v.text === \"string\")\n .map((v: any) => v.text)\n .join(\"\\n\")\n return {\n kind: \"text\",\n text,\n }\n }\n\n return {\n kind: \"text\",\n text: JSON.stringify(output),\n }\n }\n }\n\n return null\n}\n\n/**\n * The result opencode produced for a pending proxy call, if the prompt\n * carries it. For `task_batch` that means every child's result gathered\n * back onto the parent: opencode runs the children in one step and hands\n * all their results to the next call together, so a partial set is not\n * expected. If it ever happens the batch still resolves, with the gap\n * named in the text, because leaving the parent pending would send this\n * turn down the fresh-envelope path and reject the call as orphaned.\n */\nexport function extractPendingProxyResultForCall(\n prompt: LanguageModelV3CallOptions[\"prompt\"],\n call: PendingProxyCall,\n): ProxyToolResult | null {\n if (call.toolName !== TASK_BATCH_TOOL_NAME) {\n return extractPendingProxyResult(prompt, call.toolCallId)\n }\n const tasks = taskBatchTasks(call.input)\n if (tasks.length === 0) {\n return { kind: \"error\", message: \"task_batch input is not a list of task objects\" }\n }\n const children = tasks.map((task, index) => ({\n task,\n result: extractPendingProxyResult(\n prompt,\n taskBatchChildToolCallId(call.toolCallId, index),\n ),\n }))\n const answered = children.filter((child) => child.result !== null).length\n if (answered === 0) return null\n if (answered < children.length) {\n log.warn(\"task_batch resolving with child results missing\", {\n toolCallId: call.toolCallId,\n answered,\n total: children.length,\n })\n }\n return formatTaskBatchResults(children)\n}\n","/**\n * Claude stream-json control requests: deciding allow or deny per tool, and\n * writing the matching `control_response` back down the child's stdin.\n *\n * Split out of `claude-code-language-model.ts` verbatim. The three were\n * private methods whose only use of `this` was `this.config`, so they take\n * the config as their first argument now and the class delegates to them.\n */\nimport type {\n ClaudeCodeConfig,\n ClaudeStreamMessage,\n ControlRequestBehavior,\n} from \"./types.js\"\nimport { denyMessageForTool, isAskUserQuestionTool } from \"./ask-user-question.js\"\nimport { log } from \"./logger.js\"\n\nexport function controlRequestBehaviorForTool(\n config: ClaudeCodeConfig,\n toolName: string,\n): ControlRequestBehavior {\n const configured = config.controlRequestToolBehaviors\n if (configured && toolName) {\n const direct = configured[toolName] ?? configured[toolName.toLowerCase()]\n if (direct === \"allow\" || direct === \"deny\") return direct\n\n const lower = toolName.toLowerCase()\n for (const [key, behavior] of Object.entries(configured)) {\n if (key.toLowerCase() === lower && (behavior === \"allow\" || behavior === \"deny\")) {\n return behavior\n }\n }\n }\n\n // AskUserQuestion must never be auto-allowed. Allowing it lets the\n // Claude CLI resolve its own question internally — in headless mode\n // there is no TTY, so the CLI fabricates/empties the answer and the\n // model proceeds on a guess. Deny so the CLI cannot self-answer; the\n // tool_use is still streamed and rendered to the opencode user by\n // formatAskUserQuestion, and the turn stops for a real reply. An\n // explicit controlRequestToolBehaviors entry above can still override.\n if (isAskUserQuestionTool(toolName)) return \"deny\"\n\n return config.controlRequestBehavior ?? \"allow\"\n}\n\nexport function writeControlResponse(\n proc: import(\"child_process\").ChildProcess,\n requestId: string,\n response?: Record<string, unknown>,\n): void {\n const payload = {\n type: \"control_response\",\n response: {\n subtype: \"success\",\n request_id: requestId,\n response,\n },\n }\n\n try {\n proc.stdin?.write(JSON.stringify(payload) + \"\\n\")\n } catch (error) {\n log.warn(\"failed to write control response\", {\n requestId,\n error: error instanceof Error ? error.message : String(error),\n })\n }\n}\n\n/**\n * Handle Claude stream-json control requests (`can_use_tool`, etc.) and\n * respond via stdin with a matching `control_response`.\n */\nexport function handleControlRequest(\n config: ClaudeCodeConfig,\n msg: ClaudeStreamMessage,\n proc: import(\"child_process\").ChildProcess,\n): boolean {\n if (msg.type !== \"control_request\") return false\n const requestId = msg.request_id\n const request = msg.request\n if (!requestId || !request?.subtype) return false\n\n if (request.subtype === \"can_use_tool\") {\n const toolName = request.tool_name ?? \"unknown\"\n const behavior = controlRequestBehaviorForTool(config, toolName)\n\n if (behavior === \"allow\") {\n writeControlResponse(proc, requestId, {\n behavior: \"allow\",\n updatedInput: request.input ?? {},\n toolUseID: request.tool_use_id,\n })\n log.info(\"control request auto-allowed\", {\n requestId,\n toolName,\n })\n } else {\n const denyMessage = denyMessageForTool(\n toolName,\n config.controlRequestDenyMessage,\n )\n writeControlResponse(proc, requestId, {\n behavior: \"deny\",\n message: denyMessage,\n toolUseID: request.tool_use_id,\n })\n log.info(\"control request auto-denied\", {\n requestId,\n toolName,\n })\n }\n\n return true\n }\n\n // For control request subtypes we don't actively handle yet, acknowledge\n // with an empty success so the CLI stream does not stall.\n writeControlResponse(proc, requestId, {})\n log.debug(\"control request acknowledged\", {\n requestId,\n subtype: request.subtype,\n })\n return true\n}\n","/**\n * Spawn planning: what a single `claude` spawn gets before it starts.\n * Which `--mcp-config` files, which proxy tool definitions, what opencode's\n * live tool registry says, which config dir the skill bridge reads, whether\n * the plan-mode approval bridge is live, and the proxy MCP server itself.\n *\n * Split out of `claude-code-language-model.ts` verbatim. These were private\n * methods whose only use of `this` was `this.config` (and `this.modelId` in\n * the registry fetch), so they take those as arguments now and the class\n * delegates to them with its original signatures.\n */\nimport type { ClaudeCodeConfig } from \"./types.js\"\nimport { shouldStripContextReminders } from \"./message-builder.js\"\nimport { accountConfigDirPath } from \"./accounts.js\"\nimport type { FailoverSpawn } from \"./account-failover.js\"\nimport { isPlanModeQuestionActive } from \"./plan-mode-question.js\"\nimport { bridgeOpencodeMcp, type RuntimeMcpStatus } from \"./mcp-bridge.js\"\nimport {\n fetchOpencodeToolList,\n type OpencodeToolListItem,\n} from \"./runtime-status.js\"\nimport { storeCompressionSummary } from \"./compression-store.js\"\nimport {\n createProxyMcpServer,\n resolveMcpProxyToolDefs,\n DEFAULT_PROXY_TOOLS,\n TASK_BATCH_TOOL_NAME,\n type McpProxyToolResolution,\n type ModelToolEntry,\n type ProxyMcpServer,\n type ProxyToolCall,\n type ProxyToolDef,\n type ProxyToolInterceptor,\n} from \"./proxy-mcp.js\"\nimport { queuePendingProxyCall } from \"./proxy-broker.js\"\nimport { log } from \"./logger.js\"\n\n/** One per-turn snapshot of opencode's live tool registry. */\nexport interface LiveToolInfo {\n /** False when nothing answered (no SDK client, fetch failed). */\n resolved: boolean\n taskDescription: string | undefined\n questionDescription: string | undefined\n hasQuestion: boolean\n /**\n * The raw registry entries behind the fields above, so `proxyOpencodeTools`\n * can be resolved from the same single fetch rather than a second one.\n */\n items?: OpencodeToolListItem[]\n}\n\n/**\n * Build the combined `--mcp-config` list and return both the list and the\n * hash of the bridged opencode MCP block (or null when bridging is off /\n * yields nothing). The hash is used to detect mid-session config changes\n * and respawn the underlying claude process.\n *\n * `runtimeStatus` is a snapshot of opencode's `client.mcp.status()`. When\n * provided it overlays opencode's UI-toggled state on top of disk config\n * so `/mcps` toggles propagate without a config file write.\n */\nexport function effectiveMcpConfig(\n config: ClaudeCodeConfig,\n cwd: string,\n proxyConfigPath?: string,\n runtimeStatus?: RuntimeMcpStatus,\n excludeServers?: ReadonlySet<string>,\n): {\n paths: string[]\n bridgedHash: string | null\n allEnabledServerNames: string[]\n} {\n const paths = Array.isArray(config.mcpConfig)\n ? config.mcpConfig.slice()\n : config.mcpConfig\n ? [config.mcpConfig]\n : []\n let bridgedHash: string | null = null\n let allEnabledServerNames: string[] = []\n if (config.bridgeOpencodeMcp !== false) {\n const bridged = bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers)\n if (bridged) {\n if (bridged.path) paths.push(bridged.path)\n bridgedHash = bridged.hash\n allEnabledServerNames = bridged.allEnabledServerNames\n }\n }\n if (proxyConfigPath) paths.push(proxyConfigPath)\n return { paths, bridgedHash, allEnabledServerNames }\n}\n\n/** Resolve ProxyToolDef[] for the configured proxyTools names. */\nexport function resolvedProxyTools(\n config: ClaudeCodeConfig,\n): ProxyToolDef[] | null {\n const names = config.proxyTools\n if (!names || names.length === 0) return null\n const defsByName = new Map(\n DEFAULT_PROXY_TOOLS.map((t) => [t.name.toLowerCase(), t]),\n )\n const picked: ProxyToolDef[] = []\n const seen = new Set<string>()\n const unknown: string[] = []\n const pick = (def: ProxyToolDef) => {\n if (seen.has(def.name)) return\n seen.add(def.name)\n picked.push(def)\n }\n for (const n of names) {\n const def = defsByName.get(String(n).toLowerCase())\n if (!def) {\n unknown.push(String(n))\n continue\n }\n pick(def)\n // `task_batch` rides along with `task`: it is the same dispatch path for\n // two or more subagents at once (TASK_BATCH_PROXY_NOTE), and a\n // `proxyTools` list that names `Task` should not have to know it exists.\n if (def.name === \"task\") {\n const batch = defsByName.get(TASK_BATCH_TOOL_NAME)\n if (batch) pick(batch)\n }\n }\n // A typo used to vanish here. Silence is the wrong response: unknown\n // names are not proxied, so the matching Claude built-in stays enabled\n // and unmediated, and if *every* name is unknown the whole turn runs\n // with no proxy at all (issue #26).\n if (unknown.length > 0) {\n const known = [...defsByName.keys()].join(\", \")\n if (picked.length === 0) {\n log.warn(\n \"no proxyTools entry was recognised; nothing will be proxied this turn\",\n { unknown, known },\n )\n } else {\n log.warn(\"ignoring unknown proxyTools entries\", { unknown, known })\n }\n }\n return picked.length > 0 ? picked : null\n}\n\n/**\n * Resolve ProxyToolDef[] for opencode's MCP-backed tools so they go\n * through the in-process proxy instead of being bridged into Claude CLI's\n * `--mcp-config`. Routing through the proxy keeps a single execution site\n * (opencode), so the call is permission-prompted and rendered as an\n * opencode tool call.\n *\n * Opt-in (`proxyOpencodeMcpTools: true`) and off by default. It used to\n * default to true while finding nothing, because it discovered tools via\n * `client.tool.list()`, which enumerates opencode's `ToolRegistry` and not\n * the MCP tools merged into the model's tool set afterwards. Discovery now\n * reads that merged set, the `tools` array opencode passes `doStream`, so\n * the option does what it says. Turning it on by default at the same time\n * would have silently moved every existing user's MCP traffic off the\n * working direct bridge, so the default went to false instead: today's\n * behaviour is preserved exactly and crossing over is the operator's call.\n *\n * Returns null when the feature is off or nothing matched, which leaves\n * every server on the direct bridge.\n */\nexport function resolvedProxyMcpTools(\n config: ClaudeCodeConfig,\n allEnabledServerNames: string[],\n modelTools: readonly ModelToolEntry[] | undefined,\n taken?: ReadonlySet<string>,\n): McpProxyToolResolution | null {\n if (config.proxyOpencodeMcpTools !== true) return null\n if (config.bridgeOpencodeMcp === false) return null\n if (allEnabledServerNames.length === 0) return null\n\n const resolution = resolveMcpProxyToolDefs({\n serverNames: allEnabledServerNames,\n tools: modelTools,\n taken,\n })\n if (resolution.defs.length === 0) {\n // WARN, not NOTICE: only warn and error are alwaysStderr in\n // src/logger.ts, so a NOTICE would be invisible to the very operator\n // who opted in and is entitled to know their MCP calls are still\n // going direct, and so still are not permission-prompted by opencode.\n log.warn(\n \"proxyOpencodeMcpTools is on but no MCP tool was found in opencode's\" +\n \" tool set; those servers stay on the direct bridge this spawn\",\n { servers: allEnabledServerNames, modelTools: modelTools?.length ?? 0 },\n )\n return null\n }\n log.debug(\"routing opencode MCP tools through the proxy\", {\n servers: [...resolution.coveredServers],\n tools: resolution.defs.map((def) => def.name),\n })\n return resolution\n}\n\n/**\n * Live tool info derived from a single `client.tool.list()` fetch:\n *\n * - `taskDescription`: opencode's `task` tool description exactly as the\n * registry renders it for native models, including the \"Available\n * agent types\" list. Overlaid onto the static `task` proxy def so\n * Claude sees the same subagent catalog native models see, instead\n * of hunting through config files.\n * - `questionDescription` / `hasQuestion`: opencode's `question` tool\n * description and whether the registry has the entry at all. Older\n * builds lack it, in which case a `mcp__opencode_proxy__question`\n * call resolves to `⚙ invalid`; the version gate drops the def.\n *\n * Returns undefined/false when the SDK client is unavailable (direct\n * AI-SDK use, tests) so the static defs stand. `resolved` distinguishes\n * \"the registry answered and has no `question` entry\" from \"nobody\n * answered\": only the former is a real version-gate signal.\n */\nexport async function fetchLiveToolInfo(\n config: ClaudeCodeConfig,\n modelId: string,\n): Promise<LiveToolInfo> {\n const items = await fetchOpencodeToolList(\n config.provider,\n modelId,\n config.cwd,\n )\n const question = items?.find((item) => item.id === \"question\")\n return {\n resolved: items !== undefined,\n taskDescription: items?.find((item) => item.id === \"task\")?.description,\n questionDescription: question?.description,\n hasQuestion: !!question,\n items,\n }\n}\n\n/**\n * Whether dcp-style context reminders should be stripped from this turn's\n * messages. Config-only and synchronous, so it can be answered before the\n * spawn block resolves anything: `userMsg` is built well ahead of it.\n */\nexport function stripContextRemindersEnabled(\n config: ClaudeCodeConfig,\n): boolean {\n return shouldStripContextReminders({\n enabled: config.stripContextReminders,\n proxyTools: config.proxyTools,\n proxyOpencodeTools: config.proxyOpencodeTools,\n })\n}\n\n/**\n * Arguments the skill bridge needs beyond `cwd` / `cliPath`: which\n * `CLAUDE_CONFIG_DIR` this spawn reads its native skills from, and whether\n * to drop the ones it already loads. A failover moves the spawn to another\n * account, and therefore to that account's config dir.\n */\nexport function skillBridgeSpawn(\n config: ClaudeCodeConfig,\n failover: FailoverSpawn,\n): {\n configDir: string | undefined\n skipNative: boolean\n} {\n return {\n configDir:\n failover.failedOver && failover.target\n ? accountConfigDirPath(failover.target)\n : config.configDir,\n skipNative: config.bridgeSkipNativeSkills !== false,\n }\n}\n\n/** Share one lazy registry request within a turn without making it stale. */\nexport function createLiveToolInfoLoader(\n config: ClaudeCodeConfig,\n modelId: string,\n): () => Promise<LiveToolInfo> {\n let pending: Promise<LiveToolInfo> | undefined\n return () => {\n pending ??= fetchLiveToolInfo(config, modelId)\n return pending\n }\n}\n\n/**\n * Whether the ExitPlanMode approval bridge is live for this turn: the\n * operator opted in AND opencode's registry actually has the `question`\n * tool. Without the registry entry the emitted tool-call would render as\n * `⚙ invalid` and wedge the turn, so the plugin keeps the text path.\n */\nexport async function resolvePlanModeQuestion(\n config: ClaudeCodeConfig,\n compactionMode: boolean,\n loadLiveToolInfo: () => Promise<LiveToolInfo>,\n): Promise<boolean> {\n if (compactionMode || config.planModeQuestion !== true) return false\n const info = await loadLiveToolInfo()\n const active = isPlanModeQuestionActive({\n configured: config.planModeQuestion,\n opencodeHasQuestion: info.hasQuestion,\n compactionMode,\n })\n if (!active) {\n // Same reasoning as the question proxy's version-gate log: a silent\n // fallback to the text path looks from the outside like the setting\n // was ignored.\n log.info(\"plan-mode question gate\", {\n opencodeHasQuestion: info.hasQuestion,\n registryResolved: info.resolved,\n active,\n })\n }\n return active\n}\n\n/**\n * Create a proxy MCP server for a single active Claude process/session.\n * The process lifecycle owns the server lifecycle via session-manager.\n */\nexport async function ensureProxyServer(\n config: ClaudeCodeConfig,\n tools: ProxyToolDef[],\n sessionKeyForCalls: string,\n // Whether the `compress` in `tools` is the PLUGIN's def rather than\n // opencode's forwarded one. Keying the interceptor on the name alone\n // would answer a forwarded `compress` in-process and opencode would\n // never see the call: the same name, the wrong tool, silently. The\n // caller knows which list the def came from, so it decides.\n interceptCompress: boolean,\n): Promise<ProxyMcpServer> {\n const timeoutOverrides = config.proxyToolTimeoutMs\n const interceptors = new Map<string, ProxyToolInterceptor>()\n if (interceptCompress && tools.some((t) => t.name === \"compress\")) {\n interceptors.set(\"compress\", (input) => {\n const summary = typeof input.summary === \"string\" ? input.summary.trim() : \"\"\n if (!summary) {\n return {\n kind: \"error\",\n message:\n \"compress needs a non-empty `summary`: it becomes the only\" +\n \" prior context after the reset. Nothing was compressed.\",\n }\n }\n storeCompressionSummary(sessionKeyForCalls, summary)\n log.info(\"compress stored summary; session resets next turn\", {\n sessionKey: sessionKeyForCalls,\n summaryLength: summary.length,\n })\n return {\n kind: \"text\",\n text:\n \"Summary stored. Finish this turn as normal; the next turn starts\" +\n \" a fresh Claude Code session with this summary as its only prior\" +\n \" context.\",\n }\n })\n }\n const srv = await createProxyMcpServer(tools, timeoutOverrides, interceptors)\n srv.calls.on(\"call\", (call: ProxyToolCall) => {\n queuePendingProxyCall(sessionKeyForCalls, call, timeoutOverrides)\n })\n return srv\n}\n","/**\n * Title requests: telling one apart from a real turn, and the synthetic\n * title the plugin answers it with instead of spawning `claude`.\n *\n * Split out of `claude-code-language-model.ts` verbatim. `isTitleRequest`\n * inlines what the class's `getOpencodeAgent` did, which was this exact\n * `resolveOpencodeAgent` call and nothing else, so the V2 short-circuit is\n * unchanged.\n */\nimport type { LanguageModelV3CallOptions } from \"@ai-sdk/provider\"\nimport type { ClaudeCodeConfig } from \"./types.js\"\nimport { resolveOpencodeAgent } from \"./call-options.js\"\n\n/**\n * Whether this call only names the session, which gets the synthetic stub\n * rather than a `claude` spawn. opencode 1.x sends a title request with no\n * tools, and that is the whole test there. opencode 2 sends its tool set\n * along with it (measured on 2.0.11: `scope: \"tools\"`, agent `title`), so\n * every new V2 session paid for a second `claude` process just to title\n * itself; for a V2 model the request kind, carried as the `title` agent,\n * decides instead.\n */\nexport function isTitleRequest(\n config: ClaudeCodeConfig,\n scope: \"tools\" | \"no-tools\",\n options: LanguageModelV3CallOptions,\n): boolean {\n if (scope === \"no-tools\") return true\n return (\n config.hostApi === \"v2\" &&\n resolveOpencodeAgent(\n (options as any)?.headers as Record<string, string | undefined> | undefined,\n options.providerOptions as Record<string, unknown> | undefined,\n config.provider,\n ) === \"title\"\n )\n}\n\nexport function requestScope(\n options: { tools?: unknown },\n): \"tools\" | \"no-tools\" {\n const tools = options?.tools\n if (Array.isArray(tools)) return \"tools\"\n if (tools && typeof tools === \"object\") {\n return Object.keys(tools as Record<string, unknown>).length > 0\n ? \"tools\"\n : \"no-tools\"\n }\n return \"no-tools\"\n}\n\nexport function latestUserText(\n prompt: LanguageModelV3CallOptions[\"prompt\"],\n): string {\n for (let i = prompt.length - 1; i >= 0; i--) {\n const msg = prompt[i]\n if (msg.role !== \"user\") continue\n\n if (typeof msg.content === \"string\") {\n return String(msg.content).trim()\n }\n\n if (Array.isArray(msg.content)) {\n const text = (msg.content as any[])\n .filter((part) => part.type === \"text\" && typeof part.text === \"string\")\n .map((part: any) => String(part.text).trim())\n .filter(Boolean)\n .join(\" \")\n if (text) return text\n }\n }\n\n return \"\"\n}\n\nexport function synthesizeTitle(\n prompt: LanguageModelV3CallOptions[\"prompt\"],\n): string {\n const source = latestUserText(prompt)\n .replace(/\\s+/g, \" \")\n .replace(/[^\\p{L}\\p{N}\\s-]/gu, \" \")\n .trim()\n\n if (!source) return \"New Session\"\n\n const stop = new Set([\n \"a\",\n \"an\",\n \"the\",\n \"and\",\n \"or\",\n \"but\",\n \"to\",\n \"for\",\n \"of\",\n \"in\",\n \"on\",\n \"at\",\n \"with\",\n \"can\",\n \"could\",\n \"would\",\n \"should\",\n \"please\",\n \"hi\",\n \"hello\",\n \"hey\",\n \"there\",\n \"you\",\n \"your\",\n \"this\",\n \"that\",\n \"is\",\n \"are\",\n \"was\",\n \"were\",\n \"be\",\n \"do\",\n \"does\",\n \"did\",\n \"summarize\",\n \"summary\",\n \"project\",\n ])\n\n const words = source\n .split(\" \")\n .map((word) => word.trim())\n .filter(Boolean)\n .filter((word) => !stop.has(word.toLowerCase()))\n\n const picked = (words.length > 0 ? words : source.split(\" \").filter(Boolean))\n .slice(0, 6)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \")\n\n return picked || \"New Session\"\n}\n","/**\n * Mapping the Claude CLI's own counters onto the AI SDK's usage and finish\n * shapes.\n *\n * Split out of `claude-code-language-model.ts` verbatim. Neither function\n * ever read `this`, so both became free functions unchanged and the class\n * keeps its `toUsage` / `toFinishReason` methods as one-line delegates (the\n * turn controller binds them).\n */\nimport type {\n LanguageModelV3FinishReason,\n LanguageModelV3Usage,\n} from \"@ai-sdk/provider\"\nimport type { ClaudeStreamMessage } from \"./types.js\"\n\nexport function toUsage(rawUsage?: ClaudeStreamMessage[\"usage\"]): LanguageModelV3Usage {\n // Prefer the last iteration's counters over cumulative totals.\n // CLI usage is the sum across all internal tool-use iterations;\n // using it directly inflates context size and triggers premature compaction.\n const iter = rawUsage?.iterations\n const effective = iter?.length ? iter[iter.length - 1] : rawUsage\n // Claude CLI reports input_tokens as non-cached input only.\n // OpenCode expects total = noCache + cacheRead + cacheWrite.\n const noCache = effective?.input_tokens ?? 0\n const cacheRead = effective?.cache_read_input_tokens ?? 0\n const cacheWrite = effective?.cache_creation_input_tokens ?? 0\n return {\n inputTokens: {\n total: noCache + cacheRead + cacheWrite,\n noCache,\n cacheRead: cacheRead || undefined,\n cacheWrite: cacheWrite || undefined,\n },\n outputTokens: {\n total: effective?.output_tokens,\n text: effective?.output_tokens,\n reasoning: undefined,\n },\n raw: rawUsage as any,\n }\n}\n\nexport function toFinishReason(\n reason: \"stop\" | \"tool-calls\" | \"error\" = \"stop\",\n): LanguageModelV3FinishReason {\n return {\n unified: reason,\n raw: reason,\n }\n}\n","// Removes a stale unscoped `opencode-claude-code-plugin` install left in\n// opencode's plugin cache by older configs. The unscoped name is a different\n// artifact than this scoped plugin and shadows it when both coexist.\n// Disable with OPENCODE_CLAUDE_CODE_PLUGIN_NO_CLEANUP=1.\n\nimport {\n existsSync,\n readFileSync,\n realpathSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { join, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\nimport { log } from \"./logger.js\"\n\nconst STALE_PACKAGE_NAME = \"opencode-claude-code-plugin\"\nconst SUSPECT_DESCRIPTION_TOKEN = \"Claude Code\"\n\nlet alreadyRan = false\n\nfunction candidateCacheRoots(): string[] {\n const xdg = process.env.XDG_CACHE_HOME\n return [\n xdg ? join(xdg, \"opencode\") : null,\n join(homedir(), \".cache\", \"opencode\"),\n join(homedir(), \"Library\", \"Caches\", \"opencode\"),\n ].filter((p): p is string => Boolean(p))\n}\n\nfunction userOpencodeJsonPath(): string {\n const xdgConfig = process.env.XDG_CONFIG_HOME ?? join(homedir(), \".config\")\n return join(xdgConfig, \"opencode\", \"opencode.json\")\n}\n\nfunction userIntendsToUseUnscoped(): boolean {\n const cfg = userOpencodeJsonPath()\n if (!existsSync(cfg)) return false\n try {\n const json = JSON.parse(readFileSync(cfg, \"utf8\"))\n const plugins: unknown = json.plugin\n if (!Array.isArray(plugins)) return false\n return plugins.some(\n (entry) =>\n typeof entry === \"string\" &&\n /^opencode-claude-code-plugin(@[^/]+)?$/.test(entry),\n )\n } catch {\n return false\n }\n}\n\nfunction ourLoadedDir(): string | null {\n try {\n const filePath = fileURLToPath(import.meta.url)\n return realpathSync(resolve(filePath, \"..\", \"..\"))\n } catch {\n return null\n }\n}\n\n/** Test seam: re-arms the once-per-process guard. */\nexport function _resetCleanupStaleState(): void {\n alreadyRan = false\n}\n\nexport function cleanupStaleUnscopedInstall(): void {\n if (alreadyRan) return\n alreadyRan = true\n\n if (process.env.OPENCODE_CLAUDE_CODE_PLUGIN_NO_CLEANUP === \"1\") return\n if (userIntendsToUseUnscoped()) return\n\n const ourDir = ourLoadedDir()\n\n for (const cacheRoot of candidateCacheRoots()) {\n try {\n cleanupOne(cacheRoot, ourDir)\n } catch (err) {\n log.warn(\"cleanup-stale: error processing cache root\", {\n cacheRoot,\n error: String(err),\n })\n }\n }\n}\n\nfunction cleanupOne(cacheRoot: string, ourDir: string | null): void {\n if (!existsSync(cacheRoot)) return\n\n const stalePath = join(cacheRoot, \"node_modules\", STALE_PACKAGE_NAME)\n if (!existsSync(stalePath)) return\n\n // Don't self-delete if we are the unscoped install.\n let realStalePath = stalePath\n try {\n realStalePath = realpathSync(stalePath)\n } catch {\n // ignore\n }\n if (ourDir && realStalePath === ourDir) return\n\n // Verify identity before removing.\n const pkgJsonPath = join(stalePath, \"package.json\")\n if (!existsSync(pkgJsonPath)) return\n let pkg: { name?: string; description?: string } = {}\n try {\n pkg = JSON.parse(readFileSync(pkgJsonPath, \"utf8\"))\n } catch {\n return\n }\n if (pkg.name !== STALE_PACKAGE_NAME) return\n if (!pkg.description?.includes(SUSPECT_DESCRIPTION_TOKEN)) return\n\n log.info(\"cleanup-stale: removing unscoped install\", { stalePath })\n try {\n rmSync(stalePath, { recursive: true, force: true })\n } catch (err) {\n log.warn(\"cleanup-stale: rmSync failed\", {\n stalePath,\n error: String(err),\n })\n return\n }\n\n // Drop the dep from the cache root's package.json so opencode's installer\n // doesn't reinstate it on its next pass. Lockfile is left alone; bun\n // reconciles against package.json on the next install.\n const cachePkgJson = join(cacheRoot, \"package.json\")\n if (!existsSync(cachePkgJson)) return\n try {\n const cfg = JSON.parse(readFileSync(cachePkgJson, \"utf8\"))\n if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {\n delete cfg.dependencies[STALE_PACKAGE_NAME]\n writeFileSync(cachePkgJson, JSON.stringify(cfg, null, 2) + \"\\n\")\n log.info(\"cleanup-stale: pruned dep from cache package.json\")\n }\n } catch (err) {\n log.warn(\"cleanup-stale: cache package.json update failed\", {\n error: String(err),\n })\n }\n}\n","import path from \"node:path\"\n\n/**\n * A V1-shaped opencode client over opencode 2's plugin context.\n *\n * Several features ask opencode's SDK client for live state at call time: the\n * MCP runtime overlay (`mcp.status`), the tool registry (`tool.list`, which\n * gates the `question`-based features and feeds the `task` description\n * overlay), and a session's directory and parent (`session.get`, for serve-mode\n * cwd and for keeping account failover out of subagents). Every call site\n * already degrades when there is no client, which is exactly what V2 looked\n * like before this: those features quietly switched off.\n *\n * Rather than teach each call site a second API, this answers the same calls\n * in V1's response shapes (`{ data }` envelopes, V1 field names) from V2's\n * domains. Shapes read off `@opencode/client@2.0.11` `generated/types.d.ts`.\n * Anything V2 cannot answer is left out, so its caller takes its existing\n * no-client path.\n */\n\nexport interface V2ClientContext {\n readonly mcp?: {\n list?: () => Promise<{ data?: Array<{ name?: unknown; status?: { status?: unknown } }> }>\n }\n readonly tool?: {\n list?: () => Promise<ReadonlyArray<{ id?: unknown; name?: unknown; description?: unknown }>>\n }\n readonly session?: {\n get?: (input: { sessionID: string }) => Promise<unknown>\n }\n readonly agent?: {\n list?: () => Promise<{\n data?: Array<{ id?: unknown; name?: unknown; description?: unknown; mode?: unknown; hidden?: unknown }>\n }>\n }\n}\n\n/**\n * The agent list V1 carries inside its `task` description and the `task`\n * proxy overlay extracts (`extractAgentTypeList`). V2's `subagent`\n * description has no such list (measured on 2.0.11: 596 characters, no agent\n * names), which left Claude guessing names, the exact failure AGENTS.md\n * records for V1. Built from `agent.list()`: agents a subagent call may use.\n */\nexport function formatAgentTypeList(\n agents: ReadonlyArray<{ id?: unknown; name?: unknown; description?: unknown; mode?: unknown; hidden?: unknown }>,\n): string | undefined {\n const lines = agents.flatMap((agent) => {\n if (agent.hidden === true || agent.mode === \"primary\") return []\n const name = typeof agent.id === \"string\" ? agent.id : agent.name\n if (typeof name !== \"string\" || name.length === 0) return []\n const blurb = typeof agent.description === \"string\" ? agent.description.trim() : \"\"\n return [`- ${name}: ${blurb || \"(no description)\"}`]\n })\n if (lines.length === 0) return undefined\n return `Available agent types and the tools they have access to:\\n${lines.join(\"\\n\")}`\n}\n\nfunction describe(value: unknown): string {\n if (typeof value === \"string\") return value\n // V2 tools may carry `description` as a thunk. Only a plain string result\n // is usable; anything else (an Effect, a throw) is treated as empty.\n if (typeof value === \"function\") {\n try {\n const result = (value as () => unknown)()\n return typeof result === \"string\" ? result : \"\"\n } catch {\n return \"\"\n }\n }\n return \"\"\n}\n\n/** V2's `SessionInfo` in V1's `Session` terms: `directory` and `parentID`. */\nexport function toV1Session(info: unknown): Record<string, unknown> | undefined {\n if (!info || typeof info !== \"object\") return undefined\n const session = info as {\n parentID?: unknown\n subpath?: unknown\n location?: { directory?: unknown }\n }\n const root = session.location?.directory\n const directory =\n typeof root === \"string\"\n ? typeof session.subpath === \"string\" && session.subpath.length > 0\n ? path.resolve(root, session.subpath)\n : root\n : undefined\n return { ...(info as Record<string, unknown>), directory, parentID: session.parentID }\n}\n\nexport function createV1ClientShim(ctx: V2ClientContext): Record<string, unknown> {\n const client: Record<string, unknown> = {}\n\n const mcpList = ctx.mcp?.list\n if (typeof mcpList === \"function\") {\n client.mcp = {\n // V1: `{ data: { [server]: { status } } }`. V2: `{ data: [{ name, status: { status } }] }`.\n status: async () => {\n const result = await mcpList.call(ctx.mcp)\n const data: Record<string, { status: string }> = {}\n for (const server of result?.data ?? []) {\n const status = server?.status?.status\n if (typeof server?.name === \"string\" && typeof status === \"string\") {\n data[server.name] = { status }\n }\n }\n return { data }\n },\n }\n }\n\n const toolList = ctx.tool?.list\n if (typeof toolList === \"function\") {\n client.tool = {\n // V1: `{ data: [{ id, description, parameters }] }`. V2 returns the tools\n // themselves, with an Effect schema for input rather than JSON Schema, so\n // `parameters` is empty; only `proxyOpencodeTools` reads it.\n list: async () => {\n const tools = await toolList.call(ctx.tool)\n const data = (tools ?? []).flatMap((tool) => {\n const id = typeof tool?.id === \"string\" ? tool.id : tool?.name\n return typeof id === \"string\"\n ? [{ id, description: describe(tool.description), parameters: {} }]\n : []\n })\n // The `task` proxy looks its live description up by V1's id; V2 calls\n // the same tool `subagent`, and its description names no agents.\n const subagent = data.find((tool) => tool.id === \"subagent\")\n if (subagent && !data.some((tool) => tool.id === \"task\")) {\n let agentList: string | undefined\n const agentApi = ctx.agent?.list\n if (typeof agentApi === \"function\") {\n try {\n agentList = formatAgentTypeList((await agentApi.call(ctx.agent))?.data ?? [])\n } catch {\n agentList = undefined\n }\n }\n const description = agentList\n ? `${subagent.description}\\n\\n${agentList}`\n : subagent.description\n data.push({ ...subagent, id: \"task\", description })\n }\n return { data }\n },\n }\n }\n\n const sessionGet = ctx.session?.get\n if (typeof sessionGet === \"function\") {\n client.session = {\n get: async (options: { path: { id: string } }) => ({\n data: toV1Session(await sessionGet.call(ctx.session, { sessionID: options.path.id })),\n }),\n }\n }\n\n return client\n}\n","import type { LanguageModelV3 } from \"@ai-sdk/provider\"\nimport {\n BASE_PROVIDER_ID,\n accountDisplayName,\n accountModelSuffix,\n accountProviderId,\n ensureAccountRuntime,\n resolveAccounts,\n} from \"./accounts.js\"\nimport { log } from \"./logger.js\"\nimport { defaultModels } from \"./models.js\"\nimport type { OpenCodeModel } from \"./opencode-types.js\"\nimport { BTW_COMMAND_DESCRIPTION } from \"./btw-command.js\"\nimport { DOCTOR_COMMAND, DOCTOR_COMMAND_DESCRIPTION } from \"./doctor.js\"\nimport type {\n V2Cleanup,\n V2CommandInvocation,\n V2Context,\n V2Delivery,\n V2Event,\n V2ModelInfo,\n V2ProviderInfo,\n V2Registration,\n V2RequestKind,\n} from \"./opencode-v2-types.js\"\nimport {\n isUsableDirectory,\n setOpencodeClient,\n setOpencodeProjectDirectory,\n} from \"./runtime-status.js\"\nimport { createV1ClientShim, type V2ClientContext } from \"./v2-client.js\"\nimport {\n deleteActiveProcessesForSession,\n ensureProcessExitCleanup,\n} from \"./session-manager.js\"\nimport { readBundledSkillInfos } from \"./skill-bridge.js\"\nimport { logStartupDiagnostics } from \"./startup-diagnostics.js\"\nimport type { ClaudeCodeProviderSettings } from \"./types.js\"\n\n/**\n * opencode 2.x entrypoint. V1 calls the default export's `server()`, V2 calls\n * its `setup(ctx)`; the two never run in the same process, and nothing here\n * translates one API into the other.\n *\n * The provider is split across two V2 surfaces where V1 had one hook:\n * `provider.transform` publishes the metadata (providers and models) and the\n * `aisdk` `sdk` hook hands opencode the object that builds the language model.\n * opencode never imports `package` itself: `AISDK.language` only checks that it\n * carries the `aisdk:` prefix and then asks the `sdk` hooks for the SDK, which\n * is how its own bundled providers work too. So the name below is a label, not\n * something that has to resolve.\n */\nexport const V2_PLUGIN_PACKAGE = \"aisdk:@khalilgharbaoui/opencode-claude-code-plugin\"\n\n/** The same header opencode 1.x sets itself, so the language model needs no V2 branch to read it. */\nexport const SESSION_AFFINITY_HEADER = \"x-session-affinity\"\n\n/**\n * V1 tags the opencode agent into `providerOptions` from `chat.params`. V2's\n * `model.request` hook can only set headers, so the agent travels as one.\n */\nexport const OPENCODE_AGENT_HEADER = \"x-opencode-agent\"\n\n// Keys the plugin itself consumes. They configure the provider set, not a\n// language model, so they are never passed to `createClaudeCode`.\nconst PLUGIN_ONLY_SETTINGS = [\"accounts\", \"defaultSubagentModel\"]\n\n// Keys opencode adds to the SDK options on its own (`prepareOptions` in the\n// V2 aisdk runtime). None of them means anything to the Claude CLI.\nconst OPENCODE_SDK_OPTION_KEYS = [\"fetch\", \"headers\", \"body\", \"name\"]\n\nexport function isClaudeCodeProviderId(providerID: string | undefined): boolean {\n if (typeof providerID !== \"string\") return false\n return providerID === BASE_PROVIDER_ID || providerID.startsWith(`${BASE_PROVIDER_ID}-`)\n}\n\n/**\n * The agent name the language model keys its special paths on. V2 tells the\n * request kind apart from the agent, so a compaction or title call arrives with\n * the session's ordinary agent; map the kind back onto the names V1 used, which\n * are what `isCompactionCall` and the title stub already test for.\n */\nexport function agentForRequest(kind: V2RequestKind, agent: string): string {\n if (kind === \"compaction\") return \"compaction\"\n if (kind === \"title\") return \"title\"\n return agent\n}\n\nfunction releasedAt(date: string | undefined): number {\n const ms = date ? Date.parse(date) : Number.NaN\n return Number.isFinite(ms) ? ms : 0\n}\n\nfunction modalities(flags: Record<string, boolean | undefined>): string[] {\n return Object.entries(flags)\n .filter(([, enabled]) => enabled === true)\n .map(([name]) => name)\n}\n\n/** One registry entry in V2's `Model.Info` shape. */\nexport function toV2Model(\n model: OpenCodeModel,\n providerID: string,\n modelId: string,\n): V2ModelInfo {\n return {\n id: modelId,\n modelID: modelId,\n providerID,\n name: model.name,\n family: model.family,\n capabilities: {\n tools: model.capabilities.toolcall,\n input: modalities(model.capabilities.input),\n output: modalities(model.capabilities.output),\n },\n variants: Object.entries(model.variants ?? {}).map(([id, settings]) => ({\n id,\n settings: { ...settings },\n })),\n time: { released: releasedAt(model.release_date) },\n cost: [\n {\n input: model.cost.input,\n output: model.cost.output,\n cache: { read: model.cost.cache.read, write: model.cost.cache.write },\n },\n ],\n status: model.status ?? \"active\",\n enabled: true,\n limit: { context: model.limit.context, output: model.limit.output },\n package: V2_PLUGIN_PACKAGE,\n }\n}\n\nexport function v2ModelsForProvider(providerID: string, modelSuffix?: string): V2ModelInfo[] {\n return Object.entries(defaultModels).map(([id, model]) =>\n toV2Model(model, providerID, modelSuffix ? `${id}@${modelSuffix}` : id),\n )\n}\n\nexport interface V2ProviderPlan {\n info: V2ProviderInfo\n models: V2ModelInfo[]\n}\n\nfunction stripPluginOnly(settings: Record<string, unknown> | undefined): Record<string, unknown> {\n const result = { ...(settings ?? {}) }\n for (const key of PLUGIN_ONLY_SETTINGS) delete result[key]\n return result\n}\n\n/**\n * The providers to publish, from the `claude-code` settings the operator\n * configured. Mirrors V1's config hook: no `accounts` means one `claude-code`\n * provider, and a list means one `claude-code-<account>` provider per entry\n * with the seed removed. The account wrapper script is not built here because\n * provider transforms are synchronous; `resolveSdkSettings` builds it when the\n * model is first asked for.\n */\nexport function planV2Providers(\n seedSettings: Record<string, unknown> | undefined,\n defaultProxyTools: readonly string[],\n): V2ProviderPlan[] {\n const base: Record<string, unknown> = {\n cliPath: \"claude\",\n proxyTools: [...defaultProxyTools],\n ...stripPluginOnly(seedSettings),\n }\n const accounts = resolveAccounts(seedSettings?.accounts)\n\n if (!accounts) {\n return [\n {\n info: {\n id: BASE_PROVIDER_ID,\n name: \"Claude Code\",\n activation: \"enabled\",\n package: V2_PLUGIN_PACKAGE,\n settings: { ...base, providerID: BASE_PROVIDER_ID },\n },\n models: v2ModelsForProvider(BASE_PROVIDER_ID),\n },\n ]\n }\n\n return accounts.map((account) => {\n const providerID = accountProviderId(account)\n return {\n info: {\n id: providerID,\n name: accountDisplayName(account),\n activation: \"enabled\",\n package: V2_PLUGIN_PACKAGE,\n settings: {\n ...base,\n account,\n // The resolved list, so this account's language model can offer the\n // others when it runs out of usage (src/account-failover.ts).\n failoverAccounts: accounts,\n providerID,\n // Kept separately because the wrapper replaces `cliPath`, and a\n // failover builds another account's wrapper on the same base.\n baseCliPath: String(base.cliPath ?? \"claude\"),\n },\n },\n models: v2ModelsForProvider(providerID, accountModelSuffix(account)),\n }\n })\n}\n\n/**\n * The settings `createClaudeCode` is called with. What opencode hands the hook\n * wins over the planned copy: by then its config plugin has merged the\n * operator's provider settings over ours, and model or variant settings sit on\n * top of that. The planned copy fills what opencode's lacks, which is\n * everything for an account provider the config never names. opencode's own\n * transport keys are removed first. An account provider's wrapper is built\n * here, once per SDK, since it writes a script to disk.\n */\nexport async function resolveSdkSettings(\n providerID: string,\n planned: Record<string, unknown> | undefined,\n eventOptions: Record<string, unknown>,\n): Promise<ClaudeCodeProviderSettings> {\n const fromEvent = { ...eventOptions }\n for (const key of OPENCODE_SDK_OPTION_KEYS) delete fromEvent[key]\n const merged: Record<string, unknown> = {\n ...stripPluginOnly({ ...(planned ?? {}), ...fromEvent }),\n providerID,\n // The only place a model is told it serves opencode 2.\n hostApi: \"v2\",\n }\n\n const account = typeof merged.account === \"string\" ? merged.account : undefined\n if (account) {\n const base = String(merged.baseCliPath ?? merged.cliPath ?? \"claude\")\n const runtime = await ensureAccountRuntime(account, base)\n Object.assign(merged, runtime, { baseCliPath: base })\n }\n\n return merged as ClaudeCodeProviderSettings\n}\n\nfunction plainObject(value: unknown): Record<string, unknown> | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined\n}\n\n/**\n * The `claude-code` settings the operator configured, as the provider set is\n * planned. This cannot come from the provider editor: opencode 2 applies the\n * config file's provider block through its own `opencode.config.provider`\n * plugin, after plugin transforms, so config overrides plugin defaults and a\n * plugin never sees it (measured on 2.0.11: `accounts` was absent from the\n * seed record). So it is read from the same on-disk layers opencode reads.\n *\n * Both config shapes are accepted, because V2 promises to keep reading V1\n * files: V1's `options` and V2's `settings`, the latter winning. The V2-native\n * home for plugin-level settings, the plugin's own `options` in the `plugins`\n * array, wins over both.\n */\nexport function configuredSeedSettings(\n config: Record<string, unknown>,\n pluginOptions: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n const provider = plainObject(plainObject(config.provider)?.[BASE_PROVIDER_ID])\n return {\n ...(plainObject(provider?.options) ?? {}),\n ...(plainObject(provider?.settings) ?? {}),\n ...(pluginOptions ?? {}),\n }\n}\n\n/**\n * Whether `setup` is really running inside opencode 2. opencode 1.18 calls a\n * dual export's `setup` as well as its `server` (measured on 1.18.32), with its\n * own smaller v2-compat context that has no `provider` or `session` domain.\n * Registering anything there would put a second provider path next to the one\n * `server()` already built, so a context without every domain we use, or one\n * that names a 1.x version, gets nothing.\n */\nexport function isOpencodeV2Context(ctx: unknown): ctx is V2Context {\n const context = plainObject(ctx)\n if (!context) return false\n const version = plainObject(context.app)?.version\n if (typeof version === \"string\") {\n const major = Number.parseInt(version.replace(/^v/, \"\"), 10)\n if (Number.isFinite(major) && major < 2) return false\n }\n const hasHook = (domain: unknown, name: \"transform\" | \"hook\") =>\n typeof plainObject(domain)?.[name] === \"function\"\n return (\n hasHook(context.provider, \"transform\") &&\n hasHook(context.aisdk, \"hook\") &&\n hasHook(context.session, \"hook\")\n )\n}\n\n/**\n * What a command sends as the session's next message: the same text V1's\n * config template produces (`/<name> $ARGUMENTS`), so the language model's\n * existing `/btw` and doctor branches answer it unchanged.\n */\nexport function commandPromptText(command: string, args: string | undefined): string {\n const rest = (args ?? \"\").trim()\n return rest ? `/${command} ${rest}` : `/${command}`\n}\n\n/** The session a V2 `session.deleted` event names (`data.sessionID`). */\nexport function deletedSessionIdV2(event: V2Event | undefined): string | undefined {\n if (!event || event.type !== \"session.deleted\") return undefined\n const id = event.data?.sessionID\n return typeof id === \"string\" && id.length > 0 ? id : undefined\n}\n\nasync function releaseDeletedSessions(\n subscribe: (options?: { signal?: AbortSignal }) => AsyncIterable<V2Event>,\n signal: AbortSignal,\n): Promise<void> {\n try {\n for await (const event of subscribe({ signal })) {\n if (signal.aborted) break\n const sessionID = deletedSessionIdV2(event)\n if (!sessionID) continue\n const released = deleteActiveProcessesForSession(sessionID)\n if (released.length > 0) {\n log.info(\"released claude state for deleted session\", { sessionID, released })\n }\n }\n } catch (err) {\n if (!signal.aborted) {\n log.warn(\"opencode 2 event subscription ended; deleted sessions keep their claude process until idle eviction\", {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n}\n\nexport interface V2SetupDeps {\n /** `createClaudeCode`, passed in so this module never imports the entrypoint. */\n createProvider: (settings: ClaudeCodeProviderSettings) => {\n languageModel(modelId: string): LanguageModelV3\n }\n defaultProxyTools: readonly string[]\n /** The merged on-disk opencode config for a directory (`loadMergedOpencodeConfig`). */\n loadConfig: (directory: string) => Record<string, unknown>\n /**\n * V1's registry builder, reused so the same agents are eligible for\n * `forceModel`, `defaultSubagentModel` and `reasoningEffort` on both majors:\n * markdown agents on disk plus the config's `agent` block. Not V2's\n * `agent.list()`, which includes opencode's built-ins; those must never be\n * rewritten (src/agent-models.ts).\n */\n buildAgentRegistry?: (config: Record<string, unknown>) => Promise<void>\n}\n\nexport function createV2Setup(deps: V2SetupDeps): (ctx: V2Context) => Promise<V2Cleanup> {\n return async (ctx) => {\n if (!isOpencodeV2Context(ctx)) {\n log.debug(\"setup called outside opencode 2; leaving it to server()\", {\n version: (ctx as { app?: { version?: unknown } } | undefined)?.app?.version,\n })\n return () => undefined\n }\n ensureProcessExitCleanup()\n // The live-state calls (MCP status, tool registry, session lookups) are\n // answered in V1's shapes, so their callers need no V2 branch.\n setOpencodeClient(createV1ClientShim(ctx as unknown as V2ClientContext))\n const directory = ctx.location?.directory\n setOpencodeProjectDirectory(isUsableDirectory(directory) ? directory : undefined)\n\n if (deps.buildAgentRegistry) {\n try {\n const config = deps.loadConfig(directory ?? process.cwd())\n // V1's builder reads `provider.claude-code.options`; hand it the seed\n // settings in that slot whichever config shape they came from.\n await deps.buildAgentRegistry({\n ...config,\n provider: {\n ...(plainObject(config.provider) ?? {}),\n [BASE_PROVIDER_ID]: {\n options: configuredSeedSettings(config, plainObject(ctx.options)),\n },\n },\n })\n } catch (err) {\n log.warn(\"failed to build the agent registry for opencode 2\", {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n const planned = new Map<string, Record<string, unknown>>()\n const registrations: V2Registration[] = []\n\n registrations.push(\n await ctx.provider.transform((editor) => {\n const seed = editor.get(BASE_PROVIDER_ID)\n // Whatever the editor already holds is plugin-level (ours from an\n // earlier pass, or another plugin's); the operator's config wins.\n const seedSettings = {\n ...(seed?.provider.settings ?? {}),\n ...configuredSeedSettings(\n deps.loadConfig(directory ?? process.cwd()),\n plainObject(ctx.options),\n ),\n }\n const plans = planV2Providers(seedSettings, deps.defaultProxyTools)\n planned.clear()\n\n for (const plan of plans) {\n planned.set(plan.info.id, plan.info.settings ?? {})\n if (editor.get(plan.info.id)) {\n editor.update(plan.info.id, (provider) => {\n provider.name = plan.info.name\n provider.activation = plan.info.activation\n provider.package = plan.info.package\n provider.settings = plan.info.settings\n })\n editor.models.set(plan.info.id, plan.models)\n } else {\n editor.add({ info: plan.info, models: plan.models })\n }\n }\n\n // An account expansion replaces the seed, exactly as V1 deletes it.\n if (seed && !plans.some((plan) => plan.info.id === BASE_PROVIDER_ID)) {\n editor.remove(BASE_PROVIDER_ID)\n }\n\n logStartupDiagnostics(\n Object.fromEntries(\n plans.map((plan) => [plan.info.id, { name: plan.info.name, options: plan.info.settings }]),\n ),\n ctx.app?.version,\n )\n }),\n )\n\n registrations.push(\n await ctx.aisdk.hook(\"sdk\", async (event) => {\n const providerID = event.model.providerID\n if (!isClaudeCodeProviderId(providerID)) return\n const settings = await resolveSdkSettings(providerID, planned.get(providerID), event.options)\n event.sdk = deps.createProvider(settings)\n log.debug(\"v2 sdk created\", { providerID, cliPath: settings.cliPath })\n }),\n )\n\n registrations.push(\n await ctx.session.hook(\"model.request\", (event) => {\n if (!isClaudeCodeProviderId(event.model.providerID)) return\n event.headers[SESSION_AFFINITY_HEADER] = event.sessionID\n event.headers[OPENCODE_AGENT_HEADER] = agentForRequest(event.kind, event.agent)\n }),\n )\n\n // `/btw` and `/claude-code-doctor`. V1 injects templates into the user's\n // config and has to guard against clobbering a command of the same name;\n // here they are real commands, and the config's own commands are applied\n // after plugin transforms, so a user-defined one still wins.\n const prompt = ctx.session.prompt\n if (ctx.command?.transform && typeof prompt === \"function\") {\n const send = (\n input: V2CommandInvocation,\n command: string,\n delivery: V2Delivery,\n ): Promise<void> =>\n prompt\n .call(ctx.session, {\n ...input.prompt,\n sessionID: input.sessionID,\n text: commandPromptText(command, input.prompt.text),\n delivery,\n })\n .then(() => undefined)\n registrations.push(\n await ctx.command.transform((editor) => {\n editor.add({\n name: DOCTOR_COMMAND,\n description: DOCTOR_COMMAND_DESCRIPTION,\n execute: (input) => send(input, DOCTOR_COMMAND, input.delivery),\n })\n // Always queued. A `/btw` steered into a running turn becomes that\n // turn's next step, the one carrying the results of the tools opencode\n // just ran, and answering the aside there swallows the continuation\n // (the failure V1's hold exists to prevent). Queued, the aside branch\n // answers it from the idle process once the turn is over. V1's\n // answer-inside-the-running-turn needs a session-status route V2's\n // plugin session domain does not offer.\n editor.add({\n name: \"btw\",\n description: BTW_COMMAND_DESCRIPTION,\n execute: (input) => send(input, \"btw\", \"queue\"),\n })\n }),\n )\n }\n\n // The bundled `claude-code-plugin` skill, listed by opencode for every\n // provider. V1 adds its directory to the config's `skills.paths`; V2 has no\n // config hook, so it is registered directly. A skill of the same id that\n // opencode already found (the operator's own copy) is left alone.\n const bundledSkills = readBundledSkillInfos()\n if (typeof ctx.skill?.transform === \"function\" && bundledSkills.length > 0) {\n registrations.push(\n await ctx.skill.transform((editor) => {\n const added: string[] = []\n for (const skill of bundledSkills) {\n if (editor.get(skill.id)) continue\n editor.add(skill)\n added.push(skill.id)\n }\n log.debug(\"v2 bundled skills registered\", { added })\n }),\n )\n }\n\n // Release a deleted session's `claude` child, proxy server and session id\n // at once, as V1's `event` hook does.\n const events = new AbortController()\n if (typeof ctx.event?.subscribe === \"function\") {\n void releaseDeletedSessions(ctx.event.subscribe.bind(ctx.event), events.signal)\n }\n\n log.info(\"claude-code plugin set up for opencode 2\", {\n opencode: ctx.app?.version,\n directory,\n })\n\n return async () => {\n events.abort()\n for (const registration of registrations.splice(0)) {\n await registration.dispose().catch(() => undefined)\n }\n }\n }\n}\n","import type { LanguageModelV3 } from \"@ai-sdk/provider\"\nimport { ClaudeCodeLanguageModel } from \"./claude-code-language-model.js\"\nimport { defaultModels, toConfigModel } from \"./models.js\"\nimport type {\n OpenCodeConfig,\n OpenCodeEvent,\n OpenCodeModel,\n OpenCodePlugin,\n OpenCodeProvider,\n} from \"./opencode-types.js\"\nimport type { ClaudeCodeProviderSettings } from \"./types.js\"\nimport {\n BASE_PROVIDER_ID,\n accountDisplayName,\n accountModelSuffix,\n accountProviderId,\n ensureAccountRuntime,\n resolveAccounts,\n} from \"./accounts.js\"\nimport {\n type AgentRecord,\n agentDirectories,\n getDefaultSubagentModel,\n readAgentMarkdownRecords,\n setAgentRegistry,\n setDefaultSubagentModel,\n} from \"./agent-models.js\"\nimport { cleanupStaleUnscopedInstall } from \"./cleanup-stale.js\"\nimport { DOCTOR_COMMAND, DOCTOR_COMMAND_DESCRIPTION } from \"./doctor.js\"\nimport { configureLogger, log } from \"./logger.js\"\nimport {\n BTW_COMMAND_DESCRIPTION,\n handleBtwCommand,\n type BtwSdkClient,\n} from \"./btw-command.js\"\nimport { registerBundledSkillPath } from \"./skill-bridge.js\"\nimport {\n deleteActiveProcessesForSession,\n ensureProcessExitCleanup,\n} from \"./session-manager.js\"\nimport { getOpencodeClient } from \"./runtime-status.js\"\nimport {\n getOpencodeProjectDirectory,\n isUsableDirectory,\n setOpencodeClient,\n setOpencodeProjectDirectory,\n} from \"./runtime-status.js\"\nimport {\n logStartupDiagnostics,\n pickOpencodeVersion,\n type DiagnosticsProviderEntry,\n} from \"./startup-diagnostics.js\"\nimport { loadMergedOpencodeConfig } from \"./mcp-bridge.js\"\nimport { createV2Setup } from \"./v2.js\"\n\nexport interface ClaudeCodeProvider {\n specificationVersion: \"v3\"\n (modelId: string): LanguageModelV3\n languageModel(modelId: string): LanguageModelV3\n}\n\n// Picks the best directory from opencode's plugin context (`directory` /\n// `worktree`). Result is handed to runtime-status so it's available as a\n// *fallback* at spawn time only when `process.cwd()` is unusable (macOS\n// GUI launches at `/`). Never baked into provider config — see #4.\nfunction pickOpencodeDirectory(input: unknown): string | undefined {\n if (!input || typeof input !== \"object\") return undefined\n const ctx = input as { directory?: unknown; worktree?: unknown }\n if (isUsableDirectory(ctx.directory)) return ctx.directory\n if (isUsableDirectory(ctx.worktree)) return ctx.worktree\n return undefined\n}\n\nlet warnedAnthropicApiKey = false\nlet warnedPlanModeNoExit = false\n\n// `Question` is deliberately absent: enabling it disables Claude Code's\n// built-in AskUserQuestion (via --disallowedTools) and replaces the\n// stop-and-wait deny/markdown path with an in-turn blocking form. That is a\n// behavior trade against the issue-#8 guarantee, so it stays opt-in until it\n// has the same live mileage Task had before v0.10.0 flipped it on. Users opt\n// in by listing it in `proxyTools`; see README \"Question proxy tool\".\nexport const DEFAULT_PROXY_TOOL_NAMES = [\n \"Bash\",\n \"Edit\",\n \"Write\",\n \"WebFetch\",\n \"Task\",\n]\n\n/**\n * Registers `/btw` unless the user defined their own. Returns whether the\n * registration is ours: the command hook only intercepts `btw` in that case,\n * so a user-defined command keeps opencode's normal behaviour end to end.\n */\nexport function registerSideQuestionCommand(config: OpenCodeConfig): boolean {\n config.command ??= {}\n if (config.command.btw) return false\n config.command.btw = {\n template: \"/btw $ARGUMENTS\",\n description: BTW_COMMAND_DESCRIPTION,\n }\n return true\n}\n\n/**\n * Registers `/claude-code-doctor` unless the user defined their own command of\n * that name. Unlike `/btw` there is no hook to guard: the command is a plain\n * template and the language model answers the message it produces, so leaving\n * a user definition alone here is the whole guard.\n *\n * The name carries no slash. opencode invokes a command as `/<key>` and takes\n * everything after the first space as `$ARGUMENTS`, so `claude-code doctor`\n * would be the command `claude-code` with the argument `doctor`.\n */\nexport function registerDoctorCommand(config: OpenCodeConfig): boolean {\n config.command ??= {}\n if (config.command[DOCTOR_COMMAND]) return false\n config.command[DOCTOR_COMMAND] = {\n template: `/${DOCTOR_COMMAND} $ARGUMENTS`,\n description: DOCTOR_COMMAND_DESCRIPTION,\n }\n return true\n}\n\nlet ownsSideQuestionCommand = false\n\n// One-time heads-up: an API key in the environment makes Claude Code bill\n// pay-as-you-go (Console) instead of the logged-in Pro/Max subscription, which\n// silently bypasses the Agent SDK plan credit. Surfaced once per process.\nfunction warnIfAnthropicApiKey(ignore: boolean | undefined): void {\n if (warnedAnthropicApiKey) return\n if (!process.env.ANTHROPIC_API_KEY && !process.env.ANTHROPIC_AUTH_TOKEN) return\n warnedAnthropicApiKey = true\n if (ignore) {\n log.warn(\n \"ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN detected; stripping it from claude spawns (ignoreAnthropicApiKey) so requests use your subscription auth, not pay-as-you-go API billing.\",\n )\n } else {\n log.warn(\n \"ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN detected; claude may bill as pay-as-you-go API usage instead of your subscription / Agent SDK credit. Set provider option `ignoreAnthropicApiKey: true` to force subscription auth.\",\n )\n }\n}\n\n// Plan mode is enforced (buildCliArgs drops the skip-permissions flag for it),\n// so the read-only guarantee holds. The cost is that headless Claude Code is\n// not offered an `ExitPlanMode` tool, measured on 2.1.258, so nothing can\n// release plan mode mid-session and approving a plan in chat will not let\n// Claude write. Say so once per process rather than let it look like a hang.\nexport function _resetPlanModeWarningForTests(): void {\n warnedPlanModeNoExit = false\n}\n\nexport function warnIfPlanModeCannotExit(permissionMode: string | undefined): void {\n if (permissionMode !== \"plan\") return\n if (warnedPlanModeNoExit) return\n warnedPlanModeNoExit = true\n log.warn(\n \"permissionMode \\\"plan\\\" is enforced: claude cannot edit files or run commands, and --dangerously-skip-permissions is deliberately not passed so it stays that way. Headless Claude Code is not offered an ExitPlanMode tool, so nothing releases plan mode mid-session; approving a plan in chat does not unlock writes. Leaving plan mode means changing the config and restarting opencode.\",\n { permissionMode, measuredOn: \"claude-code 2.1.258\" },\n )\n}\n\nexport function createClaudeCode(\n settings: ClaudeCodeProviderSettings = {},\n): ClaudeCodeProvider {\n if (settings.logging) {\n configureLogger({\n file: settings.logging.file ?? false,\n dir: settings.logging.dir ?? null,\n mode: settings.logging.mode ?? \"silent\",\n level: settings.logging.level ?? \"info\",\n })\n }\n warnIfAnthropicApiKey(settings.ignoreAnthropicApiKey)\n warnIfPlanModeCannotExit(settings.permissionMode)\n const cliPath =\n settings.cliPath ?? process.env.CLAUDE_CLI_PATH ?? \"claude\"\n const providerName = settings.providerID ?? settings.name ?? \"claude-code\"\n const proxyTools = settings.proxyTools ?? [...DEFAULT_PROXY_TOOL_NAMES]\n\n const createModel = (modelId: string): LanguageModelV3 => {\n return new ClaudeCodeLanguageModel(modelId, {\n provider: providerName,\n cliPath,\n hostApi: settings.hostApi,\n cwd: settings.cwd,\n account: settings.account,\n configDir: settings.configDir,\n failoverAccounts: settings.failoverAccounts,\n baseCliPath: settings.baseCliPath ?? cliPath,\n accountFailover: settings.accountFailover ?? \"ask\",\n providerID: settings.providerID,\n skipPermissions: settings.skipPermissions ?? true,\n permissionMode: settings.permissionMode,\n mcpConfig: settings.mcpConfig,\n strictMcpConfig: settings.strictMcpConfig,\n bridgeOpencodeMcp: settings.bridgeOpencodeMcp ?? true,\n controlRequestBehavior: settings.controlRequestBehavior ?? \"allow\",\n controlRequestToolBehaviors: settings.controlRequestToolBehaviors,\n controlRequestDenyMessage: settings.controlRequestDenyMessage,\n proxyTools,\n proxyOpencodeTools: settings.proxyOpencodeTools,\n stripContextReminders: settings.stripContextReminders === true,\n extraDisallowedTools: settings.extraDisallowedTools,\n proxyToolTimeoutMs: settings.proxyToolTimeoutMs,\n planModeQuestion: settings.planModeQuestion ?? false,\n webSearch: settings.webSearch,\n hotReloadMcp: settings.hotReloadMcp ?? true,\n proxyOpencodeMcpTools: settings.proxyOpencodeMcpTools === true,\n multiStepContinuation: settings.multiStepContinuation ?? true,\n autoContinueIncompleteTurns:\n settings.autoContinueIncompleteTurns ?? \"smart\",\n compactionModel: settings.compactionModel,\n ignoreAnthropicApiKey: settings.ignoreAnthropicApiKey,\n idleProcessTimeoutMs: settings.idleProcessTimeoutMs,\n bridgeOpencodeSkills: settings.bridgeOpencodeSkills === true,\n bridgeSkipNativeSkills: settings.bridgeSkipNativeSkills !== false,\n turnStats: settings.turnStats === true,\n interactive: settings.interactive,\n interactiveBypass: settings.interactiveBypass,\n interactiveAllowTools: settings.interactiveAllowTools,\n interactiveSystemPrompt: settings.interactiveSystemPrompt,\n })\n }\n\n const provider = function (modelId: string) {\n return createModel(modelId)\n } as ClaudeCodeProvider\n\n provider.specificationVersion = \"v3\"\n provider.languageModel = createModel\n\n return provider\n}\n\n// ---------------------------------------------------------------------------\n// OpenCode plugin interface\n// ---------------------------------------------------------------------------\n\nconst PROVIDER_ID = BASE_PROVIDER_ID\nconst PACKAGE_NPM = \"@khalilgharbaoui/opencode-claude-code-plugin\"\n\nfunction pluginEntrypoint(): string {\n return import.meta.url.startsWith(\"file:\") ? import.meta.url : PACKAGE_NPM\n}\n\nfunction cleanProviderOptions(\n options: Record<string, unknown> = {},\n): Record<string, unknown> {\n const result = { ...options }\n delete result.accounts\n // Consumed by the config hook (agent registry), not by the language model.\n delete result.defaultSubagentModel\n return result\n}\n\nfunction defaultModelsForProvider(\n providerModels: OpenCodeProvider[\"models\"],\n providerID = PROVIDER_ID,\n modelSuffix?: string,\n) {\n const models = Object.fromEntries(\n Object.entries(defaultModels).map(([id, model]) => {\n const modelId = modelSuffix ? `${id}@${modelSuffix}` : id\n const existing = providerModels[id] ?? providerModels[modelId]\n return [\n modelId,\n {\n ...model,\n id: modelId,\n providerID,\n api: {\n ...model.api,\n id: modelId,\n npm: existing?.api?.npm ?? model.api.npm,\n url: existing?.api?.url ?? model.api.url,\n },\n },\n ]\n }),\n )\n\n for (const [id, model] of Object.entries(providerModels)) {\n if (!(id in models)) {\n models[id] = {\n ...model,\n providerID,\n }\n }\n }\n\n return models\n}\n\n/**\n * Build models in OpenCode's config schema format (flat properties like\n * `temperature`, `reasoning`, `cost.cache_read`, `modalities`, etc.)\n * so the config-path provider loader parses them correctly.\n */\nexport function configModelsForProvider(\n providerModels: OpenCodeProvider[\"models\"],\n providerID: string,\n modelSuffix?: string,\n): Record<string, Record<string, unknown>> {\n const models: Record<string, Record<string, unknown>> = {}\n\n for (const [id, model] of Object.entries(defaultModels)) {\n const modelId = modelSuffix ? `${id}@${modelSuffix}` : id\n const existing = providerModels[id] ?? providerModels[modelId]\n const existingVariants =\n existing && typeof (existing as { variants?: unknown }).variants === \"object\"\n ? ((existing as { variants?: Record<string, Record<string, unknown>> }).variants ?? {})\n : {}\n const full: OpenCodeModel = {\n ...model,\n id: modelId,\n providerID,\n api: {\n ...model.api,\n id: modelId,\n npm: existing?.api?.npm ?? model.api.npm,\n url: existing?.api?.url ?? model.api.url,\n },\n variants: {\n ...(model.variants ?? {}),\n ...existingVariants,\n },\n }\n models[modelId] = toConfigModel(full)\n }\n\n for (const [id, model] of Object.entries(providerModels)) {\n if (!(id in models)) {\n models[id] = toConfigModel({ ...model, providerID } as OpenCodeModel)\n }\n }\n\n return models\n}\n\nasync function providerConfig(\n existing: {\n name?: string\n npm?: string\n options?: Record<string, unknown>\n models?: Record<string, unknown>\n } | undefined,\n providerID = PROVIDER_ID,\n optionDefaults: Record<string, unknown> = {},\n displayName?: string,\n) {\n const mergedOptions: Record<string, unknown> = {\n cliPath: \"claude\",\n proxyTools: [...DEFAULT_PROXY_TOOL_NAMES],\n ...optionDefaults,\n ...cleanProviderOptions(existing?.options),\n providerID,\n }\n\n const cliPath = String(mergedOptions.cliPath ?? \"claude\")\n const account =\n typeof mergedOptions.account === \"string\" ? mergedOptions.account : undefined\n const runtime = account\n ? await ensureAccountRuntime(account, cliPath)\n : { cliPath }\n\n return {\n name: displayName ?? existing?.name,\n npm: existing?.npm ?? pluginEntrypoint(),\n options: {\n ...mergedOptions,\n ...runtime,\n // The pre-wrapper binary, kept because `runtime` replaces `cliPath`\n // with the account's wrapper and a failover has to build ANOTHER\n // account's wrapper on top of the same base (src/account-failover.ts).\n baseCliPath: cliPath,\n },\n // models is intentionally omitted: both callers overwrite it with\n // configModelsForProvider(), which emits the flat config schema\n // opencode's config-path loader parses (and merges user variants).\n }\n}\n\n/**\n * Narrow opencode's full provider map down to the ones this plugin owns\n * (`claude-code` plus every `claude-code-<account>` expansion) so startup\n * diagnostics never report another provider's options.\n */\nexport function claudeCodeProviders(\n providers: Record<string, DiagnosticsProviderEntry> | undefined,\n): Record<string, DiagnosticsProviderEntry> {\n const out: Record<string, DiagnosticsProviderEntry> = {}\n for (const [id, entry] of Object.entries(providers ?? {})) {\n if (id === PROVIDER_ID || id.startsWith(`${PROVIDER_ID}-`)) out[id] = entry\n }\n return out\n}\n\nasync function expandAccountProviders(config: {\n provider?: Record<\n string,\n {\n name?: string\n npm?: string\n options?: Record<string, unknown>\n models?: Record<string, unknown>\n }\n >\n}): Promise<boolean> {\n const seed = config.provider?.[PROVIDER_ID]\n const accounts = resolveAccounts(seed?.options?.accounts)\n\n if (!accounts) return false\n\n config.provider ??= {}\n\n const seedOptions = cleanProviderOptions(seed?.options)\n let expandedCount = 0\n\n for (const account of accounts) {\n const providerID = accountProviderId(account)\n try {\n const existing = config.provider[providerID]\n const modelSuffix = accountModelSuffix(account)\n\n config.provider[providerID] = {\n ...existing,\n ...(await providerConfig(\n existing,\n providerID,\n {\n ...seedOptions,\n account,\n // The resolved list, so this account's language model can offer\n // the others when it runs out of usage. `accounts` itself stays\n // stripped by cleanProviderOptions.\n failoverAccounts: accounts,\n },\n accountDisplayName(account),\n )),\n models: configModelsForProvider(\n (existing?.models ?? seed?.models ?? {}) as OpenCodeProvider[\"models\"],\n providerID,\n modelSuffix,\n ),\n }\n expandedCount++\n } catch (err) {\n log.error(\"failed to expand account provider\", {\n account,\n providerID,\n error: String(err),\n })\n }\n }\n\n if (expandedCount > 0) {\n delete config.provider[PROVIDER_ID]\n }\n\n return expandedCount > 0\n}\n\n/**\n * Record what every known agent asked for, so `resolveAgentModel` and\n * `resolveAgentEffort` can answer at spawn time without the language model\n * needing to see opencode's config.\n *\n * Runs BEFORE `expandAccountProviders`, which deletes the seed provider entry\n * once it has expanded it: `defaultSubagentModel` has to be read while it is\n * still there.\n *\n * Purely observational. It defines no agents and changes no agent's config;\n * an agent this plugin never heard of is simply absent from the registry,\n * which is what keeps opencode's built-ins out of the override path.\n */\nexport async function buildAgentRegistry(config: OpenCodeConfig): Promise<void> {\n const options = config.provider?.[PROVIDER_ID]?.options\n const configured = options?.defaultSubagentModel\n setDefaultSubagentModel(\n typeof configured === \"string\" ? configured : undefined,\n )\n\n // Markdown agents may or may not reach a plugin's config hook (undocumented\n // either way), so they are read from disk and then overlaid with whatever\n // config does carry, which is authoritative when both describe one agent.\n const records: Record<string, AgentRecord> = await readAgentMarkdownRecords(\n agentDirectories(\n process.env.HOME ?? process.env.USERPROFILE,\n getOpencodeProjectDirectory(),\n ),\n )\n\n for (const [name, agent] of Object.entries(config.agent ?? {})) {\n const bag = (agent.options ?? {}) as Record<string, unknown>\n const pick = (key: string): string | undefined => {\n const value = agent[key] ?? bag[key]\n return typeof value === \"string\" ? value : undefined\n }\n\n records[name] = {\n mode: pick(\"mode\") ?? records[name]?.mode,\n model: pick(\"model\") ?? records[name]?.model,\n forceModel: pick(\"forceModel\") ?? records[name]?.forceModel,\n reasoningEffort:\n pick(\"reasoningEffort\") ?? records[name]?.reasoningEffort,\n }\n }\n\n setAgentRegistry(records)\n log.debug(\"agent registry built\", {\n agents: Object.keys(records).length,\n defaultSubagentModel: getDefaultSubagentModel(),\n })\n}\n\n/**\n * The opencode session id a `session.deleted` bus event names, or undefined\n * for any other event. opencode publishes `{ type, properties: { info } }`\n * under `payload`, and the deleted session's own record is `properties.info`.\n */\nexport function extractDeletedSessionId(event: OpenCodeEvent | undefined): string | undefined {\n const payload = event?.payload ?? event\n if (!payload || payload.type !== \"session.deleted\") return undefined\n const properties = payload.properties as { info?: { id?: unknown } } | undefined\n const id = properties?.info?.id\n return typeof id === \"string\" && id.length > 0 ? id : undefined\n}\n\nconst server: OpenCodePlugin = async (input) => {\n cleanupStaleUnscopedInstall()\n // Retained `claude` children would otherwise outlive a hard opencode exit,\n // reparented to init. Armed once per process however often this runs.\n ensureProcessExitCleanup()\n\n const opencodeVersion = pickOpencodeVersion(input)\n\n // Capture the SDK client so the language model can query opencode's\n // in-memory MCP state per-turn for the runtime overlay. `input` is\n // `unknown` here (kept loose since opencode adds fields over time);\n // narrow defensively.\n if (input && typeof input === \"object\" && \"client\" in input) {\n setOpencodeClient((input as { client?: unknown }).client)\n }\n\n // Capture opencode's project-aware directory as a *fallback* used at\n // Claude CLI spawn time only when `process.cwd()` is unusable. Rescues\n // macOS GUI launches at `/` without freezing the value into provider\n // config, so opencode workspace switches mid-session still take effect.\n // See `resolveSpawnCwd` in runtime-status.ts and issue #4.\n setOpencodeProjectDirectory(pickOpencodeDirectory(input))\n\n return {\n config: async (config) => {\n if (registerSideQuestionCommand(config)) ownsSideQuestionCommand = true\n registerDoctorCommand(config)\n // The bundled `claude-code-plugin` skill: opencode lists it for every\n // provider via skills.paths; the spawn path also stages it as a\n // --plugin-dir so Claude's own Skill tool can load it.\n registerBundledSkillPath(config)\n config.provider ??= {}\n\n await buildAgentRegistry(config)\n\n const expanded = await expandAccountProviders(config)\n if (expanded) {\n logStartupDiagnostics(\n claudeCodeProviders(config.provider),\n opencodeVersion,\n )\n return\n }\n\n const existing = config.provider[PROVIDER_ID]\n config.provider[PROVIDER_ID] = {\n ...existing,\n ...(await providerConfig(existing)),\n models: configModelsForProvider(\n (existing?.models ?? {}) as OpenCodeProvider[\"models\"],\n PROVIDER_ID,\n ),\n }\n logStartupDiagnostics(\n claudeCodeProviders(config.provider),\n opencodeVersion,\n )\n },\n // Only `session.deleted` is acted on. MCP config drift is still detected\n // at turn start by the hot-reload check in `claude-code-language-model.ts`,\n // which respawns claude safely between turns, and eviction on\n // `global.disposed` would kill an in-flight stream and abort the user's\n // current turn. A deleted session has no turn left to abort, and its\n // `claude` child would otherwise linger until the idle timer or LRU\n // pressure took it, with its session id kept for a resume that never comes.\n event: async ({ event }) => {\n const sessionID = extractDeletedSessionId(event)\n if (!sessionID) return\n const released = deleteActiveProcessesForSession(sessionID)\n if (released.length > 0) {\n log.info(\"released claude state for deleted session\", { sessionID, released })\n }\n },\n provider: {\n id: PROVIDER_ID,\n models: async (provider) => defaultModelsForProvider(provider.models),\n },\n // Inject opencode's agent name into providerOptions so the language\n // model can distinguish /compact (and title) calls from normal turns.\n // Without this, every no-tools call looks like a title request and\n // gets short-circuited to a synthetic stub.\n // /btw is asked from here, the moment the command is typed, busy or not.\n // The message itself still goes through: opencode queues it behind the\n // running turn and the aside branch in the language model then answers it\n // from the early answer, so the exchange is kept in this conversation.\n \"command.execute.before\": async (input) => {\n if (input.command !== \"btw\" || !ownsSideQuestionCommand) return\n await handleBtwCommand(getOpencodeClient() as BtwSdkClient | null, input)\n },\n \"chat.params\": async (input, output) => {\n const providerID = input.model?.providerID ?? input.provider?.info?.id\n // The hook fires for every provider opencode is configured with, not\n // just ours — keep this at debug to avoid log spam on non-claude-code\n // calls.\n log.debug(\"chat.params hook fired\", {\n agent: input.agent,\n providerID,\n sessionID: input.sessionID,\n })\n if (typeof providerID !== \"string\") return\n if (providerID !== PROVIDER_ID && !providerID.startsWith(`${PROVIDER_ID}-`)) return\n\n // Inject sessionID BEFORE the agent guard so session isolation works\n // even when input.agent is absent (older opencode, provider-switch\n // edge paths). resolveSessionAffinity reads this as a fallback when\n // the x-session-affinity header is missing.\n if (typeof input.sessionID === \"string\" && input.sessionID.length > 0) {\n output.options ??= {}\n ;(output.options as Record<string, unknown>).opencodeSessionID = input.sessionID\n }\n\n if (!input.agent) return\n // opencode wraps the entire `output.options` bag under the providerID\n // via ProviderTransform.providerOptions(model, options) → { [providerID]: options }\n // before handing it to the language model as `providerOptions`. So we\n // write fields at the TOP LEVEL of output.options, not nested under\n // providerID — otherwise the model sees providerOptions[id][id].opencodeAgent.\n output.options ??= {}\n ;(output.options as Record<string, unknown>).opencodeAgent = input.agent\n log.debug(\"chat.params tagged providerOptions\", {\n agent: input.agent,\n sessionID: input.sessionID,\n providerID,\n })\n },\n }\n}\n\n// One package, both opencode majors: 1.x calls `server()`, 2.x calls\n// `setup(ctx)`. This is the documented dual shape from opencode's V1 migration\n// guide, and V1 has accepted an object entrypoint since 1.18.29. See V2.md.\nexport default {\n id: \"@khalilgharbaoui/opencode-claude-code-plugin\",\n server,\n setup: createV2Setup({\n createProvider: createClaudeCode,\n defaultProxyTools: DEFAULT_PROXY_TOOL_NAMES,\n loadConfig: loadMergedOpencodeConfig,\n buildAgentRegistry: (config) => buildAgentRegistry(config as OpenCodeConfig),\n }),\n}\n\n// ---------------------------------------------------------------------------\n// Re-exports\n// ---------------------------------------------------------------------------\n\nexport { ClaudeCodeLanguageModel } from \"./claude-code-language-model.js\"\nexport { bridgeOpencodeMcp } from \"./mcp-bridge.js\"\nexport {\n type AgentRecord,\n getAgentRegistry,\n getDefaultSubagentModel,\n resolveAgentModel,\n} from \"./agent-models.js\"\nexport { defaultModels } from \"./models.js\"\nexport type {\n ClaudeCodeConfig,\n ClaudeCodeProviderSettings,\n ClaudeStreamMessage,\n} from \"./types.js\"\nexport type { OpenCodeHooks, OpenCodeModel, OpenCodePlugin } from \"./opencode-types.js\"\n"],"mappings":"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","names":["list","classify","homedir","server","key","sessionKey","KEY_SEPARATOR","homedir","pendingQuestions","pendingKey","sessionKey","KEY_SEPARATOR","randomUUID","EventEmitter","fs","path","path","server","resolve","reject","EventEmitter","EventEmitter","sessionKey","sessionKey","randomUUID","isRecord","randomUUID","resolve","resolve","randomUUID","resolve","sessionKey","EventEmitter","resolve","execFile","fs","path","promisify","fs","path","os","crypto","resolve","execFileAsync","promisify","execFile","isRecord","SYSTEM_REMINDER_BLOCK","server","list","str","path","path","crypto","fs","os","path","fileURLToPath","fileURLToPath","dirExists","fileExists","EventEmitter","unlink","os","fs","path","randomUUID","randomUUID","EventEmitter","unlink","readFileSync","writeFileSync","homedir","tmpdir","randomUUID","dirname","join","path","readFileSync","join","dirname","homedir","tmpdir","randomUUID","writeFileSync","content","unlink","spawn","createInterface","resolve","toUsage","toFinishReason","handleControlRequest","stream","completeResult","block","planId","list","existsSync","readFileSync","realpathSync","rmSync","writeFileSync","homedir","join","resolve","fileURLToPath","join","homedir","existsSync","readFileSync","fileURLToPath","realpathSync","resolve","existsSync","join","realpathSync","readFileSync","rmSync","writeFileSync","path","server","PROVIDER_ID","input"]}
|